Skip to content

Commit b8aab74

Browse files
Merge updated #739 into feat/tailwind-v4-complete
Resolved libs/extractor/src/tailwind.rs by retaining #749's nested declarations alongside #705's per-class v4 compilation and #734/#739 theme and utility integration. Resolved packages/bun-plugin/src/plugin.ts by retaining #703's SourceLoader/scanner/build stylesheet emission and #749's Tailwind watcher with its returned file list. Resolved packages/rsbuild-plugin/src/plugin.ts by combining #704's synchronous complete stylesheet helpers with #749's stat-based Tailwind watch invalidation. Resolved packages/rsbuild-plugin/src/__tests__/checkout-isolation.test.ts and plugin.test.ts by retaining #704's onBeforeBuild/rootPath setup alongside all Tailwind watcher tests. After reproducing four failing stylesheet tests, their shared test setup now selects the second Rspack config callback (stylesheet completion), while the first remains the Tailwind watcher. No snapshots or lockfiles were hand-merged, no PR intent was removed, and #734/#739/#749 remain separate PRs. Refs #687 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2 parents 8104fde + 3626556 commit b8aab74

23 files changed

Lines changed: 966 additions & 154 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"changes": {
3+
"packages/bun-plugin/package.json": "Patch"
4+
},
5+
"note": "Bun plugin works with Bun.build: `DevupUI()` (from `@devup-ui/bun-plugin/register`) serves the generated stylesheet to the bundler's CSS loader once every other module is loaded, so the build emits a CSS output with the styles of the whole bundle instead of an empty module; class names are short there and readable under the runtime (`debug` option). Files importing the packages Devup UI takes the place of (`@emotion/react`, `@emotion/styled`, `styled-components`, `@vanilla-extract/css`), `@stylexjs/stylex`, or a subpath or re-export of these or `@devup-ui/react`, are compiled instead of skipped",
6+
"date": "2026-09-30T00:00:00.000Z"
7+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"changes": {
3+
"packages/webpack-plugin/package.json": "Patch",
4+
"packages/rsbuild-plugin/package.json": "Patch",
5+
"packages/plugin-utils/package.json": "Patch"
6+
},
7+
"note": "Webpack and Rsbuild builds hold every style in their stylesheets: a stylesheet module is built on its first import, so styles extracted later (the global styles of files webpack or Rsbuild reached late, often outside `src`) were missing from the output. The files the entries reach under `src` are now extracted before the build, the shared base on disk is kept current, and when a stylesheet still changed during the build it is compiled once more, writing only the complete result. Rsbuild serves each stylesheet from the current sheet instead of a module-wide string kept only when it grew, which left edits that shortened the CSS and later builds in the same process stale. plugin-utils adds computeReachableFiles",
8+
"date": "2026-09-30T00:00:00.000Z"
9+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"changes": {
3+
"packages/eslint-plugin/package.json": "Patch",
4+
"bindings/devup-ui-wasm/package.json": "Patch"
5+
},
6+
"note": "style-order-range only checks styleOrder where the build reads it: a prop of a Devup UI component and a top-level key of a style object passed to css/globalCss/keyframes. A styleOrder key in nested data, in a prop value such as an element passed as a prop, or in a call of a function sharing a name with an Object.prototype member is no longer reported. The array, typography and media rules already skip non-style data, and css-utils-literal-only accepts a css()/keyframes() result held in a const inside a function. No new build errors.",
7+
"date": "2026-10-01T00:00:00.000Z"
8+
}

‎bun.lock‎

Lines changed: 9 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎packages/bun-plugin/README.md‎

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,29 @@ Add the zero-config entry to Bun's preload list:
4040
preload = ["@devup-ui/bun-plugin"]
4141
```
4242

43-
## Custom Shorthands
44-
45-
To configure custom shorthands, preload a local module instead of the
43+
## Bundling with `Bun.build`
44+
45+
```ts
46+
import { DevupUI } from '@devup-ui/bun-plugin/register'
47+
48+
await Bun.build({
49+
entrypoints: ['./src/index.tsx'],
50+
outdir: './dist',
51+
plugins: [DevupUI()],
52+
})
53+
```
54+
55+
The build emits the stylesheet as a CSS output holding the styles of every
56+
module in the bundle. Imports of the packages Devup UI takes the place of
57+
(`@emotion/react`, `@emotion/styled`, `styled-components`,
58+
`@vanilla-extract/css`) and of `@stylexjs/stylex` are compiled too.
59+
60+
Class names are short in `Bun.build` and readable under the runtime; pass
61+
`debug` to choose.
62+
63+
## Custom Shorthands
64+
65+
To configure custom shorthands, preload a local module instead of the
4666
zero-config entry:
4767

4868
```toml

‎packages/bun-plugin/__regression__/css-emit.bun.ts‎

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,97 @@ await import(cssPath)
6969
},
7070
)
7171

72+
const registerEntry = resolve(import.meta.dir, '..', 'dist', 'register.mjs')
73+
74+
function run(cwd: string, script: string) {
75+
writeFileSync(join(cwd, 'bunfig.toml'), '')
76+
writeFileSync(join(cwd, 'check.ts'), script)
77+
const result = Bun.spawnSync([process.execPath, 'run', 'check.ts'], {
78+
cwd,
79+
stdout: 'pipe',
80+
stderr: 'pipe',
81+
env: { ...process.env, BUN_RUNTIME_TRANSPILER_CACHE_PATH: '0' },
82+
})
83+
expect(
84+
result.exitCode,
85+
result.stdout.toString() + result.stderr.toString(),
86+
).toBe(0)
87+
}
88+
89+
it('bundles the styles of every module into the Bun.build stylesheet', () => {
90+
const cwd = mkdtempSync(join(tmpdir(), 'devup-css-emit-'))
91+
try {
92+
const widths = Array.from({ length: 8 }, (_, i) => 301 + i)
93+
for (const width of widths) {
94+
writeFileSync(
95+
join(cwd, `fixture-${width}.ts`),
96+
`import { css } from '@devup-ui/react'
97+
export const cls = css({ width: '${width}px' })
98+
`,
99+
)
100+
}
101+
writeFileSync(
102+
join(cwd, 'entry.ts'),
103+
widths
104+
.map((width) => `export { cls as c${width} } from './fixture-${width}'`)
105+
.join('\n'),
106+
)
107+
run(
108+
cwd,
109+
`import { expect } from 'bun:test'
110+
import { DevupUI } from ${JSON.stringify(registerEntry.replaceAll('\\', '/'))}
111+
112+
const result = await Bun.build({
113+
entrypoints: ['./entry.ts'],
114+
outdir: './out',
115+
plugins: [DevupUI()],
116+
})
117+
expect(result.success).toBe(true)
118+
const stylesheets = result.outputs.filter((output) => output.path.endsWith('.css'))
119+
expect(stylesheets).toHaveLength(1)
120+
const css = (await stylesheets[0].text()).replace(/\\s+/g, '')
121+
for (const width of ${JSON.stringify(widths)}) expect(css).toContain('width:' + width + 'px')
122+
`,
123+
)
124+
} finally {
125+
rmSync(cwd, { recursive: true, force: true })
126+
}
127+
})
128+
129+
it('compiles the packages Devup UI takes the place of, and StyleX', () => {
130+
const cwd = mkdtempSync(join(tmpdir(), 'devup-css-emit-'))
131+
try {
132+
writeFileSync(
133+
join(cwd, 'emotion.ts'),
134+
`import { css } from '@emotion/react'
135+
export const cls = css({ width: '201px' })
136+
`,
137+
)
138+
writeFileSync(
139+
join(cwd, 'stylex.ts'),
140+
`import * as stylex from '@stylexjs/stylex'
141+
export const styles = stylex.create({ box: { width: '203px' } })
142+
`,
143+
)
144+
run(
145+
cwd,
146+
`import { readFileSync } from 'node:fs'
147+
import { expect } from 'bun:test'
148+
149+
await import(${JSON.stringify(pluginEntry.replaceAll('\\', '/'))})
150+
const emotion = await import('./emotion.ts')
151+
const stylex = await import('./stylex.ts')
152+
expect(emotion.cls).toBeTruthy()
153+
expect(stylex.styles).toBeTruthy()
154+
const css = readFileSync('./df/devup-ui/devup-ui.css', 'utf-8')
155+
for (const width of [201, 203]) expect(css).toContain('width:' + width + 'px')
156+
`,
157+
)
158+
} finally {
159+
rmSync(cwd, { recursive: true, force: true })
160+
}
161+
})
162+
72163
it('emits vanilla-extract styles through the WASM engine', () => {
73164
const cwd = mkdtempSync(join(tmpdir(), 'devup-css-emit-'))
74165
try {

0 commit comments

Comments
 (0)