@vizejs/vite-plugin 0.0.1-alpha.101 → 0.0.1-alpha.103

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 CHANGED
@@ -459,6 +459,14 @@ interface VizeOptions {
459
459
  * Custom config file path (overrides automatic search)
460
460
  */
461
461
  configFile?: string;
462
+ /**
463
+ * Handle .vue files in node_modules (on-demand compilation).
464
+ * When true, vize will compile .vue files from node_modules that other plugins
465
+ * (like vite-plugin-vue-inspector) may import directly.
466
+ * Set to false if another Vue plugin (e.g. Nuxt) handles node_modules .vue files.
467
+ * @default true
468
+ */
469
+ handleNodeModulesVue?: boolean;
462
470
  /**
463
471
  * Enable debug logging
464
472
  * @default false
@@ -492,7 +500,22 @@ declare function loadConfig(root: string, options?: LoadConfigOptions): Promise<
492
500
  * Used by musea() and other plugins to access the unified config.
493
501
  */
494
502
  declare const vizeConfigStore: Map<string, VizeConfig>;
495
- declare function vize(options?: VizeOptions): Plugin;
503
+ interface DynamicImportAliasRule {
504
+ fromPrefix: string;
505
+ toPrefix: string;
506
+ }
507
+ /**
508
+ * Rewrite static asset URLs in compiled template output.
509
+ *
510
+ * Transforms property values like `src: "@/assets/logo.svg"` into import
511
+ * statements hoisted to the top of the module, so Vite's module resolution
512
+ * pipeline handles alias expansion and asset hashing in both dev and build.
513
+ */
514
+ declare function rewriteStaticAssetUrls(code: string, aliasRules: DynamicImportAliasRule[]): string;
515
+ declare const __internal: {
516
+ rewriteStaticAssetUrls: typeof rewriteStaticAssetUrls;
517
+ };
518
+ declare function vize(options?: VizeOptions): Plugin[];
496
519
 
497
520
  //#endregion
498
- export { CompiledModule, LoadConfigOptions, VizeConfig, VizeOptions, vize as default, defineConfig, loadConfig, vize, vizeConfigStore };
521
+ export { CompiledModule, LoadConfigOptions, VizeConfig, VizeOptions, __internal, vize as default, defineConfig, loadConfig, vize, vizeConfigStore };
package/dist/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import { transformWithOxc } from "vite";
2
2
  import path from "node:path";
3
3
  import fs from "node:fs";
4
+ import { createRequire } from "node:module";
5
+ import { pathToFileURL } from "node:url";
4
6
  import { glob } from "tinyglobby";
5
7
  import * as native from "@vizejs/native";
6
8
  import { createHash } from "node:crypto";
@@ -116,6 +118,69 @@ ${output}`;
116
118
  if (!isProduction && isDev && hasExportDefault) output += generateHmrCode(compiled.scopeId, hmrUpdateType ?? "full-reload");
117
119
  return output;
118
120
  }
121
+ /**
122
+ * Resolve CSS @import statements by inlining the imported files,
123
+ * then resolve @custom-media definitions within the combined CSS.
124
+ *
125
+ * This is necessary because Vize embeds CSS as a JS string via
126
+ * document.createElement('style'), bypassing Vite's CSS pipeline.
127
+ */
128
+ function resolveCssImports(css, importer, aliasRules, isDev, devUrlBase) {
129
+ const customMedia = new Map();
130
+ const importRegex = /^@import\s+(?:"([^"]+)"|'([^']+)');?\s*$/gm;
131
+ let result = css;
132
+ result = result.replace(importRegex, (_match, dqPath, sqPath) => {
133
+ const importPath = dqPath || sqPath;
134
+ if (!importPath) return _match;
135
+ const resolved = resolveCssPath(importPath, importer, aliasRules);
136
+ if (!resolved || !fs.existsSync(resolved)) return _match;
137
+ try {
138
+ const content = fs.readFileSync(resolved, "utf-8");
139
+ parseCustomMedia(content, customMedia);
140
+ return content;
141
+ } catch {
142
+ return _match;
143
+ }
144
+ });
145
+ parseCustomMedia(result, customMedia);
146
+ result = result.replace(/^@custom-media\s+[^;]+;\s*$/gm, "");
147
+ if (customMedia.size > 0) for (const [name, query] of customMedia) {
148
+ const escaped = name.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
149
+ result = result.replace(new RegExp(`\\(${escaped}\\)`, "g"), query);
150
+ }
151
+ if (isDev) result = result.replace(/url\(\s*(["']?)([^"')]+)\1\s*\)/g, (_match, quote, urlPath) => {
152
+ const trimmed = urlPath.trim();
153
+ if (trimmed.startsWith("data:") || trimmed.startsWith("http://") || trimmed.startsWith("https://") || trimmed.startsWith("/@fs/")) return _match;
154
+ const resolved = resolveCssPath(trimmed, importer, aliasRules);
155
+ if (resolved && fs.existsSync(resolved)) {
156
+ const normalized = resolved.replace(/\\/g, "/");
157
+ const base = devUrlBase ?? "/";
158
+ const prefix = base.endsWith("/") ? base : base + "/";
159
+ return `url("${prefix}@fs${normalized}")`;
160
+ }
161
+ return _match;
162
+ });
163
+ result = result.replace(/:deep\(([^()]*(?:\([^()]*\))*[^()]*)\)/g, "$1");
164
+ result = result.replace(/\n{3,}/g, "\n\n");
165
+ return result;
166
+ }
167
+ function parseCustomMedia(css, map) {
168
+ const re = /@custom-media\s+(--[\w-]+)\s+(.+?)\s*;/g;
169
+ let m;
170
+ while ((m = re.exec(css)) !== null) map.set(m[1], m[2]);
171
+ }
172
+ function resolveCssPath(importPath, importer, aliasRules) {
173
+ for (const rule of aliasRules) if (importPath.startsWith(rule.find)) {
174
+ const resolved = importPath.replace(rule.find, rule.replacement);
175
+ return path.resolve(resolved);
176
+ }
177
+ if (importPath.startsWith(".")) {
178
+ const dir = path.dirname(importer);
179
+ return path.resolve(dir, importPath);
180
+ }
181
+ if (path.isAbsolute(importPath)) return importPath;
182
+ return null;
183
+ }
119
184
 
120
185
  //#endregion
121
186
  //#region src/compiler.ts
@@ -250,9 +315,69 @@ async function loadConfigFile(configPath, env) {
250
315
  * Used by musea() and other plugins to access the unified config.
251
316
  */
252
317
  const vizeConfigStore = new Map();
253
- const VIRTUAL_PREFIX = "\0vize:";
318
+ const LEGACY_VIZE_PREFIX = "\0vize:";
254
319
  const VIRTUAL_CSS_MODULE = "virtual:vize-styles";
255
320
  const RESOLVED_CSS_MODULE = "\0vize:all-styles.css";
321
+ /** Check if a module ID is a vize-compiled virtual module */
322
+ function isVizeVirtual(id) {
323
+ return id.startsWith("\0") && id.endsWith(".vue.ts");
324
+ }
325
+ /** Create a virtual module ID from a real .vue file path */
326
+ function toVirtualId(realPath) {
327
+ return "\0" + realPath + ".ts";
328
+ }
329
+ /** Extract the real .vue file path from a virtual module ID */
330
+ function fromVirtualId(virtualId) {
331
+ return virtualId.slice(1, -3);
332
+ }
333
+ function escapeRegExp(value) {
334
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
335
+ }
336
+ function toBrowserImportPrefix(replacement) {
337
+ const normalized = replacement.replace(/\\/g, "/");
338
+ if (normalized.startsWith("/@fs/")) return normalized;
339
+ if (path.isAbsolute(replacement) && fs.existsSync(replacement)) return `/@fs${normalized}`;
340
+ return normalized;
341
+ }
342
+ function normalizeFsIdForBuild(id) {
343
+ const [pathPart, queryPart] = id.split("?");
344
+ if (!pathPart.startsWith("/@fs/")) return id;
345
+ const normalizedPath = pathPart.slice(4);
346
+ return queryPart ? `${normalizedPath}?${queryPart}` : normalizedPath;
347
+ }
348
+ function rewriteDynamicTemplateImports(code, aliasRules) {
349
+ let rewritten = code;
350
+ for (const rule of aliasRules) {
351
+ const pattern = new RegExp(`\\bimport\\s*\\(\\s*\`${escapeRegExp(rule.fromPrefix)}`, "g");
352
+ rewritten = rewritten.replace(pattern, `import(/* @vite-ignore */ \`${rule.toPrefix}`);
353
+ }
354
+ rewritten = rewritten.replace(/\bimport\s*\(\s*`/g, "import(/* @vite-ignore */ `");
355
+ return rewritten;
356
+ }
357
+ /**
358
+ * Rewrite static asset URLs in compiled template output.
359
+ *
360
+ * Transforms property values like `src: "@/assets/logo.svg"` into import
361
+ * statements hoisted to the top of the module, so Vite's module resolution
362
+ * pipeline handles alias expansion and asset hashing in both dev and build.
363
+ */
364
+ function rewriteStaticAssetUrls(code, aliasRules) {
365
+ let rewritten = code;
366
+ const imports = [];
367
+ let counter = 0;
368
+ for (const rule of aliasRules) {
369
+ const pattern = new RegExp(`("?src"?\\s*:\\s*)(?:"(${escapeRegExp(rule.fromPrefix)}[^"]+)"|'(${escapeRegExp(rule.fromPrefix)}[^']+)')`, "g");
370
+ rewritten = rewritten.replace(pattern, (_match, prefix, dqPath, sqPath) => {
371
+ const fullPath = dqPath || sqPath;
372
+ const varName = `__vize_static_${counter++}`;
373
+ imports.push(`import ${varName} from ${JSON.stringify(fullPath)};`);
374
+ return `${prefix}${varName}`;
375
+ });
376
+ }
377
+ if (imports.length > 0) rewritten = imports.join("\n") + "\n" + rewritten;
378
+ return rewritten;
379
+ }
380
+ const __internal = { rewriteStaticAssetUrls };
256
381
  function createLogger(debug) {
257
382
  return {
258
383
  log: (...args) => debug && console.log("[vize]", ...args),
@@ -261,18 +386,61 @@ function createLogger(debug) {
261
386
  error: (...args) => console.error("[vize]", ...args)
262
387
  };
263
388
  }
389
+ /**
390
+ * Built-in Vite/Vue/Nuxt define keys that are handled by Vite's own transform pipeline.
391
+ * These must NOT be replaced by the vize plugin because:
392
+ * 1. Nuxt runs both client and server Vite builds, each with different values
393
+ * (e.g., import.meta.server = true on server, false on client).
394
+ * 2. Vite's import.meta transform already handles these correctly per-environment.
395
+ */
396
+ const BUILTIN_DEFINE_PREFIXES = [
397
+ "import.meta.server",
398
+ "import.meta.client",
399
+ "import.meta.dev",
400
+ "import.meta.test",
401
+ "import.meta.prerender",
402
+ "import.meta.env",
403
+ "import.meta.hot",
404
+ "__VUE_",
405
+ "__NUXT_",
406
+ "process.env"
407
+ ];
408
+ function isBuiltinDefine(key) {
409
+ return BUILTIN_DEFINE_PREFIXES.some((prefix) => key === prefix || key.startsWith(prefix + ".") || key.startsWith(prefix + "_"));
410
+ }
411
+ /**
412
+ * Apply Vite define replacements to code.
413
+ * Replaces keys like `import.meta.vfFeatures.photoSection` with their values.
414
+ * Uses word-boundary-aware matching to avoid replacing inside strings or partial matches.
415
+ */
416
+ function applyDefineReplacements(code, defines) {
417
+ const sortedKeys = Object.keys(defines).sort((a, b) => b.length - a.length);
418
+ let result = code;
419
+ for (const key of sortedKeys) {
420
+ if (!result.includes(key)) continue;
421
+ const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
422
+ const re = new RegExp(escaped + "(?![\\w$.])", "g");
423
+ result = result.replace(re, defines[key]);
424
+ }
425
+ return result;
426
+ }
264
427
  function vize(options = {}) {
265
428
  const cache = new Map();
266
- const virtualToReal = new Map();
267
429
  const collectedCss = new Map();
268
430
  let isProduction;
269
431
  let root;
432
+ let clientViteBase = "/";
433
+ let serverViteBase = "/";
270
434
  let server = null;
271
435
  let filter;
272
436
  let scanPatterns;
273
437
  let ignorePatterns;
274
438
  let mergedOptions;
439
+ let dynamicImportAliasRules = [];
440
+ let cssAliasRules = [];
275
441
  let extractCss = false;
442
+ let clientViteDefine = {};
443
+ let serverViteDefine = {};
276
444
  const logger = createLogger(options.debug ?? false);
277
445
  async function compileAll() {
278
446
  const startTime = performance.now();
@@ -294,7 +462,7 @@ function vize(options = {}) {
294
462
  }
295
463
  const result = compileBatch(fileContents, cache, { ssr: mergedOptions.ssr ?? false });
296
464
  if (isProduction) {
297
- for (const fileResult of result.results) if (fileResult.css) collectedCss.set(fileResult.path, fileResult.css);
465
+ for (const fileResult of result.results) if (fileResult.css) collectedCss.set(fileResult.path, resolveCssImports(fileResult.css, fileResult.path, cssAliasRules, false));
298
466
  }
299
467
  const elapsed = (performance.now() - startTime).toFixed(2);
300
468
  logger.info(`Pre-compilation complete: ${result.successCount} succeeded, ${result.failedCount} failed (${elapsed}ms, native batch: ${result.timeMs.toFixed(2)}ms)`);
@@ -305,36 +473,45 @@ function vize(options = {}) {
305
473
  else if (id.startsWith("/") && !fs.existsSync(id)) resolved = path.resolve(root, id.slice(1));
306
474
  else if (path.isAbsolute(id)) resolved = id;
307
475
  else if (importer) {
308
- let realImporter = importer.startsWith(VIRTUAL_PREFIX) ? virtualToReal.get(importer) ?? importer.slice(VIRTUAL_PREFIX.length) : importer;
309
- if (realImporter.endsWith(".vue.ts")) realImporter = realImporter.slice(0, -3);
476
+ const realImporter = isVizeVirtual(importer) ? fromVirtualId(importer) : importer;
310
477
  resolved = path.resolve(path.dirname(realImporter), id);
311
478
  } else resolved = path.resolve(root, id);
312
479
  if (!path.isAbsolute(resolved)) resolved = path.resolve(root, resolved);
313
480
  return path.normalize(resolved);
314
481
  }
315
- return {
482
+ const mainPlugin = {
316
483
  name: "vite-plugin-vize",
317
484
  enforce: "pre",
318
- config() {
319
- return { optimizeDeps: {
320
- include: ["vue"],
321
- exclude: ["virtual:vize-styles"],
322
- esbuildOptions: { plugins: [{
323
- name: "vize-externalize-vue",
324
- setup(build) {
325
- build.onResolve({ filter: /\.vue$/ }, (args) => ({
326
- path: args.path,
327
- external: true
328
- }));
329
- }
330
- }] },
331
- rolldownOptions: { external: [/\.vue$/] }
332
- } };
485
+ config(_, env) {
486
+ return {
487
+ define: {
488
+ __VUE_OPTIONS_API__: true,
489
+ __VUE_PROD_DEVTOOLS__: env.command === "serve",
490
+ __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false
491
+ },
492
+ optimizeDeps: {
493
+ include: ["vue"],
494
+ exclude: ["virtual:vize-styles"],
495
+ rolldownOptions: { external: [/\.vue$/] }
496
+ }
497
+ };
333
498
  },
334
499
  async configResolved(resolvedConfig) {
335
500
  root = options.root ?? resolvedConfig.root;
336
501
  isProduction = options.isProduction ?? resolvedConfig.isProduction;
502
+ const isSsrBuild = !!resolvedConfig.build?.ssr;
503
+ if (isSsrBuild) serverViteBase = resolvedConfig.base ?? "/";
504
+ else clientViteBase = resolvedConfig.base ?? "/";
337
505
  extractCss = isProduction;
506
+ const isSsr = !!resolvedConfig.build?.ssr;
507
+ const envDefine = {};
508
+ if (resolvedConfig.define) for (const [key, value] of Object.entries(resolvedConfig.define)) {
509
+ if (isBuiltinDefine(key)) continue;
510
+ if (typeof value === "string") envDefine[key] = value;
511
+ else envDefine[key] = JSON.stringify(value);
512
+ }
513
+ if (isSsr) serverViteDefine = envDefine;
514
+ else clientViteDefine = envDefine;
338
515
  const configEnv = {
339
516
  mode: resolvedConfig.mode,
340
517
  command: resolvedConfig.command === "build" ? "build" : "serve",
@@ -364,6 +541,27 @@ function vize(options = {}) {
364
541
  scanPatterns: options.scanPatterns ?? viteConfig.scanPatterns,
365
542
  ignorePatterns: options.ignorePatterns ?? viteConfig.ignorePatterns
366
543
  };
544
+ dynamicImportAliasRules = [];
545
+ for (const alias of resolvedConfig.resolve.alias) {
546
+ if (typeof alias.find !== "string" || typeof alias.replacement !== "string") continue;
547
+ const fromPrefix = alias.find.endsWith("/") ? alias.find : `${alias.find}/`;
548
+ const replacement = toBrowserImportPrefix(alias.replacement);
549
+ const toPrefix = replacement.endsWith("/") ? replacement : `${replacement}/`;
550
+ dynamicImportAliasRules.push({
551
+ fromPrefix,
552
+ toPrefix
553
+ });
554
+ }
555
+ dynamicImportAliasRules.sort((a, b) => b.fromPrefix.length - a.fromPrefix.length);
556
+ cssAliasRules = [];
557
+ for (const alias of resolvedConfig.resolve.alias) {
558
+ if (typeof alias.find !== "string" || typeof alias.replacement !== "string") continue;
559
+ cssAliasRules.push({
560
+ find: alias.find,
561
+ replacement: alias.replacement
562
+ });
563
+ }
564
+ cssAliasRules.sort((a, b) => b.find.length - a.find.length);
367
565
  filter = createFilter(mergedOptions.include, mergedOptions.exclude);
368
566
  scanPatterns = mergedOptions.scanPatterns ?? ["**/*.vue"];
369
567
  ignorePatterns = mergedOptions.ignorePatterns ?? [
@@ -374,120 +572,235 @@ function vize(options = {}) {
374
572
  },
375
573
  configureServer(devServer) {
376
574
  server = devServer;
575
+ devServer.middlewares.use((req, _res, next) => {
576
+ if (req.url && req.url.includes("__x00__")) {
577
+ const [urlPath, queryPart] = req.url.split("?");
578
+ let cleanedPath = urlPath.replace(/__x00__/g, "");
579
+ cleanedPath = cleanedPath.replace(/^\/@id\/\//, "/@fs/");
580
+ if (cleanedPath.startsWith("/@fs/")) {
581
+ const fsPath = cleanedPath.slice(4);
582
+ if (fsPath.startsWith("/") && fs.existsSync(fsPath) && fs.statSync(fsPath).isFile() && !fsPath.endsWith(".vue.ts")) {
583
+ const cleaned = queryPart ? `${cleanedPath}?${queryPart}` : cleanedPath;
584
+ if (cleaned !== req.url) {
585
+ logger.log(`middleware: rewriting ${req.url} → ${cleaned}`);
586
+ req.url = cleaned;
587
+ }
588
+ }
589
+ }
590
+ }
591
+ next();
592
+ });
377
593
  },
378
594
  async buildStart() {
379
595
  await compileAll();
380
596
  logger.log("Cache keys:", [...cache.keys()].slice(0, 3));
381
597
  },
382
598
  async resolveId(id, importer) {
383
- if (id.startsWith("\0")) return null;
599
+ const isBuild = server === null;
600
+ if (id.startsWith("\0")) {
601
+ if (isVizeVirtual(id)) return null;
602
+ if (id.startsWith(LEGACY_VIZE_PREFIX)) {
603
+ const rawPath = id.slice(LEGACY_VIZE_PREFIX.length);
604
+ const cleanPath$1 = rawPath.endsWith(".ts") ? rawPath.slice(0, -3) : rawPath;
605
+ if (!cleanPath$1.endsWith(".vue")) {
606
+ logger.log(`resolveId: redirecting legacy virtual ID to ${cleanPath$1}`);
607
+ return cleanPath$1;
608
+ }
609
+ }
610
+ const cleanPath = id.slice(1);
611
+ if (cleanPath.startsWith("/") && !cleanPath.endsWith(".vue.ts")) {
612
+ const [pathPart, queryPart] = cleanPath.split("?");
613
+ const querySuffix = queryPart ? `?${queryPart}` : "";
614
+ logger.log(`resolveId: redirecting \0-prefixed non-vue ID to ${pathPart}${querySuffix}`);
615
+ const redirected = pathPart + querySuffix;
616
+ return isBuild ? normalizeFsIdForBuild(redirected) : redirected;
617
+ }
618
+ return null;
619
+ }
384
620
  if (id.startsWith("vize:")) {
385
621
  let realPath = id.slice(5);
386
622
  if (realPath.endsWith(".ts")) realPath = realPath.slice(0, -3);
387
623
  logger.log(`resolveId: redirecting stale vize: ID to ${realPath}`);
388
- if (realPath.includes("node_modules")) return this.resolve(realPath, importer, { skipSelf: true });
389
- return this.resolve(realPath, importer, { skipSelf: true });
624
+ const resolved = await this.resolve(realPath, importer, { skipSelf: true });
625
+ if (resolved && isBuild && resolved.id.startsWith("/@fs/")) return {
626
+ ...resolved,
627
+ id: normalizeFsIdForBuild(resolved.id)
628
+ };
629
+ return resolved;
390
630
  }
391
631
  if (id === VIRTUAL_CSS_MODULE) return RESOLVED_CSS_MODULE;
632
+ if (isBuild && id.startsWith("/@fs/")) return normalizeFsIdForBuild(id);
392
633
  if (id.includes("?vue&type=style")) return id;
393
- if (importer?.startsWith(VIRTUAL_PREFIX)) {
394
- const realImporter = virtualToReal.get(importer) ?? importer.slice(VIRTUAL_PREFIX.length);
395
- const cleanImporter = realImporter.endsWith(".ts") ? realImporter.slice(0, -3) : realImporter;
634
+ if (importer && isVizeVirtual(importer)) {
635
+ const cleanImporter = fromVirtualId(importer);
396
636
  logger.log(`resolveId from virtual: id=${id}, cleanImporter=${cleanImporter}`);
397
637
  if (id.startsWith("#")) try {
398
638
  return await this.resolve(id, cleanImporter, { skipSelf: true });
399
639
  } catch {
400
640
  return null;
401
641
  }
402
- if (!id.endsWith(".vue")) if (id.startsWith("./") || id.startsWith("../")) {
403
- const [pathPart, queryPart] = id.split("?");
404
- const querySuffix = queryPart ? `?${queryPart}` : "";
405
- const resolved = path.resolve(path.dirname(cleanImporter), pathPart);
406
- for (const ext of [
407
- "",
408
- ".ts",
409
- ".tsx",
410
- ".js",
411
- ".jsx",
412
- ".json"
413
- ]) if (fs.existsSync(resolved + ext)) {
414
- const finalPath = resolved + ext + querySuffix;
415
- logger.log(`resolveId: resolved relative ${id} to ${finalPath}`);
416
- return finalPath;
417
- }
418
- } else {
419
- if (id.includes("/dist/") || id.includes("/lib/") || id.includes("/es/")) {
420
- logger.log(`resolveId: skipping already-resolved path ${id}`);
421
- return null;
642
+ if (!id.endsWith(".vue")) {
643
+ if (id.includes("/dist/") || id.includes("/lib/") || id.includes("/es/")) return null;
644
+ try {
645
+ const resolved = await this.resolve(id, cleanImporter, { skipSelf: true });
646
+ if (resolved) {
647
+ logger.log(`resolveId: resolved ${id} to ${resolved.id} via Vite resolver`);
648
+ if (isBuild && resolved.id.startsWith("/@fs/")) return {
649
+ ...resolved,
650
+ id: normalizeFsIdForBuild(resolved.id)
651
+ };
652
+ return resolved;
653
+ }
654
+ } catch {}
655
+ if (id.startsWith("./") || id.startsWith("../")) {
656
+ const [pathPart, queryPart] = id.split("?");
657
+ const querySuffix = queryPart ? `?${queryPart}` : "";
658
+ const resolved = path.resolve(path.dirname(cleanImporter), pathPart);
659
+ for (const ext of [
660
+ "",
661
+ ".ts",
662
+ ".tsx",
663
+ ".js",
664
+ ".jsx",
665
+ ".json"
666
+ ]) {
667
+ const candidate = resolved + ext;
668
+ if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
669
+ const finalPath = candidate + querySuffix;
670
+ logger.log(`resolveId: resolved relative ${id} to ${finalPath}`);
671
+ return finalPath;
672
+ }
673
+ }
674
+ if (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory()) for (const indexFile of [
675
+ "/index.ts",
676
+ "/index.tsx",
677
+ "/index.js",
678
+ "/index.jsx"
679
+ ]) {
680
+ const candidate = resolved + indexFile;
681
+ if (fs.existsSync(candidate)) {
682
+ const finalPath = candidate + querySuffix;
683
+ logger.log(`resolveId: resolved directory ${id} to ${finalPath}`);
684
+ return finalPath;
685
+ }
686
+ }
422
687
  }
423
- logger.log(`resolveId: resolving external ${id} from ${cleanImporter}`);
424
- const resolved = await this.resolve(id, cleanImporter, { skipSelf: true });
425
- logger.log(`resolveId: resolved external ${id} to`, resolved?.id ?? "null");
426
- return resolved;
688
+ return null;
427
689
  }
428
690
  }
429
691
  if (id.endsWith(".vue")) {
430
- if (id.includes("node_modules")) {
692
+ const handleNodeModules = mergedOptions.handleNodeModulesVue ?? true;
693
+ if (!handleNodeModules && id.includes("node_modules")) {
431
694
  logger.log(`resolveId: skipping node_modules import ${id}`);
432
695
  return null;
433
696
  }
434
697
  const resolved = resolveVuePath(id, importer);
435
- if (resolved.includes("node_modules")) {
698
+ const isNodeModulesPath = resolved.includes("node_modules");
699
+ if (!handleNodeModules && isNodeModulesPath) {
436
700
  logger.log(`resolveId: skipping node_modules path ${resolved}`);
437
701
  return null;
438
702
  }
439
- if (!filter(resolved)) {
703
+ if (!isNodeModulesPath && !filter(resolved)) {
440
704
  logger.log(`resolveId: skipping filtered path ${resolved}`);
441
705
  return null;
442
706
  }
443
707
  const hasCache = cache.has(resolved);
444
708
  const fileExists = fs.existsSync(resolved);
445
709
  logger.log(`resolveId: id=${id}, resolved=${resolved}, hasCache=${hasCache}, fileExists=${fileExists}, importer=${importer ?? "none"}`);
446
- if (hasCache || fileExists) {
447
- const virtualId = VIRTUAL_PREFIX + resolved + ".ts";
448
- virtualToReal.set(virtualId, resolved);
449
- return virtualId;
710
+ if (hasCache || fileExists) return toVirtualId(resolved);
711
+ if (!fileExists && !path.isAbsolute(id)) {
712
+ const viteResolved = await this.resolve(id, importer, { skipSelf: true });
713
+ if (viteResolved && viteResolved.id.endsWith(".vue")) {
714
+ const realPath = viteResolved.id;
715
+ const isResolvedNodeModules = realPath.includes("node_modules");
716
+ if ((isResolvedNodeModules ? handleNodeModules : filter(realPath)) && (cache.has(realPath) || fs.existsSync(realPath))) {
717
+ logger.log(`resolveId: resolved via Vite fallback ${id} to ${realPath}`);
718
+ return toVirtualId(realPath);
719
+ }
720
+ }
450
721
  }
451
722
  }
452
723
  return null;
453
724
  },
454
- load(id) {
725
+ load(id, loadOptions) {
726
+ const currentBase = loadOptions?.ssr ? serverViteBase : clientViteBase;
455
727
  if (id === RESOLVED_CSS_MODULE) {
456
728
  const allCss = Array.from(collectedCss.values()).join("\n\n");
457
729
  return allCss;
458
730
  }
459
731
  if (id.includes("?vue&type=style")) {
460
732
  const [filename] = id.split("?");
461
- const realPath = filename.startsWith(VIRTUAL_PREFIX) ? virtualToReal.get(filename) ?? filename.slice(VIRTUAL_PREFIX.length) : filename;
733
+ const realPath = isVizeVirtual(filename) ? fromVirtualId(filename) : filename;
462
734
  const compiled = cache.get(realPath);
463
- if (compiled?.css) return compiled.css;
735
+ if (compiled?.css) return resolveCssImports(compiled.css, realPath, cssAliasRules, server !== null, currentBase);
464
736
  return "";
465
737
  }
466
- if (id.startsWith(VIRTUAL_PREFIX)) {
467
- const lookupId = id.endsWith(".ts") ? id.slice(0, -3) : id;
468
- const realPath = virtualToReal.get(id) ?? lookupId.slice(VIRTUAL_PREFIX.length);
469
- const compiled = cache.get(realPath);
738
+ if (isVizeVirtual(id)) {
739
+ const realPath = fromVirtualId(id);
740
+ if (!realPath.endsWith(".vue")) {
741
+ logger.log(`load: skipping non-vue virtual module ${realPath}`);
742
+ return null;
743
+ }
744
+ let compiled = cache.get(realPath);
745
+ if (!compiled && fs.existsSync(realPath)) {
746
+ logger.log(`load: on-demand compiling ${realPath}`);
747
+ compiled = compileFile(realPath, cache, {
748
+ sourceMap: mergedOptions.sourceMap ?? !isProduction,
749
+ ssr: mergedOptions.ssr ?? false
750
+ });
751
+ }
470
752
  if (compiled) {
471
- const output = generateOutput(compiled, {
753
+ if (compiled.css) compiled = {
754
+ ...compiled,
755
+ css: resolveCssImports(compiled.css, realPath, cssAliasRules, server !== null, currentBase)
756
+ };
757
+ const output = rewriteStaticAssetUrls(rewriteDynamicTemplateImports(generateOutput(compiled, {
472
758
  isProduction,
473
759
  isDev: server !== null,
474
760
  extractCss
475
- });
761
+ }), dynamicImportAliasRules), dynamicImportAliasRules);
476
762
  return {
477
763
  code: output,
478
764
  map: null
479
765
  };
480
766
  }
481
767
  }
768
+ if (id.startsWith("\0")) {
769
+ const afterPrefix = id.startsWith(LEGACY_VIZE_PREFIX) ? id.slice(LEGACY_VIZE_PREFIX.length) : id.slice(1);
770
+ if (afterPrefix.includes("?commonjs-")) return null;
771
+ const [pathPart, queryPart] = afterPrefix.split("?");
772
+ const querySuffix = queryPart ? `?${queryPart}` : "";
773
+ const fsPath = pathPart.startsWith("/@fs/") ? pathPart.slice(4) : pathPart;
774
+ if (fsPath.startsWith("/") && fs.existsSync(fsPath) && fs.statSync(fsPath).isFile()) {
775
+ const importPath = server === null ? `${pathToFileURL(fsPath).href}${querySuffix}` : "/@fs" + fsPath + querySuffix;
776
+ logger.log(`load: proxying \0-prefixed file ${id} → re-export from ${importPath}`);
777
+ return `export { default } from ${JSON.stringify(importPath)};\nexport * from ${JSON.stringify(importPath)};`;
778
+ }
779
+ }
482
780
  return null;
483
781
  },
484
- async transform(code, id) {
485
- if (id.startsWith(VIRTUAL_PREFIX) && id.endsWith(".ts")) {
486
- const result = await transformWithOxc(code, id.slice(VIRTUAL_PREFIX.length), { lang: "ts" });
487
- return {
488
- code: result.code,
489
- map: result.map
490
- };
782
+ async transform(code, id, options$1) {
783
+ if (isVizeVirtual(id)) {
784
+ const realPath = fromVirtualId(id);
785
+ try {
786
+ const result = await transformWithOxc(code, realPath, { lang: "ts" });
787
+ const defines = options$1?.ssr ? serverViteDefine : clientViteDefine;
788
+ let transformed = result.code;
789
+ if (Object.keys(defines).length > 0) transformed = applyDefineReplacements(transformed, defines);
790
+ return {
791
+ code: transformed,
792
+ map: result.map
793
+ };
794
+ } catch (e) {
795
+ logger.error(`transformWithOxc failed for ${realPath}:`, e);
796
+ const dumpPath = `/tmp/vize-oxc-error-${path.basename(realPath)}.ts`;
797
+ fs.writeFileSync(dumpPath, code, "utf-8");
798
+ logger.error(`Dumped failing code to ${dumpPath}`);
799
+ return {
800
+ code: "export default {}",
801
+ map: null
802
+ };
803
+ }
491
804
  }
492
805
  return null;
493
806
  },
@@ -503,7 +816,7 @@ function vize(options = {}) {
503
816
  const newCompiled = cache.get(file);
504
817
  const updateType = detectHmrUpdateType(prevCompiled, newCompiled);
505
818
  logger.log(`Re-compiled: ${path.relative(root, file)} (${updateType})`);
506
- const virtualId = VIRTUAL_PREFIX + file + ".ts";
819
+ const virtualId = toVirtualId(file);
507
820
  const modules = server$1.moduleGraph.getModulesByFile(virtualId) ?? server$1.moduleGraph.getModulesByFile(file);
508
821
  if (updateType === "style-only" && newCompiled.css) {
509
822
  server$1.ws.send({
@@ -512,7 +825,7 @@ function vize(options = {}) {
512
825
  data: {
513
826
  id: newCompiled.scopeId,
514
827
  type: "style-only",
515
- css: newCompiled.css
828
+ css: resolveCssImports(newCompiled.css, file, cssAliasRules, true, clientViteBase)
516
829
  }
517
830
  });
518
831
  return [];
@@ -535,8 +848,33 @@ function vize(options = {}) {
535
848
  }
536
849
  }
537
850
  };
851
+ let compilerSfc = null;
852
+ const loadCompilerSfc = () => {
853
+ if (!compilerSfc) try {
854
+ const require = createRequire(import.meta.url);
855
+ compilerSfc = require("@vue/compiler-sfc");
856
+ } catch {
857
+ compilerSfc = { parse: () => ({
858
+ descriptor: {},
859
+ errors: []
860
+ }) };
861
+ }
862
+ return compilerSfc;
863
+ };
864
+ const vueCompatPlugin = {
865
+ name: "vite:vue",
866
+ api: { get options() {
867
+ return {
868
+ compiler: loadCompilerSfc(),
869
+ isProduction: isProduction ?? false,
870
+ root: root ?? process.cwd(),
871
+ template: {}
872
+ };
873
+ } }
874
+ };
875
+ return [vueCompatPlugin, mainPlugin];
538
876
  }
539
877
  var src_default = vize;
540
878
 
541
879
  //#endregion
542
- export { src_default as default, defineConfig, loadConfig, vize, vizeConfigStore };
880
+ export { __internal, src_default as default, defineConfig, loadConfig, vize, vizeConfigStore };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vizejs/vite-plugin",
3
- "version": "0.0.1-alpha.101",
3
+ "version": "0.0.1-alpha.103",
4
4
  "description": "High-performance native Vite plugin for Vue SFC compilation powered by Vize",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -42,8 +42,8 @@
42
42
  "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-beta.0"
43
43
  },
44
44
  "dependencies": {
45
- "tinyglobby": "^0.2.0",
46
- "@vizejs/native": "0.0.1-alpha.101"
45
+ "@vizejs/native": "file:../vize-native",
46
+ "tinyglobby": "^0.2.0"
47
47
  },
48
48
  "scripts": {
49
49
  "build": "tsdown",