zyzz 0.0.21 → 0.0.23

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.
Files changed (233) hide show
  1. package/Zyzz.podspec +16 -0
  2. package/dist/Style.d.ts +3 -1
  3. package/dist/Style.d.ts.map +1 -1
  4. package/dist/Style.js.map +1 -1
  5. package/dist/babel/NativeJsx.d.ts.map +1 -1
  6. package/dist/babel/NativeJsx.js +217 -15
  7. package/dist/babel/NativeJsx.js.map +1 -1
  8. package/dist/babel/index.d.ts +6 -2
  9. package/dist/babel/index.d.ts.map +1 -1
  10. package/dist/babel/index.js +10 -1
  11. package/dist/babel/index.js.map +1 -1
  12. package/dist/compiler/Graph.d.ts.map +1 -1
  13. package/dist/compiler/Graph.js +107 -0
  14. package/dist/compiler/Graph.js.map +1 -1
  15. package/dist/compiler/Native.d.ts +6 -3
  16. package/dist/compiler/Native.d.ts.map +1 -1
  17. package/dist/compiler/Native.js +142 -24
  18. package/dist/compiler/Native.js.map +1 -1
  19. package/dist/compiler/Source.d.ts +2 -0
  20. package/dist/compiler/Source.d.ts.map +1 -1
  21. package/dist/compiler/Source.js +17 -5
  22. package/dist/compiler/Source.js.map +1 -1
  23. package/dist/compiler/internal/Configurations.d.ts +2 -0
  24. package/dist/compiler/internal/Configurations.d.ts.map +1 -1
  25. package/dist/compiler/internal/Configurations.js +21 -3
  26. package/dist/compiler/internal/Configurations.js.map +1 -1
  27. package/dist/compiler/internal/Contract.d.ts.map +1 -1
  28. package/dist/compiler/internal/Contract.js +75 -38
  29. package/dist/compiler/internal/Contract.js.map +1 -1
  30. package/dist/compiler/internal/Contributions.d.ts.map +1 -1
  31. package/dist/compiler/internal/Contributions.js +7 -1
  32. package/dist/compiler/internal/Contributions.js.map +1 -1
  33. package/dist/compiler/internal/Descriptors.d.ts.map +1 -1
  34. package/dist/compiler/internal/Descriptors.js +11 -2
  35. package/dist/compiler/internal/Descriptors.js.map +1 -1
  36. package/dist/compiler/internal/Dynamic.d.ts.map +1 -1
  37. package/dist/compiler/internal/Dynamic.js +3 -0
  38. package/dist/compiler/internal/Dynamic.js.map +1 -1
  39. package/dist/compiler/internal/Expression.js +2 -2
  40. package/dist/compiler/internal/Expression.js.map +1 -1
  41. package/dist/compiler/internal/NativeBindings.d.ts +24 -26
  42. package/dist/compiler/internal/NativeBindings.d.ts.map +1 -1
  43. package/dist/compiler/internal/NativeBindings.js +62 -8
  44. package/dist/compiler/internal/NativeBindings.js.map +1 -1
  45. package/dist/compiler/internal/NativeMedia.d.ts +14 -0
  46. package/dist/compiler/internal/NativeMedia.d.ts.map +1 -0
  47. package/dist/compiler/internal/NativeMedia.js +287 -0
  48. package/dist/compiler/internal/NativeMedia.js.map +1 -0
  49. package/dist/compiler/internal/NativeVars.d.ts +28 -0
  50. package/dist/compiler/internal/NativeVars.d.ts.map +1 -0
  51. package/dist/compiler/internal/NativeVars.js +160 -0
  52. package/dist/compiler/internal/NativeVars.js.map +1 -0
  53. package/dist/compiler/internal/PackedStyles.d.ts +2 -0
  54. package/dist/compiler/internal/PackedStyles.d.ts.map +1 -1
  55. package/dist/compiler/internal/PackedStyles.js +2 -0
  56. package/dist/compiler/internal/PackedStyles.js.map +1 -1
  57. package/dist/compiler/internal/Themes.d.ts +24 -1
  58. package/dist/compiler/internal/Themes.d.ts.map +1 -1
  59. package/dist/compiler/internal/Themes.js +120 -3
  60. package/dist/compiler/internal/Themes.js.map +1 -1
  61. package/dist/compiler/internal/Vars.d.ts +1 -1
  62. package/dist/compiler/internal/Vars.d.ts.map +1 -1
  63. package/dist/compiler/internal/Vars.js +7 -2
  64. package/dist/compiler/internal/Vars.js.map +1 -1
  65. package/dist/default.js.zyzz.json +1 -1
  66. package/dist/internal/Completion.d.ts +1 -1
  67. package/dist/internal/Completion.d.ts.map +1 -1
  68. package/dist/internal/Targets.d.ts.map +1 -1
  69. package/dist/internal/Targets.js +2 -0
  70. package/dist/internal/Targets.js.map +1 -1
  71. package/dist/internal/Token.d.ts +0 -2
  72. package/dist/internal/Token.d.ts.map +1 -1
  73. package/dist/internal/Token.js.map +1 -1
  74. package/dist/internal/Value.d.ts +7 -0
  75. package/dist/internal/Value.d.ts.map +1 -1
  76. package/dist/internal/Value.js.map +1 -1
  77. package/dist/metro/Graph.d.ts +22 -7
  78. package/dist/metro/Graph.d.ts.map +1 -1
  79. package/dist/metro/Graph.js +188 -42
  80. package/dist/metro/Graph.js.map +1 -1
  81. package/dist/metro/Watch.d.ts +28 -4
  82. package/dist/metro/Watch.d.ts.map +1 -1
  83. package/dist/metro/Watch.js +39 -7
  84. package/dist/metro/Watch.js.map +1 -1
  85. package/dist/metro/index.d.ts +15 -4
  86. package/dist/metro/index.d.ts.map +1 -1
  87. package/dist/metro/index.js +27 -0
  88. package/dist/metro/index.js.map +1 -1
  89. package/dist/metro/transformer.d.ts +5 -1
  90. package/dist/metro/transformer.d.ts.map +1 -1
  91. package/dist/metro/transformer.js +5 -3
  92. package/dist/metro/transformer.js.map +1 -1
  93. package/dist/react-native/StyleSheet.d.ts +8 -3
  94. package/dist/react-native/StyleSheet.d.ts.map +1 -1
  95. package/dist/react-native/StyleSheet.js +25 -34
  96. package/dist/react-native/StyleSheet.js.map +1 -1
  97. package/dist/react-native/index.d.ts +1 -1
  98. package/dist/react-native/index.d.ts.map +1 -1
  99. package/dist/react-native/index.js +1 -1
  100. package/dist/react-native/index.js.map +1 -1
  101. package/dist/react-native/index.native.d.ts +5 -0
  102. package/dist/react-native/index.native.js +20 -0
  103. package/dist/react-native/index.native.js.map +7 -0
  104. package/dist/react-native/internal/Calculation.d.ts +7 -0
  105. package/dist/react-native/internal/Calculation.d.ts.map +1 -0
  106. package/dist/react-native/internal/Calculation.js +134 -0
  107. package/dist/react-native/internal/Calculation.js.map +1 -0
  108. package/dist/react-native/internal/Device.js +218 -0
  109. package/dist/react-native/internal/Device.js.map +7 -0
  110. package/dist/react-native/internal/NativeZyzz.js +7 -0
  111. package/dist/react-native/internal/NativeZyzz.js.map +7 -0
  112. package/dist/react-native/internal/Scalar.d.ts +16 -0
  113. package/dist/react-native/internal/Scalar.d.ts.map +1 -1
  114. package/dist/react-native/internal/Scalar.js +62 -4
  115. package/dist/react-native/internal/Scalar.js.map +1 -1
  116. package/dist/react-native/internal/Store.d.ts +32 -0
  117. package/dist/react-native/internal/Store.d.ts.map +1 -0
  118. package/dist/react-native/internal/Store.js +57 -0
  119. package/dist/react-native/internal/Store.js.map +1 -0
  120. package/dist/react-native/internal/Styles.d.ts +19 -0
  121. package/dist/react-native/internal/Styles.d.ts.map +1 -0
  122. package/dist/react-native/internal/Styles.js +35 -0
  123. package/dist/react-native/internal/Styles.js.map +1 -0
  124. package/dist/react-native/internal/Tokens.d.ts +21 -0
  125. package/dist/react-native/internal/Tokens.d.ts.map +1 -0
  126. package/dist/react-native/internal/Tokens.js +58 -0
  127. package/dist/react-native/internal/Tokens.js.map +1 -0
  128. package/dist/react-native/internal/Viewport.d.ts +6 -0
  129. package/dist/react-native/internal/Viewport.d.ts.map +1 -0
  130. package/dist/react-native/internal/Viewport.js +4 -0
  131. package/dist/react-native/internal/Viewport.js.map +1 -0
  132. package/dist/react-native/react.d.ts +102 -14
  133. package/dist/react-native/react.d.ts.map +1 -1
  134. package/dist/react-native/react.js +86 -18
  135. package/dist/react-native/react.js.map +1 -1
  136. package/dist/react-native/reanimated.d.ts +39 -0
  137. package/dist/react-native/reanimated.d.ts.map +1 -0
  138. package/dist/react-native/reanimated.js +76 -0
  139. package/dist/react-native/reanimated.js.map +1 -0
  140. package/dist/runtime/NativeContext.d.ts +10 -1
  141. package/dist/runtime/NativeContext.d.ts.map +1 -1
  142. package/dist/runtime/NativeContext.js +67 -14
  143. package/dist/runtime/NativeContext.js.map +1 -1
  144. package/dist/runtime/NativeDynamic.d.ts +3 -2
  145. package/dist/runtime/NativeDynamic.d.ts.map +1 -1
  146. package/dist/runtime/NativeDynamic.js +37 -7
  147. package/dist/runtime/NativeDynamic.js.map +1 -1
  148. package/dist/runtime/NativeStatic.d.ts +23 -0
  149. package/dist/runtime/NativeStatic.d.ts.map +1 -0
  150. package/dist/runtime/NativeStatic.js +106 -0
  151. package/dist/runtime/NativeStatic.js.map +1 -0
  152. package/dist/runtime/NativeVars.d.ts +53 -0
  153. package/dist/runtime/NativeVars.d.ts.map +1 -0
  154. package/dist/runtime/NativeVars.js +77 -0
  155. package/dist/runtime/NativeVars.js.map +1 -0
  156. package/dist/runtime/index.d.ts +4 -0
  157. package/dist/runtime/index.d.ts.map +1 -1
  158. package/dist/runtime/index.js +4 -0
  159. package/dist/runtime/index.js.map +1 -1
  160. package/dist/runtime/internal/NativeMedia.d.ts +25 -0
  161. package/dist/runtime/internal/NativeMedia.d.ts.map +1 -0
  162. package/dist/runtime/internal/NativeMedia.js +36 -0
  163. package/dist/runtime/internal/NativeMedia.js.map +1 -0
  164. package/dist/vite/index.d.ts.map +1 -1
  165. package/dist/vite/index.js +7 -35
  166. package/dist/vite/index.js.map +1 -1
  167. package/dist/web/Css.d.ts.map +1 -1
  168. package/dist/web/Css.js +31 -19
  169. package/dist/web/Css.js.map +1 -1
  170. package/dist/web/internal/Themes.d.ts +0 -2
  171. package/dist/web/internal/Themes.d.ts.map +1 -1
  172. package/dist/web/internal/Themes.js +30 -28
  173. package/dist/web/internal/Themes.js.map +1 -1
  174. package/dist/web/page.d.ts +2 -0
  175. package/dist/web/page.d.ts.map +1 -1
  176. package/native/NativeZyzz.cpp +215 -0
  177. package/native/NativeZyzz.h +52 -0
  178. package/native/ZyzzSpecJSI.h +65 -0
  179. package/native/android/CMakeLists.txt +8 -0
  180. package/native/android/ZyzzSpec-generated.cpp +50 -0
  181. package/native/android/ZyzzSpec.h +31 -0
  182. package/native/ios/ZyzzModuleProvider.mm +13 -0
  183. package/package.json +39 -1
  184. package/react-native.config.ts +14 -0
  185. package/src/Style.ts +11 -8
  186. package/src/babel/NativeJsx.ts +319 -33
  187. package/src/babel/index.ts +21 -3
  188. package/src/compiler/Graph.ts +126 -0
  189. package/src/compiler/Native.ts +158 -26
  190. package/src/compiler/Source.ts +32 -5
  191. package/src/compiler/internal/Configurations.ts +25 -3
  192. package/src/compiler/internal/Contract.ts +98 -50
  193. package/src/compiler/internal/Contributions.ts +7 -1
  194. package/src/compiler/internal/Descriptors.ts +12 -8
  195. package/src/compiler/internal/Dynamic.ts +4 -0
  196. package/src/compiler/internal/Expression.ts +2 -2
  197. package/src/compiler/internal/NativeBindings.ts +99 -15
  198. package/src/compiler/internal/NativeMedia.ts +342 -0
  199. package/src/compiler/internal/NativeVars.ts +231 -0
  200. package/src/compiler/internal/PackedStyles.ts +4 -0
  201. package/src/compiler/internal/Themes.ts +193 -2
  202. package/src/compiler/internal/Vars.ts +12 -1
  203. package/src/internal/Completion.ts +6 -3
  204. package/src/internal/Targets.ts +2 -0
  205. package/src/internal/Token.ts +0 -2
  206. package/src/internal/Value.ts +20 -0
  207. package/src/metro/Graph.ts +250 -50
  208. package/src/metro/Watch.ts +95 -10
  209. package/src/metro/index.ts +62 -4
  210. package/src/metro/transformer.ts +21 -11
  211. package/src/react-native/StyleSheet.ts +56 -49
  212. package/src/react-native/index.native.ts +10 -0
  213. package/src/react-native/index.ts +1 -1
  214. package/src/react-native/internal/Calculation.ts +159 -0
  215. package/src/react-native/internal/Device.ts +297 -0
  216. package/src/react-native/internal/NativeZyzz.ts +20 -0
  217. package/src/react-native/internal/Scalar.ts +86 -4
  218. package/src/react-native/internal/Store.ts +78 -0
  219. package/src/react-native/internal/Styles.ts +77 -0
  220. package/src/react-native/internal/Tokens.ts +85 -0
  221. package/src/react-native/internal/Viewport.ts +8 -0
  222. package/src/react-native/react.ts +218 -26
  223. package/src/react-native/reanimated.ts +162 -0
  224. package/src/runtime/NativeContext.ts +88 -17
  225. package/src/runtime/NativeDynamic.ts +42 -10
  226. package/src/runtime/NativeStatic.ts +145 -0
  227. package/src/runtime/NativeVars.ts +189 -0
  228. package/src/runtime/index.ts +6 -0
  229. package/src/runtime/internal/NativeMedia.ts +63 -0
  230. package/src/vite/index.ts +9 -42
  231. package/src/web/Css.ts +42 -27
  232. package/src/web/internal/Themes.ts +34 -57
  233. package/src/web/page.ts +2 -0
@@ -0,0 +1,189 @@
1
+ /** Selects immutable native variable tables emitted by the compiler. @module */
2
+ import type * as Literal from '../internal/Literal.js'
3
+ import * as Media from './internal/NativeMedia.js'
4
+ import type * as StyleSheet from '../react-native/StyleSheet.js'
5
+ import type * as Vars from '../Vars.js'
6
+
7
+ const binding = Symbol('zyzz.native.vars')
8
+
9
+ /** Static values with deferred diagnostics for unsupported native leaves. */
10
+ export type EncodedTree = {
11
+ readonly [key: string]: string | number | EncodedTree | readonly [string]
12
+ }
13
+
14
+ /** Resolves portable reference domains to readonly native value types. */
15
+ export type Values<
16
+ values,
17
+ category = undefined,
18
+ root extends boolean = true,
19
+ > = {
20
+ readonly [key in Exclude<
21
+ keyof values,
22
+ root extends true ? 'breakpoint' | 'containerNames' : never
23
+ >]: values[key] extends Vars.Value
24
+ ? category extends 'fontWeight'
25
+ ? Exclude<StyleSheet.NativeStyle['fontWeight'], undefined>
26
+ : category extends 'typography'
27
+ ? key extends 'fontWeight'
28
+ ? Exclude<StyleSheet.NativeStyle['fontWeight'], undefined>
29
+ : key extends 'lineHeight'
30
+ ? number
31
+ : Scalar<Vars.Scalar<values[key]>>
32
+ : Scalar<Vars.Scalar<values[key]>>
33
+ : Values<values[key], root extends true ? key : category, false>
34
+ }
35
+
36
+ /** Native scalar values nested under their authored variable paths. */
37
+ export type Tree = { readonly [key: string]: string | number | Tree }
38
+
39
+ type Scalar<value> = value extends number | Literal.Length ? number : string
40
+
41
+ type Definition = {
42
+ readonly defaultVars: string
43
+ readonly media?:
44
+ | {
45
+ readonly profiles: Readonly<Record<string, Definition['profiles']>>
46
+ readonly queries: readonly Media.Query[]
47
+ }
48
+ | undefined
49
+ readonly profiles: Readonly<
50
+ Record<string, Readonly<Record<'dark' | 'light', Tree>>>
51
+ >
52
+ readonly unnamed: boolean
53
+ }
54
+
55
+ /** Copies and shares equal branches across the compiled profiles. */
56
+ export function create(options: create.Options): object {
57
+ const shared = new Map<string, Tree>()
58
+
59
+ function freeze(tree: EncodedTree): Tree {
60
+ const key = JSON.stringify(tree)
61
+ const previous = shared.get(key)
62
+ if (previous) return previous
63
+
64
+ const value: Tree = {}
65
+ for (const [name, entry] of Object.entries(tree)) {
66
+ const diagnostic = Array.isArray(entry) ? String(entry[0]) : undefined
67
+ Object.defineProperty(
68
+ value,
69
+ name,
70
+ diagnostic !== undefined
71
+ ? {
72
+ enumerable: true,
73
+ get() {
74
+ throw new Error(diagnostic)
75
+ },
76
+ }
77
+ : {
78
+ enumerable: true,
79
+ value:
80
+ typeof entry === 'object'
81
+ ? freeze(entry as EncodedTree)
82
+ : entry,
83
+ },
84
+ )
85
+ }
86
+ Object.freeze(value)
87
+ shared.set(key, value)
88
+ return value
89
+ }
90
+
91
+ function profiles(value: create.Options['profiles']): Definition['profiles'] {
92
+ return Object.freeze(
93
+ Object.fromEntries(
94
+ Object.entries(value).map(([name, schemes]) => [
95
+ name,
96
+ Object.freeze({
97
+ dark: freeze(schemes.dark),
98
+ light: freeze(schemes.light),
99
+ }),
100
+ ]),
101
+ ),
102
+ )
103
+ }
104
+
105
+ return Object.freeze({
106
+ [binding]: Object.freeze({
107
+ defaultVars: options.defaultVars,
108
+ ...(options.media
109
+ ? {
110
+ media: Object.freeze({
111
+ profiles: Object.freeze(
112
+ Object.fromEntries(
113
+ Object.entries(options.media.profiles).map(([key, value]) => [
114
+ key,
115
+ profiles(value),
116
+ ]),
117
+ ),
118
+ ),
119
+ queries: options.media.queries,
120
+ }),
121
+ }
122
+ : {}),
123
+ profiles: profiles(options.profiles),
124
+ unnamed: options.unnamed,
125
+ }),
126
+ })
127
+ }
128
+
129
+ /** Compiler-generated native profiles. */
130
+ export declare namespace create {
131
+ /** Static tables and the owning configuration's default name. */
132
+ type Options = {
133
+ /** Fallback when the Provider omits its selection. */
134
+ readonly defaultVars: string
135
+ /** Precompiled alternatives for supported window queries. */
136
+ readonly media?:
137
+ | {
138
+ readonly profiles: Readonly<Record<string, Options['profiles']>>
139
+ readonly queries: readonly Media.Query[]
140
+ }
141
+ | undefined
142
+ /** Native values for every configured name and scheme. */
143
+ readonly profiles: Readonly<
144
+ Record<string, Readonly<Record<'dark' | 'light', EncodedTree>>>
145
+ >
146
+ /** A standalone definition whose values do not depend on the selected name. */
147
+ readonly unnamed: boolean
148
+ }
149
+ }
150
+
151
+ /** Reads a compiled profile without evaluating authoring or accessing device state. */
152
+ export function read(value: object, selection: read.Options): Tree {
153
+ if (
154
+ !value ||
155
+ (typeof value !== 'object' && typeof value !== 'function') ||
156
+ !Object.hasOwn(value, binding)
157
+ )
158
+ throw new Error(
159
+ 'useVars requires variables compiled by the native adapter.',
160
+ )
161
+
162
+ const definition = (value as { readonly [binding]: Definition })[binding]
163
+ const profiles =
164
+ definition.media && selection.viewport
165
+ ? definition.media.profiles[
166
+ Media.select(definition.media.queries, selection.viewport)
167
+ ]!
168
+ : definition.profiles
169
+ const name = selection.set ?? definition.defaultVars
170
+ const profile =
171
+ (Object.hasOwn(profiles, name) ? profiles[name] : undefined) ??
172
+ (definition.unnamed ? profiles.default : undefined)
173
+ if (!profile) throw new Error(`Unknown native vars: ${name}.`)
174
+
175
+ return profile[selection.colorScheme]
176
+ }
177
+
178
+ /** Selection supplied by the nearest native Provider. */
179
+ export declare namespace read {
180
+ /** Resolved appearance and an optional variable name. */
181
+ type Options = {
182
+ /** Resolved native color scheme. */
183
+ readonly colorScheme: 'dark' | 'light'
184
+ /** Selected name, or the configuration default when omitted. */
185
+ readonly set?: string | undefined
186
+ /** Native window dimensions; unsupported leaves remain lazy when absent. */
187
+ readonly viewport?: Media.Viewport | undefined
188
+ }
189
+ }
@@ -29,6 +29,12 @@ export * as NativeContext from './NativeContext.js'
29
29
  /** Ordered native scalar bindings for generated dynamic callables. */
30
30
  export * as NativeDynamic from './NativeDynamic.js'
31
31
 
32
+ /** Finite native selection without dynamic value conversion. */
33
+ export * as NativeStatic from './NativeStatic.js'
34
+
35
+ /** Readonly native variable selection for compiled hooks. */
36
+ export * as NativeVars from './NativeVars.js'
37
+
32
38
  /** Scoped recipe inputs bound to fixed variable slots. */
33
39
  export * as PayloadRecipe from './PayloadRecipe.js'
34
40
 
@@ -0,0 +1,63 @@
1
+ /** Selects precompiled media alternatives using native logical dimensions. @module */
2
+
3
+ /** Comparison data emitted by the compiler, with no CSS parsing at application time. */
4
+ export type Query =
5
+ | { readonly kind: 'and'; readonly queries: readonly Query[] }
6
+ | {
7
+ readonly kind: 'compare'
8
+ readonly left: number | 'height' | 'width'
9
+ readonly operator: '<' | '<=' | '=' | '>' | '>='
10
+ readonly right: number | 'height' | 'width'
11
+ }
12
+ | { readonly kind: 'not'; readonly query: Query }
13
+ | { readonly kind: 'or'; readonly queries: readonly Query[] }
14
+
15
+ /** Window measurements supplied by the native React adapter. */
16
+ export type Viewport = {
17
+ readonly height: number
18
+ readonly width: number
19
+ }
20
+
21
+ /** Computes the selected alternative without interpreting authoring code. */
22
+ export function select(
23
+ queries: readonly Query[],
24
+ viewport: Viewport | undefined,
25
+ ): string {
26
+ if (!viewport)
27
+ throw new Error(
28
+ 'Native media queries require the native Provider window dimensions.',
29
+ )
30
+ if (
31
+ !Number.isFinite(viewport.height) ||
32
+ viewport.height < 0 ||
33
+ !Number.isFinite(viewport.width) ||
34
+ viewport.width < 0
35
+ )
36
+ throw new Error('Native window dimensions must be finite and nonnegative.')
37
+
38
+ const dimensions = viewport
39
+ function matches(query: Query): boolean {
40
+ if (query.kind === 'and') return query.queries.every(matches)
41
+ if (query.kind === 'or') return query.queries.some(matches)
42
+ if (query.kind === 'not') return !matches(query.query)
43
+
44
+ const left =
45
+ typeof query.left === 'number' ? query.left : dimensions[query.left]
46
+ const right =
47
+ typeof query.right === 'number' ? query.right : dimensions[query.right]
48
+ switch (query.operator) {
49
+ case '<':
50
+ return left < right
51
+ case '<=':
52
+ return left <= right
53
+ case '=':
54
+ return left === right
55
+ case '>':
56
+ return left > right
57
+ case '>=':
58
+ return left >= right
59
+ }
60
+ }
61
+
62
+ return queries.map((query) => (matches(query) ? '1' : '0')).join('')
63
+ }
package/src/vite/index.ts CHANGED
@@ -20,6 +20,7 @@ import * as Scope from '../compiler/internal/Scope.js'
20
20
  import type { Environment, Plugin, Rollup, ViteDevServer } from 'vite'
21
21
  import * as Graph from '../compiler/Graph.js'
22
22
  import * as Source from '../compiler/Source.js'
23
+ import * as ThemeRules from '../web/internal/Themes.js'
23
24
 
24
25
  /**
25
26
  * Compiles physical project source without executing authoring code.
@@ -749,6 +750,7 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
749
750
  )
750
751
 
751
752
  const result = entry.compiler.compile({
753
+ [ThemeRules.shared]: native ? undefined : 'all',
752
754
  [Syntax.cache]: snapshot(entry.environment).programs(modules),
753
755
  compiler: options.compiler,
754
756
  reset: options.reset ? Reset.read() : undefined,
@@ -812,46 +814,6 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
812
814
  configurations.set(key, configuration)
813
815
  }
814
816
 
815
- const map = new Mapping.GenMapping()
816
- const styles: string[] = []
817
- let line = 0
818
-
819
- for (const [id, output] of Object.entries(result.modules)) {
820
- if (!connected.has(sourceFile(id)!)) continue
821
- if (!output.css) continue
822
-
823
- for (const mapping of Mapping.allMappings(
824
- Mapping.fromMap(JSON.stringify(output.cssMap)),
825
- )) {
826
- const generated = {
827
- column: mapping.generated.column,
828
- line: mapping.generated.line + line,
829
- }
830
-
831
- if (mapping.source !== undefined && mapping.original !== undefined) {
832
- const location = {
833
- generated,
834
- original: mapping.original,
835
- source: sourceFile(mapping.source) ?? mapping.source,
836
- }
837
-
838
- if (mapping.name === undefined) Mapping.addMapping(map, location)
839
- else Mapping.addMapping(map, { ...location, name: mapping.name })
840
- } else Mapping.addMapping(map, { generated })
841
- }
842
-
843
- for (const [index, source] of output.cssMap.sources.entries())
844
- if (source !== null)
845
- Mapping.setSourceContent(
846
- map,
847
- sourceFile(source) ?? source,
848
- output.cssMap.sourcesContent?.[index] ?? null,
849
- )
850
-
851
- styles.push(output.css)
852
- line += output.css.split('\n').length
853
- }
854
-
855
817
  const owners = new Map<string, string>()
856
818
 
857
819
  for (const id of new Set(Object.values(result.sharedAssetOwners ?? {}))) {
@@ -1018,10 +980,15 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
1018
980
  contractIds: Object.keys(compiled),
1019
981
  configurations,
1020
982
  connections: staged,
1021
- css: styles.join('\n'),
983
+ css: output.css,
1022
984
  sharedCss: new TextDecoder().decode(shared.code),
1023
985
  sharedCssMap: sharedMap,
1024
- cssMap: Mapping.toEncodedMap(map),
986
+ cssMap: {
987
+ ...output.cssMap,
988
+ sources: output.cssMap.sources.map((source) =>
989
+ source === null ? null : (sourceFile(source) ?? source),
990
+ ),
991
+ },
1025
992
  map: {
1026
993
  ...output.map,
1027
994
  sources: output.map.sources.map((source) =>
package/src/web/Css.ts CHANGED
@@ -56,7 +56,18 @@ export function compile<
56
56
  return result
57
57
  }
58
58
 
59
+ const bodies = new Map<Style.NamedStyle, string>()
60
+ const declarations = new Map<Style.Declaration, string>()
61
+ type Property = {
62
+ readonly important: Map<Style.Declaration['value'], string>
63
+ readonly name: string
64
+ readonly normal: Map<Style.Declaration['value'], string>
65
+ }
66
+ const declarationProperties = new Map<string, Property>()
67
+ const serialized = new Map<object, string>()
68
+
59
69
  // Only primitive declaration lists can be interned without erasing token identity.
70
+ // Reuse their emitted bodies throughout analysis and output.
60
71
  const repeated = new Map<string, Style.NamedStyle>()
61
72
 
62
73
  const canonicalStyles = options.styles.styles.map((style) => {
@@ -68,13 +79,7 @@ export function compile<
68
79
  )
69
80
  return style
70
81
 
71
- const key = JSON.stringify(
72
- style.declarations.map(({ important, property, value }) => [
73
- property,
74
- value,
75
- important,
76
- ]),
77
- )
82
+ const key = nested(style)
78
83
  const previous = repeated.get(key)
79
84
  if (previous) return previous
80
85
 
@@ -269,8 +274,6 @@ export function compile<
269
274
  if (Object.hasOwn(Cascade.shorthands, property))
270
275
  join(property, domain(property))
271
276
 
272
- const serialized = new Map<object, string>()
273
-
274
277
  function serialize(input: Style.Declaration['value']): number | string {
275
278
  if (typeof input !== 'object' || input === null) return input
276
279
 
@@ -298,22 +301,32 @@ export function compile<
298
301
  return input as number | string
299
302
  }
300
303
 
301
- const declarations = new Map<Style.Declaration, string>()
302
- const propertyNames = new Map<string, string>()
303
-
304
304
  function declarationBody(declaration: Style.Declaration): string {
305
305
  const previous = declarations.get(declaration)
306
306
  if (previous !== undefined) return previous
307
+
307
308
  const { important, property, value } = declaration
308
- const name = propertyNames.get(property) ?? Literal.name(property)
309
- propertyNames.set(property, name)
310
- const body = `${name}:${serialize(value)}${important ? '!important' : ''};`
309
+ let entry = declarationProperties.get(property)
310
+ if (!entry) {
311
+ entry = {
312
+ important: new Map(),
313
+ name: Literal.name(property),
314
+ normal: new Map(),
315
+ }
316
+ declarationProperties.set(property, entry)
317
+ }
318
+
319
+ const values = important ? entry.important : entry.normal
320
+ let body = values.get(value)
321
+ if (body === undefined) {
322
+ body = `${entry.name}:${serialize(value)}${important ? '!important' : ''};`
323
+ values.set(value, body)
324
+ }
325
+
311
326
  declarations.set(declaration, body)
312
327
  return body
313
328
  }
314
329
 
315
- const bodies = new Map<Style.NamedStyle, string>()
316
-
317
330
  function nested(style: Style.NamedStyle): string {
318
331
  const previous = bodies.get(style)
319
332
  if (previous !== undefined) return previous
@@ -401,11 +414,8 @@ export function compile<
401
414
  }
402
415
 
403
416
  function validate(style: Style.NamedStyle) {
404
- if (
405
- outputMode(style) !== undefined &&
406
- outputMode(style) !== 'atomic' &&
407
- outputMode(style) !== 'grouped'
408
- )
417
+ const mode = outputMode(style)
418
+ if (mode !== undefined && mode !== 'atomic' && mode !== 'grouped')
409
419
  throw new CompileError([
410
420
  {
411
421
  code: 'invalid_output',
@@ -421,7 +431,8 @@ export function compile<
421
431
  const rules = new Map<string, string>()
422
432
  type ConditionalRule = { conditions: readonly string[]; value: string }
423
433
  const conditionalRules = new Map<string, ConditionalRule>()
424
- const identical = new Map<string, string>()
434
+ type SharedRules = Record<'atomic' | 'grouped', Map<string, string>>
435
+ const identical = new Map<string | undefined, SharedRules>()
425
436
  const explicitBodies = new Map<string, string>()
426
437
  const selectors = new Map<string, string>()
427
438
  const explicitModes = new Set<string>()
@@ -465,7 +476,7 @@ export function compile<
465
476
  explicit === undefined &&
466
477
  options.composition === 'independent' &&
467
478
  !options.development
468
- ? `${mode}:${JSON.stringify(representation(style))}:${nested(canonicalStyles[styleIndex]!)}`
479
+ ? `${mode}:${style.rules ? JSON.stringify(representation(style)) : ''}:${nested(canonicalStyles[styleIndex]!)}`
469
480
  : undefined
470
481
  if (application !== undefined && applications.has(application)) {
471
482
  classes[style.name] = applications.get(application)!
@@ -478,6 +489,11 @@ export function compile<
478
489
  const cssNamespace =
479
490
  labels?.[style.name]?.namespace ??
480
491
  (options.scope ? Identity.name(options.scope) : undefined)
492
+ let sharedRules = identical.get(cssNamespace)
493
+ if (!sharedRules) {
494
+ sharedRules = { atomic: new Map(), grouped: new Map() }
495
+ identical.set(cssNamespace, sharedRules)
496
+ }
481
497
 
482
498
  function emit(
483
499
  body: string,
@@ -493,9 +509,8 @@ export function compile<
493
509
  !options.development &&
494
510
  output === mode &&
495
511
  options.composition === 'independent'
496
- const key = JSON.stringify([cssNamespace, output, body])
497
512
  const reusable = explicit === undefined && (shared || independent)
498
- const previous = reusable ? identical.get(key) : undefined
513
+ const previous = reusable ? sharedRules![output].get(body) : undefined
499
514
  if (previous && !names.includes(previous)) {
500
515
  names.push(previous)
501
516
  return
@@ -544,7 +559,7 @@ export function compile<
544
559
  conditionalRules.set(identity, conditional)
545
560
  if (explicit !== undefined) selectors.set(identity, explicit)
546
561
  else {
547
- if (reusable) identical.set(key, identity)
562
+ if (reusable) sharedRules![output].set(body, identity)
548
563
  names.push(identity)
549
564
  }
550
565
  }
@@ -15,6 +15,24 @@ export function create() {
15
15
  { identity: string | undefined; paths: Map<string, string> }
16
16
  >()
17
17
 
18
+ const anonymous = new Map<Token.Contract, Map<string, string>>()
19
+ let nextVariable = 0
20
+ function property(contract: Token.Contract, path: string): string {
21
+ const identity = contract[Token.identity]
22
+ if (identity) return variable(identity, path)
23
+ let paths = anonymous.get(contract)
24
+ if (!paths) {
25
+ paths = new Map()
26
+ anonymous.set(contract, paths)
27
+ }
28
+ let name = paths.get(path)
29
+ if (!name) {
30
+ name = `--z${(nextVariable++).toString(36)}`
31
+ paths.set(path, name)
32
+ }
33
+ return name
34
+ }
35
+
18
36
  const defaults = new Map<string, readonly Rule[]>()
19
37
  const defaultGroups = new Map<Token.Contract, Set<string>>()
20
38
 
@@ -33,11 +51,7 @@ export function create() {
33
51
  let name = contract.paths.get(token.path)
34
52
 
35
53
  if (!name) {
36
- name = variable(
37
- contract.identity,
38
- token.path,
39
- token.contract.variableNaming,
40
- )
54
+ name = property(token.contract, token.path)
41
55
  contract.paths.set(token.path, name)
42
56
  }
43
57
 
@@ -51,6 +65,7 @@ export function create() {
51
65
  separate?: 'all' | 'defaults',
52
66
  ) {
53
67
  const classes: Record<string, string> = Object.create(null)
68
+ let nextScope = 0
54
69
  const scopeBodies = new Map<string, string>()
55
70
  const completeDefaults = new Set<Token.Contract>()
56
71
  const empty: string[] = []
@@ -68,14 +83,17 @@ export function create() {
68
83
 
69
84
  // Anonymous contracts are graph-local. Source-owned contracts retain stable
70
85
  // identities across separately compiled components and theme scopes.
71
- const className = scope(
72
- data.cssName ??
73
- (data.contract[Token.identity] &&
74
- name.startsWith(`${data.contract[Token.identity]}-`)
75
- ? name.slice(data.contract[Token.identity]!.length + 1)
76
- : name),
77
- data.contract[Token.identity],
78
- )
86
+ const className =
87
+ data.contract[Token.identity] || data.cssName
88
+ ? scope(
89
+ data.cssName ??
90
+ (data.contract[Token.identity] &&
91
+ name.startsWith(`${data.contract[Token.identity]}-`)
92
+ ? name.slice(data.contract[Token.identity]!.length + 1)
93
+ : name),
94
+ data.contract[Token.identity],
95
+ )
96
+ : `z${(nextScope++).toString(36)}`
79
97
 
80
98
  classes[name] = className
81
99
 
@@ -91,10 +109,7 @@ export function create() {
91
109
  for (const path of Object.keys(data.values)) {
92
110
  if (contract.paths.has(path)) continue
93
111
 
94
- contract.paths.set(
95
- path,
96
- variable(contract.identity, path, data.contract.variableNaming),
97
- )
112
+ contract.paths.set(path, property(data.contract, path))
98
113
  }
99
114
 
100
115
  contracts.set(data.contract, contract)
@@ -125,28 +140,6 @@ export function create() {
125
140
  selector: `.${className}`,
126
141
  value: literal(value, label, data.contract),
127
142
  })
128
- // Standalone styles have no contract metadata to select their original spelling.
129
- if (
130
- contract.identity?.startsWith('id-') &&
131
- data.contract.variableNaming === undefined
132
- )
133
- declarations.push({
134
- conditions: [],
135
- property: variable(
136
- `id-${Array.from(
137
- contract.identity
138
- .slice(3)
139
- .replace(/_([0-9a-f]+)_/g, (_, code: string) =>
140
- String.fromCodePoint(Number.parseInt(code, 16)),
141
- ),
142
- (character) => character.codePointAt(0)!.toString(16),
143
- ).join('-')}`,
144
- path,
145
- 'legacy',
146
- ),
147
- selector: `.${className}`,
148
- value: `var(${name})`,
149
- })
150
143
  }
151
144
 
152
145
  rules.push(...declarations)
@@ -266,7 +259,7 @@ export function create() {
266
259
  const base = literal(value.default, label, owner)
267
260
  // Rule consolidation must not change existing variable identities.
268
261
  const css = `:where(*){--fallback:${base};}${conditionalCss(value, '--fallback', ':where(*)', label, owner)}`
269
- const name = `${variable(owner?.[Token.identity], label ?? 'value')}-fallback-${Identity.name(css)}`
262
+ const name = `${owner ? property(owner, label ?? 'value') : variable(undefined, label ?? 'value')}-fallback-${Identity.name(css)}`
270
263
  const emitted: Rule[] = [
271
264
  { conditions: [], property: name, selector: ':where(*)', value: base },
272
265
  ]
@@ -341,14 +334,6 @@ export function create() {
341
334
  return { emit, serialize }
342
335
  }
343
336
 
344
- /** Escapes a scope key into the identifier segment of its compiled class. */
345
- export function encode(value: string): string {
346
- return value.replace(
347
- /[^a-zA-Z0-9-]/g,
348
- (character) => `_${character.charCodeAt(0).toString(16)}_`,
349
- )
350
- }
351
-
352
337
  /** Combines generated declarations without crossing competing declarations of the same property. */
353
338
  export function render(rules: readonly Rule[]): string {
354
339
  const groups: {
@@ -424,15 +409,7 @@ export function scope(value: string, identity?: string): string {
424
409
  return `z-theme-${namespace}${Identity.name(value)}`
425
410
  }
426
411
 
427
- function variable(
428
- identity: string | undefined,
429
- path: string,
430
- naming?: 'legacy' | 'hashed',
431
- ): string {
432
- if (naming === 'legacy' && identity)
433
- return `--z-t${encode(identity)}-${encode(path)}`
434
- if (naming === 'hashed')
435
- return `--z-${Identity.label(path)}-${Identity.compact(JSON.stringify([identity, path]))}`
412
+ function variable(identity: string | undefined, path: string): string {
436
413
  const namespace = identity?.startsWith('id-') ? `${identity.slice(3)}-` : ''
437
414
  return `--z-${namespace}${path.split('.').map(Identity.name).join('-')}`
438
415
  }
package/src/web/page.ts CHANGED
@@ -35,6 +35,8 @@ export declare namespace page {
35
35
  | 'crop cross'
36
36
  | 'cross crop'
37
37
  | undefined
38
+ /** Handling of page margins that exceed the printer's printable area. */
39
+ readonly pageMarginSafety?: 'none' | 'clamp' | 'add' | undefined
38
40
  /** Rotation applied to a laid-out page. */
39
41
  readonly pageOrientation?:
40
42
  | 'upright'