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.
Files changed (41) hide show
  1. package/lib/commands/create.js +40 -85
  2. package/lib/config/default-plugins.js +6 -6
  3. package/lib/index.d.ts +81 -25
  4. package/lib/index.js +7 -6
  5. package/lib/main.js +5 -4
  6. package/lib/scaffold/action-wrapper.js +8 -7
  7. package/lib/scaffold/actions/create-hooks.js +8 -9
  8. package/lib/scaffold/actions/generate-files.js +17 -15
  9. package/lib/scaffold/actions/global-prompts.js +11 -26
  10. package/lib/scaffold/actions/index.js +18 -21
  11. package/lib/scaffold/actions/install-modules.js +3 -6
  12. package/lib/scaffold/actions/link-modules.js +3 -5
  13. package/lib/scaffold/actions/load-preset.js +50 -72
  14. package/lib/scaffold/actions/mkdir.js +4 -4
  15. package/lib/scaffold/actions/post-create-hooks.js +6 -8
  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 +8 -7
  19. package/lib/scaffold/actions/prompt-hooks.js +10 -57
  20. package/lib/scaffold/actions/setup-pkg.js +17 -29
  21. package/lib/scaffold/actions/write-gasket-config.js +102 -17
  22. package/lib/scaffold/actions/write-pkg.js +5 -9
  23. package/lib/scaffold/config-builder.js +82 -7
  24. package/lib/scaffold/create-context.js +15 -21
  25. package/lib/scaffold/dump-error-context.js +5 -5
  26. package/lib/scaffold/files.js +2 -2
  27. package/lib/scaffold/utils.js +4 -97
  28. package/lib/utils/create-option.js +2 -4
  29. package/lib/utils/index.js +6 -16
  30. package/lib/utils/logo.js +1 -3
  31. package/lib/utils/process-args.js +1 -3
  32. package/lib/utils/process-command.js +3 -7
  33. package/lib/utils/process-options.js +1 -3
  34. package/lib/utils/warn-if-outdated.js +11 -9
  35. package/package.json +14 -12
  36. package/lib/scaffold/actions/apply-preset-config.js +0 -29
  37. package/lib/scaffold/actions/cli-version.js +0 -84
  38. package/lib/scaffold/actions/load-pkg-for-debug.js +0 -24
  39. package/lib/scaffold/create-engine.js +0 -21
  40. package/lib/scaffold/fetcher.js +0 -194
  41. 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
- };
@@ -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;
@@ -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
- };