Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion deno.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "veryfront",
"version": "0.1.1153",
"version": "0.1.1154",
"license": "Apache-2.0",
"nodeModulesDir": "auto",
"minimumDependencyAge": {
Expand Down
18 changes: 15 additions & 3 deletions src/cache/dependency-graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ export function normalizeSpecifierToPath(
projectDir: string,
): string {
if (specifier.startsWith("@/")) {
return normalizeExtension(`${projectDir}/${specifier.slice(2)}`);
return normalizeDependencyPath(`${projectDir}/${specifier.slice(2)}`, fromFile);
}

if (specifier.startsWith("./") || specifier.startsWith("../")) {
Expand All @@ -223,16 +223,28 @@ export function normalizeSpecifierToPath(
else if (part !== ".") parts.push(part);
}

return normalizeExtension(`/${parts.join("/")}`);
return normalizeDependencyPath(`/${parts.join("/")}`, fromFile);
}

if (specifier.startsWith("file://")) {
return normalizeExtension(specifier.slice(7));
return normalizeDependencyPath(specifier.slice(7), fromFile);
}

return specifier;
}

function normalizeDependencyPath(path: string, fromFile: string): string {
if (
fromFile.endsWith(".src") &&
!path.endsWith(".src") &&
/\.(?:[cm]?[jt]sx?|mdx?)$/.test(path)
) {
return `${path}.src`;
}

return normalizeExtension(path);
}
Comment thread
kojiwakayama marked this conversation as resolved.

function normalizeExtension(path: string): string {
return path.replace(/\.(tsx?|jsx)$/, ".js");
}
Expand Down
29 changes: 29 additions & 0 deletions src/cache/dependency-tracking.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,35 @@ describe("Dependency tracking cache invalidation", () => {
expect(hash1).not.toBe(hash2);
});

it("should hash dependencies stored as compiled framework .src files", async () => {
const entryPath = "/framework/dist/framework-src/react/context/index.tsx.src";
const dependencyPath = "/framework/dist/framework-src/react/runtime/core.ts.src";
const entryCode =
`import { core } from "../runtime/core.ts";\nexport const context = core;\n`;

const filesV1 = new Map<string, string>([
[entryPath, entryCode],
[dependencyPath, `export const core = "v1";\n`],
]);
const filesV2 = new Map<string, string>([
[entryPath, entryCode],
[dependencyPath, `export const core = "v2";\n`],
]);

const hash1 = await computeDepsHash(
entryPath,
createGetContent(filesV1),
"/project",
);
const hash2 = await computeDepsHash(
entryPath,
createGetContent(filesV2),
"/project",
);

expect(hash1).not.toBe(hash2);
});

it("should reuse cached content for overlapping dependency graphs", async () => {
const files = new Map<string, string>([
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,33 @@ describe("extractAllFilePaths", () => {
assertEquals(extractAllFilePaths(code), ["/app/.cache/markdown.tsx"]);
});

it("preserves compiled framework .src cache paths", () => {
const code = [
`import context from "file:///tmp/deno-compile-veryfront/dist/framework-src/react/context/index.tsx.src";`,
`import core from "file:///tmp/deno-compile-veryfront/dist/framework-src/react/runtime/core.ts.src?v=42";`,
].join("\n");

assertEquals(extractAllFilePaths(code), [
"/tmp/deno-compile-veryfront/dist/framework-src/react/context/index.tsx.src",
"/tmp/deno-compile-veryfront/dist/framework-src/react/runtime/core.ts.src",
]);
});

it("does not truncate unsupported file URL suffixes into valid-looking paths", () => {
const code = [
`import source from "file:///tmp/project/Button.ts.source";`,
`import sourceMap from "file:///tmp/project/Button.js.map";`,
].join("\n");

assertEquals(extractAllFilePaths(code), []);
});

it("ignores file URLs with a host component", () => {
const code = `import remote from "file://cache-host/tmp/project/Button.js";`;

assertEquals(extractAllFilePaths(code), []);
});

it("strips query parameters from extracted paths", () => {
const code = `import a from "file:///tmp/project/Button.tsx?v=123";`;
assertEquals(extractAllFilePaths(code), ["/tmp/project/Button.tsx"]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,16 +142,17 @@ export function extractHttpBundlePaths(code: string): Array<{ path: string; hash
*/
export function extractAllFilePaths(code: string): string[] {
// Create regex per call to avoid shared lastIndex state across concurrent calls.
const allFilePathsPattern = /file:\/\/([^"'\s]+\.(?:mjs|js|tsx|ts|jsx)(?:\?[^"'\s]*)?)/gi;
const allFilePathsPattern = /file:\/\/(\/[^"'\s]+)/gi;
const supportedPathPattern = /\.(?:mjs|js|tsx|ts|jsx)(?:\.src)?$/i;

const paths: string[] = [];
const seen = new Set<string>();

let match: RegExpExecArray | null;
while ((match = allFilePathsPattern.exec(code)) !== null) {
const path = match[1]?.replace(/\?.*$/, "");
const path = match[1]?.replace(/[?#].*$/, "");

if (!path || seen.has(path)) continue;
if (!path || !supportedPathPattern.test(path) || seen.has(path)) continue;

seen.add(path);
paths.push(path);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import "#veryfront/schemas/_test-setup.ts";
import { assertEquals, assertNotEquals } from "#veryfront/testing/assert.ts";
import { describe, it } from "#veryfront/testing/bdd.ts";
import { join } from "#veryfront/compat/path/index.ts";
import { join, toFileUrl } from "#veryfront/compat/path/index.ts";
import { denoAdapter } from "#veryfront/platform/adapters/runtime/deno/index.ts";
import {
makeTempDir,
Expand Down Expand Up @@ -157,6 +157,47 @@ describe("SSRCacheManager", { sanitizeResources: false, sanitizeOps: false }, ()
}
});

it("accepts cache entries that reference existing compiled framework sources", async () => {
const projectDir = await makeTempDir({ prefix: "vf-ssr-cache-manager-" });
const embeddedSourcePath = join(
projectDir,
"dist",
"framework-src",
"react",
"runtime",
"core.ts.src",
);

try {
await mkdir(join(projectDir, "dist", "framework-src", "react", "runtime"), {
recursive: true,
});
await writeTextFile(embeddedSourcePath, `export const core = "compiled";`);

const cacheManager = new SSRCacheManager({
projectDir,
projectId: `project-${crypto.randomUUID()}`,
contentSourceId: `preview-${crypto.randomUUID()}`,
adapter: denoAdapter,
dev: true,
});

const isValid = await cacheManager.validateCachedCode(
`import { core } from "${toFileUrl(embeddedSourcePath).href}"; export default core;`,
join(projectDir, "pages", "index.tsx"),
"memory-cache",
{
checkLocalPaths: true,
checkInvalidEsmShPath: false,
},
);

assertEquals(isValid, true);
} finally {
await remove(projectDir, { recursive: true });
}
});

it("rejects redis cache entries with nested legacy .cache TSX imports inside vfmods", async () => {
const projectDir = await makeTempDir({ prefix: "vf-ssr-cache-manager-" });
const projectId = `project-${crypto.randomUUID()}`;
Expand Down
15 changes: 15 additions & 0 deletions src/modules/react-loader/ssr-module-loader/tmp-paths.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,21 @@ describe("modules/react-loader/ssr-module-loader/tmp-paths", () => {
);
});

it("builds hashed JavaScript paths for compiled framework .src files", () => {
const tempPath = buildTempModulePath(
"/cache/mdx/v0-1-1154/project/source",
"/tmp/deno-compile-veryfront/dist/framework-src/react/runtime/core.ts.src",
"/project",
"0.1.1154",
"deadbeefcafebabe",
);

assertEquals(
tempPath,
"/cache/mdx/v0-1-1154/project/source/tmp/deno-compile-veryfront/dist/framework-src/react/runtime/core.v0-1-1154.deadbeef.js",
);
});

it("keeps absolute path structure when file is outside project dir", () => {
const projectHash = hashCodeHex("my/project");
const tempPath = buildTempModulePath(
Expand Down
2 changes: 1 addition & 1 deletion src/modules/react-loader/ssr-module-loader/tmp-paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,6 @@ export function buildTempModulePath(
const hashSuffix = contentHash
? `.v${versionPrefix}.${contentHash.slice(0, 8)}`
: `.v${versionPrefix}`;
const jsPath = relativePath.replace(/\.(tsx?|jsx|mdx)$/, `${hashSuffix}.js`);
const jsPath = relativePath.replace(/\.(tsx?|jsx|mdx)(?:\.src)?$/, `${hashSuffix}.js`);
return join(tmpDir, jsPath);
}
2 changes: 1 addition & 1 deletion src/utils/version-constant.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Keep in sync with deno.json version.
// scripts/release.ts updates this constant during releases.
/** Shared version value. */
export const VERSION = "0.1.1153";
export const VERSION = "0.1.1154";
Loading