diff --git a/scripts/create-type-stubs.mjs b/scripts/create-type-stubs.mjs deleted file mode 100644 index 2176ee5..0000000 --- a/scripts/create-type-stubs.mjs +++ /dev/null @@ -1,137 +0,0 @@ -#!/usr/bin/env node -/** - * Analyzes TypeScript errors and creates stub modules with proper named exports. - * Run: node scripts/create-type-stubs.mjs - */ -import { execSync } from 'child_process'; -import { writeFileSync, existsSync, mkdirSync } from 'fs'; -import { dirname, join } from 'path'; - -const ROOT = '/Users/konghayao/code/ai/claude-code'; - -// Run tsc and capture errors (tsc exits non-zero on type errors, that's expected) -let errors; -try { - errors = execSync('npx tsc --noEmit 2>&1', { encoding: 'utf-8', cwd: ROOT }); -} catch (e) { - errors = e.stdout || ''; -} - -// Map: resolved file path -> Set of needed named exports -const stubExports = new Map(); -// Map: resolved file path -> Set of needed default export names -const defaultExports = new Map(); - -for (const line of errors.split('\n')) { - // TS2614: Module '"X"' has no exported member 'Y'. Did you mean to use 'import Y from "X"' instead? - let m = line.match(/error TS2614: Module '"(.+?)"' has no exported member '(.+?)'\. Did you mean to use 'import .* from/); - if (m) { - const [, mod, member] = m; - if (!defaultExports.has(mod)) defaultExports.set(mod, new Set()); - defaultExports.get(mod).add(member); - continue; - } - - // TS2305: Module '"X"' has no exported member 'Y' - m = line.match(/error TS2305: Module '"(.+?)"' has no exported member '(.+?)'/); - if (m) { - const [, mod, member] = m; - if (!stubExports.has(mod)) stubExports.set(mod, new Set()); - stubExports.get(mod).add(member); - } - - // TS2724: '"X"' has no exported member named 'Y'. Did you mean 'Z'? - m = line.match(/error TS2724: '"(.+?)"' has no exported member named '(.+?)'/); - if (m) { - const [, mod, member] = m; - if (!stubExports.has(mod)) stubExports.set(mod, new Set()); - stubExports.get(mod).add(member); - } - - // TS2306: File 'X' is not a module - m = line.match(/error TS2306: File '(.+?)' is not a module/); - if (m) { - const filePath = m[1]; - if (!stubExports.has(filePath)) stubExports.set(filePath, new Set()); - } - - // TS2307: Cannot find module 'X' - m = line.match(/^(.+?)\(\d+,\d+\): error TS2307: Cannot find module '(.+?)'/); - if (m) { - const [srcFile, mod] = [m[1], m[2]]; - if (mod.endsWith('.md')) continue; - if (!mod.startsWith('.') && !mod.startsWith('src/')) continue; - // Will be resolved below - const srcDir = dirname(srcFile); - const resolved = join(ROOT, srcDir, mod).replace(/\.js$/, '.ts'); - if (resolved.startsWith(ROOT + '/') && !existsSync(resolved)) { - if (!stubExports.has(resolved)) stubExports.set(resolved, new Set()); - } - } -} - -// Also parse actual import statements from source files to find what's needed -import { readFileSync } from 'fs'; -const allSourceFiles = execSync('find src -name "*.ts" -o -name "*.tsx"', { encoding: 'utf-8', cwd: ROOT }).trim().split('\n'); - -for (const file of allSourceFiles) { - const content = readFileSync(join(ROOT, file), 'utf-8'); - const srcDir = dirname(file); - - // Find all import { X, Y } from 'module' - const importRegex = /import\s+(?:type\s+)?\{([^}]+)\}\s+from\s+['"](.+?)['"]/g; - let match; - while ((match = importRegex.exec(content)) !== null) { - const members = match[1].split(',').map(s => s.trim().split(/\s+as\s+/)[0].trim()).filter(Boolean); - let mod = match[2]; - if (!mod.startsWith('.') && !mod.startsWith('src/')) continue; - - const resolved = join(ROOT, srcDir, mod).replace(/\.js$/, '.ts'); - if (resolved.startsWith(ROOT + '/') && !existsSync(resolved)) { - if (!stubExports.has(resolved)) stubExports.set(resolved, new Set()); - for (const member of members) { - stubExports.get(resolved).add(member); - } - } - } -} - -// Now create/update all stub files -let created = 0; -for (const [filePath, exports] of stubExports) { - const relPath = filePath.replace(ROOT + '/', ''); - const dir = dirname(filePath); - - if (!existsSync(dir)) { - mkdirSync(dir, { recursive: true }); - } - - const lines = ['// Auto-generated type stub — replace with real implementation']; - - for (const exp of exports) { - lines.push(`export type ${exp} = any;`); - } - - // Check if there are default exports needed - for (const [mod, defs] of defaultExports) { - // Match the module path - const modNorm = mod.replace(/\.js$/, '').replace(/^src\//, ''); - const filePathNorm = relPath.replace(/\.ts$/, ''); - if (modNorm === filePathNorm || mod === relPath) { - for (const def of defs) { - lines.push(`export type ${def} = any;`); - } - } - } - - // Ensure at least export {} - if (exports.size === 0) { - lines.push('export {};'); - } - - writeFileSync(filePath, lines.join('\n') + '\n'); - created++; -} - -console.log(`Created/updated ${created} stub files`); -console.log(`Total named exports resolved: ${[...stubExports.values()].reduce((a, b) => a + b.size, 0)}`); diff --git a/scripts/fix-default-stubs.mjs b/scripts/fix-default-stubs.mjs deleted file mode 100644 index 009bcc6..0000000 --- a/scripts/fix-default-stubs.mjs +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/env node -/** - * Finds all stub files with `export default {} as any` and rewrites them - * with proper named exports based on what the source code actually imports. - */ -import { execSync } from 'child_process'; -import { readFileSync, writeFileSync, existsSync } from 'fs'; -import { dirname, join, relative, resolve } from 'path'; - -const ROOT = '/Users/konghayao/code/ai/claude-code'; - -// Step 1: Find all stub files with only `export default {} as any` -const stubFiles = new Set(); -const allTsFiles = execSync('find src -name "*.ts" -o -name "*.tsx"', { - encoding: 'utf-8', cwd: ROOT -}).trim().split('\n'); - -for (const f of allTsFiles) { - const fullPath = join(ROOT, f); - const content = readFileSync(fullPath, 'utf-8').trim(); - if (content === 'export default {} as any') { - stubFiles.add(f); // relative path like src/types/message.ts - } -} - -console.log(`Found ${stubFiles.size} stub files with 'export default {} as any'`); - -// Step 2: Scan all source files for imports from these stub modules -// Map: stub file path -> { types: Set, values: Set } -const stubNeeds = new Map(); -for (const sf of stubFiles) { - stubNeeds.set(sf, { types: new Set(), values: new Set() }); -} - -// Helper: resolve an import path from a source file to a stub file -function resolveImport(srcFile, importPath) { - // Handle src/ prefix imports - if (importPath.startsWith('src/')) { - const resolved = importPath.replace(/\.js$/, '.ts'); - if (stubFiles.has(resolved)) return resolved; - return null; - } - // Handle relative imports - if (importPath.startsWith('.')) { - const srcDir = dirname(srcFile); - const resolved = join(srcDir, importPath).replace(/\.js$/, '.ts'); - if (stubFiles.has(resolved)) return resolved; - // Try .tsx - const resolvedTsx = join(srcDir, importPath).replace(/\.js$/, '.tsx'); - if (stubFiles.has(resolvedTsx)) return resolvedTsx; - return null; - } - return null; -} - -for (const srcFile of allTsFiles) { - if (stubFiles.has(srcFile)) continue; // skip stub files themselves - - const fullPath = join(ROOT, srcFile); - const content = readFileSync(fullPath, 'utf-8'); - - // Match: import type { A, B } from 'path' - const typeImportRegex = /import\s+type\s+\{([^}]+)\}\s+from\s+['"](.+?)['"]/g; - let match; - while ((match = typeImportRegex.exec(content)) !== null) { - const members = match[1].split(',').map(s => { - const parts = s.trim().split(/\s+as\s+/); - return parts[0].trim(); - }).filter(Boolean); - const resolved = resolveImport(srcFile, match[2]); - if (resolved && stubNeeds.has(resolved)) { - for (const m of members) stubNeeds.get(resolved).types.add(m); - } - } - - // Match: import { A, B } from 'path' (NOT import type) - const valueImportRegex = /import\s+(?!type\s)\{([^}]+)\}\s+from\s+['"](.+?)['"]/g; - while ((match = valueImportRegex.exec(content)) !== null) { - const rawMembers = match[1]; - const members = rawMembers.split(',').map(s => { - // Handle `type Foo` inline type imports - const trimmed = s.trim(); - if (trimmed.startsWith('type ')) { - return { name: trimmed.replace(/^type\s+/, '').split(/\s+as\s+/)[0].trim(), isType: true }; - } - return { name: trimmed.split(/\s+as\s+/)[0].trim(), isType: false }; - }).filter(m => m.name); - - const resolved = resolveImport(srcFile, match[2]); - if (resolved && stubNeeds.has(resolved)) { - for (const m of members) { - if (m.isType) { - stubNeeds.get(resolved).types.add(m.name); - } else { - stubNeeds.get(resolved).values.add(m.name); - } - } - } - } - - // Match: import Default from 'path' - const defaultImportRegex = /import\s+(?!type\s)(\w+)\s+from\s+['"](.+?)['"]/g; - while ((match = defaultImportRegex.exec(content)) !== null) { - const name = match[1]; - if (name === 'type') continue; - const resolved = resolveImport(srcFile, match[2]); - if (resolved && stubNeeds.has(resolved)) { - stubNeeds.get(resolved).values.add('__default__:' + name); - } - } -} - -// Step 3: Rewrite stub files -let updated = 0; -for (const [stubFile, needs] of stubNeeds) { - const fullPath = join(ROOT, stubFile); - const lines = ['// Auto-generated stub — replace with real implementation']; - - let hasDefault = false; - - // Add type exports - for (const t of needs.types) { - // Don't add as type if also in values - if (!needs.values.has(t)) { - lines.push(`export type ${t} = any;`); - } - } - - // Add value exports (as const with any type) - for (const v of needs.values) { - if (v.startsWith('__default__:')) { - hasDefault = true; - continue; - } - // Check if it's likely a type (starts with uppercase and not a known function pattern) - // But since it's imported without `type`, treat as value to be safe - lines.push(`export const ${v}: any = (() => {}) as any;`); - } - - // Add default export if needed - if (hasDefault) { - lines.push(`export default {} as any;`); - } - - if (needs.types.size === 0 && needs.values.size === 0) { - lines.push('export {};'); - } - - writeFileSync(fullPath, lines.join('\n') + '\n'); - updated++; -} - -console.log(`Updated ${updated} stub files`); - -// Print summary -for (const [stubFile, needs] of stubNeeds) { - if (needs.types.size > 0 || needs.values.size > 0) { - console.log(` ${stubFile}: ${needs.types.size} types, ${needs.values.size} values`); - } -} diff --git a/scripts/fix-missing-exports.mjs b/scripts/fix-missing-exports.mjs deleted file mode 100644 index 0992e73..0000000 --- a/scripts/fix-missing-exports.mjs +++ /dev/null @@ -1,228 +0,0 @@ -#!/usr/bin/env node -/** - * Fixes TS2339 "Property X does not exist on type 'typeof import(...)'" - * by adding missing exports to the stub module files. - * Also re-runs TS2305/TS2724 fixes. - */ -import { execSync } from 'child_process'; -import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs'; -import { dirname, join } from 'path'; - -const ROOT = '/Users/konghayao/code/ai/claude-code'; - -// Run tsc and capture errors -let errors; -try { - errors = execSync('npx tsc --noEmit 2>&1', { encoding: 'utf-8', cwd: ROOT, maxBuffer: 50 * 1024 * 1024 }); -} catch (e) { - errors = e.stdout || ''; -} - -// ============================================================ -// 1. Fix TS2339 on typeof import(...) - add missing exports -// ============================================================ -// Map: module file path -> Set -const missingExports = new Map(); - -for (const line of errors.split('\n')) { - // TS2339: Property 'X' does not exist on type 'typeof import("path")' - let m = line.match(/error TS2339: Property '(\w+)' does not exist on type 'typeof import\("(.+?)"\)'/); - if (m) { - const [, prop, modPath] = m; - let filePath; - if (modPath.startsWith('/')) { - filePath = modPath; - } else { - continue; // skip non-absolute paths for now - } - // Try .ts then .tsx - for (const ext of ['.ts', '.tsx']) { - const fp = filePath + ext; - if (existsSync(fp)) { - if (!missingExports.has(fp)) missingExports.set(fp, new Set()); - missingExports.get(fp).add(prop); - break; - } - } - } - - // TS2339 on type '{ default: typeof import("...") }' (namespace import) - m = line.match(/error TS2339: Property '(\w+)' does not exist on type '\{ default: typeof import\("(.+?)"\)/); - if (m) { - const [, prop, modPath] = m; - for (const ext of ['.ts', '.tsx']) { - const fp = (modPath.startsWith('/') ? modPath : join(ROOT, modPath)) + ext; - if (existsSync(fp)) { - if (!missingExports.has(fp)) missingExports.set(fp, new Set()); - missingExports.get(fp).add(prop); - break; - } - } - } -} - -console.log(`Found ${missingExports.size} modules needing export additions for TS2339`); - -let ts2339Fixed = 0; -for (const [filePath, props] of missingExports) { - const content = readFileSync(filePath, 'utf-8'); - const existingExports = new Set(); - // Parse existing exports - const exportRegex = /export\s+(?:type|const|function|class|let|var|default)\s+(\w+)/g; - let em; - while ((em = exportRegex.exec(content)) !== null) { - existingExports.add(em[1]); - } - - const newExports = []; - for (const prop of props) { - if (!existingExports.has(prop) && !content.includes(`export { ${prop}`) && !content.includes(`, ${prop}`)) { - newExports.push(`export const ${prop}: any = (() => {}) as any;`); - ts2339Fixed++; - } - } - - if (newExports.length > 0) { - const newContent = content.trimEnd() + '\n' + newExports.join('\n') + '\n'; - writeFileSync(filePath, newContent); - } -} -console.log(`Added ${ts2339Fixed} missing exports for TS2339`); - -// ============================================================ -// 2. Fix TS2305 - Module has no exported member -// ============================================================ -const ts2305Fixes = new Map(); - -for (const line of errors.split('\n')) { - let m = line.match(/^(.+?)\(\d+,\d+\): error TS2305: Module '"(.+?)"' has no exported member '(.+?)'/); - if (!m) continue; - const [, srcFile, mod, member] = m; - - // Resolve module path - let resolvedPath; - if (mod.startsWith('.') || mod.startsWith('src/')) { - const base = mod.startsWith('.') ? join(dirname(srcFile), mod) : mod; - const resolved = join(ROOT, base).replace(/\.js$/, ''); - for (const ext of ['.ts', '.tsx']) { - if (existsSync(resolved + ext)) { - resolvedPath = resolved + ext; - break; - } - } - } - - if (resolvedPath) { - if (!ts2305Fixes.has(resolvedPath)) ts2305Fixes.set(resolvedPath, new Set()); - ts2305Fixes.get(resolvedPath).add(member); - } -} - -let ts2305Fixed = 0; -for (const [filePath, members] of ts2305Fixes) { - const content = readFileSync(filePath, 'utf-8'); - const newExports = []; - - for (const member of members) { - if (!content.includes(`export type ${member}`) && !content.includes(`export const ${member}`) && !content.includes(`export function ${member}`)) { - newExports.push(`export type ${member} = any;`); - ts2305Fixed++; - } - } - - if (newExports.length > 0) { - writeFileSync(filePath, content.trimEnd() + '\n' + newExports.join('\n') + '\n'); - } -} -console.log(`Added ${ts2305Fixed} missing exports for TS2305`); - -// ============================================================ -// 3. Fix TS2724 - no exported member named X. Did you mean Y? -// ============================================================ -const ts2724Fixes = new Map(); - -for (const line of errors.split('\n')) { - let m = line.match(/^(.+?)\(\d+,\d+\): error TS2724: '"(.+?)"' has no exported member named '(.+?)'/); - if (!m) continue; - const [, srcFile, mod, member] = m; - - let resolvedPath; - if (mod.startsWith('.') || mod.startsWith('src/')) { - const base = mod.startsWith('.') ? join(dirname(srcFile), mod) : mod; - const resolved = join(ROOT, base).replace(/\.js$/, ''); - for (const ext of ['.ts', '.tsx']) { - if (existsSync(resolved + ext)) { - resolvedPath = resolved + ext; - break; - } - } - } - - if (resolvedPath) { - if (!ts2724Fixes.has(resolvedPath)) ts2724Fixes.set(resolvedPath, new Set()); - ts2724Fixes.get(resolvedPath).add(member); - } -} - -let ts2724Fixed = 0; -for (const [filePath, members] of ts2724Fixes) { - const content = readFileSync(filePath, 'utf-8'); - const newExports = []; - - for (const member of members) { - if (!content.includes(`export type ${member}`) && !content.includes(`export const ${member}`)) { - newExports.push(`export type ${member} = any;`); - ts2724Fixed++; - } - } - - if (newExports.length > 0) { - writeFileSync(filePath, content.trimEnd() + '\n' + newExports.join('\n') + '\n'); - } -} -console.log(`Added ${ts2724Fixed} missing exports for TS2724`); - -// ============================================================ -// 4. Fix TS2307 - Cannot find module (create stub files) -// ============================================================ -let ts2307Fixed = 0; - -for (const line of errors.split('\n')) { - let m = line.match(/^(.+?)\(\d+,\d+\): error TS2307: Cannot find module '(.+?)'/); - if (!m) continue; - const [, srcFile, mod] = m; - if (mod.endsWith('.md') || mod.endsWith('.css')) continue; - if (!mod.startsWith('.') && !mod.startsWith('src/')) continue; - - const srcDir = dirname(srcFile); - let resolved; - if (mod.startsWith('.')) { - resolved = join(ROOT, srcDir, mod).replace(/\.js$/, '.ts'); - } else { - resolved = join(ROOT, mod).replace(/\.js$/, '.ts'); - } - - if (!existsSync(resolved) && resolved.startsWith(ROOT + '/src/')) { - const dir = dirname(resolved); - if (!existsSync(dir)) mkdirSync(dir, { recursive: true }); - - // Collect imports from the source file for this module - const srcContent = readFileSync(join(ROOT, srcFile), 'utf-8'); - const importRegex = new RegExp(`import\\s+(?:type\\s+)?\\{([^}]+)\\}\\s+from\\s+['"]${mod.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}['"]`, 'g'); - const members = new Set(); - let im; - while ((im = importRegex.exec(srcContent)) !== null) { - im[1].split(',').map(s => s.trim().replace(/^type\s+/, '').split(/\s+as\s+/)[0].trim()).filter(Boolean).forEach(m => members.add(m)); - } - - const lines = ['// Auto-generated stub']; - for (const member of members) { - lines.push(`export type ${member} = any;`); - } - if (members.size === 0) lines.push('export {};'); - - writeFileSync(resolved, lines.join('\n') + '\n'); - ts2307Fixed++; - } -} -console.log(`Created ${ts2307Fixed} new stub files for TS2307`); diff --git a/scripts/remove-sourcemaps.mjs b/scripts/remove-sourcemaps.mjs deleted file mode 100644 index 8037e44..0000000 --- a/scripts/remove-sourcemaps.mjs +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env node -/** - * 清除 src/ 下所有 .ts/.tsx 文件中的 //# sourceMappingURL= 行 - * 用法: node scripts/remove-sourcemaps.mjs [--dry-run] - */ -import { readdir, readFile, writeFile } from "fs/promises"; -import { join, extname } from "path"; - -const SRC_DIR = new URL("../src", import.meta.url).pathname; -const DRY_RUN = process.argv.includes("--dry-run"); -const EXTENSIONS = new Set([".ts", ".tsx"]); -const PATTERN = /^\s*\/\/# sourceMappingURL=.*$/gm; - -async function* walk(dir) { - for (const entry of await readdir(dir, { withFileTypes: true })) { - const full = join(dir, entry.name); - if (entry.isDirectory()) { - yield* walk(full); - } else if (EXTENSIONS.has(extname(entry.name))) { - yield full; - } - } -} - -let total = 0; -for await (const file of walk(SRC_DIR)) { - const content = await readFile(file, "utf8"); - if (!PATTERN.test(content)) continue; - // reset lastIndex after test - PATTERN.lastIndex = 0; - const cleaned = content.replace(PATTERN, "").replace(/\n{3,}/g, "\n\n"); - if (DRY_RUN) { - console.log(`[dry-run] ${file}`); - } else { - await writeFile(file, cleaned, "utf8"); - } - total++; -} - -console.log(`\n${DRY_RUN ? "[dry-run] " : ""}Processed ${total} files.`);