create-addfox-app 0.1.1-beta.17 → 0.1.1-beta.19
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/cli.js
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { dirname, join, relative, resolve as external_node_path_resolve } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { createRequire } from "node:module";
|
|
6
|
+
import { execSync } from "node:child_process";
|
|
7
|
+
import prompts from "prompts";
|
|
8
|
+
import { blue, cyan, dim, gray, green, lightBlue, magenta, red, trueColor, yellow } from "kolorist";
|
|
9
|
+
import minimist from "minimist";
|
|
10
|
+
import { cp, mkdir } from "node:fs/promises";
|
|
11
|
+
import { detectPackageManager, getExecCommand, getInstallCommand, getRunCommand } from "@addfox/pkg-manager";
|
|
12
12
|
const TEMPLATE_BASE = "templates";
|
|
13
13
|
function getPackageRoot() {
|
|
14
|
-
return (
|
|
14
|
+
return external_node_path_resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
15
15
|
}
|
|
16
16
|
function getBundledTemplatesDir() {
|
|
17
|
-
return
|
|
17
|
+
return join(getPackageRoot(), TEMPLATE_BASE);
|
|
18
18
|
}
|
|
19
19
|
function hasLocalTemplate(templateName) {
|
|
20
|
-
return
|
|
20
|
+
return existsSync(join(getBundledTemplatesDir(), templateName));
|
|
21
21
|
}
|
|
22
22
|
const SKIP_RELATIVE_SEGMENTS = new Set([
|
|
23
23
|
"node_modules",
|
|
@@ -25,35 +25,35 @@ const SKIP_RELATIVE_SEGMENTS = new Set([
|
|
|
25
25
|
".pnpm"
|
|
26
26
|
]);
|
|
27
27
|
function shouldCopyLocalTemplatePath(src, templateRoot) {
|
|
28
|
-
const root = (
|
|
29
|
-
const rel =
|
|
28
|
+
const root = external_node_path_resolve(templateRoot);
|
|
29
|
+
const rel = relative(root, external_node_path_resolve(src));
|
|
30
30
|
if (rel.startsWith("..")) return true;
|
|
31
31
|
for (const part of rel.split(/[/\\]/))if (SKIP_RELATIVE_SEGMENTS.has(part)) return false;
|
|
32
32
|
return true;
|
|
33
33
|
}
|
|
34
34
|
async function copyBundledTemplate(templateName, destDir) {
|
|
35
|
-
const templatePath =
|
|
36
|
-
if (!
|
|
37
|
-
await
|
|
35
|
+
const templatePath = join(getBundledTemplatesDir(), templateName);
|
|
36
|
+
if (!existsSync(templatePath)) throw new Error(`Bundled template "${templateName}" not found under create-addfox-app. Reinstall the package.`);
|
|
37
|
+
await mkdir(destDir, {
|
|
38
38
|
recursive: true
|
|
39
39
|
});
|
|
40
|
-
const resolvedTemplate = (
|
|
41
|
-
await
|
|
40
|
+
const resolvedTemplate = external_node_path_resolve(templatePath);
|
|
41
|
+
await cp(resolvedTemplate, destDir, {
|
|
42
42
|
recursive: true,
|
|
43
43
|
filter: (src)=>shouldCopyLocalTemplatePath(src, resolvedTemplate)
|
|
44
44
|
});
|
|
45
45
|
}
|
|
46
46
|
const FRAMES = [
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
47
|
+
"⠋",
|
|
48
|
+
"⠙",
|
|
49
|
+
"⠹",
|
|
50
|
+
"⠸",
|
|
51
|
+
"⠼",
|
|
52
|
+
"⠴",
|
|
53
|
+
"⠦",
|
|
54
|
+
"⠧",
|
|
55
|
+
"⠇",
|
|
56
|
+
"⠏"
|
|
57
57
|
];
|
|
58
58
|
const INTERVAL_MS = 80;
|
|
59
59
|
function clearSpinnerLine() {
|
|
@@ -67,7 +67,7 @@ function sleep(ms) {
|
|
|
67
67
|
async function runWithTemplateSpinner(label, fn, options) {
|
|
68
68
|
const minVisibleMs = options?.minVisibleMs ?? 0;
|
|
69
69
|
if (!process.stdout.isTTY) {
|
|
70
|
-
console.log(
|
|
70
|
+
console.log(yellow(`\n ${label}\n`));
|
|
71
71
|
return fn();
|
|
72
72
|
}
|
|
73
73
|
let frameIndex = 0;
|
|
@@ -75,7 +75,7 @@ async function runWithTemplateSpinner(label, fn, options) {
|
|
|
75
75
|
const timer = setInterval(()=>{
|
|
76
76
|
const frame = FRAMES[frameIndex % FRAMES.length];
|
|
77
77
|
frameIndex += 1;
|
|
78
|
-
process.stdout.write(`\r ${
|
|
78
|
+
process.stdout.write(`\r ${yellow(frame)} ${label}`);
|
|
79
79
|
}, INTERVAL_MS);
|
|
80
80
|
const startedAt = Date.now();
|
|
81
81
|
try {
|
|
@@ -181,22 +181,22 @@ function filterAppEntries(destDir, selectedEntries) {
|
|
|
181
181
|
const useAll = selectedEntries.includes("__all__");
|
|
182
182
|
if (useAll) return;
|
|
183
183
|
const keepSet = new Set(selectedEntries);
|
|
184
|
-
const appPath =
|
|
185
|
-
if (!
|
|
186
|
-
const dirs =
|
|
184
|
+
const appPath = join(destDir, APP_DIR);
|
|
185
|
+
if (!existsSync(appPath)) return;
|
|
186
|
+
const dirs = readdirSync(appPath, {
|
|
187
187
|
withFileTypes: true
|
|
188
188
|
}).filter((d)=>d.isDirectory()).map((d)=>d.name);
|
|
189
189
|
for (const dir of dirs){
|
|
190
190
|
const isEntryDir = ENTRY_APP_DIRS.includes(dir);
|
|
191
|
-
if (isEntryDir && !keepSet.has(dir))
|
|
191
|
+
if (isEntryDir && !keepSet.has(dir)) rmSync(join(appPath, dir), {
|
|
192
192
|
recursive: true
|
|
193
193
|
});
|
|
194
194
|
}
|
|
195
195
|
}
|
|
196
196
|
function getExistingAppEntryDirs(destDir) {
|
|
197
|
-
const appPath =
|
|
198
|
-
if (!
|
|
199
|
-
return
|
|
197
|
+
const appPath = join(destDir, APP_DIR);
|
|
198
|
+
if (!existsSync(appPath)) return [];
|
|
199
|
+
return readdirSync(appPath, {
|
|
200
200
|
withFileTypes: true
|
|
201
201
|
}).filter((d)=>d.isDirectory()).map((d)=>d.name).filter((name)=>ENTRY_APP_DIRS.includes(name));
|
|
202
202
|
}
|
|
@@ -338,11 +338,11 @@ function stripUtf8Bom(text) {
|
|
|
338
338
|
return text.startsWith(UTF8_BOM) ? text.slice(UTF8_BOM.length) : text;
|
|
339
339
|
}
|
|
340
340
|
function readJsonFile(path) {
|
|
341
|
-
const raw =
|
|
341
|
+
const raw = readFileSync(path, "utf8");
|
|
342
342
|
return JSON.parse(stripUtf8Bom(raw));
|
|
343
343
|
}
|
|
344
344
|
function writeJsonFile(path, data) {
|
|
345
|
-
|
|
345
|
+
writeFileSync(path, JSON.stringify(data, null, 2), "utf8");
|
|
346
346
|
}
|
|
347
347
|
const RSTEST_CORE = "^0.9.4";
|
|
348
348
|
const RSTEST_COVERAGE = "^0.3.0";
|
|
@@ -418,10 +418,10 @@ function generateRstestConfig(language, testKinds) {
|
|
|
418
418
|
}
|
|
419
419
|
function writeUnitSample(root, language) {
|
|
420
420
|
const ext = getTestExt(language);
|
|
421
|
-
|
|
421
|
+
mkdirSync(external_node_path_resolve(root, "__tests__"), {
|
|
422
422
|
recursive: true
|
|
423
423
|
});
|
|
424
|
-
const path = (
|
|
424
|
+
const path = external_node_path_resolve(root, "__tests__", `example.test.${ext}`);
|
|
425
425
|
const body = "ts" === language ? `import { describe, expect, it } from "@rstest/core";
|
|
426
426
|
|
|
427
427
|
describe("example unit tests", () => {
|
|
@@ -437,15 +437,15 @@ describe("example unit tests", () => {
|
|
|
437
437
|
});
|
|
438
438
|
});
|
|
439
439
|
`;
|
|
440
|
-
|
|
440
|
+
writeFileSync(path, body, "utf-8");
|
|
441
441
|
}
|
|
442
442
|
function writeE2eSample(root, language) {
|
|
443
443
|
const ext = getTestExt(language);
|
|
444
|
-
const dir = (
|
|
445
|
-
|
|
444
|
+
const dir = external_node_path_resolve(root, "__tests__", "e2e");
|
|
445
|
+
mkdirSync(dir, {
|
|
446
446
|
recursive: true
|
|
447
447
|
});
|
|
448
|
-
const path = (
|
|
448
|
+
const path = external_node_path_resolve(dir, `example.browser.test.${ext}`);
|
|
449
449
|
const body = `import { describe, expect, it } from "@rstest/core";
|
|
450
450
|
import { page } from "@rstest/browser";
|
|
451
451
|
|
|
@@ -458,7 +458,7 @@ describe("E2E browser tests", () => {
|
|
|
458
458
|
});
|
|
459
459
|
});
|
|
460
460
|
`;
|
|
461
|
-
|
|
461
|
+
writeFileSync(path, body, "utf-8");
|
|
462
462
|
}
|
|
463
463
|
function mergeDevDeps(pkg, testKinds, installRsdoctor) {
|
|
464
464
|
const dev = pkg.devDependencies ?? {};
|
|
@@ -482,7 +482,7 @@ function mergeScripts(pkg, testKinds) {
|
|
|
482
482
|
function applyTestAndReportSetup(root, language, selection) {
|
|
483
483
|
const { testKinds, installRsdoctor } = selection;
|
|
484
484
|
if (0 === testKinds.length && !installRsdoctor) return;
|
|
485
|
-
const pkgPath = (
|
|
485
|
+
const pkgPath = external_node_path_resolve(root, "package.json");
|
|
486
486
|
const pkg = readJsonFile(pkgPath);
|
|
487
487
|
mergeDevDeps(pkg, testKinds, installRsdoctor);
|
|
488
488
|
mergeScripts(pkg, testKinds);
|
|
@@ -490,19 +490,19 @@ function applyTestAndReportSetup(root, language, selection) {
|
|
|
490
490
|
if (testKinds.length > 0) {
|
|
491
491
|
const configName = "ts" === language ? "rstest.config.ts" : "rstest.config.js";
|
|
492
492
|
const content = generateRstestConfig(language, testKinds);
|
|
493
|
-
|
|
493
|
+
writeFileSync(external_node_path_resolve(root, configName), content, "utf-8");
|
|
494
494
|
if (testKinds.includes("unit")) writeUnitSample(root, language);
|
|
495
495
|
if (testKinds.includes("e2e")) writeE2eSample(root, language);
|
|
496
496
|
}
|
|
497
497
|
}
|
|
498
498
|
const RESET = "\x1b[0m";
|
|
499
499
|
const LOGO_LINES = [
|
|
500
|
-
"
|
|
501
|
-
"
|
|
502
|
-
"
|
|
503
|
-
"
|
|
504
|
-
"
|
|
505
|
-
"
|
|
500
|
+
" █████╗ ██████╗ ██████╗ ███████╗ ██████╗ ██╗ ██╗",
|
|
501
|
+
"██╔══██╗██╔══██╗██╔══██╗██╔════╝██╔═══██╗╚██╗██╔╝",
|
|
502
|
+
"███████║██║ ██║██║ ██║█████╗ ██║ ██║ ╚███╔╝ ",
|
|
503
|
+
"██╔══██║██║ ██║██║ ██║██╔══╝ ██║ ██║ ██╔██╗ ",
|
|
504
|
+
"██║ ██║██████╔╝██████╔╝██║ ╚██████╔╝██╔╝ ██╗",
|
|
505
|
+
"╚═╝ ╚═╝╚═════╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝"
|
|
506
506
|
];
|
|
507
507
|
const GRADIENT_START_256 = 208;
|
|
508
508
|
const GRADIENT_END_256 = 213;
|
|
@@ -571,8 +571,8 @@ function styleFileName(engine) {
|
|
|
571
571
|
return "global.scss";
|
|
572
572
|
}
|
|
573
573
|
function listUiEntryScripts(root, language) {
|
|
574
|
-
const appDir =
|
|
575
|
-
if (!
|
|
574
|
+
const appDir = join(root, "app");
|
|
575
|
+
if (!existsSync(appDir)) return [];
|
|
576
576
|
const extOrder = "ts" === language ? [
|
|
577
577
|
".tsx",
|
|
578
578
|
".ts",
|
|
@@ -585,12 +585,12 @@ function listUiEntryScripts(root, language) {
|
|
|
585
585
|
".svelte"
|
|
586
586
|
];
|
|
587
587
|
const out = [];
|
|
588
|
-
for (const name of
|
|
588
|
+
for (const name of readdirSync(appDir)){
|
|
589
589
|
if (SKIP_STYLE_ENTRIES.has(name)) continue;
|
|
590
|
-
const sub =
|
|
591
|
-
if (
|
|
592
|
-
const p =
|
|
593
|
-
if (
|
|
590
|
+
const sub = join(appDir, name);
|
|
591
|
+
if (statSync(sub).isDirectory()) for (const ext of extOrder){
|
|
592
|
+
const p = join(sub, `index${ext}`);
|
|
593
|
+
if (existsSync(p)) {
|
|
594
594
|
out.push(p);
|
|
595
595
|
break;
|
|
596
596
|
}
|
|
@@ -599,23 +599,23 @@ function listUiEntryScripts(root, language) {
|
|
|
599
599
|
return out;
|
|
600
600
|
}
|
|
601
601
|
function buildStyleImportLine(entryFile, engine) {
|
|
602
|
-
const entryDir =
|
|
603
|
-
const stylePath =
|
|
604
|
-
const rel =
|
|
602
|
+
const entryDir = join(entryFile, "..");
|
|
603
|
+
const stylePath = join(entryDir, "..", "styles", styleFileName(engine));
|
|
604
|
+
const rel = relative(entryDir, stylePath).replace(/\\/g, "/");
|
|
605
605
|
return `import "${rel}";`;
|
|
606
606
|
}
|
|
607
607
|
function prependImportIfMissing(filePath, line) {
|
|
608
|
-
const raw =
|
|
608
|
+
const raw = readFileSync(filePath, "utf8");
|
|
609
609
|
if (raw.includes("/styles/global.") || raw.includes("/styles/uno.css")) return;
|
|
610
|
-
|
|
610
|
+
writeFileSync(filePath, `${line}\n${raw}`, "utf8");
|
|
611
611
|
}
|
|
612
612
|
function writeTailwindFiles(root) {
|
|
613
|
-
const dir =
|
|
614
|
-
|
|
613
|
+
const dir = join(root, "app", "styles");
|
|
614
|
+
mkdirSync(dir, {
|
|
615
615
|
recursive: true
|
|
616
616
|
});
|
|
617
|
-
|
|
618
|
-
|
|
617
|
+
writeFileSync(join(dir, "global.css"), '@import "tailwindcss";\n', "utf8");
|
|
618
|
+
writeFileSync(join(root, "postcss.config.mjs"), `export default {
|
|
619
619
|
plugins: {
|
|
620
620
|
"@tailwindcss/postcss": {},
|
|
621
621
|
autoprefixer: {},
|
|
@@ -624,20 +624,20 @@ function writeTailwindFiles(root) {
|
|
|
624
624
|
`, "utf8");
|
|
625
625
|
}
|
|
626
626
|
function writeUnoFiles(root) {
|
|
627
|
-
const dir =
|
|
628
|
-
|
|
627
|
+
const dir = join(root, "app", "styles");
|
|
628
|
+
mkdirSync(dir, {
|
|
629
629
|
recursive: true
|
|
630
630
|
});
|
|
631
|
-
|
|
631
|
+
writeFileSync(join(dir, "uno.css"), `@unocss preflights;
|
|
632
632
|
@unocss default;
|
|
633
633
|
`, "utf8");
|
|
634
|
-
|
|
634
|
+
writeFileSync(join(root, "postcss.config.mjs"), `import UnoCSS from "@unocss/postcss";
|
|
635
635
|
|
|
636
636
|
export default {
|
|
637
637
|
plugins: [UnoCSS()],
|
|
638
638
|
};
|
|
639
639
|
`, "utf8");
|
|
640
|
-
|
|
640
|
+
writeFileSync(join(root, "uno.config.ts"), `import { defineConfig, presetUno } from "unocss";
|
|
641
641
|
|
|
642
642
|
export default defineConfig({
|
|
643
643
|
content: {
|
|
@@ -648,12 +648,11 @@ export default defineConfig({
|
|
|
648
648
|
`, "utf8");
|
|
649
649
|
}
|
|
650
650
|
function writeLessSassFiles(root, engine) {
|
|
651
|
-
const dir =
|
|
652
|
-
|
|
651
|
+
const dir = join(root, "app", "styles");
|
|
652
|
+
mkdirSync(dir, {
|
|
653
653
|
recursive: true
|
|
654
654
|
});
|
|
655
|
-
|
|
656
|
-
else (0, __WEBPACK_EXTERNAL_MODULE_node_fs_5ea92f0c__.writeFileSync)((0, __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__.join)(dir, "global.scss"), "body {\n margin: 0;\n}\n", "utf8");
|
|
655
|
+
"less" === engine ? writeFileSync(join(dir, "global.less"), "body {\n margin: 0;\n}\n", "utf8") : writeFileSync(join(dir, "global.scss"), "body {\n margin: 0;\n}\n", "utf8");
|
|
657
656
|
}
|
|
658
657
|
function mergeDevDependencies(pkg, engine) {
|
|
659
658
|
const dev = pkg.devDependencies ?? {};
|
|
@@ -667,7 +666,7 @@ function writeEngineFiles(root, engine) {
|
|
|
667
666
|
}
|
|
668
667
|
function applyStyleEngine(root, _framework, language, engine) {
|
|
669
668
|
if ("none" === engine) return;
|
|
670
|
-
const pkgPath =
|
|
669
|
+
const pkgPath = join(root, "package.json");
|
|
671
670
|
const pkg = readJsonFile(pkgPath);
|
|
672
671
|
mergeDevDependencies(pkg, engine);
|
|
673
672
|
writeJsonFile(pkgPath, pkg);
|
|
@@ -675,7 +674,7 @@ function applyStyleEngine(root, _framework, language, engine) {
|
|
|
675
674
|
const entries = listUiEntryScripts(root, language);
|
|
676
675
|
for (const file of entries)prependImportIfMissing(file, buildStyleImportLine(file, engine));
|
|
677
676
|
}
|
|
678
|
-
const cli_require =
|
|
677
|
+
const cli_require = createRequire(import.meta.url);
|
|
679
678
|
try {
|
|
680
679
|
const figures = cli_require("prompts/lib/util/figures");
|
|
681
680
|
figures.radioOn = "\u25CF";
|
|
@@ -686,15 +685,15 @@ const PROMPT_SELECT_HINT = "- Use arrow-keys. Enter to confirm";
|
|
|
686
685
|
function multiselectInstructions(showToggleAll) {
|
|
687
686
|
return "\nInstructions:\n ↑/↓: Highlight option\n ←/→/[space]: Toggle selection\n" + (showToggleAll ? " a: Toggle all\n" : "") + " Enter to confirm";
|
|
688
687
|
}
|
|
689
|
-
const orange =
|
|
688
|
+
const orange = trueColor(230, 138, 46);
|
|
690
689
|
function getFrameworkChoicesColored() {
|
|
691
690
|
const colors = {
|
|
692
|
-
vanilla:
|
|
693
|
-
vue:
|
|
694
|
-
react:
|
|
695
|
-
preact:
|
|
696
|
-
svelte:
|
|
697
|
-
solid:
|
|
691
|
+
vanilla: gray,
|
|
692
|
+
vue: green,
|
|
693
|
+
react: cyan,
|
|
694
|
+
preact: yellow,
|
|
695
|
+
svelte: red,
|
|
696
|
+
solid: lightBlue
|
|
698
697
|
};
|
|
699
698
|
return FRAMEWORKS.map((f)=>({
|
|
700
699
|
...f,
|
|
@@ -704,11 +703,11 @@ function getFrameworkChoicesColored() {
|
|
|
704
703
|
function getLanguageChoicesColored() {
|
|
705
704
|
return [
|
|
706
705
|
{
|
|
707
|
-
title:
|
|
706
|
+
title: cyan("TypeScript"),
|
|
708
707
|
value: "ts"
|
|
709
708
|
},
|
|
710
709
|
{
|
|
711
|
-
title:
|
|
710
|
+
title: yellow("JavaScript"),
|
|
712
711
|
value: "js"
|
|
713
712
|
}
|
|
714
713
|
];
|
|
@@ -716,9 +715,9 @@ function getLanguageChoicesColored() {
|
|
|
716
715
|
function getPackageManagerChoicesColored() {
|
|
717
716
|
const colors = {
|
|
718
717
|
pnpm: orange,
|
|
719
|
-
npm:
|
|
720
|
-
yarn:
|
|
721
|
-
bun:
|
|
718
|
+
npm: red,
|
|
719
|
+
yarn: blue,
|
|
720
|
+
bun: green
|
|
722
721
|
};
|
|
723
722
|
return PACKAGE_MANAGER_CHOICES.map((c)=>({
|
|
724
723
|
...c,
|
|
@@ -727,11 +726,11 @@ function getPackageManagerChoicesColored() {
|
|
|
727
726
|
}
|
|
728
727
|
function getStyleEngineChoicesColored() {
|
|
729
728
|
const colors = {
|
|
730
|
-
none:
|
|
731
|
-
tailwindcss:
|
|
732
|
-
unocss:
|
|
733
|
-
less:
|
|
734
|
-
sass:
|
|
729
|
+
none: dim,
|
|
730
|
+
tailwindcss: cyan,
|
|
731
|
+
unocss: green,
|
|
732
|
+
less: yellow,
|
|
733
|
+
sass: magenta
|
|
735
734
|
};
|
|
736
735
|
return STYLE_ENGINES.map((c)=>({
|
|
737
736
|
...c,
|
|
@@ -740,8 +739,8 @@ function getStyleEngineChoicesColored() {
|
|
|
740
739
|
}
|
|
741
740
|
function getVersion() {
|
|
742
741
|
try {
|
|
743
|
-
const pkgPath = (
|
|
744
|
-
const pkg = JSON.parse(
|
|
742
|
+
const pkgPath = external_node_path_resolve(dirname(fileURLToPath(import.meta.url)), "..", "package.json");
|
|
743
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
745
744
|
return pkg.version ?? "0.0.0";
|
|
746
745
|
} catch {
|
|
747
746
|
return "0.0.0";
|
|
@@ -787,7 +786,7 @@ function parseStyleEngineArg(raw) {
|
|
|
787
786
|
return "tailwindcss";
|
|
788
787
|
}
|
|
789
788
|
async function confirmOverwrite(dir) {
|
|
790
|
-
const { action } = await (
|
|
789
|
+
const { action } = await prompts({
|
|
791
790
|
type: "select",
|
|
792
791
|
name: "action",
|
|
793
792
|
message: `Directory "${dir}" already exists. What would you like to do?`,
|
|
@@ -808,7 +807,7 @@ async function confirmOverwrite(dir) {
|
|
|
808
807
|
return "overwrite" === action;
|
|
809
808
|
}
|
|
810
809
|
async function promptOptions() {
|
|
811
|
-
const res = await (
|
|
810
|
+
const res = await prompts([
|
|
812
811
|
{
|
|
813
812
|
type: "select",
|
|
814
813
|
name: "framework",
|
|
@@ -843,7 +842,7 @@ async function promptOptions() {
|
|
|
843
842
|
message: "Select extension entries",
|
|
844
843
|
choices: ENTRY_CHOICES,
|
|
845
844
|
min: 1,
|
|
846
|
-
hint: "Space to toggle (
|
|
845
|
+
hint: "Space to toggle (○ unselected, ● selected)",
|
|
847
846
|
instructions: multiselectInstructions(true)
|
|
848
847
|
}
|
|
849
848
|
]);
|
|
@@ -858,26 +857,26 @@ async function promptOptions() {
|
|
|
858
857
|
}
|
|
859
858
|
const TEST_KIND_CHOICES = [
|
|
860
859
|
{
|
|
861
|
-
title: "Unit
|
|
860
|
+
title: "Unit — rstest (Node)",
|
|
862
861
|
value: "unit"
|
|
863
862
|
},
|
|
864
863
|
{
|
|
865
|
-
title: "E2E
|
|
864
|
+
title: "E2E — rstest + Playwright (browser)",
|
|
866
865
|
value: "e2e"
|
|
867
866
|
}
|
|
868
867
|
];
|
|
869
868
|
async function promptTestAndReport() {
|
|
870
|
-
const testRes = await (
|
|
869
|
+
const testRes = await prompts({
|
|
871
870
|
type: "multiselect",
|
|
872
871
|
name: "testKinds",
|
|
873
872
|
message: "Initialize test config? (optional)",
|
|
874
873
|
choices: TEST_KIND_CHOICES,
|
|
875
874
|
min: 0,
|
|
876
|
-
hint: "Space toggles
|
|
875
|
+
hint: "Space toggles — leave empty to skip",
|
|
877
876
|
instructions: multiselectInstructions(true)
|
|
878
877
|
});
|
|
879
878
|
if (void 0 === testRes.testKinds) return null;
|
|
880
|
-
const docRes = await (
|
|
879
|
+
const docRes = await prompts({
|
|
881
880
|
type: "select",
|
|
882
881
|
name: "installRsdoctor",
|
|
883
882
|
message: "Install Rsdoctor (@rsdoctor/rspack-plugin) for bundle analysis? After install, use addfox dev or addfox build with --report.",
|
|
@@ -910,26 +909,26 @@ function resolveTestSelectionFromArgv(argv) {
|
|
|
910
909
|
};
|
|
911
910
|
}
|
|
912
911
|
function updatePackageName(destDir, projectName) {
|
|
913
|
-
const pkgPath = (
|
|
914
|
-
if (!
|
|
912
|
+
const pkgPath = external_node_path_resolve(destDir, "package.json");
|
|
913
|
+
if (!existsSync(pkgPath)) return;
|
|
915
914
|
const pkg = readJsonFile(pkgPath);
|
|
916
915
|
pkg.name = projectName.replace(/\s+/g, "-").toLowerCase();
|
|
917
916
|
writeJsonFile(pkgPath, pkg);
|
|
918
917
|
}
|
|
919
918
|
async function main() {
|
|
920
|
-
const argv = (
|
|
919
|
+
const argv = minimist(process.argv.slice(2));
|
|
921
920
|
if (argv.help || argv.h) return void printHelp();
|
|
922
921
|
if (argv.version || argv.v) return void console.log(getVersion());
|
|
923
922
|
const targetDir = argv._[0] ?? "my-extension";
|
|
924
923
|
const cliFramework = argv.framework;
|
|
925
924
|
const cliLanguage = argv.language;
|
|
926
925
|
printAddfoxLogo();
|
|
927
|
-
console.log(
|
|
928
|
-
const root = (
|
|
929
|
-
if (
|
|
926
|
+
console.log(blue("\n Create Addfox App\n"));
|
|
927
|
+
const root = external_node_path_resolve(process.cwd(), targetDir);
|
|
928
|
+
if (existsSync(root)) {
|
|
930
929
|
const confirmed = await confirmOverwrite(targetDir);
|
|
931
930
|
if (!confirmed) process.exit(0);
|
|
932
|
-
|
|
931
|
+
rmSync(root, {
|
|
933
932
|
recursive: true,
|
|
934
933
|
force: true
|
|
935
934
|
});
|
|
@@ -938,7 +937,7 @@ async function main() {
|
|
|
938
937
|
framework: cliFramework,
|
|
939
938
|
styleEngine: parseStyleEngineArg(argv.style),
|
|
940
939
|
language: cliLanguage,
|
|
941
|
-
packageManager:
|
|
940
|
+
packageManager: detectPackageManager(),
|
|
942
941
|
entries: [
|
|
943
942
|
"__all__"
|
|
944
943
|
]
|
|
@@ -961,7 +960,7 @@ async function main() {
|
|
|
961
960
|
minVisibleMs: TEMPLATE_SPINNER_MIN_MS
|
|
962
961
|
} : void 0);
|
|
963
962
|
} catch (err) {
|
|
964
|
-
console.error(
|
|
963
|
+
console.error(red(`\n Failed to copy template: ${err.message}\n`));
|
|
965
964
|
process.exit(1);
|
|
966
965
|
}
|
|
967
966
|
const useAllEntries = options.entries.includes("__all__");
|
|
@@ -971,19 +970,19 @@ async function main() {
|
|
|
971
970
|
] : options.entries.filter((e)=>"__all__" !== e && existingDirs.includes(e));
|
|
972
971
|
if (!useAllEntries && effectiveEntries.length > 0) filterAppEntries(root, effectiveEntries);
|
|
973
972
|
const configExt = "ts" === options.language ? "ts" : "js";
|
|
974
|
-
const configPath = (
|
|
973
|
+
const configPath = external_node_path_resolve(root, `addfox.config.${configExt}`);
|
|
975
974
|
let configContent;
|
|
976
|
-
if (
|
|
977
|
-
const existing =
|
|
975
|
+
if (existsSync(configPath)) {
|
|
976
|
+
const existing = readFileSync(configPath, "utf-8");
|
|
978
977
|
configContent = existing.trim().length > 0 && existing.includes("defineConfig") ? mergeScaffoldIntoAddfoxConfig(existing, options.styleEngine) : generateAddfoxConfig(options.framework, options.language, options.styleEngine);
|
|
979
978
|
} else configContent = generateAddfoxConfig(options.framework, options.language, options.styleEngine);
|
|
980
|
-
|
|
979
|
+
writeFileSync(configPath, configContent, "utf-8");
|
|
981
980
|
updatePackageName(root, targetDir);
|
|
982
981
|
applyStyleEngine(root, options.framework, options.language, options.styleEngine);
|
|
983
982
|
applyTestAndReportSetup(root, options.language, testSelection);
|
|
984
|
-
const installCmd =
|
|
985
|
-
const devCmd =
|
|
986
|
-
const { installSkills } = await (
|
|
983
|
+
const installCmd = getInstallCommand(pm);
|
|
984
|
+
const devCmd = getRunCommand(pm, "dev");
|
|
985
|
+
const { installSkills } = await prompts({
|
|
987
986
|
type: "select",
|
|
988
987
|
name: "installSkills",
|
|
989
988
|
message: "Install addfox skills?",
|
|
@@ -1001,20 +1000,20 @@ async function main() {
|
|
|
1001
1000
|
hint: PROMPT_SELECT_HINT
|
|
1002
1001
|
});
|
|
1003
1002
|
if (true === installSkills) {
|
|
1004
|
-
const execCmd =
|
|
1003
|
+
const execCmd = getExecCommand(pm);
|
|
1005
1004
|
const fullCmd = `${execCmd} skills add ${SKILLS_REPO}`;
|
|
1006
|
-
console.log(
|
|
1005
|
+
console.log(yellow("\n Running: " + fullCmd + "\n"));
|
|
1007
1006
|
try {
|
|
1008
|
-
|
|
1007
|
+
execSync(fullCmd, {
|
|
1009
1008
|
cwd: root,
|
|
1010
1009
|
stdio: "inherit"
|
|
1011
1010
|
});
|
|
1012
1011
|
} catch {
|
|
1013
|
-
console.log(
|
|
1012
|
+
console.log(dim(" (Skills install skipped or failed; you can run it later.)\n"));
|
|
1014
1013
|
}
|
|
1015
1014
|
}
|
|
1016
|
-
console.log(
|
|
1017
|
-
console.log(
|
|
1015
|
+
console.log(green("\n ✓ Project created successfully\n"));
|
|
1016
|
+
console.log(dim(" Next steps:\n"));
|
|
1018
1017
|
console.log(` cd ${targetDir}`);
|
|
1019
1018
|
console.log(` ${installCmd}`);
|
|
1020
1019
|
console.log(` ${devCmd}\n`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-addfox-app",
|
|
3
|
-
"version": "0.1.1-beta.
|
|
3
|
+
"version": "0.1.1-beta.19",
|
|
4
4
|
"description": "Interactive scaffolder for addfox extension projects",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -17,15 +17,15 @@
|
|
|
17
17
|
"prompts": "^2.4.2",
|
|
18
18
|
"kolorist": "^1.8.0",
|
|
19
19
|
"minimist": "^1.2.8",
|
|
20
|
-
"@addfox/pkg-manager": "0.1.1-beta.
|
|
20
|
+
"@addfox/pkg-manager": "0.1.1-beta.12"
|
|
21
21
|
},
|
|
22
22
|
"engines": {
|
|
23
23
|
"node": ">=18.0.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@rslib/core": "^0.
|
|
27
|
-
"@rstest/core": "^0.9.
|
|
28
|
-
"@rstest/coverage-istanbul": "^0.3.
|
|
26
|
+
"@rslib/core": "^0.21.3",
|
|
27
|
+
"@rstest/core": "^0.9.9",
|
|
28
|
+
"@rstest/coverage-istanbul": "^0.3.2",
|
|
29
29
|
"@types/node": "^20.0.0",
|
|
30
30
|
"typescript": "^5.0.0"
|
|
31
31
|
},
|