Skip to content

create Babel-based AST parser for CommonJS sources#3162

Merged
boneskull merged 3 commits into
boneskull/ast-servicefrom
boneskull/cjs-ast-parser
Apr 30, 2026
Merged

create Babel-based AST parser for CommonJS sources#3162
boneskull merged 3 commits into
boneskull/ast-servicefrom
boneskull/cjs-ast-parser

Conversation

@boneskull

@boneskull boneskull commented Apr 5, 2026

Copy link
Copy Markdown
Member

feat(compartment-mapper): add Babel-based CJS parser with import() support

Adds parse-cjs-babel.js, a new opt-in CJS parser that uses CjsModuleSource from @endo/module-source instead of the character-level lexer. Consumers swap it in via parserForLanguage:

import { parserForLanguageWithCjsBabel } from '@endo/compartment-mapper/import-parsers.js';

await importLocation(readPowers, entryUrl, {
  parserForLanguage: parserForLanguageWithCjsBabel,
});

Also adds import() support for CJS modules:

  • parse-cjs-shared-export-wrapper.js now provides an importFn that invokes compartment.import(), bridging dynamic imports in CJS onto the SES module loader
  • The CJS functor receives $h͏_import as a parameter when import() calls are detected, matching the ESM evasion pattern

The existing parse-cjs.js (lexer-based) remains the default; nothing breaks for current consumers.

Includes integration tests and test fixtures covering both the new parser and dynamic import() in CJS.

May supersede #3154.

feat(module-source): add CJS Babel AST analysis, transformation, and CjsModuleSource

Replaces the character-level lexer approach to CJS module analysis with Babel AST visitors, enabling @endo/parser-pipeline to share a single parse across CJS analysis, transformation, evasive-transform, and other consumers.

New exports:

  • CjsModuleSource: constructor that parses, analyzes, transforms CJS source and produces a frozen record with imports, exports, reexports, cjsFunctor, and __needsImport__
  • createCjsModuleSourcePasses(): paired analyzer/transform visitor passes for use with @endo/parser-pipeline, including buildRecord

The CJS Babel plugin detects all patterns the lexer handles: require(), exports.*, module.exports, Object.defineProperty, Babel/TS reexport helpers, esbuild hints, spread reexports, and import() calls. The transform rewrites import() to the SES hidden identifier ($h͏_import) for compartment evasion.

Includes 77 new tests: unit tests for both APIs plus a full compat suite ported from @endo/cjs-module-analyzer's test cases.


This is probably a better solution than #3154 for adding import() support to CommonJS. We may want to do both... unsure.

There could be a performance penalty to using the new parser over the lexer, but I'm hoping that leveraging @endo/parser-pipeline (#3158) in LavaMoat will mitigate that.

@boneskull

boneskull commented Apr 5, 2026

Copy link
Copy Markdown
Member Author

Warning

This PR is part of a stack and targets branch boneskull/module-source-pipeline, not master.
DO NOT MERGE until feat(module-source): expose createModuleSourcePasses() #3159 is merged into master.

📚 Pull Request Stack


Managed by gh-stack

@changeset-bot

changeset-bot Bot commented Apr 5, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d74629e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 9 packages
Name Type
@endo/compartment-mapper Minor
@endo/module-source Minor
@endo/parser-pipeline Patch
@endo/bundle-source Patch
@endo/check-bundle Patch
@endo/daemon Patch
@endo/import-bundle Patch
@endo/test262-runner Patch
@endo/cli Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an opt-in Babel-AST-based CommonJS parsing/analysis path (via @endo/module-source) and wires dynamic import() bridging for CJS execution in compartment-mapper, alongside extensive test coverage and fixtures.

Changes:

  • Introduce CjsModuleSource + CJS visitor passes / functor builder in @endo/module-source.
  • Add parse-cjs-babel and parserForLanguageWithCjsBabel opt-in parser in @endo/compartment-mapper.
  • Add integration/compat tests and fixtures for CJS/ESM dynamic import() and CJS analyzer compatibility.

Reviewed changes

Copilot reviewed 20 out of 25 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
packages/module-source/test/cjs-visitor-passes.test.js New tests for CJS Babel visitor passes + transform behavior (incl import() rewrite).
packages/module-source/test/cjs-module-source.test.js New unit tests for CjsModuleSource record shape, freezing, shebang handling, and import() support.
packages/module-source/test/cjs-compat.test.js Large compat suite ported from lexer analyzer tests, targeting CjsModuleSource.
packages/module-source/src/visitor-passes.js Add createCjsModuleSourcePasses() alongside existing ESM passes.
packages/module-source/src/types/visitor-passes.ts Add CJS analysis/passes types and buildRecord() return type.
packages/module-source/src/types/module-source.ts Add CJS record/state types; reorganize/extend source map hook types.
packages/module-source/src/source-options.js Add createCjsSourceOptions() state bag factory for CJS plugin.
packages/module-source/src/cjs-transform-analyze.js New parse/traverse/generate pipeline to produce frozen CjsModuleSourceRecord.
packages/module-source/src/cjs-module-source.js New CjsModuleSource constructor wrapper around the analyzer.
packages/module-source/src/cjs-functor.js Build CJS functor source and assemble frozen CJS module record.
packages/module-source/src/cjs-babel-plugin.js New Babel plugin for CJS analysis (require/exports/reexports/import()) + transform (import() rewrite + hidden-id guard).
packages/module-source/README.md Document CJS variant and tweak XS heading.
packages/module-source/index.js Export CjsModuleSource + createCjsModuleSourcePasses.
packages/compartment-mapper/test/fixtures-dynamic-import-esm/node_modules/app/package.json Add ESM dynamic import fixture package metadata.
packages/compartment-mapper/test/fixtures-dynamic-import-esm/node_modules/app/index.js Fixture: ESM module that uses dynamic import().
packages/compartment-mapper/test/fixtures-dynamic-import-esm/node_modules/app/foo.js Fixture: ESM dependency providing default export.
packages/compartment-mapper/test/fixtures-cjs-compat/node_modules/dynamic-import/package.json Add CJS dynamic import fixture package metadata.
packages/compartment-mapper/test/fixtures-cjs-compat/node_modules/dynamic-import/index.js Fixture: CJS module exporting async fn that calls import().
packages/compartment-mapper/test/dynamic-import-esm.test.js Integration test for ESM dynamic import() via scaffold.
packages/compartment-mapper/test/cjs-compat.test.js Run compat suite with both default lexer parser and new Babel CJS parser; add dynamic-import CJS test.
packages/compartment-mapper/src/types/external.ts Minor import ordering adjustment (SourceMapObject import).
packages/compartment-mapper/src/parse-cjs-shared-export-wrapper.js Add importFn that bridges to compartment.import() for CJS functors.
packages/compartment-mapper/src/parse-cjs-babel.js New opt-in CJS parser using CjsModuleSource and importFn injection when needed.
packages/compartment-mapper/src/import-parsers.js Add parserForLanguageWithCjsBabel helper alongside default parsers.
packages/compartment-mapper/import-parsers.js Re-export parserForLanguageWithCjsBabel from package entrypoint.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/module-source/src/cjs-transform-analyze.js
Comment thread packages/module-source/test/cjs-visitor-passes.test.js Outdated
Comment thread packages/compartment-mapper/src/parse-cjs-babel.js Outdated
Comment thread packages/module-source/src/cjs-babel-plugin.js
Comment thread packages/compartment-mapper/test/cjs-compat.test.js
Comment thread packages/module-source/README.md Outdated
Comment thread packages/module-source/src/types/module-source.ts Outdated
@boneskull boneskull force-pushed the boneskull/module-source-pipeline branch from 93e7e2c to 656bc39 Compare April 7, 2026 22:49
@boneskull boneskull force-pushed the boneskull/cjs-ast-parser branch from f4ccfb3 to 3b30f06 Compare April 7, 2026 22:49
@boneskull boneskull force-pushed the boneskull/module-source-pipeline branch from 656bc39 to 7594c39 Compare April 7, 2026 22:57
@boneskull boneskull force-pushed the boneskull/cjs-ast-parser branch from 3b30f06 to d3334bc Compare April 7, 2026 22:57
@boneskull boneskull force-pushed the boneskull/module-source-pipeline branch from 7594c39 to 84dd4ee Compare April 7, 2026 23:05
@boneskull boneskull force-pushed the boneskull/cjs-ast-parser branch 2 times, most recently from 8265794 to 526cf25 Compare April 7, 2026 23:13
@boneskull boneskull force-pushed the boneskull/module-source-pipeline branch from b1c50eb to 59ff80d Compare April 7, 2026 23:27
@boneskull boneskull force-pushed the boneskull/cjs-ast-parser branch from 60d7132 to 7c3888c Compare April 7, 2026 23:27
@boneskull boneskull force-pushed the boneskull/module-source-pipeline branch from 59ff80d to 9d57d5e Compare April 7, 2026 23:33
@boneskull boneskull force-pushed the boneskull/cjs-ast-parser branch 2 times, most recently from 8a0a4c9 to 9766810 Compare April 8, 2026 19:26
@boneskull boneskull force-pushed the boneskull/module-source-pipeline branch from 9d57d5e to a7fa619 Compare April 14, 2026 02:22
@boneskull boneskull force-pushed the boneskull/cjs-ast-parser branch 3 times, most recently from ee18142 to b77700f Compare April 14, 2026 02:38
@boneskull boneskull changed the base branch from boneskull/module-source-pipeline to boneskull/evasive-pipeline April 14, 2026 02:40
@boneskull boneskull force-pushed the boneskull/evasive-pipeline branch from cbb007c to 6472705 Compare April 14, 2026 03:10
@boneskull boneskull force-pushed the boneskull/cjs-ast-parser branch from b77700f to aae2670 Compare April 14, 2026 03:10
@boneskull boneskull force-pushed the boneskull/cjs-ast-parser branch from 47a33d5 to e77ae52 Compare April 16, 2026 02:13
@boneskull boneskull force-pushed the boneskull/module-source-pipeline branch from 6d15d60 to 5750f33 Compare April 16, 2026 02:15
@boneskull boneskull force-pushed the boneskull/cjs-ast-parser branch from e77ae52 to 2fd4ab2 Compare April 16, 2026 02:15
@boneskull boneskull force-pushed the boneskull/module-source-pipeline branch from 5750f33 to 1fd899f Compare April 16, 2026 02:19
@boneskull boneskull force-pushed the boneskull/cjs-ast-parser branch from 2fd4ab2 to 252b91c Compare April 16, 2026 02:19
@boneskull boneskull force-pushed the boneskull/module-source-pipeline branch from 1fd899f to f50448a Compare April 16, 2026 02:38
@boneskull boneskull force-pushed the boneskull/cjs-ast-parser branch from 252b91c to 145cb97 Compare April 16, 2026 02:39
@boneskull boneskull force-pushed the boneskull/module-source-pipeline branch from f50448a to 65e196f Compare April 16, 2026 20:58
@boneskull boneskull force-pushed the boneskull/cjs-ast-parser branch from 145cb97 to 8d92961 Compare April 16, 2026 20:58
@boneskull boneskull force-pushed the boneskull/module-source-pipeline branch from 65e196f to aefe20a Compare April 21, 2026 22:37
@boneskull boneskull force-pushed the boneskull/cjs-ast-parser branch from 8d92961 to 0c30403 Compare April 21, 2026 22:37
@boneskull boneskull force-pushed the boneskull/module-source-pipeline branch from aefe20a to 3073d30 Compare April 22, 2026 04:14
@boneskull boneskull force-pushed the boneskull/cjs-ast-parser branch from 0c30403 to ae1c9eb Compare April 22, 2026 04:14
@boneskull boneskull force-pushed the boneskull/module-source-pipeline branch from 3073d30 to c130a92 Compare April 23, 2026 01:42
@boneskull boneskull force-pushed the boneskull/cjs-ast-parser branch from ae1c9eb to 564f98d Compare April 23, 2026 01:42
@boneskull boneskull force-pushed the boneskull/module-source-pipeline branch from c130a92 to 2f6e9e5 Compare April 29, 2026 20:54
@boneskull boneskull force-pushed the boneskull/cjs-ast-parser branch from 564f98d to d74629e Compare April 29, 2026 20:54
- Fixes `CompartmentDescriptor` so that it is generic on the `PackagePolicy`; externally-defined `ParseFn`s can now refer to the specific contents of a custom `PackagePolicy` present in a `CompartmentDescriptor`.
- Introduces `ParseSourceMapHook`; differentiated from `@endo/module-source`'s `SourceMapHook`.
- Fixes type of `PolicyItem`; eliminates confusion between `void` (no extra union members) and `any` (`SomePackagePolicy`).
Exposes AST-based parser for CJS, as well as an `analyzeCjs` function from the `analyzer.js` subpath export.

`CjsModuleSource` is also exported from the main entry point.
…port support

Expose Babel-based CJS parser, `parse-cjs-babel`. Expose shared functionality for wrapping CJS functors with `__dirname`, `__filename`, etc. Add support for dynamic `import()` (`parse-cjs-babel` only).
@boneskull boneskull force-pushed the boneskull/module-source-pipeline branch from 2f6e9e5 to eb2e3c0 Compare April 30, 2026 01:37
@boneskull boneskull force-pushed the boneskull/cjs-ast-parser branch from d74629e to edbf51e Compare April 30, 2026 01:37
Base automatically changed from boneskull/module-source-pipeline to boneskull/ast-service April 30, 2026 01:37
@boneskull boneskull merged commit edbf51e into boneskull/ast-service Apr 30, 2026
18 of 25 checks passed
@boneskull boneskull deleted the boneskull/cjs-ast-parser branch April 30, 2026 01:37
@boneskull boneskull restored the boneskull/cjs-ast-parser branch April 30, 2026 01:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request lavamoat

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants