From 250e052f44dd313b658abebc707242a7b25be340 Mon Sep 17 00:00:00 2001 From: Hubert Bieszczad <48803618+Brentlok@users.noreply.github.com> Date: Mon, 5 Oct 2026 07:57:04 +0200 Subject: [PATCH] fix(mobile): upgrade Uniwind and remove local patch (#14597) --- apps/mobile/package.json | 2 +- .../src/lib/uniwind-platform-variants.test.ts | 10 +- docs/internals/mobile-development.md | 11 +- patches/uniwind@1.11.0.patch | 1011 ----------------- pnpm-lock.yaml | 11 +- pnpm-workspace.yaml | 2 +- 6 files changed, 16 insertions(+), 1031 deletions(-) delete mode 100644 patches/uniwind@1.11.0.patch diff --git a/apps/mobile/package.json b/apps/mobile/package.json index df0d8aea4c52d2d5378f642d36c71ddc6b19ae52..602ffb66bacea68f64d7f1c4789f7f6b94e6135d 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -127,7 +127,7 @@ "react-native-worklets": "0.13.0", "shiki": "4.2.0", "tailwind-merge": "^3.5.0", - "uniwind": "1.11.0" + "uniwind": "1.12.1" }, "devDependencies": { "@effect/vitest": "catalog:", diff --git a/apps/mobile/src/lib/uniwind-platform-variants.test.ts b/apps/mobile/src/lib/uniwind-platform-variants.test.ts index 12fa86b33c088c8d9fcc06926977bf3402c3b205..32e0323e7a19d8fee54ec045ce5a57d02c287f6e 100644 --- a/apps/mobile/src/lib/uniwind-platform-variants.test.ts +++ b/apps/mobile/src/lib/uniwind-platform-variants.test.ts @@ -10,7 +10,7 @@ import { beforeAll, describe, expect, it } from "vite-plus/test"; // `@media ios { ... }` block, and uniwind's CSS processor used to drop the // block's media queries after its first nested rule. Everything past the first // utility compiled unguarded and shipped to both platforms. These tests run -// the installed (patched) uniwind compiler over real Tailwind output, per +// the installed uniwind compiler over real Tailwind output, per // platform, and assert what each bundle receives. The compiler itself runs in // a plain Node child process (uniwind-platform-variants.fixture.cjs) so no // test-runner module transforms sit between the test and the shipped code. @@ -215,11 +215,9 @@ describe("uniwind keeps media rules nested inside class rules attached to the cl }); }); -// The patch file carries three independent uniwind fixes (state/data selector -// variants, the Metro native-styles fingerprint, and this media-query scoping -// fix). Regenerating it for one fix must not silently drop the others — this -// suite exercises the shipped transformer and compiler for the other two. -describe("uniwind patch keeps pre-existing selector and transformer behavior", () => { +// Keep guarding selector variants and the Metro native-styles fingerprint +// alongside media-query scoping when upgrading the installed Uniwind package. +describe("uniwind keeps selector and transformer behavior", () => { let output: FixtureOutput; beforeAll(() => { diff --git a/docs/internals/mobile-development.md b/docs/internals/mobile-development.md index 9f0cb9d2e2fb4c72992e65b53d0ea8dc81d93532..d1d375584bb02594163acf3a7024ae3fec95c960 100644 --- a/docs/internals/mobile-development.md +++ b/docs/internals/mobile-development.md @@ -17,12 +17,11 @@ shutdown, but a supervisor created after its cleanup runs would escape it. A clo parent scope also closes late arrivals, preventing interrupted startup or runtime replacement from leaving a WebSocket alive outside the new registry. -The [Uniwind patch](../../patches/uniwind@1.11.0.patch) still compiles CSS on Metro -updates so newly used classes are discovered. It skips global style invalidation -only when the generated stylesheet and theme list are unchanged. Skipping compilation -would lose new classes; invalidating every consumer for unchanged output makes an -ordinary component edit refresh the whole app. The fingerprint is recorded only -after initialization succeeds. +Uniwind compiles CSS on Metro updates so newly used classes are discovered. It +skips global style invalidation only when the generated stylesheet and theme list +are unchanged. Skipping compilation would lose new classes; invalidating every +consumer for unchanged output makes an ordinary component edit refresh the whole +app. The fingerprint is recorded only after initialization succeeds. The [expo-notifications patch](../../patches/expo-notifications@57.0.15.patch) protects `NotificationCenterManager`'s delegates and pending responses with a lock. React runtimes can diff --git a/patches/uniwind@1.11.0.patch b/patches/uniwind@1.11.0.patch deleted file mode 100644 index 4a418bf6b077e69ecdae08e19e0cd692fbcafa45..0000000000000000000000000000000000000000 --- a/patches/uniwind@1.11.0.patch +++ /dev/null @@ -1,1011 +0,0 @@ -diff --git a/dist/common/bundler/css-processor/processor.js b/dist/common/bundler/css-processor/processor.js -index 7dd369f..48321d1 100644 ---- a/dist/common/bundler/css-processor/processor.js -+++ b/dist/common/bundler/css-processor/processor.js -@@ -105,6 +105,64 @@ class ProcessorBuilder { - style.importantProperties.push(declaration.property); - } - } -+ readSelectorVariants(tokens) { -+ const variants = { -+ rtl: null, -+ theme: null, -+ active: null, -+ focus: null, -+ disabled: null, -+ dataAttributes: null -+ }; -+ tokens.forEach((token) => { -+ if (token.type === "pseudo-class" && token.kind === "where") { -+ token.selectors.forEach((compound) => { -+ compound.forEach((inner) => { -+ if (inner.type === "class" && this.bundlerConfig.themes.includes(inner.name)) { -+ variants.theme = inner.name; -+ } -+ if (inner.type === "pseudo-class" && inner.kind === "dir") { -+ variants.rtl = inner.direction === "rtl"; -+ } -+ }); -+ }); -+ } -+ if (token.type === "pseudo-class" && token.kind === "active") { -+ variants.active = true; -+ } -+ if (token.type === "pseudo-class" && token.kind === "focus") { -+ variants.focus = true; -+ } -+ if (token.type === "pseudo-class" && token.kind === "disabled") { -+ variants.disabled = true; -+ } -+ if (token.type === "attribute" && token.operation === null && token.name.startsWith("data-")) { -+ variants.dataAttributes ??= {}; -+ variants.dataAttributes[token.name] = `"true"`; -+ } -+ if (token.type === "attribute" && token.operation?.operator === "equal" && token.name.startsWith("data-")) { -+ variants.dataAttributes ??= {}; -+ variants.dataAttributes[token.name] = `"${token.operation.value}"`; -+ } -+ }); -+ const hasVariants = Object.values(variants).some(_utils.isDefined); -+ return { variants, hasVariants }; -+ } -+ withSelectorVariants(variants, run) { -+ this.declarationConfig.rtl ??= variants.rtl; -+ this.declarationConfig.theme ??= variants.theme; -+ this.declarationConfig.active ??= variants.active; -+ this.declarationConfig.focus ??= variants.focus; -+ this.declarationConfig.disabled ??= variants.disabled; -+ this.declarationConfig.dataAttributes ??= variants.dataAttributes; -+ run(); -+ this.declarationConfig.rtl = null; -+ this.declarationConfig.theme = null; -+ this.declarationConfig.active = null; -+ this.declarationConfig.focus = null; -+ this.declarationConfig.disabled = null; -+ this.declarationConfig.dataAttributes = null; -+ } - parseRuleRec(rule) { - if (this.declarationConfig.className !== null) { - const lastStyle = this.stylesheets[this.declarationConfig.className]?.at(-1); -@@ -120,64 +178,30 @@ class ProcessorBuilder { - this.declarationConfig.className = newClassName; - this.stylesheets[newClassName] ??= []; - this.stylesheets[newClassName].push({}); -- rule.value.declarations?.declarations?.forEach(declaration => this.addDeclaration(declaration)); -- rule.value.declarations?.importantDeclarations?.forEach(declaration => this.addDeclaration(declaration, true)); -- rule.value.rules?.forEach(rule2 => this.parseRuleRec(rule2)); -- return; -- } -- let rtl = null; -- let theme = null; -- let active = null; -- let focus = null; -- let disabled = null; -- let dataAttributes = null; -- selector.forEach(selector2 => { -- if (selector2.type === "pseudo-class" && selector2.kind === "where") { -- selector2.selectors.forEach(selector3 => { -- selector3.forEach(selector4 => { -- if (selector4.type === "class" && this.bundlerConfig.themes.includes(selector4.name)) { -- theme = selector4.name; -- } -- if (selector4.type === "pseudo-class" && selector4.kind === "dir") { -- rtl = selector4.direction === "rtl"; -- } -- }); -- }); -- } -- if (selector2.type === "pseudo-class" && selector2.kind === "active") { -- active = true; -+ const trailing = selector.slice(1); -+ const { variants, hasVariants } = this.readSelectorVariants(trailing); -+ if (trailing.length > 0 && !hasVariants) { -+ return; - } -- if (selector2.type === "pseudo-class" && selector2.kind === "focus") { -- focus = true; -- } -- if (selector2.type === "pseudo-class" && selector2.kind === "disabled") { -- disabled = true; -- } -- if (selector2.type === "attribute" && selector2.operation === null && selector2.name.startsWith("data-")) { -- dataAttributes ??= {}; -- dataAttributes[selector2.name] = `"true"`; -- } -- if (selector2.type === "attribute" && selector2.operation?.operator === "equal" && selector2.name.startsWith("data-")) { -- dataAttributes ??= {}; -- dataAttributes[selector2.name] = `"${selector2.operation.value}"`; -+ const parseClassRule = () => { -+ rule.value.declarations?.declarations?.forEach(declaration => this.addDeclaration(declaration)); -+ rule.value.declarations?.importantDeclarations?.forEach(declaration => this.addDeclaration(declaration, true)); -+ rule.value.rules?.forEach(rule2 => this.parseRuleRec(rule2)); -+ }; -+ if (hasVariants) { -+ this.withSelectorVariants(variants, parseClassRule); -+ } else { -+ parseClassRule(); - } -- }); -- if ([rtl, theme, active, focus, disabled, dataAttributes].some(_utils.isDefined)) { -- this.declarationConfig.rtl ??= rtl; -- this.declarationConfig.theme ??= theme; -- this.declarationConfig.active ??= active; -- this.declarationConfig.focus ??= focus; -- this.declarationConfig.disabled ??= disabled; -- this.declarationConfig.dataAttributes ??= dataAttributes; -- rule.value.declarations?.declarations?.forEach(declaration => this.addDeclaration(declaration)); -- rule.value.declarations?.importantDeclarations?.forEach(declaration => this.addDeclaration(declaration, true)); -- rule.value.rules?.forEach(rule2 => this.parseRuleRec(rule2)); -- this.declarationConfig.rtl = null; -- this.declarationConfig.theme = null; -- this.declarationConfig.active = null; -- this.declarationConfig.focus = null; -- this.declarationConfig.disabled = null; -- this.declarationConfig.dataAttributes = null; -+ return; -+ } -+ const { variants, hasVariants } = this.readSelectorVariants(selector); -+ if (hasVariants) { -+ this.withSelectorVariants(variants, () => { -+ rule.value.declarations?.declarations?.forEach(declaration => this.addDeclaration(declaration)); -+ rule.value.declarations?.importantDeclarations?.forEach(declaration => this.addDeclaration(declaration, true)); -+ rule.value.rules?.forEach(rule2 => this.parseRuleRec(rule2)); -+ }); - return; - } - selector.forEach(selectorToken => { -@@ -199,11 +223,16 @@ class ProcessorBuilder { - const { - mediaQueries - } = rule.value.query; -- this.declarationConfig.mediaQueries.push(...mediaQueries); -+ const outerConfig = this.declarationConfig; -+ const blockMediaQueries = [...outerConfig.mediaQueries, ...mediaQueries]; - rule.value.rules.forEach(rule2 => { -+ this.declarationConfig = { -+ ...outerConfig, -+ mediaQueries: blockMediaQueries -+ }; - this.parseRuleRec(rule2); -- this.declarationConfig = this.getDeclarationConfig(); - }); -+ this.declarationConfig = outerConfig; - return; - } - if (rule.type === "layer-block") { -diff --git a/dist/metro/transformer.cjs b/dist/metro/transformer.cjs -index d57ca81..83b58d1 100644 ---- a/dist/metro/transformer.cjs -+++ b/dist/metro/transformer.cjs -@@ -7,6 +7,7 @@ const culori = require('culori'); - const node = require('@tailwindcss/node'); - const oxide = require('@tailwindcss/oxide'); - const fs = require('fs'); -+const node_crypto = require('node:crypto'); - - function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; } - -@@ -1537,6 +1538,64 @@ class ProcessorBuilder { - style.importantProperties.push(declaration.property); - } - } -+ readSelectorVariants(tokens) { -+ const variants = { -+ rtl: null, -+ theme: null, -+ active: null, -+ focus: null, -+ disabled: null, -+ dataAttributes: null -+ }; -+ tokens.forEach((token) => { -+ if (token.type === "pseudo-class" && token.kind === "where") { -+ token.selectors.forEach((compound) => { -+ compound.forEach((inner) => { -+ if (inner.type === "class" && this.bundlerConfig.themes.includes(inner.name)) { -+ variants.theme = inner.name; -+ } -+ if (inner.type === "pseudo-class" && inner.kind === "dir") { -+ variants.rtl = inner.direction === "rtl"; -+ } -+ }); -+ }); -+ } -+ if (token.type === "pseudo-class" && token.kind === "active") { -+ variants.active = true; -+ } -+ if (token.type === "pseudo-class" && token.kind === "focus") { -+ variants.focus = true; -+ } -+ if (token.type === "pseudo-class" && token.kind === "disabled") { -+ variants.disabled = true; -+ } -+ if (token.type === "attribute" && token.operation === null && token.name.startsWith("data-")) { -+ variants.dataAttributes ??= {}; -+ variants.dataAttributes[token.name] = `"true"`; -+ } -+ if (token.type === "attribute" && token.operation?.operator === "equal" && token.name.startsWith("data-")) { -+ variants.dataAttributes ??= {}; -+ variants.dataAttributes[token.name] = `"${token.operation.value}"`; -+ } -+ }); -+ const hasVariants = Object.values(variants).some(config.isDefined); -+ return { variants, hasVariants }; -+ } -+ withSelectorVariants(variants, run) { -+ this.declarationConfig.rtl ??= variants.rtl; -+ this.declarationConfig.theme ??= variants.theme; -+ this.declarationConfig.active ??= variants.active; -+ this.declarationConfig.focus ??= variants.focus; -+ this.declarationConfig.disabled ??= variants.disabled; -+ this.declarationConfig.dataAttributes ??= variants.dataAttributes; -+ run(); -+ this.declarationConfig.rtl = null; -+ this.declarationConfig.theme = null; -+ this.declarationConfig.active = null; -+ this.declarationConfig.focus = null; -+ this.declarationConfig.disabled = null; -+ this.declarationConfig.dataAttributes = null; -+ } - parseRuleRec(rule) { - if (this.declarationConfig.className !== null) { - const lastStyle = this.stylesheets[this.declarationConfig.className]?.at(-1); -@@ -1552,64 +1611,30 @@ class ProcessorBuilder { - this.declarationConfig.className = newClassName; - this.stylesheets[newClassName] ??= []; - this.stylesheets[newClassName].push({}); -- rule.value.declarations?.declarations?.forEach((declaration) => this.addDeclaration(declaration)); -- rule.value.declarations?.importantDeclarations?.forEach((declaration) => this.addDeclaration(declaration, true)); -- rule.value.rules?.forEach((rule2) => this.parseRuleRec(rule2)); -- return; -- } -- let rtl = null; -- let theme = null; -- let active = null; -- let focus = null; -- let disabled = null; -- let dataAttributes = null; -- selector.forEach((selector2) => { -- if (selector2.type === "pseudo-class" && selector2.kind === "where") { -- selector2.selectors.forEach((selector3) => { -- selector3.forEach((selector4) => { -- if (selector4.type === "class" && this.bundlerConfig.themes.includes(selector4.name)) { -- theme = selector4.name; -- } -- if (selector4.type === "pseudo-class" && selector4.kind === "dir") { -- rtl = selector4.direction === "rtl"; -- } -- }); -- }); -- } -- if (selector2.type === "pseudo-class" && selector2.kind === "active") { -- active = true; -- } -- if (selector2.type === "pseudo-class" && selector2.kind === "focus") { -- focus = true; -+ const trailing = selector.slice(1); -+ const { variants, hasVariants } = this.readSelectorVariants(trailing); -+ if (trailing.length > 0 && !hasVariants) { -+ return; - } -- if (selector2.type === "pseudo-class" && selector2.kind === "disabled") { -- disabled = true; -- } -- if (selector2.type === "attribute" && selector2.operation === null && selector2.name.startsWith("data-")) { -- dataAttributes ??= {}; -- dataAttributes[selector2.name] = `"true"`; -- } -- if (selector2.type === "attribute" && selector2.operation?.operator === "equal" && selector2.name.startsWith("data-")) { -- dataAttributes ??= {}; -- dataAttributes[selector2.name] = `"${selector2.operation.value}"`; -+ const parseClassRule = () => { -+ rule.value.declarations?.declarations?.forEach((declaration) => this.addDeclaration(declaration)); -+ rule.value.declarations?.importantDeclarations?.forEach((declaration) => this.addDeclaration(declaration, true)); -+ rule.value.rules?.forEach((rule2) => this.parseRuleRec(rule2)); -+ }; -+ if (hasVariants) { -+ this.withSelectorVariants(variants, parseClassRule); -+ } else { -+ parseClassRule(); - } -- }); -- if ([rtl, theme, active, focus, disabled, dataAttributes].some(config.isDefined)) { -- this.declarationConfig.rtl ??= rtl; -- this.declarationConfig.theme ??= theme; -- this.declarationConfig.active ??= active; -- this.declarationConfig.focus ??= focus; -- this.declarationConfig.disabled ??= disabled; -- this.declarationConfig.dataAttributes ??= dataAttributes; -- rule.value.declarations?.declarations?.forEach((declaration) => this.addDeclaration(declaration)); -- rule.value.declarations?.importantDeclarations?.forEach((declaration) => this.addDeclaration(declaration, true)); -- rule.value.rules?.forEach((rule2) => this.parseRuleRec(rule2)); -- this.declarationConfig.rtl = null; -- this.declarationConfig.theme = null; -- this.declarationConfig.active = null; -- this.declarationConfig.focus = null; -- this.declarationConfig.disabled = null; -- this.declarationConfig.dataAttributes = null; -+ return; -+ } -+ const { variants, hasVariants } = this.readSelectorVariants(selector); -+ if (hasVariants) { -+ this.withSelectorVariants(variants, () => { -+ rule.value.declarations?.declarations?.forEach((declaration) => this.addDeclaration(declaration)); -+ rule.value.declarations?.importantDeclarations?.forEach((declaration) => this.addDeclaration(declaration, true)); -+ rule.value.rules?.forEach((rule2) => this.parseRuleRec(rule2)); -+ }); - return; - } - selector.forEach((selectorToken) => { -@@ -1629,11 +1654,13 @@ class ProcessorBuilder { - } - if (rule.type === "media") { - const { mediaQueries } = rule.value.query; -- this.declarationConfig.mediaQueries.push(...mediaQueries); -+ const outerConfig = this.declarationConfig; -+ const blockMediaQueries = [...outerConfig.mediaQueries, ...mediaQueries]; - rule.value.rules.forEach((rule2) => { -+ this.declarationConfig = { ...outerConfig, mediaQueries: blockMediaQueries }; - this.parseRuleRec(rule2); -- this.declarationConfig = this.getDeclarationConfig(); - }); -+ this.declarationConfig = outerConfig; - return; - } - if (rule.type === "layer-block") { -@@ -1753,10 +1780,11 @@ const transform = async (config$1, projectRoot, filePath, data, options) => { - await bundlerConfig.generateArtifacts(cssArtifactPath); - const virtualCode = await compileCSS(bundlerConfig); - const isWeb = bundlerConfig.platform === config.Platform.Web; -+ const nativeStylesFingerprint = isWeb ? void 0 : node_crypto.createHash("sha256").update(virtualCode).update("\0").update(bundlerConfig.stringifiedThemes).digest("hex"); - data = Buffer.from( - isWeb ? virtualCode : [ - `const { Uniwind } = require('uniwind');`, -- `Uniwind.__reinit(rt => ${virtualCode}, ${bundlerConfig.stringifiedThemes});` -+ `Uniwind.__reinit(rt => ${virtualCode}, ${bundlerConfig.stringifiedThemes}, '${nativeStylesFingerprint}');` - ].join(""), - "utf-8" - ); -diff --git a/dist/metro/transformer.mjs b/dist/metro/transformer.mjs -index d3e7475..f92b73b 100644 ---- a/dist/metro/transformer.mjs -+++ b/dist/metro/transformer.mjs -@@ -5,6 +5,7 @@ import { converter, parse, formatHex, formatHex8 } from 'culori'; - import { compile } from '@tailwindcss/node'; - import { Scanner } from '@tailwindcss/oxide'; - import fs from 'fs'; -+import { createHash } from 'node:crypto'; - - const toCamelCase = (str) => str.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()); - const pipe = (data) => ((...fns) => fns.reduce((acc, fn) => fn(acc), data)); -@@ -1530,6 +1531,64 @@ class ProcessorBuilder { - style.importantProperties.push(declaration.property); - } - } -+ readSelectorVariants(tokens) { -+ const variants = { -+ rtl: null, -+ theme: null, -+ active: null, -+ focus: null, -+ disabled: null, -+ dataAttributes: null -+ }; -+ tokens.forEach((token) => { -+ if (token.type === "pseudo-class" && token.kind === "where") { -+ token.selectors.forEach((compound) => { -+ compound.forEach((inner) => { -+ if (inner.type === "class" && this.bundlerConfig.themes.includes(inner.name)) { -+ variants.theme = inner.name; -+ } -+ if (inner.type === "pseudo-class" && inner.kind === "dir") { -+ variants.rtl = inner.direction === "rtl"; -+ } -+ }); -+ }); -+ } -+ if (token.type === "pseudo-class" && token.kind === "active") { -+ variants.active = true; -+ } -+ if (token.type === "pseudo-class" && token.kind === "focus") { -+ variants.focus = true; -+ } -+ if (token.type === "pseudo-class" && token.kind === "disabled") { -+ variants.disabled = true; -+ } -+ if (token.type === "attribute" && token.operation === null && token.name.startsWith("data-")) { -+ variants.dataAttributes ??= {}; -+ variants.dataAttributes[token.name] = `"true"`; -+ } -+ if (token.type === "attribute" && token.operation?.operator === "equal" && token.name.startsWith("data-")) { -+ variants.dataAttributes ??= {}; -+ variants.dataAttributes[token.name] = `"${token.operation.value}"`; -+ } -+ }); -+ const hasVariants = Object.values(variants).some(isDefined); -+ return { variants, hasVariants }; -+ } -+ withSelectorVariants(variants, run) { -+ this.declarationConfig.rtl ??= variants.rtl; -+ this.declarationConfig.theme ??= variants.theme; -+ this.declarationConfig.active ??= variants.active; -+ this.declarationConfig.focus ??= variants.focus; -+ this.declarationConfig.disabled ??= variants.disabled; -+ this.declarationConfig.dataAttributes ??= variants.dataAttributes; -+ run(); -+ this.declarationConfig.rtl = null; -+ this.declarationConfig.theme = null; -+ this.declarationConfig.active = null; -+ this.declarationConfig.focus = null; -+ this.declarationConfig.disabled = null; -+ this.declarationConfig.dataAttributes = null; -+ } - parseRuleRec(rule) { - if (this.declarationConfig.className !== null) { - const lastStyle = this.stylesheets[this.declarationConfig.className]?.at(-1); -@@ -1545,64 +1604,30 @@ class ProcessorBuilder { - this.declarationConfig.className = newClassName; - this.stylesheets[newClassName] ??= []; - this.stylesheets[newClassName].push({}); -- rule.value.declarations?.declarations?.forEach((declaration) => this.addDeclaration(declaration)); -- rule.value.declarations?.importantDeclarations?.forEach((declaration) => this.addDeclaration(declaration, true)); -- rule.value.rules?.forEach((rule2) => this.parseRuleRec(rule2)); -- return; -- } -- let rtl = null; -- let theme = null; -- let active = null; -- let focus = null; -- let disabled = null; -- let dataAttributes = null; -- selector.forEach((selector2) => { -- if (selector2.type === "pseudo-class" && selector2.kind === "where") { -- selector2.selectors.forEach((selector3) => { -- selector3.forEach((selector4) => { -- if (selector4.type === "class" && this.bundlerConfig.themes.includes(selector4.name)) { -- theme = selector4.name; -- } -- if (selector4.type === "pseudo-class" && selector4.kind === "dir") { -- rtl = selector4.direction === "rtl"; -- } -- }); -- }); -- } -- if (selector2.type === "pseudo-class" && selector2.kind === "active") { -- active = true; -- } -- if (selector2.type === "pseudo-class" && selector2.kind === "focus") { -- focus = true; -+ const trailing = selector.slice(1); -+ const { variants, hasVariants } = this.readSelectorVariants(trailing); -+ if (trailing.length > 0 && !hasVariants) { -+ return; - } -- if (selector2.type === "pseudo-class" && selector2.kind === "disabled") { -- disabled = true; -- } -- if (selector2.type === "attribute" && selector2.operation === null && selector2.name.startsWith("data-")) { -- dataAttributes ??= {}; -- dataAttributes[selector2.name] = `"true"`; -- } -- if (selector2.type === "attribute" && selector2.operation?.operator === "equal" && selector2.name.startsWith("data-")) { -- dataAttributes ??= {}; -- dataAttributes[selector2.name] = `"${selector2.operation.value}"`; -+ const parseClassRule = () => { -+ rule.value.declarations?.declarations?.forEach((declaration) => this.addDeclaration(declaration)); -+ rule.value.declarations?.importantDeclarations?.forEach((declaration) => this.addDeclaration(declaration, true)); -+ rule.value.rules?.forEach((rule2) => this.parseRuleRec(rule2)); -+ }; -+ if (hasVariants) { -+ this.withSelectorVariants(variants, parseClassRule); -+ } else { -+ parseClassRule(); - } -- }); -- if ([rtl, theme, active, focus, disabled, dataAttributes].some(isDefined)) { -- this.declarationConfig.rtl ??= rtl; -- this.declarationConfig.theme ??= theme; -- this.declarationConfig.active ??= active; -- this.declarationConfig.focus ??= focus; -- this.declarationConfig.disabled ??= disabled; -- this.declarationConfig.dataAttributes ??= dataAttributes; -- rule.value.declarations?.declarations?.forEach((declaration) => this.addDeclaration(declaration)); -- rule.value.declarations?.importantDeclarations?.forEach((declaration) => this.addDeclaration(declaration, true)); -- rule.value.rules?.forEach((rule2) => this.parseRuleRec(rule2)); -- this.declarationConfig.rtl = null; -- this.declarationConfig.theme = null; -- this.declarationConfig.active = null; -- this.declarationConfig.focus = null; -- this.declarationConfig.disabled = null; -- this.declarationConfig.dataAttributes = null; -+ return; -+ } -+ const { variants, hasVariants } = this.readSelectorVariants(selector); -+ if (hasVariants) { -+ this.withSelectorVariants(variants, () => { -+ rule.value.declarations?.declarations?.forEach((declaration) => this.addDeclaration(declaration)); -+ rule.value.declarations?.importantDeclarations?.forEach((declaration) => this.addDeclaration(declaration, true)); -+ rule.value.rules?.forEach((rule2) => this.parseRuleRec(rule2)); -+ }); - return; - } - selector.forEach((selectorToken) => { -@@ -1622,11 +1647,13 @@ class ProcessorBuilder { - } - if (rule.type === "media") { - const { mediaQueries } = rule.value.query; -- this.declarationConfig.mediaQueries.push(...mediaQueries); -+ const outerConfig = this.declarationConfig; -+ const blockMediaQueries = [...outerConfig.mediaQueries, ...mediaQueries]; - rule.value.rules.forEach((rule2) => { -+ this.declarationConfig = { ...outerConfig, mediaQueries: blockMediaQueries }; - this.parseRuleRec(rule2); -- this.declarationConfig = this.getDeclarationConfig(); - }); -+ this.declarationConfig = outerConfig; - return; - } - if (rule.type === "layer-block") { -@@ -1746,10 +1773,11 @@ const transform = async (config, projectRoot, filePath, data, options) => { - await bundlerConfig.generateArtifacts(cssArtifactPath); - const virtualCode = await compileCSS(bundlerConfig); - const isWeb = bundlerConfig.platform === Platform.Web; -+ const nativeStylesFingerprint = isWeb ? void 0 : createHash("sha256").update(virtualCode).update("\0").update(bundlerConfig.stringifiedThemes).digest("hex"); - data = Buffer.from( - isWeb ? virtualCode : [ - `const { Uniwind } = require('uniwind');`, -- `Uniwind.__reinit(rt => ${virtualCode}, ${bundlerConfig.stringifiedThemes});` -+ `Uniwind.__reinit(rt => ${virtualCode}, ${bundlerConfig.stringifiedThemes}, '${nativeStylesFingerprint}');` - ].join(""), - "utf-8" - ); -diff --git a/dist/module/bundler/css-processor/processor.js b/dist/module/bundler/css-processor/processor.js -index 58c96b6..6849ca7 100644 ---- a/dist/module/bundler/css-processor/processor.js -+++ b/dist/module/bundler/css-processor/processor.js -@@ -99,6 +99,64 @@ export class ProcessorBuilder { - style.importantProperties.push(declaration.property); - } - } -+ readSelectorVariants(tokens) { -+ const variants = { -+ rtl: null, -+ theme: null, -+ active: null, -+ focus: null, -+ disabled: null, -+ dataAttributes: null -+ }; -+ tokens.forEach((token) => { -+ if (token.type === "pseudo-class" && token.kind === "where") { -+ token.selectors.forEach((compound) => { -+ compound.forEach((inner) => { -+ if (inner.type === "class" && this.bundlerConfig.themes.includes(inner.name)) { -+ variants.theme = inner.name; -+ } -+ if (inner.type === "pseudo-class" && inner.kind === "dir") { -+ variants.rtl = inner.direction === "rtl"; -+ } -+ }); -+ }); -+ } -+ if (token.type === "pseudo-class" && token.kind === "active") { -+ variants.active = true; -+ } -+ if (token.type === "pseudo-class" && token.kind === "focus") { -+ variants.focus = true; -+ } -+ if (token.type === "pseudo-class" && token.kind === "disabled") { -+ variants.disabled = true; -+ } -+ if (token.type === "attribute" && token.operation === null && token.name.startsWith("data-")) { -+ variants.dataAttributes ??= {}; -+ variants.dataAttributes[token.name] = `"true"`; -+ } -+ if (token.type === "attribute" && token.operation?.operator === "equal" && token.name.startsWith("data-")) { -+ variants.dataAttributes ??= {}; -+ variants.dataAttributes[token.name] = `"${token.operation.value}"`; -+ } -+ }); -+ const hasVariants = Object.values(variants).some(isDefined); -+ return { variants, hasVariants }; -+ } -+ withSelectorVariants(variants, run) { -+ this.declarationConfig.rtl ??= variants.rtl; -+ this.declarationConfig.theme ??= variants.theme; -+ this.declarationConfig.active ??= variants.active; -+ this.declarationConfig.focus ??= variants.focus; -+ this.declarationConfig.disabled ??= variants.disabled; -+ this.declarationConfig.dataAttributes ??= variants.dataAttributes; -+ run(); -+ this.declarationConfig.rtl = null; -+ this.declarationConfig.theme = null; -+ this.declarationConfig.active = null; -+ this.declarationConfig.focus = null; -+ this.declarationConfig.disabled = null; -+ this.declarationConfig.dataAttributes = null; -+ } - parseRuleRec(rule) { - if (this.declarationConfig.className !== null) { - const lastStyle = this.stylesheets[this.declarationConfig.className]?.at(-1); -@@ -114,64 +172,30 @@ export class ProcessorBuilder { - this.declarationConfig.className = newClassName; - this.stylesheets[newClassName] ??= []; - this.stylesheets[newClassName].push({}); -- rule.value.declarations?.declarations?.forEach((declaration) => this.addDeclaration(declaration)); -- rule.value.declarations?.importantDeclarations?.forEach((declaration) => this.addDeclaration(declaration, true)); -- rule.value.rules?.forEach((rule2) => this.parseRuleRec(rule2)); -- return; -- } -- let rtl = null; -- let theme = null; -- let active = null; -- let focus = null; -- let disabled = null; -- let dataAttributes = null; -- selector.forEach((selector2) => { -- if (selector2.type === "pseudo-class" && selector2.kind === "where") { -- selector2.selectors.forEach((selector3) => { -- selector3.forEach((selector4) => { -- if (selector4.type === "class" && this.bundlerConfig.themes.includes(selector4.name)) { -- theme = selector4.name; -- } -- if (selector4.type === "pseudo-class" && selector4.kind === "dir") { -- rtl = selector4.direction === "rtl"; -- } -- }); -- }); -- } -- if (selector2.type === "pseudo-class" && selector2.kind === "active") { -- active = true; -+ const trailing = selector.slice(1); -+ const { variants, hasVariants } = this.readSelectorVariants(trailing); -+ if (trailing.length > 0 && !hasVariants) { -+ return; - } -- if (selector2.type === "pseudo-class" && selector2.kind === "focus") { -- focus = true; -- } -- if (selector2.type === "pseudo-class" && selector2.kind === "disabled") { -- disabled = true; -- } -- if (selector2.type === "attribute" && selector2.operation === null && selector2.name.startsWith("data-")) { -- dataAttributes ??= {}; -- dataAttributes[selector2.name] = `"true"`; -- } -- if (selector2.type === "attribute" && selector2.operation?.operator === "equal" && selector2.name.startsWith("data-")) { -- dataAttributes ??= {}; -- dataAttributes[selector2.name] = `"${selector2.operation.value}"`; -+ const parseClassRule = () => { -+ rule.value.declarations?.declarations?.forEach((declaration) => this.addDeclaration(declaration)); -+ rule.value.declarations?.importantDeclarations?.forEach((declaration) => this.addDeclaration(declaration, true)); -+ rule.value.rules?.forEach((rule2) => this.parseRuleRec(rule2)); -+ }; -+ if (hasVariants) { -+ this.withSelectorVariants(variants, parseClassRule); -+ } else { -+ parseClassRule(); - } -- }); -- if ([rtl, theme, active, focus, disabled, dataAttributes].some(isDefined)) { -- this.declarationConfig.rtl ??= rtl; -- this.declarationConfig.theme ??= theme; -- this.declarationConfig.active ??= active; -- this.declarationConfig.focus ??= focus; -- this.declarationConfig.disabled ??= disabled; -- this.declarationConfig.dataAttributes ??= dataAttributes; -- rule.value.declarations?.declarations?.forEach((declaration) => this.addDeclaration(declaration)); -- rule.value.declarations?.importantDeclarations?.forEach((declaration) => this.addDeclaration(declaration, true)); -- rule.value.rules?.forEach((rule2) => this.parseRuleRec(rule2)); -- this.declarationConfig.rtl = null; -- this.declarationConfig.theme = null; -- this.declarationConfig.active = null; -- this.declarationConfig.focus = null; -- this.declarationConfig.disabled = null; -- this.declarationConfig.dataAttributes = null; -+ return; -+ } -+ const { variants, hasVariants } = this.readSelectorVariants(selector); -+ if (hasVariants) { -+ this.withSelectorVariants(variants, () => { -+ rule.value.declarations?.declarations?.forEach((declaration) => this.addDeclaration(declaration)); -+ rule.value.declarations?.importantDeclarations?.forEach((declaration) => this.addDeclaration(declaration, true)); -+ rule.value.rules?.forEach((rule2) => this.parseRuleRec(rule2)); -+ }); - return; - } - selector.forEach((selectorToken) => { -@@ -191,11 +215,13 @@ export class ProcessorBuilder { - } - if (rule.type === "media") { - const { mediaQueries } = rule.value.query; -- this.declarationConfig.mediaQueries.push(...mediaQueries); -+ const outerConfig = this.declarationConfig; -+ const blockMediaQueries = [...outerConfig.mediaQueries, ...mediaQueries]; - rule.value.rules.forEach((rule2) => { -+ this.declarationConfig = { ...outerConfig, mediaQueries: blockMediaQueries }; - this.parseRuleRec(rule2); -- this.declarationConfig = this.getDeclarationConfig(); - }); -+ this.declarationConfig = outerConfig; - return; - } - if (rule.type === "layer-block") { -diff --git a/src/bundler/adapters/metro/transformer.ts b/src/bundler/adapters/metro/transformer.ts -index 08059b0..05c0515 100644 ---- a/src/bundler/adapters/metro/transformer.ts -+++ b/src/bundler/adapters/metro/transformer.ts -@@ -5,6 +5,7 @@ import { Platform } from '@/common/consts' - import type * as ExpoMetroConfig from '@expo/metro-config' - import type * as MetroTransformWorker from 'metro-transform-worker' - import type { JsTransformerConfig, JsTransformOptions } from 'metro-transform-worker' -+import { createHash } from 'node:crypto' - import path from 'path' - - const cssArtifactPath = path.resolve(__dirname, '../../uniwind.css') -@@ -69,13 +70,20 @@ export const transform = async ( - await bundlerConfig.generateArtifacts(cssArtifactPath) - const virtualCode = await compileCSS(bundlerConfig) - const isWeb = bundlerConfig.platform === Platform.Web -+ const nativeStylesFingerprint = isWeb -+ ? undefined -+ : createHash('sha256') -+ .update(virtualCode) -+ .update('\0') -+ .update(bundlerConfig.stringifiedThemes) -+ .digest('hex') - - data = Buffer.from( - isWeb - ? virtualCode - : [ - `const { Uniwind } = require('uniwind');`, -- `Uniwind.__reinit(rt => ${virtualCode}, ${bundlerConfig.stringifiedThemes});`, -+ `Uniwind.__reinit(rt => ${virtualCode}, ${bundlerConfig.stringifiedThemes}, '${nativeStylesFingerprint}');`, - ].join(''), - 'utf-8', - ) -diff --git a/src/bundler/css-processor/processor.ts b/src/bundler/css-processor/processor.ts -index c8b3bb8..4af4199 100644 ---- a/src/bundler/css-processor/processor.ts -+++ b/src/bundler/css-processor/processor.ts -@@ -123,6 +123,77 @@ export class ProcessorBuilder { - } - } - -+ private readSelectorVariants(tokens: ReadonlyArray) { -+ const variants = { -+ rtl: null as boolean | null, -+ theme: null as string | null, -+ active: null as boolean | null, -+ focus: null as boolean | null, -+ disabled: null as boolean | null, -+ dataAttributes: null as Record | null, -+ } -+ -+ tokens.forEach(token => { -+ if (token.type === 'pseudo-class' && token.kind === 'where') { -+ token.selectors.forEach((compound: Array) => { -+ compound.forEach(inner => { -+ if (inner.type === 'class' && this.bundlerConfig.themes.includes(inner.name)) { -+ variants.theme = inner.name -+ } -+ -+ if (inner.type === 'pseudo-class' && inner.kind === 'dir') { -+ variants.rtl = inner.direction === 'rtl' -+ } -+ }) -+ }) -+ } -+ -+ if (token.type === 'pseudo-class' && token.kind === 'active') { -+ variants.active = true -+ } -+ -+ if (token.type === 'pseudo-class' && token.kind === 'focus') { -+ variants.focus = true -+ } -+ -+ if (token.type === 'pseudo-class' && token.kind === 'disabled') { -+ variants.disabled = true -+ } -+ -+ // data-x -+ if (token.type === 'attribute' && token.operation === null && token.name.startsWith('data-')) { -+ variants.dataAttributes ??= {} -+ variants.dataAttributes[token.name] = `"true"` -+ } -+ -+ // data-x= -+ if (token.type === 'attribute' && token.operation?.operator === 'equal' && token.name.startsWith('data-')) { -+ variants.dataAttributes ??= {} -+ variants.dataAttributes[token.name] = `"${token.operation.value}"` -+ } -+ }) -+ -+ return { variants, hasVariants: Object.values(variants).some(isDefined) } -+ } -+ -+ private withSelectorVariants(variants: ReturnType['variants'], run: () => void) { -+ this.declarationConfig.rtl ??= variants.rtl -+ this.declarationConfig.theme ??= variants.theme -+ this.declarationConfig.active ??= variants.active -+ this.declarationConfig.focus ??= variants.focus -+ this.declarationConfig.disabled ??= variants.disabled -+ this.declarationConfig.dataAttributes ??= variants.dataAttributes -+ -+ run() -+ -+ this.declarationConfig.rtl = null -+ this.declarationConfig.theme = null -+ this.declarationConfig.active = null -+ this.declarationConfig.focus = null -+ this.declarationConfig.disabled = null -+ this.declarationConfig.dataAttributes = null -+ } -+ - private parseRuleRec(rule: Rule) { - if (this.declarationConfig.className !== null) { - const lastStyle = this.stylesheets[this.declarationConfig.className]?.at(-1) -@@ -142,78 +213,41 @@ export class ProcessorBuilder { - this.stylesheets[newClassName] ??= [] - this.stylesheets[newClassName].push({}) - -- rule.value.declarations?.declarations?.forEach(declaration => this.addDeclaration(declaration)) -- rule.value.declarations?.importantDeclarations?.forEach(declaration => this.addDeclaration(declaration, true)) -- rule.value.rules?.forEach(rule => this.parseRuleRec(rule)) -+ // Tailwind >= 4.3.3 emits `.active\:x:active {}` instead of -+ // nesting `&:active` under the class, so the variant tokens -+ // sit right after the class token. A compound the runtime -+ // cannot express (e.g. `[aria-disabled="true"]`) used to be -+ // an empty nested rule and must not become unconditional. -+ const trailing = selector.slice(1) -+ const { variants, hasVariants } = this.readSelectorVariants(trailing) - -- return -- } -- -- let rtl = null as boolean | null -- let theme = null as string | null -- let active = null as boolean | null -- let focus = null as boolean | null -- let disabled = null as boolean | null -- let dataAttributes = null as Record | null -- -- selector.forEach(selector => { -- if (selector.type === 'pseudo-class' && selector.kind === 'where') { -- selector.selectors.forEach(selector => { -- selector.forEach(selector => { -- if (selector.type === 'class' && this.bundlerConfig.themes.includes(selector.name)) { -- theme = selector.name -- } -- -- if (selector.type === 'pseudo-class' && selector.kind === 'dir') { -- rtl = selector.direction === 'rtl' -- } -- }) -- }) -+ if (trailing.length > 0 && !hasVariants) { -+ return - } - -- if (selector.type === 'pseudo-class' && selector.kind === 'active') { -- active = true -- } -- -- if (selector.type === 'pseudo-class' && selector.kind === 'focus') { -- focus = true -+ const parseClassRule = () => { -+ rule.value.declarations?.declarations?.forEach(declaration => this.addDeclaration(declaration)) -+ rule.value.declarations?.importantDeclarations?.forEach(declaration => this.addDeclaration(declaration, true)) -+ rule.value.rules?.forEach(rule => this.parseRuleRec(rule)) - } - -- if (selector.type === 'pseudo-class' && selector.kind === 'disabled') { -- disabled = true -+ if (hasVariants) { -+ this.withSelectorVariants(variants, parseClassRule) -+ } else { -+ parseClassRule() - } - -- // data-x -- if (selector.type === 'attribute' && selector.operation === null && selector.name.startsWith('data-')) { -- dataAttributes ??= {} -- dataAttributes[selector.name] = `"true"` -- } -+ return -+ } - -- // data-x= -- if (selector.type === 'attribute' && selector.operation?.operator === 'equal' && selector.name.startsWith('data-')) { -- dataAttributes ??= {} -- dataAttributes[selector.name] = `"${selector.operation.value}"` -- } -- }) -+ const { variants, hasVariants } = this.readSelectorVariants(selector) - -- if ([rtl, theme, active, focus, disabled, dataAttributes].some(isDefined)) { -- this.declarationConfig.rtl ??= rtl -- this.declarationConfig.theme ??= theme -- this.declarationConfig.active ??= active -- this.declarationConfig.focus ??= focus -- this.declarationConfig.disabled ??= disabled -- this.declarationConfig.dataAttributes ??= dataAttributes -- -- rule.value.declarations?.declarations?.forEach(declaration => this.addDeclaration(declaration)) -- rule.value.declarations?.importantDeclarations?.forEach(declaration => this.addDeclaration(declaration, true)) -- rule.value.rules?.forEach(rule => this.parseRuleRec(rule)) -- -- this.declarationConfig.rtl = null -- this.declarationConfig.theme = null -- this.declarationConfig.active = null -- this.declarationConfig.focus = null -- this.declarationConfig.disabled = null -- this.declarationConfig.dataAttributes = null -+ if (hasVariants) { -+ this.withSelectorVariants(variants, () => { -+ rule.value.declarations?.declarations?.forEach(declaration => this.addDeclaration(declaration)) -+ rule.value.declarations?.importantDeclarations?.forEach(declaration => this.addDeclaration(declaration, true)) -+ rule.value.rules?.forEach(rule => this.parseRuleRec(rule)) -+ }) - - return - } -@@ -241,13 +275,21 @@ export class ProcessorBuilder { - - if (rule.type === 'media') { - const { mediaQueries } = rule.value.query -+ // Media queries have to outlive the reset between the block's sibling -+ // rules: resetting only after each parse left every rule past the first -+ // without the block's queries, shipping ios:/android: variants and later -+ // utilities of a width breakpoint unguarded to every platform. Reset to -+ // the outer config instead of a fresh one so media nested inside a class -+ // rule keeps writing into that class. -+ const outerConfig = this.declarationConfig -+ const blockMediaQueries = [...outerConfig.mediaQueries, ...mediaQueries] - -- this.declarationConfig.mediaQueries.push(...mediaQueries) - rule.value.rules.forEach(rule => { -+ this.declarationConfig = { ...outerConfig, mediaQueries: blockMediaQueries } - this.parseRuleRec(rule) -- this.declarationConfig = this.getDeclarationConfig() - }) - -+ this.declarationConfig = outerConfig - return - } - -diff --git a/src/core/config/config.native.ts b/src/core/config/config.native.ts -index 5e0832b..01aed1b 100644 ---- a/src/core/config/config.native.ts -+++ b/src/core/config/config.native.ts -@@ -8,6 +8,8 @@ import type { CSSVariables, GenerateStyleSheetsCallback, ThemeName } from '../ty - import { UniwindConfigBuilder as UniwindConfigBuilderBase } from './config.common' - - class UniwindConfigBuilder extends UniwindConfigBuilderBase { -+ private stylesFingerprint: string | undefined -+ - constructor() { - super() - } -@@ -35,9 +37,18 @@ class UniwindConfigBuilder extends UniwindConfigBuilderBase { - UniwindListener.notify([StyleDependency.Insets]) - } - -- protected __reinit(generateStyleSheetCallback: GenerateStyleSheetsCallback, themes: Array) { -+ protected __reinit( -+ generateStyleSheetCallback: GenerateStyleSheetsCallback, -+ themes: Array, -+ stylesFingerprint?: string, -+ ) { -+ if (__DEV__ && stylesFingerprint !== undefined && stylesFingerprint === this.stylesFingerprint) { -+ return -+ } -+ - super.__reinit(generateStyleSheetCallback, themes) - UniwindStore.reinit(generateStyleSheetCallback, themes) -+ this.stylesFingerprint = stylesFingerprint - } - - protected onThemeChange() { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 447b1c9e77f90c9a45482a2048b269ab405258ca..2c4421d93ead6d5ab4fd3a17f130960dabc67c58 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -115,7 +115,6 @@ patchedDependencies: react-native-nitro-markdown@0.5.8: 642b47830730acff3761fe29cf54271d18b493c0070c750097850b7ea032e00c react-native-nitro-modules@0.35.9: daf4a639ce3f951af630a3711e4082d58e3a11e5a79f45efb0b59925327ebde2 react-native-screens@4.28.0: cc1301436ee62c39860acc9ee1a1e60b7d47e6abd211c42effa62c056d165483 - uniwind@1.11.0: a91f3953483cfd434436de6358ff5b63c49974ef148fad91246c26ba95332625 importers: @@ -487,8 +486,8 @@ importers: specifier: ^3.5.0 version: 3.6.0 uniwind: - specifier: 1.11.0 - version: 1.11.0(patch_hash=a91f3953483cfd434436de6358ff5b63c49974ef148fad91246c26ba95332625)(@expo/metro-config@58.0.6(patch_hash=90fa980e8b6480631fb2fbb04559834e3c612db40c550cd828f11be297d8d760)(bufferutil@4.1.0)(expo@58.0.2)(typescript@7.0.2)(utf-8-validate@6.0.6))(metro-cache@0.87.1)(metro-transform-worker@0.87.1(bufferutil@4.1.0)(utf-8-validate@6.0.6))(metro@0.87.1(bufferutil@4.1.0)(utf-8-validate@6.0.6))(react-native@0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6))(react@19.3.0)(tailwindcss@4.3.3) + specifier: 1.12.1 + version: 1.12.1(@expo/metro-config@58.0.6(patch_hash=90fa980e8b6480631fb2fbb04559834e3c612db40c550cd828f11be297d8d760)(bufferutil@4.1.0)(expo@58.0.2)(typescript@7.0.2)(utf-8-validate@6.0.6))(metro-cache@0.87.1)(metro-transform-worker@0.87.1(bufferutil@4.1.0)(utf-8-validate@6.0.6))(metro@0.87.1(bufferutil@4.1.0)(utf-8-validate@6.0.6))(react-native@0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6))(react@19.3.0)(tailwindcss@4.3.3) devDependencies: '@effect/vitest': specifier: 4.0.0-rc.115 @@ -10905,8 +10904,8 @@ packages: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} - uniwind@1.11.0: - resolution: {integrity: sha512-e2mYlHrZzAQtZUf3STrfVDTetspv7p5vm/cu6p8kjLRQaL9r6QFG+MlwZ3hrI2LHnzgzjbSo+MwvoE380zz2JQ==} + uniwind@1.12.1: + resolution: {integrity: sha512-u0OVMVqefGchOpsPIwIYvcF2Jl9JtVVaXBb+wfqAYoyLrhN27Ll50aQ7aFT81k73JSq+He6dNlcbTl2Qb9cTMw==} hasBin: true peerDependencies: '@expo/metro-config': 58.0.6 @@ -22081,7 +22080,7 @@ snapshots: universalify@2.0.1: {} - uniwind@1.11.0(patch_hash=a91f3953483cfd434436de6358ff5b63c49974ef148fad91246c26ba95332625)(@expo/metro-config@58.0.6(patch_hash=90fa980e8b6480631fb2fbb04559834e3c612db40c550cd828f11be297d8d760)(bufferutil@4.1.0)(expo@58.0.2)(typescript@7.0.2)(utf-8-validate@6.0.6))(metro-cache@0.87.1)(metro-transform-worker@0.87.1(bufferutil@4.1.0)(utf-8-validate@6.0.6))(metro@0.87.1(bufferutil@4.1.0)(utf-8-validate@6.0.6))(react-native@0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6))(react@19.3.0)(tailwindcss@4.3.3): + uniwind@1.12.1(@expo/metro-config@58.0.6(patch_hash=90fa980e8b6480631fb2fbb04559834e3c612db40c550cd828f11be297d8d760)(bufferutil@4.1.0)(expo@58.0.2)(typescript@7.0.2)(utf-8-validate@6.0.6))(metro-cache@0.87.1)(metro-transform-worker@0.87.1(bufferutil@4.1.0)(utf-8-validate@6.0.6))(metro@0.87.1(bufferutil@4.1.0)(utf-8-validate@6.0.6))(react-native@0.88.0-rc.3(@babel/core@7.29.7)(@react-native/metro-config@0.88.0-rc.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.3.0)(bufferutil@4.1.0)(react@19.3.0)(utf-8-validate@6.0.6))(react@19.3.0)(tailwindcss@4.3.3): dependencies: '@tailwindcss/node': 4.3.3 '@tailwindcss/oxide': 4.3.3 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 5bd65fcacbbb04c5d7a2c0a19bc6d0fb7bfe7bdf..11f052624a4842d03bfe431e8dfc95aeb13a0f6a 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -125,6 +125,7 @@ minimumReleaseAgeExclude: - "@distilled.cloud/stripe@1.0.0-rc.12" - "@shadcn/lint@0.1.5" - cn@0.3.2 + - uniwind@1.12.1 - "@expo/dom-webview@58.0.2" - "expo-auth-session@58.0.7" - "expo-camera@58.0.7" @@ -263,7 +264,6 @@ patchedDependencies: react-native-keyboard-controller@1.22.6: patches/react-native-keyboard-controller@1.22.6.patch react-native-nitro-modules@0.35.9: patches/react-native-nitro-modules@0.35.9.patch react-native-screens@4.28.0: patches/react-native-screens@4.28.0.patch - uniwind@1.11.0: patches/uniwind@1.11.0.patch peerDependencyRules: allowAny: -- 2.54.0