yeoman-environment 3.5.1 → 3.8.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/cli/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  const {YeomanCommand} = require('../lib/util/command');
3
- const {printGroupedGenerator, environmentAction} = require('./utils');
4
3
  const packageJson = require('../package.json');
5
4
  const Env = require('..');
5
+ const {printGroupedGenerator, environmentAction} = require('./utils');
6
6
 
7
7
  const program = new YeomanCommand();
8
8
 
package/cli/utils.js CHANGED
@@ -5,15 +5,15 @@ const Env = require('..');
5
5
 
6
6
  const printGroupedGenerator = (generators, env) => {
7
7
  const grouped = groupBy(generators, 'packagePath');
8
- Object.entries(grouped).forEach(([packagePath, group]) => {
8
+ for (const [packagePath, group] of Object.entries(grouped)) {
9
9
  const namespace = env.toNamespace(group[0].namespace);
10
10
  console.log(` ${namespace.packageNamespace} at ${packagePath}`);
11
- group.forEach(generator => {
11
+ for (const generator of group) {
12
12
  const generatorNamespace = env.toNamespace(generator.namespace);
13
13
  console.log(` :${generatorNamespace.generator || 'app'}`);
14
- });
14
+ }
15
15
  console.log('');
16
- });
16
+ }
17
17
  console.log(`${generators.length} generators`);
18
18
  };
19
19
 
package/lib/adapter.js CHANGED
@@ -1,4 +1,3 @@
1
- 'use strict';
2
1
  const inquirer = require('inquirer');
3
2
  const diff = require('diff');
4
3
  const chalk = require('chalk');
package/lib/command.js CHANGED
@@ -1,4 +1,3 @@
1
- 'use strict';
2
1
  const debug = require('debug')('yeoman:environment:command');
3
2
  const {Option} = require('commander');
4
3
 
@@ -1,4 +1,3 @@
1
- 'use strict';
2
1
  const assert = require('assert');
3
2
  const semver = require('semver');
4
3
  const pacote = require('pacote');
@@ -32,9 +31,9 @@ composability.installLocalGenerators = async function (packages) {
32
31
  return true;
33
32
  }
34
33
  const toInstall = {};
35
- entries.forEach(([packageName, version]) => {
34
+ for (const [packageName, version] of entries) {
36
35
  toInstall[packageName] = version;
37
- });
36
+ }
38
37
  if (await this.repository.installPackages(toInstall)) {
39
38
  const packagesToLookup = entries.map(([packageName, _]) => packageName);
40
39
  this.lookupLocalPackages(packagesToLookup);
@@ -1,7 +1,7 @@
1
- 'use strict';
2
1
  const fs = require('fs');
3
2
  const path = require('path');
4
3
  const EventEmitter = require('events');
4
+ const crypto = require('crypto');
5
5
  const chalk = require('chalk');
6
6
  const _ = require('lodash');
7
7
  const GroupedQueue = require('grouped-queue');
@@ -11,13 +11,11 @@ const memFs = require('mem-fs');
11
11
  const FileEditor = require('mem-fs-editor');
12
12
  const debug = require('debug')('yeoman:environment');
13
13
  const isScoped = require('is-scoped');
14
- const crypto = require('crypto');
15
14
  const npmlog = require('npmlog');
15
+ const semver = require('semver');
16
+ const slash = require('slash');
16
17
  const {TrackerGroup} = require('are-we-there-yet');
17
-
18
- const {promisify} = require('util');
19
- const {pipeline: _pipeline} = require('stream');
20
- const pipeline = promisify(_pipeline);
18
+ const {pipeline, transform} = require('p-transform');
21
19
 
22
20
  const ENVIRONMENT_VERSION = require('../package.json').version;
23
21
  const Store = require('./store');
@@ -31,7 +29,6 @@ const {
31
29
  createCommitTransform,
32
30
  createConflicterCheckTransform,
33
31
  createConflicterStatusTransform,
34
- createEachFileTransform,
35
32
  createModifiedTransform,
36
33
  createYoRcTransform,
37
34
  createYoResolveTransform
@@ -47,13 +44,13 @@ function splitArgsFromString(argsString) {
47
44
  return result;
48
45
  }
49
46
  const quoteSeparatedArgs = argsString.split(/("[^"]*")/).filter(x => x);
50
- quoteSeparatedArgs.forEach(arg => {
47
+ for (const arg of quoteSeparatedArgs) {
51
48
  if (arg.match('\x22')) {
52
49
  result.push(arg.replace(/"/g, ''));
53
50
  } else {
54
51
  result = result.concat(arg.trim().split(' '));
55
52
  }
56
- });
53
+ }
57
54
  return result;
58
55
  }
59
56
 
@@ -332,12 +329,12 @@ class Environment extends Base {
332
329
  this.repository = new YeomanRepository(this.options.yeomanRepository);
333
330
 
334
331
  if (!this.options.experimental) {
335
- process.argv.forEach(value => {
332
+ for (const value of process.argv) {
336
333
  if (value === '--experimental') {
337
334
  this.options.experimental = true;
338
335
  debug('Set environment as experimental');
339
336
  }
340
- });
337
+ }
341
338
  }
342
339
 
343
340
  this.loadSharedOptions(this.options);
@@ -351,6 +348,8 @@ class Environment extends Base {
351
348
 
352
349
  // Store the YeomanCompose by paths and uniqueBy feature.
353
350
  this._composeStore = {};
351
+
352
+ this.enableConflicterIgnore = semver.satisfies(this.getVersion('mem-fs-editor'), '>= 9.2.0');
354
353
  }
355
354
 
356
355
  /**
@@ -583,6 +582,9 @@ class Environment extends Base {
583
582
  getPackagePath(namespace) {
584
583
  if (namespace.includes(':')) {
585
584
  const generator = this.get(namespace) || {};
585
+ if (generator.then) {
586
+ return generator.then(generator => generator.packagePath);
587
+ }
586
588
  return generator.packagePath;
587
589
  }
588
590
  const packagePaths = this.getPackagePaths(namespace) || [];
@@ -750,7 +752,7 @@ class Environment extends Base {
750
752
  'You can see available generators via ' +
751
753
  chalk.yellow('npm search yeoman-generator') + ' or via ' + chalk.yellow('http://yeoman.io/generators/') + '. \n' +
752
754
  'Install them with ' + chalk.yellow(`npm install ${generatorHint}`) + '.\n\n' +
753
- 'To see all your installed generators run ' + chalk.yellow('yo') + ' without any arguments. ' +
755
+ 'To see all your installed generators run ' + chalk.yellow('yo --generators') + '. ' +
754
756
  'Adding the ' + chalk.yellow('--help') + ' option will also show subgenerators. \n\n' +
755
757
  'If ' + chalk.yellow('yo') + ' cannot find the generator, run ' + chalk.yellow('yo doctor') + ' to troubleshoot your system.'
756
758
  );
@@ -759,8 +761,16 @@ class Environment extends Base {
759
761
  };
760
762
 
761
763
  maybeGenerator = maybeGenerator || this.get(namespaceOrPath);
762
- if (maybeGenerator && maybeGenerator.then) {
763
- return maybeGenerator.then(Generator => checkGenerator(Generator)).then(Generator => this.instantiate(Generator, args, options));
764
+ if (maybeGenerator && (maybeGenerator.then || maybeGenerator.prototype._postConstruct)) {
765
+ return Promise.resolve(maybeGenerator)
766
+ .then(Generator => checkGenerator(Generator))
767
+ .then(Generator => this.instantiate(Generator, args, options))
768
+ .then(async generator => {
769
+ if (!options.help && generator._postConstruct) {
770
+ await generator._postConstruct();
771
+ }
772
+ return generator;
773
+ });
764
774
  }
765
775
 
766
776
  return this.instantiate(checkGenerator(maybeGenerator), args, options);
@@ -855,9 +865,7 @@ class Environment extends Base {
855
865
  getAllGenerators() {
856
866
  return Object.fromEntries([
857
867
  ...Object.entries(this._generators),
858
- ...Object.entries(this._generatorsForPath).map(([root, generatorStore]) => {
859
- return Object.entries(generatorStore).map(([namespace, generator]) => ([`${root}#${namespace}`, generator]));
860
- }).flat()
868
+ ...Object.entries(this._generatorsForPath).flatMap(([root, generatorStore]) => Object.entries(generatorStore).map(([namespace, generator]) => ([`${root}#${namespace}`, generator])))
861
869
  ]);
862
870
  }
863
871
 
@@ -1112,28 +1120,39 @@ class Environment extends Base {
1112
1120
  throw new Error('Missing namespace');
1113
1121
  }
1114
1122
 
1123
+ // Normalize path
1124
+ let ns = slash(filepath);
1125
+
1126
+ // Ignore path before latest node_modules
1127
+ const REPOSITORY_PATH = '/node_modules/';
1128
+ if (ns.includes(REPOSITORY_PATH)) {
1129
+ ns = ns.slice(ns.lastIndexOf(REPOSITORY_PATH) + REPOSITORY_PATH.length, ns.length);
1130
+ }
1131
+
1115
1132
  // Cleanup extension and normalize path for differents OS
1116
- let ns = path.normalize(filepath.replace(new RegExp(escapeStrRe(path.extname(filepath)) + '$'), ''));
1133
+ const parsed = path.parse(ns);
1134
+ ns = parsed.dir ? `${parsed.dir}/${parsed.name}` : parsed.name;
1117
1135
 
1118
1136
  // Sort lookups by length so biggest are removed first
1119
- const nsLookups = _(lookups.concat(['..'])).map(found => path.normalize(found)).sortBy('length').value().reverse();
1137
+ const nsLookups = _([...lookups, '..']).map(found => slash(found)).sortBy('length').value().reverse();
1120
1138
 
1121
1139
  // If `ns` contains a lookup dir in its path, remove it.
1122
- ns = nsLookups.reduce((ns, lookup) => {
1140
+ for (let lookup of nsLookups) {
1123
1141
  // Only match full directory (begin with leading slash or start of input, end with trailing slash)
1124
- lookup = new RegExp(`(?:\\\\|/|^)${escapeStrRe(lookup)}(?=\\\\|/)`, 'g');
1125
- return ns.replace(lookup, '');
1126
- }, ns);
1142
+ lookup = new RegExp(`(?:/|^)${escapeStrRe(lookup)}(?=/)`, 'g');
1143
+ ns = ns.replace(lookup, '');
1144
+ }
1127
1145
 
1128
- const folders = ns.split(path.sep);
1146
+ const folders = ns.split('/');
1129
1147
  const scope = _.findLast(folders, folder => folder.indexOf('@') === 0);
1130
1148
 
1131
1149
  // Cleanup `ns` from unwanted parts and then normalize slashes to `:`
1132
1150
  ns = ns
1151
+ .replace(/\/\//g, '') // Remove double `/`
1133
1152
  .replace(/(.*generator-)/, '') // Remove before `generator-`
1134
- .replace(/[/\\](index|main)$/, '') // Remove `/index` or `/main`
1135
- .replace(/^[/\\]+/, '') // Remove leading `/`
1136
- .replace(/[/\\]+/g, ':'); // Replace slashes by `:`
1153
+ .replace(/\/(index|main)$/, '') // Remove `/index` or `/main`
1154
+ .replace(/^\//, '') // Remove leading `/`
1155
+ .replace(/\/+/g, ':'); // Replace slashes by `:`
1137
1156
 
1138
1157
  if (scope) {
1139
1158
  ns = `${scope}/${ns}`;
@@ -1199,12 +1218,12 @@ class Environment extends Base {
1199
1218
  stream,
1200
1219
  createModifiedTransform(),
1201
1220
  ...transformStreams,
1202
- createEachFileTransform(file => {
1221
+ transform(file => {
1203
1222
  if (log) {
1204
1223
  log.completeWork(10);
1205
1224
  npmlog.info('Completed', path.relative(this.logCwd, file.path));
1206
1225
  }
1207
- }, {autoForward: false, logName: 'environment:log'})
1226
+ }, 'environment:log')
1208
1227
  ).then(() => {
1209
1228
  if (log) {
1210
1229
  log.finish();
@@ -1221,10 +1240,23 @@ class Environment extends Base {
1221
1240
  commitSharedFs(stream = this.sharedFs.stream()) {
1222
1241
  return new Promise((resolve, reject) => {
1223
1242
  debug('committing files');
1243
+
1244
+ const conflicterStatus = {};
1245
+ if (this.enableConflicterIgnore) {
1246
+ conflicterStatus.fileActions = [{
1247
+ key: 'i',
1248
+ name: 'ignore, do not overwrite and remember (experimental)',
1249
+ value: ({relativeFilePath}) => {
1250
+ this.fs.append(`${this.cwd}/.yo-resolve`, `${relativeFilePath} skip`, {create: true});
1251
+ return 'skip';
1252
+ }
1253
+ }];
1254
+ }
1255
+
1224
1256
  this.fs.commit([
1225
1257
  createYoResolveTransform(this.conflicter),
1226
1258
  createYoRcTransform(),
1227
- createConflicterCheckTransform(this.conflicter),
1259
+ createConflicterCheckTransform(this.conflicter, conflicterStatus),
1228
1260
  createConflicterStatusTransform(),
1229
1261
  // Use custom commit transform due to out of order transform.
1230
1262
  createCommitTransform(this.fs)
@@ -1236,10 +1268,7 @@ class Environment extends Base {
1236
1268
  reject(error);
1237
1269
  return;
1238
1270
  }
1239
-
1240
- // Force to empty Conflicter queue.
1241
- this.conflicter.queue.once('end', () => resolve(value));
1242
- this.conflicter.queue.run();
1271
+ resolve(value);
1243
1272
  });
1244
1273
  });
1245
1274
  }
@@ -1260,14 +1289,21 @@ class Environment extends Base {
1260
1289
  } else {
1261
1290
  customCommitTask = this.commitSharedFs.bind(this);
1262
1291
  }
1292
+
1293
+ if (this.enableConflicterIgnore) {
1294
+ debug('Adding queueCommit event listener');
1295
+ this.sharedFs.once('change', queueCommit);
1296
+ }
1263
1297
  const result = customCommitTask();
1264
1298
  if (!result || !result.then) {
1265
1299
  done();
1266
1300
  return;
1267
1301
  }
1268
1302
  return result.then(() => {
1269
- debug('Adding queueCommit event listener');
1270
- this.sharedFs.once('change', queueCommit);
1303
+ if (!this.enableConflicterIgnore) {
1304
+ debug('Adding queueCommit event listener');
1305
+ this.sharedFs.once('change', queueCommit);
1306
+ }
1271
1307
  done();
1272
1308
  }
1273
1309
  , stop);
@@ -1,4 +1,3 @@
1
- 'use strict';
2
1
  const debug = require('debug')('yeoman:environment:features');
3
2
 
4
3
  module.exports = cls => class extends cls {
@@ -1,4 +1,3 @@
1
- 'use strict';
2
1
  const debug = require('debug')('yeoman:environment:compose');
3
2
  const EventEmitter = require('events');
4
3
  const path = require('path');
@@ -39,9 +38,7 @@ composability.lookupLocalNamespaces = function (namespacesToLookup) {
39
38
  namespacesToLookup = Array.isArray(namespacesToLookup) ? namespacesToLookup : [namespacesToLookup];
40
39
  namespacesToLookup = namespacesToLookup.map(ns => this.requireNamespace(ns));
41
40
  // Keep only those packages that has a compatible version.
42
- namespacesToLookup = namespacesToLookup.filter(ns => {
43
- return this.repository.verifyInstalledVersion(ns.generatorHint, ns.semver) !== undefined;
44
- });
41
+ namespacesToLookup = namespacesToLookup.filter(ns => this.repository.verifyInstalledVersion(ns.generatorHint, ns.semver) !== undefined);
45
42
  return this.lookupLocalPackages(namespacesToLookup.map(ns => ns.generatorHint));
46
43
  };
47
44
 
@@ -66,18 +63,13 @@ composability.lookupNamespaces = function (namespaces, options = {}) {
66
63
  const genPath = ns.generator.split(':').join('/');
67
64
  let filePatterns = [`${genPath}/index.?s`, `${genPath}.?s`];
68
65
  const lookups = options.lookups || this.lookups;
69
- filePatterns = lookups.map(prefix => {
70
- return filePatterns.map(pattern => path.join(prefix, pattern));
71
- }).reduce(
72
- (accumulator, currentValue) => accumulator.concat(currentValue),
73
- []
74
- );
66
+ filePatterns = lookups.flatMap(prefix => filePatterns.map(pattern => path.join(prefix, pattern)));
75
67
  nsOptions.filePatterns = filePatterns;
76
68
  nsOptions.singleResult = true;
77
69
  }
78
70
  return nsOptions;
79
71
  });
80
- return options_.map(opt => this.lookup({...opt, ...options})).reduce((acc, cur) => acc.concat(cur), []);
72
+ return options_.flatMap(opt => this.lookup({...opt, ...options}));
81
73
  };
82
74
 
83
75
  /**
@@ -100,7 +92,7 @@ composability.prepareEnvironment = async function (namespaces) {
100
92
 
101
93
  const assertMissing = missing => {
102
94
  if (missing.length > 0) {
103
- throw new Error(`Error preparing environment for ${missing.map(ns => ns.complete).join()}`);
95
+ throw new Error(`Error preparing environment for ${missing.map(ns => ns.complete).join(',')}`);
104
96
  }
105
97
  };
106
98
 
@@ -143,7 +135,7 @@ composability.prepareEnvironment = async function (namespaces) {
143
135
  return true;
144
136
  }
145
137
  // At last, try to lookup if install failed.
146
- this.lookupLocalNamespaces(missing.concat(toLookup));
138
+ this.lookupLocalNamespaces([...missing, ...toLookup]);
147
139
 
148
140
  assertMissing(updateMissing());
149
141
  return true;
@@ -276,18 +268,14 @@ class YeomanCompose extends EventEmitter {
276
268
  * @return {Promise} Promise YeomanWith
277
269
  */
278
270
  async with(namespace, generatorOptions, ...methodArgs) {
279
- let namespaces = [].concat(namespace);
271
+ let namespaces = [namespace].flat();
280
272
  debug(`Compose with generator ${namespace} at ${this._destinationRoot}`);
281
273
  namespaces = namespaces.map(namespace => this.env.requireNamespace(namespace));
282
274
  const brokenNamespace = namespaces.find(namespace => !namespace.generator);
283
275
  if (brokenNamespace) {
284
276
  throw new Error(`Generator part in namespace is missing: ${brokenNamespace.id}`);
285
277
  }
286
- const namespacesId = namespaces.map(namespace => {
287
- return namespace.instanceId && namespace.instanceId === '*' ? this._getInstanceNames(namespace.namespace).map(instanceId => {
288
- return namespace.with({instanceId}).id;
289
- }) : [namespace.id];
290
- }).flat();
278
+ const namespacesId = namespaces.flatMap(namespace => namespace.instanceId && namespace.instanceId === '*' ? this._getInstanceNames(namespace.namespace).map(instanceId => namespace.with({instanceId}).id) : [namespace.id]);
291
279
 
292
280
  const composedGenerators = await Promise.all(namespacesId.map(nsId => this.require(nsId, generatorOptions)));
293
281
  const composedProxy = new Proxy(composedGenerators, multipleGeneratorProxyHandler);
package/lib/namespace.js CHANGED
@@ -1,5 +1,3 @@
1
- 'use strict';
2
-
3
1
  const YeomanNamespace = require('./util/namespace');
4
2
 
5
3
  module.exports = cls => class extends cls {
@@ -1,4 +1,3 @@
1
- 'use strict';
2
1
  const path = require('path');
3
2
  const debug = require('debug')('yeoman:environment:package-manager');
4
3
  const preferredPm = require('preferred-pm');
package/lib/resolver.js CHANGED
@@ -1,7 +1,6 @@
1
- 'use strict';
2
- const arrify = require('arrify');
3
1
  const path = require('path');
4
2
  const fs = require('fs');
3
+ const arrify = require('arrify');
5
4
  const _ = require('lodash');
6
5
  const globby = require('globby');
7
6
  const debug = require('debug')('yeoman:environment');
@@ -324,8 +323,7 @@ packageLookup._getGlobalNpmPaths = function (filterPaths = true) {
324
323
  } else if (win32) {
325
324
  paths.push(path.join(process.env.APPDATA, 'npm/node_modules'));
326
325
  } else {
327
- paths.push('/usr/lib/node_modules');
328
- paths.push('/usr/local/lib/node_modules');
326
+ paths.push('/usr/lib/node_modules', '/usr/local/lib/node_modules');
329
327
  }
330
328
 
331
329
  // Add NVM prefix directory
@@ -406,7 +404,7 @@ resolver.alias = function (match, value) {
406
404
  return this;
407
405
  }
408
406
 
409
- const aliases = this.aliases.slice(0).reverse();
407
+ const aliases = [...this.aliases].reverse();
410
408
 
411
409
  return aliases.reduce((resolved, alias) => {
412
410
  if (!alias.match.test(resolved)) {
@@ -1,4 +1,3 @@
1
- 'use strict';
2
1
  const spawn = require('execa');
3
2
 
4
3
  /**
package/lib/store.js CHANGED
@@ -1,5 +1,3 @@
1
- 'use strict';
2
-
3
1
  const debug = require('debug')('yeoman:environment:store');
4
2
 
5
3
  const {requireOrImport} = require('./util/esm');
@@ -76,9 +74,7 @@ class Store {
76
74
  }
77
75
 
78
76
  if (maybeGenerator.then) {
79
- return maybeGenerator.then(esmGenerator => {
80
- return [esmGenerator, {...this._meta[namespace]}];
81
- });
77
+ return maybeGenerator.then(esmGenerator => [esmGenerator, {...this._meta[namespace]}]);
82
78
  }
83
79
 
84
80
  Object.assign(maybeGenerator, this._meta[namespace]);
@@ -1,8 +1,7 @@
1
- 'use strict';
2
- const Table = require('cli-table');
3
1
  const fs = require('fs');
4
- const dateFormat = require('dateformat');
5
2
  const path = require('path');
3
+ const Table = require('cli-table');
4
+ const dateFormat = require('dateformat');
6
5
  const prettyBytes = require('pretty-bytes');
7
6
 
8
7
  const {isBinaryFileSync} = require('isbinaryfile');
@@ -54,9 +54,9 @@ class YeomanCommand extends Command {
54
54
  */
55
55
  addGeneratorOptions(options) {
56
56
  options = options || {};
57
- Object.entries(options).forEach(([key, value]) => {
57
+ for (const [key, value] of Object.entries(options)) {
58
58
  this._addGeneratorOption(key, value);
59
- });
59
+ }
60
60
  return this;
61
61
  }
62
62
 
@@ -1,4 +1,3 @@
1
- 'use strict';
2
1
  const fs = require('fs');
3
2
  const path = require('path');
4
3
  const jsdiff = require('diff');
@@ -174,10 +173,10 @@ class Conflicter {
174
173
  * 4. If diverged, prepare and show up the file collision menu
175
174
  *
176
175
  * @param {Object} file - Vinyl file
177
- * @return {Promise} Promise a status string ('identical', 'create',
178
- * 'skip', 'force')
176
+ * @param {Object} [conflicterStatus] - Conflicter status
177
+ * @return {Promise<Vinyl>} Promise the Vinyl file
179
178
  */
180
- checkForCollision(file) {
179
+ checkForCollision(file, conflicterStatus) {
181
180
  const rfilepath = path.relative(this.cwd, file.path);
182
181
  if (file.conflicter) {
183
182
  this._log(file.conflicter, rfilepath);
@@ -195,8 +194,9 @@ class Conflicter {
195
194
  file.conflicterLog = 'create';
196
195
  return Promise.resolve(file);
197
196
  }
197
+ const isForce = () => this.force || (conflicterStatus && conflicterStatus.force);
198
198
 
199
- if (this.force) {
199
+ if (isForce()) {
200
200
  this._log('force', rfilepath);
201
201
  file.conflicter = 'force';
202
202
  return Promise.resolve(file);
@@ -222,7 +222,7 @@ class Conflicter {
222
222
 
223
223
  return new Promise((resolve, reject) => {
224
224
  this.queue.add('conflicts', next => {
225
- if (this.force) {
225
+ if (isForce()) {
226
226
  file.conflicter = 'force';
227
227
  this.adapter.log.force(rfilepath);
228
228
  resolve(file);
@@ -230,7 +230,7 @@ class Conflicter {
230
230
  return;
231
231
  }
232
232
  this.adapter.log.conflict(rfilepath);
233
- return this._ask(file, 1).then(action => {
233
+ return this._ask(file, 1, conflicterStatus).then(action => {
234
234
  this.adapter.log[action || 'force'](rfilepath);
235
235
  file.conflicter = action;
236
236
  resolve(file);
@@ -257,7 +257,7 @@ class Conflicter {
257
257
  * @param {Object} file vinyl file object
258
258
  * @param {Number} counter prompts
259
259
  */
260
- _ask(file, counter) {
260
+ _ask(file, counter, conflicterStatus) {
261
261
  if (file.conflicter) {
262
262
  return Promise.resolve(file.conflicter);
263
263
  }
@@ -266,6 +266,7 @@ class Conflicter {
266
266
  name: 'action',
267
267
  type: 'expand',
268
268
  message: `Overwrite ${rfilepath}?`,
269
+ pageSize: 20,
269
270
  choices: [
270
271
  {
271
272
  key: 'y',
@@ -309,9 +310,16 @@ class Conflicter {
309
310
  value: 'edit'
310
311
  }
311
312
  );
313
+ if (conflicterStatus && conflicterStatus.fileActions) {
314
+ prompt.choices.push(...conflicterStatus.fileActions);
315
+ }
312
316
  }
313
317
 
314
318
  return this.adapter.prompt([prompt]).then(result => {
319
+ if (typeof result.action === 'function') {
320
+ return result.action.call(this, {file, relativeFilePath: rfilepath});
321
+ }
322
+
315
323
  if (result.action === 'abort') {
316
324
  this.adapter.log.writeln('Aborting ...');
317
325
  throw AbortedError.create('Process aborted by user');
@@ -325,11 +333,15 @@ class Conflicter {
325
333
  throw new Error(`Recursive error ${prompt.message}`);
326
334
  }
327
335
 
328
- return this._ask(file, counter);
336
+ return this._ask(file, counter, conflicterStatus);
329
337
  }
330
338
 
331
339
  if (result.action === 'force') {
332
- this.force = true;
340
+ if (conflicterStatus) {
341
+ conflicterStatus.force = true;
342
+ } else {
343
+ this.force = true;
344
+ }
333
345
  }
334
346
 
335
347
  if (result.action === 'write') {
@@ -338,7 +350,7 @@ class Conflicter {
338
350
 
339
351
  if (result.action === 'reload') {
340
352
  if (this._detectConflict(file)) {
341
- return this._ask(file, counter);
353
+ return this._ask(file, counter, conflicterStatus);
342
354
  }
343
355
  return 'identical';
344
356
  }
@@ -353,7 +365,7 @@ class Conflicter {
353
365
  }]).then(answers => {
354
366
  file.contents = Buffer.from(answers.content || '', 'utf8');
355
367
  if (this._detectConflict(file)) {
356
- return this._ask(file, counter);
368
+ return this._ask(file, counter, conflicterStatus);
357
369
  }
358
370
  return 'skip';
359
371
  });
package/lib/util/log.js CHANGED
@@ -1,5 +1,4 @@
1
1
  /** @module env/log */
2
- 'use strict';
3
2
  const util = require('util');
4
3
  const EventEmitter = require('events');
5
4
  const _ = require('lodash');
@@ -14,22 +14,18 @@ module.exports = class YeomanNamespace {
14
14
  this.generator = parsed.generator;
15
15
  this.instanceId = parsed.instanceId;
16
16
  this.semver = parsed.semver;
17
- if (parsed.method) {
18
- this.methods = parsed.method.split('+');
19
- } else {
20
- this.methods = parsed.methods;
21
- }
17
+ this.methods = parsed.method ? parsed.method.split('+') : parsed.methods;
22
18
  this.flags = parsed.flags;
23
19
 
24
20
  // Populate flags
25
21
  if (this.flags) {
26
- Object.entries(flags).forEach(([name, value]) => {
22
+ for (const [name, value] of Object.entries(flags)) {
27
23
  if (this.flags === value) {
28
24
  this[name] = true;
29
25
  } else {
30
26
  delete this[name];
31
27
  }
32
- });
28
+ }
33
29
  }
34
30
 
35
31
  debug('Parsed namespace %o', this);
@@ -44,11 +40,11 @@ module.exports = class YeomanNamespace {
44
40
 
45
41
  const parsed = {complete};
46
42
  // Populate fields
47
- Object.entries(groups).forEach(([name, value]) => {
43
+ for (const [name, value] of Object.entries(groups)) {
48
44
  if (result[value]) {
49
45
  parsed[name] = result[value];
50
46
  }
51
- });
47
+ }
52
48
  return parsed;
53
49
  }
54
50
 
@@ -1,10 +1,11 @@
1
+ const {Transform} = require('stream');
1
2
  const debug = require('debug');
2
3
  const {default: PQueue} = require('p-queue');
3
- const {Transform} = require('stream');
4
4
 
5
5
  module.exports = class OOOTransform extends Transform {
6
6
  /**
7
7
  * @private
8
+ * @deprecated
8
9
  * Out Of Order Transform
9
10
  */
10
11
  constructor(options) {
@@ -1,5 +1,4 @@
1
1
  /** @module env/repository */
2
- 'use strict';
3
2
  const fs = require('fs');
4
3
  const path = require('path');
5
4
  const semver = require('semver');
@@ -92,9 +91,9 @@ class YeomanRepository {
92
91
  throw new Error(`Package ${packageName} already loaded`);
93
92
  }
94
93
 
95
- toCleanup.forEach(cache => {
94
+ for (const cache of toCleanup) {
96
95
  delete require.cache[cache];
97
- });
96
+ }
98
97
  }
99
98
 
100
99
  /**
@@ -177,14 +176,14 @@ class YeomanRepository {
177
176
  try {
178
177
  await this.reifyRepository({add: [...packagesArgs]});
179
178
  } catch (error) {
180
- Object.keys(packages).forEach(packageName => {
179
+ for (const packageName of Object.keys(packages)) {
181
180
  this.log.error(`${packageName} cannot be installed. ${error}`);
182
- });
181
+ }
183
182
  return false;
184
183
  }
185
- Object.keys(packages).forEach(packageName => {
184
+ for (const packageName of Object.keys(packages)) {
186
185
  this.log.ok(`${packageName} installed.`);
187
- });
186
+ }
188
187
  return true;
189
188
  }
190
189
 
@@ -1,15 +1,18 @@
1
- 'use strict';
2
-
3
- const {WError} = require('error');
4
1
  const fs = require('fs');
5
2
  const path = require('path');
3
+ const {WError} = require('error');
6
4
  const findUp = require('find-up');
7
5
  const minimatch = require('minimatch');
6
+ const {transform, passthrough, filter} = require('p-transform');
8
7
  const OOOTransform = require('./out-of-order-transform');
9
8
 
9
+ const {Minimatch} = minimatch;
10
+
10
11
  class YoResolveError extends WError {}
11
12
 
12
13
  /**
14
+ * @deprecated
15
+ *
13
16
  * Transform api should be avoided by generators without a executable.
14
17
  * May break between major yo versions.
15
18
  *
@@ -46,6 +49,8 @@ function fileIsModified(file) {
46
49
  }
47
50
 
48
51
  /**
52
+ * @deprecated
53
+ *
49
54
  * Transform api should be avoided by generators without a executable.
50
55
  * May break between major yo versions.
51
56
  *
@@ -103,8 +108,8 @@ function parseYoAttributesFile(yoAttributeFileName) {
103
108
  * Transform api should be avoided by generators without a executable.
104
109
  * May break between major yo versions.
105
110
  */
106
- function createConflicterCheckTransform(conflicter) {
107
- return createEachFileTransform(file => conflicter.checkForCollision(file), {logName: 'environment:conflicter-check'});
111
+ function createConflicterCheckTransform(conflicter, conflicterStatus) {
112
+ return passthrough(file => conflicter.checkForCollision(file, conflicterStatus), 'environment:conflicter-check');
108
113
  }
109
114
 
110
115
  /**
@@ -122,23 +127,21 @@ function getConflicterStatusForFile(conflicter, filePath, yoAttributeFileName =
122
127
 
123
128
  let fileStatus;
124
129
  if (yoResolveFiles) {
125
- yoResolveFiles.forEach(yoResolveFile => {
130
+ for (const yoResolveFile of yoResolveFiles) {
126
131
  if (conflicter.yoResolveByFile[yoResolveFile] === undefined) {
127
132
  conflicter.yoResolveByFile[yoResolveFile] = parseYoAttributesFile(yoResolveFile);
128
133
  }
129
- });
134
+ }
130
135
  yoResolveFiles
131
136
  .map(yoResolveFile => conflicter.yoResolveByFile[yoResolveFile])
132
137
  .map(attributes => attributes)
133
- .find(yoResolve => {
134
- return Object.entries(yoResolve).some(([pattern, status]) => {
135
- if (minimatch(filePath, pattern)) {
136
- fileStatus = status;
137
- return true;
138
- }
139
- return false;
140
- });
141
- });
138
+ .find(yoResolve => Object.entries(yoResolve).some(([pattern, status]) => {
139
+ if (minimatch(filePath, pattern)) {
140
+ fileStatus = status;
141
+ return true;
142
+ }
143
+ return false;
144
+ }));
142
145
  }
143
146
  return fileStatus;
144
147
  }
@@ -154,9 +157,9 @@ function getConflicterStatusForFile(conflicter, filePath, yoAttributeFileName =
154
157
  * @return {Transform} A Transform https://nodejs.org/api/stream.html#stream_class_stream_transform
155
158
  */
156
159
  function createYoResolveTransform(conflicter, yoResolveFileName) {
157
- return createEachFileTransform(file => {
160
+ return passthrough(file => {
158
161
  file.conflicter = file.conflicter || getConflicterStatusForFile(conflicter, file.path, yoResolveFileName);
159
- }, {logName: 'environment:yo-resolve'});
162
+ }, 'environment:yo-resolve');
160
163
  }
161
164
 
162
165
  /**
@@ -167,13 +170,10 @@ function createYoResolveTransform(conflicter, yoResolveFileName) {
167
170
  * @return {Transform} A Transform https://nodejs.org/api/stream.html#stream_class_stream_transform
168
171
  */
169
172
  function createYoRcTransform() {
170
- return createEachFileTransform(file => {
171
- const filename = path.basename(file.path);
172
- // Config file should not be processed by the conflicter. Force override.
173
- if (filename === '.yo-rc.json' || filename === '.yo-rc-global.json') {
174
- file.conflicter = 'force';
175
- }
176
- }, {logName: 'environment:yo-rc'});
173
+ // Config files should not be processed by the conflicter. Force override.
174
+ return patternSpy(file => {
175
+ file.conflicter = 'force';
176
+ }, '**/{.yo-rc.json,.yo-resolve,.yo-rc-global.json}', 'environment:yo-rc');
177
177
  }
178
178
 
179
179
  /**
@@ -185,7 +185,7 @@ function createYoRcTransform() {
185
185
  * @return {Transform} A Transform https://nodejs.org/api/stream.html#stream_class_stream_transform
186
186
  */
187
187
  function createConflicterStatusTransform() {
188
- return createFileTransform(function (file) {
188
+ return transform(file => {
189
189
  const action = file.conflicter;
190
190
 
191
191
  delete file.conflicter;
@@ -194,17 +194,16 @@ function createConflicterStatusTransform() {
194
194
  delete file.conflicterLog;
195
195
 
196
196
  if (!action && file.state) {
197
- this.push(file);
198
- return;
197
+ return file;
199
198
  }
200
199
 
201
200
  if (action === 'skip') {
202
201
  delete file.state;
203
202
  delete file.isNew;
204
- } else {
205
- this.push(file);
203
+ return undefined;
206
204
  }
207
- }, {logName: 'environment:conflicter-status'});
205
+ return file;
206
+ }, 'environment:conflicter-status');
208
207
  }
209
208
 
210
209
  /**
@@ -212,7 +211,7 @@ function createConflicterStatusTransform() {
212
211
  * May break between major yo versions.
213
212
  */
214
213
  function createModifiedTransform() {
215
- return createEachFileTransform({forwardUmodified: false, logName: 'environment:modified'});
214
+ return filter(file => fileIsModified(file), 'environment:modified');
216
215
  }
217
216
 
218
217
  /**
@@ -220,7 +219,30 @@ function createModifiedTransform() {
220
219
  * May break between major yo versions.
221
220
  */
222
221
  function createCommitTransform(memFsEditor) {
223
- return createFileTransform(file => memFsEditor.commitFileAsync(file), {logName: 'environment:commit'});
222
+ return transform(file => memFsEditor.commitFileAsync(file), 'environment:commit');
223
+ }
224
+
225
+ /**
226
+ * Conditional filter on pattern.
227
+ *
228
+ * @param {String} pattern - Minimatch pattern.
229
+ * @param {Object} options - Minimatch options.
230
+ */
231
+ function patternFilter(pattern, options) {
232
+ const minimatch = new Minimatch(pattern, options);
233
+ return filter(file => minimatch.match(file.path));
234
+ }
235
+
236
+ /**
237
+ * Conditional spy on pattern.
238
+ *
239
+ * @param {Function} spy.
240
+ * @param {String} pattern - Minimatch pattern.
241
+ * @param {Object} options - Minimatch options.
242
+ */
243
+ function patternSpy(spy, pattern, options) {
244
+ const minimatch = new Minimatch(pattern, options);
245
+ return passthrough(file => (minimatch.match(file.path) ? spy(file) : undefined));
224
246
  }
225
247
 
226
248
  module.exports = {
@@ -233,5 +255,7 @@ module.exports = {
233
255
  createModifiedTransform,
234
256
  createCommitTransform,
235
257
  createConflicterCheckTransform,
236
- createConflicterStatusTransform
258
+ createConflicterStatusTransform,
259
+ patternFilter,
260
+ patternSpy
237
261
  };
package/lib/util/util.js CHANGED
@@ -1,5 +1,4 @@
1
1
  /** @module env/util */
2
- 'use strict';
3
2
  const execa = require('execa');
4
3
  const GroupedQueue = require('grouped-queue');
5
4
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yeoman-environment",
3
- "version": "3.5.1",
3
+ "version": "3.8.1",
4
4
  "description": "Handles the lifecyle and bootstrapping of generators in a specific environment",
5
5
  "homepage": "http://yeoman.io",
6
6
  "author": "Yeoman",
@@ -12,6 +12,17 @@
12
12
  "lib"
13
13
  ],
14
14
  "main": "lib/environment.js",
15
+ "exports": {
16
+ ".": "./lib/environment.js",
17
+ "./cli/": "./cli/",
18
+ "./lib/": "./lib/",
19
+ "./lib/util/": "./lib/util/",
20
+ "./adapter": "./lib/adapter.js",
21
+ "./conflicter": "./lib/util/conflicter.js",
22
+ "./log": "./lib/util/log.js",
23
+ "./transform": "./lib/util/transform.js",
24
+ "./package.json": "./package.json"
25
+ },
15
26
  "keywords": [
16
27
  "development",
17
28
  "dev",
@@ -50,8 +61,8 @@
50
61
  "coverage": "nyc report --reporter=text-lcov | coveralls"
51
62
  },
52
63
  "dependencies": {
53
- "@npmcli/arborist": "^2.2.2",
54
- "are-we-there-yet": "^1.1.5",
64
+ "@npmcli/arborist": "^4.0.4",
65
+ "are-we-there-yet": "^2.0.0",
55
66
  "arrify": "^2.0.1",
56
67
  "binaryextensions": "^4.15.0",
57
68
  "chalk": "^4.1.0",
@@ -73,9 +84,10 @@
73
84
  "mem-fs": "^1.2.0 || ^2.0.0",
74
85
  "mem-fs-editor": "^8.1.2 || ^9.0.0",
75
86
  "minimatch": "^3.0.4",
76
- "npmlog": "^4.1.2",
87
+ "npmlog": "^5.0.1",
77
88
  "p-queue": "^6.6.2",
78
- "pacote": "^11.2.6",
89
+ "p-transform": "^1.3.0",
90
+ "pacote": "^12.0.2",
79
91
  "preferred-pm": "^3.0.3",
80
92
  "pretty-bytes": "^5.3.0",
81
93
  "semver": "^7.1.3",
@@ -92,19 +104,19 @@
92
104
  "devDependencies": {
93
105
  "coveralls": "^3.0.2",
94
106
  "cross-spawn": "^7.0.1",
95
- "fs-extra": "^9.0.1",
107
+ "fs-extra": "^10.0.0",
96
108
  "jsdoc": "^3.6.6",
97
- "mocha": "^8.0.1",
109
+ "mocha": "^9.1.3",
98
110
  "nyc": "^15.0.0",
99
111
  "prettier": "^2.2.1",
100
112
  "proxyquire": "^2.1.3",
101
- "sinon": "^9.0.2",
113
+ "sinon": "^12.0.1",
102
114
  "sinon-test": "^3.0.0",
103
115
  "tui-jsdoc-template": "^1.2.2",
104
116
  "xo": "^0.37.1",
105
117
  "yeoman-assert": "^3.1.1",
106
118
  "yeoman-generator": "^5.1.0",
107
- "yeoman-test": "^5.1.0"
119
+ "yeoman-test": "^6.2.0"
108
120
  },
109
121
  "xo": {
110
122
  "space": true,