@quilted/create 0.1.12 → 0.1.15

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.
Files changed (67) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/build/cjs/app.cjs +303 -0
  3. package/build/cjs/index.cjs +5220 -5198
  4. package/build/cjs/index2.cjs +374 -0
  5. package/build/cjs/index3.cjs +7854 -0
  6. package/build/cjs/minimatch.cjs +1202 -0
  7. package/build/cjs/package-manager.cjs +300 -0
  8. package/build/cjs/package.cjs +410 -0
  9. package/build/cjs/standalone.cjs +151 -0
  10. package/build/esm/app.mjs +280 -0
  11. package/build/esm/index.mjs +5206 -5195
  12. package/build/esm/index2.mjs +365 -0
  13. package/build/esm/index3.mjs +7852 -0
  14. package/build/esm/minimatch.mjs +1200 -0
  15. package/build/esm/package-manager.mjs +270 -0
  16. package/build/esm/package.mjs +387 -0
  17. package/build/esm/standalone.mjs +149 -0
  18. package/build/tsconfig.tsbuildinfo +1 -1
  19. package/build/typescript/app.d.ts +2 -0
  20. package/build/typescript/app.d.ts.map +1 -0
  21. package/build/typescript/help.d.ts +6 -0
  22. package/build/typescript/help.d.ts.map +1 -0
  23. package/build/typescript/package.d.ts +2 -0
  24. package/build/typescript/package.d.ts.map +1 -0
  25. package/build/typescript/shared/package-manager.d.ts +3 -0
  26. package/build/typescript/shared/package-manager.d.ts.map +1 -0
  27. package/build/typescript/shared/prompts.d.ts +22 -0
  28. package/build/typescript/shared/prompts.d.ts.map +1 -0
  29. package/build/typescript/shared/tsconfig.d.ts +3 -0
  30. package/build/typescript/shared/tsconfig.d.ts.map +1 -0
  31. package/build/typescript/shared.d.ts +21 -0
  32. package/build/typescript/shared.d.ts.map +1 -0
  33. package/package.json +13 -3
  34. package/source/app.ts +387 -0
  35. package/source/create.ts +43 -383
  36. package/source/help.ts +132 -0
  37. package/source/package.ts +510 -0
  38. package/source/shared/package-manager.ts +74 -0
  39. package/source/shared/prompts.ts +133 -0
  40. package/source/shared/tsconfig.ts +90 -0
  41. package/source/shared.ts +165 -0
  42. package/templates/{app → app-basic}/App.tsx +4 -7
  43. package/templates/{app → app-basic}/features/Start/Start.module.css +0 -0
  44. package/templates/{app → app-basic}/features/Start/Start.tsx +1 -1
  45. package/templates/{app → app-basic}/features/Start/index.ts +0 -0
  46. package/templates/{app → app-basic}/foundation/Head.tsx +8 -8
  47. package/templates/{app → app-basic}/foundation/Http.tsx +10 -6
  48. package/templates/{app → app-basic}/package.json +10 -1
  49. package/templates/{app → app-basic}/quilt.project.ts +0 -0
  50. package/templates/{app → app-basic}/server.tsx +0 -0
  51. package/templates/{app → app-basic}/shared/types.ts +0 -0
  52. package/templates/{app → app-basic}/tsconfig.json +0 -0
  53. package/templates/app-single-file/App.tsx +168 -0
  54. package/templates/app-single-file/package.json +30 -0
  55. package/templates/app-single-file/quilt.project.ts +6 -0
  56. package/templates/app-single-file/tsconfig.json +9 -0
  57. package/templates/{workspace → github}/_github/workflows/actions/prepare/action.yml +0 -0
  58. package/templates/{workspace → github}/_github/workflows/ci.yml +0 -0
  59. package/templates/package/package.json +24 -9
  60. package/templates/package/quilt.project.ts +1 -1
  61. package/templates/vscode/_vscode/extensions.json +7 -0
  62. package/templates/vscode/_vscode/settings.json +17 -0
  63. package/templates/workspace/_gitignore +1 -1
  64. package/templates/workspace/_prettierignore +1 -0
  65. package/templates/workspace/package.json +1 -6
  66. package/templates/workspace/tsconfig.json +6 -1
  67. package/templates/workspace/pnpm-workspace.yaml +0 -3
@@ -0,0 +1,510 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import {execSync} from 'child_process';
4
+
5
+ import arg from 'arg';
6
+ import * as color from 'colorette';
7
+ import {stripIndent} from 'common-tags';
8
+
9
+ import {printHelp} from './help';
10
+ import {
11
+ format,
12
+ loadTemplate,
13
+ createOutputTarget,
14
+ isEmpty,
15
+ emptyDirectory,
16
+ toValidPackageName,
17
+ relativeDirectoryForDisplay,
18
+ } from './shared';
19
+ import {
20
+ prompt,
21
+ getCreateAsMonorepo,
22
+ getExtrasToSetup,
23
+ getPackageManager,
24
+ getShouldInstall,
25
+ } from './shared/prompts';
26
+ import {addToTsConfig} from './shared/tsconfig';
27
+ import {addToPackageManagerWorkspaces} from './shared/package-manager';
28
+
29
+ type Arguments = ReturnType<typeof getArgv>;
30
+
31
+ export async function createPackage() {
32
+ const argv = getArgv();
33
+
34
+ if (argv['--help']) {
35
+ const additionalOptions = stripIndent`
36
+ ${color.cyan(`--react`)}, ${color.cyan(`--no-react`)}
37
+ Whether this package will use React. If you don’t provide this option, the command
38
+ will ask you about it later.
39
+
40
+ ${color.cyan(`--public`)}, ${color.cyan(`--private`)}
41
+ Whether this package will be published for other projects to install. If you do not
42
+ provide this option, the command will ask you about it later.
43
+
44
+ ${color.cyan(`--registry`)}
45
+ The package registry to publish this package to. This option only applies if you create
46
+ a public package. If you do not provide this option, it will use the default NPM registry.
47
+ `;
48
+
49
+ printHelp({
50
+ kind: 'package',
51
+ options: additionalOptions,
52
+ packageManager: argv['--package-manager']?.toLowerCase(),
53
+ });
54
+ return;
55
+ }
56
+
57
+ const inWorkspace = fs.existsSync('quilt.workspace.ts');
58
+
59
+ const name = await getName(argv);
60
+ const directory = await getDirectory(argv, {name, inWorkspace});
61
+ const isPublic = await getPublic(argv);
62
+ const useReact = await getReact(argv);
63
+
64
+ const createAsMonorepo = !inWorkspace && (await getCreateAsMonorepo(argv));
65
+ const shouldInstall = await getShouldInstall(argv);
66
+ const packageManager = await getPackageManager(argv);
67
+ const setupExtras = await getExtrasToSetup(argv, {inWorkspace});
68
+
69
+ const partOfMonorepo = inWorkspace || createAsMonorepo;
70
+
71
+ const packageDirectory = createAsMonorepo
72
+ ? path.join(
73
+ directory,
74
+ `packages/${toValidPackageName(name.split('/').pop()!)}`,
75
+ )
76
+ : directory;
77
+
78
+ if (fs.existsSync(directory)) {
79
+ await emptyDirectory(directory);
80
+
81
+ if (packageDirectory !== directory) {
82
+ fs.mkdirSync(packageDirectory, {recursive: true});
83
+ }
84
+ } else {
85
+ fs.mkdirSync(packageDirectory, {recursive: true});
86
+ }
87
+
88
+ const rootDirectory = inWorkspace ? process.cwd() : directory;
89
+ const outputRoot = createOutputTarget(rootDirectory);
90
+ const packageTemplate = loadTemplate('package');
91
+ const workspaceTemplate = loadTemplate('workspace');
92
+
93
+ let quiltProject = await packageTemplate.read('quilt.project.ts');
94
+
95
+ if (useReact) {
96
+ quiltProject = quiltProject.replace('react: false', 'react: true');
97
+ }
98
+
99
+ // If we aren’t already in a workspace, copy the workspace files over, which
100
+ // are needed if we are making a monorepo or not.
101
+ if (!inWorkspace) {
102
+ await workspaceTemplate.copy(directory, (file) => {
103
+ // When this is a single project, we use the project’s Quilt configuration as the base.
104
+ if (file === 'quilt.workspace.ts') return createAsMonorepo;
105
+
106
+ // We need to make some adjustments to the root package.json
107
+ return file !== 'package.json';
108
+ });
109
+
110
+ // If we are creating a monorepo, we need to add the root package.json and
111
+ // package manager workspace configuration.
112
+ if (createAsMonorepo) {
113
+ const workspacePackageJson = JSON.parse(
114
+ await workspaceTemplate.read('package.json'),
115
+ );
116
+
117
+ workspacePackageJson.name = toValidPackageName(name!);
118
+
119
+ if (packageManager === 'pnpm') {
120
+ await outputRoot.write(
121
+ 'pnpm-workspace.yaml',
122
+ await format(
123
+ `
124
+ packages:
125
+ - './packages/*'
126
+ `,
127
+ {as: 'yaml'},
128
+ ),
129
+ );
130
+ } else {
131
+ workspacePackageJson.workspaces = ['packages/*'];
132
+ }
133
+
134
+ await outputRoot.write(
135
+ 'package.json',
136
+ await format(JSON.stringify(workspacePackageJson), {
137
+ as: 'json-stringify',
138
+ }),
139
+ );
140
+ } else {
141
+ const [projectPackageJson, projectTSConfig, workspacePackageJson] =
142
+ await Promise.all([
143
+ packageTemplate
144
+ .read('package.json')
145
+ .then((content) => JSON.parse(content)),
146
+ packageTemplate
147
+ .read('tsconfig.json')
148
+ .then((content) => JSON.parse(content)),
149
+ workspaceTemplate
150
+ .read('package.json')
151
+ .then((content) => JSON.parse(content)),
152
+ ]);
153
+
154
+ workspacePackageJson.eslintConfig = projectPackageJson.eslintConfig;
155
+ workspacePackageJson.browserslist = projectPackageJson.browserslist;
156
+
157
+ const newPackageJson: Record<string, any> = {};
158
+
159
+ // We want to put the project’s dependencies in the package.json, respecting
160
+ // the preferred ordering (dependencies, peer dependencies, dev dependencies).
161
+ for (const [key, value] of Object.entries(projectPackageJson)) {
162
+ if (key !== 'devDependencies') {
163
+ newPackageJson[key] = value;
164
+ continue;
165
+ }
166
+
167
+ newPackageJson.dependencies = projectPackageJson.dependencies;
168
+ newPackageJson.peerDependencies = projectPackageJson.peerDependencies;
169
+ newPackageJson.peerDependenciesMeta =
170
+ projectPackageJson.peerDependenciesMeta;
171
+ newPackageJson.devDependencies = sortKeys({
172
+ ...workspacePackageJson.devDependencies,
173
+ ...projectPackageJson.devDependencies,
174
+ });
175
+ }
176
+
177
+ adjustPackageJson(newPackageJson, {
178
+ name: toValidPackageName(name!),
179
+ react: useReact,
180
+ isPublic,
181
+ registry: argv['--registry'],
182
+ });
183
+
184
+ const addBackToTSConfigInclude = new Set([
185
+ 'quilt.project.ts',
186
+ '*.test.ts',
187
+ '*.test.tsx',
188
+ ]);
189
+
190
+ projectTSConfig.exclude = projectTSConfig.exclude.filter(
191
+ (excluded: string) => !addBackToTSConfigInclude.has(excluded),
192
+ );
193
+
194
+ quiltProject = quiltProject
195
+ .replace('quiltPackage', 'quiltWorkspace, quiltPackage')
196
+ .replace('quiltPackage(', 'quiltWorkspace(), quiltPackage(');
197
+
198
+ await outputRoot.write(
199
+ 'quilt.project.ts',
200
+ await format(quiltProject, {as: 'typescript'}),
201
+ );
202
+
203
+ await outputRoot.write(
204
+ 'package.json',
205
+ await format(JSON.stringify(newPackageJson), {
206
+ as: 'json-stringify',
207
+ }),
208
+ );
209
+
210
+ await outputRoot.write(
211
+ 'tsconfig.json',
212
+ await format(JSON.stringify(projectTSConfig), {as: 'json'}),
213
+ );
214
+ }
215
+
216
+ if (setupExtras.has('github')) {
217
+ await loadTemplate('github').copy(directory);
218
+ }
219
+
220
+ if (setupExtras.has('vscode')) {
221
+ await loadTemplate('vscode').copy(directory);
222
+ }
223
+ }
224
+
225
+ await packageTemplate.copy(packageDirectory, (file) => {
226
+ // If we are in a monorepo, we can use all the template files as they are
227
+ if (file === 'tsconfig.json') {
228
+ return partOfMonorepo;
229
+ }
230
+
231
+ // We need to make some adjustments the project’s package.json and Quilt config
232
+ return file !== 'package.json' && file !== 'quilt.project.ts';
233
+ });
234
+
235
+ if (partOfMonorepo) {
236
+ // Write the package’s package.json (the root one was already created)
237
+ const projectPackageJson = JSON.parse(
238
+ await packageTemplate.read('package.json'),
239
+ );
240
+
241
+ adjustPackageJson(projectPackageJson, {
242
+ name: toValidPackageName(name),
243
+ react: useReact,
244
+ isPublic,
245
+ registry: argv['--registry'],
246
+ });
247
+
248
+ await outputRoot.write(
249
+ path.join(packageDirectory, 'package.json'),
250
+ await format(JSON.stringify(projectPackageJson), {
251
+ as: 'json-stringify',
252
+ }),
253
+ );
254
+
255
+ await Promise.all([
256
+ addToTsConfig(packageDirectory, outputRoot),
257
+ addToPackageManagerWorkspaces(
258
+ packageDirectory,
259
+ outputRoot,
260
+ packageManager,
261
+ ),
262
+ ]);
263
+ }
264
+
265
+ if (shouldInstall) {
266
+ process.stdout.write('\nInstalling dependencies...\n');
267
+ // TODO: better loading, handle errors
268
+ execSync(`${packageManager} install`, {cwd: rootDirectory});
269
+ process.stdout.moveCursor(0, -1);
270
+ process.stdout.clearLine(1);
271
+ console.log('Installed dependencies.');
272
+ }
273
+
274
+ const commands: string[] = [];
275
+
276
+ if (!inWorkspace && directory !== process.cwd()) {
277
+ commands.push(
278
+ `cd ${color.cyan(
279
+ relativeDirectoryForDisplay(path.relative(process.cwd(), directory)),
280
+ )} ${color.dim('# Move into your new package’s directory')}`,
281
+ );
282
+ }
283
+
284
+ if (!shouldInstall) {
285
+ commands.push(
286
+ `pnpm install ${color.dim('# Install all your dependencies')}`,
287
+ );
288
+ }
289
+
290
+ if (!inWorkspace) {
291
+ // TODO: change this condition to check if git was initialized already
292
+ commands.push(
293
+ `git init && git add -A && git commit -m "Initial commit" ${color.dim(
294
+ '# Start your git history (optional)',
295
+ )}`,
296
+ );
297
+ }
298
+
299
+ const whatsNext = stripIndent`
300
+ Your new package is ready to go! There’s just ${
301
+ commands.length > 1 ? 'a few more steps' : 'one more step'
302
+ } you’ll need to take
303
+ in order to start building:
304
+ `;
305
+
306
+ console.log();
307
+ console.log(whatsNext);
308
+ console.log();
309
+ console.log(commands.map((command) => ` ${command}`).join('\n'));
310
+
311
+ const followUp = stripIndent`
312
+ Quilt can also help you build, test, lint, and type-check your new package.
313
+ You can learn more about building packages with Quilt by reading the documentation:
314
+ ${color.underline(
315
+ color.magenta(
316
+ 'https://github.com/lemonmade/quilt/tree/main/documentation',
317
+ ),
318
+ )}
319
+
320
+ Have fun! 🎉
321
+ `;
322
+
323
+ console.log();
324
+ console.log(followUp);
325
+ }
326
+
327
+ // Argument handling
328
+
329
+ function getArgv() {
330
+ const argv = arg(
331
+ {
332
+ '--yes': Boolean,
333
+ '-y': '--yes',
334
+ '--name': String,
335
+ '--directory': String,
336
+ '--install': Boolean,
337
+ '--no-install': Boolean,
338
+ '--monorepo': Boolean,
339
+ '--no-monorepo': Boolean,
340
+ '--package-manager': String,
341
+ '--extras': [String],
342
+ '--no-extras': Boolean,
343
+ '--react': Boolean,
344
+ '--no-react': Boolean,
345
+ '--public': Boolean,
346
+ '--private': Boolean,
347
+ '--registry': String,
348
+ '--help': Boolean,
349
+ '-h': '--help',
350
+ },
351
+ {permissive: true},
352
+ );
353
+
354
+ return argv;
355
+ }
356
+
357
+ async function getName(argv: Arguments) {
358
+ let {'--name': name} = argv;
359
+
360
+ if (name == null) {
361
+ name = await prompt({
362
+ type: 'text',
363
+ message: 'What would you like to name your new package?',
364
+ initial: '@my-team/package',
365
+ });
366
+ }
367
+
368
+ return name!;
369
+ }
370
+
371
+ async function getDirectory(
372
+ argv: Arguments,
373
+ {name, inWorkspace}: {name: string; inWorkspace: boolean},
374
+ ) {
375
+ let directory = argv['--directory']
376
+ ? path.resolve(argv['--directory'])
377
+ : undefined;
378
+
379
+ if (directory == null) {
380
+ const basePackageName = toValidPackageName(name.split('/').pop()!);
381
+ const defaultDirectory = inWorkspace
382
+ ? `packages/${basePackageName}`
383
+ : basePackageName;
384
+
385
+ directory = path.resolve(
386
+ await prompt({
387
+ type: 'text',
388
+ message: 'Where would you like to create your new package?',
389
+ initial: defaultDirectory,
390
+ }),
391
+ );
392
+ }
393
+
394
+ while (!argv['--yes']) {
395
+ if (fs.existsSync(directory) && !(await isEmpty(directory))) {
396
+ const relativeDirectory = path.relative(process.cwd(), directory);
397
+
398
+ const empty = await prompt({
399
+ type: 'confirm',
400
+ message: `Directory ${color.bold(
401
+ relativeDirectoryForDisplay(relativeDirectory),
402
+ )} is not empty, is it safe to empty it?`,
403
+ initial: true,
404
+ });
405
+
406
+ if (empty) break;
407
+
408
+ const promptDirectory = await prompt({
409
+ type: 'text',
410
+ message: 'What directory do you want to create your package in?',
411
+ });
412
+
413
+ directory = path.resolve(promptDirectory);
414
+ } else {
415
+ break;
416
+ }
417
+ }
418
+
419
+ return directory;
420
+ }
421
+
422
+ async function getPublic(argv: Arguments) {
423
+ let isPublic: boolean;
424
+
425
+ if (argv['--public'] || argv['--yes']) {
426
+ isPublic = true;
427
+ } else if (argv['--private']) {
428
+ isPublic = false;
429
+ } else {
430
+ isPublic = await prompt({
431
+ type: 'confirm',
432
+ message: 'Will you publish this package to use in other projects?',
433
+ initial: true,
434
+ });
435
+ }
436
+
437
+ return isPublic;
438
+ }
439
+
440
+ async function getReact(argv: Arguments) {
441
+ let useReact: boolean;
442
+
443
+ if (argv['--react'] || argv['--yes']) {
444
+ useReact = true;
445
+ } else if (argv['--no-react']) {
446
+ useReact = false;
447
+ } else {
448
+ useReact = await prompt({
449
+ type: 'confirm',
450
+ message: 'Will this package depend on React?',
451
+ initial: true,
452
+ });
453
+ }
454
+
455
+ return useReact;
456
+ }
457
+
458
+ function adjustPackageJson(
459
+ packageJson: Record<string, any>,
460
+ {
461
+ name,
462
+ react,
463
+ isPublic,
464
+ registry,
465
+ }: {name: string; react: boolean; isPublic: boolean; registry?: string},
466
+ ) {
467
+ packageJson.name = name;
468
+
469
+ const packageParts = name.split('/');
470
+ const scope = packageParts[0]!.startsWith('@') ? packageParts[0] : undefined;
471
+ const finalRegistry = registry ?? 'https://registry.npmjs.org';
472
+
473
+ if (scope) {
474
+ packageJson.publishConfig[`${scope}/registry`] = finalRegistry;
475
+ } else if (registry) {
476
+ packageJson.publishConfig.registry = finalRegistry;
477
+ }
478
+
479
+ if (isPublic) {
480
+ delete packageJson.private;
481
+ } else {
482
+ delete packageJson.publishConfig;
483
+ }
484
+
485
+ if (!react) {
486
+ delete packageJson.dependencies['@types/react'];
487
+ delete packageJson.devDependencies['react'];
488
+ delete packageJson.peerDependencies['react'];
489
+ delete packageJson.peerDependenciesMeta['react'];
490
+
491
+ packageJson.eslintConfig.extends = packageJson.eslintConfig.extends.filter(
492
+ (extend: string) => !extend.includes('react'),
493
+ );
494
+ }
495
+
496
+ return packageJson;
497
+ }
498
+
499
+ function sortKeys(object: Record<string, any>) {
500
+ const newObject: Record<string, any> = {};
501
+ const sortedEntries = Object.entries(object).sort(([keyOne], [keyTwo]) =>
502
+ keyOne.localeCompare(keyTwo),
503
+ );
504
+
505
+ for (const [key, value] of sortedEntries) {
506
+ newObject[key] = value;
507
+ }
508
+
509
+ return newObject;
510
+ }
@@ -0,0 +1,74 @@
1
+ import {relative} from 'path';
2
+
3
+ import type {OutputTarget} from '../shared';
4
+ import {format, relativeDirectoryForDisplay} from '../shared';
5
+
6
+ export async function addToPackageManagerWorkspaces(
7
+ directory: string,
8
+ output: OutputTarget,
9
+ packageManager: 'yarn' | 'npm' | 'pnpm',
10
+ ) {
11
+ if (packageManager === 'pnpm') {
12
+ const {parse, stringify} = await import('yaml');
13
+ const workspaceYaml = parse(await output.read('pnpm-workspace.yaml'));
14
+
15
+ workspaceYaml.packages = await addToWorkspaces(
16
+ relative(output.root, directory),
17
+ workspaceYaml.packages ?? [],
18
+ );
19
+
20
+ await output.write(
21
+ 'pnpm-workspace.yaml',
22
+ await format(stringify(workspaceYaml), {as: 'yaml'}),
23
+ );
24
+ } else {
25
+ const packageJson = JSON.parse(await output.read('package.json'));
26
+
27
+ packageJson.workspaces = await addToWorkspaces(
28
+ relative(output.root, directory),
29
+ packageJson.workspaces ?? [],
30
+ );
31
+
32
+ await output.write(
33
+ 'package.json',
34
+ await format(JSON.stringify(packageJson), {
35
+ as: 'json-stringify',
36
+ }),
37
+ );
38
+ }
39
+ }
40
+
41
+ async function addToWorkspaces(relative: string, workspaces: string[]) {
42
+ if (workspaces.length === 0) {
43
+ return [relative];
44
+ }
45
+
46
+ // Default documentation seems to generally exclude leading `./` on paths
47
+ let pretty = false;
48
+ let hasMatch = false;
49
+
50
+ const {default: minimatch} = await import('minimatch');
51
+
52
+ for (const pattern of workspaces) {
53
+ let normalizedPattern = pattern;
54
+
55
+ if (pattern.startsWith('./')) {
56
+ pretty = true;
57
+ normalizedPattern = pattern.slice(2);
58
+ }
59
+
60
+ if (minimatch(relative, normalizedPattern)) {
61
+ hasMatch = true;
62
+ break;
63
+ }
64
+ }
65
+
66
+ if (hasMatch) {
67
+ return workspaces;
68
+ }
69
+
70
+ return [
71
+ ...workspaces,
72
+ pretty ? relativeDirectoryForDisplay(relative) : relative,
73
+ ].sort((patternOne, patternTwo) => patternOne.localeCompare(patternTwo));
74
+ }
@@ -0,0 +1,133 @@
1
+ import * as fs from 'fs';
2
+
3
+ import type {Result as ArgvResult} from 'arg';
4
+ import prompts from 'prompts';
5
+ import type {PromptObject} from 'prompts';
6
+ import * as color from 'colorette';
7
+
8
+ import {AbortError} from '@quilted/events';
9
+
10
+ export async function prompt(prompt: Omit<PromptObject, 'name'>) {
11
+ const result = await prompts<'value'>(
12
+ {name: 'value', ...prompt},
13
+ {
14
+ onCancel() {
15
+ throw new AbortError();
16
+ },
17
+ },
18
+ );
19
+
20
+ return result.value;
21
+ }
22
+
23
+ type BaseArguments = ArgvResult<{
24
+ '--yes': BooleanConstructor;
25
+ '--install': BooleanConstructor;
26
+ '--no-install': BooleanConstructor;
27
+ '--monorepo': BooleanConstructor;
28
+ '--no-monorepo': BooleanConstructor;
29
+ '--extras': [StringConstructor];
30
+ '--no-extras': BooleanConstructor;
31
+ '--package-manager': StringConstructor;
32
+ }>;
33
+
34
+ export async function getCreateAsMonorepo(argv: BaseArguments) {
35
+ let createAsMonorepo: boolean;
36
+
37
+ if (argv['--monorepo' || argv['--yes']]) {
38
+ createAsMonorepo = true;
39
+ } else if (argv['--no-monorepo']) {
40
+ createAsMonorepo = false;
41
+ } else {
42
+ createAsMonorepo = await prompt({
43
+ type: 'confirm',
44
+ message:
45
+ 'Do you want to create this app as a monorepo, with room for more projects?',
46
+ initial: true,
47
+ });
48
+ }
49
+
50
+ return createAsMonorepo;
51
+ }
52
+
53
+ export async function getShouldInstall(argv: BaseArguments) {
54
+ let shouldInstall: boolean;
55
+
56
+ if (argv['--install'] || argv['--yes']) {
57
+ shouldInstall = true;
58
+ } else if (argv['--no-install']) {
59
+ shouldInstall = false;
60
+ } else {
61
+ shouldInstall = await prompt({
62
+ type: 'confirm',
63
+ message:
64
+ 'Do you want to install dependencies for this app after creating it?',
65
+ initial: true,
66
+ });
67
+ }
68
+
69
+ return shouldInstall;
70
+ }
71
+
72
+ const VALID_PACKAGE_MANAGERS = new Set(['pnpm', 'npm', 'yarn']);
73
+
74
+ export async function getPackageManager(argv: BaseArguments) {
75
+ let packageManager!: 'yarn' | 'npm' | 'pnpm';
76
+
77
+ if (argv['--package-manager']) {
78
+ const explicitPackageManager =
79
+ argv['--package-manager'].toLocaleLowerCase();
80
+
81
+ packageManager = VALID_PACKAGE_MANAGERS.has(explicitPackageManager)
82
+ ? (explicitPackageManager as any)
83
+ : 'npm';
84
+ } else {
85
+ const npmUserAgent = process.env['npm_config_user_agent'] ?? 'npm';
86
+
87
+ if (npmUserAgent.includes('pnpm') || fs.existsSync('pnpm-lock.yaml')) {
88
+ packageManager = 'pnpm';
89
+ } else if (npmUserAgent.includes('yarn') || fs.existsSync('yarn.lock')) {
90
+ packageManager = 'yarn';
91
+ } else {
92
+ packageManager = 'npm';
93
+ }
94
+ }
95
+
96
+ return packageManager;
97
+ }
98
+
99
+ type Extra = 'github' | 'vscode';
100
+ const VALID_EXTRAS = new Set<Extra>(['github', 'vscode']);
101
+
102
+ export async function getExtrasToSetup(
103
+ argv: BaseArguments,
104
+ {inWorkspace}: {inWorkspace: boolean},
105
+ ) {
106
+ if (inWorkspace || argv['--no-extras']) return new Set<Extra>();
107
+
108
+ if (argv['--extras']) {
109
+ return new Set<Extra>(
110
+ argv['--extras'].filter((extra) =>
111
+ VALID_EXTRAS.has(extra as any),
112
+ ) as Extra[],
113
+ );
114
+ }
115
+
116
+ const setupExtras = await prompt({
117
+ type: 'multiselect',
118
+ message: 'Which additional tools would you like to configure?',
119
+ instructions: color.dim(
120
+ `\n Use ${color.bold('space')} to select, ${color.bold(
121
+ 'a',
122
+ )} to select all, ${color.bold('return')} to submit`,
123
+ ),
124
+ choices: [
125
+ {title: 'VSCode', value: 'vscode'},
126
+ {title: 'GitHub', value: 'github'},
127
+ ],
128
+ });
129
+
130
+ const extrasToSetup = new Set<Extra>(setupExtras as any[]);
131
+
132
+ return extrasToSetup;
133
+ }