Skip to content

Commit

Permalink
feat(compartment-mapper): Support source map generation
Browse files Browse the repository at this point in the history
  • Loading branch information
kriskowal committed Jul 21, 2023
1 parent ee5febf commit 7f2dc59
Show file tree
Hide file tree
Showing 10 changed files with 158 additions and 12 deletions.
2 changes: 2 additions & 0 deletions packages/compartment-mapper/src/archive.js
Expand Up @@ -302,6 +302,7 @@ const digestLocation = async (powers, moduleLocation, options) => {
commonDependencies = undefined,
importHook: exitModuleImportHook = undefined,
policy = undefined,
sourceMapHook = undefined,
} = options || {};
const { read, computeSha512 } = unpackReadPowers(powers);
const {
Expand Down Expand Up @@ -350,6 +351,7 @@ const digestLocation = async (powers, moduleLocation, options) => {
entryCompartmentName,
entryModuleSpecifier,
exitModuleImportHook: consolidatedExitModuleImportHook,
sourceMapHook,
});
// Induce importHook to record all the necessary modules to import the given module specifier.
const { compartment, attenuatorsCompartment } = link(compartmentMap, {
Expand Down
3 changes: 3 additions & 0 deletions packages/compartment-mapper/src/bundle.js
Expand Up @@ -167,6 +167,7 @@ function getBundlerKitForModule(module) {
* @param {Set<string>} [options.tags]
* @param {object} [options.commonDependencies]
* @param {Array<string>} [options.searchSuffixes]
* @param {import('./types.js').SourceMapHook} [options.sourceMapHook]
* @returns {Promise<string>}
*/
export const makeBundle = async (read, moduleLocation, options) => {
Expand All @@ -176,6 +177,7 @@ export const makeBundle = async (read, moduleLocation, options) => {
tags: tagsOption,
searchSuffixes,
commonDependencies,
sourceMapHook = undefined,
} = options || {};
const tags = new Set(tagsOption);

Expand Down Expand Up @@ -212,6 +214,7 @@ export const makeBundle = async (read, moduleLocation, options) => {
searchSuffixes,
entryCompartmentName,
entryModuleSpecifier,
sourceMapHook,
});

// Induce importHook to record all the necessary modules to import the given module specifier.
Expand Down
31 changes: 30 additions & 1 deletion packages/compartment-mapper/src/import-archive.js
Expand Up @@ -12,6 +12,7 @@
/** @typedef {import('./types.js').HashFn} HashFn */
/** @typedef {import('./types.js').StaticModuleType} StaticModuleType */
/** @typedef {import('./types.js').ComputeSourceLocationHook} ComputeSourceLocationHook */
/** @typedef {import('./types.js').ComputeSourceMapLocationHook} ComputeSourceMapLocationHook */
/** @typedef {import('./types.js').LoadArchiveOptions} LoadArchiveOptions */
/** @typedef {import('./types.js').ExecuteOptions} ExecuteOptions */
/** @typedef {import('./types.js').ImportHookMaker} ImportHookMaker */
Expand Down Expand Up @@ -77,6 +78,7 @@ const postponeErrorToExecute = errorMessage => {
* @param {HashFn} [computeSha512]
* @param {ComputeSourceLocationHook} [computeSourceLocation]
* @param {ExitModuleImportHook} [exitModuleImportHook]
* @param {import('./types.js').ComputeSourceMapLocationHook} [computeSourceMapLocation]
* @returns {ImportHookMaker}
*/
const makeArchiveImportHookMaker = (
Expand All @@ -86,6 +88,7 @@ const makeArchiveImportHookMaker = (
computeSha512 = undefined,
computeSourceLocation = undefined,
exitModuleImportHook = undefined,
computeSourceMapLocation = undefined,
) => {
// per-assembly:
/** @type {ImportHookMaker} */
Expand Down Expand Up @@ -190,12 +193,28 @@ const makeArchiveImportHookMaker = (
sourceLocation;
}

let sourceMapUrl;
if (
computeSourceMapLocation !== undefined &&
module.sha512 !== undefined
) {
sourceMapUrl = computeSourceMapLocation({
compartment: packageLocation,
module: moduleSpecifier,
location: sourceLocation,
sha512: module.sha512,
});
}

// eslint-disable-next-line no-await-in-loop
const { record } = await parse(
moduleBytes,
moduleSpecifier,
sourceLocation,
packageLocation,
{
sourceMapUrl,
},
);
return { record, specifier: moduleSpecifier };
};
Expand Down Expand Up @@ -240,6 +259,7 @@ const makeFeauxModuleExportsNamespace = Compartment => {
* @param {ExitModuleImportHook} [options.importHook]
* @param {CompartmentConstructor} [options.Compartment]
* @param {ComputeSourceLocationHook} [options.computeSourceLocation]
* @param {ComputeSourceMapLocationHook} [options.computeSourceMapLocation]
* @returns {Promise<Application>}
*/
export const parseArchive = async (
Expand All @@ -251,6 +271,7 @@ export const parseArchive = async (
computeSha512 = undefined,
expectedSha512 = undefined,
computeSourceLocation = undefined,
computeSourceMapLocation = undefined,
Compartment = DefaultCompartment,
modules = undefined,
importHook: exitModuleImportHook = undefined,
Expand Down Expand Up @@ -319,6 +340,7 @@ export const parseArchive = async (
computeSha512,
computeSourceLocation,
compartmentExitModuleImportHook,
computeSourceMapLocation,
);
// A weakness of the current Compartment design is that the `modules` map
// must be given a module namespace object that passes a brand check.
Expand Down Expand Up @@ -366,6 +388,7 @@ export const parseArchive = async (
computeSha512,
computeSourceLocation,
compartmentExitModuleImportHook,
computeSourceMapLocation,
);
const { compartment, pendingJobsPromise } = link(compartmentMap, {
makeImportHook,
Expand Down Expand Up @@ -398,13 +421,19 @@ export const loadArchive = async (
options = {},
) => {
const { read, computeSha512 } = unpackReadPowers(readPowers);
const { expectedSha512, computeSourceLocation, modules } = options;
const {
expectedSha512,
computeSourceLocation,
modules,
computeSourceMapLocation,
} = options;
const archiveBytes = await read(archiveLocation);
return parseArchive(archiveBytes, archiveLocation, {
computeSha512,
expectedSha512,
computeSourceLocation,
modules,
computeSourceMapLocation,
});
};

Expand Down
18 changes: 18 additions & 0 deletions packages/compartment-mapper/src/import-hook.js
Expand Up @@ -113,6 +113,7 @@ export const exitModuleImportHookMaker = ({
* @param {string} options.entryCompartmentName
* @param {string} options.entryModuleSpecifier
* @param {ExitModuleImportHook} [options.exitModuleImportHook]
* @param {import('./types.js').SourceMapHook} [options.sourceMapHook]
* @returns {ImportHookMaker}
*/
export const makeImportHookMaker = (
Expand All @@ -124,6 +125,7 @@ export const makeImportHookMaker = (
archiveOnly = false,
computeSha512 = undefined,
searchSuffixes = nodejsConventionSearchSuffixes,
sourceMapHook = undefined,
entryCompartmentName,
entryModuleSpecifier,
exitModuleImportHook = undefined,
Expand Down Expand Up @@ -300,6 +302,8 @@ export const makeImportHookMaker = (
_error => undefined,
);
if (moduleBytes !== undefined) {
/** @type {string | undefined} */
let sourceMap;
// eslint-disable-next-line no-await-in-loop
const envelope = await parse(
moduleBytes,
Expand All @@ -308,6 +312,11 @@ export const makeImportHookMaker = (
packageLocation,
{
readPowers,
sourceMapHook:
sourceMapHook &&
(nextSourceMapObject => {
sourceMap = JSON.stringify(nextSourceMapObject);
}),
},
);
const {
Expand All @@ -334,6 +343,15 @@ export const makeImportHookMaker = (
let sha512;
if (computeSha512 !== undefined) {
sha512 = computeSha512(transformedBytes);

if (sourceMapHook !== undefined && sourceMap !== undefined) {
sourceMapHook(sourceMap, {
compartment: packageLocation,
module: candidateSpecifier,
location: moduleLocation,
sha512,
});
}
}

const packageRelativeLocation = moduleLocation.slice(
Expand Down
19 changes: 17 additions & 2 deletions packages/compartment-mapper/src/link.js
Expand Up @@ -91,13 +91,25 @@ const makeExtensionParser = (
language = languageForExtension[extension] || extension;
}

let sourceMap;

if (has(moduleTransforms, language)) {
try {
({ bytes, parser: language } = await moduleTransforms[language](
({
bytes,
parser: language,
sourceMap,
} = await moduleTransforms[language](
bytes,
specifier,
location,
packageLocation,
{
// At time of writing, sourceMap is always undefined, but keeping
// it here is more resilient if the surrounding if block becomes a
// loop for multi-step transforms.
sourceMap,
},
));
} catch (err) {
throw Error(
Expand All @@ -115,7 +127,10 @@ const makeExtensionParser = (
);
}
const { parse } = parserForLanguage[language];
return parse(bytes, specifier, location, packageLocation, options);
return parse(bytes, specifier, location, packageLocation, {
sourceMap,
...options,
});
};
};

Expand Down
14 changes: 10 additions & 4 deletions packages/compartment-mapper/src/parse-archive-mjs.js
Expand Up @@ -8,12 +8,18 @@ const textDecoder = new TextDecoder();
/** @type {import('./types.js').ParseFn} */
export const parseArchiveMjs = async (
bytes,
_specifier,
_location,
_packageLocation,
specifier,
sourceUrl,
packageLocation,
options = {},
) => {
const { sourceMap, sourceMapHook } = options;
const source = textDecoder.decode(bytes);
const record = new StaticModuleRecord(source);
const record = new StaticModuleRecord(source, {
sourceMap,
sourceMapUrl: sourceUrl,
sourceMapHook,
});
const pre = textEncoder.encode(JSON.stringify(record));
return {
parser: 'pre-mjs-json',
Expand Down
11 changes: 9 additions & 2 deletions packages/compartment-mapper/src/parse-mjs.js
Expand Up @@ -8,11 +8,18 @@ const textDecoder = new TextDecoder();
export const parseMjs = async (
bytes,
_specifier,
location,
sourceUrl,
_packageLocation,
options = {},
) => {
const { sourceMap, sourceMapHook } = options;
const source = textDecoder.decode(bytes);
const record = new StaticModuleRecord(source, location);
const record = new StaticModuleRecord(source, {
sourceUrl,
sourceMap,
sourceMapUrl: sourceUrl,
sourceMapHook,
});
return {
parser: 'mjs',
bytes,
Expand Down
10 changes: 8 additions & 2 deletions packages/compartment-mapper/src/parse-pre-mjs.js
Expand Up @@ -10,11 +10,17 @@ export const parsePreMjs = async (
_specifier,
location,
_packageLocation,
{ sourceMapUrl },
) => {
const text = textDecoder.decode(bytes);
const record = parseLocatedJson(text, location);
// eslint-disable-next-line no-underscore-dangle
record.__syncModuleProgram__ += `//# sourceURL=${location}\n`;
if (sourceMapUrl) {
// eslint-disable-next-line no-underscore-dangle
record.__syncModuleProgram__ += `//# sourceMappingURL=${sourceMapUrl}\n`;
} else {
// eslint-disable-next-line no-underscore-dangle
record.__syncModuleProgram__ += `//# sourceURL=${location}\n`;
}
return {
parser: 'pre-mjs-json',
bytes,
Expand Down
38 changes: 37 additions & 1 deletion packages/compartment-mapper/src/types.js
Expand Up @@ -194,24 +194,56 @@ export {};
* @property {ShouldDeferError} shouldDeferError
* @property {Record<string, Compartment>} compartments
*/

/**
* @callback ImportHookMaker
* @param {ImportHookMakerOptions} options
* @returns {ImportHook}
*/

/**
* @typedef {object} SourceMapHookDetails
* @property {string} compartment
* @property {string} module
* @property {string} location
* @property {string} sha512
*/

/**
* @callback SourceMapHook
* @param {string} sourceMap
* @param {SourceMapHookDetails} details
*/

/**
* @typedef {object} ComputeSourceMapLocationDetails
* @property {string} compartment
* @property {string} module
* @property {string} location
* @property {string} sha512
*/

/**
* @callback ComputeSourceMapLocationHook
* @param {ComputeSourceMapLocationDetails} details
* @returns {string}
*/

/**
* @callback ParseFn
* @param {Uint8Array} bytes
* @param {string} specifier
* @param {string} location
* @param {string} packageLocation
* @param {object} [options]
* @param {string} [options.sourceMap]
* @param {string} [options.sourceMapUrl]
* @param {ReadFn | ReadPowers} [options.readPowers]
* @returns {Promise<{
* bytes: Uint8Array,
* parser: Language,
* record: FinalStaticModuleType,
* sourceMap?: string,
* }>}
*/

Expand Down Expand Up @@ -243,6 +275,7 @@ export {};
* @property {Record<string, any>} [modules]
* @property {typeof Compartment} [Compartment]
* @property {ComputeSourceLocationHook} [computeSourceLocation]
* @property {ComputeSourceMapLocationHook} [computeSourceMapLocation]
*/

/**
Expand Down Expand Up @@ -283,7 +316,9 @@ export {};
* @param {string} specifier
* @param {string} location
* @param {string} packageLocation
* @returns {Promise<{bytes: Uint8Array, parser: Language}>}
* @param {object} [options]
* @param {string} [options.sourceMap]
* @returns {Promise<{bytes: Uint8Array, parser: Language, sourceMap?: string}>}
*/

// /////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -335,6 +370,7 @@ export {};
* @property {ExitModuleImportHook} [importHook]
* @property {Array<string>} [searchSuffixes]
* @property {Record<string, string>} [commonDependencies]
* @property {SourceMapHook} [sourceMapHook]
*/

// /////////////////////////////////////////////////////////////////////////////
Expand Down

0 comments on commit 7f2dc59

Please sign in to comment.