tailwindcss/scripts/pack-packages.mjs
Philipp Spiess 83ce4c0a30
Add experimental @tailwindcss/oxide-wasm32-wasi (#17558)
Closes #17448
Closes #13133

This PR adds an a new Oxide target for `wasm32-wasip1-threads`:
`@tailwindcss/oxide-wasm32-wasi`. The goal of this is to enable more
environments to run Oxide, including (but not limited to) StackBlitz.

We're making use of `napi-rs`'s upcoming v3 features to simplify the
setup here, meaning `napi-rs` will configure the WASM target and create
an npm package that works across Node and browser environments.

## MacOS AArch64 issues

While setting up an integration test for the new WASM target, I ran into
an issue where FS reads where not terminating on macOS. After some
research I found this to be a limitation of the Node.js container
interface right now, see: https://github.com/nodejs/node/issues/47193

### Windows issues

We also found that the Node.js wasi container does not properly support
Windows: https://github.com/nodejs/uvwasi/issues/11

For now we, it's probably best for MacOS AArch64 users and Windows users
to use the native modules instead.

## Test plan

The `@tailwindcss/oxide-wasm32-wasi` npm package can be built locally
via `pnpm build` and then run with the Oxide API. A usage example can be
taken from the newly added integration test.

Furthermore this was tested to work as a polyfill on StackBlitz:
https://stackblitz.com/edit/vitejs-vite-uks3gt5p

[ci-all]

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-04-11 17:19:55 +02:00

68 lines
1.8 KiB
JavaScript

import { exec, execSync } from 'node:child_process'
import fs from 'node:fs/promises'
import { platform } from 'node:os'
import path, { dirname } from 'node:path'
import url from 'node:url'
const __dirname = path.dirname(url.fileURLToPath(import.meta.url))
let root = path.resolve(__dirname, '..')
let command = platform() === 'win32' ? 'cd' : 'pwd'
let rawPaths = execSync(`pnpm --silent --filter=!./playgrounds/* -r exec ${command}`).toString()
let paths = rawPaths
.trim()
.split(/\r?\n/)
.map((x) => path.join(x, 'package.json'))
let workspaces = new Map()
// Track all the workspaces
for (let path of paths) {
let pkg = await fs.readFile(path, 'utf8').then(JSON.parse)
if (pkg.private) continue
workspaces.set(pkg.name, { version: pkg.version ?? '', dir: dirname(path) })
}
// Clean dist folder
await fs.rm(path.join(root, 'dist'), { recursive: true, force: true })
Promise.all(
[...workspaces.entries()].map(async ([name, { version, dir }]) => {
function pack() {
return new Promise((resolve) => {
exec(
`pnpm pack --pack-gzip-level=0 --pack-destination="${path.join(root, 'dist').replace(/\\/g, '\\\\')}"`,
{ cwd: dir },
(err, stdout, stderr) => {
if (err) {
console.error(err, stdout, stderr)
}
resolve(lastLine(stdout.trim()))
},
)
})
}
let filename = await pack()
// Remove version suffix
await fs.rename(
path.join(root, 'dist', path.basename(filename)),
path.join(root, 'dist', pkgToFilename(name)),
)
}),
).then(() => {
console.log('Done.')
})
function pkgToFilename(name) {
return `${name.replace('@', '').replace('/', '-')}.tgz`
}
function lastLine(str) {
let index = str.lastIndexOf('\n')
if (index === -1) return str
return str.slice(index + 1)
}