Repository: ember-cli/ember-cli
Pull request #7806 (2018-05-01 21:51:37)

Split external tree and import transforms


Patch:
diff --git a/lib/broccoli/default-packager.js b/lib/broccoli/default-packager.js
index 2746bf33b..610a316a0 100644
--- a/lib/broccoli/default-packager.js
+++ b/lib/broccoli/default-packager.js
@@ -124,9 +124,50 @@ module.exports = class DefaultPackager {
     this.areTestsEnabled = this.options.areTestsEnabled;
     this.scriptOutputFiles = this.options.scriptOutputFiles;
     this.storeConfigInMeta = this.options.storeConfigInMeta;
+    this.customTransformsMap = this.options.customTransformsMap;
     this.isModuleUnificationEnabled = this.options.isModuleUnificationEnabled;
   }
 
+  /*
+   * Runs all registered transformations on the passed in tree and returns the
+   * result.
+   *
+   * Passed-in tree:
+   *
+   * ```
+   * /
+   * ├── addon-tree-output/
+   * ├── bower_components/
+   * └── vendor/
+   * ```
+   *
+   * `customTransformsMap` has information about files that needed to be
+   * transformed and the actual transformation functions that are executed.
+   *
+   * @private
+   * @method applyCustomTransforms
+   * @param {BroccoliTree} External (vendor) tree
+   * @return {BroccoliTree}
+  */
+  applyCustomTransforms(externalTree) {
+    for (let customTransformEntry of this.customTransformsMap) {
+      let transformName = customTransformEntry[0];
+      let transformConfig = customTransformEntry[1];
+
+      let transformTree = new Funnel(externalTree, {
+        files: transformConfig.files,
+        annotation: `Funnel (custom transform: ${transformName})`,
+      });
+
+      externalTree = mergeTrees([externalTree, transformConfig.callback(transformTree, transformConfig.options)], {
+        annotation: `TreeMerger (custom transform: ${transformName})`,
+        overwrite: true,
+      });
+    }
+
+    return externalTree;
+  }
+
   /*
    * Returns a single tree with `ember-cli` internal files with the following
    * structure:
diff --git a/lib/broccoli/ember-app.js b/lib/broccoli/ember-app.js
index 04f189bae..89d4bb28e 100644
--- a/lib/broccoli/ember-app.js
+++ b/lib/broccoli/ember-app.js
@@ -163,6 +163,7 @@ class EmberApp {
       areTestsEnabled: this.tests,
       scriptOutputFiles: this._scriptOutputFiles,
       autoRun: this.options.autoRun,
+      customTransformsMap: this._customTransformsMap,
       storeConfigInMeta: this.options.storeConfigInMeta,
       isModuleUnificationEnabled: experiments.MODULE_UNIFICATION && !!this.trees.src,
       distPaths: {
@@ -1218,22 +1219,37 @@ class EmberApp {
     return this._cachedAddonSrcTree;
   }
 
-  /**
-    @private
-    @method _processedExternalTree
-    @return
+  /*
+   * Gather all dependencies external to `ember-cli`, namely:
+   *
+   * + app `vendor` files
+   * + add-ons' `vendor` files
+   * + bower packages
+   * + node modules
+   *
+   * Resulting tree:
+   *
+   * ```
+   * /
+   * ├── addon-tree-output/
+   * ├── bower_components/
+   * └── vendor/
+   * ```
+   *
+   * @private
+   * @method getExternalTree
+   * @return {BroccoliTree}
   */
-  _processedExternalTree() {
+  getExternalTree() {
     if (!this._cachedExternalTree) {
       let vendorTrees = this.addonTreesFor('vendor');
 
       vendorTrees.push(this.trees.vendor);
 
-      let mergedVendorTrees = mergeTrees(vendorTrees, {
+      let vendor = this._defaultPackager.packageVendor(mergeTrees(vendorTrees, {
         overwrite: true,
         annotation: 'TreeMerger (vendor)',
-      });
-      let vendor = this._defaultPackager.packageVendor(mergedVendorTrees);
+      }));
 
       let addons = this.addonTree();
       let addonsSrc = this.addonSrcTree();
@@ -1247,27 +1263,10 @@ class EmberApp {
 
       trees = this._nodeModuleTrees().concat(trees);
 
-      let externalTree = mergeTrees(trees, {
+      this._cachedExternalTree = mergeTrees(trees, {
         annotation: 'TreeMerger (ExternalTree)',
         overwrite: true,
       });
-
-      for (let customTransformEntry of this._customTransformsMap) {
-        let transformName = customTransformEntry[0];
-        let transformConfig = customTransformEntry[1];
-
-        let transformTree = new Funnel(externalTree, {
-          files: transformConfig.files,
-          annotation: `Funnel (custom transform: ${transformName})`,
-        });
-
-        externalTree = mergeTrees([externalTree, transformConfig.callback(transformTree, transformConfig.options)], {
-          annotation: `TreeMerger (custom transform: ${transformName})`,
-          overwrite: true,
-        });
-      }
-
-      this._cachedExternalTree = externalTree;
     }
 
     return this._cachedExternalTree;
@@ -1327,7 +1326,7 @@ class EmberApp {
       overwrite: true,
     });
 
-    let external = this._processedExternalTree();
+    let external = this._defaultPackager.applyCustomTransforms(this._debugTree(this.getExternalTree(), 'external'));
     let postprocessedApp = this._defaultPackager.processJavascript(mergedTree);
     let emberCLITree = this._defaultPackager.packageEmberCliInternalFiles();
 
@@ -1473,7 +1472,7 @@ class EmberApp {
   styles() {
     if (!this._cachedStylesTree) {
       let addonTrees = this.addonTreesFor('styles');
-      let external = this._processedExternalTree();
+      let external = this.getExternalTree();
 
       let styles;
       if (this.trees.styles) {
@@ -1550,7 +1549,7 @@ class EmberApp {
 
     testSupportPath = testSupportPath.testSupport || testSupportPath;
 
-    let external = this._processedExternalTree();
+    let external = this.getExternalTree();
     let emberCLITree = this._defaultPackager.packageEmberCliInternalFiles();
     let addonTestSupportTree = this.addonTestSupportTree();
 
@@ -1617,7 +1616,7 @@ class EmberApp {
     @return {Tree} Merged tree for other assets
    */
   otherAssets() {
-    let external = this._processedExternalTree();
+    let external = this.getExternalTree();
     // combine obviously shared funnels.
     let otherAssetTrees = funnelReducer(this.otherAssetPaths).map(options => {
       options.annotation = `Funnel


Test patch:
diff --git a/tests/unit/broccoli/default-packager/external-test.js b/tests/unit/broccoli/default-packager/external-test.js
new file mode 100644
index 000000000..e9995394f
--- /dev/null
+++ b/tests/unit/broccoli/default-packager/external-test.js
@@ -0,0 +1,84 @@
+'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: External', function() {
+  let input, output;
+
+  let EXTERNAL = {
+    'addon-tree-output': {},
+    'bower_components': {},
+    vendor: {
+      'auth0-js.js': '',
+      'auth0-lock.js': '',
+      'auth0-lock-passwordless.js': '',
+    },
+  };
+
+  before(co.wrap(function *() {
+    input = yield createTempDir();
+
+    input.write(EXTERNAL);
+  }));
+
+  after(co.wrap(function *() {
+    yield input.dispose();
+  }));
+
+  afterEach(co.wrap(function *() {
+    yield output.dispose();
+  }));
+
+  it('applies transforms to an external tree', co.wrap(function *() {
+    let customTransformsMap = new Map();
+
+    customTransformsMap.set('amd', {
+      files: [
+        'vendor/auth0-js.js',
+        'vendor/auth0-lock.js',
+        'vendor/auth0-lock-passwordless.js',
+      ],
+      callback(tree, options) {
+        const stew = require('broccoli-stew');
+
+        return stew.map(tree, (content, relativePath) => {
+          const name = options[relativePath].as;
+
+          return `${name} was transformed`;
+        });
+      },
+      processOptions() { },
+      options: {
+        'vendor/auth0-js.js': {
+          as: 'auth0',
+        },
+        'vendor/auth0-lock-passwordless.js': {
+          as: 'auth0-lock-passwordless',
+        },
+        'vendor/auth0-lock.js': {
+          as: 'auth0-lock',
+        },
+      },
+    });
+
+    let defaultPackager = new DefaultPackager({
+      customTransformsMap,
+    });
+
+    output = yield buildOutput(defaultPackager.applyCustomTransforms(input.path()));
+
+    let outputFiles = output.read();
+
+    expect(outputFiles.vendor).to.deep.equal({
+      'auth0-js.js': 'auth0 was transformed',
+      'auth0-lock.js': 'auth0-lock was transformed',
+      'auth0-lock-passwordless.js': 'auth0-lock-passwordless was transformed',
+    });
+  }));
+});