zyzz 0.0.18 → 0.0.20

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 (120) 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/Vars.d.ts +22 -15
  6. package/dist/Vars.d.ts.map +1 -1
  7. package/dist/Vars.js +26 -0
  8. package/dist/Vars.js.map +1 -1
  9. package/dist/cli/index.js +5 -0
  10. package/dist/cli/index.js.map +1 -1
  11. package/dist/compiler/Source.d.ts.map +1 -1
  12. package/dist/compiler/Source.js +3 -0
  13. package/dist/compiler/Source.js.map +1 -1
  14. package/dist/compiler/internal/Configurations.d.ts.map +1 -1
  15. package/dist/compiler/internal/Configurations.js +3 -0
  16. package/dist/compiler/internal/Configurations.js.map +1 -1
  17. package/dist/compiler/internal/Contract.d.ts.map +1 -1
  18. package/dist/compiler/internal/Contract.js +30 -4
  19. package/dist/compiler/internal/Contract.js.map +1 -1
  20. package/dist/compiler/internal/Themes.d.ts.map +1 -1
  21. package/dist/compiler/internal/Themes.js +83 -23
  22. package/dist/compiler/internal/Themes.js.map +1 -1
  23. package/dist/default.d.ts +7 -11
  24. package/dist/default.d.ts.map +1 -1
  25. package/dist/default.js +4 -5
  26. package/dist/default.js.map +2 -2
  27. package/dist/default.js.zyzz.json +1 -1
  28. package/dist/index.d.ts +15 -0
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +15 -0
  31. package/dist/index.js.map +1 -1
  32. package/dist/internal/Token.d.ts +11 -2
  33. package/dist/internal/Token.d.ts.map +1 -1
  34. package/dist/internal/Token.js +2 -0
  35. package/dist/internal/Token.js.map +1 -1
  36. package/dist/internal/VariableSets.d.ts.map +1 -1
  37. package/dist/internal/VariableSets.js +18 -1
  38. package/dist/internal/VariableSets.js.map +1 -1
  39. package/dist/node/Host.d.ts +2 -0
  40. package/dist/node/Host.d.ts.map +1 -1
  41. package/dist/node/Host.js +7 -0
  42. package/dist/node/Host.js.map +1 -1
  43. package/dist/oxlint/index.d.ts +6 -0
  44. package/dist/oxlint/index.d.ts.map +1 -0
  45. package/dist/oxlint/index.js +18 -0
  46. package/dist/oxlint/index.js.map +1 -0
  47. package/dist/oxlint/internal/Bindings.d.ts +20 -0
  48. package/dist/oxlint/internal/Bindings.d.ts.map +1 -0
  49. package/dist/oxlint/internal/Bindings.js +203 -0
  50. package/dist/oxlint/internal/Bindings.js.map +1 -0
  51. package/dist/oxlint/internal/Declarations.d.ts +19 -0
  52. package/dist/oxlint/internal/Declarations.d.ts.map +1 -0
  53. package/dist/oxlint/internal/Declarations.js +114 -0
  54. package/dist/oxlint/internal/Declarations.js.map +1 -0
  55. package/dist/oxlint/rules/noConflictingProps.d.ts +5 -0
  56. package/dist/oxlint/rules/noConflictingProps.d.ts.map +1 -0
  57. package/dist/oxlint/rules/noConflictingProps.js +45 -0
  58. package/dist/oxlint/rules/noConflictingProps.js.map +1 -0
  59. package/dist/oxlint/rules/noUnused.d.ts +5 -0
  60. package/dist/oxlint/rules/noUnused.d.ts.map +1 -0
  61. package/dist/oxlint/rules/noUnused.js +62 -0
  62. package/dist/oxlint/rules/noUnused.js.map +1 -0
  63. package/dist/oxlint/rules/restrictedProperties.d.ts +5 -0
  64. package/dist/oxlint/rules/restrictedProperties.d.ts.map +1 -0
  65. package/dist/oxlint/rules/restrictedProperties.js +82 -0
  66. package/dist/oxlint/rules/restrictedProperties.js.map +1 -0
  67. package/dist/oxlint/rules/useLogicalProperties.d.ts +5 -0
  68. package/dist/oxlint/rules/useLogicalProperties.d.ts.map +1 -0
  69. package/dist/oxlint/rules/useLogicalProperties.js +76 -0
  70. package/dist/oxlint/rules/useLogicalProperties.js.map +1 -0
  71. package/dist/oxlint/rules/validStyles.d.ts +5 -0
  72. package/dist/oxlint/rules/validStyles.d.ts.map +1 -0
  73. package/dist/oxlint/rules/validStyles.js +75 -0
  74. package/dist/oxlint/rules/validStyles.js.map +1 -0
  75. package/dist/react-native/StyleSheet.js +2 -0
  76. package/dist/react-native/StyleSheet.js.map +1 -1
  77. package/dist/runtime/Composition.d.ts +1 -1
  78. package/dist/runtime/ConditionalRecipe.d.ts +1 -1
  79. package/dist/runtime/PayloadRecipe.d.ts +1 -1
  80. package/dist/runtime/Recipe.d.ts +1 -1
  81. package/dist/styleFunction.d.ts +1 -1
  82. package/dist/styleFunction.d.ts.map +1 -1
  83. package/dist/styleFunction.js.map +1 -1
  84. package/dist/variants.d.ts +14 -6
  85. package/dist/variants.d.ts.map +1 -1
  86. package/dist/variants.js.map +1 -1
  87. package/dist/vite/index.d.ts +2 -0
  88. package/dist/vite/index.d.ts.map +1 -1
  89. package/dist/vite/index.js +75 -30
  90. package/dist/vite/index.js.map +1 -1
  91. package/dist/web/internal/Themes.d.ts.map +1 -1
  92. package/dist/web/internal/Themes.js +4 -0
  93. package/dist/web/internal/Themes.js.map +1 -1
  94. package/package.json +7 -1
  95. package/src/Config.ts +5 -0
  96. package/src/Vars.ts +88 -29
  97. package/src/cli/index.ts +5 -0
  98. package/src/compiler/Source.ts +3 -0
  99. package/src/compiler/internal/Configurations.ts +3 -0
  100. package/src/compiler/internal/Contract.ts +47 -4
  101. package/src/compiler/internal/Themes.ts +109 -34
  102. package/src/default.ts +8 -10
  103. package/src/index.ts +15 -0
  104. package/src/internal/Authoring.ts +1 -1
  105. package/src/internal/Token.ts +39 -11
  106. package/src/internal/VariableSets.ts +25 -1
  107. package/src/node/Host.ts +19 -0
  108. package/src/oxlint/index.ts +21 -0
  109. package/src/oxlint/internal/Bindings.ts +270 -0
  110. package/src/oxlint/internal/Declarations.ts +137 -0
  111. package/src/oxlint/rules/noConflictingProps.ts +52 -0
  112. package/src/oxlint/rules/noUnused.ts +67 -0
  113. package/src/oxlint/rules/restrictedProperties.ts +92 -0
  114. package/src/oxlint/rules/useLogicalProperties.ts +81 -0
  115. package/src/oxlint/rules/validStyles.ts +88 -0
  116. package/src/react-native/StyleSheet.ts +6 -0
  117. package/src/styleFunction.ts +1 -3
  118. package/src/variants.ts +38 -16
  119. package/src/vite/index.ts +94 -33
  120. package/src/web/internal/Themes.ts +4 -0
package/src/Vars.ts CHANGED
@@ -7,9 +7,16 @@ import * as Token from './internal/Token.js'
7
7
  import * as VariableSets from './internal/VariableSets.js'
8
8
  import type { style } from './styleFunction.js'
9
9
 
10
- /** Portable scalar reference with its accepted value domain. */
11
- export type Reference<group extends Token.Group = Token.Group> =
12
- Token.Reference<group>
10
+ export type { Composition } from './internal/Token.js'
11
+
12
+ /** Portable scalar reference retaining its value domain and optional literal precision. */
13
+ export type Reference<
14
+ group extends Token.Group = Token.Group,
15
+ scalar = never,
16
+ > = Token.Reference<group> &
17
+ ([scalar] extends [never]
18
+ ? { readonly [Token.scalar]?: unknown }
19
+ : { readonly [Token.scalar]: scalar })
13
20
 
14
21
  const shape = Symbol('zyzz.variables.shape')
15
22
 
@@ -18,9 +25,10 @@ export type Value =
18
25
  | string
19
26
  | number
20
27
  | Token.Reference
28
+ | Token.Composition
21
29
  | {
22
- readonly light: string | Token.Reference
23
- readonly dark: string | Token.Reference
30
+ readonly light: string | Token.Reference | Token.Composition<'color'>
31
+ readonly dark: string | Token.Reference | Token.Composition<'color'>
24
32
  }
25
33
  | ({ readonly default: Value } & {
26
34
  readonly [query: `@media ${string}`]: Value
@@ -47,32 +55,41 @@ export type References<values, root extends boolean = true> = {
47
55
  keyof values,
48
56
  root extends true ? 'breakpoint' | 'containerNames' : never
49
57
  >]: values[key] extends Value
50
- ? Token.Reference<Domain<Scalar<values[key]>>> & {
51
- readonly [Token.scalar]: Scalar<values[key]>
52
- }
58
+ ? Reference<
59
+ Domain<Scalar<values[key]>>,
60
+ Literal.Color extends Scalar<values[key]>
61
+ ? never
62
+ : Literal.Length extends Scalar<values[key]>
63
+ ? never
64
+ : Scalar<values[key]>
65
+ >
53
66
  : References<values[key], false>
54
67
  }
55
68
 
56
69
  /** Resolves the scalar types represented by all conditional branches. */
57
70
  export type Scalar<value> = Value extends value
58
71
  ? string | number
59
- : value extends { readonly [Token.scalar]: infer scalar }
60
- ? scalar
61
- : value extends readonly string[]
62
- ? value
63
- : value extends Token.Reference<infer group>
64
- ? group extends 'color'
65
- ? Literal.Color
66
- : group extends 'spacing'
67
- ? Literal.Length
68
- : group extends 'number'
69
- ? number
70
- : string
71
- : value extends { default: unknown }
72
- ? Scalar<value[keyof value]>
73
- : value extends { light: infer light; dark: infer dark }
74
- ? Scalar<light | dark>
75
- : value
72
+ : value extends Token.Composition<infer group>
73
+ ? group extends 'color'
74
+ ? Literal.Color
75
+ : Literal.Length
76
+ : value extends { readonly [Token.scalar]: infer scalar }
77
+ ? scalar
78
+ : value extends readonly string[]
79
+ ? value
80
+ : value extends Token.Reference<infer group>
81
+ ? group extends 'color'
82
+ ? Literal.Color
83
+ : group extends 'spacing'
84
+ ? Literal.Length
85
+ : group extends 'number'
86
+ ? number
87
+ : string
88
+ : value extends { default: unknown }
89
+ ? Scalar<value[keyof value]>
90
+ : value extends { light: infer light; dark: infer dark }
91
+ ? Scalar<light | dark>
92
+ : value
76
93
 
77
94
  /** Scalar domain shared by compatible set alternatives. */
78
95
  export type Domain<value> = [value] extends [Literal.Color]
@@ -83,6 +100,41 @@ export type Domain<value> = [value] extends [Literal.Color]
83
100
  ? 'number'
84
101
  : 'string'
85
102
 
103
+ /**
104
+ * Composes immutable web CSS text and live references in a color or spacing domain.
105
+ * @throws {InvalidError} If parts are empty, nonfinite, contain declaration markers, or include unsupported values.
106
+ */
107
+ export function compose<group extends 'color' | 'spacing'>(
108
+ group: group,
109
+ parts: readonly (string | number | Reference)[],
110
+ ): Token.Composition<group> {
111
+ if (group !== 'color' && group !== 'spacing')
112
+ throw new InvalidError(
113
+ [],
114
+ 'Compositions require a color or spacing domain.',
115
+ )
116
+ if (!Array.isArray(parts) || !parts.length)
117
+ throw new InvalidError([], 'Compositions require nonempty CSS parts.')
118
+
119
+ const values = Array.from(parts, (part) => {
120
+ if (Token.is(part)) return part
121
+ if (typeof part === 'number' && Number.isFinite(part)) return String(part)
122
+ if (typeof part === 'string' && !/[;{}!]/.test(part)) return part
123
+ throw new InvalidError(
124
+ [],
125
+ 'Composition parts must be CSS text, finite numbers, or variable references.',
126
+ )
127
+ })
128
+ if (!values.some((part) => typeof part !== 'string' || part.trim()))
129
+ throw new InvalidError([], 'Compositions require nonempty CSS parts.')
130
+
131
+ return Object.freeze({
132
+ [Token.expression]: true as const,
133
+ group,
134
+ parts: Object.freeze(values),
135
+ })
136
+ }
137
+
86
138
  /** Creates typed references without emitting CSS or reading the environment. */
87
139
  export function define<const values extends Values>(
88
140
  values: values & NoInfer<Validated<values, false, true>>,
@@ -166,12 +218,19 @@ type Compatible<scalar> = [scalar] extends [Literal.Color]
166
218
  ?
167
219
  | Literal.Color
168
220
  | Token.Reference<'color'>
221
+ | Token.Composition<'color'>
169
222
  | {
170
- readonly light: Literal.Color | Token.Reference<'color'>
171
- readonly dark: Literal.Color | Token.Reference<'color'>
223
+ readonly light:
224
+ | Literal.Color
225
+ | Token.Reference<'color'>
226
+ | Token.Composition<'color'>
227
+ readonly dark:
228
+ | Literal.Color
229
+ | Token.Reference<'color'>
230
+ | Token.Composition<'color'>
172
231
  }
173
232
  : [scalar] extends [Literal.Length]
174
- ? Literal.Length | Token.Reference<'spacing'>
233
+ ? Literal.Length | Token.Reference<'spacing'> | Token.Composition<'spacing'>
175
234
  : [scalar] extends [number]
176
235
  ? number | Token.Reference<'number'>
177
236
  : string | Token.Reference<'string'>
@@ -221,7 +280,7 @@ type Validated<
221
280
  ? value
222
281
  : value extends readonly string[]
223
282
  ? never
224
- : value extends Token.Reference
283
+ : value extends Token.Reference | Token.Composition
225
284
  ? value
226
285
  : value extends string | number
227
286
  ? Literal.Checked<value>
package/src/cli/index.ts CHANGED
@@ -25,6 +25,10 @@ const options = z.object({
25
25
  .boolean()
26
26
  .default(false)
27
27
  .describe('Emit only CSS and CSS maps without source transformation'),
28
+ external: z
29
+ .array(z.string())
30
+ .optional()
31
+ .describe('External import name or trailing * prefix (repeatable)'),
28
32
  minify: z.boolean().default(false).describe('Minify emitted CSS'),
29
33
  'out-dir': z.string().default('dist').describe('Output directory'),
30
34
  'package-id': z
@@ -178,6 +182,7 @@ async function open(context: open.Context) {
178
182
  : {}),
179
183
  compiler: !context.options['css-only'],
180
184
  css: { minify: context.options.minify },
185
+ external: context.options.external,
181
186
  modules: !context.options['css-only'],
182
187
  outDir: Path.resolve(context.options['out-dir']),
183
188
  packageId,
@@ -375,6 +375,9 @@ export function extract(options: extract.Options): extract.ReturnType {
375
375
  if (
376
376
  name === 'Config' ||
377
377
  name === 'cx' ||
378
+ name === 'defineConfig' ||
379
+ name === 'defineVars' ||
380
+ name === 'extendVars' ||
378
381
  name === 'style' ||
379
382
  name === 'Theme' ||
380
383
  name === 'Vars' ||
@@ -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 }
@@ -294,6 +295,8 @@ export declare namespace collect {
294
295
 
295
296
  /** Encodes validated literal options as a declaration type, never executable text. */
296
297
  export function type(value: unknown): string {
298
+ if (Token.isExpression(value) && 'group' in value)
299
+ return `import('zyzz').Vars.Composition<'${value.group}'>`
297
300
  if (Token.is(value)) return `import('zyzz').Vars.Reference<'${value.group}'>`
298
301
  if (Array.isArray(value)) return `readonly [${value.map(type).join(',')}]`
299
302
  if (!value || typeof value !== 'object') return JSON.stringify(value)
@@ -26,7 +26,7 @@ export function read(
26
26
  if (
27
27
  ![
28
28
  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
29
- 22, 23, 24, 25, 26, 27, 28,
29
+ 22, 23, 24, 25, 26, 27, 28, 29,
30
30
  ].includes(data.version as number)
31
31
  )
32
32
  throw new Error('Unsupported Zyzz contract version.')
@@ -69,6 +69,26 @@ export function read(
69
69
  decode(value),
70
70
  ]),
71
71
  )
72
+ if (Object.hasOwn(fields, '$composition')) {
73
+ if ((data.version as number) < 29)
74
+ throw new Error(
75
+ 'Composed variables require contract version 29 or later.',
76
+ )
77
+ const composition = record(fields.$composition)
78
+ if (
79
+ (composition.group !== 'color' && composition.group !== 'spacing') ||
80
+ !Array.isArray(composition.parts)
81
+ )
82
+ throw new Error('Invalid packed variable composition.')
83
+ return Vars.compose(
84
+ composition.group,
85
+ composition.parts.map(decode) as readonly (
86
+ | string
87
+ | number
88
+ | Vars.Reference
89
+ )[],
90
+ )
91
+ }
72
92
  if (Object.hasOwn(fields, '$variable')) {
73
93
  const reference = record(fields.$variable)
74
94
  const identity = string(reference.identity)
@@ -294,7 +314,7 @@ export function read(
294
314
  if (
295
315
  ![
296
316
  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
297
- 27, 28,
317
+ 27, 28, 29,
298
318
  ].includes(data.version as number) ||
299
319
  ![
300
320
  'cssFunction',
@@ -590,7 +610,8 @@ function tokens(tree: Theme.References<Theme.Tokens>): Record<string, unknown> {
590
610
  }
591
611
 
592
612
  function type(value: unknown): string {
593
- if (Token.is(value)) return Configurations.type(value)
613
+ if (Token.is(value) || Token.isExpression(value))
614
+ return Configurations.type(value)
594
615
  if (Array.isArray(value)) return `readonly [${value.map(type).join(',')}]`
595
616
  if (!value || typeof value !== 'object') return JSON.stringify(value)
596
617
 
@@ -727,6 +748,22 @@ export function write(
727
748
  ]),
728
749
  ),
729
750
  version: (() => {
751
+ function composed(value: unknown): boolean {
752
+ if (Token.isExpression(value)) return true
753
+ if (Token.is(value)) return composed(value.value)
754
+ return (
755
+ !!value &&
756
+ typeof value === 'object' &&
757
+ Object.values(value).some(composed)
758
+ )
759
+ }
760
+ if (
761
+ Object.values(themes).some((theme) =>
762
+ composed(theme[Token.definition].values),
763
+ )
764
+ )
765
+ return 29
766
+
730
767
  if (
731
768
  Object.values(themes).some(
732
769
  (theme) =>
@@ -1031,6 +1068,10 @@ function extended(signature: NonNullable<Themes.Call['function']>): boolean {
1031
1068
  }
1032
1069
 
1033
1070
  function encode(value: unknown): unknown {
1071
+ if (Token.isExpression(value) && 'group' in value)
1072
+ return {
1073
+ $composition: { group: value.group, parts: value.parts.map(encode) },
1074
+ }
1034
1075
  if (Token.is(value))
1035
1076
  return {
1036
1077
  $variable: {
@@ -1044,7 +1085,9 @@ function encode(value: unknown): unknown {
1044
1085
  const fields = Object.fromEntries(
1045
1086
  Object.entries(value).map(([key, value]) => [key, encode(value)]),
1046
1087
  )
1047
- return Object.hasOwn(fields, '$variable') || Object.hasOwn(fields, '$object')
1088
+ return Object.hasOwn(fields, '$composition') ||
1089
+ Object.hasOwn(fields, '$variable') ||
1090
+ Object.hasOwn(fields, '$object')
1048
1091
  ? { $object: fields }
1049
1092
  : fields
1050
1093
  }
@@ -144,6 +144,7 @@ export function collect(program: Ast.Program, options: collect.Options) {
144
144
  const imports = new Set<number>()
145
145
  const variableImports = new Set<number>()
146
146
  const configImports = new Set<number>()
147
+ const factoryImports = new Map<number, 'create' | 'define' | 'extend'>()
147
148
  const configs = new Map<string, Link>()
148
149
  const configBindings = new Map<number, Link>()
149
150
  const factoryReferences = new Set<number>()
@@ -188,11 +189,31 @@ export function collect(program: Ast.Program, options: collect.Options) {
188
189
  : specifier.imported.value) === 'Config'
189
190
  )
190
191
  configImports.add(specifier.start)
192
+ else if (
193
+ specifier.type === 'ImportSpecifier' &&
194
+ specifier.importKind !== 'type' &&
195
+ ['defineConfig', 'defineVars', 'extendVars'].includes(
196
+ specifier.imported.type === 'Identifier'
197
+ ? specifier.imported.name
198
+ : specifier.imported.value,
199
+ )
200
+ ) {
201
+ const name =
202
+ specifier.imported.type === 'Identifier'
203
+ ? specifier.imported.name
204
+ : specifier.imported.value
205
+ if (name === 'defineConfig')
206
+ factoryImports.set(specifier.start, 'create')
207
+ else if (name === 'defineVars')
208
+ factoryImports.set(specifier.start, 'define')
209
+ else factoryImports.set(specifier.start, 'extend')
210
+ }
191
211
  }
192
212
 
193
213
  if (
194
214
  !imports.size &&
195
215
  !configImports.size &&
216
+ !factoryImports.size &&
196
217
  !Object.keys(options.links ?? {}).length
197
218
  )
198
219
  return undefined
@@ -251,6 +272,7 @@ export function collect(program: Ast.Program, options: collect.Options) {
251
272
 
252
273
  const namespaces = new Set<string>()
253
274
  const configNamespaces = new Set<string>()
275
+ const factoryFunctions = new Map<string, 'create' | 'define' | 'extend'>()
254
276
  const variableNamespaces = new Set<string>()
255
277
  for (const node of program.body)
256
278
  if (node.type === 'ImportDeclaration')
@@ -264,6 +286,11 @@ export function collect(program: Ast.Program, options: collect.Options) {
264
286
  if (imports.has(specifier.start)) namespaces.add(specifier.local.name)
265
287
  else if (configImports.has(specifier.start))
266
288
  configNamespaces.add(specifier.local.name)
289
+ else if (factoryImports.has(specifier.start))
290
+ factoryFunctions.set(
291
+ specifier.local.name,
292
+ factoryImports.get(specifier.start)!,
293
+ )
267
294
 
268
295
  function resolve(node: Ast.Node): Link | undefined {
269
296
  if (node.type === 'Identifier') {
@@ -426,6 +453,35 @@ export function collect(program: Ast.Program, options: collect.Options) {
426
453
  if (node.type === 'TSAsExpression' || node.type === 'TSSatisfiesExpression')
427
454
  return data(node.expression, derived)
428
455
 
456
+ if (node.type === 'CallExpression' && !node.optional) {
457
+ const callee = node.callee
458
+ if (
459
+ callee.type === 'MemberExpression' &&
460
+ !callee.computed &&
461
+ !callee.optional &&
462
+ callee.object.type === 'Identifier' &&
463
+ variableNamespaces.has(callee.object.name) &&
464
+ derived?.name !== callee.object.name &&
465
+ callee.property.type === 'Identifier' &&
466
+ callee.property.name === 'compose'
467
+ ) {
468
+ if (node.arguments.length !== 2)
469
+ return fail(
470
+ 'Vars.compose requires a literal domain and parts array.',
471
+ node,
472
+ )
473
+ const group = data(node.arguments[0]!, derived)
474
+ const parts = data(node.arguments[1]!, derived)
475
+ if ((group !== 'color' && group !== 'spacing') || !Array.isArray(parts))
476
+ return fail(
477
+ 'Vars.compose requires a literal domain and parts array.',
478
+ node,
479
+ )
480
+ factoryReferences.add(callee.start)
481
+ return Vars.compose(group, parts)
482
+ }
483
+ }
484
+
429
485
  if (
430
486
  node.type === 'Literal' &&
431
487
  (typeof node.value === 'string' || typeof node.value === 'number')
@@ -536,37 +592,52 @@ export function collect(program: Ast.Program, options: collect.Options) {
536
592
  registerAlias({ declaration, statement, variable })
537
593
 
538
594
  const expression = variable.init
539
- if (
540
- expression?.type !== 'CallExpression' ||
541
- expression.callee.type !== 'MemberExpression' ||
542
- expression.callee.object.type !== 'Identifier' ||
543
- (!namespaces.has(expression.callee.object.name) &&
544
- !configNamespaces.has(expression.callee.object.name))
545
- )
546
- continue
595
+ if (expression?.type !== 'CallExpression' || expression.optional) continue
547
596
 
548
597
  const member = expression.callee
598
+ const directFactory =
599
+ member.type === 'Identifier'
600
+ ? factoryFunctions.get(member.name)
601
+ : undefined
549
602
  if (
550
- member.computed ||
551
- member.optional ||
552
- expression.optional ||
553
- member.property.type !== 'Identifier' ||
554
- !(
555
- configNamespaces.has(expression.callee.object.name)
556
- ? ['create']
557
- : ['define', 'extend']
558
- ).includes(member.property.name)
603
+ !directFactory &&
604
+ (member.type !== 'MemberExpression' ||
605
+ member.object.type !== 'Identifier' ||
606
+ (!namespaces.has(member.object.name) &&
607
+ !configNamespaces.has(member.object.name)) ||
608
+ member.computed ||
609
+ member.optional ||
610
+ member.property.type !== 'Identifier' ||
611
+ !(
612
+ configNamespaces.has(member.object.name)
613
+ ? ['create']
614
+ : ['define', 'extend']
615
+ ).includes(member.property.name))
559
616
  )
560
617
  continue
561
618
 
619
+ const config =
620
+ directFactory === 'create' ||
621
+ (member.type === 'MemberExpression' &&
622
+ member.object.type === 'Identifier' &&
623
+ configNamespaces.has(member.object.name))
624
+ const method =
625
+ member.type === 'MemberExpression' &&
626
+ member.property.type === 'Identifier'
627
+ ? member.property.name
628
+ : directFactory
629
+ const variableSet =
630
+ directFactory === 'define' ||
631
+ directFactory === 'extend' ||
632
+ (member.type === 'MemberExpression' &&
633
+ member.object.type === 'Identifier' &&
634
+ variableNamespaces.has(member.object.name))
635
+
562
636
  if (
563
637
  (statement.type === 'ExportNamedDeclaration' && !options.linked) ||
564
638
  declaration.kind !== 'const' ||
565
639
  (variable.id.type !== 'Identifier' &&
566
- !(
567
- configNamespaces.has(expression.callee.object.name) &&
568
- variable.id.type === 'ObjectPattern'
569
- ))
640
+ !(config && variable.id.type === 'ObjectPattern'))
570
641
  )
571
642
  fail(
572
643
  'Define local themes with a module-level const; exported themes require source linking.',
@@ -601,10 +672,7 @@ export function collect(program: Ast.Program, options: collect.Options) {
601
672
  fail('Configuration destructuring requires a binding.', variable)
602
673
 
603
674
  let name = `${options.namespace}-${binding}`
604
- if (
605
- !configNamespaces.has(expression.callee.object.name) &&
606
- member.property.name === 'define'
607
- ) {
675
+ if (!config && method === 'define') {
608
676
  const argument = expression.arguments[1]
609
677
  const callback = argument && Expression.unwrap(argument)
610
678
  const id = Identifiers.explicit(
@@ -617,7 +685,7 @@ export function collect(program: Ast.Program, options: collect.Options) {
617
685
  if (id !== undefined) name = Identity.requireId(id, 'Vars.define')
618
686
  }
619
687
 
620
- if (configNamespaces.has(expression.callee.object.name)) {
688
+ if (config) {
621
689
  try {
622
690
  const link = Configurations.collect({
623
691
  data,
@@ -772,7 +840,7 @@ export function collect(program: Ast.Program, options: collect.Options) {
772
840
  let output: Call['output']
773
841
 
774
842
  try {
775
- if (member.property.name === 'define') {
843
+ if (method === 'define') {
776
844
  if (
777
845
  expression.arguments.length < 1 ||
778
846
  expression.arguments.length > 3
@@ -786,9 +854,6 @@ export function collect(program: Ast.Program, options: collect.Options) {
786
854
 
787
855
  tokenType = type(expression.arguments[0]!)
788
856
 
789
- const variableSet = variableNamespaces.has(
790
- expression.callee.object.name,
791
- )
792
857
  const argument = expression.arguments[1]
793
858
  const callback = argument && Expression.unwrap(argument)
794
859
  if (
@@ -860,7 +925,7 @@ export function collect(program: Ast.Program, options: collect.Options) {
860
925
  )
861
926
 
862
927
  factoryReferences.add(base!.start)
863
- definition = variableNamespaces.has(expression.callee.object.name)
928
+ definition = variableSet
864
929
  ? VariableSets.theme(
865
930
  Vars.extend(
866
931
  VariableSets.from(themes[parent.name]![Token.definition]),
@@ -892,7 +957,7 @@ export function collect(program: Ast.Program, options: collect.Options) {
892
957
  }
893
958
 
894
959
  const call = Object.freeze({
895
- ...(variableNamespaces.has(expression.callee.object.name)
960
+ ...(variableSet
896
961
  ? {
897
962
  variableSet: true,
898
963
  directVariables: true,
@@ -1286,12 +1351,22 @@ export function collect(program: Ast.Program, options: collect.Options) {
1286
1351
 
1287
1352
  if (
1288
1353
  binding?.type === 'Import' &&
1289
- (imports.has(binding.node.start) || configImports.has(binding.node.start))
1354
+ (imports.has(binding.node.start) ||
1355
+ configImports.has(binding.node.start) ||
1356
+ factoryImports.has(binding.node.start))
1290
1357
  ) {
1358
+ if (
1359
+ factoryImports.has(binding.node.start) &&
1360
+ parent.type === 'CallExpression' &&
1361
+ factories.has(parent.start)
1362
+ )
1363
+ return true
1364
+
1291
1365
  if (
1292
1366
  parent.type === 'MemberExpression' &&
1293
1367
  grandparent?.type === 'CallExpression' &&
1294
- factories.has(grandparent.start)
1368
+ (factories.has(grandparent.start) ||
1369
+ factoryReferences.has(parent.start))
1295
1370
  )
1296
1371
  return true
1297
1372
 
package/src/default.ts CHANGED
@@ -80,8 +80,8 @@ export const tokens = {
80
80
  1000: { dark: '#fef3dc', light: '#4e2009' },
81
81
  },
82
82
  background: {
83
- 100: { dark: '#0a0a0a', light: '#fff' },
84
- 200: { dark: '#000', light: '#fafafa' },
83
+ primary: { dark: '#000', light: '#fafafa' },
84
+ surface: { dark: '#0a0a0a', light: '#fff' },
85
85
  },
86
86
  black: '#000',
87
87
  blue: {
@@ -169,7 +169,6 @@ export const tokens = {
169
169
  900: { dark: '#ff6166', light: '#cb2a2f' },
170
170
  1000: { dark: '#feecee', light: '#391417' },
171
171
  },
172
- surface: { dark: '#0a0a0a', light: '#fff' },
173
172
  teal: {
174
173
  100: { dark: '#04201b', light: '#eefcf9' },
175
174
  200: { dark: '#062822', light: '#e5faf6' },
@@ -428,14 +427,14 @@ export const tokens = {
428
427
  fontSize: '20px',
429
428
  fontWeight: 400,
430
429
  letterSpacing: '0px',
431
- lineHeight: '36px',
430
+ lineHeight: '28px',
432
431
  strong: {
433
432
  fontFamily:
434
433
  'Geist, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"',
435
434
  fontSize: '20px',
436
435
  fontWeight: 550,
437
436
  letterSpacing: '0px',
438
- lineHeight: '36px',
437
+ lineHeight: '28px',
439
438
  },
440
439
  },
441
440
  '24': {
@@ -722,8 +721,8 @@ const config = Config.create({
722
721
  1000: { dark: '#fef3dc', light: '#4e2009' },
723
722
  },
724
723
  background: {
725
- 100: { dark: '#0a0a0a', light: '#fff' },
726
- 200: { dark: '#000', light: '#fafafa' },
724
+ primary: { dark: '#000', light: '#fafafa' },
725
+ surface: { dark: '#0a0a0a', light: '#fff' },
727
726
  },
728
727
  black: '#000',
729
728
  blue: {
@@ -811,7 +810,6 @@ const config = Config.create({
811
810
  900: { dark: '#ff6166', light: '#cb2a2f' },
812
811
  1000: { dark: '#feecee', light: '#391417' },
813
812
  },
814
- surface: { dark: '#0a0a0a', light: '#fff' },
815
813
  teal: {
816
814
  100: { dark: '#04201b', light: '#eefcf9' },
817
815
  200: { dark: '#062822', light: '#e5faf6' },
@@ -1070,14 +1068,14 @@ const config = Config.create({
1070
1068
  fontSize: '20px',
1071
1069
  fontWeight: 400,
1072
1070
  letterSpacing: '0px',
1073
- lineHeight: '36px',
1071
+ lineHeight: '28px',
1074
1072
  strong: {
1075
1073
  fontFamily:
1076
1074
  'Geist, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"',
1077
1075
  fontSize: '20px',
1078
1076
  fontWeight: 550,
1079
1077
  letterSpacing: '0px',
1080
- lineHeight: '36px',
1078
+ lineHeight: '28px',
1081
1079
  },
1082
1080
  },
1083
1081
  '24': {
package/src/index.ts CHANGED
@@ -8,6 +8,21 @@ export * as Config from './Config.js'
8
8
  export { style } from './styleFunction.js'
9
9
  /** Ordered composition of applied styling props. */
10
10
  export { cx } from './cx.js'
11
+ /**
12
+ * Creates isolated authoring with optional vars and named alternatives.
13
+ * @alias Config.create
14
+ */
15
+ export { create as defineConfig } from './Config.js'
16
+ /**
17
+ * Creates immutable typed variable sets without emitting CSS.
18
+ * @alias Vars.define
19
+ */
20
+ export { define as defineVars } from './Vars.js'
21
+ /**
22
+ * Overrides variable values while preserving their shared contract.
23
+ * @alias Vars.extend
24
+ */
25
+ export { extend as extendVars } from './Vars.js'
11
26
  /** Inferred component inputs from callable style definitions. */
12
27
  export type * as Props from './Props.js'
13
28
  /** Typed literal style definitions and validation diagnostics. */
@@ -157,7 +157,7 @@ export function variants(
157
157
  return (values: Record<string, unknown> & style.Options = {}) => {
158
158
  const result = { ...props(values as style.Options) } as {
159
159
  className: string
160
- style?: style.Props['style']
160
+ style?: NonNullable<style.Props['style']>
161
161
  [key: `data-${string}`]: string | undefined
162
162
  }
163
163
  const bindings: Record<string, string | number> = {}