@forgecart/cli 2.202610040408.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.
- package/dist/src/commands/__test__/cli-harness.d.ts +22 -1
- package/dist/src/commands/__test__/cli-harness.js +26 -3
- package/dist/src/commands/__test__/cli-harness.js.map +1 -1
- package/dist/src/commands/design-system-seat.d.ts +8 -5
- package/dist/src/commands/design-system-seat.js +6 -8
- package/dist/src/commands/design-system-seat.js.map +1 -1
- package/dist/src/commands/init-questions.d.ts +3 -2
- package/dist/src/commands/init-questions.js +5 -4
- package/dist/src/commands/init-questions.js.map +1 -1
- package/dist/src/commands/init.d.ts +25 -12
- package/dist/src/commands/init.js +186 -64
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/refresh.d.ts +5 -3
- package/dist/src/commands/refresh.js +40 -16
- package/dist/src/commands/refresh.js.map +1 -1
- package/dist/src/commands/template-manifest.d.ts +18 -9
- package/dist/src/commands/template-manifest.js +22 -17
- package/dist/src/commands/template-manifest.js.map +1 -1
- package/dist/src/commands/type/init.types.d.ts +6 -3
- package/dist/src/template/template-set.d.ts +22 -7
- package/dist/src/template/template-set.js +36 -11
- package/dist/src/template/template-set.js.map +1 -1
- package/dist/src/template/template-unavailable-error.d.ts +5 -6
- package/dist/src/template/template-unavailable-error.js +6 -14
- package/dist/src/template/template-unavailable-error.js.map +1 -1
- package/dist/src/template/type/template-set.types.d.ts +20 -2
- package/dist/src/template/type/template-set.types.js.map +1 -1
- package/dist/src/template/util/template-identity.util.d.ts +23 -0
- package/dist/src/template/util/template-identity.util.js +41 -0
- package/dist/src/template/util/template-identity.util.js.map +1 -0
- 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
|
|
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 {
|
|
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
|
|
15
|
-
export const
|
|
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;
|
|
1
|
+
{"version":3,"file":"cli-harness.js","sourceRoot":"","sources":["../../../../src/commands/__test__/cli-harness.ts"],"names":[],"mappings":"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"}
|
|
@@ -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
|
|
42
|
-
* template renders. The two differ when the merchant chose a design
|
|
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
|
-
/**
|
|
77
|
-
|
|
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
|
-
/**
|
|
89
|
-
|
|
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:
|
|
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":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAyB,MAAM,wCAAwC,CAAC;AAE9F;;;;;;;;;;;;;;;;GAgBG;AAEH,uFAAuF;AACvF,MAAM,CAAN,IAAY,WAGX;AAHD,WAAY,WAAW;IACrB,kCAAmB,CAAA;IACnB,8BAAe,CAAA;AACjB,CAAC,EAHW,WAAW,KAAX,WAAW,QAGtB;AA+BD
|
|
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
|
|
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
|
|
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
|
|
60
|
+
const source = this.templateSet.templateSource(identity);
|
|
60
61
|
const catalog = await this.askCatalog(identity);
|
|
61
|
-
return { answers: { ...identity, catalog },
|
|
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":"AAMA,OAAO,EAAE,cAAc,EAAE,MAAM,sCAAsC,CAAC;AAEtE,OAAO,EAAE,aAAa,EAAyB,MAAM,wCAAwC,CAAC;AAK9F;;;GAGG;AACH,IAAK,eAIJ;AAJD,WAAK,eAAe;IAClB,+CAA4B,CAAA;IAC5B,sCAAmB,CAAA;IACnB,sCAAmB,CAAA;AACrB,CAAC,EAJI,eAAe,KAAf,eAAe,QAInB;AAED,oEAAoE;AACpE,MAAM,YAAY,GAAG,KAAK,CAAC;AAE3B,oEAAoE;AACpE,MAAM,UAAU,GAAG,IAAI,CAAC;AAExB,sFAAsF;AACtF,MAAM,YAAY,GAAsB;IACtC,EAAE,EAAE,aAAa;IACjB,KAAK,EAAE,OAAO;IACd,IAAI,EAAE,6CAA6C;CACpD,CAAC;AAEF,2DAA2D;AAC3D,MAAM,eAAe,GAAiC;IACpD,EAAE,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,+CAA+C,EAAE;IACtF,EAAE,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,gDAAgD,EAAE;CAC3F,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,OAAO,aAAa;IAOL;IACA;IACA;IARnB;;;;OAIG;IACH,YACmB,WAAwB,EACxB,KAAyB,EACzB,MAAsB;QAFtB,gBAAW,GAAX,WAAW,CAAa;QACxB,UAAK,GAAL,KAAK,CAAoB;QACzB,WAAM,GAAN,MAAM,CAAgB;IACtC,CAAC;IAEJ
|
|
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
|
|
18
|
-
* `package-lock.json`) already installed
|
|
19
|
-
*
|
|
20
|
-
*
|
|
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
|
|
88
|
-
* template the answers picked otherwise
|
|
89
|
-
*
|
|
90
|
-
* the
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
* question
|
|
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
|
|
24
|
-
* `package-lock.json`) already installed
|
|
25
|
-
*
|
|
26
|
-
*
|
|
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
|
-
*
|
|
75
|
-
* {@link
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
|
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
|
|
272
|
-
* template copy lands over
|
|
273
|
-
* the workspace, not to an init run: the one it already records
|
|
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
|
-
|
|
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
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
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
|
|
335
|
-
* image's pre-warmed tree (node_modules + a
|
|
336
|
-
*
|
|
337
|
-
*
|
|
338
|
-
*
|
|
339
|
-
*
|
|
340
|
-
*
|
|
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
|
|
344
|
-
throw new Error(`--config-only requires an existing
|
|
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
|
|
359
|
-
* template the answers picked otherwise
|
|
360
|
-
*
|
|
361
|
-
* the
|
|
362
|
-
*
|
|
363
|
-
*
|
|
364
|
-
*
|
|
365
|
-
* question
|
|
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,
|
|
379
|
-
|
|
380
|
-
|
|
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
|
|
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)
|
|
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
|
|
398
|
-
|
|
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
|