zyzz 0.0.16 → 0.0.17
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/Style.d.ts +4 -3
- package/dist/Style.d.ts.map +1 -1
- package/dist/internal/Cascade.d.ts +29 -0
- package/dist/internal/Cascade.d.ts.map +1 -1
- package/dist/internal/Cascade.js +45 -0
- package/dist/internal/Cascade.js.map +1 -1
- package/dist/internal/Compound.d.ts +12 -0
- package/dist/internal/Compound.d.ts.map +1 -1
- package/dist/internal/Literal.d.ts +1063 -27
- package/dist/internal/Literal.d.ts.map +1 -1
- package/dist/internal/Literal.js +739 -3
- package/dist/internal/Literal.js.map +1 -1
- package/dist/next/loader.js +43 -17
- package/dist/next/loader.js.map +1 -1
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +19 -7
- package/dist/vite/index.js.map +1 -1
- package/dist/web/Css.d.ts.map +1 -1
- package/dist/web/Css.js +2 -1
- package/dist/web/Css.js.map +1 -1
- package/dist/web/internal/Themes.d.ts +1 -8
- package/dist/web/internal/Themes.d.ts.map +1 -1
- package/dist/web/internal/Themes.js +55 -16
- package/dist/web/internal/Themes.js.map +1 -1
- package/package.json +1 -1
- package/src/Style.ts +23 -14
- package/src/internal/Cascade.ts +45 -0
- package/src/internal/Compound.ts +56 -0
- package/src/internal/Literal.ts +746 -3
- package/src/next/loader.ts +49 -17
- package/src/vite/index.ts +24 -5
- package/src/web/Css.ts +2 -1
- package/src/web/internal/Themes.ts +70 -17
package/src/next/loader.ts
CHANGED
|
@@ -238,8 +238,7 @@ async function compile(context: Context, source: string) {
|
|
|
238
238
|
const hash = Crypto.createHash('sha256')
|
|
239
239
|
.update(
|
|
240
240
|
options.development
|
|
241
|
-
? (stylesheet.id ??
|
|
242
|
-
JSON.stringify([context.resourcePath, index, graph.dependencies]))
|
|
241
|
+
? (stylesheet.id ?? JSON.stringify([context.resourcePath, index]))
|
|
243
242
|
: css,
|
|
244
243
|
)
|
|
245
244
|
.digest('hex')
|
|
@@ -265,33 +264,66 @@ async function compile(context: Context, source: string) {
|
|
|
265
264
|
}
|
|
266
265
|
let request = file
|
|
267
266
|
if (options.development) {
|
|
268
|
-
|
|
269
|
-
request = `${file}.js`
|
|
270
|
-
const stylesheet =
|
|
267
|
+
request =
|
|
271
268
|
options.bundler === 'turbopack'
|
|
272
|
-
?
|
|
273
|
-
:
|
|
274
|
-
const code = `'use client';import ${JSON.stringify(stylesheet)};`
|
|
275
|
-
const existing = await Fs.readFile(request, 'utf8').catch(
|
|
276
|
-
(error: NodeJS.ErrnoException) => {
|
|
277
|
-
if (error.code !== 'ENOENT') throw error
|
|
278
|
-
return undefined
|
|
279
|
-
},
|
|
280
|
-
)
|
|
281
|
-
if (existing !== code) await Fs.writeFile(request, code)
|
|
269
|
+
? Path.join(directory, 'style.css') + `?zyzz=${hash}`
|
|
270
|
+
: file
|
|
282
271
|
} else if (options.bundler === 'turbopack') {
|
|
283
272
|
// Persist CSS with the cached transform, independently of generated filesystem artifacts.
|
|
284
273
|
request =
|
|
285
274
|
Path.join(directory, 'style.css') +
|
|
286
275
|
`?zyzz=${hash}&css=${Buffer.from(css).toString('base64url')}`
|
|
287
276
|
}
|
|
288
|
-
const relative = Path.relative(
|
|
277
|
+
const relative = Path.relative(
|
|
278
|
+
options.development ? directory : Path.dirname(context.resourcePath),
|
|
279
|
+
request,
|
|
280
|
+
)
|
|
289
281
|
.split(Path.sep)
|
|
290
282
|
.join('/')
|
|
291
283
|
requests.push(
|
|
292
|
-
|
|
284
|
+
`${options.development ? '@import' : 'import'} ${JSON.stringify(relative.startsWith('../') ? relative : `./${relative}`)};`,
|
|
293
285
|
)
|
|
294
286
|
}
|
|
295
287
|
|
|
288
|
+
if (options.development && requests.length) {
|
|
289
|
+
// Keep the client boundary stable when a dependency changes the stylesheet imports.
|
|
290
|
+
const hash = Crypto.createHash('sha256')
|
|
291
|
+
.update(context.resourcePath)
|
|
292
|
+
.digest('hex')
|
|
293
|
+
const stylesheet = Path.join(directory, `${hash}.css`)
|
|
294
|
+
const css = requests.join('\n')
|
|
295
|
+
const previous = await Fs.readFile(stylesheet, 'utf8').catch(
|
|
296
|
+
(error: NodeJS.ErrnoException) => {
|
|
297
|
+
if (error.code !== 'ENOENT') throw error
|
|
298
|
+
return undefined
|
|
299
|
+
},
|
|
300
|
+
)
|
|
301
|
+
if (previous !== css) {
|
|
302
|
+
const temporary = `${stylesheet}.${Crypto.randomUUID()}.tmp`
|
|
303
|
+
await Fs.writeFile(temporary, css)
|
|
304
|
+
await Fs.rename(temporary, stylesheet)
|
|
305
|
+
}
|
|
306
|
+
const file = Path.join(directory, `${hash}.js`)
|
|
307
|
+
const request =
|
|
308
|
+
options.bundler === 'turbopack'
|
|
309
|
+
? `./style.css?zyzz=${hash}`
|
|
310
|
+
: `./${hash}.css`
|
|
311
|
+
try {
|
|
312
|
+
await Fs.writeFile(
|
|
313
|
+
file,
|
|
314
|
+
`'use client';import ${JSON.stringify(request)};`,
|
|
315
|
+
{ flag: 'wx' },
|
|
316
|
+
)
|
|
317
|
+
} catch (error) {
|
|
318
|
+
if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error
|
|
319
|
+
}
|
|
320
|
+
const relative = Path.relative(Path.dirname(context.resourcePath), file)
|
|
321
|
+
.split(Path.sep)
|
|
322
|
+
.join('/')
|
|
323
|
+
return {
|
|
324
|
+
code: `${output.code}\nimport ${JSON.stringify(relative.startsWith('../') ? relative : `./${relative}`)};`,
|
|
325
|
+
map,
|
|
326
|
+
}
|
|
327
|
+
}
|
|
296
328
|
return { code: `${output.code}\n${requests.join('\n')}`, map }
|
|
297
329
|
}
|
package/src/vite/index.ts
CHANGED
|
@@ -724,6 +724,8 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
724
724
|
modules: result.modules,
|
|
725
725
|
code: output.code,
|
|
726
726
|
contractIds: Object.keys(result.contracts),
|
|
727
|
+
configurations: new Map<string, Catalogs.Catalog>(),
|
|
728
|
+
connections: staged,
|
|
727
729
|
css: '',
|
|
728
730
|
sharedCss: '',
|
|
729
731
|
cssMap: map,
|
|
@@ -758,9 +760,13 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
758
760
|
for (const key of catalogs.keys())
|
|
759
761
|
if (key.startsWith(`${id}\0`)) catalogs.delete(key)
|
|
760
762
|
|
|
763
|
+
const configurations = new Map<string, Catalogs.Catalog>()
|
|
761
764
|
for (const [id, contract] of Object.entries(compiled))
|
|
762
|
-
for (const configuration of Catalogs.read(contract))
|
|
763
|
-
|
|
765
|
+
for (const configuration of Catalogs.read(contract)) {
|
|
766
|
+
const key = `${id}\0${configuration.identity}`
|
|
767
|
+
catalogs.set(key, configuration)
|
|
768
|
+
configurations.set(key, configuration)
|
|
769
|
+
}
|
|
764
770
|
|
|
765
771
|
const map = new Mapping.GenMapping()
|
|
766
772
|
const styles: string[] = []
|
|
@@ -967,6 +973,8 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
967
973
|
modules: result.modules,
|
|
968
974
|
code: output.code,
|
|
969
975
|
contractIds: Object.keys(compiled),
|
|
976
|
+
configurations,
|
|
977
|
+
connections: staged,
|
|
970
978
|
css: styles.join('\n'),
|
|
971
979
|
sharedCss: new TextDecoder().decode(shared.code),
|
|
972
980
|
sharedCssMap: sharedMap,
|
|
@@ -996,7 +1004,7 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
996
1004
|
// The seed only anchors the compile, which visits every discovered source.
|
|
997
1005
|
if (!entry || !sources.has(entry.file)) {
|
|
998
1006
|
const file = sources.keys().next().value
|
|
999
|
-
if (file === undefined) return
|
|
1007
|
+
if (file === undefined) return { catalogs: new Map(), edges: new Map() }
|
|
1000
1008
|
|
|
1001
1009
|
entry = {
|
|
1002
1010
|
compiler: compiler(environment),
|
|
@@ -1019,7 +1027,16 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
1019
1027
|
if (!output.contractIds.some((id) => key.startsWith(`${id}\0`)))
|
|
1020
1028
|
catalogs.delete(key)
|
|
1021
1029
|
|
|
1022
|
-
|
|
1030
|
+
// Concurrent module transforms must not replace this request's compiled catalogs.
|
|
1031
|
+
return {
|
|
1032
|
+
catalogs: output?.configurations ?? new Map(catalogs),
|
|
1033
|
+
edges: output?.connections ?? new Map(edges),
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
type DocumentState = {
|
|
1038
|
+
catalogs: ReadonlyMap<string, Catalogs.Catalog>
|
|
1039
|
+
edges: ReadonlyMap<string, ReadonlySet<string>>
|
|
1023
1040
|
}
|
|
1024
1041
|
|
|
1025
1042
|
/** Catalogs reachable from a served document's module scripts; every catalog when none resolve. */
|
|
@@ -1027,6 +1044,7 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
1027
1044
|
html: string,
|
|
1028
1045
|
filename: string,
|
|
1029
1046
|
resolve: Host['resolve'],
|
|
1047
|
+
{ catalogs, edges }: DocumentState,
|
|
1030
1048
|
) {
|
|
1031
1049
|
const reachable = new Set<string>()
|
|
1032
1050
|
|
|
@@ -1560,7 +1578,7 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
1560
1578
|
if (!context.server)
|
|
1561
1579
|
return entryCatalogs(context.bundle, context.chunk)
|
|
1562
1580
|
|
|
1563
|
-
await initializations(context.server)
|
|
1581
|
+
const state = await initializations(context.server)
|
|
1564
1582
|
|
|
1565
1583
|
const environment = context.server.environments.client
|
|
1566
1584
|
|
|
@@ -1569,6 +1587,7 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
1569
1587
|
context.filename,
|
|
1570
1588
|
(source, importer) =>
|
|
1571
1589
|
environment.pluginContainer.resolveId(source, importer),
|
|
1590
|
+
state,
|
|
1572
1591
|
)
|
|
1573
1592
|
})()
|
|
1574
1593
|
// Saved preferences apply before any other script or visible content.
|
package/src/web/Css.ts
CHANGED
|
@@ -94,7 +94,7 @@ export function compile<
|
|
|
94
94
|
// Logical dimensions may alias either physical axis in inherited writing modes.
|
|
95
95
|
// Preserve physical-only factoring when no logical dimension is authored.
|
|
96
96
|
const logicalSizing = [...properties].some((property) =>
|
|
97
|
-
/^(min|max)?(blockSize|inlineSize)$/i.test(property),
|
|
97
|
+
/^(min|max)?(blockSize|inlineSize)$/i.test(canonical(property)),
|
|
98
98
|
)
|
|
99
99
|
const resets = properties.has('all')
|
|
100
100
|
const combinedLines = new Set<string>()
|
|
@@ -165,6 +165,7 @@ export function compile<
|
|
|
165
165
|
[
|
|
166
166
|
'whiteSpace',
|
|
167
167
|
'whiteSpaceCollapse',
|
|
168
|
+
'whiteSpaceTrim',
|
|
168
169
|
'textWrap',
|
|
169
170
|
'textWrapMode',
|
|
170
171
|
'textWrapStyle',
|
|
@@ -18,6 +18,7 @@ export function create() {
|
|
|
18
18
|
>()
|
|
19
19
|
|
|
20
20
|
const defaults = new Map<string, readonly Rule[]>()
|
|
21
|
+
const defaultGroups = new Map<Token.Contract, Set<string>>()
|
|
21
22
|
|
|
22
23
|
function serialize(token: Token.Reference): string {
|
|
23
24
|
const value = token.value
|
|
@@ -42,7 +43,7 @@ export function create() {
|
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
const label = contract.identity?.startsWith('src-') ? token.path : undefined
|
|
45
|
-
return `var(${name},${literal(value, label)})`
|
|
46
|
+
return `var(${name},${literal(value, label, token.contract)})`
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
function emit(
|
|
@@ -51,6 +52,7 @@ export function create() {
|
|
|
51
52
|
separate?: 'all' | 'defaults',
|
|
52
53
|
) {
|
|
53
54
|
const classes: Record<string, string> = Object.create(null)
|
|
55
|
+
const completeDefaults = new Set<Token.Contract>()
|
|
54
56
|
const empty: string[] = []
|
|
55
57
|
const rules: Rule[] = []
|
|
56
58
|
|
|
@@ -97,6 +99,16 @@ export function create() {
|
|
|
97
99
|
}
|
|
98
100
|
|
|
99
101
|
if (!contract) continue
|
|
102
|
+
// Independently compiled consumers must import identical fallback contents.
|
|
103
|
+
if (separate === 'all' && contract.identity) {
|
|
104
|
+
completeDefaults.add(data.contract)
|
|
105
|
+
for (const [path, value] of Object.entries(data.values))
|
|
106
|
+
literal(
|
|
107
|
+
value,
|
|
108
|
+
contract.identity.startsWith('src-') ? path : undefined,
|
|
109
|
+
data.contract,
|
|
110
|
+
)
|
|
111
|
+
}
|
|
100
112
|
if (!contract.paths.size) empty.push(`.${className}{}`)
|
|
101
113
|
|
|
102
114
|
const declarations: Rule[] = []
|
|
@@ -112,7 +124,7 @@ export function create() {
|
|
|
112
124
|
conditions: [],
|
|
113
125
|
property: name,
|
|
114
126
|
selector: `.${className}`,
|
|
115
|
-
value: literal(value, label),
|
|
127
|
+
value: literal(value, label, data.contract),
|
|
116
128
|
})
|
|
117
129
|
}
|
|
118
130
|
|
|
@@ -124,6 +136,8 @@ export function create() {
|
|
|
124
136
|
`.${className}`,
|
|
125
137
|
rules,
|
|
126
138
|
contract.identity?.startsWith('src-') ? path : undefined,
|
|
139
|
+
[],
|
|
140
|
+
data.contract,
|
|
127
141
|
)
|
|
128
142
|
}
|
|
129
143
|
|
|
@@ -134,6 +148,31 @@ export function create() {
|
|
|
134
148
|
scopes.set(rule.property, entries)
|
|
135
149
|
}
|
|
136
150
|
|
|
151
|
+
const grouped = new Set<string>()
|
|
152
|
+
const resources: Resource[] = []
|
|
153
|
+
if (separate === 'all')
|
|
154
|
+
for (const [contract, names] of defaultGroups) {
|
|
155
|
+
if (!completeDefaults.has(contract)) continue
|
|
156
|
+
|
|
157
|
+
// Sort independent properties only. Each property retains its authored conditional order.
|
|
158
|
+
const rules = [...names].sort().flatMap((name) => defaults.get(name)!)
|
|
159
|
+
resources.push({
|
|
160
|
+
css: render(rules),
|
|
161
|
+
id: JSON.stringify([
|
|
162
|
+
contract[Token.identity],
|
|
163
|
+
'fallbacks',
|
|
164
|
+
Object.entries(themes)
|
|
165
|
+
.filter(
|
|
166
|
+
([, theme]) => theme[Token.definition].contract === contract,
|
|
167
|
+
)
|
|
168
|
+
.map(([name]) => name)
|
|
169
|
+
.sort(),
|
|
170
|
+
]),
|
|
171
|
+
rules,
|
|
172
|
+
})
|
|
173
|
+
for (const name of names) grouped.add(name)
|
|
174
|
+
}
|
|
175
|
+
|
|
137
176
|
return {
|
|
138
177
|
classes: Object.freeze(classes),
|
|
139
178
|
css:
|
|
@@ -151,11 +190,14 @@ export function create() {
|
|
|
151
190
|
.join('\n'),
|
|
152
191
|
resources: separate
|
|
153
192
|
? [
|
|
154
|
-
...
|
|
155
|
-
|
|
156
|
-
id
|
|
157
|
-
rules
|
|
158
|
-
|
|
193
|
+
...resources,
|
|
194
|
+
...[...defaults]
|
|
195
|
+
.filter(([id]) => !grouped.has(id))
|
|
196
|
+
.map(([id, rules]) => ({
|
|
197
|
+
css: render(rules),
|
|
198
|
+
id,
|
|
199
|
+
rules,
|
|
200
|
+
})),
|
|
159
201
|
// Keep every scope of a property together so overlapping themes retain precedence.
|
|
160
202
|
...(separate === 'all'
|
|
161
203
|
? [...scopes].map(([property, rules]) => ({
|
|
@@ -172,25 +214,34 @@ export function create() {
|
|
|
172
214
|
}
|
|
173
215
|
}
|
|
174
216
|
|
|
175
|
-
function literal(
|
|
217
|
+
function literal(
|
|
218
|
+
value: Token.Value,
|
|
219
|
+
label?: string,
|
|
220
|
+
owner?: Token.Contract,
|
|
221
|
+
): string {
|
|
176
222
|
if (Token.is(value)) return serialize(value)
|
|
177
223
|
if (typeof value !== 'object') return String(value)
|
|
178
224
|
if ('default' in value) {
|
|
179
225
|
// Separate fallback properties preserve extensions and resolve references within each scope.
|
|
180
|
-
const base = literal(value.default, label)
|
|
226
|
+
const base = literal(value.default, label, owner)
|
|
181
227
|
// Rule consolidation must not change existing variable identities.
|
|
182
|
-
const css = `:where(*){--fallback:${base};}${conditionalCss(value, '--fallback', ':where(*)', label)}`
|
|
228
|
+
const css = `:where(*){--fallback:${base};}${conditionalCss(value, '--fallback', ':where(*)', label, owner)}`
|
|
183
229
|
const name = label
|
|
184
230
|
? `--z-${Identity.label(label)}-fallback-${Identity.compact(css)}`
|
|
185
231
|
: `--z-f${Identity.hash(css)}`
|
|
186
232
|
const emitted: Rule[] = [
|
|
187
233
|
{ conditions: [], property: name, selector: ':where(*)', value: base },
|
|
188
234
|
]
|
|
189
|
-
conditional(value, name, ':where(*)', emitted, label)
|
|
235
|
+
conditional(value, name, ':where(*)', emitted, label, [], owner)
|
|
190
236
|
defaults.set(name, emitted)
|
|
237
|
+
if (owner) {
|
|
238
|
+
const names = defaultGroups.get(owner) ?? new Set<string>()
|
|
239
|
+
names.add(name)
|
|
240
|
+
defaultGroups.set(owner, names)
|
|
241
|
+
}
|
|
191
242
|
return `var(${name})`
|
|
192
243
|
}
|
|
193
|
-
return `light-dark(${literal(value.light, label)},${literal(value.dark, label)})`
|
|
244
|
+
return `light-dark(${literal(value.light, label, owner)},${literal(value.dark, label, owner)})`
|
|
194
245
|
}
|
|
195
246
|
|
|
196
247
|
function conditionalCss(
|
|
@@ -198,6 +249,7 @@ export function create() {
|
|
|
198
249
|
property: string,
|
|
199
250
|
selector: string,
|
|
200
251
|
label?: string,
|
|
252
|
+
owner?: Token.Contract,
|
|
201
253
|
): string {
|
|
202
254
|
if (
|
|
203
255
|
!value ||
|
|
@@ -207,12 +259,12 @@ export function create() {
|
|
|
207
259
|
)
|
|
208
260
|
return ''
|
|
209
261
|
return (
|
|
210
|
-
conditionalCss(value.default, property, selector, label) +
|
|
262
|
+
conditionalCss(value.default, property, selector, label, owner) +
|
|
211
263
|
Object.entries(value)
|
|
212
264
|
.filter(([query]) => query !== 'default')
|
|
213
265
|
.map(
|
|
214
266
|
([query, entry]) =>
|
|
215
|
-
`${query}{${selector}{${property}:${literal(entry, label)};}${conditionalCss(entry, property, selector, label)}}`,
|
|
267
|
+
`${query}{${selector}{${property}:${literal(entry, label, owner)};}${conditionalCss(entry, property, selector, label, owner)}}`,
|
|
216
268
|
)
|
|
217
269
|
.join('')
|
|
218
270
|
)
|
|
@@ -225,6 +277,7 @@ export function create() {
|
|
|
225
277
|
rules: Rule[],
|
|
226
278
|
label?: string,
|
|
227
279
|
conditions: readonly string[] = [],
|
|
280
|
+
owner?: Token.Contract,
|
|
228
281
|
) {
|
|
229
282
|
if (
|
|
230
283
|
!value ||
|
|
@@ -233,7 +286,7 @@ export function create() {
|
|
|
233
286
|
!('default' in value)
|
|
234
287
|
)
|
|
235
288
|
return
|
|
236
|
-
conditional(value.default, name, selector, rules, label, conditions)
|
|
289
|
+
conditional(value.default, name, selector, rules, label, conditions, owner)
|
|
237
290
|
for (const [query, entry] of Object.entries(value)) {
|
|
238
291
|
if (query === 'default') continue
|
|
239
292
|
const nested = [...conditions, query]
|
|
@@ -241,9 +294,9 @@ export function create() {
|
|
|
241
294
|
conditions: nested,
|
|
242
295
|
property: name,
|
|
243
296
|
selector,
|
|
244
|
-
value: literal(entry, label),
|
|
297
|
+
value: literal(entry, label, owner),
|
|
245
298
|
})
|
|
246
|
-
conditional(entry, name, selector, rules, label, nested)
|
|
299
|
+
conditional(entry, name, selector, rules, label, nested, owner)
|
|
247
300
|
}
|
|
248
301
|
}
|
|
249
302
|
|