Skip to content
Draft
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/tidy-single-sync.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

fix: avoid generating route types twice during svelte-kit sync
6 changes: 5 additions & 1 deletion packages/kit/src/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,11 @@ if (command === 'sync') {
const sveltekit_config = extract_svelte_config(vite_config);

const sync = await import('./core/sync/sync.js');
sync.all_types(sveltekit_config, vite_config.root);
// Resolving a build config already syncs via the compile plugin. Plugin-free configs
// (for example, component libraries) still need the types-only fallback.
if (!vite_config.plugins.some((plugin) => plugin.name === 'vite-plugin-sveltekit-compile')) {
sync.all_types(sveltekit_config, vite_config.root);
}

const entry = resolve_env_entry(sveltekit_config, vite_config.root);
await sync.env(sveltekit_config, entry, vite_config.root, values.mode);
Expand Down
35 changes: 35 additions & 0 deletions packages/kit/test/apps/options/test/test.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,46 @@
import process from 'node:process';
import path from 'node:path';
import { spawnSync } from 'node:child_process';
import { expect } from '@playwright/test';
import { test } from '../../../utils.js';

test.describe.configure({ mode: 'parallel' });

test.skip(!!process.env.PATHS_ASSETS || process.env.PATHS_RELATIVE === 'false');

test('sync generates route types once', ({ javaScriptEnabled }) => {
test.skip(!javaScriptEnabled);

// Every write_all_types invocation writes route_meta_data.json, even when the types are current.
const preload = `
import fs from 'node:fs';
const write = fs.writeFileSync;
fs.writeFileSync = function(file, ...args) {
if (String(file).endsWith('route_meta_data.json')) {
console.log('route types generated');
}
return write.call(this, file, ...args);
};
`;

const result = spawnSync(
process.execPath,
[
'--import',
`data:text/javascript,${encodeURIComponent(preload)}`,
'node_modules/@sveltejs/kit/svelte-kit.js',
'sync',
'--config',
'vite.custom.config.js'
],
{ cwd: path.resolve(import.meta.dirname, '..'), encoding: 'utf-8', timeout: 30_000 }
);

expect(result.error).toBeUndefined();
expect(result.status, result.stderr).toBe(0);
expect(result.stdout.match(/route types generated/g)).toHaveLength(1);
});

test.describe('CSP', () => {
test('blocks script from external site', async ({ page, start_server }) => {
const { port } = await start_server((req, res) => {
Expand Down
108 changes: 108 additions & 0 deletions packages/kit/test/apps/options/unit-test/vite.spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { test, expect } from 'vitest';
import { spawnSync } from 'node:child_process';
import path from 'node:path';
import fs from 'node:fs';
import { createFilter, resolveConfig } from 'vite';
import { bullet_list } from '../../../../src/utils/format.js';

Expand Down Expand Up @@ -78,3 +79,110 @@ test('transformIndexHtml warning for app plugins', { timeout }, () => {
contains: [bullet_list(['app-html-transform'])]
});
});

test('sync generates types without the SvelteKit plugin', { timeout }, () => {
const temp = path.join(cwd, '.test-tmp');
fs.mkdirSync(temp, { recursive: true });
const root = fs.mkdtempSync(path.join(temp, 'sync-'));

try {
fs.writeFileSync(
path.join(root, 'vite.config.js'),
'export default { root: import.meta.dirname };'
);
fs.mkdirSync(path.join(root, 'src/routes'), { recursive: true });
fs.writeFileSync(
path.join(root, 'src/routes/+page.server.js'),
'export const load = () => ({ message: "hello" });'
);

const result = spawnSync(
process.execPath,
[
'node_modules/@sveltejs/kit/svelte-kit.js',
'sync',
'--config',
path.join(root, 'vite.config.js')
],
{ cwd, encoding: 'utf-8', timeout }
);

expect(result.error).toBeUndefined();
expect(result.status, result.stderr).toBe(0);
expect(fs.existsSync(path.join(root, 'node_modules/$app/tsconfig.json'))).toBe(true);
expect(
fs.readFileSync(path.join(root, '.svelte-kit/types/src/routes/$types.d.ts'), 'utf-8')
).toContain('PageServerLoad');
expect(fs.existsSync(path.join(root, 'node_modules/$app/types/index.d.ts'))).toBe(true);
expect(fs.existsSync(path.join(root, 'node_modules/$app/types/env.d.ts'))).toBe(true);
} finally {
fs.rmSync(root, { recursive: true, force: true });
}
});

test.each(['build', 'serve'])(
'sync generates fresh types with compile plugin apply: %s',
{ timeout },
(apply) => {
const temp = path.join(cwd, '.test-tmp');
fs.mkdirSync(temp, { recursive: true });
const root = fs.mkdtempSync(path.join(temp, 'sync-'));

try {
fs.writeFileSync(
path.join(root, 'vite.config.js'),
`
import { sveltekit } from '@sveltejs/kit/vite';
const plugins = await sveltekit({ files: { src: 'source' }, outDir: '.custom-output' });
plugins.find((plugin) => plugin.name === 'vite-plugin-sveltekit-compile').apply = '${apply}';
export default { root: import.meta.dirname, plugins };
`
);
fs.mkdirSync(path.join(root, 'source/routes'), { recursive: true });
fs.writeFileSync(
path.join(root, 'source/app.html'),
'<html><head>%sveltekit.head%</head><body>%sveltekit.body%</body></html>'
);
fs.writeFileSync(
path.join(root, 'source/routes/+page.server.js'),
'export const load = () => ({ message: "hello" });'
);
fs.writeFileSync(
path.join(root, 'source/env.js'),
'export const variables = import.meta.env.MODE === "custom" ? { CUSTOM_MODE: {} } : { OTHER_MODE: {} };'
);

const result = spawnSync(
process.execPath,
[
'node_modules/@sveltejs/kit/svelte-kit.js',
'sync',
'--config',
path.join(root, 'vite.config.js'),
'--mode',
'custom'
],
{ cwd, encoding: 'utf-8', timeout }
);

expect(result.error).toBeUndefined();
expect(result.status, result.stderr).toBe(0);
expect(fs.existsSync(path.join(root, 'node_modules/$app/tsconfig.json'))).toBe(true);
expect(
fs.readFileSync(path.join(root, '.custom-output/types/source/routes/$types.d.ts'), 'utf-8')
).toContain('PageServerLoad');
expect(fs.existsSync(path.join(root, 'node_modules/$app/types/index.d.ts'))).toBe(true);
const env_types = fs.readFileSync(
path.join(root, 'node_modules/$app/types/env.d.ts'),
'utf-8'
);
expect(env_types).toContain('CUSTOM_MODE');
expect(env_types).not.toContain('OTHER_MODE');
expect(fs.existsSync(path.join(root, '.custom-output/generated/build/server.js'))).toBe(
apply === 'build'
);
} finally {
fs.rmSync(root, { recursive: true, force: true });
}
}
);
5 changes: 5 additions & 0 deletions scripts/sync-all.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ for (const directories of [
chdir(cwd);

const vite_config = await load_vite_config();
// The compile plugin has already synced these files while resolving the config.
if (vite_config.plugins.some((plugin) => plugin.name === 'vite-plugin-sveltekit-compile')) {
continue;
}

const sveltekit_config = extract_svelte_config(vite_config);

const manifest_data = create_manifest_data(sveltekit_config, cwd);
Expand Down