@davidwells/extract-deps 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/dep-graph.js +40 -21
- package/tests/dep-graph.test.js +29 -0
- package/tests/fixtures/node-module-named-export/handler-correct.js +6 -0
- package/tests/fixtures/node-module-named-export/handler.js +6 -0
- package/tests/fixtures/node-module-named-export/node_modules/fake-named-pkg/index.js +4 -0
- package/tests/fixtures/node-module-named-export/node_modules/fake-named-pkg/package.json +6 -0
- package/tests/fixtures/node-module-named-export/package.json +4 -0
- package/types/dep-graph.d.ts +2 -1
- package/types/dep-graph.d.ts.map +1 -1
- package/types/detect-syntax.d.ts.map +1 -1
- package/types/get-exports.d.ts +1 -1
- package/types/get-exports.d.ts.map +1 -1
- package/types/get-imports.d.ts +6 -1
- package/types/get-imports.d.ts.map +1 -1
- package/types/get-requires.d.ts +3 -0
- package/types/get-requires.d.ts.map +1 -1
- package/types/index.d.ts +2 -0
- package/types/index.d.ts.map +1 -1
- package/types/utils.d.ts +12 -0
- package/types/utils.d.ts.map +1 -1
package/package.json
CHANGED
package/src/dep-graph.js
CHANGED
|
@@ -106,23 +106,34 @@ function verifyImportedValuesExist(dep, targetDeps, targetPath, sourcePath, erro
|
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
|
-
// For non-destructured imports,
|
|
109
|
+
// For non-destructured imports, verify there's a default export
|
|
110
|
+
// Only applies to ESM imports - CJS require() gets whatever module.exports is
|
|
110
111
|
else {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
|
|
112
|
+
if (dep.isESM) {
|
|
113
|
+
// ESM import X from 'pkg' requires a default export
|
|
114
|
+
const hasModuleExports = targetDeps.moduleExports && targetDeps.moduleExports.length > 0
|
|
115
|
+
const hasESMExports = targetDeps.exports && targetDeps.exports.length > 0
|
|
116
|
+
const hasDefaultExport =
|
|
117
|
+
hasModuleExports ||
|
|
118
|
+
(hasESMExports && targetDeps.exports.some(exp => exp.exportType === 'default'))
|
|
119
|
+
|
|
120
|
+
// Only fail if we can detect exports but none are default
|
|
121
|
+
// If we can't detect any exports (e.g., complex/minified file), assume valid
|
|
122
|
+
const hasDetectableExports = hasModuleExports || hasESMExports
|
|
123
|
+
|
|
124
|
+
if (hasDetectableExports && !hasDefaultExport) {
|
|
125
|
+
const error = new Error(
|
|
126
|
+
`Import not found: "${targetPath}" does not have a default export.\n` +
|
|
127
|
+
`Import statement: ${dep.line}\n` +
|
|
128
|
+
`Importing from: ${sourcePath}`
|
|
129
|
+
)
|
|
130
|
+
if (throwOnFirstError) {
|
|
131
|
+
throw error
|
|
132
|
+
}
|
|
133
|
+
errors.push(error)
|
|
123
134
|
}
|
|
124
|
-
errors.push(error)
|
|
125
135
|
}
|
|
136
|
+
// CJS require() can get any module.exports - no default check needed
|
|
126
137
|
}
|
|
127
138
|
}
|
|
128
139
|
|
|
@@ -166,20 +177,20 @@ function buildGraphRecursively(
|
|
|
166
177
|
// Collect all relative dependencies
|
|
167
178
|
const relativeDeps = []
|
|
168
179
|
if (deps.requires && deps.requires.length > 0) {
|
|
169
|
-
relativeDeps.push(...deps.requires.filter(req => req.isRelative))
|
|
180
|
+
relativeDeps.push(...deps.requires.filter(req => req.isRelative).map(req => ({ ...req, isESM: false })))
|
|
170
181
|
}
|
|
171
182
|
if (deps.imports && deps.imports.length > 0) {
|
|
172
|
-
relativeDeps.push(...deps.imports.filter(imp => imp.isRelative))
|
|
183
|
+
relativeDeps.push(...deps.imports.filter(imp => imp.isRelative).map(imp => ({ ...imp, isESM: true })))
|
|
173
184
|
}
|
|
174
185
|
|
|
175
186
|
// Collect all non-relative dependencies (node_modules)
|
|
176
187
|
const nodeModuleDeps = []
|
|
177
188
|
if (verifyNodeModules || includeNodeModules) {
|
|
178
189
|
if (deps.requires && deps.requires.length > 0) {
|
|
179
|
-
nodeModuleDeps.push(...deps.requires.filter(req => !req.isRelative))
|
|
190
|
+
nodeModuleDeps.push(...deps.requires.filter(req => !req.isRelative).map(req => ({ ...req, isESM: false })))
|
|
180
191
|
}
|
|
181
192
|
if (deps.imports && deps.imports.length > 0) {
|
|
182
|
-
nodeModuleDeps.push(...deps.imports.filter(imp => !imp.isRelative))
|
|
193
|
+
nodeModuleDeps.push(...deps.imports.filter(imp => !imp.isRelative).map(imp => ({ ...imp, isESM: true })))
|
|
183
194
|
}
|
|
184
195
|
}
|
|
185
196
|
|
|
@@ -242,11 +253,11 @@ function buildGraphRecursively(
|
|
|
242
253
|
continue
|
|
243
254
|
}
|
|
244
255
|
|
|
256
|
+
let resolvedPath
|
|
245
257
|
try {
|
|
246
|
-
//
|
|
258
|
+
// resolveImport throws if file doesn't exist
|
|
247
259
|
const resolved = resolveImport(from, parentURL)
|
|
248
|
-
|
|
249
|
-
// If we get here, the module resolved successfully
|
|
260
|
+
resolvedPath = fileURLToPath(resolved)
|
|
250
261
|
} catch (error) {
|
|
251
262
|
const err = new Error(
|
|
252
263
|
`"${line}" could not be resolved in ${filePath}\n\n` +
|
|
@@ -258,6 +269,14 @@ function buildGraphRecursively(
|
|
|
258
269
|
throw err
|
|
259
270
|
}
|
|
260
271
|
errors.push(err)
|
|
272
|
+
continue
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Verify import/export styles match (only needed for ESM imports)
|
|
276
|
+
if (dep.isESM) {
|
|
277
|
+
const targetContent = fs.readFileSync(resolvedPath, 'utf8')
|
|
278
|
+
const targetDeps = extractDeps(targetContent)
|
|
279
|
+
verifyImportedValuesExist(dep, targetDeps, resolvedPath, filePath, errors, throwOnFirstError)
|
|
261
280
|
}
|
|
262
281
|
}
|
|
263
282
|
}
|
package/tests/dep-graph.test.js
CHANGED
|
@@ -521,4 +521,33 @@ test('resolveDepPaths with returnErrors: true and no errors', () => {
|
|
|
521
521
|
assert.is(result.errors.length, 0, 'Should have no errors')
|
|
522
522
|
})
|
|
523
523
|
|
|
524
|
+
// Node module export verification tests
|
|
525
|
+
const NODE_MODULE_NAMED_FIXTURE = path.resolve(__dirname, './fixtures/node-module-named-export/handler.js')
|
|
526
|
+
const NODE_MODULE_NAMED_CORRECT = path.resolve(__dirname, './fixtures/node-module-named-export/handler-correct.js')
|
|
527
|
+
|
|
528
|
+
test('verifyNodeModules: true should catch default import when only named exports exist', () => {
|
|
529
|
+
let errorMessage = ''
|
|
530
|
+
try {
|
|
531
|
+
depGraph(NODE_MODULE_NAMED_FIXTURE, { verifyNodeModules: true })
|
|
532
|
+
} catch (error) {
|
|
533
|
+
errorMessage = error.message
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
assert.ok(errorMessage.includes('does not have a default export'), 'Should detect missing default export')
|
|
537
|
+
assert.ok(errorMessage.includes('fake-named-pkg'), 'Should mention the package')
|
|
538
|
+
})
|
|
539
|
+
|
|
540
|
+
test('verifyNodeModules: true should pass for correct named import', () => {
|
|
541
|
+
assert.not.throws(() => {
|
|
542
|
+
depGraph(NODE_MODULE_NAMED_CORRECT, { verifyNodeModules: true })
|
|
543
|
+
}, 'Should pass for correct named import')
|
|
544
|
+
})
|
|
545
|
+
|
|
546
|
+
test('verifyNodeModules with returnErrors should collect export mismatch errors', () => {
|
|
547
|
+
const result = depGraph(NODE_MODULE_NAMED_FIXTURE, { verifyNodeModules: true, returnErrors: true })
|
|
548
|
+
|
|
549
|
+
assert.ok(result.errors.length > 0, 'Should have errors')
|
|
550
|
+
assert.ok(result.errors[0].message.includes('does not have a default export'), 'Should be default export error')
|
|
551
|
+
})
|
|
552
|
+
|
|
524
553
|
test.run()
|
package/types/dep-graph.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ export function depGraph(filePath: string, options?: {
|
|
|
29
29
|
* @param {boolean} [options.verifyNodeModules] - If true, verify node_modules packages can be resolved. Default: false
|
|
30
30
|
* @param {boolean} [options.throwOnFirstError] - If true, throw immediately on first error. Default: false
|
|
31
31
|
* @param {boolean} [options.returnErrors] - If true, include errors array in result instead of throwing
|
|
32
|
-
* @returns {{ files: string[], nodeModules: string[], errors?: Error[] }} - Object with files in topological order and
|
|
32
|
+
* @returns {{ files: string[], nodeModules: string[], builtIns: string[], errors?: Error[] }} - Object with files in topological order, npm packages, and Node.js built-ins
|
|
33
33
|
*/
|
|
34
34
|
export function resolveDepPaths(filePath: string, options?: {
|
|
35
35
|
verifyGraph?: boolean;
|
|
@@ -39,6 +39,7 @@ export function resolveDepPaths(filePath: string, options?: {
|
|
|
39
39
|
}): {
|
|
40
40
|
files: string[];
|
|
41
41
|
nodeModules: string[];
|
|
42
|
+
builtIns: string[];
|
|
42
43
|
errors?: Error[];
|
|
43
44
|
};
|
|
44
45
|
//# sourceMappingURL=dep-graph.d.ts.map
|
package/types/dep-graph.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dep-graph.d.ts","sourceRoot":"","sources":["../src/dep-graph.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dep-graph.d.ts","sourceRoot":"","sources":["../src/dep-graph.js"],"names":[],"mappings":"AA4RA;;;;;;;;;;;GAWG;AACH,mCAVW,MAAM,YAEd;IAA0B,UAAU,GAA5B,OAAO;IACW,WAAW,GAA7B,OAAO;IACW,iBAAiB,GAAnC,OAAO;IACW,kBAAkB,GAApC,OAAO;IACW,iBAAiB,GAAnC,OAAO;IACW,YAAY,GAA9B,OAAO;CACf,GAAU,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAAC;IAAC,KAAK,gBAAM;IAAC,MAAM,EAAE,KAAK,EAAE,CAAA;CAAC,GAAC,SAAS,CA8BzE;AAGD;;;;;;;;;GASG;AACH,0CARW,MAAM,YAEd;IAA0B,WAAW,GAA7B,OAAO;IACW,iBAAiB,GAAnC,OAAO;IACW,iBAAiB,GAAnC,OAAO;IACW,YAAY,GAA9B,OAAO;CACf,GAAU;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,WAAW,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAA;CAAE,CAkF5F"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detect-syntax.d.ts","sourceRoot":"","sources":["../src/detect-syntax.js"],"names":[],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"detect-syntax.d.ts","sourceRoot":"","sources":["../src/detect-syntax.js"],"names":[],"mappings":";;;;;;;oBAcc,OAAO;;AAHrB;;;;GAIG;AAEH;;;;;;GAMG;AACH,mCAJW,MAAM,SACN,mBAAmB,GACjB,OAAO,CAMnB;AAED;;;;;;GAMG;AACH,mCAJW,MAAM,SACN,mBAAmB,GACjB,OAAO,CAMnB;AAED;;;;;;GAMG;AACH,mCAJW,MAAM,SACN,mBAAmB,OAe7B"}
|
package/types/get-exports.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-exports.d.ts","sourceRoot":"","sources":["../src/get-exports.js"],"names":[],"mappings":"AAiBA
|
|
1
|
+
{"version":3,"file":"get-exports.d.ts","sourceRoot":"","sources":["../src/get-exports.js"],"names":[],"mappings":"AAiBA,+DAiCC;AAED;;;;;;;;IAqDC;AAED;;;;;;IAeC;AAED,uEAuBC;AAED;;;;;;IAeC;AAED;;;;;;;IAgBC;AAED;;;;;;IAeC;AAED;;;;;;;;;;;;IA0BC"}
|
package/types/get-imports.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export function getImports(code
|
|
1
|
+
export function getImports(code?: string, options?: {}): {
|
|
2
2
|
line: string;
|
|
3
3
|
varType: string;
|
|
4
4
|
varName: string;
|
|
@@ -7,6 +7,7 @@ export function getImports(code: any, options?: {}): {
|
|
|
7
7
|
index: number;
|
|
8
8
|
isImport: boolean;
|
|
9
9
|
isRelative: boolean;
|
|
10
|
+
isBuiltIn: boolean;
|
|
10
11
|
}[];
|
|
11
12
|
export function getStaticImports(targetText?: string): {
|
|
12
13
|
line: string;
|
|
@@ -17,6 +18,7 @@ export function getStaticImports(targetText?: string): {
|
|
|
17
18
|
index: number;
|
|
18
19
|
isImport: boolean;
|
|
19
20
|
isRelative: boolean;
|
|
21
|
+
isBuiltIn: boolean;
|
|
20
22
|
}[];
|
|
21
23
|
export function getTypeImports(targetText?: string): {
|
|
22
24
|
importStatement: string;
|
|
@@ -26,6 +28,7 @@ export function getTypeImports(targetText?: string): {
|
|
|
26
28
|
isImport: boolean;
|
|
27
29
|
isTypeImport: boolean;
|
|
28
30
|
isRelative: boolean;
|
|
31
|
+
isBuiltIn: boolean;
|
|
29
32
|
}[];
|
|
30
33
|
export function getDynamicImports(targetText?: string): {
|
|
31
34
|
line: string;
|
|
@@ -36,6 +39,7 @@ export function getDynamicImports(targetText?: string): {
|
|
|
36
39
|
index: number;
|
|
37
40
|
isImport: boolean;
|
|
38
41
|
isRelative: boolean;
|
|
42
|
+
isBuiltIn: boolean;
|
|
39
43
|
isDynamic: boolean;
|
|
40
44
|
}[];
|
|
41
45
|
export function getAssertionImports(targetText?: string): {
|
|
@@ -49,6 +53,7 @@ export function getAssertionImports(targetText?: string): {
|
|
|
49
53
|
index: number;
|
|
50
54
|
isImport: boolean;
|
|
51
55
|
isRelative: boolean;
|
|
56
|
+
isBuiltIn: boolean;
|
|
52
57
|
isAssertion: boolean;
|
|
53
58
|
}[];
|
|
54
59
|
//# sourceMappingURL=get-imports.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-imports.d.ts","sourceRoot":"","sources":["../src/get-imports.js"],"names":[],"mappings":"AAWA
|
|
1
|
+
{"version":3,"file":"get-imports.d.ts","sourceRoot":"","sources":["../src/get-imports.js"],"names":[],"mappings":"AAWA;;;;;;;;;;IAUC;AAGD;;;;;;;;;;IAmBC;AAED;;;;;;;;;IAkBC;AAED;;;;;;;;;;;IAqBC;AAED;;;;;;;;;;;;;IAyBC"}
|
package/types/get-requires.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export function getRequires(code: any): {
|
|
|
7
7
|
index: number;
|
|
8
8
|
isRequire: boolean;
|
|
9
9
|
isRelative: boolean;
|
|
10
|
+
isBuiltIn: boolean;
|
|
10
11
|
isDynamic: boolean;
|
|
11
12
|
}[];
|
|
12
13
|
export function getStaticRequires(targetText?: string): {
|
|
@@ -18,6 +19,7 @@ export function getStaticRequires(targetText?: string): {
|
|
|
18
19
|
index: number;
|
|
19
20
|
isRequire: boolean;
|
|
20
21
|
isRelative: boolean;
|
|
22
|
+
isBuiltIn: boolean;
|
|
21
23
|
isDynamic: boolean;
|
|
22
24
|
}[];
|
|
23
25
|
export function getDynamicRequires(code: any): {
|
|
@@ -29,6 +31,7 @@ export function getDynamicRequires(code: any): {
|
|
|
29
31
|
index: number;
|
|
30
32
|
isRequire: boolean;
|
|
31
33
|
isRelative: boolean;
|
|
34
|
+
isBuiltIn: boolean;
|
|
32
35
|
isDynamic: boolean;
|
|
33
36
|
}[];
|
|
34
37
|
//# sourceMappingURL=get-requires.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-requires.d.ts","sourceRoot":"","sources":["../src/get-requires.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"get-requires.d.ts","sourceRoot":"","sources":["../src/get-requires.js"],"names":[],"mappings":"AAgEA;;;;;;;;;;;IAIC;AA7BD;;;;;;;;;;;IAuBC;AAvDD;;;;;;;;;;;IA6BC"}
|
package/types/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export function extractDeps(code: any, filePath?: string): {
|
|
|
8
8
|
index: number;
|
|
9
9
|
isImport: boolean;
|
|
10
10
|
isRelative: boolean;
|
|
11
|
+
isBuiltIn: boolean;
|
|
11
12
|
}[];
|
|
12
13
|
exports: any[];
|
|
13
14
|
requires: {
|
|
@@ -19,6 +20,7 @@ export function extractDeps(code: any, filePath?: string): {
|
|
|
19
20
|
index: number;
|
|
20
21
|
isRequire: boolean;
|
|
21
22
|
isRelative: boolean;
|
|
23
|
+
isBuiltIn: boolean;
|
|
22
24
|
isDynamic: boolean;
|
|
23
25
|
}[];
|
|
24
26
|
moduleExports: any[];
|
package/types/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":"AAkBA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":"AAkBA;;;;;;;;;;;;;;;;;;;;;;;;;;EAgBC"}
|
package/types/utils.d.ts
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strip comments from code, preserving :// in URLs
|
|
3
|
+
* @param {string} code
|
|
4
|
+
* @returns {string}
|
|
5
|
+
*/
|
|
6
|
+
export function stripComments(code: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* Check if a module specifier is a Node.js built-in
|
|
9
|
+
* @param {string} moduleName - The module specifier (e.g., 'fs', 'node:fs/promises')
|
|
10
|
+
* @returns {boolean}
|
|
11
|
+
*/
|
|
12
|
+
export function isBuiltIn(moduleName: string): boolean;
|
|
1
13
|
export function isRelativeImport(importLocation: any): boolean;
|
|
2
14
|
export function isRelativeImportWithQuotes(importLocation: any): boolean;
|
|
3
15
|
export function getNames(names: any, aliasKey?: string): any;
|
package/types/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.js"],"names":[],"mappings":"AAMA;;;;GAIG;AACH,oCAHW,MAAM,GACJ,MAAM,CAYlB;AAED;;;;GAIG;AACH,sCAHW,MAAM,GACJ,OAAO,CAUnB;AAED,+DAEC;AAED,yEAKC;AAgFD,6DA+DC;AAGD,yDAqCC"}
|