@vizejs/vite-plugin 0.302.0 → 0.303.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.
Files changed (2) hide show
  1. package/dist/index.mjs +371 -89
  2. package/package.json +3 -3
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { registerResolvedVizeConfig, unregisterResolvedVizeConfig } from "./internal/config-bridge.mjs";
2
2
  import { createRequire } from "node:module";
3
- import { createHash } from "node:crypto";
3
+ import crypto, { createHash } from "node:crypto";
4
4
  import * as native from "@vizejs/native";
5
5
  import { applyViteDefineReplacements, buildInspectorGraph, chunkVitePrecompileFiles, classifyVitePluginRequest, createViteBareImportBases, createViteBareImportCandidates, createViteVirtualId, detectViteHmrUpdateType, diffVitePrecompileFiles, generateViteHmrCode, hasViteHmrChanges, isViteBareSpecifier, normalizeViteCssModuleFilename, normalizeViteDevMiddlewareUrl, normalizeVitePrecompileBatchSize, normalizeViteRequireBase, normalizeViteResolvedVuePath, resolveViteAliasRequest, resolveViteCssImports, resolveViteRelativeImport, resolveViteVuePath, rewriteViteDynamicTemplateImports, rewriteViteImportMetaGlobBase, rewriteViteStaticAssetUrls, scopeViteCssForPipeline, shouldApplyViteDefineInVirtualModule, splitViteIdQuery, toViteBrowserImportPrefix, transformViteCssVarsForPipeline } from "@vizejs/native";
6
6
  import { parseSync } from "oxc-parser";
@@ -526,6 +526,83 @@ function createLogger(debug) {
526
526
  };
527
527
  }
528
528
  //#endregion
529
+ //#region src/plugin/precompile.ts
530
+ const DEFAULT_PRECOMPILE_IGNORE_PATTERNS = [
531
+ "node_modules/**",
532
+ "dist/**",
533
+ ".git/**",
534
+ ".nuxt/**",
535
+ ".output/**",
536
+ ".nitro/**",
537
+ "coverage/**"
538
+ ];
539
+ function isPrecompileSfcPath(path) {
540
+ return path.endsWith(".vue");
541
+ }
542
+ function diffPrecompileFiles(files, currentMetadata, previousMetadata) {
543
+ return diffVitePrecompileFiles([...files], toNativePrecompileMetadataEntries(currentMetadata), toNativePrecompileMetadataEntries(previousMetadata));
544
+ }
545
+ function normalizePrecompileBatchSize(value) {
546
+ return normalizeVitePrecompileBatchSize(value);
547
+ }
548
+ function chunkPrecompileFiles(files, batchSize, options = {}) {
549
+ return chunkVitePrecompileFiles([...files], batchSize, {
550
+ maxBytes: options.maxBytes,
551
+ metadata: options.metadata ? toNativePrecompileMetadataEntries(options.metadata) : void 0
552
+ });
553
+ }
554
+ function toNativePrecompileMetadataEntries(metadata) {
555
+ const entries = [];
556
+ for (const [path, value] of metadata) entries.push({
557
+ path,
558
+ mtimeMs: value.mtimeMs,
559
+ size: value.size
560
+ });
561
+ return entries;
562
+ }
563
+ //#endregion
564
+ //#region src/plugin/state.ts
565
+ function getEnvironmentCache(state, ssr) {
566
+ return ssr ? state.ssrCache : state.cache;
567
+ }
568
+ function getCompileOptionsForRequest(state, ssr) {
569
+ const options = {
570
+ sourceMap: state.mergedOptions?.sourceMap ?? !state.isProduction,
571
+ ssr,
572
+ vapor: !ssr && (state.mergedOptions?.vapor ?? false),
573
+ customRenderer: state.mergedOptions?.customRenderer ?? false,
574
+ templateSyntax: state.mergedOptions?.templateSyntax ?? "standard"
575
+ };
576
+ if (state.mergedOptions?.mode !== void 0) options.mode = state.mergedOptions.mode;
577
+ if (state.mergedOptions?.runtimeModuleName !== void 0) options.runtimeModuleName = state.mergedOptions.runtimeModuleName;
578
+ if (state.mergedOptions?.runtimeGlobalName !== void 0) options.runtimeGlobalName = state.mergedOptions.runtimeGlobalName;
579
+ if (state.mergedOptions?.vueVersion !== void 0) options.vueVersion = state.mergedOptions.vueVersion;
580
+ if (state.mergedOptions?.experimentalInTagComments) options.experimentalInTagComments = true;
581
+ if (state.mergedOptions?.experimentalPatternedTemplate) options.experimentalPatternedTemplate = true;
582
+ if (state.mergedOptions?.experimentalServerScript) options.experimentalServerScript = true;
583
+ return options;
584
+ }
585
+ function syncCollectedCssForFile(state, filePath, compiled) {
586
+ if (!compiled || !state.extractCss) return;
587
+ if (compiled.styles?.length) {
588
+ state.collectedCss.delete(filePath);
589
+ return;
590
+ }
591
+ if (compiled.css && !hasDelegatedStyles(compiled)) state.collectedCss.set(filePath, resolveCssImports(compiled.css, filePath, state.cssAliasRules, false));
592
+ else state.collectedCss.delete(filePath);
593
+ }
594
+ function shouldExtractCssForRequest(state, ssr) {
595
+ return state.isProduction && !ssr;
596
+ }
597
+ function clearBuildCaches(state) {
598
+ state.cache.clear();
599
+ state.ssrCache.clear();
600
+ state.collectedCss.clear();
601
+ state.precompileMetadata.clear();
602
+ state.pendingHmrUpdateTypes.clear();
603
+ state.viteResolveCache?.clear();
604
+ }
605
+ //#endregion
529
606
  //#region src/compile-options.ts
530
607
  function buildCompileFileOptions(filePath, options) {
531
608
  return {
@@ -742,82 +819,272 @@ function compileBatch(files, cache, options) {
742
819
  }
743
820
  return result;
744
821
  }
745
- //#endregion
746
- //#region src/plugin/precompile.ts
747
- const DEFAULT_PRECOMPILE_IGNORE_PATTERNS = [
748
- "node_modules/**",
749
- "dist/**",
750
- ".git/**",
751
- ".nuxt/**",
752
- ".output/**",
753
- ".nitro/**",
754
- "coverage/**"
755
- ];
756
- function isPrecompileSfcPath(path) {
757
- return path.endsWith(".vue");
822
+ /** SHA-256 of the exact source text handed to the compiler. */
823
+ function hashPrecompileSource(source) {
824
+ return crypto.createHash("sha256").update(source, "utf8").digest("hex");
758
825
  }
759
- function diffPrecompileFiles(files, currentMetadata, previousMetadata) {
760
- return diffVitePrecompileFiles([...files], toNativePrecompileMetadataEntries(currentMetadata), toNativePrecompileMetadataEntries(previousMetadata));
826
+ /** Key-independent JSON: object key order must not change the hash. */
827
+ function stableStringify(value) {
828
+ if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
829
+ if (typeof value === "object" && value !== null) return `{${Object.entries(value).filter(([, item]) => item !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, item]) => `${JSON.stringify(key)}:${stableStringify(item)}`).join(",")}}`;
830
+ return JSON.stringify(value) ?? "null";
761
831
  }
762
- function normalizePrecompileBatchSize(value) {
763
- return normalizeVitePrecompileBatchSize(value);
832
+ function describeBinary(binary) {
833
+ try {
834
+ const stat = fs.statSync(binary);
835
+ return `${path.basename(binary)}:${stat.size}:${stat.mtimeMs}`;
836
+ } catch {
837
+ return `${path.basename(binary)}:unresolved`;
838
+ }
764
839
  }
765
- function chunkPrecompileFiles(files, batchSize, options = {}) {
766
- return chunkVitePrecompileFiles([...files], batchSize, {
767
- maxBytes: options.maxBytes,
768
- metadata: options.metadata ? toNativePrecompileMetadataEntries(options.metadata) : void 0
769
- });
840
+ /**
841
+ * Identity of the native compiler that produced (or would produce) the output.
842
+ *
843
+ * The package version covers releases; the binary's size and mtime cover local
844
+ * `pnpm --dir npm/native build:debug` rebuilds, which change codegen without
845
+ * changing any version. Hashing the binary itself would be airtight but costs
846
+ * hundreds of milliseconds for a >30 MB artifact, so a rebuild is treated as a
847
+ * new identity — a miss, never a stale hit.
848
+ */
849
+ function resolveCompilerIdentity() {
850
+ const configured = process.env.NAPI_RS_NATIVE_LIBRARY_PATH;
851
+ try {
852
+ const manifestPath = createRequire(import.meta.url).resolve("@vizejs/native/package.json");
853
+ const packageDir = path.dirname(manifestPath);
854
+ const version = JSON.parse(fs.readFileSync(manifestPath, "utf-8")).version;
855
+ const binaries = configured ? [configured] : fs.readdirSync(packageDir).filter((name) => name.endsWith(".node")).sort().map((name) => path.join(packageDir, name));
856
+ return {
857
+ version: typeof version === "string" ? version : "unknown",
858
+ binaries: binaries.map(describeBinary)
859
+ };
860
+ } catch {
861
+ return {
862
+ version: "unresolved",
863
+ binaries: [configured ?? "unresolved"]
864
+ };
865
+ }
770
866
  }
771
- function toNativePrecompileMetadataEntries(metadata) {
772
- const entries = [];
773
- for (const [path, value] of metadata) entries.push({
774
- path,
775
- mtimeMs: value.mtimeMs,
776
- size: value.size
867
+ /**
868
+ * Hash of everything outside the source text that changes compiled output.
869
+ *
870
+ * `compileOptions` must be the object actually handed to the native batch
871
+ * compiler, so a newly added compile option cannot be forgotten here.
872
+ */
873
+ function computePrecompileCacheKey(compileOptions) {
874
+ const material = stableStringify({
875
+ format: 1,
876
+ compiler: resolveCompilerIdentity(),
877
+ options: compileOptions
777
878
  });
778
- return entries;
879
+ return crypto.createHash("sha256").update(material, "utf8").digest("hex").slice(0, 32);
779
880
  }
780
881
  //#endregion
781
- //#region src/plugin/state.ts
782
- function getEnvironmentCache(state, ssr) {
783
- return ssr ? state.ssrCache : state.cache;
784
- }
785
- function getCompileOptionsForRequest(state, ssr) {
786
- const options = {
787
- sourceMap: state.mergedOptions?.sourceMap ?? !state.isProduction,
788
- ssr,
789
- vapor: !ssr && (state.mergedOptions?.vapor ?? false),
790
- customRenderer: state.mergedOptions?.customRenderer ?? false,
791
- templateSyntax: state.mergedOptions?.templateSyntax ?? "standard"
882
+ //#region src/plugin/precompile-cache.ts
883
+ /**
884
+ * Persistent (on-disk) pre-compile cache.
885
+ *
886
+ * `state.precompileMetadata` plus `state.cache` already skip recompilation
887
+ * inside one process, but both are empty on every process start, so `vite
888
+ * build`, CI, and each dev-server start recompile the whole scan from scratch.
889
+ * This module backs them with a manifest under
890
+ * `node_modules/.vize/vite-precompile/` so a cold process can restore the
891
+ * previous run's output.
892
+ *
893
+ * The two invalidation gates -- manifest identity and per-entry source hash --
894
+ * live in `./precompile-cache-key.ts`, which documents why each one is safe.
895
+ * Two more gates live here:
896
+ *
897
+ * - **Shape.** Entries are validated before use and dropped individually if
898
+ * they do not describe a complete `CompiledModule`. The manifest's own
899
+ * `format`/`key` are re-checked after parsing, so a manifest reached by any
900
+ * route other than its key still gets rejected.
901
+ * - **`src` imports.** SFCs that pull blocks in through `<script src>` /
902
+ * `<template src>` / `<style src>` are never persisted: their output depends
903
+ * on files whose content this cache does not hash.
904
+ *
905
+ * A missing, truncated, or corrupt manifest degrades to a full recompile: every
906
+ * read is guarded and any failure yields an empty cache. Writes go through a
907
+ * sibling temp file and a rename, so an interrupted write cannot be read back.
908
+ *
909
+ * Set `VIZE_PRECOMPILE_CACHE=0` to force a full recompile without editing any
910
+ * config.
911
+ */
912
+ /** Manifest location, relative to the Vite root. */
913
+ const PRECOMPILE_CACHE_DIR = path.join("node_modules", ".vize", "vite-precompile");
914
+ /** Set to `0`/`false` to force a full recompile without editing the config. */
915
+ const PRECOMPILE_CACHE_ENV = "VIZE_PRECOMPILE_CACHE";
916
+ /**
917
+ * Whether `module` may be persisted.
918
+ *
919
+ * Modules assembled from `src` imports depend on sibling files that this cache
920
+ * does not hash, so they are recompiled on every cold start instead.
921
+ */
922
+ function isPersistablePrecompileModule(module) {
923
+ return !module.dependencies || module.dependencies.length === 0;
924
+ }
925
+ function isCompiledModule(value) {
926
+ if (typeof value !== "object" || value === null) return false;
927
+ const module = value;
928
+ if (typeof module.code !== "string" || typeof module.scopeId !== "string") return false;
929
+ if (typeof module.hasScoped !== "boolean") return false;
930
+ if (module.css !== void 0 && typeof module.css !== "string") return false;
931
+ if (module.styles !== void 0 && !Array.isArray(module.styles)) return false;
932
+ if (module.macroArtifacts !== void 0 && !Array.isArray(module.macroArtifacts)) return false;
933
+ return isPersistablePrecompileModule(module);
934
+ }
935
+ function isCacheEntry(value) {
936
+ if (typeof value !== "object" || value === null) return false;
937
+ const entry = value;
938
+ return typeof entry.hash === "string" && entry.hash.length > 0 && isCompiledModule(entry.module);
939
+ }
940
+ /** Whether the environment forces the cache off. */
941
+ function isPrecompileCacheDisabledByEnv(env = process.env) {
942
+ const value = env[PRECOMPILE_CACHE_ENV];
943
+ return value === "0" || value === "false";
944
+ }
945
+ const disabledCache = {
946
+ file: null,
947
+ get: () => void 0,
948
+ set: () => {},
949
+ delete: () => {},
950
+ retain: () => {},
951
+ flush: () => false
952
+ };
953
+ /** A cache that never hits and never writes. */
954
+ function createDisabledPrecompileCache() {
955
+ return disabledCache;
956
+ }
957
+ function openPrecompileCache(options) {
958
+ const { root, compileOptions, onDiagnostic, env = process.env } = options;
959
+ if (!root || isPrecompileCacheDisabledByEnv(env)) return createDisabledPrecompileCache();
960
+ const key = computePrecompileCacheKey(compileOptions);
961
+ const file = path.join(root, PRECOMPILE_CACHE_DIR, `${key}.json`);
962
+ const entries = readManifestEntries(file, key, onDiagnostic);
963
+ let dirty = false;
964
+ return {
965
+ file,
966
+ get(filePath, sourceHash) {
967
+ const entry = entries.get(filePath);
968
+ return entry && entry.hash === sourceHash ? entry.module : void 0;
969
+ },
970
+ set(filePath, sourceHash, module) {
971
+ if (!isPersistablePrecompileModule(module)) {
972
+ if (entries.delete(filePath)) dirty = true;
973
+ return;
974
+ }
975
+ const existing = entries.get(filePath);
976
+ if (existing?.hash === sourceHash && existing.module === module) return;
977
+ entries.set(filePath, {
978
+ hash: sourceHash,
979
+ module
980
+ });
981
+ dirty = true;
982
+ },
983
+ delete(filePath) {
984
+ if (entries.delete(filePath)) dirty = true;
985
+ },
986
+ retain(files) {
987
+ const keep = files instanceof Set ? files : new Set(files);
988
+ for (const filePath of entries.keys()) if (!keep.has(filePath)) {
989
+ entries.delete(filePath);
990
+ dirty = true;
991
+ }
992
+ },
993
+ flush() {
994
+ if (!dirty) return false;
995
+ if (!writeManifest(file, {
996
+ format: 1,
997
+ key,
998
+ entries: Object.fromEntries(entries)
999
+ }, onDiagnostic)) return false;
1000
+ dirty = false;
1001
+ return true;
1002
+ }
792
1003
  };
793
- if (state.mergedOptions?.mode !== void 0) options.mode = state.mergedOptions.mode;
794
- if (state.mergedOptions?.runtimeModuleName !== void 0) options.runtimeModuleName = state.mergedOptions.runtimeModuleName;
795
- if (state.mergedOptions?.runtimeGlobalName !== void 0) options.runtimeGlobalName = state.mergedOptions.runtimeGlobalName;
796
- if (state.mergedOptions?.vueVersion !== void 0) options.vueVersion = state.mergedOptions.vueVersion;
797
- if (state.mergedOptions?.experimentalInTagComments) options.experimentalInTagComments = true;
798
- if (state.mergedOptions?.experimentalPatternedTemplate) options.experimentalPatternedTemplate = true;
799
- if (state.mergedOptions?.experimentalServerScript) options.experimentalServerScript = true;
800
- return options;
801
1004
  }
802
- function syncCollectedCssForFile(state, filePath, compiled) {
803
- if (!compiled || !state.extractCss) return;
804
- if (compiled.styles?.length) {
805
- state.collectedCss.delete(filePath);
806
- return;
1005
+ /**
1006
+ * Parse the manifest, or return an empty map.
1007
+ *
1008
+ * A missing, truncated, corrupt, or foreign manifest is indistinguishable from
1009
+ * no cache at all, which is exactly the safe outcome: recompile everything.
1010
+ */
1011
+ function readManifestEntries(file, key, onDiagnostic) {
1012
+ const entries = /* @__PURE__ */ new Map();
1013
+ let raw;
1014
+ try {
1015
+ raw = fs.readFileSync(file, "utf-8");
1016
+ } catch {
1017
+ return entries;
807
1018
  }
808
- if (compiled.css && !hasDelegatedStyles(compiled)) state.collectedCss.set(filePath, resolveCssImports(compiled.css, filePath, state.cssAliasRules, false));
809
- else state.collectedCss.delete(filePath);
1019
+ let parsed;
1020
+ try {
1021
+ parsed = JSON.parse(raw);
1022
+ } catch (error) {
1023
+ onDiagnostic?.(`Discarding corrupt pre-compile cache ${file}:`, error);
1024
+ return entries;
1025
+ }
1026
+ const manifest = parsed;
1027
+ if (typeof manifest !== "object" || manifest === null || manifest.format !== 1 || manifest.key !== key || typeof manifest.entries !== "object" || manifest.entries === null) {
1028
+ onDiagnostic?.(`Ignoring pre-compile cache ${file}: unrecognized manifest`);
1029
+ return entries;
1030
+ }
1031
+ for (const [filePath, entry] of Object.entries(manifest.entries)) if (isCacheEntry(entry)) entries.set(filePath, entry);
1032
+ return entries;
810
1033
  }
811
- function shouldExtractCssForRequest(state, ssr) {
812
- return state.isProduction && !ssr;
1034
+ /** Write through a sibling temp file so a crash cannot leave a partial manifest. */
1035
+ function writeManifest(file, manifest, onDiagnostic) {
1036
+ const temp = `${file}.${process.pid}.${crypto.randomBytes(4).toString("hex")}.tmp`;
1037
+ try {
1038
+ fs.mkdirSync(path.dirname(file), { recursive: true });
1039
+ fs.writeFileSync(temp, JSON.stringify(manifest));
1040
+ fs.renameSync(temp, file);
1041
+ return true;
1042
+ } catch (error) {
1043
+ try {
1044
+ fs.rmSync(temp, { force: true });
1045
+ } catch {}
1046
+ onDiagnostic?.(`Failed to write pre-compile cache ${file}:`, error);
1047
+ return false;
1048
+ }
813
1049
  }
814
- function clearBuildCaches(state) {
815
- state.cache.clear();
816
- state.ssrCache.clear();
817
- state.collectedCss.clear();
818
- state.precompileMetadata.clear();
819
- state.pendingHmrUpdateTypes.clear();
820
- state.viteResolveCache?.clear();
1050
+ //#endregion
1051
+ //#region src/plugin/precompile-run.ts
1052
+ /**
1053
+ * The pre-compilation pass.
1054
+ *
1055
+ * Scans the configured patterns, diffs them against the previous in-process
1056
+ * run, restores whatever the persistent cache can prove is still valid, and
1057
+ * batch-compiles the rest. Extracted from `state.ts` so the state module stays
1058
+ * a state module.
1059
+ */
1060
+ /**
1061
+ * The options the pre-compile batch actually compiles with.
1062
+ *
1063
+ * Also the input to the cache key, so the two can never drift: a new option
1064
+ * that reaches the native compiler reaches the key with it.
1065
+ */
1066
+ function resolvePrecompileBatchOptions(state) {
1067
+ return {
1068
+ ssr: false,
1069
+ vapor: state.mergedOptions.vapor ?? false,
1070
+ mode: state.mergedOptions.mode,
1071
+ customRenderer: state.mergedOptions.customRenderer ?? false,
1072
+ templateSyntax: state.mergedOptions.templateSyntax ?? "standard",
1073
+ experimentalInTagComments: state.mergedOptions.experimentalInTagComments ?? false,
1074
+ experimentalPatternedTemplate: state.mergedOptions.experimentalPatternedTemplate ?? false,
1075
+ experimentalServerScript: state.mergedOptions.experimentalServerScript ?? false,
1076
+ runtimeModuleName: state.mergedOptions.runtimeModuleName,
1077
+ runtimeGlobalName: state.mergedOptions.runtimeGlobalName,
1078
+ vueVersion: state.mergedOptions.vueVersion
1079
+ };
1080
+ }
1081
+ function openCacheForRun(state, batchOptions) {
1082
+ if (!state.root) return createDisabledPrecompileCache();
1083
+ return openPrecompileCache({
1084
+ root: state.root,
1085
+ compileOptions: buildCompileBatchOptions(batchOptions),
1086
+ onDiagnostic: (message, error) => error === void 0 ? state.logger.warn(message) : state.logger.warn(message, error)
1087
+ });
821
1088
  }
822
1089
  /**
823
1090
  * Pre-compile all Vue files matching scan patterns.
@@ -858,41 +1125,50 @@ async function compileAll(state) {
858
1125
  if (state.extractCss) state.collectedCss.delete(file);
859
1126
  state.pendingHmrUpdateTypes.delete(file);
860
1127
  }
1128
+ const batchOptions = resolvePrecompileBatchOptions(state);
1129
+ const cache = openCacheForRun(state, batchOptions);
1130
+ const cacheEnabled = cache.file !== null;
861
1131
  let successCount = 0;
1132
+ let restoredCount = 0;
862
1133
  let failedCount = 0;
863
1134
  let nativeTimeMs = 0;
864
1135
  const precompileFailures = [];
865
1136
  const chunks = chunkPrecompileFiles(changedFiles, state.precompileBatchSize, { metadata: currentMetadata });
866
1137
  for (const chunk of chunks) {
867
1138
  const fileContents = [];
868
- for (const file of chunk) try {
869
- const source = fs.readFileSync(file, "utf-8");
1139
+ const sourceHashes = /* @__PURE__ */ new Map();
1140
+ for (const file of chunk) {
1141
+ let source;
1142
+ try {
1143
+ source = fs.readFileSync(file, "utf-8");
1144
+ } catch (e) {
1145
+ failedCount++;
1146
+ state.cache.delete(file);
1147
+ if (state.extractCss) state.collectedCss.delete(file);
1148
+ state.precompileMetadata.delete(file);
1149
+ cache.delete(file);
1150
+ precompileFailures.push(`[vize] Failed to read ${file}: ${formatUnknownError$2(e)}`);
1151
+ state.logger.error(`Failed to read ${file}:`, e);
1152
+ continue;
1153
+ }
1154
+ const sourceHash = cacheEnabled ? hashPrecompileSource(source) : void 0;
1155
+ const restored = sourceHash === void 0 ? void 0 : cache.get(file, sourceHash);
1156
+ if (restored) {
1157
+ state.cache.set(file, restored);
1158
+ const metadata = currentMetadata.get(file);
1159
+ if (metadata) state.precompileMetadata.set(file, metadata);
1160
+ syncCollectedCssForFile(state, file, restored);
1161
+ restoredCount++;
1162
+ continue;
1163
+ }
1164
+ if (sourceHash !== void 0) sourceHashes.set(file, sourceHash);
870
1165
  fileContents.push({
871
1166
  path: file,
872
1167
  source
873
1168
  });
874
- } catch (e) {
875
- failedCount++;
876
- state.cache.delete(file);
877
- if (state.extractCss) state.collectedCss.delete(file);
878
- state.precompileMetadata.delete(file);
879
- precompileFailures.push(`[vize] Failed to read ${file}: ${formatUnknownError$2(e)}`);
880
- state.logger.error(`Failed to read ${file}:`, e);
881
1169
  }
882
1170
  if (fileContents.length === 0) continue;
883
- const result = compileBatch(fileContents, state.cache, {
884
- ssr: false,
885
- vapor: state.mergedOptions.vapor ?? false,
886
- mode: state.mergedOptions.mode,
887
- customRenderer: state.mergedOptions.customRenderer ?? false,
888
- templateSyntax: state.mergedOptions.templateSyntax ?? "standard",
889
- experimentalInTagComments: state.mergedOptions.experimentalInTagComments ?? false,
890
- experimentalPatternedTemplate: state.mergedOptions.experimentalPatternedTemplate ?? false,
891
- experimentalServerScript: state.mergedOptions.experimentalServerScript ?? false,
892
- runtimeModuleName: state.mergedOptions.runtimeModuleName,
893
- runtimeGlobalName: state.mergedOptions.runtimeGlobalName,
894
- vueVersion: state.mergedOptions.vueVersion
895
- });
1171
+ const result = compileBatch(fileContents, state.cache, batchOptions);
896
1172
  const chunkFailedCount = result.results.filter((fileResult) => fileResult.errors.length > 0).length;
897
1173
  failedCount += chunkFailedCount;
898
1174
  successCount += result.results.length - chunkFailedCount;
@@ -903,16 +1179,22 @@ async function compileAll(state) {
903
1179
  state.cache.delete(fileResult.path);
904
1180
  if (state.extractCss) state.collectedCss.delete(fileResult.path);
905
1181
  state.precompileMetadata.delete(fileResult.path);
1182
+ cache.delete(fileResult.path);
906
1183
  precompileFailures.push(formatCompileErrorMessage(fileResult.path, fileResult.errors));
907
1184
  continue;
908
1185
  }
909
1186
  if (metadata) state.precompileMetadata.set(fileResult.path, metadata);
910
- syncCollectedCssForFile(state, fileResult.path, state.cache.get(fileResult.path));
1187
+ const compiled = state.cache.get(fileResult.path);
1188
+ const sourceHash = sourceHashes.get(fileResult.path);
1189
+ if (compiled && sourceHash !== void 0) cache.set(fileResult.path, sourceHash, compiled);
1190
+ syncCollectedCssForFile(state, fileResult.path, compiled);
911
1191
  }
912
1192
  }
1193
+ cache.retain(new Set(sfcFiles));
1194
+ cache.flush();
913
1195
  const elapsed = (performance.now() - startTime).toFixed(2);
914
1196
  const batchLabel = chunks.length === 1 ? "batch" : "batches";
915
- state.logger.info(`Pre-compilation complete: ${successCount} recompiled, ${cachedFileCount} reused, ${failedCount} failed (${elapsed}ms, native ${batchLabel}: ${nativeTimeMs.toFixed(2)}ms)`);
1197
+ state.logger.info(`Pre-compilation complete: ${successCount} recompiled, ${restoredCount} restored from disk, ${cachedFileCount} reused, ${failedCount} failed (${elapsed}ms, native ${batchLabel}: ${nativeTimeMs.toFixed(2)}ms)`);
916
1198
  if (failedCount > 0) {
917
1199
  const details = precompileFailures.length > 0 ? `\n\n${precompileFailures.join("\n\n")}` : "";
918
1200
  throw new Error(`[vize] Pre-compilation failed for ${failedCount} file(s).${details}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vizejs/vite-plugin",
3
- "version": "0.302.0",
3
+ "version": "0.303.0",
4
4
  "description": "High-performance native Vite plugin for Vue SFC compilation powered by Vize",
5
5
  "keywords": [
6
6
  "compiler",
@@ -45,10 +45,10 @@
45
45
  "access": "public"
46
46
  },
47
47
  "dependencies": {
48
- "@vizejs/native": "0.302.0",
48
+ "@vizejs/native": "0.303.0",
49
49
  "oxc-parser": "0.133.0",
50
50
  "tinyglobby": "0.2.16",
51
- "vize": "0.302.0"
51
+ "vize": "0.303.0"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@types/node": "25.9.2",