-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Expand file tree
/
Copy pathremote.js
More file actions
110 lines (90 loc) · 3.09 KB
/
remote.js
File metadata and controls
110 lines (90 loc) · 3.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/** @import { ServerMetadata } from 'types' */
/** @import { OutputBundle } from 'rollup' */
import fs from 'node:fs';
import path from 'node:path';
import { Parser } from 'acorn';
import MagicString from 'magic-string';
import { posixify } from '../../../utils/filesystem.js';
import { import_peer } from '../../../utils/import.js';
import { s } from '../../../utils/misc.js';
/**
* @param {string} out
* @param {Array<{ hash: string, file: string }>} remotes
* @param {ServerMetadata} metadata
* @param {string} cwd
* @param {OutputBundle} server_bundle
*/
export async function treeshake_prerendered_remotes(out, remotes, metadata, cwd, server_bundle) {
if (remotes.length === 0) return;
const vite = /** @type {typeof import('vite')} */ (await import_peer('vite'));
for (const remote of remotes) {
const exports_map = metadata.remotes.get(remote.hash);
if (!exports_map) continue;
/** @type {string[]} */
const dynamic = [];
/** @type {string[]} */
const prerendered = [];
for (const [name, value] of exports_map) {
(value.dynamic ? dynamic : prerendered).push(name);
}
if (prerendered.length === 0) continue; // nothing to treeshake
// remove file extension
const remote_filename = path.basename(remote.file).split('.').slice(0, -1).join('.');
const remote_chunk = Object.values(server_bundle).find((chunk) => {
return chunk.name === remote_filename;
});
if (!remote_chunk) return;
const chunk_path = posixify(path.relative(cwd, `${out}/server/${remote_chunk.fileName}`));
const code = fs.readFileSync(chunk_path, 'utf-8');
const parsed = Parser.parse(code, { sourceType: 'module', ecmaVersion: 'latest' });
const modified_code = new MagicString(code);
for (const fn of prerendered) {
for (const node of parsed.body) {
const declaration =
node.type === 'ExportNamedDeclaration'
? node.declaration
: node.type === 'VariableDeclaration'
? node
: null;
if (!declaration || declaration.type !== 'VariableDeclaration') continue;
for (const declarator of declaration.declarations) {
if (declarator.id.type === 'Identifier' && declarator.id.name === fn) {
modified_code.overwrite(
node.start,
node.end,
`const ${fn} = { __: { type: 'prerender', id: ${s(`${remote.hash}/${fn}`)}, name: ${s(fn)} } }`
);
}
}
}
}
for (const node of parsed.body) {
if (node.type === 'ExportDefaultDeclaration') {
modified_code.remove(node.start, node.end);
}
}
const stubbed = modified_code.toString();
fs.writeFileSync(chunk_path, stubbed);
const bundle = /** @type {import('vite').Rollup.RollupOutput} */ (
await vite.build({
configFile: false,
build: {
write: false,
ssr: true,
target: 'esnext',
rollupOptions: {
// treat everything as external
external: (id) => !id.endsWith(chunk_path),
input: {
treeshaken: chunk_path
}
}
}
})
);
const chunk = bundle.output.find((c) => c.type === 'chunk' && c.name === 'treeshaken');
if (chunk && chunk.type === 'chunk') {
fs.writeFileSync(chunk_path, chunk.code);
}
}
}