Skip to content

fix(next): deliver complete webpack CSS across compilers and cached builds - #783

Merged
owjs3901 merged 4 commits into
mainfrom
fix/next-webpack-css-delivery
Oct 17, 2026
Merged

owjs3901 merged 4 commits into
mainfrom
fix/next-webpack-css-delivery

Conversation

@owjs3901

@owjs3901 owjs3901 commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Refs #686 (Next webpack CSS 전달). 최신 main a6ecc125에서 분기했고 #781의 fd4bb5d4를 실제 두 부모 merge 23e58b86로 반영했습니다. #781이 먼저 병합되어야 합니다. #719의 변경은 이 브랜치에 포함하지 않습니다.

요약

Next 16.3.6의 next build --webpack에서 서버·edge compiler가 추출한 스타일과 클래스 할당이 다음 compiler의 reset으로 지워지는 문제를 고칩니다. Next의 loader-query 진입점은 일반 파일 그래프의 진입점이 아니어서 기존 사전 추출이 비었고, 최종 클라이언트 CSS에는 서버 스타일이 없거나 SSR 클래스와 다른 클래스에 선언이 붙었습니다.

Next가 결정론적으로 수집한 완전한 파일 집합을 각 compiler의 정상 reset·설정·번호 배정 뒤에 다시 추출합니다. 런타임 스타일링, 상태를 보존하는 전역 캐시, reset 생략, 의존성 추가로 우회하지 않습니다.

동작

  • apps/next의 실제 main webpack 빌드에서 클라이언트 스타일·테마는 남지만 서버 layout의 흰 글씨와 globalCss의 body 배경·글씨가 빠지는 것을 먼저 확인했습니다. 수정 후 같은 앱의 webpack 빌드에서 서버·클라이언트·라이브러리·테마·globalCss가 모두 복구됐습니다.
  • 실제 서버→edge→클라이언트 순서의 계측에서 동일 PID·동일 WASM engine을 확인했습니다. reset 전 존재하던 스타일이 reset 후 사라지고 Next loader-query에 대한 prewarm 파일은 0개였습니다.
  • 두 CSS 모드에서 off/on/off 대조 실험으로 완전한 사전 추출만 켜면 CSS와 SSR 클래스 대응이 복구되고, 끄면 다시 손실되는 것을 확인했습니다. CSS 파일 쓰기·snapshot·additional-pass·정상 reset 자체는 유지합니다.
  • webpack 어댑터의 productionPrewarmFiles는 진입점이 아니라 완전한 파일 목록입니다. undefined는 기존 일반 webpack의 진입점 도달 범위를 유지하고, []를 포함한 명시적 목록은 해당 범위를 그대로 사용합니다. 경로를 정규화·중복 제거·정렬하고 외부 파일도 필터로 버리지 않습니다.
  • Next의 허용된 외부 패키지는 barrel 아래 상대 import·re-export·리터럴 dynamic import와 허용된 의존성까지 수집합니다. 기존 include 정책과 CJS→MJS 우선순위·CJS-only 제외 정책을 유지합니다.
  • reset 뒤 custom shorthand를 다시 등록합니다. 사전 추출 초기화가 실패하면 build session을 즉시 반환하므로 다음 무관한 빌드에 부분 스타일·prefix·옵션이 새지 않습니다.
  • webpack persistent cache가 이전 build ID의 CSS 요청을 보존해 새 CSS-loader 규칙을 건너뛰는 두 번째 실제 오류도 고칩니다. production 기본 경로는 .next/cache/devup-ui로 안정화하며, 개발의 build-ID 경로와 명시적 cssDir은 유지합니다. cache를 끄거나 넓은 정규식으로 우회하지 않습니다.

부모 세션이 직접 실행한 실제 HTTP 수신 증거(정규화된 선언):

GET / -> server-proof class="e-a e-b" -> background:#800000; padding:13px
GET /client -> client-proof class="b-a" -> color:#00ffff
GET /edge-page -> edge-page-proof class="f-a f-b" -> color:#daa520; padding:19px
GET /_next/static/css/5e0e15d3df8b6297.css -> 200

실제로 해당 문서가 링크한 위 CSS 파일의 내용 일부:

.e-a{background:maroon}.e-b{padding:13px}
.a-a{color:rebeccapurple}.a-b{margin-left:17px}

위 클래스 이름과 URL hash는 실제 receipt이며 고정된 공개 계약이나 테스트 기대값이 아닙니다. 두 모드에서 server·client·Edge HTML과 각 문서의 stylesheet를 HTTP200으로 가져와 실제 클래스마다 모든 선언을 검사하고, cold·cold-repeat·이미 채워진 warm webpack cache에서도 CSS bytes와 클래스 대응이 같음을 확인합니다.

새로 생기는 오류

명시적인 production 사전 추출 중 실제로 발생한 파일 수집·manifest·읽기·추출 오류는 더 이상 조용히 무시하고 불완전한 CSS를 내보내지 않습니다. 원래 오류를 그대로 빌드에 전달하며, 스타일 구문 오류는 기존 engine의 위치 포함 진단을 유지합니다. 일반 webpack의 기존 best-effort 경로와 watch 동작은 유지합니다.

남는 한계

edge API가 JSON으로 반환하는 클래스는 HTML 문서가 아닙니다. 기본 모드에서 그 CSS가 정적 파일에 생성되는 것과 / 문서가 그 stylesheet를 링크하는 것은 별도 계약이며, API-only CSS를 root-linked로 주장하지 않습니다. 기존 App Router Edge API 회귀는 유지했고, coordinator 승인에 따라 실제 Edge HTML은 지원되는 Pages Router experimental-edge 페이지로 검증했습니다.

Windows·Next 16.3.6의 App Router Edge HTML은 Devup 설정·import를 제거한 native Next-only 대조군과 workers-off 대조군에서도 React consumer-manifest module 253/9296 누락으로 HTTP500을 재현했습니다. 이 외부 Next 문제는 별도로 보고했으며 App Router Edge HTML 성공을 주장하지 않습니다. Node runtime으로 바꿔서 Edge 검증을 통과시킨 것이 아닙니다.

기존 정적 그래프·공유 resolver가 지원하는 import/경로 정책을 사용합니다. 비리터럴 import, bundler 전용 사용자 resolver·조건, CJS-only 패키지에 새 지원을 약속하지 않습니다. 빌드마다 상태를 격리하며 런타임 해석으로 대체하지 않습니다.

검증

  • 회귀 우선: 실제 Next의 두 모드 off/on/off 손실·복구 증거, 외부 barrel 누락·사전 추출 오류 무시 회귀, 실제 webpack 생성자 실패 후 부분 스타일이 다음 빌드에 새는 회귀를 수정 전에 실패시켰습니다.

  • 실제 WASM·webpack compiler를 순서대로 생성·종료한 뒤 CSS bytes·클래스 대응이 동일하며, 다음 무관한 일반 빌드에는 이전 스타일·prefix·shorthand가 남지 않습니다. assertion 실패·비동기 shutdown 오류에서도 모든 compiler 종료를 기다린 다음 fixture를 제거하며 오류를 보존합니다.

  • 전체 Bun 5,928 pass / 0 fail / 함수·라인 100%, 두 CSS 모드 HTTP/cache matrix와 기존 5개 Next process 소유권·실패·timeout 회귀 포함.

  • 기본 스택·Rust 1.99의 명시적 workspace clippy -D warnings 및 Rust workspace 전체 통과. 새 WASM·루트 build와 관련 패키지 typecheck 통과.

  • 최종 정식 pre-commit hook 통과: 749.4초, peak 3,156,398,080 bytes, jobs=2·기본 스택·Rust 1.99·CI unset·12GB watchdog, lint·tarpaulin·Bun 생략 없음. 기존 lint 경고 9개 외 추가 없음. 핵심 커밋 훅도 288.9초에 통과했고, 그 첫 시도의 임시 계측 복원 줄바꿈 오류는 세 파일만 포맷 복구한 뒤 새 커밋 시도로 해결했습니다.

  • Windows 물리 포맷의 Rust coverage는 **98.11%(17,182/17,513)**이며 100%로 주장하지 않습니다. Linux CI의 --fail-under 100 결과를 별도로 확인합니다.

  • 부모가 최종 소스에서 직접 수행한 수동 QA: 두 모드의 cold/repeat/warm 총 18개 HTML 문서, 각 문서가 실제 링크한 CSS와 클래스별 모든 선언, global/theme·외부 barrel, 깨진 HTTP 경계 5개, 실제 WASM/compiler 생성자 실패 후 빌드 격리 통과. 모든 owner가 tree_exit active=[] 뒤 fixture를 제거했습니다. 브라우저 hydration·시각·computed-style 검증으로 과장하지 않습니다.

  • experimental.webpackBuildWorker:true의 실제 서로 다른 worker PID를 관찰했으며, 옵션만 켠 것을 다중 프로세스 검증으로 간주하지 않았습니다.

  • 전체 목표·최종 코드/HTTP gate APPROVE: W65b-delivery-ready-review.md. LSP는 설치되지 않았고 기존에 설치 거절된 상태이므로 LSP clean을 주장하지 않습니다.

  • head: 42cb0a4e9d9f1e5eb9b4f87bfbd4ff3a000183da (핵심 수정 b1dc9465, cache 수정·HTTP 회귀 42cb0a4e). 정상 push했고, 측정 context 416.7K(06:17:49Z)입니다.

  • 최종 CI 38858583003 success: 정확히 위 head의 publish·benchmark·landing-next-e2e·vinext-rsc-css-e2e 및 Codecov/patch 모두 성공. Linux Rust 100.00%(12,393/12,393), 전체 Bun 5,928 pass/0 fail/함수·라인 100%, preload race 8/0, 두 모드 HTTP/cache·기존 fault 회귀와 landing/RSC e2e를 통과했습니다.

  • 2026-10-17: main(b4c143b, 1차 묶음 fix(next): support JavaScript JSX and webpack source map objects #781·fix(extractor): compose styled extensions, merge attrs and let later JSX spreads win #707~fix(extractor): forward refs from generated styled components #722·fix(react,components): support React 18 peers and drop React 19-only APIs #720·fix(components): forward refs from the UI kit form components #721 병합 후)을 병합했습니다. #781과 main 사이에 merge base가 두 개라 GitHub가 충돌로 표시했지만 실제 내용 충돌은 없어 자동 병합되었습니다(코드 변경 없음).

owjs3901 and others added 3 commits October 16, 2026 03:38
Refs #686

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #686

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #686

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Changepacks

@devup-ui/wasm@1.0.83 → 1.0.84 - bindings/devup-ui-wasm/package.json

Patch

  • Rewriting styled-components, Emotion and vanilla-extract imports keeps type-only imports type-only: import { type DefaultTheme, ThemeProvider } now leaves import type { DefaultTheme } on the package instead of a value import that made it a runtime dependency, and an import type declaration is left as written. spin.getName() on a keyframes() binding becomes the generated keyframes name instead of a call on a string that threw at runtime
  • The styled-components and Emotion compat ThemeProvider now gives its theme through React context: useTheme, withTheme and the new ThemeConsumer return the real theme object (an object merged over the outer theme, or what a theme function makes of it) instead of an empty Proxy of var() references, withTheme forwards refs, lets a caller's theme win and sets a displayName. Styles keep reading the CSS variables the provider declares. styled-components v6.4 createTheme is available from the compat entry, and ThemeConsumer and createTheme imports are redirected there instead of staying a runtime dependency on styled-components
  • css(a, b) composing classes whose styles the build knows (a const bound to css() in the file, or a css() result another module exports with a rule object every value of which is known) merges their styles: a later part's declaration replaces an earlier one's for the same property, selector, breakpoint and layer, also under conditions (css(base, cond && danger)) and for ||/?? parts, where the classes used to be joined and the stylesheet order picked the winner. vanilla-extract style([a, b]) passes each composed style as its own argument, so the later one wins, and a style composed again later (style([a, b, a])) is kept. Classes the build does not know (strings, props, CSS Modules) are kept as they are
  • styled(Base) extending a styled component the file binds to a const renders Base's tag directly with Base's styles composed under its own, so the extension's declarations replace Base's for the same property, selector, breakpoint and layer instead of losing to them by stylesheet order; Base's attrs apply before the extension's. attrs merge as styled-components merges them: className is joined with the caller's, style is merged and other props replace earlier ones. On an element, a className or style spread after the explicit prop now wins, as React merges props in the order they are written
  • stylex.props() and stylex.attrs() merge namespaces this file creates key by key as StyleX does: a later namespace's key replaces an earlier one's, so props(red, blue) is blue and props(blue, red) is red, and a key set to null removes the earlier value; conditions (cond && styles.x, cond ? a : b) choose per key. Arguments the build cannot read key by key (namespaces from other modules, props, dynamic namespaces, include()) keep the previous joined behavior
  • Respect source-order property replacement across conditional spreads in styled rule objects, and document that styled callbacks cannot return runtime class names. No new build errors; existing located Cannot compose errors remain unchanged.
  • Preserve unitless legacy vendor numbers, classify vendor time values as milliseconds and emit the Opera vendor prefix correctly; reject nested responsive arrays and known dead unprefixed CSS property names with source-located build errors, while preserving supported experimental CSS, unknown properties and documented shorthands.
  • Class names and file numbers no longer depend on the order files are processed in. Every plugin walks the files it can extract (the project's source and the included packages) before the first transform and numbers them in path order (a file collapsed into a bucket is numbered as the bucket, and only files that mention the package, StyleX or an aliased package take numbers) through the new seedFileMap, so identical builds give identical class prefixes, CSS file names and output hashes; in development, files that appear later are numbered after the existing ones. Styles are given class names in a fixed order, not the order of a hash set. The exported class, file and canonical maps are canonical JSON (sorted keys). Stylesheet evaluation (vanilla-extract) refuses Math.random(), Date.now(), new Date() and Date(), which differ between builds, with a build error; fixed dates such as new Date(0) are unchanged. resetBuildState starts a build from its own options: the prefix, atom hoisting, routes, buckets, names, numbers and styles an earlier build in the process left are cleared, and the prefix is set on every setup even when it is absent. Engine snapshots from concurrent transforms are written one after another per file, so an older snapshot is never written last. File lists are ordered by code point rather than the locale
  • Typography documents the emitted default breakpoints, and source-backed integration tests keep the public breakpoint lists and range bounds synchronized with the production defaults. No runtime changes or new build errors.
  • @emotion/css is aliased by default: css and keyframes compile like Devup UI's own (numbers in px, as Emotion reads them), injectGlobal declares global CSS. cx takes class names, as in Emotion: strings and runtime values are classes kept as written (falsy values are skipped), an object is a { name: condition } class map, arrays nest, and inline css() calls and css() classes compose with the rules css(a, b) uses. merge(className) splits what the build can read (string tokens, template literal parts) and composes the css() classes in it, keeping a runtime string it cannot split as it is. Namespace members, constant-string bracket access, const destructuring and const alias chains compile through these same named APIs with scope-aware binding resolution; numeric values retain Emotion units after constant inlining and build-time evaluation. The Emotion import disappears when no runtime-only member is used. Ambient declarations type the styling imports (cx(...args: ClassNamesArg[]), merge(className: string)). New located build errors: cx() cannot use a spread, a getter, a setter or a method in a class map, or a template literal call; merge() takes exactly one class string, not a spread or a template literal call; the root module has no default export, namespace member keys must be constant strings naming supported exports, and styling functions cannot be passed, mutated or re-exported. Optional styling reads/calls, namespace rest destructuring, styling defaults or nested patterns, non-const aliases, exported styling bindings and reads whose initialization order cannot be established are build errors instead of runtime dependencies. Styling loaded through require, import-equals or dynamic import must use static ES module imports; an unawaited dynamic namespace that the build cannot follow is also a located error. Its runtime-only APIs (cache, flush, hydrate, sheet, getRegisteredStyles) stay on @emotion/css, including their nested/default destructuring, optional reads and recognized runtime-loader access
  • Emotion's CacheProvider is available from the compat entry as a no-op that renders its children, and import { CacheProvider } from '@emotion/react' is redirected there instead of keeping @emotion/react as a runtime dependency; the cache it configures holds styles Emotion inserts at runtime, which Devup UI writes at build time
  • In rule objects written against Emotion, styled-components or vanilla-extract, a property whose value is an array of literals (background: ['red', 'linear-gradient(...)']) declares each value in order as a browser fallback, as those libraries do, instead of being read as Devup UI breakpoints; numbers get px as the library gives them. Arrays holding runtime values stay as written
  • 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.
  • The extractor guide shows reproducible native-element output, generated CSS-variable bindings, and actual atomic CSS rather than obsolete class names. Documentation only: no new build errors.
  • Accept JSX in .js, .mjs and .cjs throughout extraction without changing TypeScript dialects or plain JavaScript output, and pass parsed source-map objects to webpack callbacks for Next SWC compatibility. Malformed relevant source now fails with located parser diagnostics instead of unlocated parser-panic messages or silently recovered output.
  • Selectors: a nested rule without & in a styled or css template is scoped to the component instead of becoming a global rule (.child {} and > p {} select descendants, :hover {} and ::before {} the component), and a rule nested in a selector list applies under each of its selectors; content keeps the quotes of its strings (content: 'x', content: ""); params go on the pseudo-class that takes them, so _groupNthChild gives :is(...):nth-child(2n) .a also when nested; a bare selectors key is a pseudo-class name, an attribute of the element or a descendant selector ('div p', '> p'), under a parent too, and keeps the case of class names; ::part(), ::slotted(), ::highlight(), ::picker() and the view-transition pseudo-elements get two colons. New build errors: a _name prop or selector key that names nothing (_notASelector, selectors={{ div: ... }}), and a string under a selector that is not CSS text (_hover="external-class")
  • Styled components the build generates are wrapped in React's forwardRef, so a ref passed to them reaches the element they render on React 18 as well as React 19; the file imports forwardRef from react when it defines one
  • Styled components no longer pass every prop to the tag they render: $ props, theme and the props their style functions or attrs read are kept away from a tag unless it takes them as attributes, and shouldForwardProp (Emotion options or styled-components withConfig) is evaluated at build time. A shouldForwardProp the build cannot evaluate is a build error naming the file, line and code, with the forms it accepts. An element using a styled component the file defines drops props the component neither reads nor passes on; spread props are passed as written
  • Styled components render what their as prop names, defaulting to the tag or component they were defined with, and pass forwardedAs on as as, as styled-components and Emotion do; as used to reach the rendered tag as an attribute. Component.withComponent(target) on a styled component the file binds to a const builds a component rendering the same styles and attrs as target (a tag name or a component JSX can name), where it used to call a method the generated function does not have and throw
  • stylex.attrs() gives the dynamic CSS variables it sets as style attribute text ("--a:" + value), as StyleX does for spreading onto non-React targets, instead of a style object; stylex.props() keeps the object React takes. The attrs() return type declares style as a string
  • Tailwind classes in a className: each class compiles on its own and every class Tailwind does not define (custom, plugin and analytics classes) stays as written, where any recognized class used to rebuild the whole className without them and could rewrite part of another class; a class touching a template interpolation (icon-${name}) is left as written, and template classNames no longer get spaces inserted around interpolations. A class whose variant is not understood stays as written instead of applying unconditionally; variants follow Tailwind v4: stacked variants compose (hover:focus: matches the element itself), hover is guarded by @media (hover: hover), group-/peer- use the group/peer marker classes (and group-hover/name), and data-, aria-, has-, nth-, , **, supports-[…] and [&…]/[@media …] variants compile. Utilities emit all their declarations (size-, rounded-t-, truncate, sr-only, not-sr-only, text-sm with its line height, text-sm/6, leading-), translate/rotate/scale/skew compose through Tailwind's individual properties and registered variables (@Property) with negative values kept negative, and arbitrary values follow Tailwind's grammar: spaces for underscores except in url() and var() names, _ for an underscore, spaces around math operators, type hints, [property:value] and (--variable) shorthands. Words that only share a utility's prefix (select-wrapper, order-summary, cursor-foo) are no longer compiled
  • Order standalone nonresponsive hover-capability guards within their selector group so active, focus and disabled styles can override hover. Keep responsive Tailwind hover and Devup selector prop ordering unchanged. No new build errors.
  • More of Tailwind CSS v4 compiles: container queries (@sm:, @max-md:, @min-[400px]:, @sm/main:) and @container utilities, mask-* (all but the gradient masks), text-shadow-, inset-shadow-, font-variant-numeric utilities (tabular-nums and the like), 3D transforms (rotate-x/y/z, translate-z, scale-z, translate-3d, scale-3d, transform-3d, backface-, perspective-, perspective-origin-), contain- and outline-hidden (with its forced-colors fallback). The project's Tailwind CSS is read further: functional @Utility with --value() and --modifier(), @apply in @Utility, and theme(--name) in arbitrary values; classes of @plugin entries stay as written. A className now compiles the classes of cn()/twMerge() calls whose arguments are all static (merged as tailwind-merge does, the later class wins), of consts bound to class strings, and of cva() calls (base, variants, compound variants). The Rsbuild plugin reads the Tailwind CSS again and compiles every module again when it changes, and the Bun plugin reads it again. No new build errors
  • Tailwind classes in a className follow Tailwind CSS v4: colors are the v4 default palette (oklch) and the colors and shadows of devup.json theme are usable as Tailwind theme values (bg-primary, text-text, shadow-card, as var(--token)); the opacity modifier (bg-red-500/50, /[71.37%], /(--alpha)) mixes with color-mix as v4 does; a trailing or leading ! makes every declaration important; blur, brightness, contrast, grayscale, hue-rotate, invert, saturate, sepia and their backdrop-* forms compose through Tailwind's custom properties instead of overwriting filter and backdrop-filter; gradients use bg-linear-, bg-conic- and bg-radial-* with from-, via- and to-* composing through the gradient variables (bg-gradient-to-* is not defined by v4 and stays as written); animate-* brings the @Keyframes it names and transition, duration, delay and ease follow v4; space-* and divide-* apply to the children like v4 (:where(& > :not(:last-child))); @Property rules are emitted only for the custom properties a class uses. Anything the build cannot reproduce exactly (drop-shadow-*) stays as written in the className. No new build errors
  • Tailwind classes in a className read the Tailwind CSS of the project: the file that imports tailwindcss (the tailwind.css option of devup.json, else a conventional path) gives @theme variables (colors, spacing, shadows, blurs, radii, easings, animations with their @Keyframes, default transitions, breakpoints), @Utility utilities and @custom-variant variants, and the Vite, webpack and Next.js plugins read it again when it or a CSS file it imports changes. Anything that cannot be read exactly, and the classes of namespaces the build does not read, stay as written. More of Tailwind CSS v4 compiles: ring-, inset-ring-, ring-offset-* and drop-shadow-* compose with shadows and filters, borders and outlines draw in --tw-border-style and --tw-outline-style (a border now shows its style), line-clamp, text decoration, border-spacing, logical and scroll spacing, tables, perspective, font-stretch and more; the arguments of clsx(), classnames() and classNames() and [...].join(' ') compile in place. Radii follow v4 (rounded-sm is 0.25rem, rounded-full is calc(infinity * 1px)). No new build errors
  • A styled-components or Emotion style function reading the theme by literal keys, such as p => p.theme.space[2] or ({ theme }) => theme.colors['brand'], now reads the CSS variable ThemeProvider declares (var(--space-2)). A theme read the build cannot turn into a CSS variable, such as p => p.theme, p => p.theme.brand() or theme.space[i], is a build error naming the file, line, column and code, where it used to read a theme object that does not exist at runtime and give undefined or throw
  • Generate React JSX-runtime calls for styled components in non-JSX source modules while preserving JSX and TSX output. No new build errors.
  • While @vanilla-extract/css is aliased, importing @vanilla-extract/recipes or @vanilla-extract/sprinkles is a build error naming the file, line and column: those packages generate CSS Devup UI does not compile, so their rules used to be silently missing from the stylesheet. The error says to write the styles with css(), or to disable the alias and build with the vanilla-extract plugin. Type-only imports are allowed
  • Skip source-map generation in the codeExtractWithoutSourceMap WASM export while preserving extracted code, CSS and dependencies. Add real WASM and Next loader regressions for map-enabled and map-disabled extraction. No new build errors.

@devup-ui/bun-plugin@1.0.22 → 1.0.23 - packages/bun-plugin/package.json

Patch

  • 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
  • Class names and file numbers no longer depend on the order files are processed in. Every plugin walks the files it can extract (the project's source and the included packages) before the first transform and numbers them in path order (a file collapsed into a bucket is numbered as the bucket, and only files that mention the package, StyleX or an aliased package take numbers) through the new seedFileMap, so identical builds give identical class prefixes, CSS file names and output hashes; in development, files that appear later are numbered after the existing ones. Styles are given class names in a fixed order, not the order of a hash set. The exported class, file and canonical maps are canonical JSON (sorted keys). Stylesheet evaluation (vanilla-extract) refuses Math.random(), Date.now(), new Date() and Date(), which differ between builds, with a build error; fixed dates such as new Date(0) are unchanged. resetBuildState starts a build from its own options: the prefix, atom hoisting, routes, buckets, names, numbers and styles an earlier build in the process left are cleared, and the prefix is set on every setup even when it is absent. Engine snapshots from concurrent transforms are written one after another per file, so an older snapshot is never written last. File lists are ordered by code point rather than the locale
  • More of Tailwind CSS v4 compiles: container queries (@sm:, @max-md:, @min-[400px]:, @sm/main:) and @container utilities, mask-* (all but the gradient masks), text-shadow-, inset-shadow-, font-variant-numeric utilities (tabular-nums and the like), 3D transforms (rotate-x/y/z, translate-z, scale-z, translate-3d, scale-3d, transform-3d, backface-, perspective-, perspective-origin-), contain- and outline-hidden (with its forced-colors fallback). The project's Tailwind CSS is read further: functional @Utility with --value() and --modifier(), @apply in @Utility, and theme(--name) in arbitrary values; classes of @plugin entries stay as written. A className now compiles the classes of cn()/twMerge() calls whose arguments are all static (merged as tailwind-merge does, the later class wins), of consts bound to class strings, and of cva() calls (base, variants, compound variants). The Rsbuild plugin reads the Tailwind CSS again and compiles every module again when it changes, and the Bun plugin reads it again. No new build errors
  • Tailwind classes in a className read the Tailwind CSS of the project: the file that imports tailwindcss (the tailwind.css option of devup.json, else a conventional path) gives @theme variables (colors, spacing, shadows, blurs, radii, easings, animations with their @Keyframes, default transitions, breakpoints), @Utility utilities and @custom-variant variants, and the Vite, webpack and Next.js plugins read it again when it or a CSS file it imports changes. Anything that cannot be read exactly, and the classes of namespaces the build does not read, stay as written. More of Tailwind CSS v4 compiles: ring-, inset-ring-, ring-offset-* and drop-shadow-* compose with shadows and filters, borders and outlines draw in --tw-border-style and --tw-outline-style (a border now shows its style), line-clamp, text decoration, border-spacing, logical and scroll spacing, tables, perspective, font-stretch and more; the arguments of clsx(), classnames() and classNames() and [...].join(' ') compile in place. Radii follow v4 (rounded-sm is 0.25rem, rounded-full is calc(infinity * 1px)). No new build errors

@devup-ui/components@0.1.60 → 0.1.61 - packages/components/package.json

Patch

  • @devup-ui/react and @devup-ui/components take React as a peer of ^18 || ^19 instead of * and no longer depend on React 19 themselves, so a React 18 app no longer gets a second React. Select and Stepper read their context with useContext instead of React 19's use() and provide it with Context.Provider instead of React 19's (which crashes React 18), and Select repositions its container from an effect instead of a callback ref returning cleanup, which React 18 ignores
  • Button, Checkbox, Input, Radio and Textarea forward their ref to the button, input or textarea they render, so a ref reaches the form element on React 18 as well as React 19

@devup-ui/eslint-plugin@1.0.22 → 1.0.23 - packages/eslint-plugin/package.json

Patch

  • The @devup-ui/react README and the css-utils-literal-only rule docs link their mention of build errors to the new Build Errors reference (https://devup-ui.com/docs/build-errors), which lists the build errors grouped by API; the docs sidebar lists the page after Supported Syntax & Limitations, whose Build errors section links to it. Documentation only: no new build errors
  • ESLint rules now check exactly what the build reads. The array, typography, media and styleOrder rules and css-utils-literal-only follow the import aliases the build compiles by default (@emotion/react, @emotion/styled, styled-components, @vanilla-extract/css) and the rules of styled (styled.div({...}), styled("div", {...}), styled(Base)({...}, {...}); not .attrs()), createGlobalStyle({...}) and the styles of Emotion Global, and check and fix a module-level const object that only styles read. Vanilla-extract stylesheets (.css.ts/.css.js) are no longer read as Devup UI. New rule no-runtime-read reports css/keyframes/styled/Box read as a value ("X is read at runtime, where it does not exist"), and css-utils-literal-only reports globals that do not exist where a .css.ts is evaluated (window, document, process, ...). No new build errors.
  • 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.

@devup-ui/next-plugin@1.0.90 → 1.0.91 - packages/next-plugin/package.json

Patch

  • Isolate the coordinator port file per owning process when another live next dev/build holds it, record the owner pid, and report port, pid and recovery when the coordinator is missing or unreachable
  • Class names and file numbers no longer depend on the order files are processed in. Every plugin walks the files it can extract (the project's source and the included packages) before the first transform and numbers them in path order (a file collapsed into a bucket is numbered as the bucket, and only files that mention the package, StyleX or an aliased package take numbers) through the new seedFileMap, so identical builds give identical class prefixes, CSS file names and output hashes; in development, files that appear later are numbered after the existing ones. Styles are given class names in a fixed order, not the order of a hash set. The exported class, file and canonical maps are canonical JSON (sorted keys). Stylesheet evaluation (vanilla-extract) refuses Math.random(), Date.now(), new Date() and Date(), which differ between builds, with a build error; fixed dates such as new Date(0) are unchanged. resetBuildState starts a build from its own options: the prefix, atom hoisting, routes, buckets, names, numbers and styles an earlier build in the process left are cleared, and the prefix is set on every setup even when it is absent. Engine snapshots from concurrent transforms are written one after another per file, so an older snapshot is never written last. File lists are ordered by code point rather than the locale
  • Reconstruct the complete deterministic Next webpack stylesheet after each server, edge and client compiler reset, including accepted external package dependency closures, so delivered CSS matches rendered classes in per-file and single-CSS modes. Keep production CSS request paths stable across build IDs so persistent webpack cache cannot bypass the CSS loader. Genuine production prewarm collection, read and extraction failures now fail the build instead of silently dropping styles; failed initialization releases its build session without leaking styles into unrelated builds.
  • Accept JSX in .js, .mjs and .cjs throughout extraction without changing TypeScript dialects or plain JavaScript output, and pass parsed source-map objects to webpack callbacks for Next SWC compatibility. Malformed relevant source now fails with located parser diagnostics instead of unlocated parser-panic messages or silently recovered output.
  • Tailwind classes in a className read the Tailwind CSS of the project: the file that imports tailwindcss (the tailwind.css option of devup.json, else a conventional path) gives @theme variables (colors, spacing, shadows, blurs, radii, easings, animations with their @Keyframes, default transitions, breakpoints), @Utility utilities and @custom-variant variants, and the Vite, webpack and Next.js plugins read it again when it or a CSS file it imports changes. Anything that cannot be read exactly, and the classes of namespaces the build does not read, stay as written. More of Tailwind CSS v4 compiles: ring-, inset-ring-, ring-offset-* and drop-shadow-* compose with shadows and filters, borders and outlines draw in --tw-border-style and --tw-outline-style (a border now shows its style), line-clamp, text decoration, border-spacing, logical and scroll spacing, tables, perspective, font-stretch and more; the arguments of clsx(), classnames() and classNames() and [...].join(' ') compile in place. Radii follow v4 (rounded-sm is 0.25rem, rounded-full is calc(infinity * 1px)). No new build errors
  • Skip source-map generation in the codeExtractWithoutSourceMap WASM export while preserving extracted code, CSS and dependencies. Add real WASM and Next loader regressions for map-enabled and map-disabled extraction. No new build errors.

@devup-ui/plugin-utils@1.0.17 → 1.0.18 - packages/plugin-utils/package.json

Patch

  • 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
  • Class names and file numbers no longer depend on the order files are processed in. Every plugin walks the files it can extract (the project's source and the included packages) before the first transform and numbers them in path order (a file collapsed into a bucket is numbered as the bucket, and only files that mention the package, StyleX or an aliased package take numbers) through the new seedFileMap, so identical builds give identical class prefixes, CSS file names and output hashes; in development, files that appear later are numbered after the existing ones. Styles are given class names in a fixed order, not the order of a hash set. The exported class, file and canonical maps are canonical JSON (sorted keys). Stylesheet evaluation (vanilla-extract) refuses Math.random(), Date.now(), new Date() and Date(), which differ between builds, with a build error; fixed dates such as new Date(0) are unchanged. resetBuildState starts a build from its own options: the prefix, atom hoisting, routes, buckets, names, numbers and styles an earlier build in the process left are cleared, and the prefix is set on every setup even when it is absent. Engine snapshots from concurrent transforms are written one after another per file, so an older snapshot is never written last. File lists are ordered by code point rather than the locale
  • @emotion/css is aliased by default: css and keyframes compile like Devup UI's own (numbers in px, as Emotion reads them), injectGlobal declares global CSS. cx takes class names, as in Emotion: strings and runtime values are classes kept as written (falsy values are skipped), an object is a { name: condition } class map, arrays nest, and inline css() calls and css() classes compose with the rules css(a, b) uses. merge(className) splits what the build can read (string tokens, template literal parts) and composes the css() classes in it, keeping a runtime string it cannot split as it is. Namespace members, constant-string bracket access, const destructuring and const alias chains compile through these same named APIs with scope-aware binding resolution; numeric values retain Emotion units after constant inlining and build-time evaluation. The Emotion import disappears when no runtime-only member is used. Ambient declarations type the styling imports (cx(...args: ClassNamesArg[]), merge(className: string)). New located build errors: cx() cannot use a spread, a getter, a setter or a method in a class map, or a template literal call; merge() takes exactly one class string, not a spread or a template literal call; the root module has no default export, namespace member keys must be constant strings naming supported exports, and styling functions cannot be passed, mutated or re-exported. Optional styling reads/calls, namespace rest destructuring, styling defaults or nested patterns, non-const aliases, exported styling bindings and reads whose initialization order cannot be established are build errors instead of runtime dependencies. Styling loaded through require, import-equals or dynamic import must use static ES module imports; an unawaited dynamic namespace that the build cannot follow is also a located error. Its runtime-only APIs (cache, flush, hydrate, sheet, getRegisteredStyles) stay on @emotion/css, including their nested/default destructuring, optional reads and recognized runtime-loader access
  • Reconstruct the complete deterministic Next webpack stylesheet after each server, edge and client compiler reset, including accepted external package dependency closures, so delivered CSS matches rendered classes in per-file and single-CSS modes. Keep production CSS request paths stable across build IDs so persistent webpack cache cannot bypass the CSS loader. Genuine production prewarm collection, read and extraction failures now fail the build instead of silently dropping styles; failed initialization releases its build session without leaking styles into unrelated builds.
  • Tailwind classes in a className read the Tailwind CSS of the project: the file that imports tailwindcss (the tailwind.css option of devup.json, else a conventional path) gives @theme variables (colors, spacing, shadows, blurs, radii, easings, animations with their @Keyframes, default transitions, breakpoints), @Utility utilities and @custom-variant variants, and the Vite, webpack and Next.js plugins read it again when it or a CSS file it imports changes. Anything that cannot be read exactly, and the classes of namespaces the build does not read, stay as written. More of Tailwind CSS v4 compiles: ring-, inset-ring-, ring-offset-* and drop-shadow-* compose with shadows and filters, borders and outlines draw in --tw-border-style and --tw-outline-style (a border now shows its style), line-clamp, text decoration, border-spacing, logical and scroll spacing, tables, perspective, font-stretch and more; the arguments of clsx(), classnames() and classNames() and [...].join(' ') compile in place. Radii follow v4 (rounded-sm is 0.25rem, rounded-full is calc(infinity * 1px)). No new build errors

@devup-ui/react@1.0.44 → 1.0.45 - packages/react/package.json

Patch

  • The @devup-ui/react README and the css-utils-literal-only rule docs link their mention of build errors to the new Build Errors reference (https://devup-ui.com/docs/build-errors), which lists the build errors grouped by API; the docs sidebar lists the page after Supported Syntax & Limitations, whose Build errors section links to it. Documentation only: no new build errors
  • The styled-components and Emotion compat ThemeProvider now gives its theme through React context: useTheme, withTheme and the new ThemeConsumer return the real theme object (an object merged over the outer theme, or what a theme function makes of it) instead of an empty Proxy of var() references, withTheme forwards refs, lets a caller's theme win and sets a displayName. Styles keep reading the CSS variables the provider declares. styled-components v6.4 createTheme is available from the compat entry, and ThemeConsumer and createTheme imports are redirected there instead of staying a runtime dependency on styled-components
  • @emotion/css is aliased by default: css and keyframes compile like Devup UI's own (numbers in px, as Emotion reads them), injectGlobal declares global CSS. cx takes class names, as in Emotion: strings and runtime values are classes kept as written (falsy values are skipped), an object is a { name: condition } class map, arrays nest, and inline css() calls and css() classes compose with the rules css(a, b) uses. merge(className) splits what the build can read (string tokens, template literal parts) and composes the css() classes in it, keeping a runtime string it cannot split as it is. Namespace members, constant-string bracket access, const destructuring and const alias chains compile through these same named APIs with scope-aware binding resolution; numeric values retain Emotion units after constant inlining and build-time evaluation. The Emotion import disappears when no runtime-only member is used. Ambient declarations type the styling imports (cx(...args: ClassNamesArg[]), merge(className: string)). New located build errors: cx() cannot use a spread, a getter, a setter or a method in a class map, or a template literal call; merge() takes exactly one class string, not a spread or a template literal call; the root module has no default export, namespace member keys must be constant strings naming supported exports, and styling functions cannot be passed, mutated or re-exported. Optional styling reads/calls, namespace rest destructuring, styling defaults or nested patterns, non-const aliases, exported styling bindings and reads whose initialization order cannot be established are build errors instead of runtime dependencies. Styling loaded through require, import-equals or dynamic import must use static ES module imports; an unawaited dynamic namespace that the build cannot follow is also a located error. Its runtime-only APIs (cache, flush, hydrate, sheet, getRegisteredStyles) stay on @emotion/css, including their nested/default destructuring, optional reads and recognized runtime-loader access
  • Emotion's CacheProvider is available from the compat entry as a no-op that renders its children, and import { CacheProvider } from '@emotion/react' is redirected there instead of keeping @emotion/react as a runtime dependency; the cache it configures holds styles Emotion inserts at runtime, which Devup UI writes at build time
  • The README no longer claims every CSS-in-JS pattern compiles: it describes what the build compiles and that anything it cannot know is a CSS variable or a located build error, and links the new Supported Syntax & Limitations page, which lists what compiles, the style override rules (where a later part wins and where the CSS cascade decides), Baseline 2024 browser support, that a component taking the css prop must pass className and style on, the runtime-only APIs and how each is handled, and the known limitations
  • @devup-ui/react and @devup-ui/components take React as a peer of ^18 || ^19 instead of * and no longer depend on React 19 themselves, so a React 18 app no longer gets a second React. Select and Stepper read their context with useContext instead of React 19's use() and provide it with Context.Provider instead of React 19's (which crashes React 18), and Select repositions its container from an effect instead of a callback ref returning cleanup, which React 18 ignores
  • stylex.attrs() gives the dynamic CSS variables it sets as style attribute text ("--a:" + value), as StyleX does for spreading onto non-React targets, instead of a style object; stylex.props() keeps the object React takes. The attrs() return type declares style as a string

@devup-ui/reset-css@1.0.31 → 1.0.32 - packages/reset-css/package.json

Patch

  • Auto-update: depends on '@devup-ui/react' via a local workspace dependency

@devup-ui/rsbuild-plugin@1.0.67 → 1.0.68 - packages/rsbuild-plugin/package.json

Patch

  • 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
  • Class names and file numbers no longer depend on the order files are processed in. Every plugin walks the files it can extract (the project's source and the included packages) before the first transform and numbers them in path order (a file collapsed into a bucket is numbered as the bucket, and only files that mention the package, StyleX or an aliased package take numbers) through the new seedFileMap, so identical builds give identical class prefixes, CSS file names and output hashes; in development, files that appear later are numbered after the existing ones. Styles are given class names in a fixed order, not the order of a hash set. The exported class, file and canonical maps are canonical JSON (sorted keys). Stylesheet evaluation (vanilla-extract) refuses Math.random(), Date.now(), new Date() and Date(), which differ between builds, with a build error; fixed dates such as new Date(0) are unchanged. resetBuildState starts a build from its own options: the prefix, atom hoisting, routes, buckets, names, numbers and styles an earlier build in the process left are cleared, and the prefix is set on every setup even when it is absent. Engine snapshots from concurrent transforms are written one after another per file, so an older snapshot is never written last. File lists are ordered by code point rather than the locale
  • More of Tailwind CSS v4 compiles: container queries (@sm:, @max-md:, @min-[400px]:, @sm/main:) and @container utilities, mask-* (all but the gradient masks), text-shadow-, inset-shadow-, font-variant-numeric utilities (tabular-nums and the like), 3D transforms (rotate-x/y/z, translate-z, scale-z, translate-3d, scale-3d, transform-3d, backface-, perspective-, perspective-origin-), contain- and outline-hidden (with its forced-colors fallback). The project's Tailwind CSS is read further: functional @Utility with --value() and --modifier(), @apply in @Utility, and theme(--name) in arbitrary values; classes of @plugin entries stay as written. A className now compiles the classes of cn()/twMerge() calls whose arguments are all static (merged as tailwind-merge does, the later class wins), of consts bound to class strings, and of cva() calls (base, variants, compound variants). The Rsbuild plugin reads the Tailwind CSS again and compiles every module again when it changes, and the Bun plugin reads it again. No new build errors
  • Tailwind classes in a className read the Tailwind CSS of the project: the file that imports tailwindcss (the tailwind.css option of devup.json, else a conventional path) gives @theme variables (colors, spacing, shadows, blurs, radii, easings, animations with their @Keyframes, default transitions, breakpoints), @Utility utilities and @custom-variant variants, and the Vite, webpack and Next.js plugins read it again when it or a CSS file it imports changes. Anything that cannot be read exactly, and the classes of namespaces the build does not read, stay as written. More of Tailwind CSS v4 compiles: ring-, inset-ring-, ring-offset-* and drop-shadow-* compose with shadows and filters, borders and outlines draw in --tw-border-style and --tw-outline-style (a border now shows its style), line-clamp, text decoration, border-spacing, logical and scroll spacing, tables, perspective, font-stretch and more; the arguments of clsx(), classnames() and classNames() and [...].join(' ') compile in place. Radii follow v4 (rounded-sm is 0.25rem, rounded-full is calc(infinity * 1px)). No new build errors

@devup-ui/vite-plugin@1.0.73 → 1.0.74 - packages/vite-plugin/package.json

Patch

  • Class names and file numbers no longer depend on the order files are processed in. Every plugin walks the files it can extract (the project's source and the included packages) before the first transform and numbers them in path order (a file collapsed into a bucket is numbered as the bucket, and only files that mention the package, StyleX or an aliased package take numbers) through the new seedFileMap, so identical builds give identical class prefixes, CSS file names and output hashes; in development, files that appear later are numbered after the existing ones. Styles are given class names in a fixed order, not the order of a hash set. The exported class, file and canonical maps are canonical JSON (sorted keys). Stylesheet evaluation (vanilla-extract) refuses Math.random(), Date.now(), new Date() and Date(), which differ between builds, with a build error; fixed dates such as new Date(0) are unchanged. resetBuildState starts a build from its own options: the prefix, atom hoisting, routes, buckets, names, numbers and styles an earlier build in the process left are cleared, and the prefix is set on every setup even when it is absent. Engine snapshots from concurrent transforms are written one after another per file, so an older snapshot is never written last. File lists are ordered by code point rather than the locale
  • Tailwind classes in a className read the Tailwind CSS of the project: the file that imports tailwindcss (the tailwind.css option of devup.json, else a conventional path) gives @theme variables (colors, spacing, shadows, blurs, radii, easings, animations with their @Keyframes, default transitions, breakpoints), @Utility utilities and @custom-variant variants, and the Vite, webpack and Next.js plugins read it again when it or a CSS file it imports changes. Anything that cannot be read exactly, and the classes of namespaces the build does not read, stay as written. More of Tailwind CSS v4 compiles: ring-, inset-ring-, ring-offset-* and drop-shadow-* compose with shadows and filters, borders and outlines draw in --tw-border-style and --tw-outline-style (a border now shows its style), line-clamp, text decoration, border-spacing, logical and scroll spacing, tables, perspective, font-stretch and more; the arguments of clsx(), classnames() and classNames() and [...].join(' ') compile in place. Radii follow v4 (rounded-sm is 0.25rem, rounded-full is calc(infinity * 1px)). No new build errors

@devup-ui/webpack-plugin@1.0.71 → 1.0.72 - packages/webpack-plugin/package.json

Patch

  • 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
  • Class names and file numbers no longer depend on the order files are processed in. Every plugin walks the files it can extract (the project's source and the included packages) before the first transform and numbers them in path order (a file collapsed into a bucket is numbered as the bucket, and only files that mention the package, StyleX or an aliased package take numbers) through the new seedFileMap, so identical builds give identical class prefixes, CSS file names and output hashes; in development, files that appear later are numbered after the existing ones. Styles are given class names in a fixed order, not the order of a hash set. The exported class, file and canonical maps are canonical JSON (sorted keys). Stylesheet evaluation (vanilla-extract) refuses Math.random(), Date.now(), new Date() and Date(), which differ between builds, with a build error; fixed dates such as new Date(0) are unchanged. resetBuildState starts a build from its own options: the prefix, atom hoisting, routes, buckets, names, numbers and styles an earlier build in the process left are cleared, and the prefix is set on every setup even when it is absent. Engine snapshots from concurrent transforms are written one after another per file, so an older snapshot is never written last. File lists are ordered by code point rather than the locale
  • Reconstruct the complete deterministic Next webpack stylesheet after each server, edge and client compiler reset, including accepted external package dependency closures, so delivered CSS matches rendered classes in per-file and single-CSS modes. Keep production CSS request paths stable across build IDs so persistent webpack cache cannot bypass the CSS loader. Genuine production prewarm collection, read and extraction failures now fail the build instead of silently dropping styles; failed initialization releases its build session without leaking styles into unrelated builds.
  • Accept JSX in .js, .mjs and .cjs throughout extraction without changing TypeScript dialects or plain JavaScript output, and pass parsed source-map objects to webpack callbacks for Next SWC compatibility. Malformed relevant source now fails with located parser diagnostics instead of unlocated parser-panic messages or silently recovered output.
  • Tailwind classes in a className read the Tailwind CSS of the project: the file that imports tailwindcss (the tailwind.css option of devup.json, else a conventional path) gives @theme variables (colors, spacing, shadows, blurs, radii, easings, animations with their @Keyframes, default transitions, breakpoints), @Utility utilities and @custom-variant variants, and the Vite, webpack and Next.js plugins read it again when it or a CSS file it imports changes. Anything that cannot be read exactly, and the classes of namespaces the build does not read, stay as written. More of Tailwind CSS v4 compiles: ring-, inset-ring-, ring-offset-* and drop-shadow-* compose with shadows and filters, borders and outlines draw in --tw-border-style and --tw-outline-style (a border now shows its style), line-clamp, text decoration, border-spacing, logical and scroll spacing, tables, perspective, font-stretch and more; the arguments of clsx(), classnames() and classNames() and [...].join(' ') compile in place. Radii follow v4 (rounded-sm is 0.25rem, rounded-full is calc(infinity * 1px)). No new build errors

@codecov

codecov Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
packages/next-plugin/src/plugin.ts 100.00% <100.00%> (ø)
packages/next-plugin/src/prewarm.ts 100.00% <100.00%> (ø)
packages/plugin-utils/src/import-graph.ts 100.00% <100.00%> (ø)
packages/webpack-plugin/src/plugin.ts 100.00% <100.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

owjs3901 added a commit that referenced this pull request Oct 17, 2026
owjs3901 added a commit that referenced this pull request Oct 17, 2026
@owjs3901
owjs3901 merged commit b0401cb into main Oct 17, 2026
5 checks passed
@owjs3901
owjs3901 deleted the fix/next-webpack-css-delivery branch October 17, 2026 15:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant