yeoman-generator 5.4.2 → 5.6.0

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.
@@ -6,8 +6,14 @@ const dargs = require('dargs');
6
6
  const chalk = require('chalk');
7
7
 
8
8
  /**
9
+ * @deprecated since version 5.0.0.
10
+ * Not included by default.
9
11
  * @mixin
10
12
  * @alias actions/install
13
+ *
14
+ * @example
15
+ * const Generator = require('yeoman-generator');
16
+ * _.extend(Generator.prototype, require('yeoman-generator/lib/actions/install'));
11
17
  */
12
18
  const install = module.exports;
13
19
 
@@ -1,10 +1,10 @@
1
1
  'use strict';
2
2
 
3
- /**
4
- * @mixin
5
- * @alias actions/packege-json
6
- */
7
3
  module.exports = (cls) =>
4
+ /**
5
+ * @mixin
6
+ * @alias actions/package-json
7
+ */
8
8
  class extends cls {
9
9
  /**
10
10
  * @private
@@ -42,6 +42,9 @@ module.exports = (cls) =>
42
42
  /**
43
43
  * Add dependencies to the destination the package.json.
44
44
  *
45
+ * Environment watches for package.json changes at `this.env.cwd`, and triggers an package manager install if it has been committed to disk.
46
+ * If package.json is at a different folder, like a changed generator root, propagate it to the Environment like `this.env.cwd = this.destinationPath()`.
47
+ *
45
48
  * @param {Object|string|string[]} dependencies
46
49
  * @return {Promise} a 'packageName: packageVersion' object
47
50
  */
@@ -54,6 +57,9 @@ module.exports = (cls) =>
54
57
  /**
55
58
  * Add dependencies to the destination the package.json.
56
59
  *
60
+ * Environment watches for package.json changes at `this.env.cwd`, and triggers an package manager install if it has been committed to disk.
61
+ * If package.json is at a different folder, like a changed generator root, propagate it to the Environment like `this.env.cwd = this.destinationPath()`.
62
+ *
57
63
  * @param {Object|string|string[]} dependencies
58
64
  * @return {Promise} a 'packageName: packageVersion' object
59
65
  */
package/lib/index.js CHANGED
@@ -101,8 +101,8 @@ class Generator extends Base {
101
101
  * Every generator should extend this base class.
102
102
  *
103
103
  * @constructor
104
+ * @augments actions/package-json
104
105
  * @mixes actions/help
105
- * @mixes actions/install
106
106
  * @mixes actions/spawn-command
107
107
  * @mixes actions/user
108
108
  * @mixes actions/fs
@@ -335,10 +335,27 @@ class Generator extends Base {
335
335
  *
336
336
  * @param {Object[]} priorities - Priorities
337
337
  * @param {String} priorities.priorityName - Priority name
338
- * @param {String} priorities.before - The new priority will be queued before the `before` priority.
338
+ * @param {String} [priorities.before] - The new priority will be queued before the `before` priority. Required for new priorities.
339
339
  * @param {String} [priorities.queueName] - Name to be used at grouped-queue
340
+ * @param {boolean} [priorities.edit] - Edit a priority
341
+ * @param {boolean} [priorities.skip] - Queued manually only
342
+ * @param {Object[]|function} [priorities.args] - Arguments to pass to tasks
340
343
  */
341
344
  registerPriorities(priorities) {
345
+ priorities = priorities.filter((priority) => {
346
+ if (priority.edit) {
347
+ const queue = this._queues[priority.priorityName];
348
+ if (!queue) {
349
+ throw new Error(
350
+ `Error editing priority ${priority.priorityName}, not found`
351
+ );
352
+ }
353
+
354
+ Object.assign(queue, {...priority, edit: undefined});
355
+ }
356
+
357
+ return !priority.edit;
358
+ });
342
359
  const customPriorities = priorities.map((customPriority) => {
343
360
  // Keep backward compatibility with name
344
361
  const newPriority = {
@@ -849,40 +866,25 @@ class Generator extends Base {
849
866
  }
850
867
 
851
868
  /**
852
- * Schedule methods on a run queue.
869
+ * Schedule tasks from a group on a run queue.
853
870
  *
854
871
  * @param {Object} taskGroup: Object containing tasks.
855
872
  * @param {TaskOptions} [taskOptions]: options.
856
873
  */
857
874
  queueTaskGroup(taskGroup, taskOptions) {
858
- // Run each queue items
859
- _.each(taskGroup, (newMethod, newMethodName) => {
860
- if (!_.isFunction(newMethod) || !methodIsValid(newMethodName)) return;
861
-
862
- this.queueTask({
863
- ...taskOptions,
864
- method: newMethod,
865
- taskName: newMethodName
866
- });
875
+ this.extractTasksFromGroup(taskGroup, taskOptions).forEach((task) => {
876
+ this.queueTask(task);
867
877
  });
868
878
  }
869
879
 
870
880
  /**
871
881
  * @private
872
- * Schedule a generator's method on a run queue.
882
+ * Extract tasks from a priority.
873
883
  *
874
884
  * @param {String} name: The method name to schedule.
875
885
  * @param {TaskOptions} [taskOptions]: options.
876
886
  */
877
- queueOwnTask(name, taskOptions = {}) {
878
- const {taskPrefix} = this.features;
879
- const propertyName = taskPrefix ? `${taskPrefix}${name}` : name;
880
- const property = Object.getOwnPropertyDescriptor(
881
- Object.getPrototypeOf(this),
882
- propertyName
883
- );
884
- const item = property.value ? property.value : property.get.call(this);
885
-
887
+ extractTasksFromPriority(name, taskOptions = {}) {
886
888
  const priority = this._queues[name];
887
889
  taskOptions = {
888
890
  ...priority,
@@ -891,20 +893,63 @@ class Generator extends Base {
891
893
  ...taskOptions
892
894
  };
893
895
 
894
- // Name points to a function; run it!
896
+ if (taskOptions.auto && priority && priority.skip) {
897
+ return [];
898
+ }
899
+
900
+ const {taskPrefix} = this.features;
901
+ const propertyName = taskPrefix ? `${taskPrefix}${name}` : name;
902
+ const property = Object.getOwnPropertyDescriptor(
903
+ taskOptions.taskOrigin || Object.getPrototypeOf(this),
904
+ propertyName
905
+ );
906
+ if (!property) return [];
907
+
908
+ const item = property.value ? property.value : property.get.call(this);
909
+
910
+ // Name points to a function; single task
895
911
  if (typeof item === 'function') {
896
- taskOptions.taskName = name;
897
- taskOptions.method = item;
898
- this.queueTask(taskOptions);
899
- return;
912
+ return [{...taskOptions, taskName: name, method: item}];
900
913
  }
901
914
 
902
- // Not a queue hash; stop
903
- if (!priority) {
904
- return;
915
+ if (!item || !priority) {
916
+ return [];
905
917
  }
906
918
 
907
- this.queueTaskGroup(item, taskOptions);
919
+ return this.extractTasksFromGroup(item, taskOptions);
920
+ }
921
+
922
+ /**
923
+ * @private
924
+ * Extract tasks from group.
925
+ *
926
+ * @param {Object} group: Task group.
927
+ * @param {TaskOptions} [taskOptions]: options.
928
+ */
929
+ extractTasksFromGroup(group, taskOptions) {
930
+ return Object.entries(group)
931
+ .map(([taskName, method]) => {
932
+ if (typeof method !== 'function' || !methodIsValid(taskName)) return;
933
+ return {
934
+ ...taskOptions,
935
+ method,
936
+ taskName
937
+ };
938
+ })
939
+ .filter(Boolean);
940
+ }
941
+
942
+ /**
943
+ * @private
944
+ * Schedule a generator's method on a run queue.
945
+ *
946
+ * @param {String} name: The method name to schedule.
947
+ * @param {TaskOptions} [taskOptions]: options.
948
+ */
949
+ queueOwnTask(name, taskOptions) {
950
+ this.extractTasksFromPriority(name, taskOptions).forEach((task) =>
951
+ this.queueTask(task)
952
+ );
908
953
  }
909
954
 
910
955
  /**
@@ -975,6 +1020,8 @@ class Generator extends Base {
975
1020
 
976
1021
  for (const methodName of validMethods)
977
1022
  this.queueOwnTask(methodName, taskOptions);
1023
+
1024
+ this.emit('queueOwnTasks');
978
1025
  }
979
1026
 
980
1027
  /**
@@ -983,24 +1030,11 @@ class Generator extends Base {
983
1030
  * @param {Task} task: Task to be queued.
984
1031
  */
985
1032
  queueTask(task) {
986
- const {reject, queueName = 'default', taskName: methodName, method} = task;
987
- const once = task.once ? methodName : undefined;
988
-
989
- const priority = Object.entries(this._queues).find(
990
- ([_, options]) => options.queueName === queueName
991
- );
992
- const priorityName = priority ? priority[0] : undefined;
1033
+ const {queueName = 'default', taskName: methodName, run, once} = task;
993
1034
 
994
- const self = this;
995
1035
  const {runLoop} = this.env;
996
- let namespace = '';
997
- if (self.options && self.options.namespace) {
998
- namespace = self.options.namespace;
999
- }
1000
-
1001
- // Task status allows to ignore (cancel) current queued tasks.
1002
- // Each queueOwnTasks (complete run) create a new taskStatus.
1003
- const taskStatus = this._taskStatus || {};
1036
+ const {_taskStatus: taskStatus, options = {}} = this;
1037
+ const {namespace = ''} = options;
1004
1038
 
1005
1039
  debug(
1006
1040
  `Queueing ${namespace}#${methodName} with options %o`,
@@ -1010,59 +1044,94 @@ class Generator extends Base {
1010
1044
  queueName,
1011
1045
  // Run-queue's done(continue), pause
1012
1046
  (continueQueue) => {
1013
- debug(`Running ${namespace}#${methodName}`);
1014
- self.emit(`method:${methodName}`);
1015
- const taskCancelled = task.cancellable && taskStatus.cancelled;
1016
- if (taskCancelled) {
1017
- continueQueue();
1018
- return;
1019
- }
1020
-
1021
- runAsync(function () {
1022
- self.async = () => this.async();
1023
- self.runningState = {namespace, queueName, methodName};
1024
- return method.apply(self, self.args);
1025
- })()
1026
- .then(() => {
1027
- delete self.runningState;
1028
- const eventName = `done$${
1029
- namespace || 'unknownnamespace'
1030
- }#${methodName}`;
1031
- debug(`Done event ${eventName}`);
1032
- self.env.emit(eventName, {
1033
- namespace,
1034
- generator: self,
1035
- queueName,
1036
- priorityName
1037
- });
1038
- continueQueue();
1039
- })
1040
- .catch((error) => {
1041
- debug(
1042
- `An error occured while running ${namespace}#${methodName}`,
1043
- error
1044
- );
1045
- if (reject) {
1046
- debug('Rejecting task promise, queue will continue normally');
1047
- reject(error);
1048
- continueQueue();
1049
- return;
1050
- }
1051
-
1052
- delete self.runningState;
1053
- try {
1054
- self.env.emit('error', error);
1055
- } catch (error) {
1056
- setImmediate(() => {
1057
- throw error;
1058
- });
1059
- }
1060
- });
1047
+ this.executeTask(task, undefined, taskStatus).then(continueQueue);
1061
1048
  },
1062
- {once, run: task.run}
1049
+ {once: once ? methodName : undefined, run}
1063
1050
  );
1064
1051
  }
1065
1052
 
1053
+ /**
1054
+ * @private
1055
+ * Execute a task.
1056
+ *
1057
+ * @param {Task} task: Task to be executed.
1058
+ * @param {string[]} args: Task arguments.
1059
+ * @param {Object} taskStatus.
1060
+ * @return Promise
1061
+ */
1062
+ executeTask(
1063
+ task,
1064
+ args = task.args || this.args,
1065
+ taskStatus = this._taskStatus || {}
1066
+ ) {
1067
+ return new Promise((resolve) => {
1068
+ const {
1069
+ reject,
1070
+ queueName = 'default',
1071
+ taskName: methodName,
1072
+ method
1073
+ } = task;
1074
+ const {namespace = ''} = this.options || {};
1075
+ args = typeof args === 'function' ? args(this) : args;
1076
+
1077
+ const priority = Object.entries(this._queues).find(
1078
+ ([_, options]) => options.queueName === queueName
1079
+ );
1080
+ const priorityName = priority ? priority[0] : undefined;
1081
+
1082
+ debug(`Running ${namespace}#${methodName}`);
1083
+ this.emit(`method:${methodName}`);
1084
+ const taskCancelled = task.cancellable && taskStatus.cancelled;
1085
+ if (taskCancelled) {
1086
+ resolve();
1087
+ return;
1088
+ }
1089
+
1090
+ const generator = this;
1091
+
1092
+ runAsync(function () {
1093
+ generator.async = () => this.async();
1094
+ generator.runningState = {namespace, queueName, methodName};
1095
+ return method.apply(generator, args);
1096
+ })()
1097
+ .then(() => {
1098
+ delete this.runningState;
1099
+ const eventName = `done$${
1100
+ namespace || 'unknownnamespace'
1101
+ }#${methodName}`;
1102
+ debug(`Done event ${eventName}`);
1103
+ this.env.emit(eventName, {
1104
+ namespace,
1105
+ generator: this,
1106
+ queueName,
1107
+ priorityName
1108
+ });
1109
+ resolve();
1110
+ })
1111
+ .catch((error) => {
1112
+ debug(
1113
+ `An error occured while running ${namespace}#${methodName}`,
1114
+ error
1115
+ );
1116
+ if (reject) {
1117
+ debug('Rejecting task promise, queue will continue normally');
1118
+ reject(error);
1119
+ resolve();
1120
+ return;
1121
+ }
1122
+
1123
+ delete this.runningState;
1124
+ try {
1125
+ this.env.emit('error', error);
1126
+ } catch (error) {
1127
+ setImmediate(() => {
1128
+ throw error;
1129
+ });
1130
+ }
1131
+ });
1132
+ });
1133
+ }
1134
+
1066
1135
  /**
1067
1136
  * Generator config Storage.
1068
1137
  */
@@ -1075,7 +1144,23 @@ class Generator extends Base {
1075
1144
  }
1076
1145
 
1077
1146
  /**
1078
- * Package.json Storage.
1147
+ * Package.json Storage resolved to `this.destinationPath('package.json')`.
1148
+ *
1149
+ * Environment watches for package.json changes at `this.env.cwd`, and triggers an package manager install if it has been committed to disk.
1150
+ * If package.json is at a different folder, like a changed generator root, propagate it to the Environment like `this.env.cwd = this.destinationPath()`.
1151
+ *
1152
+ * @example
1153
+ * this.packageJson.merge({
1154
+ * scripts: {
1155
+ * start: 'webpack --serve',
1156
+ * },
1157
+ * dependencies: {
1158
+ * ...
1159
+ * },
1160
+ * peerDependencies: {
1161
+ * ...
1162
+ * },
1163
+ * });
1079
1164
  */
1080
1165
  get packageJson() {
1081
1166
  if (!this._packageJson) {
@@ -1104,7 +1189,7 @@ class Generator extends Base {
1104
1189
  startOver(options = {}) {
1105
1190
  this.cancelCancellableTasks();
1106
1191
  Object.assign(this.options, options);
1107
- this.queueOwnTasks();
1192
+ this.queueOwnTasks({auto: true});
1108
1193
  }
1109
1194
 
1110
1195
  /**
@@ -1143,7 +1228,7 @@ class Generator extends Base {
1143
1228
  debug(
1144
1229
  `Queueing generator ${this.options.namespace} with generator version ${this.yoGeneratorVersion}`
1145
1230
  );
1146
- this.queueOwnTasks();
1231
+ this.queueOwnTasks({auto: true});
1147
1232
 
1148
1233
  if (this._composedWith.some((generator) => generator.then)) {
1149
1234
  return Promise.all(this._composedWith).then(async (generators) => {
@@ -1315,23 +1400,36 @@ this.composeWith({
1315
1400
  * Return a storage instance.
1316
1401
  * @param {String} storePath The path of the json file
1317
1402
  * @param {String} [path] The name in which is stored inside the json
1318
- * @param {String} [lodashPath] Treat path as an lodash path
1403
+ * @param {boolean|Object} [options] Treat path as an lodash path
1319
1404
  * @return {Storage} json storage
1320
1405
  */
1321
- createStorage(storePath, path, lodashPath = false) {
1406
+ createStorage(storePath, path, options) {
1407
+ if (typeof path === 'object') {
1408
+ options = path;
1409
+ path = undefined;
1410
+ } else if (typeof options === 'boolean') {
1411
+ options = {lodashPath: options};
1412
+ }
1413
+
1322
1414
  storePath = this.destinationPath(storePath);
1323
- return new Storage(path, this.fs, storePath, lodashPath);
1415
+ return new Storage(path, this.fs, storePath, options);
1324
1416
  }
1325
1417
 
1326
1418
  /**
1327
1419
  * Return a storage instance.
1328
1420
  * @param {String} [rootName] The rootName in which is stored inside .yo-rc.json
1421
+ * @param {object} [options] Storage options
1329
1422
  * @return {Storage} Generator storage
1330
1423
  * @private
1331
1424
  */
1332
- _getStorage(rootName = this.rootGeneratorName()) {
1425
+ _getStorage(rootName = this.rootGeneratorName(), options) {
1426
+ if (typeof rootName === 'object') {
1427
+ options = rootName;
1428
+ rootName = this.rootGeneratorName();
1429
+ }
1430
+
1333
1431
  const storePath = path.join(this.destinationRoot(), '.yo-rc.json');
1334
- return new Storage(rootName, this.fs, storePath);
1432
+ return new Storage(rootName, this.fs, storePath, options);
1335
1433
  }
1336
1434
 
1337
1435
  /**
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
  const assert = require('assert');
3
3
  const _ = require('lodash');
4
+ const sortKeys = require('sort-keys');
4
5
 
5
6
  /**
6
7
  * Proxy handler for Storage
@@ -41,6 +42,7 @@ const proxyHandler = {
41
42
  * @param {Boolean} [options.lodashPath=false] Set true to treat name as a lodash path.
42
43
  * @param {Boolean} [options.disableCache=false] Set true to disable json object cache.
43
44
  * @param {Boolean} [options.disableCacheByFile=false] Set true to cleanup cache for every fs change.
45
+ * @param {Boolean} [options.sorted=false] Set true to write sorted json.
44
46
  *
45
47
  * @example
46
48
  * class extend Generator {
@@ -62,9 +64,10 @@ class Storage {
62
64
  }
63
65
 
64
66
  _.defaults(options, {
65
- lodash: false,
67
+ lodashPath: false,
66
68
  disableCache: false,
67
- disableCacheByFile: false
69
+ disableCacheByFile: false,
70
+ sorted: false
68
71
  });
69
72
 
70
73
  assert(configPath, 'A config filepath is required to create a storage');
@@ -76,6 +79,7 @@ class Storage {
76
79
  this.lodashPath = options.lodashPath;
77
80
  this.disableCache = options.disableCache;
78
81
  this.disableCacheByFile = options.disableCacheByFile;
82
+ this.sorted = options.sorted;
79
83
 
80
84
  this.existed = Object.keys(this._store).length > 0;
81
85
 
@@ -113,6 +117,10 @@ class Storage {
113
117
  * @private
114
118
  */
115
119
  _persist(value) {
120
+ if (this.sorted) {
121
+ value = sortKeys(value, {deep: true});
122
+ }
123
+
116
124
  let fullStore;
117
125
  if (this.name) {
118
126
  fullStore = this.fs.readJSON(this.path, {});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yeoman-generator",
3
- "version": "5.4.2",
3
+ "version": "5.6.0",
4
4
  "description": "Rails-inspired generator system that provides scaffolding for your apps",
5
5
  "homepage": "http://yeoman.io",
6
6
  "author": "Yeoman",
@@ -22,12 +22,12 @@
22
22
  "app"
23
23
  ],
24
24
  "config": {
25
- "doc_path": "./yeoman-generator-doc"
25
+ "doc_path": "../yeoman-generator-doc/"
26
26
  },
27
27
  "devDependencies": {
28
28
  "coveralls": "^3.0.3",
29
29
  "inquirer": "^7.3.2",
30
- "jsdoc": "^3.6.3",
30
+ "jsdoc": "^3.6.7",
31
31
  "mem-fs": "^2.1.0",
32
32
  "mem-fs-editor": "^9.0.0",
33
33
  "mocha": "^8.0.1",
@@ -55,9 +55,9 @@
55
55
  "pretest": "xo",
56
56
  "test": "nyc mocha --forbid-only",
57
57
  "doc": "npm run doc:generate && npm run doc:fix && npm run doc:prettier",
58
- "doc:generate": "jsdoc -c jsdoc.json -d $npm_package_config_doc_path",
59
- "doc:prettier": "prettier $npm_package_config_doc_path --write --ignore-path .prettierignore-doc",
60
- "doc:fix": "sed -i -e 's:^[[:space:]]*<!--[[:space:]]*$::g' -e 's:^[[:space:]]*-->[[:space:]]*$::g' $npm_package_config_doc_path/global.html || true",
58
+ "doc:generate": "jsdoc -c jsdoc.json -d $npm_package_config_doc_path$DOC_FOLDER",
59
+ "doc:prettier": "prettier $npm_package_config_doc_path$DOC_FOLDER --write --ignore-path .prettierignore-doc",
60
+ "doc:fix": "sed -i -e 's:^[[:space:]]*<!--[[:space:]]*$::g' -e 's:^[[:space:]]*-->[[:space:]]*$::g' $npm_package_config_doc_path$DOC_FOLDER/global.html || true",
61
61
  "coverage": "nyc report --reporter=text-lcov | coveralls"
62
62
  },
63
63
  "dependencies": {
@@ -71,7 +71,8 @@
71
71
  "read-pkg-up": "^7.0.1",
72
72
  "run-async": "^2.0.0",
73
73
  "semver": "^7.2.1",
74
- "shelljs": "^0.8.4",
74
+ "shelljs": "^0.8.5",
75
+ "sort-keys": "^4.2.0",
75
76
  "text-table": "^0.2.0"
76
77
  },
77
78
  "peerDependencies": {