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
@@ -32,6 +32,8 @@ import * as Themes from './internal/Themes.js'
32
32
  import * as ThemeValues from '../web/internal/Themes.js'
33
33
  import * as Token from '../internal/Token.js'
34
34
  import * as Literal from '../internal/Literal.js'
35
+ import * as Names from './internal/Names.js'
36
+ import * as ClassName from '../web/internal/ClassName.js'
35
37
  import * as Typography from '../internal/Typography.js'
36
38
  import * as Value from '../internal/Value.js'
37
39
  import * as Vars from './internal/Vars.js'
@@ -57,6 +59,10 @@ export type Call = {
57
59
  | undefined
58
60
  /** Class identity available without source rewriting. */
59
61
  readonly portable?: string | undefined
62
+ /** Readable authored binding used for contextual CSS names. */
63
+ readonly cssName?: string | undefined
64
+ /** Explicit configuration namespace for emitted CSS. */
65
+ readonly cssNamespace?: string | undefined
60
66
  /** Flattened definition identities for nested compositions. */
61
67
  readonly portableInputs?: readonly string[] | undefined
62
68
  /** Ownership retained by a separately compiled callable. */
@@ -214,6 +220,7 @@ export function extract(options: extract.Options): extract.ReturnType {
214
220
  }
215
221
 
216
222
  const program = parsed.program
223
+ const authoredNames = Names.collect(program)
217
224
  const scopeTracker = new Scope.Tracker({ preserveExitedScopes: true })
218
225
 
219
226
  Walker.walk(program, { scopeTracker })
@@ -375,6 +382,9 @@ export function extract(options: extract.Options): extract.ReturnType {
375
382
  if (
376
383
  name === 'Config' ||
377
384
  name === 'cx' ||
385
+ name === 'defineConfig' ||
386
+ name === 'defineVars' ||
387
+ name === 'extendVars' ||
378
388
  name === 'style' ||
379
389
  name === 'Theme' ||
380
390
  name === 'Vars' ||
@@ -537,6 +547,13 @@ export function extract(options: extract.Options): extract.ReturnType {
537
547
  pending,
538
548
  options[Themes.context]?.links,
539
549
  selectorKeys,
550
+ new Map(
551
+ [...(themes?.styles ?? [])].flatMap(([start, context]) => {
552
+ const id = context.theme[Token.definition].contract[Token.identity]
553
+ return id?.startsWith('id-') ? [[start, id.slice(3)] as const] : []
554
+ }),
555
+ ),
556
+ options.moduleId,
540
557
  )
541
558
  } catch (error) {
542
559
  if (!(error instanceof Themes.InvalidError)) throw error
@@ -546,7 +563,13 @@ export function extract(options: extract.Options): extract.ReturnType {
546
563
  }
547
564
  })()
548
565
 
566
+ const cssNameCounts = new Map<string, number>()
549
567
  for (const call of pending) {
568
+ const name = authoredNames.get(call.start)!
569
+ cssNameCounts.set(name, (cssNameCounts.get(name) ?? 0) + 1)
570
+ }
571
+ const cssNameIndices = new Map<string, number>()
572
+ for (const [callIndex, call] of pending.entries()) {
550
573
  const explicitId = (() => {
551
574
  try {
552
575
  return Identifiers.explicit(call)
@@ -555,10 +578,29 @@ export function extract(options: extract.Options): extract.ReturnType {
555
578
  return undefined
556
579
  }
557
580
  })()
581
+ const contractIdentity = themes?.styles.get(call.start)?.theme[
582
+ Token.definition
583
+ ].contract[Token.identity]
584
+ const cssNamespace = contractIdentity?.startsWith('id-')
585
+ ? contractIdentity.slice(3)
586
+ : undefined
587
+ const authoredName = authoredNames.get(call.start)!
588
+ const nameIndex = cssNameIndices.get(authoredName) ?? 0
589
+ cssNameIndices.set(authoredName, nameIndex + 1)
590
+ const cssName =
591
+ explicitId !== undefined
592
+ ? Identity.name(explicitId)
593
+ : `${Identity.compact(options.moduleId).slice(-6)}-${
594
+ authoredName === 'style'
595
+ ? `style-${callIndex}`
596
+ : cssNameCounts.get(authoredName)! > 1
597
+ ? `${authoredName}-${nameIndex}`
598
+ : authoredName
599
+ }`
558
600
  const definitionId =
559
601
  explicitId === undefined
560
- ? `${namespace}-${call.start}`
561
- : Identity.requireId(explicitId, 'style')
602
+ ? `${cssNamespace ? `${cssNamespace}-` : ''}${cssName}`
603
+ : `id-${cssNamespace ? `${cssNamespace}-` : ''}${Identity.requireId(explicitId, 'style').slice(3)}`
562
604
  let argument = call.arguments[0]
563
605
 
564
606
  if (call.arguments.length === 0)
@@ -1302,10 +1344,12 @@ export function extract(options: extract.Options): extract.ReturnType {
1302
1344
  !definition.styles[0]!.targets)
1303
1345
  )
1304
1346
  return undefined
1305
- return Identity.style(definition.styles[0]!)
1347
+ return Identity.style(definition.styles[0]!, cssNamespace)
1306
1348
  })(),
1307
1349
  }
1308
1350
  : {}),
1351
+ cssName,
1352
+ cssNamespace,
1309
1353
  end: call.end,
1310
1354
  name,
1311
1355
  start: call.start,
@@ -1325,6 +1369,7 @@ export function extract(options: extract.Options): extract.ReturnType {
1325
1369
  }
1326
1370
 
1327
1371
  try {
1372
+ let compositionIndex = 0
1328
1373
  for (const entry of options.target === 'native'
1329
1374
  ? []
1330
1375
  : Compositions.collect({
@@ -1336,7 +1381,16 @@ export function extract(options: extract.Options): extract.ReturnType {
1336
1381
  source: options.source,
1337
1382
  styles,
1338
1383
  })) {
1339
- calls.push(...(entry.cases ?? []).map((entry) => entry.call), entry.call)
1384
+ const authored = authoredNames.get(entry.call.start)
1385
+ const name = authored && authored !== 'style' ? authored : 'composition'
1386
+ const cssName = `${name}-${compositionIndex++}-${entry.call.cssName ?? 'style'}`
1387
+ calls.push(
1388
+ ...(entry.cases ?? []).map((value, index) => ({
1389
+ ...value.call,
1390
+ cssName: `${cssName}-${index}`,
1391
+ })),
1392
+ { ...entry.call, cssName },
1393
+ )
1340
1394
  styles.push(
1341
1395
  ...(entry.cases ?? []).map((entry) => entry.style),
1342
1396
  entry.style,
@@ -1480,6 +1534,33 @@ export function extract(options: extract.Options): extract.ReturnType {
1480
1534
  }
1481
1535
  }
1482
1536
 
1537
+ const callsByName = new Map(calls.map((call) => [call.name, call]))
1538
+ const definition = {
1539
+ styles: Object.freeze(styles),
1540
+ [ClassName.labels]: Object.freeze(
1541
+ Object.fromEntries(
1542
+ styles.map((style) => {
1543
+ let name = style.name
1544
+ let call = callsByName.get(name)
1545
+ while (!call && name.includes('-')) {
1546
+ name = name.slice(0, name.lastIndexOf('-'))
1547
+ call = callsByName.get(name)
1548
+ }
1549
+ return [
1550
+ style.name,
1551
+ {
1552
+ name: call?.cssName
1553
+ ? call.cssName + style.name.slice(call.name.length)
1554
+ : style.name,
1555
+ namespace: call?.cssNamespace,
1556
+ },
1557
+ ]
1558
+ }),
1559
+ ),
1560
+ ),
1561
+ }
1562
+ Object.defineProperty(definition, ClassName.labels, { enumerable: false })
1563
+
1483
1564
  return Object.freeze({
1484
1565
  namespaces: contributionData.filter(
1485
1566
  (value): value is Extract<Css.Contribution, { kind: 'namespace' }> =>
@@ -1519,7 +1600,7 @@ export function extract(options: extract.Options): extract.ReturnType {
1519
1600
  )
1520
1601
  }),
1521
1602
  ),
1522
- styles: Object.freeze({ styles: Object.freeze(styles) }),
1603
+ styles: Object.freeze(definition),
1523
1604
  ...(variables.calls.length
1524
1605
  ? { variableCalls: Object.freeze(variables.calls) }
1525
1606
  : {}),
@@ -7,6 +7,7 @@ import * as Token from '../internal/Token.js'
7
7
  import * as Applications from './internal/Applications.js'
8
8
  import type * as Ast from '@oxc-project/types'
9
9
  import * as Css from '../web/Css.js'
10
+ import * as ClassName from '../web/internal/ClassName.js'
10
11
  import * as Expression from './internal/Expression.js'
11
12
  import MagicString from 'magic-string'
12
13
  import * as Mapping from '@jridgewell/gen-mapping'
@@ -71,11 +72,7 @@ export function compile(options: compile.Options): compile.ReturnType {
71
72
  'CSS-only named stylesheet declarations require an explicit id.',
72
73
  )
73
74
  for (const call of extracted.variableCalls ?? [])
74
- if (
75
- Object.values(call.slots).some(
76
- (slot) => !slot.name.startsWith('--z-vid-'),
77
- )
78
- )
75
+ if (!call.explicit)
79
76
  throw new Error('CSS-only output requires an explicit variable id.')
80
77
  }
81
78
 
@@ -198,7 +195,7 @@ export function compile(options: compile.Options): compile.ReturnType {
198
195
  const emitted = Css.compile({
199
196
  [ThemeRules.shared]: options[ThemeRules.shared],
200
197
  development: options.development,
201
- scope: options.moduleId,
198
+ [ClassName.labels]: extracted.styles[ClassName.labels],
202
199
  composition: options.composition,
203
200
  cssOutput: options.cssOutput,
204
201
  names: portable ? portableNames : undefined,
@@ -291,56 +288,19 @@ export function compile(options: compile.Options): compile.ReturnType {
291
288
  `${variables}.create(${JSON.stringify(call.slots.value)})`,
292
289
  )
293
290
 
294
- const first = extracted.calls[0]
295
- const scope = first ? first.name.slice(6, first.name.lastIndexOf('-')) : ''
296
291
  const identities = new Map(
297
292
  extracted.calls
298
293
  .filter((call) => call.identity)
299
294
  .map((call) => [call.name, call.identity!]),
300
295
  )
301
296
 
302
- const modes = new Map(
303
- extracted.styles.styles.map((style) => [style.name, style.cssOutput]),
304
- )
305
- const ownersByClass = new Map<string, string | false>()
306
- for (const [style, value] of Object.entries(emitted.classes))
307
- for (const name of value.split(' ').filter(Boolean))
308
- ownersByClass.set(name, ownersByClass.has(name) ? false : style)
309
-
310
- const names = new Map<string, string>()
311
-
312
- for (const classes of Object.values(emitted.classes))
313
- for (const name of classes.split(' ').filter(Boolean))
314
- names.set(
315
- name,
316
- (() => {
317
- const owner = ownersByClass.get(name)
318
- const identity = owner && identities.get(owner)
319
- if (
320
- (modes.get(owner || '') ?? options.cssOutput) === 'grouped' &&
321
- options.composition === 'independent' &&
322
- name.startsWith('g_') &&
323
- identity &&
324
- !identity.includes(' ')
325
- )
326
- return identity
327
- if (name.startsWith('g_')) return `g_${scope}_${name.slice(2)}`
328
- return name.startsWith('z_base')
329
- ? `z-${scope}-${name.slice(2)}`
330
- : name
331
- })(),
332
- )
333
-
334
297
  const classes = Object.freeze(
335
298
  Object.fromEntries(
336
299
  Object.entries(emitted.classes).map(([name, value]) => [
337
300
  name,
338
301
  [
339
302
  ...new Set([
340
- ...value
341
- .split(' ')
342
- .filter(Boolean)
343
- .map((part) => names.get(part)!),
303
+ ...value.split(' ').filter(Boolean),
344
304
  ...(identities.has(name) &&
345
305
  (!portable || portableNames[name]?.startsWith('z-compose-'))
346
306
  ? [
@@ -930,8 +890,16 @@ export function compile(options: compile.Options): compile.ReturnType {
930
890
  (scoped ? scoped.split('\n') : [])
931
891
  .map((rule, index) => {
932
892
  const line = index + 1 + (prefix ? prefix.split('\n').length : 0)
933
- const brace = rule.indexOf('{')
934
- const name = rule.slice(1, brace)
893
+ const openers = new Map<number, number>()
894
+ const conditionStarts = declarationStarts(rule, true, openers)
895
+ const selectorStart =
896
+ conditionStarts.find((start) => rule[start] === '.') ?? 0
897
+ const brace = openers.get(selectorStart) ?? rule.indexOf('{')
898
+ const name = rule
899
+ .slice(selectorStart + 1, brace)
900
+ .replace(/\\([0-9a-f]+) ?/gi, (_, code: string) =>
901
+ String.fromCodePoint(Number.parseInt(code, 16)),
902
+ )
935
903
  const linkedOwner = linkedOwners.get(name)
936
904
 
937
905
  if (linkedOwner) {
@@ -995,11 +963,11 @@ export function compile(options: compile.Options): compile.ReturnType {
995
963
  return rule
996
964
  }
997
965
 
998
- const selector = `.${names.get(name)!}`
966
+ const selector = `.${ClassName.selector(name)}`
999
967
  const call = owners.get(name)!
1000
968
 
1001
969
  Mapping.addMapping(cssMap, {
1002
- generated: { column: 0, line },
970
+ generated: { column: selectorStart, line },
1003
971
  name: call.name,
1004
972
  original: position(call.start),
1005
973
  source: options.moduleId,
@@ -1071,8 +1039,6 @@ export function compile(options: compile.Options): compile.ReturnType {
1071
1039
 
1072
1040
  const ordered = declarations(style)
1073
1041
  const authored = locations(call.body ?? definitions.get(call.start)!)
1074
- const openers = new Map<number, number>()
1075
- const conditionStarts = declarationStarts(body, true, openers)
1076
1042
  const conditions: string[] = []
1077
1043
 
1078
1044
  function collectConditions(style: Style.NamedStyle) {
@@ -1084,17 +1050,25 @@ export function compile(options: compile.Options): compile.ReturnType {
1084
1050
  }
1085
1051
 
1086
1052
  collectConditions(style)
1087
- let conditionCursor = 0
1053
+ const mappedConditions = new Set<number>()
1088
1054
 
1089
1055
  for (const start of conditionStarts) {
1090
- const condition = body.slice(start, openers.get(start))
1091
- const index = conditions.indexOf(condition, conditionCursor)
1056
+ const condition = rule.slice(start, openers.get(start))
1057
+ const index = conditions.findIndex(
1058
+ (value, index) =>
1059
+ value === condition && !mappedConditions.has(index),
1060
+ )
1092
1061
  const node = conditionNodes[index]
1093
1062
  if (!node) continue
1094
1063
 
1095
- conditionCursor = index + 1
1064
+ mappedConditions.add(index)
1096
1065
  Mapping.addMapping(cssMap, {
1097
- generated: { column: selector.length + start, line },
1066
+ generated: {
1067
+ column:
1068
+ start +
1069
+ (start > brace ? selector.length - (brace - selectorStart) : 0),
1070
+ line,
1071
+ },
1098
1072
  name: options.source.slice(node.key.start, node.key.end),
1099
1073
  original: position(node.key.start),
1100
1074
  source: options.moduleId,
@@ -1124,7 +1098,10 @@ export function compile(options: compile.Options): compile.ReturnType {
1124
1098
  used.add(propertyIndex)
1125
1099
 
1126
1100
  Mapping.addMapping(cssMap, {
1127
- generated: { column: selector.length + start, line },
1101
+ generated: {
1102
+ column: selectorStart + selector.length + start,
1103
+ line,
1104
+ },
1128
1105
  name: declaration.property,
1129
1106
  original: position(location.start),
1130
1107
  source: options.moduleId,
@@ -1132,7 +1109,7 @@ export function compile(options: compile.Options): compile.ReturnType {
1132
1109
  cursor = start + text.length
1133
1110
  }
1134
1111
 
1135
- return selector + body
1112
+ return rule.slice(0, selectorStart) + selector + body
1136
1113
  })
1137
1114
  .join('\n'),
1138
1115
  ]
@@ -1153,10 +1130,11 @@ export function compile(options: compile.Options): compile.ReturnType {
1153
1130
  Mapping.toEncodedMap(cssMap),
1154
1131
  true,
1155
1132
  )
1156
- return Object.freeze({
1133
+ const result = {
1157
1134
  ...(emitted[ThemeRules.shared]
1158
1135
  ? { [ThemeRules.shared]: emitted[ThemeRules.shared] }
1159
1136
  : {}),
1137
+ [ClassName.rules]: emitted[ClassName.rules],
1160
1138
  classes,
1161
1139
  code: portable ? options.source : module.toString(),
1162
1140
  css: namespaced.css,
@@ -1170,7 +1148,9 @@ export function compile(options: compile.Options): compile.ReturnType {
1170
1148
  version: 3 as const,
1171
1149
  },
1172
1150
  vars: emitted.vars,
1173
- })
1151
+ }
1152
+ Object.defineProperty(result, ClassName.rules, { enumerable: false })
1153
+ return Object.freeze(result)
1174
1154
  }
1175
1155
 
1176
1156
  /** Source transform contracts. */
@@ -1187,7 +1167,7 @@ export declare namespace compile {
1187
1167
  readonly composition?: Css.compile.Options['composition']
1188
1168
  /** Default CSS representation for definitions without an explicit mode. */
1189
1169
  readonly cssOutput?: Css.compile.Options['cssOutput']
1190
- /** Stable declaration names for CSS-only development updates. */
1170
+ /** Retains live definitions for development updates. */
1191
1171
  readonly development?: boolean | undefined
1192
1172
  /**
1193
1173
  * Emit the `color-scheme` selection classes beside the theme scopes.
@@ -1206,6 +1186,8 @@ export declare namespace compile {
1206
1186
  readonly classes: Readonly<Record<string, string>>
1207
1187
  /** Rewritten source with imports for surviving props callables. */
1208
1188
  readonly code: string
1189
+ /** Exact emitted rules retained for collision diagnostics. */
1190
+ readonly [ClassName.rules]: Readonly<Record<string, string>>
1209
1191
  /** Ordered, unminified stylesheet text. */
1210
1192
  readonly css: string
1211
1193
  /** Standard stylesheet map with authored selector/declaration locations. */
@@ -90,7 +90,15 @@ export function read(contract: string): readonly Catalog[] {
90
90
  return [...configurations].map(([identity, { entries, storageKey }]) => ({
91
91
  entries: [...entries].map(
92
92
  ([name, scope]) =>
93
- [name, `z_theme-${ThemeValues.encode(scope)}`] as const,
93
+ [
94
+ name,
95
+ ThemeValues.scope(
96
+ scope.startsWith(`${identity}-`)
97
+ ? scope.slice(identity.length + 1)
98
+ : scope,
99
+ identity,
100
+ ),
101
+ ] as const,
94
102
  ),
95
103
  identity,
96
104
  storageKey,
@@ -503,6 +503,13 @@ export function collect(options: collect.Options) {
503
503
  )
504
504
  : undefined
505
505
  const call: Source.Call = {
506
+ cssName: selected
507
+ .map((call) =>
508
+ [call.cssNamespace, call.cssName ?? call.identity ?? 'style']
509
+ .filter(Boolean)
510
+ .join('-'),
511
+ )
512
+ .join('-'),
506
513
  ...(portable(selected)
507
514
  ? {
508
515
  portable: portable(selected),
@@ -148,6 +148,7 @@ export function collect(options: collect.Options): Themes.Link {
148
148
  }
149
149
  : {}),
150
150
  [Token.identity]: identity,
151
+ ...(input.id !== undefined ? { [Token.complete]: true } : {}),
151
152
  cssOutput: input.cssOutput ?? 'atomic',
152
153
  ...(input.defaultLayer !== undefined
153
154
  ? { defaultLayer: input.defaultLayer }
@@ -161,7 +162,7 @@ export function collect(options: collect.Options): Themes.Link {
161
162
 
162
163
  for (const [key, original] of Object.entries(catalog)) {
163
164
  const name = `${identity}-${key}`
164
- const definition = Token.bind(original, contract)
165
+ const definition = Token.bind(original, contract, key)
165
166
  const tokenType = type({
166
167
  ...values(original.tokens),
167
168
  ...original[Token.definition].queries,
@@ -241,6 +242,7 @@ export function collect(options: collect.Options): Themes.Link {
241
242
 
242
243
  return {}
243
244
  })(),
245
+ ...(input.id !== undefined ? { id: input.id } : {}),
244
246
  ...(input.cssOutput ? { cssOutput: input.cssOutput } : {}),
245
247
  ...(input.defaultLayer !== undefined
246
248
  ? { defaultLayer: input.defaultLayer }
@@ -294,6 +296,8 @@ export declare namespace collect {
294
296
 
295
297
  /** Encodes validated literal options as a declaration type, never executable text. */
296
298
  export function type(value: unknown): string {
299
+ if (Token.isExpression(value) && 'group' in value)
300
+ return `import('zyzz').Vars.Composition<'${value.group}'>`
297
301
  if (Token.is(value)) return `import('zyzz').Vars.Reference<'${value.group}'>`
298
302
  if (Array.isArray(value)) return `readonly [${value.map(type).join(',')}]`
299
303
  if (!value || typeof value !== 'object') return JSON.stringify(value)