zyzz 0.0.13 → 0.0.15
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 +49 -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 +8 -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 +30 -7
- 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 +14 -1
- package/dist/web/internal/Themes.d.ts.map +1 -1
- package/dist/web/internal/Themes.js +45 -15
- package/dist/web/internal/Themes.js.map +1 -1
- package/package.json +1 -1
- package/src/compiler/Graph.ts +61 -6
- package/src/compiler/Source.ts +6 -1
- package/src/compiler/Transform.ts +12 -2
- package/src/compiler/internal/Stylesheets.ts +3 -0
- package/src/internal/Identity.ts +32 -0
- package/src/next/loader.ts +30 -11
- package/src/web/Css.ts +7 -1
- package/src/web/internal/Themes.ts +61 -13
package/src/compiler/Graph.ts
CHANGED
|
@@ -17,6 +17,7 @@ import type * as Theme from '../internal/Theme.js'
|
|
|
17
17
|
import * as Token from '../internal/Token.js'
|
|
18
18
|
import * as Contract from './internal/Contract.js'
|
|
19
19
|
import * as Relative from './internal/Relative.js'
|
|
20
|
+
import * as ThemeRules from '../web/internal/Themes.js'
|
|
20
21
|
import * as Themes from './internal/Themes.js'
|
|
21
22
|
import * as Source from './Source.js'
|
|
22
23
|
import * as Scope from './internal/Scope.js'
|
|
@@ -75,11 +76,13 @@ export declare namespace compile {
|
|
|
75
76
|
type ReturnType = {
|
|
76
77
|
/** Packed resources deduplicated by the host across scoped source outputs. */
|
|
77
78
|
readonly [Stylesheets.packed]?: readonly Stylesheets.Resource[]
|
|
79
|
+
/** Independently loaded reset for module-based hosts. */
|
|
80
|
+
readonly [Stylesheets.reset]?: Stylesheets.Resource | undefined
|
|
78
81
|
/** Versioned compiler-only JSON per module; publish beside the compiled entrypoint as <entry>.zyzz.json. */
|
|
79
82
|
readonly contracts: Readonly<Record<string, string>>
|
|
80
83
|
/** Direct static runtime source and library-contract dependencies, keyed by module identity. */
|
|
81
84
|
readonly dependencies: Readonly<Record<string, readonly string[]>>
|
|
82
|
-
/**
|
|
85
|
+
/** Shared contributions and responsive token defaults. Load before module CSS. */
|
|
83
86
|
readonly sharedCss?: string | undefined
|
|
84
87
|
/** Source map for source-owned and packed global contributions. */
|
|
85
88
|
readonly sharedCssMap?: Mapping.EncodedSourceMap | undefined
|
|
@@ -190,6 +193,11 @@ function build(options: compile.Options, cache?: Cache): Cache {
|
|
|
190
193
|
if (cache?.reset !== options.reset) cache = undefined
|
|
191
194
|
if (cache?.cssOutput !== options.cssOutput) cache = undefined
|
|
192
195
|
if (cache?.composition !== options.composition) cache = undefined
|
|
196
|
+
if (
|
|
197
|
+
(cache?.entry === undefined) !==
|
|
198
|
+
(options[Stylesheets.entry] === undefined)
|
|
199
|
+
)
|
|
200
|
+
cache = undefined
|
|
193
201
|
const ids = Object.keys(options.modules).sort()
|
|
194
202
|
const programs = new Map<string, ReturnType<typeof Syntax.parse>>()
|
|
195
203
|
function parse(input: Syntax.parse.Options) {
|
|
@@ -1329,7 +1337,7 @@ function build(options: compile.Options, cache?: Cache): Cache {
|
|
|
1329
1337
|
const sharedVisited = new Set<string>()
|
|
1330
1338
|
const packed: Stylesheets.Resource[] = []
|
|
1331
1339
|
|
|
1332
|
-
const
|
|
1340
|
+
const sharedSections = (() => {
|
|
1333
1341
|
try {
|
|
1334
1342
|
const entry = options[Stylesheets.entry]
|
|
1335
1343
|
const packedSections =
|
|
@@ -1379,7 +1387,7 @@ function build(options: compile.Options, cache?: Cache): Cache {
|
|
|
1379
1387
|
if (order.css) packed.unshift({ ...order, id: order.css })
|
|
1380
1388
|
}
|
|
1381
1389
|
|
|
1382
|
-
return
|
|
1390
|
+
return [
|
|
1383
1391
|
...(resetSource === undefined
|
|
1384
1392
|
? []
|
|
1385
1393
|
: [
|
|
@@ -1393,7 +1401,7 @@ function build(options: compile.Options, cache?: Cache): Cache {
|
|
|
1393
1401
|
},
|
|
1394
1402
|
]),
|
|
1395
1403
|
...sharedSections,
|
|
1396
|
-
...(options.reset === undefined
|
|
1404
|
+
...(options.reset === undefined || entry !== undefined
|
|
1397
1405
|
? []
|
|
1398
1406
|
: [
|
|
1399
1407
|
{
|
|
@@ -1404,7 +1412,7 @@ function build(options: compile.Options, cache?: Cache): Cache {
|
|
|
1404
1412
|
layers: [['reset']],
|
|
1405
1413
|
},
|
|
1406
1414
|
]),
|
|
1407
|
-
]
|
|
1415
|
+
] satisfies Stylesheets.Section[]
|
|
1408
1416
|
} catch (error) {
|
|
1409
1417
|
return fail(
|
|
1410
1418
|
error instanceof Stylesheets.ConflictError ? error.source : ids[0]!,
|
|
@@ -1413,7 +1421,19 @@ function build(options: compile.Options, cache?: Cache): Cache {
|
|
|
1413
1421
|
}
|
|
1414
1422
|
})()
|
|
1415
1423
|
|
|
1416
|
-
|
|
1424
|
+
function renderShared(sections: readonly Stylesheets.Section[]) {
|
|
1425
|
+
try {
|
|
1426
|
+
return Stylesheets.render(sections)
|
|
1427
|
+
} catch (error) {
|
|
1428
|
+
return fail(
|
|
1429
|
+
error instanceof Stylesheets.ConflictError ? error.source : ids[0]!,
|
|
1430
|
+
(error as Error).message,
|
|
1431
|
+
)
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
const contributions = renderShared(sharedSections)
|
|
1436
|
+
|
|
1417
1437
|
// Every stylesheet includes all graph scopes, including unimported alternatives.
|
|
1418
1438
|
const names = Object.keys(themes)
|
|
1419
1439
|
const previousNames = Object.keys(previous?.themes ?? {})
|
|
@@ -1549,6 +1569,8 @@ function build(options: compile.Options, cache?: Cache): Cache {
|
|
|
1549
1569
|
extracted.get(moduleId) === previous!.extracted.get(moduleId)
|
|
1550
1570
|
? previous!.result.modules[moduleId]!
|
|
1551
1571
|
: Transform.compile({
|
|
1572
|
+
[ThemeRules.shared]:
|
|
1573
|
+
options[Stylesheets.entry] === undefined ? 'defaults' : 'all',
|
|
1552
1574
|
compiler: options.compiler,
|
|
1553
1575
|
development: options.development,
|
|
1554
1576
|
composition: options.composition,
|
|
@@ -1627,6 +1649,23 @@ function build(options: compile.Options, cache?: Cache): Cache {
|
|
|
1627
1649
|
styleClasses[call.identity] = modules[moduleId]!.classes[call.name]!
|
|
1628
1650
|
}
|
|
1629
1651
|
|
|
1652
|
+
const defaults = new Map<string, Stylesheets.Section>()
|
|
1653
|
+
if (options[Stylesheets.entry] === undefined)
|
|
1654
|
+
for (const [source, output] of Object.entries(modules))
|
|
1655
|
+
for (const resource of output[ThemeRules.shared] ?? [])
|
|
1656
|
+
if (!defaults.has(resource.id))
|
|
1657
|
+
defaults.set(resource.id, {
|
|
1658
|
+
css: resource.css,
|
|
1659
|
+
key: resource.id,
|
|
1660
|
+
layers: [],
|
|
1661
|
+
source,
|
|
1662
|
+
})
|
|
1663
|
+
|
|
1664
|
+
const shared = defaults.size
|
|
1665
|
+
? renderShared([...sharedSections, ...defaults.values()])
|
|
1666
|
+
: contributions
|
|
1667
|
+
const sharedCss = shared.css
|
|
1668
|
+
|
|
1630
1669
|
function publishedStyle(link: Themes.Link): Themes.Link {
|
|
1631
1670
|
return {
|
|
1632
1671
|
...link,
|
|
@@ -1698,6 +1737,22 @@ function build(options: compile.Options, cache?: Cache): Cache {
|
|
|
1698
1737
|
transformed,
|
|
1699
1738
|
resolutions: Object.freeze(resolutions),
|
|
1700
1739
|
result: Object.freeze({
|
|
1740
|
+
...(options[Stylesheets.entry] !== undefined &&
|
|
1741
|
+
options.reset !== undefined
|
|
1742
|
+
? {
|
|
1743
|
+
[Stylesheets.reset]: {
|
|
1744
|
+
id: 'zyzz/reset.css',
|
|
1745
|
+
...Stylesheets.render([
|
|
1746
|
+
{
|
|
1747
|
+
content: options.reset,
|
|
1748
|
+
css: options.reset,
|
|
1749
|
+
layers: [['reset']],
|
|
1750
|
+
source: 'zyzz/reset.css',
|
|
1751
|
+
},
|
|
1752
|
+
]),
|
|
1753
|
+
},
|
|
1754
|
+
}
|
|
1755
|
+
: {}),
|
|
1701
1756
|
...(packed.length ? { [Stylesheets.packed]: packed } : {}),
|
|
1702
1757
|
...(sharedCss
|
|
1703
1758
|
? {
|
package/src/compiler/Source.ts
CHANGED
|
@@ -263,7 +263,12 @@ export function extract(options: extract.Options): extract.ReturnType {
|
|
|
263
263
|
try {
|
|
264
264
|
return Themes.collect(program, {
|
|
265
265
|
staticBindings: staticData.bindings,
|
|
266
|
-
namespace
|
|
266
|
+
namespace: `src-${Identity.label(
|
|
267
|
+
options.moduleId
|
|
268
|
+
.split('/')
|
|
269
|
+
.at(-1)!
|
|
270
|
+
.replace(/\.[cm]?[jt]sx?$/, ''),
|
|
271
|
+
)}-${Identity.compact(options.moduleId)}`,
|
|
267
272
|
contributionCalls: new Set(
|
|
268
273
|
contributions.calls.map((call) => call.start),
|
|
269
274
|
),
|
|
@@ -15,6 +15,7 @@ import * as Scheme from '../internal/Scheme.js'
|
|
|
15
15
|
import * as Syntax from './internal/Syntax.js'
|
|
16
16
|
import * as Source from './Source.js'
|
|
17
17
|
import type * as Style from '../Style.js'
|
|
18
|
+
import * as ThemeRules from '../web/internal/Themes.js'
|
|
18
19
|
import * as Themes from './internal/Themes.js'
|
|
19
20
|
import * as Walker from 'oxc-walker'
|
|
20
21
|
|
|
@@ -195,6 +196,7 @@ export function compile(options: compile.Options): compile.ReturnType {
|
|
|
195
196
|
)
|
|
196
197
|
|
|
197
198
|
const emitted = Css.compile({
|
|
199
|
+
[ThemeRules.shared]: options[ThemeRules.shared],
|
|
198
200
|
development: options.development,
|
|
199
201
|
scope: options.moduleId,
|
|
200
202
|
composition: options.composition,
|
|
@@ -595,7 +597,8 @@ export function compile(options: compile.Options): compile.ReturnType {
|
|
|
595
597
|
return `{${helpers(JSON.stringify(call.options?.themes ? entries : []), call.options?.themes ? String(fallback) : undefined).replace(/,$/, '')}}`
|
|
596
598
|
usesSelection = true
|
|
597
599
|
const selector = `${selection}.create(${JSON.stringify(entries)},${call.options?.output === 'html'},'set',${JSON.stringify(fallback)})`
|
|
598
|
-
|
|
600
|
+
// Rollup drops required selectors when a pure call initializes a destructured object property.
|
|
601
|
+
return `{${helpers(JSON.stringify(call.options?.themes ? entries : []), call.options?.themes ? String(fallback) : undefined)}vars:${selector}}`
|
|
599
602
|
}
|
|
600
603
|
|
|
601
604
|
if (call.options?.themes) {
|
|
@@ -980,7 +983,7 @@ export function compile(options: compile.Options): compile.ReturnType {
|
|
|
980
983
|
|
|
981
984
|
if (
|
|
982
985
|
rule.startsWith(':root{') ||
|
|
983
|
-
rule.startsWith(':where(*){--z-
|
|
986
|
+
rule.startsWith(':where(*){--z-') ||
|
|
984
987
|
(rule.startsWith('@media ') &&
|
|
985
988
|
(rule.includes(':root{') ||
|
|
986
989
|
Object.values(emitted.vars).some((name) =>
|
|
@@ -1150,6 +1153,9 @@ export function compile(options: compile.Options): compile.ReturnType {
|
|
|
1150
1153
|
true,
|
|
1151
1154
|
)
|
|
1152
1155
|
return Object.freeze({
|
|
1156
|
+
...(emitted[ThemeRules.shared]
|
|
1157
|
+
? { [ThemeRules.shared]: emitted[ThemeRules.shared] }
|
|
1158
|
+
: {}),
|
|
1153
1159
|
classes,
|
|
1154
1160
|
code: portable ? options.source : module.toString(),
|
|
1155
1161
|
css: namespaced.css,
|
|
@@ -1172,6 +1178,8 @@ export declare namespace compile {
|
|
|
1172
1178
|
type ErrorType = Css.CompileError | Source.ExtractError
|
|
1173
1179
|
/** Supplied module identity and source; no file loading occurs. */
|
|
1174
1180
|
type Options = Source.extract.Options & {
|
|
1181
|
+
/** Separates generated token definitions for independently loaded modules. */
|
|
1182
|
+
readonly [ThemeRules.shared]?: 'all' | 'defaults' | undefined
|
|
1175
1183
|
/** Disable source rewriting while emitting CSS for runtime authoring. Defaults to true. */
|
|
1176
1184
|
readonly compiler?: boolean | undefined
|
|
1177
1185
|
/** Whether compiled applications can be combined with one another. */
|
|
@@ -1191,6 +1199,8 @@ export declare namespace compile {
|
|
|
1191
1199
|
type ReturnType = {
|
|
1192
1200
|
/** Internal native node replacements, when supplied by the graph adapter. */
|
|
1193
1201
|
readonly [Edits.key]?: readonly Edits.Edit[] | undefined
|
|
1202
|
+
/** Generated token resources delivered alongside this module's CSS. */
|
|
1203
|
+
readonly [ThemeRules.shared]?: readonly ThemeRules.Resource[] | undefined
|
|
1194
1204
|
/** Module-scoped class lists keyed by extracted definition identity. */
|
|
1195
1205
|
readonly classes: Readonly<Record<string, string>>
|
|
1196
1206
|
/** Rewritten source with imports for surviving props callables. */
|
|
@@ -23,6 +23,9 @@ export const entry = Symbol('stylesheet entry')
|
|
|
23
23
|
/** Independently shared packed sections for hosts that load source modules separately. */
|
|
24
24
|
export const packed = Symbol('packed stylesheets')
|
|
25
25
|
|
|
26
|
+
/** Reset stylesheet shared independently of authored contributions and layer order. */
|
|
27
|
+
export const reset = Symbol('reset stylesheet')
|
|
28
|
+
|
|
26
29
|
/** One packed resource with stable source ownership for development updates. */
|
|
27
30
|
export type Resource = ReturnType<typeof render> & { readonly id: string }
|
|
28
31
|
|
package/src/internal/Identity.ts
CHANGED
|
@@ -3,6 +3,28 @@ import * as Binding from './Binding.js'
|
|
|
3
3
|
import * as Token from './Token.js'
|
|
4
4
|
import type * as Style from '../Style.js'
|
|
5
5
|
|
|
6
|
+
/** Encodes both hash streams in eleven CSS identifier characters. */
|
|
7
|
+
export function compact(value: string): string {
|
|
8
|
+
let first = 2166136261
|
|
9
|
+
let second = 5381
|
|
10
|
+
for (let index = 0; index < value.length; index++) {
|
|
11
|
+
const code = value.charCodeAt(index)
|
|
12
|
+
first = Math.imul(first ^ code, 16777619)
|
|
13
|
+
second = Math.imul(second, 33) ^ code
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const alphabet =
|
|
17
|
+
'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-'
|
|
18
|
+
let bits = (BigInt(first >>> 0) << 32n) | BigInt(second >>> 0)
|
|
19
|
+
let result = ''
|
|
20
|
+
for (let index = 0; index < 11; index++) {
|
|
21
|
+
result = alphabet[Number(bits & 63n)]! + result
|
|
22
|
+
bits >>= 6n
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return result
|
|
26
|
+
}
|
|
27
|
+
|
|
6
28
|
/** Encodes an explicit identity without losing punctuation or Unicode distinctions. */
|
|
7
29
|
export function encode(value: string): string {
|
|
8
30
|
return Array.from(value, (character) =>
|
|
@@ -10,6 +32,16 @@ export function encode(value: string): string {
|
|
|
10
32
|
).join('-')
|
|
11
33
|
}
|
|
12
34
|
|
|
35
|
+
/** Keeps an authored label readable while its separate hash disambiguates spelling. */
|
|
36
|
+
export function label(value: string): string {
|
|
37
|
+
return (
|
|
38
|
+
value
|
|
39
|
+
.replace(/[^a-zA-Z0-9-]+/g, '-')
|
|
40
|
+
.replace(/^-+|-+$/g, '')
|
|
41
|
+
.slice(0, 64) || 'value'
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
13
45
|
/** Requires an explicit identity at an uncompiled authoring boundary. */
|
|
14
46
|
export function requireId(id: string | undefined, kind: string): string {
|
|
15
47
|
if (!id)
|
package/src/next/loader.ts
CHANGED
|
@@ -3,8 +3,9 @@ import * as AtRules from '../compiler/internal/AtRules.js'
|
|
|
3
3
|
import * as Crypto from 'node:crypto'
|
|
4
4
|
import * as Fs from 'node:fs/promises'
|
|
5
5
|
import * as Path from 'node:path'
|
|
6
|
-
import * as Stylesheets from '../compiler/internal/Stylesheets.js'
|
|
7
6
|
import * as Project from './internal/Project.js'
|
|
7
|
+
import * as Stylesheets from '../compiler/internal/Stylesheets.js'
|
|
8
|
+
import * as ThemeRules from '../web/internal/Themes.js'
|
|
8
9
|
|
|
9
10
|
type Context = Project.Context & {
|
|
10
11
|
async(): (error: Error | null, code?: string, map?: object) => void
|
|
@@ -36,9 +37,16 @@ async function compile(context: Context, source: string) {
|
|
|
36
37
|
const root = options.root
|
|
37
38
|
const directory = Path.resolve(root, '.zyzz', 'next')
|
|
38
39
|
if (options.mode) {
|
|
39
|
-
const
|
|
40
|
+
const query = new URLSearchParams(context.resourceQuery)
|
|
41
|
+
const hash = query.get('zyzz')
|
|
40
42
|
if (!hash || !/^[a-f0-9]{64}$/.test(hash))
|
|
41
43
|
throw new Error('Invalid generated stylesheet request.')
|
|
44
|
+
const css = query.get('css')
|
|
45
|
+
if (css !== null)
|
|
46
|
+
return {
|
|
47
|
+
code: Buffer.from(css, 'base64url').toString('utf8'),
|
|
48
|
+
map: undefined,
|
|
49
|
+
}
|
|
42
50
|
const file = Path.join(directory, `${hash}.css`)
|
|
43
51
|
context.addDependency(file)
|
|
44
52
|
return { code: await Fs.readFile(file, 'utf8'), map: undefined }
|
|
@@ -178,6 +186,15 @@ async function compile(context: Context, source: string) {
|
|
|
178
186
|
}
|
|
179
187
|
|
|
180
188
|
const styles = [
|
|
189
|
+
...(graph[Stylesheets.reset]
|
|
190
|
+
? [
|
|
191
|
+
{
|
|
192
|
+
css: graph[Stylesheets.reset].css,
|
|
193
|
+
id: graph[Stylesheets.reset].id,
|
|
194
|
+
map: JSON.stringify(graph[Stylesheets.reset].map),
|
|
195
|
+
},
|
|
196
|
+
]
|
|
197
|
+
: []),
|
|
181
198
|
...(await Promise.all(
|
|
182
199
|
(graph[Stylesheets.packed] ?? []).map(async (resource) => {
|
|
183
200
|
const output = await compileShared(resource)
|
|
@@ -193,6 +210,11 @@ async function compile(context: Context, source: string) {
|
|
|
193
210
|
css: shared?.code.toString(),
|
|
194
211
|
map: shared?.map?.toString(),
|
|
195
212
|
},
|
|
213
|
+
...(output[ThemeRules.shared] ?? []).map((resource) => ({
|
|
214
|
+
css: resource.css,
|
|
215
|
+
id: resource.id,
|
|
216
|
+
map: undefined,
|
|
217
|
+
})),
|
|
196
218
|
{ id: undefined, css: output.css, map: JSON.stringify(cssMap) },
|
|
197
219
|
]
|
|
198
220
|
const requests: string[] = []
|
|
@@ -215,13 +237,7 @@ async function compile(context: Context, source: string) {
|
|
|
215
237
|
: '')
|
|
216
238
|
const hash = Crypto.createHash('sha256')
|
|
217
239
|
.update(
|
|
218
|
-
options.development
|
|
219
|
-
!(
|
|
220
|
-
index === (graph[Stylesheets.packed]?.length ?? 0) &&
|
|
221
|
-
graph.sharedCssMap?.sources.every(
|
|
222
|
-
(name) => name === 'zyzz/reset.css',
|
|
223
|
-
)
|
|
224
|
-
)
|
|
240
|
+
options.development
|
|
225
241
|
? (stylesheet.id ??
|
|
226
242
|
JSON.stringify([context.resourcePath, index, graph.dependencies]))
|
|
227
243
|
: css,
|
|
@@ -240,7 +256,7 @@ async function compile(context: Context, source: string) {
|
|
|
240
256
|
await Fs.writeFile(temporary, css)
|
|
241
257
|
await Fs.rename(temporary, file)
|
|
242
258
|
}
|
|
243
|
-
} else {
|
|
259
|
+
} else if (options.bundler !== 'turbopack') {
|
|
244
260
|
try {
|
|
245
261
|
await Fs.writeFile(file, css, { flag: 'wx' })
|
|
246
262
|
} catch (error) {
|
|
@@ -264,7 +280,10 @@ async function compile(context: Context, source: string) {
|
|
|
264
280
|
)
|
|
265
281
|
if (existing !== code) await Fs.writeFile(request, code)
|
|
266
282
|
} else if (options.bundler === 'turbopack') {
|
|
267
|
-
|
|
283
|
+
// Persist CSS with the cached transform, independently of generated filesystem artifacts.
|
|
284
|
+
request =
|
|
285
|
+
Path.join(directory, 'style.css') +
|
|
286
|
+
`?zyzz=${hash}&css=${Buffer.from(css).toString('base64url')}`
|
|
268
287
|
}
|
|
269
288
|
const relative = Path.relative(Path.dirname(context.resourcePath), request)
|
|
270
289
|
.split(Path.sep)
|
package/src/web/Css.ts
CHANGED
|
@@ -670,8 +670,9 @@ export function compile<
|
|
|
670
670
|
? (theme ??= Themes.create()).emit(
|
|
671
671
|
options.vars ?? {},
|
|
672
672
|
options.schemes ?? false,
|
|
673
|
+
options[Themes.shared],
|
|
673
674
|
)
|
|
674
|
-
: { classes: Object.freeze({}), css: '' }
|
|
675
|
+
: { classes: Object.freeze({}), css: '', resources: [] }
|
|
675
676
|
} catch (error) {
|
|
676
677
|
throw new CompileError([
|
|
677
678
|
{
|
|
@@ -692,6 +693,7 @@ export function compile<
|
|
|
692
693
|
.join('\n')
|
|
693
694
|
|
|
694
695
|
return Object.freeze({
|
|
696
|
+
...(scopes.resources.length ? { [Themes.shared]: scopes.resources } : {}),
|
|
695
697
|
...(contributionCss ? { contributionCss, scopedCss } : {}),
|
|
696
698
|
classes: Object.freeze(classes),
|
|
697
699
|
css: [contributionCss, scopedCss].filter(Boolean).join('\n'),
|
|
@@ -709,6 +711,8 @@ export declare namespace compile {
|
|
|
709
711
|
name extends string = string,
|
|
710
712
|
themeName extends string = string,
|
|
711
713
|
> = {
|
|
714
|
+
/** Separates generated token rules for hosts with independent CSS resources. */
|
|
715
|
+
readonly [Themes.shared]?: 'all' | 'defaults' | undefined
|
|
712
716
|
/** Fixed class identities used by CSS-only consumers. */
|
|
713
717
|
readonly names?: Readonly<Record<string, string>> | undefined
|
|
714
718
|
/**
|
|
@@ -743,6 +747,8 @@ export declare namespace compile {
|
|
|
743
747
|
name extends string = string,
|
|
744
748
|
themeName extends string = string,
|
|
745
749
|
> = {
|
|
750
|
+
/** Shared token resources omitted from this module's stylesheet. */
|
|
751
|
+
readonly [Themes.shared]?: readonly Themes.Resource[] | undefined
|
|
746
752
|
/** Contribution text separated for graph-wide hoisting. */
|
|
747
753
|
readonly contributionCss?: string | undefined
|
|
748
754
|
/** Ordinary scope and style rules when contributions were supplied. */
|
|
@@ -41,12 +41,14 @@ 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
54
|
const rules: string[] = []
|
|
@@ -101,13 +103,20 @@ export function create() {
|
|
|
101
103
|
if (value === undefined)
|
|
102
104
|
throw new Error('Theme scope is missing a live token.')
|
|
103
105
|
|
|
104
|
-
|
|
106
|
+
const label = contract.identity?.startsWith('src-') ? path : undefined
|
|
107
|
+
return `${name}:${literal(value, label)};`
|
|
105
108
|
})
|
|
106
109
|
.join('')
|
|
107
110
|
|
|
108
111
|
rules.push(`.${className}{${body}}`)
|
|
109
112
|
for (const [path, name] of contract.paths)
|
|
110
|
-
conditional(
|
|
113
|
+
conditional(
|
|
114
|
+
data.values[path]!,
|
|
115
|
+
name,
|
|
116
|
+
`.${className}`,
|
|
117
|
+
rules,
|
|
118
|
+
contract.identity?.startsWith('src-') ? path : undefined,
|
|
119
|
+
)
|
|
111
120
|
}
|
|
112
121
|
|
|
113
122
|
// Scheme rules travel with the module that can apply them, so lowered
|
|
@@ -116,26 +125,50 @@ export function create() {
|
|
|
116
125
|
|
|
117
126
|
return {
|
|
118
127
|
classes: Object.freeze(classes),
|
|
119
|
-
css:
|
|
128
|
+
css:
|
|
129
|
+
separate === 'all'
|
|
130
|
+
? ''
|
|
131
|
+
: [...(separate ? [] : defaults.values()), ...rules].join('\n'),
|
|
132
|
+
resources: separate
|
|
133
|
+
? [
|
|
134
|
+
...[...defaults].map(([id, css]) => ({ css, id })),
|
|
135
|
+
// Scope rules stay together to preserve cascade order without one import per token.
|
|
136
|
+
...(separate === 'all' && rules.length
|
|
137
|
+
? [
|
|
138
|
+
{
|
|
139
|
+
css: rules.join('\n'),
|
|
140
|
+
id: JSON.stringify([
|
|
141
|
+
classes,
|
|
142
|
+
[...contracts.values()].flatMap((contract) => [
|
|
143
|
+
...contract.paths.values(),
|
|
144
|
+
]),
|
|
145
|
+
]),
|
|
146
|
+
},
|
|
147
|
+
]
|
|
148
|
+
: []),
|
|
149
|
+
]
|
|
150
|
+
: [],
|
|
120
151
|
}
|
|
121
152
|
}
|
|
122
153
|
|
|
123
|
-
function literal(value: Token.Value): string {
|
|
154
|
+
function literal(value: Token.Value, label?: string): string {
|
|
124
155
|
if (Token.is(value)) return serialize(value)
|
|
125
156
|
if (typeof value !== 'object') return String(value)
|
|
126
157
|
if ('default' in value) {
|
|
127
158
|
// Separate fallback properties preserve extensions and resolve references within each scope.
|
|
128
|
-
const base = literal(value.default)
|
|
159
|
+
const base = literal(value.default, label)
|
|
129
160
|
const rules = [`:where(*){--fallback:${base};}`]
|
|
130
|
-
conditional(value, '--fallback', ':where(*)', rules)
|
|
161
|
+
conditional(value, '--fallback', ':where(*)', rules, label)
|
|
131
162
|
const css = rules.join('')
|
|
132
|
-
const name =
|
|
163
|
+
const name = label
|
|
164
|
+
? `--z-${Identity.label(label)}-fallback-${Identity.compact(css)}`
|
|
165
|
+
: `--z-f${Identity.hash(css)}`
|
|
133
166
|
const emitted = [`:where(*){${name}:${base};}`]
|
|
134
|
-
conditional(value, name, ':where(*)', emitted)
|
|
167
|
+
conditional(value, name, ':where(*)', emitted, label)
|
|
135
168
|
defaults.set(name, emitted.join(''))
|
|
136
169
|
return `var(${name})`
|
|
137
170
|
}
|
|
138
|
-
return `light-dark(${literal(value.light)},${literal(value.dark)})`
|
|
171
|
+
return `light-dark(${literal(value.light, label)},${literal(value.dark, label)})`
|
|
139
172
|
}
|
|
140
173
|
|
|
141
174
|
function conditional(
|
|
@@ -143,6 +176,7 @@ export function create() {
|
|
|
143
176
|
name: string,
|
|
144
177
|
selector: string,
|
|
145
178
|
rules: string[],
|
|
179
|
+
label?: string,
|
|
146
180
|
) {
|
|
147
181
|
if (
|
|
148
182
|
!value ||
|
|
@@ -151,11 +185,11 @@ export function create() {
|
|
|
151
185
|
!('default' in value)
|
|
152
186
|
)
|
|
153
187
|
return
|
|
154
|
-
conditional(value.default, name, selector, rules)
|
|
188
|
+
conditional(value.default, name, selector, rules, label)
|
|
155
189
|
for (const [query, entry] of Object.entries(value)) {
|
|
156
190
|
if (query === 'default') continue
|
|
157
|
-
const nested = [`${selector}{${name}:${literal(entry)};}`]
|
|
158
|
-
conditional(entry, name, selector, nested)
|
|
191
|
+
const nested = [`${selector}{${name}:${literal(entry, label)};}`]
|
|
192
|
+
conditional(entry, name, selector, nested, label)
|
|
159
193
|
rules.push(`${query}{${nested.join('')}}`)
|
|
160
194
|
}
|
|
161
195
|
}
|
|
@@ -171,6 +205,20 @@ export function encode(value: string): string {
|
|
|
171
205
|
)
|
|
172
206
|
}
|
|
173
207
|
|
|
208
|
+
/** One generated definition whose scope order must remain intact. */
|
|
209
|
+
export type Resource = {
|
|
210
|
+
/** Complete fallback or scoped token definition. */
|
|
211
|
+
readonly css: string
|
|
212
|
+
/** Stable identity for stylesheet replacement during development. */
|
|
213
|
+
readonly id: string
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/** Selects independently shareable defaults or all token rules. */
|
|
217
|
+
export const shared = Symbol('shared token rules')
|
|
218
|
+
|
|
174
219
|
function variable(index: number | string, path: string): string {
|
|
220
|
+
// Packed declarations retain the spelling assigned by their compiler.
|
|
221
|
+
if (typeof index === 'string' && index.startsWith('src-'))
|
|
222
|
+
return `--z-${Identity.label(path)}-${Identity.compact(JSON.stringify([index, path]))}`
|
|
175
223
|
return `--z-t${encode(String(index))}-${encode(path)}`
|
|
176
224
|
}
|