auklet 0.2.10 → 0.3.1
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/core/styleProcessor.d.ts +1 -0
- package/dist/css/core/styleProcessor.js +15 -0
- package/dist/css/vite/hmr.d.ts +30 -6
- package/dist/css/vite/hmr.js +243 -38
- package/dist/css/vite/moduleGraph/graph.d.ts +1 -0
- package/dist/css/vite/moduleGraph/graph.js +3 -0
- package/dist/css/vite/moduleGraph/loadResult.d.ts +4 -0
- package/dist/css/vite/moduleGraph/loadResult.js +14 -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 +15 -1
- package/dist/css/vite/moduleGraph/requestCache.js +68 -5
- package/dist/css/vite/moduleGraph/styleCodeFactory.d.ts +1 -0
- package/dist/css/vite/moduleGraph/styleCodeFactory.js +77 -14
- package/dist/css/vite/moduleGraph/types.d.ts +6 -0
- package/dist/css/vite/vitePlugin.d.ts +1 -1
- package/dist/css/vite/vitePlugin.js +55 -17
- package/package.json +1 -1
|
@@ -23,6 +23,7 @@ export declare class StyleProcessor {
|
|
|
23
23
|
private throwMissingLocalStyleImport;
|
|
24
24
|
collectImportedStyleFiles(styleFiles: Array<string>): Set<string>;
|
|
25
25
|
collectImportedStyleFileReferences(styleFiles: Array<string>): StyleFileImportReference[];
|
|
26
|
+
collectStyleImportReferences(styleFiles: Array<string>): StyleFileImportReference[];
|
|
26
27
|
collectStyleImportSpecifiers(styleFiles: Array<string>): Set<string>;
|
|
27
28
|
assertNoLocalStyleImportCycles(styleFiles: Array<string>): void;
|
|
28
29
|
assertNoSourceRootEscapingLocalStyleImports(styleFiles: Array<string>): void;
|
|
@@ -98,6 +98,21 @@ export class StyleProcessor {
|
|
|
98
98
|
}
|
|
99
99
|
return imports;
|
|
100
100
|
}
|
|
101
|
+
collectStyleImportReferences(styleFiles) {
|
|
102
|
+
const imports = [];
|
|
103
|
+
for (const styleFile of styleFiles) {
|
|
104
|
+
const css = fs.readFileSync(styleFile, 'utf8');
|
|
105
|
+
const root = this.parse(css, styleFile);
|
|
106
|
+
root.walkAtRules('import', (rule) => {
|
|
107
|
+
const specifier = this.parseImportSpecifier(rule.params);
|
|
108
|
+
if (!specifier)
|
|
109
|
+
return;
|
|
110
|
+
const { reference } = this.resolveStyleImportReference(specifier, styleFile);
|
|
111
|
+
imports.push(reference);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
return imports;
|
|
115
|
+
}
|
|
101
116
|
collectStyleImportSpecifiers(styleFiles) {
|
|
102
117
|
const specifiers = new Set();
|
|
103
118
|
for (const styleFile of styleFiles) {
|
package/dist/css/vite/hmr.d.ts
CHANGED
|
@@ -1,18 +1,42 @@
|
|
|
1
1
|
import type { HotUpdateOptions, ViteDevServer } from 'vite';
|
|
2
2
|
import type { ModuleStyleGraph } from '#auklet/css/vite/moduleGraph/graph';
|
|
3
|
+
type TrackedVirtualStyleFileKind = 'entry' | 'dependency';
|
|
4
|
+
export type AukletStyleHmrOptions = {
|
|
5
|
+
pruneDelayMs?: number;
|
|
6
|
+
};
|
|
3
7
|
export declare class AukletStyleHmr {
|
|
4
8
|
private readonly graph;
|
|
5
9
|
private readonly lastUpdateTimes;
|
|
6
10
|
private suppressFullReloadUntil;
|
|
7
11
|
private readonly virtualIdsByDependency;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
12
|
+
private readonly filesByVirtualId;
|
|
13
|
+
private readonly schedulePruneStaleVirtualDependencies;
|
|
14
|
+
private readonly pruneDelayMs;
|
|
15
|
+
constructor(graph: () => ModuleStyleGraph, options?: AukletStyleHmrOptions);
|
|
16
|
+
trackVirtualStyleDependency(file: string, virtualId: string, kind?: TrackedVirtualStyleFileKind): void;
|
|
17
|
+
replaceVirtualStyleDependency(virtualId: string, files: Array<string>, fileKinds?: Array<{
|
|
18
|
+
file: string;
|
|
19
|
+
kind: TrackedVirtualStyleFileKind;
|
|
20
|
+
}>): void;
|
|
21
|
+
hasTrackedStyleDependency(file: string, moduleGraph?: Pick<ViteDevServer['moduleGraph'], 'getModuleById'>): boolean;
|
|
22
|
+
pruneStaleVirtualDependencies(moduleGraph: Pick<ViteDevServer['moduleGraph'], 'getModuleById'>): void;
|
|
23
|
+
scheduleStaleVirtualDependencyPrune(moduleGraph: Pick<ViteDevServer['moduleGraph'], 'getModuleById'>): void;
|
|
24
|
+
cancelStaleVirtualDependencyPrune(): void;
|
|
11
25
|
installFullReloadGuard(server: Pick<ViteDevServer, 'ws'>): void;
|
|
12
|
-
handleStyleHotUpdate(context: HotUpdateOptions): never[] | undefined
|
|
13
|
-
handleSourceModuleChange(server: Pick<ViteDevServer, 'moduleGraph' | 'ws'>, file: string): boolean
|
|
14
|
-
private
|
|
26
|
+
handleStyleHotUpdate(context: HotUpdateOptions): Promise<never[] | undefined>;
|
|
27
|
+
handleSourceModuleChange(server: Pick<ViteDevServer, 'moduleGraph' | 'ws'>, file: string): Promise<boolean>;
|
|
28
|
+
private parseTrackedVirtualIds;
|
|
29
|
+
private createTrackedVirtualStyleUpdates;
|
|
30
|
+
private invalidateTrackedVirtualPackages;
|
|
31
|
+
private getDependencyVirtualIds;
|
|
32
|
+
private getTrackedStyleFileEntries;
|
|
33
|
+
private createTrackedVirtualStyleResults;
|
|
34
|
+
private parseTrackedVirtualId;
|
|
35
|
+
private parseTrackedVirtualIdWithKind;
|
|
36
|
+
private refreshTrackedVirtualStyleUpdates;
|
|
37
|
+
private sameStyleLoadResult;
|
|
15
38
|
private suppressFullReload;
|
|
16
39
|
private shouldSuppressFullReload;
|
|
17
40
|
private isDuplicateUpdate;
|
|
18
41
|
}
|
|
42
|
+
export {};
|
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,41 @@ 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, kind) => {
|
|
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) ??
|
|
21
|
+
new Map();
|
|
22
|
+
const previousKind = files.get(normalizedFile);
|
|
23
|
+
files.set(normalizedFile, previousKind === 'entry' || kind === 'entry' ? 'entry' : kind);
|
|
24
|
+
filesByVirtualId.set(virtualId, files);
|
|
19
25
|
};
|
|
20
|
-
const
|
|
21
|
-
|
|
26
|
+
const removeVirtualStyleDependency = (virtualIdsByDependency, filesByVirtualId, file, virtualId) => {
|
|
27
|
+
const normalizedFile = normalizeFileKey(file);
|
|
28
|
+
const values = virtualIdsByDependency.get(normalizedFile);
|
|
29
|
+
if (values) {
|
|
30
|
+
values.delete(virtualId);
|
|
31
|
+
if (!values.size) {
|
|
32
|
+
virtualIdsByDependency.delete(normalizedFile);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const files = filesByVirtualId.get(virtualId);
|
|
36
|
+
if (files) {
|
|
37
|
+
files.delete(normalizedFile);
|
|
38
|
+
if (!files.size) {
|
|
39
|
+
filesByVirtualId.delete(virtualId);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
22
42
|
};
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
43
|
+
const sameStringSets = (left, right) => {
|
|
44
|
+
if (left.length !== right.length)
|
|
45
|
+
return false;
|
|
46
|
+
const rightSet = new Set(right);
|
|
47
|
+
if (rightSet.size !== left.length)
|
|
48
|
+
return false;
|
|
49
|
+
return left.every((item) => rightSet.has(item));
|
|
28
50
|
};
|
|
29
51
|
const createJsUpdates = (virtualIds, timestamp) => {
|
|
30
52
|
return virtualIds.map((id) => {
|
|
@@ -44,14 +66,47 @@ export class AukletStyleHmr {
|
|
|
44
66
|
lastUpdateTimes = new Map();
|
|
45
67
|
suppressFullReloadUntil = 0;
|
|
46
68
|
virtualIdsByDependency = new Map();
|
|
47
|
-
|
|
69
|
+
filesByVirtualId = new Map();
|
|
70
|
+
schedulePruneStaleVirtualDependencies;
|
|
71
|
+
pruneDelayMs;
|
|
72
|
+
constructor(graph, options = {}) {
|
|
48
73
|
this.graph = graph;
|
|
74
|
+
this.pruneDelayMs = options.pruneDelayMs ?? 2 * 60 * 1000;
|
|
75
|
+
this.schedulePruneStaleVirtualDependencies = throttle(this.pruneDelayMs, (moduleGraph) => this.pruneStaleVirtualDependencies(moduleGraph));
|
|
76
|
+
}
|
|
77
|
+
trackVirtualStyleDependency(file, virtualId, kind = 'dependency') {
|
|
78
|
+
addVirtualStyleDependency(this.virtualIdsByDependency, this.filesByVirtualId, file, virtualId, kind);
|
|
79
|
+
}
|
|
80
|
+
replaceVirtualStyleDependency(virtualId, files, fileKinds = []) {
|
|
81
|
+
const normalizedFiles = new Set(files.map((file) => normalizeFileKey(file)));
|
|
82
|
+
const previousFiles = this.filesByVirtualId.get(virtualId) ?? new Map();
|
|
83
|
+
const nextKinds = new Map(fileKinds.map((item) => [normalizeFileKey(item.file), item.kind]));
|
|
84
|
+
for (const file of previousFiles.keys()) {
|
|
85
|
+
if (normalizedFiles.has(file))
|
|
86
|
+
continue;
|
|
87
|
+
removeVirtualStyleDependency(this.virtualIdsByDependency, this.filesByVirtualId, file, virtualId);
|
|
88
|
+
}
|
|
89
|
+
for (const file of normalizedFiles) {
|
|
90
|
+
addVirtualStyleDependency(this.virtualIdsByDependency, this.filesByVirtualId, file, virtualId, nextKinds.get(file) ?? 'dependency');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
hasTrackedStyleDependency(file, moduleGraph) {
|
|
94
|
+
return this.getDependencyVirtualIds(file, moduleGraph).length > 0;
|
|
95
|
+
}
|
|
96
|
+
pruneStaleVirtualDependencies(moduleGraph) {
|
|
97
|
+
for (const [normalizedFile, virtualIds] of this.virtualIdsByDependency) {
|
|
98
|
+
for (const virtualId of Array.from(virtualIds)) {
|
|
99
|
+
if (moduleGraph.getModuleById(virtualId))
|
|
100
|
+
continue;
|
|
101
|
+
removeVirtualStyleDependency(this.virtualIdsByDependency, this.filesByVirtualId, normalizedFile, virtualId);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
49
104
|
}
|
|
50
|
-
|
|
51
|
-
|
|
105
|
+
scheduleStaleVirtualDependencyPrune(moduleGraph) {
|
|
106
|
+
this.schedulePruneStaleVirtualDependencies(moduleGraph);
|
|
52
107
|
}
|
|
53
|
-
|
|
54
|
-
|
|
108
|
+
cancelStaleVirtualDependencyPrune() {
|
|
109
|
+
this.schedulePruneStaleVirtualDependencies.cancel();
|
|
55
110
|
}
|
|
56
111
|
installFullReloadGuard(server) {
|
|
57
112
|
const send = server.ws.send.bind(server.ws);
|
|
@@ -69,49 +124,199 @@ export class AukletStyleHmr {
|
|
|
69
124
|
send(payload);
|
|
70
125
|
});
|
|
71
126
|
}
|
|
72
|
-
handleStyleHotUpdate(context) {
|
|
127
|
+
async handleStyleHotUpdate(context) {
|
|
73
128
|
const graph = this.graph();
|
|
74
|
-
if (!graph.
|
|
75
|
-
|
|
76
|
-
|
|
129
|
+
if (!graph.isStyleFile(context.file)) {
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
const isSourceGraphFile = graph.isSourceGraphFile(context.file);
|
|
133
|
+
const trackedEntries = this.getTrackedStyleFileEntries(context.file, context.server.moduleGraph);
|
|
134
|
+
if (!trackedEntries.length) {
|
|
135
|
+
if (isSourceGraphFile) {
|
|
136
|
+
graph.invalidateFile(context.file);
|
|
137
|
+
}
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
const entryEntries = isSourceGraphFile
|
|
141
|
+
? trackedEntries.filter((item) => item.kind === 'entry')
|
|
142
|
+
: [];
|
|
143
|
+
const dependencyEntries = isSourceGraphFile
|
|
144
|
+
? trackedEntries.filter((item) => item.kind === 'dependency')
|
|
145
|
+
: trackedEntries;
|
|
146
|
+
const previousResults = entryEntries.length
|
|
147
|
+
? this.createTrackedVirtualStyleResults(graph, entryEntries)
|
|
148
|
+
: null;
|
|
149
|
+
if (isSourceGraphFile) {
|
|
150
|
+
graph.invalidateFile(context.file);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
this.invalidateTrackedVirtualPackages(graph, dependencyEntries);
|
|
77
154
|
}
|
|
78
|
-
this.suppressFullReload();
|
|
79
|
-
graph.invalidateFile(context.file);
|
|
80
155
|
if (this.isDuplicateUpdate(context.file)) {
|
|
81
156
|
return [];
|
|
82
157
|
}
|
|
83
|
-
const updates =
|
|
84
|
-
|
|
158
|
+
const updates = [
|
|
159
|
+
...(dependencyEntries.length
|
|
160
|
+
? this.createTrackedVirtualStyleUpdates(context.server, dependencyEntries, context.timestamp)
|
|
161
|
+
: []),
|
|
162
|
+
...(entryEntries.length
|
|
163
|
+
? await this.refreshTrackedVirtualStyleUpdates(context.server, entryEntries, previousResults, context.timestamp)
|
|
164
|
+
: []),
|
|
165
|
+
];
|
|
166
|
+
if (!updates.length) {
|
|
167
|
+
return undefined;
|
|
168
|
+
}
|
|
169
|
+
this.suppressFullReload();
|
|
170
|
+
context.server.ws.send({
|
|
171
|
+
type: 'update',
|
|
172
|
+
updates,
|
|
173
|
+
});
|
|
174
|
+
logger.info(`package css hmr ${getRelativeFile(context.file)} tracked=${trackedEntries.length} updates=${updates.length}`);
|
|
85
175
|
return [];
|
|
86
176
|
}
|
|
87
|
-
handleSourceModuleChange(server, file) {
|
|
177
|
+
async handleSourceModuleChange(server, file) {
|
|
88
178
|
const graph = this.graph();
|
|
89
179
|
if (!graph.isSourceGraphFile(file) || !graph.isSourceModuleFile(file)) {
|
|
90
180
|
return false;
|
|
91
181
|
}
|
|
182
|
+
const virtualIds = this.getDependencyVirtualIds(file, server.moduleGraph);
|
|
183
|
+
if (!virtualIds.length) {
|
|
184
|
+
graph.invalidateFile(file);
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
const parsedVirtualIds = this.parseTrackedVirtualIds(graph, virtualIds);
|
|
188
|
+
if (!parsedVirtualIds.length) {
|
|
189
|
+
graph.invalidateFile(file);
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
const previousResults = this.createTrackedVirtualStyleResults(graph, parsedVirtualIds);
|
|
92
193
|
graph.invalidateFile(file);
|
|
93
|
-
const updates = this.
|
|
94
|
-
|
|
194
|
+
const updates = await this.refreshTrackedVirtualStyleUpdates(server, parsedVirtualIds, previousResults, Date.now());
|
|
195
|
+
if (!updates.length) {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
this.suppressFullReload();
|
|
199
|
+
server.ws.send({
|
|
200
|
+
type: 'update',
|
|
201
|
+
updates,
|
|
202
|
+
});
|
|
203
|
+
logger.info(`package css source hmr ${getRelativeFile(file)} tracked=${parsedVirtualIds.length} updates=${updates.length}`);
|
|
95
204
|
return true;
|
|
96
205
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
206
|
+
parseTrackedVirtualIds(graph, virtualIds) {
|
|
207
|
+
return virtualIds
|
|
208
|
+
.map((id) => this.parseTrackedVirtualId(graph, id))
|
|
209
|
+
.filter((item) => item !== null);
|
|
210
|
+
}
|
|
211
|
+
createTrackedVirtualStyleUpdates(server, parsedVirtualIds, timestamp) {
|
|
212
|
+
const changedVirtualIds = [];
|
|
213
|
+
for (const item of parsedVirtualIds) {
|
|
214
|
+
changedVirtualIds.push(item.id);
|
|
215
|
+
const module = server.moduleGraph.getModuleById(item.id);
|
|
216
|
+
if (module) {
|
|
217
|
+
server.moduleGraph.invalidateModule(module);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return createJsUpdates(changedVirtualIds, timestamp);
|
|
221
|
+
}
|
|
222
|
+
invalidateTrackedVirtualPackages(graph, parsedVirtualIds) {
|
|
223
|
+
const packageNames = new Set(parsedVirtualIds.map((item) => item.parsed.packageName));
|
|
224
|
+
for (const packageName of packageNames) {
|
|
225
|
+
graph.invalidatePackage(packageName);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
getDependencyVirtualIds(file, moduleGraph) {
|
|
229
|
+
const normalizedFile = normalizeFileKey(file);
|
|
230
|
+
const virtualIds = Array.from(this.virtualIdsByDependency.get(normalizedFile) ?? []);
|
|
231
|
+
if (!moduleGraph) {
|
|
232
|
+
return virtualIds;
|
|
233
|
+
}
|
|
234
|
+
const liveVirtualIds = virtualIds.filter((virtualId) => moduleGraph.getModuleById(virtualId));
|
|
235
|
+
if (liveVirtualIds.length !== virtualIds.length) {
|
|
236
|
+
const liveSet = new Set(liveVirtualIds);
|
|
237
|
+
for (const virtualId of virtualIds) {
|
|
238
|
+
if (liveSet.has(virtualId))
|
|
239
|
+
continue;
|
|
240
|
+
removeVirtualStyleDependency(this.virtualIdsByDependency, this.filesByVirtualId, normalizedFile, virtualId);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return liveVirtualIds;
|
|
244
|
+
}
|
|
245
|
+
getTrackedStyleFileEntries(file, moduleGraph) {
|
|
246
|
+
const normalizedFile = normalizeFileKey(file);
|
|
247
|
+
const virtualIds = Array.from(this.virtualIdsByDependency.get(normalizedFile) ?? []);
|
|
248
|
+
const entries = virtualIds
|
|
249
|
+
.map((id) => this.parseTrackedVirtualIdWithKind(id, normalizedFile, moduleGraph))
|
|
250
|
+
.filter((item) => Boolean(item));
|
|
251
|
+
if (!moduleGraph) {
|
|
252
|
+
return entries;
|
|
253
|
+
}
|
|
254
|
+
if (entries.length !== virtualIds.length) {
|
|
255
|
+
const liveSet = new Set(entries.map((item) => item.id));
|
|
256
|
+
for (const virtualId of virtualIds) {
|
|
257
|
+
if (liveSet.has(virtualId))
|
|
258
|
+
continue;
|
|
259
|
+
removeVirtualStyleDependency(this.virtualIdsByDependency, this.filesByVirtualId, normalizedFile, virtualId);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return entries;
|
|
263
|
+
}
|
|
264
|
+
createTrackedVirtualStyleResults(graph, parsedVirtualIds) {
|
|
265
|
+
const previousResults = new Map();
|
|
266
|
+
for (const item of parsedVirtualIds) {
|
|
267
|
+
previousResults.set(item.id, graph.peekPackageStyleCode(item.parsed));
|
|
102
268
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
269
|
+
return previousResults;
|
|
270
|
+
}
|
|
271
|
+
parseTrackedVirtualId(graph, id) {
|
|
272
|
+
const parsedId = id.startsWith(RESOLVED_VIRTUAL_ID_PREFIX)
|
|
273
|
+
? id.slice(RESOLVED_VIRTUAL_ID_PREFIX.length)
|
|
274
|
+
: id;
|
|
275
|
+
const parsed = graph.parsePackageStyleId(parsedId);
|
|
276
|
+
if (!parsed || !graph.getPackageNames().includes(parsed.packageName)) {
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
return {
|
|
280
|
+
id,
|
|
281
|
+
parsed,
|
|
282
|
+
kind: 'dependency',
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
parseTrackedVirtualIdWithKind(id, normalizedFile, moduleGraph) {
|
|
286
|
+
if (moduleGraph && !moduleGraph.getModuleById(id)) {
|
|
287
|
+
return null;
|
|
109
288
|
}
|
|
289
|
+
const parsed = this.parseTrackedVirtualId(this.graph(), id);
|
|
290
|
+
if (!parsed)
|
|
291
|
+
return null;
|
|
292
|
+
const kind = this.filesByVirtualId.get(id)?.get(normalizedFile) ?? 'dependency';
|
|
110
293
|
return {
|
|
111
|
-
|
|
112
|
-
|
|
294
|
+
...parsed,
|
|
295
|
+
kind,
|
|
113
296
|
};
|
|
114
297
|
}
|
|
298
|
+
async refreshTrackedVirtualStyleUpdates(server, parsedVirtualIds, previousResults, timestamp) {
|
|
299
|
+
const graph = this.graph();
|
|
300
|
+
const changedVirtualIds = [];
|
|
301
|
+
for (const item of parsedVirtualIds) {
|
|
302
|
+
const nextResult = await graph.createPackageStyleCode(item.parsed);
|
|
303
|
+
const previousResult = previousResults.get(item.id);
|
|
304
|
+
if (!previousResult ||
|
|
305
|
+
!this.sameStyleLoadResult(previousResult, nextResult)) {
|
|
306
|
+
changedVirtualIds.push(item.id);
|
|
307
|
+
const module = server.moduleGraph.getModuleById(item.id);
|
|
308
|
+
if (module) {
|
|
309
|
+
server.moduleGraph.invalidateModule(module);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return createJsUpdates(changedVirtualIds, timestamp);
|
|
314
|
+
}
|
|
315
|
+
sameStyleLoadResult(left, right) {
|
|
316
|
+
return (left.code === right.code &&
|
|
317
|
+
sameStringSets(left.watchFiles, right.watchFiles) &&
|
|
318
|
+
sameStringSets(left.dependencyPackages ?? [], right.dependencyPackages ?? []));
|
|
319
|
+
}
|
|
115
320
|
suppressFullReload() {
|
|
116
321
|
this.suppressFullReloadUntil = Date.now() + FULL_RELOAD_SUPPRESS_MS;
|
|
117
322
|
}
|
|
@@ -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,5 +1,15 @@
|
|
|
1
1
|
// 合并递归 style graph 加载结果,同时保留 CSS 顺序并去重 watch files。
|
|
2
2
|
export function mergeLoadResults(...results) {
|
|
3
|
+
const watchFileKinds = new Map();
|
|
4
|
+
for (const result of results) {
|
|
5
|
+
for (const item of result.watchFileKinds ?? []) {
|
|
6
|
+
const currentKind = watchFileKinds.get(item.file);
|
|
7
|
+
if (currentKind === 'entry' || item.kind === currentKind) {
|
|
8
|
+
continue;
|
|
9
|
+
}
|
|
10
|
+
watchFileKinds.set(item.file, item.kind === 'entry' ? 'entry' : (currentKind ?? item.kind));
|
|
11
|
+
}
|
|
12
|
+
}
|
|
3
13
|
return {
|
|
4
14
|
code: results
|
|
5
15
|
.map((result) => result.code)
|
|
@@ -8,5 +18,9 @@ export function mergeLoadResults(...results) {
|
|
|
8
18
|
watchFiles: Array.from(new Set(results.flatMap((result) => result.watchFiles))),
|
|
9
19
|
cacheInputFiles: Array.from(new Set(results.flatMap((result) => result.cacheInputFiles ?? []))),
|
|
10
20
|
dependencyPackages: Array.from(new Set(results.flatMap((result) => result.dependencyPackages ?? []))),
|
|
21
|
+
watchFileKinds: Array.from(watchFileKinds, ([file, kind]) => ({
|
|
22
|
+
file,
|
|
23
|
+
kind,
|
|
24
|
+
})),
|
|
11
25
|
};
|
|
12
26
|
}
|
|
@@ -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,13 +75,14 @@ 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): {
|
|
75
82
|
cacheInputFiles: string[];
|
|
76
83
|
code: string;
|
|
77
84
|
watchFiles: Array<string>;
|
|
85
|
+
watchFileKinds?: Array<import("#auklet/css/vite/moduleGraph/types").PackageStyleWatchFile>;
|
|
78
86
|
dependencyPackages?: Array<string>;
|
|
79
87
|
};
|
|
80
88
|
private createContext;
|
|
@@ -87,4 +95,10 @@ export declare class ModuleStyleGraphRequestCache {
|
|
|
87
95
|
private getLoadResultKey;
|
|
88
96
|
private getLoadResultPackageName;
|
|
89
97
|
private invalidateLoadResults;
|
|
98
|
+
private discardLoadResult;
|
|
99
|
+
peekLoadResult(parsed: PackageStyleId): PackageStyleLoadResult | null;
|
|
100
|
+
private getGraphVersion;
|
|
101
|
+
private bumpGraphVersion;
|
|
102
|
+
private bumpLoadResultVersion;
|
|
90
103
|
}
|
|
104
|
+
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,6 +14,7 @@ export declare class StyleCodeFactory {
|
|
|
14
14
|
private createModuleStyleCode;
|
|
15
15
|
private createSourceModuleStyleCode;
|
|
16
16
|
private createOwnSourceStyleCode;
|
|
17
|
+
private collectSourceStyleWatchFiles;
|
|
17
18
|
private toDevModuleImportSpecifier;
|
|
18
19
|
private toDevExternalStyleSpecifier;
|
|
19
20
|
private parsePackageStyleIdInRequest;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
1
2
|
import path from 'node:path';
|
|
2
3
|
import postcss from 'postcss';
|
|
3
4
|
import { mergeLoadResults } from '#auklet/css/vite/moduleGraph/loadResult';
|
|
@@ -14,19 +15,30 @@ export class StyleCodeFactory {
|
|
|
14
15
|
this.config = config;
|
|
15
16
|
}
|
|
16
17
|
async createPackageStyleCode(parsed, cache) {
|
|
17
|
-
return cache.getLoadResult(parsed, async () =>
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
return cache.getLoadResult(parsed, async () => {
|
|
19
|
+
const context = await cache.getContext(parsed);
|
|
20
|
+
if (!context) {
|
|
21
|
+
return {
|
|
22
|
+
result: {
|
|
23
|
+
code: '',
|
|
24
|
+
watchFiles: [],
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const cachedResult = cache.readPersistentLoadResult(parsed, context);
|
|
29
|
+
if (cachedResult) {
|
|
30
|
+
return {
|
|
31
|
+
result: cachedResult,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const result = this.normalizeTopLevelImports(await this.createUncachedPackageStyleCode(parsed, cache, context));
|
|
22
35
|
return {
|
|
23
|
-
|
|
24
|
-
|
|
36
|
+
result,
|
|
37
|
+
commit: () => cache.writePersistentLoadResult(parsed, context, result),
|
|
25
38
|
};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return cachedResult;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
async createUncachedPackageStyleCode(parsed, cache, context) {
|
|
30
42
|
let result;
|
|
31
43
|
if (parsed.stylePath === STYLE_ENTRY) {
|
|
32
44
|
result = await this.createStyleCode(context, cache);
|
|
@@ -45,7 +57,7 @@ export class StyleCodeFactory {
|
|
|
45
57
|
else {
|
|
46
58
|
result = await this.createSourceModuleStyleCode(context, cache, parsed.stylePath);
|
|
47
59
|
}
|
|
48
|
-
return
|
|
60
|
+
return result;
|
|
49
61
|
}
|
|
50
62
|
async createStyleCode(context, cache) {
|
|
51
63
|
const results = [];
|
|
@@ -155,7 +167,7 @@ export class StyleCodeFactory {
|
|
|
155
167
|
async createSourceModuleStyleCode(context, cache, stylePath) {
|
|
156
168
|
context.packageContext.assertPreservedLocalStyleImports();
|
|
157
169
|
const sourceModuleDir = removeStyleExtension(stylePath);
|
|
158
|
-
const {
|
|
170
|
+
const { sourceFiles } = context.packageContext;
|
|
159
171
|
const entry = createModuleStyleEntryPlan(context.packageContext, sourceModuleDir);
|
|
160
172
|
const sourceStyleDir = path.join(context.sourceRoot, sourceModuleDir, this.config.output.styleDir);
|
|
161
173
|
const moduleStyleResults = [];
|
|
@@ -178,16 +190,31 @@ export class StyleCodeFactory {
|
|
|
178
190
|
}
|
|
179
191
|
}
|
|
180
192
|
const ownStyleCode = this.createOwnSourceStyleCode(context, entry.ownStyleFiles);
|
|
193
|
+
const sourceWatchFiles = this.collectSourceStyleWatchFiles(context, entry.ownStyleFiles);
|
|
181
194
|
return mergeLoadResults(...moduleStyleResults, {
|
|
182
195
|
code: [createImportCode(moduleStyleSpecifiers), ownStyleCode]
|
|
183
196
|
.filter((code) => code.trim())
|
|
184
197
|
.join('\n'),
|
|
185
198
|
watchFiles: [
|
|
186
199
|
...context.configPaths,
|
|
187
|
-
...
|
|
200
|
+
...sourceWatchFiles.map((item) => item.file),
|
|
188
201
|
...moduleStyleWatchFiles,
|
|
189
202
|
...sourceFiles.filter((file) => /\.(ts|tsx)$/.test(file)),
|
|
190
203
|
],
|
|
204
|
+
watchFileKinds: [
|
|
205
|
+
...context.configPaths.map((file) => ({
|
|
206
|
+
file,
|
|
207
|
+
kind: 'dependency',
|
|
208
|
+
})),
|
|
209
|
+
...sourceWatchFiles,
|
|
210
|
+
...moduleStyleWatchFiles.map((file) => ({
|
|
211
|
+
file,
|
|
212
|
+
kind: 'dependency',
|
|
213
|
+
})),
|
|
214
|
+
...sourceFiles
|
|
215
|
+
.filter((file) => /\.(ts|tsx)$/.test(file))
|
|
216
|
+
.map((file) => ({ file, kind: 'dependency' })),
|
|
217
|
+
],
|
|
191
218
|
cacheInputFiles: moduleStyleCacheInputFiles,
|
|
192
219
|
});
|
|
193
220
|
}
|
|
@@ -209,6 +236,42 @@ export class StyleCodeFactory {
|
|
|
209
236
|
}
|
|
210
237
|
return root.nodes?.length ? context.styleProcessor.stringify(root) : '';
|
|
211
238
|
}
|
|
239
|
+
collectSourceStyleWatchFiles(context, styleFiles) {
|
|
240
|
+
const watchFiles = new Map();
|
|
241
|
+
const pending = styleFiles.map((file) => ({
|
|
242
|
+
file,
|
|
243
|
+
kind: 'entry',
|
|
244
|
+
}));
|
|
245
|
+
const visited = new Set();
|
|
246
|
+
while (pending.length) {
|
|
247
|
+
const current = pending.pop();
|
|
248
|
+
if (!current)
|
|
249
|
+
continue;
|
|
250
|
+
const styleFile = current.file;
|
|
251
|
+
const normalized = path.resolve(styleFile);
|
|
252
|
+
if (visited.has(normalized))
|
|
253
|
+
continue;
|
|
254
|
+
visited.add(normalized);
|
|
255
|
+
const previousKind = watchFiles.get(normalized);
|
|
256
|
+
watchFiles.set(normalized, previousKind === 'entry' || current.kind === 'entry'
|
|
257
|
+
? 'entry'
|
|
258
|
+
: 'dependency');
|
|
259
|
+
for (const reference of context.styleProcessor.collectStyleImportReferences([normalized])) {
|
|
260
|
+
if (!context.resolver.isStyleFile(reference.imported) ||
|
|
261
|
+
!fs.existsSync(reference.imported)) {
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
pending.push({
|
|
265
|
+
file: reference.imported,
|
|
266
|
+
kind: 'dependency',
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return Array.from(watchFiles, ([file, kind]) => ({
|
|
271
|
+
file,
|
|
272
|
+
kind,
|
|
273
|
+
}));
|
|
274
|
+
}
|
|
212
275
|
toDevModuleImportSpecifier(specifier, context, cache, sourceStyleDir) {
|
|
213
276
|
return createDevModuleStyleSpecifier(specifier, {
|
|
214
277
|
sourceStyleDir,
|
|
@@ -9,10 +9,16 @@ export type PackageStyleId = {
|
|
|
9
9
|
packageName: string;
|
|
10
10
|
stylePath: string;
|
|
11
11
|
};
|
|
12
|
+
export type PackageStyleWatchFileKind = 'entry' | 'dependency';
|
|
13
|
+
export type PackageStyleWatchFile = {
|
|
14
|
+
file: string;
|
|
15
|
+
kind: PackageStyleWatchFileKind;
|
|
16
|
+
};
|
|
12
17
|
export type PackageStyleLoadResult = {
|
|
13
18
|
code: string;
|
|
14
19
|
cacheInputFiles?: Array<string>;
|
|
15
20
|
watchFiles: Array<string>;
|
|
21
|
+
watchFileKinds?: Array<PackageStyleWatchFile>;
|
|
16
22
|
dependencyPackages?: Array<string>;
|
|
17
23
|
};
|
|
18
24
|
export type LoadAukletConfig = (packageRoot: string, options?: {
|
|
@@ -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, result.watchFileKinds);
|
|
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,35 @@ 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
|
+
if (hmr.hasTrackedStyleDependency(file)) {
|
|
154
|
+
await hmr.handleStyleHotUpdate({
|
|
155
|
+
file,
|
|
156
|
+
modules: [],
|
|
157
|
+
server,
|
|
158
|
+
timestamp: Date.now(),
|
|
159
|
+
type: 'update',
|
|
160
|
+
read: async () => '',
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
graph.invalidateFile(file);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
else if (graph.isSourceModuleFile(file)) {
|
|
168
|
+
await hmr.handleSourceModuleChange(server, file);
|
|
169
|
+
}
|
|
134
170
|
}
|
|
135
|
-
|
|
136
|
-
|
|
171
|
+
catch (error) {
|
|
172
|
+
logger.error('package css change watcher failed');
|
|
173
|
+
logger.error(error);
|
|
174
|
+
server.ws.send(createWatcherErrorPayload(error, file));
|
|
137
175
|
}
|
|
138
176
|
});
|
|
139
177
|
},
|