@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
package/source/create.ts CHANGED
@@ -1,420 +1,80 @@
1
1
  /* eslint no-console: off */
2
2
 
3
- import * as fs from 'fs';
4
- import * as path from 'path';
5
- import {EOL} from 'os';
6
- import {fileURLToPath} from 'url';
7
-
8
- import prompts from 'prompts';
9
- import type {Answers} from 'prompts';
10
3
  import arg from 'arg';
11
4
  import * as color from 'colorette';
12
- import {packageDirectory} from 'pkg-dir';
5
+ import {stripIndent} from 'common-tags';
6
+ import {AbortError} from '@quilted/events';
13
7
 
14
- class CancellationError extends Error {}
8
+ import {printHelp} from './help';
9
+ import {prompt} from './shared';
15
10
 
16
- const argv = arg({});
17
- const cwd = process.cwd();
11
+ const VALID_PROJECT_KINDS = new Set(['app', 'package']);
18
12
 
19
13
  run().catch((error) => {
14
+ if (AbortError.test(error)) return;
15
+
20
16
  console.error(error);
21
17
  process.exitCode = 1;
22
18
  });
23
19
 
24
20
  async function run() {
25
- const create = argv._[0];
26
-
27
- if (!create) {
28
- if (fs.existsSync('quilt.workspace.ts')) {
29
- await createProject();
30
- } else {
31
- await createWorkspace();
32
- }
33
-
34
- return;
35
- }
21
+ const permissiveArgs = arg(
22
+ {'--help': Boolean, '-h': '--help', '--package-manager': String},
23
+ {permissive: true},
24
+ );
36
25
 
37
- switch (create) {
38
- case 'workspace': {
39
- await createWorkspace(argv._[1]);
40
- break;
41
- }
42
- case 'package': {
43
- await createPackage(argv._[1]);
44
- break;
45
- }
46
- case 'app': {
47
- await createApp();
48
- break;
49
- }
50
- default: {
51
- throw new Error(`Unknown argument: ${create}`);
52
- }
53
- }
54
- }
26
+ const firstArgument = permissiveArgs._[0]?.toLowerCase();
55
27
 
56
- async function createWorkspace(explicitName?: string) {
57
- if (fs.existsSync('quilt.workspace.ts')) {
58
- console.log(`\nYou’re already in a Quilt workspace!`);
59
- console.log();
60
- console.log(`Run one of the following to add projects to your workspace:`);
61
- console.log(` pnpm create @quilted app # create a new app`);
62
- console.log(` pnpm create @quilted package # create a new package`);
28
+ if (
29
+ firstArgument != null &&
30
+ !firstArgument.startsWith('-') &&
31
+ !VALID_PROJECT_KINDS.has(firstArgument)
32
+ ) {
33
+ // TODO: show help with error message
34
+ process.exitCode = 1;
63
35
  return;
64
36
  }
65
37
 
66
- let targetDirectory = explicitName;
67
- const defaultName = targetDirectory || 'my-project';
68
-
69
- let result: Answers<'name' | 'overwrite'>;
70
-
71
- try {
72
- result = await prompts<'name' | 'overwrite'>(
73
- [
74
- {
75
- type: targetDirectory ? null : 'text',
76
- name: 'name',
77
- message: 'What would you like to name this workspace?',
78
- initial: defaultName,
79
- onState: (state) => {
80
- targetDirectory = state.value.trim() || defaultName;
81
- },
82
- },
83
- {
84
- type: () =>
85
- !fs.existsSync(targetDirectory!) || isEmpty(targetDirectory!)
86
- ? (null as any)
87
- : 'confirm',
88
- name: 'overwrite',
89
- message: () =>
90
- `Target directory (${
91
- targetDirectory === '.' ? 'current directory' : targetDirectory
92
- }) is not empty. Remove existing files and continue?`,
93
- onState: (overwrite: boolean) => {
94
- if (overwrite === false) {
95
- throw new CancellationError();
96
- }
97
- },
98
- },
99
- ],
100
- {
101
- onCancel: () => {
102
- throw new CancellationError();
103
- },
104
- },
105
- );
106
- } catch (cancelled) {
107
- if (cancelled instanceof CancellationError) return;
108
- throw cancelled;
109
- }
110
-
111
- const {overwrite} = result;
112
- const root = path.join(cwd, targetDirectory!);
113
- const packageName = toValidPackageName(targetDirectory!);
38
+ let kind =
39
+ firstArgument == null || !VALID_PROJECT_KINDS.has(firstArgument)
40
+ ? undefined
41
+ : firstArgument;
114
42
 
115
- if (overwrite) {
116
- emptyDirectory(root);
117
- } else if (!fs.existsSync(root)) {
118
- fs.mkdirSync(root, {recursive: true});
119
- }
120
-
121
- console.log(
122
- `\nCreating Quilt workspace in ${color.cyan(path.relative(cwd, root))}...`,
123
- );
43
+ const header = stripIndent`
44
+ 🧵 ${color.bold('quilt create')}
45
+ `;
124
46
 
125
- const templateRoot = await templateDirectory('workspace');
126
- const template = createTemplateFileSystem(templateRoot, root);
127
-
128
- for (const file of template.files()) {
129
- if (file === 'package.json') {
130
- const packageJson = JSON.parse(template.read(file));
131
- packageJson.name = packageName;
132
- template.write(file, JSON.stringify(packageJson, null, 2) + EOL);
133
- continue;
134
- }
135
-
136
- template.copy(file);
137
- }
138
-
139
- console.log(`\nDone! Here’s what you’ll need to do next:\n`);
47
+ console.log(header);
140
48
  console.log();
141
- if (root !== cwd) {
142
- console.log(` cd ${path.relative(cwd, root)}`);
49
+
50
+ if (permissiveArgs['--help'] && !kind) {
51
+ printHelp({
52
+ packageManager: permissiveArgs['--package-manager']?.toLowerCase(),
53
+ });
54
+ return;
143
55
  }
144
- console.log(` pnpm install # install dependencies`);
145
- console.log(
146
- ` git init && git add --all && git commit --message "Initial commit" # start your git history`,
147
- );
148
- console.log(
149
- ` pnpm create @quilted app # create an app in your workspace, or`,
150
- );
151
- console.log(
152
- ` pnpm create @quilted package # create a package in your workspace`,
153
- );
154
- console.log();
155
- }
156
56
 
157
- async function createProject() {
158
- const result = await prompts<'type'>([
159
- {
57
+ if (kind == null) {
58
+ kind = await prompt({
160
59
  type: 'select',
161
- name: 'type',
162
60
  message: 'What kind of project would you like to create?',
163
61
  choices: [
164
62
  {title: 'App', value: 'app'},
165
63
  {title: 'Package', value: 'package'},
166
64
  ],
167
- },
168
- ]);
65
+ });
66
+ }
169
67
 
170
- switch (result.type) {
171
- case 'package': {
172
- await createPackage();
173
- break;
174
- }
68
+ switch (kind) {
175
69
  case 'app': {
70
+ const {createApp} = await import('./app');
176
71
  await createApp();
177
72
  break;
178
73
  }
179
- }
180
- }
181
-
182
- async function createPackage(explicitName?: string) {
183
- let name!: string;
184
- let scope: string | undefined;
185
- let targetDirectory!: string;
186
-
187
- let result: Answers<'name' | 'overwrite'>;
188
-
189
- try {
190
- result = await prompts<'name' | 'overwrite'>(
191
- [
192
- {
193
- type: 'text',
194
- name: 'name',
195
- message: 'What would you like to name this package?',
196
- initial: toValidPackageName(explicitName ?? ''),
197
- onState: (state) => {
198
- name = toValidPackageName(state.value);
199
- scope = name.match(/^@[^/]+/)?.[0] ?? undefined;
200
- targetDirectory = path.join(
201
- 'packages',
202
- scope ? name.replace(`${scope}/`, '') : name,
203
- );
204
- },
205
- },
206
- {
207
- type: () =>
208
- !fs.existsSync(targetDirectory!) || isEmpty(targetDirectory!)
209
- ? (null as any)
210
- : 'confirm',
211
- name: 'overwrite',
212
- message: () =>
213
- `Target directory (${targetDirectory}) is not empty. Remove existing files and continue?`,
214
- onState: (overwrite: boolean) => {
215
- if (overwrite === false) {
216
- throw new CancellationError();
217
- }
218
- },
219
- },
220
- ],
221
- {
222
- onCancel: () => {
223
- throw new CancellationError();
224
- },
225
- },
226
- );
227
- } catch (cancelled) {
228
- if (cancelled instanceof CancellationError) return;
229
- throw cancelled;
230
- }
231
-
232
- const {overwrite} = result;
233
-
234
- const root = path.join(cwd, targetDirectory!);
235
-
236
- if (overwrite) {
237
- emptyDirectory(root);
238
- } else if (!fs.existsSync(root)) {
239
- fs.mkdirSync(root, {recursive: true});
240
- }
241
-
242
- console.log(
243
- `\nCreating ${color.cyan(name)} package in ${color.cyan(
244
- path.relative(cwd, root),
245
- )}...`,
246
- );
247
-
248
- const templateRoot = await templateDirectory('package');
249
- const template = createTemplateFileSystem(templateRoot, root);
250
-
251
- for (const file of template.files()) {
252
- switch (file) {
253
- case 'package.json': {
254
- const packageJson = JSON.parse(template.read(file));
255
- packageJson.name = name;
256
-
257
- if (scope) {
258
- packageJson.publishConfig[`${scope}:registry`] =
259
- 'https://registry.npmjs.org';
260
- }
261
-
262
- template.write(file, JSON.stringify(packageJson, null, 2) + EOL);
263
- break;
264
- }
265
- case 'README.md': {
266
- template.write(
267
- file,
268
- template.read(file).replace('# Package', `# ${name}`),
269
- );
270
- break;
271
- }
272
- default: {
273
- template.copy(file);
274
- }
275
- }
276
-
277
- if (file === 'package.json') {
278
- const packageJson = JSON.parse(template.read(file));
279
- packageJson.name = name;
280
-
281
- if (scope) {
282
- packageJson.publishConfig[`${scope}:registry`] =
283
- 'https://registry.npmjs.org';
284
- }
285
-
286
- template.write(file, JSON.stringify(packageJson, null, 2) + EOL);
287
- continue;
288
- }
289
-
290
- template.copy(file);
291
- }
292
-
293
- console.log(`\nDone! Your new package has been created.`);
294
- console.log(`Get started by adding source code in the \`source\` directory.`);
295
- console.log(
296
- `Make sure to edit the \`description\` and \`repository\` sections of your \`package.json\` before publishing.`,
297
- );
298
- console.log(`Have fun!`);
299
- console.log();
300
- }
301
-
302
- async function createApp() {
303
- const root = path.join(cwd, 'app');
304
-
305
- if (fs.existsSync(root)) {
306
- const {overwrite} = await prompts<'overwrite'>([
307
- {
308
- type: 'confirm',
309
- name: 'overwrite',
310
- message: () =>
311
- `Target directory (./app) is not empty. Remove existing files and continue?`,
312
- },
313
- ]);
314
-
315
- if (!overwrite) return;
316
-
317
- emptyDirectory(root);
318
- } else if (!fs.existsSync(root)) {
319
- fs.mkdirSync(root, {recursive: true});
320
- }
321
-
322
- console.log(`\nCreating app in ${color.cyan(path.relative(cwd, root))}...`);
323
-
324
- const templateRoot = await templateDirectory('app');
325
- const template = createTemplateFileSystem(templateRoot, root);
326
-
327
- for (const file of template.files()) {
328
- if (file === 'package.json') {
329
- const packageJson = JSON.parse(template.read(file));
330
- packageJson.name = 'app';
331
- template.write(file, JSON.stringify(packageJson, null, 2) + EOL);
332
- continue;
74
+ case 'package': {
75
+ const {createPackage} = await import('./package');
76
+ await createPackage();
77
+ break;
333
78
  }
334
-
335
- template.copy(file);
336
- }
337
-
338
- console.log(`\nDone! Your new app has been created.`);
339
- console.log(`Get started by:`);
340
- console.log(` * running \`pnpm develop\` to start the development server`);
341
- console.log(` * editing code in the \`app\` directory`);
342
- console.log(`Have fun!`);
343
- console.log();
344
- }
345
-
346
- // Utilities
347
-
348
- function createTemplateFileSystem(templateRoot: string, targetRoot: string) {
349
- return {
350
- write(file: string, content: string) {
351
- const targetPath = path.join(
352
- targetRoot,
353
- file.startsWith('_') ? `.${file.slice(1)}` : file,
354
- );
355
- fs.writeFileSync(targetPath, content);
356
- },
357
- copy(file: string) {
358
- const targetPath = path.join(
359
- targetRoot,
360
- file.startsWith('_') ? `.${file.slice(1)}` : file,
361
- );
362
- copy(path.join(templateRoot, file), targetPath);
363
- },
364
- read(file: string) {
365
- return fs.readFileSync(path.join(templateRoot, file), {encoding: 'utf8'});
366
- },
367
- files() {
368
- return fs.readdirSync(templateRoot);
369
- },
370
- };
371
- }
372
-
373
- async function templateDirectory(name: 'package' | 'app' | 'workspace') {
374
- const packageRoot = await packageDirectory({
375
- cwd: path.dirname(fileURLToPath(import.meta.url)),
376
- });
377
-
378
- return path.join(packageRoot, 'templates', name);
379
- }
380
-
381
- function toValidPackageName(projectName: string) {
382
- return projectName
383
- .trim()
384
- .toLowerCase()
385
- .replace(/\s+/g, '-')
386
- .replace(/^[._]/, '')
387
- .replace(/[^a-z0-9-~@/]+/g, '-');
388
- }
389
-
390
- function copy(source: string, destination: string) {
391
- const stat = fs.statSync(source);
392
- if (stat.isDirectory()) {
393
- copyDirectory(source, destination);
394
- } else {
395
- fs.copyFileSync(source, destination);
396
- }
397
- }
398
-
399
- function copyDirectory(source: string, destination: string) {
400
- fs.mkdirSync(destination, {recursive: true});
401
- for (const file of fs.readdirSync(source)) {
402
- const srcFile = path.resolve(source, file);
403
- const destFile = path.resolve(destination, file);
404
- copy(srcFile, destFile);
405
- }
406
- }
407
-
408
- function isEmpty(path: string) {
409
- return fs.readdirSync(path).length === 0;
410
- }
411
-
412
- function emptyDirectory(dir: string) {
413
- if (!fs.existsSync(dir)) {
414
- return;
415
- }
416
-
417
- for (const file of fs.readdirSync(dir)) {
418
- fs.rmSync(path.resolve(dir, file), {force: true, recursive: true});
419
79
  }
420
80
  }
package/source/help.ts ADDED
@@ -0,0 +1,132 @@
1
+ import * as color from 'colorette';
2
+ import {stripIndent} from 'common-tags';
3
+
4
+ export function printHelp({
5
+ kind,
6
+ options: customOptions,
7
+ packageManager,
8
+ }: {kind?: 'app' | 'package'; options?: string; packageManager?: string} = {}) {
9
+ const command = createCommand(packageManager);
10
+
11
+ const usage = stripIndent`
12
+ ${color.bold('Usage:')} ${command} ${
13
+ kind ? color.magenta(kind) : color.magenta('[kind]')
14
+ } ${color.green('[name]')} ${color.cyan('[options]')}
15
+ `;
16
+
17
+ console.log(usage);
18
+
19
+ const example = stripIndent`
20
+ ${color.bold('Example:')} ${command} ${color.magenta(
21
+ kind ?? 'app',
22
+ )} ${color.green(`my-${kind ?? 'app'}`)} ${color.cyan('--install')}
23
+ `;
24
+
25
+ console.log(color.dim(example));
26
+
27
+ if (!kind) {
28
+ const kindSection = stripIndent`
29
+ ${color.bold(
30
+ color.magenta('kind'),
31
+ )} can be one of the following project types:
32
+
33
+ - ${color.magenta('app')}, a web application
34
+ - ${color.magenta('package')}, a shared library of code
35
+
36
+ You’ll be asked a few additional questions based on the kind you choose.
37
+ `;
38
+
39
+ console.log();
40
+ console.log(kindSection);
41
+ }
42
+
43
+ const optionsSection = stripIndent`
44
+ ${color.cyan('--name')}
45
+ The name of the project. When creating a package, you should use the public name you
46
+ want for the package, including any scope you want to use (example: ${color.bold(
47
+ '@my-org/my-package',
48
+ )}).
49
+ If you don’t provide a name with this flag, this command will ask you for one later.
50
+
51
+ ${color.cyan('--directory')}
52
+ The directory to create the project in. If you don’t provide this flag, Quilt will pick
53
+ a default directory based on the kind of the project and the name you provided.
54
+
55
+ ${color.cyan(`--monorepo`)}, ${color.cyan(`--no-monorepo`)}
56
+ If you aren’t already in a monorepo, this flag will create your new project with some
57
+ additional structure that allows it to be a monorepo — that is, you will be able to add
58
+ multiple projects to a single repository.
59
+
60
+ ${color.cyan(`--install`)}, ${color.cyan(`--no-install`)}
61
+ Whether to install dependencies in the newly created project. If you do not provide this
62
+ flag, Quilt will ask you about it later.
63
+
64
+ ${color.cyan('--package-manager')}
65
+ The package manager to use for your new project. This choice will be used to populate
66
+ some commands, and will be used to install dependencies if the ${color.cyan(
67
+ '--install',
68
+ )} flag is set.
69
+
70
+ Must be one of the following: ${color.bold('pnpm')}, ${color.bold(
71
+ 'npm',
72
+ )}, or ${color.bold('yarn')}.
73
+
74
+ ${color.cyan(`--extras`)}, ${color.cyan(`--no-extras`)}
75
+ Extra developer tools to configure when creating your new project. This option only
76
+ applies when creating a brand new project, not when adding a project to an existing
77
+ workspace. You can include this flag multiple times to add multiple tools. You can
78
+ enable any of the following tools:
79
+
80
+ - ${color.bold(
81
+ 'github',
82
+ )}, which adds a continuous integration (CI) GitHub Action to your project.
83
+ - ${color.bold(
84
+ 'vscode',
85
+ )}, which adds some basic VSCode settings to your project.
86
+
87
+ ${color.cyan('--yes')}, ${color.cyan('-y')}
88
+ Answers “yes” to any question this command would have asked.
89
+
90
+ ${color.cyan('--help')}, ${color.cyan('-h')}
91
+ Prints this help documentation.
92
+ `;
93
+
94
+ console.log();
95
+ console.log(
96
+ `${color.bold(color.cyan('options'))} can include any of these flags:`,
97
+ );
98
+
99
+ if (customOptions) {
100
+ console.log();
101
+ console.log(printOptionsSection(customOptions));
102
+ }
103
+
104
+ console.log();
105
+ console.log(printOptionsSection(optionsSection));
106
+ }
107
+
108
+ function printOptionsSection(section: string) {
109
+ return section
110
+ .split('\n')
111
+ .map((line) => (line.length > 0 ? ` ${line}` : line))
112
+ .join('\n');
113
+ }
114
+
115
+ function createCommand(explicitPackageManager?: string) {
116
+ let packageManager: string;
117
+
118
+ const npmUserAgent = process.env['npm_config_user_agent'] ?? 'npm';
119
+
120
+ if (npmUserAgent.includes('pnpm') || explicitPackageManager === 'pnpm') {
121
+ packageManager = 'pnpm';
122
+ } else if (
123
+ npmUserAgent.includes('yarn') ||
124
+ explicitPackageManager === 'yarn'
125
+ ) {
126
+ packageManager = 'yarn';
127
+ } else {
128
+ packageManager = 'npm';
129
+ }
130
+
131
+ return `${packageManager} create @quilted`;
132
+ }