zyzz 0.0.26 → 0.0.28
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/Source.d.ts.map +1 -1
- package/dist/compiler/Source.js +165 -11
- package/dist/compiler/Source.js.map +1 -1
- package/dist/compiler/Transform.d.ts.map +1 -1
- package/dist/compiler/Transform.js +3 -0
- package/dist/compiler/Transform.js.map +1 -1
- package/dist/compiler/internal/Applications.d.ts.map +1 -1
- package/dist/compiler/internal/Applications.js +42 -1
- package/dist/compiler/internal/Applications.js.map +1 -1
- package/dist/compiler/internal/Selectors.d.ts +3 -2
- package/dist/compiler/internal/Selectors.d.ts.map +1 -1
- package/dist/compiler/internal/Selectors.js +23 -11
- package/dist/compiler/internal/Selectors.js.map +1 -1
- package/dist/internal/Authoring.d.ts.map +1 -1
- package/dist/internal/Authoring.js +37 -7
- package/dist/internal/Authoring.js.map +1 -1
- package/dist/internal/Configuration.d.ts +6 -1
- package/dist/internal/Configuration.d.ts.map +1 -1
- package/dist/internal/Configuration.js.map +1 -1
- package/dist/internal/Literal.d.ts +1 -1
- package/dist/internal/Literal.d.ts.map +1 -1
- package/dist/internal/Literal.js +3 -0
- package/dist/internal/Literal.js.map +1 -1
- package/dist/internal/Theme.d.ts +4 -0
- package/dist/internal/Theme.d.ts.map +1 -1
- package/dist/internal/Theme.js.map +1 -1
- package/dist/styleFunction.d.ts +11 -2
- package/dist/styleFunction.d.ts.map +1 -1
- package/dist/styleFunction.js +3 -1
- package/dist/styleFunction.js.map +1 -1
- package/dist/vite/index.d.ts +7 -0
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +173 -22
- package/dist/vite/index.js.map +1 -1
- package/dist/web/internal/Themes.d.ts.map +1 -1
- package/dist/web/internal/Themes.js +38 -2
- package/dist/web/internal/Themes.js.map +1 -1
- package/package.json +13 -1
- package/src/compiler/Source.ts +237 -13
- package/src/compiler/Transform.ts +4 -1
- package/src/compiler/internal/Applications.ts +57 -1
- package/src/compiler/internal/Selectors.ts +25 -11
- package/src/internal/Authoring.ts +53 -18
- package/src/internal/Configuration.ts +38 -1
- package/src/internal/Literal.ts +3 -0
- package/src/internal/Theme.ts +15 -0
- package/src/styleFunction.ts +31 -2
- package/src/vite/index.ts +242 -27
- package/src/web/internal/Themes.ts +47 -2
package/src/internal/Literal.ts
CHANGED
|
@@ -1588,7 +1588,10 @@ const properties = {
|
|
|
1588
1588
|
direction: { kind: 'enum', values: ['ltr', 'rtl'] },
|
|
1589
1589
|
display: {
|
|
1590
1590
|
kind: 'enum',
|
|
1591
|
+
// The Compat Standard keeps the legacy box keywords that -webkit-line-clamp requires.
|
|
1591
1592
|
values: [
|
|
1593
|
+
'-webkit-box',
|
|
1594
|
+
'-webkit-inline-box',
|
|
1592
1595
|
'block',
|
|
1593
1596
|
'block flex',
|
|
1594
1597
|
'block flow',
|
package/src/internal/Theme.ts
CHANGED
|
@@ -38,6 +38,14 @@ export type StyleFactory<tokens extends Tokens> = {
|
|
|
38
38
|
(values extends Binding.Inputs<values> ? unknown : never),
|
|
39
39
|
options?: style.DefinitionOptions,
|
|
40
40
|
): style.Dynamic<values>
|
|
41
|
+
<const entries>(
|
|
42
|
+
styles: entries & {
|
|
43
|
+
readonly [index in keyof entries]: Entry<entries[index], tokens>
|
|
44
|
+
},
|
|
45
|
+
...options: entries extends readonly unknown[]
|
|
46
|
+
? [options?: style.DefinitionOptions]
|
|
47
|
+
: [invalid: never, unavailable: never]
|
|
48
|
+
): style.ReturnType
|
|
41
49
|
<const styles extends Record<string, unknown>>(
|
|
42
50
|
styles: styles & NoInfer<Style.Accepted<styles, tokens>>,
|
|
43
51
|
options?: style.DefinitionOptions,
|
|
@@ -56,6 +64,13 @@ export type StyleFactory<tokens extends Tokens> = {
|
|
|
56
64
|
): style.ReturnType
|
|
57
65
|
}
|
|
58
66
|
|
|
67
|
+
// Distributes over mixed element unions so each member keeps its own contract.
|
|
68
|
+
type Entry<entry, tokens extends Tokens> = entry extends (
|
|
69
|
+
...args: never
|
|
70
|
+
) => unknown
|
|
71
|
+
? style.Composable<entry>
|
|
72
|
+
: entry & NoInfer<Style.Accepted<entry, tokens>>
|
|
73
|
+
|
|
59
74
|
/**
|
|
60
75
|
* Defines scalar tokens and typography sets without defaults or environment access.
|
|
61
76
|
* Colors currently use the supported literal color grammar; spacing and radius
|
package/src/styleFunction.ts
CHANGED
|
@@ -29,9 +29,21 @@ type VariableOptions<input> = input extends {
|
|
|
29
29
|
}
|
|
30
30
|
: unknown
|
|
31
31
|
|
|
32
|
+
// Composition entries are static definitions or exact literal objects.
|
|
33
|
+
type Entries<entries> = {
|
|
34
|
+
readonly [index in keyof entries]: Entry<entries[index]>
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Distributes over mixed element unions so each member keeps its own contract.
|
|
38
|
+
type Entry<entry> = entry extends (...args: never) => unknown
|
|
39
|
+
? style.Composable<entry>
|
|
40
|
+
: entry & NoInfer<Style.Accepted<entry, {}, true>> & Completion.Properties
|
|
41
|
+
|
|
32
42
|
/**
|
|
33
43
|
* Declares literal styles for source extraction. Requires a compile-time transform.
|
|
34
|
-
*
|
|
44
|
+
* An array composes static definitions and literal objects in order, so later entries
|
|
45
|
+
* win as later declarations do.
|
|
46
|
+
* @param styles - Token-free literal CSS properties, or an array of static definitions and literal objects. Omit for an empty definition.
|
|
35
47
|
* @returns A callable style definition after source rewriting.
|
|
36
48
|
* @throws {Error} When a definition needs an explicit identity and none is supplied.
|
|
37
49
|
*/
|
|
@@ -53,16 +65,29 @@ export function style<
|
|
|
53
65
|
? [options?: style.DefinitionOptions]
|
|
54
66
|
: [invalid: never, unavailable: never]
|
|
55
67
|
: [invalid: never, unavailable: never]
|
|
56
|
-
|
|
68
|
+
// TypeScript types a call that matches no overload with this signature. Other
|
|
69
|
+
// inputs leave `callback` at its constraint, so their definitions stay static.
|
|
70
|
+
): ((...args: never[]) => unknown) extends callback
|
|
71
|
+
? style.ReturnType
|
|
72
|
+
: style.Dynamic<values>
|
|
57
73
|
export function style<const styles extends Record<string, unknown>>(
|
|
58
74
|
styles: styles &
|
|
59
75
|
NoInfer<Style.Accepted<styles, {}, true>> &
|
|
60
76
|
Completion.Properties,
|
|
61
77
|
options?: style.DefinitionOptions,
|
|
62
78
|
): style.ReturnType
|
|
79
|
+
export function style<const entries>(
|
|
80
|
+
styles: entries & Entries<entries>,
|
|
81
|
+
// Exclude non-array inputs by arity so they cannot mask object-property diagnostics.
|
|
82
|
+
...options: entries extends readonly unknown[]
|
|
83
|
+
? [options?: style.DefinitionOptions]
|
|
84
|
+
: [invalid: never, unavailable: never]
|
|
85
|
+
): style.ReturnType
|
|
63
86
|
export function style(
|
|
64
87
|
styles?: unknown,
|
|
65
88
|
options: style.DefinitionOptions = {},
|
|
89
|
+
// Matches the arity-gated overloads, whose invalid branches add parameters.
|
|
90
|
+
..._: readonly never[]
|
|
66
91
|
): style.ReturnType {
|
|
67
92
|
return Authoring.create(styles, options)
|
|
68
93
|
}
|
|
@@ -73,6 +98,10 @@ type Reference = { readonly [identity]: true }
|
|
|
73
98
|
|
|
74
99
|
/** Contracts for the literal authoring boundary. */
|
|
75
100
|
export declare namespace style {
|
|
101
|
+
/** Accepts a static definition as a composition entry. Dynamic definitions and variants require cx. */
|
|
102
|
+
type Composable<entry> = entry extends Reference & (() => unknown)
|
|
103
|
+
? entry
|
|
104
|
+
: ReturnType<Output>
|
|
76
105
|
/** Stable identity for selectors and definitions that cannot be content-addressed. */
|
|
77
106
|
type DefinitionOptions = { readonly id?: string | undefined }
|
|
78
107
|
/** Callable compiled bindings with required scalar inputs and styling overrides. */
|
package/src/vite/index.ts
CHANGED
|
@@ -9,16 +9,24 @@ import * as Contracts from '../node/internal/Contracts.js'
|
|
|
9
9
|
import * as Mapping from '@jridgewell/gen-mapping'
|
|
10
10
|
import * as Lightning from 'lightningcss'
|
|
11
11
|
import * as Crypto from 'node:crypto'
|
|
12
|
+
import { existsSync } from 'node:fs'
|
|
12
13
|
import * as Fs from 'node:fs/promises'
|
|
13
14
|
import * as Path from 'node:path'
|
|
14
15
|
import * as Reset from '../node/Reset.js'
|
|
15
|
-
import { isBuiltin } from 'node:module'
|
|
16
|
+
import { createRequire, isBuiltin } from 'node:module'
|
|
16
17
|
import * as Parser from 'oxc-parser'
|
|
17
18
|
import * as Walker from 'oxc-walker'
|
|
18
19
|
import * as Snapshot from '../node/internal/Snapshot.js'
|
|
19
20
|
import * as Syntax from '../compiler/internal/Syntax.js'
|
|
20
21
|
import * as Scope from '../compiler/internal/Scope.js'
|
|
21
|
-
import type {
|
|
22
|
+
import type {
|
|
23
|
+
DevEnvironment,
|
|
24
|
+
Environment,
|
|
25
|
+
Plugin,
|
|
26
|
+
Rollup,
|
|
27
|
+
UserConfig,
|
|
28
|
+
ViteDevServer,
|
|
29
|
+
} from 'vite'
|
|
22
30
|
import * as Graph from '../compiler/Graph.js'
|
|
23
31
|
import * as Source from '../compiler/Source.js'
|
|
24
32
|
import * as ThemeRules from '../web/internal/Themes.js'
|
|
@@ -51,6 +59,7 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
51
59
|
const states = new WeakMap<Environment, Map<string, Entry>>()
|
|
52
60
|
const removed = new WeakMap<Environment, Set<string>>()
|
|
53
61
|
const reads = new WeakMap<Environment, Map<string, string>>()
|
|
62
|
+
const lookups = new WeakMap<Environment, Lookups>()
|
|
54
63
|
const compilers = new WeakMap<Environment, Graph.create.ReturnType>()
|
|
55
64
|
const packages = Contracts.create()
|
|
56
65
|
|
|
@@ -71,8 +80,11 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
71
80
|
const initializers = new WeakMap<Environment, Entry>()
|
|
72
81
|
const sourceEntrypoints = new Set<string>()
|
|
73
82
|
const identities = new Map<string, string>()
|
|
83
|
+
const exclusions = new Map<string, boolean>()
|
|
74
84
|
const lazyImports = new WeakMap<Parser.ParseResult, string[]>()
|
|
75
85
|
let directories: readonly string[]
|
|
86
|
+
let filter: (id: unknown) => boolean
|
|
87
|
+
let optimizeRuntime = false
|
|
76
88
|
let root: string
|
|
77
89
|
|
|
78
90
|
function snapshot(environment: Environment) {
|
|
@@ -149,10 +161,26 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
149
161
|
Path.isAbsolute(id) &&
|
|
150
162
|
sourceRoot(id) !== undefined &&
|
|
151
163
|
/\.[cm]?[jt]sx?$/.test(id) &&
|
|
152
|
-
!/\.(?:d|test|test-d|bench|bench-d)\.[cm]?[jt]sx?$/.test(id)
|
|
164
|
+
!/\.(?:d|test|test-d|bench|bench-d)\.[cm]?[jt]sx?$/.test(id) &&
|
|
165
|
+
!excluded(id)
|
|
153
166
|
)
|
|
154
167
|
}
|
|
155
168
|
|
|
169
|
+
/** Matches excluded files inside source directories. Package files never match, so packed contracts stay readable. */
|
|
170
|
+
function excluded(id: string) {
|
|
171
|
+
const file = id.split(/[?#]/)[0]!
|
|
172
|
+
let value = exclusions.get(file)
|
|
173
|
+
|
|
174
|
+
// Vite's filter recompiles each glob per call, and every compile checks each import.
|
|
175
|
+
if (value === undefined) {
|
|
176
|
+
value =
|
|
177
|
+
Path.isAbsolute(file) && sourceRoot(file) !== undefined && !filter(file)
|
|
178
|
+
exclusions.set(file, value)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return value
|
|
182
|
+
}
|
|
183
|
+
|
|
156
184
|
function sourceRoot(file: string) {
|
|
157
185
|
return directories.find((directory) => {
|
|
158
186
|
const relative = Path.relative(directory, file)
|
|
@@ -355,6 +383,7 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
355
383
|
event: string,
|
|
356
384
|
) {
|
|
357
385
|
reads.delete(environment)
|
|
386
|
+
lookups.delete(environment)
|
|
358
387
|
if (event === 'delete') {
|
|
359
388
|
if (entries(environment).has(file)) {
|
|
360
389
|
let files = removed.get(environment)
|
|
@@ -392,9 +421,32 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
392
421
|
allSources = false,
|
|
393
422
|
everything = false,
|
|
394
423
|
) {
|
|
424
|
+
// Every compile visits the discovered project, so modules share lookups
|
|
425
|
+
// until the watcher reports a change. Full-project compiles look up afresh.
|
|
426
|
+
const cache = (() => {
|
|
427
|
+
const fresh: Lookups = { contracts: new Map(), resolutions: new Map() }
|
|
428
|
+
if (everything) return fresh
|
|
429
|
+
|
|
430
|
+
let value = lookups.get(entry.environment)
|
|
431
|
+
if (!value) {
|
|
432
|
+
value = fresh
|
|
433
|
+
lookups.set(entry.environment, value)
|
|
434
|
+
}
|
|
435
|
+
return value
|
|
436
|
+
})()
|
|
437
|
+
|
|
395
438
|
async function resolve(source: string, importer: string) {
|
|
396
439
|
if (isBuiltin(source)) return { id: source, external: true }
|
|
397
|
-
|
|
440
|
+
|
|
441
|
+
const key = `${importer}\0${source}`
|
|
442
|
+
let pending = cache.resolutions.get(key)
|
|
443
|
+
if (!pending) {
|
|
444
|
+
pending = host.resolve(source, importer)
|
|
445
|
+
cache.resolutions.set(key, pending)
|
|
446
|
+
pending.catch(() => cache.resolutions.delete(key))
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const resolved = await pending
|
|
398
450
|
if (!resolved) return resolved
|
|
399
451
|
|
|
400
452
|
if (entry.environment.mode === 'dev') {
|
|
@@ -416,15 +468,41 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
416
468
|
const lazy: Record<string, ReadonlySet<string>> = Object.create(null)
|
|
417
469
|
const modules: Record<string, string> = Object.create(null)
|
|
418
470
|
const files = new Set<string>()
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
471
|
+
|
|
472
|
+
// A package contract derives from the files it reads and the resolutions
|
|
473
|
+
// above. A cached contract still makes this compile watch those files.
|
|
474
|
+
async function readContract(file: string, required = false) {
|
|
475
|
+
const key = `${required}\0${file}`
|
|
476
|
+
let pending = cache.contracts.get(key)
|
|
477
|
+
if (!pending) {
|
|
478
|
+
const read = new Set<string>()
|
|
479
|
+
|
|
480
|
+
pending = packages
|
|
481
|
+
.read(file, {
|
|
482
|
+
read: async (path) => {
|
|
483
|
+
const source = await snapshot(entry.environment).read(path)
|
|
484
|
+
read.add(path)
|
|
485
|
+
return source
|
|
486
|
+
},
|
|
487
|
+
required,
|
|
488
|
+
resolve: async (specifier, importer) =>
|
|
489
|
+
(await resolve(specifier, importer))?.id.split(/[?#]/)[0],
|
|
490
|
+
})
|
|
491
|
+
.then((contract) => ({ contract, files: read }))
|
|
492
|
+
cache.contracts.set(key, pending)
|
|
493
|
+
pending.catch(() => cache.contracts.delete(key))
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const derived = await pending
|
|
497
|
+
|
|
498
|
+
for (const path of derived.files) {
|
|
499
|
+
if (files.has(path)) continue
|
|
500
|
+
|
|
501
|
+
files.add(path)
|
|
502
|
+
host.watch(path)
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
return derived.contract
|
|
428
506
|
}
|
|
429
507
|
|
|
430
508
|
async function visit(file: string, source?: string) {
|
|
@@ -522,11 +600,18 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
522
600
|
await visit(resolved.id)
|
|
523
601
|
continue
|
|
524
602
|
}
|
|
603
|
+
|
|
604
|
+
// An excluded source stays a runtime import instead of deriving a package contract.
|
|
605
|
+
if (excluded(resolved.id)) {
|
|
606
|
+
resolutions[specifier] = null
|
|
607
|
+
continue
|
|
608
|
+
}
|
|
609
|
+
|
|
525
610
|
if (resolved.external || !eligible(resolved.id)) {
|
|
526
611
|
const physical = resolved.id.split('?')[0]!.split('#')[0]!
|
|
527
612
|
const contract =
|
|
528
613
|
Path.isAbsolute(physical) && /\.[cm]?[jt]sx?$/.test(physical)
|
|
529
|
-
? await
|
|
614
|
+
? await readContract(physical)
|
|
530
615
|
: undefined
|
|
531
616
|
|
|
532
617
|
if (contract !== undefined) {
|
|
@@ -648,7 +733,11 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
648
733
|
continue
|
|
649
734
|
|
|
650
735
|
const resolved = await resolve(specifier, file)
|
|
651
|
-
if (
|
|
736
|
+
if (
|
|
737
|
+
!resolved ||
|
|
738
|
+
(!resolved.external && eligible(resolved.id)) ||
|
|
739
|
+
excluded(resolved.id)
|
|
740
|
+
)
|
|
652
741
|
continue
|
|
653
742
|
|
|
654
743
|
if (resource(specifier) || resource(resolved.id)) continue
|
|
@@ -657,7 +746,7 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
657
746
|
if (!Path.isAbsolute(physical) || !/\.[cm]?[jt]sx?$/.test(physical))
|
|
658
747
|
continue
|
|
659
748
|
|
|
660
|
-
const contract = await
|
|
749
|
+
const contract = await readContract(physical)
|
|
661
750
|
if (contract === undefined) continue
|
|
662
751
|
|
|
663
752
|
contracts[resolved.id] = contract
|
|
@@ -715,9 +804,9 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
715
804
|
imports[owner] ??= Object.create(null)
|
|
716
805
|
imports[owner]![specifier] = target.id
|
|
717
806
|
|
|
718
|
-
contracts[target.id] ??= (await
|
|
807
|
+
contracts[target.id] ??= (await readContract(
|
|
719
808
|
target.id.split(/[?#]/)[0]!,
|
|
720
|
-
|
|
809
|
+
true,
|
|
721
810
|
))!
|
|
722
811
|
|
|
723
812
|
await dependencies(target.id)
|
|
@@ -995,10 +1084,10 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
995
1084
|
async function initializations(server: ViteDevServer) {
|
|
996
1085
|
const environment = server.environments.client
|
|
997
1086
|
reads.delete(environment)
|
|
1087
|
+
lookups.delete(environment)
|
|
998
1088
|
const host: Host = {
|
|
999
1089
|
asset: async (file) => file,
|
|
1000
|
-
resolve: (
|
|
1001
|
-
environment.pluginContainer.resolveId(source, importer),
|
|
1090
|
+
resolve: resolver(environment),
|
|
1002
1091
|
watch: (file) => server.watcher.add(file),
|
|
1003
1092
|
}
|
|
1004
1093
|
const sources = await discover(environment, host)
|
|
@@ -1189,10 +1278,17 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
1189
1278
|
return {
|
|
1190
1279
|
buildStart() {
|
|
1191
1280
|
reads.delete(this.environment)
|
|
1281
|
+
lookups.delete(this.environment)
|
|
1192
1282
|
},
|
|
1193
1283
|
config: {
|
|
1194
1284
|
order: 'post',
|
|
1195
|
-
handler(config) {
|
|
1285
|
+
handler(config, env) {
|
|
1286
|
+
// Ordered after other config hooks, so plugin-provided aliases are visible.
|
|
1287
|
+
optimizeRuntime =
|
|
1288
|
+
env.command === 'serve' &&
|
|
1289
|
+
options.compiler !== false &&
|
|
1290
|
+
installed(config)
|
|
1291
|
+
|
|
1196
1292
|
if (native) return
|
|
1197
1293
|
// Inline color-scheme changes cannot initialize Lightning's lowered helpers.
|
|
1198
1294
|
return {
|
|
@@ -1221,6 +1317,32 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
1221
1317
|
}
|
|
1222
1318
|
},
|
|
1223
1319
|
},
|
|
1320
|
+
configEnvironment: {
|
|
1321
|
+
// Plugins without enforce settle their environment options first.
|
|
1322
|
+
order: 'post',
|
|
1323
|
+
handler(name, config) {
|
|
1324
|
+
if (!optimizeRuntime) return
|
|
1325
|
+
|
|
1326
|
+
const optimizeDeps = config.optimizeDeps ?? {}
|
|
1327
|
+
const consumer =
|
|
1328
|
+
config.consumer ?? (name === 'client' ? 'client' : 'server')
|
|
1329
|
+
|
|
1330
|
+
// Vite defaults server environments to noDiscovery. An include list there
|
|
1331
|
+
// would create an explicit optimizer that otherwise never exists.
|
|
1332
|
+
if (optimizeDeps.noDiscovery ?? consumer === 'server') return
|
|
1333
|
+
if (
|
|
1334
|
+
optimizeDeps.include?.includes(runtime) ||
|
|
1335
|
+
optimizeDeps.exclude?.some((id) => id === 'zyzz' || id === runtime)
|
|
1336
|
+
)
|
|
1337
|
+
return
|
|
1338
|
+
|
|
1339
|
+
// Vite scans authored source, so the runtime import that compilation
|
|
1340
|
+
// adds first appears while a request transforms a module. Discovering
|
|
1341
|
+
// it then re-runs the optimizer, and modules loaded before the re-run
|
|
1342
|
+
// can keep a separate copy of a shared dependency such as React.
|
|
1343
|
+
return { optimizeDeps: { include: [runtime] } }
|
|
1344
|
+
},
|
|
1345
|
+
},
|
|
1224
1346
|
configureServer(server) {
|
|
1225
1347
|
// Directory watches belong to the server, not module import dependencies.
|
|
1226
1348
|
server.watcher.add(directories.slice(1))
|
|
@@ -1257,6 +1379,14 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
1257
1379
|
}),
|
|
1258
1380
|
)),
|
|
1259
1381
|
]
|
|
1382
|
+
|
|
1383
|
+
// Portable build adapters share this module and do not require Vite.
|
|
1384
|
+
const { createFilter } = await import('vite')
|
|
1385
|
+
const patterns = [...configFiles, ...(options.exclude ?? [])]
|
|
1386
|
+
|
|
1387
|
+
filter = createFilter(null, patterns, { resolve: root })
|
|
1388
|
+
exclusions.clear()
|
|
1389
|
+
|
|
1260
1390
|
if (native) return
|
|
1261
1391
|
const lightning = (config.css.lightningcss ??= {})
|
|
1262
1392
|
lightning.include =
|
|
@@ -1370,8 +1500,7 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
1370
1500
|
? await compile(
|
|
1371
1501
|
first,
|
|
1372
1502
|
{
|
|
1373
|
-
resolve: (
|
|
1374
|
-
this.environment.pluginContainer.resolveId(source, importer),
|
|
1503
|
+
resolve: resolver(this.environment),
|
|
1375
1504
|
watch: (file) => {
|
|
1376
1505
|
server.watcher.add(file)
|
|
1377
1506
|
},
|
|
@@ -1465,7 +1594,10 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
1465
1594
|
const output = await compile(
|
|
1466
1595
|
entry,
|
|
1467
1596
|
{
|
|
1468
|
-
resolve:
|
|
1597
|
+
resolve:
|
|
1598
|
+
this.environment.mode === 'dev'
|
|
1599
|
+
? resolver(this.environment)
|
|
1600
|
+
: (source, importer) => this.resolve(source, importer),
|
|
1469
1601
|
watch: (file) => this.addWatchFile(file),
|
|
1470
1602
|
asset: async (file) => {
|
|
1471
1603
|
const reference = this.emitFile({
|
|
@@ -1527,7 +1659,10 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
1527
1659
|
const output = await compile(
|
|
1528
1660
|
entry,
|
|
1529
1661
|
{
|
|
1530
|
-
resolve:
|
|
1662
|
+
resolve:
|
|
1663
|
+
this.environment.mode === 'dev'
|
|
1664
|
+
? resolver(this.environment)
|
|
1665
|
+
: (source, importer) => this.resolve(source, importer),
|
|
1531
1666
|
watch: (file) => this.addWatchFile(file),
|
|
1532
1667
|
asset: async (file) => {
|
|
1533
1668
|
const reference = this.emitFile({
|
|
@@ -1629,8 +1764,7 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
1629
1764
|
return await documentCatalogs(
|
|
1630
1765
|
html,
|
|
1631
1766
|
context.filename,
|
|
1632
|
-
(
|
|
1633
|
-
environment.pluginContainer.resolveId(source, importer),
|
|
1767
|
+
resolver(environment),
|
|
1634
1768
|
state,
|
|
1635
1769
|
)
|
|
1636
1770
|
})()
|
|
@@ -1666,7 +1800,81 @@ type Host = {
|
|
|
1666
1800
|
watch: (file: string) => void
|
|
1667
1801
|
}
|
|
1668
1802
|
|
|
1803
|
+
type Lookups = {
|
|
1804
|
+
contracts: Map<
|
|
1805
|
+
string,
|
|
1806
|
+
Promise<{ contract: string | undefined; files: ReadonlySet<string> }>
|
|
1807
|
+
>
|
|
1808
|
+
resolutions: Map<string, ReturnType<Host['resolve']>>
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
/**
|
|
1812
|
+
* Whether `zyzz/runtime` resolves inside node_modules from the Vite root. Vite
|
|
1813
|
+
* optimizes a discovered import only there, so linked and aliased copies stay
|
|
1814
|
+
* source modules beside the other entrypoints.
|
|
1815
|
+
*/
|
|
1816
|
+
function installed(config: UserConfig) {
|
|
1817
|
+
const aliases = config.resolve?.alias ?? {}
|
|
1818
|
+
const entries = Array.isArray(aliases)
|
|
1819
|
+
? aliases
|
|
1820
|
+
: Object.entries(aliases).map(([find, replacement]) => ({
|
|
1821
|
+
find,
|
|
1822
|
+
replacement,
|
|
1823
|
+
}))
|
|
1824
|
+
|
|
1825
|
+
// Matches Vite's alias semantics: a string entry covers its subpaths.
|
|
1826
|
+
if (
|
|
1827
|
+
entries.some((entry) =>
|
|
1828
|
+
typeof entry.find === 'string'
|
|
1829
|
+
? runtime === entry.find || runtime.startsWith(`${entry.find}/`)
|
|
1830
|
+
: entry.find.test(runtime),
|
|
1831
|
+
)
|
|
1832
|
+
)
|
|
1833
|
+
return false
|
|
1834
|
+
|
|
1835
|
+
try {
|
|
1836
|
+
const require = createRequire(
|
|
1837
|
+
Path.join(Path.resolve(config.root ?? ''), 'package.json'),
|
|
1838
|
+
)
|
|
1839
|
+
|
|
1840
|
+
// Vite keeps a symlinked package at its node_modules path, which Node dereferences.
|
|
1841
|
+
if (config.resolve?.preserveSymlinks)
|
|
1842
|
+
return (require.resolve.paths('zyzz') ?? []).some((directory) =>
|
|
1843
|
+
existsSync(Path.join(directory, 'zyzz/package.json')),
|
|
1844
|
+
)
|
|
1845
|
+
|
|
1846
|
+
return require.resolve(runtime).split(Path.sep).includes('node_modules')
|
|
1847
|
+
} catch {
|
|
1848
|
+
return false
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
// Vite forwards `scan` to `resolveId` hooks but leaves it out of its public types.
|
|
1853
|
+
type ScanOptions = NonNullable<
|
|
1854
|
+
Parameters<DevEnvironment['pluginContainer']['resolveId']>[2]
|
|
1855
|
+
> & { scan: boolean }
|
|
1856
|
+
|
|
1857
|
+
const scan: ScanOptions = { scan: true }
|
|
1858
|
+
|
|
1859
|
+
/**
|
|
1860
|
+
* Resolves imports read from source before other plugins transform their
|
|
1861
|
+
* importers. Marking them as scans, as Vite's dependency scanner does, keeps the
|
|
1862
|
+
* optimizer from discovering their dependencies and lets plugins tell them apart
|
|
1863
|
+
* from imports that reach the module graph.
|
|
1864
|
+
*/
|
|
1865
|
+
function resolver(environment: DevEnvironment): Host['resolve'] {
|
|
1866
|
+
return (source, importer) =>
|
|
1867
|
+
environment.pluginContainer.resolveId(source, importer, scan)
|
|
1868
|
+
}
|
|
1869
|
+
|
|
1870
|
+
// Vite and Vitest load their own configuration modules, which application code never imports.
|
|
1871
|
+
const configFiles = [
|
|
1872
|
+
'**/vite.config.*',
|
|
1873
|
+
'**/vitest.config.*',
|
|
1874
|
+
'**/vitest.workspace.*',
|
|
1875
|
+
]
|
|
1669
1876
|
const prefix = '\0zyzz:'
|
|
1877
|
+
const runtime = 'zyzz/runtime'
|
|
1670
1878
|
const sharedId = `${prefix}shared.css`
|
|
1671
1879
|
|
|
1672
1880
|
function cssId(file: string) {
|
|
@@ -1679,6 +1887,13 @@ export declare namespace zyzz {
|
|
|
1679
1887
|
type Options = {
|
|
1680
1888
|
/** False retains authored calls and requires explicit identities where needed. */
|
|
1681
1889
|
readonly compiler?: boolean | undefined
|
|
1890
|
+
/**
|
|
1891
|
+
* Source modules left out of discovery and transforms. Globs resolve against the
|
|
1892
|
+
* Vite root unless they start with `**`, and regular expressions test absolute paths.
|
|
1893
|
+
* Imports of excluded modules stay runtime imports. Vite and Vitest configuration
|
|
1894
|
+
* modules are always excluded.
|
|
1895
|
+
*/
|
|
1896
|
+
readonly exclude?: readonly (RegExp | string)[] | undefined
|
|
1682
1897
|
/** Additional source directories, absolute or relative to the Vite root. */
|
|
1683
1898
|
readonly include?: readonly string[] | undefined
|
|
1684
1899
|
/** Native context captured at creation. Disables CSS delivery and initialization scripts. */
|
|
@@ -70,6 +70,7 @@ export function create() {
|
|
|
70
70
|
const completeDefaults = new Set<Token.Contract>()
|
|
71
71
|
const empty: string[] = []
|
|
72
72
|
const rules: Rule[] = []
|
|
73
|
+
const varied = variedPaths(themes)
|
|
73
74
|
|
|
74
75
|
for (const [name, theme] of Object.entries(themes)) {
|
|
75
76
|
if (!name) throw new Error('Theme names must be nonempty.')
|
|
@@ -132,6 +133,12 @@ export function create() {
|
|
|
132
133
|
const value = data.values[path]
|
|
133
134
|
// A set sharing its contract with an extension lacks the extension's added paths.
|
|
134
135
|
if (value === undefined) continue
|
|
136
|
+
// An inherited base reference resolves through its fallback, unless a sibling set overrides it.
|
|
137
|
+
if (
|
|
138
|
+
inherited(value, path, data.contract) &&
|
|
139
|
+
!varied.get(data.contract)?.has(path)
|
|
140
|
+
)
|
|
141
|
+
continue
|
|
135
142
|
|
|
136
143
|
const label = path
|
|
137
144
|
declarations.push({
|
|
@@ -258,9 +265,9 @@ export function create() {
|
|
|
258
265
|
if ('default' in value) {
|
|
259
266
|
// Separate fallback properties preserve extensions and resolve references within each scope.
|
|
260
267
|
const base = literal(value.default, label, owner)
|
|
261
|
-
// Rule consolidation must not change existing variable identities.
|
|
268
|
+
// Rule consolidation must not change existing variable identities, so the name hashes the fallback's CSS.
|
|
262
269
|
const css = `:where(*){--fallback:${base};}${conditionalCss(value, '--fallback', ':where(*)', label, owner)}`
|
|
263
|
-
const name = `${owner ? property(owner, label ?? 'value') : variable(undefined, label ?? 'value')}-fallback-${Identity.
|
|
270
|
+
const name = `${owner ? property(owner, label ?? 'value') : variable(undefined, label ?? 'value')}-fallback-${Identity.hash(css)}`
|
|
264
271
|
const emitted: Rule[] = [
|
|
265
272
|
{ conditions: [], property: name, selector: ':where(*)', value: base },
|
|
266
273
|
]
|
|
@@ -410,6 +417,44 @@ export function scope(value: string, identity?: string): string {
|
|
|
410
417
|
return `z-theme-${namespace}${Identity.name(value)}`
|
|
411
418
|
}
|
|
412
419
|
|
|
420
|
+
/** Whether a set's value is its base's own reference at the same path, as named extensions inherit. */
|
|
421
|
+
function inherited(value: Token.Value, path: string, contract: Token.Contract) {
|
|
422
|
+
return Token.is(value) && value.path === path && value.contract !== contract
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/** Paths whose values differ between sets that share a contract. */
|
|
426
|
+
function variedPaths(
|
|
427
|
+
themes: Readonly<Record<string, Theme.Definition | Vars.Definition>>,
|
|
428
|
+
) {
|
|
429
|
+
const seen = new Map<Token.Contract, Map<string, string>>()
|
|
430
|
+
const varied = new Map<Token.Contract, Set<string>>()
|
|
431
|
+
|
|
432
|
+
for (const theme of Object.values(themes)) {
|
|
433
|
+
const data = Object.getOwnPropertyDescriptor(theme, Token.definition)
|
|
434
|
+
?.value as Token.Metadata | undefined
|
|
435
|
+
if (!data) continue
|
|
436
|
+
|
|
437
|
+
const values = seen.get(data.contract) ?? new Map<string, string>()
|
|
438
|
+
seen.set(data.contract, values)
|
|
439
|
+
|
|
440
|
+
for (const [path, value] of Object.entries(data.values)) {
|
|
441
|
+
const key = Token.is(value)
|
|
442
|
+
? JSON.stringify([value.contract[Token.identity], value.path])
|
|
443
|
+
: JSON.stringify(value)
|
|
444
|
+
const previous = values.get(path)
|
|
445
|
+
|
|
446
|
+
if (previous === undefined) values.set(path, key)
|
|
447
|
+
else if (previous !== key) {
|
|
448
|
+
const paths = varied.get(data.contract) ?? new Set<string>()
|
|
449
|
+
paths.add(path)
|
|
450
|
+
varied.set(data.contract, paths)
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
return varied
|
|
456
|
+
}
|
|
457
|
+
|
|
413
458
|
function variable(identity: string | undefined, path: string): string {
|
|
414
459
|
const namespace = identity?.startsWith('id-') ? `${identity.slice(3)}-` : ''
|
|
415
460
|
return `--z-${namespace}${path.split('.').map(Identity.name).join('-')}`
|