diff --git a/packages/uniwind/src/bundler/css-compiler/compileNativeCSS.ts b/packages/uniwind/src/bundler/css-compiler/compileNativeCSS.ts index 0d65d7fe..404b4ff3 100644 --- a/packages/uniwind/src/bundler/css-compiler/compileNativeCSS.ts +++ b/packages/uniwind/src/bundler/css-compiler/compileNativeCSS.ts @@ -12,19 +12,19 @@ export const compileNativeCSS = (bundlerConfig: UniwindBundlerConfig, tailwindCS ) const vars = serializeJSObject( Processor.vars, - (key, value) => `get "${key}"() { return ${value} }`, + (key, value) => `"${key}": vars => ${value}`, ) const scopedVars = Object.fromEntries( Object.entries(Processor.scopedVars) .map(([scopedVarsName, scopedVars]) => [ scopedVarsName, - serializeJSObject(scopedVars, (key, value) => `get "${key}"() { return ${value} }`), + serializeJSObject(scopedVars, (key, value) => `"${key}": vars => ${value}`), ]), ) const serializedScopedVars = Object.entries(scopedVars) .map(([scopedVarsName, scopedVars]) => `"${scopedVarsName}": ({ ${scopedVars} }),`) .join('') - const currentColorVar = `get currentColor() { return rt.colorScheme === 'dark' ? '#ffffff' : '#000000' },` + const currentColorVar = `currentColor: () => rt.colorScheme === 'dark' ? '#ffffff' : '#000000',` return [ '({', diff --git a/packages/uniwind/src/bundler/css-processor/addMetaToStylesTemplate.ts b/packages/uniwind/src/bundler/css-processor/addMetaToStylesTemplate.ts index e9b727f2..d9667122 100644 --- a/packages/uniwind/src/bundler/css-processor/addMetaToStylesTemplate.ts +++ b/packages/uniwind/src/bundler/css-processor/addMetaToStylesTemplate.ts @@ -6,9 +6,9 @@ import type { StyleSheetTemplate } from './types' import { toCamelCase } from './utils' const extractVarsFromString = (value: string) => { - const thisIndexes = [...value.matchAll(/this\[/g)].map(m => m.index) + const varsIndexes = [...value.matchAll(/vars\[/g)].map(m => m.index) - return thisIndexes.map(index => { + return varsIndexes.map(index => { const afterIndex = value.slice(index + 5) const closingIndex = afterIndex.indexOf(']') const varName = afterIndex.slice(0, closingIndex) @@ -76,7 +76,7 @@ export const addMetaToStylesTemplate = (Processor: ProcessorBuilder, currentPlat const entries = Object.entries(rest) .flatMap(([property, value]) => Processor.RN.cssToRN(property, value)) - .map(([property, value]) => [`"${property}"`, `function() { return ${serialize(value)} }`]) + .map(([property, value]) => [`"${property}"`, `function(vars) { return ${serialize(value)} }`]) if (platform) { const isTV = currentPlatform === Platform.AndroidTV || currentPlatform === Platform.AppleTV diff --git a/packages/uniwind/src/bundler/css-processor/color.ts b/packages/uniwind/src/bundler/css-processor/color.ts index 88d030b0..4d13dfc5 100644 --- a/packages/uniwind/src/bundler/css-processor/color.ts +++ b/packages/uniwind/src/bundler/css-processor/color.ts @@ -37,7 +37,7 @@ export class Color { try { if (color.type === 'currentcolor') { - return 'this["currentColor"]' + return 'vars["currentColor"]?.(vars)' } if (color.type === 'rgb' || color.type === 'srgb') { diff --git a/packages/uniwind/src/bundler/css-processor/css.ts b/packages/uniwind/src/bundler/css-processor/css.ts index a1d19f84..80e14c6f 100644 --- a/packages/uniwind/src/bundler/css-processor/css.ts +++ b/packages/uniwind/src/bundler/css-processor/css.ts @@ -176,7 +176,7 @@ export class CSS { } if (declarationValue.value === 'currentcolor') { - return 'this["currentColor"]' + return 'vars["currentColor"]?.(vars)' } return declarationValue.value @@ -212,7 +212,7 @@ export class CSS { case 'pair': return declarationValue.inside.type case 'currentcolor': - return 'this["currentColor"]' + return 'vars["currentColor"]?.(vars)' case 'calc': return this.Processor.Functions.processCalc(declarationValue.value) case 'min': diff --git a/packages/uniwind/src/bundler/css-processor/units.ts b/packages/uniwind/src/bundler/css-processor/units.ts index 212afda3..cc2cdf6f 100644 --- a/packages/uniwind/src/bundler/css-processor/units.ts +++ b/packages/uniwind/src/bundler/css-processor/units.ts @@ -23,7 +23,7 @@ export class Units { case 'rem': return length.value * this.Processor.vars['--uniwind-em'] case 'em': - return `this[\`--uniwind-em\`] * ${length.value}` + return `vars["--uniwind-em"]?.(vars) * ${length.value}` default: this.logger.warn(`Unsupported unit - ${length.unit}`) diff --git a/packages/uniwind/src/bundler/css-processor/utils.ts b/packages/uniwind/src/bundler/css-processor/utils.ts index 79dfbc9a..b8e24c36 100644 --- a/packages/uniwind/src/bundler/css-processor/utils.ts +++ b/packages/uniwind/src/bundler/css-processor/utils.ts @@ -41,15 +41,12 @@ export const smartSplit = (str: string, separator = ' ' as string | RegExp) => { export const addMissingSpaces = (str: string) => pipe(str)( x => x.trim(), - x => x.replace(/([^ {])this/g, '$1 this'), x => x.replace(/\](?=\d)/g, '] '), x => x.replace(/\](?=")/g, '] '), - x => x.replace(/\)(?=\S)/g, ') '), + x => x.replace(/\)(?=[^\s,])/g, ') '), x => x.replace(/(?(arr: Array) => Array.from(new Set(arr)) - export const isValidJSValue = (jsValueString: string) => { try { new Function(`const test = ${jsValueString}`) @@ -67,7 +64,7 @@ export const shouldBeSerialized = (value: string) => { return [ isNumber(value), - value.startsWith('this['), + value.startsWith('vars['), value.startsWith('rt.'), /[*/+-]/.test(value), value.includes('"'), diff --git a/packages/uniwind/src/bundler/css-processor/var.ts b/packages/uniwind/src/bundler/css-processor/var.ts index f5a8af36..f257d348 100644 --- a/packages/uniwind/src/bundler/css-processor/var.ts +++ b/packages/uniwind/src/bundler/css-processor/var.ts @@ -5,7 +5,7 @@ export class Var { constructor(private readonly Processor: ProcessorBuilder) {} processVar(variable: Variable): string { - const value = `this[\`${variable.name.ident}\`]` + const value = `vars[${JSON.stringify(variable.name.ident)}]?.(vars)` if (!variable.fallback || variable.fallback.length === 0) { return value diff --git a/packages/uniwind/src/core/config/config.native.ts b/packages/uniwind/src/core/config/config.native.ts index fb54a3cc..917e1d40 100644 --- a/packages/uniwind/src/core/config/config.native.ts +++ b/packages/uniwind/src/core/config/config.native.ts @@ -37,11 +37,7 @@ class UniwindConfigBuilder extends UniwindConfigBuilderBase { } UniwindStore.vars[theme] ??= {} - Object.defineProperty(UniwindStore.vars[theme], varName, { - configurable: true, - enumerable: true, - get: getValue, - }) + UniwindStore.vars[theme][varName] = getValue }) UniwindListener.notify([StyleDependency.Variables]) diff --git a/packages/uniwind/src/core/native/native-utils.ts b/packages/uniwind/src/core/native/native-utils.ts index 90cc9cd9..a29ba8d8 100644 --- a/packages/uniwind/src/core/native/native-utils.ts +++ b/packages/uniwind/src/core/native/native-utils.ts @@ -31,15 +31,6 @@ export function lightDark(this: UniwindRuntime, light: string, dark: string) { return light } -export const cloneWithAccessors = (obj: T) => { - const proto = Object.getPrototypeOf(obj) - const clone = Object.create(proto) - - Object.defineProperties(clone, Object.getOwnPropertyDescriptors(obj)) - - return clone -} - export const parseColor = (type: string, color: string) => { try { const parsedColor = parse(`${type}(${color})`) diff --git a/packages/uniwind/src/core/native/parsers/textShadow.ts b/packages/uniwind/src/core/native/parsers/textShadow.ts index ca46d9af..55120160 100644 --- a/packages/uniwind/src/core/native/parsers/textShadow.ts +++ b/packages/uniwind/src/core/native/parsers/textShadow.ts @@ -10,28 +10,16 @@ export const parseTextShadowMutation = (styles: Record) => { const [offsetX, offsetY, radius] = offsets if (offsetX !== undefined && offsetY !== undefined) { - Object.defineProperty(styles, 'textShadowOffset', { - configurable: true, - enumerable: true, - value: { - width: Number(offsetX), - height: Number(offsetY), - }, - }) + styles.textShadowOffset = { + width: Number(offsetX), + height: Number(offsetY), + } delete styles.textShadow } if (radius !== undefined) { - Object.defineProperty(styles, 'textShadowRadius', { - configurable: true, - enumerable: true, - value: Number(radius), - }) + styles.textShadowRadius = Number(radius) } - Object.defineProperty(styles, 'textShadowColor', { - configurable: true, - enumerable: true, - value: color, - }) + styles.textShadowColor = color } diff --git a/packages/uniwind/src/core/native/parsers/transforms.ts b/packages/uniwind/src/core/native/parsers/transforms.ts index c75e5558..733fff7f 100644 --- a/packages/uniwind/src/core/native/parsers/transforms.ts +++ b/packages/uniwind/src/core/native/parsers/transforms.ts @@ -54,10 +54,6 @@ export const parseTransformsMutation = (styles: Record) => { } if (transformsResult.length > 0) { - Object.defineProperty(styles, 'transform', { - configurable: true, - enumerable: true, - value: transformsResult, - }) + styles.transform = transformsResult } } diff --git a/packages/uniwind/src/core/native/store.ts b/packages/uniwind/src/core/native/store.ts index 01b5951f..23f24cab 100644 --- a/packages/uniwind/src/core/native/store.ts +++ b/packages/uniwind/src/core/native/store.ts @@ -1,8 +1,7 @@ import { Dimensions, Platform } from 'react-native' import { Orientation, Platform as UniwindPlatform, StyleDependency, UNIWIND_PLATFORM_VARIABLES, UNIWIND_THEME_VARIABLES } from '../../common/consts' import { UniwindListener } from '../listener' -import type { ComponentState, GenerateStyleSheetsCallback, RNStyle, Style, StyleSheets, ThemeName, UniwindContextType } from '../types' -import { cloneWithAccessors } from './native-utils' +import type { ComponentState, GenerateStyleSheetsCallback, RNStyle, Style, StyleSheets, ThemeName, UniwindContextType, Var, Vars } from '../types' import { parseBoxShadow, parseFontVariant, parseTextShadowMutation, parseTransformsMutation, resolveGradient } from './parsers' import { UniwindRuntime } from './runtime' @@ -16,7 +15,7 @@ const emptyState: StylesResult = { styles: {}, dependencies: [], dependencySum: class UniwindStoreBuilder { runtime = UniwindRuntime - vars = {} as Record> + vars = {} as Record private stylesheet = {} as StyleSheets private cache = {} as Record> @@ -66,20 +65,20 @@ class UniwindStoreBuilder { const platformVars = scopedVars[`${UNIWIND_PLATFORM_VARIABLES}${platform}`] if (commonPlatformVars) { - Object.defineProperties(vars, Object.getOwnPropertyDescriptors(commonPlatformVars)) + Object.assign(vars, commonPlatformVars) } if (platformVars) { - Object.defineProperties(vars, Object.getOwnPropertyDescriptors(platformVars)) + Object.assign(vars, platformVars) } this.stylesheet = stylesheet this.vars = Object.fromEntries(themes.map(theme => { - const clonedVars = cloneWithAccessors(vars) + const clonedVars = Object.create(vars) as Vars const themeVars = scopedVars[`${UNIWIND_THEME_VARIABLES}${theme}`] if (themeVars) { - Object.defineProperties(clonedVars, Object.getOwnPropertyDescriptors(themeVars)) + Object.assign(clonedVars, themeVars) } return [theme, clonedVars] @@ -97,7 +96,7 @@ class UniwindStoreBuilder { state: ComponentState | undefined, uniwindContext: UniwindContextType, ) { - const result = {} as Record + const resultGetters = {} as Record const theme = uniwindContext.scopedTheme ?? this.runtime.currentThemeName // At this point we're sure that theme is correct let vars = this.vars[theme]! @@ -160,20 +159,12 @@ class UniwindStoreBuilder { if (property[0] === '-') { // Clone vars object if we are adding inline variables if (vars === originalVars) { - vars = cloneWithAccessors(originalVars) + vars = Object.create(originalVars) } - Object.defineProperty(vars, property, { - configurable: true, - enumerable: true, - get: valueGetter, - }) + vars[property] = valueGetter } else { - Object.defineProperty(result, property, { - configurable: true, - enumerable: true, - get: () => valueGetter.call(vars), - }) + resultGetters[property] = valueGetter } bestBreakpoints.set(property, style) @@ -181,66 +172,42 @@ class UniwindStoreBuilder { } } + const result = Object.fromEntries( + Object.entries(resultGetters).map(([property, valueGetter]) => [property, valueGetter(vars)]), + ) as Record + if (result.lineHeight !== undefined && result.lineHeight < 6) { - Object.defineProperty(result, 'lineHeight', { - value: result.fontSize * result.lineHeight, - configurable: true, - enumerable: true, - }) + result.lineHeight *= result.fontSize } if (result.boxShadow !== undefined) { - Object.defineProperty(result, 'boxShadow', { - value: parseBoxShadow(result.boxShadow), - configurable: true, - enumerable: true, - }) + result.boxShadow = parseBoxShadow(result.boxShadow) } if (result.visibility === 'hidden') { - Object.defineProperty(result, 'display', { - value: 'none', - configurable: true, - enumerable: true, - }) + result.display = 'none' } if ( result.borderStyle !== undefined && result.borderColor === undefined ) { - Object.defineProperty(result, 'borderColor', { - value: '#000000', - configurable: true, - enumerable: true, - }) + result.borderColor = '#000000' } if ( result.outlineStyle !== undefined && result.outlineColor === undefined ) { - Object.defineProperty(result, 'outlineColor', { - value: '#000000', - configurable: true, - enumerable: true, - }) + result.outlineColor = '#000000' } if (result.fontVariant !== undefined) { - Object.defineProperty(result, 'fontVariant', { - value: parseFontVariant(result.fontVariant), - configurable: true, - enumerable: true, - }) + result.fontVariant = parseFontVariant(result.fontVariant) } parseTransformsMutation(result) if (result.experimental_backgroundImage !== undefined) { - Object.defineProperty(result, 'experimental_backgroundImage', { - value: resolveGradient(result.experimental_backgroundImage), - configurable: true, - enumerable: true, - }) + result.experimental_backgroundImage = resolveGradient(result.experimental_backgroundImage) } if (result.textShadow !== undefined) { @@ -248,7 +215,7 @@ class UniwindStoreBuilder { } return { - styles: { ...result } as RNStyle, + styles: result, dependencies: Array.from(dependencies), dependencySum, hasDataAttributes, diff --git a/packages/uniwind/src/core/types.ts b/packages/uniwind/src/core/types.ts index 0fb3ce04..28ba87cc 100644 --- a/packages/uniwind/src/core/types.ts +++ b/packages/uniwind/src/core/types.ts @@ -3,8 +3,11 @@ import type { ImageStyle, StyleProp, TextStyle, ViewStyle } from 'react-native' import type { ColorScheme, Orientation, StyleDependency } from '../common/consts' import type { UniwindContext } from './context' +export type Vars = Record +export type Var = (vars: Vars) => unknown + export type Style = { - entries: Array<[string, () => unknown]> + entries: Array<[string, Var]> minWidth: number maxWidth: number orientation: Orientation | null @@ -26,8 +29,8 @@ export type StyleSheets = Record> export type GenerateStyleSheetsCallback = (rt: UniwindRuntime) => { stylesheet: StyleSheets - vars: Record - scopedVars: Partial>> + vars: Vars + scopedVars: Partial> } export interface UniwindConfig {} diff --git a/packages/uniwind/src/hooks/useCSSVariable/getVariableValue.native.ts b/packages/uniwind/src/hooks/useCSSVariable/getVariableValue.native.ts index dae0d663..1db6a95d 100644 --- a/packages/uniwind/src/hooks/useCSSVariable/getVariableValue.native.ts +++ b/packages/uniwind/src/hooks/useCSSVariable/getVariableValue.native.ts @@ -1,5 +1,8 @@ import { UniwindRuntime, UniwindStore } from '../../core/native' import type { UniwindContextType } from '../../core/types' -export const getVariableValue = (name: string, uniwindContext: UniwindContextType) => - UniwindStore.vars[uniwindContext.scopedTheme ?? UniwindRuntime.currentThemeName]?.[name] +export const getVariableValue = (name: string, uniwindContext: UniwindContextType) => { + const vars = UniwindStore.vars[uniwindContext.scopedTheme ?? UniwindRuntime.currentThemeName] + + return vars?.[name]?.(vars) +} diff --git a/packages/uniwind/tests/native/styles-parsing/colors.test.tsx b/packages/uniwind/tests/native/styles-parsing/colors.test.tsx index 7a343cef..45da11ba 100644 --- a/packages/uniwind/tests/native/styles-parsing/colors.test.tsx +++ b/packages/uniwind/tests/native/styles-parsing/colors.test.tsx @@ -102,4 +102,16 @@ describe('Colors', () => { const customP3Styles = getStylesFromId('custom-p3') expect(customP3Styles.backgroundColor).toBe('#ffd400') }) + + test('Inline vars cascade before style resolution', () => { + const { getStylesFromId } = renderUniwind( + , + ) + + const styles = getStylesFromId('var-cascade-after') + expect(styles.color).toBe('#123456') + }) }) diff --git a/packages/uniwind/tests/test.css b/packages/uniwind/tests/test.css index 32ac20e7..9300e88e 100644 --- a/packages/uniwind/tests/test.css +++ b/packages/uniwind/tests/test.css @@ -40,3 +40,8 @@ flex-shrink: 1; flex-basis: 0%; } + +@utility var-cascade-after { + color: var(--inline-after); + --inline-after: #123456; +}