@tamagui/static 3.0.0-beta.889.1 → 3.0.0-beta.901.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tamagui/static",
3
- "version": "3.0.0-beta.889.1",
3
+ "version": "3.0.0-beta.901.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.889.1",
49
- "@tamagui/compiler-core": "3.0.0-beta.889.1",
50
- "@tamagui/config-default": "3.0.0-beta.889.1",
51
- "@tamagui/core": "3.0.0-beta.889.1",
52
- "@tamagui/dom": "3.0.0-beta.889.1",
53
- "@tamagui/fake-react-native": "3.0.0-beta.889.1",
54
- "@tamagui/generate-themes": "3.0.0-beta.889.1",
55
- "@tamagui/helpers": "3.0.0-beta.889.1",
56
- "@tamagui/helpers-node": "3.0.0-beta.889.1",
57
- "@tamagui/react-native-web-internals": "3.0.0-beta.889.1",
58
- "@tamagui/react-native-web-lite": "3.0.0-beta.889.1",
59
- "@tamagui/shorthands": "3.0.0-beta.889.1",
60
- "@tamagui/style-grammar": "3.0.0-beta.889.1",
61
- "@tamagui/types": "3.0.0-beta.889.1",
62
- "@tamagui/web": "3.0.0-beta.889.1",
48
+ "@tamagui/cli-color": "3.0.0-beta.901.1",
49
+ "@tamagui/compiler-core": "3.0.0-beta.901.1",
50
+ "@tamagui/config-default": "3.0.0-beta.901.1",
51
+ "@tamagui/core": "3.0.0-beta.901.1",
52
+ "@tamagui/dom": "3.0.0-beta.901.1",
53
+ "@tamagui/fake-react-native": "3.0.0-beta.901.1",
54
+ "@tamagui/generate-themes": "3.0.0-beta.901.1",
55
+ "@tamagui/helpers": "3.0.0-beta.901.1",
56
+ "@tamagui/helpers-node": "3.0.0-beta.901.1",
57
+ "@tamagui/react-native-web-internals": "3.0.0-beta.901.1",
58
+ "@tamagui/react-native-web-lite": "3.0.0-beta.901.1",
59
+ "@tamagui/shorthands": "3.0.0-beta.901.1",
60
+ "@tamagui/style-grammar": "3.0.0-beta.901.1",
61
+ "@tamagui/types": "3.0.0-beta.901.1",
62
+ "@tamagui/web": "3.0.0-beta.901.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.889.1",
74
+ "@tamagui/build": "3.0.0-beta.901.1",
75
75
  "@types/find-root": "^1.1.2",
76
76
  "@types/node": "^22.1.0",
77
77
  "@types/webpack": "^4.41.26",
@@ -1329,10 +1329,49 @@ export function createTamaguiCompilerHost(
1329
1329
  'animatedBy',
1330
1330
  'render',
1331
1331
  ])
1332
+ // a frontend's className is its style input: each value it can take
1333
+ // resolves through the same pipeline as a static class string
1334
+ const isFrontendClassName = (name: string, component: LoweringComponent): boolean =>
1335
+ name === 'className' && !!(component.staticConfig as StaticConfig).styleFrontend
1332
1336
  const canLowerConditionalStyleProp = (
1333
1337
  name: string,
1334
1338
  component: LoweringComponent
1335
- ): boolean => isStyleProp(name, component) && !runtimeOnlyStyleProps.has(name)
1339
+ ): boolean =>
1340
+ isStyleProp(name, component) &&
1341
+ (!runtimeOnlyStyleProps.has(name) || isFrontendClassName(name, component))
1342
+ // the finite class strings a frontend className takes at runtime (a template
1343
+ // over a static array with a dynamic index, a static array member). each one
1344
+ // lowers like a conditional branch, selected by the runtime string
1345
+ const frontendClassDomain = (
1346
+ entry: MaterializedElement['entries'][number],
1347
+ component: LoweringComponent
1348
+ ): string[] | null => {
1349
+ if (
1350
+ entry.kind !== 'prop' ||
1351
+ entry.value.kind !== 'bailout' ||
1352
+ !isFrontendClassName(entry.name, component)
1353
+ ) {
1354
+ return null
1355
+ }
1356
+ const dynamic = entry.value.dynamic
1357
+ if (dynamic?.type !== 'string' || !dynamic.values?.length) return null
1358
+ return dynamic.values.map(String)
1359
+ }
1360
+ type DynamicStyleEntry = Extract<
1361
+ MaterializedElement['entries'][number],
1362
+ { kind: 'prop' }
1363
+ > & {
1364
+ /** a member of the element's `style` object, lowered like the direct prop of its name */
1365
+ styleMember?: true
1366
+ }
1367
+ // an entry that lowers per branch as classes on web
1368
+ const conditionalClassEntry = (
1369
+ entry: DynamicStyleEntry,
1370
+ component: LoweringComponent
1371
+ ): boolean =>
1372
+ (entry.value.kind === 'conditional' &&
1373
+ canLowerConditionalStyleProp(entry.name, component)) ||
1374
+ frontendClassDomain(entry, component) !== null
1336
1375
 
1337
1376
  const isInvalidHostStyleProp = (
1338
1377
  name: string,
@@ -1597,6 +1636,13 @@ export function createTamaguiCompilerHost(
1597
1636
  if ((component.staticConfig as StaticConfig).resolvers?.length) {
1598
1637
  return false
1599
1638
  }
1639
+ if (
1640
+ !options.disablePartialExtraction &&
1641
+ platform === 'web' &&
1642
+ isFrontendClassName(name, component)
1643
+ ) {
1644
+ return true
1645
+ }
1600
1646
  return (
1601
1647
  !options.disablePartialExtraction &&
1602
1648
  ((platform === 'web' && !!directStyleName(name, component)) ||
@@ -1725,14 +1771,53 @@ export function createTamaguiCompilerHost(
1725
1771
  `input type ${props.type} has no native text-entry control`
1726
1772
  )
1727
1773
  }
1728
- const dynamicStyleEntries = input.element.entries.filter(
1729
- (
1730
- entry
1731
- ): entry is Extract<MaterializedElement['entries'][number], { kind: 'prop' }> =>
1732
- entry.kind === 'prop' &&
1733
- (entry.value.kind === 'bailout' || entry.value.kind === 'conditional') &&
1734
- isStyleProp(entry.name, component)
1735
- )
1774
+ // `style={{ width: expr, height: 10 }}`: the static members stay one
1775
+ // style object, and each dynamic member lowers like the direct prop of
1776
+ // its name
1777
+ let styleObjectStatic: Record<string, unknown> | null = null
1778
+ const styleMemberEntries: DynamicStyleEntry[] = []
1779
+ for (const entry of input.element.entries) {
1780
+ if (
1781
+ entry.kind !== 'prop' ||
1782
+ entry.name !== 'style' ||
1783
+ entry.value.kind !== 'object'
1784
+ ) {
1785
+ continue
1786
+ }
1787
+ styleObjectStatic = {}
1788
+ for (const member of entry.value.members) {
1789
+ if (member.value.kind === 'static') {
1790
+ styleObjectStatic[member.name] = member.value.value
1791
+ continue
1792
+ }
1793
+ if (!isStyleProp(member.name, component)) {
1794
+ return bailout(
1795
+ input,
1796
+ 'local/dynamic-style-value',
1797
+ `Style prop ${member.name} could not be evaluated`,
1798
+ member.span
1799
+ )
1800
+ }
1801
+ styleMemberEntries.push({
1802
+ kind: 'prop',
1803
+ name: member.name,
1804
+ span: member.span,
1805
+ value: member.value,
1806
+ styleMember: true,
1807
+ })
1808
+ }
1809
+ if (Object.keys(styleObjectStatic).length === 0) styleObjectStatic = null
1810
+ }
1811
+ if (styleObjectStatic) props.style = styleObjectStatic
1812
+ const dynamicStyleEntries: DynamicStyleEntry[] = [
1813
+ ...input.element.entries.filter(
1814
+ (entry): entry is DynamicStyleEntry =>
1815
+ entry.kind === 'prop' &&
1816
+ (entry.value.kind === 'bailout' || entry.value.kind === 'conditional') &&
1817
+ isStyleProp(entry.name, component)
1818
+ ),
1819
+ ...styleMemberEntries,
1820
+ ]
1736
1821
  // both platforms: web needs runtime event mapping, and a flattened bare
1737
1822
  // RN View silently ignores onPress/onLongPress (Tamagui wires press via
1738
1823
  // its responder system at runtime)
@@ -1856,6 +1941,35 @@ export function createTamaguiCompilerHost(
1856
1941
  ) ||
1857
1942
  animatedByNeedsRuntime
1858
1943
  let dynamicHostStyleProperties: string[] | null = null
1944
+ // the entries lane A carries as inline style: all of them when they all
1945
+ // lower that way, else the ones that do not lower per branch as classes,
1946
+ // so `width={w * 2}` shares an element with `bg={cond ? 'red' : 'blue'}`
1947
+ let laneAEntries: DynamicStyleEntry[] = []
1948
+ const laneAOwners = new Set<string>()
1949
+ // the CSS properties lane A took from the element's style object. the
1950
+ // style layer is the runtime's highest tier, and an inline style wins
1951
+ // over a class in CSS, so a class on the same property may coexist
1952
+ const laneAMemberOwners = new Set<string>()
1953
+ // a static value and an inline dynamic one interact unless the dynamic
1954
+ // one is a style member on the very same property
1955
+ const conflictsWithInline = (
1956
+ owners: Set<string>,
1957
+ dynamicOwners: Set<string>,
1958
+ memberOwners: Set<string>
1959
+ ) =>
1960
+ [...owners].some(
1961
+ (owner) =>
1962
+ !memberOwners.has(owner) && cssOwnersConflict(new Set([owner]), dynamicOwners)
1963
+ )
1964
+ const laneAAttempts = [dynamicStyleEntries]
1965
+ {
1966
+ const rest = dynamicStyleEntries.filter(
1967
+ (entry) => !conditionalClassEntry(entry, component)
1968
+ )
1969
+ if (rest.length > 0 && rest.length < dynamicStyleEntries.length) {
1970
+ laneAAttempts.push(rest)
1971
+ }
1972
+ }
1859
1973
  if (
1860
1974
  platform === 'web' &&
1861
1975
  !options.disablePartialExtraction &&
@@ -1865,105 +1979,135 @@ export function createTamaguiCompilerHost(
1865
1979
  (entry) => entry.kind === 'spread' && entry.value.kind !== 'static'
1866
1980
  )
1867
1981
  ) {
1868
- const seen = new Set<string>()
1869
- const properties: string[] = []
1870
- const dynamicOwners = new Set<string>()
1871
- for (const entry of dynamicStyleEntries) {
1872
- if (
1873
- entry.kind !== 'prop' ||
1874
- (entry.value.kind !== 'bailout' && entry.value.kind !== 'conditional')
1875
- ) {
1876
- continue
1877
- }
1878
- const name = directStyleName(entry.name, component)
1879
- if (!name || seen.has(name)) {
1880
- properties.length = 0
1881
- break
1882
- }
1883
- const owners = dynamicStyleOwners(name, component.staticConfig as StaticConfig)
1884
- if (!owners) {
1885
- properties.length = 0
1886
- break
1887
- }
1888
- let property: string | null = null
1889
- const expression = input.source.slice(
1890
- entry.value.span.start,
1891
- entry.value.span.end
1892
- )
1893
- if (
1894
- resolvedCssTransition !== null &&
1895
- (name === 'opacity' || name === 'scale')
1896
- ) {
1897
- property =
1898
- name === 'opacity'
1899
- ? `opacity: (${expression})`
1900
- : `transform: "scale(" + (${expression}) + ")"`
1901
- } else if (entry.value.kind === 'bailout' && owners.has(name)) {
1902
- const dynamic = entry.value.dynamic
1903
- if (dynamic?.type === 'number') {
1904
- property = `${JSON.stringify(name)}: (${expression})`
1905
- } else if (dynamic?.type === 'string' && dynamic.values?.length) {
1906
- let valuesStayLiteral = true
1907
- for (const value of dynamic.values) {
1908
- const split = resolveSplitStyles(
1909
- { [entry.name]: value },
1910
- partialStaticConfig(component.staticConfig as StaticConfig)
1911
- )
1912
- const atomics = Object.values(split?.rulesToInsert ?? {}) as any[]
1913
- if (
1914
- atomics.length !== 1 ||
1915
- atomics[0]?.[StyleObjectProperty] !== name ||
1916
- atomics[0]?.[StyleObjectValue] !== value
1917
- ) {
1918
- valuesStayLiteral = false
1919
- break
1920
- }
1921
- }
1922
- if (valuesStayLiteral) {
1982
+ for (const attempt of laneAAttempts) {
1983
+ const seen = new Set<string>()
1984
+ const properties: string[] = []
1985
+ const dynamicOwners = new Set<string>()
1986
+ const memberOwners = new Set<string>()
1987
+ for (const entry of attempt) {
1988
+ if (
1989
+ entry.kind !== 'prop' ||
1990
+ (entry.value.kind !== 'bailout' && entry.value.kind !== 'conditional')
1991
+ ) {
1992
+ continue
1993
+ }
1994
+ const name = directStyleName(entry.name, component)
1995
+ if (!name || seen.has(name)) {
1996
+ properties.length = 0
1997
+ break
1998
+ }
1999
+ const owners = dynamicStyleOwners(
2000
+ name,
2001
+ component.staticConfig as StaticConfig
2002
+ )
2003
+ if (!owners) {
2004
+ properties.length = 0
2005
+ break
2006
+ }
2007
+ let property: string | null = null
2008
+ const expression = input.source.slice(
2009
+ entry.value.span.start,
2010
+ entry.value.span.end
2011
+ )
2012
+ if (
2013
+ resolvedCssTransition !== null &&
2014
+ (name === 'opacity' || name === 'scale')
2015
+ ) {
2016
+ property =
2017
+ name === 'opacity'
2018
+ ? `opacity: (${expression})`
2019
+ : `transform: "scale(" + (${expression}) + ")"`
2020
+ } else if (entry.value.kind === 'bailout' && owners.has(name)) {
2021
+ const dynamic = entry.value.dynamic
2022
+ if (dynamic?.type === 'number') {
1923
2023
  property = `${JSON.stringify(name)}: (${expression})`
2024
+ } else if (dynamic?.type === 'string' && dynamic.values?.length) {
2025
+ let valuesStayLiteral = true
2026
+ for (const value of dynamic.values) {
2027
+ const split = resolveSplitStyles(
2028
+ { [entry.name]: value },
2029
+ partialStaticConfig(component.staticConfig as StaticConfig)
2030
+ )
2031
+ const atomics = Object.values(split?.rulesToInsert ?? {}) as any[]
2032
+ if (
2033
+ atomics.length !== 1 ||
2034
+ atomics[0]?.[StyleObjectProperty] !== name ||
2035
+ atomics[0]?.[StyleObjectValue] !== value
2036
+ ) {
2037
+ valuesStayLiteral = false
2038
+ break
2039
+ }
2040
+ }
2041
+ if (valuesStayLiteral) {
2042
+ property = `${JSON.stringify(name)}: (${expression})`
2043
+ }
1924
2044
  }
1925
2045
  }
2046
+ if (!property) {
2047
+ properties.length = 0
2048
+ break
2049
+ }
2050
+ seen.add(name)
2051
+ for (const owner of owners) {
2052
+ dynamicOwners.add(owner)
2053
+ if (entry.styleMember) memberOwners.add(owner)
2054
+ }
2055
+ properties.push(property)
1926
2056
  }
1927
- if (!property) {
1928
- properties.length = 0
2057
+ if (
2058
+ properties.length === attempt.length &&
2059
+ !input.element.entries.some((entry) => {
2060
+ if (entry.kind !== 'prop' || entry.value.kind !== 'static') return false
2061
+ const name = directStyleName(entry.name, component)
2062
+ if (!name) return false
2063
+ const owners = styleOwners(
2064
+ name,
2065
+ entry.value.value,
2066
+ component.staticConfig as StaticConfig
2067
+ )
2068
+ return !!owners && conflictsWithInline(owners, dynamicOwners, memberOwners)
2069
+ }) &&
2070
+ // the static members of the style object outrank every direct
2071
+ // prop at runtime, so an inline dynamic direct prop on the same
2072
+ // property would invert them
2073
+ !Object.entries(staticObject(props.style) ? props.style : {}).some(
2074
+ ([name, value]) => {
2075
+ const owners = styleOwners(
2076
+ name,
2077
+ value,
2078
+ component.staticConfig as StaticConfig
2079
+ )
2080
+ return !!owners && cssOwnersConflict(owners, dynamicOwners)
2081
+ }
2082
+ )
2083
+ ) {
2084
+ dynamicHostStyleProperties = properties
2085
+ laneAEntries = attempt
2086
+ for (const owner of dynamicOwners) laneAOwners.add(owner)
2087
+ for (const owner of memberOwners) laneAMemberOwners.add(owner)
1929
2088
  break
1930
2089
  }
1931
- seen.add(name)
1932
- for (const owner of owners) dynamicOwners.add(owner)
1933
- properties.push(property)
1934
- }
1935
- if (
1936
- properties.length === dynamicStyleEntries.length &&
1937
- !input.element.entries.some((entry) => {
1938
- if (entry.kind !== 'prop' || entry.value.kind !== 'static') return false
1939
- const name = directStyleName(entry.name, component)
1940
- if (!name) return false
1941
- const owners = styleOwners(
1942
- name,
1943
- entry.value.value,
1944
- component.staticConfig as StaticConfig
1945
- )
1946
- return !!owners && cssOwnersConflict(owners, dynamicOwners)
1947
- })
1948
- ) {
1949
- dynamicHostStyleProperties = properties
1950
2090
  }
1951
2091
  }
1952
2092
  // web per-branch conditional classes need the same preconditions the
1953
2093
  // native per-branch path has, and take precedence over web partial
1954
2094
  // extraction because a full flatten beats a runtime component
2095
+ const laneACoversAll =
2096
+ dynamicHostStyleProperties !== null &&
2097
+ laneAEntries.length === dynamicStyleEntries.length
2098
+ // the entries that lower per branch as classes: all of them, or the ones
2099
+ // lane A left behind
2100
+ const webClassEntries =
2101
+ dynamicHostStyleProperties === null
2102
+ ? dynamicStyleEntries
2103
+ : dynamicStyleEntries.filter((entry) => !laneAEntries.includes(entry))
1955
2104
  const supportsWebConditionalClasses =
1956
2105
  platform === 'web' &&
1957
2106
  !options.disablePartialExtraction &&
1958
2107
  (input.element.form === 'jsx' || input.element.propsSpan !== null) &&
1959
- dynamicHostStyleProperties === null &&
1960
- dynamicStyleEntries.length > 0 &&
1961
- dynamicStyleEntries.every(
1962
- (entry) =>
1963
- entry.kind === 'prop' &&
1964
- entry.value.kind === 'conditional' &&
1965
- canLowerConditionalStyleProp(entry.name, component)
1966
- )
2108
+ !laneACoversAll &&
2109
+ webClassEntries.length > 0 &&
2110
+ webClassEntries.every((entry) => conditionalClassEntry(entry, component))
1967
2111
  // a theme name of any value selects a theme, while themeInverse only opens a
1968
2112
  // boundary when it is on
1969
2113
  const themeEntry = componentOnlyProp('theme')
@@ -2030,7 +2174,7 @@ export function createTamaguiCompilerHost(
2030
2174
  if (
2031
2175
  platform === 'web' &&
2032
2176
  (dynamicStyleEntries.length > 0 || runtimeAnimationRequired) &&
2033
- dynamicHostStyleProperties === null &&
2177
+ !laneACoversAll &&
2034
2178
  !supportsWebConditionalClasses &&
2035
2179
  component.partialRuntimeSafe
2036
2180
  ) {
@@ -2171,7 +2315,7 @@ export function createTamaguiCompilerHost(
2171
2315
  )
2172
2316
  if (
2173
2317
  dynamicStyleEntries.length > 0 &&
2174
- dynamicHostStyleProperties === null &&
2318
+ !laneACoversAll &&
2175
2319
  !supportsNativeDynamicStyles &&
2176
2320
  !supportsWebConditionalClasses
2177
2321
  ) {
@@ -2215,11 +2359,12 @@ export function createTamaguiCompilerHost(
2215
2359
  'Native style() pieces remain on the runtime path'
2216
2360
  )
2217
2361
  }
2218
- const propsForConditional = (
2219
- target: Extract<MaterializedElement['entries'][number], { kind: 'prop' }>,
2220
- value: unknown
2221
- ) => {
2362
+ const propsForConditional = (target: DynamicStyleEntry, value: unknown) => {
2222
2363
  const branchProps: Record<string, unknown> = {}
2364
+ if (styleObjectStatic) branchProps.style = styleObjectStatic
2365
+ if (target.styleMember) {
2366
+ branchProps.style = { ...styleObjectStatic, [target.name]: value }
2367
+ }
2223
2368
  for (const entry of input.element.entries) {
2224
2369
  if (entry === target) {
2225
2370
  branchProps[target.name] = value
@@ -3297,13 +3442,14 @@ export function createTamaguiCompilerHost(
3297
3442
  const staticClasses = new Set(baseStaticClasses)
3298
3443
  const webConditionalCSS: string[] = []
3299
3444
  const webConditionalKeys = new Set<string>()
3300
- const webConditionalEntries = supportsWebConditionalClasses
3301
- ? dynamicStyleEntries.filter((entry) => entry.value.kind === 'conditional')
3302
- : []
3445
+ const webConditionalEntries = supportsWebConditionalClasses ? webClassEntries : []
3303
3446
  for (const entry of webConditionalEntries) {
3304
- if (entry.value.kind !== 'conditional') continue
3305
- const tree = entry.value.tree
3306
- const leaves = collectLeaves(tree)
3447
+ const tree = entry.value.kind === 'conditional' ? entry.value.tree : null
3448
+ const domain = frontendClassDomain(entry, component)
3449
+ if (!tree && !domain) continue
3450
+ const leaves = tree
3451
+ ? collectLeaves(tree)
3452
+ : domain!.map((value) => ({ value, dependencies: [] }))
3307
3453
  const leafArtifactsMap = new Map<
3308
3454
  (typeof leaves)[number],
3309
3455
  { classes: string[]; css: string[] }
@@ -3385,6 +3531,14 @@ export function createTamaguiCompilerHost(
3385
3531
  }
3386
3532
  entryConditionalKeys.add(key)
3387
3533
  }
3534
+ if (conflictsWithInline(changedKeys, laneAOwners, laneAMemberOwners)) {
3535
+ return bailout(
3536
+ input,
3537
+ 'local/dynamic-style-value',
3538
+ `${entry.name} and an inline dynamic style both contribute a CSS property; their precedence cannot be resolved per-branch`,
3539
+ entry.value.span
3540
+ )
3541
+ }
3388
3542
  const branchArtifacts = extractedStyleArtifacts(
3389
3543
  branchSplit,
3390
3544
  branchProps,
@@ -3416,22 +3570,32 @@ export function createTamaguiCompilerHost(
3416
3570
  if (!baseStaticClasses.has(cls)) staticClasses.add(cls)
3417
3571
  }
3418
3572
 
3573
+ const leafClasses = (leaf: (typeof leaves)[number]): string => {
3574
+ const artifacts = leafArtifactsMap.get(leaf)
3575
+ const only = artifacts
3576
+ ? artifacts.classes.filter((item) => !sharedInConditional.has(item))
3577
+ : []
3578
+ return only.join(' ')
3579
+ }
3419
3580
  function serializeWebTree(node: BranchDecisionNode): string {
3420
- if (node.kind === 'leaf') {
3421
- const artifacts = leafArtifactsMap.get(node)
3422
- const only = artifacts
3423
- ? artifacts.classes.filter((item) => !sharedInConditional.has(item))
3424
- : []
3425
- return JSON.stringify(only.join(' '))
3426
- }
3581
+ if (node.kind === 'leaf') return JSON.stringify(leafClasses(node))
3427
3582
  const test = input.source.slice(node.test.start, node.test.end)
3428
3583
  const truePart = serializeWebTree(node.whenTrue)
3429
3584
  const falsePart = serializeWebTree(node.whenFalse)
3430
3585
  return `(${test}) ? ${truePart} : ${falsePart}`
3431
3586
  }
3432
3587
 
3433
- const classExpr = serializeWebTree(tree)
3434
- programClassSources.push(classExpr)
3588
+ if (tree) {
3589
+ programClassSources.push(serializeWebTree(tree))
3590
+ } else {
3591
+ // the runtime string picks its resolved classes; a string outside the
3592
+ // domain cannot occur, since the domain is the array's full contents
3593
+ const table = Object.fromEntries(
3594
+ leaves.map((leaf) => [leaf.value, leafClasses(leaf)])
3595
+ )
3596
+ const key = input.source.slice(entry.value.span.start, entry.value.span.end)
3597
+ programClassSources.push(`(${JSON.stringify(table)})[${key}]`)
3598
+ }
3435
3599
  }
3436
3600
  const webClassName = [...staticClasses].join(' ')
3437
3601
  const hasStyleProgram = programClassSources.length > 0
@@ -1 +1 @@
1
- {"version":3,"file":"compilerHost.d.ts","sourceRoot":"","sources":["../src/compilerHost.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,oBAAoB,EACpB,cAAc,EAQf,MAAM,wBAAwB,CAAA;AAoC/B,OAAO,KAAK,EAAiC,qBAAqB,EAAE,MAAM,cAAc,CAAA;AAExF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAIhE,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED;;;;GAIG;AACH,MAAM,WAAW,yBAAyB;IACxC,+EAA+E;IAC/E,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;CAClD;AAED,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,SAAS,gBAAgB,EAAE,EACvC,gBAAgB,EAAE,SAAS,uBAAuB,EAAE,GACnD,yBAAyB,CAS3B;AAED,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,cAAc,CAAA;IACtB,aAAa,EAAE,qBAAqB,CAAA;IACpC,UAAU,EAAE,gBAAgB,EAAE,CAAA;IAC9B,gBAAgB,EAAE,uBAAuB,EAAE,CAAA;IAC3C,qFAAqF;IACrF,QAAQ,CAAC,EAAE,yBAAyB,CAAA;IACpC,wEAAwE;IACxE,wBAAwB,CAAC,EAAE,OAAO,CAAA;IAClC,mEAAmE;IACnE,0BAA0B,CAAC,EAAE,OAAO,CAAA;IACpC;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AA+2BD,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,0BAA0B,GAClC,oBAAoB,CA4/EtB"}
1
+ {"version":3,"file":"compilerHost.d.ts","sourceRoot":"","sources":["../src/compilerHost.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,oBAAoB,EACpB,cAAc,EAQf,MAAM,wBAAwB,CAAA;AAoC/B,OAAO,KAAK,EAAiC,qBAAqB,EAAE,MAAM,cAAc,CAAA;AAExF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAIhE,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED;;;;GAIG;AACH,MAAM,WAAW,yBAAyB;IACxC,+EAA+E;IAC/E,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;CAClD;AAED,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,SAAS,gBAAgB,EAAE,EACvC,gBAAgB,EAAE,SAAS,uBAAuB,EAAE,GACnD,yBAAyB,CAS3B;AAED,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,cAAc,CAAA;IACtB,aAAa,EAAE,qBAAqB,CAAA;IACpC,UAAU,EAAE,gBAAgB,EAAE,CAAA;IAC9B,gBAAgB,EAAE,uBAAuB,EAAE,CAAA;IAC3C,qFAAqF;IACrF,QAAQ,CAAC,EAAE,yBAAyB,CAAA;IACpC,wEAAwE;IACxE,wBAAwB,CAAC,EAAE,OAAO,CAAA;IAClC,mEAAmE;IACnE,0BAA0B,CAAC,EAAE,OAAO,CAAA;IACpC;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AA+2BD,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,0BAA0B,GAClC,oBAAoB,CAgqFtB"}