Created
April 18, 2023 11:24
-
-
Save timostamm/3797825096a418b1f4c97d979e2d4ae7 to your computer and use it in GitHub Desktop.
analyze a package in the local file system with @arethetypeswrong/core
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import type {Analysis, FS, Host} from "@arethetypeswrong/core"; | |
import {checkPackage, getProblems, summarizeProblems} from "@arethetypeswrong/core"; | |
import {readdirSync, readFileSync} from "node:fs"; | |
import path from "node:path"; | |
import {z} from "zod"; | |
export async function typeswrong(projectPath: string) { | |
const pkgPath = path.join(projectPath, "package.json"); | |
const pkg = readJsonFile(pkgPath, PackageJson); | |
const host = createLocalFsHost(projectPath, pkg.name); | |
const analysis: Analysis = await checkPackage(pkg.name, undefined, host); | |
if (!analysis.containsTypes) { | |
throw new Error("no types found"); | |
} | |
const problems = getProblems(analysis); | |
const summaries = summarizeProblems(problems, analysis); | |
if (problems.length > 0) { | |
console.log(JSON.stringify(summaries, null, 2)); | |
console.log(JSON.stringify(problems, null, 2)); | |
throw new Error("got problems " + problems.length); | |
} | |
return {problems, summaries}; | |
} | |
function createLocalFsHost(projectPath: string, expectPackageName: string): Host { | |
return { | |
async createPackageFS(packageName: string, packageVersion?: string): Promise<FS> { | |
if (packageName !== expectPackageName) { | |
throw new Error(`expected package name "${expectPackageName}" but got "${packageName}"`); | |
} | |
if (packageVersion !== undefined) { | |
throw new Error("argument not supported"); | |
} | |
const files = replacePathPrefix(readAllFiles(projectPath, ["node_modules"]), projectPath, "/node_modules/" + packageName ); | |
return { | |
readFile: (path: string) => { | |
const contents = files.get(path); | |
if (!contents) { | |
throw new Error(`File not found: ${path}`); | |
} | |
return contents; | |
}, | |
fileExists: (path: string) => files.has(path), | |
directoryExists: (path: string) => { | |
if (!path.endsWith("/") && !path.endsWith("\\")) { | |
path += "/"; | |
} | |
for (const name of files.keys()) { | |
if (name.startsWith(path)) { | |
return true; | |
} | |
} | |
return false; | |
}, | |
listFiles: () => Array.from(files.keys()), | |
}; | |
}, | |
createPackageFSFromTarball() { | |
throw new Error("not implemented"); | |
} | |
}; | |
} | |
function readAllFiles(dir: string, ignoreNames: string[]): Map<string, string> { | |
const all = new Map<string, string>(); | |
readAllFilesRec(all, dir, ignoreNames); | |
return all; | |
} | |
function readAllFilesRec(all: Map<string, string>, curr: string, ignoreNames: string[]): void { | |
for (const ent of readdirSync(curr, {withFileTypes: true})) { | |
if (ignoreNames.includes(ent.name)) { | |
continue; | |
} | |
const name = path.join(curr, ent.name); | |
if (ent.isFile()) { | |
const content = readFileSync(name, "utf-8"); | |
all.set(name, content); | |
} else if (ent.isDirectory()) { | |
readAllFilesRec(all, name, ignoreNames); | |
} | |
} | |
} | |
function replacePathPrefix(files: Map<string, string>, oldPrefix: string, newPrefix: string) { | |
const out = new Map<string, string>(); | |
for (const [name, content] of files.entries()) { | |
if (name.startsWith(oldPrefix)) { | |
out.set(newPrefix + name.substring(oldPrefix.length), content); | |
} else { | |
out.set(name, content); | |
} | |
} | |
return out; | |
} | |
const PackageJson = z.object({ | |
name: z.string(), | |
version: z.string().optional(), | |
type: z.union([z.literal("module"), z.literal("commonjs")]).optional(), | |
}); | |
function readJsonFile<T extends z.ZodObject<any>>(path: string, schema: T): T["_output"] { | |
try { | |
const text = readFileSync(path, "utf-8"); | |
const json = JSON.parse(text); | |
return schema.parse(json); | |
} catch (e) { | |
throw new Error(`failed to read ${path}: ${e instanceof Error ? e.message : String(e)}`); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment