Skip to content

Commit 8b9e986

Browse files
committed
Try new build
1 parent 869968b commit 8b9e986

File tree

3 files changed

+224
-1
lines changed

3 files changed

+224
-1
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@
115115
"scripts": {
116116
"prepare": "husky && npm run test:install && run-s build",
117117
"build": "npm-run-all --parallel 'build:*'",
118-
"build:core": "microbundle build --raw --no-generateTypes -f cjs,esm,umd",
118+
"build:core": "node scripts/build-core-new.cjs",
119119
"build:debug": "microbundle build --raw --no-generateTypes -f cjs,esm,umd --cwd debug",
120120
"build:devtools": "microbundle build --raw --no-generateTypes -f cjs,esm,umd --cwd devtools",
121121
"build:hooks": "microbundle build --raw --no-generateTypes -f cjs,esm,umd --cwd hooks",

scripts/build-core-new.cjs

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
#!/usr/bin/env node
2+
const { build } = require('esbuild');
3+
const { minify } = require('terser');
4+
const fs = require('node:fs/promises');
5+
const path = require('node:path');
6+
const babel = require('@babel/core');
7+
const zlib = require('node:zlib');
8+
9+
(async () => {
10+
const root = path.join(__dirname, '..');
11+
const distDir = path.join(root, 'dist');
12+
const entry = path.join(root, 'src/index.js');
13+
const pkg = JSON.parse(
14+
String(await fs.readFile(path.join(root, 'package.json')))
15+
);
16+
const mangleConfig = JSON.parse(
17+
String(await fs.readFile(path.join(root, 'mangle.json')))
18+
);
19+
20+
const rename = {};
21+
for (const original in mangleConfig.props.props) {
22+
let name = original;
23+
if (name.startsWith('$')) name = name.slice(1);
24+
rename[name] = mangleConfig.props.props[original];
25+
}
26+
27+
const babelCache = new Map();
28+
function babelRenamePlugin() {
29+
return {
30+
name: 'babel-rename-properties',
31+
setup(buildApi) {
32+
buildApi.onLoad({ filter: /\/src\/.*\.js$/ }, async args => {
33+
const code = String(await fs.readFile(args.path));
34+
const cacheKey = args.path + '::' + code;
35+
const cached = babelCache.get(cacheKey);
36+
if (cached) return cached;
37+
const result = await babel.transformAsync(code, {
38+
filename: args.path,
39+
babelrc: false,
40+
configFile: false,
41+
presets: [
42+
43+
],
44+
plugins: [['babel-plugin-transform-rename-properties', { rename }]]
45+
});
46+
const out = { contents: result.code, loader: 'js' };
47+
babelCache.set(cacheKey, out);
48+
return out;
49+
});
50+
}
51+
};
52+
}
53+
54+
await fs.mkdir(distDir, { recursive: true });
55+
56+
const sharedEsbuildOptions = {
57+
entryPoints: [entry],
58+
bundle: true,
59+
sourcemap: true,
60+
sourcesContent: true,
61+
plugins: [babelRenamePlugin()],
62+
// Use a modern baseline; legacy transforms (let->var) are handled by Babel previously in microbundle.
63+
target: ['es2020'],
64+
define: { 'process.env.NODE_ENV': '"production"' }
65+
};
66+
67+
// Build only ESM first; we'll derive CJS manually to avoid boilerplate helpers esbuild adds.
68+
await build({
69+
...sharedEsbuildOptions,
70+
format: 'esm',
71+
outfile: path.join(distDir, 'preact.mjs'),
72+
minify: false
73+
});
74+
75+
// Simple ESM->CJS transform: replace export statements with module.exports assignments.
76+
// This is intentionally minimal because Preact's entry only uses named exports.
77+
const esmCode = String(await fs.readFile(path.join(distDir, 'preact.mjs')));
78+
let cjsCode = esmCode;
79+
// Strip direct export keywords (we'll re-add explicit exports lines):
80+
cjsCode = cjsCode
81+
.replace(/export function (\w+)/g, 'function $1')
82+
.replace(/export const (\w+)/g, 'const $1')
83+
.replace(/export class (\w+)/g, 'class $1');
84+
85+
const exportLines = [];
86+
const exported = new Set();
87+
cjsCode = cjsCode.replace(/export \{([^}]+)\};?/g, (m, names) => {
88+
const parts = names.split(',');
89+
for (let raw of parts) {
90+
const s = raw.trim();
91+
if (!s) continue;
92+
let local = s;
93+
let alias = s;
94+
if (s.includes(' as ')) {
95+
[local, alias] = s.split(/\s+as\s+/);
96+
}
97+
if (!exported.has(alias)) {
98+
exportLines.push(`exports.${alias} = ${local};`);
99+
exported.add(alias);
100+
}
101+
}
102+
return '';
103+
});
104+
// default export (not currently used, but handle generically):
105+
cjsCode = cjsCode.replace(/export default ([^;]+);?/g, (m, expr) => {
106+
if (!exported.has('default')) {
107+
exported.add('default');
108+
return `module.exports = ${expr};`;
109+
}
110+
return '';
111+
});
112+
if (exportLines.length) cjsCode += '\n' + exportLines.join('\n') + '\n';
113+
await fs.writeFile(path.join(distDir, 'preact.js'), cjsCode);
114+
115+
await build({
116+
...sharedEsbuildOptions,
117+
format: 'iife',
118+
globalName: pkg.amdName || 'preact',
119+
outfile: path.join(distDir, 'preact.umd.js'),
120+
minify: false
121+
});
122+
123+
const reserved = mangleConfig.minify.mangle.properties.reserved;
124+
const additionalProps = mangleConfig.props.props;
125+
Object.values(additionalProps).forEach(name => {
126+
reserved.push(name);
127+
});
128+
// Build a terser base config that more closely mirrors microbundle's setup.
129+
// NOTE: Previously we accidentally overwrote property mangle settings by spreading a non-existent
130+
// mangleConfig.mangle root key. That disabled property mangling (regex /^_[^_]/), inflating size.
131+
// Here we explicitly wire those settings back in and enable toplevel optimizations.
132+
const terserBase = {
133+
// Keep only the needed pieces from mangleConfig.minify (avoid copying its nested objects blindly):
134+
compress: {
135+
...mangleConfig.minify.compress,
136+
// Extra safe-ish optimizations used historically in Preact's build (align with microbundle defaults):
137+
unsafe: true,
138+
pure_getters: true,
139+
keep_infinity: true,
140+
unsafe_proto: true,
141+
passes: 3, // microbundle uses a low pass count; higher passes can sometimes increase size slightly
142+
toplevel: true
143+
},
144+
mangle: {
145+
toplevel: true,
146+
properties: {
147+
...mangleConfig.minify.mangle.properties,
148+
reserved
149+
}
150+
},
151+
format: {
152+
shebang: true,
153+
shorthand: true,
154+
wrap_func_args: false,
155+
comments: /^\s*([@#]__[A-Z]+__\s*$|@cc_on)/,
156+
preserve_annotations: true
157+
},
158+
module: true,
159+
sourceMap: true
160+
};
161+
async function minifyFile(inputPath, outputPath, { module }) {
162+
const code = String(await fs.readFile(inputPath));
163+
const result = await minify(code, {
164+
...terserBase,
165+
module,
166+
sourceMap: {
167+
filename: path.basename(outputPath),
168+
url: path.basename(outputPath) + '.map'
169+
}
170+
});
171+
await fs.writeFile(outputPath, result.code + '\n');
172+
if (result.map)
173+
await fs.writeFile(
174+
outputPath + '.map',
175+
typeof result.map === 'string' ? result.map : JSON.stringify(result.map)
176+
);
177+
}
178+
179+
await minifyFile(
180+
path.join(distDir, 'preact.js'),
181+
path.join(distDir, 'preact.js'),
182+
{ module: true }
183+
);
184+
await minifyFile(
185+
path.join(distDir, 'preact.mjs'),
186+
path.join(distDir, 'preact.mjs'),
187+
{ module: true }
188+
);
189+
await minifyFile(
190+
path.join(distDir, 'preact.umd.js'),
191+
path.join(distDir, 'preact.umd.js'),
192+
{ module: false }
193+
);
194+
195+
async function sizeOf(p) {
196+
try {
197+
return (await fs.stat(p)).size;
198+
} catch {
199+
return 0;
200+
}
201+
}
202+
const rawFiles = ['preact.js', 'preact.mjs', 'preact.umd.js'];
203+
console.log('\n[build:core:new] Generated files (raw | gzip | brotli):');
204+
for (const f of rawFiles) {
205+
const abs = path.join(distDir, f);
206+
const size = await sizeOf(abs);
207+
let line = f.padEnd(24) + String(size).padStart(8) + ' bytes';
208+
const code = await fs.readFile(abs);
209+
const gz = zlib.gzipSync(code);
210+
const br = zlib.brotliCompressSync(code, {
211+
params: { [zlib.constants.BROTLI_PARAM_QUALITY]: 11 }
212+
});
213+
line +=
214+
' | ' +
215+
String(gz.length).padStart(2) +
216+
' | ' +
217+
String(br.length).padStart(2);
218+
console.log(line);
219+
}
220+
console.log('\nDone.');
221+
})();

src/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,5 @@ export { cloneElement } from './clone-element';
1111
export { createContext } from './create-context';
1212
export { toChildArray } from './diff/children';
1313
export { default as options } from './options';
14+
15+
// test

0 commit comments

Comments
 (0)