create-gasket-app 7.0.0-cli.7 → 7.0.0-next.0
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/README.md +222 -2
- package/lib/commands/create.js +192 -0
- package/lib/config/default-plugins.js +7 -0
- package/lib/index.d.ts +205 -0
- package/lib/index.js +22 -9
- package/lib/main.js +19 -0
- package/lib/scaffold/action-wrapper.js +36 -0
- package/lib/scaffold/actions/apply-preset-config.js +29 -0
- package/lib/scaffold/actions/cli-version.js +84 -0
- package/lib/scaffold/actions/create-hooks.js +26 -0
- package/lib/scaffold/actions/generate-files.js +183 -0
- package/lib/scaffold/actions/global-prompts.js +161 -0
- package/lib/scaffold/actions/index.js +36 -0
- package/lib/scaffold/actions/install-modules.js +18 -0
- package/lib/scaffold/actions/link-modules.js +21 -0
- package/lib/scaffold/actions/load-pkg-for-debug.js +24 -0
- package/lib/scaffold/actions/load-preset.js +82 -0
- package/lib/scaffold/actions/mkdir.js +30 -0
- package/lib/scaffold/actions/post-create-hooks.js +33 -0
- package/lib/scaffold/actions/print-report.js +88 -0
- package/lib/scaffold/actions/prompt-hooks.js +90 -0
- package/lib/scaffold/actions/setup-pkg.js +47 -0
- package/lib/scaffold/actions/write-gasket-config.js +32 -0
- package/lib/scaffold/actions/write-pkg.js +23 -0
- package/lib/scaffold/config-builder.js +448 -0
- package/lib/scaffold/create-context.js +193 -0
- package/lib/scaffold/create-engine.js +21 -0
- package/lib/scaffold/dump-error-context.js +28 -0
- package/lib/scaffold/fetcher.js +194 -0
- package/lib/scaffold/files.js +30 -0
- package/lib/scaffold/utils.js +115 -0
- package/lib/utils/create-option.js +20 -0
- package/lib/utils/index.js +16 -0
- package/lib/utils/logo.js +14 -0
- package/lib/utils/process-args.js +39 -0
- package/lib/utils/process-command.js +52 -0
- package/lib/utils/process-options.js +46 -0
- package/lib/utils/setup.js +25 -0
- package/lib/utils/warn-if-outdated.js +76 -0
- package/package.json +23 -3
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const ora = require('ora');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Decorate a create action with spinner.
|
|
5
|
+
* If an action throws, a fail spinner will render for the step, regardless of
|
|
6
|
+
* if the spinner was started or not.
|
|
7
|
+
*
|
|
8
|
+
* @param {String} label - Label for the spinner
|
|
9
|
+
* @param {Function} fn - Action to wrap
|
|
10
|
+
* @param {Boolean} startSpinner - Should the spinner start
|
|
11
|
+
* @returns {Function} decorated action
|
|
12
|
+
*/
|
|
13
|
+
module.exports = function withSpinner(label, fn, { startSpinner = true } = {}) {
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Decorated function
|
|
17
|
+
*
|
|
18
|
+
* @param {CreateContext} context - Create context
|
|
19
|
+
* @returns {Promise} promise
|
|
20
|
+
*/
|
|
21
|
+
async function wrapper(context) {
|
|
22
|
+
const spinner = ora(label);
|
|
23
|
+
if (startSpinner) spinner.start();
|
|
24
|
+
try {
|
|
25
|
+
await fn(context, spinner);
|
|
26
|
+
if (spinner.isSpinning) spinner.succeed();
|
|
27
|
+
} catch (err) {
|
|
28
|
+
spinner.fail();
|
|
29
|
+
context.errors.push(err.stack);
|
|
30
|
+
throw err;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
wrapper.wrapped = fn;
|
|
35
|
+
return wrapper;
|
|
36
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const { flattenPresets } = require('@gasket/resolve');
|
|
2
|
+
const action = require('../action-wrapper');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Extracts predefined createContext from presets and applies to context.
|
|
6
|
+
* This allows presets to preemptively define fill in the create context.
|
|
7
|
+
* This will prevent prompts from appearing for the associated prompts.
|
|
8
|
+
*
|
|
9
|
+
* For example, `presetContext` contains `appDescription`, the prompt for the
|
|
10
|
+
* newly created app's description will not appear.
|
|
11
|
+
*
|
|
12
|
+
* We take whatever is in the context at a higher priority, because any
|
|
13
|
+
* config that is the context at this point must come from CLI flags,
|
|
14
|
+
* as none of the global prompts have run yet. Parent presets take priority
|
|
15
|
+
* over extended presets.
|
|
16
|
+
*
|
|
17
|
+
* @param {CreateContext} context - Create context
|
|
18
|
+
*/
|
|
19
|
+
function applyPresetConfig(context) {
|
|
20
|
+
const { presetInfos = [] } = context;
|
|
21
|
+
|
|
22
|
+
const presetContext = flattenPresets(presetInfos).reduce((acc, presetInfo) => {
|
|
23
|
+
return { ...(presetInfo.module && presetInfo.module.createContext || {}), ...acc };
|
|
24
|
+
}, context);
|
|
25
|
+
|
|
26
|
+
Object.assign(context, presetContext);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = action('Apply preset context', applyPresetConfig, { startSpinner: false });
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
const semver = require('semver');
|
|
2
|
+
const { flattenPresets } = require('@gasket/resolve');
|
|
3
|
+
const action = require('../action-wrapper');
|
|
4
|
+
const pkgVersion = require('../../../package.json').version;
|
|
5
|
+
const pkgVersionCompatible = `^${pkgVersion}`;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Helper to check if a module version is a file path
|
|
9
|
+
*
|
|
10
|
+
* @param {string} v - Version to check
|
|
11
|
+
* @returns {Boolean} result
|
|
12
|
+
*/
|
|
13
|
+
const isFile = v => v && v.includes('file:');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Determines the active cli version, and the version required to be installed for app.
|
|
17
|
+
*
|
|
18
|
+
* @param {CreateContext} context - Create context
|
|
19
|
+
* @param {Spinner} spinner - Spinner
|
|
20
|
+
*/
|
|
21
|
+
function resolveCliVersion(context, spinner) {
|
|
22
|
+
const { presetInfos, warnings } = context;
|
|
23
|
+
|
|
24
|
+
//
|
|
25
|
+
// Gather presets with cli dependencies
|
|
26
|
+
//
|
|
27
|
+
const cliPresets = flattenPresets(presetInfos)
|
|
28
|
+
.map(p => p.package)
|
|
29
|
+
.filter(p => p.dependencies && p.dependencies['@gasket/cli']);
|
|
30
|
+
|
|
31
|
+
//
|
|
32
|
+
// Find the preset with minimum cli version required.
|
|
33
|
+
// File paths are always preferred; for integration tests and local development
|
|
34
|
+
//
|
|
35
|
+
const minPreset = cliPresets.reduce((acc, cur) => {
|
|
36
|
+
const nextVersion = cur.dependencies['@gasket/cli'];
|
|
37
|
+
const prevVersion = acc && acc.dependencies['@gasket/cli'];
|
|
38
|
+
if (isFile(nextVersion)) return cur;
|
|
39
|
+
if (isFile(prevVersion)) return acc;
|
|
40
|
+
return !acc || semver.gt(semver.coerce(prevVersion), semver.coerce(nextVersion)) ? cur : acc;
|
|
41
|
+
}, null);
|
|
42
|
+
|
|
43
|
+
const requiredVersion = minPreset && minPreset.dependencies['@gasket/cli'];
|
|
44
|
+
|
|
45
|
+
//
|
|
46
|
+
// Issue warnings if determined cli version does not meet a preset's requirements
|
|
47
|
+
//
|
|
48
|
+
if (requiredVersion && !isFile(requiredVersion)) {
|
|
49
|
+
let hasWarning = false;
|
|
50
|
+
cliPresets.forEach(p => {
|
|
51
|
+
const v = p.dependencies['@gasket/cli'];
|
|
52
|
+
// installed version mismatch
|
|
53
|
+
if (!semver.satisfies(semver.coerce(v).version, requiredVersion)) {
|
|
54
|
+
warnings.push(
|
|
55
|
+
`Installed @gasket/cli@${requiredVersion} for ${minPreset.name}@${minPreset.version} ` +
|
|
56
|
+
`which does not satisfy version (${v}) ` +
|
|
57
|
+
`required by ${p.name}@${p.version}`
|
|
58
|
+
);
|
|
59
|
+
hasWarning = true;
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
//
|
|
64
|
+
// Issue warnings if globally installed version mismatch with app installed
|
|
65
|
+
//
|
|
66
|
+
if (!semver.satisfies(pkgVersion, requiredVersion)) {
|
|
67
|
+
warnings.push(
|
|
68
|
+
`Installed @gasket/cli@${requiredVersion} ` +
|
|
69
|
+
`which is not compatible with global version (${pkgVersion}) ` +
|
|
70
|
+
`used to execute \`gasket create\``
|
|
71
|
+
);
|
|
72
|
+
hasWarning = true;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (hasWarning) spinner.warn('CLI version mismatch');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
Object.assign(context, {
|
|
79
|
+
cliVersion: pkgVersion,
|
|
80
|
+
cliVersionRequired: requiredVersion || pkgVersionCompatible
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = action('Resolve CLI versions', resolveCliVersion, { startSpinner: false });
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const createEngine = require('../create-engine');
|
|
2
|
+
const action = require('../action-wrapper');
|
|
3
|
+
const ConfigBuilder = require('../config-builder');
|
|
4
|
+
const Files = require('../files');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Executes the `create` hook for all registered plugins.
|
|
8
|
+
* Adds `files` to context for plugins to add their files and templates.
|
|
9
|
+
*
|
|
10
|
+
* @param {CreateContext} context - Create context
|
|
11
|
+
* @returns {Promise} promise
|
|
12
|
+
*/
|
|
13
|
+
async function createHooks(context) {
|
|
14
|
+
const { dest, presets = [], plugins = [], warnings } = context;
|
|
15
|
+
|
|
16
|
+
const files = new Files();
|
|
17
|
+
const gasketConfig = ConfigBuilder.create({}, { orderBy: ['plugins'], warnings });
|
|
18
|
+
Object.assign(context, { files, gasketConfig });
|
|
19
|
+
|
|
20
|
+
const gasket = await createEngine({ dest, presets, plugins });
|
|
21
|
+
await gasket.execApply('create', async function applyCreate(plugin, handler) {
|
|
22
|
+
await handler(context.runWith(plugin));
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
module.exports = action('Execute create hooks', createHooks);
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/* eslint-disable max-statements */
|
|
2
|
+
const debug = require('diagnostics')('gasket:create-gasket-app:generate-files');
|
|
3
|
+
const fs = require('fs').promises;
|
|
4
|
+
const { promisify } = require('util');
|
|
5
|
+
const Handlebars = require('handlebars');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const mkdirp = require('mkdirp');
|
|
8
|
+
const { dim } = require('chalk');
|
|
9
|
+
const action = require('../action-wrapper');
|
|
10
|
+
|
|
11
|
+
const glob = promisify(require('glob'));
|
|
12
|
+
const flatten = (acc, values) => (acc || []).concat(values);
|
|
13
|
+
const reSep = /[/\\]+/;
|
|
14
|
+
const joinSep = pthArr => pthArr.join(path.sep);
|
|
15
|
+
const splitSep = pthStr => pthStr.split(reSep);
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Find all duplicate target files and reduce to single descriptor.
|
|
19
|
+
* Keeps track of overrides.
|
|
20
|
+
* Last in wins.
|
|
21
|
+
*
|
|
22
|
+
* @param {object[]} descriptors - Details about files to generate
|
|
23
|
+
* @returns {object[]} descriptors
|
|
24
|
+
*/
|
|
25
|
+
function reduceDescriptors(descriptors) {
|
|
26
|
+
const reduced = descriptors.reduce((acc, desc) => {
|
|
27
|
+
const { target } = desc;
|
|
28
|
+
if (acc.has(target)) {
|
|
29
|
+
const prev = acc.get(target);
|
|
30
|
+
if (prev.from !== desc.from) {
|
|
31
|
+
debug(`Using "${target}" from ${desc.from} instead of ${prev.from}`);
|
|
32
|
+
desc.overrides = prev.from;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
acc.set(target, desc);
|
|
36
|
+
return acc;
|
|
37
|
+
}, new Map());
|
|
38
|
+
|
|
39
|
+
return Array.from(reduced.values());
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Assemble the description objects from glob results
|
|
44
|
+
*
|
|
45
|
+
* @param {string} dest - app destination
|
|
46
|
+
* @param {string} from - Plugin source name
|
|
47
|
+
* @param {string} pattern - pattern to glob
|
|
48
|
+
* @param {string[]} srcPaths - resulting paths from glob
|
|
49
|
+
* @returns {object[]} descriptors
|
|
50
|
+
*/
|
|
51
|
+
function assembleDescriptors(dest, from, pattern, srcPaths) {
|
|
52
|
+
const output = joinSep(splitSep(dest));
|
|
53
|
+
const baseParts = splitSep(path.resolve(pattern.replace(/[/\\]+\.?\*.*$/, '')));
|
|
54
|
+
const base = joinSep(baseParts);
|
|
55
|
+
return srcPaths.map((srcPath) => {
|
|
56
|
+
const parts = splitSep(srcPath);
|
|
57
|
+
const srcFile = joinSep(parts);
|
|
58
|
+
const target = joinSep(parts.slice(baseParts.length)).replace('.template', '');
|
|
59
|
+
return {
|
|
60
|
+
pattern,
|
|
61
|
+
base,
|
|
62
|
+
srcFile,
|
|
63
|
+
targetFile: joinSep([output, target]),
|
|
64
|
+
target,
|
|
65
|
+
from
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Build a list of descriptions of all files we want to generate
|
|
72
|
+
*
|
|
73
|
+
* @param {CreateContext} context - Create context
|
|
74
|
+
* @returns {object[]} descriptors
|
|
75
|
+
*/
|
|
76
|
+
async function getDescriptors(context) {
|
|
77
|
+
const { dest, files } = context;
|
|
78
|
+
const descriptors = (
|
|
79
|
+
await Promise.all(files.globSets.map(async set => {
|
|
80
|
+
const { globs, source } = set;
|
|
81
|
+
const matches = await Promise.all(globs.map(async pattern => {
|
|
82
|
+
pattern = splitSep(pattern).join('/'); // Glob uses / for all OS's
|
|
83
|
+
const srcPaths = await glob(pattern, { nodir: true });
|
|
84
|
+
return assembleDescriptors(dest, source.name, pattern, srcPaths);
|
|
85
|
+
}));
|
|
86
|
+
return matches.reduce(flatten);
|
|
87
|
+
}))
|
|
88
|
+
).reduce(flatten);
|
|
89
|
+
|
|
90
|
+
return reduceDescriptors(descriptors);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Read file content, apply templating, then write out target file.
|
|
95
|
+
*
|
|
96
|
+
* @param {CreateContext} context - Create context
|
|
97
|
+
* @param {object[]} descriptors - Details about files to generate
|
|
98
|
+
* @returns {boolean} hasWarnings
|
|
99
|
+
*/
|
|
100
|
+
async function performGenerate(context, descriptors) {
|
|
101
|
+
const { warnings, errors, generatedFiles } = context;
|
|
102
|
+
let hasWarning = false;
|
|
103
|
+
let hasError = false;
|
|
104
|
+
|
|
105
|
+
const handlebars = Handlebars.create();
|
|
106
|
+
handlebars.registerHelper('json', JSON.stringify);
|
|
107
|
+
handlebars.registerHelper('jspretty', (data) => {
|
|
108
|
+
return JSON.stringify(data).replace(/"/g, '\'');
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
debug('descriptors', JSON.stringify(descriptors, null, 2));
|
|
112
|
+
|
|
113
|
+
await Promise.all(descriptors.map(async desc => {
|
|
114
|
+
const targetDir = path.dirname(desc.targetFile);
|
|
115
|
+
let content;
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
const source = await fs.readFile(desc.srcFile, { encoding: 'utf8' });
|
|
119
|
+
content = source;
|
|
120
|
+
try {
|
|
121
|
+
content = handlebars.compile(source)(context);
|
|
122
|
+
} catch (compileErr) {
|
|
123
|
+
hasWarning = true;
|
|
124
|
+
warnings.push(`Error templating ${desc.targetFile}: ${compileErr.message}`);
|
|
125
|
+
}
|
|
126
|
+
} catch (readErr) {
|
|
127
|
+
if (readErr.code === 'EISDIR') {
|
|
128
|
+
// Skipping directory errors.
|
|
129
|
+
// node-glob seems to incorrectly captures some directories on Windows
|
|
130
|
+
hasWarning = true;
|
|
131
|
+
warnings.push(`Directory matched as template file: ${desc.targetFile}`);
|
|
132
|
+
} else {
|
|
133
|
+
hasError = true;
|
|
134
|
+
errors.push(`Error reading template ${desc.srcFile}: ${readErr.message}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (!content) return;
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
await mkdirp(targetDir);
|
|
141
|
+
try {
|
|
142
|
+
await fs.writeFile(desc.targetFile, content, { encoding: 'utf8' });
|
|
143
|
+
const message = desc.target + (desc.overrides ? dim(` – from ${desc.from}`) : '');
|
|
144
|
+
generatedFiles.add(message);
|
|
145
|
+
} catch (writeErr) {
|
|
146
|
+
hasError = true;
|
|
147
|
+
errors.push(`Error writing ${desc.targetFile}: ${writeErr.message}`);
|
|
148
|
+
}
|
|
149
|
+
} catch (err) {
|
|
150
|
+
hasError = true;
|
|
151
|
+
errors.push(`Error creating directory ${targetDir}: ${err.message}`);
|
|
152
|
+
}
|
|
153
|
+
}));
|
|
154
|
+
|
|
155
|
+
return [hasWarning, hasError];
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Generate the app files and templates using context
|
|
160
|
+
*
|
|
161
|
+
* @param {CreateContext} context - Create context
|
|
162
|
+
* @param {Spinner} spinner - Spinner
|
|
163
|
+
* @returns {Promise} promise
|
|
164
|
+
*/
|
|
165
|
+
async function generateFiles(context, spinner) {
|
|
166
|
+
const { files } = context;
|
|
167
|
+
|
|
168
|
+
if (!files.globSets.length) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
let descriptors = await getDescriptors(context);
|
|
173
|
+
descriptors = reduceDescriptors(descriptors);
|
|
174
|
+
const [hasWarnings, hasError] = await performGenerate(context, descriptors);
|
|
175
|
+
|
|
176
|
+
if (hasWarnings) spinner.warn();
|
|
177
|
+
if (hasError) spinner.fail();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
module.exports = action('Generate app contents', generateFiles);
|
|
181
|
+
// exported for unit testing
|
|
182
|
+
module.exports._getDescriptors = getDescriptors;
|
|
183
|
+
module.exports._assembleDescriptors = assembleDescriptors;
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
const inquirer = require('inquirer');
|
|
2
|
+
const { pluginIdentifier, flattenPresets } = require('@gasket/resolve');
|
|
3
|
+
const action = require('../action-wrapper');
|
|
4
|
+
const { addPluginsToContext } = require('../utils');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* What is your app description?
|
|
8
|
+
*
|
|
9
|
+
* @param {CreateContext} context - Create context
|
|
10
|
+
* @param {function} prompt - function to prompt user
|
|
11
|
+
* @returns {Promise} promise
|
|
12
|
+
*/
|
|
13
|
+
async function chooseAppDescription(context, prompt) {
|
|
14
|
+
if (!('appDescription' in context)) {
|
|
15
|
+
const { appDescription } = await prompt([
|
|
16
|
+
{
|
|
17
|
+
name: 'appDescription',
|
|
18
|
+
message: 'What is your app description?',
|
|
19
|
+
type: 'input',
|
|
20
|
+
default: 'A basic gasket app'
|
|
21
|
+
}
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
Object.assign(context, { appDescription });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* What package manager do you want to use?
|
|
30
|
+
*
|
|
31
|
+
* @param {CreateContext} context - Create context
|
|
32
|
+
* @param {function} prompt - function to prompt user
|
|
33
|
+
* @returns {Promise} promise
|
|
34
|
+
*/
|
|
35
|
+
async function choosePackageManager(context, prompt) {
|
|
36
|
+
const packageManager =
|
|
37
|
+
context.packageManager ||
|
|
38
|
+
(
|
|
39
|
+
await prompt([
|
|
40
|
+
{
|
|
41
|
+
name: 'packageManager',
|
|
42
|
+
message: 'Which packager would you like to use?',
|
|
43
|
+
type: 'list',
|
|
44
|
+
choices: [{ name: 'npm' }, { name: 'yarn' }]
|
|
45
|
+
}
|
|
46
|
+
])
|
|
47
|
+
).packageManager;
|
|
48
|
+
|
|
49
|
+
const installCmd = context.installCmd || `${packageManager} install`;
|
|
50
|
+
|
|
51
|
+
const runners = {
|
|
52
|
+
npm: 'npx',
|
|
53
|
+
yarn: 'yarn'
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const localCmd = context.localCmd || `${runners[packageManager]} gasket local`;
|
|
57
|
+
|
|
58
|
+
Object.assign(context, {
|
|
59
|
+
packageManager,
|
|
60
|
+
installCmd,
|
|
61
|
+
localCmd
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Choose your unit test suite
|
|
67
|
+
*
|
|
68
|
+
* @param {CreateContext} context - Create context
|
|
69
|
+
* @param {function} prompt - function to prompt user
|
|
70
|
+
* @returns {Promise} promise
|
|
71
|
+
*/
|
|
72
|
+
async function chooseTestPlugin(context, prompt) {
|
|
73
|
+
// Combine user-provided plugins with preset-provided plugins.
|
|
74
|
+
const { presetInfos = [], plugins = [] } = context;
|
|
75
|
+
|
|
76
|
+
// Flatten all plugins from presets and concat short names with cli plugins
|
|
77
|
+
const allPlugins = flattenPresets(presetInfos)
|
|
78
|
+
.map((presetInfo) => presetInfo.plugins || [])
|
|
79
|
+
.reduce((acc, arr) => acc.concat(arr), [])
|
|
80
|
+
.map((pluginInfo) => pluginIdentifier(pluginInfo.name).shortName)
|
|
81
|
+
.concat(plugins);
|
|
82
|
+
|
|
83
|
+
const knownTestPlugins = { mocha: '@gasket/mocha', jest: '@gasket/jest', cypress: '@gasket/cypress' };
|
|
84
|
+
|
|
85
|
+
if (!('testPlugin' in context)) {
|
|
86
|
+
let testPlugin = Object.values(knownTestPlugins).find((p) => allPlugins.includes(p));
|
|
87
|
+
|
|
88
|
+
if ('testSuite' in context) {
|
|
89
|
+
testPlugin = knownTestPlugins[context.testSuite];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!testPlugin) {
|
|
93
|
+
({ testPlugin } = await prompt([
|
|
94
|
+
{
|
|
95
|
+
name: 'testPlugin',
|
|
96
|
+
message: 'Choose your unit test suite',
|
|
97
|
+
type: 'list',
|
|
98
|
+
choices: [
|
|
99
|
+
{ name: 'none (not recommended)', value: 'none' },
|
|
100
|
+
{ name: 'mocha + nyc + sinon + chai', value: '@gasket/mocha' },
|
|
101
|
+
{ name: 'jest', value: '@gasket/jest' },
|
|
102
|
+
{ name: 'cypress', value: '@gasket/cypress' }
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
]));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (testPlugin && testPlugin !== 'none') {
|
|
109
|
+
addPluginsToContext([testPlugin], context);
|
|
110
|
+
Object.assign(context, { testPlugin });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Given that gasket is creating in an already existing directory, it should
|
|
117
|
+
* confirm with the user that it's intentionally overwriting that directory
|
|
118
|
+
*
|
|
119
|
+
* @param {CreateContext} context - Create context
|
|
120
|
+
* @param {function} prompt - function to prompt user
|
|
121
|
+
* @returns {Promise} promise
|
|
122
|
+
*/
|
|
123
|
+
async function allowExtantOverwriting(context, prompt) {
|
|
124
|
+
const { dest, extant } = context;
|
|
125
|
+
if (extant && !('destOverride' in context)) {
|
|
126
|
+
const { destOverride } = await prompt([
|
|
127
|
+
{
|
|
128
|
+
name: 'destOverride',
|
|
129
|
+
type: 'confirm',
|
|
130
|
+
message: `Override contents of ${dest} ?`,
|
|
131
|
+
default: true
|
|
132
|
+
}
|
|
133
|
+
]);
|
|
134
|
+
|
|
135
|
+
Object.assign(context, { destOverride });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const questions = [
|
|
140
|
+
chooseAppDescription,
|
|
141
|
+
choosePackageManager,
|
|
142
|
+
chooseTestPlugin,
|
|
143
|
+
allowExtantOverwriting
|
|
144
|
+
];
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Fire off prompts for user input
|
|
148
|
+
*
|
|
149
|
+
* @param {CreateContext} context - Create context
|
|
150
|
+
* @returns {Promise} promise
|
|
151
|
+
*/
|
|
152
|
+
async function globalPrompts(context) {
|
|
153
|
+
const prompt = context.prompts ? inquirer.prompt : () => ({});
|
|
154
|
+
for (var fn of questions) {
|
|
155
|
+
await fn(context, prompt);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
module.exports = action('Global prompts', globalPrompts, { startSpinner: false });
|
|
160
|
+
|
|
161
|
+
module.exports.questions = questions;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
|
|
2
|
+
const mkDir = require('./mkdir');
|
|
3
|
+
const loadPreset = require('./load-preset');
|
|
4
|
+
const cliVersion = require('./cli-version');
|
|
5
|
+
const globalPrompts = require('./global-prompts');
|
|
6
|
+
const setupPkg = require('./setup-pkg');
|
|
7
|
+
const writePkg = require('./write-pkg');
|
|
8
|
+
const installModules = require('./install-modules');
|
|
9
|
+
const linkModules = require('./link-modules');
|
|
10
|
+
const writeGasketConfig = require('./write-gasket-config');
|
|
11
|
+
const loadPkgForDebug = require('./load-pkg-for-debug');
|
|
12
|
+
const promptHooks = require('./prompt-hooks');
|
|
13
|
+
const createHooks = require('./create-hooks');
|
|
14
|
+
const generateFiles = require('./generate-files');
|
|
15
|
+
const postCreateHooks = require('./post-create-hooks');
|
|
16
|
+
const applyPresetConfig = require('./apply-preset-config');
|
|
17
|
+
const printReport = require('./print-report');
|
|
18
|
+
|
|
19
|
+
module.exports = {
|
|
20
|
+
mkDir,
|
|
21
|
+
loadPreset,
|
|
22
|
+
cliVersion,
|
|
23
|
+
globalPrompts,
|
|
24
|
+
setupPkg,
|
|
25
|
+
writePkg,
|
|
26
|
+
installModules,
|
|
27
|
+
linkModules,
|
|
28
|
+
writeGasketConfig,
|
|
29
|
+
loadPkgForDebug,
|
|
30
|
+
promptHooks,
|
|
31
|
+
createHooks,
|
|
32
|
+
postCreateHooks,
|
|
33
|
+
generateFiles,
|
|
34
|
+
applyPresetConfig,
|
|
35
|
+
printReport
|
|
36
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const action = require('../action-wrapper');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Installs node_modules using the selected package manager
|
|
5
|
+
*
|
|
6
|
+
* @param {CreateContext} context - Create context
|
|
7
|
+
* @returns {Promise} promise
|
|
8
|
+
*/
|
|
9
|
+
async function installModules(context) {
|
|
10
|
+
const { pkgManager } = context;
|
|
11
|
+
|
|
12
|
+
await pkgManager.install();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
module.exports = action('Install node modules', installModules);
|
|
17
|
+
|
|
18
|
+
module.exports.update = action('Update node modules', installModules);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const action = require('../action-wrapper');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Links local packages using the selected package manager
|
|
5
|
+
*
|
|
6
|
+
* @param {CreateContext} context - Create context
|
|
7
|
+
* @param {Spinner} spinner - Spinner
|
|
8
|
+
* @returns {Promise} promise
|
|
9
|
+
*/
|
|
10
|
+
async function linkModules(context, spinner) {
|
|
11
|
+
const { pkgLinks, pkgManager } = context;
|
|
12
|
+
|
|
13
|
+
if (pkgLinks && pkgLinks.length) {
|
|
14
|
+
spinner.start();
|
|
15
|
+
await pkgManager.link(pkgLinks);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module.exports = action('Link node modules', linkModules, { startSpinner: false });
|
|
20
|
+
|
|
21
|
+
module.exports.update = action('Relink node modules', linkModules, { startSpinner: false });
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs').promises;
|
|
3
|
+
const action = require('../action-wrapper');
|
|
4
|
+
const ConfigBuilder = require('../config-builder');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Debug-only load existing app package.json for context.pkg
|
|
8
|
+
*
|
|
9
|
+
* @param {CreateContext} context - Create context
|
|
10
|
+
* @returns {Promise} promise
|
|
11
|
+
*/
|
|
12
|
+
async function loadPkForDebug(context) {
|
|
13
|
+
const { dest, warnings } = context;
|
|
14
|
+
|
|
15
|
+
const filePath = path.join(dest, 'package.json');
|
|
16
|
+
|
|
17
|
+
const contents = await fs.readFile(filePath, 'utf8');
|
|
18
|
+
const fields = JSON.parse(contents);
|
|
19
|
+
const pkg = ConfigBuilder.createPackageJson(fields, { warnings });
|
|
20
|
+
|
|
21
|
+
Object.assign(context, { pkg });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = action('Load package.json for debug. 🐲 Here be dragons!', loadPkForDebug);
|