Skip to content

Commit b61a9f2

Browse files
committed
docs(detector): add concise jsdoc for visitor and helper functions
1 parent 133d1c7 commit b61a9f2

1 file changed

Lines changed: 35 additions & 0 deletions

File tree

lib/detector.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { log } from './log';
55

66
import { ALIAS_AS_RELATIVE, ALIAS_AS_RESOLVABLE } from './common';
77

8+
/** Type guard for plain literal nodes; rejects template literals with interpolations. */
89
function 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. */
2426
function 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. */
4043
function 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. */
8286
function 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. */
105110
function 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. */
115121
function 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. */
124131
function 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. */
153161
function 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. */
176185
function 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. */
184194
function 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. */
200211
function 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+
*/
240256
export 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. */
312329
function 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. */
351369
function 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. */
384403
export 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. */
402422
function 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. */
424445
function 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.). */
452474
export 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`. */
462485
export function visitorUseSCWD(n: babelTypes.Node) {
463486
if (!babelTypes.isCallExpression(n)) {
464487
return null;
@@ -489,6 +512,11 @@ export function visitorUseSCWD(n: babelTypes.Node) {
489512

490513
type 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+
*/
492520
function 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. */
524553
export 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+
*/
532567
export function detect(
533568
body: string,
534569
visitor: VisitorFunction,

0 commit comments

Comments
 (0)