create-gasket-app 7.0.0-cli.7 → 7.0.0-next.1
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,82 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const action = require('../action-wrapper');
|
|
3
|
+
const PackageFetcher = require('../fetcher');
|
|
4
|
+
const { presetIdentifier, Loader } = require('@gasket/resolve');
|
|
5
|
+
|
|
6
|
+
const loader = new Loader();
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Fetches the preset packages and loads PresetInfos
|
|
10
|
+
*
|
|
11
|
+
* @param {[String]} rawPresets - Presets names
|
|
12
|
+
* @param {String} cwd - Root path
|
|
13
|
+
* @param {String} from from
|
|
14
|
+
* @returns {PresetInfo[]} loaded presetInfos
|
|
15
|
+
* @private
|
|
16
|
+
*/
|
|
17
|
+
async function remotePresets(rawPresets, cwd, from) {
|
|
18
|
+
if (!rawPresets) { return []; }
|
|
19
|
+
|
|
20
|
+
const allRemotePresets = await Promise.all(rawPresets.map(async rawName => {
|
|
21
|
+
const packageName = presetIdentifier(rawName).full;
|
|
22
|
+
const fetcher = new PackageFetcher({ cwd, packageName });
|
|
23
|
+
const pkgPath = await fetcher.clone();
|
|
24
|
+
|
|
25
|
+
const presetInfo = loader.loadPreset(pkgPath, { from: from, rawName }, { shallow: true });
|
|
26
|
+
const { name: presetName, dependencies } = presetInfo.package;
|
|
27
|
+
if (!dependencies) {
|
|
28
|
+
return presetInfo;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const presetDeps = Object.keys(dependencies).filter(k => presetIdentifier.isValidFullName(k));
|
|
32
|
+
const rawPresetsDeps = presetDeps.map(presetDep => `${presetDep}@${dependencies[presetDep]}`);
|
|
33
|
+
|
|
34
|
+
const presetInfoDeps = await remotePresets(rawPresetsDeps, cwd, presetName);
|
|
35
|
+
return {
|
|
36
|
+
...presetInfo,
|
|
37
|
+
presets: presetInfoDeps
|
|
38
|
+
};
|
|
39
|
+
}));
|
|
40
|
+
|
|
41
|
+
return allRemotePresets;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Loads PresetInfo from local preset package
|
|
46
|
+
*
|
|
47
|
+
* @param {CreateContext} context - Create context
|
|
48
|
+
* @returns {PresetInfo[]} loaded presetInfos
|
|
49
|
+
* @private
|
|
50
|
+
*/
|
|
51
|
+
function localPreset(context) {
|
|
52
|
+
const { cwd, localPresets = [] } = context;
|
|
53
|
+
if (!localPresets) { return []; }
|
|
54
|
+
|
|
55
|
+
const presetInfos = [];
|
|
56
|
+
localPresets.forEach(localPresetPath => {
|
|
57
|
+
const pkgPath = path.resolve(cwd, localPresetPath);
|
|
58
|
+
const presetInfo = loader.loadPreset(pkgPath, { from: 'cli' }, { shallow: false });
|
|
59
|
+
presetInfo.rawName = `${presetInfo.package.name}@file:${pkgPath}`;
|
|
60
|
+
presetInfos.push(presetInfo);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
return presetInfos;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Downloads the target preset package and adds the package.json
|
|
68
|
+
* contents and plugins dependencies to context.
|
|
69
|
+
*
|
|
70
|
+
* @param {CreateContext} context - Create context
|
|
71
|
+
* @returns {Promise} promise
|
|
72
|
+
*/
|
|
73
|
+
async function loadPreset(context) {
|
|
74
|
+
const { rawPresets = [], cwd } = context;
|
|
75
|
+
let presetInfos = await remotePresets(rawPresets, cwd, 'cli');
|
|
76
|
+
presetInfos = presetInfos.concat(localPreset(context));
|
|
77
|
+
|
|
78
|
+
const presets = presetInfos.map(p => presetIdentifier(p.rawName).shortName);
|
|
79
|
+
Object.assign(context, { presets, presetInfos });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
module.exports = action('Load presets', loadPreset);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const { mkdir } = require('fs').promises;
|
|
2
|
+
const action = require('../action-wrapper');
|
|
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
|
+
module.exports = action('Set up directory', mkDir);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const action = require('../action-wrapper');
|
|
2
|
+
const createEngine = require('../create-engine');
|
|
3
|
+
const { runShellCommand } = require('@gasket/utils');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Executes the `postCreate` hook for all registered plugins.
|
|
7
|
+
*
|
|
8
|
+
* @param {CreateContext} context - Create context
|
|
9
|
+
* @returns {Promise} promise
|
|
10
|
+
*/
|
|
11
|
+
async function postCreateHooks(context) {
|
|
12
|
+
const { dest, presets = [], plugins = [] } = 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
|
+
|
|
29
|
+
const gasket = await createEngine({ dest, presets, plugins });
|
|
30
|
+
await gasket.exec('postCreate', context, utils);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = action('Execute postCreate hooks', postCreateHooks);
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
const { bold } = require('chalk');
|
|
2
|
+
const action = require('../action-wrapper');
|
|
3
|
+
const asciiLogo = require('../../utils/logo');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Logs a new line in the console
|
|
7
|
+
* @private
|
|
8
|
+
*/
|
|
9
|
+
const newline = () => {
|
|
10
|
+
console.log('');
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Converts a camelCase string to Space Case
|
|
15
|
+
* @see: https://stackoverflow.com/questions/4149276/javascript-camelcase-to-regular-form?answertab=active#tab-top
|
|
16
|
+
*
|
|
17
|
+
* @param {string} str - camelCase string to fixup
|
|
18
|
+
* @returns {string} result
|
|
19
|
+
* @private
|
|
20
|
+
*/
|
|
21
|
+
const toSpaceCase = str => str.replace(/([A-Z])/g, ' $1')
|
|
22
|
+
.replace(/^./, s => s.toUpperCase());
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Builds the report object from context
|
|
26
|
+
*
|
|
27
|
+
* @param {CreateContext} context - Create context
|
|
28
|
+
* @returns {Object} report
|
|
29
|
+
* @private
|
|
30
|
+
*/
|
|
31
|
+
function buildReport(context) {
|
|
32
|
+
const {
|
|
33
|
+
appName,
|
|
34
|
+
dest,
|
|
35
|
+
fullPresets,
|
|
36
|
+
plugins,
|
|
37
|
+
generatedFiles: generatedFilesSet,
|
|
38
|
+
messages,
|
|
39
|
+
warnings,
|
|
40
|
+
errors,
|
|
41
|
+
nextSteps
|
|
42
|
+
} = context;
|
|
43
|
+
|
|
44
|
+
const generatedFiles = Array.from(generatedFilesSet);
|
|
45
|
+
generatedFiles.sort();
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
appName,
|
|
49
|
+
output: dest,
|
|
50
|
+
presets: fullPresets,
|
|
51
|
+
plugins,
|
|
52
|
+
generatedFiles,
|
|
53
|
+
messages,
|
|
54
|
+
warnings,
|
|
55
|
+
errors,
|
|
56
|
+
nextSteps
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Outputs create command details to the console
|
|
62
|
+
|
|
63
|
+
* @param {CreateContext} context - Create context
|
|
64
|
+
*/
|
|
65
|
+
function printReport(context) {
|
|
66
|
+
const report = buildReport(context);
|
|
67
|
+
const { warnings, errors } = context;
|
|
68
|
+
|
|
69
|
+
console.log(`✨Success!
|
|
70
|
+
|
|
71
|
+
Finished with ${warnings.length} warnings and ${errors.length} errors using
|
|
72
|
+
` + asciiLogo);
|
|
73
|
+
|
|
74
|
+
Object.entries(report).forEach(([k, v]) => {
|
|
75
|
+
if (!v || !v.length) return;
|
|
76
|
+
newline();
|
|
77
|
+
console.log(`${bold(toSpaceCase(k))}`);
|
|
78
|
+
if (Array.isArray(v)) {
|
|
79
|
+
v.map(o => console.log(` ${o}`));
|
|
80
|
+
} else {
|
|
81
|
+
console.log(` ${v}`);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
newline();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
module.exports = action('Print report', printReport, { startSpinner: false });
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
const inquirer = require('inquirer');
|
|
2
|
+
const action = require('../action-wrapper');
|
|
3
|
+
const { addPluginsToContext, addPluginsToPkg, getPluginsWithVersions } = require('../utils');
|
|
4
|
+
const createEngine = require('../create-engine');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Create the `addPlugins` function with context
|
|
8
|
+
*
|
|
9
|
+
* @param {CreateContext} context - Create context
|
|
10
|
+
* @returns {addPlugins} addPlugins
|
|
11
|
+
*/
|
|
12
|
+
const createAddPlugins = context => {
|
|
13
|
+
/**
|
|
14
|
+
* Allows plugins to add plugins based on user input.
|
|
15
|
+
*
|
|
16
|
+
* @typedef {Function} addPlugins
|
|
17
|
+
*
|
|
18
|
+
* @param {...PluginDesc} pluginsToAdd - Additional plugins to add
|
|
19
|
+
* @returns {Promise} promise
|
|
20
|
+
*/
|
|
21
|
+
return async function addPlugins(...pluginsToAdd) {
|
|
22
|
+
const { pkg, pkgLinks = [], pkgManager } = context;
|
|
23
|
+
|
|
24
|
+
addPluginsToContext(pluginsToAdd, context);
|
|
25
|
+
const pluginIds = await getPluginsWithVersions(pluginsToAdd, pkgManager);
|
|
26
|
+
addPluginsToPkg(pluginIds, pkg);
|
|
27
|
+
|
|
28
|
+
//
|
|
29
|
+
// Install new plugins if not already linked
|
|
30
|
+
//
|
|
31
|
+
const toInstall = pluginIds.filter(p => !pkgLinks.includes(p.fullName));
|
|
32
|
+
if (toInstall.length) {
|
|
33
|
+
await pkgManager.install(toInstall.map(p => p.full));
|
|
34
|
+
//
|
|
35
|
+
// relink because npm blows them away on installs...
|
|
36
|
+
//
|
|
37
|
+
if (pkgLinks.length) {
|
|
38
|
+
await pkgManager.link(pkgLinks);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
await execPluginPrompts(context, pluginIds.map(p => p.shortName));
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Initializes engine with provide preset and plugins
|
|
48
|
+
* to execute their prompt lifecycle hooks.
|
|
49
|
+
*
|
|
50
|
+
* @param {CreateContext} context - Create context
|
|
51
|
+
* @param {String[]} plugins - plugins to load
|
|
52
|
+
* @param {String} [presets] - presets to load
|
|
53
|
+
* @returns {Promise} promise
|
|
54
|
+
* @private
|
|
55
|
+
*/
|
|
56
|
+
async function execPluginPrompts(context, plugins = [], presets = []) {
|
|
57
|
+
const { dest } = context;
|
|
58
|
+
|
|
59
|
+
const gasket = await createEngine({ dest, presets, plugins });
|
|
60
|
+
|
|
61
|
+
//
|
|
62
|
+
// @see: https://github.com/SBoudrias/Inquirer.js/#inquirercreatepromptmodule---prompt-function
|
|
63
|
+
//
|
|
64
|
+
const prompt = context.prompts ? inquirer.createPromptModule() : () => ({});
|
|
65
|
+
const addPlugins = createAddPlugins(context);
|
|
66
|
+
|
|
67
|
+
const nextContext = await gasket.execWaterfall('prompt', context, { prompt, addPlugins });
|
|
68
|
+
//
|
|
69
|
+
// Ensure the original context is transformed in case a prompt returns a new object
|
|
70
|
+
//
|
|
71
|
+
Object.assign(context, nextContext);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Executes the `prompt` hook for all registered plugins.
|
|
76
|
+
* Adds `prompt` and `addPlugins` util functions for prompting features.
|
|
77
|
+
*
|
|
78
|
+
* @param {CreateContext} context - Create context
|
|
79
|
+
* @returns {Promise} promise
|
|
80
|
+
*/
|
|
81
|
+
async function promptHooks(context) {
|
|
82
|
+
//
|
|
83
|
+
// Because `execPluginPrompts` is recursively, we need to start it
|
|
84
|
+
// with the processPlugins and presets from our initial context
|
|
85
|
+
//
|
|
86
|
+
const { presets, plugins } = context;
|
|
87
|
+
await execPluginPrompts(context, plugins, presets);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
module.exports = action('Plugin prompts', promptHooks, { startSpinner: false });
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const action = require('../action-wrapper');
|
|
2
|
+
const ConfigBuilder = require('../config-builder');
|
|
3
|
+
const { addPluginsToPkg, getPluginsWithVersions } = require('../utils');
|
|
4
|
+
const { PackageManager } = require('@gasket/utils');
|
|
5
|
+
const { presetIdentifier } = require('@gasket/resolve');
|
|
6
|
+
const { devDependencies } = require('../../../package');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Initializes the ConfigBuilder builder and adds to context.
|
|
10
|
+
*
|
|
11
|
+
* @param {CreateContext} context - Create context
|
|
12
|
+
* @returns {Promise} promise
|
|
13
|
+
*/
|
|
14
|
+
async function setupPkg(context) {
|
|
15
|
+
const { appName, appDescription, presetInfos = [], rawPlugins = [], cliVersionRequired, warnings } = context;
|
|
16
|
+
|
|
17
|
+
const pkg = ConfigBuilder.createPackageJson({
|
|
18
|
+
name: appName,
|
|
19
|
+
version: '0.0.0',
|
|
20
|
+
description: appDescription
|
|
21
|
+
}, { warnings });
|
|
22
|
+
|
|
23
|
+
// The preset package itself must be included in the dependencies
|
|
24
|
+
// of the `pkg` to be bootstrapped.
|
|
25
|
+
pkg.add('dependencies', presetInfos.reduce((acc, presetInfo) => {
|
|
26
|
+
// Use rawName in case version or file path was set in cli args
|
|
27
|
+
// Otherwise fallback to resolved version
|
|
28
|
+
const { fullName, version } = presetIdentifier(presetInfo.rawName).withVersion(`^${presetInfo.version}`);
|
|
29
|
+
|
|
30
|
+
acc[fullName] = version;
|
|
31
|
+
return acc;
|
|
32
|
+
}, {
|
|
33
|
+
'@gasket/cli': cliVersionRequired
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
pkg.add('depencencies', {
|
|
37
|
+
dotenv: devDependencies.dotenv
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const pkgManager = new PackageManager(context);
|
|
41
|
+
const pluginIds = await getPluginsWithVersions(rawPlugins, pkgManager);
|
|
42
|
+
addPluginsToPkg(pluginIds, pkg);
|
|
43
|
+
|
|
44
|
+
Object.assign(context, { pkg, pkgManager });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
module.exports = action('Set up package.json', setupPkg, { startSpinner: false });
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const JSON5 = require('json5');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { writeFile } = require('fs').promises;
|
|
4
|
+
const action = require('../action-wrapper');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Writes the contents of `pkg` to the app's package.json.
|
|
8
|
+
*
|
|
9
|
+
* @param {CreateContext} context - Create context
|
|
10
|
+
* @returns {Promise} promise
|
|
11
|
+
*/
|
|
12
|
+
async function writeGasketConfig(context) {
|
|
13
|
+
const { dest, gasketConfig, presets, plugins, generatedFiles } = context;
|
|
14
|
+
const fileName = 'gasket.config.js';
|
|
15
|
+
const filePath = path.join(dest, fileName);
|
|
16
|
+
|
|
17
|
+
gasketConfig.add('plugins', {
|
|
18
|
+
presets,
|
|
19
|
+
...(plugins && plugins.length ? { add: plugins } : {})
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
//
|
|
23
|
+
// Use JSON5 to stringify the config and add export for CommonJS
|
|
24
|
+
//
|
|
25
|
+
const contents = 'module.exports = ' +
|
|
26
|
+
JSON5.stringify(gasketConfig, null, 2) + ';\n';
|
|
27
|
+
|
|
28
|
+
await writeFile(filePath, contents, 'utf8');
|
|
29
|
+
generatedFiles.add(fileName);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = action('Write gasket config', writeGasketConfig);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const { writeFile } = require('fs').promises;
|
|
3
|
+
const action = require('../action-wrapper');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Writes the contents of `pkg` to the app's package.json.
|
|
7
|
+
*
|
|
8
|
+
* @param {CreateContext} context - Create context
|
|
9
|
+
* @param {Boolean} update - Is an update step
|
|
10
|
+
* @returns {Promise} promise
|
|
11
|
+
*/
|
|
12
|
+
async function writePkg(context) {
|
|
13
|
+
const { dest, pkg, generatedFiles } = context;
|
|
14
|
+
const fileName = 'package.json';
|
|
15
|
+
const filePath = path.join(dest, fileName);
|
|
16
|
+
|
|
17
|
+
await writeFile(filePath, JSON.stringify(pkg, null, 2), 'utf8');
|
|
18
|
+
generatedFiles.add(fileName);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = action('Write package.json', writePkg);
|
|
22
|
+
|
|
23
|
+
module.exports.update = action('Update package.json', writePkg);
|