Skip to content

Commit

Permalink
Merge pull request #941 from eoneill/ember-cli-addon-proxy
Browse files Browse the repository at this point in the history
  • Loading branch information
rwjblue authored Aug 26, 2021
2 parents 25f368a + f3bc77b commit 56391d8
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 7 deletions.
32 changes: 32 additions & 0 deletions packages/compat/src/get-real-addon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// As of [email protected], addon instances _may_ be proxied. This can become a
// problem when patching (setting/restoring) methods on the addon instance or
// comparing with the addon `__proto__`. The purpose of this util method is to
// correctly return the _real_ (original) addon instance and not the proxy.
// @see https://github.com/ember-cli/ember-cli/pull/9487

let TARGET_INSTANCE_SYMBOL: any;

try {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const targetInstanceModule = require('ember-cli/lib/models/per-bundle-addon-cache/target-instance');

if (targetInstanceModule) {
TARGET_INSTANCE_SYMBOL = targetInstanceModule.TARGET_INSTANCE;
}
} catch (e) {
// we only want to handle the error when this module isn't found; i.e.,
// when a consumer of `ember-engines` is using an old version of `ember-cli`
// (less than `ember-cli` 3.28)
if (!e || e.code !== 'MODULE_NOT_FOUND') {
throw e;
}
}

/**
* Given an addon instance, gets the _real_ addon instance
* @param maybeProxyAddon - the addon instance, which may be a proxy
* @returns the _real_ (not proxied) addon instance
*/
export default function getRealAddon(maybeProxyAddon: any): any {
return (TARGET_INSTANCE_SYMBOL && maybeProxyAddon[TARGET_INSTANCE_SYMBOL]) || maybeProxyAddon;
}
21 changes: 14 additions & 7 deletions packages/compat/src/v1-addon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import TemplateCompilerBroccoliPlugin from './template-compiler-broccoli-plugin'
import { fromPairs } from 'lodash';
import { getEmberExports } from '@embroider/core/src/load-ember-template-compiler';
import prepHtmlbarsAstPluginsForUnwrap from './prepare-htmlbars-ast-plugins';
import getRealAddon from './get-real-addon';

const stockTreeNames = Object.freeze([
'addon',
Expand Down Expand Up @@ -357,13 +358,15 @@ export default class V1Addon {
}

protected customizes(...treeNames: string[]) {
// get the real addon as we're going to compare with __proto__
const realAddon = getRealAddon(this.addonInstance);
return Boolean(
treeNames.find(treeName => {
return (
// customized hook exists in actual code exported from their index.js
this.mainModule[treeName] ||
// addon instance doesn't match its own prototype
(this.addonInstance.__proto__ && this.addonInstance[treeName] !== this.addonInstance.__proto__[treeName])
(realAddon.__proto__ && realAddon[treeName] !== realAddon.__proto__[treeName])
);
})
);
Expand Down Expand Up @@ -605,9 +608,11 @@ export default class V1Addon {
if (!this.customizes('treeFor')) {
return false;
}
// get the real addon as we're going to patch and restore `_super`
const realAddon = getRealAddon(this.addonInstance);
let origSuper = this.addonInstance._super;
try {
this.addonInstance._super = stubbedSuper;
realAddon._super = stubbedSuper;
let result = this.mainModule.treeFor?.call(this.addonInstance, name);
if (result === markedEmptyTree) {
// the method returns _super unchanged, so tree is not suppressed and we
Expand All @@ -623,8 +628,8 @@ export default class V1Addon {
unsupported(`${this.name} has a custom treeFor() method that is doing some arbitrary broccoli processing.`);
return false;
} finally {
if (this.addonInstance._super === stubbedSuper) {
this.addonInstance._super = origSuper;
if (realAddon._super === stubbedSuper) {
realAddon._super = origSuper;
}
}
}
Expand All @@ -634,11 +639,13 @@ export default class V1Addon {
{ neuterPreprocessors } = { neuterPreprocessors: false }
): Node | undefined {
return this.throughTreeCache(name, 'original', () => {
// get the real addon as we're going to patch and restore `preprocessJs`
const realAddon = getRealAddon(this.addonInstance);
let original;
try {
if (neuterPreprocessors) {
original = this.addonInstance.preprocessJs;
this.addonInstance.preprocessJs = function (tree: Node) {
original = realAddon.preprocessJs;
realAddon.preprocessJs = function (tree: Node) {
return tree;
};
}
Expand All @@ -648,7 +655,7 @@ export default class V1Addon {
return this.addonInstance._treeFor(name);
} finally {
if (neuterPreprocessors) {
this.addonInstance.preprocessJs = original;
realAddon.preprocessJs = original;
}
}
});
Expand Down

0 comments on commit 56391d8

Please sign in to comment.