create-gtkx 2.0.0-beta.10 → 2.0.0-beta.11

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 +1 @@
1
- {"version":3,"file":"package-managers.js","sourceRoot":"","sources":["../src/package-managers.ts"],"names":[],"mappings":"AAEA,MAAM,gBAAgB,GAAG;IACrB;QACI,KAAK,EAAE,MAAM;QACb,KAAK,EAAE,MAAM;QACb,UAAU,EAAE,UAAU;QACtB,UAAU,EAAE,UAAU;QACtB,cAAc,EAAE,cAAc;QAC9B,aAAa,EAAE,IAAI;KACtB;IACD;QACI,KAAK,EAAE,KAAK;QACZ,KAAK,EAAE,KAAK;QACZ,UAAU,EAAE,aAAa;QACzB,UAAU,EAAE,aAAa;QACzB,cAAc,EAAE,aAAa;QAC7B,aAAa,EAAE,KAAK;KACvB;IACD;QACI,KAAK,EAAE,MAAM;QACb,KAAK,EAAE,MAAM;QACb,UAAU,EAAE,UAAU;QACtB,UAAU,EAAE,UAAU;QACtB,cAAc,EAAE,cAAc;QAC9B,aAAa,EAAE,KAAK;KACvB;CACK,CAAC;AAEX,MAAM,sBAAsB,GAAqB,gBAAgB,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAClG,MAAM,gCAAgC,GAAG,oBAAoB,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;AAElG,MAAM,qBAAqB,GAAG,CAAC,IAAY,EAA0B,EAAE,CAClE,sBAAmC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AAExD,OAAO,EACH,gBAAgB,EAChB,gCAAgC,EAChC,sBAAsB,EACtB,qBAAqB,GAExB,CAAC","sourcesContent":["type PackageManager = (typeof PACKAGE_MANAGERS)[number][\"value\"];\n\nconst PACKAGE_MANAGERS = [\n {\n value: \"pnpm\",\n label: \"pnpm\",\n devCommand: \"pnpm dev\",\n addCommand: \"pnpm add\",\n installCommand: \"pnpm install\",\n isRecommended: true,\n },\n {\n value: \"npm\",\n label: \"npm\",\n devCommand: \"npm run dev\",\n addCommand: \"npm install\",\n installCommand: \"npm install\",\n isRecommended: false,\n },\n {\n value: \"yarn\",\n label: \"yarn\",\n devCommand: \"yarn dev\",\n addCommand: \"yarn add\",\n installCommand: \"yarn install\",\n isRecommended: false,\n },\n] as const;\n\nconst PACKAGE_MANAGER_VALUES: PackageManager[] = PACKAGE_MANAGERS.map((manager) => manager.value);\nconst PACKAGE_MANAGER_FLAG_DESCRIPTION = `Package manager (${PACKAGE_MANAGER_VALUES.join(\", \")})`;\n\nconst isKnownPackageManager = (name: string): name is PackageManager =>\n (PACKAGE_MANAGER_VALUES as string[]).includes(name);\n\nexport {\n PACKAGE_MANAGERS,\n PACKAGE_MANAGER_FLAG_DESCRIPTION,\n PACKAGE_MANAGER_VALUES,\n isKnownPackageManager,\n type PackageManager,\n};\n"]}
1
+ {"version":3,"file":"package-managers.js","sourceRoot":"","sources":["../src/package-managers.ts"],"names":[],"mappings":"AAEA,MAAM,gBAAgB,GAAG;IACrB;QACI,KAAK,EAAE,MAAM;QACb,KAAK,EAAE,MAAM;QACb,UAAU,EAAE,UAAU;QACtB,cAAc,EAAE,cAAc;QAC9B,aAAa,EAAE,IAAI;KACtB;IACD;QACI,KAAK,EAAE,KAAK;QACZ,KAAK,EAAE,KAAK;QACZ,UAAU,EAAE,aAAa;QACzB,cAAc,EAAE,aAAa;QAC7B,aAAa,EAAE,KAAK;KACvB;IACD;QACI,KAAK,EAAE,MAAM;QACb,KAAK,EAAE,MAAM;QACb,UAAU,EAAE,UAAU;QACtB,cAAc,EAAE,cAAc;QAC9B,aAAa,EAAE,KAAK;KACvB;CACK,CAAC;AAEX,MAAM,sBAAsB,GAAqB,gBAAgB,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAClG,MAAM,gCAAgC,GAAG,oBAAoB,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;AAElG,MAAM,qBAAqB,GAAG,CAAC,IAAY,EAA0B,EAAE,CAClE,sBAAmC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AAExD,OAAO,EACH,gBAAgB,EAChB,gCAAgC,EAChC,sBAAsB,EACtB,qBAAqB,GAExB,CAAC","sourcesContent":["type PackageManager = (typeof PACKAGE_MANAGERS)[number][\"value\"];\n\nconst PACKAGE_MANAGERS = [\n {\n value: \"pnpm\",\n label: \"pnpm\",\n devCommand: \"pnpm dev\",\n installCommand: \"pnpm install\",\n isRecommended: true,\n },\n {\n value: \"npm\",\n label: \"npm\",\n devCommand: \"npm run dev\",\n installCommand: \"npm install\",\n isRecommended: false,\n },\n {\n value: \"yarn\",\n label: \"yarn\",\n devCommand: \"yarn dev\",\n installCommand: \"yarn install\",\n isRecommended: false,\n },\n] as const;\n\nconst PACKAGE_MANAGER_VALUES: PackageManager[] = PACKAGE_MANAGERS.map((manager) => manager.value);\nconst PACKAGE_MANAGER_FLAG_DESCRIPTION = `Package manager (${PACKAGE_MANAGER_VALUES.join(\", \")})`;\n\nconst isKnownPackageManager = (name: string): name is PackageManager =>\n (PACKAGE_MANAGER_VALUES as string[]).includes(name);\n\nexport {\n PACKAGE_MANAGERS,\n PACKAGE_MANAGER_FLAG_DESCRIPTION,\n PACKAGE_MANAGER_VALUES,\n isKnownPackageManager,\n type PackageManager,\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"scaffolder.d.ts","sourceRoot":"","sources":["../src/scaffolder.ts"],"names":[],"mappings":"AAsBA,KAAK,aAAa,GAAG;IACjB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACnC,oBAAoB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC3C,aAAa,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACpC,eAAe,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACtC,yBAAyB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACnD,CAAC;AA41BF,QAAA,MAAM,QAAQ,GAAU,UAAS,aAAkB,KAAG,OAAO,CAAC,IAAI,CAmBjE,CAAC;AAEF,OAAO,EAAE,QAAQ,EAAE,CAAC"}
1
+ {"version":3,"file":"scaffolder.d.ts","sourceRoot":"","sources":["../src/scaffolder.ts"],"names":[],"mappings":"AAqBA,KAAK,aAAa,GAAG;IACjB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACnC,oBAAoB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC3C,aAAa,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACpC,eAAe,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACtC,yBAAyB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACnD,CAAC;AA8qBF,QAAA,MAAM,QAAQ,GAAU,UAAS,aAAkB,KAAG,OAAO,CAAC,IAAI,CAkBjE,CAAC;AAEF,OAAO,EAAE,QAAQ,EAAE,CAAC"}
@@ -3,13 +3,12 @@ import { APPLICATION_ID_MAX_LENGTH, isValidApplicationId } from "@gtkx/config/in
3
3
  import { errorMessage, tryResolveExecutable, upperFirst } from "@gtkx/utils";
4
4
  import { execFileSync } from "node:child_process";
5
5
  import { lstatSync, mkdirSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
6
- import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
6
+ import { basename, dirname, join, relative, resolve, sep } from "node:path";
7
7
  import { addDependency, detectPackageManager as nypmDetectPackageManager } from "nypm";
8
8
  import { x } from "tinyexec";
9
9
  import packageManifest from "../package.json" with { type: "json" };
10
10
  import { writeBuildAllowance } from "./build-allowance.js";
11
11
  import { OperationCanceledError, ScaffoldAbortedError } from "./errors.js";
12
- import { getInstallHint } from "./install-hints.js";
13
12
  import { updateManifest } from "./manifest.js";
14
13
  import { isKnownPackageManager, PACKAGE_MANAGER_VALUES, PACKAGE_MANAGERS, } from "./package-managers.js";
15
14
  import { isValidProjectName } from "./project-name.js";
@@ -17,15 +16,11 @@ import { listTemplates, renderFile } from "./templates.js";
17
16
  const selfVersion = packageManifest.version;
18
17
  const ICON_TEMPLATE_NAME = "icon.svg";
19
18
  const TYPESCRIPT_VERSION = "^6.0.3";
20
- const GTKX_ENV_MODULE_HEADER = `/**
21
- * Generated by \`gtkx codegen\`, \`gtkx dev\`, and \`gtkx build\`; do not edit.
22
- */`;
23
19
  const DEPENDENCIES = ["@gtkx/cairo", "@gtkx/css", "@gtkx/runtime", "@gtkx/react", "react"];
24
20
  const DEV_DEPENDENCIES = ["@gtkx/cli", "@gtkx/config", "@gtkx/mcp", "vite"];
25
21
  const TYPESCRIPT_DEV_DEPENDENCIES = ["@types/node", "@types/react", "typescript"];
26
22
  const TESTING_DEV_DEPENDENCIES = ["@gtkx/testing", "vitest"];
27
23
  const DEV_COMMAND = Object.fromEntries(PACKAGE_MANAGERS.map((manager) => [manager.value, manager.devCommand]));
28
- const ADD_COMMAND = Object.fromEntries(PACKAGE_MANAGERS.map((manager) => [manager.value, manager.addCommand]));
29
24
  const INSTALL_COMMAND = Object.fromEntries(PACKAGE_MANAGERS.map((manager) => [manager.value, manager.installCommand]));
30
25
  const INHERITED_SCRIPT_POLICY_VARS = ["npm_config_allow_scripts", "npm_config_allow-scripts"];
31
26
  const TESTING_TEMPLATES = new Set(["vitest.config.ts", "tests/app.test.tsx"]);
@@ -35,7 +30,6 @@ const HEADLESS_COMPOSITOR_NOTE = `
35
30
  To run tests, you need a headless Wayland compositor installed:
36
31
  Fedora: sudo dnf install sway
37
32
  Ubuntu: sudo apt install sway`;
38
- const RECOVERY_HEADING = "Finish the setup by adding the dependencies again, which is safe to repeat:";
39
33
  const APPLICATION_ID_FORMAT_ERROR = "Application ID must be reverse domain notation (e.g., com.example.myapp)";
40
34
  const APPLICATION_ID_PREFIX = "com.";
41
35
  const APPLICATION_ID_SUFFIX = ".app";
@@ -60,14 +54,6 @@ const applicationIdSegment = (name) => {
60
54
  return segment.slice(0, APPLICATION_ID_SEGMENT_LIMIT);
61
55
  };
62
56
  const suggestApplicationId = (name) => `${APPLICATION_ID_PREFIX}${applicationIdSegment(name)}${APPLICATION_ID_SUFFIX}`;
63
- const stripTrailingSlashes = (value) => {
64
- let end = value.length;
65
- while (end > 0 && value[end - 1] === "/") {
66
- end--;
67
- }
68
- return value.slice(0, end);
69
- };
70
- const formatTargetDir = (target) => stripTrailingSlashes(target.trim().replaceAll(/[<>:"\\|?*]/g, ""));
71
57
  const deriveProjectName = (target) => basename(resolve(process.cwd(), target));
72
58
  const isDirEmpty = (dir) => {
73
59
  const entries = readdirSync(dir);
@@ -133,12 +119,12 @@ const validateTargetDir = (target) => {
133
119
  }
134
120
  return validateProjectName(deriveProjectName(target));
135
121
  };
136
- const validateTargetAnswer = (value) => validateTargetDir(formatTargetDir(value ?? ""));
137
- const promptTarget = async () => formatTargetDir(guardCancellation(await p.text({
122
+ const validateTargetAnswer = (value) => validateTargetDir(value ?? "");
123
+ const promptTarget = async () => guardCancellation(await p.text({
138
124
  message: "Project directory",
139
125
  placeholder: "my-app",
140
126
  validate: validateTargetAnswer,
141
- })));
127
+ }));
142
128
  const promptApplicationId = async (name) => {
143
129
  const defaultApplicationId = suggestApplicationId(name);
144
130
  return guardCancellation(await p.text({
@@ -185,10 +171,7 @@ const promptPackageManager = async () => {
185
171
  initialValue: initial,
186
172
  }));
187
173
  };
188
- const isOptionEnabled = async (value, isInteractive, message) => value ??
189
- (isInteractive
190
- ? guardCancellation(await p.confirm({ message, initialValue: true }))
191
- : true);
174
+ const isOptionEnabled = async (value, isInteractive, message) => value ?? (!isInteractive || guardCancellation(await p.confirm({ message, initialValue: true })));
192
175
  const formatFileList = (heading, files) => {
193
176
  const indentedFiles = files.map((file) => ` ${file}`).join("\n");
194
177
  return `${heading}:\n${indentedFiles}`;
@@ -231,14 +214,16 @@ const acceptNonEmptyDirectory = async (target, options, changes) => {
231
214
  refuseNonEmptyDirectory(target, changes);
232
215
  };
233
216
  const assertTargetIsDirectory = (root, target) => {
234
- const stats = statSync(root, { throwIfNoEntry: false });
217
+ const stats = lstatSync(root, { throwIfNoEntry: false });
218
+ if (stats?.isSymbolicLink()) {
219
+ fail(`Cannot scaffold through a symbolic link: ${root}`);
220
+ }
235
221
  if (stats !== undefined && !stats.isDirectory()) {
236
222
  fail(`Target "${target}" is not a directory`);
237
223
  }
238
224
  };
239
225
  const validateTargetAvailability = async (root, target, options, changes) => {
240
226
  const stats = statSync(root, { throwIfNoEntry: false });
241
- assertTargetIsDirectory(root, target);
242
227
  if (stats === undefined || isDirEmpty(root)) {
243
228
  return;
244
229
  }
@@ -248,7 +233,7 @@ const resolveTarget = async (options) => {
248
233
  if (options.name === undefined && options.isInteractive) {
249
234
  return promptTarget();
250
235
  }
251
- const target = formatTargetDir(options.name ?? "");
236
+ const target = options.name ?? "";
252
237
  const error = validateTargetDir(target);
253
238
  return error === undefined ? target : fail(error);
254
239
  };
@@ -280,7 +265,6 @@ const resolveOptions = async (options) => {
280
265
  const displayName = await resolveDisplayName(options, name);
281
266
  const applicationId = await resolveApplicationId(options, name);
282
267
  const root = resolve(process.cwd(), target);
283
- const isCurrentDirectory = root === process.cwd();
284
268
  const shouldInitialize = shouldInitializeGit(root);
285
269
  const packageManager = await resolvePackageManager(requested, options);
286
270
  const isTypescript = await isOptionEnabled(options.isTypescript, options.isInteractive, "Use TypeScript?");
@@ -288,7 +272,6 @@ const resolveOptions = async (options) => {
288
272
  return {
289
273
  target,
290
274
  root,
291
- isCurrentDirectory,
292
275
  name,
293
276
  displayName,
294
277
  applicationId,
@@ -324,31 +307,6 @@ const templateContext = (resolved) => ({
324
307
  developerName: gitConfigValue("user.name") ?? resolved.displayName,
325
308
  developerEmail: gitConfigValue("user.email"),
326
309
  });
327
- const replaceTemplateDestination = (destination) => {
328
- const entry = lstatSync(destination, { throwIfNoEntry: false });
329
- if (entry === undefined) {
330
- return;
331
- }
332
- if (!entry.isFile() && !entry.isSymbolicLink()) {
333
- fail(`Cannot replace scaffold file because its destination is not a file: ${destination}`);
334
- }
335
- rmSync(destination, { force: true });
336
- };
337
- const projectRelativeDestination = (root, destination) => {
338
- const relativePath = relative(root, destination);
339
- const isOutsideProject = relativePath.length === 0 || relativePath.startsWith(`..${sep}`) ||
340
- isAbsolute(relativePath);
341
- if (isOutsideProject) {
342
- fail(`Scaffold destination is outside the project: ${destination}`);
343
- }
344
- return relativePath;
345
- };
346
- const assertRootIsNotSymbolicLink = (root) => {
347
- const rootEntry = lstatSync(root, { throwIfNoEntry: false });
348
- if (rootEntry?.isSymbolicLink()) {
349
- fail(`Cannot scaffold through a symbolic link: ${root}`);
350
- }
351
- };
352
310
  const assertSafeAncestor = (ancestor) => {
353
311
  const entry = lstatSync(ancestor, { throwIfNoEntry: false });
354
312
  if (entry?.isSymbolicLink()) {
@@ -367,18 +325,17 @@ const assertSafeAncestors = (root, relativePath) => {
367
325
  assertSafeAncestor(ancestor);
368
326
  }
369
327
  };
370
- const assertReplaceableDestination = (destination) => {
328
+ const hasReplaceableDestination = (destination) => {
371
329
  const entry = lstatSync(destination, { throwIfNoEntry: false });
372
330
  const isReplaceable = entry === undefined || entry.isFile() || entry.isSymbolicLink();
373
331
  if (!isReplaceable) {
374
332
  fail(`Cannot replace scaffold file because its destination is not a file: ${destination}`);
375
333
  }
334
+ return entry !== undefined;
376
335
  };
377
- const assertSafeDestination = (root, destination) => {
378
- const relativePath = projectRelativeDestination(root, destination);
379
- assertRootIsNotSymbolicLink(root);
380
- assertSafeAncestors(root, relativePath);
381
- assertReplaceableDestination(destination);
336
+ const hasExistingDestination = (root, destination) => {
337
+ assertSafeAncestors(root, relative(root, destination));
338
+ return hasReplaceableDestination(destination);
382
339
  };
383
340
  const plannedScaffoldFiles = async (root, resolved) => {
384
341
  const context = templateContext(resolved);
@@ -394,54 +351,24 @@ const plannedScaffoldFiles = async (root, resolved) => {
394
351
  return files;
395
352
  };
396
353
  const auxiliaryDestinations = (root, resolved) => resolved.packageManager === "pnpm" ? [join(root, "pnpm-workspace.yaml")] : [];
397
- const envModulePath = (root) => join(root, "node_modules", ".gtkx", "env.d.ts");
398
- const shouldWriteEnvModule = (resolved) => resolved.isTypescript && resolved.shouldInstallDependencies;
399
- const generatedDestinations = (root, resolved) => shouldWriteEnvModule(resolved) ? [envModulePath(root)] : [];
400
- const isPresent = (path) => lstatSync(path, { throwIfNoEntry: false }) !== undefined;
401
- const classifyScaffoldChange = (root, destination) => {
402
- const entry = lstatSync(destination, { throwIfNoEntry: false });
403
- if (entry === undefined) {
404
- return undefined;
405
- }
406
- return { destination, relativePath: projectRelativeDestination(root, destination) };
407
- };
408
- const addScaffoldChange = (plan, change) => {
409
- if (change === undefined) {
410
- return;
411
- }
412
- plan.replaceDestinations.push(change.destination);
413
- plan.changes.push(change.relativePath);
414
- };
415
354
  const planScaffoldProject = async (root, resolved) => {
416
355
  const files = await plannedScaffoldFiles(root, resolved);
417
- const reported = [...files.map((file) => file.destination), ...auxiliaryDestinations(root, resolved)];
418
- const generated = generatedDestinations(root, resolved);
419
- for (const destination of [...reported, ...generated]) {
420
- assertSafeDestination(root, destination);
421
- }
422
- const plan = {
356
+ const destinations = [...files.map((file) => file.destination), ...auxiliaryDestinations(root, resolved)];
357
+ const replaceDestinations = destinations.filter((destination) => hasExistingDestination(root, destination));
358
+ return {
423
359
  files,
424
- replaceDestinations: generated.filter((destination) => isPresent(destination)),
425
- changes: [],
360
+ replaceDestinations,
361
+ changes: replaceDestinations.map((destination) => relative(root, destination)),
426
362
  };
427
- for (const destination of reported) {
428
- addScaffoldChange(plan, classifyScaffoldChange(root, destination));
429
- }
430
- return plan;
431
363
  };
432
- const scaffoldProject = (root, resolved, plan) => {
364
+ const scaffoldProject = (plan) => {
433
365
  for (const destination of plan.replaceDestinations) {
434
- replaceTemplateDestination(destination);
366
+ rmSync(destination);
435
367
  }
436
368
  for (const file of plan.files) {
437
369
  mkdirSync(dirname(file.destination), { recursive: true });
438
370
  writeFileSync(file.destination, file.contents);
439
371
  }
440
- if (resolved.shouldIncludeTesting) {
441
- updateManifest(root, (manifest) => {
442
- manifest.scripts.test = "vitest run";
443
- });
444
- }
445
372
  };
446
373
  const withoutInheritedScriptPolicy = async (run) => {
447
374
  const saved = INHERITED_SCRIPT_POLICY_VARS.map((key) => [key, process.env[key]]);
@@ -459,47 +386,20 @@ const withoutInheritedScriptPolicy = async (run) => {
459
386
  }
460
387
  }
461
388
  };
462
- const installDependencies = async (options) => {
463
- const { cwd, packageManager, dependencies, isDev } = options;
464
- await withoutInheritedScriptPolicy(async () => {
465
- await addDependency(dependencies, { cwd, packageManager, dev: isDev, silent: true });
466
- });
467
- };
468
- const pin = (names) => names.map((dependency) => {
469
- if (dependency.startsWith("@gtkx/")) {
470
- return `${dependency}@^${selfVersion}`;
471
- }
472
- return dependency === "typescript" ? `${dependency}@${TYPESCRIPT_VERSION}` : dependency;
473
- });
474
389
  const dependencyVersions = (names) => Object.fromEntries(names.map((dependency) => {
475
390
  if (dependency.startsWith("@gtkx/")) {
476
391
  return [dependency, `^${selfVersion}`];
477
392
  }
478
393
  return [dependency, dependency === "typescript" ? TYPESCRIPT_VERSION : "latest"];
479
394
  }));
395
+ const dependencyArguments = (names) => Object.entries(dependencyVersions(names)).map(([name, version]) => version === "latest" ? name : `${name}@${version}`);
480
396
  const writeDependencies = (root, devDependencies) => {
481
397
  updateManifest(root, (manifest) => {
482
398
  manifest.dependencies = dependencyVersions(DEPENDENCIES);
483
399
  manifest.devDependencies = dependencyVersions(devDependencies);
484
400
  });
485
401
  };
486
- const formatRecovery = ({ resolved, devDependencies }) => {
487
- const add = ADD_COMMAND[resolved.packageManager];
488
- const steps = [
489
- ...(resolved.isCurrentDirectory ? [] : [`cd ${resolved.target}`]),
490
- `${add} ${pin(DEPENDENCIES).join(" ")}`,
491
- `${add} -D ${pin(devDependencies).join(" ")}`,
492
- ];
493
- const indented = steps.map((step) => ` ${step}`).join("\n");
494
- return `${RECOVERY_HEADING}\n${indented}`;
495
- };
496
- const explainInstallFailure = (options) => {
497
- const hint = getInstallHint(errorMessage(options.error));
498
- if (hint !== undefined) {
499
- p.log.warn(hint);
500
- }
501
- p.log.info(formatRecovery(options));
502
- };
402
+ const formatRecovery = (resolved) => `To finish setup, run ${INSTALL_COMMAND[resolved.packageManager]} in ${resolved.root}.`;
503
403
  const runWithSpinner = async (step) => {
504
404
  const spinner = p.spinner();
505
405
  spinner.start(step.pending);
@@ -510,31 +410,28 @@ const runWithSpinner = async (step) => {
510
410
  catch (error) {
511
411
  spinner.error(step.failed);
512
412
  p.log.error(errorMessage(error));
513
- step.explain?.(error);
413
+ step.explain?.();
514
414
  throw new ScaffoldAbortedError(step.failed);
515
415
  }
516
416
  };
517
- const installAllDependencies = async (options) => {
518
- const { resolved, devDependencies } = options;
417
+ const installAllDependencies = async (resolved, devDependencies) => {
519
418
  const { root, packageManager } = resolved;
520
419
  await runWithSpinner({
521
420
  pending: "Installing dependencies...",
522
421
  done: "Dependencies installed",
523
422
  failed: "Failed to install dependencies",
524
423
  run: async () => {
525
- await installDependencies({ cwd: root, packageManager, dependencies: pin(DEPENDENCIES), isDev: false });
526
- await installDependencies({ cwd: root, packageManager, dependencies: pin(devDependencies), isDev: true });
424
+ await withoutInheritedScriptPolicy(async () => {
425
+ const options = { cwd: root, packageManager, silent: true };
426
+ await addDependency(dependencyArguments(DEPENDENCIES), options);
427
+ await addDependency(dependencyArguments(devDependencies), { ...options, dev: true });
428
+ });
527
429
  },
528
- explain: (error) => {
529
- explainInstallFailure({ error, resolved, devDependencies });
430
+ explain: () => {
431
+ p.log.info(formatRecovery(resolved));
530
432
  },
531
433
  });
532
434
  };
533
- const writeInitialEnvModule = (root) => {
534
- const path = envModulePath(root);
535
- mkdirSync(dirname(path), { recursive: true });
536
- writeFileSync(path, `${GTKX_ENV_MODULE_HEADER}\n`);
537
- };
538
435
  const isInsideGitRepository = (root) => {
539
436
  const git = tryResolveExecutable("git");
540
437
  if (git === undefined) {
@@ -567,10 +464,9 @@ const initializeGitRepo = async (root, shouldInitialize) => {
567
464
  };
568
465
  const printNextSteps = (resolved) => {
569
466
  const devCmd = DEV_COMMAND[resolved.packageManager];
570
- const cdStep = resolved.isCurrentDirectory ? "" : `cd ${resolved.target}\n`;
571
467
  const installStep = resolved.shouldInstallDependencies ? "" : `${INSTALL_COMMAND[resolved.packageManager]}\n`;
572
468
  const testingNote = resolved.shouldIncludeTesting ? HEADLESS_COMPOSITOR_NOTE : "";
573
- p.note(`${cdStep}${installStep}${devCmd}${testingNote}`, "Next steps");
469
+ p.note(`${installStep}${devCmd}${testingNote}`, `Run in ${resolved.root}`);
574
470
  };
575
471
  const createProjectStructure = async (root, resolved, plan) => {
576
472
  await runWithSpinner({
@@ -579,11 +475,8 @@ const createProjectStructure = async (root, resolved, plan) => {
579
475
  failed: "Failed to create the project structure",
580
476
  run: () => {
581
477
  mkdirSync(root, { recursive: true });
582
- scaffoldProject(root, resolved, plan);
478
+ scaffoldProject(plan);
583
479
  writeBuildAllowance(root, resolved.packageManager);
584
- if (shouldWriteEnvModule(resolved)) {
585
- writeInitialEnvModule(root);
586
- }
587
480
  },
588
481
  });
589
482
  };
@@ -596,12 +489,10 @@ const scaffold = async (options = {}) => {
596
489
  const plan = await planScaffoldProject(root, resolved);
597
490
  await validateTargetAvailability(root, resolved.target, options, plan.changes);
598
491
  await createProjectStructure(root, resolved, plan);
492
+ writeDependencies(root, devDeps);
599
493
  reportCompletedChanges(plan.changes);
600
494
  if (resolved.shouldInstallDependencies) {
601
- await installAllDependencies({ resolved, devDependencies: devDeps });
602
- }
603
- else {
604
- writeDependencies(root, devDeps);
495
+ await installAllDependencies(resolved, devDeps);
605
496
  }
606
497
  await initializeGitRepo(root, resolved.shouldInitializeGit);
607
498
  printNextSteps(resolved);