@vizejs/vite-plugin 0.13.0 → 0.14.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/index.d.ts +74 -63
- package/dist/index.js +782 -740
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { transformWithOxc } from "vite";
|
|
2
|
-
import path from "node:path";
|
|
3
1
|
import fs from "node:fs";
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import path from "node:path";
|
|
6
4
|
import { glob } from "tinyglobby";
|
|
7
5
|
import * as native from "@vizejs/native";
|
|
8
|
-
import {
|
|
6
|
+
import { pathToFileURL } from "node:url";
|
|
7
|
+
import { transformWithOxc } from "vite";
|
|
8
|
+
import { createRequire } from "node:module";
|
|
9
9
|
|
|
10
10
|
//#region src/hmr.ts
|
|
11
11
|
/**
|
|
@@ -67,7 +67,73 @@ if (import.meta.hot) {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
//#endregion
|
|
70
|
-
//#region src/utils.ts
|
|
70
|
+
//#region src/utils/css.ts
|
|
71
|
+
/**
|
|
72
|
+
* Resolve CSS @import statements by inlining the imported files,
|
|
73
|
+
* then resolve @custom-media definitions within the combined CSS.
|
|
74
|
+
*
|
|
75
|
+
* This is necessary because Vize embeds CSS as a JS string via
|
|
76
|
+
* document.createElement('style'), bypassing Vite's CSS pipeline.
|
|
77
|
+
*/
|
|
78
|
+
function resolveCssImports(css, importer, aliasRules, isDev, devUrlBase) {
|
|
79
|
+
const customMedia = new Map();
|
|
80
|
+
const importRegex = /^@import\s+(?:"([^"]+)"|'([^']+)');?\s*$/gm;
|
|
81
|
+
let result = css;
|
|
82
|
+
result = result.replace(importRegex, (_match, dqPath, sqPath) => {
|
|
83
|
+
const importPath = dqPath || sqPath;
|
|
84
|
+
if (!importPath) return _match;
|
|
85
|
+
const resolved = resolveCssPath(importPath, importer, aliasRules);
|
|
86
|
+
if (!resolved || !fs.existsSync(resolved)) return _match;
|
|
87
|
+
try {
|
|
88
|
+
const content = fs.readFileSync(resolved, "utf-8");
|
|
89
|
+
parseCustomMedia(content, customMedia);
|
|
90
|
+
return content;
|
|
91
|
+
} catch {
|
|
92
|
+
return _match;
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
parseCustomMedia(result, customMedia);
|
|
96
|
+
result = result.replace(/^@custom-media\s+[^;]+;\s*$/gm, "");
|
|
97
|
+
if (customMedia.size > 0) for (const [name, query] of customMedia) {
|
|
98
|
+
const escaped = name.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
|
99
|
+
result = result.replace(new RegExp(`\\(${escaped}\\)`, "g"), query);
|
|
100
|
+
}
|
|
101
|
+
if (isDev) result = result.replace(/url\(\s*(["']?)([^"')]+)\1\s*\)/g, (_match, quote, urlPath) => {
|
|
102
|
+
const trimmed = urlPath.trim();
|
|
103
|
+
if (trimmed.startsWith("data:") || trimmed.startsWith("http://") || trimmed.startsWith("https://") || trimmed.startsWith("/@fs/")) return _match;
|
|
104
|
+
const resolved = resolveCssPath(trimmed, importer, aliasRules);
|
|
105
|
+
if (resolved && fs.existsSync(resolved)) {
|
|
106
|
+
const normalized = resolved.replace(/\\/g, "/");
|
|
107
|
+
const base = devUrlBase ?? "/";
|
|
108
|
+
const prefix = base.endsWith("/") ? base : base + "/";
|
|
109
|
+
return `url("${prefix}@fs${normalized}")`;
|
|
110
|
+
}
|
|
111
|
+
return _match;
|
|
112
|
+
});
|
|
113
|
+
result = result.replace(/:deep\(([^()]*(?:\([^()]*\))*[^()]*)\)/g, "$1");
|
|
114
|
+
result = result.replace(/\n{3,}/g, "\n\n");
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
function parseCustomMedia(css, map) {
|
|
118
|
+
const re = /@custom-media\s+(--[\w-]+)\s+(.+?)\s*;/g;
|
|
119
|
+
let m;
|
|
120
|
+
while ((m = re.exec(css)) !== null) map.set(m[1], m[2]);
|
|
121
|
+
}
|
|
122
|
+
function resolveCssPath(importPath, importer, aliasRules) {
|
|
123
|
+
for (const rule of aliasRules) if (importPath.startsWith(rule.find)) {
|
|
124
|
+
const resolved = importPath.replace(rule.find, rule.replacement);
|
|
125
|
+
return path.resolve(resolved);
|
|
126
|
+
}
|
|
127
|
+
if (importPath.startsWith(".")) {
|
|
128
|
+
const dir = path.dirname(importer);
|
|
129
|
+
return path.resolve(dir, importPath);
|
|
130
|
+
}
|
|
131
|
+
if (path.isAbsolute(importPath)) return importPath;
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
//#endregion
|
|
136
|
+
//#region src/utils/index.ts
|
|
71
137
|
/** Known CSS preprocessor languages that must be delegated to Vite */
|
|
72
138
|
const PREPROCESSOR_LANGS = new Set([
|
|
73
139
|
"scss",
|
|
@@ -178,162 +244,122 @@ ${output}`;
|
|
|
178
244
|
if (!isProduction && isDev && hasExportDefault) output += generateHmrCode(compiled.scopeId, hmrUpdateType ?? "full-reload");
|
|
179
245
|
return output;
|
|
180
246
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
*/
|
|
188
|
-
function
|
|
189
|
-
|
|
190
|
-
const importRegex = /^@import\s+(?:"([^"]+)"|'([^']+)');?\s*$/gm;
|
|
191
|
-
let result = css;
|
|
192
|
-
result = result.replace(importRegex, (_match, dqPath, sqPath) => {
|
|
193
|
-
const importPath = dqPath || sqPath;
|
|
194
|
-
if (!importPath) return _match;
|
|
195
|
-
const resolved = resolveCssPath(importPath, importer, aliasRules);
|
|
196
|
-
if (!resolved || !fs.existsSync(resolved)) return _match;
|
|
197
|
-
try {
|
|
198
|
-
const content = fs.readFileSync(resolved, "utf-8");
|
|
199
|
-
parseCustomMedia(content, customMedia);
|
|
200
|
-
return content;
|
|
201
|
-
} catch {
|
|
202
|
-
return _match;
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
parseCustomMedia(result, customMedia);
|
|
206
|
-
result = result.replace(/^@custom-media\s+[^;]+;\s*$/gm, "");
|
|
207
|
-
if (customMedia.size > 0) for (const [name, query] of customMedia) {
|
|
208
|
-
const escaped = name.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
|
209
|
-
result = result.replace(new RegExp(`\\(${escaped}\\)`, "g"), query);
|
|
210
|
-
}
|
|
211
|
-
if (isDev) result = result.replace(/url\(\s*(["']?)([^"')]+)\1\s*\)/g, (_match, quote, urlPath) => {
|
|
212
|
-
const trimmed = urlPath.trim();
|
|
213
|
-
if (trimmed.startsWith("data:") || trimmed.startsWith("http://") || trimmed.startsWith("https://") || trimmed.startsWith("/@fs/")) return _match;
|
|
214
|
-
const resolved = resolveCssPath(trimmed, importer, aliasRules);
|
|
215
|
-
if (resolved && fs.existsSync(resolved)) {
|
|
216
|
-
const normalized = resolved.replace(/\\/g, "/");
|
|
217
|
-
const base = devUrlBase ?? "/";
|
|
218
|
-
const prefix = base.endsWith("/") ? base : base + "/";
|
|
219
|
-
return `url("${prefix}@fs${normalized}")`;
|
|
220
|
-
}
|
|
221
|
-
return _match;
|
|
222
|
-
});
|
|
223
|
-
result = result.replace(/:deep\(([^()]*(?:\([^()]*\))*[^()]*)\)/g, "$1");
|
|
224
|
-
result = result.replace(/\n{3,}/g, "\n\n");
|
|
225
|
-
return result;
|
|
247
|
+
|
|
248
|
+
//#endregion
|
|
249
|
+
//#region src/virtual.ts
|
|
250
|
+
const LEGACY_VIZE_PREFIX = "\0vize:";
|
|
251
|
+
const VIRTUAL_CSS_MODULE = "virtual:vize-styles";
|
|
252
|
+
const RESOLVED_CSS_MODULE = "\0vize:all-styles.css";
|
|
253
|
+
/** Check if a module ID is a vize-compiled virtual module */
|
|
254
|
+
function isVizeVirtual(id) {
|
|
255
|
+
return id.startsWith("\0") && id.endsWith(".vue.ts");
|
|
226
256
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
while ((m = re.exec(css)) !== null) map.set(m[1], m[2]);
|
|
257
|
+
/** Create a virtual module ID from a real .vue file path */
|
|
258
|
+
function toVirtualId(realPath) {
|
|
259
|
+
return "\0" + realPath + ".ts";
|
|
231
260
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
261
|
+
/** Extract the real .vue file path from a virtual module ID */
|
|
262
|
+
function fromVirtualId(virtualId) {
|
|
263
|
+
return virtualId.slice(1, -3);
|
|
264
|
+
}
|
|
265
|
+
function escapeRegExp(value) {
|
|
266
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
267
|
+
}
|
|
268
|
+
function toBrowserImportPrefix(replacement) {
|
|
269
|
+
const normalized = replacement.replace(/\\/g, "/");
|
|
270
|
+
if (normalized.startsWith("/@fs/")) return normalized;
|
|
271
|
+
if (path.isAbsolute(replacement) && fs.existsSync(replacement)) return `/@fs${normalized}`;
|
|
272
|
+
return normalized;
|
|
273
|
+
}
|
|
274
|
+
function normalizeFsIdForBuild(id) {
|
|
275
|
+
const [pathPart, queryPart] = id.split("?");
|
|
276
|
+
if (!pathPart.startsWith("/@fs/")) return id;
|
|
277
|
+
const normalizedPath = pathPart.slice(4);
|
|
278
|
+
return queryPart ? `${normalizedPath}?${queryPart}` : normalizedPath;
|
|
279
|
+
}
|
|
280
|
+
function rewriteDynamicTemplateImports(code, aliasRules) {
|
|
281
|
+
let rewritten = code;
|
|
282
|
+
for (const rule of aliasRules) {
|
|
283
|
+
const pattern = new RegExp(`\\bimport\\s*\\(\\s*\`${escapeRegExp(rule.fromPrefix)}`, "g");
|
|
284
|
+
rewritten = rewritten.replace(pattern, `import(/* @vite-ignore */ \`${rule.toPrefix}`);
|
|
240
285
|
}
|
|
241
|
-
|
|
242
|
-
return
|
|
286
|
+
rewritten = rewritten.replace(/\bimport\s*\(\s*`/g, "import(/* @vite-ignore */ `");
|
|
287
|
+
return rewritten;
|
|
243
288
|
}
|
|
244
289
|
|
|
245
290
|
//#endregion
|
|
246
|
-
//#region src/
|
|
247
|
-
const { compileSfc, compileSfcBatchWithResults } = native;
|
|
291
|
+
//#region src/transform.ts
|
|
248
292
|
/**
|
|
249
|
-
*
|
|
250
|
-
*
|
|
293
|
+
* Rewrite static asset URLs in compiled template output.
|
|
294
|
+
*
|
|
295
|
+
* Transforms property values like `src: "@/assets/logo.svg"` into import
|
|
296
|
+
* statements hoisted to the top of the module, so Vite's module resolution
|
|
297
|
+
* pipeline handles alias expansion and asset hashing in both dev and build.
|
|
251
298
|
*/
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
let
|
|
257
|
-
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
content,
|
|
266
|
-
lang,
|
|
267
|
-
scoped,
|
|
268
|
-
module: isModule,
|
|
269
|
-
index
|
|
299
|
+
const SCRIPT_EXTENSIONS = /\.(js|mjs|cjs|ts|mts|cts|jsx|tsx)$/i;
|
|
300
|
+
function rewriteStaticAssetUrls(code, aliasRules) {
|
|
301
|
+
let rewritten = code;
|
|
302
|
+
const imports = [];
|
|
303
|
+
let counter = 0;
|
|
304
|
+
for (const rule of aliasRules) {
|
|
305
|
+
const pattern = new RegExp(`("?src"?\\s*:\\s*)(?:"(${escapeRegExp(rule.fromPrefix)}[^"]+)"|'(${escapeRegExp(rule.fromPrefix)}[^']+)')`, "g");
|
|
306
|
+
rewritten = rewritten.replace(pattern, (match, prefix, dqPath, sqPath) => {
|
|
307
|
+
const fullPath = dqPath || sqPath;
|
|
308
|
+
if (fullPath && SCRIPT_EXTENSIONS.test(fullPath)) return match;
|
|
309
|
+
const varName = `__vize_static_${counter++}`;
|
|
310
|
+
imports.push(`import ${varName} from ${JSON.stringify(fullPath)};`);
|
|
311
|
+
return `${prefix}${varName}`;
|
|
270
312
|
});
|
|
271
|
-
index++;
|
|
272
|
-
}
|
|
273
|
-
return blocks;
|
|
274
|
-
}
|
|
275
|
-
function compileFile(filePath, cache, options, source) {
|
|
276
|
-
const content = source ?? fs.readFileSync(filePath, "utf-8");
|
|
277
|
-
const scopeId = generateScopeId(filePath);
|
|
278
|
-
const hasScoped = /<style[^>]*\bscoped\b/.test(content);
|
|
279
|
-
const result = compileSfc(content, {
|
|
280
|
-
filename: filePath,
|
|
281
|
-
sourceMap: options.sourceMap,
|
|
282
|
-
ssr: options.ssr,
|
|
283
|
-
scopeId: hasScoped ? `data-v-${scopeId}` : void 0
|
|
284
|
-
});
|
|
285
|
-
if (result.errors.length > 0) {
|
|
286
|
-
const errorMsg = result.errors.join("\n");
|
|
287
|
-
console.error(`[vize] Compilation error in ${filePath}:\n${errorMsg}`);
|
|
288
313
|
}
|
|
289
|
-
if (
|
|
290
|
-
|
|
291
|
-
});
|
|
292
|
-
const styles = extractStyleBlocks(content);
|
|
293
|
-
const compiled = {
|
|
294
|
-
code: result.code,
|
|
295
|
-
css: result.css,
|
|
296
|
-
scopeId,
|
|
297
|
-
hasScoped,
|
|
298
|
-
styles
|
|
299
|
-
};
|
|
300
|
-
cache.set(filePath, compiled);
|
|
301
|
-
return compiled;
|
|
314
|
+
if (imports.length > 0) rewritten = imports.join("\n") + "\n" + rewritten;
|
|
315
|
+
return rewritten;
|
|
302
316
|
}
|
|
303
317
|
/**
|
|
304
|
-
*
|
|
305
|
-
*
|
|
318
|
+
* Built-in Vite/Vue/Nuxt define keys that are handled by Vite's own transform pipeline.
|
|
319
|
+
* These must NOT be replaced by the vize plugin because:
|
|
320
|
+
* 1. Nuxt runs both client and server Vite builds, each with different values
|
|
321
|
+
* (e.g., import.meta.server = true on server, false on client).
|
|
322
|
+
* 2. Vite's import.meta transform already handles these correctly per-environment.
|
|
306
323
|
*/
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
if (
|
|
332
|
-
|
|
333
|
-
|
|
324
|
+
const BUILTIN_DEFINE_PREFIXES = [
|
|
325
|
+
"import.meta.server",
|
|
326
|
+
"import.meta.client",
|
|
327
|
+
"import.meta.dev",
|
|
328
|
+
"import.meta.test",
|
|
329
|
+
"import.meta.prerender",
|
|
330
|
+
"import.meta.env",
|
|
331
|
+
"import.meta.hot",
|
|
332
|
+
"__VUE_",
|
|
333
|
+
"__NUXT_",
|
|
334
|
+
"process.env"
|
|
335
|
+
];
|
|
336
|
+
function isBuiltinDefine(key) {
|
|
337
|
+
return BUILTIN_DEFINE_PREFIXES.some((prefix) => key === prefix || key.startsWith(prefix + ".") || key.startsWith(prefix + "_"));
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Apply Vite define replacements to code.
|
|
341
|
+
* Replaces keys like `import.meta.vfFeatures.photoSection` with their values.
|
|
342
|
+
* Uses word-boundary-aware matching to avoid replacing inside strings or partial matches.
|
|
343
|
+
*/
|
|
344
|
+
function applyDefineReplacements(code, defines) {
|
|
345
|
+
const sortedKeys = Object.keys(defines).sort((a, b) => b.length - a.length);
|
|
346
|
+
let result = code;
|
|
347
|
+
for (const key of sortedKeys) {
|
|
348
|
+
if (!result.includes(key)) continue;
|
|
349
|
+
const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
350
|
+
const re = new RegExp(escaped + "(?![\\w$.])", "g");
|
|
351
|
+
result = result.replace(re, defines[key]);
|
|
334
352
|
}
|
|
335
353
|
return result;
|
|
336
354
|
}
|
|
355
|
+
function createLogger(debug) {
|
|
356
|
+
return {
|
|
357
|
+
log: (...args) => debug && console.log("[vize]", ...args),
|
|
358
|
+
info: (...args) => console.log("[vize]", ...args),
|
|
359
|
+
warn: (...args) => console.warn("[vize]", ...args),
|
|
360
|
+
error: (...args) => console.error("[vize]", ...args)
|
|
361
|
+
};
|
|
362
|
+
}
|
|
337
363
|
|
|
338
364
|
//#endregion
|
|
339
365
|
//#region src/config.ts
|
|
@@ -413,189 +439,603 @@ async function loadConfigFile(configPath, env) {
|
|
|
413
439
|
const vizeConfigStore = new Map();
|
|
414
440
|
|
|
415
441
|
//#endregion
|
|
416
|
-
//#region src/
|
|
417
|
-
const
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
442
|
+
//#region src/compiler.ts
|
|
443
|
+
const { compileSfc, compileSfcBatchWithResults } = native;
|
|
444
|
+
/**
|
|
445
|
+
* Extract style block metadata from a Vue SFC source string.
|
|
446
|
+
* Parses `<style>` tags to determine lang, scoped, and module attributes.
|
|
447
|
+
*/
|
|
448
|
+
function extractStyleBlocks(source) {
|
|
449
|
+
const blocks = [];
|
|
450
|
+
const styleRegex = /<style([^>]*)>([\s\S]*?)<\/style>/gi;
|
|
451
|
+
let match;
|
|
452
|
+
let index = 0;
|
|
453
|
+
while ((match = styleRegex.exec(source)) !== null) {
|
|
454
|
+
const attrs = match[1];
|
|
455
|
+
const content = match[2];
|
|
456
|
+
const lang = attrs.match(/\blang=["']([^"']+)["']/)?.[1] ?? null;
|
|
457
|
+
const scoped = /\bscoped\b/.test(attrs);
|
|
458
|
+
const moduleMatch = attrs.match(/\bmodule(?:=["']([^"']+)["'])?\b/);
|
|
459
|
+
const isModule = moduleMatch ? moduleMatch[1] || true : false;
|
|
460
|
+
blocks.push({
|
|
461
|
+
content,
|
|
462
|
+
lang,
|
|
463
|
+
scoped,
|
|
464
|
+
module: isModule,
|
|
465
|
+
index
|
|
466
|
+
});
|
|
467
|
+
index++;
|
|
468
|
+
}
|
|
469
|
+
return blocks;
|
|
440
470
|
}
|
|
441
|
-
function
|
|
442
|
-
const
|
|
443
|
-
|
|
444
|
-
const
|
|
445
|
-
|
|
471
|
+
function compileFile(filePath, cache, options, source) {
|
|
472
|
+
const content = source ?? fs.readFileSync(filePath, "utf-8");
|
|
473
|
+
const scopeId = generateScopeId(filePath);
|
|
474
|
+
const hasScoped = /<style[^>]*\bscoped\b/.test(content);
|
|
475
|
+
const result = compileSfc(content, {
|
|
476
|
+
filename: filePath,
|
|
477
|
+
sourceMap: options.sourceMap,
|
|
478
|
+
ssr: options.ssr,
|
|
479
|
+
scopeId: hasScoped ? `data-v-${scopeId}` : void 0
|
|
480
|
+
});
|
|
481
|
+
if (result.errors.length > 0) {
|
|
482
|
+
const errorMsg = result.errors.join("\n");
|
|
483
|
+
console.error(`[vize] Compilation error in ${filePath}:\n${errorMsg}`);
|
|
484
|
+
}
|
|
485
|
+
if (result.warnings.length > 0) result.warnings.forEach((warning) => {
|
|
486
|
+
console.warn(`[vize] Warning in ${filePath}: ${warning}`);
|
|
487
|
+
});
|
|
488
|
+
const styles = extractStyleBlocks(content);
|
|
489
|
+
const compiled = {
|
|
490
|
+
code: result.code,
|
|
491
|
+
css: result.css,
|
|
492
|
+
scopeId,
|
|
493
|
+
hasScoped,
|
|
494
|
+
styles
|
|
495
|
+
};
|
|
496
|
+
cache.set(filePath, compiled);
|
|
497
|
+
return compiled;
|
|
446
498
|
}
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
499
|
+
/**
|
|
500
|
+
* Batch compile multiple files in parallel using native Rust multithreading.
|
|
501
|
+
* Returns per-file results with content hashes for HMR.
|
|
502
|
+
*/
|
|
503
|
+
function compileBatch(files, cache, options) {
|
|
504
|
+
const inputs = files.map((f) => ({
|
|
505
|
+
path: f.path,
|
|
506
|
+
source: f.source
|
|
507
|
+
}));
|
|
508
|
+
const result = compileSfcBatchWithResults(inputs, { ssr: options.ssr });
|
|
509
|
+
const sourceMap = new Map();
|
|
510
|
+
for (const f of files) sourceMap.set(f.path, f.source);
|
|
511
|
+
for (const fileResult of result.results) {
|
|
512
|
+
if (fileResult.errors.length === 0) {
|
|
513
|
+
const source = sourceMap.get(fileResult.path);
|
|
514
|
+
const styles = source ? extractStyleBlocks(source) : void 0;
|
|
515
|
+
cache.set(fileResult.path, {
|
|
516
|
+
code: fileResult.code,
|
|
517
|
+
css: fileResult.css,
|
|
518
|
+
scopeId: fileResult.scopeId,
|
|
519
|
+
hasScoped: fileResult.hasScoped,
|
|
520
|
+
templateHash: fileResult.templateHash,
|
|
521
|
+
styleHash: fileResult.styleHash,
|
|
522
|
+
scriptHash: fileResult.scriptHash,
|
|
523
|
+
styles
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
if (fileResult.errors.length > 0) console.error(`[vize] Compilation error in ${fileResult.path}:\n${fileResult.errors.join("\n")}`);
|
|
527
|
+
if (fileResult.warnings.length > 0) fileResult.warnings.forEach((warning) => {
|
|
528
|
+
console.warn(`[vize] Warning in ${fileResult.path}: ${warning}`);
|
|
529
|
+
});
|
|
452
530
|
}
|
|
453
|
-
|
|
454
|
-
return rewritten;
|
|
531
|
+
return result;
|
|
455
532
|
}
|
|
456
533
|
|
|
457
534
|
//#endregion
|
|
458
|
-
//#region src/
|
|
535
|
+
//#region src/plugin/state.ts
|
|
459
536
|
/**
|
|
460
|
-
*
|
|
461
|
-
*
|
|
462
|
-
* Transforms property values like `src: "@/assets/logo.svg"` into import
|
|
463
|
-
* statements hoisted to the top of the module, so Vite's module resolution
|
|
464
|
-
* pipeline handles alias expansion and asset hashing in both dev and build.
|
|
537
|
+
* Pre-compile all Vue files matching scan patterns.
|
|
465
538
|
*/
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
539
|
+
async function compileAll(state) {
|
|
540
|
+
const startTime = performance.now();
|
|
541
|
+
const files = await glob(state.scanPatterns, {
|
|
542
|
+
cwd: state.root,
|
|
543
|
+
ignore: state.ignorePatterns,
|
|
544
|
+
absolute: true
|
|
545
|
+
});
|
|
546
|
+
state.logger.info(`Pre-compiling ${files.length} Vue files...`);
|
|
547
|
+
const fileContents = [];
|
|
548
|
+
for (const file of files) try {
|
|
549
|
+
const source = fs.readFileSync(file, "utf-8");
|
|
550
|
+
fileContents.push({
|
|
551
|
+
path: file,
|
|
552
|
+
source
|
|
479
553
|
});
|
|
554
|
+
} catch (e) {
|
|
555
|
+
state.logger.error(`Failed to read ${file}:`, e);
|
|
480
556
|
}
|
|
481
|
-
|
|
482
|
-
|
|
557
|
+
const result = compileBatch(fileContents, state.cache, { ssr: state.mergedOptions.ssr ?? false });
|
|
558
|
+
if (state.isProduction) {
|
|
559
|
+
for (const fileResult of result.results) if (fileResult.css) {
|
|
560
|
+
const cached = state.cache.get(fileResult.path);
|
|
561
|
+
const hasDelegated = cached?.styles?.some((s) => s.lang !== null && [
|
|
562
|
+
"scss",
|
|
563
|
+
"sass",
|
|
564
|
+
"less",
|
|
565
|
+
"stylus",
|
|
566
|
+
"styl"
|
|
567
|
+
].includes(s.lang) || s.module !== false);
|
|
568
|
+
if (!hasDelegated) state.collectedCss.set(fileResult.path, resolveCssImports(fileResult.css, fileResult.path, state.cssAliasRules, false));
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
const elapsed = (performance.now() - startTime).toFixed(2);
|
|
572
|
+
state.logger.info(`Pre-compilation complete: ${result.successCount} succeeded, ${result.failedCount} failed (${elapsed}ms, native batch: ${result.timeMs.toFixed(2)}ms)`);
|
|
483
573
|
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
"import.meta.hot",
|
|
499
|
-
"__VUE_",
|
|
500
|
-
"__NUXT_",
|
|
501
|
-
"process.env"
|
|
502
|
-
];
|
|
503
|
-
function isBuiltinDefine(key) {
|
|
504
|
-
return BUILTIN_DEFINE_PREFIXES.some((prefix) => key === prefix || key.startsWith(prefix + ".") || key.startsWith(prefix + "_"));
|
|
574
|
+
|
|
575
|
+
//#endregion
|
|
576
|
+
//#region src/plugin/resolve.ts
|
|
577
|
+
function resolveVuePath(state, id, importer) {
|
|
578
|
+
let resolved;
|
|
579
|
+
if (id.startsWith("/@fs/")) resolved = id.slice(4);
|
|
580
|
+
else if (id.startsWith("/") && !fs.existsSync(id)) resolved = path.resolve(state.root, id.slice(1));
|
|
581
|
+
else if (path.isAbsolute(id)) resolved = id;
|
|
582
|
+
else if (importer) {
|
|
583
|
+
const realImporter = isVizeVirtual(importer) ? fromVirtualId(importer) : importer;
|
|
584
|
+
resolved = path.resolve(path.dirname(realImporter), id);
|
|
585
|
+
} else resolved = path.resolve(state.root, id);
|
|
586
|
+
if (!path.isAbsolute(resolved)) resolved = path.resolve(state.root, resolved);
|
|
587
|
+
return path.normalize(resolved);
|
|
505
588
|
}
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
589
|
+
async function resolveIdHook(ctx, state, id, importer) {
|
|
590
|
+
const isBuild = state.server === null;
|
|
591
|
+
if (id.startsWith("\0")) {
|
|
592
|
+
if (isVizeVirtual(id)) return null;
|
|
593
|
+
if (id.startsWith(LEGACY_VIZE_PREFIX)) {
|
|
594
|
+
const rawPath = id.slice(LEGACY_VIZE_PREFIX.length);
|
|
595
|
+
const cleanPath$1 = rawPath.endsWith(".ts") ? rawPath.slice(0, -3) : rawPath;
|
|
596
|
+
if (!cleanPath$1.endsWith(".vue")) {
|
|
597
|
+
state.logger.log(`resolveId: redirecting legacy virtual ID to ${cleanPath$1}`);
|
|
598
|
+
return cleanPath$1;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
const cleanPath = id.slice(1);
|
|
602
|
+
if (cleanPath.startsWith("/") && !cleanPath.endsWith(".vue.ts")) {
|
|
603
|
+
const [pathPart, queryPart] = cleanPath.split("?");
|
|
604
|
+
const querySuffix = queryPart ? `?${queryPart}` : "";
|
|
605
|
+
state.logger.log(`resolveId: redirecting \0-prefixed non-vue ID to ${pathPart}${querySuffix}`);
|
|
606
|
+
const redirected = pathPart + querySuffix;
|
|
607
|
+
return isBuild ? normalizeFsIdForBuild(redirected) : redirected;
|
|
608
|
+
}
|
|
609
|
+
return null;
|
|
519
610
|
}
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
611
|
+
if (id.startsWith("vize:")) {
|
|
612
|
+
let realPath = id.slice(5);
|
|
613
|
+
if (realPath.endsWith(".ts")) realPath = realPath.slice(0, -3);
|
|
614
|
+
state.logger.log(`resolveId: redirecting stale vize: ID to ${realPath}`);
|
|
615
|
+
const resolved = await ctx.resolve(realPath, importer, { skipSelf: true });
|
|
616
|
+
if (resolved && isBuild && resolved.id.startsWith("/@fs/")) return {
|
|
617
|
+
...resolved,
|
|
618
|
+
id: normalizeFsIdForBuild(resolved.id)
|
|
619
|
+
};
|
|
620
|
+
return resolved;
|
|
621
|
+
}
|
|
622
|
+
if (id === VIRTUAL_CSS_MODULE) return RESOLVED_CSS_MODULE;
|
|
623
|
+
if (isBuild && id.startsWith("/@fs/")) return normalizeFsIdForBuild(id);
|
|
624
|
+
if (id.includes("?macro=true")) {
|
|
625
|
+
const filePath = id.split("?")[0];
|
|
626
|
+
const resolved = resolveVuePath(state, filePath, importer);
|
|
627
|
+
if (resolved && fs.existsSync(resolved)) return `\0${resolved}?macro=true`;
|
|
628
|
+
}
|
|
629
|
+
if (id.includes("?vue&type=style") || id.includes("?vue=&type=style")) {
|
|
630
|
+
const params = new URLSearchParams(id.split("?")[1]);
|
|
631
|
+
const lang = params.get("lang") || "css";
|
|
632
|
+
if (params.has("module")) return `\0${id}.module.${lang}`;
|
|
633
|
+
return `\0${id}.${lang}`;
|
|
634
|
+
}
|
|
635
|
+
const isMacroImporter = importer?.startsWith("\0") && importer?.endsWith("?macro=true");
|
|
636
|
+
if (importer && (isVizeVirtual(importer) || isMacroImporter)) {
|
|
637
|
+
const cleanImporter = isMacroImporter ? importer.slice(1).replace("?macro=true", "") : fromVirtualId(importer);
|
|
638
|
+
state.logger.log(`resolveId from virtual: id=${id}, cleanImporter=${cleanImporter}`);
|
|
639
|
+
if (id.startsWith("#")) try {
|
|
640
|
+
return await ctx.resolve(id, cleanImporter, { skipSelf: true });
|
|
641
|
+
} catch {
|
|
642
|
+
return null;
|
|
643
|
+
}
|
|
644
|
+
if (!id.endsWith(".vue")) {
|
|
645
|
+
if (!id.startsWith("./") && !id.startsWith("../") && !id.startsWith("/")) {
|
|
646
|
+
const matchesAlias = state.cssAliasRules.some((rule) => id === rule.find || id.startsWith(rule.find + "/"));
|
|
647
|
+
if (!matchesAlias) try {
|
|
648
|
+
const resolved = await ctx.resolve(id, cleanImporter, { skipSelf: true });
|
|
649
|
+
if (resolved) {
|
|
650
|
+
state.logger.log(`resolveId: resolved bare ${id} to ${resolved.id} via Vite resolver`);
|
|
651
|
+
if (isBuild && resolved.id.startsWith("/@fs/")) return {
|
|
652
|
+
...resolved,
|
|
653
|
+
id: normalizeFsIdForBuild(resolved.id)
|
|
654
|
+
};
|
|
655
|
+
return resolved;
|
|
656
|
+
}
|
|
657
|
+
} catch {}
|
|
658
|
+
return null;
|
|
659
|
+
}
|
|
660
|
+
try {
|
|
661
|
+
const resolved = await ctx.resolve(id, cleanImporter, { skipSelf: true });
|
|
662
|
+
if (resolved) {
|
|
663
|
+
state.logger.log(`resolveId: resolved ${id} to ${resolved.id} via Vite resolver`);
|
|
664
|
+
if (isBuild && resolved.id.startsWith("/@fs/")) return {
|
|
665
|
+
...resolved,
|
|
666
|
+
id: normalizeFsIdForBuild(resolved.id)
|
|
667
|
+
};
|
|
668
|
+
return resolved;
|
|
669
|
+
}
|
|
670
|
+
} catch {}
|
|
671
|
+
if (id.startsWith("./") || id.startsWith("../")) {
|
|
672
|
+
const [pathPart, queryPart] = id.split("?");
|
|
673
|
+
const querySuffix = queryPart ? `?${queryPart}` : "";
|
|
674
|
+
const resolved = path.resolve(path.dirname(cleanImporter), pathPart);
|
|
675
|
+
for (const ext of [
|
|
676
|
+
"",
|
|
677
|
+
".ts",
|
|
678
|
+
".tsx",
|
|
679
|
+
".js",
|
|
680
|
+
".jsx",
|
|
681
|
+
".json"
|
|
682
|
+
]) {
|
|
683
|
+
const candidate = resolved + ext;
|
|
684
|
+
if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
|
|
685
|
+
const finalPath = candidate + querySuffix;
|
|
686
|
+
state.logger.log(`resolveId: resolved relative ${id} to ${finalPath}`);
|
|
687
|
+
return finalPath;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
if (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory()) for (const indexFile of [
|
|
691
|
+
"/index.ts",
|
|
692
|
+
"/index.tsx",
|
|
693
|
+
"/index.js",
|
|
694
|
+
"/index.jsx"
|
|
695
|
+
]) {
|
|
696
|
+
const candidate = resolved + indexFile;
|
|
697
|
+
if (fs.existsSync(candidate)) {
|
|
698
|
+
const finalPath = candidate + querySuffix;
|
|
699
|
+
state.logger.log(`resolveId: resolved directory ${id} to ${finalPath}`);
|
|
700
|
+
return finalPath;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
return null;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
if (id.endsWith(".vue")) {
|
|
708
|
+
const handleNodeModules = state.initialized ? state.mergedOptions.handleNodeModulesVue ?? true : true;
|
|
709
|
+
if (!handleNodeModules && id.includes("node_modules")) {
|
|
710
|
+
state.logger.log(`resolveId: skipping node_modules import ${id}`);
|
|
711
|
+
return null;
|
|
712
|
+
}
|
|
713
|
+
const resolved = resolveVuePath(state, id, importer);
|
|
714
|
+
const isNodeModulesPath = resolved.includes("node_modules");
|
|
715
|
+
if (!handleNodeModules && isNodeModulesPath) {
|
|
716
|
+
state.logger.log(`resolveId: skipping node_modules path ${resolved}`);
|
|
717
|
+
return null;
|
|
718
|
+
}
|
|
719
|
+
if (state.filter && !isNodeModulesPath && !state.filter(resolved)) {
|
|
720
|
+
state.logger.log(`resolveId: skipping filtered path ${resolved}`);
|
|
721
|
+
return null;
|
|
722
|
+
}
|
|
723
|
+
const hasCache = state.cache.has(resolved);
|
|
724
|
+
const fileExists = fs.existsSync(resolved);
|
|
725
|
+
state.logger.log(`resolveId: id=${id}, resolved=${resolved}, hasCache=${hasCache}, fileExists=${fileExists}, importer=${importer ?? "none"}`);
|
|
726
|
+
if (hasCache || fileExists) return toVirtualId(resolved);
|
|
727
|
+
if (!fileExists && !path.isAbsolute(id)) {
|
|
728
|
+
const viteResolved = await ctx.resolve(id, importer, { skipSelf: true });
|
|
729
|
+
if (viteResolved && viteResolved.id.endsWith(".vue")) {
|
|
730
|
+
const realPath = viteResolved.id;
|
|
731
|
+
const isResolvedNodeModules = realPath.includes("node_modules");
|
|
732
|
+
if ((isResolvedNodeModules ? handleNodeModules : state.filter(realPath)) && (state.cache.has(realPath) || fs.existsSync(realPath))) {
|
|
733
|
+
state.logger.log(`resolveId: resolved via Vite fallback ${id} to ${realPath}`);
|
|
734
|
+
return toVirtualId(realPath);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
return null;
|
|
529
740
|
}
|
|
530
741
|
|
|
531
742
|
//#endregion
|
|
532
|
-
//#region src/plugin.ts
|
|
533
|
-
function
|
|
534
|
-
const
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
let
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
743
|
+
//#region src/plugin/load.ts
|
|
744
|
+
function loadHook(state, id, loadOptions) {
|
|
745
|
+
const currentBase = loadOptions?.ssr ? state.serverViteBase : state.clientViteBase;
|
|
746
|
+
if (id === RESOLVED_CSS_MODULE) {
|
|
747
|
+
const allCss = Array.from(state.collectedCss.values()).join("\n\n");
|
|
748
|
+
return allCss;
|
|
749
|
+
}
|
|
750
|
+
let styleId = id;
|
|
751
|
+
if (id.startsWith("\0") && id.includes("?vue")) styleId = id.slice(1).replace(/\.module\.\w+$/, "").replace(/\.\w+$/, "");
|
|
752
|
+
if (styleId.includes("?vue&type=style") || styleId.includes("?vue=&type=style")) {
|
|
753
|
+
const [filename, queryString] = styleId.split("?");
|
|
754
|
+
const realPath = isVizeVirtual(filename) ? fromVirtualId(filename) : filename;
|
|
755
|
+
const params = new URLSearchParams(queryString);
|
|
756
|
+
const indexStr = params.get("index");
|
|
757
|
+
const lang = params.get("lang");
|
|
758
|
+
const _hasModule = params.has("module");
|
|
759
|
+
const scoped = params.get("scoped");
|
|
760
|
+
const compiled = state.cache.get(realPath);
|
|
761
|
+
const blockIndex = indexStr !== null ? parseInt(indexStr, 10) : -1;
|
|
762
|
+
if (compiled?.styles && blockIndex >= 0 && blockIndex < compiled.styles.length) {
|
|
763
|
+
const block = compiled.styles[blockIndex];
|
|
764
|
+
let styleContent = block.content;
|
|
765
|
+
if (scoped && block.scoped && lang && lang !== "css") {
|
|
766
|
+
const lines = styleContent.split("\n");
|
|
767
|
+
const hoisted = [];
|
|
768
|
+
const body = [];
|
|
769
|
+
for (const line of lines) {
|
|
770
|
+
const trimmed = line.trimStart();
|
|
771
|
+
if (trimmed.startsWith("@use ") || trimmed.startsWith("@forward ") || trimmed.startsWith("@import ")) hoisted.push(line);
|
|
772
|
+
else body.push(line);
|
|
773
|
+
}
|
|
774
|
+
const bodyContent = body.join("\n");
|
|
775
|
+
const hoistedContent = hoisted.length > 0 ? hoisted.join("\n") + "\n\n" : "";
|
|
776
|
+
styleContent = `${hoistedContent}[${scoped}] {\n${bodyContent}\n}`;
|
|
777
|
+
}
|
|
778
|
+
return {
|
|
779
|
+
code: styleContent,
|
|
780
|
+
map: null
|
|
781
|
+
};
|
|
782
|
+
}
|
|
783
|
+
if (compiled?.css) return resolveCssImports(compiled.css, realPath, state.cssAliasRules, state.server !== null, currentBase);
|
|
784
|
+
return "";
|
|
785
|
+
}
|
|
786
|
+
if (id.startsWith("\0") && id.endsWith("?macro=true")) {
|
|
787
|
+
const realPath = id.slice(1).replace("?macro=true", "");
|
|
788
|
+
if (fs.existsSync(realPath)) {
|
|
789
|
+
const source = fs.readFileSync(realPath, "utf-8");
|
|
790
|
+
const setupMatch = source.match(/<script\s+setup[^>]*>([\s\S]*?)<\/script>/);
|
|
791
|
+
if (setupMatch) {
|
|
792
|
+
const scriptContent = setupMatch[1];
|
|
793
|
+
return {
|
|
794
|
+
code: `${scriptContent}\nexport default {}`,
|
|
795
|
+
map: null
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
return {
|
|
800
|
+
code: "export default {}",
|
|
801
|
+
map: null
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
if (isVizeVirtual(id)) {
|
|
805
|
+
const realPath = fromVirtualId(id);
|
|
806
|
+
if (!realPath.endsWith(".vue")) {
|
|
807
|
+
state.logger.log(`load: skipping non-vue virtual module ${realPath}`);
|
|
808
|
+
return null;
|
|
809
|
+
}
|
|
810
|
+
let compiled = state.cache.get(realPath);
|
|
811
|
+
if (!compiled && fs.existsSync(realPath)) {
|
|
812
|
+
state.logger.log(`load: on-demand compiling ${realPath}`);
|
|
813
|
+
compiled = compileFile(realPath, state.cache, {
|
|
814
|
+
sourceMap: state.mergedOptions?.sourceMap ?? !(state.isProduction ?? false),
|
|
815
|
+
ssr: state.mergedOptions?.ssr ?? false
|
|
566
816
|
});
|
|
817
|
+
}
|
|
818
|
+
if (compiled) {
|
|
819
|
+
const hasDelegated = compiled.styles?.some((s) => s.lang !== null && [
|
|
820
|
+
"scss",
|
|
821
|
+
"sass",
|
|
822
|
+
"less",
|
|
823
|
+
"stylus",
|
|
824
|
+
"styl"
|
|
825
|
+
].includes(s.lang) || s.module !== false);
|
|
826
|
+
if (compiled.css && !hasDelegated) compiled = {
|
|
827
|
+
...compiled,
|
|
828
|
+
css: resolveCssImports(compiled.css, realPath, state.cssAliasRules, state.server !== null, currentBase)
|
|
829
|
+
};
|
|
830
|
+
const output = rewriteStaticAssetUrls(rewriteDynamicTemplateImports(generateOutput(compiled, {
|
|
831
|
+
isProduction: state.isProduction,
|
|
832
|
+
isDev: state.server !== null,
|
|
833
|
+
extractCss: state.extractCss,
|
|
834
|
+
filePath: realPath
|
|
835
|
+
}), state.dynamicImportAliasRules), state.dynamicImportAliasRules);
|
|
836
|
+
return {
|
|
837
|
+
code: output,
|
|
838
|
+
map: null
|
|
839
|
+
};
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
if (id.startsWith("\0")) {
|
|
843
|
+
const afterPrefix = id.startsWith(LEGACY_VIZE_PREFIX) ? id.slice(LEGACY_VIZE_PREFIX.length) : id.slice(1);
|
|
844
|
+
if (afterPrefix.includes("?commonjs-")) return null;
|
|
845
|
+
const [pathPart, queryPart] = afterPrefix.split("?");
|
|
846
|
+
const querySuffix = queryPart ? `?${queryPart}` : "";
|
|
847
|
+
const fsPath = pathPart.startsWith("/@fs/") ? pathPart.slice(4) : pathPart;
|
|
848
|
+
if (fsPath.startsWith("/") && fs.existsSync(fsPath) && fs.statSync(fsPath).isFile()) {
|
|
849
|
+
const importPath = state.server === null ? `${pathToFileURL(fsPath).href}${querySuffix}` : "/@fs" + fsPath + querySuffix;
|
|
850
|
+
state.logger.log(`load: proxying \0-prefixed file ${id} -> re-export from ${importPath}`);
|
|
851
|
+
return `export { default } from ${JSON.stringify(importPath)};\nexport * from ${JSON.stringify(importPath)};`;
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
return null;
|
|
855
|
+
}
|
|
856
|
+
async function transformHook(state, code, id, options) {
|
|
857
|
+
const isMacro = id.startsWith("\0") && id.endsWith("?macro=true");
|
|
858
|
+
if (isVizeVirtual(id) || isMacro) {
|
|
859
|
+
const realPath = isMacro ? id.slice(1).replace("?macro=true", "") : fromVirtualId(id);
|
|
860
|
+
try {
|
|
861
|
+
const result = await transformWithOxc(code, realPath, { lang: "ts" });
|
|
862
|
+
const defines = options?.ssr ? state.serverViteDefine : state.clientViteDefine;
|
|
863
|
+
let transformed = result.code;
|
|
864
|
+
if (Object.keys(defines).length > 0) transformed = applyDefineReplacements(transformed, defines);
|
|
865
|
+
return {
|
|
866
|
+
code: transformed,
|
|
867
|
+
map: result.map
|
|
868
|
+
};
|
|
567
869
|
} catch (e) {
|
|
568
|
-
logger.error(`
|
|
870
|
+
state.logger.error(`transformWithOxc failed for ${realPath}:`, e);
|
|
871
|
+
const dumpPath = `/tmp/vize-oxc-error-${path.basename(realPath)}.ts`;
|
|
872
|
+
fs.writeFileSync(dumpPath, code, "utf-8");
|
|
873
|
+
state.logger.error(`Dumped failing code to ${dumpPath}`);
|
|
874
|
+
return {
|
|
875
|
+
code: "export default {}",
|
|
876
|
+
map: null
|
|
877
|
+
};
|
|
569
878
|
}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
879
|
+
}
|
|
880
|
+
return null;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
//#endregion
|
|
884
|
+
//#region src/plugin/hmr.ts
|
|
885
|
+
async function handleHotUpdateHook(state, ctx) {
|
|
886
|
+
const { file, server, read } = ctx;
|
|
887
|
+
if (file.endsWith(".vue") && state.filter(file)) try {
|
|
888
|
+
const source = await read();
|
|
889
|
+
const prevCompiled = state.cache.get(file);
|
|
890
|
+
compileFile(file, state.cache, {
|
|
891
|
+
sourceMap: state.mergedOptions?.sourceMap ?? !state.isProduction,
|
|
892
|
+
ssr: state.mergedOptions?.ssr ?? false
|
|
893
|
+
}, source);
|
|
894
|
+
const newCompiled = state.cache.get(file);
|
|
895
|
+
const updateType = detectHmrUpdateType(prevCompiled, newCompiled);
|
|
896
|
+
state.logger.log(`Re-compiled: ${path.relative(state.root, file)} (${updateType})`);
|
|
897
|
+
const virtualId = toVirtualId(file);
|
|
898
|
+
const modules = server.moduleGraph.getModulesByFile(virtualId) ?? server.moduleGraph.getModulesByFile(file);
|
|
899
|
+
const hasDelegated = newCompiled.styles?.some((s) => s.lang !== null && [
|
|
900
|
+
"scss",
|
|
901
|
+
"sass",
|
|
902
|
+
"less",
|
|
903
|
+
"stylus",
|
|
904
|
+
"styl"
|
|
905
|
+
].includes(s.lang) || s.module !== false);
|
|
906
|
+
if (hasDelegated && updateType === "style-only") {
|
|
907
|
+
const affectedModules = new Set();
|
|
908
|
+
for (const block of newCompiled.styles ?? []) {
|
|
909
|
+
const params = new URLSearchParams();
|
|
910
|
+
params.set("vue", "");
|
|
911
|
+
params.set("type", "style");
|
|
912
|
+
params.set("index", String(block.index));
|
|
913
|
+
if (block.scoped) params.set("scoped", `data-v-${newCompiled.scopeId}`);
|
|
914
|
+
params.set("lang", block.lang ?? "css");
|
|
915
|
+
if (block.module !== false) params.set("module", typeof block.module === "string" ? block.module : "");
|
|
916
|
+
const styleId = `${file}?${params.toString()}`;
|
|
917
|
+
const styleMods = server.moduleGraph.getModulesByFile(styleId);
|
|
918
|
+
if (styleMods) for (const mod of styleMods) affectedModules.add(mod);
|
|
582
919
|
}
|
|
920
|
+
if (modules) for (const mod of modules) affectedModules.add(mod);
|
|
921
|
+
if (affectedModules.size > 0) return [...affectedModules];
|
|
922
|
+
}
|
|
923
|
+
if (updateType === "style-only" && newCompiled.css && !hasDelegated) {
|
|
924
|
+
server.ws.send({
|
|
925
|
+
type: "custom",
|
|
926
|
+
event: "vize:update",
|
|
927
|
+
data: {
|
|
928
|
+
id: newCompiled.scopeId,
|
|
929
|
+
type: "style-only",
|
|
930
|
+
css: resolveCssImports(newCompiled.css, file, state.cssAliasRules, true, state.clientViteBase)
|
|
931
|
+
}
|
|
932
|
+
});
|
|
933
|
+
return [];
|
|
583
934
|
}
|
|
584
|
-
|
|
585
|
-
|
|
935
|
+
if (modules) return [...modules];
|
|
936
|
+
} catch (e) {
|
|
937
|
+
state.logger.error(`Re-compilation failed for ${file}:`, e);
|
|
586
938
|
}
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
939
|
+
}
|
|
940
|
+
function handleGenerateBundleHook(state, emitFile) {
|
|
941
|
+
if (!state.extractCss || state.collectedCss.size === 0) return;
|
|
942
|
+
const allCss = Array.from(state.collectedCss.values()).join("\n\n");
|
|
943
|
+
if (allCss.trim()) {
|
|
944
|
+
emitFile({
|
|
945
|
+
type: "asset",
|
|
946
|
+
fileName: "assets/vize-components.css",
|
|
947
|
+
source: allCss
|
|
948
|
+
});
|
|
949
|
+
state.logger.log(`Extracted CSS to assets/vize-components.css (${state.collectedCss.size} components)`);
|
|
598
950
|
}
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
//#endregion
|
|
954
|
+
//#region src/plugin/compat.ts
|
|
955
|
+
function createVueCompatPlugin(state) {
|
|
956
|
+
let compilerSfc = null;
|
|
957
|
+
const loadCompilerSfc = () => {
|
|
958
|
+
if (!compilerSfc) try {
|
|
959
|
+
const require = createRequire(import.meta.url);
|
|
960
|
+
compilerSfc = require("@vue/compiler-sfc");
|
|
961
|
+
} catch {
|
|
962
|
+
compilerSfc = { parse: () => ({
|
|
963
|
+
descriptor: {},
|
|
964
|
+
errors: []
|
|
965
|
+
}) };
|
|
966
|
+
}
|
|
967
|
+
return compilerSfc;
|
|
968
|
+
};
|
|
969
|
+
return {
|
|
970
|
+
name: "vite:vue",
|
|
971
|
+
api: { get options() {
|
|
972
|
+
return {
|
|
973
|
+
compiler: loadCompilerSfc(),
|
|
974
|
+
isProduction: state.isProduction ?? false,
|
|
975
|
+
root: state.root ?? process.cwd(),
|
|
976
|
+
template: {}
|
|
977
|
+
};
|
|
978
|
+
} }
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
function createPostTransformPlugin(state) {
|
|
982
|
+
return {
|
|
983
|
+
name: "vize:post-transform",
|
|
984
|
+
enforce: "post",
|
|
985
|
+
async transform(code, id, transformOptions) {
|
|
986
|
+
if (!id.endsWith(".vue") && !id.endsWith(".vue.ts") && !id.includes("node_modules") && id.endsWith(".setup.ts") && /<script\s+setup[\s>]/.test(code)) {
|
|
987
|
+
state.logger.log(`post-transform: compiling virtual SFC content from ${id}`);
|
|
988
|
+
try {
|
|
989
|
+
const compiled = compileFile(id, state.cache, {
|
|
990
|
+
sourceMap: state.mergedOptions?.sourceMap ?? !(state.isProduction ?? false),
|
|
991
|
+
ssr: state.mergedOptions?.ssr ?? false
|
|
992
|
+
}, code);
|
|
993
|
+
const output = generateOutput(compiled, {
|
|
994
|
+
isProduction: state.isProduction,
|
|
995
|
+
isDev: state.server !== null,
|
|
996
|
+
extractCss: state.extractCss,
|
|
997
|
+
filePath: id
|
|
998
|
+
});
|
|
999
|
+
const result = await transformWithOxc(output, id, { lang: "ts" });
|
|
1000
|
+
const defines = transformOptions?.ssr ? state.serverViteDefine : state.clientViteDefine;
|
|
1001
|
+
let transformed = result.code;
|
|
1002
|
+
if (Object.keys(defines).length > 0) transformed = applyDefineReplacements(transformed, defines);
|
|
1003
|
+
return {
|
|
1004
|
+
code: transformed,
|
|
1005
|
+
map: result.map
|
|
1006
|
+
};
|
|
1007
|
+
} catch (e) {
|
|
1008
|
+
state.logger.error(`Virtual SFC compilation failed for ${id}:`, e);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
return null;
|
|
1012
|
+
}
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
//#endregion
|
|
1017
|
+
//#region src/plugin/index.ts
|
|
1018
|
+
function vize(options = {}) {
|
|
1019
|
+
const state = {
|
|
1020
|
+
cache: new Map(),
|
|
1021
|
+
collectedCss: new Map(),
|
|
1022
|
+
isProduction: false,
|
|
1023
|
+
root: "",
|
|
1024
|
+
clientViteBase: "/",
|
|
1025
|
+
serverViteBase: "/",
|
|
1026
|
+
server: null,
|
|
1027
|
+
filter: () => true,
|
|
1028
|
+
scanPatterns: null,
|
|
1029
|
+
ignorePatterns: [],
|
|
1030
|
+
mergedOptions: options,
|
|
1031
|
+
initialized: false,
|
|
1032
|
+
dynamicImportAliasRules: [],
|
|
1033
|
+
cssAliasRules: [],
|
|
1034
|
+
extractCss: false,
|
|
1035
|
+
clientViteDefine: {},
|
|
1036
|
+
serverViteDefine: {},
|
|
1037
|
+
logger: createLogger(options.debug ?? false)
|
|
1038
|
+
};
|
|
599
1039
|
const mainPlugin = {
|
|
600
1040
|
name: "vite-plugin-vize",
|
|
601
1041
|
enforce: "pre",
|
|
@@ -610,12 +1050,12 @@ function vize(options = {}) {
|
|
|
610
1050
|
};
|
|
611
1051
|
},
|
|
612
1052
|
async configResolved(resolvedConfig) {
|
|
613
|
-
root = options.root ?? resolvedConfig.root;
|
|
614
|
-
isProduction = options.isProduction ?? resolvedConfig.isProduction;
|
|
1053
|
+
state.root = options.root ?? resolvedConfig.root;
|
|
1054
|
+
state.isProduction = options.isProduction ?? resolvedConfig.isProduction;
|
|
615
1055
|
const isSsrBuild = !!resolvedConfig.build?.ssr;
|
|
616
|
-
if (isSsrBuild) serverViteBase = resolvedConfig.base ?? "/";
|
|
617
|
-
else clientViteBase = resolvedConfig.base ?? "/";
|
|
618
|
-
extractCss = isProduction;
|
|
1056
|
+
if (isSsrBuild) state.serverViteBase = resolvedConfig.base ?? "/";
|
|
1057
|
+
else state.clientViteBase = resolvedConfig.base ?? "/";
|
|
1058
|
+
state.extractCss = state.isProduction;
|
|
619
1059
|
const isSsr = !!resolvedConfig.build?.ssr;
|
|
620
1060
|
const envDefine = {};
|
|
621
1061
|
if (resolvedConfig.define) for (const [key, value] of Object.entries(resolvedConfig.define)) {
|
|
@@ -623,8 +1063,8 @@ function vize(options = {}) {
|
|
|
623
1063
|
if (typeof value === "string") envDefine[key] = value;
|
|
624
1064
|
else envDefine[key] = JSON.stringify(value);
|
|
625
1065
|
}
|
|
626
|
-
if (isSsr) serverViteDefine = envDefine;
|
|
627
|
-
else clientViteDefine = envDefine;
|
|
1066
|
+
if (isSsr) state.serverViteDefine = envDefine;
|
|
1067
|
+
else state.clientViteDefine = envDefine;
|
|
628
1068
|
const configEnv = {
|
|
629
1069
|
mode: resolvedConfig.mode,
|
|
630
1070
|
command: resolvedConfig.command === "build" ? "build" : "serve",
|
|
@@ -632,19 +1072,19 @@ function vize(options = {}) {
|
|
|
632
1072
|
};
|
|
633
1073
|
let fileConfig = null;
|
|
634
1074
|
if (options.configMode !== false) {
|
|
635
|
-
fileConfig = await loadConfig(root, {
|
|
1075
|
+
fileConfig = await loadConfig(state.root, {
|
|
636
1076
|
mode: options.configMode ?? "root",
|
|
637
1077
|
configFile: options.configFile,
|
|
638
1078
|
env: configEnv
|
|
639
1079
|
});
|
|
640
1080
|
if (fileConfig) {
|
|
641
|
-
logger.log("Loaded config from vize.config file");
|
|
642
|
-
vizeConfigStore.set(root, fileConfig);
|
|
1081
|
+
state.logger.log("Loaded config from vize.config file");
|
|
1082
|
+
vizeConfigStore.set(state.root, fileConfig);
|
|
643
1083
|
}
|
|
644
1084
|
}
|
|
645
1085
|
const viteConfig = fileConfig?.vite ?? {};
|
|
646
1086
|
const compilerConfig = fileConfig?.compiler ?? {};
|
|
647
|
-
mergedOptions = {
|
|
1087
|
+
state.mergedOptions = {
|
|
648
1088
|
...options,
|
|
649
1089
|
ssr: options.ssr ?? compilerConfig.ssr ?? false,
|
|
650
1090
|
sourceMap: options.sourceMap ?? compilerConfig.sourceMap,
|
|
@@ -654,38 +1094,38 @@ function vize(options = {}) {
|
|
|
654
1094
|
scanPatterns: options.scanPatterns ?? viteConfig.scanPatterns,
|
|
655
1095
|
ignorePatterns: options.ignorePatterns ?? viteConfig.ignorePatterns
|
|
656
1096
|
};
|
|
657
|
-
dynamicImportAliasRules = [];
|
|
1097
|
+
state.dynamicImportAliasRules = [];
|
|
658
1098
|
for (const alias of resolvedConfig.resolve.alias) {
|
|
659
1099
|
if (typeof alias.find !== "string" || typeof alias.replacement !== "string") continue;
|
|
660
1100
|
const fromPrefix = alias.find.endsWith("/") ? alias.find : `${alias.find}/`;
|
|
661
1101
|
const replacement = toBrowserImportPrefix(alias.replacement);
|
|
662
1102
|
const toPrefix = replacement.endsWith("/") ? replacement : `${replacement}/`;
|
|
663
|
-
dynamicImportAliasRules.push({
|
|
1103
|
+
state.dynamicImportAliasRules.push({
|
|
664
1104
|
fromPrefix,
|
|
665
1105
|
toPrefix
|
|
666
1106
|
});
|
|
667
1107
|
}
|
|
668
|
-
dynamicImportAliasRules.sort((a, b) => b.fromPrefix.length - a.fromPrefix.length);
|
|
669
|
-
cssAliasRules = [];
|
|
1108
|
+
state.dynamicImportAliasRules.sort((a, b) => b.fromPrefix.length - a.fromPrefix.length);
|
|
1109
|
+
state.cssAliasRules = [];
|
|
670
1110
|
for (const alias of resolvedConfig.resolve.alias) {
|
|
671
1111
|
if (typeof alias.find !== "string" || typeof alias.replacement !== "string") continue;
|
|
672
|
-
cssAliasRules.push({
|
|
1112
|
+
state.cssAliasRules.push({
|
|
673
1113
|
find: alias.find,
|
|
674
1114
|
replacement: alias.replacement
|
|
675
1115
|
});
|
|
676
1116
|
}
|
|
677
|
-
cssAliasRules.sort((a, b) => b.find.length - a.find.length);
|
|
678
|
-
filter = createFilter(mergedOptions.include, mergedOptions.exclude);
|
|
679
|
-
scanPatterns = mergedOptions.scanPatterns ?? ["**/*.vue"];
|
|
680
|
-
ignorePatterns = mergedOptions.ignorePatterns ?? [
|
|
1117
|
+
state.cssAliasRules.sort((a, b) => b.find.length - a.find.length);
|
|
1118
|
+
state.filter = createFilter(state.mergedOptions.include, state.mergedOptions.exclude);
|
|
1119
|
+
state.scanPatterns = state.mergedOptions.scanPatterns ?? ["**/*.vue"];
|
|
1120
|
+
state.ignorePatterns = state.mergedOptions.ignorePatterns ?? [
|
|
681
1121
|
"node_modules/**",
|
|
682
1122
|
"dist/**",
|
|
683
1123
|
".git/**"
|
|
684
1124
|
];
|
|
685
|
-
initialized = true;
|
|
1125
|
+
state.initialized = true;
|
|
686
1126
|
},
|
|
687
1127
|
configureServer(devServer) {
|
|
688
|
-
server = devServer;
|
|
1128
|
+
state.server = devServer;
|
|
689
1129
|
devServer.middlewares.use((req, _res, next) => {
|
|
690
1130
|
if (req.url && req.url.includes("__x00__")) {
|
|
691
1131
|
const [urlPath, queryPart] = req.url.split("?");
|
|
@@ -696,7 +1136,7 @@ function vize(options = {}) {
|
|
|
696
1136
|
if (fsPath.startsWith("/") && fs.existsSync(fsPath) && fs.statSync(fsPath).isFile() && !fsPath.endsWith(".vue.ts")) {
|
|
697
1137
|
const cleaned = queryPart ? `${cleanedPath}?${queryPart}` : cleanedPath;
|
|
698
1138
|
if (cleaned !== req.url) {
|
|
699
|
-
logger.log(`middleware: rewriting ${req.url} -> ${cleaned}`);
|
|
1139
|
+
state.logger.log(`middleware: rewriting ${req.url} -> ${cleaned}`);
|
|
700
1140
|
req.url = cleaned;
|
|
701
1141
|
}
|
|
702
1142
|
}
|
|
@@ -706,428 +1146,30 @@ function vize(options = {}) {
|
|
|
706
1146
|
});
|
|
707
1147
|
},
|
|
708
1148
|
async buildStart() {
|
|
709
|
-
if (!scanPatterns) return;
|
|
710
|
-
await compileAll();
|
|
711
|
-
logger.log("Cache keys:", [...cache.keys()].slice(0, 3));
|
|
1149
|
+
if (!state.scanPatterns) return;
|
|
1150
|
+
await compileAll(state);
|
|
1151
|
+
state.logger.log("Cache keys:", [...state.cache.keys()].slice(0, 3));
|
|
712
1152
|
},
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
if (id.startsWith("\0")) {
|
|
716
|
-
if (isVizeVirtual(id)) return null;
|
|
717
|
-
if (id.startsWith(LEGACY_VIZE_PREFIX)) {
|
|
718
|
-
const rawPath = id.slice(LEGACY_VIZE_PREFIX.length);
|
|
719
|
-
const cleanPath$1 = rawPath.endsWith(".ts") ? rawPath.slice(0, -3) : rawPath;
|
|
720
|
-
if (!cleanPath$1.endsWith(".vue")) {
|
|
721
|
-
logger.log(`resolveId: redirecting legacy virtual ID to ${cleanPath$1}`);
|
|
722
|
-
return cleanPath$1;
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
const cleanPath = id.slice(1);
|
|
726
|
-
if (cleanPath.startsWith("/") && !cleanPath.endsWith(".vue.ts")) {
|
|
727
|
-
const [pathPart, queryPart] = cleanPath.split("?");
|
|
728
|
-
const querySuffix = queryPart ? `?${queryPart}` : "";
|
|
729
|
-
logger.log(`resolveId: redirecting \0-prefixed non-vue ID to ${pathPart}${querySuffix}`);
|
|
730
|
-
const redirected = pathPart + querySuffix;
|
|
731
|
-
return isBuild ? normalizeFsIdForBuild(redirected) : redirected;
|
|
732
|
-
}
|
|
733
|
-
return null;
|
|
734
|
-
}
|
|
735
|
-
if (id.startsWith("vize:")) {
|
|
736
|
-
let realPath = id.slice(5);
|
|
737
|
-
if (realPath.endsWith(".ts")) realPath = realPath.slice(0, -3);
|
|
738
|
-
logger.log(`resolveId: redirecting stale vize: ID to ${realPath}`);
|
|
739
|
-
const resolved = await this.resolve(realPath, importer, { skipSelf: true });
|
|
740
|
-
if (resolved && isBuild && resolved.id.startsWith("/@fs/")) return {
|
|
741
|
-
...resolved,
|
|
742
|
-
id: normalizeFsIdForBuild(resolved.id)
|
|
743
|
-
};
|
|
744
|
-
return resolved;
|
|
745
|
-
}
|
|
746
|
-
if (id === VIRTUAL_CSS_MODULE) return RESOLVED_CSS_MODULE;
|
|
747
|
-
if (isBuild && id.startsWith("/@fs/")) return normalizeFsIdForBuild(id);
|
|
748
|
-
if (id.includes("?macro=true")) {
|
|
749
|
-
const filePath = id.split("?")[0];
|
|
750
|
-
const resolved = resolveVuePath(filePath, importer);
|
|
751
|
-
if (resolved && fs.existsSync(resolved)) return `\0${resolved}?macro=true`;
|
|
752
|
-
}
|
|
753
|
-
if (id.includes("?vue&type=style") || id.includes("?vue=&type=style")) {
|
|
754
|
-
const params = new URLSearchParams(id.split("?")[1]);
|
|
755
|
-
const lang = params.get("lang") || "css";
|
|
756
|
-
if (params.has("module")) return `\0${id}.module.${lang}`;
|
|
757
|
-
return `\0${id}.${lang}`;
|
|
758
|
-
}
|
|
759
|
-
const isMacroImporter = importer?.startsWith("\0") && importer?.endsWith("?macro=true");
|
|
760
|
-
if (importer && (isVizeVirtual(importer) || isMacroImporter)) {
|
|
761
|
-
const cleanImporter = isMacroImporter ? importer.slice(1).replace("?macro=true", "") : fromVirtualId(importer);
|
|
762
|
-
logger.log(`resolveId from virtual: id=${id}, cleanImporter=${cleanImporter}`);
|
|
763
|
-
if (id.startsWith("#")) try {
|
|
764
|
-
return await this.resolve(id, cleanImporter, { skipSelf: true });
|
|
765
|
-
} catch {
|
|
766
|
-
return null;
|
|
767
|
-
}
|
|
768
|
-
if (!id.endsWith(".vue")) {
|
|
769
|
-
if (!id.startsWith("./") && !id.startsWith("../") && !id.startsWith("/")) {
|
|
770
|
-
const matchesAlias = cssAliasRules.some((rule) => id === rule.find || id.startsWith(rule.find + "/"));
|
|
771
|
-
if (!matchesAlias) try {
|
|
772
|
-
const resolved = await this.resolve(id, cleanImporter, { skipSelf: true });
|
|
773
|
-
if (resolved) {
|
|
774
|
-
logger.log(`resolveId: resolved bare ${id} to ${resolved.id} via Vite resolver`);
|
|
775
|
-
if (isBuild && resolved.id.startsWith("/@fs/")) return {
|
|
776
|
-
...resolved,
|
|
777
|
-
id: normalizeFsIdForBuild(resolved.id)
|
|
778
|
-
};
|
|
779
|
-
return resolved;
|
|
780
|
-
}
|
|
781
|
-
} catch {}
|
|
782
|
-
return null;
|
|
783
|
-
}
|
|
784
|
-
try {
|
|
785
|
-
const resolved = await this.resolve(id, cleanImporter, { skipSelf: true });
|
|
786
|
-
if (resolved) {
|
|
787
|
-
logger.log(`resolveId: resolved ${id} to ${resolved.id} via Vite resolver`);
|
|
788
|
-
if (isBuild && resolved.id.startsWith("/@fs/")) return {
|
|
789
|
-
...resolved,
|
|
790
|
-
id: normalizeFsIdForBuild(resolved.id)
|
|
791
|
-
};
|
|
792
|
-
return resolved;
|
|
793
|
-
}
|
|
794
|
-
} catch {}
|
|
795
|
-
if (id.startsWith("./") || id.startsWith("../")) {
|
|
796
|
-
const [pathPart, queryPart] = id.split("?");
|
|
797
|
-
const querySuffix = queryPart ? `?${queryPart}` : "";
|
|
798
|
-
const resolved = path.resolve(path.dirname(cleanImporter), pathPart);
|
|
799
|
-
for (const ext of [
|
|
800
|
-
"",
|
|
801
|
-
".ts",
|
|
802
|
-
".tsx",
|
|
803
|
-
".js",
|
|
804
|
-
".jsx",
|
|
805
|
-
".json"
|
|
806
|
-
]) {
|
|
807
|
-
const candidate = resolved + ext;
|
|
808
|
-
if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
|
|
809
|
-
const finalPath = candidate + querySuffix;
|
|
810
|
-
logger.log(`resolveId: resolved relative ${id} to ${finalPath}`);
|
|
811
|
-
return finalPath;
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
if (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory()) for (const indexFile of [
|
|
815
|
-
"/index.ts",
|
|
816
|
-
"/index.tsx",
|
|
817
|
-
"/index.js",
|
|
818
|
-
"/index.jsx"
|
|
819
|
-
]) {
|
|
820
|
-
const candidate = resolved + indexFile;
|
|
821
|
-
if (fs.existsSync(candidate)) {
|
|
822
|
-
const finalPath = candidate + querySuffix;
|
|
823
|
-
logger.log(`resolveId: resolved directory ${id} to ${finalPath}`);
|
|
824
|
-
return finalPath;
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
return null;
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
if (id.endsWith(".vue")) {
|
|
832
|
-
const handleNodeModules = initialized ? mergedOptions.handleNodeModulesVue ?? true : true;
|
|
833
|
-
if (!handleNodeModules && id.includes("node_modules")) {
|
|
834
|
-
logger.log(`resolveId: skipping node_modules import ${id}`);
|
|
835
|
-
return null;
|
|
836
|
-
}
|
|
837
|
-
const resolved = resolveVuePath(id, importer);
|
|
838
|
-
const isNodeModulesPath = resolved.includes("node_modules");
|
|
839
|
-
if (!handleNodeModules && isNodeModulesPath) {
|
|
840
|
-
logger.log(`resolveId: skipping node_modules path ${resolved}`);
|
|
841
|
-
return null;
|
|
842
|
-
}
|
|
843
|
-
if (filter && !isNodeModulesPath && !filter(resolved)) {
|
|
844
|
-
logger.log(`resolveId: skipping filtered path ${resolved}`);
|
|
845
|
-
return null;
|
|
846
|
-
}
|
|
847
|
-
const hasCache = cache.has(resolved);
|
|
848
|
-
const fileExists = fs.existsSync(resolved);
|
|
849
|
-
logger.log(`resolveId: id=${id}, resolved=${resolved}, hasCache=${hasCache}, fileExists=${fileExists}, importer=${importer ?? "none"}`);
|
|
850
|
-
if (hasCache || fileExists) return toVirtualId(resolved);
|
|
851
|
-
if (!fileExists && !path.isAbsolute(id)) {
|
|
852
|
-
const viteResolved = await this.resolve(id, importer, { skipSelf: true });
|
|
853
|
-
if (viteResolved && viteResolved.id.endsWith(".vue")) {
|
|
854
|
-
const realPath = viteResolved.id;
|
|
855
|
-
const isResolvedNodeModules = realPath.includes("node_modules");
|
|
856
|
-
if ((isResolvedNodeModules ? handleNodeModules : filter(realPath)) && (cache.has(realPath) || fs.existsSync(realPath))) {
|
|
857
|
-
logger.log(`resolveId: resolved via Vite fallback ${id} to ${realPath}`);
|
|
858
|
-
return toVirtualId(realPath);
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
return null;
|
|
1153
|
+
resolveId(id, importer) {
|
|
1154
|
+
return resolveIdHook(this, state, id, importer);
|
|
864
1155
|
},
|
|
865
1156
|
load(id, loadOptions) {
|
|
866
|
-
|
|
867
|
-
if (id === RESOLVED_CSS_MODULE) {
|
|
868
|
-
const allCss = Array.from(collectedCss.values()).join("\n\n");
|
|
869
|
-
return allCss;
|
|
870
|
-
}
|
|
871
|
-
let styleId = id;
|
|
872
|
-
if (id.startsWith("\0") && id.includes("?vue")) styleId = id.slice(1).replace(/\.module\.\w+$/, "").replace(/\.\w+$/, "");
|
|
873
|
-
if (styleId.includes("?vue&type=style") || styleId.includes("?vue=&type=style")) {
|
|
874
|
-
const [filename, queryString] = styleId.split("?");
|
|
875
|
-
const realPath = isVizeVirtual(filename) ? fromVirtualId(filename) : filename;
|
|
876
|
-
const params = new URLSearchParams(queryString);
|
|
877
|
-
const indexStr = params.get("index");
|
|
878
|
-
const lang = params.get("lang");
|
|
879
|
-
const _hasModule = params.has("module");
|
|
880
|
-
const scoped = params.get("scoped");
|
|
881
|
-
const compiled = cache.get(realPath);
|
|
882
|
-
const blockIndex = indexStr !== null ? parseInt(indexStr, 10) : -1;
|
|
883
|
-
if (compiled?.styles && blockIndex >= 0 && blockIndex < compiled.styles.length) {
|
|
884
|
-
const block = compiled.styles[blockIndex];
|
|
885
|
-
let styleContent = block.content;
|
|
886
|
-
if (scoped && block.scoped && lang && lang !== "css") {
|
|
887
|
-
const lines = styleContent.split("\n");
|
|
888
|
-
const hoisted = [];
|
|
889
|
-
const body = [];
|
|
890
|
-
for (const line of lines) {
|
|
891
|
-
const trimmed = line.trimStart();
|
|
892
|
-
if (trimmed.startsWith("@use ") || trimmed.startsWith("@forward ") || trimmed.startsWith("@import ")) hoisted.push(line);
|
|
893
|
-
else body.push(line);
|
|
894
|
-
}
|
|
895
|
-
const bodyContent = body.join("\n");
|
|
896
|
-
const hoistedContent = hoisted.length > 0 ? hoisted.join("\n") + "\n\n" : "";
|
|
897
|
-
styleContent = `${hoistedContent}[${scoped}] {\n${bodyContent}\n}`;
|
|
898
|
-
}
|
|
899
|
-
return {
|
|
900
|
-
code: styleContent,
|
|
901
|
-
map: null
|
|
902
|
-
};
|
|
903
|
-
}
|
|
904
|
-
if (compiled?.css) return resolveCssImports(compiled.css, realPath, cssAliasRules, server !== null, currentBase);
|
|
905
|
-
return "";
|
|
906
|
-
}
|
|
907
|
-
if (id.startsWith("\0") && id.endsWith("?macro=true")) {
|
|
908
|
-
const realPath = id.slice(1).replace("?macro=true", "");
|
|
909
|
-
if (fs.existsSync(realPath)) {
|
|
910
|
-
const source = fs.readFileSync(realPath, "utf-8");
|
|
911
|
-
const setupMatch = source.match(/<script\s+setup[^>]*>([\s\S]*?)<\/script>/);
|
|
912
|
-
if (setupMatch) {
|
|
913
|
-
const scriptContent = setupMatch[1];
|
|
914
|
-
return {
|
|
915
|
-
code: `${scriptContent}\nexport default {}`,
|
|
916
|
-
map: null
|
|
917
|
-
};
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
return {
|
|
921
|
-
code: "export default {}",
|
|
922
|
-
map: null
|
|
923
|
-
};
|
|
924
|
-
}
|
|
925
|
-
if (isVizeVirtual(id)) {
|
|
926
|
-
const realPath = fromVirtualId(id);
|
|
927
|
-
if (!realPath.endsWith(".vue")) {
|
|
928
|
-
logger.log(`load: skipping non-vue virtual module ${realPath}`);
|
|
929
|
-
return null;
|
|
930
|
-
}
|
|
931
|
-
let compiled = cache.get(realPath);
|
|
932
|
-
if (!compiled && fs.existsSync(realPath)) {
|
|
933
|
-
logger.log(`load: on-demand compiling ${realPath}`);
|
|
934
|
-
compiled = compileFile(realPath, cache, {
|
|
935
|
-
sourceMap: mergedOptions?.sourceMap ?? !(isProduction ?? false),
|
|
936
|
-
ssr: mergedOptions?.ssr ?? false
|
|
937
|
-
});
|
|
938
|
-
}
|
|
939
|
-
if (compiled) {
|
|
940
|
-
const hasDelegated = compiled.styles?.some((s) => s.lang !== null && [
|
|
941
|
-
"scss",
|
|
942
|
-
"sass",
|
|
943
|
-
"less",
|
|
944
|
-
"stylus",
|
|
945
|
-
"styl"
|
|
946
|
-
].includes(s.lang) || s.module !== false);
|
|
947
|
-
if (compiled.css && !hasDelegated) compiled = {
|
|
948
|
-
...compiled,
|
|
949
|
-
css: resolveCssImports(compiled.css, realPath, cssAliasRules, server !== null, currentBase)
|
|
950
|
-
};
|
|
951
|
-
const output = rewriteStaticAssetUrls(rewriteDynamicTemplateImports(generateOutput(compiled, {
|
|
952
|
-
isProduction,
|
|
953
|
-
isDev: server !== null,
|
|
954
|
-
extractCss,
|
|
955
|
-
filePath: realPath
|
|
956
|
-
}), dynamicImportAliasRules), dynamicImportAliasRules);
|
|
957
|
-
return {
|
|
958
|
-
code: output,
|
|
959
|
-
map: null
|
|
960
|
-
};
|
|
961
|
-
}
|
|
962
|
-
}
|
|
963
|
-
if (id.startsWith("\0")) {
|
|
964
|
-
const afterPrefix = id.startsWith(LEGACY_VIZE_PREFIX) ? id.slice(LEGACY_VIZE_PREFIX.length) : id.slice(1);
|
|
965
|
-
if (afterPrefix.includes("?commonjs-")) return null;
|
|
966
|
-
const [pathPart, queryPart] = afterPrefix.split("?");
|
|
967
|
-
const querySuffix = queryPart ? `?${queryPart}` : "";
|
|
968
|
-
const fsPath = pathPart.startsWith("/@fs/") ? pathPart.slice(4) : pathPart;
|
|
969
|
-
if (fsPath.startsWith("/") && fs.existsSync(fsPath) && fs.statSync(fsPath).isFile()) {
|
|
970
|
-
const importPath = server === null ? `${pathToFileURL(fsPath).href}${querySuffix}` : "/@fs" + fsPath + querySuffix;
|
|
971
|
-
logger.log(`load: proxying \0-prefixed file ${id} -> re-export from ${importPath}`);
|
|
972
|
-
return `export { default } from ${JSON.stringify(importPath)};\nexport * from ${JSON.stringify(importPath)};`;
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
return null;
|
|
1157
|
+
return loadHook(state, id, loadOptions);
|
|
976
1158
|
},
|
|
977
|
-
async transform(code, id,
|
|
978
|
-
|
|
979
|
-
if (isVizeVirtual(id) || isMacro) {
|
|
980
|
-
const realPath = isMacro ? id.slice(1).replace("?macro=true", "") : fromVirtualId(id);
|
|
981
|
-
try {
|
|
982
|
-
const result = await transformWithOxc(code, realPath, { lang: "ts" });
|
|
983
|
-
const defines = options$1?.ssr ? serverViteDefine : clientViteDefine;
|
|
984
|
-
let transformed = result.code;
|
|
985
|
-
if (Object.keys(defines).length > 0) transformed = applyDefineReplacements(transformed, defines);
|
|
986
|
-
return {
|
|
987
|
-
code: transformed,
|
|
988
|
-
map: result.map
|
|
989
|
-
};
|
|
990
|
-
} catch (e) {
|
|
991
|
-
logger.error(`transformWithOxc failed for ${realPath}:`, e);
|
|
992
|
-
const dumpPath = `/tmp/vize-oxc-error-${path.basename(realPath)}.ts`;
|
|
993
|
-
fs.writeFileSync(dumpPath, code, "utf-8");
|
|
994
|
-
logger.error(`Dumped failing code to ${dumpPath}`);
|
|
995
|
-
return {
|
|
996
|
-
code: "export default {}",
|
|
997
|
-
map: null
|
|
998
|
-
};
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
return null;
|
|
1159
|
+
async transform(code, id, transformOptions) {
|
|
1160
|
+
return transformHook(state, code, id, transformOptions);
|
|
1002
1161
|
},
|
|
1003
1162
|
async handleHotUpdate(ctx) {
|
|
1004
|
-
|
|
1005
|
-
if (file.endsWith(".vue") && filter(file)) try {
|
|
1006
|
-
const source = await read();
|
|
1007
|
-
const prevCompiled = cache.get(file);
|
|
1008
|
-
compileFile(file, cache, {
|
|
1009
|
-
sourceMap: mergedOptions?.sourceMap ?? !isProduction,
|
|
1010
|
-
ssr: mergedOptions?.ssr ?? false
|
|
1011
|
-
}, source);
|
|
1012
|
-
const newCompiled = cache.get(file);
|
|
1013
|
-
const updateType = detectHmrUpdateType(prevCompiled, newCompiled);
|
|
1014
|
-
logger.log(`Re-compiled: ${path.relative(root, file)} (${updateType})`);
|
|
1015
|
-
const virtualId = toVirtualId(file);
|
|
1016
|
-
const modules = server$1.moduleGraph.getModulesByFile(virtualId) ?? server$1.moduleGraph.getModulesByFile(file);
|
|
1017
|
-
const hasDelegated = newCompiled.styles?.some((s) => s.lang !== null && [
|
|
1018
|
-
"scss",
|
|
1019
|
-
"sass",
|
|
1020
|
-
"less",
|
|
1021
|
-
"stylus",
|
|
1022
|
-
"styl"
|
|
1023
|
-
].includes(s.lang) || s.module !== false);
|
|
1024
|
-
if (hasDelegated && updateType === "style-only") {
|
|
1025
|
-
const affectedModules = new Set();
|
|
1026
|
-
for (const block of newCompiled.styles ?? []) {
|
|
1027
|
-
const params = new URLSearchParams();
|
|
1028
|
-
params.set("vue", "");
|
|
1029
|
-
params.set("type", "style");
|
|
1030
|
-
params.set("index", String(block.index));
|
|
1031
|
-
if (block.scoped) params.set("scoped", `data-v-${newCompiled.scopeId}`);
|
|
1032
|
-
params.set("lang", block.lang ?? "css");
|
|
1033
|
-
if (block.module !== false) params.set("module", typeof block.module === "string" ? block.module : "");
|
|
1034
|
-
const styleId = `${file}?${params.toString()}`;
|
|
1035
|
-
const styleMods = server$1.moduleGraph.getModulesByFile(styleId);
|
|
1036
|
-
if (styleMods) for (const mod of styleMods) affectedModules.add(mod);
|
|
1037
|
-
}
|
|
1038
|
-
if (modules) for (const mod of modules) affectedModules.add(mod);
|
|
1039
|
-
if (affectedModules.size > 0) return [...affectedModules];
|
|
1040
|
-
}
|
|
1041
|
-
if (updateType === "style-only" && newCompiled.css && !hasDelegated) {
|
|
1042
|
-
server$1.ws.send({
|
|
1043
|
-
type: "custom",
|
|
1044
|
-
event: "vize:update",
|
|
1045
|
-
data: {
|
|
1046
|
-
id: newCompiled.scopeId,
|
|
1047
|
-
type: "style-only",
|
|
1048
|
-
css: resolveCssImports(newCompiled.css, file, cssAliasRules, true, clientViteBase)
|
|
1049
|
-
}
|
|
1050
|
-
});
|
|
1051
|
-
return [];
|
|
1052
|
-
}
|
|
1053
|
-
if (modules) return [...modules];
|
|
1054
|
-
} catch (e) {
|
|
1055
|
-
logger.error(`Re-compilation failed for ${file}:`, e);
|
|
1056
|
-
}
|
|
1163
|
+
return handleHotUpdateHook(state, ctx);
|
|
1057
1164
|
},
|
|
1058
|
-
generateBundle(
|
|
1059
|
-
|
|
1060
|
-
const allCss = Array.from(collectedCss.values()).join("\n\n");
|
|
1061
|
-
if (allCss.trim()) {
|
|
1062
|
-
this.emitFile({
|
|
1063
|
-
type: "asset",
|
|
1064
|
-
fileName: "assets/vize-components.css",
|
|
1065
|
-
source: allCss
|
|
1066
|
-
});
|
|
1067
|
-
logger.log(`Extracted CSS to assets/vize-components.css (${collectedCss.size} components)`);
|
|
1068
|
-
}
|
|
1069
|
-
}
|
|
1070
|
-
};
|
|
1071
|
-
let compilerSfc = null;
|
|
1072
|
-
const loadCompilerSfc = () => {
|
|
1073
|
-
if (!compilerSfc) try {
|
|
1074
|
-
const require = createRequire(import.meta.url);
|
|
1075
|
-
compilerSfc = require("@vue/compiler-sfc");
|
|
1076
|
-
} catch {
|
|
1077
|
-
compilerSfc = { parse: () => ({
|
|
1078
|
-
descriptor: {},
|
|
1079
|
-
errors: []
|
|
1080
|
-
}) };
|
|
1081
|
-
}
|
|
1082
|
-
return compilerSfc;
|
|
1083
|
-
};
|
|
1084
|
-
const vueCompatPlugin = {
|
|
1085
|
-
name: "vite:vue",
|
|
1086
|
-
api: { get options() {
|
|
1087
|
-
return {
|
|
1088
|
-
compiler: loadCompilerSfc(),
|
|
1089
|
-
isProduction: isProduction ?? false,
|
|
1090
|
-
root: root ?? process.cwd(),
|
|
1091
|
-
template: {}
|
|
1092
|
-
};
|
|
1093
|
-
} }
|
|
1094
|
-
};
|
|
1095
|
-
const postTransformPlugin = {
|
|
1096
|
-
name: "vize:post-transform",
|
|
1097
|
-
enforce: "post",
|
|
1098
|
-
async transform(code, id, transformOptions) {
|
|
1099
|
-
if (!id.endsWith(".vue") && !id.endsWith(".vue.ts") && !id.includes("node_modules") && id.endsWith(".setup.ts") && /<script\s+setup[\s>]/.test(code)) {
|
|
1100
|
-
logger.log(`post-transform: compiling virtual SFC content from ${id}`);
|
|
1101
|
-
try {
|
|
1102
|
-
const compiled = compileFile(id, cache, {
|
|
1103
|
-
sourceMap: mergedOptions?.sourceMap ?? !(isProduction ?? false),
|
|
1104
|
-
ssr: mergedOptions?.ssr ?? false
|
|
1105
|
-
}, code);
|
|
1106
|
-
const output = generateOutput(compiled, {
|
|
1107
|
-
isProduction,
|
|
1108
|
-
isDev: server !== null,
|
|
1109
|
-
extractCss,
|
|
1110
|
-
filePath: id
|
|
1111
|
-
});
|
|
1112
|
-
const result = await transformWithOxc(output, id, { lang: "ts" });
|
|
1113
|
-
const defines = transformOptions?.ssr ? serverViteDefine : clientViteDefine;
|
|
1114
|
-
let transformed = result.code;
|
|
1115
|
-
if (Object.keys(defines).length > 0) transformed = applyDefineReplacements(transformed, defines);
|
|
1116
|
-
return {
|
|
1117
|
-
code: transformed,
|
|
1118
|
-
map: result.map
|
|
1119
|
-
};
|
|
1120
|
-
} catch (e) {
|
|
1121
|
-
logger.error(`Virtual SFC compilation failed for ${id}:`, e);
|
|
1122
|
-
}
|
|
1123
|
-
}
|
|
1124
|
-
return null;
|
|
1165
|
+
generateBundle() {
|
|
1166
|
+
handleGenerateBundleHook(state, this.emitFile.bind(this));
|
|
1125
1167
|
}
|
|
1126
1168
|
};
|
|
1127
1169
|
return [
|
|
1128
|
-
|
|
1170
|
+
createVueCompatPlugin(state),
|
|
1129
1171
|
mainPlugin,
|
|
1130
|
-
|
|
1172
|
+
createPostTransformPlugin(state)
|
|
1131
1173
|
];
|
|
1132
1174
|
}
|
|
1133
1175
|
|