UNPKG

svelte-migrate

Version:

A CLI for migrating Svelte(Kit) codebases

131 lines (116 loc) 3.83 kB
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/package', '^2.3.7'], ['@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); }