@@ -5,6 +5,7 @@ import { log } from './log';
55
66import { ALIAS_AS_RELATIVE , ALIAS_AS_RESOLVABLE } from './common' ;
77
8+ /** Type guard for plain literal nodes; rejects template literals with interpolations. */
89function isLiteral ( node : babelTypes . Node ) : node is babelTypes . Literal {
910 if ( node == null ) {
1011 return false ;
@@ -21,6 +22,7 @@ function isLiteral(node: babelTypes.Node): node is babelTypes.Literal {
2122 return true ;
2223}
2324
25+ /** Extracts the runtime value of a literal. Throws on null/regexp — never valid module specifiers. */
2426function getLiteralValue ( node : babelTypes . Literal ) {
2527 if ( node . type === 'TemplateLiteral' ) {
2628 return node . quasis [ 0 ] . value . raw ;
@@ -37,6 +39,7 @@ function getLiteralValue(node: babelTypes.Literal) {
3739 return node . value ;
3840}
3941
42+ /** Renders an import specifier list back to source (`a, { b, c as d }`) for log output. */
4043function reconstructSpecifiers (
4144 specs : (
4245 | babelTypes . ImportDefaultSpecifier
@@ -79,6 +82,7 @@ function reconstructSpecifiers(
7982 return defaults . join ( ', ' ) ;
8083}
8184
85+ /** Prints any AST node back to a single-line source string, used when an arg isn't a literal. */
8286function reconstruct ( node : babelTypes . Node ) {
8387 let v = generate ( node , { comments : false } ) . code . replace ( / \n / g, '' ) ;
8488 let v2 ;
@@ -102,6 +106,7 @@ interface Was {
102106 v3 ?: string ;
103107}
104108
109+ /** Fills a template (e.g. `require({v1}{c2}{v2})`) with captured args to produce the printable form of a match. */
105110function forge ( pattern : string , was : Was ) {
106111 return pattern
107112 . replace ( '{c1}' , ', ' )
@@ -112,6 +117,7 @@ function forge(pattern: string, was: Was) {
112117 . replace ( '{v3}' , was . v3 ? was . v3 : '' ) ;
113118}
114119
120+ /** Guards the 2nd arg of require/require.resolve — only pkg's `must-exclude`/`may-exclude` markers are honored. */
115121function valid2 ( v2 ?: Was [ 'v2' ] ) {
116122 return (
117123 v2 === undefined ||
@@ -121,6 +127,7 @@ function valid2(v2?: Was['v2']) {
121127 ) ;
122128}
123129
130+ /** Matches `require.resolve("lit"[, "lit"])`. Returns captured args or null. */
124131function visitorRequireResolve ( n : babelTypes . Node ) {
125132 if ( ! babelTypes . isCallExpression ( n ) ) {
126133 return null ;
@@ -150,6 +157,7 @@ function visitorRequireResolve(n: babelTypes.Node) {
150157 } ;
151158}
152159
160+ /** Matches `require("lit"[, "lit"])`. Returns captured args or null. */
153161function visitorRequire ( n : babelTypes . Node ) {
154162 if ( ! babelTypes . isCallExpression ( n ) ) {
155163 return null ;
@@ -173,6 +181,7 @@ function visitorRequire(n: babelTypes.Node) {
173181 } ;
174182}
175183
184+ /** Matches a static ESM `import … from "lit"` declaration. */
176185function visitorImport ( n : babelTypes . Node ) {
177186 if ( ! babelTypes . isImportDeclaration ( n ) ) {
178187 return null ;
@@ -181,6 +190,7 @@ function visitorImport(n: babelTypes.Node) {
181190 return { v1 : n . source . value , v3 : reconstructSpecifiers ( n . specifiers ) } ;
182191}
183192
193+ /** Matches dynamic `import("lit")` so bundler-emitted chunk splits get walked like static imports. */
184194function visitorDynamicImport ( n : babelTypes . Node ) {
185195 if ( ! babelTypes . isCallExpression ( n ) ) {
186196 return null ;
@@ -197,6 +207,7 @@ function visitorDynamicImport(n: babelTypes.Node) {
197207 return { v1 : getLiteralValue ( n . arguments [ 0 ] as babelTypes . Literal ) } ;
198208}
199209
210+ /** Matches `path.join(__dirname, "lit")` — treats the joined path as a snapshot asset reference. */
200211function visitorPathJoin ( n : babelTypes . Node ) {
201212 if ( ! babelTypes . isCallExpression ( n ) ) {
202213 return null ;
@@ -237,6 +248,11 @@ function visitorPathJoin(n: babelTypes.Node) {
237248 return { v1 : getLiteralValue ( n . arguments [ 1 ] as babelTypes . StringLiteral ) } ;
238249}
239250
251+ /**
252+ * Runs each literal-arg matcher in order and returns the first hit as a
253+ * `{alias, aliasType, mustExclude?, mayExclude?}` derivative for the walker to
254+ * bundle. When `test` is true returns a printable form (used by unit tests).
255+ */
240256export function visitorSuccessful ( node : babelTypes . Node , test = false ) {
241257 let was : Was | null = visitorRequireResolve ( node ) ;
242258
@@ -309,6 +325,7 @@ export function visitorSuccessful(node: babelTypes.Node, test = false) {
309325 return null ;
310326}
311327
328+ /** Matches `require.resolve(<non-literal>[, "lit"])` — feeds the "Cannot resolve" warning path. */
312329function nonLiteralRequireResolve ( n : babelTypes . Node ) {
313330 if ( ! babelTypes . isCallExpression ( n ) ) {
314331 return null ;
@@ -348,6 +365,7 @@ function nonLiteralRequireResolve(n: babelTypes.Node) {
348365 } ;
349366}
350367
368+ /** Matches `require(<non-literal>[, "lit"])` — feeds the "Cannot resolve" warning path. */
351369function nonLiteralRequire ( n : babelTypes . Node ) {
352370 if ( ! babelTypes . isCallExpression ( n ) ) {
353371 return null ;
@@ -381,6 +399,7 @@ function nonLiteralRequire(n: babelTypes.Node) {
381399 } ;
382400}
383401
402+ /** Entry visitor for dynamic requires whose target isn't known at build time — returns the alias to warn about. */
384403export function visitorNonLiteral ( n : babelTypes . Node ) {
385404 const was = nonLiteralRequireResolve ( n ) || nonLiteralRequire ( n ) ;
386405
@@ -399,6 +418,7 @@ export function visitorNonLiteral(n: babelTypes.Node) {
399418 return null ;
400419}
401420
421+ /** Loose `require(...)` match (no literal gate) — used only to surface malformed-require diagnostics. */
402422function isRequire ( n : babelTypes . Node ) {
403423 if ( ! babelTypes . isCallExpression ( n ) ) {
404424 return null ;
@@ -421,6 +441,7 @@ function isRequire(n: babelTypes.Node) {
421441 return { v1 : reconstruct ( n . arguments [ 0 ] ) } ;
422442}
423443
444+ /** Loose `require.resolve(...)` match (no literal gate) — used only for malformed-require diagnostics. */
424445function isRequireResolve ( n : babelTypes . Node ) {
425446 if ( ! babelTypes . isCallExpression ( n ) ) {
426447 return null ;
@@ -449,6 +470,7 @@ function isRequireResolve(n: babelTypes.Node) {
449470 return { v1 : reconstruct ( n . arguments [ 0 ] ) } ;
450471}
451472
473+ /** Fires on require/require.resolve shapes the literal matchers rejected (wrong arg count, etc.). */
452474export function visitorMalformed ( n : babelTypes . Node ) {
453475 const was = isRequireResolve ( n ) || isRequire ( n ) ;
454476
@@ -459,6 +481,7 @@ export function visitorMalformed(n: babelTypes.Node) {
459481 return null ;
460482}
461483
484+ /** Flags `path.resolve(...)` so the walker can warn that it resolves against `process.cwd()` at runtime, not `__dirname`. */
462485export function visitorUseSCWD ( n : babelTypes . Node ) {
463486 if ( ! babelTypes . isCallExpression ( n ) ) {
464487 return null ;
@@ -489,6 +512,11 @@ export function visitorUseSCWD(n: babelTypes.Node) {
489512
490513type VisitorFunction = ( node : babelTypes . Node , trying ?: boolean ) => boolean ;
491514
515+ /**
516+ * Iterative DFS over the AST. `visitor` returns true to descend into children;
517+ * `trying` is propagated inside try/catch bodies so the walker can downgrade
518+ * downstream warnings to debug.
519+ */
492520function traverse ( ast : babelTypes . File , visitor : VisitorFunction ) {
493521 // modified esprima-walk to support
494522 // visitor return value and "trying" flag
@@ -521,6 +549,7 @@ function traverse(ast: babelTypes.File, visitor: VisitorFunction) {
521549 }
522550}
523551
552+ /** `babel.parse` wrapper. `isEsm` selects `sourceType: 'module'` so `import.meta` / top-level await parse cleanly. */
524553export function parse ( body : string , isEsm = false ) {
525554 return babel . parse ( body , {
526555 allowImportExportEverywhere : true ,
@@ -529,6 +558,12 @@ export function parse(body: string, isEsm = false) {
529558 } ) ;
530559}
531560
561+ /**
562+ * Parses `body` and walks the AST with `visitor`. Parse failures are logged
563+ * (not thrown) so one unparseable file doesn't abort the whole build — but the
564+ * file's dependencies are then skipped, which is why callers must pass the
565+ * correct `isEsm` flag.
566+ */
532567export function detect (
533568 body : string ,
534569 visitor : VisitorFunction ,
0 commit comments