veryfront 0.1.87 → 0.1.89
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/esm/deno.js +1 -1
- package/esm/src/cache/distributed-cache-init.d.ts +9 -0
- package/esm/src/cache/distributed-cache-init.d.ts.map +1 -1
- package/esm/src/cache/distributed-cache-init.js +70 -52
- package/esm/src/cache/module-cache.d.ts +6 -2
- package/esm/src/cache/module-cache.d.ts.map +1 -1
- package/esm/src/cache/module-cache.js +73 -56
- package/esm/src/rendering/orchestrator/module-loader/index.js +2 -2
- package/esm/src/transforms/esm/http-cache-wrapper.d.ts +3 -0
- package/esm/src/transforms/esm/http-cache-wrapper.d.ts.map +1 -1
- package/esm/src/transforms/esm/http-cache-wrapper.js +21 -7
- package/esm/src/transforms/mdx/esm-module-loader/cache/index.d.ts.map +1 -1
- package/esm/src/transforms/mdx/esm-module-loader/cache/index.js +6 -7
- package/esm/src/transforms/mdx/esm-module-loader/cache-format.d.ts +10 -0
- package/esm/src/transforms/mdx/esm-module-loader/cache-format.d.ts.map +1 -0
- package/esm/src/transforms/mdx/esm-module-loader/cache-format.js +61 -0
- package/esm/src/transforms/mdx/esm-module-loader/import-transformer.d.ts.map +1 -1
- package/esm/src/transforms/mdx/esm-module-loader/import-transformer.js +2 -3
- package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/cache-keys.js +3 -3
- package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/framework-validator.d.ts.map +1 -1
- package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/framework-validator.js +2 -1
- package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/module-cache.d.ts.map +1 -1
- package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/module-cache.js +5 -5
- package/esm/src/transforms/pipeline/stages/ssr-vf-modules/transform.d.ts +2 -2
- package/esm/src/transforms/pipeline/stages/ssr-vf-modules/transform.d.ts.map +1 -1
- package/esm/src/transforms/pipeline/stages/ssr-vf-modules/transform.js +4 -4
- package/esm/src/utils/cache-namespace.d.ts +11 -0
- package/esm/src/utils/cache-namespace.d.ts.map +1 -0
- package/esm/src/utils/cache-namespace.js +24 -0
- package/esm/src/utils/version.d.ts +1 -1
- package/esm/src/utils/version.js +1 -1
- package/package.json +1 -1
- package/src/deno.js +1 -1
- package/src/src/cache/distributed-cache-init.ts +95 -59
- package/src/src/cache/module-cache.ts +95 -57
- package/src/src/rendering/orchestrator/module-loader/index.ts +2 -2
- package/src/src/transforms/esm/http-cache-wrapper.ts +28 -7
- package/src/src/transforms/mdx/esm-module-loader/cache/index.ts +6 -8
- package/src/src/transforms/mdx/esm-module-loader/cache-format.ts +121 -0
- package/src/src/transforms/mdx/esm-module-loader/import-transformer.ts +2 -3
- package/src/src/transforms/mdx/esm-module-loader/module-fetcher/cache-keys.ts +3 -3
- package/src/src/transforms/mdx/esm-module-loader/module-fetcher/framework-validator.ts +2 -1
- package/src/src/transforms/mdx/esm-module-loader/module-fetcher/module-cache.ts +5 -5
- package/src/src/transforms/pipeline/stages/ssr-vf-modules/transform.ts +4 -4
- package/src/src/utils/cache-namespace.ts +42 -0
- package/src/src/utils/version.ts +1 -1
|
@@ -16,6 +16,7 @@ import { rendererLogger } from "../../utils/index.js";
|
|
|
16
16
|
import { VERSION } from "../../utils/version.js";
|
|
17
17
|
import { getCacheBaseDir } from "../../utils/cache-dir.js";
|
|
18
18
|
import { CacheBackends, createDistributedCacheAccessor } from "../../cache/backend.js";
|
|
19
|
+
import type { CacheBackend } from "../../cache/types.js";
|
|
19
20
|
import { HTTP_MODULE_DISTRIBUTED_TTL_SEC } from "../../utils/constants/cache.js";
|
|
20
21
|
import type {
|
|
21
22
|
BundleHash,
|
|
@@ -44,6 +45,26 @@ const getDistributedCache = createDistributedCacheAccessor(
|
|
|
44
45
|
"HTTP-CACHE-WRAPPER",
|
|
45
46
|
);
|
|
46
47
|
|
|
48
|
+
let testDistributedCacheAccessor: (() => Promise<CacheBackend | null>) | null = null;
|
|
49
|
+
|
|
50
|
+
function resolveDistributedCache(): Promise<CacheBackend | null> {
|
|
51
|
+
return testDistributedCacheAccessor ? testDistributedCacheAccessor() : getDistributedCache();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function __setDistributedCacheAccessorForTests(
|
|
55
|
+
accessor: (() => Promise<CacheBackend | null>) | null,
|
|
56
|
+
): void {
|
|
57
|
+
testDistributedCacheAccessor = accessor;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function initializeHttpModuleDistributedCache(): Promise<boolean> {
|
|
61
|
+
const distributed = await resolveDistributedCache();
|
|
62
|
+
if (!distributed) return false;
|
|
63
|
+
|
|
64
|
+
logger.info("Initialized distributed cache backend", { backend: distributed.type });
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
|
|
47
68
|
/**
|
|
48
69
|
* Generate versioned cache key for HTTP bundles.
|
|
49
70
|
* Format: {VERSION}:{prefix}:{hash}
|
|
@@ -158,7 +179,7 @@ class HttpBundleCache {
|
|
|
158
179
|
* @returns Result containing local code or failure reason
|
|
159
180
|
*/
|
|
160
181
|
async getCodeByHash(hash: BundleHash | string): Promise<GetCodeResult> {
|
|
161
|
-
const distributed = await
|
|
182
|
+
const distributed = await resolveDistributedCache();
|
|
162
183
|
if (!distributed) {
|
|
163
184
|
return { code: null, wasGzipped: false, failReason: "not_found" };
|
|
164
185
|
}
|
|
@@ -214,7 +235,7 @@ class HttpBundleCache {
|
|
|
214
235
|
* @returns Result containing local code or failure reason
|
|
215
236
|
*/
|
|
216
237
|
async getCodeByUrl(hash: BundleHash | string): Promise<GetCodeResult> {
|
|
217
|
-
const distributed = await
|
|
238
|
+
const distributed = await resolveDistributedCache();
|
|
218
239
|
if (!distributed) {
|
|
219
240
|
return { code: null, wasGzipped: false, failReason: "not_found" };
|
|
220
241
|
}
|
|
@@ -265,7 +286,7 @@ class HttpBundleCache {
|
|
|
265
286
|
url: NormalizedUrl | string,
|
|
266
287
|
ttl: number = HTTP_MODULE_DISTRIBUTED_TTL_SEC,
|
|
267
288
|
): Promise<void> {
|
|
268
|
-
const distributed = await
|
|
289
|
+
const distributed = await resolveDistributedCache();
|
|
269
290
|
if (!distributed) return;
|
|
270
291
|
|
|
271
292
|
const hashStr = typeof hash === "string" ? hash : (hash as unknown as string);
|
|
@@ -305,7 +326,7 @@ class HttpBundleCache {
|
|
|
305
326
|
async getBatchCodes(
|
|
306
327
|
hashes: Array<BundleHash | string>,
|
|
307
328
|
): Promise<Map<string, LocalModuleCode>> {
|
|
308
|
-
const distributed = await
|
|
329
|
+
const distributed = await resolveDistributedCache();
|
|
309
330
|
if (!distributed) return new Map();
|
|
310
331
|
|
|
311
332
|
const results = new Map<string, LocalModuleCode>();
|
|
@@ -357,7 +378,7 @@ class HttpBundleCache {
|
|
|
357
378
|
* @returns Original URL or null
|
|
358
379
|
*/
|
|
359
380
|
async getOriginalUrl(hash: BundleHash | string): Promise<string | null> {
|
|
360
|
-
const distributed = await
|
|
381
|
+
const distributed = await resolveDistributedCache();
|
|
361
382
|
if (!distributed) return null;
|
|
362
383
|
|
|
363
384
|
const hashStr = typeof hash === "string" ? hash : (hash as unknown as string);
|
|
@@ -378,7 +399,7 @@ class HttpBundleCache {
|
|
|
378
399
|
* @returns true if deletion was attempted, false if cache unavailable
|
|
379
400
|
*/
|
|
380
401
|
async deleteCode(hash: BundleHash | string): Promise<boolean> {
|
|
381
|
-
const distributed = await
|
|
402
|
+
const distributed = await resolveDistributedCache();
|
|
382
403
|
if (!distributed) return false;
|
|
383
404
|
|
|
384
405
|
const hashStr = typeof hash === "string" ? hash : (hash as unknown as string);
|
|
@@ -403,7 +424,7 @@ class HttpBundleCache {
|
|
|
403
424
|
* Check if distributed cache is available.
|
|
404
425
|
*/
|
|
405
426
|
async isAvailable(): Promise<boolean> {
|
|
406
|
-
const distributed = await
|
|
427
|
+
const distributed = await resolveDistributedCache();
|
|
407
428
|
return distributed !== null;
|
|
408
429
|
}
|
|
409
430
|
}
|
|
@@ -18,9 +18,9 @@ import {
|
|
|
18
18
|
type FileSystem,
|
|
19
19
|
isNotFoundError,
|
|
20
20
|
} from "../../../../platform/compat/fs.js";
|
|
21
|
-
import { VERSION } from "../../../../utils/version.js";
|
|
22
21
|
import { LOG_PREFIX_MDX_LOADER } from "../constants.js";
|
|
23
22
|
import { LRUCache } from "../../../../utils/lru-wrapper.js";
|
|
23
|
+
import { buildMdxEsmPathCacheKey, MDX_ESM_ALL_FILE_URL_PATTERN_SOURCE } from "../cache-format.js";
|
|
24
24
|
|
|
25
25
|
export type CacheLookupResult =
|
|
26
26
|
| { status: "hit"; path: string }
|
|
@@ -33,8 +33,6 @@ export const verifiedModuleDeps = new LRUCache<string, true>({
|
|
|
33
33
|
maxEntries: MAX_VERIFIED_MODULE_DEPS,
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
-
const FILE_PATH_PATTERN = /file:\/\/([^"'\s]+)/gi;
|
|
37
|
-
|
|
38
36
|
/**
|
|
39
37
|
* Check if cached code has file:// paths from a different environment.
|
|
40
38
|
* Checks both HTTP bundle paths and MDX ESM cache paths.
|
|
@@ -43,7 +41,7 @@ function hasIncompatibleCachePaths(code: string): boolean {
|
|
|
43
41
|
const localCacheBaseDir = getCacheBaseDir();
|
|
44
42
|
const localHttpCacheDir = getHttpBundleCacheDir();
|
|
45
43
|
const localMdxCacheDir = getMdxEsmCacheDir();
|
|
46
|
-
const pattern = new RegExp(
|
|
44
|
+
const pattern = new RegExp(MDX_ESM_ALL_FILE_URL_PATTERN_SOURCE, "gi");
|
|
47
45
|
|
|
48
46
|
let match: RegExpExecArray | null;
|
|
49
47
|
while ((match = pattern.exec(code)) !== null) {
|
|
@@ -87,7 +85,7 @@ function hasIncompatibleCachePaths(code: string): boolean {
|
|
|
87
85
|
*/
|
|
88
86
|
async function findMissingFileDependencies(code: string): Promise<string[]> {
|
|
89
87
|
const localFs = getLocalFs();
|
|
90
|
-
const pattern = new RegExp(
|
|
88
|
+
const pattern = new RegExp(MDX_ESM_ALL_FILE_URL_PATTERN_SOURCE, "gi");
|
|
91
89
|
const missing: string[] = [];
|
|
92
90
|
let match;
|
|
93
91
|
while ((match = pattern.exec(code)) !== null) {
|
|
@@ -335,7 +333,7 @@ function toMdxEsmCacheKey(filePath: string, projectDir?: string): string {
|
|
|
335
333
|
relativePath = relativePath.replace(/^\/+/, "");
|
|
336
334
|
const jsPath = relativePath.replace(/\.(tsx?|jsx|mdx)$/, ".js");
|
|
337
335
|
|
|
338
|
-
return `
|
|
336
|
+
return buildMdxEsmPathCacheKey(`_vf_modules/${jsPath}`);
|
|
339
337
|
}
|
|
340
338
|
|
|
341
339
|
export async function lookupMdxEsmCache(
|
|
@@ -429,9 +427,9 @@ export async function lookupMdxEsmCache(
|
|
|
429
427
|
}
|
|
430
428
|
|
|
431
429
|
// Note: We intentionally skip contentHash validation for MDX-ESM cached files.
|
|
432
|
-
// The MDX-ESM cache uses transformed-code hashes in filenames
|
|
430
|
+
// The MDX-ESM cache uses transformed-code hashes in namespaced filenames,
|
|
433
431
|
// while the SSR loader provides source-code hashes. These will never match.
|
|
434
|
-
// The cache
|
|
432
|
+
// The cache namespace in the key provides sufficient staleness protection,
|
|
435
433
|
// and the file's existence confirms it's a valid transform for this codebase.
|
|
436
434
|
// This allows both loaders to share the same module instance, preventing
|
|
437
435
|
// duplicate React contexts which break hooks like useContext.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { hashCodeHex } from "../../../utils/hash-utils.js";
|
|
2
|
+
import { createCacheNamespace } from "../../../utils/cache-namespace.js";
|
|
3
|
+
import { UNRESOLVED_VF_MODULES_PATTERN } from "./constants.js";
|
|
4
|
+
import { hashString } from "./utils/hash.js";
|
|
5
|
+
|
|
6
|
+
const ALL_FILE_URL_PATTERN_SOURCE = /file:\/\/([^"'\s]+)/.source;
|
|
7
|
+
const MJS_FILE_URL_PATTERN_SOURCE = /file:\/\/([^"'\s]+\.mjs)/.source;
|
|
8
|
+
const CACHE_NAMESPACE_SENTINEL = "__vf_cache_namespace__";
|
|
9
|
+
|
|
10
|
+
function formatMdxEsmTransformCacheKey(
|
|
11
|
+
namespace: string,
|
|
12
|
+
projectId: string,
|
|
13
|
+
normalizedPath: string,
|
|
14
|
+
contentHash: string,
|
|
15
|
+
): string {
|
|
16
|
+
return `${namespace}:${projectId}:${normalizedPath}:${contentHash}:ssr`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function formatMdxEsmPathCacheKey(namespace: string, normalizedPath: string): string {
|
|
20
|
+
return `${namespace}:${normalizedPath}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function formatMdxEsmModuleFileName(namespace: string, contentHash: string): string {
|
|
24
|
+
return `vfmod-${namespace}-${contentHash}.mjs`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function formatMdxJsxCacheFileName(namespace: string, filePath: string): string {
|
|
28
|
+
return `jsx-${namespace}-${hashString(filePath)}.mjs`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function formatFrameworkVfModuleCacheFileName(
|
|
32
|
+
namespace: string,
|
|
33
|
+
pathHash: string,
|
|
34
|
+
envKey: string,
|
|
35
|
+
contentHash: string,
|
|
36
|
+
): string {
|
|
37
|
+
return `vfmod-${namespace}-${pathHash}-${envKey}-${contentHash}.mjs`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function buildMdxEsmCacheSchemaSample() {
|
|
41
|
+
return {
|
|
42
|
+
transformKey: formatMdxEsmTransformCacheKey(
|
|
43
|
+
CACHE_NAMESPACE_SENTINEL,
|
|
44
|
+
"__vf_project__",
|
|
45
|
+
"_vf_modules/pages/index.js",
|
|
46
|
+
"deadbeef",
|
|
47
|
+
),
|
|
48
|
+
pathKey: formatMdxEsmPathCacheKey(CACHE_NAMESPACE_SENTINEL, "_vf_modules/pages/index.js"),
|
|
49
|
+
moduleFile: formatMdxEsmModuleFileName(CACHE_NAMESPACE_SENTINEL, "deadbeef"),
|
|
50
|
+
jsxFile: formatMdxJsxCacheFileName(CACHE_NAMESPACE_SENTINEL, "/tmp/project/Button.tsx"),
|
|
51
|
+
unresolvedVfModulesPattern: UNRESOLVED_VF_MODULES_PATTERN.source,
|
|
52
|
+
allFileUrlPattern: ALL_FILE_URL_PATTERN_SOURCE,
|
|
53
|
+
mjsFileUrlPattern: MJS_FILE_URL_PATTERN_SOURCE,
|
|
54
|
+
sourceHashing: [
|
|
55
|
+
hashString("_vf_modules/pages/index.jsexport default 1;"),
|
|
56
|
+
hashString("/tmp/project/Button.tsx"),
|
|
57
|
+
],
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function buildFrameworkVfModuleCacheSchemaSample() {
|
|
62
|
+
return {
|
|
63
|
+
moduleFile: formatFrameworkVfModuleCacheFileName(
|
|
64
|
+
CACHE_NAMESPACE_SENTINEL,
|
|
65
|
+
hashCodeHex("_vf_modules/_veryfront/react/components/Head.js"),
|
|
66
|
+
hashCodeHex("/app/.cache/veryfront-mdx-esm").slice(0, 8),
|
|
67
|
+
hashCodeHex("export default function Head() {}"),
|
|
68
|
+
),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export const MDX_ESM_ALL_FILE_URL_PATTERN_SOURCE = ALL_FILE_URL_PATTERN_SOURCE;
|
|
73
|
+
export const MDX_ESM_MJS_FILE_URL_PATTERN_SOURCE = MJS_FILE_URL_PATTERN_SOURCE;
|
|
74
|
+
|
|
75
|
+
export const MDX_ESM_CACHE_NAMESPACE = createCacheNamespace(
|
|
76
|
+
"mdx-esm",
|
|
77
|
+
buildMdxEsmCacheSchemaSample(),
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
export const FRAMEWORK_VF_MODULE_CACHE_NAMESPACE = createCacheNamespace(
|
|
81
|
+
"vf-framework",
|
|
82
|
+
buildFrameworkVfModuleCacheSchemaSample(),
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
export function buildMdxEsmTransformCacheKey(
|
|
86
|
+
projectId: string,
|
|
87
|
+
normalizedPath: string,
|
|
88
|
+
contentHash: string,
|
|
89
|
+
): string {
|
|
90
|
+
return formatMdxEsmTransformCacheKey(
|
|
91
|
+
MDX_ESM_CACHE_NAMESPACE,
|
|
92
|
+
projectId,
|
|
93
|
+
normalizedPath,
|
|
94
|
+
contentHash,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function buildMdxEsmPathCacheKey(normalizedPath: string): string {
|
|
99
|
+
return formatMdxEsmPathCacheKey(MDX_ESM_CACHE_NAMESPACE, normalizedPath);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function buildMdxEsmModuleFileName(contentHash: string): string {
|
|
103
|
+
return formatMdxEsmModuleFileName(MDX_ESM_CACHE_NAMESPACE, contentHash);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function buildMdxJsxCacheFileName(filePath: string): string {
|
|
107
|
+
return formatMdxJsxCacheFileName(MDX_ESM_CACHE_NAMESPACE, filePath);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function buildFrameworkVfModuleCacheFileName(
|
|
111
|
+
pathHash: string,
|
|
112
|
+
envKey: string,
|
|
113
|
+
contentHash: string,
|
|
114
|
+
): string {
|
|
115
|
+
return formatFrameworkVfModuleCacheFileName(
|
|
116
|
+
FRAMEWORK_VF_MODULE_CACHE_NAMESPACE,
|
|
117
|
+
pathHash,
|
|
118
|
+
envKey,
|
|
119
|
+
contentHash,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
@@ -11,7 +11,6 @@ import { join } from "../../../platform/compat/path/index.js";
|
|
|
11
11
|
import { rendererLogger as logger } from "../../../utils/index.js";
|
|
12
12
|
import { transformImportsWithMap } from "../../../modules/import-map/index.js";
|
|
13
13
|
import type { ImportMapConfig } from "../../../modules/import-map/index.js";
|
|
14
|
-
import { VERSION } from "../../../utils/version.js";
|
|
15
14
|
import { replaceSpecifiers } from "../../esm/lexer.js";
|
|
16
15
|
import { getLocalReactPaths, isReactSpecifier } from "../../../platform/compat/react-paths.js";
|
|
17
16
|
import {
|
|
@@ -23,7 +22,7 @@ import {
|
|
|
23
22
|
REACT_IMPORT_PATTERN,
|
|
24
23
|
} from "./constants.js";
|
|
25
24
|
import { getLocalFs } from "./cache/index.js";
|
|
26
|
-
import {
|
|
25
|
+
import { buildMdxJsxCacheFileName } from "./cache-format.js";
|
|
27
26
|
import { rewriteDntImports } from "./module-fetcher/index.js";
|
|
28
27
|
import { ensureCachedJsxModulePatched } from "./jsx-cache.js";
|
|
29
28
|
import type { ESMLoaderContext } from "./types.js";
|
|
@@ -128,7 +127,7 @@ export async function transformJsxImports(
|
|
|
128
127
|
const transformResults = await Promise.all(
|
|
129
128
|
importsToProcess.map(async ({ fullMatch, importClause, filePath, ext }) => {
|
|
130
129
|
try {
|
|
131
|
-
const transformedFileName =
|
|
130
|
+
const transformedFileName = buildMdxJsxCacheFileName(filePath);
|
|
132
131
|
const transformedPath = join(esmCacheDir, transformedFileName);
|
|
133
132
|
|
|
134
133
|
try {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @module transforms/mdx/esm-module-loader/module-fetcher/cache-keys
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { buildMdxEsmPathCacheKey, buildMdxEsmTransformCacheKey } from "../cache-format.js";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Build cache key for transformed module.
|
|
@@ -18,9 +18,9 @@ export function getTransformCacheKey(
|
|
|
18
18
|
normalizedPath: string,
|
|
19
19
|
contentHash: string,
|
|
20
20
|
): string {
|
|
21
|
-
return
|
|
21
|
+
return buildMdxEsmTransformCacheKey(projectId, normalizedPath, contentHash);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
export function getVersionedPathCacheKey(normalizedPath: string): string {
|
|
25
|
-
return
|
|
25
|
+
return buildMdxEsmPathCacheKey(normalizedPath);
|
|
26
26
|
}
|
|
@@ -13,6 +13,7 @@ import { FRAMEWORK_ROOT, LOG_PREFIX_MDX_LOADER } from "../constants.js";
|
|
|
13
13
|
import { getLocalFs } from "../cache/index.js";
|
|
14
14
|
import { extractHttpBundlePaths } from "../../../../modules/react-loader/ssr-module-loader/http-bundle-helpers.js";
|
|
15
15
|
import { ensureHttpBundlesExist } from "../../../esm/http-cache.js";
|
|
16
|
+
import { MDX_ESM_MJS_FILE_URL_PATTERN_SOURCE } from "../cache-format.js";
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Check if cached code has file:// paths that are incompatible with this environment.
|
|
@@ -110,7 +111,7 @@ export async function findMissingFileDependenciesInCode(
|
|
|
110
111
|
log: Logger,
|
|
111
112
|
): Promise<string[]> {
|
|
112
113
|
const localFs = getLocalFs();
|
|
113
|
-
const pattern =
|
|
114
|
+
const pattern = new RegExp(MDX_ESM_MJS_FILE_URL_PATTERN_SOURCE, "gi");
|
|
114
115
|
const missing: string[] = [];
|
|
115
116
|
const checked = new Set<string>();
|
|
116
117
|
|
|
@@ -10,11 +10,10 @@
|
|
|
10
10
|
import { join } from "../../../../platform/compat/path/index.js";
|
|
11
11
|
import * as posix from "../../../../../deps/jsr.io/@std/path/1.1.4/posix/mod.js";
|
|
12
12
|
import type { Logger } from "../../../../utils/logger/logger.js";
|
|
13
|
-
import { VERSION } from "../../../../utils/version.js";
|
|
14
13
|
import { LOG_PREFIX_MDX_LOADER } from "../constants.js";
|
|
15
14
|
import { getLocalFs, saveModulePathCache } from "../cache/index.js";
|
|
16
15
|
import { hashString } from "../utils/hash.js";
|
|
17
|
-
import {
|
|
16
|
+
import { buildMdxEsmModuleFileName, buildMdxEsmPathCacheKey } from "../cache-format.js";
|
|
18
17
|
import { hasUnresolvedImports } from "./nested-imports.js";
|
|
19
18
|
import { recordModuleToSession } from "./render-sessions.js";
|
|
20
19
|
|
|
@@ -60,13 +59,14 @@ export async function cacheModule(
|
|
|
60
59
|
}
|
|
61
60
|
|
|
62
61
|
const contentHash = hashString(normalizedPath + moduleCode);
|
|
63
|
-
const cachePath = join(esmCacheDir,
|
|
62
|
+
const cachePath = join(esmCacheDir, buildMdxEsmModuleFileName(contentHash));
|
|
63
|
+
const pathCacheKey = buildMdxEsmPathCacheKey(normalizedPath);
|
|
64
64
|
|
|
65
65
|
const localFs = getLocalFs();
|
|
66
66
|
try {
|
|
67
67
|
const stat = await localFs.stat(cachePath);
|
|
68
68
|
if (stat?.isFile) {
|
|
69
|
-
pathCache.set(
|
|
69
|
+
pathCache.set(pathCacheKey, cachePath);
|
|
70
70
|
log.debug(`${LOG_PREFIX_MDX_LOADER} Content cache hit: ${normalizedPath}`);
|
|
71
71
|
recordModuleToSession(normalizedPath);
|
|
72
72
|
return cachePath;
|
|
@@ -77,7 +77,7 @@ export async function cacheModule(
|
|
|
77
77
|
|
|
78
78
|
await localFs.mkdir(esmCacheDir, { recursive: true });
|
|
79
79
|
await localFs.writeTextFile(cachePath, moduleCode);
|
|
80
|
-
pathCache.set(
|
|
80
|
+
pathCache.set(pathCacheKey, cachePath);
|
|
81
81
|
await saveModulePathCache(esmCacheDir);
|
|
82
82
|
log.debug(`${LOG_PREFIX_MDX_LOADER} Cached vf_module: ${normalizedPath} -> ${cachePath}`);
|
|
83
83
|
|
|
@@ -15,7 +15,6 @@ import { hashCodeHex } from "../../../../utils/hash-utils.js";
|
|
|
15
15
|
import { getHttpBundleCacheDir, getMdxEsmCacheDir } from "../../../../utils/cache-dir.js";
|
|
16
16
|
import { cacheHttpImportsToLocal } from "../../../esm/http-cache.js";
|
|
17
17
|
import { loadImportMap } from "../../../../modules/import-map/index.js";
|
|
18
|
-
import { VERSION } from "../../../../utils/version.js";
|
|
19
18
|
import { buildReactUrl, getReactImportMap } from "../../../import-rewriter/url-builder.js";
|
|
20
19
|
import { findRelativeImports } from "./import-finder.js";
|
|
21
20
|
import { resolveRelativeFrameworkImport, resolveVeryfrontSourcePath } from "./path-resolver.js";
|
|
@@ -30,6 +29,7 @@ import {
|
|
|
30
29
|
transformingFiles,
|
|
31
30
|
veryfrontTransformCache,
|
|
32
31
|
} from "./constants.js";
|
|
32
|
+
import { buildFrameworkVfModuleCacheFileName } from "../../../mdx/esm-module-loader/cache-format.js";
|
|
33
33
|
|
|
34
34
|
const DENO_CONFIG_STUB_CODE = `export default ${JSON.stringify(denoConfig)};`;
|
|
35
35
|
|
|
@@ -46,10 +46,10 @@ export function isCyclePlaceholder(code: string): boolean {
|
|
|
46
46
|
/**
|
|
47
47
|
* Cache transformed framework code and return the file:// path.
|
|
48
48
|
*
|
|
49
|
-
* Cache key format: vfmod-{
|
|
49
|
+
* Cache key format: vfmod-{namespace}-{pathHash}-{envKey}-{contentHash}.mjs
|
|
50
50
|
*
|
|
51
51
|
* Cache invalidation is handled by:
|
|
52
|
-
* -
|
|
52
|
+
* - namespace prefix: Auto-rolls when the framework vfmod cache shape changes
|
|
53
53
|
* - envKey (FRAMEWORK_ROOT hash): Prevents cross-environment contamination
|
|
54
54
|
* (compiled binary vs source have different FRAMEWORK_ROOT values)
|
|
55
55
|
* - contentHash: Content-based invalidation
|
|
@@ -66,7 +66,7 @@ export async function cacheTransformedCode(
|
|
|
66
66
|
const envKey = hashCodeHex(FRAMEWORK_ROOT).slice(0, 8);
|
|
67
67
|
const contentHash = hashCodeHex(transformed);
|
|
68
68
|
const pathHash = hashCodeHex(vfModulePath);
|
|
69
|
-
const fileName =
|
|
69
|
+
const fileName = buildFrameworkVfModuleCacheFileName(pathHash, envKey, contentHash);
|
|
70
70
|
const frameworkCacheDir = join(cacheDir, "framework");
|
|
71
71
|
const cachePath = join(frameworkCacheDir, fileName);
|
|
72
72
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { fnv1aHash } from "./hash-utils.js";
|
|
2
|
+
|
|
3
|
+
type CacheNamespaceValue =
|
|
4
|
+
| string
|
|
5
|
+
| number
|
|
6
|
+
| boolean
|
|
7
|
+
| null
|
|
8
|
+
| readonly CacheNamespaceValue[]
|
|
9
|
+
| { readonly [key: string]: CacheNamespaceValue };
|
|
10
|
+
|
|
11
|
+
function serializeCacheNamespaceValue(value: CacheNamespaceValue): string {
|
|
12
|
+
if (value === null) return "null";
|
|
13
|
+
|
|
14
|
+
if (Array.isArray(value)) {
|
|
15
|
+
return `[${value.map((entry) => serializeCacheNamespaceValue(entry)).join(",")}]`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (typeof value === "object") {
|
|
19
|
+
const entries = Object.entries(value).sort(([left], [right]) => left.localeCompare(right));
|
|
20
|
+
return `{${
|
|
21
|
+
entries
|
|
22
|
+
.map(([key, entry]) => `${JSON.stringify(key)}:${serializeCacheNamespaceValue(entry)}`)
|
|
23
|
+
.join(",")
|
|
24
|
+
}}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return JSON.stringify(value);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Build a deterministic cache namespace from a declarative schema description.
|
|
32
|
+
* Keep the schema close to the cache builders so format changes roll the
|
|
33
|
+
* namespace automatically instead of relying on a manually bumped constant.
|
|
34
|
+
*/
|
|
35
|
+
export function createCacheNamespace(
|
|
36
|
+
scope: string,
|
|
37
|
+
schema: CacheNamespaceValue,
|
|
38
|
+
length = 10,
|
|
39
|
+
): string {
|
|
40
|
+
const serialized = serializeCacheNamespaceValue(schema);
|
|
41
|
+
return `${scope}-${fnv1aHash(serialized).slice(0, length)}`;
|
|
42
|
+
}
|
package/src/src/utils/version.ts
CHANGED