@_linked/cli 1.46.2 → 1.47.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/CHANGELOG.md +107 -0
- package/README.md +48 -36
- package/hooks/drift.cjs +169 -0
- package/hooks/git-hook.cjs +170 -0
- package/hooks/hook-shim.cjs +63 -0
- package/hooks/lock-at-rest.cjs +268 -0
- package/hooks/manifests.cjs +216 -0
- package/hooks/platform.cjs +25 -0
- package/lib/esm/app-release/build-backend.d.ts.map +1 -1
- package/lib/esm/app-release/build-backend.js +2 -1
- package/lib/esm/app-release/build-backend.js.map +1 -1
- package/lib/esm/cli-methods.d.ts.map +1 -1
- package/lib/esm/cli-methods.js +166 -150
- package/lib/esm/cli-methods.js.map +1 -1
- package/lib/esm/cli.js +20 -22
- package/lib/esm/cli.js.map +1 -1
- package/lib/esm/commands/build-app.d.ts.map +1 -1
- package/lib/esm/commands/build-app.js +2 -1
- package/lib/esm/commands/build-app.js.map +1 -1
- package/lib/esm/commands/build-package.d.ts.map +1 -1
- package/lib/esm/commands/build-package.js +6 -2
- package/lib/esm/commands/build-package.js.map +1 -1
- package/lib/esm/commands/localize.d.ts +9 -15
- package/lib/esm/commands/localize.d.ts.map +1 -1
- package/lib/esm/commands/localize.js +43 -74
- package/lib/esm/commands/localize.js.map +1 -1
- package/lib/esm/commands/setup-publish.d.ts.map +1 -1
- package/lib/esm/commands/setup-publish.js +22 -15
- package/lib/esm/commands/setup-publish.js.map +1 -1
- package/lib/esm/commands/start.d.ts +8 -0
- package/lib/esm/commands/start.d.ts.map +1 -1
- package/lib/esm/commands/start.js +20 -8
- package/lib/esm/commands/start.js.map +1 -1
- package/lib/esm/installed-packages.d.ts +5 -13
- package/lib/esm/installed-packages.d.ts.map +1 -1
- package/lib/esm/installed-packages.js +5 -38
- package/lib/esm/installed-packages.js.map +1 -1
- package/lib/esm/lifecycle.d.ts +35 -22
- package/lib/esm/lifecycle.d.ts.map +1 -1
- package/lib/esm/lifecycle.js +62 -36
- package/lib/esm/lifecycle.js.map +1 -1
- package/lib/esm/localize/adopt.d.ts +5 -5
- package/lib/esm/localize/adopt.d.ts.map +1 -1
- package/lib/esm/localize/adopt.js +46 -55
- package/lib/esm/localize/adopt.js.map +1 -1
- package/lib/esm/localize/app-root.d.ts +16 -0
- package/lib/esm/localize/app-root.d.ts.map +1 -0
- package/lib/esm/localize/app-root.js +94 -0
- package/lib/esm/localize/app-root.js.map +1 -0
- package/lib/esm/localize/app-setup.d.ts +21 -0
- package/lib/esm/localize/app-setup.d.ts.map +1 -0
- package/lib/esm/localize/app-setup.js +178 -0
- package/lib/esm/localize/app-setup.js.map +1 -0
- package/lib/esm/localize/delocalize.d.ts.map +1 -1
- package/lib/esm/localize/delocalize.js +169 -104
- package/lib/esm/localize/delocalize.js.map +1 -1
- package/lib/esm/localize/drift-hook.d.ts +18 -0
- package/lib/esm/localize/drift-hook.d.ts.map +1 -0
- package/lib/esm/localize/drift-hook.js +37 -0
- package/lib/esm/localize/drift-hook.js.map +1 -0
- package/lib/esm/localize/drift.d.ts +36 -0
- package/lib/esm/localize/drift.d.ts.map +1 -0
- package/lib/esm/localize/drift.js +34 -0
- package/lib/esm/localize/drift.js.map +1 -0
- package/lib/esm/localize/errors.d.ts +14 -8
- package/lib/esm/localize/errors.d.ts.map +1 -1
- package/lib/esm/localize/errors.js +14 -8
- package/lib/esm/localize/errors.js.map +1 -1
- package/lib/esm/localize/fsops.d.ts +11 -27
- package/lib/esm/localize/fsops.d.ts.map +1 -1
- package/lib/esm/localize/fsops.js +40 -115
- package/lib/esm/localize/fsops.js.map +1 -1
- package/lib/esm/localize/git-hooks.d.ts +33 -0
- package/lib/esm/localize/git-hooks.d.ts.map +1 -0
- package/lib/esm/localize/git-hooks.js +146 -0
- package/lib/esm/localize/git-hooks.js.map +1 -0
- package/lib/esm/localize/hook-module.d.ts +6 -0
- package/lib/esm/localize/hook-module.d.ts.map +1 -0
- package/lib/esm/localize/hook-module.js +37 -0
- package/lib/esm/localize/hook-module.js.map +1 -0
- package/lib/esm/localize/index.d.ts +14 -15
- package/lib/esm/localize/index.d.ts.map +1 -1
- package/lib/esm/localize/index.js +8 -9
- package/lib/esm/localize/index.js.map +1 -1
- package/lib/esm/localize/list.d.ts +6 -10
- package/lib/esm/localize/list.d.ts.map +1 -1
- package/lib/esm/localize/list.js +49 -189
- package/lib/esm/localize/list.js.map +1 -1
- package/lib/esm/localize/localize.d.ts +46 -62
- package/lib/esm/localize/localize.d.ts.map +1 -1
- package/lib/esm/localize/localize.js +411 -285
- package/lib/esm/localize/localize.js.map +1 -1
- package/lib/esm/localize/members.d.ts +13 -0
- package/lib/esm/localize/members.d.ts.map +1 -0
- package/lib/esm/localize/members.js +46 -0
- package/lib/esm/localize/members.js.map +1 -0
- package/lib/esm/localize/resolve.d.ts +1 -2
- package/lib/esm/localize/resolve.d.ts.map +1 -1
- package/lib/esm/localize/resolve.js +3 -5
- package/lib/esm/localize/resolve.js.map +1 -1
- package/lib/esm/localize/run.d.ts +1 -0
- package/lib/esm/localize/run.d.ts.map +1 -1
- package/lib/esm/localize/run.js +11 -6
- package/lib/esm/localize/run.js.map +1 -1
- package/lib/esm/localize/setup-lockfile.d.ts +13 -0
- package/lib/esm/localize/setup-lockfile.d.ts.map +1 -0
- package/lib/esm/localize/setup-lockfile.js +80 -0
- package/lib/esm/localize/setup-lockfile.js.map +1 -0
- package/lib/esm/utils/createPackageLocation.d.ts +4 -13
- package/lib/esm/utils/createPackageLocation.d.ts.map +1 -1
- package/lib/esm/utils/createPackageLocation.js +12 -21
- package/lib/esm/utils/createPackageLocation.js.map +1 -1
- package/lib/esm/utils/packageSetup.d.ts +5 -2
- package/lib/esm/utils/packageSetup.d.ts.map +1 -1
- package/lib/esm/utils/packageSetup.js +9 -3
- package/lib/esm/utils/packageSetup.js.map +1 -1
- package/lib/esm/utils/platform.d.ts +24 -0
- package/lib/esm/utils/platform.d.ts.map +1 -0
- package/lib/esm/utils/platform.js +62 -0
- package/lib/esm/utils/platform.js.map +1 -0
- package/lib/esm/utils/shapeReferences.d.ts.map +1 -1
- package/lib/esm/utils/shapeReferences.js +3 -2
- package/lib/esm/utils/shapeReferences.js.map +1 -1
- package/lib/esm/utils/spawn.d.ts +65 -0
- package/lib/esm/utils/spawn.d.ts.map +1 -0
- package/lib/esm/utils/spawn.js +181 -0
- package/lib/esm/utils/spawn.js.map +1 -0
- package/lib/esm/utils.d.ts.map +1 -1
- package/lib/esm/utils.js +19 -5
- package/lib/esm/utils.js.map +1 -1
- package/lib/esm/vite-config.d.ts +5 -12
- package/lib/esm/vite-config.d.ts.map +1 -1
- package/lib/esm/vite-config.js +41 -33
- package/lib/esm/vite-config.js.map +1 -1
- package/package.json +4 -3
- package/lib/esm/localize/ensure.d.ts +0 -44
- package/lib/esm/localize/ensure.d.ts.map +0 -1
- package/lib/esm/localize/ensure.js +0 -151
- package/lib/esm/localize/ensure.js.map +0 -1
- package/lib/esm/localize/manifest.d.ts +0 -63
- package/lib/esm/localize/manifest.d.ts.map +0 -1
- package/lib/esm/localize/manifest.js +0 -133
- package/lib/esm/localize/manifest.js.map +0 -1
- package/lib/esm/localize/one-copy-hook.d.ts +0 -16
- package/lib/esm/localize/one-copy-hook.d.ts.map +0 -1
- package/lib/esm/localize/one-copy-hook.js +0 -20
- package/lib/esm/localize/one-copy-hook.js.map +0 -1
- package/lib/esm/localize/provided.d.ts +0 -28
- package/lib/esm/localize/provided.d.ts.map +0 -1
- package/lib/esm/localize/provided.js +0 -89
- package/lib/esm/localize/provided.js.map +0 -1
- package/lib/esm/localize/prune.d.ts +0 -182
- package/lib/esm/localize/prune.d.ts.map +0 -1
- package/lib/esm/localize/prune.js +0 -703
- package/lib/esm/localize/prune.js.map +0 -1
- package/lib/esm/localize/relink.d.ts +0 -13
- package/lib/esm/localize/relink.d.ts.map +0 -1
- package/lib/esm/localize/relink.js +0 -181
- package/lib/esm/localize/relink.js.map +0 -1
- package/lib/esm/localized-dedupe.d.ts +0 -26
- package/lib/esm/localized-dedupe.d.ts.map +0 -1
- package/lib/esm/localized-dedupe.js +0 -139
- package/lib/esm/localized-dedupe.js.map +0 -1
- package/lib/esm/utils/relinkHooks.d.ts +0 -19
- package/lib/esm/utils/relinkHooks.d.ts.map +0 -1
- package/lib/esm/utils/relinkHooks.js +0 -74
- package/lib/esm/utils/relinkHooks.js.map +0 -1
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* One copy, always: every package the app provides loads once, whichever tool
|
|
3
|
-
* runs the code -- Vite, `tsc`, plain Node, `tsx`, a test runner.
|
|
4
|
-
*
|
|
5
|
-
* Two halves:
|
|
6
|
-
*
|
|
7
|
-
* - **`ensure(entry)`** -- the ONLY way a checkout gets installed: `npm
|
|
8
|
-
* install` inside it, then prune it (prune.ts). localize's install step,
|
|
9
|
-
* `--relink`'s reinstall and `linked localize --reinstall <pkg>` all go
|
|
10
|
-
* through it, so the copies an install puts back exist only until it
|
|
11
|
-
* returns. Hand-running `npm install` in a checkout is what `--reinstall`
|
|
12
|
-
* replaces.
|
|
13
|
-
* - **`checkOneCopy(appRoot)`** -- the run-time check. Before `linked start`,
|
|
14
|
-
* `script`, `call` and `build-all` (a commander `preAction` in cli.ts), and
|
|
15
|
-
* as `linked localize --ensure` for an app's npm `pre*` scripts. It plans
|
|
16
|
-
* the prune (prune.ts `planPrune`) without touching anything -- per-owner
|
|
17
|
-
* candidates, the same range guard, the same "would its dependencies
|
|
18
|
-
* resolve elsewhere" check -- and only when that plan removes something does
|
|
19
|
-
* it prune, and say so in one line on stderr. A copy prune would keep costs
|
|
20
|
-
* a stat and, at most, one requirement index per `node_modules` (read from
|
|
21
|
-
* npm's hidden lockfile), so a tree full of kept copies stays cheap. It
|
|
22
|
-
* never runs npm, prints nothing when there is nothing to do (or no
|
|
23
|
-
* manifest), skips a checkout recorded with `prune: false`, and never
|
|
24
|
-
* throws: a run command must not fail over a developer's private checkout
|
|
25
|
-
* state.
|
|
26
|
-
*/
|
|
27
|
-
import fs from 'node:fs';
|
|
28
|
-
import path from 'node:path';
|
|
29
|
-
import { EXIT_INSTALL_FAILED, EXIT_REFUSED, EXIT_WARNED, LocalizeError, } from './errors.js';
|
|
30
|
-
import { manifestPath, readManifest } from './manifest.js';
|
|
31
|
-
import { readJson } from './fsops.js';
|
|
32
|
-
import { newPruneCache, planPrune, pruneProvided, prunesEntry, shouldPrune, } from './prune.js';
|
|
33
|
-
import { defaultDeps } from './run.js';
|
|
34
|
-
import { reportError } from './localize.js';
|
|
35
|
-
/**
|
|
36
|
-
* `npm install --no-save` inside the checkout at `entry.path`, then prune it.
|
|
37
|
-
*
|
|
38
|
-
* `--no-save` keeps the checkout's tracked files out of it: npm installs what
|
|
39
|
-
* the committed lockfile says and writes neither `package.json` nor
|
|
40
|
-
* `package-lock.json` (only the gitignored hidden lockfile). Without it every
|
|
41
|
-
* relink left a lockfile diff in each checkout it reinstalled, written in the
|
|
42
|
-
* dialect of whichever npm was first on PATH -- measured: Node 22's bundled
|
|
43
|
-
* npm 10.9.9 drops every `"libc"` field an npm 12 lockfile records, and any
|
|
44
|
-
* npm syncs a lockfile header the release workflow bumped. Changing a
|
|
45
|
-
* checkout's dependencies is a deliberate `npm install <dep>` inside it.
|
|
46
|
-
*
|
|
47
|
-
* `--include=dev` because this runs from the app's npm lifecycle hooks, and
|
|
48
|
-
* npm exports the outer command's config to them as `npm_config_*`: measured,
|
|
49
|
-
* `npm install --omit=dev` at the app root arrives as `npm_config_omit=dev`,
|
|
50
|
-
* and an inner install that inherits it skips the checkout's
|
|
51
|
-
* devDependencies, so the checkout no longer builds. A checkout is a
|
|
52
|
-
* development copy; it always needs them.
|
|
53
|
-
*
|
|
54
|
-
* The package's name is read from the checkout's own `package.json`; the
|
|
55
|
-
* other localized packages (siblings) from the manifest, so a checkout being
|
|
56
|
-
* localized right now prunes before the manifest records it.
|
|
57
|
-
*
|
|
58
|
-
* @returns 0, or `EXIT_WARNED` when the prune failed (the install stands).
|
|
59
|
-
* @throws {LocalizeError} `EXIT_INSTALL_FAILED` when `npm install` fails.
|
|
60
|
-
*/
|
|
61
|
-
export function ensure(entry, deps, opts = {}) {
|
|
62
|
-
var _a;
|
|
63
|
-
const pkgDir = path.join(deps.appRoot, entry.path);
|
|
64
|
-
deps.log(`[localize] npm install in ${entry.path}`);
|
|
65
|
-
const install = deps.run('npm', ['install', '--no-save', '--include=dev', '--no-audit', '--no-fund'], { cwd: pkgDir });
|
|
66
|
-
if (install.status !== 0) {
|
|
67
|
-
throw new LocalizeError(`npm install failed in ${entry.path}:\n${(install.stderr || install.stdout).trim()}`, EXIT_INSTALL_FAILED);
|
|
68
|
-
}
|
|
69
|
-
if (!shouldPrune(opts) || !prunesEntry(entry))
|
|
70
|
-
return 0;
|
|
71
|
-
const name = (_a = readJson(path.join(pkgDir, 'package.json'))) === null || _a === void 0 ? void 0 : _a.name;
|
|
72
|
-
if (typeof name !== 'string')
|
|
73
|
-
return 0;
|
|
74
|
-
try {
|
|
75
|
-
const entries = Object.assign(Object.assign({}, recordedEntries(deps)), { [name]: entry });
|
|
76
|
-
pruneProvided(entries, opts, deps, { owners: [name] });
|
|
77
|
-
return 0;
|
|
78
|
-
}
|
|
79
|
-
catch (e) {
|
|
80
|
-
deps.warn(`[localize] pruning ${entry.path} failed, its node_modules are as npm left them: ${e.message}`);
|
|
81
|
-
return EXIT_WARNED;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
/** `linked localize --reinstall <pkg>`: `ensure` for one recorded package. */
|
|
85
|
-
export function reinstall(name, opts, deps) {
|
|
86
|
-
try {
|
|
87
|
-
const entry = readManifest(deps.appRoot, deps).entries[name];
|
|
88
|
-
if (!entry) {
|
|
89
|
-
throw new LocalizeError(`${name} is not localized here, so there is no checkout to reinstall. ` +
|
|
90
|
-
'`linked localize --list` shows what is.', EXIT_REFUSED);
|
|
91
|
-
}
|
|
92
|
-
if (!fs.existsSync(path.join(deps.appRoot, entry.path, 'package.json'))) {
|
|
93
|
-
throw new LocalizeError(`${name} is recorded at ${entry.path}, which has no package.json.`, EXIT_REFUSED);
|
|
94
|
-
}
|
|
95
|
-
return ensure(entry, deps, opts);
|
|
96
|
-
}
|
|
97
|
-
catch (e) {
|
|
98
|
-
return reportError(e, deps);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* The run-time check: remove every recorded checkout's own copy of what the
|
|
103
|
-
* app provides -- exactly what `pruneProvided` would remove, decided first
|
|
104
|
-
* without it, so the common case (nothing to remove, some copies kept) never
|
|
105
|
-
* reaches the prune.
|
|
106
|
-
*
|
|
107
|
-
* @returns what was removed, as `<checkout>/node_modules/<name>@<version>`.
|
|
108
|
-
*/
|
|
109
|
-
export function checkOneCopy(appRoot = process.cwd(), deps = defaultDeps(appRoot)) {
|
|
110
|
-
var _a;
|
|
111
|
-
try {
|
|
112
|
-
if (!fs.existsSync(manifestPath(appRoot)))
|
|
113
|
-
return { pruned: [] };
|
|
114
|
-
const entries = readManifest(appRoot, { warn: () => { } }).entries;
|
|
115
|
-
const owners = Object.keys(entries);
|
|
116
|
-
if (!owners.length)
|
|
117
|
-
return { pruned: [] };
|
|
118
|
-
if (!Object.values(entries).some(prunesEntry))
|
|
119
|
-
return { pruned: [] };
|
|
120
|
-
const cache = newPruneCache();
|
|
121
|
-
const plan = planPrune(entries, { appRoot }, {}, cache);
|
|
122
|
-
if (!plan.remove.length)
|
|
123
|
-
return { pruned: [] };
|
|
124
|
-
// Prune's own report is per checkout and also speaks of what it keeps;
|
|
125
|
-
// here only what went is worth a line, on every run command.
|
|
126
|
-
const quiet = { appRoot, log: () => { }, warn: () => { } };
|
|
127
|
-
const { removed } = pruneProvided(entries, {}, quiet, {
|
|
128
|
-
owners: [...new Set(plan.remove.map((d) => d.ownerName))],
|
|
129
|
-
deps: [...new Set(plan.remove.map((d) => d.dep))],
|
|
130
|
-
}, cache);
|
|
131
|
-
const pruned = removed.map((r) => `${r.where}@${r.version}`);
|
|
132
|
-
if (pruned.length) {
|
|
133
|
-
deps.warn(`[localize] one copy: removed ${pruned.join(', ')} — the app provides ` +
|
|
134
|
-
`${pruned.length === 1 ? 'it' : 'them'}.`);
|
|
135
|
-
}
|
|
136
|
-
return { pruned };
|
|
137
|
-
}
|
|
138
|
-
catch (e) {
|
|
139
|
-
deps.warn(`[localize] one-copy check failed, nothing more was removed: ${(_a = e === null || e === void 0 ? void 0 : e.message) !== null && _a !== void 0 ? _a : e}`);
|
|
140
|
-
return { pruned: [] };
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
function recordedEntries(deps) {
|
|
144
|
-
try {
|
|
145
|
-
return readManifest(deps.appRoot, { warn: () => { } }).entries;
|
|
146
|
-
}
|
|
147
|
-
catch (_a) {
|
|
148
|
-
return {};
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
//# sourceMappingURL=ensure.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ensure.js","sourceRoot":"","sources":["../../../src/localize/ensure.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * One copy, always: every package the app provides loads once, whichever tool\n * runs the code -- Vite, `tsc`, plain Node, `tsx`, a test runner.\n *\n * Two halves:\n *\n * - **`ensure(entry)`** -- the ONLY way a checkout gets installed: `npm\n * install` inside it, then prune it (prune.ts). localize's install step,\n * `--relink`'s reinstall and `linked localize --reinstall <pkg>` all go\n * through it, so the copies an install puts back exist only until it\n * returns. Hand-running `npm install` in a checkout is what `--reinstall`\n * replaces.\n * - **`checkOneCopy(appRoot)`** -- the run-time check. Before `linked start`,\n * `script`, `call` and `build-all` (a commander `preAction` in cli.ts), and\n * as `linked localize --ensure` for an app's npm `pre*` scripts. It plans\n * the prune (prune.ts `planPrune`) without touching anything -- per-owner\n * candidates, the same range guard, the same \"would its dependencies\n * resolve elsewhere\" check -- and only when that plan removes something does\n * it prune, and say so in one line on stderr. A copy prune would keep costs\n * a stat and, at most, one requirement index per `node_modules` (read from\n * npm's hidden lockfile), so a tree full of kept copies stays cheap. It\n * never runs npm, prints nothing when there is nothing to do (or no\n * manifest), skips a checkout recorded with `prune: false`, and never\n * throws: a run command must not fail over a developer's private checkout\n * state.\n */\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport {\n EXIT_INSTALL_FAILED,\n EXIT_REFUSED,\n EXIT_WARNED,\n LocalizeError,\n} from './errors.js';\nimport {manifestPath, readManifest} from './manifest.js';\nimport type {ManifestEntry} from './manifest.js';\nimport {readJson} from './fsops.js';\nimport {\n newPruneCache,\n planPrune,\n pruneProvided,\n prunesEntry,\n shouldPrune,\n} from './prune.js';\nimport type {PruneOptions} from './prune.js';\nimport {defaultDeps} from './run.js';\nimport type {Deps} from './run.js';\nimport {reportError} from './localize.js';\n\n/**\n * `npm install --no-save` inside the checkout at `entry.path`, then prune it.\n *\n * `--no-save` keeps the checkout's tracked files out of it: npm installs what\n * the committed lockfile says and writes neither `package.json` nor\n * `package-lock.json` (only the gitignored hidden lockfile). Without it every\n * relink left a lockfile diff in each checkout it reinstalled, written in the\n * dialect of whichever npm was first on PATH -- measured: Node 22's bundled\n * npm 10.9.9 drops every `\"libc\"` field an npm 12 lockfile records, and any\n * npm syncs a lockfile header the release workflow bumped. Changing a\n * checkout's dependencies is a deliberate `npm install <dep>` inside it.\n *\n * `--include=dev` because this runs from the app's npm lifecycle hooks, and\n * npm exports the outer command's config to them as `npm_config_*`: measured,\n * `npm install --omit=dev` at the app root arrives as `npm_config_omit=dev`,\n * and an inner install that inherits it skips the checkout's\n * devDependencies, so the checkout no longer builds. A checkout is a\n * development copy; it always needs them.\n *\n * The package's name is read from the checkout's own `package.json`; the\n * other localized packages (siblings) from the manifest, so a checkout being\n * localized right now prunes before the manifest records it.\n *\n * @returns 0, or `EXIT_WARNED` when the prune failed (the install stands).\n * @throws {LocalizeError} `EXIT_INSTALL_FAILED` when `npm install` fails.\n */\nexport function ensure(\n entry: Pick<ManifestEntry, 'path' | 'subdir' | 'prune'>,\n deps: Deps,\n opts: PruneOptions = {},\n): number {\n const pkgDir = path.join(deps.appRoot, entry.path);\n deps.log(`[localize] npm install in ${entry.path}`);\n const install = deps.run(\n 'npm',\n ['install', '--no-save', '--include=dev', '--no-audit', '--no-fund'],\n {cwd: pkgDir},\n );\n if (install.status !== 0) {\n throw new LocalizeError(\n `npm install failed in ${entry.path}:\\n${(install.stderr || install.stdout).trim()}`,\n EXIT_INSTALL_FAILED,\n );\n }\n if (!shouldPrune(opts) || !prunesEntry(entry)) return 0;\n\n const name = readJson(path.join(pkgDir, 'package.json'))?.name;\n if (typeof name !== 'string') return 0;\n try {\n const entries = {...recordedEntries(deps), [name]: entry as ManifestEntry};\n pruneProvided(entries, opts, deps, {owners: [name]});\n return 0;\n } catch (e) {\n deps.warn(\n `[localize] pruning ${entry.path} failed, its node_modules are as npm left them: ${e.message}`,\n );\n return EXIT_WARNED;\n }\n}\n\n/** `linked localize --reinstall <pkg>`: `ensure` for one recorded package. */\nexport function reinstall(\n name: string,\n opts: PruneOptions,\n deps: Deps,\n): number {\n try {\n const entry = readManifest(deps.appRoot, deps).entries[name];\n if (!entry) {\n throw new LocalizeError(\n `${name} is not localized here, so there is no checkout to reinstall. ` +\n '`linked localize --list` shows what is.',\n EXIT_REFUSED,\n );\n }\n if (!fs.existsSync(path.join(deps.appRoot, entry.path, 'package.json'))) {\n throw new LocalizeError(\n `${name} is recorded at ${entry.path}, which has no package.json.`,\n EXIT_REFUSED,\n );\n }\n return ensure(entry, deps, opts);\n } catch (e) {\n return reportError(e, deps);\n }\n}\n\n/**\n * The run-time check: remove every recorded checkout's own copy of what the\n * app provides -- exactly what `pruneProvided` would remove, decided first\n * without it, so the common case (nothing to remove, some copies kept) never\n * reaches the prune.\n *\n * @returns what was removed, as `<checkout>/node_modules/<name>@<version>`.\n */\nexport function checkOneCopy(\n appRoot: string = process.cwd(),\n deps: Pick<Deps, 'log' | 'warn'> = defaultDeps(appRoot),\n): {pruned: string[]} {\n try {\n if (!fs.existsSync(manifestPath(appRoot))) return {pruned: []};\n const entries = readManifest(appRoot, {warn: () => {}}).entries;\n const owners = Object.keys(entries);\n if (!owners.length) return {pruned: []};\n\n if (!Object.values(entries).some(prunesEntry)) return {pruned: []};\n\n const cache = newPruneCache();\n const plan = planPrune(entries, {appRoot}, {}, cache);\n if (!plan.remove.length) return {pruned: []};\n\n // Prune's own report is per checkout and also speaks of what it keeps;\n // here only what went is worth a line, on every run command.\n const quiet = {appRoot, log: () => {}, warn: () => {}};\n const {removed} = pruneProvided(\n entries,\n {},\n quiet,\n {\n owners: [...new Set(plan.remove.map((d) => d.ownerName))],\n deps: [...new Set(plan.remove.map((d) => d.dep))],\n },\n cache,\n );\n const pruned = removed.map((r) => `${r.where}@${r.version}`);\n if (pruned.length) {\n deps.warn(\n `[localize] one copy: removed ${pruned.join(', ')} — the app provides ` +\n `${pruned.length === 1 ? 'it' : 'them'}.`,\n );\n }\n return {pruned};\n } catch (e) {\n deps.warn(\n `[localize] one-copy check failed, nothing more was removed: ${e?.message ?? e}`,\n );\n return {pruned: []};\n }\n}\n\nfunction recordedEntries(deps: Deps): Record<string, ManifestEntry> {\n try {\n return readManifest(deps.appRoot, {warn: () => {}}).entries;\n } catch {\n return {};\n }\n}\n"]}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
export declare const MANIFEST_FILENAME = "local-packages.json";
|
|
2
|
-
/** One recorded package: where its checkout is, and where it came from. */
|
|
3
|
-
export interface ManifestEntry {
|
|
4
|
-
/** Absent for an adopted checkout with no remote. */
|
|
5
|
-
repo?: string;
|
|
6
|
-
/** The package directory, relative to the app root. */
|
|
7
|
-
path: string;
|
|
8
|
-
branch: string;
|
|
9
|
-
/** The package's directory inside the clone, for a monorepo. */
|
|
10
|
-
subdir?: string;
|
|
11
|
-
/** The range the app declared when it was localized. */
|
|
12
|
-
range?: string;
|
|
13
|
-
/**
|
|
14
|
-
* `false` when the package was localized or adopted with `--no-prune`: its
|
|
15
|
-
* checkout's `node_modules` stay as npm installed them -- no prune after
|
|
16
|
-
* `--relink` or `--reinstall`, and the run-time check (`--ensure`, before
|
|
17
|
-
* `linked start` etc.) skips it. Absent means on; localizing or adopting it
|
|
18
|
-
* again without `--no-prune` turns it back on. Optional, so schema version 1
|
|
19
|
-
* readers that do not know it simply ignore it.
|
|
20
|
-
*/
|
|
21
|
-
prune?: false;
|
|
22
|
-
}
|
|
23
|
-
export interface Manifest {
|
|
24
|
-
file: string;
|
|
25
|
-
exists: boolean;
|
|
26
|
-
version: number;
|
|
27
|
-
dir: string;
|
|
28
|
-
entries: Record<string, ManifestEntry>;
|
|
29
|
-
malformed: {
|
|
30
|
-
name: string;
|
|
31
|
-
reason: string;
|
|
32
|
-
}[];
|
|
33
|
-
}
|
|
34
|
-
/** The only schema version this build understands. */
|
|
35
|
-
export declare const SCHEMA_VERSION = 1;
|
|
36
|
-
/** Default checkout directory, relative to the consumer root. */
|
|
37
|
-
export declare const DEFAULT_DIR = "packages-local";
|
|
38
|
-
export declare function manifestPath(appRoot: string): string;
|
|
39
|
-
/**
|
|
40
|
-
* Read and validate the manifest.
|
|
41
|
-
*
|
|
42
|
-
* A missing file is NOT an error: it is the CI case and the fresh-clone case,
|
|
43
|
-
* and reads as an empty set. That is also why no `if (CI)` branch exists
|
|
44
|
-
* anywhere in this package -- the file is gitignored, so CI is a natural no-op.
|
|
45
|
-
*
|
|
46
|
-
* @throws {LocalizeError} code 3. Nothing is written in any failure case.
|
|
47
|
-
*/
|
|
48
|
-
export declare function readManifest(appRoot: string, io?: {
|
|
49
|
-
warn: (message: string) => void;
|
|
50
|
-
}): Manifest;
|
|
51
|
-
/**
|
|
52
|
-
* Write the manifest atomically: a `.tmp` in the SAME directory, so `rename`
|
|
53
|
-
* is atomic on one filesystem, then `rename` over the original.
|
|
54
|
-
*
|
|
55
|
-
* An empty `packages` object is written rather than the file being deleted, so
|
|
56
|
-
* a non-default `dir` survives delocalizing the last entry.
|
|
57
|
-
*/
|
|
58
|
-
export declare function writeManifest(appRoot: string, data: {
|
|
59
|
-
version?: number;
|
|
60
|
-
dir?: string;
|
|
61
|
-
packages?: Record<string, ManifestEntry>;
|
|
62
|
-
}): string;
|
|
63
|
-
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../../src/localize/manifest.ts"],"names":[],"mappings":"AA8BA,eAAO,MAAM,iBAAiB,wBAAwB,CAAC;AAEvD,2EAA2E;AAC3E,MAAM,WAAW,aAAa;IAC5B,qDAAqD;IACrD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACvC,SAAS,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAC,EAAE,CAAC;CAC7C;AAED,sDAAsD;AACtD,eAAO,MAAM,cAAc,IAAI,CAAC;AAEhC,iEAAiE;AACjE,eAAO,MAAM,WAAW,mBAAmB,CAAC;AAE5C,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,EAAE,GAAE;IAAC,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CAAwB,GAC3D,QAAQ,CAyFV;AAiBD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;IACJ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CAC1C,GACA,MAAM,CAcR"}
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `local-packages.json` -- the record of which packages are currently localized.
|
|
3
|
-
*
|
|
4
|
-
* The filename and schema are deliberately the SAME as the ones `@semantu/cli`
|
|
5
|
-
* already writes (schema version 1, keys are npm package names). A developer
|
|
6
|
-
* with a half-localized tree must be able to switch tools without re-cloning,
|
|
7
|
-
* and the consumer's `postinstall` must keep working across the switch.
|
|
8
|
-
*
|
|
9
|
-
* One relaxation since 0.1.0: `repo` is optional, because an adopted checkout
|
|
10
|
-
* may have no remote. localize 0.1.0 skips such an entry as partial, with a
|
|
11
|
-
* warning, and keeps the rest; other readers of this schema were not checked.
|
|
12
|
-
*
|
|
13
|
-
* Two properties are load-bearing:
|
|
14
|
-
*
|
|
15
|
-
* 1. **It is never repaired.** Unparseable JSON, an unknown `version` or a
|
|
16
|
-
* `packages` that is not an object is a refusal (exit 3) with no write.
|
|
17
|
-
* This file is the only record of what a developer intended; silently
|
|
18
|
-
* resetting it is how a half-localized tree becomes invisible.
|
|
19
|
-
* 2. **Writes are atomic** -- `<file>.tmp` then `rename` -- so a killed run
|
|
20
|
-
* cannot leave a truncated file behind.
|
|
21
|
-
*
|
|
22
|
-
* A *partial entry* is different from a malformed file: it is skipped with a
|
|
23
|
-
* warning and its siblings still process, because one bad entry must not cost
|
|
24
|
-
* a developer the other nine.
|
|
25
|
-
*/
|
|
26
|
-
import fs from 'node:fs';
|
|
27
|
-
import path from 'node:path';
|
|
28
|
-
import { EXIT_BAD_FILE, LocalizeError } from './errors.js';
|
|
29
|
-
export const MANIFEST_FILENAME = 'local-packages.json';
|
|
30
|
-
/** The only schema version this build understands. */
|
|
31
|
-
export const SCHEMA_VERSION = 1;
|
|
32
|
-
/** Default checkout directory, relative to the consumer root. */
|
|
33
|
-
export const DEFAULT_DIR = 'packages-local';
|
|
34
|
-
export function manifestPath(appRoot) {
|
|
35
|
-
return path.join(appRoot, MANIFEST_FILENAME);
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Read and validate the manifest.
|
|
39
|
-
*
|
|
40
|
-
* A missing file is NOT an error: it is the CI case and the fresh-clone case,
|
|
41
|
-
* and reads as an empty set. That is also why no `if (CI)` branch exists
|
|
42
|
-
* anywhere in this package -- the file is gitignored, so CI is a natural no-op.
|
|
43
|
-
*
|
|
44
|
-
* @throws {LocalizeError} code 3. Nothing is written in any failure case.
|
|
45
|
-
*/
|
|
46
|
-
export function readManifest(appRoot, io = { warn: console.warn }) {
|
|
47
|
-
const file = manifestPath(appRoot);
|
|
48
|
-
if (!fs.existsSync(file)) {
|
|
49
|
-
return {
|
|
50
|
-
file,
|
|
51
|
-
exists: false,
|
|
52
|
-
version: SCHEMA_VERSION,
|
|
53
|
-
dir: DEFAULT_DIR,
|
|
54
|
-
entries: {},
|
|
55
|
-
malformed: [],
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
const raw = fs.readFileSync(file, 'utf8');
|
|
59
|
-
let parsed;
|
|
60
|
-
try {
|
|
61
|
-
parsed = JSON.parse(raw);
|
|
62
|
-
}
|
|
63
|
-
catch (e) {
|
|
64
|
-
throw new LocalizeError(`${file} is not valid JSON: ${e.message}\n` +
|
|
65
|
-
`Refusing to read or rewrite it -- it is the only record of what was localized. ` +
|
|
66
|
-
`Fix it by hand, or delete it to start over.`, EXIT_BAD_FILE);
|
|
67
|
-
}
|
|
68
|
-
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
69
|
-
throw new LocalizeError(`${file} does not contain a JSON object.\n` +
|
|
70
|
-
`Refusing to read or rewrite it. Expected {"version":${SCHEMA_VERSION},"packages":{…}}.`, EXIT_BAD_FILE);
|
|
71
|
-
}
|
|
72
|
-
if (parsed.version !== SCHEMA_VERSION) {
|
|
73
|
-
throw new LocalizeError(`${file} has schema version ${JSON.stringify(parsed.version)}; this version of localize understands ` +
|
|
74
|
-
`only version ${SCHEMA_VERSION}.\n` +
|
|
75
|
-
`Refusing to read or rewrite it -- guessing at a schema it did not write is how intent ` +
|
|
76
|
-
`gets lost. Upgrade localize, or delete the file and re-localize.`, EXIT_BAD_FILE);
|
|
77
|
-
}
|
|
78
|
-
if (parsed.packages === null ||
|
|
79
|
-
typeof parsed.packages !== 'object' ||
|
|
80
|
-
Array.isArray(parsed.packages)) {
|
|
81
|
-
throw new LocalizeError(`${file} has a "packages" field that is not an object (found ` +
|
|
82
|
-
`${Array.isArray(parsed.packages) ? 'an array' : typeof parsed.packages}).\n` +
|
|
83
|
-
`Refusing to read or rewrite it.`, EXIT_BAD_FILE);
|
|
84
|
-
}
|
|
85
|
-
const dir = typeof parsed.dir === 'string' && parsed.dir.length > 0
|
|
86
|
-
? parsed.dir
|
|
87
|
-
: DEFAULT_DIR;
|
|
88
|
-
const entries = {};
|
|
89
|
-
const malformed = [];
|
|
90
|
-
for (const [name, value] of Object.entries(parsed.packages)) {
|
|
91
|
-
const reason = validateEntry(value);
|
|
92
|
-
if (reason) {
|
|
93
|
-
malformed.push({ name, reason });
|
|
94
|
-
io.warn(`[localize] ${name}: malformed entry in ${MANIFEST_FILENAME} (${reason}) -- skipped. ` +
|
|
95
|
-
`The other entries are unaffected. \`linked localize ${name}\` rewrites it.`);
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
entries[name] = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (typeof value.repo === 'string' && value.repo
|
|
99
|
-
? { repo: value.repo }
|
|
100
|
-
: {})), { path: value.path, branch: value.branch }), (typeof value.subdir === 'string' ? { subdir: value.subdir } : {})), (typeof value.range === 'string' ? { range: value.range } : {})), (value.prune === false ? { prune: false } : {}));
|
|
101
|
-
}
|
|
102
|
-
return { file, exists: true, version: SCHEMA_VERSION, dir, entries, malformed };
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* @returns {string|null} a reason the entry is unusable, else null.
|
|
106
|
-
*
|
|
107
|
-
* `repo` is optional: an adopted checkout may have no remote at all -- a
|
|
108
|
-
* package created locally that has not been pushed anywhere yet.
|
|
109
|
-
*/
|
|
110
|
-
function validateEntry(value) {
|
|
111
|
-
if (value === null || typeof value !== 'object' || Array.isArray(value))
|
|
112
|
-
return 'not an object';
|
|
113
|
-
const missing = ['path', 'branch'].filter((k) => typeof value[k] !== 'string' || value[k].length === 0);
|
|
114
|
-
return missing.length ? `missing ${missing.join(', ')}` : null;
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Write the manifest atomically: a `.tmp` in the SAME directory, so `rename`
|
|
118
|
-
* is atomic on one filesystem, then `rename` over the original.
|
|
119
|
-
*
|
|
120
|
-
* An empty `packages` object is written rather than the file being deleted, so
|
|
121
|
-
* a non-default `dir` survives delocalizing the last entry.
|
|
122
|
-
*/
|
|
123
|
-
export function writeManifest(appRoot, data) {
|
|
124
|
-
var _a;
|
|
125
|
-
const target = manifestPath(appRoot);
|
|
126
|
-
const tmp = `${target}.tmp`;
|
|
127
|
-
const out = Object.assign(Object.assign({ version: SCHEMA_VERSION }, (data.dir && data.dir !== DEFAULT_DIR ? { dir: data.dir } : {})), { packages: (_a = data.packages) !== null && _a !== void 0 ? _a : {} });
|
|
128
|
-
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
129
|
-
fs.writeFileSync(tmp, JSON.stringify(out, null, 2) + '\n', 'utf8');
|
|
130
|
-
fs.renameSync(tmp, target);
|
|
131
|
-
return target;
|
|
132
|
-
}
|
|
133
|
-
//# sourceMappingURL=manifest.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../../src/localize/manifest.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `local-packages.json` -- the record of which packages are currently localized.\n *\n * The filename and schema are deliberately the SAME as the ones `@semantu/cli`\n * already writes (schema version 1, keys are npm package names). A developer\n * with a half-localized tree must be able to switch tools without re-cloning,\n * and the consumer's `postinstall` must keep working across the switch.\n *\n * One relaxation since 0.1.0: `repo` is optional, because an adopted checkout\n * may have no remote. localize 0.1.0 skips such an entry as partial, with a\n * warning, and keeps the rest; other readers of this schema were not checked.\n *\n * Two properties are load-bearing:\n *\n * 1. **It is never repaired.** Unparseable JSON, an unknown `version` or a\n * `packages` that is not an object is a refusal (exit 3) with no write.\n * This file is the only record of what a developer intended; silently\n * resetting it is how a half-localized tree becomes invisible.\n * 2. **Writes are atomic** -- `<file>.tmp` then `rename` -- so a killed run\n * cannot leave a truncated file behind.\n *\n * A *partial entry* is different from a malformed file: it is skipped with a\n * warning and its siblings still process, because one bad entry must not cost\n * a developer the other nine.\n */\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport {EXIT_BAD_FILE, LocalizeError} from './errors.js';\n\nexport const MANIFEST_FILENAME = 'local-packages.json';\n\n/** One recorded package: where its checkout is, and where it came from. */\nexport interface ManifestEntry {\n /** Absent for an adopted checkout with no remote. */\n repo?: string;\n /** The package directory, relative to the app root. */\n path: string;\n branch: string;\n /** The package's directory inside the clone, for a monorepo. */\n subdir?: string;\n /** The range the app declared when it was localized. */\n range?: string;\n /**\n * `false` when the package was localized or adopted with `--no-prune`: its\n * checkout's `node_modules` stay as npm installed them -- no prune after\n * `--relink` or `--reinstall`, and the run-time check (`--ensure`, before\n * `linked start` etc.) skips it. Absent means on; localizing or adopting it\n * again without `--no-prune` turns it back on. Optional, so schema version 1\n * readers that do not know it simply ignore it.\n */\n prune?: false;\n}\n\nexport interface Manifest {\n file: string;\n exists: boolean;\n version: number;\n dir: string;\n entries: Record<string, ManifestEntry>;\n malformed: {name: string; reason: string}[];\n}\n\n/** The only schema version this build understands. */\nexport const SCHEMA_VERSION = 1;\n\n/** Default checkout directory, relative to the consumer root. */\nexport const DEFAULT_DIR = 'packages-local';\n\nexport function manifestPath(appRoot: string): string {\n return path.join(appRoot, MANIFEST_FILENAME);\n}\n\n/**\n * Read and validate the manifest.\n *\n * A missing file is NOT an error: it is the CI case and the fresh-clone case,\n * and reads as an empty set. That is also why no `if (CI)` branch exists\n * anywhere in this package -- the file is gitignored, so CI is a natural no-op.\n *\n * @throws {LocalizeError} code 3. Nothing is written in any failure case.\n */\nexport function readManifest(\n appRoot: string,\n io: {warn: (message: string) => void} = {warn: console.warn},\n): Manifest {\n const file = manifestPath(appRoot);\n\n if (!fs.existsSync(file)) {\n return {\n file,\n exists: false,\n version: SCHEMA_VERSION,\n dir: DEFAULT_DIR,\n entries: {},\n malformed: [],\n };\n }\n\n const raw = fs.readFileSync(file, 'utf8');\n let parsed;\n try {\n parsed = JSON.parse(raw);\n } catch (e) {\n throw new LocalizeError(\n `${file} is not valid JSON: ${e.message}\\n` +\n `Refusing to read or rewrite it -- it is the only record of what was localized. ` +\n `Fix it by hand, or delete it to start over.`,\n EXIT_BAD_FILE,\n );\n }\n\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new LocalizeError(\n `${file} does not contain a JSON object.\\n` +\n `Refusing to read or rewrite it. Expected {\"version\":${SCHEMA_VERSION},\"packages\":{…}}.`,\n EXIT_BAD_FILE,\n );\n }\n\n if (parsed.version !== SCHEMA_VERSION) {\n throw new LocalizeError(\n `${file} has schema version ${JSON.stringify(parsed.version)}; this version of localize understands ` +\n `only version ${SCHEMA_VERSION}.\\n` +\n `Refusing to read or rewrite it -- guessing at a schema it did not write is how intent ` +\n `gets lost. Upgrade localize, or delete the file and re-localize.`,\n EXIT_BAD_FILE,\n );\n }\n\n if (\n parsed.packages === null ||\n typeof parsed.packages !== 'object' ||\n Array.isArray(parsed.packages)\n ) {\n throw new LocalizeError(\n `${file} has a \"packages\" field that is not an object (found ` +\n `${Array.isArray(parsed.packages) ? 'an array' : typeof parsed.packages}).\\n` +\n `Refusing to read or rewrite it.`,\n EXIT_BAD_FILE,\n );\n }\n\n const dir =\n typeof parsed.dir === 'string' && parsed.dir.length > 0\n ? parsed.dir\n : DEFAULT_DIR;\n\n const entries: Record<string, ManifestEntry> = {};\n const malformed: Manifest['malformed'] = [];\n\n for (const [name, value] of Object.entries<any>(parsed.packages)) {\n const reason = validateEntry(value);\n if (reason) {\n malformed.push({name, reason});\n io.warn(\n `[localize] ${name}: malformed entry in ${MANIFEST_FILENAME} (${reason}) -- skipped. ` +\n `The other entries are unaffected. \\`linked localize ${name}\\` rewrites it.`,\n );\n continue;\n }\n entries[name] = {\n ...(typeof value.repo === 'string' && value.repo\n ? {repo: value.repo}\n : {}),\n path: value.path,\n branch: value.branch,\n ...(typeof value.subdir === 'string' ? {subdir: value.subdir} : {}),\n ...(typeof value.range === 'string' ? {range: value.range} : {}),\n ...(value.prune === false ? {prune: false as const} : {}),\n };\n }\n\n return {file, exists: true, version: SCHEMA_VERSION, dir, entries, malformed};\n}\n\n/**\n * @returns {string|null} a reason the entry is unusable, else null.\n *\n * `repo` is optional: an adopted checkout may have no remote at all -- a\n * package created locally that has not been pushed anywhere yet.\n */\nfunction validateEntry(value) {\n if (value === null || typeof value !== 'object' || Array.isArray(value))\n return 'not an object';\n const missing = ['path', 'branch'].filter(\n (k) => typeof value[k] !== 'string' || value[k].length === 0,\n );\n return missing.length ? `missing ${missing.join(', ')}` : null;\n}\n\n/**\n * Write the manifest atomically: a `.tmp` in the SAME directory, so `rename`\n * is atomic on one filesystem, then `rename` over the original.\n *\n * An empty `packages` object is written rather than the file being deleted, so\n * a non-default `dir` survives delocalizing the last entry.\n */\nexport function writeManifest(\n appRoot: string,\n data: {\n version?: number;\n dir?: string;\n packages?: Record<string, ManifestEntry>;\n },\n): string {\n const target = manifestPath(appRoot);\n const tmp = `${target}.tmp`;\n\n const out = {\n version: SCHEMA_VERSION,\n ...(data.dir && data.dir !== DEFAULT_DIR ? {dir: data.dir} : {}),\n packages: data.packages ?? {},\n };\n\n fs.mkdirSync(path.dirname(target), {recursive: true});\n fs.writeFileSync(tmp, JSON.stringify(out, null, 2) + '\\n', 'utf8');\n fs.renameSync(tmp, target);\n return target;\n}\n"]}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Which `linked` commands run the one-copy check (`checkOneCopy`, ensure.ts)
|
|
3
|
-
* first: the ones that run the app's code. Kept apart from `cli.ts`, which
|
|
4
|
-
* builds and parses the real program on import, so a test can install the
|
|
5
|
-
* hook on a program of its own and see exactly which commands trigger it.
|
|
6
|
-
*/
|
|
7
|
-
import type { Command } from 'commander';
|
|
8
|
-
/** `linked start`, `script`, `call` and `build-all`, and nothing else. */
|
|
9
|
-
export declare const ONE_COPY_COMMANDS: ReadonlySet<string>;
|
|
10
|
-
/**
|
|
11
|
-
* Before one of `ONE_COPY_COMMANDS`, remove any localized checkout's own copy
|
|
12
|
-
* of what the app provides. Never runs npm, silent when there is nothing to
|
|
13
|
-
* remove, a no-op with no local-packages.json, and it never throws.
|
|
14
|
-
*/
|
|
15
|
-
export declare function installOneCopyHook(program: Command, check?: (appRoot: string) => unknown): void;
|
|
16
|
-
//# sourceMappingURL=one-copy-hook.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"one-copy-hook.d.ts","sourceRoot":"","sources":["../../../src/localize/one-copy-hook.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,WAAW,CAAC;AAIvC,0EAA0E;AAC1E,eAAO,MAAM,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAKhD,CAAC;AAEH;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,OAAO,EAChB,KAAK,GAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAsB,GACjD,IAAI,CAIN"}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { checkOneCopy } from './ensure.js';
|
|
2
|
-
/** `linked start`, `script`, `call` and `build-all`, and nothing else. */
|
|
3
|
-
export const ONE_COPY_COMMANDS = new Set([
|
|
4
|
-
'start',
|
|
5
|
-
'script',
|
|
6
|
-
'call',
|
|
7
|
-
'build-all',
|
|
8
|
-
]);
|
|
9
|
-
/**
|
|
10
|
-
* Before one of `ONE_COPY_COMMANDS`, remove any localized checkout's own copy
|
|
11
|
-
* of what the app provides. Never runs npm, silent when there is nothing to
|
|
12
|
-
* remove, a no-op with no local-packages.json, and it never throws.
|
|
13
|
-
*/
|
|
14
|
-
export function installOneCopyHook(program, check = checkOneCopy) {
|
|
15
|
-
program.hook('preAction', (_program, actionCommand) => {
|
|
16
|
-
if (ONE_COPY_COMMANDS.has(actionCommand.name()))
|
|
17
|
-
check(process.cwd());
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
//# sourceMappingURL=one-copy-hook.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"one-copy-hook.js","sourceRoot":"","sources":["../../../src/localize/one-copy-hook.ts"],"names":[],"mappings":"AAQA,OAAO,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAEzC,0EAA0E;AAC1E,MAAM,CAAC,MAAM,iBAAiB,GAAwB,IAAI,GAAG,CAAC;IAC5D,OAAO;IACP,QAAQ;IACR,MAAM;IACN,WAAW;CACZ,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAChC,OAAgB,EAChB,QAAsC,YAAY;IAElD,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,aAAa,EAAE,EAAE;QACpD,IAAI,iBAAiB,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC;YAAE,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["/**\n * Which `linked` commands run the one-copy check (`checkOneCopy`, ensure.ts)\n * first: the ones that run the app's code. Kept apart from `cli.ts`, which\n * builds and parses the real program on import, so a test can install the\n * hook on a program of its own and see exactly which commands trigger it.\n */\nimport type {Command} from 'commander';\n\nimport {checkOneCopy} from './ensure.js';\n\n/** `linked start`, `script`, `call` and `build-all`, and nothing else. */\nexport const ONE_COPY_COMMANDS: ReadonlySet<string> = new Set([\n 'start',\n 'script',\n 'call',\n 'build-all',\n]);\n\n/**\n * Before one of `ONE_COPY_COMMANDS`, remove any localized checkout's own copy\n * of what the app provides. Never runs npm, silent when there is nothing to\n * remove, a no-op with no local-packages.json, and it never throws.\n */\nexport function installOneCopyHook(\n program: Command,\n check: (appRoot: string) => unknown = checkOneCopy,\n): void {\n program.hook('preAction', (_program, actionCommand) => {\n if (ONE_COPY_COMMANDS.has(actionCommand.name())) check(process.cwd());\n });\n}\n"]}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/** A dependency the app has, left out because some localized checkout asks a range it misses. */
|
|
2
|
-
export interface ProvidedSkip {
|
|
3
|
-
name: string;
|
|
4
|
-
reason: 'range';
|
|
5
|
-
/** The localized checkouts whose range the app's version does not satisfy. */
|
|
6
|
-
asks: {
|
|
7
|
-
from: string;
|
|
8
|
-
range: string;
|
|
9
|
-
}[];
|
|
10
|
-
appVersion: string;
|
|
11
|
-
}
|
|
12
|
-
export interface Provided {
|
|
13
|
-
/** Sorted: the localized packages, the dependencies the app provides, react/react-dom. */
|
|
14
|
-
names: string[];
|
|
15
|
-
skipped: ProvidedSkip[];
|
|
16
|
-
/** The localized packages among `names` (installed at the app root). */
|
|
17
|
-
localized: string[];
|
|
18
|
-
/** The names in `names` that are there as a localized checkout's dependency. */
|
|
19
|
-
dependencies: string[];
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* @param appRoot the app root (where `local-packages.json` and `node_modules` are)
|
|
23
|
-
* @param localizedNames the localized package names; defaults to the
|
|
24
|
-
* manifest's. Pruning passes the entries it is working on, so a checkout
|
|
25
|
-
* being localized right now counts before the manifest records it.
|
|
26
|
-
*/
|
|
27
|
-
export declare function providedPackages(appRoot: string, localizedNames?: string[]): Provided;
|
|
28
|
-
//# sourceMappingURL=provided.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"provided.d.ts","sourceRoot":"","sources":["../../../src/localize/provided.ts"],"names":[],"mappings":"AAsCA,iGAAiG;AACjG,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,8EAA8E;IAC9E,IAAI,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,EAAE,CAAC;IACtC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,QAAQ;IACvB,0FAA0F;IAC1F,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,wEAAwE;IACxE,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,gFAAgF;IAChF,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,cAAc,GAAE,MAAM,EAA2B,GAChD,QAAQ,CAuCV"}
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* THE rule for what an app provides to its localized checkouts -- the one list
|
|
3
|
-
* that pruning (prune.ts), the run-time one-copy check (ensure.ts) and Vite's
|
|
4
|
-
* `resolve.dedupe` (`../localized-dedupe.ts`) all start from.
|
|
5
|
-
*
|
|
6
|
-
* - every localized package the app has installed -- always, whatever a range
|
|
7
|
-
* says: the live checkout is the point of localizing it, for every importer;
|
|
8
|
-
* - every runtime dependency a localized checkout declares (`dependencies`,
|
|
9
|
-
* `peerDependencies`, `optionalDependencies` -- what its source imports) that
|
|
10
|
-
* the app has at a version satisfying EVERY localized checkout's range for
|
|
11
|
-
* it. A range the app's version does not satisfy (or that cannot be read)
|
|
12
|
-
* leaves the name out, as `skipped`, with who asks what;
|
|
13
|
-
* - `react` and `react-dom` when the app has them.
|
|
14
|
-
*
|
|
15
|
-
* Scoped to what is localized on purpose: a duplicate comes from a checkout's
|
|
16
|
-
* own `npm install`, so the checkouts' declarations are what can produce one.
|
|
17
|
-
* The server's "installed N times" warning stays as the cross-check for
|
|
18
|
-
* anything this misses (a linked package hoisted into a checkout only
|
|
19
|
-
* transitively).
|
|
20
|
-
*
|
|
21
|
-
* Synchronous and cheap -- the manifest, one `package.json` per localized
|
|
22
|
-
* package and one per dependency it names -- because the run-time check calls
|
|
23
|
-
* it before every `linked start`.
|
|
24
|
-
*/
|
|
25
|
-
import fs from 'node:fs';
|
|
26
|
-
import path from 'node:path';
|
|
27
|
-
import { readInstalledPkgSync } from '../installed-packages.js';
|
|
28
|
-
import { readManifest } from './manifest.js';
|
|
29
|
-
import { readJson } from './fsops.js';
|
|
30
|
-
import { satisfies } from './prune.js';
|
|
31
|
-
const RUNTIME_FIELDS = [
|
|
32
|
-
'dependencies',
|
|
33
|
-
'peerDependencies',
|
|
34
|
-
'optionalDependencies',
|
|
35
|
-
];
|
|
36
|
-
/**
|
|
37
|
-
* @param appRoot the app root (where `local-packages.json` and `node_modules` are)
|
|
38
|
-
* @param localizedNames the localized package names; defaults to the
|
|
39
|
-
* manifest's. Pruning passes the entries it is working on, so a checkout
|
|
40
|
-
* being localized right now counts before the manifest records it.
|
|
41
|
-
*/
|
|
42
|
-
export function providedPackages(appRoot, localizedNames = manifestNames(appRoot)) {
|
|
43
|
-
var _a, _b, _c, _d, _e;
|
|
44
|
-
const localized = localizedNames.filter((name) => fs.existsSync(path.join(appRoot, 'node_modules', name, 'package.json')));
|
|
45
|
-
const names = new Set(localized);
|
|
46
|
-
const skipped = [];
|
|
47
|
-
const dependencies = [];
|
|
48
|
-
// name -> every localized checkout's runtime range for it
|
|
49
|
-
const asks = new Map();
|
|
50
|
-
for (const from of localized) {
|
|
51
|
-
const pkg = (_a = readJson(path.join(appRoot, 'node_modules', from, 'package.json'))) !== null && _a !== void 0 ? _a : {};
|
|
52
|
-
for (const field of RUNTIME_FIELDS) {
|
|
53
|
-
for (const [dep, range] of Object.entries((_b = pkg[field]) !== null && _b !== void 0 ? _b : {})) {
|
|
54
|
-
if (names.has(dep))
|
|
55
|
-
continue;
|
|
56
|
-
const list = (_c = asks.get(dep)) !== null && _c !== void 0 ? _c : [];
|
|
57
|
-
if (!list.some((a) => a.from === from))
|
|
58
|
-
list.push({ from, range });
|
|
59
|
-
asks.set(dep, list);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
for (const [name, ranges] of asks) {
|
|
64
|
-
const appVersion = (_e = (_d = readInstalledPkgSync(name, appRoot)) === null || _d === void 0 ? void 0 : _d.json) === null || _e === void 0 ? void 0 : _e.version;
|
|
65
|
-
if (!appVersion)
|
|
66
|
-
continue; // the app does not provide it
|
|
67
|
-
const unmet = ranges.filter((a) => satisfies(appVersion, a.range) !== true);
|
|
68
|
-
if (unmet.length)
|
|
69
|
-
skipped.push({ name, reason: 'range', asks: unmet, appVersion });
|
|
70
|
-
else {
|
|
71
|
-
names.add(name);
|
|
72
|
-
dependencies.push(name);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
for (const name of ['react', 'react-dom']) {
|
|
76
|
-
if (readInstalledPkgSync(name, appRoot))
|
|
77
|
-
names.add(name);
|
|
78
|
-
}
|
|
79
|
-
return { names: [...names].sort(), skipped, localized, dependencies };
|
|
80
|
-
}
|
|
81
|
-
function manifestNames(appRoot) {
|
|
82
|
-
try {
|
|
83
|
-
return Object.keys(readManifest(appRoot, { warn: () => { } }).entries);
|
|
84
|
-
}
|
|
85
|
-
catch (_a) {
|
|
86
|
-
return [];
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
//# sourceMappingURL=provided.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"provided.js","sourceRoot":"","sources":["../../../src/localize/provided.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * THE rule for what an app provides to its localized checkouts -- the one list\n * that pruning (prune.ts), the run-time one-copy check (ensure.ts) and Vite's\n * `resolve.dedupe` (`../localized-dedupe.ts`) all start from.\n *\n * - every localized package the app has installed -- always, whatever a range\n * says: the live checkout is the point of localizing it, for every importer;\n * - every runtime dependency a localized checkout declares (`dependencies`,\n * `peerDependencies`, `optionalDependencies` -- what its source imports) that\n * the app has at a version satisfying EVERY localized checkout's range for\n * it. A range the app's version does not satisfy (or that cannot be read)\n * leaves the name out, as `skipped`, with who asks what;\n * - `react` and `react-dom` when the app has them.\n *\n * Scoped to what is localized on purpose: a duplicate comes from a checkout's\n * own `npm install`, so the checkouts' declarations are what can produce one.\n * The server's \"installed N times\" warning stays as the cross-check for\n * anything this misses (a linked package hoisted into a checkout only\n * transitively).\n *\n * Synchronous and cheap -- the manifest, one `package.json` per localized\n * package and one per dependency it names -- because the run-time check calls\n * it before every `linked start`.\n */\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport {readInstalledPkgSync} from '../installed-packages.js';\nimport {readManifest} from './manifest.js';\nimport {readJson} from './fsops.js';\nimport {satisfies} from './prune.js';\n\nconst RUNTIME_FIELDS = [\n 'dependencies',\n 'peerDependencies',\n 'optionalDependencies',\n];\n\n/** A dependency the app has, left out because some localized checkout asks a range it misses. */\nexport interface ProvidedSkip {\n name: string;\n reason: 'range';\n /** The localized checkouts whose range the app's version does not satisfy. */\n asks: {from: string; range: string}[];\n appVersion: string;\n}\n\nexport interface Provided {\n /** Sorted: the localized packages, the dependencies the app provides, react/react-dom. */\n names: string[];\n skipped: ProvidedSkip[];\n /** The localized packages among `names` (installed at the app root). */\n localized: string[];\n /** The names in `names` that are there as a localized checkout's dependency. */\n dependencies: string[];\n}\n\n/**\n * @param appRoot the app root (where `local-packages.json` and `node_modules` are)\n * @param localizedNames the localized package names; defaults to the\n * manifest's. Pruning passes the entries it is working on, so a checkout\n * being localized right now counts before the manifest records it.\n */\nexport function providedPackages(\n appRoot: string,\n localizedNames: string[] = manifestNames(appRoot),\n): Provided {\n const localized = localizedNames.filter((name) =>\n fs.existsSync(path.join(appRoot, 'node_modules', name, 'package.json')),\n );\n const names = new Set<string>(localized);\n const skipped: ProvidedSkip[] = [];\n const dependencies: string[] = [];\n\n // name -> every localized checkout's runtime range for it\n const asks = new Map<string, {from: string; range: string}[]>();\n for (const from of localized) {\n const pkg =\n readJson(path.join(appRoot, 'node_modules', from, 'package.json')) ?? {};\n for (const field of RUNTIME_FIELDS) {\n for (const [dep, range] of Object.entries<string>(pkg[field] ?? {})) {\n if (names.has(dep)) continue;\n const list = asks.get(dep) ?? [];\n if (!list.some((a) => a.from === from)) list.push({from, range});\n asks.set(dep, list);\n }\n }\n }\n\n for (const [name, ranges] of asks) {\n const appVersion = readInstalledPkgSync(name, appRoot)?.json?.version;\n if (!appVersion) continue; // the app does not provide it\n const unmet = ranges.filter((a) => satisfies(appVersion, a.range) !== true);\n if (unmet.length)\n skipped.push({name, reason: 'range', asks: unmet, appVersion});\n else {\n names.add(name);\n dependencies.push(name);\n }\n }\n\n for (const name of ['react', 'react-dom']) {\n if (readInstalledPkgSync(name, appRoot)) names.add(name);\n }\n return {names: [...names].sort(), skipped, localized, dependencies};\n}\n\nfunction manifestNames(appRoot: string): string[] {\n try {\n return Object.keys(readManifest(appRoot, {warn: () => {}}).entries);\n } catch {\n return [];\n }\n}\n"]}
|