Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support specific getter forms #25

Merged
merged 5 commits into from
Oct 31, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
93 changes: 73 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,31 +70,35 @@ STRING_LITERAL: A `"` or `'` bounded ECMA-262 string literal.

IDENTIFIER_STRING: ( `"` IDENTIFIER `"` | `'` IDENTIFIER `'` )

COMMENT_SPACE: Any ECMA-262 whitespace, ECMA-262 block comment or ECMA-262 line comment

MODULE_EXPORTS: `module` COMMENT_SPACE `.` COMMENT_SPACE `exports`
MODULE_EXPORTS: `module` `.` `exports`

EXPORTS_IDENTIFIER: MODULE_EXPORTS_IDENTIFIER | `exports`

EXPORTS_DOT_ASSIGN: EXPORTS_IDENTIFIER COMMENT_SPACE `.` COMMENT_SPACE IDENTIFIER COMMENT_SPACE `=`
EXPORTS_DOT_ASSIGN: EXPORTS_IDENTIFIER `.` IDENTIFIER `=`

EXPORTS_LITERAL_COMPUTED_ASSIGN: EXPORTS_IDENTIFIER COMMENT_SPACE `[` COMMENT_SPACE IDENTIFIER_STRING COMMENT_SPACE `]` COMMENT_SPACE `=`
EXPORTS_LITERAL_COMPUTED_ASSIGN: EXPORTS_IDENTIFIER `[` IDENTIFIER_STRING `]` `=`

EXPORTS_LITERAL_PROP: (IDENTIFIER (COMMENT_SPACE `:` COMMENT_SPACE IDENTIFIER)?) | (IDENTIFIER_STRING COMMENT_SPACE `:` COMMENT_SPACE IDENTIFIER)
EXPORTS_LITERAL_PROP: (IDENTIFIER `:` IDENTIFIER)?) | (IDENTIFIER_STRING `:` IDENTIFIER)

EXPORTS_SPREAD: `...` COMMENT_SPACE (IDENTIFIER | REQUIRE)
EXPORTS_SPREAD: `...` (IDENTIFIER | REQUIRE)

EXPORTS_MEMBER: EXPORTS_DOT_ASSIGN | EXPORTS_LITERAL_COMPUTED_ASSIGN

ES_MODULE_DEFINE: `Object` COMMENT_SPACE `.` COMMENT_SPACE `defineProperty COMMENT_SPACE `(` COMMENT_SPACE `__esModule` COMMENT_SPACE `,` COMMENT_SPACE IDENTIFIER_STRING
EXPORTS_DEFINE: `Object` `.` `defineProperty `(` IDENTIFIER_STRING `, {`
(`enumerable: true,`)?
(
`value:` |
`get:` `function`? `()` {` return IDENTIFIER (`.` IDENTIFIER | `[` IDENTIFIER_STRING `]`)? `;`? `}`
)
`})`

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

REQUIRE: `require` COMMENT_SPACE `(` COMMENT_SPACE STRING_LITERAL COMMENT_SPACE `)`
REQUIRE: `require` `(` STRING_LITERAL `)`

EXPORTS_ASSIGN: (`var` | `const` | `let`) IDENTIFIER `=` REQUIRE

MODULE_EXPORTS_ASSIGN: MODULE_EXPORTS COMMENT_SPACE `=` COMMENT_SPACE REQUIRE
MODULE_EXPORTS_ASSIGN: MODULE_EXPORTS `=` REQUIRE

EXPORT_STAR: (`__export` | `__exportStar`) `(` REQUIRE

Expand All @@ -113,9 +117,9 @@ EXPORT_STAR_LIB: `Object.keys(` IDENTIFIER$1 `).forEach(function (` IDENTIFIER$2
`})`
```

* The returned export names are taken to be the combination of:
1. `IDENTIFIER` and `IDENTIFIER_STRING` slots for all `EXPORTS_MEMBER` and `EXPORTS_LITERAL` matches.
2. `__esModule` if there is an `ES_MODULE_DEFINE` match.
Spacing between tokens is taken to be any ECMA-262 whitespace, ECMA-262 block comment or ECMA-262 line comment.

* The returned export names are taken to be the combination of the `IDENTIFIER` and `IDENTIFIER_STRING` slots for all `EXPORTS_MEMBER`, `EXPORTS_LITERAL` and `EXPORTS_DEFINE` matches.
* The reexport specifiers are taken to be the the combination of:
1. The `REQUIRE` matches of the last matched of either `MODULE_EXPORTS_ASSIGN` or `EXPORTS_LITERAL`.
2. All _top-level_ `EXPORT_STAR` `REQUIRE` matches and `EXPORTS_ASSIGN` matches whose `IDENTIFIER` also matches the first `IDENTIFIER` in `EXPORT_STAR_LIB`.
Expand Down Expand Up @@ -156,17 +160,66 @@ It will in turn underclassify in cases where the identifiers are renamed:
})(exports);
```

#### __esModule Detection

In addition, `__esModule` is detected as an export when set by `Object.defineProperty`:
`Object.defineProperty` is detected for specifically value and getter forms returning an identifier or member expression:

```js
// DETECTS: __esModule
Object.defineProperty(exports, 'a', { value: 'a' });
// DETECTS: a, b, c, d, __esModule
Object.defineProperty(exports, 'a', {
enumerable: true,
get: function () {
return q.p;
}
});
Object.defineProperty(exports, 'b', {
enumerable: true,
get: function () {
return q['p'];
}
});
Object.defineProperty(exports, 'c', {
enumerable: true,
get () {
return b;
}
});
Object.defineProperty(exports, 'd', { value: 'd' });
Object.defineProperty(exports, '__esModule', { value: true });
```

No other named exports are detected for `defineProperty` calls in order not to trigger getters or non-enumerable properties unnecessarily.
Alternative object definition structures or getter function bodies are not detected:

```js
// DETECTS: NO EXPORTS
Object.defineProperty(exports, 'a', {
enumerable: false,
get () {
return p;
}
});
Object.defineProperty(exports, 'b', {
configurable: true,
get () {
return p;
}
});
Object.defineProperty(exports, 'c', {
get: () => p
});
Object.defineProperty(exports, 'd', {
enumerable: true,
get: function () {
return dynamic();
}
});
Object.defineProperty(exports, 'e', {
enumerable: true,
get () {
return 'str';
}
});
```

`Object.defineProperties` is also not supported.

#### Exports Object Assignment

Expand Down
122 changes: 103 additions & 19 deletions lexer.js
Original file line number Diff line number Diff line change
Expand Up @@ -260,30 +260,114 @@ function tryParseObjectDefineOrKeys (keys) {
pos++;
ch = commentWhitespace();
if (ch === 100/*d*/ && source.startsWith('efineProperty', pos + 1)) {
pos += 14;
revertPos = pos - 1;
ch = commentWhitespace();
if (ch !== 40/*(*/) {
pos = revertPos;
return;
}
pos++;
ch = commentWhitespace();
if (readExportsOrModuleDotExports(ch)) {
while (true) {
pos += 14;
revertPos = pos - 1;
ch = commentWhitespace();
if (ch !== 40/*(*/) break;
pos++;
ch = commentWhitespace();
if (!readExportsOrModuleDotExports(ch)) break;
ch = commentWhitespace();
if (ch !== 44/*,*/) break;
pos++;
ch = commentWhitespace();
if (ch !== 39/*'*/ && ch !== 34/*"*/) break;
let quot = ch;
const exportPos = ++pos;
if (!identifier() || source.charCodeAt(pos) !== quot) break;
const expt = source.slice(exportPos, pos);
pos++;
ch = commentWhitespace();
if (ch !== 44/*,*/) break;
pos++;
ch = commentWhitespace();
if (ch !== 123/*{*/) break;
pos++;
ch = commentWhitespace();
if (ch === 44/*,*/) {
if (ch === 101/*e*/) {
if (!source.startsWith('numerable', pos + 1)) break;
pos += 10;
ch = commentWhitespace();
if (ch !== 58/*:*/) break;
pos++;
ch = commentWhitespace();
if (ch === 39/*'*/ || ch === 34/*"*/) {
const exportPos = ++pos;
if (identifier() && source.charCodeAt(pos) === ch) {
// revert for "("
const expt = source.slice(exportPos, pos);
if (expt === '__esModule')
addExport(expt);
}
if (ch !== 116/*t*/ || !source.startsWith('rue', pos + 1)) break;
pos += 4;
ch = commentWhitespace();
if (ch !== 44) break;
pos++;
ch = commentWhitespace();
}
if (ch === 118/*v*/) {
if (!source.startsWith('alue', pos + 1)) break;
pos += 5;
ch = commentWhitespace();
if (ch !== 58/*:*/) break;
pos++;
addExport(expt);
break;
}
else if (ch === 103/*g*/) {
if (!source.startsWith('et', pos + 1)) break;
pos += 3;
ch = commentWhitespace();
if (ch === 58/*:*/) {
pos++;
ch = commentWhitespace();
if (ch !== 102/*f*/) break;
if (!source.startsWith('unction', pos + 1)) break;
pos += 8;
ch = commentWhitespace();
}
if (ch !== 40/*(*/) break;
pos++;
ch = commentWhitespace();
if (ch !== 41/*)*/) break;
pos++;
ch = commentWhitespace();
if (ch !== 123/*{*/) break;
pos++;
ch = commentWhitespace();
if (ch !== 114/*r*/) break;
if (!source.startsWith('eturn', pos + 1)) break;
pos += 6;
ch = commentWhitespace();
if (!identifier()) break;
ch = commentWhitespace();
if (ch === 46/*.*/) {
pos++;
commentWhitespace();
if (!identifier()) break;
ch = commentWhitespace();
}
else if (ch === 91/*[*/) {
pos++;
ch = commentWhitespace();
if (ch === 39/*'*/) singleQuoteString();
else if (ch === 34/*"*/) doubleQuoteString();
else break;
pos++;
ch = commentWhitespace();
if (ch !== 93/*]*/) break;
pos++;
ch = commentWhitespace();
}
if (ch === 59/*;*/) {
pos++;
ch = commentWhitespace();
}
if (ch !== 125/*}*/) break;
pos++;
ch = commentWhitespace();
if (ch !== 125/*}*/) break;
pos++;
ch = commentWhitespace();
if (ch !== 41/*)*/) break;
addExport(expt);
return;
}
break;
}
}
else if (keys && ch === 107/*k*/ && source.startsWith('eys', pos + 1)) {
Expand Down