zyzz 0.0.8 → 0.0.11

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 (126) hide show
  1. package/README.md +8 -8
  2. package/dist/Style.d.ts +2 -2
  3. package/dist/Style.d.ts.map +1 -1
  4. package/dist/Style.js +6 -6
  5. package/dist/Style.js.map +1 -1
  6. package/dist/Vars.d.ts +2 -2
  7. package/dist/Vars.d.ts.map +1 -1
  8. package/dist/Vars.js.map +1 -1
  9. package/dist/compiler/Source.d.ts.map +1 -1
  10. package/dist/compiler/Source.js +10 -0
  11. package/dist/compiler/Source.js.map +1 -1
  12. package/dist/compiler/internal/Contract.d.ts.map +1 -1
  13. package/dist/compiler/internal/Contract.js +11 -6
  14. package/dist/compiler/internal/Contract.js.map +1 -1
  15. package/dist/compiler/internal/Contributions.d.ts.map +1 -1
  16. package/dist/compiler/internal/Contributions.js +592 -556
  17. package/dist/compiler/internal/Contributions.js.map +1 -1
  18. package/dist/compiler/internal/Expression.d.ts +4 -0
  19. package/dist/compiler/internal/Expression.d.ts.map +1 -1
  20. package/dist/compiler/internal/Expression.js +45 -0
  21. package/dist/compiler/internal/Expression.js.map +1 -1
  22. package/dist/compiler/internal/RecipeConditions.js +2 -2
  23. package/dist/compiler/internal/RecipeConditions.js.map +1 -1
  24. package/dist/compiler/internal/Themes.d.ts.map +1 -1
  25. package/dist/compiler/internal/Themes.js +2 -0
  26. package/dist/compiler/internal/Themes.js.map +1 -1
  27. package/dist/default.d.ts +75 -19
  28. package/dist/default.d.ts.map +1 -1
  29. package/dist/default.js +76 -11
  30. package/dist/default.js.map +2 -2
  31. package/dist/default.js.zyzz.json +1 -1
  32. package/dist/internal/Condition.d.ts +1 -1
  33. package/dist/internal/Condition.d.ts.map +1 -1
  34. package/dist/internal/Configuration.js +2 -2
  35. package/dist/internal/Configuration.js.map +1 -1
  36. package/dist/internal/Query.d.ts +2 -2
  37. package/dist/internal/Query.d.ts.map +1 -1
  38. package/dist/internal/Query.js +1 -1
  39. package/dist/internal/Query.js.map +1 -1
  40. package/dist/internal/Theme.d.ts +4 -4
  41. package/dist/internal/Theme.d.ts.map +1 -1
  42. package/dist/internal/Theme.js +6 -6
  43. package/dist/internal/Theme.js.map +1 -1
  44. package/dist/internal/Token.d.ts +185 -2
  45. package/dist/internal/Token.d.ts.map +1 -1
  46. package/dist/internal/Token.js +236 -23
  47. package/dist/internal/Token.js.map +1 -1
  48. package/dist/internal/Value.d.ts +5 -5
  49. package/dist/internal/Value.d.ts.map +1 -1
  50. package/dist/internal/Value.js +18 -64
  51. package/dist/internal/Value.js.map +1 -1
  52. package/dist/internal/VariableSets.d.ts.map +1 -1
  53. package/dist/internal/VariableSets.js +9 -6
  54. package/dist/internal/VariableSets.js.map +1 -1
  55. package/dist/unplugin/index.d.ts.map +1 -1
  56. package/dist/unplugin/index.js +17 -4
  57. package/dist/unplugin/index.js.map +1 -1
  58. package/dist/vite/index.d.ts.map +1 -1
  59. package/dist/vite/index.js +112 -24
  60. package/dist/vite/index.js.map +1 -1
  61. package/dist/web/colorProfile.d.ts +9 -5
  62. package/dist/web/colorProfile.d.ts.map +1 -1
  63. package/dist/web/colorProfile.js.map +1 -1
  64. package/dist/web/counterStyle.d.ts +13 -9
  65. package/dist/web/counterStyle.d.ts.map +1 -1
  66. package/dist/web/counterStyle.js.map +1 -1
  67. package/dist/web/cssFunction.d.ts +19 -14
  68. package/dist/web/cssFunction.d.ts.map +1 -1
  69. package/dist/web/cssFunction.js.map +1 -1
  70. package/dist/web/fontFace.d.ts +6 -1
  71. package/dist/web/fontFace.d.ts.map +1 -1
  72. package/dist/web/fontFace.js.map +1 -1
  73. package/dist/web/fontFeatureValues.d.ts +11 -7
  74. package/dist/web/fontFeatureValues.d.ts.map +1 -1
  75. package/dist/web/fontFeatureValues.js.map +1 -1
  76. package/dist/web/fontPaletteValues.d.ts +8 -3
  77. package/dist/web/fontPaletteValues.d.ts.map +1 -1
  78. package/dist/web/fontPaletteValues.js.map +1 -1
  79. package/dist/web/internal/Context.d.ts +3 -3
  80. package/dist/web/internal/Context.d.ts.map +1 -1
  81. package/dist/web/keyframes.d.ts +7 -3
  82. package/dist/web/keyframes.d.ts.map +1 -1
  83. package/dist/web/keyframes.js.map +1 -1
  84. package/dist/web/page.d.ts +11 -4
  85. package/dist/web/page.d.ts.map +1 -1
  86. package/dist/web/page.js.map +1 -1
  87. package/dist/web/positionTry.d.ts +6 -1
  88. package/dist/web/positionTry.d.ts.map +1 -1
  89. package/dist/web/positionTry.js.map +1 -1
  90. package/dist/web/property.d.ts +10 -5
  91. package/dist/web/property.d.ts.map +1 -1
  92. package/dist/web/property.js.map +1 -1
  93. package/dist/web/viewTransition.d.ts +10 -6
  94. package/dist/web/viewTransition.d.ts.map +1 -1
  95. package/dist/web/viewTransition.js.map +1 -1
  96. package/package.json +1 -1
  97. package/src/Style.ts +8 -8
  98. package/src/Vars.ts +2 -3
  99. package/src/compiler/Source.ts +19 -0
  100. package/src/compiler/internal/Contract.ts +19 -8
  101. package/src/compiler/internal/Contributions.ts +734 -685
  102. package/src/compiler/internal/Expression.ts +57 -0
  103. package/src/compiler/internal/RecipeConditions.ts +2 -2
  104. package/src/compiler/internal/Themes.ts +2 -0
  105. package/src/default.ts +175 -30
  106. package/src/internal/Condition.ts +3 -3
  107. package/src/internal/Configuration.ts +2 -2
  108. package/src/internal/Query.ts +3 -3
  109. package/src/internal/Theme.ts +12 -12
  110. package/src/internal/Token.ts +253 -33
  111. package/src/internal/Value.ts +40 -88
  112. package/src/internal/VariableSets.ts +28 -25
  113. package/src/unplugin/index.ts +16 -3
  114. package/src/vite/index.ts +138 -32
  115. package/src/web/colorProfile.ts +25 -10
  116. package/src/web/counterStyle.ts +29 -14
  117. package/src/web/cssFunction.ts +47 -24
  118. package/src/web/fontFace.ts +16 -3
  119. package/src/web/fontFeatureValues.ts +35 -18
  120. package/src/web/fontPaletteValues.ts +25 -11
  121. package/src/web/internal/Context.ts +9 -3
  122. package/src/web/keyframes.ts +24 -9
  123. package/src/web/page.ts +22 -5
  124. package/src/web/positionTry.ts +20 -5
  125. package/src/web/property.ts +23 -9
  126. package/src/web/viewTransition.ts +28 -13
package/src/vite/index.ts CHANGED
@@ -47,6 +47,18 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
47
47
  ReturnType<typeof Snapshot.create>
48
48
  >()
49
49
  const states = new WeakMap<Environment, Map<string, Entry>>()
50
+ const removed = new WeakMap<Environment, Set<string>>()
51
+ const reads = new WeakMap<Environment, Map<string, string>>()
52
+ const compilers = new WeakMap<Environment, Graph.create.ReturnType>()
53
+
54
+ function compiler(environment: Environment) {
55
+ let value = compilers.get(environment)
56
+ if (!value) {
57
+ value = Graph.create()
58
+ compilers.set(environment, value)
59
+ }
60
+ return value
61
+ }
50
62
  const discoveries = new WeakMap<Environment, Promise<Map<string, string>>>()
51
63
  const contributionFiles = new WeakMap<Environment, Set<string>>()
52
64
  const assets = new Map<string, string>()
@@ -55,6 +67,8 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
55
67
  const graphs = new Map<string, ReadonlySet<string>>()
56
68
  const initializers = new WeakMap<Environment, Entry>()
57
69
  const sourceEntrypoints = new Set<string>()
70
+ const identities = new Map<string, string>()
71
+ const lazyImports = new WeakMap<Parser.ParseResult, string[]>()
58
72
  let root: string
59
73
 
60
74
  function snapshot(environment: Environment) {
@@ -78,7 +92,12 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
78
92
  }
79
93
 
80
94
  function sourceId(file: string) {
81
- return `app/${Path.relative(root, file).split(Path.sep).join('/')}`
95
+ let id = identities.get(file)
96
+ if (!id) {
97
+ id = `app/${Path.relative(root, file).split(Path.sep).join('/')}`
98
+ identities.set(file, id)
99
+ }
100
+ return id
82
101
  }
83
102
 
84
103
  function resource(id: string) {
@@ -288,9 +307,19 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
288
307
  file: string,
289
308
  event: string,
290
309
  ) {
310
+ reads.delete(environment)
291
311
  if (event === 'delete') {
312
+ if (entries(environment).has(file)) {
313
+ let files = removed.get(environment)
314
+ if (!files) {
315
+ files = new Set()
316
+ removed.set(environment, files)
317
+ }
318
+ files.add(file)
319
+ }
292
320
  entries(environment).delete(file)
293
321
  snapshot(environment).delete(file, sourceId(file))
322
+ identities.delete(file)
294
323
  }
295
324
 
296
325
  const pending = discoveries.get(environment)
@@ -348,7 +377,17 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
348
377
  host.watch(file)
349
378
 
350
379
  const id = sourceId(file)
351
- const text = source ?? (await snapshot(entry.environment).read(file))
380
+ let cached = reads.get(entry.environment)
381
+ if (!cached) {
382
+ cached = new Map()
383
+ reads.set(entry.environment, cached)
384
+ }
385
+ // Full-project compilation must not reuse reads from concurrent module transforms.
386
+ const text =
387
+ source ??
388
+ (everything ? undefined : cached.get(file)) ??
389
+ (await snapshot(entry.environment).read(file))
390
+ if (!everything) cached.set(file, text)
352
391
 
353
392
  modules[id] = text
354
393
 
@@ -452,18 +491,22 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
452
491
  }
453
492
 
454
493
  // Lazy imports stay outside this compile but scope the documents that load them.
455
- const specifiers: string[] = []
456
-
457
- Walker.walk(parsed.program, {
458
- enter(node) {
459
- if (
460
- node.type === 'ImportExpression' &&
461
- node.source.type === 'Literal' &&
462
- typeof node.source.value === 'string'
463
- )
464
- specifiers.push(node.source.value)
465
- },
466
- })
494
+ let specifiers = lazyImports.get(parsed)
495
+ if (!specifiers) {
496
+ specifiers = []
497
+ const collected = specifiers
498
+ Walker.walk(parsed.program, {
499
+ enter(node) {
500
+ if (
501
+ node.type === 'ImportExpression' &&
502
+ node.source.type === 'Literal' &&
503
+ typeof node.source.value === 'string'
504
+ )
505
+ collected.push(node.source.value)
506
+ },
507
+ })
508
+ lazyImports.set(parsed, specifiers)
509
+ }
467
510
 
468
511
  const targets = new Set<string>()
469
512
 
@@ -490,8 +533,9 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
490
533
  const selected = contributionFiles.get(entry.environment)?.has(file)
491
534
 
492
535
  if (allSources) {
493
- const { program } = Parser.parseSync('source.tsx', source, {
494
- sourceType: 'module',
536
+ const { program } = snapshot(entry.environment).parse({
537
+ moduleId: sourceId(file),
538
+ source,
495
539
  })
496
540
  const dynamic: import('@oxc-project/types').ImportExpression[] = []
497
541
 
@@ -676,6 +720,8 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
676
720
  entry.files = files
677
721
  const map = Mapping.toEncodedMap(new Mapping.GenMapping())
678
722
  return {
723
+ imports,
724
+ modules: result.modules,
679
725
  code: output.code,
680
726
  contractIds: Object.keys(result.contracts),
681
727
  css: '',
@@ -917,6 +963,8 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
917
963
  entry.files = files
918
964
 
919
965
  return {
966
+ imports,
967
+ modules: result.modules,
920
968
  code: output.code,
921
969
  contractIds: Object.keys(compiled),
922
970
  css: styles.join('\n'),
@@ -935,6 +983,7 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
935
983
  /** Compiles every discovered source once so index.html can inline each configuration's script before modules load. */
936
984
  async function initializations(server: ViteDevServer) {
937
985
  const environment = server.environments.client
986
+ reads.delete(environment)
938
987
  const host: Host = {
939
988
  asset: async (file) => file,
940
989
  resolve: (source, importer) =>
@@ -950,7 +999,7 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
950
999
  if (file === undefined) return []
951
1000
 
952
1001
  entry = {
953
- compiler: Graph.create(),
1002
+ compiler: compiler(environment),
954
1003
  environment,
955
1004
  file,
956
1005
  files: new Set([file]),
@@ -1117,6 +1166,9 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
1117
1166
  }
1118
1167
 
1119
1168
  return {
1169
+ buildStart() {
1170
+ reads.delete(this.environment)
1171
+ },
1120
1172
  config: {
1121
1173
  order: 'post',
1122
1174
  handler(config) {
@@ -1261,27 +1313,73 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
1261
1313
  async watchChange(file, change) {
1262
1314
  await updateDiscovery(this.environment, file, change.event)
1263
1315
  },
1264
- async hotUpdate({ file, modules, timestamp, type }) {
1316
+ async hotUpdate({ file, modules, timestamp, type, server }) {
1265
1317
  await updateDiscovery(this.environment, file, type)
1266
1318
 
1267
- const affected = new Set(modules)
1319
+ const state = entries(this.environment)
1320
+ const first = [...state.values()].find(
1321
+ (entry) => entry.files.has(file) || eager(file),
1322
+ )
1323
+ if (!first && !removed.get(this.environment)?.has(file)) return modules
1324
+
1325
+ // Failed edits retain normal invalidation so Vite reports errors on reload.
1326
+ const output =
1327
+ first && type === 'update' && eligible(file)
1328
+ ? await compile(
1329
+ first,
1330
+ {
1331
+ resolve: (source, importer) =>
1332
+ this.environment.pluginContainer.resolveId(source, importer),
1333
+ watch: (file) => {
1334
+ server.watcher.add(file)
1335
+ },
1336
+ asset: async (file) => file,
1337
+ },
1338
+ undefined,
1339
+ true,
1340
+ true,
1341
+ ).catch(() => undefined)
1342
+ : undefined
1343
+ const affected = new Set(
1344
+ modules.filter((module) => {
1345
+ const entry = module.id && state.get(module.id)
1346
+ return (
1347
+ !output ||
1348
+ !entry ||
1349
+ entry.code !== output.modules[sourceId(entry.file)]?.code ||
1350
+ entry.imports !==
1351
+ JSON.stringify(output.imports[sourceId(entry.file)])
1352
+ )
1353
+ }),
1354
+ )
1355
+ const ids = new Set<string>()
1356
+ if (type === 'delete') ids.add(cssId(file))
1268
1357
 
1269
1358
  for (const entry of entries(this.environment).values()) {
1270
1359
  if (!entry.files.has(file) && !eager(file)) continue
1271
1360
 
1272
1361
  // Theme scopes affect CSS even when Vite's JavaScript import was erased.
1273
- for (const id of [entry.file, cssId(entry.file), sharedId]) {
1274
- const module = this.environment.moduleGraph.getModuleById(id)
1275
- if (!module) continue
1276
-
1277
- this.environment.moduleGraph.invalidateModule(
1278
- module,
1279
- new Set(),
1280
- timestamp,
1281
- true,
1282
- )
1283
- affected.add(module)
1284
- }
1362
+ if (
1363
+ !output ||
1364
+ entry.code !== output.modules[sourceId(entry.file)]?.code ||
1365
+ entry.imports !== JSON.stringify(output.imports[sourceId(entry.file)])
1366
+ )
1367
+ ids.add(entry.file)
1368
+ ids.add(cssId(entry.file))
1369
+ ids.add(sharedId)
1370
+ }
1371
+
1372
+ for (const id of ids) {
1373
+ const module = this.environment.moduleGraph.getModuleById(id)
1374
+ if (!module) continue
1375
+
1376
+ this.environment.moduleGraph.invalidateModule(
1377
+ module,
1378
+ new Set(),
1379
+ timestamp,
1380
+ true,
1381
+ )
1382
+ affected.add(module)
1285
1383
  }
1286
1384
 
1287
1385
  return [...affected]
@@ -1298,6 +1396,8 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
1298
1396
  file === undefined
1299
1397
  ? entries(this.environment).values().next().value
1300
1398
  : entries(this.environment).get(file)
1399
+ // A pruned JavaScript module can leave its stylesheet loaded in the browser.
1400
+ if (!entry && file && removed.get(this.environment)?.has(file)) return ''
1301
1401
  if (!entry) throw new Error(`Unknown Zyzz stylesheet: ${file}`)
1302
1402
 
1303
1403
  const output = await compile(
@@ -1353,12 +1453,13 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
1353
1453
 
1354
1454
  if (!entry) {
1355
1455
  entry = {
1356
- compiler: Graph.create(),
1456
+ compiler: compiler(this.environment),
1357
1457
  environment: this.environment,
1358
1458
  file: id,
1359
1459
  files: new Set([id]),
1360
1460
  }
1361
1461
  state.set(id, entry)
1462
+ removed.get(this.environment)?.delete(id)
1362
1463
  }
1363
1464
 
1364
1465
  const output = await compile(
@@ -1380,6 +1481,9 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
1380
1481
  code,
1381
1482
  )
1382
1483
 
1484
+ entry.imports = JSON.stringify(output.imports[sourceId(entry.file)])
1485
+ entry.code = output.code
1486
+
1383
1487
  if (native) return { code: output.code, map: JSON.stringify(output.map) }
1384
1488
 
1385
1489
  // Keep the CSS dependency even when the current graph has no live rules.
@@ -1479,6 +1583,8 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
1479
1583
  }
1480
1584
 
1481
1585
  type Entry = {
1586
+ code?: string
1587
+ imports?: string
1482
1588
  environment: Environment
1483
1589
  compiler: Graph.create.ReturnType
1484
1590
  file: string
@@ -5,16 +5,8 @@ import * as Identity from '../internal/Identity.js'
5
5
  import type * as RuleReference from '../internal/RuleReference.js'
6
6
 
7
7
  /** Emits static descriptors and returns a domain-specific CSS name. */
8
- export function colorProfile<
9
- const options extends Omit<colorProfile.Options, 'renderingIntent'> & {
10
- readonly renderingIntent?: string | undefined
11
- },
12
- >(
13
- options: options &
14
- Record<Exclude<keyof options, keyof colorProfile.Options>, never> &
15
- RuleReference.Checked<options> & {
16
- readonly renderingIntent?: Intent<options['renderingIntent']>
17
- },
8
+ export function colorProfile<const options extends Record<string, unknown>>(
9
+ options: options & NoInfer<Accepted<options>>,
18
10
  context: Context.Options = {},
19
11
  ): colorProfile.Reference {
20
12
  void options
@@ -58,3 +50,26 @@ type Keyword<value extends string> =
58
50
  : value extends `${infer rest}${' ' | '\t' | '\n' | '\r' | '\f'}`
59
51
  ? Keyword<rest>
60
52
  : Lexical.Fold<value>
53
+
54
+ type Accepted<input> = {
55
+ [key in keyof input as key extends Context.Group ? key : never]: Accepted<
56
+ input[key]
57
+ >
58
+ } & (keyof input extends never
59
+ ? Definition<input>
60
+ : Exclude<keyof input, Context.Group> extends never
61
+ ? unknown
62
+ : Definition<Omit<input, Context.Group>>)
63
+
64
+ type Definition<options> = options extends Omit<
65
+ colorProfile.Options,
66
+ 'renderingIntent'
67
+ > & {
68
+ readonly renderingIntent?: string | undefined
69
+ }
70
+ ? options &
71
+ Record<Exclude<keyof options, keyof colorProfile.Options>, never> &
72
+ RuleReference.Checked<options> & {
73
+ readonly renderingIntent?: Intent<options['renderingIntent']>
74
+ }
75
+ : never
@@ -5,20 +5,8 @@ import * as Identity from '../internal/Identity.js'
5
5
  import type * as RuleReference from '../internal/RuleReference.js'
6
6
 
7
7
  /** Emits static descriptors and returns a domain-specific CSS name. */
8
- export function counterStyle<
9
- const options extends Omit<counterStyle.Options, 'system'> & {
10
- readonly system?: string | undefined
11
- },
12
- >(
13
- options: options &
14
- Record<Exclude<keyof options, keyof counterStyle.Options>, never> &
15
- RuleReference.Checked<options> & {
16
- readonly system?: Checked<options['system']>
17
- } & (System<options['system']> extends 'additive'
18
- ? { readonly additiveSymbols: string }
19
- : System<options['system']> extends `extends ${string}`
20
- ? unknown
21
- : { readonly symbols: string }),
8
+ export function counterStyle<const options extends Record<string, unknown>>(
9
+ options: options & NoInfer<Accepted<options>>,
22
10
  context: Context.Options = {},
23
11
  ): counterStyle.Reference {
24
12
  void options
@@ -80,3 +68,30 @@ type Trim<value extends string> = value extends `${Space}${infer rest}`
80
68
  : value extends `${infer rest}${Space}`
81
69
  ? Trim<rest>
82
70
  : value
71
+
72
+ type Accepted<input> = {
73
+ [key in keyof input as key extends Context.Group ? key : never]: Accepted<
74
+ input[key]
75
+ >
76
+ } & (keyof input extends never
77
+ ? Definition<input>
78
+ : Exclude<keyof input, Context.Group> extends never
79
+ ? unknown
80
+ : Definition<Omit<input, Context.Group>>)
81
+
82
+ type Definition<options> = options extends Omit<
83
+ counterStyle.Options,
84
+ 'system'
85
+ > & {
86
+ readonly system?: string | undefined
87
+ }
88
+ ? options &
89
+ Record<Exclude<keyof options, keyof counterStyle.Options>, never> &
90
+ RuleReference.Checked<options> & {
91
+ readonly system?: Checked<options['system']>
92
+ } & (System<options['system']> extends 'additive'
93
+ ? { readonly additiveSymbols: string }
94
+ : System<options['system']> extends `extends ${string}`
95
+ ? unknown
96
+ : { readonly symbols: string })
97
+ : never
@@ -6,30 +6,14 @@ import type * as Literal from '../internal/Literal.js'
6
6
  import * as Identity from '../internal/Identity.js'
7
7
  import type * as Numeric from '../internal/Numeric.js'
8
8
  /** Emits a CSS function and returns a callable that formats its fixed CSS expression. */
9
- export function cssFunction<const options extends cssFunction.Options>(
10
- options: options &
11
- Record<Exclude<keyof options, keyof cssFunction.Options>, never> & {
12
- readonly body: Checked<options['body']>
13
- readonly parameters: {
14
- [index in keyof options['parameters']]: options['parameters'][index] extends {
15
- syntax: infer syntax extends string
16
- }
17
- ? options['parameters'][index] & {
18
- readonly syntax: FunctionSyntax.Checked<syntax>
19
- }
20
- : options['parameters'][index]
21
- }
22
- } & {
23
- readonly returns?: options extends {
24
- returns: infer syntax extends string
25
- }
26
- ? FunctionSyntax.Checked<syntax>
27
- : undefined
28
- },
9
+ export function cssFunction<const options extends Record<string, unknown>>(
10
+ options: options & NoInfer<Accepted<options>>,
29
11
  context: Context.Options = {},
30
12
  ): cssFunction.Reference<
31
- options['parameters'],
32
- options extends { returns: infer syntax extends cssFunction.Syntax }
13
+ Definitions<options>['parameters'],
14
+ Definitions<options> extends {
15
+ returns: infer syntax extends cssFunction.Syntax
16
+ }
33
17
  ? syntax
34
18
  : '*'
35
19
  > {
@@ -49,8 +33,10 @@ export function cssFunction<const options extends cssFunction.Options>(
49
33
  )
50
34
  .join(',') +
51
35
  ')') as cssFunction.Reference<
52
- options['parameters'],
53
- options extends { returns: infer syntax extends cssFunction.Syntax }
36
+ Definitions<options>['parameters'],
37
+ Definitions<options> extends {
38
+ returns: infer syntax extends cssFunction.Syntax
39
+ }
54
40
  ? syntax
55
41
  : '*'
56
42
  >
@@ -179,3 +165,40 @@ type InputSyntax<syntax extends string> =
179
165
  | '<transform-list>'
180
166
  ? string
181
167
  : syntax
168
+
169
+ type Accepted<input> = {
170
+ [key in keyof input as key extends Context.Group ? key : never]: Accepted<
171
+ input[key]
172
+ >
173
+ } & (keyof input extends never
174
+ ? Definition<input>
175
+ : Exclude<keyof input, Context.Group> extends never
176
+ ? unknown
177
+ : Definition<Omit<input, Context.Group>>)
178
+
179
+ type Definition<options> = options extends cssFunction.Options
180
+ ? options &
181
+ Record<Exclude<keyof options, keyof cssFunction.Options>, never> & {
182
+ readonly body: Checked<options['body']>
183
+ readonly parameters: {
184
+ [index in keyof options['parameters']]: options['parameters'][index] extends {
185
+ syntax: infer syntax extends string
186
+ }
187
+ ? options['parameters'][index] & {
188
+ readonly syntax: FunctionSyntax.Checked<syntax>
189
+ }
190
+ : options['parameters'][index]
191
+ }
192
+ } & {
193
+ readonly returns?: options extends {
194
+ returns: infer syntax extends string
195
+ }
196
+ ? FunctionSyntax.Checked<syntax>
197
+ : undefined
198
+ }
199
+ : never
200
+
201
+ type Definitions<input> = Extract<
202
+ Context.Definitions<input>,
203
+ cssFunction.Options
204
+ >
@@ -2,9 +2,8 @@
2
2
  import type * as Context from './internal/Context.js'
3
3
 
4
4
  /** Compiles literal font-face descriptors into the initial stylesheet. */
5
- export function fontFace<const options extends fontFace.Options>(
6
- options: options &
7
- Record<Exclude<keyof options, keyof fontFace.Options>, never>,
5
+ export function fontFace<const options extends Record<string, unknown>>(
6
+ options: options & NoInfer<Accepted<options>>,
8
7
  context: Context.Options = {},
9
8
  ): void {
10
9
  void context
@@ -39,3 +38,17 @@ export declare namespace fontFace {
39
38
  readonly lineGapOverride?: 'normal' | `${number}%` | undefined
40
39
  }
41
40
  }
41
+
42
+ type Accepted<input> = {
43
+ [key in keyof input as key extends Context.Group ? key : never]: Accepted<
44
+ input[key]
45
+ >
46
+ } & (keyof input extends never
47
+ ? Definition<input>
48
+ : Exclude<keyof input, Context.Group> extends never
49
+ ? unknown
50
+ : Definition<Omit<input, Context.Group>>)
51
+
52
+ type Definition<options> = options extends fontFace.Options
53
+ ? options & Record<Exclude<keyof options, keyof fontFace.Options>, never>
54
+ : never
@@ -4,25 +4,9 @@ import type * as Lexical from '../internal/Lexical.js'
4
4
 
5
5
  /** Emits eager font feature aliases in authored block and declaration order. */
6
6
  export function fontFeatureValues<
7
- const options extends Omit<fontFeatureValues.Options, 'fontDisplay'> & {
8
- readonly fontDisplay?: string | undefined
9
- },
7
+ const options extends Record<string, unknown>,
10
8
  >(
11
- options: options &
12
- Record<Exclude<keyof options, keyof fontFeatureValues.Options>, never> & {
13
- readonly fontDisplay?: Checked<
14
- options['fontDisplay'],
15
- Exclude<fontFeatureValues.Options['fontDisplay'], undefined>
16
- >
17
- } & {
18
- readonly features: Record<
19
- Exclude<
20
- keyof options['features'],
21
- keyof fontFeatureValues.Options['features']
22
- >,
23
- never
24
- >
25
- },
9
+ options: options & NoInfer<Accepted<options>>,
26
10
  context: Context.Options = {},
27
11
  ): void {
28
12
  void options
@@ -77,3 +61,36 @@ type Checked<value, domain> = value extends string
77
61
  ? value
78
62
  : never
79
63
  : undefined
64
+
65
+ type Accepted<input> = {
66
+ [key in keyof input as key extends Context.Group ? key : never]: Accepted<
67
+ input[key]
68
+ >
69
+ } & (keyof input extends never
70
+ ? Definition<input>
71
+ : Exclude<keyof input, Context.Group> extends never
72
+ ? unknown
73
+ : Definition<Omit<input, Context.Group>>)
74
+
75
+ type Definition<options> = options extends Omit<
76
+ fontFeatureValues.Options,
77
+ 'fontDisplay'
78
+ > & {
79
+ readonly fontDisplay?: string | undefined
80
+ }
81
+ ? options &
82
+ Record<Exclude<keyof options, keyof fontFeatureValues.Options>, never> & {
83
+ readonly fontDisplay?: Checked<
84
+ options['fontDisplay'],
85
+ Exclude<fontFeatureValues.Options['fontDisplay'], undefined>
86
+ >
87
+ } & {
88
+ readonly features: Record<
89
+ Exclude<
90
+ keyof options['features'],
91
+ keyof fontFeatureValues.Options['features']
92
+ >,
93
+ never
94
+ >
95
+ }
96
+ : never
@@ -5,18 +5,9 @@ import type * as Context from './internal/Context.js'
5
5
 
6
6
  /** Emits static descriptors and returns a domain-specific CSS name. */
7
7
  export function fontPaletteValues<
8
- const options extends fontPaletteValues.Options,
8
+ const options extends Record<string, unknown>,
9
9
  >(
10
- options: options &
11
- Record<Exclude<keyof options, keyof fontPaletteValues.Options>, never> &
12
- RuleReference.Checked<options> &
13
- (options extends { basePalette: infer value extends number }
14
- ? `${value}` extends `${bigint}`
15
- ? `${value}` extends `-${string}`
16
- ? never
17
- : unknown
18
- : never
19
- : unknown),
10
+ options: options & NoInfer<Accepted<options>>,
20
11
  context: Context.Options = {},
21
12
  ): fontPaletteValues.Reference {
22
13
  void options
@@ -40,3 +31,26 @@ export declare namespace fontPaletteValues {
40
31
  /** Compiler-owned reference usable in the matching CSS domain. */
41
32
  type Reference = RuleReference.Reference<'fontPaletteValues'>
42
33
  }
34
+
35
+ type Accepted<input> = {
36
+ [key in keyof input as key extends Context.Group ? key : never]: Accepted<
37
+ input[key]
38
+ >
39
+ } & (keyof input extends never
40
+ ? Definition<input>
41
+ : Exclude<keyof input, Context.Group> extends never
42
+ ? unknown
43
+ : Definition<Omit<input, Context.Group>>)
44
+
45
+ type Definition<options> = options extends fontPaletteValues.Options
46
+ ? options &
47
+ Record<Exclude<keyof options, keyof fontPaletteValues.Options>, never> &
48
+ RuleReference.Checked<options> &
49
+ (options extends { basePalette: infer value extends number }
50
+ ? `${value}` extends `${bigint}`
51
+ ? `${value}` extends `-${string}`
52
+ ? never
53
+ : unknown
54
+ : never
55
+ : unknown)
56
+ : never
@@ -7,10 +7,16 @@ export type Group =
7
7
  | `@layer ${string}`
8
8
  | `@media ${string}`
9
9
  | `@supports ${string}`
10
- /** Ordered enclosing groups, from outermost to innermost. */
10
+ /** Identity options for stylesheet declarations. */
11
11
  export type Options = {
12
12
  /** Explicit identity for named declarations without source rewriting. */
13
13
  readonly id?: string | undefined
14
- /** Enclosing groups; omitted means stylesheet scope. */
15
- readonly within?: readonly Group[] | undefined
16
14
  }
15
+
16
+ /** Complete definitions nested inside enclosing group keys. */
17
+ export type Definitions<input> =
18
+ input extends Record<string, unknown>
19
+ ? Exclude<keyof input, Group> extends never
20
+ ? Definitions<input[keyof input]>
21
+ : Omit<input, Group>
22
+ : never