feat(compartment-mapper): support for defineProperty on exports with getters
MODIFY
packages/compartment-mapper/src/parse-cjs.js
packages/compartment-mapper/src/parse-cjs.js
@@ -4,7 +4,15 @@ import { analyzeCommonJS } from '@endo/cjs-module-analyzer';
const textDecoder = new TextDecoder();
-const { freeze, keys, create } = Object;
+const { apply } = Reflect;
+const { freeze, keys, create, hasOwnProperty } = Object;
+
+/**
+ * @param {Object} object
+ * @param {string} key
+ * @returns {boolean}
+ */
+const has = (object, key) => apply(hasOwnProperty, object, [key]);
function namespaceDefaultIsACopyOfRoot(namespace) {
const ns = new Set(keys(namespace));
@@ -49,10 +57,26 @@ export const parseCjs = async (
// this makes things like exports.hasOwnProperty() work.
return moduleEnvironmentRecord[prop] || target[prop];
},
- set(target, prop, value) {
+ set(_target, prop, value) {
moduleEnvironmentRecord[prop] = value;
return true;
},
+ defineProperty(_target, prop, descriptor) {
+ // Object.defineProperty(moduleEnvironmentRecord, prop, descriptor) would work if someone in the npm
+ // community didn't come up with the idea to redefine property in the getter.
+ if (has(descriptor, 'value')) {
+ // This will result in non-enumerable properties being enumerable, but it's better than undefined
+ moduleEnvironmentRecord[prop] = descriptor.value;
+ } else {
+ // Running the getter defeats some lazy initialization tricks.
+ // Whoever depends on getters on exports for lazy init instead
+ // of factory functions should face the consequences.
+ moduleEnvironmentRecord[prop] = descriptor.get
+ ? descriptor.get()
+ : undefined;
+ }
+ return true;
+ },
},
);

MODIFY
packages/compartment-mapper/src/parse-pre-cjs.js
packages/compartment-mapper/src/parse-pre-cjs.js
@@ -4,7 +4,15 @@ import { parseLocatedJson } from './json.js';
const textDecoder = new TextDecoder();
-const { freeze, keys, create } = Object;
+const { apply } = Reflect;
+const { freeze, keys, create, hasOwnProperty } = Object;
+
+/**
+ * @param {Object} object
+ * @param {string} key
+ * @returns {boolean}
+ */
+const has = (object, key) => apply(hasOwnProperty, object, [key]);
function namespaceDefaultIsACopyOfRoot(namespace) {
const ns = new Set(keys(namespace));
@@ -50,10 +58,26 @@ export const parsePreCjs = async (
// this makes things like exports.hasOwnProperty() work.
return moduleEnvironmentRecord[prop] || target[prop];
},
- set(target, prop, value) {
+ set(_target, prop, value) {
moduleEnvironmentRecord[prop] = value;
return true;
},
+ defineProperty(_target, prop, descriptor) {
+ // Object.defineProperty(moduleEnvironmentRecord, prop, descriptor) would work if someone in the npm
+ // community didn't come up with the idea to redefine property in the getter.
+ if (has(descriptor, 'value')) {
+ // This will result in non-enumerable properties being enumerable, but it's better than undefined
+ moduleEnvironmentRecord[prop] = descriptor.value;
+ } else {
+ // Running the getter defeats some lazy initialization tricks.
+ // Whoever depends on getters on exports for lazy init instead
+ // of factory functions should face the consequences.
+ moduleEnvironmentRecord[prop] = descriptor.get
+ ? descriptor.get()
+ : undefined;
+ }
+ return true;
+ },
},
);

ADD
packages/compartment-mapper/test/fixtures-cjs-compat/node_modules/defineprop/example-elliptic.js
+'use strict';
+
+var curves = exports;
+
+function PresetCurve(name) {
+ this.name = name;
+}
+curves.PresetCurve = PresetCurve;
+
+function defineCurve(name) {
+ Object.defineProperty(curves, name, {
+ configurable: true,
+ enumerable: true,
+ get: function() {
+ var curve = new PresetCurve(name);
+ Object.defineProperty(curves, name, {
+ configurable: true,
+ enumerable: true,
+ value: curve,
+ });
+ return curve;
+ },
+ });
+}
+
+defineCurve('p192')
+defineCurve('secp256k1')
\ No newline at end of file

ADD
packages/compartment-mapper/test/fixtures-cjs-compat/node_modules/defineprop/example-ethereum-util.js
+// fragments of logic adapted from ethereumjs-util@7.1.0
+'use strict';
+// prettier-ignore
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
+}) : (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+}));
+// prettier-ignore
+var __exportStar = (this && this.__exportStar) || function(m, exports) {
+ for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p);
+};
+Object.defineProperty(exports, '__esModule', { value: true });
+/// <reference path="./@types/ethjs-util/index.ts"/>
+
+const stuff = { isOk: 1 };
+__exportStar(stuff, exports);

MODIFY
packages/compartment-mapper/test/fixtures-cjs-compat/node_modules/defineprop/main.js
packages/compartment-mapper/test/fixtures-cjs-compat/node_modules/defineprop/main.js
-// fragments of logic adapted from ethereumjs-util@7.1.0
-'use strict';
-// prettier-ignore
-var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
- if (k2 === undefined) k2 = k;
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
-}) : (function(o, m, k, k2) {
- if (k2 === undefined) k2 = k;
- o[k2] = m[k];
-}));
-// prettier-ignore
-var __exportStar = (this && this.__exportStar) || function(m, exports) {
- for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p);
-};
-Object.defineProperty(exports, '__esModule', { value: true });
-/// <reference path="./@types/ethjs-util/index.ts"/>
+const curves = require('./example-elliptic');
+const ethereum = require('./example-ethereum-util');
-const stuff = { isOk: 1 };
-__exportStar(stuff, exports);
+if( ethereum.isOk !== 1 ) {
+ throw Error('Expected example-ethereum-util.js to export isOk')
+}
+// elliptic expects this to work
+if(
+!Object.prototype.hasOwnProperty.call(curves, 'p192') ||
+!Object.prototype.hasOwnProperty.call(curves, 'secp256k1')
+) {
+ throw Error('Expected example-elliptic.js to export p192 and secp256k1')
+}
+
+if(curves.p192.name !== 'p192' || curves.secp256k1.name !== 'secp256k1') {
+ throw Error(`Expected example-elliptic.js exports to contain values: p192,secp256k1 got: ${curves.p192.name},${curves.secp256k1.name}`)
+}
\ No newline at end of file

feat(compartment-mapper): support for defineProperty on exports with getters