zyzz 0.0.3 → 0.0.6

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 (273) hide show
  1. package/README.md +34 -19
  2. package/dist/Config.d.ts +16 -167
  3. package/dist/Config.d.ts.map +1 -1
  4. package/dist/Config.js +7 -246
  5. package/dist/Config.js.map +1 -1
  6. package/dist/Style.d.ts +15 -12
  7. package/dist/Style.d.ts.map +1 -1
  8. package/dist/Style.js +51 -26
  9. package/dist/Style.js.map +1 -1
  10. package/dist/Vars.d.ts +114 -0
  11. package/dist/Vars.d.ts.map +1 -0
  12. package/dist/Vars.js +42 -0
  13. package/dist/Vars.js.map +1 -0
  14. package/dist/babel/index.d.ts +2 -0
  15. package/dist/babel/index.d.ts.map +1 -1
  16. package/dist/babel/index.js +10 -1
  17. package/dist/babel/index.js.map +1 -1
  18. package/dist/compiler/Graph.d.ts +2 -0
  19. package/dist/compiler/Graph.d.ts.map +1 -1
  20. package/dist/compiler/Graph.js +30 -9
  21. package/dist/compiler/Graph.js.map +1 -1
  22. package/dist/compiler/Native.d.ts +5 -5
  23. package/dist/compiler/Native.d.ts.map +1 -1
  24. package/dist/compiler/Native.js +21 -21
  25. package/dist/compiler/Native.js.map +1 -1
  26. package/dist/compiler/Source.d.ts +8 -6
  27. package/dist/compiler/Source.d.ts.map +1 -1
  28. package/dist/compiler/Source.js +52 -16
  29. package/dist/compiler/Source.js.map +1 -1
  30. package/dist/compiler/Transform.d.ts +1 -1
  31. package/dist/compiler/Transform.d.ts.map +1 -1
  32. package/dist/compiler/Transform.js +58 -22
  33. package/dist/compiler/Transform.js.map +1 -1
  34. package/dist/compiler/internal/Compositions.d.ts +5 -0
  35. package/dist/compiler/internal/Compositions.d.ts.map +1 -1
  36. package/dist/compiler/internal/Compositions.js +22 -3
  37. package/dist/compiler/internal/Compositions.js.map +1 -1
  38. package/dist/compiler/internal/Configurations.d.ts.map +1 -1
  39. package/dist/compiler/internal/Configurations.js +73 -12
  40. package/dist/compiler/internal/Configurations.js.map +1 -1
  41. package/dist/compiler/internal/Contract.d.ts +1 -1
  42. package/dist/compiler/internal/Contract.d.ts.map +1 -1
  43. package/dist/compiler/internal/Contract.js +155 -13
  44. package/dist/compiler/internal/Contract.js.map +1 -1
  45. package/dist/compiler/internal/Contributions.d.ts +1 -1
  46. package/dist/compiler/internal/Contributions.d.ts.map +1 -1
  47. package/dist/compiler/internal/Contributions.js +6 -4
  48. package/dist/compiler/internal/Contributions.js.map +1 -1
  49. package/dist/compiler/internal/Dynamic.js +1 -1
  50. package/dist/compiler/internal/Dynamic.js.map +1 -1
  51. package/dist/compiler/internal/Expression.d.ts +3 -0
  52. package/dist/compiler/internal/Expression.d.ts.map +1 -1
  53. package/dist/compiler/internal/Expression.js +21 -0
  54. package/dist/compiler/internal/Expression.js.map +1 -1
  55. package/dist/compiler/internal/Identifiers.d.ts +1 -1
  56. package/dist/compiler/internal/Identifiers.d.ts.map +1 -1
  57. package/dist/compiler/internal/Identifiers.js +2 -2
  58. package/dist/compiler/internal/Identifiers.js.map +1 -1
  59. package/dist/compiler/internal/NativeBindings.d.ts +1 -1
  60. package/dist/compiler/internal/NativeBindings.d.ts.map +1 -1
  61. package/dist/compiler/internal/NativeBindings.js +3 -3
  62. package/dist/compiler/internal/NativeBindings.js.map +1 -1
  63. package/dist/compiler/internal/PackedStyles.d.ts +1 -1
  64. package/dist/compiler/internal/PackedStyles.d.ts.map +1 -1
  65. package/dist/compiler/internal/PackedStyles.js +15 -25
  66. package/dist/compiler/internal/PackedStyles.js.map +1 -1
  67. package/dist/compiler/internal/Recipes.js +1 -1
  68. package/dist/compiler/internal/Recipes.js.map +1 -1
  69. package/dist/compiler/internal/Selectors.js +1 -1
  70. package/dist/compiler/internal/Selectors.js.map +1 -1
  71. package/dist/compiler/internal/Static.d.ts.map +1 -1
  72. package/dist/compiler/internal/Static.js +2 -0
  73. package/dist/compiler/internal/Static.js.map +1 -1
  74. package/dist/compiler/internal/Themes.d.ts +12 -3
  75. package/dist/compiler/internal/Themes.d.ts.map +1 -1
  76. package/dist/compiler/internal/Themes.js +192 -52
  77. package/dist/compiler/internal/Themes.js.map +1 -1
  78. package/dist/compiler/internal/{Variables.d.ts → Vars.d.ts} +1 -1
  79. package/dist/compiler/internal/Vars.d.ts.map +1 -0
  80. package/dist/compiler/internal/{Variables.js → Vars.js} +3 -3
  81. package/dist/compiler/internal/Vars.js.map +1 -0
  82. package/dist/default.d.ts +147 -2562
  83. package/dist/default.d.ts.map +1 -1
  84. package/dist/default.js +85 -80
  85. package/dist/default.js.map +2 -2
  86. package/dist/default.js.zyzz.json +1 -1
  87. package/dist/esbuild/index.d.ts +3 -0
  88. package/dist/esbuild/index.d.ts.map +1 -0
  89. package/dist/esbuild/index.js +5 -0
  90. package/dist/esbuild/index.js.map +1 -0
  91. package/dist/index.d.ts +4 -4
  92. package/dist/index.d.ts.map +1 -1
  93. package/dist/index.js +4 -4
  94. package/dist/index.js.map +1 -1
  95. package/dist/internal/Authoring.d.ts +1 -1
  96. package/dist/internal/Authoring.d.ts.map +1 -1
  97. package/dist/internal/Authoring.js +4 -4
  98. package/dist/internal/Authoring.js.map +1 -1
  99. package/dist/internal/Binding.d.ts +1 -1
  100. package/dist/internal/Binding.d.ts.map +1 -1
  101. package/dist/internal/Completion.d.ts +19 -0
  102. package/dist/internal/Completion.d.ts.map +1 -0
  103. package/dist/internal/Completion.js +2 -0
  104. package/dist/internal/Completion.js.map +1 -0
  105. package/dist/internal/Condition.d.ts +3 -1
  106. package/dist/internal/Condition.d.ts.map +1 -1
  107. package/dist/internal/Condition.js.map +1 -1
  108. package/dist/internal/Configuration.d.ts +248 -0
  109. package/dist/internal/Configuration.d.ts.map +1 -0
  110. package/dist/internal/Configuration.js +343 -0
  111. package/dist/internal/Configuration.js.map +1 -0
  112. package/dist/{Theme.d.ts → internal/Theme.d.ts} +15 -13
  113. package/dist/internal/Theme.d.ts.map +1 -0
  114. package/dist/{Theme.js → internal/Theme.js} +39 -12
  115. package/dist/internal/Theme.js.map +1 -0
  116. package/dist/internal/Token.d.ts +46 -16
  117. package/dist/internal/Token.d.ts.map +1 -1
  118. package/dist/internal/Token.js +89 -5
  119. package/dist/internal/Token.js.map +1 -1
  120. package/dist/internal/Typography.d.ts +13 -5
  121. package/dist/internal/Typography.d.ts.map +1 -1
  122. package/dist/internal/Typography.js +42 -6
  123. package/dist/internal/Typography.js.map +1 -1
  124. package/dist/internal/Value.d.ts +6 -1
  125. package/dist/internal/Value.d.ts.map +1 -1
  126. package/dist/internal/Value.js.map +1 -1
  127. package/dist/internal/VariableSets.d.ts +20 -0
  128. package/dist/internal/VariableSets.d.ts.map +1 -0
  129. package/dist/internal/VariableSets.js +317 -0
  130. package/dist/internal/VariableSets.js.map +1 -0
  131. package/dist/next/index.d.ts +8 -3
  132. package/dist/next/index.d.ts.map +1 -1
  133. package/dist/next/index.js +21 -10
  134. package/dist/next/index.js.map +1 -1
  135. package/dist/next/loader.d.ts +1 -0
  136. package/dist/next/loader.d.ts.map +1 -1
  137. package/dist/next/loader.js +25 -6
  138. package/dist/next/loader.js.map +1 -1
  139. package/dist/node/Host.d.ts.map +1 -1
  140. package/dist/node/Host.js +1 -1
  141. package/dist/node/Host.js.map +1 -1
  142. package/dist/node/Reset.d.ts +3 -0
  143. package/dist/node/Reset.d.ts.map +1 -0
  144. package/dist/node/Reset.js +7 -0
  145. package/dist/node/Reset.js.map +1 -0
  146. package/dist/react-native/Host.d.ts +2 -2
  147. package/dist/react-native/Host.d.ts.map +1 -1
  148. package/dist/react-native/Host.js +4 -4
  149. package/dist/react-native/Host.js.map +1 -1
  150. package/dist/react-native/StyleSheet.d.ts +9 -8
  151. package/dist/react-native/StyleSheet.d.ts.map +1 -1
  152. package/dist/react-native/StyleSheet.js +29 -23
  153. package/dist/react-native/StyleSheet.js.map +1 -1
  154. package/dist/react-native/Variants.d.ts +3 -3
  155. package/dist/react-native/Variants.d.ts.map +1 -1
  156. package/dist/react-native/Variants.js +2 -2
  157. package/dist/react-native/react.d.ts +1 -1
  158. package/dist/react-native/react.d.ts.map +1 -1
  159. package/dist/react-native/react.js +5 -5
  160. package/dist/react-native/react.js.map +1 -1
  161. package/dist/rollup/index.d.ts +3 -0
  162. package/dist/rollup/index.d.ts.map +1 -0
  163. package/dist/rollup/index.js +5 -0
  164. package/dist/rollup/index.js.map +1 -0
  165. package/dist/runtime/Appearance.d.ts +7 -7
  166. package/dist/runtime/Appearance.d.ts.map +1 -1
  167. package/dist/runtime/Appearance.js +15 -15
  168. package/dist/runtime/Appearance.js.map +1 -1
  169. package/dist/runtime/Dynamic.d.ts.map +1 -1
  170. package/dist/runtime/Dynamic.js +4 -1
  171. package/dist/runtime/Dynamic.js.map +1 -1
  172. package/dist/runtime/NativeContext.d.ts +4 -4
  173. package/dist/runtime/NativeContext.d.ts.map +1 -1
  174. package/dist/runtime/NativeContext.js +5 -5
  175. package/dist/runtime/NativeContext.js.map +1 -1
  176. package/dist/runtime/NativeDynamic.d.ts +1 -1
  177. package/dist/runtime/NativeDynamic.d.ts.map +1 -1
  178. package/dist/runtime/NativeDynamic.js.map +1 -1
  179. package/dist/runtime/Props.d.ts +1 -1
  180. package/dist/runtime/Props.js +3 -3
  181. package/dist/runtime/Props.js.map +1 -1
  182. package/dist/runtime/Selection.d.ts +14 -10
  183. package/dist/runtime/Selection.d.ts.map +1 -1
  184. package/dist/runtime/Selection.js +19 -18
  185. package/dist/runtime/Selection.js.map +1 -1
  186. package/dist/styleFunction.d.ts +14 -14
  187. package/dist/styleFunction.d.ts.map +1 -1
  188. package/dist/styleFunction.js.map +1 -1
  189. package/dist/unplugin/index.d.ts +22 -0
  190. package/dist/unplugin/index.d.ts.map +1 -0
  191. package/dist/unplugin/index.js +500 -0
  192. package/dist/unplugin/index.js.map +1 -0
  193. package/dist/variants.d.ts +3 -3
  194. package/dist/variants.d.ts.map +1 -1
  195. package/dist/variants.js.map +1 -1
  196. package/dist/vite/index.d.ts +2 -0
  197. package/dist/vite/index.d.ts.map +1 -1
  198. package/dist/vite/index.js +44 -12
  199. package/dist/vite/index.js.map +1 -1
  200. package/dist/web/Css.d.ts +4 -3
  201. package/dist/web/Css.d.ts.map +1 -1
  202. package/dist/web/Css.js +4 -4
  203. package/dist/web/Css.js.map +1 -1
  204. package/dist/web/internal/Themes.d.ts +3 -2
  205. package/dist/web/internal/Themes.d.ts.map +1 -1
  206. package/dist/web/internal/Themes.js +42 -6
  207. package/dist/web/internal/Themes.js.map +1 -1
  208. package/dist/webpack/index.d.ts +3 -0
  209. package/dist/webpack/index.d.ts.map +1 -0
  210. package/dist/webpack/index.js +5 -0
  211. package/dist/webpack/index.js.map +1 -0
  212. package/package.json +23 -2
  213. package/src/Config.ts +43 -685
  214. package/src/Style.ts +97 -34
  215. package/src/Vars.ts +299 -0
  216. package/src/babel/index.ts +23 -1
  217. package/src/compiler/Graph.ts +36 -9
  218. package/src/compiler/Native.ts +29 -32
  219. package/src/compiler/Source.ts +64 -22
  220. package/src/compiler/Transform.ts +73 -23
  221. package/src/compiler/internal/Compositions.ts +36 -3
  222. package/src/compiler/internal/Configurations.ts +83 -11
  223. package/src/compiler/internal/Contract.ts +207 -15
  224. package/src/compiler/internal/Contributions.ts +6 -3
  225. package/src/compiler/internal/Dynamic.ts +1 -1
  226. package/src/compiler/internal/Expression.ts +26 -0
  227. package/src/compiler/internal/Identifiers.ts +2 -1
  228. package/src/compiler/internal/NativeBindings.ts +3 -3
  229. package/src/compiler/internal/PackedStyles.ts +22 -26
  230. package/src/compiler/internal/Recipes.ts +1 -1
  231. package/src/compiler/internal/Selectors.ts +1 -1
  232. package/src/compiler/internal/Static.ts +1 -0
  233. package/src/compiler/internal/Themes.ts +266 -57
  234. package/src/compiler/internal/{Variables.ts → Vars.ts} +2 -2
  235. package/src/default.ts +173 -154
  236. package/src/esbuild/index.ts +5 -0
  237. package/src/index.ts +4 -4
  238. package/src/internal/Authoring.ts +5 -7
  239. package/src/internal/Binding.ts +1 -1
  240. package/src/internal/Completion.ts +42 -0
  241. package/src/internal/Condition.ts +31 -1
  242. package/src/internal/Configuration.ts +970 -0
  243. package/src/{Theme.ts → internal/Theme.ts} +75 -20
  244. package/src/internal/Token.ts +258 -85
  245. package/src/internal/Typography.ts +71 -16
  246. package/src/internal/Value.ts +45 -26
  247. package/src/internal/VariableSets.ts +458 -0
  248. package/src/next/index.ts +34 -12
  249. package/src/next/loader.ts +35 -6
  250. package/src/node/Host.ts +1 -2
  251. package/src/node/Reset.ts +7 -0
  252. package/src/react-native/Host.ts +6 -6
  253. package/src/react-native/StyleSheet.ts +49 -42
  254. package/src/react-native/Variants.ts +3 -3
  255. package/src/react-native/react.ts +6 -6
  256. package/src/rollup/index.ts +5 -0
  257. package/src/runtime/Appearance.ts +20 -20
  258. package/src/runtime/Dynamic.ts +4 -1
  259. package/src/runtime/NativeContext.ts +8 -8
  260. package/src/runtime/NativeDynamic.ts +2 -4
  261. package/src/runtime/Props.ts +3 -3
  262. package/src/runtime/Selection.ts +44 -33
  263. package/src/styleFunction.ts +22 -19
  264. package/src/unplugin/index.ts +604 -0
  265. package/src/variants.ts +7 -5
  266. package/src/vite/index.ts +48 -18
  267. package/src/web/Css.ts +10 -7
  268. package/src/web/internal/Themes.ts +52 -9
  269. package/src/webpack/index.ts +5 -0
  270. package/dist/Theme.d.ts.map +0 -1
  271. package/dist/Theme.js.map +0 -1
  272. package/dist/compiler/internal/Variables.d.ts.map +0 -1
  273. package/dist/compiler/internal/Variables.js.map +0 -1
@@ -0,0 +1,604 @@
1
+ /** Connects the static compiler to portable bundler hooks and the Vite adapter. @module */
2
+ import * as Mapping from '@jridgewell/gen-mapping'
3
+ import * as Crypto from 'node:crypto'
4
+ import * as Fs from 'node:fs/promises'
5
+ import * as Path from 'node:path'
6
+ import * as Reset from '../node/Reset.js'
7
+ import * as Parser from 'oxc-parser'
8
+ import { createUnplugin } from 'unplugin'
9
+ import type { UnpluginBuildContext } from 'unplugin'
10
+ import * as Graph from '../compiler/Graph.js'
11
+ import * as AtRules from '../compiler/internal/AtRules.js'
12
+ import * as Catalogs from '../compiler/internal/Catalogs.js'
13
+ import * as Contract from '../compiler/internal/Contract.js'
14
+ import * as Vite from '../vite/index.js'
15
+
16
+ /** Portable build configuration. Vite uses the options from `zyzz/vite` instead. */
17
+ export type Options = {
18
+ /** Rewrite authoring calls. Defaults to true. */
19
+ readonly compiler?: boolean | undefined
20
+ /** Include the CSS reset. Defaults to false. */
21
+ readonly reset?: boolean | undefined
22
+ /** Source directory scanned for modules and global contributions. Defaults to the working directory. */
23
+ readonly root?: string | undefined
24
+ }
25
+
26
+ const excludedPattern = /\.(?:d|test|test-d|bench|bench-d)\.[cm]?[jt]sx?$/
27
+ const sourcePattern = /\.[cm]?[jt]sx?$/
28
+
29
+ const portable = createUnplugin<Options | undefined, false>(
30
+ (options = {}, meta) => {
31
+ const root = Path.resolve(options.root ?? process.cwd())
32
+ const compiler = Graph.create()
33
+ let graph: Graph.compile.ReturnType | undefined
34
+ let pending: Promise<void> | undefined
35
+ let files = new Set<string>()
36
+ let directories = new Set<string>()
37
+ let sources = new Map<string, string>()
38
+ let assets = new Map<string, Uint8Array>()
39
+ let css = ''
40
+ let cssMap = ''
41
+ let script = ''
42
+
43
+ function identity(file: string) {
44
+ return `app/${Path.relative(root, file).split(Path.sep).join('/')}`
45
+ }
46
+
47
+ function emit(context: UnpluginBuildContext) {
48
+ if (!graph) return
49
+ for (const [fileName, source] of assets)
50
+ context.emitFile({
51
+ type: 'asset',
52
+ fileName,
53
+ source: Uint8Array.from(source),
54
+ })
55
+ context.emitFile({
56
+ type: 'asset',
57
+ fileName: 'zyzz.css',
58
+ source: `${css}/*# sourceMappingURL=zyzz.css.map */\n`,
59
+ })
60
+ context.emitFile({
61
+ type: 'asset',
62
+ fileName: 'zyzz.css.map',
63
+ source: cssMap,
64
+ })
65
+ context.emitFile({
66
+ type: 'asset',
67
+ fileName: 'zyzz.js',
68
+ source: script || '/* No saved theme selections. */\n',
69
+ })
70
+ }
71
+
72
+ function transform(code: string, id: string) {
73
+ const output = graph?.modules[identity(id)]
74
+ if (!output) return
75
+ if (code !== sources.get(id))
76
+ throw new Error(`Zyzz must run before source transforms: ${id}`)
77
+ return {
78
+ code: output.code,
79
+ map: JSON.stringify({
80
+ ...output.map,
81
+ sources: output.map.sources.map((source) =>
82
+ source?.startsWith('app/')
83
+ ? Path.join(root, source.slice(4))
84
+ : source,
85
+ ),
86
+ }),
87
+ }
88
+ }
89
+
90
+ return {
91
+ buildEnd:
92
+ meta.framework === 'webpack'
93
+ ? undefined
94
+ : function () {
95
+ emit(this)
96
+ },
97
+ async buildStart() {
98
+ pending = (async () => {
99
+ graph = undefined
100
+ files = new Set()
101
+ directories = new Set()
102
+ sources = new Map()
103
+ assets = new Map()
104
+ const modules: Record<string, string> = Object.create(null)
105
+ const imports: Record<
106
+ string,
107
+ Record<string, string | null>
108
+ > = Object.create(null)
109
+ const contracts: Record<string, string> = Object.create(null)
110
+
111
+ async function scan(directory: string): Promise<void> {
112
+ directories.add(directory)
113
+ const entries = await Fs.readdir(directory, { withFileTypes: true })
114
+
115
+ for (const entry of entries.sort((a, b) =>
116
+ a.name < b.name ? -1 : a.name > b.name ? 1 : 0,
117
+ )) {
118
+ if (
119
+ entry.name.startsWith('.') ||
120
+ [
121
+ 'node_modules',
122
+ 'dist',
123
+ 'build',
124
+ 'coverage',
125
+ 'test',
126
+ 'tests',
127
+ '__tests__',
128
+ 'fixtures',
129
+ '__fixtures__',
130
+ ].includes(entry.name)
131
+ )
132
+ continue
133
+ const file = Path.join(directory, entry.name)
134
+
135
+ if (entry.isDirectory()) await scan(file)
136
+ else if (
137
+ entry.isFile() &&
138
+ sourcePattern.test(file) &&
139
+ !excludedPattern.test(file)
140
+ ) {
141
+ const source = await Fs.readFile(file, 'utf8')
142
+
143
+ files.add(file)
144
+ sources.set(file, source)
145
+ modules[identity(file)] = source
146
+ }
147
+ }
148
+ }
149
+
150
+ const resolve = async (specifier: string, importer: string) => {
151
+ if ('resolve' in this && typeof this.resolve === 'function') {
152
+ const result = await (
153
+ this as UnpluginBuildContext & {
154
+ resolve: (
155
+ id: string,
156
+ importer: string,
157
+ ) => Promise<{
158
+ id: string
159
+ external?: boolean | 'absolute' | 'relative'
160
+ } | null>
161
+ }
162
+ ).resolve(specifier, importer)
163
+ return result && !result.external ? result.id : undefined
164
+ }
165
+
166
+ const native = this.getNativeBuildContext?.()
167
+ if (native?.framework === 'esbuild') {
168
+ const result = await native.build.resolve(specifier, {
169
+ importer,
170
+ kind: 'import-statement',
171
+ resolveDir: Path.dirname(importer),
172
+ })
173
+ if (result.errors.length)
174
+ throw new Error(
175
+ result.errors.map((error) => error.text).join('\n'),
176
+ )
177
+ return result.external ? undefined : result.path
178
+ }
179
+ if (native?.framework === 'webpack') {
180
+ const resolver = native.compiler.resolverFactory.get('normal', {
181
+ dependencyType: 'esm',
182
+ })
183
+ return new Promise<string | undefined>((accept, reject) => {
184
+ resolver.resolve(
185
+ {},
186
+ Path.dirname(importer),
187
+ specifier,
188
+ {},
189
+ (error, result) => {
190
+ if (error) reject(error)
191
+ else accept(result || undefined)
192
+ },
193
+ )
194
+ })
195
+ }
196
+ throw new Error('The bundler does not expose module resolution.')
197
+ }
198
+
199
+ async function contract(
200
+ file: string,
201
+ required = false,
202
+ ): Promise<boolean> {
203
+ if (Object.hasOwn(contracts, file)) return true
204
+ const sidecar = `${file}.zyzz.json`
205
+
206
+ files.add(sidecar)
207
+ try {
208
+ contracts[file] = await Fs.readFile(sidecar, 'utf8')
209
+ } catch (error) {
210
+ if (
211
+ !required &&
212
+ (error as NodeJS.ErrnoException).code === 'ENOENT'
213
+ )
214
+ return false
215
+
216
+ throw error
217
+ }
218
+ const metadata = Contract.read(contracts[file]!, new Map(), file)
219
+ for (const section of metadata.stylesheets) {
220
+ let owner = file
221
+ for (const specifier of section.dependency ?? []) {
222
+ const target = await resolve(specifier, owner)
223
+ if (!target)
224
+ throw new Error(
225
+ `Unable to resolve packed stylesheet dependency: ${specifier}`,
226
+ )
227
+ ;(imports[owner] ??= Object.create(null))[specifier] = target
228
+ await contract(target, true)
229
+ owner = target
230
+ }
231
+ }
232
+ return true
233
+ }
234
+
235
+ await scan(root)
236
+ for (const [file, source] of sources) {
237
+ const links = (imports[identity(file)] = Object.create(null))
238
+ for (const node of Parser.parseSync(file, source, {
239
+ sourceType: 'module',
240
+ }).program.body) {
241
+ if (
242
+ (node.type !== 'ImportDeclaration' &&
243
+ node.type !== 'ExportNamedDeclaration' &&
244
+ node.type !== 'ExportAllDeclaration') ||
245
+ !node.source
246
+ )
247
+ continue
248
+ if (
249
+ node.type === 'ImportDeclaration'
250
+ ? node.importKind === 'type'
251
+ : node.exportKind === 'type'
252
+ )
253
+ continue
254
+ if (
255
+ 'specifiers' in node &&
256
+ node.specifiers.length &&
257
+ node.specifiers.every((specifier) =>
258
+ specifier.type === 'ImportSpecifier'
259
+ ? specifier.importKind === 'type'
260
+ : specifier.type === 'ExportSpecifier' &&
261
+ specifier.exportKind === 'type',
262
+ )
263
+ )
264
+ continue
265
+
266
+ const specifier = node.source.value
267
+ links[specifier] = null
268
+ if (
269
+ specifier === 'zyzz' ||
270
+ (specifier.startsWith('zyzz/') &&
271
+ specifier !== 'zyzz/default') ||
272
+ /^(?:[a-z][a-z\d+.-]*:|\/\/)/i.test(specifier) ||
273
+ specifier.includes('?')
274
+ )
275
+ continue
276
+ const target = await resolve(specifier, file)
277
+ if (!target || !sourcePattern.test(target)) continue
278
+ files.add(target)
279
+ if (sources.has(target)) links[specifier] = identity(target)
280
+ else if (await contract(target)) links[specifier] = target
281
+ }
282
+ }
283
+
284
+ const result = compiler.compile({
285
+ compiler: options.compiler,
286
+ reset: options.reset ? Reset.read() : undefined,
287
+ contracts,
288
+ imports,
289
+ modules,
290
+ })
291
+ const copied = new Map<string, string>()
292
+ const owners = new Map<string, string>()
293
+
294
+ async function owner(id: string): Promise<string> {
295
+ if (id.startsWith('app/')) return Fs.realpath(root)
296
+ const cached = owners.get(id)
297
+ if (cached) return cached
298
+
299
+ let directory = Path.dirname(id)
300
+ for (;;) {
301
+ try {
302
+ await Fs.access(Path.join(directory, 'package.json'))
303
+ const real = await Fs.realpath(directory)
304
+ owners.set(id, real)
305
+ return real
306
+ } catch (error) {
307
+ if ((error as NodeJS.ErrnoException).code !== 'ENOENT')
308
+ throw error
309
+ const parent = Path.dirname(directory)
310
+ if (parent === directory)
311
+ throw new Error(
312
+ 'Packed assets require an owning package.json.',
313
+ )
314
+ directory = parent
315
+ }
316
+ }
317
+ }
318
+
319
+ async function copy(file: string, boundary: string): Promise<string> {
320
+ const real = await Fs.realpath(file)
321
+ if (!inside(boundary, real))
322
+ throw new Error('Asset path escapes its owning package.')
323
+ files.add(real)
324
+ const cached = copied.get(real)
325
+ if (cached) return cached
326
+
327
+ const content = await Fs.readFile(real)
328
+ const name = `zyzz-assets/${Crypto.hash('sha256', real + '\0' + content.toString('base64')).slice(0, 16)}${Path.extname(real)}`
329
+ copied.set(real, name)
330
+ if (real.endsWith('.css')) {
331
+ const processed = await stylesheet(
332
+ content.toString(),
333
+ undefined,
334
+ real,
335
+ boundary,
336
+ Path.posix.dirname(name),
337
+ )
338
+ assets.set(name, processed.code)
339
+ } else assets.set(name, content)
340
+ return name
341
+ }
342
+
343
+ async function stylesheet(
344
+ source: string,
345
+ map: string | undefined,
346
+ file: string,
347
+ boundary: string,
348
+ outputDirectory = '.',
349
+ targets?: Readonly<Record<string, string>>,
350
+ ) {
351
+ const urls = new Set<string>()
352
+ AtRules.transform({
353
+ code: Buffer.from(source),
354
+ filename: file,
355
+ visitor: {
356
+ Rule(rule) {
357
+ if (rule.type === 'import') urls.add(rule.value.url)
358
+ },
359
+ Url(url) {
360
+ urls.add(url.url)
361
+ },
362
+ },
363
+ })
364
+ const replacements = new Map<string, string>()
365
+ for (const url of urls) {
366
+ const target = targets?.[url]
367
+ if (
368
+ !target &&
369
+ (!url || /^(?:[a-z][a-z\d+.-]*:|[/?#])/i.test(url))
370
+ )
371
+ continue
372
+ const raw = (target ?? url).split(/[?#]/)[0]!
373
+ const path = (() => {
374
+ if (!target)
375
+ return Path.resolve(
376
+ Path.dirname(file),
377
+ decodeURIComponent(raw),
378
+ )
379
+ if (raw.startsWith('app/'))
380
+ return Path.join(root, decodeURIComponent(raw.slice(4)))
381
+ return decodeURIComponent(raw)
382
+ })()
383
+ const assetOwner = target
384
+ ? await owner(result.sharedAssetOwners?.[url] ?? 'app/')
385
+ : boundary
386
+ const name = await copy(path, assetOwner)
387
+ replacements.set(
388
+ url,
389
+ Path.posix.relative(outputDirectory, name) +
390
+ (target ?? url).slice(raw.length),
391
+ )
392
+ }
393
+ return AtRules.transform({
394
+ code: Buffer.from(source),
395
+ filename: file,
396
+ ...(map === undefined
397
+ ? {}
398
+ : { inputSourceMap: map, sourceMap: true }),
399
+ visitor: {
400
+ Rule(rule) {
401
+ if (rule.type !== 'import') return
402
+ const url = replacements.get(rule.value.url)
403
+ if (url) return AtRules.relocateImport(rule.value, url)
404
+ },
405
+ Url(url) {
406
+ const target = replacements.get(url.url)
407
+ if (target) return { ...url, url: target }
408
+ },
409
+ },
410
+ })
411
+ }
412
+
413
+ const shared = await stylesheet(
414
+ result.sharedCss ?? '',
415
+ JSON.stringify(
416
+ result.sharedCssMap ??
417
+ Mapping.toEncodedMap(new Mapping.GenMapping()),
418
+ ),
419
+ Path.join(root, 'zyzz.css'),
420
+ await Fs.realpath(root),
421
+ '.',
422
+ result.sharedAssets,
423
+ )
424
+ const chunks = [
425
+ {
426
+ code: Buffer.from(shared.code).toString(),
427
+ map: Buffer.from(shared.map!).toString(),
428
+ },
429
+ ]
430
+ const seen = new Set<string>()
431
+ async function visit(id: string): Promise<void> {
432
+ if (seen.has(id)) return
433
+ seen.add(id)
434
+ for (const dependency of result.dependencies[id] ?? [])
435
+ await visit(dependency)
436
+ const output = result.modules[id]
437
+ if (output) {
438
+ const processed = await stylesheet(
439
+ output.css,
440
+ JSON.stringify(output.cssMap),
441
+ Path.join(root, id.slice(4)),
442
+ await Fs.realpath(root),
443
+ )
444
+ chunks.push({
445
+ code: Buffer.from(processed.code).toString(),
446
+ map: Buffer.from(processed.map!).toString(),
447
+ })
448
+ }
449
+ }
450
+ const ids = Object.keys(result.modules)
451
+ const imported = new Set(Object.values(result.dependencies).flat())
452
+ for (const id of ids) if (!imported.has(id)) await visit(id)
453
+ for (const id of ids) await visit(id)
454
+
455
+ const map = new Mapping.GenMapping({ file: 'zyzz.css' })
456
+ let line = 0
457
+ css = ''
458
+ for (const chunk of chunks) {
459
+ if (!chunk.code) continue
460
+ const traced = Mapping.fromMap(chunk.map)
461
+ for (const mapping of Mapping.allMappings(traced)) {
462
+ const generated = {
463
+ line: mapping.generated.line + line,
464
+ column: mapping.generated.column,
465
+ }
466
+ if (
467
+ mapping.source !== undefined &&
468
+ mapping.original !== undefined
469
+ ) {
470
+ const location = {
471
+ generated,
472
+ original: mapping.original,
473
+ source: mapping.source.startsWith('app/')
474
+ ? Path.join(root, mapping.source.slice(4))
475
+ : mapping.source,
476
+ }
477
+ if (mapping.name === undefined)
478
+ Mapping.addMapping(map, location)
479
+ else
480
+ Mapping.addMapping(map, { ...location, name: mapping.name })
481
+ } else Mapping.addMapping(map, { generated })
482
+ }
483
+ const encoded = Mapping.toEncodedMap(traced)
484
+ encoded.sources.forEach((source, index) => {
485
+ if (source !== null)
486
+ Mapping.setSourceContent(
487
+ map,
488
+ source.startsWith('app/')
489
+ ? Path.join(root, source.slice(4))
490
+ : source,
491
+ encoded.sourcesContent?.[index] ?? null,
492
+ )
493
+ })
494
+ css += chunk.code + '\n'
495
+ line += chunk.code.split('\n').length
496
+ }
497
+ cssMap = JSON.stringify(Mapping.toEncodedMap(map))
498
+ script = Catalogs.scripts(
499
+ Object.values({ ...contracts, ...result.contracts }).flatMap(
500
+ Catalogs.read,
501
+ ),
502
+ ).join('\n')
503
+ graph = result
504
+ if (meta.framework === 'webpack') emit(this)
505
+ if (meta.framework === 'rollup')
506
+ for (const file of [...files, ...directories])
507
+ this.addWatchFile(file)
508
+ })()
509
+ await pending
510
+ },
511
+ enforce: 'pre',
512
+ esbuild: {
513
+ config(options) {
514
+ if (!options.outdir || options.write === false)
515
+ throw new Error(
516
+ 'Zyzz requires esbuild outdir and write: true to emit CSS assets.',
517
+ )
518
+ options.outdir = Path.resolve(
519
+ options.absWorkingDir ?? process.cwd(),
520
+ options.outdir,
521
+ )
522
+ },
523
+ setup(build) {
524
+ // Unplugin forwards watchFiles but not watchDirs from its transform hook.
525
+ build.onLoad({ filter: sourcePattern }, async ({ path }) => {
526
+ await pending
527
+ if (!sources.has(path)) return
528
+ const output = transform(await Fs.readFile(path, 'utf8'), path)
529
+ if (!output) return
530
+ return {
531
+ contents: `${output.code}\n//# sourceMappingURL=data:application/json;base64,${Buffer.from(output.map).toString('base64')}`,
532
+ loader: (() => {
533
+ if (path.endsWith('.tsx')) return 'tsx'
534
+ if (path.endsWith('.jsx')) return 'jsx'
535
+ if (/\.[cm]?ts$/.test(path)) return 'ts'
536
+ return 'js'
537
+ })(),
538
+ resolveDir: Path.dirname(path),
539
+ watchDirs: [...directories],
540
+ watchFiles: [...files],
541
+ }
542
+ })
543
+ },
544
+ },
545
+ name: 'zyzz',
546
+ rollup: {
547
+ shouldTransformCachedModule() {
548
+ return true
549
+ },
550
+ },
551
+ transform: {
552
+ filter: {
553
+ id: {
554
+ include: sourcePattern,
555
+ exclude:
556
+ /(?:^|[/\\])node_modules[/\\]|\.(?:d|test|test-d|bench|bench-d)\.[cm]?[jt]sx?$/,
557
+ },
558
+ },
559
+ async handler(code, id) {
560
+ await pending
561
+ const output = transform(code, id)
562
+ if (!output) return
563
+ for (const file of files) this.addWatchFile(file)
564
+ const native = this.getNativeBuildContext?.()
565
+ for (const directory of directories) {
566
+ if (native?.framework === 'webpack')
567
+ native.loaderContext?.addContextDependency(directory)
568
+ else if (meta.framework === 'rollup') this.addWatchFile(directory)
569
+ }
570
+ return output
571
+ },
572
+ },
573
+ webpack(compiler) {
574
+ compiler.hooks.watchRun.tap('zyzz', () => {
575
+ // Directory invalidations must also discard cached source reads before the graph and loaders rebuild.
576
+ for (const file of compiler.modifiedFiles ?? [])
577
+ if (directories.has(file)) compiler.inputFileSystem?.purge?.(file)
578
+ })
579
+ },
580
+ }
581
+ },
582
+ )
583
+
584
+ /** Bundler factories. Vite retains its native CSS delivery and environment-specific HMR. */
585
+ export const zyzz = {
586
+ /** Creates an esbuild plugin that emits stylesheets and watches source directories. */
587
+ esbuild: portable.esbuild,
588
+ /** Creates a Rollup plugin with dependency-aware cached transforms. */
589
+ rollup: portable.rollup,
590
+ /** Creates the existing Vite plugin with automatic CSS delivery and HMR. */
591
+ vite: Vite.zyzz,
592
+ /** Creates a Webpack plugin that compiles before language loaders. */
593
+ webpack: portable.webpack,
594
+ }
595
+
596
+ function inside(parent: string, child: string) {
597
+ const relative = Path.relative(parent, child)
598
+ return (
599
+ relative === '' ||
600
+ (relative !== '..' &&
601
+ !relative.startsWith(`..${Path.sep}`) &&
602
+ !Path.isAbsolute(relative))
603
+ )
604
+ }
package/src/variants.ts CHANGED
@@ -6,7 +6,7 @@ import type * as Binding from './internal/Binding.js'
6
6
  import type * as Condition from './internal/Condition.js'
7
7
  import type * as Shorthands from './internal/Shorthands.js'
8
8
  import type * as Style from './Style.js'
9
- import type * as Theme from './Theme.js'
9
+ import type * as Theme from './internal/Theme.js'
10
10
 
11
11
  type Axes<definition> = definition extends { readonly variants: infer axes }
12
12
  ? axes
@@ -62,8 +62,10 @@ type Keys<value> = value extends unknown ? keyof value : never
62
62
  type Selections<axes> = {
63
63
  readonly [axis in keyof axes]?: Choice<axes[axis]> | null | undefined
64
64
  }
65
- type Input<definition> = Selections<Axes<definition>> &
66
- style.Options &
65
+ type Input<definition, output extends style.Output> = Selections<
66
+ Axes<definition>
67
+ > &
68
+ style.Options<output> &
67
69
  (keyof Conditions<definition> extends never
68
70
  ? {}
69
71
  : {
@@ -191,10 +193,10 @@ export declare namespace variants {
191
193
 
192
194
  /** Callable selection; omitted values use defaults and null suppresses them. */
193
195
  type ReturnType<definition, output extends style.Output = 'react'> = <
194
- const input extends Input<definition> = Input<definition>,
196
+ const input extends Input<definition, output> = Input<definition, output>,
195
197
  >(
196
198
  input?: input &
197
- Record<Exclude<Keys<input>, keyof Input<definition>>, never>,
199
+ Record<Exclude<Keys<input>, keyof Input<definition, output>>, never>,
198
200
  ) => style.Props<output> & {
199
201
  readonly [axis in keyof Axes<definition> as `data-${axis & string}`]?:
200
202
  | string