Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changepacks/changepack_log_release_compat.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": {
"bindings/devup-ui-wasm/package.json": "Patch",
"packages/next-plugin/package.json": "Patch",
"packages/bun-plugin/package.json": "Patch",
"packages/plugin-utils/package.json": "Patch"
},
"note": "Add an internal aggregate compiler-state reset for isolated tests, scoped native cleanup and Next test-cache isolation; verify real Bun registration callbacks and packed production consumers across bundlers and browsers. No new application build errors are introduced.",
"date": "2026-10-07T00:00:00.000Z"
}
62 changes: 61 additions & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,66 @@ jobs:
- name: Run E2E Tests (singleCss)
run: bun run test:e2e:rsc-css

release-compat:
runs-on: ubuntu-latest
timeout-minutes: 30
env:
CARGO_BUILD_JOBS: "2"
steps:
- name: Checkout
uses: actions/checkout@v7
- uses: actions-rust-lang/setup-rust-toolchain@v2
with:
toolchain: stable
components: clippy, rustfmt
- name: Cache cargo registry + target
uses: actions/cache@v6
with:
path: |
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: cargo-release-compat-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Cache bun dependencies
uses: actions/cache@v6
with:
path: ~/.bun/install/cache
key: bun-release-compat-${{ runner.os }}-${{ hashFiles('**/bun.lock') }}
- uses: jetli/wasm-pack-action@v0.4.0
with:
version: latest
- uses: actions/setup-node@v7
with:
node-version: latest
- run: bun install
- name: Test exited-process memory sampling
run: node --test e2e/release/process-memory.test.mjs
- name: Build packages under the memory watchdog
run: node e2e/release/build-packages.mjs
- name: Cache Playwright browsers
uses: actions/cache@v6
with:
path: ~/.cache/ms-playwright
key: playwright-release-compat-${{ runner.os }}-${{ hashFiles('**/bun.lock') }}
- name: Install Playwright browsers
run: bunx playwright install chromium firefox webkit --with-deps
- name: Build packed consumers and check production computed styles
run: bun run test:release
- name: Upload release compatibility evidence
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v7
with:
name: release-compat-results
path: |
release-package-build.json
release-results.json
if-no-files-found: error
retention-days: 30

webpack-extends-theme:
runs-on: ubuntu-latest
timeout-minutes: 45
Expand Down Expand Up @@ -697,7 +757,7 @@ jobs:
# passed, and is the only job that can write contents, open pull requests or
# mint an OIDC token for npm.
release:
needs: [publish, landing-next-e2e, react-18]
needs: [publish, landing-next-e2e, react-18, release-compat]
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
timeout-minutes: 45
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,5 @@ playwright-report
/.changepacks/publish.tgz
benchmark-run.json
wasm-size.json
release-results.json
release-package-build.json
25 changes: 21 additions & 4 deletions bindings/devup-ui-wasm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,30 @@ Everything is exported from `pkg/index.js` (types in `pkg/index.d.ts`).
## State

The module keeps its state, the sheet, class maps, theme, prefix and debug flag,
in one global per process, and there is no single reset. Two consequences:
in one global per process. Two consequences:

- Register the theme, shorthands, prefix and resolver before the first
`codeExtract`, and extract the files of one build in one process.
- Tests that share a process share that state. Set what a test depends on
(`registerTheme`, `setPrefix`, `setDebug`, `importSheet`) at its start, and
run files that need an empty sheet in their own process.
- Tests that share a process share that state. The internal/testing-only
`resetStateForTesting()` export (available in normal artifacts) clears the
sheet, resolver, prefix/debug/hoist configuration, class/file/canonical/route
maps, reservation phase/miss count, frozen atom plan, shorthands, the complete
theme-token and Tailwind definition registries, and imported stylesheet caches. Call
only at a quiescent test boundary, never in a resolver callback or between
environments of a build that intentionally share state. Native caches reset
on the calling thread.

`resetBuildState()` remains the separate production build boundary. It clears
allocator bookkeeping, maps/routes, atom threshold/plan, prefix, sheet and resolver,
but leaves CSS theme-key configuration, shorthands and debug for the next build's
configuration path to overwrite. It is not an alias for the full testing reset.

Rust tests use hidden `css::test_state::TestStateGuard` and
`extractor::test_state::TestStateGuard` support compiled into dependencies so
dependent-crate tests can restore owned snapshots on normal exit, nesting and
unwind. Binding tests compose these with sheet/resolver snapshots. Keep a named
guard on its creating thread and retain `#[serial]`: guards clean up but do not
serialize concurrent tests. Cleanup does not run for panic=abort.

## Errors

Expand Down
1 change: 1 addition & 0 deletions bindings/devup-ui-wasm/src/atom_hoist_measurement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ struct Measurement {
}

fn build(modules: &[Module], hoist: bool) -> Result<Snapshot, ExtractionError> {
let _scope = super::TestStateGuard::new();
reset_build_state_internal();
let _state = BuildState;
register_theme_internal(sheet::theme::Theme::default());
Expand Down
5 changes: 5 additions & 0 deletions bindings/devup-ui-wasm/src/atom_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export const view = <Box className={cls} styleOrder={2} bg="blue"
#[test]
#[serial]
fn atom_references_and_output_are_stable_when_extraction_order_changes() {
let _scope = TestStateGuard::new();
// Given
let build = |reverse: bool| {
setup();
Expand Down Expand Up @@ -142,6 +143,7 @@ fn atom_references_and_output_are_stable_when_extraction_order_changes() {
#[test]
#[serial]
fn atom_names_and_emission_are_stable_when_debug_changes_with_prefix() {
let _scope = TestStateGuard::new();
// Given
let build = |debug| {
setup();
Expand Down Expand Up @@ -177,6 +179,7 @@ fn atom_names_and_emission_are_stable_when_debug_changes_with_prefix() {
#[test]
#[serial]
fn same_atom_stays_local_when_a_private_bucket_also_uses_it() {
let _scope = TestStateGuard::new();
// Given
setup();
let source = r#"import {Box} from '@devup-ui/react'; export const x=<Box color="red"/>;"#;
Expand Down Expand Up @@ -205,6 +208,7 @@ fn same_atom_stays_local_when_a_private_bucket_also_uses_it() {
#[test]
#[serial]
fn compat_apis_reference_emitted_atoms_when_bucket_is_predeclared() {
let _scope = TestStateGuard::new();
// Given
setup();
let sources = [
Expand Down Expand Up @@ -234,6 +238,7 @@ fn compat_apis_reference_emitted_atoms_when_bucket_is_predeclared() {
#[test]
#[serial]
fn local_ordered_styles_signal_shared_layer_order_changes() {
let _scope = TestStateGuard::new();
// Given
setup();
let plain = compile(
Expand Down
6 changes: 6 additions & 0 deletions bindings/devup-ui-wasm/src/atom_tests/lifecycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const RED: &str = "import {Box} from '@devup-ui/react'; export const x=<Box colo
#[test]
#[serial]
fn unknown_and_late_reach_stay_local_until_reset() {
let _scope = TestStateGuard::new();
// Given
setup();
reserve(&[("a.tsx", RED), ("private.tsx", RED)]);
Expand Down Expand Up @@ -38,6 +39,7 @@ fn unknown_and_late_reach_stay_local_until_reset() {
#[test]
#[serial]
fn imported_sheet_keeps_recorded_placement_after_configuration_reset() {
let _scope = TestStateGuard::new();
// Given
setup();
let shared = compile("a.tsx", RED);
Expand Down Expand Up @@ -88,6 +90,7 @@ fn imported_sheet_keeps_recorded_placement_after_configuration_reset() {
#[test]
#[serial]
fn canonical_bucket_reach_is_frozen_before_imported_styles_are_processed() {
let _scope = TestStateGuard::new();
// Given
setup();
import_canonical_map_internal(HashMap::from([
Expand Down Expand Up @@ -130,6 +133,7 @@ fn canonical_bucket_reach_is_frozen_before_imported_styles_are_processed() {
#[test]
#[serial]
fn important_and_plain_dynamic_values_emit_distinct_rules_in_every_mode() {
let _scope = TestStateGuard::new();
// Given
let source = r"import {Box} from '@devup-ui/react';
export const x=<><Box color={tone}/><Box color={differentCode}/><Box color={`${tone} !important`}/></>;";
Expand Down Expand Up @@ -166,6 +170,7 @@ fn important_and_plain_dynamic_values_emit_distinct_rules_in_every_mode() {
#[test]
#[serial]
fn keyframe_content_and_scope_resolve_when_using_single_css() {
let _scope = TestStateGuard::new();
// Given
let source = r"import {Box,keyframes} from '@devup-ui/react';
const a=keyframes({from:{opacity:0,transform:'scale(0)'},to:{opacity:1}});
Expand Down Expand Up @@ -223,6 +228,7 @@ fn keyframe_content_and_scope_resolve_when_using_single_css() {
#[test]
#[serial]
fn global_css_keyframes_use_the_same_scope_as_their_declaration() {
let _scope = TestStateGuard::new();
// Given
setup();
let source = r"import {Box,keyframes,globalCss} from '@devup-ui/react';
Expand Down
1 change: 1 addition & 0 deletions bindings/devup-ui-wasm/src/atom_tests/resumption.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use super::*;
#[test]
#[serial]
fn full_state_restore_preserves_all_symbol_slots_and_appends_new_identities() {
let _scope = TestStateGuard::new();
// Given
setup();
reserve(&[("a.tsx", SOURCE_A)]);
Expand Down
4 changes: 4 additions & 0 deletions bindings/devup-ui-wasm/src/atom_tests/single_css.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ fn build(atom: bool, debug: bool) -> (String, String, String) {
#[test]
#[serial]
fn w66_single_css_output_is_exactly_invariant_to_requested_atom_mode() {
let _scope = TestStateGuard::new();
// Given / When / Then
for debug in [false, true] {
assert_eq!(build(false, debug), build(true, debug));
Expand All @@ -54,6 +55,7 @@ fn w66_single_css_output_is_exactly_invariant_to_requested_atom_mode() {
#[test]
#[serial]
fn w66_single_css_does_not_freeze_empty_reach_before_per_file_reservation() {
let _scope = TestStateGuard::new();
// Given
setup();
css::file_routes::reset_file_routes();
Expand Down Expand Up @@ -88,6 +90,7 @@ fn w66_single_css_does_not_freeze_empty_reach_before_per_file_reservation() {
#[test]
#[serial]
fn single_css_error_and_resolver_unwind_restore_effective_atom_mode() {
let _scope = TestStateGuard::new();
// Given
setup();
let option = ExtractOption {
Expand Down Expand Up @@ -124,6 +127,7 @@ fn single_css_error_and_resolver_unwind_restore_effective_atom_mode() {
#[test]
#[serial]
fn canonical_global_placement_keeps_atom_mode_in_a_per_file_build() {
let _scope = TestStateGuard::new();
// Given
setup();
import_canonical_map_internal(HashMap::from([("global.tsx".into(), "@global".into())]));
Expand Down
2 changes: 2 additions & 0 deletions bindings/devup-ui-wasm/src/cache_protocol_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ fn legacy_import_changes_cache_generation_when_exported(
#[case] with_styles: bool,
#[case] atom_mode: bool,
) {
let _scope = TestStateGuard::new();
// Given
reset_build_state_internal();
let mut legacy = StyleSheet::default();
Expand Down Expand Up @@ -47,6 +48,7 @@ fn legacy_import_changes_cache_generation_when_exported(
#[test]
#[serial]
fn exported_generation_roundtrips_frozen_atom_placement() {
let _scope = TestStateGuard::new();
// Given
reset_build_state_internal();
css::atom_hoist::set_atom_hoist(Some(2));
Expand Down
13 changes: 13 additions & 0 deletions bindings/devup-ui-wasm/src/css_output_edge_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ fn numeric_edges_emit_css(
#[case] property: &str,
#[case] value: &str,
) -> Result<(), String> {
let _scope = super::TestStateGuard::new();
let source =
format!("import {{ Box }} from '@devup-ui/react';\nconst e = <Box {key}={{3}} />;");
let output = compile(&source)?;
Expand All @@ -73,6 +74,7 @@ fn numeric_edges_emit_css(
#[test]
#[serial]
fn flat_responsive_values_emit_css_after_base() -> Result<(), String> {
let _scope = super::TestStateGuard::new();
let source = "import { Box } from '@devup-ui/react';\nconst e = <Box p={[1, null, 2]} />;";
let output = compile(source)?;
let css = output.css().ok_or("expected emitted CSS")?;
Expand All @@ -88,6 +90,7 @@ fn flat_responsive_values_emit_css_after_base() -> Result<(), String> {
#[test]
#[serial]
fn selector_escape_hatches_emit_scoped_css() -> Result<(), String> {
let _scope = super::TestStateGuard::new();
let source = "import { Box } from '@devup-ui/react';\nconst e = <Box selectors={{ ':hover': { color: 'red' }, '& > p': { color: 'blue' } }} />;";
let output = compile(source)?;
let css = output.css().ok_or("expected emitted CSS")?;
Expand All @@ -102,6 +105,7 @@ fn selector_escape_hatches_emit_scoped_css() -> Result<(), String> {
#[test]
#[serial]
fn supported_experimental_and_unknown_properties_still_emit_css() -> Result<(), String> {
let _scope = super::TestStateGuard::new();
let source = "import { Box } from '@devup-ui/react';\nconst e = <Box strokeColor='red' imeMode='active' futureProperty='custom' WebkitBoxOrient='vertical' p={2} />;";
let output = compile(source)?;
let css = output.css().ok_or("expected emitted CSS")?;
Expand Down Expand Up @@ -129,6 +133,7 @@ fn supported_experimental_and_unknown_properties_still_emit_css() -> Result<(),
)]
#[serial]
fn dead_properties_fail_before_css_emission(#[case] source: &str) -> Result<(), String> {
let _scope = super::TestStateGuard::new();
let error = compile(source)
.err()
.ok_or("expected a declaration error")?;
Expand All @@ -140,6 +145,7 @@ fn dead_properties_fail_before_css_emission(#[case] source: &str) -> Result<(),
#[test]
#[serial]
fn nested_responsive_arrays_fail_before_css_emission() -> Result<(), String> {
let _scope = super::TestStateGuard::new();
let source = "import { Box } from '@devup-ui/react';\nconst e = <Box color={[[\"red\", \"blue\"], \"green\"]} />;";
let error = compile(source)
.err()
Expand All @@ -156,6 +162,7 @@ fn nested_responsive_arrays_fail_before_css_emission() -> Result<(), String> {
fn escaped_string_css_dead_names_fail_before_emission(
#[case] statement: &str,
) -> Result<(), String> {
let _scope = super::TestStateGuard::new();
let source = format!("import {{ css }} from '@devup-ui/react';\n{statement}");
let error = compile(&source)
.err()
Expand All @@ -168,6 +175,7 @@ fn escaped_string_css_dead_names_fail_before_emission(
#[test]
#[serial]
fn configured_shorthand_names_keep_their_valid_targets() -> Result<(), String> {
let _scope = super::TestStateGuard::new();
css::set_custom_shorthands(BTreeMap::from([(
"boxAlign".to_string(),
vec!["align-items".to_string()],
Expand Down Expand Up @@ -199,6 +207,7 @@ fn evaluated_declarations_report_authored_calls(
#[case] source: &str,
#[case] line: usize,
) -> Result<(), String> {
let _scope = super::TestStateGuard::new();
let error = compile_file("css-edges.css.ts", source)
.err()
.ok_or("expected a declaration error")?;
Expand Down Expand Up @@ -227,6 +236,7 @@ fn same_line_import_aliases_keep_authored_columns(
#[case] source: &str,
#[case] token: &str,
) -> Result<(), String> {
let _scope = super::TestStateGuard::new();
let column = source.find(token).ok_or("fixture has no expected token")? + 1;
let error = compile_file("css-edges.css.ts", source)
.err()
Expand All @@ -241,6 +251,7 @@ fn same_line_import_aliases_keep_authored_columns(
#[test]
#[serial]
fn computed_css_text_reports_its_authored_call() -> Result<(), String> {
let _scope = super::TestStateGuard::new();
let source = "import { css } from '@devup-ui/react';\nconst c = css('box-' + 'align:center');";
let error = compile(source)
.err()
Expand All @@ -253,6 +264,7 @@ fn computed_css_text_reports_its_authored_call() -> Result<(), String> {
#[test]
#[serial]
fn stylex_dead_declarations_fail_before_css_emission() -> Result<(), String> {
let _scope = super::TestStateGuard::new();
let statement = "export const c = stylex.create({ x: { boxAlign: 'center' } });";
let source = format!("import * as stylex from '@stylexjs/stylex';\n{statement}");
let column = statement.find("boxAlign").ok_or("fixture lacks property")? + 1;
Expand Down Expand Up @@ -281,6 +293,7 @@ fn stylesheet_nested_arrays_report_authored_locations(
#[case] source: &str,
#[case] location: &str,
) -> Result<(), String> {
let _scope = super::TestStateGuard::new();
let error = compile_file("css-edges.css.ts", source)
.err()
.ok_or("expected a responsive array error")?;
Expand Down
Loading
Loading