@speedkit/cli 4.21.0 → 4.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +7 -2
- package/dist/commands/generate-customer-config.d.ts +2 -0
- package/dist/commands/generate-customer-config.js +10 -2
- package/dist/services/customer-config/customer-config-service-context.d.ts +9 -1
- package/dist/services/customer-config/customer-config-service-context.js +11 -1
- package/dist/services/customer-config/customer-config-service-model.d.ts +6 -0
- package/dist/services/customer-config/customer-config-service-model.js +36 -0
- package/dist/services/customer-config/customer-config-service.d.ts +8 -3
- package/dist/services/customer-config/customer-config-service.js +71 -25
- package/dist/services/customer-config/template-source.d.ts +33 -0
- package/dist/services/customer-config/template-source.js +49 -0
- package/dist/services/integration-api/integration-api-model.js +3 -1
- package/oclif.manifest.json +20 -1
- package/package.json +8 -1
- package/dist/services/customer-config/templates/config_SpeedKit.js.hbs +0 -548
- package/dist/services/customer-config/templates/config_customer.json.hbs +0 -38
- package/dist/services/customer-config/templates/config_documentHandler.js.hbs +0 -362
- package/dist/services/customer-config/templates/config_dynamicBlocks.es6.hbs +0 -163
- package/dist/services/customer-config/templates/config_dynamicStyles.css.hbs +0 -137
- package/dist/services/customer-config/templates/config_loadHandler.js.hbs +0 -391
- package/dist/services/customer-config/templates/downtimeDetection.js.hbs +0 -209
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
# [4.22.0](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v4.21.0...v4.22.0) (2026-08-20)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **customer-config:** contract-check against the CLI's variable inventory ([7ebd2d2](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/7ebd2d28331a59dc3754fa1f6cd859f978febf6c))
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* **customer-config:** --mode flag picks the template set ([b471e8b](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/b471e8b8c6de32babfd26f69688409cae04288ae))
|
|
12
|
+
* **customer-config:** graduate an existing pre-PoC config ([28af5e2](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/28af5e2d06c8c9571da3d72563ae229b12f27d56))
|
|
13
|
+
* **customer-config:** pre-poc mode asks only what the baseline needs ([35cbcbd](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/35cbcbd698144ac82dd275dcf63a0e62b03dba24))
|
|
14
|
+
* **customer-config:** render wizard templates from sk-onboarding ([266cbe3](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/266cbe3c68f5e4aafa431e2fa4a5f9311c3e28c9))
|
|
15
|
+
|
|
1
16
|
# [4.21.0](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v4.20.5...v4.21.0) (2026-08-14)
|
|
2
17
|
|
|
3
18
|
|
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ $ npm install -g @speedkit/cli
|
|
|
21
21
|
$ sk COMMAND
|
|
22
22
|
running command...
|
|
23
23
|
$ sk (--version)
|
|
24
|
-
@speedkit/cli/4.
|
|
24
|
+
@speedkit/cli/4.22.0 linux-x64 node-v22.23.2
|
|
25
25
|
$ sk --help [COMMAND]
|
|
26
26
|
USAGE
|
|
27
27
|
$ sk COMMAND
|
|
@@ -486,13 +486,18 @@ Generates a customer config guided by a wizard 🧙
|
|
|
486
486
|
|
|
487
487
|
```
|
|
488
488
|
USAGE
|
|
489
|
-
$ sk generate-customer-config [-a <value>] [-d <value>]
|
|
489
|
+
$ sk generate-customer-config [-a <value>] [-d <value>] [--templates <value>] [--mode customer-config|pre-poc]
|
|
490
490
|
|
|
491
491
|
FLAGS
|
|
492
492
|
-a, --app=<value> The SK app name which is also being used the config output directory name
|
|
493
493
|
(kebab-case)
|
|
494
494
|
-d, --customersDirectory=<value> Name of the sk-onboarding directory where all customer configs are being stored
|
|
495
495
|
(default: "customers")
|
|
496
|
+
--mode=<option> [default: customer-config] Which config to scaffold: the full acceleration config,
|
|
497
|
+
or the tracking-only pre-PoC config
|
|
498
|
+
<options: customer-config|pre-poc>
|
|
499
|
+
--templates=<value> Path to the templates/ root (default: the sk-onboarding checkout the CLI runs in, or
|
|
500
|
+
the sk-onboarding repo on GitLab when run standalone)
|
|
496
501
|
|
|
497
502
|
DESCRIPTION
|
|
498
503
|
Generates a customer config guided by a wizard 🧙
|
|
@@ -5,6 +5,8 @@ export default class GenerateCustomerConfig extends Command {
|
|
|
5
5
|
static flags: {
|
|
6
6
|
app: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
7
|
customersDirectory: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
templates: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
mode: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
10
|
};
|
|
9
11
|
run(): Promise<void>;
|
|
10
12
|
}
|
|
@@ -16,10 +16,18 @@ export default class GenerateCustomerConfig extends Command {
|
|
|
16
16
|
char: CLIParametersChar.CustomersDirectory,
|
|
17
17
|
description: `Name of the sk-onboarding directory where all customer configs are being stored (default: "${CLIParametersExample.CustomersDirectory}")`,
|
|
18
18
|
}),
|
|
19
|
+
templates: Flags.string({
|
|
20
|
+
description: "Path to the templates/ root (default: the sk-onboarding checkout the CLI runs in, or the sk-onboarding repo on GitLab when run standalone)",
|
|
21
|
+
}),
|
|
22
|
+
mode: Flags.string({
|
|
23
|
+
options: ["customer-config", "pre-poc"],
|
|
24
|
+
default: "customer-config",
|
|
25
|
+
description: "Which config to scaffold: the full acceleration config, or the tracking-only pre-PoC config",
|
|
26
|
+
}),
|
|
19
27
|
};
|
|
20
28
|
async run() {
|
|
21
|
-
const { flags: { app, customersDirectory }, } = await this.parse(GenerateCustomerConfig);
|
|
22
|
-
const context = new CustomerConfigServiceContext(app, customersDirectory);
|
|
29
|
+
const { flags: { app, customersDirectory, templates, mode }, } = await this.parse(GenerateCustomerConfig);
|
|
30
|
+
const context = new CustomerConfigServiceContext(app, customersDirectory, templates, mode);
|
|
23
31
|
const customerConfigService = await new CustomerConfigServiceFactory(context).buildService();
|
|
24
32
|
await customerConfigService.generateConfig();
|
|
25
33
|
}
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
export declare class CustomerConfigServiceContext {
|
|
2
2
|
appName: string;
|
|
3
3
|
customersDirectory: string;
|
|
4
|
-
|
|
4
|
+
/** Explicit templates/ root; overrides checkout detection and the remote fetch. */
|
|
5
|
+
templatesPath?: string;
|
|
6
|
+
/** Template set = wizard mode: "customer-config" (default) or "pre-poc". */
|
|
7
|
+
mode: string;
|
|
8
|
+
constructor(appName?: string, customersDirectory?: string,
|
|
9
|
+
/** Explicit templates/ root; overrides checkout detection and the remote fetch. */
|
|
10
|
+
templatesPath?: string,
|
|
11
|
+
/** Template set = wizard mode: "customer-config" (default) or "pre-poc". */
|
|
12
|
+
mode?: string);
|
|
5
13
|
}
|
|
@@ -2,10 +2,20 @@ import { CLIParametersExample } from "../../models/cli-parameters.js";
|
|
|
2
2
|
export class CustomerConfigServiceContext {
|
|
3
3
|
appName;
|
|
4
4
|
customersDirectory;
|
|
5
|
-
|
|
5
|
+
templatesPath;
|
|
6
|
+
mode;
|
|
7
|
+
constructor(appName = "", customersDirectory = CLIParametersExample.CustomersDirectory,
|
|
8
|
+
/** Explicit templates/ root; overrides checkout detection and the remote fetch. */
|
|
9
|
+
templatesPath,
|
|
10
|
+
/** Template set = wizard mode: "customer-config" (default) or "pre-poc". */
|
|
11
|
+
mode = "customer-config") {
|
|
6
12
|
this.appName = appName;
|
|
7
13
|
this.customersDirectory = customersDirectory;
|
|
14
|
+
this.templatesPath = templatesPath;
|
|
15
|
+
this.mode = mode;
|
|
8
16
|
this.appName = appName;
|
|
9
17
|
this.customersDirectory = customersDirectory;
|
|
18
|
+
this.templatesPath = templatesPath;
|
|
19
|
+
this.mode = mode;
|
|
10
20
|
}
|
|
11
21
|
}
|
|
@@ -59,3 +59,9 @@ export interface CustomerConfigSettings {
|
|
|
59
59
|
isPlentymarkets?: boolean;
|
|
60
60
|
addDowntimeDetection?: boolean;
|
|
61
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Every variable this CLI can hand to the wizard templates — the template
|
|
64
|
+
* manifest in sk-onboarding must be a subset. Extend this together with
|
|
65
|
+
* `CustomerConfigSettings` when the wizard gains a question.
|
|
66
|
+
*/
|
|
67
|
+
export declare const TEMPLATE_VARIABLES: readonly string[];
|
|
@@ -32,3 +32,39 @@ export var EnvironmentType;
|
|
|
32
32
|
EnvironmentType["STAGING"] = "staging";
|
|
33
33
|
EnvironmentType["TEST"] = "test";
|
|
34
34
|
})(EnvironmentType || (EnvironmentType = {}));
|
|
35
|
+
/**
|
|
36
|
+
* Every variable this CLI can hand to the wizard templates — the template
|
|
37
|
+
* manifest in sk-onboarding must be a subset. Extend this together with
|
|
38
|
+
* `CustomerConfigSettings` when the wizard gains a question.
|
|
39
|
+
*/
|
|
40
|
+
export const TEMPLATE_VARIABLES = [
|
|
41
|
+
"appName",
|
|
42
|
+
"production.host",
|
|
43
|
+
"production.forceInstall",
|
|
44
|
+
"staging.host",
|
|
45
|
+
"staging.forceInstall",
|
|
46
|
+
"activateScopedDeployments",
|
|
47
|
+
"subRouteScope",
|
|
48
|
+
"includeServiceWorker",
|
|
49
|
+
"activateImageOptimisation",
|
|
50
|
+
"removeLazyLoading",
|
|
51
|
+
"addSSR",
|
|
52
|
+
"addSSRInnerShadowDomSupport",
|
|
53
|
+
"addDeviceDetection",
|
|
54
|
+
"useGATracking",
|
|
55
|
+
"useScrapingBee",
|
|
56
|
+
"withGoogleOptimize",
|
|
57
|
+
"activateCfRocketLoaderPlugin",
|
|
58
|
+
"hasSoftNavigations",
|
|
59
|
+
"convertSoftToHardNavigations",
|
|
60
|
+
"handleUsercentrics",
|
|
61
|
+
"isPOV",
|
|
62
|
+
"useDisabledSitesApproach",
|
|
63
|
+
"shopSystemName",
|
|
64
|
+
"isShopify",
|
|
65
|
+
"isShopware",
|
|
66
|
+
"isSalesforce",
|
|
67
|
+
"isOxid",
|
|
68
|
+
"isPlentymarkets",
|
|
69
|
+
"addDowntimeDetection",
|
|
70
|
+
];
|
|
@@ -10,15 +10,20 @@ export declare class CustomerConfigService {
|
|
|
10
10
|
private set appName(value);
|
|
11
11
|
private set outputDirPath(value);
|
|
12
12
|
private get outputDirPath();
|
|
13
|
-
|
|
13
|
+
/** Set from the "Downtime Detection" feature answer in the wizard. */
|
|
14
|
+
private includeDowntimeDetection;
|
|
14
15
|
constructor(context: CustomerConfigServiceContext, cli: CliService);
|
|
15
16
|
private getOutputDirPath;
|
|
16
|
-
private
|
|
17
|
+
private getOutputFileName;
|
|
17
18
|
private getPathWithChangedDir;
|
|
18
19
|
private isDirNotEmpty;
|
|
19
20
|
private chooseNewOutputDirectoryName;
|
|
21
|
+
/** A pre-PoC config marks itself via splitTestId "pre_poc" in config_SpeedKit.js. */
|
|
22
|
+
private isPrePocConfig;
|
|
23
|
+
/** Parks every config_* file as *_pre-poc (suffix before the extension). */
|
|
24
|
+
private renamePrePocFiles;
|
|
20
25
|
private selectOutputDirectory;
|
|
21
|
-
private
|
|
26
|
+
private compileTemplateFiles;
|
|
22
27
|
private getEnvironmentConfig;
|
|
23
28
|
private shouldFileBeOverwritten;
|
|
24
29
|
private getConfigSettings;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
-
import Handlebars from "handlebars";
|
|
4
3
|
import { analyzeSite, } from "../../helpers/site-analyzer.js";
|
|
5
|
-
import { DirectoryOverwriteOption, EnvironmentType, ShopSystem, } from "./customer-config-service-model.js";
|
|
6
|
-
import {
|
|
4
|
+
import { DirectoryOverwriteOption, EnvironmentType, ShopSystem, TEMPLATE_VARIABLES, } from "./customer-config-service-model.js";
|
|
5
|
+
import { findUnknownVariables, resolveTemplateSource, } from "./template-source.js";
|
|
7
6
|
export class CustomerConfigService {
|
|
8
7
|
context;
|
|
9
8
|
cli;
|
|
@@ -22,14 +21,8 @@ export class CustomerConfigService {
|
|
|
22
21
|
get outputDirPath() {
|
|
23
22
|
return this._outputDirPath;
|
|
24
23
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"config_documentHandler.js.hbs",
|
|
28
|
-
"config_dynamicBlocks.es6.hbs",
|
|
29
|
-
"config_dynamicStyles.css.hbs",
|
|
30
|
-
"config_loadHandler.js.hbs",
|
|
31
|
-
"config_SpeedKit.js.hbs",
|
|
32
|
-
];
|
|
24
|
+
/** Set from the "Downtime Detection" feature answer in the wizard. */
|
|
25
|
+
includeDowntimeDetection = false;
|
|
33
26
|
constructor(context, cli) {
|
|
34
27
|
this.context = context;
|
|
35
28
|
const { appName } = this.context;
|
|
@@ -39,8 +32,8 @@ export class CustomerConfigService {
|
|
|
39
32
|
getOutputDirPath(directoryName = "") {
|
|
40
33
|
return path.join(process.cwd(), this.context.customersDirectory, directoryName);
|
|
41
34
|
}
|
|
42
|
-
|
|
43
|
-
return fileName.replace(
|
|
35
|
+
getOutputFileName(fileName) {
|
|
36
|
+
return fileName.replace(/\.njk$/, "");
|
|
44
37
|
}
|
|
45
38
|
getPathWithChangedDir(directoryPath, newDirectory) {
|
|
46
39
|
return path.join(path.dirname(directoryPath), newDirectory);
|
|
@@ -65,7 +58,41 @@ export class CustomerConfigService {
|
|
|
65
58
|
});
|
|
66
59
|
return this.selectOutputDirectory(this.getPathWithChangedDir(directoryPath, newDirectoryName));
|
|
67
60
|
}
|
|
61
|
+
/** A pre-PoC config marks itself via splitTestId "pre_poc" in config_SpeedKit.js. */
|
|
62
|
+
isPrePocConfig(directoryPath) {
|
|
63
|
+
const speedKitConfigPath = path.join(directoryPath, "config_SpeedKit.js");
|
|
64
|
+
if (!fs.existsSync(speedKitConfigPath)) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
return /["']pre_poc["']/.test(fs.readFileSync(speedKitConfigPath, "utf-8"));
|
|
68
|
+
}
|
|
69
|
+
/** Parks every config_* file as *_pre-poc (suffix before the extension). */
|
|
70
|
+
renamePrePocFiles(directoryPath) {
|
|
71
|
+
const configFilePattern = /^(config_[^.]+)\.(.+)$/;
|
|
72
|
+
for (const fileName of fs.readdirSync(directoryPath)) {
|
|
73
|
+
const match = fileName.match(configFilePattern);
|
|
74
|
+
if (!match) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
fs.renameSync(path.join(directoryPath, fileName), path.join(directoryPath, `${match[1]}_pre-poc.${match[2]}`));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
68
80
|
async selectOutputDirectory(directoryPath) {
|
|
81
|
+
// Graduation: a full-config run over an existing pre-PoC config parks the
|
|
82
|
+
// pre-PoC files as *_pre-poc so both versions can be merged afterwards.
|
|
83
|
+
if (this.context.mode !== "pre-poc" && this.isPrePocConfig(directoryPath)) {
|
|
84
|
+
this.cli.spacer();
|
|
85
|
+
this.cli.write(`${directoryPath} holds a pre-PoC config (splitTestId "pre_poc").\n` +
|
|
86
|
+
"On continue, its config_* files are renamed to *_pre-poc and the full " +
|
|
87
|
+
"config is scaffolded next to them. Merge the verified trackers into the " +
|
|
88
|
+
"new config_loadHandler.js afterwards, then delete the *_pre-poc files.\n");
|
|
89
|
+
const graduate = await this.cli.confirm("Rename the pre-PoC files and continue?");
|
|
90
|
+
if (graduate) {
|
|
91
|
+
this.renamePrePocFiles(directoryPath);
|
|
92
|
+
return directoryPath;
|
|
93
|
+
}
|
|
94
|
+
// declined → fall through to the regular not-empty protection
|
|
95
|
+
}
|
|
69
96
|
// protect output directory if already existing and not empty:
|
|
70
97
|
if (this.isDirNotEmpty(directoryPath)) {
|
|
71
98
|
this.cli.spacer();
|
|
@@ -86,13 +113,22 @@ export class CustomerConfigService {
|
|
|
86
113
|
}
|
|
87
114
|
return directoryPath;
|
|
88
115
|
}
|
|
89
|
-
async
|
|
116
|
+
async compileTemplateFiles(configSettings) {
|
|
117
|
+
const source = await resolveTemplateSource(this.context.templatesPath, this.context.mode);
|
|
118
|
+
this.cli.write(`Templates: ${source.origin}\n`);
|
|
119
|
+
// Contract check: the templates may only use variables this CLI knows.
|
|
120
|
+
const unknown = findUnknownVariables(source.manifest, TEMPLATE_VARIABLES);
|
|
121
|
+
if (unknown.length > 0) {
|
|
122
|
+
throw new Error(`The templates use variables this CLI does not know: ${unknown.join(", ")}. ` +
|
|
123
|
+
"Your sk CLI is older than the templates — update it (npm i) and retry.");
|
|
124
|
+
}
|
|
90
125
|
// overwriting protection for output directory:
|
|
91
126
|
this.outputDirPath = await this.selectOutputDirectory(this.outputDirPath);
|
|
92
127
|
this.cli.spacer();
|
|
128
|
+
const templateFiles = source.manifest.files.filter((file) => file !== "downtimeDetection.js.njk" || this.includeDowntimeDetection);
|
|
93
129
|
// write files:
|
|
94
|
-
for (const templateFile of
|
|
95
|
-
const outputFileName = this.
|
|
130
|
+
for (const templateFile of templateFiles) {
|
|
131
|
+
const outputFileName = this.getOutputFileName(templateFile);
|
|
96
132
|
this.cli.startAction(`CREATE:CUSTOMER:CONFIG:${outputFileName}`, `Adding ${outputFileName}`);
|
|
97
133
|
try {
|
|
98
134
|
const outputFilePath = path.join(this.outputDirPath, outputFileName);
|
|
@@ -100,11 +136,7 @@ export class CustomerConfigService {
|
|
|
100
136
|
this.cli.endAction(`CREATE:CUSTOMER:CONFIG:${outputFileName}`, "skipped!");
|
|
101
137
|
continue;
|
|
102
138
|
}
|
|
103
|
-
const
|
|
104
|
-
const filePath = path.resolve(__dirname, "templates", templateFile);
|
|
105
|
-
const content = fs.readFileSync(filePath, "utf8");
|
|
106
|
-
const template = Handlebars.compile(content);
|
|
107
|
-
const result = template(configSettings);
|
|
139
|
+
const result = await source.render(templateFile, configSettings);
|
|
108
140
|
fs.mkdirSync(path.dirname(outputFilePath), { recursive: true });
|
|
109
141
|
fs.writeFileSync(outputFilePath, result);
|
|
110
142
|
this.cli.successAction(`CREATE:CUSTOMER:CONFIG:${outputFileName}`);
|
|
@@ -238,6 +270,22 @@ export class CustomerConfigService {
|
|
|
238
270
|
}
|
|
239
271
|
this.cli.spacer();
|
|
240
272
|
}
|
|
273
|
+
// Pre-PoC: the tracking-only set uses no acceleration answers — stop asking here.
|
|
274
|
+
if (this.context.mode === "pre-poc") {
|
|
275
|
+
return {
|
|
276
|
+
appName: this.appName,
|
|
277
|
+
production,
|
|
278
|
+
staging,
|
|
279
|
+
shopSystemName: selectedShopSystem === ShopSystem.OTHER
|
|
280
|
+
? undefined
|
|
281
|
+
: selectedShopSystem,
|
|
282
|
+
isShopify,
|
|
283
|
+
isShopware,
|
|
284
|
+
isSalesforce,
|
|
285
|
+
isOxid,
|
|
286
|
+
isPlentymarkets,
|
|
287
|
+
};
|
|
288
|
+
}
|
|
241
289
|
const resolve = (feature, fallback) => {
|
|
242
290
|
if (!siteAnalysis)
|
|
243
291
|
return { label: "", checked: fallback };
|
|
@@ -420,10 +468,8 @@ export class CustomerConfigService {
|
|
|
420
468
|
async generateConfig() {
|
|
421
469
|
this.cli.write("\nLet's generate a config...\n");
|
|
422
470
|
const configSettings = await this.getConfigSettings();
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
}
|
|
426
|
-
await this.compileHandlebarsFiles(configSettings);
|
|
471
|
+
this.includeDowntimeDetection = Boolean(configSettings.addDowntimeDetection);
|
|
472
|
+
await this.compileTemplateFiles(configSettings);
|
|
427
473
|
await this.logWarnings(configSettings);
|
|
428
474
|
this.cli.writeSuccess("\nConfig generation completed 👏\n");
|
|
429
475
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** The templates/<set>/manifest.json contract in sk-onboarding. */
|
|
2
|
+
export interface TemplateManifest {
|
|
3
|
+
engine: string;
|
|
4
|
+
engineOptions: Record<string, boolean>;
|
|
5
|
+
files: string[];
|
|
6
|
+
partials?: string[];
|
|
7
|
+
variables: Record<string, {
|
|
8
|
+
type: "boolean" | "string" | "string?";
|
|
9
|
+
sample?: unknown;
|
|
10
|
+
}>;
|
|
11
|
+
}
|
|
12
|
+
export interface TemplateSource {
|
|
13
|
+
/** Where the templates come from — shown to the user. */
|
|
14
|
+
origin: string;
|
|
15
|
+
manifest: TemplateManifest;
|
|
16
|
+
/** Renders one of manifest.files with the wizard settings ({% include %} resolves). */
|
|
17
|
+
render(file: string, settings: Record<string, unknown>): Promise<string>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Resolves where the wizard templates come from, in order:
|
|
21
|
+
* 1. an explicit --templates path (the templates/ root),
|
|
22
|
+
* 2. the sk-onboarding checkout the CLI runs in (walking up from cwd).
|
|
23
|
+
*
|
|
24
|
+
* `set` picks the template set (wizard mode), e.g. "customer-config" or "pre-poc".
|
|
25
|
+
*/
|
|
26
|
+
export declare function resolveTemplateSource(explicitPath?: string, set?: string): Promise<TemplateSource>;
|
|
27
|
+
/**
|
|
28
|
+
* The templates may only use variables this CLI knows — conditional wizard
|
|
29
|
+
* answers are legitimately absent at runtime, so the contract is checked
|
|
30
|
+
* against the CLI's variable inventory, not one run's settings object.
|
|
31
|
+
* A non-empty result means this CLI is older than the templates.
|
|
32
|
+
*/
|
|
33
|
+
export declare function findUnknownVariables(manifest: TemplateManifest, knownVariables: readonly string[]): string[];
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import nunjucks from "nunjucks";
|
|
4
|
+
const TEMPLATES_DIR = "templates";
|
|
5
|
+
/**
|
|
6
|
+
* Resolves where the wizard templates come from, in order:
|
|
7
|
+
* 1. an explicit --templates path (the templates/ root),
|
|
8
|
+
* 2. the sk-onboarding checkout the CLI runs in (walking up from cwd).
|
|
9
|
+
*
|
|
10
|
+
* `set` picks the template set (wizard mode), e.g. "customer-config" or "pre-poc".
|
|
11
|
+
*/
|
|
12
|
+
export async function resolveTemplateSource(explicitPath, set = "customer-config") {
|
|
13
|
+
if (explicitPath)
|
|
14
|
+
return localSource(path.resolve(explicitPath), set);
|
|
15
|
+
for (let dir = process.cwd();; dir = path.dirname(dir)) {
|
|
16
|
+
const candidate = path.join(dir, TEMPLATES_DIR);
|
|
17
|
+
if (fs.existsSync(path.join(candidate, set, "manifest.json"))) {
|
|
18
|
+
return localSource(candidate, set);
|
|
19
|
+
}
|
|
20
|
+
if (dir === path.dirname(dir))
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
throw new Error("No wizard templates found. Run inside an sk-onboarding checkout or " +
|
|
24
|
+
"pass --templates <path>.");
|
|
25
|
+
}
|
|
26
|
+
function buildEnvironment(loader, manifest) {
|
|
27
|
+
return new nunjucks.Environment(loader, manifest.engineOptions);
|
|
28
|
+
}
|
|
29
|
+
function localSource(templatesRoot, set) {
|
|
30
|
+
const setDir = path.join(templatesRoot, set);
|
|
31
|
+
const manifest = JSON.parse(fs.readFileSync(path.join(setDir, "manifest.json"), "utf8"));
|
|
32
|
+
const env = buildEnvironment(new nunjucks.FileSystemLoader(templatesRoot), manifest);
|
|
33
|
+
const origin = setDir;
|
|
34
|
+
return {
|
|
35
|
+
origin,
|
|
36
|
+
manifest,
|
|
37
|
+
render: async (file, settings) => env.renderString(fs.readFileSync(path.join(setDir, file), "utf8"), settings),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The templates may only use variables this CLI knows — conditional wizard
|
|
42
|
+
* answers are legitimately absent at runtime, so the contract is checked
|
|
43
|
+
* against the CLI's variable inventory, not one run's settings object.
|
|
44
|
+
* A non-empty result means this CLI is older than the templates.
|
|
45
|
+
*/
|
|
46
|
+
export function findUnknownVariables(manifest, knownVariables) {
|
|
47
|
+
const known = new Set(knownVariables);
|
|
48
|
+
return Object.keys(manifest.variables).filter((name) => !known.has(name));
|
|
49
|
+
}
|
|
@@ -26,7 +26,9 @@ export const DEFAULT_BLOCKED_FOLDERS = [
|
|
|
26
26
|
".idea",
|
|
27
27
|
"page-examples",
|
|
28
28
|
];
|
|
29
|
-
|
|
29
|
+
// "_pre-poc": parked pre-PoC files created by the graduation flow of
|
|
30
|
+
// generate-customer-config — kept for the manual merge, never deployed.
|
|
31
|
+
export const DEFAULT_BLOCKED_FILES = ["page-examples", "_pre-poc"];
|
|
30
32
|
export class IntegrationApiContext {
|
|
31
33
|
basePath;
|
|
32
34
|
configName;
|
package/oclif.manifest.json
CHANGED
|
@@ -402,6 +402,25 @@
|
|
|
402
402
|
"hasDynamicHelp": false,
|
|
403
403
|
"multiple": false,
|
|
404
404
|
"type": "option"
|
|
405
|
+
},
|
|
406
|
+
"templates": {
|
|
407
|
+
"description": "Path to the templates/ root (default: the sk-onboarding checkout the CLI runs in, or the sk-onboarding repo on GitLab when run standalone)",
|
|
408
|
+
"name": "templates",
|
|
409
|
+
"hasDynamicHelp": false,
|
|
410
|
+
"multiple": false,
|
|
411
|
+
"type": "option"
|
|
412
|
+
},
|
|
413
|
+
"mode": {
|
|
414
|
+
"description": "Which config to scaffold: the full acceleration config, or the tracking-only pre-PoC config",
|
|
415
|
+
"name": "mode",
|
|
416
|
+
"default": "customer-config",
|
|
417
|
+
"hasDynamicHelp": false,
|
|
418
|
+
"multiple": false,
|
|
419
|
+
"options": [
|
|
420
|
+
"customer-config",
|
|
421
|
+
"pre-poc"
|
|
422
|
+
],
|
|
423
|
+
"type": "option"
|
|
405
424
|
}
|
|
406
425
|
},
|
|
407
426
|
"hasDynamicHelp": false,
|
|
@@ -1112,5 +1131,5 @@
|
|
|
1112
1131
|
]
|
|
1113
1132
|
}
|
|
1114
1133
|
},
|
|
1115
|
-
"version": "4.
|
|
1134
|
+
"version": "4.22.0"
|
|
1116
1135
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@speedkit/cli",
|
|
3
3
|
"description": "Speed Kit CLI",
|
|
4
|
-
"version": "4.
|
|
4
|
+
"version": "4.22.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Baqend.com",
|
|
7
7
|
"email": "info@baqend.com"
|
|
@@ -110,6 +110,7 @@
|
|
|
110
110
|
"iconv-lite": "^0.7.2",
|
|
111
111
|
"json2csv": "^6.0.0-alpha.2",
|
|
112
112
|
"node-fetch": "^3.3.2",
|
|
113
|
+
"nunjucks": "^3.2.4",
|
|
113
114
|
"nypm": "^0.6.6",
|
|
114
115
|
"puppeteer": "^24.41.0",
|
|
115
116
|
"puppeteer-extra": "^3.3.6",
|
|
@@ -128,6 +129,7 @@
|
|
|
128
129
|
"@oclif/test": "^4.1.18",
|
|
129
130
|
"@types/chai": "^4.3.10",
|
|
130
131
|
"@types/node": "^25.6.0",
|
|
132
|
+
"@types/nunjucks": "^3.2.6",
|
|
131
133
|
"@types/uuid": "^9.0.8",
|
|
132
134
|
"chai": "^4.4.1",
|
|
133
135
|
"copyfiles": "^2.4.1",
|
|
@@ -154,5 +156,10 @@
|
|
|
154
156
|
"*.ts": [
|
|
155
157
|
"eslint --fix --quiet"
|
|
156
158
|
]
|
|
159
|
+
},
|
|
160
|
+
"overrides": {
|
|
161
|
+
"nunjucks": {
|
|
162
|
+
"chokidar": "$chokidar"
|
|
163
|
+
}
|
|
157
164
|
}
|
|
158
165
|
}
|