Skip to content
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

More bundler features #1410

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
3 changes: 2 additions & 1 deletion packages/compartment-mapper/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
"lint:js": "eslint .",
"lint:types": "tsc -p jsconfig.json",
"prettier-fixtures": "prettier --write --with-node-modules './test/fixtures-*/**/*.*js'",
"test": "ava"
"test": "ava",
"dev:livebundle": "mkdir -p dist && nodemon scripts/bundle-live-test.js --ignore dist/bu.js"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

example bundle is no longer being committed, but I kinda regret that. It'd show what changed in the PR really well.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

snapshot tests!

},
"dependencies": {
"@endo/cjs-module-analyzer": "^0.2.28",
Expand Down
26 changes: 26 additions & 0 deletions packages/compartment-mapper/scripts/bundle-live-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import 'ses';
import fs from 'fs';
import url from 'url';
import { makeBundle } from '../index.js';
import { makeReadPowers } from '../node-powers.js';

const fixture = new URL(
'../test/fixtures-0/node_modules/bundle/main.js',
import.meta.url,
).toString();
const target = new URL('../dist/bu.js', import.meta.url).toString();

const readPowers = makeReadPowers({ fs, url });

const bundle = await makeBundle(readPowers.read, fixture);
fs.writeFileSync(url.fileURLToPath(target), bundle);
// fs.writeFileSync(url.fileURLToPath(`${target}.${Date.now()}`), bundle);

console.log(`
#######################
size: ${bundle.length}
`);

// eslint-disable-next-line no-undef
global.print = console.log;
import(target);
46 changes: 20 additions & 26 deletions packages/compartment-mapper/src/bundle-cjs.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,7 @@
/** quotes strings */
const q = JSON.stringify;

const exportsCellRecord = exportsList =>
''.concat(
...exportsList.map(
exportName => `\
${exportName}: cell(${q(exportName)}),
`,
),
);

// vvv runtime to inline in the bundle vvv
/* eslint-disable no-undef */
// This function is serialized and references variables from its destination scope.
const runtime = function wrapCjsFunctor(num) {
/* eslint-disable no-undef */
function wrapCjsFunctor(num) {
return ({ imports = {} }) => {
const cModule = Object.freeze(
Object.defineProperty({}, 'exports', cells[num].default),
Expand All @@ -24,28 +13,33 @@ const runtime = function wrapCjsFunctor(num) {
.filter(k => k !== 'default' && k !== '*')
.map(k => cells[num][k].set(cModule.exports[k]));
};
/* eslint-enable no-undef */
}.toString();
}
/* eslint-enable no-undef */
const runtime = `\
${wrapCjsFunctor}`;
// ^^^ runtime to inline in the bundle ^^^

export default {
runtime,
getBundlerKit({
index,
indexedImports,
record: { cjsFunctor, exports: exportsList = {} },
}) {
getBundlerKit(
{
index,
indexedImports,
record: { cjsFunctor, exports: exportsList = {} },
},
{ __removeSourceURL = false } = {},
) {
const importsMap = JSON.stringify(indexedImports);
if (__removeSourceURL) {
cjsFunctor = cjsFunctor.replace(/\/\/# sourceURL=.*/, '');
}

return {
getFunctor: () => `\
// === functors[${index}] ===
${cjsFunctor},
`,
getCells: () => `\
{
${exportsCellRecord(exportsList)}\
},
`,
getCells: () => exportsList,
getReexportsWiring: () => '',
getFunctorCall: () => `\
wrapCjsFunctor(${index})({imports: ${importsMap}});
Expand Down
16 changes: 16 additions & 0 deletions packages/compartment-mapper/src/bundle-json.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export default {
runtime: '',
getBundlerKit({ index, record: { sourceText } }) {
return {
getFunctor: () => `\
// === functors[${index}] ===
${sourceText},
`,
getCells: () => ['default'],
getReexportsWiring: () => '',
getFunctorCall: () => `\
cells[${index}].default.set(functors[${index}]);
`,
};
},
};
134 changes: 74 additions & 60 deletions packages/compartment-mapper/src/bundle-mjs.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,6 @@
/** quotes strings */
const q = JSON.stringify;

const exportsCellRecord = exportMap =>
''.concat(
...Object.keys(exportMap).map(
exportName => `\
${exportName}: cell(${q(exportName)}),
`,
),
);

const importsCellSetter = (exportMap, index) =>
''.concat(
...Object.entries(exportMap).map(
Expand All @@ -19,63 +10,95 @@ const importsCellSetter = (exportMap, index) =>
),
);

const adaptReexport = reexportMap => {
const importImplementation = indexedImports => {
const knownEntries = Object.entries(indexedImports);
if (knownEntries.length === 0) {
return `imports() {},`;
}
return `\
imports(entries) {
const map = new Map(entries);
observeImports(map, [
${''.concat(
...knownEntries.map(
([importName, importIndex]) => `\
[${q(importName)}, ${importIndex}],
`,
),
)}\
]);},`;
};

const getReexportKeys = reexportMap => {
if (!reexportMap) {
return {};
}
const ret = Object.fromEntries(
Object.values(reexportMap)
.flat()
.map(([local, exported]) => [exported, [local]]),
);
return ret;
return Object.values(reexportMap)
.flat()
.map(([local, _exported]) => local);
};

export const runtime = `\
function observeImports(map, importName, importIndex) {
for (const [name, observers] of map.get(importName)) {
const cell = cells[importIndex][name];
if (cell === undefined) {
throw new ReferenceError(\`Cannot import name \${name}\`);
}
for (const observer of observers) {
cell.observe(observer);
// vvv runtime to inline in the bundle vvv
/* eslint-disable no-undef */
function observeImports(map, pairs) {
for (const [importName, importIndex] of pairs) {
for (const [name, observers] of map.get(importName)) {
const cell = cells[importIndex][name];
if (cell === undefined) {
throw new ReferenceError(`Cannot import name ${name}`);
}
for (const observer of observers) {
cell.observe(observer);
}
}
}
}
`;

/* eslint-enable no-undef */

const runtime = `\
${observeImports}`;
// ^^^ runtime to inline in the bundle ^^^

export default {
runtime,
getBundlerKit({
index,
indexedImports,
record: {
__syncModuleProgram__,
__fixedExportMap__ = {},
__liveExportMap__ = {},
__reexportMap__ = {},
reexports,
getBundlerKit(
{
index,
indexedImports,
record: {
__syncModuleProgram__,
__fixedExportMap__ = {},
__liveExportMap__ = {},
__reexportMap__ = {},
__needsImportMeta__ = false,
reexports,
},
},
}) {
{ __removeSourceURL = false } = {},
) {
if (__removeSourceURL) {
__syncModuleProgram__ = `${__syncModuleProgram__}`.replace(
/\/\/# sourceURL=.*/,
'',
);
}
return {
getFunctor: () => `\
// === functors[${index}] ===
${__syncModuleProgram__},
`,
getCells: () => `\
{
${exportsCellRecord(__fixedExportMap__)}${exportsCellRecord(
__liveExportMap__,
)}${exportsCellRecord(adaptReexport(__reexportMap__))}\
},
`,
getCells: () => [
...Object.keys(__fixedExportMap__),
...Object.keys(__liveExportMap__),
...getReexportKeys(__reexportMap__),
],
reexportedCells: reexports.map(importSpecifier => [
index,
indexedImports[importSpecifier],
]),
getReexportsWiring: () => {
const mappings = reexports.map(
importSpecifier => `\
Object.defineProperties(cells[${index}], Object.getOwnPropertyDescriptors(cells[${indexedImports[importSpecifier]}]));
`,
);
const mappings = [];
// Create references for export name as newname
const namedReexportsToProcess = Object.entries(__reexportMap__);
if (namedReexportsToProcess.length > 0) {
Expand All @@ -96,23 +119,14 @@ ${exportsCellRecord(__fixedExportMap__)}${exportsCellRecord(
},
getFunctorCall: () => `\
functors[${index}]({
imports(entries) {
const map = new Map(entries);
${''.concat(
...Object.entries(indexedImports).map(
([importName, importIndex]) => `\
observeImports(map, ${q(importName)}, ${importIndex});
`,
),
)}\
},
${importImplementation(indexedImports)}
liveVar: {
${importsCellSetter(__liveExportMap__, index)}\
},
onceVar: {
${importsCellSetter(__fixedExportMap__, index)}\
},
importMeta: {},
},\
${__needsImportMeta__ ? '\n importMeta: {},' : ''}
});
`,
};
Expand Down
Loading