zyzz 0.0.20 → 0.0.22

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 (298) hide show
  1. package/Zyzz.podspec +16 -0
  2. package/dist/Style.d.ts +10 -1
  3. package/dist/Style.d.ts.map +1 -1
  4. package/dist/Style.js +1 -5
  5. package/dist/Style.js.map +1 -1
  6. package/dist/babel/NativeJsx.d.ts.map +1 -1
  7. package/dist/babel/NativeJsx.js +204 -12
  8. package/dist/babel/NativeJsx.js.map +1 -1
  9. package/dist/babel/index.d.ts +6 -2
  10. package/dist/babel/index.d.ts.map +1 -1
  11. package/dist/babel/index.js +10 -1
  12. package/dist/babel/index.js.map +1 -1
  13. package/dist/compiler/Graph.d.ts +3 -0
  14. package/dist/compiler/Graph.d.ts.map +1 -1
  15. package/dist/compiler/Graph.js +108 -37
  16. package/dist/compiler/Graph.js.map +1 -1
  17. package/dist/compiler/Native.d.ts +6 -3
  18. package/dist/compiler/Native.d.ts.map +1 -1
  19. package/dist/compiler/Native.js +142 -24
  20. package/dist/compiler/Native.js.map +1 -1
  21. package/dist/compiler/Source.d.ts +6 -0
  22. package/dist/compiler/Source.d.ts.map +1 -1
  23. package/dist/compiler/Source.js +80 -11
  24. package/dist/compiler/Source.js.map +1 -1
  25. package/dist/compiler/Transform.d.ts +4 -1
  26. package/dist/compiler/Transform.d.ts.map +1 -1
  27. package/dist/compiler/Transform.js +32 -46
  28. package/dist/compiler/Transform.js.map +1 -1
  29. package/dist/compiler/internal/Catalogs.d.ts.map +1 -1
  30. package/dist/compiler/internal/Catalogs.js +6 -1
  31. package/dist/compiler/internal/Catalogs.js.map +1 -1
  32. package/dist/compiler/internal/Compositions.d.ts.map +1 -1
  33. package/dist/compiler/internal/Compositions.js +5 -0
  34. package/dist/compiler/internal/Compositions.js.map +1 -1
  35. package/dist/compiler/internal/Configurations.d.ts +2 -0
  36. package/dist/compiler/internal/Configurations.d.ts.map +1 -1
  37. package/dist/compiler/internal/Configurations.js +23 -4
  38. package/dist/compiler/internal/Configurations.js.map +1 -1
  39. package/dist/compiler/internal/Contract.d.ts +4 -1
  40. package/dist/compiler/internal/Contract.d.ts.map +1 -1
  41. package/dist/compiler/internal/Contract.js +97 -151
  42. package/dist/compiler/internal/Contract.js.map +1 -1
  43. package/dist/compiler/internal/Contributions.d.ts.map +1 -1
  44. package/dist/compiler/internal/Contributions.js +8 -4
  45. package/dist/compiler/internal/Contributions.js.map +1 -1
  46. package/dist/compiler/internal/Descriptors.d.ts.map +1 -1
  47. package/dist/compiler/internal/Descriptors.js +11 -2
  48. package/dist/compiler/internal/Descriptors.js.map +1 -1
  49. package/dist/compiler/internal/Dynamic.d.ts +1 -1
  50. package/dist/compiler/internal/Dynamic.d.ts.map +1 -1
  51. package/dist/compiler/internal/Dynamic.js +5 -5
  52. package/dist/compiler/internal/Dynamic.js.map +1 -1
  53. package/dist/compiler/internal/Names.d.ts +5 -0
  54. package/dist/compiler/internal/Names.d.ts.map +1 -0
  55. package/dist/compiler/internal/Names.js +37 -0
  56. package/dist/compiler/internal/Names.js.map +1 -0
  57. package/dist/compiler/internal/NativeBindings.d.ts +24 -26
  58. package/dist/compiler/internal/NativeBindings.d.ts.map +1 -1
  59. package/dist/compiler/internal/NativeBindings.js +61 -8
  60. package/dist/compiler/internal/NativeBindings.js.map +1 -1
  61. package/dist/compiler/internal/NativeMedia.d.ts +14 -0
  62. package/dist/compiler/internal/NativeMedia.d.ts.map +1 -0
  63. package/dist/compiler/internal/NativeMedia.js +287 -0
  64. package/dist/compiler/internal/NativeMedia.js.map +1 -0
  65. package/dist/compiler/internal/NativeVars.d.ts +28 -0
  66. package/dist/compiler/internal/NativeVars.d.ts.map +1 -0
  67. package/dist/compiler/internal/NativeVars.js +157 -0
  68. package/dist/compiler/internal/NativeVars.js.map +1 -0
  69. package/dist/compiler/internal/PackedStyles.d.ts +2 -0
  70. package/dist/compiler/internal/PackedStyles.d.ts.map +1 -1
  71. package/dist/compiler/internal/PackedStyles.js +2 -0
  72. package/dist/compiler/internal/PackedStyles.js.map +1 -1
  73. package/dist/compiler/internal/Selectors.d.ts +1 -1
  74. package/dist/compiler/internal/Selectors.d.ts.map +1 -1
  75. package/dist/compiler/internal/Selectors.js +5 -2
  76. package/dist/compiler/internal/Selectors.js.map +1 -1
  77. package/dist/compiler/internal/Stylesheets.js +2 -2
  78. package/dist/compiler/internal/Stylesheets.js.map +1 -1
  79. package/dist/compiler/internal/Themes.d.ts +22 -1
  80. package/dist/compiler/internal/Themes.d.ts.map +1 -1
  81. package/dist/compiler/internal/Themes.js +120 -4
  82. package/dist/compiler/internal/Themes.js.map +1 -1
  83. package/dist/compiler/internal/Vars.d.ts +3 -1
  84. package/dist/compiler/internal/Vars.d.ts.map +1 -1
  85. package/dist/compiler/internal/Vars.js +14 -3
  86. package/dist/compiler/internal/Vars.js.map +1 -1
  87. package/dist/default.d.ts +4 -4
  88. package/dist/default.d.ts.map +1 -1
  89. package/dist/default.js +9 -9
  90. package/dist/default.js.map +2 -2
  91. package/dist/default.js.zyzz.json +1 -1
  92. package/dist/internal/Authoring.d.ts.map +1 -1
  93. package/dist/internal/Authoring.js +8 -3
  94. package/dist/internal/Authoring.js.map +1 -1
  95. package/dist/internal/Binding.d.ts +4 -0
  96. package/dist/internal/Binding.d.ts.map +1 -1
  97. package/dist/internal/Binding.js +2 -0
  98. package/dist/internal/Binding.js.map +1 -1
  99. package/dist/internal/Completion.d.ts +1 -1
  100. package/dist/internal/Completion.d.ts.map +1 -1
  101. package/dist/internal/Configuration.d.ts +2 -2
  102. package/dist/internal/Configuration.d.ts.map +1 -1
  103. package/dist/internal/Configuration.js +4 -2
  104. package/dist/internal/Configuration.js.map +1 -1
  105. package/dist/internal/Identity.d.ts +3 -3
  106. package/dist/internal/Identity.d.ts.map +1 -1
  107. package/dist/internal/Identity.js +11 -9
  108. package/dist/internal/Identity.js.map +1 -1
  109. package/dist/internal/Targets.d.ts.map +1 -1
  110. package/dist/internal/Targets.js +2 -0
  111. package/dist/internal/Targets.js.map +1 -1
  112. package/dist/internal/Theme.js +2 -2
  113. package/dist/internal/Theme.js.map +1 -1
  114. package/dist/internal/Token.d.ts +3 -1
  115. package/dist/internal/Token.d.ts.map +1 -1
  116. package/dist/internal/Token.js +9 -2
  117. package/dist/internal/Token.js.map +1 -1
  118. package/dist/internal/Value.d.ts +7 -0
  119. package/dist/internal/Value.d.ts.map +1 -1
  120. package/dist/internal/Value.js.map +1 -1
  121. package/dist/internal/VariableSets.js +1 -1
  122. package/dist/internal/VariableSets.js.map +1 -1
  123. package/dist/metro/Graph.d.ts +22 -7
  124. package/dist/metro/Graph.d.ts.map +1 -1
  125. package/dist/metro/Graph.js +179 -42
  126. package/dist/metro/Graph.js.map +1 -1
  127. package/dist/metro/Watch.d.ts +28 -4
  128. package/dist/metro/Watch.d.ts.map +1 -1
  129. package/dist/metro/Watch.js +39 -7
  130. package/dist/metro/Watch.js.map +1 -1
  131. package/dist/metro/index.d.ts +15 -4
  132. package/dist/metro/index.d.ts.map +1 -1
  133. package/dist/metro/index.js +27 -0
  134. package/dist/metro/index.js.map +1 -1
  135. package/dist/metro/transformer.d.ts +5 -1
  136. package/dist/metro/transformer.d.ts.map +1 -1
  137. package/dist/metro/transformer.js +5 -3
  138. package/dist/metro/transformer.js.map +1 -1
  139. package/dist/node/Host.d.ts.map +1 -1
  140. package/dist/node/Host.js +2 -0
  141. package/dist/node/Host.js.map +1 -1
  142. package/dist/react-native/StyleSheet.d.ts +3 -2
  143. package/dist/react-native/StyleSheet.d.ts.map +1 -1
  144. package/dist/react-native/StyleSheet.js +17 -34
  145. package/dist/react-native/StyleSheet.js.map +1 -1
  146. package/dist/react-native/index.d.ts +1 -1
  147. package/dist/react-native/index.d.ts.map +1 -1
  148. package/dist/react-native/index.js +1 -1
  149. package/dist/react-native/index.js.map +1 -1
  150. package/dist/react-native/index.native.d.ts +5 -0
  151. package/dist/react-native/index.native.js +20 -0
  152. package/dist/react-native/index.native.js.map +7 -0
  153. package/dist/react-native/internal/Calculation.d.ts +7 -0
  154. package/dist/react-native/internal/Calculation.d.ts.map +1 -0
  155. package/dist/react-native/internal/Calculation.js +134 -0
  156. package/dist/react-native/internal/Calculation.js.map +1 -0
  157. package/dist/react-native/internal/Device.js +191 -0
  158. package/dist/react-native/internal/Device.js.map +7 -0
  159. package/dist/react-native/internal/NativeZyzz.js +7 -0
  160. package/dist/react-native/internal/NativeZyzz.js.map +7 -0
  161. package/dist/react-native/internal/Scalar.d.ts.map +1 -1
  162. package/dist/react-native/internal/Scalar.js +9 -2
  163. package/dist/react-native/internal/Scalar.js.map +1 -1
  164. package/dist/react-native/internal/Store.d.ts +32 -0
  165. package/dist/react-native/internal/Store.d.ts.map +1 -0
  166. package/dist/react-native/internal/Store.js +57 -0
  167. package/dist/react-native/internal/Store.js.map +1 -0
  168. package/dist/react-native/internal/Tokens.d.ts +21 -0
  169. package/dist/react-native/internal/Tokens.d.ts.map +1 -0
  170. package/dist/react-native/internal/Tokens.js +58 -0
  171. package/dist/react-native/internal/Tokens.js.map +1 -0
  172. package/dist/react-native/internal/Viewport.d.ts +6 -0
  173. package/dist/react-native/internal/Viewport.d.ts.map +1 -0
  174. package/dist/react-native/internal/Viewport.js +4 -0
  175. package/dist/react-native/internal/Viewport.js.map +1 -0
  176. package/dist/react-native/react.d.ts +74 -11
  177. package/dist/react-native/react.d.ts.map +1 -1
  178. package/dist/react-native/react.js +111 -12
  179. package/dist/react-native/react.js.map +1 -1
  180. package/dist/react-native/reanimated.d.ts +39 -0
  181. package/dist/react-native/reanimated.d.ts.map +1 -0
  182. package/dist/react-native/reanimated.js +76 -0
  183. package/dist/react-native/reanimated.js.map +1 -0
  184. package/dist/runtime/NativeContext.d.ts +10 -1
  185. package/dist/runtime/NativeContext.d.ts.map +1 -1
  186. package/dist/runtime/NativeContext.js +67 -14
  187. package/dist/runtime/NativeContext.js.map +1 -1
  188. package/dist/runtime/NativeDynamic.d.ts +3 -2
  189. package/dist/runtime/NativeDynamic.d.ts.map +1 -1
  190. package/dist/runtime/NativeDynamic.js +32 -7
  191. package/dist/runtime/NativeDynamic.js.map +1 -1
  192. package/dist/runtime/NativeStatic.d.ts +23 -0
  193. package/dist/runtime/NativeStatic.d.ts.map +1 -0
  194. package/dist/runtime/NativeStatic.js +106 -0
  195. package/dist/runtime/NativeStatic.js.map +1 -0
  196. package/dist/runtime/NativeVars.d.ts +53 -0
  197. package/dist/runtime/NativeVars.d.ts.map +1 -0
  198. package/dist/runtime/NativeVars.js +77 -0
  199. package/dist/runtime/NativeVars.js.map +1 -0
  200. package/dist/runtime/index.d.ts +4 -0
  201. package/dist/runtime/index.d.ts.map +1 -1
  202. package/dist/runtime/index.js +4 -0
  203. package/dist/runtime/index.js.map +1 -1
  204. package/dist/runtime/internal/NativeMedia.d.ts +25 -0
  205. package/dist/runtime/internal/NativeMedia.d.ts.map +1 -0
  206. package/dist/runtime/internal/NativeMedia.js +36 -0
  207. package/dist/runtime/internal/NativeMedia.js.map +1 -0
  208. package/dist/variable.js +1 -1
  209. package/dist/variable.js.map +1 -1
  210. package/dist/vite/index.d.ts.map +1 -1
  211. package/dist/vite/index.js +43 -47
  212. package/dist/vite/index.js.map +1 -1
  213. package/dist/web/Css.d.ts +7 -2
  214. package/dist/web/Css.d.ts.map +1 -1
  215. package/dist/web/Css.js +76 -57
  216. package/dist/web/Css.js.map +1 -1
  217. package/dist/web/internal/ClassName.d.ts +13 -2
  218. package/dist/web/internal/ClassName.d.ts.map +1 -1
  219. package/dist/web/internal/ClassName.js +17 -30
  220. package/dist/web/internal/ClassName.js.map +1 -1
  221. package/dist/web/internal/Themes.d.ts +3 -2
  222. package/dist/web/internal/Themes.d.ts.map +1 -1
  223. package/dist/web/internal/Themes.js +54 -28
  224. package/dist/web/internal/Themes.js.map +1 -1
  225. package/dist/web/page.d.ts +2 -0
  226. package/dist/web/page.d.ts.map +1 -1
  227. package/native/NativeZyzz.cpp +215 -0
  228. package/native/NativeZyzz.h +52 -0
  229. package/native/ZyzzSpecJSI.h +65 -0
  230. package/native/android/CMakeLists.txt +8 -0
  231. package/native/android/ZyzzSpec-generated.cpp +50 -0
  232. package/native/android/ZyzzSpec.h +31 -0
  233. package/native/ios/ZyzzModuleProvider.mm +13 -0
  234. package/package.json +39 -1
  235. package/react-native.config.ts +14 -0
  236. package/src/Style.ts +15 -9
  237. package/src/babel/NativeJsx.ts +305 -28
  238. package/src/babel/index.ts +21 -3
  239. package/src/compiler/Graph.ts +135 -46
  240. package/src/compiler/Native.ts +158 -26
  241. package/src/compiler/Source.ts +115 -10
  242. package/src/compiler/Transform.ts +42 -60
  243. package/src/compiler/internal/Catalogs.ts +9 -1
  244. package/src/compiler/internal/Compositions.ts +7 -0
  245. package/src/compiler/internal/Configurations.ts +27 -4
  246. package/src/compiler/internal/Contract.ts +130 -260
  247. package/src/compiler/internal/Contributions.ts +8 -6
  248. package/src/compiler/internal/Descriptors.ts +12 -8
  249. package/src/compiler/internal/Dynamic.ts +14 -9
  250. package/src/compiler/internal/Names.ts +48 -0
  251. package/src/compiler/internal/NativeBindings.ts +98 -15
  252. package/src/compiler/internal/NativeMedia.ts +342 -0
  253. package/src/compiler/internal/NativeVars.ts +228 -0
  254. package/src/compiler/internal/PackedStyles.ts +4 -0
  255. package/src/compiler/internal/Selectors.ts +6 -1
  256. package/src/compiler/internal/Stylesheets.ts +2 -2
  257. package/src/compiler/internal/Themes.ts +193 -3
  258. package/src/compiler/internal/Vars.ts +21 -2
  259. package/src/default.ts +9 -10
  260. package/src/internal/Authoring.ts +8 -2
  261. package/src/internal/Binding.ts +5 -0
  262. package/src/internal/Completion.ts +6 -3
  263. package/src/internal/Configuration.ts +7 -4
  264. package/src/internal/Identity.ts +14 -12
  265. package/src/internal/Targets.ts +2 -0
  266. package/src/internal/Theme.ts +2 -2
  267. package/src/internal/Token.ts +11 -1
  268. package/src/internal/Value.ts +20 -0
  269. package/src/internal/VariableSets.ts +1 -1
  270. package/src/metro/Graph.ts +235 -50
  271. package/src/metro/Watch.ts +95 -10
  272. package/src/metro/index.ts +62 -4
  273. package/src/metro/transformer.ts +21 -11
  274. package/src/node/Host.ts +3 -0
  275. package/src/react-native/StyleSheet.ts +28 -44
  276. package/src/react-native/index.native.ts +10 -0
  277. package/src/react-native/index.ts +1 -1
  278. package/src/react-native/internal/Calculation.ts +159 -0
  279. package/src/react-native/internal/Device.ts +231 -0
  280. package/src/react-native/internal/NativeZyzz.ts +20 -0
  281. package/src/react-native/internal/Scalar.ts +13 -2
  282. package/src/react-native/internal/Store.ts +78 -0
  283. package/src/react-native/internal/Tokens.ts +85 -0
  284. package/src/react-native/internal/Viewport.ts +8 -0
  285. package/src/react-native/react.ts +211 -19
  286. package/src/react-native/reanimated.ts +162 -0
  287. package/src/runtime/NativeContext.ts +88 -17
  288. package/src/runtime/NativeDynamic.ts +35 -10
  289. package/src/runtime/NativeStatic.ts +145 -0
  290. package/src/runtime/NativeVars.ts +189 -0
  291. package/src/runtime/index.ts +6 -0
  292. package/src/runtime/internal/NativeMedia.ts +63 -0
  293. package/src/variable.ts +1 -1
  294. package/src/vite/index.ts +52 -60
  295. package/src/web/Css.ts +123 -74
  296. package/src/web/internal/ClassName.ts +31 -36
  297. package/src/web/internal/Themes.ts +62 -39
  298. package/src/web/page.ts +2 -0
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) =>
@@ -1391,9 +1358,14 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
1391
1358
  await updateDiscovery(this.environment, file, change.event)
1392
1359
  },
1393
1360
  async hotUpdate({ file, modules, timestamp, type, server }) {
1394
- await updateDiscovery(this.environment, file, type)
1395
-
1396
1361
  const state = entries(this.environment)
1362
+ const previous = [...state.values()].map((entry) => ({
1363
+ code: entry.code,
1364
+ file: entry.file,
1365
+ imports: entry.imports,
1366
+ }))
1367
+
1368
+ await updateDiscovery(this.environment, file, type)
1397
1369
  const first = [...state.values()].find(
1398
1370
  (entry) => entry.files.has(file) || eager(file, this.environment),
1399
1371
  )
@@ -1417,17 +1389,41 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
1417
1389
  true,
1418
1390
  ).catch(() => undefined)
1419
1391
  : undefined
1420
- const affected = new Set(
1421
- modules.filter((module) => {
1422
- const entry = module.id && state.get(module.id)
1423
- return (
1424
- !output ||
1425
- !entry ||
1426
- entry.code !== output.modules[sourceId(entry.file)]?.code ||
1427
- entry.imports !==
1392
+ const javascriptChanged = await (async () => {
1393
+ if (!output) return true
1394
+
1395
+ // Portable build adapters share this module and do not require Vite.
1396
+ const { transformWithOxc } = await import('vite')
1397
+ const changed = await Promise.all(
1398
+ previous.map(async (entry) => {
1399
+ const code = output.modules[sourceId(entry.file)]?.code
1400
+ if (
1401
+ entry.imports !==
1428
1402
  JSON.stringify(output.imports[sourceId(entry.file)])
1429
- )
1430
- }),
1403
+ )
1404
+ return true
1405
+ if (entry.code === code) return false
1406
+ if (entry.code === undefined || code === undefined) return true
1407
+
1408
+ // Generated type assertions can change without changing runtime bindings.
1409
+ const [before, after] = await Promise.all([
1410
+ transformWithOxc(entry.code, entry.file, { sourcemap: false }),
1411
+ transformWithOxc(code, entry.file, { sourcemap: false }),
1412
+ ])
1413
+ return before.code !== after.code
1414
+ }),
1415
+ )
1416
+ return changed.some(Boolean)
1417
+ })()
1418
+ // Compiler-only inputs have watch edges, but their stylesheets update separately.
1419
+ const affected = new Set(
1420
+ modules.filter(
1421
+ (module) =>
1422
+ javascriptChanged ||
1423
+ (module.id
1424
+ ? !output?.modules[sourceId(module.id)]
1425
+ : module.type !== 'asset'),
1426
+ ),
1431
1427
  )
1432
1428
  const ids = new Set<string>()
1433
1429
  if (type === 'delete') ids.add(cssId(file))
@@ -1436,12 +1432,8 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
1436
1432
  if (!entry.files.has(file) && !eager(file, this.environment)) continue
1437
1433
 
1438
1434
  // Theme scopes affect CSS even when Vite's JavaScript import was erased.
1439
- if (
1440
- !output ||
1441
- entry.code !== output.modules[sourceId(entry.file)]?.code ||
1442
- entry.imports !== JSON.stringify(output.imports[sourceId(entry.file)])
1443
- )
1444
- ids.add(entry.file)
1435
+ // Rewritten importers can lose a source edge, so changed bindings must update together.
1436
+ if (javascriptChanged) ids.add(entry.file)
1445
1437
  ids.add(cssId(entry.file))
1446
1438
  ids.add(sharedId)
1447
1439
  }
package/src/web/Css.ts CHANGED
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import * as Targets from '../internal/Targets.js'
6
6
  import * as ClassName from './internal/ClassName.js'
7
+ import * as Identity from '../internal/Identity.js'
7
8
  import * as Contributions from './internal/Contributions.js'
8
9
  import * as Binding from '../internal/Binding.js'
9
10
  import * as Cascade from '../internal/Cascade.js'
@@ -33,6 +34,7 @@ export function compile<
33
34
  >(
34
35
  options: compile.Options<name, themeName>,
35
36
  ): compile.ReturnType<name, themeName> {
37
+ const labels = options[ClassName.labels] ?? options.styles[ClassName.labels]
36
38
  options = {
37
39
  ...options,
38
40
  styles: { styles: options.styles.styles.map(Targets.web) },
@@ -54,7 +56,18 @@ export function compile<
54
56
  return result
55
57
  }
56
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
+
57
69
  // Only primitive declaration lists can be interned without erasing token identity.
70
+ // Reuse their emitted bodies throughout analysis and output.
58
71
  const repeated = new Map<string, Style.NamedStyle>()
59
72
 
60
73
  const canonicalStyles = options.styles.styles.map((style) => {
@@ -66,13 +79,7 @@ export function compile<
66
79
  )
67
80
  return style
68
81
 
69
- const key = JSON.stringify(
70
- style.declarations.map(({ important, property, value }) => [
71
- property,
72
- value,
73
- important,
74
- ]),
75
- )
82
+ const key = nested(style)
76
83
  const previous = repeated.get(key)
77
84
  if (previous) return previous
78
85
 
@@ -267,8 +274,6 @@ export function compile<
267
274
  if (Object.hasOwn(Cascade.shorthands, property))
268
275
  join(property, domain(property))
269
276
 
270
- const serialized = new Map<object, string>()
271
-
272
277
  function serialize(input: Style.Declaration['value']): number | string {
273
278
  if (typeof input !== 'object' || input === null) return input
274
279
 
@@ -296,22 +301,32 @@ export function compile<
296
301
  return input as number | string
297
302
  }
298
303
 
299
- const declarations = new Map<Style.Declaration, string>()
300
- const propertyNames = new Map<string, string>()
301
-
302
304
  function declarationBody(declaration: Style.Declaration): string {
303
305
  const previous = declarations.get(declaration)
304
306
  if (previous !== undefined) return previous
307
+
305
308
  const { important, property, value } = declaration
306
- const name = propertyNames.get(property) ?? Literal.name(property)
307
- propertyNames.set(property, name)
308
- 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
+
309
326
  declarations.set(declaration, body)
310
327
  return body
311
328
  }
312
329
 
313
- const bodies = new Map<Style.NamedStyle, string>()
314
-
315
330
  function nested(style: Style.NamedStyle): string {
316
331
  const previous = bodies.get(style)
317
332
  if (previous !== undefined) return previous
@@ -399,11 +414,8 @@ export function compile<
399
414
  }
400
415
 
401
416
  function validate(style: Style.NamedStyle) {
402
- if (
403
- outputMode(style) !== undefined &&
404
- outputMode(style) !== 'atomic' &&
405
- outputMode(style) !== 'grouped'
406
- )
417
+ const mode = outputMode(style)
418
+ if (mode !== undefined && mode !== 'atomic' && mode !== 'grouped')
407
419
  throw new CompileError([
408
420
  {
409
421
  code: 'invalid_output',
@@ -417,7 +429,10 @@ export function compile<
417
429
 
418
430
  const identities = new Map<string, string>()
419
431
  const rules = new Map<string, string>()
420
- const identical = new Map<string, string>()
432
+ type ConditionalRule = { conditions: readonly string[]; value: string }
433
+ const conditionalRules = new Map<string, ConditionalRule>()
434
+ type SharedRules = Record<'atomic' | 'grouped', Map<string, string>>
435
+ const identical = new Map<string | undefined, SharedRules>()
421
436
  const explicitBodies = new Map<string, string>()
422
437
  const selectors = new Map<string, string>()
423
438
  const explicitModes = new Set<string>()
@@ -461,7 +476,7 @@ export function compile<
461
476
  explicit === undefined &&
462
477
  options.composition === 'independent' &&
463
478
  !options.development
464
- ? `${mode}:${JSON.stringify(representation(style))}:${nested(canonicalStyles[styleIndex]!)}`
479
+ ? `${mode}:${style.rules ? JSON.stringify(representation(style)) : ''}:${nested(canonicalStyles[styleIndex]!)}`
465
480
  : undefined
466
481
  if (application !== undefined && applications.has(application)) {
467
482
  classes[style.name] = applications.get(application)!
@@ -470,12 +485,23 @@ export function compile<
470
485
 
471
486
  const names: string[] = explicit === undefined ? [] : [explicit]
472
487
  let ordinal = 0
473
- const slots = new Map<string, number>()
474
- const developmentName = options.scope
475
- ? `definition-${styleIndex}`
476
- : style.name
488
+ const cssName = labels?.[style.name]?.name ?? encode(style.name)
489
+ const cssNamespace =
490
+ labels?.[style.name]?.namespace ??
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
+ }
477
497
 
478
- function emit(body: string, label: string, shared: boolean, output = mode) {
498
+ function emit(
499
+ body: string,
500
+ label: string,
501
+ shared: boolean,
502
+ output = mode,
503
+ conditional?: ConditionalRule,
504
+ ) {
479
505
  if (!body) return
480
506
 
481
507
  const independent =
@@ -483,13 +509,8 @@ export function compile<
483
509
  !options.development &&
484
510
  output === mode &&
485
511
  options.composition === 'independent'
486
- const key = `${output}:${body}`
487
- // Development slots belong to each style even when their initial values match.
488
- const reusable =
489
- explicit === undefined &&
490
- !options.development &&
491
- (shared || independent)
492
- const previous = reusable ? identical.get(key) : undefined
512
+ const reusable = explicit === undefined && (shared || independent)
513
+ const previous = reusable ? sharedRules![output].get(body) : undefined
493
514
  if (previous && !names.includes(previous)) {
494
515
  names.push(previous)
495
516
  return
@@ -498,43 +519,23 @@ export function compile<
498
519
  if (previous) shared = false
499
520
 
500
521
  // Contextual slots preserve authored ordering; development names survive value edits.
501
- const ordinalSlot = ordinal++
502
- const slot = options.development ? (slots.get(label) ?? 0) : ordinalSlot
503
- slots.set(label, slot + 1)
522
+ const slot = ordinal++
504
523
  const identity = (() => {
505
524
  if (explicit !== undefined)
506
525
  return `${explicit}-${mode}-${encode(label)}-${slot}`
507
- if (
508
- output === 'grouped' &&
509
- mode === 'grouped' &&
510
- !options.development &&
511
- options.composition === 'independent'
512
- )
513
- return `g_${rules.size.toString(36)}`
514
526
  if (output === 'grouped' && mode === 'grouped') {
515
- const name = options.development ? developmentName : style.name
516
- const scope =
517
- options.development && options.scope
518
- ? `_m${encode(options.scope)}`
519
- : ''
520
- return `g-${encode(name)}${scope}${slot ? `_s${slot}` : ''}`
527
+ return `z-${cssNamespace ? `${cssNamespace}-` : ''}${cssName}${slot ? `__${slot}` : ''}`
521
528
  }
522
529
  return ClassName.create({
523
530
  body,
524
- context:
525
- !shared || options.development
526
- ? JSON.stringify([
527
- options.scope,
528
- mode,
529
- options.development ? developmentName : style.name,
530
- ])
531
- : options.scope,
531
+ context: !shared ? cssName : undefined,
532
+ namespace: cssNamespace,
532
533
  property: label,
533
- slot: !shared || options.development ? slot : undefined,
534
- stable: options.development,
534
+ slot: !shared ? slot : undefined,
535
535
  })
536
536
  })()
537
- const owner = mode === 'atomic' ? `${style.name}:${slot}` : undefined
537
+ const owner =
538
+ mode === 'atomic' && !shared ? `${style.name}:${slot}` : undefined
538
539
  if (
539
540
  (rules.has(identity) && rules.get(identity) !== body) ||
540
541
  (owner !== undefined &&
@@ -543,15 +544,22 @@ export function compile<
543
544
  )
544
545
  diagnostics.push({
545
546
  code: 'identity_collision',
546
- message: 'Distinct rules produced the same class identifier.',
547
+ message:
548
+ 'Generated class names conflict. Supply distinct config or style ids.',
547
549
  path: [style.name],
548
550
  })
549
551
 
550
552
  if (owner !== undefined) identities.set(identity, owner)
551
553
  rules.set(identity, body)
554
+ if (
555
+ conditional?.conditions.some((condition) =>
556
+ /^@(media|supports)\b/.test(condition),
557
+ )
558
+ )
559
+ conditionalRules.set(identity, conditional)
552
560
  if (explicit !== undefined) selectors.set(identity, explicit)
553
561
  else {
554
- if (reusable) identical.set(key, identity)
562
+ if (reusable) sharedRules![output].set(body, identity)
555
563
  names.push(identity)
556
564
  }
557
565
  }
@@ -611,7 +619,7 @@ export function compile<
611
619
  !nestedComposition &&
612
620
  !conditions.length &&
613
621
  groups.get(conflict(property)) !== false
614
- emit(body, property, shared)
622
+ emit(body, property, shared, mode, { conditions, value })
615
623
  }
616
624
  }
617
625
 
@@ -673,7 +681,7 @@ export function compile<
673
681
  options.schemes ?? false,
674
682
  options[Themes.shared],
675
683
  )
676
- : { classes: Object.freeze({}), css: '', resources: [] }
684
+ : { classes: Object.freeze({}), css: '', resources: [], rules: [] }
677
685
  } catch (error) {
678
686
  throw new CompileError([
679
687
  {
@@ -686,20 +694,57 @@ export function compile<
686
694
 
687
695
  const scopedCss = [
688
696
  scopes.css,
689
- ...[...rules].map(
690
- ([name, body]) => `.${selectors.get(name) ?? name}{${body}}`,
691
- ),
697
+ ...[...rules].map((entry) => {
698
+ const [name, body] = entry
699
+ const selector = `.${ClassName.selector(selectors.get(name) ?? name)}`
700
+ const conditional = conditionalRules.get(name)
701
+ if (
702
+ !conditional ||
703
+ conditional.conditions.some(
704
+ (condition) =>
705
+ condition.startsWith('@') &&
706
+ !/^@(media|supports)\b/.test(condition),
707
+ )
708
+ )
709
+ return `${selector}{${body}}`
710
+
711
+ // Inactive conditions must surround the class rather than leave a matched empty wrapper.
712
+ const conditions = conditional.conditions.filter((condition) =>
713
+ condition.startsWith('@'),
714
+ )
715
+ const nested = conditional.conditions
716
+ .filter((condition) => !condition.startsWith('@'))
717
+ .reduceRight(
718
+ (body, condition) => `${condition}{${body}}`,
719
+ conditional.value,
720
+ )
721
+ return conditions.reduceRight(
722
+ (body, condition) => `${condition}{${body}}`,
723
+ `${selector}{${nested}}`,
724
+ )
725
+ }),
692
726
  ]
693
727
  .filter(Boolean)
694
728
  .join('\n')
695
729
 
696
- return Object.freeze({
730
+ const result = {
697
731
  ...(scopes.resources.length ? { [Themes.shared]: scopes.resources } : {}),
698
732
  ...(contributionCss ? { contributionCss, scopedCss } : {}),
733
+ [ClassName.rules]: Object.freeze({
734
+ ...Object.fromEntries(rules),
735
+ ...Object.fromEntries(
736
+ scopes.rules.map((rule) => [
737
+ `@scope:${rule.selector.slice(1)}:${Identity.name(rule.property)}:${Identity.name(JSON.stringify(rule.conditions))}`,
738
+ rule.value,
739
+ ]),
740
+ ),
741
+ }),
699
742
  classes: Object.freeze(classes),
700
743
  css: [contributionCss, scopedCss].filter(Boolean).join('\n'),
701
744
  vars: scopes.classes as Readonly<Record<themeName, string>>,
702
- })
745
+ }
746
+ Object.defineProperty(result, ClassName.rules, { enumerable: false })
747
+ return Object.freeze(result)
703
748
  }
704
749
 
705
750
  /** Input and output contracts for literal compilation. */
@@ -714,6 +759,8 @@ export declare namespace compile {
714
759
  > = {
715
760
  /** Separates generated token rules for hosts with independent CSS resources. */
716
761
  readonly [Themes.shared]?: 'all' | 'defaults' | undefined
762
+ /** Readable source bindings and consumer-owned configuration namespaces. */
763
+ readonly [ClassName.labels]?: ClassName.Labels | undefined
717
764
  /** Fixed class identities used by CSS-only consumers. */
718
765
  readonly names?: Readonly<Record<string, string>> | undefined
719
766
  /**
@@ -726,14 +773,14 @@ export declare namespace compile {
726
773
  /** CSS representation; atomic declarations are the default. */
727
774
  readonly cssOutput?: 'atomic' | 'grouped' | undefined
728
775
  readonly composition?: 'independent' | 'ordered' | undefined
729
- /** Stable declaration names for CSS-only development updates. */
776
+ /** Retains live definitions for development updates. */
730
777
  readonly development?: boolean | undefined
731
778
  /**
732
779
  * Emit the `color-scheme` selection classes. Set by modules whose runtime
733
780
  * helpers can apply a scheme, so lowered `light-dark()` resolves there.
734
781
  */
735
782
  readonly schemes?: boolean | undefined
736
- /** Optional module scope for independently delivered stylesheets. */
783
+ /** Explicit consumer-owned namespace for independently delivered stylesheets. */
737
784
  readonly scope?: string | undefined
738
785
  /** Ordered definitions; no themes or source adapter is required. */
739
786
  readonly styles: Style.Definition<name>
@@ -748,6 +795,8 @@ export declare namespace compile {
748
795
  name extends string = string,
749
796
  themeName extends string = string,
750
797
  > = {
798
+ /** Exact emitted rule bodies retained for graph-wide collision diagnostics. */
799
+ readonly [ClassName.rules]: Readonly<Record<string, string>>
751
800
  /** Shared token resources omitted from this module's stylesheet. */
752
801
  readonly [Themes.shared]?: readonly Themes.Resource[] | undefined
753
802
  /** Contribution text separated for graph-wide hoisting. */
@@ -17,22 +17,19 @@ export function create(options: create.Options): string {
17
17
  ? body.slice(prefix.length, -1)
18
18
  : ''
19
19
  const simple = /^[a-zA-Z0-9-]{1,24}$/.test(literal)
20
- const label =
20
+ const namespace = options.namespace ? `${options.namespace}-` : ''
21
+ if (options.context !== undefined)
22
+ return `z-${namespace}${options.context}-${encode(property)}${options.slot === undefined ? '' : `-${options.slot}`}`
23
+
24
+ const value = simple ? literal : `[${literal || body}]`
25
+ const readable =
21
26
  options.property === 'display' && displays.has(literal)
22
27
  ? literal
23
- : `${property.startsWith('--') ? encode(property).replaceAll('-', '_2d_') : property}${simple ? `-${literal}` : ''}`
24
-
25
- const suffix = (() => {
26
- if (options.context !== undefined)
27
- return `-${hash(options.stable || simple ? options.context : JSON.stringify([options.context, options.body]))}${options.slot === undefined ? '' : `-${options.slot}`}`
28
- if (!simple) return `-${hash(options.body)}`
29
- return ''
30
- })()
31
-
32
- if (options.stable) return `z-${encode(property)}${suffix}`
33
-
34
- // Custom properties and vendor spellings can contain identifier punctuation.
35
- return `z-${condition ? `${condition[1]}-` : ''}${encode(label)}${suffix}`
28
+ : property.startsWith('--')
29
+ ? `[${property}:${literal || body}]`
30
+ : `${property}-${value}`
31
+ // DOM class tokens cannot contain whitespace; preserve its spelling reversibly.
32
+ return `z-${namespace}${condition ? `${condition[1]}-` : ''}${readable.replace(/[\s_"'<>]/gu, (character) => `_${character.codePointAt(0)!.toString(16)}_`)}`
36
33
  }
37
34
 
38
35
  /** Atomic naming inputs; context retains declaration ordering and module ownership. */
@@ -43,12 +40,12 @@ export declare namespace create {
43
40
  readonly body: string
44
41
  /** Identity required when the declaration cannot share a global rule. */
45
42
  readonly context?: string | undefined
43
+ /** Optional consumer-owned configuration namespace. */
44
+ readonly namespace?: string | undefined
46
45
  /** Authoring property spelling. */
47
46
  readonly property: string
48
47
  /** Ordered declaration slot inside a contextual style. */
49
48
  readonly slot?: number | undefined
50
- /** Keep names independent of values for CSS-only development updates. */
51
- readonly stable?: boolean | undefined
52
49
  }
53
50
  }
54
51
 
@@ -87,26 +84,24 @@ function encode(value: string): string {
87
84
  )
88
85
  }
89
86
 
90
- // Six CSS identifier characters retain 36 bits from two deterministic streams.
91
- function hash(value: string): string {
92
- let first = 2166136261
93
- let second = 5381
94
-
95
- for (let index = 0; index < value.length; index++) {
96
- const code = value.charCodeAt(index)
97
- first = Math.imul(first ^ code, 16777619)
98
- second = Math.imul(second, 33) ^ code
99
- }
87
+ /** Escapes a generated class token for use as a CSS selector. */
88
+ export function selector(value: string): string {
89
+ return value.replace(
90
+ /[^a-zA-Z0-9_-]/gu,
91
+ (character) => `\\${character.codePointAt(0)!.toString(16)} `,
92
+ )
93
+ }
100
94
 
101
- const alphabet =
102
- '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-'
103
- let bits = (first >>> 0) * 16 + (second >>> 28)
104
- let result = ''
95
+ /** Carries exact emitted rule bodies for graph-wide collision checks. */
96
+ export const rules = Symbol('zyzz.css.rules')
105
97
 
106
- for (let index = 0; index < 6; index++) {
107
- result = alphabet[bits % 64]! + result
108
- bits = Math.floor(bits / 64)
109
- }
98
+ /** Carries authored names between source extraction and CSS emission. */
99
+ export const labels = Symbol('zyzz.css.labels')
110
100
 
111
- return result
112
- }
101
+ /** Authored names retained on extracted style data. */
102
+ export type Labels = Readonly<
103
+ Record<
104
+ string,
105
+ { readonly name: string; readonly namespace?: string | undefined }
106
+ >
107
+ >