@tamagui/static 3.0.0-beta.831.1 → 3.0.0-beta.881.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/compiler.cjs +17 -5
- package/dist/cjs/compilerHost.cjs +142 -38
- package/dist/cjs/componentDiscovery.cjs +90 -0
- package/dist/cjs/domStructuralPass.cjs +12 -2
- package/dist/cjs/exports.cjs +4 -1
- package/dist/cjs/extractor/bundleConfig.cjs +23 -0
- package/dist/cjs/zero/theme.cjs +5 -20
- package/dist/esm/compiler.mjs +18 -6
- package/dist/esm/compiler.mjs.map +1 -1
- package/dist/esm/compilerHost.mjs +140 -39
- package/dist/esm/compilerHost.mjs.map +1 -1
- package/dist/esm/componentDiscovery.mjs +65 -0
- package/dist/esm/componentDiscovery.mjs.map +1 -0
- package/dist/esm/domStructuralPass.mjs +12 -2
- package/dist/esm/domStructuralPass.mjs.map +1 -1
- package/dist/esm/exports.mjs +4 -1
- package/dist/esm/extractor/bundleConfig.mjs +23 -1
- package/dist/esm/extractor/bundleConfig.mjs.map +1 -1
- package/dist/esm/zero/theme.mjs +5 -20
- package/dist/esm/zero/theme.mjs.map +1 -1
- package/package.json +17 -17
- package/src/compiler.ts +27 -5
- package/src/compilerHost.ts +250 -73
- package/src/componentDiscovery.ts +95 -0
- package/src/domStructuralPass.ts +22 -2
- package/src/exports.ts +2 -0
- package/src/extractor/bundleConfig.ts +34 -0
- package/src/zero/theme.ts +12 -21
- package/types/compiler.d.ts +13 -0
- package/types/compiler.d.ts.map +1 -1
- package/types/compilerHost.d.ts +13 -0
- package/types/compilerHost.d.ts.map +1 -1
- package/types/componentDiscovery.d.ts +27 -0
- package/types/componentDiscovery.d.ts.map +1 -0
- package/types/domStructuralPass.d.ts.map +1 -1
- package/types/exports.d.ts +2 -0
- package/types/exports.d.ts.map +1 -1
- package/types/extractor/bundleConfig.d.ts +7 -0
- package/types/extractor/bundleConfig.d.ts.map +1 -1
- package/types/zero/theme.d.ts +0 -1
- package/types/zero/theme.d.ts.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tamagui/static",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.881.1",
|
|
4
4
|
"gitHead": "a49cc7ea6b93ba384e77a4880ae48ac4a5635c14",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"source": "src/index.ts",
|
|
@@ -45,21 +45,21 @@
|
|
|
45
45
|
"clean:build": "tamagui-build clean:build"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@tamagui/cli-color": "3.0.0-beta.
|
|
49
|
-
"@tamagui/compiler-core": "3.0.0-beta.
|
|
50
|
-
"@tamagui/config-default": "3.0.0-beta.
|
|
51
|
-
"@tamagui/core": "3.0.0-beta.
|
|
52
|
-
"@tamagui/dom": "3.0.0-beta.
|
|
53
|
-
"@tamagui/fake-react-native": "3.0.0-beta.
|
|
54
|
-
"@tamagui/generate-themes": "3.0.0-beta.
|
|
55
|
-
"@tamagui/helpers": "3.0.0-beta.
|
|
56
|
-
"@tamagui/helpers-node": "3.0.0-beta.
|
|
57
|
-
"@tamagui/react-native-web-internals": "3.0.0-beta.
|
|
58
|
-
"@tamagui/react-native-web-lite": "3.0.0-beta.
|
|
59
|
-
"@tamagui/shorthands": "3.0.0-beta.
|
|
60
|
-
"@tamagui/style-grammar": "3.0.0-beta.
|
|
61
|
-
"@tamagui/types": "3.0.0-beta.
|
|
62
|
-
"@tamagui/web": "3.0.0-beta.
|
|
48
|
+
"@tamagui/cli-color": "3.0.0-beta.881.1",
|
|
49
|
+
"@tamagui/compiler-core": "3.0.0-beta.881.1",
|
|
50
|
+
"@tamagui/config-default": "3.0.0-beta.881.1",
|
|
51
|
+
"@tamagui/core": "3.0.0-beta.881.1",
|
|
52
|
+
"@tamagui/dom": "3.0.0-beta.881.1",
|
|
53
|
+
"@tamagui/fake-react-native": "3.0.0-beta.881.1",
|
|
54
|
+
"@tamagui/generate-themes": "3.0.0-beta.881.1",
|
|
55
|
+
"@tamagui/helpers": "3.0.0-beta.881.1",
|
|
56
|
+
"@tamagui/helpers-node": "3.0.0-beta.881.1",
|
|
57
|
+
"@tamagui/react-native-web-internals": "3.0.0-beta.881.1",
|
|
58
|
+
"@tamagui/react-native-web-lite": "3.0.0-beta.881.1",
|
|
59
|
+
"@tamagui/shorthands": "3.0.0-beta.881.1",
|
|
60
|
+
"@tamagui/style-grammar": "3.0.0-beta.881.1",
|
|
61
|
+
"@tamagui/types": "3.0.0-beta.881.1",
|
|
62
|
+
"@tamagui/web": "3.0.0-beta.881.1",
|
|
63
63
|
"browserslist": "^4.28.1",
|
|
64
64
|
"check-dependency-version-consistency": "^4.1.0",
|
|
65
65
|
"esbuild": "0.28.1",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"react-native-web": "~0.21.0"
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
|
-
"@tamagui/build": "3.0.0-beta.
|
|
74
|
+
"@tamagui/build": "3.0.0-beta.881.1",
|
|
75
75
|
"@types/find-root": "^1.1.2",
|
|
76
76
|
"@types/node": "^22.1.0",
|
|
77
77
|
"@types/webpack": "^4.41.26",
|
package/src/compiler.ts
CHANGED
|
@@ -19,7 +19,8 @@ import { readFileSync } from 'node:fs'
|
|
|
19
19
|
import { createRequire } from 'node:module'
|
|
20
20
|
import path from 'node:path'
|
|
21
21
|
|
|
22
|
-
import {
|
|
22
|
+
import { ComponentDiscovery, type ComponentModuleEvaluator } from './componentDiscovery'
|
|
23
|
+
import { createComponentRegistry, createTamaguiCompilerHost } from './compilerHost'
|
|
23
24
|
import { domStructuralPass } from './domStructuralPass'
|
|
24
25
|
import type { TamaguiProjectInfo } from './extractor/bundleConfig'
|
|
25
26
|
import { loadTamagui } from './extractor/loadTamagui'
|
|
@@ -217,6 +218,15 @@ export interface CompilerInput {
|
|
|
217
218
|
project: CompilerProject
|
|
218
219
|
resolve(specifier: string, importer: string): Promise<CompilerResolution | null>
|
|
219
220
|
load(id: string): Promise<string | null>
|
|
221
|
+
/**
|
|
222
|
+
* Evaluate a host-resolved module and return its exports. The frontend asks
|
|
223
|
+
* for every package a JSX element or styled() base imports from that is not
|
|
224
|
+
* in the configured `components` list, once per module per project, and
|
|
225
|
+
* lowers against the static configs it finds. Return null (or throw) when
|
|
226
|
+
* the host cannot evaluate the module; those elements stay on the runtime
|
|
227
|
+
* path exactly as before.
|
|
228
|
+
*/
|
|
229
|
+
evaluate?: ComponentModuleEvaluator
|
|
220
230
|
}
|
|
221
231
|
|
|
222
232
|
export type CompilerUpdateInput = CompilerInput
|
|
@@ -257,6 +267,7 @@ export class CompilerFrontend {
|
|
|
257
267
|
private readonly planCaches = new Map<string, ModulePlanCache>()
|
|
258
268
|
private readonly moduleRecords = new Map<ResolvedModuleId, HostModuleInput>()
|
|
259
269
|
private moduleContext: string | null = null
|
|
270
|
+
private readonly discovery = new ComponentDiscovery()
|
|
260
271
|
|
|
261
272
|
/**
|
|
262
273
|
* One cache per project root and platform. Absent when the project produced
|
|
@@ -358,14 +369,18 @@ export class CompilerFrontend {
|
|
|
358
369
|
if (!projectInfo.tamaguiConfig || !projectInfo.components) {
|
|
359
370
|
throw new Error('The compiler requires evaluated Tamagui config and components')
|
|
360
371
|
}
|
|
372
|
+
const componentModules = input.project.componentModules.map((component) => ({
|
|
373
|
+
moduleName: component.moduleName,
|
|
374
|
+
resolvedId: cleanId(component.id),
|
|
375
|
+
}))
|
|
376
|
+
const registry = createComponentRegistry(projectInfo.components, componentModules)
|
|
377
|
+
this.discovery.seed(registry)
|
|
361
378
|
const host = createTamaguiCompilerHost({
|
|
362
379
|
target: input.target,
|
|
363
380
|
tamaguiConfig: projectInfo.tamaguiConfig,
|
|
364
381
|
components: projectInfo.components,
|
|
365
|
-
componentModules
|
|
366
|
-
|
|
367
|
-
resolvedId: cleanId(component.id),
|
|
368
|
-
})),
|
|
382
|
+
componentModules,
|
|
383
|
+
registry,
|
|
369
384
|
disablePartialExtraction: input.project.disablePartialExtraction,
|
|
370
385
|
experimentalNativeFastPath: input.project.experimentalNativeFastPath,
|
|
371
386
|
zeroRuntime: input.project.zeroRuntime,
|
|
@@ -380,6 +395,7 @@ export class CompilerFrontend {
|
|
|
380
395
|
async load(id) {
|
|
381
396
|
return modules.get(id) ?? null
|
|
382
397
|
},
|
|
398
|
+
prepare: (module) => this.discovery.prepare(module, registry, input.evaluate),
|
|
383
399
|
},
|
|
384
400
|
structuralPass: domStructuralPass,
|
|
385
401
|
})
|
|
@@ -391,6 +407,11 @@ export class CompilerFrontend {
|
|
|
391
407
|
}
|
|
392
408
|
}
|
|
393
409
|
|
|
410
|
+
/** host-resolved ids of every module discovery found components in */
|
|
411
|
+
discoveredModuleIds(): string[] {
|
|
412
|
+
return this.discovery.ids()
|
|
413
|
+
}
|
|
414
|
+
|
|
394
415
|
private async buildTree(input: CompilerUpdateInput): Promise<{
|
|
395
416
|
rootModule: HostModuleInput
|
|
396
417
|
modules: Map<ResolvedModuleId, HostModuleInput>
|
|
@@ -398,6 +419,7 @@ export class CompilerFrontend {
|
|
|
398
419
|
const moduleContext = compilerContext(input)
|
|
399
420
|
if (this.moduleContext !== moduleContext) {
|
|
400
421
|
this.moduleRecords.clear()
|
|
422
|
+
this.discovery.clear()
|
|
401
423
|
this.moduleContext = moduleContext
|
|
402
424
|
}
|
|
403
425
|
|
package/src/compilerHost.ts
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
StyleObjectRules,
|
|
22
22
|
StyleObjectValue,
|
|
23
23
|
stylePropsAll,
|
|
24
|
+
stylePropsInput,
|
|
24
25
|
stylePropsText,
|
|
25
26
|
validStyles as validStylesView,
|
|
26
27
|
} from '@tamagui/helpers'
|
|
@@ -55,11 +56,38 @@ export interface CompilerComponentModule {
|
|
|
55
56
|
resolvedId: string
|
|
56
57
|
}
|
|
57
58
|
|
|
59
|
+
/**
|
|
60
|
+
* The components lowering can resolve, keyed the way element provenance names
|
|
61
|
+
* them. The frontend owns one per compile and grows it as it discovers component
|
|
62
|
+
* modules outside the configured `components` list.
|
|
63
|
+
*/
|
|
64
|
+
export interface CompilerComponentRegistry {
|
|
65
|
+
/** host-resolved module id -> the module name its components register under */
|
|
66
|
+
modulesById: Map<string, string>
|
|
67
|
+
componentsByModule: Map<string, LoadedComponents>
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function createComponentRegistry(
|
|
71
|
+
components: readonly LoadedComponents[],
|
|
72
|
+
componentModules: readonly CompilerComponentModule[]
|
|
73
|
+
): CompilerComponentRegistry {
|
|
74
|
+
return {
|
|
75
|
+
modulesById: new Map(
|
|
76
|
+
componentModules.map((module) => [module.resolvedId, module.moduleName])
|
|
77
|
+
),
|
|
78
|
+
componentsByModule: new Map(
|
|
79
|
+
components.map((component) => [component.moduleName, component])
|
|
80
|
+
),
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
58
84
|
export interface TamaguiCompilerHostOptions {
|
|
59
85
|
target: CompilerTarget
|
|
60
86
|
tamaguiConfig: TamaguiInternalConfig
|
|
61
87
|
components: LoadedComponents[]
|
|
62
88
|
componentModules: CompilerComponentModule[]
|
|
89
|
+
/** shared with the frontend so discovery during `prepare` is visible to this host */
|
|
90
|
+
registry?: CompilerComponentRegistry
|
|
63
91
|
/** Keep elements with dynamic style props fully on the runtime path. */
|
|
64
92
|
disablePartialExtraction?: boolean
|
|
65
93
|
/** emit native theme-token mappings for the native style engine */
|
|
@@ -390,6 +418,11 @@ const runtimeAnimationProps = new Set([
|
|
|
390
418
|
'animatedBy',
|
|
391
419
|
])
|
|
392
420
|
|
|
421
|
+
// createComponent reads these and never forwards them to the host element, so a
|
|
422
|
+
// flattened element has to drop them instead of emitting an unknown attribute.
|
|
423
|
+
// only the inert values reach the emit: an active one already bailed.
|
|
424
|
+
const componentOnlyProps = new Set(['asChild', 'disableOptimization', 'themeInverse'])
|
|
425
|
+
|
|
393
426
|
const cssShorthandConflicts: Record<string, readonly string[]> = {
|
|
394
427
|
background: [
|
|
395
428
|
'backgroundAttachment',
|
|
@@ -548,6 +581,12 @@ const cssShorthandConflicts: Record<string, readonly string[]> = {
|
|
|
548
581
|
}
|
|
549
582
|
|
|
550
583
|
const cssConflictFamilies = [
|
|
584
|
+
new Set(['width', 'inlineSize']),
|
|
585
|
+
new Set(['minWidth', 'minInlineSize']),
|
|
586
|
+
new Set(['maxWidth', 'maxInlineSize']),
|
|
587
|
+
new Set(['height', 'blockSize']),
|
|
588
|
+
new Set(['minHeight', 'minBlockSize']),
|
|
589
|
+
new Set(['maxHeight', 'maxBlockSize']),
|
|
551
590
|
new Set([
|
|
552
591
|
'marginInline',
|
|
553
592
|
'marginInlineEnd',
|
|
@@ -943,6 +982,8 @@ export function createTamaguiCompilerHost(
|
|
|
943
982
|
): CompilerLoweringHost {
|
|
944
983
|
const platform = options.target === 'native' ? 'native' : 'web'
|
|
945
984
|
const core = requireTamaguiCore(platform) as any
|
|
985
|
+
const compilerVariantStyleResolver = core.styled(core.View, {}).staticConfig
|
|
986
|
+
.variantStyleResolver
|
|
946
987
|
const firstThemeName = Object.keys(options.tamaguiConfig.themes ?? {})[0] ?? ''
|
|
947
988
|
const firstTheme = options.tamaguiConfig.themes?.[firstThemeName] ?? {}
|
|
948
989
|
const theme = firstTheme
|
|
@@ -968,6 +1009,39 @@ export function createTamaguiCompilerHost(
|
|
|
968
1009
|
}
|
|
969
1010
|
return false
|
|
970
1011
|
}
|
|
1012
|
+
// A native bundle knows one thing about its platform: it is not web. So a
|
|
1013
|
+
// `web:` clause can never match there and a bare `native:` clause always
|
|
1014
|
+
// does, which makes a value built only from those two resolvable at compile
|
|
1015
|
+
// time. Every other modifier is live at render: media and container queries
|
|
1016
|
+
// measure, theme and group clauses read context, state and lifecycle clauses
|
|
1017
|
+
// track the component, and the device platforms (ios, android, tv) are not
|
|
1018
|
+
// known until the bundle runs. One of those anywhere in the value keeps the
|
|
1019
|
+
// whole element on the runtime path.
|
|
1020
|
+
const platformModifiersRegistered =
|
|
1021
|
+
modifierRegistry.get('web') === 'platform' &&
|
|
1022
|
+
modifierRegistry.get('native') === 'platform'
|
|
1023
|
+
const nativeClauseValue = (
|
|
1024
|
+
value: unknown
|
|
1025
|
+
): 'live' | { payload: unknown; matched: boolean } | null => {
|
|
1026
|
+
if (typeof value !== 'string' || !flatClausePattern.test(value)) {
|
|
1027
|
+
return isClauseObjectValue(value) ? 'live' : null
|
|
1028
|
+
}
|
|
1029
|
+
if (!platformModifiersRegistered) return 'live'
|
|
1030
|
+
const parsed = parseValue(value, modifierRegistry)
|
|
1031
|
+
if (!parsed.ok || parsed.value.clauses.length === 0) return 'live'
|
|
1032
|
+
let payload: unknown = parsed.value.base
|
|
1033
|
+
let matched = parsed.value.base !== null
|
|
1034
|
+
for (const clause of parsed.value.clauses) {
|
|
1035
|
+
// a chain containing `web` is dead here whatever else it names
|
|
1036
|
+
if (clause.modifiers.includes('web')) continue
|
|
1037
|
+
if (clause.modifiers.length !== 1 || clause.modifiers[0] !== 'native') return 'live'
|
|
1038
|
+
// platform clauses all rank the same, so a later one wins and any of them
|
|
1039
|
+
// beats the unconditional base
|
|
1040
|
+
payload = clause.payload
|
|
1041
|
+
matched = true
|
|
1042
|
+
}
|
|
1043
|
+
return { payload, matched }
|
|
1044
|
+
}
|
|
971
1045
|
const configuredAnimationDriver = options.tamaguiConfig.animations as
|
|
972
1046
|
| AnimationDriver
|
|
973
1047
|
| undefined
|
|
@@ -1031,12 +1105,9 @@ export function createTamaguiCompilerHost(
|
|
|
1031
1105
|
}
|
|
1032
1106
|
return resolved.join(' ')
|
|
1033
1107
|
}
|
|
1034
|
-
const modulesById
|
|
1035
|
-
options.
|
|
1036
|
-
|
|
1037
|
-
const componentsByModule = new Map(
|
|
1038
|
-
options.components.map((component) => [component.moduleName, component])
|
|
1039
|
-
)
|
|
1108
|
+
const { modulesById, componentsByModule } =
|
|
1109
|
+
options.registry ??
|
|
1110
|
+
createComponentRegistry(options.components, options.componentModules)
|
|
1040
1111
|
|
|
1041
1112
|
// A component's import provenance chose its authoring syntax, and the frontend
|
|
1042
1113
|
// descriptor frozen onto its static config is the only thing that knows how to
|
|
@@ -1122,7 +1193,10 @@ export function createTamaguiCompilerHost(
|
|
|
1122
1193
|
isInput: row.backing === 'textinput',
|
|
1123
1194
|
// CSS text properties may be authored on any element and inherited by
|
|
1124
1195
|
// descendant text, including from a View-backed tag on native.
|
|
1125
|
-
validStyles: {
|
|
1196
|
+
validStyles: {
|
|
1197
|
+
...validStylesView,
|
|
1198
|
+
...(row.backing === 'textinput' ? stylePropsInput : stylePropsText),
|
|
1199
|
+
},
|
|
1126
1200
|
defaultProps: {
|
|
1127
1201
|
...base.defaultProps,
|
|
1128
1202
|
...platformDefaults,
|
|
@@ -1160,29 +1234,31 @@ export function createTamaguiCompilerHost(
|
|
|
1160
1234
|
typeof definition.baseClassName.value === 'string'
|
|
1161
1235
|
? definition.baseClassName.value
|
|
1162
1236
|
: undefined
|
|
1237
|
+
const localStaticConfig = {
|
|
1238
|
+
...base.staticConfig,
|
|
1239
|
+
variants: {
|
|
1240
|
+
...base.staticConfig.variants,
|
|
1241
|
+
...(variants as object | undefined),
|
|
1242
|
+
},
|
|
1243
|
+
defaultProps: {
|
|
1244
|
+
...base.staticConfig.defaultProps,
|
|
1245
|
+
...defaultProps,
|
|
1246
|
+
...(defaultVariants as object | undefined),
|
|
1247
|
+
},
|
|
1248
|
+
defaultVariants,
|
|
1249
|
+
baseClassName: [base.staticConfig.baseClassName, baseClassName]
|
|
1250
|
+
.filter(Boolean)
|
|
1251
|
+
.join(' '),
|
|
1252
|
+
context: context ?? base.staticConfig.context,
|
|
1253
|
+
contextProps: context
|
|
1254
|
+
? contextProps
|
|
1255
|
+
: (contextProps ?? base.staticConfig.contextProps),
|
|
1256
|
+
}
|
|
1257
|
+
;(localStaticConfig as any).variantStyleResolver = compilerVariantStyleResolver
|
|
1163
1258
|
return {
|
|
1164
1259
|
key: componentKey(definition.id, definition.name),
|
|
1165
1260
|
displayName: displayName || base.displayName,
|
|
1166
|
-
staticConfig: normalizeStaticConfig(
|
|
1167
|
-
...base.staticConfig,
|
|
1168
|
-
variants: {
|
|
1169
|
-
...base.staticConfig.variants,
|
|
1170
|
-
...(variants as object | undefined),
|
|
1171
|
-
},
|
|
1172
|
-
defaultProps: {
|
|
1173
|
-
...base.staticConfig.defaultProps,
|
|
1174
|
-
...defaultProps,
|
|
1175
|
-
...(defaultVariants as object | undefined),
|
|
1176
|
-
},
|
|
1177
|
-
defaultVariants,
|
|
1178
|
-
baseClassName: [base.staticConfig.baseClassName, baseClassName]
|
|
1179
|
-
.filter(Boolean)
|
|
1180
|
-
.join(' '),
|
|
1181
|
-
context: context ?? base.staticConfig.context,
|
|
1182
|
-
contextProps: context
|
|
1183
|
-
? contextProps
|
|
1184
|
-
: (contextProps ?? base.staticConfig.contextProps),
|
|
1185
|
-
}),
|
|
1261
|
+
staticConfig: normalizeStaticConfig(localStaticConfig),
|
|
1186
1262
|
}
|
|
1187
1263
|
}
|
|
1188
1264
|
|
|
@@ -1254,10 +1330,12 @@ export function createTamaguiCompilerHost(
|
|
|
1254
1330
|
const staticConfig = component.staticConfig as StaticConfig
|
|
1255
1331
|
const validStyles =
|
|
1256
1332
|
staticConfig.validStyles ||
|
|
1257
|
-
(staticConfig.
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1333
|
+
(staticConfig.isInput
|
|
1334
|
+
? stylePropsInput
|
|
1335
|
+
: staticConfig.isText
|
|
1336
|
+
? stylePropsText
|
|
1337
|
+
: validStylesView)
|
|
1338
|
+
return name in stylePropsAll && !isValidStyleKey(name, validStyles)
|
|
1261
1339
|
}
|
|
1262
1340
|
|
|
1263
1341
|
const directStyleName = (name: string, component: LoweringComponent): string | null => {
|
|
@@ -1292,6 +1370,7 @@ export function createTamaguiCompilerHost(
|
|
|
1292
1370
|
firstThemeName,
|
|
1293
1371
|
componentState,
|
|
1294
1372
|
{
|
|
1373
|
+
isStatic: true,
|
|
1295
1374
|
resolveValues: platform === 'native' ? 'except-theme' : 'variable',
|
|
1296
1375
|
noClass: platform === 'native',
|
|
1297
1376
|
isAnimated: false,
|
|
@@ -1501,6 +1580,12 @@ export function createTamaguiCompilerHost(
|
|
|
1501
1580
|
) {
|
|
1502
1581
|
return true
|
|
1503
1582
|
}
|
|
1583
|
+
// A component resolver receives the complete props object. Without a
|
|
1584
|
+
// compiler-derived read set, resolving an opaque dynamic callsite would
|
|
1585
|
+
// execute the real resolver with an incomplete props snapshot.
|
|
1586
|
+
if ((component.staticConfig as StaticConfig).resolvers?.length) {
|
|
1587
|
+
return false
|
|
1588
|
+
}
|
|
1504
1589
|
return (
|
|
1505
1590
|
!options.disablePartialExtraction &&
|
|
1506
1591
|
((platform === 'web' && !!directStyleName(name, component)) ||
|
|
@@ -1556,15 +1641,56 @@ export function createTamaguiCompilerHost(
|
|
|
1556
1641
|
props[entry.name] = entry.value.value
|
|
1557
1642
|
}
|
|
1558
1643
|
}
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1644
|
+
if (component.staticConfig.resolvers?.length) {
|
|
1645
|
+
const unresolvedProps = input.element.entries.filter(
|
|
1646
|
+
(entry) => entry.kind === 'prop' && entry.value.kind !== 'static'
|
|
1647
|
+
)
|
|
1648
|
+
// One conditional with static leaves is still a complete prop set:
|
|
1649
|
+
// lowerCandidate evaluates the full resolver chain once per branch.
|
|
1650
|
+
// Multiple unknown props would require a cross-product/read set, so
|
|
1651
|
+
// they remain on the runtime path.
|
|
1652
|
+
const onlyStaticBranchConditional =
|
|
1653
|
+
unresolvedProps.length === 1 &&
|
|
1654
|
+
unresolvedProps[0]?.kind === 'prop' &&
|
|
1655
|
+
unresolvedProps[0].value.kind === 'conditional' &&
|
|
1656
|
+
canLowerConditionalStyleProp(unresolvedProps[0].name, component)
|
|
1657
|
+
const unresolvedProp = onlyStaticBranchConditional
|
|
1658
|
+
? undefined
|
|
1659
|
+
: unresolvedProps[0]
|
|
1660
|
+
if (unresolvedProp?.kind === 'prop') {
|
|
1661
|
+
return bailout(
|
|
1662
|
+
input,
|
|
1663
|
+
'local/dynamic-style-value',
|
|
1664
|
+
`Component resolver prop ${unresolvedProp.name} could not be evaluated`,
|
|
1665
|
+
unresolvedProp.span,
|
|
1666
|
+
undefined,
|
|
1667
|
+
unresolvedProp.name
|
|
1668
|
+
)
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
// asChild, disableOptimization and themeInverse each asked only whether the
|
|
1672
|
+
// prop was PRESENT, so a false value retained a component that requested
|
|
1673
|
+
// none of the runtime behavior the branch exists for. read the materialized
|
|
1674
|
+
// value instead, and treat a prop the compiler could not evaluate as active.
|
|
1675
|
+
// a later duplicate wins, so the last entry decides and any unevaluated
|
|
1676
|
+
// entry for that name makes the answer unknown.
|
|
1677
|
+
const componentOnlyProp = (name: string) => {
|
|
1678
|
+
let span: MaterializedElement['entries'][number]['span'] | undefined
|
|
1679
|
+
let unknown = false
|
|
1680
|
+
for (const entry of input.element.entries) {
|
|
1681
|
+
if (entry.kind !== 'prop' || entry.name !== name) continue
|
|
1682
|
+
span = entry.span
|
|
1683
|
+
if (entry.value.kind !== 'static') unknown = true
|
|
1684
|
+
}
|
|
1685
|
+
return { unknown, span }
|
|
1686
|
+
}
|
|
1687
|
+
const disableOptimization = componentOnlyProp('disableOptimization')
|
|
1688
|
+
if (disableOptimization.unknown || props.disableOptimization) {
|
|
1563
1689
|
return bailout(
|
|
1564
1690
|
input,
|
|
1565
1691
|
'local/unsupported-target',
|
|
1566
1692
|
'disableOptimization keeps the component on the runtime path',
|
|
1567
|
-
|
|
1693
|
+
disableOptimization.span
|
|
1568
1694
|
)
|
|
1569
1695
|
}
|
|
1570
1696
|
if (component.domTag && props.hidden) props.display = 'none'
|
|
@@ -1761,11 +1887,7 @@ export function createTamaguiCompilerHost(
|
|
|
1761
1887
|
name === 'opacity'
|
|
1762
1888
|
? `opacity: (${expression})`
|
|
1763
1889
|
: `transform: "scale(" + (${expression}) + ")"`
|
|
1764
|
-
} else if (
|
|
1765
|
-
entry.value.kind === 'bailout' &&
|
|
1766
|
-
owners.size === 1 &&
|
|
1767
|
-
owners.has(name)
|
|
1768
|
-
) {
|
|
1890
|
+
} else if (entry.value.kind === 'bailout' && owners.has(name)) {
|
|
1769
1891
|
const dynamic = entry.value.dynamic
|
|
1770
1892
|
if (dynamic?.type === 'number') {
|
|
1771
1893
|
property = `${JSON.stringify(name)}: (${expression})`
|
|
@@ -1831,8 +1953,13 @@ export function createTamaguiCompilerHost(
|
|
|
1831
1953
|
entry.value.kind === 'conditional' &&
|
|
1832
1954
|
canLowerConditionalStyleProp(entry.name, component)
|
|
1833
1955
|
)
|
|
1834
|
-
|
|
1835
|
-
|
|
1956
|
+
// a theme name of any value selects a theme, while themeInverse only opens a
|
|
1957
|
+
// boundary when it is on
|
|
1958
|
+
const themeEntry = componentOnlyProp('theme')
|
|
1959
|
+
const themeInverseEntry = componentOnlyProp('themeInverse')
|
|
1960
|
+
const themeBoundary = themeEntry.unknown || 'theme' in props
|
|
1961
|
+
if (themeBoundary || themeInverseEntry.unknown || props.themeInverse) {
|
|
1962
|
+
const themeProp = themeBoundary ? 'theme' : 'themeInverse'
|
|
1836
1963
|
return bailout(
|
|
1837
1964
|
input,
|
|
1838
1965
|
'local/unsupported-target',
|
|
@@ -1848,15 +1975,13 @@ export function createTamaguiCompilerHost(
|
|
|
1848
1975
|
// the single child and emits no element of its own. a flattened host view
|
|
1849
1976
|
// would add a wrapper the runtime never renders, and the child would
|
|
1850
1977
|
// never receive the merged props. both platforms.
|
|
1851
|
-
const
|
|
1852
|
-
|
|
1853
|
-
)
|
|
1854
|
-
if (asChildEntry || 'asChild' in props) {
|
|
1978
|
+
const asChild = componentOnlyProp('asChild')
|
|
1979
|
+
if (asChild.unknown || props.asChild) {
|
|
1855
1980
|
return bailout(
|
|
1856
1981
|
input,
|
|
1857
1982
|
'local/unsupported-target',
|
|
1858
1983
|
'asChild renders a Slot, not a host view',
|
|
1859
|
-
|
|
1984
|
+
asChild.span
|
|
1860
1985
|
)
|
|
1861
1986
|
}
|
|
1862
1987
|
// group and container props publish separate keys in the same context:
|
|
@@ -2047,7 +2172,11 @@ export function createTamaguiCompilerHost(
|
|
|
2047
2172
|
entry.span
|
|
2048
2173
|
)
|
|
2049
2174
|
}
|
|
2050
|
-
const
|
|
2175
|
+
const resolvedStyleStaticConfig = core.getStyleStaticConfig(
|
|
2176
|
+
component.staticConfig,
|
|
2177
|
+
core.getConfig()
|
|
2178
|
+
)
|
|
2179
|
+
const staticDefaultProps = resolvedStyleStaticConfig.defaultProps ?? {}
|
|
2051
2180
|
const defaultProps =
|
|
2052
2181
|
platform === 'web' &&
|
|
2053
2182
|
!component.staticConfig.isText &&
|
|
@@ -2062,6 +2191,19 @@ export function createTamaguiCompilerHost(
|
|
|
2062
2191
|
props
|
|
2063
2192
|
)
|
|
2064
2193
|
}
|
|
2194
|
+
const domStyleProgram = input.element.entries.find(
|
|
2195
|
+
(entry) =>
|
|
2196
|
+
entry.kind === 'prop' &&
|
|
2197
|
+
entry.name === 'style' &&
|
|
2198
|
+
entry.value.kind === 'dom-style'
|
|
2199
|
+
)
|
|
2200
|
+
if (platform === 'native' && !component.domTag && domStyleProgram) {
|
|
2201
|
+
return bailout(
|
|
2202
|
+
input,
|
|
2203
|
+
'local/unsupported-target',
|
|
2204
|
+
'Native style() pieces remain on the runtime path'
|
|
2205
|
+
)
|
|
2206
|
+
}
|
|
2065
2207
|
const propsForConditional = (
|
|
2066
2208
|
target: Extract<MaterializedElement['entries'][number], { kind: 'prop' }>,
|
|
2067
2209
|
value: unknown
|
|
@@ -2099,22 +2241,35 @@ export function createTamaguiCompilerHost(
|
|
|
2099
2241
|
branchProps
|
|
2100
2242
|
)
|
|
2101
2243
|
: core.mergeProps(defaultProps, branchProps)
|
|
2244
|
+
// the same platform-clause reduction the unconditional props get: a
|
|
2245
|
+
// branch resolves through the full pipeline, so a clause left standing
|
|
2246
|
+
// here would be decided by the build machine instead of the bundle
|
|
2247
|
+
if (platform === 'native') {
|
|
2248
|
+
for (const [name, value] of Object.entries(branchCompleteProps)) {
|
|
2249
|
+
const clause = isStyleProp(name, component) ? nativeClauseValue(value) : null
|
|
2250
|
+
if (clause === null || clause === 'live') continue
|
|
2251
|
+
if (clause.matched) branchCompleteProps[name] = clause.payload
|
|
2252
|
+
else delete branchCompleteProps[name]
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
2102
2255
|
return { branchProps, branchCompleteProps }
|
|
2103
2256
|
}
|
|
2104
|
-
//
|
|
2105
|
-
//
|
|
2106
|
-
//
|
|
2257
|
+
// native resolution evaluates clauses against the build machine's current
|
|
2258
|
+
// state, so folding a call-site, styled base, or variant clause would freeze
|
|
2259
|
+
// that state into the bundle.
|
|
2107
2260
|
if (platform === 'native') {
|
|
2108
2261
|
const isClauseValue = (name: string, value: unknown) =>
|
|
2109
2262
|
isStyleProp(name, component) &&
|
|
2110
2263
|
((typeof value === 'string' && flatClausePattern.test(value)) ||
|
|
2111
2264
|
isClauseObjectValue(value))
|
|
2112
2265
|
// a clause can also sit inside a variant definition, where it never
|
|
2113
|
-
// appears as a prop: variants: { big: { true: { width: 'gt-lg:999px' } } }
|
|
2266
|
+
// appears as a prop: variants: { big: { true: { width: 'gt-lg:999px' } } }.
|
|
2267
|
+
// those live in the styled definition, which this element cannot rewrite,
|
|
2268
|
+
// so a clause there retains whatever kind it is.
|
|
2114
2269
|
const defaultVariants = component.staticConfig.defaultVariants ?? {}
|
|
2115
|
-
const
|
|
2116
|
-
Object.entries(
|
|
2117
|
-
isClauseValue(name, value)
|
|
2270
|
+
const definitionCarriesClause =
|
|
2271
|
+
Object.entries(resolvedStyleStaticConfig.baseStyle ?? {}).some(
|
|
2272
|
+
([name, value]) => isClauseValue(name, value)
|
|
2118
2273
|
) ||
|
|
2119
2274
|
Object.entries(component.staticConfig.variants ?? {}).some(
|
|
2120
2275
|
([variantName, definitions]) =>
|
|
@@ -2129,13 +2284,43 @@ export function createTamaguiCompilerHost(
|
|
|
2129
2284
|
)
|
|
2130
2285
|
)
|
|
2131
2286
|
)
|
|
2132
|
-
if (
|
|
2287
|
+
if (definitionCarriesClause) {
|
|
2133
2288
|
return bailout(
|
|
2134
2289
|
input,
|
|
2135
2290
|
'local/unsupported-target',
|
|
2136
2291
|
'Native conditional value programs remain on the runtime path'
|
|
2137
2292
|
)
|
|
2138
2293
|
}
|
|
2294
|
+
const reducedProps: Record<string, unknown> = {}
|
|
2295
|
+
for (const [name, value] of Object.entries(completeProps)) {
|
|
2296
|
+
const clause = isStyleProp(name, component) ? nativeClauseValue(value) : null
|
|
2297
|
+
if (clause === 'live') {
|
|
2298
|
+
return bailout(
|
|
2299
|
+
input,
|
|
2300
|
+
'local/unsupported-target',
|
|
2301
|
+
'Native conditional value programs remain on the runtime path'
|
|
2302
|
+
)
|
|
2303
|
+
}
|
|
2304
|
+
if (clause === null) {
|
|
2305
|
+
reducedProps[name] = value
|
|
2306
|
+
continue
|
|
2307
|
+
}
|
|
2308
|
+
if (clause.matched) reducedProps[name] = clause.payload
|
|
2309
|
+
}
|
|
2310
|
+
completeProps = reducedProps
|
|
2311
|
+
// a conditional leaf is a clause program the same way a direct prop is,
|
|
2312
|
+
// and propsForConditional reduces the ones that fold
|
|
2313
|
+
for (const entry of dynamicStyleEntries) {
|
|
2314
|
+
if (entry.value.kind !== 'conditional') continue
|
|
2315
|
+
for (const leaf of collectLeaves(entry.value.tree)) {
|
|
2316
|
+
if (nativeClauseValue(leaf.value) !== 'live') continue
|
|
2317
|
+
return bailout(
|
|
2318
|
+
input,
|
|
2319
|
+
'local/unsupported-target',
|
|
2320
|
+
'Native conditional value programs remain on the runtime path'
|
|
2321
|
+
)
|
|
2322
|
+
}
|
|
2323
|
+
}
|
|
2139
2324
|
}
|
|
2140
2325
|
const split = resolveSplitStyles(
|
|
2141
2326
|
completeProps,
|
|
@@ -2167,13 +2352,6 @@ export function createTamaguiCompilerHost(
|
|
|
2167
2352
|
}
|
|
2168
2353
|
)
|
|
2169
2354
|
}
|
|
2170
|
-
const domStyleProgram = input.element.entries.find(
|
|
2171
|
-
(entry) =>
|
|
2172
|
-
entry.kind === 'prop' &&
|
|
2173
|
-
entry.name === 'style' &&
|
|
2174
|
-
entry.value.kind === 'dom-style'
|
|
2175
|
-
)
|
|
2176
|
-
|
|
2177
2355
|
const flatTag =
|
|
2178
2356
|
component.domTag ??
|
|
2179
2357
|
(typeof props.render === 'string'
|
|
@@ -2193,7 +2371,9 @@ export function createTamaguiCompilerHost(
|
|
|
2193
2371
|
}))
|
|
2194
2372
|
|
|
2195
2373
|
const isPropIgnored = (name: string) =>
|
|
2196
|
-
isStyleProp(name, component) ||
|
|
2374
|
+
isStyleProp(name, component) ||
|
|
2375
|
+
isInvalidHostStyleProp(name, component) ||
|
|
2376
|
+
componentOnlyProps.has(name)
|
|
2197
2377
|
const spreadReplacement = (
|
|
2198
2378
|
form: MaterializedElement['form'],
|
|
2199
2379
|
entry: MaterializedElement['entries'][number]
|
|
@@ -2210,16 +2390,11 @@ export function createTamaguiCompilerHost(
|
|
|
2210
2390
|
|
|
2211
2391
|
let styleEntries = input.element.entries.filter(
|
|
2212
2392
|
(entry) =>
|
|
2213
|
-
(entry.kind === 'prop' &&
|
|
2214
|
-
(isStyleProp(entry.name, component) ||
|
|
2215
|
-
isInvalidHostStyleProp(entry.name, component))) ||
|
|
2393
|
+
(entry.kind === 'prop' && isPropIgnored(entry.name)) ||
|
|
2216
2394
|
(entry.kind === 'spread' &&
|
|
2217
2395
|
entry.value.kind === 'static' &&
|
|
2218
2396
|
staticObject(entry.value.value) &&
|
|
2219
|
-
Object.keys(entry.value.value).some(
|
|
2220
|
-
(name) =>
|
|
2221
|
-
isStyleProp(name, component) || isInvalidHostStyleProp(name, component)
|
|
2222
|
-
))
|
|
2397
|
+
Object.keys(entry.value.value).some(isPropIgnored))
|
|
2223
2398
|
)
|
|
2224
2399
|
let invalidHostStyle:
|
|
2225
2400
|
| { entry: MaterializedElement['entries'][number]; name: string }
|
|
@@ -3361,7 +3536,8 @@ function bailout(
|
|
|
3361
3536
|
| 'local/unsupported-child',
|
|
3362
3537
|
message: string,
|
|
3363
3538
|
span = input.element.span,
|
|
3364
|
-
zero?: { rule: ZeroRule; message?: string }
|
|
3539
|
+
zero?: { rule: ZeroRule; message?: string },
|
|
3540
|
+
prop?: string
|
|
3365
3541
|
): LoweringCandidateResult {
|
|
3366
3542
|
return {
|
|
3367
3543
|
ok: false,
|
|
@@ -3371,6 +3547,7 @@ function bailout(
|
|
|
3371
3547
|
message,
|
|
3372
3548
|
span,
|
|
3373
3549
|
component: input.element.component.name,
|
|
3550
|
+
...(prop && { prop }),
|
|
3374
3551
|
...(zero && { zeroRule: zero.rule, zeroMessage: zero.message }),
|
|
3375
3552
|
},
|
|
3376
3553
|
}
|