Repository: ember-cli/ember-cli
Pull request #8013 (2018-09-05 04:03:23)

Fix SASS compilation issues
There are several ways add-on can add a style to the application:

+ create `app/styles` folder
+ create `addon/styles` folder
+ return a tree from `treeForStyles` hook

Previously, some add-ons (when using `treeForStyles` hook) would return
content under `app/styles`. That used to work before packager changes.

Because there's no contract (interface) that Ember CLI provides when you return
a tree from `treeForStyles` hook, packager treats it as an opaque tree
and moves its contents later on the "right" place. It unfortunately
creates scenarios as such:

```
the-best-app-ever/
  styles/
    app/
      styles/
```

where nested `app/styles` is a tree from an add-on.

While I believe add-ons should return content under the "root" of the
tree, we do need to support current behaviour w/o breaking anything.

Useful links:

+ `app/styles` folder in
[`ember-basic-dropdown`](https://github.com/cibernox/ember-basic-dropdown/tree/v1.0.3/app/styles)
+ `treeForStyles` hook in
[`ember-cli-tailwind`](https://github.com/embermap/ember-cli-tailwind/blob/v0.6.1/index.js#L67)
+ `treeForStyles` hook in
[`ember-bootstrap`](https://github.com/kaliber5/ember-bootstrap/blob/v0.11.3/index.js#L54)


Patch:
diff --git a/lib/broccoli/ember-app.js b/lib/broccoli/ember-app.js
index 8382f8a9d..b4099463e 100644
--- a/lib/broccoli/ember-app.js
+++ b/lib/broccoli/ember-app.js
@@ -1072,12 +1072,26 @@ class EmberApp {
    * @return {BroccoliTree}
   */
   getAddonStyles() {
-    let mergedAddonStyles = mergeTrees(this.addonTreesFor('styles'), {
+    let addons = this.addonTreesFor('styles');
+
+    let flatten = addons.map(tree =>
+      new Funnel(tree, {
+        getDestinationPath(relativePath) {
+          let stylesDirExists = relativePath.startsWith('app/styles');
+
+          if (stylesDirExists) {
+            return relativePath.replace('app/styles/', '');
+          }
+
+          return relativePath;
+        },
+      })
+    );
+    let merged = mergeTrees(flatten, {
       overwrite: true,
       annotation: 'Addon Styles',
     });
-
-    return new Funnel(mergedAddonStyles, {
+    return new Funnel(merged, {
       allowEmpty: true,
       destDir: `${this.name}/styles`,
     });


Test patch:
diff --git a/tests/unit/broccoli/ember-app-test.js b/tests/unit/broccoli/ember-app-test.js
index 1d775af80..a42e5249f 100644
--- a/tests/unit/broccoli/ember-app-test.js
+++ b/tests/unit/broccoli/ember-app-test.js
@@ -249,6 +249,40 @@ describe('EmberApp', function() {
       yield output.dispose();
     }));
 
+    it('flattens `app/styles` folder from add-ons', co.wrap(function *() {
+      let addonFooStyles = yield createTempDir();
+
+      // `ember-cli-tailwind`
+      addonFooStyles.write({
+        app: {
+          styles: {
+            'foo.css': 'foo',
+          },
+        },
+      });
+
+      let app = new EmberApp({
+        project,
+      });
+      app.addonTreesFor = function() {
+        return [
+          addonFooStyles.path(),
+        ];
+      };
+
+      let output = yield buildOutput(app.getAddonStyles());
+      let outputFiles = output.read();
+
+      expect(outputFiles['test-project']).to.deep.equal({
+        styles: {
+          'foo.css': 'foo',
+        },
+      });
+
+      yield addonFooStyles.dispose();
+      yield output.dispose();
+    }));
+
     it('returns add-ons styles files', co.wrap(function *() {
       let addonFooStyles = yield createTempDir();
       let addonBarStyles = yield createTempDir();
@@ -283,11 +317,7 @@ describe('EmberApp', function() {
 
       expect(outputFiles['test-project']).to.deep.equal({
         styles: {
-          app: {
-            styles: {
-              'foo.css': 'foo',
-            },
-          },
+          'foo.css': 'foo',
           baztrap: {
             'baztrap.css': '// baztrap.css',
           },