zyzz 0.0.23 → 0.0.25
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/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.map +1 -1
- package/dist/compiler/internal/Configurations.js +4 -0
- 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 +20 -8
- 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 +10 -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/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 +64 -27
- 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/Device.js +90 -37
- package/dist/react-native/internal/Device.js.map +3 -3
- package/dist/react-native/internal/NativeZyzz.js.map +2 -2
- package/dist/react-native/internal/Store.d.ts +4 -0
- package/dist/react-native/internal/Store.d.ts.map +1 -1
- package/dist/react-native/internal/Store.js.map +1 -1
- package/dist/react-native/internal/Viewport.d.ts +6 -0
- package/dist/react-native/internal/Viewport.d.ts.map +1 -1
- package/dist/react-native/internal/Viewport.js +2 -0
- package/dist/react-native/internal/Viewport.js.map +1 -1
- package/dist/react-native/react.d.ts +5 -2
- package/dist/react-native/react.d.ts.map +1 -1
- package/dist/react-native/react.js +60 -8
- package/dist/react-native/react.js.map +1 -1
- package/dist/runtime/NativeContext.d.ts +6 -0
- package/dist/runtime/NativeContext.d.ts.map +1 -1
- package/dist/runtime/NativeContext.js +62 -9
- package/dist/runtime/NativeContext.js.map +1 -1
- package/dist/unplugin/index.d.ts.map +1 -1
- package/dist/unplugin/index.js +35 -14
- package/dist/unplugin/index.js.map +1 -1
- package/dist/variants.d.ts +3 -2
- package/dist/variants.d.ts.map +1 -1
- package/dist/variants.js.map +1 -1
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +30 -37
- 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/native/NativeZyzz.cpp +52 -65
- package/native/NativeZyzz.h +0 -4
- package/native/ZyzzSpecJSI.h +0 -10
- package/native/android/ZyzzSpec-generated.cpp +0 -6
- package/package.json +1 -1
- package/src/Style.ts +2 -0
- 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 +4 -0
- package/src/compiler/internal/Contract.ts +59 -7
- package/src/compiler/internal/PackedStyles.ts +9 -0
- package/src/compiler/internal/Themes.ts +25 -7
- package/src/internal/Configuration.ts +23 -1
- package/src/internal/Token.ts +2 -0
- package/src/next/internal/Project.ts +28 -23
- package/src/next/loader.ts +2 -1
- package/src/node/Host.ts +90 -36
- package/src/node/internal/Contracts.ts +384 -0
- package/src/react-native/internal/Device.ts +129 -48
- package/src/react-native/internal/NativeZyzz.ts +4 -6
- package/src/react-native/internal/Store.ts +4 -0
- package/src/react-native/internal/Viewport.ts +7 -0
- package/src/react-native/react.ts +81 -11
- package/src/runtime/NativeContext.ts +83 -9
- package/src/unplugin/index.ts +39 -14
- package/src/variants.ts +3 -2
- package/src/vite/index.ts +35 -36
- package/src/web/Css.ts +228 -42
- package/src/web/internal/ClassName.ts +114 -0
|
@@ -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
|
: {}),
|
|
@@ -494,13 +494,31 @@ export function collect(program: Ast.Program, options: collect.Options) {
|
|
|
494
494
|
root = root.object
|
|
495
495
|
}
|
|
496
496
|
if (root.type !== 'Identifier') return undefined
|
|
497
|
-
const
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
if (
|
|
503
|
-
|
|
497
|
+
const name = root.name
|
|
498
|
+
let value: unknown = (() => {
|
|
499
|
+
if (derived?.name === name) return derived.vars
|
|
500
|
+
|
|
501
|
+
const call = names.get(name)
|
|
502
|
+
if (call) {
|
|
503
|
+
if (
|
|
504
|
+
!call.variableSet &&
|
|
505
|
+
!call.directVariables &&
|
|
506
|
+
!['vars', 'tokens'].includes(path.shift()!)
|
|
507
|
+
)
|
|
508
|
+
return undefined
|
|
509
|
+
return themes[call.name]?.tokens
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// Config-bound vars read the configured variables, so they follow set scoping.
|
|
513
|
+
const config = configs.get(name)
|
|
514
|
+
if (
|
|
515
|
+
!config?.call.variableConfig ||
|
|
516
|
+
(!config.call.selection && path.shift() !== 'vars')
|
|
517
|
+
)
|
|
518
|
+
return undefined
|
|
519
|
+
return themes[config.call.name]?.tokens
|
|
520
|
+
})()
|
|
521
|
+
if (value === undefined) return undefined
|
|
504
522
|
for (const key of path)
|
|
505
523
|
value =
|
|
506
524
|
value && typeof value === 'object'
|
|
@@ -63,7 +63,8 @@ export function create(
|
|
|
63
63
|
? (value as Vars.Definition)
|
|
64
64
|
: Vars.define(value as Vars.Values)
|
|
65
65
|
const theme = VariableSets.theme(definition, variableMappings)
|
|
66
|
-
|
|
66
|
+
// Tokens are ambiguous only through an explicit mapping, so skip the property scan without one.
|
|
67
|
+
if (!variableMappings) return theme
|
|
67
68
|
const metadata = theme[Token.definition]
|
|
68
69
|
const names = new Map<string, string>()
|
|
69
70
|
for (const path of Object.keys(metadata.values)) {
|
|
@@ -149,6 +150,7 @@ export function create(
|
|
|
149
150
|
for (const key of Object.keys(input))
|
|
150
151
|
if (
|
|
151
152
|
![
|
|
153
|
+
'composition',
|
|
152
154
|
'cssOutput',
|
|
153
155
|
'id',
|
|
154
156
|
'defaultLayer',
|
|
@@ -189,6 +191,13 @@ export function create(
|
|
|
189
191
|
)
|
|
190
192
|
throw new InvalidError('cssOutput must be atomic or grouped.')
|
|
191
193
|
|
|
194
|
+
if (
|
|
195
|
+
input.composition !== undefined &&
|
|
196
|
+
input.composition !== 'independent' &&
|
|
197
|
+
input.composition !== 'ordered'
|
|
198
|
+
)
|
|
199
|
+
throw new InvalidError('composition must be independent or ordered.')
|
|
200
|
+
|
|
192
201
|
if (input.theme !== undefined && input.themes !== undefined)
|
|
193
202
|
throw new InvalidError('Use either theme or themes, not both.')
|
|
194
203
|
|
|
@@ -252,6 +261,9 @@ export function create(
|
|
|
252
261
|
|
|
253
262
|
const contract = Object.freeze({
|
|
254
263
|
...(variableMode ? { variableSet: true, mappings: variableMappings } : {}),
|
|
264
|
+
...(input.composition !== undefined
|
|
265
|
+
? { composition: input.composition as 'independent' | 'ordered' }
|
|
266
|
+
: {}),
|
|
255
267
|
...(input.defaultLayer !== undefined
|
|
256
268
|
? { defaultLayer: input.defaultLayer as string }
|
|
257
269
|
: {}),
|
|
@@ -451,6 +463,11 @@ export function create(
|
|
|
451
463
|
export declare namespace create {
|
|
452
464
|
/** Optional layer names and mutually exclusive theme modes. */
|
|
453
465
|
type Options = {
|
|
466
|
+
/**
|
|
467
|
+
* Whether bound styles' class lists may combine. Independent atomic styles
|
|
468
|
+
* share rules for repeated declarations, so combine them only with `cx`.
|
|
469
|
+
*/
|
|
470
|
+
readonly composition?: 'independent' | 'ordered' | undefined
|
|
454
471
|
/** CSS representation inherited by bound helpers; atomic by default. */
|
|
455
472
|
readonly cssOutput?: 'atomic' | 'grouped' | undefined
|
|
456
473
|
/** Fallback CSS layer for bound styles and variants; unlayered when omitted. */
|
|
@@ -836,6 +853,11 @@ type Validated<options> = Record<
|
|
|
836
853
|
|
|
837
854
|
/** Vars, optional named alternatives, and category-to-property mappings. */
|
|
838
855
|
export type VariableOptions = {
|
|
856
|
+
/**
|
|
857
|
+
* Whether bound styles' class lists may combine. Independent atomic styles
|
|
858
|
+
* share rules for repeated declarations, so combine them only with `cx`.
|
|
859
|
+
*/
|
|
860
|
+
readonly composition?: 'independent' | 'ordered' | undefined
|
|
839
861
|
/** CSS representation inherited by bound helpers; atomic by default. */
|
|
840
862
|
readonly cssOutput?: 'atomic' | 'grouped' | undefined
|
|
841
863
|
/** Fallback CSS layer for bound styles and variants; unlayered when omitted. */
|
package/src/internal/Token.ts
CHANGED
|
@@ -393,6 +393,8 @@ export type Contract = {
|
|
|
393
393
|
readonly variableSet?: boolean | undefined
|
|
394
394
|
/** Configuration-local category-to-property mappings. */
|
|
395
395
|
readonly mappings?: VariableSets.Mappings | false | undefined
|
|
396
|
+
/** Whether bound styles' class lists combine, retained by configuration-bound handles. */
|
|
397
|
+
readonly composition?: 'independent' | 'ordered' | undefined
|
|
396
398
|
/** Web emission mode retained by configuration-bound theme handles. */
|
|
397
399
|
readonly cssOutput?: 'atomic' | 'grouped' | undefined
|
|
398
400
|
/** Fallback layer for declarations without an explicit layer. */
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/** Retains filesystem snapshots and an incremental graph within a Next.js loader worker. @module */
|
|
2
2
|
import * as Contract from '../../compiler/internal/Contract.js'
|
|
3
|
+
import * as Contracts from '../../node/internal/Contracts.js'
|
|
3
4
|
import * as Fs from 'node:fs/promises'
|
|
4
5
|
import * as Graph from '../../compiler/Graph.js'
|
|
5
6
|
import * as Path from 'node:path'
|
|
@@ -26,6 +27,7 @@ export type Context = {
|
|
|
26
27
|
/** Creates project state owned by a loader configuration in one worker process. */
|
|
27
28
|
export function create(options: create.Options) {
|
|
28
29
|
let compiler = Graph.create()
|
|
30
|
+
const packages = Contracts.create()
|
|
29
31
|
const files = new Map<string, { source: string; version: string }>()
|
|
30
32
|
const libraries = new Map<
|
|
31
33
|
string,
|
|
@@ -122,6 +124,20 @@ export function create(options: create.Options) {
|
|
|
122
124
|
}
|
|
123
125
|
return pending
|
|
124
126
|
}
|
|
127
|
+
// Files behind package contracts stay dependencies even when the module is unchanged.
|
|
128
|
+
const inputs = new Set<string>()
|
|
129
|
+
const host: Contracts.read.Options = {
|
|
130
|
+
read: async (file) => {
|
|
131
|
+
const source = await read(file)
|
|
132
|
+
// Next ignores node_modules edits, and tracked manifests stalled webpack rebuilds after a failed compile.
|
|
133
|
+
if (Path.basename(file) === 'package.json') return source
|
|
134
|
+
track(file)
|
|
135
|
+
inputs.add(file)
|
|
136
|
+
return source
|
|
137
|
+
},
|
|
138
|
+
resolve: async (specifier, importer) =>
|
|
139
|
+
(await resolve(Path.dirname(importer), specifier)) || undefined,
|
|
140
|
+
}
|
|
125
141
|
const id = (file: string) =>
|
|
126
142
|
`app/${Path.relative(root, file).split(Path.sep).join('/')}`
|
|
127
143
|
const eligible = (file: string) =>
|
|
@@ -322,14 +338,10 @@ export function create(options: create.Options) {
|
|
|
322
338
|
await visit(resolved)
|
|
323
339
|
continue
|
|
324
340
|
}
|
|
325
|
-
const
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
links[specifier] = resolved
|
|
330
|
-
} catch (error) {
|
|
331
|
-
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error
|
|
332
|
-
}
|
|
341
|
+
const contract = await packages.read(resolved, host)
|
|
342
|
+
if (contract === undefined) continue
|
|
343
|
+
contracts[resolved] = contract
|
|
344
|
+
links[specifier] = resolved
|
|
333
345
|
}
|
|
334
346
|
}
|
|
335
347
|
|
|
@@ -360,11 +372,10 @@ export function create(options: create.Options) {
|
|
|
360
372
|
|
|
361
373
|
const links = (imports[owner] ??= Object.create(null))
|
|
362
374
|
links[specifier] = target
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
}
|
|
375
|
+
contracts[target] ??= (await packages.read(target, {
|
|
376
|
+
...host,
|
|
377
|
+
required: true,
|
|
378
|
+
}))!
|
|
368
379
|
await dependencies(target)
|
|
369
380
|
owner = target
|
|
370
381
|
}
|
|
@@ -388,16 +399,10 @@ export function create(options: create.Options) {
|
|
|
388
399
|
if (output.code === source && !output.css) {
|
|
389
400
|
context.addDependency(context.resourcePath)
|
|
390
401
|
for (const file of runtime) context.addDependency(file)
|
|
391
|
-
for (const file of
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
context.addDependency(
|
|
396
|
-
target.startsWith('app/')
|
|
397
|
-
? Path.join(root, target.slice(4))
|
|
398
|
-
: `${target}.zyzz.json`,
|
|
399
|
-
)
|
|
400
|
-
}
|
|
402
|
+
for (const file of inputs) context.addDependency(file)
|
|
403
|
+
for (const target of Object.values(imports[entry] ?? {}))
|
|
404
|
+
if (target?.startsWith('app/'))
|
|
405
|
+
context.addDependency(Path.join(root, target.slice(4)))
|
|
401
406
|
} else {
|
|
402
407
|
for (const file of tracked) context.addDependency(file)
|
|
403
408
|
}
|
package/src/next/loader.ts
CHANGED
|
@@ -161,7 +161,8 @@ async function compile(context: Context, source: string) {
|
|
|
161
161
|
placeholder,
|
|
162
162
|
Path.relative(directory, file)
|
|
163
163
|
.split(Path.sep)
|
|
164
|
-
|
|
164
|
+
// Bundlers resolve url() paths without decoding, so escape only characters that change URL parsing.
|
|
165
|
+
.map((part) => part.replace(/[%#?]/g, encodeURIComponent))
|
|
165
166
|
.join('/') + target.slice(raw.length),
|
|
166
167
|
)
|
|
167
168
|
}
|
package/src/node/Host.ts
CHANGED
|
@@ -12,7 +12,10 @@ import { ResolverFactory } from 'oxc-resolver'
|
|
|
12
12
|
import * as Graph from '../compiler/Graph.js'
|
|
13
13
|
import * as AtRules from '../compiler/internal/AtRules.js'
|
|
14
14
|
import * as Catalogs from '../compiler/internal/Catalogs.js'
|
|
15
|
+
import * as ClassName from '../web/internal/ClassName.js'
|
|
16
|
+
import * as Contracts from './internal/Contracts.js'
|
|
15
17
|
import * as Relative from '../compiler/internal/Relative.js'
|
|
18
|
+
import * as Scheme from '../internal/Scheme.js'
|
|
16
19
|
import * as Snapshot from './internal/Snapshot.js'
|
|
17
20
|
import * as Syntax from '../compiler/internal/Syntax.js'
|
|
18
21
|
import * as Source from '../compiler/Source.js'
|
|
@@ -134,6 +137,7 @@ export async function create(options: create.Options): Promise<Runtime> {
|
|
|
134
137
|
const manifestPath = Path.join(outDir, '.zyzz.json')
|
|
135
138
|
|
|
136
139
|
const compiler = Graph.create()
|
|
140
|
+
const packages = Contracts.create()
|
|
137
141
|
const snapshot = Snapshot.create()
|
|
138
142
|
const stylesheets = new WeakMap<Transform.compile.ReturnType, Stylesheet>()
|
|
139
143
|
let closed = false
|
|
@@ -315,18 +319,16 @@ export async function create(options: create.Options): Promise<Runtime> {
|
|
|
315
319
|
async function contract(file: string, required = false): Promise<boolean> {
|
|
316
320
|
if (Object.hasOwn(contracts, file)) return true
|
|
317
321
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
throw error
|
|
329
|
-
}
|
|
322
|
+
const source = await packages.read(file, {
|
|
323
|
+
read(path) {
|
|
324
|
+
observe(path)
|
|
325
|
+
return snapshot.read(path)
|
|
326
|
+
},
|
|
327
|
+
required: required || previousContracts.has(file),
|
|
328
|
+
resolve: async (specifier, importer) => resolve(specifier, importer),
|
|
329
|
+
})
|
|
330
|
+
if (source === undefined) return false
|
|
331
|
+
contracts[file] = source
|
|
330
332
|
|
|
331
333
|
const metadata = (() => {
|
|
332
334
|
try {
|
|
@@ -459,6 +461,7 @@ export async function create(options: create.Options): Promise<Runtime> {
|
|
|
459
461
|
}
|
|
460
462
|
|
|
461
463
|
const graph = compiler.compile({
|
|
464
|
+
[ClassName.complete]: true,
|
|
462
465
|
[ThemeRules.shared]:
|
|
463
466
|
options.modules === false && !native ? 'all' : undefined,
|
|
464
467
|
[Syntax.cache]: snapshot.programs(modules),
|
|
@@ -469,13 +472,7 @@ export async function create(options: create.Options): Promise<Runtime> {
|
|
|
469
472
|
imports,
|
|
470
473
|
})
|
|
471
474
|
|
|
472
|
-
snapshot.retain(
|
|
473
|
-
new Set([
|
|
474
|
-
...inputs,
|
|
475
|
-
...Object.keys(contracts).map((file) => `${file}.zyzz.json`),
|
|
476
|
-
]),
|
|
477
|
-
modules,
|
|
478
|
-
)
|
|
475
|
+
snapshot.retain(new Set([...inputs, ...observed]), modules)
|
|
479
476
|
|
|
480
477
|
const artifactNames = [
|
|
481
478
|
'zyzz.css',
|
|
@@ -620,7 +617,10 @@ export async function create(options: create.Options): Promise<Runtime> {
|
|
|
620
617
|
artifacts.set('zyzz.shared.css.map', shared.map)
|
|
621
618
|
}
|
|
622
619
|
|
|
623
|
-
const moduleStylesheets = new Map<
|
|
620
|
+
const moduleStylesheets = new Map<
|
|
621
|
+
string,
|
|
622
|
+
{ name: string; stylesheet: Stylesheet }
|
|
623
|
+
>()
|
|
624
624
|
|
|
625
625
|
for (const name of Object.keys(sources)) {
|
|
626
626
|
const output = graph.modules[`${options.packageId}/${name}`]!
|
|
@@ -661,23 +661,46 @@ export async function create(options: create.Options): Promise<Runtime> {
|
|
|
661
661
|
|
|
662
662
|
artifacts.set(`${name}.css`, stylesheet.code)
|
|
663
663
|
artifacts.set(`${name}.css.map`, stylesheet.map)
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
rebase(stylesheet, name, css === false ? false : css.minify),
|
|
669
|
-
)
|
|
664
|
+
moduleStylesheets.set(`${options.packageId}/${name}`, {
|
|
665
|
+
name,
|
|
666
|
+
stylesheet,
|
|
667
|
+
})
|
|
670
668
|
}
|
|
671
669
|
|
|
672
670
|
// The complete stylesheet follows the module graph so a consumer's rules
|
|
673
671
|
// cascade over the rules of the modules it imports.
|
|
674
672
|
if (shared || moduleStylesheets.size) {
|
|
675
|
-
const
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
673
|
+
const parts = shared ? [shared] : []
|
|
674
|
+
const units = new Set<string>()
|
|
675
|
+
let schemes = false
|
|
676
|
+
|
|
677
|
+
// Each module stylesheet declares the scheme classes and shared rules it
|
|
678
|
+
// uses to load alone. The complete stylesheet keeps the first declaration.
|
|
679
|
+
for (const id of order(graph.dependencies, [
|
|
680
|
+
...moduleStylesheets.keys(),
|
|
681
|
+
])) {
|
|
682
|
+
const { name, stylesheet } = moduleStylesheets.get(id)!
|
|
683
|
+
const declares = Object.values(Scheme.classes).some((className) =>
|
|
684
|
+
stylesheet.code.includes(`.${className}`),
|
|
685
|
+
)
|
|
686
|
+
const names = [
|
|
687
|
+
...(graph.modules[id]![ClassName.units]?.values() ?? []),
|
|
688
|
+
].map((decision) => decision.name)
|
|
689
|
+
|
|
690
|
+
parts.push(
|
|
691
|
+
rebase(
|
|
692
|
+
stylesheet,
|
|
693
|
+
name,
|
|
694
|
+
css === false ? false : css.minify,
|
|
695
|
+
schemes && declares,
|
|
696
|
+
new Set(names.filter((name) => units.has(name))),
|
|
697
|
+
),
|
|
698
|
+
)
|
|
699
|
+
schemes ||= declares
|
|
700
|
+
for (const name of names) units.add(name)
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
const complete = concatenate(parts)
|
|
681
704
|
|
|
682
705
|
artifacts.set('zyzz.css', complete.code)
|
|
683
706
|
artifacts.set('zyzz.css.map', complete.map)
|
|
@@ -1025,7 +1048,7 @@ function concatenate(parts: readonly { code: string; map: string }[]) {
|
|
|
1025
1048
|
let offset = 0
|
|
1026
1049
|
|
|
1027
1050
|
for (const part of parts) {
|
|
1028
|
-
if (!part.code) continue
|
|
1051
|
+
if (!part.code.trim()) continue
|
|
1029
1052
|
|
|
1030
1053
|
const code = part.code.endsWith('\n') ? part.code : `${part.code}\n`
|
|
1031
1054
|
const traced = Mapping.fromMap(part.map)
|
|
@@ -1169,15 +1192,27 @@ async function read(
|
|
|
1169
1192
|
}
|
|
1170
1193
|
}
|
|
1171
1194
|
|
|
1172
|
-
/**
|
|
1195
|
+
/**
|
|
1196
|
+
* Copies a module stylesheet into the complete stylesheet at the output root,
|
|
1197
|
+
* rebasing nested relative URLs and optionally dropping the scheme rules.
|
|
1198
|
+
*/
|
|
1173
1199
|
function rebase(
|
|
1174
1200
|
stylesheet: { code: string; map: string },
|
|
1175
1201
|
name: string,
|
|
1176
1202
|
minify: boolean,
|
|
1203
|
+
schemes: boolean,
|
|
1204
|
+
emitted: ReadonlySet<string>,
|
|
1177
1205
|
) {
|
|
1178
1206
|
const directory = Path.posix.dirname(name)
|
|
1179
1207
|
|
|
1180
|
-
if (
|
|
1208
|
+
if (
|
|
1209
|
+
!schemes &&
|
|
1210
|
+
!emitted.size &&
|
|
1211
|
+
(directory === '.' || !stylesheet.code.includes('url('))
|
|
1212
|
+
)
|
|
1213
|
+
return stylesheet
|
|
1214
|
+
|
|
1215
|
+
const classes = Object.values(Scheme.classes)
|
|
1181
1216
|
|
|
1182
1217
|
const result = AtRules.transform({
|
|
1183
1218
|
code: Buffer.from(stylesheet.code),
|
|
@@ -1186,8 +1221,27 @@ function rebase(
|
|
|
1186
1221
|
minify,
|
|
1187
1222
|
sourceMap: true,
|
|
1188
1223
|
visitor: {
|
|
1224
|
+
Rule: {
|
|
1225
|
+
style(rule) {
|
|
1226
|
+
const [selector, ...others] = rule.value.selectors
|
|
1227
|
+
const [component, ...rest] = selector ?? []
|
|
1228
|
+
|
|
1229
|
+
if (
|
|
1230
|
+
!others.length &&
|
|
1231
|
+
!rest.length &&
|
|
1232
|
+
component?.type === 'class' &&
|
|
1233
|
+
((schemes && classes.includes(component.name)) ||
|
|
1234
|
+
emitted.has(component.name))
|
|
1235
|
+
)
|
|
1236
|
+
return []
|
|
1237
|
+
},
|
|
1238
|
+
},
|
|
1189
1239
|
Url(url) {
|
|
1190
|
-
if (
|
|
1240
|
+
if (
|
|
1241
|
+
directory === '.' ||
|
|
1242
|
+
/^(?:\/|[?#]|[a-z][a-z\d+.-]*:)/i.test(url.url)
|
|
1243
|
+
)
|
|
1244
|
+
return url
|
|
1191
1245
|
|
|
1192
1246
|
return { ...url, url: Path.posix.join(directory, url.url) }
|
|
1193
1247
|
},
|