UNPKG

@reliverse/rse

Version:

@reliverse/rse is your all-in-one companion for bootstrapping and improving any kind of projects (especially web apps built with frameworks like Next.js) — whether you're kicking off something new or upgrading an existing app. It is also a little AI-power

106 lines (105 loc) 3.56 kB
import path from "@reliverse/pathkit"; import fs from "@reliverse/relifso"; import { relinka } from "@reliverse/relinka"; import { readPackageJSON, writePackageJSON } from "pkg-types"; import { tsconfigJson } from "../../constants.js"; function generateTypeDefinitions(content) { let result = content; result = result.replace( /function\s+(\w+)\s*\((.*?)\)/g, (_, name, params) => { const typedParams = params.split(",").map((p) => p.trim()).filter(Boolean).map((p) => `${p}: any`); return `function ${name}(${typedParams.join(", ")})`; } ); result = result.replace( /function\s+(\w+)\s*\((.*?)\)\s*{/g, (match) => `${match}: any` ); result = result.replace( /(const|let|var)\s+(\w+)\s*=/g, (_, dec, name) => `${dec} ${name}: any =` ); result = result.replace( /class\s+(\w+)\s*{([^}]+)}/g, (_, name, body) => { const typedBody = body.replace( /(\w+)\s*=/g, (_2, propName) => `${propName}: any =` ); return `class ${name} {${typedBody}}`; } ); result = result.replace( /const\s+(\w+)\s*=\s*{([^}]+)}/g, (_, name, props) => { const interfaceProps = props.split(",").map((p) => p.trim()).filter(Boolean).map((p) => { const [propName] = p.split(":"); return ` ${propName}: any;`; }); return `interface ${name}Type { ${interfaceProps.join("\n")} } const ${name}: ${name}Type = {${props}}`; } ); return result; } export async function convertJsToTs(cwd) { const jsFiles = await fs.readdir(cwd, { recursive: true }); const jsFilePaths = jsFiles.filter((file) => typeof file === "string").filter( (file) => file.endsWith(".js") && !file.endsWith(".config.js") && !file.endsWith(".test.js") && !file.includes("node_modules") ); for (const jsFile of jsFilePaths) { const fullPath = path.join(cwd, jsFile); const tsFile = jsFile.replace(/\.js$/, ".ts"); const tsPath = path.join(cwd, tsFile); try { const content = await fs.readFile(fullPath, "utf-8"); const tsContent = generateTypeDefinitions(content); await fs.writeFile(tsPath, tsContent); relinka("success", `Converted ${jsFile} to TypeScript`); await fs.remove(fullPath); } catch (error) { relinka( "error", `Failed to convert ${jsFile}: ${error instanceof Error ? error.message : String(error)}` ); } } const tsconfigPath = path.join(cwd, tsconfigJson); if (!await fs.pathExists(tsconfigPath)) { const tsconfig = { compilerOptions: { target: "ES2020", module: "ESNext", moduleResolution: "node", esModuleInterop: true, strict: true, skipLibCheck: true, forceConsistentCasingInFileNames: true, outDir: "dist" }, include: ["src/**/*"], exclude: ["node_modules", "dist"] }; await fs.writeJson(tsconfigPath, tsconfig, { spaces: 2 }); relinka("success", "Created tsconfig.json"); } const packageJsonPath = path.join(cwd, "package.json"); if (await fs.pathExists(packageJsonPath)) { const packageJson = await readPackageJSON(packageJsonPath); packageJson.devDependencies = { ...packageJson.devDependencies, typescript: "latest", "@types/node": "latest" }; packageJson.scripts = { ...packageJson.scripts, build: "tsc", "type-check": "tsc --noEmit" }; await writePackageJSON(packageJsonPath, packageJson); relinka("success", "Updated package.json with TypeScript configuration"); } }