Repository: ember-cli/ember-cli
Pull request #9505 (2021-04-19 17:05:44)

Pass `realPath` as `root` rather than the dirname for `addonMainPath`
Currently it's possible for package infos representing the same addon to differ throughout the project. This is only true if an addon has an entry point located outside of `realPath` (eg, `realPath/lib/main.js`). This fixes this bug and adds a test-case to guard against this in the future.


Patch:
diff --git a/lib/models/package-info-cache/package-info.js b/lib/models/package-info-cache/package-info.js
index 6e30cf80b..2d10470f7 100644
--- a/lib/models/package-info-cache/package-info.js
+++ b/lib/models/package-info-cache/package-info.js
@@ -390,18 +390,17 @@ class PackageInfo {
     // TODO: Future work - allow a time budget for loading each addon and warn
     // or error for those that take too long.
     let module = require(this.addonMainPath);
-    let mainDir = path.dirname(this.addonMainPath);
 
     let ctor;
 
     if (typeof module === 'function') {
       ctor = module;
-      ctor.prototype.root = ctor.prototype.root || mainDir;
+      ctor.prototype.root = ctor.prototype.root || this.realPath;
       ctor.prototype.pkg = ctor.prototype.pkg || this.pkg;
     } else {
       const Addon = require('../addon'); // done here because of circular dependency
 
-      ctor = Addon.extend(Object.assign({ root: mainDir, pkg: this.pkg }, module));
+      ctor = Addon.extend(Object.assign({ root: this.realPath, pkg: this.pkg }, module));
     }
 
     ctor._meta_ = {


Test patch:
diff --git a/tests/fixtures/addon/simple/lib/ember-super-button/lib/ember-with-addon-main/lib/main.js b/tests/fixtures/addon/simple/lib/ember-super-button/lib/ember-with-addon-main/lib/main.js
new file mode 100644
index 000000000..3ad817af8
--- /dev/null
+++ b/tests/fixtures/addon/simple/lib/ember-super-button/lib/ember-with-addon-main/lib/main.js
@@ -0,0 +1,5 @@
+'use strict';
+
+module.exports = {
+  name: require('../package').name,
+};
diff --git a/tests/fixtures/addon/simple/lib/ember-super-button/lib/ember-with-addon-main/package.json b/tests/fixtures/addon/simple/lib/ember-super-button/lib/ember-with-addon-main/package.json
new file mode 100644
index 000000000..6cf2ccf37
--- /dev/null
+++ b/tests/fixtures/addon/simple/lib/ember-super-button/lib/ember-with-addon-main/package.json
@@ -0,0 +1,9 @@
+{
+  "name": "ember-with-addon-main",
+  "keywords": [
+    "ember-addon"
+  ],
+  "ember-addon": {
+    "main": "lib/main.js"
+  }
+}
diff --git a/tests/fixtures/addon/simple/lib/ember-super-button/package.json b/tests/fixtures/addon/simple/lib/ember-super-button/package.json
index d493c4084..a2c50d4bb 100644
--- a/tests/fixtures/addon/simple/lib/ember-super-button/package.json
+++ b/tests/fixtures/addon/simple/lib/ember-super-button/package.json
@@ -4,9 +4,12 @@
     "ember-addon"
   ],
   "ember-addon": {
-    "paths": ["./lib/ember-ng"]
+    "paths": [
+      "./lib/ember-ng",
+      "./lib/ember-with-addon-main"
+    ]
   },
   "dependencies": {
     "ember-yagni": "0"
   }
-}
+}
\ No newline at end of file
diff --git a/tests/fixtures/addon/simple/package.json b/tests/fixtures/addon/simple/package.json
index e1db417e4..ef32076f9 100644
--- a/tests/fixtures/addon/simple/package.json
+++ b/tests/fixtures/addon/simple/package.json
@@ -5,7 +5,10 @@
     "something-else": "latest"
   },
   "ember-addon": {
-    "paths": ["./lib/ember-super-button"]
+    "paths": [
+      "./lib/ember-super-button",
+      "./lib/ember-super-button/lib/ember-with-addon-main"
+    ]
   },
   "devDependencies": {
     "ember-resolver": "^7.0.0",
@@ -20,4 +23,4 @@
     "ember-addon-with-dependencies": "latest",
     "loader.js": "latest"
   }
-}
+}
\ No newline at end of file
diff --git a/tests/unit/models/package-info-cache/package-info-cache-test.js b/tests/unit/models/package-info-cache/package-info-cache-test.js
index 3687a48fb..0de1cddc7 100644
--- a/tests/unit/models/package-info-cache/package-info-cache-test.js
+++ b/tests/unit/models/package-info-cache/package-info-cache-test.js
@@ -64,13 +64,13 @@ describe('models/package-info-cache/package-info-cache-test.js', function () {
   });
 
   describe('packageInfo contents tests on valid project', function () {
-    let projectPath, packageJsonPath, packageContents, projectPackageInfo;
+    let project, projectPath, packageJsonPath, packageContents, projectPackageInfo;
 
     beforeEach(function () {
       projectPath = path.resolve(addonFixturePath, 'simple');
       packageJsonPath = path.join(projectPath, 'package.json');
       packageContents = require(packageJsonPath);
-      let project = new Project(projectPath, packageContents, ui, cli);
+      project = new Project(projectPath, packageContents, ui, cli);
       let pic = project.packageInfoCache;
 
       projectPackageInfo = pic.getEntry(projectPath);
@@ -151,12 +151,21 @@ describe('models/package-info-cache/package-info-cache-test.js', function () {
       expect(packageAndErrorNames).to.deep.equal(devDependencyNames);
     });
 
-    it('shows projectPackageInfo has 1 in-repo addon named "ember-super-button"', function () {
+    it('shows projectPackageInfo has 2 in-repo addons', function () {
       let inRepoAddons = projectPackageInfo.inRepoAddons;
+
       expect(inRepoAddons).to.exist;
-      expect(inRepoAddons.length).to.equal(1);
+      expect(inRepoAddons.length).to.equal(2);
+
       expect(inRepoAddons[0].realPath.indexOf(`simple${path.sep}lib${path.sep}ember-super-button`)).to.be.above(0);
       expect(inRepoAddons[0].pkg.name).to.equal('ember-super-button');
+
+      expect(
+        inRepoAddons[1].realPath.indexOf(
+          `simple${path.sep}lib${path.sep}ember-super-button${path.sep}lib${path.sep}ember-with-addon-main`
+        )
+      ).to.be.above(0);
+      expect(inRepoAddons[1].pkg.name).to.equal('ember-with-addon-main');
     });
 
     it('shows projectPackageInfo has 7 internal addon packages', function () {
@@ -172,6 +181,60 @@ describe('models/package-info-cache/package-info-cache-test.js', function () {
       expect(nodeModules.entries).to.exist;
       expect(Object.keys(nodeModules.entries).length).to.equal(9);
     });
+
+    it('returns stable package infos for a package info representing the same addon', function () {
+      project.initializeAddons();
+
+      const findAddonsByName = (projectOrAddon, addonToFind, _foundAddons = []) => {
+        if (!projectOrAddon) {
+          return _foundAddons;
+        }
+
+        projectOrAddon.addons.forEach((addon) => {
+          if (addon.name === addonToFind) {
+            _foundAddons.push(addon);
+          }
+
+          findAddonsByName(addon, addonToFind, _foundAddons);
+        });
+
+        return _foundAddons;
+      };
+
+      const findAllInRepoPackageInfosByPredicate = (packageInfo, predicate, _foundPackageInfos = []) => {
+        if (predicate(packageInfo)) {
+          _foundPackageInfos.push(packageInfo);
+        }
+
+        (packageInfo.inRepoAddons || []).forEach((addonPackageInfo) =>
+          findAllInRepoPackageInfosByPredicate(addonPackageInfo, predicate, _foundPackageInfos)
+        );
+
+        return _foundPackageInfos;
+      };
+
+      let allAddonsWithAddonMain = findAddonsByName(project, 'ember-with-addon-main');
+
+      let projectAddonWithMainPackageInfo = findAllInRepoPackageInfosByPredicate(
+        project._packageInfo,
+        (packageInfo) =>
+          typeof packageInfo.addonMainPath === 'string' &&
+          packageInfo.addonMainPath.endsWith(path.join('ember-with-addon-main', 'lib', 'main.js'))
+      );
+
+      let allPackageInfosForAddonWithMain = [
+        ...allAddonsWithAddonMain.map((addon) => addon._packageInfo),
+        ...projectAddonWithMainPackageInfo,
+      ];
+
+      let areAllPackageInfosEqual = allPackageInfosForAddonWithMain.every(
+        (packageInfo) => packageInfo === allPackageInfosForAddonWithMain[0]
+      );
+
+      expect(allAddonsWithAddonMain.length).to.equal(2);
+      expect(allPackageInfosForAddonWithMain.length).to.equal(4);
+      expect(areAllPackageInfosEqual).to.equal(true);
+    });
   });
 
   describe('packageInfo', function () {
diff --git a/tests/unit/models/project-test.js b/tests/unit/models/project-test.js
index 2e5893091..654fa4cec 100644
--- a/tests/unit/models/project-test.js
+++ b/tests/unit/models/project-test.js
@@ -347,6 +347,7 @@ describe('models/project.js', function () {
         'ember-non-root-addon',
         'ember-random-addon',
         'ember-super-button',
+        'ember-with-addon-main',
       ];
       expect(Object.keys(project.addonPackages)).to.deep.equal(expected);
     });