Skip to content
Open
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
8 changes: 4 additions & 4 deletions deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 12 additions & 1 deletion packages/fresh/src/dev/dev_build_cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@
Array.from(this.islandModNameToChunk.entries()).map(
async ([name, chunk]) => {
const fileUrl = maybeToFileUrl(chunk.server);
const mod = await import(fileUrl);

Check warning on line 215 in packages/fresh/src/dev/dev_build_cache.ts

View workflow job for this annotation

GitHub Actions / test (v2.x, ubuntu-latest)

unable to analyze dynamic import

if (chunk.browser === null) {
throw new Error(`Unexpected missing browser chunk`);
Expand All @@ -230,7 +230,7 @@
const fileUrl = maybeToFileUrl(file.filePath);
return {
...file,
mod: file.lazy ? () => import(fileUrl) : await import(fileUrl),

Check warning on line 233 in packages/fresh/src/dev/dev_build_cache.ts

View workflow job for this annotation

GitHub Actions / test (v2.x, ubuntu-latest)

unable to analyze dynamic import

Check warning on line 233 in packages/fresh/src/dev/dev_build_cache.ts

View workflow job for this annotation

GitHub Actions / test (v2.x, ubuntu-latest)

unable to analyze dynamic import
};
}));
this.#commands = fsItemsToCommands(files);
Expand Down Expand Up @@ -622,8 +622,19 @@
const root = ${rootPath};
setBuildCache(app, new ProdBuildCache(root, snapshot), "production");

// In dev, plugins may mutate app-level hooks such as the error interceptor
// after this module loads. Keep the fetch target refreshable so the server
// entry can pick up the latest app.handler() when that happens.
let handler = app.handler();

export function refreshHandler() {
handler = app.handler();
}

export default {
fetch: app.handler()
fetch(req, info) {
return handler(req, info);
}
Comment on lines +625 to +637
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • integrationTest("vite dev - source mapped stack traces", async () => {
    const res = await fetch(`${demoServer.address()}/tests/throw`);
    const text = await res.text();
    expect(text).toContain("throw.tsx:5:11");
    });

This fix is for the flaky test of vite dev - source mapped stack traces.

};
`;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-vite/deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"imports": {
"@babel/core": "npm:@babel/core@^7.28.0",
"@babel/preset-react": "npm:@babel/preset-react@^7.27.1",
"@deno/loader": "jsr:@deno/loader@^0.4.0",
"@deno/loader": "jsr:@deno/loader@^0.5.0",
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated the version because we needed the source map generation feature implemented in the PR.

"@marvinh-test/import-json": "jsr:@marvinh-test/import-json@^0.0.1",
"@remix-run/node-fetch-server": "npm:@remix-run/node-fetch-server@^0.12.0",
"@prefresh/vite": "npm:@prefresh/vite@^2.4.8",
Expand Down
39 changes: 35 additions & 4 deletions packages/plugin-vite/src/plugins/deno.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Plugin } from "vite";
import {
type Loader,
MediaType,
type ModuleLoadResponse,
RequestedModuleType,
ResolutionMode,
Workspace,
Expand All @@ -17,6 +18,8 @@ const { default: babelReact } = await import("@babel/preset-react");

const BUILTINS = new Set(builtinModules);

const decoder = new TextDecoder();

interface DenoState {
type: RequestedModuleType;
}
Expand Down Expand Up @@ -180,21 +183,23 @@ export function deno(): Plugin {
return null;
}

const code = new TextDecoder().decode(result.code);
const { code, map } = decodeLoadResult(result);

const maybeJsx = babelTransform({
ssr: this.environment.config.consumer === "server",
media: result.mediaType,
code,
id: specifier,
isDev,
inputSourceMap: map,
});
if (maybeJsx !== null) {
return maybeJsx;
}

return {
code,
map,
};
}

Expand Down Expand Up @@ -224,21 +229,23 @@ export function deno(): Plugin {
return null;
}

const code = new TextDecoder().decode(result.code);
const { code, map } = decodeLoadResult(result);

const maybeJsx = babelTransform({
ssr: this.environment.config.consumer === "server",
media: result.mediaType,
id,
code,
isDev,
inputSourceMap: map,
});
if (maybeJsx) {
return maybeJsx;
}

return {
code,
map,
};
},
transform: {
Expand Down Expand Up @@ -279,10 +286,11 @@ export function deno(): Plugin {
return;
}

const code = new TextDecoder().decode(result.code);
const { code, map } = decodeLoadResult(result);

return {
code,
map,
};
},
},
Expand Down Expand Up @@ -375,13 +383,14 @@ function babelTransform(
code: string;
id: string;
isDev: boolean;
inputSourceMap: babel.TransformOptions["inputSourceMap"];
},
) {
if (!isJsMediaType(options.media)) {
return null;
}

const { ssr, code, id, isDev } = options;
const { ssr, code, id, isDev, inputSourceMap } = options;

const presets: babel.PluginItem[] = [];
if (
Expand All @@ -400,6 +409,7 @@ function babelTransform(
const result = babel.transformSync(code, {
filename: id,
babelrc: false,
inputSourceMap,
sourceMaps: "both",
presets: presets,
plugins: [httpAbsolute(url)],
Expand All @@ -415,3 +425,24 @@ function babelTransform(

return null;
}

function decodeLoadResult(
result: Extract<ModuleLoadResponse, { kind: "module" }>,
): {
code: string;
map: babel.TransformOptions["inputSourceMap"];
} {
const map = result.sourceMap && JSON.parse(decoder.decode(result.sourceMap));
// If we pass a separate sourcemap object to Vite, remove the loader's
// inline data URL so the module only has one sourcemap source of truth.
const code = !map
? decoder.decode(result.code)
: decoder.decode(result.code).replace(
/\r?\n\/\/# sourceMappingURL=data:[^\r\n]+$/,
"",
);
return {
code,
map,
};
}
1 change: 1 addition & 0 deletions packages/plugin-vite/src/plugins/server_entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ ${code}

export function setErrorInterceptor(fn) {
internalErrorIntercept(app, fn);
refreshHandler();
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto for flaky test

}
if (import.meta.hot) import.meta.hot.accept();`;
}
Expand Down
89 changes: 86 additions & 3 deletions packages/plugin-vite/tests/build_test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { createLogger } from "vite";
import type { BabelFileResult } from "@babel/core";
import { expect } from "@std/expect";
import { walk } from "@std/fs/walk";
import {
Expand All @@ -13,6 +15,7 @@ import {
launchProd,
usingEnv,
} from "./test_utils.ts";
import { toPosix } from "fresh/internal-dev";
import * as path from "@std/path";
import { FRESH_CSS_PLACEHOLDER } from "../src/plugins/server_snapshot.ts";

Expand Down Expand Up @@ -592,9 +595,13 @@ integrationTest(
"vite build - custom rollup entryFileNames in server.js",
async () => {
await using res = await buildVite(DEMO_DIR, {
rollupOutput: {
entryFileNames: "[hash].mjs",
chunkFileNames: "[hash].mjs",
build: {
rollupOptions: {
output: {
entryFileNames: "[hash].mjs",
chunkFileNames: "[hash].mjs",
},
},
},
});

Expand Down Expand Up @@ -828,3 +835,79 @@ integrationTest(
);
},
);

integrationTest(
"vite build - ssr sourcemap should be generated collectly",
async () => {
await using tmp = await buildVite(DEMO_DIR, {
environments: {
ssr: {
build: {
sourcemap: true,
},
},
},
});

const serverAssetsDir = path.join(tmp.tmp, "_fresh", "server", "assets");
for await (
const entry of walk(serverAssetsDir, {
exts: [".mjs"],
includeDirs: false,
})
) {
const js = await Deno.readTextFile(entry.path);
const match = js.match(/\/\/# sourceMappingURL=(.+)$/m);
const mapPath = path.join(path.dirname(entry.path), match![1]);
const mapText = await Deno.readTextFile(mapPath);
const map: NonNullable<BabelFileResult["map"]> = JSON.parse(mapText);

// check a specific sourcemap file which contains
// the reference of original source file
if (entry.name.includes("_fresh-route___tests_feed-")) {
expect(
map.sources.some((source) =>
toPosix(source).endsWith("demo/routes/tests/feed.tsx")
),
).toBe(true);
}
}
},
);

// rollup specific test
// https://rollupjs.org/troubleshooting/#warning-sourcemap-is-likely-to-be-incorrect
// this test could be broke if it will migrate to rolldown
integrationTest(
"vite build - ssr sourcemap should be generated without warnings",
async () => {
const warnMsgs = new Set<string>();
const customLogger = createLogger("error");
customLogger.warn = (msg) => {
customLogger.hasWarned = true;
warnMsgs.add(msg);
};
customLogger.warnOnce = (msg) => {
customLogger.hasWarned = true;
warnMsgs.add(msg);
};

await using _ = await buildVite(DEMO_DIR, {
logLevel: "warn",
clearScreen: true,
customLogger,
environments: {
ssr: {
build: {
sourcemap: true,
},
},
},
});

const sourceMapIncorrectMsg = warnMsgs.has(
"[plugin deno] Sourcemap is likely to be incorrect: a plugin (deno) was used to transform files, but didn't generate a sourcemap for the transformation. Consult the plugin documentation for help",
);
expect(sourceMapIncorrectMsg).not.toBeTruthy();
},
);
22 changes: 7 additions & 15 deletions packages/plugin-vite/tests/test_utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createBuilder } from "vite";
import { createBuilder, mergeConfig } from "vite";
import * as path from "@std/path";
import { walk } from "@std/fs/walk";
import { integrationTest, withTmpDir } from "../../fresh/src/test_utils.ts";
Expand Down Expand Up @@ -135,34 +135,23 @@ export async function withDevServer(

export async function buildVite(
fixtureDir: string,
options?: {
base?: string;
rollupOutput?: {
entryFileNames?: string;
chunkFileNames?: string;
assetFileNames?: string;
};
},
config?: Parameters<typeof createBuilder>[0],
) {
const tmp = await withTmpDir({
dir: path.join(import.meta.dirname!, ".."),
prefix: "tmp_vite_",
});

const builder = await createBuilder({
const defaults = {
logLevel: "error",
root: fixtureDir,
base: options?.base,
build: {
emptyOutDir: true,
},
environments: {
ssr: {
build: {
outDir: path.join(tmp.dir, "_fresh", "server"),
rollupOptions: options?.rollupOutput
? { output: options.rollupOutput }
: undefined,
},
},
client: {
Expand All @@ -171,7 +160,10 @@ export async function buildVite(
},
},
},
});
} satisfies Parameters<typeof createBuilder>[0];
const builder = await createBuilder(
mergeConfig(defaults, config ?? {}),
);
await builder.buildApp();

return {
Expand Down
Loading