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/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')} ], 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 563c391b02ab..e9cd7205e690 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); } } diff --git a/packages/kit/src/exports/vite/build/build_server.js b/packages/kit/src/exports/vite/build/build_server.js index 1b53cd9fb702..9b116d4234df 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..da3134c313fa 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 } = 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 {{ 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/+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-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 @@ +