diff --git a/packages/compat/src/v1-addon.ts b/packages/compat/src/v1-addon.ts index eefdef51a..bbe2ad510 100644 --- a/packages/compat/src/v1-addon.ts +++ b/packages/compat/src/v1-addon.ts @@ -70,6 +70,19 @@ const dynamicTreeHooks = Object.freeze([ 'treeForVendor', ]); +const defaultMethods = { + app: 'treeForApp', + addon: 'treeForAddon', + 'addon-styles': 'treeForAddonStyles', + 'addon-templates': 'treeForAddonTemplates', + 'addon-test-support': 'treeForAddonTestSupport', + public: 'treeForPublic', + styles: 'treeForStyles', + templates: 'treeForTemplates', + 'test-support': 'treeForTestSupport', + vendor: 'treeForVendor', +}; + const appPublicationDir = '_app_'; const fastbootPublicationDir = '_fastboot_'; @@ -400,12 +413,22 @@ export default class V1Addon { // customized hook exists in actual code exported from their index.js this.mainModule[treeName] || // addon instance doesn't match its own prototype - (realAddon.__proto__ && realAddon[treeName] !== realAddon.__proto__[treeName]) + (realAddon.__proto__ && realAddon[treeName] !== realAddon.__proto__[treeName]) || + this.customizesHookName(treeName) ); }) ); } + private customizesHookName(treeName: string): boolean { + for (let [name, methodName] of Object.entries(defaultMethods)) { + if (methodName === treeName) { + return this.addonInstance.treeForMethods?.[name] !== methodName; + } + } + return false; + } + @Memoize() private hasStockTree(treeName: AddonTreePath): boolean { if (this.suppressesTree(treeName)) { diff --git a/packages/shared-internals/src/ember-cli-models.ts b/packages/shared-internals/src/ember-cli-models.ts index 55d8038e2..074635dab 100644 --- a/packages/shared-internals/src/ember-cli-models.ts +++ b/packages/shared-internals/src/ember-cli-models.ts @@ -109,6 +109,7 @@ interface BaseAddonInstance { public: string; vendor: string; }; + treeForMethods: Record; } export type AddonTreePath = keyof BaseAddonInstance['treePaths']; diff --git a/tests/scenarios/stage1-test.ts b/tests/scenarios/stage1-test.ts index 5e363dd53..6a61800e4 100644 --- a/tests/scenarios/stage1-test.ts +++ b/tests/scenarios/stage1-test.ts @@ -272,6 +272,26 @@ appScenarios externallyCustomized.pkg.name = 'externally-customized'; troubleMaker.pkg.name = 'trouble-maker'; + // an addon that customizes a tree by mutating treeForMethods + let patchesMethodName = baseAddon(); + merge(patchesMethodName.files, { + injected: { + hello: { 'world.js': '// hello' }, + }, + 'index.js': ` + const { join } = require('path'); + module.exports = { + name: 'patches-method-name', + included() { + this.treeForMethods['addon'] = 'notTheUsual'; + }, + notTheUsual() { + return join(this.root, 'injected'); + } + }`, + }); + patchesMethodName.pkg.name = 'patches-method-name'; + // an addon that customizes packageJSON['ember-addon'].main and then uses // stock trees. Setting the main actually changes the root for *all* stock // trees. @@ -346,6 +366,7 @@ appScenarios project.addDependency(movedMain); project.addDependency(suppressed); project.addDependency(suppressedCustom); + project.addDependency(patchesMethodName); project.pkg['ember-addon'] = { paths: ['lib/disabled-in-repo-addon', 'lib/blacklisted-in-repo-addon'] }; merge(project.files, loadFromFixtureData('blacklisted-addon-build-options')); @@ -380,6 +401,10 @@ appScenarios assert.ok(fs.existsSync(join(workspaceDir, 'node_modules/externally-customized/public/hello/world.js'))); }); + test('custom tree hooks are detected when they have been customized via treeForMethod names', function (assert) { + assert.ok(fs.existsSync(join(workspaceDir, 'node_modules/patches-method-name/hello/world.js'))); + }); + test('addon with customized ember-addon.main can still use stock trees', function (assert) { let fileContents = fs.readFileSync(join(workspaceDir, 'node_modules/moved-main/helpers/hello.js')); assert.ok(/hello-world/.test(fileContents.toString()));