create-bestax 4.2.18 → 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +13 -3
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +1 -0
  4. package/dist/constants.d.ts +12 -0
  5. package/dist/constants.d.ts.map +1 -1
  6. package/dist/constants.js +69 -1
  7. package/dist/file-system.d.ts +5 -1
  8. package/dist/file-system.d.ts.map +1 -1
  9. package/dist/file-system.js +11 -1
  10. package/dist/library-range.d.ts +16 -0
  11. package/dist/library-range.d.ts.map +1 -0
  12. package/dist/library-range.js +40 -0
  13. package/dist/project-creator.d.ts +8 -0
  14. package/dist/project-creator.d.ts.map +1 -1
  15. package/dist/project-creator.js +62 -3
  16. package/dist/prompts.d.ts +1 -0
  17. package/dist/prompts.d.ts.map +1 -1
  18. package/dist/prompts.js +15 -1
  19. package/package.json +8 -8
  20. package/templates/skills/bestax-custom-component/references/component-catalog.md +5 -1
  21. package/templates/skills/bestax-form/SKILL.md +40 -15
  22. package/templates/skills/bestax-form/references/api.md +10 -8
  23. package/templates/skills/bestax-form/references/patterns.md +68 -0
  24. package/templates/skills/bestax-icons/references/icon-libraries.md +14 -1
  25. package/templates/skills/bestax-layout-scaffold/SKILL.md +31 -5
  26. package/templates/skills/bestax-layout-scaffold/references/archetypes.md +2 -2
  27. package/templates/skills/bestax-layout-scaffold/references/layout-components.md +52 -5
  28. package/templates/skills/bestax-migrate/SKILL.md +19 -5
  29. package/templates/skills/bestax-migrate/references/bloomer/component-map.md +1 -1
  30. package/templates/skills/bestax-migrate/references/bloomer/prop-map.md +9 -6
  31. package/templates/skills/bestax-migrate/references/bloomer/unmappables.md +4 -1
  32. package/templates/skills/bestax-migrate/references/bulma-classes/component-map.md +182 -105
  33. package/templates/skills/bestax-migrate/references/bulma-classes/prop-map.md +50 -17
  34. package/templates/skills/bestax-migrate/references/bulma-classes/unmappables.md +372 -64
  35. package/templates/skills/bestax-migrate/references/css-migration.md +12 -5
  36. package/templates/skills/bestax-migrate/references/rbx/prop-map.md +20 -14
  37. package/templates/skills/bestax-migrate/references/react-bulma-components/prop-map.md +27 -22
  38. package/templates/skills/bestax-migrate/references/react-bulma-components/unmappables.md +3 -3
  39. package/templates/skills/bestax-optimize/references/modular-build.md +59 -17
  40. package/templates/skills/bestax-theming/SKILL.md +22 -8
  41. package/templates/skills/bestax-theming/examples/dark-mode.tsx +100 -17
  42. package/templates/skills/bestax-theming/examples/theme-config.tsx +20 -8
  43. package/templates/skills/bestax-theming/references/css-variables.md +268 -20
  44. package/templates/skills/bestax-theming/references/themeable-components.md +4 -0
  45. package/templates/vite/README.md +2 -2
  46. package/templates/vite/_oxlintrc.json +8 -0
  47. package/templates/vite/package.json +9 -13
  48. package/templates/vite/src/main.jsx +1 -1
  49. package/templates/vite/vite.config.js +1 -1
  50. package/templates/vite-ts/README.md +2 -2
  51. package/templates/vite-ts/_oxlintrc.json +8 -0
  52. package/templates/vite-ts/package.json +11 -16
  53. package/templates/vite-ts/src/main.tsx +1 -1
  54. package/templates/vite/eslint.config.js +0 -29
  55. package/templates/vite-ts/eslint.config.js +0 -26
package/README.md CHANGED
@@ -44,9 +44,10 @@ You'll be asked to:
44
44
  1. Enter a project name or path (and, if that directory already holds files, confirm removing
45
45
  them; nothing is removed until you have answered the last question)
46
46
  2. Select a framework (Vite or Vite + TypeScript)
47
- 3. Choose a Bulma CSS flavor (the `--bulma` options below)
48
- 4. Select an icon library (Font Awesome, Material Icons, etc.)
49
- 5. Choose whether to install the bestax AI skills into `.claude/skills/`
47
+ 3. Choose a linter (Oxlint or ESLint)
48
+ 4. Choose a Bulma CSS flavor (the `--bulma` options below)
49
+ 5. Select an icon library (Font Awesome, Material Icons, etc.)
50
+ 6. Choose whether to install the bestax AI skills into `.claude/skills/`
50
51
 
51
52
  Every prompt has a flag equivalent, so the whole flow is scriptable and CI-friendly — see the options below.
52
53
 
@@ -73,6 +74,11 @@ with or without it.
73
74
 
74
75
  - `-t, --template <template>` - Template to use: `vite` or `vite-ts`
75
76
  **Default:** `vite`
77
+ - `--lint <linter>` - Linter:
78
+ - `oxlint` - Oxlint, as `npm create vite` sets up. `vite-ts` gets TypeScript 7.
79
+ - `eslint` - ESLint with typescript-eslint, as `npm create vite -- --eslint` sets up.
80
+ typescript-eslint doesn't support TypeScript 7 yet, so `vite-ts` stays on TypeScript 6.
81
+ **Default:** `oxlint`, which a run without a terminal also gets when it leaves `--lint` out
76
82
  - `-b, --bulma <flavor>` - Bulma CSS flavor:
77
83
  - `complete` - Full Bulma CSS with all components and helpers
78
84
  - `prefixed` - Prefixed version for compatibility
@@ -94,6 +100,7 @@ with or without it.
94
100
  - `-y, --yes` - Skip prompts and use defaults or provided options.
95
101
  When used, the following defaults are selected unless overridden by flags:
96
102
  - Template: `vite`
103
+ - Linter: `oxlint`
97
104
  - Bulma flavor: `complete`
98
105
  - Icon library: `none`
99
106
  - AI skills: installed (pass `--no-skills` to opt out)
@@ -112,6 +119,9 @@ with or without it.
112
119
  # Create a TypeScript project with Font Awesome icons
113
120
  npm create bestax@latest my-app -- -t vite-ts -i fontawesome -b complete
114
121
 
122
+ # The same, linted with ESLint (and on TypeScript 6)
123
+ npm create bestax@latest my-app -- -t vite-ts -i fontawesome --lint eslint
124
+
115
125
  # Use all defaults (skip prompts)
116
126
  npm create bestax@latest my-app -- -y
117
127
 
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,CAyCnC;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"}
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,CA0CnC;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
@@ -55,6 +55,7 @@ export function createCLI() {
55
55
  .version(getPackageVersion())
56
56
  .argument('[project-directory]', 'project directory to create, a name or a path (its last part names the package)')
57
57
  .option('-t, --template <template>', 'template to use (vite, vite-ts)')
58
+ .option('--lint <linter>', 'linter (oxlint, eslint)')
58
59
  .option('-b, --bulma <flavor>', 'Bulma CSS flavor (complete, prefixed, no-helpers, no-helpers-prefixed, no-dark-mode)')
59
60
  .option('-i, --icon <library>', 'icon library (none, fontawesome, mdi, ionicons, material-icons, material-symbols)')
60
61
  .option('--skills', 'install the bestax AI skills into .claude/skills')
@@ -40,6 +40,7 @@ export declare const LAUNCH_JSON: string;
40
40
  export declare const PROMPTS: {
41
41
  readonly PROJECT_NAME: "Project name:";
42
42
  readonly SELECT_FRAMEWORK: "Select a framework:";
43
+ readonly SELECT_LINTER: "Which linter would you like to use?";
43
44
  readonly SELECT_ICON_LIBRARY: "Would you like to add an icon library?";
44
45
  readonly SELECT_BULMA_FLAVOR: "Which Bulma CSS flavor would you like to use?";
45
46
  readonly INSTALL_SKILLS: "Install the bestax AI skills (.claude/skills) for Claude Code and other agents?";
@@ -71,6 +72,17 @@ export interface BulmaFlavor {
71
72
  noHelpers?: boolean;
72
73
  }
73
74
  export declare const BULMA_FLAVORS: BulmaFlavor[];
75
+ export interface Linter {
76
+ name: string;
77
+ display: string;
78
+ description: string;
79
+ color: typeof chalk.yellow;
80
+ }
81
+ export declare const LINTERS: Linter[];
82
+ export declare const ESLINT_DEV_DEPENDENCIES: Record<string, string>;
83
+ export declare const ESLINT_TYPESCRIPT_DEV_DEPENDENCIES: Record<string, string>;
84
+ /** The eslint.config.js create-vite's `--eslint` writes. */
85
+ export declare const ESLINT_CONFIG: (typescript: boolean) => string;
74
86
  export declare const CSS_ORDER_COMMENT: string;
75
87
  export interface StarterClass {
76
88
  prop: string;
@@ -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;;;;;;+CASe,MAAM,QAAQ,MAAM;6CAEtB,MAAM;;;wCAQX,MAAM,WAAW,MAAM,EAAE;oCAE7B,MAAM;wCAIF,MAAM;gDAKE,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"}
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;;;;;;+CASe,MAAM,QAAQ,MAAM;6CAEtB,MAAM;;;wCAQX,MAAM,WAAW,MAAM,EAAE;oCAE7B,MAAM;wCAIF,MAAM;gDAKE,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;;;;;;;;CASV,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;AAEF,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,OAAO,KAAK,CAAC,MAAM,CAAC;CAC5B;AAKD,eAAO,MAAM,OAAO,EAAE,MAAM,EAe3B,CAAC;AAIF,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAM1D,CAAC;AAKF,eAAO,MAAM,kCAAkC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAGrE,CAAC;AAEF,4DAA4D;AAC5D,eAAO,MAAM,aAAa,GAAI,YAAY,OAAO,KAAG,MA8BtC,CAAC;AAQf,eAAO,MAAM,iBAAiB,QAEiC,CAAC;AAEhE,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
@@ -76,6 +76,7 @@ export const LAUNCH_JSON = JSON.stringify({
76
76
  export const PROMPTS = {
77
77
  PROJECT_NAME: 'Project name:',
78
78
  SELECT_FRAMEWORK: 'Select a framework:',
79
+ SELECT_LINTER: 'Which linter would you like to use?',
79
80
  SELECT_ICON_LIBRARY: 'Would you like to add an icon library?',
80
81
  SELECT_BULMA_FLAVOR: 'Which Bulma CSS flavor would you like to use?',
81
82
  INSTALL_SKILLS: 'Install the bestax AI skills (.claude/skills) for Claude Code and other agents?',
@@ -345,11 +346,78 @@ export const BULMA_FLAVORS = [
345
346
  importStatement: "import '@allxsmith/bestax-bulma/versions/bestax-no-dark-mode.css';",
346
347
  },
347
348
  ];
349
+ // The templates ship create-vite's default lint setup, Oxlint, and the
350
+ // scaffolder writes create-vite's `--eslint` setup in its place for ESLint
351
+ // (ProjectCreator.setupLinter).
352
+ export const LINTERS = [
353
+ {
354
+ name: 'oxlint',
355
+ display: 'Oxlint (Recommended)',
356
+ description: "create-vite's default, and TypeScript 7 for Vite + TypeScript",
357
+ color: chalk.green,
358
+ },
359
+ {
360
+ name: 'eslint',
361
+ display: 'ESLint',
362
+ description: 'with typescript-eslint, which keeps Vite + TypeScript on TypeScript 6',
363
+ color: chalk.blue,
364
+ },
365
+ ];
366
+ // The packages create-vite's `--eslint` puts in place of oxlint. When
367
+ // create-vite moves them, these follow (create-bestax/CLAUDE.md).
368
+ export const ESLINT_DEV_DEPENDENCIES = {
369
+ '@eslint/js': '^10.0.1',
370
+ eslint: '^10.13.0',
371
+ 'eslint-plugin-react-hooks': '^7.1.1',
372
+ 'eslint-plugin-react-refresh': '^0.5.7',
373
+ globals: '^17.13.0',
374
+ };
375
+ // What `--eslint` adds for a TypeScript app. typescript-eslint loads
376
+ // TypeScript's compiler API, which TypeScript 7.0 doesn't ship, and accepts
377
+ // TypeScript below 6.1 only, so the app stays on TypeScript 6 (#394).
378
+ export const ESLINT_TYPESCRIPT_DEV_DEPENDENCIES = {
379
+ typescript: '~6.0.3',
380
+ 'typescript-eslint': '^8.72.0',
381
+ };
382
+ /** The eslint.config.js create-vite's `--eslint` writes. */
383
+ export const ESLINT_CONFIG = (typescript) => [
384
+ "import js from '@eslint/js';",
385
+ "import globals from 'globals';",
386
+ "import reactHooks from 'eslint-plugin-react-hooks';",
387
+ "import reactRefresh from 'eslint-plugin-react-refresh';",
388
+ ...(typescript ? ["import tseslint from 'typescript-eslint';"] : []),
389
+ "import { defineConfig, globalIgnores } from 'eslint/config';",
390
+ '',
391
+ 'export default defineConfig([',
392
+ " globalIgnores(['dist']),",
393
+ ' {',
394
+ typescript
395
+ ? " files: ['**/*.{ts,tsx}'],"
396
+ : " files: ['**/*.{js,jsx}'],",
397
+ ' extends: [',
398
+ ' js.configs.recommended,',
399
+ ...(typescript ? [' tseslint.configs.recommended,'] : []),
400
+ ' reactHooks.configs.flat.recommended,',
401
+ ' reactRefresh.configs.vite,',
402
+ ' ],',
403
+ ' languageOptions: {',
404
+ ' globals: globals.browser,',
405
+ ...(typescript
406
+ ? []
407
+ : [' parserOptions: { ecmaFeatures: { jsx: true } },']),
408
+ ' },',
409
+ ' },',
410
+ ']);',
411
+ '',
412
+ ].join('\n');
348
413
  // Sits above the stylesheet import in the templates' entry files, and the
349
414
  // scaffolder writes it back when it swaps in a flavor's stylesheet, so the
350
415
  // reason for the import order survives. The no-helpers classes depend on it.
416
+ // It names src/index.css, the app's global stylesheet, rather than the starter
417
+ // page's src/App.css, which loads after bestax's too but goes when a port
418
+ // replaces the page (#1240).
351
419
  export const CSS_ORDER_COMMENT = "// Bestax's stylesheet loads before the app's own CSS, so a named class in\n" +
352
- '// src/App.css wins over a Bulma rule of the same weight.';
420
+ '// src/index.css wins over a Bulma rule of the same weight.';
353
421
  // Every helper prop the starter App uses, with the named class that stands in
354
422
  // for it under a `noHelpers` flavor, where the prop would render nothing. The
355
423
  // scaffolder swaps each prop for its class and writes the rules into
@@ -25,5 +25,9 @@ export declare function ensureDirectory(targetPath: string): Promise<void>;
25
25
  export declare function copyDirectory(source: string, destination: string, renames?: Record<string, string>): Promise<void>;
26
26
  export declare function readJsonFile<T = unknown>(filePath: string): Promise<T>;
27
27
  export declare function writeJsonFile(filePath: string, data: unknown, spaces?: number): Promise<void>;
28
- export declare function updatePackageJson(targetPath: string, projectName: string): Promise<void>;
28
+ /**
29
+ * Names the copied app's package and sets the given dependencies' ranges. One
30
+ * the template already lists keeps its place among the others.
31
+ */
32
+ export declare function updatePackageJson(targetPath: string, projectName: string, dependencies?: Record<string, string>): Promise<void>;
29
33
  //# sourceMappingURL=file-system.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"file-system.d.ts","sourceRoot":"","sources":["../src/file-system.ts"],"names":[],"mappings":"AAIA,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,OAAO,CAAC,CAElB;AAED,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAM3E;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAkBxB;AAED;;;;GAIG;AACH,wBAAsB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAMlE;AAED,4EAA4E;AAC5E,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,EAAE,CAAC,CAKnB;AAED,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtE;AAED,wBAAsB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEvE;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,IAAI,CAAC,CAef;AAED,wBAAsB,YAAY,CAAC,CAAC,GAAG,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAE5E;AAED,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,OAAO,EACb,MAAM,SAAI,GACT,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAQf"}
1
+ {"version":3,"file":"file-system.d.ts","sourceRoot":"","sources":["../src/file-system.ts"],"names":[],"mappings":"AAIA,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,OAAO,CAAC,CAElB;AAED,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAM3E;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAkBxB;AAED;;;;GAIG;AACH,wBAAsB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAMlE;AAED,4EAA4E;AAC5E,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,EAAE,CAAC,CAKnB;AAED,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtE;AAED,wBAAsB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEvE;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,IAAI,CAAC,CAef;AAED,wBAAsB,YAAY,CAAC,CAAC,GAAG,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAE5E;AAED,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,OAAO,EACb,MAAM,SAAI,GACT,OAAO,CAAC,IAAI,CAAC,CAEf;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,YAAY,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GACxC,OAAO,CAAC,IAAI,CAAC,CAcf"}
@@ -90,11 +90,21 @@ export async function readJsonFile(filePath) {
90
90
  export async function writeJsonFile(filePath, data, spaces = 2) {
91
91
  await fs.writeJson(filePath, data, { spaces });
92
92
  }
93
- export async function updatePackageJson(targetPath, projectName) {
93
+ /**
94
+ * Names the copied app's package and sets the given dependencies' ranges. One
95
+ * the template already lists keeps its place among the others.
96
+ */
97
+ export async function updatePackageJson(targetPath, projectName, dependencies = {}) {
94
98
  const packageJsonPath = path.join(targetPath, 'package.json');
95
99
  if (fs.existsSync(packageJsonPath)) {
96
100
  const packageJson = await fs.readJson(packageJsonPath);
97
101
  packageJson.name = projectName;
102
+ if (Object.keys(dependencies).length > 0) {
103
+ packageJson.dependencies = {
104
+ ...packageJson.dependencies,
105
+ ...dependencies,
106
+ };
107
+ }
98
108
  await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
99
109
  }
100
110
  }
@@ -0,0 +1,16 @@
1
+ /** The library every template is built on. */
2
+ export declare const LIBRARY = "@allxsmith/bestax-bulma";
3
+ /**
4
+ * The range a scaffolded app gives @allxsmith/bestax-bulma: the one this
5
+ * CLI's own manifest declares, so the app is floored at the release the CLI
6
+ * was built with (#1240). The manifest spells it `workspace:^`, which
7
+ * `pnpm publish` rewrites to a caret on the library's version in the checkout
8
+ * it packs, and CI releases bulma-ui first in the same job. A published CLI
9
+ * therefore reads a plain range, the one `npm create bestax` installed the
10
+ * library from beside it. Run from this repo, the protocol is still there,
11
+ * and it is resolved the way `pnpm publish` resolves it, from the version of
12
+ * the library the workspace links, so a scaffold made with a checkout's build
13
+ * gets that checkout's release.
14
+ */
15
+ export declare function libraryRange(packageRoot?: string): string;
16
+ //# sourceMappingURL=library-range.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"library-range.d.ts","sourceRoot":"","sources":["../src/library-range.ts"],"names":[],"mappings":"AAIA,8CAA8C;AAC9C,eAAO,MAAM,OAAO,4BAA4B,CAAC;AAqBjD;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,WAAW,GAAE,MAAqB,GAAG,MAAM,CAkBvE"}
@@ -0,0 +1,40 @@
1
+ import { readFileSync } from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ /** The library every template is built on. */
5
+ export const LIBRARY = '@allxsmith/bestax-bulma';
6
+ const WORKSPACE = 'workspace:';
7
+ // dist/ and src/ both sit directly under the package root.
8
+ const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
9
+ function readManifest(dir) {
10
+ return JSON.parse(readFileSync(path.join(dir, 'package.json'), 'utf8'));
11
+ }
12
+ /**
13
+ * The range a scaffolded app gives @allxsmith/bestax-bulma: the one this
14
+ * CLI's own manifest declares, so the app is floored at the release the CLI
15
+ * was built with (#1240). The manifest spells it `workspace:^`, which
16
+ * `pnpm publish` rewrites to a caret on the library's version in the checkout
17
+ * it packs, and CI releases bulma-ui first in the same job. A published CLI
18
+ * therefore reads a plain range, the one `npm create bestax` installed the
19
+ * library from beside it. Run from this repo, the protocol is still there,
20
+ * and it is resolved the way `pnpm publish` resolves it, from the version of
21
+ * the library the workspace links, so a scaffold made with a checkout's build
22
+ * gets that checkout's release.
23
+ */
24
+ export function libraryRange(packageRoot = PACKAGE_ROOT) {
25
+ const declared = readManifest(packageRoot).dependencies?.[LIBRARY];
26
+ if (declared === undefined) {
27
+ throw new Error(`${path.join(packageRoot, 'package.json')} declares no ${LIBRARY} dependency.`);
28
+ }
29
+ if (!declared.startsWith(WORKSPACE))
30
+ return declared;
31
+ const { version } = readManifest(path.join(packageRoot, 'node_modules', LIBRARY));
32
+ // What pnpm publish writes for each form: `*` the version itself, `^` and
33
+ // `~` that operator on it, and a range as written.
34
+ const spec = declared.slice(WORKSPACE.length);
35
+ if (spec === '*')
36
+ return version;
37
+ if (spec === '^' || spec === '~')
38
+ return `${spec}${version}`;
39
+ return spec;
40
+ }
@@ -8,6 +8,7 @@ export interface ProjectConfig {
8
8
  }
9
9
  export interface CLIOptions {
10
10
  template?: string;
11
+ lint?: string;
11
12
  bulma?: string;
12
13
  icon?: string;
13
14
  skills?: boolean;
@@ -64,6 +65,13 @@ export declare class ProjectCreator {
64
65
  * user picked, if any.
65
66
  */
66
67
  updateReadme(targetPath: string, iconLibrary: string): Promise<void>;
68
+ /**
69
+ * The templates ship create-vite's default lint setup, Oxlint. For ESLint,
70
+ * write what create-vite's `--eslint` writes in its place: the config, the
71
+ * packages and the lint script, and for TypeScript, TypeScript 6, since
72
+ * typescript-eslint can't load TypeScript 7 (#394).
73
+ */
74
+ setupLinter(targetPath: string, linter: string, template: string): Promise<void>;
67
75
  setupSkills(targetPath: string, projectName: string, options: ClaudeMdOptions): Promise<void>;
68
76
  setupBulmaFlavor(targetPath: string, bulmaFlavor: string, template: string): Promise<void>;
69
77
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"project-creator.d.ts","sourceRoot":"","sources":["../src/project-creator.ts"],"names":[],"mappings":"AA+BA,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;IAIjC;;;OAGG;IACG,cAAc,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAajE;;;;;;;;;;OAUG;IACG,sBAAsB,CAC1B,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,UAAU,GACnB,OAAO,CAAC,qBAAqB,CAAC;IAqCjC;;;;;;OAMG;YACW,qBAAqB;IAanC;;;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;IAuItE;;;;;;;;OAQG;YACW,eAAe;CA2B9B"}
1
+ {"version":3,"file":"project-creator.d.ts","sourceRoot":"","sources":["../src/project-creator.ts"],"names":[],"mappings":"AAgCA,OAAO,EAeL,KAAK,eAAe,EACrB,MAAM,gBAAgB,CAAC;AAexB,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,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,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;IAIjC;;;OAGG;IACG,cAAc,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAajE;;;;;;;;;;OAUG;IACG,sBAAsB,CAC1B,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,UAAU,GACnB,OAAO,CAAC,qBAAqB,CAAC;IAqCjC;;;;;;OAMG;YACW,qBAAqB;IAanC;;;OAGG;IACH,eAAe,CAAC,OAAO,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI;IAwBpD,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;IAkB1E;;;;;OAKG;IACG,WAAW,CACf,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAmCV,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;IA4JtE;;;;;;;;OAQG;YACW,eAAe;CA2B9B"}
@@ -3,18 +3,21 @@ import { fileURLToPath } from 'url';
3
3
  import chalk from 'chalk';
4
4
  import fs from 'fs-extra';
5
5
  import { checkDirectoryExists, listDirectoryEntries, findNonDirectoryInPath, realPath, emptyDirectory, ensureDirectory, copyDirectory, updatePackageJson, } from './file-system.js';
6
- import { promptProjectName, promptOverwriteDirectory, promptTemplate, promptIconLibrary, promptBulmaFlavor, promptInstallSkills, promptTelemetryConsent, } from './prompts.js';
6
+ import { promptProjectName, promptOverwriteDirectory, promptTemplate, promptLinter, promptIconLibrary, promptBulmaFlavor, promptInstallSkills, promptTelemetryConsent, } from './prompts.js';
7
7
  import { reportScaffold } from './telemetry.js';
8
8
  import { displayHeader, displaySuccess, displayError, displayCancelled, } from './display.js';
9
9
  import { validateProjectDirectory, toValidPackageName } from './validators.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';
10
+ import { MESSAGES, TEMPLATES, ICON_LIBRARIES, BULMA_FLAVORS, LINTERS, ESLINT_CONFIG, ESLINT_DEV_DEPENDENCIES, ESLINT_TYPESCRIPT_DEV_DEPENDENCIES, CLAUDE_MD, LAUNCH_JSON, CONFIG_PROVIDER_ICON_VALUES, NO_HELPERS_STARTER_CLASSES, NO_HELPERS_APP_CSS, CSS_ORDER_COMMENT, } from './constants.js';
11
11
  import { detectPackageManager } from './package-manager.js';
12
+ import { LIBRARY, libraryRange } from './library-range.js';
12
13
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
13
14
  // npm strips literal `.gitignore` files from published tarballs, so the
14
15
  // templates track `_gitignore` instead and the scaffolder renames it back
15
16
  // on copy — otherwise published scaffolds would ship with no .gitignore.
17
+ // The Oxlint config follows create-vite, which names it the same way.
16
18
  const TEMPLATE_RENAMES = {
17
19
  _gitignore: '.gitignore',
20
+ '_oxlintrc.json': '.oxlintrc.json',
18
21
  };
19
22
  export class ProjectCreator {
20
23
  templatesDir;
@@ -97,6 +100,10 @@ export class ProjectCreator {
97
100
  if (options?.template && !validTemplates.includes(options.template)) {
98
101
  return `Invalid template: ${options.template}. Valid options are: ${validTemplates.join(', ')}`;
99
102
  }
103
+ const validLinters = LINTERS.map(l => l.name);
104
+ if (options?.lint && !validLinters.includes(options.lint)) {
105
+ return `Invalid linter: ${options.lint}. Valid options are: ${validLinters.join(', ')}`;
106
+ }
100
107
  const validFlavors = BULMA_FLAVORS.map(f => f.name);
101
108
  if (options?.bulma && !validFlavors.includes(options.bulma)) {
102
109
  return `Invalid Bulma flavor: ${options.bulma}. Valid options are: ${validFlavors.join(', ')}`;
@@ -146,6 +153,37 @@ export class ProjectCreator {
146
153
  }
147
154
  await fs.writeFile(readmePath, readme);
148
155
  }
156
+ /**
157
+ * The templates ship create-vite's default lint setup, Oxlint. For ESLint,
158
+ * write what create-vite's `--eslint` writes in its place: the config, the
159
+ * packages and the lint script, and for TypeScript, TypeScript 6, since
160
+ * typescript-eslint can't load TypeScript 7 (#394).
161
+ */
162
+ async setupLinter(targetPath, linter, template) {
163
+ if (linter !== 'eslint')
164
+ return;
165
+ const isTypeScript = template.includes('-ts');
166
+ await fs.remove(path.join(targetPath, '.oxlintrc.json'));
167
+ await fs.writeFile(path.join(targetPath, 'eslint.config.js'), ESLINT_CONFIG(isTypeScript));
168
+ const packageJsonPath = path.join(targetPath, 'package.json');
169
+ if (fs.existsSync(packageJsonPath)) {
170
+ const packageJson = await fs.readJson(packageJsonPath);
171
+ const devDependencies = {
172
+ ...packageJson.devDependencies,
173
+ ...ESLINT_DEV_DEPENDENCIES,
174
+ ...(isTypeScript ? ESLINT_TYPESCRIPT_DEV_DEPENDENCIES : {}),
175
+ };
176
+ delete devDependencies.oxlint;
177
+ packageJson.devDependencies = Object.fromEntries(Object.entries(devDependencies).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0));
178
+ packageJson.scripts = { ...packageJson.scripts, lint: 'eslint .' };
179
+ await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
180
+ }
181
+ const readmePath = path.join(targetPath, 'README.md');
182
+ if (fs.existsSync(readmePath)) {
183
+ const readme = await fs.readFile(readmePath, 'utf8');
184
+ await fs.writeFile(readmePath, readme.replace(/\bOxlint\b/g, 'ESLint'));
185
+ }
186
+ }
149
187
  async setupSkills(targetPath, projectName, options) {
150
188
  const skillsSrc = path.join(this.templatesDir, 'skills');
151
189
  if (!(await checkDirectoryExists(skillsSrc))) {
@@ -465,6 +503,24 @@ export class ProjectCreator {
465
503
  process.exit(1);
466
504
  }
467
505
  }
506
+ // Select linter (use option or prompt). The question is newer than the
507
+ // others, so a run without a terminal that answers every other one by
508
+ // flag takes the default here rather than stopping, as it did before the
509
+ // question existed (#192).
510
+ let linter;
511
+ if (options?.lint) {
512
+ linter = options.lint;
513
+ }
514
+ else if (options?.yes || !process.stdin.isTTY) {
515
+ linter = 'oxlint'; // default linter
516
+ }
517
+ else {
518
+ linter = await promptLinter();
519
+ if (linter === null) {
520
+ displayCancelled();
521
+ process.exit(1);
522
+ }
523
+ }
468
524
  // Select Bulma flavor (use option or prompt)
469
525
  let bulmaFlavor;
470
526
  if (options?.bulma) {
@@ -511,6 +567,8 @@ export class ProjectCreator {
511
567
  }
512
568
  }
513
569
  try {
570
+ // Read before the directory is emptied, so a failed read costs nothing.
571
+ const libraryDependency = { [LIBRARY]: libraryRange() };
514
572
  if (directoryPlan === 'overwrite') {
515
573
  console.log(chalk.yellow(MESSAGES.EMPTYING_DIRECTORY(targetDir)));
516
574
  await emptyDirectory(targetPath);
@@ -523,12 +581,13 @@ export class ProjectCreator {
523
581
  console.log(chalk.dim(MESSAGES.PACKAGE_NAME_NORMALIZED(packageName)));
524
582
  }
525
583
  await this.copyTemplate(template, targetPath);
526
- await updatePackageJson(targetPath, packageName);
584
+ await updatePackageJson(targetPath, packageName, libraryDependency);
527
585
  await this.updateIndexHtmlTitle(targetPath, projectName);
528
586
  await this.updateReadme(targetPath, iconLibrary);
529
587
  await this.setupBulmaFlavor(targetPath, bulmaFlavor, template);
530
588
  await this.setupIconLibrary(targetPath, iconLibrary, template);
531
589
  await this.setupConfigProvider(targetPath, bulmaFlavor, iconLibrary, template);
590
+ await this.setupLinter(targetPath, linter, template);
532
591
  if (installSkills) {
533
592
  await this.setupSkills(targetPath, projectName, {
534
593
  bulmaFlavor,
package/dist/prompts.d.ts CHANGED
@@ -8,6 +8,7 @@ export declare function promptOverwriteDirectory(targetDir: string, entries: str
8
8
  */
9
9
  export declare function promptInstallSkills(): Promise<boolean | null>;
10
10
  export declare function promptTemplate(): Promise<string | null>;
11
+ export declare function promptLinter(): Promise<string | null>;
11
12
  export declare function promptIconLibrary(): Promise<string | null>;
12
13
  /**
13
14
  * Consent for anonymous usage telemetry. Deliberately does NOT call
@@ -1 +1 @@
1
- {"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AAyBA,0EAA0E;AAC1E,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAWhE;AAED,sFAAsF;AACtF,wBAAsB,wBAAwB,CAC5C,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC,OAAO,CAAC,CAUlB;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAUnE;AAED,wBAAsB,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAa7D;AAED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAchE;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CA2BtE;AAED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgBhE"}
1
+ {"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AA0BA,0EAA0E;AAC1E,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAWhE;AAED,sFAAsF;AACtF,wBAAsB,wBAAwB,CAC5C,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC,OAAO,CAAC,CAUlB;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAUnE;AAED,wBAAsB,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAa7D;AAED,wBAAsB,YAAY,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAc3D;AAED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAchE;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CA2BtE;AAED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgBhE"}
package/dist/prompts.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import prompts from 'prompts';
2
2
  import chalk from 'chalk';
3
- import { TEMPLATES, DEFAULT_PROJECT_NAME, PROMPTS as PROMPT_MESSAGES, MESSAGES, ICON_LIBRARIES, BULMA_FLAVORS, } from './constants.js';
3
+ import { TEMPLATES, DEFAULT_PROJECT_NAME, PROMPTS as PROMPT_MESSAGES, MESSAGES, ICON_LIBRARIES, BULMA_FLAVORS, LINTERS, } from './constants.js';
4
4
  import { validateProjectDirectory } from './validators.js';
5
5
  import { displayError } from './display.js';
6
6
  /**
@@ -64,6 +64,20 @@ export async function promptTemplate() {
64
64
  });
65
65
  return response.template || null;
66
66
  }
67
+ export async function promptLinter() {
68
+ ensureInteractive();
69
+ const response = await prompts({
70
+ type: 'select',
71
+ name: 'linter',
72
+ message: PROMPT_MESSAGES.SELECT_LINTER,
73
+ choices: LINTERS.map(linter => ({
74
+ title: linter.color(`${linter.display} - ${linter.description}`),
75
+ value: linter.name,
76
+ })),
77
+ initial: 0, // Default to 'oxlint'
78
+ });
79
+ return response.linter || null;
80
+ }
67
81
  export async function promptIconLibrary() {
68
82
  ensureInteractive();
69
83
  const response = await prompts({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-bestax",
3
- "version": "4.2.18",
3
+ "version": "4.3.0",
4
4
  "description": "Create a new bestax-bulma project",
5
5
  "type": "module",
6
6
  "bin": {
@@ -34,26 +34,26 @@
34
34
  "url": "https://github.com/allxsmith/bestax/issues"
35
35
  },
36
36
  "dependencies": {
37
- "chalk": "^6.0.0",
37
+ "chalk": "^6.0.1",
38
38
  "commander": "^15.0.0",
39
39
  "figures": "^6.1.0",
40
- "fs-extra": "^11.4.0",
40
+ "fs-extra": "^11.4.1",
41
41
  "prompts": "^2.4.2",
42
42
  "react": "^18.0.0 || ^19.0.0",
43
43
  "react-dom": "^18.0.0 || ^19.0.0",
44
- "@allxsmith/bestax-bulma": "^5.37.0"
44
+ "@allxsmith/bestax-bulma": "^5.49.1"
45
45
  },
46
46
  "devDependencies": {
47
47
  "@jest/globals": "^30.0.0",
48
- "@playwright/test": "^1.63.0",
48
+ "@playwright/test": "^1.64.1",
49
49
  "@types/fs-extra": "^11.0.4",
50
50
  "@types/jest": "^30.0.0",
51
- "@types/node": "^26.6.4",
51
+ "@types/node": "^26.6.5",
52
52
  "@types/prompts": "^2.4.9",
53
53
  "@types/wait-on": "^5.3.4",
54
54
  "jest": "^30.2.0",
55
55
  "semver": "^7.8.5",
56
- "ts-jest": "^29.4.14",
56
+ "ts-jest": "^29.4.15",
57
57
  "typescript": "^6.0.3",
58
58
  "wait-on": "^9.5.1"
59
59
  },
@@ -78,5 +78,5 @@
78
78
  "clean": "rimraf dist",
79
79
  "release": "npx semantic-release"
80
80
  },
81
- "readme": "# create-bestax\n\n[![npm version](https://img.shields.io/npm/v/create-bestax.svg)](https://www.npmjs.com/package/create-bestax)\n[![npm downloads](https://img.shields.io/npm/dm/create-bestax.svg)](https://www.npmjs.com/package/create-bestax)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Socket Badge](https://badge.socket.dev/npm/package/create-bestax/latest)](https://socket.dev/npm/package/create-bestax/overview)\n[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/allxsmith/bestax/badge)](https://scorecard.dev/viewer/?uri=github.com/allxsmith/bestax)\n[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/14361/badge)](https://www.bestpractices.dev/projects/14361)\n[![npm provenance](https://img.shields.io/badge/npm-provenance-3fb950.svg)](https://www.npmjs.com/package/create-bestax#provenance)\n[![Security policy](https://img.shields.io/badge/security-policy-blue.svg)](https://github.com/allxsmith/bestax/blob/main/SECURITY.md)\n\nThe scaffolder for [`@allxsmith/bestax-bulma`](https://www.npmjs.com/package/@allxsmith/bestax-bulma) — spin up a Vite app pre-wired for the **Bulma v1** React component library in one command. Picks your framework (JS or TypeScript), CSS flavor, and icon library, and can drop in the bestax **AI skills** so an agent like Claude Code knows the library from the first prompt.\n\nPart of the [bestax monorepo](https://github.com/allxsmith/bestax) — see also [`@allxsmith/bestax-bulma`](https://www.npmjs.com/package/@allxsmith/bestax-bulma) for the components themselves.\n\n## Requirements\n\n- **Node.js 22 or newer.** Node 18 and 20 are both past end-of-life. On an older runtime the\n CLI exits immediately with an explicit upgrade message.\n- npm 10 or newer (ships with Node 22), or yarn/pnpm.\n\n## Usage\n\n### Quick Start\n\n```bash\n# Using npm\nnpm create bestax@latest my-app\n\n# Using npx\nnpx create-bestax@latest my-app\n```\n\n### Interactive Mode\n\nRunning without arguments will prompt you for all options:\n\n```bash\nnpm create bestax@latest\n```\n\nYou'll be asked to:\n\n1. Enter a project name or path (and, if that directory already holds files, confirm removing\n them; nothing is removed until you have answered the last question)\n2. Select a framework (Vite or Vite + TypeScript)\n3. Choose a Bulma CSS flavor (the `--bulma` options below)\n4. Select an icon library (Font Awesome, Material Icons, etc.)\n5. Choose whether to install the bestax AI skills into `.claude/skills/`\n\nEvery prompt has a flag equivalent, so the whole flow is scriptable and CI-friendly — see the options below.\n\n### Command Line Options\n\n```bash\nnpm create bestax@latest [project-directory] -- [options]\n```\n\nPut the options after `--`. npm needs it to pass them on at all, and so does bun, whose\n`bun create` rejects short flags such as `-t` and `-y` itself. pnpm and Yarn 1 accept the options\nwith or without it.\n\n**Arguments:**\n\n- `[project-directory]` - Optional project directory: a name such as `my-app`, or a path,\n absolute or relative, such as `../apps/my-app`, whose missing folders are created. Its last\n part is the project name, held to the same rules as a bare name. It becomes the page title as\n typed, and the `package.json` name lower-cased the way `npm init` does and without a leading\n `_`, since npm rejects both. A path ending in `.` or `..` is refused, like a bare `.` or `..`,\n so name the directory you want.\n\n**Options:**\n\n- `-t, --template <template>` - Template to use: `vite` or `vite-ts` \n **Default:** `vite`\n- `-b, --bulma <flavor>` - Bulma CSS flavor:\n - `complete` - Full Bulma CSS with all components and helpers\n - `prefixed` - Prefixed version for compatibility\n - `no-helpers` - Without helper classes\n - `no-helpers-prefixed` - Prefixed without helpers\n - `no-dark-mode` - Without dark mode support \n **Default:** `complete`\n- `-i, --icon <library>` - Icon library:\n - `none` - No icon library\n - `fontawesome` - Font Awesome\n - `mdi` - Material Design Icons\n - `ionicons` - Ionicons\n - `material-icons` - Material Icons\n - `material-symbols` - Material Symbols \n **Default:** `none`\n- `--skills` / `--no-skills` - Install (or skip) the bestax AI skills into\n `.claude/skills/` (with a `CLAUDE.md`) so a Claude Code session picks them up\n automatically. When omitted you're prompted; with `-y` the default is to install.\n- `-y, --yes` - Skip prompts and use defaults or provided options. \n When used, the following defaults are selected unless overridden by flags:\n - Template: `vite`\n - Bulma flavor: `complete`\n - Icon library: `none`\n - AI skills: installed (pass `--no-skills` to opt out)\n\n `-y` never deletes files. If the target directory is not empty, the run stops, lists what is\n there, and changes nothing; add `--overwrite` to empty it first. Earlier versions emptied the\n directory under `-y` alone, so a script that relied on that now needs `--overwrite`.\n\n- `--overwrite` - Remove existing files if the target directory is not empty, without asking.\n Every other flag is checked first, so a typo stops the run before anything is deleted. It\n never empties the current directory or a folder that holds it, however the path is written.\n\n**Example:**\n\n```bash\n# Create a TypeScript project with Font Awesome icons\nnpm create bestax@latest my-app -- -t vite-ts -i fontawesome -b complete\n\n# Use all defaults (skip prompts)\nnpm create bestax@latest my-app -- -y\n\n# Scaffold into a path; the package is named my-app\nnpm create bestax@latest ../apps/my-app -- -y\n\n# The same with bun\nbun create bestax@latest my-app -- -y\n```\n\n## Templates\n\nAvailable templates:\n\n- `vite` - Vite + JavaScript\n- `vite-ts` - Vite + TypeScript\n\nEach template includes:\n\n- Pre-configured bestax-bulma integration (React 19)\n- The latest `@allxsmith/bestax-bulma`, which ships Bulma v1 automatically\n- Icon library support (Font Awesome, Material Design, etc.)\n- Sample components demonstrating library usage\n- Development and build scripts\n\n## For AI Tools\n\nScaffolding for an AI agent? Pass `--skills` (or accept the prompt) to drop the bestax\nAgent Skills and a `CLAUDE.md` into the new project so Claude Code, Cursor, or Copilot\nbuild the bestax way from the start. The library also ships LLM-optimized docs:\n\n- [LLMs guide](https://bestax.io/docs/guides/llms) — using bestax with Claude Code, Cursor, Copilot\n- [Agent Skills](https://bestax.io/docs/skills/intro) — teach your agent the bestax conventions\n- [llms.txt](https://bestax.io/llms.txt) (curated index) · [llms-full.txt](https://bestax.io/llms-full.txt) (full docs)\n\n## Development\n\n```bash\nnpm run build # Build the CLI\nnpm run dev # Watch mode\nnpm test # Run tests\nnpm run lint # Lint CLI source code\nnpm run typecheck # Type check CLI source code\n```\n\n**Note on Templates:** Template files in `templates/` are excluded from linting. They should be manually validated by scaffolding a test project and running lint/build there before releasing.\n\n## Hardened by default\n\nA scaffolder runs with write access to your filesystem and picks your starting dependencies, so how it is built and published matters:\n\n- **Signed provenance** — every release carries a sigstore attestation linking the tarball to the exact commit and CI run that built it. Check the **Provenance** section on the [npm page](https://www.npmjs.com/package/create-bestax#provenance), or run `npm audit signatures`.\n- **npm OIDC trusted publishing** — short-lived, per-run credentials; no long-lived `NPM_TOKEN` exists to be stolen. Release commits and tags are GPG-signed.\n- **Socket.dev scans every PR** for malware, install scripts, obfuscated code, and privilege escalation before it can reach `main`.\n- **Dependencies are a deliberate act** — install scripts are blocked unless individually allow-listed, freshly published versions are refused for 3 days, and CI installs only what the reviewed lockfile resolves.\n- **Every GitHub Action is pinned to a full commit SHA**, so a compromised action release can't roll silently into a build of this CLI.\n- **CodeQL, Dependency Review, and Dependabot** run continuously, alongside a high-severity `pnpm audit` gate.\n- **Layered AI review before merge** — [CodeRabbit](https://coderabbit.ai) plus an independent adversarial Claude review (a different model from the one writing AI-authored changes), on top of required green CI, an approving review, and a human merge.\n\nFull detail: [`SECURITY.md`](https://github.com/allxsmith/bestax/blob/main/SECURITY.md) · [Security guide](https://bestax.io/docs/guides/security)\n\n## Telemetry\n\n`create-bestax` can send one **anonymous** usage event after a successful\nscaffold — only if you opt in when asked (once, at the end of a scaffold). The\nevent is just the choices you made — template, Bulma flavor, icon library,\nskills, package manager — plus the CLI version, Node major version, and OS\nplatform; never names, IPs, machine IDs, paths, or file contents, and no\nidentifier exists that could link two events together. Opt out any time with\n`--no-telemetry`, `BESTAX_TELEMETRY=0`, or `DO_NOT_TRACK=1`.\n\nFull disclosure of every field and control: [Telemetry guide](https://bestax.io/docs/guides/telemetry)\n\n## Publishing\n\nThis package uses semantic-release with scope-based rules. Only commits with `feat(create-bestax)` or `fix(create-bestax)` will trigger releases.\n"
81
+ "readme": "# create-bestax\n\n[![npm version](https://img.shields.io/npm/v/create-bestax.svg)](https://www.npmjs.com/package/create-bestax)\n[![npm downloads](https://img.shields.io/npm/dm/create-bestax.svg)](https://www.npmjs.com/package/create-bestax)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Socket Badge](https://badge.socket.dev/npm/package/create-bestax/latest)](https://socket.dev/npm/package/create-bestax/overview)\n[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/allxsmith/bestax/badge)](https://scorecard.dev/viewer/?uri=github.com/allxsmith/bestax)\n[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/14361/badge)](https://www.bestpractices.dev/projects/14361)\n[![npm provenance](https://img.shields.io/badge/npm-provenance-3fb950.svg)](https://www.npmjs.com/package/create-bestax#provenance)\n[![Security policy](https://img.shields.io/badge/security-policy-blue.svg)](https://github.com/allxsmith/bestax/blob/main/SECURITY.md)\n\nThe scaffolder for [`@allxsmith/bestax-bulma`](https://www.npmjs.com/package/@allxsmith/bestax-bulma) — spin up a Vite app pre-wired for the **Bulma v1** React component library in one command. Picks your framework (JS or TypeScript), CSS flavor, and icon library, and can drop in the bestax **AI skills** so an agent like Claude Code knows the library from the first prompt.\n\nPart of the [bestax monorepo](https://github.com/allxsmith/bestax) — see also [`@allxsmith/bestax-bulma`](https://www.npmjs.com/package/@allxsmith/bestax-bulma) for the components themselves.\n\n## Requirements\n\n- **Node.js 22 or newer.** Node 18 and 20 are both past end-of-life. On an older runtime the\n CLI exits immediately with an explicit upgrade message.\n- npm 10 or newer (ships with Node 22), or yarn/pnpm.\n\n## Usage\n\n### Quick Start\n\n```bash\n# Using npm\nnpm create bestax@latest my-app\n\n# Using npx\nnpx create-bestax@latest my-app\n```\n\n### Interactive Mode\n\nRunning without arguments will prompt you for all options:\n\n```bash\nnpm create bestax@latest\n```\n\nYou'll be asked to:\n\n1. Enter a project name or path (and, if that directory already holds files, confirm removing\n them; nothing is removed until you have answered the last question)\n2. Select a framework (Vite or Vite + TypeScript)\n3. Choose a linter (Oxlint or ESLint)\n4. Choose a Bulma CSS flavor (the `--bulma` options below)\n5. Select an icon library (Font Awesome, Material Icons, etc.)\n6. Choose whether to install the bestax AI skills into `.claude/skills/`\n\nEvery prompt has a flag equivalent, so the whole flow is scriptable and CI-friendly — see the options below.\n\n### Command Line Options\n\n```bash\nnpm create bestax@latest [project-directory] -- [options]\n```\n\nPut the options after `--`. npm needs it to pass them on at all, and so does bun, whose\n`bun create` rejects short flags such as `-t` and `-y` itself. pnpm and Yarn 1 accept the options\nwith or without it.\n\n**Arguments:**\n\n- `[project-directory]` - Optional project directory: a name such as `my-app`, or a path,\n absolute or relative, such as `../apps/my-app`, whose missing folders are created. Its last\n part is the project name, held to the same rules as a bare name. It becomes the page title as\n typed, and the `package.json` name lower-cased the way `npm init` does and without a leading\n `_`, since npm rejects both. A path ending in `.` or `..` is refused, like a bare `.` or `..`,\n so name the directory you want.\n\n**Options:**\n\n- `-t, --template <template>` - Template to use: `vite` or `vite-ts` \n **Default:** `vite`\n- `--lint <linter>` - Linter:\n - `oxlint` - Oxlint, as `npm create vite` sets up. `vite-ts` gets TypeScript 7.\n - `eslint` - ESLint with typescript-eslint, as `npm create vite -- --eslint` sets up.\n typescript-eslint doesn't support TypeScript 7 yet, so `vite-ts` stays on TypeScript 6. \n **Default:** `oxlint`, which a run without a terminal also gets when it leaves `--lint` out\n- `-b, --bulma <flavor>` - Bulma CSS flavor:\n - `complete` - Full Bulma CSS with all components and helpers\n - `prefixed` - Prefixed version for compatibility\n - `no-helpers` - Without helper classes\n - `no-helpers-prefixed` - Prefixed without helpers\n - `no-dark-mode` - Without dark mode support \n **Default:** `complete`\n- `-i, --icon <library>` - Icon library:\n - `none` - No icon library\n - `fontawesome` - Font Awesome\n - `mdi` - Material Design Icons\n - `ionicons` - Ionicons\n - `material-icons` - Material Icons\n - `material-symbols` - Material Symbols \n **Default:** `none`\n- `--skills` / `--no-skills` - Install (or skip) the bestax AI skills into\n `.claude/skills/` (with a `CLAUDE.md`) so a Claude Code session picks them up\n automatically. When omitted you're prompted; with `-y` the default is to install.\n- `-y, --yes` - Skip prompts and use defaults or provided options. \n When used, the following defaults are selected unless overridden by flags:\n - Template: `vite`\n - Linter: `oxlint`\n - Bulma flavor: `complete`\n - Icon library: `none`\n - AI skills: installed (pass `--no-skills` to opt out)\n\n `-y` never deletes files. If the target directory is not empty, the run stops, lists what is\n there, and changes nothing; add `--overwrite` to empty it first. Earlier versions emptied the\n directory under `-y` alone, so a script that relied on that now needs `--overwrite`.\n\n- `--overwrite` - Remove existing files if the target directory is not empty, without asking.\n Every other flag is checked first, so a typo stops the run before anything is deleted. It\n never empties the current directory or a folder that holds it, however the path is written.\n\n**Example:**\n\n```bash\n# Create a TypeScript project with Font Awesome icons\nnpm create bestax@latest my-app -- -t vite-ts -i fontawesome -b complete\n\n# The same, linted with ESLint (and on TypeScript 6)\nnpm create bestax@latest my-app -- -t vite-ts -i fontawesome --lint eslint\n\n# Use all defaults (skip prompts)\nnpm create bestax@latest my-app -- -y\n\n# Scaffold into a path; the package is named my-app\nnpm create bestax@latest ../apps/my-app -- -y\n\n# The same with bun\nbun create bestax@latest my-app -- -y\n```\n\n## Templates\n\nAvailable templates:\n\n- `vite` - Vite + JavaScript\n- `vite-ts` - Vite + TypeScript\n\nEach template includes:\n\n- Pre-configured bestax-bulma integration (React 19)\n- The latest `@allxsmith/bestax-bulma`, which ships Bulma v1 automatically\n- Icon library support (Font Awesome, Material Design, etc.)\n- Sample components demonstrating library usage\n- Development and build scripts\n\n## For AI Tools\n\nScaffolding for an AI agent? Pass `--skills` (or accept the prompt) to drop the bestax\nAgent Skills and a `CLAUDE.md` into the new project so Claude Code, Cursor, or Copilot\nbuild the bestax way from the start. The library also ships LLM-optimized docs:\n\n- [LLMs guide](https://bestax.io/docs/guides/llms) — using bestax with Claude Code, Cursor, Copilot\n- [Agent Skills](https://bestax.io/docs/skills/intro) — teach your agent the bestax conventions\n- [llms.txt](https://bestax.io/llms.txt) (curated index) · [llms-full.txt](https://bestax.io/llms-full.txt) (full docs)\n\n## Development\n\n```bash\nnpm run build # Build the CLI\nnpm run dev # Watch mode\nnpm test # Run tests\nnpm run lint # Lint CLI source code\nnpm run typecheck # Type check CLI source code\n```\n\n**Note on Templates:** Template files in `templates/` are excluded from linting. They should be manually validated by scaffolding a test project and running lint/build there before releasing.\n\n## Hardened by default\n\nA scaffolder runs with write access to your filesystem and picks your starting dependencies, so how it is built and published matters:\n\n- **Signed provenance** — every release carries a sigstore attestation linking the tarball to the exact commit and CI run that built it. Check the **Provenance** section on the [npm page](https://www.npmjs.com/package/create-bestax#provenance), or run `npm audit signatures`.\n- **npm OIDC trusted publishing** — short-lived, per-run credentials; no long-lived `NPM_TOKEN` exists to be stolen. Release commits and tags are GPG-signed.\n- **Socket.dev scans every PR** for malware, install scripts, obfuscated code, and privilege escalation before it can reach `main`.\n- **Dependencies are a deliberate act** — install scripts are blocked unless individually allow-listed, freshly published versions are refused for 3 days, and CI installs only what the reviewed lockfile resolves.\n- **Every GitHub Action is pinned to a full commit SHA**, so a compromised action release can't roll silently into a build of this CLI.\n- **CodeQL, Dependency Review, and Dependabot** run continuously, alongside a high-severity `pnpm audit` gate.\n- **Layered AI review before merge** — [CodeRabbit](https://coderabbit.ai) plus an independent adversarial Claude review (a different model from the one writing AI-authored changes), on top of required green CI, an approving review, and a human merge.\n\nFull detail: [`SECURITY.md`](https://github.com/allxsmith/bestax/blob/main/SECURITY.md) · [Security guide](https://bestax.io/docs/guides/security)\n\n## Telemetry\n\n`create-bestax` can send one **anonymous** usage event after a successful\nscaffold — only if you opt in when asked (once, at the end of a scaffold). The\nevent is just the choices you made — template, Bulma flavor, icon library,\nskills, package manager — plus the CLI version, Node major version, and OS\nplatform; never names, IPs, machine IDs, paths, or file contents, and no\nidentifier exists that could link two events together. Opt out any time with\n`--no-telemetry`, `BESTAX_TELEMETRY=0`, or `DO_NOT_TRACK=1`.\n\nFull disclosure of every field and control: [Telemetry guide](https://bestax.io/docs/guides/telemetry)\n\n## Publishing\n\nThis package uses semantic-release with scope-based rules. Only commits with `feat(create-bestax)` or `fix(create-bestax)` will trigger releases.\n"
82
82
  }