zyzz 0.0.27 → 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 -1113
- 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 -958
- 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/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 +5 -2
- package/dist/internal/Literal.d.ts.map +1 -1
- package/dist/internal/Literal.js +7 -2
- 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/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/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/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 +7 -0
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +862 -216
- 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 -1579
- package/src/compiler/Transform.ts +16 -1312
- 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/Authoring.ts +53 -18
- package/src/internal/Configuration.ts +38 -1
- package/src/internal/Literal.ts +7 -2
- package/src/internal/Theme.ts +15 -0
- 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/styleFunction.ts +31 -2
- package/src/unplugin/index.ts +6 -6
- package/src/vite/index.ts +1157 -241
- 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 -352
- 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 -12
- package/dist/compiler/internal/Selectors.d.ts.map +0 -1
- package/dist/compiler/internal/Selectors.js +0 -268
- 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 -554
- 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 -339
- 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/dist/vite/index.js
CHANGED
|
@@ -5,18 +5,20 @@
|
|
|
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';
|
|
11
12
|
import * as Crypto from 'node:crypto';
|
|
13
|
+
import { existsSync } from 'node:fs';
|
|
12
14
|
import * as Fs from 'node:fs/promises';
|
|
13
15
|
import * as Path from 'node:path';
|
|
14
16
|
import * as Reset from '../node/Reset.js';
|
|
15
|
-
import
|
|
17
|
+
import * as Vlq from '../internal/Vlq.js';
|
|
18
|
+
import { createRequire, isBuiltin } from 'node:module';
|
|
16
19
|
import * as Parser from 'oxc-parser';
|
|
17
20
|
import * as Walker from 'oxc-walker';
|
|
18
21
|
import * as Snapshot from '../node/internal/Snapshot.js';
|
|
19
|
-
import * as Syntax from '../compiler/internal/Syntax.js';
|
|
20
22
|
import * as Scope from '../compiler/internal/Scope.js';
|
|
21
23
|
import * as Graph from '../compiler/Graph.js';
|
|
22
24
|
import * as Source from '../compiler/Source.js';
|
|
@@ -43,7 +45,23 @@ export function zyzz(options = {}) {
|
|
|
43
45
|
const states = new WeakMap();
|
|
44
46
|
const removed = new WeakMap();
|
|
45
47
|
const reads = new WeakMap();
|
|
48
|
+
const lookups = new WeakMap();
|
|
46
49
|
const compilers = new WeakMap();
|
|
50
|
+
const projects = new WeakMap();
|
|
51
|
+
// The shared stylesheet each environment last loaded, which an edit compares against.
|
|
52
|
+
const servedShared = new WeakMap();
|
|
53
|
+
// Watcher events advance an environment's generation, which scopes memoized whole-project work.
|
|
54
|
+
const generations = new WeakMap();
|
|
55
|
+
let devServer;
|
|
56
|
+
const watching = new Set();
|
|
57
|
+
// Chokidar matches its ignore patterns on every add, and each compile watches its whole closure.
|
|
58
|
+
const watched = new Set();
|
|
59
|
+
// Each compile reads the catalogs of every contract it saw, which rarely changes text.
|
|
60
|
+
const catalogReads = new Map();
|
|
61
|
+
const dependencyChains = new Map();
|
|
62
|
+
const edgeMemos = new WeakMap();
|
|
63
|
+
// A development entry's last output, reused until a watcher event or a recompiled graph.
|
|
64
|
+
const outputs = new WeakMap();
|
|
47
65
|
const packages = Contracts.create();
|
|
48
66
|
function compiler(environment) {
|
|
49
67
|
let value = compilers.get(environment);
|
|
@@ -53,6 +71,144 @@ export function zyzz(options = {}) {
|
|
|
53
71
|
}
|
|
54
72
|
return value;
|
|
55
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Watches a compile input from a hook. Vite checks the filesystem each time a file outside its
|
|
76
|
+
* root is watched, and every compile reads the same package files, so development watches
|
|
77
|
+
* each of those once.
|
|
78
|
+
*/
|
|
79
|
+
function watcher(context) {
|
|
80
|
+
return (file) => {
|
|
81
|
+
if (devServer && !file.startsWith(`${devServer.config.root}/`)) {
|
|
82
|
+
if (watching.has(file))
|
|
83
|
+
return;
|
|
84
|
+
watching.add(file);
|
|
85
|
+
}
|
|
86
|
+
context.addWatchFile(file);
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* The packed stylesheet dependency chains of a library contract. Every compile reads each
|
|
91
|
+
* contract it reaches, and design system contracts are large, so each text parses once.
|
|
92
|
+
*/
|
|
93
|
+
function stylesheetDependencies(id, contract) {
|
|
94
|
+
let value = dependencyChains.get(contract);
|
|
95
|
+
if (value)
|
|
96
|
+
return value;
|
|
97
|
+
const metadata = (() => {
|
|
98
|
+
try {
|
|
99
|
+
return JSON.parse(contract);
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
Graph.compile({ modules: {}, contracts: { [id]: contract } });
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
})();
|
|
106
|
+
value = Array.isArray(metadata?.stylesheets)
|
|
107
|
+
? metadata.stylesheets
|
|
108
|
+
.map((section) => section?.dependency)
|
|
109
|
+
.filter((dependency) => Array.isArray(dependency))
|
|
110
|
+
: [];
|
|
111
|
+
// Rebuilt libraries leave their earlier texts behind, so the cache stays bounded.
|
|
112
|
+
if (dependencyChains.size > 256)
|
|
113
|
+
dependencyChains.clear();
|
|
114
|
+
dependencyChains.set(contract, value);
|
|
115
|
+
return value;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Each module's import edges in an environment, shared by every entry compile that walks the
|
|
119
|
+
* module. Added and removed files and dependency optimization change resolutions, so either
|
|
120
|
+
* starts over.
|
|
121
|
+
*/
|
|
122
|
+
function importEdges(environment) {
|
|
123
|
+
const metadata = environment.mode === 'dev'
|
|
124
|
+
? environment.depsOptimizer?.metadata
|
|
125
|
+
: undefined;
|
|
126
|
+
const key = `${metadata?.browserHash}:${Object.keys(metadata?.discovered ?? {}).length}`;
|
|
127
|
+
let value = edgeMemos.get(environment);
|
|
128
|
+
if (value?.key !== key) {
|
|
129
|
+
value = { files: new Map(), key };
|
|
130
|
+
edgeMemos.set(environment, value);
|
|
131
|
+
}
|
|
132
|
+
return value.files;
|
|
133
|
+
}
|
|
134
|
+
/** Adds a compile input to the development watcher once. */
|
|
135
|
+
function watch(server, file) {
|
|
136
|
+
if (watched.has(file))
|
|
137
|
+
return;
|
|
138
|
+
watched.add(file);
|
|
139
|
+
server.watcher.add(file);
|
|
140
|
+
}
|
|
141
|
+
function read(contract) {
|
|
142
|
+
let value = catalogReads.get(contract);
|
|
143
|
+
if (!value) {
|
|
144
|
+
if (catalogReads.size > 256)
|
|
145
|
+
catalogReads.clear();
|
|
146
|
+
value = Catalogs.read(contract);
|
|
147
|
+
catalogReads.set(contract, value);
|
|
148
|
+
}
|
|
149
|
+
return value;
|
|
150
|
+
}
|
|
151
|
+
function project(environment) {
|
|
152
|
+
let value = projects.get(environment);
|
|
153
|
+
if (!value) {
|
|
154
|
+
value = {
|
|
155
|
+
compiler: Graph.create(),
|
|
156
|
+
contracts: Object.create(null),
|
|
157
|
+
fallback: Graph.create(),
|
|
158
|
+
imports: Object.create(null),
|
|
159
|
+
modules: Object.create(null),
|
|
160
|
+
outcome: undefined,
|
|
161
|
+
};
|
|
162
|
+
projects.set(environment, value);
|
|
163
|
+
}
|
|
164
|
+
return value;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Development merges every module a transform reaches into one graph per environment, so
|
|
168
|
+
* modules compile once per change instead of once per importer. A module's output depends only
|
|
169
|
+
* on its own import closure, so the merged graph returns the output a closure compile would.
|
|
170
|
+
* While some other module fails, the requested closure compiles alone and reports only its own
|
|
171
|
+
* errors, as before.
|
|
172
|
+
*/
|
|
173
|
+
function compileProject(environment, input, id) {
|
|
174
|
+
const value = project(environment);
|
|
175
|
+
let changed = value.outcome === undefined;
|
|
176
|
+
for (const [module, source] of Object.entries(input.modules))
|
|
177
|
+
if (value.modules[module] !== source) {
|
|
178
|
+
value.modules[module] = source;
|
|
179
|
+
changed = true;
|
|
180
|
+
}
|
|
181
|
+
for (const [module, resolutions] of Object.entries(input.imports ?? {}))
|
|
182
|
+
if (!same(value.imports[module], resolutions)) {
|
|
183
|
+
value.imports[module] = resolutions;
|
|
184
|
+
changed = true;
|
|
185
|
+
}
|
|
186
|
+
for (const [module, contract] of Object.entries(input.contracts ?? {}))
|
|
187
|
+
if (value.contracts[module] !== contract) {
|
|
188
|
+
value.contracts[module] = contract;
|
|
189
|
+
changed = true;
|
|
190
|
+
}
|
|
191
|
+
if (changed)
|
|
192
|
+
value.outcome = (() => {
|
|
193
|
+
try {
|
|
194
|
+
return {
|
|
195
|
+
result: value.compiler.compile({
|
|
196
|
+
...input,
|
|
197
|
+
contracts: value.contracts,
|
|
198
|
+
imports: value.imports,
|
|
199
|
+
modules: value.modules,
|
|
200
|
+
}),
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
return { error };
|
|
205
|
+
}
|
|
206
|
+
})();
|
|
207
|
+
const outcome = value.outcome;
|
|
208
|
+
if ('result' in outcome && Object.hasOwn(outcome.result.modules, id))
|
|
209
|
+
return outcome.result;
|
|
210
|
+
return value.fallback.compile(input);
|
|
211
|
+
}
|
|
56
212
|
const discoveries = new WeakMap();
|
|
57
213
|
const contributionFiles = new WeakMap();
|
|
58
214
|
const assets = new Map();
|
|
@@ -62,8 +218,13 @@ export function zyzz(options = {}) {
|
|
|
62
218
|
const initializers = new WeakMap();
|
|
63
219
|
const sourceEntrypoints = new Set();
|
|
64
220
|
const identities = new Map();
|
|
221
|
+
const exclusions = new Map();
|
|
222
|
+
// Every compile checks each import against the source directories.
|
|
223
|
+
const roots = new Map();
|
|
65
224
|
const lazyImports = new WeakMap();
|
|
66
225
|
let directories;
|
|
226
|
+
let filter;
|
|
227
|
+
let optimizeRuntime = false;
|
|
67
228
|
let root;
|
|
68
229
|
function snapshot(environment) {
|
|
69
230
|
let value = snapshots.get(environment);
|
|
@@ -125,16 +286,33 @@ export function zyzz(options = {}) {
|
|
|
125
286
|
return (Path.isAbsolute(id) &&
|
|
126
287
|
sourceRoot(id) !== undefined &&
|
|
127
288
|
/\.[cm]?[jt]sx?$/.test(id) &&
|
|
128
|
-
!/\.(?:d|test|test-d|bench|bench-d)\.[cm]?[jt]sx?$/.test(id)
|
|
289
|
+
!/\.(?:d|test|test-d|bench|bench-d)\.[cm]?[jt]sx?$/.test(id) &&
|
|
290
|
+
!excluded(id));
|
|
291
|
+
}
|
|
292
|
+
/** Matches excluded files inside source directories. Package files never match, so packed contracts stay readable. */
|
|
293
|
+
function excluded(id) {
|
|
294
|
+
const file = id.split(/[?#]/)[0];
|
|
295
|
+
let value = exclusions.get(file);
|
|
296
|
+
// Vite's filter recompiles each glob per call, and every compile checks each import.
|
|
297
|
+
if (value === undefined) {
|
|
298
|
+
value =
|
|
299
|
+
Path.isAbsolute(file) && sourceRoot(file) !== undefined && !filter(file);
|
|
300
|
+
exclusions.set(file, value);
|
|
301
|
+
}
|
|
302
|
+
return value;
|
|
129
303
|
}
|
|
130
304
|
function sourceRoot(file) {
|
|
131
|
-
|
|
305
|
+
if (roots.has(file))
|
|
306
|
+
return roots.get(file);
|
|
307
|
+
const root = directories.find((directory) => {
|
|
132
308
|
const relative = Path.relative(directory, file);
|
|
133
309
|
return (relative !== '..' &&
|
|
134
310
|
!relative.startsWith(`..${Path.sep}`) &&
|
|
135
311
|
!Path.isAbsolute(relative) &&
|
|
136
312
|
!relative.split(Path.sep).includes('node_modules'));
|
|
137
313
|
});
|
|
314
|
+
roots.set(file, root);
|
|
315
|
+
return root;
|
|
138
316
|
}
|
|
139
317
|
function discover(environment, host) {
|
|
140
318
|
let pending = discoveries.get(environment);
|
|
@@ -275,6 +453,37 @@ export function zyzz(options = {}) {
|
|
|
275
453
|
}
|
|
276
454
|
async function updateDiscovery(environment, file, event) {
|
|
277
455
|
reads.delete(environment);
|
|
456
|
+
generations.set(environment, (generations.get(environment) ?? 0) + 1);
|
|
457
|
+
const changed = entries(environment).get(file);
|
|
458
|
+
if (changed)
|
|
459
|
+
changed.source = undefined;
|
|
460
|
+
// An edit keeps every resolution. Adding or removing a file can change where a
|
|
461
|
+
// specifier resolves, and a contract derives again only when a file it read changes.
|
|
462
|
+
// Editors save through temporary files, which no specifier resolves to.
|
|
463
|
+
const resolvable = event !== 'update' &&
|
|
464
|
+
/\.(?:[cm]?[jt]sx?|json|css|svelte|vue)$|(?:^|[/\\])package\.json$/.test(file);
|
|
465
|
+
const cache = lookups.get(environment);
|
|
466
|
+
// Import edges derive from resolutions and module text, which each memo compares.
|
|
467
|
+
if (resolvable)
|
|
468
|
+
edgeMemos.delete(environment);
|
|
469
|
+
if (cache) {
|
|
470
|
+
if (resolvable)
|
|
471
|
+
cache.resolutions.clear();
|
|
472
|
+
for (const [key, files] of cache.read)
|
|
473
|
+
if (files.has(file)) {
|
|
474
|
+
cache.contracts.delete(key);
|
|
475
|
+
cache.read.delete(key);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
if (event === 'delete') {
|
|
479
|
+
const value = projects.get(environment);
|
|
480
|
+
const id = sourceId(file);
|
|
481
|
+
if (value && Object.hasOwn(value.modules, id)) {
|
|
482
|
+
delete value.modules[id];
|
|
483
|
+
delete value.imports[id];
|
|
484
|
+
value.outcome = undefined;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
278
487
|
if (event === 'delete') {
|
|
279
488
|
if (entries(environment).has(file)) {
|
|
280
489
|
let files = removed.get(environment);
|
|
@@ -304,36 +513,142 @@ export function zyzz(options = {}) {
|
|
|
304
513
|
}
|
|
305
514
|
}
|
|
306
515
|
async function compile(entry, host, code, allSources = false, everything = false) {
|
|
516
|
+
const generation = generations.get(entry.environment) ?? 0;
|
|
517
|
+
const memo = outputs.get(entry);
|
|
518
|
+
const current = projects.get(entry.environment)?.outcome;
|
|
519
|
+
// Without a watcher event the closure is unchanged, so a stylesheet load after its
|
|
520
|
+
// transform reuses the output while the merged graph has not recompiled.
|
|
521
|
+
if (memo &&
|
|
522
|
+
!everything &&
|
|
523
|
+
memo.allSources === allSources &&
|
|
524
|
+
memo.generation === generation &&
|
|
525
|
+
(code === undefined || code === memo.code) &&
|
|
526
|
+
current &&
|
|
527
|
+
'result' in current &&
|
|
528
|
+
current.result === memo.result) {
|
|
529
|
+
for (const file of entry.files)
|
|
530
|
+
host.watch(file);
|
|
531
|
+
return memo.output;
|
|
532
|
+
}
|
|
533
|
+
const output = await compileEntry(entry, host, code, allSources, everything);
|
|
534
|
+
const outcome = projects.get(entry.environment)?.outcome;
|
|
535
|
+
// A concurrent compile can replace the merged result while this one renders, so only an
|
|
536
|
+
// output of the current result is kept.
|
|
537
|
+
if (entry.environment.mode === 'dev' &&
|
|
538
|
+
!everything &&
|
|
539
|
+
outcome &&
|
|
540
|
+
'result' in outcome &&
|
|
541
|
+
outcome.result.modules === output.modules &&
|
|
542
|
+
generation === (generations.get(entry.environment) ?? 0))
|
|
543
|
+
outputs.set(entry, {
|
|
544
|
+
allSources,
|
|
545
|
+
code,
|
|
546
|
+
generation,
|
|
547
|
+
output,
|
|
548
|
+
result: outcome.result,
|
|
549
|
+
});
|
|
550
|
+
return output;
|
|
551
|
+
}
|
|
552
|
+
async function compileEntry(entry, host, code, allSources = false, everything = false) {
|
|
553
|
+
// Every compile visits the discovered project, so modules share lookups. Watcher events
|
|
554
|
+
// drop the resolutions and contracts a change can affect. Full-project compiles look up
|
|
555
|
+
// afresh, since a document must not trail the watcher.
|
|
556
|
+
const cache = (() => {
|
|
557
|
+
const fresh = {
|
|
558
|
+
contracts: new Map(),
|
|
559
|
+
read: new Map(),
|
|
560
|
+
resolutions: new Map(),
|
|
561
|
+
};
|
|
562
|
+
if (everything)
|
|
563
|
+
return fresh;
|
|
564
|
+
let value = lookups.get(entry.environment);
|
|
565
|
+
if (!value) {
|
|
566
|
+
value = fresh;
|
|
567
|
+
lookups.set(entry.environment, value);
|
|
568
|
+
}
|
|
569
|
+
return value;
|
|
570
|
+
})();
|
|
571
|
+
// Full-project compiles walk afresh, as they look up afresh.
|
|
572
|
+
const memos = everything ? undefined : importEdges(entry.environment);
|
|
307
573
|
async function resolve(source, importer) {
|
|
308
574
|
if (isBuiltin(source))
|
|
309
575
|
return { id: source, external: true };
|
|
310
|
-
const
|
|
576
|
+
const key = `${importer}\0${source}`;
|
|
577
|
+
let pending = cache.resolutions.get(key);
|
|
578
|
+
if (!pending) {
|
|
579
|
+
pending = host.resolve(source, importer);
|
|
580
|
+
cache.resolutions.set(key, pending);
|
|
581
|
+
pending.catch(() => cache.resolutions.delete(key));
|
|
582
|
+
}
|
|
583
|
+
const resolved = await pending;
|
|
311
584
|
if (!resolved)
|
|
312
585
|
return resolved;
|
|
313
586
|
if (entry.environment.mode === 'dev') {
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
...
|
|
317
|
-
}).find((item) => item.file === resolved.id.split('?')[0]);
|
|
318
|
-
if (optimized?.src)
|
|
319
|
-
return { ...resolved, id: optimized.src };
|
|
587
|
+
const source = optimized().get(resolved.id.split('?')[0]);
|
|
588
|
+
if (source)
|
|
589
|
+
return { ...resolved, id: source };
|
|
320
590
|
}
|
|
321
591
|
return { ...resolved, id: normalize(resolved.id) };
|
|
322
592
|
}
|
|
593
|
+
// Optimized dependency sources by bundled file, indexed once per compile rather than
|
|
594
|
+
// searched for every resolution.
|
|
595
|
+
let indexed;
|
|
596
|
+
function optimized() {
|
|
597
|
+
if (indexed)
|
|
598
|
+
return indexed;
|
|
599
|
+
indexed = new Map();
|
|
600
|
+
const environment = entry.environment;
|
|
601
|
+
const metadata = environment.mode === 'dev'
|
|
602
|
+
? environment.depsOptimizer?.metadata
|
|
603
|
+
: undefined;
|
|
604
|
+
// Discovered entries replace optimized ones of the same name, and the first entry
|
|
605
|
+
// bundling a file wins, as a search of the merged record finds it.
|
|
606
|
+
for (const item of Object.values({
|
|
607
|
+
...metadata?.optimized,
|
|
608
|
+
...metadata?.discovered,
|
|
609
|
+
}))
|
|
610
|
+
if (!indexed.has(item.file))
|
|
611
|
+
indexed.set(item.file, item.src ?? '');
|
|
612
|
+
return indexed;
|
|
613
|
+
}
|
|
323
614
|
const imports = Object.create(null);
|
|
324
615
|
const contracts = Object.create(null);
|
|
325
616
|
const lazy = Object.create(null);
|
|
326
617
|
const modules = Object.create(null);
|
|
327
618
|
const files = new Set();
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
619
|
+
// A package contract derives from the files it reads and the resolutions
|
|
620
|
+
// above. A cached contract still makes this compile watch those files.
|
|
621
|
+
async function readContract(file, required = false) {
|
|
622
|
+
const key = `${required}\0${file}`;
|
|
623
|
+
let pending = cache.contracts.get(key);
|
|
624
|
+
if (!pending) {
|
|
625
|
+
const read = new Set();
|
|
626
|
+
pending = packages
|
|
627
|
+
.read(file, {
|
|
628
|
+
read: async (path) => {
|
|
629
|
+
const source = await snapshot(entry.environment).read(path);
|
|
630
|
+
read.add(path);
|
|
631
|
+
return source;
|
|
632
|
+
},
|
|
633
|
+
required,
|
|
634
|
+
resolve: async (specifier, importer) => (await resolve(specifier, importer))?.id.split(/[?#]/)[0],
|
|
635
|
+
})
|
|
636
|
+
.then((contract) => {
|
|
637
|
+
cache.read.set(key, read);
|
|
638
|
+
return { contract, files: read };
|
|
639
|
+
});
|
|
640
|
+
cache.contracts.set(key, pending);
|
|
641
|
+
pending.catch(() => cache.contracts.delete(key));
|
|
642
|
+
}
|
|
643
|
+
const derived = await pending;
|
|
644
|
+
for (const path of derived.files) {
|
|
645
|
+
if (files.has(path))
|
|
646
|
+
continue;
|
|
647
|
+
files.add(path);
|
|
648
|
+
host.watch(path);
|
|
649
|
+
}
|
|
650
|
+
return derived.contract;
|
|
651
|
+
}
|
|
337
652
|
async function visit(file, source) {
|
|
338
653
|
if (files.has(file))
|
|
339
654
|
return;
|
|
@@ -345,14 +660,37 @@ export function zyzz(options = {}) {
|
|
|
345
660
|
cached = new Map();
|
|
346
661
|
reads.set(entry.environment, cached);
|
|
347
662
|
}
|
|
663
|
+
// Earlier plugins can rewrite a module before this one sees it, as router plugins drop
|
|
664
|
+
// server routes from client route trees. Its importers read the code Vite transformed, so
|
|
665
|
+
// every graph in the environment holds one version of each module.
|
|
348
666
|
// Full-project compilation must not reuse reads from concurrent module transforms.
|
|
349
667
|
const text = source ??
|
|
668
|
+
(everything
|
|
669
|
+
? undefined
|
|
670
|
+
: entries(entry.environment).get(file)?.source) ??
|
|
350
671
|
(everything ? undefined : cached.get(file)) ??
|
|
351
672
|
(await snapshot(entry.environment).read(file));
|
|
352
673
|
if (!everything)
|
|
353
674
|
cached.set(file, text);
|
|
354
675
|
modules[id] = text;
|
|
676
|
+
const memo = memos?.get(file);
|
|
677
|
+
// Replaying the edges keeps the order in which modules and contracts join the compile.
|
|
678
|
+
if (memo?.text === text) {
|
|
679
|
+
imports[id] = { ...memo.resolutions };
|
|
680
|
+
for (const step of memo.steps) {
|
|
681
|
+
if (typeof step === 'string') {
|
|
682
|
+
await visit(step);
|
|
683
|
+
continue;
|
|
684
|
+
}
|
|
685
|
+
const contract = await readContract(step.physical);
|
|
686
|
+
if (contract !== undefined)
|
|
687
|
+
contracts[step.id] = contract;
|
|
688
|
+
}
|
|
689
|
+
lazy[id] = memo.lazy;
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
355
692
|
const resolutions = Object.create(null);
|
|
693
|
+
const steps = [];
|
|
356
694
|
imports[id] = resolutions;
|
|
357
695
|
const parsed = snapshot(entry.environment).parse({
|
|
358
696
|
moduleId: id,
|
|
@@ -401,14 +739,21 @@ export function zyzz(options = {}) {
|
|
|
401
739
|
/[/\\]src[/\\]default\.[cm]?ts$/.test(resolved.id)) {
|
|
402
740
|
sourceEntrypoints.add(normalize(resolved.id));
|
|
403
741
|
resolutions[specifier] = sourceId(resolved.id);
|
|
742
|
+
steps.push(resolved.id);
|
|
404
743
|
await visit(resolved.id);
|
|
405
744
|
continue;
|
|
406
745
|
}
|
|
746
|
+
// An excluded source stays a runtime import instead of deriving a package contract.
|
|
747
|
+
if (excluded(resolved.id)) {
|
|
748
|
+
resolutions[specifier] = null;
|
|
749
|
+
continue;
|
|
750
|
+
}
|
|
407
751
|
if (resolved.external || !eligible(resolved.id)) {
|
|
408
752
|
const physical = resolved.id.split('?')[0].split('#')[0];
|
|
409
|
-
const
|
|
410
|
-
|
|
411
|
-
:
|
|
753
|
+
const readable = Path.isAbsolute(physical) && /\.[cm]?[jt]sx?$/.test(physical);
|
|
754
|
+
if (readable)
|
|
755
|
+
steps.push({ id: resolved.id, physical });
|
|
756
|
+
const contract = readable ? await readContract(physical) : undefined;
|
|
412
757
|
if (contract !== undefined) {
|
|
413
758
|
contracts[resolved.id] = contract;
|
|
414
759
|
resolutions[specifier] = resolved.id;
|
|
@@ -418,6 +763,7 @@ export function zyzz(options = {}) {
|
|
|
418
763
|
continue;
|
|
419
764
|
}
|
|
420
765
|
resolutions[specifier] = sourceId(resolved.id);
|
|
766
|
+
steps.push(resolved.id);
|
|
421
767
|
await visit(resolved.id);
|
|
422
768
|
}
|
|
423
769
|
// Lazy imports stay outside this compile but scope the documents that load them.
|
|
@@ -445,6 +791,7 @@ export function zyzz(options = {}) {
|
|
|
445
791
|
targets.add(eligible(resolved.id) ? sourceId(resolved.id) : resolved.id);
|
|
446
792
|
}
|
|
447
793
|
lazy[id] = targets;
|
|
794
|
+
memos?.set(file, { lazy: targets, resolutions, steps, text });
|
|
448
795
|
}
|
|
449
796
|
await visit(entry.file, code);
|
|
450
797
|
const connected = new Set(files);
|
|
@@ -495,14 +842,16 @@ export function zyzz(options = {}) {
|
|
|
495
842
|
(specifier.startsWith('zyzz/') && specifier !== 'zyzz/default'))
|
|
496
843
|
continue;
|
|
497
844
|
const resolved = await resolve(specifier, file);
|
|
498
|
-
if (!resolved ||
|
|
845
|
+
if (!resolved ||
|
|
846
|
+
(!resolved.external && eligible(resolved.id)) ||
|
|
847
|
+
excluded(resolved.id))
|
|
499
848
|
continue;
|
|
500
849
|
if (resource(specifier) || resource(resolved.id))
|
|
501
850
|
continue;
|
|
502
851
|
const physical = resolved.id.split(/[?#]/)[0];
|
|
503
852
|
if (!Path.isAbsolute(physical) || !/\.[cm]?[jt]sx?$/.test(physical))
|
|
504
853
|
continue;
|
|
505
|
-
const contract = await
|
|
854
|
+
const contract = await readContract(physical);
|
|
506
855
|
if (contract === undefined)
|
|
507
856
|
continue;
|
|
508
857
|
contracts[resolved.id] = contract;
|
|
@@ -526,22 +875,9 @@ export function zyzz(options = {}) {
|
|
|
526
875
|
if (loaded.has(id))
|
|
527
876
|
return;
|
|
528
877
|
loaded.add(id);
|
|
529
|
-
const
|
|
530
|
-
try {
|
|
531
|
-
return JSON.parse(contracts[id]);
|
|
532
|
-
}
|
|
533
|
-
catch (error) {
|
|
534
|
-
Graph.compile({ modules: {}, contracts: { [id]: contracts[id] } });
|
|
535
|
-
throw error;
|
|
536
|
-
}
|
|
537
|
-
})();
|
|
538
|
-
if (!Array.isArray(metadata?.stylesheets))
|
|
539
|
-
return;
|
|
540
|
-
for (const section of metadata.stylesheets) {
|
|
541
|
-
if (!Array.isArray(section?.dependency))
|
|
542
|
-
continue;
|
|
878
|
+
for (const dependency of stylesheetDependencies(id, contracts[id])) {
|
|
543
879
|
let owner = id;
|
|
544
|
-
for (const specifier of
|
|
880
|
+
for (const specifier of dependency) {
|
|
545
881
|
if (typeof specifier !== 'string' ||
|
|
546
882
|
!specifier ||
|
|
547
883
|
specifier.includes('\0'))
|
|
@@ -551,7 +887,7 @@ export function zyzz(options = {}) {
|
|
|
551
887
|
throw new Error('Unable to resolve packed stylesheet dependency.');
|
|
552
888
|
imports[owner] ??= Object.create(null);
|
|
553
889
|
imports[owner][specifier] = target.id;
|
|
554
|
-
contracts[target.id] ??= (await
|
|
890
|
+
contracts[target.id] ??= (await readContract(target.id.split(/[?#]/)[0], true));
|
|
555
891
|
await dependencies(target.id);
|
|
556
892
|
owner = target.id;
|
|
557
893
|
}
|
|
@@ -572,9 +908,9 @@ export function zyzz(options = {}) {
|
|
|
572
908
|
...Object.values(resolutions).filter((target) => target !== null),
|
|
573
909
|
...(lazy[id] ?? []),
|
|
574
910
|
]));
|
|
575
|
-
const
|
|
911
|
+
const input = {
|
|
912
|
+
[Results.lean]: true,
|
|
576
913
|
[ThemeRules.shared]: native ? undefined : 'all',
|
|
577
|
-
[Syntax.cache]: snapshot(entry.environment).programs(modules),
|
|
578
914
|
compiler: options.compiler,
|
|
579
915
|
reset: options.reset ? Reset.read() : undefined,
|
|
580
916
|
native,
|
|
@@ -582,7 +918,10 @@ export function zyzz(options = {}) {
|
|
|
582
918
|
development: entry.environment.mode !== 'build',
|
|
583
919
|
imports,
|
|
584
920
|
modules,
|
|
585
|
-
}
|
|
921
|
+
};
|
|
922
|
+
const result = entry.environment.mode === 'dev' && !native && !everything
|
|
923
|
+
? compileProject(entry.environment, input, sourceId(entry.file))
|
|
924
|
+
: entry.compiler.compile(input);
|
|
586
925
|
if (native) {
|
|
587
926
|
const output = result.modules[sourceId(entry.file)];
|
|
588
927
|
entry.files = files;
|
|
@@ -595,6 +934,7 @@ export function zyzz(options = {}) {
|
|
|
595
934
|
configurations: new Map(),
|
|
596
935
|
connections: staged,
|
|
597
936
|
css: '',
|
|
937
|
+
graphSharedCss: '',
|
|
598
938
|
sharedCss: '',
|
|
599
939
|
cssMap: map,
|
|
600
940
|
sharedCssMap: map,
|
|
@@ -622,132 +962,143 @@ export function zyzz(options = {}) {
|
|
|
622
962
|
catalogs.delete(key);
|
|
623
963
|
const configurations = new Map();
|
|
624
964
|
for (const [id, contract] of Object.entries(compiled))
|
|
625
|
-
for (const configuration of
|
|
965
|
+
for (const configuration of read(contract)) {
|
|
626
966
|
const key = `${id}\0${configuration.identity}`;
|
|
627
967
|
catalogs.set(key, configuration);
|
|
628
968
|
configurations.set(key, configuration);
|
|
629
969
|
}
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
970
|
+
// Development renders the shared stylesheet for its own request only; transforms and module
|
|
971
|
+
// stylesheets read module outputs.
|
|
972
|
+
async function renderShared() {
|
|
973
|
+
const owners = new Map();
|
|
974
|
+
for (const id of new Set(Object.values(result.sharedAssetOwners ?? {}))) {
|
|
975
|
+
if (!Path.isAbsolute(id))
|
|
976
|
+
continue;
|
|
977
|
+
let directory = Path.dirname(id.split(/[?#]/)[0]);
|
|
978
|
+
for (;;) {
|
|
979
|
+
try {
|
|
980
|
+
await Fs.access(Path.join(directory, 'package.json'));
|
|
981
|
+
owners.set(id, await Fs.realpath(directory));
|
|
982
|
+
break;
|
|
983
|
+
}
|
|
984
|
+
catch (error) {
|
|
985
|
+
if (error.code !== 'ENOENT')
|
|
986
|
+
throw error;
|
|
987
|
+
}
|
|
988
|
+
const parent = Path.dirname(directory);
|
|
989
|
+
if (parent === directory)
|
|
990
|
+
throw new Error('Packed assets require an owning package.json.');
|
|
991
|
+
directory = parent;
|
|
644
992
|
}
|
|
645
|
-
const parent = Path.dirname(directory);
|
|
646
|
-
if (parent === directory)
|
|
647
|
-
throw new Error('Packed assets require an owning package.json.');
|
|
648
|
-
directory = parent;
|
|
649
993
|
}
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
.
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
994
|
+
const assetUrls = new Map();
|
|
995
|
+
for (const [key, target] of Object.entries(result.sharedAssets ?? {})) {
|
|
996
|
+
const raw = target.split(/[?#]/)[0];
|
|
997
|
+
const identity = result.sharedAssetOwners?.[key];
|
|
998
|
+
const path = sourceFile(decodeURIComponent(raw)) ??
|
|
999
|
+
(Path.isAbsolute(raw) ? decodeURIComponent(raw) : undefined);
|
|
1000
|
+
if (!path)
|
|
1001
|
+
throw new Error('Asset path escapes the Vite graph.');
|
|
1002
|
+
const asset = await (async () => {
|
|
1003
|
+
try {
|
|
1004
|
+
return { file: await Fs.realpath(path), package: false };
|
|
1005
|
+
}
|
|
1006
|
+
catch (error) {
|
|
1007
|
+
if (error.code !== 'ENOENT' ||
|
|
1008
|
+
!identity ||
|
|
1009
|
+
!sourceFile(identity))
|
|
1010
|
+
throw error;
|
|
1011
|
+
const importer = sourceFile(identity);
|
|
1012
|
+
const specifier = Path.relative(Path.dirname(importer), path)
|
|
1013
|
+
.split(Path.sep)
|
|
1014
|
+
.join('/');
|
|
1015
|
+
if (specifier.startsWith('.') || Path.isAbsolute(specifier))
|
|
1016
|
+
throw error;
|
|
1017
|
+
const resolved = await host.resolve(specifier, importer);
|
|
1018
|
+
if (!resolved || resolved.external)
|
|
1019
|
+
throw error;
|
|
1020
|
+
return {
|
|
1021
|
+
file: await Fs.realpath(resolved.id.split('?')[0]),
|
|
1022
|
+
package: true,
|
|
1023
|
+
};
|
|
1024
|
+
}
|
|
1025
|
+
})();
|
|
1026
|
+
const file = asset.file;
|
|
1027
|
+
const owner = identity && sourceFile(identity)
|
|
1028
|
+
? sourceRoot(sourceFile(identity))
|
|
1029
|
+
: identity
|
|
1030
|
+
? owners.get(identity)
|
|
1031
|
+
: undefined;
|
|
1032
|
+
const relative = owner ? Path.relative(owner, file) : '..';
|
|
1033
|
+
if (!asset.package &&
|
|
1034
|
+
(relative === '..' ||
|
|
1035
|
+
relative.startsWith(`..${Path.sep}`) ||
|
|
1036
|
+
Path.isAbsolute(relative)))
|
|
1037
|
+
throw new Error('Asset path escapes its owning package.');
|
|
1038
|
+
host.watch(file);
|
|
1039
|
+
files.add(file);
|
|
1040
|
+
let url;
|
|
1041
|
+
if (asset.package || /[?#%]/.test(file)) {
|
|
1042
|
+
if (entry.environment.mode === 'build') {
|
|
1043
|
+
url = await host.asset(file);
|
|
1044
|
+
if (target.slice(raw.length))
|
|
1045
|
+
url += `$_${target.slice(raw.length)}__`;
|
|
1046
|
+
}
|
|
1047
|
+
else {
|
|
1048
|
+
url = `/@zyzz/asset/${Crypto.hash('sha256', file)}/${encodeURIComponent(Path.basename(file))}`;
|
|
1049
|
+
assets.set(url, file);
|
|
1050
|
+
url += target.slice(raw.length);
|
|
1051
|
+
}
|
|
703
1052
|
}
|
|
704
1053
|
else {
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
1054
|
+
// Vite reads `/@fs/` URLs with `decodeURI`, which keeps reserved escapes
|
|
1055
|
+
// such as `%40` in scoped package paths, so those characters stay raw.
|
|
1056
|
+
const pathname = encodeURI(file.replaceAll('\\', '/'));
|
|
1057
|
+
url = `/@fs/${pathname}${target.slice(raw.length)}`;
|
|
708
1058
|
}
|
|
1059
|
+
assetUrls.set(target, url);
|
|
709
1060
|
}
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
return AtRules.relocateImport(rule.value, assetUrls.get(target));
|
|
737
|
-
},
|
|
738
|
-
Url(url) {
|
|
739
|
-
const target = result.sharedAssets?.[url.url];
|
|
740
|
-
if (!target)
|
|
741
|
-
return;
|
|
742
|
-
return { ...url, url: assetUrls.get(target) };
|
|
1061
|
+
const shared = !Object.keys(result.sharedAssets ?? {}).length
|
|
1062
|
+
? {
|
|
1063
|
+
code: new TextEncoder().encode(result.sharedCss ?? ''),
|
|
1064
|
+
map: new TextEncoder().encode(JSON.stringify(result.sharedCssMap ??
|
|
1065
|
+
Mapping.toEncodedMap(new Mapping.GenMapping()))),
|
|
1066
|
+
}
|
|
1067
|
+
: AtRules.transform({
|
|
1068
|
+
filename: 'zyzz.shared.css',
|
|
1069
|
+
code: new TextEncoder().encode(result.sharedCss ?? ''),
|
|
1070
|
+
sourceMap: true,
|
|
1071
|
+
inputSourceMap: JSON.stringify(result.sharedCssMap ??
|
|
1072
|
+
Mapping.toEncodedMap(new Mapping.GenMapping())),
|
|
1073
|
+
visitor: {
|
|
1074
|
+
Rule(rule) {
|
|
1075
|
+
if (rule.type !== 'import')
|
|
1076
|
+
return;
|
|
1077
|
+
const target = result.sharedAssets?.[rule.value.url];
|
|
1078
|
+
if (target)
|
|
1079
|
+
return AtRules.relocateImport(rule.value, assetUrls.get(target));
|
|
1080
|
+
},
|
|
1081
|
+
Url(url) {
|
|
1082
|
+
const target = result.sharedAssets?.[url.url];
|
|
1083
|
+
if (!target)
|
|
1084
|
+
return;
|
|
1085
|
+
return { ...url, url: assetUrls.get(target) };
|
|
1086
|
+
},
|
|
743
1087
|
},
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
1088
|
+
});
|
|
1089
|
+
const sharedMap = JSON.parse(new TextDecoder().decode(shared.map));
|
|
1090
|
+
sharedMap.sources = sharedMap.sources.map((source) => source === null ? null : (sourceFile(source) ?? source));
|
|
1091
|
+
return { css: new TextDecoder().decode(shared.code), map: sharedMap };
|
|
1092
|
+
}
|
|
1093
|
+
const sharedOutput = entry.environment.mode !== 'dev' || allSources
|
|
1094
|
+
? await renderShared()
|
|
1095
|
+
: {
|
|
1096
|
+
css: '',
|
|
1097
|
+
map: Mapping.toEncodedMap(new Mapping.GenMapping()),
|
|
1098
|
+
};
|
|
748
1099
|
const output = result.modules[sourceId(entry.file)];
|
|
749
1100
|
entry.files = files;
|
|
750
|
-
|
|
1101
|
+
const value = {
|
|
751
1102
|
imports,
|
|
752
1103
|
modules: result.modules,
|
|
753
1104
|
code: output.code,
|
|
@@ -755,8 +1106,10 @@ export function zyzz(options = {}) {
|
|
|
755
1106
|
configurations,
|
|
756
1107
|
connections: staged,
|
|
757
1108
|
css: output.css,
|
|
758
|
-
|
|
759
|
-
|
|
1109
|
+
// Edits compare the graph's stylesheet, which precedes asset relocation.
|
|
1110
|
+
graphSharedCss: result.sharedCss ?? '',
|
|
1111
|
+
sharedCss: sharedOutput.css,
|
|
1112
|
+
sharedCssMap: sharedOutput.map,
|
|
760
1113
|
cssMap: {
|
|
761
1114
|
...output.cssMap,
|
|
762
1115
|
sources: output.cssMap.sources.map((source) => source === null ? null : (sourceFile(source) ?? source)),
|
|
@@ -766,6 +1119,7 @@ export function zyzz(options = {}) {
|
|
|
766
1119
|
sources: output.map.sources.map((source) => source === null ? null : (sourceFile(source) ?? source)),
|
|
767
1120
|
},
|
|
768
1121
|
};
|
|
1122
|
+
return value;
|
|
769
1123
|
}
|
|
770
1124
|
/** Compiles every discovered source once so index.html can inline each configuration's script before modules load. */
|
|
771
1125
|
async function initializations(server) {
|
|
@@ -773,8 +1127,8 @@ export function zyzz(options = {}) {
|
|
|
773
1127
|
reads.delete(environment);
|
|
774
1128
|
const host = {
|
|
775
1129
|
asset: async (file) => file,
|
|
776
|
-
resolve: (
|
|
777
|
-
watch: (file) => server
|
|
1130
|
+
resolve: resolver(environment),
|
|
1131
|
+
watch: (file) => watch(server, file),
|
|
778
1132
|
};
|
|
779
1133
|
const sources = await discover(environment, host);
|
|
780
1134
|
let entry = initializers.get(environment);
|
|
@@ -783,8 +1137,9 @@ export function zyzz(options = {}) {
|
|
|
783
1137
|
const file = sources.keys().next().value;
|
|
784
1138
|
if (file === undefined)
|
|
785
1139
|
return { catalogs: new Map(), edges: new Map() };
|
|
1140
|
+
// Whole-project compiles keep their own cache beside the transforms' merged graph.
|
|
786
1141
|
entry = {
|
|
787
|
-
compiler:
|
|
1142
|
+
compiler: initializers.get(environment)?.compiler ?? Graph.create(),
|
|
788
1143
|
environment,
|
|
789
1144
|
file,
|
|
790
1145
|
files: new Set([file]),
|
|
@@ -908,13 +1263,116 @@ export function zyzz(options = {}) {
|
|
|
908
1263
|
.filter(([key]) => contracts.has(key.split('\0')[0]))
|
|
909
1264
|
.map(([, configuration]) => configuration);
|
|
910
1265
|
}
|
|
1266
|
+
/**
|
|
1267
|
+
* The modules an edit invalidates: those whose code or stylesheet differs from what Vite served.
|
|
1268
|
+
* A concurrent compile can already hold the edit, so outputs are not compared with each other.
|
|
1269
|
+
*/
|
|
1270
|
+
async function changes(environment, after, served, modules, timestamp, edited, deleted) {
|
|
1271
|
+
// Portable build adapters share this module and do not require Vite.
|
|
1272
|
+
const { transformWithOxc } = await import('vite');
|
|
1273
|
+
const code = new Set();
|
|
1274
|
+
const css = new Set();
|
|
1275
|
+
for (const [entry, { code: current, css: stylesheet, imports },] of served.entries) {
|
|
1276
|
+
const id = sourceId(entry.file);
|
|
1277
|
+
const next = after.modules[id];
|
|
1278
|
+
if (!next)
|
|
1279
|
+
continue;
|
|
1280
|
+
// A stylesheet Vite never loaded has no module to invalidate.
|
|
1281
|
+
if (stylesheet !== undefined && stylesheet !== next.css)
|
|
1282
|
+
css.add(entry.file);
|
|
1283
|
+
// A stylesheet request can compile a module whose code was never transformed here.
|
|
1284
|
+
if (current === undefined)
|
|
1285
|
+
continue;
|
|
1286
|
+
if (imports !== JSON.stringify(projects.get(environment)?.imports[id])) {
|
|
1287
|
+
code.add(entry.file);
|
|
1288
|
+
continue;
|
|
1289
|
+
}
|
|
1290
|
+
if (current === next.code)
|
|
1291
|
+
continue;
|
|
1292
|
+
// Generated type assertions can change without changing runtime bindings.
|
|
1293
|
+
const [left, right] = await Promise.all([
|
|
1294
|
+
transformWithOxc(current, entry.file, { sourcemap: false }),
|
|
1295
|
+
transformWithOxc(next.code, entry.file, { sourcemap: false }),
|
|
1296
|
+
]);
|
|
1297
|
+
if (left.code !== right.code)
|
|
1298
|
+
code.add(entry.file);
|
|
1299
|
+
}
|
|
1300
|
+
const stylesheets = new Set();
|
|
1301
|
+
if (deleted !== undefined)
|
|
1302
|
+
stylesheets.add(cssId(deleted));
|
|
1303
|
+
if (served.shared !== undefined &&
|
|
1304
|
+
served.shared !== (after.sharedCss ?? ''))
|
|
1305
|
+
stylesheets.add(sharedId);
|
|
1306
|
+
for (const file of css)
|
|
1307
|
+
stylesheets.add(cssId(file));
|
|
1308
|
+
// Compiled modules the edit left unchanged skip Vite's own update; other modules, such as
|
|
1309
|
+
// plain JavaScript or assets, keep it.
|
|
1310
|
+
const affected = new Set(modules.filter((module) => {
|
|
1311
|
+
if (!module.id)
|
|
1312
|
+
return module.type !== 'asset';
|
|
1313
|
+
const file = normalize(module.id);
|
|
1314
|
+
return Object.hasOwn(after.modules, sourceId(file))
|
|
1315
|
+
? code.has(file)
|
|
1316
|
+
: true;
|
|
1317
|
+
}));
|
|
1318
|
+
for (const file of code) {
|
|
1319
|
+
const module = environment.moduleGraph.getModuleById(file);
|
|
1320
|
+
if (!module)
|
|
1321
|
+
continue;
|
|
1322
|
+
environment.moduleGraph.invalidateModule(module, new Set(), timestamp, true);
|
|
1323
|
+
affected.add(module);
|
|
1324
|
+
}
|
|
1325
|
+
const sheets = [];
|
|
1326
|
+
// The edited module's stylesheet updates first, ahead of stylesheets other modules share.
|
|
1327
|
+
for (const id of [...stylesheets].sort((left, right) => Number(right === cssId(edited)) - Number(left === cssId(edited)))) {
|
|
1328
|
+
const module = environment.moduleGraph.getModuleById(id);
|
|
1329
|
+
if (!module)
|
|
1330
|
+
continue;
|
|
1331
|
+
environment.moduleGraph.invalidateModule(module, new Set(), timestamp, true);
|
|
1332
|
+
sheets.push(module);
|
|
1333
|
+
}
|
|
1334
|
+
// A stylesheet accepts its own updates and only its module imports it, so it is never within
|
|
1335
|
+
// an import cycle. Vite proves that by walking every importer of the importing module, which
|
|
1336
|
+
// costs each stylesheet of a shared edit the whole graph, so stylesheet-only edits send the
|
|
1337
|
+
// same update directly. Edits that also change code keep Vite's single batched update.
|
|
1338
|
+
if (!affected.size &&
|
|
1339
|
+
sheets.length &&
|
|
1340
|
+
sheets.every((module) => module.isSelfAccepting)) {
|
|
1341
|
+
const updates = sheets.map((module) => {
|
|
1342
|
+
const path = module.url.startsWith('/') || module.url.startsWith('.')
|
|
1343
|
+
? module.url
|
|
1344
|
+
: `/@id/${module.url.replace('\0', '__x00__')}`;
|
|
1345
|
+
return {
|
|
1346
|
+
acceptedPath: path,
|
|
1347
|
+
explicitImportRequired: false,
|
|
1348
|
+
isWithinCircularImport: false,
|
|
1349
|
+
path,
|
|
1350
|
+
timestamp,
|
|
1351
|
+
type: 'js-update',
|
|
1352
|
+
};
|
|
1353
|
+
});
|
|
1354
|
+
environment.logger.info(`hmr update ${updates.map((update) => update.path).join(', ')}`, { timestamp: true });
|
|
1355
|
+
environment.hot.send({ type: 'update', updates });
|
|
1356
|
+
return [];
|
|
1357
|
+
}
|
|
1358
|
+
for (const module of sheets)
|
|
1359
|
+
affected.add(module);
|
|
1360
|
+
return [...affected];
|
|
1361
|
+
}
|
|
911
1362
|
return {
|
|
912
1363
|
buildStart() {
|
|
1364
|
+
edgeMemos.delete(this.environment);
|
|
913
1365
|
reads.delete(this.environment);
|
|
1366
|
+
lookups.delete(this.environment);
|
|
914
1367
|
},
|
|
915
1368
|
config: {
|
|
916
1369
|
order: 'post',
|
|
917
|
-
handler(config) {
|
|
1370
|
+
handler(config, env) {
|
|
1371
|
+
// Ordered after other config hooks, so plugin-provided aliases are visible.
|
|
1372
|
+
optimizeRuntime =
|
|
1373
|
+
env.command === 'serve' &&
|
|
1374
|
+
options.compiler !== false &&
|
|
1375
|
+
installed(config);
|
|
918
1376
|
if (native)
|
|
919
1377
|
return;
|
|
920
1378
|
// Inline color-scheme changes cannot initialize Lightning's lowered helpers.
|
|
@@ -943,7 +1401,30 @@ export function zyzz(options = {}) {
|
|
|
943
1401
|
};
|
|
944
1402
|
},
|
|
945
1403
|
},
|
|
1404
|
+
configEnvironment: {
|
|
1405
|
+
// Plugins without enforce settle their environment options first.
|
|
1406
|
+
order: 'post',
|
|
1407
|
+
handler(name, config) {
|
|
1408
|
+
if (!optimizeRuntime)
|
|
1409
|
+
return;
|
|
1410
|
+
const optimizeDeps = config.optimizeDeps ?? {};
|
|
1411
|
+
const consumer = config.consumer ?? (name === 'client' ? 'client' : 'server');
|
|
1412
|
+
// Vite defaults server environments to noDiscovery. An include list there
|
|
1413
|
+
// would create an explicit optimizer that otherwise never exists.
|
|
1414
|
+
if (optimizeDeps.noDiscovery ?? consumer === 'server')
|
|
1415
|
+
return;
|
|
1416
|
+
if (optimizeDeps.include?.includes(runtime) ||
|
|
1417
|
+
optimizeDeps.exclude?.some((id) => id === 'zyzz' || id === runtime))
|
|
1418
|
+
return;
|
|
1419
|
+
// Vite scans authored source, so the runtime import that compilation
|
|
1420
|
+
// adds first appears while a request transforms a module. Discovering
|
|
1421
|
+
// it then re-runs the optimizer, and modules loaded before the re-run
|
|
1422
|
+
// can keep a separate copy of a shared dependency such as React.
|
|
1423
|
+
return { optimizeDeps: { include: [runtime] } };
|
|
1424
|
+
},
|
|
1425
|
+
},
|
|
946
1426
|
configureServer(server) {
|
|
1427
|
+
devServer = server;
|
|
947
1428
|
// Directory watches belong to the server, not module import dependencies.
|
|
948
1429
|
server.watcher.add(directories.slice(1));
|
|
949
1430
|
server.middlewares.use((request, response, next) => {
|
|
@@ -971,6 +1452,12 @@ export function zyzz(options = {}) {
|
|
|
971
1452
|
return file;
|
|
972
1453
|
}))),
|
|
973
1454
|
];
|
|
1455
|
+
// Portable build adapters share this module and do not require Vite.
|
|
1456
|
+
const { createFilter } = await import('vite');
|
|
1457
|
+
const patterns = [...configFiles, ...(options.exclude ?? [])];
|
|
1458
|
+
filter = createFilter(null, patterns, { resolve: root });
|
|
1459
|
+
exclusions.clear();
|
|
1460
|
+
roots.clear();
|
|
974
1461
|
if (native)
|
|
975
1462
|
return;
|
|
976
1463
|
const lightning = (config.css.lightningcss ??= {});
|
|
@@ -1033,10 +1520,46 @@ export function zyzz(options = {}) {
|
|
|
1033
1520
|
},
|
|
1034
1521
|
},
|
|
1035
1522
|
async watchChange(file, change) {
|
|
1523
|
+
// A removed file leaves the watcher, so a later compile that reads it again adds it again.
|
|
1524
|
+
if (change.event === 'delete')
|
|
1525
|
+
watched.delete(file);
|
|
1036
1526
|
await updateDiscovery(this.environment, file, change.event);
|
|
1037
1527
|
},
|
|
1038
1528
|
async hotUpdate({ file, modules, timestamp, type, server }) {
|
|
1039
1529
|
const state = entries(this.environment);
|
|
1530
|
+
const before = projects.get(this.environment)?.outcome;
|
|
1531
|
+
// Concurrent transforms update entries while this compiles, so the served output is read now.
|
|
1532
|
+
const served = {
|
|
1533
|
+
entries: new Map([...state.values()].map((entry) => [
|
|
1534
|
+
entry,
|
|
1535
|
+
{ code: entry.code, css: entry.css, imports: entry.imports },
|
|
1536
|
+
])),
|
|
1537
|
+
shared: servedShared.get(this.environment),
|
|
1538
|
+
};
|
|
1539
|
+
// The merged development graph compares module outputs before and after the edit, so
|
|
1540
|
+
// only modules whose code or stylesheet changed update.
|
|
1541
|
+
if (!native && before && 'result' in before) {
|
|
1542
|
+
await updateDiscovery(this.environment, file, type);
|
|
1543
|
+
// The edited module's own entry, or one whose closure reads it, merges its new source.
|
|
1544
|
+
// A contribution source no module imports joins every closure.
|
|
1545
|
+
const first = state.get(file) ??
|
|
1546
|
+
[...state.values()].find((entry) => entry.files.has(file)) ??
|
|
1547
|
+
(eager(file, this.environment)
|
|
1548
|
+
? state.values().next().value
|
|
1549
|
+
: undefined);
|
|
1550
|
+
if (!first && !removed.get(this.environment)?.has(file))
|
|
1551
|
+
return modules;
|
|
1552
|
+
const compiled = first && type === 'update' && eligible(file)
|
|
1553
|
+
? await compile(first, {
|
|
1554
|
+
resolve: resolver(this.environment),
|
|
1555
|
+
watch: (file) => watch(server, file),
|
|
1556
|
+
asset: async (file) => file,
|
|
1557
|
+
}).then(() => true, () => false)
|
|
1558
|
+
: false;
|
|
1559
|
+
const after = projects.get(this.environment)?.outcome;
|
|
1560
|
+
if (compiled && after && 'result' in after)
|
|
1561
|
+
return await changes(this.environment, after.result, served, modules, timestamp, file, type === 'delete' ? file : undefined);
|
|
1562
|
+
}
|
|
1040
1563
|
const previous = [...state.values()].map((entry) => ({
|
|
1041
1564
|
code: entry.code,
|
|
1042
1565
|
file: entry.file,
|
|
@@ -1049,10 +1572,8 @@ export function zyzz(options = {}) {
|
|
|
1049
1572
|
// Failed edits retain normal invalidation so Vite reports errors on reload.
|
|
1050
1573
|
const output = first && type === 'update' && eligible(file)
|
|
1051
1574
|
? await compile(first, {
|
|
1052
|
-
resolve: (
|
|
1053
|
-
watch: (file) =>
|
|
1054
|
-
server.watcher.add(file);
|
|
1055
|
-
},
|
|
1575
|
+
resolve: resolver(this.environment),
|
|
1576
|
+
watch: (file) => watch(server, file),
|
|
1056
1577
|
asset: async (file) => file,
|
|
1057
1578
|
}, undefined, true, true).catch(() => undefined)
|
|
1058
1579
|
: undefined;
|
|
@@ -1113,17 +1634,40 @@ export function zyzz(options = {}) {
|
|
|
1113
1634
|
const file = id === sharedId
|
|
1114
1635
|
? undefined
|
|
1115
1636
|
: decodeURIComponent(id.slice(prefix.length, -4));
|
|
1116
|
-
|
|
1637
|
+
let entry = file === undefined
|
|
1117
1638
|
? entries(this.environment).values().next().value
|
|
1118
1639
|
: entries(this.environment).get(file);
|
|
1119
1640
|
// A pruned JavaScript module can leave its stylesheet loaded in the browser.
|
|
1120
1641
|
if (!entry && file && removed.get(this.environment)?.has(file))
|
|
1121
1642
|
return '';
|
|
1643
|
+
// Server rendering links the stylesheets of modules this environment has not transformed
|
|
1644
|
+
// yet, so a development request compiles the module where the browser loads it.
|
|
1645
|
+
if (!entry && this.environment.mode === 'dev') {
|
|
1646
|
+
const anchor = file ??
|
|
1647
|
+
(await discover(this.environment, {
|
|
1648
|
+
asset: async (file) => file,
|
|
1649
|
+
resolve: resolver(this.environment),
|
|
1650
|
+
watch: watcher(this),
|
|
1651
|
+
}))
|
|
1652
|
+
.keys()
|
|
1653
|
+
.next().value;
|
|
1654
|
+
if (anchor !== undefined && eligible(anchor)) {
|
|
1655
|
+
entry = {
|
|
1656
|
+
compiler: compiler(this.environment),
|
|
1657
|
+
environment: this.environment,
|
|
1658
|
+
file: anchor,
|
|
1659
|
+
files: new Set([anchor]),
|
|
1660
|
+
};
|
|
1661
|
+
entries(this.environment).set(anchor, entry);
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1122
1664
|
if (!entry)
|
|
1123
1665
|
throw new Error(`Unknown Zyzz stylesheet: ${file}`);
|
|
1124
1666
|
const output = await compile(entry, {
|
|
1125
|
-
resolve:
|
|
1126
|
-
|
|
1667
|
+
resolve: this.environment.mode === 'dev'
|
|
1668
|
+
? resolver(this.environment)
|
|
1669
|
+
: (source, importer) => this.resolve(source, importer),
|
|
1670
|
+
watch: watcher(this),
|
|
1127
1671
|
asset: async (file) => {
|
|
1128
1672
|
const reference = this.emitFile({
|
|
1129
1673
|
type: 'asset',
|
|
@@ -1137,12 +1681,18 @@ export function zyzz(options = {}) {
|
|
|
1137
1681
|
const transport = (css) => this.environment.config.command === 'build'
|
|
1138
1682
|
? AtRules.rename(AtRules.rename(Namespaces.protect(css), 'font-feature-values', '-zyzz-ffv-000000000'), 'font-palette-values', '-zyzz-fpv-000000000')
|
|
1139
1683
|
: css;
|
|
1140
|
-
if (id === sharedId)
|
|
1684
|
+
if (id === sharedId) {
|
|
1685
|
+
servedShared.set(this.environment, output.graphSharedCss);
|
|
1141
1686
|
return {
|
|
1142
1687
|
code: transport(output.sharedCss),
|
|
1143
1688
|
map: JSON.stringify(output.sharedCssMap),
|
|
1144
1689
|
};
|
|
1145
|
-
|
|
1690
|
+
}
|
|
1691
|
+
entry.css = output.css;
|
|
1692
|
+
return {
|
|
1693
|
+
code: transport(output.css),
|
|
1694
|
+
map: JSON.stringify(output.cssMap),
|
|
1695
|
+
};
|
|
1146
1696
|
},
|
|
1147
1697
|
name: 'zyzz',
|
|
1148
1698
|
resolveId(id) {
|
|
@@ -1165,9 +1715,12 @@ export function zyzz(options = {}) {
|
|
|
1165
1715
|
state.set(id, entry);
|
|
1166
1716
|
removed.get(this.environment)?.delete(id);
|
|
1167
1717
|
}
|
|
1718
|
+
entry.source = code;
|
|
1168
1719
|
const output = await compile(entry, {
|
|
1169
|
-
resolve:
|
|
1170
|
-
|
|
1720
|
+
resolve: this.environment.mode === 'dev'
|
|
1721
|
+
? resolver(this.environment)
|
|
1722
|
+
: (source, importer) => this.resolve(source, importer),
|
|
1723
|
+
watch: watcher(this),
|
|
1171
1724
|
asset: async (file) => {
|
|
1172
1725
|
const reference = this.emitFile({
|
|
1173
1726
|
type: 'asset',
|
|
@@ -1184,53 +1737,23 @@ export function zyzz(options = {}) {
|
|
|
1184
1737
|
return { code: output.code, map: JSON.stringify(output.map) };
|
|
1185
1738
|
// Keep the CSS dependency even when the current graph has no live rules.
|
|
1186
1739
|
// Later edits can introduce styles without changing this import boundary.
|
|
1187
|
-
const
|
|
1188
|
-
sourceType: 'module',
|
|
1189
|
-
});
|
|
1190
|
-
let offset = output.code.startsWith('#!')
|
|
1191
|
-
? output.code.indexOf('\n') + 1
|
|
1192
|
-
: 0;
|
|
1193
|
-
for (const node of parsed.program.body) {
|
|
1194
|
-
if (node.type !== 'ExpressionStatement' || !node.directive)
|
|
1195
|
-
break;
|
|
1196
|
-
offset = node.end;
|
|
1197
|
-
}
|
|
1740
|
+
const offset = prologue(output.code);
|
|
1198
1741
|
const sharedImport = `\nimport ${JSON.stringify(sharedId)};\n`;
|
|
1199
|
-
const map = Mapping.fromMap(JSON.stringify(output.map));
|
|
1200
1742
|
// The inserted dependency shifts only generated positions after the prologue.
|
|
1201
1743
|
const before = output.code.slice(0, offset);
|
|
1202
1744
|
const insertionLine = before.split('\n').length;
|
|
1203
1745
|
const insertionColumn = before.length - (before.lastIndexOf('\n') + 1);
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
generated.column >= insertionColumn)) {
|
|
1210
|
-
if (generated.line === insertionLine)
|
|
1211
|
-
generated.column -= insertionColumn;
|
|
1212
|
-
generated.line += 2;
|
|
1213
|
-
}
|
|
1214
|
-
if (mapping.source !== undefined && mapping.original !== undefined) {
|
|
1215
|
-
const location = {
|
|
1216
|
-
generated,
|
|
1217
|
-
original: mapping.original,
|
|
1218
|
-
source: mapping.source,
|
|
1219
|
-
};
|
|
1220
|
-
if (mapping.name === undefined)
|
|
1221
|
-
Mapping.addMapping(shifted, location);
|
|
1222
|
-
else
|
|
1223
|
-
Mapping.addMapping(shifted, { ...location, name: mapping.name });
|
|
1224
|
-
}
|
|
1225
|
-
else
|
|
1226
|
-
Mapping.addMapping(shifted, { generated });
|
|
1227
|
-
}
|
|
1228
|
-
for (const [index, source] of output.map.sources.entries())
|
|
1229
|
-
if (source !== null)
|
|
1230
|
-
Mapping.setSourceContent(shifted, source, output.map.sourcesContent?.[index] ?? null);
|
|
1746
|
+
// A build compiles a fixed project, so an empty module sheet stays empty. Importing it
|
|
1747
|
+
// would link an empty stylesheet from every chunk that holds no styled module.
|
|
1748
|
+
const stylesheetImport = this.environment.mode === 'build' && !output.css.trim()
|
|
1749
|
+
? ''
|
|
1750
|
+
: `\nimport ${JSON.stringify(cssId(id))};`;
|
|
1231
1751
|
return {
|
|
1232
|
-
code: `${before}${sharedImport}${output.code.slice(offset)}
|
|
1233
|
-
map: JSON.stringify(
|
|
1752
|
+
code: `${before}${sharedImport}${output.code.slice(offset)}${stylesheetImport}`,
|
|
1753
|
+
map: JSON.stringify({
|
|
1754
|
+
...output.map,
|
|
1755
|
+
mappings: insert(output.map.mappings, insertionLine - 1, insertionColumn),
|
|
1756
|
+
}),
|
|
1234
1757
|
};
|
|
1235
1758
|
},
|
|
1236
1759
|
transformIndexHtml: {
|
|
@@ -1246,7 +1769,7 @@ export function zyzz(options = {}) {
|
|
|
1246
1769
|
return entryCatalogs(context.bundle, context.chunk);
|
|
1247
1770
|
const state = await initializations(context.server);
|
|
1248
1771
|
const environment = context.server.environments.client;
|
|
1249
|
-
return await documentCatalogs(html, context.filename, (
|
|
1772
|
+
return await documentCatalogs(html, context.filename, resolver(environment), state);
|
|
1250
1773
|
})();
|
|
1251
1774
|
// Saved preferences apply before any other script or visible content.
|
|
1252
1775
|
return Catalogs.scripts(configurations).map((children) => ({
|
|
@@ -1258,7 +1781,130 @@ export function zyzz(options = {}) {
|
|
|
1258
1781
|
},
|
|
1259
1782
|
};
|
|
1260
1783
|
}
|
|
1784
|
+
/**
|
|
1785
|
+
* The end of a module's hashbang and directive prologue, where an added import keeps directives
|
|
1786
|
+
* first. Source without a leading string literal skips parsing.
|
|
1787
|
+
*/
|
|
1788
|
+
function prologue(code) {
|
|
1789
|
+
let index = code.startsWith('#!') ? code.indexOf('\n') + 1 : 0;
|
|
1790
|
+
const start = index;
|
|
1791
|
+
for (;;) {
|
|
1792
|
+
while (index < code.length && /\s/.test(code[index]))
|
|
1793
|
+
index++;
|
|
1794
|
+
if (code.startsWith('//', index)) {
|
|
1795
|
+
const end = code.indexOf('\n', index);
|
|
1796
|
+
index = end === -1 ? code.length : end;
|
|
1797
|
+
}
|
|
1798
|
+
else if (code.startsWith('/*', index)) {
|
|
1799
|
+
const end = code.indexOf('*/', index + 2);
|
|
1800
|
+
index = end === -1 ? code.length : end + 2;
|
|
1801
|
+
}
|
|
1802
|
+
else
|
|
1803
|
+
break;
|
|
1804
|
+
}
|
|
1805
|
+
if (code[index] !== "'" && code[index] !== '"')
|
|
1806
|
+
return start;
|
|
1807
|
+
let offset = start;
|
|
1808
|
+
const { program } = Parser.parseSync('source.tsx', code, {
|
|
1809
|
+
sourceType: 'module',
|
|
1810
|
+
});
|
|
1811
|
+
for (const node of program.body) {
|
|
1812
|
+
if (node.type !== 'ExpressionStatement' || !node.directive)
|
|
1813
|
+
break;
|
|
1814
|
+
offset = node.end;
|
|
1815
|
+
}
|
|
1816
|
+
return offset;
|
|
1817
|
+
}
|
|
1818
|
+
/**
|
|
1819
|
+
* Moves encoded mappings for two lines inserted at a generated position. Segments at or past
|
|
1820
|
+
* `column` on `line` start the line after the insertion, and every later line moves down by two.
|
|
1821
|
+
* Other fields stay relative to the segment before them, which keeps its order.
|
|
1822
|
+
*/
|
|
1823
|
+
function insert(mappings, line, column) {
|
|
1824
|
+
const lines = mappings.split(';');
|
|
1825
|
+
const text = lines[line];
|
|
1826
|
+
if (text === undefined)
|
|
1827
|
+
return mappings;
|
|
1828
|
+
let generated = 0;
|
|
1829
|
+
let split = -1;
|
|
1830
|
+
let position = 0;
|
|
1831
|
+
const segments = text ? text.split(',') : [];
|
|
1832
|
+
for (const [index, segment] of segments.entries()) {
|
|
1833
|
+
const [delta] = Vlq.decode(segment);
|
|
1834
|
+
generated += delta;
|
|
1835
|
+
if (generated >= column) {
|
|
1836
|
+
split = index;
|
|
1837
|
+
position = generated;
|
|
1838
|
+
break;
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
if (split === -1) {
|
|
1842
|
+
lines.splice(line + 1, 0, '', '');
|
|
1843
|
+
return lines.join(';');
|
|
1844
|
+
}
|
|
1845
|
+
const moved = segments.slice(split);
|
|
1846
|
+
const fields = Vlq.decode(moved[0]);
|
|
1847
|
+
fields[0] = position - column;
|
|
1848
|
+
moved[0] = Vlq.encode(fields);
|
|
1849
|
+
lines.splice(line, 1, segments.slice(0, split).join(','), '', moved.join(','));
|
|
1850
|
+
return lines.join(';');
|
|
1851
|
+
}
|
|
1852
|
+
/** Whether two import resolution records hold the same entries. */
|
|
1853
|
+
function same(left, right) {
|
|
1854
|
+
if (!left)
|
|
1855
|
+
return false;
|
|
1856
|
+
const keys = Object.keys(right);
|
|
1857
|
+
if (Object.keys(left).length !== keys.length)
|
|
1858
|
+
return false;
|
|
1859
|
+
return keys.every((key) => left[key] === right[key]);
|
|
1860
|
+
}
|
|
1861
|
+
/**
|
|
1862
|
+
* Whether `zyzz/runtime` resolves inside node_modules from the Vite root. Vite
|
|
1863
|
+
* optimizes a discovered import only there, so linked and aliased copies stay
|
|
1864
|
+
* source modules beside the other entrypoints.
|
|
1865
|
+
*/
|
|
1866
|
+
function installed(config) {
|
|
1867
|
+
const aliases = config.resolve?.alias ?? {};
|
|
1868
|
+
const entries = Array.isArray(aliases)
|
|
1869
|
+
? aliases
|
|
1870
|
+
: Object.entries(aliases).map(([find, replacement]) => ({
|
|
1871
|
+
find,
|
|
1872
|
+
replacement,
|
|
1873
|
+
}));
|
|
1874
|
+
// Matches Vite's alias semantics: a string entry covers its subpaths.
|
|
1875
|
+
if (entries.some((entry) => typeof entry.find === 'string'
|
|
1876
|
+
? runtime === entry.find || runtime.startsWith(`${entry.find}/`)
|
|
1877
|
+
: entry.find.test(runtime)))
|
|
1878
|
+
return false;
|
|
1879
|
+
try {
|
|
1880
|
+
const require = createRequire(Path.join(Path.resolve(config.root ?? ''), 'package.json'));
|
|
1881
|
+
// Vite keeps a symlinked package at its node_modules path, which Node dereferences.
|
|
1882
|
+
if (config.resolve?.preserveSymlinks)
|
|
1883
|
+
return (require.resolve.paths('zyzz') ?? []).some((directory) => existsSync(Path.join(directory, 'zyzz/package.json')));
|
|
1884
|
+
return require.resolve(runtime).split(Path.sep).includes('node_modules');
|
|
1885
|
+
}
|
|
1886
|
+
catch {
|
|
1887
|
+
return false;
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
const scan = { scan: true };
|
|
1891
|
+
/**
|
|
1892
|
+
* Resolves imports read from source before other plugins transform their
|
|
1893
|
+
* importers. Marking them as scans, as Vite's dependency scanner does, keeps the
|
|
1894
|
+
* optimizer from discovering their dependencies and lets plugins tell them apart
|
|
1895
|
+
* from imports that reach the module graph.
|
|
1896
|
+
*/
|
|
1897
|
+
function resolver(environment) {
|
|
1898
|
+
return (source, importer) => environment.pluginContainer.resolveId(source, importer, scan);
|
|
1899
|
+
}
|
|
1900
|
+
// Vite and Vitest load their own configuration modules, which application code never imports.
|
|
1901
|
+
const configFiles = [
|
|
1902
|
+
'**/vite.config.*',
|
|
1903
|
+
'**/vitest.config.*',
|
|
1904
|
+
'**/vitest.workspace.*',
|
|
1905
|
+
];
|
|
1261
1906
|
const prefix = '\0zyzz:';
|
|
1907
|
+
const runtime = 'zyzz/runtime';
|
|
1262
1908
|
const sharedId = `${prefix}shared.css`;
|
|
1263
1909
|
function cssId(file) {
|
|
1264
1910
|
return `${prefix}${encodeURIComponent(file)}.css`;
|