Repository: ember-cli/ember-cli
Pull request #9562 (2021-06-02 17:27:21)

Update `addon-proxy` to support Embroider
This PR updates `addon-proxy` to properly support Embroider, specifically we need to handle `preprocessJs` separately for Embroider.

Some additional context here:

Embroider patches `preprocessJs`, so we want to maintain local state within the proxy rather than allowing a patched `preprocessJs` set on the original addon instance itself.

For more info as to where this happens, see:
https://github.com/embroider-build/embroider/blob/master/packages/compat/src/v1-addon.ts#L634


Patch:
diff --git a/lib/models/per-bundle-addon-cache/addon-proxy.js b/lib/models/per-bundle-addon-cache/addon-proxy.js
index 9c41b3c7d..ce1ccd1ce 100644
--- a/lib/models/per-bundle-addon-cache/addon-proxy.js
+++ b/lib/models/per-bundle-addon-cache/addon-proxy.js
@@ -64,6 +64,18 @@ function validateCacheKey(realAddonInstance, treeType, newCacheKey) {
 function getAddonProxy(targetCacheEntry, parent) {
   let _app;
 
+  // handle `preprocessJs` separately for Embroider
+  //
+  // some context here:
+  //
+  // Embroider patches `preprocessJs`, so we want to maintain local state within the
+  // proxy rather than allowing a patched `preprocessJs` set on the original addon
+  // instance itself
+  //
+  // for more info as to where this happens, see:
+  // https://github.com/embroider-build/embroider/blob/master/packages/compat/src/v1-addon.ts#L634
+  let _preprocessJs;
+
   return new Proxy(targetCacheEntry, {
     get(targetCacheEntry, property) {
       if (property === 'parent') {
@@ -74,6 +86,11 @@ function getAddonProxy(targetCacheEntry, parent) {
         return _app;
       }
 
+      // only return `_preprocessJs` here if it was previously set to a patched version
+      if (property === 'preprocessJs' && _preprocessJs) {
+        return _preprocessJs;
+      }
+
       // keep proxies from even trying to set or initialize addons
       if (property === 'initializeAddons') {
         return undefined;
@@ -124,6 +141,11 @@ function getAddonProxy(targetCacheEntry, parent) {
         return true;
       }
 
+      if (property === 'preprocessJs') {
+        _preprocessJs = value;
+        return true;
+      }
+
       if (targetCacheEntry[TARGET_INSTANCE]) {
         return Reflect.set(targetCacheEntry[TARGET_INSTANCE], property, value);
       }


Test patch:
diff --git a/tests/unit/models/per-bundle-addon-cache/addon-proxy-test.js b/tests/unit/models/per-bundle-addon-cache/addon-proxy-test.js
new file mode 100644
index 000000000..6e7231bf2
--- /dev/null
+++ b/tests/unit/models/per-bundle-addon-cache/addon-proxy-test.js
@@ -0,0 +1,48 @@
+'use strict';
+
+const expect = require('chai').expect;
+
+const { getAddonProxy } = require('../../../../lib/models/per-bundle-addon-cache/addon-proxy');
+const { TARGET_INSTANCE } = require('../../../../lib/models/per-bundle-addon-cache/target-instance');
+
+describe('Unit | addon-proxy-test', function () {
+  it('it allows a patched `preprocessJs` and is never set on the original addon instance', function () {
+    const realAddon = {
+      addons: ['foo'],
+      preprocessJs() {},
+    };
+
+    const proxy1 = getAddonProxy({ [TARGET_INSTANCE]: realAddon }, {});
+    const proxy2 = getAddonProxy({ [TARGET_INSTANCE]: realAddon }, {});
+
+    const originalPreprocessJs1 = proxy1.preprocessJs;
+    const originalPreprocessJs2 = proxy2.preprocessJs;
+
+    proxy1.preprocessJs = () => {};
+
+    expect(proxy1[TARGET_INSTANCE].preprocessJs).to.equal(
+      realAddon.preprocessJs,
+      "original addon's `preprocessJs` has not been modified"
+    );
+
+    proxy2.preprocessJs = () => {};
+
+    expect(proxy2[TARGET_INSTANCE].preprocessJs).to.equal(
+      realAddon.preprocessJs,
+      "original addon's `preprocessJs` has not been modified"
+    );
+
+    proxy1.preprocessJs();
+
+    // restore original
+    proxy1.preprocessJs = originalPreprocessJs1;
+
+    proxy2.preprocessJs();
+
+    // restore original
+    proxy2.preprocessJs = originalPreprocessJs2;
+
+    proxy1.preprocessJs();
+    proxy2.preprocessJs();
+  });
+});