create-gasket-app 7.0.3-cli.0 → 7.2.0-canary.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.
Files changed (34) hide show
  1. package/README.md +222 -2
  2. package/lib/commands/create.js +148 -0
  3. package/lib/config/default-plugins.js +7 -0
  4. package/lib/index.d.ts +264 -0
  5. package/lib/index.js +24 -9
  6. package/lib/scaffold/action-wrapper.js +37 -0
  7. package/lib/scaffold/actions/create-hooks.js +25 -0
  8. package/lib/scaffold/actions/generate-files.js +185 -0
  9. package/lib/scaffold/actions/global-prompts.js +158 -0
  10. package/lib/scaffold/actions/index.js +33 -0
  11. package/lib/scaffold/actions/install-modules.js +15 -0
  12. package/lib/scaffold/actions/link-modules.js +19 -0
  13. package/lib/scaffold/actions/load-preset.js +60 -0
  14. package/lib/scaffold/actions/mkdir.js +30 -0
  15. package/lib/scaffold/actions/post-create-hooks.js +31 -0
  16. package/lib/scaffold/actions/preset-config-hooks.js +13 -0
  17. package/lib/scaffold/actions/preset-prompt-hooks.js +17 -0
  18. package/lib/scaffold/actions/print-report.js +89 -0
  19. package/lib/scaffold/actions/prompt-hooks.js +43 -0
  20. package/lib/scaffold/actions/setup-pkg.js +34 -0
  21. package/lib/scaffold/actions/write-gasket-config.js +117 -0
  22. package/lib/scaffold/actions/write-pkg.js +19 -0
  23. package/lib/scaffold/config-builder.js +523 -0
  24. package/lib/scaffold/create-context.js +187 -0
  25. package/lib/scaffold/dump-error-context.js +28 -0
  26. package/lib/scaffold/files.js +30 -0
  27. package/lib/scaffold/utils.js +22 -0
  28. package/lib/utils/create-option.js +18 -0
  29. package/lib/utils/index.js +5 -0
  30. package/lib/utils/logo.js +12 -0
  31. package/lib/utils/process-args.js +37 -0
  32. package/lib/utils/process-command.js +50 -0
  33. package/lib/utils/process-options.js +44 -0
  34. package/package.json +29 -7
@@ -0,0 +1,37 @@
1
+ import ora from '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
+ export default function withSpinner(label, fn, { startSpinner = true } = {}) {
14
+ /**
15
+ * Decorated function
16
+ *
17
+ * @param {ActionWrapperParams} params - ActionWrapperParams
18
+ * @param {GasketEngine} params.gasket - Gasket config
19
+ * @param {CreateContext} params.context - Create context
20
+ * @returns {Promise} promise
21
+ */
22
+ async function wrapper({ gasket = {}, context }) {
23
+ const spinner = ora(label);
24
+ if (startSpinner) spinner.start();
25
+ try {
26
+ await fn({ gasket, context, spinner });
27
+ if (spinner.isSpinning) spinner.succeed();
28
+ } catch (err) {
29
+ spinner.fail();
30
+ context.errors.push(err.stack);
31
+ throw err;
32
+ }
33
+ }
34
+
35
+ wrapper.wrapped = fn;
36
+ return wrapper;
37
+ }
@@ -0,0 +1,25 @@
1
+ import action from '../action-wrapper.js';
2
+ import { ConfigBuilder } from '../config-builder.js';
3
+ import { Files } from '../files.js';
4
+
5
+ /**
6
+ * Executes the `create` hook for all registered plugins.
7
+ * Adds `files` to context for plugins to add their files and templates.
8
+ *
9
+ * @param {GasketEngine} gasket - Gasket API
10
+ * @param {CreateContext} context - Create context
11
+ * @returns {Promise} promise
12
+ */
13
+ async function createHooks({ gasket, context }) {
14
+ const { plugins = [], warnings } = context;
15
+
16
+ const files = new Files();
17
+ const gasketConfig = ConfigBuilder.create({}, { orderBy: ['plugins'], warnings, plugins });
18
+ Object.assign(context, { files, gasketConfig });
19
+
20
+ await gasket.execApply('create', async function applyCreate(plugin, handler) {
21
+ await handler(context.runWith(plugin));
22
+ });
23
+ }
24
+
25
+ export default action('Execute create hooks', createHooks);
@@ -0,0 +1,185 @@
1
+ import { default as diagnostics } from 'diagnostics';
2
+ const debug = diagnostics('gasket:cli:generate-files');
3
+ import fs from 'fs/promises';
4
+ import { promisify } from 'util';
5
+ import Handlebars from 'handlebars';
6
+ import path from 'path';
7
+ import mkdirp from 'mkdirp';
8
+ import chalk from 'chalk';
9
+ const { dim } = chalk;
10
+ import action from '../action-wrapper.js';
11
+ import * as syncGlob from 'glob';
12
+ const glob = promisify(syncGlob.default);
13
+
14
+ const flatten = (acc, values) => (acc || []).concat(values);
15
+ const reSep = /[/\\]+/;
16
+ const joinSep = pthArr => pthArr.join(path.sep);
17
+ const splitSep = pthStr => pthStr.split(reSep);
18
+
19
+ /**
20
+ * Find all duplicate target files and reduce to single descriptor.
21
+ * Keeps track of overrides.
22
+ * Last in wins.
23
+ *
24
+ * @param {object[]} descriptors - Details about files to generate
25
+ * @returns {object[]} descriptors
26
+ */
27
+ function reduceDescriptors(descriptors) {
28
+ const reduced = descriptors.reduce((acc, desc) => {
29
+ const { target } = desc;
30
+ if (acc.has(target)) {
31
+ const prev = acc.get(target);
32
+ if (prev.from !== desc.from) {
33
+ debug(`Using "${target}" from ${desc.from} instead of ${prev.from}`);
34
+ desc.overrides = prev.from;
35
+ }
36
+ }
37
+ acc.set(target, desc);
38
+ return acc;
39
+ }, new Map());
40
+
41
+ return Array.from(reduced.values());
42
+ }
43
+
44
+ /**
45
+ * Assemble the description objects from glob results
46
+ *
47
+ * @param {string} dest - app destination
48
+ * @param {string} from - Plugin source name
49
+ * @param {string} pattern - pattern to glob
50
+ * @param {string[]} srcPaths - resulting paths from glob
51
+ * @returns {object[]} descriptors
52
+ */
53
+ function assembleDescriptors(dest, from, pattern, srcPaths) {
54
+ const output = joinSep(splitSep(dest));
55
+ const baseParts = splitSep(path.resolve(pattern.replace(/[/\\]+\.?\*.*$/, '')));
56
+ const base = joinSep(baseParts);
57
+ return srcPaths.map((srcPath) => {
58
+ const parts = splitSep(srcPath);
59
+ const srcFile = joinSep(parts);
60
+ const target = joinSep(parts.slice(baseParts.length)).replace('.template', '');
61
+ return {
62
+ pattern,
63
+ base,
64
+ srcFile,
65
+ targetFile: joinSep([output, target]),
66
+ target,
67
+ from
68
+ };
69
+ });
70
+ }
71
+
72
+ /**
73
+ * Build a list of descriptions of all files we want to generate
74
+ *
75
+ * @param {CreateContext} context - Create context
76
+ * @returns {object[]} descriptors
77
+ */
78
+ async function getDescriptors(context) {
79
+ const { dest, files } = context;
80
+ const descriptors = (
81
+ await Promise.all(files.globSets.map(async set => {
82
+ const { globs, source } = set;
83
+ const matches = await Promise.all(globs.map(async pattern => {
84
+ pattern = splitSep(pattern).join('/'); // Glob uses / for all OS's
85
+ const srcPaths = await glob(pattern, { nodir: true });
86
+ return assembleDescriptors(dest, source.name, pattern, srcPaths);
87
+ }));
88
+ return matches.reduce(flatten);
89
+ }))
90
+ ).reduce(flatten);
91
+
92
+ return reduceDescriptors(descriptors);
93
+ }
94
+
95
+ /**
96
+ * Read file content, apply templating, then write out target file.
97
+ *
98
+ * @param {CreateContext} context - Create context
99
+ * @param {object[]} descriptors - Details about files to generate
100
+ * @returns {boolean} hasWarnings
101
+ */
102
+ async function performGenerate(context, descriptors) {
103
+ const { warnings, errors, generatedFiles } = context;
104
+ let hasWarning = false;
105
+ let hasError = false;
106
+
107
+ const handlebars = Handlebars.create();
108
+ handlebars.registerHelper('json', JSON.stringify);
109
+ handlebars.registerHelper('jspretty', (data) => {
110
+ return JSON.stringify(data).replace(/"/g, '\'');
111
+ });
112
+
113
+ debug('descriptors', JSON.stringify(descriptors, null, 2));
114
+
115
+ await Promise.all(descriptors.map(async desc => {
116
+ const targetDir = path.dirname(desc.targetFile);
117
+ let content;
118
+
119
+ try {
120
+ const source = await fs.readFile(desc.srcFile, { encoding: 'utf8' });
121
+ content = source;
122
+ try {
123
+ content = handlebars.compile(source)(context);
124
+ } catch (compileErr) {
125
+ hasWarning = true;
126
+ warnings.push(`Error templating ${desc.targetFile}: ${compileErr.message}`);
127
+ }
128
+ } catch (readErr) {
129
+ if (readErr.code === 'EISDIR') {
130
+ // Skipping directory errors.
131
+ // node-glob seems to incorrectly captures some directories on Windows
132
+ hasWarning = true;
133
+ warnings.push(`Directory matched as template file: ${desc.targetFile}`);
134
+ } else {
135
+ hasError = true;
136
+ errors.push(`Error reading template ${desc.srcFile}: ${readErr.message}`);
137
+ }
138
+ }
139
+ if (!content) return;
140
+
141
+ try {
142
+ await mkdirp(targetDir);
143
+ try {
144
+ await fs.writeFile(desc.targetFile, content, { encoding: 'utf8' });
145
+ const message = desc.target + (desc.overrides ? dim(` – from ${desc.from}`) : '');
146
+ generatedFiles.add(message);
147
+ } catch (writeErr) {
148
+ hasError = true;
149
+ errors.push(`Error writing ${desc.targetFile}: ${writeErr.message}`);
150
+ }
151
+ } catch (err) {
152
+ hasError = true;
153
+ errors.push(`Error creating directory ${targetDir}: ${err.message}`);
154
+ }
155
+ }));
156
+
157
+ return [hasWarning, hasError];
158
+ }
159
+
160
+ /**
161
+ * Generate the app files and templates using context
162
+ *
163
+ * @param {CreateContext} context - Create context
164
+ * @param {Spinner} spinner - Spinner
165
+ * @returns {Promise} promise
166
+ */
167
+ async function generateFiles({ context, spinner }) {
168
+ const { files } = context;
169
+
170
+ if (!files.globSets.length) {
171
+ return;
172
+ }
173
+
174
+ let descriptors = await getDescriptors(context);
175
+ descriptors = reduceDescriptors(descriptors);
176
+ const [hasWarnings, hasError] = await performGenerate(context, descriptors);
177
+
178
+ if (hasWarnings) spinner.warn();
179
+ if (hasError) spinner.fail();
180
+ }
181
+
182
+ export default action('Generate app contents', generateFiles);
183
+ // exported for unit testing
184
+ export const _getDescriptors = getDescriptors;
185
+ export const _assembleDescriptors = assembleDescriptors;
@@ -0,0 +1,158 @@
1
+ import inquirer from 'inquirer';
2
+ import action from '../action-wrapper.js';
3
+
4
+ /**
5
+ * What is your app description?
6
+ *
7
+ * @param {CreateContext} context - Create context
8
+ * @param {function} prompt - function to prompt user
9
+ * @returns {Promise} promise
10
+ */
11
+ async function chooseAppDescription(context, prompt) {
12
+ if (!('appDescription' in context)) {
13
+ const { appDescription } = await prompt([
14
+ {
15
+ name: 'appDescription',
16
+ message: 'What is your app description?',
17
+ type: 'input',
18
+ default: 'A basic gasket app'
19
+ }
20
+ ]);
21
+
22
+ Object.assign(context, { appDescription });
23
+ }
24
+ }
25
+
26
+ /**
27
+ * What package manager do you want to use?
28
+ *
29
+ * @param {CreateContext} context - Create context
30
+ * @param {function} prompt - function to prompt user
31
+ * @returns {Promise} promise
32
+ */
33
+ async function choosePackageManager(context, prompt) {
34
+ const packageManager =
35
+ context.packageManager ||
36
+ (
37
+ await prompt([
38
+ {
39
+ name: 'packageManager',
40
+ message: 'Which packager would you like to use?',
41
+ type: 'list',
42
+ choices: [{ name: 'npm' }, { name: 'yarn' }]
43
+ }
44
+ ])
45
+ ).packageManager;
46
+
47
+ const installCmd = context.installCmd || `${packageManager} install`;
48
+
49
+ const runners = {
50
+ npm: 'npx',
51
+ yarn: 'yarn'
52
+ };
53
+
54
+ const localCmd = context.localCmd || `${runners[packageManager]} gasket local`;
55
+
56
+ Object.assign(context, {
57
+ packageManager,
58
+ installCmd,
59
+ localCmd
60
+ });
61
+ }
62
+
63
+ /**
64
+ * Choose your unit test suite and integration test suite
65
+ *
66
+ * @param {CreateContext} context - Create context
67
+ * @param {function} prompt - function to prompt user
68
+ * @returns {Promise} promise
69
+ */
70
+ async function chooseTestPlugins(context, prompt) {
71
+ const knownTestPlugins = {
72
+ unit: { mocha: '@gasket/plugin-mocha', jest: '@gasket/plugin-jest' },
73
+ integration: { cypress: '@gasket/plugin-cypress' }
74
+ };
75
+
76
+ const testTypes = ['unit', 'integration'];
77
+ const testPlugins = [];
78
+
79
+ if (!('testPlugins' in context)) {
80
+ for (const type of testTypes) {
81
+ if (type + 'TestSuite' in context) {
82
+ const testSuite = knownTestPlugins[type][context[type + 'TestSuite']];
83
+ if (testSuite) testPlugins.push(testSuite);
84
+ } else {
85
+ const plugin = await promptForTestPlugin(
86
+ prompt,
87
+ `Choose your ${type} test suite`,
88
+ Object.entries(knownTestPlugins[type]).map(([name, value]) => ({ name, value }))
89
+ );
90
+
91
+ if (plugin) testPlugins.push(plugin);
92
+ }
93
+ }
94
+
95
+ if (testPlugins.length > 0) {
96
+ Object.assign(context, { testPlugins });
97
+ }
98
+ }
99
+ }
100
+
101
+ async function promptForTestPlugin(prompt, message, choices) {
102
+ const { testPlugin } = await prompt([
103
+ {
104
+ name: 'testPlugin',
105
+ message,
106
+ type: 'list',
107
+ choices: [{ name: 'none', value: 'none' }, ...choices]
108
+ }
109
+ ]);
110
+
111
+ return testPlugin !== 'none' ? testPlugin : null;
112
+ }
113
+
114
+ /**
115
+ * Given that gasket is creating in an already existing directory, it should
116
+ * confirm with the user that it's intentionally overwriting that directory
117
+ *
118
+ * @param {CreateContext} context - Create context
119
+ * @param {function} prompt - function to prompt user
120
+ * @returns {Promise} promise
121
+ */
122
+ async function allowExtantOverwriting(context, prompt) {
123
+ const { dest, extant } = context;
124
+ if (extant && !('destOverride' in context)) {
125
+ const { destOverride } = await prompt([
126
+ {
127
+ name: 'destOverride',
128
+ type: 'confirm',
129
+ message: `Override contents of ${dest} ?`,
130
+ default: true
131
+ }
132
+ ]);
133
+
134
+ Object.assign(context, { destOverride });
135
+ }
136
+ }
137
+
138
+ export const questions = [
139
+ chooseAppDescription,
140
+ choosePackageManager,
141
+ chooseTestPlugins,
142
+ allowExtantOverwriting
143
+ ];
144
+
145
+ /**
146
+ * Fire off prompts for user input
147
+ *
148
+ * @param {CreateContext} context - Create context
149
+ * @returns {Promise} promise
150
+ */
151
+ async function globalPrompts({ context }) {
152
+ const prompt = context.prompts ? inquirer.prompt : () => ({});
153
+ for (var fn of questions) {
154
+ await fn(context, prompt);
155
+ }
156
+ }
157
+
158
+ export default action('Global prompts', globalPrompts, { startSpinner: false });
@@ -0,0 +1,33 @@
1
+ import mkDir from './mkdir.js';
2
+ import loadPreset from './load-preset.js';
3
+ import globalPrompts from './global-prompts.js';
4
+ import setupPkg from './setup-pkg.js';
5
+ import writePkg from './write-pkg.js';
6
+ import installModules from './install-modules.js';
7
+ import linkModules from './link-modules.js';
8
+ import writeGasketConfig from './write-gasket-config.js';
9
+ import presetPromptHooks from './preset-prompt-hooks.js';
10
+ import presetConfigHooks from './preset-config-hooks.js';
11
+ import promptHooks from './prompt-hooks.js';
12
+ import createHooks from './create-hooks.js';
13
+ import generateFiles from './generate-files.js';
14
+ import postCreateHooks from './post-create-hooks.js';
15
+ import printReport from './print-report.js';
16
+
17
+ export {
18
+ mkDir,
19
+ loadPreset,
20
+ globalPrompts,
21
+ setupPkg,
22
+ writePkg,
23
+ installModules,
24
+ linkModules,
25
+ writeGasketConfig,
26
+ presetPromptHooks,
27
+ presetConfigHooks,
28
+ promptHooks,
29
+ createHooks,
30
+ postCreateHooks,
31
+ generateFiles,
32
+ printReport
33
+ };
@@ -0,0 +1,15 @@
1
+ import action from '../action-wrapper.js';
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
+ export default action('Install node modules', installModules);
@@ -0,0 +1,19 @@
1
+ import action from '../action-wrapper.js';
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
+ export default action('Link node modules', linkModules, { startSpinner: false });
@@ -0,0 +1,60 @@
1
+ import path from 'path';
2
+ import os from 'os';
3
+ import action from '../action-wrapper.js';
4
+ import { default as gasketUtils } from '@gasket/utils';
5
+ import { mkdtemp } from 'fs/promises';
6
+ import { createRequire } from 'module';
7
+ const require = createRequire(import.meta.url);
8
+ const hasVersion = /@(\^[0-9]\.|[0-9]).+/;
9
+
10
+ /**
11
+ * loadPresets - Load presets to temp directory
12
+ * @param {CreateContext} context - Create context
13
+ */
14
+ async function loadPresets({ context }) {
15
+ const tmpDir = await mkdtemp(path.join(os.tmpdir(), `gasket-create-${context.appName}`));
16
+ context.tmpDir = tmpDir;
17
+
18
+ const modPath = path.join(tmpDir, 'node_modules');
19
+ const pkgManager = new gasketUtils.PackageManager({
20
+ packageManager: context.packageManager,
21
+ dest: tmpDir
22
+ });
23
+ const pkgVerb = pkgManager.manager === 'yarn' ? 'add' : 'install';
24
+
25
+ const remotePresets = context.rawPresets.map(async preset => {
26
+ const parts = hasVersion.test(preset) && preset.split('@').filter(Boolean);
27
+ const name = parts ? `@${parts[0]}` : preset;
28
+ const version = parts ? `@${parts[1]}` : '@latest';
29
+
30
+ try {
31
+ // Install the preset
32
+ await pkgManager.exec(pkgVerb, [`${name}${version}`]);
33
+ // Get the package file
34
+ const pkgFile = require(path.join(modPath, name, 'package.json'));
35
+ // Import the preset via the package file attrs, name and main
36
+ // We can't specify the cwd for the import, so we need to use the full path
37
+ // expects type:module & "main": "lib/fullpath.js"
38
+ const mod = await import(`${modPath}/${name}/${pkgFile.main}`);
39
+ return mod.default || mod;
40
+ } catch (err) {
41
+ throw new Error(`Failed to install preset ${name}${version}`);
42
+ }
43
+ });
44
+
45
+ const localPresets = context.localPresets.map(async localPresetPath => {
46
+
47
+ try {
48
+ await pkgManager.exec(pkgVerb, [localPresetPath]);
49
+ const pkgFile = require(path.join(localPresetPath, 'package.json'));
50
+ const mod = await import(`${modPath}/${pkgFile.name}/${pkgFile.main}`);
51
+ return mod.default || mod;
52
+ } catch (err) {
53
+ throw new Error(`Failed to install local preset ${localPresetPath}`);
54
+ }
55
+ });
56
+
57
+ context.presets = await Promise.all([...remotePresets, ...localPresets]);
58
+ }
59
+
60
+ export default action('Load presets', loadPresets);
@@ -0,0 +1,30 @@
1
+ import { mkdir } from 'fs/promises';
2
+ import action from '../action-wrapper.js';
3
+
4
+ /**
5
+ * Validates this instance can execute without common blockers:
6
+ * - Target destination on disk is available. Validate by acquiring
7
+ * a lock through `mkdir`.
8
+ *
9
+ * @param {CreateContext} context - Create context
10
+ * @param {Spinner} spinner - Spinner
11
+ * @returns {Promise} promise
12
+ */
13
+ async function mkDir({ context, spinner }) {
14
+ const { dest, relDest, extant, destOverride } = context;
15
+
16
+ // At this point, either the directory does not exist, or the user has
17
+ // confirmed that this directory can be overwritten, so we can proceed.
18
+
19
+ if (!extant) {
20
+ spinner.text = `Make directory: ${relDest}`;
21
+ await mkdir(dest);
22
+ } else {
23
+ spinner.text = `Existing directory: ${relDest}`;
24
+ if (!destOverride) {
25
+ throw new Error(`Directory ${relDest} was not allowed to be overwritten.`);
26
+ }
27
+ }
28
+ }
29
+
30
+ export default action('Set up directory', mkDir);
@@ -0,0 +1,31 @@
1
+ import action from '../action-wrapper.js';
2
+ import { runShellCommand } from '@gasket/utils';
3
+
4
+ /**
5
+ * Executes the `postCreate` hook for all registered plugins.
6
+ *
7
+ * @param {GasketEngine} gasket - Gasket API
8
+ * @param {CreateContext} context - Create context
9
+ * @returns {Promise} promise
10
+ */
11
+ async function postCreateHooks({ gasket, context }) {
12
+ const { dest } = context;
13
+
14
+ /**
15
+ * Run an npm script in the context of the created application
16
+ * @param {String} script name of script
17
+ * @returns {Promise} A promise represents if npm succeeds or fails.
18
+ */
19
+ async function runScript(script) {
20
+ return await runShellCommand('npm', ['run', script], { cwd: dest });
21
+ }
22
+
23
+ /**
24
+ * An object with one value for now, so adding more utilities
25
+ * in future is easy.
26
+ */
27
+ const utils = { runScript };
28
+ await gasket.exec('postCreate', context, utils);
29
+ }
30
+
31
+ export default action('Execute postCreate hooks', postCreateHooks);
@@ -0,0 +1,13 @@
1
+ import action from '../action-wrapper.js';
2
+
3
+ /**
4
+ * presetConfigHooks - exec `presetConfig` hook
5
+ * @param {Gasket} gasket - Preset gasket instance
6
+ * @param {CreateContext} context - Create context
7
+ */
8
+ async function presetConfigHooks({ gasket, context }) {
9
+ const config = await gasket.execWaterfall('presetConfig', context);
10
+ Object.assign(context.presetConfig, config);
11
+ }
12
+
13
+ export default action('Preset Config', presetConfigHooks, { startSpinner: false });
@@ -0,0 +1,17 @@
1
+ import inquirer from 'inquirer';
2
+ import action from '../action-wrapper.js';
3
+
4
+ /**
5
+ * presetPromptHooks - exec `presetPrompt` hook
6
+ * @param {Gasket} gasket - Preset gasket instance
7
+ * @param {CreateContext} context - Create context
8
+ */
9
+ async function presetPromptHooks({ gasket, context }) {
10
+ const prompt = context.prompts ? inquirer.createPromptModule() : () => ({});
11
+
12
+ const nextContext = await gasket.execWaterfall('presetPrompt', context, { prompt });
13
+
14
+ Object.assign(context, nextContext);
15
+ }
16
+
17
+ export default action('Preset prompts', presetPromptHooks, { startSpinner: false });