Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/hot-jokes-refuse.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

fix: error when an adapter tries to delete the SvelteKit build output
26 changes: 24 additions & 2 deletions packages/kit/src/core/adapt/builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { loadEnv } from 'vite';
import * as devalue from 'devalue';
import { createReadStream, createWriteStream, existsSync, statSync } from 'node:fs';
import { extname, resolve, join, dirname, relative } from 'node:path';
import { extname, resolve, join, dirname, relative, isAbsolute } from 'node:path';
import { pipeline } from 'node:stream';
import { promisify, styleText } from 'node:util';
import zlib from 'node:zlib';
Expand Down Expand Up @@ -87,7 +87,19 @@ export function create_builder({

return {
log,
rimraf,
/** @param {string} dir */
rimraf(dir) {
const target = resolve(dir);
const output = resolve(config.kit.outDir, 'output');

if (contains(output, target) || contains(target, output)) {
throw new Error(
`Cannot delete ${target}, because it overlaps with ${output}, which SvelteKit copies the build output from`
);
}

rimraf(target);
},
mkdirp,
copy,

Expand Down Expand Up @@ -263,6 +275,16 @@ export function create_builder({
};
}

/**
* Whether `b` is `a` or lives inside it
* @param {string} a
* @param {string} b
*/
function contains(a, b) {
const path = relative(a, b);
return !path.startsWith('..') && !isAbsolute(path);
}

/**
* @param {string} file
* @param {'gz' | 'br'} format
Expand Down
11 changes: 11 additions & 0 deletions packages/kit/src/core/adapt/builder.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,17 @@ test('copy files', () => {
rmSync(dest, { force: true, recursive: true });
});

test('refuses to delete the build output', () => {
const outDir = join(import.meta.dirname, 'fixtures/basic/.svelte-kit');

// @ts-expect-error - we don't need the whole config for this test
const builder = create_builder({ config: { kit: { outDir } }, route_data: [] });

expect(() => builder.rimraf(join(outDir, 'output/client'))).toThrow('Cannot delete');
expect(() => builder.rimraf(outDir)).toThrow('Cannot delete');
expect(() => builder.rimraf(join(outDir, 'netlify-tmp'))).not.toThrow();
});

test('compress files', async () => {
// @ts-expect-error - we don't need the whole config for this test
const builder = create_builder({
Expand Down
2 changes: 1 addition & 1 deletion packages/kit/src/exports/public.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ type UnpackValidationError<T> =
export interface Builder {
/** Print messages to the console. `log.info` and `log.minor` are silent unless Vite's `logLevel` is `info`. */
log: Logger;
/** Remove `dir` and all its contents. */
/** Remove `dir` and all its contents. Throws if `dir` overlaps with `${config.kit.outDir}/output`, which `writeClient`, `writeServer` and `writePrerendered` copy from. */
rimraf: (dir: string) => void;
/** Create `dir` and any required parent directories. */
mkdirp: (dir: string) => void;
Expand Down
2 changes: 1 addition & 1 deletion packages/kit/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ declare module '@sveltejs/kit' {
export interface Builder {
/** Print messages to the console. `log.info` and `log.minor` are silent unless Vite's `logLevel` is `info`. */
log: Logger;
/** Remove `dir` and all its contents. */
/** Remove `dir` and all its contents. Throws if `dir` overlaps with `${config.kit.outDir}/output`, which `writeClient`, `writeServer` and `writePrerendered` copy from. */
rimraf: (dir: string) => void;
/** Create `dir` and any required parent directories. */
mkdirp: (dir: string) => void;
Expand Down
Loading