Repository: ember-cli/ember-cli
Pull request #7674 (2018-03-11 20:43:55)

Package Public
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 2f92aa1d3..a89cb67ac 100644
--- a/lib/broccoli/default-packager.js
+++ b/lib/broccoli/default-packager.js
@@ -2,6 +2,7 @@
 
 const p = require('ember-cli-preprocess-registry/preprocessors');
 const Funnel = require('broccoli-funnel');
+const mergeTrees = require('./merge-trees');
 const addonProcessTree = require('../utilities/addon-process-tree');
 
 const preprocessJs = p.preprocessJs;
@@ -20,6 +21,7 @@ module.exports = class DefaultPackager {
     this._cachedTests = null;
     this._cachedBower = null;
     this._cachedVendor = null;
+    this._cachedPublic = null;
 
     this.options = options || {};
 
@@ -140,7 +142,6 @@ module.exports = class DefaultPackager {
    * @private
    * @method packageTests
    * @param {BroccoliTree} tree
-   * @param {String} name The name of Ember.js application
   */
   packageTests(tree) {
     if (this._cachedTests === null) {
@@ -160,4 +161,49 @@ module.exports = class DefaultPackager {
 
     return this._cachedTests;
   }
+
+  /*
+   * Given input trees (both application and add-ons), merges them into one.
+   *
+   * Given a tree:
+   *
+   * ```
+   * ├── 500.html
+   * ├── images
+   * ├── maintenance.html
+   * └── robots.txt
+   * ```
+   *
+   * And add-on tree:
+   *
+   * ```
+   * ember-fetch/
+   * └── fastboot-fetch.js
+   * ```
+   *
+   * Returns:
+   *
+   * ```
+   * ├── 500.html
+   * ├── ember-fetch
+   * │   └── fastboot-fetch.js
+   * ├── images
+   * ├── maintenance.html
+   * └── robots.txt
+   * ```
+   *
+   * @private
+   * @method packagePublic
+   * @param {Array<BroccoliTree>} trees
+  */
+  packagePublic(trees) {
+    if (this._cachedPublic === null) {
+      this._cachedPublic = mergeTrees(trees, {
+        overwrite: true,
+        annotation: 'Packaged Public',
+      });
+    }
+
+    return this._cachedPublic;
+  }
 };
diff --git a/lib/broccoli/ember-app.js b/lib/broccoli/ember-app.js
index b34641a06..01d90654b 100644
--- a/lib/broccoli/ember-app.js
+++ b/lib/broccoli/ember-app.js
@@ -986,25 +986,6 @@ class EmberApp {
     });
   }
 
-  /**
-    Returns the tree for /public
-
-    @private
-    @method publicTree
-    @return {Tree} Tree for /public
-   */
-  publicTree() {
-    let addonPublicTrees = this.addonTreesFor('public');
-
-    addonPublicTrees = addonPublicTrees.concat(this.trees.public);
-
-    return mergeTrees(addonPublicTrees, {
-      overwrite: true,
-      annotation: 'TreeMerge (public)',
-    });
-  }
-
-
   /**
     @private
     @method _processedAppTree
@@ -1442,7 +1423,7 @@ class EmberApp {
       annotation: 'TreeMerger (tests)',
     });
 
-    let coreTestTree = this._defaultPackager.packageTests(mergedTests, this.name);
+    let coreTestTree = this._defaultPackager.packageTests(mergedTests);
 
     let appTestTree = this.appTests(coreTestTree);
     let testFilesTree = this.testFiles(coreTestTree);
@@ -1935,6 +1916,11 @@ class EmberApp {
     @return {Array} An array of trees
    */
   toArray() {
+    let addonPublicTrees = this.addonTreesFor('public');
+    addonPublicTrees = addonPublicTrees.concat(this.trees.public);
+
+    let publicTree = this._defaultPackager.packagePublic(addonPublicTrees);
+
     let sourceTrees = [
       this.index(),
       this.javascript(),
@@ -1942,7 +1928,7 @@ class EmberApp {
       // undefined when `experiments.MODULE_UNIFICATION` is not available
       this._srcAfterStylePreprocessing,
       this.otherAssets(),
-      this.publicTree(),
+      publicTree,
     ].filter(Boolean);
 
     if (this.tests && this.trees.tests) {


Test patch:
diff --git a/tests/unit/broccoli/default-packager/public-test.js b/tests/unit/broccoli/default-packager/public-test.js
new file mode 100644
index 000000000..180999bd1
--- /dev/null
+++ b/tests/unit/broccoli/default-packager/public-test.js
@@ -0,0 +1,69 @@
+'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: Public', function() {
+  let appInput, addonInput, output;
+
+  let APP_PUBLIC = {
+    images: {},
+    'robots.txt': '',
+    '500.html': '',
+  };
+
+  let ADDON_PUBLIC = {
+    'ember-fetch': {
+      'fastboot-fetch.js': '',
+    },
+  };
+
+  before(co.wrap(function *() {
+    appInput = yield createTempDir();
+    addonInput = yield createTempDir();
+
+    appInput.write(APP_PUBLIC);
+    addonInput.write(ADDON_PUBLIC);
+  }));
+
+  after(co.wrap(function *() {
+    yield appInput.dispose();
+    yield addonInput.dispose();
+  }));
+
+  afterEach(co.wrap(function *() {
+    yield output.dispose();
+  }));
+
+  it('caches packaged public tree', co.wrap(function *() {
+    let defaultPackager = new DefaultPackager();
+
+    expect(defaultPackager._cachedPublic).to.equal(null);
+
+    output = yield buildOutput(defaultPackager.packagePublic([
+      appInput.path(),
+      addonInput.path(),
+    ]));
+
+    expect(defaultPackager._cachedPublic).to.not.equal(null);
+    expect(defaultPackager._cachedPublic._annotation).to.equal('Packaged Public');
+  }));
+
+  it('packages public files', co.wrap(function *() {
+    let defaultPackager = new DefaultPackager();
+
+    output = yield buildOutput(defaultPackager.packagePublic([
+      appInput.path(),
+      addonInput.path(),
+    ]));
+
+    let outputFiles = output.read();
+
+    expect(outputFiles).to.deep.equal(Object.assign(APP_PUBLIC, ADDON_PUBLIC));
+  }));
+});