A JS module syntax lexer used in [es-module-shims](https://github.com/guybedford/es-module-shims).
Outputs the list of exports and locations of import specifiers, including dynamic import and import meta handling.
A very small single JS file (4KiB gzipped) that includes inlined Web Assembly for very fast source analysis of ECMAScript module syntax only.
For an example of the performance, Angular 1 (720KiB) is fully parsed in 5ms, in comparison to the fastest JS parser, Acorn which takes over 100ms.
_Comprehensively handles the JS language grammar while remaining small and fast. - ~10ms per MB of JS cold and ~5ms per MB of JS warm, [see benchmarks](#benchmarks) for more info._
// - a is currently -1 even if there is an assertion
// - e is currently the character before the closing )
// For nested dynamic imports, the se value of the outer import is -1 as end tracking does not
// currently support nested dynamic immports
// import.meta is indicated by imports[3].d === -2
// Returns true
imports[4].d===-2;
// Returns "import /*comment!*/.meta"
source.slice(imports[4].s,imports[4].e);
// ss and se are the same for import meta
})();
```
### CSP asm.js Build
The default version of the library uses Wasm and (safe) eval usage for performance and a minimal footprint.
Neither of these represent security escalation possibilities since there are no execution string injection vectors, but that can still violate existing CSP policies for applications.
For a version that works with CSP eval disabled, use the `es-module-lexer/js` build:
```js
import{parse}from'es-module-lexer/js';
```
Instead of Web Assembly, this uses an asm.js build which is almost as fast as the Wasm version ([see benchmarks below](#benchmarks)).
### Escape Sequences
To handle escape sequences in specifier strings, the `.n` field of imported specifiers will be provided where possible.
For dynamic import expressions, this field will be empty if not a valid JS string.
### Facade Detection
Facade modules that only use import / export syntax can be detected via the third return value:
```js
const[,,facade]=parse(`
export * from 'external';
import * as ns from 'external2';
export { a as b } from 'external3';
export { ns };
`);
facade===true;
```
### ESM Detection
Modules that uses ESM syntaxes can be detected via the fourth return value:
```js
const[,,,hasModuleSyntax]=parse(`
export {}
`);
hasModuleSyntax===true;
```
Dynamic imports are ignored since they can be used in Non-ESM files.
```js
const[,,,hasModuleSyntax]=parse(`
import('./foo.js')
`);
hasModuleSyntax===false;
```
### Environment Support
Node.js 10+, and [all browsers with Web Assembly support](https://caniuse.com/#feat=wasm).
### Grammar Support
* Token state parses all line comments, block comments, strings, template strings, blocks, parens and punctuators.
* Division operator / regex token ambiguity is handled via backtracking checks against punctuator prefixes, including closing brace or paren backtracking.
* Always correctly parses valid JS source, but may parse invalid JS source without errors.
### Limitations
The lexing approach is designed to deal with the full language grammar including RegEx / division operator ambiguity through backtracking and paren / brace tracking.
The only limitation to the reduced parser is that the "exports" list may not correctly gather all export identifiers in the following edge cases:
```js
// Only "a" is detected as an export, "q" isn't
exportvara='asdf',q=z;
// "b" is not detected as an export
exportvar{a:b}=asdf;
```
The above cases are handled gracefully in that the lexer will keep going fine, it will just not properly detect the export names above.
### Benchmarks
Benchmarks can be run with `npm run bench`.
Current results for a high spec machine:
#### Wasm Build
```
Module load time
> 5ms
Cold Run, All Samples
test/samples/*.js (3123 KiB)
> 18ms
Warm Runs (average of 25 runs)
test/samples/angular.js (739 KiB)
> 3ms
test/samples/angular.min.js (188 KiB)
> 1ms
test/samples/d3.js (508 KiB)
> 3ms
test/samples/d3.min.js (274 KiB)
> 2ms
test/samples/magic-string.js (35 KiB)
> 0ms
test/samples/magic-string.min.js (20 KiB)
> 0ms
test/samples/rollup.js (929 KiB)
> 4.32ms
test/samples/rollup.min.js (429 KiB)
> 2.16ms
Warm Runs, All Samples (average of 25 runs)
test/samples/*.js (3123 KiB)
> 14.16ms
```
#### JS Build (asm.js)
```
Module load time
> 2ms
Cold Run, All Samples
test/samples/*.js (3123 KiB)
> 34ms
Warm Runs (average of 25 runs)
test/samples/angular.js (739 KiB)
> 3ms
test/samples/angular.min.js (188 KiB)
> 1ms
test/samples/d3.js (508 KiB)
> 3ms
test/samples/d3.min.js (274 KiB)
> 2ms
test/samples/magic-string.js (35 KiB)
> 0ms
test/samples/magic-string.min.js (20 KiB)
> 0ms
test/samples/rollup.js (929 KiB)
> 5ms
test/samples/rollup.min.js (429 KiB)
> 3.04ms
Warm Runs, All Samples (average of 25 runs)
test/samples/*.js (3123 KiB)
> 17.12ms
```
### Building
This project uses [Chomp](https://chompbuild.com) for building.
With Chomp installed, download the WASI SDK 12.0 from https://github.com/WebAssembly/wasi-sdk/releases/tag/wasi-sdk-12.