auklet 0.2.10 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/css/vite/hmr.d.ts +19 -4
- package/dist/css/vite/hmr.js +162 -21
- package/dist/css/vite/moduleGraph/graph.d.ts +1 -0
- package/dist/css/vite/moduleGraph/graph.js +3 -0
- package/dist/css/vite/moduleGraph/persistentCache.d.ts +3 -1
- package/dist/css/vite/moduleGraph/persistentCache.js +12 -8
- package/dist/css/vite/moduleGraph/requestCache.d.ts +14 -1
- package/dist/css/vite/moduleGraph/requestCache.js +68 -5
- package/dist/css/vite/moduleGraph/styleCodeFactory.js +23 -12
- package/dist/css/vite/vitePlugin.d.ts +1 -1
- package/dist/css/vite/vitePlugin.js +53 -17
- package/package.json +1 -1
package/dist/css/vite/hmr.d.ts
CHANGED
|
@@ -1,17 +1,32 @@
|
|
|
1
1
|
import type { HotUpdateOptions, ViteDevServer } from 'vite';
|
|
2
2
|
import type { ModuleStyleGraph } from '#auklet/css/vite/moduleGraph/graph';
|
|
3
|
+
export type AukletStyleHmrOptions = {
|
|
4
|
+
pruneDelayMs?: number;
|
|
5
|
+
};
|
|
3
6
|
export declare class AukletStyleHmr {
|
|
4
7
|
private readonly graph;
|
|
5
8
|
private readonly lastUpdateTimes;
|
|
6
9
|
private suppressFullReloadUntil;
|
|
7
10
|
private readonly virtualIdsByDependency;
|
|
8
|
-
|
|
11
|
+
private readonly filesByVirtualId;
|
|
12
|
+
private readonly schedulePruneStaleVirtualDependencies;
|
|
13
|
+
private readonly pruneDelayMs;
|
|
14
|
+
constructor(graph: () => ModuleStyleGraph, options?: AukletStyleHmrOptions);
|
|
9
15
|
trackVirtualStyleDependency(file: string, virtualId: string): void;
|
|
10
|
-
|
|
16
|
+
replaceVirtualStyleDependency(virtualId: string, files: Array<string>): void;
|
|
17
|
+
hasTrackedStyleDependency(file: string, moduleGraph?: Pick<ViteDevServer['moduleGraph'], 'getModuleById'>): boolean;
|
|
18
|
+
pruneStaleVirtualDependencies(moduleGraph: Pick<ViteDevServer['moduleGraph'], 'getModuleById'>): void;
|
|
19
|
+
scheduleStaleVirtualDependencyPrune(moduleGraph: Pick<ViteDevServer['moduleGraph'], 'getModuleById'>): void;
|
|
20
|
+
cancelStaleVirtualDependencyPrune(): void;
|
|
11
21
|
installFullReloadGuard(server: Pick<ViteDevServer, 'ws'>): void;
|
|
12
|
-
handleStyleHotUpdate(context: HotUpdateOptions): never[] | undefined
|
|
13
|
-
handleSourceModuleChange(server: Pick<ViteDevServer, 'moduleGraph' | 'ws'>, file: string): boolean
|
|
22
|
+
handleStyleHotUpdate(context: HotUpdateOptions): Promise<never[] | undefined>;
|
|
23
|
+
handleSourceModuleChange(server: Pick<ViteDevServer, 'moduleGraph' | 'ws'>, file: string): Promise<boolean>;
|
|
14
24
|
private sendVirtualStyleUpdates;
|
|
25
|
+
private parseTrackedVirtualIds;
|
|
26
|
+
private getDependencyVirtualIds;
|
|
27
|
+
private parseTrackedVirtualId;
|
|
28
|
+
private createTrackedVirtualStyleResults;
|
|
29
|
+
private refreshSourceModuleUpdates;
|
|
15
30
|
private suppressFullReload;
|
|
16
31
|
private shouldSuppressFullReload;
|
|
17
32
|
private isDuplicateUpdate;
|
package/dist/css/vite/hmr.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import { isString } from 'aidly';
|
|
2
|
+
import { isString, throttle } from 'aidly';
|
|
3
3
|
import { normalizeFileKey } from '#auklet/utils';
|
|
4
4
|
import { createAukletLogger } from '#auklet/logger';
|
|
5
5
|
const FULL_RELOAD_SUPPRESS_MS = 100;
|
|
6
6
|
const DUPLICATE_UPDATE_IGNORE_MS = 500;
|
|
7
|
+
const RESOLVED_VIRTUAL_ID_PREFIX = '\0auklet-css:';
|
|
7
8
|
const logger = createAukletLogger({ scope: 'css:vite' });
|
|
8
9
|
const toBrowserVirtualPath = (id) => {
|
|
9
10
|
return `/@id/${id.replace('\0', '__x00__')}`;
|
|
@@ -11,20 +12,39 @@ const toBrowserVirtualPath = (id) => {
|
|
|
11
12
|
const getRelativeFile = (file) => {
|
|
12
13
|
return path.relative(process.cwd(), file);
|
|
13
14
|
};
|
|
14
|
-
const addVirtualStyleDependency = (virtualIdsByDependency, file, virtualId) => {
|
|
15
|
+
const addVirtualStyleDependency = (virtualIdsByDependency, filesByVirtualId, file, virtualId) => {
|
|
15
16
|
const normalizedFile = normalizeFileKey(file);
|
|
16
17
|
const values = virtualIdsByDependency.get(normalizedFile) ?? new Set();
|
|
17
18
|
values.add(virtualId);
|
|
18
19
|
virtualIdsByDependency.set(normalizedFile, values);
|
|
20
|
+
const files = filesByVirtualId.get(virtualId) ?? new Set();
|
|
21
|
+
files.add(normalizedFile);
|
|
22
|
+
filesByVirtualId.set(virtualId, files);
|
|
19
23
|
};
|
|
20
|
-
const
|
|
21
|
-
|
|
24
|
+
const removeVirtualStyleDependency = (virtualIdsByDependency, filesByVirtualId, file, virtualId) => {
|
|
25
|
+
const normalizedFile = normalizeFileKey(file);
|
|
26
|
+
const values = virtualIdsByDependency.get(normalizedFile);
|
|
27
|
+
if (values) {
|
|
28
|
+
values.delete(virtualId);
|
|
29
|
+
if (!values.size) {
|
|
30
|
+
virtualIdsByDependency.delete(normalizedFile);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const files = filesByVirtualId.get(virtualId);
|
|
34
|
+
if (files) {
|
|
35
|
+
files.delete(normalizedFile);
|
|
36
|
+
if (!files.size) {
|
|
37
|
+
filesByVirtualId.delete(virtualId);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
22
40
|
};
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
41
|
+
const sameStringSets = (left, right) => {
|
|
42
|
+
if (left.length !== right.length)
|
|
43
|
+
return false;
|
|
44
|
+
const rightSet = new Set(right);
|
|
45
|
+
if (rightSet.size !== left.length)
|
|
46
|
+
return false;
|
|
47
|
+
return left.every((item) => rightSet.has(item));
|
|
28
48
|
};
|
|
29
49
|
const createJsUpdates = (virtualIds, timestamp) => {
|
|
30
50
|
return virtualIds.map((id) => {
|
|
@@ -44,14 +64,46 @@ export class AukletStyleHmr {
|
|
|
44
64
|
lastUpdateTimes = new Map();
|
|
45
65
|
suppressFullReloadUntil = 0;
|
|
46
66
|
virtualIdsByDependency = new Map();
|
|
47
|
-
|
|
67
|
+
filesByVirtualId = new Map();
|
|
68
|
+
schedulePruneStaleVirtualDependencies;
|
|
69
|
+
pruneDelayMs;
|
|
70
|
+
constructor(graph, options = {}) {
|
|
48
71
|
this.graph = graph;
|
|
72
|
+
this.pruneDelayMs = options.pruneDelayMs ?? 2 * 60 * 1000;
|
|
73
|
+
this.schedulePruneStaleVirtualDependencies = throttle(this.pruneDelayMs, (moduleGraph) => this.pruneStaleVirtualDependencies(moduleGraph));
|
|
49
74
|
}
|
|
50
75
|
trackVirtualStyleDependency(file, virtualId) {
|
|
51
|
-
addVirtualStyleDependency(this.virtualIdsByDependency, file, virtualId);
|
|
76
|
+
addVirtualStyleDependency(this.virtualIdsByDependency, this.filesByVirtualId, file, virtualId);
|
|
77
|
+
}
|
|
78
|
+
replaceVirtualStyleDependency(virtualId, files) {
|
|
79
|
+
const normalizedFiles = new Set(files.map((file) => normalizeFileKey(file)));
|
|
80
|
+
const previousFiles = this.filesByVirtualId.get(virtualId) ?? new Set();
|
|
81
|
+
for (const file of previousFiles) {
|
|
82
|
+
if (normalizedFiles.has(file))
|
|
83
|
+
continue;
|
|
84
|
+
removeVirtualStyleDependency(this.virtualIdsByDependency, this.filesByVirtualId, file, virtualId);
|
|
85
|
+
}
|
|
86
|
+
for (const file of normalizedFiles) {
|
|
87
|
+
addVirtualStyleDependency(this.virtualIdsByDependency, this.filesByVirtualId, file, virtualId);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
hasTrackedStyleDependency(file, moduleGraph) {
|
|
91
|
+
return this.getDependencyVirtualIds(file, moduleGraph).length > 0;
|
|
92
|
+
}
|
|
93
|
+
pruneStaleVirtualDependencies(moduleGraph) {
|
|
94
|
+
for (const [normalizedFile, virtualIds] of this.virtualIdsByDependency) {
|
|
95
|
+
for (const virtualId of Array.from(virtualIds)) {
|
|
96
|
+
if (moduleGraph.getModuleById(virtualId))
|
|
97
|
+
continue;
|
|
98
|
+
removeVirtualStyleDependency(this.virtualIdsByDependency, this.filesByVirtualId, normalizedFile, virtualId);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
scheduleStaleVirtualDependencyPrune(moduleGraph) {
|
|
103
|
+
this.schedulePruneStaleVirtualDependencies(moduleGraph);
|
|
52
104
|
}
|
|
53
|
-
|
|
54
|
-
|
|
105
|
+
cancelStaleVirtualDependencyPrune() {
|
|
106
|
+
this.schedulePruneStaleVirtualDependencies.cancel();
|
|
55
107
|
}
|
|
56
108
|
installFullReloadGuard(server) {
|
|
57
109
|
const send = server.ws.send.bind(server.ws);
|
|
@@ -69,7 +121,7 @@ export class AukletStyleHmr {
|
|
|
69
121
|
send(payload);
|
|
70
122
|
});
|
|
71
123
|
}
|
|
72
|
-
handleStyleHotUpdate(context) {
|
|
124
|
+
async handleStyleHotUpdate(context) {
|
|
73
125
|
const graph = this.graph();
|
|
74
126
|
if (!graph.isSourceGraphFile(context.file) ||
|
|
75
127
|
!graph.isStyleFile(context.file)) {
|
|
@@ -81,24 +133,43 @@ export class AukletStyleHmr {
|
|
|
81
133
|
return [];
|
|
82
134
|
}
|
|
83
135
|
const updates = this.sendVirtualStyleUpdates(context.file, context.server, context.timestamp);
|
|
136
|
+
if (!updates.sent) {
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
84
139
|
logger.info(`package css hmr ${getRelativeFile(context.file)} tracked=${updates.tracked} updates=${updates.sent}`);
|
|
85
140
|
return [];
|
|
86
141
|
}
|
|
87
|
-
handleSourceModuleChange(server, file) {
|
|
142
|
+
async handleSourceModuleChange(server, file) {
|
|
88
143
|
const graph = this.graph();
|
|
89
144
|
if (!graph.isSourceGraphFile(file) || !graph.isSourceModuleFile(file)) {
|
|
90
145
|
return false;
|
|
91
146
|
}
|
|
147
|
+
const virtualIds = this.getDependencyVirtualIds(file, server.moduleGraph);
|
|
148
|
+
if (!virtualIds.length) {
|
|
149
|
+
graph.invalidateFile(file);
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
const parsedVirtualIds = this.parseTrackedVirtualIds(graph, virtualIds);
|
|
153
|
+
if (!parsedVirtualIds.length) {
|
|
154
|
+
graph.invalidateFile(file);
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
const previousResults = this.createTrackedVirtualStyleResults(graph, parsedVirtualIds);
|
|
92
158
|
graph.invalidateFile(file);
|
|
93
|
-
const updates = this.
|
|
159
|
+
const updates = await this.refreshSourceModuleUpdates(server, parsedVirtualIds, previousResults);
|
|
160
|
+
if (!updates.sent) {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
94
163
|
logger.info(`package css source hmr ${getRelativeFile(file)} tracked=${updates.tracked} updates=${updates.sent}`);
|
|
95
|
-
return
|
|
164
|
+
return updates.sent > 0;
|
|
96
165
|
}
|
|
97
166
|
sendVirtualStyleUpdates(file, server, timestamp) {
|
|
98
|
-
const virtualIds = getDependencyVirtualIds(
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
167
|
+
const virtualIds = this.getDependencyVirtualIds(file, server.moduleGraph);
|
|
168
|
+
for (const id of virtualIds) {
|
|
169
|
+
const module = server.moduleGraph.getModuleById(id);
|
|
170
|
+
if (module) {
|
|
171
|
+
server.moduleGraph.invalidateModule(module);
|
|
172
|
+
}
|
|
102
173
|
}
|
|
103
174
|
const updates = createJsUpdates(virtualIds, timestamp);
|
|
104
175
|
if (updates.length) {
|
|
@@ -112,6 +183,76 @@ export class AukletStyleHmr {
|
|
|
112
183
|
tracked: virtualIds.length,
|
|
113
184
|
};
|
|
114
185
|
}
|
|
186
|
+
parseTrackedVirtualIds(graph, virtualIds) {
|
|
187
|
+
return virtualIds
|
|
188
|
+
.map((id) => this.parseTrackedVirtualId(graph, id))
|
|
189
|
+
.filter((item) => Boolean(item));
|
|
190
|
+
}
|
|
191
|
+
getDependencyVirtualIds(file, moduleGraph) {
|
|
192
|
+
const normalizedFile = normalizeFileKey(file);
|
|
193
|
+
const virtualIds = Array.from(this.virtualIdsByDependency.get(normalizedFile) ?? []);
|
|
194
|
+
if (!moduleGraph) {
|
|
195
|
+
return virtualIds;
|
|
196
|
+
}
|
|
197
|
+
const liveVirtualIds = virtualIds.filter((virtualId) => moduleGraph.getModuleById(virtualId));
|
|
198
|
+
if (liveVirtualIds.length !== virtualIds.length) {
|
|
199
|
+
const liveSet = new Set(liveVirtualIds);
|
|
200
|
+
for (const virtualId of virtualIds) {
|
|
201
|
+
if (liveSet.has(virtualId))
|
|
202
|
+
continue;
|
|
203
|
+
removeVirtualStyleDependency(this.virtualIdsByDependency, this.filesByVirtualId, normalizedFile, virtualId);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return liveVirtualIds;
|
|
207
|
+
}
|
|
208
|
+
parseTrackedVirtualId(graph, id) {
|
|
209
|
+
const parsedId = id.startsWith(RESOLVED_VIRTUAL_ID_PREFIX)
|
|
210
|
+
? id.slice(RESOLVED_VIRTUAL_ID_PREFIX.length)
|
|
211
|
+
: id;
|
|
212
|
+
const parsed = graph.parsePackageStyleId(parsedId);
|
|
213
|
+
if (!parsed || !graph.getPackageNames().includes(parsed.packageName)) {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
return {
|
|
217
|
+
id,
|
|
218
|
+
parsed,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
createTrackedVirtualStyleResults(graph, parsedVirtualIds) {
|
|
222
|
+
const previousResults = new Map();
|
|
223
|
+
for (const item of parsedVirtualIds) {
|
|
224
|
+
previousResults.set(item.id, graph.peekPackageStyleCode(item.parsed));
|
|
225
|
+
}
|
|
226
|
+
return previousResults;
|
|
227
|
+
}
|
|
228
|
+
async refreshSourceModuleUpdates(server, parsedVirtualIds, previousResults) {
|
|
229
|
+
const graph = this.graph();
|
|
230
|
+
const changedVirtualIds = [];
|
|
231
|
+
for (const item of parsedVirtualIds) {
|
|
232
|
+
const nextResult = await graph.createPackageStyleCode(item.parsed);
|
|
233
|
+
const previousResult = previousResults.get(item.id);
|
|
234
|
+
if (!previousResult ||
|
|
235
|
+
previousResult.code !== nextResult.code ||
|
|
236
|
+
!sameStringSets(previousResult.watchFiles, nextResult.watchFiles)) {
|
|
237
|
+
changedVirtualIds.push(item.id);
|
|
238
|
+
const module = server.moduleGraph.getModuleById(item.id);
|
|
239
|
+
if (module) {
|
|
240
|
+
server.moduleGraph.invalidateModule(module);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
const updates = createJsUpdates(changedVirtualIds, Date.now());
|
|
245
|
+
if (updates.length) {
|
|
246
|
+
server.ws.send({
|
|
247
|
+
type: 'update',
|
|
248
|
+
updates,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
return {
|
|
252
|
+
sent: updates.length,
|
|
253
|
+
tracked: parsedVirtualIds.length,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
115
256
|
suppressFullReload() {
|
|
116
257
|
this.suppressFullReloadUntil = Date.now() + FULL_RELOAD_SUPPRESS_MS;
|
|
117
258
|
}
|
|
@@ -16,6 +16,7 @@ export declare class ModuleStyleGraph {
|
|
|
16
16
|
getPackageNames(): string[];
|
|
17
17
|
getWatchRoots(): Promise<string[]>;
|
|
18
18
|
createPackageStyleCode(parsed: PackageStyleId): Promise<import("#auklet/css/vite/moduleGraph/types").PackageStyleLoadResult>;
|
|
19
|
+
peekPackageStyleCode(parsed: PackageStyleId): import("#auklet/css/vite/moduleGraph/types").PackageStyleLoadResult | null;
|
|
19
20
|
invalidatePackage(packageName: string): void;
|
|
20
21
|
invalidateFile(file: string): string | null;
|
|
21
22
|
isSourceModuleFile(file: string): boolean;
|
|
@@ -60,6 +60,9 @@ export class ModuleStyleGraph {
|
|
|
60
60
|
createPackageStyleCode(parsed) {
|
|
61
61
|
return this.styleCodeFactory.createPackageStyleCode(parsed, this.requestCache);
|
|
62
62
|
}
|
|
63
|
+
peekPackageStyleCode(parsed) {
|
|
64
|
+
return this.requestCache.peekLoadResult(parsed);
|
|
65
|
+
}
|
|
63
66
|
invalidatePackage(packageName) {
|
|
64
67
|
this.requestCache.invalidatePackage(packageName);
|
|
65
68
|
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { PackageStyleLoadResult } from '#auklet/css/vite/moduleGraph/types';
|
|
2
2
|
export type PersistentStyleGraphCacheOptions = {
|
|
3
3
|
root: string;
|
|
4
|
+
maxCacheFiles?: number;
|
|
4
5
|
};
|
|
5
6
|
export declare class PersistentStyleGraphCache {
|
|
6
|
-
private static readonly
|
|
7
|
+
private static readonly lastCleanupTimes;
|
|
7
8
|
private readonly cacheRoot;
|
|
9
|
+
private readonly maxCacheFiles;
|
|
8
10
|
constructor(options: PersistentStyleGraphCacheOptions);
|
|
9
11
|
createKey(value: unknown): string;
|
|
10
12
|
read(key: string): PackageStyleLoadResult | null;
|
|
@@ -4,6 +4,7 @@ import crypto from 'node:crypto';
|
|
|
4
4
|
import { toPosixPath } from '#auklet/utils';
|
|
5
5
|
const cacheVersion = 'v1';
|
|
6
6
|
const maxCacheFiles = 5000;
|
|
7
|
+
const cleanupIntervalMs = 5 * 60 * 1000;
|
|
7
8
|
const staleCacheAgeMs = 7 * 24 * 60 * 60 * 1000;
|
|
8
9
|
const stableStringify = (value) => {
|
|
9
10
|
if (Array.isArray(value)) {
|
|
@@ -68,10 +69,12 @@ const statCachedFile = (file) => {
|
|
|
68
69
|
};
|
|
69
70
|
};
|
|
70
71
|
export class PersistentStyleGraphCache {
|
|
71
|
-
static
|
|
72
|
+
static lastCleanupTimes = new Map();
|
|
72
73
|
cacheRoot;
|
|
74
|
+
maxCacheFiles;
|
|
73
75
|
constructor(options) {
|
|
74
76
|
this.cacheRoot = path.join(options.root, 'node_modules', '.auklet', 'cache', 'vite-style', cacheVersion);
|
|
77
|
+
this.maxCacheFiles = options.maxCacheFiles ?? maxCacheFiles;
|
|
75
78
|
}
|
|
76
79
|
createKey(value) {
|
|
77
80
|
return hashValue({
|
|
@@ -96,10 +99,10 @@ export class PersistentStyleGraphCache {
|
|
|
96
99
|
}
|
|
97
100
|
}
|
|
98
101
|
write(key, result, inputFiles) {
|
|
99
|
-
const files = Array.from(new Map(inputFiles
|
|
100
|
-
.map((file) => statCachedFile(file))
|
|
101
|
-
.map((file) => [file.file, file])).values());
|
|
102
102
|
try {
|
|
103
|
+
const files = Array.from(new Map(inputFiles
|
|
104
|
+
.map((file) => statCachedFile(file))
|
|
105
|
+
.map((file) => [file.file, file])).values());
|
|
103
106
|
fs.mkdirSync(this.cacheRoot, { recursive: true });
|
|
104
107
|
fs.writeFileSync(this.getCacheFile(key), `${JSON.stringify({
|
|
105
108
|
files,
|
|
@@ -114,11 +117,12 @@ export class PersistentStyleGraphCache {
|
|
|
114
117
|
this.cleanupStaleEntries();
|
|
115
118
|
}
|
|
116
119
|
cleanupStaleEntries() {
|
|
117
|
-
|
|
120
|
+
const now = Date.now();
|
|
121
|
+
const lastCleanupTime = PersistentStyleGraphCache.lastCleanupTimes.get(this.cacheRoot) ?? 0;
|
|
122
|
+
if (now - lastCleanupTime < cleanupIntervalMs)
|
|
118
123
|
return;
|
|
119
|
-
PersistentStyleGraphCache.
|
|
124
|
+
PersistentStyleGraphCache.lastCleanupTimes.set(this.cacheRoot, now);
|
|
120
125
|
try {
|
|
121
|
-
const now = Date.now();
|
|
122
126
|
const entries = fs
|
|
123
127
|
.readdirSync(this.cacheRoot, { withFileTypes: true })
|
|
124
128
|
.filter((entry) => entry.isFile() && entry.name.endsWith('.json'))
|
|
@@ -137,7 +141,7 @@ export class PersistentStyleGraphCache {
|
|
|
137
141
|
}
|
|
138
142
|
freshEntries.push(entry);
|
|
139
143
|
}
|
|
140
|
-
const overflowCount = freshEntries.length - maxCacheFiles;
|
|
144
|
+
const overflowCount = freshEntries.length - this.maxCacheFiles;
|
|
141
145
|
if (overflowCount <= 0)
|
|
142
146
|
return;
|
|
143
147
|
for (const entry of freshEntries
|
|
@@ -4,6 +4,10 @@ import type { StyleProcessor } from '#auklet/css/core/styleProcessor';
|
|
|
4
4
|
import type { WorkspaceStyleResolver } from '#auklet/css/core/workspaceStyleResolver';
|
|
5
5
|
import type { StylePackageSource } from '#auklet/css/vite/moduleGraph/packageSource/types';
|
|
6
6
|
import type { LoadAukletConfig, PackageStyleId, PackageStyleLoadResult } from '#auklet/css/vite/moduleGraph/types';
|
|
7
|
+
type LoadResultEntry = {
|
|
8
|
+
commit?: () => PackageStyleLoadResult;
|
|
9
|
+
result: PackageStyleLoadResult;
|
|
10
|
+
};
|
|
7
11
|
export type PackageStyleContext = {
|
|
8
12
|
normalizedConfig: NormalizedAukletConfig;
|
|
9
13
|
context: ResolvedModuleStyleBuildContext;
|
|
@@ -25,9 +29,12 @@ export declare class ModuleStyleGraphRequestCache {
|
|
|
25
29
|
private readonly options;
|
|
26
30
|
private readonly contexts;
|
|
27
31
|
private readonly loadResults;
|
|
32
|
+
private readonly settledLoadResults;
|
|
28
33
|
private readonly loadResultDependencies;
|
|
34
|
+
private readonly loadResultVersions;
|
|
29
35
|
private readonly loadAukletConfig;
|
|
30
36
|
private readonly persistentCache;
|
|
37
|
+
private graphVersion;
|
|
31
38
|
constructor(options: ModuleStyleGraphRequestCacheOptions);
|
|
32
39
|
getPackageNames(): string[];
|
|
33
40
|
isKnownPackageName(packageName: string): boolean;
|
|
@@ -68,7 +75,7 @@ export declare class ModuleStyleGraphRequestCache {
|
|
|
68
75
|
sourceRoot: string;
|
|
69
76
|
styleProcessor: StyleProcessor;
|
|
70
77
|
} | null>;
|
|
71
|
-
getLoadResult(parsed: PackageStyleId, create: () => Promise<
|
|
78
|
+
getLoadResult(parsed: PackageStyleId, create: () => Promise<LoadResultEntry>): Promise<PackageStyleLoadResult>;
|
|
72
79
|
invalidatePackage(packageName: string): void;
|
|
73
80
|
readPersistentLoadResult(parsed: PackageStyleId, context: PackageStyleContext): PackageStyleLoadResult | null;
|
|
74
81
|
writePersistentLoadResult(parsed: PackageStyleId, context: PackageStyleContext, result: PackageStyleLoadResult): {
|
|
@@ -87,4 +94,10 @@ export declare class ModuleStyleGraphRequestCache {
|
|
|
87
94
|
private getLoadResultKey;
|
|
88
95
|
private getLoadResultPackageName;
|
|
89
96
|
private invalidateLoadResults;
|
|
97
|
+
private discardLoadResult;
|
|
98
|
+
peekLoadResult(parsed: PackageStyleId): PackageStyleLoadResult | null;
|
|
99
|
+
private getGraphVersion;
|
|
100
|
+
private bumpGraphVersion;
|
|
101
|
+
private bumpLoadResultVersion;
|
|
90
102
|
}
|
|
103
|
+
export {};
|
|
@@ -8,9 +8,12 @@ export class ModuleStyleGraphRequestCache {
|
|
|
8
8
|
options;
|
|
9
9
|
contexts = new Map();
|
|
10
10
|
loadResults = new Map();
|
|
11
|
+
settledLoadResults = new Map();
|
|
11
12
|
loadResultDependencies = new Map();
|
|
13
|
+
loadResultVersions = new Map();
|
|
12
14
|
loadAukletConfig;
|
|
13
15
|
persistentCache;
|
|
16
|
+
graphVersion = 0;
|
|
14
17
|
constructor(options) {
|
|
15
18
|
this.options = options;
|
|
16
19
|
this.loadAukletConfig = options.loadAukletConfig ?? loadAukletConfig;
|
|
@@ -37,14 +40,53 @@ export class ModuleStyleGraphRequestCache {
|
|
|
37
40
|
const cachedResult = this.loadResults.get(key);
|
|
38
41
|
if (cachedResult)
|
|
39
42
|
return cachedResult;
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
const version = this.bumpLoadResultVersion(key);
|
|
44
|
+
const graphVersion = this.getGraphVersion();
|
|
45
|
+
let result;
|
|
46
|
+
result = create()
|
|
47
|
+
.then(async (entry) => {
|
|
48
|
+
if (this.getGraphVersion() !== graphVersion) {
|
|
49
|
+
const currentResult = this.loadResults.get(key);
|
|
50
|
+
if (currentResult && currentResult !== result) {
|
|
51
|
+
return currentResult;
|
|
52
|
+
}
|
|
53
|
+
this.discardLoadResult(key);
|
|
54
|
+
return this.getLoadResult(parsed, create);
|
|
55
|
+
}
|
|
56
|
+
if (this.loadResultVersions.get(key) === version) {
|
|
57
|
+
this.loadResultDependencies.set(key, new Set(entry.result.dependencyPackages ?? []));
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
const committedResult = entry.commit?.();
|
|
61
|
+
if (committedResult) {
|
|
62
|
+
entry.result = committedResult;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// Persistent cache writes are best-effort and must not fail dev CSS.
|
|
67
|
+
}
|
|
68
|
+
this.settledLoadResults.set(key, entry.result);
|
|
69
|
+
return entry.result;
|
|
70
|
+
})
|
|
71
|
+
.catch((error) => {
|
|
72
|
+
if (this.getGraphVersion() !== graphVersion) {
|
|
73
|
+
const currentResult = this.loadResults.get(key);
|
|
74
|
+
if (currentResult && currentResult !== result) {
|
|
75
|
+
return currentResult;
|
|
76
|
+
}
|
|
77
|
+
this.discardLoadResult(key);
|
|
78
|
+
return this.getLoadResult(parsed, create);
|
|
79
|
+
}
|
|
80
|
+
if (this.loadResultVersions.get(key) === version) {
|
|
81
|
+
this.discardLoadResult(key);
|
|
82
|
+
}
|
|
83
|
+
throw error;
|
|
43
84
|
});
|
|
44
85
|
this.loadResults.set(key, result);
|
|
45
86
|
return result;
|
|
46
87
|
}
|
|
47
88
|
invalidatePackage(packageName) {
|
|
89
|
+
this.bumpGraphVersion();
|
|
48
90
|
this.contexts.delete(packageName);
|
|
49
91
|
this.invalidateLoadResults(packageName);
|
|
50
92
|
}
|
|
@@ -208,8 +250,29 @@ export class ModuleStyleGraphRequestCache {
|
|
|
208
250
|
continue;
|
|
209
251
|
}
|
|
210
252
|
}
|
|
211
|
-
this.
|
|
212
|
-
this.loadResultDependencies.delete(key);
|
|
253
|
+
this.discardLoadResult(key);
|
|
213
254
|
}
|
|
214
255
|
}
|
|
256
|
+
discardLoadResult(key) {
|
|
257
|
+
this.bumpGraphVersion();
|
|
258
|
+
this.bumpLoadResultVersion(key);
|
|
259
|
+
this.loadResults.delete(key);
|
|
260
|
+
this.loadResultDependencies.delete(key);
|
|
261
|
+
this.settledLoadResults.delete(key);
|
|
262
|
+
}
|
|
263
|
+
peekLoadResult(parsed) {
|
|
264
|
+
return this.settledLoadResults.get(this.getLoadResultKey(parsed)) ?? null;
|
|
265
|
+
}
|
|
266
|
+
getGraphVersion() {
|
|
267
|
+
return this.graphVersion;
|
|
268
|
+
}
|
|
269
|
+
bumpGraphVersion() {
|
|
270
|
+
this.graphVersion += 1;
|
|
271
|
+
return this.graphVersion;
|
|
272
|
+
}
|
|
273
|
+
bumpLoadResultVersion(key) {
|
|
274
|
+
const nextVersion = (this.loadResultVersions.get(key) ?? 0) + 1;
|
|
275
|
+
this.loadResultVersions.set(key, nextVersion);
|
|
276
|
+
return nextVersion;
|
|
277
|
+
}
|
|
215
278
|
}
|
|
@@ -14,19 +14,30 @@ export class StyleCodeFactory {
|
|
|
14
14
|
this.config = config;
|
|
15
15
|
}
|
|
16
16
|
async createPackageStyleCode(parsed, cache) {
|
|
17
|
-
return cache.getLoadResult(parsed, async () =>
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
return cache.getLoadResult(parsed, async () => {
|
|
18
|
+
const context = await cache.getContext(parsed);
|
|
19
|
+
if (!context) {
|
|
20
|
+
return {
|
|
21
|
+
result: {
|
|
22
|
+
code: '',
|
|
23
|
+
watchFiles: [],
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const cachedResult = cache.readPersistentLoadResult(parsed, context);
|
|
28
|
+
if (cachedResult) {
|
|
29
|
+
return {
|
|
30
|
+
result: cachedResult,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const result = this.normalizeTopLevelImports(await this.createUncachedPackageStyleCode(parsed, cache, context));
|
|
22
34
|
return {
|
|
23
|
-
|
|
24
|
-
|
|
35
|
+
result,
|
|
36
|
+
commit: () => cache.writePersistentLoadResult(parsed, context, result),
|
|
25
37
|
};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return cachedResult;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
async createUncachedPackageStyleCode(parsed, cache, context) {
|
|
30
41
|
let result;
|
|
31
42
|
if (parsed.stylePath === STYLE_ENTRY) {
|
|
32
43
|
result = await this.createStyleCode(context, cache);
|
|
@@ -45,7 +56,7 @@ export class StyleCodeFactory {
|
|
|
45
56
|
else {
|
|
46
57
|
result = await this.createSourceModuleStyleCode(context, cache, parsed.stylePath);
|
|
47
58
|
}
|
|
48
|
-
return
|
|
59
|
+
return result;
|
|
49
60
|
}
|
|
50
61
|
async createStyleCode(context, cache) {
|
|
51
62
|
const results = [];
|
|
@@ -15,6 +15,6 @@ export declare function aukletStylePlugin(options?: AukletStylePluginOptions): {
|
|
|
15
15
|
configureServer(server: ViteDevServer): Promise<void>;
|
|
16
16
|
hotUpdate: {
|
|
17
17
|
order: "pre";
|
|
18
|
-
handler(context: HotUpdateOptions): never[] | undefined
|
|
18
|
+
handler(context: HotUpdateOptions): Promise<never[] | undefined>;
|
|
19
19
|
};
|
|
20
20
|
};
|
|
@@ -2,10 +2,12 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { AukletStyleHmr } from '#auklet/css/vite/hmr';
|
|
4
4
|
import { ModuleStyleGraph } from '#auklet/css/vite/moduleGraph/graph';
|
|
5
|
+
import { createAukletLogger } from '#auklet/logger';
|
|
5
6
|
const WORKSPACE_FILE = 'pnpm-workspace.yaml';
|
|
6
7
|
const VIRTUAL_ID_PREFIX = 'virtual:auklet-css:';
|
|
7
8
|
const RESOLVED_VIRTUAL_ID_PREFIX = '\0auklet-css:';
|
|
8
9
|
const BROWSER_VIRTUAL_ID_PREFIX = 'auklet-css:';
|
|
10
|
+
const logger = createAukletLogger({ scope: 'css:vite' });
|
|
9
11
|
const stripQuery = (id) => id.split('?')[0];
|
|
10
12
|
const toResolvedVirtualId = (id) => {
|
|
11
13
|
if (id.startsWith(RESOLVED_VIRTUAL_ID_PREFIX)) {
|
|
@@ -42,6 +44,20 @@ const resolveGraphRoot = (mode, viteRoot) => {
|
|
|
42
44
|
return findWorkspaceRoot(viteRoot) ?? process.cwd();
|
|
43
45
|
return viteRoot;
|
|
44
46
|
};
|
|
47
|
+
const createWatcherErrorPayload = (error, file) => {
|
|
48
|
+
const err = error instanceof Error
|
|
49
|
+
? error
|
|
50
|
+
: new Error(typeof error === 'string' ? error : String(error));
|
|
51
|
+
return {
|
|
52
|
+
type: 'error',
|
|
53
|
+
err: {
|
|
54
|
+
message: err.message,
|
|
55
|
+
stack: err.stack ?? err.message,
|
|
56
|
+
plugin: 'auklet-css',
|
|
57
|
+
id: file,
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
};
|
|
45
61
|
const invalidateVirtualModules = (server, graph) => {
|
|
46
62
|
const modules = [];
|
|
47
63
|
for (const packageName of graph.getPackageNames()) {
|
|
@@ -57,6 +73,7 @@ const invalidateVirtualModules = (server, graph) => {
|
|
|
57
73
|
};
|
|
58
74
|
export function aukletStylePlugin(options = {}) {
|
|
59
75
|
let graph = null;
|
|
76
|
+
let moduleGraph = null;
|
|
60
77
|
const getGraph = () => {
|
|
61
78
|
if (!graph) {
|
|
62
79
|
graph = createModuleStyleGraph(options, process.cwd());
|
|
@@ -93,15 +110,24 @@ export function aukletStylePlugin(options = {}) {
|
|
|
93
110
|
if (!parsed)
|
|
94
111
|
return null;
|
|
95
112
|
const result = await graph.createPackageStyleCode(parsed);
|
|
113
|
+
if (moduleGraph) {
|
|
114
|
+
hmr.pruneStaleVirtualDependencies(moduleGraph);
|
|
115
|
+
}
|
|
116
|
+
hmr.replaceVirtualStyleDependency(id, result.watchFiles);
|
|
96
117
|
for (const file of result.watchFiles) {
|
|
97
|
-
hmr.trackVirtualStyleDependency(file, id);
|
|
98
118
|
this.addWatchFile?.(file);
|
|
99
119
|
}
|
|
100
120
|
return result.code;
|
|
101
121
|
},
|
|
102
122
|
async configureServer(server) {
|
|
103
123
|
const graph = getGraph();
|
|
124
|
+
moduleGraph = server.moduleGraph;
|
|
104
125
|
hmr.installFullReloadGuard(server);
|
|
126
|
+
const close = server.close.bind(server);
|
|
127
|
+
server.close = (async () => {
|
|
128
|
+
hmr.cancelStaleVirtualDependencyPrune();
|
|
129
|
+
return close();
|
|
130
|
+
});
|
|
105
131
|
server.watcher.add(await graph.getWatchRoots());
|
|
106
132
|
const invalidateStyleGraph = (file) => {
|
|
107
133
|
if (!graph.isSourceGraphFile(file))
|
|
@@ -117,23 +143,33 @@ export function aukletStylePlugin(options = {}) {
|
|
|
117
143
|
};
|
|
118
144
|
server.watcher.on('add', reloadStyleGraph);
|
|
119
145
|
server.watcher.on('unlink', reloadStyleGraph);
|
|
120
|
-
server.watcher.on('change', (file) => {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
file
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
146
|
+
server.watcher.on('change', async (file) => {
|
|
147
|
+
try {
|
|
148
|
+
hmr.scheduleStaleVirtualDependencyPrune(server.moduleGraph);
|
|
149
|
+
if (graph.isStyleConfigFile(file)) {
|
|
150
|
+
reloadStyleGraph(file);
|
|
151
|
+
}
|
|
152
|
+
else if (graph.isStyleFile(file)) {
|
|
153
|
+
graph.invalidateFile(file);
|
|
154
|
+
if (hmr.hasTrackedStyleDependency(file, server.moduleGraph)) {
|
|
155
|
+
await hmr.handleStyleHotUpdate({
|
|
156
|
+
file,
|
|
157
|
+
modules: [],
|
|
158
|
+
server,
|
|
159
|
+
timestamp: Date.now(),
|
|
160
|
+
type: 'update',
|
|
161
|
+
read: async () => '',
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
else if (graph.isSourceModuleFile(file)) {
|
|
166
|
+
await hmr.handleSourceModuleChange(server, file);
|
|
167
|
+
}
|
|
134
168
|
}
|
|
135
|
-
|
|
136
|
-
|
|
169
|
+
catch (error) {
|
|
170
|
+
logger.error('package css change watcher failed');
|
|
171
|
+
logger.error(error);
|
|
172
|
+
server.ws.send(createWatcherErrorPayload(error, file));
|
|
137
173
|
}
|
|
138
174
|
});
|
|
139
175
|
},
|