Skip to content

Commit

Permalink
only include last reexport (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
guybedford authored Oct 4, 2020
1 parent 9a551db commit a15d309
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 42 deletions.
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

0 comments on commit a15d309

Please sign in to comment.