zyzz 0.0.3 → 0.0.6
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.
- package/README.md +34 -19
- package/dist/Config.d.ts +16 -167
- package/dist/Config.d.ts.map +1 -1
- package/dist/Config.js +7 -246
- package/dist/Config.js.map +1 -1
- package/dist/Style.d.ts +15 -12
- package/dist/Style.d.ts.map +1 -1
- package/dist/Style.js +51 -26
- package/dist/Style.js.map +1 -1
- package/dist/Vars.d.ts +114 -0
- package/dist/Vars.d.ts.map +1 -0
- package/dist/Vars.js +42 -0
- package/dist/Vars.js.map +1 -0
- package/dist/babel/index.d.ts +2 -0
- package/dist/babel/index.d.ts.map +1 -1
- package/dist/babel/index.js +10 -1
- package/dist/babel/index.js.map +1 -1
- package/dist/compiler/Graph.d.ts +2 -0
- package/dist/compiler/Graph.d.ts.map +1 -1
- package/dist/compiler/Graph.js +30 -9
- package/dist/compiler/Graph.js.map +1 -1
- package/dist/compiler/Native.d.ts +5 -5
- package/dist/compiler/Native.d.ts.map +1 -1
- package/dist/compiler/Native.js +21 -21
- package/dist/compiler/Native.js.map +1 -1
- package/dist/compiler/Source.d.ts +8 -6
- package/dist/compiler/Source.d.ts.map +1 -1
- package/dist/compiler/Source.js +52 -16
- package/dist/compiler/Source.js.map +1 -1
- package/dist/compiler/Transform.d.ts +1 -1
- package/dist/compiler/Transform.d.ts.map +1 -1
- package/dist/compiler/Transform.js +58 -22
- package/dist/compiler/Transform.js.map +1 -1
- package/dist/compiler/internal/Compositions.d.ts +5 -0
- package/dist/compiler/internal/Compositions.d.ts.map +1 -1
- package/dist/compiler/internal/Compositions.js +22 -3
- package/dist/compiler/internal/Compositions.js.map +1 -1
- package/dist/compiler/internal/Configurations.d.ts.map +1 -1
- package/dist/compiler/internal/Configurations.js +73 -12
- package/dist/compiler/internal/Configurations.js.map +1 -1
- package/dist/compiler/internal/Contract.d.ts +1 -1
- package/dist/compiler/internal/Contract.d.ts.map +1 -1
- package/dist/compiler/internal/Contract.js +155 -13
- package/dist/compiler/internal/Contract.js.map +1 -1
- package/dist/compiler/internal/Contributions.d.ts +1 -1
- package/dist/compiler/internal/Contributions.d.ts.map +1 -1
- package/dist/compiler/internal/Contributions.js +6 -4
- package/dist/compiler/internal/Contributions.js.map +1 -1
- package/dist/compiler/internal/Dynamic.js +1 -1
- package/dist/compiler/internal/Dynamic.js.map +1 -1
- package/dist/compiler/internal/Expression.d.ts +3 -0
- package/dist/compiler/internal/Expression.d.ts.map +1 -1
- package/dist/compiler/internal/Expression.js +21 -0
- package/dist/compiler/internal/Expression.js.map +1 -1
- package/dist/compiler/internal/Identifiers.d.ts +1 -1
- package/dist/compiler/internal/Identifiers.d.ts.map +1 -1
- package/dist/compiler/internal/Identifiers.js +2 -2
- package/dist/compiler/internal/Identifiers.js.map +1 -1
- package/dist/compiler/internal/NativeBindings.d.ts +1 -1
- package/dist/compiler/internal/NativeBindings.d.ts.map +1 -1
- package/dist/compiler/internal/NativeBindings.js +3 -3
- package/dist/compiler/internal/NativeBindings.js.map +1 -1
- package/dist/compiler/internal/PackedStyles.d.ts +1 -1
- package/dist/compiler/internal/PackedStyles.d.ts.map +1 -1
- package/dist/compiler/internal/PackedStyles.js +15 -25
- package/dist/compiler/internal/PackedStyles.js.map +1 -1
- package/dist/compiler/internal/Recipes.js +1 -1
- package/dist/compiler/internal/Recipes.js.map +1 -1
- package/dist/compiler/internal/Selectors.js +1 -1
- package/dist/compiler/internal/Selectors.js.map +1 -1
- package/dist/compiler/internal/Static.d.ts.map +1 -1
- package/dist/compiler/internal/Static.js +2 -0
- package/dist/compiler/internal/Static.js.map +1 -1
- package/dist/compiler/internal/Themes.d.ts +12 -3
- package/dist/compiler/internal/Themes.d.ts.map +1 -1
- package/dist/compiler/internal/Themes.js +192 -52
- package/dist/compiler/internal/Themes.js.map +1 -1
- package/dist/compiler/internal/{Variables.d.ts → Vars.d.ts} +1 -1
- package/dist/compiler/internal/Vars.d.ts.map +1 -0
- package/dist/compiler/internal/{Variables.js → Vars.js} +3 -3
- package/dist/compiler/internal/Vars.js.map +1 -0
- package/dist/default.d.ts +147 -2562
- package/dist/default.d.ts.map +1 -1
- package/dist/default.js +85 -80
- package/dist/default.js.map +2 -2
- package/dist/default.js.zyzz.json +1 -1
- package/dist/esbuild/index.d.ts +3 -0
- package/dist/esbuild/index.d.ts.map +1 -0
- package/dist/esbuild/index.js +5 -0
- package/dist/esbuild/index.js.map +1 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/internal/Authoring.d.ts +1 -1
- package/dist/internal/Authoring.d.ts.map +1 -1
- package/dist/internal/Authoring.js +4 -4
- package/dist/internal/Authoring.js.map +1 -1
- package/dist/internal/Binding.d.ts +1 -1
- package/dist/internal/Binding.d.ts.map +1 -1
- package/dist/internal/Completion.d.ts +19 -0
- package/dist/internal/Completion.d.ts.map +1 -0
- package/dist/internal/Completion.js +2 -0
- package/dist/internal/Completion.js.map +1 -0
- package/dist/internal/Condition.d.ts +3 -1
- package/dist/internal/Condition.d.ts.map +1 -1
- package/dist/internal/Condition.js.map +1 -1
- package/dist/internal/Configuration.d.ts +248 -0
- package/dist/internal/Configuration.d.ts.map +1 -0
- package/dist/internal/Configuration.js +343 -0
- package/dist/internal/Configuration.js.map +1 -0
- package/dist/{Theme.d.ts → internal/Theme.d.ts} +15 -13
- package/dist/internal/Theme.d.ts.map +1 -0
- package/dist/{Theme.js → internal/Theme.js} +39 -12
- package/dist/internal/Theme.js.map +1 -0
- package/dist/internal/Token.d.ts +46 -16
- package/dist/internal/Token.d.ts.map +1 -1
- package/dist/internal/Token.js +89 -5
- package/dist/internal/Token.js.map +1 -1
- package/dist/internal/Typography.d.ts +13 -5
- package/dist/internal/Typography.d.ts.map +1 -1
- package/dist/internal/Typography.js +42 -6
- package/dist/internal/Typography.js.map +1 -1
- package/dist/internal/Value.d.ts +6 -1
- package/dist/internal/Value.d.ts.map +1 -1
- package/dist/internal/Value.js.map +1 -1
- package/dist/internal/VariableSets.d.ts +20 -0
- package/dist/internal/VariableSets.d.ts.map +1 -0
- package/dist/internal/VariableSets.js +317 -0
- package/dist/internal/VariableSets.js.map +1 -0
- package/dist/next/index.d.ts +8 -3
- package/dist/next/index.d.ts.map +1 -1
- package/dist/next/index.js +21 -10
- package/dist/next/index.js.map +1 -1
- package/dist/next/loader.d.ts +1 -0
- package/dist/next/loader.d.ts.map +1 -1
- package/dist/next/loader.js +25 -6
- package/dist/next/loader.js.map +1 -1
- package/dist/node/Host.d.ts.map +1 -1
- package/dist/node/Host.js +1 -1
- package/dist/node/Host.js.map +1 -1
- package/dist/node/Reset.d.ts +3 -0
- package/dist/node/Reset.d.ts.map +1 -0
- package/dist/node/Reset.js +7 -0
- package/dist/node/Reset.js.map +1 -0
- package/dist/react-native/Host.d.ts +2 -2
- package/dist/react-native/Host.d.ts.map +1 -1
- package/dist/react-native/Host.js +4 -4
- package/dist/react-native/Host.js.map +1 -1
- package/dist/react-native/StyleSheet.d.ts +9 -8
- package/dist/react-native/StyleSheet.d.ts.map +1 -1
- package/dist/react-native/StyleSheet.js +29 -23
- package/dist/react-native/StyleSheet.js.map +1 -1
- package/dist/react-native/Variants.d.ts +3 -3
- package/dist/react-native/Variants.d.ts.map +1 -1
- package/dist/react-native/Variants.js +2 -2
- package/dist/react-native/react.d.ts +1 -1
- package/dist/react-native/react.d.ts.map +1 -1
- package/dist/react-native/react.js +5 -5
- package/dist/react-native/react.js.map +1 -1
- package/dist/rollup/index.d.ts +3 -0
- package/dist/rollup/index.d.ts.map +1 -0
- package/dist/rollup/index.js +5 -0
- package/dist/rollup/index.js.map +1 -0
- package/dist/runtime/Appearance.d.ts +7 -7
- package/dist/runtime/Appearance.d.ts.map +1 -1
- package/dist/runtime/Appearance.js +15 -15
- package/dist/runtime/Appearance.js.map +1 -1
- package/dist/runtime/Dynamic.d.ts.map +1 -1
- package/dist/runtime/Dynamic.js +4 -1
- package/dist/runtime/Dynamic.js.map +1 -1
- package/dist/runtime/NativeContext.d.ts +4 -4
- package/dist/runtime/NativeContext.d.ts.map +1 -1
- package/dist/runtime/NativeContext.js +5 -5
- package/dist/runtime/NativeContext.js.map +1 -1
- package/dist/runtime/NativeDynamic.d.ts +1 -1
- package/dist/runtime/NativeDynamic.d.ts.map +1 -1
- package/dist/runtime/NativeDynamic.js.map +1 -1
- package/dist/runtime/Props.d.ts +1 -1
- package/dist/runtime/Props.js +3 -3
- package/dist/runtime/Props.js.map +1 -1
- package/dist/runtime/Selection.d.ts +14 -10
- package/dist/runtime/Selection.d.ts.map +1 -1
- package/dist/runtime/Selection.js +19 -18
- package/dist/runtime/Selection.js.map +1 -1
- package/dist/styleFunction.d.ts +14 -14
- package/dist/styleFunction.d.ts.map +1 -1
- package/dist/styleFunction.js.map +1 -1
- package/dist/unplugin/index.d.ts +22 -0
- package/dist/unplugin/index.d.ts.map +1 -0
- package/dist/unplugin/index.js +500 -0
- package/dist/unplugin/index.js.map +1 -0
- package/dist/variants.d.ts +3 -3
- package/dist/variants.d.ts.map +1 -1
- package/dist/variants.js.map +1 -1
- package/dist/vite/index.d.ts +2 -0
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +44 -12
- package/dist/vite/index.js.map +1 -1
- package/dist/web/Css.d.ts +4 -3
- package/dist/web/Css.d.ts.map +1 -1
- package/dist/web/Css.js +4 -4
- package/dist/web/Css.js.map +1 -1
- package/dist/web/internal/Themes.d.ts +3 -2
- package/dist/web/internal/Themes.d.ts.map +1 -1
- package/dist/web/internal/Themes.js +42 -6
- package/dist/web/internal/Themes.js.map +1 -1
- package/dist/webpack/index.d.ts +3 -0
- package/dist/webpack/index.d.ts.map +1 -0
- package/dist/webpack/index.js +5 -0
- package/dist/webpack/index.js.map +1 -0
- package/package.json +23 -2
- package/src/Config.ts +43 -685
- package/src/Style.ts +97 -34
- package/src/Vars.ts +299 -0
- package/src/babel/index.ts +23 -1
- package/src/compiler/Graph.ts +36 -9
- package/src/compiler/Native.ts +29 -32
- package/src/compiler/Source.ts +64 -22
- package/src/compiler/Transform.ts +73 -23
- package/src/compiler/internal/Compositions.ts +36 -3
- package/src/compiler/internal/Configurations.ts +83 -11
- package/src/compiler/internal/Contract.ts +207 -15
- package/src/compiler/internal/Contributions.ts +6 -3
- package/src/compiler/internal/Dynamic.ts +1 -1
- package/src/compiler/internal/Expression.ts +26 -0
- package/src/compiler/internal/Identifiers.ts +2 -1
- package/src/compiler/internal/NativeBindings.ts +3 -3
- package/src/compiler/internal/PackedStyles.ts +22 -26
- package/src/compiler/internal/Recipes.ts +1 -1
- package/src/compiler/internal/Selectors.ts +1 -1
- package/src/compiler/internal/Static.ts +1 -0
- package/src/compiler/internal/Themes.ts +266 -57
- package/src/compiler/internal/{Variables.ts → Vars.ts} +2 -2
- package/src/default.ts +173 -154
- package/src/esbuild/index.ts +5 -0
- package/src/index.ts +4 -4
- package/src/internal/Authoring.ts +5 -7
- package/src/internal/Binding.ts +1 -1
- package/src/internal/Completion.ts +42 -0
- package/src/internal/Condition.ts +31 -1
- package/src/internal/Configuration.ts +970 -0
- package/src/{Theme.ts → internal/Theme.ts} +75 -20
- package/src/internal/Token.ts +258 -85
- package/src/internal/Typography.ts +71 -16
- package/src/internal/Value.ts +45 -26
- package/src/internal/VariableSets.ts +458 -0
- package/src/next/index.ts +34 -12
- package/src/next/loader.ts +35 -6
- package/src/node/Host.ts +1 -2
- package/src/node/Reset.ts +7 -0
- package/src/react-native/Host.ts +6 -6
- package/src/react-native/StyleSheet.ts +49 -42
- package/src/react-native/Variants.ts +3 -3
- package/src/react-native/react.ts +6 -6
- package/src/rollup/index.ts +5 -0
- package/src/runtime/Appearance.ts +20 -20
- package/src/runtime/Dynamic.ts +4 -1
- package/src/runtime/NativeContext.ts +8 -8
- package/src/runtime/NativeDynamic.ts +2 -4
- package/src/runtime/Props.ts +3 -3
- package/src/runtime/Selection.ts +44 -33
- package/src/styleFunction.ts +22 -19
- package/src/unplugin/index.ts +604 -0
- package/src/variants.ts +7 -5
- package/src/vite/index.ts +48 -18
- package/src/web/Css.ts +10 -7
- package/src/web/internal/Themes.ts +52 -9
- package/src/webpack/index.ts +5 -0
- package/dist/Theme.d.ts.map +0 -1
- package/dist/Theme.js.map +0 -1
- package/dist/compiler/internal/Variables.d.ts.map +0 -1
- package/dist/compiler/internal/Variables.js.map +0 -1
|
@@ -2,21 +2,69 @@
|
|
|
2
2
|
import type * as Literal from './Literal.js'
|
|
3
3
|
import * as Token from './Token.js'
|
|
4
4
|
|
|
5
|
-
/**
|
|
6
|
-
export
|
|
5
|
+
/** Responsive query blocks accepted within a typography set. */
|
|
6
|
+
export type Condition = `@media ${string}` | `@container ${string}`
|
|
7
|
+
|
|
8
|
+
/** Recognizes responsive blocks without accepting selectors or unrelated at-rules. */
|
|
9
|
+
export function condition(key: string): key is Condition {
|
|
10
|
+
return /^@(media|container)\s+\S/.test(key)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Expands one named set into portable field references and ordered query blocks. */
|
|
14
|
+
export function entries(
|
|
7
15
|
theme: object | undefined,
|
|
8
16
|
name: unknown,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
return properties.filter((property) =>
|
|
18
|
-
Object.hasOwn(data.values, `typography.${name}.${property}`),
|
|
17
|
+
explicit: ReadonlySet<string> = new Set(),
|
|
18
|
+
important = false,
|
|
19
|
+
): readonly (readonly [string, unknown])[] {
|
|
20
|
+
if (
|
|
21
|
+
!theme ||
|
|
22
|
+
typeof name !== 'string' ||
|
|
23
|
+
name.split('.').some((part) => part.startsWith('@'))
|
|
19
24
|
)
|
|
25
|
+
return []
|
|
26
|
+
let tree: unknown = Object.getOwnPropertyDescriptor(theme, 'tokens')?.value
|
|
27
|
+
for (const part of ['typography', ...name.split('.')]) {
|
|
28
|
+
if (!tree || typeof tree !== 'object') return []
|
|
29
|
+
tree = Object.getOwnPropertyDescriptor(tree, part)?.value
|
|
30
|
+
}
|
|
31
|
+
function expand(value: unknown): readonly (readonly [string, unknown])[] {
|
|
32
|
+
if (!value || typeof value !== 'object') return []
|
|
33
|
+
return Object.entries(value).flatMap(
|
|
34
|
+
([key, child]): (readonly [string, unknown])[] => {
|
|
35
|
+
if (
|
|
36
|
+
properties.includes(key as Property) &&
|
|
37
|
+
Token.is(child) &&
|
|
38
|
+
!explicit.has(key)
|
|
39
|
+
)
|
|
40
|
+
return [
|
|
41
|
+
[key, important ? Token.compose([child, ' !important']) : child],
|
|
42
|
+
]
|
|
43
|
+
if (condition(key)) {
|
|
44
|
+
const nested = expand(child)
|
|
45
|
+
return nested.length ? [[key, Object.fromEntries(nested)]] : []
|
|
46
|
+
}
|
|
47
|
+
return []
|
|
48
|
+
},
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
return expand(tree)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Lists expanded fields and queries in source-map traversal order. */
|
|
55
|
+
export function keys(
|
|
56
|
+
theme: object | undefined,
|
|
57
|
+
name: unknown,
|
|
58
|
+
explicit: ReadonlySet<string>,
|
|
59
|
+
): readonly string[] {
|
|
60
|
+
function collect(values: readonly (readonly [string, unknown])[]): string[] {
|
|
61
|
+
return values.flatMap(([key, value]) =>
|
|
62
|
+
condition(key)
|
|
63
|
+
? [key, ...collect(Object.entries(value as object))]
|
|
64
|
+
: [key],
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
return collect(entries(theme, name, explicit))
|
|
20
68
|
}
|
|
21
69
|
|
|
22
70
|
/** Names of sets containing at least one typography declaration. */
|
|
@@ -27,10 +75,15 @@ export type Names<tokens> = tokens extends { typography: infer tree }
|
|
|
27
75
|
type Paths<tree> = string extends keyof tree
|
|
28
76
|
? string
|
|
29
77
|
: {
|
|
30
|
-
[key in Exclude<
|
|
31
|
-
|
|
78
|
+
[key in Exclude<
|
|
79
|
+
Extract<keyof tree, string | number>,
|
|
80
|
+
Property | Condition
|
|
81
|
+
>]:
|
|
82
|
+
| (Extract<keyof tree[key], Property | Condition> extends never
|
|
83
|
+
? never
|
|
84
|
+
: `${key}`)
|
|
32
85
|
| `${key}.${Paths<tree[key]>}`
|
|
33
|
-
}[Exclude<Extract<keyof tree, string | number>, Property>]
|
|
86
|
+
}[Exclude<Extract<keyof tree, string | number>, Property | Condition>]
|
|
34
87
|
|
|
35
88
|
/** Scalar properties applied by a typography set. */
|
|
36
89
|
export const properties = [
|
|
@@ -45,7 +98,9 @@ export const properties = [
|
|
|
45
98
|
export type Property = (typeof properties)[number]
|
|
46
99
|
|
|
47
100
|
/** A set may supply any of the five typography properties. */
|
|
48
|
-
export type Set = Pick<Literal.Properties, Property>
|
|
101
|
+
export type Set = Pick<Literal.Properties, Property> & {
|
|
102
|
+
readonly [key in Condition]?: Set
|
|
103
|
+
}
|
|
49
104
|
|
|
50
105
|
/** Nested names may contain sets and additional named variants. */
|
|
51
106
|
export type Sets = { readonly [name: string]: Set | Sets }
|
package/src/internal/Value.ts
CHANGED
|
@@ -22,37 +22,56 @@ export type Atom<value> =
|
|
|
22
22
|
*/
|
|
23
23
|
export type Accepted<style, properties> = {
|
|
24
24
|
[property in keyof style]: property extends keyof properties
|
|
25
|
-
? style[property] extends
|
|
26
|
-
?
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
? style[property]
|
|
25
|
+
? style[property] extends { readonly [Token.scalar]: infer scalar }
|
|
26
|
+
? property extends keyof Literal.Properties
|
|
27
|
+
? [scalar] extends [Literal.Properties[property]]
|
|
28
|
+
? style[property] & Checked<Record<property, scalar>>[property]
|
|
29
|
+
: never
|
|
31
30
|
: never
|
|
32
|
-
:
|
|
33
|
-
|
|
34
|
-
style[property]
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
? Fold<style[property]> extends Input<
|
|
46
|
-
| Lowercase<Extract<Literal.Properties[property], string>>
|
|
47
|
-
| Extract<Literal.Properties[property], number>
|
|
31
|
+
: References<style[property], Exclude<properties[property], undefined>> &
|
|
32
|
+
(style[property] extends Binding.Reference
|
|
33
|
+
? Binding.Reference<style[property]['type']> extends Exclude<
|
|
34
|
+
properties[property],
|
|
35
|
+
undefined
|
|
36
|
+
>
|
|
37
|
+
? style[property]
|
|
38
|
+
: never
|
|
39
|
+
: FunctionValue.Is<style[property]> extends true
|
|
40
|
+
? FunctionValue.Accepted<
|
|
41
|
+
style[property],
|
|
42
|
+
Exclude<properties[property], undefined>,
|
|
43
|
+
property
|
|
48
44
|
>
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
45
|
+
: Exclude<style[property], undefined> extends Exclude<
|
|
46
|
+
properties[property],
|
|
47
|
+
undefined
|
|
48
|
+
>
|
|
49
|
+
? Exclude<style[property], undefined>
|
|
50
|
+
: property extends keyof Literal.Properties
|
|
51
|
+
? style[property] extends
|
|
52
|
+
| string
|
|
53
|
+
| readonly (number | string)[]
|
|
54
|
+
? Fold<style[property]> extends Input<
|
|
55
|
+
| Lowercase<
|
|
56
|
+
Extract<Literal.Properties[property], string>
|
|
57
|
+
>
|
|
58
|
+
| Extract<Literal.Properties[property], number>
|
|
59
|
+
>
|
|
60
|
+
? style[property]
|
|
61
|
+
: Exclude<properties[property], undefined>
|
|
62
|
+
: Exclude<properties[property], undefined>
|
|
63
|
+
: Exclude<properties[property], undefined>)
|
|
53
64
|
: never
|
|
54
65
|
}
|
|
55
66
|
|
|
67
|
+
type References<value, allowed> = value extends Token.Reference
|
|
68
|
+
? value extends Extract<allowed, Token.Reference>
|
|
69
|
+
? unknown
|
|
70
|
+
: never
|
|
71
|
+
: value extends readonly unknown[]
|
|
72
|
+
? { [key in keyof value]: References<value[key], allowed> }
|
|
73
|
+
: unknown
|
|
74
|
+
|
|
56
75
|
type Fold<value> = value extends string
|
|
57
76
|
? Lexical.Fold<Canonical<Spaces<Normalized<Lexical.Normalized<value>>>>>
|
|
58
77
|
: value extends readonly unknown[]
|
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
/** Normalizes shared variable data into compiler-owned token contracts. @module */
|
|
2
|
+
import * as Query from './Query.js'
|
|
3
|
+
import * as Typography from './Typography.js'
|
|
4
|
+
import * as Condition from './Condition.js'
|
|
5
|
+
import * as Identity from './Identity.js'
|
|
6
|
+
import * as Literal from './Literal.js'
|
|
7
|
+
import * as Token from './Token.js'
|
|
8
|
+
import * as Theme from './Theme.js'
|
|
9
|
+
import * as Vars from '../Vars.js'
|
|
10
|
+
|
|
11
|
+
/** Wraps a variable set for the shared style/compiler contract. */
|
|
12
|
+
export function theme(
|
|
13
|
+
variables: Vars.Definition,
|
|
14
|
+
mappings?: Vars.Mappings | false,
|
|
15
|
+
): Theme.Definition {
|
|
16
|
+
const original = Theme.define({})
|
|
17
|
+
const metadata = variables[Token.definition]
|
|
18
|
+
if (!metadata?.contract.variableSet)
|
|
19
|
+
throw new Vars.InvalidError([], 'Expected a variable set.')
|
|
20
|
+
const contract = Object.freeze({
|
|
21
|
+
...metadata.contract,
|
|
22
|
+
...(mappings !== undefined ? { mappings } : {}),
|
|
23
|
+
})
|
|
24
|
+
const tokens = from({
|
|
25
|
+
...metadata,
|
|
26
|
+
contract,
|
|
27
|
+
values: rebind(metadata, contract),
|
|
28
|
+
})
|
|
29
|
+
const definition = Object.create(null)
|
|
30
|
+
Object.defineProperties(definition, {
|
|
31
|
+
...Object.getOwnPropertyDescriptors(original),
|
|
32
|
+
className: {
|
|
33
|
+
get: () => {
|
|
34
|
+
const identity = metadata.contract[Token.identity]
|
|
35
|
+
return identity
|
|
36
|
+
? `z_theme-${identity}-${Identity.hash(JSON.stringify(metadata.values))}`
|
|
37
|
+
: original.className
|
|
38
|
+
},
|
|
39
|
+
enumerable: true,
|
|
40
|
+
},
|
|
41
|
+
tokens: { value: tokens, enumerable: true },
|
|
42
|
+
vars: { value: tokens, enumerable: true },
|
|
43
|
+
[Token.definition]: { value: tokens[Token.definition] },
|
|
44
|
+
})
|
|
45
|
+
return Object.freeze(definition) as Theme.Definition
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Rebuilds a reference tree from normalized compiler metadata. */
|
|
49
|
+
export function from(metadata: Token.Metadata): Vars.Definition {
|
|
50
|
+
return buildTree(
|
|
51
|
+
metadata.values,
|
|
52
|
+
metadata.contract,
|
|
53
|
+
metadata.queries,
|
|
54
|
+
metadata.paths,
|
|
55
|
+
) as Vars.Definition
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Validates and freezes a complete set or compatible partial override. */
|
|
59
|
+
export function build(
|
|
60
|
+
input: unknown,
|
|
61
|
+
contract: Token.Contract,
|
|
62
|
+
base?: Token.Metadata['values'],
|
|
63
|
+
baseQueries?: Query.Metadata,
|
|
64
|
+
basePaths?: Token.Metadata['paths'],
|
|
65
|
+
): Vars.Definition {
|
|
66
|
+
const values: Record<string, Token.Value> = Object.assign(
|
|
67
|
+
Object.create(null),
|
|
68
|
+
base,
|
|
69
|
+
)
|
|
70
|
+
const paths = { ...basePaths }
|
|
71
|
+
const conditions: string[][] = []
|
|
72
|
+
const active = new Set<object>()
|
|
73
|
+
function visit(input: unknown, path: string[]) {
|
|
74
|
+
if (
|
|
75
|
+
typeof input === 'string' ||
|
|
76
|
+
typeof input === 'number' ||
|
|
77
|
+
Token.is(input) ||
|
|
78
|
+
(path.length > 0 &&
|
|
79
|
+
input &&
|
|
80
|
+
typeof input === 'object' &&
|
|
81
|
+
('default' in input ||
|
|
82
|
+
('light' in input && 'dark' in input) ||
|
|
83
|
+
(Object.keys(input).length <= 2 &&
|
|
84
|
+
('light' in input || 'dark' in input))))
|
|
85
|
+
) {
|
|
86
|
+
if (!path.length)
|
|
87
|
+
throw new Vars.InvalidError(path, 'Expected a variable record.')
|
|
88
|
+
const value = read(input, path, queryData)
|
|
89
|
+
const name = path.join('.')
|
|
90
|
+
if (base && !Object.hasOwn(base, name))
|
|
91
|
+
throw new Vars.InvalidError(
|
|
92
|
+
path,
|
|
93
|
+
'Extensions cannot add variable paths.',
|
|
94
|
+
)
|
|
95
|
+
if (base && domain(base[name]!) !== domain(value))
|
|
96
|
+
throw new Vars.InvalidError(
|
|
97
|
+
path,
|
|
98
|
+
'Variable overrides must preserve their domain.',
|
|
99
|
+
)
|
|
100
|
+
if (path.some((part) => Typography.condition(part)))
|
|
101
|
+
paths[name] = Object.freeze([...path])
|
|
102
|
+
values[name] = value
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
const entries = record(input, path)
|
|
106
|
+
if (active.has(input as object))
|
|
107
|
+
throw new Vars.InvalidError(path, 'Cyclic variables are not supported.')
|
|
108
|
+
active.add(input as object)
|
|
109
|
+
if (!entries.length && !base)
|
|
110
|
+
throw new Vars.InvalidError(path, 'Variable records cannot be empty.')
|
|
111
|
+
for (const [key, value] of entries) {
|
|
112
|
+
const query = path[0] === 'typography' && Typography.condition(key)
|
|
113
|
+
if (query) conditions.push([...path, key])
|
|
114
|
+
if (!query && (!key || /[.!]/.test(key) || key.startsWith('@')))
|
|
115
|
+
throw new Vars.InvalidError(
|
|
116
|
+
[...path, key],
|
|
117
|
+
'Expected a nonempty variable key without dots or conditions.',
|
|
118
|
+
)
|
|
119
|
+
visit(value, [...path, key])
|
|
120
|
+
}
|
|
121
|
+
active.delete(input as object)
|
|
122
|
+
}
|
|
123
|
+
const fields = Object.fromEntries(record(input, []))
|
|
124
|
+
const queries = { ...baseQueries }
|
|
125
|
+
for (const key of ['breakpoints', 'containers', 'containerNames'] as const) {
|
|
126
|
+
if (Object.hasOwn(fields, key)) {
|
|
127
|
+
const value = fields[key]
|
|
128
|
+
if (base) {
|
|
129
|
+
if (key === 'containerNames') {
|
|
130
|
+
if (
|
|
131
|
+
JSON.stringify(value) !==
|
|
132
|
+
JSON.stringify(baseQueries?.containerNames ?? [])
|
|
133
|
+
)
|
|
134
|
+
throw new Vars.InvalidError(
|
|
135
|
+
[key],
|
|
136
|
+
'Extensions cannot change container identities.',
|
|
137
|
+
)
|
|
138
|
+
} else {
|
|
139
|
+
for (const [name] of record(value, [key]))
|
|
140
|
+
if (!Object.hasOwn(baseQueries?.[key] ?? {}, name))
|
|
141
|
+
throw new Vars.InvalidError(
|
|
142
|
+
[key, name],
|
|
143
|
+
'Extensions cannot add query thresholds.',
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
Object.assign(queries, {
|
|
148
|
+
[key]:
|
|
149
|
+
key === 'containerNames'
|
|
150
|
+
? value
|
|
151
|
+
: { ...baseQueries?.[key], ...value },
|
|
152
|
+
})
|
|
153
|
+
delete fields[key]
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
const queryData = Theme.define(queries as Theme.Tokens)[Token.definition]
|
|
157
|
+
.queries
|
|
158
|
+
if (Object.keys(fields).length) visit(fields, [])
|
|
159
|
+
for (const path of conditions) {
|
|
160
|
+
try {
|
|
161
|
+
Condition.normalize(
|
|
162
|
+
Query.resolve(
|
|
163
|
+
path.at(-1)!,
|
|
164
|
+
queryData ?? { breakpoints: {}, containers: {}, containerNames: [] },
|
|
165
|
+
),
|
|
166
|
+
)
|
|
167
|
+
} catch (error) {
|
|
168
|
+
throw new Vars.InvalidError(path, (error as Error).message)
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return buildTree(
|
|
172
|
+
Object.freeze(values),
|
|
173
|
+
contract,
|
|
174
|
+
queryData,
|
|
175
|
+
Object.keys(paths).length ? Object.freeze(paths) : undefined,
|
|
176
|
+
) as Vars.Definition
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function buildTree(
|
|
180
|
+
values: Token.Metadata['values'],
|
|
181
|
+
contract: Token.Contract,
|
|
182
|
+
queries?: Query.Metadata,
|
|
183
|
+
paths?: Token.Metadata['paths'],
|
|
184
|
+
) {
|
|
185
|
+
values = rebind({ contract, values }, contract)
|
|
186
|
+
|
|
187
|
+
type Tree = { [key: string]: Tree | Token.Reference }
|
|
188
|
+
const tree: Tree = Object.create(null)
|
|
189
|
+
for (const [path, value] of Object.entries(values)) {
|
|
190
|
+
const parts = paths?.[path] ?? path.split('.')
|
|
191
|
+
let target = tree
|
|
192
|
+
for (const key of parts.slice(0, -1))
|
|
193
|
+
target = (target[key] ??= Object.create(null)) as Tree
|
|
194
|
+
target[parts.at(-1)!] = Token.create({
|
|
195
|
+
contract,
|
|
196
|
+
group: domain(value),
|
|
197
|
+
path,
|
|
198
|
+
value,
|
|
199
|
+
})
|
|
200
|
+
}
|
|
201
|
+
function freeze(tree: Tree) {
|
|
202
|
+
for (const value of Object.values(tree)) if (!Token.is(value)) freeze(value)
|
|
203
|
+
Object.freeze(tree)
|
|
204
|
+
}
|
|
205
|
+
Object.defineProperty(tree, Token.definition, {
|
|
206
|
+
value: Object.freeze({
|
|
207
|
+
contract,
|
|
208
|
+
values,
|
|
209
|
+
queries,
|
|
210
|
+
...(paths ? { paths } : {}),
|
|
211
|
+
}),
|
|
212
|
+
})
|
|
213
|
+
freeze(tree)
|
|
214
|
+
return tree
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function record(input: unknown, path: string[]) {
|
|
218
|
+
if (
|
|
219
|
+
!input ||
|
|
220
|
+
typeof input !== 'object' ||
|
|
221
|
+
![null, Object.prototype].includes(Object.getPrototypeOf(input))
|
|
222
|
+
)
|
|
223
|
+
throw new Vars.InvalidError(path, 'Expected a plain variable record.')
|
|
224
|
+
return Reflect.ownKeys(input).map((key) => {
|
|
225
|
+
const descriptor = Object.getOwnPropertyDescriptor(input, key)!
|
|
226
|
+
if (
|
|
227
|
+
typeof key !== 'string' ||
|
|
228
|
+
!descriptor.enumerable ||
|
|
229
|
+
!('value' in descriptor)
|
|
230
|
+
)
|
|
231
|
+
throw new Vars.InvalidError(
|
|
232
|
+
path,
|
|
233
|
+
'Vars require enumerable data properties.',
|
|
234
|
+
)
|
|
235
|
+
return [key, descriptor.value] as const
|
|
236
|
+
})
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function read(
|
|
240
|
+
input: unknown,
|
|
241
|
+
path: string[],
|
|
242
|
+
queries: Query.Metadata | undefined,
|
|
243
|
+
active = new Set<object>(),
|
|
244
|
+
): Token.Value {
|
|
245
|
+
if (Token.is(input)) return input
|
|
246
|
+
if (typeof input === 'number' && Number.isFinite(input)) return input
|
|
247
|
+
if (typeof input === 'string' && input.trim() && !/[;{}]/.test(input))
|
|
248
|
+
return input
|
|
249
|
+
if (active.has(input as object))
|
|
250
|
+
throw new Vars.InvalidError(path, 'Cyclic variables are not supported.')
|
|
251
|
+
const next = new Set(active).add(input as object)
|
|
252
|
+
const entries = record(input, path)
|
|
253
|
+
const fields = Object.fromEntries(entries)
|
|
254
|
+
if (Object.hasOwn(fields, 'default')) {
|
|
255
|
+
for (const [key] of entries)
|
|
256
|
+
if (key !== 'default' && (!/^@media\s+\S/.test(key) || /[;{}]/.test(key)))
|
|
257
|
+
throw new Vars.InvalidError(
|
|
258
|
+
[...path, key],
|
|
259
|
+
'Expected an @media condition.',
|
|
260
|
+
)
|
|
261
|
+
const result = Object.fromEntries(
|
|
262
|
+
entries.map(([key, value]) => {
|
|
263
|
+
let condition = key
|
|
264
|
+
try {
|
|
265
|
+
if (key !== 'default')
|
|
266
|
+
condition = Condition.normalize(
|
|
267
|
+
Query.resolve(
|
|
268
|
+
key,
|
|
269
|
+
queries ?? {
|
|
270
|
+
breakpoints: {},
|
|
271
|
+
containers: {},
|
|
272
|
+
containerNames: [],
|
|
273
|
+
},
|
|
274
|
+
),
|
|
275
|
+
)
|
|
276
|
+
} catch (error) {
|
|
277
|
+
throw new Vars.InvalidError([...path, key], (error as Error).message)
|
|
278
|
+
}
|
|
279
|
+
return [condition, read(value, [...path, key], queries, next)]
|
|
280
|
+
}),
|
|
281
|
+
) as Token.Conditions
|
|
282
|
+
const expected = domain(result.default)
|
|
283
|
+
if (Object.values(result).some((value) => domain(value) !== expected))
|
|
284
|
+
throw new Vars.InvalidError(
|
|
285
|
+
path,
|
|
286
|
+
'Conditional values must share one domain.',
|
|
287
|
+
)
|
|
288
|
+
return Object.freeze(result)
|
|
289
|
+
}
|
|
290
|
+
if (
|
|
291
|
+
entries.length === 2 &&
|
|
292
|
+
Object.hasOwn(fields, 'light') &&
|
|
293
|
+
Object.hasOwn(fields, 'dark')
|
|
294
|
+
) {
|
|
295
|
+
const light = read(fields.light, [...path, 'light'], queries, next)
|
|
296
|
+
const dark = read(fields.dark, [...path, 'dark'], queries, next)
|
|
297
|
+
if (domain(light) !== 'color' || domain(dark) !== 'color')
|
|
298
|
+
throw new Vars.InvalidError(path, 'Color schemes require colors.')
|
|
299
|
+
return Object.freeze({ light, dark })
|
|
300
|
+
}
|
|
301
|
+
throw new Vars.InvalidError(
|
|
302
|
+
path,
|
|
303
|
+
'Expected a scalar, a complete color pair, or media overrides with a default.',
|
|
304
|
+
)
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/** Infers the shared declaration domain after following defaults and references. */
|
|
308
|
+
export function domain(value: Token.Value): Token.Group {
|
|
309
|
+
if (Token.is(value)) return value.group
|
|
310
|
+
if (typeof value === 'object')
|
|
311
|
+
return 'default' in value ? domain(value.default) : 'color'
|
|
312
|
+
if (
|
|
313
|
+
typeof value === 'string' &&
|
|
314
|
+
(/^(?:#|(?:color|color-mix|contrast-color|light-dark|rgb|rgba|hsl|hsla|hwb|lab|lch|oklab|oklch)\()/i.test(
|
|
315
|
+
value,
|
|
316
|
+
) ||
|
|
317
|
+
(!/[0-9()#]/.test(value) && Literal.isLiteral('color', value)))
|
|
318
|
+
)
|
|
319
|
+
return 'color'
|
|
320
|
+
if (
|
|
321
|
+
value === 0 ||
|
|
322
|
+
value === '0' ||
|
|
323
|
+
(typeof value === 'string' &&
|
|
324
|
+
new RegExp(
|
|
325
|
+
`^[+-]?(?:[0-9]*\\.[0-9]+|[0-9]+)(?:${Literal.lengthUnits.join('|')})$`,
|
|
326
|
+
'i',
|
|
327
|
+
).test(value))
|
|
328
|
+
)
|
|
329
|
+
return 'spacing'
|
|
330
|
+
return typeof value === 'number' ? 'number' : 'string'
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/** Copies and validates category mappings without retaining mutable caller input. */
|
|
334
|
+
export function mappings(input: unknown): Vars.Mappings | false | undefined {
|
|
335
|
+
if (input === undefined || input === false) return input
|
|
336
|
+
return Object.freeze(
|
|
337
|
+
Object.fromEntries(
|
|
338
|
+
record(input, ['mappings']).map(([category, value]) => {
|
|
339
|
+
if (
|
|
340
|
+
!category ||
|
|
341
|
+
!Array.isArray(value) ||
|
|
342
|
+
value.some(
|
|
343
|
+
(property) =>
|
|
344
|
+
typeof property !== 'string' ||
|
|
345
|
+
!Object.hasOwn(Literal.rules, property),
|
|
346
|
+
)
|
|
347
|
+
)
|
|
348
|
+
throw new Vars.InvalidError(
|
|
349
|
+
['mappings', category],
|
|
350
|
+
'Expected an array of CSS property names.',
|
|
351
|
+
)
|
|
352
|
+
if (new Set(value).size !== value.length)
|
|
353
|
+
throw new Vars.InvalidError(
|
|
354
|
+
['mappings', category],
|
|
355
|
+
'Property mappings cannot contain duplicates.',
|
|
356
|
+
)
|
|
357
|
+
return [category, Object.freeze([...value])]
|
|
358
|
+
}),
|
|
359
|
+
),
|
|
360
|
+
)
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/** Combines disjoint variable leaves without mutating either authored record. */
|
|
364
|
+
export function merge(
|
|
365
|
+
base: unknown,
|
|
366
|
+
derived: unknown,
|
|
367
|
+
path: string[] = [],
|
|
368
|
+
): Vars.Values {
|
|
369
|
+
const result: Record<string, unknown> = Object.assign(
|
|
370
|
+
Object.create(null),
|
|
371
|
+
Object.fromEntries(record(base, path)),
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
for (const [key, value] of record(derived, path)) {
|
|
375
|
+
const next = [...path, key]
|
|
376
|
+
if (Object.hasOwn(result, key)) {
|
|
377
|
+
for (const entry of [result[key], value])
|
|
378
|
+
if (
|
|
379
|
+
!entry ||
|
|
380
|
+
typeof entry !== 'object' ||
|
|
381
|
+
Array.isArray(entry) ||
|
|
382
|
+
Token.is(entry) ||
|
|
383
|
+
'default' in entry ||
|
|
384
|
+
('light' in entry && 'dark' in entry) ||
|
|
385
|
+
(Object.keys(entry).length <= 2 &&
|
|
386
|
+
('light' in entry || 'dark' in entry))
|
|
387
|
+
)
|
|
388
|
+
throw new Vars.InvalidError(
|
|
389
|
+
next,
|
|
390
|
+
'Derived variables cannot replace existing paths.',
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
result[key] = merge(result[key], value, next)
|
|
394
|
+
} else result[key] = value
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return result as Vars.Values
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/** Updates references within a set while retaining references to independent sets. */
|
|
401
|
+
export function rebind(
|
|
402
|
+
metadata: Token.Metadata,
|
|
403
|
+
contract: Token.Contract,
|
|
404
|
+
): Token.Metadata['values'] {
|
|
405
|
+
const active = new Set<string>()
|
|
406
|
+
const values: Record<string, Token.Value> = Object.create(null)
|
|
407
|
+
|
|
408
|
+
function resolve(value: Token.Value): Token.Value {
|
|
409
|
+
if (Token.is(value)) {
|
|
410
|
+
if (
|
|
411
|
+
value.contract !== metadata.contract &&
|
|
412
|
+
(metadata.contract[Token.identity] === undefined ||
|
|
413
|
+
value.contract[Token.identity] !== metadata.contract[Token.identity])
|
|
414
|
+
)
|
|
415
|
+
return value
|
|
416
|
+
|
|
417
|
+
return Token.create({
|
|
418
|
+
contract,
|
|
419
|
+
group: value.group,
|
|
420
|
+
path: value.path,
|
|
421
|
+
value: visit(value.path),
|
|
422
|
+
})
|
|
423
|
+
}
|
|
424
|
+
if (typeof value !== 'object') return value
|
|
425
|
+
|
|
426
|
+
return Object.freeze(
|
|
427
|
+
Object.fromEntries(
|
|
428
|
+
Object.entries(value).map(([key, entry]) => [key, resolve(entry)]),
|
|
429
|
+
),
|
|
430
|
+
) as Token.Value
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function visit(path: string): Token.Value {
|
|
434
|
+
if (Object.hasOwn(values, path)) return values[path]!
|
|
435
|
+
if (active.has(path))
|
|
436
|
+
throw new Vars.InvalidError(
|
|
437
|
+
path.split('.'),
|
|
438
|
+
'Cyclic variables are not supported.',
|
|
439
|
+
)
|
|
440
|
+
if (!Object.hasOwn(metadata.values, path))
|
|
441
|
+
throw new Vars.InvalidError(
|
|
442
|
+
path.split('.'),
|
|
443
|
+
'Referenced variable path is missing.',
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
active.add(path)
|
|
447
|
+
values[path] = resolve(metadata.values[path]!)
|
|
448
|
+
active.delete(path)
|
|
449
|
+
return values[path]!
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
for (const path of Object.keys(metadata.values)) visit(path)
|
|
453
|
+
return Object.freeze(
|
|
454
|
+
Object.fromEntries(
|
|
455
|
+
Object.keys(metadata.values).map((path) => [path, values[path]!]),
|
|
456
|
+
),
|
|
457
|
+
)
|
|
458
|
+
}
|