zyzz 0.0.14 → 0.0.16
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/dist/compiler/Graph.d.ts +3 -1
- package/dist/compiler/Graph.d.ts.map +1 -1
- package/dist/compiler/Graph.js +58 -5
- package/dist/compiler/Graph.js.map +1 -1
- package/dist/compiler/Source.d.ts.map +1 -1
- package/dist/compiler/Source.js +4 -1
- package/dist/compiler/Source.js.map +1 -1
- package/dist/compiler/Transform.d.ts +5 -0
- package/dist/compiler/Transform.d.ts.map +1 -1
- package/dist/compiler/Transform.js +9 -2
- package/dist/compiler/Transform.js.map +1 -1
- package/dist/compiler/internal/Stylesheets.d.ts +2 -0
- package/dist/compiler/internal/Stylesheets.d.ts.map +1 -1
- package/dist/compiler/internal/Stylesheets.js +2 -0
- package/dist/compiler/internal/Stylesheets.js.map +1 -1
- package/dist/default.js +1 -1
- package/dist/default.js.map +1 -1
- package/dist/default.js.zyzz.json +1 -1
- package/dist/internal/Identity.d.ts +4 -0
- package/dist/internal/Identity.d.ts.map +1 -1
- package/dist/internal/Identity.js +25 -0
- package/dist/internal/Identity.js.map +1 -1
- package/dist/next/loader.d.ts.map +1 -1
- package/dist/next/loader.js +17 -4
- package/dist/next/loader.js.map +1 -1
- package/dist/web/Css.d.ts +5 -0
- package/dist/web/Css.d.ts.map +1 -1
- package/dist/web/Css.js +3 -2
- package/dist/web/Css.js.map +1 -1
- package/dist/web/internal/Themes.d.ts +33 -1
- package/dist/web/internal/Themes.d.ts.map +1 -1
- package/dist/web/internal/Themes.js +134 -29
- package/dist/web/internal/Themes.js.map +1 -1
- package/package.json +1 -1
- package/src/compiler/Graph.ts +70 -6
- package/src/compiler/Source.ts +6 -1
- package/src/compiler/Transform.ts +13 -2
- package/src/compiler/internal/Stylesheets.ts +3 -0
- package/src/internal/Identity.ts +32 -0
- package/src/next/loader.ts +17 -8
- package/src/web/Css.ts +7 -1
- package/src/web/internal/Themes.ts +190 -31
|
@@ -17,7 +17,7 @@ export function create() {
|
|
|
17
17
|
{ identity: string | undefined; paths: Map<string, string> }
|
|
18
18
|
>()
|
|
19
19
|
|
|
20
|
-
const defaults = new Map<string,
|
|
20
|
+
const defaults = new Map<string, readonly Rule[]>()
|
|
21
21
|
|
|
22
22
|
function serialize(token: Token.Reference): string {
|
|
23
23
|
const value = token.value
|
|
@@ -41,15 +41,18 @@ export function create() {
|
|
|
41
41
|
contract.paths.set(token.path, name)
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
const label = contract.identity?.startsWith('src-') ? token.path : undefined
|
|
45
|
+
return `var(${name},${literal(value, label)})`
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
function emit(
|
|
48
49
|
themes: Readonly<Record<string, Theme.Definition | Vars.Definition>>,
|
|
49
50
|
schemes = false,
|
|
51
|
+
separate?: 'all' | 'defaults',
|
|
50
52
|
) {
|
|
51
53
|
const classes: Record<string, string> = Object.create(null)
|
|
52
|
-
const
|
|
54
|
+
const empty: string[] = []
|
|
55
|
+
const rules: Rule[] = []
|
|
53
56
|
|
|
54
57
|
for (const [name, theme] of Object.entries(themes)) {
|
|
55
58
|
if (!name) throw new Error('Theme names must be nonempty.')
|
|
@@ -94,55 +97,134 @@ export function create() {
|
|
|
94
97
|
}
|
|
95
98
|
|
|
96
99
|
if (!contract) continue
|
|
100
|
+
if (!contract.paths.size) empty.push(`.${className}{}`)
|
|
97
101
|
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
102
|
+
const declarations: Rule[] = []
|
|
103
|
+
// Resolving a value can add references to this contract.
|
|
104
|
+
const paths = [...contract.paths]
|
|
105
|
+
for (const [path, name] of paths) {
|
|
106
|
+
const value = data.values[path]
|
|
107
|
+
if (value === undefined)
|
|
108
|
+
throw new Error('Theme scope is missing a live token.')
|
|
103
109
|
|
|
104
|
-
|
|
110
|
+
const label = contract.identity?.startsWith('src-') ? path : undefined
|
|
111
|
+
declarations.push({
|
|
112
|
+
conditions: [],
|
|
113
|
+
property: name,
|
|
114
|
+
selector: `.${className}`,
|
|
115
|
+
value: literal(value, label),
|
|
105
116
|
})
|
|
106
|
-
|
|
117
|
+
}
|
|
107
118
|
|
|
108
|
-
rules.push(
|
|
119
|
+
rules.push(...declarations)
|
|
109
120
|
for (const [path, name] of contract.paths)
|
|
110
|
-
conditional(
|
|
121
|
+
conditional(
|
|
122
|
+
data.values[path]!,
|
|
123
|
+
name,
|
|
124
|
+
`.${className}`,
|
|
125
|
+
rules,
|
|
126
|
+
contract.identity?.startsWith('src-') ? path : undefined,
|
|
127
|
+
)
|
|
111
128
|
}
|
|
112
129
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
130
|
+
const scopes = new Map<string, Rule[]>()
|
|
131
|
+
for (const rule of rules) {
|
|
132
|
+
const entries = scopes.get(rule.property) ?? []
|
|
133
|
+
entries.push(rule)
|
|
134
|
+
scopes.set(rule.property, entries)
|
|
135
|
+
}
|
|
116
136
|
|
|
117
137
|
return {
|
|
118
138
|
classes: Object.freeze(classes),
|
|
119
|
-
css:
|
|
139
|
+
css:
|
|
140
|
+
separate === 'all'
|
|
141
|
+
? ''
|
|
142
|
+
: [
|
|
143
|
+
...empty,
|
|
144
|
+
render([
|
|
145
|
+
...(separate ? [] : [...defaults.values()].flat()),
|
|
146
|
+
...rules,
|
|
147
|
+
]),
|
|
148
|
+
...(schemes ? [Scheme.css] : []),
|
|
149
|
+
]
|
|
150
|
+
.filter(Boolean)
|
|
151
|
+
.join('\n'),
|
|
152
|
+
resources: separate
|
|
153
|
+
? [
|
|
154
|
+
...[...defaults].map(([id, rules]) => ({
|
|
155
|
+
css: render(rules),
|
|
156
|
+
id,
|
|
157
|
+
rules,
|
|
158
|
+
})),
|
|
159
|
+
// Keep every scope of a property together so overlapping themes retain precedence.
|
|
160
|
+
...(separate === 'all'
|
|
161
|
+
? [...scopes].map(([property, rules]) => ({
|
|
162
|
+
css: render(rules),
|
|
163
|
+
id: JSON.stringify([classes, property]),
|
|
164
|
+
rules,
|
|
165
|
+
}))
|
|
166
|
+
: []),
|
|
167
|
+
...(separate === 'all' && schemes
|
|
168
|
+
? [{ css: Scheme.css, id: 'zyzz-color-scheme' }]
|
|
169
|
+
: []),
|
|
170
|
+
]
|
|
171
|
+
: [],
|
|
120
172
|
}
|
|
121
173
|
}
|
|
122
174
|
|
|
123
|
-
function literal(value: Token.Value): string {
|
|
175
|
+
function literal(value: Token.Value, label?: string): string {
|
|
124
176
|
if (Token.is(value)) return serialize(value)
|
|
125
177
|
if (typeof value !== 'object') return String(value)
|
|
126
178
|
if ('default' in value) {
|
|
127
179
|
// Separate fallback properties preserve extensions and resolve references within each scope.
|
|
128
|
-
const base = literal(value.default)
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
180
|
+
const base = literal(value.default, label)
|
|
181
|
+
// Rule consolidation must not change existing variable identities.
|
|
182
|
+
const css = `:where(*){--fallback:${base};}${conditionalCss(value, '--fallback', ':where(*)', label)}`
|
|
183
|
+
const name = label
|
|
184
|
+
? `--z-${Identity.label(label)}-fallback-${Identity.compact(css)}`
|
|
185
|
+
: `--z-f${Identity.hash(css)}`
|
|
186
|
+
const emitted: Rule[] = [
|
|
187
|
+
{ conditions: [], property: name, selector: ':where(*)', value: base },
|
|
188
|
+
]
|
|
189
|
+
conditional(value, name, ':where(*)', emitted, label)
|
|
190
|
+
defaults.set(name, emitted)
|
|
136
191
|
return `var(${name})`
|
|
137
192
|
}
|
|
138
|
-
return `light-dark(${literal(value.light)},${literal(value.dark)})`
|
|
193
|
+
return `light-dark(${literal(value.light, label)},${literal(value.dark, label)})`
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function conditionalCss(
|
|
197
|
+
value: Token.Value,
|
|
198
|
+
property: string,
|
|
199
|
+
selector: string,
|
|
200
|
+
label?: string,
|
|
201
|
+
): string {
|
|
202
|
+
if (
|
|
203
|
+
!value ||
|
|
204
|
+
typeof value !== 'object' ||
|
|
205
|
+
Token.is(value) ||
|
|
206
|
+
!('default' in value)
|
|
207
|
+
)
|
|
208
|
+
return ''
|
|
209
|
+
return (
|
|
210
|
+
conditionalCss(value.default, property, selector, label) +
|
|
211
|
+
Object.entries(value)
|
|
212
|
+
.filter(([query]) => query !== 'default')
|
|
213
|
+
.map(
|
|
214
|
+
([query, entry]) =>
|
|
215
|
+
`${query}{${selector}{${property}:${literal(entry, label)};}${conditionalCss(entry, property, selector, label)}}`,
|
|
216
|
+
)
|
|
217
|
+
.join('')
|
|
218
|
+
)
|
|
139
219
|
}
|
|
140
220
|
|
|
141
221
|
function conditional(
|
|
142
222
|
value: Token.Value,
|
|
143
223
|
name: string,
|
|
144
224
|
selector: string,
|
|
145
|
-
rules:
|
|
225
|
+
rules: Rule[],
|
|
226
|
+
label?: string,
|
|
227
|
+
conditions: readonly string[] = [],
|
|
146
228
|
) {
|
|
147
229
|
if (
|
|
148
230
|
!value ||
|
|
@@ -151,12 +233,17 @@ export function create() {
|
|
|
151
233
|
!('default' in value)
|
|
152
234
|
)
|
|
153
235
|
return
|
|
154
|
-
conditional(value.default, name, selector, rules)
|
|
236
|
+
conditional(value.default, name, selector, rules, label, conditions)
|
|
155
237
|
for (const [query, entry] of Object.entries(value)) {
|
|
156
238
|
if (query === 'default') continue
|
|
157
|
-
const nested = [
|
|
158
|
-
|
|
159
|
-
|
|
239
|
+
const nested = [...conditions, query]
|
|
240
|
+
rules.push({
|
|
241
|
+
conditions: nested,
|
|
242
|
+
property: name,
|
|
243
|
+
selector,
|
|
244
|
+
value: literal(entry, label),
|
|
245
|
+
})
|
|
246
|
+
conditional(entry, name, selector, rules, label, nested)
|
|
160
247
|
}
|
|
161
248
|
}
|
|
162
249
|
|
|
@@ -171,6 +258,78 @@ export function encode(value: string): string {
|
|
|
171
258
|
)
|
|
172
259
|
}
|
|
173
260
|
|
|
261
|
+
/** Combines generated declarations without crossing competing declarations of the same property. */
|
|
262
|
+
export function render(rules: readonly Rule[]): string {
|
|
263
|
+
const groups: {
|
|
264
|
+
conditions: readonly string[]
|
|
265
|
+
declarations: Rule[]
|
|
266
|
+
selector: string
|
|
267
|
+
}[] = []
|
|
268
|
+
for (const rule of rules) {
|
|
269
|
+
let target: (typeof groups)[number] | undefined
|
|
270
|
+
for (let index = groups.length - 1; index >= 0; index--) {
|
|
271
|
+
const group = groups[index]!
|
|
272
|
+
if (
|
|
273
|
+
group.selector === rule.selector &&
|
|
274
|
+
JSON.stringify(group.conditions) === JSON.stringify(rule.conditions)
|
|
275
|
+
) {
|
|
276
|
+
target = group
|
|
277
|
+
break
|
|
278
|
+
}
|
|
279
|
+
if (group.declarations.some((entry) => entry.property === rule.property))
|
|
280
|
+
break
|
|
281
|
+
}
|
|
282
|
+
if (!target) {
|
|
283
|
+
target = {
|
|
284
|
+
conditions: rule.conditions,
|
|
285
|
+
declarations: [],
|
|
286
|
+
selector: rule.selector,
|
|
287
|
+
}
|
|
288
|
+
groups.push(target)
|
|
289
|
+
}
|
|
290
|
+
const previous = target.declarations.findLast(
|
|
291
|
+
(entry) => entry.property === rule.property,
|
|
292
|
+
)
|
|
293
|
+
if (previous?.value !== rule.value) target.declarations.push(rule)
|
|
294
|
+
}
|
|
295
|
+
return groups
|
|
296
|
+
.map(({ conditions, declarations, selector }) => {
|
|
297
|
+
let css = `${selector}{${declarations.map(({ property, value }) => `${property}:${value};`).join('')}}`
|
|
298
|
+
for (const condition of [...conditions].reverse())
|
|
299
|
+
css = `${condition}{${css}}`
|
|
300
|
+
return css
|
|
301
|
+
})
|
|
302
|
+
.join('\n')
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/** One generated definition whose scope order must remain intact. */
|
|
306
|
+
export type Resource = {
|
|
307
|
+
/** Complete fallback or scoped token definition. */
|
|
308
|
+
readonly css: string
|
|
309
|
+
/** Stable identity for stylesheet replacement during development. */
|
|
310
|
+
readonly id: string
|
|
311
|
+
/** Generated declarations eligible for consolidation. */
|
|
312
|
+
readonly rules?: readonly Rule[] | undefined
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/** One generated custom-property declaration and its ordered conditional scope. */
|
|
316
|
+
export type Rule = {
|
|
317
|
+
/** Outer-to-inner conditional rules. */
|
|
318
|
+
readonly conditions: readonly string[]
|
|
319
|
+
/** Generated custom-property name. */
|
|
320
|
+
readonly property: string
|
|
321
|
+
/** Theme or universal fallback selector. */
|
|
322
|
+
readonly selector: string
|
|
323
|
+
/** Serialized custom-property value. */
|
|
324
|
+
readonly value: string
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/** Selects independently shareable defaults or all token rules. */
|
|
328
|
+
export const shared = Symbol('shared token rules')
|
|
329
|
+
|
|
174
330
|
function variable(index: number | string, path: string): string {
|
|
331
|
+
// Packed declarations retain the spelling assigned by their compiler.
|
|
332
|
+
if (typeof index === 'string' && index.startsWith('src-'))
|
|
333
|
+
return `--z-${Identity.label(path)}-${Identity.compact(JSON.stringify([index, path]))}`
|
|
175
334
|
return `--z-t${encode(String(index))}-${encode(path)}`
|
|
176
335
|
}
|