Skip to content

Commit

Permalink
fix(bundle-source): regain default 'getExport'
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelfig committed Feb 28, 2020
1 parent bec9c66 commit f234d49
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 20 deletions.
52 changes: 40 additions & 12 deletions packages/bundle-source/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import eventualSend from '@agoric/acorn-eventual-send';
import * as acorn from 'acorn';

const DEFAULT_MODULE_FORMAT = 'getExport';
const DEFAULT_FILE_PREFIX = '/bundled-source';
const SUPPORTED_FORMATS = ['getExport', 'nestedEvaluate'];

export default async function bundleSource(
startFilename,
moduleFormat = DEFAULT_MODULE_FORMAT,
access,
access = undefined,
) {
if (moduleFormat !== 'getExport') {
if (!SUPPORTED_FORMATS.includes(moduleFormat)) {
throw Error(`moduleFormat ${moduleFormat} is not implemented`);
}
const { commonjsPlugin, rollup, resolvePlugin, pathResolve } = access || {
Expand All @@ -25,7 +27,7 @@ export default async function bundleSource(
const bundle = await rollup({
input: resolvedPath,
treeshake: false,
preserveModules: true,
preserveModules: moduleFormat === 'nestedEvaluate',
external: ['@agoric/evaluate', '@agoric/nat', '@agoric/harden'],
plugins: [resolvePlugin({ preferBuiltins: true }), commonjsPlugin()],
acornInjectPlugins: [eventualSend(acorn)],
Expand Down Expand Up @@ -65,36 +67,59 @@ export default async function bundleSource(
// be evaluated and invoked to get at the exports.

// const sourceMap = `//# sourceMappingURL=${output[0].map.toUrl()}\n`;
const sourceMap = `//# sourceURL:file:///bundle-source/${moduleFormat}-preamble.js`;

// console.log(sourceMap);
let sourceMap;
let source;
if (moduleFormat === 'getExport') {
sourceMap = `//# sourceURL=${resolvedPath}\n`;

if (Object.keys(sourceBundle).length !== 1) {
throw Error('unprepared for more than one chunk');
}

source = `\
function getExport() { 'use strict'; \
let exports = {}; \
const module = { exports }; \
\
${sourceBundle[entrypoint]}
return module.exports;
}
`;
} else if (moduleFormat === 'nestedEvaluate') {
sourceMap = `//# sourceURL=${DEFAULT_FILE_PREFIX}-preamble.js\n`;

// This function's source code is inlined in the output bundle.
// It creates an evaluable string for a given module filename.
const filePrefix = DEFAULT_FILE_PREFIX;
function createEvalString(filename) {
const code = sourceBundle[filename];
if (!code) {
return undefined;
}
return `\
(function getOneExport(require) { \
(function getExport(require) { \
'use strict'; \
let exports = {}; \
const module = { exports }; \
\
${code}
return module.exports;
})
//# sourceURL=file:///bundle-source/${moduleFormat}/${filename}
//# sourceURL=${filePrefix}/${filename}
`;
}

// This function's source code is inlined in the output bundle.
// It figures out the exports from a given module filename.
const nsBundle = {};
const nestedEvaluate = _src => {
throw Error('need to override nestedEvaluate');
};
function computeExports(filename, powers) {
const { eval: myEval, require: myRequire, _log } = powers;
const { require: myRequire, _log } = powers;
// This captures the endowed require.
const match = filename.match(/^(.*)\/[^\/]+$/);
const thisdir = match ? match[1] : '.';
Expand Down Expand Up @@ -149,15 +174,19 @@ export default async function bundleSource(
return myRequire(filename);
}

// log('evaluating', code);
return (1, myEval)(code)(contextRequire);
// log('evaluating', typeof nestedEvaluate, code);
return nestedEvaluate(code)(contextRequire);
}

source = `\
function getExport() {
function getExportWithNestedEvaluate(filePrefix) {
'use strict';
// Serialised sources.
if (filePrefix === undefined) {
filePrefix = ${JSON.stringify(DEFAULT_FILE_PREFIX)};
}
const moduleFormat = ${JSON.stringify(moduleFormat)};
const entrypoint = ${JSON.stringify(entrypoint)};
const sourceBundle = ${JSON.stringify(sourceBundle, undefined, 2)};
const nsBundle = {};
Expand All @@ -166,8 +195,7 @@ function getExport() {
${computeExports}
// Evaluate the entrypoint recursively.
const entrypoint = ${JSON.stringify(entrypoint)}
return computeExports(entrypoint, { eval, require, log(...args) { return console.log(...args); } });
return computeExports(entrypoint, { require, log(...args) { return console.log(...args); } });
}`;
}

Expand Down
74 changes: 66 additions & 8 deletions packages/bundle-source/test/sanity.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,87 @@
import { test } from 'tape-promise/tape';
import { evaluateProgram as evaluate } from '@agoric/evaluate';
import bundleSource from '..';

test('sanity', async t => {
test('nestedEvaluate', async t => {
try {
const {
moduleFormat: mf1,
source: src1,
sourceMap: map1,
} = await bundleSource(`${__dirname}/../demo/dir1`);
} = await bundleSource(`${__dirname}/../demo/dir1`, 'nestedEvaluate');

const srcMap1 = `(${src1}\n)()\n${map1}`;
const srcMap1 = `(${src1})\n${map1}`;

// console.log(srcMap1);

t.equal(mf1, 'nestedEvaluate', 'module format is nestedEvaluate');
t.assert(src1.match(/require\('@agoric\/harden'\)/), 'harden is required');

// Fake out `require('@agoric/harden')`.
const require = _ => o => o;
const nestedEvaluate = src => {
// console.log('========== evaluating', src);
return evaluate(src, { require, nestedEvaluate });
};
const ex1 = nestedEvaluate(srcMap1)();

const bundle = ex1.default();
const err = bundle.makeError('foo');
t.assert(
err.stack.indexOf('(/bundled-source/encourage.js:') >= 0,
'bundled source is in stack trace',
);

const {
moduleFormat: mf2,
source: src2,
sourceMap: map2,
} = await bundleSource(
`${__dirname}/../demo/dir1/encourage.js`,
'nestedEvaluate',
);
t.equal(mf2, 'nestedEvaluate', 'module format 2 is nestedEvaluate');

const srcMap2 = `(${src2})\n${map2}`;

const ex2 = nestedEvaluate(srcMap2)();
t.equal(ex2.message, `You're great!`, 'exported message matches');
t.equal(
ex2.encourage('Nick'),
`Hey Nick! You're great!`,
'exported encourage matches',
);
} catch (e) {
t.isNot(e, e, 'unexpected exception');
} finally {
t.end();
}
});

test('getExport', async t => {
try {
const {
moduleFormat: mf1,
source: src1,
sourceMap: map1,
} = await bundleSource(`${__dirname}/../demo/dir1`, 'getExport');

const srcMap1 = `(${src1})\n${map1}`;

// console.log(srcMap1);

t.equal(mf1, 'getExport', 'module format is getExport');
t.assert(src1.match(/require\('@agoric\/harden'\)/), 'harden is required');

// Fake out `require('@agoric/harden')`.
// eslint-disable-next-line no-eval
const ex1 = (1, eval)(`function require() { return o => o };${srcMap1}`);
const ex1 = eval(`const require = _ => o => o;${srcMap1}`)();

const bundle = ex1.default();
const err = bundle.makeError('foo');
t.assert(
err.stack.indexOf('(file:///bundle-source/getExport/encourage.js:') >= 0,
'bundled source is in stack trace',
err.stack.indexOf('(/bundled-source/encourage.js:') < 0,
'bundled source is not in stack trace',
);

const {
Expand All @@ -33,10 +91,10 @@ test('sanity', async t => {
} = await bundleSource(`${__dirname}/../demo/dir1/encourage.js`);
t.equal(mf2, 'getExport', 'module format 2 is getExport');

const srcMap2 = `(${src2}\n)()\n${map2}`;
const srcMap2 = `(${src2})\n${map2}`;

// eslint-disable-next-line no-eval
const ex2 = (1, eval)(srcMap2);
const ex2 = eval(srcMap2)();
t.equal(ex2.message, `You're great!`, 'exported message matches');
t.equal(
ex2.encourage('Nick'),
Expand Down

0 comments on commit f234d49

Please sign in to comment.