mirror of
https://github.com/aphrody-labs/tailwindcss.git
synced 2026-10-13 21:30:00 +00:00
fix(aphrody): restore turbo build, drop unverified cargo/postcss changes, fix compileStream
- build is turbo again (oxide, all packages, .d.ts); fast-build.ts stays as build:fast, documented as a JS-only dev build, with the browser intellisense stub - remove .cargo/config.toml (cargo has no linker-flavor key, rust-lld on macOS) and restore the upstream release profile (lto = true) - revert the @tailwindcss/postcss candidate-hash cache and its duplicate compileStream/hashCandidates exports - @tailwindcss/node compileStream: optional base/onDependency, no circular re-export from compile.ts, exported from index.cts, meaningful tests; drop the unused hashCandidates - restore the v3 playground upgrade script (package.json was invalid JSON) - test preload warns instead of silently skipping the snapshot serializer
This commit is contained in:
1 parent
e0bf5e2316
commit
f8a1923171
14 files changed
+118
-235
No files matched your search
@@ -1,32 +0,0 @@
|
||||
[target.'cfg(all(windows, target_env = "msvc"))']
|
||||
linker = "rust-lld"
|
||||
rustflags = ["-Ctarget-feature=+crt-static"]
|
||||
|
||||
[target.x86_64-pc-windows-msvc]
|
||||
linker = "rust-lld"
|
||||
rustflags = ["-Ctarget-feature=+crt-static"]
|
||||
|
||||
[target.aarch64-pc-windows-msvc]
|
||||
linker = "rust-lld"
|
||||
rustflags = ["-Ctarget-feature=+crt-static"]
|
||||
|
||||
[target.'cfg(target_os = "macos")']
|
||||
linker = "rust-lld"
|
||||
|
||||
[target.x86_64-apple-darwin]
|
||||
linker = "rust-lld"
|
||||
|
||||
[target.aarch64-apple-darwin]
|
||||
linker = "rust-lld"
|
||||
|
||||
[target.x86_64-unknown-linux-gnu]
|
||||
linker-flavor = "gnu-lld-cc"
|
||||
|
||||
[target.aarch64-unknown-linux-gnu]
|
||||
linker-flavor = "gnu-lld-cc"
|
||||
|
||||
[target.x86_64-unknown-linux-musl]
|
||||
linker-flavor = "gnu-lld-cc"
|
||||
|
||||
[target.aarch64-unknown-linux-musl]
|
||||
linker-flavor = "gnu-lld-cc"
|
||||
@@ -20,6 +20,8 @@ through npm aliases (`"tailwindcss": "npm:@aphrody/tailwindcss@<v>"`), so `@impo
|
||||
- `bun install` (lockfile `bun.lock`, isolated linker). Workspaces, catalog, patched dependencies and the build allow
|
||||
list live in the root `package.json`; `pnpm-workspace.yaml` and `pnpm-lock.yaml` are removed.
|
||||
- `bun run build`, `bun run test` (`cargo test && bun test`), `bun run test:integrations`.
|
||||
- `bun run build:fast` (`scripts/aphrody/fast-build.ts`): JS-only dev build of tailwindcss, node, postcss, cli and
|
||||
browser with `Bun.build`; no oxide binding, no `.d.ts`, not used by CI or releases.
|
||||
- Script rewrite (pnpm/npm/node/vitest to bun): `scripts/aphrody/bunify.ts` (`--check`, `--write`).
|
||||
- Workspace helpers (replace `pnpm -r`): `scripts/aphrody/workspaces.ts`.
|
||||
- Integration test projects are still installed with pnpm, like a user's project.
|
||||
|
||||
+1
-8
@@ -3,11 +3,4 @@ resolver = "2"
|
||||
members = ["crates/*"]
|
||||
|
||||
[profile.release]
|
||||
lto = "thin"
|
||||
codegen-units = 16
|
||||
opt-level = 3
|
||||
|
||||
[profile.dev]
|
||||
codegen-units = 256
|
||||
incremental = true
|
||||
debug = 1
|
||||
lto = true
|
||||
+1
-1
@@ -34,7 +34,7 @@
|
||||
"scripts": {
|
||||
"format": "prettier --write .",
|
||||
"lint": "prettier --check . && turbo lint",
|
||||
"build": "bun ./scripts/aphrody/fast-build.ts",
|
||||
"build": "turbo build --filter=!./playgrounds/*",
|
||||
"build:fast": "bun ./scripts/aphrody/fast-build.ts",
|
||||
"postbuild": "bun ./scripts/pack-packages.mjs",
|
||||
"dev": "turbo dev --filter=!./playgrounds/*",
|
||||
|
||||
@@ -279,5 +279,3 @@ function runResolver(
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
export * from './stream'
|
||||
@@ -7,6 +7,7 @@ export * from './instrumentation'
|
||||
export * from './normalize-path'
|
||||
export * from './optimize'
|
||||
export * from './source-maps'
|
||||
export * from './stream'
|
||||
export { env }
|
||||
|
||||
// In Bun, ESM modules will also populate `require.cache`, so the module hook is
|
||||
|
||||
@@ -1,37 +1,41 @@
|
||||
import { describe, expect, it } from 'bun:test'
|
||||
import { compileStream, hashCandidates } from './index'
|
||||
import { compileStream } from './index'
|
||||
|
||||
describe('@tailwindcss/node web features', () => {
|
||||
it('hashes candidate lists consistently using crypto.subtle', async () => {
|
||||
const list = ['flex', 'hidden', 'p-4', 'bg-red-500']
|
||||
const hashA = await hashCandidates(list)
|
||||
const hashB = await hashCandidates([...list])
|
||||
expect(hashA).toBe(hashB)
|
||||
expect(typeof hashA).toBe('string')
|
||||
expect(hashA.length).toBe(64) // SHA-256 hex string
|
||||
function streamOf(...chunks: string[]) {
|
||||
let encoder = new TextEncoder()
|
||||
return new ReadableStream<Uint8Array>({
|
||||
start(controller) {
|
||||
for (let chunk of chunks) controller.enqueue(encoder.encode(chunk))
|
||||
controller.close()
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const hashDifferent = await hashCandidates(['flex', 'hidden'])
|
||||
expect(hashA).not.toBe(hashDifferent)
|
||||
async function readAll(stream: ReadableStream<Uint8Array>) {
|
||||
let decoder = new TextDecoder()
|
||||
let output = ''
|
||||
for await (let chunk of stream) output += decoder.decode(chunk, { stream: true })
|
||||
return output + decoder.decode()
|
||||
}
|
||||
|
||||
describe('compileStream', () => {
|
||||
it('compiles a chunked CSS stream with the given candidates', async () => {
|
||||
let output = await readAll(
|
||||
compileStream(streamOf('@tailwind utilities;\n@utility foo-', 'bar { color: blue; }'), {
|
||||
candidates: ['foo-bar', 'baz'],
|
||||
}),
|
||||
)
|
||||
|
||||
expect(output).toContain('.foo-bar')
|
||||
expect(output).toContain('color: blue')
|
||||
expect(output).not.toContain('baz')
|
||||
})
|
||||
|
||||
it('compiles CSS via native Web Streams (ReadableStream)', async () => {
|
||||
const encoder = new TextEncoder()
|
||||
const decoder = new TextDecoder()
|
||||
const css = '@utility foo-bar { color: blue; }'
|
||||
it('emits no utilities without candidates', async () => {
|
||||
let output = await readAll(
|
||||
compileStream(streamOf('@tailwind utilities;\n@utility foo-bar { color: blue; }')),
|
||||
)
|
||||
|
||||
const inStream = new ReadableStream({
|
||||
start(controller) {
|
||||
controller.enqueue(encoder.encode(css))
|
||||
controller.close()
|
||||
},
|
||||
})
|
||||
|
||||
const outStream = compileStream(inStream)
|
||||
let output = ''
|
||||
for await (const chunk of outStream) {
|
||||
output += decoder.decode(chunk)
|
||||
}
|
||||
|
||||
expect(output).toContain('foo-bar')
|
||||
expect(output).not.toContain('.foo-bar')
|
||||
})
|
||||
})
|
||||
@@ -1,36 +1,33 @@
|
||||
import { compile, type CompileOptions } from './compile'
|
||||
|
||||
export async function hashCandidates(candidates: string[] | Iterable<string>): Promise<string> {
|
||||
const encoder = new TextEncoder()
|
||||
const content = Array.isArray(candidates) ? candidates.join('\0') : Array.from(candidates).join('\0')
|
||||
const hashBuffer = await globalThis.crypto.subtle.digest('SHA-256', encoder.encode(content))
|
||||
return Array.from(new Uint8Array(hashBuffer))
|
||||
.map((b) => b.toString(16).padStart(2, '0'))
|
||||
.join('')
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles a CSS stream with `compile()` and emits the result of
|
||||
* `compiler.build(candidates)`. The input is buffered until the stream ends.
|
||||
* No source scanning happens here: pass the candidates explicitly.
|
||||
*/
|
||||
export function compileStream(
|
||||
stream: ReadableStream<Uint8Array>,
|
||||
options?: CompileOptions & { candidates?: string[] },
|
||||
options: Partial<CompileOptions> & { candidates?: string[] } = {},
|
||||
): ReadableStream<Uint8Array> {
|
||||
const textDecoder = new TextDecoderStream()
|
||||
const textEncoder = new TextEncoderStream()
|
||||
let { candidates = [], ...compileOptions } = options
|
||||
let buffer = ''
|
||||
|
||||
const transform = new TransformStream<string, string>({
|
||||
let transform = new TransformStream<string, string>({
|
||||
transform(chunk) {
|
||||
buffer += chunk
|
||||
},
|
||||
async flush(controller) {
|
||||
const compileOptions: CompileOptions = options ?? {
|
||||
base: process.cwd(),
|
||||
onDependency: () => {},
|
||||
}
|
||||
const compiler = await compile(buffer, compileOptions)
|
||||
const compiled = compiler.build(options?.candidates ?? [])
|
||||
controller.enqueue(compiled)
|
||||
let compiler = await compile(buffer, {
|
||||
...compileOptions,
|
||||
base: compileOptions.base ?? process.cwd(),
|
||||
onDependency: compileOptions.onDependency ?? (() => {}),
|
||||
})
|
||||
controller.enqueue(compiler.build(candidates))
|
||||
},
|
||||
})
|
||||
|
||||
return stream.pipeThrough(textDecoder).pipeThrough(transform).pipeThrough(textEncoder)
|
||||
return stream
|
||||
.pipeThrough(new TextDecoderStream())
|
||||
.pipeThrough(transform)
|
||||
.pipeThrough(new TextEncoderStream())
|
||||
}
|
||||
@@ -1,13 +1,7 @@
|
||||
import tailwindcss, * as allExports from './index.ts'
|
||||
import tailwindcss from './index.ts'
|
||||
|
||||
// This is used instead of `export default` to work around a bug in
|
||||
// `postcss-load-config`
|
||||
|
||||
for (let key in allExports) {
|
||||
if (key === 'default') continue
|
||||
// @ts-ignore
|
||||
tailwindcss[key] = allExports[key]
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
export = tailwindcss
|
||||
@@ -1,6 +1,5 @@
|
||||
import QuickLRU from '@alloc/quick-lru'
|
||||
import {
|
||||
compile,
|
||||
compileAst,
|
||||
env,
|
||||
Features,
|
||||
@@ -28,7 +27,6 @@ interface CacheEntry {
|
||||
cachedPostCssAst: Root
|
||||
optimizedPostCssAst: Root
|
||||
fullRebuildPaths: string[]
|
||||
candidateHash: string
|
||||
}
|
||||
const cache = new QuickLRU<string, CacheEntry>({ maxSize: 50 })
|
||||
|
||||
@@ -46,7 +44,6 @@ function getContextFromCache(postcss: Postcss, inputFile: string, opts: PluginOp
|
||||
optimizedPostCssAst: postcss.root(),
|
||||
|
||||
fullRebuildPaths: [] as string[],
|
||||
candidateHash: '',
|
||||
}
|
||||
cache.set(key, entry)
|
||||
return entry
|
||||
@@ -249,12 +246,6 @@ function tailwindcss(opts: PluginOptions = {}): AcceptedPlugin {
|
||||
let candidates = compiler.features & Features.Utilities ? context.scanner.scan() : []
|
||||
DEBUG && I.end('Scan for candidates')
|
||||
|
||||
DEBUG && I.start('Hash candidates')
|
||||
let candidateHash = await hashCandidates(candidates)
|
||||
let candidatesChanged = candidateHash !== context.candidateHash
|
||||
context.candidateHash = candidateHash
|
||||
DEBUG && I.end('Hash candidates')
|
||||
|
||||
if (compiler.features & Features.Utilities) {
|
||||
DEBUG && I.start('Register dependency messages')
|
||||
// Add all found files as direct dependencies
|
||||
@@ -305,41 +296,39 @@ function tailwindcss(opts: PluginOptions = {}): AcceptedPlugin {
|
||||
DEBUG && I.end('Register dependency messages')
|
||||
}
|
||||
|
||||
if (candidatesChanged || context.tailwindCssAst.length === 0 || rebuildStrategy === 'full') {
|
||||
DEBUG && I.start('Build utilities')
|
||||
let tailwindCssAst = compiler.build(candidates)
|
||||
DEBUG && I.end('Build utilities')
|
||||
DEBUG && I.start('Build utilities')
|
||||
let tailwindCssAst = compiler.build(candidates)
|
||||
DEBUG && I.end('Build utilities')
|
||||
|
||||
if (context.tailwindCssAst !== tailwindCssAst) {
|
||||
if (optimize) {
|
||||
DEBUG && I.start('Optimization')
|
||||
if (context.tailwindCssAst !== tailwindCssAst) {
|
||||
if (optimize) {
|
||||
DEBUG && I.start('Optimization')
|
||||
|
||||
DEBUG && I.start('AST -> CSS')
|
||||
let css = toCss(tailwindCssAst)
|
||||
DEBUG && I.end('AST -> CSS')
|
||||
DEBUG && I.start('AST -> CSS')
|
||||
let css = toCss(tailwindCssAst)
|
||||
DEBUG && I.end('AST -> CSS')
|
||||
|
||||
DEBUG && I.start('Lightning CSS')
|
||||
let optimized = optimizeCss(css, {
|
||||
minify: typeof optimize === 'object' ? optimize.minify : true,
|
||||
})
|
||||
DEBUG && I.end('Lightning CSS')
|
||||
DEBUG && I.start('Lightning CSS')
|
||||
let optimized = optimizeCss(css, {
|
||||
minify: typeof optimize === 'object' ? optimize.minify : true,
|
||||
})
|
||||
DEBUG && I.end('Lightning CSS')
|
||||
|
||||
DEBUG && I.start('CSS -> PostCSS AST')
|
||||
context.optimizedPostCssAst = postcss.parse(optimized.code, result.opts)
|
||||
DEBUG && I.end('CSS -> PostCSS AST')
|
||||
DEBUG && I.start('CSS -> PostCSS AST')
|
||||
context.optimizedPostCssAst = postcss.parse(optimized.code, result.opts)
|
||||
DEBUG && I.end('CSS -> PostCSS AST')
|
||||
|
||||
DEBUG && I.end('Optimization')
|
||||
} else {
|
||||
// Convert our AST to a PostCSS AST
|
||||
DEBUG && I.start('Transform Tailwind CSS AST into PostCSS AST')
|
||||
context.cachedPostCssAst = cssAstToPostCssAst(postcss, tailwindCssAst, root.source)
|
||||
DEBUG && I.end('Transform Tailwind CSS AST into PostCSS AST')
|
||||
}
|
||||
DEBUG && I.end('Optimization')
|
||||
} else {
|
||||
// Convert our AST to a PostCSS AST
|
||||
DEBUG && I.start('Transform Tailwind CSS AST into PostCSS AST')
|
||||
context.cachedPostCssAst = cssAstToPostCssAst(postcss, tailwindCssAst, root.source)
|
||||
DEBUG && I.end('Transform Tailwind CSS AST into PostCSS AST')
|
||||
}
|
||||
|
||||
context.tailwindCssAst = tailwindCssAst
|
||||
}
|
||||
|
||||
context.tailwindCssAst = tailwindCssAst
|
||||
|
||||
DEBUG && I.start('Update PostCSS AST')
|
||||
root.removeAll()
|
||||
root.append(
|
||||
@@ -386,55 +375,3 @@ function tailwindcss(opts: PluginOptions = {}): AcceptedPlugin {
|
||||
}
|
||||
|
||||
export default Object.assign(tailwindcss, { postcss: true }) as PluginCreator<PluginOptions>
|
||||
|
||||
export async function hashCandidates(candidates: string[] | Iterable<string>): Promise<string> {
|
||||
const encoder = new TextEncoder()
|
||||
const content = Array.isArray(candidates) ? candidates.join('\0') : Array.from(candidates).join('\0')
|
||||
const hashBuffer = await globalThis.crypto.subtle.digest('SHA-256', encoder.encode(content))
|
||||
return Array.from(new Uint8Array(hashBuffer))
|
||||
.map((b) => b.toString(16).padStart(2, '0'))
|
||||
.join('')
|
||||
}
|
||||
|
||||
export function compileStream(
|
||||
stream: ReadableStream<Uint8Array>,
|
||||
opts: PluginOptions = {},
|
||||
): ReadableStream<Uint8Array> {
|
||||
const textDecoder = new TextDecoderStream()
|
||||
const textEncoder = new TextEncoderStream()
|
||||
let buffer = ''
|
||||
|
||||
const transform = new TransformStream<string, string>({
|
||||
transform(chunk) {
|
||||
buffer += chunk
|
||||
},
|
||||
async flush(controller) {
|
||||
const base = opts.base ?? process.cwd()
|
||||
const compiler = await compile(buffer, {
|
||||
base,
|
||||
onDependency: () => {},
|
||||
shouldRewriteUrls: opts.transformAssetUrls ?? true,
|
||||
})
|
||||
const candidates: string[] = []
|
||||
if (compiler.features & Features.Utilities) {
|
||||
const sources = (() => {
|
||||
if (compiler.root === 'none') return []
|
||||
if (compiler.root === null) return [{ base, pattern: '**/*', negated: false }]
|
||||
return [{ ...compiler.root, negated: false }]
|
||||
})().concat(compiler.sources)
|
||||
const scanner = new Scanner({ sources })
|
||||
candidates.push(...scanner.scan())
|
||||
}
|
||||
let output = compiler.build(candidates)
|
||||
const optimize = opts.optimize ?? process.env.NODE_ENV === 'production'
|
||||
if (optimize) {
|
||||
output = optimizeCss(output, {
|
||||
minify: typeof opts.optimize === 'object' ? opts.optimize.minify : true,
|
||||
}).code
|
||||
}
|
||||
controller.enqueue(output)
|
||||
},
|
||||
})
|
||||
|
||||
return stream.pipeThrough(textDecoder).pipeThrough(transform).pipeThrough(textEncoder)
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
import { describe, expect, it } from 'bun:test'
|
||||
import { compileStream, hashCandidates } from './index'
|
||||
|
||||
describe('@tailwindcss/postcss web features', () => {
|
||||
it('hashes candidate lists consistently using crypto.subtle', async () => {
|
||||
const list = ['font-bold', 'text-center', 'm-2']
|
||||
const hashA = await hashCandidates(list)
|
||||
const hashB = await hashCandidates([...list])
|
||||
expect(hashA).toBe(hashB)
|
||||
expect(typeof hashA).toBe('string')
|
||||
expect(hashA.length).toBe(64) // SHA-256 hex string
|
||||
|
||||
const hashDifferent = await hashCandidates(['font-bold', 'text-left'])
|
||||
expect(hashA).not.toBe(hashDifferent)
|
||||
})
|
||||
|
||||
it('compiles CSS via native Web Streams (ReadableStream)', async () => {
|
||||
const encoder = new TextEncoder()
|
||||
const decoder = new TextDecoder()
|
||||
const css = '@utility test-stream { display: flex; }'
|
||||
|
||||
const inStream = new ReadableStream({
|
||||
start(controller) {
|
||||
controller.enqueue(encoder.encode(css))
|
||||
controller.close()
|
||||
},
|
||||
})
|
||||
|
||||
const outStream = compileStream(inStream)
|
||||
let output = ''
|
||||
for await (const chunk of outStream) {
|
||||
output += decoder.decode(chunk)
|
||||
}
|
||||
|
||||
expect(output).toContain('test-stream')
|
||||
})
|
||||
})
|
||||
@@ -6,7 +6,7 @@
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
|
||||
"upgrade": "bun scripts/upgrade.mjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "16.2.7",
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
// Fast parallel build pipeline using Bun.build for Tailwind CSS monorepo packages.
|
||||
// Compiles packages/tailwindcss, @tailwindcss-postcss, @tailwindcss-node, @tailwindcss-cli, @tailwindcss-browser.
|
||||
// `bun run build:fast`: JS-only development build with Bun.build, in parallel, of
|
||||
// tailwindcss, @tailwindcss/postcss, @tailwindcss/node, @tailwindcss/cli and
|
||||
// @tailwindcss/browser. It does not build the oxide binding (Rust), the other
|
||||
// packages (vite, webpack, turbopack, upgrade, standalone) nor the .d.ts files,
|
||||
// and tailwindcss/dist also gets index.* next to lib.*. Releases and CI use
|
||||
// `bun run build` (turbo + tsup).
|
||||
|
||||
import { copyFileSync, existsSync, mkdirSync } from 'node:fs'
|
||||
import { join, resolve } from 'node:path'
|
||||
@@ -7,7 +11,7 @@ import { join, resolve } from 'node:path'
|
||||
const ROOT = resolve(import.meta.dir, '..', '..')
|
||||
const startTime = performance.now()
|
||||
|
||||
console.log('⚡ Starting Bun fast-build pipeline...')
|
||||
console.log('fast-build: Bun.build of 5 packages')
|
||||
|
||||
async function buildTailwindCss() {
|
||||
const pkgDir = join(ROOT, 'packages', 'tailwindcss')
|
||||
@@ -228,6 +232,22 @@ async function buildBrowser() {
|
||||
loader: {
|
||||
'.css': 'text',
|
||||
},
|
||||
// Same stub as packages/@tailwindcss-browser/tsup.config.ts.
|
||||
plugins: [
|
||||
{
|
||||
name: 'patch-intellisense-apis',
|
||||
setup(build) {
|
||||
build.onLoad({ filter: /intellisense\.ts$/ }, () => ({
|
||||
loader: 'ts',
|
||||
contents: `
|
||||
export function getClassList() { return [] }
|
||||
export function getVariants() { return [] }
|
||||
export function canonicalizeCandidates() { return [] }
|
||||
`,
|
||||
}))
|
||||
},
|
||||
},
|
||||
],
|
||||
define: {
|
||||
'process.env.NODE_ENV': JSON.stringify('production'),
|
||||
'process.env.FEATURES_ENV': JSON.stringify('stable'),
|
||||
@@ -252,7 +272,7 @@ const built = await Promise.all([
|
||||
])
|
||||
|
||||
const durationMs = (performance.now() - startTime).toFixed(1)
|
||||
console.log(`✅ Fast build completed in ${durationMs}ms:`)
|
||||
console.log(`fast-build: done in ${durationMs}ms:`)
|
||||
for (const name of built) {
|
||||
console.log(` - ${name}`)
|
||||
}
|
||||
@@ -1,9 +1,15 @@
|
||||
// bun test preload: what packages/tailwindcss/vitest.config.mts sets for vitest.
|
||||
// Needs `expect.addSnapshotSerializer` from the Aphrody Bun fork (984c0a12081).
|
||||
// Needs `expect.addSnapshotSerializer` from the Aphrody Bun fork (984c0a12081);
|
||||
// on another Bun the oklab snapshots of packages/tailwindcss will not match.
|
||||
import { expect } from 'bun:test'
|
||||
import serializer from '../../packages/tailwindcss/src/test-utils/custom-serializer.ts'
|
||||
|
||||
try {
|
||||
;(expect as any).addSnapshotSerializer?.(serializer as any)
|
||||
} catch {}
|
||||
|
||||
let addSnapshotSerializer = (expect as any).addSnapshotSerializer
|
||||
if (typeof addSnapshotSerializer === 'function') {
|
||||
addSnapshotSerializer(serializer)
|
||||
} else {
|
||||
console.warn(
|
||||
`test-preload: expect.addSnapshotSerializer is missing in Bun ${Bun.version}; ` +
|
||||
'use the Aphrody Bun fork (package.json packageManager) for snapshot parity.',
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user