create-gtkx 2.0.0-beta.1 → 2.0.0-beta.3
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/dist/command.d.ts +9 -1
- package/dist/command.d.ts.map +1 -1
- package/dist/command.js +13 -1
- package/dist/command.js.map +1 -1
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +10 -4
- package/dist/manifest.js.map +1 -1
- package/dist/package-managers.d.ts +3 -0
- package/dist/package-managers.d.ts.map +1 -1
- package/dist/package-managers.js +24 -3
- package/dist/package-managers.js.map +1 -1
- package/dist/scaffolder.d.ts +2 -0
- package/dist/scaffolder.d.ts.map +1 -1
- package/dist/scaffolder.js +161 -36
- package/dist/scaffolder.js.map +1 -1
- package/dist/templates/gtkx.config.ts.ejs +2 -3
- package/dist/templates/mise.toml.ejs +2 -0
- package/dist/templates/src/app.tsx.ejs +1 -1
- package/dist/templates.d.ts +1 -1
- package/dist/templates.d.ts.map +1 -1
- package/dist/templates.js.map +1 -1
- package/package.json +3 -3
- package/src/command.ts +15 -1
- package/src/manifest.ts +10 -4
- package/src/package-managers.ts +24 -3
- package/src/scaffolder.ts +214 -41
- package/src/templates/gtkx.config.ts.ejs +2 -3
- package/src/templates/mise.toml.ejs +2 -0
- package/src/templates/src/app.tsx.ejs +1 -1
- package/src/templates.ts +1 -1
package/src/scaffolder.ts
CHANGED
|
@@ -2,8 +2,8 @@ import * as p from "@clack/prompts";
|
|
|
2
2
|
import { APPLICATION_ID_MAX_LENGTH, isValidApplicationId } from "@gtkx/config/internal";
|
|
3
3
|
import { errorMessage, tryResolveExecutable, upperFirst } from "@gtkx/utils";
|
|
4
4
|
import { execFileSync } from "node:child_process";
|
|
5
|
-
import { mkdirSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
6
|
-
import { basename, dirname, join, resolve } from "node:path";
|
|
5
|
+
import { lstatSync, mkdirSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { basename, dirname, isAbsolute, 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" };
|
|
@@ -23,11 +23,13 @@ import { listTemplates, renderFile, type TemplateContext } from "./templates.js"
|
|
|
23
23
|
type CreateOptions = {
|
|
24
24
|
name?: string | undefined;
|
|
25
25
|
applicationId?: string | undefined;
|
|
26
|
+
displayName?: string | undefined;
|
|
26
27
|
packageManager?: string | undefined;
|
|
27
28
|
isTypescript?: boolean | undefined;
|
|
28
29
|
shouldIncludeTesting?: boolean | undefined;
|
|
29
30
|
isInteractive?: boolean | undefined;
|
|
30
31
|
shouldOverwrite?: boolean | undefined;
|
|
32
|
+
shouldInstallDependencies?: boolean | undefined;
|
|
31
33
|
};
|
|
32
34
|
|
|
33
35
|
type ResolvedOptions = {
|
|
@@ -35,13 +37,17 @@ type ResolvedOptions = {
|
|
|
35
37
|
root: string;
|
|
36
38
|
isCurrentDirectory: boolean;
|
|
37
39
|
name: string;
|
|
40
|
+
displayName: string;
|
|
38
41
|
applicationId: string;
|
|
39
42
|
packageManager: PackageManager;
|
|
40
43
|
isTypescript: boolean;
|
|
41
44
|
shouldIncludeTesting: boolean;
|
|
42
|
-
|
|
45
|
+
shouldInitializeGit: boolean;
|
|
46
|
+
shouldInstallDependencies: boolean;
|
|
43
47
|
};
|
|
44
48
|
|
|
49
|
+
type ScaffoldFile = { destination: string; contents: string };
|
|
50
|
+
|
|
45
51
|
type InstallDependenciesOptions = {
|
|
46
52
|
cwd: string;
|
|
47
53
|
packageManager: PackageManager;
|
|
@@ -88,6 +94,10 @@ const ADD_COMMAND: Record<PackageManager, string> = Object.fromEntries(
|
|
|
88
94
|
PACKAGE_MANAGERS.map((manager) => [manager.value, manager.addCommand]),
|
|
89
95
|
) as Record<PackageManager, string>;
|
|
90
96
|
|
|
97
|
+
const INSTALL_COMMAND: Record<PackageManager, string> = Object.fromEntries(
|
|
98
|
+
PACKAGE_MANAGERS.map((manager) => [manager.value, manager.installCommand]),
|
|
99
|
+
) as Record<PackageManager, string>;
|
|
100
|
+
|
|
91
101
|
const INHERITED_SCRIPT_POLICY_VARS = ["npm_config_allow_scripts", "npm_config_allow-scripts"];
|
|
92
102
|
const TESTING_TEMPLATES = new Set(["vitest.config.ts", "tests/app.test.tsx"]);
|
|
93
103
|
const TYPESCRIPT_TEMPLATES = new Set(["tsconfig.json", "src/gtkx-env.d.ts"]);
|
|
@@ -106,7 +116,8 @@ const APPLICATION_ID_SUFFIX = ".app";
|
|
|
106
116
|
const APPLICATION_ID_SEGMENT_LIMIT = APPLICATION_ID_MAX_LENGTH - APPLICATION_ID_PREFIX.length -
|
|
107
117
|
APPLICATION_ID_SUFFIX.length;
|
|
108
118
|
|
|
109
|
-
const
|
|
119
|
+
const displayNameFromProjectName = (name: string): string =>
|
|
120
|
+
name.split("-").map((part) => upperFirst(part)).join(" ");
|
|
110
121
|
|
|
111
122
|
const gitConfigValue = (key: string): string | null => {
|
|
112
123
|
const git = tryResolveExecutable("git");
|
|
@@ -153,6 +164,12 @@ const isDirEmpty = (dir: string): boolean => {
|
|
|
153
164
|
return entries.length === 0 || (entries.length === 1 && entries[0] === ".git");
|
|
154
165
|
};
|
|
155
166
|
|
|
167
|
+
const shouldInitializeGit = (root: string): boolean => {
|
|
168
|
+
const entry = lstatSync(root, { throwIfNoEntry: false });
|
|
169
|
+
|
|
170
|
+
return entry === undefined || (entry.isDirectory() && readdirSync(root).length === 0);
|
|
171
|
+
};
|
|
172
|
+
|
|
156
173
|
const getDevDependencies = ({
|
|
157
174
|
isTypescript,
|
|
158
175
|
shouldIncludeTesting,
|
|
@@ -200,6 +217,17 @@ const validateApplicationIdInput = (value: string | undefined): string | undefin
|
|
|
200
217
|
const validateApplicationIdAnswer = (value: string | undefined): string | undefined =>
|
|
201
218
|
value ? validateApplicationIdFormat(value) : undefined;
|
|
202
219
|
|
|
220
|
+
const validateDisplayName = (value: string | undefined): string | undefined => {
|
|
221
|
+
if (value === undefined || value.trim().length === 0) {
|
|
222
|
+
return "Display name is required";
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return value.includes("\n") || value.includes("\r") ? "Display name must be a single line" : undefined;
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const validateDisplayNameAnswer = (value: string | undefined): string | undefined =>
|
|
229
|
+
value ? validateDisplayName(value) : undefined;
|
|
230
|
+
|
|
203
231
|
const fail = (message: string): never => {
|
|
204
232
|
p.log.error(message);
|
|
205
233
|
throw new ScaffoldAbortedError(message);
|
|
@@ -252,6 +280,19 @@ const promptApplicationId = async (name: string): Promise<string> => {
|
|
|
252
280
|
);
|
|
253
281
|
};
|
|
254
282
|
|
|
283
|
+
const promptDisplayName = async (name: string): Promise<string> => {
|
|
284
|
+
const defaultDisplayName = displayNameFromProjectName(name);
|
|
285
|
+
|
|
286
|
+
return guardCancellation(
|
|
287
|
+
await p.text({
|
|
288
|
+
message: "Display name",
|
|
289
|
+
placeholder: defaultDisplayName,
|
|
290
|
+
defaultValue: defaultDisplayName,
|
|
291
|
+
validate: validateDisplayNameAnswer,
|
|
292
|
+
}),
|
|
293
|
+
);
|
|
294
|
+
};
|
|
295
|
+
|
|
255
296
|
const packageManagerOption = (manager: (typeof PACKAGE_MANAGERS)[number], detected: PackageManager | undefined) => {
|
|
256
297
|
let hint: string | undefined;
|
|
257
298
|
|
|
@@ -297,16 +338,6 @@ const isOptionEnabled = async (
|
|
|
297
338
|
? guardCancellation(await p.confirm({ message, initialValue: true }))
|
|
298
339
|
: true);
|
|
299
340
|
|
|
300
|
-
const emptyDir = (root: string): void => {
|
|
301
|
-
for (const entry of readdirSync(root)) {
|
|
302
|
-
if (entry === ".git") {
|
|
303
|
-
continue;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
rmSync(join(root, entry), { recursive: true, force: true });
|
|
307
|
-
}
|
|
308
|
-
};
|
|
309
|
-
|
|
310
341
|
const shouldOverwriteDirectory = async (target: string, options: CreateOptions): Promise<boolean> => {
|
|
311
342
|
if (!options.isInteractive) {
|
|
312
343
|
return options.shouldOverwrite === true;
|
|
@@ -314,17 +345,17 @@ const shouldOverwriteDirectory = async (target: string, options: CreateOptions):
|
|
|
314
345
|
|
|
315
346
|
return guardCancellation(
|
|
316
347
|
await p.confirm({
|
|
317
|
-
message: `Directory "${target}" is not empty.
|
|
348
|
+
message: `Directory "${target}" is not empty. Replace scaffold files?`,
|
|
318
349
|
initialValue: false,
|
|
319
350
|
}),
|
|
320
351
|
);
|
|
321
352
|
};
|
|
322
353
|
|
|
323
|
-
const
|
|
354
|
+
const validateTargetAvailability = async (root: string, target: string, options: CreateOptions): Promise<void> => {
|
|
324
355
|
const stats = statSync(root, { throwIfNoEntry: false });
|
|
325
356
|
|
|
326
357
|
if (stats === undefined) {
|
|
327
|
-
return
|
|
358
|
+
return;
|
|
328
359
|
}
|
|
329
360
|
|
|
330
361
|
if (!stats.isDirectory()) {
|
|
@@ -332,14 +363,14 @@ const shouldEmptyTargetDirectory = async (root: string, target: string, options:
|
|
|
332
363
|
}
|
|
333
364
|
|
|
334
365
|
if (isDirEmpty(root)) {
|
|
335
|
-
return
|
|
366
|
+
return;
|
|
336
367
|
}
|
|
337
368
|
|
|
338
369
|
if (await shouldOverwriteDirectory(target, options)) {
|
|
339
|
-
return
|
|
370
|
+
return;
|
|
340
371
|
}
|
|
341
372
|
|
|
342
|
-
|
|
373
|
+
fail(`Directory "${target}" is not empty`);
|
|
343
374
|
};
|
|
344
375
|
|
|
345
376
|
const resolveTarget = async (options: CreateOptions): Promise<string> => {
|
|
@@ -362,6 +393,14 @@ const resolveApplicationId = async (options: CreateOptions, name: string): Promi
|
|
|
362
393
|
return error === undefined ? applicationId : fail(error);
|
|
363
394
|
};
|
|
364
395
|
|
|
396
|
+
const resolveDisplayName = async (options: CreateOptions, name: string): Promise<string> => {
|
|
397
|
+
const value = options.displayName ??
|
|
398
|
+
(options.isInteractive ? await promptDisplayName(name) : displayNameFromProjectName(name));
|
|
399
|
+
const error = validateDisplayName(value);
|
|
400
|
+
|
|
401
|
+
return error === undefined ? value.trim() : fail(error);
|
|
402
|
+
};
|
|
403
|
+
|
|
365
404
|
const resolvePackageManager = async (
|
|
366
405
|
requested: PackageManager | undefined,
|
|
367
406
|
options: CreateOptions,
|
|
@@ -381,10 +420,12 @@ const resolveOptions = async (options: CreateOptions): Promise<ResolvedOptions>
|
|
|
381
420
|
const requested = requestedPackageManager(options.packageManager);
|
|
382
421
|
const target = await resolveTarget(options);
|
|
383
422
|
const name = deriveProjectName(target);
|
|
423
|
+
const displayName = await resolveDisplayName(options, name);
|
|
384
424
|
const applicationId = await resolveApplicationId(options, name);
|
|
385
425
|
const root = resolve(process.cwd(), target);
|
|
386
426
|
const isCurrentDirectory = root === process.cwd();
|
|
387
|
-
const
|
|
427
|
+
const shouldInitialize = shouldInitializeGit(root);
|
|
428
|
+
await validateTargetAvailability(root, target, options);
|
|
388
429
|
const packageManager = await resolvePackageManager(requested, options);
|
|
389
430
|
const isTypescript = await isOptionEnabled(options.isTypescript, options.isInteractive, "Use TypeScript?");
|
|
390
431
|
|
|
@@ -399,11 +440,13 @@ const resolveOptions = async (options: CreateOptions): Promise<ResolvedOptions>
|
|
|
399
440
|
root,
|
|
400
441
|
isCurrentDirectory,
|
|
401
442
|
name,
|
|
443
|
+
displayName,
|
|
402
444
|
applicationId,
|
|
403
445
|
packageManager,
|
|
404
446
|
isTypescript,
|
|
405
447
|
shouldIncludeTesting,
|
|
406
|
-
|
|
448
|
+
shouldInitializeGit: shouldInitialize,
|
|
449
|
+
shouldInstallDependencies: options.shouldInstallDependencies !== false,
|
|
407
450
|
};
|
|
408
451
|
};
|
|
409
452
|
|
|
@@ -430,25 +473,123 @@ const destinationPath = (template: string, resolved: ResolvedOptions): string =>
|
|
|
430
473
|
const templateContext = (resolved: ResolvedOptions): TemplateContext => ({
|
|
431
474
|
name: resolved.name,
|
|
432
475
|
applicationId: resolved.applicationId,
|
|
433
|
-
|
|
476
|
+
displayName: resolved.displayName,
|
|
434
477
|
shouldIncludeTesting: resolved.shouldIncludeTesting,
|
|
435
478
|
isTypescript: resolved.isTypescript,
|
|
436
479
|
importExtension: resolved.isTypescript ? ".js" : ".jsx",
|
|
437
|
-
developerName: gitConfigValue("user.name") ??
|
|
480
|
+
developerName: gitConfigValue("user.name") ?? resolved.displayName,
|
|
438
481
|
developerEmail: gitConfigValue("user.email"),
|
|
439
482
|
});
|
|
440
483
|
|
|
441
|
-
const
|
|
484
|
+
const replaceTemplateDestination = (destination: string): void => {
|
|
485
|
+
const entry = lstatSync(destination, { throwIfNoEntry: false });
|
|
486
|
+
|
|
487
|
+
if (entry === undefined) {
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (!entry.isFile() && !entry.isSymbolicLink()) {
|
|
492
|
+
fail(`Cannot replace scaffold file because its destination is not a file: ${destination}`);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
rmSync(destination, { force: true });
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
const projectRelativeDestination = (root: string, destination: string): string => {
|
|
499
|
+
const relativePath = relative(root, destination);
|
|
500
|
+
const isOutsideProject = relativePath.length === 0 || relativePath.startsWith(`..${sep}`) ||
|
|
501
|
+
isAbsolute(relativePath);
|
|
502
|
+
|
|
503
|
+
if (isOutsideProject) {
|
|
504
|
+
fail(`Scaffold destination is outside the project: ${destination}`);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
return relativePath;
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
const assertRootIsNotSymbolicLink = (root: string): void => {
|
|
511
|
+
const rootEntry = lstatSync(root, { throwIfNoEntry: false });
|
|
512
|
+
|
|
513
|
+
if (rootEntry?.isSymbolicLink()) {
|
|
514
|
+
fail(`Cannot scaffold through a symbolic link: ${root}`);
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
const assertSafeAncestor = (ancestor: string): void => {
|
|
519
|
+
const entry = lstatSync(ancestor, { throwIfNoEntry: false });
|
|
520
|
+
|
|
521
|
+
if (entry?.isSymbolicLink()) {
|
|
522
|
+
fail(`Cannot scaffold through a symbolic link: ${ancestor}`);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
if (entry !== undefined && !entry.isDirectory()) {
|
|
526
|
+
fail(`Cannot scaffold through a non-directory path: ${ancestor}`);
|
|
527
|
+
}
|
|
528
|
+
};
|
|
529
|
+
|
|
530
|
+
const assertSafeAncestors = (root: string, relativePath: string): void => {
|
|
531
|
+
let ancestor = root;
|
|
532
|
+
const segments = relativePath.split(sep);
|
|
533
|
+
const ancestorSegments = segments.slice(0, -1);
|
|
534
|
+
|
|
535
|
+
for (const segment of ancestorSegments) {
|
|
536
|
+
ancestor = join(ancestor, segment);
|
|
537
|
+
assertSafeAncestor(ancestor);
|
|
538
|
+
}
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
const assertReplaceableDestination = (destination: string): void => {
|
|
542
|
+
const entry = lstatSync(destination, { throwIfNoEntry: false });
|
|
543
|
+
const isReplaceable = entry === undefined || entry.isFile() || entry.isSymbolicLink();
|
|
544
|
+
|
|
545
|
+
if (!isReplaceable) {
|
|
546
|
+
fail(`Cannot replace scaffold file because its destination is not a file: ${destination}`);
|
|
547
|
+
}
|
|
548
|
+
};
|
|
549
|
+
|
|
550
|
+
const assertSafeDestination = (root: string, destination: string): void => {
|
|
551
|
+
const relativePath = projectRelativeDestination(root, destination);
|
|
552
|
+
assertRootIsNotSymbolicLink(root);
|
|
553
|
+
assertSafeAncestors(root, relativePath);
|
|
554
|
+
assertReplaceableDestination(destination);
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
const plannedScaffoldFiles = async (root: string, resolved: ResolvedOptions): Promise<ScaffoldFile[]> => {
|
|
442
558
|
const context = templateContext(resolved);
|
|
559
|
+
const files: ScaffoldFile[] = [];
|
|
443
560
|
|
|
444
561
|
for (const template of listTemplates()) {
|
|
445
|
-
if (
|
|
446
|
-
|
|
562
|
+
if (isTemplateIncluded(template, resolved)) {
|
|
563
|
+
files.push({
|
|
564
|
+
destination: join(root, destinationPath(template, resolved)),
|
|
565
|
+
contents: await renderFile(template, context),
|
|
566
|
+
});
|
|
447
567
|
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
return files;
|
|
571
|
+
};
|
|
572
|
+
|
|
573
|
+
const auxiliaryDestinations = (root: string, resolved: ResolvedOptions): string[] => [
|
|
574
|
+
...(resolved.packageManager === "pnpm" ? [join(root, "pnpm-workspace.yaml")] : []),
|
|
575
|
+
...(resolved.isTypescript ? [join(root, "node_modules", ".gtkx", "env.d.ts")] : []),
|
|
576
|
+
];
|
|
577
|
+
|
|
578
|
+
const scaffoldProject = async (root: string, resolved: ResolvedOptions): Promise<void> => {
|
|
579
|
+
const files = await plannedScaffoldFiles(root, resolved);
|
|
580
|
+
const destinations = [...files.map((file) => file.destination), ...auxiliaryDestinations(root, resolved)];
|
|
581
|
+
|
|
582
|
+
for (const destination of destinations) {
|
|
583
|
+
assertSafeDestination(root, destination);
|
|
584
|
+
}
|
|
448
585
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
586
|
+
for (const destination of destinations) {
|
|
587
|
+
replaceTemplateDestination(destination);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
for (const file of files) {
|
|
591
|
+
mkdirSync(dirname(file.destination), { recursive: true });
|
|
592
|
+
writeFileSync(file.destination, file.contents);
|
|
452
593
|
}
|
|
453
594
|
|
|
454
595
|
if (resolved.shouldIncludeTesting) {
|
|
@@ -487,6 +628,17 @@ const installDependencies = async (options: InstallDependenciesOptions): Promise
|
|
|
487
628
|
const pin = (names: string[]): string[] =>
|
|
488
629
|
names.map((dependency) => dependency.startsWith("@gtkx/") ? `${dependency}@^${selfVersion}` : dependency);
|
|
489
630
|
|
|
631
|
+
const dependencyVersions = (names: string[]): Record<string, string> => Object.fromEntries(
|
|
632
|
+
names.map((dependency) => [dependency, dependency.startsWith("@gtkx/") ? `^${selfVersion}` : "latest"]),
|
|
633
|
+
);
|
|
634
|
+
|
|
635
|
+
const writeDependencies = (root: string, devDependencies: string[]): void => {
|
|
636
|
+
updateManifest(root, (manifest) => {
|
|
637
|
+
manifest.dependencies = dependencyVersions(DEPENDENCIES);
|
|
638
|
+
manifest.devDependencies = dependencyVersions(devDependencies);
|
|
639
|
+
});
|
|
640
|
+
};
|
|
641
|
+
|
|
490
642
|
const formatRecovery = ({ resolved, devDependencies }: InstallFailureOptions): string => {
|
|
491
643
|
const add = ADD_COMMAND[resolved.packageManager];
|
|
492
644
|
|
|
@@ -550,7 +702,27 @@ const writeInitialEnvModule = (root: string): void => {
|
|
|
550
702
|
writeFileSync(join(storeDir, "env.d.ts"), `${GTKX_ENV_MODULE_HEADER}\n`);
|
|
551
703
|
};
|
|
552
704
|
|
|
553
|
-
const
|
|
705
|
+
const isInsideGitRepository = (root: string): boolean => {
|
|
706
|
+
const git = tryResolveExecutable("git");
|
|
707
|
+
|
|
708
|
+
if (git === undefined) {
|
|
709
|
+
return false;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
try {
|
|
713
|
+
execFileSync(git, ["-C", root, "rev-parse", "--show-toplevel"], { stdio: "ignore" });
|
|
714
|
+
|
|
715
|
+
return true;
|
|
716
|
+
} catch {
|
|
717
|
+
return false;
|
|
718
|
+
}
|
|
719
|
+
};
|
|
720
|
+
|
|
721
|
+
const initializeGitRepo = async (root: string, shouldInitialize: boolean): Promise<void> => {
|
|
722
|
+
if (!shouldInitialize || isInsideGitRepository(root)) {
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
|
|
554
726
|
const spinner = p.spinner();
|
|
555
727
|
spinner.start("Initializing git repository...");
|
|
556
728
|
|
|
@@ -568,8 +740,9 @@ const initializeGitRepo = async (root: string): Promise<void> => {
|
|
|
568
740
|
const printNextSteps = (resolved: ResolvedOptions): void => {
|
|
569
741
|
const devCmd = DEV_COMMAND[resolved.packageManager];
|
|
570
742
|
const cdStep = resolved.isCurrentDirectory ? "" : `cd ${resolved.target}\n`;
|
|
743
|
+
const installStep = resolved.shouldInstallDependencies ? "" : `${INSTALL_COMMAND[resolved.packageManager]}\n`;
|
|
571
744
|
const testingNote = resolved.shouldIncludeTesting ? HEADLESS_COMPOSITOR_NOTE : "";
|
|
572
|
-
p.note(`${cdStep}${devCmd}${testingNote}`, "Next steps");
|
|
745
|
+
p.note(`${cdStep}${installStep}${devCmd}${testingNote}`, "Next steps");
|
|
573
746
|
};
|
|
574
747
|
|
|
575
748
|
const createProjectStructure = async (root: string, resolved: ResolvedOptions): Promise<void> => {
|
|
@@ -579,13 +752,12 @@ const createProjectStructure = async (root: string, resolved: ResolvedOptions):
|
|
|
579
752
|
failed: "Failed to create the project structure",
|
|
580
753
|
run: async () => {
|
|
581
754
|
mkdirSync(root, { recursive: true });
|
|
582
|
-
|
|
583
|
-
if (resolved.shouldEmptyTarget) {
|
|
584
|
-
emptyDir(root);
|
|
585
|
-
}
|
|
586
|
-
|
|
587
755
|
await scaffoldProject(root, resolved);
|
|
588
756
|
writeBuildAllowance(root, resolved.packageManager);
|
|
757
|
+
|
|
758
|
+
if (resolved.isTypescript) {
|
|
759
|
+
writeInitialEnvModule(root);
|
|
760
|
+
}
|
|
589
761
|
},
|
|
590
762
|
});
|
|
591
763
|
};
|
|
@@ -596,13 +768,14 @@ const scaffold = async (options: CreateOptions = {}): Promise<void> => {
|
|
|
596
768
|
const { root } = resolved;
|
|
597
769
|
const devDeps = getDevDependencies(resolved);
|
|
598
770
|
await createProjectStructure(root, resolved);
|
|
599
|
-
await installAllDependencies({ resolved, devDependencies: devDeps });
|
|
600
771
|
|
|
601
|
-
if (resolved.
|
|
602
|
-
|
|
772
|
+
if (resolved.shouldInstallDependencies) {
|
|
773
|
+
await installAllDependencies({ resolved, devDependencies: devDeps });
|
|
774
|
+
} else {
|
|
775
|
+
writeDependencies(root, devDeps);
|
|
603
776
|
}
|
|
604
777
|
|
|
605
|
-
await initializeGitRepo(root);
|
|
778
|
+
await initializeGitRepo(root, resolved.shouldInitializeGit);
|
|
606
779
|
printNextSteps(resolved);
|
|
607
780
|
};
|
|
608
781
|
|
|
@@ -4,11 +4,10 @@ export default defineConfig({
|
|
|
4
4
|
applicationId: "<%= applicationId %>",
|
|
5
5
|
applicationIcon: "data/icons",
|
|
6
6
|
deploy: {
|
|
7
|
-
name:
|
|
7
|
+
name: <%- sourceStringLiteral(displayName) %>,
|
|
8
8
|
summary: "A GTK4 application built with GTKX",
|
|
9
9
|
description: [
|
|
10
|
-
|
|
11
|
-
+ "widgets from React. Replace this paragraph with a description of what your application does.",
|
|
10
|
+
<%- sourceStringLiteral(`${displayName} is a GTK4 and Adwaita application built with GTKX, which renders native GObject widgets from React. Replace this paragraph with a description of what your application does.`) %>,
|
|
12
11
|
],
|
|
13
12
|
categories: ["Utility"],
|
|
14
13
|
},
|