Skip to content

Commit

Permalink
[fix] symlink routes (#6796)
Browse files Browse the repository at this point in the history
* [fix] symlink routes

Fixes #6303

* add test

* support symlinks

* lint

* allow symlinked endpoints

* try this

* ugh

Co-authored-by: Rich Harris <hello@rich-harris.dev>
  • Loading branch information
dummdidumm and Rich-Harris committed Sep 20, 2022
1 parent ff056c8 commit 5a54eb4
Show file tree
Hide file tree
Showing 11 changed files with 68 additions and 16 deletions.
5 changes: 5 additions & 0 deletions .changeset/silent-jeans-vanish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

[fix] symlink routes
3 changes: 2 additions & 1 deletion packages/kit/src/core/generate_manifest/index.js
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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')}
],
Expand Down
13 changes: 8 additions & 5 deletions packages/kit/src/core/sync/create_manifest_data/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
}
}
Expand Down
10 changes: 8 additions & 2 deletions packages/kit/src/exports/vite/build/build_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';

/**
Expand Down Expand Up @@ -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?
);
Expand Down
32 changes: 25 additions & 7 deletions packages/kit/src/exports/vite/build/utils.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import fs from 'fs';
import path from 'path';
import * as vite from 'vite';
import { get_aliases } from '../utils.js';

Expand Down Expand Up @@ -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);

Expand All @@ -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 {{
Expand Down
1 change: 1 addition & 0 deletions packages/kit/test/apps/basics/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/test/errors.json
!/.env
/src/routes/routing/symlink-from
2 changes: 1 addition & 1 deletion packages/kit/test/apps/basics/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

<a href="/routing/a">a</a>
<a href="/routing/ambiguous/ok.json" rel="external">ok</a>
<a href="/routing/symlink-from">symlinked</a>
<a href="http://localhost:{$page.url.searchParams.get('port')}">elsewhere</a>
<a href="/static.json">static.json</a>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<h1>symlinked</h1>
7 changes: 7 additions & 0 deletions packages/kit/test/apps/basics/test/setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import fs from 'fs';

if (process.platform !== 'win32') {
process.chdir('src/routes/routing');
fs.rmSync('symlink-from', { recursive: true, force: true });
fs.symlinkSync('symlink-to', 'symlink-from', 'dir');
}
9 changes: 9 additions & 0 deletions packages/kit/test/apps/basics/test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1639,6 +1639,15 @@ test.describe('Routing', () => {
expect(await page.textContent('h1')).toBe('404');
expect(await page.textContent('p')).toBe('This is your custom error page saying: "Not Found"');
});

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');
});
}
});

test.describe('Matchers', () => {
Expand Down

0 comments on commit 5a54eb4

Please sign in to comment.