@_linked/cli 1.38.0 → 1.40.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.
Files changed (42) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +72 -8
  3. package/defaults/app-react-native/docker-compose.yml +1 -1
  4. package/lib/esm/cli-methods.d.ts +2 -1
  5. package/lib/esm/cli-methods.d.ts.map +1 -1
  6. package/lib/esm/cli-methods.js +219 -47
  7. package/lib/esm/cli-methods.js.map +1 -1
  8. package/lib/esm/cli.d.ts.map +1 -1
  9. package/lib/esm/cli.js +19 -5
  10. package/lib/esm/cli.js.map +1 -1
  11. package/lib/esm/commands/build-package.d.ts +1 -1
  12. package/lib/esm/commands/build-package.d.ts.map +1 -1
  13. package/lib/esm/commands/build-package.js +3 -2
  14. package/lib/esm/commands/build-package.js.map +1 -1
  15. package/lib/esm/commands/localize.d.ts +33 -0
  16. package/lib/esm/commands/localize.d.ts.map +1 -1
  17. package/lib/esm/commands/localize.js +72 -10
  18. package/lib/esm/commands/localize.js.map +1 -1
  19. package/lib/esm/installed-packages.d.ts +45 -5
  20. package/lib/esm/installed-packages.d.ts.map +1 -1
  21. package/lib/esm/installed-packages.js +90 -5
  22. package/lib/esm/installed-packages.js.map +1 -1
  23. package/lib/esm/lifecycle.d.ts +9 -2
  24. package/lib/esm/lifecycle.d.ts.map +1 -1
  25. package/lib/esm/lifecycle.js +45 -16
  26. package/lib/esm/lifecycle.js.map +1 -1
  27. package/lib/esm/localized-dedupe.d.ts +20 -0
  28. package/lib/esm/localized-dedupe.d.ts.map +1 -0
  29. package/lib/esm/localized-dedupe.js +182 -0
  30. package/lib/esm/localized-dedupe.js.map +1 -0
  31. package/lib/esm/utils/createPackageLocation.d.ts +76 -0
  32. package/lib/esm/utils/createPackageLocation.d.ts.map +1 -0
  33. package/lib/esm/utils/createPackageLocation.js +157 -0
  34. package/lib/esm/utils/createPackageLocation.js.map +1 -0
  35. package/lib/esm/utils.d.ts.map +1 -1
  36. package/lib/esm/utils.js +2 -1
  37. package/lib/esm/utils.js.map +1 -1
  38. package/lib/esm/vite-config.d.ts +8 -0
  39. package/lib/esm/vite-config.d.ts.map +1 -1
  40. package/lib/esm/vite-config.js +67 -48
  41. package/lib/esm/vite-config.js.map +1 -1
  42. package/package.json +3 -3
@@ -0,0 +1,182 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ /**
11
+ * What Vite's `resolve.dedupe` lists: the packages a LOCALIZED checkout could
12
+ * otherwise load a second copy of.
13
+ *
14
+ * The duplicates come from localization, not from being a linked package. A
15
+ * localized checkout (`linked localize`) is a git clone under the app root with
16
+ * its own `npm install`, so its `node_modules` holds its own copy of what it
17
+ * depends on — often older than the app's, and a registry copy of any sibling
18
+ * that is localized too. Vite serves the checkout's source and resolves its
19
+ * imports from there, so without a dedupe the dev server loads those copies next
20
+ * to the app's (measured: three `@_linked/core` in one SSR process).
21
+ *
22
+ * So the list is:
23
+ *
24
+ * - every localized package — always, whatever a range says: the live checkout
25
+ * is the point of localizing it, for every importer;
26
+ * - every runtime dependency a localized checkout declares (`dependencies`,
27
+ * `peerDependencies`, `optionalDependencies` — what its source imports) that
28
+ * the app has at a version satisfying EVERY localized checkout's range for it;
29
+ * - `react` and `react-dom` when the app has them.
30
+ *
31
+ * A dedupe resolves the name from the app root for EVERY importer and checks no
32
+ * version — measured: a checkout asking `motion@^13` got the app's 12.43.0. Two
33
+ * cases are therefore left out:
34
+ *
35
+ * - a range the app's version does not satisfy: the checkout keeps its own copy
36
+ * (reported, see `describeSkipped`);
37
+ * - a name some registry install in the app nests its own copy of. npm only
38
+ * nests when the hoisted copy does not satisfy that package, so deduping the
39
+ * name would hand it the wrong version.
40
+ *
41
+ * Plain Node, `tsx` and test runners never read this list; for them
42
+ * `linked localize` removes the checkout's copies from disk instead.
43
+ */
44
+ import fs from 'node:fs';
45
+ import path from 'node:path';
46
+ import semver from 'semver';
47
+ import { readInstalledPkg } from './installed-packages.js';
48
+ import { localizedPackageNames } from './lifecycle.js';
49
+ const RUNTIME_FIELDS = [
50
+ 'dependencies',
51
+ 'peerDependencies',
52
+ 'optionalDependencies',
53
+ ];
54
+ export function localizedDedupe() {
55
+ return __awaiter(this, arguments, void 0, function* (appRoot = process.cwd()) {
56
+ var _a, _b, _c, _d;
57
+ const localized = localizedPackageNames(appRoot).filter((name) => fs.existsSync(path.join(appRoot, 'node_modules', name, 'package.json')));
58
+ const names = new Set(localized);
59
+ const skipped = [];
60
+ // name -> every localized checkout's runtime range for it
61
+ const asks = new Map();
62
+ for (const from of localized) {
63
+ const pkg = (_a = readJson(path.join(appRoot, 'node_modules', from, 'package.json'))) !== null && _a !== void 0 ? _a : {};
64
+ for (const field of RUNTIME_FIELDS) {
65
+ for (const [dep, range] of Object.entries((_b = pkg[field]) !== null && _b !== void 0 ? _b : {})) {
66
+ if (names.has(dep))
67
+ continue;
68
+ const list = (_c = asks.get(dep)) !== null && _c !== void 0 ? _c : [];
69
+ if (!list.some((a) => a.from === from))
70
+ list.push({ from, range });
71
+ asks.set(dep, list);
72
+ }
73
+ }
74
+ }
75
+ const nested = asks.size
76
+ ? nestedCopies(path.join(appRoot, 'node_modules'))
77
+ : new Map();
78
+ for (const [name, ranges] of asks) {
79
+ const app = yield readInstalledPkg(name, appRoot);
80
+ if (!((_d = app === null || app === void 0 ? void 0 : app.json) === null || _d === void 0 ? void 0 : _d.version))
81
+ continue; // nothing at the root to dedupe to
82
+ const appVersion = app.json.version;
83
+ const unmet = ranges.filter((a) => !satisfies(appVersion, a.range));
84
+ if (unmet.length) {
85
+ skipped.push({ name, reason: 'range', asks: unmet, appVersion });
86
+ }
87
+ else if (nested.has(name)) {
88
+ skipped.push({ name, reason: 'nested', nestedAt: nested.get(name) });
89
+ }
90
+ else {
91
+ names.add(name);
92
+ }
93
+ }
94
+ for (const name of ['react', 'react-dom']) {
95
+ if (yield readInstalledPkg(name, appRoot))
96
+ names.add(name);
97
+ }
98
+ return { names: [...names].sort(), skipped };
99
+ });
100
+ }
101
+ /** One line for the dev log about the ranges the app does not satisfy; null when there are none. */
102
+ export function describeSkipped(skipped) {
103
+ const range = skipped.filter((s) => s.reason === 'range');
104
+ if (!range.length)
105
+ return null;
106
+ return (`[linked] dedupe: ${range.length} package${range.length === 1 ? '' : 's'} a localized checkout ` +
107
+ `keeps its own copy of, because the app's version is outside its range: ` +
108
+ range
109
+ .map((s) => `${s.name} (${s.asks.map((a) => `${a.from} asks ${a.range}`).join(', ')}; app ${s.appVersion})`)
110
+ .join(', '));
111
+ }
112
+ function satisfies(version, range) {
113
+ try {
114
+ return (semver.validRange(range) !== null && semver.satisfies(version, range));
115
+ }
116
+ catch (_a) {
117
+ return false;
118
+ }
119
+ }
120
+ /**
121
+ * Every package name nested below the top level of `nm` — a registry install's
122
+ * private copy — mapped to one place it sits. Symlinks (localized checkouts,
123
+ * workspaces) are not followed: their trees are not the app's install.
124
+ */
125
+ function nestedCopies(nm) {
126
+ const out = new Map();
127
+ const walk = (dir, depth) => {
128
+ for (const name of packageNames(dir)) {
129
+ const pkgDir = path.join(dir, name);
130
+ let stat;
131
+ try {
132
+ stat = fs.lstatSync(pkgDir);
133
+ }
134
+ catch (_a) {
135
+ continue;
136
+ }
137
+ if (stat.isSymbolicLink())
138
+ continue;
139
+ if (depth > 0 && !out.has(name))
140
+ out.set(name, path.relative(path.dirname(nm), pkgDir));
141
+ walk(path.join(pkgDir, 'node_modules'), depth + 1);
142
+ }
143
+ };
144
+ walk(nm, 0);
145
+ return out;
146
+ }
147
+ function packageNames(nm) {
148
+ let entries;
149
+ try {
150
+ entries = fs.readdirSync(nm);
151
+ }
152
+ catch (_a) {
153
+ return [];
154
+ }
155
+ const out = [];
156
+ for (const entry of entries) {
157
+ if (entry.startsWith('.'))
158
+ continue;
159
+ if (entry.startsWith('@')) {
160
+ try {
161
+ for (const sub of fs.readdirSync(path.join(nm, entry)))
162
+ out.push(`${entry}/${sub}`);
163
+ }
164
+ catch (_b) {
165
+ /* not a directory */
166
+ }
167
+ }
168
+ else {
169
+ out.push(entry);
170
+ }
171
+ }
172
+ return out;
173
+ }
174
+ function readJson(file) {
175
+ try {
176
+ return JSON.parse(fs.readFileSync(file, 'utf8'));
177
+ }
178
+ catch (_a) {
179
+ return null;
180
+ }
181
+ }
182
+ //# sourceMappingURL=localized-dedupe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"localized-dedupe.js","sourceRoot":"","sources":["../../src/localized-dedupe.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What Vite's `resolve.dedupe` lists: the packages a LOCALIZED checkout could\n * otherwise load a second copy of.\n *\n * The duplicates come from localization, not from being a linked package. A\n * localized checkout (`linked localize`) is a git clone under the app root with\n * its own `npm install`, so its `node_modules` holds its own copy of what it\n * depends on — often older than the app's, and a registry copy of any sibling\n * that is localized too. Vite serves the checkout's source and resolves its\n * imports from there, so without a dedupe the dev server loads those copies next\n * to the app's (measured: three `@_linked/core` in one SSR process).\n *\n * So the list is:\n *\n * - every localized package — always, whatever a range says: the live checkout\n * 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 it;\n * - `react` and `react-dom` when the app has them.\n *\n * A dedupe resolves the name from the app root for EVERY importer and checks no\n * version — measured: a checkout asking `motion@^13` got the app's 12.43.0. Two\n * cases are therefore left out:\n *\n * - a range the app's version does not satisfy: the checkout keeps its own copy\n * (reported, see `describeSkipped`);\n * - a name some registry install in the app nests its own copy of. npm only\n * nests when the hoisted copy does not satisfy that package, so deduping the\n * name would hand it the wrong version.\n *\n * Plain Node, `tsx` and test runners never read this list; for them\n * `linked localize` removes the checkout's copies from disk instead.\n */\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport semver from 'semver';\nimport {readInstalledPkg} from './installed-packages.js';\nimport {localizedPackageNames} from './lifecycle.js';\n\nconst RUNTIME_FIELDS = [\n 'dependencies',\n 'peerDependencies',\n 'optionalDependencies',\n];\n\nexport interface SkippedDedupe {\n name: string;\n reason: 'range' | 'nested';\n /** For `range`: who asks what, and what the app has. */\n asks?: {from: string; range: string}[];\n appVersion?: string;\n /** For `nested`: one registry install that carries its own copy. */\n nestedAt?: string;\n}\n\nexport interface LocalizedDedupe {\n names: string[];\n skipped: SkippedDedupe[];\n}\n\nexport async function localizedDedupe(\n appRoot: string = process.cwd(),\n): Promise<LocalizedDedupe> {\n const localized = localizedPackageNames(appRoot).filter((name) =>\n fs.existsSync(path.join(appRoot, 'node_modules', name, 'package.json')),\n );\n const names = new Set<string>(localized);\n const skipped: SkippedDedupe[] = [];\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 const nested = asks.size\n ? nestedCopies(path.join(appRoot, 'node_modules'))\n : new Map();\n for (const [name, ranges] of asks) {\n const app = await readInstalledPkg(name, appRoot);\n if (!app?.json?.version) continue; // nothing at the root to dedupe to\n const appVersion = app.json.version;\n const unmet = ranges.filter((a) => !satisfies(appVersion, a.range));\n if (unmet.length) {\n skipped.push({name, reason: 'range', asks: unmet, appVersion});\n } else if (nested.has(name)) {\n skipped.push({name, reason: 'nested', nestedAt: nested.get(name)});\n } else {\n names.add(name);\n }\n }\n\n for (const name of ['react', 'react-dom']) {\n if (await readInstalledPkg(name, appRoot)) names.add(name);\n }\n return {names: [...names].sort(), skipped};\n}\n\n/** One line for the dev log about the ranges the app does not satisfy; null when there are none. */\nexport function describeSkipped(skipped: SkippedDedupe[]): string | null {\n const range = skipped.filter((s) => s.reason === 'range');\n if (!range.length) return null;\n return (\n `[linked] dedupe: ${range.length} package${range.length === 1 ? '' : 's'} a localized checkout ` +\n `keeps its own copy of, because the app's version is outside its range: ` +\n range\n .map(\n (s) =>\n `${s.name} (${s.asks!.map((a) => `${a.from} asks ${a.range}`).join(', ')}; app ${s.appVersion})`,\n )\n .join(', ')\n );\n}\n\nfunction satisfies(version: string, range: string): boolean {\n try {\n return (\n semver.validRange(range) !== null && semver.satisfies(version, range)\n );\n } catch {\n return false;\n }\n}\n\n/**\n * Every package name nested below the top level of `nm` — a registry install's\n * private copy — mapped to one place it sits. Symlinks (localized checkouts,\n * workspaces) are not followed: their trees are not the app's install.\n */\nfunction nestedCopies(nm: string): Map<string, string> {\n const out = new Map<string, string>();\n const walk = (dir: string, depth: number) => {\n for (const name of packageNames(dir)) {\n const pkgDir = path.join(dir, name);\n let stat: fs.Stats;\n try {\n stat = fs.lstatSync(pkgDir);\n } catch {\n continue;\n }\n if (stat.isSymbolicLink()) continue;\n if (depth > 0 && !out.has(name))\n out.set(name, path.relative(path.dirname(nm), pkgDir));\n walk(path.join(pkgDir, 'node_modules'), depth + 1);\n }\n };\n walk(nm, 0);\n return out;\n}\n\nfunction packageNames(nm: string): string[] {\n let entries: string[];\n try {\n entries = fs.readdirSync(nm);\n } catch {\n return [];\n }\n const out: string[] = [];\n for (const entry of entries) {\n if (entry.startsWith('.')) continue;\n if (entry.startsWith('@')) {\n try {\n for (const sub of fs.readdirSync(path.join(nm, entry)))\n out.push(`${entry}/${sub}`);\n } catch {\n /* not a directory */\n }\n } else {\n out.push(entry);\n }\n }\n return out;\n}\n\nfunction readJson(file: string): any {\n try {\n return JSON.parse(fs.readFileSync(file, 'utf8'));\n } catch {\n return null;\n }\n}\n"]}
@@ -0,0 +1,76 @@
1
+ export declare const PACKAGE_LOCATIONS: readonly ["packages", "packages-local"];
2
+ export type PackageLocation = (typeof PACKAGE_LOCATIONS)[number];
3
+ export interface CreatePackageOptions {
4
+ /** Validated at runtime too, since the command line passes any string. */
5
+ location?: PackageLocation;
6
+ /** A git URL. Implies `packages-local`. */
7
+ remote?: string;
8
+ /** Push the initial commit. Needs `remote`. */
9
+ push?: boolean;
10
+ }
11
+ /**
12
+ * - `outside`: no app above us — the old behaviour, unchanged.
13
+ * - `resolved`: the flags (or the prompt's answers) settle it.
14
+ * - `ask`: inside an app, no flags, on a terminal.
15
+ * - `refuse`: a contradiction, or no answer and no terminal to ask.
16
+ */
17
+ export type TargetDecision = {
18
+ kind: 'outside';
19
+ } | {
20
+ kind: 'resolved';
21
+ location: PackageLocation;
22
+ remote?: string;
23
+ push: boolean;
24
+ } | {
25
+ kind: 'ask';
26
+ } | {
27
+ kind: 'refuse';
28
+ message: string;
29
+ };
30
+ /** A refusal of create-package's own, as opposed to a crash. */
31
+ export declare class CreatePackageError extends Error {
32
+ constructor(message: string);
33
+ }
34
+ /**
35
+ * The nearest directory, from `start` upwards, whose package.json says
36
+ * `linkedApp: true`. That is the app — whether or not it has `workspaces`,
37
+ * which the app template does not.
38
+ */
39
+ export declare function findLinkedAppRoot(start: string): string | null;
40
+ /**
41
+ * Flags decide everything they can. Any of `--location`, `--remote` or
42
+ * `--push` is taken as the complete answer, so a scripted call never meets a
43
+ * prompt; with none of them, a terminal is asked and anything else is refused.
44
+ */
45
+ export declare function decideCreatePackageTarget({ appRoot, options, isTTY, }: {
46
+ appRoot: string | null;
47
+ options: CreatePackageOptions;
48
+ isTTY: boolean;
49
+ }): TargetDecision;
50
+ export declare function isPackageLocation(value: string): value is PackageLocation;
51
+ /** `@scope/foo-bar` → `foo-bar`: the folder name under `packages/`. */
52
+ export declare function bareName(name: string): string;
53
+ /**
54
+ * Make sure the app's `workspaces` covers `packages/*`, so a package created
55
+ * there is a workspace member. Mutates `pkg`.
56
+ *
57
+ * @returns whether it changed, and a warning when it cannot tell.
58
+ */
59
+ export declare function ensureWorkspaceGlob(pkg: Record<string, any>): {
60
+ changed: boolean;
61
+ warning?: string;
62
+ };
63
+ export declare const RELINK_COMMAND = "linked localize --relink";
64
+ /**
65
+ * Make sure the app's `postinstall` recreates localize's links. Without it the
66
+ * next `npm install` prunes a packages-local link as extraneous. With no
67
+ * `local-packages.json` the hook prints nothing and exits 0, so it is safe to
68
+ * commit for everyone. Mutates `pkg`.
69
+ */
70
+ export declare function ensureRelinkPostinstall(pkg: Record<string, any>): boolean;
71
+ /**
72
+ * A git remote as npm wants it in `repository.url`: `git+https://…`, or
73
+ * `git+ssh://git@host/path` for the scp-like `git@host:path` form.
74
+ */
75
+ export declare function repositoryUrlFor(remote: string): string;
76
+ //# sourceMappingURL=createPackageLocation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createPackageLocation.d.ts","sourceRoot":"","sources":["../../../src/utils/createPackageLocation.ts"],"names":[],"mappings":"AAgBA,eAAO,MAAM,iBAAiB,yCAA0C,CAAC;AACzE,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjE,MAAM,WAAW,oBAAoB;IACnC,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+CAA+C;IAC/C,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GACtB;IAAC,IAAI,EAAE,SAAS,CAAA;CAAC,GACjB;IAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,eAAe,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAC,GAC7E;IAAC,IAAI,EAAE,KAAK,CAAA;CAAC,GACb;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,CAAC;AAEtC,gEAAgE;AAChE,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAgB9D;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,EACxC,OAAO,EACP,OAAO,EACP,KAAK,GACN,EAAE;IACD,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,oBAAoB,CAAC;IAC9B,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,cAAc,CAwCjB;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,eAAe,CAEzE;AAMD,uEAAuE;AACvE,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAM7C;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG;IAC7D,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAsBA;AAED,eAAO,MAAM,cAAc,6BAA6B,CAAC;AAEzD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAQzE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAOvD"}
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Where `linked create-package` puts a new package, decided without touching
3
+ * anything — the IO lives in `createPackage`.
4
+ *
5
+ * Inside an app there are two homes, and they mean different things:
6
+ *
7
+ * - `packages/` — the package is part of the app's own repository: a tracked
8
+ * npm workspace member, declared as a dependency like any other.
9
+ * - `packages-local/` — the package has a repository of its own. It goes where
10
+ * `linked localize` keeps checkouts, under the same name localize would give
11
+ * it (`@_linked/foo` → `packages-local/_linked-foo`), and is handed to
12
+ * localize's `adopt`, so the two can never disagree about it.
13
+ */
14
+ import fs from 'fs';
15
+ import path from 'path';
16
+ export const PACKAGE_LOCATIONS = ['packages', 'packages-local'];
17
+ /** A refusal of create-package's own, as opposed to a crash. */
18
+ export class CreatePackageError extends Error {
19
+ constructor(message) {
20
+ super(message);
21
+ this.name = 'CreatePackageError';
22
+ }
23
+ }
24
+ /**
25
+ * The nearest directory, from `start` upwards, whose package.json says
26
+ * `linkedApp: true`. That is the app — whether or not it has `workspaces`,
27
+ * which the app template does not.
28
+ */
29
+ export function findLinkedAppRoot(start) {
30
+ let dir = path.resolve(start);
31
+ // eslint-disable-next-line no-constant-condition
32
+ while (true) {
33
+ try {
34
+ const pkg = JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf8'));
35
+ if ((pkg === null || pkg === void 0 ? void 0 : pkg.linkedApp) === true)
36
+ return dir;
37
+ }
38
+ catch (_a) {
39
+ // no package.json here, or not one we can read: keep walking
40
+ }
41
+ const parent = path.dirname(dir);
42
+ if (parent === dir)
43
+ return null;
44
+ dir = parent;
45
+ }
46
+ }
47
+ /**
48
+ * Flags decide everything they can. Any of `--location`, `--remote` or
49
+ * `--push` is taken as the complete answer, so a scripted call never meets a
50
+ * prompt; with none of them, a terminal is asked and anything else is refused.
51
+ */
52
+ export function decideCreatePackageTarget({ appRoot, options, isTTY, }) {
53
+ var _a;
54
+ const { remote, push = false } = options;
55
+ const location = options.location;
56
+ const given = location !== undefined || remote !== undefined || push;
57
+ if (location !== undefined && !isPackageLocation(location)) {
58
+ return refuse(`--location must be one of ${PACKAGE_LOCATIONS.join(', ')}, not "${location}".`);
59
+ }
60
+ if (!appRoot) {
61
+ return given
62
+ ? refuse(`--location, --remote and --push only apply inside a linked app, and no package.json ` +
63
+ `with "linkedApp": true was found above this directory.`)
64
+ : { kind: 'outside' };
65
+ }
66
+ if (push && !remote) {
67
+ return refuse('--push needs --remote: there is nowhere to push to.');
68
+ }
69
+ if (remote && location === 'packages') {
70
+ return refuse(`--remote gives the package a repository of its own, but packages/ is for packages that ` +
71
+ `live in the app's repository. Use --location packages-local, or drop --remote.`);
72
+ }
73
+ if (given) {
74
+ return Object.assign(Object.assign({ kind: 'resolved', location: (_a = location) !== null && _a !== void 0 ? _a : 'packages-local' }, (remote ? { remote } : {})), { push });
75
+ }
76
+ if (isTTY)
77
+ return { kind: 'ask' };
78
+ return refuse(`Where should the package go? Pass --location packages (part of this app's repository) or ` +
79
+ `--location packages-local (its own repository, optionally with --remote <git-url>).`);
80
+ }
81
+ export function isPackageLocation(value) {
82
+ return PACKAGE_LOCATIONS.includes(value);
83
+ }
84
+ function refuse(message) {
85
+ return { kind: 'refuse', message };
86
+ }
87
+ /** `@scope/foo-bar` → `foo-bar`: the folder name under `packages/`. */
88
+ export function bareName(name) {
89
+ const match = name.match(/(@[\w\-]+\/)?([\w\-]+)/);
90
+ if (!match) {
91
+ throw new CreatePackageError(`"${name}" is not a usable package name.`);
92
+ }
93
+ return match[2];
94
+ }
95
+ /**
96
+ * Make sure the app's `workspaces` covers `packages/*`, so a package created
97
+ * there is a workspace member. Mutates `pkg`.
98
+ *
99
+ * @returns whether it changed, and a warning when it cannot tell.
100
+ */
101
+ export function ensureWorkspaceGlob(pkg) {
102
+ var _a;
103
+ const covers = (globs) => globs.some((g) => g === 'packages/*' || g === 'packages/**');
104
+ if (pkg.workspaces === undefined) {
105
+ pkg.workspaces = ['packages/*'];
106
+ return { changed: true };
107
+ }
108
+ const globs = Array.isArray(pkg.workspaces)
109
+ ? pkg.workspaces
110
+ : Array.isArray((_a = pkg.workspaces) === null || _a === void 0 ? void 0 : _a.packages)
111
+ ? pkg.workspaces.packages
112
+ : null;
113
+ if (!globs) {
114
+ return {
115
+ changed: false,
116
+ warning: `the app's "workspaces" field has a shape this command does not edit; make sure it covers packages/*.`,
117
+ };
118
+ }
119
+ if (covers(globs))
120
+ return { changed: false };
121
+ globs.push('packages/*');
122
+ return { changed: true };
123
+ }
124
+ export const RELINK_COMMAND = 'linked localize --relink';
125
+ /**
126
+ * Make sure the app's `postinstall` recreates localize's links. Without it the
127
+ * next `npm install` prunes a packages-local link as extraneous. With no
128
+ * `local-packages.json` the hook prints nothing and exits 0, so it is safe to
129
+ * commit for everyone. Mutates `pkg`.
130
+ */
131
+ export function ensureRelinkPostinstall(pkg) {
132
+ var _a;
133
+ pkg.scripts = (_a = pkg.scripts) !== null && _a !== void 0 ? _a : {};
134
+ const current = pkg.scripts.postinstall;
135
+ if (current && /localize\s+--relink/.test(current))
136
+ return false;
137
+ pkg.scripts.postinstall = current
138
+ ? `${current} && ${RELINK_COMMAND}`
139
+ : RELINK_COMMAND;
140
+ return true;
141
+ }
142
+ /**
143
+ * A git remote as npm wants it in `repository.url`: `git+https://…`, or
144
+ * `git+ssh://git@host/path` for the scp-like `git@host:path` form.
145
+ */
146
+ export function repositoryUrlFor(remote) {
147
+ const url = remote.trim();
148
+ if (url.startsWith('git+'))
149
+ return url;
150
+ const scp = /^([\w.-]+@[\w.-]+):(?!\/)(.+)$/.exec(url);
151
+ if (scp)
152
+ return `git+ssh://${scp[1]}/${scp[2]}`;
153
+ if (/^(https?|ssh):\/\//.test(url))
154
+ return `git+${url}`;
155
+ return url;
156
+ }
157
+ //# sourceMappingURL=createPackageLocation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createPackageLocation.js","sourceRoot":"","sources":["../../../src/utils/createPackageLocation.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Where `linked create-package` puts a new package, decided without touching\n * anything — the IO lives in `createPackage`.\n *\n * Inside an app there are two homes, and they mean different things:\n *\n * - `packages/` — the package is part of the app's own repository: a tracked\n * npm workspace member, declared as a dependency like any other.\n * - `packages-local/` — the package has a repository of its own. It goes where\n * `linked localize` keeps checkouts, under the same name localize would give\n * it (`@_linked/foo` → `packages-local/_linked-foo`), and is handed to\n * localize's `adopt`, so the two can never disagree about it.\n */\nimport fs from 'fs';\nimport path from 'path';\n\nexport const PACKAGE_LOCATIONS = ['packages', 'packages-local'] as const;\nexport type PackageLocation = (typeof PACKAGE_LOCATIONS)[number];\n\nexport interface CreatePackageOptions {\n /** Validated at runtime too, since the command line passes any string. */\n location?: PackageLocation;\n /** A git URL. Implies `packages-local`. */\n remote?: string;\n /** Push the initial commit. Needs `remote`. */\n push?: boolean;\n}\n\n/**\n * - `outside`: no app above us — the old behaviour, unchanged.\n * - `resolved`: the flags (or the prompt's answers) settle it.\n * - `ask`: inside an app, no flags, on a terminal.\n * - `refuse`: a contradiction, or no answer and no terminal to ask.\n */\nexport type TargetDecision =\n | {kind: 'outside'}\n | {kind: 'resolved'; location: PackageLocation; remote?: string; push: boolean}\n | {kind: 'ask'}\n | {kind: 'refuse'; message: string};\n\n/** A refusal of create-package's own, as opposed to a crash. */\nexport class CreatePackageError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'CreatePackageError';\n }\n}\n\n/**\n * The nearest directory, from `start` upwards, whose package.json says\n * `linkedApp: true`. That is the app — whether or not it has `workspaces`,\n * which the app template does not.\n */\nexport function findLinkedAppRoot(start: string): string | null {\n let dir = path.resolve(start);\n // eslint-disable-next-line no-constant-condition\n while (true) {\n try {\n const pkg = JSON.parse(\n fs.readFileSync(path.join(dir, 'package.json'), 'utf8'),\n );\n if (pkg?.linkedApp === true) return dir;\n } catch {\n // no package.json here, or not one we can read: keep walking\n }\n const parent = path.dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\n/**\n * Flags decide everything they can. Any of `--location`, `--remote` or\n * `--push` is taken as the complete answer, so a scripted call never meets a\n * prompt; with none of them, a terminal is asked and anything else is refused.\n */\nexport function decideCreatePackageTarget({\n appRoot,\n options,\n isTTY,\n}: {\n appRoot: string | null;\n options: CreatePackageOptions;\n isTTY: boolean;\n}): TargetDecision {\n const {remote, push = false} = options;\n const location: string | undefined = options.location;\n const given = location !== undefined || remote !== undefined || push;\n\n if (location !== undefined && !isPackageLocation(location)) {\n return refuse(\n `--location must be one of ${PACKAGE_LOCATIONS.join(', ')}, not \"${location}\".`,\n );\n }\n if (!appRoot) {\n return given\n ? refuse(\n `--location, --remote and --push only apply inside a linked app, and no package.json ` +\n `with \"linkedApp\": true was found above this directory.`,\n )\n : {kind: 'outside'};\n }\n if (push && !remote) {\n return refuse('--push needs --remote: there is nowhere to push to.');\n }\n if (remote && location === 'packages') {\n return refuse(\n `--remote gives the package a repository of its own, but packages/ is for packages that ` +\n `live in the app's repository. Use --location packages-local, or drop --remote.`,\n );\n }\n if (given) {\n return {\n kind: 'resolved',\n location: (location as PackageLocation) ?? 'packages-local',\n ...(remote ? {remote} : {}),\n push,\n };\n }\n if (isTTY) return {kind: 'ask'};\n return refuse(\n `Where should the package go? Pass --location packages (part of this app's repository) or ` +\n `--location packages-local (its own repository, optionally with --remote <git-url>).`,\n );\n}\n\nexport function isPackageLocation(value: string): value is PackageLocation {\n return (PACKAGE_LOCATIONS as readonly string[]).includes(value);\n}\n\nfunction refuse(message: string): TargetDecision {\n return {kind: 'refuse', message};\n}\n\n/** `@scope/foo-bar` → `foo-bar`: the folder name under `packages/`. */\nexport function bareName(name: string): string {\n const match = name.match(/(@[\\w\\-]+\\/)?([\\w\\-]+)/);\n if (!match) {\n throw new CreatePackageError(`\"${name}\" is not a usable package name.`);\n }\n return match[2];\n}\n\n/**\n * Make sure the app's `workspaces` covers `packages/*`, so a package created\n * there is a workspace member. Mutates `pkg`.\n *\n * @returns whether it changed, and a warning when it cannot tell.\n */\nexport function ensureWorkspaceGlob(pkg: Record<string, any>): {\n changed: boolean;\n warning?: string;\n} {\n const covers = (globs: unknown[]) =>\n globs.some((g) => g === 'packages/*' || g === 'packages/**');\n\n if (pkg.workspaces === undefined) {\n pkg.workspaces = ['packages/*'];\n return {changed: true};\n }\n const globs = Array.isArray(pkg.workspaces)\n ? pkg.workspaces\n : Array.isArray(pkg.workspaces?.packages)\n ? pkg.workspaces.packages\n : null;\n if (!globs) {\n return {\n changed: false,\n warning: `the app's \"workspaces\" field has a shape this command does not edit; make sure it covers packages/*.`,\n };\n }\n if (covers(globs)) return {changed: false};\n globs.push('packages/*');\n return {changed: true};\n}\n\nexport const RELINK_COMMAND = 'linked localize --relink';\n\n/**\n * Make sure the app's `postinstall` recreates localize's links. Without it the\n * next `npm install` prunes a packages-local link as extraneous. With no\n * `local-packages.json` the hook prints nothing and exits 0, so it is safe to\n * commit for everyone. Mutates `pkg`.\n */\nexport function ensureRelinkPostinstall(pkg: Record<string, any>): boolean {\n pkg.scripts = pkg.scripts ?? {};\n const current: string | undefined = pkg.scripts.postinstall;\n if (current && /localize\\s+--relink/.test(current)) return false;\n pkg.scripts.postinstall = current\n ? `${current} && ${RELINK_COMMAND}`\n : RELINK_COMMAND;\n return true;\n}\n\n/**\n * A git remote as npm wants it in `repository.url`: `git+https://…`, or\n * `git+ssh://git@host/path` for the scp-like `git@host:path` form.\n */\nexport function repositoryUrlFor(remote: string): string {\n const url = remote.trim();\n if (url.startsWith('git+')) return url;\n const scp = /^([\\w.-]+@[\\w.-]+):(?!\\/)(.+)$/.exec(url);\n if (scp) return `git+ssh://${scp[1]}/${scp[2]}`;\n if (/^(https?|ssh):\\/\\//.test(url)) return `git+${url}`;\n return url;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AACA,OAAO,EAAO,WAAW,EAAC,MAAM,eAAe,CAAC;AAKhD,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,iBAAiB,CAAC;AAsBpD,eAAO,IAAI,cAAc,GAAmB,aAAQ,sBAwEnD,CAAC;AA4BF;;;;;;;;;;;;;GAaG;AACH,eAAO,IAAI,sBAAsB,GAAa,YAAY,MAAM,YAQ/D,CAAC;AACF;;;;;;;;GAQG;AACH,eAAO,IAAI,wBAAwB,GACjC,YAAY,MAAM,EAClB,cAAc,MAAM,YAerB,CAAC;AAEF,eAAO,IAAI,cAAc,GAAa,aAAoB,EAAE,eAAY,QAcvE,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,IAAI,oBAAoB,GAC7B,iBAAY,EACZ,kBAAiB,GAAG,CAAC,MAAM,CAAa,KACvC,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EA2I5B,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,gBAAW;;;;CAE3C,CAAC;AAEF,eAAO,MAAM,yBAAyB,GAAI,gBAAW;;;;CAUpD,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,gBAAW,KACV,OAAO,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAC,CAoBzD,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,aAAQ,EAAE,WAAW;;;;CAsBxD,CAAC;AAGF,eAAO,IAAI,oBAAoB,GAAa,gBAAW,EAAE,cAAS,QA2BjE,CAAC;AACF,wBAAgB,KAAK,CACnB,GAAG,KAAA,EACH,GAAG,GAAE,OAAe,EACpB,UAAU,GAAE,OAAe,EAC3B,OAAO,GAAE,GAAQ,GAChB,OAAO,CAAC,IAAI,CAAC,CAmDf;AAED,wBAAgB,WAAW,CACzB,OAAO,KAAA,EACP,GAAG,UAAQ,EACX,UAAU,GAAE,OAAe,EAC3B,OAAO,CAAC,EAAE,WAAW,EACrB,UAAU,GAAE,OAAe,GAC1B,OAAO,CAAC,MAAM,CAAC,CA2BjB;AAED,OAAO,EAAC,4BAA4B,EAAC,MAAM,uBAAuB,CAAC;AACnE,wBAAgB,kBAAkB,CAAC,YAAY,KAAA,EAAE,QAAQ,KAAA,EAAE,GAAG,CAAC,KAAA,UAwB9D;AAED,eAAO,MAAM,eAAe,GAC1B,KAAK,cAAc,EACnB,uBAAuB,OAAO,EAC9B,MAAK,OAAe,qBAyCrB,CAAC;AAEF,wBAAgB,GAAG,CAAC,GAAG,QAAQ,OAAA,QAI9B;AAED,wBAAgB,KAAK,CAAC,MAAM,KAAA,EAAE,GAAG,QAAQ,OAAA,QAIxC;AAED,wBAAgB,SAAS,CAAC,GAAG,QAAQ,OAAA,QAiBpC;AAED,wBAAgB,IAAI,CAAC,GAAG,QAAQ,OAAA,QAI/B;AAED,wBAAgB,OAAO,CAAC,GAAG,KAAA,OAI1B;AAED,wBAAgB,uBAAuB;;;EAKtC;AAED;;;;;GAKG;AACH,wBAAsB,QAAQ,CAC5B,GAAG,EAAE,MAAM,EACX,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,MAAM,EAAE,CAAC,CAenB;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE7C"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AACA,OAAO,EAAO,WAAW,EAAC,MAAM,eAAe,CAAC;AAKhD,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,iBAAiB,CAAC;AAuBpD,eAAO,IAAI,cAAc,GAAmB,aAAQ,sBAwEnD,CAAC;AA4BF;;;;;;;;;;;;;GAaG;AACH,eAAO,IAAI,sBAAsB,GAAa,YAAY,MAAM,YAQ/D,CAAC;AACF;;;;;;;;GAQG;AACH,eAAO,IAAI,wBAAwB,GACjC,YAAY,MAAM,EAClB,cAAc,MAAM,YAerB,CAAC;AAEF,eAAO,IAAI,cAAc,GAAa,aAAoB,EAAE,eAAY,QAcvE,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,IAAI,oBAAoB,GAC7B,iBAAY,EACZ,kBAAiB,GAAG,CAAC,MAAM,CAAa,KACvC,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EA2I5B,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,gBAAW;;;;CAE3C,CAAC;AAEF,eAAO,MAAM,yBAAyB,GAAI,gBAAW;;;;CAUpD,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,gBAAW,KACV,OAAO,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAC,CAoBzD,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,aAAQ,EAAE,WAAW;;;;CAsBxD,CAAC;AAGF,eAAO,IAAI,oBAAoB,GAAa,gBAAW,EAAE,cAAS,QA2BjE,CAAC;AACF,wBAAgB,KAAK,CACnB,GAAG,KAAA,EACH,GAAG,GAAE,OAAe,EACpB,UAAU,GAAE,OAAe,EAC3B,OAAO,GAAE,GAAQ,GAChB,OAAO,CAAC,IAAI,CAAC,CAmDf;AAED,wBAAgB,WAAW,CACzB,OAAO,KAAA,EACP,GAAG,UAAQ,EACX,UAAU,GAAE,OAAe,EAC3B,OAAO,CAAC,EAAE,WAAW,EACrB,UAAU,GAAE,OAAe,GAC1B,OAAO,CAAC,MAAM,CAAC,CA2BjB;AAED,OAAO,EAAC,4BAA4B,EAAC,MAAM,uBAAuB,CAAC;AACnE,wBAAgB,kBAAkB,CAAC,YAAY,KAAA,EAAE,QAAQ,KAAA,EAAE,GAAG,CAAC,KAAA,UAwB9D;AAED,eAAO,MAAM,eAAe,GAC1B,KAAK,cAAc,EACnB,uBAAuB,OAAO,EAC9B,MAAK,OAAe,qBAyCrB,CAAC;AAEF,wBAAgB,GAAG,CAAC,GAAG,QAAQ,OAAA,QAI9B;AAED,wBAAgB,KAAK,CAAC,MAAM,KAAA,EAAE,GAAG,QAAQ,OAAA,QAIxC;AAED,wBAAgB,SAAS,CAAC,GAAG,QAAQ,OAAA,QAiBpC;AAED,wBAAgB,IAAI,CAAC,GAAG,QAAQ,OAAA,QAI/B;AAED,wBAAgB,OAAO,CAAC,GAAG,KAAA,OAI1B;AAED,wBAAgB,uBAAuB;;;EAKtC;AAED;;;;;GAKG;AACH,wBAAsB,QAAQ,CAC5B,GAAG,EAAE,MAAM,EACX,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,MAAM,EAAE,CAAC,CAenB;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE7C"}
package/lib/esm/utils.js CHANGED
@@ -13,6 +13,7 @@ import * as fs from 'fs';
13
13
  import { builtinModules } from 'module';
14
14
  import * as path from 'path';
15
15
  import ts from 'typescript';
16
+ import { isLinkedPackageJson } from './installed-packages.js';
16
17
  import { findNearestPackageJsonSync } from 'find-nearest-package-json';
17
18
  import * as glob from 'glob';
18
19
  var gruntConfig;
@@ -194,7 +195,7 @@ export var getLINCDDependencies = function (packageJson, checkedPackages = new S
194
195
  if (!checkedPackages.has(dependency)) {
195
196
  let [modulePackageJson, modulePath] = getModulePackageJSON(dependency);
196
197
  checkedPackages.add(dependency);
197
- if (modulePackageJson === null || modulePackageJson === void 0 ? void 0 : modulePackageJson.lincd) {
198
+ if (isLinkedPackageJson(modulePackageJson)) {
198
199
  lincdPackagePaths.push([
199
200
  modulePackageJson.name,
200
201
  modulePath,