@moluoxixi/vite-config 0.0.33 → 0.0.35

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/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  ```ts
8
8
  // vite.config.ts
9
- import viteConfig, { wrapperEnv } from '@moluoxixi/viteconfig'
9
+ import viteConfig, { wrapperEnv } from '@moluoxixi/vite-config'
10
10
  import path from 'node:path'
11
11
  import { loadEnv } from 'vite'
12
12
 
@@ -0,0 +1,86 @@
1
+ import { createVirtualPlugin } from "../_utils/virtual.mjs";
2
+ import { findParentRouteHandle, parseModulePath, processComponent, cleanRoute, findDefaultRouteHandle, generateRoutes } from "./routeGenerator.mjs";
3
+ function extractGlob(globVal) {
4
+ return globVal.glob || globVal;
5
+ }
6
+ function getEagerOption(globVal, globalEager) {
7
+ return globVal.eager ?? globalEager ?? false;
8
+ }
9
+ function generateImportCode(varName, glob, eager) {
10
+ if (eager) {
11
+ return `const ${varName} = import.meta.glob(${JSON.stringify(glob)}, { eager: true, import: 'default' });
12
+ `;
13
+ }
14
+ return `const ${varName} = import.meta.glob(${JSON.stringify(glob)});
15
+ `;
16
+ }
17
+ function createAutoRoutesPlugin({ routeConfig, virtualModuleId, dts, root, eager: globalEager }) {
18
+ const VIRTUAL_MODULE_ID = virtualModuleId || "virtual:auto-routes";
19
+ const watchGlobs = Object.values(routeConfig).flatMap((globVal) => {
20
+ const g = extractGlob(globVal);
21
+ return Array.isArray(g) ? g : [g];
22
+ });
23
+ return createVirtualPlugin(
24
+ {
25
+ name: "vite-plugin-auto-routes",
26
+ virtualModuleId: VIRTUAL_MODULE_ID,
27
+ dts,
28
+ root,
29
+ watch: watchGlobs,
30
+ enforce: "pre",
31
+ // 生成虚拟模块代码:仅负责产出字符串,监听/HMR/缓存由工厂统一处理
32
+ generateModule: () => {
33
+ const imports = [];
34
+ const routes = [];
35
+ Object.entries(routeConfig).forEach(([prefix, globVal], index) => {
36
+ const varName = `files${index}`;
37
+ const glob = extractGlob(globVal);
38
+ const eager = getEagerOption(globVal, globalEager);
39
+ const baseRoute = globVal.baseRoute;
40
+ const baseRouteParam = baseRoute !== void 0 ? JSON.stringify(baseRoute) : "undefined";
41
+ imports.push(generateImportCode(varName, glob, eager));
42
+ routes.push(`...generateRoutes(${varName}, '${prefix}',${baseRouteParam}, ${eager})`);
43
+ });
44
+ const routesCode = routes.length > 0 ? `[${routes.join(",\n")}]` : "[]";
45
+ return `
46
+ ${imports.join("\n")}
47
+ ${findParentRouteHandle}
48
+ ${parseModulePath}
49
+ ${processComponent}
50
+ ${cleanRoute}
51
+ ${findDefaultRouteHandle}
52
+
53
+ const findParentRoute = ${findParentRouteHandle}
54
+ // 用于routes
55
+ const generateRoutes = ${generateRoutes};
56
+ // 用于导出
57
+ const findDefaultRoute = ${findDefaultRouteHandle};
58
+
59
+ const routes = ${routesCode}.flat().filter(Boolean);
60
+
61
+ export { routes, findDefaultRoute };
62
+ export default routes;
63
+ `;
64
+ },
65
+ // 生成类型声明文件
66
+ generateDts: () => `// 此文件由ViteConfig自动生成,请勿手动修改
67
+ declare module 'virtual:auto-routes' {
68
+ interface RouteModule {
69
+ path: string
70
+ name: string
71
+ meta?: any
72
+ component: () => Promise<any>
73
+ children?: RouteModule[]
74
+ }
75
+
76
+ const routes: RouteModule[]
77
+ const findDefaultRoute: (routes: any[]) => string
78
+ export { findDefaultRoute, routes }
79
+ export default routes
80
+ }`
81
+ }
82
+ );
83
+ }
84
+ export {
85
+ createAutoRoutesPlugin as default
86
+ };
@@ -0,0 +1,123 @@
1
+ function cleanRoute(route) {
2
+ const cleaned = { ...route };
3
+ if (cleaned.children) {
4
+ if (Array.isArray(cleaned.children) && cleaned.children.length > 0) {
5
+ cleaned.children = cleaned.children.map(cleanRoute);
6
+ } else {
7
+ delete cleaned.children;
8
+ }
9
+ }
10
+ return cleaned;
11
+ }
12
+ function findParentRouteHandle(modules, parentPath) {
13
+ for (const route of modules) {
14
+ if (route.path === parentPath) {
15
+ return route;
16
+ }
17
+ if (route.children) {
18
+ const found = findParentRouteHandle(route.children, parentPath);
19
+ if (found)
20
+ return found;
21
+ }
22
+ }
23
+ return void 0;
24
+ }
25
+ function parseModulePath(modulePath) {
26
+ const pathArr = modulePath.split("/").filter((item) => item && !item.includes("."));
27
+ if (pathArr.at(-1) === "src") {
28
+ pathArr.pop();
29
+ }
30
+ const componentName = pathArr.at(-1);
31
+ const path = `/${pathArr.join("/")}`;
32
+ const parentPath = `/${pathArr.slice(0, -1).join("/")}`;
33
+ return { pathArr, componentName, path, parentPath };
34
+ }
35
+ function processComponent(componentLoader, componentName, eager) {
36
+ if (eager) {
37
+ return {
38
+ component: componentLoader,
39
+ metaTitle: (componentLoader == null ? void 0 : componentLoader.name) || componentName
40
+ };
41
+ }
42
+ return {
43
+ component: typeof componentLoader === "function" ? componentLoader : componentLoader,
44
+ metaTitle: componentName
45
+ };
46
+ }
47
+ function generateRoutes(files, prefix = "", baseRoute, eager = false) {
48
+ const newBaseRoute = typeof baseRoute === "string" ? { name: baseRoute } : baseRoute;
49
+ const modules = newBaseRoute ? [newBaseRoute] : [];
50
+ const fileKeys = Object.keys(files);
51
+ if (fileKeys.length === 0) {
52
+ return [];
53
+ }
54
+ return fileKeys.sort((a, b) => {
55
+ const aLength = a.split("/").length;
56
+ const bLength = b.split("/").length;
57
+ return bLength > aLength ? -1 : 1;
58
+ }).reduce((modules2 = [], modulePath) => {
59
+ const componentLoader = files[modulePath];
60
+ if (!componentLoader || modulePath === "install") {
61
+ return modules2;
62
+ }
63
+ const { path, parentPath, componentName } = parseModulePath(modulePath);
64
+ if (!componentName) {
65
+ return modules2;
66
+ }
67
+ let parentRoute;
68
+ if (newBaseRoute) {
69
+ newBaseRoute.children = newBaseRoute.children || [];
70
+ newBaseRoute.name = newBaseRoute.name || prefix;
71
+ newBaseRoute.path = `/${(newBaseRoute.path || parentPath).split("/").filter((item) => item && !item.includes(".")).join("/")}`;
72
+ parentRoute = newBaseRoute;
73
+ } else {
74
+ parentRoute = findParentRouteHandle(modules2, parentPath);
75
+ }
76
+ const { component, metaTitle } = processComponent(componentLoader, componentName, eager);
77
+ const routeItem = {
78
+ path,
79
+ name: componentName,
80
+ meta: {
81
+ title: metaTitle
82
+ },
83
+ component
84
+ };
85
+ if (parentRoute) {
86
+ if (!parentRoute.children) {
87
+ parentRoute.children = [];
88
+ }
89
+ parentRoute.children.push(routeItem);
90
+ } else {
91
+ modules2.push(routeItem);
92
+ }
93
+ return modules2;
94
+ }, modules).map(cleanRoute).filter((route) => {
95
+ return !(route.children && Array.isArray(route.children) && route.children.length === 0);
96
+ });
97
+ }
98
+ function findDefaultRouteHandle(routes) {
99
+ var _a, _b, _c;
100
+ if (!routes || routes.length === 0) {
101
+ return "";
102
+ }
103
+ for (const route of routes) {
104
+ if ((_a = route.meta) == null ? void 0 : _a.default) {
105
+ return route.path || "";
106
+ }
107
+ if ((_b = route.children) == null ? void 0 : _b.length) {
108
+ const childPath = findDefaultRouteHandle(route.children);
109
+ if (childPath) {
110
+ return childPath;
111
+ }
112
+ }
113
+ }
114
+ return ((_c = routes[0]) == null ? void 0 : _c.path) || "";
115
+ }
116
+ export {
117
+ cleanRoute,
118
+ findDefaultRouteHandle,
119
+ findParentRouteHandle,
120
+ generateRoutes,
121
+ parseModulePath,
122
+ processComponent
123
+ };
@@ -0,0 +1,8 @@
1
+ function getType(obj, type) {
2
+ {
3
+ return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase() === type.toLowerCase();
4
+ }
5
+ }
6
+ export {
7
+ getType
8
+ };
@@ -0,0 +1,13 @@
1
+ async function dynamicImport(modulePromise, exportName = "default") {
2
+ const module = await modulePromise;
3
+ if (exportName === "default") {
4
+ return module.default ?? module;
5
+ }
6
+ if (!(exportName in module)) {
7
+ throw new Error(`模块中不存在导出 "${exportName}"`);
8
+ }
9
+ return module[exportName];
10
+ }
11
+ export {
12
+ dynamicImport
13
+ };
@@ -0,0 +1,56 @@
1
+ function isObject(value) {
2
+ return Object.prototype.toString.call(value) === "[object Object]";
3
+ }
4
+ function deepMerge(target, source) {
5
+ if (!isObject(source)) {
6
+ return target;
7
+ }
8
+ return Object.keys(source).reduce((acc, key) => {
9
+ const sourceValue = source[key];
10
+ const targetValue = acc[key];
11
+ if (isObject(sourceValue) && isObject(targetValue)) {
12
+ acc[key] = deepMerge(targetValue, sourceValue);
13
+ } else if (isObject(sourceValue)) {
14
+ acc[key] = deepMerge({}, sourceValue);
15
+ } else {
16
+ acc[key] = sourceValue;
17
+ }
18
+ return acc;
19
+ }, { ...target });
20
+ }
21
+ function validateMutuallyExclusive(values, defaultKey) {
22
+ const keys = Object.keys(values);
23
+ const enabledKeys = [];
24
+ for (const key of keys) {
25
+ if (values[key] === true) {
26
+ enabledKeys.push(key);
27
+ }
28
+ }
29
+ if (enabledKeys.length > 1) {
30
+ const keysStr = enabledKeys.join("、");
31
+ const allKeysStr = keys.join("、");
32
+ const selectedKey = enabledKeys.includes(defaultKey) ? defaultKey : enabledKeys[0];
33
+ console.warn(
34
+ `[validateMutuallyExclusive] ${allKeysStr} 只能启用一个,但当前启用了:${keysStr}。已自动选择:${String(selectedKey)}`
35
+ );
36
+ const result2 = {};
37
+ for (const key of keys) {
38
+ result2[key] = key === selectedKey;
39
+ }
40
+ return result2;
41
+ }
42
+ const hasEnabledKey = enabledKeys.length > 0;
43
+ const result = {};
44
+ for (const key of keys) {
45
+ if (!hasEnabledKey && defaultKey && key === defaultKey) {
46
+ result[key] = true;
47
+ } else {
48
+ result[key] = values[key] ?? false;
49
+ }
50
+ }
51
+ return result;
52
+ }
53
+ export {
54
+ deepMerge,
55
+ validateMutuallyExclusive
56
+ };
@@ -0,0 +1,389 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+ import { getType } from "./base.mjs";
5
+ function resolvePatternsToAbsolute(patterns, rootDir) {
6
+ return patterns.map((p) => Vite.normalizePath(Path.isAbsolute(p) ? p : Path.resolve(rootDir, p)));
7
+ }
8
+ function extractStaticPrefixFromGlob(absPattern) {
9
+ const special = ["*", "?", "{", "}", "!", "(", ")", "[", "]"];
10
+ const idx = absPattern.split("").findIndex((ch) => special.includes(ch));
11
+ return idx === -1 ? absPattern : absPattern.slice(0, idx);
12
+ }
13
+ function createMatcher(prefixes) {
14
+ return (absFile) => prefixes.some((prefix) => absFile.startsWith(prefix));
15
+ }
16
+ class VirtualModuleState {
17
+ constructor() {
18
+ /** 标记服务器是否正在关闭,避免关闭阶段再触发无效操作 */
19
+ __publicField(this, "isServerClosing", false);
20
+ /** 标记初始化是否完成,初始化期间的变化会被延迟处理 */
21
+ __publicField(this, "isInitialized", false);
22
+ /** 标记初始化期间是否有文件变化,初始化完成后会处理这些变化 */
23
+ __publicField(this, "hasPendingChange", false);
24
+ /** HMR 热更新的防抖函数,lodash-es debounce 返回的函数有 cancel 方法,可以手动取消 */
25
+ __publicField(this, "hmrDebouncedInvalidate");
26
+ /** watchChange 钩子的防抖函数,用于清理模块缓存 */
27
+ __publicField(this, "watchChangeDebouncedClear");
28
+ /** 文件监听器的防抖函数 */
29
+ __publicField(this, "watcherDebouncedInvalidate");
30
+ }
31
+ /**
32
+ * 清理所有防抖定时器
33
+ * 在服务器关闭时调用,确保不会有待执行的防抖任务
34
+ */
35
+ clearAll() {
36
+ var _a, _b, _c;
37
+ (_a = this.hmrDebouncedInvalidate) == null ? void 0 : _a.cancel();
38
+ (_b = this.watchChangeDebouncedClear) == null ? void 0 : _b.cancel();
39
+ (_c = this.watcherDebouncedInvalidate) == null ? void 0 : _c.cancel();
40
+ }
41
+ }
42
+ function invalidateModules(server, virtualModuleId, moduleCache) {
43
+ const ids = Array.from(moduleCache.keys()).filter(
44
+ (k) => k === virtualModuleId || k.startsWith(`${virtualModuleId}/`)
45
+ );
46
+ const mods = [];
47
+ for (const vid of ids) {
48
+ moduleCache.delete(vid);
49
+ const mod = server.moduleGraph.getModuleById(vid);
50
+ if (mod) {
51
+ server.moduleGraph.invalidateModule(mod);
52
+ mods.push(mod);
53
+ }
54
+ }
55
+ return mods;
56
+ }
57
+ function sendHmrUpdate(server, mods) {
58
+ var _a;
59
+ if (mods.length === 0) {
60
+ server == null ? void 0 : server.ws.send({ type: "full-reload" });
61
+ return;
62
+ }
63
+ try {
64
+ for (const mod of mods) {
65
+ try {
66
+ (_a = server.reloadModule) == null ? void 0 : _a.call(server, mod);
67
+ } catch {
68
+ }
69
+ }
70
+ server == null ? void 0 : server.ws.send({
71
+ type: "update",
72
+ updates: mods.map((mod) => ({
73
+ type: "js-update",
74
+ path: mod.url,
75
+ acceptedPath: mod.url,
76
+ timestamp: Date.now()
77
+ }))
78
+ });
79
+ } catch {
80
+ server == null ? void 0 : server.ws.send({ type: "full-reload" });
81
+ }
82
+ }
83
+ function setupFileWatcher(options) {
84
+ var _a;
85
+ const { server, watchPatterns, isWatchedPath, onInvalidate, debounceMs, onInitialized } = options;
86
+ let watcherReady = false;
87
+ let netReady = false;
88
+ let enabled = false;
89
+ const debouncedInvalidate = LodashEs.debounce(onInvalidate, debounceMs, {
90
+ trailing: true,
91
+ leading: false
92
+ });
93
+ const maybeEnable = () => {
94
+ var _a2;
95
+ if (enabled || !watcherReady || !netReady)
96
+ return;
97
+ enabled = true;
98
+ try {
99
+ if (watchPatterns.length > 0)
100
+ server.watcher.add(watchPatterns);
101
+ } catch {
102
+ }
103
+ const handleFileChange = (eventName, file) => {
104
+ if (eventName === "change" || eventName === "unlink" || eventName === "unlinkDir" || watcherReady && (eventName === "add" || eventName === "addDir")) {
105
+ const abs = Vite.normalizePath(
106
+ Path.isAbsolute(file) ? file : Path.resolve(server.config.root, file)
107
+ );
108
+ if (isWatchedPath(abs))
109
+ debouncedInvalidate();
110
+ }
111
+ };
112
+ server.watcher.on("all", handleFileChange);
113
+ const cleanup = () => {
114
+ debouncedInvalidate.cancel();
115
+ server.watcher.off("all", handleFileChange);
116
+ };
117
+ server.watcher.once("close", cleanup);
118
+ (_a2 = server.httpServer) == null ? void 0 : _a2.once("close", cleanup);
119
+ onInitialized == null ? void 0 : onInitialized();
120
+ };
121
+ server.watcher.once("ready", () => {
122
+ watcherReady = true;
123
+ maybeEnable();
124
+ });
125
+ (_a = server.httpServer) == null ? void 0 : _a.once("listening", () => {
126
+ netReady = true;
127
+ maybeEnable();
128
+ });
129
+ const wsAny = server.ws;
130
+ if (typeof (wsAny == null ? void 0 : wsAny.on) === "function") {
131
+ const connectionHandler = () => {
132
+ var _a2, _b;
133
+ netReady = true;
134
+ maybeEnable();
135
+ try {
136
+ (_a2 = wsAny.off) == null ? void 0 : _a2.call(wsAny, "connection", connectionHandler);
137
+ } catch {
138
+ }
139
+ try {
140
+ (_b = wsAny.removeListener) == null ? void 0 : _b.call(wsAny, "connection", connectionHandler);
141
+ } catch {
142
+ }
143
+ };
144
+ wsAny.on("connection", connectionHandler);
145
+ }
146
+ }
147
+ function writeDtsFile(config, rootDir, dts, generateDts) {
148
+ if (dts === false || !generateDts)
149
+ return;
150
+ try {
151
+ const dtsPath = typeof dts === "string" ? Path.isAbsolute(dts) ? dts : Path.resolve(rootDir, dts) : Path.resolve(rootDir, "./src/typings/virtual-module.d.ts");
152
+ const content = generateDts({ config });
153
+ const normalized = Vite.normalizePath(dtsPath);
154
+ const dir = Path.dirname(normalized);
155
+ Fs.mkdirSync(dir, { recursive: true });
156
+ Fs.writeFileSync(normalized, content, "utf-8");
157
+ } catch {
158
+ }
159
+ }
160
+ function callUserHook(hook, context, ...args) {
161
+ if (!hook)
162
+ return void 0;
163
+ if (typeof hook === "function") {
164
+ return hook.call(context, ...args);
165
+ } else if (hook.handler) {
166
+ return hook.handler.call(context, ...args);
167
+ }
168
+ return void 0;
169
+ }
170
+ function createVirtualPlugin(userConfig) {
171
+ const {
172
+ virtualModuleId,
173
+ dts,
174
+ root,
175
+ watch,
176
+ debounceMs = 2e3,
177
+ ...restConfig
178
+ } = userConfig;
179
+ const VIRTUAL_MODULE_ID = virtualModuleId;
180
+ const {
181
+ resolveId,
182
+ load,
183
+ configResolved,
184
+ configureServer,
185
+ handleHotUpdate,
186
+ watchChange,
187
+ generateModule,
188
+ generateDts,
189
+ ...restHooks
190
+ } = restConfig;
191
+ const moduleCache = /* @__PURE__ */ new Map();
192
+ const state = new VirtualModuleState();
193
+ let resolvedViteConfig;
194
+ let watchPatterns = [];
195
+ let isWatchedPath = () => true;
196
+ const performInvalidate = (server) => {
197
+ if (state.isServerClosing)
198
+ return;
199
+ const mods = invalidateModules(server, VIRTUAL_MODULE_ID, moduleCache);
200
+ sendHmrUpdate(server, mods);
201
+ };
202
+ const handleFileChange = (server) => {
203
+ if (!state.isInitialized) {
204
+ state.hasPendingChange = true;
205
+ return;
206
+ }
207
+ performInvalidate(server);
208
+ };
209
+ return {
210
+ /**
211
+ * 解析虚拟模块 ID
212
+ * 当 import 语句引用虚拟模块时,Vite 会调用此方法
213
+ * @param args - resolveId 的所有参数
214
+ * @returns 如果匹配虚拟模块 ID,返回该 ID;否则返回 undefined
215
+ */
216
+ resolveId(...args) {
217
+ const [id] = args;
218
+ if (id === VIRTUAL_MODULE_ID || id.startsWith(`${VIRTUAL_MODULE_ID}/`))
219
+ return id;
220
+ return callUserHook(resolveId, this, ...args);
221
+ },
222
+ /**
223
+ * 配置解析完成钩子
224
+ * 在 Vite 配置解析完成后调用,用于初始化监听路径和生成类型声明文件
225
+ * @param args - configResolved 的所有参数
226
+ */
227
+ configResolved(...args) {
228
+ const [config] = args;
229
+ resolvedViteConfig = config;
230
+ const rootDir = root || config.root;
231
+ const patterns = Array.isArray(watch) ? watch : watch ? [watch] : [];
232
+ watchPatterns = resolvePatternsToAbsolute(patterns, rootDir);
233
+ const watchPrefixes = watchPatterns.map(extractStaticPrefixFromGlob);
234
+ isWatchedPath = watchPrefixes.length === 0 ? () => true : createMatcher(watchPrefixes);
235
+ writeDtsFile(config, rootDir, dts, generateDts);
236
+ callUserHook(configResolved, this, ...args);
237
+ },
238
+ /**
239
+ * 配置开发服务器钩子
240
+ * 在开发服务器启动时调用,用于设置文件监听、信号处理和清理逻辑
241
+ * @param args - configureServer 的所有参数
242
+ */
243
+ configureServer(...args) {
244
+ var _a;
245
+ const [server] = args;
246
+ const handleSignal = () => {
247
+ var _a2;
248
+ if (state.isServerClosing)
249
+ return;
250
+ state.isServerClosing = true;
251
+ Promise.resolve((_a2 = server == null ? void 0 : server.close) == null ? void 0 : _a2.call(server)).finally(() => {
252
+ try {
253
+ Process.exit(0);
254
+ } catch {
255
+ }
256
+ });
257
+ };
258
+ Process.once("SIGINT", handleSignal);
259
+ Process.once("SIGTERM", handleSignal);
260
+ (_a = server.httpServer) == null ? void 0 : _a.once("close", () => {
261
+ state.isServerClosing = true;
262
+ state.clearAll();
263
+ });
264
+ setupFileWatcher({
265
+ server,
266
+ watchPatterns,
267
+ isWatchedPath,
268
+ onInvalidate: () => handleFileChange(server),
269
+ debounceMs,
270
+ onInitialized: () => {
271
+ state.isInitialized = true;
272
+ if (state.hasPendingChange) {
273
+ state.hasPendingChange = false;
274
+ setTimeout(() => {
275
+ if (!state.isServerClosing)
276
+ performInvalidate(server);
277
+ }, 0);
278
+ }
279
+ }
280
+ });
281
+ callUserHook(configureServer, this, ...args);
282
+ },
283
+ /**
284
+ * 处理热更新钩子
285
+ * 当 Vite 检测到文件变化时调用,用于触发虚拟模块的失效和更新
286
+ * @param args - handleHotUpdate 的所有参数
287
+ * @returns 空数组,表示不阻止其他插件的处理
288
+ * @remarks
289
+ * - 使用防抖机制避免频繁触发
290
+ * - 初始化期间的变化会被延迟处理
291
+ * - 只处理匹配监听路径的文件变化
292
+ */
293
+ handleHotUpdate(...args) {
294
+ const [ctx] = args;
295
+ const { server } = ctx;
296
+ const rootDir = root || (resolvedViteConfig == null ? void 0 : resolvedViteConfig.root) || server.config.root;
297
+ const abs = Vite.normalizePath(
298
+ Path.isAbsolute(ctx.file) ? ctx.file : Path.resolve(rootDir, ctx.file)
299
+ );
300
+ if (!isWatchedPath(abs) || state.isServerClosing)
301
+ return;
302
+ if (!state.isInitialized) {
303
+ state.hasPendingChange = true;
304
+ return [];
305
+ }
306
+ if (!state.hmrDebouncedInvalidate) {
307
+ state.hmrDebouncedInvalidate = LodashEs.debounce(
308
+ () => {
309
+ if (state.isServerClosing)
310
+ return;
311
+ performInvalidate(server);
312
+ },
313
+ debounceMs,
314
+ { trailing: true, leading: false }
315
+ );
316
+ }
317
+ state.hmrDebouncedInvalidate();
318
+ return callUserHook(handleHotUpdate, this, ...args) || [];
319
+ },
320
+ /**
321
+ * 监听文件变化钩子
322
+ * 当 Vite 的依赖预构建或文件系统检测到变化时调用
323
+ * 主要用于清理模块缓存,让下次加载时重新生成
324
+ * @param args - watchChange 的所有参数
325
+ * @remarks
326
+ * - 与 handleHotUpdate 不同,此钩子主要用于清理缓存,不触发 HMR
327
+ * - 使用防抖机制避免频繁清理
328
+ * - 初始化期间的变化会被延迟处理
329
+ */
330
+ watchChange(...args) {
331
+ try {
332
+ const [id] = args;
333
+ const rootDir = root || (resolvedViteConfig == null ? void 0 : resolvedViteConfig.root) || Process.cwd();
334
+ const absId = Vite.normalizePath(Path.isAbsolute(id) ? id : Path.resolve(rootDir, id));
335
+ if (!isWatchedPath(absId) || state.isServerClosing) {
336
+ return;
337
+ }
338
+ if (!state.isInitialized) {
339
+ state.hasPendingChange = true;
340
+ return;
341
+ }
342
+ if (!state.watchChangeDebouncedClear) {
343
+ state.watchChangeDebouncedClear = LodashEs.debounce(
344
+ () => {
345
+ if (state.isServerClosing)
346
+ return;
347
+ for (const k of Array.from(moduleCache.keys())) {
348
+ if (k === VIRTUAL_MODULE_ID || k.startsWith(`${VIRTUAL_MODULE_ID}/`))
349
+ moduleCache.delete(k);
350
+ }
351
+ },
352
+ debounceMs,
353
+ { trailing: true, leading: false }
354
+ );
355
+ }
356
+ state.watchChangeDebouncedClear();
357
+ return callUserHook(watchChange, this, ...args);
358
+ } catch {
359
+ }
360
+ },
361
+ /**
362
+ * 加载虚拟模块内容
363
+ * 当 import 语句引用虚拟模块时,Vite 会调用此方法获取模块代码
364
+ * @param args - load 的所有参数
365
+ * @returns 模块代码字符串,如果 ID 不匹配则返回 undefined
366
+ * @remarks
367
+ * - 支持同步和异步的 generateModule 函数
368
+ * - 生成的代码会被缓存,直到模块被失效
369
+ * - 子路径导入(如 'virtual:routes/sub')会传入完整 ID 给 generateModule
370
+ */
371
+ async load(...args) {
372
+ const [id] = args;
373
+ if (id === VIRTUAL_MODULE_ID || id.startsWith(`${VIRTUAL_MODULE_ID}/`)) {
374
+ const code = getType(generateModule, "asyncfunction") ? await generateModule({ id, config: resolvedViteConfig }) : generateModule({ id, config: resolvedViteConfig });
375
+ moduleCache.set(id, code);
376
+ return code;
377
+ }
378
+ return await callUserHook(load, this, ...args);
379
+ },
380
+ // 使用 rest 参数包含所有其他未使用的钩子(包括 name, transform, enforce 等)
381
+ ...restHooks
382
+ };
383
+ }
384
+ export {
385
+ createMatcher,
386
+ createVirtualPlugin,
387
+ extractStaticPrefixFromGlob,
388
+ resolvePatternsToAbsolute
389
+ };
package/es/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
+ import { AutoRoutesConfig, CDNOptions, CompressionPlugin, ImageminPlugin, PluginConfig, PluginType, QiankunPlugin, VisualizerOptions, ViteConfigType } from './src/_types';
1
2
  import { createViteConfig, getViteConfig } from './src/index.ts';
2
3
  export default createViteConfig;
3
4
  export { wrapperEnv } from './src/_utils/index.ts';
4
5
  export { getViteConfig, createViteConfig as ViteConfig };
6
+ export type { AutoRoutesConfig, CDNOptions, CompressionPlugin, ImageminPlugin, PluginConfig, PluginType, QiankunPlugin, VisualizerOptions, ViteConfigType };