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,9 +1,9 @@
1
1
  'use strict';
2
2
 
3
- const color = require('chalk');
4
3
  const promises = require('fs/promises');
5
4
  const fs = require('fs');
6
5
  const path = require('path');
6
+ const color = require('chalk');
7
7
 
8
8
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
9
9
 
@@ -91,7 +91,7 @@ const GitAttributes = [
91
91
  ].join("\n");
92
92
  const defaultFormatterConfig = {
93
93
  printWidth: 102,
94
- tabWidth: 4,
94
+ tabWidth: 2,
95
95
  useTabs: false,
96
96
  semi: true,
97
97
  singleQuote: true,
@@ -103,17 +103,13 @@ const defaultPrettierConfig = {
103
103
  $schema: "https://json.schemastore.org/prettierrc",
104
104
  ...defaultFormatterConfig,
105
105
  overrides: [
106
- {
107
- files: ["*.json", "**/*.json"],
108
- options: { tabWidth: 2 }
109
- },
110
106
  {
111
107
  files: ["*.md", "**/*.md"],
112
- options: { tabWidth: 2, semi: false }
108
+ options: { semi: false }
113
109
  },
114
110
  {
115
111
  files: ["*.yml", "*.yaml", "**/*.yml", "**/*.yaml"],
116
- options: { tabWidth: 2, semi: false }
112
+ options: { semi: false }
117
113
  }
118
114
  ]
119
115
  };
@@ -155,22 +151,24 @@ const AI_PLATFORM_HINTS = {
155
151
  function generateAiFiles(files, params) {
156
152
  const { platforms, isMonorepo, configStrategy, ...rest } = params;
157
153
  if (platforms.length === 0) return;
158
- files[".ai/workspace.md"] = {
159
- type: "text",
160
- content: generateWorkspace({
161
- ...rest,
162
- isMonorepo: !!isMonorepo,
163
- configStrategy: configStrategy ?? "stealth"
164
- })
165
- };
166
- const pointer = "See `.ai/` for project rules.\n";
167
- for (const platform of platforms) {
168
- const path = platform === "agents" ? "AGENTS.md" : "CLAUDE.md";
169
- files[path] = { type: "text", content: pointer };
154
+ const content = generateWorkspace({
155
+ ...rest,
156
+ isMonorepo: !!isMonorepo});
157
+ const pointer = "See [`AGENTS.md`](./Agents.md) for agent context.\n";
158
+ const hasAgents = platforms.includes("agents");
159
+ const hasClaude = platforms.includes("claude");
160
+ const isSingleton = platforms.length === 1;
161
+ if (hasAgents) files["AGENTS.md"] = { type: "text", content };
162
+ if (hasClaude) {
163
+ if (isSingleton) {
164
+ files["CLAUDE.md"] = { type: "text", content };
165
+ } else {
166
+ files["CLAUDE.md"] = { type: "text", content: pointer };
167
+ }
170
168
  }
171
169
  }
172
170
  function generateWorkspace(ctx) {
173
- const { name, packageManager, linter, formatter, isMonorepo, configStrategy } = ctx;
171
+ const { name, packageManager, linter, formatter, isMonorepo} = ctx;
174
172
  const sections = [
175
173
  `# ${name}`,
176
174
  "",
@@ -187,54 +185,572 @@ function generateWorkspace(ctx) {
187
185
  ];
188
186
  if (isMonorepo) {
189
187
  sections.push(
190
- "",
191
- "## Structure",
192
- "",
193
- "- `apps/` \u2014 applications",
194
- "- `packages/` \u2014 shared libraries",
195
- "- `.config/` \u2014 shared config packages",
196
- "",
197
- "## Monorepo",
198
188
  "",
199
189
  "- Use `workspace:*` for internal dependencies",
200
190
  `- New packages: \`${packageManager} create krispya <name> --workspace\``
201
191
  );
202
- } else if (configStrategy === "root") {
203
- sections.push(
204
- "",
205
- "## Structure",
206
- "",
207
- "- `src/` \u2014 source code",
208
- "- `dist/` \u2014 generated, don't edit",
209
- "- Config files (`tsconfig.json`, etc.) are at project root"
210
- );
211
- } else {
212
- sections.push(
213
- "",
214
- "## Structure",
215
- "",
216
- "- `src/` \u2014 source code",
217
- "- `.config/` \u2014 configs, don't move",
218
- "- `dist/` \u2014 generated, don't edit"
219
- );
220
192
  }
221
193
  sections.push("");
222
194
  return sections.join("\n");
223
195
  }
224
196
 
197
+ function getLanguageFromTemplate(template) {
198
+ return template.endsWith("-js") ? "javascript" : "typescript";
199
+ }
200
+ function getBaseTemplate(template) {
201
+ return template.replace("-js", "");
202
+ }
203
+
204
+ async function getLatestNpmVersion(packageName, fallback) {
205
+ try {
206
+ const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`);
207
+ const data = await response.json();
208
+ return data.version;
209
+ } catch {
210
+ return fallback;
211
+ }
212
+ }
213
+ async function getLatestPnpmVersion() {
214
+ return getLatestNpmVersion("pnpm", "10.11.0");
215
+ }
216
+ async function getLatestYarnVersion() {
217
+ return getLatestNpmVersion("yarn", "4.6.0");
218
+ }
219
+ async function getLatestNpmCliVersion() {
220
+ return getLatestNpmVersion("npm", "11.0.0");
221
+ }
222
+ async function getLatestNodeVersion() {
223
+ try {
224
+ const response = await fetch("https://nodejs.org/dist/index.json");
225
+ const data = await response.json();
226
+ const latestVersion = data[0];
227
+ if (latestVersion) {
228
+ return latestVersion.version.replace(/^v/, "");
229
+ }
230
+ return "25.0.0";
231
+ } catch {
232
+ return "25.0.0";
233
+ }
234
+ }
235
+ function validateNameSegment(segment, label) {
236
+ if (!segment.length) {
237
+ return `${label} is required`;
238
+ }
239
+ if (!/^[a-z0-9-]+$/.test(segment)) {
240
+ return `${label} must be lowercase and contain only letters, numbers, and hyphens`;
241
+ }
242
+ if (segment.startsWith("-") || segment.endsWith("-")) {
243
+ return `${label} cannot start or end with a hyphen`;
244
+ }
245
+ if (segment.includes("--")) {
246
+ return `${label} cannot contain consecutive hyphens`;
247
+ }
248
+ return void 0;
249
+ }
250
+ function validatePackageName(name) {
251
+ if (!name.length) {
252
+ return "Package name is required";
253
+ }
254
+ if (name.includes("..") || name.includes("\\")) {
255
+ return "Package name cannot contain path traversal sequences";
256
+ }
257
+ if (name.startsWith("@")) {
258
+ const slashIndex = name.indexOf("/");
259
+ if (slashIndex === -1) {
260
+ return "Scoped package name must include a package name after the scope (e.g., @scope/name)";
261
+ }
262
+ if (name.indexOf("/", slashIndex + 1) !== -1) {
263
+ return "Package name can only have one slash for scoped packages";
264
+ }
265
+ const scope = name.slice(1, slashIndex);
266
+ const packageName = name.slice(slashIndex + 1);
267
+ const scopeError = validateNameSegment(scope, "Scope");
268
+ if (scopeError) return scopeError;
269
+ const nameError = validateNameSegment(packageName, "Package name");
270
+ if (nameError) return nameError;
271
+ return void 0;
272
+ }
273
+ if (name.includes("/")) {
274
+ return "Unscoped package name cannot contain slashes. Use @scope/name format for scoped packages";
275
+ }
276
+ return validateNameSegment(name, "Package name");
277
+ }
278
+ function parseWorkspaceYamlContent(content) {
279
+ const directories = [];
280
+ let inPackagesSection = false;
281
+ for (const line of content.split("\n")) {
282
+ const trimmed = line.trim();
283
+ if (trimmed === "packages:") {
284
+ inPackagesSection = true;
285
+ continue;
286
+ }
287
+ if (inPackagesSection && trimmed && !line.startsWith(" ") && !line.startsWith(" ") && !trimmed.startsWith("-")) {
288
+ break;
289
+ }
290
+ if (inPackagesSection && trimmed.startsWith("-")) {
291
+ const entry = trimmed.slice(1).trim().replace(/^["']|["']$/g, "").replace(/^\.\//, "").replace(/\/\*.*$/, "");
292
+ if (entry && !entry.startsWith(".")) {
293
+ directories.push(entry);
294
+ }
295
+ }
296
+ }
297
+ return directories;
298
+ }
299
+ async function pathExists(path) {
300
+ try {
301
+ await promises.access(path, fs.constants.F_OK);
302
+ return true;
303
+ } catch {
304
+ return false;
305
+ }
306
+ }
307
+ function detectLinterFromScript(script) {
308
+ if (!script) return void 0;
309
+ if (script.includes("oxlint")) return "oxlint";
310
+ if (script.includes("eslint")) return "eslint";
311
+ if (script.includes("biome check") || script.includes("biome lint")) return "biome";
312
+ return void 0;
313
+ }
314
+ function detectFormatterFromScript(script) {
315
+ if (!script) return void 0;
316
+ if (script.includes("prettier")) return "prettier";
317
+ if (script.includes("oxfmt")) return "oxfmt";
318
+ if (script.includes("biome format")) return "biome";
319
+ return void 0;
320
+ }
321
+ async function detectLinterFromConfig(root) {
322
+ if (await pathExists(path.join(root, ".config/oxlint"))) return "oxlint";
323
+ if (await pathExists(path.join(root, ".config/eslint"))) return "eslint";
324
+ if (await pathExists(path.join(root, "biome.json"))) {
325
+ try {
326
+ const content = await promises.readFile(path.join(root, "biome.json"), "utf-8");
327
+ const config = JSON.parse(content);
328
+ if (config.linter?.enabled !== false) return "biome";
329
+ } catch {
330
+ return "biome";
331
+ }
332
+ }
333
+ return void 0;
334
+ }
335
+ async function detectFormatterFromConfig(root) {
336
+ if (await pathExists(path.join(root, ".config/prettier"))) return "prettier";
337
+ if (await pathExists(path.join(root, ".config/oxfmt"))) return "oxfmt";
338
+ if (await pathExists(path.join(root, "biome.json"))) {
339
+ try {
340
+ const content = await promises.readFile(path.join(root, "biome.json"), "utf-8");
341
+ const config = JSON.parse(content);
342
+ if (config.formatter?.enabled !== false) return "biome";
343
+ } catch {
344
+ return "biome";
345
+ }
346
+ }
347
+ return void 0;
348
+ }
349
+ function detectLinterFromDeps(devDeps) {
350
+ if (!devDeps) return void 0;
351
+ if (devDeps["@biomejs/biome"]) return "biome";
352
+ if (devDeps.eslint) return "eslint";
353
+ if (devDeps.oxlint) return "oxlint";
354
+ return void 0;
355
+ }
356
+ function detectFormatterFromDeps(devDeps) {
357
+ if (!devDeps) return void 0;
358
+ if (devDeps["@biomejs/biome"]) return "biome";
359
+ if (devDeps.prettier) return "prettier";
360
+ if (devDeps.oxfmt) return "oxfmt";
361
+ return void 0;
362
+ }
363
+ async function detectTooling(root) {
364
+ try {
365
+ const pkgPath = path.join(root, "package.json");
366
+ const content = await promises.readFile(pkgPath, "utf-8");
367
+ const pkg = JSON.parse(content);
368
+ const linter = detectLinterFromScript(pkg.scripts?.lint) ?? await detectLinterFromConfig(root) ?? detectLinterFromDeps(pkg.devDependencies);
369
+ const formatter = detectFormatterFromScript(pkg.scripts?.format) ?? await detectFormatterFromConfig(root) ?? detectFormatterFromDeps(pkg.devDependencies);
370
+ return { linter, formatter };
371
+ } catch {
372
+ return { linter: void 0, formatter: void 0 };
373
+ }
374
+ }
375
+ function generateRandomName() {
376
+ const adjectives = [
377
+ "red",
378
+ "blue",
379
+ "green",
380
+ "yellow",
381
+ "purple",
382
+ "orange",
383
+ "pink",
384
+ "black",
385
+ "white",
386
+ "tiny",
387
+ "big",
388
+ "small",
389
+ "large",
390
+ "huge",
391
+ "giant",
392
+ "mini",
393
+ "mega",
394
+ "super",
395
+ "happy",
396
+ "sad",
397
+ "angry",
398
+ "calm",
399
+ "quiet",
400
+ "loud",
401
+ "silent",
402
+ "noisy",
403
+ "shiny",
404
+ "dull",
405
+ "bright",
406
+ "dark",
407
+ "fuzzy",
408
+ "smooth",
409
+ "rough",
410
+ "soft"
411
+ ];
412
+ const nouns = [
413
+ "apple",
414
+ "banana",
415
+ "cherry",
416
+ "date",
417
+ "elderberry",
418
+ "fig",
419
+ "grape",
420
+ "honeydew",
421
+ "cat",
422
+ "dog",
423
+ "elephant",
424
+ "fox",
425
+ "giraffe",
426
+ "horse",
427
+ "iguana",
428
+ "jaguar",
429
+ "mountain",
430
+ "river",
431
+ "ocean",
432
+ "desert",
433
+ "forest",
434
+ "jungle",
435
+ "meadow",
436
+ "valley",
437
+ "star",
438
+ "moon",
439
+ "sun",
440
+ "planet",
441
+ "comet",
442
+ "asteroid",
443
+ "galaxy",
444
+ "universe"
445
+ ];
446
+ const randomAdjective = adjectives[Math.floor(Math.random() * adjectives.length)];
447
+ const randomNoun = nouns[Math.floor(Math.random() * nouns.length)];
448
+ return `${randomAdjective}-${randomNoun}`;
449
+ }
450
+
451
+ const PACKAGE_VERSION_DEFINITIONS = {
452
+ "@biomejs/biome": { fallbackVersion: "2.0.0" },
453
+ "@react-three/drei": { fallbackVersion: "10.0.0" },
454
+ "@react-three/fiber": { fallbackVersion: "9.0.0" },
455
+ "@react-three/handle": { fallbackVersion: "6.6.16" },
456
+ "@react-three/offscreen": { fallbackVersion: "0.0.8" },
457
+ "@react-three/postprocessing": { fallbackVersion: "3.0.4" },
458
+ "@react-three/rapier": { fallbackVersion: "2.1.0" },
459
+ "@react-three/uikit": { fallbackVersion: "0.8.15" },
460
+ "@react-three/xr": { fallbackVersion: "6.6.16" },
461
+ "@testing-library/dom": { fallbackVersion: "10.4.0" },
462
+ "@testing-library/react": { fallbackVersion: "16.2.0" },
463
+ "@types/react": { fallbackVersion: "19.0.0" },
464
+ "@types/react-dom": { fallbackVersion: "19.0.0" },
465
+ "@types/three": { fallbackVersion: "0.175.0", prefix: "~" },
466
+ "@vitejs/plugin-basic-ssl": { fallbackVersion: "2.0.0" },
467
+ "@vitejs/plugin-react": { fallbackVersion: "4.4.1" },
468
+ "@viverse/cli": { fallbackVersion: "0.9.5-beta.8" },
469
+ eslint: { fallbackVersion: "9.17.0" },
470
+ "eslint-plugin-react-hooks": { fallbackVersion: "5.1.0" },
471
+ jsdom: { fallbackVersion: "26.0.0" },
472
+ koota: { fallbackVersion: "0.4.0" },
473
+ leva: { fallbackVersion: "0.10.0" },
474
+ oxfmt: { fallbackVersion: "0.21.0" },
475
+ oxlint: { fallbackVersion: "1.36.0" },
476
+ prettier: { fallbackVersion: "3.4.2" },
477
+ react: { fallbackVersion: "19.0.0" },
478
+ "react-dom": { fallbackVersion: "19.0.0" },
479
+ three: { fallbackVersion: "0.175.0", prefix: "~" },
480
+ tsdown: { fallbackVersion: "0.12.0" },
481
+ "typescript-eslint": { fallbackVersion: "8.18.0" },
482
+ unbuild: { fallbackVersion: "3.5.0" },
483
+ vite: { fallbackVersion: "6.3.4" },
484
+ vitest: { fallbackVersion: "4.0.0" },
485
+ zustand: { fallbackVersion: "5.0.3" }
486
+ };
487
+ function addPackageName(packageNames, explicitVersions, packageName) {
488
+ if (!explicitVersions.has(packageName)) {
489
+ packageNames.add(packageName);
490
+ }
491
+ }
492
+ function getExplicitVersionPackages(options) {
493
+ return /* @__PURE__ */ new Set([
494
+ ...Object.keys(options.dependencies ?? {}),
495
+ ...Object.keys(options.versions ?? {})
496
+ ]);
497
+ }
498
+ function getPackageFallbackVersion(packageName) {
499
+ const definition = PACKAGE_VERSION_DEFINITIONS[packageName];
500
+ if (definition == null) {
501
+ throw new Error(`Missing package version definition for ${packageName}`);
502
+ }
503
+ return definition.fallbackVersion;
504
+ }
505
+ function getResolvedPackageVersion(versions, packageName) {
506
+ return versions[packageName] ?? getPackageFallbackVersion(packageName);
507
+ }
508
+ function formatResolvedPackageVersion(versions, packageName, prefix) {
509
+ const resolvedPrefix = prefix ?? PACKAGE_VERSION_DEFINITIONS[packageName]?.prefix ?? "^";
510
+ return `${resolvedPrefix}${getResolvedPackageVersion(versions, packageName)}`;
511
+ }
512
+ function assignResolvedPackageVersion(target, versions, packageName, prefix) {
513
+ target[packageName] = formatResolvedPackageVersion(versions, packageName, prefix);
514
+ }
515
+ function getPackageManagerSpec(packageManager) {
516
+ return packageManager ?? { name: "pnpm" };
517
+ }
518
+ function getPackageManagerName(packageManager) {
519
+ return getPackageManagerSpec(packageManager).name;
520
+ }
521
+ function formatPackageManager(packageManager) {
522
+ const spec = getPackageManagerSpec(packageManager);
523
+ return spec.version ? `${spec.name}@${spec.version}` : spec.name;
524
+ }
525
+ function parsePackageManager(packageManager) {
526
+ if (packageManager == null || packageManager.length === 0) {
527
+ return void 0;
528
+ }
529
+ const atIndex = packageManager.indexOf("@");
530
+ if (atIndex === -1) {
531
+ return { name: packageManager };
532
+ }
533
+ return {
534
+ name: packageManager.slice(0, atIndex),
535
+ version: packageManager.slice(atIndex + 1)
536
+ };
537
+ }
538
+ function getEngineSpec(engine) {
539
+ return engine ?? { name: "node" };
540
+ }
541
+ function getEngineName(engine) {
542
+ return getEngineSpec(engine).name;
543
+ }
544
+ function parseEngine(engines) {
545
+ if (engines == null) {
546
+ return void 0;
547
+ }
548
+ const [name, range] = Object.entries(engines).find(
549
+ ([engineName]) => engineName !== "npm" && engineName !== "pnpm" && engineName !== "yarn"
550
+ ) ?? [];
551
+ if (name == null) {
552
+ return void 0;
553
+ }
554
+ const version = range?.match(/(\d+(?:\.\d+(?:\.\d+)?)?)/)?.[1];
555
+ return { name, version };
556
+ }
557
+ async function resolvePackageManager(options) {
558
+ const packageManager = getPackageManagerSpec(options.packageManager);
559
+ if (packageManager.version == null) {
560
+ if (packageManager.name === "pnpm") {
561
+ packageManager.version = await getLatestPnpmVersion();
562
+ } else if (packageManager.name === "yarn") {
563
+ packageManager.version = await getLatestYarnVersion();
564
+ } else if (packageManager.name === "npm") {
565
+ packageManager.version = await getLatestNpmCliVersion();
566
+ }
567
+ }
568
+ return packageManager;
569
+ }
570
+ async function resolveEngine(options) {
571
+ const engine = getEngineSpec(options.engine);
572
+ if (engine.version == null && engine.name === "node") {
573
+ engine.version = await getLatestNodeVersion();
574
+ }
575
+ return engine;
576
+ }
577
+ async function resolvePackageVersions(packageNames, existingVersions = {}) {
578
+ const versions = { ...existingVersions };
579
+ const uniquePackageNames = [...new Set(packageNames)];
580
+ await Promise.all(
581
+ uniquePackageNames.map(async (packageName) => {
582
+ if (versions[packageName] != null) return;
583
+ versions[packageName] = await getLatestNpmVersion(
584
+ packageName,
585
+ getPackageFallbackVersion(packageName)
586
+ );
587
+ })
588
+ );
589
+ return versions;
590
+ }
591
+ async function resolveProjectPackageVersions(options) {
592
+ return resolvePackageVersions(collectProjectPackageNames(options), options.versions);
593
+ }
594
+ async function resolveMonorepoRootPackageVersions(params) {
595
+ const packageNames = /* @__PURE__ */ new Set();
596
+ const explicitVersions = new Set(Object.keys(params.versions ?? {}));
597
+ addPackageName(packageNames, explicitVersions, getLinterPackage(params.linter));
598
+ if (params.formatter !== "biome" || params.linter !== "biome") {
599
+ addPackageName(packageNames, explicitVersions, getFormatterPackage(params.formatter));
600
+ }
601
+ return resolvePackageVersions(packageNames, params.versions);
602
+ }
603
+ function collectProjectPackageNames(options) {
604
+ const packageNames = /* @__PURE__ */ new Set();
605
+ const explicitVersions = getExplicitVersionPackages(options);
606
+ const template = options.template ?? "vanilla";
607
+ const baseTemplate = getBaseTemplate(template);
608
+ const language = getLanguageFromTemplate(template);
609
+ const isLibrary = options.projectType === "library";
610
+ const isReact = baseTemplate === "react" || baseTemplate === "r3f";
611
+ const isR3f = baseTemplate === "r3f";
612
+ const isTypescript = language === "typescript";
613
+ const inWorkspace = options.workspaceRoot != null;
614
+ const testing = options.testing ?? (isLibrary ? "vitest" : "none");
615
+ const linter = options.linter ?? "oxlint";
616
+ const formatter = options.formatter ?? "prettier";
617
+ const bundler = options.libraryBundler ?? "unbuild";
618
+ const packageManager = getPackageManagerName(options.packageManager);
619
+ if (!isLibrary) {
620
+ addPackageName(packageNames, explicitVersions, "vite");
621
+ }
622
+ if (isReact) {
623
+ if (!isLibrary) {
624
+ addPackageName(packageNames, explicitVersions, "react");
625
+ addPackageName(packageNames, explicitVersions, "react-dom");
626
+ addPackageName(packageNames, explicitVersions, "@vitejs/plugin-react");
627
+ }
628
+ if (isTypescript) {
629
+ addPackageName(packageNames, explicitVersions, "@types/react");
630
+ addPackageName(packageNames, explicitVersions, "@types/react-dom");
631
+ }
632
+ }
633
+ if (isR3f) {
634
+ if (!isLibrary) {
635
+ addPackageName(packageNames, explicitVersions, "three");
636
+ addPackageName(packageNames, explicitVersions, "@react-three/fiber");
637
+ }
638
+ if (isTypescript) {
639
+ addPackageName(packageNames, explicitVersions, "@types/three");
640
+ }
641
+ if (isEnabledOption(options.drei)) {
642
+ addPackageName(packageNames, explicitVersions, "@react-three/drei");
643
+ }
644
+ if (isEnabledOption(options.handle)) {
645
+ addPackageName(packageNames, explicitVersions, "@react-three/handle");
646
+ }
647
+ if (isEnabledOption(options.koota)) {
648
+ addPackageName(packageNames, explicitVersions, "koota");
649
+ }
650
+ if (isEnabledOption(options.leva)) {
651
+ addPackageName(packageNames, explicitVersions, "leva");
652
+ }
653
+ if (isEnabledOption(options.rapier)) {
654
+ addPackageName(packageNames, explicitVersions, "@react-three/rapier");
655
+ }
656
+ if (isEnabledOption(options.uikit)) {
657
+ addPackageName(packageNames, explicitVersions, "@react-three/uikit");
658
+ }
659
+ if (isEnabledOption(options.zustand)) {
660
+ addPackageName(packageNames, explicitVersions, "zustand");
661
+ }
662
+ if (isEnabledOption(options.xr)) {
663
+ addPackageName(packageNames, explicitVersions, "@react-three/xr");
664
+ addPackageName(packageNames, explicitVersions, "@vitejs/plugin-basic-ssl");
665
+ }
666
+ if (!isEnabledOption(options.xr) && isEnabledOption(options.offscreen)) {
667
+ addPackageName(packageNames, explicitVersions, "@react-three/offscreen");
668
+ }
669
+ if (!isEnabledOption(options.xr) && isEnabledOption(options.postprocessing)) {
670
+ addPackageName(packageNames, explicitVersions, "@react-three/postprocessing");
671
+ }
672
+ if (isEnabledOption(options.viverse) && packageManager === "npm") {
673
+ addPackageName(packageNames, explicitVersions, "@viverse/cli");
674
+ }
675
+ }
676
+ if (testing === "vitest") {
677
+ addPackageName(packageNames, explicitVersions, "vitest");
678
+ if (isReact) {
679
+ addPackageName(packageNames, explicitVersions, "@testing-library/react");
680
+ addPackageName(packageNames, explicitVersions, "@testing-library/dom");
681
+ addPackageName(packageNames, explicitVersions, "jsdom");
682
+ }
683
+ }
684
+ if (linter === "eslint") {
685
+ addPackageName(packageNames, explicitVersions, "eslint");
686
+ if (isTypescript) {
687
+ addPackageName(packageNames, explicitVersions, "typescript-eslint");
688
+ }
689
+ if (isReact) {
690
+ addPackageName(packageNames, explicitVersions, "eslint-plugin-react-hooks");
691
+ }
692
+ } else if (linter === "oxlint") {
693
+ if (!inWorkspace) {
694
+ addPackageName(packageNames, explicitVersions, "oxlint");
695
+ }
696
+ } else if (linter === "biome") {
697
+ addPackageName(packageNames, explicitVersions, "@biomejs/biome");
698
+ }
699
+ if (formatter === "prettier") {
700
+ addPackageName(packageNames, explicitVersions, "prettier");
701
+ } else if (formatter === "oxfmt") {
702
+ if (!inWorkspace) {
703
+ addPackageName(packageNames, explicitVersions, "oxfmt");
704
+ }
705
+ } else if (formatter === "biome") {
706
+ addPackageName(packageNames, explicitVersions, "@biomejs/biome");
707
+ }
708
+ if (isLibrary) {
709
+ addPackageName(packageNames, explicitVersions, bundler === "unbuild" ? "unbuild" : "tsdown");
710
+ }
711
+ return [...packageNames];
712
+ }
713
+ function getLinterPackage(linter) {
714
+ if (linter === "biome") {
715
+ return "@biomejs/biome";
716
+ }
717
+ return linter;
718
+ }
719
+ function getFormatterPackage(formatter) {
720
+ if (formatter === "biome") {
721
+ return "@biomejs/biome";
722
+ }
723
+ return formatter;
724
+ }
725
+ function isEnabledOption(option) {
726
+ return option != null && option !== false;
727
+ }
728
+
225
729
  function generateTypescriptConfig(baseTemplateOrParams) {
226
730
  const params = typeof baseTemplateOrParams === "string" ? { baseTemplate: baseTemplateOrParams } : baseTemplateOrParams;
227
- const { baseTemplate, useConfigPackage, configStrategy = "stealth" } = params;
731
+ const {
732
+ baseTemplate,
733
+ useConfigPackage,
734
+ configStrategy = "stealth",
735
+ engine,
736
+ versions = {}
737
+ } = params;
228
738
  const isReact = baseTemplate === "react";
229
739
  const isR3f = baseTemplate === "r3f";
230
740
  const files = {};
231
741
  const devDependencies = {};
742
+ if (getEngineName(engine) === "node" && getEngineSpec(engine).version) {
743
+ const majorVersion = getEngineSpec(engine).version.split(".")[0];
744
+ devDependencies["@types/node"] = `^${majorVersion}.0.0`;
745
+ } else {
746
+ devDependencies["@types/node"] = "^22.0.0";
747
+ }
232
748
  if (isReact || isR3f) {
233
- devDependencies["@types/react"] = "^19.0.0";
234
- devDependencies["@types/react-dom"] = "^19.0.0";
749
+ assignResolvedPackageVersion(devDependencies, versions, "@types/react");
750
+ assignResolvedPackageVersion(devDependencies, versions, "@types/react-dom");
235
751
  }
236
752
  if (isR3f) {
237
- devDependencies["@types/three"] = "~0.175.0";
753
+ assignResolvedPackageVersion(devDependencies, versions, "@types/three", "~");
238
754
  }
239
755
  if (useConfigPackage) {
240
756
  devDependencies["@config/typescript"] = "workspace:*";
@@ -257,7 +773,10 @@ function generateTypescriptConfig(baseTemplateOrParams) {
257
773
  const tsConfig = {
258
774
  $schema: "https://json.schemastore.org/tsconfig",
259
775
  files: [],
260
- references: [{ path: "./.config/tsconfig.app.json" }, { path: "./.config/tsconfig.node.json" }]
776
+ references: [
777
+ { path: "./.config/tsconfig.app.json" },
778
+ { path: "./.config/tsconfig.node.json" }
779
+ ]
261
780
  };
262
781
  files["tsconfig.json"] = {
263
782
  type: "text",
@@ -363,6 +882,7 @@ function generateTypescriptConfig(baseTemplateOrParams) {
363
882
  return { files, devDependencies };
364
883
  }
365
884
 
885
+ const DEFAULT_LIBRARY_VERSION = "0.1.0";
366
886
  function generatePackageJson(params) {
367
887
  const {
368
888
  name,
@@ -376,14 +896,15 @@ function generatePackageJson(params) {
376
896
  workspaceDependencies
377
897
  } = params;
378
898
  const files = {};
379
- const packageManager = options.packageManager ?? "pnpm";
380
- const isPnpm = packageManager === "pnpm";
899
+ const packageManager = getPackageManagerSpec(options.packageManager);
900
+ const isPnpm = packageManager.name === "pnpm";
381
901
  const packageJson = {
382
902
  name,
383
903
  description: "Built with \u{1F339} create-krispya",
384
904
  type: "module"
385
905
  };
386
906
  if (isLibrary) {
907
+ packageJson.version = DEFAULT_LIBRARY_VERSION;
387
908
  packageJson.main = "./dist/index.mjs";
388
909
  packageJson.module = "./dist/index.mjs";
389
910
  if (language === "typescript") {
@@ -405,10 +926,16 @@ function generatePackageJson(params) {
405
926
  allDependencies[pkgName] = "workspace:*";
406
927
  }
407
928
  }
929
+ const allDevDependencies = { ...devDependencies };
930
+ const engine = getEngineSpec(options.engine);
931
+ if (getEngineName(engine) === "node" && engine.version) {
932
+ const majorVersion = engine.version.split(".")[0];
933
+ allDevDependencies["@types/node"] ??= `^${majorVersion}.0.0`;
934
+ }
408
935
  packageJson.scripts = scripts;
409
936
  packageJson.dependencies = sortKeys(allDependencies);
410
- if (Object.keys(devDependencies).length > 0) {
411
- packageJson.devDependencies = sortKeys(devDependencies);
937
+ if (Object.keys(allDevDependencies).length > 0) {
938
+ packageJson.devDependencies = sortKeys(allDevDependencies);
412
939
  }
413
940
  if (isLibrary && Object.keys(peerDependencies).length > 0) {
414
941
  packageJson.peerDependencies = sortKeys(peerDependencies);
@@ -416,25 +943,14 @@ function generatePackageJson(params) {
416
943
  const isMonorepoPackage = options.workspaceRoot != null;
417
944
  if (!isMonorepoPackage) {
418
945
  const engines = {};
419
- if (isPnpm) {
420
- const pnpmVersion = options.pnpmVersion ?? "10.11.0";
421
- const majorVersion = pnpmVersion.split(".")[0];
422
- engines.pnpm = `>=${majorVersion}.0.0`;
423
- packageJson.packageManager = `pnpm@${pnpmVersion}`;
424
- } else if (packageManager === "yarn") {
425
- const yarnVersion = options.yarnVersion ?? "4.6.0";
426
- const majorVersion = yarnVersion.split(".")[0];
427
- engines.yarn = `>=${majorVersion}.0.0`;
428
- packageJson.packageManager = `yarn@${yarnVersion}`;
429
- } else if (packageManager === "npm") {
430
- const npmVersion = options.npmVersion ?? "11.0.0";
431
- const majorVersion = npmVersion.split(".")[0];
432
- engines.npm = `>=${majorVersion}.0.0`;
433
- packageJson.packageManager = `npm@${npmVersion}`;
946
+ if (packageManager.version != null) {
947
+ const majorVersion = packageManager.version.split(".")[0];
948
+ engines[packageManager.name] = `>=${majorVersion}.0.0`;
949
+ packageJson.packageManager = formatPackageManager(packageManager);
434
950
  }
435
- if (options.nodeVersion) {
436
- const majorVersion = options.nodeVersion.split(".")[0];
437
- engines.node = `>=${majorVersion}.0.0`;
951
+ if (engine.version != null) {
952
+ const majorVersion = engine.version.split(".")[0];
953
+ engines[engine.name] = `>=${majorVersion}.0.0`;
438
954
  }
439
955
  if (Object.keys(engines).length > 0) {
440
956
  packageJson.engines = engines;
@@ -752,6 +1268,22 @@ function generateTestFiles(params) {
752
1268
  return files;
753
1269
  }
754
1270
 
1271
+ const COMMON_GITIGNORE_LINES = [
1272
+ "node_modules",
1273
+ "dist",
1274
+ "*.tsbuildinfo",
1275
+ ".env",
1276
+ ".env.*",
1277
+ "!.env.example"
1278
+ ];
1279
+ function generateGitignore(variant) {
1280
+ const lines = variant === "workspace-root" ? [...COMMON_GITIGNORE_LINES, ".DS_Store"] : COMMON_GITIGNORE_LINES;
1281
+ return {
1282
+ type: "text",
1283
+ content: lines.join("\n")
1284
+ };
1285
+ }
1286
+
755
1287
  function generateVscodeFiles$1(params) {
756
1288
  const { codeSnippets, vscodeSettings } = params;
757
1289
  const files = {};
@@ -774,7 +1306,7 @@ function generateVscodeFiles$1(params) {
774
1306
  );
775
1307
  files[".vscode/settings.json"] = {
776
1308
  type: "text",
777
- content: JSON.stringify(sortedSettings, null, " ")
1309
+ content: JSON.stringify(sortedSettings, null, 2)
778
1310
  };
779
1311
  }
780
1312
  return files;
@@ -1227,25 +1759,31 @@ function generateMonorepo(params) {
1227
1759
  linter,
1228
1760
  formatter,
1229
1761
  packageManager,
1230
- pnpmVersion,
1231
1762
  pnpmManageVersions,
1232
- nodeVersion,
1763
+ engine,
1764
+ versions = {},
1233
1765
  aiPlatforms
1234
1766
  } = params;
1235
1767
  const files = {};
1236
- const isPnpm = packageManager === "pnpm";
1768
+ const isPnpm = packageManager.name === "pnpm";
1237
1769
  const devDependencies = {};
1770
+ if (engine?.name === "node" && engine.version) {
1771
+ const majorVersion = engine.version.split(".")[0];
1772
+ devDependencies["@types/node"] = `^${majorVersion}.0.0`;
1773
+ } else {
1774
+ devDependencies["@types/node"] = "^22.0.0";
1775
+ }
1238
1776
  if (linter === "oxlint") {
1239
- devDependencies["oxlint"] = "^1.36.0";
1777
+ assignResolvedPackageVersion(devDependencies, versions, "oxlint");
1240
1778
  } else if (linter === "eslint") {
1241
- devDependencies["eslint"] = "^9.17.0";
1779
+ assignResolvedPackageVersion(devDependencies, versions, "eslint");
1242
1780
  } else if (linter === "biome") {
1243
- devDependencies["@biomejs/biome"] = "^1.9.4";
1781
+ assignResolvedPackageVersion(devDependencies, versions, "@biomejs/biome");
1244
1782
  }
1245
1783
  if (formatter === "oxfmt") {
1246
- devDependencies["oxfmt"] = "^0.21.0";
1784
+ assignResolvedPackageVersion(devDependencies, versions, "oxfmt");
1247
1785
  } else if (formatter === "prettier") {
1248
- devDependencies["prettier"] = "^3.4.2";
1786
+ assignResolvedPackageVersion(devDependencies, versions, "prettier");
1249
1787
  }
1250
1788
  const rootPackageJson = {
1251
1789
  name: "root",
@@ -1262,14 +1800,14 @@ function generateMonorepo(params) {
1262
1800
  devDependencies
1263
1801
  };
1264
1802
  const engines = {};
1265
- if (isPnpm && pnpmVersion) {
1266
- const majorVersion = pnpmVersion.split(".")[0];
1803
+ if (isPnpm && packageManager.version) {
1804
+ const majorVersion = packageManager.version.split(".")[0];
1267
1805
  engines.pnpm = `>=${majorVersion}.0.0`;
1268
- rootPackageJson.packageManager = `pnpm@${pnpmVersion}`;
1806
+ rootPackageJson.packageManager = formatPackageManager(packageManager);
1269
1807
  }
1270
- if (nodeVersion) {
1271
- const majorVersion = nodeVersion.split(".")[0];
1272
- engines.node = `>=${majorVersion}.0.0`;
1808
+ if (engine?.version) {
1809
+ const majorVersion = engine.version.split(".")[0];
1810
+ engines[engine.name] = `>=${majorVersion}.0.0`;
1273
1811
  }
1274
1812
  if (Object.keys(engines).length > 0) {
1275
1813
  rootPackageJson.engines = engines;
@@ -1283,13 +1821,7 @@ function generateMonorepo(params) {
1283
1821
  if (pnpmManageVersions) {
1284
1822
  workspaceLines.push("manage-package-manager-versions: true", "");
1285
1823
  }
1286
- workspaceLines.push(
1287
- "packages:",
1288
- ' - ".config/*"',
1289
- ' - "apps/*"',
1290
- ' - "packages/*"',
1291
- ""
1292
- );
1824
+ workspaceLines.push("packages:", ' - ".config/*"', ' - "apps/*"', ' - "packages/*"', "");
1293
1825
  workspaceLines.push("onlyBuiltDependencies:", " - esbuild");
1294
1826
  files["pnpm-workspace.yaml"] = {
1295
1827
  type: "text",
@@ -1334,8 +1866,9 @@ export default [...base];
1334
1866
  `
1335
1867
  };
1336
1868
  } else if (linter === "biome") {
1869
+ const biomeVersion = getResolvedPackageVersion(versions, "@biomejs/biome");
1337
1870
  const biomeConfig = {
1338
- $schema: "https://biomejs.dev/schemas/1.9.4/schema.json",
1871
+ $schema: `https://biomejs.dev/schemas/${biomeVersion}/schema.json`,
1339
1872
  vcs: {
1340
1873
  enabled: true,
1341
1874
  clientKind: "git",
@@ -1361,10 +1894,7 @@ export default [...base];
1361
1894
  } else if (formatter === "prettier") {
1362
1895
  generatePrettierConfigPackage(files);
1363
1896
  }
1364
- files[".gitignore"] = {
1365
- type: "text",
1366
- content: ["node_modules", "dist", "*.tsbuildinfo", ".DS_Store"].join("\n")
1367
- };
1897
+ files[".gitignore"] = generateGitignore("workspace-root");
1368
1898
  files[".gitattributes"] = {
1369
1899
  type: "text",
1370
1900
  content: `* text=auto eol=lf
@@ -1387,12 +1917,12 @@ This monorepo workspace was generated with create-krispya.
1387
1917
 
1388
1918
  ## Development Commands
1389
1919
 
1390
- - \`${packageManager} install\` to install all dependencies
1391
- - \`${packageManager} run dev\` to run all applications in development mode
1392
- - \`${packageManager} run build\` to build all packages and applications
1393
- - \`${packageManager} run test\` to run tests across the workspace
1394
- - \`${packageManager} run lint\` to lint all code
1395
- - \`${packageManager} run format\` to format all code
1920
+ - \`${packageManager.name} install\` to install all dependencies
1921
+ - \`${packageManager.name} run dev\` to run all applications in development mode
1922
+ - \`${packageManager.name} run build\` to build all packages and applications
1923
+ - \`${packageManager.name} run test\` to run tests across the workspace
1924
+ - \`${packageManager.name} run lint\` to lint all code
1925
+ - \`${packageManager.name} run format\` to format all code
1396
1926
 
1397
1927
  ## Adding Packages
1398
1928
 
@@ -1402,7 +1932,7 @@ To add a new package to this workspace, run create-krispya from this directory a
1402
1932
  if (aiPlatforms && aiPlatforms.length > 0) {
1403
1933
  generateAiFiles(files, {
1404
1934
  name,
1405
- packageManager,
1935
+ packageManager: packageManager.name,
1406
1936
  linter,
1407
1937
  formatter,
1408
1938
  isMonorepo: true,
@@ -1454,10 +1984,17 @@ function generateVscodeFiles(files, linter, formatter) {
1454
1984
  type: "text",
1455
1985
  content: JSON.stringify({ recommendations }, null, 2)
1456
1986
  };
1457
- files[".vscode/settings.json"] = {
1458
- type: "text",
1459
- content: JSON.stringify(settings, null, " ")
1460
- };
1987
+ const codeSnippets = {};
1988
+ if (recommendations.length > 0) {
1989
+ codeSnippets["vscode-extension-suggestion"] = recommendations;
1990
+ }
1991
+ Object.assign(
1992
+ files,
1993
+ generateVscodeFiles$1({
1994
+ codeSnippets,
1995
+ vscodeSettings: settings
1996
+ })
1997
+ );
1461
1998
  }
1462
1999
 
1463
2000
  const monorepo = {
@@ -1478,8 +2015,8 @@ function generateBiome(generator, options) {
1478
2015
  if (options == null || !options.linter && !options.formatter) {
1479
2016
  return;
1480
2017
  }
1481
- const version = generator.versions.biome ?? "2.0.0";
1482
- generator.addDevDependency("@biomejs/biome", `^${version}`);
2018
+ const version = generator.getVersion("@biomejs/biome");
2019
+ generator.addDevDependency("@biomejs/biome");
1483
2020
  const { rules } = defaultLinterConfig;
1484
2021
  const biomeConfig = {
1485
2022
  $schema: `https://biomejs.dev/schemas/${version}/schema.json`
@@ -1535,10 +2072,7 @@ function generateBiome(generator, options) {
1535
2072
  generator.addScript("lint", "biome lint --config-path .config .");
1536
2073
  }
1537
2074
  if (options.formatter) {
1538
- generator.addScript(
1539
- "format",
1540
- "biome format --config-path .config --write ."
1541
- );
2075
+ generator.addScript("format", "biome format --config-path .config --write .");
1542
2076
  }
1543
2077
  generator.addVscodeSetting("biome.linter.configPath", ".config/biome.json");
1544
2078
  } else {
@@ -1558,9 +2092,7 @@ function generateBiome(generator, options) {
1558
2092
  if (options.formatter) roles.push("formatter");
1559
2093
  generator.inject(
1560
2094
  "readme-tools",
1561
- `[Biome](https://biomejs.dev/) - Fast ${roles.join(
1562
- " and "
1563
- )} for JavaScript and TypeScript`
2095
+ `[Biome](https://biomejs.dev/) - Fast ${roles.join(" and ")} for JavaScript and TypeScript`
1564
2096
  );
1565
2097
  generator.inject("vscode-extension-suggestion", "biomejs.biome");
1566
2098
  generator.addVscodeSetting("biome.enabled", true);
@@ -1573,7 +2105,7 @@ function generateDrei(generator, options) {
1573
2105
  if (options == null) {
1574
2106
  return;
1575
2107
  }
1576
- generator.addDependency("@react-three/drei", "^10.0.0");
2108
+ generator.addDependency("@react-three/drei");
1577
2109
  generator.inject("import", `import { Environment } from "@react-three/drei"`);
1578
2110
  generator.inject("scene", '<Environment background preset="city" />');
1579
2111
  generator.inject(
@@ -1582,19 +2114,11 @@ function generateDrei(generator, options) {
1582
2114
  );
1583
2115
  }
1584
2116
 
1585
- function getLanguageFromTemplate(template) {
1586
- return template.endsWith("-js") ? "javascript" : "typescript";
1587
- }
1588
- function getBaseTemplate(template) {
1589
- return template.replace("-js", "");
1590
- }
1591
-
1592
2117
  function toEslintLevel(level) {
1593
2118
  return level;
1594
2119
  }
1595
2120
  function generateEslint(generator, options) {
1596
- const version = generator.versions.eslint ?? "9.17.0";
1597
- generator.addDevDependency("eslint", `^${version}`);
2121
+ generator.addDevDependency("eslint");
1598
2122
  const template = generator.options.template ?? "vanilla";
1599
2123
  const baseTemplate = getBaseTemplate(template);
1600
2124
  const isTypescript = getLanguageFromTemplate(template) === "typescript";
@@ -1603,12 +2127,12 @@ function generateEslint(generator, options) {
1603
2127
  const imports = ['import js from "@eslint/js"'];
1604
2128
  const configs = ["js.configs.recommended"];
1605
2129
  if (isTypescript) {
1606
- generator.addDevDependency("typescript-eslint", "^8.18.0");
2130
+ generator.addDevDependency("typescript-eslint");
1607
2131
  imports.push('import tseslint from "typescript-eslint"');
1608
2132
  configs.push("...tseslint.configs.recommended");
1609
2133
  }
1610
2134
  if (isReact) {
1611
- generator.addDevDependency("eslint-plugin-react-hooks", "^5.1.0");
2135
+ generator.addDevDependency("eslint-plugin-react-hooks");
1612
2136
  imports.push('import reactHooks from "eslint-plugin-react-hooks"');
1613
2137
  }
1614
2138
  const ignoresArray = JSON.stringify(defaultLinterConfig.ignorePatterns);
@@ -1710,7 +2234,7 @@ export function Box(props: ThreeElements['mesh']) {
1710
2234
  }
1711
2235
 
1712
2236
  function generateGithubPages(generator, options) {
1713
- if (options === false || (generator.options.packageManager ?? "npm") != "npm") {
2237
+ if (options === false || getPackageManagerName(generator.options.packageManager) !== "npm") {
1714
2238
  return;
1715
2239
  }
1716
2240
  generator.addFile(".github/workflows/gh-pages.yml", {
@@ -1774,7 +2298,7 @@ function generateHandle(generator, options) {
1774
2298
  if (options == null) {
1775
2299
  return;
1776
2300
  }
1777
- generator.addDependency("@react-three/handle", "^6.6.16");
2301
+ generator.addDependency("@react-three/handle");
1778
2302
  generator.inject(
1779
2303
  "readme-libraries",
1780
2304
  `[@react-three/handle](https://pmndrs.github.io/xr/docs/handles/introduction) - interactive controls and handles for your 3D objects`
@@ -1785,7 +2309,7 @@ function generateKoota(generator, options) {
1785
2309
  if (options == null) {
1786
2310
  return;
1787
2311
  }
1788
- generator.addDependency("koota", "^0.4.0");
2312
+ generator.addDependency("koota");
1789
2313
  generator.inject(
1790
2314
  "readme-libraries",
1791
2315
  `[koota](https://github.com/pmndrs/koota) - ECS-based state management library optimized for real-time apps, games, and XR experiences`
@@ -1796,7 +2320,7 @@ function generateLeva(generator, options) {
1796
2320
  if (options == null) {
1797
2321
  return;
1798
2322
  }
1799
- generator.addDependency("leva", "^0.10.0");
2323
+ generator.addDependency("leva");
1800
2324
  generator.inject(
1801
2325
  "readme-libraries",
1802
2326
  `[leva](https://github.com/pmndrs/leva) - HTML GUI panel for React with lightweight, beautiful and extensible controls`
@@ -1814,7 +2338,7 @@ function generateOffscreen(generator, options) {
1814
2338
  );
1815
2339
  return;
1816
2340
  }
1817
- generator.addDependency("@react-three/offscreen", "^0.0.8");
2341
+ generator.addDependency("@react-three/offscreen");
1818
2342
  generator.inject(
1819
2343
  "readme-libraries",
1820
2344
  `[@react-three/offscreen](https://github.com/pmndrs/offscreen) - Offload your scene to a worker thread for better performance`
@@ -1824,13 +2348,12 @@ function generateOffscreen(generator, options) {
1824
2348
  function generateOxfmt(generator, options) {
1825
2349
  const isMonorepo = generator.options.workspaceRoot != null;
1826
2350
  if (isMonorepo) {
1827
- generator.addDevDependency("@config/oxfmt", "workspace:*");
2351
+ generator.addDevDependency("@config/oxfmt", { version: "workspace:*" });
1828
2352
  const configPath = "node_modules/@config/oxfmt/base.json";
1829
2353
  generator.addScript("format", `oxfmt -c ${configPath} --write .`);
1830
2354
  generator.addVscodeSetting("oxc.fmt.configPath", configPath);
1831
2355
  } else {
1832
- const version = generator.versions.oxfmt ?? "0.1.0";
1833
- generator.addDevDependency("oxfmt", `^${version}`);
2356
+ generator.addDevDependency("oxfmt");
1834
2357
  const isStealth = generator.isStealthConfig();
1835
2358
  if (isStealth) {
1836
2359
  generator.addFile(".config/oxfmt.json", {
@@ -1876,13 +2399,12 @@ function generateOxlint(generator, options) {
1876
2399
  const isReact = baseTemplate === "react" || baseTemplate === "r3f";
1877
2400
  const isMonorepo = generator.options.workspaceRoot != null;
1878
2401
  if (isMonorepo) {
1879
- generator.addDevDependency("@config/oxlint", "workspace:*");
2402
+ generator.addDevDependency("@config/oxlint", { version: "workspace:*" });
1880
2403
  const configPath = isReact ? "node_modules/@config/oxlint/react.json" : "node_modules/@config/oxlint/base.json";
1881
2404
  generator.addScript("lint", `oxlint -c ${configPath}`);
1882
2405
  generator.addVscodeSetting("oxc.configPath", configPath);
1883
2406
  } else {
1884
- const version = generator.versions.oxlint ?? "0.16.0";
1885
- generator.addDevDependency("oxlint", `^${version}`);
2407
+ generator.addDevDependency("oxlint");
1886
2408
  const isStealth = generator.isStealthConfig();
1887
2409
  const { rules } = defaultLinterConfig;
1888
2410
  const plugins = ["unicorn", "typescript", "oxc"];
@@ -1943,7 +2465,7 @@ function generatePostprocessing(generator, options) {
1943
2465
  );
1944
2466
  return;
1945
2467
  }
1946
- generator.addDependency("@react-three/postprocessing", "^3.0.4");
2468
+ generator.addDependency("@react-three/postprocessing");
1947
2469
  generator.inject(
1948
2470
  "readme-libraries",
1949
2471
  `[@react-three/postprocessing](https://react-postprocessing.docs.pmnd.rs/) - Post-processing effects for @react-three/fiber`
@@ -1951,18 +2473,14 @@ function generatePostprocessing(generator, options) {
1951
2473
  }
1952
2474
 
1953
2475
  function generatePrettier(generator, options) {
1954
- const version = generator.versions.prettier ?? "3.4.2";
1955
- generator.addDevDependency("prettier", `^${version}`);
2476
+ generator.addDevDependency("prettier");
1956
2477
  const isStealth = generator.isStealthConfig();
1957
2478
  if (isStealth) {
1958
2479
  generator.addFile(".config/prettier.json", {
1959
2480
  type: "text",
1960
2481
  content: JSON.stringify(defaultPrettierConfig, null, 2)
1961
2482
  });
1962
- generator.addScript(
1963
- "format",
1964
- "prettier --config .config/prettier.json --write ."
1965
- );
2483
+ generator.addScript("format", "prettier --config .config/prettier.json --write .");
1966
2484
  generator.addVscodeSetting("prettier.configPath", ".config/prettier.json");
1967
2485
  } else {
1968
2486
  generator.addFile(".prettierrc", {
@@ -1971,22 +2489,16 @@ function generatePrettier(generator, options) {
1971
2489
  });
1972
2490
  generator.addScript("format", "prettier --write .");
1973
2491
  }
1974
- generator.inject(
1975
- "readme-tools",
1976
- "[Prettier](https://prettier.io/) - Opinionated code formatter"
1977
- );
2492
+ generator.inject("readme-tools", "[Prettier](https://prettier.io/) - Opinionated code formatter");
1978
2493
  generator.inject("vscode-extension-suggestion", "esbenp.prettier-vscode");
1979
- generator.addVscodeSetting(
1980
- "editor.defaultFormatter",
1981
- "esbenp.prettier-vscode"
1982
- );
2494
+ generator.addVscodeSetting("editor.defaultFormatter", "esbenp.prettier-vscode");
1983
2495
  }
1984
2496
 
1985
2497
  function generateRapier(generator, options) {
1986
2498
  if (options == null) {
1987
2499
  return;
1988
2500
  }
1989
- generator.addDependency("@react-three/rapier", "^2.1.0");
2501
+ generator.addDependency("@react-three/rapier");
1990
2502
  generator.inject(
1991
2503
  "readme-libraries",
1992
2504
  `[@react-three/rapier](https://github.com/pmndrs/react-three-rapier) - Physics based on Rapier for your @react-three/fiber scene`
@@ -2085,8 +2597,12 @@ function generateProvidersModule(generator) {
2085
2597
  const wrappedComponents = resolvedProviders.filter(
2086
2598
  (provider) => provider.type === "wrapped-jsx"
2087
2599
  );
2088
- const inlineComponents = resolvedProviders.filter((provider) => provider.type === "inline-jsx");
2089
- const layoutEffects = resolvedProviders.filter((provider) => provider.type === "layout-effect");
2600
+ const inlineComponents = resolvedProviders.filter(
2601
+ (provider) => provider.type === "inline-jsx"
2602
+ );
2603
+ const layoutEffects = resolvedProviders.filter(
2604
+ (provider) => provider.type === "layout-effect"
2605
+ );
2090
2606
  const declaredProps = providerProps.map((prop) => `${prop.declaredPropName} = ${prop.declaredPropDefaultValue}`).join(", ");
2091
2607
  const declaredTypes = providerProps.map((prop) => `${prop.declaredPropName}?: ${prop.declaredPropType}`).join("; ");
2092
2608
  const reactImports = ["type ReactNode"];
@@ -2161,7 +2677,7 @@ function generateTriplex(generator, options) {
2161
2677
  }
2162
2678
 
2163
2679
  function generateTsdown(generator) {
2164
- generator.addDevDependency("tsdown", "^0.12.0");
2680
+ generator.addDevDependency("tsdown");
2165
2681
  const template = generator.options.template ?? "vanilla";
2166
2682
  const baseTemplate = getBaseTemplate(template);
2167
2683
  const language = getLanguageFromTemplate(template);
@@ -2197,7 +2713,7 @@ function generateUikit(generator, options) {
2197
2713
  if (options == null) {
2198
2714
  return;
2199
2715
  }
2200
- generator.addDependency("@react-three/uikit", "^0.8.15");
2716
+ generator.addDependency("@react-three/uikit");
2201
2717
  generator.inject(
2202
2718
  "readme-libraries",
2203
2719
  `[@react-three/uikit](https://pmndrs.github.io/uikit/docs/) - UI primitives for React Three Fiber`
@@ -2205,7 +2721,7 @@ function generateUikit(generator, options) {
2205
2721
  }
2206
2722
 
2207
2723
  function generateUnbuild(generator) {
2208
- generator.addDevDependency("unbuild", "^3.5.0");
2724
+ generator.addDevDependency("unbuild");
2209
2725
  const template = generator.options.template ?? "vanilla";
2210
2726
  const baseTemplate = getBaseTemplate(template);
2211
2727
  const language = getLanguageFromTemplate(template);
@@ -2250,15 +2766,14 @@ function generateUnbuild(generator) {
2250
2766
  }
2251
2767
 
2252
2768
  function generateVitest(generator) {
2253
- const version = generator.versions.vitest ?? "4.0.0";
2254
- generator.addDevDependency("vitest", `^${version}`);
2769
+ generator.addDevDependency("vitest");
2255
2770
  const template = generator.options.template ?? "vanilla";
2256
2771
  const baseTemplate = getBaseTemplate(template);
2257
2772
  const isReact = baseTemplate === "react" || baseTemplate === "r3f";
2258
2773
  if (isReact) {
2259
- generator.addDevDependency("@testing-library/react", "^16.2.0");
2260
- generator.addDevDependency("@testing-library/dom", "^10.4.0");
2261
- generator.addDevDependency("jsdom", "^26.0.0");
2774
+ generator.addDevDependency("@testing-library/react");
2775
+ generator.addDevDependency("@testing-library/dom");
2776
+ generator.addDevDependency("jsdom");
2262
2777
  }
2263
2778
  if (isReact) {
2264
2779
  generator.configureVite({ test: { environment: "jsdom" } });
@@ -2271,7 +2786,7 @@ function generateVitest(generator) {
2271
2786
  }
2272
2787
 
2273
2788
  function generateViverse(generator, options) {
2274
- if (options == null || (generator.options.packageManager ?? "npm") != "npm") {
2789
+ if (options == null || getPackageManagerName(generator.options.packageManager) !== "npm") {
2275
2790
  return;
2276
2791
  }
2277
2792
  generator.addFile(".github/workflows/viverse.yml", {
@@ -2329,7 +2844,7 @@ jobs:
2329
2844
 
2330
2845
  `
2331
2846
  });
2332
- generator.addDependency("@viverse/cli", "^0.9.5-beta.8");
2847
+ generator.addDependency("@viverse/cli");
2333
2848
  generator.inject(
2334
2849
  "readme-start",
2335
2850
  `A GitHub CI/CD workflow for publishing to Viverse is configured.
@@ -2356,8 +2871,8 @@ function generateXr(generator, options) {
2356
2871
  if (options === true) {
2357
2872
  options = {};
2358
2873
  }
2359
- generator.addDependency("@react-three/xr", "^6.6.16");
2360
- generator.addDependency("@vitejs/plugin-basic-ssl", "^2.0.0");
2874
+ generator.addDependency("@react-three/xr");
2875
+ generator.addDependency("@vitejs/plugin-basic-ssl");
2361
2876
  generator.inject("import", "import { XR, createXRStore } from '@react-three/xr'");
2362
2877
  generator.inject(
2363
2878
  `global-start`,
@@ -2414,7 +2929,7 @@ function generateZustand(generator, options) {
2414
2929
  if (options == null) {
2415
2930
  return;
2416
2931
  }
2417
- generator.addDependency("zustand", "^5.0.3");
2932
+ generator.addDependency("zustand");
2418
2933
  generator.inject(
2419
2934
  "readme-libraries",
2420
2935
  `[zustand](https://zustand.docs.pmnd.rs/) - small, fast and scalable state-management solution`
@@ -2452,255 +2967,6 @@ function merge(target, modification) {
2452
2967
  return modification;
2453
2968
  }
2454
2969
 
2455
- async function getLatestNpmVersion(packageName, fallback) {
2456
- try {
2457
- const response = await fetch(
2458
- `https://registry.npmjs.org/${packageName}/latest`
2459
- );
2460
- const data = await response.json();
2461
- return data.version;
2462
- } catch {
2463
- return fallback;
2464
- }
2465
- }
2466
- async function getLatestPnpmVersion() {
2467
- return getLatestNpmVersion("pnpm", "10.11.0");
2468
- }
2469
- async function getLatestYarnVersion() {
2470
- return getLatestNpmVersion("yarn", "4.6.0");
2471
- }
2472
- async function getLatestNpmCliVersion() {
2473
- return getLatestNpmVersion("npm", "11.0.0");
2474
- }
2475
- async function getLatestNodeVersion() {
2476
- try {
2477
- const response = await fetch("https://nodejs.org/dist/index.json");
2478
- const data = await response.json();
2479
- const ltsVersion = data.find((v) => v.lts);
2480
- if (ltsVersion) {
2481
- return ltsVersion.version.replace(/^v/, "");
2482
- }
2483
- return "22.0.0";
2484
- } catch {
2485
- return "22.0.0";
2486
- }
2487
- }
2488
- function validateNameSegment(segment, label) {
2489
- if (!segment.length) {
2490
- return `${label} is required`;
2491
- }
2492
- if (!/^[a-z0-9-]+$/.test(segment)) {
2493
- return `${label} must be lowercase and contain only letters, numbers, and hyphens`;
2494
- }
2495
- if (segment.startsWith("-") || segment.endsWith("-")) {
2496
- return `${label} cannot start or end with a hyphen`;
2497
- }
2498
- if (segment.includes("--")) {
2499
- return `${label} cannot contain consecutive hyphens`;
2500
- }
2501
- return void 0;
2502
- }
2503
- function validatePackageName(name) {
2504
- if (!name.length) {
2505
- return "Package name is required";
2506
- }
2507
- if (name.includes("..") || name.includes("\\")) {
2508
- return "Package name cannot contain path traversal sequences";
2509
- }
2510
- if (name.startsWith("@")) {
2511
- const slashIndex = name.indexOf("/");
2512
- if (slashIndex === -1) {
2513
- return "Scoped package name must include a package name after the scope (e.g., @scope/name)";
2514
- }
2515
- if (name.indexOf("/", slashIndex + 1) !== -1) {
2516
- return "Package name can only have one slash for scoped packages";
2517
- }
2518
- const scope = name.slice(1, slashIndex);
2519
- const packageName = name.slice(slashIndex + 1);
2520
- const scopeError = validateNameSegment(scope, "Scope");
2521
- if (scopeError) return scopeError;
2522
- const nameError = validateNameSegment(packageName, "Package name");
2523
- if (nameError) return nameError;
2524
- return void 0;
2525
- }
2526
- if (name.includes("/")) {
2527
- return "Unscoped package name cannot contain slashes. Use @scope/name format for scoped packages";
2528
- }
2529
- return validateNameSegment(name, "Package name");
2530
- }
2531
- function parseWorkspaceYamlContent(content) {
2532
- const directories = [];
2533
- let inPackagesSection = false;
2534
- for (const line of content.split("\n")) {
2535
- const trimmed = line.trim();
2536
- if (trimmed === "packages:") {
2537
- inPackagesSection = true;
2538
- continue;
2539
- }
2540
- if (inPackagesSection && trimmed && !line.startsWith(" ") && !line.startsWith(" ") && !trimmed.startsWith("-")) {
2541
- break;
2542
- }
2543
- if (inPackagesSection && trimmed.startsWith("-")) {
2544
- const entry = trimmed.slice(1).trim().replace(/^["']|["']$/g, "").replace(/^\.\//, "").replace(/\/\*.*$/, "");
2545
- if (entry && !entry.startsWith(".")) {
2546
- directories.push(entry);
2547
- }
2548
- }
2549
- }
2550
- return directories;
2551
- }
2552
- async function pathExists(path) {
2553
- try {
2554
- await promises.access(path, fs.constants.F_OK);
2555
- return true;
2556
- } catch {
2557
- return false;
2558
- }
2559
- }
2560
- function detectLinterFromScript(script) {
2561
- if (!script) return void 0;
2562
- if (script.includes("oxlint")) return "oxlint";
2563
- if (script.includes("eslint")) return "eslint";
2564
- if (script.includes("biome check") || script.includes("biome lint")) return "biome";
2565
- return void 0;
2566
- }
2567
- function detectFormatterFromScript(script) {
2568
- if (!script) return void 0;
2569
- if (script.includes("prettier")) return "prettier";
2570
- if (script.includes("oxfmt")) return "oxfmt";
2571
- if (script.includes("biome format")) return "biome";
2572
- return void 0;
2573
- }
2574
- async function detectLinterFromConfig(root) {
2575
- if (await pathExists(path.join(root, ".config/oxlint"))) return "oxlint";
2576
- if (await pathExists(path.join(root, ".config/eslint"))) return "eslint";
2577
- if (await pathExists(path.join(root, "biome.json"))) {
2578
- try {
2579
- const content = await promises.readFile(path.join(root, "biome.json"), "utf-8");
2580
- const config = JSON.parse(content);
2581
- if (config.linter?.enabled !== false) return "biome";
2582
- } catch {
2583
- return "biome";
2584
- }
2585
- }
2586
- return void 0;
2587
- }
2588
- async function detectFormatterFromConfig(root) {
2589
- if (await pathExists(path.join(root, ".config/prettier"))) return "prettier";
2590
- if (await pathExists(path.join(root, ".config/oxfmt"))) return "oxfmt";
2591
- if (await pathExists(path.join(root, "biome.json"))) {
2592
- try {
2593
- const content = await promises.readFile(path.join(root, "biome.json"), "utf-8");
2594
- const config = JSON.parse(content);
2595
- if (config.formatter?.enabled !== false) return "biome";
2596
- } catch {
2597
- return "biome";
2598
- }
2599
- }
2600
- return void 0;
2601
- }
2602
- function detectLinterFromDeps(devDeps) {
2603
- if (!devDeps) return void 0;
2604
- if (devDeps["@biomejs/biome"]) return "biome";
2605
- if (devDeps.eslint) return "eslint";
2606
- if (devDeps.oxlint) return "oxlint";
2607
- return void 0;
2608
- }
2609
- function detectFormatterFromDeps(devDeps) {
2610
- if (!devDeps) return void 0;
2611
- if (devDeps["@biomejs/biome"]) return "biome";
2612
- if (devDeps.prettier) return "prettier";
2613
- if (devDeps.oxfmt) return "oxfmt";
2614
- return void 0;
2615
- }
2616
- async function detectTooling(root) {
2617
- try {
2618
- const pkgPath = path.join(root, "package.json");
2619
- const content = await promises.readFile(pkgPath, "utf-8");
2620
- const pkg = JSON.parse(content);
2621
- const linter = detectLinterFromScript(pkg.scripts?.lint) ?? await detectLinterFromConfig(root) ?? detectLinterFromDeps(pkg.devDependencies);
2622
- const formatter = detectFormatterFromScript(pkg.scripts?.format) ?? await detectFormatterFromConfig(root) ?? detectFormatterFromDeps(pkg.devDependencies);
2623
- return { linter, formatter };
2624
- } catch {
2625
- return { linter: void 0, formatter: void 0 };
2626
- }
2627
- }
2628
- function generateRandomName() {
2629
- const adjectives = [
2630
- "red",
2631
- "blue",
2632
- "green",
2633
- "yellow",
2634
- "purple",
2635
- "orange",
2636
- "pink",
2637
- "black",
2638
- "white",
2639
- "tiny",
2640
- "big",
2641
- "small",
2642
- "large",
2643
- "huge",
2644
- "giant",
2645
- "mini",
2646
- "mega",
2647
- "super",
2648
- "happy",
2649
- "sad",
2650
- "angry",
2651
- "calm",
2652
- "quiet",
2653
- "loud",
2654
- "silent",
2655
- "noisy",
2656
- "shiny",
2657
- "dull",
2658
- "bright",
2659
- "dark",
2660
- "fuzzy",
2661
- "smooth",
2662
- "rough",
2663
- "soft"
2664
- ];
2665
- const nouns = [
2666
- "apple",
2667
- "banana",
2668
- "cherry",
2669
- "date",
2670
- "elderberry",
2671
- "fig",
2672
- "grape",
2673
- "honeydew",
2674
- "cat",
2675
- "dog",
2676
- "elephant",
2677
- "fox",
2678
- "giraffe",
2679
- "horse",
2680
- "iguana",
2681
- "jaguar",
2682
- "mountain",
2683
- "river",
2684
- "ocean",
2685
- "desert",
2686
- "forest",
2687
- "jungle",
2688
- "meadow",
2689
- "valley",
2690
- "star",
2691
- "moon",
2692
- "sun",
2693
- "planet",
2694
- "comet",
2695
- "asteroid",
2696
- "galaxy",
2697
- "universe"
2698
- ];
2699
- const randomAdjective = adjectives[Math.floor(Math.random() * adjectives.length)];
2700
- const randomNoun = nouns[Math.floor(Math.random() * nouns.length)];
2701
- return `${randomAdjective}-${randomNoun}`;
2702
- }
2703
-
2704
2970
  function generate(options) {
2705
2971
  const clonedOptions = structuredClone(options);
2706
2972
  const template = clonedOptions.template ?? "vanilla";
@@ -2722,16 +2988,16 @@ function generate(options) {
2722
2988
  const devDependencies = {};
2723
2989
  const peerDependencies = {};
2724
2990
  if (!isLibrary) {
2725
- devDependencies.vite = versions.vite ? `^${versions.vite}` : "^6.3.4";
2991
+ assignResolvedPackageVersion(devDependencies, versions, "vite");
2726
2992
  }
2727
2993
  if (isReact || isR3f) {
2728
2994
  if (isLibrary) {
2729
2995
  peerDependencies["react"] = "^18.0.0 || ^19.0.0";
2730
2996
  peerDependencies["react-dom"] = "^18.0.0 || ^19.0.0";
2731
2997
  } else {
2732
- dependencies["react"] = "^19.0.0";
2733
- dependencies["react-dom"] = "^19.0.0";
2734
- devDependencies["@vitejs/plugin-react"] = "^4.4.1";
2998
+ assignResolvedPackageVersion(dependencies, versions, "react");
2999
+ assignResolvedPackageVersion(dependencies, versions, "react-dom");
3000
+ assignResolvedPackageVersion(devDependencies, versions, "@vitejs/plugin-react");
2735
3001
  }
2736
3002
  }
2737
3003
  if (isR3f) {
@@ -2739,15 +3005,17 @@ function generate(options) {
2739
3005
  peerDependencies["three"] = ">=0.150.0";
2740
3006
  peerDependencies["@react-three/fiber"] = "^8.0.0 || ^9.0.0";
2741
3007
  } else {
2742
- dependencies["three"] = "~0.175.0";
2743
- dependencies["@react-three/fiber"] = "^9.0.0";
3008
+ assignResolvedPackageVersion(dependencies, versions, "three", "~");
3009
+ assignResolvedPackageVersion(dependencies, versions, "@react-three/fiber");
2744
3010
  }
2745
3011
  }
2746
3012
  if (language === "typescript") {
2747
3013
  const tsResult = generateTypescriptConfig({
2748
3014
  baseTemplate,
2749
3015
  useConfigPackage: clonedOptions.workspaceRoot != null,
2750
- configStrategy: clonedOptions.configStrategy
3016
+ configStrategy: clonedOptions.configStrategy,
3017
+ engine: clonedOptions.engine,
3018
+ versions
2751
3019
  });
2752
3020
  Object.assign(files, tsResult.files);
2753
3021
  Object.assign(devDependencies, tsResult.devDependencies);
@@ -2759,9 +3027,7 @@ function generate(options) {
2759
3027
  build: "vite build"
2760
3028
  };
2761
3029
  if (!isLibrary && (isReact || isR3f)) {
2762
- codeSnippets["vite-config-import"] = [
2763
- "import react from '@vitejs/plugin-react'"
2764
- ];
3030
+ codeSnippets["vite-config-import"] = ["import react from '@vitejs/plugin-react'"];
2765
3031
  }
2766
3032
  if (!isLibrary && isR3f) {
2767
3033
  codeSnippets["import"] = [`import { Canvas } from "@react-three/fiber"`];
@@ -2781,16 +3047,29 @@ function generate(options) {
2781
3047
  const generator = {
2782
3048
  options: clonedOptions,
2783
3049
  versions,
3050
+ getVersion(name2) {
3051
+ return getResolvedPackageVersion(versions, name2);
3052
+ },
2784
3053
  isStealthConfig() {
2785
3054
  return (clonedOptions.configStrategy ?? "stealth") === "stealth";
2786
3055
  },
2787
- addDependency(name2, semver) {
2788
- dependencies[name2];
2789
- dependencies[name2] = semver;
3056
+ addDependency(name2, options2) {
3057
+ if (dependencies[name2] != null) {
3058
+ return;
3059
+ }
3060
+ dependencies[name2] = resolveDependencySemver(name2, versions, options2);
3061
+ },
3062
+ addDevDependency(name2, options2) {
3063
+ if (devDependencies[name2] != null) {
3064
+ return;
3065
+ }
3066
+ devDependencies[name2] = resolveDependencySemver(name2, versions, options2);
2790
3067
  },
2791
- addDevDependency(name2, semver) {
2792
- devDependencies[name2];
2793
- devDependencies[name2] = semver;
3068
+ addPeerDependency(name2, semver) {
3069
+ if (peerDependencies[name2] != null) {
3070
+ return;
3071
+ }
3072
+ peerDependencies[name2] = semver;
2794
3073
  },
2795
3074
  addFile(path, content) {
2796
3075
  files[path] = content;
@@ -2839,11 +3118,8 @@ function generate(options) {
2839
3118
  } else if (libraryBundler === "tsdown") {
2840
3119
  generateTsdown(generator);
2841
3120
  }
2842
- const packageManager2 = clonedOptions.packageManager ?? "pnpm";
2843
- generator.addScript(
2844
- "release",
2845
- `${packageManager2} run build && ${packageManager2} publish`
2846
- );
3121
+ const packageManager2 = getPackageManagerName(clonedOptions.packageManager);
3122
+ generator.addScript("release", `${packageManager2} run build && ${packageManager2} publish`);
2847
3123
  }
2848
3124
  const testing = clonedOptions.testing ?? (isLibrary ? "vitest" : "none");
2849
3125
  if (testing === "vitest") {
@@ -2882,7 +3158,7 @@ function generate(options) {
2882
3158
  if (!isLibrary) {
2883
3159
  files["vite.config.ts"] = generateViteConfig({ viteConfig, codeSnippets });
2884
3160
  }
2885
- const packageManager = options.packageManager ?? "pnpm";
3161
+ const packageManager = getPackageManagerName(options.packageManager);
2886
3162
  files["README.md"] = generateReadme({
2887
3163
  name,
2888
3164
  baseTemplate,
@@ -2930,16 +3206,13 @@ function generate(options) {
2930
3206
  Object.assign(files, generateVscodeFiles$1({ codeSnippets, vscodeSettings }));
2931
3207
  }
2932
3208
  if (!isMonorepoPackage) {
2933
- files[".gitignore"] = {
2934
- type: "text",
2935
- content: ["node_modules", "dist", "*.tsbuildinfo"].join("\n")
2936
- };
3209
+ files[".gitignore"] = generateGitignore("standalone");
2937
3210
  files[".gitattributes"] = { type: "text", content: GitAttributes };
2938
3211
  }
2939
3212
  if (!isMonorepoPackage && clonedOptions.aiPlatforms?.length) {
2940
3213
  generateAiFiles(files, {
2941
3214
  name,
2942
- packageManager: clonedOptions.packageManager ?? "pnpm",
3215
+ packageManager: getPackageManagerName(clonedOptions.packageManager),
2943
3216
  linter: clonedOptions.linter ?? "oxlint",
2944
3217
  formatter: clonedOptions.formatter ?? "prettier",
2945
3218
  isMonorepo: false,
@@ -2949,14 +3222,22 @@ function generate(options) {
2949
3222
  }
2950
3223
  return files;
2951
3224
  }
3225
+ function resolveDependencySemver(name, versions, options = {}) {
3226
+ if (options.version != null) {
3227
+ return options.version;
3228
+ }
3229
+ return formatResolvedPackageVersion(versions, name, options.prefix);
3230
+ }
2952
3231
 
2953
3232
  exports.AI_PLATFORM_HINTS = AI_PLATFORM_HINTS;
2954
3233
  exports.AI_PLATFORM_LABELS = AI_PLATFORM_LABELS;
2955
3234
  exports.ALL_AI_PLATFORMS = ALL_AI_PLATFORMS;
2956
3235
  exports.detectTooling = detectTooling;
3236
+ exports.formatResolvedPackageVersion = formatResolvedPackageVersion;
2957
3237
  exports.generate = generate;
2958
3238
  exports.generateAiFiles = generateAiFiles;
2959
3239
  exports.generateEslintConfigPackage = generateEslintConfigPackage;
3240
+ exports.generateGitignore = generateGitignore;
2960
3241
  exports.generateMonorepo = generateMonorepo;
2961
3242
  exports.generateOxfmtConfigPackage = generateOxfmtConfigPackage;
2962
3243
  exports.generateOxlintConfigPackage = generateOxlintConfigPackage;
@@ -2965,12 +3246,21 @@ exports.generateRandomName = generateRandomName;
2965
3246
  exports.generateTypescriptConfigPackage = generateTypescriptConfigPackage;
2966
3247
  exports.generateVscodeFiles = generateVscodeFiles;
2967
3248
  exports.getBaseTemplate = getBaseTemplate;
3249
+ exports.getEngineName = getEngineName;
2968
3250
  exports.getLanguageFromTemplate = getLanguageFromTemplate;
2969
3251
  exports.getLatestNodeVersion = getLatestNodeVersion;
2970
3252
  exports.getLatestNpmCliVersion = getLatestNpmCliVersion;
2971
3253
  exports.getLatestNpmVersion = getLatestNpmVersion;
2972
3254
  exports.getLatestPnpmVersion = getLatestPnpmVersion;
2973
3255
  exports.getLatestYarnVersion = getLatestYarnVersion;
3256
+ exports.getPackageManagerName = getPackageManagerName;
3257
+ exports.getResolvedPackageVersion = getResolvedPackageVersion;
2974
3258
  exports.monorepo = monorepo;
3259
+ exports.parseEngine = parseEngine;
3260
+ exports.parsePackageManager = parsePackageManager;
2975
3261
  exports.parseWorkspaceYamlContent = parseWorkspaceYamlContent;
3262
+ exports.resolveEngine = resolveEngine;
3263
+ exports.resolveMonorepoRootPackageVersions = resolveMonorepoRootPackageVersions;
3264
+ exports.resolvePackageManager = resolvePackageManager;
3265
+ exports.resolveProjectPackageVersions = resolveProjectPackageVersions;
2976
3266
  exports.validatePackageName = validatePackageName;