Skip to content

Commit 89cde88

Browse files
authored
do not emit Object.defineProperty exports (#24)
1 parent 8767c95 commit 89cde88

File tree

4 files changed

+27
-14
lines changed

4 files changed

+27
-14
lines changed

README.md

+20-8
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ EXPORTS_SPREAD: `...` COMMENT_SPACE (IDENTIFIER | REQUIRE)
8686

8787
EXPORTS_MEMBER: EXPORTS_DOT_ASSIGN | EXPORTS_LITERAL_COMPUTED_ASSIGN
8888

89-
EXPORTS_DEFINE: `Object` COMMENT_SPACE `.` COMMENT_SPACE `defineProperty COMMENT_SPACE `(` EXPORTS_IDENTIFIER COMMENT_SPACE `,` COMMENT_SPACE IDENTIFIER_STRING
89+
ES_MODULE_DEFINE: `Object` COMMENT_SPACE `.` COMMENT_SPACE `defineProperty COMMENT_SPACE `(` COMMENT_SPACE `__esModule` COMMENT_SPACE `,` COMMENT_SPACE IDENTIFIER_STRING
9090

9191
EXPORTS_LITERAL: MODULE_EXPORTS COMMENT_SPACE `=` COMMENT_SPACE `{` COMMENT_SPACE (EXPORTS_LITERAL_PROP | EXPORTS_SPREAD) COMMENT_SPACE `,` COMMENT_SPACE)+ `}`
9292

@@ -113,7 +113,9 @@ EXPORT_STAR_LIB: `Object.keys(` IDENTIFIER$1 `).forEach(function (` IDENTIFIER$2
113113
`})`
114114
```
115115

116-
* The returned export names are the matched `IDENTIFIER` and `IDENTIFIER_STRING` slots for all `EXPORTS_MEMBER`, `EXPORTS_DEFINE` and `EXPORTS_LITERAL` matches.
116+
* The returned export names are taken to be the combination of:
117+
1. `IDENTIFIER` and `IDENTIFIER_STRING` slots for all `EXPORTS_MEMBER` and `EXPORTS_LITERAL` matches.
118+
2. `__esModule` if there is an `ES_MODULE_DEFINE` match.
117119
* The reexport specifiers are taken to be the the combination of:
118120
1. The `REQUIRE` matches of the last matched of either `MODULE_EXPORTS_ASSIGN` or `EXPORTS_LITERAL`.
119121
2. All _top-level_ `EXPORT_STAR` `REQUIRE` matches and `EXPORTS_ASSIGN` matches whose `IDENTIFIER` also matches the first `IDENTIFIER` in `EXPORT_STAR_LIB`.
@@ -125,11 +127,10 @@ EXPORT_STAR_LIB: `Object.keys(` IDENTIFIER$1 `).forEach(function (` IDENTIFIER$2
125127
The basic matching rules for named exports are `exports.name`, `exports['name']` or `Object.defineProperty(exports, 'name', ...)`. This matching is done without scope analysis and regardless of the expression position:
126128

127129
```js
128-
// DETECTS EXPORTS: a, b, c
130+
// DETECTS EXPORTS: a, b
129131
(function (exports) {
130132
exports.a = 'a';
131133
exports['b'] = 'b';
132-
Object.defineProperty(exports, 'c', { value: 'c' });
133134
})(exports);
134135
```
135136

@@ -141,21 +142,32 @@ Because there is no scope analysis, the above detection may overclassify:
141142
exports.a = 'a';
142143
exports['b'] = 'b';
143144
if (false)
144-
Object.defineProperty(exports, 'c', { value: 'c' });
145+
exports.c = 'c';
145146
})(NOT_EXPORTS, NOT_OBJECT);
146147
```
147148

148149
It will in turn underclassify in cases where the identifiers are renamed:
149150

150151
```js
151152
// DETECTS: NO EXPORTS
152-
(function (e, defineProperty) {
153+
(function (e) {
153154
e.a = 'a';
154155
e['b'] = 'b';
155-
defineProperty(e, 'c', { value: 'c' });
156-
})(exports, defineProperty);
156+
})(exports);
157+
```
158+
159+
#### __esModule Detection
160+
161+
In addition, `__esModule` is detected as an export when set by `Object.defineProperty`:
162+
163+
```js
164+
// DETECTS: __esModule
165+
Object.defineProperty(exports, 'a', { value: 'a' });
166+
Object.defineProperty(exports, '__esModule', { value: true });
157167
```
158168

169+
No other named exports are detected for `defineProperty` calls in order not to trigger getters or non-enumerable properties unnecessarily.
170+
159171
#### Exports Object Assignment
160172

161173
A best-effort is made to detect `module.exports` object assignments, but because this is not a full parser, arbitrary expressions are not handled in the

lexer.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,9 @@ function tryParseObjectDefineOrKeys (keys) {
278278
const exportPos = ++pos;
279279
if (identifier() && source.charCodeAt(pos) === ch) {
280280
// revert for "("
281-
addExport(source.slice(exportPos, pos));
281+
const expt = source.slice(exportPos, pos);
282+
if (expt === '__esModule')
283+
addExport(expt);
282284
}
283285
}
284286
}

src/lexer.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,8 @@ void tryParseObjectDefineOrKeys (bool keys) {
290290
uint16_t* exportPos = ++pos;
291291
if (identifier(*pos) && *pos == ch) {
292292
// revert for "("
293-
addExport(exportPos, pos);
293+
if (pos - exportPos == 10 && str_eq10(exportPos, '_', '_', 'e', 's', 'M', 'o', 'd', 'u', 'l', 'e'))
294+
addExport(exportPos, pos);
294295
}
295296
}
296297
}

test/_unit.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -453,10 +453,8 @@ suite('Lexer', () => {
453453
Object.defineProperty(module.exports, 'thing', { value: true });
454454
Object.defineProperty(exports, "__esModule", { value: true });
455455
`);
456-
assert.equal(exports.length, 3);
457-
assert.equal(exports[0], 'namedExport');
458-
assert.equal(exports[1], 'thing');
459-
assert.equal(exports[2], '__esModule');
456+
assert.equal(exports.length, 1);
457+
assert.equal(exports[0], '__esModule');
460458
});
461459

462460
test('module assign', () => {

0 commit comments

Comments
 (0)