ember-cli 3.28.3 → 4.0.0-beta.3

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 (95) hide show
  1. package/.github/workflows/ci.yml +5 -1
  2. package/CHANGELOG.md +75 -0
  3. package/README.md +1 -1
  4. package/blueprints/addon/additional-dev-dependencies.json +2 -2
  5. package/blueprints/addon/files/.github/workflows/ci.yml +73 -0
  6. package/blueprints/addon/files/.travis.yml +1 -1
  7. package/blueprints/addon/files/npmignore +4 -0
  8. package/blueprints/addon/index.js +14 -5
  9. package/blueprints/app/files/.eslintignore +3 -0
  10. package/blueprints/app/files/.eslintrc.js +1 -1
  11. package/blueprints/app/files/.github/workflows/ci.yml +41 -0
  12. package/blueprints/app/files/.prettierignore +4 -0
  13. package/blueprints/app/files/config/targets.js +0 -15
  14. package/blueprints/app/files/gitignore +3 -0
  15. package/blueprints/app/files/package.json +20 -21
  16. package/blueprints/app/index.js +23 -2
  17. package/docs/build/classes/Addon.html +1 -1
  18. package/docs/build/classes/AmdTransformAddon.html +1 -1
  19. package/docs/build/classes/Blueprint.html +1 -1
  20. package/docs/build/classes/Builder.html +1 -1
  21. package/docs/build/classes/CLI.html +1 -1
  22. package/docs/build/classes/Command.html +1 -1
  23. package/docs/build/classes/DefaultPackager.html +1 -1
  24. package/docs/build/classes/EmberAddon.html +39 -39
  25. package/docs/build/classes/EmberApp.html +39 -39
  26. package/docs/build/classes/HardwareInfo.html +1 -1
  27. package/docs/build/classes/HistorySupportAddon.html +1 -1
  28. package/docs/build/classes/Instrumentation.html +1 -1
  29. package/docs/build/classes/NodeModulesList.html +1 -1
  30. package/docs/build/classes/NpmTask.html +4 -4
  31. package/docs/build/classes/PackageInfo.html +1 -1
  32. package/docs/build/classes/PackageInfoCache.html +1 -1
  33. package/docs/build/classes/PerBundleAddonCache {.html +1 -1
  34. package/docs/build/classes/Project.html +1 -1
  35. package/docs/build/classes/ServeFilesAddon.html +1 -1
  36. package/docs/build/classes/TestsServerAddon.html +1 -1
  37. package/docs/build/classes/WatcherAddon.html +1 -1
  38. package/docs/build/classes/WindowsSymlinkChecker.html +1 -1
  39. package/docs/build/data.json +36 -36
  40. package/docs/build/files/lib_broccoli_default-packager.js.html +1 -1
  41. package/docs/build/files/lib_broccoli_ember-addon.js.html +1 -1
  42. package/docs/build/files/lib_broccoli_ember-app.js.html +63 -21
  43. package/docs/build/files/lib_cli_cli.js.html +1 -1
  44. package/docs/build/files/lib_models_addon-info.js.html +1 -1
  45. package/docs/build/files/lib_models_addon.js.html +1 -1
  46. package/docs/build/files/lib_models_blueprint.js.html +5 -5
  47. package/docs/build/files/lib_models_builder.js.html +1 -1
  48. package/docs/build/files/lib_models_command.js.html +1 -1
  49. package/docs/build/files/lib_models_hardware-info.js.html +1 -1
  50. package/docs/build/files/lib_models_host-info-cache.js.html +1 -1
  51. package/docs/build/files/lib_models_installation-checker.js.html +1 -1
  52. package/docs/build/files/lib_models_instantiate-addons.js.html +1 -1
  53. package/docs/build/files/lib_models_instrumentation.js.html +1 -1
  54. package/docs/build/files/lib_models_package-info-cache_index.js.html +1 -1
  55. package/docs/build/files/lib_models_package-info-cache_node-modules-list.js.html +1 -1
  56. package/docs/build/files/lib_models_package-info-cache_package-info.js.html +1 -1
  57. package/docs/build/files/lib_models_per-bundle-addon-cache_addon-proxy.js.html +1 -1
  58. package/docs/build/files/lib_models_per-bundle-addon-cache_index.js.html +1 -1
  59. package/docs/build/files/lib_models_per-bundle-addon-cache_target-instance.js.html +1 -1
  60. package/docs/build/files/lib_models_project.js.html +1 -1
  61. package/docs/build/files/lib_models_task.js.html +1 -1
  62. package/docs/build/files/lib_tasks_build-watch.js.html +1 -1
  63. package/docs/build/files/lib_tasks_npm-task.js.html +134 -46
  64. package/docs/build/files/lib_tasks_serve.js.html +1 -1
  65. package/docs/build/files/lib_tasks_server_middleware_broccoli-serve-files_index.js.html +1 -1
  66. package/docs/build/files/lib_tasks_server_middleware_broccoli-watcher_index.js.html +1 -1
  67. package/docs/build/files/lib_tasks_server_middleware_history-support_index.js.html +1 -1
  68. package/docs/build/files/lib_tasks_server_middleware_tests-server_index.js.html +1 -1
  69. package/docs/build/files/lib_tasks_test-server.js.html +1 -1
  70. package/docs/build/files/lib_tasks_transforms_amd_index.js.html +1 -1
  71. package/docs/build/files/lib_utilities_ember-app-utils.js.html +1 -1
  72. package/docs/build/files/lib_utilities_insert-into-file.js.html +1 -1
  73. package/docs/build/files/lib_utilities_is-lazy-engine.js.html +1 -1
  74. package/docs/build/files/lib_utilities_is-yarn-project.js.html +1 -1
  75. package/docs/build/files/lib_utilities_valid-project-name.js.html +1 -1
  76. package/docs/build/files/lib_utilities_will-interrupt-process.js.html +1 -1
  77. package/docs/build/files/lib_utilities_windows-admin.js.html +1 -1
  78. package/docs/build/index.html +2 -2
  79. package/docs/build/modules/ember-cli.html +2 -2
  80. package/docs/build/modules/is-lazy-engine.html +1 -1
  81. package/docs/perf-guide.md +2 -0
  82. package/lib/broccoli/ember-app.js +62 -20
  83. package/lib/commands/addon.js +6 -0
  84. package/lib/commands/init.js +18 -2
  85. package/lib/commands/install.js +9 -1
  86. package/lib/commands/new.js +6 -0
  87. package/lib/models/blueprint.js +4 -4
  88. package/lib/tasks/addon-install.js +10 -1
  89. package/lib/tasks/install-blueprint.js +1 -0
  90. package/lib/tasks/npm-install.js +2 -2
  91. package/lib/tasks/npm-task.js +133 -45
  92. package/lib/tasks/npm-uninstall.js +2 -2
  93. package/package.json +2 -2
  94. package/tests/helpers/dist-checker.js +21 -1
  95. package/tests/helpers/ember.js +2 -2
@@ -84,7 +84,7 @@ class EmberApp {
84
84
  - trees, defaults to `{}`
85
85
  - jshintrc, defaults to `{}`
86
86
  - vendorFiles, defaults to `{}`
87
- - addons, defaults to `{ blacklist: [], whitelist: [] }`
87
+ - addons, defaults to `{ exclude: [], include: [] }`
88
88
 
89
89
  @class EmberApp
90
90
  @constructor
@@ -170,10 +170,12 @@ class EmberApp {
170
170
  this._cachedAddonBundles = {};
171
171
 
172
172
  if (this.project.perBundleAddonCache && this.project.perBundleAddonCache.numProxies > 0) {
173
- if (this.options.addons.whitelist && this.options.addons.whitelist.length) {
173
+ if (this.options.addons.include && this.options.addons.include.length) {
174
+ let optionKey = this.options.addons.hasWhitelist ? 'whitelist' : 'include';
175
+
174
176
  throw new Error(
175
177
  [
176
- '[ember-cli] addon bundle caching is disabled for apps that specify an addon `whitelist`',
178
+ `[ember-cli] addon bundle caching is disabled for apps that specify an addon "${optionKey}"`,
177
179
  '',
178
180
  'All addons using bundle caching:',
179
181
  ...this.project.perBundleAddonCache.getPathsToAddonsOptedIn(),
@@ -181,10 +183,12 @@ class EmberApp {
181
183
  );
182
184
  }
183
185
 
184
- if (this.options.addons.blacklist && this.options.addons.blacklist.length) {
186
+ if (this.options.addons.exclude && this.options.addons.exclude.length) {
187
+ let optionKey = this.options.addons.hasBlacklist ? 'blacklist' : 'exclude';
188
+
185
189
  throw new Error(
186
190
  [
187
- '[ember-cli] addon bundle caching is disabled for apps that specify an addon `blacklist`',
191
+ `[ember-cli] addon bundle caching is disabled for apps that specify an addon "${optionKey}"`,
188
192
  '',
189
193
  'All addons using bundle caching:',
190
194
  ...this.project.perBundleAddonCache.getPathsToAddonsOptedIn(),
@@ -366,6 +370,40 @@ class EmberApp {
366
370
 
367
371
  this.options = defaultsDeep(options, detectedDefaultOptions, DEFAULT_CONFIG);
368
372
 
373
+ if (this.options.addons.blacklist) {
374
+ if (this.options.addons.exclude) {
375
+ throw new Error('Specifying both "blacklist" and "exclude" is not supported. Please use only one.');
376
+ }
377
+
378
+ this.options.addons.hasBlacklist = true;
379
+ this.options.addons.exclude = this.options.addons.blacklist;
380
+ }
381
+
382
+ Object.defineProperty(this.options.addons, 'blacklist', {
383
+ get() {
384
+ console.log(chalk.yellow('Please use "exclude" instead of "blacklist".'));
385
+
386
+ return this.exclude;
387
+ },
388
+ });
389
+
390
+ if (this.options.addons.whitelist) {
391
+ if (this.options.addons.include) {
392
+ throw new Error('Specifying both "whitelist" and "include" is not supported. Please use only one.');
393
+ }
394
+
395
+ this.options.addons.hasWhitelist = true;
396
+ this.options.addons.include = this.options.addons.whitelist;
397
+ }
398
+
399
+ Object.defineProperty(this.options.addons, 'whitelist', {
400
+ get() {
401
+ console.log(chalk.yellow('Please use "include" instead of "whitelist".'));
402
+
403
+ return this.include;
404
+ },
405
+ });
406
+
369
407
  // For now we must disable Babel sourcemaps due to unforeseen
370
408
  // performance regressions.
371
409
  if (!('sourceMaps' in this.options.babel)) {
@@ -555,24 +593,24 @@ class EmberApp {
555
593
 
556
594
  /**
557
595
  @private
558
- @method _addonDisabledByBlacklist
596
+ @method _addonDisabledByExclude
559
597
  @param {Addon} addon
560
598
  @return {Boolean}
561
599
  */
562
- _addonDisabledByBlacklist(addon) {
563
- let blacklist = this.options.addons.blacklist;
564
- return !!blacklist && blacklist.indexOf(addon.name) !== -1;
600
+ _addonDisabledByExclude(addon) {
601
+ let exclude = this.options.addons.exclude;
602
+ return !!exclude && exclude.indexOf(addon.name) !== -1;
565
603
  }
566
604
 
567
605
  /**
568
606
  @private
569
- @method _addonDisabledByWhitelist
607
+ @method _addonDisabledByInclude
570
608
  @param {Addon} addon
571
609
  @return {Boolean}
572
610
  */
573
- _addonDisabledByWhitelist(addon) {
574
- let whitelist = this.options.addons.whitelist;
575
- return !!whitelist && whitelist.indexOf(addon.name) === -1;
611
+ _addonDisabledByInclude(addon) {
612
+ let include = this.options.addons.include;
613
+ return !!include && include.indexOf(addon.name) === -1;
576
614
  }
577
615
 
578
616
  /**
@@ -616,7 +654,7 @@ class EmberApp {
616
654
  return false;
617
655
  }
618
656
 
619
- return !this._addonDisabledByBlacklist(addon) && !this._addonDisabledByWhitelist(addon);
657
+ return !this._addonDisabledByExclude(addon) && !this._addonDisabledByInclude(addon);
620
658
  }
621
659
 
622
660
  /**
@@ -628,18 +666,22 @@ class EmberApp {
628
666
  _notifyAddonIncluded() {
629
667
  let addonNames = this.project.addons.map((addon) => addon.name);
630
668
 
631
- if (this.options.addons.blacklist) {
632
- this.options.addons.blacklist.forEach((addonName) => {
669
+ if (this.options.addons.exclude) {
670
+ let optionKey = this.options.addons.hasBlacklist ? 'blacklist' : 'exclude';
671
+
672
+ this.options.addons.exclude.forEach((addonName) => {
633
673
  if (addonNames.indexOf(addonName) === -1) {
634
- throw new Error(`Addon "${addonName}" defined in blacklist is not found`);
674
+ throw new Error(`Addon "${addonName}" defined in "${optionKey}" is not found`);
635
675
  }
636
676
  });
637
677
  }
638
678
 
639
- if (this.options.addons.whitelist) {
640
- this.options.addons.whitelist.forEach((addonName) => {
679
+ if (this.options.addons.include) {
680
+ let optionKey = this.options.addons.hasWhitelist ? 'whitelist' : 'include';
681
+
682
+ this.options.addons.include.forEach((addonName) => {
641
683
  if (addonNames.indexOf(addonName) === -1) {
642
- throw new Error(`Addon "${addonName}" defined in whitelist is not found`);
684
+ throw new Error(`Addon "${addonName}" defined in "${optionKey}" is not found`);
643
685
  }
644
686
  });
645
687
  }
@@ -20,6 +20,12 @@ module.exports = NewCommand.extend({
20
20
  type: String,
21
21
  description: "Sets the base human language of the addon's own test application via index.html",
22
22
  },
23
+ {
24
+ name: 'ci-provider',
25
+ type: ['travis', 'github'],
26
+ default: 'github',
27
+ description: 'Installs the default CI blueprint. Either Travis or Github Actions is supported.',
28
+ },
23
29
  ],
24
30
 
25
31
  anonymousOptions: ['<addon-name>'],
@@ -37,6 +37,12 @@ module.exports = Command.extend({
37
37
  description: 'Sets the base human language of the application via index.html',
38
38
  },
39
39
  { name: 'embroider', type: Boolean, default: false, description: 'Enables the build system to use Embroider' },
40
+ {
41
+ name: 'ci-provider',
42
+ type: ['travis', 'github'],
43
+ default: 'github',
44
+ description: 'Installs the default CI blueprint. Either Travis or Github Actions is supported.',
45
+ },
40
46
  ],
41
47
 
42
48
  anonymousOptions: ['<glob-pattern>'],
@@ -59,6 +65,7 @@ module.exports = Command.extend({
59
65
 
60
66
  let project = this.project;
61
67
  let packageName = (commandOptions.name !== '.' && commandOptions.name) || project.name();
68
+ let ciProvider = commandOptions.ciProvider;
62
69
 
63
70
  if (!packageName) {
64
71
  let message =
@@ -75,7 +82,7 @@ module.exports = Command.extend({
75
82
 
76
83
  let blueprintOpts = clone(commandOptions);
77
84
 
78
- if (blueprintOpts.yarn === undefined) {
85
+ if (blueprintOpts.yarn === undefined && project.isEmberCLIProject()) {
79
86
  blueprintOpts.yarn = isYarnProject(project.root);
80
87
  }
81
88
 
@@ -84,6 +91,7 @@ module.exports = Command.extend({
84
91
  targetFiles: rawArgs || '',
85
92
  rawArgs: rawArgs.toString(),
86
93
  blueprint: normalizeBlueprint(blueprintOpts.blueprint || this._defaultBlueprint()),
94
+ ciProvider,
87
95
  });
88
96
 
89
97
  if (!isValidProjectName(packageName)) {
@@ -93,9 +101,17 @@ module.exports = Command.extend({
93
101
  await this.runTask('InstallBlueprint', blueprintOpts);
94
102
 
95
103
  if (!commandOptions.skipNpm) {
104
+ let packageManager =
105
+ commandOptions.yarn === true
106
+ ? 'yarn'
107
+ : // supported for legacy reasons
108
+ commandOptions.yarn === false
109
+ ? 'npm'
110
+ : undefined;
111
+
96
112
  await this.runTask('NpmInstall', {
97
113
  verbose: commandOptions.verbose,
98
- useYarn: commandOptions.yarn,
114
+ packageManager,
99
115
  });
100
116
  }
101
117
 
@@ -13,7 +13,15 @@ module.exports = Command.extend({
13
13
  { name: 'save', type: Boolean, default: false, aliases: ['S'] },
14
14
  { name: 'save-dev', type: Boolean, default: true, aliases: ['D'] },
15
15
  { name: 'save-exact', type: Boolean, default: false, aliases: ['E', 'exact'] },
16
- { name: 'yarn', type: Boolean, description: 'Use --yarn to enforce yarn usage, or --no-yarn to enforce npm' }, // no default means use yarn if the blueprint has a yarn.lock
16
+ {
17
+ name: 'package-manager',
18
+ type: ['npm', 'pnpm', 'yarn'],
19
+ description:
20
+ 'Use this option to force the usage of a specific package manager. ' +
21
+ 'By default, ember-cli will try to detect the right package manager ' +
22
+ 'from any lockfiles that exist in your project.',
23
+ aliases: [{ yarn: 'yarn' }, { pnpm: 'pnpm' }],
24
+ },
17
25
  ],
18
26
 
19
27
  anonymousOptions: ['<addon-name>'],
@@ -35,6 +35,12 @@ module.exports = Command.extend({
35
35
  description: 'Sets the base human language of the application via index.html',
36
36
  },
37
37
  { name: 'embroider', type: Boolean, default: false, description: 'Enables the build system to use Embroider' },
38
+ {
39
+ name: 'ci-provider',
40
+ type: ['travis', 'github'],
41
+ default: 'github',
42
+ description: 'Installs the default CI blueprint. Either Travis or Github Actions is supported.',
43
+ },
38
44
  ],
39
45
 
40
46
  anonymousOptions: ['<app-name>'],
@@ -446,7 +446,7 @@ let Blueprint = CoreObject.extend({
446
446
  this.project = options.project;
447
447
  this.pod = options.pod;
448
448
  this.options = options;
449
- this.hasPathToken = hasPathToken(this.files());
449
+ this.hasPathToken = hasPathToken(this.files(this.options));
450
450
 
451
451
  ui.writeLine(`installing ${this.name}`);
452
452
 
@@ -476,7 +476,7 @@ let Blueprint = CoreObject.extend({
476
476
  this.project = options.project;
477
477
  this.pod = options.pod;
478
478
  this.options = options;
479
- this.hasPathToken = hasPathToken(this.files());
479
+ this.hasPathToken = hasPathToken(this.files(this.options));
480
480
 
481
481
  ui.writeLine(`uninstalling ${this.name}`);
482
482
 
@@ -717,7 +717,7 @@ let Blueprint = CoreObject.extend({
717
717
  @return {Array} files
718
718
  */
719
719
  _getFilesForInstall(targetFiles) {
720
- let files = this.files();
720
+ let files = this.files(this.options);
721
721
 
722
722
  // if we've defined targetFiles, get file info on ones that match
723
723
  return (targetFiles && targetFiles.length > 0 && _.intersection(files, targetFiles)) || files;
@@ -765,7 +765,7 @@ let Blueprint = CoreObject.extend({
765
765
  @param {Object} templateVariables
766
766
  */
767
767
  processFilesForUninstall(intoDir, templateVariables) {
768
- let fileInfos = this._getFileInfos(this.files(), intoDir, templateVariables);
768
+ let fileInfos = this._getFileInfos(this.files(this.options), intoDir, templateVariables);
769
769
 
770
770
  this._ignoreUpdateFiles();
771
771
 
@@ -35,13 +35,22 @@ class AddonInstallTask extends Task {
35
35
 
36
36
  ui.startProgress(chalk.green('Installing addon package'), chalk.green('.'));
37
37
 
38
+ let packageManager =
39
+ commandOptions.packageManager ||
40
+ (commandOptions.yarn === true
41
+ ? 'yarn'
42
+ : // supported for legacy reasons
43
+ commandOptions.yarn === false
44
+ ? 'npm'
45
+ : undefined);
46
+
38
47
  return npmInstall
39
48
  .run({
40
49
  packages: packageNames,
41
50
  save: commandOptions.save,
42
51
  'save-dev': !commandOptions.save,
43
52
  'save-exact': commandOptions.saveExact,
44
- useYarn: commandOptions.yarn,
53
+ packageManager,
45
54
  })
46
55
  .then(() => this.project.reloadAddons())
47
56
  .then(() => this.installBlueprint(blueprintInstall, packageNames, blueprintOptions))
@@ -40,6 +40,7 @@ class InstallBlueprintTask extends Task {
40
40
  dryRun: options.dryRun,
41
41
  targetFiles: options.targetFiles,
42
42
  rawArgs: options.rawArgs,
43
+ ciProvider: options.ciProvider,
43
44
  };
44
45
 
45
46
  installOptions = merge(installOptions, options || {});
@@ -25,11 +25,11 @@ class NpmInstallTask extends NpmTask {
25
25
  }
26
26
 
27
27
  formatStartMessage(packages) {
28
- return `${this.useYarn ? 'Yarn' : 'npm'}: Installing ${formatPackageList(packages)} ...`;
28
+ return `${this.packageManagerOutputName}: Installing ${formatPackageList(packages)} ...`;
29
29
  }
30
30
 
31
31
  formatCompleteMessage(packages) {
32
- return `${this.useYarn ? 'Yarn' : 'npm'}: Installed ${formatPackageList(packages)}`;
32
+ return `${this.packageManagerOutputName}: Installed ${formatPackageList(packages)}`;
33
33
  }
34
34
  }
35
35
 
@@ -4,6 +4,7 @@
4
4
 
5
5
  const chalk = require('chalk');
6
6
  const execa = require('../utilities/execa');
7
+ const findUp = require('find-up');
7
8
  const semver = require('semver');
8
9
  const SilentError = require('silent-error');
9
10
  const isYarnProject = require('../utilities/is-yarn-project');
@@ -28,6 +29,10 @@ class NpmTask extends Task {
28
29
  this.versionConstraints = '3 || 4 || 5 || 6';
29
30
  }
30
31
 
32
+ get packageManagerOutputName() {
33
+ return this.packageManager.name;
34
+ }
35
+
31
36
  npm(args) {
32
37
  logger.info('npm: %j', args);
33
38
  return execa('npm', args, { preferLocal: false });
@@ -38,10 +43,19 @@ class NpmTask extends Task {
38
43
  return execa('yarn', args, { preferLocal: false });
39
44
  }
40
45
 
46
+ pnpm(args) {
47
+ logger.info('pnpm: %j', args);
48
+ return execa('pnpm', args, { preferLocal: false });
49
+ }
50
+
41
51
  hasYarnLock() {
42
52
  return isYarnProject(this.project.root);
43
53
  }
44
54
 
55
+ async hasPNPMLock() {
56
+ return Boolean(await findUp('pnpm-lock.yaml', { cwd: this.project.root }));
57
+ }
58
+
45
59
  async checkYarn() {
46
60
  try {
47
61
  let result = await this.yarn(['--version']);
@@ -54,9 +68,7 @@ class NpmTask extends Task {
54
68
  logger.info('yarn --version: %s', version);
55
69
  }
56
70
 
57
- this.useYarn = true;
58
-
59
- return { yarnVersion: version };
71
+ return { name: 'yarn', version };
60
72
  } catch (error) {
61
73
  logger.error('yarn --version failed: %s', error);
62
74
 
@@ -71,6 +83,28 @@ class NpmTask extends Task {
71
83
  }
72
84
  }
73
85
 
86
+ async checkPNPM() {
87
+ try {
88
+ let result = await this.pnpm(['--version']);
89
+ let version = result.stdout;
90
+
91
+ logger.info('pnpm --version: %s', version);
92
+
93
+ return { name: 'pnpm', version };
94
+ } catch (error) {
95
+ logger.error('pnpm --version failed: %s', error);
96
+
97
+ if (error.code === 'ENOENT') {
98
+ throw new SilentError(
99
+ 'Ember CLI is now using pnpm, but was not able to find it.\n' +
100
+ 'Please install pnpm using the instructions at https://pnpm.io/installation'
101
+ );
102
+ }
103
+
104
+ throw error;
105
+ }
106
+ }
107
+
74
108
  async checkNpmVersion() {
75
109
  try {
76
110
  let result = await this.npm(['--version']);
@@ -95,7 +129,7 @@ class NpmTask extends Task {
95
129
  );
96
130
  }
97
131
 
98
- return { npmVersion: version };
132
+ return { name: 'npm', version };
99
133
  } catch (error) {
100
134
  logger.error('npm --version failed: %s', error);
101
135
 
@@ -124,37 +158,55 @@ class NpmTask extends Task {
124
158
  * @method findPackageManager
125
159
  * @return {Promise}
126
160
  */
127
- async findPackageManager() {
128
- if (this.useYarn === true) {
161
+ async findPackageManager(packageManager = null) {
162
+ if (packageManager === 'yarn') {
129
163
  logger.info('yarn requested -> trying yarn');
130
164
  return this.checkYarn();
131
165
  }
132
166
 
133
- if (this.useYarn === false) {
167
+ if (packageManager === 'npm') {
134
168
  logger.info('npm requested -> using npm');
135
169
  return this.checkNpmVersion();
136
170
  }
137
171
 
138
- if (!this.hasYarnLock()) {
139
- logger.info('yarn.lock not found -> using npm');
140
- return this.checkNpmVersion();
172
+ if (packageManager === 'pnpm') {
173
+ logger.info('pnpm requested -> using pnpm');
174
+ return this.checkPNPM();
141
175
  }
142
176
 
143
- logger.info('yarn.lock found -> trying yarn');
144
- try {
145
- const yarnResult = await this.checkYarn();
146
- logger.info('yarn found -> using yarn');
147
- return yarnResult;
148
- } catch (_err) {
149
- logger.info('yarn not found -> using npm');
150
- return this.checkNpmVersion();
177
+ if (this.hasYarnLock()) {
178
+ logger.info('yarn.lock found -> trying yarn');
179
+ try {
180
+ const yarnResult = await this.checkYarn();
181
+ logger.info('yarn found -> using yarn');
182
+ return yarnResult;
183
+ } catch (_err) {
184
+ logger.info('yarn not found');
185
+ }
186
+ } else {
187
+ logger.info('yarn.lock not found');
188
+ }
189
+
190
+ if (this.hasPNPMLock()) {
191
+ logger.info('pnpm-lock.yaml found -> trying pnpm');
192
+ try {
193
+ let result = await this.checkPNPM();
194
+ logger.info('pnpm found -> using pnpm');
195
+ return result;
196
+ } catch (_err) {
197
+ logger.info('pnpm not found');
198
+ }
199
+ } else {
200
+ logger.info('pnpm-lock.yaml not found');
151
201
  }
202
+
203
+ logger.info('using npm');
204
+ return this.checkNpmVersion();
152
205
  }
153
206
 
154
207
  async run(options) {
155
- this.useYarn = options.useYarn;
208
+ this.packageManager = await this.findPackageManager(options.packageManager);
156
209
 
157
- let result = await this.findPackageManager();
158
210
  let ui = this.ui;
159
211
  let startMessage = this.formatStartMessage(options.packages);
160
212
  let completeMessage = this.formatCompleteMessage(options.packages);
@@ -165,31 +217,37 @@ class NpmTask extends Task {
165
217
  ui.writeLine(prependEmoji('🚧', 'Installing packages... This might take a couple of minutes.'));
166
218
  ui.startProgress(chalk.green(startMessage));
167
219
 
168
- let promise;
169
- if (this.useYarn) {
170
- this.yarnVersion = result.yarnVersion;
171
- let args = this.toYarnArgs(this.command, options);
172
- promise = this.yarn(args);
173
- } else {
174
- let args = this.toNpmArgs(this.command, options);
175
- promise = this.npm(args);
176
-
177
- // as of 2018-10-09 npm 5 and 6 _break_ the hierarchy of `node_modules`
178
- // after a `npm install foo` (deletes files/folders other than
179
- // what was directly installed) in some circumstances, see:
180
- //
181
- // * https://github.com/npm/npm/issues/16853
182
- // * https://github.com/npm/npm/issues/17379
183
- //
184
- // this ensures that we run a full `npm install` **after** any `npm
185
- // install foo` runs to ensure that we have a fully functional
186
- // node_modules hierarchy
187
- if (result.npmVersion && semver.lt(result.npmVersion, '5.7.1')) {
188
- promise = promise.then(() => this.npm(['install']));
220
+ try {
221
+ if (this.packageManager.name === 'yarn') {
222
+ let args = this.toYarnArgs(this.command, options);
223
+ await this.yarn(args);
224
+ } else if (this.packageManager.name === 'pnpm') {
225
+ let args = this.toPNPMArgs(this.command, options);
226
+ await this.pnpm(args);
227
+ } else {
228
+ let args = this.toNpmArgs(this.command, options);
229
+ await this.npm(args);
230
+
231
+ // as of 2018-10-09 npm 5 and 6 _break_ the hierarchy of `node_modules`
232
+ // after a `npm install foo` (deletes files/folders other than
233
+ // what was directly installed) in some circumstances, see:
234
+ //
235
+ // * https://github.com/npm/npm/issues/16853
236
+ // * https://github.com/npm/npm/issues/17379
237
+ //
238
+ // this ensures that we run a full `npm install` **after** any `npm
239
+ // install foo` runs to ensure that we have a fully functional
240
+ // node_modules hierarchy
241
+ let npmVersion = this.packageManager.version;
242
+ if (npmVersion && semver.lt(npmVersion, '5.7.1')) {
243
+ await this.npm(['install']);
244
+ }
189
245
  }
246
+ } finally {
247
+ ui.stopProgress();
190
248
  }
191
249
 
192
- return promise.finally(() => ui.stopProgress()).then(() => ui.writeLine(chalk.green(completeMessage)));
250
+ ui.writeLine(chalk.green(completeMessage));
193
251
  }
194
252
 
195
253
  toNpmArgs(command, options) {
@@ -212,9 +270,9 @@ class NpmTask extends Task {
212
270
  }
213
271
 
214
272
  if (options.verbose) {
215
- args.push('--loglevel verbose');
273
+ args.push('--loglevel', 'verbose');
216
274
  } else {
217
- args.push('--loglevel error');
275
+ args.push('--loglevel', 'error');
218
276
  }
219
277
 
220
278
  if (options.packages) {
@@ -262,13 +320,43 @@ class NpmTask extends Task {
262
320
  // Yarn v2 defaults to non-interactive
263
321
  // with an optional -i flag
264
322
 
265
- if (semver.lt(this.yarnVersion, '2.0.0')) {
323
+ if (semver.lt(this.packageManager.version, '2.0.0')) {
266
324
  args.push('--non-interactive');
267
325
  }
268
326
 
269
327
  return args;
270
328
  }
271
329
 
330
+ toPNPMArgs(command, options) {
331
+ let args = [];
332
+
333
+ if (command === 'install') {
334
+ if (options.save) {
335
+ args.push('add');
336
+ } else if (options['save-dev']) {
337
+ args.push('add', '--save-dev');
338
+ } else if (options.packages) {
339
+ throw new Error(`npm command "${command} ${options.packages.join(' ')}" can not be translated to pnpm command`);
340
+ } else {
341
+ args.push('install');
342
+ }
343
+
344
+ if (options['save-exact']) {
345
+ args.push('--save-exact');
346
+ }
347
+ } else if (command === 'uninstall') {
348
+ args.push('remove');
349
+ } else {
350
+ throw new Error(`npm command "${command}" can not be translated to pnpm command`);
351
+ }
352
+
353
+ if (options.packages) {
354
+ args = args.concat(options.packages);
355
+ }
356
+
357
+ return args;
358
+ }
359
+
272
360
  formatStartMessage(/* packages */) {
273
361
  return '';
274
362
  }
@@ -12,11 +12,11 @@ class NpmUninstallTask extends NpmTask {
12
12
  }
13
13
 
14
14
  formatStartMessage(packages) {
15
- return `${this.useYarn ? 'Yarn' : 'npm'}: Uninstalling ${formatPackageList(packages)} ...`;
15
+ return `${this.packageManagerOutputName}: Uninstalling ${formatPackageList(packages)} ...`;
16
16
  }
17
17
 
18
18
  formatCompleteMessage(packages) {
19
- return `${this.useYarn ? 'Yarn' : 'npm'}: Uninstalled ${formatPackageList(packages)}`;
19
+ return `${this.packageManagerOutputName}: Uninstalled ${formatPackageList(packages)}`;
20
20
  }
21
21
  }
22
22
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ember-cli",
3
- "version": "3.28.3",
3
+ "version": "4.0.0-beta.3",
4
4
  "description": "Command line tool for developing ambitious ember.js apps",
5
5
  "keywords": [
6
6
  "app",
@@ -125,7 +125,7 @@
125
125
  "tree-sync": "^2.1.0",
126
126
  "uuid": "^8.3.2",
127
127
  "walk-sync": "^2.2.0",
128
- "watch-detector": "^1.0.0",
128
+ "watch-detector": "^1.0.1",
129
129
  "workerpool": "^6.1.4",
130
130
  "yam": "^1.0.0"
131
131
  },