create-krispya 0.10.0 → 0.11.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.
package/dist/cli.cjs CHANGED
@@ -9,7 +9,7 @@ const node_module = require('node:module');
9
9
  const node_path = require('node:path');
10
10
  const node_process = require('node:process');
11
11
  const undici = require('undici');
12
- const workspace = require('./shared/create-krispya.DTHeUlq4.cjs');
12
+ const workspace = require('./shared/create-krispya.8KaGuRpu.cjs');
13
13
  const Conf = require('conf');
14
14
  const promises = require('fs/promises');
15
15
  const fs = require('fs');
@@ -20,15 +20,15 @@ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentS
20
20
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
21
21
 
22
22
  function _interopNamespaceCompat(e) {
23
- if (e && typeof e === 'object' && 'default' in e) return e;
24
- const n = Object.create(null);
25
- if (e) {
26
- for (const k in e) {
27
- n[k] = e[k];
28
- }
23
+ if (e && typeof e === 'object' && 'default' in e) return e;
24
+ const n = Object.create(null);
25
+ if (e) {
26
+ for (const k in e) {
27
+ n[k] = e[k];
29
28
  }
30
- n.default = e;
31
- return n;
29
+ }
30
+ n.default = e;
31
+ return n;
32
32
  }
33
33
 
34
34
  const p__namespace = /*#__PURE__*/_interopNamespaceCompat(p);
@@ -90,9 +90,7 @@ function formatConfigSummary(options, inherited) {
90
90
  const testing = options.testing ?? (projectType === "library" ? "vitest" : "none");
91
91
  lines.push(formatRow("Testing", testing));
92
92
  if (!inherited) {
93
- lines.push(
94
- formatRow("Editor config", options.ide === "none" ? "generic" : "generic + vscode")
95
- );
93
+ lines.push(formatRow("Editor config", options.ide === "none" ? "generic" : "generic + vscode"));
96
94
  const configStrategy = options.configStrategy ?? "stealth";
97
95
  lines.push(formatRow("Config strategy", configStrategy));
98
96
  }
@@ -556,7 +554,7 @@ async function promptForOptions(name, presets) {
556
554
  let projectName = name;
557
555
  if (!projectName) {
558
556
  const nameResult = await p__namespace.text({
559
- message: "What is your project named?",
557
+ message: "Project name:",
560
558
  placeholder: workspace.generateRandomName(),
561
559
  defaultValue: workspace.generateRandomName(),
562
560
  validate: (value) => {
@@ -570,7 +568,7 @@ async function promptForOptions(name, presets) {
570
568
  projectName = nameResult;
571
569
  }
572
570
  const projectType = await p__namespace.select({
573
- message: "Project type",
571
+ message: "Project type:",
574
572
  options: [
575
573
  { value: "app", label: "Application" },
576
574
  { value: "library", label: "Library" },
@@ -599,11 +597,11 @@ function presetsToInheritedSettings(presets) {
599
597
  }
600
598
  async function promptForPackageOptions(projectName, projectType, inheritedSettings, presets) {
601
599
  const templateSelection = await p__namespace.select({
602
- message: "Select a template",
600
+ message: "Select a template:",
603
601
  options: [
604
- { value: "vanilla", label: "Vanilla" },
605
- { value: "react", label: "React", hint: "experimental" },
606
- { value: "r3f", label: "React Three Fiber", hint: "experimental" }
602
+ { value: "vanilla", label: color__default.yellow("Vanilla") },
603
+ { value: "react", label: color__default.cyan("React"), hint: "experimental" },
604
+ { value: "r3f", label: color__default.magenta("React Three Fiber"), hint: "experimental" }
607
605
  ],
608
606
  initialValue: presets?.template ? workspace.getBaseTemplate(presets.template) : "vanilla"
609
607
  });
@@ -837,10 +835,7 @@ async function getWorkspacePackages(monorepoRoot) {
837
835
  for (const entry of entries) {
838
836
  if (!entry.isDirectory()) continue;
839
837
  try {
840
- const content = await promises$1.readFile(
841
- node_path.join(packagesDir, entry.name, "package.json"),
842
- "utf-8"
843
- );
838
+ const content = await promises$1.readFile(node_path.join(packagesDir, entry.name, "package.json"), "utf-8");
844
839
  const pkg = JSON.parse(content);
845
840
  if (pkg.name) names.push(pkg.name);
846
841
  } catch {
@@ -1117,9 +1112,7 @@ async function handleFixCommand(options) {
1117
1112
  spinner.start("Fixing workspace...");
1118
1113
  try {
1119
1114
  const files = {};
1120
- const tsConfigExists = await fileExists$1(
1121
- node_path.join(monorepoRoot, ".config/typescript/package.json")
1122
- );
1115
+ const tsConfigExists = await fileExists$1(node_path.join(monorepoRoot, ".config/typescript/package.json"));
1123
1116
  if (!tsConfigExists) {
1124
1117
  workspace.renderTypescriptConfigPackage(files);
1125
1118
  }
@@ -1127,9 +1120,7 @@ async function handleFixCommand(options) {
1127
1120
  const oxlintExists = await fileExists$1(node_path.join(monorepoRoot, ".config/oxlint/package.json"));
1128
1121
  if (!oxlintExists) workspace.renderOxlintConfigPackage(files);
1129
1122
  } else if (linter === "eslint") {
1130
- const eslintPkgExists = await fileExists$1(
1131
- node_path.join(monorepoRoot, ".config/eslint/package.json")
1132
- );
1123
+ const eslintPkgExists = await fileExists$1(node_path.join(monorepoRoot, ".config/eslint/package.json"));
1133
1124
  if (!eslintPkgExists) {
1134
1125
  if (existingConfigs.eslintConfigPath) {
1135
1126
  await migrateEslintConfig(monorepoRoot, files);
@@ -1142,9 +1133,7 @@ async function handleFixCommand(options) {
1142
1133
  const oxfmtExists = await fileExists$1(node_path.join(monorepoRoot, ".config/oxfmt/package.json"));
1143
1134
  if (!oxfmtExists) workspace.renderOxfmtConfigPackage(files);
1144
1135
  } else if (formatter === "prettier") {
1145
- const prettierPkgExists = await fileExists$1(
1146
- node_path.join(monorepoRoot, ".config/prettier/package.json")
1147
- );
1136
+ const prettierPkgExists = await fileExists$1(node_path.join(monorepoRoot, ".config/prettier/package.json"));
1148
1137
  if (!prettierPkgExists) {
1149
1138
  if (existingConfigs.prettierConfigPath) {
1150
1139
  await migratePrettierConfig(monorepoRoot, files);
@@ -1206,9 +1195,7 @@ async function handleFixCommand(options) {
1206
1195
  console.log(color__default.dim(` Generated ${pkgName}`));
1207
1196
  }
1208
1197
  const vscodeSettingsExists = await fileExists$1(node_path.join(monorepoRoot, ".vscode/settings.json"));
1209
- const vscodeExtensionsExists = await fileExists$1(
1210
- node_path.join(monorepoRoot, ".vscode/extensions.json")
1211
- );
1198
+ const vscodeExtensionsExists = await fileExists$1(node_path.join(monorepoRoot, ".vscode/extensions.json"));
1212
1199
  const vscodeExists = vscodeSettingsExists && vscodeExtensionsExists;
1213
1200
  if (!vscodeExists) {
1214
1201
  let addVscode = false;
@@ -1264,10 +1251,31 @@ async function handleFixCommand(options) {
1264
1251
  }
1265
1252
  }
1266
1253
 
1254
+ function detectViteTemplate(pkg) {
1255
+ if (!hasPackage(pkg, "vite")) return void 0;
1256
+ if (hasPackage(pkg, "@react-three/fiber")) return "r3f";
1257
+ if (hasPackage(pkg, "react") || hasPackage(pkg, "@vitejs/plugin-react")) return "react";
1258
+ return "vanilla";
1259
+ }
1260
+ function renderExpectedViteConfig(template) {
1261
+ const isReact = template === "react" || template === "r3f";
1262
+ const codeSnippets = isReact ? { "vite-config-import": ["import react from '@vitejs/plugin-react';"] } : {};
1263
+ const viteConfig = {
1264
+ base: "./"
1265
+ };
1266
+ if (isReact) {
1267
+ viteConfig.plugins = ["$raw:react()"];
1268
+ }
1269
+ if (template === "r3f") {
1270
+ viteConfig.resolve = { dedupe: ["three"] };
1271
+ }
1272
+ return workspace.renderViteConfig({ viteConfig, codeSnippets });
1273
+ }
1267
1274
  async function detectCurrentConfig(root, isMonorepo = true) {
1268
1275
  let name = root.split(/[/\\]/).pop() ?? "workspace";
1269
1276
  let packageManager = "pnpm";
1270
1277
  let hasTypecheck = false;
1278
+ let viteTemplate;
1271
1279
  try {
1272
1280
  const pkgPath = path.join(root, "package.json");
1273
1281
  const content = await promises.readFile(pkgPath, "utf-8");
@@ -1279,6 +1287,7 @@ async function detectCurrentConfig(root, isMonorepo = true) {
1279
1287
  packageManager = pkgJson.packageManager.split("@")[0] ?? packageManager;
1280
1288
  }
1281
1289
  hasTypecheck = pkgJson.scripts?.typecheck != null;
1290
+ viteTemplate = detectViteTemplate(pkgJson);
1282
1291
  } catch {
1283
1292
  }
1284
1293
  const tooling = await workspace.detectTooling(root);
@@ -1290,7 +1299,8 @@ async function detectCurrentConfig(root, isMonorepo = true) {
1290
1299
  packageManager,
1291
1300
  isMonorepo,
1292
1301
  configStrategy,
1293
- hasTypecheck
1302
+ hasTypecheck,
1303
+ viteTemplate
1294
1304
  };
1295
1305
  }
1296
1306
  async function detectSinglePackageConfigStrategy(root) {
@@ -1354,6 +1364,9 @@ async function planExpectedFiles(config) {
1354
1364
  content: workspace.toPrettierIgnoreContent()
1355
1365
  };
1356
1366
  }
1367
+ if (!isMonorepo && config.viteTemplate != null) {
1368
+ rootConfig["vite.config.ts"] = renderExpectedViteConfig(config.viteTemplate);
1369
+ }
1357
1370
  if (linter === "biome" || formatter === "biome") {
1358
1371
  const biomeVersion = workspace.getResolvedPackageVersion(versions, "@biomejs/biome");
1359
1372
  const biomeConfig = {
@@ -1907,9 +1920,7 @@ async function collectUpdateCategories(projectRoot, config, isMonorepo) {
1907
1920
  category: "package-json",
1908
1921
  label: "package.json",
1909
1922
  changes: packageJsonScriptChanges,
1910
- hasUserModifications: packageJsonScriptChanges.some(
1911
- (change) => change.status === "modified"
1912
- )
1923
+ hasUserModifications: packageJsonScriptChanges.some((change) => change.status === "modified")
1913
1924
  });
1914
1925
  }
1915
1926
  const oxlintConfigChanges = await getOxlintConfigReplacementUpdates(projectRoot, config);
@@ -1928,9 +1939,7 @@ async function collectUpdateCategories(projectRoot, config, isMonorepo) {
1928
1939
  category: "workspace-config",
1929
1940
  label: "Workspace Config",
1930
1941
  changes: workspaceConfigChanges,
1931
- hasUserModifications: workspaceConfigChanges.some(
1932
- (change) => change.status === "modified"
1933
- )
1942
+ hasUserModifications: workspaceConfigChanges.some((change) => change.status === "modified")
1934
1943
  });
1935
1944
  }
1936
1945
  }
@@ -1970,9 +1979,7 @@ async function processUpdateCategory(category, projectRoot, options) {
1970
1979
  if (changesToApply.length > 0) {
1971
1980
  await applyUpdates(changesToApply, projectRoot);
1972
1981
  const addedCount = changesToApply.filter((change) => change.status === "added").length;
1973
- const updatedFilesCount = changesToApply.filter(
1974
- (change) => change.status === "modified"
1975
- ).length;
1982
+ const updatedFilesCount = changesToApply.filter((change) => change.status === "modified").length;
1976
1983
  const parts = [];
1977
1984
  if (addedCount > 0) parts.push(`added ${addedCount}`);
1978
1985
  if (updatedFilesCount > 0) parts.push(`updated ${updatedFilesCount}`);
@@ -2399,10 +2406,7 @@ async function main() {
2399
2406
  ).option(
2400
2407
  "--node-version <version>",
2401
2408
  'set Node.js version for engines.node field (default: "latest")'
2402
- ).option("--workspace", "Add package to current monorepo workspace (non-interactive)").option("--dir <directory>", "Target directory for --workspace (default: apps/ or packages/)").option("--clear-config", "Clear saved preferences").option("--config-path", "Print the path to the config file").option("--check", "Check if current directory is in a valid monorepo workspace").option("--fix", "Fix monorepo by generating missing .config packages").option("--update", "Update monorepo workspace to latest configuration").option("-y, --yes", "Non-interactive mode - accept all prompts").option(
2403
- "--path <directory>",
2404
- "Run in specified directory instead of current working directory"
2405
- ).action(async (name, options) => {
2409
+ ).option("--workspace", "Add package to current monorepo workspace (non-interactive)").option("--dir <directory>", "Target directory for --workspace (default: apps/ or packages/)").option("--clear-config", "Clear saved preferences").option("--config-path", "Print the path to the config file").option("--check", "Check if current directory is in a valid monorepo workspace").option("--fix", "Fix monorepo by generating missing .config packages").option("--update", "Update monorepo workspace to latest configuration").option("-y, --yes", "Non-interactive mode - accept all prompts").option("--path <directory>", "Run in specified directory instead of current working directory").action(async (name, options) => {
2406
2410
  if (options.path) {
2407
2411
  process.chdir(options.path);
2408
2412
  }
@@ -2464,9 +2468,7 @@ async function main() {
2464
2468
  }
2465
2469
  if (options.dir && !options.workspace) {
2466
2470
  console.error(color__default.red("Error:") + " --dir requires --workspace flag");
2467
- console.log(
2468
- color__default.dim(" Example: pnpm create krispya my-lib --workspace --dir examples")
2469
- );
2471
+ console.log(color__default.dim(" Example: pnpm create krispya my-lib --workspace --dir examples"));
2470
2472
  process.exit(1);
2471
2473
  }
2472
2474
  if (options.workspace) {
package/dist/cli.mjs CHANGED
@@ -7,7 +7,7 @@ import { createRequire } from 'node:module';
7
7
  import { resolve, join as join$1, dirname } from 'node:path';
8
8
  import { cwd } from 'node:process';
9
9
  import { fetch } from 'undici';
10
- import { q as getEngineName, a as getBaseTemplate, b as getLanguageFromTemplate, s as getPackageManagerName, g as generateRandomName, A as ALL_AI_PLATFORMS, t as AI_PLATFORM_LABELS, x as AI_PLATFORM_HINTS, d as detectTooling, y as parsePackageManager, z as parseEngine, p as parseWorkspaceYamlContent, B as renderTypescriptConfigPackage, C as renderOxlintConfigPackage, D as renderEslintConfigPackage, E as renderOxfmtConfigPackage, F as renderPrettierConfigPackage, G as resolveMonorepoRootPackageVersions, H as getResolvedPackageVersion, I as renderVscodeFiles, J as renderAiFiles, K as renderVscodeFiles$1, L as renderEditorConfig, M as renderGitignore, N as toPrettierIgnoreContent, O as mergePackageJsonScripts, P as packageJsonScripts, Q as resolveDefaultPackageJsonScripts, R as formatResolvedPackageVersion, S as renderOxlintConfig, k as planProject, r as resolveProjectPlanInput, v as validatePackageName, o as resolveWorkspacePlanInput, l as planWorkspace } from './shared/create-krispya.DKKVmsqH.mjs';
10
+ import { q as getEngineName, a as getBaseTemplate, b as getLanguageFromTemplate, s as getPackageManagerName, g as generateRandomName, A as ALL_AI_PLATFORMS, t as AI_PLATFORM_LABELS, x as AI_PLATFORM_HINTS, d as detectTooling, y as parsePackageManager, z as parseEngine, p as parseWorkspaceYamlContent, B as renderTypescriptConfigPackage, C as renderOxlintConfigPackage, D as renderEslintConfigPackage, E as renderOxfmtConfigPackage, F as renderPrettierConfigPackage, G as resolveMonorepoRootPackageVersions, H as getResolvedPackageVersion, I as renderVscodeFiles, J as renderAiFiles, K as renderVscodeFiles$1, L as renderEditorConfig, M as renderGitignore, N as toPrettierIgnoreContent, O as mergePackageJsonScripts, P as renderViteConfig, Q as packageJsonScripts, R as resolveDefaultPackageJsonScripts, S as formatResolvedPackageVersion, T as renderOxlintConfig, k as planProject, r as resolveProjectPlanInput, v as validatePackageName, o as resolveWorkspacePlanInput, l as planWorkspace } from './shared/create-krispya.DblF9gKc.mjs';
11
11
  import Conf from 'conf';
12
12
  import { access, constants, readFile as readFile$1, mkdir as mkdir$1, writeFile as writeFile$1 } from 'fs/promises';
13
13
  import { constants as constants$2 } from 'fs';
@@ -69,9 +69,7 @@ function formatConfigSummary(options, inherited) {
69
69
  const testing = options.testing ?? (projectType === "library" ? "vitest" : "none");
70
70
  lines.push(formatRow("Testing", testing));
71
71
  if (!inherited) {
72
- lines.push(
73
- formatRow("Editor config", options.ide === "none" ? "generic" : "generic + vscode")
74
- );
72
+ lines.push(formatRow("Editor config", options.ide === "none" ? "generic" : "generic + vscode"));
75
73
  const configStrategy = options.configStrategy ?? "stealth";
76
74
  lines.push(formatRow("Config strategy", configStrategy));
77
75
  }
@@ -535,7 +533,7 @@ async function promptForOptions(name, presets) {
535
533
  let projectName = name;
536
534
  if (!projectName) {
537
535
  const nameResult = await p.text({
538
- message: "What is your project named?",
536
+ message: "Project name:",
539
537
  placeholder: generateRandomName(),
540
538
  defaultValue: generateRandomName(),
541
539
  validate: (value) => {
@@ -549,7 +547,7 @@ async function promptForOptions(name, presets) {
549
547
  projectName = nameResult;
550
548
  }
551
549
  const projectType = await p.select({
552
- message: "Project type",
550
+ message: "Project type:",
553
551
  options: [
554
552
  { value: "app", label: "Application" },
555
553
  { value: "library", label: "Library" },
@@ -578,11 +576,11 @@ function presetsToInheritedSettings(presets) {
578
576
  }
579
577
  async function promptForPackageOptions(projectName, projectType, inheritedSettings, presets) {
580
578
  const templateSelection = await p.select({
581
- message: "Select a template",
579
+ message: "Select a template:",
582
580
  options: [
583
- { value: "vanilla", label: "Vanilla" },
584
- { value: "react", label: "React", hint: "experimental" },
585
- { value: "r3f", label: "React Three Fiber", hint: "experimental" }
581
+ { value: "vanilla", label: color.yellow("Vanilla") },
582
+ { value: "react", label: color.cyan("React"), hint: "experimental" },
583
+ { value: "r3f", label: color.magenta("React Three Fiber"), hint: "experimental" }
586
584
  ],
587
585
  initialValue: presets?.template ? getBaseTemplate(presets.template) : "vanilla"
588
586
  });
@@ -816,10 +814,7 @@ async function getWorkspacePackages(monorepoRoot) {
816
814
  for (const entry of entries) {
817
815
  if (!entry.isDirectory()) continue;
818
816
  try {
819
- const content = await readFile(
820
- join$1(packagesDir, entry.name, "package.json"),
821
- "utf-8"
822
- );
817
+ const content = await readFile(join$1(packagesDir, entry.name, "package.json"), "utf-8");
823
818
  const pkg = JSON.parse(content);
824
819
  if (pkg.name) names.push(pkg.name);
825
820
  } catch {
@@ -1096,9 +1091,7 @@ async function handleFixCommand(options) {
1096
1091
  spinner.start("Fixing workspace...");
1097
1092
  try {
1098
1093
  const files = {};
1099
- const tsConfigExists = await fileExists$1(
1100
- join$1(monorepoRoot, ".config/typescript/package.json")
1101
- );
1094
+ const tsConfigExists = await fileExists$1(join$1(monorepoRoot, ".config/typescript/package.json"));
1102
1095
  if (!tsConfigExists) {
1103
1096
  renderTypescriptConfigPackage(files);
1104
1097
  }
@@ -1106,9 +1099,7 @@ async function handleFixCommand(options) {
1106
1099
  const oxlintExists = await fileExists$1(join$1(monorepoRoot, ".config/oxlint/package.json"));
1107
1100
  if (!oxlintExists) renderOxlintConfigPackage(files);
1108
1101
  } else if (linter === "eslint") {
1109
- const eslintPkgExists = await fileExists$1(
1110
- join$1(monorepoRoot, ".config/eslint/package.json")
1111
- );
1102
+ const eslintPkgExists = await fileExists$1(join$1(monorepoRoot, ".config/eslint/package.json"));
1112
1103
  if (!eslintPkgExists) {
1113
1104
  if (existingConfigs.eslintConfigPath) {
1114
1105
  await migrateEslintConfig(monorepoRoot, files);
@@ -1121,9 +1112,7 @@ async function handleFixCommand(options) {
1121
1112
  const oxfmtExists = await fileExists$1(join$1(monorepoRoot, ".config/oxfmt/package.json"));
1122
1113
  if (!oxfmtExists) renderOxfmtConfigPackage(files);
1123
1114
  } else if (formatter === "prettier") {
1124
- const prettierPkgExists = await fileExists$1(
1125
- join$1(monorepoRoot, ".config/prettier/package.json")
1126
- );
1115
+ const prettierPkgExists = await fileExists$1(join$1(monorepoRoot, ".config/prettier/package.json"));
1127
1116
  if (!prettierPkgExists) {
1128
1117
  if (existingConfigs.prettierConfigPath) {
1129
1118
  await migratePrettierConfig(monorepoRoot, files);
@@ -1185,9 +1174,7 @@ async function handleFixCommand(options) {
1185
1174
  console.log(color.dim(` Generated ${pkgName}`));
1186
1175
  }
1187
1176
  const vscodeSettingsExists = await fileExists$1(join$1(monorepoRoot, ".vscode/settings.json"));
1188
- const vscodeExtensionsExists = await fileExists$1(
1189
- join$1(monorepoRoot, ".vscode/extensions.json")
1190
- );
1177
+ const vscodeExtensionsExists = await fileExists$1(join$1(monorepoRoot, ".vscode/extensions.json"));
1191
1178
  const vscodeExists = vscodeSettingsExists && vscodeExtensionsExists;
1192
1179
  if (!vscodeExists) {
1193
1180
  let addVscode = false;
@@ -1243,10 +1230,31 @@ async function handleFixCommand(options) {
1243
1230
  }
1244
1231
  }
1245
1232
 
1233
+ function detectViteTemplate(pkg) {
1234
+ if (!hasPackage(pkg, "vite")) return void 0;
1235
+ if (hasPackage(pkg, "@react-three/fiber")) return "r3f";
1236
+ if (hasPackage(pkg, "react") || hasPackage(pkg, "@vitejs/plugin-react")) return "react";
1237
+ return "vanilla";
1238
+ }
1239
+ function renderExpectedViteConfig(template) {
1240
+ const isReact = template === "react" || template === "r3f";
1241
+ const codeSnippets = isReact ? { "vite-config-import": ["import react from '@vitejs/plugin-react';"] } : {};
1242
+ const viteConfig = {
1243
+ base: "./"
1244
+ };
1245
+ if (isReact) {
1246
+ viteConfig.plugins = ["$raw:react()"];
1247
+ }
1248
+ if (template === "r3f") {
1249
+ viteConfig.resolve = { dedupe: ["three"] };
1250
+ }
1251
+ return renderViteConfig({ viteConfig, codeSnippets });
1252
+ }
1246
1253
  async function detectCurrentConfig(root, isMonorepo = true) {
1247
1254
  let name = root.split(/[/\\]/).pop() ?? "workspace";
1248
1255
  let packageManager = "pnpm";
1249
1256
  let hasTypecheck = false;
1257
+ let viteTemplate;
1250
1258
  try {
1251
1259
  const pkgPath = join(root, "package.json");
1252
1260
  const content = await readFile$1(pkgPath, "utf-8");
@@ -1258,6 +1266,7 @@ async function detectCurrentConfig(root, isMonorepo = true) {
1258
1266
  packageManager = pkgJson.packageManager.split("@")[0] ?? packageManager;
1259
1267
  }
1260
1268
  hasTypecheck = pkgJson.scripts?.typecheck != null;
1269
+ viteTemplate = detectViteTemplate(pkgJson);
1261
1270
  } catch {
1262
1271
  }
1263
1272
  const tooling = await detectTooling(root);
@@ -1269,7 +1278,8 @@ async function detectCurrentConfig(root, isMonorepo = true) {
1269
1278
  packageManager,
1270
1279
  isMonorepo,
1271
1280
  configStrategy,
1272
- hasTypecheck
1281
+ hasTypecheck,
1282
+ viteTemplate
1273
1283
  };
1274
1284
  }
1275
1285
  async function detectSinglePackageConfigStrategy(root) {
@@ -1333,6 +1343,9 @@ async function planExpectedFiles(config) {
1333
1343
  content: toPrettierIgnoreContent()
1334
1344
  };
1335
1345
  }
1346
+ if (!isMonorepo && config.viteTemplate != null) {
1347
+ rootConfig["vite.config.ts"] = renderExpectedViteConfig(config.viteTemplate);
1348
+ }
1336
1349
  if (linter === "biome" || formatter === "biome") {
1337
1350
  const biomeVersion = getResolvedPackageVersion(versions, "@biomejs/biome");
1338
1351
  const biomeConfig = {
@@ -1886,9 +1899,7 @@ async function collectUpdateCategories(projectRoot, config, isMonorepo) {
1886
1899
  category: "package-json",
1887
1900
  label: "package.json",
1888
1901
  changes: packageJsonScriptChanges,
1889
- hasUserModifications: packageJsonScriptChanges.some(
1890
- (change) => change.status === "modified"
1891
- )
1902
+ hasUserModifications: packageJsonScriptChanges.some((change) => change.status === "modified")
1892
1903
  });
1893
1904
  }
1894
1905
  const oxlintConfigChanges = await getOxlintConfigReplacementUpdates(projectRoot, config);
@@ -1907,9 +1918,7 @@ async function collectUpdateCategories(projectRoot, config, isMonorepo) {
1907
1918
  category: "workspace-config",
1908
1919
  label: "Workspace Config",
1909
1920
  changes: workspaceConfigChanges,
1910
- hasUserModifications: workspaceConfigChanges.some(
1911
- (change) => change.status === "modified"
1912
- )
1921
+ hasUserModifications: workspaceConfigChanges.some((change) => change.status === "modified")
1913
1922
  });
1914
1923
  }
1915
1924
  }
@@ -1949,9 +1958,7 @@ async function processUpdateCategory(category, projectRoot, options) {
1949
1958
  if (changesToApply.length > 0) {
1950
1959
  await applyUpdates(changesToApply, projectRoot);
1951
1960
  const addedCount = changesToApply.filter((change) => change.status === "added").length;
1952
- const updatedFilesCount = changesToApply.filter(
1953
- (change) => change.status === "modified"
1954
- ).length;
1961
+ const updatedFilesCount = changesToApply.filter((change) => change.status === "modified").length;
1955
1962
  const parts = [];
1956
1963
  if (addedCount > 0) parts.push(`added ${addedCount}`);
1957
1964
  if (updatedFilesCount > 0) parts.push(`updated ${updatedFilesCount}`);
@@ -2378,10 +2385,7 @@ async function main() {
2378
2385
  ).option(
2379
2386
  "--node-version <version>",
2380
2387
  'set Node.js version for engines.node field (default: "latest")'
2381
- ).option("--workspace", "Add package to current monorepo workspace (non-interactive)").option("--dir <directory>", "Target directory for --workspace (default: apps/ or packages/)").option("--clear-config", "Clear saved preferences").option("--config-path", "Print the path to the config file").option("--check", "Check if current directory is in a valid monorepo workspace").option("--fix", "Fix monorepo by generating missing .config packages").option("--update", "Update monorepo workspace to latest configuration").option("-y, --yes", "Non-interactive mode - accept all prompts").option(
2382
- "--path <directory>",
2383
- "Run in specified directory instead of current working directory"
2384
- ).action(async (name, options) => {
2388
+ ).option("--workspace", "Add package to current monorepo workspace (non-interactive)").option("--dir <directory>", "Target directory for --workspace (default: apps/ or packages/)").option("--clear-config", "Clear saved preferences").option("--config-path", "Print the path to the config file").option("--check", "Check if current directory is in a valid monorepo workspace").option("--fix", "Fix monorepo by generating missing .config packages").option("--update", "Update monorepo workspace to latest configuration").option("-y, --yes", "Non-interactive mode - accept all prompts").option("--path <directory>", "Run in specified directory instead of current working directory").action(async (name, options) => {
2385
2389
  if (options.path) {
2386
2390
  process.chdir(options.path);
2387
2391
  }
@@ -2443,9 +2447,7 @@ async function main() {
2443
2447
  }
2444
2448
  if (options.dir && !options.workspace) {
2445
2449
  console.error(color.red("Error:") + " --dir requires --workspace flag");
2446
- console.log(
2447
- color.dim(" Example: pnpm create krispya my-lib --workspace --dir examples")
2448
- );
2450
+ console.log(color.dim(" Example: pnpm create krispya my-lib --workspace --dir examples"));
2449
2451
  process.exit(1);
2450
2452
  }
2451
2453
  if (options.workspace) {
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const workspace = require('./shared/create-krispya.DTHeUlq4.cjs');
3
+ const workspace = require('./shared/create-krispya.8KaGuRpu.cjs');
4
4
  require('fs/promises');
5
5
  require('fs');
6
6
  require('path');
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export { d as detectTooling, g as generateRandomName, a as getBaseTemplate, b as getLanguageFromTemplate, c as getLatestNodeVersion, e as getLatestNpmCliVersion, f as getLatestNpmMajorVersion, h as getLatestNpmVersion, i as getLatestPnpmVersion, j as getLatestYarnVersion, m as merge, p as parseWorkspaceYamlContent, k as planProject, l as planWorkspace, n as projectPlanInputToOptions, r as resolveProjectPlanInput, o as resolveWorkspacePlanInput, u as unique, v as validatePackageName, w as workspacePlanInputToMonorepoParams } from './shared/create-krispya.DKKVmsqH.mjs';
1
+ export { d as detectTooling, g as generateRandomName, a as getBaseTemplate, b as getLanguageFromTemplate, c as getLatestNodeVersion, e as getLatestNpmCliVersion, f as getLatestNpmMajorVersion, h as getLatestNpmVersion, i as getLatestPnpmVersion, j as getLatestYarnVersion, m as merge, p as parseWorkspaceYamlContent, k as planProject, l as planWorkspace, n as projectPlanInputToOptions, r as resolveProjectPlanInput, o as resolveWorkspacePlanInput, u as unique, v as validatePackageName, w as workspacePlanInputToMonorepoParams } from './shared/create-krispya.DblF9gKc.mjs';
2
2
  import 'fs/promises';
3
3
  import 'fs';
4
4
  import 'path';
@@ -198,9 +198,7 @@ function merge(target, modification) {
198
198
  const targetLabel = JSON.stringify(target);
199
199
  const modificationLabel = JSON.stringify(modification);
200
200
  if (modification == null) {
201
- throw new Error(
202
- `Cannot merge "${modificationLabel}" modification into target "${targetLabel}"`
203
- );
201
+ throw new Error(`Cannot merge "${modificationLabel}" modification into target "${targetLabel}"`);
204
202
  }
205
203
  if (target == null) {
206
204
  return modification;
@@ -880,10 +878,7 @@ function renderTypescriptConfig(baseTemplateOrParams) {
880
878
  const tsConfig = {
881
879
  $schema: "https://json.schemastore.org/tsconfig",
882
880
  files: [],
883
- references: [
884
- { path: "./.config/tsconfig.app.json" },
885
- { path: "./.config/tsconfig.node.json" }
886
- ]
881
+ references: [{ path: "./.config/tsconfig.app.json" }, { path: "./.config/tsconfig.node.json" }]
887
882
  };
888
883
  files["tsconfig.json"] = {
889
884
  type: "text",
@@ -1147,10 +1142,7 @@ function renderPackageJson(params) {
1147
1142
  const allDevDependencies = { ...devDependencies };
1148
1143
  const engine = getEngineSpec(options.engine);
1149
1144
  if (getEngineName(engine) === "node" && engine.version) {
1150
- allDevDependencies["@types/node"] ??= formatNodeTypesVersion(
1151
- options.versions,
1152
- options.engine
1153
- );
1145
+ allDevDependencies["@types/node"] ??= formatNodeTypesVersion(options.versions, options.engine);
1154
1146
  }
1155
1147
  packageJson.scripts = resolvedScripts;
1156
1148
  packageJson.dependencies = sortKeys(allDependencies);
@@ -1251,13 +1243,14 @@ function renderReadme(params) {
1251
1243
  } else if (isReact) {
1252
1244
  architectureDesc = [
1253
1245
  `- \`src/app.${jsxExt}\` defines the main application component`,
1254
- `- \`src/index.${jsxExt}\` renders the React app into the DOM`,
1246
+ `- \`src/main.${jsxExt}\` renders the React app into the DOM`,
1255
1247
  `- \`tests/\` contains your test files`,
1256
1248
  `- Static assets can be placed in the \`public\` folder`
1257
1249
  ];
1258
1250
  } else {
1259
1251
  architectureDesc = [
1260
- `- \`app.${jsxExt}\` defines the main application component containing your 3D content`,
1252
+ `- \`src/app.${jsxExt}\` defines the main application component containing your 3D content`,
1253
+ `- \`src/main.${jsxExt}\` renders the React app into the DOM`,
1261
1254
  `- Modify the content inside the \`<Canvas>\` component to change what is visible on screen`,
1262
1255
  `- \`tests/\` contains your test files`,
1263
1256
  `- Static assets can be placed in the \`public\` folder`
@@ -1295,9 +1288,9 @@ const htmlContent = `<!DOCTYPE html>
1295
1288
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
1296
1289
  <title>$title</title>
1297
1290
  </head>
1298
- <body style="margin: 0; overscroll-behavior: none; user-select: none; touch-action: none;">
1291
+ <body>
1292
+ <div id="root"></div>
1299
1293
  <script type="module" src="$indexPath"><\/script>
1300
- <div style="width: 100dvw; height: 100dvh; overflow: hidden;" id="root"></div>
1301
1294
  </body>
1302
1295
  </html>`;
1303
1296
  const viteHtmlContent = `<!DOCTYPE html>
@@ -1314,6 +1307,7 @@ const viteHtmlContent = `<!DOCTYPE html>
1314
1307
  </html>`;
1315
1308
  const indexContent = `import { StrictMode } from 'react'
1316
1309
  import { createRoot } from 'react-dom/client'
1310
+ import './index.css'
1317
1311
  import { App } from './app.js'
1318
1312
 
1319
1313
  createRoot(document.getElementById('root')!).render(
@@ -1321,28 +1315,31 @@ createRoot(document.getElementById('root')!).render(
1321
1315
  <App />
1322
1316
  </StrictMode>,
1323
1317
  )`;
1324
- const viteIndexContent = `import './style.css'
1318
+ const viteIndexContent = `import './index.css'
1325
1319
 
1326
1320
  document.querySelector('#app')!.innerHTML = \`
1327
1321
  <h1>Hello Vite!</h1>
1328
1322
  <p>Edit src/main.ts and save to see HMR in action.</p>
1329
1323
  \``;
1330
- const viteStyleContent = `body {
1331
- font-family: system-ui, -apple-system, sans-serif;
1332
- margin: 0;
1333
- padding: 2rem;
1334
- min-height: 100vh;
1335
- background: #1a1a1a;
1336
- color: #fff;
1324
+ const viteStyleContent = `:root {
1325
+ font-family:
1326
+ system-ui,
1327
+ -apple-system,
1328
+ sans-serif;
1329
+ line-height: 1.5;
1330
+ font-weight: 400;
1337
1331
  }
1338
1332
 
1339
- h1 {
1340
- color: #646cff;
1333
+ *,
1334
+ *::before,
1335
+ *::after {
1336
+ box-sizing: border-box;
1341
1337
  }
1342
1338
 
1343
- a {
1344
- color: #646cff;
1339
+ body {
1340
+ margin: 0;
1345
1341
  }`;
1342
+ const viteEnvContent = `/// <reference types="vite/client" />`;
1346
1343
 
1347
1344
  function renderSourceFiles(params) {
1348
1345
  const { name, baseTemplate, language, isLibrary, codeSnippets, replacements } = params;
@@ -1352,6 +1349,9 @@ function renderSourceFiles(params) {
1352
1349
  const isVanilla = baseTemplate === "vanilla";
1353
1350
  const isReact = baseTemplate === "react";
1354
1351
  const isR3f = baseTemplate === "r3f";
1352
+ if (!isLibrary && language === "typescript") {
1353
+ files["src/vite-env.d.ts"] = { type: "text", content: viteEnvContent };
1354
+ }
1355
1355
  if (isLibrary) {
1356
1356
  const libExt = isReact || isR3f ? jsxExt : ext;
1357
1357
  let libContent;
@@ -1385,12 +1385,19 @@ function renderSourceFiles(params) {
1385
1385
  files[`src/index.${libExt}`] = { type: "text", content: libContent };
1386
1386
  } else if (isVanilla) {
1387
1387
  files[`src/main.${ext}`] = { type: "text", content: viteIndexContent };
1388
- files["src/style.css"] = { type: "text", content: viteStyleContent };
1388
+ files["src/index.css"] = { type: "text", content: viteStyleContent };
1389
1389
  const indexHtml = viteHtmlContent.replace("$indexPath", `./src/main.${ext}`).replace("$title", name);
1390
1390
  files["index.html"] = { type: "text", content: indexHtml };
1391
1391
  } else {
1392
- files[`src/index.tsx`] = { type: "text", content: indexContent };
1393
- const indexHtml = htmlContent.replace("$indexPath", language === "javascript" ? "./src/index.jsx" : "./src/index.tsx").replace("$title", name);
1392
+ files[`src/main.${jsxExt}`] = {
1393
+ type: "text",
1394
+ content: language === "typescript" ? indexContent : indexContent.replace(
1395
+ "document.getElementById('root')!",
1396
+ "document.getElementById('root')"
1397
+ )
1398
+ };
1399
+ files["src/index.css"] = { type: "text", content: viteStyleContent };
1400
+ const indexHtml = htmlContent.replace("$indexPath", `./src/main.${jsxExt}`).replace("$title", name);
1394
1401
  files["index.html"] = { type: "text", content: indexHtml };
1395
1402
  codeSnippets["dom-end"]?.reverse();
1396
1403
  codeSnippets["global-end"]?.reverse();
@@ -1432,7 +1439,7 @@ function renderSourceFiles(params) {
1432
1439
  for (const { search, replace } of replacements) {
1433
1440
  appCode = appCode.replace(search, replace);
1434
1441
  }
1435
- files[`src/app.tsx`] = { type: "text", content: appCode };
1442
+ files[`src/app.${jsxExt}`] = { type: "text", content: appCode };
1436
1443
  }
1437
1444
  return files;
1438
1445
  }
@@ -1756,7 +1763,7 @@ function formatValue(value, indent) {
1756
1763
  if (value.startsWith("$raw:")) {
1757
1764
  return value.slice(5);
1758
1765
  }
1759
- return JSON.stringify(value);
1766
+ return `'${value.replaceAll("\\", "\\\\").replaceAll("'", "\\'")}'`;
1760
1767
  }
1761
1768
  if (typeof value === "number" || typeof value === "boolean") {
1762
1769
  return String(value);
@@ -1764,8 +1771,17 @@ function formatValue(value, indent) {
1764
1771
  if (value === null) {
1765
1772
  return "null";
1766
1773
  }
1774
+ if (value === void 0) {
1775
+ return "undefined";
1776
+ }
1777
+ if (typeof value === "bigint") {
1778
+ return value.toString();
1779
+ }
1767
1780
  if (Array.isArray(value)) {
1768
1781
  if (value.length === 0) return "[]";
1782
+ if (value.every((item) => item == null || typeof item !== "object")) {
1783
+ return `[${value.map((item) => formatValue(item, indent + 1)).join(", ")}]`;
1784
+ }
1769
1785
  const items = value.map((v) => `${innerSpaces}${formatValue(v, indent + 1)}`);
1770
1786
  return `[
1771
1787
  ${items.join(",\n")}
@@ -1775,22 +1791,22 @@ ${spaces}]`;
1775
1791
  const entries = Object.entries(value);
1776
1792
  if (entries.length === 0) return "{}";
1777
1793
  const props = entries.map(
1778
- ([key, val]) => `${innerSpaces}${key}: ${formatValue(val, indent + 1)}`
1794
+ ([key, val]) => `${innerSpaces}${key}: ${formatValue(val, indent + 1)},`
1779
1795
  );
1780
1796
  return `{
1781
- ${props.join(",\n")}
1797
+ ${props.join("\n")}
1782
1798
  ${spaces}}`;
1783
1799
  }
1784
- return String(value);
1800
+ throw new TypeError(`Unsupported vite config value type: ${typeof value}`);
1785
1801
  }
1786
1802
  function renderViteConfig(params) {
1787
1803
  const { viteConfig, codeSnippets } = params;
1788
1804
  const configBody = formatValue(viteConfig, 0);
1789
1805
  const viteConfigContent = [
1790
- `import { defineConfig } from "vite"`,
1806
+ `import { defineConfig } from 'vite';`,
1791
1807
  ...codeSnippets["vite-config-import"] ?? [],
1792
1808
  ``,
1793
- `export default defineConfig(${configBody})`,
1809
+ `export default defineConfig(${configBody});`,
1794
1810
  ``
1795
1811
  ].join("\n");
1796
1812
  return { type: "text", content: viteConfigContent };
@@ -2999,16 +3015,10 @@ function generateProvidersModule(builder) {
2999
3015
  const resolvedProviders = providers.map((provider) => providerDefs[provider]);
3000
3016
  const providerProps = resolvedProviders.flatMap((provider) => provider.props || []);
3001
3017
  const providerImports = resolvedProviders.flatMap((provider) => provider.import);
3002
- const wrappedComponents = resolvedProviders.filter(
3003
- (provider) => provider.type === "wrapped-jsx"
3004
- );
3005
- const inlineComponents = resolvedProviders.filter(
3006
- (provider) => provider.type === "inline-jsx"
3007
- );
3008
- const layoutEffects = resolvedProviders.filter(
3009
- (provider) => provider.type === "layout-effect"
3010
- );
3011
- const declaredProps = providerProps.map((prop) => `${prop.declaredPropName} = ${prop.declaredPropDefaultValue}`).join(", ");
3018
+ const wrappedComponents = resolvedProviders.filter((provider) => provider.type === "wrapped-jsx");
3019
+ const inlineComponents = resolvedProviders.filter((provider) => provider.type === "inline-jsx");
3020
+ const layoutEffects = resolvedProviders.filter((provider) => provider.type === "layout-effect");
3021
+ const declaredProps = providerProps.map((prop) => `${prop.declaredPropName} = ${String(prop.declaredPropDefaultValue)}`).join(", ");
3012
3022
  const declaredTypes = providerProps.map((prop) => `${prop.declaredPropName}?: ${prop.declaredPropType}`).join("; ");
3013
3023
  const reactImports = ["type ReactNode"];
3014
3024
  if (layoutEffects.length) {
@@ -3025,11 +3035,11 @@ ${jsdoc.split("\n").map((line) => ` * ${line}`).join("\n")}
3025
3035
  ${layoutEffects.length ? `
3026
3036
  useLayoutEffect(() => {
3027
3037
  ${layoutEffects.map((effect) => effect.code).join("\n")}
3028
- }, [${layoutEffects.map((effect) => effect.props?.[0]?.propValue)}]);
3038
+ }, [${layoutEffects.map((effect) => effect.props?.[0]?.propValue).join(", ")}]);
3029
3039
  ` : ""}
3030
3040
  return (
3031
3041
  <>
3032
- ${inlineComponents.map((provider) => provider.code)}
3042
+ ${inlineComponents.map((provider) => provider.code).join("\n")}
3033
3043
  ${wrappedComponents.reduce((acc, provider) => {
3034
3044
  const props = provider.props?.map((prop) => `${prop.propName}={${prop.propValue}}`).join(" ");
3035
3045
  return `<${provider.component} ${props}>${acc}</${provider.component}>`;
@@ -3288,7 +3298,7 @@ function planXr(builder, options) {
3288
3298
  );
3289
3299
  builder.inject("scene-start", "<XR store={store}>");
3290
3300
  builder.inject("scene-end", "</XR>");
3291
- builder.inject("vite-config-import", "import basicSsl from '@vitejs/plugin-basic-ssl'");
3301
+ builder.inject("vite-config-import", "import basicSsl from '@vitejs/plugin-basic-ssl';");
3292
3302
  builder.configureVite({
3293
3303
  server: {
3294
3304
  host: true
@@ -3604,7 +3614,7 @@ function createProjectPlan(planInput) {
3604
3614
  const vscodeSettings = {};
3605
3615
  const scripts = {};
3606
3616
  if (!isLibrary && (isReact || isR3f)) {
3607
- codeSnippets["vite-config-import"] = ["import react from '@vitejs/plugin-react'"];
3617
+ codeSnippets["vite-config-import"] = ["import react from '@vitejs/plugin-react';"];
3608
3618
  }
3609
3619
  if (!isLibrary && isR3f) {
3610
3620
  codeSnippets["import"] = [`import { Canvas } from "@react-three/fiber"`];
@@ -3881,6 +3891,7 @@ exports.renderOxlintConfig = renderOxlintConfig;
3881
3891
  exports.renderOxlintConfigPackage = renderOxlintConfigPackage;
3882
3892
  exports.renderPrettierConfigPackage = renderPrettierConfigPackage;
3883
3893
  exports.renderTypescriptConfigPackage = renderTypescriptConfigPackage;
3894
+ exports.renderViteConfig = renderViteConfig;
3884
3895
  exports.renderVscodeFiles = renderVscodeFiles;
3885
3896
  exports.renderVscodeFiles$1 = renderVscodeFiles$1;
3886
3897
  exports.resolveDefaultPackageJsonScripts = resolveDefaultPackageJsonScripts;
@@ -192,9 +192,7 @@ function merge(target, modification) {
192
192
  const targetLabel = JSON.stringify(target);
193
193
  const modificationLabel = JSON.stringify(modification);
194
194
  if (modification == null) {
195
- throw new Error(
196
- `Cannot merge "${modificationLabel}" modification into target "${targetLabel}"`
197
- );
195
+ throw new Error(`Cannot merge "${modificationLabel}" modification into target "${targetLabel}"`);
198
196
  }
199
197
  if (target == null) {
200
198
  return modification;
@@ -874,10 +872,7 @@ function renderTypescriptConfig(baseTemplateOrParams) {
874
872
  const tsConfig = {
875
873
  $schema: "https://json.schemastore.org/tsconfig",
876
874
  files: [],
877
- references: [
878
- { path: "./.config/tsconfig.app.json" },
879
- { path: "./.config/tsconfig.node.json" }
880
- ]
875
+ references: [{ path: "./.config/tsconfig.app.json" }, { path: "./.config/tsconfig.node.json" }]
881
876
  };
882
877
  files["tsconfig.json"] = {
883
878
  type: "text",
@@ -1141,10 +1136,7 @@ function renderPackageJson(params) {
1141
1136
  const allDevDependencies = { ...devDependencies };
1142
1137
  const engine = getEngineSpec(options.engine);
1143
1138
  if (getEngineName(engine) === "node" && engine.version) {
1144
- allDevDependencies["@types/node"] ??= formatNodeTypesVersion(
1145
- options.versions,
1146
- options.engine
1147
- );
1139
+ allDevDependencies["@types/node"] ??= formatNodeTypesVersion(options.versions, options.engine);
1148
1140
  }
1149
1141
  packageJson.scripts = resolvedScripts;
1150
1142
  packageJson.dependencies = sortKeys(allDependencies);
@@ -1245,13 +1237,14 @@ function renderReadme(params) {
1245
1237
  } else if (isReact) {
1246
1238
  architectureDesc = [
1247
1239
  `- \`src/app.${jsxExt}\` defines the main application component`,
1248
- `- \`src/index.${jsxExt}\` renders the React app into the DOM`,
1240
+ `- \`src/main.${jsxExt}\` renders the React app into the DOM`,
1249
1241
  `- \`tests/\` contains your test files`,
1250
1242
  `- Static assets can be placed in the \`public\` folder`
1251
1243
  ];
1252
1244
  } else {
1253
1245
  architectureDesc = [
1254
- `- \`app.${jsxExt}\` defines the main application component containing your 3D content`,
1246
+ `- \`src/app.${jsxExt}\` defines the main application component containing your 3D content`,
1247
+ `- \`src/main.${jsxExt}\` renders the React app into the DOM`,
1255
1248
  `- Modify the content inside the \`<Canvas>\` component to change what is visible on screen`,
1256
1249
  `- \`tests/\` contains your test files`,
1257
1250
  `- Static assets can be placed in the \`public\` folder`
@@ -1289,9 +1282,9 @@ const htmlContent = `<!DOCTYPE html>
1289
1282
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
1290
1283
  <title>$title</title>
1291
1284
  </head>
1292
- <body style="margin: 0; overscroll-behavior: none; user-select: none; touch-action: none;">
1285
+ <body>
1286
+ <div id="root"></div>
1293
1287
  <script type="module" src="$indexPath"><\/script>
1294
- <div style="width: 100dvw; height: 100dvh; overflow: hidden;" id="root"></div>
1295
1288
  </body>
1296
1289
  </html>`;
1297
1290
  const viteHtmlContent = `<!DOCTYPE html>
@@ -1308,6 +1301,7 @@ const viteHtmlContent = `<!DOCTYPE html>
1308
1301
  </html>`;
1309
1302
  const indexContent = `import { StrictMode } from 'react'
1310
1303
  import { createRoot } from 'react-dom/client'
1304
+ import './index.css'
1311
1305
  import { App } from './app.js'
1312
1306
 
1313
1307
  createRoot(document.getElementById('root')!).render(
@@ -1315,28 +1309,31 @@ createRoot(document.getElementById('root')!).render(
1315
1309
  <App />
1316
1310
  </StrictMode>,
1317
1311
  )`;
1318
- const viteIndexContent = `import './style.css'
1312
+ const viteIndexContent = `import './index.css'
1319
1313
 
1320
1314
  document.querySelector('#app')!.innerHTML = \`
1321
1315
  <h1>Hello Vite!</h1>
1322
1316
  <p>Edit src/main.ts and save to see HMR in action.</p>
1323
1317
  \``;
1324
- const viteStyleContent = `body {
1325
- font-family: system-ui, -apple-system, sans-serif;
1326
- margin: 0;
1327
- padding: 2rem;
1328
- min-height: 100vh;
1329
- background: #1a1a1a;
1330
- color: #fff;
1318
+ const viteStyleContent = `:root {
1319
+ font-family:
1320
+ system-ui,
1321
+ -apple-system,
1322
+ sans-serif;
1323
+ line-height: 1.5;
1324
+ font-weight: 400;
1331
1325
  }
1332
1326
 
1333
- h1 {
1334
- color: #646cff;
1327
+ *,
1328
+ *::before,
1329
+ *::after {
1330
+ box-sizing: border-box;
1335
1331
  }
1336
1332
 
1337
- a {
1338
- color: #646cff;
1333
+ body {
1334
+ margin: 0;
1339
1335
  }`;
1336
+ const viteEnvContent = `/// <reference types="vite/client" />`;
1340
1337
 
1341
1338
  function renderSourceFiles(params) {
1342
1339
  const { name, baseTemplate, language, isLibrary, codeSnippets, replacements } = params;
@@ -1346,6 +1343,9 @@ function renderSourceFiles(params) {
1346
1343
  const isVanilla = baseTemplate === "vanilla";
1347
1344
  const isReact = baseTemplate === "react";
1348
1345
  const isR3f = baseTemplate === "r3f";
1346
+ if (!isLibrary && language === "typescript") {
1347
+ files["src/vite-env.d.ts"] = { type: "text", content: viteEnvContent };
1348
+ }
1349
1349
  if (isLibrary) {
1350
1350
  const libExt = isReact || isR3f ? jsxExt : ext;
1351
1351
  let libContent;
@@ -1379,12 +1379,19 @@ function renderSourceFiles(params) {
1379
1379
  files[`src/index.${libExt}`] = { type: "text", content: libContent };
1380
1380
  } else if (isVanilla) {
1381
1381
  files[`src/main.${ext}`] = { type: "text", content: viteIndexContent };
1382
- files["src/style.css"] = { type: "text", content: viteStyleContent };
1382
+ files["src/index.css"] = { type: "text", content: viteStyleContent };
1383
1383
  const indexHtml = viteHtmlContent.replace("$indexPath", `./src/main.${ext}`).replace("$title", name);
1384
1384
  files["index.html"] = { type: "text", content: indexHtml };
1385
1385
  } else {
1386
- files[`src/index.tsx`] = { type: "text", content: indexContent };
1387
- const indexHtml = htmlContent.replace("$indexPath", language === "javascript" ? "./src/index.jsx" : "./src/index.tsx").replace("$title", name);
1386
+ files[`src/main.${jsxExt}`] = {
1387
+ type: "text",
1388
+ content: language === "typescript" ? indexContent : indexContent.replace(
1389
+ "document.getElementById('root')!",
1390
+ "document.getElementById('root')"
1391
+ )
1392
+ };
1393
+ files["src/index.css"] = { type: "text", content: viteStyleContent };
1394
+ const indexHtml = htmlContent.replace("$indexPath", `./src/main.${jsxExt}`).replace("$title", name);
1388
1395
  files["index.html"] = { type: "text", content: indexHtml };
1389
1396
  codeSnippets["dom-end"]?.reverse();
1390
1397
  codeSnippets["global-end"]?.reverse();
@@ -1426,7 +1433,7 @@ function renderSourceFiles(params) {
1426
1433
  for (const { search, replace } of replacements) {
1427
1434
  appCode = appCode.replace(search, replace);
1428
1435
  }
1429
- files[`src/app.tsx`] = { type: "text", content: appCode };
1436
+ files[`src/app.${jsxExt}`] = { type: "text", content: appCode };
1430
1437
  }
1431
1438
  return files;
1432
1439
  }
@@ -1750,7 +1757,7 @@ function formatValue(value, indent) {
1750
1757
  if (value.startsWith("$raw:")) {
1751
1758
  return value.slice(5);
1752
1759
  }
1753
- return JSON.stringify(value);
1760
+ return `'${value.replaceAll("\\", "\\\\").replaceAll("'", "\\'")}'`;
1754
1761
  }
1755
1762
  if (typeof value === "number" || typeof value === "boolean") {
1756
1763
  return String(value);
@@ -1758,8 +1765,17 @@ function formatValue(value, indent) {
1758
1765
  if (value === null) {
1759
1766
  return "null";
1760
1767
  }
1768
+ if (value === void 0) {
1769
+ return "undefined";
1770
+ }
1771
+ if (typeof value === "bigint") {
1772
+ return value.toString();
1773
+ }
1761
1774
  if (Array.isArray(value)) {
1762
1775
  if (value.length === 0) return "[]";
1776
+ if (value.every((item) => item == null || typeof item !== "object")) {
1777
+ return `[${value.map((item) => formatValue(item, indent + 1)).join(", ")}]`;
1778
+ }
1763
1779
  const items = value.map((v) => `${innerSpaces}${formatValue(v, indent + 1)}`);
1764
1780
  return `[
1765
1781
  ${items.join(",\n")}
@@ -1769,22 +1785,22 @@ ${spaces}]`;
1769
1785
  const entries = Object.entries(value);
1770
1786
  if (entries.length === 0) return "{}";
1771
1787
  const props = entries.map(
1772
- ([key, val]) => `${innerSpaces}${key}: ${formatValue(val, indent + 1)}`
1788
+ ([key, val]) => `${innerSpaces}${key}: ${formatValue(val, indent + 1)},`
1773
1789
  );
1774
1790
  return `{
1775
- ${props.join(",\n")}
1791
+ ${props.join("\n")}
1776
1792
  ${spaces}}`;
1777
1793
  }
1778
- return String(value);
1794
+ throw new TypeError(`Unsupported vite config value type: ${typeof value}`);
1779
1795
  }
1780
1796
  function renderViteConfig(params) {
1781
1797
  const { viteConfig, codeSnippets } = params;
1782
1798
  const configBody = formatValue(viteConfig, 0);
1783
1799
  const viteConfigContent = [
1784
- `import { defineConfig } from "vite"`,
1800
+ `import { defineConfig } from 'vite';`,
1785
1801
  ...codeSnippets["vite-config-import"] ?? [],
1786
1802
  ``,
1787
- `export default defineConfig(${configBody})`,
1803
+ `export default defineConfig(${configBody});`,
1788
1804
  ``
1789
1805
  ].join("\n");
1790
1806
  return { type: "text", content: viteConfigContent };
@@ -2993,16 +3009,10 @@ function generateProvidersModule(builder) {
2993
3009
  const resolvedProviders = providers.map((provider) => providerDefs[provider]);
2994
3010
  const providerProps = resolvedProviders.flatMap((provider) => provider.props || []);
2995
3011
  const providerImports = resolvedProviders.flatMap((provider) => provider.import);
2996
- const wrappedComponents = resolvedProviders.filter(
2997
- (provider) => provider.type === "wrapped-jsx"
2998
- );
2999
- const inlineComponents = resolvedProviders.filter(
3000
- (provider) => provider.type === "inline-jsx"
3001
- );
3002
- const layoutEffects = resolvedProviders.filter(
3003
- (provider) => provider.type === "layout-effect"
3004
- );
3005
- const declaredProps = providerProps.map((prop) => `${prop.declaredPropName} = ${prop.declaredPropDefaultValue}`).join(", ");
3012
+ const wrappedComponents = resolvedProviders.filter((provider) => provider.type === "wrapped-jsx");
3013
+ const inlineComponents = resolvedProviders.filter((provider) => provider.type === "inline-jsx");
3014
+ const layoutEffects = resolvedProviders.filter((provider) => provider.type === "layout-effect");
3015
+ const declaredProps = providerProps.map((prop) => `${prop.declaredPropName} = ${String(prop.declaredPropDefaultValue)}`).join(", ");
3006
3016
  const declaredTypes = providerProps.map((prop) => `${prop.declaredPropName}?: ${prop.declaredPropType}`).join("; ");
3007
3017
  const reactImports = ["type ReactNode"];
3008
3018
  if (layoutEffects.length) {
@@ -3019,11 +3029,11 @@ ${jsdoc.split("\n").map((line) => ` * ${line}`).join("\n")}
3019
3029
  ${layoutEffects.length ? `
3020
3030
  useLayoutEffect(() => {
3021
3031
  ${layoutEffects.map((effect) => effect.code).join("\n")}
3022
- }, [${layoutEffects.map((effect) => effect.props?.[0]?.propValue)}]);
3032
+ }, [${layoutEffects.map((effect) => effect.props?.[0]?.propValue).join(", ")}]);
3023
3033
  ` : ""}
3024
3034
  return (
3025
3035
  <>
3026
- ${inlineComponents.map((provider) => provider.code)}
3036
+ ${inlineComponents.map((provider) => provider.code).join("\n")}
3027
3037
  ${wrappedComponents.reduce((acc, provider) => {
3028
3038
  const props = provider.props?.map((prop) => `${prop.propName}={${prop.propValue}}`).join(" ");
3029
3039
  return `<${provider.component} ${props}>${acc}</${provider.component}>`;
@@ -3282,7 +3292,7 @@ function planXr(builder, options) {
3282
3292
  );
3283
3293
  builder.inject("scene-start", "<XR store={store}>");
3284
3294
  builder.inject("scene-end", "</XR>");
3285
- builder.inject("vite-config-import", "import basicSsl from '@vitejs/plugin-basic-ssl'");
3295
+ builder.inject("vite-config-import", "import basicSsl from '@vitejs/plugin-basic-ssl';");
3286
3296
  builder.configureVite({
3287
3297
  server: {
3288
3298
  host: true
@@ -3598,7 +3608,7 @@ function createProjectPlan(planInput) {
3598
3608
  const vscodeSettings = {};
3599
3609
  const scripts = {};
3600
3610
  if (!isLibrary && (isReact || isR3f)) {
3601
- codeSnippets["vite-config-import"] = ["import react from '@vitejs/plugin-react'"];
3611
+ codeSnippets["vite-config-import"] = ["import react from '@vitejs/plugin-react';"];
3602
3612
  }
3603
3613
  if (!isLibrary && isR3f) {
3604
3614
  codeSnippets["import"] = [`import { Canvas } from "@react-three/fiber"`];
@@ -3840,4 +3850,4 @@ async function planWorkspace(input) {
3840
3850
  };
3841
3851
  }
3842
3852
 
3843
- export { ALL_AI_PLATFORMS as A, renderTypescriptConfigPackage as B, renderOxlintConfigPackage as C, renderEslintConfigPackage as D, renderOxfmtConfigPackage as E, renderPrettierConfigPackage as F, resolveMonorepoRootPackageVersions as G, getResolvedPackageVersion as H, renderVscodeFiles as I, renderAiFiles as J, renderVscodeFiles$1 as K, renderEditorConfig as L, renderGitignore as M, toPrettierIgnoreContent as N, mergePackageJsonScripts as O, packageJsonScripts as P, resolveDefaultPackageJsonScripts as Q, formatResolvedPackageVersion as R, renderOxlintConfig as S, getBaseTemplate as a, getLanguageFromTemplate as b, getLatestNodeVersion as c, detectTooling as d, getLatestNpmCliVersion as e, getLatestNpmMajorVersion as f, generateRandomName as g, getLatestNpmVersion as h, getLatestPnpmVersion as i, getLatestYarnVersion as j, planProject as k, planWorkspace as l, merge as m, projectPlanInputToOptions as n, resolveWorkspacePlanInput as o, parseWorkspaceYamlContent as p, getEngineName as q, resolveProjectPlanInput as r, getPackageManagerName as s, AI_PLATFORM_LABELS as t, unique as u, validatePackageName as v, workspacePlanInputToMonorepoParams as w, AI_PLATFORM_HINTS as x, parsePackageManager as y, parseEngine as z };
3853
+ export { ALL_AI_PLATFORMS as A, renderTypescriptConfigPackage as B, renderOxlintConfigPackage as C, renderEslintConfigPackage as D, renderOxfmtConfigPackage as E, renderPrettierConfigPackage as F, resolveMonorepoRootPackageVersions as G, getResolvedPackageVersion as H, renderVscodeFiles as I, renderAiFiles as J, renderVscodeFiles$1 as K, renderEditorConfig as L, renderGitignore as M, toPrettierIgnoreContent as N, mergePackageJsonScripts as O, renderViteConfig as P, packageJsonScripts as Q, resolveDefaultPackageJsonScripts as R, formatResolvedPackageVersion as S, renderOxlintConfig as T, getBaseTemplate as a, getLanguageFromTemplate as b, getLatestNodeVersion as c, detectTooling as d, getLatestNpmCliVersion as e, getLatestNpmMajorVersion as f, generateRandomName as g, getLatestNpmVersion as h, getLatestPnpmVersion as i, getLatestYarnVersion as j, planProject as k, planWorkspace as l, merge as m, projectPlanInputToOptions as n, resolveWorkspacePlanInput as o, parseWorkspaceYamlContent as p, getEngineName as q, resolveProjectPlanInput as r, getPackageManagerName as s, AI_PLATFORM_LABELS as t, unique as u, validatePackageName as v, workspacePlanInputToMonorepoParams as w, AI_PLATFORM_HINTS as x, parsePackageManager as y, parseEngine as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-krispya",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "🌹 CLI for creating web projects with (my) sensible defaults",
5
5
  "keywords": [
6
6
  "cli",