ember-cli 4.1.1 → 4.3.0-beta.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.
@@ -12,6 +12,7 @@ const ConfigReplace = require('broccoli-config-replace');
12
12
  const emberAppUtils = require('../utilities/ember-app-utils');
13
13
  const funnelReducer = require('broccoli-funnel-reducer');
14
14
  const addonProcessTree = require('../utilities/addon-process-tree');
15
+ const { deprecate } = require('../debug');
15
16
 
16
17
  const preprocessCss = p.preprocessCss;
17
18
  const preprocessJs = p.preprocessJs;
@@ -628,9 +629,21 @@ module.exports = class DefaultPackager {
628
629
  * @param {String} bowerDirectory Custom path to bower components
629
630
  */
630
631
  packageBower(tree, bowerDirectory) {
632
+ let destDir = bowerDirectory || DEFAULT_BOWER_PATH;
633
+
634
+ deprecate(`Building Bower packages has been deprecated. You have Bower packages in \`${destDir}\`.`, false, {
635
+ for: 'ember-cli',
636
+ id: 'ember-cli.building-bower-packages',
637
+ since: {
638
+ available: '4.2.0',
639
+ enabled: '4.2.0',
640
+ },
641
+ until: '5.0.0',
642
+ });
643
+
631
644
  if (this._cachedBower === null) {
632
645
  this._cachedBower = new Funnel(tree, {
633
- destDir: bowerDirectory || DEFAULT_BOWER_PATH,
646
+ destDir,
634
647
  annotation: 'Packaged Bower',
635
648
  });
636
649
  }
@@ -20,9 +20,9 @@ const concat = require('broccoli-concat');
20
20
  const BroccoliDebug = require('broccoli-debug');
21
21
  const AmdFunnel = require('broccoli-amd-funnel');
22
22
  const mergeTrees = require('./merge-trees');
23
+ const broccoliMergeTrees = require('broccoli-merge-trees');
23
24
  const WatchedDir = require('broccoli-source').WatchedDir;
24
25
  const UnwatchedDir = require('broccoli-source').UnwatchedDir;
25
- const BroccoliMergeTrees = require('broccoli-merge-trees');
26
26
 
27
27
  const merge = require('ember-cli-lodash-subset').merge;
28
28
  const defaultsDeep = require('ember-cli-lodash-subset').defaultsDeep;
@@ -108,7 +108,7 @@ class EmberApp {
108
108
 
109
109
  this.registry = options.registry || p.defaultRegistry(this);
110
110
 
111
- this.bowerDirectory = this.project.bowerDirectory;
111
+ this.bowerDirectory = this.project._bowerDirectory;
112
112
 
113
113
  this._initTestsAndHinting(options);
114
114
  this._initOptions(options);
@@ -438,7 +438,7 @@ class EmberApp {
438
438
  @method _initVendorFiles
439
439
  */
440
440
  _initVendorFiles() {
441
- let bowerDeps = this.project.bowerDependencies();
441
+ let bowerDeps = this.project._bowerDependencies();
442
442
  let ember = this.project.findAddonByName('ember-source');
443
443
  let addonEmberCliShims = this.project.findAddonByName('ember-cli-shims');
444
444
  let bowerEmberCliShims = bowerDeps['ember-cli-shims'];
@@ -1727,7 +1727,7 @@ class EmberApp {
1727
1727
  }
1728
1728
 
1729
1729
  let trees = [].concat(packagedTree, additionalTrees).filter(Boolean);
1730
- let combinedPackageTree = new BroccoliMergeTrees(trees);
1730
+ let combinedPackageTree = broccoliMergeTrees(trees);
1731
1731
 
1732
1732
  return this.addonPostprocessTree('all', combinedPackageTree);
1733
1733
  }
@@ -26,6 +26,13 @@ module.exports = Command.extend({
26
26
  description:
27
27
  'Runs a blueprint against an in repo addon. ' + 'A path is expected, relative to the root of the project.',
28
28
  },
29
+ {
30
+ name: 'typescript',
31
+ type: Boolean,
32
+ aliases: ['ts'],
33
+ description:
34
+ 'Specifically destroys the TypeScript output of the `generate` command. Run `--no-typescript` to instead target the JavaScript output.',
35
+ },
29
36
  ],
30
37
 
31
38
  anonymousOptions: ['<blueprint>'],
@@ -9,6 +9,11 @@ const _ = require('ember-cli-lodash-subset');
9
9
  const EOL = require('os').EOL;
10
10
  const SilentError = require('silent-error');
11
11
 
12
+ const UNKNOWN_BLUEPRINT_ERROR =
13
+ 'The `ember generate` command requires a ' +
14
+ 'blueprint name to be specified. ' +
15
+ 'For more details, use `ember help`';
16
+
12
17
  module.exports = Command.extend({
13
18
  name: 'generate',
14
19
  description: 'Generates new code from blueprints.',
@@ -30,6 +35,12 @@ module.exports = Command.extend({
30
35
  description:
31
36
  'Runs a blueprint against an in repo addon. ' + 'A path is expected, relative to the root of the project.',
32
37
  },
38
+ {
39
+ name: 'typescript',
40
+ type: Boolean,
41
+ aliases: ['ts'],
42
+ description: 'Generates a version of the blueprint written in TypeScript (if available).',
43
+ },
33
44
  ],
34
45
 
35
46
  anonymousOptions: ['<blueprint>'],
@@ -40,13 +51,7 @@ module.exports = Command.extend({
40
51
  let blueprintName = rawArgs[0];
41
52
 
42
53
  if (!blueprintName) {
43
- return Promise.reject(
44
- new SilentError(
45
- 'The `ember generate` command requires a ' +
46
- 'blueprint name to be specified. ' +
47
- 'For more details, use `ember help`.'
48
- )
49
- );
54
+ return Promise.reject(new SilentError(UNKNOWN_BLUEPRINT_ERROR));
50
55
  }
51
56
 
52
57
  let taskArgs = {
@@ -166,3 +171,7 @@ module.exports = Command.extend({
166
171
  }
167
172
  },
168
173
  });
174
+
175
+ module.exports.ERRORS = {
176
+ UNKNOWN_BLUEPRINT_ERROR,
177
+ };
@@ -0,0 +1,37 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Verify that a certain condition is met, or throw an error if otherwise.
5
+ *
6
+ * This is useful for communicating expectations in the code to other human
7
+ * readers as well as catching bugs that accidentally violate these expectations.
8
+ *
9
+ * ```js
10
+ * const { assert } = require('ember-cli/lib/debug');
11
+ *
12
+ * // Test for truthiness:
13
+ * assert('Must pass a string.', typeof str === 'string');
14
+ *
15
+ * // Fail unconditionally:
16
+ * assert('This code path should never run.');
17
+ * ```
18
+ *
19
+ * @method assert
20
+ * @param {String} description Describes the condition.
21
+ * This will become the message of the error thrown if the assertion fails.
22
+ * @param {Any} condition Must be truthy for the assertion to pass.
23
+ * If falsy, an error will be thrown.
24
+ */
25
+ function assert(description, condition) {
26
+ if (!description) {
27
+ throw new Error('When calling `assert`, you must provide a description as the first argument.');
28
+ }
29
+
30
+ if (condition) {
31
+ return;
32
+ }
33
+
34
+ throw new Error(`ASSERTION FAILED: ${description}`);
35
+ }
36
+
37
+ module.exports = assert;
@@ -0,0 +1,111 @@
1
+ 'use strict';
2
+
3
+ const chalk = require('chalk');
4
+ const semver = require('semver');
5
+ const assert = require('./assert');
6
+
7
+ /**
8
+ * Display a deprecation message.
9
+ *
10
+ * ```js
11
+ * const { deprecate } = require('ember-cli/lib/debug');
12
+ *
13
+ * deprecate('The `foo` method is deprecated.', false, {
14
+ * for: 'ember-cli',
15
+ * id: 'ember-cli.foo-method',
16
+ * since: {
17
+ * available: '4.1.0',
18
+ * enabled: '4.2.0',
19
+ * },
20
+ * until: '5.0.0',
21
+ * url: 'https://example.com',
22
+ * });
23
+ * ```
24
+ *
25
+ * @method deprecate
26
+ * @param {String} description Describes the deprecation.
27
+ * @param {Any} condition If falsy, the deprecation message will be displayed.
28
+ * @param {Object} options An object including the deprecation's details:
29
+ * - `for` The library that the deprecation is for
30
+ * - `id` The deprecation's unique id
31
+ * - `since.available` A SemVer version indicating when the deprecation was made available
32
+ * - `since.enabled` A SemVer version indicating when the deprecation was enabled
33
+ * - `until` A SemVer version indicating until when the deprecation will be active
34
+ * - `url` A URL that refers to additional information about the deprecation
35
+ */
36
+ function deprecate(description, condition, options) {
37
+ assert('When calling `deprecate`, you must provide a description as the first argument.', description);
38
+ assert('When calling `deprecate`, you must provide a condition as the second argument.', arguments.length > 1);
39
+
40
+ assert(
41
+ 'When calling `deprecate`, you must provide an options object as the third argument. The options object must include the `for`, `id`, `since` and `until` options (`url` is optional).',
42
+ options
43
+ );
44
+
45
+ assert('When calling `deprecate`, you must provide the `for` option.', options.for);
46
+ assert('When calling `deprecate`, you must provide the `id` option.', options.id);
47
+
48
+ assert(
49
+ 'When calling `deprecate`, you must provide the `since` option. `since` must include the `available` and/or the `enabled` option.',
50
+ options.since
51
+ );
52
+
53
+ assert(
54
+ 'When calling `deprecate`, you must provide the `since.available` and/or the `since.enabled` option.',
55
+ options.since.available || options.since.enabled
56
+ );
57
+
58
+ assert(
59
+ '`since.available` must be a valid SemVer version.',
60
+ !options.since.available || isSemVer(options.since.available)
61
+ );
62
+
63
+ assert('`since.enabled` must be a valid SemVer version.', !options.since.enabled || isSemVer(options.since.enabled));
64
+
65
+ assert(
66
+ 'When calling `deprecate`, you must provide a valid SemVer version for the `until` option.',
67
+ isSemVer(options.until)
68
+ );
69
+
70
+ if (condition) {
71
+ return;
72
+ }
73
+
74
+ let message = formatMessage(description, options);
75
+
76
+ warn(message);
77
+ warn(getStackTrace());
78
+
79
+ // Return the message for testing purposes.
80
+ // This can be removed once we can register deprecation handlers.
81
+ return message;
82
+ }
83
+
84
+ function isSemVer(version) {
85
+ return semver.valid(version) !== null;
86
+ }
87
+
88
+ function formatMessage(description, options) {
89
+ let message = [`DEPRECATION: ${description}`, `[ID: ${options.id}]`];
90
+
91
+ if (options.url) {
92
+ message.push(`See ${options.url} for more details.`);
93
+ }
94
+
95
+ return message.join(' ');
96
+ }
97
+
98
+ function getStackTrace() {
99
+ let error = new Error();
100
+ let lines = error.stack.split('\n');
101
+
102
+ lines.shift(); // Remove the word `Error`.
103
+
104
+ return lines.map((line) => line.trim()).join('\n');
105
+ }
106
+
107
+ function warn(message) {
108
+ console.warn(chalk.yellow(message));
109
+ }
110
+
111
+ module.exports = deprecate;
@@ -0,0 +1,6 @@
1
+ 'use strict';
2
+
3
+ module.exports = {
4
+ assert: require('./assert'),
5
+ deprecate: require('./deprecate'),
6
+ };
@@ -23,7 +23,9 @@ const EOL = require('os').EOL;
23
23
  const bowEpParser = require('bower-endpoint-parser');
24
24
  const logger = require('heimdalljs-logger')('ember-cli:blueprint');
25
25
  const normalizeEntityName = require('ember-cli-normalize-entity-name');
26
+ const { removeTypes } = require('remove-types');
26
27
  const isAddon = require('../utilities/is-addon');
28
+ const { deprecate } = require('../debug');
27
29
 
28
30
  const initialIgnoredFiles = ['.DS_Store'];
29
31
 
@@ -195,6 +197,18 @@ let Blueprint = CoreObject.extend({
195
197
 
196
198
  _printableProperties: ['name', 'description', 'availableOptions', 'anonymousOptions', 'overridden'],
197
199
 
200
+ /**
201
+ Indicates whether or not a blueprint is a candidate for automatic transpilation from TS to JS.
202
+ This property could be false in the case that the blueprint is written in JS and is not intended
203
+ to work with TS at all, OR in the case that the blueprint is written in TS and the author does
204
+ not intend to support transpilation to JS.
205
+
206
+ @public
207
+ @property shouldTransformTypeScript
208
+ @type Boolean
209
+ */
210
+ shouldTransformTypeScript: false,
211
+
198
212
  init(blueprintPath) {
199
213
  this._super();
200
214
 
@@ -429,12 +443,85 @@ let Blueprint = CoreObject.extend({
429
443
  let fileInfos = await process.call(this, intoDir, locals);
430
444
 
431
445
  // commit changes for each FileInfo (with prompting as needed)
432
- await Promise.all(fileInfos.map((fi) => this._commit(fi)));
446
+ await Promise.all(fileInfos.map((info) => this._commit(info)));
433
447
 
434
448
  // run afterInstall/afterUninstall userland hooks
435
449
  await afterHook.call(this, options);
436
450
  },
437
451
 
452
+ /**
453
+ @private
454
+ @method shouldConvertToJS
455
+ @param {Object} options
456
+ @param {FileInfo} fileInfo
457
+ @return {Boolean}
458
+ */
459
+ shouldConvertToJS(options, fileInfo) {
460
+ // If this isn't turned on, it doesn't matter what else was passed, we're not touching it.
461
+ if (!this.shouldTransformTypeScript) {
462
+ return false;
463
+ }
464
+
465
+ // If the blueprint isn't a TS file to begin with, there's nothing to convert.
466
+ if (!isTypeScriptFile(fileInfo.outputPath)) {
467
+ // If the user wants TypeScript output but there is no TypeScript blueprint available, we want
468
+ // to warn them that they're not going to get what they're expecting while still at least giving
469
+ // them the JS output. We check for this *after* checking `shouldTranformTypeScript` because
470
+ // it's possible for people to set `{typescript: true}` in their `.ember-cli` file, which would
471
+ // then erroneously trigger this message every time they generate a JS blueprint even though
472
+ // they didn't pass the flag.
473
+ if (options.typescript === true) {
474
+ this.ui.writeLine(
475
+ chalk.yellow(
476
+ "You passed the '--typescript' flag but there is no TypeScript blueprint available. " +
477
+ 'A JavaScript blueprint will be generated instead.'
478
+ )
479
+ );
480
+ }
481
+
482
+ return false;
483
+ }
484
+
485
+ // Indicates when the user explicitly passed either `--typescript` or `--no-typescript` as opposed
486
+ // to not passing a flag at all and allowing for default behavior
487
+ const userExplicitlySelectedTypeScriptStatus = options.typescript !== undefined;
488
+
489
+ // Indicates when the user has asked for TypeScript either globally (by setting
490
+ // `isTypeScriptProject` to true) or locally (by passing the `--typescript` flag when they
491
+ // invoked the generator). Although ember-cli merges `.ember-cli` and all of the flag values into
492
+ // one object, we thought the DX would be improved by differentiating between what is intended
493
+ // to be global vs. local config.
494
+ const shouldUseTypeScript = userExplicitlySelectedTypeScriptStatus
495
+ ? options.typescript
496
+ : options.isTypeScriptProject;
497
+
498
+ // if the user wants TS output and we have a TS file available, we do *not* want to downlevel to JS
499
+ if (shouldUseTypeScript) {
500
+ return false;
501
+ }
502
+
503
+ return true;
504
+ },
505
+
506
+ /**
507
+ @private
508
+ @method convertToJS
509
+ @param {FileInfo} fileInfo
510
+ @return {Promise}
511
+ */
512
+ async convertToJS(fileInfo) {
513
+ let rendered = await fileInfo.render();
514
+
515
+ const transformed = await removeTypes(rendered);
516
+
517
+ fileInfo.rendered = transformed;
518
+
519
+ fileInfo.displayPath = replaceExtension(fileInfo.displayPath, '.js');
520
+ fileInfo.outputPath = replaceExtension(fileInfo.outputPath, '.js');
521
+
522
+ return fileInfo;
523
+ },
524
+
438
525
  /**
439
526
  @method install
440
527
  @param {Object} options
@@ -756,6 +843,17 @@ let Blueprint = CoreObject.extend({
756
843
 
757
844
  return Promise.all(fileInfos.filter(isValidFile).map(prepareConfirm))
758
845
  .then(finishProcessingForInstall)
846
+ .then((fileInfos) => {
847
+ return Promise.all(
848
+ fileInfos.map((info) => {
849
+ if (this.shouldConvertToJS(this.options, info)) {
850
+ return this.convertToJS(info);
851
+ }
852
+
853
+ return info;
854
+ })
855
+ );
856
+ })
759
857
  .then((fileInfos) => fileInfos.concat(fileInfosToRemove));
760
858
  },
761
859
 
@@ -769,7 +867,45 @@ let Blueprint = CoreObject.extend({
769
867
 
770
868
  this._ignoreUpdateFiles();
771
869
 
772
- return finishProcessingForUninstall(fileInfos.filter(isValidFile));
870
+ fileInfos = fileInfos.filter(isValidFile).reduce((acc, info) => {
871
+ // if it's possible that this blueprint could have produced either typescript OR javascript, we have to do some
872
+ // work to figure out which files to delete.
873
+ if (this.shouldTransformTypeScript) {
874
+ if (this.options.typescript === true) {
875
+ // if the user explicitly passed `--typescript`, we only want to delete TS files, so we stick with the existing
876
+ // info object since it will contain a .ts outputPath (since we know this blueprint is authored in TS because
877
+ // of our check above)
878
+ acc.push(info);
879
+ return acc;
880
+ }
881
+
882
+ const jsInfo = new FileInfo({
883
+ ...info,
884
+ outputPath: replaceExtension(info.outputPath, '.js'),
885
+ displayPath: replaceExtension(info.displayPath, '.js'),
886
+ });
887
+
888
+ if (this.options.typescript === false) {
889
+ // if the user explicitly passed `--no-typescript`, we only want to delete JS file, so we return our newly
890
+ // created jsInfo object since it contains the javascript version of the output path.
891
+ acc.push(jsInfo);
892
+ return acc;
893
+ }
894
+
895
+ if (this.options.typescript === undefined) {
896
+ // if the user didn't specify one way or the other, then both the JS and TS paths are possibilities, so we add
897
+ // both of them to the list. `finishProcessingForUninstall` will actually look to see which of them exists and
898
+ // delete whatever it finds.
899
+ acc.push(info, jsInfo);
900
+ return acc;
901
+ }
902
+ }
903
+
904
+ acc.push(info);
905
+ return acc;
906
+ }, []);
907
+
908
+ return finishProcessingForUninstall(fileInfos);
773
909
  },
774
910
 
775
911
  /**
@@ -1017,10 +1153,29 @@ let Blueprint = CoreObject.extend({
1017
1153
  ```
1018
1154
  */
1019
1155
  addBowerPackageToProject(localPackageName, target, installOptions) {
1156
+ deprecate(
1157
+ [
1158
+ `(Blueprint: \`${this.name}\`) \`addBowerPackageToProject\` has been deprecated.`,
1159
+ 'If the package is also available on the npm registry, please use `addPackageToProject` instead.',
1160
+ 'If not, please install the Bower package manually by running:',
1161
+ `\`bower install ${localPackageName} --save\``,
1162
+ ].join('\n'),
1163
+ false,
1164
+ {
1165
+ for: 'ember-cli',
1166
+ id: 'ember-cli.blueprint.add-bower-package-to-project',
1167
+ since: {
1168
+ available: '4.2.0',
1169
+ enabled: '4.2.0',
1170
+ },
1171
+ until: '5.0.0',
1172
+ }
1173
+ );
1174
+
1020
1175
  let lpn = localPackageName;
1021
1176
  let tar = target;
1022
1177
  let packageObject = bowEpParser.json2decomposed(lpn, tar);
1023
- return this.addBowerPackagesToProject([packageObject], installOptions);
1178
+ return this.addBowerPackagesToProject([packageObject], installOptions, true);
1024
1179
  },
1025
1180
 
1026
1181
  /**
@@ -1039,7 +1194,7 @@ let Blueprint = CoreObject.extend({
1039
1194
  @param {Object} installOptions
1040
1195
  @return {Promise}
1041
1196
  */
1042
- addBowerPackagesToProject(packages, installOptions) {
1197
+ addBowerPackagesToProject(packages, installOptions, _deprecationCondition = false) {
1043
1198
  let task = this.taskFor('bower-install');
1044
1199
  let installText = packages.length > 1 ? 'install bower packages' : 'install bower package';
1045
1200
  let packageNames = [];
@@ -1051,6 +1206,25 @@ let Blueprint = CoreObject.extend({
1051
1206
  })
1052
1207
  .map(bowEpParser.compose);
1053
1208
 
1209
+ deprecate(
1210
+ [
1211
+ `(Blueprint: \`${this.name}\`) \`addBowerPackagesToProject\` has been deprecated.`,
1212
+ 'If the packages are also available on the npm registry, please use `addPackagesToProject` instead.',
1213
+ 'If not, please install the Bower packages manually by running:',
1214
+ `\`bower install ${packageNames.join(' ')} --save\``,
1215
+ ].join('\n'),
1216
+ _deprecationCondition,
1217
+ {
1218
+ for: 'ember-cli',
1219
+ id: 'ember-cli.blueprint.add-bower-packages-to-project',
1220
+ since: {
1221
+ available: '4.2.0',
1222
+ enabled: '4.2.0',
1223
+ },
1224
+ until: '5.0.0',
1225
+ }
1226
+ );
1227
+
1054
1228
  this._writeStatusToUI(chalk.green, installText, packageNames.join(', '));
1055
1229
 
1056
1230
  return task.run({
@@ -1279,6 +1453,23 @@ let Blueprint = CoreObject.extend({
1279
1453
  Blueprint.lookup = function (name, options) {
1280
1454
  options = options || {};
1281
1455
 
1456
+ if (name.includes(path.sep)) {
1457
+ let blueprintPath = path.resolve(name);
1458
+ let isNameAPath = Boolean(blueprintPath);
1459
+
1460
+ if (isNameAPath) {
1461
+ if (Blueprint._existsSync(blueprintPath)) {
1462
+ return Blueprint.load(blueprintPath);
1463
+ }
1464
+
1465
+ if (!options.ignoreMissing) {
1466
+ throw new SilentError(`Unknown blueprint: ${name}`);
1467
+ }
1468
+
1469
+ return;
1470
+ }
1471
+ }
1472
+
1282
1473
  let lookupPaths = generateLookupPaths(options.paths);
1283
1474
 
1284
1475
  let lookupPath;
@@ -1581,4 +1772,18 @@ function finishProcessingForUninstall(infos) {
1581
1772
  return validInfos;
1582
1773
  }
1583
1774
 
1775
+ function replaceExtension(filePath, newExt) {
1776
+ const { dir, name } = path.parse(filePath);
1777
+
1778
+ return path.format({
1779
+ dir,
1780
+ name,
1781
+ ext: newExt,
1782
+ });
1783
+ }
1784
+
1785
+ function isTypeScriptFile(filePath) {
1786
+ return path.extname(filePath) === '.ts';
1787
+ }
1788
+
1584
1789
  module.exports = Blueprint;
@@ -48,7 +48,7 @@ class InstallationChecker {
48
48
  }
49
49
 
50
50
  bowerDependenciesNotPresent() {
51
- return !fs.existsSync(this.project.bowerDirectory);
51
+ return !fs.existsSync(this.project._bowerDirectory);
52
52
  }
53
53
 
54
54
  hasNpmDeps() {
@@ -18,6 +18,7 @@ const PackageInfoCache = require('./package-info-cache');
18
18
  const PerBundleAddonCache = require('./per-bundle-addon-cache');
19
19
  const instantiateAddons = require('./instantiate-addons');
20
20
  const HostInfoCache = require('./host-info-cache');
21
+ const { deprecate } = require('../debug');
21
22
 
22
23
  let processCwd = process.cwd();
23
24
 
@@ -117,15 +118,36 @@ class Project {
117
118
 
118
119
  if (fs.existsSync(bowerrcPath)) {
119
120
  try {
120
- this.bowerDirectory = fs.readJsonSync(bowerrcPath).directory;
121
+ this._bowerDirectory = fs.readJsonSync(bowerrcPath).directory;
121
122
  } catch (exception) {
122
123
  logger.info('failed to parse bowerc: %s', exception);
123
- this.bowerDirectory = null;
124
+ this._bowerDirectory = null;
124
125
  }
125
126
  }
126
127
 
127
- this.bowerDirectory = this.bowerDirectory || 'bower_components';
128
- logger.info('bowerDirectory: %s', this.bowerDirectory);
128
+ this._bowerDirectory = this._bowerDirectory || 'bower_components';
129
+ logger.info('bowerDirectory: %s', this._bowerDirectory);
130
+ }
131
+
132
+ get bowerDirectory() {
133
+ deprecate(
134
+ [
135
+ '`bowerDirectory` has been deprecated.',
136
+ "If you still need access to the project's Bower directory, you will have to manually resolve the project's `.bowerrc` file, and read the `directory` property instead.",
137
+ ].join('\n'),
138
+ false,
139
+ {
140
+ for: 'ember-cli',
141
+ id: 'ember-cli.project.bower-directory',
142
+ since: {
143
+ available: '4.2.0',
144
+ enabled: '4.2.0',
145
+ },
146
+ until: '5.0.0',
147
+ }
148
+ );
149
+
150
+ return this._bowerDirectory;
129
151
  }
130
152
 
131
153
  // Checks whether the project's npm dependencies are
@@ -387,6 +409,27 @@ class Project {
387
409
  @return {Object} Bower dependencies
388
410
  */
389
411
  bowerDependencies(bower) {
412
+ deprecate(
413
+ [
414
+ '`bowerDependencies` has been deprecated.',
415
+ "If you still need access to the project's Bower dependencies, you will have to manually resolve the project's `bower.json` file instead.",
416
+ ].join('\n'),
417
+ false,
418
+ {
419
+ for: 'ember-cli',
420
+ id: 'ember-cli.project.bower-dependencies',
421
+ since: {
422
+ available: '4.2.0',
423
+ enabled: '4.2.0',
424
+ },
425
+ until: '5.0.0',
426
+ }
427
+ );
428
+
429
+ return this._bowerDependencies(bower);
430
+ }
431
+
432
+ _bowerDependencies(bower) {
390
433
  if (!bower) {
391
434
  let bowerPath = path.join(this.root, 'bower.json');
392
435
  bower = fs.existsSync(bowerPath) ? require(bowerPath) : {};