@form8ion/javascript 13.0.0-beta.1 → 13.0.0-beta.11

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