@_linked/cli 1.44.1 → 1.45.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 (35) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +36 -2
  3. package/defaults/package/src/index.ts +0 -5
  4. package/defaults/package-ontology/src/index.ts +8 -0
  5. package/defaults/shape.ts +13 -10
  6. package/lib/esm/cli-methods.d.ts +10 -1
  7. package/lib/esm/cli-methods.d.ts.map +1 -1
  8. package/lib/esm/cli-methods.js +252 -97
  9. package/lib/esm/cli-methods.js.map +1 -1
  10. package/lib/esm/cli.js +60 -17
  11. package/lib/esm/cli.js.map +1 -1
  12. package/lib/esm/localize/localize.d.ts.map +1 -1
  13. package/lib/esm/localize/localize.js +25 -3
  14. package/lib/esm/localize/localize.js.map +1 -1
  15. package/lib/esm/utils/createPackageLocation.d.ts +20 -5
  16. package/lib/esm/utils/createPackageLocation.d.ts.map +1 -1
  17. package/lib/esm/utils/createPackageLocation.js +9 -14
  18. package/lib/esm/utils/createPackageLocation.js.map +1 -1
  19. package/lib/esm/utils/packageBaseUri.d.ts +27 -0
  20. package/lib/esm/utils/packageBaseUri.d.ts.map +1 -0
  21. package/lib/esm/utils/packageBaseUri.js +91 -0
  22. package/lib/esm/utils/packageBaseUri.js.map +1 -0
  23. package/lib/esm/utils/packageKind.d.ts +49 -0
  24. package/lib/esm/utils/packageKind.d.ts.map +1 -0
  25. package/lib/esm/utils/packageKind.js +94 -0
  26. package/lib/esm/utils/packageKind.js.map +1 -0
  27. package/lib/esm/utils/relinkHooks.d.ts +19 -0
  28. package/lib/esm/utils/relinkHooks.d.ts.map +1 -0
  29. package/lib/esm/utils/relinkHooks.js +74 -0
  30. package/lib/esm/utils/relinkHooks.js.map +1 -0
  31. package/package.json +1 -1
  32. /package/defaults/{package → package-ontology}/src/data/example-ontology.json +0 -0
  33. /package/defaults/{package → package-ontology}/src/data/example-ontology.json.d.ts +0 -0
  34. /package/defaults/{package → package-ontology}/src/ontologies/example-ontology.register.ts +0 -0
  35. /package/defaults/{package → package-ontology}/src/ontologies/example-ontology.ts +0 -0
@@ -13,6 +13,7 @@
13
13
  */
14
14
  import fs from 'fs';
15
15
  import path from 'path';
16
+ import { ensureRelinkHooks } from './relinkHooks.js';
16
17
  export const PACKAGE_LOCATIONS = ['packages', 'packages-local'];
17
18
  /** A refusal of create-package's own, as opposed to a crash. */
18
19
  export class CreatePackageError extends Error {
@@ -121,23 +122,17 @@ export function ensureWorkspaceGlob(pkg) {
121
122
  globs.push('packages/*');
122
123
  return { changed: true };
123
124
  }
124
- export const RELINK_COMMAND = 'linked localize --relink';
125
+ export { RELINK_COMMAND, ensureRelinkHooks } from './relinkHooks.js';
125
126
  /**
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`.
127
+ * Make sure the app's `postinstall` AND `dependencies` scripts recreate
128
+ * localize's links -- see `./relinkHooks.js` for why npm needs both. Kept
129
+ * under its old name for existing callers; prefer `ensureRelinkHooks`.
130
+ * Mutates `pkg`.
131
+ *
132
+ * @returns true when it changed `pkg`.
130
133
  */
131
134
  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;
135
+ return ensureRelinkHooks(pkg).length > 0;
141
136
  }
142
137
  /**
143
138
  * A git remote as npm wants it in `repository.url`: `git+https://…`, or
@@ -1 +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
+ {"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';\nimport type {PackageKind} from './packageKind.js';\nimport {ensureRelinkHooks} from './relinkHooks.js';\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 * What to scaffold: an ontology package, an asset package or both. See\n * `./packageKind.js`. Validated at runtime too.\n */\n kind?: PackageKind;\n /** Write the files only: no install, no build, no git. */\n skipInstall?: boolean;\n /**\n * The root the package's IRIs are minted under. Inside an app it defaults to the\n * app's `LINKED_BASE_URI`; see `./packageBaseUri.js`.\n */\n baseUri?: string;\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 {RELINK_COMMAND, ensureRelinkHooks} from './relinkHooks.js';\n\n/**\n * Make sure the app's `postinstall` AND `dependencies` scripts recreate\n * localize's links -- see `./relinkHooks.js` for why npm needs both. Kept\n * under its old name for existing callers; prefer `ensureRelinkHooks`.\n * Mutates `pkg`.\n *\n * @returns true when it changed `pkg`.\n */\nexport function ensureRelinkPostinstall(pkg: Record<string, any>): boolean {\n return ensureRelinkHooks(pkg).length > 0;\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"]}
@@ -0,0 +1,27 @@
1
+ export declare const BASE_URI_ENV = "LINKED_BASE_URI";
2
+ export type BaseUriSource = '--base-uri' | 'environment' | '.env.local' | '.env';
3
+ export interface ResolvedBaseUri {
4
+ baseUri: string;
5
+ source: BaseUriSource;
6
+ }
7
+ /**
8
+ * A root as IRIs are appended to it: absolute http(s), no query or fragment,
9
+ * ending in `/`. Mirrors the app template's `resolveBaseUri`. Empty → undefined.
10
+ */
11
+ export declare function normalizeBaseUri(value: string | undefined, from: string): string | undefined;
12
+ /**
13
+ * The base URI for a package created in `appRoot` (null outside an app, where
14
+ * only the flag counts). Undefined when no source sets one.
15
+ */
16
+ export declare function resolvePackageBaseUri({ flag, appRoot, env, }: {
17
+ flag?: string;
18
+ appRoot: string | null;
19
+ env?: NodeJS.ProcessEnv;
20
+ }): ResolvedBaseUri | undefined;
21
+ /**
22
+ * Give a scaffolded `src/package.ts` its root: `linkedPackage('x')` →
23
+ * `linkedPackage('x', {baseUri: '…'})`. Throws when the call is not found, so a
24
+ * template change cannot silently drop the root.
25
+ */
26
+ export declare function withBaseUri(packageTs: string, baseUri: string): string;
27
+ //# sourceMappingURL=packageBaseUri.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"packageBaseUri.d.ts","sourceRoot":"","sources":["../../../src/utils/packageBaseUri.ts"],"names":[],"mappings":"AAwBA,eAAO,MAAM,YAAY,oBAAoB,CAAC;AAE9C,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,aAAa,GAAG,YAAY,GAAG,MAAM,CAAC;AAEjF,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,aAAa,CAAC;CACvB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAwB5F;AAQD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,IAAI,EACJ,OAAO,EACP,GAAiB,GAClB,EAAE;IACD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB,GAAG,eAAe,GAAG,SAAS,CAc9B;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAMtE"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * The root a new package's IRIs are minted under: `linkedPackage(name, {baseUri})`
3
+ * gives shapes `{baseUri}shape/{slug}/{Name}`, and an ontology package's terms are
4
+ * `{baseUri}ont/{ontologySlug}/{Term}`. Without one, core's default
5
+ * (`https://linked.cm/`) applies.
6
+ *
7
+ * Where it comes from, first match wins:
8
+ *
9
+ * 1. `--base-uri <uri>`: explicit, and what a script or a host passes.
10
+ * 2. `LINKED_BASE_URI` in the shell environment.
11
+ * 3. `LINKED_BASE_URI` in the app's `.env.local`, then its `.env`.
12
+ *
13
+ * 2 and 3 are the variable the app template documents as "the root of this
14
+ * app's package and shape IRIs", read with the same precedence `linked start`
15
+ * gives the app's `.env` (the shell wins over the file). The file is parsed, not
16
+ * loaded, so creating a package never changes this process's environment.
17
+ * `linked.config.js` is not a source: it is a module that would have to be
18
+ * imported (running the app's code) just to scaffold a package, and it has no
19
+ * IRI setting today.
20
+ */
21
+ import fs from 'fs';
22
+ import path from 'path';
23
+ import { parseEnv } from 'util';
24
+ export const BASE_URI_ENV = 'LINKED_BASE_URI';
25
+ /**
26
+ * A root as IRIs are appended to it: absolute http(s), no query or fragment,
27
+ * ending in `/`. Mirrors the app template's `resolveBaseUri`. Empty → undefined.
28
+ */
29
+ export function normalizeBaseUri(value, from) {
30
+ const trimmed = value === null || value === void 0 ? void 0 : value.trim();
31
+ if (!trimmed)
32
+ return undefined;
33
+ let url;
34
+ try {
35
+ url = new URL(trimmed);
36
+ }
37
+ catch (_a) {
38
+ url = undefined;
39
+ }
40
+ if (!url ||
41
+ !/^https?:$/.test(url.protocol) ||
42
+ !url.hostname ||
43
+ url.search ||
44
+ url.hash ||
45
+ /\s/.test(trimmed) ||
46
+ trimmed.includes('?') ||
47
+ trimmed.includes('#')) {
48
+ throw new Error(`${from} must be an absolute http(s) URI without a query or fragment, got ${JSON.stringify(value)}`);
49
+ }
50
+ return trimmed.endsWith('/') ? trimmed : `${trimmed}/`;
51
+ }
52
+ /** `LINKED_BASE_URI` from an env file, or undefined when the file or the key is absent. */
53
+ function fromEnvFile(file) {
54
+ if (!fs.existsSync(file))
55
+ return undefined;
56
+ return parseEnv(fs.readFileSync(file, 'utf8'))[BASE_URI_ENV];
57
+ }
58
+ /**
59
+ * The base URI for a package created in `appRoot` (null outside an app, where
60
+ * only the flag counts). Undefined when no source sets one.
61
+ */
62
+ export function resolvePackageBaseUri({ flag, appRoot, env = process.env, }) {
63
+ const fromFlag = normalizeBaseUri(flag, '--base-uri');
64
+ if (fromFlag)
65
+ return { baseUri: fromFlag, source: '--base-uri' };
66
+ if (!appRoot)
67
+ return undefined;
68
+ const fromShell = normalizeBaseUri(env[BASE_URI_ENV], BASE_URI_ENV);
69
+ if (fromShell)
70
+ return { baseUri: fromShell, source: 'environment' };
71
+ for (const name of ['.env.local', '.env']) {
72
+ const file = path.join(appRoot, name);
73
+ const value = normalizeBaseUri(fromEnvFile(file), `${BASE_URI_ENV} in ${file}`);
74
+ if (value)
75
+ return { baseUri: value, source: name };
76
+ }
77
+ return undefined;
78
+ }
79
+ /**
80
+ * Give a scaffolded `src/package.ts` its root: `linkedPackage('x')` →
81
+ * `linkedPackage('x', {baseUri: '…'})`. Throws when the call is not found, so a
82
+ * template change cannot silently drop the root.
83
+ */
84
+ export function withBaseUri(packageTs, baseUri) {
85
+ const call = /linkedPackage\(\s*(['"][^'"]+['"])\s*\)/;
86
+ if (!call.test(packageTs)) {
87
+ throw new Error('src/package.ts has no linkedPackage(<name>) call to give a baseUri to.');
88
+ }
89
+ return packageTs.replace(call, `linkedPackage($1, {baseUri: ${JSON.stringify(baseUri)}})`);
90
+ }
91
+ //# sourceMappingURL=packageBaseUri.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"packageBaseUri.js","sourceRoot":"","sources":["../../../src/utils/packageBaseUri.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The root a new package's IRIs are minted under: `linkedPackage(name, {baseUri})`\n * gives shapes `{baseUri}shape/{slug}/{Name}`, and an ontology package's terms are\n * `{baseUri}ont/{ontologySlug}/{Term}`. Without one, core's default\n * (`https://linked.cm/`) applies.\n *\n * Where it comes from, first match wins:\n *\n * 1. `--base-uri <uri>`: explicit, and what a script or a host passes.\n * 2. `LINKED_BASE_URI` in the shell environment.\n * 3. `LINKED_BASE_URI` in the app's `.env.local`, then its `.env`.\n *\n * 2 and 3 are the variable the app template documents as \"the root of this\n * app's package and shape IRIs\", read with the same precedence `linked start`\n * gives the app's `.env` (the shell wins over the file). The file is parsed, not\n * loaded, so creating a package never changes this process's environment.\n * `linked.config.js` is not a source: it is a module that would have to be\n * imported (running the app's code) just to scaffold a package, and it has no\n * IRI setting today.\n */\nimport fs from 'fs';\nimport path from 'path';\nimport {parseEnv} from 'util';\n\nexport const BASE_URI_ENV = 'LINKED_BASE_URI';\n\nexport type BaseUriSource = '--base-uri' | 'environment' | '.env.local' | '.env';\n\nexport interface ResolvedBaseUri {\n baseUri: string;\n source: BaseUriSource;\n}\n\n/**\n * A root as IRIs are appended to it: absolute http(s), no query or fragment,\n * ending in `/`. Mirrors the app template's `resolveBaseUri`. Empty → undefined.\n */\nexport function normalizeBaseUri(value: string | undefined, from: string): string | undefined {\n const trimmed = value?.trim();\n if (!trimmed) return undefined;\n let url: URL;\n try {\n url = new URL(trimmed);\n } catch {\n url = undefined;\n }\n if (\n !url ||\n !/^https?:$/.test(url.protocol) ||\n !url.hostname ||\n url.search ||\n url.hash ||\n /\\s/.test(trimmed) ||\n trimmed.includes('?') ||\n trimmed.includes('#')\n ) {\n throw new Error(\n `${from} must be an absolute http(s) URI without a query or fragment, got ${JSON.stringify(value)}`,\n );\n }\n return trimmed.endsWith('/') ? trimmed : `${trimmed}/`;\n}\n\n/** `LINKED_BASE_URI` from an env file, or undefined when the file or the key is absent. */\nfunction fromEnvFile(file: string): string | undefined {\n if (!fs.existsSync(file)) return undefined;\n return parseEnv(fs.readFileSync(file, 'utf8'))[BASE_URI_ENV];\n}\n\n/**\n * The base URI for a package created in `appRoot` (null outside an app, where\n * only the flag counts). Undefined when no source sets one.\n */\nexport function resolvePackageBaseUri({\n flag,\n appRoot,\n env = process.env,\n}: {\n flag?: string;\n appRoot: string | null;\n env?: NodeJS.ProcessEnv;\n}): ResolvedBaseUri | undefined {\n const fromFlag = normalizeBaseUri(flag, '--base-uri');\n if (fromFlag) return {baseUri: fromFlag, source: '--base-uri'};\n if (!appRoot) return undefined;\n\n const fromShell = normalizeBaseUri(env[BASE_URI_ENV], BASE_URI_ENV);\n if (fromShell) return {baseUri: fromShell, source: 'environment'};\n\n for (const name of ['.env.local', '.env'] as const) {\n const file = path.join(appRoot, name);\n const value = normalizeBaseUri(fromEnvFile(file), `${BASE_URI_ENV} in ${file}`);\n if (value) return {baseUri: value, source: name};\n }\n return undefined;\n}\n\n/**\n * Give a scaffolded `src/package.ts` its root: `linkedPackage('x')` →\n * `linkedPackage('x', {baseUri: '…'})`. Throws when the call is not found, so a\n * template change cannot silently drop the root.\n */\nexport function withBaseUri(packageTs: string, baseUri: string): string {\n const call = /linkedPackage\\(\\s*(['\"][^'\"]+['\"])\\s*\\)/;\n if (!call.test(packageTs)) {\n throw new Error('src/package.ts has no linkedPackage(<name>) call to give a baseUri to.');\n }\n return packageTs.replace(call, `linkedPackage($1, {baseUri: ${JSON.stringify(baseUri)}})`);\n}\n"]}
@@ -0,0 +1,49 @@
1
+ export declare const PACKAGE_KINDS: readonly ["ontology", "assets", "both"];
2
+ export type PackageKind = (typeof PACKAGE_KINDS)[number];
3
+ /** The kinds a single package can be: `both` expands to these two. */
4
+ export type ScaffoldKind = Exclude<PackageKind, 'both'>;
5
+ export declare const KIND_SUFFIX: Record<ScaffoldKind, string>;
6
+ export type KindDecision = {
7
+ kind: 'resolved';
8
+ packageKind: PackageKind;
9
+ } | {
10
+ kind: 'ask';
11
+ } | {
12
+ kind: 'refuse';
13
+ message: string;
14
+ };
15
+ export declare function isPackageKind(value: string): value is PackageKind;
16
+ /**
17
+ * `--kind` decides it. Without it a terminal is asked; anything else is
18
+ * refused, so a scripted call never meets a prompt. `--remote` names one
19
+ * repository, which cannot hold both packages.
20
+ */
21
+ export declare function decidePackageKind({ kind, remote, isTTY, }: {
22
+ kind?: string;
23
+ remote?: string;
24
+ isTTY: boolean;
25
+ }): KindDecision;
26
+ /** `both` → `['ontology', 'assets']`, in the order they are created. */
27
+ export declare function scaffoldKinds(kind: PackageKind): ScaffoldKind[];
28
+ /**
29
+ * The package name a kind gives: `foo` → `foo-ont` / `foo-assets`, keeping an
30
+ * npm scope (`@acme/foo` → `@acme/foo-ont`). A name that already ends in the
31
+ * suffix is kept as it is, so `foo-ont` never becomes `foo-ont-ont`.
32
+ */
33
+ export declare function packageNameFor(name: string, kind: ScaffoldKind): string;
34
+ /**
35
+ * The ontology slug of an ontology package: the bare name without `-ont`.
36
+ * `@acme/foo-ont` → `foo`. It is the `{ontologySlug}` in
37
+ * `{root}ont/{ontologySlug}/{Term}` and the ontology's file name and prefix.
38
+ */
39
+ export declare function ontologySlugFor(packageName: string): string;
40
+ /**
41
+ * The template files of a kind, relative to the package root. The files every
42
+ * package has come from `defaults/package`; an asset package is that whole
43
+ * folder, an ontology package adds `defaults/package-ontology` on top of the
44
+ * shared files only.
45
+ */
46
+ export declare const PACKAGE_TEMPLATE_DIRS: Record<ScaffoldKind, string[]>;
47
+ /** The files of `defaults/package` every kind gets. The rest is asset-only. */
48
+ export declare const SHARED_TEMPLATE_FILES: readonly ["gitignore.template", "npmignore.template", "package.json", "tsconfig.json", "tsconfig-esm.json", "src/package.ts"];
49
+ //# sourceMappingURL=packageKind.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"packageKind.d.ts","sourceRoot":"","sources":["../../../src/utils/packageKind.ts"],"names":[],"mappings":"AAgBA,eAAO,MAAM,aAAa,yCAA0C,CAAC;AACrE,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD,sEAAsE;AACtE,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;AAExD,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAGpD,CAAC;AAEF,MAAM,MAAM,YAAY,GACpB;IAAC,IAAI,EAAE,UAAU,CAAC;IAAC,WAAW,EAAE,WAAW,CAAA;CAAC,GAC5C;IAAC,IAAI,EAAE,KAAK,CAAA;CAAC,GACb;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,CAAC;AAEtC,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,WAAW,CAEjE;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,IAAI,EACJ,MAAM,EACN,KAAK,GACN,EAAE;IACD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,YAAY,CAkBf;AAED,wEAAwE;AACxE,wBAAgB,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,YAAY,EAAE,CAE/D;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,MAAM,CAGvE;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAQ3D;AAED;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,EAAE,CAGhE,CAAC;AAEF,+EAA+E;AAC/E,eAAO,MAAM,qBAAqB,+HAOxB,CAAC"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * What `linked create-package` scaffolds, decided without touching anything.
3
+ *
4
+ * A linked package is one of two kinds (create-now arch-03 §Package types):
5
+ *
6
+ * - an **ontology package**, `<name>-ont`: exactly one ontology and nothing
7
+ * else. Its ontology slug is `<name>` (no `-ont`), so its terms mint
8
+ * `{root}ont/<name>/{Term}` while the package itself is `<name>-ont`.
9
+ * - an **asset package**, `<name>-assets`: shapes, components and a backend,
10
+ * and no ontology.
11
+ *
12
+ * `both` scaffolds the pair. The suffix is applied to the name given, unless
13
+ * the name already carries it.
14
+ */
15
+ import { bareName } from './createPackageLocation.js';
16
+ export const PACKAGE_KINDS = ['ontology', 'assets', 'both'];
17
+ export const KIND_SUFFIX = {
18
+ ontology: '-ont',
19
+ assets: '-assets',
20
+ };
21
+ export function isPackageKind(value) {
22
+ return PACKAGE_KINDS.includes(value);
23
+ }
24
+ /**
25
+ * `--kind` decides it. Without it a terminal is asked; anything else is
26
+ * refused, so a scripted call never meets a prompt. `--remote` names one
27
+ * repository, which cannot hold both packages.
28
+ */
29
+ export function decidePackageKind({ kind, remote, isTTY, }) {
30
+ if (kind !== undefined) {
31
+ if (!isPackageKind(kind)) {
32
+ return refuse(`--kind must be one of ${PACKAGE_KINDS.join(', ')}, not "${kind}".`);
33
+ }
34
+ if (kind === 'both' && remote) {
35
+ return refuse(`--kind both creates two packages, and --remote names one repository. Create them ` +
36
+ `separately: linked create-ont-package and linked create-asset-package.`);
37
+ }
38
+ return { kind: 'resolved', packageKind: kind };
39
+ }
40
+ if (isTTY)
41
+ return { kind: 'ask' };
42
+ return refuse(`What kind of package? Pass --kind ontology (one ontology, no shapes: <name>-ont), ` +
43
+ `--kind assets (shapes, components and a backend: <name>-assets) or --kind both.`);
44
+ }
45
+ /** `both` → `['ontology', 'assets']`, in the order they are created. */
46
+ export function scaffoldKinds(kind) {
47
+ return kind === 'both' ? ['ontology', 'assets'] : [kind];
48
+ }
49
+ /**
50
+ * The package name a kind gives: `foo` → `foo-ont` / `foo-assets`, keeping an
51
+ * npm scope (`@acme/foo` → `@acme/foo-ont`). A name that already ends in the
52
+ * suffix is kept as it is, so `foo-ont` never becomes `foo-ont-ont`.
53
+ */
54
+ export function packageNameFor(name, kind) {
55
+ const suffix = KIND_SUFFIX[kind];
56
+ return name.endsWith(suffix) ? name : `${name}${suffix}`;
57
+ }
58
+ /**
59
+ * The ontology slug of an ontology package: the bare name without `-ont`.
60
+ * `@acme/foo-ont` → `foo`. It is the `{ontologySlug}` in
61
+ * `{root}ont/{ontologySlug}/{Term}` and the ontology's file name and prefix.
62
+ */
63
+ export function ontologySlugFor(packageName) {
64
+ const bare = bareName(packageName);
65
+ const suffix = KIND_SUFFIX.ontology;
66
+ const slug = bare.endsWith(suffix) ? bare.slice(0, -suffix.length) : bare;
67
+ if (!slug) {
68
+ throw new Error(`Cannot derive an ontology slug from '${packageName}'`);
69
+ }
70
+ return slug;
71
+ }
72
+ /**
73
+ * The template files of a kind, relative to the package root. The files every
74
+ * package has come from `defaults/package`; an asset package is that whole
75
+ * folder, an ontology package adds `defaults/package-ontology` on top of the
76
+ * shared files only.
77
+ */
78
+ export const PACKAGE_TEMPLATE_DIRS = {
79
+ assets: ['package'],
80
+ ontology: ['package', 'package-ontology'],
81
+ };
82
+ /** The files of `defaults/package` every kind gets. The rest is asset-only. */
83
+ export const SHARED_TEMPLATE_FILES = [
84
+ 'gitignore.template',
85
+ 'npmignore.template',
86
+ 'package.json',
87
+ 'tsconfig.json',
88
+ 'tsconfig-esm.json',
89
+ 'src/package.ts',
90
+ ];
91
+ function refuse(message) {
92
+ return { kind: 'refuse', message };
93
+ }
94
+ //# sourceMappingURL=packageKind.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"packageKind.js","sourceRoot":"","sources":["../../../src/utils/packageKind.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAC,QAAQ,EAAC,MAAM,4BAA4B,CAAC;AAEpD,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM,CAAU,CAAC;AAMrE,MAAM,CAAC,MAAM,WAAW,GAAiC;IACvD,QAAQ,EAAE,MAAM;IAChB,MAAM,EAAE,SAAS;CAClB,CAAC;AAOF,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,OAAQ,aAAmC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC9D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,EAChC,IAAI,EACJ,MAAM,EACN,KAAK,GAKN;IACC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC;YACzB,OAAO,MAAM,CAAC,yBAAyB,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,CAAC;QACrF,CAAC;QACD,IAAI,IAAI,KAAK,MAAM,IAAI,MAAM,EAAE,CAAC;YAC9B,OAAO,MAAM,CACX,mFAAmF;gBACjF,wEAAwE,CAC3E,CAAC;QACJ,CAAC;QACD,OAAO,EAAC,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,IAAI,EAAC,CAAC;IAC/C,CAAC;IACD,IAAI,KAAK;QAAE,OAAO,EAAC,IAAI,EAAE,KAAK,EAAC,CAAC;IAChC,OAAO,MAAM,CACX,oFAAoF;QAClF,iFAAiF,CACpF,CAAC;AACJ,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,aAAa,CAAC,IAAiB;IAC7C,OAAO,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAC3D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,IAAkB;IAC7D,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IACjC,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,MAAM,EAAE,CAAC;AAC3D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,WAAmB;IACjD,MAAM,IAAI,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC;IACnC,MAAM,MAAM,GAAG,WAAW,CAAC,QAAQ,CAAC;IACpC,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC1E,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,wCAAwC,WAAW,GAAG,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAmC;IACnE,MAAM,EAAE,CAAC,SAAS,CAAC;IACnB,QAAQ,EAAE,CAAC,SAAS,EAAE,kBAAkB,CAAC;CAC1C,CAAC;AAEF,+EAA+E;AAC/E,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,oBAAoB;IACpB,oBAAoB;IACpB,cAAc;IACd,eAAe;IACf,mBAAmB;IACnB,gBAAgB;CACR,CAAC;AAEX,SAAS,MAAM,CAAC,OAAe;IAC7B,OAAO,EAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAC,CAAC;AACnC,CAAC","sourcesContent":["/**\n * What `linked create-package` scaffolds, decided without touching anything.\n *\n * A linked package is one of two kinds (create-now arch-03 §Package types):\n *\n * - an **ontology package**, `<name>-ont`: exactly one ontology and nothing\n * else. Its ontology slug is `<name>` (no `-ont`), so its terms mint\n * `{root}ont/<name>/{Term}` while the package itself is `<name>-ont`.\n * - an **asset package**, `<name>-assets`: shapes, components and a backend,\n * and no ontology.\n *\n * `both` scaffolds the pair. The suffix is applied to the name given, unless\n * the name already carries it.\n */\nimport {bareName} from './createPackageLocation.js';\n\nexport const PACKAGE_KINDS = ['ontology', 'assets', 'both'] as const;\nexport type PackageKind = (typeof PACKAGE_KINDS)[number];\n\n/** The kinds a single package can be: `both` expands to these two. */\nexport type ScaffoldKind = Exclude<PackageKind, 'both'>;\n\nexport const KIND_SUFFIX: Record<ScaffoldKind, string> = {\n ontology: '-ont',\n assets: '-assets',\n};\n\nexport type KindDecision =\n | {kind: 'resolved'; packageKind: PackageKind}\n | {kind: 'ask'}\n | {kind: 'refuse'; message: string};\n\nexport function isPackageKind(value: string): value is PackageKind {\n return (PACKAGE_KINDS as readonly string[]).includes(value);\n}\n\n/**\n * `--kind` decides it. Without it a terminal is asked; anything else is\n * refused, so a scripted call never meets a prompt. `--remote` names one\n * repository, which cannot hold both packages.\n */\nexport function decidePackageKind({\n kind,\n remote,\n isTTY,\n}: {\n kind?: string;\n remote?: string;\n isTTY: boolean;\n}): KindDecision {\n if (kind !== undefined) {\n if (!isPackageKind(kind)) {\n return refuse(`--kind must be one of ${PACKAGE_KINDS.join(', ')}, not \"${kind}\".`);\n }\n if (kind === 'both' && remote) {\n return refuse(\n `--kind both creates two packages, and --remote names one repository. Create them ` +\n `separately: linked create-ont-package and linked create-asset-package.`,\n );\n }\n return {kind: 'resolved', packageKind: kind};\n }\n if (isTTY) return {kind: 'ask'};\n return refuse(\n `What kind of package? Pass --kind ontology (one ontology, no shapes: <name>-ont), ` +\n `--kind assets (shapes, components and a backend: <name>-assets) or --kind both.`,\n );\n}\n\n/** `both` → `['ontology', 'assets']`, in the order they are created. */\nexport function scaffoldKinds(kind: PackageKind): ScaffoldKind[] {\n return kind === 'both' ? ['ontology', 'assets'] : [kind];\n}\n\n/**\n * The package name a kind gives: `foo` → `foo-ont` / `foo-assets`, keeping an\n * npm scope (`@acme/foo` → `@acme/foo-ont`). A name that already ends in the\n * suffix is kept as it is, so `foo-ont` never becomes `foo-ont-ont`.\n */\nexport function packageNameFor(name: string, kind: ScaffoldKind): string {\n const suffix = KIND_SUFFIX[kind];\n return name.endsWith(suffix) ? name : `${name}${suffix}`;\n}\n\n/**\n * The ontology slug of an ontology package: the bare name without `-ont`.\n * `@acme/foo-ont` → `foo`. It is the `{ontologySlug}` in\n * `{root}ont/{ontologySlug}/{Term}` and the ontology's file name and prefix.\n */\nexport function ontologySlugFor(packageName: string): string {\n const bare = bareName(packageName);\n const suffix = KIND_SUFFIX.ontology;\n const slug = bare.endsWith(suffix) ? bare.slice(0, -suffix.length) : bare;\n if (!slug) {\n throw new Error(`Cannot derive an ontology slug from '${packageName}'`);\n }\n return slug;\n}\n\n/**\n * The template files of a kind, relative to the package root. The files every\n * package has come from `defaults/package`; an asset package is that whole\n * folder, an ontology package adds `defaults/package-ontology` on top of the\n * shared files only.\n */\nexport const PACKAGE_TEMPLATE_DIRS: Record<ScaffoldKind, string[]> = {\n assets: ['package'],\n ontology: ['package', 'package-ontology'],\n};\n\n/** The files of `defaults/package` every kind gets. The rest is asset-only. */\nexport const SHARED_TEMPLATE_FILES = [\n 'gitignore.template',\n 'npmignore.template',\n 'package.json',\n 'tsconfig.json',\n 'tsconfig-esm.json',\n 'src/package.ts',\n] as const;\n\nfunction refuse(message: string): KindDecision {\n return {kind: 'refuse', message};\n}\n"]}
@@ -0,0 +1,19 @@
1
+ export declare const RELINK_COMMAND = "linked localize --relink";
2
+ /** The npm lifecycle scripts that run {@link RELINK_COMMAND}. See the module header. */
3
+ export declare const RELINK_HOOKS: readonly ["postinstall", "dependencies"];
4
+ /**
5
+ * Make sure each of the app's {@link RELINK_HOOKS} runs {@link RELINK_COMMAND}:
6
+ * set it when absent, append it with `&&` to a script that does something
7
+ * else, and leave a script that already relinks alone. Mutates `pkg`.
8
+ *
9
+ * @returns the hooks it added the command to (empty when nothing changed).
10
+ */
11
+ export declare function ensureRelinkHooks(pkg: Record<string, any>): string[];
12
+ /**
13
+ * {@link ensureRelinkHooks} on a `package.json` file, keeping its indentation
14
+ * and trailing newline. A missing or unparseable file is left alone.
15
+ *
16
+ * @returns the hooks it added (empty when nothing changed or nothing could be).
17
+ */
18
+ export declare function ensureRelinkHooksInFile(pkgPath: string): string[];
19
+ //# sourceMappingURL=relinkHooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relinkHooks.d.ts","sourceRoot":"","sources":["../../../src/utils/relinkHooks.ts"],"names":[],"mappings":"AAoBA,eAAO,MAAM,cAAc,6BAA6B,CAAC;AAEzD,wFAAwF;AACxF,eAAO,MAAM,YAAY,0CAA2C,CAAC;AAKrE;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,EAAE,CAYpE;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAgBjE"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * The app scripts that put `linked localize`'s links back after npm removed
3
+ * them. Shared by `create-package` (which sets them up before it adopts a new
4
+ * package) and by `localize` / `adopt` (which add them when they record a
5
+ * package into an app that does not have them yet).
6
+ *
7
+ * Why TWO hooks, both running the same command: npm runs an app's root
8
+ * `postinstall` only on a bare `npm install` / `npm ci`. It runs a root
9
+ * `dependencies` script after ANY command that changes `node_modules` --
10
+ * `npm install <name>`, `npm update`, `npm uninstall`, `npm dedupe` (npm's
11
+ * arborist, `reify.js`). Each of those replaces a localized link with the
12
+ * registry copy, silently, so `postinstall` alone leaves most of them
13
+ * unguarded. A bare `npm install` runs both scripts; `--relink` is idempotent
14
+ * and silent when there is nothing to do, so running twice costs nothing.
15
+ *
16
+ * With no `local-packages.json` the command prints nothing and exits 0, so the
17
+ * hooks are safe to commit for everyone.
18
+ */
19
+ import fs from 'fs';
20
+ export const RELINK_COMMAND = 'linked localize --relink';
21
+ /** The npm lifecycle scripts that run {@link RELINK_COMMAND}. See the module header. */
22
+ export const RELINK_HOOKS = ['postinstall', 'dependencies'];
23
+ /** Also matches the retired `linked-localize --relink` spelling. */
24
+ const RELINKS = /localize\s+--relink/;
25
+ /**
26
+ * Make sure each of the app's {@link RELINK_HOOKS} runs {@link RELINK_COMMAND}:
27
+ * set it when absent, append it with `&&` to a script that does something
28
+ * else, and leave a script that already relinks alone. Mutates `pkg`.
29
+ *
30
+ * @returns the hooks it added the command to (empty when nothing changed).
31
+ */
32
+ export function ensureRelinkHooks(pkg) {
33
+ var _a;
34
+ pkg.scripts = (_a = pkg.scripts) !== null && _a !== void 0 ? _a : {};
35
+ const added = [];
36
+ for (const hook of RELINK_HOOKS) {
37
+ const current = pkg.scripts[hook];
38
+ if (current && RELINKS.test(current))
39
+ continue;
40
+ pkg.scripts[hook] = current
41
+ ? `${current} && ${RELINK_COMMAND}`
42
+ : RELINK_COMMAND;
43
+ added.push(hook);
44
+ }
45
+ return added;
46
+ }
47
+ /**
48
+ * {@link ensureRelinkHooks} on a `package.json` file, keeping its indentation
49
+ * and trailing newline. A missing or unparseable file is left alone.
50
+ *
51
+ * @returns the hooks it added (empty when nothing changed or nothing could be).
52
+ */
53
+ export function ensureRelinkHooksInFile(pkgPath) {
54
+ var _a, _b;
55
+ let text;
56
+ let pkg;
57
+ try {
58
+ text = fs.readFileSync(pkgPath, 'utf8');
59
+ pkg = JSON.parse(text);
60
+ }
61
+ catch (_c) {
62
+ return [];
63
+ }
64
+ if (!pkg || typeof pkg !== 'object' || Array.isArray(pkg))
65
+ return [];
66
+ const added = ensureRelinkHooks(pkg);
67
+ if (!added.length)
68
+ return added;
69
+ const indent = (_b = (_a = /^[ \t]+(?=")/m.exec(text)) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : 2;
70
+ const eol = text.endsWith('\n') ? '\n' : '';
71
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, indent) + eol);
72
+ return added;
73
+ }
74
+ //# sourceMappingURL=relinkHooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relinkHooks.js","sourceRoot":"","sources":["../../../src/utils/relinkHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,MAAM,IAAI,CAAC;AAEpB,MAAM,CAAC,MAAM,cAAc,GAAG,0BAA0B,CAAC;AAEzD,wFAAwF;AACxF,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,aAAa,EAAE,cAAc,CAAU,CAAC;AAErE,oEAAoE;AACpE,MAAM,OAAO,GAAG,qBAAqB,CAAC;AAEtC;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAwB;;IACxD,GAAG,CAAC,OAAO,GAAG,MAAA,GAAG,CAAC,OAAO,mCAAI,EAAE,CAAC;IAChC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;QAChC,MAAM,OAAO,GAAuB,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACtD,IAAI,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,SAAS;QAC/C,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO;YACzB,CAAC,CAAC,GAAG,OAAO,OAAO,cAAc,EAAE;YACnC,CAAC,CAAC,cAAc,CAAC;QACnB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,OAAe;;IACrD,IAAI,IAAY,CAAC;IACjB,IAAI,GAAwB,CAAC;IAC7B,IAAI,CAAC;QACH,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACxC,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC;IAAC,WAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IACrE,MAAM,KAAK,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC;IACrC,IAAI,CAAC,KAAK,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAChC,MAAM,MAAM,GAAG,MAAA,MAAA,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,0CAAG,CAAC,CAAC,mCAAI,CAAC,CAAC;IACpD,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5C,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC;IACnE,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["/**\n * The app scripts that put `linked localize`'s links back after npm removed\n * them. Shared by `create-package` (which sets them up before it adopts a new\n * package) and by `localize` / `adopt` (which add them when they record a\n * package into an app that does not have them yet).\n *\n * Why TWO hooks, both running the same command: npm runs an app's root\n * `postinstall` only on a bare `npm install` / `npm ci`. It runs a root\n * `dependencies` script after ANY command that changes `node_modules` --\n * `npm install <name>`, `npm update`, `npm uninstall`, `npm dedupe` (npm's\n * arborist, `reify.js`). Each of those replaces a localized link with the\n * registry copy, silently, so `postinstall` alone leaves most of them\n * unguarded. A bare `npm install` runs both scripts; `--relink` is idempotent\n * and silent when there is nothing to do, so running twice costs nothing.\n *\n * With no `local-packages.json` the command prints nothing and exits 0, so the\n * hooks are safe to commit for everyone.\n */\nimport fs from 'fs';\n\nexport const RELINK_COMMAND = 'linked localize --relink';\n\n/** The npm lifecycle scripts that run {@link RELINK_COMMAND}. See the module header. */\nexport const RELINK_HOOKS = ['postinstall', 'dependencies'] as const;\n\n/** Also matches the retired `linked-localize --relink` spelling. */\nconst RELINKS = /localize\\s+--relink/;\n\n/**\n * Make sure each of the app's {@link RELINK_HOOKS} runs {@link RELINK_COMMAND}:\n * set it when absent, append it with `&&` to a script that does something\n * else, and leave a script that already relinks alone. Mutates `pkg`.\n *\n * @returns the hooks it added the command to (empty when nothing changed).\n */\nexport function ensureRelinkHooks(pkg: Record<string, any>): string[] {\n pkg.scripts = pkg.scripts ?? {};\n const added: string[] = [];\n for (const hook of RELINK_HOOKS) {\n const current: string | undefined = pkg.scripts[hook];\n if (current && RELINKS.test(current)) continue;\n pkg.scripts[hook] = current\n ? `${current} && ${RELINK_COMMAND}`\n : RELINK_COMMAND;\n added.push(hook);\n }\n return added;\n}\n\n/**\n * {@link ensureRelinkHooks} on a `package.json` file, keeping its indentation\n * and trailing newline. A missing or unparseable file is left alone.\n *\n * @returns the hooks it added (empty when nothing changed or nothing could be).\n */\nexport function ensureRelinkHooksInFile(pkgPath: string): string[] {\n let text: string;\n let pkg: Record<string, any>;\n try {\n text = fs.readFileSync(pkgPath, 'utf8');\n pkg = JSON.parse(text);\n } catch {\n return [];\n }\n if (!pkg || typeof pkg !== 'object' || Array.isArray(pkg)) return [];\n const added = ensureRelinkHooks(pkg);\n if (!added.length) return added;\n const indent = /^[ \\t]+(?=\")/m.exec(text)?.[0] ?? 2;\n const eol = text.endsWith('\\n') ? '\\n' : '';\n fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, indent) + eol);\n return added;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@_linked/cli",
3
- "version": "1.44.1",
3
+ "version": "1.45.0",
4
4
  "packageManager": "npm@11.21.0",
5
5
  "description": "Command line tools for the @_linked/* packages and apps",
6
6
  "main": "lib/esm/index.js",