zyzz 0.0.19 → 0.0.21

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 (167) hide show
  1. package/README.md +9 -9
  2. package/dist/Config.d.ts +1 -0
  3. package/dist/Config.d.ts.map +1 -1
  4. package/dist/Config.js.map +1 -1
  5. package/dist/Style.d.ts +7 -0
  6. package/dist/Style.d.ts.map +1 -1
  7. package/dist/Style.js +1 -5
  8. package/dist/Style.js.map +1 -1
  9. package/dist/Vars.d.ts +22 -15
  10. package/dist/Vars.d.ts.map +1 -1
  11. package/dist/Vars.js +26 -0
  12. package/dist/Vars.js.map +1 -1
  13. package/dist/cli/index.js +5 -0
  14. package/dist/cli/index.js.map +1 -1
  15. package/dist/compiler/Graph.d.ts +3 -0
  16. package/dist/compiler/Graph.d.ts.map +1 -1
  17. package/dist/compiler/Graph.js +44 -37
  18. package/dist/compiler/Graph.js.map +1 -1
  19. package/dist/compiler/Source.d.ts +4 -0
  20. package/dist/compiler/Source.d.ts.map +1 -1
  21. package/dist/compiler/Source.js +66 -6
  22. package/dist/compiler/Source.js.map +1 -1
  23. package/dist/compiler/Transform.d.ts +4 -1
  24. package/dist/compiler/Transform.d.ts.map +1 -1
  25. package/dist/compiler/Transform.js +32 -46
  26. package/dist/compiler/Transform.js.map +1 -1
  27. package/dist/compiler/internal/Catalogs.d.ts.map +1 -1
  28. package/dist/compiler/internal/Catalogs.js +6 -1
  29. package/dist/compiler/internal/Catalogs.js.map +1 -1
  30. package/dist/compiler/internal/Compositions.d.ts.map +1 -1
  31. package/dist/compiler/internal/Compositions.js +5 -0
  32. package/dist/compiler/internal/Compositions.js.map +1 -1
  33. package/dist/compiler/internal/Configurations.d.ts.map +1 -1
  34. package/dist/compiler/internal/Configurations.js +5 -1
  35. package/dist/compiler/internal/Configurations.js.map +1 -1
  36. package/dist/compiler/internal/Contract.d.ts +4 -1
  37. package/dist/compiler/internal/Contract.d.ts.map +1 -1
  38. package/dist/compiler/internal/Contract.js +73 -138
  39. package/dist/compiler/internal/Contract.js.map +1 -1
  40. package/dist/compiler/internal/Contributions.d.ts.map +1 -1
  41. package/dist/compiler/internal/Contributions.js +1 -3
  42. package/dist/compiler/internal/Contributions.js.map +1 -1
  43. package/dist/compiler/internal/Dynamic.d.ts +1 -1
  44. package/dist/compiler/internal/Dynamic.d.ts.map +1 -1
  45. package/dist/compiler/internal/Dynamic.js +5 -5
  46. package/dist/compiler/internal/Dynamic.js.map +1 -1
  47. package/dist/compiler/internal/Names.d.ts +5 -0
  48. package/dist/compiler/internal/Names.d.ts.map +1 -0
  49. package/dist/compiler/internal/Names.js +37 -0
  50. package/dist/compiler/internal/Names.js.map +1 -0
  51. package/dist/compiler/internal/Selectors.d.ts +1 -1
  52. package/dist/compiler/internal/Selectors.d.ts.map +1 -1
  53. package/dist/compiler/internal/Selectors.js +5 -2
  54. package/dist/compiler/internal/Selectors.js.map +1 -1
  55. package/dist/compiler/internal/Stylesheets.js +2 -2
  56. package/dist/compiler/internal/Stylesheets.js.map +1 -1
  57. package/dist/compiler/internal/Themes.d.ts.map +1 -1
  58. package/dist/compiler/internal/Themes.js +85 -24
  59. package/dist/compiler/internal/Themes.js.map +1 -1
  60. package/dist/compiler/internal/Vars.d.ts +2 -0
  61. package/dist/compiler/internal/Vars.d.ts.map +1 -1
  62. package/dist/compiler/internal/Vars.js +7 -1
  63. package/dist/compiler/internal/Vars.js.map +1 -1
  64. package/dist/default.d.ts +11 -15
  65. package/dist/default.d.ts.map +1 -1
  66. package/dist/default.js +13 -14
  67. package/dist/default.js.map +2 -2
  68. package/dist/default.js.zyzz.json +1 -1
  69. package/dist/index.d.ts +15 -0
  70. package/dist/index.d.ts.map +1 -1
  71. package/dist/index.js +15 -0
  72. package/dist/index.js.map +1 -1
  73. package/dist/internal/Authoring.d.ts.map +1 -1
  74. package/dist/internal/Authoring.js +8 -3
  75. package/dist/internal/Authoring.js.map +1 -1
  76. package/dist/internal/Binding.d.ts +4 -0
  77. package/dist/internal/Binding.d.ts.map +1 -1
  78. package/dist/internal/Binding.js +2 -0
  79. package/dist/internal/Binding.js.map +1 -1
  80. package/dist/internal/Configuration.d.ts +2 -2
  81. package/dist/internal/Configuration.d.ts.map +1 -1
  82. package/dist/internal/Configuration.js +4 -2
  83. package/dist/internal/Configuration.js.map +1 -1
  84. package/dist/internal/Identity.d.ts +3 -3
  85. package/dist/internal/Identity.d.ts.map +1 -1
  86. package/dist/internal/Identity.js +11 -9
  87. package/dist/internal/Identity.js.map +1 -1
  88. package/dist/internal/Theme.js +2 -2
  89. package/dist/internal/Theme.js.map +1 -1
  90. package/dist/internal/Token.d.ts +16 -3
  91. package/dist/internal/Token.d.ts.map +1 -1
  92. package/dist/internal/Token.js +11 -2
  93. package/dist/internal/Token.js.map +1 -1
  94. package/dist/internal/VariableSets.d.ts.map +1 -1
  95. package/dist/internal/VariableSets.js +19 -2
  96. package/dist/internal/VariableSets.js.map +1 -1
  97. package/dist/node/Host.d.ts +2 -0
  98. package/dist/node/Host.d.ts.map +1 -1
  99. package/dist/node/Host.js +9 -0
  100. package/dist/node/Host.js.map +1 -1
  101. package/dist/react-native/StyleSheet.js +2 -0
  102. package/dist/react-native/StyleSheet.js.map +1 -1
  103. package/dist/runtime/Composition.d.ts +1 -1
  104. package/dist/runtime/ConditionalRecipe.d.ts +1 -1
  105. package/dist/runtime/PayloadRecipe.d.ts +1 -1
  106. package/dist/runtime/Recipe.d.ts +1 -1
  107. package/dist/styleFunction.d.ts +1 -1
  108. package/dist/styleFunction.d.ts.map +1 -1
  109. package/dist/styleFunction.js.map +1 -1
  110. package/dist/variable.js +1 -1
  111. package/dist/variable.js.map +1 -1
  112. package/dist/variants.d.ts +14 -6
  113. package/dist/variants.d.ts.map +1 -1
  114. package/dist/variants.js.map +1 -1
  115. package/dist/vite/index.d.ts +2 -0
  116. package/dist/vite/index.d.ts.map +1 -1
  117. package/dist/vite/index.js +111 -42
  118. package/dist/vite/index.js.map +1 -1
  119. package/dist/web/Css.d.ts +7 -2
  120. package/dist/web/Css.d.ts.map +1 -1
  121. package/dist/web/Css.js +46 -39
  122. package/dist/web/Css.js.map +1 -1
  123. package/dist/web/internal/ClassName.d.ts +13 -2
  124. package/dist/web/internal/ClassName.d.ts.map +1 -1
  125. package/dist/web/internal/ClassName.js +17 -30
  126. package/dist/web/internal/ClassName.js.map +1 -1
  127. package/dist/web/internal/Themes.d.ts +3 -0
  128. package/dist/web/internal/Themes.d.ts.map +1 -1
  129. package/dist/web/internal/Themes.js +52 -24
  130. package/dist/web/internal/Themes.js.map +1 -1
  131. package/package.json +1 -1
  132. package/src/Config.ts +5 -0
  133. package/src/Style.ts +4 -1
  134. package/src/Vars.ts +88 -29
  135. package/src/cli/index.ts +5 -0
  136. package/src/compiler/Graph.ts +60 -46
  137. package/src/compiler/Source.ts +86 -5
  138. package/src/compiler/Transform.ts +42 -60
  139. package/src/compiler/internal/Catalogs.ts +9 -1
  140. package/src/compiler/internal/Compositions.ts +7 -0
  141. package/src/compiler/internal/Configurations.ts +5 -1
  142. package/src/compiler/internal/Contract.ts +104 -239
  143. package/src/compiler/internal/Contributions.ts +1 -5
  144. package/src/compiler/internal/Dynamic.ts +14 -9
  145. package/src/compiler/internal/Names.ts +48 -0
  146. package/src/compiler/internal/Selectors.ts +6 -1
  147. package/src/compiler/internal/Stylesheets.ts +2 -2
  148. package/src/compiler/internal/Themes.ts +115 -35
  149. package/src/compiler/internal/Vars.ts +9 -1
  150. package/src/default.ts +17 -20
  151. package/src/index.ts +15 -0
  152. package/src/internal/Authoring.ts +9 -3
  153. package/src/internal/Binding.ts +5 -0
  154. package/src/internal/Configuration.ts +7 -4
  155. package/src/internal/Identity.ts +14 -12
  156. package/src/internal/Theme.ts +2 -2
  157. package/src/internal/Token.ts +52 -12
  158. package/src/internal/VariableSets.ts +26 -2
  159. package/src/node/Host.ts +22 -0
  160. package/src/react-native/StyleSheet.ts +6 -0
  161. package/src/styleFunction.ts +1 -3
  162. package/src/variable.ts +1 -1
  163. package/src/variants.ts +38 -16
  164. package/src/vite/index.ts +137 -51
  165. package/src/web/Css.ts +83 -49
  166. package/src/web/internal/ClassName.ts +31 -36
  167. package/src/web/internal/Themes.ts +79 -29
@@ -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
+ >
@@ -10,8 +10,6 @@ import type * as Theme from '../../internal/Theme.js'
10
10
 
11
11
  /** Collects live token references within one in-memory compilation graph. */
12
12
  export function create() {
13
- let nextScope = 0
14
- let nextVariable = 0
15
13
  const contracts = new Map<
16
14
  Token.Contract,
17
15
  { identity: string | undefined; paths: Map<string, string> }
@@ -35,14 +33,15 @@ export function create() {
35
33
  let name = contract.paths.get(token.path)
36
34
 
37
35
  if (!name) {
38
- name =
39
- contract.identity === undefined
40
- ? `--z${nextVariable++}`
41
- : variable(contract.identity, token.path)
36
+ name = variable(
37
+ contract.identity,
38
+ token.path,
39
+ token.contract.variableNaming,
40
+ )
42
41
  contract.paths.set(token.path, name)
43
42
  }
44
43
 
45
- const label = contract.identity?.startsWith('src-') ? token.path : undefined
44
+ const label = token.path
46
45
  return `var(${name},${literal(value, label, token.contract)})`
47
46
  }
48
47
 
@@ -52,6 +51,7 @@ export function create() {
52
51
  separate?: 'all' | 'defaults',
53
52
  ) {
54
53
  const classes: Record<string, string> = Object.create(null)
54
+ const scopeBodies = new Map<string, string>()
55
55
  const completeDefaults = new Set<Token.Contract>()
56
56
  const empty: string[] = []
57
57
  const rules: Rule[] = []
@@ -68,10 +68,14 @@ export function create() {
68
68
 
69
69
  // Anonymous contracts are graph-local. Source-owned contracts retain stable
70
70
  // identities across separately compiled components and theme scopes.
71
- const className =
72
- data.contract[Token.identity] === undefined
73
- ? `t_${nextScope++}`
74
- : `z_theme-${encode(name)}`
71
+ const className = scope(
72
+ data.cssName ??
73
+ (data.contract[Token.identity] &&
74
+ name.startsWith(`${data.contract[Token.identity]}-`)
75
+ ? name.slice(data.contract[Token.identity]!.length + 1)
76
+ : name),
77
+ data.contract[Token.identity],
78
+ )
75
79
 
76
80
  classes[name] = className
77
81
 
@@ -89,9 +93,7 @@ export function create() {
89
93
 
90
94
  contract.paths.set(
91
95
  path,
92
- contract.identity === undefined
93
- ? `--z${nextVariable++}`
94
- : variable(contract.identity, path),
96
+ variable(contract.identity, path, data.contract.variableNaming),
95
97
  )
96
98
  }
97
99
 
@@ -99,15 +101,12 @@ export function create() {
99
101
  }
100
102
 
101
103
  if (!contract) continue
104
+
102
105
  // Independently compiled consumers must import identical fallback contents.
103
106
  if (separate === 'all' && contract.identity) {
104
107
  completeDefaults.add(data.contract)
105
108
  for (const [path, value] of Object.entries(data.values))
106
- literal(
107
- value,
108
- contract.identity.startsWith('src-') ? path : undefined,
109
- data.contract,
110
- )
109
+ literal(value, path, data.contract)
111
110
  }
112
111
  if (!contract.paths.size) empty.push(`.${className}{}`)
113
112
 
@@ -119,13 +118,35 @@ export function create() {
119
118
  if (value === undefined)
120
119
  throw new Error('Theme scope is missing a live token.')
121
120
 
122
- const label = contract.identity?.startsWith('src-') ? path : undefined
121
+ const label = path
123
122
  declarations.push({
124
123
  conditions: [],
125
124
  property: name,
126
125
  selector: `.${className}`,
127
126
  value: literal(value, label, data.contract),
128
127
  })
128
+ // Standalone styles have no contract metadata to select their original spelling.
129
+ if (
130
+ contract.identity?.startsWith('id-') &&
131
+ data.contract.variableNaming === undefined
132
+ )
133
+ declarations.push({
134
+ conditions: [],
135
+ property: variable(
136
+ `id-${Array.from(
137
+ contract.identity
138
+ .slice(3)
139
+ .replace(/_([0-9a-f]+)_/g, (_, code: string) =>
140
+ String.fromCodePoint(Number.parseInt(code, 16)),
141
+ ),
142
+ (character) => character.codePointAt(0)!.toString(16),
143
+ ).join('-')}`,
144
+ path,
145
+ 'legacy',
146
+ ),
147
+ selector: `.${className}`,
148
+ value: `var(${name})`,
149
+ })
129
150
  }
130
151
 
131
152
  rules.push(...declarations)
@@ -135,12 +156,26 @@ export function create() {
135
156
  name,
136
157
  `.${className}`,
137
158
  rules,
138
- contract.identity?.startsWith('src-') ? path : undefined,
159
+ path,
139
160
  [],
140
161
  data.contract,
141
162
  )
142
163
  }
143
164
 
165
+ for (const rule of rules) {
166
+ const key = JSON.stringify([
167
+ rule.selector,
168
+ rule.conditions,
169
+ rule.property,
170
+ ])
171
+ const previous = scopeBodies.get(key)
172
+ if (previous !== undefined && previous !== rule.value)
173
+ throw new Error(
174
+ `Generated scope ${rule.selector.slice(1)} conflicts. Supply distinct config ids.`,
175
+ )
176
+ scopeBodies.set(key, rule.value)
177
+ }
178
+
144
179
  const scopes = new Map<string, Rule[]>()
145
180
  for (const rule of rules) {
146
181
  const entries = scopes.get(rule.property) ?? []
@@ -188,6 +223,7 @@ export function create() {
188
223
  ]
189
224
  .filter(Boolean)
190
225
  .join('\n'),
226
+ rules,
191
227
  resources: separate
192
228
  ? [
193
229
  ...resources,
@@ -220,15 +256,17 @@ export function create() {
220
256
  owner?: Token.Contract,
221
257
  ): string {
222
258
  if (Token.is(value)) return serialize(value)
259
+ if (Token.isExpression(value))
260
+ return value.parts
261
+ .map((part) => (typeof part === 'string' ? part : serialize(part)))
262
+ .join('')
223
263
  if (typeof value !== 'object') return String(value)
224
264
  if ('default' in value) {
225
265
  // Separate fallback properties preserve extensions and resolve references within each scope.
226
266
  const base = literal(value.default, label, owner)
227
267
  // Rule consolidation must not change existing variable identities.
228
268
  const css = `:where(*){--fallback:${base};}${conditionalCss(value, '--fallback', ':where(*)', label, owner)}`
229
- const name = label
230
- ? `--z-${Identity.label(label)}-fallback-${Identity.compact(css)}`
231
- : `--z-f${Identity.hash(css)}`
269
+ const name = `${variable(owner?.[Token.identity], label ?? 'value')}-fallback-${Identity.name(css)}`
232
270
  const emitted: Rule[] = [
233
271
  { conditions: [], property: name, selector: ':where(*)', value: base },
234
272
  ]
@@ -380,9 +418,21 @@ export type Rule = {
380
418
  /** Selects independently shareable defaults or all token rules. */
381
419
  export const shared = Symbol('shared token rules')
382
420
 
383
- function variable(index: number | string, path: string): string {
384
- // Packed declarations retain the spelling assigned by their compiler.
385
- if (typeof index === 'string' && index.startsWith('src-'))
386
- return `--z-${Identity.label(path)}-${Identity.compact(JSON.stringify([index, path]))}`
387
- return `--z-t${encode(String(index))}-${encode(path)}`
421
+ /** Names an authored scope without its private source identity. */
422
+ export function scope(value: string, identity?: string): string {
423
+ const namespace = identity?.startsWith('id-') ? `${identity.slice(3)}-` : ''
424
+ return `z-theme-${namespace}${Identity.name(value)}`
425
+ }
426
+
427
+ function variable(
428
+ identity: string | undefined,
429
+ path: string,
430
+ naming?: 'legacy' | 'hashed',
431
+ ): string {
432
+ if (naming === 'legacy' && identity)
433
+ return `--z-t${encode(identity)}-${encode(path)}`
434
+ if (naming === 'hashed')
435
+ return `--z-${Identity.label(path)}-${Identity.compact(JSON.stringify([identity, path]))}`
436
+ const namespace = identity?.startsWith('id-') ? `${identity.slice(3)}-` : ''
437
+ return `--z-${namespace}${path.split('.').map(Identity.name).join('-')}`
388
438
  }