zyzz 0.0.28 → 0.0.30

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 (302) hide show
  1. package/binding/wasi/wasi-worker.mjs +186 -0
  2. package/binding/wasi/zyzz.wasi.cjs +2302 -0
  3. package/binding/wasi/zyzz.wasm32-wasi.wasm +0 -0
  4. package/binding/zyzz.linux-x64-gnu.node +0 -0
  5. package/dist/babel/index.d.ts.map +1 -1
  6. package/dist/babel/index.js +2 -4
  7. package/dist/babel/index.js.map +1 -1
  8. package/dist/babel/internal/Compilation.d.ts +0 -1
  9. package/dist/babel/internal/Compilation.d.ts.map +1 -1
  10. package/dist/compiler/Graph.d.ts +14 -11
  11. package/dist/compiler/Graph.d.ts.map +1 -1
  12. package/dist/compiler/Graph.js +57 -1442
  13. package/dist/compiler/Graph.js.map +1 -1
  14. package/dist/compiler/Native.d.ts +7 -9
  15. package/dist/compiler/Native.d.ts.map +1 -1
  16. package/dist/compiler/Native.js +12 -716
  17. package/dist/compiler/Native.js.map +1 -1
  18. package/dist/compiler/Source.d.ts +122 -24
  19. package/dist/compiler/Source.d.ts.map +1 -1
  20. package/dist/compiler/Source.js +10 -1267
  21. package/dist/compiler/Source.js.map +1 -1
  22. package/dist/compiler/Transform.d.ts +4 -3
  23. package/dist/compiler/Transform.d.ts.map +1 -1
  24. package/dist/compiler/Transform.js +13 -961
  25. package/dist/compiler/Transform.js.map +1 -1
  26. package/dist/compiler/internal/AtRules.d.ts +5 -1
  27. package/dist/compiler/internal/AtRules.d.ts.map +1 -1
  28. package/dist/compiler/internal/AtRules.js +12 -106
  29. package/dist/compiler/internal/AtRules.js.map +1 -1
  30. package/dist/compiler/internal/Context.d.ts +11 -0
  31. package/dist/compiler/internal/Context.d.ts.map +1 -0
  32. package/dist/compiler/internal/Context.js +11 -0
  33. package/dist/compiler/internal/Context.js.map +1 -0
  34. package/dist/compiler/internal/Results.d.ts +148 -0
  35. package/dist/compiler/internal/Results.d.ts.map +1 -0
  36. package/dist/compiler/internal/Results.js +259 -0
  37. package/dist/compiler/internal/Results.js.map +1 -0
  38. package/dist/compiler/internal/Syntax.d.ts +0 -2
  39. package/dist/compiler/internal/Syntax.d.ts.map +1 -1
  40. package/dist/compiler/internal/Syntax.js +0 -2
  41. package/dist/compiler/internal/Syntax.js.map +1 -1
  42. package/dist/compiler/internal/Wire.d.ts +17 -0
  43. package/dist/compiler/internal/Wire.d.ts.map +1 -0
  44. package/dist/compiler/internal/Wire.js +795 -0
  45. package/dist/compiler/internal/Wire.js.map +1 -0
  46. package/dist/internal/Addon.d.ts +28 -0
  47. package/dist/internal/Addon.d.ts.map +1 -0
  48. package/dist/internal/Addon.js +87 -0
  49. package/dist/internal/Addon.js.map +1 -0
  50. package/dist/internal/Literal.d.ts +4 -1
  51. package/dist/internal/Literal.d.ts.map +1 -1
  52. package/dist/internal/Literal.js +4 -2
  53. package/dist/internal/Literal.js.map +1 -1
  54. package/dist/internal/Token.d.ts +2 -1
  55. package/dist/internal/Token.d.ts.map +1 -1
  56. package/dist/internal/Token.js +2 -2
  57. package/dist/internal/Token.js.map +1 -1
  58. package/dist/internal/Vlq.d.ts +14 -0
  59. package/dist/internal/Vlq.d.ts.map +1 -0
  60. package/dist/internal/Vlq.js +47 -0
  61. package/dist/internal/Vlq.js.map +1 -0
  62. package/dist/internal/Wasm.d.ts +22 -0
  63. package/dist/internal/Wasm.d.ts.map +1 -0
  64. package/dist/internal/Wasm.js +83 -0
  65. package/dist/internal/Wasm.js.map +1 -0
  66. package/dist/metro/Graph.d.ts +0 -1
  67. package/dist/metro/Graph.d.ts.map +1 -1
  68. package/dist/metro/transformer.d.ts.map +1 -1
  69. package/dist/metro/transformer.js +0 -1
  70. package/dist/metro/transformer.js.map +1 -1
  71. package/dist/next/internal/Project.d.ts.map +1 -1
  72. package/dist/next/internal/Project.js +2 -5
  73. package/dist/next/internal/Project.js.map +1 -1
  74. package/dist/node/Host.d.ts.map +1 -1
  75. package/dist/node/Host.js +0 -2
  76. package/dist/node/Host.js.map +1 -1
  77. package/dist/node/internal/Contracts.d.ts.map +1 -1
  78. package/dist/node/internal/Contracts.js +3 -7
  79. package/dist/node/internal/Contracts.js.map +1 -1
  80. package/dist/node/internal/Snapshot.d.ts +0 -1
  81. package/dist/node/internal/Snapshot.d.ts.map +1 -1
  82. package/dist/node/internal/Snapshot.js +0 -6
  83. package/dist/node/internal/Snapshot.js.map +1 -1
  84. package/dist/runtime/Composition.d.ts +2 -3
  85. package/dist/runtime/Composition.d.ts.map +1 -1
  86. package/dist/runtime/Composition.js +51 -30
  87. package/dist/runtime/Composition.js.map +1 -1
  88. package/dist/runtime/Recipe.d.ts.map +1 -1
  89. package/dist/runtime/Recipe.js +16 -12
  90. package/dist/runtime/Recipe.js.map +1 -1
  91. package/dist/unplugin/index.d.ts.map +1 -1
  92. package/dist/unplugin/index.js +3 -6
  93. package/dist/unplugin/index.js.map +1 -1
  94. package/dist/vite/index.d.ts.map +1 -1
  95. package/dist/vite/index.js +696 -201
  96. package/dist/vite/index.js.map +1 -1
  97. package/dist/web/Css.d.ts +8 -3
  98. package/dist/web/Css.d.ts.map +1 -1
  99. package/dist/web/Css.js +5 -721
  100. package/dist/web/Css.js.map +1 -1
  101. package/dist/web/internal/Block.d.ts +0 -2
  102. package/dist/web/internal/Block.d.ts.map +1 -1
  103. package/dist/web/internal/Block.js +1 -12
  104. package/dist/web/internal/Block.js.map +1 -1
  105. package/dist/web/internal/ClassName.d.ts +23 -18
  106. package/dist/web/internal/ClassName.d.ts.map +1 -1
  107. package/dist/web/internal/ClassName.js +26 -55
  108. package/dist/web/internal/ClassName.js.map +1 -1
  109. package/dist/web/internal/Contributions.d.ts +3 -5
  110. package/dist/web/internal/Contributions.d.ts.map +1 -1
  111. package/dist/web/internal/Contributions.js +0 -45
  112. package/dist/web/internal/Contributions.js.map +1 -1
  113. package/dist/web/internal/Engine.browser.d.ts +8 -0
  114. package/dist/web/internal/Engine.browser.d.ts.map +1 -0
  115. package/dist/web/internal/Engine.browser.js +42 -0
  116. package/dist/web/internal/Engine.browser.js.map +1 -0
  117. package/dist/web/internal/Engine.d.ts +9 -0
  118. package/dist/web/internal/Engine.d.ts.map +1 -0
  119. package/dist/web/internal/Engine.js +14 -0
  120. package/dist/web/internal/Engine.js.map +1 -0
  121. package/dist/web/internal/Themes.d.ts +1 -9
  122. package/dist/web/internal/Themes.d.ts.map +1 -1
  123. package/dist/web/internal/Themes.js +3 -221
  124. package/dist/web/internal/Themes.js.map +1 -1
  125. package/dist/web/internal/Wasm.generated.d.ts +4 -0
  126. package/dist/web/internal/Wasm.generated.d.ts.map +1 -0
  127. package/dist/web/internal/Wasm.generated.js +4 -0
  128. package/dist/web/internal/Wasm.generated.js.map +1 -0
  129. package/dist/web/internal/Wire.d.ts +19 -0
  130. package/dist/web/internal/Wire.d.ts.map +1 -0
  131. package/dist/web/internal/Wire.js +387 -0
  132. package/dist/web/internal/Wire.js.map +1 -0
  133. package/package.json +20 -1
  134. package/src/babel/index.ts +2 -5
  135. package/src/babel/internal/Compilation.ts +0 -1
  136. package/src/compiler/Graph.ts +89 -1930
  137. package/src/compiler/Native.ts +18 -871
  138. package/src/compiler/Source.ts +144 -1803
  139. package/src/compiler/Transform.ts +16 -1315
  140. package/src/compiler/internal/AtRules.ts +27 -115
  141. package/src/compiler/internal/Context.ts +11 -0
  142. package/src/compiler/internal/Results.ts +406 -0
  143. package/src/compiler/internal/Syntax.ts +0 -3
  144. package/src/compiler/internal/Wire.ts +1045 -0
  145. package/src/internal/Addon.ts +118 -0
  146. package/src/internal/Literal.ts +4 -2
  147. package/src/internal/Token.ts +2 -2
  148. package/src/internal/Vlq.ts +59 -0
  149. package/src/internal/Wasm.ts +111 -0
  150. package/src/metro/transformer.ts +0 -1
  151. package/src/next/internal/Project.ts +2 -9
  152. package/src/node/Host.ts +0 -2
  153. package/src/node/internal/Contracts.ts +3 -7
  154. package/src/node/internal/Snapshot.ts +0 -8
  155. package/src/runtime/Composition.ts +48 -25
  156. package/src/runtime/Recipe.ts +21 -14
  157. package/src/unplugin/index.ts +6 -6
  158. package/src/vite/index.ts +922 -221
  159. package/src/web/Css.ts +12 -908
  160. package/src/web/internal/Block.ts +0 -14
  161. package/src/web/internal/ClassName.ts +52 -85
  162. package/src/web/internal/Contributions.ts +3 -59
  163. package/src/web/internal/Engine.browser.ts +51 -0
  164. package/src/web/internal/Engine.ts +17 -0
  165. package/src/web/internal/Themes.ts +3 -279
  166. package/src/web/internal/Wasm.generated.ts +3 -0
  167. package/src/web/internal/Wire.ts +466 -0
  168. package/src/web/internal/wasm.d.ts +9 -0
  169. package/dist/compiler/internal/Applications.d.ts +0 -55
  170. package/dist/compiler/internal/Applications.d.ts.map +0 -1
  171. package/dist/compiler/internal/Applications.js +0 -393
  172. package/dist/compiler/internal/Applications.js.map +0 -1
  173. package/dist/compiler/internal/ColorProfiles.d.ts +0 -3
  174. package/dist/compiler/internal/ColorProfiles.d.ts.map +0 -1
  175. package/dist/compiler/internal/ColorProfiles.js +0 -84
  176. package/dist/compiler/internal/ColorProfiles.js.map +0 -1
  177. package/dist/compiler/internal/CompositionBindings.d.ts +0 -16
  178. package/dist/compiler/internal/CompositionBindings.d.ts.map +0 -1
  179. package/dist/compiler/internal/CompositionBindings.js +0 -89
  180. package/dist/compiler/internal/CompositionBindings.js.map +0 -1
  181. package/dist/compiler/internal/Compositions.d.ts +0 -37
  182. package/dist/compiler/internal/Compositions.d.ts.map +0 -1
  183. package/dist/compiler/internal/Compositions.js +0 -494
  184. package/dist/compiler/internal/Compositions.js.map +0 -1
  185. package/dist/compiler/internal/Configurations.d.ts +0 -26
  186. package/dist/compiler/internal/Configurations.d.ts.map +0 -1
  187. package/dist/compiler/internal/Configurations.js +0 -259
  188. package/dist/compiler/internal/Configurations.js.map +0 -1
  189. package/dist/compiler/internal/Contract.d.ts +0 -30
  190. package/dist/compiler/internal/Contract.d.ts.map +0 -1
  191. package/dist/compiler/internal/Contract.js +0 -753
  192. package/dist/compiler/internal/Contract.js.map +0 -1
  193. package/dist/compiler/internal/Contributions.d.ts +0 -54
  194. package/dist/compiler/internal/Contributions.d.ts.map +0 -1
  195. package/dist/compiler/internal/Contributions.js +0 -1151
  196. package/dist/compiler/internal/Contributions.js.map +0 -1
  197. package/dist/compiler/internal/Descriptors.d.ts +0 -15
  198. package/dist/compiler/internal/Descriptors.d.ts.map +0 -1
  199. package/dist/compiler/internal/Descriptors.js +0 -192
  200. package/dist/compiler/internal/Descriptors.js.map +0 -1
  201. package/dist/compiler/internal/Dynamic.d.ts +0 -14
  202. package/dist/compiler/internal/Dynamic.d.ts.map +0 -1
  203. package/dist/compiler/internal/Dynamic.js +0 -175
  204. package/dist/compiler/internal/Dynamic.js.map +0 -1
  205. package/dist/compiler/internal/Expression.d.ts +0 -19
  206. package/dist/compiler/internal/Expression.d.ts.map +0 -1
  207. package/dist/compiler/internal/Expression.js +0 -164
  208. package/dist/compiler/internal/Expression.js.map +0 -1
  209. package/dist/compiler/internal/Functions.d.ts +0 -5
  210. package/dist/compiler/internal/Functions.d.ts.map +0 -1
  211. package/dist/compiler/internal/Functions.js +0 -191
  212. package/dist/compiler/internal/Functions.js.map +0 -1
  213. package/dist/compiler/internal/Keyframes.d.ts +0 -5
  214. package/dist/compiler/internal/Keyframes.d.ts.map +0 -1
  215. package/dist/compiler/internal/Keyframes.js +0 -51
  216. package/dist/compiler/internal/Keyframes.js.map +0 -1
  217. package/dist/compiler/internal/Names.d.ts +0 -5
  218. package/dist/compiler/internal/Names.d.ts.map +0 -1
  219. package/dist/compiler/internal/Names.js +0 -37
  220. package/dist/compiler/internal/Names.js.map +0 -1
  221. package/dist/compiler/internal/NativeBindings.d.ts +0 -72
  222. package/dist/compiler/internal/NativeBindings.d.ts.map +0 -1
  223. package/dist/compiler/internal/NativeBindings.js +0 -171
  224. package/dist/compiler/internal/NativeBindings.js.map +0 -1
  225. package/dist/compiler/internal/NativeMedia.d.ts +0 -14
  226. package/dist/compiler/internal/NativeMedia.d.ts.map +0 -1
  227. package/dist/compiler/internal/NativeMedia.js +0 -287
  228. package/dist/compiler/internal/NativeMedia.js.map +0 -1
  229. package/dist/compiler/internal/NativeVars.d.ts +0 -28
  230. package/dist/compiler/internal/NativeVars.d.ts.map +0 -1
  231. package/dist/compiler/internal/NativeVars.js +0 -160
  232. package/dist/compiler/internal/NativeVars.js.map +0 -1
  233. package/dist/compiler/internal/PackedStyles.d.ts +0 -37
  234. package/dist/compiler/internal/PackedStyles.d.ts.map +0 -1
  235. package/dist/compiler/internal/PackedStyles.js +0 -344
  236. package/dist/compiler/internal/PackedStyles.js.map +0 -1
  237. package/dist/compiler/internal/Properties.d.ts +0 -5
  238. package/dist/compiler/internal/Properties.d.ts.map +0 -1
  239. package/dist/compiler/internal/Properties.js +0 -82
  240. package/dist/compiler/internal/Properties.js.map +0 -1
  241. package/dist/compiler/internal/RecipeConditions.d.ts +0 -41
  242. package/dist/compiler/internal/RecipeConditions.d.ts.map +0 -1
  243. package/dist/compiler/internal/RecipeConditions.js +0 -82
  244. package/dist/compiler/internal/RecipeConditions.js.map +0 -1
  245. package/dist/compiler/internal/RecipePayloads.d.ts +0 -31
  246. package/dist/compiler/internal/RecipePayloads.d.ts.map +0 -1
  247. package/dist/compiler/internal/RecipePayloads.js +0 -99
  248. package/dist/compiler/internal/RecipePayloads.js.map +0 -1
  249. package/dist/compiler/internal/Recipes.d.ts +0 -32
  250. package/dist/compiler/internal/Recipes.d.ts.map +0 -1
  251. package/dist/compiler/internal/Recipes.js +0 -339
  252. package/dist/compiler/internal/Recipes.js.map +0 -1
  253. package/dist/compiler/internal/Selectors.d.ts +0 -13
  254. package/dist/compiler/internal/Selectors.d.ts.map +0 -1
  255. package/dist/compiler/internal/Selectors.js +0 -280
  256. package/dist/compiler/internal/Selectors.js.map +0 -1
  257. package/dist/compiler/internal/Static.d.ts +0 -39
  258. package/dist/compiler/internal/Static.d.ts.map +0 -1
  259. package/dist/compiler/internal/Static.js +0 -534
  260. package/dist/compiler/internal/Static.js.map +0 -1
  261. package/dist/compiler/internal/Themes.d.ts +0 -190
  262. package/dist/compiler/internal/Themes.d.ts.map +0 -1
  263. package/dist/compiler/internal/Themes.js +0 -1577
  264. package/dist/compiler/internal/Themes.js.map +0 -1
  265. package/dist/compiler/internal/Vars.d.ts +0 -33
  266. package/dist/compiler/internal/Vars.d.ts.map +0 -1
  267. package/dist/compiler/internal/Vars.js +0 -387
  268. package/dist/compiler/internal/Vars.js.map +0 -1
  269. package/dist/compiler/internal/ViewTransitions.d.ts +0 -3
  270. package/dist/compiler/internal/ViewTransitions.d.ts.map +0 -1
  271. package/dist/compiler/internal/ViewTransitions.js +0 -68
  272. package/dist/compiler/internal/ViewTransitions.js.map +0 -1
  273. package/dist/internal/Cascade.d.ts +0 -74
  274. package/dist/internal/Cascade.d.ts.map +0 -1
  275. package/dist/internal/Cascade.js +0 -210
  276. package/dist/internal/Cascade.js.map +0 -1
  277. package/src/compiler/internal/Applications.ts +0 -610
  278. package/src/compiler/internal/ColorProfiles.ts +0 -93
  279. package/src/compiler/internal/CompositionBindings.ts +0 -120
  280. package/src/compiler/internal/Compositions.ts +0 -678
  281. package/src/compiler/internal/Configurations.ts +0 -349
  282. package/src/compiler/internal/Contract.ts +0 -1028
  283. package/src/compiler/internal/Contributions.ts +0 -1561
  284. package/src/compiler/internal/Descriptors.ts +0 -242
  285. package/src/compiler/internal/Dynamic.ts +0 -275
  286. package/src/compiler/internal/Expression.ts +0 -218
  287. package/src/compiler/internal/Functions.ts +0 -206
  288. package/src/compiler/internal/Keyframes.ts +0 -62
  289. package/src/compiler/internal/Names.ts +0 -48
  290. package/src/compiler/internal/NativeBindings.ts +0 -235
  291. package/src/compiler/internal/NativeMedia.ts +0 -342
  292. package/src/compiler/internal/NativeVars.ts +0 -231
  293. package/src/compiler/internal/PackedStyles.ts +0 -473
  294. package/src/compiler/internal/Properties.ts +0 -121
  295. package/src/compiler/internal/RecipeConditions.ts +0 -139
  296. package/src/compiler/internal/RecipePayloads.ts +0 -132
  297. package/src/compiler/internal/Recipes.ts +0 -531
  298. package/src/compiler/internal/Selectors.ts +0 -353
  299. package/src/compiler/internal/Static.ts +0 -784
  300. package/src/compiler/internal/Themes.ts +0 -2398
  301. package/src/compiler/internal/Vars.ts +0 -516
  302. package/src/compiler/internal/ViewTransitions.ts +0 -82
@@ -2,34 +2,60 @@
2
2
  * Links a closed graph of source modules without executing code or reading files.
3
3
  * @module
4
4
  */
5
- import * as Contributions from './internal/Contributions.js'
6
- import * as Stylesheets from './internal/Stylesheets.js'
7
5
  import type * as Mapping from '@jridgewell/gen-mapping'
8
- import * as Css from '../web/Css.js'
6
+ import * as Addon from '../internal/Addon.js'
7
+ import type * as Css from '../web/Css.js'
9
8
  import * as ClassName from '../web/internal/ClassName.js'
10
- import * as Native from './Native.js'
11
- import * as NativeVars from './internal/NativeVars.js'
12
- import * as NativeStyleSheet from '../react-native/StyleSheet.js'
13
- import * as Edits from './internal/Edits.js'
14
- import * as Identity from '../internal/Identity.js'
15
- import type * as Ast from '@oxc-project/types'
16
- import * as Parser from 'oxc-parser'
17
- import * as Syntax from './internal/Syntax.js'
18
- import * as Walker from 'oxc-walker'
19
- import type * as Theme from '../internal/Theme.js'
20
- import * as Token from '../internal/Token.js'
21
- import * as Contract from './internal/Contract.js'
22
- import * as Relative from './internal/Relative.js'
23
9
  import * as ThemeRules from '../web/internal/Themes.js'
24
- import * as Themes from './internal/Themes.js'
25
- import * as Source from './Source.js'
26
- import * as Scope from './internal/Scope.js'
27
- import * as Static from './internal/Static.js'
28
- import * as Transform from './Transform.js'
10
+ import type * as Native from './Native.js'
11
+ import type * as Source from './Source.js'
12
+ import type * as Transform from './Transform.js'
13
+ import type * as Context from './internal/Context.js'
14
+ import * as Results from './internal/Results.js'
15
+ import * as Stylesheets from './internal/Stylesheets.js'
29
16
 
30
17
  /** Compiles supplied modules with shared theme contracts and dependency metadata. */
31
18
  export function compile(options: compile.Options): compile.ReturnType {
32
- return build(options).result
19
+ return Results.graph(
20
+ Results.call<Results.Output>('graph.compile', encode(options)),
21
+ )
22
+ }
23
+
24
+ /** The engine's answer when a snapshot names a source it no longer holds. */
25
+ const stale = '{"stale":true}'
26
+
27
+ /** Names each source equal to the one `previous` held as `0`. */
28
+ function unchanged(
29
+ sources: Readonly<Record<string, string>> | undefined,
30
+ previous: Readonly<Record<string, string>> | undefined,
31
+ ) {
32
+ if (!sources || !previous) return sources
33
+
34
+ const output: Record<string, string | 0> = {}
35
+
36
+ for (const [id, source] of Object.entries(sources))
37
+ output[id] = previous[id] === source ? 0 : source
38
+
39
+ return output
40
+ }
41
+
42
+ /** The engine's graph input. */
43
+ function encode(options: compile.Options) {
44
+ return {
45
+ complete: options[ClassName.complete],
46
+ compiler: options.compiler,
47
+ composition: options.composition,
48
+ contracts: options.contracts,
49
+ cssOutput: options.cssOutput,
50
+ development: options.development,
51
+ entry: options[Stylesheets.entry],
52
+ imports: options.imports,
53
+ lean: options[Results.lean],
54
+ modules: options.modules,
55
+ native: options.native && Results.native(options.native),
56
+ reset: options.reset,
57
+ shared: options[ThemeRules.shared],
58
+ }
33
59
  }
34
60
 
35
61
  /** Input and output of graph compilation. */
@@ -48,10 +74,8 @@ export declare namespace compile {
48
74
  readonly [Stylesheets.entry]?: string | undefined
49
75
  /** Lets modules reuse shared rule names from unrelated modules of this one graph. */
50
76
  readonly [ClassName.complete]?: boolean | undefined
51
- /** Host syntax from the same immutable source snapshot. */
52
- readonly [Syntax.cache]?:
53
- | ReadonlyMap<string, Parser.ParseResult>
54
- | undefined
77
+ /** Leaves out per-module records and publishes only configuration catalogs as contracts. */
78
+ readonly [Results.lean]?: boolean | undefined
55
79
  /** Rewrite authoring calls. False emits CSS for unchanged source. */
56
80
  readonly compiler?: boolean | undefined
57
81
  /** Whether compiled applications can be combined with one another. */
@@ -68,10 +92,7 @@ export declare namespace compile {
68
92
  | undefined
69
93
  /** Native output context. Omit to compile web modules and CSS. */
70
94
  readonly native?:
71
- | Omit<
72
- Native.compile.Options,
73
- 'moduleId' | 'source' | typeof Themes.context
74
- >
95
+ | Omit<Native.compile.Options, 'moduleId' | 'source' | typeof Context.key>
75
96
  | undefined
76
97
  /** Complete source graph keyed by stable package-relative module identities. */
77
98
  readonly modules: Readonly<Record<string, string>>
@@ -104,1920 +125,58 @@ export declare namespace compile {
104
125
 
105
126
  /** Creates an isolated compiler with bounded extraction reuse across source graphs. */
106
127
  export function create(): create.ReturnType {
107
- let previous: Cache | undefined
128
+ const decoded = new Map<string, Transform.compile.ReturnType>()
129
+ // The engine compiler owns the caches, and collecting this object releases them.
130
+ let engine: InstanceType<Addon.Exports['Graph']> | undefined
131
+ let result: compile.ReturnType | undefined
132
+ // The engine holds the sources of its last decoded snapshot, so a source equal to the one sent
133
+ // last crosses the boundary as `0`. A failed call leaves nothing sent, which resends every source.
134
+ let sent: Pick<ReturnType<typeof encode>, 'contracts' | 'modules'> | undefined
108
135
 
109
136
  return Object.freeze({
110
137
  compile(options: compile.Options): compile.ReturnType {
111
- const next = build(options, previous)
112
-
113
- previous = next
114
-
115
- return next.result
116
- },
117
- })
118
- }
119
-
120
- /** Incremental graph compiler contracts. */
121
- export declare namespace create {
122
- /** Explicitly owned compilation state; dropping the compiler releases its cache. */
123
- type ReturnType = {
124
- /** Compiles a complete source snapshot, reusing unaffected work across calls. */
125
- readonly compile: typeof compile
126
- }
127
- }
128
-
129
- type Extraction = {
130
- dependencies: readonly string[]
131
- extracted: Source.extract.ReturnType
132
- inputs: ReadonlyMap<
133
- string,
134
- Source.extract.ReturnType | ReturnType<typeof Contract.read>
135
- >
136
- resolution: string
137
- source: string
138
- }
139
-
140
- type Transformed = {
141
- classes: Readonly<Record<string, string | undefined>>
142
- extracted: Source.extract.ReturnType
143
- owners: string
144
- output: Transform.compile.ReturnType
145
- schemes: boolean
146
- themes: Readonly<Record<string, Theme.Definition>>
147
- }
148
-
149
- type Cache = {
150
- compiler: boolean
151
- composition: Css.compile.Options['composition']
152
- cssOutput: Css.compile.Options['cssOutput']
153
-
154
- contracts: string
155
- development: boolean
156
- entry: string | undefined
157
- extracted: ReadonlyMap<string, Source.extract.ReturnType>
158
- libraries: Readonly<Record<string, ReturnType<typeof Contract.read>>>
159
- reset: string | undefined
160
- retained: ReadonlyMap<string, Extraction>
161
- transformed: ReadonlyMap<string, Transformed>
162
- resolutions: Readonly<Record<string, string>>
163
- native: compile.Options['native']
164
- result: compile.ReturnType
165
- schemes: boolean
166
- shared?: 'all' | 'defaults' | undefined
167
- sources: Readonly<Record<string, string>>
168
- themes: Readonly<Record<string, Theme.Definition>>
169
- }
170
-
171
- function build(options: compile.Options, cache?: Cache): Cache {
172
- if (
173
- !!cache?.native !== !!options.native ||
174
- cache?.native?.[Edits.runtime] !== options.native?.[Edits.runtime] ||
175
- Object.keys(cache?.native ?? {}).length !==
176
- Object.keys(options.native ?? {}).length ||
177
- Object.entries(cache?.native ?? {}).some(([key, value]) => {
178
- const next = Reflect.get(options.native ?? {}, key)
179
- if (key !== 'fonts' && key !== 'vars' && key !== 'units')
180
- return value !== next
181
-
182
- return (
183
- !!value !== !!next ||
184
- Object.keys(value ?? {}).length !== Object.keys(next ?? {}).length ||
185
- Object.entries(value ?? {}).some(
186
- ([name, entry]) => Reflect.get(next ?? {}, name) !== entry,
187
- )
188
- )
189
- })
190
- )
191
- cache = undefined
192
- if (options.native && options.reset !== undefined)
193
- throw new Native.CompileError(
194
- 'The CSS reset is only supported by web builds.',
195
- )
196
- if (options.native && options.compiler === false)
197
- throw new Native.CompileError(
198
- 'Native graph compilation requires source rewriting.',
199
- )
200
- if (cache?.compiler !== (options.compiler !== false)) cache = undefined
201
- if (cache?.reset !== options.reset) cache = undefined
202
- if (cache?.cssOutput !== options.cssOutput) cache = undefined
203
- if (cache?.composition !== options.composition) cache = undefined
204
- if (cache?.shared !== options[ThemeRules.shared]) cache = undefined
205
- if (
206
- (cache?.entry === undefined) !==
207
- (options[Stylesheets.entry] === undefined)
208
- )
209
- cache = undefined
210
- const ids = Object.keys(options.modules).sort()
211
- const programs = new Map<string, ReturnType<typeof Syntax.parse>>()
212
- function parse(input: Syntax.parse.Options) {
213
- const cached =
214
- programs.get(input.moduleId) ?? options[Syntax.cache]?.get(input.moduleId)
215
- if (cached) return cached
216
- const parsed = Syntax.parse(input)
217
- programs.set(input.moduleId, parsed)
218
- return parsed
219
- }
220
-
221
- const contracts = JSON.stringify(
222
- Object.entries(options.contracts ?? {}).sort(([a], [b]) =>
223
- a.localeCompare(b),
224
- ),
225
- )
226
-
227
- const resolutions = Object.fromEntries(
228
- [...new Set([...ids, ...Object.keys(options.imports ?? {})])]
229
- .sort()
230
- .map((id) => [
231
- id,
232
- options.imports === undefined
233
- ? 'relative'
234
- : JSON.stringify(
235
- Object.entries(options.imports[id] ?? {}).sort(([a], [b]) =>
236
- a.localeCompare(b),
237
- ),
238
- ),
239
- ]),
240
- )
241
-
242
- // Host resolutions remain explicit when generated modules enter or leave the graph.
243
- const previous = (() => {
244
- if (
245
- cache &&
246
- cache.contracts === contracts &&
247
- cache.development === !!options.development &&
248
- (options.imports !== undefined ||
249
- (ids.length === Object.keys(cache.sources).length &&
250
- ids.every((id) => Object.hasOwn(cache.sources, id))))
251
- ) {
252
- return cache
253
- }
254
-
255
- return undefined
256
- })()
257
- if (
258
- previous &&
259
- previous.entry === options[Stylesheets.entry] &&
260
- ids.length === Object.keys(previous.sources).length &&
261
- Object.keys(resolutions).length ===
262
- Object.keys(previous.resolutions).length &&
263
- Object.entries(resolutions).every(
264
- ([id, value]) => value === previous.resolutions[id],
265
- ) &&
266
- ids.every(
267
- (id) =>
268
- options.modules[id] === previous.sources[id] &&
269
- resolutions[id] === previous.resolutions[id],
270
- )
271
- )
272
- return previous
273
-
274
- const retained = new Map(previous?.retained)
275
- const transformed = new Map(previous?.transformed)
276
- const dependencies: Record<string, readonly string[]> = Object.create(null)
277
- const extracted = new Map<string, Source.extract.ReturnType>()
278
- const identifiers = new Map<string, Set<string>>()
279
- const owners: Record<string, NonNullable<Themes.Context['owners']>[string]> =
280
- Object.create(null)
281
- const themes: Record<string, Theme.Definition> = Object.create(null)
282
- const visiting = new Set<string>()
283
- const libraries: Record<
284
- string,
285
- ReturnType<typeof Contract.read>
286
- > = Object.create(null)
287
- const identities = new Map<string, Token.Contract>()
288
- const variableSlots = new Map<
289
- string,
290
- { owner: string; binding: string; type: string }
291
- >()
292
-
293
- function validateLibraryLink(link: Themes.Link, owner: string) {
294
- const variableOwner = owner.includes('/')
295
- ? (link.call.variableOwner ?? owner)
296
- : owner
297
-
298
- for (const slot of Object.values(link.call.variables ?? {})) {
299
- const previous = variableSlots.get(slot.name)
300
- if (
301
- previous &&
302
- (previous.owner !== variableOwner ||
303
- previous.binding !== link.binding ||
304
- previous.type !== slot.type)
305
- )
306
- throw new Error(
307
- `Conflicting packed variable identity: ${slot.name}; supply distinct declaration ids.`,
308
- )
309
-
310
- variableSlots.set(slot.name, {
311
- owner: variableOwner,
312
- binding: link.binding,
313
- type: slot.type,
314
- })
315
- }
316
-
317
- for (const member of Object.values(link.members ?? {}))
318
- validateLibraryLink(member, owner)
319
- }
320
-
321
- for (const [id, source] of Object.entries(options.contracts ?? {})) {
322
- if (Object.hasOwn(options.modules, id))
323
- fail(id, 'A module cannot supply both source and a library contract.')
324
-
325
- try {
326
- const library =
327
- previous?.libraries[id] ?? Contract.read(source, identities, id)
328
-
329
- for (const link of Object.values(library.links))
330
- validateLibraryLink(link, id)
331
-
332
- for (const [name, theme] of Object.entries(library.themes)) {
333
- if (
334
- themes[name] &&
335
- Contract.write({}, { [name]: themes[name]! }) !==
336
- Contract.write({}, { [name]: theme })
337
- )
338
- throw new Error(`Conflicting library theme identity: ${name}`)
339
-
340
- themes[name] = theme
341
- }
342
-
343
- libraries[id] = library
344
- } catch (error) {
345
- fail(id, `Invalid library contract: ${(error as Error).message}`)
346
- }
347
- }
348
-
349
- function fail(
350
- moduleId: string,
351
- message: string,
352
- node?: Pick<Ast.Node, 'start' | 'end'>,
353
- ): never {
354
- throw new Source.ExtractError([
355
- {
356
- code: 'unsupported_syntax',
357
- end: node?.end ?? 0,
358
- message,
359
- source: moduleId,
360
- start: node?.start ?? 0,
361
- },
362
- ])
363
- }
364
-
365
- function resolve(
366
- moduleId: string,
367
- specifier: string,
368
- node: Pick<Ast.Node, 'start' | 'end'> = { start: 0, end: 0 },
369
- ): string | undefined {
370
- if (options.imports !== undefined) {
371
- const imports = options.imports[moduleId]
372
-
373
- if (!imports || !Object.hasOwn(imports, specifier))
374
- fail(moduleId, `Missing host resolution: ${specifier}`, node)
375
-
376
- const target = imports[specifier]
377
- if (target === null) return undefined
378
-
379
- if (
380
- target === undefined ||
381
- (!Object.hasOwn(options.modules, target) &&
382
- !Object.hasOwn(libraries, target))
383
- )
384
- fail(moduleId, `Missing host source module: ${specifier}`, node)
385
-
386
- return target
387
- }
388
-
389
- try {
390
- return Relative.resolve({ moduleId, modules: options.modules, specifier })
391
- } catch (error) {
392
- fail(moduleId, (error as Error).message, node)
393
- }
394
- }
395
-
396
- const constants = new Map<string, Ast.Node | undefined>()
397
- function constant(
398
- moduleId: string,
399
- name: string,
400
- active = new Set<string>(),
401
- ): Ast.Node | undefined {
402
- const key = JSON.stringify([moduleId, name])
403
- if (constants.has(key)) return constants.get(key)
404
- if (active.has(key) || !Object.hasOwn(options.modules, moduleId))
405
- return undefined
406
- const next = new Set(active).add(key)
407
- const program = parse({
408
- moduleId,
409
- source: options.modules[moduleId]!,
410
- }).program
411
- const imports: Record<string, Ast.Node> = Object.create(null)
412
- let local = name
413
- let exported = false
414
- let direct: Ast.Node | undefined
415
- const stars: string[] = []
416
- for (const statement of program.body) {
417
- if (statement.type === 'ExportDefaultDeclaration' && name === 'default') {
418
- direct = statement.declaration
419
- exported = true
420
- }
421
- if (
422
- statement.type === 'ExportAllDeclaration' &&
423
- statement.exportKind !== 'type' &&
424
- !statement.exported &&
425
- name !== 'default'
426
- ) {
427
- const target = resolve(moduleId, statement.source.value, statement)
428
- if (target) stars.push(target)
429
- }
430
- if (
431
- statement.type !== 'ExportNamedDeclaration' ||
432
- statement.exportKind === 'type'
433
- )
434
- continue
435
- if (statement.declaration?.type === 'VariableDeclaration')
436
- exported ||= statement.declaration.declarations.some(
437
- (value) => value.id.type === 'Identifier' && value.id.name === name,
438
- )
439
- for (const specifier of statement.specifiers) {
440
- if (specifier.exportKind === 'type') continue
441
- const alias =
442
- specifier.exported.type === 'Identifier'
443
- ? specifier.exported.name
444
- : specifier.exported.value
445
- if (alias !== name) continue
446
- local =
447
- specifier.local.type === 'Identifier'
448
- ? specifier.local.name
449
- : specifier.local.value
450
- if (statement.source) {
451
- const target = resolve(moduleId, statement.source.value, statement)
452
- return target ? constant(target, local, next) : undefined
453
- }
454
- exported = true
455
- }
456
- }
457
- if (!exported) {
458
- const candidates = [
459
- ...new Set(
460
- stars
461
- .map((target) => constant(target, name, next))
462
- .filter((value) => value !== undefined),
463
- ),
464
- ]
465
- const value = candidates.length === 1 ? candidates[0] : undefined
466
- if (value || !stars.length) constants.set(key, value)
467
- return value
468
- }
469
- if (
470
- direct &&
471
- [
472
- 'FunctionDeclaration',
473
- 'FunctionExpression',
474
- 'ArrowFunctionExpression',
475
- 'ClassDeclaration',
476
- 'ClassExpression',
477
- ].includes(direct.type)
478
- ) {
479
- constants.set(key, undefined)
480
- return undefined
481
- }
482
- for (const statement of program.body) {
483
- if (
484
- statement.type === 'ImportDeclaration' &&
485
- statement.importKind !== 'type'
486
- ) {
487
- const target = resolve(moduleId, statement.source.value, statement)
488
- if (!target) continue
489
- for (const specifier of statement.specifiers) {
490
- if (
491
- specifier.type === 'ImportNamespaceSpecifier' ||
492
- (specifier.type === 'ImportSpecifier' &&
493
- specifier.importKind === 'type')
494
- )
495
- continue
496
- const imported =
497
- specifier.type === 'ImportDefaultSpecifier'
498
- ? 'default'
499
- : specifier.imported.type === 'Identifier'
500
- ? specifier.imported.name
501
- : specifier.imported.value
502
- const value = constant(target, imported, next)
503
- if (value) imports[specifier.local.name] = relocate(value, specifier)
504
- }
505
- }
506
- }
507
- const scope = new Scope.Tracker({ preserveExitedScopes: true })
508
- Walker.walk(program, { scopeTracker: scope })
509
- scope.freeze()
510
- let value: Ast.Node | undefined
511
- try {
512
- const data = Static.collect(program, scope, imports)
513
- value = direct
514
- ? data.normalize(direct, new Set(), new Set(), true)
515
- : data.exported(local)
516
- } catch {
517
- return undefined
518
- }
519
- if (value && !literal(value)) value = undefined
520
- constants.set(key, value)
521
- return value
522
- }
523
-
524
- function literal(node: Ast.Node, depth = 0): boolean {
525
- if (depth > 64) return false
526
- if (node.type === 'Literal')
527
- return (
528
- node.value === null ||
529
- ['string', 'number', 'boolean'].includes(typeof node.value)
530
- )
531
- if (node.type === 'UnaryExpression')
532
- return (
533
- ['+', '-'].includes(node.operator) &&
534
- node.argument.type === 'Literal' &&
535
- typeof node.argument.value === 'number'
536
- )
537
- if (node.type === 'TemplateLiteral') return !node.expressions.length
538
- if (node.type === 'ArrayExpression')
539
- return node.elements.every((value) => value && literal(value, depth + 1))
540
- return (
541
- node.type === 'ObjectExpression' &&
542
- node.properties.every(
543
- (property) =>
544
- property.type === 'Property' &&
545
- !property.method &&
546
- property.kind === 'init' &&
547
- (!property.computed || property.key.type === 'Literal') &&
548
- literal(property.value, depth + 1),
549
- )
550
- )
551
- }
552
-
553
- function relocate(
554
- node: Ast.Node,
555
- span: Pick<Ast.Node, 'start' | 'end'>,
556
- ): Ast.Node {
557
- const copy = structuredClone(node)
558
- Walker.walk(copy, {
559
- enter(node) {
560
- node.start = span.start
561
- node.end = span.end
562
- },
563
- })
564
- return copy
565
- }
566
-
567
- const factoryPrograms = new Map<string, Ast.Program>()
568
- function factory(
569
- moduleId: string,
570
- name: string,
571
- seen = new Set<string>(),
572
- ): string | undefined {
573
- if (moduleId === 'zyzz/web')
574
- return Contributions.factories.includes(name) ? name : undefined
575
- const key = `${moduleId}#${name}`
576
- if (seen.has(key) || !Object.hasOwn(options.modules, moduleId))
577
- return undefined
578
- const next = new Set(seen).add(key)
579
- const program =
580
- factoryPrograms.get(moduleId) ??
581
- Parser.parseSync('barrel.ts', options.modules[moduleId]!, {
582
- sourceType: 'module',
583
- }).program
584
- factoryPrograms.set(moduleId, program)
585
- const target = (specifier: string, node: Ast.Node) =>
586
- specifier === 'zyzz/web' ? specifier : resolve(moduleId, specifier, node)
587
- for (const node of program.body) {
588
- if (node.type !== 'ExportNamedDeclaration' || node.exportKind === 'type')
589
- continue
590
- const declaration = node.declaration
591
- if (
592
- declaration?.type === 'VariableDeclaration' &&
593
- declaration.declarations.some(
594
- (item) => item.id.type === 'Identifier' && item.id.name === name,
595
- )
596
- )
597
- return undefined
598
- if (
599
- (declaration?.type === 'FunctionDeclaration' ||
600
- declaration?.type === 'ClassDeclaration') &&
601
- declaration.id?.name === name
602
- )
603
- return undefined
604
- for (const specifier of node.specifiers) {
605
- if (specifier.exportKind === 'type') continue
606
- const exported =
607
- specifier.exported.type === 'Identifier'
608
- ? specifier.exported.name
609
- : specifier.exported.value
610
- if (exported !== name) continue
611
- const local =
612
- specifier.local.type === 'Identifier'
613
- ? specifier.local.name
614
- : specifier.local.value
615
- if (node.source) {
616
- const id = target(node.source.value, node)
617
- return id ? factory(id, local, next) : undefined
618
- }
619
- for (const declaration of program.body) {
620
- if (
621
- declaration.type !== 'ImportDeclaration' ||
622
- declaration.importKind === 'type'
623
- )
624
- continue
625
- for (const imported of declaration.specifiers) {
626
- if (
627
- imported.type !== 'ImportSpecifier' ||
628
- imported.importKind === 'type' ||
629
- imported.local.name !== local
630
- )
631
- continue
632
- const id = target(declaration.source.value, declaration)
633
- return id
634
- ? factory(
635
- id,
636
- imported.imported.type === 'Identifier'
637
- ? imported.imported.name
638
- : imported.imported.value,
639
- next,
640
- )
641
- : undefined
642
- }
643
- }
644
- return undefined
645
- }
646
- }
647
- for (const node of program.body) {
648
- if (
649
- node.type === 'ExportAllDeclaration' &&
650
- node.exportKind !== 'type' &&
651
- !node.exported
652
- ) {
653
- const id = target(node.source.value, node)
654
- const found = id ? factory(id, name, next) : undefined
655
- if (found) return found
656
- }
657
- }
658
- return undefined
659
- }
660
- function visit(moduleId: string): Source.extract.ReturnType {
661
- const cached = extracted.get(moduleId)
662
- if (cached) return cached
663
-
664
- if (visiting.has(moduleId))
665
- fail(moduleId, 'Circular source dependencies are not supported yet.')
666
-
667
- visiting.add(moduleId)
668
-
669
- const source = options.modules[moduleId]!
670
-
671
- const snapshot =
672
- previous?.retained.get(moduleId) ??
673
- (previous && Object.hasOwn(previous.sources, moduleId)
674
- ? {
675
- dependencies: previous.result.dependencies[moduleId]!,
676
- extracted: previous.extracted.get(moduleId)!,
677
- inputs: new Map(
678
- previous.result.dependencies[moduleId]!.map((target) => [
679
- target,
680
- previous.libraries[target] ?? previous.extracted.get(target)!,
681
- ]),
682
- ),
683
- resolution: previous.resolutions[moduleId]!,
684
- source: previous.sources[moduleId]!,
685
- }
686
- : undefined)
687
- if (
688
- snapshot &&
689
- source === snapshot.source &&
690
- resolutions[moduleId] === snapshot.resolution &&
691
- snapshot.dependencies.every((target) =>
692
- Object.hasOwn(libraries, target)
693
- ? libraries[target] === snapshot.inputs.get(target)
694
- : Object.hasOwn(options.modules, target) &&
695
- visit(target) === snapshot.inputs.get(target),
696
- )
697
- ) {
698
- return retain(moduleId, snapshot.extracted, snapshot.dependencies)
699
- }
700
-
701
- // Validate identity and syntax through the public source boundary before linking.
702
- Source.extract({ moduleId, source: '' })
703
-
704
- const parsed = parse({ moduleId, source })
705
-
706
- if (parsed.errors.length) Source.extract({ moduleId, source })
707
- factoryPrograms.set(moduleId, parsed.program)
708
-
709
- const names = new Set<string>()
710
- identifiers.set(moduleId, names)
711
- Walker.walk(parsed.program, {
712
- enter(node) {
713
- if (node.type === 'Identifier') names.add(node.name)
714
- if (
715
- options.imports === undefined &&
716
- node.type === 'ImportExpression' &&
717
- (node.source.type !== 'Literal' ||
718
- typeof node.source.value !== 'string' ||
719
- node.source.value.startsWith('.'))
720
- )
721
- fail(
722
- moduleId,
723
- 'Source graph dependencies require static imports.',
724
- node,
725
- )
726
- },
727
- })
728
-
729
- const values: Record<string, Ast.Node> = Object.create(null)
730
- const links: Record<string, Themes.Link> = Object.create(null)
731
- const forwarded: Record<string, Themes.Link> = Object.create(null)
732
- const explicit = new Set<string>()
733
- const stars: Readonly<Record<string, Themes.Link>>[] = []
734
- const imports = new Set<string>()
735
-
736
- function exportedBindings(node: Ast.Node) {
737
- if (node.type === 'Identifier') explicit.add(node.name)
738
- else if (node.type === 'ObjectPattern')
739
- for (const property of node.properties)
740
- exportedBindings(
741
- property.type === 'Property' ? property.value : property.argument,
742
- )
743
- else if (node.type === 'ArrayPattern')
744
- for (const element of node.elements) {
745
- if (element) exportedBindings(element)
746
- }
747
- else if (node.type === 'AssignmentPattern') exportedBindings(node.left)
748
- else if (node.type === 'RestElement') exportedBindings(node.argument)
749
- }
750
-
751
- for (const node of parsed.program.body) {
752
- if (
753
- node.type === 'ExportNamedDeclaration' &&
754
- !node.source &&
755
- node.exportKind !== 'type'
756
- ) {
757
- for (const specifier of node.specifiers)
758
- if (specifier.exportKind !== 'type')
759
- explicit.add(
760
- specifier.exported.type === 'Identifier'
761
- ? specifier.exported.name
762
- : specifier.exported.value,
763
- )
764
-
765
- if (node.declaration?.type === 'VariableDeclaration')
766
- for (const declaration of node.declaration.declarations)
767
- exportedBindings(declaration.id)
768
- else if (
769
- (node.declaration?.type === 'FunctionDeclaration' ||
770
- node.declaration?.type === 'ClassDeclaration') &&
771
- node.declaration.id
772
- )
773
- explicit.add(node.declaration.id.name)
774
- }
775
-
776
- if (
777
- (node.type !== 'ImportDeclaration' &&
778
- node.type !== 'ExportNamedDeclaration' &&
779
- node.type !== 'ExportAllDeclaration') ||
780
- !node.source
781
- )
782
- continue
783
-
784
- if (
785
- node.type === 'ImportDeclaration'
786
- ? node.importKind === 'type'
787
- : node.exportKind === 'type'
788
- )
789
- continue
790
-
791
- if (
792
- 'specifiers' in node &&
793
- node.specifiers.length &&
794
- node.specifiers.every((specifier) =>
795
- specifier.type === 'ImportSpecifier'
796
- ? specifier.importKind === 'type'
797
- : specifier.type === 'ExportSpecifier' &&
798
- specifier.exportKind === 'type',
799
- )
800
- )
801
- continue
802
-
803
- const target = resolve(moduleId, node.source.value, node)
804
- if (!target) continue
805
-
806
- imports.add(target)
807
-
808
- const contracts =
809
- libraries[target]?.links ?? visit(target).themeExports ?? {}
810
-
811
- function namespace(name: string): Themes.Link | undefined {
812
- const first = Object.values(contracts)[0]
813
- if (!first) return undefined
814
- return {
815
- binding: `z-style-${Identity.hash(JSON.stringify([moduleId, name]))}-namespace`,
816
- call: { start: -1, end: -1, name: '', tokenType: '{}' },
817
- definition: first.definition,
818
- kind: 'style-reference',
819
- members: contracts,
820
- }
821
- }
822
-
823
- if (node.type === 'ImportDeclaration') {
824
- for (const specifier of node.specifiers) {
825
- if (specifier.type === 'ImportNamespaceSpecifier') {
826
- if (
827
- Object.values(contracts).some(
828
- (link) => link.kind !== 'style-reference',
829
- )
830
- )
831
- fail(
832
- moduleId,
833
- 'Import theme contracts by name; namespace imports are not supported.',
834
- specifier,
835
- )
836
-
837
- const link = namespace(specifier.local.name)
838
- if (link) links[specifier.local.name] = link
839
- continue
840
- }
841
-
842
- if (
843
- specifier.type === 'ImportSpecifier' &&
844
- specifier.importKind === 'type'
845
- )
846
- continue
847
-
848
- const name = (() => {
849
- if (specifier.type === 'ImportDefaultSpecifier') {
850
- return 'default'
851
- }
852
-
853
- if (specifier.imported.type === 'Identifier') {
854
- return specifier.imported.name
855
- }
856
-
857
- return specifier.imported.value
858
- })()
859
-
860
- if (Object.hasOwn(contracts, name))
861
- links[specifier.local.name] = contracts[name]!
862
- else {
863
- const value = constant(target, name)
864
- if (value) values[specifier.local.name] = relocate(value, specifier)
865
- }
866
- }
867
- } else if (node.type === 'ExportAllDeclaration') {
868
- if (node.exported) {
869
- if (
870
- Object.values(contracts).some(
871
- (link) => link.kind !== 'style-reference',
872
- )
873
- )
874
- fail(
875
- moduleId,
876
- 'Namespace theme re-exports are not supported.',
877
- node,
878
- )
879
- const name =
880
- node.exported.type === 'Identifier'
881
- ? node.exported.name
882
- : node.exported.value
883
- explicit.add(name)
884
- const link = namespace(name)
885
- if (link) forwarded[name] = link
886
- } else stars.push(contracts)
887
- } else {
888
- for (const specifier of node.specifiers) {
889
- if (specifier.exportKind === 'type') continue
890
-
891
- const name =
892
- specifier.local.type === 'Identifier'
893
- ? specifier.local.name
894
- : specifier.local.value
895
- const exported =
896
- specifier.exported.type === 'Identifier'
897
- ? specifier.exported.name
898
- : specifier.exported.value
899
-
900
- explicit.add(exported)
901
-
902
- if (Object.hasOwn(contracts, name))
903
- forwarded[exported] = contracts[name]!
904
- }
905
- }
906
- }
907
- const factories: Record<string, string> = Object.create(null)
908
- for (const node of parsed.program.body) {
909
- if (
910
- node.type !== 'ImportDeclaration' ||
911
- node.importKind === 'type' ||
912
- node.specifiers.every(
913
- (specifier) =>
914
- specifier.type === 'ImportSpecifier' &&
915
- specifier.importKind === 'type',
916
- ) ||
917
- ['zyzz', 'zyzz/web'].includes(node.source.value)
918
- )
919
- continue
920
- const target = resolve(moduleId, node.source.value, node)
921
- if (!target) continue
922
- for (const specifier of node.specifiers) {
923
- if (
924
- specifier.type !== 'ImportSpecifier' ||
925
- specifier.importKind === 'type'
926
- )
927
- continue
928
- const name =
929
- specifier.imported.type === 'Identifier'
930
- ? specifier.imported.name
931
- : specifier.imported.value
932
- const found = factory(target, name)
933
- if (found) factories[specifier.local.name] = found
934
- }
935
- }
936
-
937
- const result = Source.extract({
938
- target: options.native ? 'native' : 'web',
939
- compiler: options.compiler,
940
- moduleId,
941
- source,
942
- [Themes.context]: { constants: values, factories, links, parsed },
943
- })
944
-
945
- function outputLink(link: Themes.Link): Themes.Link {
946
- return {
947
- ...link,
948
- ...(link.style
949
- ? {
950
- style: {
951
- ...link.style,
952
- style: {
953
- ...link.style.style,
954
- cssOutput:
955
- link.style.style.cssOutput ?? options.cssOutput ?? 'atomic',
956
- },
957
- },
958
- }
959
- : {}),
960
- ...(link.members
961
- ? {
962
- members: Object.fromEntries(
963
- Object.entries(link.members).map(([name, member]) => [
964
- name,
965
- outputLink(member),
966
- ]),
967
- ),
968
- }
969
- : {}),
970
- }
971
- }
972
-
973
- const exports: Record<string, Themes.Link> = Object.assign(
974
- Object.create(null),
975
- forwarded,
976
- Object.fromEntries(
977
- Object.entries(result.themeExports ?? {}).map(([name, link]) => [
978
- name,
979
- outputLink(link),
980
- ]),
981
- ),
982
- )
983
-
984
- for (const name of Object.keys(result.themeExports ?? {}))
985
- explicit.add(name)
986
-
987
- for (const contracts of stars)
988
- for (const [name, link] of Object.entries(contracts)) {
989
- if (name === 'default' || explicit.has(name)) continue
990
-
991
- const previous = exports[name]
992
-
993
- if (
994
- previous &&
995
- (previous.binding !== link.binding || previous.kind !== link.kind)
996
- )
997
- fail(moduleId, `Ambiguous theme re-export: ${name}`)
998
-
999
- exports[name] = link
1000
- }
1001
-
1002
- const linked = Object.freeze({
1003
- ...result,
1004
- themeExports: Object.freeze(exports),
1005
- })
1006
-
1007
- return retain(moduleId, linked, Object.freeze([...imports]))
1008
- }
138
+ engine ??= new (Addon.load().Graph)()
1009
139
 
1010
- function retain(
1011
- moduleId: string,
1012
- result: Source.extract.ReturnType,
1013
- imports: readonly string[],
1014
- ): Source.extract.ReturnType {
1015
- for (const call of result.variableCalls ?? [])
1016
- for (const slot of Object.values(call.slots)) {
1017
- const owner = moduleId.replace(/\.[cm]?[jt]sx?$/, '')
1018
- const previous = variableSlots.get(slot.name)
140
+ const input = encode(options)
141
+ const previous = sent
1019
142
 
1020
- if (
1021
- previous &&
1022
- (previous.owner !== owner || previous.type !== slot.type)
1023
- )
1024
- fail(
1025
- moduleId,
1026
- `Conflicting variable identity: ${slot.name}; supply distinct declaration ids.`,
1027
- call,
1028
- )
1029
-
1030
- variableSlots.set(slot.name, {
1031
- owner,
1032
- binding: `source:${call.start}`,
1033
- type: slot.type,
1034
- })
1035
- }
1036
-
1037
- extracted.set(moduleId, result)
1038
- dependencies[moduleId] = imports
1039
- retained.delete(moduleId)
1040
- retained.set(moduleId, {
1041
- dependencies: imports,
1042
- extracted: result,
1043
- inputs: new Map(
1044
- imports.map((target) => [
1045
- target,
1046
- libraries[target] ?? extracted.get(target)!,
1047
- ]),
1048
- ),
1049
- resolution: resolutions[moduleId]!,
1050
- source: options.modules[moduleId]!,
1051
- })
1052
- if (retained.size > 256) retained.delete(retained.keys().next().value!)
1053
-
1054
- for (const call of result.themeCalls)
1055
- for (const name of new Set([
1056
- call.name,
1057
- ...Object.values(call.members ?? {}),
1058
- ]))
1059
- owners[name] = {
1060
- call: { ...call, name },
1061
- moduleId,
1062
- source: options.modules[moduleId]!,
1063
- }
1064
-
1065
- Object.assign(themes, result.vars)
1066
- visiting.delete(moduleId)
1067
-
1068
- return result
1069
- }
1070
-
1071
- for (const moduleId of ids) visit(moduleId)
143
+ sent = undefined
1072
144
 
1073
- if (options.native) {
1074
- const native = options.native
1075
- function packedLink(link: Themes.Link): Themes.Link {
1076
- return {
1077
- ...link,
1078
- ...(link.style
1079
- ? { style: { ...link.style, className: link.style.className ?? '' } }
1080
- : {}),
1081
- ...(link.members
1082
- ? {
1083
- members: Object.fromEntries(
1084
- Object.entries(link.members).map(([name, link]) => [
1085
- name,
1086
- packedLink(link),
1087
- ]),
1088
- ),
1089
- }
1090
- : {}),
1091
- }
1092
- }
1093
- const profiles = new Map<
1094
- string,
1095
- {
1096
- definitions: Map<string, string>
1097
- reads: Map<number, { name: string; source?: string }>
1098
- }
1099
- >()
1100
- const reserved = new Set(
1101
- [...identifiers.values()].flatMap((names) => [...names]),
1102
- )
1103
- function profile(moduleId: string) {
1104
- let value = profiles.get(moduleId)
1105
- if (!value) {
1106
- value = { definitions: new Map(), reads: new Map() }
1107
- profiles.set(moduleId, value)
1108
- }
1109
- return value
1110
- }
145
+ const text = (() => {
146
+ if (!previous) return engine.compile(JSON.stringify(input))
1111
147
 
1112
- function encode(read: Omit<Themes.VarsRead, 'end' | 'start'>) {
1113
- try {
1114
- return JSON.stringify(
1115
- NativeVars.compile({
1116
- ...read,
1117
- fonts: native.fonts,
1118
- units: native.units,
148
+ const output = engine.compile(
149
+ JSON.stringify({
150
+ ...input,
151
+ contracts: unchanged(input.contracts, previous.contracts),
152
+ modules: unchanged(input.modules, previous.modules),
1119
153
  }),
1120
154
  )
1121
- } catch (error) {
1122
- throw new Native.CompileError((error as Error).message)
1123
- }
1124
- }
1125
-
1126
- function define(owner: string, value: string) {
1127
- const definitions = profile(owner).definitions
1128
- let name = definitions.get(value)
1129
- if (!name) {
1130
- // Hashing the owner keeps names independent of which modules share the graph.
1131
- name = `__zyzzProfile${Identity.hash(owner + value)}`
1132
- while (reserved.has(name)) name += '_'
1133
- reserved.add(name)
1134
- definitions.set(value, name)
1135
- }
1136
- return name
1137
- }
1138
-
1139
- if (ids.some((id) => extracted.get(id)!.nativeVars?.length))
1140
- NativeStyleSheet.compile({
1141
- fonts: native.fonts,
1142
- platform: native.platform,
1143
- units: native.units,
1144
- styles: { styles: [] },
1145
- })
1146
-
1147
- // Single-file adapters compile each owner without its consumers, so owners publish every exported profile.
1148
- if (native[Edits.runtime])
1149
- for (const moduleId of ids)
1150
- for (const link of Object.values(
1151
- extracted.get(moduleId)!.themeExports ?? {},
1152
- )) {
1153
- if (
1154
- owners[link.call.name]?.moduleId !== moduleId ||
1155
- !(
1156
- link.call.variableSet ||
1157
- (link.kind === 'config' &&
1158
- link.call.variableConfig &&
1159
- !link.call.initialization &&
1160
- !link.call.root)
1161
- )
1162
- )
1163
- continue
1164
- const value = (() => {
1165
- try {
1166
- return encode(Themes.varsRead(link))
1167
- } catch {
1168
- // Consumers that read this profile report the same error.
1169
- return undefined
1170
- }
1171
- })()
1172
- if (value !== undefined) define(moduleId, value)
1173
- }
1174
-
1175
- for (const moduleId of ids) {
1176
- for (const read of extracted.get(moduleId)!.nativeVars ?? []) {
1177
- const owner = owners[read.owner]?.moduleId
1178
- // Packed catalogs without source retain their local fallback.
1179
- if (!owner || !Object.hasOwn(options.modules, owner)) continue
1180
155
 
1181
- const value = encode(read)
1182
- const name = native[Edits.runtime]
1183
- ? profiles.get(owner)?.definitions.get(value)
1184
- : define(owner, value)
1185
- if (!name) continue
156
+ return output === stale ? engine.compile(JSON.stringify(input)) : output
157
+ })()
1186
158
 
1187
- const source = (() => {
1188
- if (owner === moduleId) return undefined
1189
-
1190
- // Host identities are opaque, so reuse the consumer's own resolved specifier.
1191
- if (native[Edits.runtime] && options.imports) {
1192
- const imports = options.imports[moduleId] ?? {}
1193
- return Object.keys(imports).find((key) => imports[key] === owner)
1194
- }
1195
-
1196
- const from = moduleId.split('/').slice(0, -1)
1197
- const to = owner.split('/')
1198
- while (from.length && from[0] === to[0]) {
1199
- from.shift()
1200
- to.shift()
1201
- }
1202
- return (
1203
- (from.length ? '../'.repeat(from.length) : './') +
1204
- to.join('/').replace(/\.([cm]?)tsx?$/, '.$1js')
1205
- )
1206
- })()
1207
- if (owner !== moduleId && !source) continue
1208
- profile(moduleId).reads.set(read.start, {
1209
- name,
1210
- ...(source ? { source } : {}),
1211
- })
1212
- }
1213
- }
1214
- const modules = Object.fromEntries(
1215
- ids.map((moduleId) => {
1216
- // Single-file profiles derive only from this module's extraction and its dependencies.
1217
- if (
1218
- previous &&
1219
- (native[Edits.runtime] || !profiles.size) &&
1220
- extracted.get(moduleId) === previous.extracted.get(moduleId)
1221
- )
1222
- return [moduleId, previous.result.modules[moduleId]!]
1223
-
1224
- const imported: Record<
1225
- string,
1226
- Readonly<Record<string, Themes.Link>>
1227
- > = Object.create(null)
1228
- for (const statement of parse({
1229
- moduleId,
1230
- source: options.modules[moduleId]!,
1231
- }).program.body) {
1232
- if (
1233
- (statement.type !== 'ImportDeclaration' &&
1234
- statement.type !== 'ExportNamedDeclaration' &&
1235
- statement.type !== 'ExportAllDeclaration') ||
1236
- !statement.source
1237
- )
1238
- continue
1239
- if (
1240
- (statement.type === 'ImportDeclaration' &&
1241
- statement.importKind === 'type') ||
1242
- (statement.type !== 'ImportDeclaration' &&
1243
- statement.exportKind === 'type')
1244
- )
1245
- continue
1246
- if (
1247
- 'specifiers' in statement &&
1248
- statement.specifiers.length &&
1249
- statement.specifiers.every((specifier) =>
1250
- specifier.type === 'ImportSpecifier'
1251
- ? specifier.importKind === 'type'
1252
- : specifier.type === 'ExportSpecifier' &&
1253
- specifier.exportKind === 'type',
1254
- )
1255
- )
1256
- continue
1257
- const target = resolve(moduleId, statement.source.value, statement)
1258
- if (target && libraries[target])
1259
- imported[statement.source.value] = libraries[target].links
1260
- }
1261
- const output = Native.compile({
1262
- ...native,
1263
- [NativeVars.shared]: profiles.get(moduleId),
1264
- moduleId,
1265
- source: options.modules[moduleId]!,
1266
- [Themes.context]: {
1267
- parsed: parse({ moduleId, source: options.modules[moduleId]! }),
1268
- identifiers: identifiers.get(moduleId),
1269
- extracted: extracted.get(moduleId)!,
1270
- libraries: imported,
1271
- links: {},
1272
- },
1273
- })
1274
- return [moduleId, nativeOutput(output)]
1275
- }),
1276
- )
1277
- return {
1278
- compiler: true,
1279
- composition: options.composition,
1280
- contracts,
1281
- cssOutput: options.cssOutput,
1282
- development: !!options.development,
1283
- extracted,
1284
- libraries: Object.freeze(libraries),
1285
- native: {
1286
- ...options.native,
1287
- ...(options.native.fonts && { fonts: { ...options.native.fonts } }),
1288
- ...(options.native.vars && { vars: { ...options.native.vars } }),
1289
- ...(options.native.units && { units: { ...options.native.units } }),
1290
- },
1291
- reset: options.reset,
1292
- entry: options[Stylesheets.entry],
1293
- retained,
1294
- transformed,
1295
- resolutions: Object.freeze(resolutions),
1296
- result: Object.freeze({
1297
- contracts: Object.freeze(
1298
- Object.fromEntries(
1299
- ids
1300
- .filter(
1301
- (id) =>
1302
- Object.keys(extracted.get(id)!.themeExports ?? {}).length,
1303
- )
1304
- .map((id) => [
1305
- id,
1306
- Contract.write(
1307
- Object.fromEntries(
1308
- Object.entries(extracted.get(id)!.themeExports ?? {}).map(
1309
- ([name, link]) => [name, packedLink(link)],
1310
- ),
1311
- ),
1312
- themes,
1313
- [],
1314
- id,
1315
- ),
1316
- ]),
1317
- ),
1318
- ),
1319
- dependencies: Object.freeze(dependencies),
1320
- modules: Object.freeze(modules),
1321
- }),
1322
- schemes: false,
1323
- sources: Object.freeze({ ...options.modules }),
1324
- themes: Object.freeze(themes),
1325
- }
1326
- }
1327
-
1328
- const modules: Record<string, Transform.compile.ReturnType> =
1329
- Object.create(null)
1330
- const sharedThemes = Object.freeze(themes)
1331
- const sections = new Map<string, readonly Stylesheets.Section[]>()
1332
-
1333
- for (const id of ids) {
1334
- const extractedModule = extracted.get(id)!
1335
- const contributions = extractedModule.contributions ?? []
1336
-
1337
- if (!contributions.length) {
1338
- sections.set(id, [])
1339
- continue
1340
- }
1341
-
1342
- sections.set(
1343
- id,
1344
- contributions.map((contribution, index) => ({
1345
- source: id,
1346
- namespaces: extractedModule.namespaces,
1347
- key: String(index),
1348
- css:
1349
- contribution.kind === 'layers'
1350
- ? ''
1351
- : (Css.compile({
1352
- styles: { styles: [] },
1353
- vars: sharedThemes,
1354
- contributions: [contribution],
1355
- }).contributionCss ?? ''),
1356
- layers: contribution.kind === 'layers' ? [contribution.names] : [],
1357
- content: options.modules[id]!,
1358
- start: extractedModule.contributionStarts?.[index] ?? 0,
1359
- })),
1360
- )
1361
- }
1362
-
1363
- for (const [id, library] of Object.entries(libraries))
1364
- sections.set(
1365
- id,
1366
- library.stylesheets.map((section) => {
1367
- let owner = id
1368
-
1369
- for (const specifier of section.dependency ?? []) {
1370
- const target = resolve(owner, specifier)
1371
-
1372
- if (!target || !Object.hasOwn(libraries, target))
1373
- fail(
1374
- id,
1375
- 'Repacked stylesheet dependencies require supplied library contracts.',
1376
- )
1377
-
1378
- owner = target
1379
- }
1380
-
1381
- return {
1382
- ...section,
1383
- owner,
1384
- source: Stylesheets.resolve(owner, section.source),
1385
- }
1386
- }),
1387
- )
1388
-
1389
- const resetOwners = ids.filter(
1390
- (id) =>
1391
- options.modules[id]!.includes('zyzz/reset.css') &&
1392
- parse({
1393
- moduleId: id,
1394
- source: options.modules[id]!,
1395
- }).program.body.some(
1396
- (node) =>
1397
- node.type === 'ImportDeclaration' &&
1398
- node.source.value === 'zyzz/reset.css',
1399
- ),
1400
- )
1401
-
1402
- const layerNames = [
1403
- ...new Set(
1404
- ids.flatMap((id) =>
1405
- reachable(id).flatMap((section) => section.layers.flat()),
1406
- ),
1407
- ),
1408
- ].filter((name) => name !== 'reset')
1409
-
1410
- for (const resetOwner of resetOwners) {
1411
- const entryLayers = [
1412
- ...new Set(
1413
- reachable(resetOwner).flatMap((section) => section.layers.flat()),
1414
- ),
1415
- ].filter((name) => name !== 'reset')
1416
-
1417
- sections.set(resetOwner, [
1418
- {
1419
- source: resetOwner,
1420
- key: 'optional-reset-order',
1421
- css: '',
1422
- layers: entryLayers.length
1423
- ? entryLayers.map((name) => ['reset', name])
1424
- : [['reset']],
1425
- },
1426
- ...(sections.get(resetOwner) ?? []),
1427
- ])
1428
- }
1429
-
1430
- function dependencyPath(from: string, to: string): readonly string[] {
1431
- const queue = [{ id: from, path: [] as string[] }]
1432
- const seen = new Set<string>()
1433
-
1434
- while (queue.length) {
1435
- const current = queue.shift()!
1436
- if (current.id === to) return current.path
1437
- if (seen.has(current.id)) continue
1438
-
1439
- seen.add(current.id)
1440
-
1441
- for (const [specifier, target] of Object.entries(
1442
- options.imports?.[current.id] ?? {},
1443
- ))
1444
- if (target)
1445
- queue.push({ id: target, path: [...current.path, specifier] })
1446
- }
1447
-
1448
- fail(
1449
- from,
1450
- 'Repacked stylesheet ownership requires a resolved dependency path.',
1451
- )
1452
- }
1453
-
1454
- function reachable(
1455
- id: string,
1456
- visited = new Set<string>(),
1457
- ): readonly Stylesheets.Section[] {
1458
- if (visited.has(id)) return []
1459
-
1460
- visited.add(id)
1461
-
1462
- return [
1463
- ...(dependencies[id] ?? []).flatMap((dependency) =>
1464
- reachable(dependency, visited),
1465
- ),
1466
- ...(sections.get(id) ?? []),
1467
- ]
1468
- }
1469
-
1470
- const sharedVisited = new Set<string>()
1471
- const packed: Stylesheets.Resource[] = []
1472
-
1473
- const sharedSections = (() => {
1474
- try {
1475
- const entry = options[Stylesheets.entry]
1476
- const packedSections =
1477
- entry === undefined
1478
- ? []
1479
- : (dependencies[entry] ?? [])
1480
- .filter((id) => Object.hasOwn(libraries, id))
1481
- .flatMap((id) => reachable(id, sharedVisited))
1482
- const sharedSections =
1483
- entry === undefined
1484
- ? ids.flatMap((id) => reachable(id, sharedVisited))
1485
- : (sections.get(entry) ?? [])
1486
- if (packedSections.length) {
1487
- // Validate combined ownership and ordering before splitting independently loaded resources.
1488
- Stylesheets.render([...packedSections, ...sharedSections])
1489
- for (const section of packedSections) {
1490
- packed.push({
1491
- id: JSON.stringify([section.owner, section.source, section.key]),
1492
- ...Stylesheets.render([section]),
1493
- })
1494
- }
1495
- }
1496
- const resetSource =
1497
- (options.reset === undefined ? undefined : 'zyzz/reset.css') ??
1498
- resetOwners[0] ??
1499
- sharedSections.find((section) => section.key === 'optional-reset-order')
1500
- ?.source
1501
-
1502
- if (packed.length) {
1503
- const order = Stylesheets.render([
1504
- ...(resetSource === undefined
1505
- ? []
1506
- : [
1507
- {
1508
- source: resetSource,
1509
- css: '',
1510
- layers: layerNames.length
1511
- ? layerNames.map((name) => ['reset', name])
1512
- : [['reset']],
1513
- },
1514
- ]),
1515
- ...[...packedSections, ...sharedSections].map((section) => ({
1516
- ...section,
1517
- css: '',
1518
- })),
1519
- ])
1520
- if (order.css) packed.unshift({ ...order, id: order.css })
1521
- }
1522
-
1523
- return [
1524
- ...(resetSource === undefined
1525
- ? []
1526
- : [
1527
- {
1528
- source: resetSource,
1529
- key: 'optional-reset-graph-order',
1530
- css: '',
1531
- layers: layerNames.length
1532
- ? layerNames.map((name) => ['reset', name])
1533
- : [['reset']],
1534
- },
1535
- ]),
1536
- ...sharedSections,
1537
- ...(options.reset === undefined || entry !== undefined
1538
- ? []
1539
- : [
1540
- {
1541
- source: 'zyzz/reset.css',
1542
- key: 'optional-reset',
1543
- css: options.reset,
1544
- content: options.reset,
1545
- layers: [['reset']],
1546
- },
1547
- ]),
1548
- ] satisfies Stylesheets.Section[]
1549
- } catch (error) {
1550
- return fail(
1551
- error instanceof Stylesheets.ConflictError ? error.source : ids[0]!,
1552
- (error as Error).message,
1553
- )
1554
- }
1555
- })()
1556
-
1557
- function renderShared(sections: readonly Stylesheets.Section[]) {
1558
- try {
1559
- return Stylesheets.render(sections)
1560
- } catch (error) {
1561
- return fail(
1562
- error instanceof Stylesheets.ConflictError ? error.source : ids[0]!,
1563
- (error as Error).message,
1564
- )
1565
- }
1566
- }
1567
-
1568
- const contributions = renderShared(sharedSections)
1569
-
1570
- // Every stylesheet includes all graph scopes, including unimported alternatives.
1571
- const names = Object.keys(themes)
1572
- const previousNames = Object.keys(previous?.themes ?? {})
1573
-
1574
- const sameThemes =
1575
- previous &&
1576
- names.length === previousNames.length &&
1577
- names.every(
1578
- (name, index) =>
1579
- name === previousNames[index] && previous.themes[name] === themes[name],
1580
- )
1581
-
1582
- const styleClasses: Record<string, string> = Object.create(null)
1583
- function published(link: Themes.Link) {
1584
- if (link.style?.className !== undefined)
1585
- styleClasses[link.binding] = link.style.className
1586
- for (const member of Object.values(link.members ?? {})) published(member)
1587
- }
1588
- for (const library of Object.values(libraries))
1589
- for (const link of Object.values(library.links)) published(link)
1590
-
1591
- const emittedRules = new Map<string, { body: string; moduleId: string }>()
1592
- function registerRules(
1593
- classRules: Readonly<Record<string, string>>,
1594
- moduleId: string,
1595
- ) {
1596
- for (const [name, body] of Object.entries(classRules)) {
1597
- const previous = emittedRules.get(name)
1598
- if (previous && previous.body !== body)
1599
- throw new Css.CompileError([
1600
- {
1601
- code: 'identity_collision',
1602
- message: `Generated ${name.startsWith('@scope:') ? `scope ${name.split(':')[1]}` : `class ${name}`} conflicts with module ${previous.moduleId}. Supply distinct config or style ids.`,
1603
- path: [moduleId],
1604
- },
1605
- ])
1606
- emittedRules.set(name, { body, moduleId })
1607
- }
1608
- }
1609
- for (const [moduleId, library] of Object.entries(libraries))
1610
- registerRules(library.classRules, moduleId)
1611
- if (options.compiler === false) {
1612
- const identities = new Map<string, string>()
1613
- for (const [moduleId, library] of Object.entries(libraries)) {
1614
- function register(name: string, signature: string) {
1615
- const previous = identities.get(name)
1616
- if (previous !== undefined && previous !== signature)
1617
- fail(
1618
- moduleId,
1619
- 'The same explicit identity is used for different definitions.',
1620
- )
1621
- identities.set(name, signature)
1622
- }
1623
- for (const [name, theme] of Object.entries(library.themes)) {
1624
- const data = theme[Token.definition]
1625
- if (data.contract[Token.identity]?.startsWith('id-'))
1626
- register(`theme:${name}`, JSON.stringify(data.values))
1627
- }
1628
- function styles(link: Themes.Link) {
1629
- for (const name of link.style?.className?.split(' ') ?? [])
1630
- if (name.startsWith('z-style-id-'))
1631
- register(name, Identity.style(link.style!.style))
1632
- for (const member of Object.values(link.members ?? {})) styles(member)
1633
- }
1634
- for (const link of Object.values(library.links)) styles(link)
1635
- }
1636
- for (const [moduleId, module] of extracted) {
1637
- function register(name: string, signature: string) {
1638
- const previous = identities.get(name)
1639
- if (previous !== undefined && previous !== signature)
1640
- return fail(
1641
- moduleId,
1642
- 'The same explicit identity is used for different definitions.',
1643
- )
1644
- identities.set(name, signature)
1645
- }
1646
- for (const [name, theme] of Object.entries(module.vars)) {
1647
- const data = theme[Token.definition]
1648
- if (data.contract[Token.identity]?.startsWith('id-'))
1649
- register(`theme:${name}`, JSON.stringify(data.values))
1650
- }
1651
- for (const contribution of module.contributions ?? [])
1652
- if ('name' in contribution)
1653
- register(
1654
- `${contribution.kind}:${contribution.name}`,
1655
- Css.compile({
1656
- styles: { styles: [] },
1657
- contributions: [contribution],
1658
- }).css,
1659
- )
1660
- for (const call of module.calls) {
1661
- if (!call.portable?.startsWith('z-style-id-')) continue
1662
- const signature = Identity.style(
1663
- module.styles.styles.find((style) => style.name === call.name)!,
1664
- )
1665
- const previous = identities.get(call.portable)
1666
- if (previous !== undefined && previous !== signature)
1667
- return fail(
1668
- moduleId,
1669
- 'The same explicit style id is used for different declarations.',
1670
- )
1671
- identities.set(call.portable, signature)
1672
- }
1673
- }
1674
- }
1675
-
1676
- // Selection anywhere in the graph applies scheme classes beside scopes every
1677
- // stylesheet carries, so each stylesheet declares the matching color-scheme.
1678
- const schemes = [...extracted.values()].some((module) =>
1679
- Boolean(
1680
- module.themeAppearances?.length ||
1681
- module.themeScripts?.length ||
1682
- module.themeSelections?.length,
1683
- ),
1684
- )
1685
-
1686
- const ownerSignature = JSON.stringify(owners)
1687
- const units = ClassName.registry()
1688
- const closures = new Map<string, ReadonlySet<string>>()
1689
-
1690
- function closure(moduleId: string): ReadonlySet<string> {
1691
- const cached = closures.get(moduleId)
1692
- if (cached) return cached
1693
-
1694
- const result = new Set<string>()
1695
- closures.set(moduleId, result)
1696
- for (const dependency of dependencies[moduleId] ?? []) {
1697
- result.add(dependency)
1698
- for (const nested of closure(dependency)) result.add(nested)
1699
- }
1700
-
1701
- return result
1702
- }
1703
-
1704
- // Hosts that compile a module within several graphs must name it the same in
1705
- // each, so it may reuse only names its dependencies introduce.
1706
- function visible(moduleId: string) {
1707
- if (options[ClassName.complete]) return undefined
1708
- return (owner: string) => closure(moduleId).has(owner)
1709
- }
1710
-
1711
- function restore(moduleId: string, output: Transform.compile.ReturnType) {
1712
- return units.restore({
1713
- owner: moduleId,
1714
- used: output[ClassName.units] ?? new Map(),
1715
- visible: visible(moduleId),
1716
- })
1717
- }
1718
-
1719
- // Extraction visits dependencies first; their emitted classes must precede consumers.
1720
- for (const moduleId of extracted.keys()) {
1721
- const cached = transformed.get(moduleId)
1722
- const reusable =
1723
- cached &&
1724
- cached.extracted === extracted.get(moduleId) &&
1725
- cached.schemes === schemes &&
1726
- Object.keys(cached.themes).length === names.length &&
1727
- names.every((name) => cached.themes[name] === themes[name]) &&
1728
- Object.entries(cached.classes).every(
1729
- ([name, value]) => styleClasses[name] === value,
1730
- ) &&
1731
- cached.owners === ownerSignature &&
1732
- restore(moduleId, cached.output)
1733
- modules[moduleId] = reusable
1734
- ? cached.output
1735
- : sameThemes &&
1736
- previous!.schemes === schemes &&
1737
- extracted.get(moduleId) === previous!.extracted.get(moduleId) &&
1738
- restore(moduleId, previous!.result.modules[moduleId]!)
1739
- ? previous!.result.modules[moduleId]!
1740
- : Transform.compile({
1741
- [ClassName.units]: units.scope({
1742
- owner: moduleId,
1743
- qualifier: Identity.compact(moduleId).slice(-6),
1744
- visible: visible(moduleId),
1745
- }),
1746
- [ThemeRules.shared]:
1747
- options[Stylesheets.entry] === undefined
1748
- ? (options[ThemeRules.shared] ?? 'defaults')
1749
- : 'all',
1750
- compiler: options.compiler,
1751
- development: options.development,
1752
- composition: options.composition,
1753
- cssOutput: options.cssOutput,
1754
- moduleId,
1755
- schemes,
1756
- source: options.modules[moduleId]!,
1757
- [Themes.context]: {
1758
- parsed: parse({ moduleId, source: options.modules[moduleId]! }),
1759
- extracted: Object.freeze({
1760
- ...extracted.get(moduleId)!,
1761
- vars: sharedThemes,
1762
- contributions: undefined,
1763
- }),
1764
- links: {},
1765
- owners,
1766
- styleClasses,
1767
- },
1768
- })
1769
- transformed.delete(moduleId)
1770
- transformed.set(moduleId, {
1771
- classes: Object.fromEntries(
1772
- extracted
1773
- .get(moduleId)!
1774
- .calls.flatMap((call) =>
1775
- (call.runtimeComposition ?? []).map((input) => [
1776
- input.name,
1777
- styleClasses[input.name],
1778
- ]),
1779
- ),
1780
- ),
1781
- extracted: extracted.get(moduleId)!,
1782
- owners: ownerSignature,
1783
- output: modules[moduleId]!,
1784
- schemes,
1785
- themes: sharedThemes,
1786
- })
1787
- if (transformed.size > 256)
1788
- transformed.delete(transformed.keys().next().value!)
1789
- registerRules(modules[moduleId]![ClassName.rules], moduleId)
1790
-
1791
- for (const call of extracted.get(moduleId)!.calls)
1792
- if (
1793
- call.identity &&
1794
- !call.composition &&
1795
- !call.runtimeComposition &&
1796
- !call.compositionCase
159
+ result = Results.graph(
160
+ Results.decode<Results.Output>(text),
161
+ result,
162
+ decoded,
1797
163
  )
1798
- styleClasses[call.identity] = modules[moduleId]!.classes[call.name]!
1799
- }
1800
-
1801
- const defaults = new Set<string>()
1802
- const defaultRules = new Map<string, ThemeRules.Rule[]>()
1803
- const defaultSections: Stylesheets.Section[] = []
1804
- if (options[Stylesheets.entry] === undefined)
1805
- for (const [source, output] of Object.entries(modules))
1806
- for (const resource of output[ThemeRules.shared] ?? []) {
1807
- if (defaults.has(resource.id)) continue
1808
- defaults.add(resource.id)
1809
-
1810
- if (!resource.rules) {
1811
- defaultSections.push({
1812
- css: resource.css,
1813
- key: resource.id,
1814
- layers: [],
1815
- source,
1816
- })
1817
- continue
1818
- }
1819
-
1820
- const rules = defaultRules.get(source) ?? []
1821
- rules.push(...resource.rules)
1822
- defaultRules.set(source, rules)
164
+ // Hosts may update their records in place, so the sent text is copied.
165
+ sent = {
166
+ contracts: input.contracts && { ...input.contracts },
167
+ modules: { ...input.modules },
1823
168
  }
1824
169
 
1825
- const shared = defaults.size
1826
- ? renderShared([
1827
- ...sharedSections,
1828
- ...defaultSections,
1829
- ...[...defaultRules].map(([source, rules]) => ({
1830
- css: ThemeRules.render(rules),
1831
- key: 'theme-defaults',
1832
- layers: [],
1833
- source,
1834
- })),
1835
- ])
1836
- : contributions
1837
- const sharedCss = shared.css
1838
-
1839
- function publishedRules(
1840
- id: string,
1841
- visited = new Set<string>(),
1842
- ): Readonly<Record<string, string>> {
1843
- if (visited.has(id)) return {}
1844
- visited.add(id)
1845
- return Object.assign(
1846
- {},
1847
- ...(dependencies[id] ?? []).map((dependency) =>
1848
- publishedRules(dependency, visited),
1849
- ),
1850
- libraries[id]?.classRules,
1851
- modules[id]?.[ClassName.rules],
1852
- )
1853
- }
1854
-
1855
- function publishedStyle(link: Themes.Link): Themes.Link {
1856
- return {
1857
- ...link,
1858
- ...(link.style
1859
- ? {
1860
- style: {
1861
- ...link.style,
1862
- className: styleClasses[link.binding] ?? link.style.className,
1863
- },
1864
- }
1865
- : {}),
1866
- ...(link.members
1867
- ? {
1868
- members: Object.fromEntries(
1869
- Object.entries(link.members).map(([name, member]) => [
1870
- name,
1871
- publishedStyle(member),
1872
- ]),
1873
- ),
1874
- }
1875
- : {}),
1876
- }
1877
- }
1878
-
1879
- // A configuration whose module uses root controls or the script publishes its
1880
- // catalog, exported or not, so initialization restores selections it persists.
1881
- function configurations(id: string): readonly Contract.write.Configuration[] {
1882
- const module = extracted.get(id)!
1883
-
1884
- return module.themeCalls
1885
- .filter(
1886
- (call) =>
1887
- call.appearance &&
1888
- call.options &&
1889
- (module.themeAppearances?.includes(call.name) ||
1890
- module.themeScripts?.includes(call.name)),
1891
- )
1892
- .map((call) => {
1893
- const themeNames = call.options?.themes
1894
-
1895
- return {
1896
- identity:
1897
- sharedThemes[call.name]?.[Token.definition].contract[
1898
- Token.identity
1899
- ] ?? call.name,
1900
- ...(typeof call.options?.storageKey === 'string'
1901
- ? { storageKey: call.options.storageKey }
1902
- : {}),
1903
- themes:
1904
- themeNames && typeof themeNames === 'object'
1905
- ? Object.keys(themeNames)
1906
- : [],
1907
- }
1908
- })
1909
- }
1910
-
1911
- return {
1912
- compiler: options.compiler !== false,
1913
- shared: options[ThemeRules.shared],
1914
- composition: options.composition,
1915
- cssOutput: options.cssOutput,
1916
- contracts,
1917
- development: !!options.development,
1918
- extracted,
1919
- libraries: Object.freeze(libraries),
1920
- native: undefined,
1921
- reset: options.reset,
1922
- entry: options[Stylesheets.entry],
1923
- retained,
1924
- transformed,
1925
- resolutions: Object.freeze(resolutions),
1926
- result: Object.freeze({
1927
- ...(options[Stylesheets.entry] !== undefined &&
1928
- options.reset !== undefined
1929
- ? {
1930
- [Stylesheets.reset]: {
1931
- id: 'zyzz/reset.css',
1932
- ...Stylesheets.render([
1933
- {
1934
- content: options.reset,
1935
- css: options.reset,
1936
- layers: [['reset']],
1937
- source: 'zyzz/reset.css',
1938
- },
1939
- ]),
1940
- },
1941
- }
1942
- : {}),
1943
- ...(packed.length ? { [Stylesheets.packed]: packed } : {}),
1944
- ...(sharedCss
1945
- ? {
1946
- sharedCss,
1947
- sharedCssMap: shared.map,
1948
- sharedAssets: shared.assets,
1949
- sharedAssetOwners: shared.owners,
1950
- }
1951
- : {}),
1952
- contracts: Object.freeze(
1953
- Object.fromEntries(
1954
- (options[Stylesheets.entry] === undefined ? ids : [])
1955
- .filter(
1956
- (id) =>
1957
- Object.keys(extracted.get(id)!.themeExports ?? {}).length ||
1958
- reachable(id).length ||
1959
- configurations(id).length,
1960
- )
1961
- .map((id) => [
1962
- id,
1963
- Contract.write(
1964
- Object.fromEntries(
1965
- Object.entries(extracted.get(id)!.themeExports ?? {}).map(
1966
- ([name, link]) => [name, publishedStyle(link)],
1967
- ),
1968
- ),
1969
- sharedThemes,
1970
- reachable(id).map((section) => ({
1971
- ...section,
1972
- owner: undefined,
1973
- dependency:
1974
- section.owner && section.owner !== id
1975
- ? dependencyPath(id, section.owner)
1976
- : undefined,
1977
- source: Stylesheets.relative(
1978
- section.owner ?? id,
1979
- section.source,
1980
- ),
1981
- })),
1982
- id,
1983
- configurations(id),
1984
- publishedRules(id),
1985
- ),
1986
- ]),
1987
- ),
1988
- ),
1989
- dependencies: Object.freeze(dependencies),
1990
- modules: Object.freeze(
1991
- Object.fromEntries(ids.map((id) => [id, modules[id]!])),
1992
- ),
1993
- }),
1994
- schemes,
1995
- sources: Object.freeze({ ...options.modules }),
1996
- themes: sharedThemes,
1997
- }
1998
- }
1999
-
2000
- // A returned lazy map must not retain the graph compilation context.
2001
- function nativeOutput(
2002
- output: Native.compile.ReturnType,
2003
- ): Transform.compile.ReturnType {
2004
- let sourceMap: Mapping.EncodedSourceMap | undefined
2005
-
2006
- return Object.freeze({
2007
- [ClassName.rules]: Object.freeze({}),
2008
- classes: Object.freeze({}),
2009
- [Edits.key]: output[Edits.key],
2010
- code: output.code,
2011
- css: '',
2012
- cssMap: {
2013
- version: 3 as const,
2014
- names: [],
2015
- sources: [],
2016
- mappings: '',
170
+ return result
2017
171
  },
2018
- get map() {
2019
- return (sourceMap ??= JSON.parse(output.map) as Mapping.EncodedSourceMap)
2020
- },
2021
- vars: Object.freeze({}),
2022
172
  })
2023
173
  }
174
+
175
+ /** Incremental graph compiler contracts. */
176
+ export declare namespace create {
177
+ /** Explicitly owned compilation state; dropping the compiler releases its cache. */
178
+ type ReturnType = {
179
+ /** Compiles a complete source snapshot, reusing unaffected work across calls. */
180
+ readonly compile: typeof compile
181
+ }
182
+ }