Skip to content

Only include last reexport assignment #11

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

Merged
merged 1 commit into from
Oct 4, 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ EXPORT_STAR_LIB: `Object.keys(` IDENTIFIER$1 `).forEach(function (` IDENTIFIER$2
```

* The returned export names are the matched `IDENTIFIER` and `IDENTIFIER_STRING` slots for all `EXPORTS_MEMBER`, `EXPORTS_DEFINE` and `EXPORTS_LITERAL` matches.
* The reexport specifiers are taken to be the `STRING_LITERAL` slots of all `MODULE_EXPORTS_ASSIGN` as well as all _top-level_ `EXPORT_STAR` `REQUIRE` matches and `EXPORTS_ASSIGN` matches whose `IDENTIFIER` also matches the first `IDENTIFIER` in `EXPORT_STAR_LIB`.
* The reexport specifiers are taken to be the `STRING_LITERAL` slot of the last `MODULE_EXPORTS_ASSIGN` as well as all _top-level_ `EXPORT_STAR` `REQUIRE` matches and `EXPORTS_ASSIGN` matches whose `IDENTIFIER` also matches the first `IDENTIFIER` in `EXPORT_STAR_LIB`.

### Parsing Examples

Expand Down Expand Up @@ -180,16 +180,16 @@ module.exports = {

#### module.exports reexport assignment

Any `module.exports = require('mod')` assignment is detected as a reexport:
Any `module.exports = require('mod')` assignment is detected as a reexport, but only the last one is returned:

```js
// DETECTS REEXPORTS: a, b, c
// DETECTS REEXPORTS: c
module.exports = require('a');
(module => module.exports = require('b'))(NOT_MODULE);
if (false) module.exports = require('c');
```

As a result, the total list of exports would be inferred as the union of all of these reexported modules, which can lead to possible over-classification.
This is to avoid overclassification in Webpack bundles with externals which include `module.exports = require('external')` in their source for every external dependency.

#### Transpiler Re-exports

Expand Down
8 changes: 7 additions & 1 deletion include-wasm/cjs-module-lexer.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,14 @@ void (*addExport)(const uint16_t*, const uint16_t*) = &_addExport;
void (*addReexport)(const uint16_t*, const uint16_t*) = &_addReexport;
bool parseCJS (uint16_t* source, uint32_t sourceLen, void (*addExport)(const uint16_t* start, const uint16_t* end), void (*addReexport)(const uint16_t* start, const uint16_t* end));

enum RequireType {
Import,
ExportAssign,
ExportStar
};

void tryBacktrackAddStarExportBinding (uint16_t* pos);
bool tryParseRequire (bool directStarExport);
bool tryParseRequire (enum RequireType requireType);
void tryParseLiteralExports ();
bool readExportsOrModuleDotExports (uint16_t ch);
void tryParseModuleExportsDotAssign ();
Expand Down
8 changes: 7 additions & 1 deletion include/cjs-module-lexer.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,14 @@ void bail (uint32_t err);

bool parseCJS (uint16_t* source, uint32_t sourceLen, void (*addExport)(const uint16_t*, const uint16_t*), void (*addReexport)(const uint16_t*, const uint16_t*));

enum RequireType {
Import,
ExportAssign,
ExportStar
};

void tryBacktrackAddStarExportBinding (uint16_t* pos);
bool tryParseRequire (bool directStarExport);
bool tryParseRequire (enum RequireType requireType);
void tryParseLiteralExports ();
bool readExportsOrModuleDotExports (uint16_t ch);
void tryParseModuleExportsDotAssign ();
Expand Down
49 changes: 33 additions & 16 deletions lexer.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ let openTokenDepth,
nextBraceIsClass,
starExportMap,
lastStarExportSpecifier,
lastExportsAssignSpecifier,
_exports,
reexports;

Expand All @@ -25,11 +26,17 @@ function resetState () {
nextBraceIsClass = false;
starExportMap = Object.create(null);
lastStarExportSpecifier = null;
lastExportsAssignSpecifier = null;

_exports = new Set();
reexports = new Set();
}

// RequireType
const Import = 0;
const ExportAssign = 1;
const ExportStar = 2;

const strictReserved = new Set(['implements', 'interface', 'let', 'package', 'private', 'protected', 'public', 'static', 'yield', 'enum']);

module.exports = function parseCJS (source, name = '@') {
Expand All @@ -42,6 +49,8 @@ module.exports = function parseCJS (source, name = '@') {
e.loc = pos;
throw e;
}
if (lastExportsAssignSpecifier)
reexports.add(lastExportsAssignSpecifier);
const result = { exports: [..._exports], reexports: [...reexports] };
resetState();
return result;
Expand Down Expand Up @@ -85,7 +94,7 @@ function parseSource (cjsSource) {
continue;
case 114/*r*/:
const startPos = pos;
if (tryParseRequire(false) && keywordStart(startPos))
if (tryParseRequire(Import) && keywordStart(startPos))
tryBacktrackAddStarExportBinding(startPos - 1);
lastTokenPos = pos;
continue;
Expand All @@ -97,7 +106,7 @@ function parseSource (cjsSource) {
if (source.charCodeAt(pos) === 40/*(*/) {
openTokenPosStack[openTokenDepth++] = lastTokenPos;
if (source.charCodeAt(++pos) === 114/*r*/)
tryParseRequire(true);
tryParseRequire(ExportStar);
}
}
lastTokenPos = pos;
Expand Down Expand Up @@ -623,14 +632,14 @@ function tryParseExportsDotAssign (assign) {

// require('...')
if (ch === 114/*r*/)
tryParseRequire(true);
tryParseRequire(ExportAssign);
}
}
}
pos = revertPos;
}

function tryParseRequire (directStarExport) {
function tryParseRequire (requireType) {
// require('...')
if (source.startsWith('equire', pos + 1)) {
pos += 7;
Expand All @@ -645,27 +654,35 @@ function tryParseRequire (directStarExport) {
const reexportEnd = pos++;
ch = commentWhitespace();
if (ch === 41/*)*/) {
if (directStarExport) {
reexports.add(source.slice(reexportStart, reexportEnd));
}
else {
lastStarExportSpecifier = source.slice(reexportStart, reexportEnd);
switch (requireType) {
case ExportAssign:
lastExportsAssignSpecifier = source.slice(reexportStart, reexportEnd);
return true;
case ExportStar:
reexports.add(source.slice(reexportStart, reexportEnd));
return true;
default:
lastStarExportSpecifier = source.slice(reexportStart, reexportEnd);
return true;
}
return true;
}
}
else if (ch === 34/*"*/) {
doubleQuoteString();
const reexportEnd = pos++;
ch = commentWhitespace();
if (ch === 41/*)*/) {
if (directStarExport) {
reexports.add(source.slice(reexportStart, reexportEnd));
}
else {
lastStarExportSpecifier = source.slice(reexportStart, reexportEnd);
switch (requireType) {
case ExportAssign:
lastExportsAssignSpecifier = source.slice(reexportStart, reexportEnd);
return true;
case ExportStar:
reexports.add(source.slice(reexportStart, reexportEnd));
return true;
default:
lastStarExportSpecifier = source.slice(reexportStart, reexportEnd);
return true;
}
return true;
}
}
}
Expand Down
52 changes: 35 additions & 17 deletions src/lexer.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ uint16_t* end;
uint16_t* templateStack;
uint16_t** openTokenPosStack;
StarExportBinding* starExportStack;
uint16_t* reexportAssignStart;
uint16_t* reexportAssignEnd;
bool nextBraceIsClass;

uint16_t* lastReexportStart;
Expand Down Expand Up @@ -62,6 +64,8 @@ bool parseCJS (uint16_t* _source, uint32_t _sourceLen, void (*_addExport)(const
openTokenPosStack = &openTokenPosStack_[0];
starExportStack = &starExportStack_[0];
nextBraceIsClass = false;
reexportAssignStart = NULL;
reexportAssignEnd = NULL;

pos = (uint16_t*)(source - 1);
uint16_t ch = '\0';
Expand Down Expand Up @@ -94,7 +98,7 @@ bool parseCJS (uint16_t* _source, uint32_t _sourceLen, void (*_addExport)(const
continue;
case 'r': {
uint16_t* startPos = pos;
if (tryParseRequire(false) && keywordStart(startPos))
if (tryParseRequire(Import) && keywordStart(startPos))
tryBacktrackAddStarExportBinding(startPos - 1);
lastTokenPos = pos;
continue;
Expand All @@ -107,7 +111,7 @@ bool parseCJS (uint16_t* _source, uint32_t _sourceLen, void (*_addExport)(const
if (*pos == '(') {
openTokenPosStack[openTokenDepth++] = lastTokenPos;
if (*(++pos) == 'r')
tryParseRequire(true);
tryParseRequire(ExportStar);
}
}
lastTokenPos = pos;
Expand Down Expand Up @@ -217,6 +221,9 @@ bool parseCJS (uint16_t* _source, uint32_t _sourceLen, void (*_addExport)(const
if (templateDepth != UINT16_MAX || openTokenDepth || has_error)
return false;

if (reexportAssignStart)
addReexport(reexportAssignStart, reexportAssignEnd);

// success
return true;
}
Expand Down Expand Up @@ -647,14 +654,14 @@ void tryParseExportsDotAssign (bool assign) {

// require('...')
if (ch == 'r')
tryParseRequire(true);
tryParseRequire(ExportAssign);
}
}
}
pos = revertPos;
}

bool tryParseRequire (bool directStarExport) {
bool tryParseRequire (enum RequireType requireType) {
// require('...')
if (str_eq6(pos + 1, 'e', 'q', 'u', 'i', 'r', 'e')) {
pos += 7;
Expand All @@ -669,27 +676,38 @@ bool tryParseRequire (bool directStarExport) {
uint16_t* reexportEnd = pos++;
ch = commentWhitespace();
if (ch == ')') {
if (directStarExport) {
addReexport(reexportStart, reexportEnd);
}
else {
starExportStack->specifier_start = reexportStart;
starExportStack->specifier_end = reexportEnd;
switch (requireType) {
case ExportStar:
addReexport(reexportStart, reexportEnd);
return true;
case ExportAssign:
reexportAssignStart = reexportStart;
reexportAssignEnd = reexportEnd;
return true;
default:
starExportStack->specifier_start = reexportStart;
starExportStack->specifier_end = reexportEnd;
return true;
}
return true;
}
}
else if (ch == '"') {
doubleQuoteString();
uint16_t* reexportEnd = pos++;
ch = commentWhitespace();
if (ch == ')') {
if (directStarExport) {
addReexport(reexportStart, reexportEnd);
}
else {
starExportStack->specifier_start = reexportStart;
starExportStack->specifier_end = reexportEnd;
switch (requireType) {
case ExportStar:
addReexport(reexportStart, reexportEnd);
return true;
case ExportAssign:
reexportAssignStart = reexportStart;
reexportAssignEnd = reexportEnd;
return true;
default:
starExportStack->specifier_start = reexportStart;
starExportStack->specifier_end = reexportEnd;
return true;
}
return true;
}
Expand Down
5 changes: 2 additions & 3 deletions test/_unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -423,9 +423,8 @@ suite('Lexer', () => {
`);
assert.equal(exports.length, 1);
assert.equal(exports[0], 'asdf');
assert.equal(reexports.length, 2);
assert.equal(reexports[0], './asdf');
assert.equal(reexports[1], './another');
assert.equal(reexports.length, 1);
assert.equal(reexports[0], './another');
});

test('Single parse cases', () => {
Expand Down