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