Skip to content

Commit a472eb1

Browse files
authored
Merge pull request #1 from meteor/fix/check-scope-when-wrapping
Check scope before wrapping assignment benjamn#326
2 parents 367582a + d4ba96f commit a472eb1

File tree

23 files changed

+157
-4064
lines changed

23 files changed

+157
-4064
lines changed

.gitignore

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,6 @@
22
*.log
33
test/.cache
44
test/**/*.gz
5-
node_modules
5+
/node_modules
6+
.reify-cache
7+
.idea

README.md

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# re&middot;i&middot;fy <sub>_verb, transitive_</sub> &nbsp; [![Build Status](https://github.com/benjamn/reify/actions/workflows/node.js.yml/badge.svg)](https://github.com/benjamn/reify/actions/workflows/node.js.yml)
1+
# re&middot;i&middot;fy <sub>_verb, transitive_</sub> &nbsp; [![Build Status](https://github.com/meteor/reify/actions/workflows/node.js.yml/badge.svg)](https://github.com/meteor/reify/actions/workflows/node.js.yml)
22

33
**re&middot;i&middot;fied** <sub>past</sub> &nbsp; **re&middot;i&middot;fies** <sub>present</sub> &nbsp; **re&middot;i&middot;fy&middot;ing** <sub>participle</sub> &nbsp; **re&middot;i&middot;fi&middot;ca&middot;tion** <sub>noun</sub> &nbsp; **re&middot;i&middot;fi&middot;er** <sub>noun</sub>
44

@@ -10,17 +10,17 @@
1010
Usage
1111
---
1212

13-
1. Run `npm install --save reify` in your package or app directory. The
13+
1. Run `npm install --save @meteorjs/reify` in your package or app directory. The
1414
`--save` is important because reification only applies to modules in
15-
packages that explicitly depend on the `reify` package.
16-
2. Call `require("reify")` before importing modules that contain `import`
15+
packages that explicitly depend on the `@meteorjs/reify` package.
16+
2. Call `require("@meteorjs/reify")` before importing modules that contain `import`
1717
and `export` declarations.
1818

19-
You can also easily `reify` the Node REPL:
19+
You can also easily `@meteorjs/reify` the Node REPL:
2020

2121
```sh
2222
% node
23-
> require("reify")
23+
> require("@meteorjs/reify")
2424
{}
2525
> import { strictEqual } from "assert"
2626
> strictEqual(2 + 2, 5)
+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
require("reify/node");
1+
require("@meteorjs/reify/node");
22
module.exports = require("./module.js");

examples/babel-parser-example/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"node": ">=4"
2525
},
2626
"dependencies": {
27-
"reify": "^0.18.1"
27+
"@meteorjs/reify": "0.23.0-beta.0"
2828
},
2929
"reify": {
3030
"parser": "reify/lib/parsers/babel.js"
+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
require("reify/node");
1+
require("@meteorjs/reify/node");
22
module.exports = require("./module.js");

examples/custom-cache-example/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"node": ">=4"
2525
},
2626
"dependencies": {
27-
"reify": "^0.5.1"
27+
"@meteorjs/reify": "0.23.0-beta.0"
2828
},
2929
"reify": {
3030
"cache-directory": ".reify-custom-cache"

examples/top-level-example/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
require("reify/node");
1+
require("@meteorjs/reify/node");
22
module.exports = require("./module.js");

examples/top-level-example/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"node": ">=4"
2525
},
2626
"dependencies": {
27-
"reify": "^0.5.1"
27+
"@meteorjs/reify": "0.23.0-beta.0"
2828
},
2929
"reify": {
3030
"parser": "reify/lib/parsers/top-level.js"

lib/assignment-visitor.js

+40-7
Original file line numberDiff line numberDiff line change
@@ -80,26 +80,59 @@ function assignmentHelper(visitor, path, childName) {
8080
const assignedNames = utils.getNamesFromPattern(child);
8181
const nameCount = assignedNames.length;
8282

83-
// Wrap assignments to exported identifiers with `module.runSetters`.
83+
let scope = null;
84+
function inModuleScope(name) {
85+
if (scope === null) {
86+
scope = path.getScope();
87+
}
88+
89+
return !scope || scope.find_owner(name).parent === null;
90+
}
91+
92+
let modifiedExports = [];
93+
94+
// Identify which exports if any are modified by the assignment.
8495
for (let i = 0; i < nameCount; ++i) {
85-
if (visitor.exportedLocalNames[assignedNames[i]] === true) {
86-
wrap(visitor, path);
87-
break;
96+
let name = assignedNames[i];
97+
if (
98+
visitor.exportedLocalNames[name] &&
99+
inModuleScope(name)
100+
) {
101+
modifiedExports.push(...visitor.exportedLocalNames[name]);
88102
}
89103
}
104+
105+
// Wrap assignments to exported identifiers with `module.runSetters`.
106+
if (modifiedExports.length > 0) {
107+
wrap(visitor, path, modifiedExports);
108+
}
90109
}
91110

92-
function wrap(visitor, path) {
111+
function wrap(visitor, path, names) {
93112
const value = path.getValue();
94113

95114
if (visitor.magicString !== null) {
115+
let end = ')';
116+
if (names) {
117+
end = `,[${names.map(n => `"${n}"`).join(',')}])`;
118+
}
119+
96120
visitor.magicString.prependRight(
97121
value.start,
98122
visitor.moduleAlias + ".runSetters("
99-
).appendLeft(value.end, ")");
123+
).appendLeft(value.end, end);
100124
}
101125

102126
if (visitor.modifyAST) {
127+
let args = [value];
128+
if (names) {
129+
let array = {
130+
type: "ArrayExpression",
131+
elements: names.map(n => ({ type: "StringLiteral", value: n }))
132+
};
133+
args.push(array);
134+
}
135+
103136
path.replace({
104137
type: "CallExpression",
105138
callee: {
@@ -113,7 +146,7 @@ function wrap(visitor, path) {
113146
name: "runSetters"
114147
}
115148
},
116-
arguments: [value]
149+
arguments: args
117150
});
118151
}
119152
}

lib/fast-path.js

+17
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
const assert = require("assert");
77
const utils = require("./utils.js");
8+
const periscopic = require('periscopic');
89
const brandKey = "reify-fast-path";
910
const brand = typeof Symbol === "function"
1011
? Symbol.for(brandKey)
@@ -15,6 +16,7 @@ class FastPath {
1516
assert.notStrictEqual(ast, null);
1617
assert.strictEqual(typeof ast, "object");
1718
this.stack = [ast];
19+
this._scope = null;
1820
}
1921

2022
static isInstance(value) {
@@ -102,6 +104,21 @@ class FastPath {
102104
return getStackAt(this, 1);
103105
}
104106

107+
getScope() {
108+
if (!this._scope) {
109+
this._scope = periscopic.analyze(this.stack[0]);
110+
}
111+
112+
let node;
113+
let pos = 0;
114+
while (node = getNodeAt(this, pos++)) {
115+
let scope = this._scope.map.get(node);
116+
if (scope) {
117+
return scope;
118+
}
119+
}
120+
}
121+
105122
replace(newValue) {
106123
const s = this.stack;
107124
const len = s.length;

lib/import-export-visitor.js

+4-6
Original file line numberDiff line numberDiff line change
@@ -457,12 +457,10 @@ function addExportedLocalNames(visitor, specifierMap) {
457457
const localCount = locals.length;
458458

459459
for (let j = 0; j < localCount; ++j) {
460-
// It's tempting to record the exported name as the value here,
461-
// instead of true, but there can be more than one exported name
462-
// per local variable, and we don't actually use the exported
463-
// name(s) in the assignmentVisitor, so it's not worth the added
464-
// complexity of tracking unused information.
465-
exportedLocalNames[locals[j]] = true;
460+
if (exportedLocalNames[locals[j]] === undefined) {
461+
exportedLocalNames[locals[j]] = [];
462+
}
463+
exportedLocalNames[locals[j]].push(exported);
466464
}
467465
}
468466
}

lib/runtime/index.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@ function moduleExportAs(name) {
117117
// the module system is about to return module.exports from require. This
118118
// might happen more than once per module, in case of dependency cycles,
119119
// so we want Module.prototype.runSetters to run each time.
120-
function runSetters(valueToPassThrough) {
121-
Entry.getOrCreate(this.id, this).runSetters();
120+
function runSetters(valueToPassThrough, names) {
121+
Entry.getOrCreate(this.id, this).runSetters(names, true);
122122

123123
// Assignments to exported local variables get wrapped with calls to
124124
// module.runSetters, so module.runSetters returns the

node/utils.js

+10-4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ const FastObject = require("../lib/fast-object.js");
1111
const PkgInfo = require("./pkg-info.js");
1212
const SemVer = require("semver");
1313

14+
const REIFY_PACKAGE_NAME = '@meteorjs/reify';
15+
1416
const DEFAULT_PKG_CONFIG = {
1517
"cache-directory": ".reify-cache",
1618
parser: void 0,
@@ -23,8 +25,8 @@ const reifySemVer = require("./version.js");
2325

2426
function getReifyRange(json, name) {
2527
const entry = json[name];
26-
return utils.isObject(entry) && hasOwn.call(entry, "reify")
27-
? SemVer.validRange(entry.reify)
28+
return utils.isObject(entry) && hasOwn.call(entry, REIFY_PACKAGE_NAME)
29+
? SemVer.validRange(entry[REIFY_PACKAGE_NAME])
2830
: null;
2931
}
3032

@@ -105,7 +107,11 @@ function maxSatisfying(versions, range) {
105107
if (cacheKey in maxSatisfyingCache) {
106108
return maxSatisfyingCache[cacheKey];
107109
}
108-
return maxSatisfyingCache[cacheKey] = SemVer.maxSatisfying(versions, range);
110+
return maxSatisfyingCache[cacheKey] = SemVer.maxSatisfying(
111+
versions,
112+
range,
113+
{ includePrerelease: range === '*' }
114+
);
109115
}
110116

111117
exports.maxSatisfying = maxSatisfying;
@@ -118,7 +124,7 @@ function readPkgInfo(dirPath) {
118124
return null;
119125
}
120126

121-
const reify = pkgJSON.reify;
127+
const reify = pkgJSON[REIFY_PACKAGE_NAME];
122128

123129
if (reify === false) {
124130
// An explicit "reify": false property in package.json disables

0 commit comments

Comments
 (0)