@form8ion/javascript 13.0.0-alpha.5 → 13.0.0-beta.10

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/lib/index.mjs DELETED
@@ -1,1792 +0,0 @@
1
- import { questionNames as questionNames$2, questions } from '@travi/language-scaffolder-prompts';
2
- import deepmerge from 'deepmerge';
3
- import { fileTypes, fileExists, optionsSchemas, validateOptions, applyEnhancers, writeConfigFile } from '@form8ion/core';
4
- import { scaffoldChoice, projectTypes as projectTypes$1, packageManagers as packageManagers$1, mergeIntoExistingPackageJson, dialects as dialects$1, writePackageJson, DEV_DEPENDENCY_TYPE, PROD_DEPENDENCY_TYPE } from '@form8ion/javascript-core';
5
- import { prompt as prompt$1, Separator } from '@form8ion/overridable-prompts';
6
- import joi from 'joi';
7
- import { scaffold, lift as lift$3 } from '@form8ion/codecov';
8
- import { write as write$2 } from '@form8ion/config-file';
9
- import { info, warn, error } from '@travi/cli-messages';
10
- import * as commitConventionPlugin from '@form8ion/commit-convention';
11
- import { scaffold as scaffold$4 } from '@form8ion/commit-convention';
12
- import execa from '@form8ion/execa-wrapper';
13
- import npmConf from 'npm-conf';
14
- import { EOL } from 'os';
15
- import validatePackageName from 'validate-npm-package-name';
16
- import { promises } from 'fs';
17
- import mustache from 'mustache';
18
- import mkdir from 'make-dir';
19
- import touch from 'touch';
20
- import camelcase from 'camelcase';
21
- import { resolve } from 'path';
22
- import filedirname from 'filedirname';
23
- import * as huskyPlugin from '@form8ion/husky';
24
- import { scaffold as scaffold$3 } from '@form8ion/husky';
25
- import { promises as promises$1 } from 'node:fs';
26
- import { stringify, parse } from 'ini';
27
- import { scaffold as scaffold$2 } from '@form8ion/prettier';
28
- import * as eslintPlugin from '@form8ion/eslint';
29
- import { scaffold as scaffold$1, test as test$1 } from '@form8ion/eslint';
30
- import sortProperties from 'sort-object-keys';
31
-
32
- const questionNames$1 = {
33
- UNIT_TEST_FRAMEWORK: 'unitTestFramework',
34
- NODE_VERSION_CATEGORY: 'nodeVersionCategory',
35
- PACKAGE_MANAGER: 'packageManager',
36
- PACKAGE_BUNDLER: 'packageBundler',
37
- PROJECT_TYPE: 'projectType',
38
- PROJECT_TYPE_CHOICE: 'projectTypeChoice',
39
- SHOULD_BE_SCOPED: 'shouldBeScoped',
40
- SCOPE: 'scope',
41
- AUTHOR_NAME: 'authorName',
42
- AUTHOR_EMAIL: 'authorEmail',
43
- AUTHOR_URL: 'authorUrl',
44
- HOST: 'host',
45
- CONFIGURE_LINTING: 'configureLint',
46
- PROVIDE_EXAMPLE: 'provideExample',
47
- DIALECT: 'dialect'
48
- };
49
-
50
- async function scaffoldC8 ({projectRoot}) {
51
- await write$2({
52
- name: 'c8',
53
- format: fileTypes.JSON,
54
- path: projectRoot,
55
- config: {
56
- reporter: ['lcov', 'text-summary', 'html'],
57
- exclude: ['src/**/*-test.js', 'test/', 'thirdparty-wrappers/', 'vendor/']
58
- }
59
- });
60
-
61
- return {
62
- devDependencies: ['cross-env', 'c8'],
63
- vcsIgnore: {files: [], directories: ['/coverage/']},
64
- eslint: {ignore: {directories: ['/coverage/']}}
65
- };
66
- }
67
-
68
- async function scaffoldCoverage ({projectRoot, vcs, visibility, pathWithinParent}) {
69
- return deepmerge(await scaffoldC8({projectRoot}), await scaffold({vcs, visibility, pathWithinParent}));
70
- }
71
-
72
- function nycIsConfigured ({projectRoot}) {
73
- return fileExists(`${projectRoot}/.nycrc`);
74
- }
75
-
76
- async function removeDependencies ({packageManager, dependencies}) {
77
- await execa(packageManager, ['remove', ...dependencies]);
78
- }
79
-
80
- async function removeNyc ({projectRoot, packageManager}) {
81
- await Promise.all([
82
- promises.unlink(`${projectRoot}/.nycrc`),
83
- promises.rm(`${projectRoot}/.nyc_output`, {recursive: true, force: true}),
84
- removeDependencies({packageManager, dependencies: ['nyc', '@istanbuljs/nyc-config-babel', 'babel-plugin-istanbul']})
85
- ]);
86
- }
87
-
88
- async function lift$2({projectRoot, packageManager, vcs}) {
89
- const codecovResults = await lift$3({projectRoot, packageManager, vcs});
90
-
91
- if (await nycIsConfigured({projectRoot})) {
92
- const [c8Results] = await Promise.all([
93
- scaffoldC8({projectRoot}),
94
- removeNyc({projectRoot, packageManager})
95
- ]);
96
-
97
- return deepmerge.all([
98
- c8Results,
99
- codecovResults,
100
- {
101
- scripts: {'test:unit': 'cross-env NODE_ENV=test c8 run-s test:unit:base'},
102
- nextSteps: [{
103
- summary: 'Remove use of `@istanbuljs/nyc-config-babel` from your babel config, if present,'
104
- + ' after the migration away from `nyc`'
105
- }]
106
- }
107
- ]);
108
- }
109
-
110
- return codecovResults;
111
- }
112
-
113
- function c8IsConfigured ({projectRoot}) {
114
- return fileExists(`${projectRoot}/.c8rc.json`);
115
- }
116
-
117
- async function tester$4 ({projectRoot}) {
118
- const [c8Exists, nycExists] = await Promise.all([c8IsConfigured({projectRoot}), nycIsConfigured({projectRoot})]);
119
-
120
- return c8Exists || nycExists;
121
- }
122
-
123
- var coveragePlugin = /*#__PURE__*/Object.freeze({
124
- __proto__: null,
125
- scaffold: scaffoldCoverage,
126
- lift: lift$2,
127
- test: tester$4
128
- });
129
-
130
- async function chooseFramework ({frameworks, decisions}) {
131
- if (!Object.keys(frameworks).length) return 'Other';
132
-
133
- const answers = await prompt$1([{
134
- name: questionNames$1.UNIT_TEST_FRAMEWORK,
135
- type: 'list',
136
- message: 'Which type of unit testing framework should be used?',
137
- choices: [...Object.keys(frameworks), new Separator(), 'Other']
138
- }], decisions);
139
-
140
- return answers[questionNames$1.UNIT_TEST_FRAMEWORK];
141
- }
142
-
143
- const pluginsSchema = joi.object().pattern(/^/, optionsSchemas.form8ionPlugin).default({});
144
-
145
- async function scaffoldUnitTesting ({projectRoot, frameworks, decisions, visibility, vcs, pathWithinParent, dialect}) {
146
- const validatedFrameworks = validateOptions(pluginsSchema, frameworks);
147
- const [framework, coverage] = await Promise.all([
148
- chooseFramework({frameworks: validatedFrameworks, decisions})
149
- .then(chosenFramework => scaffoldChoice(validatedFrameworks, chosenFramework, {projectRoot, dialect})),
150
- scaffoldCoverage({projectRoot, vcs, visibility, pathWithinParent})
151
- ]);
152
-
153
- return deepmerge.all([
154
- {scripts: {'test:unit': 'cross-env NODE_ENV=test c8 run-s test:unit:base'}},
155
- framework,
156
- coverage
157
- ]);
158
- }
159
-
160
- async function scaffoldRemark ({config, projectRoot, projectType, vcs}) {
161
- await write$2({
162
- format: fileTypes.JSON,
163
- path: projectRoot,
164
- name: 'remark',
165
- config: {
166
- settings: {
167
- listItemIndent: 'one',
168
- emphasis: '_',
169
- strong: '_',
170
- bullet: '*',
171
- incrementListMarker: false
172
- },
173
- plugins: [
174
- config,
175
- ['remark-toc', {tight: true}],
176
- ...projectTypes$1.PACKAGE === projectType ? [['remark-usage', {heading: 'example'}]] : [],
177
- ...!vcs ? [['validate-links', {repository: false}]] : []
178
- ]
179
- }
180
- });
181
-
182
- return deepmerge(
183
- {
184
- devDependencies: [config, 'remark-cli', 'remark-toc'],
185
- scripts: {
186
- 'lint:md': 'remark . --frail',
187
- 'generate:md': 'remark . --output'
188
- }
189
- },
190
- {...projectTypes$1.PACKAGE === projectType && {devDependencies: ['remark-usage']}}
191
- );
192
- }
193
-
194
- async function scaffoldCodeStyle ({
195
- projectRoot,
196
- projectType,
197
- configs,
198
- vcs,
199
- configureLinting
200
- }) {
201
- return deepmerge.all(await Promise.all([
202
- configs.eslint
203
- && configureLinting
204
- && scaffold$1({projectRoot, config: configs.eslint}),
205
- scaffoldRemark({
206
- projectRoot,
207
- projectType,
208
- vcs,
209
- config: configs.remark || '@form8ion/remark-lint-preset'
210
- }),
211
- scaffold$2({projectRoot, config: configs.prettier})
212
- ].filter(Boolean)));
213
- }
214
-
215
- function lifter$4 (options) {
216
- return applyEnhancers({options, enhancers: [eslintPlugin]});
217
- }
218
-
219
- function tester$3 (options) {
220
- return test$1(options);
221
- }
222
-
223
- var codeStylePlugin = /*#__PURE__*/Object.freeze({
224
- __proto__: null,
225
- scaffold: scaffoldCodeStyle,
226
- lift: lifter$4,
227
- test: tester$3
228
- });
229
-
230
- async function write$1 ({projectRoot, config}) {
231
- await promises$1.writeFile(`${projectRoot}/.npmrc`, stringify(config));
232
- }
233
-
234
- function projectWillNotBeConsumed(projectType) {
235
- return projectTypes$1.APPLICATION === projectType || projectTypes$1.CLI === projectType;
236
- }
237
-
238
- async function scaffoldNpmConfig ({
239
- projectRoot,
240
- projectType,
241
- registries
242
- }) {
243
- await write$1({
244
- projectRoot,
245
- config: {
246
- 'update-notifier': false,
247
- ...projectWillNotBeConsumed(projectType) && {'save-exact': true},
248
- ...Object.fromEntries(Object.entries(registries)
249
- .filter(([scope]) => 'publish' !== scope)
250
- .map(([scope, url]) => {
251
- if ('registry' === scope) return ['registry', url];
252
-
253
- return [`@${scope}:registry`, url];
254
- }))
255
- }
256
- });
257
-
258
- return {scripts: {'lint:peer': 'npm ls >/dev/null'}};
259
- }
260
-
261
- function tester$2 ({projectRoot}) {
262
- return fileExists(`${projectRoot}/.npmrc`);
263
- }
264
-
265
- async function lifter$3 ({projectRoot}) {
266
- const pathToConfig = `${projectRoot}/.npmrc`;
267
-
268
- const {
269
- provenance,
270
- 'engines-strict': enginesStrict,
271
- ...remainingProperties
272
- } = parse(await promises$1.readFile(pathToConfig, 'utf-8'));
273
-
274
- await promises$1.writeFile(pathToConfig, stringify(remainingProperties));
275
-
276
- return {};
277
- }
278
-
279
- var npmConfigPlugin = /*#__PURE__*/Object.freeze({
280
- __proto__: null,
281
- scaffold: scaffoldNpmConfig,
282
- test: tester$2,
283
- lift: lifter$3
284
- });
285
-
286
- async function test({projectRoot}) {
287
- const {engines} = JSON.parse(await promises.readFile(`${projectRoot}/package.json`, 'utf8'));
288
-
289
- return !!engines?.node;
290
- }
291
-
292
- async function lift$1({packageDetails: {name}}) {
293
- return {
294
- devDependencies: ['ls-engines'],
295
- scripts: {'lint:engines': 'ls-engines'},
296
- badges: {consumer: {node: {img: `https://img.shields.io/node/v/${name}?logo=node.js`, text: 'node'}}}
297
- };
298
- }
299
-
300
- var enginesEnhancer = /*#__PURE__*/Object.freeze({
301
- __proto__: null,
302
- test: test,
303
- lift: lift$1
304
- });
305
-
306
- function buildDocumentationCommand (packageManager) {
307
- if (packageManagers$1.NPM === packageManager) return 'npm run generate:md';
308
- if (packageManagers$1.YARN === packageManager) return 'yarn generate:md';
309
-
310
- throw new Error(
311
- `The ${packageManager} package manager is currently not supported. `
312
- + `Only ${Object.values(packageManagers$1).join(' and ')} are currently supported.`
313
- );
314
- }
315
-
316
- function getInstallationCommand(packageManager) {
317
- if (packageManagers$1.NPM === packageManager) return 'npm install';
318
- if (packageManagers$1.YARN === packageManager) return 'yarn add';
319
-
320
- throw new Error(
321
- `The ${packageManager} package manager is currently not supported. `
322
- + `Only ${Object.values(packageManagers$1).join(' and ')} are currently supported.`
323
- );
324
- }
325
-
326
- function scaffoldPackageDocumentation ({scope, packageName, packageManager, visibility, provideExample}) {
327
- return {
328
- usage: `### Installation
329
- ${'Private' === visibility ? `
330
- :warning: this is a private package, so you will need to use an npm token with
331
- access to private packages under \`@${scope}\`
332
- ` : ''
333
- }
334
- \`\`\`sh
335
- $ ${getInstallationCommand(packageManager)} ${packageName}
336
- \`\`\`${provideExample
337
- ? `
338
-
339
- ### Example
340
-
341
- run \`${buildDocumentationCommand(packageManager)}\` to inject the usage example`
342
- : ''
343
- }`
344
- };
345
- }
346
-
347
- function determinePackageAccessLevelFromProjectVisibility ({projectVisibility}) {
348
- return 'Public' === projectVisibility ? 'public' : 'restricted';
349
- }
350
-
351
- function defineBadges (packageName, accessLevel) {
352
- return {
353
- consumer: {
354
- ...'public' === accessLevel && {
355
- npm: {
356
- img: `https://img.shields.io/npm/v/${packageName}?logo=npm`,
357
- text: 'npm',
358
- link: `https://www.npmjs.com/package/${packageName}`
359
- }
360
- }
361
- },
362
- status: {}
363
- };
364
- }
365
-
366
- function enhanceSlsa ({provenance}) {
367
- if (provenance) {
368
- return {
369
- badges: {
370
- status: {
371
- slsa: {
372
- img: 'https://slsa.dev/images/gh-badge-level2.svg',
373
- url: 'https://slsa.dev',
374
- text: 'SLSA Level 2'
375
- }
376
- }
377
- }
378
- };
379
- }
380
-
381
- return {};
382
- }
383
-
384
- async function liftProvenance ({projectRoot, packageDetails}) {
385
- const {publishConfig: {access}} = packageDetails;
386
-
387
- if ('public' === access) {
388
- await mergeIntoExistingPackageJson({projectRoot, config: {publishConfig: {provenance: true}}});
389
-
390
- return enhanceSlsa({provenance: true});
391
- }
392
-
393
- return {};
394
- }
395
-
396
- async function liftPublishable ({projectRoot, packageDetails}) {
397
- const {name: packageName, publishConfig: {access: packageAccessLevel}} = packageDetails;
398
- const homepage = `https://npm.im/${packageName}`;
399
-
400
- await mergeIntoExistingPackageJson({projectRoot, config: {homepage}});
401
-
402
- return deepmerge(
403
- await liftProvenance({packageDetails, projectRoot}),
404
- {
405
- homepage,
406
- devDependencies: ['publint'],
407
- scripts: {'lint:publish': 'publint --strict'},
408
- badges: defineBadges(packageName, packageAccessLevel)
409
- }
410
- );
411
- }
412
-
413
- async function scaffoldPublishable ({packageName, packageAccessLevel}) {
414
- return {
415
- badges: await defineBadges(packageName, packageAccessLevel)
416
- };
417
- }
418
-
419
- async function chooseBundler ({bundlers, decisions}) {
420
- if (!Object.keys(bundlers).length) return 'Other';
421
-
422
- const answers = await prompt$1([{
423
- name: questionNames$1.PACKAGE_BUNDLER,
424
- type: 'list',
425
- message: 'Which bundler should be used?',
426
- choices: [...Object.keys(bundlers), 'Other']
427
- }], decisions);
428
-
429
- return answers[questionNames$1.PACKAGE_BUNDLER];
430
- }
431
-
432
- async function scaffoldBundler ({projectRoot, projectType, bundlers, dialect, decisions}) {
433
- const chosenBundler = await chooseBundler({bundlers, decisions});
434
-
435
- return scaffoldChoice(bundlers, chosenBundler, {projectRoot, projectType, dialect});
436
- }
437
-
438
- function determinePathToTemplateFile (fileName) {
439
- const [, __dirname] = filedirname();
440
-
441
- return resolve(__dirname, '..', 'templates', fileName);
442
- }
443
-
444
- const defaultBuildDirectory$2 = 'lib';
445
-
446
- async function createExample(projectRoot, projectName, dialect) {
447
- return promises.writeFile(
448
- `${projectRoot}/example.js`,
449
- mustache.render(
450
- await promises.readFile(determinePathToTemplateFile('example.mustache'), 'utf8'),
451
- {projectName: camelcase(projectName), esm: dialect === dialects$1.ESM}
452
- )
453
- );
454
- }
455
-
456
- async function buildDetailsForCommonJsProject({projectRoot, projectName, provideExample}) {
457
- await Promise.all([
458
- touch(`${projectRoot}/index.js`),
459
- provideExample
460
- ? promises.writeFile(`${projectRoot}/example.js`, `const ${camelcase(projectName)} = require('.');\n`)
461
- : Promise.resolve()
462
- ]);
463
-
464
- return {};
465
- }
466
-
467
- async function buildDetails ({
468
- projectRoot,
469
- projectName,
470
- visibility,
471
- packageName,
472
- packageBundlers,
473
- dialect,
474
- provideExample,
475
- decisions
476
- }) {
477
- if (dialects$1.COMMON_JS === dialect) return buildDetailsForCommonJsProject({projectRoot, projectName, provideExample});
478
-
479
- const pathToCreatedSrcDirectory = await mkdir(`${projectRoot}/src`);
480
- const [bundlerResults] = await Promise.all([
481
- scaffoldBundler({bundlers: packageBundlers, projectRoot, dialect, decisions, projectType: projectTypes$1.PACKAGE}),
482
- provideExample ? await createExample(projectRoot, projectName, dialect) : Promise.resolve,
483
- touch(`${pathToCreatedSrcDirectory}/index.js`)
484
- ]);
485
-
486
- return deepmerge(
487
- bundlerResults,
488
- {
489
- devDependencies: ['rimraf'],
490
- scripts: {
491
- clean: `rimraf ./${defaultBuildDirectory$2}`,
492
- prebuild: 'run-s clean',
493
- build: 'npm-run-all --print-label --parallel build:*',
494
- prepack: 'run-s build',
495
- ...provideExample && {'pregenerate:md': 'run-s build'}
496
- },
497
- vcsIgnore: {directories: [`/${defaultBuildDirectory$2}/`]},
498
- buildDirectory: defaultBuildDirectory$2,
499
- badges: {
500
- consumer: {
501
- ...'Public' === visibility && {
502
- runkit: {
503
- img: `https://badge.runkitcdn.com/${packageName}.svg`,
504
- text: `Try ${packageName} on RunKit`,
505
- link: `https://npm.runkit.com/${packageName}`
506
- }
507
- }
508
- }
509
- }
510
- }
511
- );
512
- }
513
-
514
- async function scaffoldPackageType ({
515
- projectRoot,
516
- projectName,
517
- packageName,
518
- packageManager,
519
- visibility,
520
- scope,
521
- packageBundlers,
522
- decisions,
523
- dialect,
524
- provideExample,
525
- publishRegistry
526
- }) {
527
- info('Scaffolding Package Details');
528
-
529
- const packageAccessLevel = determinePackageAccessLevelFromProjectVisibility({projectVisibility: visibility});
530
- const [detailsForBuild, publishableResults] = await Promise.all([
531
- buildDetails({
532
- projectRoot,
533
- projectName,
534
- packageBundlers,
535
- visibility,
536
- packageName,
537
- dialect,
538
- provideExample,
539
- decisions
540
- }),
541
- scaffoldPublishable({packageName, packageAccessLevel}),
542
- mergeIntoExistingPackageJson({
543
- projectRoot,
544
- config: {
545
- files: ['example.js', ...dialects$1.COMMON_JS === dialect ? ['index.js'] : ['lib/']],
546
- publishConfig: {
547
- access: packageAccessLevel,
548
- ...publishRegistry && {registry: publishRegistry}
549
- },
550
- sideEffects: false,
551
- ...'Public' === visibility && {runkitExampleFilename: './example.js'},
552
- ...dialects$1.BABEL === dialect && {
553
- main: './lib/index.js',
554
- module: './lib/index.mjs',
555
- exports: {
556
- module: './lib/index.mjs',
557
- require: './lib/index.js',
558
- import: './lib/index.mjs'
559
- }
560
- },
561
- ...dialects$1.ESM === dialect && {
562
- main: './lib/index.js',
563
- exports: './lib/index.js'
564
- },
565
- ...dialects$1.TYPESCRIPT === dialect && {
566
- main: './lib/index.js',
567
- module: './lib/index.mjs',
568
- types: './lib/index.d.ts',
569
- exports: {
570
- types: './lib/index.d.ts',
571
- require: './lib/index.js',
572
- import: './lib/index.mjs'
573
- }
574
- }
575
- }
576
- })
577
- ]);
578
-
579
- return deepmerge.all([
580
- publishableResults,
581
- {
582
- documentation: scaffoldPackageDocumentation({packageName, visibility, scope, packageManager, provideExample}),
583
- nextSteps: [
584
- {summary: 'Add the appropriate `save` flag to the installation instructions in the README'},
585
- {summary: 'Define supported node.js versions as `engines.node` in the `package.json` file'},
586
- {summary: 'Publish pre-release versions to npm until package is stable enough to publish v1.0.0'}
587
- ]
588
- },
589
- detailsForBuild
590
- ]);
591
- }
592
-
593
- function liftPackage$1 ({projectRoot, packageDetails}) {
594
- return liftPublishable({projectRoot, packageDetails});
595
- }
596
-
597
- async function isPackage ({packageDetails: {exports, publishConfig, bin}}) {
598
- return !!exports || (!!publishConfig && !bin);
599
- }
600
-
601
- const defaultBuildDirectory$1 = 'public';
602
-
603
- async function scaffoldApplicationType ({projectRoot}) {
604
- info('Scaffolding Application Details');
605
-
606
- await mergeIntoExistingPackageJson({projectRoot, config: {private: true}});
607
-
608
- const buildDirectory = defaultBuildDirectory$1;
609
-
610
- return {
611
- scripts: {
612
- clean: `rimraf ./${buildDirectory}`,
613
- start: `node ./${buildDirectory}/index.js`,
614
- prebuild: 'run-s clean'
615
- },
616
- dependencies: [],
617
- devDependencies: ['rimraf'],
618
- vcsIgnore: {files: ['.env'], directories: [`/${buildDirectory}/`]},
619
- buildDirectory,
620
- nextSteps: []
621
- };
622
- }
623
-
624
- function isApplication ({packageDetails}) {
625
- return !!packageDetails.private;
626
- }
627
-
628
- async function scaffoldMonorepoType ({projectRoot}) {
629
- info('Scaffolding Monorepo Details');
630
-
631
- await mergeIntoExistingPackageJson({projectRoot, config: {private: true}});
632
-
633
- return {
634
- nextSteps: [{
635
- summary: 'Add packages to your new monorepo',
636
- description: 'Leverage [@form8ion/add-package-to-monorepo](https://npm.im/@form8ion/add-package-to-monorepo)'
637
- + ' to scaffold new packages into your new monorepo'
638
- }]
639
- };
640
- }
641
-
642
- const defaultBuildDirectory = 'bin';
643
-
644
- async function scaffoldCliType ({
645
- packageName,
646
- visibility,
647
- projectRoot,
648
- dialect,
649
- publishRegistry,
650
- decisions,
651
- packageBundlers
652
- }) {
653
- const packageAccessLevel = determinePackageAccessLevelFromProjectVisibility({projectVisibility: visibility});
654
- const [bundlerResults, publishableResults] = await Promise.all([
655
- scaffoldBundler({bundlers: packageBundlers, projectRoot, dialect, decisions, projectType: projectTypes$1.CLI}),
656
- scaffoldPublishable({packageName, packageAccessLevel}),
657
- mergeIntoExistingPackageJson({
658
- projectRoot,
659
- config: {
660
- bin: {},
661
- files: [`${defaultBuildDirectory}/`],
662
- publishConfig: {
663
- access: packageAccessLevel,
664
- ...publishRegistry && {registry: publishRegistry}
665
- }
666
- }
667
- })
668
- ]);
669
-
670
- return deepmerge.all([
671
- publishableResults,
672
- bundlerResults,
673
- {
674
- scripts: {
675
- clean: `rimraf ./${defaultBuildDirectory}`,
676
- prebuild: 'run-s clean',
677
- build: 'npm-run-all --print-label --parallel build:*',
678
- prepack: 'run-s build'
679
- },
680
- dependencies: ['update-notifier'],
681
- devDependencies: ['rimraf'],
682
- vcsIgnore: {files: [], directories: [`/${defaultBuildDirectory}/`]},
683
- buildDirectory: defaultBuildDirectory,
684
- nextSteps: [{summary: 'Define supported node.js versions as `engines.node` in the `package.json` file'}]
685
- }
686
- ]);
687
- }
688
-
689
- async function isCli ({packageDetails: {bin}}) {
690
- return !!bin;
691
- }
692
-
693
- function liftCli ({projectRoot, packageDetails}) {
694
- return liftPublishable({projectRoot, packageDetails});
695
- }
696
-
697
- async function scaffoldProjectType ({
698
- projectType,
699
- projectRoot,
700
- projectName,
701
- packageName,
702
- packageManager,
703
- visibility,
704
- packageBundlers,
705
- scope,
706
- decisions,
707
- dialect,
708
- provideExample,
709
- publishRegistry
710
- }) {
711
- switch (projectType) {
712
- case projectTypes$1.PACKAGE:
713
- return scaffoldPackageType({
714
- projectRoot,
715
- projectName,
716
- packageName,
717
- packageManager,
718
- visibility,
719
- scope,
720
- packageBundlers,
721
- decisions,
722
- dialect,
723
- provideExample,
724
- publishRegistry
725
- });
726
- case projectTypes$1.APPLICATION:
727
- return scaffoldApplicationType({projectRoot});
728
- case projectTypes$1.CLI:
729
- return scaffoldCliType({
730
- packageName,
731
- visibility,
732
- projectRoot,
733
- dialect,
734
- publishRegistry,
735
- decisions,
736
- packageBundlers
737
- });
738
- case projectTypes$1.MONOREPO:
739
- return scaffoldMonorepoType({projectRoot});
740
- case 'Other':
741
- return {};
742
- default:
743
- throw new Error(`The project-type of ${projectType} is invalid`);
744
- }
745
- }
746
-
747
- async function tester$1 ({projectRoot, packageDetails}) {
748
- return await isPackage({projectRoot, packageDetails})
749
- || await isCli({projectRoot, packageDetails})
750
- || isApplication({projectRoot, packageDetails});
751
- }
752
-
753
- function vcsRepositoryHostedOnGithub(vcs) {
754
- return vcs && 'github' === vcs.host;
755
- }
756
-
757
- async function lifter$2 ({projectRoot, packageDetails, vcs}) {
758
- if (await isPackage({projectRoot, packageDetails})) return liftPackage$1({projectRoot, packageDetails});
759
- if (await isCli({projectRoot, packageDetails})) return liftCli({projectRoot, packageDetails});
760
-
761
- let homepage;
762
-
763
- if (vcsRepositoryHostedOnGithub(vcs)) {
764
- homepage = `https://github.com/${vcs.owner}/${vcs.name}#readme`;
765
-
766
- await mergeIntoExistingPackageJson({projectRoot, config: {homepage}});
767
- }
768
-
769
- return {homepage};
770
- }
771
-
772
- var projectTypes = /*#__PURE__*/Object.freeze({
773
- __proto__: null,
774
- scaffold: scaffoldProjectType,
775
- test: tester$1,
776
- lift: lifter$2
777
- });
778
-
779
- function write ({projectRoot, config}) {
780
- return write$2({path: projectRoot, name: 'babel', format: fileTypes.JSON, config});
781
- }
782
-
783
- async function addIgnore ({projectRoot, ignore}) {
784
- if (ignore) {
785
- const existingConfig = JSON.parse(await promises$1.readFile(`${projectRoot}/.babelrc.json`, 'utf-8'));
786
-
787
- await write({projectRoot, config: {...existingConfig, ignore: [`./${ignore}/`]}});
788
- }
789
- }
790
-
791
- async function scaffoldBabel ({projectRoot, preset}) {
792
- if (!preset) {
793
- throw new Error('No babel preset provided. Cannot configure babel transpilation');
794
- }
795
-
796
- await write({projectRoot, config: {presets: [preset.name]}});
797
-
798
- return {
799
- devDependencies: ['@babel/register', preset.packageName],
800
- eslint: {}
801
- };
802
- }
803
-
804
- async function lifter$1 ({results, projectRoot}) {
805
- await addIgnore({ignore: results.buildDirectory, projectRoot});
806
-
807
- return {};
808
- }
809
-
810
- function predicate ({projectRoot}) {
811
- return fileExists(`${projectRoot}/.babelrc.json`);
812
- }
813
-
814
- async function scaffoldTypescript ({config, projectType, projectRoot, testFilenamePattern}) {
815
- const shareableTsConfigPackage = `${config.scope}/tsconfig`;
816
-
817
- await writeConfigFile({
818
- path: projectRoot,
819
- name: 'tsconfig',
820
- format: fileTypes.JSON,
821
- config: {
822
- $schema: 'https://json.schemastore.org/tsconfig',
823
- extends: shareableTsConfigPackage,
824
- compilerOptions: {
825
- rootDir: 'src',
826
- ...projectTypes$1.PACKAGE === projectType && {
827
- outDir: 'lib',
828
- declaration: true
829
- }
830
- },
831
- include: ['src/**/*.ts'],
832
- ...testFilenamePattern && {exclude: [testFilenamePattern]}
833
- }
834
- });
835
-
836
- return {
837
- eslint: {configs: ['typescript']},
838
- devDependencies: ['typescript', shareableTsConfigPackage],
839
- vcsIgnore: {files: ['tsconfig.tsbuildinfo']}
840
- };
841
- }
842
-
843
- function scaffoldDialect ({dialect, projectType, projectRoot, configs, testFilenamePattern}) {
844
- switch (dialect) {
845
- case dialects$1.BABEL:
846
- return scaffoldBabel({preset: configs.babelPreset, projectRoot});
847
- case dialects$1.TYPESCRIPT:
848
- return scaffoldTypescript({config: configs.typescript, projectType, projectRoot, testFilenamePattern});
849
- default:
850
- return {};
851
- }
852
- }
853
-
854
- var dialects = /*#__PURE__*/Object.freeze({
855
- __proto__: null,
856
- scaffold: scaffoldDialect,
857
- test: predicate,
858
- lift: lifter$1
859
- });
860
-
861
- function buildPackageDetails ({
862
- packageName,
863
- dialect,
864
- license,
865
- author,
866
- description
867
- }) {
868
- return {
869
- name: packageName,
870
- description,
871
- license,
872
- type: dialects$1.ESM === dialect ? 'module' : 'commonjs',
873
- author: `${author.name}${author.email ? ` <${author.email}>` : ''}${author.url ? ` (${author.url})` : ''}`,
874
- scripts: {}
875
- };
876
- }
877
-
878
- async function scaffoldPackage ({
879
- projectRoot,
880
- dialect,
881
- packageName,
882
- license,
883
- author,
884
- description
885
- }) {
886
- info('Configuring package.json');
887
-
888
- const packageData = await buildPackageDetails({
889
- packageName,
890
- dialect,
891
- license,
892
- author,
893
- description
894
- });
895
-
896
- await writePackageJson({projectRoot, config: packageData});
897
-
898
- return {};
899
- }
900
-
901
- function sortPackageProperties (packageContents) {
902
- return sortProperties(
903
- packageContents,
904
- [
905
- 'name',
906
- 'description',
907
- 'license',
908
- 'version',
909
- 'private',
910
- 'type',
911
- 'engines',
912
- 'author',
913
- 'contributors',
914
- 'repository',
915
- 'bugs',
916
- 'homepage',
917
- 'funding',
918
- 'keywords',
919
- 'runkitExampleFilename',
920
- 'exports',
921
- 'bin',
922
- 'main',
923
- 'module',
924
- 'types',
925
- 'sideEffects',
926
- 'scripts',
927
- 'files',
928
- 'publishConfig',
929
- 'packageManager',
930
- 'config',
931
- 'dependencies',
932
- 'devDependencies',
933
- 'peerDependencies'
934
- ]
935
- );
936
- }
937
-
938
- function defineVcsHostDetails (vcs, pathWithinParent) {
939
- return vcs && 'github' === vcs.host && {
940
- repository: pathWithinParent
941
- ? {
942
- type: 'git',
943
- url: `https://github.com/${vcs.owner}/${vcs.name}.git`,
944
- directory: pathWithinParent
945
- }
946
- : `${vcs.owner}/${vcs.name}`,
947
- bugs: `https://github.com/${vcs.owner}/${vcs.name}/issues`
948
- };
949
- }
950
-
951
- const details = {
952
- [packageManagers$1.NPM]: {
953
- installationCommand: 'install',
954
- installationFlags: {
955
- [DEV_DEPENDENCY_TYPE]: `save-${DEV_DEPENDENCY_TYPE}`,
956
- [PROD_DEPENDENCY_TYPE]: `save-${PROD_DEPENDENCY_TYPE}`,
957
- exact: 'save-exact'
958
- }
959
- },
960
- [packageManagers$1.YARN]: {
961
- installationCommand: 'add',
962
- installationFlags: {
963
- [DEV_DEPENDENCY_TYPE]: DEV_DEPENDENCY_TYPE,
964
- [PROD_DEPENDENCY_TYPE]: PROD_DEPENDENCY_TYPE,
965
- exact: 'exact'
966
- }
967
- }
968
- };
969
-
970
- function getInstallationCommandFor(manager) {
971
- return details[manager].installationCommand;
972
- }
973
-
974
- function getDependencyTypeFlag(manager, type) {
975
- return details[manager].installationFlags[type];
976
- }
977
-
978
- function getExactFlag(manager) {
979
- return details[manager].installationFlags.exact;
980
- }
981
-
982
- async function install$1 (dependencies, dependenciesType, projectRoot, packageManager = packageManagers$1.NPM) {
983
- if (dependencies.length) {
984
- info(`Installing ${dependenciesType} dependencies`, {level: 'secondary'});
985
-
986
- await execa(
987
- `. ~/.nvm/nvm.sh && nvm use && ${packageManager} ${
988
- getInstallationCommandFor(packageManager)
989
- } ${[...new Set(dependencies)].join(' ')} --${getDependencyTypeFlag(packageManager, dependenciesType)}${
990
- DEV_DEPENDENCY_TYPE === dependenciesType ? ` --${getExactFlag(packageManager)}` : ''
991
- }`,
992
- {shell: true, cwd: projectRoot}
993
- );
994
- } else warn(`No ${dependenciesType} dependencies to install`);
995
- }
996
-
997
- function projectWillBeTested(scripts) {
998
- return Object.keys(scripts).find(scriptName => scriptName.startsWith('test:'));
999
- }
1000
-
1001
- function projectShouldBeBuiltForVerification(scripts) {
1002
- return 'run-s build' === scripts['pregenerate:md'];
1003
- }
1004
-
1005
- function updateTestScript (scripts) {
1006
- return {
1007
- ...scripts,
1008
- test: `npm-run-all --print-label${
1009
- projectShouldBeBuiltForVerification(scripts) ? ' build' : ''
1010
- } --parallel lint:*${
1011
- projectWillBeTested(scripts) ? ' --parallel test:*' : ''
1012
- }`
1013
- };
1014
- }
1015
-
1016
- function liftScripts ({existingScripts, scripts}) {
1017
- return updateTestScript({...existingScripts, ...scripts});
1018
- }
1019
-
1020
- async function liftPackage ({
1021
- projectRoot,
1022
- scripts,
1023
- tags,
1024
- dependencies,
1025
- devDependencies,
1026
- packageManager,
1027
- vcs,
1028
- pathWithinParent
1029
- }) {
1030
- if (scripts || tags) {
1031
- info('Updating `package.json`', {level: 'secondary'});
1032
-
1033
- const pathToPackageJson = `${projectRoot}/package.json`;
1034
-
1035
- const existingPackageJsonContents = JSON.parse(await promises.readFile(pathToPackageJson, 'utf8'));
1036
-
1037
- await writePackageJson({
1038
- projectRoot,
1039
- config: sortPackageProperties({
1040
- ...existingPackageJsonContents,
1041
- ...defineVcsHostDetails(vcs, pathWithinParent),
1042
- scripts: liftScripts({
1043
- existingScripts: existingPackageJsonContents.scripts,
1044
- scripts
1045
- }),
1046
- ...tags && {
1047
- keywords: existingPackageJsonContents.keywords ? [...existingPackageJsonContents.keywords, ...tags] : tags
1048
- }
1049
- })
1050
- });
1051
- }
1052
-
1053
- info('Installing dependencies');
1054
-
1055
- try {
1056
- await install$1(dependencies || [], PROD_DEPENDENCY_TYPE, projectRoot, packageManager);
1057
- await install$1([...devDependencies || []], DEV_DEPENDENCY_TYPE, projectRoot, packageManager);
1058
- } catch (e) {
1059
- error('Failed to install dependencies');
1060
- error(e, {level: 'secondary'});
1061
- }
1062
- }
1063
-
1064
- function determineLockfilePathFor (packageManager) {
1065
- const lockfilePaths = {
1066
- [packageManagers$1.NPM]: 'package-lock.json',
1067
- [packageManagers$1.YARN]: 'yarn.lock'
1068
- };
1069
-
1070
- return lockfilePaths[packageManager];
1071
- }
1072
-
1073
- function npmIsUsed ({projectRoot}) {
1074
- return fileExists(`${projectRoot}/${determineLockfilePathFor(packageManagers$1.NPM)}`);
1075
- }
1076
-
1077
- function yarnIsUsed ({projectRoot}) {
1078
- return fileExists(`${projectRoot}/${determineLockfilePathFor(packageManagers$1.YARN)}`);
1079
- }
1080
-
1081
- async function jsPackageManagerIsUsed ({projectRoot}) {
1082
- const [npmFound, yarnFound] = await Promise.all([
1083
- npmIsUsed({projectRoot}),
1084
- yarnIsUsed({projectRoot})
1085
- ]);
1086
-
1087
- return npmFound || yarnFound;
1088
- }
1089
-
1090
- async function liftCorepack () {
1091
- await execa('corepack', ['use', 'npm@latest']);
1092
- }
1093
-
1094
- async function lifter () {
1095
- await liftCorepack();
1096
-
1097
- return {};
1098
- }
1099
-
1100
- async function resolvePackageManager ({projectRoot, packageManager}) {
1101
- if (packageManager) return packageManager;
1102
-
1103
- if (await npmIsUsed({projectRoot})) {
1104
- return packageManagers$1.NPM;
1105
- }
1106
-
1107
- if (await yarnIsUsed({projectRoot})) {
1108
- return packageManagers$1.YARN;
1109
- }
1110
-
1111
- throw new Error('Package-manager could not be determined');
1112
- }
1113
-
1114
- var packageManagers = /*#__PURE__*/Object.freeze({
1115
- __proto__: null,
1116
- test: jsPackageManagerIsUsed,
1117
- lift: lifter,
1118
- determineCurrent: resolvePackageManager,
1119
- defineLockfilePath: determineLockfilePathFor
1120
- });
1121
-
1122
- async function lift ({projectRoot, vcs, results, pathWithinParent, enhancers = {}}) {
1123
- info('Lifting JavaScript-specific details');
1124
-
1125
- const {
1126
- scripts,
1127
- tags,
1128
- dependencies,
1129
- devDependencies,
1130
- packageManager: manager
1131
- } = results;
1132
-
1133
- const [packageManager, packageContents] = await Promise.all([
1134
- resolvePackageManager({projectRoot, packageManager: manager}),
1135
- promises$1.readFile(`${projectRoot}/package.json`, 'utf8')
1136
- ]);
1137
-
1138
- const enhancerResults = await applyEnhancers({
1139
- results,
1140
- enhancers: {
1141
- ...enhancers,
1142
- huskyPlugin,
1143
- enginesEnhancer,
1144
- coveragePlugin,
1145
- commitConventionPlugin,
1146
- dialects,
1147
- codeStylePlugin,
1148
- npmConfigPlugin,
1149
- projectTypes,
1150
- packageManagers
1151
- },
1152
- options: {packageManager, projectRoot, vcs, packageDetails: JSON.parse(packageContents)}
1153
- });
1154
-
1155
- await liftPackage(
1156
- deepmerge.all([
1157
- {projectRoot, scripts, tags, dependencies, devDependencies, packageManager, vcs, pathWithinParent},
1158
- enhancerResults
1159
- ])
1160
- );
1161
-
1162
- return enhancerResults;
1163
- }
1164
-
1165
- function validate(options) {
1166
- const schema = joi.object().required()
1167
- .keys({
1168
- projectRoot: joi.string().required(),
1169
- projectName: joi.string().regex(/^@\w*\//, {invert: true}).required(),
1170
- visibility: joi.string().valid('Public', 'Private').required(),
1171
- license: joi.string().required(),
1172
- description: joi.string(),
1173
- pathWithinParent: joi.string()
1174
- })
1175
- .keys({
1176
- vcs: joi.object({
1177
- host: joi.string().required(),
1178
- owner: joi.string().required(),
1179
- name: joi.string().required()
1180
- })
1181
- })
1182
- .keys({
1183
- configs: joi.object({
1184
- eslint: joi.object({scope: joi.string().regex(/^@[a-z0-9-]+$/i, 'scope').required()}),
1185
- typescript: joi.object({scope: joi.string().regex(/^@[a-z0-9-]+$/i, 'scope').required()}),
1186
- prettier: joi.object({scope: joi.string().regex(/^@[a-z0-9-]+$/i, 'scope').required()}),
1187
- commitlint: joi.object({
1188
- packageName: joi.string().required(),
1189
- name: joi.string().required()
1190
- }),
1191
- babelPreset: joi.object({
1192
- packageName: joi.string().required(),
1193
- name: joi.string().required()
1194
- }),
1195
- remark: joi.string()
1196
- }).default({})
1197
- })
1198
- .keys({
1199
- plugins: {
1200
- unitTestFrameworks: pluginsSchema,
1201
- packageBundlers: pluginsSchema,
1202
- applicationTypes: pluginsSchema,
1203
- packageTypes: pluginsSchema,
1204
- monorepoTypes: pluginsSchema,
1205
- hosts: pluginsSchema,
1206
- ciServices: pluginsSchema
1207
- }
1208
- })
1209
- .keys({
1210
- decisions: joi.object()
1211
- })
1212
- .keys({registries: joi.object().pattern(joi.string(), joi.string().uri()).default({})});
1213
-
1214
- return validateOptions(schema, options);
1215
- }
1216
-
1217
- function buildDialectChoices ({babelPreset, typescript}) {
1218
- return [
1219
- {name: 'Common JS (no transpilation)', value: dialects$1.COMMON_JS, short: 'cjs'},
1220
- ...babelPreset ? [{name: 'Modern JavaScript (transpiled)', value: dialects$1.BABEL, short: 'modern'}] : [],
1221
- {name: 'ESM-only (no transpilation)', value: dialects$1.ESM, short: 'esm'},
1222
- ...typescript ? [{name: 'TypeScript', value: dialects$1.TYPESCRIPT, short: 'ts'}] : []
1223
- ];
1224
- }
1225
-
1226
- function projectIsCLI(answers) {
1227
- return projectTypes$1.CLI === answers[questionNames$1.PROJECT_TYPE];
1228
- }
1229
-
1230
- function projectIsPackage(answers) {
1231
- return projectTypes$1.PACKAGE === answers[questionNames$1.PROJECT_TYPE];
1232
- }
1233
-
1234
- function projectIsApplication(answers) {
1235
- return projectTypes$1.APPLICATION === answers[questionNames$1.PROJECT_TYPE];
1236
- }
1237
-
1238
- function packageShouldBeScoped(visibility, answers) {
1239
- return 'Private' === visibility || answers[questionNames$1.SHOULD_BE_SCOPED];
1240
- }
1241
-
1242
- function willBePublishedToNpm(answers) {
1243
- return projectIsPackage(answers) || projectIsCLI(answers);
1244
- }
1245
-
1246
- function shouldBeScopedPromptShouldBePresented(answers) {
1247
- return willBePublishedToNpm(answers);
1248
- }
1249
-
1250
- function scopePromptShouldBePresentedFactory(visibility) {
1251
- return answers => willBePublishedToNpm(answers) && packageShouldBeScoped(visibility, answers);
1252
- }
1253
-
1254
- function lintingPromptShouldBePresented({
1255
- [questionNames$2.UNIT_TESTS]: unitTested,
1256
- [questionNames$2.INTEGRATION_TESTS]: integrationTested
1257
- }) {
1258
- return !unitTested && !integrationTested;
1259
- }
1260
-
1261
- function scope(visibility) {
1262
- return input => {
1263
- if (!input && 'Private' === visibility) {
1264
- return 'Private packages must be scoped (https://docs.npmjs.com/private-modules/intro#setting-up-your-package)';
1265
- }
1266
-
1267
- return true;
1268
- };
1269
- }
1270
-
1271
- function authorQuestions({name, email, url}) {
1272
- return [
1273
- {
1274
- name: questionNames$1.AUTHOR_NAME,
1275
- message: 'What is the author\'s name?',
1276
- default: name
1277
- },
1278
- {
1279
- name: questionNames$1.AUTHOR_EMAIL,
1280
- message: 'What is the author\'s email?',
1281
- default: email
1282
- },
1283
- {
1284
- name: questionNames$1.AUTHOR_URL,
1285
- message: 'What is the author\'s website url?',
1286
- default: url
1287
- }
1288
- ];
1289
- }
1290
-
1291
- async function prompt(
1292
- ciServices,
1293
- hosts,
1294
- visibility,
1295
- vcs,
1296
- decisions,
1297
- configs,
1298
- pathWithinParent
1299
- ) {
1300
- const npmConf$1 = npmConf();
1301
-
1302
- let maybeLoggedInNpmUsername;
1303
- try {
1304
- maybeLoggedInNpmUsername = (await execa('npm', ['whoami'])).stdout;
1305
- } catch (failedExecutionResult) {
1306
- if (!decisions[questionNames$1.SCOPE]) {
1307
- warn('No logged in user found with `npm whoami`. Login with `npm login` '
1308
- + 'to use your npm account name as the package scope default.');
1309
- }
1310
- }
1311
-
1312
- const {
1313
- [questionNames$2.UNIT_TESTS]: unitTested,
1314
- [questionNames$2.INTEGRATION_TESTS]: integrationTested,
1315
- [questionNames$1.PROJECT_TYPE]: projectType,
1316
- [questionNames$2.CI_SERVICE]: ci,
1317
- [questionNames$1.HOST]: chosenHost,
1318
- [questionNames$1.SCOPE]: scope$1,
1319
- [questionNames$1.NODE_VERSION_CATEGORY]: nodeVersionCategory,
1320
- [questionNames$1.AUTHOR_NAME]: authorName,
1321
- [questionNames$1.AUTHOR_EMAIL]: authorEmail,
1322
- [questionNames$1.AUTHOR_URL]: authorUrl,
1323
- [questionNames$1.CONFIGURE_LINTING]: configureLinting,
1324
- [questionNames$1.PROVIDE_EXAMPLE]: provideExample,
1325
- [questionNames$1.PACKAGE_MANAGER]: packageManager,
1326
- [questionNames$1.DIALECT]: dialect
1327
- } = await prompt$1([
1328
- {
1329
- name: questionNames$1.DIALECT,
1330
- message: 'Which JavaScript dialect should this project follow?',
1331
- type: 'list',
1332
- choices: buildDialectChoices(configs),
1333
- default: 'babel'
1334
- },
1335
- ...pathWithinParent ? [] : [{
1336
- name: questionNames$1.NODE_VERSION_CATEGORY,
1337
- message: 'What node.js version should be used?',
1338
- type: 'list',
1339
- choices: ['LTS', 'Latest'],
1340
- default: 'LTS'
1341
- }],
1342
- {
1343
- name: questionNames$1.PACKAGE_MANAGER,
1344
- message: 'Which package manager will be used with this project?',
1345
- type: 'list',
1346
- choices: Object.values(packageManagers$1),
1347
- default: packageManagers$1.NPM
1348
- },
1349
- {
1350
- name: questionNames$1.PROJECT_TYPE,
1351
- message: 'What type of JavaScript project is this?',
1352
- type: 'list',
1353
- choices: [...Object.values(projectTypes$1), new Separator(), 'Other'],
1354
- default: projectTypes$1.PACKAGE
1355
- },
1356
- ...'Private' === visibility ? [] : [{
1357
- name: questionNames$1.SHOULD_BE_SCOPED,
1358
- message: 'Should this package be scoped?',
1359
- type: 'confirm',
1360
- when: shouldBeScopedPromptShouldBePresented,
1361
- default: true
1362
- }],
1363
- {
1364
- name: questionNames$1.SCOPE,
1365
- message: 'What is the scope?',
1366
- when: scopePromptShouldBePresentedFactory(visibility),
1367
- validate: scope(visibility),
1368
- default: maybeLoggedInNpmUsername
1369
- },
1370
- ...authorQuestions({
1371
- name: npmConf$1.get('init.author.name'),
1372
- email: npmConf$1.get('init.author.email'),
1373
- url: npmConf$1.get('init.author.url')
1374
- }),
1375
- ...questions(({vcs, ciServices, pathWithinParent})),
1376
- {
1377
- name: questionNames$1.CONFIGURE_LINTING,
1378
- message: 'Will there be source code that should be linted?',
1379
- type: 'confirm',
1380
- when: lintingPromptShouldBePresented
1381
- },
1382
- {
1383
- name: questionNames$1.PROVIDE_EXAMPLE,
1384
- message: 'Should an example be provided in the README?',
1385
- type: 'confirm',
1386
- when: projectIsPackage
1387
- },
1388
- {
1389
- name: questionNames$1.HOST,
1390
- type: 'list',
1391
- message: 'Where will the application be hosted?',
1392
- when: projectIsApplication,
1393
- choices: [...Object.keys(hosts), new Separator(), 'Other']
1394
- }
1395
- ], decisions);
1396
-
1397
- return {
1398
- tests: {unit: unitTested, integration: integrationTested},
1399
- projectType,
1400
- ci,
1401
- chosenHost,
1402
- scope: scope$1,
1403
- nodeVersionCategory,
1404
- author: {name: authorName, email: authorEmail, url: authorUrl},
1405
- configureLinting: false !== configureLinting,
1406
- provideExample,
1407
- packageManager,
1408
- dialect
1409
- };
1410
- }
1411
-
1412
- function scaffoldDocumentation ({projectTypeResults, packageManager}) {
1413
- return {
1414
- toc: `Run \`${buildDocumentationCommand(packageManager)}\` to generate a table of contents`,
1415
- ...projectTypeResults.documentation,
1416
- contributing: `### Dependencies
1417
-
1418
- \`\`\`sh
1419
- $ nvm install
1420
- $ ${packageManager} install
1421
- \`\`\`
1422
-
1423
- ### Verification
1424
-
1425
- \`\`\`sh
1426
- $ ${packageManager} test
1427
- \`\`\``
1428
- };
1429
- }
1430
-
1431
- async function determineLatestVersionOf(nodeVersionCategory) {
1432
- info('Determining version of node', {level: 'secondary'});
1433
-
1434
- const {stdout: nvmLsOutput} = await execa(
1435
- `. ~/.nvm/nvm.sh && nvm ls-remote${('LTS' === nodeVersionCategory) ? ' --lts' : ''}`,
1436
- {shell: true}
1437
- );
1438
-
1439
- const lsLines = nvmLsOutput.split('\n');
1440
- const lsLine = lsLines[lsLines.length - 2];
1441
-
1442
- return lsLine.match(/(v[0-9]+)\.[0-9]+\.[0-9]+/)[1];
1443
- }
1444
-
1445
- function install(nodeVersionCategory) {
1446
- info(`Installing ${nodeVersionCategory} version of node using nvm`, {level: 'secondary'});
1447
-
1448
- const subprocess = execa('. ~/.nvm/nvm.sh && nvm install', {shell: true});
1449
- subprocess.stdout.pipe(process.stdout);
1450
- return subprocess;
1451
- }
1452
-
1453
- async function scaffoldNodeVersion ({projectRoot, nodeVersionCategory}) {
1454
- if (!nodeVersionCategory) return undefined;
1455
-
1456
- const lowerCaseCategory = nodeVersionCategory.toLowerCase();
1457
- info(`Configuring ${lowerCaseCategory} version of node`);
1458
-
1459
- const version = await determineLatestVersionOf(nodeVersionCategory);
1460
-
1461
- await promises$1.writeFile(`${projectRoot}/.nvmrc`, version);
1462
-
1463
- await install(nodeVersionCategory);
1464
-
1465
- return version;
1466
- }
1467
-
1468
- function nvmIsUsed ({projectRoot}) {
1469
- return fileExists(`${projectRoot}/.nvmrc`);
1470
- }
1471
-
1472
- function buildBadgesDetails (contributors) {
1473
- return deepmerge.all(contributors).badges;
1474
- }
1475
-
1476
- function buildVcsIgnoreLists (vcsIgnoreLists = {}) {
1477
- return {
1478
- files: vcsIgnoreLists.files || [],
1479
- directories: ['/node_modules/', ...vcsIgnoreLists.directories || []]
1480
- };
1481
- }
1482
-
1483
- function buildPackageName (projectName, scope) {
1484
- const name = `${scope ? `@${scope}/` : ''}${projectName}`;
1485
-
1486
- const {validForNewPackages, errors} = validatePackageName(name);
1487
-
1488
- if (validForNewPackages) return name;
1489
- if (1 === errors.length && errors.includes('name cannot start with a period')) return projectName.slice(1);
1490
-
1491
- throw new Error(`The package name ${name} is invalid:${EOL}\t* ${errors.join(`${EOL}\t* `)}`);
1492
- }
1493
-
1494
- async function chooseProjectTypePlugin ({types, projectType, decisions}) {
1495
- if (!Object.keys(types).length) return 'Other';
1496
-
1497
- const answers = await prompt$1([{
1498
- name: questionNames$1.PROJECT_TYPE_CHOICE,
1499
- type: 'list',
1500
- message: `What type of ${projectType} is this?`,
1501
- choices: [...Object.keys(types), new Separator(), 'Other']
1502
- }], decisions);
1503
-
1504
- return answers[questionNames$1.PROJECT_TYPE_CHOICE];
1505
- }
1506
-
1507
- async function scaffoldProjectTypePlugin ({
1508
- projectRoot,
1509
- projectType,
1510
- projectName,
1511
- packageName,
1512
- packageManager,
1513
- scope,
1514
- dialect,
1515
- tests,
1516
- decisions,
1517
- plugins
1518
- }) {
1519
- const pluginsForProjectType = plugins[projectType];
1520
-
1521
- if (!pluginsForProjectType) return {};
1522
-
1523
- const chosenType = await chooseProjectTypePlugin({types: pluginsForProjectType, decisions, projectType});
1524
-
1525
- return scaffoldChoice(
1526
- pluginsForProjectType,
1527
- chosenType,
1528
- {projectRoot, projectName, packageName, packageManager, scope, tests, dialect}
1529
- );
1530
- }
1531
-
1532
- async function scaffoldTesting ({
1533
- projectRoot,
1534
- visibility,
1535
- tests: {unit, integration},
1536
- vcs,
1537
- unitTestFrameworks,
1538
- decisions,
1539
- dialect,
1540
- pathWithinParent
1541
- }) {
1542
- const unitResults = unit
1543
- ? await scaffoldUnitTesting({
1544
- projectRoot,
1545
- visibility,
1546
- vcs,
1547
- frameworks: unitTestFrameworks,
1548
- decisions,
1549
- dialect,
1550
- pathWithinParent
1551
- })
1552
- : {};
1553
-
1554
- return deepmerge({devDependencies: [...(unit || integration) ? ['@travi/any'] : []], eslint: {}}, unitResults);
1555
- }
1556
-
1557
- function buildAllowedHostsList ({packageManager, registries}) {
1558
- return [
1559
- ...(!registries || (registries && !registries.registry)) ? [packageManager] : [],
1560
- ...Object.values(Object.fromEntries(Object.entries(registries).filter(([scope]) => 'publish' !== scope)))
1561
- ];
1562
- }
1563
-
1564
- const lockfileLintSupportedPackageManagers = [packageManagers$1.NPM, packageManagers$1.YARN];
1565
-
1566
- function lockfileLintSupports(packageManager) {
1567
- return lockfileLintSupportedPackageManagers.includes(packageManager);
1568
- }
1569
-
1570
- async function scaffoldLockfileLint ({projectRoot, packageManager, registries}) {
1571
- if (!lockfileLintSupports(packageManager)) {
1572
- throw new Error(
1573
- `The ${packageManager} package manager is currently not supported by lockfile-lint. `
1574
- + `Only ${lockfileLintSupportedPackageManagers.join(' and ')} are currently supported.`
1575
- );
1576
- }
1577
-
1578
- await write$2({
1579
- name: 'lockfile-lint',
1580
- format: fileTypes.JSON,
1581
- path: projectRoot,
1582
- config: {
1583
- path: determineLockfilePathFor(packageManager),
1584
- type: packageManager,
1585
- 'validate-https': true,
1586
- 'allowed-hosts': buildAllowedHostsList({packageManager, registries})
1587
- }
1588
- });
1589
-
1590
- return {
1591
- devDependencies: ['lockfile-lint'],
1592
- scripts: {'lint:lockfile': 'lockfile-lint'}
1593
- };
1594
- }
1595
-
1596
- async function scaffoldLinting ({projectRoot, packageManager, registries}) {
1597
- return scaffoldLockfileLint({projectRoot, packageManager, registries});
1598
- }
1599
-
1600
- async function scaffoldVerification({
1601
- projectRoot,
1602
- dialect,
1603
- visibility,
1604
- packageManager,
1605
- vcs,
1606
- registries,
1607
- tests,
1608
- unitTestFrameworks,
1609
- decisions,
1610
- pathWithinParent
1611
- }) {
1612
- const [testingResults, lintingResults, huskyResults] = await Promise.all([
1613
- scaffoldTesting({
1614
- projectRoot,
1615
- tests,
1616
- visibility,
1617
- vcs,
1618
- unitTestFrameworks,
1619
- decisions,
1620
- dialect,
1621
- pathWithinParent
1622
- }),
1623
- scaffoldLinting({projectRoot, packageManager, registries, vcs, pathWithinParent}),
1624
- scaffold$3({projectRoot, packageManager, pathWithinParent})
1625
- ]);
1626
-
1627
- return deepmerge.all([testingResults, lintingResults, huskyResults]);
1628
- }
1629
-
1630
- async function scaffolder (options) {
1631
- info('Initializing JavaScript project');
1632
-
1633
- const {
1634
- projectRoot,
1635
- projectName,
1636
- visibility,
1637
- license,
1638
- vcs,
1639
- description,
1640
- configs,
1641
- decisions,
1642
- pathWithinParent,
1643
- registries,
1644
- plugins: {
1645
- applicationTypes,
1646
- packageTypes,
1647
- monorepoTypes,
1648
- packageBundlers,
1649
- unitTestFrameworks,
1650
- hosts,
1651
- ciServices
1652
- }
1653
- } = validate(options);
1654
-
1655
- const {
1656
- tests,
1657
- projectType,
1658
- ci,
1659
- chosenHost,
1660
- scope,
1661
- nodeVersionCategory,
1662
- author,
1663
- configureLinting,
1664
- provideExample,
1665
- packageManager,
1666
- dialect
1667
- } = await prompt(ciServices, hosts, visibility, vcs, decisions, configs, pathWithinParent);
1668
-
1669
- info('Writing project files', {level: 'secondary'});
1670
-
1671
- const packageName = buildPackageName(projectName, scope);
1672
- await scaffoldPackage({
1673
- projectRoot,
1674
- dialect,
1675
- packageName,
1676
- license,
1677
- author,
1678
- description
1679
- });
1680
- const projectTypeResults = await scaffoldProjectType({
1681
- projectType,
1682
- projectRoot,
1683
- projectName,
1684
- packageName,
1685
- packageManager,
1686
- visibility,
1687
- applicationTypes,
1688
- packageTypes,
1689
- packageBundlers,
1690
- monorepoTypes,
1691
- scope,
1692
- tests,
1693
- vcs,
1694
- decisions,
1695
- dialect,
1696
- provideExample,
1697
- publishRegistry: registries.publish
1698
- });
1699
- const verificationResults = await scaffoldVerification({
1700
- projectRoot,
1701
- dialect,
1702
- visibility,
1703
- packageManager,
1704
- vcs,
1705
- registries,
1706
- tests,
1707
- unitTestFrameworks,
1708
- decisions,
1709
- pathWithinParent
1710
- });
1711
- const [nodeVersion, npmResults, dialectResults, codeStyleResults] = await Promise.all([
1712
- scaffoldNodeVersion({projectRoot, nodeVersionCategory}),
1713
- scaffoldNpmConfig({projectType, projectRoot, registries}),
1714
- scaffoldDialect({
1715
- dialect,
1716
- configs,
1717
- projectRoot,
1718
- projectType,
1719
- testFilenamePattern: verificationResults.testFilenamePattern
1720
- }),
1721
- scaffoldCodeStyle({projectRoot, projectType, configs, vcs, configureLinting})
1722
- ]);
1723
- const projectTypePluginResults = await scaffoldProjectTypePlugin({
1724
- projectRoot,
1725
- projectType,
1726
- projectName,
1727
- packageName,
1728
- packageManager,
1729
- scope,
1730
- dialect,
1731
- tests,
1732
- decisions,
1733
- plugins: {
1734
- [projectTypes$1.PACKAGE]: packageTypes,
1735
- [projectTypes$1.APPLICATION]: applicationTypes,
1736
- [projectTypes$1.MONOREPO]: monorepoTypes
1737
- }
1738
- });
1739
- const mergedContributions = deepmerge.all([
1740
- ...(await Promise.all([
1741
- scaffoldChoice(
1742
- hosts,
1743
- chosenHost,
1744
- {buildDirectory: `./${projectTypeResults.buildDirectory}`, projectRoot, projectName, nodeVersion}
1745
- ),
1746
- scaffoldChoice(ciServices, ci, {projectRoot, vcs, visibility, projectType, projectName, nodeVersion, tests}),
1747
- scaffold$4({projectRoot, projectType, configs, pathWithinParent})
1748
- ])),
1749
- projectTypeResults,
1750
- verificationResults,
1751
- codeStyleResults,
1752
- npmResults,
1753
- dialectResults,
1754
- projectTypePluginResults
1755
- ]);
1756
-
1757
- const liftResults = await lift({
1758
- results: deepmerge({devDependencies: ['npm-run-all2'], packageManager}, mergedContributions),
1759
- projectRoot,
1760
- configs,
1761
- vcs,
1762
- pathWithinParent
1763
- });
1764
-
1765
- return {
1766
- badges: buildBadgesDetails([mergedContributions, liftResults]),
1767
- documentation: scaffoldDocumentation({projectTypeResults, packageManager}),
1768
- tags: projectTypeResults.tags,
1769
- vcsIgnore: buildVcsIgnoreLists(mergedContributions.vcsIgnore),
1770
- verificationCommand: `${buildDocumentationCommand(packageManager)} && ${packageManager} test`,
1771
- projectDetails: {...liftResults.homepage && {homepage: liftResults.homepage}},
1772
- nextSteps: mergedContributions.nextSteps
1773
- };
1774
- }
1775
-
1776
- async function tester ({projectRoot}) {
1777
- const [nvmFound, jsPackageManagerFound] = await Promise.all([
1778
- nvmIsUsed({projectRoot}),
1779
- jsPackageManagerIsUsed({projectRoot})
1780
- ]);
1781
-
1782
- const jsProjectFound = nvmFound || jsPackageManagerFound;
1783
-
1784
- if (jsProjectFound) info('JavaScript Project Detected');
1785
-
1786
- return jsProjectFound;
1787
- }
1788
-
1789
- const questionNames = {...questionNames$2, ...questionNames$1};
1790
-
1791
- export { lift, questionNames, scaffolder as scaffold, scaffoldUnitTesting, tester as test };
1792
- //# sourceMappingURL=index.mjs.map