diff --git a/packages/plugin-vite/src/plugins/deno.ts b/packages/plugin-vite/src/plugins/deno.ts index 6cefed7e463..2766660fef5 100644 --- a/packages/plugin-vite/src/plugins/deno.ts +++ b/packages/plugin-vite/src/plugins/deno.ts @@ -9,7 +9,7 @@ import { import * as path from "@std/path"; import * as babel from "@babel/core"; import { httpAbsolute } from "./patches/http_absolute.ts"; -import { JS_REG, JSX_REG } from "../utils.ts"; +import { JS_REG, JSX_REG, joinViteQuery, splitViteQuery } from "../utils.ts"; import { builtinModules } from "node:module"; // @ts-ignore Workaround for https://github.com/denoland/deno/issues/30850 @@ -69,6 +69,8 @@ export function deno(): Plugin { : browserLoader; const original = id; + let { specifier, query } = splitViteQuery(id); + id = specifier; let isHttp = false; if (id.startsWith("deno-http::")) { @@ -89,7 +91,11 @@ export function deno(): Plugin { // resolution, with us being in front due to `enforce: "pre"`. // But we still want to ignore everything `vite:resolve` does // because we're kinda replacing that plugin here. - const tmp = await this.resolve(id, importer, options); + const tmp = await this.resolve( + joinViteQuery(id, query), + importer, + options, + ); if (tmp && tmp.resolvedBy !== "vite:resolve") { if (tmp.external && !/^https?:\/\//.test(tmp.id)) { return tmp; @@ -100,7 +106,11 @@ export function deno(): Plugin { return tmp; } - id = tmp.id; + const resolvedTmp = splitViteQuery(tmp.id); + id = resolvedTmp.specifier; + if (resolvedTmp.query) { + query = resolvedTmp.query; + } } // Plugins may return lower cased drive letters on windows @@ -134,7 +144,7 @@ export function deno(): Plugin { if (resolved.startsWith("node:")) { return { - id: resolved, + id: joinViteQuery(resolved, query), external: true, }; } @@ -148,7 +158,7 @@ export function deno(): Plugin { type !== RequestedModuleType.Default || /^(https?|jsr|npm):/.test(resolved) ) { - return toDenoSpecifier(resolved, type); + return joinViteQuery(toDenoSpecifier(resolved, type), query); } if (resolved.startsWith("file://")) { @@ -156,7 +166,7 @@ export function deno(): Plugin { } return { - id: resolved, + id: joinViteQuery(resolved, query), meta: { deno: { type, @@ -202,6 +212,8 @@ export function deno(): Plugin { id = id.slice(1); } + const { specifier: loadSpecifier } = splitViteQuery(id); + const meta = this.getModuleInfo(id)?.meta.deno as | DenoState | undefined @@ -212,12 +224,12 @@ export function deno(): Plugin { // Skip for non-js files like `.css` if ( meta.type === RequestedModuleType.Default && - !JS_REG.test(id) + !JS_REG.test(loadSpecifier) ) { return; } - const url = path.toFileUrl(id); + const url = path.toFileUrl(loadSpecifier); const result = await loader.load(url.href, meta.type); if (result.kind === "external") { diff --git a/packages/plugin-vite/src/utils.ts b/packages/plugin-vite/src/utils.ts index 0a866ef347f..dad14b4891a 100644 --- a/packages/plugin-vite/src/utils.ts +++ b/packages/plugin-vite/src/utils.ts @@ -5,6 +5,27 @@ import type { ImportCheck } from "./plugins/verify_imports.ts"; export const JS_REG = /\.([tj]sx?|[mc]?[tj]s)(\?.*)?$/; export const JSX_REG = /\.[tj]sx(\?.*)?$/; +/** Split a Vite module id into specifier and query/hash (e.g. `?raw`, `?v=`). */ +export function splitViteQuery( + id: string, +): { specifier: string; query: string } { + const hashIdx = id.indexOf("#"); + const base = hashIdx === -1 ? id : id.slice(0, hashIdx); + const hash = hashIdx === -1 ? "" : id.slice(hashIdx); + const qIdx = base.indexOf("?"); + if (qIdx === -1) { + return { specifier: base, query: hash }; + } + return { + specifier: base.slice(0, qIdx), + query: base.slice(qIdx) + hash, + }; +} + +export function joinViteQuery(specifier: string, query: string): string { + return query ? specifier + query : specifier; +} + export function pathWithRoot(fileOrDir: string, root?: string): string { if (path.isAbsolute(fileOrDir)) return fileOrDir; diff --git a/packages/plugin-vite/src/utils_test.ts b/packages/plugin-vite/src/utils_test.ts new file mode 100644 index 00000000000..5c13f61a886 --- /dev/null +++ b/packages/plugin-vite/src/utils_test.ts @@ -0,0 +1,22 @@ +import { expect } from "@std/expect"; +import { joinViteQuery, splitViteQuery } from "./utils.ts"; + +Deno.test("splitViteQuery - keeps specifier and query apart", () => { + expect(splitViteQuery("@/assets/icons/plus.svg?raw")).toEqual({ + specifier: "@/assets/icons/plus.svg", + query: "?raw", + }); + expect(splitViteQuery("/abs/debug.module.js?v=ff8da874")).toEqual({ + specifier: "/abs/debug.module.js", + query: "?v=ff8da874", + }); + expect(splitViteQuery("file:///tmp/foo.js")).toEqual({ + specifier: "file:///tmp/foo.js", + query: "", + }); +}); + +Deno.test("joinViteQuery - reattaches Vite queries", () => { + expect(joinViteQuery("/abs/plus.svg", "?raw")).toBe("/abs/plus.svg?raw"); + expect(joinViteQuery("/abs/plus.svg", "")).toBe("/abs/plus.svg"); +});