Skip to content

Commit 24de245

Browse files
authored
feat: major vars refactor to resolve vars using another vars (#90)
* feat: major vars refactor to resolve vars using another vars * chore: spread result
1 parent bc26678 commit 24de245

6 files changed

Lines changed: 97 additions & 73 deletions

File tree

packages/uniwind/src/core/native/native-utils.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,12 @@ export function lightDark(this: UniwindRuntime, light: string, dark: string) {
2626

2727
return light
2828
}
29+
30+
export const cloneWithAccessors = <T extends object>(obj: T) => {
31+
const proto = Object.getPrototypeOf(obj)
32+
const clone = Object.create(proto)
33+
34+
Object.defineProperties(clone, Object.getOwnPropertyDescriptors(obj))
35+
36+
return clone
37+
}

packages/uniwind/src/core/native/store.ts

Lines changed: 45 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import { Dimensions, Platform } from 'react-native'
33
import { Orientation, StyleDependency } from '../../types'
44
import { ComponentState, GenerateStyleSheetsCallback, RNStyle, Style, StyleSheets } from '../types'
5+
import { cloneWithAccessors } from './native-utils'
56
import { parseBoxShadow, parseFontVariant, parseTransformsMutation, resolveGradient } from './parsers'
67
import { UniwindRuntime } from './runtime'
78

@@ -11,8 +12,10 @@ type StylesResult = {
1112
}
1213

1314
class UniwindStoreBuilder {
14-
stylesheets = {} as StyleSheets
15-
listeners = {
15+
runtime = UniwindRuntime
16+
private stylesheet = {} as StyleSheets
17+
private vars = {} as Record<string, unknown>
18+
private listeners = {
1619
[StyleDependency.ColorScheme]: new Set<() => void>(),
1720
[StyleDependency.Theme]: new Set<() => void>(),
1821
[StyleDependency.Dimensions]: new Set<() => void>(),
@@ -21,8 +24,8 @@ class UniwindStoreBuilder {
2124
[StyleDependency.FontScale]: new Set<() => void>(),
2225
[StyleDependency.Rtl]: new Set<() => void>(),
2326
}
24-
runtime = UniwindRuntime
25-
cache = new Map<string, StylesResult>()
27+
private cache = new Map<string, StylesResult>()
28+
private generateStyleSheetCallbackResult: ReturnType<GenerateStyleSheetsCallback> | null = null
2629

2730
subscribe(onStoreChange: () => void, dependencies: Array<StyleDependency>) {
2831
dependencies.forEach(dep => {
@@ -65,19 +68,28 @@ class UniwindStoreBuilder {
6568
}
6669

6770
reinit = (generateStyleSheetCallback?: GenerateStyleSheetsCallback) => {
68-
const styleSheet = generateStyleSheetCallback?.(this.runtime) ?? this.stylesheets
69-
const themeVars = styleSheet[`__uniwind-theme-${this.runtime.currentThemeName}`]
70-
const platformVars = styleSheet[`__uniwind-platform-${Platform.OS}`]
71+
const config = generateStyleSheetCallback?.(this.runtime) ?? this.generateStyleSheetCallbackResult
72+
73+
if (!config) {
74+
return
75+
}
76+
77+
const { scopedVars, stylesheet, vars } = config
78+
79+
this.generateStyleSheetCallbackResult = config
80+
this.stylesheet = stylesheet
81+
this.vars = vars
82+
83+
const themeVars = scopedVars[`__uniwind-theme-${this.runtime.currentThemeName}`]
84+
const platformVars = scopedVars[`__uniwind-platform-${Platform.OS}`]
7185

7286
if (themeVars) {
73-
Object.assign(styleSheet, themeVars)
87+
Object.defineProperties(this.vars, Object.getOwnPropertyDescriptors(themeVars))
7488
}
7589

7690
if (platformVars) {
77-
Object.assign(styleSheet, platformVars)
91+
Object.defineProperties(this.vars, Object.getOwnPropertyDescriptors(platformVars))
7892
}
79-
80-
this.stylesheets = styleSheet
8193
}
8294

8395
notifyListeners = (dependencies: Array<StyleDependency>) => {
@@ -86,15 +98,16 @@ class UniwindStoreBuilder {
8698

8799
private resolveStyles(classNames: string, state?: ComponentState) {
88100
const result = {} as Record<string, any>
101+
let vars = this.vars
89102
const dependencies = [] as Array<StyleDependency>
90103
const bestBreakpoints = new Map<string, Style>()
91104

92105
for (const className of classNames.split(' ')) {
93-
if (!(className in this.stylesheets)) {
106+
if (!(className in this.stylesheet)) {
94107
continue
95108
}
96109

97-
for (const style of this.stylesheets[className] as Array<Style>) {
110+
for (const style of this.stylesheet[className] as Array<Style>) {
98111
dependencies.push(...style.dependencies)
99112

100113
if (
@@ -110,16 +123,6 @@ class UniwindStoreBuilder {
110123
continue
111124
}
112125

113-
style.usedVars.forEach(varName => {
114-
if (varName in this.stylesheets && !(varName in result)) {
115-
Object.defineProperty(result, varName, {
116-
configurable: true,
117-
enumerable: false,
118-
get: this.stylesheets[varName] as () => unknown,
119-
})
120-
}
121-
})
122-
123126
for (const [property, valueGetter] of style.entries) {
124127
const previousBest = bestBreakpoints.get(property)
125128

@@ -134,11 +137,25 @@ class UniwindStoreBuilder {
134137
continue
135138
}
136139

137-
Object.defineProperty(result, property, {
138-
configurable: true,
139-
get: valueGetter,
140-
enumerable: property[0] !== '-',
141-
})
140+
if (property[0] === '-') {
141+
// Clone vars object if we are adding inline variables
142+
if (vars === this.vars) {
143+
vars = cloneWithAccessors(this.vars)
144+
}
145+
146+
Object.defineProperty(vars, property, {
147+
configurable: true,
148+
enumerable: true,
149+
get: valueGetter,
150+
})
151+
} else {
152+
Object.defineProperty(result, property, {
153+
configurable: true,
154+
enumerable: true,
155+
get: () => valueGetter.call(vars),
156+
})
157+
}
158+
142159
bestBreakpoints.set(property, style)
143160
}
144161
}

packages/uniwind/src/core/types.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { ColorScheme, Orientation, StyleDependency, UniwindConfig } from '../typ
33

44
export type Style = {
55
entries: Array<[string, () => unknown]>
6-
usedVars: Array<string>
76
minWidth: number
87
maxWidth: number
98
orientation: Orientation | null
@@ -20,9 +19,13 @@ export type Style = {
2019
disabled: boolean | null
2120
}
2221

23-
export type StyleSheets = Record<string, Array<Style> | (() => unknown)>
22+
export type StyleSheets = Record<string, Array<Style>>
2423

25-
export type GenerateStyleSheetsCallback = (rt: UniwindRuntime) => StyleSheets
24+
export type GenerateStyleSheetsCallback = (rt: UniwindRuntime) => {
25+
stylesheet: StyleSheets
26+
vars: Record<string, unknown>
27+
scopedVars: Partial<Record<string, Record<string, unknown>>>
28+
}
2629

2730
type UserThemes = UniwindConfig extends { themes: infer T extends readonly string[] } ? T
2831
: readonly string[]
Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { compile } from '@tailwindcss/node'
22
import { polyfillWeb } from './polyfillWeb'
33
import { ProcessorBuilder } from './processor'
4-
import { addMetaToStylesTemplate, serializeStylesheet } from './stylesheet'
4+
import { addMetaToStylesTemplate, serializeJS } from './stylesheet'
55
import { Platform, Polyfills } from './types'
66

77
type CompileVirtualConfig = {
@@ -28,23 +28,35 @@ export const compileVirtual = async ({ candidates, css, cssPath, platform, theme
2828

2929
Processor.transform(tailwindCSS)
3030

31-
return serializeStylesheet({
32-
...Object.fromEntries(
33-
Object.entries(Processor.vars).map(([key, value]) => {
34-
if (key.startsWith('__uniwind-')) {
35-
return [
36-
key,
37-
Object.fromEntries(
38-
Object.entries(value).map(([nestedKey, nestedValue]) => {
39-
return [nestedKey, `function() { return ${nestedValue as any} }`]
40-
}),
41-
),
42-
]
43-
}
44-
45-
return [key, `function() { return ${value} }`]
46-
}),
31+
const stylesheet = serializeJS(
32+
addMetaToStylesTemplate(Processor, platform),
33+
(key, value) => `"${key}": ${value}`,
34+
)
35+
const vars = serializeJS(
36+
Object.fromEntries(
37+
Object.entries(Processor.vars)
38+
.filter(([key]) => !key.startsWith(`__uniwind-`)),
4739
),
48-
...addMetaToStylesTemplate(Processor, platform),
49-
})
40+
(key, value) => `get "${key}"() { return ${value} }`,
41+
)
42+
const scopedVars = Object.fromEntries(
43+
Object.entries(Processor.vars)
44+
.filter(([scopedVarsName]) => scopedVarsName.startsWith(`__uniwind-`))
45+
.map(([scopedVarsName, scopedVars]) => [
46+
scopedVarsName,
47+
serializeJS(scopedVars, (key, value) => `get "${key}"() { return ${value} }`),
48+
]),
49+
)
50+
const serializedScopedVars = Object.entries(scopedVars)
51+
.map(([scopedVarsName, scopedVars]) => `"${scopedVarsName}": ({ ${scopedVars} }),`)
52+
.join('')
53+
const currentColorVar = `get currentColor() { return rt.colorScheme === 'dark' ? '#ffffff' : '#000000' },`
54+
55+
return [
56+
'({',
57+
`scopedVars: ({ ${serializedScopedVars} }),`,
58+
`vars: ({ ${currentColorVar} ${vars} }),`,
59+
`stylesheet: ({ ${stylesheet} }),`,
60+
'})',
61+
].join('')
5062
}

packages/uniwind/src/metro/stylesheet/addMetaToStylesTemplate.ts

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,6 @@ import { ProcessorBuilder } from '../processor'
33
import { Platform, StyleSheetTemplate } from '../types'
44
import { isDefined, toCamelCase } from '../utils'
55

6-
const extractVarsFromString = (value: string) => {
7-
const thisIndexes = [...value.matchAll(/this\[/g)].map(m => m.index)
8-
9-
return thisIndexes.map(index => {
10-
const afterIndex = value.slice(index + 5)
11-
const closingIndex = afterIndex.indexOf(']')
12-
const varName = afterIndex.slice(0, closingIndex)
13-
14-
return varName.replace(/[`"\\]/g, '')
15-
})
16-
}
17-
186
export const addMetaToStylesTemplate = (Processor: ProcessorBuilder, currentPlatform: Platform) => {
197
const stylesheetsEntries = Object.entries(Processor.stylesheets as StyleSheetTemplate)
208
.map(([className, stylesPerMediaQuery]) => {
@@ -50,7 +38,6 @@ export const addMetaToStylesTemplate = (Processor: ProcessorBuilder, currentPlat
5038

5139
const dependencies: Array<StyleDependency> = []
5240
const stringifiedEntries = JSON.stringify(entries)
53-
const usedVars = extractVarsFromString(stringifiedEntries)
5441

5542
if (theme !== null || stringifiedEntries.includes('--color') || stringifiedEntries.includes('rt.lightDark')) {
5643
dependencies.push(StyleDependency.Theme)
@@ -95,7 +82,6 @@ export const addMetaToStylesTemplate = (Processor: ProcessorBuilder, currentPlat
9582
active,
9683
focus,
9784
disabled,
98-
usedVars,
9985
importantProperties: importantProperties?.map(property => property.startsWith('--') ? property : toCamelCase) ?? [],
10086
complexity: [
10187
minWidth !== 0,

packages/uniwind/src/metro/stylesheet/serializeStylesheet.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { Logger } from '../logger'
22
import { addMissingSpaces, isNumber, pipe, smartSplit } from '../utils'
33

4-
type Stylesheet = Record<string, any>
5-
64
const FN_DECLARATION = 'function() { return'
75

86
const isJSExpression = (value: string) =>
@@ -169,22 +167,21 @@ const serialize = (value: any): string => {
169167
}
170168
}
171169

172-
export const serializeStylesheet = (stylesheet: Stylesheet) => {
173-
const currentColor = `get currentColor() { return function() { return rt.colorScheme === 'dark' ? '#ffffff' : '#000000' } },`
174-
170+
export const serializeJS = (stylesheet: Record<string, any>, serializer: (key: string, value: string) => string) => {
171+
// eslint-disable-next-line prefer-template
175172
const serializedStylesheet = Object.entries(stylesheet).map(([key, value]) => {
176173
const stringifiedValue = isNumber(value)
177174
? String(value)
178175
: serialize(value)
179176

180-
return `"${key}": ${stringifiedValue}`
181-
}).join(',\n')
177+
return serializer(key, stringifiedValue)
178+
}).join(',') + ','
182179

183-
const js = `({ ${currentColor} ${serializedStylesheet} })`
180+
const js = `${serializedStylesheet}`
184181

185182
try {
186183
// eslint-disable-next-line @typescript-eslint/no-implied-eval, no-new-func
187-
new Function(`function validateJS() { const fn = rt => ${js} }`)
184+
new Function(`function validateJS() { const obj = ({ ${js} }) }`)
188185
} catch {
189186
Logger.error('Failed to create virtual js')
190187
return ''

0 commit comments

Comments
 (0)