Skip to content

Commit 5c8b629

Browse files
committed
Simplify build process
1 parent 9b78b44 commit 5c8b629

File tree

3 files changed

+263
-1
lines changed

3 files changed

+263
-1
lines changed

build.mjs

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
#!/usr/bin/env node
2+
3+
import * as esbuild from 'esbuild';
4+
import { transformAsync } from '@babel/core';
5+
import { minify } from 'terser';
6+
import fs from 'node:fs/promises';
7+
import { readFileSync, existsSync } from 'node:fs';
8+
import path from 'node:path';
9+
10+
// Load mangle configuration
11+
const mangleConfig = JSON.parse(readFileSync('./mangle.json', 'utf8'));
12+
13+
// Create rename mapping for Babel plugin
14+
const rename = {};
15+
for (let prop in mangleConfig.props.props) {
16+
let name = prop;
17+
if (name[0] === '$') {
18+
name = name.slice(1);
19+
}
20+
rename[name] = mangleConfig.props.props[prop];
21+
}
22+
23+
// Build configurations for different packages
24+
const buildConfigs = [
25+
{
26+
name: 'preact',
27+
entry: 'src/index.js',
28+
outDir: 'dist',
29+
filename: 'preact',
30+
globalName: 'preact'
31+
},
32+
{
33+
name: 'preact/debug',
34+
entry: 'debug/src/index.js',
35+
outDir: 'debug/dist',
36+
external: ['preact'],
37+
filename: 'debug',
38+
globalName: 'preactDebug'
39+
},
40+
{
41+
name: 'preact/devtools',
42+
entry: 'devtools/src/index.js',
43+
outDir: 'devtools/dist',
44+
external: ['preact'],
45+
filename: 'devtools',
46+
globalName: 'preactDevtools'
47+
},
48+
{
49+
name: 'preact/hooks',
50+
entry: 'hooks/src/index.js',
51+
outDir: 'hooks/dist',
52+
external: ['preact'],
53+
filename: 'hooks',
54+
globalName: 'preactHooks'
55+
},
56+
{
57+
name: 'preact/test-utils',
58+
entry: 'test-utils/src/index.js',
59+
external: ['preact'],
60+
outDir: 'test-utils/dist',
61+
filename: 'testUtils',
62+
globalName: 'preactTestUtils'
63+
},
64+
{
65+
name: 'preact/compat',
66+
entry: 'compat/src/index.js',
67+
outDir: 'compat/dist',
68+
filename: 'compat',
69+
globalName: 'preactCompat',
70+
external: ['preact/hooks', 'preact'],
71+
globals: { 'preact/hooks': 'preactHooks' }
72+
},
73+
{
74+
name: 'preact/jsx-runtime',
75+
entry: 'jsx-runtime/src/index.js',
76+
outDir: 'jsx-runtime/dist',
77+
filename: 'jsxRuntime',
78+
external: ['preact'],
79+
globalName: 'preactJsxRuntime'
80+
}
81+
];
82+
83+
// Formats to build for each package
84+
const formats = ['cjs', 'esm', 'umd'];
85+
86+
// Function to apply Babel transformations
87+
async function applyBabelTransform(code, filename) {
88+
try {
89+
const result = await transformAsync(code, {
90+
filename,
91+
configFile: false,
92+
babelrc: false,
93+
presets: [],
94+
plugins: [['babel-plugin-transform-rename-properties', { rename }]]
95+
});
96+
return result.code;
97+
} catch (error) {
98+
console.error(`Babel transform failed for ${filename}:`, error);
99+
throw error;
100+
}
101+
}
102+
103+
// Function to minify with Terser
104+
async function minifyWithTerser(code, filename) {
105+
try {
106+
const result = await minify(code, {
107+
...mangleConfig.minify,
108+
compress: {
109+
keep_infinity: true,
110+
pure_getters: true,
111+
passes: 10
112+
},
113+
format: {
114+
wrap_func_args: false,
115+
comments: /^\s*([@#]__[A-Z]+__\s*$|@cc_on)/,
116+
preserve_annotations: true
117+
},
118+
ecma: 2017,
119+
sourceMap: true,
120+
safari10: true,
121+
module: filename.endsWith('.mjs'),
122+
toplevel: true
123+
});
124+
return result;
125+
} catch (error) {
126+
console.error(`Terser minification failed for ${filename}:`, error);
127+
throw error;
128+
}
129+
}
130+
131+
// Function to build a single package in a specific format
132+
async function buildPackage(config, format) {
133+
const {
134+
name,
135+
entry,
136+
outDir,
137+
filename,
138+
globalName,
139+
external = [],
140+
globals = {}
141+
} = config;
142+
143+
if (!existsSync(entry)) {
144+
console.warn(`⚠️ Entry file ${entry} not found, skipping ${name}`);
145+
return;
146+
}
147+
148+
console.log(`📦 Building ${name} (${format})...`);
149+
150+
// Determine output extension and format
151+
const extensions = {
152+
cjs: '.js',
153+
esm: '.mjs',
154+
umd: '.umd.js'
155+
};
156+
157+
const outputFile = path.join(outDir, `${filename}${extensions[format]}`);
158+
const mapFile = path.join(outDir, `${filename}.map${extensions[format]}`);
159+
160+
// ESBuild configuration
161+
const esbuildConfig = /** @type {import('esbuild').BuildOptions} */ {
162+
entryPoints: [entry],
163+
bundle: true,
164+
format: format === 'cjs' ? 'cjs' : format === 'esm' ? 'esm' : 'iife',
165+
platform: 'browser',
166+
target: ['es2015'],
167+
jsx: 'transform',
168+
jsxFactory: 'h',
169+
jsxFragment: 'Fragment',
170+
external: external,
171+
sourcemap: true,
172+
globalName: format === 'umd' ? globalName : undefined,
173+
plugins: [],
174+
write: false, // We'll handle writing ourselves
175+
minify: false, // We'll handle minification with Terser
176+
treeShaking: true
177+
};
178+
179+
// Add globals for UMD builds
180+
if (format === 'umd' && Object.keys(globals).length > 0) {
181+
esbuildConfig.globalName = globalName;
182+
// ESBuild doesn't support globals directly, we'll need to handle externals differently
183+
}
184+
185+
try {
186+
// Build with ESBuild
187+
// @ts-expect-error
188+
const result = await esbuild.build(esbuildConfig);
189+
let code = result.outputFiles[0].text;
190+
191+
// Apply Babel transformations
192+
code = await applyBabelTransform(code, outputFile);
193+
194+
// Ensure output directory exists
195+
await fs.mkdir(path.dirname(outputFile), { recursive: true });
196+
197+
// Write unminified version
198+
const minified = await minifyWithTerser(code, outputFile);
199+
await fs.writeFile(outputFile, minified.code);
200+
if (typeof minified.map === 'string')
201+
await fs.writeFile(mapFile, minified.map);
202+
console.log(`✅ Built ${outputFile}`);
203+
204+
// Create minified version for UMD builds
205+
if (format === 'umd') {
206+
const minifiedCode = await minifyWithTerser(code, outputFile);
207+
const minifiedFile = path.join(outDir, `${filename}.min.js`);
208+
await fs.writeFile(minifiedFile, minifiedCode.code);
209+
console.log(`✅ Minified ${minifiedFile}`);
210+
}
211+
} catch (error) {
212+
console.error(`❌ Failed to build ${name} (${format}):`, error);
213+
throw error;
214+
}
215+
}
216+
217+
// Main build function
218+
async function build() {
219+
console.log('🚀 Starting Preact build with ESBuild + Babel + Terser...\n');
220+
221+
const startTime = Date.now();
222+
let successful = 0;
223+
let failed = 0;
224+
225+
// Build each package in each format
226+
for (const config of buildConfigs) {
227+
for (const format of formats) {
228+
try {
229+
await buildPackage(config, format);
230+
successful++;
231+
} catch (error) {
232+
console.error(`❌ Build failed for ${config.name} (${format})`);
233+
failed++;
234+
}
235+
}
236+
console.log(); // Empty line between packages
237+
}
238+
239+
const endTime = Date.now();
240+
const duration = ((endTime - startTime) / 1000).toFixed(2);
241+
242+
console.log(`\n🎉 Build completed in ${duration}s`);
243+
console.log(`✅ Successful: ${successful}`);
244+
if (failed > 0) {
245+
console.log(`❌ Failed: ${failed}`);
246+
process.exit(1);
247+
}
248+
}
249+
250+
// Run the build if this script is executed directly
251+
// @ts-expect-error
252+
if (import.meta.url === `file://${process.argv[1]}`) {
253+
build().catch(error => {
254+
console.error('❌ Build failed:', error);
255+
process.exit(1);
256+
});
257+
}
258+
259+
export { build, buildPackage };

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,8 @@
121121
"types": "src/index.d.ts",
122122
"scripts": {
123123
"prepare": "husky && run-s build",
124-
"build": "npm-run-all --parallel 'build:*'",
124+
"build": "node build.mjs && node ./config/compat-entries.js",
125+
"build:microbundle": "npm-run-all --parallel 'build:*'",
125126
"build:core": "microbundle build --raw --no-generateTypes -f cjs,esm,umd",
126127
"build:debug": "microbundle build --raw --no-generateTypes -f cjs,esm,umd --cwd debug",
127128
"build:devtools": "microbundle build --raw --no-generateTypes -f cjs,esm,umd --cwd devtools",

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+
// Build

0 commit comments

Comments
 (0)