@endo/static-module-record
Advanced tools
Comparing version 0.7.20 to 0.8.1
{ | ||
"name": "@endo/static-module-record", | ||
"version": "0.7.20", | ||
"version": "0.8.1", | ||
"description": "Shim for the SES StaticModuleRecord and module-to-program transformer", | ||
@@ -22,3 +22,2 @@ "keywords": [ | ||
"type": "module", | ||
"types": "./types.d.ts", | ||
"main": "./index.js", | ||
@@ -45,6 +44,6 @@ "exports": { | ||
"@babel/types": "^7.17.0", | ||
"ses": "^0.18.5" | ||
"ses": "^0.18.7" | ||
}, | ||
"devDependencies": { | ||
"@endo/ses-ava": "^0.2.41", | ||
"@endo/ses-ava": "^0.2.43", | ||
"ava": "^5.3.0", | ||
@@ -54,3 +53,3 @@ "babel-eslint": "^10.0.3", | ||
"c8": "^7.14.0", | ||
"eslint": "^8.44.0", | ||
"eslint": "^8.46.0", | ||
"eslint-config-airbnb-base": "^15.0.0", | ||
@@ -84,3 +83,3 @@ "eslint-config-prettier": "^8.8.0", | ||
}, | ||
"gitHead": "106da55b8bcea3067f70c29c357806f3f2e55c52" | ||
"gitHead": "b38361616f968415291b089dcca75cc4a2672a35" | ||
} |
@@ -26,2 +26,53 @@ # StaticModuleRecord | ||
## Source maps | ||
The `StaticModuleRecord` is a shim for what we hope to eventually call a native | ||
`ModuleSource` constructor. | ||
However, in the absence of a native `ModuleSource`, this produces a | ||
serializable object that emulates the behavior of `ModuleSource` in conjunction | ||
with the `Compartment` constructor from `ses`. | ||
A detail that leaks from the implementation is that the constructor rewrites | ||
the source, from an ESM `[[Module]]` grammar construction to a `[[Program]]` | ||
construction suitable for confining with the compartment's confined evaluator. | ||
This transform attempts to be unobtrusive, but currently causes some alignment | ||
changes due to (hopefully temporary) limitations to the underlying code | ||
generator. | ||
In the interim, generating a source map can help. | ||
The `StaticModuleRecord` constructor accepts non-standards-track | ||
`sourceMapHook` and `sourceMapUrl` options. | ||
Previously, the sole option was a `string` argument for the `sourceUrl`, such | ||
that this would be appended to the generated source. | ||
This change allows for the old or new usage: | ||
```js | ||
new StaticModuleRecord(source, sourceUrl); | ||
// or | ||
new StaticModuleRecord(source, { sourceUrl, sourceMapUrl, sourceMapHook }); | ||
``` | ||
The `sourceMapUrl` is necessary for generating a source map. | ||
The URL will appear only in the generated source map, so a fully qualified | ||
source map URL is safe and allows for continuity if the map is generated and | ||
debugged on the same host. | ||
This is important because Endo captures precompiled Static Module Records in | ||
bundles, excluding source maps, such that a relative path is not useful. | ||
The `sourceMapHook` will receive a string source map and a details bag | ||
including: | ||
- `source` | ||
- `sourceUrl` | ||
- `sourceMapUrl` | ||
Such that the receiver can store the source map somewhere as a side-effect. | ||
Note: the `sourceMapHook` is synchronous and returns `void`. | ||
Exceptions thrown by the hook will propagate up through the constructor. If | ||
the hook returns a promise, it will be dropped and rejections will go uncaught. | ||
If the hook must do async work, these should be queued up as a job that the | ||
caller can later await. | ||
## Bug Disclosure | ||
@@ -28,0 +79,0 @@ |
/** | ||
* @callback SourceMapHook | ||
* @param {string} sourceMap | ||
* @param {string} sourceUrl | ||
* @param {string} source | ||
*/ | ||
/** | ||
* @typedef {object} Options | ||
* @property {string} [sourceUrl] | ||
* @property {string} [sourceMap] | ||
* @property {string} [sourceMapUrl] | ||
* @property {SourceMapHook} [sourceMapHook] | ||
*/ | ||
/** | ||
* StaticModuleRecord captures the effort of parsing and analyzing module text | ||
@@ -7,8 +20,21 @@ * so a cache of StaticModuleRecords may be shared by multiple Compartments. | ||
* @param {string} source | ||
* @param {string} [url] | ||
* @param {string | Options} [opts] | ||
* @returns {import('ses').PrecompiledStaticModuleInterface} | ||
*/ | ||
export function StaticModuleRecord(source: string, url?: string | undefined): import('ses').PrecompiledStaticModuleInterface; | ||
export function StaticModuleRecord(source: string, opts?: string | Options | undefined): import('ses').PrecompiledStaticModuleInterface; | ||
export class StaticModuleRecord { | ||
/** | ||
* @callback SourceMapHook | ||
* @param {string} sourceMap | ||
* @param {string} sourceUrl | ||
* @param {string} source | ||
*/ | ||
/** | ||
* @typedef {object} Options | ||
* @property {string} [sourceUrl] | ||
* @property {string} [sourceMap] | ||
* @property {string} [sourceMapUrl] | ||
* @property {SourceMapHook} [sourceMapHook] | ||
*/ | ||
/** | ||
* StaticModuleRecord captures the effort of parsing and analyzing module text | ||
@@ -19,9 +45,9 @@ * so a cache of StaticModuleRecords may be shared by multiple Compartments. | ||
* @param {string} source | ||
* @param {string} [url] | ||
* @param {string | Options} [opts] | ||
* @returns {import('ses').PrecompiledStaticModuleInterface} | ||
*/ | ||
constructor(source: string, url?: string | undefined); | ||
imports: readonly string[]; | ||
exports: readonly any[]; | ||
reexports: readonly never[]; | ||
constructor(source: string, opts?: string | Options | undefined); | ||
imports: string[]; | ||
exports: any[]; | ||
reexports: never[]; | ||
__syncModuleProgram__: string; | ||
@@ -33,2 +59,9 @@ __liveExportMap__: any; | ||
} | ||
export type SourceMapHook = (sourceMap: string, sourceUrl: string, source: string) => any; | ||
export type Options = { | ||
sourceUrl?: string | undefined; | ||
sourceMap?: string | undefined; | ||
sourceMapUrl?: string | undefined; | ||
sourceMapHook?: SourceMapHook | undefined; | ||
}; | ||
//# sourceMappingURL=static-module-record.d.ts.map |
@@ -5,4 +5,7 @@ /* eslint no-underscore-dangle: ["off"] */ | ||
const { freeze, keys, values } = Object; | ||
const { keys, values } = Object; | ||
// Disable readonly markings. | ||
const freeze = /** @type {<T>(v: T) => T} */ (Object.freeze); | ||
// If all ESM implementations were correct, it would be sufficient to | ||
@@ -35,2 +38,17 @@ // `import babel` instead of `import * as babel`. | ||
/** | ||
* @callback SourceMapHook | ||
* @param {string} sourceMap | ||
* @param {string} sourceUrl | ||
* @param {string} source | ||
*/ | ||
/** | ||
* @typedef {object} Options | ||
* @property {string} [sourceUrl] | ||
* @property {string} [sourceMap] | ||
* @property {string} [sourceMapUrl] | ||
* @property {SourceMapHook} [sourceMapHook] | ||
*/ | ||
/** | ||
* StaticModuleRecord captures the effort of parsing and analyzing module text | ||
@@ -41,6 +59,6 @@ * so a cache of StaticModuleRecords may be shared by multiple Compartments. | ||
* @param {string} source | ||
* @param {string} [url] | ||
* @param {string | Options} [opts] | ||
* @returns {import('ses').PrecompiledStaticModuleInterface} | ||
*/ | ||
export function StaticModuleRecord(source, url) { | ||
export function StaticModuleRecord(source, opts = {}) { | ||
if (new.target === undefined) { | ||
@@ -51,2 +69,5 @@ throw TypeError( | ||
} | ||
if (typeof opts === 'string') { | ||
opts = { sourceUrl: opts }; | ||
} | ||
const { | ||
@@ -60,3 +81,3 @@ imports, | ||
needsImportMeta, | ||
} = analyzeModule({ string: source, url }); | ||
} = analyzeModule(source, opts); | ||
this.imports = freeze([...keys(imports)]); | ||
@@ -63,0 +84,0 @@ this.exports = freeze( |
@@ -1,4 +0,6 @@ | ||
export function makeModuleAnalyzer(babel: any): ({ string, url }: { | ||
string: any; | ||
url: any; | ||
export function makeModuleAnalyzer(babel: any): (moduleSource: any, { sourceUrl, sourceMapUrl, sourceMap, sourceMapHook }?: { | ||
sourceUrl: any; | ||
sourceMapUrl: any; | ||
sourceMap: any; | ||
sourceMapHook: any; | ||
}) => Readonly<{ | ||
@@ -5,0 +7,0 @@ exportAlls: readonly never[]; |
@@ -9,5 +9,12 @@ import { makeTransformSource } from './transformSource.js'; | ||
const makeCreateStaticRecord = transformSource => | ||
function createStaticRecord(moduleSource, url) { | ||
function createStaticRecord( | ||
moduleSource, | ||
{ sourceUrl, sourceMapUrl, sourceMap, sourceMapHook } = {}, | ||
) { | ||
// Transform the Module source code. | ||
const sourceOptions = { | ||
sourceUrl, | ||
sourceMap, | ||
sourceMapUrl, | ||
sourceMapHook, | ||
sourceType: 'module', | ||
@@ -41,3 +48,5 @@ // exportNames of variables that are only initialized and used, but | ||
} catch (err) { | ||
const moduleLocation = url ? JSON.stringify(url) : '<unknown>'; | ||
const moduleLocation = sourceUrl | ||
? JSON.stringify(sourceUrl) | ||
: '<unknown>'; | ||
throw SyntaxError( | ||
@@ -97,4 +106,4 @@ `Error transforming source in ${moduleLocation}: ${err.message}`, | ||
if (url) { | ||
functorSource += `//# sourceURL=${url}\n`; | ||
if (sourceUrl) { | ||
functorSource += `//# sourceURL=${sourceUrl}\n`; | ||
} | ||
@@ -115,4 +124,3 @@ const moduleAnalysis = freeze({ | ||
const transformSource = makeTransformSource(makeModulePlugins, babel); | ||
const createStaticRecord = makeCreateStaticRecord(transformSource); | ||
return ({ string, url }) => createStaticRecord(string, url); | ||
return makeCreateStaticRecord(transformSource); | ||
}; | ||
@@ -119,0 +127,0 @@ |
@@ -1,2 +0,2 @@ | ||
export function makeTransformSource(makeModulePlugins: any, babel?: null): (code: any, sourceOptions?: {}) => any; | ||
export function makeTransformSource(makeModulePlugins: any, babel?: null): (source: any, sourceOptions?: {}) => any; | ||
//# sourceMappingURL=transformSource.d.ts.map |
@@ -22,16 +22,34 @@ import * as babelParser from '@babel/parser'; | ||
const transformSource = (code, sourceOptions = {}) => { | ||
const transformSource = (source, sourceOptions = {}) => { | ||
const { analyzePlugin, transformPlugin } = makeModulePlugins(sourceOptions); | ||
const ast = parseBabel(code, { sourceType: sourceOptions.sourceType }); | ||
const { sourceUrl, sourceMapUrl, sourceType, sourceMap, sourceMapHook } = | ||
sourceOptions; | ||
const ast = parseBabel(source, { sourceType }); | ||
traverseBabel(ast, visitorFromPlugin(analyzePlugin)); | ||
traverseBabel(ast, visitorFromPlugin(transformPlugin)); | ||
const { code: transformedCode } = generateBabel(ast, { | ||
retainLines: true, | ||
compact: true, | ||
verbatim: true, | ||
}); | ||
return transformedCode; | ||
const sourceMaps = sourceOptions.sourceMapHook !== undefined; | ||
const { code: transformedSource, map: transformedSourceMap } = | ||
generateBabel(ast, { | ||
sourceFileName: sourceMapUrl, | ||
sourceMaps, | ||
inputSourceMap: sourceMap, | ||
retainLines: true, | ||
compact: true, | ||
verbatim: true, | ||
}); | ||
if (sourceMaps) { | ||
sourceMapHook(transformedSourceMap, { | ||
sourceUrl, | ||
sourceMapUrl, | ||
source, | ||
}); | ||
} | ||
return transformedSource; | ||
}; | ||
@@ -38,0 +56,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
59677
1064
86
21
Updatedses@^0.18.7