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,444 @@
|
|
|
1
|
+
// Attribution (§19, module 5): adds where each occurrence's code came from (§10). It never changes the resolver's fields,
|
|
2
|
+
// and knows nothing of how a target was resolved. It never reads chunk names.
|
|
3
|
+
import { attributionConfidence } from "../confidence/index.js";
|
|
4
|
+
import { sourceKind } from "./lib/containing-package.js";
|
|
5
|
+
import { firstPartyRoots } from "./lib/first-party.js";
|
|
6
|
+
import { importerOf, introducerReasons, nameAtVersion, readLockfile, workspaceOf, } from "./lib/lockfile.js";
|
|
7
|
+
import { pathRuling } from "./lib/path-rules.js";
|
|
8
|
+
import { normalizeRemoteUrl } from "./lib/remote-url.js";
|
|
9
|
+
import { lineStarts, originalAt, position, readMap, tokenAppears, } from "./lib/source-map.js";
|
|
10
|
+
export { introducerReasons };
|
|
11
|
+
export { lockfileManifests } from "./lib/lockfile.js";
|
|
12
|
+
/** How the code behind a finding got into its artifact (§10 Provenance). */
|
|
13
|
+
export const provenances = ['first-party', 'bundled-dependency', 'remote', 'inline', 'unknown'];
|
|
14
|
+
/**
|
|
15
|
+
* Where a package's version came from (§10 Version), strongest first: a store path, a lockfile keyed by install path,
|
|
16
|
+
* then the weak content signals: the package's `package.json` in `sourcesContent`, an in-package `VERSION`
|
|
17
|
+
* constant, a banner.
|
|
18
|
+
*/
|
|
19
|
+
export const versionSources = ['store-path', 'lockfile', 'package-json', 'version-constant', 'banner'];
|
|
20
|
+
/** The strong version sources, the only ones that feed `packages` and the `previousVersion` note (§10, §11). */
|
|
21
|
+
export const strongVersionSources = new Set(['store-path', 'lockfile']);
|
|
22
|
+
/**
|
|
23
|
+
* Attributes each occurrence (§10):
|
|
24
|
+
*
|
|
25
|
+
* - Inline code is the entry document's (`inline`), and has high attribution confidence.
|
|
26
|
+
* - Remote code keys `url:` with its URL normalized by the catalog's rules (§11 Remote URL normalization), provenance
|
|
27
|
+
* `remote`, and is `high` on that key (§9). A verified map still gives its occurrences original locations, looked up
|
|
28
|
+
* as for local code, and a package the map names is evidence only (`evidence.package`).
|
|
29
|
+
* - Local code without a source map is `unknown`, with `no-map`.
|
|
30
|
+
* - Local code with a verified map is looked up there at the occurrence's lookup point, with the line reading that
|
|
31
|
+
* verified the map. A position that is unmapped, or maps to a synthetic or bundler-runtime source, is `unknown` with
|
|
32
|
+
* `no-map`. Otherwise the token check runs, and a position that fails it is discarded: `unknown`, with
|
|
33
|
+
* `token-check-failed` and the rejected candidate. A kept position's source path names the containing package, which
|
|
34
|
+
* keys `pkg:<name>` with provenance `bundled-dependency`. A source with an unrecognized scheme is `unknown` with
|
|
35
|
+
* `foreign-scheme`, and a scoped package under Yarn's pnpm linker `unknown` with `yarn-slug`. A source under no
|
|
36
|
+
* `node_modules` segment inside its namespace's inferred root is first-party, keyed `app:<root-relative path>`
|
|
37
|
+
* (`first-party-inferred` when the root lies above the bundler base), unless its map's ignore list demotes it to
|
|
38
|
+
* `unknown` (`ignore-list-demoted`). One outside the root is escaped: `unknown` with `escaped-source`. Before the
|
|
39
|
+
* root decides, the catalog's framework path rules are tried, then its escape-only shapes: a source a rule
|
|
40
|
+
* matches keys the package the rule names (`bundled-dependency`, `framework-path-rule`, with a `path-rule` step),
|
|
41
|
+
* and one an escape-only shape matches is escaped, wherever the root is.
|
|
42
|
+
*/
|
|
43
|
+
export function attribute(occurrences, artifacts, rules) {
|
|
44
|
+
return attribution(artifacts, rules, null).attribute(occurrences);
|
|
45
|
+
}
|
|
46
|
+
/** Attributes each occurrence of an ineffective write (§3 Record), as `attribute` attributes a finding's. */
|
|
47
|
+
export function attributeIneffective(writes, artifacts, rules) {
|
|
48
|
+
return attribution(artifacts, rules, null).attributeIneffective(writes);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The attribution of a scan over `artifacts` (§10), with the lockfile it was given, or null. The lockfile only adds
|
|
52
|
+
* evidence, never a key (ADR 0006):
|
|
53
|
+
*
|
|
54
|
+
* - A lockfile that can't be read or parsed, or a format version R1 doesn't read (`package-lock.json` v1), gives
|
|
55
|
+
* `no-lockfile`, with the format version as evidence, as no lockfile does.
|
|
56
|
+
* - A lockfile with no installed copy of a package at the version a store path in the scan's maps gives it is stale
|
|
57
|
+
* for the whole scan: `lockfile-stale`, and it derives nothing.
|
|
58
|
+
* - Otherwise a package's install path gives it its lockfile version (§10 Version), and a package path the lockfile
|
|
59
|
+
* says isn't installed is vendored, in the installed package around it (§10 Provenance). First-party code gets its
|
|
60
|
+
* workspace as evidence.
|
|
61
|
+
* - The importer is picked as `importerOf` says; with none, `importer-unresolved`, and no introducers.
|
|
62
|
+
*/
|
|
63
|
+
export function attribution(artifacts, rules, lockfileInput) {
|
|
64
|
+
const byId = new Map(artifacts.map((artifact) => [artifact.id ?? artifact.url, artifact]));
|
|
65
|
+
const verified = new Map();
|
|
66
|
+
const verifiedMapOf = (artifact) => {
|
|
67
|
+
const id = artifact.id ?? artifact.url;
|
|
68
|
+
let map = verified.get(id);
|
|
69
|
+
if (map === undefined)
|
|
70
|
+
verified.set(id, (map = verifiedMap(artifact)));
|
|
71
|
+
return map;
|
|
72
|
+
};
|
|
73
|
+
// One root per bundler namespace, inferred from every local-build map in the scan (§10 Provenance).
|
|
74
|
+
const local = artifacts.flatMap((artifact) => {
|
|
75
|
+
const map = artifact.origin === 'local-build' ? verifiedMapOf(artifact) : null;
|
|
76
|
+
return map === null ? [] : [map];
|
|
77
|
+
});
|
|
78
|
+
const roots = firstPartyRoots(local);
|
|
79
|
+
// The curated path data decides before the root does (§10 Escaped sources).
|
|
80
|
+
const classify = (path, directory) => {
|
|
81
|
+
const ruling = pathRuling(path, rules);
|
|
82
|
+
if (ruling.kind === 'rule')
|
|
83
|
+
return ruling;
|
|
84
|
+
if (ruling.kind === 'escaped')
|
|
85
|
+
return { kind: 'outside' };
|
|
86
|
+
return roots(path, directory);
|
|
87
|
+
};
|
|
88
|
+
const { lock, introducers } = lockState(lockfileInput, local, classify);
|
|
89
|
+
const each = (write) => {
|
|
90
|
+
const artifact = byId.get(write.artifact);
|
|
91
|
+
if (artifact === undefined)
|
|
92
|
+
throw new Error(`an occurrence names an unknown artifact ${write.artifact}`);
|
|
93
|
+
const { url, hash, origin } = artifact;
|
|
94
|
+
const { lookup, trace, ...owned } = write;
|
|
95
|
+
// A helper write's lookup point may be a call in another artifact of the write's origin (§10): that artifact's
|
|
96
|
+
// map is read there.
|
|
97
|
+
const at = lookup.artifact === undefined ? artifact : byId.get(lookup.artifact);
|
|
98
|
+
if (at === undefined)
|
|
99
|
+
throw new Error(`an occurrence's lookup names an unknown artifact ${lookup.artifact}`);
|
|
100
|
+
const found = attributionOf(artifact, lookup, verifiedMapOf, classify, rules.remoteUrlNormalization, lock, at);
|
|
101
|
+
const attributed = {
|
|
102
|
+
// `artifact` is replaced by the artifact's identity.
|
|
103
|
+
...owned,
|
|
104
|
+
artifact: { url, hash, origin },
|
|
105
|
+
attribution: found.attribution,
|
|
106
|
+
original: found.original,
|
|
107
|
+
evidence: found.evidence,
|
|
108
|
+
// A token check at a lookup point in another artifact names it (§14).
|
|
109
|
+
trace: [
|
|
110
|
+
...trace,
|
|
111
|
+
...found.trace.map((step) => (at === artifact || step.kind !== 'token-check' ? step : { ...step, artifact: at.url })),
|
|
112
|
+
],
|
|
113
|
+
};
|
|
114
|
+
return { attributed, found };
|
|
115
|
+
};
|
|
116
|
+
return {
|
|
117
|
+
attribute: (occurrences) => occurrences.map((occurrence) => {
|
|
118
|
+
const { attributed, found } = each(occurrence);
|
|
119
|
+
const { confidence, ...rest } = attributed;
|
|
120
|
+
// Introducers apply only to bundled-dependency code (§10 Record).
|
|
121
|
+
const introducedBy = found.attribution.provenance === 'bundled-dependency' ? found.introducedBy : undefined;
|
|
122
|
+
return {
|
|
123
|
+
...rest,
|
|
124
|
+
...(introducedBy === undefined ? {} : { introducedBy }),
|
|
125
|
+
confidence: { ...confidence, attribution: attributionConfidence(found.reasons) },
|
|
126
|
+
};
|
|
127
|
+
}),
|
|
128
|
+
attributeIneffective: (writes) => writes.map((write) => each(write).attributed),
|
|
129
|
+
introducers,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/** The scan's lockfile state, and why introducers are unknown (§10 Unknown is not empty). */
|
|
133
|
+
function lockState(input, maps, classify) {
|
|
134
|
+
const unknown = (reason, evidence) => ({ lock: null, introducers: { reason, evidence } });
|
|
135
|
+
if (input === null)
|
|
136
|
+
return unknown('no-lockfile', null);
|
|
137
|
+
const read = readLockfile(input.path, input.text, input.manifests ?? {});
|
|
138
|
+
if ('unusable' in read)
|
|
139
|
+
return unknown('no-lockfile', read.unusable);
|
|
140
|
+
const { lockfile } = read;
|
|
141
|
+
// Any disagreement with a store-path version makes the whole lockfile stale.
|
|
142
|
+
const disagreements = new Set();
|
|
143
|
+
const firstParty = new Set();
|
|
144
|
+
for (const map of maps) {
|
|
145
|
+
map.kinds.forEach((kind, i) => {
|
|
146
|
+
if (kind.kind === 'package') {
|
|
147
|
+
// A store path gives a version to its own package, or, for vendored code (a store-name mismatch), to the
|
|
148
|
+
// vendoring package whose store entry holds it.
|
|
149
|
+
const stored = [
|
|
150
|
+
...(kind.version === null || kind.vendoredBy !== null ? [] : [`${kind.name}@${kind.version}`]),
|
|
151
|
+
...(kind.vendoredBy === null ? [] : [kind.vendoredBy]),
|
|
152
|
+
];
|
|
153
|
+
for (const each of stored) {
|
|
154
|
+
const at = each.lastIndexOf('@');
|
|
155
|
+
if (lockfile.named(each.slice(0, at), each.slice(at + 1)).length === 0) {
|
|
156
|
+
disagreements.add(`a store path gives ${each}, which the lockfile doesn't install`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (kind.kind === 'other' && map.map.sources[i]?.ignored !== true) {
|
|
161
|
+
const classified = classify(kind.path, map.directory);
|
|
162
|
+
if (classified.kind === 'first-party')
|
|
163
|
+
firstParty.add(classified.path);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
if (disagreements.size > 0)
|
|
168
|
+
return unknown('lockfile-stale', [...disagreements].sort(compareText)[0]);
|
|
169
|
+
// A missing or unreadable manifest leaves the direct dependencies unknown.
|
|
170
|
+
if (lockfile.unresolved !== null) {
|
|
171
|
+
return { lock: { lockfile, importer: null }, introducers: { reason: 'importer-unresolved', evidence: lockfile.unresolved } };
|
|
172
|
+
}
|
|
173
|
+
const importer = importerOf(lockfile, input.importer, [...firstParty]);
|
|
174
|
+
if (importer === null) {
|
|
175
|
+
const why = input.importer !== null
|
|
176
|
+
? `the importer ${input.importer} is no workspace of the lockfile`
|
|
177
|
+
: firstParty.size === 0
|
|
178
|
+
? 'no first-party source picks one of the lockfile\'s workspaces'
|
|
179
|
+
: `the first-party sources lie in several workspaces: ${[
|
|
180
|
+
...new Set([...firstParty].map((path) => workspaceOf(lockfile, path)?.folder ?? '')),
|
|
181
|
+
]
|
|
182
|
+
.map((folder) => (folder === '' ? '.' : folder))
|
|
183
|
+
.sort(compareText)
|
|
184
|
+
.join(', ')}`;
|
|
185
|
+
return { lock: { lockfile, importer: null }, introducers: { reason: 'importer-unresolved', evidence: why } };
|
|
186
|
+
}
|
|
187
|
+
return { lock: { lockfile, importer }, introducers: { reason: null, evidence: null, confidence: lockfile.confidence } };
|
|
188
|
+
}
|
|
189
|
+
const compareText = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
|
|
190
|
+
function verifiedMap({ text, map }) {
|
|
191
|
+
if (map === null)
|
|
192
|
+
return null;
|
|
193
|
+
const read = readMap(map.raw);
|
|
194
|
+
if (read === null)
|
|
195
|
+
return null;
|
|
196
|
+
const lineSeparator = /[\u2028\u2029]/.test(text);
|
|
197
|
+
const kinds = read.sources.map((source) => sourceKind(source.raw, source.sourceRoot));
|
|
198
|
+
const directory = map.base === null ? null : map.base.split('/').slice(0, -1).filter((segment) => segment !== '');
|
|
199
|
+
const starts = lineStarts(text, map.reading);
|
|
200
|
+
return { map: read, starts, method: map.method, lineSeparator, kinds, directory, weakVersions: new Map() };
|
|
201
|
+
}
|
|
202
|
+
const unknown = { key: 'unknown', provenance: 'unknown' };
|
|
203
|
+
/** A found attribution with `reasons`, and no original location, evidence or trace step unless `more` gives them. */
|
|
204
|
+
function found(attribution, reasons, more = {}) {
|
|
205
|
+
return { attribution, original: null, evidence: {}, trace: [], reasons, ...more };
|
|
206
|
+
}
|
|
207
|
+
/** The attribution of an occurrence in `artifact`, looked up at `lookup` in `at` (§10). */
|
|
208
|
+
function attributionOf(artifact, lookup, verifiedMapOf, classify, normalization, lock, at = artifact) {
|
|
209
|
+
if (artifact.origin === 'inline')
|
|
210
|
+
return found({ key: 'inline', provenance: 'inline' }, []);
|
|
211
|
+
// The map of the artifact the lookup point is in: the write's own, or another of its origin's.
|
|
212
|
+
const verified = verifiedMapOf(at);
|
|
213
|
+
if (artifact.origin !== 'local-build') {
|
|
214
|
+
// Remote code keys `url:` with its normalized URL, and is `high` on it (§9, §11).
|
|
215
|
+
const remote = { key: `url:${normalizeRemoteUrl(artifact.url, normalization)}`, provenance: 'remote' };
|
|
216
|
+
if (verified === null)
|
|
217
|
+
return found(remote, []);
|
|
218
|
+
// Its verified map still gives original locations, and a package the map names is evidence only (§9). The
|
|
219
|
+
// lockfile describes the scanned application's install, not remote code, so it adds nothing here (§10).
|
|
220
|
+
const mapped = mappedAttribution(verified, lookup, classify, null);
|
|
221
|
+
const named = mapped.attribution.package;
|
|
222
|
+
const evidence = named === undefined ? mapped.evidence : { ...mapped.evidence, package: named };
|
|
223
|
+
return found(remote, [], { original: mapped.original, evidence, trace: mapped.trace });
|
|
224
|
+
}
|
|
225
|
+
if (verified === null)
|
|
226
|
+
return found(unknown, ['no-map']);
|
|
227
|
+
return mappedAttribution(verified, lookup, classify, lock);
|
|
228
|
+
}
|
|
229
|
+
/** The attribution of an occurrence looked up at `lookup` in an artifact's verified map (§10). */
|
|
230
|
+
function mappedAttribution(verified, lookup, classify, lock) {
|
|
231
|
+
// Raw U+2028/U+2029 can shift the positions a map gives (§5, §9).
|
|
232
|
+
const shifted = verified.lineSeparator ? ['line-separator'] : [];
|
|
233
|
+
const { line, column } = position(verified.starts, lookup.generated.start);
|
|
234
|
+
const mapped = originalAt(verified.map, line, column);
|
|
235
|
+
if (mapped === null)
|
|
236
|
+
return found(unknown, [...shifted, 'no-map']);
|
|
237
|
+
const { source } = mapped;
|
|
238
|
+
const kind = verified.kinds[mapped.index] ?? sourceKind(null, null);
|
|
239
|
+
// A synthetic or bundler-runtime source counts as unmapped, and its raw source is kept as evidence (§10).
|
|
240
|
+
if (kind.kind === 'synthetic')
|
|
241
|
+
return found(unknown, [...shifted, 'no-map'], { evidence: { source: source.raw } });
|
|
242
|
+
// Only a null or empty entry has no raw source, and both are synthetic.
|
|
243
|
+
const candidate = { source: source.raw ?? '', line: mapped.line + 1, column: mapped.column };
|
|
244
|
+
const result = lookup.token === null || source.content === null
|
|
245
|
+
? 'n/a'
|
|
246
|
+
: tokenAppears(source.content, mapped.line, lookup.token)
|
|
247
|
+
? 'passed'
|
|
248
|
+
: 'failed';
|
|
249
|
+
const trace = [tokenCheckStep(result, lookup, candidate)];
|
|
250
|
+
if (result === 'failed') {
|
|
251
|
+
return found(unknown, [...shifted, 'token-check-failed'], { evidence: { rejected: candidate }, trace });
|
|
252
|
+
}
|
|
253
|
+
// Without the token check, the map's position is capped at medium, and a mirrored sidecar is too (§10).
|
|
254
|
+
const unverified = result === 'n/a'
|
|
255
|
+
? ['token-check-n/a', ...(verified.method === 'external-sidecar' ? ['sidecar-not-token-verified'] : [])]
|
|
256
|
+
: [];
|
|
257
|
+
const kept = { original: candidate, trace };
|
|
258
|
+
const reasons = [...unverified, ...shifted];
|
|
259
|
+
switch (kind.kind) {
|
|
260
|
+
case 'foreign-scheme':
|
|
261
|
+
return found(unknown, [...reasons, 'foreign-scheme'], kept);
|
|
262
|
+
case 'yarn-slug':
|
|
263
|
+
return found(unknown, [...reasons, 'yarn-slug'], { ...kept, evidence: { slug: kind.slug } });
|
|
264
|
+
// A composed path that climbs out of its package inside `node_modules` is escaped, wherever the root is.
|
|
265
|
+
case 'escaped':
|
|
266
|
+
return found(unknown, [...reasons, 'escaped-source'], kept);
|
|
267
|
+
case 'package': {
|
|
268
|
+
const { name, subpath } = kind;
|
|
269
|
+
// A package's `dist` file may hide vendored code (§9 `prebundled-dist`).
|
|
270
|
+
const dist = subpath.slice(0, -1).includes('dist');
|
|
271
|
+
const attribution = { key: `pkg:${name}`, provenance: 'bundled-dependency', package: name };
|
|
272
|
+
const installed = lock === null ? null : installedAs(lock.lockfile, kind);
|
|
273
|
+
const vendoredBy = kind.vendoredBy ?? installed?.vendoredBy ?? null;
|
|
274
|
+
// Only a lockfile keyed by install path is a version source (§10 Version).
|
|
275
|
+
const version = versionOf(verified, kind, lock?.lockfile.keyedByInstallPath === true ? (installed?.node ?? null) : null);
|
|
276
|
+
// Vendored code, a store-name mismatch or a package the lockfile says isn't installed, still names the package
|
|
277
|
+
// its path names (§10 Provenance; ADR 0006).
|
|
278
|
+
const vendored = vendoredBy === null && installed?.vendored !== true ? [] : ['vendored'];
|
|
279
|
+
const introducedBy = lock?.importer == null || installed === null || installed.from.length === 0
|
|
280
|
+
? undefined
|
|
281
|
+
: lock.lockfile.introducers(installed.from, lock.importer);
|
|
282
|
+
return {
|
|
283
|
+
...found(attribution, [...reasons, ...(dist ? ['prebundled-dist'] : []), ...vendored], {
|
|
284
|
+
...kept,
|
|
285
|
+
evidence: { ...(vendoredBy === null ? {} : { vendoredBy }), ...(version === null ? {} : { version }) },
|
|
286
|
+
}),
|
|
287
|
+
...(introducedBy === undefined ? {} : { introducedBy }),
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
case 'other': {
|
|
291
|
+
const classified = classify(kind.path, verified.directory);
|
|
292
|
+
switch (classified.kind) {
|
|
293
|
+
// A source outside the inferred root is escaped (§10 Escaped sources), and so is an absolute path that no
|
|
294
|
+
// root prefixes.
|
|
295
|
+
case 'outside':
|
|
296
|
+
return found(unknown, [...reasons, 'escaped-source'], kept);
|
|
297
|
+
// An escaped source a framework path rule matches is a file of the package the rule names, with no version,
|
|
298
|
+
// and never `high` (§10 Framework path rules).
|
|
299
|
+
case 'rule': {
|
|
300
|
+
const attribution = { key: `pkg:${classified.package}`, provenance: 'bundled-dependency', package: classified.package };
|
|
301
|
+
const step = {
|
|
302
|
+
kind: 'path-rule',
|
|
303
|
+
rule: classified.rule,
|
|
304
|
+
package: classified.package,
|
|
305
|
+
source: candidate.source,
|
|
306
|
+
text: `the framework path rule ${classified.rule} names ${classified.package} for the escaped source ${candidate.source}`,
|
|
307
|
+
};
|
|
308
|
+
return found(attribution, [...reasons, 'framework-path-rule'], {
|
|
309
|
+
...kept,
|
|
310
|
+
evidence: { pathRule: classified.rule },
|
|
311
|
+
trace: [...trace, step],
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
// A fetched map's relative entry: remote code, keyed by its URL (§11), not by a root.
|
|
315
|
+
case 'unplaced':
|
|
316
|
+
return found(unknown, reasons, kept);
|
|
317
|
+
case 'first-party': {
|
|
318
|
+
// `ignoreList` only demotes: a listed first-party source is `unknown` (§10).
|
|
319
|
+
if (source.ignored)
|
|
320
|
+
return found(unknown, [...reasons, 'ignore-list-demoted'], kept);
|
|
321
|
+
const attribution = { key: `app:${classified.path}`, provenance: 'first-party' };
|
|
322
|
+
// Its workspace is evidence, which only a lockfile gives (§10 Provenance; ADR 0006).
|
|
323
|
+
const workspace = lock === null ? undefined : workspaceOf(lock.lockfile, classified.path);
|
|
324
|
+
const named = workspace?.name == null ? null : workspace.version === null ? workspace.name : `${workspace.name}@${workspace.version}`;
|
|
325
|
+
return found(attribution, [...reasons, ...(classified.inferred ? ['first-party-inferred'] : [])], {
|
|
326
|
+
...kept,
|
|
327
|
+
...(named === null ? {} : { evidence: { workspace: named } }),
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* How a fresh lockfile installs a package source (§10): the node its install path matches, the nodes the walk starts
|
|
336
|
+
* from, and whether it is vendored, with the vendoring package when the lockfile names one.
|
|
337
|
+
*
|
|
338
|
+
* - A store path's package is the lockfile's copies of it at the store version.
|
|
339
|
+
* - Vendored code by a store-name mismatch walks from the vendoring package's copies (§10 Walk).
|
|
340
|
+
* - Otherwise the install path is matched, by its longest suffix that is a lockfile key. A path the lockfile doesn't
|
|
341
|
+
* install is vendored: in the installed package whose directory holds it, which the walk starts from, or in none
|
|
342
|
+
* the lockfile names.
|
|
343
|
+
*/
|
|
344
|
+
function installedAs(lockfile, kind) {
|
|
345
|
+
if (kind.vendoredBy !== null) {
|
|
346
|
+
const at = kind.vendoredBy.lastIndexOf('@');
|
|
347
|
+
const from = lockfile.named(kind.vendoredBy.slice(0, at), kind.vendoredBy.slice(at + 1));
|
|
348
|
+
return { node: null, from, vendored: true, vendoredBy: null };
|
|
349
|
+
}
|
|
350
|
+
if (kind.version !== null)
|
|
351
|
+
return { node: null, from: lockfile.named(kind.name, kind.version), vendored: false, vendoredBy: null };
|
|
352
|
+
const placed = lockfile.place(kind.directory);
|
|
353
|
+
switch (placed.kind) {
|
|
354
|
+
case 'installed':
|
|
355
|
+
return { node: placed.nodes[0] ?? null, from: placed.nodes, vendored: false, vendoredBy: null };
|
|
356
|
+
case 'vendored': {
|
|
357
|
+
const [first] = placed.around;
|
|
358
|
+
return { node: null, from: placed.around, vendored: true, vendoredBy: first === undefined ? null : nameAtVersion(first) };
|
|
359
|
+
}
|
|
360
|
+
case 'unplaced':
|
|
361
|
+
return { node: null, from: [], vendored: false, vendoredBy: null };
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* A package's version, from its strongest source (§10 Version): the store path it is read from, or else the version a
|
|
366
|
+
* fresh lockfile installs at its install path, or else a weak content
|
|
367
|
+
* signal among its own files in the map, the sources in the same package directory: its `package.json` naming it, then
|
|
368
|
+
* an in-package `VERSION` constant, then a banner naming it. A signal is taken from the first source, in the map's
|
|
369
|
+
* order, that gives one.
|
|
370
|
+
*/
|
|
371
|
+
function versionOf(verified, kind, installed) {
|
|
372
|
+
if (kind.version !== null)
|
|
373
|
+
return { version: kind.version, versionSource: 'store-path' };
|
|
374
|
+
if (installed !== null && installed.version !== '')
|
|
375
|
+
return { version: installed.version, versionSource: 'lockfile' };
|
|
376
|
+
const key = `${kind.path.namespace}\0${kind.directory.join('/')}`;
|
|
377
|
+
let weak = verified.weakVersions.get(key);
|
|
378
|
+
if (weak === undefined)
|
|
379
|
+
verified.weakVersions.set(key, (weak = weakVersion(verified, kind, key)));
|
|
380
|
+
return weak;
|
|
381
|
+
}
|
|
382
|
+
const semver = String.raw `\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?`;
|
|
383
|
+
const wholeSemver = new RegExp(`^${semver}$`);
|
|
384
|
+
/** An in-package `VERSION` constant: `VERSION`, `SDK_VERSION` … assigned or keyed a version string. */
|
|
385
|
+
const versionConstant = new RegExp(String.raw `\b(?:[A-Z][A-Z0-9]*_)*VERSION\s*[:=]\s*(['"\x60])(${semver})\1`);
|
|
386
|
+
/** A source's leading block comment, where a banner is. */
|
|
387
|
+
const leadingComment = /^\s*\/\*[\s\S]*?\*\//;
|
|
388
|
+
/** The weak version of the package `kind`, from the content of its own files in the map (§10 Version), or null. */
|
|
389
|
+
function weakVersion(verified, kind, key) {
|
|
390
|
+
const own = verified.map.sources.flatMap((source, i) => {
|
|
391
|
+
const each = verified.kinds[i];
|
|
392
|
+
if (each?.kind !== 'package' || source.content === null || each.name !== kind.name)
|
|
393
|
+
return [];
|
|
394
|
+
return `${each.path.namespace}\0${each.directory.join('/')}` === key ? [{ subpath: each.subpath, content: source.content }] : [];
|
|
395
|
+
});
|
|
396
|
+
for (const { subpath, content } of own) {
|
|
397
|
+
if (subpath.length !== 1 || subpath[0] !== 'package.json')
|
|
398
|
+
continue;
|
|
399
|
+
let manifest;
|
|
400
|
+
try {
|
|
401
|
+
manifest = JSON.parse(content);
|
|
402
|
+
}
|
|
403
|
+
catch {
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
if (typeof manifest !== 'object' || manifest === null)
|
|
407
|
+
continue;
|
|
408
|
+
const { name, version } = manifest;
|
|
409
|
+
if (name === kind.name && typeof version === 'string' && wholeSemver.test(version)) {
|
|
410
|
+
return { version, versionSource: 'package-json' };
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
for (const { content } of own) {
|
|
414
|
+
const found = versionConstant.exec(content);
|
|
415
|
+
if (found !== null)
|
|
416
|
+
return { version: found[2], versionSource: 'version-constant' };
|
|
417
|
+
}
|
|
418
|
+
const named = kind.name.replace(/[.*+?^${}()|[\]\\/]/g, '\\$&');
|
|
419
|
+
const banner = new RegExp(String.raw `(?:^|[\s*!])${named}(?:\s+|@)v?(${semver})(?![0-9A-Za-z.+-])`);
|
|
420
|
+
for (const { content } of own) {
|
|
421
|
+
const comment = leadingComment.exec(content)?.[0];
|
|
422
|
+
const found = comment === undefined ? null : banner.exec(comment);
|
|
423
|
+
if (found !== null && found !== undefined)
|
|
424
|
+
return { version: found[1], versionSource: 'banner' };
|
|
425
|
+
}
|
|
426
|
+
return null;
|
|
427
|
+
}
|
|
428
|
+
/** The `token-check` step for a check at `lookup` of `candidate` (§14). */
|
|
429
|
+
function tokenCheckStep(result, lookup, candidate) {
|
|
430
|
+
const { token } = lookup;
|
|
431
|
+
const at = `${candidate.source}:${candidate.line}:${candidate.column}`;
|
|
432
|
+
const text = result === 'passed'
|
|
433
|
+
? `\`${token}\` appears within one line of ${at}, where the map puts the write`
|
|
434
|
+
: result === 'failed'
|
|
435
|
+
? `\`${token}\` does not appear within one line of ${at}, where the map puts the write, so that position is ` +
|
|
436
|
+
'discarded'
|
|
437
|
+
: token === null
|
|
438
|
+
? `the write has no token at its site, so the token check does not apply to ${at}`
|
|
439
|
+
: `the map has no source content for ${candidate.source}, so the token check does not apply`;
|
|
440
|
+
return { kind: 'token-check', result, token, lookup: location(lookup.generated), candidate, text };
|
|
441
|
+
}
|
|
442
|
+
function location({ start, end, line, column }) {
|
|
443
|
+
return { start, end, line, column };
|
|
444
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a raw `sources` entry names:
|
|
3
|
+
* - `synthetic`: a synthetic or bundler-runtime source, which counts as unmapped (`no-map`)
|
|
4
|
+
* - `foreign-scheme`: a source with a scheme §10 doesn't recognize, which keys `unknown` whether or not it names a
|
|
5
|
+
* package
|
|
6
|
+
* - `package`: a file of the package `name`, with the path segments inside the package after it, the segments of the
|
|
7
|
+
* package's own directory (which hold its other files), the version its store path gives, or null (§10 Version), and
|
|
8
|
+
* the vendoring package (`name@version`) when the path lies inside another package's store entry, or null (§10
|
|
9
|
+
* Provenance, vendored code)
|
|
10
|
+
* - `yarn-slug`: a scoped package under Yarn's pnpm linker, which names it only by the lossy `slug`
|
|
11
|
+
* - `escaped`: a composed-map path that climbs out of its own package inside `node_modules` (`node_modules/@x/src/…`,
|
|
12
|
+
* `node_modules/src/…`, `node_modules/node_modules/…`), which is escaped wherever the inferred root is
|
|
13
|
+
* - `other`: a path under no `node_modules` segment: first-party or escaped code
|
|
14
|
+
*
|
|
15
|
+
* Each but the first two carries the normalized `path` it was read from, which the inferred root is read from and
|
|
16
|
+
* first-party code is named by (§10 Provenance, §11).
|
|
17
|
+
*/
|
|
18
|
+
export type SourceKind = {
|
|
19
|
+
kind: 'synthetic';
|
|
20
|
+
} | {
|
|
21
|
+
kind: 'foreign-scheme';
|
|
22
|
+
} | {
|
|
23
|
+
kind: 'package';
|
|
24
|
+
name: string;
|
|
25
|
+
subpath: string[];
|
|
26
|
+
path: SourcePath;
|
|
27
|
+
directory: string[];
|
|
28
|
+
version: string | null;
|
|
29
|
+
vendoredBy: string | null;
|
|
30
|
+
} | {
|
|
31
|
+
kind: 'yarn-slug';
|
|
32
|
+
slug: string;
|
|
33
|
+
path: SourcePath;
|
|
34
|
+
} | {
|
|
35
|
+
kind: 'escaped';
|
|
36
|
+
path: SourcePath;
|
|
37
|
+
} | {
|
|
38
|
+
kind: 'other';
|
|
39
|
+
path: SourcePath;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* A source's normalized path (§10 Source schemes), with what it is relative to:
|
|
43
|
+
* - `relative`: a path with no scheme, relative to its map's directory (a relative `sourceRoot` joined)
|
|
44
|
+
* - `absolute`: a `file://`, absolute or drive-letter path, whose drive letter is its first segment
|
|
45
|
+
* - `webpack:<ns>`, `webpack-internal`, `turbopack` or `vite`: a path relative to that bundler's base (the webpack
|
|
46
|
+
* context, or the project root), within the bundler namespace it names
|
|
47
|
+
*
|
|
48
|
+
* `segments` has `.` and `x/..` collapsed, and keeps leading `..` segments.
|
|
49
|
+
*/
|
|
50
|
+
export interface SourcePath {
|
|
51
|
+
namespace: string;
|
|
52
|
+
segments: string[];
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The relative `sourceRoot` a map's entries may be joined to (§10 step 1), or null: a URL or an absolute or
|
|
56
|
+
* drive-letter root is ignored, and so is an empty or non-string one.
|
|
57
|
+
*/
|
|
58
|
+
export declare function relativeSourceRoot(sourceRoot: unknown): string | null;
|
|
59
|
+
/**
|
|
60
|
+
* What a raw `sources` entry names, under a map's relative `sourceRoot` (§10 Containing package):
|
|
61
|
+
*
|
|
62
|
+
* 1. **Normalize.** A null or empty entry, or one holding `\0` (a Rollup virtual module), is synthetic. A BOM or a
|
|
63
|
+
* `)]}'` prefix is stripped, then a recognized scheme; any other scheme is foreign. A synthetic source is then
|
|
64
|
+
* recognized on the entry, before any root is joined: `webpack/runtime/*`, `webpack/bootstrap`, `<anon>`,
|
|
65
|
+
* `turbopack:///[turbopack]/…`, `ignored|…` and `external "x"`, and an entry whose resource path is empty.
|
|
66
|
+
* Only an entry that had no scheme and isn't absolute is joined to the root, as a string, with a `/` between them
|
|
67
|
+
* unless the root ends with one. Loader prefixes (up to the last `!`), a `?query` (webpack's `?hash`
|
|
68
|
+
* included) and `*` suffixes are stripped, so the resource path decides. The path is percent-decoded, `\` becomes
|
|
69
|
+
* `/`, and `.` and `x/..` segments collapse (leading `..` stay). A `file://`, absolute or drive-letter path is
|
|
70
|
+
* `absolute`, whatever scheme wraps it.
|
|
71
|
+
* 2. **Read the name** from the segment after the last `node_modules` segment that a dot segment doesn't follow, or the
|
|
72
|
+
* two segments after it for a scoped package. Under Yarn's pnpm linker the package is in
|
|
73
|
+
* `node_modules/.store/<slug>-npm-<version>-<hash>/package/` (or `<slug>-virtual-<hash>`): an unscoped slug is its
|
|
74
|
+
* name, and a scoped one is lossy. A path that climbs out of its package inside `node_modules` names none.
|
|
75
|
+
*/
|
|
76
|
+
export declare function sourceKind(source: string | null, sourceRoot: string | null): SourceKind;
|