diff --git a/.changepacks/changepack_log_emotion_exports.json b/.changepacks/changepack_log_emotion_exports.json new file mode 100644 index 000000000..88bd30287 --- /dev/null +++ b/.changepacks/changepack_log_emotion_exports.json @@ -0,0 +1,8 @@ +{ + "changes": { + "packages/react/package.json": "Patch", + "bindings/devup-ui-wasm/package.json": "Patch" + }, + "note": "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", + "date": "2026-10-01T00:00:00.000Z" +} diff --git a/libs/extractor/src/import_alias_visit.rs b/libs/extractor/src/import_alias_visit.rs index c5eb83c46..acd776ac9 100644 --- a/libs/extractor/src/import_alias_visit.rs +++ b/libs/extractor/src/import_alias_visit.rs @@ -220,6 +220,7 @@ fn devup_equivalent(source: &str, imported: &str) -> Option (_, "isStyledComponent") => Some(DevupTarget::Compat("isStyledComponent")), (_, "withTheme") => Some(DevupTarget::Compat("withTheme")), (_, "useTheme") => Some(DevupTarget::Compat("useTheme")), + ("@emotion/react", "CacheProvider") => Some(DevupTarget::Compat("CacheProvider")), ("styled-components", "ThemeConsumer") => Some(DevupTarget::Compat("ThemeConsumer")), ("styled-components", "createTheme") => Some(DevupTarget::Compat("createTheme")), _ => None, @@ -921,6 +922,21 @@ import { createSprinkles } from '@vanilla-extract/sprinkles'", ); } + #[test] + fn test_emotion_cache_provider_redirects_to_compat() { + let code = transform_import_aliases( + "import { CacheProvider, Global } from '@emotion/react'", + "test.tsx", + "@devup-ui/react", + &HashMap::from([("@emotion/react".to_string(), ImportAlias::NamedToNamed)]), + ); + assert!( + code.contains("import { CacheProvider, Global } from '@devup-ui/react/compat';"), + "{code}" + ); + assert!(!code.contains("from '@emotion/react'"), "{code}"); + } + #[test] fn test_emotion_css_maps_onto_devup_equivalents() { let code = transform_import_aliases( diff --git a/packages/react/src/compat/__tests__/index.test.ts b/packages/react/src/compat/__tests__/index.test.ts index 5d7e97de0..e06fd8231 100644 --- a/packages/react/src/compat/__tests__/index.test.ts +++ b/packages/react/src/compat/__tests__/index.test.ts @@ -16,6 +16,7 @@ describe('compat entry', () => { useTheme: expect.any(Function), withTheme: expect.any(Function), + CacheProvider: expect.any(Function), isStyledComponent: expect.any(Function), ServerStyleSheet: expect.any(Function), StyleSheetManager: expect.any(Function), diff --git a/packages/react/src/compat/emotion.d.ts b/packages/react/src/compat/emotion.d.ts index b3d2ef0e4..feb514cd4 100644 --- a/packages/react/src/compat/emotion.d.ts +++ b/packages/react/src/compat/emotion.d.ts @@ -14,6 +14,7 @@ declare module '@emotion/react' { export { css, keyframes } from '@devup-ui/react' export type { StyledTheme as Theme } from '@devup-ui/react/compat' export { + CacheProvider, Global, ThemeProvider, useTheme, diff --git a/packages/react/src/compat/index.ts b/packages/react/src/compat/index.ts index 160d402ba..7155f3e4d 100644 --- a/packages/react/src/compat/index.ts +++ b/packages/react/src/compat/index.ts @@ -15,6 +15,7 @@ export { createTheme } from '../utils/create-theme' export type { ClassNamesArg } from '../utils/emotion-classes' export { cx, merge } from '../utils/emotion-classes' export { + CacheProvider, isStyledComponent, ServerStyleSheet, StyleSheetManager, diff --git a/packages/react/src/utils/__tests__/styled-compat.test.ts b/packages/react/src/utils/__tests__/styled-compat.test.ts index 3a7b15091..0caeb0490 100644 --- a/packages/react/src/utils/__tests__/styled-compat.test.ts +++ b/packages/react/src/utils/__tests__/styled-compat.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it } from 'bun:test' import { + CacheProvider, isStyledComponent, ServerStyleSheet, StyleSheetManager, @@ -23,6 +24,13 @@ describe('StyleSheetManager', () => { }) }) +describe('CacheProvider', () => { + it('renders its children untouched', () => { + expect(CacheProvider({ value: {}, children: 'child' })).toBe('child') + expect(CacheProvider({})).toBeUndefined() + }) +}) + describe('isStyledComponent', () => { it('always reports false', () => { expect(isStyledComponent(() => null)).toBe(false) diff --git a/packages/react/src/utils/styled-compat.ts b/packages/react/src/utils/styled-compat.ts index 517ad4d4f..63c346289 100644 --- a/packages/react/src/utils/styled-compat.ts +++ b/packages/react/src/utils/styled-compat.ts @@ -35,6 +35,19 @@ export function StyleSheetManager({ return children } +/** + * Emotion's `CacheProvider`, kept as a no-op: the cache it configures holds + * styles Emotion would insert at runtime, and Devup UI writes them at build time + */ +export function CacheProvider({ + children, +}: { + value?: unknown + children?: ReactNode +}): ReactNode { + return children +} + export function isStyledComponent(_target: unknown): boolean { return false }