rsetup 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,7 +8,7 @@ npx rsetup --help
8
8
 
9
9
  This is the public npm name for RepoSetup. After `npm install -g rsetup`, both `rsetup` and `reposetup` work.
10
10
 
11
- This is the `0.2.0` CLI. It requires Node.js 24 or later. Python recipes require Python 3.12 or later and uv; 3.12 and 3.13 are in the qualification matrix. Integrations are not all equally mature. Use `--dry-run` before changing an important project.
11
+ This is the `0.2.1` CLI. It requires Node.js 24 or later. Python recipes require Python 3.12 or later and uv; 3.12 and 3.13 are in the qualification matrix. Integrations are not all equally mature. Use `--dry-run` before changing an important project.
12
12
 
13
13
  The workspace libraries `@reposetup/core`, `@reposetup/registry`, and `@reposetup/integrations` are bundled into this package. They are not published separately.
14
14
 
package/dist/bin.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runCli
4
- } from "./chunk-6SOBVOJQ.js";
4
+ } from "./chunk-D3YJGGEE.js";
5
5
 
6
6
  // src/bin.ts
7
7
  var abortController = new AbortController();
@@ -288,10 +288,10 @@ function mergeDefs(...defs) {
288
288
  function cloneDef(schema) {
289
289
  return mergeDefs(schema._zod.def);
290
290
  }
291
- function getElementAtPath(obj, path12) {
292
- if (!path12)
291
+ function getElementAtPath(obj, path13) {
292
+ if (!path13)
293
293
  return obj;
294
- return path12.reduce((acc, key) => acc?.[key], obj);
294
+ return path13.reduce((acc, key) => acc?.[key], obj);
295
295
  }
296
296
  function promiseAllObject(promisesObj) {
297
297
  const keys = Object.keys(promisesObj);
@@ -631,11 +631,11 @@ function explicitlyAborted(x, startIndex = 0) {
631
631
  }
632
632
  return false;
633
633
  }
634
- function prefixIssues(path12, issues) {
634
+ function prefixIssues(path13, issues) {
635
635
  return issues.map((iss) => {
636
636
  var _a3;
637
637
  (_a3 = iss).path ?? (_a3.path = []);
638
- iss.path.unshift(path12);
638
+ iss.path.unshift(path13);
639
639
  return iss;
640
640
  });
641
641
  }
@@ -1081,16 +1081,16 @@ function flattenError(error2, mapper = (issue2) => issue2.message) {
1081
1081
  }
1082
1082
  function formatError(error2, mapper = (issue2) => issue2.message) {
1083
1083
  const fieldErrors = { _errors: [] };
1084
- const processError = (error3, path12 = []) => {
1084
+ const processError = (error3, path13 = []) => {
1085
1085
  for (const issue2 of error3.issues) {
1086
1086
  if (issue2.code === "invalid_union" && issue2.errors.length) {
1087
- issue2.errors.map((issues) => processError({ issues }, [...path12, ...issue2.path]));
1087
+ issue2.errors.map((issues) => processError({ issues }, [...path13, ...issue2.path]));
1088
1088
  } else if (issue2.code === "invalid_key") {
1089
- processError({ issues: issue2.issues }, [...path12, ...issue2.path]);
1089
+ processError({ issues: issue2.issues }, [...path13, ...issue2.path]);
1090
1090
  } else if (issue2.code === "invalid_element") {
1091
- processError({ issues: issue2.issues }, [...path12, ...issue2.path]);
1091
+ processError({ issues: issue2.issues }, [...path13, ...issue2.path]);
1092
1092
  } else {
1093
- const fullpath = [...path12, ...issue2.path];
1093
+ const fullpath = [...path13, ...issue2.path];
1094
1094
  if (fullpath.length === 0) {
1095
1095
  fieldErrors._errors.push(mapper(issue2));
1096
1096
  } else {
@@ -5977,9 +5977,10 @@ import path from "path";
5977
5977
  import { access as access2 } from "fs/promises";
5978
5978
  import path2 from "path";
5979
5979
  import path3 from "path";
5980
+ import path4 from "path";
5981
+ import path7 from "path";
5980
5982
  import path6 from "path";
5981
5983
  import path5 from "path";
5982
- import path4 from "path";
5983
5984
  import { createHash as createHash2 } from "crypto";
5984
5985
  var INTEGRATION_CATEGORIES = [
5985
5986
  "runtime",
@@ -6265,10 +6266,10 @@ async function firstExistingPath(files, candidates) {
6265
6266
  }
6266
6267
  return void 0;
6267
6268
  }
6268
- function evidence(kind, detail, path72) {
6269
+ function evidence(kind, detail, path82) {
6269
6270
  const item = { kind, detail };
6270
- if (path72 !== void 0) {
6271
- item.path = path72;
6271
+ if (path82 !== void 0) {
6272
+ item.path = path82;
6272
6273
  }
6273
6274
  return item;
6274
6275
  }
@@ -7092,6 +7093,7 @@ var runCommandOperationSchema = strictObject({
7092
7093
  requiresNetwork: boolean2().optional(),
7093
7094
  interactive: boolean2().optional(),
7094
7095
  longRunning: boolean2().optional(),
7096
+ skipsDependencyInstall: boolean2().optional(),
7095
7097
  requiresLockfile: _enum(["package-lock.json", "pnpm-lock.yaml", "bun.lock", "uv.lock"]).optional()
7096
7098
  });
7097
7099
  var createDirectoryOperationSchema = strictObject({
@@ -8410,9 +8412,14 @@ function isProjectInstallCommand(operation) {
8410
8412
  }
8411
8413
  function ensureScaffoldDependencyInstall(operations, packageManager, projectRoot) {
8412
8414
  const output = [];
8415
+ let pendingInstall;
8413
8416
  for (const [index, operation] of operations.entries()) {
8417
+ if (pendingInstall !== void 0 && (operation.type === "run_command" || operation.type === "verify")) {
8418
+ output.push(pendingInstall);
8419
+ pendingInstall = void 0;
8420
+ }
8414
8421
  output.push(operation);
8415
- if (operation.type !== "run_command" || !operation.args.includes("--skip-install")) {
8422
+ if (operation.type !== "run_command" || operation.skipsDependencyInstall !== true && !operation.args.includes("--skip-install")) {
8416
8423
  continue;
8417
8424
  }
8418
8425
  if (segmentCoversScaffoldInstall(operations, index + 1)) {
@@ -8438,8 +8445,9 @@ function ensureScaffoldDependencyInstall(operations, packageManager, projectRoot
8438
8445
  if (!installed.ok) {
8439
8446
  return { ok: false, error: installed.error };
8440
8447
  }
8441
- output.push(installed.operation);
8448
+ pendingInstall = installed.operation;
8442
8449
  }
8450
+ if (pendingInstall !== void 0) output.push(pendingInstall);
8443
8451
  return { ok: true, operations: output };
8444
8452
  }
8445
8453
  function segmentCoversScaffoldInstall(operations, start) {
@@ -8501,7 +8509,23 @@ function planResolvedIds(resolved, registry2, ids) {
8501
8509
  return {
8502
8510
  ...resolved,
8503
8511
  orderedIntegrations: ordered,
8504
- operations: validation.operations
8512
+ operations: projectRoot === "." ? validation.operations : [
8513
+ {
8514
+ type: "create_directory",
8515
+ path: projectRoot,
8516
+ behavior: "create_if_missing",
8517
+ description: "Create the selected project directory"
8518
+ },
8519
+ ...validation.operations.map(
8520
+ (operation) => "path" in operation ? {
8521
+ ...operation,
8522
+ path: path4.posix.join(
8523
+ projectRoot.replaceAll("\\", "/"),
8524
+ operation.path.replaceAll("\\", "/")
8525
+ )
8526
+ } : operation
8527
+ )
8528
+ ]
8505
8529
  };
8506
8530
  }
8507
8531
  function withBatchedInstalls(result) {
@@ -9240,10 +9264,10 @@ function resolveInsideRoot(rootDir, relativePath) {
9240
9264
  })
9241
9265
  };
9242
9266
  }
9243
- const root = path4.resolve(rootDir);
9244
- const absolutePath = path4.resolve(root, relativePath);
9245
- const relative = path4.relative(root, absolutePath);
9246
- if (relative.startsWith("..") || path4.isAbsolute(relative)) {
9267
+ const root = path5.resolve(rootDir);
9268
+ const absolutePath = path5.resolve(root, relativePath);
9269
+ const relative = path5.relative(root, absolutePath);
9270
+ if (relative.startsWith("..") || path5.isAbsolute(relative)) {
9247
9271
  return {
9248
9272
  ok: false,
9249
9273
  error: createRepoSetupError({
@@ -9261,8 +9285,8 @@ async function assertRealPathInsideRoot(rootDir, absolutePath, fs) {
9261
9285
  const root = await fs.realpath(rootDir);
9262
9286
  const existingAncestor = await nearestExistingAncestor(absolutePath, fs);
9263
9287
  const resolvedAncestor = await fs.realpath(existingAncestor);
9264
- const relative = path4.relative(root, resolvedAncestor);
9265
- if (relative === "" || !relative.startsWith("..") && !path4.isAbsolute(relative)) {
9288
+ const relative = path5.relative(root, resolvedAncestor);
9289
+ if (relative === "" || !relative.startsWith("..") && !path5.isAbsolute(relative)) {
9266
9290
  return { ok: true };
9267
9291
  }
9268
9292
  return escapedPathError(absolutePath, rootDir);
@@ -9282,9 +9306,9 @@ async function assertRealPathInsideRoot(rootDir, absolutePath, fs) {
9282
9306
  }
9283
9307
  }
9284
9308
  async function nearestExistingAncestor(pathToCheck, fs) {
9285
- let current = path4.resolve(pathToCheck);
9309
+ let current = path5.resolve(pathToCheck);
9286
9310
  while (!await fs.exists(current)) {
9287
- const parent = path4.dirname(current);
9311
+ const parent = path5.dirname(current);
9288
9312
  if (parent === current) {
9289
9313
  return current;
9290
9314
  }
@@ -9460,7 +9484,8 @@ async function executeModifyText(operation, context) {
9460
9484
  }
9461
9485
  const crlfOldText = operation.oldText.replaceAll("\n", "\r\n");
9462
9486
  const matchedText = content.includes(operation.oldText) ? operation.oldText : content.includes(crlfOldText) ? crlfOldText : operation.oldText;
9463
- const replacementText = matchedText === crlfOldText ? operation.newText.replaceAll("\n", "\r\n") : operation.newText;
9487
+ const usesCrlf = operation.oldText.includes("\n") ? matchedText === crlfOldText && crlfOldText !== operation.oldText : content.includes("\r\n");
9488
+ const replacementText = usesCrlf ? operation.newText.replaceAll("\n", "\r\n") : operation.newText;
9464
9489
  const matches = content.split(matchedText).length - 1;
9465
9490
  if (matches === 0) {
9466
9491
  return mutationFailed(`Text to replace was not found in "${operation.path}".`, {
@@ -9544,9 +9569,9 @@ async function executeAddEnvExample(operation, context) {
9544
9569
  return void 0;
9545
9570
  }
9546
9571
  async function ensureParentDirectory(absoluteFilePath, context) {
9547
- const parent = path5.dirname(absoluteFilePath);
9548
- const relative = path5.relative(context.rootDir, parent);
9549
- if (relative.startsWith("..") || path5.isAbsolute(relative)) {
9572
+ const parent = path6.dirname(absoluteFilePath);
9573
+ const relative = path6.relative(context.rootDir, parent);
9574
+ if (relative.startsWith("..") || path6.isAbsolute(relative)) {
9550
9575
  return createRepoSetupError({
9551
9576
  code: "PLAN_INVALID",
9552
9577
  message: "Refusing to create a parent directory outside the project root.",
@@ -9994,7 +10019,15 @@ async function checkRequiredLockfile(operations, context) {
9994
10019
  if (operation.type !== "run_command" || operation.requiresLockfile === void 0) {
9995
10020
  continue;
9996
10021
  }
9997
- if (await context.fs.exists(`${context.rootDir}/${operation.requiresLockfile}`)) {
10022
+ const directory = resolveInsideRoot(context.rootDir, operation.cwd);
10023
+ if (!directory.ok) return directory.error;
10024
+ const realPath = await assertRealPathInsideRoot(
10025
+ context.rootDir,
10026
+ directory.absolutePath,
10027
+ context.fs
10028
+ );
10029
+ if (!realPath.ok) return realPath.error;
10030
+ if (await context.fs.exists(`${directory.absolutePath}/${operation.requiresLockfile}`)) {
9998
10031
  continue;
9999
10032
  }
10000
10033
  return createRepoSetupError({
@@ -10007,41 +10040,52 @@ async function checkRequiredLockfile(operations, context) {
10007
10040
  return void 0;
10008
10041
  }
10009
10042
  function packageManagersInPlan(operations) {
10010
- const managers = /* @__PURE__ */ new Set();
10043
+ const directories = /* @__PURE__ */ new Map();
10011
10044
  for (const operation of operations) {
10045
+ let manager;
10012
10046
  if (operation.type === "install_package") {
10013
- managers.add(operation.packageManager);
10014
- continue;
10015
- }
10016
- if (operation.type !== "run_command") {
10017
- continue;
10047
+ manager = operation.packageManager;
10048
+ } else if (operation.type === "run_command") {
10049
+ if (operation.skipsDependencyInstall === true || operation.args.includes("--skip-install"))
10050
+ continue;
10051
+ if (operation.command === "npm" || operation.command === "npx") manager = "npm";
10052
+ else if (operation.command === "pnpm") manager = "pnpm";
10053
+ else if (operation.command === "uv") manager = "uv";
10018
10054
  }
10019
- if (operation.command === "npm" || operation.command === "npx") {
10020
- managers.add("npm");
10021
- } else if (operation.command === "pnpm") {
10022
- managers.add("pnpm");
10023
- } else if (operation.command === "uv") {
10024
- managers.add("uv");
10055
+ if (manager !== void 0 && "cwd" in operation) {
10056
+ const managers = directories.get(operation.cwd) ?? /* @__PURE__ */ new Set();
10057
+ managers.add(manager);
10058
+ directories.set(operation.cwd, managers);
10025
10059
  }
10026
10060
  }
10027
- return managers;
10061
+ return directories;
10028
10062
  }
10029
10063
  async function checkLockfileCompatibility(operations, context) {
10030
- const managers = packageManagersInPlan(operations);
10064
+ const directories = packageManagersInPlan(operations);
10031
10065
  const lockfiles = [
10032
10066
  { manager: "npm", file: "package-lock.json" },
10033
10067
  { manager: "pnpm", file: "pnpm-lock.yaml" },
10034
10068
  { manager: "bun", file: "bun.lock" },
10035
10069
  { manager: "uv", file: "uv.lock" }
10036
10070
  ];
10037
- for (const lockfile of lockfiles) {
10038
- if (managers.size > 0 && !managers.has(lockfile.manager) && await context.fs.exists(`${context.rootDir}/${lockfile.file}`))
10039
- return createRepoSetupError({
10040
- code: "LOCKFILE_CONFLICT",
10041
- message: `Found ${lockfile.file}, which conflicts with this plan's package manager.`,
10042
- details: { lockfile: lockfile.file, managers: [...managers] },
10043
- suggestion: "Use the package manager recorded by the existing lockfile or remove the conflicting lockfile deliberately."
10044
- });
10071
+ for (const [cwd, managers] of directories) {
10072
+ const directory = resolveInsideRoot(context.rootDir, cwd);
10073
+ if (!directory.ok) return directory.error;
10074
+ const realPath = await assertRealPathInsideRoot(
10075
+ context.rootDir,
10076
+ directory.absolutePath,
10077
+ context.fs
10078
+ );
10079
+ if (!realPath.ok) return realPath.error;
10080
+ for (const lockfile of lockfiles) {
10081
+ if (managers.size > 0 && !managers.has(lockfile.manager) && await context.fs.exists(`${directory.absolutePath}/${lockfile.file}`))
10082
+ return createRepoSetupError({
10083
+ code: "LOCKFILE_CONFLICT",
10084
+ message: `Found ${lockfile.file}, which conflicts with this plan's package manager.`,
10085
+ details: { lockfile: lockfile.file, cwd, managers: [...managers] },
10086
+ suggestion: "Use the package manager recorded by the existing lockfile or remove the conflicting lockfile deliberately."
10087
+ });
10088
+ }
10045
10089
  }
10046
10090
  return void 0;
10047
10091
  }
@@ -10133,7 +10177,7 @@ var silentLogger = {
10133
10177
  };
10134
10178
  async function executeInstallation(operations, options) {
10135
10179
  const context = {
10136
- rootDir: path6.resolve(options.rootDir),
10180
+ rootDir: path7.resolve(options.rootDir),
10137
10181
  fs: options.fs,
10138
10182
  runProcess: options.runProcess,
10139
10183
  logger: options.logger ?? silentLogger,
@@ -10354,7 +10398,7 @@ import {
10354
10398
  unlink,
10355
10399
  writeFile
10356
10400
  } from "fs/promises";
10357
- import path7 from "path";
10401
+ import path8 from "path";
10358
10402
  import os from "os";
10359
10403
  var DEFAULT_COMMAND_TIMEOUT_MS = 5 * 6e4;
10360
10404
  var DEFAULT_LONG_RUNNING_COMMAND_TIMEOUT_MS = 30 * 6e4;
@@ -10363,9 +10407,9 @@ var DEFAULT_MINIMUM_FREE_DISK_BYTES = 512 * 1024 * 1024;
10363
10407
  function createDefaultExecutionLock() {
10364
10408
  return {
10365
10409
  async acquire(rootDir) {
10366
- const lockRoot = path7.join(os.tmpdir(), "reposetup-locks");
10410
+ const lockRoot = path8.join(os.tmpdir(), "reposetup-locks");
10367
10411
  const lockId = createHash("sha256").update(rootDir).digest("hex");
10368
- const lockPath = path7.join(lockRoot, lockId);
10412
+ const lockPath = path8.join(lockRoot, lockId);
10369
10413
  try {
10370
10414
  await mkdir(lockRoot, { recursive: true, mode: 448 });
10371
10415
  await mkdir(lockPath, { mode: 448 });
@@ -10388,12 +10432,12 @@ function createDefaultExecutionLock() {
10388
10432
  }
10389
10433
  function createDefaultExecutionJournal(options = {}) {
10390
10434
  let journalPath;
10391
- const directory = options.directory ?? path7.join(os.tmpdir(), "reposetup-journals");
10435
+ const directory = options.directory ?? path8.join(os.tmpdir(), "reposetup-journals");
10392
10436
  return {
10393
10437
  async start(rootDir) {
10394
10438
  const rootHash = createHash("sha256").update(rootDir).digest("hex");
10395
10439
  await mkdir(directory, { recursive: true, mode: 448 });
10396
- journalPath = path7.join(directory, `${rootHash}-${randomUUID()}.jsonl`);
10440
+ journalPath = path8.join(directory, `${rootHash}-${randomUUID()}.jsonl`);
10397
10441
  await writeFile(
10398
10442
  journalPath,
10399
10443
  `${JSON.stringify({ version: 1, rootHash, status: "started" })}
@@ -10464,9 +10508,9 @@ function createDefaultExecutorFileSystem() {
10464
10508
  await writeFile(filePath, content, "utf8");
10465
10509
  },
10466
10510
  async writeFileAtomic(filePath, content) {
10467
- const temporaryPath = path7.join(
10468
- path7.dirname(filePath),
10469
- `.${path7.basename(filePath)}.reposetup-${randomUUID()}.tmp`
10511
+ const temporaryPath = path8.join(
10512
+ path8.dirname(filePath),
10513
+ `.${path8.basename(filePath)}.reposetup-${randomUUID()}.tmp`
10470
10514
  );
10471
10515
  try {
10472
10516
  await writeFile(temporaryPath, content, { encoding: "utf8", flag: "wx" });
@@ -10484,6 +10528,18 @@ function createDefaultExecutorFileSystem() {
10484
10528
  }
10485
10529
  };
10486
10530
  }
10531
+ function childProcessEnvironment(environment) {
10532
+ const outerLaunchKeys = /* @__PURE__ */ new Set([
10533
+ "npm_config_package",
10534
+ "npm_config_call",
10535
+ "npm_config_user_agent",
10536
+ "npm_execpath",
10537
+ "npm_node_execpath"
10538
+ ]);
10539
+ return Object.fromEntries(
10540
+ Object.entries(environment).filter(([key]) => !outerLaunchKeys.has(key.toLowerCase()))
10541
+ );
10542
+ }
10487
10543
  function createDefaultProcessRunner() {
10488
10544
  return (request) => {
10489
10545
  if (request.signal?.aborted === true) {
@@ -10496,7 +10552,7 @@ function createDefaultProcessRunner() {
10496
10552
  return new Promise((resolve) => {
10497
10553
  const child = spawn(launch.command, [...launch.args], {
10498
10554
  cwd: request.cwd,
10499
- env: process.env,
10555
+ env: childProcessEnvironment(process.env),
10500
10556
  shell: false,
10501
10557
  stdio: ["ignore", "pipe", "pipe"],
10502
10558
  windowsHide: true,
@@ -10600,7 +10656,7 @@ function findWindowsExecutable(command, environment, exists) {
10600
10656
  ].filter(Boolean);
10601
10657
  for (const directory of directories) {
10602
10658
  for (const extension of extensions) {
10603
- const candidate = path7.win32.join(directory, `${command}${extension}`);
10659
+ const candidate = path8.win32.join(directory, `${command}${extension}`);
10604
10660
  if (exists(candidate)) {
10605
10661
  return candidate;
10606
10662
  }
@@ -10609,7 +10665,7 @@ function findWindowsExecutable(command, environment, exists) {
10609
10665
  return void 0;
10610
10666
  }
10611
10667
  function isNativeWindowsExecutable(filePath) {
10612
- const extension = path7.win32.extname(filePath).toLowerCase();
10668
+ const extension = path8.win32.extname(filePath).toLowerCase();
10613
10669
  return extension === ".exe" || extension === ".com";
10614
10670
  }
10615
10671
  function isSimpleExecutableName(value) {
@@ -10785,8 +10841,8 @@ function createDefaultIo() {
10785
10841
  }
10786
10842
  function createDefaultFs() {
10787
10843
  return {
10788
- async readFile(path12) {
10789
- return readFile3(path12, "utf8");
10844
+ async readFile(path13) {
10845
+ return readFile3(path13, "utf8");
10790
10846
  }
10791
10847
  };
10792
10848
  }
@@ -10798,7 +10854,7 @@ function writeLine(write, text) {
10798
10854
  // src/prompt-create.ts
10799
10855
  import { checkbox, confirm, input, select } from "@inquirer/prompts";
10800
10856
  var PACKAGE_MANAGERS_BY_RUNTIME = {
10801
- node: ["npm", "pnpm", "bun"],
10857
+ node: ["npm", "pnpm"],
10802
10858
  python: ["uv", "pip"]
10803
10859
  };
10804
10860
  var OPTIONAL_PROMPT_GROUPS = [
@@ -11210,7 +11266,7 @@ async function confirmCreate() {
11210
11266
  }
11211
11267
 
11212
11268
  // src/create.ts
11213
- import path9 from "path";
11269
+ import path10 from "path";
11214
11270
 
11215
11271
  // src/presets.ts
11216
11272
  var BUNDLED_PRESETS = [
@@ -11362,9 +11418,9 @@ function copySelection(selection) {
11362
11418
  }
11363
11419
 
11364
11420
  // src/load-config.ts
11365
- import path8 from "path";
11421
+ import path9 from "path";
11366
11422
  async function loadRepoSetupConfigFile(configPath, deps) {
11367
- const resolvedPath = path8.resolve(deps.cwd, configPath);
11423
+ const resolvedPath = path9.resolve(deps.cwd, configPath);
11368
11424
  let raw;
11369
11425
  try {
11370
11426
  raw = await deps.fs.readFile(resolvedPath);
@@ -11418,12 +11474,12 @@ function postCreateCommands(config2) {
11418
11474
  return config2.packageManager === "uv" ? ["uv run flask run", ...integrationIds.has("pytest") ? ["uv run pytest"] : []] : ["flask run", ...integrationIds.has("pytest") ? ["pytest"] : []];
11419
11475
  case "fastify":
11420
11476
  case "express":
11421
- return config2.framework.options?.typescript === true ? [
11477
+ return config2.framework.options?.typescript !== false ? [
11422
11478
  `${packageRun} dev`,
11423
11479
  `${packageRun} build`,
11424
11480
  `${packageRun} start`,
11425
11481
  ...integrationIds.has("vitest") ? [`${config2.packageManager} exec vitest run`] : []
11426
- ] : ["node app.js"];
11482
+ ] : [config2.framework.id === "fastify" ? "node server.js" : "node app.js"];
11427
11483
  default:
11428
11484
  return [];
11429
11485
  }
@@ -11516,7 +11572,7 @@ async function handleCreate(input2) {
11516
11572
  writeLine(input2.deps.io.writeOut, `Elapsed: ${elapsedSeconds}s.`);
11517
11573
  writeLine(
11518
11574
  input2.deps.io.writeOut,
11519
- `Project directory: ${path9.resolve(input2.deps.cwd, planned.config.project.path ?? ".")}`
11575
+ `Project directory: ${path10.resolve(input2.deps.cwd, planned.config.project.path ?? ".")}`
11520
11576
  );
11521
11577
  for (const command of postCreateCommands(planned.config)) {
11522
11578
  writeLine(input2.deps.io.writeOut, `Next: ${command}`);
@@ -11683,18 +11739,18 @@ function findRequirementCycles(definitions) {
11683
11739
  }
11684
11740
  return [...cycles.values()];
11685
11741
  }
11686
- function visit(node2, path12, edges, color, cycles) {
11742
+ function visit(node2, path13, edges, color, cycles) {
11687
11743
  color.set(node2, "gray");
11688
11744
  for (const next of edges.get(node2) ?? []) {
11689
11745
  const nextColor = color.get(next);
11690
11746
  if (nextColor === "gray") {
11691
- const start = path12.indexOf(next);
11692
- const cycle = canonicalizeCycle([...path12.slice(start), next]);
11747
+ const start = path13.indexOf(next);
11748
+ const cycle = canonicalizeCycle([...path13.slice(start), next]);
11693
11749
  cycles.set(cycle.join("\0"), cycle);
11694
11750
  continue;
11695
11751
  }
11696
11752
  if (nextColor === "white") {
11697
- visit(next, [...path12, next], edges, color, cycles);
11753
+ visit(next, [...path13, next], edges, color, cycles);
11698
11754
  }
11699
11755
  }
11700
11756
  color.set(node2, "black");
@@ -12023,8 +12079,8 @@ function hasSelectedIntegration(context, id) {
12023
12079
  return context.config.framework.id === id || context.config.integrations.some((integration) => integration.id === id);
12024
12080
  }
12025
12081
  var QUALIFIED_VERSIONS = {
12026
- createNextApp: "16.3.5",
12027
- eslintConfigNext: "16.3.5",
12082
+ createNextApp: "16.3.6",
12083
+ eslintConfigNext: "16.3.6",
12028
12084
  vite: "8.3.0",
12029
12085
  eslint: "9.39.5",
12030
12086
  eslintJs: "9.39.5",
@@ -12110,11 +12166,13 @@ var DIRECT_VERSIONS = {
12110
12166
  zod: [npmPin("zod", QUALIFIED_VERSIONS.zod)],
12111
12167
  vitest: [
12112
12168
  npmPin("vitest", QUALIFIED_VERSIONS.vitest),
12169
+ npmPin("vite", QUALIFIED_VERSIONS.vite),
12113
12170
  npmPin("@vitejs/plugin-react", QUALIFIED_VERSIONS.vitePluginReact),
12114
12171
  npmPin("jsdom", QUALIFIED_VERSIONS.jsdom),
12115
12172
  npmPin("@testing-library/react", QUALIFIED_VERSIONS.testingLibraryReact),
12116
12173
  npmPin("@testing-library/dom", QUALIFIED_VERSIONS.testingLibraryDom),
12117
- npmPin("vite-tsconfig-paths", QUALIFIED_VERSIONS.viteTsconfigPaths)
12174
+ npmPin("vite-tsconfig-paths", QUALIFIED_VERSIONS.viteTsconfigPaths),
12175
+ npmPin("@types/node", QUALIFIED_VERSIONS.typesNode)
12118
12176
  ],
12119
12177
  "testing-library": [
12120
12178
  npmPin("@testing-library/react", QUALIFIED_VERSIONS.testingLibraryReact),
@@ -12374,11 +12432,11 @@ var dockerComposeIntegration = defineIntegration({
12374
12432
  return { supported: true };
12375
12433
  },
12376
12434
  async detect(context) {
12377
- const path12 = await firstExistingPath2(context.files, COMPOSE_PATHS);
12378
- if (path12 === void 0) {
12435
+ const path13 = await firstExistingPath2(context.files, COMPOSE_PATHS);
12436
+ if (path13 === void 0) {
12379
12437
  return notDetected();
12380
12438
  }
12381
- return detectedResult("certain", [evidence("file", `Found ${path12}`, path12)]);
12439
+ return detectedResult("certain", [evidence("file", `Found ${path13}`, path13)]);
12382
12440
  },
12383
12441
  plan(context) {
12384
12442
  if (!hasSelectedIntegration(context, "postgresql")) {
@@ -12420,11 +12478,19 @@ var dockerIntegration = defineIntegration({
12420
12478
  return supportsNodeOrPython(context);
12421
12479
  },
12422
12480
  async detect(context) {
12423
- const dockerfile = await context.files.exists("Dockerfile");
12424
- if (!dockerfile) {
12481
+ const dockerfile = await firstExistingPath2(context.files, [
12482
+ "Dockerfile",
12483
+ "compose.yaml",
12484
+ "compose.yml",
12485
+ "docker-compose.yaml",
12486
+ "docker-compose.yml"
12487
+ ]);
12488
+ if (dockerfile === void 0) {
12425
12489
  return notDetected();
12426
12490
  }
12427
- return detectedResult("likely", [evidence("file", "Found Dockerfile", "Dockerfile")]);
12491
+ return detectedResult("likely", [
12492
+ evidence("file", `Found Docker configuration: ${dockerfile}`, dockerfile)
12493
+ ]);
12428
12494
  },
12429
12495
  plan() {
12430
12496
  return [
@@ -12837,6 +12903,7 @@ var expressOptionsSchema = strictObject({
12837
12903
  var EXPRESS_TSCONFIG = `{
12838
12904
  "compilerOptions": {
12839
12905
  "target": "esnext",
12906
+ "rootDir": ".",
12840
12907
  "module": "nodenext",
12841
12908
  "rewriteRelativeImportExtensions": true,
12842
12909
  "erasableSyntaxOnly": true,
@@ -12855,7 +12922,20 @@ app.get("/", (req: Request, res: Response) => {
12855
12922
  });
12856
12923
 
12857
12924
  if (process.env.REPOSETUP_NO_LISTEN !== "1") {
12858
- app.listen(Number(process.env.PORT ?? "3000"));
12925
+ const server = app.listen(
12926
+ Number(process.env.PORT ?? "3000"),
12927
+ (error?: Error) => {
12928
+ if (error !== undefined) {
12929
+ console.error(error);
12930
+ process.exitCode = 1;
12931
+ return;
12932
+ }
12933
+ const address = server.address();
12934
+ if (address !== null && typeof address !== "string") {
12935
+ console.log("http://localhost:" + address.port);
12936
+ }
12937
+ },
12938
+ );
12859
12939
  }
12860
12940
  `;
12861
12941
  var EXPRESS_ENDPOINT_TEST = `import type { Server } from "node:http";
@@ -12889,14 +12969,6 @@ it("returns the Hello World response", async () => {
12889
12969
  expect(await response.text()).toBe("Hello World!");
12890
12970
  });
12891
12971
  `;
12892
- var EXPRESS_README = `# Express app
12893
-
12894
- Run the JavaScript entry with:
12895
-
12896
- \`node app.js\`
12897
-
12898
- For the TypeScript entry, use \`npm run dev\` while developing, then run \`npm run build\` and \`npm start\`.
12899
- `;
12900
12972
  var EXPRESS_APP_JS = `import express from "express";
12901
12973
 
12902
12974
  const app = express();
@@ -12905,7 +12977,17 @@ app.get("/", (req, res) => {
12905
12977
  res.send("Hello World!");
12906
12978
  });
12907
12979
 
12908
- app.listen(3000);
12980
+ const server = app.listen(3000, (error) => {
12981
+ if (error !== undefined) {
12982
+ console.error(error);
12983
+ process.exitCode = 1;
12984
+ return;
12985
+ }
12986
+ const address = server.address();
12987
+ if (address !== null && typeof address !== "string") {
12988
+ console.log("http://localhost:" + address.port);
12989
+ }
12990
+ });
12909
12991
  `;
12910
12992
  var expressIntegration = defineIntegration({
12911
12993
  id: "express",
@@ -12956,7 +13038,8 @@ var expressIntegration = defineIntegration({
12956
13038
  ],
12957
13039
  {
12958
13040
  description: "Install TypeScript, tsx, and Express type packages",
12959
- dev: true
13041
+ dev: true,
13042
+ allowBuild: ["esbuild"]
12960
13043
  }
12961
13044
  ),
12962
13045
  {
@@ -12986,7 +13069,7 @@ var expressIntegration = defineIntegration({
12986
13069
  scripts: {
12987
13070
  dev: "tsx watch src/app.ts",
12988
13071
  build: "tsc --outDir dist",
12989
- start: "node dist/app.js"
13072
+ start: "node dist/src/app.js"
12990
13073
  }
12991
13074
  },
12992
13075
  behavior: "merge",
@@ -13014,7 +13097,14 @@ var expressIntegration = defineIntegration({
13014
13097
  operations.push({
13015
13098
  type: "create_file",
13016
13099
  path: "README.md",
13017
- content: EXPRESS_README,
13100
+ content: [
13101
+ "# Express app",
13102
+ "",
13103
+ "From this directory:",
13104
+ "",
13105
+ typescript ? "Develop with `" + (context.config.packageManager === "npm" ? "npm run" : "pnpm") + " dev`, then build with `" + (context.config.packageManager === "npm" ? "npm run" : "pnpm") + " build` and run `" + (context.config.packageManager === "npm" ? "npm run" : "pnpm") + " start`." : "Run the JavaScript entry with `node app.js`.",
13106
+ ""
13107
+ ].join("\n"),
13018
13108
  behavior: "fail_if_exists",
13019
13109
  description: "Add Express run instructions"
13020
13110
  });
@@ -13027,14 +13117,22 @@ var expressIntegration = defineIntegration({
13027
13117
  ]);
13028
13118
  }
13029
13119
  });
13030
- var FASTAPI_README = `# FastAPI app
13031
-
13032
- From this directory, start the development server with:
13033
-
13034
- \`uv run fastapi dev\`
13035
-
13036
- Run the generated endpoint test with \`uv run pytest\`.
13037
- `;
13120
+ function pythonAppReadme(context, title, command) {
13121
+ const prefix = context.config.packageManager === "uv" ? "uv run " : "";
13122
+ const lines = [
13123
+ `# ${title} app`,
13124
+ "",
13125
+ "From this directory, start the development server with:",
13126
+ "",
13127
+ `\`${prefix}${command}\``,
13128
+ ""
13129
+ ];
13130
+ if (context.config.packageManager === "pip")
13131
+ lines.push("Activate the Python environment used during installation first.", "");
13132
+ if (hasSelectedIntegration(context, "pytest"))
13133
+ lines.push(`Run the generated endpoint test with \`${prefix}pytest\`.`, "");
13134
+ return lines.join("\n");
13135
+ }
13038
13136
  var FASTAPI_MAIN = `from fastapi import FastAPI
13039
13137
 
13040
13138
  app = FastAPI()
@@ -13109,7 +13207,7 @@ var fastapiIntegration = defineIntegration({
13109
13207
  {
13110
13208
  type: "create_file",
13111
13209
  path: "README.md",
13112
- content: FASTAPI_README,
13210
+ content: pythonAppReadme(context, "FastAPI", "fastapi dev"),
13113
13211
  behavior: "fail_if_exists",
13114
13212
  description: "Add FastAPI run instructions"
13115
13213
  },
@@ -13134,6 +13232,7 @@ var fastifyOptionsSchema = strictObject({
13134
13232
  var FASTIFY_TSCONFIG = `{
13135
13233
  "compilerOptions": {
13136
13234
  "target": "esnext",
13235
+ "rootDir": ".",
13137
13236
  "module": "nodenext",
13138
13237
  "rewriteRelativeImportExtensions": true,
13139
13238
  "erasableSyntaxOnly": true,
@@ -13152,11 +13251,12 @@ app.get("/", function () {
13152
13251
  });
13153
13252
 
13154
13253
  if (process.env.REPOSETUP_NO_LISTEN !== "1") {
13155
- app.listen({ port: Number(process.env.PORT ?? "3000"), host: "127.0.0.1" }, (error) => {
13254
+ app.listen({ port: Number(process.env.PORT ?? "3000"), host: "127.0.0.1" }, (error, address) => {
13156
13255
  if (error) {
13157
- app.log.error(error);
13256
+ console.error(error);
13158
13257
  process.exit(1);
13159
13258
  }
13259
+ console.log(address);
13160
13260
  });
13161
13261
  }
13162
13262
  `;
@@ -13271,7 +13371,7 @@ var fastifyIntegration = defineIntegration({
13271
13371
  scripts: {
13272
13372
  dev: "tsx watch src/server.ts",
13273
13373
  build: "tsc --outDir dist",
13274
- start: "node dist/server.js"
13374
+ start: "node dist/src/server.js"
13275
13375
  }
13276
13376
  },
13277
13377
  behavior: "merge",
@@ -13309,14 +13409,6 @@ var fastifyIntegration = defineIntegration({
13309
13409
  ]);
13310
13410
  }
13311
13411
  });
13312
- var FLASK_README = `# Flask app
13313
-
13314
- From this directory, start the development server with:
13315
-
13316
- \`uv run flask run\`
13317
-
13318
- Run the generated endpoint test with \`uv run pytest\`.
13319
- `;
13320
13412
  var FLASK_APP = `from flask import Flask
13321
13413
 
13322
13414
  app = Flask(__name__)
@@ -13388,7 +13480,7 @@ var flaskIntegration = defineIntegration({
13388
13480
  {
13389
13481
  type: "create_file",
13390
13482
  path: "README.md",
13391
- content: FLASK_README,
13483
+ content: pythonAppReadme(context, "Flask", "flask run"),
13392
13484
  behavior: "fail_if_exists",
13393
13485
  description: "Add Flask run instructions"
13394
13486
  },
@@ -13817,7 +13909,7 @@ var nextjsIntegration = defineIntegration({
13817
13909
  context.config.packageManager === "pnpm" ? "--use-pnpm" : "--use-npm",
13818
13910
  // Official create-next-app flag: skip the generator's install so later
13819
13911
  // install_package ops (or a deferred project install) own node_modules.
13820
- // Verified for create-next-app@16.3.5 via --help and
13912
+ // Verified for create-next-app@16.3.6 via --help and
13821
13913
  // https://nextjs.org/docs/app/api-reference/cli/create-next-app
13822
13914
  "--skip-install",
13823
13915
  "--yes"
@@ -13848,7 +13940,7 @@ var nextjsIntegration = defineIntegration({
13848
13940
  };
13849
13941
  const usesPrisma = context.config.integrations.some(({ id }) => id === "prisma");
13850
13942
  const pnpmBuildPolicy = context.config.packageManager === "pnpm" ? {
13851
- // create-next-app 16.3.5 writes this placeholder policy when its
13943
+ // create-next-app 16.3.6 writes this placeholder policy when its
13852
13944
  // install is skipped. Replace it before RepoSetup's consolidated
13853
13945
  // install so the generator's known native dependencies can build.
13854
13946
  type: "modify_text",
@@ -13870,7 +13962,7 @@ var nextjsIntegration = defineIntegration({
13870
13962
  description: "Enable create-next-app's declared pnpm dependency builds"
13871
13963
  } : void 0;
13872
13964
  const standaloneTypecheckFix = typescript ? {
13873
- // create-next-app 16.3.5's default LayoutProps global is emitted
13965
+ // create-next-app 16.3.6's default LayoutProps global is emitted
13874
13966
  // by Next's type generation. Use an explicit type so `tsc --noEmit`
13875
13967
  // works before a development server or build has run.
13876
13968
  type: "modify_text",
@@ -14458,7 +14550,8 @@ function sqlitePlan(context) {
14458
14550
  execLocalBin(
14459
14551
  context,
14460
14552
  "prisma",
14461
- ["init", "--datasource-provider", "sqlite", "--output", "../generated/prisma"],
14553
+ // Verified Prisma 7.10.0 CLI flag: avoid fetching unrelated agent instructions.
14554
+ ["init", "--datasource-provider", "sqlite", "--output", "../generated/prisma", "--no-skills"],
14462
14555
  { description: "Initialize Prisma with the SQLite provider" }
14463
14556
  ),
14464
14557
  {
@@ -14509,7 +14602,14 @@ function postgresPlan(context) {
14509
14602
  execLocalBin(
14510
14603
  context,
14511
14604
  "prisma",
14512
- ["init", "--datasource-provider", "postgresql", "--output", "../generated/prisma"],
14605
+ [
14606
+ "init",
14607
+ "--datasource-provider",
14608
+ "postgresql",
14609
+ "--output",
14610
+ "../generated/prisma",
14611
+ "--no-skills"
14612
+ ],
14513
14613
  { description: "Initialize Prisma with the PostgreSQL provider" }
14514
14614
  ),
14515
14615
  {
@@ -14820,6 +14920,9 @@ var reactViteIntegration = defineIntegration({
14820
14920
  const hasVite = pkg !== void 0 && hasPackageDependency(pkg, "vite");
14821
14921
  const hasReact = pkg !== void 0 && hasPackageDependency(pkg, "react");
14822
14922
  const config2 = await firstExistingPath2(context.files, VITE_CONFIG_PATHS);
14923
+ if (pkg !== void 0 && hasPackageDependency(pkg, "next") && config2 === void 0) {
14924
+ return notDetected();
14925
+ }
14823
14926
  if (!hasReact || !hasVite && config2 === void 0) {
14824
14927
  return notDetected();
14825
14928
  }
@@ -14841,12 +14944,15 @@ var reactViteIntegration = defineIntegration({
14841
14944
  const vite = `vite@${QUALIFIED_VERSIONS.vite}`;
14842
14945
  return [
14843
14946
  requireNodeRange(NODE_ENGINE_RANGES.vite, `Vite ${QUALIFIED_VERSIONS.vite}`),
14844
- pnpmOrNpmCreate(
14845
- context,
14846
- { pnpmName: vite, npmName: vite },
14847
- ["--template", template, "--no-interactive"],
14848
- { description: "Scaffold React with create-vite", longRunning: true }
14849
- ),
14947
+ {
14948
+ ...pnpmOrNpmCreate(
14949
+ context,
14950
+ { pnpmName: vite, npmName: vite },
14951
+ ["--template", template, "--no-interactive"],
14952
+ { description: "Scaffold React with create-vite", longRunning: true }
14953
+ ),
14954
+ skipsDependencyInstall: true
14955
+ },
14850
14956
  ...context.config.packageManager === "pnpm" ? [pnpmViteBuildApproval()] : []
14851
14957
  ];
14852
14958
  },
@@ -14915,7 +15021,7 @@ var shadcnIntegration = defineIntegration({
14915
15021
  status: "experimental",
14916
15022
  documentationUrl: "https://ui.shadcn.com/docs/cli",
14917
15023
  keywords: ["ui", "components", "radix"],
14918
- verification: { verifiedAt: "2026-09-22" },
15024
+ verification: { verifiedAt: "2026-10-08" },
14919
15025
  requirements: [
14920
15026
  {
14921
15027
  kind: "requires",
@@ -14944,17 +15050,78 @@ var shadcnIntegration = defineIntegration({
14944
15050
  },
14945
15051
  plan(context) {
14946
15052
  const template = context.config.framework.id === "react-vite" ? "vite" : "next";
15053
+ const viteAliases = template === "vite" ? viteAliasOperations(context) : [];
14947
15054
  return [
15055
+ ...viteAliases,
14948
15056
  requireNodeRange(NODE_ENGINE_RANGES.shadcn, `shadcn ${QUALIFIED_VERSIONS.shadcn}`),
14949
- dlx(context, npmPin("shadcn", QUALIFIED_VERSIONS.shadcn), ["init", "--yes", "-t", template], {
14950
- description: "Initialize shadcn/ui in the existing app"
14951
- })
15057
+ dlx(
15058
+ context,
15059
+ npmPin("shadcn", QUALIFIED_VERSIONS.shadcn),
15060
+ ["init", "--yes", "--defaults", "-t", template],
15061
+ {
15062
+ description: "Initialize shadcn/ui in the existing app"
15063
+ }
15064
+ ),
15065
+ {
15066
+ type: "verify",
15067
+ cwd: context.projectRoot,
15068
+ command: "node",
15069
+ args: [
15070
+ "-e",
15071
+ "const fs = require('node:fs'); if (!fs.existsSync('components.json')) { console.error('shadcn did not initialize components.json. Review the initialization output before retrying.'); process.exit(1); }"
15072
+ ],
15073
+ description: "Require shadcn initialization to produce components.json"
15074
+ }
14952
15075
  ];
14953
15076
  },
14954
15077
  async verify(context) {
14955
15078
  return mergeVerify([await missingAnyFile(context, ["components.json"], "components.json")]);
14956
15079
  }
14957
15080
  });
15081
+ function viteAliasOperations(context) {
15082
+ const compilerOptions = { baseUrl: ".", paths: { "@/*": ["./src/*"] } };
15083
+ return [
15084
+ ...usesTypescript(context) ? [
15085
+ {
15086
+ type: "modify_json",
15087
+ path: "tsconfig.json",
15088
+ merge: { compilerOptions },
15089
+ behavior: "merge",
15090
+ description: "Configure the root TypeScript shadcn alias"
15091
+ },
15092
+ {
15093
+ // create-vite's app tsconfig contains comments; preserve JSONC.
15094
+ type: "modify_text",
15095
+ path: "tsconfig.app.json",
15096
+ oldText: '"compilerOptions": {',
15097
+ newText: '"compilerOptions": {\n "baseUrl": ".",\n "paths": { "@/*": ["./src/*"] },',
15098
+ description: "Configure the app TypeScript shadcn alias"
15099
+ }
15100
+ ] : [
15101
+ {
15102
+ type: "create_file",
15103
+ path: "jsconfig.json",
15104
+ content: JSON.stringify({ compilerOptions }, null, 2) + "\n",
15105
+ behavior: "fail_if_exists",
15106
+ description: "Configure the JavaScript shadcn alias"
15107
+ }
15108
+ ],
15109
+ {
15110
+ type: "modify_text",
15111
+ path: usesTypescript(context) ? "vite.config.ts" : "vite.config.js",
15112
+ oldText: "import { defineConfig } from 'vite'",
15113
+ newText: "import { defineConfig } from 'vite'\nimport { fileURLToPath } from 'node:url'",
15114
+ description: "Resolve the Vite source alias on every supported platform"
15115
+ },
15116
+ {
15117
+ type: "modify_text",
15118
+ path: usesTypescript(context) ? "vite.config.ts" : "vite.config.js",
15119
+ oldText: "export default defineConfig({",
15120
+ newText: "export default defineConfig({\n resolve: {\n alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) },\n },",
15121
+ description: "Enable the shadcn Vite source alias"
15122
+ }
15123
+ ];
15124
+ }
14958
15125
  var sqlalchemyIntegration = defineIntegration({
14959
15126
  id: "sqlalchemy",
14960
15127
  name: "SQLAlchemy",
@@ -15067,7 +15234,7 @@ var sqliteIntegration = defineIntegration({
15067
15234
  }
15068
15235
  });
15069
15236
  var POSTCSS_CONFIG_PATHS = ["postcss.config.mjs", "postcss.config.js"];
15070
- var GLOBAL_CSS_PATHS = ["app/globals.css", "src/app/globals.css"];
15237
+ var GLOBAL_CSS_PATHS = ["app/globals.css", "src/app/globals.css", "src/index.css"];
15071
15238
  var POSTCSS_CONFIG = `const config = {
15072
15239
  plugins: {
15073
15240
  "@tailwindcss/postcss": {},
@@ -15110,7 +15277,7 @@ var tailwindIntegration = defineIntegration({
15110
15277
  const postcss = await firstExistingPath2(context.files, POSTCSS_CONFIG_PATHS);
15111
15278
  const cssPath = await firstExistingPath2(context.files, GLOBAL_CSS_PATHS);
15112
15279
  const css = cssPath === void 0 ? void 0 : await context.files.readText(cssPath);
15113
- const hasImport = css !== void 0 && css.includes('@import "tailwindcss"');
15280
+ const hasImport = css !== void 0 && /@import\s+["']tailwindcss["']/.test(css);
15114
15281
  if (!hasTailwind && postcss === void 0 && !hasImport && !hasVitePlugin) {
15115
15282
  return notDetected();
15116
15283
  }
@@ -15144,9 +15311,25 @@ var tailwindIntegration = defineIntegration({
15144
15311
  }
15145
15312
  ),
15146
15313
  {
15147
- type: "show_message",
15148
- message: 'Add the @tailwindcss/vite plugin to vite.config and add @import "tailwindcss"; to your CSS. See https://tailwindcss.com/docs/installation/using-vite',
15149
- description: "Point at the official Tailwind Vite plugin steps"
15314
+ type: "modify_text",
15315
+ path: usesTypescript(context) ? "vite.config.ts" : "vite.config.js",
15316
+ oldText: "import react from '@vitejs/plugin-react'",
15317
+ newText: "import react from '@vitejs/plugin-react'\nimport tailwindcss from '@tailwindcss/vite'",
15318
+ description: "Import the official Tailwind Vite plugin"
15319
+ },
15320
+ {
15321
+ type: "modify_text",
15322
+ path: usesTypescript(context) ? "vite.config.ts" : "vite.config.js",
15323
+ oldText: "plugins: [react()],",
15324
+ newText: "plugins: [react(), tailwindcss()],",
15325
+ description: "Enable the Tailwind Vite plugin"
15326
+ },
15327
+ {
15328
+ type: "modify_text",
15329
+ path: "src/index.css",
15330
+ oldText: ":root {\n font-family:",
15331
+ newText: "@import 'tailwindcss';\n\n:root {\n font-family:",
15332
+ description: "Import Tailwind without removing existing Vite styles"
15150
15333
  }
15151
15334
  ];
15152
15335
  }
@@ -15189,11 +15372,14 @@ var tailwindIntegration = defineIntegration({
15189
15372
  async verify(context) {
15190
15373
  const cssPath = await firstExistingPath2(context.files, GLOBAL_CSS_PATHS);
15191
15374
  const css = cssPath === void 0 ? void 0 : await context.files.readText(cssPath);
15192
- const missingImport = cssPath !== void 0 && (css === void 0 || !css.includes('@import "tailwindcss"')) ? failVerify(
15375
+ const hasVitePlugin = context.packageJson !== void 0 && hasPackageDependency(context.packageJson, "@tailwindcss/vite");
15376
+ const missingImport = cssPath === void 0 && hasVitePlugin ? failVerify(
15377
+ "The Vite project is missing a known Tailwind CSS entry file.",
15378
+ 'Add @import "tailwindcss"; to src/index.css. Doctor does not edit CSS.'
15379
+ ) : cssPath !== void 0 && (css === void 0 || !/@import\s+["']tailwindcss["']/.test(css)) ? failVerify(
15193
15380
  `${cssPath} does not import Tailwind.`,
15194
15381
  `Add @import "tailwindcss"; to ${cssPath}. Doctor does not edit CSS.`
15195
15382
  ) : void 0;
15196
- const hasVitePlugin = context.packageJson !== void 0 && hasPackageDependency(context.packageJson, "@tailwindcss/vite");
15197
15383
  const missingPostcss = hasVitePlugin ? void 0 : await missingAnyFile(
15198
15384
  context,
15199
15385
  POSTCSS_CONFIG_PATHS,
@@ -15490,21 +15676,27 @@ var uvIntegration = defineIntegration({
15490
15676
  ];
15491
15677
  }
15492
15678
  });
15493
- var VITEST_CONFIG_TS = `import { defineConfig } from 'vitest/config'
15679
+ var VITEST_CONFIG_TS = `import { realpathSync } from 'node:fs'
15680
+ import { fileURLToPath } from 'node:url'
15681
+ import { defineConfig } from 'vitest/config'
15494
15682
  import react from '@vitejs/plugin-react'
15495
15683
  import tsconfigPaths from 'vite-tsconfig-paths'
15496
15684
 
15497
15685
  export default defineConfig({
15686
+ root: realpathSync.native(fileURLToPath(new URL('.', import.meta.url))),
15498
15687
  plugins: [tsconfigPaths(), react()],
15499
15688
  test: {
15500
15689
  environment: 'jsdom',
15501
15690
  },
15502
15691
  })
15503
15692
  `;
15504
- var VITEST_CONFIG_JS = `import { defineConfig } from 'vitest/config'
15693
+ var VITEST_CONFIG_JS = `import { realpathSync } from 'node:fs'
15694
+ import { fileURLToPath } from 'node:url'
15695
+ import { defineConfig } from 'vitest/config'
15505
15696
  import react from '@vitejs/plugin-react'
15506
15697
 
15507
15698
  export default defineConfig({
15699
+ root: realpathSync.native(fileURLToPath(new URL('.', import.meta.url))),
15508
15700
  plugins: [react()],
15509
15701
  test: {
15510
15702
  environment: 'jsdom',
@@ -15653,13 +15845,17 @@ export default defineConfig({})
15653
15845
  const typescript = context.config.framework.options?.typescript !== false;
15654
15846
  const packages = typescript ? [
15655
15847
  npmPin("vitest", QUALIFIED_VERSIONS.vitest),
15848
+ npmPin("vite", QUALIFIED_VERSIONS.vite),
15656
15849
  npmPin("@vitejs/plugin-react", QUALIFIED_VERSIONS.vitePluginReact),
15657
15850
  npmPin("jsdom", QUALIFIED_VERSIONS.jsdom),
15658
15851
  npmPin("@testing-library/react", QUALIFIED_VERSIONS.testingLibraryReact),
15659
15852
  npmPin("@testing-library/dom", QUALIFIED_VERSIONS.testingLibraryDom),
15660
- npmPin("vite-tsconfig-paths", QUALIFIED_VERSIONS.viteTsconfigPaths)
15853
+ npmPin("vite-tsconfig-paths", QUALIFIED_VERSIONS.viteTsconfigPaths),
15854
+ // create-next-app defaults to Node 20 types, outside Vitest 5's peer range.
15855
+ npmPin("@types/node", QUALIFIED_VERSIONS.typesNode)
15661
15856
  ] : [
15662
15857
  npmPin("vitest", QUALIFIED_VERSIONS.vitest),
15858
+ npmPin("vite", QUALIFIED_VERSIONS.vite),
15663
15859
  npmPin("@vitejs/plugin-react", QUALIFIED_VERSIONS.vitePluginReact),
15664
15860
  npmPin("jsdom", QUALIFIED_VERSIONS.jsdom),
15665
15861
  npmPin("@testing-library/react", QUALIFIED_VERSIONS.testingLibraryReact),
@@ -15671,7 +15867,8 @@ export default defineConfig({})
15671
15867
  addPackages(context, packages, {
15672
15868
  description: "Install Vitest and the official Next.js test packages",
15673
15869
  dev: true,
15674
- allowBuild: ["esbuild"]
15870
+ allowBuild: ["esbuild"],
15871
+ exact: true
15675
15872
  }),
15676
15873
  {
15677
15874
  type: "create_file",
@@ -15879,7 +16076,7 @@ async function handleDoctor(input2) {
15879
16076
 
15880
16077
  // src/export.ts
15881
16078
  import { access as access4, writeFile as writeFile2 } from "fs/promises";
15882
- import path10 from "path";
16079
+ import path11 from "path";
15883
16080
  var EXPORT_FILE_NAME = "reposetup.json";
15884
16081
  async function handleExport(input2) {
15885
16082
  if (input2.packageManager !== void 0 && !isKnownPackageManager(input2.packageManager)) {
@@ -15912,7 +16109,7 @@ async function handleExport(input2) {
15912
16109
  }
15913
16110
  return EXIT_CODES.SUCCESS;
15914
16111
  }
15915
- const outputPath = path10.join(exported.projectRoot, EXPORT_FILE_NAME);
16112
+ const outputPath = path11.join(exported.projectRoot, EXPORT_FILE_NAME);
15916
16113
  if (!input2.yes && await fileExists(outputPath)) {
15917
16114
  writeLine(
15918
16115
  input2.deps.io.writeErr,
@@ -16213,15 +16410,15 @@ function renderStack(stack, verbose) {
16213
16410
  for (const item of allItems(stack)) {
16214
16411
  lines.push("", `${item.name} evidence:`);
16215
16412
  for (const entry of item.evidence) {
16216
- const path12 = entry.path === void 0 ? "" : ` (${entry.path})`;
16217
- lines.push(` - ${entry.detail}${path12}`);
16413
+ const path13 = entry.path === void 0 ? "" : ` (${entry.path})`;
16414
+ lines.push(` - ${entry.detail}${path13}`);
16218
16415
  }
16219
16416
  }
16220
16417
  if (stack.language !== void 0) {
16221
16418
  lines.push("", `Language evidence:`);
16222
16419
  for (const entry of stack.language.evidence) {
16223
- const path12 = entry.path === void 0 ? "" : ` (${entry.path})`;
16224
- lines.push(` - ${entry.detail}${path12}`);
16420
+ const path13 = entry.path === void 0 ? "" : ` (${entry.path})`;
16421
+ lines.push(` - ${entry.detail}${path13}`);
16225
16422
  }
16226
16423
  }
16227
16424
  }
@@ -16305,15 +16502,15 @@ async function handleStack(input2) {
16305
16502
 
16306
16503
  // src/version.ts
16307
16504
  import { existsSync as existsSync2, readFileSync } from "fs";
16308
- import path11 from "path";
16505
+ import path12 from "path";
16309
16506
  import { fileURLToPath } from "url";
16310
16507
  var PACKAGE_NAME = "rsetup";
16311
16508
  function cliVersion() {
16312
- const here = path11.dirname(fileURLToPath(import.meta.url));
16509
+ const here = path12.dirname(fileURLToPath(import.meta.url));
16313
16510
  const candidates = [
16314
- path11.join(here, "..", "package.json"),
16315
- path11.join(here, "package.json"),
16316
- path11.join(here, "..", "..", "package.json")
16511
+ path12.join(here, "..", "package.json"),
16512
+ path12.join(here, "package.json"),
16513
+ path12.join(here, "..", "..", "package.json")
16317
16514
  ];
16318
16515
  for (const file of candidates) {
16319
16516
  if (!existsSync2(file)) {
@@ -16483,4 +16680,4 @@ export {
16483
16680
  EXIT_CODES,
16484
16681
  runCli
16485
16682
  };
16486
- //# sourceMappingURL=chunk-6SOBVOJQ.js.map
16683
+ //# sourceMappingURL=chunk-D3YJGGEE.js.map
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  EXIT_CODES,
3
3
  runCli
4
- } from "./chunk-6SOBVOJQ.js";
4
+ } from "./chunk-D3YJGGEE.js";
5
5
 
6
6
  // src/index.ts
7
7
  var packageName = "rsetup";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rsetup",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -47,9 +47,9 @@
47
47
  "LICENSE"
48
48
  ],
49
49
  "devDependencies": {
50
- "@reposetup/core": "0.1.1",
51
- "@reposetup/integrations": "0.1.1",
52
- "@reposetup/registry": "0.1.1",
50
+ "@reposetup/core": "0.2.1",
51
+ "@reposetup/integrations": "0.2.1",
52
+ "@reposetup/registry": "0.2.1",
53
53
  "@types/node": "^22.20.4",
54
54
  "tsup": "^8.5.1",
55
55
  "typescript": "^5.9.3",