@form8ion/javascript 13.0.0-beta.7 → 13.0.0-beta.9

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,1795 +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
- const vcsSchema = joi.object({
1166
- host: joi.string().required(),
1167
- owner: joi.string().required(),
1168
- name: joi.string().required()
1169
- });
1170
-
1171
- const scopeBasedConfigSchema = joi.object({scope: joi.string().regex(/^@[a-z0-9-]+$/i, 'scope').required()});
1172
-
1173
- const nameBasedConfigSchema = joi.object({
1174
- packageName: joi.string().required(),
1175
- name: joi.string().required()
1176
- });
1177
-
1178
- const registriesSchema = joi.object().pattern(joi.string(), joi.string().uri()).default({});
1179
-
1180
- const visibilitySchema = joi.string().valid('Public', 'Private').required();
1181
-
1182
- const projectNameSchema = joi.string().regex(/^@\w*\//, {invert: true}).required();
1183
-
1184
- function validate(options) {
1185
- const schema = joi.object({
1186
- projectRoot: joi.string().required(),
1187
- projectName: projectNameSchema,
1188
- visibility: visibilitySchema,
1189
- license: joi.string().required(),
1190
- description: joi.string(),
1191
- pathWithinParent: joi.string(),
1192
- decisions: joi.object(),
1193
- vcs: vcsSchema,
1194
- registries: registriesSchema,
1195
- configs: joi.object({
1196
- eslint: scopeBasedConfigSchema,
1197
- typescript: scopeBasedConfigSchema,
1198
- prettier: scopeBasedConfigSchema,
1199
- commitlint: nameBasedConfigSchema,
1200
- babelPreset: nameBasedConfigSchema,
1201
- remark: joi.string()
1202
- }).default({}),
1203
- plugins: {
1204
- unitTestFrameworks: pluginsSchema,
1205
- packageBundlers: pluginsSchema,
1206
- applicationTypes: pluginsSchema,
1207
- packageTypes: pluginsSchema,
1208
- monorepoTypes: pluginsSchema,
1209
- hosts: pluginsSchema,
1210
- ciServices: pluginsSchema
1211
- }
1212
- }).required();
1213
- // .keys({
1214
- // projectName: joi.string().regex(/^@\w*\//, {invert: true}).required(),
1215
- // })
1216
-
1217
- return validateOptions(schema, options);
1218
- }
1219
-
1220
- function buildDialectChoices ({babelPreset, typescript}) {
1221
- return [
1222
- {name: 'Common JS (no transpilation)', value: dialects$1.COMMON_JS, short: 'cjs'},
1223
- ...babelPreset ? [{name: 'Modern JavaScript (transpiled)', value: dialects$1.BABEL, short: 'modern'}] : [],
1224
- {name: 'ESM-only (no transpilation)', value: dialects$1.ESM, short: 'esm'},
1225
- ...typescript ? [{name: 'TypeScript', value: dialects$1.TYPESCRIPT, short: 'ts'}] : []
1226
- ];
1227
- }
1228
-
1229
- function projectIsCLI(answers) {
1230
- return projectTypes$1.CLI === answers[questionNames$1.PROJECT_TYPE];
1231
- }
1232
-
1233
- function projectIsPackage(answers) {
1234
- return projectTypes$1.PACKAGE === answers[questionNames$1.PROJECT_TYPE];
1235
- }
1236
-
1237
- function projectIsApplication(answers) {
1238
- return projectTypes$1.APPLICATION === answers[questionNames$1.PROJECT_TYPE];
1239
- }
1240
-
1241
- function packageShouldBeScoped(visibility, answers) {
1242
- return 'Private' === visibility || answers[questionNames$1.SHOULD_BE_SCOPED];
1243
- }
1244
-
1245
- function willBePublishedToNpm(answers) {
1246
- return projectIsPackage(answers) || projectIsCLI(answers);
1247
- }
1248
-
1249
- function shouldBeScopedPromptShouldBePresented(answers) {
1250
- return willBePublishedToNpm(answers);
1251
- }
1252
-
1253
- function scopePromptShouldBePresentedFactory(visibility) {
1254
- return answers => willBePublishedToNpm(answers) && packageShouldBeScoped(visibility, answers);
1255
- }
1256
-
1257
- function lintingPromptShouldBePresented({
1258
- [questionNames$2.UNIT_TESTS]: unitTested,
1259
- [questionNames$2.INTEGRATION_TESTS]: integrationTested
1260
- }) {
1261
- return !unitTested && !integrationTested;
1262
- }
1263
-
1264
- function scope(visibility) {
1265
- return input => {
1266
- if (!input && 'Private' === visibility) {
1267
- return 'Private packages must be scoped (https://docs.npmjs.com/private-modules/intro#setting-up-your-package)';
1268
- }
1269
-
1270
- return true;
1271
- };
1272
- }
1273
-
1274
- function authorQuestions({name, email, url}) {
1275
- return [
1276
- {
1277
- name: questionNames$1.AUTHOR_NAME,
1278
- message: 'What is the author\'s name?',
1279
- default: name
1280
- },
1281
- {
1282
- name: questionNames$1.AUTHOR_EMAIL,
1283
- message: 'What is the author\'s email?',
1284
- default: email
1285
- },
1286
- {
1287
- name: questionNames$1.AUTHOR_URL,
1288
- message: 'What is the author\'s website url?',
1289
- default: url
1290
- }
1291
- ];
1292
- }
1293
-
1294
- async function prompt(
1295
- ciServices,
1296
- hosts,
1297
- visibility,
1298
- vcs,
1299
- decisions,
1300
- configs,
1301
- pathWithinParent
1302
- ) {
1303
- const npmConf$1 = npmConf();
1304
-
1305
- let maybeLoggedInNpmUsername;
1306
- try {
1307
- maybeLoggedInNpmUsername = (await execa('npm', ['whoami'])).stdout;
1308
- } catch (failedExecutionResult) {
1309
- if (!decisions[questionNames$1.SCOPE]) {
1310
- warn('No logged in user found with `npm whoami`. Login with `npm login` '
1311
- + 'to use your npm account name as the package scope default.');
1312
- }
1313
- }
1314
-
1315
- const {
1316
- [questionNames$2.UNIT_TESTS]: unitTested,
1317
- [questionNames$2.INTEGRATION_TESTS]: integrationTested,
1318
- [questionNames$1.PROJECT_TYPE]: projectType,
1319
- [questionNames$2.CI_SERVICE]: ci,
1320
- [questionNames$1.HOST]: chosenHost,
1321
- [questionNames$1.SCOPE]: scope$1,
1322
- [questionNames$1.NODE_VERSION_CATEGORY]: nodeVersionCategory,
1323
- [questionNames$1.AUTHOR_NAME]: authorName,
1324
- [questionNames$1.AUTHOR_EMAIL]: authorEmail,
1325
- [questionNames$1.AUTHOR_URL]: authorUrl,
1326
- [questionNames$1.CONFIGURE_LINTING]: configureLinting,
1327
- [questionNames$1.PROVIDE_EXAMPLE]: provideExample,
1328
- [questionNames$1.PACKAGE_MANAGER]: packageManager,
1329
- [questionNames$1.DIALECT]: dialect
1330
- } = await prompt$1([
1331
- {
1332
- name: questionNames$1.DIALECT,
1333
- message: 'Which JavaScript dialect should this project follow?',
1334
- type: 'list',
1335
- choices: buildDialectChoices(configs),
1336
- default: 'babel'
1337
- },
1338
- ...pathWithinParent ? [] : [{
1339
- name: questionNames$1.NODE_VERSION_CATEGORY,
1340
- message: 'What node.js version should be used?',
1341
- type: 'list',
1342
- choices: ['LTS', 'Latest'],
1343
- default: 'LTS'
1344
- }],
1345
- {
1346
- name: questionNames$1.PACKAGE_MANAGER,
1347
- message: 'Which package manager will be used with this project?',
1348
- type: 'list',
1349
- choices: Object.values(packageManagers$1),
1350
- default: packageManagers$1.NPM
1351
- },
1352
- {
1353
- name: questionNames$1.PROJECT_TYPE,
1354
- message: 'What type of JavaScript project is this?',
1355
- type: 'list',
1356
- choices: [...Object.values(projectTypes$1), new Separator(), 'Other'],
1357
- default: projectTypes$1.PACKAGE
1358
- },
1359
- ...'Private' === visibility ? [] : [{
1360
- name: questionNames$1.SHOULD_BE_SCOPED,
1361
- message: 'Should this package be scoped?',
1362
- type: 'confirm',
1363
- when: shouldBeScopedPromptShouldBePresented,
1364
- default: true
1365
- }],
1366
- {
1367
- name: questionNames$1.SCOPE,
1368
- message: 'What is the scope?',
1369
- when: scopePromptShouldBePresentedFactory(visibility),
1370
- validate: scope(visibility),
1371
- default: maybeLoggedInNpmUsername
1372
- },
1373
- ...authorQuestions({
1374
- name: npmConf$1.get('init.author.name'),
1375
- email: npmConf$1.get('init.author.email'),
1376
- url: npmConf$1.get('init.author.url')
1377
- }),
1378
- ...questions(({vcs, ciServices, pathWithinParent})),
1379
- {
1380
- name: questionNames$1.CONFIGURE_LINTING,
1381
- message: 'Will there be source code that should be linted?',
1382
- type: 'confirm',
1383
- when: lintingPromptShouldBePresented
1384
- },
1385
- {
1386
- name: questionNames$1.PROVIDE_EXAMPLE,
1387
- message: 'Should an example be provided in the README?',
1388
- type: 'confirm',
1389
- when: projectIsPackage
1390
- },
1391
- {
1392
- name: questionNames$1.HOST,
1393
- type: 'list',
1394
- message: 'Where will the application be hosted?',
1395
- when: projectIsApplication,
1396
- choices: [...Object.keys(hosts), new Separator(), 'Other']
1397
- }
1398
- ], decisions);
1399
-
1400
- return {
1401
- tests: {unit: unitTested, integration: integrationTested},
1402
- projectType,
1403
- ci,
1404
- chosenHost,
1405
- scope: scope$1,
1406
- nodeVersionCategory,
1407
- author: {name: authorName, email: authorEmail, url: authorUrl},
1408
- configureLinting: false !== configureLinting,
1409
- provideExample,
1410
- packageManager,
1411
- dialect
1412
- };
1413
- }
1414
-
1415
- function scaffoldDocumentation ({projectTypeResults, packageManager}) {
1416
- return {
1417
- toc: `Run \`${buildDocumentationCommand(packageManager)}\` to generate a table of contents`,
1418
- ...projectTypeResults.documentation,
1419
- contributing: `### Dependencies
1420
-
1421
- \`\`\`sh
1422
- $ nvm install
1423
- $ ${packageManager} install
1424
- \`\`\`
1425
-
1426
- ### Verification
1427
-
1428
- \`\`\`sh
1429
- $ ${packageManager} test
1430
- \`\`\``
1431
- };
1432
- }
1433
-
1434
- async function determineLatestVersionOf(nodeVersionCategory) {
1435
- info('Determining version of node', {level: 'secondary'});
1436
-
1437
- const {stdout: nvmLsOutput} = await execa(
1438
- `. ~/.nvm/nvm.sh && nvm ls-remote${('LTS' === nodeVersionCategory) ? ' --lts' : ''}`,
1439
- {shell: true}
1440
- );
1441
-
1442
- const lsLines = nvmLsOutput.split('\n');
1443
- const lsLine = lsLines[lsLines.length - 2];
1444
-
1445
- return lsLine.match(/(v[0-9]+)\.[0-9]+\.[0-9]+/)[1];
1446
- }
1447
-
1448
- function install(nodeVersionCategory) {
1449
- info(`Installing ${nodeVersionCategory} version of node using nvm`, {level: 'secondary'});
1450
-
1451
- const subprocess = execa('. ~/.nvm/nvm.sh && nvm install', {shell: true});
1452
- subprocess.stdout.pipe(process.stdout);
1453
- return subprocess;
1454
- }
1455
-
1456
- async function scaffoldNodeVersion ({projectRoot, nodeVersionCategory}) {
1457
- if (!nodeVersionCategory) return undefined;
1458
-
1459
- const lowerCaseCategory = nodeVersionCategory.toLowerCase();
1460
- info(`Configuring ${lowerCaseCategory} version of node`);
1461
-
1462
- const version = await determineLatestVersionOf(nodeVersionCategory);
1463
-
1464
- await promises$1.writeFile(`${projectRoot}/.nvmrc`, version);
1465
-
1466
- await install(nodeVersionCategory);
1467
-
1468
- return version;
1469
- }
1470
-
1471
- function nvmIsUsed ({projectRoot}) {
1472
- return fileExists(`${projectRoot}/.nvmrc`);
1473
- }
1474
-
1475
- function buildBadgesDetails (contributors) {
1476
- return deepmerge.all(contributors).badges;
1477
- }
1478
-
1479
- function buildVcsIgnoreLists (vcsIgnoreLists = {}) {
1480
- return {
1481
- files: vcsIgnoreLists.files || [],
1482
- directories: ['/node_modules/', ...vcsIgnoreLists.directories || []]
1483
- };
1484
- }
1485
-
1486
- function buildPackageName (projectName, scope) {
1487
- const name = `${scope ? `@${scope}/` : ''}${projectName}`;
1488
-
1489
- const {validForNewPackages, errors} = validatePackageName(name);
1490
-
1491
- if (validForNewPackages) return name;
1492
- if (1 === errors.length && errors.includes('name cannot start with a period')) return projectName.slice(1);
1493
-
1494
- throw new Error(`The package name ${name} is invalid:${EOL}\t* ${errors.join(`${EOL}\t* `)}`);
1495
- }
1496
-
1497
- async function chooseProjectTypePlugin ({types, projectType, decisions}) {
1498
- if (!Object.keys(types).length) return 'Other';
1499
-
1500
- const answers = await prompt$1([{
1501
- name: questionNames$1.PROJECT_TYPE_CHOICE,
1502
- type: 'list',
1503
- message: `What type of ${projectType} is this?`,
1504
- choices: [...Object.keys(types), new Separator(), 'Other']
1505
- }], decisions);
1506
-
1507
- return answers[questionNames$1.PROJECT_TYPE_CHOICE];
1508
- }
1509
-
1510
- async function scaffoldProjectTypePlugin ({
1511
- projectRoot,
1512
- projectType,
1513
- projectName,
1514
- packageName,
1515
- packageManager,
1516
- scope,
1517
- dialect,
1518
- tests,
1519
- decisions,
1520
- plugins
1521
- }) {
1522
- const pluginsForProjectType = plugins[projectType];
1523
-
1524
- if (!pluginsForProjectType) return {};
1525
-
1526
- const chosenType = await chooseProjectTypePlugin({types: pluginsForProjectType, decisions, projectType});
1527
-
1528
- return scaffoldChoice(
1529
- pluginsForProjectType,
1530
- chosenType,
1531
- {projectRoot, projectName, packageName, packageManager, scope, tests, dialect}
1532
- );
1533
- }
1534
-
1535
- async function scaffoldTesting ({
1536
- projectRoot,
1537
- visibility,
1538
- tests: {unit, integration},
1539
- vcs,
1540
- unitTestFrameworks,
1541
- decisions,
1542
- dialect,
1543
- pathWithinParent
1544
- }) {
1545
- const unitResults = unit
1546
- ? await scaffoldUnitTesting({
1547
- projectRoot,
1548
- visibility,
1549
- vcs,
1550
- frameworks: unitTestFrameworks,
1551
- decisions,
1552
- dialect,
1553
- pathWithinParent
1554
- })
1555
- : {};
1556
-
1557
- return deepmerge({devDependencies: [...(unit || integration) ? ['@travi/any'] : []], eslint: {}}, unitResults);
1558
- }
1559
-
1560
- function buildAllowedHostsList ({packageManager, registries}) {
1561
- return [
1562
- ...(!registries || (registries && !registries.registry)) ? [packageManager] : [],
1563
- ...Object.values(Object.fromEntries(Object.entries(registries).filter(([scope]) => 'publish' !== scope)))
1564
- ];
1565
- }
1566
-
1567
- const lockfileLintSupportedPackageManagers = [packageManagers$1.NPM, packageManagers$1.YARN];
1568
-
1569
- function lockfileLintSupports(packageManager) {
1570
- return lockfileLintSupportedPackageManagers.includes(packageManager);
1571
- }
1572
-
1573
- async function scaffoldLockfileLint ({projectRoot, packageManager, registries}) {
1574
- if (!lockfileLintSupports(packageManager)) {
1575
- throw new Error(
1576
- `The ${packageManager} package manager is currently not supported by lockfile-lint. `
1577
- + `Only ${lockfileLintSupportedPackageManagers.join(' and ')} are currently supported.`
1578
- );
1579
- }
1580
-
1581
- await write$2({
1582
- name: 'lockfile-lint',
1583
- format: fileTypes.JSON,
1584
- path: projectRoot,
1585
- config: {
1586
- path: determineLockfilePathFor(packageManager),
1587
- type: packageManager,
1588
- 'validate-https': true,
1589
- 'allowed-hosts': buildAllowedHostsList({packageManager, registries})
1590
- }
1591
- });
1592
-
1593
- return {
1594
- devDependencies: ['lockfile-lint'],
1595
- scripts: {'lint:lockfile': 'lockfile-lint'}
1596
- };
1597
- }
1598
-
1599
- async function scaffoldLinting ({projectRoot, packageManager, registries}) {
1600
- return scaffoldLockfileLint({projectRoot, packageManager, registries});
1601
- }
1602
-
1603
- async function scaffoldVerification({
1604
- projectRoot,
1605
- dialect,
1606
- visibility,
1607
- packageManager,
1608
- vcs,
1609
- registries,
1610
- tests,
1611
- unitTestFrameworks,
1612
- decisions,
1613
- pathWithinParent
1614
- }) {
1615
- const [testingResults, lintingResults, huskyResults] = await Promise.all([
1616
- scaffoldTesting({
1617
- projectRoot,
1618
- tests,
1619
- visibility,
1620
- vcs,
1621
- unitTestFrameworks,
1622
- decisions,
1623
- dialect,
1624
- pathWithinParent
1625
- }),
1626
- scaffoldLinting({projectRoot, packageManager, registries, vcs, pathWithinParent}),
1627
- scaffold$3({projectRoot, packageManager, pathWithinParent})
1628
- ]);
1629
-
1630
- return deepmerge.all([testingResults, lintingResults, huskyResults]);
1631
- }
1632
-
1633
- async function scaffolder (options) {
1634
- info('Initializing JavaScript project');
1635
-
1636
- const {
1637
- projectRoot,
1638
- projectName,
1639
- visibility,
1640
- license,
1641
- vcs,
1642
- description,
1643
- configs,
1644
- decisions,
1645
- pathWithinParent,
1646
- registries,
1647
- plugins: {
1648
- applicationTypes,
1649
- packageTypes,
1650
- monorepoTypes,
1651
- packageBundlers,
1652
- unitTestFrameworks,
1653
- hosts,
1654
- ciServices
1655
- }
1656
- } = validate(options);
1657
-
1658
- const {
1659
- tests,
1660
- projectType,
1661
- ci,
1662
- chosenHost,
1663
- scope,
1664
- nodeVersionCategory,
1665
- author,
1666
- configureLinting,
1667
- provideExample,
1668
- packageManager,
1669
- dialect
1670
- } = await prompt(ciServices, hosts, visibility, vcs, decisions, configs, pathWithinParent);
1671
-
1672
- info('Writing project files', {level: 'secondary'});
1673
-
1674
- const packageName = buildPackageName(projectName, scope);
1675
- await scaffoldPackage({
1676
- projectRoot,
1677
- dialect,
1678
- packageName,
1679
- license,
1680
- author,
1681
- description
1682
- });
1683
- const projectTypeResults = await scaffoldProjectType({
1684
- projectType,
1685
- projectRoot,
1686
- projectName,
1687
- packageName,
1688
- packageManager,
1689
- visibility,
1690
- applicationTypes,
1691
- packageTypes,
1692
- packageBundlers,
1693
- monorepoTypes,
1694
- scope,
1695
- tests,
1696
- vcs,
1697
- decisions,
1698
- dialect,
1699
- provideExample,
1700
- publishRegistry: registries.publish
1701
- });
1702
- const verificationResults = await scaffoldVerification({
1703
- projectRoot,
1704
- dialect,
1705
- visibility,
1706
- packageManager,
1707
- vcs,
1708
- registries,
1709
- tests,
1710
- unitTestFrameworks,
1711
- decisions,
1712
- pathWithinParent
1713
- });
1714
- const [nodeVersion, npmResults, dialectResults, codeStyleResults] = await Promise.all([
1715
- scaffoldNodeVersion({projectRoot, nodeVersionCategory}),
1716
- scaffoldNpmConfig({projectType, projectRoot, registries}),
1717
- scaffoldDialect({
1718
- dialect,
1719
- configs,
1720
- projectRoot,
1721
- projectType,
1722
- testFilenamePattern: verificationResults.testFilenamePattern
1723
- }),
1724
- scaffoldCodeStyle({projectRoot, projectType, configs, vcs, configureLinting})
1725
- ]);
1726
- const projectTypePluginResults = await scaffoldProjectTypePlugin({
1727
- projectRoot,
1728
- projectType,
1729
- projectName,
1730
- packageName,
1731
- packageManager,
1732
- scope,
1733
- dialect,
1734
- tests,
1735
- decisions,
1736
- plugins: {
1737
- [projectTypes$1.PACKAGE]: packageTypes,
1738
- [projectTypes$1.APPLICATION]: applicationTypes,
1739
- [projectTypes$1.MONOREPO]: monorepoTypes
1740
- }
1741
- });
1742
- const mergedContributions = deepmerge.all([
1743
- ...(await Promise.all([
1744
- scaffoldChoice(
1745
- hosts,
1746
- chosenHost,
1747
- {buildDirectory: `./${projectTypeResults.buildDirectory}`, projectRoot, projectName, nodeVersion}
1748
- ),
1749
- scaffoldChoice(ciServices, ci, {projectRoot, vcs, visibility, projectType, projectName, nodeVersion, tests}),
1750
- scaffold$4({projectRoot, projectType, configs, pathWithinParent})
1751
- ])),
1752
- projectTypeResults,
1753
- verificationResults,
1754
- codeStyleResults,
1755
- npmResults,
1756
- dialectResults,
1757
- projectTypePluginResults
1758
- ]);
1759
-
1760
- const liftResults = await lift({
1761
- results: deepmerge({devDependencies: ['npm-run-all2'], packageManager}, mergedContributions),
1762
- projectRoot,
1763
- configs,
1764
- vcs,
1765
- pathWithinParent
1766
- });
1767
-
1768
- return {
1769
- badges: buildBadgesDetails([mergedContributions, liftResults]),
1770
- documentation: scaffoldDocumentation({projectTypeResults, packageManager}),
1771
- tags: projectTypeResults.tags,
1772
- vcsIgnore: buildVcsIgnoreLists(mergedContributions.vcsIgnore),
1773
- verificationCommand: `${buildDocumentationCommand(packageManager)} && ${packageManager} test`,
1774
- projectDetails: {...liftResults.homepage && {homepage: liftResults.homepage}},
1775
- nextSteps: mergedContributions.nextSteps
1776
- };
1777
- }
1778
-
1779
- async function tester ({projectRoot}) {
1780
- const [nvmFound, jsPackageManagerFound] = await Promise.all([
1781
- nvmIsUsed({projectRoot}),
1782
- jsPackageManagerIsUsed({projectRoot})
1783
- ]);
1784
-
1785
- const jsProjectFound = nvmFound || jsPackageManagerFound;
1786
-
1787
- if (jsProjectFound) info('JavaScript Project Detected');
1788
-
1789
- return jsProjectFound;
1790
- }
1791
-
1792
- const questionNames = {...questionNames$2, ...questionNames$1};
1793
-
1794
- export { lift, questionNames, scaffolder as scaffold, scaffoldUnitTesting, tester as test };
1795
- //# sourceMappingURL=index.mjs.map