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.
- package/README.md +8 -166
- package/dist/build-allowance.d.ts.map +1 -1
- package/dist/build-allowance.js +6 -5
- package/dist/build-allowance.js.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/command.d.ts +3 -2
- package/dist/command.d.ts.map +1 -1
- package/dist/command.js +88 -90
- package/dist/command.js.map +1 -1
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +2 -10
- package/dist/manifest.js.map +1 -1
- package/dist/package-managers.d.ts +0 -3
- package/dist/package-managers.d.ts.map +1 -1
- package/dist/package-managers.js +0 -3
- package/dist/package-managers.js.map +1 -1
- package/dist/scaffolder.d.ts.map +1 -1
- package/dist/scaffolder.js +37 -146
- package/dist/scaffolder.js.map +1 -1
- package/dist/templates/gtkx.config.ts.ejs +2 -2
- package/dist/templates/package.json.ejs +4 -1
- package/package.json +24 -12
- package/src/build-allowance.ts +6 -7
- package/src/cli.ts +1 -1
- package/src/command.ts +108 -109
- package/src/manifest.ts +2 -10
- package/src/package-managers.ts +0 -3
- package/src/scaffolder.ts +51 -227
- package/src/templates/gtkx.config.ts.ejs +2 -2
- package/src/templates/package.json.ejs +4 -1
- package/dist/install-hints.d.ts +0 -3
- package/dist/install-hints.d.ts.map +0 -1
- package/dist/install-hints.js +0 -35
- package/dist/install-hints.js.map +0 -1
- package/src/install-hints.ts +0 -54
|
@@ -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,
|
|
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"]}
|
package/dist/scaffolder.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scaffolder.d.ts","sourceRoot":"","sources":["../src/scaffolder.ts"],"names":[],"mappings":"
|
|
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"}
|
package/dist/scaffolder.js
CHANGED
|
@@ -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,
|
|
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(
|
|
137
|
-
const promptTarget = async () =>
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
378
|
-
|
|
379
|
-
|
|
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
|
|
418
|
-
const
|
|
419
|
-
|
|
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
|
|
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 = (
|
|
364
|
+
const scaffoldProject = (plan) => {
|
|
433
365
|
for (const destination of plan.replaceDestinations) {
|
|
434
|
-
|
|
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 = (
|
|
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?.(
|
|
413
|
+
step.explain?.();
|
|
514
414
|
throw new ScaffoldAbortedError(step.failed);
|
|
515
415
|
}
|
|
516
416
|
};
|
|
517
|
-
const installAllDependencies = async (
|
|
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
|
|
526
|
-
|
|
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: (
|
|
529
|
-
|
|
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(`${
|
|
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(
|
|
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(
|
|
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);
|