Thread module sourceURLs for locations in stack traces
MODIFY
packages/ses/README.md
packages/ses/README.md
@@ -94,7 +94,7 @@ const c1 = new Compartment({}, {}, {
importHook: async moduleSpecifier => {
const moduleLocation = locate(moduleSpecifier);
const moduleText = await retrieve(moduleLocation);
- return new ModuleStaticRecord(moduleText);
+ return new ModuleStaticRecord(moduleText, moduleLocation);
},
});
```

MODIFY
packages/ses/src/compartment-shim.js
packages/ses/src/compartment-shim.js
@@ -38,8 +38,8 @@ const moduleAnalyses = new WeakMap();
* so a cache of ModuleStaticRecords may be shared by multiple Compartments.
*/
export class ModuleStaticRecord {
- constructor(text) {
- const analysis = analyzeModule({ string: text });
+ constructor(string, url) {
+ const analysis = analyzeModule({ string, url });
this.imports = Object.keys(analysis.imports).sort();

MODIFY
packages/ses/test/import-commons.js
packages/ses/test/import-commons.js
@@ -23,5 +23,5 @@ export const makeStaticRetriever = sources => {
export const makeImporter = (locate, retrieve) => async moduleSpecifier => {
const moduleLocation = locate(moduleSpecifier);
const string = await retrieve(moduleLocation);
- return new ModuleStaticRecord(string);
+ return new ModuleStaticRecord(string, moduleLocation);
};

ADD
packages/ses/test/import-stack-traces.test.js
+import tap from 'tap';
+import { Compartment } from '../src/main.js';
+import { resolveNode, makeNodeImporter } from './node.js';
+
+const { test } = tap;
+
+test('preserve file names in stack traces', async t => {
+ const makeImportHook = makeNodeImporter({
+ 'https://example.com/packages/erroneous/main.js': `
+ throw new Error("threw an error");
+ `,
+ });
+
+ const compartment = new Compartment(
+ {}, // endowments
+ {}, // module map
+ {
+ resolveHook: resolveNode,
+ importHook: makeImportHook('https://example.com/packages/erroneous'),
+ },
+ );
+
+ let error;
+ try {
+ await compartment.import('./main.js');
+ } catch (_error) {
+ error = _error;
+ }
+
+ // Not all environments that run this test will necessarily surface stack
+ // traces, but all that do should respect the //# sourceURL directive that
+ // transform-module injects.
+ if (error.stack != null) {
+ t.ok(
+ /https:\/\/example.com\/packages\/erroneous/.exec(error.stack),
+ 'stack trace contains file name of emitting module',
+ );
+ }
+
+ t.end();
+});

MODIFY
packages/transform-module/src/main.js
packages/transform-module/src/main.js
@@ -82,7 +82,7 @@ const makeTransformSource = babel =>
};
const makeCreateStaticRecord = transformSource =>
- function createStaticRecord(moduleSource) {
+ function createStaticRecord(moduleSource, url) {
// Transform the Module source code.
const sourceOptions = {
sourceType: 'module',
@@ -151,7 +151,8 @@ const makeCreateStaticRecord = transformSource =>
}) => { \
${preamble} \
${scriptSource}
-})`;
+})
+//# sourceURL=${url}`;
const moduleStaticRecord = freeze({
exportAlls: freeze(sourceOptions.exportAlls),
@@ -167,7 +168,7 @@ const makeCreateStaticRecord = transformSource =>
export const makeModuleAnalyzer = babel => {
const transformSource = makeTransformSource(babel);
const createStaticRecord = makeCreateStaticRecord(transformSource);
- return ({ string }) => createStaticRecord(string);
+ return ({ string, url }) => createStaticRecord(string, url);
};
export const makeModuleTransformer = (babel, importer) => {
@@ -199,7 +200,7 @@ export const makeModuleTransformer = (babel, importer) => {
if (ss.sourceType === 'module') {
// Do the rewrite of our own sources.
- const staticRecord = createStaticRecord(source);
+ const staticRecord = createStaticRecord(source, url);
Object.assign(endowments, {
// Import our own source directly, returning a promise.
[h.HIDDEN_IMPORT_SELF]: () => curryImporter({ url, staticRecord }),

Thread module sourceURLs for locations in stack traces (#290)