Skip to content

3.0.0

Choose a tag to compare

@guybedford guybedford released this 07 Sep 05:54
· 6 commits to main since this release

Breaking

  • Full build records are tagged unions discriminated by type (static | reexport-star | dynamic | import-meta; direct | reexport | reexport-all) with descriptive fields (specifier, importStart/importEnd, attributes, phase, name, localName, from, importIndex, exportStart) replacing the terse v2 names and numeric type tags. (#233)
  • es-module-lexer/minimal is a new entry point holding the v2-shaped API: parse returns [imports, exports] only; at is dropped (read attributes via source.slice(a, se - 1)); export { a as b } from 'c' reports the imported name in ln/ls/le. (#211, #240)
  • ImportType is a type-only union of named numeric literal aliases (StaticImportType = 1, DynamicImportType = 2, …); no runtime enum is exported. initSync is no longer exported — parse initializes on first use. (#240)
  • es-module-lexer/js is the asm.js build; the unmaintained pure-JS port is removed. (#204)
  • Invalid escape sequences in static specifiers throw a parse error instead of yielding undefined. (#238)
  • Node.js 18+ is required. (#232)

Features

  • TypeScript: type-only imports, exports, aliases, interfaces and ambient declarations are lexed as accepted by Node.js type stripping; type-only edges carry typeOnly, dynamic imports carry a best-effort probablyTypeOnly, and erased declaration bodies never leak nested import() types. Full builds only. (#220, #241)
  • Export origin analysis: reexports resolve to their originating import (importName, from, importIndex) regardless of statement order; export * from appears in exports as reexport-all and its import as reexport-star. (#229)
  • Template globs: a dynamic import whose argument is a single template literal reports a glob specifier with each ${…} collapsed to * (./locales/*.js), disambiguated by the parser so regex/division inside substitutions is handled. (#205)
  • Dynamic import attributes is typed null; the options argument is located by attributesStart. (#240)
  • parse initializes the Wasm synchronously on first call; await init is only required on browser main threads, and all four entry points export the same parse / init API. (#237, #240)

Performance

  • SIMD scanning for long line comments and templates in the full Wasm build (the minimal build stays Wasm MVP). (#231, #235)
  • Identifier and number runs are skipped in bulk: ~15% faster sweeps across Wasm and asm.js. (#227)
  • Module strings decode without eval: one-import parse 354 → 238 ns, and the Wasm builds now work under --disallow-code-generation-from-strings. (#239)
  • Short sources (< 64 chars) copy via the typed-array loop rather than Buffer. (#242)
  • Strict specifier decoding avoids V8's sloppy/strict cache mismatch. (#220)

Fixes

  • Bounds checks on the open-token and dynamic-import stacks; nesting past 1024 brackets or 512 dynamic imports is a parse error rather than memory corruption. (#243)
  • Unannotated declare let/var and shorthand declare module 'm' no longer erase the following statement; export default interface Foo {} reports Foo as its local name. (#241)
  • Typed export binding lists collect every declarator; export { type as } / type as as follow TypeScript's specifier table; U+00A0 is whitespace around import/export names. (#220)
  • Minimal build enforces Wasm MVP (no post-MVP features) and is tested in Firefox 67. (#234)

Docs & tooling

  • README restructured: package exports table with Brotli footprint and cold/warm parse speed per MB, environment/CSP/usage up front, minimal-build "Upgrading from v2" section, WASI build steps replaced by the emsdk checkouts the build installs itself. (#240)
  • chomp bench runs all four builds in one process and prints the README table; generated types/ are tracked in the repo. (#240)
  • TypeScript differential fuzzer against stripTypeScriptTypes in CI; no-eval lanes for minimal and CommonJS. (#220, #239)