Skip to content

Commit b054d66

Browse files
kumavisnaugtur
andcommittedDec 16, 2022
refactor(compartment-mapper): refactor bundler to produce less output
Co-authored-by: naugtur <[email protected]> Co-authered-by: kumavis <[email protected]>
1 parent 3a496da commit b054d66

File tree

8 files changed

+376
-241
lines changed

8 files changed

+376
-241
lines changed
 

‎packages/compartment-mapper/scripts/bundle-live-test.js

+7
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ const readPowers = makeReadPowers({ fs, url });
1414

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

19+
console.log(`
20+
#######################
21+
size: ${bundle.length}
22+
`);
23+
24+
// eslint-disable-next-line no-undef
1825
global.print = console.log;
1926
import(target);

‎packages/compartment-mapper/src/bundle-cjs.js

+9-21
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,7 @@
1-
/** quotes strings */
2-
const q = JSON.stringify;
3-
4-
const exportsCellRecord = exportsList =>
5-
''.concat(
6-
...exportsList.map(
7-
exportName => `\
8-
${exportName}: cell(${q(exportName)}),
9-
`,
10-
),
11-
);
12-
1+
// vvv runtime to inline in the bundle vvv
2+
/* eslint-disable no-undef */
133
// This function is serialized and references variables from its destination scope.
14-
const runtime = function wrapCjsFunctor(num) {
15-
/* eslint-disable no-undef */
4+
function wrapCjsFunctor(num) {
165
return ({ imports = {} }) => {
176
const cModule = Object.freeze(
187
Object.defineProperty({}, 'exports', cells[num].default),
@@ -24,8 +13,11 @@ const runtime = function wrapCjsFunctor(num) {
2413
.filter(k => k !== 'default' && k !== '*')
2514
.map(k => cells[num][k].set(cModule.exports[k]));
2615
};
27-
/* eslint-enable no-undef */
28-
}.toString();
16+
}
17+
/* eslint-enable no-undef */
18+
const runtime = `\
19+
${wrapCjsFunctor}`;
20+
// ^^^ runtime to inline in the bundle ^^^
2921

3022
export default {
3123
runtime,
@@ -41,11 +33,7 @@ export default {
4133
// === functors[${index}] ===
4234
${cjsFunctor},
4335
`,
44-
getCells: () => `\
45-
{
46-
${exportsCellRecord(exportsList)}\
47-
},
48-
`,
36+
getCells: () => exportsList,
4937
getReexportsWiring: () => '',
5038
getFunctorCall: () => `\
5139
wrapCjsFunctor(${index})({imports: ${importsMap}});

‎packages/compartment-mapper/src/bundle-json.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@ export default {
66
// === functors[${index}] ===
77
${sourceText},
88
`,
9-
getCells: () => `\
10-
{ default: cell('default') },
11-
`,
9+
getCells: () => ['default'],
1210
getReexportsWiring: () => '',
1311
getFunctorCall: () => `\
1412
cells[${index}].default.set(functors[${index}]);

‎packages/compartment-mapper/src/bundle-mjs.js

+56-50
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,6 @@
11
/** quotes strings */
22
const q = JSON.stringify;
33

4-
const exportsCellRecord = exportMap =>
5-
''.concat(
6-
...Object.keys(exportMap).map(
7-
exportName => `\
8-
${exportName}: cell(${q(exportName)}),
9-
`,
10-
),
11-
);
12-
134
const importsCellSetter = (exportMap, index) =>
145
''.concat(
156
...Object.entries(exportMap).map(
@@ -19,31 +10,56 @@ const importsCellSetter = (exportMap, index) =>
1910
),
2011
);
2112

22-
const adaptReexport = reexportMap => {
13+
const importImplementation = indexedImports => {
14+
const knownEntries = Object.entries(indexedImports);
15+
if (knownEntries.length === 0) {
16+
return `imports() {},`;
17+
}
18+
return `\
19+
imports(entries) {
20+
const map = new Map(entries);
21+
observeImports(map, [
22+
${''.concat(
23+
...knownEntries.map(
24+
([importName, importIndex]) => `\
25+
[${q(importName)}, ${importIndex}],
26+
`,
27+
),
28+
)}\
29+
]);},`;
30+
};
31+
32+
const getReexportKeys = reexportMap => {
2333
if (!reexportMap) {
2434
return {};
2535
}
26-
const ret = Object.fromEntries(
27-
Object.values(reexportMap)
28-
.flat()
29-
.map(([local, exported]) => [exported, [local]]),
30-
);
31-
return ret;
36+
return Object.values(reexportMap)
37+
.flat()
38+
.map(([local, _exported]) => local);
3239
};
3340

34-
export const runtime = `\
35-
function observeImports(map, importName, importIndex) {
36-
for (const [name, observers] of map.get(importName)) {
37-
const cell = cells[importIndex][name];
38-
if (cell === undefined) {
39-
throw new ReferenceError(\`Cannot import name \${name}\`);
40-
}
41-
for (const observer of observers) {
42-
cell.observe(observer);
41+
// vvv runtime to inline in the bundle vvv
42+
/* eslint-disable no-undef */
43+
function observeImports(map, pairs) {
44+
for (const [importName, importIndex] of pairs) {
45+
for (const [name, observers] of map.get(importName)) {
46+
const cell = cells[importIndex][name];
47+
if (cell === undefined) {
48+
throw new ReferenceError(`Cannot import name ${name}`);
49+
}
50+
for (const observer of observers) {
51+
cell.observe(observer);
52+
}
4353
}
4454
}
4555
}
46-
`;
56+
57+
/* eslint-enable no-undef */
58+
59+
const runtime = `\
60+
${observeImports}
61+
${observeImports}`;
62+
// ^^^ runtime to inline in the bundle ^^^
4763

4864
export default {
4965
runtime,
@@ -55,6 +71,7 @@ export default {
5571
__fixedExportMap__ = {},
5672
__liveExportMap__ = {},
5773
__reexportMap__ = {},
74+
__needsImportMeta__ = false,
5875
reexports,
5976
},
6077
}) {
@@ -63,19 +80,17 @@ export default {
6380
// === functors[${index}] ===
6481
${__syncModuleProgram__},
6582
`,
66-
getCells: () => `\
67-
{
68-
${exportsCellRecord(__fixedExportMap__)}${exportsCellRecord(
69-
__liveExportMap__,
70-
)}${exportsCellRecord(adaptReexport(__reexportMap__))}\
71-
},
72-
`,
83+
getCells: () => [
84+
...Object.keys(__fixedExportMap__),
85+
...Object.keys(__liveExportMap__),
86+
...getReexportKeys(__reexportMap__),
87+
],
88+
reexportedCells: reexports.map(importSpecifier => [
89+
index,
90+
indexedImports[importSpecifier],
91+
]),
7392
getReexportsWiring: () => {
74-
const mappings = reexports.map(
75-
importSpecifier => `\
76-
Object.defineProperties(cells[${index}], Object.getOwnPropertyDescriptors(cells[${indexedImports[importSpecifier]}]));
77-
`,
78-
);
93+
const mappings = [];
7994
// Create references for export name as newname
8095
const namedReexportsToProcess = Object.entries(__reexportMap__);
8196
if (namedReexportsToProcess.length > 0) {
@@ -96,23 +111,14 @@ ${exportsCellRecord(__fixedExportMap__)}${exportsCellRecord(
96111
},
97112
getFunctorCall: () => `\
98113
functors[${index}]({
99-
imports(entries) {
100-
const map = new Map(entries);
101-
${''.concat(
102-
...Object.entries(indexedImports).map(
103-
([importName, importIndex]) => `\
104-
observeImports(map, ${q(importName)}, ${importIndex});
105-
`,
106-
),
107-
)}\
108-
},
114+
${importImplementation(indexedImports)}
109115
liveVar: {
110116
${importsCellSetter(__liveExportMap__, index)}\
111117
},
112118
onceVar: {
113119
${importsCellSetter(__fixedExportMap__, index)}\
114-
},
115-
importMeta: {},
120+
},\
121+
${__needsImportMeta__ ? '\n importMeta: {},' : ''}
116122
});
117123
`,
118124
};

‎packages/compartment-mapper/src/bundle.js

+54-25
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,49 @@ function getBundlerKitForModule(module) {
149149
return getBundlerKit(module);
150150
}
151151

152+
// vvv runtime to inline in the bundle vvv
153+
/* eslint-disable no-undef */
154+
function cell(name, value = undefined) {
155+
const observers = [];
156+
return Object.freeze({
157+
get: Object.freeze(() => {
158+
return value;
159+
}),
160+
set: Object.freeze(newValue => {
161+
value = newValue;
162+
for (const observe of observers) {
163+
observe(value);
164+
}
165+
}),
166+
observe: Object.freeze(observe => {
167+
observers.push(observe);
168+
observe(value);
169+
}),
170+
enumerable: true,
171+
});
172+
}
173+
function makeCells(orderedCells, reexports) {
174+
const cells = orderedCells.map(cs =>
175+
cs.reduce((kv, c) => {
176+
kv[c] = cell(c);
177+
return kv;
178+
}, {}),
179+
);
180+
for (const [to, from] of reexports) {
181+
Object.defineProperties(
182+
cells[to],
183+
Object.getOwnPropertyDescriptors(cells[from]),
184+
);
185+
}
186+
return cells;
187+
}
188+
/* eslint-enable no-undef */
189+
const runtime = `\
190+
${cell}
191+
${makeCells}`;
192+
193+
// ^^^ runtime to inline in the bundle ^^^
194+
152195
/**
153196
* @param {ReadFn} read
154197
* @param {string} moduleLocation
@@ -251,29 +294,17 @@ export const makeBundle = async (read, moduleLocation, options) => {
251294
${''.concat(...modules.map(m => m.bundlerKit.getFunctor()))}\
252295
]; // functors end
253296
254-
const cell = (name, value = undefined) => {
255-
const observers = [];
256-
return Object.freeze({
257-
get: Object.freeze(() => {
258-
return value;
259-
}),
260-
set: Object.freeze((newValue) => {
261-
value = newValue;
262-
for (const observe of observers) {
263-
observe(value);
264-
}
265-
}),
266-
observe: Object.freeze((observe) => {
267-
observers.push(observe);
268-
observe(value);
269-
}),
270-
enumerable: true,
271-
});
272-
};
273-
274-
const cells = [
275-
${''.concat(...modules.map(m => m.bundlerKit.getCells()))}\
276-
];
297+
//runtime
298+
${''.concat(
299+
runtime,
300+
...Array.from(parsersInUse).map(parser => getRuntime(parser)),
301+
)}
302+
// runtime end
303+
const cells = makeCells(
304+
${JSON.stringify([...modules.map(m => m.bundlerKit.getCells())], null, 2)},
305+
/* export * from */
306+
${JSON.stringify(modules.flatMap(m => m.bundlerKit.reexportedCells || []))}
307+
);
277308
278309
${''.concat(...modules.map(m => m.bundlerKit.getReexportsWiring()))}\
279310
@@ -283,8 +314,6 @@ ${''.concat(...modules.map(m => m.bundlerKit.getReexportsWiring()))}\
283314
cells[index]['*'] = cell('*', namespaces[index]);
284315
}
285316
286-
${''.concat(...Array.from(parsersInUse).map(parser => getRuntime(parser)))}
287-
288317
${''.concat(...modules.map(m => m.bundlerKit.getFunctorCall()))}\
289318
290319
return cells[cells.length - 1]['*'].get();

‎packages/compartment-mapper/test/snapshots/test-bundle.js.md

+146-142
Large diffs are not rendered by default.
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import test from 'ava';
2+
3+
import parserJson from '../src/parse-json.js';
4+
// import parserText from '../src/parse-text.js';
5+
// import parserBytes from '../src/parse-bytes.js';
6+
import parserArchiveCjs from '../src/parse-archive-cjs.js';
7+
import parserArchiveMjs from '../src/parse-archive-mjs.js';
8+
9+
import mjsSupport from '../src/bundle-mjs.js';
10+
import cjsSupport from '../src/bundle-cjs.js';
11+
import jsonSupport from '../src/bundle-json.js';
12+
13+
const textEncoder = new TextEncoder();
14+
15+
const tests = [
16+
{
17+
name: 'pre-mjs-json',
18+
parser: parserArchiveMjs,
19+
bundlerKit: mjsSupport,
20+
assertions: (t, kit) => {
21+
t.deepEqual(kit.getCells(), ['default', 'a', 'x']);
22+
t.deepEqual(kit.reexportedCells, [
23+
['<index for self>', '<index for: ./b>'],
24+
]);
25+
t.is(
26+
kit.getReexportsWiring().trim(),
27+
'Object.defineProperties(cells[<index for self>], {"z": { value: cells[<index for: x>]["x"] } });',
28+
);
29+
},
30+
sample: `
31+
import rea from './a';
32+
export * from './b';
33+
export { x as z } from 'x';
34+
export const a = 1;
35+
export default a;
36+
import.meta.uttered
37+
`,
38+
},
39+
{
40+
name: 'pre-cjs-json',
41+
parser: parserArchiveCjs,
42+
bundlerKit: cjsSupport,
43+
assertions: (t, kit) => {
44+
t.deepEqual(kit.getCells(), ['a', 'b', 'default']);
45+
t.is(kit.reexportedCells, undefined);
46+
},
47+
sample: `
48+
const a = require('a');
49+
const b = 1
50+
module.exports = { a, b };
51+
`,
52+
},
53+
{
54+
name: 'json',
55+
parser: parserJson,
56+
bundlerKit: jsonSupport,
57+
assertions: (t, kit) => {
58+
t.deepEqual(kit.getCells(), ['default']);
59+
t.is(kit.reexportedCells, undefined);
60+
},
61+
sample: `
62+
{ "name": "foo" }
63+
`,
64+
},
65+
];
66+
67+
tests.forEach(({ name, parser, bundlerKit, sample, assertions }) => {
68+
test(`bundler kit / ${name}`, async t => {
69+
const module = await parser.parse(
70+
textEncoder.encode(sample),
71+
'_specifier',
72+
'_location',
73+
'_packageLocation',
74+
);
75+
76+
const { imports = [], reexports = [] } = module.record;
77+
const kit = bundlerKit.getBundlerKit({
78+
index: '<index for self>',
79+
indexedImports: Object.fromEntries(
80+
[...imports, ...reexports].map(importSpecifier => [
81+
importSpecifier,
82+
`<index for: ${importSpecifier}>`,
83+
]),
84+
),
85+
record: module.record,
86+
});
87+
88+
assertions(t, kit);
89+
90+
// all kits need to pass these:
91+
t.is(typeof bundlerKit.runtime, 'string');
92+
93+
t.is(typeof kit.getFunctor, 'function');
94+
t.is(typeof kit.getCells, 'function');
95+
t.is(typeof kit.getReexportsWiring, 'function');
96+
t.is(typeof kit.getFunctorCall, 'function');
97+
98+
t.is(typeof kit.getFunctor(), 'string');
99+
t.true(Array.isArray(kit.getCells()));
100+
t.is(typeof kit.getReexportsWiring(), 'string');
101+
t.is(typeof kit.getFunctorCall(), 'string');
102+
});
103+
});

0 commit comments

Comments
 (0)
Please sign in to comment.