Skip to content

Instantly share code, notes, and snippets.

@mizchi
Created April 16, 2022 11:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mizchi/1d16e46185587225d08293e069474139 to your computer and use it in GitHub Desktop.
Save mizchi/1d16e46185587225d08293e069474139 to your computer and use it in GitHub Desktop.
import ts, { servicesVersion } from "typescript";
type Files = { [key: string]: string };
type SourceFiles = { [key: string]: ts.SourceFile | undefined };
function createHostedProgram(rawFiles: Files, root: string[]) {
}
import fs from "fs/promises";
import path from "path";
// import zlib from "zlib";
const tsSourcePath = path.join(__dirname, "../../node_modules/typescript/lib/");
async function readLibFiles(): Promise<Files> {
const libFiles = await fs.readdir(tsSourcePath);
const entries = await Promise.all(
libFiles
.filter((file) => file.startsWith("lib.") && file.endsWith(".d.ts"))
.map(async (f) => {
const content = await fs.readFile(path.join(tsSourcePath, f), "utf8");
return [`/ts/lib/${f}`, content];
})
);
return Object.fromEntries(entries);
}
// const dumpPath = path.join(__dirname, "libs.json.gz");
// async function dumpLibsFile(libFiles: Files) {
// const zipped = zlib.gzipSync(JSON.stringify(libFiles));
// await fs.writeFile(dumpPath, zipped);
// console.log("gen >", "libs.json.gz", zipped.length);
// }
// async function loadLibsFile(): Promise<Files> {
// const buf = await fs.readFile(dumpPath);
// const text = zlib.gunzipSync(buf).toString("utf-8");
// return JSON.parse(text);
// }
const foo = 1;
const y = 1 + foo;
async function main() {
const libFiles = await readLibFiles();
const rawFiles: Files = {
...libFiles,
"/file.ts": `const foo=1;
export const bar = foo + 1;
export const x: number = false;
`,
"/input.ts": `import { x } from "./file"; 1 as false;`,
};
// const program = createHostedProgram({ ...libFiles, ...rawFiles }, ["/input.ts"]);
const compiledFiles: SourceFiles = {};
const options: ts.CompilerOptions = {
target: ts.ScriptTarget.ESNext,
};
const moduleResolutionHost: ts.ModuleResolutionHost = {
fileExists: (filePath) => !!rawFiles[filePath],
directoryExists: (dirPath) => dirPath === "/",
getDirectories: () => [],
readFile: (filePath) => {
// console.log("read", filePath);
return rawFiles[filePath];
},
}
// const host: ts.CompilerHost = {
// ...moduleResolutionHost,
// getCurrentDirectory: () => "/",
// getNewLine: () => "\n",
// getDefaultLibFileName: () => {
// return "/ts/lib/lib.esnext.d.ts";
// },
// getCanonicalFileName: (fileName) => fileName,
// getSourceFile: (filePath) => {
// // compile on demand
// const compiled = compiledFiles[filePath];
// if (compiled) return compiled;
// compiledFiles[filePath] = ts.createSourceFile(
// filePath,
// rawFiles[filePath],
// ts.ScriptTarget.Latest
// );
// return compiledFiles[filePath];
// },
// writeFile: (fileName, data) => {
// console.log("ts write >", fileName, data);
// },
// useCaseSensitiveFileNames: () => true,
// };
// const program = ts.createProgram({
// options,
// rootNames: ['/input.ts'],
// host,
// });
const fileVersions: ts.MapLike<{ version: number }> = {};
const serviceHost: ts.LanguageServiceHost = {
...moduleResolutionHost,
// getCompilerHost() { return host; },
getScriptFileNames: () => Object.keys(rawFiles),
getScriptVersion: fileName =>
fileVersions[fileName] && fileVersions[fileName].version.toString(),
getScriptSnapshot: fileName => {
if (!rawFiles[fileName]) return undefined;
return ts.ScriptSnapshot.fromString(rawFiles[fileName]);
},
getCurrentDirectory: () => '/',
getCompilationSettings: () => options,
getDefaultLibFileName: _options => {
return "/ts/lib/lib.esnext.d.ts";
},
useCaseSensitiveFileNames: () => true,
writeFile: (fileName, data) => {
console.log("ts write >", fileName, data);
},
};
const registry = ts.createDocumentRegistry();
const service = ts.createLanguageService(serviceHost, registry);
const program = service.getProgram()!;
const checker = program.getTypeChecker();
const sourceFile = program.getSourceFile('/file.ts')!;
const symbols = getNodes(sourceFile).map(n => checker.getSymbolAtLocation(n));
// console.log(symbols);
for (const symbol of symbols) {
if(symbol == null) continue;
// const loc = symbol.getDeclarations()?.[0]?.getSourceFile().getLineAndCharacterOfPosition(symbol.getDeclarations()?.[0]?.getStart());
// console.log(symbol?.getDeclarations());
// console.log(symbol?.getDeclarations());
const decls = symbol.getDeclarations();
if (decls == null) continue;
for (const decl of decls) {
const range: ts.TextRange = {
pos: decl.getStart(sourceFile),
end: decl.getEnd(),
}
console.log("decls----------", ts.SyntaxKind[decl.kind], decl.getText(sourceFile));
// console.log(decl);
const locations = service.findRenameLocations('/file.ts', decl.getStart(), true, false);
console.log(locations);
if (locations == null) continue;
for (const loc of locations) {
const s = program.getSourceFile(loc.fileName);
if (s == null) continue;
const transformed = ts.transform(s, [
(context) => {
return (sourceFile) => {
// newRange.pos = newSource.getLineAndCharacterOfPosition(newRange.pos).character;
// newRange.end = newSource.getLineAndCharacterOfPosition(newRange.end).character;
// const newText = newSource.getText().substring(newRange.pos, newRange.end);
// console.log("newText", newText);
// newSource.text = newSource.text.substring(0, newRange.pos) + newText + newSource.text.substring(newRange.end);
// return newSource;
// // }
const visit = (node: ts.Node) => {
return node;
};
return ts.factory.updateSourceFile(sourceFile, ts.visitNodes(sourceFile.statements, visit))
}
}
]);
const newSource = transformed.transformed[0];
console.log(newSource.getFullText());
// return this.documentRegistry.createOrUpdateSourceFile(filePath, this.context.compilerOptions.get(), ts.ScriptSnapshot.fromString(text), scriptKind);
// transformed.transformed
// registry.updateDocument(loc.fileName, {}, transformed.transformed[0], "1", ts.ScriptKind.TSX);
};
// const edits = service.getEditsForFileRename('/file.ts', '/file2.ts', {}, undefined);
// const refactors = service.getApplicableRefactors('/file.ts', range, undefined);
// console.log(refactors?.[0]);
// const edits = service.getEditsForRefactor('/file.ts', {}, range, 'rename', 'newName', undefined);
// console.log(locations);
// console.log(edits?.[0].actions);
// service.findReferences(decl.)
// const renameInfo = service.getRenameInfo('/file.ts', decl.getStart(sourceFile));
// console.log(renameInfo);
// service.applyCodeActionCommand(renameInfo);
// service.getRenameInfo(decl, 0);
}
// symbol.getRena
}
// service.getRenameInfo(program, )
// const p = service.findReferences('/input.ts', 0);
// const program = service.getProgram()!;
// const refs = service.getFileReferences('/file.ts');
// const refs = service.findReferences('/input.ts', 5);
// console.log(refs);
// program.
// const refs = program.getProjectReferences();
// console.log(refs);
// console.log(program);
// console.log(service);
// const out = service.getEmitOutput("/input.ts");
// console.log("out", out);
// program.get
// const files = program.getSourceFiles();
// // ts.createLanguageService(host)
// for (const file of files) {
// if (!file.fileName.includes("/ts/lib/")) {
// console.log(file.fileName, `\n` + file.text);
// }
// }
// const checker = program.getTypeChecker();
// checker.getPre
// console.log(program.getCurrentDirectory());;
// console.log(program.getSymbolCount());
// // program.getSourceFile()
// const source = program.getSourceFile('/input.ts');
// source?.statements.forEach((node) => {
// console.log(node);
// // node.sy
// });
// ts.
// ts.visitEachChild(source, (node) => {
// return node;
// }, {});
// program.
// const ret = ts.getPreEmitDiagnostics(program);
// for (const d of ret) {
// // @ts-ignore
// console.log(d.file?.fileName, '~', d.messageText);
// }
}
const getNodes = (sf: ts.SourceFile): ts.Identifier[] => {
const nodes: ts.Identifier[] = [];
const allNodes = (n: ts.Node) => {
ts.forEachChild(n, n => {
ts.isIdentifier(n) && nodes.push(n);
allNodes(n);
return false;
})
};
allNodes(sf);
return nodes;
}
main().catch((e) => console.error(e));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment