@_linked/cli 1.46.1 → 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 +113 -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 -165
- package/lib/esm/localize/prune.d.ts.map +0 -1
- package/lib/esm/localize/prune.js +0 -619
- 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 -179
- 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
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which directory is the app: the one whose `package.json` lists the
|
|
3
|
+
* workspaces, wherever in it a command runs. `linked localize` run inside
|
|
4
|
+
* `packages/foo` must set up and install the app, not the member; `build-all`
|
|
5
|
+
* plans from the same root.
|
|
6
|
+
*
|
|
7
|
+
* Only a directory that is a workspace of the one above it belongs to it. A
|
|
8
|
+
* git worktree nested in an app (`<app>/.claude/worktrees/<wt>`) has its own
|
|
9
|
+
* `package.json` with `workspaces`, and none of the app's globs match it: it
|
|
10
|
+
* is its own app, and a command run there must not act on the main checkout.
|
|
11
|
+
*/
|
|
12
|
+
import fs from 'node:fs';
|
|
13
|
+
import path from 'node:path';
|
|
14
|
+
import { readJson } from './fsops.js';
|
|
15
|
+
import { requireHook } from './hook-module.js';
|
|
16
|
+
let manifests;
|
|
17
|
+
/** `hooks/manifests.cjs`: the glob matching the hooks use, loaded on first use. */
|
|
18
|
+
function manifestsModule() {
|
|
19
|
+
manifests !== null && manifests !== void 0 ? manifests : (manifests = requireHook('manifests.cjs', (mod) => typeof mod.workspaceGlobs === 'function' &&
|
|
20
|
+
typeof mod.globsMatch === 'function'));
|
|
21
|
+
return manifests;
|
|
22
|
+
}
|
|
23
|
+
function manifestAt(dir) {
|
|
24
|
+
const file = path.join(dir, 'package.json');
|
|
25
|
+
const pkg = fs.existsSync(file) ? readJson(file) : null;
|
|
26
|
+
if (!pkg || typeof pkg !== 'object')
|
|
27
|
+
return null;
|
|
28
|
+
return {
|
|
29
|
+
workspaces: 'workspaces' in pkg && !!pkg.workspaces,
|
|
30
|
+
linkedPackage: 'linkedPackage' in pkg && pkg.linkedPackage === true,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The workspace root for `start`: the nearest ancestor (or `start` itself)
|
|
35
|
+
* whose `package.json` has `workspaces`, then each higher ancestor with
|
|
36
|
+
* `workspaces` whose globs match the root found so far -- the first one that
|
|
37
|
+
* does not ends the climb. Of that chain, the topmost that is not itself a
|
|
38
|
+
* linked package (`"linkedPackage": true`), else the topmost. Null when there
|
|
39
|
+
* is none.
|
|
40
|
+
*/
|
|
41
|
+
export function findWorkspaceRoot(start) {
|
|
42
|
+
var _a;
|
|
43
|
+
const nearest = nearestWithWorkspaces(path.resolve(start));
|
|
44
|
+
if (!nearest)
|
|
45
|
+
return null;
|
|
46
|
+
let topmost = nearest.dir;
|
|
47
|
+
let topmostApp = nearest.linkedPackage ? null : nearest.dir;
|
|
48
|
+
for (let above = path.dirname(topmost);; above = path.dirname(above)) {
|
|
49
|
+
const m = manifestAt(above);
|
|
50
|
+
if (m === null || m === void 0 ? void 0 : m.workspaces) {
|
|
51
|
+
const { workspaceGlobs, globsMatch } = manifestsModule();
|
|
52
|
+
const rel = path.relative(above, topmost).split(path.sep).join('/');
|
|
53
|
+
const globs = (_a = workspaceGlobs(above)) !== null && _a !== void 0 ? _a : [];
|
|
54
|
+
if (!globsMatch(globs, rel) && !underPackagesLocal(globs, rel))
|
|
55
|
+
break;
|
|
56
|
+
topmost = above;
|
|
57
|
+
if (!m.linkedPackage)
|
|
58
|
+
topmostApp = above;
|
|
59
|
+
}
|
|
60
|
+
if (path.dirname(above) === above)
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
return topmostApp !== null && topmostApp !== void 0 ? topmostApp : topmost;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* A `--subdir` checkout is a symlink `packages-local/<name>` into
|
|
67
|
+
* `packages-local/.repos/<mono>/…`, and a command run in it sees the real
|
|
68
|
+
* path, which `packages-local/*` does not match: the climb would stop at the
|
|
69
|
+
* framework monorepo and localize would set it up. So anything under
|
|
70
|
+
* `packages-local/` (`.repos/`, `.setup-aside/` too) belongs to an app whose
|
|
71
|
+
* globs list `packages-local/*`.
|
|
72
|
+
*/
|
|
73
|
+
function underPackagesLocal(globs, rel) {
|
|
74
|
+
return (rel.startsWith('packages-local/') && globs.includes('packages-local/*'));
|
|
75
|
+
}
|
|
76
|
+
function nearestWithWorkspaces(start) {
|
|
77
|
+
for (let dir = start;; dir = path.dirname(dir)) {
|
|
78
|
+
const m = manifestAt(dir);
|
|
79
|
+
if (m === null || m === void 0 ? void 0 : m.workspaces)
|
|
80
|
+
return { dir, linkedPackage: m.linkedPackage };
|
|
81
|
+
if (path.dirname(dir) === dir)
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The app root for a command run in `start`: the workspace root above it, or
|
|
87
|
+
* `start` itself for an app that has no `workspaces` yet (localize's set-up
|
|
88
|
+
* adds them).
|
|
89
|
+
*/
|
|
90
|
+
export function findAppRoot(start) {
|
|
91
|
+
var _a;
|
|
92
|
+
return (_a = findWorkspaceRoot(start)) !== null && _a !== void 0 ? _a : path.resolve(start);
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=app-root.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-root.js","sourceRoot":"","sources":["../../../src/localize/app-root.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Which directory is the app: the one whose `package.json` lists the\n * workspaces, wherever in it a command runs. `linked localize` run inside\n * `packages/foo` must set up and install the app, not the member; `build-all`\n * plans from the same root.\n *\n * Only a directory that is a workspace of the one above it belongs to it. A\n * git worktree nested in an app (`<app>/.claude/worktrees/<wt>`) has its own\n * `package.json` with `workspaces`, and none of the app's globs match it: it\n * is its own app, and a command run there must not act on the main checkout.\n */\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport {readJson} from './fsops.js';\nimport {requireHook} from './hook-module.js';\n\ninterface ManifestsModule {\n workspaceGlobs: (appRoot: string) => string[] | null;\n globsMatch: (globs: string[], rel: string) => boolean;\n}\n\nlet manifests: ManifestsModule | undefined;\n\n/** `hooks/manifests.cjs`: the glob matching the hooks use, loaded on first use. */\nfunction manifestsModule(): ManifestsModule {\n manifests ??= requireHook<ManifestsModule>(\n 'manifests.cjs',\n (mod) =>\n typeof mod.workspaceGlobs === 'function' &&\n typeof mod.globsMatch === 'function',\n );\n return manifests;\n}\n\ninterface Manifest {\n workspaces: boolean;\n linkedPackage: boolean;\n}\n\nfunction manifestAt(dir: string): Manifest | null {\n const file = path.join(dir, 'package.json');\n const pkg: unknown = fs.existsSync(file) ? readJson(file) : null;\n if (!pkg || typeof pkg !== 'object') return null;\n return {\n workspaces: 'workspaces' in pkg && !!pkg.workspaces,\n linkedPackage: 'linkedPackage' in pkg && pkg.linkedPackage === true,\n };\n}\n\n/**\n * The workspace root for `start`: the nearest ancestor (or `start` itself)\n * whose `package.json` has `workspaces`, then each higher ancestor with\n * `workspaces` whose globs match the root found so far -- the first one that\n * does not ends the climb. Of that chain, the topmost that is not itself a\n * linked package (`\"linkedPackage\": true`), else the topmost. Null when there\n * is none.\n */\nexport function findWorkspaceRoot(start: string): string | null {\n const nearest = nearestWithWorkspaces(path.resolve(start));\n if (!nearest) return null;\n let topmost = nearest.dir;\n let topmostApp: string | null = nearest.linkedPackage ? null : nearest.dir;\n for (let above = path.dirname(topmost); ; above = path.dirname(above)) {\n const m = manifestAt(above);\n if (m?.workspaces) {\n const {workspaceGlobs, globsMatch} = manifestsModule();\n const rel = path.relative(above, topmost).split(path.sep).join('/');\n const globs = workspaceGlobs(above) ?? [];\n if (!globsMatch(globs, rel) && !underPackagesLocal(globs, rel)) break;\n topmost = above;\n if (!m.linkedPackage) topmostApp = above;\n }\n if (path.dirname(above) === above) break;\n }\n return topmostApp ?? topmost;\n}\n\n/**\n * A `--subdir` checkout is a symlink `packages-local/<name>` into\n * `packages-local/.repos/<mono>/…`, and a command run in it sees the real\n * path, which `packages-local/*` does not match: the climb would stop at the\n * framework monorepo and localize would set it up. So anything under\n * `packages-local/` (`.repos/`, `.setup-aside/` too) belongs to an app whose\n * globs list `packages-local/*`.\n */\nfunction underPackagesLocal(globs: string[], rel: string): boolean {\n return (\n rel.startsWith('packages-local/') && globs.includes('packages-local/*')\n );\n}\n\nfunction nearestWithWorkspaces(\n start: string,\n): {dir: string; linkedPackage: boolean} | null {\n for (let dir = start; ; dir = path.dirname(dir)) {\n const m = manifestAt(dir);\n if (m?.workspaces) return {dir, linkedPackage: m.linkedPackage};\n if (path.dirname(dir) === dir) return null;\n }\n}\n\n/**\n * The app root for a command run in `start`: the workspace root above it, or\n * `start` itself for an app that has no `workspaces` yet (localize's set-up\n * adds them).\n */\nexport function findAppRoot(start: string): string {\n return findWorkspaceRoot(start) ?? path.resolve(start);\n}\n"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface SetupChange {
|
|
2
|
+
file: 'package.json' | '.gitignore';
|
|
3
|
+
what: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* What the app's `postinstall` and `dependencies` run: the hook by its path,
|
|
7
|
+
* not through `linked` (see hooks/lock-at-rest.cjs), and only when the cli is
|
|
8
|
+
* installed -- `npm ci --omit=dev` leaves it out, and the install must not
|
|
9
|
+
* fail for that.
|
|
10
|
+
*/
|
|
11
|
+
export declare const LOCK_AT_REST_COMMAND: string;
|
|
12
|
+
/** Make the app ready for checkouts; returns what it changed. */
|
|
13
|
+
export declare function ensureAppSetup(appRoot: string): SetupChange[];
|
|
14
|
+
/**
|
|
15
|
+
* The `overrides` keys -- at any depth, as `a > b` -- that name one of
|
|
16
|
+
* `names`. A root one makes npm fail with `EOVERRIDE` once that package is a
|
|
17
|
+
* workspace member; a nested one installs, but overrides the checkout the
|
|
18
|
+
* developer localized it to use.
|
|
19
|
+
*/
|
|
20
|
+
export declare function conflictingOverrides(appRoot: string, names: string[]): string[];
|
|
21
|
+
//# sourceMappingURL=app-setup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-setup.d.ts","sourceRoot":"","sources":["../../../src/localize/app-setup.ts"],"names":[],"mappings":"AAwBA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,cAAc,GAAG,YAAY,CAAC;IACpC,IAAI,EAAE,MAAM,CAAC;CACd;AAGD;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,QAEoC,CAAC;AAWtE,iEAAiE;AACjE,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,EAAE,CAE7D;AAoGD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EAAE,GACd,MAAM,EAAE,CAeV"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one-time setup an app needs before a checkout can be a workspace
|
|
3
|
+
* member, made by `linked localize` and committed by the developer:
|
|
4
|
+
*
|
|
5
|
+
* - `"packages-local/*"` in `workspaces`, so npm links and dedupes checkouts;
|
|
6
|
+
* - `postinstall` and `dependencies` run the lock-at-rest hook, which puts the
|
|
7
|
+
* committed `package-lock.json` back after npm rewrote it to list them;
|
|
8
|
+
* - `.gitignore` keeps checkouts and parked checkouts out of git.
|
|
9
|
+
*
|
|
10
|
+
* Idempotent: a second run changes nothing and returns `[]`. `package.json` is
|
|
11
|
+
* rewritten only when something changed, with its own indentation, key order,
|
|
12
|
+
* line endings and trailing newline, so the diff is the setup and nothing else.
|
|
13
|
+
*
|
|
14
|
+
* It never edits `overrides`. npm refuses (`EOVERRIDE`) a root override on a
|
|
15
|
+
* workspace member's name, and what to do about it -- drop the override or
|
|
16
|
+
* not localize -- is the developer's call; `conflictingOverrides` names them.
|
|
17
|
+
*/
|
|
18
|
+
import fs from 'node:fs';
|
|
19
|
+
import path from 'node:path';
|
|
20
|
+
import { withEolOf } from '../utils/platform.js';
|
|
21
|
+
import { LocalizeError, EXIT_REFUSED } from './errors.js';
|
|
22
|
+
import { LOCAL_DIR, PARKED_DIR } from './members.js';
|
|
23
|
+
const WORKSPACE_GLOB = `${LOCAL_DIR}/*`;
|
|
24
|
+
/**
|
|
25
|
+
* What the app's `postinstall` and `dependencies` run: the hook by its path,
|
|
26
|
+
* not through `linked` (see hooks/lock-at-rest.cjs), and only when the cli is
|
|
27
|
+
* installed -- `npm ci --omit=dev` leaves it out, and the install must not
|
|
28
|
+
* fail for that.
|
|
29
|
+
*/
|
|
30
|
+
export const LOCK_AT_REST_COMMAND = `node -e "const p='node_modules/@_linked/cli/hooks/lock-at-rest.cjs';` +
|
|
31
|
+
`require('fs').existsSync(p)&&require(require('path').resolve(p))"`;
|
|
32
|
+
const HOOK_SCRIPTS = ['postinstall', 'dependencies'];
|
|
33
|
+
/**
|
|
34
|
+
* Steps the hook replaces: what an app ran before checkouts were workspace
|
|
35
|
+
* members, and the hook's first, unguarded form. Matched as a whole step of
|
|
36
|
+
* an `&&` chain.
|
|
37
|
+
*/
|
|
38
|
+
const REPLACED_STEP = /(^|&&)(\s*)(?:(?:npx )?linked localize --relink|node node_modules\/@_linked\/cli\/hooks\/lock-at-rest\.cjs)(\s*)(?=&&|$)/g;
|
|
39
|
+
const IGNORED = [`${LOCAL_DIR}/`, `${PARKED_DIR}/`];
|
|
40
|
+
/** Make the app ready for checkouts; returns what it changed. */
|
|
41
|
+
export function ensureAppSetup(appRoot) {
|
|
42
|
+
return [...setupPackageJson(appRoot), ...setupGitignore(appRoot)];
|
|
43
|
+
}
|
|
44
|
+
function setupPackageJson(appRoot) {
|
|
45
|
+
var _a, _b;
|
|
46
|
+
const file = path.join(appRoot, 'package.json');
|
|
47
|
+
const { text, pkg } = readPackageJson(file);
|
|
48
|
+
const changes = [];
|
|
49
|
+
const change = (what) => changes.push({ file: 'package.json', what });
|
|
50
|
+
// npm reads an array; the `{packages: [...]}` form is yarn's, and npm reads its `packages` too.
|
|
51
|
+
const ws = pkg.workspaces;
|
|
52
|
+
const globs = Array.isArray(ws)
|
|
53
|
+
? ws
|
|
54
|
+
: ws && typeof ws === 'object' && Array.isArray(ws.packages)
|
|
55
|
+
? ws.packages
|
|
56
|
+
: null;
|
|
57
|
+
if (!globs) {
|
|
58
|
+
if (ws !== undefined)
|
|
59
|
+
throw new LocalizeError(`package.json "workspaces" is neither a list nor {"packages": [...]}; ` +
|
|
60
|
+
`add "${WORKSPACE_GLOB}" to it by hand.`, EXIT_REFUSED);
|
|
61
|
+
pkg.workspaces = [WORKSPACE_GLOB];
|
|
62
|
+
change(`workspaces: ["${WORKSPACE_GLOB}"]`);
|
|
63
|
+
}
|
|
64
|
+
else if (!globs.includes(WORKSPACE_GLOB)) {
|
|
65
|
+
globs.push(WORKSPACE_GLOB);
|
|
66
|
+
change(`workspaces += "${WORKSPACE_GLOB}"`);
|
|
67
|
+
}
|
|
68
|
+
if (pkg.scripts === undefined)
|
|
69
|
+
pkg.scripts = {};
|
|
70
|
+
const scripts = pkg.scripts;
|
|
71
|
+
if (!scripts || typeof scripts !== 'object' || Array.isArray(scripts))
|
|
72
|
+
throw new LocalizeError('package.json "scripts" is not an object.', EXIT_REFUSED);
|
|
73
|
+
for (const name of HOOK_SCRIPTS) {
|
|
74
|
+
const before = scripts[name];
|
|
75
|
+
const next = withHook(typeof before === 'string' ? before : undefined);
|
|
76
|
+
if (next === before)
|
|
77
|
+
continue;
|
|
78
|
+
scripts[name] = next;
|
|
79
|
+
change(typeof before === 'string'
|
|
80
|
+
? `scripts.${name}: "${before}" -> "${next}"`
|
|
81
|
+
: `scripts.${name}: "${next}"`);
|
|
82
|
+
}
|
|
83
|
+
if (changes.length) {
|
|
84
|
+
const indent = (_b = (_a = text.match(/^([ \t]+)"/m)) === null || _a === void 0 ? void 0 : _a[1]) !== null && _b !== void 0 ? _b : ' ';
|
|
85
|
+
const eol = text.endsWith('\n') ? '\n' : '';
|
|
86
|
+
fs.writeFileSync(file, withEolOf(JSON.stringify(pkg, null, indent) + eol, text));
|
|
87
|
+
}
|
|
88
|
+
return changes;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* The script with the hook in it: the hook alone for none, an old
|
|
92
|
+
* `linked localize --relink` step or the unguarded hook replaced by it,
|
|
93
|
+
* anything else kept with the hook after it. A script that already runs the
|
|
94
|
+
* hook (or runs `hooks/lock-at-rest.cjs` some other way) is returned as it is.
|
|
95
|
+
*/
|
|
96
|
+
function withHook(script) {
|
|
97
|
+
if (script === undefined || !script.trim())
|
|
98
|
+
return LOCK_AT_REST_COMMAND;
|
|
99
|
+
if (script.includes(LOCK_AT_REST_COMMAND))
|
|
100
|
+
return script;
|
|
101
|
+
const replaced = script.replace(REPLACED_STEP, (_step, chain, before, after) => `${chain}${before}${LOCK_AT_REST_COMMAND}${after}`);
|
|
102
|
+
if (replaced !== script)
|
|
103
|
+
return replaced;
|
|
104
|
+
if (script.includes('hooks/lock-at-rest.cjs'))
|
|
105
|
+
return script;
|
|
106
|
+
return `${script} && ${LOCK_AT_REST_COMMAND}`;
|
|
107
|
+
}
|
|
108
|
+
function setupGitignore(appRoot) {
|
|
109
|
+
const file = path.join(appRoot, '.gitignore');
|
|
110
|
+
let text = '';
|
|
111
|
+
try {
|
|
112
|
+
text = fs.readFileSync(file, 'utf8');
|
|
113
|
+
}
|
|
114
|
+
catch (_a) {
|
|
115
|
+
/* none yet */
|
|
116
|
+
}
|
|
117
|
+
// `packages-local`, `/packages-local` and `packages-local/` all ignore the directory.
|
|
118
|
+
const have = new Set(text
|
|
119
|
+
.split(/\r?\n/)
|
|
120
|
+
.map((l) => l.trim().replace(/^\//, '').replace(/\/$/, '')));
|
|
121
|
+
const missing = IGNORED.filter((line) => !have.has(line.replace(/\/$/, '')));
|
|
122
|
+
if (!missing.length)
|
|
123
|
+
return [];
|
|
124
|
+
const sep = text === '' ? '' : text.endsWith('\n') ? '\n' : '\n\n';
|
|
125
|
+
const added = `${sep}# linked localize\n${missing.join('\n')}\n`;
|
|
126
|
+
fs.writeFileSync(file, text + withEolOf(added, text));
|
|
127
|
+
return missing.map((line) => ({ file: '.gitignore', what: `ignore ${line}` }));
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* The `overrides` keys -- at any depth, as `a > b` -- that name one of
|
|
131
|
+
* `names`. A root one makes npm fail with `EOVERRIDE` once that package is a
|
|
132
|
+
* workspace member; a nested one installs, but overrides the checkout the
|
|
133
|
+
* developer localized it to use.
|
|
134
|
+
*/
|
|
135
|
+
export function conflictingOverrides(appRoot, names) {
|
|
136
|
+
const { pkg } = readPackageJson(path.join(appRoot, 'package.json'));
|
|
137
|
+
const wanted = new Set(names);
|
|
138
|
+
const found = [];
|
|
139
|
+
const walk = (overrides, trail) => {
|
|
140
|
+
if (!overrides || typeof overrides !== 'object')
|
|
141
|
+
return;
|
|
142
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
143
|
+
if (key === '.')
|
|
144
|
+
continue;
|
|
145
|
+
const keyPath = [...trail, key];
|
|
146
|
+
if (wanted.has(overrideName(key)))
|
|
147
|
+
found.push(keyPath.join(' > '));
|
|
148
|
+
walk(value, keyPath);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
walk(pkg.overrides, []);
|
|
152
|
+
return found;
|
|
153
|
+
}
|
|
154
|
+
/** `@scope/name@^2` -> `@scope/name`, `name@1` -> `name`. */
|
|
155
|
+
function overrideName(key) {
|
|
156
|
+
const at = key.indexOf('@', 1);
|
|
157
|
+
return at === -1 ? key : key.slice(0, at);
|
|
158
|
+
}
|
|
159
|
+
function readPackageJson(file) {
|
|
160
|
+
let text;
|
|
161
|
+
try {
|
|
162
|
+
text = fs.readFileSync(file, 'utf8');
|
|
163
|
+
}
|
|
164
|
+
catch (_a) {
|
|
165
|
+
throw new LocalizeError(`no package.json at ${path.dirname(file)}.`, EXIT_REFUSED);
|
|
166
|
+
}
|
|
167
|
+
let pkg;
|
|
168
|
+
try {
|
|
169
|
+
pkg = JSON.parse(text);
|
|
170
|
+
}
|
|
171
|
+
catch (e) {
|
|
172
|
+
throw new LocalizeError(`${file} is not valid JSON: ${e.message}`, EXIT_REFUSED);
|
|
173
|
+
}
|
|
174
|
+
if (!pkg || typeof pkg !== 'object' || Array.isArray(pkg))
|
|
175
|
+
throw new LocalizeError(`${file} is not a JSON object.`, EXIT_REFUSED);
|
|
176
|
+
return { text, pkg: pkg };
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=app-setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-setup.js","sourceRoot":"","sources":["../../../src/localize/app-setup.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The one-time setup an app needs before a checkout can be a workspace\n * member, made by `linked localize` and committed by the developer:\n *\n * - `\"packages-local/*\"` in `workspaces`, so npm links and dedupes checkouts;\n * - `postinstall` and `dependencies` run the lock-at-rest hook, which puts the\n * committed `package-lock.json` back after npm rewrote it to list them;\n * - `.gitignore` keeps checkouts and parked checkouts out of git.\n *\n * Idempotent: a second run changes nothing and returns `[]`. `package.json` is\n * rewritten only when something changed, with its own indentation, key order,\n * line endings and trailing newline, so the diff is the setup and nothing else.\n *\n * It never edits `overrides`. npm refuses (`EOVERRIDE`) a root override on a\n * workspace member's name, and what to do about it -- drop the override or\n * not localize -- is the developer's call; `conflictingOverrides` names them.\n */\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport {withEolOf} from '../utils/platform.js';\nimport {LocalizeError, EXIT_REFUSED} from './errors.js';\nimport {LOCAL_DIR, PARKED_DIR} from './members.js';\n\nexport interface SetupChange {\n file: 'package.json' | '.gitignore';\n what: string;\n}\n\nconst WORKSPACE_GLOB = `${LOCAL_DIR}/*`;\n/**\n * What the app's `postinstall` and `dependencies` run: the hook by its path,\n * not through `linked` (see hooks/lock-at-rest.cjs), and only when the cli is\n * installed -- `npm ci --omit=dev` leaves it out, and the install must not\n * fail for that.\n */\nexport const LOCK_AT_REST_COMMAND =\n `node -e \"const p='node_modules/@_linked/cli/hooks/lock-at-rest.cjs';` +\n `require('fs').existsSync(p)&&require(require('path').resolve(p))\"`;\nconst HOOK_SCRIPTS = ['postinstall', 'dependencies'] as const;\n/**\n * Steps the hook replaces: what an app ran before checkouts were workspace\n * members, and the hook's first, unguarded form. Matched as a whole step of\n * an `&&` chain.\n */\nconst REPLACED_STEP =\n /(^|&&)(\\s*)(?:(?:npx )?linked localize --relink|node node_modules\\/@_linked\\/cli\\/hooks\\/lock-at-rest\\.cjs)(\\s*)(?=&&|$)/g;\nconst IGNORED = [`${LOCAL_DIR}/`, `${PARKED_DIR}/`];\n\n/** Make the app ready for checkouts; returns what it changed. */\nexport function ensureAppSetup(appRoot: string): SetupChange[] {\n return [...setupPackageJson(appRoot), ...setupGitignore(appRoot)];\n}\n\nfunction setupPackageJson(appRoot: string): SetupChange[] {\n const file = path.join(appRoot, 'package.json');\n const {text, pkg} = readPackageJson(file);\n const changes: SetupChange[] = [];\n const change = (what: string) => changes.push({file: 'package.json', what});\n\n // npm reads an array; the `{packages: [...]}` form is yarn's, and npm reads its `packages` too.\n const ws = pkg.workspaces;\n const globs: unknown[] | null = Array.isArray(ws)\n ? ws\n : ws && typeof ws === 'object' && Array.isArray(ws.packages)\n ? ws.packages\n : null;\n if (!globs) {\n if (ws !== undefined)\n throw new LocalizeError(\n `package.json \"workspaces\" is neither a list nor {\"packages\": [...]}; ` +\n `add \"${WORKSPACE_GLOB}\" to it by hand.`,\n EXIT_REFUSED,\n );\n pkg.workspaces = [WORKSPACE_GLOB];\n change(`workspaces: [\"${WORKSPACE_GLOB}\"]`);\n } else if (!globs.includes(WORKSPACE_GLOB)) {\n globs.push(WORKSPACE_GLOB);\n change(`workspaces += \"${WORKSPACE_GLOB}\"`);\n }\n\n if (pkg.scripts === undefined) pkg.scripts = {};\n const scripts = pkg.scripts;\n if (!scripts || typeof scripts !== 'object' || Array.isArray(scripts))\n throw new LocalizeError(\n 'package.json \"scripts\" is not an object.',\n EXIT_REFUSED,\n );\n for (const name of HOOK_SCRIPTS) {\n const before: unknown = scripts[name];\n const next = withHook(typeof before === 'string' ? before : undefined);\n if (next === before) continue;\n scripts[name] = next;\n change(\n typeof before === 'string'\n ? `scripts.${name}: \"${before}\" -> \"${next}\"`\n : `scripts.${name}: \"${next}\"`,\n );\n }\n\n if (changes.length) {\n const indent = text.match(/^([ \\t]+)\"/m)?.[1] ?? ' ';\n const eol = text.endsWith('\\n') ? '\\n' : '';\n fs.writeFileSync(\n file,\n withEolOf(JSON.stringify(pkg, null, indent) + eol, text),\n );\n }\n return changes;\n}\n\n/**\n * The script with the hook in it: the hook alone for none, an old\n * `linked localize --relink` step or the unguarded hook replaced by it,\n * anything else kept with the hook after it. A script that already runs the\n * hook (or runs `hooks/lock-at-rest.cjs` some other way) is returned as it is.\n */\nfunction withHook(script: string | undefined): string {\n if (script === undefined || !script.trim()) return LOCK_AT_REST_COMMAND;\n if (script.includes(LOCK_AT_REST_COMMAND)) return script;\n const replaced = script.replace(\n REPLACED_STEP,\n (_step, chain: string, before: string, after: string) =>\n `${chain}${before}${LOCK_AT_REST_COMMAND}${after}`,\n );\n if (replaced !== script) return replaced;\n if (script.includes('hooks/lock-at-rest.cjs')) return script;\n return `${script} && ${LOCK_AT_REST_COMMAND}`;\n}\n\nfunction setupGitignore(appRoot: string): SetupChange[] {\n const file = path.join(appRoot, '.gitignore');\n let text = '';\n try {\n text = fs.readFileSync(file, 'utf8');\n } catch {\n /* none yet */\n }\n // `packages-local`, `/packages-local` and `packages-local/` all ignore the directory.\n const have = new Set(\n text\n .split(/\\r?\\n/)\n .map((l) => l.trim().replace(/^\\//, '').replace(/\\/$/, '')),\n );\n const missing = IGNORED.filter((line) => !have.has(line.replace(/\\/$/, '')));\n if (!missing.length) return [];\n const sep = text === '' ? '' : text.endsWith('\\n') ? '\\n' : '\\n\\n';\n const added = `${sep}# linked localize\\n${missing.join('\\n')}\\n`;\n fs.writeFileSync(file, text + withEolOf(added, text));\n return missing.map((line) => ({file: '.gitignore', what: `ignore ${line}`}));\n}\n\n/**\n * The `overrides` keys -- at any depth, as `a > b` -- that name one of\n * `names`. A root one makes npm fail with `EOVERRIDE` once that package is a\n * workspace member; a nested one installs, but overrides the checkout the\n * developer localized it to use.\n */\nexport function conflictingOverrides(\n appRoot: string,\n names: string[],\n): string[] {\n const {pkg} = readPackageJson(path.join(appRoot, 'package.json'));\n const wanted = new Set(names);\n const found: string[] = [];\n const walk = (overrides: unknown, trail: string[]) => {\n if (!overrides || typeof overrides !== 'object') return;\n for (const [key, value] of Object.entries(overrides)) {\n if (key === '.') continue;\n const keyPath = [...trail, key];\n if (wanted.has(overrideName(key))) found.push(keyPath.join(' > '));\n walk(value, keyPath);\n }\n };\n walk(pkg.overrides, []);\n return found;\n}\n\n/** `@scope/name@^2` -> `@scope/name`, `name@1` -> `name`. */\nfunction overrideName(key: string): string {\n const at = key.indexOf('@', 1);\n return at === -1 ? key : key.slice(0, at);\n}\n\nfunction readPackageJson(file: string): {\n text: string;\n pkg: Record<string, any>;\n} {\n let text: string;\n try {\n text = fs.readFileSync(file, 'utf8');\n } catch {\n throw new LocalizeError(\n `no package.json at ${path.dirname(file)}.`,\n EXIT_REFUSED,\n );\n }\n let pkg: unknown;\n try {\n pkg = JSON.parse(text);\n } catch (e) {\n throw new LocalizeError(\n `${file} is not valid JSON: ${(e as Error).message}`,\n EXIT_REFUSED,\n );\n }\n if (!pkg || typeof pkg !== 'object' || Array.isArray(pkg))\n throw new LocalizeError(`${file} is not a JSON object.`, EXIT_REFUSED);\n return {text, pkg: pkg as Record<string, any>};\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delocalize.d.ts","sourceRoot":"","sources":["../../../src/localize/delocalize.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"delocalize.d.ts","sourceRoot":"","sources":["../../../src/localize/delocalize.ts"],"names":[],"mappings":"AA6CA,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,UAAU,CAAC;AAEnC,wBAAgB,UAAU,CACxB,KAAK,EAAE,MAAM,EAAE,EACf,IAAI,EAAE;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAC,EACxC,IAAI,EAAE,IAAI,GACT,MAAM,CAoCR"}
|