Repository: ember-cli/ember-cli
Pull request #7654 (2018-02-27 21:59:01)

Package Vendor
This change is related to the [spike](https://github.com/ember-cli/ember-cli/pull/7562) and is backwards compatible. To avoid making changes to `ember-app` in one go, we can move processing/packaging logic (one tree at a time) to `DefaultPackager` while adding tests/documentation and deprecating private undocumented methods.


Patch:
diff --git a/lib/broccoli/default-packager.js b/lib/broccoli/default-packager.js
index 4c4e67d06..9a2dba8c5 100644
--- a/lib/broccoli/default-packager.js
+++ b/lib/broccoli/default-packager.js
@@ -3,6 +3,7 @@
 const Funnel = require('broccoli-funnel');
 
 const DEFAULT_BOWER_PATH = 'bower_components';
+const DEFAULT_VENDOR_PATH = 'vendor';
 
 /**
  * Responsible for packaging Ember.js application.
@@ -13,6 +14,7 @@ const DEFAULT_BOWER_PATH = 'bower_components';
 module.exports = class DefaultPackager {
   constructor() {
     this._cachedBower = null;
+    this._cachedVendor = null;
   }
 
   /*
@@ -44,7 +46,6 @@ module.exports = class DefaultPackager {
   packageBower(tree, bowerDirectory) {
     if (this._cachedBower === null) {
       this._cachedBower = new Funnel(tree, {
-        srcDir: '/',
         destDir: bowerDirectory || DEFAULT_BOWER_PATH,
         annotation: 'Packaged Bower',
       });
@@ -52,4 +53,48 @@ module.exports = class DefaultPackager {
 
     return this._cachedBower;
   }
+
+  /*
+   * Given an input tree, returns a properly assembled Broccoli tree with vendor
+   * files.
+   *
+   * Given a tree:
+   *
+   * ```
+   * ├── babel-polyfill/
+   * ├── ember-cli-shims/
+   * ├── ember-load-initializers/
+   * ├── ember-qunit/
+   * ├── ember-resolver/
+   * ├── sinon/
+   * └── tether/
+   * ```
+   *
+   * Returns:
+   *
+   * ```
+   * vendor/
+   * ├── babel-polyfill/
+   * ├── ember-cli-shims/
+   * ├── ember-load-initializers/
+   * ├── ember-qunit/
+   * ├── ember-resolver/
+   * ├── sinon/
+   * └── tether/
+   * ```
+   *
+   * @private
+   * @method packageVendor
+   * @param {BroccoliTree} tree
+  */
+  packageVendor(tree) {
+    if (this._cachedVendor === null) {
+      this._cachedVendor = new Funnel(tree, {
+        destDir: DEFAULT_VENDOR_PATH,
+        annotation: 'Packaged Vendor',
+      });
+    }
+
+    return this._cachedVendor;
+  }
 };
diff --git a/lib/broccoli/ember-app.js b/lib/broccoli/ember-app.js
index daf0b81ed..1ccb67593 100644
--- a/lib/broccoli/ember-app.js
+++ b/lib/broccoli/ember-app.js
@@ -1189,40 +1189,22 @@ class EmberApp {
 
   /**
     @private
-    @method _processedVendorTree
+    @method _processedExternalTree
     @return
   */
-  _processedVendorTree() {
-    if (!this._cachedVendorTree) {
-      let trees = this.addonTreesFor('vendor');
+  _processedExternalTree() {
+    if (!this._cachedExternalTree) {
+      let vendorTrees = this.addonTreesFor('vendor');
 
       if (this.trees.vendor) {
-        trees.push(this.trees.vendor);
+        vendorTrees.push(this.trees.vendor);
       }
 
-      let mergedVendor = mergeTrees(trees, {
+      let vendor = this._defaultPackager.packageVendor(mergeTrees(vendorTrees, {
         overwrite: true,
         annotation: 'TreeMerger (vendor)',
-      });
-
-      this._cachedVendorTree = new Funnel(mergedVendor, {
-        srcDir: '/',
-        destDir: 'vendor/',
-        annotation: 'Funnel (vendor)',
-      });
-    }
-
-    return this._cachedVendorTree;
-  }
+      }));
 
-  /**
-    @private
-    @method _processedExternalTree
-    @return
-  */
-  _processedExternalTree() {
-    if (!this._cachedExternalTree) {
-      let vendor = this._processedVendorTree();
       let addons = this.addonTree();
 
       let trees = [vendor].concat(addons);


Test patch:
diff --git a/tests/unit/broccoli/default-packager/vendor-test.js b/tests/unit/broccoli/default-packager/vendor-test.js
new file mode 100644
index 000000000..47d45a256
--- /dev/null
+++ b/tests/unit/broccoli/default-packager/vendor-test.js
@@ -0,0 +1,82 @@
+'use strict';
+
+const co = require('co');
+const expect = require('chai').expect;
+const DefaultPackager = require('../../../../lib/broccoli/default-packager');
+const broccoliTestHelper = require('broccoli-test-helper');
+
+const buildOutput = broccoliTestHelper.buildOutput;
+const createTempDir = broccoliTestHelper.createTempDir;
+
+describe('Default Packager: Vendor', function() {
+  let input, output;
+
+  let VENDOR_PACKAGES = {
+    'babel-polyfill': {
+      'polyfill.js': 'polyfill',
+      'polyfill.min.js': 'polyfill',
+    },
+    'ember-fetch.js': 'ember fetch',
+    'ember-weakmap-passthrough.js': 'ember weakmap',
+    'ember-weakmap-polyfill.js': 'ember weakmap',
+    'install-getowner-polyfill.js': 'install getowner',
+    'ember-cli-mirage': {
+      'prentender-shim.js': 'pretender',
+    },
+    'ember-cli-shims': {
+      'app-shims.js': 'app shims',
+      'deprecations.js': 'deprecations',
+    },
+    'ember': {
+      'ember.min.js': 'ember',
+    },
+    loader: {
+      'loader.js': 'loader',
+    },
+    'ember-resolver': {
+      'legacy-shims.js': 'legacy shims',
+    },
+    tether: {
+      js: {
+        'tether.js': 'tether',
+      },
+    },
+  };
+
+  before(co.wrap(function *() {
+    input = yield createTempDir();
+
+    input.write(VENDOR_PACKAGES);
+  }));
+
+  after(co.wrap(function *() {
+    yield input.dispose();
+  }));
+
+  afterEach(co.wrap(function *() {
+    yield output.dispose();
+  }));
+
+  it('caches packaged vendor tree', co.wrap(function *() {
+    let defaultPackager = new DefaultPackager();
+
+    expect(defaultPackager._cachedVendor).to.equal(null);
+
+    output = yield buildOutput(defaultPackager.packageVendor(input.path()));
+
+    expect(defaultPackager._cachedVendor).to.not.equal(null);
+    expect(defaultPackager._cachedVendor._annotation).to.equal('Packaged Vendor');
+  }));
+
+  it('packages vendor files', co.wrap(function *() {
+    let defaultPackager = new DefaultPackager();
+
+    output = yield buildOutput(defaultPackager.packageVendor(input.path()));
+
+    let outputFiles = output.read();
+
+    expect(outputFiles).to.deep.equal({
+      vendor: VENDOR_PACKAGES,
+    });
+  }));
+});
diff --git a/tests/unit/broccoli/ember-app-test.js b/tests/unit/broccoli/ember-app-test.js
index e2fdda651..c2597a44b 100644
--- a/tests/unit/broccoli/ember-app-test.js
+++ b/tests/unit/broccoli/ember-app-test.js
@@ -537,14 +537,6 @@ describe('EmberApp', function() {
           td.when(app.addonTreesFor(), { ignoreExtraArgs: true }).thenReturn(['batman']);
         });
 
-        it('_processedVendorTree calls addonTreesFor', function() {
-          app._processedVendorTree();
-
-          let args = td.explain(app.addonTreesFor).calls.map(function(call) { return call.args[0]; });
-
-          expect(args).to.deep.equal(['vendor']);
-        });
-
         it('_processedAppTree calls addonTreesFor', function() {
           app._processedAppTree();