From 99e03b9f266d4cedb563f07c931f8ed2a2ccae82 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Wed, 14 Sep 2022 11:59:24 +0200 Subject: [PATCH 1/7] [fix] symlink routes Fixes #6303 --- .changeset/silent-jeans-vanish.md | 5 +++++ .../kit/src/core/sync/create_manifest_data/index.js | 13 ++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) create mode 100644 .changeset/silent-jeans-vanish.md diff --git a/.changeset/silent-jeans-vanish.md b/.changeset/silent-jeans-vanish.md new file mode 100644 index 000000000000..44b09d9c159d --- /dev/null +++ b/.changeset/silent-jeans-vanish.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +[fix] symlink routes diff --git a/packages/kit/src/core/sync/create_manifest_data/index.js b/packages/kit/src/core/sync/create_manifest_data/index.js index c386bf1ed6f1..1ed163280aef 100644 --- a/packages/kit/src/core/sync/create_manifest_data/index.js +++ b/packages/kit/src/core/sync/create_manifest_data/index.js @@ -164,13 +164,16 @@ function create_routes_and_nodes(cwd, config, fallback) { const dir = path.join(cwd, routes_base, id); - const files = fs.readdirSync(dir, { - withFileTypes: true - }); + // We can't use withFileTypes because of a NodeJs bug which returns wrong results + // with isDirectory() in case of symlinks: https://github.com/nodejs/node/issues/30646 + const files = fs.readdirSync(dir).map((name) => ({ + is_dir: fs.statSync(path.join(dir, name)).isDirectory(), + name + })); // process files first for (const file of files) { - if (file.isDirectory()) continue; + if (file.is_dir) continue; if (!file.name.startsWith('+')) continue; if (!valid_extensions.find((ext) => file.name.endsWith(ext))) continue; @@ -213,7 +216,7 @@ function create_routes_and_nodes(cwd, config, fallback) { // then handle children for (const file of files) { - if (file.isDirectory()) { + if (file.is_dir) { walk(depth + 1, path.posix.join(id, file.name), file.name, route); } } From 1c9ba3fde39bf6d30e6f0c3a628481011f41a1d0 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 14 Sep 2022 17:53:11 -0400 Subject: [PATCH 2/7] add test --- .../kit/test/apps/basics/src/routes/routing/+page.svelte | 1 + .../kit/test/apps/basics/src/routes/routing/symlink-from | 1 + .../apps/basics/src/routes/routing/symlink-to/+page.svelte | 1 + packages/kit/test/apps/basics/test/test.js | 7 +++++++ 4 files changed, 10 insertions(+) create mode 120000 packages/kit/test/apps/basics/src/routes/routing/symlink-from create mode 100644 packages/kit/test/apps/basics/src/routes/routing/symlink-to/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/routing/+page.svelte b/packages/kit/test/apps/basics/src/routes/routing/+page.svelte index 61f62ec6f5f5..1d300bf63df1 100644 --- a/packages/kit/test/apps/basics/src/routes/routing/+page.svelte +++ b/packages/kit/test/apps/basics/src/routes/routing/+page.svelte @@ -6,6 +6,7 @@ a ok +symlinked elsewhere static.json diff --git a/packages/kit/test/apps/basics/src/routes/routing/symlink-from b/packages/kit/test/apps/basics/src/routes/routing/symlink-from new file mode 120000 index 000000000000..7fcec67beeea --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/symlink-from @@ -0,0 +1 @@ +symlink-to \ No newline at end of file diff --git a/packages/kit/test/apps/basics/src/routes/routing/symlink-to/+page.svelte b/packages/kit/test/apps/basics/src/routes/routing/symlink-to/+page.svelte new file mode 100644 index 000000000000..d80b82ddfd84 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/symlink-to/+page.svelte @@ -0,0 +1 @@ +

symlinked

diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js index 16c81f3d7704..bedac331e1df 100644 --- a/packages/kit/test/apps/basics/test/test.js +++ b/packages/kit/test/apps/basics/test/test.js @@ -1616,6 +1616,13 @@ test.describe('Routing', () => { await page.goto('/static'); expect(await page.textContent('h1')).toBe('hello'); }); + + test('Respects symlinks', async ({ page, clicknav }) => { + await page.goto('/routing'); + await clicknav('[href="/routing/symlink-from"]'); + + expect(await page.textContent('h1')).toBe('symlinked'); + }); }); test.describe('Matchers', () => { From 7d13c23e351796be02822d7e29ad087b1180c53a Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 14 Sep 2022 18:06:44 -0400 Subject: [PATCH 3/7] support symlinks --- .../src/exports/vite/build/build_server.js | 10 ++++-- packages/kit/src/exports/vite/build/utils.js | 32 +++++++++++++++---- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/packages/kit/src/exports/vite/build/build_server.js b/packages/kit/src/exports/vite/build/build_server.js index 77011b11e92c..f9c863d11cf9 100644 --- a/packages/kit/src/exports/vite/build/build_server.js +++ b/packages/kit/src/exports/vite/build/build_server.js @@ -4,7 +4,13 @@ import { mkdirp, posixify, resolve_entry } from '../../../utils/filesystem.js'; import { get_vite_config, merge_vite_configs } from '../utils.js'; import { load_error_page, load_template } from '../../../core/config/index.js'; import { runtime_directory } from '../../../core/utils.js'; -import { create_build, find_deps, get_default_build_config, is_http_method } from './utils.js'; +import { + create_build, + find_deps, + get_default_build_config, + is_http_method, + resolve_symlinks +} from './utils.js'; import { s } from '../../../utils/misc.js'; /** @@ -285,7 +291,7 @@ export async function build_server(options, client) { exports.push( `export const component = async () => (await import('../${ - vite_manifest[node.component].file + resolve_symlinks(vite_manifest, node.component).chunk.file }')).default;`, `export const file = '${entry.file}';` // TODO what is this? ); diff --git a/packages/kit/src/exports/vite/build/utils.js b/packages/kit/src/exports/vite/build/utils.js index 4a0e19e4ac3a..95c5fcf84bd0 100644 --- a/packages/kit/src/exports/vite/build/utils.js +++ b/packages/kit/src/exports/vite/build/utils.js @@ -1,3 +1,5 @@ +import fs from 'fs'; +import path from 'path'; import * as vite from 'vite'; import { get_aliases } from '../utils.js'; @@ -44,14 +46,14 @@ export function find_deps(manifest, entry, add_dynamic_css) { const stylesheets = new Set(); /** - * @param {string} file + * @param {string} current * @param {boolean} add_js */ - function traverse(file, add_js) { - if (seen.has(file)) return; - seen.add(file); + function traverse(current, add_js) { + if (seen.has(current)) return; + seen.add(current); - const chunk = manifest[file]; + const { chunk, file } = resolve_symlinks(manifest, current); if (add_js) imports.add(chunk.file); @@ -68,15 +70,31 @@ export function find_deps(manifest, entry, add_dynamic_css) { } } - traverse(entry, true); + const { chunk, file } = resolve_symlinks(manifest, entry); + + traverse(file, true); return { - file: manifest[entry].file, + file: chunk.file, imports: Array.from(imports), stylesheets: Array.from(stylesheets) }; } +/** + * @param {import('vite').Manifest} manifest + * @param {string} file + */ +export function resolve_symlinks(manifest, file) { + while (!manifest[file]) { + file = path.relative('.', fs.realpathSync(file)); + } + + const chunk = manifest[file]; + + return { chunk, file }; +} + /** * The Vite configuration that we use by default. * @param {{ From 14c5e8c1ac2992c8ed35f9a549f25003e6eb5943 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 14 Sep 2022 18:08:38 -0400 Subject: [PATCH 4/7] lint --- packages/kit/src/exports/vite/build/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kit/src/exports/vite/build/utils.js b/packages/kit/src/exports/vite/build/utils.js index 95c5fcf84bd0..da3134c313fa 100644 --- a/packages/kit/src/exports/vite/build/utils.js +++ b/packages/kit/src/exports/vite/build/utils.js @@ -53,7 +53,7 @@ export function find_deps(manifest, entry, add_dynamic_css) { if (seen.has(current)) return; seen.add(current); - const { chunk, file } = resolve_symlinks(manifest, current); + const { chunk } = resolve_symlinks(manifest, current); if (add_js) imports.add(chunk.file); From 1a4526bdb250543858c94795d7c7a42fb4ab25a2 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 15 Sep 2022 12:49:10 -0400 Subject: [PATCH 5/7] allow symlinked endpoints --- packages/kit/src/core/generate_manifest/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/kit/src/core/generate_manifest/index.js b/packages/kit/src/core/generate_manifest/index.js index d0e35d59fb48..91ec76997636 100644 --- a/packages/kit/src/core/generate_manifest/index.js +++ b/packages/kit/src/core/generate_manifest/index.js @@ -1,5 +1,6 @@ import { s } from '../../utils/misc.js'; import { get_mime_lookup } from '../utils.js'; +import { resolve_symlinks } from '../../exports/vite/build/utils.js'; /** * Generates the data used to write the server-side manifest.js file. This data is used in the Vite @@ -65,7 +66,7 @@ export function generate_manifest({ build_data, relative_path, routes, format = names: ${s(route.names)}, types: ${s(route.types)}, page: ${route.page ? `{ layouts: ${get_nodes(route.page.layouts)}, errors: ${get_nodes(route.page.errors)}, leaf: ${route.page.leaf} }` : 'null'}, - endpoint: ${route.endpoint ? loader(`${relative_path}/${build_data.server.vite_manifest[route.endpoint.file].file}`) : 'null'} + endpoint: ${route.endpoint ? loader(`${relative_path}/${resolve_symlinks(build_data.server.vite_manifest, route.endpoint.file).chunk.file}`) : 'null'} }`; }).filter(Boolean).join(',\n\t\t\t\t')} ], From d91916714f0d0fef0110a30e5e52c76c01218efc Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 19 Sep 2022 16:32:34 -0400 Subject: [PATCH 6/7] try this --- packages/kit/test/apps/basics/.gitignore | 1 + packages/kit/test/apps/basics/package.json | 2 +- .../kit/test/apps/basics/src/routes/routing/symlink-from | 1 - packages/kit/test/apps/basics/test/setup.js | 5 +++++ 4 files changed, 7 insertions(+), 2 deletions(-) delete mode 120000 packages/kit/test/apps/basics/src/routes/routing/symlink-from create mode 100644 packages/kit/test/apps/basics/test/setup.js diff --git a/packages/kit/test/apps/basics/.gitignore b/packages/kit/test/apps/basics/.gitignore index fa2f5a579236..fad4d3e1518d 100644 --- a/packages/kit/test/apps/basics/.gitignore +++ b/packages/kit/test/apps/basics/.gitignore @@ -1,2 +1,3 @@ /test/errors.json !/.env +/src/routes/routing/symlink-from \ No newline at end of file diff --git a/packages/kit/test/apps/basics/package.json b/packages/kit/test/apps/basics/package.json index c3da79b257c6..76e9cf5b7cda 100644 --- a/packages/kit/test/apps/basics/package.json +++ b/packages/kit/test/apps/basics/package.json @@ -7,7 +7,7 @@ "build": "vite build", "preview": "vite preview", "check": "svelte-kit sync && tsc && svelte-check", - "test": "npm run test:dev && npm run test:build", + "test": "node test/setup.js && npm run test:dev && npm run test:build", "test:dev": "rimraf test/errors.json && cross-env DEV=true playwright test", "test:build": "rimraf test/errors.json && playwright test" }, diff --git a/packages/kit/test/apps/basics/src/routes/routing/symlink-from b/packages/kit/test/apps/basics/src/routes/routing/symlink-from deleted file mode 120000 index 7fcec67beeea..000000000000 --- a/packages/kit/test/apps/basics/src/routes/routing/symlink-from +++ /dev/null @@ -1 +0,0 @@ -symlink-to \ No newline at end of file diff --git a/packages/kit/test/apps/basics/test/setup.js b/packages/kit/test/apps/basics/test/setup.js new file mode 100644 index 000000000000..6b97143d8bf2 --- /dev/null +++ b/packages/kit/test/apps/basics/test/setup.js @@ -0,0 +1,5 @@ +import fs from 'fs'; + +process.chdir('src/routes/routing'); +fs.rmSync('symlink-from', { recursive: true, force: true }); +fs.symlinkSync('symlink-to', 'symlink-from', 'dir'); From 98dbe99e8ed210054252ef928c551367012c2d18 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 20 Sep 2022 10:17:26 -0400 Subject: [PATCH 7/7] ugh --- packages/kit/test/apps/basics/test/setup.js | 8 +++++--- packages/kit/test/apps/basics/test/test.js | 12 +++++++----- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/packages/kit/test/apps/basics/test/setup.js b/packages/kit/test/apps/basics/test/setup.js index 6b97143d8bf2..9be580053cd0 100644 --- a/packages/kit/test/apps/basics/test/setup.js +++ b/packages/kit/test/apps/basics/test/setup.js @@ -1,5 +1,7 @@ import fs from 'fs'; -process.chdir('src/routes/routing'); -fs.rmSync('symlink-from', { recursive: true, force: true }); -fs.symlinkSync('symlink-to', 'symlink-from', 'dir'); +if (process.platform !== 'win32') { + process.chdir('src/routes/routing'); + fs.rmSync('symlink-from', { recursive: true, force: true }); + fs.symlinkSync('symlink-to', 'symlink-from', 'dir'); +} diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js index 4ff9073fb399..5542f4efcb2e 100644 --- a/packages/kit/test/apps/basics/test/test.js +++ b/packages/kit/test/apps/basics/test/test.js @@ -1634,12 +1634,14 @@ test.describe('Routing', () => { expect(await page.textContent('h1')).toBe('hello'); }); - test('Respects symlinks', async ({ page, clicknav }) => { - await page.goto('/routing'); - await clicknav('[href="/routing/symlink-from"]'); + if (process.platform !== 'win32') { + test('Respects symlinks', async ({ page, clicknav }) => { + await page.goto('/routing'); + await clicknav('[href="/routing/symlink-from"]'); - expect(await page.textContent('h1')).toBe('symlinked'); - }); + expect(await page.textContent('h1')).toBe('symlinked'); + }); + } }); test.describe('Matchers', () => {