@vizejs/vite-plugin 0.291.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.
- package/dist/index.mjs +405 -153
- package/package.json +4 -3
package/dist/index.mjs
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
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
|
-
import
|
|
7
|
-
import { parseSync } from "vite";
|
|
6
|
+
import { parseSync } from "oxc-parser";
|
|
8
7
|
import fs from "node:fs";
|
|
9
8
|
import { glob } from "tinyglobby";
|
|
10
9
|
import path from "node:path";
|
|
11
10
|
import { pathToFileURL } from "node:url";
|
|
11
|
+
import * as vite from "vite";
|
|
12
12
|
//#region src/hmr.ts
|
|
13
13
|
function hasHmrChanges(prev, next) {
|
|
14
14
|
if (!prev) return true;
|
|
@@ -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 {
|
|
@@ -624,7 +701,23 @@ function inlineStyleSrcBlocks(source, filePath, dependencies) {
|
|
|
624
701
|
return `<style${stripSrcAttribute(`${beforeSrc}${afterSrc}`)}>\n${imported.content}\n</style>`;
|
|
625
702
|
});
|
|
626
703
|
}
|
|
704
|
+
/**
|
|
705
|
+
* Cheap necessary condition for an SFC block carrying a `src` attribute.
|
|
706
|
+
*
|
|
707
|
+
* `extractSfcSrcInfo` parses a full SFC descriptor, so calling it for every file
|
|
708
|
+
* costs a second whole-source parse per compile on top of the one the compiler
|
|
709
|
+
* itself performs -- and block `src` attributes are rare (roughly 4% of files in
|
|
710
|
+
* a real app). Any `<script src>`/`<template src>`/`<style src>` necessarily
|
|
711
|
+
* contains `src` followed by `=`, so a source without this substring provably
|
|
712
|
+
* has nothing to inline and can skip the descriptor parse entirely. False
|
|
713
|
+
* positives (e.g. `<img src=...>` in a template) simply take the original path.
|
|
714
|
+
*/
|
|
715
|
+
const SFC_SRC_ATTRIBUTE_HINT = /\bsrc\s*=/i;
|
|
627
716
|
function resolveSfcSrcImports(filePath, source) {
|
|
717
|
+
if (!SFC_SRC_ATTRIBUTE_HINT.test(source)) return {
|
|
718
|
+
source,
|
|
719
|
+
dependencies: []
|
|
720
|
+
};
|
|
628
721
|
const dependencies = [];
|
|
629
722
|
const srcInfo = native.extractSfcSrcInfo(source, filePath);
|
|
630
723
|
let resolvedSource = source;
|
|
@@ -726,82 +819,272 @@ function compileBatch(files, cache, options) {
|
|
|
726
819
|
}
|
|
727
820
|
return result;
|
|
728
821
|
}
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
"node_modules/**",
|
|
733
|
-
"dist/**",
|
|
734
|
-
".git/**",
|
|
735
|
-
".nuxt/**",
|
|
736
|
-
".output/**",
|
|
737
|
-
".nitro/**",
|
|
738
|
-
"coverage/**"
|
|
739
|
-
];
|
|
740
|
-
function isPrecompileSfcPath(path) {
|
|
741
|
-
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");
|
|
742
825
|
}
|
|
743
|
-
|
|
744
|
-
|
|
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";
|
|
745
831
|
}
|
|
746
|
-
function
|
|
747
|
-
|
|
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
|
+
}
|
|
748
839
|
}
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
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
|
+
}
|
|
754
866
|
}
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
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
|
|
761
878
|
});
|
|
762
|
-
return
|
|
879
|
+
return crypto.createHash("sha256").update(material, "utf8").digest("hex").slice(0, 32);
|
|
763
880
|
}
|
|
764
881
|
//#endregion
|
|
765
|
-
//#region src/plugin/
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
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
|
+
}
|
|
776
1003
|
};
|
|
777
|
-
if (state.mergedOptions?.mode !== void 0) options.mode = state.mergedOptions.mode;
|
|
778
|
-
if (state.mergedOptions?.runtimeModuleName !== void 0) options.runtimeModuleName = state.mergedOptions.runtimeModuleName;
|
|
779
|
-
if (state.mergedOptions?.runtimeGlobalName !== void 0) options.runtimeGlobalName = state.mergedOptions.runtimeGlobalName;
|
|
780
|
-
if (state.mergedOptions?.vueVersion !== void 0) options.vueVersion = state.mergedOptions.vueVersion;
|
|
781
|
-
if (state.mergedOptions?.experimentalInTagComments) options.experimentalInTagComments = true;
|
|
782
|
-
if (state.mergedOptions?.experimentalPatternedTemplate) options.experimentalPatternedTemplate = true;
|
|
783
|
-
if (state.mergedOptions?.experimentalServerScript) options.experimentalServerScript = true;
|
|
784
|
-
return options;
|
|
785
1004
|
}
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
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;
|
|
791
1018
|
}
|
|
792
|
-
|
|
793
|
-
|
|
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;
|
|
794
1033
|
}
|
|
795
|
-
|
|
796
|
-
|
|
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
|
+
}
|
|
797
1049
|
}
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
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
|
+
});
|
|
805
1088
|
}
|
|
806
1089
|
/**
|
|
807
1090
|
* Pre-compile all Vue files matching scan patterns.
|
|
@@ -842,41 +1125,50 @@ async function compileAll(state) {
|
|
|
842
1125
|
if (state.extractCss) state.collectedCss.delete(file);
|
|
843
1126
|
state.pendingHmrUpdateTypes.delete(file);
|
|
844
1127
|
}
|
|
1128
|
+
const batchOptions = resolvePrecompileBatchOptions(state);
|
|
1129
|
+
const cache = openCacheForRun(state, batchOptions);
|
|
1130
|
+
const cacheEnabled = cache.file !== null;
|
|
845
1131
|
let successCount = 0;
|
|
1132
|
+
let restoredCount = 0;
|
|
846
1133
|
let failedCount = 0;
|
|
847
1134
|
let nativeTimeMs = 0;
|
|
848
1135
|
const precompileFailures = [];
|
|
849
1136
|
const chunks = chunkPrecompileFiles(changedFiles, state.precompileBatchSize, { metadata: currentMetadata });
|
|
850
1137
|
for (const chunk of chunks) {
|
|
851
1138
|
const fileContents = [];
|
|
852
|
-
|
|
853
|
-
|
|
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);
|
|
854
1165
|
fileContents.push({
|
|
855
1166
|
path: file,
|
|
856
1167
|
source
|
|
857
1168
|
});
|
|
858
|
-
} catch (e) {
|
|
859
|
-
failedCount++;
|
|
860
|
-
state.cache.delete(file);
|
|
861
|
-
if (state.extractCss) state.collectedCss.delete(file);
|
|
862
|
-
state.precompileMetadata.delete(file);
|
|
863
|
-
precompileFailures.push(`[vize] Failed to read ${file}: ${formatUnknownError$2(e)}`);
|
|
864
|
-
state.logger.error(`Failed to read ${file}:`, e);
|
|
865
1169
|
}
|
|
866
1170
|
if (fileContents.length === 0) continue;
|
|
867
|
-
const result = compileBatch(fileContents, state.cache,
|
|
868
|
-
ssr: false,
|
|
869
|
-
vapor: state.mergedOptions.vapor ?? false,
|
|
870
|
-
mode: state.mergedOptions.mode,
|
|
871
|
-
customRenderer: state.mergedOptions.customRenderer ?? false,
|
|
872
|
-
templateSyntax: state.mergedOptions.templateSyntax ?? "standard",
|
|
873
|
-
experimentalInTagComments: state.mergedOptions.experimentalInTagComments ?? false,
|
|
874
|
-
experimentalPatternedTemplate: state.mergedOptions.experimentalPatternedTemplate ?? false,
|
|
875
|
-
experimentalServerScript: state.mergedOptions.experimentalServerScript ?? false,
|
|
876
|
-
runtimeModuleName: state.mergedOptions.runtimeModuleName,
|
|
877
|
-
runtimeGlobalName: state.mergedOptions.runtimeGlobalName,
|
|
878
|
-
vueVersion: state.mergedOptions.vueVersion
|
|
879
|
-
});
|
|
1171
|
+
const result = compileBatch(fileContents, state.cache, batchOptions);
|
|
880
1172
|
const chunkFailedCount = result.results.filter((fileResult) => fileResult.errors.length > 0).length;
|
|
881
1173
|
failedCount += chunkFailedCount;
|
|
882
1174
|
successCount += result.results.length - chunkFailedCount;
|
|
@@ -887,16 +1179,22 @@ async function compileAll(state) {
|
|
|
887
1179
|
state.cache.delete(fileResult.path);
|
|
888
1180
|
if (state.extractCss) state.collectedCss.delete(fileResult.path);
|
|
889
1181
|
state.precompileMetadata.delete(fileResult.path);
|
|
1182
|
+
cache.delete(fileResult.path);
|
|
890
1183
|
precompileFailures.push(formatCompileErrorMessage(fileResult.path, fileResult.errors));
|
|
891
1184
|
continue;
|
|
892
1185
|
}
|
|
893
1186
|
if (metadata) state.precompileMetadata.set(fileResult.path, metadata);
|
|
894
|
-
|
|
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);
|
|
895
1191
|
}
|
|
896
1192
|
}
|
|
1193
|
+
cache.retain(new Set(sfcFiles));
|
|
1194
|
+
cache.flush();
|
|
897
1195
|
const elapsed = (performance.now() - startTime).toFixed(2);
|
|
898
1196
|
const batchLabel = chunks.length === 1 ? "batch" : "batches";
|
|
899
|
-
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)`);
|
|
900
1198
|
if (failedCount > 0) {
|
|
901
1199
|
const details = precompileFailures.length > 0 ? `\n\n${precompileFailures.join("\n\n")}` : "";
|
|
902
1200
|
throw new Error(`[vize] Pre-compilation failed for ${failedCount} file(s).${details}`);
|
|
@@ -1649,67 +1947,21 @@ function createVirtualTypeScriptTransformer(viteApi) {
|
|
|
1649
1947
|
throw new Error("Installed Vite does not expose transformWithOxc or transformWithEsbuild");
|
|
1650
1948
|
};
|
|
1651
1949
|
}
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
const char = code[index];
|
|
1668
|
-
const next = code[index + 1];
|
|
1669
|
-
if (lineComment) {
|
|
1670
|
-
if (char === "\n" || char === "\r") lineComment = false;
|
|
1671
|
-
continue;
|
|
1672
|
-
}
|
|
1673
|
-
if (blockComment) {
|
|
1674
|
-
if (char === "*" && next === "/") {
|
|
1675
|
-
blockComment = false;
|
|
1676
|
-
index += 1;
|
|
1677
|
-
}
|
|
1678
|
-
continue;
|
|
1679
|
-
}
|
|
1680
|
-
if (quote) {
|
|
1681
|
-
if (escaped) escaped = false;
|
|
1682
|
-
else if (char === "\\") escaped = true;
|
|
1683
|
-
else if (char === quote) quote = null;
|
|
1684
|
-
continue;
|
|
1685
|
-
}
|
|
1686
|
-
if (char === "/" && next === "/") {
|
|
1687
|
-
lineComment = true;
|
|
1688
|
-
index += 1;
|
|
1689
|
-
continue;
|
|
1690
|
-
}
|
|
1691
|
-
if (char === "/" && next === "*") {
|
|
1692
|
-
blockComment = true;
|
|
1693
|
-
index += 1;
|
|
1694
|
-
continue;
|
|
1695
|
-
}
|
|
1696
|
-
if (char === "'" || char === "\"" || char === "`") {
|
|
1697
|
-
quote = char;
|
|
1698
|
-
continue;
|
|
1699
|
-
}
|
|
1700
|
-
if (char === "{" || char === "(" || char === "[") {
|
|
1701
|
-
stack.push(char);
|
|
1702
|
-
continue;
|
|
1703
|
-
}
|
|
1704
|
-
if (char === "}" || char === ")" || char === "]") {
|
|
1705
|
-
const open = stack.pop();
|
|
1706
|
-
if (char === "}" && open !== "{" || char === ")" && open !== "(" || char === "]" && open !== "[") return true;
|
|
1707
|
-
}
|
|
1708
|
-
}
|
|
1709
|
-
return quote !== null || blockComment || stack.length > 0;
|
|
1710
|
-
}
|
|
1711
|
-
function needsVirtualTypeScriptTransform(code) {
|
|
1712
|
-
return TYPE_DECLARATION_RE.test(code) || TYPE_ASSERTION_RE.test(code) || TYPED_BINDING_RE.test(code) || GENERIC_FUNCTION_RE.test(code) || TYPED_PARAMETER_RE.test(code) || RETURN_TYPE_RE.test(code) || ACCESS_MODIFIER_RE.test(code) || SATISFIES_RE.test(code) || hasUnbalancedDelimiters(code);
|
|
1950
|
+
/**
|
|
1951
|
+
* Report whether `realPath` names a module Vize's own compiler emitted.
|
|
1952
|
+
*
|
|
1953
|
+
* Vize's Rust emitter guarantees plain JavaScript for every module it produces
|
|
1954
|
+
* (`ensure_javascript_output` at the napi boundary), so re-running Vite's
|
|
1955
|
+
* TypeScript strip over emitter output is a pure re-print. Every emitted module
|
|
1956
|
+
* is recorded in one of the two environment caches, so cache membership is the
|
|
1957
|
+
* cheap, allocation-free proof that the code came from the emitter.
|
|
1958
|
+
*
|
|
1959
|
+
* The probe fails safe: a module the caches do not know about still gets the
|
|
1960
|
+
* strip, which keeps hand-written and malformed virtual modules behaving
|
|
1961
|
+
* exactly as they did before.
|
|
1962
|
+
*/
|
|
1963
|
+
function isVizeEmitterOutput(state, realPath) {
|
|
1964
|
+
return state.cache.has(realPath) || state.ssrCache.has(realPath);
|
|
1713
1965
|
}
|
|
1714
1966
|
const transformVirtualTypeScript = createVirtualTypeScriptTransformer(vite);
|
|
1715
1967
|
function getOxcDumpPath(root, realPath) {
|
|
@@ -1731,7 +1983,7 @@ function formatUnknownError$1(error) {
|
|
|
1731
1983
|
return error instanceof Error ? error.message : String(error);
|
|
1732
1984
|
}
|
|
1733
1985
|
async function transformVizeVirtualModule(state, code, realPath, ssr, forceTypeScriptTransform = false) {
|
|
1734
|
-
const needsTsTransform = forceTypeScriptTransform ||
|
|
1986
|
+
const needsTsTransform = forceTypeScriptTransform || !isVizeEmitterOutput(state, realPath);
|
|
1735
1987
|
try {
|
|
1736
1988
|
let transformed = (needsTsTransform ? await transformVirtualTypeScript(code, realPath) : { code }).code;
|
|
1737
1989
|
if (transformed.includes("import.meta.")) transformed = applyDefineReplacements(transformed, getVirtualModuleDefines(state, ssr));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vizejs/vite-plugin",
|
|
3
|
-
"version": "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,9 +45,10 @@
|
|
|
45
45
|
"access": "public"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@vizejs/native": "0.
|
|
48
|
+
"@vizejs/native": "0.303.0",
|
|
49
|
+
"oxc-parser": "0.133.0",
|
|
49
50
|
"tinyglobby": "0.2.16",
|
|
50
|
-
"vize": "0.
|
|
51
|
+
"vize": "0.303.0"
|
|
51
52
|
},
|
|
52
53
|
"devDependencies": {
|
|
53
54
|
"@types/node": "25.9.2",
|