From 507311b79821d4d725917a3666db9c9844d9076e Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Thu, 25 Jan 2024 10:04:04 +0100 Subject: [PATCH] fix: strip `/@fs` prefix correctly on Windows when invoking `read()` in dev mode (#11728) Windows absolute paths look like `/@fs/C:/..` - when stripping the fs prefix, we need to remove the additional slash after the fs --- .changeset/fresh-points-smoke.md | 5 +++++ packages/kit/src/exports/vite/dev/index.js | 10 +++++----- packages/kit/src/utils/filesystem.js | 13 +++++++++++++ .../basics/src/routes/read-file/+page.server.js | 8 +++++++- .../basics/src/routes/read-file/+page.svelte | 1 + .../apps/basics/test/cross-platform/test.js | 17 +++++++++++++++++ packages/kit/test/apps/basics/test/test.js | 13 ------------- packages/kit/test/apps/read-file-test.txt | 1 + 8 files changed, 49 insertions(+), 19 deletions(-) create mode 100644 .changeset/fresh-points-smoke.md create mode 100644 packages/kit/test/apps/read-file-test.txt diff --git a/.changeset/fresh-points-smoke.md b/.changeset/fresh-points-smoke.md new file mode 100644 index 000000000000..100667bb8544 --- /dev/null +++ b/.changeset/fresh-points-smoke.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: strip `/@fs` prefix correctly on Windows when invoking `read()` in dev mode diff --git a/packages/kit/src/exports/vite/dev/index.js b/packages/kit/src/exports/vite/dev/index.js index 5981025da4ce..c3616321094e 100644 --- a/packages/kit/src/exports/vite/dev/index.js +++ b/packages/kit/src/exports/vite/dev/index.js @@ -8,7 +8,7 @@ import { isCSSRequest, loadEnv, buildErrorMessage } from 'vite'; import { createReadableStream, getRequest, setResponse } from '../../../exports/node/index.js'; import { installPolyfills } from '../../../exports/node/polyfills.js'; import { coalesce_to_error } from '../../../utils/error.js'; -import { posixify, resolve_entry, to_fs } from '../../../utils/filesystem.js'; +import { from_fs, posixify, resolve_entry, to_fs } from '../../../utils/filesystem.js'; import { load_error_page } from '../../../core/config/index.js'; import { SVELTE_KIT_ASSETS } from '../../../constants.js'; import * as sync from '../../../core/sync/sync.js'; @@ -87,7 +87,7 @@ export async function dev(vite, vite_config, svelte_config) { /** @param {string} id */ async function resolve(id) { - const url = id.startsWith('..') ? `/@fs${path.posix.resolve(id)}` : `/${id}`; + const url = id.startsWith('..') ? to_fs(path.posix.resolve(id)) : `/${id}`; const module = await loud_ssr_load_module(url); @@ -137,8 +137,8 @@ export async function dev(vite, vite_config, svelte_config) { server_assets: new Proxy( {}, { - has: (_, /** @type {string} */ file) => fs.existsSync(file.replace(/^\/@fs/, '')), - get: (_, /** @type {string} */ file) => fs.statSync(file.replace(/^\/@fs/, '')).size + has: (_, /** @type {string} */ file) => fs.existsSync(from_fs(file)), + get: (_, /** @type {string} */ file) => fs.statSync(from_fs(file)).size } ), nodes: manifest_data.nodes.map((node, index) => { @@ -493,7 +493,7 @@ export async function dev(vite, vite_config, svelte_config) { await server.init({ env, - read: (file) => createReadableStream(file.replace(/^\/@fs/, '')) + read: (file) => createReadableStream(from_fs(file)) }); const request = await getRequest({ diff --git a/packages/kit/src/utils/filesystem.js b/packages/kit/src/utils/filesystem.js index d79ead041153..3534295ff011 100644 --- a/packages/kit/src/utils/filesystem.js +++ b/packages/kit/src/utils/filesystem.js @@ -148,6 +148,19 @@ export function to_fs(str) { }${str}`; } +/** + * Removes `/@fs` prefix from given path and posixifies it + * @param {string} str + */ +export function from_fs(str) { + str = posixify(str); + if (!str.startsWith('/@fs')) return str; + + str = str.slice(4); + // Windows/Linux separation - Windows starts with a drive letter, we need to strip the additional / here + return str[2] === ':' && /[A-Z]/.test(str[1]) ? str.slice(1) : str; +} + /** * Given an entry point like [cwd]/src/hooks, returns a filename like [cwd]/src/hooks.js or [cwd]/src/hooks/index.js * @param {string} entry diff --git a/packages/kit/test/apps/basics/src/routes/read-file/+page.server.js b/packages/kit/test/apps/basics/src/routes/read-file/+page.server.js index e6e59d503a8a..1b89fef87df1 100644 --- a/packages/kit/test/apps/basics/src/routes/read-file/+page.server.js +++ b/packages/kit/test/apps/basics/src/routes/read-file/+page.server.js @@ -3,6 +3,11 @@ import { read } from '$app/server'; import auto from './auto.txt'; import url from './url.txt?url'; +const glob = import.meta.glob('../../../../read-file-test.txt', { + as: 'url', + eager: true +}); + export async function load() { if (!dev && !auto.startsWith('data:')) { throw new Error('expected auto.txt to be inlined'); @@ -10,6 +15,7 @@ export async function load() { return { auto: await read(auto).text(), - url: await read(url).text() + url: await read(url).text(), + glob: await read(Object.values(glob)[0]).text() }; } diff --git a/packages/kit/test/apps/basics/src/routes/read-file/+page.svelte b/packages/kit/test/apps/basics/src/routes/read-file/+page.svelte index a23efdda786c..0b8a429cb0b9 100644 --- a/packages/kit/test/apps/basics/src/routes/read-file/+page.svelte +++ b/packages/kit/test/apps/basics/src/routes/read-file/+page.svelte @@ -4,3 +4,4 @@
{data.auto}
{data.url}
+{data.glob}
diff --git a/packages/kit/test/apps/basics/test/cross-platform/test.js b/packages/kit/test/apps/basics/test/cross-platform/test.js index 93b38f17302b..76418c2fa83f 100644 --- a/packages/kit/test/apps/basics/test/cross-platform/test.js +++ b/packages/kit/test/apps/basics/test/cross-platform/test.js @@ -1033,3 +1033,20 @@ test.describe('XSS', () => { ); }); }); + +test.describe('$app/server', () => { + test('can read a file', async ({ page }) => { + await page.goto('/read-file'); + + const auto = await page.textContent('[data-testid="auto"]'); + const url = await page.textContent('[data-testid="url"]'); + const glob = await page.textContent('[data-testid="glob"]'); + + // the emoji is there to check that base64 decoding works correctly + expect(auto.trim()).toBe('Imported without ?url 😎'); + expect(url.trim()).toBe('Imported with ?url 😎'); + expect(glob.trim()).toBe( + 'Imported with url glob from the read-file test in basics. Placed here outside the app folder to force a /@fs prefix 😎' + ); + }); +}); diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js index 9c6d8a5c14f8..170c0eb16ae2 100644 --- a/packages/kit/test/apps/basics/test/test.js +++ b/packages/kit/test/apps/basics/test/test.js @@ -732,19 +732,6 @@ test.describe('$app/paths', () => { }); }); -test.describe('$app/server', () => { - test('can read a file', async ({ page }) => { - await page.goto('/read-file'); - - const auto = await page.textContent('[data-testid="auto"]'); - const url = await page.textContent('[data-testid="url"]'); - - // the emoji is there to check that base64 decoding works correctly - expect(auto.trim()).toBe('Imported without ?url 😎'); - expect(url.trim()).toBe('Imported with ?url 😎'); - }); -}); - test.describe('$app/stores', () => { test('can access page.url', async ({ baseURL, page }) => { await page.goto('/origin'); diff --git a/packages/kit/test/apps/read-file-test.txt b/packages/kit/test/apps/read-file-test.txt new file mode 100644 index 000000000000..7417d9e87b0b --- /dev/null +++ b/packages/kit/test/apps/read-file-test.txt @@ -0,0 +1 @@ +Imported with url glob from the read-file test in basics. Placed here outside the app folder to force a /@fs prefix 😎