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.
Files changed (40) hide show
  1. package/README.md +222 -2
  2. package/lib/commands/create.js +192 -0
  3. package/lib/config/default-plugins.js +7 -0
  4. package/lib/index.d.ts +205 -0
  5. package/lib/index.js +22 -9
  6. package/lib/main.js +19 -0
  7. package/lib/scaffold/action-wrapper.js +36 -0
  8. package/lib/scaffold/actions/apply-preset-config.js +29 -0
  9. package/lib/scaffold/actions/cli-version.js +84 -0
  10. package/lib/scaffold/actions/create-hooks.js +26 -0
  11. package/lib/scaffold/actions/generate-files.js +183 -0
  12. package/lib/scaffold/actions/global-prompts.js +161 -0
  13. package/lib/scaffold/actions/index.js +36 -0
  14. package/lib/scaffold/actions/install-modules.js +18 -0
  15. package/lib/scaffold/actions/link-modules.js +21 -0
  16. package/lib/scaffold/actions/load-pkg-for-debug.js +24 -0
  17. package/lib/scaffold/actions/load-preset.js +82 -0
  18. package/lib/scaffold/actions/mkdir.js +30 -0
  19. package/lib/scaffold/actions/post-create-hooks.js +33 -0
  20. package/lib/scaffold/actions/print-report.js +88 -0
  21. package/lib/scaffold/actions/prompt-hooks.js +90 -0
  22. package/lib/scaffold/actions/setup-pkg.js +47 -0
  23. package/lib/scaffold/actions/write-gasket-config.js +32 -0
  24. package/lib/scaffold/actions/write-pkg.js +23 -0
  25. package/lib/scaffold/config-builder.js +448 -0
  26. package/lib/scaffold/create-context.js +193 -0
  27. package/lib/scaffold/create-engine.js +21 -0
  28. package/lib/scaffold/dump-error-context.js +28 -0
  29. package/lib/scaffold/fetcher.js +194 -0
  30. package/lib/scaffold/files.js +30 -0
  31. package/lib/scaffold/utils.js +115 -0
  32. package/lib/utils/create-option.js +20 -0
  33. package/lib/utils/index.js +16 -0
  34. package/lib/utils/logo.js +14 -0
  35. package/lib/utils/process-args.js +39 -0
  36. package/lib/utils/process-command.js +52 -0
  37. package/lib/utils/process-options.js +46 -0
  38. package/lib/utils/setup.js +25 -0
  39. package/lib/utils/warn-if-outdated.js +76 -0
  40. package/package.json +23 -3
@@ -0,0 +1,194 @@
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;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Utility for plugins to add files and templates for generating
3
+ *
4
+ * @type {Files}
5
+ */
6
+ module.exports = class Files {
7
+ constructor() {
8
+ this.globSets = [];
9
+ }
10
+
11
+ /**
12
+ * Return array of globs
13
+ *
14
+ * @deprecated
15
+ * @returns {string[]} `globby` compatible patterns
16
+ */
17
+ get globs() {
18
+ return this.globSets.reduce((acc, cur) => [...acc, ...cur.globs], []);
19
+ }
20
+
21
+ /**
22
+ * Adds the specified `globby` compatible patterns, `globs`,
23
+ * into the set of all sources for this set of files.
24
+ * @param {string[]} globs - `globby` compatible patterns
25
+ * @param {Object} source - Plugin to blame if conflicts arise from this operation.
26
+ */
27
+ add({ globs, source }) {
28
+ this.globSets.push({ globs, source });
29
+ }
30
+ };
@@ -0,0 +1,115 @@
1
+ const path = require('path');
2
+ const untildify = require('untildify');
3
+ const { pluginIdentifier } = require('@gasket/resolve');
4
+
5
+ /**
6
+ * Pushes plugins short names for use in gasket.config.js
7
+ * Pushes the original plugin identifiers under rawPlugins
8
+ *
9
+ * @param {PluginDesc[]} plugins - Plugins names
10
+ * @param {CreateContext} context - Create context
11
+ */
12
+ function addPluginsToContext(plugins, context) {
13
+ context.rawPlugins = context.rawPlugins || [];
14
+ context.plugins = context.plugins || [];
15
+
16
+ context.rawPlugins = plugins.reduce((acc, cur) => {
17
+ const next = pluginIdentifier(cur);
18
+
19
+ // get the index of existing plugins by comparing short names
20
+ const idx = acc.map(p => pluginIdentifier(p).shortName).indexOf(next.shortName);
21
+
22
+ if (idx > -1) {
23
+ // if the existing plugin does not specify a version, use the next one
24
+ if (!pluginIdentifier(acc[idx]).version) {
25
+ acc[idx] = cur;
26
+ }
27
+ } else {
28
+ // if we don't already have an entry, push the next one
29
+ acc.push(cur);
30
+ }
31
+
32
+ return acc;
33
+ }, context.rawPlugins);
34
+
35
+ context.plugins = plugins.reduce((acc, cur) => {
36
+ const next = pluginIdentifier(cur).shortName;
37
+ if (!acc.includes(next)) acc.push(next);
38
+ return acc;
39
+ }, context.plugins);
40
+ }
41
+
42
+ /**
43
+ * Adds plugins and dependencies of the app package
44
+ *
45
+ * @param {PluginDesc[]|pluginIdentifier[]} plugins - Plugins names
46
+ * @param {PackageJson} pkg - Package builder
47
+ * @param {String} [field] - Dependency type (Default: dependencies)
48
+ */
49
+ function addPluginsToPkg(plugins, pkg, field = 'dependencies') {
50
+ const pluginIds = plugins.map(p => pluginIdentifier(p).withVersion());
51
+ pkg.add(field, pluginIds.reduce((acc, p) => {
52
+ if (!acc[p.fullName]) {
53
+ acc[p.fullName] = p.version;
54
+ }
55
+ return acc;
56
+ }, {}));
57
+ }
58
+
59
+ /**
60
+ * Look up registry version of provided plugin descriptions w/o versions set.
61
+ *
62
+ * @param {PluginDesc[]|pluginIdentifier[]} plugins - Plugins names
63
+ * @param {PackageManager} pkgManager - Package manager instance
64
+ * @returns {Promise<pluginIdentifier[]>} plugins
65
+ */
66
+ async function getPluginsWithVersions(plugins, pkgManager) {
67
+ return await Promise.all(
68
+ plugins.map(async name => {
69
+ const id = pluginIdentifier(name);
70
+ if (id.version != null) return id;
71
+ const version = (await pkgManager.info([id.fullName, 'version'])).data;
72
+ return id.withVersion(`^${version}`);
73
+ })
74
+ );
75
+ }
76
+
77
+ /**
78
+ * Takes a file path and transforms it to be absolute if not already
79
+ *
80
+ * @param {string} filepath - Path to file that may be relative or have tildy
81
+ * @returns {string} absolute filepath
82
+ */
83
+ function ensureAbsolute(filepath) {
84
+ filepath = untildify(filepath);
85
+ if (path.isAbsolute(filepath)) return filepath;
86
+ return path.resolve(process.cwd(), filepath);
87
+ }
88
+
89
+
90
+ /**
91
+ * Parses JSON file or string to assign to context
92
+ *
93
+ * @param {CreateContext} context - Create context.
94
+ * @param {Object} configFlags - flags to read config from
95
+ * @param {string} configFlags.config - JSON string of config values
96
+ * @param {string} configFlags.configFile - path to JSON file of config values
97
+ */
98
+ function readConfig(context, { config, configFile }) {
99
+ if (config) {
100
+ const parsedConfig = JSON.parse(config);
101
+ Object.assign(context, parsedConfig);
102
+ } else if (configFile) {
103
+ const parsedConfigFile = require(path.resolve(context.cwd, configFile));
104
+ Object.assign(context, parsedConfigFile);
105
+ }
106
+ }
107
+
108
+
109
+ module.exports = {
110
+ addPluginsToContext,
111
+ addPluginsToPkg,
112
+ getPluginsWithVersions,
113
+ ensureAbsolute,
114
+ readConfig
115
+ };
@@ -0,0 +1,20 @@
1
+ const { Option } = require('commander');
2
+
3
+ /**
4
+ * createOption - Create a commander option
5
+ * @param {CLICommandOption} definition - The option configuration
6
+ * @returns {CommnaderOption} option
7
+ */
8
+ function createOption(definition) {
9
+ const option = new Option(...definition.options);
10
+ const { defaultValue, conflicts, parse, hidden, required } = definition;
11
+
12
+ if (conflicts.length) option.conflicts(definition.conflicts);
13
+ if (hidden) option.hideHelp();
14
+ if (typeof defaultValue !== 'undefined') option.default(defaultValue);
15
+ if (parse) option.argParser(parse);
16
+ if (required) option.makeOptionMandatory();
17
+ return option;
18
+ }
19
+
20
+ module.exports = createOption;
@@ -0,0 +1,16 @@
1
+ const processCommand = require('./process-command');
2
+ const processArgs = require('./process-args');
3
+ const processOptions = require('./process-options');
4
+ const createOption = require('./create-option');
5
+ const logo = require('./logo');
6
+ const warnIfOutdated = require('./warn-if-outdated');
7
+
8
+
9
+ module.exports = {
10
+ processCommand,
11
+ processArgs,
12
+ processOptions,
13
+ createOption,
14
+ logo,
15
+ warnIfOutdated
16
+ };
@@ -0,0 +1,14 @@
1
+ const logo = `
2
+ ▄▄▄▄████▀▀█▄
3
+ ▄███▀▀▀▀▀▀▀█▄▄██
4
+ ▄██▀ ▄▄▄▄▄ ▀██▌ ▄▄▄
5
+ ▄██▀ ███▀▀▀▀█ ██ ██ ██
6
+ █▀▀█ ██▌ ▐█▌ ▄▀▀▀█▄ ▄█▀▀▀▄ ██ ▐██▀ ▄█▀▀█▄ ▀██▀▀
7
+ █▄▄█ ██▌ ▀▀██ ▐█▌ ▄▄▄▄██ ▀▀█▄▄▄ █████ ██▄▄██ ██
8
+ ▀██▄ ███▄▄▄▄██ ██ ▀▄▄▄▀█▄ ▀▄▄▄█▀ ▄██▄ ██▄ ▀█▄▄▄▄ ▀█▄▄
9
+ ▀██▄ ▀▀▀ ▀▀ ▄██▌
10
+ ▀███▄▄▄▄▄▄▄█▀▀██
11
+ ▀▀▀▀████▄▄█▀
12
+ `;
13
+
14
+ module.exports = logo;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * isValidArg - Validates the argument configuration
3
+ * @param {CLICommandArg} arg The argument configuration
4
+ * @returns {boolean} True if valid, false otherwise
5
+ */
6
+ function isValidArg(arg) {
7
+ const keys = Object.keys(arg);
8
+ if (arg.required && arg.default) return false;
9
+ return keys.length &&
10
+ keys.length <= 4 &&
11
+ arg.name && arg.description;
12
+ }
13
+
14
+ /**
15
+ * processArgs - Process the arguments configuration
16
+ * @param {CLICommandArg[]} args Array of argument configurations
17
+ * @returns {ProccesedCLICommandArg[]} Array of argument definitions
18
+ */
19
+ function processArgs(args) {
20
+ if (!Array.isArray(args) || !args.every(isValidArg)) throw new Error('Invalid argument(s) configuration');
21
+
22
+ return args.reduce((acc, arg) => {
23
+ const def = [];
24
+ const { name, description, required = false, default: defaultValue } = arg;
25
+ if (required) {
26
+ def.push(`<${name}>`, description);
27
+ } else {
28
+ def.push(`[${name}]`, description);
29
+ }
30
+
31
+ if (defaultValue) def.push(defaultValue);
32
+
33
+ acc.push(def);
34
+ return acc;
35
+
36
+ }, []);
37
+ }
38
+
39
+ module.exports = processArgs;
@@ -0,0 +1,52 @@
1
+ const { Command } = require('commander');
2
+ const program = new Command();
3
+ const processArgs = require('./process-args');
4
+ const processOptions = require('./process-options');
5
+ const createOption = require('./create-option');
6
+
7
+ /**
8
+ * isValidCommand - Validates the command configuration
9
+ * @param {CLICommand} command The command configuration
10
+ * @returns {boolean} True if valid, false otherwise
11
+ */
12
+ function isValidCommand(command) {
13
+ const keys = Object.keys(command);
14
+ return keys.length &&
15
+ keys.length <= 7 &&
16
+ command &&
17
+ command.id &&
18
+ command.description &&
19
+ command.action &&
20
+ typeof command.action === 'function';
21
+ }
22
+
23
+ /**
24
+ * processCommand - Process the command configuration
25
+ * @param {CLICommand} command The command configuration
26
+ * @returns {ProccesedCLICommand} The command instance
27
+ */
28
+ function processCommand(command) {
29
+ if (!isValidCommand(command)) throw new Error('Invalid command configuration');
30
+ const { id, description, action, args, options, hidden = false, default: isDefault = false } = command;
31
+ const cmd = program
32
+ .command(id)
33
+ .description(description)
34
+ .action(action);
35
+
36
+ if (args) {
37
+ const cmdArgs = processArgs(args);
38
+ cmdArgs.forEach(arg => cmd.argument(...arg));
39
+ }
40
+
41
+ if (options) {
42
+ const cmdOpts = processOptions(options);
43
+ cmdOpts.forEach(o => {
44
+ const option = createOption(o);
45
+ cmd.addOption(option);
46
+ });
47
+ }
48
+
49
+ return { command: cmd, hidden, isDefault };
50
+ }
51
+
52
+ module.exports = processCommand;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * isValidOption - Validate the option
3
+ * @param {CLICommandOption} option The option configuration
4
+ * @returns {boolean} True if valid, false otherwise
5
+ */
6
+ function isValidOption(option) {
7
+ const keys = Object.keys(option);
8
+ return keys.length &&
9
+ keys.length <= 5 &&
10
+ option.name &&
11
+ option.description;
12
+ }
13
+
14
+ /**
15
+ * processOptions - Process the options configuration
16
+ * @param {CLICommandOption[]} options Array of option configurations
17
+ * @returns {ProccesedCLICommandOption[]} Processed option definitions
18
+ */
19
+ function processOptions(options) {
20
+ if (!Array.isArray(options) || !options.every(isValidOption)) throw new Error('Invalid option(s) configuration');
21
+
22
+ return options.reduce((acc, option) => {
23
+ const def = [];
24
+ const {
25
+ name,
26
+ description,
27
+ required = false,
28
+ short,
29
+ parse,
30
+ type = 'string',
31
+ conflicts = [],
32
+ hidden = false,
33
+ default: defaultValue
34
+ } = option;
35
+
36
+ const format = required ? `<${name}>` : `[${name}]`;
37
+ const flags = short ? `-${short}, --${name}` : `--${name}`;
38
+
39
+ def.push(`${flags}${type !== 'boolean' ? ` ${format}` : ''}`, description);
40
+ acc.push({ options: def, conflicts, hidden, defaultValue, parse, required });
41
+
42
+ return acc;
43
+ }, []);
44
+ }
45
+
46
+ module.exports = processOptions;
@@ -0,0 +1,25 @@
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
+ };
@@ -0,0 +1,76 @@
1
+ const { runShellCommand } = require('@gasket/utils');
2
+ const semver = require('semver');
3
+ const chalk = require('chalk');
4
+ const { readFile, writeFile } = require('fs/promises');
5
+ const path = require('path');
6
+
7
+ const cachePath = path.join(__dirname, '..', '..', '.cache'); // Place at root of gasket-cli
8
+ const LATEST_VERSION = 'latestVersion';
9
+ const LATEST_VERSION_UPDATE_TIME = 'latestVersionUpdateTime';
10
+
11
+ /**
12
+ * Checks if the latest version update time is older than seven days
13
+ * @param {number} currentTime - current time in milliseconds
14
+ * @param {number} latestVersionUpdateTime - latest version update time in milliseconds
15
+ * @returns {boolean} - true if latestVersionUpdateTime is older than seven days
16
+ */
17
+ function isOlderThanSevenDays(currentTime, latestVersionUpdateTime) {
18
+ const sevenDaysInMillis = 7 * 24 * 60 * 60 * 1000; // Seven days in milliseconds
19
+ const timeDifference = Math.abs(currentTime - latestVersionUpdateTime);
20
+ return timeDifference > sevenDaysInMillis;
21
+ }
22
+
23
+ /**
24
+ * Fetches the latest version of a package from npm
25
+ * @param {string} pkgName - package name
26
+ * @param {number} currentTime - current time in milliseconds
27
+ * @param {object} cache - cache object
28
+ * @returns {string} - latest version of the package
29
+ */
30
+ async function getLatestVersion(pkgName, currentTime, cache) {
31
+ if (
32
+ !cache[LATEST_VERSION_UPDATE_TIME] ||
33
+ !cache[LATEST_VERSION] ||
34
+ isOlderThanSevenDays(currentTime, cache[LATEST_VERSION_UPDATE_TIME])
35
+ ) {
36
+ try {
37
+ const cmdResult = await runShellCommand('npm', ['view', pkgName, 'version'], {});
38
+ if (cmdResult?.stdout) {
39
+ const latestVersion = cmdResult.stdout.trim();
40
+ cache[LATEST_VERSION_UPDATE_TIME] = currentTime;
41
+ cache[LATEST_VERSION] = latestVersion;
42
+ await writeFile(cachePath, JSON.stringify(cache));
43
+ return latestVersion;
44
+ }
45
+ } catch (error) {
46
+ console.error('Error fetching latest version:', error);
47
+ }
48
+ } else {
49
+ return cache[LATEST_VERSION];
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Warns if the package is outdated
55
+ * @param {string} pkgName - package name
56
+ * @param {string} currentVersion - current version of the package
57
+ */
58
+ module.exports = async function warnIfOutdated(pkgName, currentVersion) {
59
+ const currentTime = new Date().getTime();
60
+ let cache = {};
61
+
62
+ try {
63
+ const file = await readFile(cachePath, 'utf8');
64
+ cache = JSON.parse(file);
65
+ } catch (error) {
66
+ // console.error('Error reading cache file:', error);
67
+ }
68
+
69
+ const latestVersion = await getLatestVersion(pkgName, currentTime, cache);
70
+
71
+ if (typeof latestVersion === 'string' && semver.gt(latestVersion, currentVersion)) {
72
+ console.warn(
73
+ ` ${chalk.yellow('›')} Warning: ${pkgName} update available from ${chalk.green(latestVersion)} to ${chalk.green(currentVersion)}`
74
+ );
75
+ }
76
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-gasket-app",
3
- "version": "7.0.0-cli.7",
3
+ "version": "7.0.0-next.1",
4
4
  "description": "starter pack for creating a gasket app",
5
5
  "main": "lib",
6
6
  "bin": {
@@ -31,10 +31,30 @@
31
31
  },
32
32
  "homepage": "https://github.com/godaddy/gasket/tree/main/packages/create-gasket-app",
33
33
  "dependencies": {
34
- "@gasket/cli": "^7.0.0-cli.7"
34
+ "@gasket/engine": "^7.0.0-next.1",
35
+ "@gasket/plugin-git": "^7.0.0-next.1",
36
+ "@gasket/plugin-lifecycle": "^7.0.0-next.1",
37
+ "@gasket/plugin-logger": "^7.0.0-next.1",
38
+ "@gasket/plugin-metadata": "^7.0.0-next.1",
39
+ "@gasket/plugin-start": "^7.0.0-next.1",
40
+ "@gasket/resolve": "^7.0.0-next.1",
41
+ "@gasket/utils": "^7.0.0-next.1",
42
+ "commander": "^12.0.0",
43
+ "deepmerge": "^4.3.1",
44
+ "handlebars": "^4.7.8",
45
+ "inquirer": "6.5.2",
46
+ "json5": "^2.2.3",
47
+ "mkdirp": "^1.0.4",
48
+ "ora": "3.4.0",
49
+ "pump": "^3.0.0",
50
+ "semver": "^7.5.4",
51
+ "tar-fs": "^2.0.0",
52
+ "untildify": "^4.0.0"
35
53
  },
36
54
  "devDependencies": {
37
55
  "cross-env": "^7.0.3",
56
+ "diagnostics": "^2.0.2",
57
+ "dotenv": "^16.4.5",
38
58
  "eslint": "^8.56.0",
39
59
  "eslint-config-godaddy": "^7.0.2",
40
60
  "eslint-plugin-jest": "^27.6.3",
@@ -57,5 +77,5 @@
57
77
  "eslintIgnore": [
58
78
  "test/fixtures"
59
79
  ],
60
- "gitHead": "465ca0b08a4a7513935fa60b186a4d1238b12fa6"
80
+ "gitHead": "05300b094247de1fdfaf06afcdfed24280e3f084"
61
81
  }