diff --git a/packages/sv/src/migrate/migrations/sveltekit-3/index.ts b/packages/sv/src/migrate/migrations/sveltekit-3/index.ts index 47fde3c8b..322a6dffd 100644 --- a/packages/sv/src/migrate/migrations/sveltekit-3/index.ts +++ b/packages/sv/src/migrate/migrations/sveltekit-3/index.ts @@ -6,6 +6,7 @@ import externalRedirects from './tasks/external-redirects.ts'; import packageJson from './tasks/package-json.ts'; import params from './tasks/params.ts'; import paths from './tasks/paths.ts'; +import prepareAiMigration from './tasks/prepare-ai-migration.ts'; import shallowRouting from './tasks/shallow-routing.ts'; import svelteConfig from './tasks/svelte-config.ts'; import tsconfig from './tasks/tsconfig.ts'; @@ -38,5 +39,6 @@ export default defineMigration({ tasks.add(params, { prerequisite: false }); tasks.add(tsconfig, { prerequisite: true }); tasks.add(appState, { prerequisite: false }); + tasks.add(prepareAiMigration, { prerequisite: false }); } }); diff --git a/packages/sv/src/migrate/migrations/sveltekit-3/tasks/package-json.ts b/packages/sv/src/migrate/migrations/sveltekit-3/tasks/package-json.ts index a5cfbb827..13e1c69e4 100644 --- a/packages/sv/src/migrate/migrations/sveltekit-3/tasks/package-json.ts +++ b/packages/sv/src/migrate/migrations/sveltekit-3/tasks/package-json.ts @@ -7,10 +7,10 @@ export default defineMigrationTask({ run: (args) => { // instead of duplicating the logic of the experimental addon, lets just call it. // migrates kit to it's 'next' version. - experimentalAddon.run({ - ...args, - options: { versions: ['kit-3'], features: [] }, - cancel: () => {} - }); + // experimentalAddon.run({ + // ...args, + // options: { versions: ['kit-3'], features: [] }, + // cancel: () => {} + // }); } }); diff --git a/packages/sv/src/migrate/migrations/sveltekit-3/tasks/prepare-ai-migration.ts b/packages/sv/src/migrate/migrations/sveltekit-3/tasks/prepare-ai-migration.ts new file mode 100644 index 000000000..509036b93 --- /dev/null +++ b/packages/sv/src/migrate/migrations/sveltekit-3/tasks/prepare-ai-migration.ts @@ -0,0 +1,169 @@ +import { defineMigrationTask } from '../../../index.ts'; + +const REPORT_PATH = 'MIGRATION_TASKS.md'; +export const GENERATED_MARKER = ''; + +type MigrationTask = { + title: string; + checks: Array<{ + include: string | string[]; + patterns: Array; + }>; + summary: string; + instructions: string; + links: Array<{ label: string; url: string }>; +}; + +const migrationTasks: MigrationTask[] = [ + { + title: 'Remove `experimental.handleRenderingErrors`', + checks: [ + { + include: '**/{svelte,vite}.config.{js,ts,mjs,mts,cjs,cts}', + patterns: ['handleRenderingErrors'] + } + ], + summary: + 'SvelteKit 3 removes the `experimental.handleRenderingErrors` feature flag because rendering errors are now handled this way by default.', + instructions: + 'Remove the `handleRenderingErrors` property from the SvelteKit configuration. If that leaves an empty `experimental` object, remove that object as well. No replacement option is needed — SvelteKit 3 always wraps route components in error boundaries so rendering errors reach the nearest `+error.svelte` page.', + links: [ + { + label: 'SvelteKit PR #16265', + url: 'https://github.com/sveltejs/kit/pull/16265' + } + ] + }, + { + // Temporary: this is already handled by the automatic Kit 3 migration. It exercises a + // migration task with multiple detection rules and can be removed after this task is proven. + title: 'Replace the `$lib` alias with `#lib` and remove `files.lib`', + checks: [ + { + include: '**/*.{js,ts,svelte,mjs,mts,cjs,cts}', + patterns: ['$lib'] + }, + { + include: '**/{svelte,vite}.config.{js,ts,mjs,mts,cjs,cts}', + patterns: [/\bfiles\s*:\s*\{[\s\S]*?\blib\s*:/] + } + ], + summary: + 'SvelteKit 3 removes the built-in `$lib` alias and the `kit.files.lib` configuration. The replacement is a Node subpath import named `#lib`, configured through the package.json `imports` field.', + instructions: + 'Replace remaining `$lib` module references with `#lib`. Add `"#lib": "./src/lib/index.js"` and `"#lib/*": "./src/lib/*"` to package.json `imports`, adjusting `src/lib` if the project used a custom library directory. Remove `files.lib` from the SvelteKit configuration. Check imports, re-exports, dynamic imports, ambient module declarations, tests, and configuration strings rather than assuming every textual match is executable code. If the project intentionally needs to preserve `$lib`, configure it explicitly through SvelteKit `alias` instead of adding the `#lib` imports.', + links: [ + { + label: 'SvelteKit PR #16360', + url: 'https://github.com/sveltejs/kit/pull/16360' + }, + { + label: 'Node.js package imports', + url: 'https://nodejs.org/api/packages.html#subpath-imports' + } + ] + } +]; + +type Finding = { task: MigrationTask; files: string[] }; + +export default defineMigrationTask({ + id: 'prepare-ai-migration', + description: 'Prepare instructions for AI-assisted migration tasks', + run: ({ sv }) => { + const findings: Finding[] = []; + + for (const task of migrationTasks) { + const files = new Set(); + for (const check of task.checks) { + sv.files( + { + include: check.include, + exclude: [REPORT_PATH], + where: (content) => check.patterns.some((pattern) => matches(content, pattern)) + }, + (_content, file) => { + files.add(file); + return false; + } + ); + } + + if (files.size > 0) findings.push({ task, files: [...files].sort() }); + } + + if (findings.length === 0) return; + + sv.file(REPORT_PATH, (content) => { + if (content && !content.includes(GENERATED_MARKER)) { + throw new Error( + `'${REPORT_PATH}' already exists and was not generated by this migration. Move or rename it before rerunning the task.` + ); + } + + return renderReport(findings); + }); + } +}); + +function matches(content: string, pattern: string | RegExp): boolean { + return typeof pattern === 'string' ? content.includes(pattern) : pattern.test(content); +} + +export function renderReport(findings: Finding[]): string { + const sections = findings.map(({ task, files }) => { + const links = task.links.map(({ label, url }) => `- [${label}](${url})`).join('\n'); + const locations = files.map((file) => `- [ ] \`${file}\``).join('\n'); + + return `### ${task.title} + +${task.summary} + +#### What to do + +${task.instructions} + +#### References + +${links} + +#### Files to review + +${locations}`; + }); + + return `${GENERATED_MARKER} +# SvelteKit 3 migration tasks + +## For humans + +Some migrations are uncommon, context-dependent, or disproportionately difficult to automate safely. Modern AI assistants can handle many of these cases effectively when they are given focused context. This file uses that current technology to bridge the gap while keeping the automatic migrations reliable and maintainable. + +The findings are intentionally broad and may include files that do not require changes. Review the AI's work before accepting it. + +## For AI assistants + +Work through the migration tasks below one at a time: + +1. Read the linked documentation or pull request before changing code. +2. Inspect every listed file and its surrounding context. +3. Ignore a finding when the file is not actually affected. +4. Make the smallest necessary change. Do not perform unrelated refactors or introduce unnecessary abstractions. +5. Ask the user when application intent cannot be inferred. +6. Run the project's appropriate checks after editing. + +Also search the project for \`@migration-task\` comments. They may describe additional manual migration work that must be resolved. + +## Migration tasks + +${sections.join('\n\n')} + +## Final verification + +- [ ] Review every migration task and ignore any irrelevant findings. +- [ ] Resolve remaining \`@migration-task\` comments. +- [ ] Run the project's type checker and tests. +- [ ] Build the project successfully. +- [ ] Delete this file when all migration work is complete. +`; +} diff --git a/packages/sv/src/migrate/migrations/sveltekit-3/tests/prepare-ai-migration/MIGRATION_TASKS.snapshot.md b/packages/sv/src/migrate/migrations/sveltekit-3/tests/prepare-ai-migration/MIGRATION_TASKS.snapshot.md new file mode 100644 index 000000000..fb78556a2 --- /dev/null +++ b/packages/sv/src/migrate/migrations/sveltekit-3/tests/prepare-ai-migration/MIGRATION_TASKS.snapshot.md @@ -0,0 +1,67 @@ + +# SvelteKit 3 migration tasks + +## For humans + +Some migrations are uncommon, context-dependent, or disproportionately difficult to automate safely. Modern AI assistants can handle many of these cases effectively when they are given focused context. This file uses that current technology to bridge the gap while keeping the automatic migrations reliable and maintainable. + +The findings are intentionally broad and may include files that do not require changes. Review the AI's work before accepting it. + +## For AI assistants + +Work through the migration tasks below one at a time: + +1. Read the linked documentation or pull request before changing code. +2. Inspect every listed file and its surrounding context. +3. Ignore a finding when the file is not actually affected. +4. Make the smallest necessary change. Do not perform unrelated refactors or introduce unnecessary abstractions. +5. Ask the user when application intent cannot be inferred. +6. Run the project's appropriate checks after editing. + +Also search the project for `@migration-task` comments. They may describe additional manual migration work that must be resolved. + +## Migration tasks + +### Remove `experimental.handleRenderingErrors` + +SvelteKit 3 removes the `experimental.handleRenderingErrors` feature flag because rendering errors are now handled this way by default. + +#### What to do + +Remove the `handleRenderingErrors` property from the SvelteKit configuration. If that leaves an empty `experimental` object, remove that object as well. No replacement option is needed — SvelteKit 3 always wraps route components in error boundaries so rendering errors reach the nearest `+error.svelte` page. + +#### References + +- [SvelteKit PR #16265](https://github.com/sveltejs/kit/pull/16265) + +#### Files to review + +- [ ] `nested/svelte.config.ts` +- [ ] `svelte.config.js` +- [ ] `vite.config.ts` + +### Replace the `$lib` alias with `#lib` and remove `files.lib` + +SvelteKit 3 removes the built-in `$lib` alias and the `kit.files.lib` configuration. The replacement is a Node subpath import named `#lib`, configured through the package.json `imports` field. + +#### What to do + +Replace remaining `$lib` module references with `#lib`. Add `"#lib": "./src/lib/index.js"` and `"#lib/*": "./src/lib/*"` to package.json `imports`, adjusting `src/lib` if the project used a custom library directory. Remove `files.lib` from the SvelteKit configuration. Check imports, re-exports, dynamic imports, ambient module declarations, tests, and configuration strings rather than assuming every textual match is executable code. If the project intentionally needs to preserve `$lib`, configure it explicitly through SvelteKit `alias` instead of adding the `#lib` imports. + +#### References + +- [SvelteKit PR #16360](https://github.com/sveltejs/kit/pull/16360) +- [Node.js package imports](https://nodejs.org/api/packages.html#subpath-imports) + +#### Files to review + +- [ ] `src/routes/+page.svelte` +- [ ] `svelte.config.js` + +## Final verification + +- [ ] Review every migration task and ignore any irrelevant findings. +- [ ] Resolve remaining `@migration-task` comments. +- [ ] Run the project's type checker and tests. +- [ ] Build the project successfully. +- [ ] Delete this file when all migration work is complete. diff --git a/packages/sv/src/migrate/migrations/sveltekit-3/tests/prepare-ai-migration/nested/svelte.config.ts b/packages/sv/src/migrate/migrations/sveltekit-3/tests/prepare-ai-migration/nested/svelte.config.ts new file mode 100644 index 000000000..3a509c3ad --- /dev/null +++ b/packages/sv/src/migrate/migrations/sveltekit-3/tests/prepare-ai-migration/nested/svelte.config.ts @@ -0,0 +1,4 @@ +// This intentionally broad textual match may be irrelevant and should still be reported. +const removedOption = 'handleRenderingErrors'; + +export default { removedOption }; diff --git a/packages/sv/src/migrate/migrations/sveltekit-3/tests/prepare-ai-migration/src/routes/+page.svelte b/packages/sv/src/migrate/migrations/sveltekit-3/tests/prepare-ai-migration/src/routes/+page.svelte new file mode 100644 index 000000000..490d4276a --- /dev/null +++ b/packages/sv/src/migrate/migrations/sveltekit-3/tests/prepare-ai-migration/src/routes/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/packages/sv/src/migrate/migrations/sveltekit-3/tests/prepare-ai-migration/svelte.config.js b/packages/sv/src/migrate/migrations/sveltekit-3/tests/prepare-ai-migration/svelte.config.js new file mode 100644 index 000000000..1fdb05057 --- /dev/null +++ b/packages/sv/src/migrate/migrations/sveltekit-3/tests/prepare-ai-migration/svelte.config.js @@ -0,0 +1,11 @@ +export default { + kit: { + // This directory was previously available through $lib. + files: { + lib: 'src/shared' + }, + experimental: { + handleRenderingErrors: true + } + } +}; diff --git a/packages/sv/src/migrate/migrations/sveltekit-3/tests/prepare-ai-migration/vite.config.ts b/packages/sv/src/migrate/migrations/sveltekit-3/tests/prepare-ai-migration/vite.config.ts new file mode 100644 index 000000000..aaefc96f0 --- /dev/null +++ b/packages/sv/src/migrate/migrations/sveltekit-3/tests/prepare-ai-migration/vite.config.ts @@ -0,0 +1,6 @@ +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [sveltekit({ experimental: { handleRenderingErrors: true } })] +});