create-krispya 0.5.1 → 0.5.2

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.
@@ -256,6 +256,16 @@ function generatePackageJson(params) {
256
256
  const majorVersion = pnpmVersion.split(".")[0];
257
257
  engines.pnpm = `>=${majorVersion}.0.0`;
258
258
  packageJson.packageManager = `pnpm@${pnpmVersion}`;
259
+ } else if (packageManager === "yarn") {
260
+ const yarnVersion = options.yarnVersion ?? "4.6.0";
261
+ const majorVersion = yarnVersion.split(".")[0];
262
+ engines.yarn = `>=${majorVersion}.0.0`;
263
+ packageJson.packageManager = `yarn@${yarnVersion}`;
264
+ } else if (packageManager === "npm") {
265
+ const npmVersion = options.npmVersion ?? "11.0.0";
266
+ const majorVersion = npmVersion.split(".")[0];
267
+ engines.npm = `>=${majorVersion}.0.0`;
268
+ packageJson.packageManager = `npm@${npmVersion}`;
259
269
  }
260
270
  if (options.nodeVersion) {
261
271
  const majorVersion = options.nodeVersion.split(".")[0];
@@ -649,154 +659,128 @@ function generateViteConfig(params) {
649
659
  return { type: "text", content: viteConfigContent };
650
660
  }
651
661
 
652
- function generateMonorepo(params) {
653
- const { name, linter, formatter, packageManager, pnpmVersion, pnpmManageVersions, nodeVersion } = params;
654
- const files = {};
655
- const isPnpm = packageManager === "pnpm";
656
- const devDependencies = {};
657
- if (linter === "oxlint") {
658
- devDependencies["oxlint"] = "^1.36.0";
659
- } else if (linter === "eslint") {
660
- devDependencies["eslint"] = "^9.17.0";
661
- } else if (linter === "biome") {
662
- devDependencies["@biomejs/biome"] = "^1.9.4";
663
- }
664
- if (formatter === "oxfmt") {
665
- devDependencies["oxfmt"] = "^0.21.0";
666
- } else if (formatter === "prettier") {
667
- devDependencies["prettier"] = "^3.4.2";
668
- }
669
- const rootPackageJson = {
670
- name: "root",
671
- version: "0.0.0",
672
- private: true,
673
- type: "module",
674
- scripts: {
675
- dev: "pnpm --filter './apps/*' run dev",
676
- build: "pnpm --filter './packages/*' run build && pnpm --filter './apps/*' run build",
677
- test: "pnpm -r run test",
678
- lint: linter === "oxlint" ? "oxlint ." : linter === "biome" ? "biome check ." : "eslint .",
679
- format: formatter === "oxfmt" ? "oxfmt ." : formatter === "biome" ? "biome format . --write" : "prettier --write ."
680
- },
681
- devDependencies
682
- };
683
- const engines = {};
684
- if (isPnpm && pnpmVersion) {
685
- const majorVersion = pnpmVersion.split(".")[0];
686
- engines.pnpm = `>=${majorVersion}.0.0`;
687
- rootPackageJson.packageManager = `pnpm@${pnpmVersion}`;
688
- }
689
- if (nodeVersion) {
690
- const majorVersion = nodeVersion.split(".")[0];
691
- engines.node = `>=${majorVersion}.0.0`;
692
- }
693
- if (Object.keys(engines).length > 0) {
694
- rootPackageJson.engines = engines;
695
- }
696
- files["package.json"] = {
697
- type: "text",
698
- content: JSON.stringify(rootPackageJson, null, 2)
699
- };
700
- if (isPnpm) {
701
- const workspaceLines = [];
702
- if (pnpmManageVersions) {
703
- workspaceLines.push("manage-package-manager-versions: true", "");
662
+ function generateAiFiles(files, params) {
663
+ const { name, packageManager, linter, formatter, aiFiles } = params;
664
+ const content = getAiInstructionsContent({
665
+ name,
666
+ packageManager,
667
+ linter,
668
+ formatter
669
+ });
670
+ for (const fileChoice of aiFiles) {
671
+ switch (fileChoice) {
672
+ case "cursor-rules":
673
+ files[".cursor/rules"] = { type: "text", content };
674
+ break;
675
+ case "agents-md":
676
+ files["AGENTS.md"] = { type: "text", content };
677
+ break;
678
+ case "claude-md":
679
+ files["CLAUDE.md"] = { type: "text", content };
680
+ break;
681
+ case "copilot-md":
682
+ files[".github/copilot-instructions.md"] = { type: "text", content };
683
+ break;
704
684
  }
705
- workspaceLines.push("packages:", ' - ".config/*"', ' - "apps/*"', ' - "packages/*"', "");
706
- workspaceLines.push("onlyBuiltDependencies:", " - esbuild");
707
- files["pnpm-workspace.yaml"] = {
708
- type: "text",
709
- content: workspaceLines.join("\n")
710
- };
711
685
  }
712
- generateTypescriptConfigPackage(files);
713
- if (linter === "oxlint") {
714
- generateOxlintConfigPackage(files);
715
- } else if (linter === "eslint") {
716
- files["eslint.config.js"] = {
717
- type: "text",
718
- content: `export default [
719
- // Add your ESLint rules here
720
- ];
721
- `
722
- };
723
- } else if (linter === "biome") {
724
- const biomeConfig = {
725
- $schema: "https://biomejs.dev/schemas/1.9.4/schema.json",
726
- vcs: {
727
- enabled: true,
728
- clientKind: "git",
729
- useIgnoreFile: true
730
- },
731
- linter: {
732
- enabled: true,
733
- rules: {
734
- recommended: true
735
- }
736
- },
737
- formatter: {
738
- enabled: formatter === "biome"
739
- }
740
- };
741
- files["biome.json"] = {
742
- type: "text",
743
- content: JSON.stringify(biomeConfig, null, 2)
744
- };
686
+ }
687
+ function getConfigPackagesDescription(linter, formatter) {
688
+ const packages = ["`@config/typescript`"];
689
+ if (linter !== "biome") {
690
+ packages.push(`\`@config/${linter}\``);
745
691
  }
746
- if (formatter === "oxfmt") {
747
- generateOxfmtConfigPackage(files);
748
- } else if (formatter === "prettier") {
749
- const prettierConfig = {
750
- semi: true,
751
- singleQuote: false,
752
- trailingComma: "es5",
753
- printWidth: 100
754
- };
755
- files[".prettierrc.json"] = {
756
- type: "text",
757
- content: JSON.stringify(prettierConfig, null, 2)
758
- };
692
+ if (formatter !== "biome" && formatter !== linter) {
693
+ packages.push(`\`@config/${formatter}\``);
759
694
  }
760
- files[".gitignore"] = {
761
- type: "text",
762
- content: ["node_modules", "dist", "*.tsbuildinfo", ".DS_Store"].join("\n")
763
- };
764
- files[".gitattributes"] = {
765
- type: "text",
766
- content: `* text=auto eol=lf
767
- *.{cmd,[cC][mM][dD]} text eol=crlf
768
- *.{bat,[bB][aA][tT]} text eol=crlf
769
- `
770
- };
771
- generateVscodeFiles(files, linter, formatter);
772
- files["README.md"] = {
773
- type: "text",
774
- content: `# ${name}
695
+ let description = `- \`.config/\`: shared config packages (${packages.join(", ")})`;
696
+ if (linter === "biome" || formatter === "biome") {
697
+ description += "\n- `biome.json`: Biome configuration (root level)";
698
+ }
699
+ return description;
700
+ }
701
+ function getAiInstructionsContent(params) {
702
+ const { name, packageManager, linter, formatter } = params;
703
+ const configDescription = getConfigPackagesDescription(linter, formatter);
704
+ return `# ${name}
775
705
 
776
- This monorepo workspace was generated with create-krispya.
706
+ This is a pnpm monorepo workspace generated with \`create-krispya\`.
777
707
 
778
- ## Structure
708
+ ## Most important rule (package creation)
779
709
 
780
- - \`apps/\` - Applications
781
- - \`packages/\` - Shared packages and libraries
782
- - \`.config/\` - Shared configuration packages
710
+ If you need a new app/package for any reason, **ALWAYS** create it with \`create-krispya\` (do not hand-create folders/package.json).
783
711
 
784
- ## Development Commands
712
+ ### Non-interactive (preferred for agents)
785
713
 
786
- - \`${packageManager} install\` to install all dependencies
787
- - \`${packageManager} run dev\` to run all applications in development mode
788
- - \`${packageManager} run build\` to build all packages and applications
789
- - \`${packageManager} run test\` to run tests across the workspace
790
- - \`${packageManager} run lint\` to lint all code
791
- - \`${packageManager} run format\` to format all code
714
+ \`\`\`bash
715
+ ${packageManager} create krispya <name> --workspace [options]
716
+ \`\`\`
792
717
 
793
- ## Adding Packages
718
+ - The package directory will be \`apps/<name>\` (apps) or \`packages/<name>\` (libraries), unless \`--dir\` is provided.
719
+ - Package names default to \`@${name}/<name>\` but you can pass any name (scoped or unscoped).
794
720
 
795
- To add a new package to this workspace, run create-krispya from this directory and it will detect the monorepo.
796
- `
797
- };
798
- return { files };
721
+ ### Workspace maintenance (non-interactive)
722
+
723
+ \`\`\`bash
724
+ ${packageManager} create krispya --check # validate workspace
725
+ ${packageManager} create krispya --fix --linter ${linter} --formatter ${formatter} # fix missing .config packages
726
+ \`\`\`
727
+
728
+ For non-interactive \`--fix\`, you MUST provide both \`--linter\` and \`--formatter\` flags.
729
+
730
+ ## Package creation options (CLI truth)
731
+
732
+ | Option | Values | Notes |
733
+ |--------|--------|-------|
734
+ | \`--type\` | app, library | default: app |
735
+ | \`--template\` | vanilla, vanilla-js, react, react-js, r3f, r3f-js | default: vanilla |
736
+ | \`--dir\` | any directory | requires \`--workspace\`; default: \`apps/\` or \`packages/\` |
737
+ | \`--bundler\` | unbuild, tsdown | libraries only; default: unbuild |
738
+
739
+ ### R3F flags (r3f templates only)
740
+
741
+ \`--drei\` \`--handle\` \`--leva\` \`--postprocessing\` \`--rapier\` \`--xr\` \`--uikit\` \`--offscreen\` \`--zustand\` \`--koota\` \`--triplex\` \`--viverse\`
742
+
743
+ ### Examples
744
+
745
+ \`\`\`bash
746
+ # React library (@${name}/ui) in packages/ui
747
+ ${packageManager} create krispya ui --workspace --type library --template react
748
+
749
+ # R3F app with physics + controls (@${name}/game) in apps/game
750
+ ${packageManager} create krispya game --workspace --type app --template r3f --drei --rapier --leva
751
+
752
+ # App in a custom directory
753
+ ${packageManager} create krispya demo --workspace --type app --template react --dir examples
754
+ \`\`\`
755
+
756
+ ## After creating a package
757
+
758
+ \`\`\`bash
759
+ ${packageManager} install
760
+ \`\`\`
761
+
762
+ - Use \`"workspace:*"\` for internal deps (e.g. \`"@${name}/ui": "workspace:*"\`).
763
+
764
+ ## Workspace commands
765
+
766
+ \`\`\`bash
767
+ ${packageManager} install # Install all dependencies
768
+ ${packageManager} run dev # Run all apps in dev mode
769
+ ${packageManager} run build # Build packages then apps
770
+ ${packageManager} run test # Run all tests
771
+ ${packageManager} run lint # Lint with ${linter}
772
+ ${packageManager} run format # Format with ${formatter}
773
+ \`\`\`
774
+
775
+ ## Structure + conventions
776
+
777
+ - \`apps/\`: applications (\`--type app\`)
778
+ - \`packages/\`: libraries (\`--type library\`)
779
+ ${configDescription}
780
+ - TS configs extend \`@config/typescript/*\` (base/app/node/react)
781
+ `;
799
782
  }
783
+
800
784
  function generateTypescriptConfigPackage(files) {
801
785
  const basePath = ".config/typescript";
802
786
  files[`${basePath}/package.json`] = {
@@ -993,48 +977,175 @@ oxlint -c node_modules/@config/oxlint/base.json
993
977
  )
994
978
  };
995
979
  }
996
- function generateVscodeFiles(files, linter, formatter) {
997
- const recommendations = [];
998
- const settings = {};
999
- if (linter === "oxlint") {
1000
- recommendations.push("oxc.oxc-vscode");
1001
- settings["oxc.enable"] = true;
1002
- settings["eslint.enable"] = false;
1003
- settings["biome.enabled"] = false;
1004
- } else if (linter === "eslint") {
1005
- recommendations.push("dbaeumer.vscode-eslint");
1006
- settings["eslint.enable"] = true;
1007
- settings["oxc.enable"] = false;
1008
- settings["biome.enabled"] = false;
1009
- } else if (linter === "biome") {
1010
- recommendations.push("biomejs.biome");
1011
- settings["biome.enabled"] = true;
1012
- settings["eslint.enable"] = false;
1013
- settings["oxc.enable"] = false;
980
+ function generateEslintConfigPackage(files) {
981
+ const basePath = ".config/eslint";
982
+ files[`${basePath}/package.json`] = {
983
+ type: "text",
984
+ content: JSON.stringify(
985
+ {
986
+ name: "@config/eslint",
987
+ version: "0.1.0",
988
+ private: true,
989
+ type: "module",
990
+ exports: {
991
+ "./base": "./base.js",
992
+ "./react": "./react.js"
993
+ },
994
+ files: ["base.js", "react.js"],
995
+ devDependencies: {
996
+ "@eslint/js": "^9.17.0",
997
+ "typescript-eslint": "^8.18.0"
998
+ }
999
+ },
1000
+ null,
1001
+ 2
1002
+ )
1003
+ };
1004
+ files[`${basePath}/README.md`] = {
1005
+ type: "text",
1006
+ content: `# \`@config/eslint\`
1007
+
1008
+ Shared ESLint configurations for the monorepo.
1009
+
1010
+ ## Usage
1011
+
1012
+ In your package's \`eslint.config.js\`:
1013
+
1014
+ \`\`\`js
1015
+ import base from "@config/eslint/base";
1016
+
1017
+ export default [...base];
1018
+ \`\`\`
1019
+
1020
+ Or for React projects:
1021
+
1022
+ \`\`\`js
1023
+ import react from "@config/eslint/react";
1024
+
1025
+ export default [...react];
1026
+ \`\`\`
1027
+
1028
+ ## Available Configs
1029
+
1030
+ - \`base\` - Base linting rules for TypeScript projects
1031
+ - \`react\` - Extends base with React-specific rules
1032
+ `
1033
+ };
1034
+ files[`${basePath}/base.js`] = {
1035
+ type: "text",
1036
+ content: `import js from "@eslint/js";
1037
+ import tseslint from "typescript-eslint";
1038
+
1039
+ export default tseslint.config(
1040
+ js.configs.recommended,
1041
+ ...tseslint.configs.recommended,
1042
+ {
1043
+ rules: {
1044
+ "@typescript-eslint/no-unused-vars": [
1045
+ "error",
1046
+ {
1047
+ argsIgnorePattern: "^_",
1048
+ varsIgnorePattern: "^_",
1049
+ caughtErrorsIgnorePattern: "^_",
1050
+ },
1051
+ ],
1052
+ },
1053
+ },
1054
+ {
1055
+ ignores: ["dist/**", "node_modules/**"],
1014
1056
  }
1015
- if (formatter === "oxfmt") {
1016
- if (!recommendations.includes("oxc.oxc-vscode")) {
1017
- recommendations.push("oxc.oxc-vscode");
1018
- }
1019
- settings["editor.defaultFormatter"] = "oxc.oxc-vscode";
1020
- settings["[json]"] = { "editor.defaultFormatter": "vscode.json-language-features" };
1021
- settings["[jsonc]"] = { "editor.defaultFormatter": "vscode.json-language-features" };
1022
- } else if (formatter === "prettier") {
1023
- recommendations.push("esbenp.prettier-vscode");
1024
- settings["editor.defaultFormatter"] = "esbenp.prettier-vscode";
1025
- } else if (formatter === "biome") {
1026
- if (!recommendations.includes("biomejs.biome")) {
1027
- recommendations.push("biomejs.biome");
1028
- }
1029
- settings["editor.defaultFormatter"] = "biomejs.biome";
1057
+ );
1058
+ `
1059
+ };
1060
+ files[`${basePath}/react.js`] = {
1061
+ type: "text",
1062
+ content: `import js from "@eslint/js";
1063
+ import tseslint from "typescript-eslint";
1064
+
1065
+ export default tseslint.config(
1066
+ js.configs.recommended,
1067
+ ...tseslint.configs.recommended,
1068
+ {
1069
+ rules: {
1070
+ "@typescript-eslint/no-unused-vars": [
1071
+ "error",
1072
+ {
1073
+ argsIgnorePattern: "^_",
1074
+ varsIgnorePattern: "^_",
1075
+ caughtErrorsIgnorePattern: "^_",
1076
+ },
1077
+ ],
1078
+ },
1079
+ },
1080
+ {
1081
+ ignores: ["dist/**", "node_modules/**"],
1030
1082
  }
1031
- files[".vscode/extensions.json"] = {
1083
+ );
1084
+ `
1085
+ };
1086
+ }
1087
+ function generatePrettierConfigPackage(files) {
1088
+ const basePath = ".config/prettier";
1089
+ files[`${basePath}/package.json`] = {
1032
1090
  type: "text",
1033
- content: JSON.stringify({ recommendations }, null, 2)
1091
+ content: JSON.stringify(
1092
+ {
1093
+ name: "@config/prettier",
1094
+ version: "0.1.0",
1095
+ private: true,
1096
+ type: "module",
1097
+ exports: {
1098
+ ".": "./base.json"
1099
+ },
1100
+ files: ["base.json"]
1101
+ },
1102
+ null,
1103
+ 2
1104
+ )
1034
1105
  };
1035
- files[".vscode/settings.json"] = {
1106
+ files[`${basePath}/README.md`] = {
1036
1107
  type: "text",
1037
- content: JSON.stringify(settings, null, " ")
1108
+ content: `# \`@config/prettier\`
1109
+
1110
+ Shared Prettier configuration for the monorepo.
1111
+
1112
+ ## Usage
1113
+
1114
+ In your package's \`package.json\`:
1115
+
1116
+ \`\`\`json
1117
+ {
1118
+ "prettier": "@config/prettier"
1119
+ }
1120
+ \`\`\`
1121
+
1122
+ Or in \`.prettierrc.json\`:
1123
+
1124
+ \`\`\`json
1125
+ "@config/prettier"
1126
+ \`\`\`
1127
+
1128
+ ## Available Configs
1129
+
1130
+ - Default export - Base formatter settings
1131
+ `
1132
+ };
1133
+ files[`${basePath}/base.json`] = {
1134
+ type: "text",
1135
+ content: JSON.stringify(
1136
+ {
1137
+ printWidth: defaultFormatterConfig.printWidth,
1138
+ tabWidth: defaultFormatterConfig.tabWidth,
1139
+ useTabs: defaultFormatterConfig.useTabs,
1140
+ semi: defaultFormatterConfig.semi,
1141
+ singleQuote: defaultFormatterConfig.singleQuote,
1142
+ trailingComma: defaultFormatterConfig.trailingComma,
1143
+ bracketSpacing: defaultFormatterConfig.bracketSpacing,
1144
+ arrowParens: defaultFormatterConfig.arrowParens
1145
+ },
1146
+ null,
1147
+ 2
1148
+ )
1038
1149
  };
1039
1150
  }
1040
1151
  function generateOxfmtConfigPackage(files) {
@@ -1090,9 +1201,221 @@ oxfmt -c node_modules/@config/oxfmt/base.json --write .
1090
1201
  };
1091
1202
  }
1092
1203
 
1204
+ function generateMonorepo(params) {
1205
+ const {
1206
+ name,
1207
+ linter,
1208
+ formatter,
1209
+ packageManager,
1210
+ pnpmVersion,
1211
+ pnpmManageVersions,
1212
+ nodeVersion,
1213
+ aiFiles
1214
+ } = params;
1215
+ const files = {};
1216
+ const isPnpm = packageManager === "pnpm";
1217
+ const devDependencies = {};
1218
+ if (linter === "oxlint") {
1219
+ devDependencies["oxlint"] = "^1.36.0";
1220
+ } else if (linter === "eslint") {
1221
+ devDependencies["eslint"] = "^9.17.0";
1222
+ } else if (linter === "biome") {
1223
+ devDependencies["@biomejs/biome"] = "^1.9.4";
1224
+ }
1225
+ if (formatter === "oxfmt") {
1226
+ devDependencies["oxfmt"] = "^0.21.0";
1227
+ } else if (formatter === "prettier") {
1228
+ devDependencies["prettier"] = "^3.4.2";
1229
+ }
1230
+ const rootPackageJson = {
1231
+ name: "root",
1232
+ version: "0.0.0",
1233
+ private: true,
1234
+ type: "module",
1235
+ scripts: {
1236
+ dev: "pnpm --filter './apps/*' run dev",
1237
+ build: "pnpm --filter './packages/*' run build && pnpm --filter './apps/*' run build",
1238
+ test: "pnpm -r run test",
1239
+ lint: linter === "oxlint" ? "oxlint ." : linter === "biome" ? "biome check ." : "eslint .",
1240
+ format: formatter === "oxfmt" ? "oxfmt ." : formatter === "biome" ? "biome format . --write" : "prettier --write ."
1241
+ },
1242
+ devDependencies
1243
+ };
1244
+ const engines = {};
1245
+ if (isPnpm && pnpmVersion) {
1246
+ const majorVersion = pnpmVersion.split(".")[0];
1247
+ engines.pnpm = `>=${majorVersion}.0.0`;
1248
+ rootPackageJson.packageManager = `pnpm@${pnpmVersion}`;
1249
+ }
1250
+ if (nodeVersion) {
1251
+ const majorVersion = nodeVersion.split(".")[0];
1252
+ engines.node = `>=${majorVersion}.0.0`;
1253
+ }
1254
+ if (Object.keys(engines).length > 0) {
1255
+ rootPackageJson.engines = engines;
1256
+ }
1257
+ files["package.json"] = {
1258
+ type: "text",
1259
+ content: JSON.stringify(rootPackageJson, null, 2)
1260
+ };
1261
+ if (isPnpm) {
1262
+ const workspaceLines = [];
1263
+ if (pnpmManageVersions) {
1264
+ workspaceLines.push("manage-package-manager-versions: true", "");
1265
+ }
1266
+ workspaceLines.push(
1267
+ "packages:",
1268
+ ' - ".config/*"',
1269
+ ' - "apps/*"',
1270
+ ' - "packages/*"',
1271
+ ""
1272
+ );
1273
+ workspaceLines.push("onlyBuiltDependencies:", " - esbuild");
1274
+ files["pnpm-workspace.yaml"] = {
1275
+ type: "text",
1276
+ content: workspaceLines.join("\n")
1277
+ };
1278
+ }
1279
+ generateTypescriptConfigPackage(files);
1280
+ if (linter === "oxlint") {
1281
+ generateOxlintConfigPackage(files);
1282
+ } else if (linter === "eslint") {
1283
+ generateEslintConfigPackage(files);
1284
+ } else if (linter === "biome") {
1285
+ const biomeConfig = {
1286
+ $schema: "https://biomejs.dev/schemas/1.9.4/schema.json",
1287
+ vcs: {
1288
+ enabled: true,
1289
+ clientKind: "git",
1290
+ useIgnoreFile: true
1291
+ },
1292
+ linter: {
1293
+ enabled: true,
1294
+ rules: {
1295
+ recommended: true
1296
+ }
1297
+ },
1298
+ formatter: {
1299
+ enabled: formatter === "biome"
1300
+ }
1301
+ };
1302
+ files["biome.json"] = {
1303
+ type: "text",
1304
+ content: JSON.stringify(biomeConfig, null, 2)
1305
+ };
1306
+ }
1307
+ if (formatter === "oxfmt") {
1308
+ generateOxfmtConfigPackage(files);
1309
+ } else if (formatter === "prettier") {
1310
+ generatePrettierConfigPackage(files);
1311
+ }
1312
+ files[".gitignore"] = {
1313
+ type: "text",
1314
+ content: ["node_modules", "dist", "*.tsbuildinfo", ".DS_Store"].join("\n")
1315
+ };
1316
+ files[".gitattributes"] = {
1317
+ type: "text",
1318
+ content: `* text=auto eol=lf
1319
+ *.{cmd,[cC][mM][dD]} text eol=crlf
1320
+ *.{bat,[bB][aA][tT]} text eol=crlf
1321
+ `
1322
+ };
1323
+ generateVscodeFiles(files, linter, formatter);
1324
+ files["README.md"] = {
1325
+ type: "text",
1326
+ content: `# ${name}
1327
+
1328
+ This monorepo workspace was generated with create-krispya.
1329
+
1330
+ ## Structure
1331
+
1332
+ - \`apps/\` - Applications
1333
+ - \`packages/\` - Shared packages and libraries
1334
+ - \`.config/\` - Shared configuration packages
1335
+
1336
+ ## Development Commands
1337
+
1338
+ - \`${packageManager} install\` to install all dependencies
1339
+ - \`${packageManager} run dev\` to run all applications in development mode
1340
+ - \`${packageManager} run build\` to build all packages and applications
1341
+ - \`${packageManager} run test\` to run tests across the workspace
1342
+ - \`${packageManager} run lint\` to lint all code
1343
+ - \`${packageManager} run format\` to format all code
1344
+
1345
+ ## Adding Packages
1346
+
1347
+ To add a new package to this workspace, run create-krispya from this directory and it will detect the monorepo.
1348
+ `
1349
+ };
1350
+ if (aiFiles && aiFiles.length > 0) {
1351
+ generateAiFiles(files, {
1352
+ name,
1353
+ packageManager,
1354
+ linter,
1355
+ formatter,
1356
+ aiFiles
1357
+ });
1358
+ }
1359
+ return { files };
1360
+ }
1361
+ function generateVscodeFiles(files, linter, formatter) {
1362
+ const recommendations = [];
1363
+ const settings = {};
1364
+ if (linter === "oxlint") {
1365
+ recommendations.push("oxc.oxc-vscode");
1366
+ settings["oxc.enable"] = true;
1367
+ settings["eslint.enable"] = false;
1368
+ settings["biome.enabled"] = false;
1369
+ } else if (linter === "eslint") {
1370
+ recommendations.push("dbaeumer.vscode-eslint");
1371
+ settings["eslint.enable"] = true;
1372
+ settings["oxc.enable"] = false;
1373
+ settings["biome.enabled"] = false;
1374
+ } else if (linter === "biome") {
1375
+ recommendations.push("biomejs.biome");
1376
+ settings["biome.enabled"] = true;
1377
+ settings["eslint.enable"] = false;
1378
+ settings["oxc.enable"] = false;
1379
+ }
1380
+ if (formatter === "oxfmt") {
1381
+ if (!recommendations.includes("oxc.oxc-vscode")) {
1382
+ recommendations.push("oxc.oxc-vscode");
1383
+ }
1384
+ settings["editor.defaultFormatter"] = "oxc.oxc-vscode";
1385
+ settings["[json]"] = {
1386
+ "editor.defaultFormatter": "vscode.json-language-features"
1387
+ };
1388
+ settings["[jsonc]"] = {
1389
+ "editor.defaultFormatter": "vscode.json-language-features"
1390
+ };
1391
+ } else if (formatter === "prettier") {
1392
+ recommendations.push("esbenp.prettier-vscode");
1393
+ settings["editor.defaultFormatter"] = "esbenp.prettier-vscode";
1394
+ } else if (formatter === "biome") {
1395
+ if (!recommendations.includes("biomejs.biome")) {
1396
+ recommendations.push("biomejs.biome");
1397
+ }
1398
+ settings["editor.defaultFormatter"] = "biomejs.biome";
1399
+ }
1400
+ files[".vscode/extensions.json"] = {
1401
+ type: "text",
1402
+ content: JSON.stringify({ recommendations }, null, 2)
1403
+ };
1404
+ files[".vscode/settings.json"] = {
1405
+ type: "text",
1406
+ content: JSON.stringify(settings, null, " ")
1407
+ };
1408
+ }
1409
+
1093
1410
  const monorepo = {
1094
1411
  __proto__: null,
1095
- generateMonorepo: generateMonorepo
1412
+ generateEslintConfigPackage: generateEslintConfigPackage,
1413
+ generateMonorepo: generateMonorepo,
1414
+ generateOxfmtConfigPackage: generateOxfmtConfigPackage,
1415
+ generateOxlintConfigPackage: generateOxlintConfigPackage,
1416
+ generatePrettierConfigPackage: generatePrettierConfigPackage,
1417
+ generateTypescriptConfigPackage: generateTypescriptConfigPackage,
1418
+ generateVscodeFiles: generateVscodeFiles
1096
1419
  };
1097
1420
 
1098
1421
  function toBiomeLevel(level) {
@@ -2021,7 +2344,9 @@ function merge(target, modification) {
2021
2344
 
2022
2345
  async function getLatestNpmVersion(packageName, fallback) {
2023
2346
  try {
2024
- const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`);
2347
+ const response = await fetch(
2348
+ `https://registry.npmjs.org/${packageName}/latest`
2349
+ );
2025
2350
  const data = await response.json();
2026
2351
  return data.version;
2027
2352
  } catch {
@@ -2031,6 +2356,12 @@ async function getLatestNpmVersion(packageName, fallback) {
2031
2356
  async function getLatestPnpmVersion() {
2032
2357
  return getLatestNpmVersion("pnpm", "10.11.0");
2033
2358
  }
2359
+ async function getLatestYarnVersion() {
2360
+ return getLatestNpmVersion("yarn", "4.6.0");
2361
+ }
2362
+ async function getLatestNpmCliVersion() {
2363
+ return getLatestNpmVersion("npm", "11.0.0");
2364
+ }
2034
2365
  async function getLatestNodeVersion() {
2035
2366
  try {
2036
2367
  const response = await fetch("https://nodejs.org/dist/index.json");
@@ -2044,6 +2375,70 @@ async function getLatestNodeVersion() {
2044
2375
  return "22.0.0";
2045
2376
  }
2046
2377
  }
2378
+ function validateNameSegment(segment, label) {
2379
+ if (!segment.length) {
2380
+ return `${label} is required`;
2381
+ }
2382
+ if (!/^[a-z0-9-]+$/.test(segment)) {
2383
+ return `${label} must be lowercase and contain only letters, numbers, and hyphens`;
2384
+ }
2385
+ if (segment.startsWith("-") || segment.endsWith("-")) {
2386
+ return `${label} cannot start or end with a hyphen`;
2387
+ }
2388
+ if (segment.includes("--")) {
2389
+ return `${label} cannot contain consecutive hyphens`;
2390
+ }
2391
+ return void 0;
2392
+ }
2393
+ function validatePackageName(name) {
2394
+ if (!name.length) {
2395
+ return "Package name is required";
2396
+ }
2397
+ if (name.includes("..") || name.includes("\\")) {
2398
+ return "Package name cannot contain path traversal sequences";
2399
+ }
2400
+ if (name.startsWith("@")) {
2401
+ const slashIndex = name.indexOf("/");
2402
+ if (slashIndex === -1) {
2403
+ return "Scoped package name must include a package name after the scope (e.g., @scope/name)";
2404
+ }
2405
+ if (name.indexOf("/", slashIndex + 1) !== -1) {
2406
+ return "Package name can only have one slash for scoped packages";
2407
+ }
2408
+ const scope = name.slice(1, slashIndex);
2409
+ const packageName = name.slice(slashIndex + 1);
2410
+ const scopeError = validateNameSegment(scope, "Scope");
2411
+ if (scopeError) return scopeError;
2412
+ const nameError = validateNameSegment(packageName, "Package name");
2413
+ if (nameError) return nameError;
2414
+ return void 0;
2415
+ }
2416
+ if (name.includes("/")) {
2417
+ return "Unscoped package name cannot contain slashes. Use @scope/name format for scoped packages";
2418
+ }
2419
+ return validateNameSegment(name, "Package name");
2420
+ }
2421
+ function parseWorkspaceYamlContent(content) {
2422
+ const directories = [];
2423
+ let inPackagesSection = false;
2424
+ for (const line of content.split("\n")) {
2425
+ const trimmed = line.trim();
2426
+ if (trimmed === "packages:") {
2427
+ inPackagesSection = true;
2428
+ continue;
2429
+ }
2430
+ if (inPackagesSection && trimmed && !line.startsWith(" ") && !line.startsWith(" ") && !trimmed.startsWith("-")) {
2431
+ break;
2432
+ }
2433
+ if (inPackagesSection && trimmed.startsWith("-")) {
2434
+ const entry = trimmed.slice(1).trim().replace(/^["']|["']$/g, "").replace(/^\.\//, "").replace(/\/\*.*$/, "");
2435
+ if (entry && !entry.startsWith(".")) {
2436
+ directories.push(entry);
2437
+ }
2438
+ }
2439
+ }
2440
+ return directories;
2441
+ }
2047
2442
  function generateRandomName() {
2048
2443
  const adjectives = [
2049
2444
  "red",
@@ -2350,11 +2745,22 @@ function generate(options) {
2350
2745
  }
2351
2746
 
2352
2747
  exports.generate = generate;
2748
+ exports.generateAiFiles = generateAiFiles;
2749
+ exports.generateEslintConfigPackage = generateEslintConfigPackage;
2353
2750
  exports.generateMonorepo = generateMonorepo;
2751
+ exports.generateOxfmtConfigPackage = generateOxfmtConfigPackage;
2752
+ exports.generateOxlintConfigPackage = generateOxlintConfigPackage;
2753
+ exports.generatePrettierConfigPackage = generatePrettierConfigPackage;
2354
2754
  exports.generateRandomName = generateRandomName;
2755
+ exports.generateTypescriptConfigPackage = generateTypescriptConfigPackage;
2756
+ exports.generateVscodeFiles = generateVscodeFiles;
2355
2757
  exports.getBaseTemplate = getBaseTemplate;
2356
2758
  exports.getLanguageFromTemplate = getLanguageFromTemplate;
2357
2759
  exports.getLatestNodeVersion = getLatestNodeVersion;
2760
+ exports.getLatestNpmCliVersion = getLatestNpmCliVersion;
2358
2761
  exports.getLatestNpmVersion = getLatestNpmVersion;
2359
2762
  exports.getLatestPnpmVersion = getLatestPnpmVersion;
2763
+ exports.getLatestYarnVersion = getLatestYarnVersion;
2360
2764
  exports.monorepo = monorepo;
2765
+ exports.parseWorkspaceYamlContent = parseWorkspaceYamlContent;
2766
+ exports.validatePackageName = validatePackageName;