@forgecart/cli 2.202610040759.0 → 2.202610041107.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 (31) hide show
  1. package/dist/src/commands/__test__/cli-harness.d.ts +22 -1
  2. package/dist/src/commands/__test__/cli-harness.js +26 -3
  3. package/dist/src/commands/__test__/cli-harness.js.map +1 -1
  4. package/dist/src/commands/design-system-seat.d.ts +8 -5
  5. package/dist/src/commands/design-system-seat.js +6 -8
  6. package/dist/src/commands/design-system-seat.js.map +1 -1
  7. package/dist/src/commands/init-questions.d.ts +3 -2
  8. package/dist/src/commands/init-questions.js +5 -4
  9. package/dist/src/commands/init-questions.js.map +1 -1
  10. package/dist/src/commands/init.d.ts +25 -12
  11. package/dist/src/commands/init.js +186 -64
  12. package/dist/src/commands/init.js.map +1 -1
  13. package/dist/src/commands/refresh.d.ts +5 -3
  14. package/dist/src/commands/refresh.js +40 -16
  15. package/dist/src/commands/refresh.js.map +1 -1
  16. package/dist/src/commands/template-manifest.d.ts +18 -9
  17. package/dist/src/commands/template-manifest.js +22 -17
  18. package/dist/src/commands/template-manifest.js.map +1 -1
  19. package/dist/src/commands/type/init.types.d.ts +6 -3
  20. package/dist/src/template/template-set.d.ts +22 -7
  21. package/dist/src/template/template-set.js +36 -11
  22. package/dist/src/template/template-set.js.map +1 -1
  23. package/dist/src/template/template-unavailable-error.d.ts +5 -6
  24. package/dist/src/template/template-unavailable-error.js +6 -14
  25. package/dist/src/template/template-unavailable-error.js.map +1 -1
  26. package/dist/src/template/type/template-set.types.d.ts +20 -2
  27. package/dist/src/template/type/template-set.types.js.map +1 -1
  28. package/dist/src/template/util/template-identity.util.d.ts +23 -0
  29. package/dist/src/template/util/template-identity.util.js +41 -0
  30. package/dist/src/template/util/template-identity.util.js.map +1 -0
  31. package/package.json +1 -1
@@ -9,7 +9,28 @@
9
9
  * `dist/bin/forgecart.js` exists when these run.
10
10
  */
11
11
  export declare const CLI: string;
12
- /** The bundled storefront template the built CLI scaffolds from. */
12
+ /** The CLI's bundled template set: `template-set.json` and every row's tree, beside `dist/`. */
13
+ export declare const TEMPLATES_DIR: string;
14
+ /** A template identity: the UI framework and the example a template renders. */
15
+ export interface TemplateIdentity {
16
+ uiFramework: string;
17
+ example: string;
18
+ }
19
+ /** A row of the template set (#2775): an identity and the directory its tree lives in. */
20
+ export interface TemplateRow extends TemplateIdentity {
21
+ directory: string;
22
+ }
23
+ /** Every row the template set ships, in its order — the trees a per-row spec iterates. */
24
+ export declare const TEMPLATE_ROWS: readonly TemplateRow[];
25
+ /**
26
+ * The bundled template the built CLI scaffolds `identity` from: its row's
27
+ * `directory`, resolved from `template-set.json` as the CLI resolves it. An
28
+ * identity the set has no row for fails here, named.
29
+ */
30
+ export declare function templateDir(identity: TemplateIdentity): string;
31
+ /** The identity every `init --yes` run answers: the set's first UI framework and first example. */
32
+ export declare const ANSWERED_IDENTITY: TemplateIdentity;
33
+ /** The bundled template every `init --yes` run scaffolds: {@link ANSWERED_IDENTITY}'s row. */
13
34
  export declare const TEMPLATE_SRC: string;
14
35
  export interface CliRun {
15
36
  status: number | null;
@@ -1,5 +1,6 @@
1
1
  import { spawnSync } from 'node:child_process';
2
- import { resolve } from 'node:path';
2
+ import { readFileSync } from 'node:fs';
3
+ import { join, resolve } from 'node:path';
3
4
  /**
4
5
  * Shared black-box harness for the CLI command specs.
5
6
  *
@@ -11,8 +12,30 @@ import { resolve } from 'node:path';
11
12
  * `dist/bin/forgecart.js` exists when these run.
12
13
  */
13
14
  export const CLI = resolve(__dirname, '..', '..', '..', 'dist', 'bin', 'forgecart.js');
14
- /** The bundled storefront template the built CLI scaffolds from. */
15
- export const TEMPLATE_SRC = resolve(__dirname, '..', '..', '..', 'templates', 'storefront');
15
+ /** The CLI's bundled template set: `template-set.json` and every row's tree, beside `dist/`. */
16
+ export const TEMPLATES_DIR = resolve(__dirname, '..', '..', '..', 'templates');
17
+ const TEMPLATE_SET = JSON.parse(readFileSync(join(TEMPLATES_DIR, 'template-set.json'), 'utf8'));
18
+ /** Every row the template set ships, in its order — the trees a per-row spec iterates. */
19
+ export const TEMPLATE_ROWS = TEMPLATE_SET.templates;
20
+ /**
21
+ * The bundled template the built CLI scaffolds `identity` from: its row's
22
+ * `directory`, resolved from `template-set.json` as the CLI resolves it. An
23
+ * identity the set has no row for fails here, named.
24
+ */
25
+ export function templateDir(identity) {
26
+ const row = TEMPLATE_ROWS.find(({ uiFramework, example }) => uiFramework === identity.uiFramework && example === identity.example);
27
+ if (row === undefined) {
28
+ throw new Error(`template-set.json has no row for ${identity.uiFramework}/${identity.example}`);
29
+ }
30
+ return join(TEMPLATES_DIR, row.directory);
31
+ }
32
+ /** The identity every `init --yes` run answers: the set's first UI framework and first example. */
33
+ export const ANSWERED_IDENTITY = {
34
+ uiFramework: TEMPLATE_SET.uiFrameworks.at(0)?.id ?? '',
35
+ example: TEMPLATE_SET.examples.at(0)?.id ?? '',
36
+ };
37
+ /** The bundled template every `init --yes` run scaffolds: {@link ANSWERED_IDENTITY}'s row. */
38
+ export const TEMPLATE_SRC = templateDir(ANSWERED_IDENTITY);
16
39
  /**
17
40
  * Run the built CLI with `args` exactly as typed: the run a spec makes when
18
41
  * the questions a command asks are its subject.
@@ -1 +1 @@
1
- {"version":3,"file":"cli-harness.js","sourceRoot":"","sources":["../../../../src/commands/__test__/cli-harness.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC;AAEvF,oEAAoE;AACpE,MAAM,CAAC,MAAM,YAAY,GAAG,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC;AAU5F;;;GAGG;AACH,MAAM,UAAU,aAAa,CAC3B,IAAuB,EACvB,GAAW,EACX,eAAuC,EAAE;IAEzC,4EAA4E;IAC5E,gEAAgE;IAChE,MAAM,GAAG,GAAuC,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IACnE,OAAO,GAAG,CAAC,sBAAsB,CAAC;IAClC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IAEjC,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IAC3F,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,GAAG,EAAE,GAAG,MAAM,CAAC,MAAM,IAAI,EAAE,GAAG,MAAM,CAAC,MAAM,IAAI,EAAE,EAAE;QACnD,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,EAAE;KAC5B,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,MAAM,CACpB,IAAuB,EACvB,GAAW,EACX,eAAuC,EAAE;IAEzC,MAAM,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAChE,OAAO,aAAa,CAAC,QAAQ,EAAE,GAAG,EAAE,YAAY,CAAC,CAAC;AACpD,CAAC"}
1
+ {"version":3,"file":"cli-harness.js","sourceRoot":"","sources":["../../../../src/commands/__test__/cli-harness.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE1C;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC;AAEvF,gGAAgG;AAChG,MAAM,CAAC,MAAM,aAAa,GAAG,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;AAoB/E,MAAM,YAAY,GAAsB,IAAI,CAAC,KAAK,CAChD,YAAY,CAAC,IAAI,CAAC,aAAa,EAAE,mBAAmB,CAAC,EAAE,MAAM,CAAC,CAC/D,CAAC;AAEF,0FAA0F;AAC1F,MAAM,CAAC,MAAM,aAAa,GAA2B,YAAY,CAAC,SAAS,CAAC;AAE5E;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,QAA0B;IACpD,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,CAC5B,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,EAAE,EAAE,CAC3B,WAAW,KAAK,QAAQ,CAAC,WAAW,IAAI,OAAO,KAAK,QAAQ,CAAC,OAAO,CACvE,CAAC;IACF,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,oCAAoC,QAAQ,CAAC,WAAW,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IAClG,CAAC;IACD,OAAO,IAAI,CAAC,aAAa,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC;AAC5C,CAAC;AAED,mGAAmG;AACnG,MAAM,CAAC,MAAM,iBAAiB,GAAqB;IACjD,WAAW,EAAE,YAAY,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE;IACtD,OAAO,EAAE,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE;CAC/C,CAAC;AAEF,8FAA8F;AAC9F,MAAM,CAAC,MAAM,YAAY,GAAG,WAAW,CAAC,iBAAiB,CAAC,CAAC;AAU3D;;;GAGG;AACH,MAAM,UAAU,aAAa,CAC3B,IAAuB,EACvB,GAAW,EACX,eAAuC,EAAE;IAEzC,4EAA4E;IAC5E,gEAAgE;IAChE,MAAM,GAAG,GAAuC,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IACnE,OAAO,GAAG,CAAC,sBAAsB,CAAC;IAClC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IAEjC,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IAC3F,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,GAAG,EAAE,GAAG,MAAM,CAAC,MAAM,IAAI,EAAE,GAAG,MAAM,CAAC,MAAM,IAAI,EAAE,EAAE;QACnD,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,EAAE;KAC5B,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,MAAM,CACpB,IAAuB,EACvB,GAAW,EACX,eAAuC,EAAE;IAEzC,MAAM,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAChE,OAAO,aAAa,CAAC,QAAQ,EAAE,GAAG,EAAE,YAAY,CAAC,CAAC;AACpD,CAAC"}
@@ -38,9 +38,9 @@ export interface DesignSystemView {
38
38
  }
39
39
  /**
40
40
  * The design-system member a refresh reports beside its per-file verdicts: the
41
- * choice the workspace recorded (null while unset) against the one the bundled
42
- * template renders. The two differ when the merchant chose a design system the
43
- * template does not render.
41
+ * choice the workspace recorded (null while unset) against the one the
42
+ * workspace's template renders. The two differ when the merchant chose a design
43
+ * system the template does not render.
44
44
  */
45
45
  export interface DesignSystemPartition {
46
46
  recorded: DesignSystemChoice | null;
@@ -73,8 +73,11 @@ export declare function templateDesignSystem({ uiFramework, example, }: Template
73
73
  export declare function stampDesignSystem(choice: DesignSystemChoice): DesignSystemSeat;
74
74
  /** The seat as the command reports it: its three fields, each null while unset. */
75
75
  export declare function viewDesignSystem(seat: DesignSystemSeat | undefined): DesignSystemView;
76
- /** The design-system member of a refresh report for a workspace recording `seat`. */
77
- export declare function designSystemPartition(seat: DesignSystemSeat | undefined): DesignSystemPartition;
76
+ /**
77
+ * The design-system member of a refresh report for a workspace recording
78
+ * `seat`, rendered from `template` — the identity its manifest records (#2775).
79
+ */
80
+ export declare function designSystemPartition(seat: DesignSystemSeat | undefined, template: TemplateIdentity): DesignSystemPartition;
78
81
  /** Whether two choices name the same UI framework and content mode. */
79
82
  export declare function sameDesignSystem(a: DesignSystemChoice, b: DesignSystemChoice): boolean;
80
83
  /** A choice in words, as the human output prints it: `shadcn (example content)`. */
@@ -23,11 +23,6 @@ export var ContentMode;
23
23
  ContentMode["EXAMPLE"] = "example";
24
24
  ContentMode["BLANK"] = "blank";
25
25
  })(ContentMode || (ContentMode = {}));
26
- /** The design system the bundled storefront template renders: daisyUI with example content. */
27
- const BUNDLED_TEMPLATE_DESIGN_SYSTEM = {
28
- uiFramework: 'daisyui',
29
- contentMode: ContentMode.EXAMPLE,
30
- };
31
26
  /** One segment of a UI framework id: lowercase letters and digits. */
32
27
  const UI_FRAMEWORK_ID_SEGMENT = /^[a-z0-9]+$/;
33
28
  /** The longest UI framework id the seat records. */
@@ -85,11 +80,14 @@ export function viewDesignSystem(seat) {
85
80
  recordedAt: seat?.recordedAt ?? null,
86
81
  };
87
82
  }
88
- /** The design-system member of a refresh report for a workspace recording `seat`. */
89
- export function designSystemPartition(seat) {
83
+ /**
84
+ * The design-system member of a refresh report for a workspace recording
85
+ * `seat`, rendered from `template` — the identity its manifest records (#2775).
86
+ */
87
+ export function designSystemPartition(seat, template) {
90
88
  return {
91
89
  recorded: recordedChoice(seat),
92
- rendered: { ...BUNDLED_TEMPLATE_DESIGN_SYSTEM },
90
+ rendered: templateDesignSystem(template),
93
91
  };
94
92
  }
95
93
  /** Whether two choices name the same UI framework and content mode. */
@@ -1 +1 @@
1
- {"version":3,"file":"design-system-seat.js","sourceRoot":"","sources":["../../../src/commands/design-system-seat.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"design-system-seat.js","sourceRoot":"","sources":["../../../src/commands/design-system-seat.ts"],"names":[],"mappings":"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"}
@@ -23,9 +23,10 @@ export declare class InitQuestions {
23
23
  */
24
24
  constructor(templateSet: TemplateSet, flags: InitQuestionsInput, prompt: QuestionPrompt);
25
25
  /**
26
- * Ask, in order, and settle the template the answers pick: an identity the
26
+ * Ask, in order, and settle where the answers render from: an identity the
27
27
  * release renders no template for is refused before the catalog question,
28
- * and the catalog question is asked for an example only.
28
+ * and the catalog question is asked for an example only — a blank storefront
29
+ * renders no template and takes no catalog (#2775).
29
30
  */
30
31
  ask(): Promise<SettledScaffold>;
31
32
  /** The UI framework and the example: what the flags leave open is asked in one go. */
@@ -50,15 +50,16 @@ export class InitQuestions {
50
50
  this.prompt = prompt;
51
51
  }
52
52
  /**
53
- * Ask, in order, and settle the template the answers pick: an identity the
53
+ * Ask, in order, and settle where the answers render from: an identity the
54
54
  * release renders no template for is refused before the catalog question,
55
- * and the catalog question is asked for an example only.
55
+ * and the catalog question is asked for an example only — a blank storefront
56
+ * renders no template and takes no catalog (#2775).
56
57
  */
57
58
  async ask() {
58
59
  const identity = await this.askIdentity();
59
- const templateDir = this.templateSet.templateDir(identity);
60
+ const source = this.templateSet.templateSource(identity);
60
61
  const catalog = await this.askCatalog(identity);
61
- return { answers: { ...identity, catalog }, templateDir };
62
+ return { answers: { ...identity, catalog }, source };
62
63
  }
63
64
  /** The UI framework and the example: what the flags leave open is asked in one go. */
64
65
  async askIdentity() {
@@ -1 +1 @@
1
- {"version":3,"file":"init-questions.js","sourceRoot":"","sources":["../../../src/commands/init-questions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"init-questions.js","sourceRoot":"","sources":["../../../src/commands/init-questions.ts"],"names":[],"mappings":"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"}
@@ -14,12 +14,24 @@ import type { InitQuestionsInput } from './type/init.input.js';
14
14
  export declare const DEFAULT_SHOP_API_URL = "https://api.forgecart.com/shop-api";
15
15
  /**
16
16
  * Environment variable naming a dependency-warmed storefront template — a copy
17
- * of the bundled template with `node_modules` (and the generated
18
- * `package-lock.json`) already installed. The workspace-pod image bakes one at
19
- * build time (`docker/workspace-pod/Dockerfile`) and exports this variable so a
20
- * pod scaffold copies warm dependencies instead of paying a cold `npm install`.
17
+ * of one bundled template with `node_modules` (and the generated
18
+ * `package-lock.json`) already installed, and the identity it was warmed from
19
+ * recorded at {@link WARMED_TEMPLATE_IDENTITY_PATH}. The workspace-pod image
20
+ * bakes one at build time (`docker/workspace-pod/Dockerfile`) and exports this
21
+ * variable so a pod scaffold of THAT identity copies warm dependencies instead
22
+ * of paying a cold `npm install`.
21
23
  */
22
24
  export declare const WORKSPACE_TEMPLATE_DIR_ENV = "WORKSPACE_TEMPLATE_DIR";
25
+ /**
26
+ * Where a warmed template records the template identity it was warmed from,
27
+ * relative to its root, as `{ "uiFramework": …, "example": … }` (#2775) — baked
28
+ * beside the image's stamp carrier (`.forgecart/template-stamp`). Init uses a
29
+ * warmed template only for a scaffold of that identity: another identity's
30
+ * dependencies are not this template's, whatever its `package.json` says. A
31
+ * warmed template without the file was baked before the image recorded one,
32
+ * from {@link PRE_IDENTITY_TEMPLATE}.
33
+ */
34
+ export declare const WARMED_TEMPLATE_IDENTITY_PATH = ".forgecart/template-identity.json";
23
35
  /**
24
36
  * Marker file, relative to the scaffolded storefront root, recording that
25
37
  * `node_modules` was copied fully installed from a warmed template whose
@@ -84,14 +96,15 @@ export interface InitOptions extends QuestionPromptInput, InitQuestionsInput {
84
96
  * Resolves the channel token and the target directory, asks init's questions
85
97
  * ({@link InitQuestions}) — refusing, before any file is written, a template
86
98
  * the release does not render — scaffolds the storefront from the warmed
87
- * template when one is available (see {@link resolveScaffoldPlan}) or the
88
- * template the answers picked otherwise, writes the ForgeCart config with the
89
- * answers and `.env`, inventories the render under its template identity with
90
- * the workspace's design-system seat — the one it records, else the one the
91
- * answers determine (#2808) — mirrored into the config, and prints next steps.
92
- * Throws a clear Error on any failure (missing token, unrendered template,
93
- * missing template, copy failure); an unanswered question exits 2 through the
94
- * question adapter's refusal.
99
+ * template of the answers' identity when one is available (see {@link
100
+ * resolveScaffoldPlan}) or the template the answers picked otherwise — or, for
101
+ * a blank storefront, nothing at all (#2775) — writes the ForgeCart config with
102
+ * the answers and `.env`, inventories the render under its template identity
103
+ * with the workspace's design-system seat — the one it records, else the one
104
+ * the answers determine (#2808) — mirrored into the config, and prints next
105
+ * steps. Throws a clear Error on any failure (missing token, unrendered
106
+ * template, missing template, copy failure); an unanswered question exits 2
107
+ * through the question adapter's refusal.
95
108
  */
96
109
  export declare function runInit(name: string | undefined, options: InitOptions, templateSet: TemplateSet): Promise<void>;
97
110
  /**
@@ -4,10 +4,11 @@ import { Command, Option } from 'commander';
4
4
  import { print } from '../output.js';
5
5
  import { QuestionPromptAdapter } from '../question/question-prompt.adapter.js';
6
6
  import { TemplateSet } from '../template/template-set.js';
7
+ import { describeTemplateIdentity, isBlankTemplate, parseTemplateIdentity, PRE_IDENTITY_TEMPLATE, sameTemplateIdentity, } from '../template/util/template-identity.util.js';
7
8
  import { readPackageVersion } from '../version.js';
8
9
  import { stampDesignSystem, templateDesignSystem, } from './design-system-seat.js';
9
10
  import { InitQuestions } from './init-questions.js';
10
- import { readTemplateManifest, serializeTemplateManifest, templateManifestPath, writeTemplateManifest, } from './template-manifest.js';
11
+ import { inventoryTemplate, manifestTemplate, readTemplateManifest, serializeTemplateManifest, templateManifestPath, writeTemplateManifest, } from './template-manifest.js';
11
12
  /**
12
13
  * Default shop API base, INCLUDING the mounted path. The API serves the shop
13
14
  * GraphQL endpoint at `/shop-api` (`app/forgecart/src/main.ts`), and the SDK
@@ -20,12 +21,24 @@ import { readTemplateManifest, serializeTemplateManifest, templateManifestPath,
20
21
  export const DEFAULT_SHOP_API_URL = 'https://api.forgecart.com/shop-api';
21
22
  /**
22
23
  * Environment variable naming a dependency-warmed storefront template — a copy
23
- * of the bundled template with `node_modules` (and the generated
24
- * `package-lock.json`) already installed. The workspace-pod image bakes one at
25
- * build time (`docker/workspace-pod/Dockerfile`) and exports this variable so a
26
- * pod scaffold copies warm dependencies instead of paying a cold `npm install`.
24
+ * of one bundled template with `node_modules` (and the generated
25
+ * `package-lock.json`) already installed, and the identity it was warmed from
26
+ * recorded at {@link WARMED_TEMPLATE_IDENTITY_PATH}. The workspace-pod image
27
+ * bakes one at build time (`docker/workspace-pod/Dockerfile`) and exports this
28
+ * variable so a pod scaffold of THAT identity copies warm dependencies instead
29
+ * of paying a cold `npm install`.
27
30
  */
28
31
  export const WORKSPACE_TEMPLATE_DIR_ENV = 'WORKSPACE_TEMPLATE_DIR';
32
+ /**
33
+ * Where a warmed template records the template identity it was warmed from,
34
+ * relative to its root, as `{ "uiFramework": …, "example": … }` (#2775) — baked
35
+ * beside the image's stamp carrier (`.forgecart/template-stamp`). Init uses a
36
+ * warmed template only for a scaffold of that identity: another identity's
37
+ * dependencies are not this template's, whatever its `package.json` says. A
38
+ * warmed template without the file was baked before the image recorded one,
39
+ * from {@link PRE_IDENTITY_TEMPLATE}.
40
+ */
41
+ export const WARMED_TEMPLATE_IDENTITY_PATH = '.forgecart/template-identity.json';
29
42
  /**
30
43
  * Marker file, relative to the scaffolded storefront root, recording that
31
44
  * `node_modules` was copied fully installed from a warmed template whose
@@ -71,15 +84,60 @@ async function readNormalizedManifest(manifestPath) {
71
84
  }
72
85
  }
73
86
  /**
74
- * Decide how to source the scaffold. The warmed template is used only when
75
- * {@link WORKSPACE_TEMPLATE_DIR_ENV} points at a directory that actually
76
- * contains an installed `node_modules`; full reuse (skipping the install)
77
- * additionally requires its manifest to match the bundled template's, byte for
78
- * semantic byte. Every failure mode degrades to the cold bundled path, so
79
- * images without a warmed template — and machines where the variable is unset —
80
- * behave exactly as before.
87
+ * The template identity a warmed template was warmed from: the one its file
88
+ * records ({@link WARMED_TEMPLATE_IDENTITY_PATH}), else — no file — {@link
89
+ * PRE_IDENTITY_TEMPLATE}, the one tree every image warmed before it recorded an
90
+ * identity. Null when the file is there but names no identity it can read.
91
+ */
92
+ async function readWarmedIdentity(warmedDir) {
93
+ const identityPath = join(warmedDir, WARMED_TEMPLATE_IDENTITY_PATH);
94
+ if (!(await isFile(identityPath)))
95
+ return { ...PRE_IDENTITY_TEMPLATE };
96
+ try {
97
+ return parseTemplateIdentity(JSON.parse(await readFile(identityPath, 'utf8')));
98
+ }
99
+ catch {
100
+ return null;
101
+ }
102
+ }
103
+ /**
104
+ * The one line naming both identities when the warmed template is passed over:
105
+ * the identity it was warmed from (or an unreadable record of one) and the one
106
+ * this scaffold renders.
107
+ */
108
+ function passOverLine(warmedDir, warmed, scaffold) {
109
+ const warmedFrom = warmed === null
110
+ ? 'records no readable template identity'
111
+ : `was warmed from ${describeTemplateIdentity(warmed)}`;
112
+ return (`The warmed template at ${warmedDir} ${warmedFrom}, not ${describeTemplateIdentity(scaffold)}: ` +
113
+ 'scaffolding from the bundled template.');
114
+ }
115
+ /**
116
+ * Decide how to source the scaffold of `identity`. A blank storefront has no
117
+ * template, so nothing to source (#2775); any other identity renders from its
118
+ * bundled template directory, warmed or not ({@link resolveTemplatePlan}).
119
+ */
120
+ async function resolveScaffoldPlan(source, identity) {
121
+ if (source.kind === 'blank')
122
+ return { source: 'blank' };
123
+ if (!(await isDirectory(source.templateDir))) {
124
+ throw new Error(`Storefront template not found at ${source.templateDir}.`);
125
+ }
126
+ return resolveTemplatePlan(source.templateDir, identity);
127
+ }
128
+ /**
129
+ * Decide how to source a template scaffold. The warmed template is used only
130
+ * when {@link WORKSPACE_TEMPLATE_DIR_ENV} points at a directory that actually
131
+ * contains an installed `node_modules` and was warmed from the identity the
132
+ * scaffold renders (#2775, {@link readWarmedIdentity}) — a warmed template of
133
+ * another identity, or with an unreadable record of one, is passed over with
134
+ * one printed line naming both; full reuse (skipping the install) additionally
135
+ * requires its manifest to match the bundled template's, byte for semantic
136
+ * byte. Every failure mode degrades to the cold bundled path, so images without
137
+ * a warmed template — and machines where the variable is unset — behave exactly
138
+ * as before.
81
139
  */
82
- async function resolveScaffoldPlan(bundledTemplateDir) {
140
+ async function resolveTemplatePlan(bundledTemplateDir, identity) {
83
141
  const bundled = { source: 'bundled', templateDir: bundledTemplateDir };
84
142
  const warmedDir = process.env[WORKSPACE_TEMPLATE_DIR_ENV];
85
143
  if (!warmedDir)
@@ -89,6 +147,11 @@ async function resolveScaffoldPlan(bundledTemplateDir) {
89
147
  const warmedNodeModulesDir = join(warmedDir, 'node_modules');
90
148
  if (!(await isDirectory(warmedNodeModulesDir)))
91
149
  return bundled;
150
+ const warmedIdentity = await readWarmedIdentity(warmedDir);
151
+ if (warmedIdentity === null || !sameTemplateIdentity(warmedIdentity, identity)) {
152
+ print(passOverLine(warmedDir, warmedIdentity, identity));
153
+ return bundled;
154
+ }
92
155
  const warmedManifest = await readNormalizedManifest(join(warmedDir, 'package.json'));
93
156
  const bundledManifest = await readNormalizedManifest(join(bundledTemplateDir, 'package.json'));
94
157
  if (!warmedManifest || !bundledManifest || warmedManifest !== bundledManifest) {
@@ -268,19 +331,18 @@ async function writeChannelConfig(targetDir, channelToken, options, scaffold, de
268
331
  await writeFile(envPath, mergeEnv(existingEnv, channelToken, options), 'utf8');
269
332
  }
270
333
  /**
271
- * The design-system seat a full scaffold records (#2773), settled before the
272
- * template copy lands over the workspace at `targetDir`. The seat belongs to
273
- * the workspace, not to an init run: the one it already records stands — a
274
- * re-scaffold never overwrites a recorded choice — else the seat the
334
+ * The design-system seat a full scaffold records (#2773), settled from the
335
+ * workspace's `manifest` before the template copy lands over it. The seat
336
+ * belongs to the workspace, not to an init run: the one it already records
337
+ * stands — a re-scaffold never overwrites a recorded choice — else the seat the
275
338
  * scaffold's answers determine (#2808).
276
339
  */
277
- async function scaffoldDesignSystem(targetDir, answers) {
278
- const manifest = await readTemplateManifest(targetDir);
340
+ function scaffoldDesignSystem(manifest, answers) {
279
341
  return manifest?.designSystem ?? stampDesignSystem(templateDesignSystem(answers));
280
342
  }
281
343
  /**
282
344
  * Settle the design-system seat of a tree configured in place (#2773): the
283
- * seat its template manifest records stands — `--config-only` never
345
+ * seat its template `manifest` records stands — `--config-only` never
284
346
  * overwrites a recorded choice. A manifest that records none is given the
285
347
  * seat determined by the answers the tree was scaffolded with (its scaffold
286
348
  * record, #2808), written before the config mirrors it, so the copy every
@@ -288,8 +350,7 @@ async function scaffoldDesignSystem(targetDir, answers) {
288
350
  * scaffold record — a pre-warmed tree — records none: init never mints the
289
351
  * manifest (refresh does), and derives no seat from answers no scaffold took.
290
352
  */
291
- async function settleInPlaceDesignSystem(targetDir, scaffold) {
292
- const manifest = await readTemplateManifest(targetDir);
353
+ async function settleInPlaceDesignSystem(targetDir, manifest, scaffold) {
293
354
  if (manifest === null)
294
355
  return undefined;
295
356
  if (manifest.designSystem !== undefined)
@@ -300,6 +361,47 @@ async function settleInPlaceDesignSystem(targetDir, scaffold) {
300
361
  await writeFile(templateManifestPath(targetDir), serializeTemplateManifest({ ...manifest, designSystem }), 'utf8');
301
362
  return designSystem;
302
363
  }
364
+ /**
365
+ * Land the plan's files in the target: the template tree — plus, when the
366
+ * manifests diverged, the warmed `node_modules` as a seed — or, for a blank
367
+ * storefront, nothing at all.
368
+ */
369
+ async function landScaffold(targetDir, plan) {
370
+ if (plan.source === 'blank')
371
+ return;
372
+ await cp(plan.templateDir, targetDir, { recursive: true });
373
+ if (plan.source !== 'bundled-with-warmed-modules')
374
+ return;
375
+ await cp(plan.warmedNodeModulesDir, join(targetDir, 'node_modules'), { recursive: true });
376
+ }
377
+ /**
378
+ * The render's inventory for the template manifest: every file the plan's
379
+ * template landed, hashed from the plan's template dir — the exact bytes the
380
+ * copy landed — and none for a blank storefront, which renders no file.
381
+ */
382
+ async function inventoryOf(plan) {
383
+ if (plan.source === 'blank')
384
+ return {};
385
+ return inventoryTemplate(plan.templateDir);
386
+ }
387
+ /**
388
+ * Whether the tree at `targetDir` is a workspace a scaffold left: a storefront,
389
+ * by its `package.json`, or a blank workspace (#2775), by the manifest naming
390
+ * blank — a blank workspace has no `package.json`.
391
+ */
392
+ async function isScaffolded(targetDir, manifest) {
393
+ if (manifest !== null && isBlankTemplate(manifestTemplate(manifest)))
394
+ return true;
395
+ return isFile(join(targetDir, 'package.json'));
396
+ }
397
+ /** The `--application` note: the NestJS application scaffold is not here yet. */
398
+ function printApplicationNote(application) {
399
+ if (!application)
400
+ return;
401
+ print('Note: --application was requested. A NestJS application scaffold arrives ' +
402
+ 'in a later phase (TODO).');
403
+ print('');
404
+ }
303
405
  /** Print the post-scaffold next steps. */
304
406
  function printNextSteps(targetDir, application, depsInstalled) {
305
407
  print('');
@@ -314,15 +416,35 @@ function printNextSteps(targetDir, application, depsInstalled) {
314
416
  }
315
417
  print(' npm run dev');
316
418
  print('');
317
- if (application) {
318
- print('Note: --application was requested. A NestJS application scaffold arrives ' +
319
- 'in a later phase (TODO).');
320
- print('');
419
+ printApplicationNote(application);
420
+ }
421
+ /**
422
+ * Print what a blank scaffold left (#2775): the workspace's `.forgecart/` record
423
+ * and nothing to install or serve.
424
+ */
425
+ function printBlankScaffold(targetDir, application) {
426
+ print('');
427
+ print(`Blank workspace created at ${targetDir}: .forgecart/ records the answers, ` +
428
+ 'and no storefront template was rendered — there is nothing to install or serve.');
429
+ print('');
430
+ printApplicationNote(application);
431
+ }
432
+ /** Print what the scaffold the plan sourced left, and the next steps. */
433
+ function printScaffolded(targetDir, application, plan) {
434
+ if (plan.source === 'blank') {
435
+ printBlankScaffold(targetDir, application);
436
+ return;
321
437
  }
438
+ printNextSteps(targetDir, application, plan.source === 'warmed');
322
439
  }
323
440
  /** Print the post-scaffold summary for the `--config-only` serve-in-place path. */
324
- function printConfigOnly(targetDir) {
441
+ function printConfigOnly(targetDir, blank) {
325
442
  print('');
443
+ if (blank) {
444
+ print('Blank workspace configured: there is no storefront to install or serve.');
445
+ print('');
446
+ return;
447
+ }
326
448
  print('Storefront configured (pre-warmed: dependencies and dev cache already in place).');
327
449
  print('');
328
450
  print('Next steps:');
@@ -331,22 +453,24 @@ function printConfigOnly(targetDir) {
331
453
  print('');
332
454
  }
333
455
  /**
334
- * Serve-in-place: the target is already a warm storefront materialized from the
335
- * image's pre-warmed tree (node_modules + a warm `.next`). Write only the
336
- * per-channel config + env — copying the template would clobber the pre-warmed
337
- * `.next` and bust the Turbopack dev cache the bake exists to provide — and ask
338
- * nothing: the tree already has its template identity, and its config keeps the
339
- * scaffold record it carries and mirrors the design-system seat the workspace
340
- * records (#2773), settled first ({@link settleInPlaceDesignSystem}, #2808).
456
+ * Serve-in-place: the target is already a scaffolded workspace — a warm
457
+ * storefront materialized from the image's pre-warmed tree (node_modules + a
458
+ * warm `.next`), or a blank workspace (#2775). Write only the per-channel
459
+ * config + env — copying the template would clobber the pre-warmed `.next` and
460
+ * bust the Turbopack dev cache the bake exists to provide — and ask nothing: the
461
+ * tree already has its template identity, and its config keeps the scaffold
462
+ * record it carries and mirrors the design-system seat the workspace records
463
+ * (#2773), settled first ({@link settleInPlaceDesignSystem}, #2808).
341
464
  */
342
- async function configureInPlace(targetDir, channelToken, options) {
343
- if (!(await isFile(join(targetDir, 'package.json')))) {
344
- throw new Error(`--config-only requires an existing storefront at ${targetDir} (no package.json found).`);
465
+ async function configureInPlace(targetDir, channelToken, options, manifest) {
466
+ if (!(await isScaffolded(targetDir, manifest))) {
467
+ throw new Error(`--config-only requires an existing workspace at ${targetDir} ` +
468
+ '(no package.json found, and no template manifest naming a blank workspace).');
345
469
  }
346
470
  const scaffold = await readScaffoldRecord(targetDir);
347
- const designSystem = await settleInPlaceDesignSystem(targetDir, scaffold);
471
+ const designSystem = await settleInPlaceDesignSystem(targetDir, manifest, scaffold);
348
472
  await writeChannelConfig(targetDir, channelToken, options, scaffold, designSystem);
349
- printConfigOnly(targetDir);
473
+ printConfigOnly(targetDir, isBlankTemplate(manifestTemplate(manifest)));
350
474
  }
351
475
  /**
352
476
  * Run `forgecart init`: the credentials, then the questions, then the scaffold
@@ -355,14 +479,15 @@ async function configureInPlace(targetDir, channelToken, options) {
355
479
  * Resolves the channel token and the target directory, asks init's questions
356
480
  * ({@link InitQuestions}) — refusing, before any file is written, a template
357
481
  * the release does not render — scaffolds the storefront from the warmed
358
- * template when one is available (see {@link resolveScaffoldPlan}) or the
359
- * template the answers picked otherwise, writes the ForgeCart config with the
360
- * answers and `.env`, inventories the render under its template identity with
361
- * the workspace's design-system seat — the one it records, else the one the
362
- * answers determine (#2808) — mirrored into the config, and prints next steps.
363
- * Throws a clear Error on any failure (missing token, unrendered template,
364
- * missing template, copy failure); an unanswered question exits 2 through the
365
- * question adapter's refusal.
482
+ * template of the answers' identity when one is available (see {@link
483
+ * resolveScaffoldPlan}) or the template the answers picked otherwise — or, for
484
+ * a blank storefront, nothing at all (#2775) — writes the ForgeCart config with
485
+ * the answers and `.env`, inventories the render under its template identity
486
+ * with the workspace's design-system seat — the one it records, else the one
487
+ * the answers determine (#2808) — mirrored into the config, and prints next
488
+ * steps. Throws a clear Error on any failure (missing token, unrendered
489
+ * template, missing template, copy failure); an unanswered question exits 2
490
+ * through the question adapter's refusal.
366
491
  */
367
492
  export async function runInit(name, options, templateSet) {
368
493
  const channelToken = options.token?.trim();
@@ -370,32 +495,29 @@ export async function runInit(name, options, templateSet) {
370
495
  throw new Error('A channel token is required. Pass it with --token <token> (or -t <token>).');
371
496
  }
372
497
  const targetDir = name ? resolve(process.cwd(), name) : process.cwd();
498
+ // The workspace's own record: its identity and its design-system seat, which
499
+ // belongs to the workspace, not to an init run — both init paths carry a seat
500
+ // it records into the files they rewrite (#2773), and settle the one the
501
+ // scaffold's answers determine where it records none (#2808).
502
+ const manifest = await readTemplateManifest(targetDir);
373
503
  if (options.configOnly) {
374
- await configureInPlace(targetDir, channelToken, options);
504
+ await configureInPlace(targetDir, channelToken, options, manifest);
375
505
  return;
376
506
  }
377
507
  const questions = new InitQuestions(templateSet, options, new QuestionPromptAdapter(options));
378
- const { answers, templateDir } = await questions.ask();
379
- if (!(await isDirectory(templateDir))) {
380
- throw new Error(`Storefront template not found at ${templateDir}.`);
381
- }
382
- const designSystem = await scaffoldDesignSystem(targetDir, answers);
383
- const plan = await resolveScaffoldPlan(templateDir);
508
+ const { answers, source } = await questions.ask();
509
+ const identity = { uiFramework: answers.uiFramework, example: answers.example };
510
+ const plan = await resolveScaffoldPlan(source, identity);
511
+ const designSystem = scaffoldDesignSystem(manifest, answers);
384
512
  await mkdir(targetDir, { recursive: true });
385
- await cp(plan.templateDir, targetDir, { recursive: true });
386
- if (plan.source === 'bundled-with-warmed-modules') {
387
- await cp(plan.warmedNodeModulesDir, join(targetDir, 'node_modules'), {
388
- recursive: true,
389
- });
390
- }
513
+ await landScaffold(targetDir, plan);
391
514
  await writeChannelConfig(targetDir, channelToken, options, answers, designSystem);
392
515
  await writeDepsReadyMarker(targetDir, plan);
393
516
  // Inventory the render for `forgecart refresh` (#1116) under the template
394
- // identity it re-renders from (#2774) — hashed from the plan's template dir,
395
- // the exact bytes the copy above landed — recording the workspace's
517
+ // identity it re-renders from (#2774), recording the workspace's
396
518
  // design-system seat (#2773) under the stamp its config mirrors.
397
- await writeTemplateManifest(targetDir, plan.templateDir, readPackageVersion(), { uiFramework: answers.uiFramework, example: answers.example }, designSystem);
398
- printNextSteps(name ?? '.', options.application ?? false, plan.source === 'warmed');
519
+ await writeTemplateManifest(targetDir, await inventoryOf(plan), readPackageVersion(), identity, designSystem);
520
+ printScaffolded(name ?? '.', options.application ?? false, plan);
399
521
  }
400
522
  /**
401
523
  * Build the `init [name]` command: its own flags, its answers to its questions