diff --git a/.changeset/config.json b/.changeset/config.json index 246e927640ff..fc418611aeac 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -6,5 +6,5 @@ "access": "public", "baseBranch": "main", "bumpVersionsWithWorkspaceProtocolOnly": true, - "ignore": ["!(@sveltejs/*|create-svelte|svelte-migrate)"] + "ignore": ["!(@sveltejs/*|create-svelte)"] } diff --git a/.prettierrc b/.prettierrc index 2d14308ebeb3..6f5a1610c17f 100644 --- a/.prettierrc +++ b/.prettierrc @@ -27,8 +27,7 @@ "packages/package/test/fixtures/**/expected/**/*", "packages/package/test/watch/expected/**/*", "packages/package/test/watch/package/**/*", - "packages/kit/src/core/postbuild/fixtures/**/*", - "packages/migrate/migrations/routes/*/samples.md" + "packages/kit/src/core/postbuild/fixtures/**/*" ], "options": { "requirePragma": true diff --git a/README.md b/README.md index bdc90a935771..ada896b1fa99 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,6 @@ Web development, streamlined. Read the [documentation](https://svelte.dev/docs/k | [@sveltejs/enhanced-img](packages/enhanced-img) | [Changelog](packages/enhanced-img/CHANGELOG.md) | | [@sveltejs/package](packages/package) | [Changelog](packages/package/CHANGELOG.md) | | [create-svelte](packages/create-svelte) | [Changelog](packages/create-svelte/CHANGELOG.md) | -| [svelte-migrate](packages/migrate) | [Changelog](packages/migrate/CHANGELOG.md) | [Additional adapters](https://sveltesociety.dev/packages?category=sveltekit-adapters) are maintained by the community. diff --git a/packages/migrate/CHANGELOG.md b/packages/migrate/CHANGELOG.md deleted file mode 100644 index 96020c5b63b7..000000000000 --- a/packages/migrate/CHANGELOG.md +++ /dev/null @@ -1,345 +0,0 @@ -# svelte-migrate - -## 1.6.8 -### Patch Changes - - -- fix: prevent duplicate imports ([#12931](https://github.com/sveltejs/kit/pull/12931)) - -## 1.6.7 -### Patch Changes - - -- fix: prefer TS in unclear migration situations if `tsconfig.json` found ([#12881](https://github.com/sveltejs/kit/pull/12881)) - -## 1.6.6 -### Patch Changes - - -- docs: update URLs for new svelte.dev site ([#12857](https://github.com/sveltejs/kit/pull/12857)) - -## 1.6.5 -### Patch Changes - - -- docs: demonstrate sv migrate over prior commands ([#12840](https://github.com/sveltejs/kit/pull/12840)) - - -- fix: bump enhanced-img version to avoid peer dep warning ([#12852](https://github.com/sveltejs/kit/pull/12852)) - -## 1.6.4 -### Patch Changes - - -- fix: migrate `svelte` and `vite-plugin-svelte` to latest ([#12838](https://github.com/sveltejs/kit/pull/12838)) - -## 1.6.3 -### Patch Changes - - -- chore: add `svelte-eslint-parser` to list of migratable dependencies ([#12828](https://github.com/sveltejs/kit/pull/12828)) - -## 1.6.2 -### Patch Changes - - -- chore: upgrade to ts-morph 24 ([#12781](https://github.com/sveltejs/kit/pull/12781)) - -## 1.6.1 -### Patch Changes - - -- chore: upgrade to ts-morph 23 ([#12607](https://github.com/sveltejs/kit/pull/12607)) - -## 1.6.0 -### Minor Changes - - -- feat: pass filename to `migrate` to allow for `svelte:self` migration ([#12749](https://github.com/sveltejs/kit/pull/12749)) - - -### Patch Changes - - -- fix: prompt SvelteKit 2 migration during Svelte 5 migration if necessary ([#12748](https://github.com/sveltejs/kit/pull/12748)) - -## 1.5.1 -### Patch Changes - - -- fix: use `next` versions for `svelte` and `vite-plugin-svelte` ([#12729](https://github.com/sveltejs/kit/pull/12729)) - -## 1.5.0 -### Minor Changes - - -- feat: add Svelte 5 migration ([#12519](https://github.com/sveltejs/kit/pull/12519)) - -## 1.4.5 -### Patch Changes - - -- chore: configure provenance in a simpler manner ([#12570](https://github.com/sveltejs/kit/pull/12570)) - -## 1.4.4 -### Patch Changes - - -- chore: package provenance ([#12567](https://github.com/sveltejs/kit/pull/12567)) - -## 1.4.3 - -### Patch Changes - -- chore: add keywords for discovery in npm search ([#12330](https://github.com/sveltejs/kit/pull/12330)) - -## 1.4.2 - -### Patch Changes - -- fix: bump import-meta-resolve to remove deprecation warnings ([#12240](https://github.com/sveltejs/kit/pull/12240)) - -## 1.4.1 - -### Patch Changes - -- fix: continue traversing the children of non-self-closing elements ([#12175](https://github.com/sveltejs/kit/pull/12175)) - -## 1.4.0 - -### Minor Changes - -- feat: add self-closing-tags migration ([#12128](https://github.com/sveltejs/kit/pull/12128)) - -## 1.3.8 - -### Patch Changes - -- chore(deps): update dependency ts-morph to v22 ([`4447269e979f2b5be18e0fded0b5843a6258542d`](https://github.com/sveltejs/kit/commit/4447269e979f2b5be18e0fded0b5843a6258542d)) - -## 1.3.7 - -### Patch Changes - -- fix: don't downgrade versions when bumping dependencies ([#11716](https://github.com/sveltejs/kit/pull/11716)) - -## 1.3.6 - -### Patch Changes - -- fix: correct link to docs ([#11407](https://github.com/sveltejs/kit/pull/11407)) - -## 1.3.5 - -### Patch Changes - -- chore: update primary branch from master to main ([`47779436c5f6c4d50011d0ef8b2709a07c0fec5d`](https://github.com/sveltejs/kit/commit/47779436c5f6c4d50011d0ef8b2709a07c0fec5d)) - -## 1.3.4 - -### Patch Changes - -- suggest running migrate command with latest if migration does not exist ([#11362](https://github.com/sveltejs/kit/pull/11362)) - -## 1.3.3 - -### Patch Changes - -- chore: insert package at sorted position ([#11332](https://github.com/sveltejs/kit/pull/11332)) - -- fix: adjust cookie migration logic, note installation ([#11331](https://github.com/sveltejs/kit/pull/11331)) - -## 1.3.2 - -### Patch Changes - -- fix: handle jsconfig.json ([#11325](https://github.com/sveltejs/kit/pull/11325)) - -## 1.3.1 - -### Patch Changes - -- chore: fix broken migration links ([#11320](https://github.com/sveltejs/kit/pull/11320)) - -## 1.3.0 - -### Minor Changes - -- feat: add sveltekit v2 migration ([#11294](https://github.com/sveltejs/kit/pull/11294)) - -## 1.2.8 - -### Patch Changes - -- chore(deps): update dependency ts-morph to v21 ([#11181](https://github.com/sveltejs/kit/pull/11181)) - -## 1.2.7 - -### Patch Changes - -- chore(deps): update dependency ts-morph to v20 ([#10766](https://github.com/sveltejs/kit/pull/10766)) - -## 1.2.6 - -### Patch Changes - -- fix: do not downgrade versions ([#10352](https://github.com/sveltejs/kit/pull/10352)) - -## 1.2.5 - -### Patch Changes - -- fix: note old eslint plugin deprecation ([#10319](https://github.com/sveltejs/kit/pull/10319)) - -## 1.2.4 - -### Patch Changes - -- fix: ensure glob finds all files in folders ([#10230](https://github.com/sveltejs/kit/pull/10230)) - -## 1.2.3 - -### Patch Changes - -- fix: handle missing fields in migrate script ([#10221](https://github.com/sveltejs/kit/pull/10221)) - -## 1.2.2 - -### Patch Changes - -- fix: finalize svelte-4 migration ([#10195](https://github.com/sveltejs/kit/pull/10195)) - -- fix: changed `index` to `index.d.ts` in `typesVersions` ([#10180](https://github.com/sveltejs/kit/pull/10180)) - -## 1.2.1 - -### Patch Changes - -- docs: update readme ([#10066](https://github.com/sveltejs/kit/pull/10066)) - -## 1.2.0 - -### Minor Changes - -- feat: add Svelte 4 migration ([#9729](https://github.com/sveltejs/kit/pull/9729)) - -## 1.1.3 - -### Patch Changes - -- fix: include index in typesVersions because it's always matched ([#9147](https://github.com/sveltejs/kit/pull/9147)) - -## 1.1.2 - -### Patch Changes - -- fix: update existing exports with prepended outdir ([#9133](https://github.com/sveltejs/kit/pull/9133)) - -- fix: use typesVersions to wire up deep imports ([#9133](https://github.com/sveltejs/kit/pull/9133)) - -## 1.1.1 - -### Patch Changes - -- fix: include utils in migrate's published files ([#9085](https://github.com/sveltejs/kit/pull/9085)) - -## 1.1.0 - -### Minor Changes - -- feat: add `@sveltejs/package` migration (v1->v2) ([#8922](https://github.com/sveltejs/kit/pull/8922)) - -## 1.0.1 - -### Patch Changes - -- fix: correctly check for old load props ([#8537](https://github.com/sveltejs/kit/pull/8537)) - -## 1.0.0 - -### Major Changes - -First major release, see below for the history of changes that lead up to this. -Starting from now all releases follow semver and changes will be listed as Major/Minor/Patch - -## 1.0.0-next.13 - -### Patch Changes - -- fix: more robust uppercase migration ([#7033](https://github.com/sveltejs/kit/pull/7033)) - -## 1.0.0-next.12 - -### Patch Changes - -- feat: do uppercase http verbs migration on the fly ([#6371](https://github.com/sveltejs/kit/pull/6371)) - -## 1.0.0-next.11 - -### Patch Changes - -- fix: git mv files correctly when they contain \$ characters ([#6129](https://github.com/sveltejs/kit/pull/6129)) - -## 1.0.0-next.10 - -### Patch Changes - -- Revert change to suggest props destructuring ([#6099](https://github.com/sveltejs/kit/pull/6099)) - -## 1.0.0-next.9 - -### Patch Changes - -- Handle Error without message, handle status 200, handle missing body ([#6096](https://github.com/sveltejs/kit/pull/6096)) - -## 1.0.0-next.8 - -### Patch Changes - -- Suggest props destructuring if possible ([#6069](https://github.com/sveltejs/kit/pull/6069)) -- Fix typo in migration task ([#6070](https://github.com/sveltejs/kit/pull/6070)) - -## 1.0.0-next.7 - -### Patch Changes - -- Migrate type comments on arrow functions ([#5933](https://github.com/sveltejs/kit/pull/5933)) -- Use LayoutLoad inside +layout.js files ([#5931](https://github.com/sveltejs/kit/pull/5931)) - -## 1.0.0-next.6 - -### Patch Changes - -- Create `.ts` files from `${whitespace}`; - } - - if (/lang(?:uage)?=(['"])(ts|typescript)\1/.test(attrs)) { - ext = '.ts'; - } - - module = dedent(contents.replace(/^\n/, '')); - - const declared = find_declarations(contents); - const delete_var = (/** @type {string } */ key) => { - const declaration = declared?.get(key); - if (declaration && !declaration.import) { - declared?.delete(key); - } - }; - delete_var('load'); - delete_var('router'); - delete_var('hydrate'); - delete_var('prerender'); - const delete_kit_type = (/** @type {string } */ key) => { - const declaration = declared?.get(key); - if ( - declaration && - declaration.import?.type_only && - declaration.import.from === '@sveltejs/kit' && - !new RegExp(`\\W${key}\\W`).test(except_str(content, match)) - ) { - declared?.delete(key); - } - }; - delete_kit_type('Load'); - delete_kit_type('LoadEvent'); - delete_kit_type('LoadOutput'); - - if (!declared || declared.size > 0) { - const body = `\n${indent}${error( - 'Check code was safely removed', - TASKS.PAGE_MODULE_CTX - )}\n${comment(contents, indent)}`; - - return `${body}${whitespace}`; - } - - // nothing was declared here, we can safely remove the script - return ''; - } - - if (!is_error && /export let [\w]+[^"`'\w\s]/.test(contents)) { - contents = `\n${indent}${error('Add data prop', TASKS.PAGE_DATA_PROP)}\n${contents}`; - // Possible TODO: migrate props to data.prop, or suggest $: ({propX, propY, ...} = data); - } - - return `${contents}${whitespace}`; - } - ); - - return { module, main, ext }; -} - -/** @param {string} content */ -function find_declarations(content) { - const file = parse(content); - if (!file) return; - - /** @type {Map} */ - const declared = new Map(); - /** - * @param {string} name - * @param {{from: string, type_only: boolean}} [import_def] - */ - function add(name, import_def) { - declared.set(name, { name, import: import_def }); - } - - for (const statement of file.ast.statements) { - if (ts.isImportDeclaration(statement) && statement.importClause) { - const type_only = statement.importClause.isTypeOnly; - const from = ts.isStringLiteral(statement.moduleSpecifier) - ? statement.moduleSpecifier.text - : ''; - - if (statement.importClause.name) { - add(statement.importClause.name.text, { from, type_only }); - } - - const bindings = statement.importClause.namedBindings; - - if (bindings) { - if (ts.isNamespaceImport(bindings)) { - add(bindings.name.text, { from, type_only }); - } else { - for (const binding of bindings.elements) { - add(binding.name.text, { from, type_only: type_only || binding.isTypeOnly }); - } - } - } - } else if (ts.isVariableStatement(statement)) { - for (const declaration of statement.declarationList.declarations) { - if (ts.isIdentifier(declaration.name)) { - add(declaration.name.text); - } else { - return; // bail out if it's not a simple variable - } - } - } else if (ts.isFunctionDeclaration(statement) || ts.isClassDeclaration(statement)) { - if (statement.name && ts.isIdentifier(statement.name)) { - add(statement.name.text); - } - } else if (ts.isExportDeclaration(statement) && !statement.exportClause) { - return; // export * from '..' -> bail - } - } - - return declared; -} diff --git a/packages/migrate/migrations/routes/migrate_scripts/index.spec.js b/packages/migrate/migrations/routes/migrate_scripts/index.spec.js deleted file mode 100644 index a52fc7128ff1..000000000000 --- a/packages/migrate/migrations/routes/migrate_scripts/index.spec.js +++ /dev/null @@ -1,14 +0,0 @@ -import { assert, test } from 'vitest'; -import { migrate_scripts } from './index.js'; -import { read_samples } from '../../../utils.js'; - -for (const sample of read_samples(new URL('./samples.md', import.meta.url))) { - test(sample.description, () => { - const actual = migrate_scripts( - sample.before, - sample.description.includes('error'), - sample.description.includes('moved') - ); - assert.equal(actual.main, sample.after); - }); -} diff --git a/packages/migrate/migrations/routes/migrate_scripts/samples.md b/packages/migrate/migrations/routes/migrate_scripts/samples.md deleted file mode 100644 index 6f57fb1f4c6d..000000000000 --- a/packages/migrate/migrations/routes/migrate_scripts/samples.md +++ /dev/null @@ -1,247 +0,0 @@ -## No module context, no page exports - -```svelte before - - - - -

{sry}

-``` - -```svelte after - - - - -

{sry}

-``` - -## Module context that can be removed - -```svelte before - - - -``` - -```svelte after - -``` - -## Module context with moved imports - -```svelte before - - - - -{sry} -``` - -```svelte after - - - - -{sry} -``` - -## Module context with type imports only - -```svelte before - -``` - -```svelte after -``` - -## Module context with type imports only but used in instance script - -```svelte before - - - -``` - -```svelte after - - - -``` - -## Module context with export * from '..' - -```svelte before - -``` - -```svelte after - -``` - -## Module context with named imports - -```svelte before - - - -``` - -```svelte after - - - -``` - -## Module context with named imports that have same name as a load option - -```svelte before - - - -``` - -```svelte after - - - -``` diff --git a/packages/migrate/migrations/routes/migrate_server/index.js b/packages/migrate/migrations/routes/migrate_server/index.js deleted file mode 100644 index d9092d554651..000000000000 --- a/packages/migrate/migrations/routes/migrate_server/index.js +++ /dev/null @@ -1,190 +0,0 @@ -import ts from 'typescript'; -import { - automigration, - uppercase_migration, - error, - get_function_node, - get_object_nodes, - is_new, - is_string_like, - manual_return_migration, - parse, - rewrite_returns, - unwrap -} from '../utils.js'; -import * as TASKS from '../tasks.js'; -import { dedent, guess_indent, indent_at_line } from '../../../utils.js'; - -const give_up = `${error('Update +server.js', TASKS.STANDALONE_ENDPOINT)}\n\n`; - -/** - * @param {string} content - * @returns {string} - */ -export function migrate_server(content) { - const file = parse(content); - if (!file) return give_up + content; - - const indent = guess_indent(content); - - const methods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'].filter((name) => - file.exports.map.has(name) - ); - - // If user didn't do the uppercase verbs migration yet, do it here on the fly. - const uppercased = uppercase_migration(methods, file); - if (!uppercased) { - return give_up + content; - } else if (uppercased !== content) { - return migrate_server(uppercased); - } - - const unmigrated = new Set(methods); - - /** @type {Map} */ - const imports = new Map(); - - for (const statement of file.ast.statements) { - for (const method of methods) { - const fn = get_function_node(statement, /** @type{string} */ (file.exports.map.get(method))); - if (fn?.body) { - rewrite_returns(fn.body, (expr, node) => { - // leave `() => new Response(...)` alone - if (is_new(expr, 'Response')) return; - - const value = unwrap(expr); - const nodes = ts.isObjectLiteralExpression(value) && get_object_nodes(value); - - if (nodes) { - const body_is_object_literal = nodes.body && ts.isObjectLiteralExpression(nodes.body); - - if (body_is_object_literal || (nodes.body && ts.isIdentifier(nodes.body))) { - let result; - - let name = 'json'; - let i = 1; - while (content.includes(name)) name = `json$${i++}`; - - imports.set('json', name); - - const body = dedent(nodes.body.getText()); - - if (nodes.headers || (nodes.status && nodes.status.getText() !== '200')) { - const start = indent_at_line(content, expr.pos); - const properties = []; - - if (nodes.status && nodes.status.getText() !== '200') { - properties.push(`status: ${nodes.status.getText()}`); - } - - if (nodes.headers) { - properties.push(`headers: ${nodes.headers.getText()}`); - } - - const ws = `\n${start}`; - const init = `{${ws}${indent}${properties.join(`,${ws}${indent}`)}${ws}}`; - - result = `${name}(${body}, ${init})`; - } else { - result = `${name}(${body})`; - } - - if (body_is_object_literal) { - automigration(expr, file.code, result); - } else { - manual_return_migration( - node || fn, - file.code, - TASKS.STANDALONE_ENDPOINT, - `return ${result};` - ); - } - - return; - } - - let safe_headers = !nodes.headers || !ts.isObjectLiteralExpression(nodes.headers); - if (nodes.headers && ts.isObjectLiteralExpression(nodes.headers)) { - // if `headers` is an object literal, and it either doesn't contain - // `set-cookie` or `set-cookie` is a string, then the headers - // are safe to use in a `Response` - const set_cookie_value = nodes.headers.properties.find((prop) => { - return ( - ts.isPropertyAssignment(prop) && - ts.isStringLiteral(prop.name) && - /set-cookie/i.test(prop.name.text) - ); - }); - - if (!set_cookie_value || is_string_like(set_cookie_value)) { - safe_headers = true; - } - } - - const safe_body = - !nodes.body || - is_string_like(nodes.body) || - (ts.isCallExpression(nodes.body) && - nodes.body.expression.getText() === 'JSON.stringify'); - - if (safe_headers) { - const status = nodes.status ? nodes.status.getText() : '200'; - const headers = nodes.headers?.getText(); - const body = dedent(nodes.body?.getText() || 'undefined'); - - const multiline = /\n/.test(headers); - - const init = [ - status !== '200' && `status: ${status}`, - headers && `headers: ${headers}` - ].filter(Boolean); - - const indent = indent_at_line(content, expr.getStart()); - const end_whitespace = multiline ? `\n${indent}` : ' '; - const join_whitespace = multiline ? end_whitespace + guess_indent(content) : ' '; - - const response = - init.length > 0 - ? `new Response(${body}, {${join_whitespace}${init.join( - `,${join_whitespace}` - )}${end_whitespace}})` - : `new Response(${body})`; - - if (safe_body) { - automigration(expr, file.code, response); - } else { - manual_return_migration( - node || fn, - file.code, - TASKS.STANDALONE_ENDPOINT, - `return ${response};` - ); - } - - return; - } - } - - manual_return_migration(node || fn, file.code, TASKS.STANDALONE_ENDPOINT); - }); - - unmigrated.delete(method); - } - } - } - - if (imports.size) { - const has_imports = file.ast.statements.some((statement) => ts.isImportDeclaration(statement)); - const specifiers = Array.from(imports).map(([name, local]) => - name === local ? name : `${name} as ${local}` - ); - const declaration = `import { ${specifiers.join(', ')} } from '@sveltejs/kit';`; - file.code.prependLeft(0, declaration + (has_imports ? '\n' : '\n\n')); - } - - if (unmigrated.size) { - return give_up + file.code.toString(); - } - - return file.code.toString(); -} diff --git a/packages/migrate/migrations/routes/migrate_server/index.spec.js b/packages/migrate/migrations/routes/migrate_server/index.spec.js deleted file mode 100644 index 8ad424534ef0..000000000000 --- a/packages/migrate/migrations/routes/migrate_server/index.spec.js +++ /dev/null @@ -1,10 +0,0 @@ -import { assert, test } from 'vitest'; -import { migrate_server } from './index.js'; -import { read_samples } from '../../../utils.js'; - -for (const sample of read_samples(new URL('./samples.md', import.meta.url))) { - test(sample.description, () => { - const actual = migrate_server(sample.before); - assert.equal(actual, sample.after); - }); -} diff --git a/packages/migrate/migrations/routes/migrate_server/samples.md b/packages/migrate/migrations/routes/migrate_server/samples.md deleted file mode 100644 index 3bc08662552f..000000000000 --- a/packages/migrate/migrations/routes/migrate_server/samples.md +++ /dev/null @@ -1,212 +0,0 @@ -## A GET function that returns a JSON object - -```js before -export function GET() { - return { - body: { - a: 1 - } - }; -} -``` - -```js after -import { json } from '@sveltejs/kit'; - -export function GET() { - return json({ - a: 1 - }); -} -``` - -## A GET function that returns a JSON object and already specifies a 'json' identifier - -```js before -export function GET() { - const json = 'shadow'; - - return { - body: { - a: 1 - } - }; -} -``` - -```js after -import { json as json$1 } from '@sveltejs/kit'; - -export function GET() { - const json = 'shadow'; - - return json$1({ - a: 1 - }); -} -``` - -## A GET function that returns a JSON object with custom headers - -```js before -export function GET() { - return { - headers: { - 'x-foo': '123' - }, - body: { - a: 1 - } - }; -} -``` - -```js after -import { json } from '@sveltejs/kit'; - -export function GET() { - return json({ - a: 1 - }, { - headers: { - 'x-foo': '123' - } - }); -} -``` - -## A GET arrow function that returns a JSON object - -```js before -export const GET = () => ({ - body: { - a: 1 - } -}); -``` - -```js after -import { json } from '@sveltejs/kit'; - -export const GET = () => json({ - a: 1 -}); -``` - -## GET returns we can't migrate - -```js before -export function GET() { - if (a) { - return { - body - }; - } else if (b) { - return { - body: new ReadableStream(), - headers: { - 'content-type': 'octasomething' - } - } - } else if (c) { - return { - body: 'string', - headers: { - 'x-foo': 'bar' - } - } - } -} -``` - -```js after -import { json } from '@sveltejs/kit'; - -export function GET() { - if (a) { - throw new Error("@migration task: Migrate this return statement (https://github.com/sveltejs/kit/discussions/5774#discussioncomment-3292701)"); - // Suggestion (check for correctness before using): - // return json(body); - return { - body - }; - } else if (b) { - throw new Error("@migration task: Migrate this return statement (https://github.com/sveltejs/kit/discussions/5774#discussioncomment-3292701)"); - // Suggestion (check for correctness before using): - // return new Response(new ReadableStream(), { - // headers: { - // 'content-type': 'octasomething' - // } - // }); - return { - body: new ReadableStream(), - headers: { - 'content-type': 'octasomething' - } - } - } else if (c) { - return new Response('string', { - headers: { - 'x-foo': 'bar' - } - }) - } -} -``` - -## A function that returns a Response - -```js before -export const GET = () => new Response('text'); -``` - -```js after -export const GET = () => new Response('text'); -``` - -## A function that returns an unknown value - -```js before -export const GET = () => createResponse('text'); -``` - -```js after -throw new Error("@migration task: Migrate this return statement (https://github.com/sveltejs/kit/discussions/5774#discussioncomment-3292701)"); -export const GET = () => createResponse('text'); -``` - -## A function that returns nothing - -```js before -export function GET() { - return; -} -``` - -```js after -export function GET() { - return; -} -``` - -## A GET function that returns a JSON object - -```js before -export function get() { - return { - body: { - a: 1 - } - }; -} -``` - -```js after -import { json } from '@sveltejs/kit'; - -export function GET() { - return json({ - a: 1 - }); -} -``` diff --git a/packages/migrate/migrations/routes/tasks.js b/packages/migrate/migrations/routes/tasks.js deleted file mode 100644 index 775623232882..000000000000 --- a/packages/migrate/migrations/routes/tasks.js +++ /dev/null @@ -1,5 +0,0 @@ -export const STANDALONE_ENDPOINT = '3292701'; -export const PAGE_ENDPOINT = '3292699'; -export const PAGE_LOAD = '3292693'; -export const PAGE_MODULE_CTX = '3292722'; -export const PAGE_DATA_PROP = '3292707'; diff --git a/packages/migrate/migrations/routes/utils.js b/packages/migrate/migrations/routes/utils.js deleted file mode 100644 index 0b8073e6c0fc..000000000000 --- a/packages/migrate/migrations/routes/utils.js +++ /dev/null @@ -1,374 +0,0 @@ -import ts from 'typescript'; -import MagicString from 'magic-string'; -import { comment, indent_at_line } from '../../utils.js'; - -/** - * @param {string} description - * @param {string} [comment_id] - */ -export function task(description, comment_id) { - return ( - `@migration task: ${description}` + - (comment_id - ? ` (https://github.com/sveltejs/kit/discussions/5774#discussioncomment-${comment_id})` - : '') - ); -} - -/** - * @param {string} description - * @param {string} comment_id - */ -export function error(description, comment_id) { - return `throw new Error(${JSON.stringify(task(description, comment_id))});`; -} - -/** @param {string} content */ -export function adjust_imports(content) { - try { - const ast = ts.createSourceFile( - 'filename.ts', - content, - ts.ScriptTarget.Latest, - true, - ts.ScriptKind.TS - ); - - const code = new MagicString(content); - - /** @param {number} pos */ - function adjust(pos) { - // TypeScript AST is a clusterfuck, we need to step forward to find - // where the node _actually_ starts - while (content[pos] !== '.') pos += 1; - - // replace ../ with ../../ and ./ with ../ - code.prependLeft(pos, content[pos + 1] === '.' ? '../' : '.'); - } - - /** @param {ts.Node} node */ - function walk(node) { - if (ts.isImportDeclaration(node)) { - const text = /** @type {ts.StringLiteral} */ (node.moduleSpecifier).text; - if (text[0] === '.') adjust(node.moduleSpecifier.pos); - } - - if (ts.isCallExpression(node) && node.expression.getText() === 'import') { - const arg = node.arguments[0]; - - if (ts.isStringLiteral(arg)) { - if (arg.text[0] === '.') adjust(arg.pos); - } else if (ts.isTemplateLiteral(arg) && !ts.isNoSubstitutionTemplateLiteral(arg)) { - if (arg.head.text[0] === '.') adjust(arg.head.pos); - } - } - - node.forEachChild(walk); - } - - ast.forEachChild(walk); - - return code.toString(); - } catch { - // this is enough of an edge case that it's probably fine to - // just leave the code as we found it - return content; - } -} - -/** - * - * @param {ts.Node} node - * @param {MagicString} str - * @param {string} comment_nr - * @param {string} [suggestion] - */ -export function manual_return_migration(node, str, comment_nr, suggestion) { - manual_migration(node, str, 'Migrate this return statement', comment_nr, suggestion); -} - -/** - * @param {ts.Node} node - * @param {MagicString} str - * @param {string} message - * @param {string} comment_nr - * @param {string} [suggestion] - */ -export function manual_migration(node, str, message, comment_nr, suggestion) { - // handle case where this is called on a (arrow) function - if (ts.isFunctionExpression(node) || ts.isArrowFunction(node)) { - node = node.parent.parent.parent; - } - - const indent = indent_at_line(str.original, node.getStart()); - - let appended = ''; - - if (suggestion) { - appended = `\n${indent}// Suggestion (check for correctness before using):\n${indent}// ${comment( - suggestion, - indent - )}`; - } - - str.prependLeft(node.getStart(), error(message, comment_nr) + appended + `\n${indent}`); -} - -/** - * - * @param {ts.Node} node - * @param {MagicString} str - * @param {string} migration - */ -export function automigration(node, str, migration) { - str.overwrite(node.getStart(), node.getEnd(), migration); -} - -/** - * @param {ts.ObjectLiteralExpression} node - */ -export function get_object_nodes(node) { - /** @type {Record} */ - const obj = {}; - - for (const property of node.properties) { - if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name)) { - obj[property.name.text] = property.initializer; - } else if (ts.isShorthandPropertyAssignment(property)) { - obj[property.name.text] = property.name; - } else { - return null; // object contains funky stuff like computed properties/accessors — bail - } - } - - return obj; -} - -/** - * @param {ts.Node} node - */ -export function is_string_like(node) { - return ( - ts.isStringLiteral(node) || - ts.isTemplateExpression(node) || - ts.isNoSubstitutionTemplateLiteral(node) - ); -} - -/** @param {ts.SourceFile} node */ -export function get_exports(node) { - /** @type {Map} */ - const map = new Map(); - - let complex = false; - - for (const statement of node.statements) { - if ( - ts.isExportDeclaration(statement) && - statement.exportClause && - ts.isNamedExports(statement.exportClause) - ) { - // export { x }, export { x as y } - for (const specifier of statement.exportClause.elements) { - map.set(specifier.name.text, specifier.propertyName?.text || specifier.name.text); - } - } else if ( - ts.isFunctionDeclaration(statement) && - statement.name && - ts.getModifiers(statement)?.[0]?.kind === ts.SyntaxKind.ExportKeyword - ) { - // export function x ... - map.set(statement.name.text, statement.name.text); - } else if ( - ts.isVariableStatement(statement) && - ts.getModifiers(statement)?.[0]?.kind === ts.SyntaxKind.ExportKeyword - ) { - // export const x = ..., y = ... - for (const declaration of statement.declarationList.declarations) { - if (ts.isIdentifier(declaration.name)) { - map.set(declaration.name.text, declaration.name.text); - } else { - // might need to bail out on encountering this edge case, - // because this stuff can get pretty intense - complex = true; - } - } - } - } - - return { map, complex }; -} - -/** - * @param {ts.Node} statement - * @param {string[]} names - * @returns {ts.FunctionDeclaration | ts.FunctionExpression | ts.ArrowFunction | undefined} - */ -export function get_function_node(statement, ...names) { - if ( - ts.isFunctionDeclaration(statement) && - statement.name && - names.includes(statement.name.text) - ) { - // export function x ... - return statement; - } - - if (ts.isVariableStatement(statement)) { - for (const declaration of statement.declarationList.declarations) { - if ( - ts.isIdentifier(declaration.name) && - names.includes(declaration.name.text) && - declaration.initializer && - (ts.isArrowFunction(declaration.initializer) || - ts.isFunctionExpression(declaration.initializer)) - ) { - // export const x = ... - return declaration.initializer; - } - } - } -} - -/** - * Utility for rewriting return statements. - * If `node` is `undefined`, it means it's a concise arrow function body (`() => ({}))`. - * Lone `return;` statements are left untouched. - * @param {ts.Block | ts.ConciseBody} block - * @param {(expression: ts.Expression, node: ts.ReturnStatement | undefined) => void} callback - */ -export function rewrite_returns(block, callback) { - if (ts.isBlock(block)) { - /** @param {ts.Node} node */ - function walk(node) { - if ( - ts.isArrowFunction(node) || - ts.isFunctionExpression(node) || - ts.isFunctionDeclaration(node) - ) { - // don't cross this boundary - return; - } - - if (ts.isReturnStatement(node) && node.expression) { - callback(node.expression, node); - return; - } - - node.forEachChild(walk); - } - - block.forEachChild(walk); - } else { - callback(block, undefined); - } -} - -/** @param {ts.Node} node */ -export function unwrap(node) { - if (ts.isParenthesizedExpression(node)) { - return node.expression; - } - - return node; -} - -/** - * @param {ts.Node} node - * @param {string} name - * @returns {node is ts.isNewExpression} - */ -export function is_new(node, name) { - return ( - ts.isNewExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === name - ); -} - -/** @param {string} content */ -export function parse(content) { - try { - const ast = ts.createSourceFile( - 'filename.ts', - content, - ts.ScriptTarget.Latest, - true, - ts.ScriptKind.TS - ); - - const code = new MagicString(content); - - return { - ast, - code, - exports: get_exports(ast) - }; - } catch { - return null; - } -} - -/** - * @param {ts.Node} node - * @param {MagicString} code - * @param {string} old_type - * @param {string} new_type - */ -export function rewrite_type(node, code, old_type, new_type) { - // @ts-ignore - const jsDoc = node.jsDoc || node.parent?.parent?.parent?.jsDoc; - if (jsDoc) { - // @ts-ignore - for (const comment of jsDoc) { - const str = comment.getText(); - const index = str.indexOf(old_type); - - if (index !== -1) { - code.overwrite(comment.pos + index, comment.pos + index + old_type.length, new_type); - } - } - } - - // @ts-ignore - const type = node.type || node.parent.type; // handle both fn and var declarations - - if (type?.typeName?.escapedText.startsWith(old_type)) { - const start = type.getStart(); - code.overwrite(start, start + old_type.length, new_type); - } -} - -/** - * Does the HTTP verbs uppercase migration if it didn't happen yet. If a string - * is returned, the migration was done or wasn't needed. If undefined is returned, - * the migration is needed but couldn't be done. - * - * @param {string[]} methods - * @param {NonNullable>} file - */ -export function uppercase_migration(methods, file) { - const old_methods = new Set( - ['get', 'post', 'put', 'patch', 'del'].filter((name) => file.exports.map.has(name)) - ); - - if (old_methods.size && !methods.length) { - for (const statement of file.ast.statements) { - for (const method of old_methods) { - const fn = get_function_node( - statement, - /** @type{string} */ (file.exports.map.get(method)) - ); - if (!fn?.name) { - continue; - } - file.code.overwrite( - fn.name.getStart(), - fn.name.getEnd(), - method === 'del' ? 'DELETE' : method.toUpperCase() - ); - old_methods.delete(method); - } - } - } - - return old_methods.size ? undefined : file.code.toString(); -} diff --git a/packages/migrate/migrations/self-closing-tags/index.js b/packages/migrate/migrations/self-closing-tags/index.js deleted file mode 100644 index ced6ca3089e7..000000000000 --- a/packages/migrate/migrations/self-closing-tags/index.js +++ /dev/null @@ -1,57 +0,0 @@ -import colors from 'kleur'; -import fs from 'node:fs'; -import process from 'node:process'; -import prompts from 'prompts'; -import glob from 'tiny-glob/sync.js'; -import { remove_self_closing_tags } from './migrate.js'; -import { pathToFileURL } from 'node:url'; -import { resolve } from 'import-meta-resolve'; - -export async function migrate() { - let compiler; - try { - compiler = await import_from_cwd('svelte/compiler'); - } catch { - console.log(colors.bold().red('❌ Could not find a local Svelte installation.')); - return; - } - - console.log( - colors.bold().yellow('\nThis will update .svelte files inside the current directory\n') - ); - - const response = await prompts({ - type: 'confirm', - name: 'value', - message: 'Continue?', - initial: false - }); - - if (!response.value) { - process.exit(1); - } - - const files = glob('**/*.svelte') - .map((file) => file.replace(/\\/g, '/')) - .filter((file) => !file.includes('/node_modules/')); - - for (const file of files) { - try { - const code = await remove_self_closing_tags(compiler, fs.readFileSync(file, 'utf-8')); - fs.writeFileSync(file, code); - } catch { - // continue - } - } - - console.log(colors.bold().green('✔ Your project has been updated')); - console.log(' If using Prettier, please upgrade to the latest prettier-plugin-svelte version'); -} - -/** @param {string} name */ -function import_from_cwd(name) { - const cwd = pathToFileURL(process.cwd()).href; - const url = resolve(name, cwd + '/x.js'); - - return import(url); -} diff --git a/packages/migrate/migrations/self-closing-tags/migrate.js b/packages/migrate/migrations/self-closing-tags/migrate.js deleted file mode 100644 index 73f4ec0a60f5..000000000000 --- a/packages/migrate/migrations/self-closing-tags/migrate.js +++ /dev/null @@ -1,192 +0,0 @@ -import MagicString from 'magic-string'; -import { walk } from 'zimmerframe'; - -const VoidElements = [ - 'area', - 'base', - 'br', - 'col', - 'embed', - 'hr', - 'img', - 'input', - 'keygen', - 'link', - 'menuitem', - 'meta', - 'param', - 'source', - 'track', - 'wbr' -]; - -const SVGElements = [ - 'altGlyph', - 'altGlyphDef', - 'altGlyphItem', - 'animate', - 'animateColor', - 'animateMotion', - 'animateTransform', - 'circle', - 'clipPath', - 'color-profile', - 'cursor', - 'defs', - 'desc', - 'discard', - 'ellipse', - 'feBlend', - 'feColorMatrix', - 'feComponentTransfer', - 'feComposite', - 'feConvolveMatrix', - 'feDiffuseLighting', - 'feDisplacementMap', - 'feDistantLight', - 'feDropShadow', - 'feFlood', - 'feFuncA', - 'feFuncB', - 'feFuncG', - 'feFuncR', - 'feGaussianBlur', - 'feImage', - 'feMerge', - 'feMergeNode', - 'feMorphology', - 'feOffset', - 'fePointLight', - 'feSpecularLighting', - 'feSpotLight', - 'feTile', - 'feTurbulence', - 'filter', - 'font', - 'font-face', - 'font-face-format', - 'font-face-name', - 'font-face-src', - 'font-face-uri', - 'foreignObject', - 'g', - 'glyph', - 'glyphRef', - 'hatch', - 'hatchpath', - 'hkern', - 'image', - 'line', - 'linearGradient', - 'marker', - 'mask', - 'mesh', - 'meshgradient', - 'meshpatch', - 'meshrow', - 'metadata', - 'missing-glyph', - 'mpath', - 'path', - 'pattern', - 'polygon', - 'polyline', - 'radialGradient', - 'rect', - 'set', - 'solidcolor', - 'stop', - 'svg', - 'switch', - 'symbol', - 'text', - 'textPath', - 'tref', - 'tspan', - 'unknown', - 'use', - 'view', - 'vkern' -]; - -/** - * @param {{ preprocess: any, parse: any }} svelte_compiler - * @param {string} source - */ -export async function remove_self_closing_tags({ preprocess, parse }, source) { - const preprocessed = await preprocess(source, { - /** @param {{ content: string }} input */ - script: ({ content }) => ({ - code: content - .split('\n') - .map((line) => ' '.repeat(line.length)) - .join('\n') - }), - /** @param {{ content: string }} input */ - style: ({ content }) => ({ - code: content - .split('\n') - .map((line) => ' '.repeat(line.length)) - .join('\n') - }) - }); - const ast = parse(preprocessed.code); - const ms = new MagicString(source); - /** @type {Array<() => void>} */ - const updates = []; - let is_foreign = false; - let is_custom_element = false; - - walk(ast.html, null, { - _(node, { next, stop }) { - if (node.type === 'Options') { - const namespace = node.attributes.find( - /** @param {any} a */ - (a) => a.type === 'Attribute' && a.name === 'namespace' - ); - if (namespace?.value[0].data === 'foreign') { - is_foreign = true; - stop(); - return; - } - - is_custom_element = node.attributes.some( - /** @param {any} a */ - (a) => a.type === 'Attribute' && (a.name === 'customElement' || a.name === 'tag') - ); - } - - if (node.type === 'Element' || node.type === 'Slot') { - const is_self_closing = source[node.end - 2] === '/'; - if ( - !is_self_closing || - VoidElements.includes(node.name) || - SVGElements.includes(node.name) || - !/^[a-z0-9_-]+$/.test(node.name) - ) { - next(); - return; - } - - let start = node.end - 2; - if (source[start - 1] === ' ') { - start--; - } - updates.push(() => { - if (node.type === 'Element' || is_custom_element) { - ms.update(start, node.end, `>`); - } - }); - } - - next(); - } - }); - - if (is_foreign) { - return source; - } - - updates.forEach((update) => update()); - return ms.toString(); -} diff --git a/packages/migrate/migrations/self-closing-tags/migrate.spec.js b/packages/migrate/migrations/self-closing-tags/migrate.spec.js deleted file mode 100644 index 421f00519732..000000000000 --- a/packages/migrate/migrations/self-closing-tags/migrate.spec.js +++ /dev/null @@ -1,32 +0,0 @@ -import { assert, test } from 'vitest'; -import * as compiler from 'svelte/compiler'; -import { remove_self_closing_tags } from './migrate.js'; - -/** @type {Record} */ -const tests = { - '
': '
', - '
': '
', - '': '', - '
': '
', - '
': '
', - '\t': '\t
', - '': '', - '': '', - '': '', - '': '', - '': '', - '': - '', - '': '', - '': '', - '
': - '
', - '
': '
' -}; - -for (const input in tests) { - test(input, async () => { - const output = tests[input]; - assert.equal(await remove_self_closing_tags(compiler, input), output); - }); -} diff --git a/packages/migrate/migrations/svelte-4/index.js b/packages/migrate/migrations/svelte-4/index.js deleted file mode 100644 index 4fea5173da0e..000000000000 --- a/packages/migrate/migrations/svelte-4/index.js +++ /dev/null @@ -1,112 +0,0 @@ -import colors from 'kleur'; -import fs from 'node:fs'; -import process from 'node:process'; -import prompts from 'prompts'; -import glob from 'tiny-glob/sync.js'; -import { bail, check_git, update_js_file, update_svelte_file } from '../../utils.js'; -import { transform_code, transform_svelte_code, update_pkg_json } from './migrate.js'; - -export async function migrate() { - if (!fs.existsSync('package.json')) { - bail('Please re-run this script in a directory with a package.json'); - } - - console.log( - colors - .bold() - .yellow( - '\nThis will update files in the current directory\n' + - "If you're inside a monorepo, don't run this in the root directory, rather run it in all projects independently.\n" - ) - ); - - const use_git = check_git(); - - const response = await prompts({ - type: 'confirm', - name: 'value', - message: 'Continue?', - initial: false - }); - - if (!response.value) { - process.exit(1); - } - - const folders = await prompts({ - type: 'multiselect', - name: 'value', - message: 'Which folders should be migrated?', - choices: fs - .readdirSync('.') - .filter( - (dir) => fs.statSync(dir).isDirectory() && dir !== 'node_modules' && !dir.startsWith('.') - ) - .map((dir) => ({ title: dir, value: dir, selected: true })) - }); - - if (!folders.value?.length) { - process.exit(1); - } - - const migrate_transition = await prompts({ - type: 'confirm', - name: 'value', - message: - 'Add the `|global` modifier to currently global transitions for backwards compatibility? More info at https://svelte.dev/docs/svelte/v4-migration-guide#transitions-are-local-by-default', - initial: true - }); - - update_pkg_json(); - - // const { default: config } = fs.existsSync('svelte.config.js') - // ? await import(pathToFileURL(path.resolve('svelte.config.js')).href) - // : { default: {} }; - - /** @type {string[]} */ - const svelte_extensions = /* config.extensions ?? - disabled because it would break .svx */ [ - '.svelte' - ]; - const extensions = [...svelte_extensions, '.ts', '.js']; - // For some reason {folders.value.join(',')} as part of the glob doesn't work and returns less files - const files = folders.value.flatMap( - /** @param {string} folder */ (folder) => - glob(`${folder}/**`, { filesOnly: true, dot: true }) - .map((file) => file.replace(/\\/g, '/')) - .filter((file) => !file.includes('/node_modules/')) - ); - - for (const file of files) { - if (extensions.some((ext) => file.endsWith(ext))) { - if (svelte_extensions.some((ext) => file.endsWith(ext))) { - update_svelte_file(file, transform_code, (code) => - transform_svelte_code(code, migrate_transition.value) - ); - } else { - update_js_file(file, transform_code); - } - } - } - - console.log(colors.bold().green('✔ Your project has been migrated')); - - console.log('\nRecommended next steps:\n'); - - const cyan = colors.bold().cyan; - - const tasks = [ - use_git && cyan('git commit -m "migration to Svelte 4"'), - 'Review the migration guide at https://svelte.dev/docs/svelte/v4-migration-guide', - 'Read the updated docs at https://svelte.dev/docs/svelte' - ].filter(Boolean); - - tasks.forEach((task, i) => { - console.log(` ${i + 1}: ${task}`); - }); - - console.log(''); - - if (use_git) { - console.log(`Run ${cyan('git diff')} to review changes.\n`); - } -} diff --git a/packages/migrate/migrations/svelte-4/migrate.js b/packages/migrate/migrations/svelte-4/migrate.js deleted file mode 100644 index c459380ef441..000000000000 --- a/packages/migrate/migrations/svelte-4/migrate.js +++ /dev/null @@ -1,348 +0,0 @@ -import fs from 'node:fs'; -import { Project, ts, Node, SyntaxKind } from 'ts-morph'; -import { log_migration, log_on_ts_modification, update_pkg } from '../../utils.js'; - -export function update_pkg_json() { - fs.writeFileSync( - 'package.json', - update_pkg_json_content(fs.readFileSync('package.json', 'utf8')) - ); -} - -/** - * @param {string} content - */ -export function update_pkg_json_content(content) { - return update_pkg(content, [ - ['svelte', '^4.0.0'], - ['svelte-check', '^3.4.3'], - ['svelte-preprocess', '^5.0.3'], - ['@sveltejs/kit', '^1.20.4'], - ['@sveltejs/vite-plugin-svelte', '^2.4.1'], - [ - 'svelte-loader', - '^3.1.8', - ' (if you are still on webpack 4, you need to update to webpack 5)' - ], - ['rollup-plugin-svelte', '^7.1.5'], - ['prettier-plugin-svelte', '^2.10.1'], - ['eslint-plugin-svelte', '^2.30.0'], - [ - 'eslint-plugin-svelte3', - '^4.0.0', - ' (this package is deprecated, use eslint-plugin-svelte instead. More info: https://svelte.dev/docs/svelte/v4-migration-guide#new-eslint-package)' - ], - [ - 'typescript', - '^5.0.0', - ' (this might introduce new type errors due to breaking changes within TypeScript)' - ] - ]); -} - -/** - * @param {string} code - * @param {boolean} is_ts - */ -export function transform_code(code, is_ts) { - const project = new Project({ useInMemoryFileSystem: true }); - const source = project.createSourceFile('svelte.ts', code); - update_imports(source, is_ts); - update_typeof_svelte_component(source, is_ts); - update_action_types(source, is_ts); - update_action_return_types(source, is_ts); - return source.getFullText(); -} - -/** - * @param {string} code - * @param {boolean} migrate_transition - */ -export function transform_svelte_code(code, migrate_transition) { - code = update_svelte_options(code); - return update_transitions(code, migrate_transition); -} - -/** - * -> - * @param {string} code - */ -function update_svelte_options(code) { - return code.replace(//, (match) => { - log_migration( - 'Replaced `svelte:options` `tag` attribute with `customElement` attribute: https://svelte.dev/docs/svelte/v4-migration-guide#custom-elements-with-svelte' - ); - return match.replace('tag=', 'customElement='); - }); -} - -/** - * transition/in/out:x -> transition/in/out:x|global - * transition/in/out|local:x -> transition/in/out:x - * @param {string} code - * @param {boolean} migrate_transition - */ -function update_transitions(code, migrate_transition) { - if (migrate_transition) { - const replaced = code.replace(/(\s)(transition:|in:|out:)(\w+)(?=[\s>=])/g, '$1$2$3|global'); - if (replaced !== code) { - log_migration( - 'Added `|global` to `transition`, `in`, and `out` directives (transitions are local by default now): https://svelte.dev/docs/svelte/v4-migration-guide#transitions-are-local-by-default' - ); - } - code = replaced; - } - const replaced = code.replace(/(\s)(transition:|in:|out:)(\w+)(\|local)(?=[\s>=])/g, '$1$2$3'); - if (replaced !== code) { - log_migration( - 'Removed `|local` from `transition`, `in`, and `out` directives (transitions are local by default now): https://svelte.dev/docs/svelte/v4-migration-guide#transitions-are-local-by-default' - ); - } - return replaced; -} - -/** - * Action -> Action - * @param {import('ts-morph').SourceFile} source - * @param {boolean} is_ts - */ -function update_action_types(source, is_ts) { - const logger = log_on_ts_modification( - source, - 'Updated `Action` interface usages: https://svelte.dev/docs/svelte/v4-migration-guide#stricter-types-for-svelte-functions' - ); - - const imports = get_imports(source, 'svelte/action', 'Action'); - for (const namedImport of imports) { - const identifiers = find_identifiers(source, namedImport.getAliasNode()?.getText() ?? 'Action'); - for (const id of identifiers) { - const parent = id.getParent(); - if (Node.isTypeReference(parent)) { - const type_args = parent.getTypeArguments(); - if (type_args.length === 1) { - parent.addTypeArgument('any'); - } else if (type_args.length === 0) { - parent.addTypeArgument('HTMLElement'); - parent.addTypeArgument('any'); - } - } - } - } - - if (!is_ts) { - replaceInJsDoc(source, (text) => { - return text.replace( - /import\((['"])svelte\/action['"]\).Action(<\w+>)?(?=[^<\w]|$)/g, - (_, quote, type) => - `import(${quote}svelte/action${quote}).Action<${ - type ? type.slice(1, -1) + '' : 'HTMLElement' - }, any>` - ); - }); - } - - logger(); -} - -/** - * ActionReturn -> ActionReturn - * @param {import('ts-morph').SourceFile} source - * @param {boolean} is_ts - */ -function update_action_return_types(source, is_ts) { - const logger = log_on_ts_modification( - source, - 'Updated `ActionReturn` interface usages: https://svelte.dev/docs/svelte/v4-migration-guide#stricter-types-for-svelte-functions' - ); - - const imports = get_imports(source, 'svelte/action', 'ActionReturn'); - for (const namedImport of imports) { - const identifiers = find_identifiers( - source, - namedImport.getAliasNode()?.getText() ?? 'ActionReturn' - ); - for (const id of identifiers) { - const parent = id.getParent(); - if (Node.isTypeReference(parent)) { - const type_args = parent.getTypeArguments(); - if (type_args.length === 0) { - parent.addTypeArgument('any'); - } - } - } - } - - if (!is_ts) { - replaceInJsDoc(source, (text) => { - return text.replace( - /import\((['"])svelte\/action['"]\).ActionReturn(?=[^<\w]|$)/g, - 'import($1svelte/action$1).ActionReturn' - ); - }); - } - - logger(); -} - -/** - * SvelteComponentTyped -> SvelteComponent - * @param {import('ts-morph').SourceFile} source - * @param {boolean} is_ts - */ -function update_imports(source, is_ts) { - const logger = log_on_ts_modification( - source, - 'Replaced `SvelteComponentTyped` imports with `SvelteComponent` imports: https://svelte.dev/docs/svelte/v4-migration-guide#stricter-types-for-svelte-functions' - ); - - const identifiers = find_identifiers(source, 'SvelteComponent'); - const can_rename = identifiers.every((id) => { - const parent = id.getParent(); - return ( - (Node.isImportSpecifier(parent) && - !parent.getAliasNode() && - parent - .getParent() - .getParent() - .getParentIfKind(SyntaxKind.ImportDeclaration) - ?.getModuleSpecifier() - .getText() === 'svelte') || - !is_declaration(parent) - ); - }); - - const imports = get_imports(source, 'svelte', 'SvelteComponentTyped'); - for (const namedImport of imports) { - if (can_rename) { - namedImport.renameAlias('SvelteComponent'); - if ( - namedImport - .getParent() - .getElements() - .some((e) => !e.getAliasNode() && e.getNameNode().getText() === 'SvelteComponent') - ) { - namedImport.remove(); - } else { - namedImport.setName('SvelteComponent'); - namedImport.removeAlias(); - } - } else { - namedImport.renameAlias('SvelteComponentTyped'); - namedImport.setName('SvelteComponent'); - } - } - - if (!is_ts) { - replaceInJsDoc(source, (text) => { - return text.replace( - /import\((['"])svelte['"]\)\.SvelteComponentTyped(?=\W|$)/g, - 'import($1svelte$1).SvelteComponent' - ); - }); - } - - logger(); -} - -/** - * typeof SvelteComponent -> typeof SvelteComponent - * @param {import('ts-morph').SourceFile} source - * @param {boolean} is_ts - */ -function update_typeof_svelte_component(source, is_ts) { - const logger = log_on_ts_modification( - source, - 'Adjusted `typeof SvelteComponent` to `typeof SvelteComponent`: https://svelte.dev/docs/svelte/v4-migration-guide#stricter-types-for-svelte-functions' - ); - - const imports = get_imports(source, 'svelte', 'SvelteComponent'); - - for (const type of imports) { - if (type) { - const name = type.getAliasNode() ?? type.getNameNode(); - if (Node.isIdentifier(name)) { - name.findReferencesAsNodes().forEach((ref) => { - const parent = ref.getParent(); - if (parent && Node.isTypeQuery(parent)) { - const id = parent.getFirstChildByKind(ts.SyntaxKind.Identifier); - if (id?.getText() === name.getText()) { - const typeArguments = parent.getTypeArguments(); - if (typeArguments.length === 0) { - parent.addTypeArgument('any'); - } - } - } - }); - } - } - } - - if (!is_ts) { - replaceInJsDoc(source, (text) => { - return text.replace( - /typeof import\((['"])svelte['"]\)\.SvelteComponent(?=[^<\w]|$)/g, - 'typeof import($1svelte$1).SvelteComponent' - ); - }); - } - - logger(); -} - -/** - * @param {import('ts-morph').SourceFile} source - * @param {string} from - * @param {string} name - */ -function get_imports(source, from, name) { - return source - .getImportDeclarations() - .filter((i) => i.getModuleSpecifierValue() === from) - .flatMap((i) => i.getNamedImports()) - .filter((i) => i.getName() === name); -} - -/** - * @param {import('ts-morph').SourceFile} source - * @param {string} name - */ -function find_identifiers(source, name) { - return source.getDescendantsOfKind(ts.SyntaxKind.Identifier).filter((i) => i.getText() === name); -} - -/** - * Does not include imports - * @param {Node} node - */ -function is_declaration(node) { - return ( - Node.isVariableDeclaration(node) || - Node.isFunctionDeclaration(node) || - Node.isClassDeclaration(node) || - Node.isTypeAliasDeclaration(node) || - Node.isInterfaceDeclaration(node) - ); -} - -/** - * @param {import('ts-morph').SourceFile} source - * @param {(text: string) => string | undefined} replacer - */ -function replaceInJsDoc(source, replacer) { - source.forEachChild((node) => { - if (Node.isJSDocable(node)) { - const tags = node.getJsDocs().flatMap((jsdoc) => jsdoc.getTags()); - tags.forEach((t) => - t.forEachChild((c) => { - if (Node.isJSDocTypeExpression(c)) { - const text = c.getText().slice(1, -1); - const replacement = replacer(text); - if (replacement && replacement !== text) { - c.replaceWithText(`{${replacement}}`); - } - } - }) - ); - } - }); -} diff --git a/packages/migrate/migrations/svelte-4/migrate.spec.js b/packages/migrate/migrations/svelte-4/migrate.spec.js deleted file mode 100644 index f0c34cddd1da..000000000000 --- a/packages/migrate/migrations/svelte-4/migrate.spec.js +++ /dev/null @@ -1,382 +0,0 @@ -import { assert, test } from 'vitest'; -import { transform_code, transform_svelte_code, update_pkg_json_content } from './migrate.js'; - -test('Updates SvelteComponentTyped #1', () => { - const result = transform_code( - `import { SvelteComponentTyped } from 'svelte'; - -export class Foo extends SvelteComponentTyped<{}> {} - -const bar: SvelteComponentTyped = null;`, - true - ); - assert.equal( - result, - `import { SvelteComponent } from 'svelte'; - -export class Foo extends SvelteComponent<{}> {} - -const bar: SvelteComponent = null;` - ); -}); - -test('Updates SvelteComponentTyped #2', () => { - const result = transform_code( - `import { SvelteComponentTyped, SvelteComponent } from 'svelte'; - -export class Foo extends SvelteComponentTyped<{}> {} - -const bar: SvelteComponentTyped = null; -const baz: SvelteComponent = null;`, - true - ); - assert.equal( - result, - `import { SvelteComponent } from 'svelte'; - -export class Foo extends SvelteComponent<{}> {} - -const bar: SvelteComponent = null; -const baz: SvelteComponent = null;` - ); -}); - -test('Updates SvelteComponentTyped #3', () => { - const result = transform_code( - `import { SvelteComponentTyped } from 'svelte'; - -interface SvelteComponent {} - -export class Foo extends SvelteComponentTyped<{}> {} - -const bar: SvelteComponentTyped = null; -const baz: SvelteComponent = null;`, - true - ); - assert.equal( - result, - `import { SvelteComponent as SvelteComponentTyped } from 'svelte'; - -interface SvelteComponent {} - -export class Foo extends SvelteComponentTyped<{}> {} - -const bar: SvelteComponentTyped = null; -const baz: SvelteComponent = null;` - ); -}); - -test('Updates SvelteComponentTyped (jsdoc)', () => { - const result = transform_code( - ` - /** @type {import('svelte').SvelteComponentTyped} */ - const bar = null; - /** @type {import('svelte').SvelteComponentTyped} */ - const baz = null; - `, - false - ); - assert.equal( - result, - ` - /** @type {import('svelte').SvelteComponent} */ - const bar = null; - /** @type {import('svelte').SvelteComponent} */ - const baz = null; - ` - ); -}); - -test('Updates typeof SvelteComponent', () => { - const result = transform_code( - `import { SvelteComponent } from 'svelte'; - import { SvelteComponent as C } from 'svelte'; - - const a: typeof SvelteComponent = null; - function b(c: typeof SvelteComponent) {} - const c: typeof SvelteComponent = null; - const d: typeof C = null; - `, - true - ); - assert.equal( - result, - `import { SvelteComponent } from 'svelte'; - import { SvelteComponent as C } from 'svelte'; - - const a: typeof SvelteComponent = null; - function b(c: typeof SvelteComponent) {} - const c: typeof SvelteComponent = null; - const d: typeof C = null; - ` - ); -}); - -test('Updates typeof SvelteComponent (jsdoc)', () => { - const result = transform_code( - ` - /** @type {typeof import('svelte').SvelteComponent} */ - const a = null; - /** @type {typeof import('svelte').SvelteComponent} */ - const c = null; - /** @type {typeof C} */ - const d: typeof C = null; - `, - false - ); - assert.equal( - result, - ` - /** @type {typeof import('svelte').SvelteComponent} */ - const a = null; - /** @type {typeof import('svelte').SvelteComponent} */ - const c = null; - /** @type {typeof C} */ - const d: typeof C = null; - ` - ); -}); - -test('Updates Action and ActionReturn', () => { - const result = transform_code( - `import type { Action, ActionReturn } from 'svelte/action'; - - const a: Action = () => {}; - const b: Action = () => {}; - const c: Action = () => {}; - const d: Action = () => {}; - const e: ActionReturn = () => {}; - const f: ActionReturn = () => {}; - const g: ActionReturn = () => {}; - `, - true - ); - assert.equal( - result, - - `import type { Action, ActionReturn } from 'svelte/action'; - - const a: Action = () => {}; - const b: Action = () => {}; - const c: Action = () => {}; - const d: Action = () => {}; - const e: ActionReturn = () => {}; - const f: ActionReturn = () => {}; - const g: ActionReturn = () => {}; - ` - ); -}); - -test('Updates Action and ActionReturn (jsdoc)', () => { - const result = transform_code( - ` - /** @type {import('svelte/action').Action} */ - const a = () => {}; - /** @type {import('svelte/action').Action} */ - const b = () => {}; - /** @type {import('svelte/action').Action} */ - const c = () => {}; - /** @type {import('svelte/action').Action} */ - const d = () => {}; - /** @type {import('svelte/action').ActionReturn} */ - const e = () => {}; - /** @type {import('svelte/action').ActionReturn} */ - const f = () => {}; - /** @type {import('svelte/action').ActionReturn} */ - const g = () => {}; - `, - false - ); - assert.equal( - result, - - ` - /** @type {import('svelte/action').Action} */ - const a = () => {}; - /** @type {import('svelte/action').Action} */ - const b = () => {}; - /** @type {import('svelte/action').Action} */ - const c = () => {}; - /** @type {import('svelte/action').Action} */ - const d = () => {}; - /** @type {import('svelte/action').ActionReturn} */ - const e = () => {}; - /** @type {import('svelte/action').ActionReturn} */ - const f = () => {}; - /** @type {import('svelte/action').ActionReturn} */ - const g = () => {}; - ` - ); -}); - -test('Updates svelte:options #1', () => { - const result = transform_svelte_code( - ` - -
hi
`, - true - ); - assert.equal( - result, - ` - -
hi
` - ); -}); - -test('Updates svelte:options #2', () => { - const result = transform_svelte_code( - ` - - - -
hi
`, - true - ); - assert.equal( - result, - ` - - - -
hi
` - ); -}); - -test('Updates transitions', () => { - const result = transform_svelte_code( - `
-
-
-
-
-
-
-
-
-
-
-
- -
-
- `, - true - ); - assert.equal( - result, - `
-
-
-
-
-
-
-
-
-
-
-
- -
-
- ` - ); -}); - -test('Updates transitions #2', () => { - const result = transform_svelte_code( - `
-
-
-
-
-
-
-
-
-
-
-
- -
-
- `, - false - ); - assert.equal( - result, - `
-
-
-
-
-
-
-
-
-
-
-
- -
-
- ` - ); -}); - -test('Update package.json', () => { - const result = update_pkg_json_content(`{ - "name": "svelte-app", - "version": "1.0.0", - "devDependencies": { - "svelte": "^3.0.0", - "svelte-check": "^1.0.0", - "svelte-preprocess": "^5.0.0" - }, - "dependencies": { - "@sveltejs/kit": "^1.0.0" - } -}`); - assert.equal( - result, - `{ - "name": "svelte-app", - "version": "1.0.0", - "devDependencies": { - "svelte": "^4.0.0", - "svelte-check": "^3.4.3", - "svelte-preprocess": "^5.0.3" - }, - "dependencies": { - "@sveltejs/kit": "^1.20.4" - } -}` - ); -}); - -test('Does not downgrade versions', () => { - const result = update_pkg_json_content(`{ - "devDependencies": { - "svelte": "^4.0.5", - "typescript": "github:idk" - } -}`); - assert.equal( - result, - `{ - "devDependencies": { - "svelte": "^4.0.5", - "typescript": "github:idk" - } -}` - ); -}); diff --git a/packages/migrate/migrations/svelte-5/index.js b/packages/migrate/migrations/svelte-5/index.js deleted file mode 100644 index a6b17d421232..000000000000 --- a/packages/migrate/migrations/svelte-5/index.js +++ /dev/null @@ -1,216 +0,0 @@ -import { resolve } from 'import-meta-resolve'; -import colors from 'kleur'; -import { execSync } from 'node:child_process'; -import process from 'node:process'; -import fs from 'node:fs'; -import { dirname } from 'node:path'; -import { fileURLToPath, pathToFileURL } from 'node:url'; -import prompts from 'prompts'; -import semver from 'semver'; -import glob from 'tiny-glob/sync.js'; -import { bail, check_git, update_js_file, update_svelte_file } from '../../utils.js'; -import { migrate as migrate_svelte_4 } from '../svelte-4/index.js'; -import { migrate as migrate_sveltekit_2 } from '../sveltekit-2/index.js'; -import { transform_module_code, transform_svelte_code, update_pkg_json } from './migrate.js'; - -export async function migrate() { - if (!fs.existsSync('package.json')) { - bail('Please re-run this script in a directory with a package.json'); - } - - console.log( - 'This migration is experimental — please report any bugs to https://github.com/sveltejs/svelte/issues' - ); - - const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8')); - - const svelte_dep = pkg.devDependencies?.svelte ?? pkg.dependencies?.svelte; - if (svelte_dep && semver.validRange(svelte_dep) && semver.gtr('4.0.0', svelte_dep)) { - console.log( - colors - .bold() - .yellow( - '\nDetected Svelte 3. You need to upgrade to Svelte version 4 first (`npx sv migrate svelte-4`).\n' - ) - ); - const response = await prompts({ - type: 'confirm', - name: 'value', - message: 'Run svelte-4 migration now?', - initial: false - }); - if (!response.value) { - process.exit(1); - } else { - await migrate_svelte_4(); - console.log( - colors - .bold() - .green( - 'svelte-4 migration complete. Check that everything is ok, then run `npx sv migrate svelte-5` again to continue the Svelte 5 migration.\n' - ) - ); - process.exit(0); - } - } - - const kit_dep = pkg.devDependencies?.['@sveltejs/kit'] ?? pkg.dependencies?.['@sveltejs/kit']; - if (kit_dep && semver.validRange(kit_dep) && semver.gtr('2.0.0', kit_dep)) { - console.log( - colors - .bold() - .yellow( - '\nDetected SvelteKit 1. You need to upgrade to SvelteKit version 2 first (`npx sv migrate sveltekit-2`).\n' - ) - ); - const response = await prompts({ - type: 'confirm', - name: 'value', - message: 'Run sveltekit-2 migration now?', - initial: false - }); - if (!response.value) { - process.exit(1); - } else { - await migrate_sveltekit_2(); - console.log( - colors - .bold() - .green( - 'sveltekit-2 migration complete. Check that everything is ok, then run `npx sv migrate svelte-5` again to continue the Svelte 5 migration.\n' - ) - ); - process.exit(0); - } - } - - let migrate; - try { - try { - ({ migrate } = await import_from_cwd('svelte/compiler')); - if (!migrate) throw new Error('found Svelte 4'); - } catch { - execSync('npm install svelte@^5.0.0 --no-save', { - stdio: 'inherit', - cwd: dirname(fileURLToPath(import.meta.url)) - }); - const url = resolve('svelte/compiler', import.meta.url); - ({ migrate } = await import(url)); - } - } catch (e) { - console.log(e); - console.log( - colors - .bold() - .red( - '❌ Could not install Svelte. Manually bump the dependency to version 5 in your package.json, install it, then try again.' - ) - ); - return; - } - - console.log( - colors - .bold() - .yellow( - '\nThis will update files in the current directory\n' + - "If you're inside a monorepo, don't run this in the root directory, rather run it in all projects independently.\n" - ) - ); - - const use_git = check_git(); - - const response = await prompts({ - type: 'confirm', - name: 'value', - message: 'Continue?', - initial: false - }); - - if (!response.value) { - process.exit(1); - } - - const folders = await prompts({ - type: 'multiselect', - name: 'value', - message: 'Which folders should be migrated?', - choices: fs - .readdirSync('.') - .filter( - (dir) => fs.statSync(dir).isDirectory() && dir !== 'node_modules' && !dir.startsWith('.') - ) - .map((dir) => ({ title: dir, value: dir, selected: true })) - }); - - if (!folders.value?.length) { - process.exit(1); - } - - update_pkg_json(); - - const use_ts = fs.existsSync('tsconfig.json'); - - // const { default: config } = fs.existsSync('svelte.config.js') - // ? await import(pathToFileURL(path.resolve('svelte.config.js')).href) - // : { default: {} }; - - /** @type {string[]} */ - const svelte_extensions = /* config.extensions ?? - disabled because it would break .svx */ [ - '.svelte' - ]; - const extensions = [...svelte_extensions, '.ts', '.js']; - // For some reason {folders.value.join(',')} as part of the glob doesn't work and returns less files - const files = folders.value.flatMap( - /** @param {string} folder */ (folder) => - glob(`${folder}/**`, { filesOnly: true, dot: true }) - .map((file) => file.replace(/\\/g, '/')) - .filter((file) => !file.includes('/node_modules/')) - ); - - for (const file of files) { - if (extensions.some((ext) => file.endsWith(ext))) { - if (svelte_extensions.some((ext) => file.endsWith(ext))) { - update_svelte_file(file, transform_module_code, (code) => - transform_svelte_code(code, migrate, { filename: file, use_ts }) - ); - } else { - update_js_file(file, transform_module_code); - } - } - } - - console.log(colors.bold().green('✔ Your project has been migrated')); - - console.log('\nRecommended next steps:\n'); - - const cyan = colors.bold().cyan; - - const tasks = [ - "install the updated dependencies ('npm i' / 'pnpm i' / etc) " + - '(note that there may be peer dependency issues when not all your libraries officially support Svelte 5 yet. In this case try installing with the --force option)', - use_git && cyan('git commit -m "migration to Svelte 5"'), - 'Review the breaking changes at https://svelte-5-preview.vercel.app/docs/breaking-changes' - // replace with this once it's live: - // 'Review the migration guide at https://svelte.dev/docs/svelte/v5-migration-guide', - // 'Read the updated docs at https://svelte.dev/docs/svelte' - ].filter(Boolean); - - tasks.forEach((task, i) => { - console.log(` ${i + 1}: ${task}`); - }); - - console.log(''); - - if (use_git) { - console.log(`Run ${cyan('git diff')} to review changes.\n`); - } -} - -/** @param {string} name */ -function import_from_cwd(name) { - const cwd = pathToFileURL(process.cwd()).href; - const url = resolve(name, cwd + '/x.js'); - - return import(url); -} diff --git a/packages/migrate/migrations/svelte-5/migrate.js b/packages/migrate/migrations/svelte-5/migrate.js deleted file mode 100644 index eb9e750f46a0..000000000000 --- a/packages/migrate/migrations/svelte-5/migrate.js +++ /dev/null @@ -1,129 +0,0 @@ -import fs from 'node:fs'; -import { Project, ts, Node } from 'ts-morph'; -import { add_named_import, update_pkg } from '../../utils.js'; - -export function update_pkg_json() { - fs.writeFileSync( - 'package.json', - update_pkg_json_content(fs.readFileSync('package.json', 'utf8')) - ); -} - -/** - * @param {string} content - */ -export function update_pkg_json_content(content) { - return update_pkg(content, [ - ['svelte', '^5.0.0'], - ['svelte-check', '^4.0.0'], - ['svelte-preprocess', '^6.0.0'], - ['@sveltejs/enhanced-img', '^0.3.9'], - ['@sveltejs/kit', '^2.5.27'], - ['@sveltejs/vite-plugin-svelte', '^4.0.0'], - [ - 'svelte-loader', - '^3.2.3', - ' (if you are still on webpack 4, you need to update to webpack 5)' - ], - ['rollup-plugin-svelte', '^7.2.2'], - ['prettier', '^3.1.0'], - ['prettier-plugin-svelte', '^3.2.6'], - ['eslint-plugin-svelte', '^2.45.1'], - ['svelte-eslint-parser', '^0.42.0'], - [ - 'eslint-plugin-svelte3', - '^4.0.0', - ' (this package is deprecated, use eslint-plugin-svelte instead. More info: https://svelte.dev/docs/svelte/v4-migration-guide#new-eslint-package)' - ], - [ - 'typescript', - '^5.5.0', - ' (this might introduce new type errors due to breaking changes within TypeScript)' - ], - ['vite', '^5.4.4'] - ]); -} - -/** - * @param {string} code - */ -export function transform_module_code(code) { - const project = new Project({ useInMemoryFileSystem: true }); - const source = project.createSourceFile('svelte.ts', code); - update_component_instantiation(source); - return source.getFullText(); -} - -/** - * @param {string} code - * @param {(source: string, options: { filename?: string, use_ts?: boolean }) => { code: string }} transform_code - * @param {{ filename?: string, use_ts?: boolean }} options - */ -export function transform_svelte_code(code, transform_code, options) { - return transform_code(code, options).code; -} - -/** - * new Component(...) -> mount(Component, ...) - * @param {import('ts-morph').SourceFile} source - */ -function update_component_instantiation(source) { - const imports = source - .getImportDeclarations() - .filter((i) => i.getModuleSpecifierValue().endsWith('.svelte')) - .flatMap((i) => i.getDefaultImport() || []); - - for (const defaultImport of imports) { - const identifiers = find_identifiers(source, defaultImport.getText()); - - for (const id of identifiers) { - const parent = id.getParent(); - - if (Node.isNewExpression(parent)) { - const args = parent.getArguments(); - - if (args.length === 1) { - const method = - Node.isObjectLiteralExpression(args[0]) && !!args[0].getProperty('hydrate') - ? 'hydrate' - : 'mount'; - - if (method === 'hydrate') { - /** @type {import('ts-morph').ObjectLiteralExpression} */ (args[0]) - .getProperty('hydrate') - ?.remove(); - } - - add_named_import(source, 'svelte', method); - - const declaration = parent - .getParentIfKind(ts.SyntaxKind.VariableDeclaration) - ?.getNameNode(); - if (Node.isIdentifier(declaration)) { - const usages = declaration.findReferencesAsNodes(); - for (const usage of usages) { - const parent = usage.getParent(); - if (Node.isPropertyAccessExpression(parent) && parent.getName() === '$destroy') { - const call_expr = parent.getParentIfKind(ts.SyntaxKind.CallExpression); - if (call_expr) { - call_expr.replaceWithText(`unmount(${usage.getText()})`); - add_named_import(source, 'svelte', 'unmount'); - } - } - } - } - - parent.replaceWithText(`${method}(${id.getText()}, ${args[0].getText()})`); - } - } - } - } -} - -/** - * @param {import('ts-morph').SourceFile} source - * @param {string} name - */ -function find_identifiers(source, name) { - return source.getDescendantsOfKind(ts.SyntaxKind.Identifier).filter((i) => i.getText() === name); -} diff --git a/packages/migrate/migrations/svelte-5/migrate.spec.js b/packages/migrate/migrations/svelte-5/migrate.spec.js deleted file mode 100644 index 0f9e3e6d4db6..000000000000 --- a/packages/migrate/migrations/svelte-5/migrate.spec.js +++ /dev/null @@ -1,135 +0,0 @@ -import { assert, test } from 'vitest'; -import { transform_module_code, update_pkg_json_content } from './migrate.js'; - -test('Updates component creation #1', () => { - const result = transform_module_code( - `import App from './App.svelte' - -const app = new App({ - target: document.getElementById('app')! -}) - -export default app` - ); - assert.equal( - result, - `import App from './App.svelte' -import { mount } from "svelte"; - -const app = mount(App, { - target: document.getElementById('app')! -}) - -export default app` - ); -}); - -test('Updates component creation #2', () => { - const result = transform_module_code( - `import App from './App.svelte' - -new App({ - target: document.getElementById('app')!, - hydrate: true -})` - ); - assert.equal( - result, - `import App from './App.svelte' -import { hydrate } from "svelte"; - -hydrate(App, { - target: document.getElementById('app')! -})` - ); -}); - -test('Updates component creation #3', () => { - const result = transform_module_code( - `import App from './App.svelte' - -const x = new App({ - target: document.getElementById('app')! -}); - -function destroy() { - x.$destroy(); -} -` - ); - assert.equal( - result, - `import App from './App.svelte' -import { mount, unmount } from "svelte"; - -const x = mount(App, { - target: document.getElementById('app')! -}); - -function destroy() { - unmount(x); -} -` - ); -}); - -test('Updates component creation with multiple components', () => { - const result = transform_module_code( - `import App from './App.svelte'; -import Child from './Child.svelte'; - -const x = new App({ - target: document.getElementById('app')! -}); -const y = new Child({ - target: document.getElementById('child')! -}); -` - ); - assert.equal( - result, - `import App from './App.svelte'; -import Child from './Child.svelte'; -import { mount } from "svelte"; - -const x = mount(App, { - target: document.getElementById('app')! -}); -const y = mount(Child, { - target: document.getElementById('child')! -}); -` - ); -}); - -test('Update package.json', () => { - const result = update_pkg_json_content(`{ - "name": "svelte-app", - "version": "1.0.0", - "devDependencies": { - "svelte": "^4.0.0", - "svelte-check": "^3.0.0", - "svelte-preprocess": "^5.0.0", - "svelte-eslint-parser": "^0.41.1" - }, - "dependencies": { - "@sveltejs/kit": "^2.0.0" - } -}`); - assert.equal( - result, - `{ - "name": "svelte-app", - "version": "1.0.0", - "devDependencies": { - "svelte": "^5.0.0", - "svelte-check": "^4.0.0", - "svelte-preprocess": "^6.0.0", - "svelte-eslint-parser": "^0.42.0" - }, - "dependencies": { - "@sveltejs/kit": "^2.5.27" - } -}` - ); -}); diff --git a/packages/migrate/migrations/sveltekit-2/index.js b/packages/migrate/migrations/sveltekit-2/index.js deleted file mode 100644 index c62442aba5b2..000000000000 --- a/packages/migrate/migrations/sveltekit-2/index.js +++ /dev/null @@ -1,167 +0,0 @@ -import colors from 'kleur'; -import fs from 'node:fs'; -import process from 'node:process'; -import prompts from 'prompts'; -import semver from 'semver'; -import glob from 'tiny-glob/sync.js'; -import { - bail, - check_git, - update_js_file, - update_svelte_file, - update_tsconfig -} from '../../utils.js'; -import { migrate as migrate_svelte_4 } from '../svelte-4/index.js'; -import { - transform_code, - update_pkg_json, - update_svelte_config, - update_tsconfig_content -} from './migrate.js'; - -export async function migrate() { - if (!fs.existsSync('package.json')) { - bail('Please re-run this script in a directory with a package.json'); - } - - if (!fs.existsSync('svelte.config.js')) { - bail('Please re-run this script in a directory with a svelte.config.js'); - } - - console.log( - colors - .bold() - .yellow( - '\nThis will update files in the current directory\n' + - "If you're inside a monorepo, run this in individual project directories rather than the workspace root.\n" - ) - ); - - const use_git = check_git(); - - const response = await prompts({ - type: 'confirm', - name: 'value', - message: 'Continue?', - initial: false - }); - - if (!response.value) { - process.exit(1); - } - - const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8')); - const svelte_dep = pkg.devDependencies?.svelte ?? pkg.dependencies?.svelte; - if (svelte_dep === undefined) { - bail('Please install Svelte before continuing'); - } - - if (semver.validRange(svelte_dep) && semver.gtr('4.0.0', svelte_dep)) { - console.log( - colors - .bold() - .yellow( - '\nSvelteKit 2 requires Svelte 4 or newer. We recommend running the `svelte-4` migration first (`npx sv migrate svelte-4`).\n' - ) - ); - const response = await prompts({ - type: 'confirm', - name: 'value', - message: 'Run `svelte-4` migration now?', - initial: false - }); - if (!response.value) { - process.exit(1); - } else { - await migrate_svelte_4(); - console.log( - colors - .bold() - .green('`svelte-4` migration complete. Continue with `sveltekit-2` migration?\n') - ); - const response = await prompts({ - type: 'confirm', - name: 'value', - message: 'Continue?', - initial: false - }); - if (!response.value) { - process.exit(1); - } - } - } - - const folders = await prompts({ - type: 'multiselect', - name: 'value', - message: 'Which folders should be migrated?', - choices: fs - .readdirSync('.') - .filter( - (dir) => - fs.statSync(dir).isDirectory() && - dir !== 'node_modules' && - dir !== 'dist' && - !dir.startsWith('.') - ) - .map((dir) => ({ title: dir, value: dir, selected: dir === 'src' })) - }); - - if (!folders.value?.length) { - process.exit(1); - } - - update_pkg_json(); - update_tsconfig(update_tsconfig_content); - update_svelte_config(); - - // const { default: config } = fs.existsSync('svelte.config.js') - // ? await import(pathToFileURL(path.resolve('svelte.config.js')).href) - // : { default: {} }; - - /** @type {string[]} */ - const svelte_extensions = /* config.extensions ?? - disabled because it would break .svx */ [ - '.svelte' - ]; - const extensions = [...svelte_extensions, '.ts', '.js']; - // For some reason {folders.value.join(',')} as part of the glob doesn't work and returns less files - const files = folders.value.flatMap( - /** @param {string} folder */ (folder) => - glob(`${folder}/**`, { filesOnly: true, dot: true }) - .map((file) => file.replace(/\\/g, '/')) - .filter((file) => !file.includes('/node_modules/')) - ); - - for (const file of files) { - if (extensions.some((ext) => file.endsWith(ext))) { - if (svelte_extensions.some((ext) => file.endsWith(ext))) { - update_svelte_file(file, transform_code, (code) => code); - } else { - update_js_file(file, transform_code); - } - } - } - - console.log(colors.bold().green('✔ Your project has been migrated')); - - console.log('\nRecommended next steps:\n'); - - const cyan = colors.bold().cyan; - - const tasks = [ - 'Run npm install (or the corresponding installation command of your package manager)', - use_git && cyan('git commit -m "migration to SvelteKit 2"'), - 'Review the migration guide at https://svelte.dev/docs/kit/migrating-to-sveltekit-2', - 'Read the updated docs at https://svelte.dev/docs/kit' - ].filter(Boolean); - - tasks.forEach((task, i) => { - console.log(` ${i + 1}: ${task}`); - }); - - console.log(''); - - if (use_git) { - console.log(`Run ${cyan('git diff')} to review changes.\n`); - } -} diff --git a/packages/migrate/migrations/sveltekit-2/migrate.js b/packages/migrate/migrations/sveltekit-2/migrate.js deleted file mode 100644 index 9657be37d480..000000000000 --- a/packages/migrate/migrations/sveltekit-2/migrate.js +++ /dev/null @@ -1,318 +0,0 @@ -import fs from 'node:fs'; -import { Project, Node, SyntaxKind } from 'ts-morph'; -import { - add_named_import, - log_migration, - log_on_ts_modification, - update_pkg -} from '../../utils.js'; -import path from 'node:path'; - -export function update_pkg_json() { - fs.writeFileSync( - 'package.json', - update_pkg_json_content(fs.readFileSync('package.json', 'utf8')) - ); -} - -/** - * @param {string} content - */ -export function update_pkg_json_content(content) { - return update_pkg(content, [ - // All other bumps are done as part of the Svelte 4 migration - ['@sveltejs/kit', '^2.0.0'], - ['@sveltejs/adapter-static', '^3.0.0'], - ['@sveltejs/adapter-node', '^2.0.0'], - ['@sveltejs/adapter-vercel', '^4.0.0'], - ['@sveltejs/adapter-netlify', '^3.0.0'], - ['@sveltejs/adapter-cloudflare', '^3.0.0'], - ['@sveltejs/adapter-cloudflare-workers', '^2.0.0'], - ['@sveltejs/adapter-auto', '^3.0.0'], - ['vite', '^5.0.0'], - ['vitest', '^1.0.0'], - ['typescript', '^5.0.0'], // should already be done by Svelte 4 migration, but who knows - [ - '@sveltejs/vite-plugin-svelte', - '^3.0.0', - ' (vite-plugin-svelte is a peer dependency of SvelteKit now)', - 'devDependencies' - ] - ]); -} - -/** @param {string} content */ -export function update_tsconfig_content(content) { - if (!content.includes('"extends"')) { - // Don't touch the tsconfig if people opted out of our default config - return content; - } - - let updated = content - .split('\n') - .filter( - (line) => !line.includes('importsNotUsedAsValues') && !line.includes('preserveValueImports') - ) - .join('\n'); - if (updated !== content) { - log_migration( - 'Removed deprecated `importsNotUsedAsValues` and `preserveValueImports`' + - ' from tsconfig.json: https://svelte.dev/docs/kit/migrating-to-sveltekit-2#updated-dependency-requirements' - ); - } - - content = updated; - updated = content.replace('"moduleResolution": "node"', '"moduleResolution": "bundler"'); - if (updated !== content) { - log_migration( - 'Updated `moduleResolution` to `bundler`' + - ' in tsconfig.json: https://svelte.dev/docs/kit/migrating-to-sveltekit-2#updated-dependency-requirements' - ); - } - - if (content.includes('"paths":') || content.includes('"baseUrl":')) { - log_migration( - '`paths` and/or `baseUrl` detected in your tsconfig.json - remove it and use `kit.alias` instead: https://svelte.dev/docs/kit/migrating-to-sveltekit-2#generated-tsconfig-json-is-more-strict' - ); - } - - return updated; -} - -export function update_svelte_config() { - fs.writeFileSync( - 'svelte.config.js', - update_svelte_config_content(fs.readFileSync('svelte.config.js', 'utf8')) - ); -} - -/** - * @param {string} code - */ -export function update_svelte_config_content(code) { - const regex = /\s*dangerZone:\s*{[^}]*},?/g; - const result = code.replace(regex, ''); - if (result !== code) { - log_migration( - 'Removed `dangerZone` from svelte.config.js: https://svelte.dev/docs/kit/migrating-to-sveltekit-2#server-fetches-are-not-trackable-anymore' - ); - } - - const project = new Project({ useInMemoryFileSystem: true }); - const source = project.createSourceFile('svelte.ts', result); - - const namedImport = get_import(source, '@sveltejs/kit/vite', 'vitePreprocess'); - if (!namedImport) return result; - - const logger = log_on_ts_modification( - source, - 'Changed `vitePreprocess` import: https://svelte.dev/docs/kit/migrating-to-sveltekit-2#vitepreprocess-is-no-longer-exported-from-sveltejs-kit-vite' - ); - - if (namedImport.getParent().getParent().getNamedImports().length === 1) { - namedImport - .getParent() - .getParent() - .getParentIfKind(SyntaxKind.ImportDeclaration) - ?.setModuleSpecifier('@sveltejs/vite-plugin-svelte'); - } else { - namedImport.remove(); - add_named_import(source, '@sveltejs/vite-plugin-svelte', 'vitePreprocess'); - } - - logger(); - return source.getFullText(); -} - -/** - * @param {string} code - * @param {boolean} _is_ts - * @param {string} file_path - */ -export function transform_code(code, _is_ts, file_path) { - const project = new Project({ useInMemoryFileSystem: true }); - const source = project.createSourceFile('svelte.ts', code); - remove_throws(source); - add_cookie_note(file_path, source); - replace_resolve_path(source); - return source.getFullText(); -} - -/** - * `throw redirect(..)` -> `redirect(..)` - * @param {import('ts-morph').SourceFile} source - */ -function remove_throws(source) { - const logger = log_on_ts_modification( - source, - 'Removed `throw` from redirect/error functions: https://svelte.dev/docs/kit/migrating-to-sveltekit-2#redirect-and-error-are-no-longer-thrown-by-you' - ); - - /** @param {string} id */ - function remove_throw(id) { - const named_import = get_import(source, '@sveltejs/kit', id); - if (!named_import) return; - const name_node = named_import.getNameNode(); - if (Node.isIdentifier(name_node)) { - for (const id of name_node.findReferencesAsNodes()) { - const call_expression = id.getParent(); - const throw_stmt = call_expression?.getParent(); - if (Node.isCallExpression(call_expression) && Node.isThrowStatement(throw_stmt)) { - throw_stmt.replaceWithText((writer) => { - writer.setIndentationLevel(0); - writer.write(call_expression.getText() + ';'); - }); - } - } - } - } - - remove_throw('redirect'); - remove_throw('error'); - - logger(); -} - -/** - * Adds `path` option to `cookies.set/delete/serialize` calls - * @param {string} file_path - * @param {import('ts-morph').SourceFile} source - */ -function add_cookie_note(file_path, source) { - const basename = path.basename(file_path); - if ( - basename !== '+page.js' && - basename !== '+page.ts' && - basename !== '+page.server.js' && - basename !== '+page.server.ts' && - basename !== '+server.js' && - basename !== '+server.ts' && - basename !== 'hooks.server.js' && - basename !== 'hooks.server.ts' - ) { - return; - } - - const logger = log_on_ts_modification( - source, - 'Search codebase for `@migration` and manually add the `path` option to `cookies.set/delete/serialize` calls: https://svelte.dev/docs/kit/migrating-to-sveltekit-2#path-is-now-a-required-option-for-cookies' - ); - - const calls = []; - - for (const call of source.getDescendantsOfKind(SyntaxKind.CallExpression)) { - const expression = call.getExpression(); - if (!Node.isPropertyAccessExpression(expression)) { - continue; - } - - const name = expression.getName(); - if (name !== 'set' && name !== 'delete' && name !== 'serialize') { - continue; - } - - if (call.getText().includes('path')) { - continue; - } - - const options_arg = call.getArguments()[name === 'delete' ? 1 : 2]; - if (options_arg && !Node.isObjectLiteralExpression(options_arg)) { - continue; - } - - const parent_function = call.getFirstAncestor( - /** @returns {ancestor is import('ts-morph').FunctionDeclaration | import('ts-morph').FunctionExpression | import('ts-morph').ArrowFunction} */ - (ancestor) => { - // Check if this is inside a function - const fn_declaration = ancestor.asKind(SyntaxKind.FunctionDeclaration); - const fn_expression = ancestor.asKind(SyntaxKind.FunctionExpression); - const arrow_fn_expression = ancestor.asKind(SyntaxKind.ArrowFunction); - return !!fn_declaration || !!fn_expression || !!arrow_fn_expression; - } - ); - if (!parent_function) { - continue; - } - - const expression_text = expression.getExpression().getText(); - if ( - expression_text !== 'cookies' && - (!expression_text.includes('.') || - expression_text.split('.').pop() !== 'cookies' || - !parent_function.getParameter(expression_text.split('.')[0])) - ) { - continue; - } - - const parent = call.getFirstAncestorByKind(SyntaxKind.Block); - if (!parent) { - continue; - } - - calls.push(() => - call.replaceWithText((writer) => { - writer.setIndentationLevel(0); // prevent ts-morph from being unhelpful and adding its own indentation - writer.write('/* @migration task: add path argument */ ' + call.getText()); - }) - ); - } - - for (const call of calls) { - call(); - } - - logger(); -} - -/** - * `resolvePath` from `@sveltejs/kit` -> `resolveRoute` from `$app/paths` - * @param {import('ts-morph').SourceFile} source - */ -function replace_resolve_path(source) { - const named_import = get_import(source, '@sveltejs/kit', 'resolvePath'); - if (!named_import) return; - - const logger = log_on_ts_modification( - source, - 'Replaced `resolvePath` with `resolveRoute`: https://svelte.dev/docs/kit/migrating-to-sveltekit-2#resolvePath-has-been-removed' - ); - - const name_node = named_import.getNameNode(); - if (Node.isIdentifier(name_node)) { - for (const id of name_node.findReferencesAsNodes()) { - id.replaceWithText('resolveRoute'); - } - } - if (named_import.getParent().getParent().getNamedImports().length === 1) { - named_import.getParent().getParent().getParent().remove(); - } else { - named_import.remove(); - } - - const paths_import = source.getImportDeclaration( - (i) => i.getModuleSpecifierValue() === '$app/paths' - ); - if (paths_import) { - paths_import.addNamedImport('resolveRoute'); - } else { - source.addImportDeclaration({ - moduleSpecifier: '$app/paths', - namedImports: ['resolveRoute'] - }); - } - - logger(); -} - -/** - * @param {import('ts-morph').SourceFile} source - * @param {string} from - * @param {string} name - */ -function get_import(source, from, name) { - return source - .getImportDeclarations() - .filter((i) => i.getModuleSpecifierValue() === from) - .flatMap((i) => i.getNamedImports()) - .find((i) => i.getName() === name); -} diff --git a/packages/migrate/migrations/sveltekit-2/migrate.spec.js b/packages/migrate/migrations/sveltekit-2/migrate.spec.js deleted file mode 100644 index a297d947657c..000000000000 --- a/packages/migrate/migrations/sveltekit-2/migrate.spec.js +++ /dev/null @@ -1,32 +0,0 @@ -import { assert, test } from 'vitest'; -import { - transform_code, - update_svelte_config_content, - update_tsconfig_content -} from './migrate.js'; -import { read_samples } from '../../utils.js'; - -for (const sample of read_samples(new URL('./svelte-config-samples.md', import.meta.url))) { - test('svelte.config.js: ' + sample.description, () => { - const actual = update_svelte_config_content(sample.before); - assert.equal(actual, sample.after); - }); -} - -for (const sample of read_samples(new URL('./tsconfig-samples.md', import.meta.url))) { - test('tsconfig.json: ' + sample.description, () => { - const actual = update_tsconfig_content(sample.before); - assert.equal(actual, sample.after); - }); -} - -for (const sample of read_samples(new URL('./tsjs-samples.md', import.meta.url))) { - test('JS/TS file: ' + sample.description, () => { - const actual = transform_code( - sample.before, - sample.filename?.endsWith('.ts') ?? false, - sample.filename ?? '+page.js' - ); - assert.equal(actual, sample.after); - }); -} diff --git a/packages/migrate/migrations/sveltekit-2/svelte-config-samples.md b/packages/migrate/migrations/sveltekit-2/svelte-config-samples.md deleted file mode 100644 index 9a06096a3986..000000000000 --- a/packages/migrate/migrations/sveltekit-2/svelte-config-samples.md +++ /dev/null @@ -1,126 +0,0 @@ -## Removes dangerZone (1) - -```js before -export default { - kit: { - foo: bar, - dangerZone: { - trackServerFetches: true - }, - baz: qux - } -}; -``` - -```js after -export default { - kit: { - foo: bar, - baz: qux - } -}; -``` - -## Removes dangerZone (2) - -```js before -export default { - kit: { - foo: bar, - dangerZone: { - trackServerFetches: true - } - } -}; -``` - - -```js after -export default { - kit: { - foo: bar, - } -}; -``` - -## Replaces vitePreprocess import (1) - -```js before -import adapter from '@sveltejs/adapter-auto'; -import { vitePreprocess } from '@sveltejs/kit/vite'; - -/** @type {import('@sveltejs/kit').Config} */ -const config = { - // Consult https://svelte.dev/docs/kit/integrations#preprocessors - // for more information about preprocessors - preprocess: vitePreprocess(), - - kit: { - adapter: adapter() - } -}; - -export default config; -``` - -```js after -import adapter from '@sveltejs/adapter-auto'; -import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; - -/** @type {import('@sveltejs/kit').Config} */ -const config = { - // Consult https://svelte.dev/docs/kit/integrations#preprocessors - // for more information about preprocessors - preprocess: vitePreprocess(), - - kit: { - adapter: adapter() - } -}; - -export default config; -``` - -## Replaces vitePreprocess import (2) - -```js before -import adapter from '@sveltejs/adapter-auto'; -import { vitePreprocess, foo } from '@sveltejs/kit/vite'; - -export default { - preprocess: vitePreprocess() -}; -``` - - -```js after -import adapter from '@sveltejs/adapter-auto'; -import { foo } from '@sveltejs/kit/vite'; -import { vitePreprocess } from "@sveltejs/vite-plugin-svelte"; - -export default { - preprocess: vitePreprocess() -}; -``` - -## Replaces vitePreprocess import (3) - -```js before -import adapter from '@sveltejs/adapter-auto'; -import { vitePreprocess, foo } from '@sveltejs/kit/vite'; -import { a } from '@sveltejs/vite-plugin-svelte'; - -export default { - preprocess: vitePreprocess() -}; -``` - -```js after -import adapter from '@sveltejs/adapter-auto'; -import { foo } from '@sveltejs/kit/vite'; -import { a, vitePreprocess } from '@sveltejs/vite-plugin-svelte'; - -export default { - preprocess: vitePreprocess() -}; -``` diff --git a/packages/migrate/migrations/sveltekit-2/tsconfig-samples.md b/packages/migrate/migrations/sveltekit-2/tsconfig-samples.md deleted file mode 100644 index 93d357fac974..000000000000 --- a/packages/migrate/migrations/sveltekit-2/tsconfig-samples.md +++ /dev/null @@ -1,40 +0,0 @@ -## Removes importsNotUsedAsValues/preserveValueImports - -```json before -{ - "extends": "./.svelte-kit/tsconfig.json", - "compilerOptions": { - "importsNotUsedAsValues": "error", - "preserveValueImports": true - } -} -``` - - -```json after -{ - "extends": "./.svelte-kit/tsconfig.json", - "compilerOptions": { - } -} -``` - -## Leaves tsconfig alone - -```json before -{ - "compilerOptions": { - "importsNotUsedAsValues": "error", - "preserveValueImports": true - } -} -``` - -```json after -{ - "compilerOptions": { - "importsNotUsedAsValues": "error", - "preserveValueImports": true - } -} -``` diff --git a/packages/migrate/migrations/sveltekit-2/tsjs-samples.md b/packages/migrate/migrations/sveltekit-2/tsjs-samples.md deleted file mode 100644 index 3fed9bf6b95f..000000000000 --- a/packages/migrate/migrations/sveltekit-2/tsjs-samples.md +++ /dev/null @@ -1,170 +0,0 @@ -## Removes throws - -```js before -import { redirect, error } from '@sveltejs/kit'; - -throw redirect(); -redirect(); -throw error(); -error(); -function x() { - let redirect = true; - throw redirect(); -} -``` - -```js after -import { redirect, error } from '@sveltejs/kit'; - -redirect(); -redirect(); -error(); -error(); -function x() { - let redirect = true; - throw redirect(); -} -``` - -## Leaves redirect/error from other sources alone - -```js before -import { redirect, error } from 'somewhere-else'; - -throw redirect(); -redirect(); -throw error(); -error(); -``` - -```js after -import { redirect, error } from 'somewhere-else'; - -throw redirect(); -redirect(); -throw error(); -error(); -``` - -## Notes cookie migration - -```js before -export function load({ cookies }) { - cookies.set('foo', 'bar'); -} -``` - -```js after -export function load({ cookies }) { - /* @migration task: add path argument */ cookies.set('foo', 'bar'); -} -``` - -## Notes cookie migration with multiple occurences - -```js before -export function load({ cookies }) { - cookies.delete('foo'); - cookies.set('x', 'y', { z: '' }); -} -``` - -```js after -export function load({ cookies }) { - /* @migration task: add path argument */ cookies.delete('foo'); - /* @migration task: add path argument */ cookies.set('x', 'y', { z: '' }); -} -``` - -## Handles non-destructured argument - -```js before -export function load(event) { - event.cookies.set('x', 'y'); -} -``` - -```js after -export function load(event) { - /* @migration task: add path argument */ event.cookies.set('x', 'y'); -} -``` - -## Recognizes cookies false positives - -```js before -export function load({ cookies }) { - cookies.set('foo', 'bar', { path: '/' }); -} - -export function foo(event) { - x.cookies.set('foo', 'bar'); -} - -export function bar(event) { - event.x.set('foo', 'bar'); -} - -cookies.set('foo', 'bar'); -``` - -```js after -export function load({ cookies }) { - cookies.set('foo', 'bar', { path: '/' }); -} - -export function foo(event) { - x.cookies.set('foo', 'bar'); -} - -export function bar(event) { - event.x.set('foo', 'bar'); -} - -cookies.set('foo', 'bar'); -``` - -## Replaces resolvePath - -```js before -import { resolvePath } from '@sveltejs/kit'; - -resolvePath('x', y); -``` - - -```js after -import { resolveRoute } from "$app/paths"; - -resolveRoute('x', y); -``` - -## Replaces resolvePath taking care of imports - -```js before -import { resolvePath, x } from '@sveltejs/kit'; -import { y } from '$app/paths'; - -resolvePath('x'); -``` - -```js after -import { x } from '@sveltejs/kit'; -import { y, resolveRoute } from '$app/paths'; - -resolveRoute('x'); -``` - -## Doesn't replace resolvePath from other sources - -```js before -import { resolvePath } from 'x'; - -resolvePath('x'); -``` - -```js after -import { resolvePath } from 'x'; - -resolvePath('x'); -``` diff --git a/packages/migrate/package.json b/packages/migrate/package.json deleted file mode 100644 index be1a716729fb..000000000000 --- a/packages/migrate/package.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "name": "svelte-migrate", - "version": "1.6.8", - "description": "A CLI for migrating Svelte(Kit) codebases", - "keywords": [ - "migration", - "upgrade", - "svelte", - "sveltekit", - "tool" - ], - "repository": { - "type": "git", - "url": "https://github.com/sveltejs/kit", - "directory": "packages/migrate" - }, - "license": "MIT", - "homepage": "https://svelte.dev", - "type": "module", - "bin": { - "svelte-migrate": "./bin.js" - }, - "files": [ - "bin.js", - "migrations", - "utils.js", - "!migrations/**/*.spec.js", - "!migrations/**/samples.md" - ], - "dependencies": { - "import-meta-resolve": "^4.1.0", - "kleur": "^4.1.5", - "magic-string": "^0.30.5", - "prompts": "^2.4.2", - "semver": "^7.5.4", - "tiny-glob": "^0.2.9", - "ts-morph": "^24.0.0", - "typescript": "^5.3.3", - "zimmerframe": "^1.1.2" - }, - "devDependencies": { - "@types/node": "^18.19.48", - "@types/prompts": "^2.4.9", - "@types/semver": "^7.5.6", - "svelte": "^4.2.10", - "vitest": "^2.0.1" - }, - "scripts": { - "test": "vitest run --silent", - "check": "tsc", - "lint": "prettier --check .", - "format": "pnpm lint --write" - } -} diff --git a/packages/migrate/tsconfig.json b/packages/migrate/tsconfig.json deleted file mode 100644 index 26885cff272f..000000000000 --- a/packages/migrate/tsconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "compilerOptions": { - "allowJs": true, - "checkJs": true, - "noEmit": true, - "strict": true, - "target": "es2022", - "module": "node16", - "moduleResolution": "node16", - "allowSyntheticDefaultImports": true - } -} diff --git a/packages/migrate/utils.js b/packages/migrate/utils.js deleted file mode 100644 index 944461f02a78..000000000000 --- a/packages/migrate/utils.js +++ /dev/null @@ -1,421 +0,0 @@ -import colors from 'kleur'; -import MagicString from 'magic-string'; -import { execFileSync, execSync } from 'node:child_process'; -import fs from 'node:fs'; -import path from 'node:path'; -import process from 'node:process'; -import semver from 'semver'; -import ts from 'typescript'; - -/** @param {string} message */ -export function bail(message) { - console.error(colors.bold().red(message)); - process.exit(1); -} - -/** @param {string} file */ -export function relative(file) { - return path.relative('.', file); -} -/** - * - * @param {string} file - * @param {string} renamed - * @param {string} content - * @param {boolean} use_git - */ -export function move_file(file, renamed, content, use_git) { - if (use_git) { - execFileSync('git', ['mv', file, renamed]); - } else { - fs.unlinkSync(file); - } - - fs.writeFileSync(renamed, content); -} - -/** - * @param {string} contents - * @param {string} indent - */ -export function comment(contents, indent) { - return contents.replace(new RegExp(`^${indent}`, 'gm'), `${indent}// `); -} - -/** @param {string} content */ -export function dedent(content) { - const indent = guess_indent(content); - if (!indent) return content; - - /** @type {string[]} */ - const substitutions = []; - - try { - const ast = ts.createSourceFile( - 'filename.ts', - content, - ts.ScriptTarget.Latest, - true, - ts.ScriptKind.TS - ); - - const code = new MagicString(content); - - /** @param {ts.Node} node */ - function walk(node) { - if (ts.isTemplateLiteral(node)) { - let pos = node.pos; - while (/\s/.test(content[pos])) pos += 1; - - code.overwrite(pos, node.end, `____SUBSTITUTION_${substitutions.length}____`); - substitutions.push(node.getText()); - } - - node.forEachChild(walk); - } - - ast.forEachChild(walk); - - return code - .toString() - .replace(new RegExp(`^${indent}`, 'gm'), '') - .replace(/____SUBSTITUTION_(\d+)____/g, (match, index) => substitutions[index]); - } catch { - // as above — ignore this edge case - return content; - } -} - -/** @param {string} content */ -export function guess_indent(content) { - const lines = content.split('\n'); - - const tabbed = lines.filter((line) => /^\t+/.test(line)); - const spaced = lines.filter((line) => /^ {2,}/.test(line)); - - if (tabbed.length === 0 && spaced.length === 0) { - return null; - } - - // More lines tabbed than spaced? Assume tabs, and - // default to tabs in the case of a tie (or nothing - // to go on) - if (tabbed.length >= spaced.length) { - return '\t'; - } - - // Otherwise, we need to guess the multiple - const min = spaced.reduce((previous, current) => { - const count = /^ +/.exec(current)?.[0].length ?? 0; - return Math.min(count, previous); - }, Infinity); - - return ' '.repeat(min); -} - -/** - * @param {string} content - * @param {number} offset - */ -export function indent_at_line(content, offset) { - const substr = content.substring(content.lastIndexOf('\n', offset) + 1, offset); - return /\s*/.exec(substr)?.[0] ?? ''; -} - -/** - * @param {string} content - * @param {string} except - */ -export function except_str(content, except) { - const start = content.indexOf(except); - const end = start + except.length; - return content.substring(0, start) + content.substring(end); -} - -/** - * @returns {boolean} True if git is installed - */ -export function check_git() { - let use_git = false; - - let dir = process.cwd(); - do { - if (fs.existsSync(path.join(dir, '.git'))) { - use_git = true; - break; - } - } while (dir !== (dir = path.dirname(dir))); - - if (use_git) { - try { - const status = execSync('git status --porcelain', { stdio: 'pipe' }).toString(); - - if (status) { - const message = - 'Your git working directory is dirty — we recommend committing your changes before running this migration.\n'; - console.log(colors.bold().red(message)); - } - } catch { - // would be weird to have a .git folder if git is not installed, - // but always expect the unexpected - const message = - 'Could not detect a git installation. If this is unexpected, please raise an issue: https://github.com/sveltejs/kit.\n'; - console.log(colors.bold().red(message)); - use_git = false; - } - } - - return use_git; -} - -/** - * Get a list of all files in a directory - * @param {string} cwd - the directory to walk - * @param {boolean} [dirs] - whether to include directories in the result - */ -export function walk(cwd, dirs = false) { - /** @type {string[]} */ - const all_files = []; - - /** @param {string} dir */ - function walk_dir(dir) { - const files = fs.readdirSync(path.join(cwd, dir)); - - for (const file of files) { - const joined = path.join(dir, file); - const stats = fs.statSync(path.join(cwd, joined)); - if (stats.isDirectory()) { - if (dirs) all_files.push(joined); - walk_dir(joined); - } else { - all_files.push(joined); - } - } - } - - return walk_dir(''), all_files; -} - -/** @param {string} str */ -export function posixify(str) { - return str.replace(/\\/g, '/'); -} - -/** - * @param {string} content - * @param {Array<[string, string, string?, ('dependencies' | 'devDependencies')?]>} updates - */ -export function update_pkg(content, updates) { - const indent = content.split('\n')[1].match(/^\s+/)?.[0] || ' '; - const pkg = JSON.parse(content); - - /** - * @param {string} name - * @param {string} version - * @param {string} [additional] - * @param {'dependencies' | 'devDependencies' | undefined} [insert] - */ - function update_pkg(name, version, additional = '', insert) { - /** - * @param {string} type - */ - const updateVersion = (type) => { - const existingRange = pkg[type]?.[name]; - - if ( - existingRange && - semver.validRange(existingRange) && - !semver.subset(existingRange, version) - ) { - // Check if the new version range is an upgrade - const minExistingVersion = semver.minVersion(existingRange); - const minNewVersion = semver.minVersion(version); - - if (minExistingVersion && minNewVersion && semver.gt(minNewVersion, minExistingVersion)) { - log_migration(`Updated ${name} to ${version}`); - pkg[type][name] = version; - } - } - }; - - updateVersion('dependencies'); - updateVersion('devDependencies'); - - if (insert && !pkg[insert]?.[name]) { - if (!pkg[insert]) pkg[insert] = {}; - - // Insert the property in sorted position without adjusting other positions so diffs are easier to read - const sorted_keys = Object.keys(pkg[insert]).sort(); - const index = sorted_keys.findIndex((key) => name.localeCompare(key) === -1); - const insert_index = index !== -1 ? index : sorted_keys.length; - const new_properties = Object.entries(pkg[insert]); - new_properties.splice(insert_index, 0, [name, version]); - pkg[insert] = Object.fromEntries(new_properties); - - log_migration(`Added ${name} version ${version} ${additional}`); - } - } - - for (const update of updates) { - update_pkg(...update); - } - - const result = JSON.stringify(pkg, null, indent); - if (content.endsWith('\n')) return result + '\n'; - return result; -} - -const logged_migrations = new Set(); - -/** - * @param {import('ts-morph').SourceFile} source - * @param {string} text - */ -export function log_on_ts_modification(source, text) { - let logged = false; - const log = () => { - if (!logged) { - logged = true; - log_migration(text); - } - }; - source.onModified(log); - return () => source.onModified(log, false); -} - -/** @param {string} text */ -export function log_migration(text) { - if (logged_migrations.has(text)) return; - console.log(text); - logged_migrations.add(text); -} - -/** - * Parses the scripts contents and invoked `transform_script_code` with it, then runs the result through `transform_svelte_code`. - * The result is written back to disk. - * @param {string} file_path - * @param {(code: string, is_ts: boolean, file_path: string) => string} transform_script_code - * @param {(code: string, file_path: string) => string} transform_svelte_code - */ -export function update_svelte_file(file_path, transform_script_code, transform_svelte_code) { - try { - const content = fs.readFileSync(file_path, 'utf-8'); - const updated = content.replace( - /([^]+?)<\/script>(\n*)/g, - (_match, attrs, contents, whitespace) => { - return `${transform_script_code( - contents, - (attrs.includes('lang=') || attrs.includes('type=')) && - (attrs.includes('ts') || attrs.includes('typescript')), - file_path - )}${whitespace}`; - } - ); - fs.writeFileSync(file_path, transform_svelte_code(updated, file_path), 'utf-8'); - } catch (err) { - // TODO: change to import('svelte/compiler').Warning after upgrading to Svelte 5 - const e = /** @type {any} */ (err); - console.warn(buildExtendedLogMessage(e), e.frame); - console.info(e.stack); - } -} - -/** - * Reads the file and invokes `transform_code` with its contents. The result is written back to disk. - * @param {string} file_path - * @param {(code: string, is_ts: boolean, file_path: string) => string} transform_code - */ -export function update_js_file(file_path, transform_code) { - try { - const content = fs.readFileSync(file_path, 'utf-8'); - const updated = transform_code(content, file_path.endsWith('.ts'), file_path); - fs.writeFileSync(file_path, updated, 'utf-8'); - } catch (err) { - // TODO: change to import('svelte/compiler').Warning after upgrading to Svelte 5 - const e = /** @type {any} */ (err); - console.warn(buildExtendedLogMessage(e), e.frame); - console.info(e.stack); - } -} - -/** - * @param {any} w - */ -export function buildExtendedLogMessage(w) { - const parts = []; - if (w.filename) { - parts.push(w.filename); - } - if (w.start) { - parts.push(':', w.start.line, ':', w.start.column); - } - if (w.message) { - if (parts.length > 0) { - parts.push(' '); - } - parts.push(w.message); - } - return parts.join(''); -} - -/** - * Updates the tsconfig/jsconfig.json file with the provided function. - * @param {(content: string) => string} update_tsconfig_content - */ -export function update_tsconfig(update_tsconfig_content) { - const file = fs.existsSync('tsconfig.json') - ? 'tsconfig.json' - : fs.existsSync('jsconfig.json') - ? 'jsconfig.json' - : null; - if (file) { - fs.writeFileSync(file, update_tsconfig_content(fs.readFileSync(file, 'utf8'))); - } -} - -/** @param {string | URL} test_file */ -export function read_samples(test_file) { - const markdown = fs.readFileSync(test_file, 'utf8').replaceAll('\r\n', '\n'); - const samples = markdown - .split(/^##/gm) - .slice(1) - .map((block) => { - const description = block.split('\n')[0]; - const before = /```(js|ts|svelte) before\n([^]*?)\n```/.exec(block); - const after = /```(js|ts|svelte) after\n([^]*?)\n```/.exec(block); - - const match = /> file: (.+)/.exec(block); - - return { - description, - before: before ? before[2] : '', - after: after ? after[2] : '', - filename: match?.[1], - solo: block.includes('> solo') - }; - }); - - if (samples.some((sample) => sample.solo)) { - return samples.filter((sample) => sample.solo); - } - - return samples; -} - -/** - * @param {import('ts-morph').SourceFile} source - * @param {string} _import - * @param {string} method - */ -export function add_named_import(source, _import, method) { - const existing = source.getImportDeclaration(_import); - if (existing) { - if (existing.getNamedImports().some((i) => i.getName() === method)) return; - existing?.addNamedImport(method); - } else { - source.addImportDeclaration({ - moduleSpecifier: _import, - namedImports: [method] - }); - } -} diff --git a/packages/migrate/utils.spec.js b/packages/migrate/utils.spec.js deleted file mode 100644 index a75641e6055f..000000000000 --- a/packages/migrate/utils.spec.js +++ /dev/null @@ -1,93 +0,0 @@ -import { assert, test } from 'vitest'; -import { update_pkg } from './utils.js'; - -test('Inserts package at correct position (1)', () => { - const result = update_pkg( - `{ - "dependencies": { - "a": "1", - "z": "3", - "c": "4" - } -}`, - [['b', '2', '', 'dependencies']] - ); - - assert.equal( - result, - `{ - "dependencies": { - "a": "1", - "b": "2", - "z": "3", - "c": "4" - } -}` - ); -}); - -test('Inserts package at correct position (2)', () => { - const result = update_pkg( - `{ - "dependencies": { - "a": "1", - "b": "2" - } -}`, - [['c', '3', '', 'dependencies']] - ); - - assert.equal( - result, - `{ - "dependencies": { - "a": "1", - "b": "2", - "c": "3" - } -}` - ); -}); - -test('Inserts package at correct position (3)', () => { - const result = update_pkg( - `{ - "dependencies": { - "b": "2", - "c": "3" - } -}`, - [['a', '1', '', 'dependencies']] - ); - - assert.equal( - result, - `{ - "dependencies": { - "a": "1", - "b": "2", - "c": "3" - } -}` - ); -}); - -test('Does not downgrade versions', () => { - const result = update_pkg( - `{ - "devDependencies": { - "@sveltejs/kit": "^2.4.3" - } -}`, - [['@sveltejs/kit', '^2.0.0']] - ); - - assert.equal( - result, - `{ - "devDependencies": { - "@sveltejs/kit": "^2.4.3" - } -}` - ); -}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0a360411ab50..a9eb3d780d8b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1028,52 +1028,6 @@ importers: specifier: ^2.0.1 version: 2.0.1(@types/node@18.19.50)(lightningcss@1.24.1) - packages/migrate: - dependencies: - import-meta-resolve: - specifier: ^4.1.0 - version: 4.1.0 - kleur: - specifier: ^4.1.5 - version: 4.1.5 - magic-string: - specifier: ^0.30.5 - version: 0.30.11 - prompts: - specifier: ^2.4.2 - version: 2.4.2 - semver: - specifier: ^7.5.4 - version: 7.6.3 - tiny-glob: - specifier: ^0.2.9 - version: 0.2.9 - ts-morph: - specifier: ^24.0.0 - version: 24.0.0 - typescript: - specifier: ^5.3.3 - version: 5.4.5 - zimmerframe: - specifier: ^1.1.2 - version: 1.1.2 - devDependencies: - '@types/node': - specifier: ^18.19.48 - version: 18.19.50 - '@types/prompts': - specifier: ^2.4.9 - version: 2.4.9 - '@types/semver': - specifier: ^7.5.6 - version: 7.5.8 - svelte: - specifier: ^4.2.10 - version: 4.2.19 - vitest: - specifier: ^2.0.1 - version: 2.0.1(@types/node@18.19.50)(lightningcss@1.24.1) - packages/package: dependencies: chokidar: @@ -1956,9 +1910,6 @@ packages: resolution: {integrity: sha512-qhUGGDHcpbY2zpjW3SwqchuW8J/5EzlPFud7xNntHKA7f3a/mx5+g+ruJKFHSAiVZYo30PALt+AyhmPUNKH/Og==} engines: {node: ^14.13.1 || ^16.0.0 || >=18} - '@ts-morph/common@0.25.0': - resolution: {integrity: sha512-kMnZz+vGGHi4GoHnLmMhGNjm44kGtKUXGnOvrKmMwAuvNjM/PgKVGfUnL7IDvK7Jb2QQ82jq3Zmp04Gy+r3Dkg==} - '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} @@ -1983,9 +1934,6 @@ packages: '@types/node@18.19.50': resolution: {integrity: sha512-xonK+NRrMBRtkL1hVCc3G+uXtjh1Al4opBLjqVmipe5ZAaBYWW6cNAiBVZ1BvmkBhep698rP3UM3aRAdSALuhg==} - '@types/prompts@2.4.9': - resolution: {integrity: sha512-qTxFi6Buiu8+50/+3DGIWLHM6QuWsEKugJnnP6iv2Mc4ncxE4A/OJkjuVOA+5X0X1S/nq5VJRa8Lu+nwcvbrKA==} - '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} @@ -2234,9 +2182,6 @@ packages: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} - code-block-writer@13.0.3: - resolution: {integrity: sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==} - code-red@1.0.4: resolution: {integrity: sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==} @@ -2818,10 +2763,6 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - kleur@3.0.3: - resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} - engines: {node: '>=6'} - kleur@4.1.5: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} @@ -3136,9 +3077,6 @@ packages: pascal-case@3.1.2: resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} - path-browserify@1.0.1: - resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} - path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -3263,10 +3201,6 @@ packages: printable-characters@1.0.42: resolution: {integrity: sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==} - prompts@2.4.2: - resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} - engines: {node: '>= 6'} - pseudomap@1.0.2: resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} @@ -3429,9 +3363,6 @@ packages: resolution: {integrity: sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==} engines: {node: '>=18'} - sisteransi@1.0.5: - resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -3606,10 +3537,6 @@ packages: resolution: {integrity: sha512-7cR8rLy2QhYHpsBDBVYnnWXm8uRTr38RoZakFSW7Bs7PzfMPNZthuMLkwqZv7MTu8lhQ91cOFYS5a7iFj2oR3w==} engines: {node: '>=4'} - tinyglobby@0.2.9: - resolution: {integrity: sha512-8or1+BGEdk1Zkkw2ii16qSS7uVrQJPre5A9o/XkWPATkk23FZh/15BKFxPnlTy6vkljZxLqYCzzBMj30ZrSvjw==} - engines: {node: '>=12.0.0'} - tinypool@1.0.0: resolution: {integrity: sha512-KIKExllK7jp3uvrNtvRBYBWBOAXSX8ZvoaD8T+7KB/QHIuoJW3Pmr60zucywjAlMb5TeXUkcs/MWeWLu0qvuAQ==} engines: {node: ^18.0.0 || >=20.0.0} @@ -3643,9 +3570,6 @@ packages: peerDependencies: typescript: '>=4.2.0' - ts-morph@24.0.0: - resolution: {integrity: sha512-2OAOg/Ob5yx9Et7ZX4CvTCc0UFoZHwLEJ+dpDPSUi5TgwwlTlX47w+iFRrEwzUZwYACjq83cgjS/Da50Ga37uw==} - tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} @@ -4583,12 +4507,6 @@ snapshots: transitivePeerDependencies: - encoding - '@ts-morph/common@0.25.0': - dependencies: - minimatch: 9.0.5 - path-browserify: 1.0.1 - tinyglobby: 0.2.9 - '@types/connect@3.4.38': dependencies: '@types/node': 18.19.50 @@ -4614,11 +4532,6 @@ snapshots: dependencies: undici-types: 5.26.5 - '@types/prompts@2.4.9': - dependencies: - '@types/node': 18.19.50 - kleur: 3.0.3 - '@types/resolve@1.20.2': {} '@types/semver@7.5.8': {} @@ -4906,8 +4819,6 @@ snapshots: ci-info@3.9.0: {} - code-block-writer@13.0.3: {} - code-red@1.0.4: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 @@ -5529,8 +5440,6 @@ snapshots: dependencies: json-buffer: 3.0.1 - kleur@3.0.3: {} - kleur@4.1.5: {} known-css-properties@0.34.0: {} @@ -5803,8 +5712,6 @@ snapshots: no-case: 3.0.4 tslib: 2.6.2 - path-browserify@1.0.1: {} - path-exists@4.0.0: {} path-is-absolute@1.0.1: {} @@ -5895,11 +5802,6 @@ snapshots: printable-characters@1.0.42: {} - prompts@2.4.2: - dependencies: - kleur: 3.0.3 - sisteransi: 1.0.5 - pseudomap@1.0.2: {} publint@0.2.7: @@ -6085,8 +5987,6 @@ snapshots: mrmime: 2.0.0 totalist: 3.0.1 - sisteransi@1.0.5: {} - slash@3.0.0: {} source-map-js@1.2.1: {} @@ -6251,11 +6151,6 @@ snapshots: tinydate@1.3.0: {} - tinyglobby@0.2.9: - dependencies: - fdir: 6.4.0(picomatch@4.0.2) - picomatch: 4.0.2 - tinypool@1.0.0: {} tinyspy@3.0.0: {} @@ -6284,11 +6179,6 @@ snapshots: dependencies: typescript: 5.6.3 - ts-morph@24.0.0: - dependencies: - '@ts-morph/common': 0.25.0 - code-block-writer: 13.0.3 - tslib@2.6.2: {} type-check@0.4.0: