Repository: ember-cli/ember-cli
Pull request #7054 (2017-05-19 15:01:42)

Allow imports from scoped packages
A couple small changes based on Slack conversation with @rwjblue and @Turbo87 yesterday:
 - prevent duplicate funnels being created for multiple imports from the same package
 - fix imports from scoped packages (previously would fail when trying to resolve `@scope/package.json` instead of `@scope/name/package.json`)


Patch:
diff --git a/lib/broccoli/ember-app.js b/lib/broccoli/ember-app.js
index 633beac18..3606fdcad 100644
--- a/lib/broccoli/ember-app.js
+++ b/lib/broccoli/ember-app.js
@@ -135,7 +135,7 @@ class EmberApp {
     this.otherAssetPaths = [];
     this.legacyTestFilesToAppend = [];
     this.vendorTestStaticStyles = [];
-    this._nodeModules = [];
+    this._nodeModules = new Map();
 
     this.trees = this.options.trees;
 
@@ -945,7 +945,7 @@ class EmberApp {
 
   _nodeModuleTrees() {
     if (!this._cachedNodeModuleTrees) {
-      this._cachedNodeModuleTrees = this._nodeModules.map(module => new Funnel(module.path, {
+      this._cachedNodeModuleTrees = Array.from(this._nodeModules.values(), module => new Funnel(module.path, {
         srcDir: '/',
         destDir: `node_modules/${module.name}/`,
         annotation: `Funnel (node_modules/${module.name})`,
@@ -1538,12 +1538,12 @@ class EmberApp {
       prepend: false,
     });
 
-    let match = assetPath.match(/^node_modules\/([^/]+)\//);
+    let match = assetPath.match(/^node_modules\/((@[^/]+\/)?[^/]+)\//);
     if (match) {
       let basedir = options.resolveFrom || this.project.root;
       let name = match[1];
       let _path = path.dirname(resolve.sync(`${name}/package.json`, { basedir }));
-      this._nodeModules.push({ name, path: _path });
+      this._nodeModules.set(_path, { name, path: _path });
     }
 
     let directory = path.dirname(assetPath);


Test patch:
diff --git a/tests/unit/broccoli/ember-app/import-test.js b/tests/unit/broccoli/ember-app/import-test.js
index 881bbad4d..ddf01d0a4 100644
--- a/tests/unit/broccoli/ember-app/import-test.js
+++ b/tests/unit/broccoli/ember-app/import-test.js
@@ -152,12 +152,19 @@ describe('EmberApp.import()', function() {
           'package.json': '{}',
           'moment.js': 'window.moment = "what does time even mean?";',
         },
+        '@scoped': {
+          'private': {
+            'package.json': '{}',
+            'index.js': 'window.secret = "sssshhhhh";',
+          },
+        },
       },
     });
 
     let app = createApp();
 
     app.import('node_modules/moment/moment.js');
+    app.import('node_modules/@scoped/private/index.js');
 
     let output = yield buildOutput(app.javascript());
     let outputTree = output.read();
@@ -166,6 +173,7 @@ describe('EmberApp.import()', function() {
     expect(outputTree['assets']['vendor.js']).to.contain('window.Ember = {');
     expect(outputTree['assets']['vendor.js']).to.contain('window.$ = function() {');
     expect(outputTree['assets']['vendor.js']).to.contain('window.moment');
+    expect(outputTree['assets']['vendor.js']).to.contain('window.secret');
   }));
 
   it('handles imports from node with different environments (development)', co.wrap(function *() {