zyzz 0.0.6 → 0.0.7
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 +26 -1
- package/dist/Style.d.ts +3 -3
- package/dist/Style.d.ts.map +1 -1
- package/dist/Style.js +12 -1
- package/dist/Style.js.map +1 -1
- package/dist/internal/Authoring.d.ts.map +1 -1
- package/dist/internal/Authoring.js +14 -0
- package/dist/internal/Authoring.js.map +1 -1
- package/dist/internal/Binding.d.ts +7 -0
- package/dist/internal/Binding.d.ts.map +1 -1
- package/dist/internal/Binding.js.map +1 -1
- package/dist/internal/Configuration.d.ts +1 -1
- package/dist/internal/Configuration.d.ts.map +1 -1
- package/dist/internal/Configuration.js +7 -6
- package/dist/internal/Configuration.js.map +1 -1
- package/dist/internal/Theme.d.ts +1 -1
- package/dist/internal/Theme.d.ts.map +1 -1
- package/dist/internal/Theme.js.map +1 -1
- package/dist/internal/Token.d.ts +3 -1
- package/dist/internal/Token.d.ts.map +1 -1
- package/dist/internal/Token.js +28 -3
- package/dist/internal/Token.js.map +1 -1
- package/dist/internal/Value.d.ts +22 -0
- package/dist/internal/Value.d.ts.map +1 -1
- package/dist/internal/Value.js +76 -9
- package/dist/internal/Value.js.map +1 -1
- package/dist/next/internal/Project.d.ts +28 -0
- package/dist/next/internal/Project.d.ts.map +1 -0
- package/dist/next/internal/Project.js +249 -0
- package/dist/next/internal/Project.js.map +1 -0
- package/dist/next/loader.d.ts +2 -5
- package/dist/next/loader.d.ts.map +1 -1
- package/dist/next/loader.js +11 -150
- package/dist/next/loader.js.map +1 -1
- package/dist/variants.d.ts +1 -1
- package/dist/variants.d.ts.map +1 -1
- package/dist/variants.js.map +1 -1
- package/package.json +1 -1
- package/src/Style.ts +42 -8
- package/src/internal/Authoring.ts +21 -0
- package/src/internal/Binding.ts +30 -0
- package/src/internal/Configuration.ts +19 -13
- package/src/internal/Theme.ts +5 -1
- package/src/internal/Token.ts +34 -3
- package/src/internal/Value.ts +180 -10
- package/src/next/internal/Project.ts +337 -0
- package/src/next/loader.ts +19 -196
- package/src/variants.ts +1 -0
package/src/Style.ts
CHANGED
|
@@ -68,13 +68,20 @@ export type Accepted<
|
|
|
68
68
|
? never
|
|
69
69
|
: Value.Atom<Typography.Names<tokens>>
|
|
70
70
|
: key extends keyof Literal.Properties
|
|
71
|
-
?
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
? (literal extends true
|
|
72
|
+
? LiteralDeclarations
|
|
73
|
+
: keyof tokens extends never
|
|
74
74
|
? LiteralDeclarations
|
|
75
|
-
: DeclarationProperties<tokens>
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
: DeclarationProperties<tokens>)[key] extends style[key]
|
|
76
|
+
? style[key]
|
|
77
|
+
: Value.Accepted<
|
|
78
|
+
Pick<style, key>,
|
|
79
|
+
literal extends true
|
|
80
|
+
? LiteralDeclarations
|
|
81
|
+
: DeclarationProperties<tokens>
|
|
82
|
+
>[key] &
|
|
83
|
+
Value.Checked<Pick<style, key>, tokens>[key] &
|
|
84
|
+
Value.Tokens<style[key], tokens, key>
|
|
78
85
|
: key extends Condition.Keys<tokens, key>
|
|
79
86
|
? [style[key]] extends [undefined]
|
|
80
87
|
? never
|
|
@@ -605,10 +612,11 @@ export function define(
|
|
|
605
612
|
)
|
|
606
613
|
continue
|
|
607
614
|
}
|
|
615
|
+
const custom = parsed?.custom
|
|
608
616
|
const scalar = parsed ? parsed.value : entry
|
|
609
617
|
|
|
610
618
|
const resolved = (() => {
|
|
611
|
-
if (!theme) return scalar
|
|
619
|
+
if (!theme || custom) return scalar
|
|
612
620
|
|
|
613
621
|
if (typeof scalar !== 'string' && typeof scalar !== 'number')
|
|
614
622
|
return scalar
|
|
@@ -645,6 +653,25 @@ export function define(
|
|
|
645
653
|
continue
|
|
646
654
|
}
|
|
647
655
|
|
|
656
|
+
if (
|
|
657
|
+
theme &&
|
|
658
|
+
!custom &&
|
|
659
|
+
Token.mapped(theme, key) &&
|
|
660
|
+
!Token.is(resolved) &&
|
|
661
|
+
!(Binding.is(resolved) && !resolved.name.startsWith('--z-d')) &&
|
|
662
|
+
!(
|
|
663
|
+
Token.isExpression(resolved) &&
|
|
664
|
+
resolved.parts.every((part) => Token.is(part) || part === '')
|
|
665
|
+
)
|
|
666
|
+
) {
|
|
667
|
+
report(
|
|
668
|
+
'invalid_value',
|
|
669
|
+
[name, authoredProperty],
|
|
670
|
+
'Expected a configured token or a bracketed CSS value.',
|
|
671
|
+
)
|
|
672
|
+
continue
|
|
673
|
+
}
|
|
674
|
+
|
|
648
675
|
const value = parsed && resolved === '0' ? 0 : resolved
|
|
649
676
|
|
|
650
677
|
declarations.push(
|
|
@@ -774,6 +801,7 @@ export class InvalidError extends Error {
|
|
|
774
801
|
type LiteralAtoms = {
|
|
775
802
|
readonly [property in keyof Literal.Properties]-?: Value.Atom<
|
|
776
803
|
| Exclude<Literal.Properties[property], undefined>
|
|
804
|
+
| `[${string}]`
|
|
777
805
|
| Binding.Reference<'*'>
|
|
778
806
|
| {
|
|
779
807
|
[kind in Binding.Kind]: property extends Binding.Property<
|
|
@@ -812,7 +840,13 @@ export type NamedStyle<name extends string = string> = {
|
|
|
812
840
|
/** Supported literal and token declarations. Unknown properties and undefined values are rejected. */
|
|
813
841
|
export type DeclarationProperties<tokens extends Theme.Tokens = {}> = {
|
|
814
842
|
readonly [property in keyof Literal.Properties]: Value.Fallbacks<
|
|
815
|
-
|
|
|
843
|
+
| (property extends `--${string}`
|
|
844
|
+
? LiteralAtoms[property]
|
|
845
|
+
: [Token.Names<tokens, property>] extends [never]
|
|
846
|
+
? LiteralAtoms[property]
|
|
847
|
+
:
|
|
848
|
+
| Value.Atom<`[${string}]`>
|
|
849
|
+
| Extract<LiteralAtoms[property], Binding.Reference>)
|
|
816
850
|
| Value.Atom<Token.Names<tokens, property>>
|
|
817
851
|
| {
|
|
818
852
|
[group in Token.Group]: property extends Token.Properties<group>
|
|
@@ -74,6 +74,10 @@ export function create(
|
|
|
74
74
|
if (!dynamic && !id && Object.keys(input as object).length === 0)
|
|
75
75
|
Identity.requireId(undefined, 'Empty style')
|
|
76
76
|
|
|
77
|
+
if (id && !dynamic && options.theme)
|
|
78
|
+
Style.define({ style: body(input as Record<string, unknown>) } as never, {
|
|
79
|
+
vars: options.theme,
|
|
80
|
+
})
|
|
77
81
|
const className = id
|
|
78
82
|
? `z-style-${id}`
|
|
79
83
|
: Identity.style(
|
|
@@ -125,6 +129,23 @@ export function variants(
|
|
|
125
129
|
options: Options = {},
|
|
126
130
|
): unknown {
|
|
127
131
|
const id = Identity.requireId(options.id, 'variants')
|
|
132
|
+
if (options.theme) {
|
|
133
|
+
const styles = [
|
|
134
|
+
input.base,
|
|
135
|
+
...Object.values(
|
|
136
|
+
(input.variants ?? {}) as Record<string, Record<string, unknown>>,
|
|
137
|
+
).flatMap(Object.values),
|
|
138
|
+
...((input.compoundVariants ?? []) as readonly { style: unknown }[]).map(
|
|
139
|
+
(entry) => entry.style,
|
|
140
|
+
),
|
|
141
|
+
]
|
|
142
|
+
for (const input of styles)
|
|
143
|
+
if (input && typeof input === 'object')
|
|
144
|
+
Style.define(
|
|
145
|
+
{ style: body(input as Record<string, unknown>) } as never,
|
|
146
|
+
{ vars: options.theme },
|
|
147
|
+
)
|
|
148
|
+
}
|
|
128
149
|
const className = `z-style-${id}`
|
|
129
150
|
const axes = Object.entries(
|
|
130
151
|
(input.variants ?? {}) as Record<string, Record<string, unknown>>,
|
package/src/internal/Binding.ts
CHANGED
|
@@ -127,6 +127,36 @@ export type Checked<style> = {
|
|
|
127
127
|
: unknown
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
/** Excludes token-name slots, which cannot resolve token names during application. */
|
|
131
|
+
export type TokenSlots<style, values, tokens, mappings = {}> = {
|
|
132
|
+
[property in keyof style]: property extends keyof Literal.Properties
|
|
133
|
+
? SlotValue<
|
|
134
|
+
style[property],
|
|
135
|
+
values[keyof values],
|
|
136
|
+
Token.Names<tokens, property>
|
|
137
|
+
>
|
|
138
|
+
: property extends keyof mappings
|
|
139
|
+
? mappings[property] extends readonly (infer target extends
|
|
140
|
+
keyof Literal.Properties)[]
|
|
141
|
+
? SlotValue<
|
|
142
|
+
style[property],
|
|
143
|
+
values[keyof values],
|
|
144
|
+
Token.Names<tokens, target>
|
|
145
|
+
>
|
|
146
|
+
: unknown
|
|
147
|
+
: property extends 'vars'
|
|
148
|
+
? unknown
|
|
149
|
+
: style[property] extends Record<string, unknown>
|
|
150
|
+
? TokenSlots<style[property], values, tokens, mappings>
|
|
151
|
+
: unknown
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
type SlotValue<value, inputs, names> = value extends readonly unknown[]
|
|
155
|
+
? { [key in keyof value]: SlotValue<value[key], inputs, names> }
|
|
156
|
+
: Extract<Extract<value, inputs>, names> extends never
|
|
157
|
+
? unknown
|
|
158
|
+
: never
|
|
159
|
+
|
|
130
160
|
/** Rejects reserved callback field names and importance-bearing value domains. */
|
|
131
161
|
export type Inputs<values> = {
|
|
132
162
|
[key in keyof values]: key extends
|
|
@@ -277,14 +277,16 @@ export function create(
|
|
|
277
277
|
})
|
|
278
278
|
}
|
|
279
279
|
}
|
|
280
|
-
function boundVariants(
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
280
|
+
function boundVariants(theme?: Theme.Definition) {
|
|
281
|
+
return (
|
|
282
|
+
definition: Record<string, unknown>,
|
|
283
|
+
options: style.DefinitionOptions = {},
|
|
284
|
+
) =>
|
|
285
|
+
Authoring.variants(definition, {
|
|
286
|
+
...options,
|
|
287
|
+
output: input.output as style.Output | undefined,
|
|
288
|
+
theme,
|
|
289
|
+
})
|
|
288
290
|
}
|
|
289
291
|
function handle(theme: Theme.Definition, name: string) {
|
|
290
292
|
const original = Token.bind(theme, contract)
|
|
@@ -313,7 +315,7 @@ export function create(
|
|
|
313
315
|
})
|
|
314
316
|
Object.assign(select, {
|
|
315
317
|
style: boundStyle(original),
|
|
316
|
-
variants: boundVariants,
|
|
318
|
+
variants: boundVariants(original),
|
|
317
319
|
})
|
|
318
320
|
return select
|
|
319
321
|
}
|
|
@@ -410,7 +412,9 @@ export function create(
|
|
|
410
412
|
),
|
|
411
413
|
theme: bound[input.defaultTheme],
|
|
412
414
|
themes: Object.freeze(select),
|
|
413
|
-
variants: boundVariants
|
|
415
|
+
variants: boundVariants(
|
|
416
|
+
bound[input.defaultTheme] as unknown as Theme.Definition,
|
|
417
|
+
),
|
|
414
418
|
})
|
|
415
419
|
}
|
|
416
420
|
|
|
@@ -424,7 +428,7 @@ export function create(
|
|
|
424
428
|
script: Appearance.create([], { storageKey }),
|
|
425
429
|
style: boundStyle(theme as unknown as Theme.Definition),
|
|
426
430
|
theme,
|
|
427
|
-
variants: boundVariants,
|
|
431
|
+
variants: boundVariants(theme as unknown as Theme.Definition),
|
|
428
432
|
})
|
|
429
433
|
}
|
|
430
434
|
|
|
@@ -436,7 +440,7 @@ export function create(
|
|
|
436
440
|
appearance: Appearance.root([], { storageKey }),
|
|
437
441
|
script: Appearance.create([], { storageKey }),
|
|
438
442
|
style: boundStyle(theme),
|
|
439
|
-
variants: boundVariants,
|
|
443
|
+
variants: boundVariants(theme),
|
|
440
444
|
})
|
|
441
445
|
}
|
|
442
446
|
|
|
@@ -590,7 +594,9 @@ export type StyleFactory<
|
|
|
590
594
|
values: values,
|
|
591
595
|
) => styles &
|
|
592
596
|
NoInfer<
|
|
593
|
-
Body<styles, tokens, layers, mappings> &
|
|
597
|
+
Body<styles, tokens, layers, mappings> &
|
|
598
|
+
Binding.Checked<styles> &
|
|
599
|
+
Binding.TokenSlots<styles, values, tokens, mappings>
|
|
594
600
|
>) &
|
|
595
601
|
Completion.Properties<tokens, NoInfer<styles>>,
|
|
596
602
|
...options: Parameters<callback> extends [Record<string, string | number>]
|
package/src/internal/Theme.ts
CHANGED
|
@@ -30,7 +30,11 @@ export type StyleFactory<tokens extends Tokens> = {
|
|
|
30
30
|
styles: ((
|
|
31
31
|
values: values,
|
|
32
32
|
) => styles &
|
|
33
|
-
NoInfer<
|
|
33
|
+
NoInfer<
|
|
34
|
+
Style.Accepted<styles, tokens> &
|
|
35
|
+
Binding.Checked<styles> &
|
|
36
|
+
Binding.TokenSlots<styles, values, tokens>
|
|
37
|
+
>) &
|
|
34
38
|
(values extends Binding.Inputs<values> ? unknown : never),
|
|
35
39
|
options?: style.DefinitionOptions,
|
|
36
40
|
): style.Dynamic<values>
|
package/src/internal/Token.ts
CHANGED
|
@@ -444,11 +444,11 @@ export type Reference<group extends Group = Group> = {
|
|
|
444
444
|
|
|
445
445
|
const reference = Symbol('zyzz.token')
|
|
446
446
|
|
|
447
|
-
/** Resolves
|
|
447
|
+
/** Resolves configured token names, with specific color groups first. */
|
|
448
448
|
export function resolve(value: unknown, options: resolve.Options): unknown {
|
|
449
449
|
if (
|
|
450
|
-
(
|
|
451
|
-
|
|
450
|
+
options.property.startsWith('--') ||
|
|
451
|
+
(typeof value !== 'string' && typeof value !== 'number')
|
|
452
452
|
)
|
|
453
453
|
return value
|
|
454
454
|
|
|
@@ -529,6 +529,37 @@ export function resolve(value: unknown, options: resolve.Options): unknown {
|
|
|
529
529
|
return value
|
|
530
530
|
}
|
|
531
531
|
|
|
532
|
+
/** Whether the configured catalog supplies tokens for a property. */
|
|
533
|
+
export function mapped(
|
|
534
|
+
theme: object,
|
|
535
|
+
property: keyof Literal.Properties,
|
|
536
|
+
): boolean {
|
|
537
|
+
if (property.startsWith('--')) return false
|
|
538
|
+
const data = Object.getOwnPropertyDescriptor(theme, definition)
|
|
539
|
+
?.value as Metadata
|
|
540
|
+
return Object.keys(data.values).some((path) => {
|
|
541
|
+
const category = path.split('.')[0]!
|
|
542
|
+
const targets =
|
|
543
|
+
data.contract.mappings === false
|
|
544
|
+
? undefined
|
|
545
|
+
: data.contract.mappings?.[category]
|
|
546
|
+
if (
|
|
547
|
+
data.contract.mappings !== false &&
|
|
548
|
+
!(targets
|
|
549
|
+
? targets.includes(property)
|
|
550
|
+
: accepts(category as Group, property))
|
|
551
|
+
)
|
|
552
|
+
return false
|
|
553
|
+
let leaf: unknown = Object.getOwnPropertyDescriptor(theme, 'tokens')?.value
|
|
554
|
+
for (const key of path.split('.'))
|
|
555
|
+
leaf =
|
|
556
|
+
leaf && typeof leaf === 'object'
|
|
557
|
+
? Object.getOwnPropertyDescriptor(leaf, key)?.value
|
|
558
|
+
: undefined
|
|
559
|
+
return is(leaf) && acceptsReference(leaf, property)
|
|
560
|
+
})
|
|
561
|
+
}
|
|
562
|
+
|
|
532
563
|
/** Input contract for theme token resolution. */
|
|
533
564
|
export declare namespace resolve {
|
|
534
565
|
/** Property domain and immutable theme metadata supplied by style validation. */
|
package/src/internal/Value.ts
CHANGED
|
@@ -21,6 +21,60 @@ export type Atom<value> =
|
|
|
21
21
|
* Checks authored properties and retains validated input types to avoid expanding whole-property fallback unions.
|
|
22
22
|
*/
|
|
23
23
|
export type Accepted<style, properties> = {
|
|
24
|
+
[property in keyof style]: AcceptedValue<
|
|
25
|
+
style[property],
|
|
26
|
+
property,
|
|
27
|
+
properties
|
|
28
|
+
>
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
type AcceptedValue<value, property extends PropertyKey, properties> =
|
|
32
|
+
Extract<value, `[${string}]` | `[${string}] !important`> extends never
|
|
33
|
+
? AcceptedMember<Record<property, value>, properties>[property]
|
|
34
|
+
: value extends unknown
|
|
35
|
+
? AcceptedMember<Record<property, value>, properties>[property]
|
|
36
|
+
: never
|
|
37
|
+
|
|
38
|
+
type AcceptedMember<style, properties> = {
|
|
39
|
+
[property in keyof style]: property extends keyof properties
|
|
40
|
+
? style[property] extends `[${infer value}]` | `[${infer value}] !important`
|
|
41
|
+
?
|
|
42
|
+
| (property extends 'gridTemplateColumns' | 'gridTemplateRows'
|
|
43
|
+
? style[property] extends LiteralAccepted<
|
|
44
|
+
Pick<style, property>,
|
|
45
|
+
Literal.Properties
|
|
46
|
+
>[property]
|
|
47
|
+
? style[property]
|
|
48
|
+
: never
|
|
49
|
+
: never)
|
|
50
|
+
| (ScalarValue<value, property> extends LiteralAccepted<
|
|
51
|
+
Record<property, ScalarValue<value, property>>,
|
|
52
|
+
Literal.Properties
|
|
53
|
+
>[property]
|
|
54
|
+
? style[property]
|
|
55
|
+
: never)
|
|
56
|
+
: style[property] extends readonly [unknown, ...unknown[]]
|
|
57
|
+
? AcceptedArray<style[property], property, properties>
|
|
58
|
+
: LiteralAccepted<Pick<style, property>, properties>[property]
|
|
59
|
+
: never
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
type ScalarValue<value, property> =
|
|
63
|
+
value extends `${infer numeric extends number}`
|
|
64
|
+
? property extends keyof Literal.Properties
|
|
65
|
+
? number extends Literal.Properties[property]
|
|
66
|
+
? numeric
|
|
67
|
+
: value
|
|
68
|
+
: value
|
|
69
|
+
: value
|
|
70
|
+
|
|
71
|
+
type AcceptedArray<values, property extends PropertyKey, properties> = {
|
|
72
|
+
[index in keyof values]: values[index] extends readonly unknown[]
|
|
73
|
+
? never
|
|
74
|
+
: Accepted<Record<property, values[index]>, properties>[property]
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
type LiteralAccepted<style, properties> = {
|
|
24
78
|
[property in keyof style]: property extends keyof properties
|
|
25
79
|
? style[property] extends { readonly [Token.scalar]: infer scalar }
|
|
26
80
|
? property extends keyof Literal.Properties
|
|
@@ -80,6 +134,22 @@ type Fold<value> = value extends string
|
|
|
80
134
|
|
|
81
135
|
/** Refines concrete scalar spellings; already-broad property contracts need no literal refinement. */
|
|
82
136
|
export type Checked<style, tokens = {}> = {
|
|
137
|
+
[property in keyof style]: style[property] extends
|
|
138
|
+
| `[${infer value}]`
|
|
139
|
+
| `[${infer value}] !important`
|
|
140
|
+
?
|
|
141
|
+
| (property extends 'gridTemplateColumns' | 'gridTemplateRows'
|
|
142
|
+
? LiteralChecked<Pick<style, property>>[property]
|
|
143
|
+
: never)
|
|
144
|
+
| LiteralChecked<
|
|
145
|
+
Record<property, ScalarValue<value, property>>
|
|
146
|
+
>[property]
|
|
147
|
+
: style[property] extends readonly [unknown, ...unknown[]]
|
|
148
|
+
? CheckedArray<style[property], property, tokens>
|
|
149
|
+
: LiteralChecked<Pick<style, property>, tokens>[property]
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
type LiteralChecked<style, tokens = {}> = {
|
|
83
153
|
[property in keyof style]: style[property] extends string & Binding.Reference
|
|
84
154
|
? style[property] extends Binding.Reference<'*'>
|
|
85
155
|
? unknown
|
|
@@ -121,6 +191,40 @@ export type Checked<style, tokens = {}> = {
|
|
|
121
191
|
: unknown)
|
|
122
192
|
}
|
|
123
193
|
|
|
194
|
+
type CheckedArray<values, property extends PropertyKey, tokens> = {
|
|
195
|
+
[index in keyof values]: Checked<
|
|
196
|
+
Record<property, values[index]>,
|
|
197
|
+
tokens
|
|
198
|
+
>[property]
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** Requires tokens for properties with configured values. */
|
|
202
|
+
export type Tokens<
|
|
203
|
+
value,
|
|
204
|
+
tokens,
|
|
205
|
+
property extends keyof Literal.Properties,
|
|
206
|
+
> = property extends `--${string}`
|
|
207
|
+
? unknown
|
|
208
|
+
: [Token.Names<tokens, property>] extends [never]
|
|
209
|
+
? unknown
|
|
210
|
+
: value extends readonly unknown[]
|
|
211
|
+
? { [index in keyof value]: Tokens<value[index], tokens, property> }
|
|
212
|
+
: value extends
|
|
213
|
+
| Binding.Reference
|
|
214
|
+
| Token.Reference
|
|
215
|
+
| Token.Variable
|
|
216
|
+
| `${Token.Variable} !important`
|
|
217
|
+
| `[${string}]`
|
|
218
|
+
| `[${string}] !important`
|
|
219
|
+
? unknown
|
|
220
|
+
: value extends Atom<Token.Names<tokens, property>>
|
|
221
|
+
? unknown
|
|
222
|
+
: value extends number
|
|
223
|
+
? `${value}` extends Token.Names<tokens, property>
|
|
224
|
+
? unknown
|
|
225
|
+
: never
|
|
226
|
+
: never
|
|
227
|
+
|
|
124
228
|
type Importance<value> = value extends readonly unknown[]
|
|
125
229
|
? { [key in keyof value]: Importance<value[key]> }
|
|
126
230
|
: value extends `${infer body}!${infer suffix}`
|
|
@@ -263,7 +367,11 @@ export function parse(
|
|
|
263
367
|
property: keyof Literal.Properties,
|
|
264
368
|
):
|
|
265
369
|
| { invalid: true }
|
|
266
|
-
| {
|
|
370
|
+
| {
|
|
371
|
+
custom?: true
|
|
372
|
+
important: boolean
|
|
373
|
+
value: number | string | Token.Expression
|
|
374
|
+
}
|
|
267
375
|
| undefined {
|
|
268
376
|
if (Token.isExpression(input)) {
|
|
269
377
|
const text = input.parts
|
|
@@ -275,6 +383,16 @@ export function parse(
|
|
|
275
383
|
|
|
276
384
|
const parts = [...input.parts]
|
|
277
385
|
let remaining = text.length - String(parsed.value).length
|
|
386
|
+
if (parsed.custom) {
|
|
387
|
+
for (let index = 0; index < parts.length; index++) {
|
|
388
|
+
const part = parts[index]
|
|
389
|
+
if (part === '') continue
|
|
390
|
+
if (typeof part !== 'string') return undefined
|
|
391
|
+
parts[index] = part.slice(1)
|
|
392
|
+
break
|
|
393
|
+
}
|
|
394
|
+
remaining--
|
|
395
|
+
}
|
|
278
396
|
|
|
279
397
|
for (let index = parts.length - 1; remaining > 0 && index >= 0; index--) {
|
|
280
398
|
const part = parts[index]
|
|
@@ -286,7 +404,11 @@ export function parse(
|
|
|
286
404
|
remaining -= count
|
|
287
405
|
}
|
|
288
406
|
|
|
289
|
-
return {
|
|
407
|
+
return {
|
|
408
|
+
...(parsed.custom ? { custom: true } : {}),
|
|
409
|
+
important: parsed.important,
|
|
410
|
+
value: Token.compose(parts.filter((part) => part !== '')),
|
|
411
|
+
}
|
|
290
412
|
}
|
|
291
413
|
|
|
292
414
|
if (typeof input !== 'string') return undefined
|
|
@@ -317,7 +439,15 @@ export function parse(
|
|
|
317
439
|
marker = marker === 0 ? -1 : input.lastIndexOf('!', marker - 1)
|
|
318
440
|
}
|
|
319
441
|
|
|
320
|
-
if (marker < 0)
|
|
442
|
+
if (marker < 0) {
|
|
443
|
+
if (bracketed(input))
|
|
444
|
+
return {
|
|
445
|
+
custom: true,
|
|
446
|
+
important: false,
|
|
447
|
+
value: scalar(input.slice(1, -1)),
|
|
448
|
+
}
|
|
449
|
+
return undefined
|
|
450
|
+
}
|
|
321
451
|
if (input.slice(marker - 1) !== ' !important' || escaped(marker - 1))
|
|
322
452
|
return { invalid: true }
|
|
323
453
|
|
|
@@ -326,14 +456,54 @@ export function parse(
|
|
|
326
456
|
while (end > 0 && /[ \t\n\r\f]/.test(input[end - 1]!) && !escaped(end - 1))
|
|
327
457
|
end--
|
|
328
458
|
|
|
329
|
-
const
|
|
330
|
-
const
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
459
|
+
const body = input.slice(0, end)
|
|
460
|
+
const custom = bracketed(body)
|
|
461
|
+
const text = custom ? body.slice(1, -1) : body
|
|
462
|
+
const value = scalar(text)
|
|
463
|
+
|
|
464
|
+
return {
|
|
465
|
+
...(custom ? { custom: true } : {}),
|
|
466
|
+
important: true as const,
|
|
467
|
+
value,
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function scalar(text: string): number | string {
|
|
471
|
+
const numeric =
|
|
472
|
+
Literal.rule(property)?.kind === 'number' ||
|
|
473
|
+
Literal.rule(property)?.kind === 'grid-line'
|
|
474
|
+
return numeric && /^[+-]?(?:\d*\.\d+|\d+)(?:[eE][+-]?\d+)?$/.test(text)
|
|
335
475
|
? Number(text)
|
|
336
476
|
: text
|
|
477
|
+
}
|
|
478
|
+
}
|
|
337
479
|
|
|
338
|
-
|
|
480
|
+
/** Recognizes one enclosing pair without consuming native grid line lists. */
|
|
481
|
+
function bracketed(value: string): boolean {
|
|
482
|
+
if (!value.startsWith('[') || !value.endsWith(']')) return false
|
|
483
|
+
let depth = 0
|
|
484
|
+
let quote = ''
|
|
485
|
+
for (let index = 0; index < value.length; index++) {
|
|
486
|
+
const character = value[index]!
|
|
487
|
+
if (character === '\\') {
|
|
488
|
+
index++
|
|
489
|
+
continue
|
|
490
|
+
}
|
|
491
|
+
if (quote) {
|
|
492
|
+
if (character === quote) quote = ''
|
|
493
|
+
continue
|
|
494
|
+
}
|
|
495
|
+
if (character === '/' && value[index + 1] === '*') {
|
|
496
|
+
const end = value.indexOf('*/', index + 2)
|
|
497
|
+
if (end < 0) return false
|
|
498
|
+
index = end + 1
|
|
499
|
+
continue
|
|
500
|
+
}
|
|
501
|
+
if (character === '"' || character === "'") {
|
|
502
|
+
quote = character
|
|
503
|
+
continue
|
|
504
|
+
}
|
|
505
|
+
if (character === '[') depth++
|
|
506
|
+
if (character === ']' && --depth === 0) return index === value.length - 1
|
|
507
|
+
}
|
|
508
|
+
return false
|
|
339
509
|
}
|