-
Notifications
You must be signed in to change notification settings - Fork 75
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
feat: Preserve formatting #2444
Merged
kriskowal
merged 16 commits into
master
from
kriskowal-babel-preserve-format-integration
Mar 8, 2025
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
9487964
test(module-source): Add format preservation test
kriskowal b97b306
feat(module-source): Preserve format with Babel integration
kriskowal ee78005
feat(evasive-transform): Preserve format with Babel
kriskowal d64015c
refactor(module-source): Converge file naming convention
kriskowal cfd65d1
refactor(module-source): More compact boilerplate
kriskowal 68fb704
fix: Update version management scripts
kriskowal bde16ee
chore: Upgrade Babel
kriskowal 96fe149
refactor: Migrate from @agoric/babel-generator back to @babel/generator
kriskowal 1a8e997
chore: yarn up @babel/generator
kriskowal 87a9ba5
chore(evasive-transform): Update sourcemap test snapshot
kriskowal c660b93
refactor(evasive-transform): Add sourceMap option, remove unmapLoc
kriskowal 7f60adf
chore(evasive-transform): Update golden masters
kriskowal 4fd218b
refactor(evasive-transform): Relieve dependence on Rollup
kriskowal aae5655
feat(bundle-source)!: Replace getExport and nestedEvaluate implementa…
kriskowal c98bd23
refactor(ses): Compensate Hermes transform for Babel upgrade
kriskowal afb5c93
chore: Update yarn.lock
kriskowal File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -121,20 +121,32 @@ not exist. | |
## getExport moduleFormat | ||
|
||
The most primitive `moduleFormat` is the `"getExport"` format. | ||
It generates source like: | ||
It generates a script where the completion value (last expression evaluated) | ||
is a function that accepts an optional `sourceUrlPrefix`. | ||
|
||
```js | ||
function getExport() { | ||
let exports = {}; | ||
const module = { exports }; | ||
// CommonJS source translated from the inputs. | ||
... | ||
return module.exports; | ||
} | ||
cosnt { source } = await bundleSource('program.js', { format: 'getExport' }); | ||
const exports = eval(source)(); | ||
``` | ||
|
||
To evaluate it and obtain the resulting module namespace, you need to endow | ||
a `require` function to resolve external imports. | ||
A bundle in `getExport` format can import host modules through a | ||
lexically-scoped CommonJS `require` function. | ||
One can be endowed using a Hardened JavaScript `Compartment`. | ||
|
||
```js | ||
const compartment = new Compartment({ | ||
globals: { require }, | ||
__options__: true, // until SES and XS implementations converge | ||
}); | ||
const exports = compartment.evaluate(source)(); | ||
``` | ||
|
||
> [!WARNING] | ||
> The `getExport` format was previously implemented using | ||
> [Rollup](https://rollupjs.org/) and is implemented with | ||
> `@endo/compartment-mapper/functor.js` starting with version 4 of | ||
> `@endo/bundle-source`. | ||
> See `nestedEvaluate` below for compatibility caveats. | ||
|
||
## nestedEvaluate moduleFormat | ||
|
||
|
@@ -145,9 +157,70 @@ to evaluate submodules in the same context as the parent function. | |
The advantage of this format is that it helps preserve the filenames within | ||
the bundle in the event of any stack traces. | ||
|
||
Also, the toplevel `getExport(filePrefix = "/bundled-source")` accepts an | ||
optional `filePrefix` argument (which is prepended to relative paths for the | ||
bundled files) in order to help give context to stack traces. | ||
The completion value of a `nestedEvaluate` bundle is a function that accepts | ||
the `sourceUrlPrefix` for every module in the bundle, which will appear in stack | ||
traces and assist debuggers to find a matching source file. | ||
|
||
```js | ||
cosnt { source } = await bundleSource('program.js', { format: 'nestedEvaluate' }); | ||
const compartment = new Compartment({ | ||
globals: { | ||
require, | ||
nestedEvaluate: source => compartment.evaluate(source), | ||
}, | ||
__options__: true, // until SES and XS implementations converge | ||
}); | ||
const exports = compartment.evaluate(source)('bundled-sources/.../'); | ||
``` | ||
|
||
In the absence of a `nextedEvaluate` function in lexical scope, the bundle will | ||
use the `eval` function in lexical scope. | ||
|
||
> [!WARNING] | ||
> The `nestedEvaluate` format was previously implemented using | ||
> [Rollup](https://rollupjs.org/) and is implemented with | ||
> `@endo/compartment-mapper/functor.js` starting with version 4 of | ||
> `@endo/bundle-source`. | ||
> Their behaviors are not identical. | ||
> | ||
> 1. Version 3 used different heuristics than Node.js 18 for inferring whether | ||
> a module was in CommonJS format or ESM format. Version 4 does not guess, | ||
> but relies on the `"type": "module"` directive in `package.json` to indicate | ||
> that a `.js` extension implies ESM format, or respects the explicit `.cjs` | ||
> and `.mjs` extensions. | ||
> 2. Version 3 supports [live | ||
> bindings](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#imported_values_can_only_be_modified_by_the_exporter) | ||
> and Version 4 does not. | ||
> 3. Version 3 can import any package that is discoverable by walking parent directories | ||
> until the dependency or devDependeny is found in a `node_modules` directory. | ||
> Version 4 requires that the dependent package explicitly note the dependency | ||
> in `package.json`. | ||
> 4. Version 3 and 4 generate different text. | ||
> Any treatment of that text that is sensitive to the exact shape of the | ||
> text is fragile and may break even between minor and patch versions. | ||
> 5. Version 4 makes flags already supported by `endoZipBase64` format | ||
> universal to all formats, including `dev`, `elideComments`, | ||
> `noTransforms`, and `conditions`. | ||
Comment on lines
+179
to
+203
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
|
||
## endoScript moduleFormat | ||
|
||
The `ses` shim uses the `endoScript` format to generate its distribution bundles, | ||
suitable for injecting in a web page with a `<script>` tag. | ||
For this format, extract the `source` from the generated JSON envelope and place | ||
it in a file you embed in a web page, an Agoric | ||
[Core Eval](https://docs.agoric.com/guides/coreeval/) script, or evaluate | ||
anywhere that accepts scripts. | ||
|
||
```js | ||
const { source } = await bundleSource('program.js', { format: 'endoScript' }); | ||
const compartment = new Compartment(); | ||
compartment.evaluate(source); | ||
``` | ||
|
||
Unlike `getExport` and `nestedEvaluate`, the `dev` option to `bundleSource` is | ||
required for any bundle that imports `devDependencies`. | ||
The `endoScript` format does not support importing host modules with CommonJS | ||
`require`. | ||
|
||
## endoZipBase64 moduleFormat | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
packages/bundle-source/demo/dir1/sub/more.js → ...ages/bundle-source/demo/dir1/sub/more.cjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
/* global exports require */ | ||
const things = require('./things.js'); | ||
const things = require('./things.cjs'); | ||
|
||
exports.more = `have more ${things.description}`; |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,5 @@ | ||
import 'fs'; | ||
import * as fs from 'fs'; | ||
import { readFileSync } from 'fs'; | ||
|
||
assert(fs[Symbol.toStringTag] === 'Module'); | ||
assert(typeof readFileSync === 'function'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Text should explicitly document that a lexically-scoped
nestedEvaluate
function is required (for bundles that look for it, e.g. those with submodules) or explain what happens when it is not present.