monkeypatchlist 0.0.0-stage → 0.1.0
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/LICENSE +202 -0
- package/NOTICE +61 -0
- package/README.md +183 -2
- package/catalog/catalog.json +552211 -0
- package/catalog/catalog.schema.json +787 -0
- package/catalog/changelog.json +3 -0
- package/dist/bin/monkeypatchlist.d.ts +2 -0
- package/dist/bin/monkeypatchlist.js +3 -0
- package/dist/catalog.d.ts +561 -0
- package/dist/catalog.js +58 -0
- package/dist/cli.d.ts +84 -0
- package/dist/cli.js +470 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/lockfile-input.d.ts +9 -0
- package/dist/lockfile-input.js +91 -0
- package/dist/packages/analysis/binding.d.ts +2 -0
- package/dist/packages/analysis/binding.js +5 -0
- package/dist/packages/analysis/index.d.ts +10 -0
- package/dist/packages/analysis/index.js +13 -0
- package/dist/packages/analysis/job.d.ts +33 -0
- package/dist/packages/analysis/job.js +85 -0
- package/dist/packages/analysis/lib/absence-tags.d.ts +34 -0
- package/dist/packages/analysis/lib/absence-tags.js +340 -0
- package/dist/packages/analysis/lib/ast.d.ts +28 -0
- package/dist/packages/analysis/lib/ast.js +64 -0
- package/dist/packages/analysis/lib/binding.d.ts +36 -0
- package/dist/packages/analysis/lib/binding.js +66 -0
- package/dist/packages/analysis/lib/budget.d.ts +80 -0
- package/dist/packages/analysis/lib/budget.js +31 -0
- package/dist/packages/analysis/lib/catch-arms.d.ts +19 -0
- package/dist/packages/analysis/lib/catch-arms.js +351 -0
- package/dist/packages/analysis/lib/chunk-loads.d.ts +160 -0
- package/dist/packages/analysis/lib/chunk-loads.js +882 -0
- package/dist/packages/analysis/lib/chunk-registry.d.ts +45 -0
- package/dist/packages/analysis/lib/chunk-registry.js +334 -0
- package/dist/packages/analysis/lib/code-routes.d.ts +52 -0
- package/dist/packages/analysis/lib/code-routes.js +921 -0
- package/dist/packages/analysis/lib/collisions.d.ts +22 -0
- package/dist/packages/analysis/lib/collisions.js +103 -0
- package/dist/packages/analysis/lib/definitions.d.ts +76 -0
- package/dist/packages/analysis/lib/definitions.js +782 -0
- package/dist/packages/analysis/lib/early-exits.d.ts +39 -0
- package/dist/packages/analysis/lib/early-exits.js +161 -0
- package/dist/packages/analysis/lib/effect.d.ts +138 -0
- package/dist/packages/analysis/lib/effect.js +800 -0
- package/dist/packages/analysis/lib/global-object.d.ts +581 -0
- package/dist/packages/analysis/lib/global-object.js +8240 -0
- package/dist/packages/analysis/lib/keys.d.ts +33 -0
- package/dist/packages/analysis/lib/keys.js +82 -0
- package/dist/packages/analysis/lib/line-starts.d.ts +41 -0
- package/dist/packages/analysis/lib/line-starts.js +79 -0
- package/dist/packages/analysis/lib/link-group.d.ts +51 -0
- package/dist/packages/analysis/lib/link-group.js +487 -0
- package/dist/packages/analysis/lib/module-links.d.ts +117 -0
- package/dist/packages/analysis/lib/module-links.js +1060 -0
- package/dist/packages/analysis/lib/nested-code.d.ts +38 -0
- package/dist/packages/analysis/lib/nested-code.js +200 -0
- package/dist/packages/analysis/lib/numeric.d.ts +16 -0
- package/dist/packages/analysis/lib/numeric.js +216 -0
- package/dist/packages/analysis/lib/once-flags.d.ts +12 -0
- package/dist/packages/analysis/lib/once-flags.js +190 -0
- package/dist/packages/analysis/lib/parameter-arguments.d.ts +12 -0
- package/dist/packages/analysis/lib/parameter-arguments.js +34 -0
- package/dist/packages/analysis/lib/parse.d.ts +10 -0
- package/dist/packages/analysis/lib/parse.js +39 -0
- package/dist/packages/analysis/lib/partial.d.ts +60 -0
- package/dist/packages/analysis/lib/partial.js +518 -0
- package/dist/packages/analysis/lib/print.d.ts +3 -0
- package/dist/packages/analysis/lib/print.js +150 -0
- package/dist/packages/analysis/lib/reachability.d.ts +137 -0
- package/dist/packages/analysis/lib/reachability.js +1295 -0
- package/dist/packages/analysis/lib/resolver.d.ts +13 -0
- package/dist/packages/analysis/lib/resolver.js +2886 -0
- package/dist/packages/analysis/lib/restores.d.ts +42 -0
- package/dist/packages/analysis/lib/restores.js +482 -0
- package/dist/packages/analysis/lib/rewritten-deletes.d.ts +6 -0
- package/dist/packages/analysis/lib/rewritten-deletes.js +142 -0
- package/dist/packages/analysis/lib/scope.d.ts +110 -0
- package/dist/packages/analysis/lib/scope.js +443 -0
- package/dist/packages/analysis/lib/script-loads.d.ts +56 -0
- package/dist/packages/analysis/lib/script-loads.js +359 -0
- package/dist/packages/analysis/lib/summaries.d.ts +177 -0
- package/dist/packages/analysis/lib/summaries.js +498 -0
- package/dist/packages/analysis/lib/types.d.ts +632 -0
- package/dist/packages/analysis/lib/types.js +128 -0
- package/dist/packages/analysis/lib/wrap.d.ts +34 -0
- package/dist/packages/analysis/lib/wrap.js +407 -0
- package/dist/packages/analysis/lib/writes.d.ts +358 -0
- package/dist/packages/analysis/lib/writes.js +1612 -0
- package/dist/packages/analysis/visit-order.d.ts +3 -0
- package/dist/packages/analysis/visit-order.js +23 -0
- package/dist/packages/attribution/index.d.ts +233 -0
- package/dist/packages/attribution/index.js +444 -0
- package/dist/packages/attribution/lib/containing-package.d.ts +76 -0
- package/dist/packages/attribution/lib/containing-package.js +304 -0
- package/dist/packages/attribution/lib/first-party.d.ts +37 -0
- package/dist/packages/attribution/lib/first-party.js +153 -0
- package/dist/packages/attribution/lib/lockfile-graph.d.ts +99 -0
- package/dist/packages/attribution/lib/lockfile-graph.js +158 -0
- package/dist/packages/attribution/lib/lockfile.d.ts +33 -0
- package/dist/packages/attribution/lib/lockfile.js +330 -0
- package/dist/packages/attribution/lib/path-rules.d.ts +29 -0
- package/dist/packages/attribution/lib/path-rules.js +84 -0
- package/dist/packages/attribution/lib/pnpm.d.ts +4 -0
- package/dist/packages/attribution/lib/pnpm.js +133 -0
- package/dist/packages/attribution/lib/remote-url.d.ts +7 -0
- package/dist/packages/attribution/lib/remote-url.js +24 -0
- package/dist/packages/attribution/lib/source-map.d.ts +51 -0
- package/dist/packages/attribution/lib/source-map.js +125 -0
- package/dist/packages/attribution/lib/yaml.d.ts +9 -0
- package/dist/packages/attribution/lib/yaml.js +277 -0
- package/dist/packages/attribution/lib/yarn.d.ts +7 -0
- package/dist/packages/attribution/lib/yarn.js +227 -0
- package/dist/packages/baseline/index.d.ts +45 -0
- package/dist/packages/baseline/index.js +41 -0
- package/dist/packages/baseline/lib/error.d.ts +15 -0
- package/dist/packages/baseline/lib/error.js +25 -0
- package/dist/packages/baseline/lib/format.d.ts +49 -0
- package/dist/packages/baseline/lib/format.js +22 -0
- package/dist/packages/baseline/lib/migrations.d.ts +19 -0
- package/dist/packages/baseline/lib/migrations.js +7 -0
- package/dist/packages/baseline/lib/previous.d.ts +23 -0
- package/dist/packages/baseline/lib/previous.js +64 -0
- package/dist/packages/baseline/lib/read.d.ts +39 -0
- package/dist/packages/baseline/lib/read.js +219 -0
- package/dist/packages/baseline/lib/report.d.ts +25 -0
- package/dist/packages/baseline/lib/report.js +104 -0
- package/dist/packages/baseline/lib/skew.d.ts +51 -0
- package/dist/packages/baseline/lib/skew.js +75 -0
- package/dist/packages/compare/index.d.ts +120 -0
- package/dist/packages/compare/index.js +274 -0
- package/dist/packages/compare/lib/catalog-notes.d.ts +73 -0
- package/dist/packages/compare/lib/catalog-notes.js +224 -0
- package/dist/packages/completeness/index.d.ts +246 -0
- package/dist/packages/completeness/index.js +249 -0
- package/dist/packages/confidence/index.d.ts +54 -0
- package/dist/packages/confidence/index.js +71 -0
- package/dist/packages/config/index.d.ts +107 -0
- package/dist/packages/config/index.js +95 -0
- package/dist/packages/config/lib/command-line.d.ts +23 -0
- package/dist/packages/config/lib/command-line.js +239 -0
- package/dist/packages/config/lib/config-file.d.ts +11 -0
- package/dist/packages/config/lib/config-file.js +119 -0
- package/dist/packages/config/lib/scan-config-id.d.ts +28 -0
- package/dist/packages/config/lib/scan-config-id.js +54 -0
- package/dist/packages/config/lib/schema.d.ts +2 -0
- package/dist/packages/config/lib/schema.js +31 -0
- package/dist/packages/config/lib/types.d.ts +64 -0
- package/dist/packages/config/lib/types.js +6 -0
- package/dist/packages/config/lib/urls.d.ts +6 -0
- package/dist/packages/config/lib/urls.js +9 -0
- package/dist/packages/discovery/index.d.ts +277 -0
- package/dist/packages/discovery/index.js +1950 -0
- package/dist/packages/discovery/lib/import-map.d.ts +68 -0
- package/dist/packages/discovery/lib/import-map.js +180 -0
- package/dist/packages/discovery/lib/requests.d.ts +58 -0
- package/dist/packages/discovery/lib/requests.js +591 -0
- package/dist/packages/discovery/lib/scopes.d.ts +6 -0
- package/dist/packages/discovery/lib/scopes.js +286 -0
- package/dist/packages/discovery/lib/source-maps.d.ts +56 -0
- package/dist/packages/discovery/lib/source-maps.js +209 -0
- package/dist/packages/discovery/lib/swept-code.d.ts +27 -0
- package/dist/packages/discovery/lib/swept-code.js +107 -0
- package/dist/packages/discovery/lib/syntax.d.ts +17 -0
- package/dist/packages/discovery/lib/syntax.js +39 -0
- package/dist/packages/findings/index.d.ts +106 -0
- package/dist/packages/findings/index.js +254 -0
- package/dist/packages/findings/lib/scope.d.ts +38 -0
- package/dist/packages/findings/lib/scope.js +110 -0
- package/dist/packages/parallel/index.d.ts +2 -0
- package/dist/packages/parallel/index.js +5 -0
- package/dist/packages/parallel/lib/protocol.d.ts +15 -0
- package/dist/packages/parallel/lib/protocol.js +2 -0
- package/dist/packages/parallel/lib/runner.d.ts +36 -0
- package/dist/packages/parallel/lib/runner.js +96 -0
- package/dist/packages/parallel/lib/schedule.d.ts +16 -0
- package/dist/packages/parallel/lib/schedule.js +52 -0
- package/dist/packages/parallel/lib/worker.d.ts +1 -0
- package/dist/packages/parallel/lib/worker.js +18 -0
- package/dist/packages/policy/index.d.ts +36 -0
- package/dist/packages/policy/index.js +22 -0
- package/dist/packages/report/explain.d.ts +26 -0
- package/dist/packages/report/explain.js +99 -0
- package/dist/packages/report/index.d.ts +210 -0
- package/dist/packages/report/index.js +388 -0
- package/dist/packages/report/terminal.d.ts +11 -0
- package/dist/packages/report/terminal.js +331 -0
- package/dist/packages/source-store/fixture.d.ts +22 -0
- package/dist/packages/source-store/fixture.js +58 -0
- package/dist/packages/source-store/index.d.ts +278 -0
- package/dist/packages/source-store/index.js +322 -0
- package/dist/packages/source-store/lib/addresses.d.ts +6 -0
- package/dist/packages/source-store/lib/addresses.js +91 -0
- package/dist/packages/source-store/lib/connection.d.ts +15 -0
- package/dist/packages/source-store/lib/connection.js +135 -0
- package/dist/packages/source-store/lib/content-types.d.ts +16 -0
- package/dist/packages/source-store/lib/content-types.js +75 -0
- package/dist/packages/source-store/lib/data-urls.d.ts +10 -0
- package/dist/packages/source-store/lib/data-urls.js +85 -0
- package/dist/packages/source-store/lib/deadline.d.ts +18 -0
- package/dist/packages/source-store/lib/deadline.js +44 -0
- package/dist/packages/source-store/lib/exchange.d.ts +29 -0
- package/dist/packages/source-store/lib/exchange.js +112 -0
- package/dist/packages/source-store/lib/integrity.d.ts +19 -0
- package/dist/packages/source-store/lib/integrity.js +86 -0
- package/dist/packages/source-store/lib/outcomes.d.ts +12 -0
- package/dist/packages/source-store/lib/outcomes.js +22 -0
- package/dist/packages/source-store/lib/proxy.d.ts +11 -0
- package/dist/packages/source-store/lib/proxy.js +53 -0
- package/dist/packages/source-store/lib/session.d.ts +12 -0
- package/dist/packages/source-store/lib/session.js +139 -0
- package/dist/packages/source-store/lib/urls.d.ts +6 -0
- package/dist/packages/source-store/lib/urls.js +15 -0
- package/dist/packages/source-store/resolve.d.ts +59 -0
- package/dist/packages/source-store/resolve.js +45 -0
- package/dist/scan.d.ts +65 -0
- package/dist/scan.js +592 -0
- package/package.json +95 -4
- package/schema/config.schema.json +133 -0
- package/schema/report.schema.json +1905 -0
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
// The containing package (§10 Containing package): read from a source map's raw `sources` entry alone, after
|
|
2
|
+
// normalizing it.
|
|
3
|
+
/**
|
|
4
|
+
* The relative `sourceRoot` a map's entries may be joined to (§10 step 1), or null: a URL or an absolute or
|
|
5
|
+
* drive-letter root is ignored, and so is an empty or non-string one.
|
|
6
|
+
*/
|
|
7
|
+
export function relativeSourceRoot(sourceRoot) {
|
|
8
|
+
if (typeof sourceRoot !== 'string' || sourceRoot === '')
|
|
9
|
+
return null;
|
|
10
|
+
if (scheme.test(sourceRoot) || absolute(sourceRoot))
|
|
11
|
+
return null;
|
|
12
|
+
return sourceRoot;
|
|
13
|
+
}
|
|
14
|
+
/** Whether a path is absolute: it starts with `/` or `\`, or with a drive letter. */
|
|
15
|
+
function absolute(path) {
|
|
16
|
+
return /^[/\\]/.test(path) || driveLetter.test(path);
|
|
17
|
+
}
|
|
18
|
+
/** A URL scheme at the start of a string. */
|
|
19
|
+
const scheme = /^[A-Za-z][A-Za-z0-9+.-]*:/;
|
|
20
|
+
/** A drive letter at the start of a path, which is no scheme. */
|
|
21
|
+
const driveLetter = /^[A-Za-z]:(?:[/\\]|$)/;
|
|
22
|
+
/**
|
|
23
|
+
* The recognized schemes (§10 Source schemes), each with the prefix stripped before the path is read. A relative, an
|
|
24
|
+
* absolute or a drive-letter path has none. `turbopack:///[turbopack]/…` is the bundler's runtime.
|
|
25
|
+
*/
|
|
26
|
+
const recognized = [
|
|
27
|
+
[/^webpack:\/\/([^/]*)(?:\/|$)/, (match) => `webpack:${match[1]}`],
|
|
28
|
+
[/^webpack-internal:\/\/\//, () => 'webpack-internal'],
|
|
29
|
+
[/^turbopack:\/\/\/\[project\]\//, () => 'turbopack'],
|
|
30
|
+
[/^vite:/, () => 'vite'],
|
|
31
|
+
[/^file:\/\//, () => 'absolute'],
|
|
32
|
+
];
|
|
33
|
+
/** The synthetic and bundler-runtime sources, once the scheme is stripped (§10 step 1). */
|
|
34
|
+
const synthetic = [
|
|
35
|
+
/^webpack\/runtime\//,
|
|
36
|
+
/^webpack\/bootstrap/,
|
|
37
|
+
/^<anon>$/,
|
|
38
|
+
/^ignored\|/,
|
|
39
|
+
/^external\s/,
|
|
40
|
+
];
|
|
41
|
+
/**
|
|
42
|
+
* What a raw `sources` entry names, under a map's relative `sourceRoot` (§10 Containing package):
|
|
43
|
+
*
|
|
44
|
+
* 1. **Normalize.** A null or empty entry, or one holding `\0` (a Rollup virtual module), is synthetic. A BOM or a
|
|
45
|
+
* `)]}'` prefix is stripped, then a recognized scheme; any other scheme is foreign. A synthetic source is then
|
|
46
|
+
* recognized on the entry, before any root is joined: `webpack/runtime/*`, `webpack/bootstrap`, `<anon>`,
|
|
47
|
+
* `turbopack:///[turbopack]/…`, `ignored|…` and `external "x"`, and an entry whose resource path is empty.
|
|
48
|
+
* Only an entry that had no scheme and isn't absolute is joined to the root, as a string, with a `/` between them
|
|
49
|
+
* unless the root ends with one. Loader prefixes (up to the last `!`), a `?query` (webpack's `?hash`
|
|
50
|
+
* included) and `*` suffixes are stripped, so the resource path decides. The path is percent-decoded, `\` becomes
|
|
51
|
+
* `/`, and `.` and `x/..` segments collapse (leading `..` stay). A `file://`, absolute or drive-letter path is
|
|
52
|
+
* `absolute`, whatever scheme wraps it.
|
|
53
|
+
* 2. **Read the name** from the segment after the last `node_modules` segment that a dot segment doesn't follow, or the
|
|
54
|
+
* two segments after it for a scoped package. Under Yarn's pnpm linker the package is in
|
|
55
|
+
* `node_modules/.store/<slug>-npm-<version>-<hash>/package/` (or `<slug>-virtual-<hash>`): an unscoped slug is its
|
|
56
|
+
* name, and a scoped one is lossy. A path that climbs out of its package inside `node_modules` names none.
|
|
57
|
+
*/
|
|
58
|
+
export function sourceKind(source, sourceRoot) {
|
|
59
|
+
if (source === null || source === '' || source.includes('\0'))
|
|
60
|
+
return { kind: 'synthetic' };
|
|
61
|
+
let path = source;
|
|
62
|
+
if (path.startsWith('\ufeff'))
|
|
63
|
+
path = path.slice(1);
|
|
64
|
+
if (path.startsWith(")]}'"))
|
|
65
|
+
path = path.slice(4);
|
|
66
|
+
if (/^turbopack:\/\/\/\[turbopack\]\//.test(path))
|
|
67
|
+
return { kind: 'synthetic' };
|
|
68
|
+
const match = recognized.map(([pattern, name]) => [pattern.exec(path), name]).find(([found]) => found);
|
|
69
|
+
let namespace = 'relative';
|
|
70
|
+
if (match !== undefined) {
|
|
71
|
+
const [found, name] = match;
|
|
72
|
+
namespace = name(found);
|
|
73
|
+
path = path.slice(found[0].length);
|
|
74
|
+
// `file:///C:/x` names the drive-letter path `C:/x`.
|
|
75
|
+
if (namespace === 'absolute' && /^\/[A-Za-z]:(?:[/\\]|$)/.test(path))
|
|
76
|
+
path = path.slice(1);
|
|
77
|
+
}
|
|
78
|
+
else if (scheme.test(path) && !driveLetter.test(path))
|
|
79
|
+
return { kind: 'foreign-scheme' };
|
|
80
|
+
if (synthetic.some((pattern) => pattern.test(path)))
|
|
81
|
+
return { kind: 'synthetic' };
|
|
82
|
+
// An entry whose resource path is empty once the namespace, `./` and `?query` are stripped is synthetic too, such
|
|
83
|
+
// as Next's context and hash stubs `webpack://_N_E/?6505`, whatever the root.
|
|
84
|
+
if (segments(normalized(resourcePath(path))).length === 0)
|
|
85
|
+
return { kind: 'synthetic' };
|
|
86
|
+
if (absolute(path))
|
|
87
|
+
namespace = 'absolute';
|
|
88
|
+
else if (namespace === 'relative' && sourceRoot !== null) {
|
|
89
|
+
path = `${sourceRoot}${sourceRoot.endsWith('/') ? '' : '/'}${path}`;
|
|
90
|
+
}
|
|
91
|
+
const read = segments(normalized(resourcePath(path)));
|
|
92
|
+
return packageOf(read, { namespace, segments: read });
|
|
93
|
+
}
|
|
94
|
+
/** A path with its loader prefixes (up to the last `!`), `?query` (from the first `?`) and `*` suffixes stripped. */
|
|
95
|
+
function resourcePath(path) {
|
|
96
|
+
const resource = path.slice(path.lastIndexOf('!') + 1);
|
|
97
|
+
const query = resource.indexOf('?');
|
|
98
|
+
return (query >= 0 ? resource.slice(0, query) : resource).replace(/\*+$/, '');
|
|
99
|
+
}
|
|
100
|
+
/** A path percent-decoded, with POSIX separators. */
|
|
101
|
+
const normalized = (path) => percentDecoded(path).replaceAll('\\', '/');
|
|
102
|
+
/** The path's segments, with empty and `.` segments dropped and each `x/..` collapsed. Leading `..` segments stay. */
|
|
103
|
+
function segments(path) {
|
|
104
|
+
const out = [];
|
|
105
|
+
for (const segment of path.split('/')) {
|
|
106
|
+
if (segment === '' || segment === '.')
|
|
107
|
+
continue;
|
|
108
|
+
if (segment === '..' && out.length > 0 && out.at(-1) !== '..')
|
|
109
|
+
out.pop();
|
|
110
|
+
else
|
|
111
|
+
out.push(segment);
|
|
112
|
+
}
|
|
113
|
+
return out;
|
|
114
|
+
}
|
|
115
|
+
/** A Yarn pnpm-linker store entry's directory: `<slug>-npm-<version>-<hash>` or `<slug>-virtual-<hash>`. */
|
|
116
|
+
const yarnStoreEntry = /^(.+)-(?:npm-.+|virtual)-[0-9a-f]{10}$/;
|
|
117
|
+
/** A Yarn pnpm-linker store entry's directory with a version: `<slug>-npm-<version>-<hash>`. */
|
|
118
|
+
const yarnStoreVersion = /^(.+)-npm-(.+)-[0-9a-f]{10}$/;
|
|
119
|
+
/** A Yarn cache archive, local or global: `<slug>-npm-<version>-<hash>[-<hash>].zip`. */
|
|
120
|
+
const yarnArchive = /^(.+)-npm-(.+?)-[0-9a-f]{10}(?:-[0-9a-f]+)?\.zip$/;
|
|
121
|
+
/**
|
|
122
|
+
* A semver version at the start of a store entry's version part, and what may follow it: nothing, or pnpm's peer and
|
|
123
|
+
* patch suffixes (`_react@18.2.0`, `(react@18.2.0)`).
|
|
124
|
+
*/
|
|
125
|
+
const storeVersion = /^(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?)(?:[_(].*)?$/;
|
|
126
|
+
/** The version a store entry's version part gives, or null when it isn't a registry version (a git or file spec). */
|
|
127
|
+
function versionIn(part) {
|
|
128
|
+
return storeVersion.exec(part)?.[1] ?? null;
|
|
129
|
+
}
|
|
130
|
+
/** Yarn's slug of a package name: a scoped name's `/` becomes `-`. */
|
|
131
|
+
const yarnSlug = (name) => name.replace('/', '-');
|
|
132
|
+
/**
|
|
133
|
+
* The version a store path gives the package `name`, whose `node_modules` segment is at `at` (§10 Version): only when
|
|
134
|
+
* the store entry right in front of that segment names the package itself, so a package inside another package's entry
|
|
135
|
+
* (vendored code) gets none.
|
|
136
|
+
*
|
|
137
|
+
* - pnpm: `.pnpm/<name>@<version>/node_modules/<name>`, a scope's `/` written `+`
|
|
138
|
+
* - Bun: `.bun/<name>@<version>/node_modules/<name>`, likewise
|
|
139
|
+
* - Yarn: `<slug>-npm-<version>-<hash>.zip/node_modules/<name>`, in the project's cache or the global one
|
|
140
|
+
* - pnpm's global virtual store: `<store>/v<n>/links/<name>/<version>/<hash>/node_modules/<name>`
|
|
141
|
+
*/
|
|
142
|
+
function storePathVersion(path, at, name) {
|
|
143
|
+
const entry = path[at - 1];
|
|
144
|
+
if (entry === undefined)
|
|
145
|
+
return null;
|
|
146
|
+
const store = path[at - 2];
|
|
147
|
+
if (store === '.pnpm' || store === '.bun') {
|
|
148
|
+
const prefix = `${name.replace('/', '+')}@`;
|
|
149
|
+
return entry.startsWith(prefix) ? versionIn(entry.slice(prefix.length)) : null;
|
|
150
|
+
}
|
|
151
|
+
const archive = yarnArchive.exec(entry);
|
|
152
|
+
if (archive !== null)
|
|
153
|
+
return archive[1] === yarnSlug(name) ? versionIn(archive[2]) : null;
|
|
154
|
+
const parts = name.split('/');
|
|
155
|
+
const links = at - 3 - parts.length;
|
|
156
|
+
if (path[links] === 'links' && /^v\d+$/.test(path[links - 1] ?? '')) {
|
|
157
|
+
const named = parts.every((part, k) => path[links + 1 + k] === part);
|
|
158
|
+
return named ? versionIn(path[at - 2]) : null;
|
|
159
|
+
}
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* The vendoring package of the package `name`, whose `node_modules` segment is at `at`, as `name@version` (§10
|
|
164
|
+
* Provenance, vendored code; ADR 0006), or null: a **store-name mismatch**, where the nearest store entry the path lies
|
|
165
|
+
* in names another package. Every store layout §10 Version reads counts:
|
|
166
|
+
*
|
|
167
|
+
* - pnpm `.pnpm/<n>@<v>` and Bun `.bun/<n>@<v>`, a scope's `/` written `+`
|
|
168
|
+
* - Yarn's cache archives `<slug>-npm-<v>-<hash>.zip`, named by an unscoped slug, or for a scoped slug by the package
|
|
169
|
+
* directory inside the archive
|
|
170
|
+
* - Yarn's pnpm-linker store `.store/<slug>-npm-<v>-<hash>`, named by an unscoped slug
|
|
171
|
+
* - pnpm's global virtual store `v<n>/links/<name>/<v>/<hash>`
|
|
172
|
+
*
|
|
173
|
+
* So `.pnpm/telemetry-agent@1.2.0/node_modules/telemetry-agent/node_modules/tiny-helper/…` and the collapsed
|
|
174
|
+
* `.pnpm/telemetry-agent@1.2.0/node_modules/tiny-helper/…`, which a hoisted build of telemetry-agent composes, are both
|
|
175
|
+
* tiny-helper vendored in telemetry-agent@1.2.0. A store entry that gives no registry version (a git or file
|
|
176
|
+
* dependency), or whose package can't be named (a lossy scoped slug), detects nothing.
|
|
177
|
+
*/
|
|
178
|
+
function vendoringPackage(path, at, name) {
|
|
179
|
+
for (let j = at - 1; j >= 0; j--) {
|
|
180
|
+
const owner = storeOwner(path, j, at);
|
|
181
|
+
if (owner === undefined)
|
|
182
|
+
continue;
|
|
183
|
+
return owner === null || owner.name === name ? null : `${owner.name}@${owner.version}`;
|
|
184
|
+
}
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* The package the store entry at `j` names, when `path[j]` is a store entry before the segment `at`: null when it is
|
|
189
|
+
* one but can't be named with a registry version, undefined when it is none.
|
|
190
|
+
*/
|
|
191
|
+
function storeOwner(path, j, at) {
|
|
192
|
+
const entry = path[j];
|
|
193
|
+
const store = path[j - 1];
|
|
194
|
+
if ((store === '.pnpm' || store === '.bun') && path[j + 1] === 'node_modules') {
|
|
195
|
+
const split = entry.indexOf('@', 1);
|
|
196
|
+
if (split <= 0)
|
|
197
|
+
return null;
|
|
198
|
+
const version = versionIn(entry.slice(split + 1));
|
|
199
|
+
const named = entry.slice(0, split);
|
|
200
|
+
const name = named.startsWith('@') ? named.replace('+', '/') : named;
|
|
201
|
+
return version === null ? null : { name, version };
|
|
202
|
+
}
|
|
203
|
+
const archive = yarnArchive.exec(entry);
|
|
204
|
+
if (archive !== null && path[j + 1] === 'node_modules') {
|
|
205
|
+
const version = versionIn(archive[2]);
|
|
206
|
+
if (version === null)
|
|
207
|
+
return null;
|
|
208
|
+
if (!archive[1].startsWith('@'))
|
|
209
|
+
return { name: archive[1], version };
|
|
210
|
+
// A scoped slug is lossy: the package directory inside the archive names it, when the path passes through it.
|
|
211
|
+
const scope = path[j + 2];
|
|
212
|
+
const inner = path[j + 3];
|
|
213
|
+
if (scope?.startsWith('@') !== true || inner === undefined || j + 3 >= at)
|
|
214
|
+
return null;
|
|
215
|
+
const name = `${scope}/${inner}`;
|
|
216
|
+
return yarnSlug(name) === archive[1] ? { name, version } : null;
|
|
217
|
+
}
|
|
218
|
+
if (store === '.store' && path[j - 2] === 'node_modules' && yarnStoreEntry.test(entry)) {
|
|
219
|
+
const versioned = yarnStoreVersion.exec(entry);
|
|
220
|
+
const version = versioned === null ? null : versionIn(versioned[2]);
|
|
221
|
+
if (versioned === null || version === null || versioned[1].startsWith('@'))
|
|
222
|
+
return null;
|
|
223
|
+
return { name: versioned[1], version };
|
|
224
|
+
}
|
|
225
|
+
if (path[j + 1] === 'node_modules') {
|
|
226
|
+
for (const parts of [1, 2]) {
|
|
227
|
+
const links = j - 2 - parts;
|
|
228
|
+
if (path[links] !== 'links' || !/^v\d+$/.test(path[links - 1] ?? ''))
|
|
229
|
+
continue;
|
|
230
|
+
const name = path.slice(links + 1, links + 1 + parts).join('/');
|
|
231
|
+
if (parts === 2 && !name.startsWith('@'))
|
|
232
|
+
continue;
|
|
233
|
+
const version = versionIn(path[j - 1]);
|
|
234
|
+
return version === null ? null : { name, version };
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return undefined;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Whether a normalized path climbs out of its own package inside `node_modules` (§10 Escaped sources): it holds,
|
|
241
|
+
* as whole segments anywhere, `node_modules/src/`, `node_modules/@<scope>/src/` or `node_modules/node_modules/`, as
|
|
242
|
+
* esbuild gives when it composes a dependency's own shipped maps (Sentry 7's `node_modules/@sentry/src/…`).
|
|
243
|
+
*/
|
|
244
|
+
function climbsOutOfPackage(path) {
|
|
245
|
+
return path.some((segment, i) => {
|
|
246
|
+
if (segment !== 'node_modules')
|
|
247
|
+
return false;
|
|
248
|
+
const next = path[i + 1];
|
|
249
|
+
if (next === 'node_modules' || next === 'src')
|
|
250
|
+
return i + 2 < path.length;
|
|
251
|
+
return next?.startsWith('@') === true && path[i + 2] === 'src' && i + 3 < path.length;
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
/** The package a normalized path is in: the last `node_modules` segment that a dot segment doesn't follow decides. */
|
|
255
|
+
function packageOf(path, read) {
|
|
256
|
+
if (climbsOutOfPackage(path))
|
|
257
|
+
return { kind: 'escaped', path: read };
|
|
258
|
+
for (let i = path.length - 1; i >= 0; i--) {
|
|
259
|
+
if (path[i] !== 'node_modules')
|
|
260
|
+
continue;
|
|
261
|
+
const next = path[i + 1];
|
|
262
|
+
if (next === undefined)
|
|
263
|
+
continue;
|
|
264
|
+
if (next.startsWith('.')) {
|
|
265
|
+
const slug = next === '.store' && path[i + 3] === 'package' ? yarnStoreEntry.exec(path[i + 2] ?? '')?.[1] : undefined;
|
|
266
|
+
if (slug === undefined)
|
|
267
|
+
continue;
|
|
268
|
+
if (slug.startsWith('@'))
|
|
269
|
+
return { kind: 'yarn-slug', slug, path: read };
|
|
270
|
+
const versioned = yarnStoreVersion.exec(path[i + 2]);
|
|
271
|
+
const version = versioned === null ? null : versionIn(versioned[2]);
|
|
272
|
+
const directory = path.slice(0, i + 4);
|
|
273
|
+
const vendoredBy = vendoringPackage(path, i, slug);
|
|
274
|
+
return { kind: 'package', name: slug, subpath: path.slice(i + 4), path: read, directory, version, vendoredBy };
|
|
275
|
+
}
|
|
276
|
+
const scoped = next.startsWith('@');
|
|
277
|
+
if (scoped && path[i + 2] === undefined)
|
|
278
|
+
continue;
|
|
279
|
+
const name = scoped ? `${next}/${path[i + 2]}` : next;
|
|
280
|
+
const end = i + (scoped ? 3 : 2);
|
|
281
|
+
const version = storePathVersion(path, i, name);
|
|
282
|
+
const vendoredBy = vendoringPackage(path, i, name);
|
|
283
|
+
return { kind: 'package', name, subpath: path.slice(end), path: read, directory: path.slice(0, end), version, vendoredBy };
|
|
284
|
+
}
|
|
285
|
+
return { kind: 'other', path: read };
|
|
286
|
+
}
|
|
287
|
+
const decoder = new TextDecoder('utf-8');
|
|
288
|
+
/** A path percent-decoded as UTF-8 (URL § percent-decode): an invalid escape is left as it is. */
|
|
289
|
+
function percentDecoded(text) {
|
|
290
|
+
if (!text.includes('%'))
|
|
291
|
+
return text;
|
|
292
|
+
const bytes = [...new TextEncoder().encode(text)];
|
|
293
|
+
const out = [];
|
|
294
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
295
|
+
const hex = String.fromCharCode(bytes[i + 1] ?? 0, bytes[i + 2] ?? 0);
|
|
296
|
+
if (bytes[i] === 0x25 && /^[0-9a-f]{2}$/i.test(hex)) {
|
|
297
|
+
out.push(Number.parseInt(hex, 16));
|
|
298
|
+
i += 2;
|
|
299
|
+
}
|
|
300
|
+
else
|
|
301
|
+
out.push(bytes[i]);
|
|
302
|
+
}
|
|
303
|
+
return decoder.decode(Uint8Array.from(out));
|
|
304
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { SourceKind, SourcePath } from './containing-package.ts';
|
|
2
|
+
/**
|
|
3
|
+
* The frame of a map, for its relative sources: its build-relative directory's segments (§10), or null for a
|
|
4
|
+
* fetched map, which has no place in the build. A relative entry is resolved against it.
|
|
5
|
+
*/
|
|
6
|
+
export type MapDirectory = readonly string[] | null;
|
|
7
|
+
/** A verified map of local-build code, as root inference reads it: its sources' kinds, and its directory. */
|
|
8
|
+
export interface MapSources {
|
|
9
|
+
kinds: readonly SourceKind[];
|
|
10
|
+
directory: MapDirectory;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* How a source that names no package is attributed (§10 Provenance):
|
|
14
|
+
* - `first-party`: inside its namespace's inferred root, named by its root-relative `path`, and `inferred` when that
|
|
15
|
+
* root lies above the bundler base (§9 `first-party-inferred`)
|
|
16
|
+
* - `outside`: outside the root, or in a namespace with none, so it is escaped
|
|
17
|
+
* - `unplaced`: a relative entry of a map with no place in the build, a fetched one, whose code is remote and keyed
|
|
18
|
+
* by its URL, not by a root
|
|
19
|
+
*/
|
|
20
|
+
export type Classified = {
|
|
21
|
+
kind: 'first-party';
|
|
22
|
+
path: string;
|
|
23
|
+
inferred: boolean;
|
|
24
|
+
} | {
|
|
25
|
+
kind: 'outside';
|
|
26
|
+
} | {
|
|
27
|
+
kind: 'unplaced';
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Infers one root per bundler namespace across the scan (§10 Provenance): the common ancestor of every
|
|
31
|
+
* in-project package-source prefix in it. A namespace with none takes the bundler's own base: the webpack context or
|
|
32
|
+
* the project root for `webpack://`, `webpack-internal:///`, `turbopack:///[project]/` and `vite:` paths, and for
|
|
33
|
+
* relative paths each map's own directory (§10 Roots with no shared place). Absolute paths, under every wrapper, share
|
|
34
|
+
* one namespace with no base, so with no package source among them they have no root. Returns how each source
|
|
35
|
+
* that names no package is attributed.
|
|
36
|
+
*/
|
|
37
|
+
export declare function firstPartyRoots(maps: readonly MapSources[]): (path: SourcePath, directory: MapDirectory) => Classified;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// First-party code (§10 Provenance, §11 Attribution keys): the inferred root of each bundler namespace, and the
|
|
2
|
+
// root-relative path first-party code is named by.
|
|
3
|
+
/**
|
|
4
|
+
* A source's path resolved into its namespace's frame: a relative entry against its map's build-relative
|
|
5
|
+
* directory, into the one namespace the relative-path maps of the build share, and any other path as it is. Null for
|
|
6
|
+
* a relative entry of a map whose directory isn't known.
|
|
7
|
+
*/
|
|
8
|
+
function resolved(path, directory) {
|
|
9
|
+
const segments = path.namespace === 'relative' ? (directory === null ? null : [...directory, ...path.segments]) : path.segments;
|
|
10
|
+
if (segments === null)
|
|
11
|
+
return null;
|
|
12
|
+
return { namespace: path.namespace, place: placeOf(collapsed(segments)) };
|
|
13
|
+
}
|
|
14
|
+
/** Segments with each `x/..` collapsed; leading `..` segments stay. */
|
|
15
|
+
function collapsed(segments) {
|
|
16
|
+
const out = [];
|
|
17
|
+
for (const segment of segments) {
|
|
18
|
+
if (segment === '..' && out.length > 0 && out.at(-1) !== '..')
|
|
19
|
+
out.pop();
|
|
20
|
+
else
|
|
21
|
+
out.push(segment);
|
|
22
|
+
}
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
25
|
+
function placeOf(segments) {
|
|
26
|
+
let up = 0;
|
|
27
|
+
while (segments[up] === '..')
|
|
28
|
+
up++;
|
|
29
|
+
return { up, down: segments.slice(up) };
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The in-project package-source prefix of a package source (§10 Provenance): the place in front of its first
|
|
33
|
+
* `node_modules` or `.yarn` segment. Null for a source in the pnpm or Bun global store or Yarn's global cache, which
|
|
34
|
+
* don't count.
|
|
35
|
+
*/
|
|
36
|
+
function packagePrefix(place) {
|
|
37
|
+
const { down } = place;
|
|
38
|
+
if (globalStore(down))
|
|
39
|
+
return null;
|
|
40
|
+
const at = down.findIndex((segment) => segment === 'node_modules' || segment === '.yarn');
|
|
41
|
+
if (at < 0)
|
|
42
|
+
return null;
|
|
43
|
+
return { up: place.up, down: down.slice(0, at) };
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Whether a path lies in a global store: the pnpm global virtual store (`<store>/v<n>/links/…`), Bun's
|
|
47
|
+
* (`.bun/install/cache/…`), or Yarn's global cache (`.yarn/berry/cache/…`).
|
|
48
|
+
*/
|
|
49
|
+
function globalStore(down) {
|
|
50
|
+
return down.some((segment, i) => (segment === 'links' && /^v\d+$/.test(down[i - 1] ?? '')) ||
|
|
51
|
+
(segment === '.bun' && down[i + 1] === 'install' && down[i + 2] === 'cache') ||
|
|
52
|
+
(segment === '.yarn' && down[i + 1] === 'berry' && down[i + 2] === 'cache'));
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* A place as tokens in a frame `frame` levels above the namespace's: each level of the frame the place doesn't name is
|
|
56
|
+
* a marker, which stands for the same unnamed directory in every place, and differs from every named segment.
|
|
57
|
+
*/
|
|
58
|
+
function tokens(place, frame) {
|
|
59
|
+
return [...Array.from({ length: frame - place.up }, (_, level) => `\0${level}`), ...place.down];
|
|
60
|
+
}
|
|
61
|
+
/** The common ancestor of places in one namespace's frame (§10 Provenance). */
|
|
62
|
+
function commonAncestor(places) {
|
|
63
|
+
const frame = Math.max(...places.map(({ up }) => up));
|
|
64
|
+
const all = places.map((place) => tokens(place, frame));
|
|
65
|
+
const [first = []] = all;
|
|
66
|
+
let length = 0;
|
|
67
|
+
while (length < first.length && all.every((each) => each[length] === first[length]))
|
|
68
|
+
length++;
|
|
69
|
+
const common = first.slice(0, length);
|
|
70
|
+
const markers = common.filter((token) => token.startsWith('\0')).length;
|
|
71
|
+
return { up: frame - markers, down: common.slice(markers) };
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The root-relative path of a place under `root` (§11 Root-relative paths), or null when the place lies outside
|
|
75
|
+
* it. The root's prefix is stripped where the path has it, and a level of the frame the path climbs to without naming
|
|
76
|
+
* it is dropped with its `../`.
|
|
77
|
+
*/
|
|
78
|
+
function rootRelative(place, root) {
|
|
79
|
+
const frame = Math.max(place.up, root.up);
|
|
80
|
+
const path = tokens(place, frame);
|
|
81
|
+
const prefix = tokens(root, frame);
|
|
82
|
+
if (!prefix.every((token, i) => path[i] === token))
|
|
83
|
+
return null;
|
|
84
|
+
const rest = path.slice(prefix.length).filter((token) => !token.startsWith('\0'));
|
|
85
|
+
return rest.length === 0 ? null : rest.join('/');
|
|
86
|
+
}
|
|
87
|
+
/** Whether `root` lies strictly above `base`: it is an ancestor of `base`, and not `base` itself (§9). */
|
|
88
|
+
function above(root, base) {
|
|
89
|
+
const frame = Math.max(root.up, base.up);
|
|
90
|
+
const ancestor = tokens(root, frame);
|
|
91
|
+
const of = tokens(base, frame);
|
|
92
|
+
return ancestor.length < of.length && ancestor.every((token, i) => of[i] === token);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* The bundler namespaces whose base is the webpack context or the project root, the frame of their paths. `vite:` is
|
|
96
|
+
* based like webpack (§10 Roots with no shared place).
|
|
97
|
+
*/
|
|
98
|
+
const basedNamespaces = /^(?:webpack:.*|webpack-internal|turbopack|vite)$/;
|
|
99
|
+
/**
|
|
100
|
+
* Infers one root per bundler namespace across the scan (§10 Provenance): the common ancestor of every
|
|
101
|
+
* in-project package-source prefix in it. A namespace with none takes the bundler's own base: the webpack context or
|
|
102
|
+
* the project root for `webpack://`, `webpack-internal:///`, `turbopack:///[project]/` and `vite:` paths, and for
|
|
103
|
+
* relative paths each map's own directory (§10 Roots with no shared place). Absolute paths, under every wrapper, share
|
|
104
|
+
* one namespace with no base, so with no package source among them they have no root. Returns how each source
|
|
105
|
+
* that names no package is attributed.
|
|
106
|
+
*/
|
|
107
|
+
export function firstPartyRoots(maps) {
|
|
108
|
+
const prefixes = new Map();
|
|
109
|
+
for (const { kinds, directory } of maps) {
|
|
110
|
+
for (const kind of kinds) {
|
|
111
|
+
if (kind.kind === 'synthetic' || kind.kind === 'foreign-scheme' || kind.kind === 'other')
|
|
112
|
+
continue;
|
|
113
|
+
const at = resolved(kind.path, directory);
|
|
114
|
+
const prefix = at === null ? null : packagePrefix(at.place);
|
|
115
|
+
if (at === null || prefix === null)
|
|
116
|
+
continue;
|
|
117
|
+
prefixes.set(at.namespace, [...(prefixes.get(at.namespace) ?? []), prefix]);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const roots = new Map();
|
|
121
|
+
const rootOf = (namespace) => {
|
|
122
|
+
let root = roots.get(namespace);
|
|
123
|
+
if (root !== undefined)
|
|
124
|
+
return root;
|
|
125
|
+
const found = prefixes.get(namespace);
|
|
126
|
+
if (found !== undefined)
|
|
127
|
+
root = commonAncestor(found);
|
|
128
|
+
else if (basedNamespaces.test(namespace))
|
|
129
|
+
root = { up: 0, down: [] };
|
|
130
|
+
// Relative maps with no package source anywhere each take their own directory as root (§10 Roots with no shared
|
|
131
|
+
// place).
|
|
132
|
+
else if (namespace === 'relative')
|
|
133
|
+
root = 'own';
|
|
134
|
+
else
|
|
135
|
+
root = 'none';
|
|
136
|
+
roots.set(namespace, root);
|
|
137
|
+
return root;
|
|
138
|
+
};
|
|
139
|
+
return (path, directory) => {
|
|
140
|
+
const at = resolved(path, directory);
|
|
141
|
+
if (at === null)
|
|
142
|
+
return { kind: 'unplaced' };
|
|
143
|
+
// The bundler base: the map's directory for relative paths, the webpack context or project root otherwise. An
|
|
144
|
+
// absolute path has none.
|
|
145
|
+
const base = at.namespace === 'relative' ? placeOf(directory ?? []) : at.namespace === 'absolute' ? null : { up: 0, down: [] };
|
|
146
|
+
const found = rootOf(at.namespace);
|
|
147
|
+
const root = found === 'own' ? base : found;
|
|
148
|
+
const name = root === 'none' || root === null ? null : rootRelative(at.place, root);
|
|
149
|
+
if (root === 'none' || root === null || name === null)
|
|
150
|
+
return { kind: 'outside' };
|
|
151
|
+
return { kind: 'first-party', path: name, inferred: base !== null && above(root, base) };
|
|
152
|
+
};
|
|
153
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/** An installed package: its key in the lockfile, its name and version (`''` when the lockfile records none). */
|
|
2
|
+
export interface Node {
|
|
3
|
+
key: string;
|
|
4
|
+
name: string;
|
|
5
|
+
version: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* A workspace (importer): its folder relative to the lockfile's directory (`''` for the root), and its name and version
|
|
9
|
+
* as the lockfile records them, which are evidence (§10 Provenance) and what `lockfile.importer` may name.
|
|
10
|
+
*/
|
|
11
|
+
export interface Workspace {
|
|
12
|
+
folder: string;
|
|
13
|
+
name: string | null;
|
|
14
|
+
version: string | null;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Where a lockfile puts a package directory (§10 Walk, Vendored):
|
|
18
|
+
* - `installed`: the nodes it is (several only for one version's peer variants)
|
|
19
|
+
* - `vendored`: the lockfile says it isn't installed; `around` is the installed package whose directory holds it, if any
|
|
20
|
+
* - `unplaced`: the lockfile can't say which copy it is
|
|
21
|
+
*/
|
|
22
|
+
export type Placed = {
|
|
23
|
+
kind: 'installed';
|
|
24
|
+
nodes: Node[];
|
|
25
|
+
} | {
|
|
26
|
+
kind: 'vendored';
|
|
27
|
+
around: Node[];
|
|
28
|
+
} | {
|
|
29
|
+
kind: 'unplaced';
|
|
30
|
+
};
|
|
31
|
+
/** A lockfile read into an installed-package graph. */
|
|
32
|
+
export interface Lockfile {
|
|
33
|
+
format: 'npm' | 'bun' | 'pnpm' | 'yarn';
|
|
34
|
+
/** Whether it is keyed by install path, so a strong version source (§10 Version): `package-lock.json`, `bun.lock`. */
|
|
35
|
+
keyedByInstallPath: boolean;
|
|
36
|
+
/** How certain what it derives is: `yarn.lock`'s facts are capped at `medium` (§10 Formats). */
|
|
37
|
+
confidence: 'high' | 'medium';
|
|
38
|
+
workspaces: Workspace[];
|
|
39
|
+
/**
|
|
40
|
+
* Why no importer can be picked, whatever the first-party paths are: a manifest that the importer's direct
|
|
41
|
+
* dependencies needed and that is missing or unreadable. Null otherwise.
|
|
42
|
+
*/
|
|
43
|
+
unresolved: string | null;
|
|
44
|
+
/** Where the lockfile puts a package's directory, as a map path gives it. */
|
|
45
|
+
place(directory: readonly string[]): Placed;
|
|
46
|
+
/** The nodes of `name` at `version`, wherever installed. */
|
|
47
|
+
named(name: string, version: string): Node[];
|
|
48
|
+
/**
|
|
49
|
+
* The introducers of the nodes `from` for the importer `workspace` (§10 Walk): its direct dependencies whose
|
|
50
|
+
* installed tree includes one of them, as `name@version`, sorted. A direct dependency introduces itself.
|
|
51
|
+
*/
|
|
52
|
+
introducers(from: readonly Node[], workspace: Workspace): string[];
|
|
53
|
+
}
|
|
54
|
+
/** A lockfile read, or why none is usable, with the format version as evidence. */
|
|
55
|
+
export type ReadLockfile = {
|
|
56
|
+
lockfile: Lockfile;
|
|
57
|
+
} | {
|
|
58
|
+
unusable: string;
|
|
59
|
+
};
|
|
60
|
+
/** A node's `name@version`, or its name alone when the lockfile records no version. */
|
|
61
|
+
export declare const nameAtVersion: ({ name, version }: Node) => string;
|
|
62
|
+
/** A lockfile over `nodes`, whose dependency edges and workspaces' direct dependencies are resolved by the format. */
|
|
63
|
+
export declare function graph(parts: {
|
|
64
|
+
format: Lockfile['format'];
|
|
65
|
+
keyedByInstallPath: boolean;
|
|
66
|
+
confidence: Lockfile['confidence'];
|
|
67
|
+
nodes: Iterable<Node>;
|
|
68
|
+
workspaces: Workspace[];
|
|
69
|
+
unresolved?: string | null;
|
|
70
|
+
/** The nodes a node depends on: `dependencies`, `optionalDependencies` and resolved peers. */
|
|
71
|
+
edges: (node: Node) => Node[];
|
|
72
|
+
/** A workspace's direct dependencies, its dev dependencies included. */
|
|
73
|
+
direct: (workspace: Workspace) => Node[];
|
|
74
|
+
place: (directory: readonly string[]) => Placed;
|
|
75
|
+
}): Lockfile;
|
|
76
|
+
/** The package names of a `node_modules/<a>/node_modules/<b>` chain, or null when it is no such chain. */
|
|
77
|
+
export declare function installChain(segments: readonly string[]): string[] | null;
|
|
78
|
+
/**
|
|
79
|
+
* Where a lockfile that isn't keyed by install path (`pnpm-lock.yaml`, `yarn.lock`) puts a package directory: by the
|
|
80
|
+
* names of its `node_modules` chain. The first name is the workspace's (the folder before the chain) or else the root's
|
|
81
|
+
* direct dependency of that name, or else the lockfile's only version of it; each further name is what the package
|
|
82
|
+
* before it depends on by that name. A name the lockfile doesn't install at all is vendored, in the package before it.
|
|
83
|
+
* Anything else is unplaced: the lockfile doesn't record where its copies are installed.
|
|
84
|
+
*/
|
|
85
|
+
export declare function placeByName(directory: readonly string[], byName: ReadonlyMap<string, Node[]>, workspaces: readonly Workspace[], edges: (node: Node) => Node[], direct: (workspace: Workspace) => Node[]): Placed;
|
|
86
|
+
/** A `package.json` manifest's dependency lists: only these are read from it. */
|
|
87
|
+
export interface Manifest {
|
|
88
|
+
name: string | null;
|
|
89
|
+
version: string | null;
|
|
90
|
+
/** `dependencies`, `optionalDependencies` and `peerDependencies`, by name, with their ranges. */
|
|
91
|
+
production: [string, string][];
|
|
92
|
+
/** `devDependencies`, by name, with their ranges. */
|
|
93
|
+
development: [string, string][];
|
|
94
|
+
}
|
|
95
|
+
/** A manifest's text read, or null when it is missing or isn't a manifest. */
|
|
96
|
+
export declare function readManifest(text: string | null | undefined): Manifest | null;
|
|
97
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
98
|
+
/** A folder relative to the lockfile's directory, normalized: `.` is `''`, and `./`, `x/..` and a trailing `/` go. */
|
|
99
|
+
export declare function normalizeFolder(folder: string): string;
|