|
1 | | -import { $ } from 'bun' |
2 | 1 | import { createHash } from 'node:crypto' |
3 | 2 | import { mkdir, readFile, writeFile } from 'node:fs/promises' |
4 | 3 | import * as path from 'node:path' |
5 | 4 | import { fileURLToPath } from 'node:url' |
6 | 5 |
|
7 | 6 | const __dirname = fileURLToPath(new URL('.', import.meta.url)) |
8 | 7 |
|
9 | | -async function buildForPlatform(triple: string, outfile: string) { |
10 | | - // We wrap this in a retry because occasionally the atomic rename fails for some reason |
11 | | - for (let i = 0; i < 5; ++i) { |
12 | | - try { |
13 | | - let cmd = $`bun build --compile --target=${triple} ./src/index.ts --outfile=${outfile} --env inline` |
14 | | - |
15 | | - // This env var is used by our patched versions of Lightning CSS and Parcel Watcher to |
16 | | - // statically bundle the proper binaries for musl vs glibc |
17 | | - cmd = cmd.env({ |
18 | | - PLATFORM_LIBC: triple.includes('-musl') ? 'musl' : 'glibc', |
19 | | - |
20 | | - // Workaround for Bun binary downloads failing on Windows CI when |
21 | | - // USERPROFILE is passed through by Turborepo. |
22 | | - USERPROFILE: '', |
23 | | - }) |
24 | | - |
25 | | - return await cmd |
26 | | - } catch (err) { |
27 | | - if (i < 5) continue |
28 | | - |
29 | | - throw new Error(`Failed to build for platform ${triple}`, { cause: err }) |
30 | | - } |
31 | | - } |
| 8 | +// Workaround for Bun binary downloads failing on Windows CI when |
| 9 | +// USERPROFILE is passed through by Turborepo. |
| 10 | +// |
| 11 | +// Unfortunately, setting this at runtime doesn't appear to work so we have to |
| 12 | +// spawn a new process without the env var. |
| 13 | +if (process.env.NESTED_BUILD !== '1' && process.env.USERPROFILE && process.env.USERPROFILE !== '') { |
| 14 | + let result = await Bun.$`bun ${fileURLToPath(import.meta.url)}`.env({ |
| 15 | + USERPROFILE: '', |
| 16 | + NESTED_BUILD: '1', |
| 17 | + }) |
| 18 | + |
| 19 | + process.exit(result.exitCode) |
32 | 20 | } |
33 | 21 |
|
34 | | -async function build(triple: string, file: string) { |
35 | | - let start = process.hrtime.bigint() |
36 | | - |
37 | | - let outfile = path.resolve(__dirname, `../dist/${file}`) |
38 | | - |
39 | | - await buildForPlatform(triple, outfile) |
40 | | - |
41 | | - await new Promise((resolve) => setTimeout(resolve, 100)) |
| 22 | +// We use baseline builds for all x64 platforms to ensure compatibility with |
| 23 | +// older hardware. |
| 24 | +let builds: { target: Bun.Build.Target; name: string }[] = [ |
| 25 | + { name: 'tailwindcss-linux-arm64', target: 'bun-linux-arm64' }, |
| 26 | + { name: 'tailwindcss-linux-arm64-musl', target: 'bun-linux-arm64-musl' }, |
| 27 | + // @ts-expect-error: Either the types are wrong or the runtime needs to be updated |
| 28 | + // to accept a `-glibc` at the end like the types suggest. |
| 29 | + { name: 'tailwindcss-linux-x64', target: 'bun-linux-x64-baseline' }, |
| 30 | + { name: 'tailwindcss-linux-x64-musl', target: 'bun-linux-x64-baseline-musl' }, |
| 31 | + { name: 'tailwindcss-macos-arm64', target: 'bun-darwin-arm64' }, |
| 32 | + { name: 'tailwindcss-macos-x64', target: 'bun-darwin-x64-baseline' }, |
| 33 | + { name: 'tailwindcss-windows-x64.exe', target: 'bun-windows-x64-baseline' }, |
| 34 | +] |
| 35 | + |
| 36 | +let summary: { target: Bun.Build.Target; name: string; sum: string }[] = [] |
| 37 | + |
| 38 | +// Build platform binaries and checksum them. |
| 39 | +let start = process.hrtime.bigint() |
| 40 | +for (let { target, name } of builds) { |
| 41 | + let outfile = path.resolve(__dirname, `../dist/${name}`) |
| 42 | + |
| 43 | + let result = await Bun.build({ |
| 44 | + entrypoints: ['./src/index.ts'], |
| 45 | + target: 'node', |
| 46 | + minify: { |
| 47 | + whitespace: false, |
| 48 | + syntax: true, |
| 49 | + identifiers: false, |
| 50 | + keepNames: true, |
| 51 | + }, |
| 52 | + |
| 53 | + define: { |
| 54 | + // This ensures only necessary binaries are bundled for linux targets |
| 55 | + // It reduces binary size since no runtime selection is necessary |
| 56 | + 'process.env.PLATFORM_LIBC': JSON.stringify(target.includes('-musl') ? 'musl' : 'glibc'), |
| 57 | + |
| 58 | + // This prevents the WASI build from being bundled with the binary |
| 59 | + 'process.env.NAPI_RS_FORCE_WASI': JSON.stringify(''), |
| 60 | + |
| 61 | + // This simplifies the Oxide loading code a small amount |
| 62 | + 'process.env.NAPI_RS_NATIVE_LIBRARY_PATH': JSON.stringify(''), |
| 63 | + |
| 64 | + // No need to support additional NODE_PATHs in the standalone build |
| 65 | + 'process.env.NODE_PATH': JSON.stringify(''), |
| 66 | + }, |
| 67 | + |
| 68 | + compile: { |
| 69 | + target, |
| 70 | + outfile, |
| 71 | + |
| 72 | + // Disable .env loading |
| 73 | + autoloadDotenv: false, |
| 74 | + |
| 75 | + // Disable bunfig.toml loading |
| 76 | + autoloadBunfig: false, |
| 77 | + }, |
| 78 | + |
| 79 | + plugins: [ |
| 80 | + { |
| 81 | + name: 'tailwindcss-plugin', |
| 82 | + setup(build) { |
| 83 | + build.onLoad({ filter: /tailwindcss-oxide\.wasi\.cjs$/ }, async (args) => { |
| 84 | + return { contents: '' } |
| 85 | + }) |
| 86 | + }, |
| 87 | + }, |
| 88 | + ], |
| 89 | + }) |
| 90 | + |
| 91 | + let entry = result.outputs.find((output) => output.kind === 'entry-point') |
| 92 | + if (!entry) throw new Error(`Build failed for ${target}`) |
42 | 93 |
|
43 | 94 | let content = await readFile(outfile) |
44 | | - let sum = createHash('sha256').update(content).digest('hex') |
45 | 95 |
|
46 | | - let elapsed = process.hrtime.bigint() - start |
47 | | - |
48 | | - return { |
49 | | - triple, |
50 | | - file, |
51 | | - sum, |
52 | | - elapsed, |
53 | | - } |
| 96 | + summary.push({ |
| 97 | + target, |
| 98 | + name, |
| 99 | + sum: createHash('sha256').update(content).digest('hex'), |
| 100 | + }) |
54 | 101 | } |
55 | 102 |
|
56 | 103 | await mkdir(path.resolve(__dirname, '../dist'), { recursive: true }) |
57 | 104 |
|
58 | | -// Build platform binaries and checksum them. We use baseline builds for all x64 platforms to ensure |
59 | | -// compatibility with older hardware. |
60 | | -let results = await Promise.all([ |
61 | | - build('bun-linux-arm64', './tailwindcss-linux-arm64'), |
62 | | - build('bun-linux-arm64-musl', './tailwindcss-linux-arm64-musl'), |
63 | | - |
64 | | - build('bun-linux-x64-baseline', './tailwindcss-linux-x64'), |
65 | | - build('bun-linux-x64-musl-baseline', './tailwindcss-linux-x64-musl'), |
66 | | - |
67 | | - build('bun-darwin-arm64', './tailwindcss-macos-arm64'), |
68 | | - build('bun-darwin-x64-baseline', './tailwindcss-macos-x64'), |
69 | | - |
70 | | - build('bun-windows-x64-baseline', './tailwindcss-windows-x64.exe'), |
71 | | -]) |
72 | | - |
73 | 105 | // Write the checksums to a file |
74 | 106 | let sumsFile = path.resolve(__dirname, '../dist/sha256sums.txt') |
75 | | -let sums = results.map(({ file, sum }) => `${sum} ${file}`) |
76 | | - |
77 | | -console.table( |
78 | | - results.map(({ triple, sum, elapsed }) => ({ |
79 | | - triple, |
80 | | - sum, |
81 | | - elapsed: `${(Number(elapsed) / 1e6).toFixed(0)}ms`, |
82 | | - })), |
83 | | -) |
| 107 | +let sums = summary.map(({ name, sum }) => `${sum} ./${name}`) |
84 | 108 |
|
85 | 109 | await writeFile(sumsFile, sums.join('\n') + '\n') |
| 110 | + |
| 111 | +console.table(summary.map(({ target, sum }) => ({ target, sum }))) |
| 112 | + |
| 113 | +let elapsed = process.hrtime.bigint() - start |
| 114 | +console.log(`Build completed in ${(Number(elapsed) / 1e6).toFixed(0)}ms`) |
0 commit comments