Skip to content

Commit

Permalink
Support specific getter forms (#25)
Browse files Browse the repository at this point in the history
  • Loading branch information
guybedford committed Oct 31, 2020
1 parent 638f6d6 commit 4c2b81c
Show file tree
Hide file tree
Showing 4 changed files with 361 additions and 61 deletions.
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
Loading

0 comments on commit 4c2b81c

Please sign in to comment.