create-gasket-app 7.0.0-next.2 → 7.0.0-next.21
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/commands/create.js +40 -85
- package/lib/config/default-plugins.js +6 -6
- package/lib/index.d.ts +81 -25
- package/lib/index.js +7 -6
- package/lib/main.js +5 -4
- package/lib/scaffold/action-wrapper.js +8 -7
- package/lib/scaffold/actions/create-hooks.js +8 -9
- package/lib/scaffold/actions/generate-files.js +17 -15
- package/lib/scaffold/actions/global-prompts.js +11 -26
- package/lib/scaffold/actions/index.js +18 -21
- package/lib/scaffold/actions/install-modules.js +3 -6
- package/lib/scaffold/actions/link-modules.js +3 -5
- package/lib/scaffold/actions/load-preset.js +50 -72
- package/lib/scaffold/actions/mkdir.js +4 -4
- package/lib/scaffold/actions/post-create-hooks.js +6 -8
- package/lib/scaffold/actions/preset-config-hooks.js +13 -0
- package/lib/scaffold/actions/preset-prompt-hooks.js +17 -0
- package/lib/scaffold/actions/print-report.js +8 -7
- package/lib/scaffold/actions/prompt-hooks.js +10 -57
- package/lib/scaffold/actions/setup-pkg.js +17 -29
- package/lib/scaffold/actions/write-gasket-config.js +102 -17
- package/lib/scaffold/actions/write-pkg.js +5 -9
- package/lib/scaffold/config-builder.js +82 -7
- package/lib/scaffold/create-context.js +15 -21
- package/lib/scaffold/dump-error-context.js +5 -5
- package/lib/scaffold/files.js +2 -2
- package/lib/scaffold/utils.js +4 -97
- package/lib/utils/create-option.js +2 -4
- package/lib/utils/index.js +6 -16
- package/lib/utils/logo.js +1 -3
- package/lib/utils/process-args.js +1 -3
- package/lib/utils/process-command.js +3 -7
- package/lib/utils/process-options.js +1 -3
- package/lib/utils/warn-if-outdated.js +11 -9
- package/package.json +14 -12
- package/lib/scaffold/actions/apply-preset-config.js +0 -29
- package/lib/scaffold/actions/cli-version.js +0 -84
- package/lib/scaffold/actions/load-pkg-for-debug.js +0 -24
- package/lib/scaffold/create-engine.js +0 -21
- package/lib/scaffold/fetcher.js +0 -194
- package/lib/utils/setup.js +0 -25
|
@@ -1,84 +0,0 @@
|
|
|
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 });
|
|
@@ -1,24 +0,0 @@
|
|
|
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);
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
const path = require('path');
|
|
2
|
-
const GasketEngine = require('@gasket/engine');
|
|
3
|
-
const defaultPlugins = require('../config/default-plugins');
|
|
4
|
-
|
|
5
|
-
module.exports = async function createEngine({ dest, presets = [], plugins = [] }) {
|
|
6
|
-
const resolveFrom = path.join(dest, 'node_modules');
|
|
7
|
-
|
|
8
|
-
const engineConfig = {
|
|
9
|
-
plugins: {
|
|
10
|
-
presets,
|
|
11
|
-
add: [...defaultPlugins, ...plugins]
|
|
12
|
-
},
|
|
13
|
-
root: dest
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const engine = new GasketEngine(engineConfig, { resolveFrom });
|
|
17
|
-
engine.command = { id: 'create' };
|
|
18
|
-
await engine.exec('init');
|
|
19
|
-
|
|
20
|
-
return engine;
|
|
21
|
-
};
|
package/lib/scaffold/fetcher.js
DELETED
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
const path = require('path');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
const { rename, unlink } = fs.promises;
|
|
4
|
-
const tar = require('tar-fs');
|
|
5
|
-
const zlib = require('zlib');
|
|
6
|
-
const pump = require('pump');
|
|
7
|
-
const mkdirp = require('mkdirp');
|
|
8
|
-
const debug = require('diagnostics')('gasket:create-gasket-app:fetcher');
|
|
9
|
-
const { PackageManager } = require('@gasket/utils');
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Simple helper class that can also be re-used in tests for similar
|
|
13
|
-
* file I/O operations
|
|
14
|
-
*/
|
|
15
|
-
const Fetcher = class Fetcher {
|
|
16
|
-
constructor(opts = {}) {
|
|
17
|
-
this.dir = path.join(opts.tmp || require('os').tmpdir(), this._id());
|
|
18
|
-
debug('tmpdir', this.dir);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Ensures an eventually consistent rm -rf (aka rimraf) of
|
|
23
|
-
* the target sourceDir by atomically moving it into this
|
|
24
|
-
* tmp directory and then spawning a detached child process
|
|
25
|
-
* to remove it.
|
|
26
|
-
* @param {string} sourceDir Directory within this repository. *
|
|
27
|
-
* @returns {Promise} result
|
|
28
|
-
*/
|
|
29
|
-
async vacuum(sourceDir) {
|
|
30
|
-
const spawn = require('cross-spawn');
|
|
31
|
-
const basename = path.basename(sourceDir);
|
|
32
|
-
const rootDir = path.join(__dirname, '..', '..');
|
|
33
|
-
|
|
34
|
-
if (!sourceDir.startsWith(rootDir)) {
|
|
35
|
-
console.error('Refusing to rimraf vacuum unsafe directory: ', sourceDir);
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
await mkdirp(this.dir);
|
|
40
|
-
try {
|
|
41
|
-
await rename(sourceDir, path.join(this.dir, basename));
|
|
42
|
-
} catch (err) {
|
|
43
|
-
if (err.code === 'ENOENT') return;
|
|
44
|
-
throw err;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const rm = spawn(process.execPath, [
|
|
48
|
-
require.resolve('rimraf/bin.js'),
|
|
49
|
-
this.dir
|
|
50
|
-
], { detached: true });
|
|
51
|
-
|
|
52
|
-
rm.on('error', err => {
|
|
53
|
-
console.error(`Error removing ${this.dir}: ${err.message}`);
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Returns a reasonably unique name for a subdirectory in `os.tmpdir()`
|
|
59
|
-
* @returns {String} Reasonably unique name
|
|
60
|
-
* @private
|
|
61
|
-
*/
|
|
62
|
-
_id() {
|
|
63
|
-
return `gasket-${process.hrtime().join('-')}`;
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Package fetcher that represents the core file system logic
|
|
70
|
-
* for getting npm packages without having to perform a
|
|
71
|
-
* configuration.
|
|
72
|
-
*
|
|
73
|
-
* @type {PackageFetcher}
|
|
74
|
-
* @public
|
|
75
|
-
*/
|
|
76
|
-
module.exports = class PackageFetcher {
|
|
77
|
-
constructor(opts = {}) {
|
|
78
|
-
this.cwd = opts.cwd || process.cwd();
|
|
79
|
-
this.packageName = opts.packageName;
|
|
80
|
-
this.tmp = new Fetcher(opts);
|
|
81
|
-
debug('init', this.packageName, this.tmp.dir);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Simple wrapper around clone that also reads the package.json
|
|
86
|
-
* from disk once it has been downloaded.
|
|
87
|
-
*
|
|
88
|
-
* @returns {Promise<Object>} Parsed package.json for the fetched package.
|
|
89
|
-
*/
|
|
90
|
-
async readPackage() {
|
|
91
|
-
const dest = await this.clone();
|
|
92
|
-
return require(path.join(dest, 'package.json'));
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Critical path file system operations to pull down the npm package
|
|
97
|
-
* that represents the desired gasket scaffold, `this.packageName`. The logic
|
|
98
|
-
* revolves around the assumption:
|
|
99
|
-
*
|
|
100
|
-
* 1. Move (i.e. rename(2)) is the only truly atomic file system operation.
|
|
101
|
-
*
|
|
102
|
-
* Since we use `npm` as the baseline the algorithm becomes:
|
|
103
|
-
*
|
|
104
|
-
* 1. Create the target `tmp` directory to untar into
|
|
105
|
-
* 2. Fetch the tarball using `npm pack` directly as a child process
|
|
106
|
-
* 3. Unpack the tarball into the target `tmp` directory
|
|
107
|
-
* - All `npm` package tarbals have a nested "package" directory in them.
|
|
108
|
-
* 4. Remove the tarball created by `npm pack`.
|
|
109
|
-
*
|
|
110
|
-
* @returns {Promise<string>} Full path of the cloned destination on local disk.
|
|
111
|
-
* @public
|
|
112
|
-
*/
|
|
113
|
-
async clone() {
|
|
114
|
-
await mkdirp(this.tmp.dir);
|
|
115
|
-
|
|
116
|
-
const tarballFile = await this.fetch(this.packageName, this.tmp.dir);
|
|
117
|
-
const tarball = path.join(this.tmp.dir, tarballFile);
|
|
118
|
-
|
|
119
|
-
await this.unpack(tarball, this.tmp.dir);
|
|
120
|
-
|
|
121
|
-
try {
|
|
122
|
-
await unlink(tarball);
|
|
123
|
-
} catch (e) {
|
|
124
|
-
// eslint ignore no-empty
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return path.join(this.tmp.dir, 'package');
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Fetch the tarball for the scaffold
|
|
132
|
-
*
|
|
133
|
-
* @param {string} packageName An npm package name (e.g. @gasket/app-template).
|
|
134
|
-
* @param {string} [dir] Directory the package needs to be downloaded.
|
|
135
|
-
* @returns {Promise} A promise represents if fetch succeeds or fails
|
|
136
|
-
* @public
|
|
137
|
-
*/
|
|
138
|
-
async fetch(packageName = this.packageName, dir = this.cwd) {
|
|
139
|
-
const argv = [
|
|
140
|
-
'pack', packageName,
|
|
141
|
-
'--loglevel', 'error',
|
|
142
|
-
'--prefer-online'
|
|
143
|
-
];
|
|
144
|
-
|
|
145
|
-
const { stdout } = await PackageManager.spawnNpm(argv, {
|
|
146
|
-
cwd: dir
|
|
147
|
-
}).catch(err => {
|
|
148
|
-
// TODO: dump npm stdout and stderr to a more useful location.
|
|
149
|
-
throw new Error(`Failed to fetch ${packageName}: npm exited with non-zero code\n${err.stdout}\n\n${err.stderr}`);
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
// Remark: we should **always** get a tarball, but is it
|
|
153
|
-
// worthwhile to validate that assumption?
|
|
154
|
-
return stdout.split('\n').filter(Boolean)[0];
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Unpack the tarball into given directory.
|
|
159
|
-
*
|
|
160
|
-
* Used from https://github.com/ywangii/await-targz under MIT.
|
|
161
|
-
*
|
|
162
|
-
* @param {String} tarball Path for tarball to be extracted
|
|
163
|
-
* @param {String} dir Path for the file to be extracted
|
|
164
|
-
* @returns {Promise} A promise represents if unpack tarball succeeds or fails
|
|
165
|
-
* @public
|
|
166
|
-
*/
|
|
167
|
-
async unpack(tarball, dir) {
|
|
168
|
-
return {
|
|
169
|
-
then: async (fulfill, reject) => {
|
|
170
|
-
const logOpts = { tarball, dir };
|
|
171
|
-
const readableStream = fs.createReadStream(tarball).once('error', this._logError(`fs.createReadStream`, logOpts));
|
|
172
|
-
const unzip = zlib.createUnzip().once('error', this._logError(`zlib.createUnzip`, logOpts));
|
|
173
|
-
const extract = tar.extract(dir).once('error', this._logError(`tar.extract`, logOpts));
|
|
174
|
-
|
|
175
|
-
pump(readableStream, unzip, extract, err => {
|
|
176
|
-
if (err) return reject(err);
|
|
177
|
-
fulfill();
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/*
|
|
184
|
-
* Simple logging helper for streams.
|
|
185
|
-
*/
|
|
186
|
-
_logError(msg, context) {
|
|
187
|
-
return err => { console.log(msg, context, err); };
|
|
188
|
-
}
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
//
|
|
192
|
-
// Expose Fetcher onto the exports for re-use
|
|
193
|
-
//
|
|
194
|
-
module.exports.Fetcher = Fetcher;
|
package/lib/utils/setup.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Step which runs before oclif any everything else is required.
|
|
3
|
-
* Handles the `--require` flags to allow for early importing of instrumentation
|
|
4
|
-
* modules and the like.
|
|
5
|
-
*
|
|
6
|
-
* @param {string[]} [argv] - CLI arguments - defaults to process.argv
|
|
7
|
-
*/
|
|
8
|
-
module.exports = function setup(argv = process.argv) {
|
|
9
|
-
// Using to parse args before mounting all of oclif
|
|
10
|
-
const getopts = require('getopts');
|
|
11
|
-
const opts = getopts(argv, {
|
|
12
|
-
alias: {
|
|
13
|
-
require: ['r']
|
|
14
|
-
}
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
if (opts.require) {
|
|
18
|
-
const required = Array.isArray(opts.require) ? opts.require : [opts.require];
|
|
19
|
-
required.forEach(module => {
|
|
20
|
-
require(
|
|
21
|
-
require.resolve(module, { paths: [process.cwd()] })
|
|
22
|
-
);
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
};
|