zyzz 0.0.24 → 0.0.25

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 (87) hide show
  1. package/README.md +23 -365
  2. package/dist/Style.d.ts +2 -0
  3. package/dist/Style.d.ts.map +1 -1
  4. package/dist/compiler/Graph.d.ts +3 -0
  5. package/dist/compiler/Graph.d.ts.map +1 -1
  6. package/dist/compiler/Graph.js +38 -2
  7. package/dist/compiler/Graph.js.map +1 -1
  8. package/dist/compiler/Source.d.ts.map +1 -1
  9. package/dist/compiler/Source.js +11 -3
  10. package/dist/compiler/Source.js.map +1 -1
  11. package/dist/compiler/Transform.d.ts +4 -0
  12. package/dist/compiler/Transform.d.ts.map +1 -1
  13. package/dist/compiler/Transform.js +79 -15
  14. package/dist/compiler/Transform.js.map +1 -1
  15. package/dist/compiler/internal/Applications.d.ts +20 -0
  16. package/dist/compiler/internal/Applications.d.ts.map +1 -1
  17. package/dist/compiler/internal/Applications.js +151 -47
  18. package/dist/compiler/internal/Applications.js.map +1 -1
  19. package/dist/compiler/internal/Compositions.d.ts.map +1 -1
  20. package/dist/compiler/internal/Compositions.js +6 -0
  21. package/dist/compiler/internal/Compositions.js.map +1 -1
  22. package/dist/compiler/internal/Configurations.d.ts.map +1 -1
  23. package/dist/compiler/internal/Configurations.js +4 -0
  24. package/dist/compiler/internal/Configurations.js.map +1 -1
  25. package/dist/compiler/internal/Contract.d.ts.map +1 -1
  26. package/dist/compiler/internal/Contract.js +34 -7
  27. package/dist/compiler/internal/Contract.js.map +1 -1
  28. package/dist/compiler/internal/PackedStyles.d.ts.map +1 -1
  29. package/dist/compiler/internal/PackedStyles.js +7 -0
  30. package/dist/compiler/internal/PackedStyles.js.map +1 -1
  31. package/dist/compiler/internal/Themes.d.ts.map +1 -1
  32. package/dist/compiler/internal/Themes.js +20 -8
  33. package/dist/compiler/internal/Themes.js.map +1 -1
  34. package/dist/internal/Configuration.d.ts +10 -0
  35. package/dist/internal/Configuration.d.ts.map +1 -1
  36. package/dist/internal/Configuration.js +8 -0
  37. package/dist/internal/Configuration.js.map +1 -1
  38. package/dist/internal/Token.d.ts +2 -0
  39. package/dist/internal/Token.d.ts.map +1 -1
  40. package/dist/internal/Token.js.map +1 -1
  41. package/dist/next/internal/Project.d.ts.map +1 -1
  42. package/dist/next/internal/Project.js +30 -25
  43. package/dist/next/internal/Project.js.map +1 -1
  44. package/dist/next/loader.js +2 -1
  45. package/dist/next/loader.js.map +1 -1
  46. package/dist/node/Host.d.ts.map +1 -1
  47. package/dist/node/Host.js +32 -23
  48. package/dist/node/Host.js.map +1 -1
  49. package/dist/node/internal/Contracts.d.ts +22 -0
  50. package/dist/node/internal/Contracts.d.ts.map +1 -0
  51. package/dist/node/internal/Contracts.js +287 -0
  52. package/dist/node/internal/Contracts.js.map +1 -0
  53. package/dist/unplugin/index.d.ts.map +1 -1
  54. package/dist/unplugin/index.js +31 -13
  55. package/dist/unplugin/index.js.map +1 -1
  56. package/dist/vite/index.d.ts.map +1 -1
  57. package/dist/vite/index.js +30 -37
  58. package/dist/vite/index.js.map +1 -1
  59. package/dist/web/Css.d.ts +10 -5
  60. package/dist/web/Css.d.ts.map +1 -1
  61. package/dist/web/Css.js +170 -31
  62. package/dist/web/Css.js.map +1 -1
  63. package/dist/web/internal/ClassName.d.ts +49 -0
  64. package/dist/web/internal/ClassName.d.ts.map +1 -1
  65. package/dist/web/internal/ClassName.js +57 -0
  66. package/dist/web/internal/ClassName.js.map +1 -1
  67. package/package.json +1 -1
  68. package/src/Style.ts +2 -0
  69. package/src/compiler/Graph.ts +42 -2
  70. package/src/compiler/Source.ts +11 -3
  71. package/src/compiler/Transform.ts +130 -29
  72. package/src/compiler/internal/Applications.ts +239 -63
  73. package/src/compiler/internal/Compositions.ts +9 -0
  74. package/src/compiler/internal/Configurations.ts +4 -0
  75. package/src/compiler/internal/Contract.ts +59 -7
  76. package/src/compiler/internal/PackedStyles.ts +9 -0
  77. package/src/compiler/internal/Themes.ts +25 -7
  78. package/src/internal/Configuration.ts +21 -0
  79. package/src/internal/Token.ts +2 -0
  80. package/src/next/internal/Project.ts +28 -23
  81. package/src/next/loader.ts +2 -1
  82. package/src/node/Host.ts +31 -24
  83. package/src/node/internal/Contracts.ts +384 -0
  84. package/src/unplugin/index.ts +35 -13
  85. package/src/vite/index.ts +35 -36
  86. package/src/web/Css.ts +228 -42
  87. package/src/web/internal/ClassName.ts +114 -0
@@ -46,6 +46,8 @@ export declare namespace compile {
46
46
  readonly [ThemeRules.shared]?: 'all' | 'defaults' | undefined
47
47
  /** Host entrypoints emit owned stylesheet effects without packed contracts. */
48
48
  readonly [Stylesheets.entry]?: string | undefined
49
+ /** Lets modules reuse shared rule names from unrelated modules of this one graph. */
50
+ readonly [ClassName.complete]?: boolean | undefined
49
51
  /** Host syntax from the same immutable source snapshot. */
50
52
  readonly [Syntax.cache]?:
51
53
  | ReadonlyMap<string, Parser.ParseResult>
@@ -1682,6 +1684,37 @@ function build(options: compile.Options, cache?: Cache): Cache {
1682
1684
  )
1683
1685
 
1684
1686
  const ownerSignature = JSON.stringify(owners)
1687
+ const units = ClassName.registry()
1688
+ const closures = new Map<string, ReadonlySet<string>>()
1689
+
1690
+ function closure(moduleId: string): ReadonlySet<string> {
1691
+ const cached = closures.get(moduleId)
1692
+ if (cached) return cached
1693
+
1694
+ const result = new Set<string>()
1695
+ closures.set(moduleId, result)
1696
+ for (const dependency of dependencies[moduleId] ?? []) {
1697
+ result.add(dependency)
1698
+ for (const nested of closure(dependency)) result.add(nested)
1699
+ }
1700
+
1701
+ return result
1702
+ }
1703
+
1704
+ // Hosts that compile a module within several graphs must name it the same in
1705
+ // each, so it may reuse only names its dependencies introduce.
1706
+ function visible(moduleId: string) {
1707
+ if (options[ClassName.complete]) return undefined
1708
+ return (owner: string) => closure(moduleId).has(owner)
1709
+ }
1710
+
1711
+ function restore(moduleId: string, output: Transform.compile.ReturnType) {
1712
+ return units.restore({
1713
+ owner: moduleId,
1714
+ used: output[ClassName.units] ?? new Map(),
1715
+ visible: visible(moduleId),
1716
+ })
1717
+ }
1685
1718
 
1686
1719
  // Extraction visits dependencies first; their emitted classes must precede consumers.
1687
1720
  for (const moduleId of extracted.keys()) {
@@ -1695,14 +1728,21 @@ function build(options: compile.Options, cache?: Cache): Cache {
1695
1728
  Object.entries(cached.classes).every(
1696
1729
  ([name, value]) => styleClasses[name] === value,
1697
1730
  ) &&
1698
- cached.owners === ownerSignature
1731
+ cached.owners === ownerSignature &&
1732
+ restore(moduleId, cached.output)
1699
1733
  modules[moduleId] = reusable
1700
1734
  ? cached.output
1701
1735
  : sameThemes &&
1702
1736
  previous!.schemes === schemes &&
1703
- extracted.get(moduleId) === previous!.extracted.get(moduleId)
1737
+ extracted.get(moduleId) === previous!.extracted.get(moduleId) &&
1738
+ restore(moduleId, previous!.result.modules[moduleId]!)
1704
1739
  ? previous!.result.modules[moduleId]!
1705
1740
  : Transform.compile({
1741
+ [ClassName.units]: units.scope({
1742
+ owner: moduleId,
1743
+ qualifier: Identity.compact(moduleId).slice(-6),
1744
+ visible: visible(moduleId),
1745
+ }),
1706
1746
  [ThemeRules.shared]:
1707
1747
  options[Stylesheets.entry] === undefined
1708
1748
  ? (options[ThemeRules.shared] ?? 'defaults')
@@ -1291,11 +1291,19 @@ export function extract(options: extract.Options): extract.ReturnType {
1291
1291
 
1292
1292
  if (diagnostics.length !== before) continue
1293
1293
 
1294
- const cssOutput = themes?.styles.get(call.start)?.theme[Token.definition]
1295
- .contract.cssOutput
1294
+ const contract = themes?.styles.get(call.start)?.theme[Token.definition]
1295
+ .contract
1296
+ const composition = contract?.composition
1297
+ const cssOutput = contract?.cssOutput
1296
1298
  styles.push(
1297
1299
  ...definition.styles.map((style) =>
1298
- cssOutput ? Object.freeze({ ...style, cssOutput }) : style,
1300
+ composition || cssOutput
1301
+ ? Object.freeze({
1302
+ ...style,
1303
+ ...(composition ? { composition } : {}),
1304
+ ...(cssOutput ? { cssOutput } : {}),
1305
+ })
1306
+ : style,
1299
1307
  ),
1300
1308
  )
1301
1309
 
@@ -9,6 +9,7 @@ import type * as Ast from '@oxc-project/types'
9
9
  import * as Css from '../web/Css.js'
10
10
  import * as ClassName from '../web/internal/ClassName.js'
11
11
  import * as Expression from './internal/Expression.js'
12
+ import * as Identity from '../internal/Identity.js'
12
13
  import MagicString from 'magic-string'
13
14
  import * as Mapping from '@jridgewell/gen-mapping'
14
15
  import * as Namespaces from './internal/Namespaces.js'
@@ -183,6 +184,47 @@ export function compile(options: compile.Options): compile.ReturnType {
183
184
  },
184
185
  })
185
186
 
187
+ const found = localApplications?.find() ?? []
188
+ // Compositions supersede their inputs' classes, so those reads stay unfolded.
189
+ const composing = (application: Span) =>
190
+ extracted.calls.some(
191
+ (call) =>
192
+ (call.composition || call.runtimeComposition) &&
193
+ call.start < application.start &&
194
+ application.end <= call.end,
195
+ )
196
+ const folded = new Set(
197
+ found.filter(
198
+ (application) =>
199
+ !composing(application) &&
200
+ !extracted.calls.some(
201
+ (call) =>
202
+ call.output === 'html' &&
203
+ call.runtimeComposition?.some(
204
+ (input) => input.applicationStart === application.start,
205
+ ),
206
+ ),
207
+ ),
208
+ )
209
+
210
+ const local =
211
+ portable || options.development
212
+ ? undefined
213
+ : localApplications?.definitions()
214
+
215
+ // Styles applied only alone to DOM elements, or inside cx, never meet another
216
+ // style's classes. Atomic ones share rules like independent styles.
217
+ const standalone = new Set(
218
+ [...(local?.values.keys() ?? [])].filter((name) =>
219
+ found.every(
220
+ (application) =>
221
+ application.name !== name ||
222
+ application.terminal ||
223
+ composing(application),
224
+ ),
225
+ ),
226
+ )
227
+
186
228
  // Scheme rules accompany modules whose runtime helpers can apply a scheme class.
187
229
  const schemes =
188
230
  options.schemes ??
@@ -196,6 +238,12 @@ export function compile(options: compile.Options): compile.ReturnType {
196
238
  [ThemeRules.shared]: options[ThemeRules.shared],
197
239
  development: options.development,
198
240
  [ClassName.labels]: extracted.styles[ClassName.labels],
241
+ [ClassName.units]:
242
+ options[ClassName.units] ??
243
+ ClassName.registry().scope({
244
+ owner: options.moduleId,
245
+ qualifier: Identity.compact(options.moduleId).slice(-6),
246
+ }),
199
247
  composition: options.composition,
200
248
  cssOutput: options.cssOutput,
201
249
  names: portable ? portableNames : undefined,
@@ -206,14 +254,23 @@ export function compile(options: compile.Options): compile.ReturnType {
206
254
  if (portable || options.development) return extracted.styles
207
255
 
208
256
  const dead = localApplications?.dead()
209
- if (!dead?.size) return extracted.styles
257
+ if (!dead?.size && !standalone.size) return extracted.styles
210
258
 
211
259
  return {
212
- styles: extracted.styles.styles.map((style) =>
213
- dead.has(style.name)
214
- ? { cssOutput: style.cssOutput, declarations: [], name: style.name }
215
- : style,
216
- ),
260
+ styles: extracted.styles.styles.map((style) => {
261
+ if (dead?.has(style.name))
262
+ return {
263
+ cssOutput: style.cssOutput,
264
+ declarations: [],
265
+ name: style.name,
266
+ }
267
+
268
+ // Grouped output keeps one class per style.
269
+ return standalone.has(style.name) &&
270
+ (style.cssOutput ?? options.cssOutput ?? 'atomic') === 'atomic'
271
+ ? { ...style, composition: 'independent' as const }
272
+ : style
273
+ }),
217
274
  }
218
275
  })(),
219
276
  contributions: extracted.contributions,
@@ -319,6 +376,35 @@ export function compile(options: compile.Options): compile.ReturnType {
319
376
  ),
320
377
  )
321
378
 
379
+ // Definitions read only through folded applications need no class list of
380
+ // their own. True marks definitions whose every read follows initialization.
381
+ const unread = new Map(
382
+ [...(local?.values ?? [])].filter(([name]) =>
383
+ found.every(
384
+ (application) => application.name !== name || folded.has(application),
385
+ ),
386
+ ),
387
+ )
388
+ const namespaces = (local?.namespaces ?? []).filter((namespace) =>
389
+ [...namespace.ends.keys()].every((name) => unread.get(name)),
390
+ )
391
+
392
+ const erased = new Map(namespaces.flatMap((namespace) => [...namespace.ends]))
393
+
394
+ // An ambient namespace keeps member types without runtime initialization.
395
+ for (const namespace of namespaces)
396
+ module.prependRight(namespace.start, 'declare ')
397
+
398
+ for (const call of extracted.calls) {
399
+ const end = erased.get(call.name)
400
+ if (end !== undefined)
401
+ module.overwrite(
402
+ end,
403
+ call.start,
404
+ `: import('zyzz').style.ReturnType${call.output === 'html' ? "<'html'>" : ''}`,
405
+ )
406
+ }
407
+
322
408
  let callable = false
323
409
 
324
410
  const composed = (node: Span) =>
@@ -438,6 +524,17 @@ export function compile(options: compile.Options): compile.ReturnType {
438
524
  return typed ? `(${result} as ${type})` : result
439
525
  }
440
526
 
527
+ const initialized = unread.get(call.name)
528
+ if (initialized !== undefined) {
529
+ if (erased.has(call.name)) return ''
530
+
531
+ // Guards read only truthiness, and failures before initialization still call it.
532
+ const value = initialized ? 'void 0' : '1'
533
+ return /\.[cm]?tsx?$/.test(options.moduleId)
534
+ ? `(${value} as unknown as import('zyzz').style.ReturnType${call.output === 'html' ? "<'html'>" : ''})`
535
+ : value
536
+ }
537
+
441
538
  if (application.folded)
442
539
  return composeHtml
443
540
  ? `${compositionHtml}.from({className:${JSON.stringify(classes[call.name])}})`
@@ -463,31 +560,13 @@ export function compile(options: compile.Options): compile.ReturnType {
463
560
  !call.slots &&
464
561
  !call.runtimeComposition &&
465
562
  !application.folded &&
563
+ !unread.has(call.name) &&
466
564
  call.output !== 'html'
467
565
  )
468
566
  callable = true
469
567
  }
470
568
 
471
- for (const application of localApplications?.find() ?? []) {
472
- if (
473
- extracted.calls.some(
474
- (call) =>
475
- call.output === 'html' &&
476
- call.runtimeComposition?.some(
477
- (input) => input.applicationStart === application.start,
478
- ),
479
- )
480
- )
481
- continue
482
- if (
483
- extracted.calls.some(
484
- (call) =>
485
- (call.composition || call.runtimeComposition) &&
486
- call.start < application.start &&
487
- application.end <= call.end,
488
- )
489
- )
490
- continue
569
+ for (const application of folded) {
491
570
  const className = JSON.stringify(classes[application.name])
492
571
 
493
572
  const key =
@@ -495,12 +574,18 @@ export function compile(options: compile.Options): compile.ReturnType {
495
574
  'html'
496
575
  ? 'class'
497
576
  : 'className'
577
+ const callee = options.source.slice(
578
+ application.start,
579
+ application.calleeEnd,
580
+ )
498
581
 
499
- // Keep a callable guard so bundlers also retain failures before initialization.
500
582
  module.overwrite(
501
583
  application.start,
502
584
  application.end,
503
- `(${options.source.slice(application.start, application.calleeEnd)}?{${key}:${className}}:${options.source.slice(application.start, application.calleeEnd)}())`,
585
+ application.initialized
586
+ ? `({${key}:${className}})`
587
+ : // Keep a callable guard so bundlers also retain failures before initialization.
588
+ `(${callee}?{${key}:${className}}:${callee}())`,
504
589
  )
505
590
  }
506
591
 
@@ -688,7 +773,17 @@ export function compile(options: compile.Options): compile.ReturnType {
688
773
  specifier.importKind === 'type' ||
689
774
  !(
690
775
  node.source.value === 'zyzz'
691
- ? ['Config', 'cx', 'style', 'Vars', 'variable', 'variants']
776
+ ? [
777
+ 'Config',
778
+ 'cx',
779
+ 'defineConfig',
780
+ 'defineVars',
781
+ 'extendVars',
782
+ 'style',
783
+ 'Vars',
784
+ 'variable',
785
+ 'variants',
786
+ ]
692
787
  : [
693
788
  'Css',
694
789
  'cssFunction',
@@ -1135,6 +1230,7 @@ export function compile(options: compile.Options): compile.ReturnType {
1135
1230
  ? { [ThemeRules.shared]: emitted[ThemeRules.shared] }
1136
1231
  : {}),
1137
1232
  [ClassName.rules]: emitted[ClassName.rules],
1233
+ [ClassName.units]: emitted[ClassName.units],
1138
1234
  classes,
1139
1235
  code: portable ? options.source : module.toString(),
1140
1236
  css: namespaced.css,
@@ -1150,6 +1246,7 @@ export function compile(options: compile.Options): compile.ReturnType {
1150
1246
  vars: emitted.vars,
1151
1247
  }
1152
1248
  Object.defineProperty(result, ClassName.rules, { enumerable: false })
1249
+ Object.defineProperty(result, ClassName.units, { enumerable: false })
1153
1250
  return Object.freeze(result)
1154
1251
  }
1155
1252
 
@@ -1161,6 +1258,8 @@ export declare namespace compile {
1161
1258
  type Options = Source.extract.Options & {
1162
1259
  /** Separates generated token definitions for independently loaded modules. */
1163
1260
  readonly [ThemeRules.shared]?: 'all' | 'defaults' | undefined
1261
+ /** Names shared independent atomic rules, with one name table across a graph's modules. */
1262
+ readonly [ClassName.units]?: ClassName.Units | undefined
1164
1263
  /** Disable source rewriting while emitting CSS for runtime authoring. Defaults to true. */
1165
1264
  readonly compiler?: boolean | undefined
1166
1265
  /** Whether compiled applications can be combined with one another. */
@@ -1188,6 +1287,8 @@ export declare namespace compile {
1188
1287
  readonly code: string
1189
1288
  /** Exact emitted rules retained for collision diagnostics. */
1190
1289
  readonly [ClassName.rules]: Readonly<Record<string, string>>
1290
+ /** Shared rule names chosen by this module, for graph cache validation. */
1291
+ readonly [ClassName.units]?: ClassName.Units['used'] | undefined
1191
1292
  /** Ordered, unminified stylesheet text. */
1192
1293
  readonly css: string
1193
1294
  /** Standard stylesheet map with authored selector/declaration locations. */