zyzz 0.0.28 → 0.0.29
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/binding/wasi/wasi-worker.mjs +186 -0
- package/binding/wasi/zyzz.wasi.cjs +2302 -0
- package/binding/wasi/zyzz.wasm32-wasi.wasm +0 -0
- package/binding/zyzz.linux-x64-gnu.node +0 -0
- package/dist/babel/index.d.ts.map +1 -1
- package/dist/babel/index.js +2 -4
- package/dist/babel/index.js.map +1 -1
- package/dist/babel/internal/Compilation.d.ts +0 -1
- package/dist/babel/internal/Compilation.d.ts.map +1 -1
- package/dist/compiler/Graph.d.ts +14 -11
- package/dist/compiler/Graph.d.ts.map +1 -1
- package/dist/compiler/Graph.js +57 -1442
- package/dist/compiler/Graph.js.map +1 -1
- package/dist/compiler/Native.d.ts +7 -9
- package/dist/compiler/Native.d.ts.map +1 -1
- package/dist/compiler/Native.js +12 -716
- package/dist/compiler/Native.js.map +1 -1
- package/dist/compiler/Source.d.ts +122 -24
- package/dist/compiler/Source.d.ts.map +1 -1
- package/dist/compiler/Source.js +10 -1267
- package/dist/compiler/Source.js.map +1 -1
- package/dist/compiler/Transform.d.ts +4 -3
- package/dist/compiler/Transform.d.ts.map +1 -1
- package/dist/compiler/Transform.js +13 -961
- package/dist/compiler/Transform.js.map +1 -1
- package/dist/compiler/internal/AtRules.d.ts +5 -1
- package/dist/compiler/internal/AtRules.d.ts.map +1 -1
- package/dist/compiler/internal/AtRules.js +12 -106
- package/dist/compiler/internal/AtRules.js.map +1 -1
- package/dist/compiler/internal/Context.d.ts +11 -0
- package/dist/compiler/internal/Context.d.ts.map +1 -0
- package/dist/compiler/internal/Context.js +11 -0
- package/dist/compiler/internal/Context.js.map +1 -0
- package/dist/compiler/internal/Results.d.ts +148 -0
- package/dist/compiler/internal/Results.d.ts.map +1 -0
- package/dist/compiler/internal/Results.js +259 -0
- package/dist/compiler/internal/Results.js.map +1 -0
- package/dist/compiler/internal/Syntax.d.ts +0 -2
- package/dist/compiler/internal/Syntax.d.ts.map +1 -1
- package/dist/compiler/internal/Syntax.js +0 -2
- package/dist/compiler/internal/Syntax.js.map +1 -1
- package/dist/compiler/internal/Wire.d.ts +17 -0
- package/dist/compiler/internal/Wire.d.ts.map +1 -0
- package/dist/compiler/internal/Wire.js +795 -0
- package/dist/compiler/internal/Wire.js.map +1 -0
- package/dist/internal/Addon.d.ts +28 -0
- package/dist/internal/Addon.d.ts.map +1 -0
- package/dist/internal/Addon.js +87 -0
- package/dist/internal/Addon.js.map +1 -0
- package/dist/internal/Literal.d.ts +4 -1
- package/dist/internal/Literal.d.ts.map +1 -1
- package/dist/internal/Literal.js +4 -2
- package/dist/internal/Literal.js.map +1 -1
- package/dist/internal/Token.d.ts +2 -1
- package/dist/internal/Token.d.ts.map +1 -1
- package/dist/internal/Token.js +2 -2
- package/dist/internal/Token.js.map +1 -1
- package/dist/internal/Vlq.d.ts +14 -0
- package/dist/internal/Vlq.d.ts.map +1 -0
- package/dist/internal/Vlq.js +47 -0
- package/dist/internal/Vlq.js.map +1 -0
- package/dist/internal/Wasm.d.ts +22 -0
- package/dist/internal/Wasm.d.ts.map +1 -0
- package/dist/internal/Wasm.js +83 -0
- package/dist/internal/Wasm.js.map +1 -0
- package/dist/metro/Graph.d.ts +0 -1
- package/dist/metro/Graph.d.ts.map +1 -1
- package/dist/metro/transformer.d.ts.map +1 -1
- package/dist/metro/transformer.js +0 -1
- package/dist/metro/transformer.js.map +1 -1
- package/dist/next/internal/Project.d.ts.map +1 -1
- package/dist/next/internal/Project.js +2 -5
- package/dist/next/internal/Project.js.map +1 -1
- package/dist/node/Host.d.ts.map +1 -1
- package/dist/node/Host.js +0 -2
- package/dist/node/Host.js.map +1 -1
- package/dist/node/internal/Contracts.d.ts.map +1 -1
- package/dist/node/internal/Contracts.js +3 -7
- package/dist/node/internal/Contracts.js.map +1 -1
- package/dist/node/internal/Snapshot.d.ts +0 -1
- package/dist/node/internal/Snapshot.d.ts.map +1 -1
- package/dist/node/internal/Snapshot.js +0 -6
- package/dist/node/internal/Snapshot.js.map +1 -1
- package/dist/runtime/Composition.d.ts +2 -3
- package/dist/runtime/Composition.d.ts.map +1 -1
- package/dist/runtime/Composition.js +51 -30
- package/dist/runtime/Composition.js.map +1 -1
- package/dist/runtime/Recipe.d.ts.map +1 -1
- package/dist/runtime/Recipe.js +16 -12
- package/dist/runtime/Recipe.js.map +1 -1
- package/dist/unplugin/index.d.ts.map +1 -1
- package/dist/unplugin/index.js +3 -6
- package/dist/unplugin/index.js.map +1 -1
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +696 -201
- package/dist/vite/index.js.map +1 -1
- package/dist/web/Css.d.ts +8 -3
- package/dist/web/Css.d.ts.map +1 -1
- package/dist/web/Css.js +5 -721
- package/dist/web/Css.js.map +1 -1
- package/dist/web/internal/Block.d.ts +0 -2
- package/dist/web/internal/Block.d.ts.map +1 -1
- package/dist/web/internal/Block.js +1 -12
- package/dist/web/internal/Block.js.map +1 -1
- package/dist/web/internal/ClassName.d.ts +23 -18
- package/dist/web/internal/ClassName.d.ts.map +1 -1
- package/dist/web/internal/ClassName.js +26 -55
- package/dist/web/internal/ClassName.js.map +1 -1
- package/dist/web/internal/Contributions.d.ts +3 -5
- package/dist/web/internal/Contributions.d.ts.map +1 -1
- package/dist/web/internal/Contributions.js +0 -45
- package/dist/web/internal/Contributions.js.map +1 -1
- package/dist/web/internal/Engine.browser.d.ts +8 -0
- package/dist/web/internal/Engine.browser.d.ts.map +1 -0
- package/dist/web/internal/Engine.browser.js +42 -0
- package/dist/web/internal/Engine.browser.js.map +1 -0
- package/dist/web/internal/Engine.d.ts +9 -0
- package/dist/web/internal/Engine.d.ts.map +1 -0
- package/dist/web/internal/Engine.js +14 -0
- package/dist/web/internal/Engine.js.map +1 -0
- package/dist/web/internal/Themes.d.ts +1 -9
- package/dist/web/internal/Themes.d.ts.map +1 -1
- package/dist/web/internal/Themes.js +3 -221
- package/dist/web/internal/Themes.js.map +1 -1
- package/dist/web/internal/Wasm.generated.d.ts +4 -0
- package/dist/web/internal/Wasm.generated.d.ts.map +1 -0
- package/dist/web/internal/Wasm.generated.js +4 -0
- package/dist/web/internal/Wasm.generated.js.map +1 -0
- package/dist/web/internal/Wire.d.ts +19 -0
- package/dist/web/internal/Wire.d.ts.map +1 -0
- package/dist/web/internal/Wire.js +387 -0
- package/dist/web/internal/Wire.js.map +1 -0
- package/package.json +20 -1
- package/src/babel/index.ts +2 -5
- package/src/babel/internal/Compilation.ts +0 -1
- package/src/compiler/Graph.ts +89 -1930
- package/src/compiler/Native.ts +18 -871
- package/src/compiler/Source.ts +144 -1803
- package/src/compiler/Transform.ts +16 -1315
- package/src/compiler/internal/AtRules.ts +27 -115
- package/src/compiler/internal/Context.ts +11 -0
- package/src/compiler/internal/Results.ts +406 -0
- package/src/compiler/internal/Syntax.ts +0 -3
- package/src/compiler/internal/Wire.ts +1045 -0
- package/src/internal/Addon.ts +118 -0
- package/src/internal/Literal.ts +4 -2
- package/src/internal/Token.ts +2 -2
- package/src/internal/Vlq.ts +59 -0
- package/src/internal/Wasm.ts +111 -0
- package/src/metro/transformer.ts +0 -1
- package/src/next/internal/Project.ts +2 -9
- package/src/node/Host.ts +0 -2
- package/src/node/internal/Contracts.ts +3 -7
- package/src/node/internal/Snapshot.ts +0 -8
- package/src/runtime/Composition.ts +48 -25
- package/src/runtime/Recipe.ts +21 -14
- package/src/unplugin/index.ts +6 -6
- package/src/vite/index.ts +922 -221
- package/src/web/Css.ts +12 -908
- package/src/web/internal/Block.ts +0 -14
- package/src/web/internal/ClassName.ts +52 -85
- package/src/web/internal/Contributions.ts +3 -59
- package/src/web/internal/Engine.browser.ts +51 -0
- package/src/web/internal/Engine.ts +17 -0
- package/src/web/internal/Themes.ts +3 -279
- package/src/web/internal/Wasm.generated.ts +3 -0
- package/src/web/internal/Wire.ts +466 -0
- package/src/web/internal/wasm.d.ts +9 -0
- package/dist/compiler/internal/Applications.d.ts +0 -55
- package/dist/compiler/internal/Applications.d.ts.map +0 -1
- package/dist/compiler/internal/Applications.js +0 -393
- package/dist/compiler/internal/Applications.js.map +0 -1
- package/dist/compiler/internal/ColorProfiles.d.ts +0 -3
- package/dist/compiler/internal/ColorProfiles.d.ts.map +0 -1
- package/dist/compiler/internal/ColorProfiles.js +0 -84
- package/dist/compiler/internal/ColorProfiles.js.map +0 -1
- package/dist/compiler/internal/CompositionBindings.d.ts +0 -16
- package/dist/compiler/internal/CompositionBindings.d.ts.map +0 -1
- package/dist/compiler/internal/CompositionBindings.js +0 -89
- package/dist/compiler/internal/CompositionBindings.js.map +0 -1
- package/dist/compiler/internal/Compositions.d.ts +0 -37
- package/dist/compiler/internal/Compositions.d.ts.map +0 -1
- package/dist/compiler/internal/Compositions.js +0 -494
- package/dist/compiler/internal/Compositions.js.map +0 -1
- package/dist/compiler/internal/Configurations.d.ts +0 -26
- package/dist/compiler/internal/Configurations.d.ts.map +0 -1
- package/dist/compiler/internal/Configurations.js +0 -259
- package/dist/compiler/internal/Configurations.js.map +0 -1
- package/dist/compiler/internal/Contract.d.ts +0 -30
- package/dist/compiler/internal/Contract.d.ts.map +0 -1
- package/dist/compiler/internal/Contract.js +0 -753
- package/dist/compiler/internal/Contract.js.map +0 -1
- package/dist/compiler/internal/Contributions.d.ts +0 -54
- package/dist/compiler/internal/Contributions.d.ts.map +0 -1
- package/dist/compiler/internal/Contributions.js +0 -1151
- package/dist/compiler/internal/Contributions.js.map +0 -1
- package/dist/compiler/internal/Descriptors.d.ts +0 -15
- package/dist/compiler/internal/Descriptors.d.ts.map +0 -1
- package/dist/compiler/internal/Descriptors.js +0 -192
- package/dist/compiler/internal/Descriptors.js.map +0 -1
- package/dist/compiler/internal/Dynamic.d.ts +0 -14
- package/dist/compiler/internal/Dynamic.d.ts.map +0 -1
- package/dist/compiler/internal/Dynamic.js +0 -175
- package/dist/compiler/internal/Dynamic.js.map +0 -1
- package/dist/compiler/internal/Expression.d.ts +0 -19
- package/dist/compiler/internal/Expression.d.ts.map +0 -1
- package/dist/compiler/internal/Expression.js +0 -164
- package/dist/compiler/internal/Expression.js.map +0 -1
- package/dist/compiler/internal/Functions.d.ts +0 -5
- package/dist/compiler/internal/Functions.d.ts.map +0 -1
- package/dist/compiler/internal/Functions.js +0 -191
- package/dist/compiler/internal/Functions.js.map +0 -1
- package/dist/compiler/internal/Keyframes.d.ts +0 -5
- package/dist/compiler/internal/Keyframes.d.ts.map +0 -1
- package/dist/compiler/internal/Keyframes.js +0 -51
- package/dist/compiler/internal/Keyframes.js.map +0 -1
- package/dist/compiler/internal/Names.d.ts +0 -5
- package/dist/compiler/internal/Names.d.ts.map +0 -1
- package/dist/compiler/internal/Names.js +0 -37
- package/dist/compiler/internal/Names.js.map +0 -1
- package/dist/compiler/internal/NativeBindings.d.ts +0 -72
- package/dist/compiler/internal/NativeBindings.d.ts.map +0 -1
- package/dist/compiler/internal/NativeBindings.js +0 -171
- package/dist/compiler/internal/NativeBindings.js.map +0 -1
- package/dist/compiler/internal/NativeMedia.d.ts +0 -14
- package/dist/compiler/internal/NativeMedia.d.ts.map +0 -1
- package/dist/compiler/internal/NativeMedia.js +0 -287
- package/dist/compiler/internal/NativeMedia.js.map +0 -1
- package/dist/compiler/internal/NativeVars.d.ts +0 -28
- package/dist/compiler/internal/NativeVars.d.ts.map +0 -1
- package/dist/compiler/internal/NativeVars.js +0 -160
- package/dist/compiler/internal/NativeVars.js.map +0 -1
- package/dist/compiler/internal/PackedStyles.d.ts +0 -37
- package/dist/compiler/internal/PackedStyles.d.ts.map +0 -1
- package/dist/compiler/internal/PackedStyles.js +0 -344
- package/dist/compiler/internal/PackedStyles.js.map +0 -1
- package/dist/compiler/internal/Properties.d.ts +0 -5
- package/dist/compiler/internal/Properties.d.ts.map +0 -1
- package/dist/compiler/internal/Properties.js +0 -82
- package/dist/compiler/internal/Properties.js.map +0 -1
- package/dist/compiler/internal/RecipeConditions.d.ts +0 -41
- package/dist/compiler/internal/RecipeConditions.d.ts.map +0 -1
- package/dist/compiler/internal/RecipeConditions.js +0 -82
- package/dist/compiler/internal/RecipeConditions.js.map +0 -1
- package/dist/compiler/internal/RecipePayloads.d.ts +0 -31
- package/dist/compiler/internal/RecipePayloads.d.ts.map +0 -1
- package/dist/compiler/internal/RecipePayloads.js +0 -99
- package/dist/compiler/internal/RecipePayloads.js.map +0 -1
- package/dist/compiler/internal/Recipes.d.ts +0 -32
- package/dist/compiler/internal/Recipes.d.ts.map +0 -1
- package/dist/compiler/internal/Recipes.js +0 -339
- package/dist/compiler/internal/Recipes.js.map +0 -1
- package/dist/compiler/internal/Selectors.d.ts +0 -13
- package/dist/compiler/internal/Selectors.d.ts.map +0 -1
- package/dist/compiler/internal/Selectors.js +0 -280
- package/dist/compiler/internal/Selectors.js.map +0 -1
- package/dist/compiler/internal/Static.d.ts +0 -39
- package/dist/compiler/internal/Static.d.ts.map +0 -1
- package/dist/compiler/internal/Static.js +0 -534
- package/dist/compiler/internal/Static.js.map +0 -1
- package/dist/compiler/internal/Themes.d.ts +0 -190
- package/dist/compiler/internal/Themes.d.ts.map +0 -1
- package/dist/compiler/internal/Themes.js +0 -1577
- package/dist/compiler/internal/Themes.js.map +0 -1
- package/dist/compiler/internal/Vars.d.ts +0 -33
- package/dist/compiler/internal/Vars.d.ts.map +0 -1
- package/dist/compiler/internal/Vars.js +0 -387
- package/dist/compiler/internal/Vars.js.map +0 -1
- package/dist/compiler/internal/ViewTransitions.d.ts +0 -3
- package/dist/compiler/internal/ViewTransitions.d.ts.map +0 -1
- package/dist/compiler/internal/ViewTransitions.js +0 -68
- package/dist/compiler/internal/ViewTransitions.js.map +0 -1
- package/dist/internal/Cascade.d.ts +0 -74
- package/dist/internal/Cascade.d.ts.map +0 -1
- package/dist/internal/Cascade.js +0 -210
- package/dist/internal/Cascade.js.map +0 -1
- package/src/compiler/internal/Applications.ts +0 -610
- package/src/compiler/internal/ColorProfiles.ts +0 -93
- package/src/compiler/internal/CompositionBindings.ts +0 -120
- package/src/compiler/internal/Compositions.ts +0 -678
- package/src/compiler/internal/Configurations.ts +0 -349
- package/src/compiler/internal/Contract.ts +0 -1028
- package/src/compiler/internal/Contributions.ts +0 -1561
- package/src/compiler/internal/Descriptors.ts +0 -242
- package/src/compiler/internal/Dynamic.ts +0 -275
- package/src/compiler/internal/Expression.ts +0 -218
- package/src/compiler/internal/Functions.ts +0 -206
- package/src/compiler/internal/Keyframes.ts +0 -62
- package/src/compiler/internal/Names.ts +0 -48
- package/src/compiler/internal/NativeBindings.ts +0 -235
- package/src/compiler/internal/NativeMedia.ts +0 -342
- package/src/compiler/internal/NativeVars.ts +0 -231
- package/src/compiler/internal/PackedStyles.ts +0 -473
- package/src/compiler/internal/Properties.ts +0 -121
- package/src/compiler/internal/RecipeConditions.ts +0 -139
- package/src/compiler/internal/RecipePayloads.ts +0 -132
- package/src/compiler/internal/Recipes.ts +0 -531
- package/src/compiler/internal/Selectors.ts +0 -353
- package/src/compiler/internal/Static.ts +0 -784
- package/src/compiler/internal/Themes.ts +0 -2398
- package/src/compiler/internal/Vars.ts +0 -516
- package/src/compiler/internal/ViewTransitions.ts +0 -82
package/src/vite/index.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import * as Namespaces from '../compiler/internal/Namespaces.js'
|
|
6
6
|
import * as AtRules from '../compiler/internal/AtRules.js'
|
|
7
7
|
import * as Catalogs from '../compiler/internal/Catalogs.js'
|
|
8
|
+
import * as Results from '../compiler/internal/Results.js'
|
|
8
9
|
import * as Contracts from '../node/internal/Contracts.js'
|
|
9
10
|
import * as Mapping from '@jridgewell/gen-mapping'
|
|
10
11
|
import * as Lightning from 'lightningcss'
|
|
@@ -13,15 +14,16 @@ import { existsSync } from 'node:fs'
|
|
|
13
14
|
import * as Fs from 'node:fs/promises'
|
|
14
15
|
import * as Path from 'node:path'
|
|
15
16
|
import * as Reset from '../node/Reset.js'
|
|
17
|
+
import * as Vlq from '../internal/Vlq.js'
|
|
16
18
|
import { createRequire, isBuiltin } from 'node:module'
|
|
17
19
|
import * as Parser from 'oxc-parser'
|
|
18
20
|
import * as Walker from 'oxc-walker'
|
|
19
21
|
import * as Snapshot from '../node/internal/Snapshot.js'
|
|
20
|
-
import * as Syntax from '../compiler/internal/Syntax.js'
|
|
21
22
|
import * as Scope from '../compiler/internal/Scope.js'
|
|
22
23
|
import type {
|
|
23
24
|
DevEnvironment,
|
|
24
25
|
Environment,
|
|
26
|
+
EnvironmentModuleNode,
|
|
25
27
|
Plugin,
|
|
26
28
|
Rollup,
|
|
27
29
|
UserConfig,
|
|
@@ -61,6 +63,33 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
61
63
|
const reads = new WeakMap<Environment, Map<string, string>>()
|
|
62
64
|
const lookups = new WeakMap<Environment, Lookups>()
|
|
63
65
|
const compilers = new WeakMap<Environment, Graph.create.ReturnType>()
|
|
66
|
+
const projects = new WeakMap<Environment, Project>()
|
|
67
|
+
// The shared stylesheet each environment last loaded, which an edit compares against.
|
|
68
|
+
const servedShared = new WeakMap<Environment, string>()
|
|
69
|
+
// Watcher events advance an environment's generation, which scopes memoized whole-project work.
|
|
70
|
+
const generations = new WeakMap<Environment, number>()
|
|
71
|
+
let devServer: ViteDevServer | undefined
|
|
72
|
+
const watching = new Set<string>()
|
|
73
|
+
// Chokidar matches its ignore patterns on every add, and each compile watches its whole closure.
|
|
74
|
+
const watched = new Set<string>()
|
|
75
|
+
// Each compile reads the catalogs of every contract it saw, which rarely changes text.
|
|
76
|
+
const catalogReads = new Map<string, readonly Catalogs.Catalog[]>()
|
|
77
|
+
const dependencyChains = new Map<string, readonly (readonly unknown[])[]>()
|
|
78
|
+
const edgeMemos = new WeakMap<
|
|
79
|
+
Environment,
|
|
80
|
+
{ files: Map<string, Edges>; key: string }
|
|
81
|
+
>()
|
|
82
|
+
// A development entry's last output, reused until a watcher event or a recompiled graph.
|
|
83
|
+
const outputs = new WeakMap<
|
|
84
|
+
Entry,
|
|
85
|
+
{
|
|
86
|
+
allSources: boolean
|
|
87
|
+
code: string | undefined
|
|
88
|
+
generation: number
|
|
89
|
+
output: Awaited<ReturnType<typeof compileEntry>>
|
|
90
|
+
result: Graph.compile.ReturnType
|
|
91
|
+
}
|
|
92
|
+
>()
|
|
64
93
|
const packages = Contracts.create()
|
|
65
94
|
|
|
66
95
|
function compiler(environment: Environment) {
|
|
@@ -71,6 +100,168 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
71
100
|
}
|
|
72
101
|
return value
|
|
73
102
|
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Watches a compile input from a hook. Vite checks the filesystem each time a file outside its
|
|
106
|
+
* root is watched, and every compile reads the same package files, so development watches
|
|
107
|
+
* each of those once.
|
|
108
|
+
*/
|
|
109
|
+
function watcher(context: { addWatchFile: (id: string) => void }) {
|
|
110
|
+
return (file: string) => {
|
|
111
|
+
if (devServer && !file.startsWith(`${devServer.config.root}/`)) {
|
|
112
|
+
if (watching.has(file)) return
|
|
113
|
+
|
|
114
|
+
watching.add(file)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
context.addWatchFile(file)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* The packed stylesheet dependency chains of a library contract. Every compile reads each
|
|
123
|
+
* contract it reaches, and design system contracts are large, so each text parses once.
|
|
124
|
+
*/
|
|
125
|
+
function stylesheetDependencies(id: string, contract: string) {
|
|
126
|
+
let value = dependencyChains.get(contract)
|
|
127
|
+
if (value) return value
|
|
128
|
+
|
|
129
|
+
const metadata = (() => {
|
|
130
|
+
try {
|
|
131
|
+
return JSON.parse(contract)
|
|
132
|
+
} catch (error) {
|
|
133
|
+
Graph.compile({ modules: {}, contracts: { [id]: contract } })
|
|
134
|
+
throw error
|
|
135
|
+
}
|
|
136
|
+
})()
|
|
137
|
+
|
|
138
|
+
value = Array.isArray(metadata?.stylesheets)
|
|
139
|
+
? (metadata.stylesheets as readonly { dependency?: unknown }[])
|
|
140
|
+
.map((section) => section?.dependency)
|
|
141
|
+
.filter((dependency): dependency is readonly unknown[] =>
|
|
142
|
+
Array.isArray(dependency),
|
|
143
|
+
)
|
|
144
|
+
: []
|
|
145
|
+
// Rebuilt libraries leave their earlier texts behind, so the cache stays bounded.
|
|
146
|
+
if (dependencyChains.size > 256) dependencyChains.clear()
|
|
147
|
+
dependencyChains.set(contract, value)
|
|
148
|
+
return value
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Each module's import edges in an environment, shared by every entry compile that walks the
|
|
153
|
+
* module. Added and removed files and dependency optimization change resolutions, so either
|
|
154
|
+
* starts over.
|
|
155
|
+
*/
|
|
156
|
+
function importEdges(environment: Environment) {
|
|
157
|
+
const metadata =
|
|
158
|
+
environment.mode === 'dev'
|
|
159
|
+
? (environment as DevEnvironment).depsOptimizer?.metadata
|
|
160
|
+
: undefined
|
|
161
|
+
const key = `${metadata?.browserHash}:${Object.keys(metadata?.discovered ?? {}).length}`
|
|
162
|
+
let value = edgeMemos.get(environment)
|
|
163
|
+
|
|
164
|
+
if (value?.key !== key) {
|
|
165
|
+
value = { files: new Map(), key }
|
|
166
|
+
edgeMemos.set(environment, value)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return value.files
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Adds a compile input to the development watcher once. */
|
|
173
|
+
function watch(server: ViteDevServer, file: string) {
|
|
174
|
+
if (watched.has(file)) return
|
|
175
|
+
|
|
176
|
+
watched.add(file)
|
|
177
|
+
server.watcher.add(file)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function read(contract: string) {
|
|
181
|
+
let value = catalogReads.get(contract)
|
|
182
|
+
|
|
183
|
+
if (!value) {
|
|
184
|
+
if (catalogReads.size > 256) catalogReads.clear()
|
|
185
|
+
|
|
186
|
+
value = Catalogs.read(contract)
|
|
187
|
+
catalogReads.set(contract, value)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return value
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function project(environment: Environment) {
|
|
194
|
+
let value = projects.get(environment)
|
|
195
|
+
if (!value) {
|
|
196
|
+
value = {
|
|
197
|
+
compiler: Graph.create(),
|
|
198
|
+
contracts: Object.create(null),
|
|
199
|
+
fallback: Graph.create(),
|
|
200
|
+
imports: Object.create(null),
|
|
201
|
+
modules: Object.create(null),
|
|
202
|
+
outcome: undefined,
|
|
203
|
+
}
|
|
204
|
+
projects.set(environment, value)
|
|
205
|
+
}
|
|
206
|
+
return value
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Development merges every module a transform reaches into one graph per environment, so
|
|
211
|
+
* modules compile once per change instead of once per importer. A module's output depends only
|
|
212
|
+
* on its own import closure, so the merged graph returns the output a closure compile would.
|
|
213
|
+
* While some other module fails, the requested closure compiles alone and reports only its own
|
|
214
|
+
* errors, as before.
|
|
215
|
+
*/
|
|
216
|
+
function compileProject(
|
|
217
|
+
environment: Environment,
|
|
218
|
+
input: Graph.compile.Options,
|
|
219
|
+
id: string,
|
|
220
|
+
) {
|
|
221
|
+
const value = project(environment)
|
|
222
|
+
let changed = value.outcome === undefined
|
|
223
|
+
|
|
224
|
+
for (const [module, source] of Object.entries(input.modules))
|
|
225
|
+
if (value.modules[module] !== source) {
|
|
226
|
+
value.modules[module] = source
|
|
227
|
+
changed = true
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
for (const [module, resolutions] of Object.entries(input.imports ?? {}))
|
|
231
|
+
if (!same(value.imports[module], resolutions)) {
|
|
232
|
+
value.imports[module] = resolutions
|
|
233
|
+
changed = true
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
for (const [module, contract] of Object.entries(input.contracts ?? {}))
|
|
237
|
+
if (value.contracts[module] !== contract) {
|
|
238
|
+
value.contracts[module] = contract
|
|
239
|
+
changed = true
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (changed)
|
|
243
|
+
value.outcome = (() => {
|
|
244
|
+
try {
|
|
245
|
+
return {
|
|
246
|
+
result: value.compiler.compile({
|
|
247
|
+
...input,
|
|
248
|
+
contracts: value.contracts,
|
|
249
|
+
imports: value.imports,
|
|
250
|
+
modules: value.modules,
|
|
251
|
+
}),
|
|
252
|
+
}
|
|
253
|
+
} catch (error) {
|
|
254
|
+
return { error }
|
|
255
|
+
}
|
|
256
|
+
})()
|
|
257
|
+
|
|
258
|
+
const outcome = value.outcome!
|
|
259
|
+
|
|
260
|
+
if ('result' in outcome && Object.hasOwn(outcome.result.modules, id))
|
|
261
|
+
return outcome.result
|
|
262
|
+
|
|
263
|
+
return value.fallback.compile(input)
|
|
264
|
+
}
|
|
74
265
|
const discoveries = new WeakMap<Environment, Promise<Map<string, string>>>()
|
|
75
266
|
const contributionFiles = new WeakMap<Environment, Set<string>>()
|
|
76
267
|
const assets = new Map<string, string>()
|
|
@@ -81,6 +272,8 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
81
272
|
const sourceEntrypoints = new Set<string>()
|
|
82
273
|
const identities = new Map<string, string>()
|
|
83
274
|
const exclusions = new Map<string, boolean>()
|
|
275
|
+
// Every compile checks each import against the source directories.
|
|
276
|
+
const roots = new Map<string, string | undefined>()
|
|
84
277
|
const lazyImports = new WeakMap<Parser.ParseResult, string[]>()
|
|
85
278
|
let directories: readonly string[]
|
|
86
279
|
let filter: (id: unknown) => boolean
|
|
@@ -182,7 +375,9 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
182
375
|
}
|
|
183
376
|
|
|
184
377
|
function sourceRoot(file: string) {
|
|
185
|
-
|
|
378
|
+
if (roots.has(file)) return roots.get(file)
|
|
379
|
+
|
|
380
|
+
const root = directories.find((directory) => {
|
|
186
381
|
const relative = Path.relative(directory, file)
|
|
187
382
|
|
|
188
383
|
return (
|
|
@@ -192,6 +387,9 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
192
387
|
!relative.split(Path.sep).includes('node_modules')
|
|
193
388
|
)
|
|
194
389
|
})
|
|
390
|
+
|
|
391
|
+
roots.set(file, root)
|
|
392
|
+
return root
|
|
195
393
|
}
|
|
196
394
|
|
|
197
395
|
function discover(environment: Environment, host: Host) {
|
|
@@ -383,7 +581,46 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
383
581
|
event: string,
|
|
384
582
|
) {
|
|
385
583
|
reads.delete(environment)
|
|
386
|
-
|
|
584
|
+
generations.set(environment, (generations.get(environment) ?? 0) + 1)
|
|
585
|
+
|
|
586
|
+
const changed = entries(environment).get(file)
|
|
587
|
+
|
|
588
|
+
if (changed) changed.source = undefined
|
|
589
|
+
|
|
590
|
+
// An edit keeps every resolution. Adding or removing a file can change where a
|
|
591
|
+
// specifier resolves, and a contract derives again only when a file it read changes.
|
|
592
|
+
// Editors save through temporary files, which no specifier resolves to.
|
|
593
|
+
const resolvable =
|
|
594
|
+
event !== 'update' &&
|
|
595
|
+
/\.(?:[cm]?[jt]sx?|json|css|svelte|vue)$|(?:^|[/\\])package\.json$/.test(
|
|
596
|
+
file,
|
|
597
|
+
)
|
|
598
|
+
const cache = lookups.get(environment)
|
|
599
|
+
|
|
600
|
+
// Import edges derive from resolutions and module text, which each memo compares.
|
|
601
|
+
if (resolvable) edgeMemos.delete(environment)
|
|
602
|
+
|
|
603
|
+
if (cache) {
|
|
604
|
+
if (resolvable) cache.resolutions.clear()
|
|
605
|
+
|
|
606
|
+
for (const [key, files] of cache.read)
|
|
607
|
+
if (files.has(file)) {
|
|
608
|
+
cache.contracts.delete(key)
|
|
609
|
+
cache.read.delete(key)
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
if (event === 'delete') {
|
|
614
|
+
const value = projects.get(environment)
|
|
615
|
+
const id = sourceId(file)
|
|
616
|
+
|
|
617
|
+
if (value && Object.hasOwn(value.modules, id)) {
|
|
618
|
+
delete value.modules[id]
|
|
619
|
+
delete value.imports[id]
|
|
620
|
+
value.outcome = undefined
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
387
624
|
if (event === 'delete') {
|
|
388
625
|
if (entries(environment).has(file)) {
|
|
389
626
|
let files = removed.get(environment)
|
|
@@ -421,10 +658,66 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
421
658
|
allSources = false,
|
|
422
659
|
everything = false,
|
|
423
660
|
) {
|
|
424
|
-
|
|
425
|
-
|
|
661
|
+
const generation = generations.get(entry.environment) ?? 0
|
|
662
|
+
const memo = outputs.get(entry)
|
|
663
|
+
const current = projects.get(entry.environment)?.outcome
|
|
664
|
+
|
|
665
|
+
// Without a watcher event the closure is unchanged, so a stylesheet load after its
|
|
666
|
+
// transform reuses the output while the merged graph has not recompiled.
|
|
667
|
+
if (
|
|
668
|
+
memo &&
|
|
669
|
+
!everything &&
|
|
670
|
+
memo.allSources === allSources &&
|
|
671
|
+
memo.generation === generation &&
|
|
672
|
+
(code === undefined || code === memo.code) &&
|
|
673
|
+
current &&
|
|
674
|
+
'result' in current &&
|
|
675
|
+
current.result === memo.result
|
|
676
|
+
) {
|
|
677
|
+
for (const file of entry.files) host.watch(file)
|
|
678
|
+
return memo.output
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
const output = await compileEntry(entry, host, code, allSources, everything)
|
|
682
|
+
const outcome = projects.get(entry.environment)?.outcome
|
|
683
|
+
|
|
684
|
+
// A concurrent compile can replace the merged result while this one renders, so only an
|
|
685
|
+
// output of the current result is kept.
|
|
686
|
+
if (
|
|
687
|
+
entry.environment.mode === 'dev' &&
|
|
688
|
+
!everything &&
|
|
689
|
+
outcome &&
|
|
690
|
+
'result' in outcome &&
|
|
691
|
+
outcome.result.modules === output.modules &&
|
|
692
|
+
generation === (generations.get(entry.environment) ?? 0)
|
|
693
|
+
)
|
|
694
|
+
outputs.set(entry, {
|
|
695
|
+
allSources,
|
|
696
|
+
code,
|
|
697
|
+
generation,
|
|
698
|
+
output,
|
|
699
|
+
result: outcome.result,
|
|
700
|
+
})
|
|
701
|
+
|
|
702
|
+
return output
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
async function compileEntry(
|
|
706
|
+
entry: Entry,
|
|
707
|
+
host: Host,
|
|
708
|
+
code?: string,
|
|
709
|
+
allSources = false,
|
|
710
|
+
everything = false,
|
|
711
|
+
) {
|
|
712
|
+
// Every compile visits the discovered project, so modules share lookups. Watcher events
|
|
713
|
+
// drop the resolutions and contracts a change can affect. Full-project compiles look up
|
|
714
|
+
// afresh, since a document must not trail the watcher.
|
|
426
715
|
const cache = (() => {
|
|
427
|
-
const fresh: Lookups = {
|
|
716
|
+
const fresh: Lookups = {
|
|
717
|
+
contracts: new Map(),
|
|
718
|
+
read: new Map(),
|
|
719
|
+
resolutions: new Map(),
|
|
720
|
+
}
|
|
428
721
|
if (everything) return fresh
|
|
429
722
|
|
|
430
723
|
let value = lookups.get(entry.environment)
|
|
@@ -435,6 +728,9 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
435
728
|
return value
|
|
436
729
|
})()
|
|
437
730
|
|
|
731
|
+
// Full-project compiles walk afresh, as they look up afresh.
|
|
732
|
+
const memos = everything ? undefined : importEdges(entry.environment)
|
|
733
|
+
|
|
438
734
|
async function resolve(source: string, importer: string) {
|
|
439
735
|
if (isBuiltin(source)) return { id: source, external: true }
|
|
440
736
|
|
|
@@ -450,16 +746,39 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
450
746
|
if (!resolved) return resolved
|
|
451
747
|
|
|
452
748
|
if (entry.environment.mode === 'dev') {
|
|
453
|
-
const
|
|
454
|
-
|
|
455
|
-
...entry.environment.depsOptimizer?.metadata.discovered,
|
|
456
|
-
}).find((item) => item.file === resolved.id.split('?')[0])
|
|
457
|
-
if (optimized?.src) return { ...resolved, id: optimized.src }
|
|
749
|
+
const source = optimized().get(resolved.id.split('?')[0]!)
|
|
750
|
+
if (source) return { ...resolved, id: source }
|
|
458
751
|
}
|
|
459
752
|
|
|
460
753
|
return { ...resolved, id: normalize(resolved.id) }
|
|
461
754
|
}
|
|
462
755
|
|
|
756
|
+
// Optimized dependency sources by bundled file, indexed once per compile rather than
|
|
757
|
+
// searched for every resolution.
|
|
758
|
+
let indexed: Map<string, string> | undefined
|
|
759
|
+
|
|
760
|
+
function optimized() {
|
|
761
|
+
if (indexed) return indexed
|
|
762
|
+
|
|
763
|
+
indexed = new Map()
|
|
764
|
+
|
|
765
|
+
const environment = entry.environment
|
|
766
|
+
const metadata =
|
|
767
|
+
environment.mode === 'dev'
|
|
768
|
+
? environment.depsOptimizer?.metadata
|
|
769
|
+
: undefined
|
|
770
|
+
|
|
771
|
+
// Discovered entries replace optimized ones of the same name, and the first entry
|
|
772
|
+
// bundling a file wins, as a search of the merged record finds it.
|
|
773
|
+
for (const item of Object.values({
|
|
774
|
+
...metadata?.optimized,
|
|
775
|
+
...metadata?.discovered,
|
|
776
|
+
}))
|
|
777
|
+
if (!indexed.has(item.file)) indexed.set(item.file, item.src ?? '')
|
|
778
|
+
|
|
779
|
+
return indexed
|
|
780
|
+
}
|
|
781
|
+
|
|
463
782
|
const imports: Record<
|
|
464
783
|
string,
|
|
465
784
|
Record<string, string | null>
|
|
@@ -488,7 +807,10 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
488
807
|
resolve: async (specifier, importer) =>
|
|
489
808
|
(await resolve(specifier, importer))?.id.split(/[?#]/)[0],
|
|
490
809
|
})
|
|
491
|
-
.then((contract) =>
|
|
810
|
+
.then((contract) => {
|
|
811
|
+
cache.read.set(key, read)
|
|
812
|
+
return { contract, files: read }
|
|
813
|
+
})
|
|
492
814
|
cache.contracts.set(key, pending)
|
|
493
815
|
pending.catch(() => cache.contracts.delete(key))
|
|
494
816
|
}
|
|
@@ -517,16 +839,43 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
517
839
|
cached = new Map()
|
|
518
840
|
reads.set(entry.environment, cached)
|
|
519
841
|
}
|
|
842
|
+
// Earlier plugins can rewrite a module before this one sees it, as router plugins drop
|
|
843
|
+
// server routes from client route trees. Its importers read the code Vite transformed, so
|
|
844
|
+
// every graph in the environment holds one version of each module.
|
|
520
845
|
// Full-project compilation must not reuse reads from concurrent module transforms.
|
|
521
846
|
const text =
|
|
522
847
|
source ??
|
|
848
|
+
(everything
|
|
849
|
+
? undefined
|
|
850
|
+
: entries(entry.environment).get(file)?.source) ??
|
|
523
851
|
(everything ? undefined : cached.get(file)) ??
|
|
524
852
|
(await snapshot(entry.environment).read(file))
|
|
525
853
|
if (!everything) cached.set(file, text)
|
|
526
854
|
|
|
527
855
|
modules[id] = text
|
|
528
856
|
|
|
857
|
+
const memo = memos?.get(file)
|
|
858
|
+
|
|
859
|
+
// Replaying the edges keeps the order in which modules and contracts join the compile.
|
|
860
|
+
if (memo?.text === text) {
|
|
861
|
+
imports[id] = { ...memo.resolutions }
|
|
862
|
+
|
|
863
|
+
for (const step of memo.steps) {
|
|
864
|
+
if (typeof step === 'string') {
|
|
865
|
+
await visit(step)
|
|
866
|
+
continue
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
const contract = await readContract(step.physical)
|
|
870
|
+
if (contract !== undefined) contracts[step.id] = contract
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
lazy[id] = memo.lazy
|
|
874
|
+
return
|
|
875
|
+
}
|
|
876
|
+
|
|
529
877
|
const resolutions: Record<string, string | null> = Object.create(null)
|
|
878
|
+
const steps: Edges['steps'][number][] = []
|
|
530
879
|
|
|
531
880
|
imports[id] = resolutions
|
|
532
881
|
|
|
@@ -597,6 +946,7 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
597
946
|
) {
|
|
598
947
|
sourceEntrypoints.add(normalize(resolved.id))
|
|
599
948
|
resolutions[specifier] = sourceId(resolved.id)
|
|
949
|
+
steps.push(resolved.id)
|
|
600
950
|
await visit(resolved.id)
|
|
601
951
|
continue
|
|
602
952
|
}
|
|
@@ -609,10 +959,12 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
609
959
|
|
|
610
960
|
if (resolved.external || !eligible(resolved.id)) {
|
|
611
961
|
const physical = resolved.id.split('?')[0]!.split('#')[0]!
|
|
612
|
-
const
|
|
962
|
+
const readable =
|
|
613
963
|
Path.isAbsolute(physical) && /\.[cm]?[jt]sx?$/.test(physical)
|
|
614
|
-
|
|
615
|
-
|
|
964
|
+
|
|
965
|
+
if (readable) steps.push({ id: resolved.id, physical })
|
|
966
|
+
|
|
967
|
+
const contract = readable ? await readContract(physical) : undefined
|
|
616
968
|
|
|
617
969
|
if (contract !== undefined) {
|
|
618
970
|
contracts[resolved.id] = contract
|
|
@@ -625,6 +977,7 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
625
977
|
}
|
|
626
978
|
|
|
627
979
|
resolutions[specifier] = sourceId(resolved.id)
|
|
980
|
+
steps.push(resolved.id)
|
|
628
981
|
await visit(resolved.id)
|
|
629
982
|
}
|
|
630
983
|
|
|
@@ -658,6 +1011,7 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
658
1011
|
}
|
|
659
1012
|
|
|
660
1013
|
lazy[id] = targets
|
|
1014
|
+
memos?.set(file, { lazy: targets, resolutions, steps, text })
|
|
661
1015
|
}
|
|
662
1016
|
|
|
663
1017
|
await visit(entry.file, code)
|
|
@@ -774,22 +1128,10 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
774
1128
|
|
|
775
1129
|
loaded.add(id)
|
|
776
1130
|
|
|
777
|
-
const
|
|
778
|
-
try {
|
|
779
|
-
return JSON.parse(contracts[id]!)
|
|
780
|
-
} catch (error) {
|
|
781
|
-
Graph.compile({ modules: {}, contracts: { [id]: contracts[id]! } })
|
|
782
|
-
throw error
|
|
783
|
-
}
|
|
784
|
-
})()
|
|
785
|
-
if (!Array.isArray(metadata?.stylesheets)) return
|
|
786
|
-
|
|
787
|
-
for (const section of metadata.stylesheets) {
|
|
788
|
-
if (!Array.isArray(section?.dependency)) continue
|
|
789
|
-
|
|
1131
|
+
for (const dependency of stylesheetDependencies(id, contracts[id]!)) {
|
|
790
1132
|
let owner = id
|
|
791
1133
|
|
|
792
|
-
for (const specifier of
|
|
1134
|
+
for (const specifier of dependency) {
|
|
793
1135
|
if (
|
|
794
1136
|
typeof specifier !== 'string' ||
|
|
795
1137
|
!specifier ||
|
|
@@ -837,9 +1179,9 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
837
1179
|
]),
|
|
838
1180
|
)
|
|
839
1181
|
|
|
840
|
-
const
|
|
1182
|
+
const input: Graph.compile.Options = {
|
|
1183
|
+
[Results.lean]: true,
|
|
841
1184
|
[ThemeRules.shared]: native ? undefined : 'all',
|
|
842
|
-
[Syntax.cache]: snapshot(entry.environment).programs(modules),
|
|
843
1185
|
compiler: options.compiler,
|
|
844
1186
|
reset: options.reset ? Reset.read() : undefined,
|
|
845
1187
|
native,
|
|
@@ -847,7 +1189,11 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
847
1189
|
development: entry.environment.mode !== 'build',
|
|
848
1190
|
imports,
|
|
849
1191
|
modules,
|
|
850
|
-
}
|
|
1192
|
+
}
|
|
1193
|
+
const result =
|
|
1194
|
+
entry.environment.mode === 'dev' && !native && !everything
|
|
1195
|
+
? compileProject(entry.environment, input, sourceId(entry.file))
|
|
1196
|
+
: entry.compiler.compile(input)
|
|
851
1197
|
|
|
852
1198
|
if (native) {
|
|
853
1199
|
const output = result.modules[sourceId(entry.file)]!
|
|
@@ -861,6 +1207,7 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
861
1207
|
configurations: new Map<string, Catalogs.Catalog>(),
|
|
862
1208
|
connections: staged,
|
|
863
1209
|
css: '',
|
|
1210
|
+
graphSharedCss: '',
|
|
864
1211
|
sharedCss: '',
|
|
865
1212
|
cssMap: map,
|
|
866
1213
|
sharedCssMap: map,
|
|
@@ -896,166 +1243,181 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
896
1243
|
|
|
897
1244
|
const configurations = new Map<string, Catalogs.Catalog>()
|
|
898
1245
|
for (const [id, contract] of Object.entries(compiled))
|
|
899
|
-
for (const configuration of
|
|
1246
|
+
for (const configuration of read(contract)) {
|
|
900
1247
|
const key = `${id}\0${configuration.identity}`
|
|
901
1248
|
catalogs.set(key, configuration)
|
|
902
1249
|
configurations.set(key, configuration)
|
|
903
1250
|
}
|
|
904
1251
|
|
|
905
|
-
|
|
1252
|
+
// Development renders the shared stylesheet for its own request only; transforms and module
|
|
1253
|
+
// stylesheets read module outputs.
|
|
1254
|
+
async function renderShared() {
|
|
1255
|
+
const owners = new Map<string, string>()
|
|
906
1256
|
|
|
907
|
-
|
|
908
|
-
|
|
1257
|
+
for (const id of new Set(Object.values(result.sharedAssetOwners ?? {}))) {
|
|
1258
|
+
if (!Path.isAbsolute(id)) continue
|
|
909
1259
|
|
|
910
|
-
|
|
1260
|
+
let directory = Path.dirname(id.split(/[?#]/)[0]!)
|
|
911
1261
|
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
1262
|
+
for (;;) {
|
|
1263
|
+
try {
|
|
1264
|
+
await Fs.access(Path.join(directory, 'package.json'))
|
|
1265
|
+
owners.set(id, await Fs.realpath(directory))
|
|
1266
|
+
break
|
|
1267
|
+
} catch (error) {
|
|
1268
|
+
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error
|
|
1269
|
+
}
|
|
920
1270
|
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
1271
|
+
const parent = Path.dirname(directory)
|
|
1272
|
+
if (parent === directory)
|
|
1273
|
+
throw new Error('Packed assets require an owning package.json.')
|
|
924
1274
|
|
|
925
|
-
|
|
1275
|
+
directory = parent
|
|
1276
|
+
}
|
|
926
1277
|
}
|
|
927
|
-
}
|
|
928
1278
|
|
|
929
|
-
|
|
1279
|
+
const assetUrls = new Map<string, string>()
|
|
930
1280
|
|
|
931
|
-
|
|
932
|
-
|
|
1281
|
+
for (const [key, target] of Object.entries(result.sharedAssets ?? {})) {
|
|
1282
|
+
const raw = target.split(/[?#]/)[0]!
|
|
933
1283
|
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
1284
|
+
const identity = result.sharedAssetOwners?.[key]
|
|
1285
|
+
const path =
|
|
1286
|
+
sourceFile(decodeURIComponent(raw)) ??
|
|
1287
|
+
(Path.isAbsolute(raw) ? decodeURIComponent(raw) : undefined)
|
|
1288
|
+
if (!path) throw new Error('Asset path escapes the Vite graph.')
|
|
939
1289
|
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
1290
|
+
const asset = await (async () => {
|
|
1291
|
+
try {
|
|
1292
|
+
return { file: await Fs.realpath(path), package: false }
|
|
1293
|
+
} catch (error) {
|
|
1294
|
+
if (
|
|
1295
|
+
(error as NodeJS.ErrnoException).code !== 'ENOENT' ||
|
|
1296
|
+
!identity ||
|
|
1297
|
+
!sourceFile(identity)
|
|
1298
|
+
)
|
|
1299
|
+
throw error
|
|
1300
|
+
|
|
1301
|
+
const importer = sourceFile(identity)!
|
|
1302
|
+
const specifier = Path.relative(Path.dirname(importer), path)
|
|
1303
|
+
.split(Path.sep)
|
|
1304
|
+
.join('/')
|
|
1305
|
+
if (specifier.startsWith('.') || Path.isAbsolute(specifier))
|
|
1306
|
+
throw error
|
|
1307
|
+
const resolved = await host.resolve(specifier, importer)
|
|
1308
|
+
if (!resolved || resolved.external) throw error
|
|
1309
|
+
return {
|
|
1310
|
+
file: await Fs.realpath(resolved.id.split('?')[0]!),
|
|
1311
|
+
package: true,
|
|
1312
|
+
}
|
|
962
1313
|
}
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
const file = asset.file
|
|
966
|
-
|
|
967
|
-
const owner =
|
|
968
|
-
identity && sourceFile(identity)
|
|
969
|
-
? sourceRoot(sourceFile(identity)!)
|
|
970
|
-
: identity
|
|
971
|
-
? owners.get(identity)
|
|
972
|
-
: undefined
|
|
973
|
-
|
|
974
|
-
const relative = owner ? Path.relative(owner, file) : '..'
|
|
975
|
-
if (
|
|
976
|
-
!asset.package &&
|
|
977
|
-
(relative === '..' ||
|
|
978
|
-
relative.startsWith(`..${Path.sep}`) ||
|
|
979
|
-
Path.isAbsolute(relative))
|
|
980
|
-
)
|
|
981
|
-
throw new Error('Asset path escapes its owning package.')
|
|
1314
|
+
})()
|
|
1315
|
+
const file = asset.file
|
|
982
1316
|
|
|
983
|
-
|
|
984
|
-
|
|
1317
|
+
const owner =
|
|
1318
|
+
identity && sourceFile(identity)
|
|
1319
|
+
? sourceRoot(sourceFile(identity)!)
|
|
1320
|
+
: identity
|
|
1321
|
+
? owners.get(identity)
|
|
1322
|
+
: undefined
|
|
985
1323
|
|
|
986
|
-
|
|
1324
|
+
const relative = owner ? Path.relative(owner, file) : '..'
|
|
1325
|
+
if (
|
|
1326
|
+
!asset.package &&
|
|
1327
|
+
(relative === '..' ||
|
|
1328
|
+
relative.startsWith(`..${Path.sep}`) ||
|
|
1329
|
+
Path.isAbsolute(relative))
|
|
1330
|
+
)
|
|
1331
|
+
throw new Error('Asset path escapes its owning package.')
|
|
987
1332
|
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
url = await host.asset(file)
|
|
1333
|
+
host.watch(file)
|
|
1334
|
+
files.add(file)
|
|
991
1335
|
|
|
992
|
-
|
|
1336
|
+
let url: string
|
|
1337
|
+
|
|
1338
|
+
if (asset.package || /[?#%]/.test(file)) {
|
|
1339
|
+
if (entry.environment.mode === 'build') {
|
|
1340
|
+
url = await host.asset(file)
|
|
1341
|
+
|
|
1342
|
+
if (target.slice(raw.length))
|
|
1343
|
+
url += `$_${target.slice(raw.length)}__`
|
|
1344
|
+
} else {
|
|
1345
|
+
url = `/@zyzz/asset/${Crypto.hash('sha256', file)}/${encodeURIComponent(Path.basename(file))}`
|
|
1346
|
+
assets.set(url, file)
|
|
1347
|
+
url += target.slice(raw.length)
|
|
1348
|
+
}
|
|
993
1349
|
} else {
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
1350
|
+
// Vite reads `/@fs/` URLs with `decodeURI`, which keeps reserved escapes
|
|
1351
|
+
// such as `%40` in scoped package paths, so those characters stay raw.
|
|
1352
|
+
const pathname = encodeURI(file.replaceAll('\\', '/'))
|
|
1353
|
+
|
|
1354
|
+
url = `/@fs/${pathname}${target.slice(raw.length)}`
|
|
997
1355
|
}
|
|
998
|
-
} else {
|
|
999
|
-
// Vite reads `/@fs/` URLs with `decodeURI`, which keeps reserved escapes
|
|
1000
|
-
// such as `%40` in scoped package paths, so those characters stay raw.
|
|
1001
|
-
const pathname = encodeURI(file.replaceAll('\\', '/'))
|
|
1002
1356
|
|
|
1003
|
-
|
|
1357
|
+
assetUrls.set(target, url)
|
|
1004
1358
|
}
|
|
1005
1359
|
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1360
|
+
const shared = !Object.keys(result.sharedAssets ?? {}).length
|
|
1361
|
+
? {
|
|
1362
|
+
code: new TextEncoder().encode(result.sharedCss ?? ''),
|
|
1363
|
+
map: new TextEncoder().encode(
|
|
1364
|
+
JSON.stringify(
|
|
1365
|
+
result.sharedCssMap ??
|
|
1366
|
+
Mapping.toEncodedMap(new Mapping.GenMapping()),
|
|
1367
|
+
),
|
|
1368
|
+
),
|
|
1369
|
+
}
|
|
1370
|
+
: AtRules.transform({
|
|
1371
|
+
filename: 'zyzz.shared.css',
|
|
1372
|
+
code: new TextEncoder().encode(result.sharedCss ?? ''),
|
|
1373
|
+
sourceMap: true,
|
|
1374
|
+
inputSourceMap: JSON.stringify(
|
|
1014
1375
|
result.sharedCssMap ??
|
|
1015
1376
|
Mapping.toEncodedMap(new Mapping.GenMapping()),
|
|
1016
1377
|
),
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
const target = result.sharedAssets?.[rule.value.url]
|
|
1031
|
-
if (target)
|
|
1032
|
-
return AtRules.relocateImport(
|
|
1033
|
-
rule.value,
|
|
1034
|
-
assetUrls.get(target)!,
|
|
1035
|
-
)
|
|
1036
|
-
},
|
|
1037
|
-
Url(url) {
|
|
1038
|
-
const target = result.sharedAssets?.[url.url]
|
|
1039
|
-
if (!target) return
|
|
1378
|
+
visitor: {
|
|
1379
|
+
Rule(rule) {
|
|
1380
|
+
if (rule.type !== 'import') return
|
|
1381
|
+
const target = result.sharedAssets?.[rule.value.url]
|
|
1382
|
+
if (target)
|
|
1383
|
+
return AtRules.relocateImport(
|
|
1384
|
+
rule.value,
|
|
1385
|
+
assetUrls.get(target)!,
|
|
1386
|
+
)
|
|
1387
|
+
},
|
|
1388
|
+
Url(url) {
|
|
1389
|
+
const target = result.sharedAssets?.[url.url]
|
|
1390
|
+
if (!target) return
|
|
1040
1391
|
|
|
1041
|
-
|
|
1392
|
+
return { ...url, url: assetUrls.get(target)! }
|
|
1393
|
+
},
|
|
1042
1394
|
},
|
|
1043
|
-
}
|
|
1044
|
-
})
|
|
1395
|
+
})
|
|
1045
1396
|
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1397
|
+
const sharedMap = JSON.parse(
|
|
1398
|
+
new TextDecoder().decode(shared.map!),
|
|
1399
|
+
) as Mapping.EncodedSourceMap
|
|
1049
1400
|
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1401
|
+
sharedMap.sources = sharedMap.sources.map((source) =>
|
|
1402
|
+
source === null ? null : (sourceFile(source) ?? source),
|
|
1403
|
+
)
|
|
1404
|
+
|
|
1405
|
+
return { css: new TextDecoder().decode(shared.code), map: sharedMap }
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
const sharedOutput =
|
|
1409
|
+
entry.environment.mode !== 'dev' || allSources
|
|
1410
|
+
? await renderShared()
|
|
1411
|
+
: {
|
|
1412
|
+
css: '',
|
|
1413
|
+
map: Mapping.toEncodedMap(new Mapping.GenMapping()),
|
|
1414
|
+
}
|
|
1053
1415
|
|
|
1054
1416
|
const output = result.modules[sourceId(entry.file)]!
|
|
1055
1417
|
|
|
1056
1418
|
entry.files = files
|
|
1057
1419
|
|
|
1058
|
-
|
|
1420
|
+
const value = {
|
|
1059
1421
|
imports,
|
|
1060
1422
|
modules: result.modules,
|
|
1061
1423
|
code: output.code,
|
|
@@ -1063,8 +1425,10 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
1063
1425
|
configurations,
|
|
1064
1426
|
connections: staged,
|
|
1065
1427
|
css: output.css,
|
|
1066
|
-
|
|
1067
|
-
|
|
1428
|
+
// Edits compare the graph's stylesheet, which precedes asset relocation.
|
|
1429
|
+
graphSharedCss: result.sharedCss ?? '',
|
|
1430
|
+
sharedCss: sharedOutput.css,
|
|
1431
|
+
sharedCssMap: sharedOutput.map,
|
|
1068
1432
|
cssMap: {
|
|
1069
1433
|
...output.cssMap,
|
|
1070
1434
|
sources: output.cssMap.sources.map((source) =>
|
|
@@ -1078,17 +1442,18 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
1078
1442
|
),
|
|
1079
1443
|
},
|
|
1080
1444
|
}
|
|
1445
|
+
|
|
1446
|
+
return value
|
|
1081
1447
|
}
|
|
1082
1448
|
|
|
1083
1449
|
/** Compiles every discovered source once so index.html can inline each configuration's script before modules load. */
|
|
1084
1450
|
async function initializations(server: ViteDevServer) {
|
|
1085
1451
|
const environment = server.environments.client
|
|
1086
1452
|
reads.delete(environment)
|
|
1087
|
-
lookups.delete(environment)
|
|
1088
1453
|
const host: Host = {
|
|
1089
1454
|
asset: async (file) => file,
|
|
1090
1455
|
resolve: resolver(environment),
|
|
1091
|
-
watch: (file) => server
|
|
1456
|
+
watch: (file) => watch(server, file),
|
|
1092
1457
|
}
|
|
1093
1458
|
const sources = await discover(environment, host)
|
|
1094
1459
|
let entry = initializers.get(environment)
|
|
@@ -1098,8 +1463,9 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
1098
1463
|
const file = sources.keys().next().value
|
|
1099
1464
|
if (file === undefined) return { catalogs: new Map(), edges: new Map() }
|
|
1100
1465
|
|
|
1466
|
+
// Whole-project compiles keep their own cache beside the transforms' merged graph.
|
|
1101
1467
|
entry = {
|
|
1102
|
-
compiler:
|
|
1468
|
+
compiler: initializers.get(environment)?.compiler ?? Graph.create(),
|
|
1103
1469
|
environment,
|
|
1104
1470
|
file,
|
|
1105
1471
|
files: new Set([file]),
|
|
@@ -1275,8 +1641,153 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
1275
1641
|
.map(([, configuration]) => configuration)
|
|
1276
1642
|
}
|
|
1277
1643
|
|
|
1644
|
+
/**
|
|
1645
|
+
* The modules an edit invalidates: those whose code or stylesheet differs from what Vite served.
|
|
1646
|
+
* A concurrent compile can already hold the edit, so outputs are not compared with each other.
|
|
1647
|
+
*/
|
|
1648
|
+
async function changes(
|
|
1649
|
+
environment: DevEnvironment,
|
|
1650
|
+
after: Graph.compile.ReturnType,
|
|
1651
|
+
served: Served,
|
|
1652
|
+
modules: readonly EnvironmentModuleNode[],
|
|
1653
|
+
timestamp: number,
|
|
1654
|
+
edited: string,
|
|
1655
|
+
deleted: string | undefined,
|
|
1656
|
+
) {
|
|
1657
|
+
// Portable build adapters share this module and do not require Vite.
|
|
1658
|
+
const { transformWithOxc } = await import('vite')
|
|
1659
|
+
const code = new Set<string>()
|
|
1660
|
+
const css = new Set<string>()
|
|
1661
|
+
|
|
1662
|
+
for (const [
|
|
1663
|
+
entry,
|
|
1664
|
+
{ code: current, css: stylesheet, imports },
|
|
1665
|
+
] of served.entries) {
|
|
1666
|
+
const id = sourceId(entry.file)
|
|
1667
|
+
const next = after.modules[id]
|
|
1668
|
+
|
|
1669
|
+
if (!next) continue
|
|
1670
|
+
|
|
1671
|
+
// A stylesheet Vite never loaded has no module to invalidate.
|
|
1672
|
+
if (stylesheet !== undefined && stylesheet !== next.css)
|
|
1673
|
+
css.add(entry.file)
|
|
1674
|
+
|
|
1675
|
+
// A stylesheet request can compile a module whose code was never transformed here.
|
|
1676
|
+
if (current === undefined) continue
|
|
1677
|
+
|
|
1678
|
+
if (imports !== JSON.stringify(projects.get(environment)?.imports[id])) {
|
|
1679
|
+
code.add(entry.file)
|
|
1680
|
+
continue
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
if (current === next.code) continue
|
|
1684
|
+
|
|
1685
|
+
// Generated type assertions can change without changing runtime bindings.
|
|
1686
|
+
const [left, right] = await Promise.all([
|
|
1687
|
+
transformWithOxc(current, entry.file, { sourcemap: false }),
|
|
1688
|
+
transformWithOxc(next.code, entry.file, { sourcemap: false }),
|
|
1689
|
+
])
|
|
1690
|
+
|
|
1691
|
+
if (left.code !== right.code) code.add(entry.file)
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
const stylesheets = new Set<string>()
|
|
1695
|
+
|
|
1696
|
+
if (deleted !== undefined) stylesheets.add(cssId(deleted))
|
|
1697
|
+
if (
|
|
1698
|
+
served.shared !== undefined &&
|
|
1699
|
+
served.shared !== (after.sharedCss ?? '')
|
|
1700
|
+
)
|
|
1701
|
+
stylesheets.add(sharedId)
|
|
1702
|
+
for (const file of css) stylesheets.add(cssId(file))
|
|
1703
|
+
|
|
1704
|
+
// Compiled modules the edit left unchanged skip Vite's own update; other modules, such as
|
|
1705
|
+
// plain JavaScript or assets, keep it.
|
|
1706
|
+
const affected = new Set(
|
|
1707
|
+
modules.filter((module) => {
|
|
1708
|
+
if (!module.id) return module.type !== 'asset'
|
|
1709
|
+
|
|
1710
|
+
const file = normalize(module.id)
|
|
1711
|
+
|
|
1712
|
+
return Object.hasOwn(after.modules, sourceId(file))
|
|
1713
|
+
? code.has(file)
|
|
1714
|
+
: true
|
|
1715
|
+
}),
|
|
1716
|
+
)
|
|
1717
|
+
|
|
1718
|
+
for (const file of code) {
|
|
1719
|
+
const module = environment.moduleGraph.getModuleById(file)
|
|
1720
|
+
if (!module) continue
|
|
1721
|
+
|
|
1722
|
+
environment.moduleGraph.invalidateModule(
|
|
1723
|
+
module,
|
|
1724
|
+
new Set(),
|
|
1725
|
+
timestamp,
|
|
1726
|
+
true,
|
|
1727
|
+
)
|
|
1728
|
+
affected.add(module)
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
const sheets: EnvironmentModuleNode[] = []
|
|
1732
|
+
|
|
1733
|
+
// The edited module's stylesheet updates first, ahead of stylesheets other modules share.
|
|
1734
|
+
for (const id of [...stylesheets].sort(
|
|
1735
|
+
(left, right) =>
|
|
1736
|
+
Number(right === cssId(edited)) - Number(left === cssId(edited)),
|
|
1737
|
+
)) {
|
|
1738
|
+
const module = environment.moduleGraph.getModuleById(id)
|
|
1739
|
+
if (!module) continue
|
|
1740
|
+
|
|
1741
|
+
environment.moduleGraph.invalidateModule(
|
|
1742
|
+
module,
|
|
1743
|
+
new Set(),
|
|
1744
|
+
timestamp,
|
|
1745
|
+
true,
|
|
1746
|
+
)
|
|
1747
|
+
sheets.push(module)
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
// A stylesheet accepts its own updates and only its module imports it, so it is never within
|
|
1751
|
+
// an import cycle. Vite proves that by walking every importer of the importing module, which
|
|
1752
|
+
// costs each stylesheet of a shared edit the whole graph, so stylesheet-only edits send the
|
|
1753
|
+
// same update directly. Edits that also change code keep Vite's single batched update.
|
|
1754
|
+
if (
|
|
1755
|
+
!affected.size &&
|
|
1756
|
+
sheets.length &&
|
|
1757
|
+
sheets.every((module) => module.isSelfAccepting)
|
|
1758
|
+
) {
|
|
1759
|
+
const updates = sheets.map((module) => {
|
|
1760
|
+
const path =
|
|
1761
|
+
module.url.startsWith('/') || module.url.startsWith('.')
|
|
1762
|
+
? module.url
|
|
1763
|
+
: `/@id/${module.url.replace('\0', '__x00__')}`
|
|
1764
|
+
|
|
1765
|
+
return {
|
|
1766
|
+
acceptedPath: path,
|
|
1767
|
+
explicitImportRequired: false,
|
|
1768
|
+
isWithinCircularImport: false,
|
|
1769
|
+
path,
|
|
1770
|
+
timestamp,
|
|
1771
|
+
type: 'js-update' as const,
|
|
1772
|
+
}
|
|
1773
|
+
})
|
|
1774
|
+
|
|
1775
|
+
environment.logger.info(
|
|
1776
|
+
`hmr update ${updates.map((update) => update.path).join(', ')}`,
|
|
1777
|
+
{ timestamp: true },
|
|
1778
|
+
)
|
|
1779
|
+
environment.hot.send({ type: 'update', updates })
|
|
1780
|
+
return []
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
for (const module of sheets) affected.add(module)
|
|
1784
|
+
|
|
1785
|
+
return [...affected]
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1278
1788
|
return {
|
|
1279
1789
|
buildStart() {
|
|
1790
|
+
edgeMemos.delete(this.environment)
|
|
1280
1791
|
reads.delete(this.environment)
|
|
1281
1792
|
lookups.delete(this.environment)
|
|
1282
1793
|
},
|
|
@@ -1344,6 +1855,8 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
1344
1855
|
},
|
|
1345
1856
|
},
|
|
1346
1857
|
configureServer(server) {
|
|
1858
|
+
devServer = server
|
|
1859
|
+
|
|
1347
1860
|
// Directory watches belong to the server, not module import dependencies.
|
|
1348
1861
|
server.watcher.add(directories.slice(1))
|
|
1349
1862
|
|
|
@@ -1386,6 +1899,7 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
1386
1899
|
|
|
1387
1900
|
filter = createFilter(null, patterns, { resolve: root })
|
|
1388
1901
|
exclusions.clear()
|
|
1902
|
+
roots.clear()
|
|
1389
1903
|
|
|
1390
1904
|
if (native) return
|
|
1391
1905
|
const lightning = (config.css.lightningcss ??= {})
|
|
@@ -1478,10 +1992,65 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
1478
1992
|
},
|
|
1479
1993
|
},
|
|
1480
1994
|
async watchChange(file, change) {
|
|
1995
|
+
// A removed file leaves the watcher, so a later compile that reads it again adds it again.
|
|
1996
|
+
if (change.event === 'delete') watched.delete(file)
|
|
1997
|
+
|
|
1481
1998
|
await updateDiscovery(this.environment, file, change.event)
|
|
1482
1999
|
},
|
|
1483
2000
|
async hotUpdate({ file, modules, timestamp, type, server }) {
|
|
1484
2001
|
const state = entries(this.environment)
|
|
2002
|
+
const before = projects.get(this.environment)?.outcome
|
|
2003
|
+
// Concurrent transforms update entries while this compiles, so the served output is read now.
|
|
2004
|
+
const served: Served = {
|
|
2005
|
+
entries: new Map(
|
|
2006
|
+
[...state.values()].map((entry) => [
|
|
2007
|
+
entry,
|
|
2008
|
+
{ code: entry.code, css: entry.css, imports: entry.imports },
|
|
2009
|
+
]),
|
|
2010
|
+
),
|
|
2011
|
+
shared: servedShared.get(this.environment),
|
|
2012
|
+
}
|
|
2013
|
+
|
|
2014
|
+
// The merged development graph compares module outputs before and after the edit, so
|
|
2015
|
+
// only modules whose code or stylesheet changed update.
|
|
2016
|
+
if (!native && before && 'result' in before) {
|
|
2017
|
+
await updateDiscovery(this.environment, file, type)
|
|
2018
|
+
|
|
2019
|
+
// The edited module's own entry, or one whose closure reads it, merges its new source.
|
|
2020
|
+
// A contribution source no module imports joins every closure.
|
|
2021
|
+
const first =
|
|
2022
|
+
state.get(file) ??
|
|
2023
|
+
[...state.values()].find((entry) => entry.files.has(file)) ??
|
|
2024
|
+
(eager(file, this.environment)
|
|
2025
|
+
? state.values().next().value
|
|
2026
|
+
: undefined)
|
|
2027
|
+
if (!first && !removed.get(this.environment)?.has(file)) return modules
|
|
2028
|
+
|
|
2029
|
+
const compiled =
|
|
2030
|
+
first && type === 'update' && eligible(file)
|
|
2031
|
+
? await compile(first, {
|
|
2032
|
+
resolve: resolver(this.environment),
|
|
2033
|
+
watch: (file) => watch(server, file),
|
|
2034
|
+
asset: async (file) => file,
|
|
2035
|
+
}).then(
|
|
2036
|
+
() => true,
|
|
2037
|
+
() => false,
|
|
2038
|
+
)
|
|
2039
|
+
: false
|
|
2040
|
+
const after = projects.get(this.environment)?.outcome
|
|
2041
|
+
|
|
2042
|
+
if (compiled && after && 'result' in after)
|
|
2043
|
+
return await changes(
|
|
2044
|
+
this.environment,
|
|
2045
|
+
after.result,
|
|
2046
|
+
served,
|
|
2047
|
+
modules,
|
|
2048
|
+
timestamp,
|
|
2049
|
+
file,
|
|
2050
|
+
type === 'delete' ? file : undefined,
|
|
2051
|
+
)
|
|
2052
|
+
}
|
|
2053
|
+
|
|
1485
2054
|
const previous = [...state.values()].map((entry) => ({
|
|
1486
2055
|
code: entry.code,
|
|
1487
2056
|
file: entry.file,
|
|
@@ -1501,9 +2070,7 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
1501
2070
|
first,
|
|
1502
2071
|
{
|
|
1503
2072
|
resolve: resolver(this.environment),
|
|
1504
|
-
watch: (file) =>
|
|
1505
|
-
server.watcher.add(file)
|
|
1506
|
-
},
|
|
2073
|
+
watch: (file) => watch(server, file),
|
|
1507
2074
|
asset: async (file) => file,
|
|
1508
2075
|
},
|
|
1509
2076
|
undefined,
|
|
@@ -1583,12 +2150,39 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
1583
2150
|
id === sharedId
|
|
1584
2151
|
? undefined
|
|
1585
2152
|
: decodeURIComponent(id.slice(prefix.length, -4))
|
|
1586
|
-
|
|
2153
|
+
let entry =
|
|
1587
2154
|
file === undefined
|
|
1588
2155
|
? entries(this.environment).values().next().value
|
|
1589
2156
|
: entries(this.environment).get(file)
|
|
1590
2157
|
// A pruned JavaScript module can leave its stylesheet loaded in the browser.
|
|
1591
2158
|
if (!entry && file && removed.get(this.environment)?.has(file)) return ''
|
|
2159
|
+
|
|
2160
|
+
// Server rendering links the stylesheets of modules this environment has not transformed
|
|
2161
|
+
// yet, so a development request compiles the module where the browser loads it.
|
|
2162
|
+
if (!entry && this.environment.mode === 'dev') {
|
|
2163
|
+
const anchor =
|
|
2164
|
+
file ??
|
|
2165
|
+
(
|
|
2166
|
+
await discover(this.environment, {
|
|
2167
|
+
asset: async (file) => file,
|
|
2168
|
+
resolve: resolver(this.environment),
|
|
2169
|
+
watch: watcher(this),
|
|
2170
|
+
})
|
|
2171
|
+
)
|
|
2172
|
+
.keys()
|
|
2173
|
+
.next().value
|
|
2174
|
+
|
|
2175
|
+
if (anchor !== undefined && eligible(anchor)) {
|
|
2176
|
+
entry = {
|
|
2177
|
+
compiler: compiler(this.environment),
|
|
2178
|
+
environment: this.environment,
|
|
2179
|
+
file: anchor,
|
|
2180
|
+
files: new Set([anchor]),
|
|
2181
|
+
}
|
|
2182
|
+
entries(this.environment).set(anchor, entry)
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2185
|
+
|
|
1592
2186
|
if (!entry) throw new Error(`Unknown Zyzz stylesheet: ${file}`)
|
|
1593
2187
|
|
|
1594
2188
|
const output = await compile(
|
|
@@ -1598,7 +2192,7 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
1598
2192
|
this.environment.mode === 'dev'
|
|
1599
2193
|
? resolver(this.environment)
|
|
1600
2194
|
: (source, importer) => this.resolve(source, importer),
|
|
1601
|
-
watch: (
|
|
2195
|
+
watch: watcher(this),
|
|
1602
2196
|
asset: async (file) => {
|
|
1603
2197
|
const reference = this.emitFile({
|
|
1604
2198
|
type: 'asset',
|
|
@@ -1626,12 +2220,19 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
1626
2220
|
'-zyzz-fpv-000000000',
|
|
1627
2221
|
)
|
|
1628
2222
|
: css
|
|
1629
|
-
if (id === sharedId)
|
|
2223
|
+
if (id === sharedId) {
|
|
2224
|
+
servedShared.set(this.environment, output.graphSharedCss)
|
|
1630
2225
|
return {
|
|
1631
2226
|
code: transport(output.sharedCss),
|
|
1632
2227
|
map: JSON.stringify(output.sharedCssMap),
|
|
1633
2228
|
}
|
|
1634
|
-
|
|
2229
|
+
}
|
|
2230
|
+
|
|
2231
|
+
entry.css = output.css
|
|
2232
|
+
return {
|
|
2233
|
+
code: transport(output.css),
|
|
2234
|
+
map: JSON.stringify(output.cssMap),
|
|
2235
|
+
}
|
|
1635
2236
|
},
|
|
1636
2237
|
name: 'zyzz',
|
|
1637
2238
|
resolveId(id) {
|
|
@@ -1656,6 +2257,8 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
1656
2257
|
removed.get(this.environment)?.delete(id)
|
|
1657
2258
|
}
|
|
1658
2259
|
|
|
2260
|
+
entry.source = code
|
|
2261
|
+
|
|
1659
2262
|
const output = await compile(
|
|
1660
2263
|
entry,
|
|
1661
2264
|
{
|
|
@@ -1663,7 +2266,7 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
1663
2266
|
this.environment.mode === 'dev'
|
|
1664
2267
|
? resolver(this.environment)
|
|
1665
2268
|
: (source, importer) => this.resolve(source, importer),
|
|
1666
|
-
watch: (
|
|
2269
|
+
watch: watcher(this),
|
|
1667
2270
|
asset: async (file) => {
|
|
1668
2271
|
const reference = this.emitFile({
|
|
1669
2272
|
type: 'asset',
|
|
@@ -1685,64 +2288,29 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
1685
2288
|
|
|
1686
2289
|
// Keep the CSS dependency even when the current graph has no live rules.
|
|
1687
2290
|
// Later edits can introduce styles without changing this import boundary.
|
|
1688
|
-
const
|
|
1689
|
-
sourceType: 'module',
|
|
1690
|
-
})
|
|
1691
|
-
let offset = output.code.startsWith('#!')
|
|
1692
|
-
? output.code.indexOf('\n') + 1
|
|
1693
|
-
: 0
|
|
1694
|
-
|
|
1695
|
-
for (const node of parsed.program.body) {
|
|
1696
|
-
if (node.type !== 'ExpressionStatement' || !node.directive) break
|
|
1697
|
-
|
|
1698
|
-
offset = node.end
|
|
1699
|
-
}
|
|
1700
|
-
|
|
2291
|
+
const offset = prologue(output.code)
|
|
1701
2292
|
const sharedImport = `\nimport ${JSON.stringify(sharedId)};\n`
|
|
1702
|
-
const map = Mapping.fromMap(JSON.stringify(output.map))
|
|
1703
2293
|
// The inserted dependency shifts only generated positions after the prologue.
|
|
1704
2294
|
const before = output.code.slice(0, offset)
|
|
1705
2295
|
const insertionLine = before.split('\n').length
|
|
1706
2296
|
const insertionColumn = before.length - (before.lastIndexOf('\n') + 1)
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
generated.line > insertionLine ||
|
|
1714
|
-
(generated.line === insertionLine &&
|
|
1715
|
-
generated.column >= insertionColumn)
|
|
1716
|
-
) {
|
|
1717
|
-
if (generated.line === insertionLine)
|
|
1718
|
-
generated.column -= insertionColumn
|
|
1719
|
-
|
|
1720
|
-
generated.line += 2
|
|
1721
|
-
}
|
|
1722
|
-
|
|
1723
|
-
if (mapping.source !== undefined && mapping.original !== undefined) {
|
|
1724
|
-
const location = {
|
|
1725
|
-
generated,
|
|
1726
|
-
original: mapping.original,
|
|
1727
|
-
source: mapping.source,
|
|
1728
|
-
}
|
|
1729
|
-
|
|
1730
|
-
if (mapping.name === undefined) Mapping.addMapping(shifted, location)
|
|
1731
|
-
else Mapping.addMapping(shifted, { ...location, name: mapping.name })
|
|
1732
|
-
} else Mapping.addMapping(shifted, { generated })
|
|
1733
|
-
}
|
|
1734
|
-
|
|
1735
|
-
for (const [index, source] of output.map.sources.entries())
|
|
1736
|
-
if (source !== null)
|
|
1737
|
-
Mapping.setSourceContent(
|
|
1738
|
-
shifted,
|
|
1739
|
-
source,
|
|
1740
|
-
output.map.sourcesContent?.[index] ?? null,
|
|
1741
|
-
)
|
|
2297
|
+
// A build compiles a fixed project, so an empty module sheet stays empty. Importing it
|
|
2298
|
+
// would link an empty stylesheet from every chunk that holds no styled module.
|
|
2299
|
+
const stylesheetImport =
|
|
2300
|
+
this.environment.mode === 'build' && !output.css.trim()
|
|
2301
|
+
? ''
|
|
2302
|
+
: `\nimport ${JSON.stringify(cssId(id))};`
|
|
1742
2303
|
|
|
1743
2304
|
return {
|
|
1744
|
-
code: `${before}${sharedImport}${output.code.slice(offset)}
|
|
1745
|
-
map: JSON.stringify(
|
|
2305
|
+
code: `${before}${sharedImport}${output.code.slice(offset)}${stylesheetImport}`,
|
|
2306
|
+
map: JSON.stringify({
|
|
2307
|
+
...output.map,
|
|
2308
|
+
mappings: insert(
|
|
2309
|
+
output.map.mappings,
|
|
2310
|
+
insertionLine - 1,
|
|
2311
|
+
insertionColumn,
|
|
2312
|
+
),
|
|
2313
|
+
}),
|
|
1746
2314
|
}
|
|
1747
2315
|
},
|
|
1748
2316
|
transformIndexHtml: {
|
|
@@ -1779,13 +2347,25 @@ export function zyzz(options: zyzz.Options = {}): Plugin {
|
|
|
1779
2347
|
}
|
|
1780
2348
|
}
|
|
1781
2349
|
|
|
2350
|
+
/** One module's import edges: its resolutions and, in order, its visits and contract reads. */
|
|
2351
|
+
type Edges = {
|
|
2352
|
+
lazy: ReadonlySet<string>
|
|
2353
|
+
resolutions: Readonly<Record<string, string | null>>
|
|
2354
|
+
steps: readonly (string | { id: string; physical: string })[]
|
|
2355
|
+
text: string
|
|
2356
|
+
}
|
|
2357
|
+
|
|
1782
2358
|
type Entry = {
|
|
1783
2359
|
code?: string
|
|
1784
|
-
imports?: string
|
|
1785
|
-
environment: Environment
|
|
1786
2360
|
compiler: Graph.create.ReturnType
|
|
2361
|
+
/** The stylesheet Vite last loaded for this module. */
|
|
2362
|
+
css?: string | undefined
|
|
2363
|
+
environment: Environment
|
|
1787
2364
|
file: string
|
|
1788
2365
|
files: Set<string>
|
|
2366
|
+
imports?: string
|
|
2367
|
+
/** The code Vite last passed to the transform, until a watcher event changes the file. */
|
|
2368
|
+
source?: string | undefined
|
|
1789
2369
|
}
|
|
1790
2370
|
|
|
1791
2371
|
type Host = {
|
|
@@ -1805,9 +2385,130 @@ type Lookups = {
|
|
|
1805
2385
|
string,
|
|
1806
2386
|
Promise<{ contract: string | undefined; files: ReadonlySet<string> }>
|
|
1807
2387
|
>
|
|
2388
|
+
/** Files each settled contract read, so an edit to one drops only its contracts. */
|
|
2389
|
+
read: Map<string, ReadonlySet<string>>
|
|
1808
2390
|
resolutions: Map<string, ReturnType<Host['resolve']>>
|
|
1809
2391
|
}
|
|
1810
2392
|
|
|
2393
|
+
/** One development environment's merged source graph and its last compilation. */
|
|
2394
|
+
type Project = {
|
|
2395
|
+
compiler: Graph.create.ReturnType
|
|
2396
|
+
contracts: Record<string, string>
|
|
2397
|
+
/** Compiles a closure alone while the merged graph fails. */
|
|
2398
|
+
fallback: Graph.create.ReturnType
|
|
2399
|
+
imports: Record<string, Record<string, string | null>>
|
|
2400
|
+
modules: Record<string, string>
|
|
2401
|
+
outcome:
|
|
2402
|
+
| { readonly error: unknown }
|
|
2403
|
+
| { readonly result: Graph.compile.ReturnType }
|
|
2404
|
+
| undefined
|
|
2405
|
+
}
|
|
2406
|
+
|
|
2407
|
+
/** What an environment served when an edit arrived. */
|
|
2408
|
+
type Served = {
|
|
2409
|
+
entries: ReadonlyMap<
|
|
2410
|
+
Entry,
|
|
2411
|
+
{
|
|
2412
|
+
code: string | undefined
|
|
2413
|
+
css: string | undefined
|
|
2414
|
+
imports: string | undefined
|
|
2415
|
+
}
|
|
2416
|
+
>
|
|
2417
|
+
shared: string | undefined
|
|
2418
|
+
}
|
|
2419
|
+
|
|
2420
|
+
/**
|
|
2421
|
+
* The end of a module's hashbang and directive prologue, where an added import keeps directives
|
|
2422
|
+
* first. Source without a leading string literal skips parsing.
|
|
2423
|
+
*/
|
|
2424
|
+
function prologue(code: string) {
|
|
2425
|
+
let index = code.startsWith('#!') ? code.indexOf('\n') + 1 : 0
|
|
2426
|
+
const start = index
|
|
2427
|
+
|
|
2428
|
+
for (;;) {
|
|
2429
|
+
while (index < code.length && /\s/.test(code[index]!)) index++
|
|
2430
|
+
|
|
2431
|
+
if (code.startsWith('//', index)) {
|
|
2432
|
+
const end = code.indexOf('\n', index)
|
|
2433
|
+
index = end === -1 ? code.length : end
|
|
2434
|
+
} else if (code.startsWith('/*', index)) {
|
|
2435
|
+
const end = code.indexOf('*/', index + 2)
|
|
2436
|
+
index = end === -1 ? code.length : end + 2
|
|
2437
|
+
} else break
|
|
2438
|
+
}
|
|
2439
|
+
|
|
2440
|
+
if (code[index] !== "'" && code[index] !== '"') return start
|
|
2441
|
+
|
|
2442
|
+
let offset = start
|
|
2443
|
+
const { program } = Parser.parseSync('source.tsx', code, {
|
|
2444
|
+
sourceType: 'module',
|
|
2445
|
+
})
|
|
2446
|
+
|
|
2447
|
+
for (const node of program.body) {
|
|
2448
|
+
if (node.type !== 'ExpressionStatement' || !node.directive) break
|
|
2449
|
+
|
|
2450
|
+
offset = node.end
|
|
2451
|
+
}
|
|
2452
|
+
|
|
2453
|
+
return offset
|
|
2454
|
+
}
|
|
2455
|
+
|
|
2456
|
+
/**
|
|
2457
|
+
* Moves encoded mappings for two lines inserted at a generated position. Segments at or past
|
|
2458
|
+
* `column` on `line` start the line after the insertion, and every later line moves down by two.
|
|
2459
|
+
* Other fields stay relative to the segment before them, which keeps its order.
|
|
2460
|
+
*/
|
|
2461
|
+
function insert(mappings: string, line: number, column: number) {
|
|
2462
|
+
const lines = mappings.split(';')
|
|
2463
|
+
const text = lines[line]
|
|
2464
|
+
|
|
2465
|
+
if (text === undefined) return mappings
|
|
2466
|
+
|
|
2467
|
+
let generated = 0
|
|
2468
|
+
let split = -1
|
|
2469
|
+
let position = 0
|
|
2470
|
+
const segments = text ? text.split(',') : []
|
|
2471
|
+
|
|
2472
|
+
for (const [index, segment] of segments.entries()) {
|
|
2473
|
+
const [delta] = Vlq.decode(segment)
|
|
2474
|
+
|
|
2475
|
+
generated += delta!
|
|
2476
|
+
|
|
2477
|
+
if (generated >= column) {
|
|
2478
|
+
split = index
|
|
2479
|
+
position = generated
|
|
2480
|
+
break
|
|
2481
|
+
}
|
|
2482
|
+
}
|
|
2483
|
+
|
|
2484
|
+
if (split === -1) {
|
|
2485
|
+
lines.splice(line + 1, 0, '', '')
|
|
2486
|
+
return lines.join(';')
|
|
2487
|
+
}
|
|
2488
|
+
|
|
2489
|
+
const moved = segments.slice(split)
|
|
2490
|
+
const fields = Vlq.decode(moved[0]!)
|
|
2491
|
+
|
|
2492
|
+
fields[0] = position - column
|
|
2493
|
+
moved[0] = Vlq.encode(fields)
|
|
2494
|
+
lines.splice(line, 1, segments.slice(0, split).join(','), '', moved.join(','))
|
|
2495
|
+
|
|
2496
|
+
return lines.join(';')
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2499
|
+
/** Whether two import resolution records hold the same entries. */
|
|
2500
|
+
function same(
|
|
2501
|
+
left: Readonly<Record<string, string | null>> | undefined,
|
|
2502
|
+
right: Readonly<Record<string, string | null>>,
|
|
2503
|
+
) {
|
|
2504
|
+
if (!left) return false
|
|
2505
|
+
|
|
2506
|
+
const keys = Object.keys(right)
|
|
2507
|
+
if (Object.keys(left).length !== keys.length) return false
|
|
2508
|
+
|
|
2509
|
+
return keys.every((key) => left[key] === right[key])
|
|
2510
|
+
}
|
|
2511
|
+
|
|
1811
2512
|
/**
|
|
1812
2513
|
* Whether `zyzz/runtime` resolves inside node_modules from the Vite root. Vite
|
|
1813
2514
|
* optimizes a discovered import only there, so linked and aliased copies stay
|