zyzz 0.0.24 → 0.0.26
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 +23 -365
- package/dist/Style.d.ts +2 -0
- package/dist/Style.d.ts.map +1 -1
- package/dist/Vars.d.ts +18 -2
- package/dist/Vars.d.ts.map +1 -1
- package/dist/Vars.js +21 -3
- package/dist/Vars.js.map +1 -1
- package/dist/compiler/Graph.d.ts +3 -0
- package/dist/compiler/Graph.d.ts.map +1 -1
- package/dist/compiler/Graph.js +38 -2
- package/dist/compiler/Graph.js.map +1 -1
- package/dist/compiler/Source.d.ts.map +1 -1
- package/dist/compiler/Source.js +11 -3
- package/dist/compiler/Source.js.map +1 -1
- package/dist/compiler/Transform.d.ts +4 -0
- package/dist/compiler/Transform.d.ts.map +1 -1
- package/dist/compiler/Transform.js +79 -15
- package/dist/compiler/Transform.js.map +1 -1
- package/dist/compiler/internal/Applications.d.ts +20 -0
- package/dist/compiler/internal/Applications.d.ts.map +1 -1
- package/dist/compiler/internal/Applications.js +151 -47
- package/dist/compiler/internal/Applications.js.map +1 -1
- package/dist/compiler/internal/Compositions.d.ts.map +1 -1
- package/dist/compiler/internal/Compositions.js +6 -0
- package/dist/compiler/internal/Compositions.js.map +1 -1
- package/dist/compiler/internal/Configurations.d.ts +3 -0
- package/dist/compiler/internal/Configurations.d.ts.map +1 -1
- package/dist/compiler/internal/Configurations.js +6 -1
- package/dist/compiler/internal/Configurations.js.map +1 -1
- package/dist/compiler/internal/Contract.d.ts.map +1 -1
- package/dist/compiler/internal/Contract.js +34 -7
- package/dist/compiler/internal/Contract.js.map +1 -1
- package/dist/compiler/internal/PackedStyles.d.ts.map +1 -1
- package/dist/compiler/internal/PackedStyles.js +7 -0
- package/dist/compiler/internal/PackedStyles.js.map +1 -1
- package/dist/compiler/internal/Themes.d.ts.map +1 -1
- package/dist/compiler/internal/Themes.js +123 -35
- package/dist/compiler/internal/Themes.js.map +1 -1
- package/dist/internal/Configuration.d.ts +10 -0
- package/dist/internal/Configuration.d.ts.map +1 -1
- package/dist/internal/Configuration.js +13 -1
- package/dist/internal/Configuration.js.map +1 -1
- package/dist/internal/Token.d.ts +2 -0
- package/dist/internal/Token.d.ts.map +1 -1
- package/dist/internal/Token.js.map +1 -1
- package/dist/internal/VariableSets.d.ts +20 -4
- package/dist/internal/VariableSets.d.ts.map +1 -1
- package/dist/internal/VariableSets.js +70 -19
- package/dist/internal/VariableSets.js.map +1 -1
- package/dist/next/internal/Project.d.ts.map +1 -1
- package/dist/next/internal/Project.js +30 -25
- package/dist/next/internal/Project.js.map +1 -1
- package/dist/next/loader.js +2 -1
- package/dist/next/loader.js.map +1 -1
- package/dist/node/Host.d.ts.map +1 -1
- package/dist/node/Host.js +32 -23
- package/dist/node/Host.js.map +1 -1
- package/dist/node/internal/Contracts.d.ts +22 -0
- package/dist/node/internal/Contracts.d.ts.map +1 -0
- package/dist/node/internal/Contracts.js +287 -0
- package/dist/node/internal/Contracts.js.map +1 -0
- package/dist/react-native/internal/Tokens.d.ts.map +1 -1
- package/dist/react-native/internal/Tokens.js +3 -6
- package/dist/react-native/internal/Tokens.js.map +1 -1
- package/dist/unplugin/index.d.ts.map +1 -1
- package/dist/unplugin/index.js +31 -13
- package/dist/unplugin/index.js.map +1 -1
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +33 -44
- package/dist/vite/index.js.map +1 -1
- package/dist/web/Css.d.ts +10 -5
- package/dist/web/Css.d.ts.map +1 -1
- package/dist/web/Css.js +170 -31
- package/dist/web/Css.js.map +1 -1
- package/dist/web/internal/ClassName.d.ts +49 -0
- package/dist/web/internal/ClassName.d.ts.map +1 -1
- package/dist/web/internal/ClassName.js +57 -0
- package/dist/web/internal/ClassName.js.map +1 -1
- package/dist/web/internal/Themes.d.ts.map +1 -1
- package/dist/web/internal/Themes.js +4 -2
- package/dist/web/internal/Themes.js.map +1 -1
- package/package.json +1 -1
- package/src/Style.ts +2 -0
- package/src/Vars.ts +104 -12
- package/src/compiler/Graph.ts +42 -2
- package/src/compiler/Source.ts +11 -3
- package/src/compiler/Transform.ts +130 -29
- package/src/compiler/internal/Applications.ts +239 -63
- package/src/compiler/internal/Compositions.ts +9 -0
- package/src/compiler/internal/Configurations.ts +8 -1
- package/src/compiler/internal/Contract.ts +59 -7
- package/src/compiler/internal/PackedStyles.ts +9 -0
- package/src/compiler/internal/Themes.ts +174 -52
- package/src/internal/Configuration.ts +26 -4
- package/src/internal/Token.ts +2 -0
- package/src/internal/VariableSets.ts +104 -24
- package/src/next/internal/Project.ts +28 -23
- package/src/next/loader.ts +2 -1
- package/src/node/Host.ts +31 -24
- package/src/node/internal/Contracts.ts +384 -0
- package/src/react-native/internal/Tokens.ts +5 -6
- package/src/unplugin/index.ts +35 -13
- package/src/vite/index.ts +38 -45
- package/src/web/Css.ts +228 -42
- package/src/web/internal/ClassName.ts +114 -0
- package/src/web/internal/Themes.ts +12 -11
|
@@ -34,6 +34,10 @@ export function create(
|
|
|
34
34
|
}
|
|
35
35
|
direct: Source.Call | undefined
|
|
36
36
|
members: Map<string, Source.Call>
|
|
37
|
+
/** Namespace statement start and member identifier ends by extracted name. */
|
|
38
|
+
namespace?: Namespace | undefined
|
|
39
|
+
/** Offset after which top-level reads observe an initialized binding. */
|
|
40
|
+
ready: number
|
|
37
41
|
}[] = []
|
|
38
42
|
|
|
39
43
|
for (const statement of program.body) {
|
|
@@ -43,6 +47,7 @@ export function create(
|
|
|
43
47
|
statement.body?.type === 'TSModuleBlock'
|
|
44
48
|
) {
|
|
45
49
|
const members = new Map<string, Source.Call>()
|
|
50
|
+
const ends = new Map<string, number>()
|
|
46
51
|
let valid = true
|
|
47
52
|
|
|
48
53
|
for (const member of statement.body.body) {
|
|
@@ -71,6 +76,7 @@ export function create(
|
|
|
71
76
|
}
|
|
72
77
|
|
|
73
78
|
members.set(declaration.id.name, call)
|
|
79
|
+
ends.set(call.name, declaration.id.end)
|
|
74
80
|
memberBindings.set(declaration.id.start, call.name)
|
|
75
81
|
}
|
|
76
82
|
}
|
|
@@ -80,6 +86,8 @@ export function create(
|
|
|
80
86
|
declaration: { id: statement.id },
|
|
81
87
|
direct: undefined,
|
|
82
88
|
members,
|
|
89
|
+
namespace: { ends, start: statement.start },
|
|
90
|
+
ready: statement.end,
|
|
83
91
|
})
|
|
84
92
|
|
|
85
93
|
continue
|
|
@@ -142,6 +150,7 @@ export function create(
|
|
|
142
150
|
declaration: { ...declaration, id: declaration.id },
|
|
143
151
|
direct,
|
|
144
152
|
members,
|
|
153
|
+
ready: declaration.end,
|
|
145
154
|
})
|
|
146
155
|
}
|
|
147
156
|
}
|
|
@@ -149,6 +158,9 @@ export function create(
|
|
|
149
158
|
if (!candidates.length) return undefined
|
|
150
159
|
|
|
151
160
|
const lexical = new Set<Ast.Node>()
|
|
161
|
+
// Reads inside functions, classes, or namespaces may run before initialization.
|
|
162
|
+
const deferred = new Set<Ast.Node>()
|
|
163
|
+
const terminals = new Set<Ast.Node>()
|
|
152
164
|
const scope = new Scope.Tracker({ preserveExitedScopes: true })
|
|
153
165
|
Walker.walk(program, { scopeTracker: scope })
|
|
154
166
|
scope.freeze()
|
|
@@ -185,8 +197,23 @@ export function create(
|
|
|
185
197
|
candidates.some(
|
|
186
198
|
(candidate) => candidate.declaration.id.start === id.start,
|
|
187
199
|
)
|
|
188
|
-
)
|
|
200
|
+
) {
|
|
189
201
|
lexical.add(node)
|
|
202
|
+
if (terminal(ancestors)) terminals.add(node)
|
|
203
|
+
if (
|
|
204
|
+
ancestors.some((node) =>
|
|
205
|
+
[
|
|
206
|
+
'ArrowFunctionExpression',
|
|
207
|
+
'ClassDeclaration',
|
|
208
|
+
'ClassExpression',
|
|
209
|
+
'FunctionDeclaration',
|
|
210
|
+
'FunctionExpression',
|
|
211
|
+
'TSModuleDeclaration',
|
|
212
|
+
].includes(node.type),
|
|
213
|
+
)
|
|
214
|
+
)
|
|
215
|
+
deferred.add(node)
|
|
216
|
+
}
|
|
190
217
|
},
|
|
191
218
|
leave() {
|
|
192
219
|
ancestors.pop()
|
|
@@ -200,6 +227,80 @@ export function create(
|
|
|
200
227
|
>(candidates.map(({ declaration }) => [declaration.id.name, []]))
|
|
201
228
|
let evaluation = false
|
|
202
229
|
|
|
230
|
+
// Returns undefined when any read escapes. Recipes, dynamic styles, and
|
|
231
|
+
// applications with overrides keep their runtime calls.
|
|
232
|
+
function collect(candidate: (typeof candidates)[number]):
|
|
233
|
+
| {
|
|
234
|
+
readonly applications: readonly Application[]
|
|
235
|
+
readonly called: ReadonlySet<string>
|
|
236
|
+
}
|
|
237
|
+
| undefined {
|
|
238
|
+
const { declaration, direct, members } = candidate
|
|
239
|
+
const applications: Application[] = []
|
|
240
|
+
const called = new Set<string>()
|
|
241
|
+
|
|
242
|
+
for (const reference of references.get(declaration.id.name) ?? []) {
|
|
243
|
+
if (reference === declaration.id) continue
|
|
244
|
+
|
|
245
|
+
let callee: Ast.Node = reference
|
|
246
|
+
let call = direct
|
|
247
|
+
|
|
248
|
+
if (!direct) {
|
|
249
|
+
const member = parents.get(reference)
|
|
250
|
+
|
|
251
|
+
if (
|
|
252
|
+
member?.type !== 'MemberExpression' ||
|
|
253
|
+
member.object !== reference ||
|
|
254
|
+
member.computed ||
|
|
255
|
+
member.optional ||
|
|
256
|
+
member.property.type !== 'Identifier'
|
|
257
|
+
)
|
|
258
|
+
return undefined
|
|
259
|
+
|
|
260
|
+
call = members.get(member.property.name)
|
|
261
|
+
callee = member
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (
|
|
265
|
+
call?.composition ||
|
|
266
|
+
call?.runtimeComposition ||
|
|
267
|
+
(!options.dynamic && (call?.slots || call?.recipe))
|
|
268
|
+
)
|
|
269
|
+
continue
|
|
270
|
+
|
|
271
|
+
const application = parents.get(callee)
|
|
272
|
+
|
|
273
|
+
if (
|
|
274
|
+
!call ||
|
|
275
|
+
application?.type !== 'CallExpression' ||
|
|
276
|
+
application.callee !== callee ||
|
|
277
|
+
application.optional
|
|
278
|
+
)
|
|
279
|
+
return undefined
|
|
280
|
+
|
|
281
|
+
// An override needs the runtime callable, but exposes no other definition.
|
|
282
|
+
if (!options.dynamic && application.arguments.length) {
|
|
283
|
+
called.add(call.name)
|
|
284
|
+
continue
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
applications.push({
|
|
288
|
+
calleeEnd: callee.end,
|
|
289
|
+
end: application.end,
|
|
290
|
+
initialized:
|
|
291
|
+
!deferred.has(reference) &&
|
|
292
|
+
reference.start >= candidate.ready &&
|
|
293
|
+
(!candidate.namespace ||
|
|
294
|
+
namespaceCounts.get(declaration.id.name) === 1),
|
|
295
|
+
name: call.name,
|
|
296
|
+
start: application.start,
|
|
297
|
+
terminal: terminals.has(reference),
|
|
298
|
+
})
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return { applications, called }
|
|
302
|
+
}
|
|
303
|
+
|
|
203
304
|
return {
|
|
204
305
|
dead() {
|
|
205
306
|
if (evaluation) return new Set<string>()
|
|
@@ -255,6 +356,58 @@ export function create(
|
|
|
255
356
|
|
|
256
357
|
return result
|
|
257
358
|
},
|
|
359
|
+
definitions() {
|
|
360
|
+
const namespaces: Namespace[] = []
|
|
361
|
+
const values = new Map<string, boolean>()
|
|
362
|
+
|
|
363
|
+
if (evaluation) return { namespaces, values }
|
|
364
|
+
|
|
365
|
+
const counts = new Map<string, number>()
|
|
366
|
+
|
|
367
|
+
for (const { declaration } of candidates)
|
|
368
|
+
counts.set(
|
|
369
|
+
declaration.id.name,
|
|
370
|
+
(counts.get(declaration.id.name) ?? 0) + 1,
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
for (const candidate of candidates) {
|
|
374
|
+
const name = candidate.declaration.id.name
|
|
375
|
+
if (counts.get(name)! > 1 || (namespaceCounts.get(name) ?? 0) > 1)
|
|
376
|
+
continue
|
|
377
|
+
|
|
378
|
+
const result = collect(candidate)
|
|
379
|
+
if (!result) continue
|
|
380
|
+
|
|
381
|
+
let erased = !!candidate.namespace
|
|
382
|
+
|
|
383
|
+
for (const call of candidate.direct
|
|
384
|
+
? [candidate.direct]
|
|
385
|
+
: candidate.members.values()) {
|
|
386
|
+
if (
|
|
387
|
+
call.composition ||
|
|
388
|
+
call.runtimeComposition ||
|
|
389
|
+
call.slots ||
|
|
390
|
+
call.recipe ||
|
|
391
|
+
result.called.has(call.name)
|
|
392
|
+
) {
|
|
393
|
+
erased = false
|
|
394
|
+
continue
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const initialized = result.applications.every(
|
|
398
|
+
(application) =>
|
|
399
|
+
application.name !== call.name || application.initialized,
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
values.set(call.name, initialized)
|
|
403
|
+
erased &&= initialized
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
if (erased) namespaces.push(candidate.namespace!)
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return { namespaces, values }
|
|
410
|
+
},
|
|
258
411
|
enter(node, parent) {
|
|
259
412
|
if (
|
|
260
413
|
node.type === 'MemberExpression' &&
|
|
@@ -280,68 +433,9 @@ export function create(
|
|
|
280
433
|
find() {
|
|
281
434
|
if (evaluation) return []
|
|
282
435
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
const applications: Application[] = []
|
|
287
|
-
let valid = true
|
|
288
|
-
|
|
289
|
-
for (const reference of references.get(declaration.id.name) ?? []) {
|
|
290
|
-
if (reference === declaration.id) continue
|
|
291
|
-
|
|
292
|
-
let callee: Ast.Node = reference
|
|
293
|
-
let call = direct
|
|
294
|
-
|
|
295
|
-
if (!direct) {
|
|
296
|
-
const member = parents.get(reference)
|
|
297
|
-
|
|
298
|
-
if (
|
|
299
|
-
member?.type !== 'MemberExpression' ||
|
|
300
|
-
member.object !== reference ||
|
|
301
|
-
member.computed ||
|
|
302
|
-
member.optional ||
|
|
303
|
-
member.property.type !== 'Identifier'
|
|
304
|
-
) {
|
|
305
|
-
valid = false
|
|
306
|
-
break
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
call = members.get(member.property.name)
|
|
310
|
-
callee = member
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
if (
|
|
314
|
-
call?.composition ||
|
|
315
|
-
call?.runtimeComposition ||
|
|
316
|
-
(!options.dynamic && (call?.slots || call?.recipe))
|
|
317
|
-
)
|
|
318
|
-
continue
|
|
319
|
-
|
|
320
|
-
const application = parents.get(callee)
|
|
321
|
-
|
|
322
|
-
if (
|
|
323
|
-
!call ||
|
|
324
|
-
application?.type !== 'CallExpression' ||
|
|
325
|
-
application.callee !== callee ||
|
|
326
|
-
application.optional ||
|
|
327
|
-
(!options.dynamic && application.arguments.length)
|
|
328
|
-
) {
|
|
329
|
-
valid = false
|
|
330
|
-
break
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
applications.push({
|
|
334
|
-
calleeEnd: callee.end,
|
|
335
|
-
end: application.end,
|
|
336
|
-
name: call.name,
|
|
337
|
-
start: application.start,
|
|
338
|
-
})
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
if (valid) result.push(...applications)
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
return result
|
|
436
|
+
return candidates.flatMap(
|
|
437
|
+
(candidate) => collect(candidate)?.applications ?? [],
|
|
438
|
+
)
|
|
345
439
|
},
|
|
346
440
|
}
|
|
347
441
|
}
|
|
@@ -352,16 +446,37 @@ type Application = {
|
|
|
352
446
|
readonly calleeEnd: number
|
|
353
447
|
/** End of the application. */
|
|
354
448
|
readonly end: number
|
|
449
|
+
/** Whether the read runs at top level after its definition initializes. */
|
|
450
|
+
readonly initialized: boolean
|
|
355
451
|
/** Extracted class identity. */
|
|
356
452
|
readonly name: string
|
|
357
453
|
/** Start of the application. */
|
|
358
454
|
readonly start: number
|
|
455
|
+
/** Whether a DOM element receives the props alone, so its classes meet no other style's. */
|
|
456
|
+
readonly terminal: boolean
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/** A top-level namespace whose members are all static definitions. */
|
|
460
|
+
type Namespace = {
|
|
461
|
+
/** Member identifier ends keyed by extracted name. */
|
|
462
|
+
readonly ends: ReadonlyMap<string, number>
|
|
463
|
+
/** Start of the namespace statement. */
|
|
464
|
+
readonly start: number
|
|
359
465
|
}
|
|
360
466
|
|
|
361
467
|
/** Binding references gathered during the transform's existing walk. */
|
|
362
468
|
type Collector = {
|
|
363
469
|
/** Definitions with no observable value reads, retaining escaped containers. */
|
|
364
470
|
dead: () => ReadonlySet<string>
|
|
471
|
+
/**
|
|
472
|
+
* Static definitions whose every read is a found application, keyed by
|
|
473
|
+
* extracted name with whether every read is initialized. Namespaces list
|
|
474
|
+
* those whose definitions all qualify with initialized reads.
|
|
475
|
+
*/
|
|
476
|
+
definitions: () => {
|
|
477
|
+
readonly namespaces: readonly Namespace[]
|
|
478
|
+
readonly values: ReadonlyMap<string, boolean>
|
|
479
|
+
}
|
|
365
480
|
/** Records relevant reads and their immediate parents. */
|
|
366
481
|
enter: (node: Ast.Node, parent: Ast.Node | null | undefined) => void
|
|
367
482
|
/** Resolves applications after traversal completes. */
|
|
@@ -376,3 +491,64 @@ export declare namespace create {
|
|
|
376
491
|
readonly dynamic?: boolean | undefined
|
|
377
492
|
}
|
|
378
493
|
}
|
|
494
|
+
|
|
495
|
+
// Matches a no-argument application spread onto, or read as the class of, a DOM element.
|
|
496
|
+
function terminal(path: readonly Ast.Node[]): boolean {
|
|
497
|
+
let index = path.length - 1
|
|
498
|
+
let node = path[index]
|
|
499
|
+
const member = path[index - 1]
|
|
500
|
+
if (member?.type === 'MemberExpression' && member.object === node)
|
|
501
|
+
node = path[--index]
|
|
502
|
+
|
|
503
|
+
const call = path[--index]
|
|
504
|
+
if (
|
|
505
|
+
call?.type !== 'CallExpression' ||
|
|
506
|
+
call.callee !== node ||
|
|
507
|
+
call.arguments.length
|
|
508
|
+
)
|
|
509
|
+
return false
|
|
510
|
+
|
|
511
|
+
const parent = path[--index]
|
|
512
|
+
if (parent?.type === 'JSXSpreadAttribute')
|
|
513
|
+
return parent.argument === call && alone(path[index - 1], parent)
|
|
514
|
+
|
|
515
|
+
if (
|
|
516
|
+
parent?.type !== 'MemberExpression' ||
|
|
517
|
+
parent.object !== call ||
|
|
518
|
+
parent.computed ||
|
|
519
|
+
parent.property.type !== 'Identifier' ||
|
|
520
|
+
!/^class(?:Name)?$/.test(parent.property.name)
|
|
521
|
+
)
|
|
522
|
+
return false
|
|
523
|
+
|
|
524
|
+
const container = path[--index]
|
|
525
|
+
const attribute = path[--index]
|
|
526
|
+
return (
|
|
527
|
+
container?.type === 'JSXExpressionContainer' &&
|
|
528
|
+
container.expression === parent &&
|
|
529
|
+
attribute?.type === 'JSXAttribute' &&
|
|
530
|
+
attribute.value === container &&
|
|
531
|
+
attribute.name.type === 'JSXIdentifier' &&
|
|
532
|
+
/^class(?:Name)?$/.test(attribute.name.name) &&
|
|
533
|
+
alone(path[index - 1], attribute)
|
|
534
|
+
)
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// Other spreads or class attributes could add another style's classes.
|
|
538
|
+
function alone(element: Ast.Node | undefined, own: Ast.Node): boolean {
|
|
539
|
+
return (
|
|
540
|
+
element?.type === 'JSXOpeningElement' &&
|
|
541
|
+
element.name.type === 'JSXIdentifier' &&
|
|
542
|
+
/^[a-z]/.test(element.name.name) &&
|
|
543
|
+
element.attributes.every(
|
|
544
|
+
(attribute) =>
|
|
545
|
+
attribute === own ||
|
|
546
|
+
(attribute.type === 'JSXAttribute' &&
|
|
547
|
+
!(
|
|
548
|
+
attribute.name.type === 'JSXIdentifier'
|
|
549
|
+
? attribute.name.name
|
|
550
|
+
: attribute.name.namespace.name
|
|
551
|
+
).startsWith('class')),
|
|
552
|
+
)
|
|
553
|
+
)
|
|
554
|
+
}
|
|
@@ -405,7 +405,15 @@ export function collect(options: collect.Options) {
|
|
|
405
405
|
node,
|
|
406
406
|
)
|
|
407
407
|
const name = `composition-${options.identity}-${node.start}`
|
|
408
|
+
// A composition of independent styles keeps their sharing contract.
|
|
409
|
+
const independent = (calls: readonly Source.Call[]) =>
|
|
410
|
+
calls.every(
|
|
411
|
+
(call) => styles.get(call.name)!.composition === 'independent',
|
|
412
|
+
)
|
|
413
|
+
? ({ composition: 'independent' } as const)
|
|
414
|
+
: {}
|
|
408
415
|
const style: Style.NamedStyle = {
|
|
416
|
+
...independent(selected),
|
|
409
417
|
cssOutput: 'atomic',
|
|
410
418
|
name,
|
|
411
419
|
declarations: [],
|
|
@@ -580,6 +588,7 @@ export function collect(options: collect.Options) {
|
|
|
580
588
|
.join(' '),
|
|
581
589
|
},
|
|
582
590
|
style: {
|
|
591
|
+
...independent(included),
|
|
583
592
|
cssOutput: 'atomic',
|
|
584
593
|
name,
|
|
585
594
|
declarations: [],
|
|
@@ -149,6 +149,9 @@ export function collect(options: collect.Options): Themes.Link {
|
|
|
149
149
|
: {}),
|
|
150
150
|
[Token.identity]: identity,
|
|
151
151
|
...(input.id !== undefined ? { [Token.complete]: true } : {}),
|
|
152
|
+
...(input.composition !== undefined
|
|
153
|
+
? { composition: input.composition }
|
|
154
|
+
: {}),
|
|
152
155
|
cssOutput: input.cssOutput ?? 'atomic',
|
|
153
156
|
...(input.defaultLayer !== undefined
|
|
154
157
|
? { defaultLayer: input.defaultLayer }
|
|
@@ -243,6 +246,7 @@ export function collect(options: collect.Options): Themes.Link {
|
|
|
243
246
|
return {}
|
|
244
247
|
})(),
|
|
245
248
|
...(input.id !== undefined ? { id: input.id } : {}),
|
|
249
|
+
...(input.composition ? { composition: input.composition } : {}),
|
|
246
250
|
...(input.cssOutput ? { cssOutput: input.cssOutput } : {}),
|
|
247
251
|
...(input.defaultLayer !== undefined
|
|
248
252
|
? { defaultLayer: input.defaultLayer }
|
|
@@ -330,7 +334,10 @@ export function type(value: unknown): string {
|
|
|
330
334
|
.join(';')}}`
|
|
331
335
|
}
|
|
332
336
|
|
|
333
|
-
|
|
337
|
+
/** Reads the authored leaf values of a reference tree. */
|
|
338
|
+
export function values(
|
|
339
|
+
tree: Theme.References<Theme.Tokens>,
|
|
340
|
+
): Record<string, unknown> {
|
|
334
341
|
return Object.fromEntries(
|
|
335
342
|
Object.entries(tree).map(([key, value]) => [
|
|
336
343
|
key,
|
|
@@ -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, 30, 31, 32, 33,
|
|
29
|
+
22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
|
|
30
30
|
].includes(data.version as number)
|
|
31
31
|
)
|
|
32
32
|
throw new Error('Unsupported Zyzz contract version.')
|
|
@@ -149,6 +149,22 @@ export function read(
|
|
|
149
149
|
| 'grouped'
|
|
150
150
|
| undefined
|
|
151
151
|
|
|
152
|
+
if (entry.composition !== undefined) {
|
|
153
|
+
if ((data.version as number) < 34)
|
|
154
|
+
throw new Error(
|
|
155
|
+
'Composition modes require contract version 34 or later.',
|
|
156
|
+
)
|
|
157
|
+
if (
|
|
158
|
+
entry.composition !== 'independent' &&
|
|
159
|
+
entry.composition !== 'ordered'
|
|
160
|
+
)
|
|
161
|
+
throw new Error('Invalid packed composition mode.')
|
|
162
|
+
}
|
|
163
|
+
const composition = entry.composition as
|
|
164
|
+
| 'independent'
|
|
165
|
+
| 'ordered'
|
|
166
|
+
| undefined
|
|
167
|
+
|
|
152
168
|
if (entry.defaultLayer !== undefined) {
|
|
153
169
|
if ((data.version as number) < 27)
|
|
154
170
|
throw new Error('Default layers require contract version 27 or later.')
|
|
@@ -180,6 +196,14 @@ export function read(
|
|
|
180
196
|
'Conflicting packed shorthand mappings for one theme identity.',
|
|
181
197
|
)
|
|
182
198
|
|
|
199
|
+
if (
|
|
200
|
+
contract &&
|
|
201
|
+
(contract.composition ?? 'ordered') !== (composition ?? 'ordered')
|
|
202
|
+
)
|
|
203
|
+
throw new Error(
|
|
204
|
+
'Conflicting packed composition modes for one theme identity.',
|
|
205
|
+
)
|
|
206
|
+
|
|
183
207
|
if (
|
|
184
208
|
contract &&
|
|
185
209
|
(contract.cssOutput ?? 'atomic') !== (cssOutput ?? 'atomic')
|
|
@@ -204,6 +228,7 @@ export function read(
|
|
|
204
228
|
...(entry.shorthands !== undefined
|
|
205
229
|
? { shorthands: Shorthands.read(entry.shorthands) }
|
|
206
230
|
: {}),
|
|
231
|
+
...(composition ? { composition } : {}),
|
|
207
232
|
...(cssOutput ? { cssOutput } : {}),
|
|
208
233
|
...(defaultLayer !== undefined ? { defaultLayer } : {}),
|
|
209
234
|
[Token.complete]: true,
|
|
@@ -333,7 +358,7 @@ export function read(
|
|
|
333
358
|
if (
|
|
334
359
|
![
|
|
335
360
|
9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
|
|
336
|
-
27, 28, 29, 30, 31, 32, 33,
|
|
361
|
+
27, 28, 29, 30, 31, 32, 33, 34,
|
|
337
362
|
].includes(data.version as number) ||
|
|
338
363
|
![
|
|
339
364
|
'cssFunction',
|
|
@@ -514,6 +539,14 @@ export function read(
|
|
|
514
539
|
'Configuration CSS output disagrees with linked theme metadata.',
|
|
515
540
|
)
|
|
516
541
|
|
|
542
|
+
if (
|
|
543
|
+
(options.composition ?? 'ordered') !==
|
|
544
|
+
(definition[Token.definition].contract.composition ?? 'ordered')
|
|
545
|
+
)
|
|
546
|
+
throw new Error(
|
|
547
|
+
'Configuration composition disagrees with linked theme metadata.',
|
|
548
|
+
)
|
|
549
|
+
|
|
517
550
|
if (
|
|
518
551
|
options.defaultLayer !==
|
|
519
552
|
definition[Token.definition].contract.defaultLayer
|
|
@@ -829,6 +862,9 @@ export function write(
|
|
|
829
862
|
...(theme[Token.definition].contract.defaultLayer !== undefined
|
|
830
863
|
? { defaultLayer: theme[Token.definition].contract.defaultLayer }
|
|
831
864
|
: {}),
|
|
865
|
+
...(theme[Token.definition].contract.composition
|
|
866
|
+
? { composition: theme[Token.definition].contract.composition }
|
|
867
|
+
: {}),
|
|
832
868
|
...(theme[Token.definition].contract.cssOutput
|
|
833
869
|
? { cssOutput: theme[Token.definition].contract.cssOutput }
|
|
834
870
|
: {}),
|
|
@@ -843,13 +879,29 @@ export function write(
|
|
|
843
879
|
},
|
|
844
880
|
]),
|
|
845
881
|
),
|
|
846
|
-
version:
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
882
|
+
version: (() => {
|
|
883
|
+
if (
|
|
884
|
+
Object.values(themes).some(
|
|
885
|
+
(theme) => theme[Token.definition].contract.composition,
|
|
886
|
+
) ||
|
|
887
|
+
Object.values(links).some(composed)
|
|
888
|
+
)
|
|
889
|
+
return 34
|
|
890
|
+
|
|
891
|
+
if (Object.values(links).some(nativeCatalog)) return 33
|
|
892
|
+
if (Object.values(links).some(nativeProvider)) return 32
|
|
893
|
+
|
|
894
|
+
return 31
|
|
895
|
+
})(),
|
|
851
896
|
})
|
|
852
897
|
|
|
898
|
+
function composed(link: Themes.Link): boolean {
|
|
899
|
+
return (
|
|
900
|
+
Boolean(link.style?.style.composition) ||
|
|
901
|
+
Object.values(link.members ?? {}).some(composed)
|
|
902
|
+
)
|
|
903
|
+
}
|
|
904
|
+
|
|
853
905
|
function nativeProvider(link: Themes.Link): boolean {
|
|
854
906
|
return (
|
|
855
907
|
link.call.nativeProvider === true ||
|
|
@@ -160,7 +160,16 @@ export function read(
|
|
|
160
160
|
item.cssOutput !== 'grouped'
|
|
161
161
|
)
|
|
162
162
|
throw new Error('Invalid packed style CSS output mode.')
|
|
163
|
+
if (
|
|
164
|
+
item.composition !== undefined &&
|
|
165
|
+
(version < 34 ||
|
|
166
|
+
(item.composition !== 'independent' && item.composition !== 'ordered'))
|
|
167
|
+
)
|
|
168
|
+
throw new Error('Invalid packed style composition mode.')
|
|
163
169
|
return {
|
|
170
|
+
...(item.composition
|
|
171
|
+
? { composition: item.composition as 'independent' | 'ordered' }
|
|
172
|
+
: {}),
|
|
164
173
|
...(item.cssOutput
|
|
165
174
|
? { cssOutput: item.cssOutput as 'atomic' | 'grouped' }
|
|
166
175
|
: {}),
|