From 5ce12fb9f93e442afeaed57c2febc03ca8e2c654 Mon Sep 17 00:00:00 2001 From: montjeffrey Date: Fri, 9 Oct 2026 17:52:55 -0400 Subject: [PATCH] fix(carriers): ignore member calls named require or import, and warn on deeply nested arguments - Call patterns now skip a require/import preceded by a dot, with or without whitespace or optional chaining, so records.require('./label') and obj?.import('./x') are method calls rather than module loads. - Replace the one-level dynamic-argument pattern with a balanced-paren scan over the string-free skeleton, so require(path.join(__dirname, getName())) and require(a(b(c()))) are reported as dynamic. Rescanning all 802 repository JavaScript files against the previous head changes 6 results, each a newly reported dynamic argument nested deeper than one level (docker eval checks and one test); no specifier or package result changes. The real registry is unchanged: 35/35 resolved, zero failures or warnings. --- scripts/ci/validate-carrier-closure.js | 36 +++++++++++++++++++++----- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/scripts/ci/validate-carrier-closure.js b/scripts/ci/validate-carrier-closure.js index cea037909..b6e4682db 100644 --- a/scripts/ci/validate-carrier-closure.js +++ b/scripts/ci/validate-carrier-closure.js @@ -26,12 +26,14 @@ const BUILTINS = new Set(builtinModules); const SCRIPT_EXTENSIONS = new Set(['.js', '.cjs', '.mjs']); const MARK = '\u0000'; const SLOT = `${MARK}(\\d+)${MARK}`; -const CALL_PATTERN = new RegExp(`\\b(require|import)\\s*\\(\\s*${SLOT}\\s*\\)`, 'g'); +// `obj.require(`, `obj?.import(` and `obj . require(` are method calls, not module loads. +const NOT_MEMBER = '(? { + const start = match.index + match[0].length; + let depth = 1; + let cursor = start; + while (cursor < code.length && depth > 0) { + if (code[cursor] === '(') depth += 1; + else if (code[cursor] === ')') depth -= 1; + cursor += 1; + } + if (depth === 0) found.push(code.slice(start, cursor - 1).trim()); + }); + return found; +} + function renderExpression(text, literals) { return text.replace(SLOT_PATTERN, (_whole, slotIndex) => `'${literals[Number(slotIndex)]}'`).split(CLOSE).join(' ').trim(); } @@ -220,11 +243,10 @@ function extractReferences(rawSource) { collect(SLOT_PATTERN, match[1], slotMatch => segments.push(literals[Number(slotMatch[1])])); specifiers.add(`./${segments.join('/')}`); }); - collect(DYNAMIC_PATTERN, code, match => { - const argument = match[1].trim(); - if (new RegExp(`^${SLOT}$`).test(argument) || STATIC_JOIN_PATTERN.test(argument)) return; + for (const argument of callArguments(code)) { + if (!argument || new RegExp(`^${SLOT}$`).test(argument) || STATIC_JOIN_PATTERN.test(argument)) continue; dynamic.add(renderExpression(argument, literals)); - }); + } return { specifiers: [...specifiers], imports: [...imports], packages: [...packages], dynamic: [...dynamic] }; }