create-bestax 4.2.13 → 4.2.15
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/README.md +11 -7
- package/dist/cli.d.ts +11 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +17 -2
- package/dist/constants.d.ts +9 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +102 -31
- package/dist/index.js +2 -2
- package/dist/project-creator.d.ts +19 -0
- package/dist/project-creator.d.ts.map +1 -1
- package/dist/project-creator.js +91 -38
- package/package.json +3 -3
- package/templates/skills/bestax-custom-component/examples/stat-card.tsx +2 -2
- package/templates/skills/bestax-form/SKILL.md +17 -7
- package/templates/skills/bestax-form/references/api.md +10 -10
- package/templates/skills/bestax-form/references/patterns.md +9 -1
- package/templates/skills/bestax-icons/SKILL.md +8 -6
- package/templates/skills/bestax-icons/examples/icon-usage.tsx +3 -3
- package/templates/skills/bestax-icons/references/icon-libraries.md +12 -7
- package/templates/skills/bestax-layout-scaffold/examples/app-shell.tsx +1 -1
- package/templates/skills/bestax-layout-scaffold/examples/card-grid.tsx +0 -1
- package/templates/skills/bestax-layout-scaffold/examples/centered.tsx +0 -1
- package/templates/skills/bestax-layout-scaffold/examples/content-page.tsx +0 -1
- package/templates/skills/bestax-layout-scaffold/examples/landing.tsx +1 -1
- package/templates/skills/bestax-migrate/references/bulma-classes/component-map.md +13 -4
- package/templates/skills/bestax-migrate/references/bulma-classes/unmappables.md +10 -1
- package/templates/skills/bestax-optimize/SKILL.md +10 -6
- package/templates/skills/bestax-optimize/references/modular-build.md +13 -8
- package/templates/skills/bestax-theming/examples/dark-mode.tsx +1 -1
- package/templates/skills/bestax-theming/examples/theme-config.tsx +2 -2
- package/templates/skills/bestax-theming/references/css-variables.md +3 -1
- package/templates/vite/README.md +5 -6
- package/templates/vite/src/App.css +14 -1
- package/templates/vite/src/App.jsx +128 -100
- package/templates/vite/src/main.jsx +3 -2
- package/templates/vite-ts/README.md +5 -6
- package/templates/vite-ts/src/App.css +14 -1
- package/templates/vite-ts/src/App.tsx +128 -100
- package/templates/vite-ts/src/main.tsx +3 -2
- package/templates/vite-ts/tsconfig.app.json +25 -0
- package/templates/vite-ts/tsconfig.json +6 -25
- package/templates/vite-ts/tsconfig.node.json +2 -2
package/README.md
CHANGED
|
@@ -29,9 +29,6 @@ npm create bestax@latest my-app
|
|
|
29
29
|
|
|
30
30
|
# Using npx
|
|
31
31
|
npx create-bestax@latest my-app
|
|
32
|
-
|
|
33
|
-
# Alternative naming
|
|
34
|
-
npm create bestax-bulma@latest my-app
|
|
35
32
|
```
|
|
36
33
|
|
|
37
34
|
### Interactive Mode
|
|
@@ -47,7 +44,7 @@ You'll be asked to:
|
|
|
47
44
|
1. Enter a project name (and, if that directory already holds files, confirm removing them;
|
|
48
45
|
nothing is removed until you have answered the last question)
|
|
49
46
|
2. Select a framework (Vite or Vite + TypeScript)
|
|
50
|
-
3. Choose a Bulma CSS flavor (
|
|
47
|
+
3. Choose a Bulma CSS flavor (the `--bulma` options below)
|
|
51
48
|
4. Select an icon library (Font Awesome, Material Icons, etc.)
|
|
52
49
|
5. Choose whether to install the bestax AI skills into `.claude/skills/`
|
|
53
50
|
|
|
@@ -56,9 +53,13 @@ Every prompt has a flag equivalent, so the whole flow is scriptable and CI-frien
|
|
|
56
53
|
### Command Line Options
|
|
57
54
|
|
|
58
55
|
```bash
|
|
59
|
-
npm create bestax@latest [project-directory] [options]
|
|
56
|
+
npm create bestax@latest [project-directory] -- [options]
|
|
60
57
|
```
|
|
61
58
|
|
|
59
|
+
Put the options after `--`. npm needs it to pass them on at all, and so does bun, whose
|
|
60
|
+
`bun create` rejects short flags such as `-t` and `-y` itself. pnpm and Yarn 1 accept the options
|
|
61
|
+
with or without it.
|
|
62
|
+
|
|
62
63
|
**Arguments:**
|
|
63
64
|
|
|
64
65
|
- `[project-directory]` - Optional project directory name. It also becomes the `package.json`
|
|
@@ -104,10 +105,13 @@ npm create bestax@latest [project-directory] [options]
|
|
|
104
105
|
|
|
105
106
|
```bash
|
|
106
107
|
# Create a TypeScript project with Font Awesome icons
|
|
107
|
-
npm create bestax@latest my-app -t vite-ts -i fontawesome -b complete
|
|
108
|
+
npm create bestax@latest my-app -- -t vite-ts -i fontawesome -b complete
|
|
108
109
|
|
|
109
110
|
# Use all defaults (skip prompts)
|
|
110
|
-
npm create bestax@latest my-app -y
|
|
111
|
+
npm create bestax@latest my-app -- -y
|
|
112
|
+
|
|
113
|
+
# The same with bun
|
|
114
|
+
bun create bestax@latest my-app -- -y
|
|
111
115
|
```
|
|
112
116
|
|
|
113
117
|
## Templates
|
package/dist/cli.d.ts
CHANGED
|
@@ -16,5 +16,15 @@ export declare function copyTemplate(templatePath: string, targetPath: string):
|
|
|
16
16
|
export declare function createProject(projectDir?: string, options?: CLIOptions): Promise<void>;
|
|
17
17
|
export declare function createCLI(): Command;
|
|
18
18
|
export declare function isMainModule(importMetaUrl: string, argv1: string): boolean;
|
|
19
|
-
|
|
19
|
+
/**
|
|
20
|
+
* pnpm, bun and bunx hand a `--` to the scaffolder as it was typed, where npm
|
|
21
|
+
* and Yarn 1 strip it, so `pnpm create bestax my-app -- -t vite-ts` arrives as
|
|
22
|
+
* `my-app -- -t vite-ts`. Commander reads `--` as the end of the options, and
|
|
23
|
+
* every flag after it became an extra argument: "too many arguments" (#950).
|
|
24
|
+
* Bun users need that `--`, because `bun create` rejects short flags such as
|
|
25
|
+
* `-t` and `-y` itself. So the first `--` after the script path is dropped
|
|
26
|
+
* before parsing, and a second keeps its usual meaning.
|
|
27
|
+
*/
|
|
28
|
+
export declare function dropForwardedSeparator(argv: readonly string[]): string[];
|
|
29
|
+
export declare function runCLI(argv?: readonly string[]): Promise<void>;
|
|
20
30
|
//# sourceMappingURL=cli.d.ts.map
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,OAAO,EACL,cAAc,EACd,KAAK,UAAU,EACf,KAAK,qBAAqB,EAC3B,MAAM,sBAAsB,CAAC;AAI9B,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC1E,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACtE,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAGzE,OAAO,EAAE,cAAc,EAAE,CAAC;AAC1B,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EACL,iBAAiB,EACjB,wBAAwB,EACxB,cAAc,IAAI,cAAc,EAChC,iBAAiB,IAAI,iBAAiB,EACtC,iBAAiB,IAAI,iBAAiB,GACvC,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,aAAa,EACb,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAG1B,wBAAsB,cAAc,CAClC,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAGxB;AAED,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,qBAAqB,CAAC,CAGhC;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGxD;AAED,wBAAsB,YAAY,CAChC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAsB,aAAa,CACjC,UAAU,CAAC,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,UAAU,GACnB,OAAO,CAAC,IAAI,CAAC,CAGf;AAcD,wBAAgB,SAAS,IAAI,OAAO,CAsCnC;AAED,wBAAgB,YAAY,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAE1E;AAED,wBAAsB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,OAAO,EACL,cAAc,EACd,KAAK,UAAU,EACf,KAAK,qBAAqB,EAC3B,MAAM,sBAAsB,CAAC;AAI9B,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC1E,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACtE,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAGzE,OAAO,EAAE,cAAc,EAAE,CAAC;AAC1B,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EACL,iBAAiB,EACjB,wBAAwB,EACxB,cAAc,IAAI,cAAc,EAChC,iBAAiB,IAAI,iBAAiB,EACtC,iBAAiB,IAAI,iBAAiB,GACvC,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,aAAa,EACb,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAG1B,wBAAsB,cAAc,CAClC,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAGxB;AAED,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,qBAAqB,CAAC,CAGhC;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGxD;AAED,wBAAsB,YAAY,CAChC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAsB,aAAa,CACjC,UAAU,CAAC,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,UAAU,GACnB,OAAO,CAAC,IAAI,CAAC,CAGf;AAcD,wBAAgB,SAAS,IAAI,OAAO,CAsCnC;AAED,wBAAgB,YAAY,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAE1E;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAKxE;AAED,wBAAsB,MAAM,CAC1B,IAAI,GAAE,SAAS,MAAM,EAAiB,GACrC,OAAO,CAAC,IAAI,CAAC,CAGf"}
|
package/dist/cli.js
CHANGED
|
@@ -71,9 +71,24 @@ export function createCLI() {
|
|
|
71
71
|
export function isMainModule(importMetaUrl, argv1) {
|
|
72
72
|
return importMetaUrl === `file://${argv1}`;
|
|
73
73
|
}
|
|
74
|
-
|
|
74
|
+
/**
|
|
75
|
+
* pnpm, bun and bunx hand a `--` to the scaffolder as it was typed, where npm
|
|
76
|
+
* and Yarn 1 strip it, so `pnpm create bestax my-app -- -t vite-ts` arrives as
|
|
77
|
+
* `my-app -- -t vite-ts`. Commander reads `--` as the end of the options, and
|
|
78
|
+
* every flag after it became an extra argument: "too many arguments" (#950).
|
|
79
|
+
* Bun users need that `--`, because `bun create` rejects short flags such as
|
|
80
|
+
* `-t` and `-y` itself. So the first `--` after the script path is dropped
|
|
81
|
+
* before parsing, and a second keeps its usual meaning.
|
|
82
|
+
*/
|
|
83
|
+
export function dropForwardedSeparator(argv) {
|
|
84
|
+
const separator = argv.indexOf('--', 2);
|
|
85
|
+
return separator === -1
|
|
86
|
+
? [...argv]
|
|
87
|
+
: [...argv.slice(0, separator), ...argv.slice(separator + 1)];
|
|
88
|
+
}
|
|
89
|
+
export async function runCLI(argv = process.argv) {
|
|
75
90
|
const program = createCLI();
|
|
76
|
-
await program.parseAsync();
|
|
91
|
+
await program.parseAsync(dropForwardedSeparator(argv));
|
|
77
92
|
}
|
|
78
93
|
// Only run if this is the main module
|
|
79
94
|
/* istanbul ignore next */
|
package/dist/constants.d.ts
CHANGED
|
@@ -66,6 +66,15 @@ export interface BulmaFlavor {
|
|
|
66
66
|
color: typeof chalk.yellow;
|
|
67
67
|
importStatement: string;
|
|
68
68
|
needsPrefix?: boolean;
|
|
69
|
+
noHelpers?: boolean;
|
|
69
70
|
}
|
|
70
71
|
export declare const BULMA_FLAVORS: BulmaFlavor[];
|
|
72
|
+
export declare const CSS_ORDER_COMMENT: string;
|
|
73
|
+
export interface StarterClass {
|
|
74
|
+
prop: string;
|
|
75
|
+
className: string;
|
|
76
|
+
declaration: string;
|
|
77
|
+
}
|
|
78
|
+
export declare const NO_HELPERS_STARTER_CLASSES: StarterClass[];
|
|
79
|
+
export declare const NO_HELPERS_APP_CSS: string;
|
|
71
80
|
//# sourceMappingURL=constants.d.ts.map
|
package/dist/constants.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,KAAK,CAAC,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,SAAS,EAAE,QAAQ,EAG/B,CAAC;AAEF,eAAO,MAAM,oBAAoB,kBAAkB,CAAC;AACpD,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAC3C,eAAO,MAAM,kBAAkB,QAAsB,CAAC;AAGtD,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAOpC,eAAO,MAAM,QAAQ;;;;;;6CASa,MAAM;;;wCAQX,MAAM,WAAW,MAAM,EAAE;oCAE7B,MAAM;gDAIM,MAAM,WAAW,MAAM,EAAE;uCAIlC,MAAM;sCACP,MAAM;4CAEA,MAAM;;;;;wCAMV,MAAM,mBAAmB,MAAM;;;;;CAclD,CAAC;AASX,eAAO,MAAM,WAAW,QAed,CAAC;AAEX,eAAO,MAAM,OAAO;;;;;;;CAQV,CAAC;AAOX,eAAO,MAAM,2BAA2B,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAK9D,CAAC;AAKF,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,KAAK,CAAC,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,SAAS,EAAE,QAAQ,EAG/B,CAAC;AAEF,eAAO,MAAM,oBAAoB,kBAAkB,CAAC;AACpD,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAC3C,eAAO,MAAM,kBAAkB,QAAsB,CAAC;AAGtD,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAOpC,eAAO,MAAM,QAAQ;;;;;;6CASa,MAAM;;;wCAQX,MAAM,WAAW,MAAM,EAAE;oCAE7B,MAAM;gDAIM,MAAM,WAAW,MAAM,EAAE;uCAIlC,MAAM;sCACP,MAAM;4CAEA,MAAM;;;;;wCAMV,MAAM,mBAAmB,MAAM;;;;;CAclD,CAAC;AASX,eAAO,MAAM,WAAW,QAed,CAAC;AAEX,eAAO,MAAM,OAAO;;;;;;;CAQV,CAAC;AAOX,eAAO,MAAM,2BAA2B,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAK9D,CAAC;AAKF,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AAkCD,eAAO,MAAM,SAAS,GACpB,aAAa,MAAM,EACnB,8BAA8B,eAAe,KAC5C,MA8HF,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,KAAK,CAAC,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IAIrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IAGzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,cAAc,EAAE,WAAW,EA8DvC,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,OAAO,KAAK,CAAC,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IAGtB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,eAAO,MAAM,aAAa,EAAE,WAAW,EA8CtC,CAAC;AAKF,eAAO,MAAM,iBAAiB,QAE+B,CAAC;AAE9D,MAAM,WAAW,YAAY;IAE3B,IAAI,EAAE,MAAM,CAAC;IAEb,SAAS,EAAE,MAAM,CAAC;IAElB,WAAW,EAAE,MAAM,CAAC;CACrB;AAOD,eAAO,MAAM,0BAA0B,EAAE,YAAY,EAgBpD,CAAC;AAEF,eAAO,MAAM,kBAAkB,QAMjB,CAAC"}
|
package/dist/constants.js
CHANGED
|
@@ -87,6 +87,36 @@ export const CONFIG_PROVIDER_ICON_VALUES = {
|
|
|
87
87
|
'material-icons': 'material-icons',
|
|
88
88
|
'material-symbols': 'material-symbols',
|
|
89
89
|
};
|
|
90
|
+
// The house style opens with the inline-style table for a flavor that has the
|
|
91
|
+
// helper classes, and with the named-class rule for one that does not.
|
|
92
|
+
const HELPER_HOUSE_STYLE = `**Never inline \`style={{}}\`** — the components accept helper props that cover the common
|
|
93
|
+
cases. Before writing \`style\`, translate each declaration with this table:
|
|
94
|
+
|
|
95
|
+
| Inline style you're about to write | Helper props instead |
|
|
96
|
+
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
97
|
+
| \`marginTop: '1rem'\` (any margin/padding) | \`mt="4"\` — \`m\`/\`mt\`/\`mx\`/\`p\`/\`py\`/… scale: \`1\`=0.25rem, \`2\`=0.5rem, \`3\`=0.75rem, \`4\`=1rem, \`5\`=1.5rem, \`6\`=3rem (nearest step) |
|
|
98
|
+
| \`textAlign: 'center'\` | \`textAlign="centered"\` (also \`left\`, \`right\`, \`justified\`) |
|
|
99
|
+
| \`color: '#…'\` | \`textColor\` with the nearest Bulma color: \`primary\`, \`link\`, \`info\`, \`success\`, \`warning\`, \`danger\`, \`white\`, \`black\`, \`grey\` (+ \`grey-light\`, \`grey-dark\`, …) |
|
|
100
|
+
| \`backgroundColor: '#…'\` | \`bgColor\` (same palette) |
|
|
101
|
+
| \`fontSize: …\` | \`textSize="1"\`…\`"7"\` (\`1\` largest) — for headings use \`Title\`/\`SubTitle\` \`size\` |
|
|
102
|
+
| \`fontWeight: …\` | \`textWeight\`: \`light\`, \`normal\`, \`medium\`, \`semibold\`, \`bold\` |
|
|
103
|
+
| \`textTransform\`, italics | \`textTransform\`: \`uppercase\`, \`lowercase\`, \`capitalized\`, \`italic\` |
|
|
104
|
+
| \`display: 'flex'\` + flex properties | same-named props: \`display="flex"\`, \`flexDirection\`, \`justifyContent\`, \`alignItems\`, \`flexWrap\` |
|
|
105
|
+
| \`height: '100%'\` on a flex child | \`flexGrow="1"\` |
|
|
106
|
+
| \`display: 'none'\` | \`visibility="hidden"\`, or responsive \`display*\` props (\`displayMobile\`, \`displayTablet\`, …) |
|
|
107
|
+
| \`gap: '1rem'\` (flex or grid) | \`gap="2"\`, or one axis: \`columnGap\`, \`rowGap\`; gap scale: \`1\`=0.5rem, \`2\`=1rem, \`4\`=2rem, half steps (\`"1.5"\`) |
|
|
108
|
+
|
|
109
|
+
- Spacing, typography, and flex helpers are on every component; \`textColor\`/\`bgColor\` are
|
|
110
|
+
on the content components (\`Box\`, \`Block\`, \`Title\`, \`Content\`, \`Hero\`, \`Card\`, …) — the
|
|
111
|
+
ones with a semantic \`color\` variant (\`Tag\`, \`Tabs\`, \`Panel\`) take \`color\` instead.
|
|
112
|
+
\`Notification\` is the mixed case: it takes \`textColor\`, but its background comes from
|
|
113
|
+
the semantic \`color\` prop, not \`bgColor\`.
|
|
114
|
+
- No helper matches (e.g. \`maxWidth\`, a one-off gradient)? Add a named class to
|
|
115
|
+
\`src/App.css\` and pass it via \`className\` — still never inline \`style\`.`;
|
|
116
|
+
const NO_HELPERS_HOUSE_STYLE = `**Never inline \`style={{}}\`**, and don't reach for helper props in its place either:
|
|
117
|
+
this app's flavor leaves out Bulma's helper classes, so \`mt="4"\`, \`textAlign="centered"\` and \`flexGrow="1"\`
|
|
118
|
+
render nothing. Write a named class in \`src/App.css\`, which loads after Bulma's CSS, and pass it via \`className\`.
|
|
119
|
+
`;
|
|
90
120
|
export const CLAUDE_MD = (projectName, { bulmaFlavor, iconLibrary }) => {
|
|
91
121
|
const flavor = BULMA_FLAVORS.find(f => f.name === bulmaFlavor);
|
|
92
122
|
const icon = ICON_LIBRARIES.find(lib => lib.name === iconLibrary);
|
|
@@ -99,6 +129,13 @@ export const CLAUDE_MD = (projectName, { bulmaFlavor, iconLibrary }) => {
|
|
|
99
129
|
setupLines.push(`- Every Bulma class carries the \`bestax-\` prefix and the app is wrapped in ` +
|
|
100
130
|
`\`<ConfigProvider classPrefix="bestax-">\` — custom CSS selectors must match the prefix.`);
|
|
101
131
|
}
|
|
132
|
+
if (flavor?.noHelpers) {
|
|
133
|
+
setupLines.push(`- This flavor leaves out Bulma's helper classes, so **helper props that add those classes render nothing**: ` +
|
|
134
|
+
`\`mt\`, \`gap\`, \`textAlign\`, \`textColor\`, \`display\` and \`flexGrow\` still add ` +
|
|
135
|
+
`class names, but no rule matches them. \`skeleton\` still works, because the flavor keeps ` +
|
|
136
|
+
`Bulma's skeleton styles. The starter page lays itself out with named classes in ` +
|
|
137
|
+
`\`src/App.css\` instead.`);
|
|
138
|
+
}
|
|
102
139
|
const providerIconValue = CONFIG_PROVIDER_ICON_VALUES[iconLibrary];
|
|
103
140
|
setupLines.push(iconLibrary === 'none'
|
|
104
141
|
? `- No icon library is installed — add one before using \`<Icon>\` ` +
|
|
@@ -107,7 +144,17 @@ export const CLAUDE_MD = (projectName, { bulmaFlavor, iconLibrary }) => {
|
|
|
107
144
|
(providerIconValue
|
|
108
145
|
? ` The app is already wrapped in \`<ConfigProvider iconLibrary="${providerIconValue}">\` ` +
|
|
109
146
|
`— extend that provider rather than adding a second one.`
|
|
110
|
-
: '')
|
|
147
|
+
: '') +
|
|
148
|
+
(icon?.setupInstructions ? ` ${icon.setupInstructions}` : ''));
|
|
149
|
+
const houseStyleOpening = flavor?.noHelpers
|
|
150
|
+
? NO_HELPERS_HOUSE_STYLE
|
|
151
|
+
: HELPER_HOUSE_STYLE;
|
|
152
|
+
// The wrapper elements are only a way out where their helper props render.
|
|
153
|
+
const utilityClassRule = flavor?.noHelpers
|
|
154
|
+
? `- Don't hand-write Bulma utility classes either: this flavor doesn't ship them, so a
|
|
155
|
+
\`has-text-…\` class renders nothing, and the named class above is what works.`
|
|
156
|
+
: `- Don't hand-write Bulma utility classes either — bare text/markup has wrapper elements that
|
|
157
|
+
take the same helper props: \`Span\`, \`Paragraph\`, \`Strong\`, not \`<span className="has-text-…">\`.`;
|
|
111
158
|
return `# ${projectName}
|
|
112
159
|
|
|
113
160
|
This app is built with [\`@allxsmith/bestax-bulma\`](https://bestax.io) — React components for
|
|
@@ -119,36 +166,12 @@ ${setupLines.join('\n')}
|
|
|
119
166
|
|
|
120
167
|
## House style
|
|
121
168
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
| Inline style you're about to write | Helper props instead |
|
|
126
|
-
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
127
|
-
| \`marginTop: '1rem'\` (any margin/padding) | \`mt="4"\` — \`m\`/\`mt\`/\`mx\`/\`p\`/\`py\`/… scale: \`1\`=0.25rem, \`2\`=0.5rem, \`3\`=0.75rem, \`4\`=1rem, \`5\`=1.5rem, \`6\`=3rem (nearest step) |
|
|
128
|
-
| \`textAlign: 'center'\` | \`textAlign="centered"\` (also \`left\`, \`right\`, \`justified\`) |
|
|
129
|
-
| \`color: '#…'\` | \`textColor\` with the nearest Bulma color: \`primary\`, \`link\`, \`info\`, \`success\`, \`warning\`, \`danger\`, \`white\`, \`black\`, \`grey\` (+ \`grey-light\`, \`grey-dark\`, …) |
|
|
130
|
-
| \`backgroundColor: '#…'\` | \`bgColor\` (same palette) |
|
|
131
|
-
| \`fontSize: …\` | \`textSize="1"\`…\`"7"\` (\`1\` largest) — for headings use \`Title\`/\`SubTitle\` \`size\` |
|
|
132
|
-
| \`fontWeight: …\` | \`textWeight\`: \`light\`, \`normal\`, \`medium\`, \`semibold\`, \`bold\` |
|
|
133
|
-
| \`textTransform\`, italics | \`textTransform\`: \`uppercase\`, \`lowercase\`, \`capitalized\`, \`italic\` |
|
|
134
|
-
| \`display: 'flex'\` + flex properties | same-named props: \`display="flex"\`, \`flexDirection\`, \`justifyContent\`, \`alignItems\`, \`flexWrap\` |
|
|
135
|
-
| \`height: '100%'\` on a flex child | \`flexGrow="1"\` |
|
|
136
|
-
| \`display: 'none'\` | \`visibility="hidden"\`, or responsive \`display*\` props (\`displayMobile\`, \`displayTablet\`, …) |
|
|
137
|
-
| \`gap: '1rem'\` (flex or grid) | \`gap="2"\`, or one axis: \`columnGap\`, \`rowGap\`; gap scale: \`1\`=0.5rem, \`2\`=1rem, \`4\`=2rem, half steps (\`"1.5"\`) |
|
|
138
|
-
|
|
139
|
-
- Spacing, typography, and flex helpers are on every component; \`textColor\`/\`bgColor\` are
|
|
140
|
-
on the content components (\`Box\`, \`Block\`, \`Title\`, \`Content\`, \`Hero\`, \`Card\`, …) — the
|
|
141
|
-
ones with a semantic \`color\` variant (\`Tag\`, \`Tabs\`, \`Panel\`) take \`color\` instead.
|
|
142
|
-
\`Notification\` is the mixed case: it takes \`textColor\`, but its background comes from
|
|
143
|
-
the semantic \`color\` prop, not \`bgColor\`.
|
|
144
|
-
- No helper matches (e.g. \`maxWidth\`, a one-off gradient)? Add a named class to
|
|
145
|
-
\`src/App.css\` and pass it via \`className\` — still never inline \`style\`.
|
|
146
|
-
- Don't hand-write Bulma utility classes either — bare text/markup has wrapper elements that
|
|
147
|
-
take the same helper props: \`Span\`, \`Paragraph\`, \`Strong\`, not \`<span className="has-text-…">\`.
|
|
169
|
+
${houseStyleOpening}
|
|
170
|
+
${utilityClassRule}
|
|
148
171
|
The one exception: companion classes Bulma requires on \`<html>\`/\`<body>\` (e.g.
|
|
149
172
|
\`has-navbar-fixed-top\` with \`Navbar fixed="top"\`) are hand-added in \`index.html\` — no
|
|
150
173
|
component renders those elements.
|
|
151
|
-
- Compound sub-parts (\`
|
|
174
|
+
- Compound sub-parts (\`Modal.*\`, \`Tabs.*\`, \`Message.*\`) take \`className\` + HTML
|
|
152
175
|
attributes and their own few props — no Bulma helper props, no \`as\`/\`href\`: nest a
|
|
153
176
|
\`Link\`/\`Span\` inside instead. \`Tabs.Tab\` and \`Tabs.Content.Item\` each require \`index={i}\`,
|
|
154
177
|
and \`Tabs.Tab\` has built-in \`icon\`/\`disabled\` props — no nested \`Icon\` needed.
|
|
@@ -250,15 +273,30 @@ export const ICON_LIBRARIES = [
|
|
|
250
273
|
color: chalk.yellow,
|
|
251
274
|
packageName: 'material-icons',
|
|
252
275
|
packageVersion: '^1.13.14',
|
|
253
|
-
|
|
276
|
+
// The package's bare import puts the filled, outlined, round, sharp and
|
|
277
|
+
// two-tone fonts into the build. Filled is the style Icon renders by
|
|
278
|
+
// default, so it is the only one imported.
|
|
279
|
+
importStatement: "import 'material-icons/iconfont/filled.css';",
|
|
280
|
+
setupInstructions: 'Only the filled style is loaded (`material-icons/iconfont/filled.css`, the variant `<Icon>` ' +
|
|
281
|
+
'uses by default). Import `outlined.css`, `round.css`, `sharp.css` or `two-tone.css` from ' +
|
|
282
|
+
'`material-icons/iconfont/` in `src/main.*` before using those variants.',
|
|
254
283
|
},
|
|
255
284
|
{
|
|
256
285
|
name: 'material-symbols',
|
|
257
286
|
display: 'Material Symbols',
|
|
258
287
|
color: chalk.magenta,
|
|
259
288
|
packageName: 'material-symbols',
|
|
260
|
-
|
|
261
|
-
|
|
289
|
+
// 0.x, so the caret holds the minor: this follows the newest minor
|
|
290
|
+
// bestax-bulma's material-symbols peer range admits, not the newest
|
|
291
|
+
// published one, or npm refuses the install as a peer conflict.
|
|
292
|
+
packageVersion: '^0.46.0',
|
|
293
|
+
// The package's bare import puts the outlined, rounded and sharp fonts
|
|
294
|
+
// into the build. Outlined is the style Icon renders by default, so it is
|
|
295
|
+
// the only one imported.
|
|
296
|
+
importStatement: "import 'material-symbols/outlined.css';",
|
|
297
|
+
setupInstructions: 'Only the outlined style is loaded (`material-symbols/outlined.css`, the variant `<Icon>` ' +
|
|
298
|
+
'uses by default). Import `material-symbols/rounded.css` or `sharp.css` in `src/main.*` ' +
|
|
299
|
+
'before using `variant="rounded"` or `"sharp"`.',
|
|
262
300
|
},
|
|
263
301
|
];
|
|
264
302
|
export const BULMA_FLAVORS = [
|
|
@@ -283,6 +321,7 @@ export const BULMA_FLAVORS = [
|
|
|
283
321
|
description: 'Core components only, no utility classes — helper props need them (~67 KB gzip)',
|
|
284
322
|
color: chalk.yellow,
|
|
285
323
|
importStatement: "import '@allxsmith/bestax-bulma/versions/bestax-no-helpers.css';",
|
|
324
|
+
noHelpers: true,
|
|
286
325
|
},
|
|
287
326
|
{
|
|
288
327
|
name: 'no-helpers-prefixed',
|
|
@@ -291,6 +330,7 @@ export const BULMA_FLAVORS = [
|
|
|
291
330
|
color: chalk.magenta,
|
|
292
331
|
importStatement: "import '@allxsmith/bestax-bulma/versions/bestax-no-helpers-prefixed.css';",
|
|
293
332
|
needsPrefix: true,
|
|
333
|
+
noHelpers: true,
|
|
294
334
|
},
|
|
295
335
|
{
|
|
296
336
|
name: 'no-dark-mode',
|
|
@@ -300,3 +340,34 @@ export const BULMA_FLAVORS = [
|
|
|
300
340
|
importStatement: "import '@allxsmith/bestax-bulma/versions/bestax-no-dark-mode.css';",
|
|
301
341
|
},
|
|
302
342
|
];
|
|
343
|
+
// Sits above the stylesheet import in the templates' entry files, and the
|
|
344
|
+
// scaffolder writes it back when it swaps in a flavor's stylesheet, so the
|
|
345
|
+
// reason for the import order survives. The no-helpers classes depend on it.
|
|
346
|
+
export const CSS_ORDER_COMMENT = "// Bestax's stylesheet loads before the app's own CSS, so a named class in\n" +
|
|
347
|
+
'// src/App.css wins over a Bulma rule of the same weight.';
|
|
348
|
+
// Every helper prop the starter App uses, with the named class that stands in
|
|
349
|
+
// for it under a `noHelpers` flavor, where the prop would render nothing. The
|
|
350
|
+
// scaffolder swaps each prop for its class and writes the rules into
|
|
351
|
+
// src/App.css, so the starter keeps its layout and shows that flavor's way of
|
|
352
|
+
// styling. A helper prop added to the starter needs a row here.
|
|
353
|
+
export const NO_HELPERS_STARTER_CLASSES = [
|
|
354
|
+
{
|
|
355
|
+
prop: 'textAlign="centered"',
|
|
356
|
+
className: 'page-title',
|
|
357
|
+
declaration: 'text-align: center;',
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
prop: 'display="flex"',
|
|
361
|
+
className: 'card-column',
|
|
362
|
+
declaration: 'display: flex;',
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
prop: 'flexGrow="1"',
|
|
366
|
+
className: 'card-fill',
|
|
367
|
+
declaration: 'flex-grow: 1;',
|
|
368
|
+
},
|
|
369
|
+
];
|
|
370
|
+
export const NO_HELPERS_APP_CSS = '\n/* This flavor of Bulma leaves out the helper classes, so helper props such\n' +
|
|
371
|
+
' as textAlign and flexGrow render nothing here. Named classes like these do\n' +
|
|
372
|
+
' the job instead: write one, then pass it with className. */\n' +
|
|
373
|
+
NO_HELPERS_STARTER_CLASSES.map(({ className, declaration }) => `.${className} {\n ${declaration}\n}\n`).join('\n');
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,6 @@ if (majorVersion < MINIMUM_NODE_VERSION) {
|
|
|
14
14
|
`Please upgrade your Node.js version.`);
|
|
15
15
|
process.exit(1);
|
|
16
16
|
}
|
|
17
|
-
const {
|
|
18
|
-
await
|
|
17
|
+
const { runCLI } = await import('./cli.js');
|
|
18
|
+
await runCLI();
|
|
19
19
|
export {};
|
|
@@ -42,9 +42,28 @@ export declare class ProjectCreator {
|
|
|
42
42
|
getTemplatePath(template: string): string;
|
|
43
43
|
copyTemplate(template: string, targetPath: string): Promise<void>;
|
|
44
44
|
updateIndexHtmlTitle(targetPath: string, projectName: string): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* The template README is written with pnpm commands and lists no icon
|
|
47
|
+
* library. Rewrite the commands for the package manager that ran the CLI
|
|
48
|
+
* (the one the success message names too) and list the icon library the
|
|
49
|
+
* user picked, if any.
|
|
50
|
+
*/
|
|
51
|
+
updateReadme(targetPath: string, iconLibrary: string): Promise<void>;
|
|
45
52
|
setupSkills(targetPath: string, projectName: string, options: ClaudeMdOptions): Promise<void>;
|
|
46
53
|
setupBulmaFlavor(targetPath: string, bulmaFlavor: string, template: string): Promise<void>;
|
|
54
|
+
/**
|
|
55
|
+
* A flavor without Bulma's helper classes leaves the starter's helper props
|
|
56
|
+
* rendering nothing, so its title loses its centering and its cards their
|
|
57
|
+
* equal heights. Swap each prop for the named class in
|
|
58
|
+
* NO_HELPERS_STARTER_CLASSES and append the classes' rules to src/App.css.
|
|
59
|
+
*/
|
|
60
|
+
replaceStarterHelperProps(targetPath: string, template: string): Promise<void>;
|
|
47
61
|
private getIconName;
|
|
62
|
+
/**
|
|
63
|
+
* The `iconProps` entries for a starter card icon. Decorative, since the
|
|
64
|
+
* card title beside it says the same thing, so it is hidden from screen
|
|
65
|
+
* readers rather than announced as "icon".
|
|
66
|
+
*/
|
|
48
67
|
private getIconProps;
|
|
49
68
|
setupIconLibrary(targetPath: string, iconLibrary: string, template: string): Promise<void>;
|
|
50
69
|
setupConfigProvider(targetPath: string, bulmaFlavor: string, iconLibrary: string, template: string): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-creator.d.ts","sourceRoot":"","sources":["../src/project-creator.ts"],"names":[],"mappings":"AA8BA,OAAO,
|
|
1
|
+
{"version":3,"file":"project-creator.d.ts","sourceRoot":"","sources":["../src/project-creator.ts"],"names":[],"mappings":"AA8BA,OAAO,EAWL,KAAK,eAAe,EACrB,MAAM,gBAAgB,CAAC;AAYxB,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;GAIG;AACH,MAAM,MAAM,qBAAqB,GAAG,OAAO,GAAG,WAAW,GAAG,MAAM,CAAC;AAEnE,qBAAa,cAAc;IACzB,OAAO,CAAC,YAAY,CAAS;gBAEjB,YAAY,CAAC,EAAE,MAAM;IAI3B,cAAc,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAajE;;;;;;;OAOG;IACG,sBAAsB,CAC1B,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,UAAU,GACnB,OAAO,CAAC,qBAAqB,CAAC;IA2BjC;;;OAGG;IACH,eAAe,CAAC,OAAO,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI;IAmBpD,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAInC,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMjE,oBAAoB,CACxB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC;IAgBhB;;;;;OAKG;IACG,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBpE,WAAW,CACf,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,IAAI,CAAC;IAqBV,gBAAgB,CACpB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAyDhB;;;;;OAKG;IACG,yBAAyB,CAC7B,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAmBhB,OAAO,CAAC,WAAW;IA2BnB;;;;OAIG;IACH,OAAO,CAAC,YAAY;IAcd,gBAAgB,CACpB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAsJV,mBAAmB,CACvB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAmEV,MAAM,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAoItE;;;;;;;;OAQG;YACW,eAAe;CA2B9B"}
|
package/dist/project-creator.js
CHANGED
|
@@ -7,7 +7,8 @@ import { promptProjectName, promptOverwriteDirectory, promptTemplate, promptIcon
|
|
|
7
7
|
import { reportScaffold } from './telemetry.js';
|
|
8
8
|
import { displayHeader, displaySuccess, displayError, displayCancelled, } from './display.js';
|
|
9
9
|
import { validateProjectName, toValidPackageName } from './validators.js';
|
|
10
|
-
import { MESSAGES, TEMPLATES, ICON_LIBRARIES, BULMA_FLAVORS, CLAUDE_MD, LAUNCH_JSON, CONFIG_PROVIDER_ICON_VALUES, } from './constants.js';
|
|
10
|
+
import { MESSAGES, TEMPLATES, ICON_LIBRARIES, BULMA_FLAVORS, CLAUDE_MD, LAUNCH_JSON, CONFIG_PROVIDER_ICON_VALUES, NO_HELPERS_STARTER_CLASSES, NO_HELPERS_APP_CSS, CSS_ORDER_COMMENT, } from './constants.js';
|
|
11
|
+
import { detectPackageManager } from './package-manager.js';
|
|
11
12
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
13
|
// npm strips literal `.gitignore` files from published tarballs, so the
|
|
13
14
|
// templates track `_gitignore` instead and the scaffolder renames it back
|
|
@@ -100,6 +101,25 @@ export class ProjectCreator {
|
|
|
100
101
|
await fs.writeFile(indexHtmlPath, updated);
|
|
101
102
|
}
|
|
102
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* The template README is written with pnpm commands and lists no icon
|
|
106
|
+
* library. Rewrite the commands for the package manager that ran the CLI
|
|
107
|
+
* (the one the success message names too) and list the icon library the
|
|
108
|
+
* user picked, if any.
|
|
109
|
+
*/
|
|
110
|
+
async updateReadme(targetPath, iconLibrary) {
|
|
111
|
+
const readmePath = path.join(targetPath, 'README.md');
|
|
112
|
+
if (!fs.existsSync(readmePath))
|
|
113
|
+
return;
|
|
114
|
+
const pm = detectPackageManager();
|
|
115
|
+
let readme = await fs.readFile(readmePath, 'utf8');
|
|
116
|
+
readme = readme.replace(/\bpnpm (install|run)\b/g, `${pm} $1`);
|
|
117
|
+
const library = ICON_LIBRARIES.find(lib => lib.name === iconLibrary);
|
|
118
|
+
if (library && iconLibrary !== 'none') {
|
|
119
|
+
readme = readme.replace(/^(- .*bestax-bulma component library)$/m, `$1\n- 🎯 ${library.display} icons`);
|
|
120
|
+
}
|
|
121
|
+
await fs.writeFile(readmePath, readme);
|
|
122
|
+
}
|
|
103
123
|
async setupSkills(targetPath, projectName, options) {
|
|
104
124
|
const skillsSrc = path.join(this.templatesDir, 'skills');
|
|
105
125
|
if (!(await checkDirectoryExists(skillsSrc))) {
|
|
@@ -122,11 +142,13 @@ export class ProjectCreator {
|
|
|
122
142
|
const mainFilePath = path.join(targetPath, 'src', mainFileName);
|
|
123
143
|
if (fs.existsSync(mainFilePath)) {
|
|
124
144
|
let content = await fs.readFile(mainFilePath, 'utf8');
|
|
125
|
-
// Replace the default bestax CSS import
|
|
126
|
-
|
|
145
|
+
// Replace the default bestax CSS import, and the comment block directly
|
|
146
|
+
// above it, with the selected flavor's import under the comment that
|
|
147
|
+
// says why the stylesheets load before the app's own CSS.
|
|
148
|
+
const bestaxImportRegex = /(?:[ \t]*\/\/[^\n]*\n)+[ \t]*import\s+['"]@allxsmith\/bestax-bulma\/bestax\.css['"]\s*;?/;
|
|
127
149
|
const bestaxImportSimpleRegex = /import\s+['"]@allxsmith\/bestax-bulma\/bestax\.css['"]\s*;?/;
|
|
128
150
|
if (bestaxImportRegex.test(content)) {
|
|
129
|
-
content = content.replace(bestaxImportRegex,
|
|
151
|
+
content = content.replace(bestaxImportRegex, CSS_ORDER_COMMENT + '\n' + flavor.importStatement);
|
|
130
152
|
}
|
|
131
153
|
else if (bestaxImportSimpleRegex.test(content)) {
|
|
132
154
|
content = content.replace(bestaxImportSimpleRegex, flavor.importStatement);
|
|
@@ -145,6 +167,29 @@ export class ProjectCreator {
|
|
|
145
167
|
}
|
|
146
168
|
await fs.writeFile(mainFilePath, content);
|
|
147
169
|
}
|
|
170
|
+
if (flavor.noHelpers) {
|
|
171
|
+
await this.replaceStarterHelperProps(targetPath, template);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* A flavor without Bulma's helper classes leaves the starter's helper props
|
|
176
|
+
* rendering nothing, so its title loses its centering and its cards their
|
|
177
|
+
* equal heights. Swap each prop for the named class in
|
|
178
|
+
* NO_HELPERS_STARTER_CLASSES and append the classes' rules to src/App.css.
|
|
179
|
+
*/
|
|
180
|
+
async replaceStarterHelperProps(targetPath, template) {
|
|
181
|
+
const appFileName = template.includes('-ts') ? 'App.tsx' : 'App.jsx';
|
|
182
|
+
const appFilePath = path.join(targetPath, 'src', appFileName);
|
|
183
|
+
const appCssPath = path.join(targetPath, 'src', 'App.css');
|
|
184
|
+
if (!fs.existsSync(appFilePath) || !fs.existsSync(appCssPath))
|
|
185
|
+
return;
|
|
186
|
+
let appContent = await fs.readFile(appFilePath, 'utf8');
|
|
187
|
+
for (const { prop, className } of NO_HELPERS_STARTER_CLASSES) {
|
|
188
|
+
appContent = appContent.split(prop).join(`className="${className}"`);
|
|
189
|
+
}
|
|
190
|
+
await fs.writeFile(appFilePath, appContent);
|
|
191
|
+
const css = await fs.readFile(appCssPath, 'utf8');
|
|
192
|
+
await fs.writeFile(appCssPath, css.replace(/\n*$/, '\n') + NO_HELPERS_APP_CSS);
|
|
148
193
|
}
|
|
149
194
|
getIconName(iconLibrary, iconType) {
|
|
150
195
|
const iconMappings = {
|
|
@@ -168,13 +213,20 @@ export class ProjectCreator {
|
|
|
168
213
|
};
|
|
169
214
|
return iconMappings[iconLibrary]?.[iconType] || '';
|
|
170
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* The `iconProps` entries for a starter card icon. Decorative, since the
|
|
218
|
+
* card title beside it says the same thing, so it is hidden from screen
|
|
219
|
+
* readers rather than announced as "icon".
|
|
220
|
+
*/
|
|
171
221
|
getIconProps(iconLibrary, iconType) {
|
|
172
222
|
const iconName = this.getIconName(iconLibrary, iconType);
|
|
173
223
|
if (!iconName)
|
|
174
|
-
return
|
|
175
|
-
return
|
|
176
|
-
|
|
177
|
-
: `
|
|
224
|
+
return [];
|
|
225
|
+
return [
|
|
226
|
+
`name: '${iconName}'`,
|
|
227
|
+
...(iconLibrary === 'fontawesome' ? [`variant: 'solid'`] : []),
|
|
228
|
+
`'aria-hidden': 'true'`,
|
|
229
|
+
];
|
|
178
230
|
}
|
|
179
231
|
async setupIconLibrary(targetPath, iconLibrary, template) {
|
|
180
232
|
if (iconLibrary === 'none')
|
|
@@ -251,48 +303,48 @@ export class ProjectCreator {
|
|
|
251
303
|
}
|
|
252
304
|
}
|
|
253
305
|
}
|
|
254
|
-
// Now add
|
|
306
|
+
// Now add an icon to each card title in App.jsx/App.tsx (for all icon
|
|
307
|
+
// libraries). IconText spaces the icon from its text with Bulma's own
|
|
308
|
+
// .icon-text rule rather than a margin helper, so the gap holds in the
|
|
309
|
+
// no-helpers flavors too.
|
|
255
310
|
const isTypeScript = template.includes('-ts');
|
|
256
311
|
const appFileName = isTypeScript ? 'App.tsx' : 'App.jsx';
|
|
257
312
|
const appFilePath = path.join(targetPath, 'src', appFileName);
|
|
258
313
|
if (fs.existsSync(appFilePath)) {
|
|
259
314
|
let appContent = await fs.readFile(appFilePath, 'utf8');
|
|
260
|
-
// Add
|
|
315
|
+
// Add IconText to the bestax-bulma imports
|
|
261
316
|
const bulmaImportRegex = /(import\s+\{[\s\S]*?)(}\s+from\s+['"]@allxsmith\/bestax-bulma['"])/;
|
|
262
|
-
if (bulmaImportRegex.test(appContent) &&
|
|
263
|
-
|
|
317
|
+
if (bulmaImportRegex.test(appContent) &&
|
|
318
|
+
!/\bIconText\b/.test(appContent)) {
|
|
264
319
|
const importMatch = appContent.match(bulmaImportRegex);
|
|
265
320
|
if (importMatch) {
|
|
266
321
|
const beforeClosingBrace = importMatch[1];
|
|
267
|
-
// Remove any trailing comma and whitespace, then add
|
|
322
|
+
// Remove any trailing comma and whitespace, then add IconText properly
|
|
268
323
|
const cleanedImport = beforeClosingBrace.replace(/,?\s*$/, '');
|
|
269
|
-
appContent = appContent.replace(bulmaImportRegex, cleanedImport + ',\n
|
|
324
|
+
appContent = appContent.replace(bulmaImportRegex, cleanedImport + ',\n IconText,\n$2');
|
|
270
325
|
}
|
|
271
326
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
if (iconProps) {
|
|
294
|
-
appContent = appContent.replace(examplesRegex, `Card.Header.Title>\n <Icon ${iconProps} size="small" mr="2" />\n Examples\n </Card.Header.Title>`);
|
|
295
|
-
}
|
|
327
|
+
const cardIcons = [
|
|
328
|
+
['rocket', 'Quick Start'],
|
|
329
|
+
['book', 'Documentation'],
|
|
330
|
+
['code', 'Examples'],
|
|
331
|
+
];
|
|
332
|
+
for (const [iconType, title] of cardIcons) {
|
|
333
|
+
const iconProps = this.getIconProps(iconLibrary, iconType);
|
|
334
|
+
if (iconProps.length === 0)
|
|
335
|
+
continue;
|
|
336
|
+
const titleRegex = new RegExp(`^([ \\t]*)(<Card\\.Header\\.Title[^>]*>)\\s*${title}\\s*(</Card\\.Header\\.Title>)`, 'm');
|
|
337
|
+
appContent = appContent.replace(titleRegex, (_match, indent, open, close) => [
|
|
338
|
+
`${indent}${open}`,
|
|
339
|
+
`${indent} <IconText`,
|
|
340
|
+
`${indent} iconProps={{`,
|
|
341
|
+
...iconProps.map(entry => `${indent} ${entry},`),
|
|
342
|
+
`${indent} }}`,
|
|
343
|
+
`${indent} >`,
|
|
344
|
+
`${indent} ${title}`,
|
|
345
|
+
`${indent} </IconText>`,
|
|
346
|
+
`${indent}${close}`,
|
|
347
|
+
].join('\n'));
|
|
296
348
|
}
|
|
297
349
|
await fs.writeFile(appFilePath, appContent);
|
|
298
350
|
}
|
|
@@ -444,6 +496,7 @@ export class ProjectCreator {
|
|
|
444
496
|
await this.copyTemplate(template, targetPath);
|
|
445
497
|
await updatePackageJson(targetPath, packageName);
|
|
446
498
|
await this.updateIndexHtmlTitle(targetPath, projectName);
|
|
499
|
+
await this.updateReadme(targetPath, iconLibrary);
|
|
447
500
|
await this.setupBulmaFlavor(targetPath, bulmaFlavor, template);
|
|
448
501
|
await this.setupIconLibrary(targetPath, iconLibrary, template);
|
|
449
502
|
await this.setupConfigProvider(targetPath, bulmaFlavor, iconLibrary, template);
|