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>
This commit is contained in:
Philipp Spiess 2025-04-11 17:19:55 +02:00 • committed by GitHub
parent 6e1f53348d
commit 83ce4c0a30
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 1432 additions and 78 deletions

View file

@ -67,12 +67,20 @@ jobs:
uses: actions/cache@v4
with:
path: |
./target/
./crates/node/*.node
./crates/node/index.js
./crates/node/*.wasm
./crates/node/index.d.ts
./crates/node/index.js
./crates/node/browser.js
./crates/node/tailwindcss-oxide.wasi-browser.js
./crates/node/tailwindcss-oxide.wasi.cjs
./crates/node/wasi-worker-browser.mjs
./crates/node/wasi-worker.mjs
key: ${{ runner.os }}-oxide-${{ hashFiles('./crates/**/*') }}
- name: Setup WASM target
run: rustup target add wasm32-wasip1-threads
- name: Install dependencies
run: pnpm install

View file

@ -30,7 +30,7 @@ jobs:
- vite
- cli
- postcss
- workers
- oxide
- webpack
# Exclude windows and macos from being built on feature branches
@ -76,12 +76,20 @@ jobs:
uses: actions/cache@v4
with:
path: |
./target/
./crates/node/*.node
./crates/node/index.js
./crates/node/*.wasm
./crates/node/index.d.ts
./crates/node/index.js
./crates/node/browser.js
./crates/node/tailwindcss-oxide.wasi-browser.js
./crates/node/tailwindcss-oxide.wasi.cjs
./crates/node/wasi-worker-browser.mjs
./crates/node/wasi-worker.mjs
key: ${{ runner.os }}-oxide-${{ hashFiles('./crates/**/*') }}
- name: Setup WASM target
run: rustup target add wasm32-wasip1-threads
- name: Install dependencies
run: pnpm install

View file

@ -68,7 +68,7 @@ jobs:
container:
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine
name: Build ${{ matrix.target }} (OXIDE)
name: Build ${{ matrix.target }} (oxide)
runs-on: ${{ matrix.os }}
container: ${{ matrix.container }}
timeout-minutes: 15
@ -82,6 +82,12 @@ jobs:
node-version: ${{ env.NODE_VERSION }}
cache: 'pnpm'
- name: Install gcc-arm-linux-gnueabihf
if: ${{ matrix.target == 'armv7-unknown-linux-gnueabihf' }}
run: |
sudo apt-get update
sudo apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf -y
# Cargo already skips downloading dependencies if they already exist
- name: Cache cargo
uses: actions/cache@v4
@ -99,10 +105,15 @@ jobs:
uses: actions/cache@v4
with:
path: |
./oxide/target/
./crates/node/*.node
./crates/node/index.js
./crates/node/*.wasm
./crates/node/index.d.ts
./crates/node/index.js
./crates/node/browser.js
./crates/node/tailwindcss-oxide.wasi-browser.js
./crates/node/tailwindcss-oxide.wasi.cjs
./crates/node/wasi-worker-browser.mjs
./crates/node/wasi-worker.mjs
key: ${{ runner.os }}-${{ matrix.target }}-oxide-${{ hashFiles('./crates/**/*') }}
- name: Install Node.JS
@ -122,7 +133,7 @@ jobs:
run: pnpm install --ignore-scripts --filter=!./playgrounds/*
- name: Build release
run: pnpm run --filter ${{ env.OXIDE_LOCATION }} build
run: pnpm run --filter ${{ env.OXIDE_LOCATION }} build:platform --target=${{ matrix.target }}
env:
RUST_TARGET: ${{ matrix.target }}
JEMALLOC_SYS_WITH_LG_PAGE: ${{ matrix.page-size }}
@ -172,7 +183,7 @@ jobs:
node -v
echo "~~~~ pnpm --version ~~~~"
pnpm --version
pnpm run --filter ${{ env.OXIDE_LOCATION }} build
pnpm run --filter ${{ env.OXIDE_LOCATION }} build:platform
strip -x ${{ env.OXIDE_LOCATION }}/*.node
ls -la ${{ env.OXIDE_LOCATION }}
- name: Upload artifacts
@ -233,12 +244,20 @@ jobs:
uses: actions/cache@v4
with:
path: |
./oxide/target/
./crates/node/*.node
./crates/node/index.js
./crates/node/*.wasm
./crates/node/index.d.ts
./crates/node/index.js
./crates/node/browser.js
./crates/node/tailwindcss-oxide.wasi-browser.js
./crates/node/tailwindcss-oxide.wasi.cjs
./crates/node/wasi-worker-browser.mjs
./crates/node/wasi-worker.mjs
key: ${{ runner.os }}-${{ matrix.target }}-oxide-${{ hashFiles('./crates/**/*') }}
- name: Setup WASM target
run: rustup target add wasm32-wasip1-threads
- name: Install dependencies
run: pnpm --filter=!./playgrounds/* install
@ -281,12 +300,18 @@ jobs:
echo "$RELEASE_NOTES" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
- name: Upload Standalone Artifacts
- name: Upload standalone artifacts
uses: actions/upload-artifact@v4
with:
name: tailwindcss-standalone
path: packages/@tailwindcss-standalone/dist/
- name: Upload npm package tarballs
uses: actions/upload-artifact@v4
with:
name: npm-package-tarballs
path: dist/*.tgz
- name: Prepare GitHub Release
uses: softprops/action-gh-release@v2
with:

View file

@ -67,7 +67,7 @@ jobs:
container:
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine
name: Build ${{ matrix.target }} (OXIDE)
name: Build ${{ matrix.target }} (oxide)
runs-on: ${{ matrix.os }}
container: ${{ matrix.container }}
timeout-minutes: 15
@ -81,6 +81,12 @@ jobs:
node-version: ${{ env.NODE_VERSION }}
cache: 'pnpm'
- name: Install gcc-arm-linux-gnueabihf
if: ${{ matrix.target == 'armv7-unknown-linux-gnueabihf' }}
run: |
sudo apt-get update
sudo apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf -y
# Cargo already skips downloading dependencies if they already exist
- name: Cache cargo
uses: actions/cache@v4
@ -98,10 +104,15 @@ jobs:
uses: actions/cache@v4
with:
path: |
./oxide/target/
./crates/node/*.node
./crates/node/index.js
./crates/node/*.wasm
./crates/node/index.d.ts
./crates/node/index.js
./crates/node/browser.js
./crates/node/tailwindcss-oxide.wasi-browser.js
./crates/node/tailwindcss-oxide.wasi.cjs
./crates/node/wasi-worker-browser.mjs
./crates/node/wasi-worker.mjs
key: ${{ runner.os }}-${{ matrix.target }}-oxide-${{ hashFiles('./crates/**/*') }}
- name: Install Node.JS
@ -121,7 +132,7 @@ jobs:
run: pnpm install --ignore-scripts --filter=!./playgrounds/*
- name: Build release
run: pnpm run --filter ${{ env.OXIDE_LOCATION }} build
run: pnpm run --filter ${{ env.OXIDE_LOCATION }} build:platform --target=${{ matrix.target }}
env:
RUST_TARGET: ${{ matrix.target }}
JEMALLOC_SYS_WITH_LG_PAGE: ${{ matrix.page-size }}
@ -171,7 +182,7 @@ jobs:
echo "~~~~ pnpm --version ~~~~"
pnpm --version
pnpm install --ignore-scripts --filter=!./playgrounds/* || true
pnpm run --filter ${{ env.OXIDE_LOCATION }} build
pnpm run --filter ${{ env.OXIDE_LOCATION }} build:platform
strip -x ${{ env.OXIDE_LOCATION }}/*.node
ls -la ${{ env.OXIDE_LOCATION }}
- name: Upload artifacts
@ -230,12 +241,20 @@ jobs:
uses: actions/cache@v4
with:
path: |
./oxide/target/
./crates/node/*.node
./crates/node/index.js
./crates/node/*.wasm
./crates/node/index.d.ts
./crates/node/index.js
./crates/node/browser.js
./crates/node/tailwindcss-oxide.wasi-browser.js
./crates/node/tailwindcss-oxide.wasi.cjs
./crates/node/wasi-worker-browser.mjs
./crates/node/wasi-worker.mjs
key: ${{ runner.os }}-${{ matrix.target }}-oxide-${{ hashFiles('./crates/**/*') }}
- name: Setup WASM target
run: rustup target add wasm32-wasip1-threads
- name: Install dependencies
run: pnpm --filter=!./playgrounds/* install
@ -272,6 +291,12 @@ jobs:
- name: Lock pre-release versions
run: node ./scripts/lock-pre-release-versions.mjs
- name: Upload npm package tarballs
uses: actions/upload-artifact@v4
with:
name: npm-package-tarballs
path: dist/*.tgz
- name: Publish
run: pnpm --recursive publish --tag ${{ env.RELEASE_CHANNEL }} --no-git-checks
env:

View file

@ -67,7 +67,7 @@ jobs:
container:
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine
name: Build ${{ matrix.target }} (OXIDE)
name: Build ${{ matrix.target }} (oxide)
runs-on: ${{ matrix.os }}
container: ${{ matrix.container }}
timeout-minutes: 15
@ -81,6 +81,12 @@ jobs:
node-version: ${{ env.NODE_VERSION }}
cache: 'pnpm'
- name: Install gcc-arm-linux-gnueabihf
if: ${{ matrix.target == 'armv7-unknown-linux-gnueabihf' }}
run: |
sudo apt-get update
sudo apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf -y
# Cargo already skips downloading dependencies if they already exist
- name: Cache cargo
uses: actions/cache@v4
@ -98,10 +104,15 @@ jobs:
uses: actions/cache@v4
with:
path: |
./oxide/target/
./crates/node/*.node
./crates/node/index.js
./crates/node/*.wasm
./crates/node/index.d.ts
./crates/node/index.js
./crates/node/browser.js
./crates/node/tailwindcss-oxide.wasi-browser.js
./crates/node/tailwindcss-oxide.wasi.cjs
./crates/node/wasi-worker-browser.mjs
./crates/node/wasi-worker.mjs
key: ${{ runner.os }}-${{ matrix.target }}-oxide-${{ hashFiles('./crates/**/*') }}
- name: Install Node.JS
@ -121,7 +132,7 @@ jobs:
run: pnpm install --ignore-scripts --filter=!./playgrounds/*
- name: Build release
run: pnpm run --filter ${{ env.OXIDE_LOCATION }} build
run: pnpm run --filter ${{ env.OXIDE_LOCATION }} build:platform --target=${{ matrix.target }}
env:
RUST_TARGET: ${{ matrix.target }}
JEMALLOC_SYS_WITH_LG_PAGE: ${{ matrix.page-size }}
@ -171,7 +182,7 @@ jobs:
echo "~~~~ pnpm --version ~~~~"
pnpm --version
pnpm install --ignore-scripts --filter=!./playgrounds/* || true
pnpm run --filter ${{ env.OXIDE_LOCATION }} build
pnpm run --filter ${{ env.OXIDE_LOCATION }} build:platform
strip -x ${{ env.OXIDE_LOCATION }}/*.node
ls -la ${{ env.OXIDE_LOCATION }}
- name: Upload artifacts
@ -225,12 +236,20 @@ jobs:
uses: actions/cache@v4
with:
path: |
./oxide/target/
./crates/node/*.node
./crates/node/index.js
./crates/node/*.wasm
./crates/node/index.d.ts
./crates/node/index.js
./crates/node/browser.js
./crates/node/tailwindcss-oxide.wasi-browser.js
./crates/node/tailwindcss-oxide.wasi.cjs
./crates/node/wasi-worker-browser.mjs
./crates/node/wasi-worker.mjs
key: ${{ runner.os }}-${{ matrix.target }}-oxide-${{ hashFiles('./crates/**/*') }}
- name: Setup WASM target
run: rustup target add wasm32-wasip1-threads
- name: Install dependencies
run: pnpm --filter=!./playgrounds/* install

View file

@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- Add experimental `@tailwindcss/oxide-wasm32-wasi` target ([#17558](https://github.com/tailwindlabs/tailwindcss/pull/17558))
### Fixed
- Ensure `color-mix(…)` polyfills do not cause used CSS variables to be removed ([#17555](https://github.com/tailwindlabs/tailwindcss/pull/17555))

8
Cargo.lock generated
View file

@ -300,9 +300,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "napi"
version = "2.16.16"
version = "2.16.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "839ae2ee5e62c6348669c50098b187c08115bd3cced658c9c0bf945fca0fec83"
checksum = "55740c4ae1d8696773c78fdafd5d0e5fe9bc9f1b071c7ba493ba5c413a9184f3"
dependencies = [
"bitflags",
"ctor",
@ -313,9 +313,9 @@ dependencies = [
[[package]]
name = "napi-build"
version = "2.1.4"
version = "2.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db836caddef23662b94e16bf1f26c40eceb09d6aee5d5b06a7ac199320b69b19"
checksum = "e28acfa557c083f6e254a786e01ba253fc56f18ee000afcd4f79af735f73a6da"
[[package]]
name = "napi-derive"

View file

@ -121,7 +121,7 @@ dist
.AppleDouble
.LSOverride
# Icon must end with two
# Icon must end with two
Icon
@ -194,8 +194,13 @@ Cargo.lock
!.yarn/sdks
!.yarn/versions
*.node
# Generated
*.node
*.wasm
index.d.ts
index.js
browser.js
tailwindcss-oxide.wasi-browser.js
tailwindcss-oxide.wasi.cjs
wasi-worker-browser.mjs
wasi-worker.mjs

View file

@ -8,10 +8,10 @@ crate-type = ["cdylib"]
[dependencies]
# Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix
napi = { version = "2.16.16", default-features = false, features = ["napi4"] }
napi = { version = "2.16.17", default-features = false, features = ["napi4"] }
napi-derive = "2.16.13"
tailwindcss-oxide = { path = "../oxide" }
rayon = "1.10.0"
[build-dependencies]
napi-build = "2.1.4"
napi-build = "2.1.6"

View file

@ -0,0 +1 @@
node_modules/

View file

@ -0,0 +1 @@
node-linker=hoisted

View file

@ -0,0 +1,3 @@
# `@tailwindcss/oxide-wasm32-wasi`
This is the **wasm32-wasip1-threads** binary for `@tailwindcss/oxide`

View file

@ -0,0 +1,45 @@
{
"name": "@tailwindcss/oxide-wasm32-wasi",
"version": "4.1.3",
"cpu": [
"wasm32"
],
"main": "tailwindcss-oxide.wasi.cjs",
"files": [
"tailwindcss-oxide.wasm32-wasi.wasm",
"tailwindcss-oxide.wasi.cjs",
"tailwindcss-oxide.wasi-browser.js",
"wasi-worker.mjs",
"wasi-worker-browser.mjs"
],
"license": "MIT",
"engines": {
"node": ">=14.0.0"
},
"publishConfig": {
"provenance": true,
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/tailwindlabs/tailwindcss.git",
"directory": "crates/node"
},
"browser": "tailwindcss-oxide.wasi-browser.js",
"dependencies": {
"@napi-rs/wasm-runtime": "^0.2.8",
"@emnapi/core": "^1.4.0",
"@emnapi/runtime": "^1.4.0",
"@tybys/wasm-util": "^0.9.0",
"@emnapi/wasi-threads": "^1.0.1",
"tslib": "^2.8.0"
},
"bundledDependencies": [
"@napi-rs/wasm-runtime",
"@emnapi/core",
"@emnapi/runtime",
"@tybys/wasm-util",
"@emnapi/wasi-threads",
"tslib"
]
}

View file

@ -9,25 +9,33 @@
"main": "index.js",
"types": "index.d.ts",
"napi": {
"name": "tailwindcss-oxide",
"triples": {
"additional": [
"armv7-linux-androideabi",
"aarch64-linux-android",
"aarch64-apple-darwin",
"aarch64-unknown-linux-gnu",
"aarch64-unknown-linux-musl",
"armv7-unknown-linux-gnueabihf",
"x86_64-unknown-linux-musl",
"x86_64-unknown-freebsd",
"i686-pc-windows-msvc",
"aarch64-pc-windows-msvc"
]
"binaryName": "tailwindcss-oxide",
"packageName": "@tailwindcss/oxide",
"targets": [
"armv7-linux-androideabi",
"aarch64-linux-android",
"aarch64-apple-darwin",
"aarch64-unknown-linux-gnu",
"aarch64-unknown-linux-musl",
"armv7-unknown-linux-gnueabihf",
"x86_64-unknown-linux-musl",
"x86_64-unknown-freebsd",
"i686-pc-windows-msvc",
"aarch64-pc-windows-msvc",
"wasm32-wasip1-threads"
],
"wasm": {
"initialMemory": 16384,
"browser": {
"fs": true
}
}
},
"license": "MIT",
"devDependencies": {
"@napi-rs/cli": "^2.18.4"
"@napi-rs/cli": "^3.0.0-alpha.77",
"@napi-rs/wasm-runtime": "^0.2.8",
"emnapi": "1.3.1"
},
"engines": {
"node": ">= 10"
@ -42,7 +50,11 @@
},
"scripts": {
"artifacts": "napi artifacts",
"build": "napi build --platform --release --no-const-enum",
"build": "pnpm run build:platform && pnpm run build:wasm",
"build:platform": "napi build --platform --release --no-const-enum",
"postbuild:platform": "node ./scripts/move-artifacts.mjs",
"build:wasm": "napi build --release --target wasm32-wasip1-threads --no-const-enum",
"postbuild:wasm": "node ./scripts/move-artifacts.mjs",
"dev": "cargo watch --quiet --shell 'npm run build'",
"build:debug": "napi build --platform --no-const-enum",
"version": "napi version"
@ -57,7 +69,8 @@
"@tailwindcss/oxide-linux-arm64-musl": "workspace:*",
"@tailwindcss/oxide-linux-x64-gnu": "workspace:*",
"@tailwindcss/oxide-linux-x64-musl": "workspace:*",
"@tailwindcss/oxide-win32-x64-msvc": "workspace:*",
"@tailwindcss/oxide-win32-arm64-msvc": "workspace:*"
"@tailwindcss/oxide-wasm32-wasi": "workspace:*",
"@tailwindcss/oxide-win32-arm64-msvc": "workspace:*",
"@tailwindcss/oxide-win32-x64-msvc": "workspace:*"
}
}

View file

@ -0,0 +1,37 @@
import fs from 'node:fs/promises'
import path from 'node:path'
import url from 'node:url'
const __dirname = path.dirname(url.fileURLToPath(import.meta.url))
let root = path.resolve(__dirname, '..')
const tailwindcssOxideRoot = path.join(root)
// Move napi artifacts into sub packages
for (let file of await fs.readdir(tailwindcssOxideRoot)) {
if (file.startsWith('tailwindcss-oxide.') && file.endsWith('.node')) {
let target = file.split('.')[1]
await fs.cp(
path.join(tailwindcssOxideRoot, file),
path.join(tailwindcssOxideRoot, 'npm', target, file),
)
console.log(`Moved ${file} to npm/${target}`)
}
}
// Move napi wasm artifacts into sub package
let wasmArtifacts = {
'tailwindcss-oxide.debug.wasm': 'tailwindcss-oxide.wasm32-wasi.debug.wasm',
'tailwindcss-oxide.wasm': 'tailwindcss-oxide.wasm32-wasi.wasm',
'tailwindcss-oxide.wasi-browser.js': 'tailwindcss-oxide.wasi-browser.js',
'tailwindcss-oxide.wasi.cjs': 'tailwindcss-oxide.wasi.cjs',
'wasi-worker-browser.mjs': 'wasi-worker-browser.mjs',
'wasi-worker.mjs': 'wasi-worker.mjs',
}
for (let file of await fs.readdir(tailwindcssOxideRoot)) {
if (!wasmArtifacts[file]) continue
await fs.cp(
path.join(tailwindcssOxideRoot, file),
path.join(tailwindcssOxideRoot, 'npm', 'wasm32-wasi', wasmArtifacts[file]),
)
console.log(`Moved ${file} to npm/wasm32-wasi`)
}

View file

@ -0,0 +1,59 @@
import { css, js, json, test } from '../utils'
// This test runs the the wasm build using the `node:wasi` runtine.
//
// There are currently a known problems that the Node WASI preview implementation does not properly
// handle FS reads on macOS and it does not implement all APIs on Windows. Beacuse of that, this
// test is only run on Linux for now.
//
// https://github.com/nodejs/node/issues/47193
// https://github.com/nodejs/uvwasi/issues/11
let testFn = process.platform === 'linux' ? test : test.skip
testFn(
'@tailwindcss/oxide-wasm32-wasi can be loaded into a Node.js process',
{
fs: {
'package.json': json`
{
"dependencies": {
"@tailwindcss/oxide-wasm32-wasi": "workspace:^"
}
}
`,
'src/index.css': css`@import 'tailwindcss/utilities';`,
'src/index.js': js`
const className = "content-['src/index.js']"
module.exports = { className }
`,
'index.mjs': js`
import { Scanner } from '@tailwindcss/oxide-wasm32-wasi'
import { join, resolve } from 'node:path'
let scanner = new Scanner({
sources: [
{
base: join(process.cwd(), 'src'),
pattern: '**/*',
negated: false,
},
],
})
console.log(JSON.stringify(scanner.scan()))
process.exit()
`,
},
},
async ({ expect, exec }) => {
let output = await exec(`node index.mjs`)
expect(JSON.parse(output)).toMatchInlineSnapshot(`
[
"className",
"const",
"content-['src/index.js']",
"exports",
]
`)
},
)

1134
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

View file

@ -24,18 +24,6 @@ for (let path of paths) {
workspaces.set(pkg.name, { version: pkg.version ?? '', dir: dirname(path) })
}
// Move napi artifacts into sub packages
const tailwindcssOxideRoot = path.join(root, 'crates', 'node')
for (let file of await fs.readdir(tailwindcssOxideRoot)) {
if (file.startsWith('tailwindcss-oxide.') && file.endsWith('.node')) {
let target = file.split('.')[1]
await fs.cp(
path.join(tailwindcssOxideRoot, file),
path.join(tailwindcssOxideRoot, 'npm', target, file),
)
}
}
// Clean dist folder
await fs.rm(path.join(root, 'dist'), { recursive: true, force: true })

View file

@ -28,8 +28,9 @@ const syncedWorkspaces = new Map([
'crates/node/npm/linux-arm64-musl',
'crates/node/npm/linux-x64-gnu',
'crates/node/npm/linux-x64-musl',
'crates/node/npm/win32-x64-msvc',
'crates/node/npm/wasm32-wasi',
'crates/node/npm/win32-arm64-msvc',
'crates/node/npm/win32-x64-msvc',
],
],
['@tailwindcss/cli', ['packages/@tailwindcss-standalone']],