create-krispya 0.6.0 → 0.8.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.
@@ -1,7 +1,7 @@
1
- import color from 'chalk';
2
1
  import { readFile, access } from 'fs/promises';
3
2
  import { constants } from 'fs';
4
3
  import { join } from 'path';
4
+ import color from 'chalk';
5
5
 
6
6
  const HtmlContent = `<!DOCTYPE html>
7
7
  <html lang="en">
@@ -85,7 +85,7 @@ const GitAttributes = [
85
85
  ].join("\n");
86
86
  const defaultFormatterConfig = {
87
87
  printWidth: 102,
88
- tabWidth: 4,
88
+ tabWidth: 2,
89
89
  useTabs: false,
90
90
  semi: true,
91
91
  singleQuote: true,
@@ -97,17 +97,13 @@ const defaultPrettierConfig = {
97
97
  $schema: "https://json.schemastore.org/prettierrc",
98
98
  ...defaultFormatterConfig,
99
99
  overrides: [
100
- {
101
- files: ["*.json", "**/*.json"],
102
- options: { tabWidth: 2 }
103
- },
104
100
  {
105
101
  files: ["*.md", "**/*.md"],
106
- options: { tabWidth: 2, semi: false }
102
+ options: { semi: false }
107
103
  },
108
104
  {
109
105
  files: ["*.yml", "*.yaml", "**/*.yml", "**/*.yaml"],
110
- options: { tabWidth: 2, semi: false }
106
+ options: { semi: false }
111
107
  }
112
108
  ]
113
109
  };
@@ -149,22 +145,24 @@ const AI_PLATFORM_HINTS = {
149
145
  function generateAiFiles(files, params) {
150
146
  const { platforms, isMonorepo, configStrategy, ...rest } = params;
151
147
  if (platforms.length === 0) return;
152
- files[".ai/workspace.md"] = {
153
- type: "text",
154
- content: generateWorkspace({
155
- ...rest,
156
- isMonorepo: !!isMonorepo,
157
- configStrategy: configStrategy ?? "stealth"
158
- })
159
- };
160
- const pointer = "See `.ai/` for project rules.\n";
161
- for (const platform of platforms) {
162
- const path = platform === "agents" ? "AGENTS.md" : "CLAUDE.md";
163
- files[path] = { type: "text", content: pointer };
148
+ const content = generateWorkspace({
149
+ ...rest,
150
+ isMonorepo: !!isMonorepo});
151
+ const pointer = "See [`AGENTS.md`](./Agents.md) for agent context.\n";
152
+ const hasAgents = platforms.includes("agents");
153
+ const hasClaude = platforms.includes("claude");
154
+ const isSingleton = platforms.length === 1;
155
+ if (hasAgents) files["AGENTS.md"] = { type: "text", content };
156
+ if (hasClaude) {
157
+ if (isSingleton) {
158
+ files["CLAUDE.md"] = { type: "text", content };
159
+ } else {
160
+ files["CLAUDE.md"] = { type: "text", content: pointer };
161
+ }
164
162
  }
165
163
  }
166
164
  function generateWorkspace(ctx) {
167
- const { name, packageManager, linter, formatter, isMonorepo, configStrategy } = ctx;
165
+ const { name, packageManager, linter, formatter, isMonorepo} = ctx;
168
166
  const sections = [
169
167
  `# ${name}`,
170
168
  "",
@@ -181,54 +179,572 @@ function generateWorkspace(ctx) {
181
179
  ];
182
180
  if (isMonorepo) {
183
181
  sections.push(
184
- "",
185
- "## Structure",
186
- "",
187
- "- `apps/` \u2014 applications",
188
- "- `packages/` \u2014 shared libraries",
189
- "- `.config/` \u2014 shared config packages",
190
- "",
191
- "## Monorepo",
192
182
  "",
193
183
  "- Use `workspace:*` for internal dependencies",
194
184
  `- New packages: \`${packageManager} create krispya <name> --workspace\``
195
185
  );
196
- } else if (configStrategy === "root") {
197
- sections.push(
198
- "",
199
- "## Structure",
200
- "",
201
- "- `src/` \u2014 source code",
202
- "- `dist/` \u2014 generated, don't edit",
203
- "- Config files (`tsconfig.json`, etc.) are at project root"
204
- );
205
- } else {
206
- sections.push(
207
- "",
208
- "## Structure",
209
- "",
210
- "- `src/` \u2014 source code",
211
- "- `.config/` \u2014 configs, don't move",
212
- "- `dist/` \u2014 generated, don't edit"
213
- );
214
186
  }
215
187
  sections.push("");
216
188
  return sections.join("\n");
217
189
  }
218
190
 
191
+ function getLanguageFromTemplate(template) {
192
+ return template.endsWith("-js") ? "javascript" : "typescript";
193
+ }
194
+ function getBaseTemplate(template) {
195
+ return template.replace("-js", "");
196
+ }
197
+
198
+ async function getLatestNpmVersion(packageName, fallback) {
199
+ try {
200
+ const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`);
201
+ const data = await response.json();
202
+ return data.version;
203
+ } catch {
204
+ return fallback;
205
+ }
206
+ }
207
+ async function getLatestPnpmVersion() {
208
+ return getLatestNpmVersion("pnpm", "10.11.0");
209
+ }
210
+ async function getLatestYarnVersion() {
211
+ return getLatestNpmVersion("yarn", "4.6.0");
212
+ }
213
+ async function getLatestNpmCliVersion() {
214
+ return getLatestNpmVersion("npm", "11.0.0");
215
+ }
216
+ async function getLatestNodeVersion() {
217
+ try {
218
+ const response = await fetch("https://nodejs.org/dist/index.json");
219
+ const data = await response.json();
220
+ const latestVersion = data[0];
221
+ if (latestVersion) {
222
+ return latestVersion.version.replace(/^v/, "");
223
+ }
224
+ return "25.0.0";
225
+ } catch {
226
+ return "25.0.0";
227
+ }
228
+ }
229
+ function validateNameSegment(segment, label) {
230
+ if (!segment.length) {
231
+ return `${label} is required`;
232
+ }
233
+ if (!/^[a-z0-9-]+$/.test(segment)) {
234
+ return `${label} must be lowercase and contain only letters, numbers, and hyphens`;
235
+ }
236
+ if (segment.startsWith("-") || segment.endsWith("-")) {
237
+ return `${label} cannot start or end with a hyphen`;
238
+ }
239
+ if (segment.includes("--")) {
240
+ return `${label} cannot contain consecutive hyphens`;
241
+ }
242
+ return void 0;
243
+ }
244
+ function validatePackageName(name) {
245
+ if (!name.length) {
246
+ return "Package name is required";
247
+ }
248
+ if (name.includes("..") || name.includes("\\")) {
249
+ return "Package name cannot contain path traversal sequences";
250
+ }
251
+ if (name.startsWith("@")) {
252
+ const slashIndex = name.indexOf("/");
253
+ if (slashIndex === -1) {
254
+ return "Scoped package name must include a package name after the scope (e.g., @scope/name)";
255
+ }
256
+ if (name.indexOf("/", slashIndex + 1) !== -1) {
257
+ return "Package name can only have one slash for scoped packages";
258
+ }
259
+ const scope = name.slice(1, slashIndex);
260
+ const packageName = name.slice(slashIndex + 1);
261
+ const scopeError = validateNameSegment(scope, "Scope");
262
+ if (scopeError) return scopeError;
263
+ const nameError = validateNameSegment(packageName, "Package name");
264
+ if (nameError) return nameError;
265
+ return void 0;
266
+ }
267
+ if (name.includes("/")) {
268
+ return "Unscoped package name cannot contain slashes. Use @scope/name format for scoped packages";
269
+ }
270
+ return validateNameSegment(name, "Package name");
271
+ }
272
+ function parseWorkspaceYamlContent(content) {
273
+ const directories = [];
274
+ let inPackagesSection = false;
275
+ for (const line of content.split("\n")) {
276
+ const trimmed = line.trim();
277
+ if (trimmed === "packages:") {
278
+ inPackagesSection = true;
279
+ continue;
280
+ }
281
+ if (inPackagesSection && trimmed && !line.startsWith(" ") && !line.startsWith(" ") && !trimmed.startsWith("-")) {
282
+ break;
283
+ }
284
+ if (inPackagesSection && trimmed.startsWith("-")) {
285
+ const entry = trimmed.slice(1).trim().replace(/^["']|["']$/g, "").replace(/^\.\//, "").replace(/\/\*.*$/, "");
286
+ if (entry && !entry.startsWith(".")) {
287
+ directories.push(entry);
288
+ }
289
+ }
290
+ }
291
+ return directories;
292
+ }
293
+ async function pathExists(path) {
294
+ try {
295
+ await access(path, constants.F_OK);
296
+ return true;
297
+ } catch {
298
+ return false;
299
+ }
300
+ }
301
+ function detectLinterFromScript(script) {
302
+ if (!script) return void 0;
303
+ if (script.includes("oxlint")) return "oxlint";
304
+ if (script.includes("eslint")) return "eslint";
305
+ if (script.includes("biome check") || script.includes("biome lint")) return "biome";
306
+ return void 0;
307
+ }
308
+ function detectFormatterFromScript(script) {
309
+ if (!script) return void 0;
310
+ if (script.includes("prettier")) return "prettier";
311
+ if (script.includes("oxfmt")) return "oxfmt";
312
+ if (script.includes("biome format")) return "biome";
313
+ return void 0;
314
+ }
315
+ async function detectLinterFromConfig(root) {
316
+ if (await pathExists(join(root, ".config/oxlint"))) return "oxlint";
317
+ if (await pathExists(join(root, ".config/eslint"))) return "eslint";
318
+ if (await pathExists(join(root, "biome.json"))) {
319
+ try {
320
+ const content = await readFile(join(root, "biome.json"), "utf-8");
321
+ const config = JSON.parse(content);
322
+ if (config.linter?.enabled !== false) return "biome";
323
+ } catch {
324
+ return "biome";
325
+ }
326
+ }
327
+ return void 0;
328
+ }
329
+ async function detectFormatterFromConfig(root) {
330
+ if (await pathExists(join(root, ".config/prettier"))) return "prettier";
331
+ if (await pathExists(join(root, ".config/oxfmt"))) return "oxfmt";
332
+ if (await pathExists(join(root, "biome.json"))) {
333
+ try {
334
+ const content = await readFile(join(root, "biome.json"), "utf-8");
335
+ const config = JSON.parse(content);
336
+ if (config.formatter?.enabled !== false) return "biome";
337
+ } catch {
338
+ return "biome";
339
+ }
340
+ }
341
+ return void 0;
342
+ }
343
+ function detectLinterFromDeps(devDeps) {
344
+ if (!devDeps) return void 0;
345
+ if (devDeps["@biomejs/biome"]) return "biome";
346
+ if (devDeps.eslint) return "eslint";
347
+ if (devDeps.oxlint) return "oxlint";
348
+ return void 0;
349
+ }
350
+ function detectFormatterFromDeps(devDeps) {
351
+ if (!devDeps) return void 0;
352
+ if (devDeps["@biomejs/biome"]) return "biome";
353
+ if (devDeps.prettier) return "prettier";
354
+ if (devDeps.oxfmt) return "oxfmt";
355
+ return void 0;
356
+ }
357
+ async function detectTooling(root) {
358
+ try {
359
+ const pkgPath = join(root, "package.json");
360
+ const content = await readFile(pkgPath, "utf-8");
361
+ const pkg = JSON.parse(content);
362
+ const linter = detectLinterFromScript(pkg.scripts?.lint) ?? await detectLinterFromConfig(root) ?? detectLinterFromDeps(pkg.devDependencies);
363
+ const formatter = detectFormatterFromScript(pkg.scripts?.format) ?? await detectFormatterFromConfig(root) ?? detectFormatterFromDeps(pkg.devDependencies);
364
+ return { linter, formatter };
365
+ } catch {
366
+ return { linter: void 0, formatter: void 0 };
367
+ }
368
+ }
369
+ function generateRandomName() {
370
+ const adjectives = [
371
+ "red",
372
+ "blue",
373
+ "green",
374
+ "yellow",
375
+ "purple",
376
+ "orange",
377
+ "pink",
378
+ "black",
379
+ "white",
380
+ "tiny",
381
+ "big",
382
+ "small",
383
+ "large",
384
+ "huge",
385
+ "giant",
386
+ "mini",
387
+ "mega",
388
+ "super",
389
+ "happy",
390
+ "sad",
391
+ "angry",
392
+ "calm",
393
+ "quiet",
394
+ "loud",
395
+ "silent",
396
+ "noisy",
397
+ "shiny",
398
+ "dull",
399
+ "bright",
400
+ "dark",
401
+ "fuzzy",
402
+ "smooth",
403
+ "rough",
404
+ "soft"
405
+ ];
406
+ const nouns = [
407
+ "apple",
408
+ "banana",
409
+ "cherry",
410
+ "date",
411
+ "elderberry",
412
+ "fig",
413
+ "grape",
414
+ "honeydew",
415
+ "cat",
416
+ "dog",
417
+ "elephant",
418
+ "fox",
419
+ "giraffe",
420
+ "horse",
421
+ "iguana",
422
+ "jaguar",
423
+ "mountain",
424
+ "river",
425
+ "ocean",
426
+ "desert",
427
+ "forest",
428
+ "jungle",
429
+ "meadow",
430
+ "valley",
431
+ "star",
432
+ "moon",
433
+ "sun",
434
+ "planet",
435
+ "comet",
436
+ "asteroid",
437
+ "galaxy",
438
+ "universe"
439
+ ];
440
+ const randomAdjective = adjectives[Math.floor(Math.random() * adjectives.length)];
441
+ const randomNoun = nouns[Math.floor(Math.random() * nouns.length)];
442
+ return `${randomAdjective}-${randomNoun}`;
443
+ }
444
+
445
+ const PACKAGE_VERSION_DEFINITIONS = {
446
+ "@biomejs/biome": { fallbackVersion: "2.0.0" },
447
+ "@react-three/drei": { fallbackVersion: "10.0.0" },
448
+ "@react-three/fiber": { fallbackVersion: "9.0.0" },
449
+ "@react-three/handle": { fallbackVersion: "6.6.16" },
450
+ "@react-three/offscreen": { fallbackVersion: "0.0.8" },
451
+ "@react-three/postprocessing": { fallbackVersion: "3.0.4" },
452
+ "@react-three/rapier": { fallbackVersion: "2.1.0" },
453
+ "@react-three/uikit": { fallbackVersion: "0.8.15" },
454
+ "@react-three/xr": { fallbackVersion: "6.6.16" },
455
+ "@testing-library/dom": { fallbackVersion: "10.4.0" },
456
+ "@testing-library/react": { fallbackVersion: "16.2.0" },
457
+ "@types/react": { fallbackVersion: "19.0.0" },
458
+ "@types/react-dom": { fallbackVersion: "19.0.0" },
459
+ "@types/three": { fallbackVersion: "0.175.0", prefix: "~" },
460
+ "@vitejs/plugin-basic-ssl": { fallbackVersion: "2.0.0" },
461
+ "@vitejs/plugin-react": { fallbackVersion: "4.4.1" },
462
+ "@viverse/cli": { fallbackVersion: "0.9.5-beta.8" },
463
+ eslint: { fallbackVersion: "9.17.0" },
464
+ "eslint-plugin-react-hooks": { fallbackVersion: "5.1.0" },
465
+ jsdom: { fallbackVersion: "26.0.0" },
466
+ koota: { fallbackVersion: "0.4.0" },
467
+ leva: { fallbackVersion: "0.10.0" },
468
+ oxfmt: { fallbackVersion: "0.21.0" },
469
+ oxlint: { fallbackVersion: "1.36.0" },
470
+ prettier: { fallbackVersion: "3.4.2" },
471
+ react: { fallbackVersion: "19.0.0" },
472
+ "react-dom": { fallbackVersion: "19.0.0" },
473
+ three: { fallbackVersion: "0.175.0", prefix: "~" },
474
+ tsdown: { fallbackVersion: "0.12.0" },
475
+ "typescript-eslint": { fallbackVersion: "8.18.0" },
476
+ unbuild: { fallbackVersion: "3.5.0" },
477
+ vite: { fallbackVersion: "6.3.4" },
478
+ vitest: { fallbackVersion: "4.0.0" },
479
+ zustand: { fallbackVersion: "5.0.3" }
480
+ };
481
+ function addPackageName(packageNames, explicitVersions, packageName) {
482
+ if (!explicitVersions.has(packageName)) {
483
+ packageNames.add(packageName);
484
+ }
485
+ }
486
+ function getExplicitVersionPackages(options) {
487
+ return /* @__PURE__ */ new Set([
488
+ ...Object.keys(options.dependencies ?? {}),
489
+ ...Object.keys(options.versions ?? {})
490
+ ]);
491
+ }
492
+ function getPackageFallbackVersion(packageName) {
493
+ const definition = PACKAGE_VERSION_DEFINITIONS[packageName];
494
+ if (definition == null) {
495
+ throw new Error(`Missing package version definition for ${packageName}`);
496
+ }
497
+ return definition.fallbackVersion;
498
+ }
499
+ function getResolvedPackageVersion(versions, packageName) {
500
+ return versions[packageName] ?? getPackageFallbackVersion(packageName);
501
+ }
502
+ function formatResolvedPackageVersion(versions, packageName, prefix) {
503
+ const resolvedPrefix = prefix ?? PACKAGE_VERSION_DEFINITIONS[packageName]?.prefix ?? "^";
504
+ return `${resolvedPrefix}${getResolvedPackageVersion(versions, packageName)}`;
505
+ }
506
+ function assignResolvedPackageVersion(target, versions, packageName, prefix) {
507
+ target[packageName] = formatResolvedPackageVersion(versions, packageName, prefix);
508
+ }
509
+ function getPackageManagerSpec(packageManager) {
510
+ return packageManager ?? { name: "pnpm" };
511
+ }
512
+ function getPackageManagerName(packageManager) {
513
+ return getPackageManagerSpec(packageManager).name;
514
+ }
515
+ function formatPackageManager(packageManager) {
516
+ const spec = getPackageManagerSpec(packageManager);
517
+ return spec.version ? `${spec.name}@${spec.version}` : spec.name;
518
+ }
519
+ function parsePackageManager(packageManager) {
520
+ if (packageManager == null || packageManager.length === 0) {
521
+ return void 0;
522
+ }
523
+ const atIndex = packageManager.indexOf("@");
524
+ if (atIndex === -1) {
525
+ return { name: packageManager };
526
+ }
527
+ return {
528
+ name: packageManager.slice(0, atIndex),
529
+ version: packageManager.slice(atIndex + 1)
530
+ };
531
+ }
532
+ function getEngineSpec(engine) {
533
+ return engine ?? { name: "node" };
534
+ }
535
+ function getEngineName(engine) {
536
+ return getEngineSpec(engine).name;
537
+ }
538
+ function parseEngine(engines) {
539
+ if (engines == null) {
540
+ return void 0;
541
+ }
542
+ const [name, range] = Object.entries(engines).find(
543
+ ([engineName]) => engineName !== "npm" && engineName !== "pnpm" && engineName !== "yarn"
544
+ ) ?? [];
545
+ if (name == null) {
546
+ return void 0;
547
+ }
548
+ const version = range?.match(/(\d+(?:\.\d+(?:\.\d+)?)?)/)?.[1];
549
+ return { name, version };
550
+ }
551
+ async function resolvePackageManager(options) {
552
+ const packageManager = getPackageManagerSpec(options.packageManager);
553
+ if (packageManager.version == null) {
554
+ if (packageManager.name === "pnpm") {
555
+ packageManager.version = await getLatestPnpmVersion();
556
+ } else if (packageManager.name === "yarn") {
557
+ packageManager.version = await getLatestYarnVersion();
558
+ } else if (packageManager.name === "npm") {
559
+ packageManager.version = await getLatestNpmCliVersion();
560
+ }
561
+ }
562
+ return packageManager;
563
+ }
564
+ async function resolveEngine(options) {
565
+ const engine = getEngineSpec(options.engine);
566
+ if (engine.version == null && engine.name === "node") {
567
+ engine.version = await getLatestNodeVersion();
568
+ }
569
+ return engine;
570
+ }
571
+ async function resolvePackageVersions(packageNames, existingVersions = {}) {
572
+ const versions = { ...existingVersions };
573
+ const uniquePackageNames = [...new Set(packageNames)];
574
+ await Promise.all(
575
+ uniquePackageNames.map(async (packageName) => {
576
+ if (versions[packageName] != null) return;
577
+ versions[packageName] = await getLatestNpmVersion(
578
+ packageName,
579
+ getPackageFallbackVersion(packageName)
580
+ );
581
+ })
582
+ );
583
+ return versions;
584
+ }
585
+ async function resolveProjectPackageVersions(options) {
586
+ return resolvePackageVersions(collectProjectPackageNames(options), options.versions);
587
+ }
588
+ async function resolveMonorepoRootPackageVersions(params) {
589
+ const packageNames = /* @__PURE__ */ new Set();
590
+ const explicitVersions = new Set(Object.keys(params.versions ?? {}));
591
+ addPackageName(packageNames, explicitVersions, getLinterPackage(params.linter));
592
+ if (params.formatter !== "biome" || params.linter !== "biome") {
593
+ addPackageName(packageNames, explicitVersions, getFormatterPackage(params.formatter));
594
+ }
595
+ return resolvePackageVersions(packageNames, params.versions);
596
+ }
597
+ function collectProjectPackageNames(options) {
598
+ const packageNames = /* @__PURE__ */ new Set();
599
+ const explicitVersions = getExplicitVersionPackages(options);
600
+ const template = options.template ?? "vanilla";
601
+ const baseTemplate = getBaseTemplate(template);
602
+ const language = getLanguageFromTemplate(template);
603
+ const isLibrary = options.projectType === "library";
604
+ const isReact = baseTemplate === "react" || baseTemplate === "r3f";
605
+ const isR3f = baseTemplate === "r3f";
606
+ const isTypescript = language === "typescript";
607
+ const inWorkspace = options.workspaceRoot != null;
608
+ const testing = options.testing ?? (isLibrary ? "vitest" : "none");
609
+ const linter = options.linter ?? "oxlint";
610
+ const formatter = options.formatter ?? "prettier";
611
+ const bundler = options.libraryBundler ?? "unbuild";
612
+ const packageManager = getPackageManagerName(options.packageManager);
613
+ if (!isLibrary) {
614
+ addPackageName(packageNames, explicitVersions, "vite");
615
+ }
616
+ if (isReact) {
617
+ if (!isLibrary) {
618
+ addPackageName(packageNames, explicitVersions, "react");
619
+ addPackageName(packageNames, explicitVersions, "react-dom");
620
+ addPackageName(packageNames, explicitVersions, "@vitejs/plugin-react");
621
+ }
622
+ if (isTypescript) {
623
+ addPackageName(packageNames, explicitVersions, "@types/react");
624
+ addPackageName(packageNames, explicitVersions, "@types/react-dom");
625
+ }
626
+ }
627
+ if (isR3f) {
628
+ if (!isLibrary) {
629
+ addPackageName(packageNames, explicitVersions, "three");
630
+ addPackageName(packageNames, explicitVersions, "@react-three/fiber");
631
+ }
632
+ if (isTypescript) {
633
+ addPackageName(packageNames, explicitVersions, "@types/three");
634
+ }
635
+ if (isEnabledOption(options.drei)) {
636
+ addPackageName(packageNames, explicitVersions, "@react-three/drei");
637
+ }
638
+ if (isEnabledOption(options.handle)) {
639
+ addPackageName(packageNames, explicitVersions, "@react-three/handle");
640
+ }
641
+ if (isEnabledOption(options.koota)) {
642
+ addPackageName(packageNames, explicitVersions, "koota");
643
+ }
644
+ if (isEnabledOption(options.leva)) {
645
+ addPackageName(packageNames, explicitVersions, "leva");
646
+ }
647
+ if (isEnabledOption(options.rapier)) {
648
+ addPackageName(packageNames, explicitVersions, "@react-three/rapier");
649
+ }
650
+ if (isEnabledOption(options.uikit)) {
651
+ addPackageName(packageNames, explicitVersions, "@react-three/uikit");
652
+ }
653
+ if (isEnabledOption(options.zustand)) {
654
+ addPackageName(packageNames, explicitVersions, "zustand");
655
+ }
656
+ if (isEnabledOption(options.xr)) {
657
+ addPackageName(packageNames, explicitVersions, "@react-three/xr");
658
+ addPackageName(packageNames, explicitVersions, "@vitejs/plugin-basic-ssl");
659
+ }
660
+ if (!isEnabledOption(options.xr) && isEnabledOption(options.offscreen)) {
661
+ addPackageName(packageNames, explicitVersions, "@react-three/offscreen");
662
+ }
663
+ if (!isEnabledOption(options.xr) && isEnabledOption(options.postprocessing)) {
664
+ addPackageName(packageNames, explicitVersions, "@react-three/postprocessing");
665
+ }
666
+ if (isEnabledOption(options.viverse) && packageManager === "npm") {
667
+ addPackageName(packageNames, explicitVersions, "@viverse/cli");
668
+ }
669
+ }
670
+ if (testing === "vitest") {
671
+ addPackageName(packageNames, explicitVersions, "vitest");
672
+ if (isReact) {
673
+ addPackageName(packageNames, explicitVersions, "@testing-library/react");
674
+ addPackageName(packageNames, explicitVersions, "@testing-library/dom");
675
+ addPackageName(packageNames, explicitVersions, "jsdom");
676
+ }
677
+ }
678
+ if (linter === "eslint") {
679
+ addPackageName(packageNames, explicitVersions, "eslint");
680
+ if (isTypescript) {
681
+ addPackageName(packageNames, explicitVersions, "typescript-eslint");
682
+ }
683
+ if (isReact) {
684
+ addPackageName(packageNames, explicitVersions, "eslint-plugin-react-hooks");
685
+ }
686
+ } else if (linter === "oxlint") {
687
+ if (!inWorkspace) {
688
+ addPackageName(packageNames, explicitVersions, "oxlint");
689
+ }
690
+ } else if (linter === "biome") {
691
+ addPackageName(packageNames, explicitVersions, "@biomejs/biome");
692
+ }
693
+ if (formatter === "prettier") {
694
+ addPackageName(packageNames, explicitVersions, "prettier");
695
+ } else if (formatter === "oxfmt") {
696
+ if (!inWorkspace) {
697
+ addPackageName(packageNames, explicitVersions, "oxfmt");
698
+ }
699
+ } else if (formatter === "biome") {
700
+ addPackageName(packageNames, explicitVersions, "@biomejs/biome");
701
+ }
702
+ if (isLibrary) {
703
+ addPackageName(packageNames, explicitVersions, bundler === "unbuild" ? "unbuild" : "tsdown");
704
+ }
705
+ return [...packageNames];
706
+ }
707
+ function getLinterPackage(linter) {
708
+ if (linter === "biome") {
709
+ return "@biomejs/biome";
710
+ }
711
+ return linter;
712
+ }
713
+ function getFormatterPackage(formatter) {
714
+ if (formatter === "biome") {
715
+ return "@biomejs/biome";
716
+ }
717
+ return formatter;
718
+ }
719
+ function isEnabledOption(option) {
720
+ return option != null && option !== false;
721
+ }
722
+
219
723
  function generateTypescriptConfig(baseTemplateOrParams) {
220
724
  const params = typeof baseTemplateOrParams === "string" ? { baseTemplate: baseTemplateOrParams } : baseTemplateOrParams;
221
- const { baseTemplate, useConfigPackage, configStrategy = "stealth" } = params;
725
+ const {
726
+ baseTemplate,
727
+ useConfigPackage,
728
+ configStrategy = "stealth",
729
+ engine,
730
+ versions = {}
731
+ } = params;
222
732
  const isReact = baseTemplate === "react";
223
733
  const isR3f = baseTemplate === "r3f";
224
734
  const files = {};
225
735
  const devDependencies = {};
736
+ if (getEngineName(engine) === "node" && getEngineSpec(engine).version) {
737
+ const majorVersion = getEngineSpec(engine).version.split(".")[0];
738
+ devDependencies["@types/node"] = `^${majorVersion}.0.0`;
739
+ } else {
740
+ devDependencies["@types/node"] = "^22.0.0";
741
+ }
226
742
  if (isReact || isR3f) {
227
- devDependencies["@types/react"] = "^19.0.0";
228
- devDependencies["@types/react-dom"] = "^19.0.0";
743
+ assignResolvedPackageVersion(devDependencies, versions, "@types/react");
744
+ assignResolvedPackageVersion(devDependencies, versions, "@types/react-dom");
229
745
  }
230
746
  if (isR3f) {
231
- devDependencies["@types/three"] = "~0.175.0";
747
+ assignResolvedPackageVersion(devDependencies, versions, "@types/three", "~");
232
748
  }
233
749
  if (useConfigPackage) {
234
750
  devDependencies["@config/typescript"] = "workspace:*";
@@ -251,7 +767,10 @@ function generateTypescriptConfig(baseTemplateOrParams) {
251
767
  const tsConfig = {
252
768
  $schema: "https://json.schemastore.org/tsconfig",
253
769
  files: [],
254
- references: [{ path: "./.config/tsconfig.app.json" }, { path: "./.config/tsconfig.node.json" }]
770
+ references: [
771
+ { path: "./.config/tsconfig.app.json" },
772
+ { path: "./.config/tsconfig.node.json" }
773
+ ]
255
774
  };
256
775
  files["tsconfig.json"] = {
257
776
  type: "text",
@@ -357,6 +876,7 @@ function generateTypescriptConfig(baseTemplateOrParams) {
357
876
  return { files, devDependencies };
358
877
  }
359
878
 
879
+ const DEFAULT_LIBRARY_VERSION = "0.1.0";
360
880
  function generatePackageJson(params) {
361
881
  const {
362
882
  name,
@@ -370,14 +890,15 @@ function generatePackageJson(params) {
370
890
  workspaceDependencies
371
891
  } = params;
372
892
  const files = {};
373
- const packageManager = options.packageManager ?? "pnpm";
374
- const isPnpm = packageManager === "pnpm";
893
+ const packageManager = getPackageManagerSpec(options.packageManager);
894
+ const isPnpm = packageManager.name === "pnpm";
375
895
  const packageJson = {
376
896
  name,
377
897
  description: "Built with \u{1F339} create-krispya",
378
898
  type: "module"
379
899
  };
380
900
  if (isLibrary) {
901
+ packageJson.version = DEFAULT_LIBRARY_VERSION;
381
902
  packageJson.main = "./dist/index.mjs";
382
903
  packageJson.module = "./dist/index.mjs";
383
904
  if (language === "typescript") {
@@ -399,10 +920,16 @@ function generatePackageJson(params) {
399
920
  allDependencies[pkgName] = "workspace:*";
400
921
  }
401
922
  }
923
+ const allDevDependencies = { ...devDependencies };
924
+ const engine = getEngineSpec(options.engine);
925
+ if (getEngineName(engine) === "node" && engine.version) {
926
+ const majorVersion = engine.version.split(".")[0];
927
+ allDevDependencies["@types/node"] ??= `^${majorVersion}.0.0`;
928
+ }
402
929
  packageJson.scripts = scripts;
403
930
  packageJson.dependencies = sortKeys(allDependencies);
404
- if (Object.keys(devDependencies).length > 0) {
405
- packageJson.devDependencies = sortKeys(devDependencies);
931
+ if (Object.keys(allDevDependencies).length > 0) {
932
+ packageJson.devDependencies = sortKeys(allDevDependencies);
406
933
  }
407
934
  if (isLibrary && Object.keys(peerDependencies).length > 0) {
408
935
  packageJson.peerDependencies = sortKeys(peerDependencies);
@@ -410,25 +937,14 @@ function generatePackageJson(params) {
410
937
  const isMonorepoPackage = options.workspaceRoot != null;
411
938
  if (!isMonorepoPackage) {
412
939
  const engines = {};
413
- if (isPnpm) {
414
- const pnpmVersion = options.pnpmVersion ?? "10.11.0";
415
- const majorVersion = pnpmVersion.split(".")[0];
416
- engines.pnpm = `>=${majorVersion}.0.0`;
417
- packageJson.packageManager = `pnpm@${pnpmVersion}`;
418
- } else if (packageManager === "yarn") {
419
- const yarnVersion = options.yarnVersion ?? "4.6.0";
420
- const majorVersion = yarnVersion.split(".")[0];
421
- engines.yarn = `>=${majorVersion}.0.0`;
422
- packageJson.packageManager = `yarn@${yarnVersion}`;
423
- } else if (packageManager === "npm") {
424
- const npmVersion = options.npmVersion ?? "11.0.0";
425
- const majorVersion = npmVersion.split(".")[0];
426
- engines.npm = `>=${majorVersion}.0.0`;
427
- packageJson.packageManager = `npm@${npmVersion}`;
940
+ if (packageManager.version != null) {
941
+ const majorVersion = packageManager.version.split(".")[0];
942
+ engines[packageManager.name] = `>=${majorVersion}.0.0`;
943
+ packageJson.packageManager = formatPackageManager(packageManager);
428
944
  }
429
- if (options.nodeVersion) {
430
- const majorVersion = options.nodeVersion.split(".")[0];
431
- engines.node = `>=${majorVersion}.0.0`;
945
+ if (engine.version != null) {
946
+ const majorVersion = engine.version.split(".")[0];
947
+ engines[engine.name] = `>=${majorVersion}.0.0`;
432
948
  }
433
949
  if (Object.keys(engines).length > 0) {
434
950
  packageJson.engines = engines;
@@ -746,6 +1262,22 @@ function generateTestFiles(params) {
746
1262
  return files;
747
1263
  }
748
1264
 
1265
+ const COMMON_GITIGNORE_LINES = [
1266
+ "node_modules",
1267
+ "dist",
1268
+ "*.tsbuildinfo",
1269
+ ".env",
1270
+ ".env.*",
1271
+ "!.env.example"
1272
+ ];
1273
+ function generateGitignore(variant) {
1274
+ const lines = variant === "workspace-root" ? [...COMMON_GITIGNORE_LINES, ".DS_Store"] : COMMON_GITIGNORE_LINES;
1275
+ return {
1276
+ type: "text",
1277
+ content: lines.join("\n")
1278
+ };
1279
+ }
1280
+
749
1281
  function generateVscodeFiles$1(params) {
750
1282
  const { codeSnippets, vscodeSettings } = params;
751
1283
  const files = {};
@@ -768,7 +1300,7 @@ function generateVscodeFiles$1(params) {
768
1300
  );
769
1301
  files[".vscode/settings.json"] = {
770
1302
  type: "text",
771
- content: JSON.stringify(sortedSettings, null, " ")
1303
+ content: JSON.stringify(sortedSettings, null, 2)
772
1304
  };
773
1305
  }
774
1306
  return files;
@@ -1221,25 +1753,31 @@ function generateMonorepo(params) {
1221
1753
  linter,
1222
1754
  formatter,
1223
1755
  packageManager,
1224
- pnpmVersion,
1225
1756
  pnpmManageVersions,
1226
- nodeVersion,
1757
+ engine,
1758
+ versions = {},
1227
1759
  aiPlatforms
1228
1760
  } = params;
1229
1761
  const files = {};
1230
- const isPnpm = packageManager === "pnpm";
1762
+ const isPnpm = packageManager.name === "pnpm";
1231
1763
  const devDependencies = {};
1764
+ if (engine?.name === "node" && engine.version) {
1765
+ const majorVersion = engine.version.split(".")[0];
1766
+ devDependencies["@types/node"] = `^${majorVersion}.0.0`;
1767
+ } else {
1768
+ devDependencies["@types/node"] = "^22.0.0";
1769
+ }
1232
1770
  if (linter === "oxlint") {
1233
- devDependencies["oxlint"] = "^1.36.0";
1771
+ assignResolvedPackageVersion(devDependencies, versions, "oxlint");
1234
1772
  } else if (linter === "eslint") {
1235
- devDependencies["eslint"] = "^9.17.0";
1773
+ assignResolvedPackageVersion(devDependencies, versions, "eslint");
1236
1774
  } else if (linter === "biome") {
1237
- devDependencies["@biomejs/biome"] = "^1.9.4";
1775
+ assignResolvedPackageVersion(devDependencies, versions, "@biomejs/biome");
1238
1776
  }
1239
1777
  if (formatter === "oxfmt") {
1240
- devDependencies["oxfmt"] = "^0.21.0";
1778
+ assignResolvedPackageVersion(devDependencies, versions, "oxfmt");
1241
1779
  } else if (formatter === "prettier") {
1242
- devDependencies["prettier"] = "^3.4.2";
1780
+ assignResolvedPackageVersion(devDependencies, versions, "prettier");
1243
1781
  }
1244
1782
  const rootPackageJson = {
1245
1783
  name: "root",
@@ -1256,14 +1794,14 @@ function generateMonorepo(params) {
1256
1794
  devDependencies
1257
1795
  };
1258
1796
  const engines = {};
1259
- if (isPnpm && pnpmVersion) {
1260
- const majorVersion = pnpmVersion.split(".")[0];
1797
+ if (isPnpm && packageManager.version) {
1798
+ const majorVersion = packageManager.version.split(".")[0];
1261
1799
  engines.pnpm = `>=${majorVersion}.0.0`;
1262
- rootPackageJson.packageManager = `pnpm@${pnpmVersion}`;
1800
+ rootPackageJson.packageManager = formatPackageManager(packageManager);
1263
1801
  }
1264
- if (nodeVersion) {
1265
- const majorVersion = nodeVersion.split(".")[0];
1266
- engines.node = `>=${majorVersion}.0.0`;
1802
+ if (engine?.version) {
1803
+ const majorVersion = engine.version.split(".")[0];
1804
+ engines[engine.name] = `>=${majorVersion}.0.0`;
1267
1805
  }
1268
1806
  if (Object.keys(engines).length > 0) {
1269
1807
  rootPackageJson.engines = engines;
@@ -1277,13 +1815,7 @@ function generateMonorepo(params) {
1277
1815
  if (pnpmManageVersions) {
1278
1816
  workspaceLines.push("manage-package-manager-versions: true", "");
1279
1817
  }
1280
- workspaceLines.push(
1281
- "packages:",
1282
- ' - ".config/*"',
1283
- ' - "apps/*"',
1284
- ' - "packages/*"',
1285
- ""
1286
- );
1818
+ workspaceLines.push("packages:", ' - ".config/*"', ' - "apps/*"', ' - "packages/*"', "");
1287
1819
  workspaceLines.push("onlyBuiltDependencies:", " - esbuild");
1288
1820
  files["pnpm-workspace.yaml"] = {
1289
1821
  type: "text",
@@ -1328,8 +1860,9 @@ export default [...base];
1328
1860
  `
1329
1861
  };
1330
1862
  } else if (linter === "biome") {
1863
+ const biomeVersion = getResolvedPackageVersion(versions, "@biomejs/biome");
1331
1864
  const biomeConfig = {
1332
- $schema: "https://biomejs.dev/schemas/1.9.4/schema.json",
1865
+ $schema: `https://biomejs.dev/schemas/${biomeVersion}/schema.json`,
1333
1866
  vcs: {
1334
1867
  enabled: true,
1335
1868
  clientKind: "git",
@@ -1355,10 +1888,7 @@ export default [...base];
1355
1888
  } else if (formatter === "prettier") {
1356
1889
  generatePrettierConfigPackage(files);
1357
1890
  }
1358
- files[".gitignore"] = {
1359
- type: "text",
1360
- content: ["node_modules", "dist", "*.tsbuildinfo", ".DS_Store"].join("\n")
1361
- };
1891
+ files[".gitignore"] = generateGitignore("workspace-root");
1362
1892
  files[".gitattributes"] = {
1363
1893
  type: "text",
1364
1894
  content: `* text=auto eol=lf
@@ -1381,12 +1911,12 @@ This monorepo workspace was generated with create-krispya.
1381
1911
 
1382
1912
  ## Development Commands
1383
1913
 
1384
- - \`${packageManager} install\` to install all dependencies
1385
- - \`${packageManager} run dev\` to run all applications in development mode
1386
- - \`${packageManager} run build\` to build all packages and applications
1387
- - \`${packageManager} run test\` to run tests across the workspace
1388
- - \`${packageManager} run lint\` to lint all code
1389
- - \`${packageManager} run format\` to format all code
1914
+ - \`${packageManager.name} install\` to install all dependencies
1915
+ - \`${packageManager.name} run dev\` to run all applications in development mode
1916
+ - \`${packageManager.name} run build\` to build all packages and applications
1917
+ - \`${packageManager.name} run test\` to run tests across the workspace
1918
+ - \`${packageManager.name} run lint\` to lint all code
1919
+ - \`${packageManager.name} run format\` to format all code
1390
1920
 
1391
1921
  ## Adding Packages
1392
1922
 
@@ -1396,7 +1926,7 @@ To add a new package to this workspace, run create-krispya from this directory a
1396
1926
  if (aiPlatforms && aiPlatforms.length > 0) {
1397
1927
  generateAiFiles(files, {
1398
1928
  name,
1399
- packageManager,
1929
+ packageManager: packageManager.name,
1400
1930
  linter,
1401
1931
  formatter,
1402
1932
  isMonorepo: true,
@@ -1448,10 +1978,17 @@ function generateVscodeFiles(files, linter, formatter) {
1448
1978
  type: "text",
1449
1979
  content: JSON.stringify({ recommendations }, null, 2)
1450
1980
  };
1451
- files[".vscode/settings.json"] = {
1452
- type: "text",
1453
- content: JSON.stringify(settings, null, " ")
1454
- };
1981
+ const codeSnippets = {};
1982
+ if (recommendations.length > 0) {
1983
+ codeSnippets["vscode-extension-suggestion"] = recommendations;
1984
+ }
1985
+ Object.assign(
1986
+ files,
1987
+ generateVscodeFiles$1({
1988
+ codeSnippets,
1989
+ vscodeSettings: settings
1990
+ })
1991
+ );
1455
1992
  }
1456
1993
 
1457
1994
  const monorepo = {
@@ -1472,8 +2009,8 @@ function generateBiome(generator, options) {
1472
2009
  if (options == null || !options.linter && !options.formatter) {
1473
2010
  return;
1474
2011
  }
1475
- const version = generator.versions.biome ?? "2.0.0";
1476
- generator.addDevDependency("@biomejs/biome", `^${version}`);
2012
+ const version = generator.getVersion("@biomejs/biome");
2013
+ generator.addDevDependency("@biomejs/biome");
1477
2014
  const { rules } = defaultLinterConfig;
1478
2015
  const biomeConfig = {
1479
2016
  $schema: `https://biomejs.dev/schemas/${version}/schema.json`
@@ -1529,10 +2066,7 @@ function generateBiome(generator, options) {
1529
2066
  generator.addScript("lint", "biome lint --config-path .config .");
1530
2067
  }
1531
2068
  if (options.formatter) {
1532
- generator.addScript(
1533
- "format",
1534
- "biome format --config-path .config --write ."
1535
- );
2069
+ generator.addScript("format", "biome format --config-path .config --write .");
1536
2070
  }
1537
2071
  generator.addVscodeSetting("biome.linter.configPath", ".config/biome.json");
1538
2072
  } else {
@@ -1552,9 +2086,7 @@ function generateBiome(generator, options) {
1552
2086
  if (options.formatter) roles.push("formatter");
1553
2087
  generator.inject(
1554
2088
  "readme-tools",
1555
- `[Biome](https://biomejs.dev/) - Fast ${roles.join(
1556
- " and "
1557
- )} for JavaScript and TypeScript`
2089
+ `[Biome](https://biomejs.dev/) - Fast ${roles.join(" and ")} for JavaScript and TypeScript`
1558
2090
  );
1559
2091
  generator.inject("vscode-extension-suggestion", "biomejs.biome");
1560
2092
  generator.addVscodeSetting("biome.enabled", true);
@@ -1567,7 +2099,7 @@ function generateDrei(generator, options) {
1567
2099
  if (options == null) {
1568
2100
  return;
1569
2101
  }
1570
- generator.addDependency("@react-three/drei", "^10.0.0");
2102
+ generator.addDependency("@react-three/drei");
1571
2103
  generator.inject("import", `import { Environment } from "@react-three/drei"`);
1572
2104
  generator.inject("scene", '<Environment background preset="city" />');
1573
2105
  generator.inject(
@@ -1576,19 +2108,11 @@ function generateDrei(generator, options) {
1576
2108
  );
1577
2109
  }
1578
2110
 
1579
- function getLanguageFromTemplate(template) {
1580
- return template.endsWith("-js") ? "javascript" : "typescript";
1581
- }
1582
- function getBaseTemplate(template) {
1583
- return template.replace("-js", "");
1584
- }
1585
-
1586
2111
  function toEslintLevel(level) {
1587
2112
  return level;
1588
2113
  }
1589
2114
  function generateEslint(generator, options) {
1590
- const version = generator.versions.eslint ?? "9.17.0";
1591
- generator.addDevDependency("eslint", `^${version}`);
2115
+ generator.addDevDependency("eslint");
1592
2116
  const template = generator.options.template ?? "vanilla";
1593
2117
  const baseTemplate = getBaseTemplate(template);
1594
2118
  const isTypescript = getLanguageFromTemplate(template) === "typescript";
@@ -1597,12 +2121,12 @@ function generateEslint(generator, options) {
1597
2121
  const imports = ['import js from "@eslint/js"'];
1598
2122
  const configs = ["js.configs.recommended"];
1599
2123
  if (isTypescript) {
1600
- generator.addDevDependency("typescript-eslint", "^8.18.0");
2124
+ generator.addDevDependency("typescript-eslint");
1601
2125
  imports.push('import tseslint from "typescript-eslint"');
1602
2126
  configs.push("...tseslint.configs.recommended");
1603
2127
  }
1604
2128
  if (isReact) {
1605
- generator.addDevDependency("eslint-plugin-react-hooks", "^5.1.0");
2129
+ generator.addDevDependency("eslint-plugin-react-hooks");
1606
2130
  imports.push('import reactHooks from "eslint-plugin-react-hooks"');
1607
2131
  }
1608
2132
  const ignoresArray = JSON.stringify(defaultLinterConfig.ignorePatterns);
@@ -1704,7 +2228,7 @@ export function Box(props: ThreeElements['mesh']) {
1704
2228
  }
1705
2229
 
1706
2230
  function generateGithubPages(generator, options) {
1707
- if (options === false || (generator.options.packageManager ?? "npm") != "npm") {
2231
+ if (options === false || getPackageManagerName(generator.options.packageManager) !== "npm") {
1708
2232
  return;
1709
2233
  }
1710
2234
  generator.addFile(".github/workflows/gh-pages.yml", {
@@ -1768,7 +2292,7 @@ function generateHandle(generator, options) {
1768
2292
  if (options == null) {
1769
2293
  return;
1770
2294
  }
1771
- generator.addDependency("@react-three/handle", "^6.6.16");
2295
+ generator.addDependency("@react-three/handle");
1772
2296
  generator.inject(
1773
2297
  "readme-libraries",
1774
2298
  `[@react-three/handle](https://pmndrs.github.io/xr/docs/handles/introduction) - interactive controls and handles for your 3D objects`
@@ -1779,7 +2303,7 @@ function generateKoota(generator, options) {
1779
2303
  if (options == null) {
1780
2304
  return;
1781
2305
  }
1782
- generator.addDependency("koota", "^0.4.0");
2306
+ generator.addDependency("koota");
1783
2307
  generator.inject(
1784
2308
  "readme-libraries",
1785
2309
  `[koota](https://github.com/pmndrs/koota) - ECS-based state management library optimized for real-time apps, games, and XR experiences`
@@ -1790,7 +2314,7 @@ function generateLeva(generator, options) {
1790
2314
  if (options == null) {
1791
2315
  return;
1792
2316
  }
1793
- generator.addDependency("leva", "^0.10.0");
2317
+ generator.addDependency("leva");
1794
2318
  generator.inject(
1795
2319
  "readme-libraries",
1796
2320
  `[leva](https://github.com/pmndrs/leva) - HTML GUI panel for React with lightweight, beautiful and extensible controls`
@@ -1808,7 +2332,7 @@ function generateOffscreen(generator, options) {
1808
2332
  );
1809
2333
  return;
1810
2334
  }
1811
- generator.addDependency("@react-three/offscreen", "^0.0.8");
2335
+ generator.addDependency("@react-three/offscreen");
1812
2336
  generator.inject(
1813
2337
  "readme-libraries",
1814
2338
  `[@react-three/offscreen](https://github.com/pmndrs/offscreen) - Offload your scene to a worker thread for better performance`
@@ -1818,13 +2342,12 @@ function generateOffscreen(generator, options) {
1818
2342
  function generateOxfmt(generator, options) {
1819
2343
  const isMonorepo = generator.options.workspaceRoot != null;
1820
2344
  if (isMonorepo) {
1821
- generator.addDevDependency("@config/oxfmt", "workspace:*");
2345
+ generator.addDevDependency("@config/oxfmt", { version: "workspace:*" });
1822
2346
  const configPath = "node_modules/@config/oxfmt/base.json";
1823
2347
  generator.addScript("format", `oxfmt -c ${configPath} --write .`);
1824
2348
  generator.addVscodeSetting("oxc.fmt.configPath", configPath);
1825
2349
  } else {
1826
- const version = generator.versions.oxfmt ?? "0.1.0";
1827
- generator.addDevDependency("oxfmt", `^${version}`);
2350
+ generator.addDevDependency("oxfmt");
1828
2351
  const isStealth = generator.isStealthConfig();
1829
2352
  if (isStealth) {
1830
2353
  generator.addFile(".config/oxfmt.json", {
@@ -1870,13 +2393,12 @@ function generateOxlint(generator, options) {
1870
2393
  const isReact = baseTemplate === "react" || baseTemplate === "r3f";
1871
2394
  const isMonorepo = generator.options.workspaceRoot != null;
1872
2395
  if (isMonorepo) {
1873
- generator.addDevDependency("@config/oxlint", "workspace:*");
2396
+ generator.addDevDependency("@config/oxlint", { version: "workspace:*" });
1874
2397
  const configPath = isReact ? "node_modules/@config/oxlint/react.json" : "node_modules/@config/oxlint/base.json";
1875
2398
  generator.addScript("lint", `oxlint -c ${configPath}`);
1876
2399
  generator.addVscodeSetting("oxc.configPath", configPath);
1877
2400
  } else {
1878
- const version = generator.versions.oxlint ?? "0.16.0";
1879
- generator.addDevDependency("oxlint", `^${version}`);
2401
+ generator.addDevDependency("oxlint");
1880
2402
  const isStealth = generator.isStealthConfig();
1881
2403
  const { rules } = defaultLinterConfig;
1882
2404
  const plugins = ["unicorn", "typescript", "oxc"];
@@ -1937,7 +2459,7 @@ function generatePostprocessing(generator, options) {
1937
2459
  );
1938
2460
  return;
1939
2461
  }
1940
- generator.addDependency("@react-three/postprocessing", "^3.0.4");
2462
+ generator.addDependency("@react-three/postprocessing");
1941
2463
  generator.inject(
1942
2464
  "readme-libraries",
1943
2465
  `[@react-three/postprocessing](https://react-postprocessing.docs.pmnd.rs/) - Post-processing effects for @react-three/fiber`
@@ -1945,18 +2467,14 @@ function generatePostprocessing(generator, options) {
1945
2467
  }
1946
2468
 
1947
2469
  function generatePrettier(generator, options) {
1948
- const version = generator.versions.prettier ?? "3.4.2";
1949
- generator.addDevDependency("prettier", `^${version}`);
2470
+ generator.addDevDependency("prettier");
1950
2471
  const isStealth = generator.isStealthConfig();
1951
2472
  if (isStealth) {
1952
2473
  generator.addFile(".config/prettier.json", {
1953
2474
  type: "text",
1954
2475
  content: JSON.stringify(defaultPrettierConfig, null, 2)
1955
2476
  });
1956
- generator.addScript(
1957
- "format",
1958
- "prettier --config .config/prettier.json --write ."
1959
- );
2477
+ generator.addScript("format", "prettier --config .config/prettier.json --write .");
1960
2478
  generator.addVscodeSetting("prettier.configPath", ".config/prettier.json");
1961
2479
  } else {
1962
2480
  generator.addFile(".prettierrc", {
@@ -1965,22 +2483,16 @@ function generatePrettier(generator, options) {
1965
2483
  });
1966
2484
  generator.addScript("format", "prettier --write .");
1967
2485
  }
1968
- generator.inject(
1969
- "readme-tools",
1970
- "[Prettier](https://prettier.io/) - Opinionated code formatter"
1971
- );
2486
+ generator.inject("readme-tools", "[Prettier](https://prettier.io/) - Opinionated code formatter");
1972
2487
  generator.inject("vscode-extension-suggestion", "esbenp.prettier-vscode");
1973
- generator.addVscodeSetting(
1974
- "editor.defaultFormatter",
1975
- "esbenp.prettier-vscode"
1976
- );
2488
+ generator.addVscodeSetting("editor.defaultFormatter", "esbenp.prettier-vscode");
1977
2489
  }
1978
2490
 
1979
2491
  function generateRapier(generator, options) {
1980
2492
  if (options == null) {
1981
2493
  return;
1982
2494
  }
1983
- generator.addDependency("@react-three/rapier", "^2.1.0");
2495
+ generator.addDependency("@react-three/rapier");
1984
2496
  generator.inject(
1985
2497
  "readme-libraries",
1986
2498
  `[@react-three/rapier](https://github.com/pmndrs/react-three-rapier) - Physics based on Rapier for your @react-three/fiber scene`
@@ -2079,8 +2591,12 @@ function generateProvidersModule(generator) {
2079
2591
  const wrappedComponents = resolvedProviders.filter(
2080
2592
  (provider) => provider.type === "wrapped-jsx"
2081
2593
  );
2082
- const inlineComponents = resolvedProviders.filter((provider) => provider.type === "inline-jsx");
2083
- const layoutEffects = resolvedProviders.filter((provider) => provider.type === "layout-effect");
2594
+ const inlineComponents = resolvedProviders.filter(
2595
+ (provider) => provider.type === "inline-jsx"
2596
+ );
2597
+ const layoutEffects = resolvedProviders.filter(
2598
+ (provider) => provider.type === "layout-effect"
2599
+ );
2084
2600
  const declaredProps = providerProps.map((prop) => `${prop.declaredPropName} = ${prop.declaredPropDefaultValue}`).join(", ");
2085
2601
  const declaredTypes = providerProps.map((prop) => `${prop.declaredPropName}?: ${prop.declaredPropType}`).join("; ");
2086
2602
  const reactImports = ["type ReactNode"];
@@ -2155,7 +2671,7 @@ function generateTriplex(generator, options) {
2155
2671
  }
2156
2672
 
2157
2673
  function generateTsdown(generator) {
2158
- generator.addDevDependency("tsdown", "^0.12.0");
2674
+ generator.addDevDependency("tsdown");
2159
2675
  const template = generator.options.template ?? "vanilla";
2160
2676
  const baseTemplate = getBaseTemplate(template);
2161
2677
  const language = getLanguageFromTemplate(template);
@@ -2191,7 +2707,7 @@ function generateUikit(generator, options) {
2191
2707
  if (options == null) {
2192
2708
  return;
2193
2709
  }
2194
- generator.addDependency("@react-three/uikit", "^0.8.15");
2710
+ generator.addDependency("@react-three/uikit");
2195
2711
  generator.inject(
2196
2712
  "readme-libraries",
2197
2713
  `[@react-three/uikit](https://pmndrs.github.io/uikit/docs/) - UI primitives for React Three Fiber`
@@ -2199,7 +2715,7 @@ function generateUikit(generator, options) {
2199
2715
  }
2200
2716
 
2201
2717
  function generateUnbuild(generator) {
2202
- generator.addDevDependency("unbuild", "^3.5.0");
2718
+ generator.addDevDependency("unbuild");
2203
2719
  const template = generator.options.template ?? "vanilla";
2204
2720
  const baseTemplate = getBaseTemplate(template);
2205
2721
  const language = getLanguageFromTemplate(template);
@@ -2244,15 +2760,14 @@ function generateUnbuild(generator) {
2244
2760
  }
2245
2761
 
2246
2762
  function generateVitest(generator) {
2247
- const version = generator.versions.vitest ?? "4.0.0";
2248
- generator.addDevDependency("vitest", `^${version}`);
2763
+ generator.addDevDependency("vitest");
2249
2764
  const template = generator.options.template ?? "vanilla";
2250
2765
  const baseTemplate = getBaseTemplate(template);
2251
2766
  const isReact = baseTemplate === "react" || baseTemplate === "r3f";
2252
2767
  if (isReact) {
2253
- generator.addDevDependency("@testing-library/react", "^16.2.0");
2254
- generator.addDevDependency("@testing-library/dom", "^10.4.0");
2255
- generator.addDevDependency("jsdom", "^26.0.0");
2768
+ generator.addDevDependency("@testing-library/react");
2769
+ generator.addDevDependency("@testing-library/dom");
2770
+ generator.addDevDependency("jsdom");
2256
2771
  }
2257
2772
  if (isReact) {
2258
2773
  generator.configureVite({ test: { environment: "jsdom" } });
@@ -2265,7 +2780,7 @@ function generateVitest(generator) {
2265
2780
  }
2266
2781
 
2267
2782
  function generateViverse(generator, options) {
2268
- if (options == null || (generator.options.packageManager ?? "npm") != "npm") {
2783
+ if (options == null || getPackageManagerName(generator.options.packageManager) !== "npm") {
2269
2784
  return;
2270
2785
  }
2271
2786
  generator.addFile(".github/workflows/viverse.yml", {
@@ -2323,7 +2838,7 @@ jobs:
2323
2838
 
2324
2839
  `
2325
2840
  });
2326
- generator.addDependency("@viverse/cli", "^0.9.5-beta.8");
2841
+ generator.addDependency("@viverse/cli");
2327
2842
  generator.inject(
2328
2843
  "readme-start",
2329
2844
  `A GitHub CI/CD workflow for publishing to Viverse is configured.
@@ -2350,8 +2865,8 @@ function generateXr(generator, options) {
2350
2865
  if (options === true) {
2351
2866
  options = {};
2352
2867
  }
2353
- generator.addDependency("@react-three/xr", "^6.6.16");
2354
- generator.addDependency("@vitejs/plugin-basic-ssl", "^2.0.0");
2868
+ generator.addDependency("@react-three/xr");
2869
+ generator.addDependency("@vitejs/plugin-basic-ssl");
2355
2870
  generator.inject("import", "import { XR, createXRStore } from '@react-three/xr'");
2356
2871
  generator.inject(
2357
2872
  `global-start`,
@@ -2408,7 +2923,7 @@ function generateZustand(generator, options) {
2408
2923
  if (options == null) {
2409
2924
  return;
2410
2925
  }
2411
- generator.addDependency("zustand", "^5.0.3");
2926
+ generator.addDependency("zustand");
2412
2927
  generator.inject(
2413
2928
  "readme-libraries",
2414
2929
  `[zustand](https://zustand.docs.pmnd.rs/) - small, fast and scalable state-management solution`
@@ -2446,255 +2961,6 @@ function merge(target, modification) {
2446
2961
  return modification;
2447
2962
  }
2448
2963
 
2449
- async function getLatestNpmVersion(packageName, fallback) {
2450
- try {
2451
- const response = await fetch(
2452
- `https://registry.npmjs.org/${packageName}/latest`
2453
- );
2454
- const data = await response.json();
2455
- return data.version;
2456
- } catch {
2457
- return fallback;
2458
- }
2459
- }
2460
- async function getLatestPnpmVersion() {
2461
- return getLatestNpmVersion("pnpm", "10.11.0");
2462
- }
2463
- async function getLatestYarnVersion() {
2464
- return getLatestNpmVersion("yarn", "4.6.0");
2465
- }
2466
- async function getLatestNpmCliVersion() {
2467
- return getLatestNpmVersion("npm", "11.0.0");
2468
- }
2469
- async function getLatestNodeVersion() {
2470
- try {
2471
- const response = await fetch("https://nodejs.org/dist/index.json");
2472
- const data = await response.json();
2473
- const ltsVersion = data.find((v) => v.lts);
2474
- if (ltsVersion) {
2475
- return ltsVersion.version.replace(/^v/, "");
2476
- }
2477
- return "22.0.0";
2478
- } catch {
2479
- return "22.0.0";
2480
- }
2481
- }
2482
- function validateNameSegment(segment, label) {
2483
- if (!segment.length) {
2484
- return `${label} is required`;
2485
- }
2486
- if (!/^[a-z0-9-]+$/.test(segment)) {
2487
- return `${label} must be lowercase and contain only letters, numbers, and hyphens`;
2488
- }
2489
- if (segment.startsWith("-") || segment.endsWith("-")) {
2490
- return `${label} cannot start or end with a hyphen`;
2491
- }
2492
- if (segment.includes("--")) {
2493
- return `${label} cannot contain consecutive hyphens`;
2494
- }
2495
- return void 0;
2496
- }
2497
- function validatePackageName(name) {
2498
- if (!name.length) {
2499
- return "Package name is required";
2500
- }
2501
- if (name.includes("..") || name.includes("\\")) {
2502
- return "Package name cannot contain path traversal sequences";
2503
- }
2504
- if (name.startsWith("@")) {
2505
- const slashIndex = name.indexOf("/");
2506
- if (slashIndex === -1) {
2507
- return "Scoped package name must include a package name after the scope (e.g., @scope/name)";
2508
- }
2509
- if (name.indexOf("/", slashIndex + 1) !== -1) {
2510
- return "Package name can only have one slash for scoped packages";
2511
- }
2512
- const scope = name.slice(1, slashIndex);
2513
- const packageName = name.slice(slashIndex + 1);
2514
- const scopeError = validateNameSegment(scope, "Scope");
2515
- if (scopeError) return scopeError;
2516
- const nameError = validateNameSegment(packageName, "Package name");
2517
- if (nameError) return nameError;
2518
- return void 0;
2519
- }
2520
- if (name.includes("/")) {
2521
- return "Unscoped package name cannot contain slashes. Use @scope/name format for scoped packages";
2522
- }
2523
- return validateNameSegment(name, "Package name");
2524
- }
2525
- function parseWorkspaceYamlContent(content) {
2526
- const directories = [];
2527
- let inPackagesSection = false;
2528
- for (const line of content.split("\n")) {
2529
- const trimmed = line.trim();
2530
- if (trimmed === "packages:") {
2531
- inPackagesSection = true;
2532
- continue;
2533
- }
2534
- if (inPackagesSection && trimmed && !line.startsWith(" ") && !line.startsWith(" ") && !trimmed.startsWith("-")) {
2535
- break;
2536
- }
2537
- if (inPackagesSection && trimmed.startsWith("-")) {
2538
- const entry = trimmed.slice(1).trim().replace(/^["']|["']$/g, "").replace(/^\.\//, "").replace(/\/\*.*$/, "");
2539
- if (entry && !entry.startsWith(".")) {
2540
- directories.push(entry);
2541
- }
2542
- }
2543
- }
2544
- return directories;
2545
- }
2546
- async function pathExists(path) {
2547
- try {
2548
- await access(path, constants.F_OK);
2549
- return true;
2550
- } catch {
2551
- return false;
2552
- }
2553
- }
2554
- function detectLinterFromScript(script) {
2555
- if (!script) return void 0;
2556
- if (script.includes("oxlint")) return "oxlint";
2557
- if (script.includes("eslint")) return "eslint";
2558
- if (script.includes("biome check") || script.includes("biome lint")) return "biome";
2559
- return void 0;
2560
- }
2561
- function detectFormatterFromScript(script) {
2562
- if (!script) return void 0;
2563
- if (script.includes("prettier")) return "prettier";
2564
- if (script.includes("oxfmt")) return "oxfmt";
2565
- if (script.includes("biome format")) return "biome";
2566
- return void 0;
2567
- }
2568
- async function detectLinterFromConfig(root) {
2569
- if (await pathExists(join(root, ".config/oxlint"))) return "oxlint";
2570
- if (await pathExists(join(root, ".config/eslint"))) return "eslint";
2571
- if (await pathExists(join(root, "biome.json"))) {
2572
- try {
2573
- const content = await readFile(join(root, "biome.json"), "utf-8");
2574
- const config = JSON.parse(content);
2575
- if (config.linter?.enabled !== false) return "biome";
2576
- } catch {
2577
- return "biome";
2578
- }
2579
- }
2580
- return void 0;
2581
- }
2582
- async function detectFormatterFromConfig(root) {
2583
- if (await pathExists(join(root, ".config/prettier"))) return "prettier";
2584
- if (await pathExists(join(root, ".config/oxfmt"))) return "oxfmt";
2585
- if (await pathExists(join(root, "biome.json"))) {
2586
- try {
2587
- const content = await readFile(join(root, "biome.json"), "utf-8");
2588
- const config = JSON.parse(content);
2589
- if (config.formatter?.enabled !== false) return "biome";
2590
- } catch {
2591
- return "biome";
2592
- }
2593
- }
2594
- return void 0;
2595
- }
2596
- function detectLinterFromDeps(devDeps) {
2597
- if (!devDeps) return void 0;
2598
- if (devDeps["@biomejs/biome"]) return "biome";
2599
- if (devDeps.eslint) return "eslint";
2600
- if (devDeps.oxlint) return "oxlint";
2601
- return void 0;
2602
- }
2603
- function detectFormatterFromDeps(devDeps) {
2604
- if (!devDeps) return void 0;
2605
- if (devDeps["@biomejs/biome"]) return "biome";
2606
- if (devDeps.prettier) return "prettier";
2607
- if (devDeps.oxfmt) return "oxfmt";
2608
- return void 0;
2609
- }
2610
- async function detectTooling(root) {
2611
- try {
2612
- const pkgPath = join(root, "package.json");
2613
- const content = await readFile(pkgPath, "utf-8");
2614
- const pkg = JSON.parse(content);
2615
- const linter = detectLinterFromScript(pkg.scripts?.lint) ?? await detectLinterFromConfig(root) ?? detectLinterFromDeps(pkg.devDependencies);
2616
- const formatter = detectFormatterFromScript(pkg.scripts?.format) ?? await detectFormatterFromConfig(root) ?? detectFormatterFromDeps(pkg.devDependencies);
2617
- return { linter, formatter };
2618
- } catch {
2619
- return { linter: void 0, formatter: void 0 };
2620
- }
2621
- }
2622
- function generateRandomName() {
2623
- const adjectives = [
2624
- "red",
2625
- "blue",
2626
- "green",
2627
- "yellow",
2628
- "purple",
2629
- "orange",
2630
- "pink",
2631
- "black",
2632
- "white",
2633
- "tiny",
2634
- "big",
2635
- "small",
2636
- "large",
2637
- "huge",
2638
- "giant",
2639
- "mini",
2640
- "mega",
2641
- "super",
2642
- "happy",
2643
- "sad",
2644
- "angry",
2645
- "calm",
2646
- "quiet",
2647
- "loud",
2648
- "silent",
2649
- "noisy",
2650
- "shiny",
2651
- "dull",
2652
- "bright",
2653
- "dark",
2654
- "fuzzy",
2655
- "smooth",
2656
- "rough",
2657
- "soft"
2658
- ];
2659
- const nouns = [
2660
- "apple",
2661
- "banana",
2662
- "cherry",
2663
- "date",
2664
- "elderberry",
2665
- "fig",
2666
- "grape",
2667
- "honeydew",
2668
- "cat",
2669
- "dog",
2670
- "elephant",
2671
- "fox",
2672
- "giraffe",
2673
- "horse",
2674
- "iguana",
2675
- "jaguar",
2676
- "mountain",
2677
- "river",
2678
- "ocean",
2679
- "desert",
2680
- "forest",
2681
- "jungle",
2682
- "meadow",
2683
- "valley",
2684
- "star",
2685
- "moon",
2686
- "sun",
2687
- "planet",
2688
- "comet",
2689
- "asteroid",
2690
- "galaxy",
2691
- "universe"
2692
- ];
2693
- const randomAdjective = adjectives[Math.floor(Math.random() * adjectives.length)];
2694
- const randomNoun = nouns[Math.floor(Math.random() * nouns.length)];
2695
- return `${randomAdjective}-${randomNoun}`;
2696
- }
2697
-
2698
2964
  function generate(options) {
2699
2965
  const clonedOptions = structuredClone(options);
2700
2966
  const template = clonedOptions.template ?? "vanilla";
@@ -2716,16 +2982,16 @@ function generate(options) {
2716
2982
  const devDependencies = {};
2717
2983
  const peerDependencies = {};
2718
2984
  if (!isLibrary) {
2719
- devDependencies.vite = versions.vite ? `^${versions.vite}` : "^6.3.4";
2985
+ assignResolvedPackageVersion(devDependencies, versions, "vite");
2720
2986
  }
2721
2987
  if (isReact || isR3f) {
2722
2988
  if (isLibrary) {
2723
2989
  peerDependencies["react"] = "^18.0.0 || ^19.0.0";
2724
2990
  peerDependencies["react-dom"] = "^18.0.0 || ^19.0.0";
2725
2991
  } else {
2726
- dependencies["react"] = "^19.0.0";
2727
- dependencies["react-dom"] = "^19.0.0";
2728
- devDependencies["@vitejs/plugin-react"] = "^4.4.1";
2992
+ assignResolvedPackageVersion(dependencies, versions, "react");
2993
+ assignResolvedPackageVersion(dependencies, versions, "react-dom");
2994
+ assignResolvedPackageVersion(devDependencies, versions, "@vitejs/plugin-react");
2729
2995
  }
2730
2996
  }
2731
2997
  if (isR3f) {
@@ -2733,15 +2999,17 @@ function generate(options) {
2733
2999
  peerDependencies["three"] = ">=0.150.0";
2734
3000
  peerDependencies["@react-three/fiber"] = "^8.0.0 || ^9.0.0";
2735
3001
  } else {
2736
- dependencies["three"] = "~0.175.0";
2737
- dependencies["@react-three/fiber"] = "^9.0.0";
3002
+ assignResolvedPackageVersion(dependencies, versions, "three", "~");
3003
+ assignResolvedPackageVersion(dependencies, versions, "@react-three/fiber");
2738
3004
  }
2739
3005
  }
2740
3006
  if (language === "typescript") {
2741
3007
  const tsResult = generateTypescriptConfig({
2742
3008
  baseTemplate,
2743
3009
  useConfigPackage: clonedOptions.workspaceRoot != null,
2744
- configStrategy: clonedOptions.configStrategy
3010
+ configStrategy: clonedOptions.configStrategy,
3011
+ engine: clonedOptions.engine,
3012
+ versions
2745
3013
  });
2746
3014
  Object.assign(files, tsResult.files);
2747
3015
  Object.assign(devDependencies, tsResult.devDependencies);
@@ -2753,9 +3021,7 @@ function generate(options) {
2753
3021
  build: "vite build"
2754
3022
  };
2755
3023
  if (!isLibrary && (isReact || isR3f)) {
2756
- codeSnippets["vite-config-import"] = [
2757
- "import react from '@vitejs/plugin-react'"
2758
- ];
3024
+ codeSnippets["vite-config-import"] = ["import react from '@vitejs/plugin-react'"];
2759
3025
  }
2760
3026
  if (!isLibrary && isR3f) {
2761
3027
  codeSnippets["import"] = [`import { Canvas } from "@react-three/fiber"`];
@@ -2775,16 +3041,29 @@ function generate(options) {
2775
3041
  const generator = {
2776
3042
  options: clonedOptions,
2777
3043
  versions,
3044
+ getVersion(name2) {
3045
+ return getResolvedPackageVersion(versions, name2);
3046
+ },
2778
3047
  isStealthConfig() {
2779
3048
  return (clonedOptions.configStrategy ?? "stealth") === "stealth";
2780
3049
  },
2781
- addDependency(name2, semver) {
2782
- dependencies[name2];
2783
- dependencies[name2] = semver;
3050
+ addDependency(name2, options2) {
3051
+ if (dependencies[name2] != null) {
3052
+ return;
3053
+ }
3054
+ dependencies[name2] = resolveDependencySemver(name2, versions, options2);
3055
+ },
3056
+ addDevDependency(name2, options2) {
3057
+ if (devDependencies[name2] != null) {
3058
+ return;
3059
+ }
3060
+ devDependencies[name2] = resolveDependencySemver(name2, versions, options2);
2784
3061
  },
2785
- addDevDependency(name2, semver) {
2786
- devDependencies[name2];
2787
- devDependencies[name2] = semver;
3062
+ addPeerDependency(name2, semver) {
3063
+ if (peerDependencies[name2] != null) {
3064
+ return;
3065
+ }
3066
+ peerDependencies[name2] = semver;
2788
3067
  },
2789
3068
  addFile(path, content) {
2790
3069
  files[path] = content;
@@ -2833,11 +3112,8 @@ function generate(options) {
2833
3112
  } else if (libraryBundler === "tsdown") {
2834
3113
  generateTsdown(generator);
2835
3114
  }
2836
- const packageManager2 = clonedOptions.packageManager ?? "pnpm";
2837
- generator.addScript(
2838
- "release",
2839
- `${packageManager2} run build && ${packageManager2} publish`
2840
- );
3115
+ const packageManager2 = getPackageManagerName(clonedOptions.packageManager);
3116
+ generator.addScript("release", `${packageManager2} run build && ${packageManager2} publish`);
2841
3117
  }
2842
3118
  const testing = clonedOptions.testing ?? (isLibrary ? "vitest" : "none");
2843
3119
  if (testing === "vitest") {
@@ -2876,7 +3152,7 @@ function generate(options) {
2876
3152
  if (!isLibrary) {
2877
3153
  files["vite.config.ts"] = generateViteConfig({ viteConfig, codeSnippets });
2878
3154
  }
2879
- const packageManager = options.packageManager ?? "pnpm";
3155
+ const packageManager = getPackageManagerName(options.packageManager);
2880
3156
  files["README.md"] = generateReadme({
2881
3157
  name,
2882
3158
  baseTemplate,
@@ -2924,16 +3200,13 @@ function generate(options) {
2924
3200
  Object.assign(files, generateVscodeFiles$1({ codeSnippets, vscodeSettings }));
2925
3201
  }
2926
3202
  if (!isMonorepoPackage) {
2927
- files[".gitignore"] = {
2928
- type: "text",
2929
- content: ["node_modules", "dist", "*.tsbuildinfo"].join("\n")
2930
- };
3203
+ files[".gitignore"] = generateGitignore("standalone");
2931
3204
  files[".gitattributes"] = { type: "text", content: GitAttributes };
2932
3205
  }
2933
3206
  if (!isMonorepoPackage && clonedOptions.aiPlatforms?.length) {
2934
3207
  generateAiFiles(files, {
2935
3208
  name,
2936
- packageManager: clonedOptions.packageManager ?? "pnpm",
3209
+ packageManager: getPackageManagerName(clonedOptions.packageManager),
2937
3210
  linter: clonedOptions.linter ?? "oxlint",
2938
3211
  formatter: clonedOptions.formatter ?? "prettier",
2939
3212
  isMonorepo: false,
@@ -2943,5 +3216,11 @@ function generate(options) {
2943
3216
  }
2944
3217
  return files;
2945
3218
  }
3219
+ function resolveDependencySemver(name, versions, options = {}) {
3220
+ if (options.version != null) {
3221
+ return options.version;
3222
+ }
3223
+ return formatResolvedPackageVersion(versions, name, options.prefix);
3224
+ }
2946
3225
 
2947
- export { ALL_AI_PLATFORMS as A, getLanguageFromTemplate as a, generateRandomName as b, generateAiFiles as c, detectTooling as d, generateVscodeFiles as e, generateTypescriptConfigPackage as f, getBaseTemplate as g, generateOxlintConfigPackage as h, generateEslintConfigPackage as i, generateOxfmtConfigPackage as j, generatePrettierConfigPackage as k, getLatestNpmVersion as l, generate as m, getLatestPnpmVersion as n, getLatestYarnVersion as o, parseWorkspaceYamlContent as p, getLatestNpmCliVersion as q, getLatestNodeVersion as r, AI_PLATFORM_LABELS as s, AI_PLATFORM_HINTS as t, generateMonorepo as u, validatePackageName as v, monorepo as w };
3226
+ export { ALL_AI_PLATFORMS as A, validatePackageName as B, generateMonorepo as C, getLatestNodeVersion as D, getLatestNpmCliVersion as E, getLatestNpmVersion as F, getLatestPnpmVersion as G, getLatestYarnVersion as H, monorepo as I, getBaseTemplate as a, getLanguageFromTemplate as b, getPackageManagerName as c, generateRandomName as d, detectTooling as e, generateAiFiles as f, getEngineName as g, generateVscodeFiles as h, generateTypescriptConfigPackage as i, generateOxlintConfigPackage as j, generateEslintConfigPackage as k, generateOxfmtConfigPackage as l, generatePrettierConfigPackage as m, generateGitignore as n, getResolvedPackageVersion as o, parseWorkspaceYamlContent as p, formatResolvedPackageVersion as q, resolveMonorepoRootPackageVersions as r, resolvePackageManager as s, resolveEngine as t, resolveProjectPackageVersions as u, generate as v, parsePackageManager as w, parseEngine as x, AI_PLATFORM_LABELS as y, AI_PLATFORM_HINTS as z };