@davidwells/extract-deps 0.1.14 → 0.1.15
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 +4 -0
- package/src/index.js +3 -1
- package/tests/dep-graph-json.test.js +79 -0
- package/types/dep-graph.d.ts +4 -1
- package/types/dep-graph.d.ts.map +1 -1
- package/types/get-cjs-exports.d.ts.map +1 -1
- package/types/get-exports.d.ts.map +1 -1
- package/types/get-imports.d.ts.map +1 -1
- package/types/get-requires.d.ts.map +1 -1
- package/types/utils.d.ts +8 -0
- package/types/utils.d.ts.map +1 -1
package/package.json
CHANGED
package/src/dep-graph.js
CHANGED
|
@@ -164,6 +164,10 @@ function resolveImportPathUncached(baseDir, importPath) {
|
|
|
164
164
|
* @param {boolean} throwOnFirstError - Whether to throw immediately on first error
|
|
165
165
|
*/
|
|
166
166
|
function verifyImportedValuesExist(dep, targetDeps, targetPath, sourcePath, errors, throwOnFirstError) {
|
|
167
|
+
// JSON modules have no export statements to check: the parsed value IS the default export
|
|
168
|
+
// (ESM, `with { type: 'json' }`) or module.exports (CJS require). Scanning the JSON text
|
|
169
|
+
// for exports found none and reported a missing default export for `require('./x.json')`.
|
|
170
|
+
if (/\.json$/i.test(targetPath)) return
|
|
167
171
|
// Check if this is a destructured import
|
|
168
172
|
if (dep.definitions && dep.definitions.expectsDestructuring && dep.definitions.destructuredParameters) {
|
|
169
173
|
const importedNames = dep.definitions.destructuredParameters.map(p => p.name.replace(/,$/, ''))
|
package/src/index.js
CHANGED
|
@@ -45,7 +45,9 @@ function extractDeps(code, filePath = '') {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
function parseDeps(code) {
|
|
48
|
-
|
|
48
|
+
// Import-attribute imports (`import data from './x.json' with { type: 'json' }`) are
|
|
49
|
+
// dependencies too: without them a changed JSON file never reaches its importers
|
|
50
|
+
const imports = getImports(code, { includeAssertionImports: true })
|
|
49
51
|
//console.log('imports', imports)
|
|
50
52
|
const requires = getRequires(code)
|
|
51
53
|
// console.log('requires', requires)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// JSON modules in the dependency graph (smart-ci sci-nrnc.4 / sci-nrnc.11)
|
|
2
|
+
const { test } = require('uvu')
|
|
3
|
+
const assert = require('uvu/assert')
|
|
4
|
+
const fs = require('fs')
|
|
5
|
+
const os = require('os')
|
|
6
|
+
const path = require('path')
|
|
7
|
+
const { resolveDepPaths, clearDepGraphCache } = require('../src/dep-graph')
|
|
8
|
+
|
|
9
|
+
function makeFiles(files) {
|
|
10
|
+
const dir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'extract-deps-json-')))
|
|
11
|
+
for (const [rel, content] of Object.entries(files)) {
|
|
12
|
+
fs.mkdirSync(path.dirname(path.join(dir, rel)), { recursive: true })
|
|
13
|
+
fs.writeFileSync(path.join(dir, rel), content)
|
|
14
|
+
}
|
|
15
|
+
return dir
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const VERIFY = { verifyGraph: true, verifyNodeModules: true, includeNodeModules: true, returnErrors: true }
|
|
19
|
+
|
|
20
|
+
test('CommonJS require of JSON: listed, and no "default export" error', () => {
|
|
21
|
+
clearDepGraphCache()
|
|
22
|
+
const dir = makeFiles({
|
|
23
|
+
'handler.js': "const messages = require('./messages.json')\nmodule.exports.handler = async () => messages.hello\n",
|
|
24
|
+
'messages.json': '{ "hello": "Foobar!!!" }\n',
|
|
25
|
+
})
|
|
26
|
+
try {
|
|
27
|
+
const { files, errors } = resolveDepPaths(path.join(dir, 'handler.js'), VERIFY)
|
|
28
|
+
assert.ok(files.includes(path.join(dir, 'messages.json')), 'messages.json is a dependency')
|
|
29
|
+
assert.equal((errors || []).map(e => e.message), [])
|
|
30
|
+
} finally {
|
|
31
|
+
fs.rmSync(dir, { recursive: true, force: true })
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('CommonJS destructuring require of JSON: no export errors', () => {
|
|
36
|
+
clearDepGraphCache()
|
|
37
|
+
const dir = makeFiles({
|
|
38
|
+
'handler.js': "const { hello } = require('./messages.json')\nmodule.exports.handler = async () => hello\n",
|
|
39
|
+
'messages.json': '{ "hello": "hi" }\n',
|
|
40
|
+
})
|
|
41
|
+
try {
|
|
42
|
+
const { errors } = resolveDepPaths(path.join(dir, 'handler.js'), VERIFY)
|
|
43
|
+
assert.equal((errors || []).map(e => e.message), [])
|
|
44
|
+
} finally {
|
|
45
|
+
fs.rmSync(dir, { recursive: true, force: true })
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
test('ESM import of JSON with import attributes (with / assert): listed as a dependency', () => {
|
|
50
|
+
clearDepGraphCache()
|
|
51
|
+
const dir = makeFiles({
|
|
52
|
+
'src/app.mjs': "import contract from '../docs/api.json' with { type: 'json' }\nimport legacy from './legacy.json' assert { type: 'json' }\nexport const h = () => [contract, legacy]\n",
|
|
53
|
+
'src/legacy.json': '{}\n',
|
|
54
|
+
'docs/api.json': '{ "version": 1 }\n',
|
|
55
|
+
})
|
|
56
|
+
try {
|
|
57
|
+
const { files, errors } = resolveDepPaths(path.join(dir, 'src/app.mjs'), VERIFY)
|
|
58
|
+
assert.ok(files.includes(path.join(dir, 'docs/api.json')), 'with { type: json } import is a dependency')
|
|
59
|
+
assert.ok(files.includes(path.join(dir, 'src/legacy.json')), 'assert { type: json } import is a dependency')
|
|
60
|
+
assert.equal((errors || []).map(e => e.message), [])
|
|
61
|
+
} finally {
|
|
62
|
+
fs.rmSync(dir, { recursive: true, force: true })
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
test('a missing JSON import is still reported', () => {
|
|
67
|
+
clearDepGraphCache()
|
|
68
|
+
const dir = makeFiles({
|
|
69
|
+
'app.mjs': "import contract from './missing.json' with { type: 'json' }\nexport const h = () => contract\n",
|
|
70
|
+
})
|
|
71
|
+
try {
|
|
72
|
+
const { errors } = resolveDepPaths(path.join(dir, 'app.mjs'), VERIFY)
|
|
73
|
+
assert.ok((errors || []).length > 0, 'missing.json is an error')
|
|
74
|
+
} finally {
|
|
75
|
+
fs.rmSync(dir, { recursive: true, force: true })
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
test.run()
|
package/types/dep-graph.d.ts
CHANGED
|
@@ -22,7 +22,8 @@ export function depGraph(filePath: string, options?: {
|
|
|
22
22
|
errors: Error[];
|
|
23
23
|
} | undefined;
|
|
24
24
|
/**
|
|
25
|
-
* Get all dependency file paths in build order (dependencies first), deduplicated
|
|
25
|
+
* Get all dependency file paths in build order (dependencies first), deduplicated.
|
|
26
|
+
* Files in an import cycle are kept together, in discovery order.
|
|
26
27
|
* @param {string} filePath - Entry file path
|
|
27
28
|
* @param {object} options - Options to pass to depGraph
|
|
28
29
|
* @param {boolean} [options.verifyGraph] - If true, verify imports/exports. Default: true
|
|
@@ -42,4 +43,6 @@ export function resolveDepPaths(filePath: string, options?: {
|
|
|
42
43
|
builtIns: string[];
|
|
43
44
|
errors?: Error[];
|
|
44
45
|
};
|
|
46
|
+
/** Drop memoized parses and resolutions (e.g. between runs in a long-lived process). */
|
|
47
|
+
export function clearDepGraphCache(): void;
|
|
45
48
|
//# 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":"AA6ZA;;;;;;;;;;;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;AAgJD;;;;;;;;;;GAUG;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,CAgD5F;AArkBD,wFAAwF;AACxF,2CAKC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-cjs-exports.d.ts","sourceRoot":"","sources":["../src/get-cjs-exports.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"get-cjs-exports.d.ts","sourceRoot":"","sources":["../src/get-cjs-exports.js"],"names":[],"mappings":"AAyCA,gDAoDC;AA+BD;;;;;;;;IAqGC;AAED;;;;;;;IAqBC;AAED;;;;;;;IAqBC;AAED;;;;;;;IAiDC;AAED;;;;;;;;;IAwEC"}
|
|
@@ -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,+DAsCC;AAED;;;;;;;;IAqDC;AAED;;;;;;IAeC;AAED,uEAuBC;AAED;;;;;;IAeC;AAED;;;;;;;IAgBC;AAED;;;;;;IAeC;AAED;;;;;;;;;;;;IA0BC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-imports.d.ts","sourceRoot":"","sources":["../src/get-imports.js"],"names":[],"mappings":"AAsCA;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"get-imports.d.ts","sourceRoot":"","sources":["../src/get-imports.js"],"names":[],"mappings":"AAsCA;;;;;;;;;;IAiBC;AAGD;;;;;;;;;;IAsCC;AAED;;;;;;;;;IAkBC;AAED;;;;;;;;;;;IAuBC;AAED;;;;;;;;;;;;;IA0BC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-requires.d.ts","sourceRoot":"","sources":["../src/get-requires.js"],"names":[],"mappings":"AA0GA;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"get-requires.d.ts","sourceRoot":"","sources":["../src/get-requires.js"],"names":[],"mappings":"AA0GA;;;;;;;;;;;IAWC;AAtCD;;;;;;;;;;;IAyBC;AA/FD;;;;;;;;;;;IA6BC"}
|
package/types/utils.d.ts
CHANGED
|
@@ -63,6 +63,14 @@ export function stripStringContents(code: string): string;
|
|
|
63
63
|
* @returns {string}
|
|
64
64
|
*/
|
|
65
65
|
export function stripCommentsAndStrings(code: string): string;
|
|
66
|
+
/**
|
|
67
|
+
* `stripStringContents(stripComments(code))`, the prelude of the import, require
|
|
68
|
+
* and export scanners (comments first, then strings: see getImports). Pure in
|
|
69
|
+
* `code`; the latest result is kept for the next scanner of the same text.
|
|
70
|
+
* @param {string} code
|
|
71
|
+
* @returns {string}
|
|
72
|
+
*/
|
|
73
|
+
export function stripCommentsThenStrings(code: string): string;
|
|
66
74
|
/**
|
|
67
75
|
* Check if a module specifier is a Node.js built-in
|
|
68
76
|
* @param {string} moduleName - The module specifier (e.g., 'fs', 'node:fs/promises')
|
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":"AA0RA;;;;;;GAMG;AACH,oCAHW,MAAM,GACJ,MAAM,CAIlB;AA1RD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,0CAHW,MAAM,GACJ,MAAM,CAuDlB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,8CAHW,MAAM,GACJ,MAAM,CAIlB;AAiLD;;;;;;GAMG;AACH,+CAHW,MAAM,GACJ,MAAM,CAQlB;AAED;;;;GAIG;AACH,sCAHW,MAAM,GACJ,OAAO,CAUnB;AAED,+DAEC;AAED,yEAKC;AAgFD,6DA+DC;AAGD,yDAqCC"}
|