@vizejs/vite-plugin 0.0.1-alpha.98 → 0.1.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 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,62 @@ 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 initialized = false;
440
+ let dynamicImportAliasRules = [];
441
+ let cssAliasRules = [];
275
442
  let extractCss = false;
443
+ let clientViteDefine = {};
444
+ let serverViteDefine = {};
276
445
  const logger = createLogger(options.debug ?? false);
277
446
  async function compileAll() {
278
447
  const startTime = performance.now();
@@ -294,7 +463,7 @@ function vize(options = {}) {
294
463
  }
295
464
  const result = compileBatch(fileContents, cache, { ssr: mergedOptions.ssr ?? false });
296
465
  if (isProduction) {
297
- for (const fileResult of result.results) if (fileResult.css) collectedCss.set(fileResult.path, fileResult.css);
466
+ for (const fileResult of result.results) if (fileResult.css) collectedCss.set(fileResult.path, resolveCssImports(fileResult.css, fileResult.path, cssAliasRules, false));
298
467
  }
299
468
  const elapsed = (performance.now() - startTime).toFixed(2);
300
469
  logger.info(`Pre-compilation complete: ${result.successCount} succeeded, ${result.failedCount} failed (${elapsed}ms, native batch: ${result.timeMs.toFixed(2)}ms)`);
@@ -305,36 +474,45 @@ function vize(options = {}) {
305
474
  else if (id.startsWith("/") && !fs.existsSync(id)) resolved = path.resolve(root, id.slice(1));
306
475
  else if (path.isAbsolute(id)) resolved = id;
307
476
  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);
477
+ const realImporter = isVizeVirtual(importer) ? fromVirtualId(importer) : importer;
310
478
  resolved = path.resolve(path.dirname(realImporter), id);
311
479
  } else resolved = path.resolve(root, id);
312
480
  if (!path.isAbsolute(resolved)) resolved = path.resolve(root, resolved);
313
481
  return path.normalize(resolved);
314
482
  }
315
- return {
483
+ const mainPlugin = {
316
484
  name: "vite-plugin-vize",
317
485
  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
- } };
486
+ config(_, env) {
487
+ return {
488
+ define: {
489
+ __VUE_OPTIONS_API__: true,
490
+ __VUE_PROD_DEVTOOLS__: env.command === "serve",
491
+ __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false
492
+ },
493
+ optimizeDeps: {
494
+ include: ["vue"],
495
+ exclude: ["virtual:vize-styles"],
496
+ rolldownOptions: { external: [/\.vue$/] }
497
+ }
498
+ };
333
499
  },
334
500
  async configResolved(resolvedConfig) {
335
501
  root = options.root ?? resolvedConfig.root;
336
502
  isProduction = options.isProduction ?? resolvedConfig.isProduction;
503
+ const isSsrBuild = !!resolvedConfig.build?.ssr;
504
+ if (isSsrBuild) serverViteBase = resolvedConfig.base ?? "/";
505
+ else clientViteBase = resolvedConfig.base ?? "/";
337
506
  extractCss = isProduction;
507
+ const isSsr = !!resolvedConfig.build?.ssr;
508
+ const envDefine = {};
509
+ if (resolvedConfig.define) for (const [key, value] of Object.entries(resolvedConfig.define)) {
510
+ if (isBuiltinDefine(key)) continue;
511
+ if (typeof value === "string") envDefine[key] = value;
512
+ else envDefine[key] = JSON.stringify(value);
513
+ }
514
+ if (isSsr) serverViteDefine = envDefine;
515
+ else clientViteDefine = envDefine;
338
516
  const configEnv = {
339
517
  mode: resolvedConfig.mode,
340
518
  command: resolvedConfig.command === "build" ? "build" : "serve",
@@ -364,6 +542,27 @@ function vize(options = {}) {
364
542
  scanPatterns: options.scanPatterns ?? viteConfig.scanPatterns,
365
543
  ignorePatterns: options.ignorePatterns ?? viteConfig.ignorePatterns
366
544
  };
545
+ dynamicImportAliasRules = [];
546
+ for (const alias of resolvedConfig.resolve.alias) {
547
+ if (typeof alias.find !== "string" || typeof alias.replacement !== "string") continue;
548
+ const fromPrefix = alias.find.endsWith("/") ? alias.find : `${alias.find}/`;
549
+ const replacement = toBrowserImportPrefix(alias.replacement);
550
+ const toPrefix = replacement.endsWith("/") ? replacement : `${replacement}/`;
551
+ dynamicImportAliasRules.push({
552
+ fromPrefix,
553
+ toPrefix
554
+ });
555
+ }
556
+ dynamicImportAliasRules.sort((a, b) => b.fromPrefix.length - a.fromPrefix.length);
557
+ cssAliasRules = [];
558
+ for (const alias of resolvedConfig.resolve.alias) {
559
+ if (typeof alias.find !== "string" || typeof alias.replacement !== "string") continue;
560
+ cssAliasRules.push({
561
+ find: alias.find,
562
+ replacement: alias.replacement
563
+ });
564
+ }
565
+ cssAliasRules.sort((a, b) => b.find.length - a.find.length);
367
566
  filter = createFilter(mergedOptions.include, mergedOptions.exclude);
368
567
  scanPatterns = mergedOptions.scanPatterns ?? ["**/*.vue"];
369
568
  ignorePatterns = mergedOptions.ignorePatterns ?? [
@@ -371,118 +570,240 @@ function vize(options = {}) {
371
570
  "dist/**",
372
571
  ".git/**"
373
572
  ];
573
+ initialized = true;
374
574
  },
375
575
  configureServer(devServer) {
376
576
  server = devServer;
577
+ devServer.middlewares.use((req, _res, next) => {
578
+ if (req.url && req.url.includes("__x00__")) {
579
+ const [urlPath, queryPart] = req.url.split("?");
580
+ let cleanedPath = urlPath.replace(/__x00__/g, "");
581
+ cleanedPath = cleanedPath.replace(/^\/@id\/\//, "/@fs/");
582
+ if (cleanedPath.startsWith("/@fs/")) {
583
+ const fsPath = cleanedPath.slice(4);
584
+ if (fsPath.startsWith("/") && fs.existsSync(fsPath) && fs.statSync(fsPath).isFile() && !fsPath.endsWith(".vue.ts")) {
585
+ const cleaned = queryPart ? `${cleanedPath}?${queryPart}` : cleanedPath;
586
+ if (cleaned !== req.url) {
587
+ logger.log(`middleware: rewriting ${req.url} → ${cleaned}`);
588
+ req.url = cleaned;
589
+ }
590
+ }
591
+ }
592
+ }
593
+ next();
594
+ });
377
595
  },
378
596
  async buildStart() {
597
+ if (!scanPatterns) return;
379
598
  await compileAll();
380
599
  logger.log("Cache keys:", [...cache.keys()].slice(0, 3));
381
600
  },
382
601
  async resolveId(id, importer) {
383
- if (id.startsWith("\0")) return null;
602
+ const isBuild = server === null;
603
+ if (id.startsWith("\0")) {
604
+ if (isVizeVirtual(id)) return null;
605
+ if (id.startsWith(LEGACY_VIZE_PREFIX)) {
606
+ const rawPath = id.slice(LEGACY_VIZE_PREFIX.length);
607
+ const cleanPath$1 = rawPath.endsWith(".ts") ? rawPath.slice(0, -3) : rawPath;
608
+ if (!cleanPath$1.endsWith(".vue")) {
609
+ logger.log(`resolveId: redirecting legacy virtual ID to ${cleanPath$1}`);
610
+ return cleanPath$1;
611
+ }
612
+ }
613
+ const cleanPath = id.slice(1);
614
+ if (cleanPath.startsWith("/") && !cleanPath.endsWith(".vue.ts")) {
615
+ const [pathPart, queryPart] = cleanPath.split("?");
616
+ const querySuffix = queryPart ? `?${queryPart}` : "";
617
+ logger.log(`resolveId: redirecting \0-prefixed non-vue ID to ${pathPart}${querySuffix}`);
618
+ const redirected = pathPart + querySuffix;
619
+ return isBuild ? normalizeFsIdForBuild(redirected) : redirected;
620
+ }
621
+ return null;
622
+ }
384
623
  if (id.startsWith("vize:")) {
385
624
  let realPath = id.slice(5);
386
625
  if (realPath.endsWith(".ts")) realPath = realPath.slice(0, -3);
387
626
  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 });
627
+ const resolved = await this.resolve(realPath, importer, { skipSelf: true });
628
+ if (resolved && isBuild && resolved.id.startsWith("/@fs/")) return {
629
+ ...resolved,
630
+ id: normalizeFsIdForBuild(resolved.id)
631
+ };
632
+ return resolved;
390
633
  }
391
634
  if (id === VIRTUAL_CSS_MODULE) return RESOLVED_CSS_MODULE;
635
+ if (isBuild && id.startsWith("/@fs/")) return normalizeFsIdForBuild(id);
392
636
  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;
637
+ if (importer && isVizeVirtual(importer)) {
638
+ const cleanImporter = fromVirtualId(importer);
396
639
  logger.log(`resolveId from virtual: id=${id}, cleanImporter=${cleanImporter}`);
397
- if (!id.endsWith(".vue")) if (id.startsWith("./") || id.startsWith("../")) {
398
- const [pathPart, queryPart] = id.split("?");
399
- const querySuffix = queryPart ? `?${queryPart}` : "";
400
- const resolved = path.resolve(path.dirname(cleanImporter), pathPart);
401
- for (const ext of [
402
- "",
403
- ".ts",
404
- ".tsx",
405
- ".js",
406
- ".jsx",
407
- ".json"
408
- ]) if (fs.existsSync(resolved + ext)) {
409
- const finalPath = resolved + ext + querySuffix;
410
- logger.log(`resolveId: resolved relative ${id} to ${finalPath}`);
411
- return finalPath;
412
- }
413
- } else {
414
- if (id.includes("/dist/") || id.includes("/lib/") || id.includes("/es/")) {
415
- logger.log(`resolveId: skipping already-resolved path ${id}`);
416
- return null;
640
+ if (id.startsWith("#")) try {
641
+ return await this.resolve(id, cleanImporter, { skipSelf: true });
642
+ } catch {
643
+ return null;
644
+ }
645
+ if (!id.endsWith(".vue")) {
646
+ if (id.includes("/dist/") || id.includes("/lib/") || id.includes("/es/")) return null;
647
+ try {
648
+ const resolved = await this.resolve(id, cleanImporter, { skipSelf: true });
649
+ if (resolved) {
650
+ logger.log(`resolveId: resolved ${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
+ if (id.startsWith("./") || id.startsWith("../")) {
659
+ const [pathPart, queryPart] = id.split("?");
660
+ const querySuffix = queryPart ? `?${queryPart}` : "";
661
+ const resolved = path.resolve(path.dirname(cleanImporter), pathPart);
662
+ for (const ext of [
663
+ "",
664
+ ".ts",
665
+ ".tsx",
666
+ ".js",
667
+ ".jsx",
668
+ ".json"
669
+ ]) {
670
+ const candidate = resolved + ext;
671
+ if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
672
+ const finalPath = candidate + querySuffix;
673
+ logger.log(`resolveId: resolved relative ${id} to ${finalPath}`);
674
+ return finalPath;
675
+ }
676
+ }
677
+ if (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory()) for (const indexFile of [
678
+ "/index.ts",
679
+ "/index.tsx",
680
+ "/index.js",
681
+ "/index.jsx"
682
+ ]) {
683
+ const candidate = resolved + indexFile;
684
+ if (fs.existsSync(candidate)) {
685
+ const finalPath = candidate + querySuffix;
686
+ logger.log(`resolveId: resolved directory ${id} to ${finalPath}`);
687
+ return finalPath;
688
+ }
689
+ }
417
690
  }
418
- logger.log(`resolveId: resolving external ${id} from ${cleanImporter}`);
419
- const resolved = await this.resolve(id, cleanImporter, { skipSelf: true });
420
- logger.log(`resolveId: resolved external ${id} to`, resolved?.id ?? "null");
421
- return resolved;
691
+ return null;
422
692
  }
423
693
  }
424
694
  if (id.endsWith(".vue")) {
425
- if (id.includes("node_modules")) {
695
+ const handleNodeModules = initialized ? mergedOptions.handleNodeModulesVue ?? true : true;
696
+ if (!handleNodeModules && id.includes("node_modules")) {
426
697
  logger.log(`resolveId: skipping node_modules import ${id}`);
427
698
  return null;
428
699
  }
429
700
  const resolved = resolveVuePath(id, importer);
430
- if (resolved.includes("node_modules")) {
701
+ const isNodeModulesPath = resolved.includes("node_modules");
702
+ if (!handleNodeModules && isNodeModulesPath) {
431
703
  logger.log(`resolveId: skipping node_modules path ${resolved}`);
432
704
  return null;
433
705
  }
434
- if (!filter(resolved)) {
706
+ if (filter && !isNodeModulesPath && !filter(resolved)) {
435
707
  logger.log(`resolveId: skipping filtered path ${resolved}`);
436
708
  return null;
437
709
  }
438
710
  const hasCache = cache.has(resolved);
439
711
  const fileExists = fs.existsSync(resolved);
440
712
  logger.log(`resolveId: id=${id}, resolved=${resolved}, hasCache=${hasCache}, fileExists=${fileExists}, importer=${importer ?? "none"}`);
441
- if (hasCache || fileExists) {
442
- const virtualId = VIRTUAL_PREFIX + resolved + ".ts";
443
- virtualToReal.set(virtualId, resolved);
444
- return virtualId;
713
+ if (hasCache || fileExists) return toVirtualId(resolved);
714
+ if (!fileExists && !path.isAbsolute(id)) {
715
+ const viteResolved = await this.resolve(id, importer, { skipSelf: true });
716
+ if (viteResolved && viteResolved.id.endsWith(".vue")) {
717
+ const realPath = viteResolved.id;
718
+ const isResolvedNodeModules = realPath.includes("node_modules");
719
+ if ((isResolvedNodeModules ? handleNodeModules : filter(realPath)) && (cache.has(realPath) || fs.existsSync(realPath))) {
720
+ logger.log(`resolveId: resolved via Vite fallback ${id} to ${realPath}`);
721
+ return toVirtualId(realPath);
722
+ }
723
+ }
445
724
  }
446
725
  }
447
726
  return null;
448
727
  },
449
- load(id) {
728
+ load(id, loadOptions) {
729
+ const currentBase = loadOptions?.ssr ? serverViteBase : clientViteBase;
450
730
  if (id === RESOLVED_CSS_MODULE) {
451
731
  const allCss = Array.from(collectedCss.values()).join("\n\n");
452
732
  return allCss;
453
733
  }
454
734
  if (id.includes("?vue&type=style")) {
455
735
  const [filename] = id.split("?");
456
- const realPath = filename.startsWith(VIRTUAL_PREFIX) ? virtualToReal.get(filename) ?? filename.slice(VIRTUAL_PREFIX.length) : filename;
736
+ const realPath = isVizeVirtual(filename) ? fromVirtualId(filename) : filename;
457
737
  const compiled = cache.get(realPath);
458
- if (compiled?.css) return compiled.css;
738
+ if (compiled?.css) return resolveCssImports(compiled.css, realPath, cssAliasRules, server !== null, currentBase);
459
739
  return "";
460
740
  }
461
- if (id.startsWith(VIRTUAL_PREFIX)) {
462
- const lookupId = id.endsWith(".ts") ? id.slice(0, -3) : id;
463
- const realPath = virtualToReal.get(id) ?? lookupId.slice(VIRTUAL_PREFIX.length);
464
- const compiled = cache.get(realPath);
741
+ if (isVizeVirtual(id)) {
742
+ const realPath = fromVirtualId(id);
743
+ if (!realPath.endsWith(".vue")) {
744
+ logger.log(`load: skipping non-vue virtual module ${realPath}`);
745
+ return null;
746
+ }
747
+ let compiled = cache.get(realPath);
748
+ if (!compiled && fs.existsSync(realPath)) {
749
+ logger.log(`load: on-demand compiling ${realPath}`);
750
+ compiled = compileFile(realPath, cache, {
751
+ sourceMap: mergedOptions?.sourceMap ?? !(isProduction ?? false),
752
+ ssr: mergedOptions?.ssr ?? false
753
+ });
754
+ }
465
755
  if (compiled) {
466
- const output = generateOutput(compiled, {
756
+ if (compiled.css) compiled = {
757
+ ...compiled,
758
+ css: resolveCssImports(compiled.css, realPath, cssAliasRules, server !== null, currentBase)
759
+ };
760
+ const output = rewriteStaticAssetUrls(rewriteDynamicTemplateImports(generateOutput(compiled, {
467
761
  isProduction,
468
762
  isDev: server !== null,
469
763
  extractCss
470
- });
764
+ }), dynamicImportAliasRules), dynamicImportAliasRules);
471
765
  return {
472
766
  code: output,
473
767
  map: null
474
768
  };
475
769
  }
476
770
  }
771
+ if (id.startsWith("\0")) {
772
+ const afterPrefix = id.startsWith(LEGACY_VIZE_PREFIX) ? id.slice(LEGACY_VIZE_PREFIX.length) : id.slice(1);
773
+ if (afterPrefix.includes("?commonjs-")) return null;
774
+ const [pathPart, queryPart] = afterPrefix.split("?");
775
+ const querySuffix = queryPart ? `?${queryPart}` : "";
776
+ const fsPath = pathPart.startsWith("/@fs/") ? pathPart.slice(4) : pathPart;
777
+ if (fsPath.startsWith("/") && fs.existsSync(fsPath) && fs.statSync(fsPath).isFile()) {
778
+ const importPath = server === null ? `${pathToFileURL(fsPath).href}${querySuffix}` : "/@fs" + fsPath + querySuffix;
779
+ logger.log(`load: proxying \0-prefixed file ${id} → re-export from ${importPath}`);
780
+ return `export { default } from ${JSON.stringify(importPath)};\nexport * from ${JSON.stringify(importPath)};`;
781
+ }
782
+ }
477
783
  return null;
478
784
  },
479
- async transform(code, id) {
480
- if (id.startsWith(VIRTUAL_PREFIX) && id.endsWith(".ts")) {
481
- const result = await transformWithOxc(code, id.slice(VIRTUAL_PREFIX.length), { lang: "ts" });
482
- return {
483
- code: result.code,
484
- map: result.map
485
- };
785
+ async transform(code, id, options$1) {
786
+ if (isVizeVirtual(id)) {
787
+ const realPath = fromVirtualId(id);
788
+ try {
789
+ const result = await transformWithOxc(code, realPath, { lang: "ts" });
790
+ const defines = options$1?.ssr ? serverViteDefine : clientViteDefine;
791
+ let transformed = result.code;
792
+ if (Object.keys(defines).length > 0) transformed = applyDefineReplacements(transformed, defines);
793
+ return {
794
+ code: transformed,
795
+ map: result.map
796
+ };
797
+ } catch (e) {
798
+ logger.error(`transformWithOxc failed for ${realPath}:`, e);
799
+ const dumpPath = `/tmp/vize-oxc-error-${path.basename(realPath)}.ts`;
800
+ fs.writeFileSync(dumpPath, code, "utf-8");
801
+ logger.error(`Dumped failing code to ${dumpPath}`);
802
+ return {
803
+ code: "export default {}",
804
+ map: null
805
+ };
806
+ }
486
807
  }
487
808
  return null;
488
809
  },
@@ -492,13 +813,13 @@ function vize(options = {}) {
492
813
  const source = await read();
493
814
  const prevCompiled = cache.get(file);
494
815
  compileFile(file, cache, {
495
- sourceMap: mergedOptions.sourceMap ?? !isProduction,
496
- ssr: mergedOptions.ssr ?? false
816
+ sourceMap: mergedOptions?.sourceMap ?? !isProduction,
817
+ ssr: mergedOptions?.ssr ?? false
497
818
  }, source);
498
819
  const newCompiled = cache.get(file);
499
820
  const updateType = detectHmrUpdateType(prevCompiled, newCompiled);
500
821
  logger.log(`Re-compiled: ${path.relative(root, file)} (${updateType})`);
501
- const virtualId = VIRTUAL_PREFIX + file + ".ts";
822
+ const virtualId = toVirtualId(file);
502
823
  const modules = server$1.moduleGraph.getModulesByFile(virtualId) ?? server$1.moduleGraph.getModulesByFile(file);
503
824
  if (updateType === "style-only" && newCompiled.css) {
504
825
  server$1.ws.send({
@@ -507,7 +828,7 @@ function vize(options = {}) {
507
828
  data: {
508
829
  id: newCompiled.scopeId,
509
830
  type: "style-only",
510
- css: newCompiled.css
831
+ css: resolveCssImports(newCompiled.css, file, cssAliasRules, true, clientViteBase)
511
832
  }
512
833
  });
513
834
  return [];
@@ -530,8 +851,33 @@ function vize(options = {}) {
530
851
  }
531
852
  }
532
853
  };
854
+ let compilerSfc = null;
855
+ const loadCompilerSfc = () => {
856
+ if (!compilerSfc) try {
857
+ const require = createRequire(import.meta.url);
858
+ compilerSfc = require("@vue/compiler-sfc");
859
+ } catch {
860
+ compilerSfc = { parse: () => ({
861
+ descriptor: {},
862
+ errors: []
863
+ }) };
864
+ }
865
+ return compilerSfc;
866
+ };
867
+ const vueCompatPlugin = {
868
+ name: "vite:vue",
869
+ api: { get options() {
870
+ return {
871
+ compiler: loadCompilerSfc(),
872
+ isProduction: isProduction ?? false,
873
+ root: root ?? process.cwd(),
874
+ template: {}
875
+ };
876
+ } }
877
+ };
878
+ return [vueCompatPlugin, mainPlugin];
533
879
  }
534
880
  var src_default = vize;
535
881
 
536
882
  //#endregion
537
- export { src_default as default, defineConfig, loadConfig, vize, vizeConfigStore };
883
+ export { __internal, src_default as default, defineConfig, loadConfig, vize, vizeConfigStore };
package/package.json CHANGED
@@ -1,9 +1,8 @@
1
1
  {
2
2
  "name": "@vizejs/vite-plugin",
3
- "version": "0.0.1-alpha.98",
3
+ "version": "0.1.0",
4
4
  "description": "High-performance native Vite plugin for Vue SFC compilation powered by Vize",
5
5
  "publishConfig": {
6
- "provenance": true,
7
6
  "access": "public"
8
7
  },
9
8
  "type": "module",
@@ -44,7 +43,7 @@
44
43
  },
45
44
  "dependencies": {
46
45
  "tinyglobby": "^0.2.0",
47
- "@vizejs/native": "0.0.1-alpha.98"
46
+ "@vizejs/native": "0.1.0"
48
47
  },
49
48
  "scripts": {
50
49
  "build": "tsdown",