diff options
| author | 2023-04-28 01:36:44 +0800 | |
|---|---|---|
| committer | 2023-04-28 01:36:44 +0800 | |
| commit | dd84b9d64fb98746a230cd24233ff50a562c39c9 (patch) | |
| tree | b583261ef00b3afe72ec4d6dacb31e57779a6faf /packages/turbo-codemod/src/runner | |
| parent | 0b46fcd72ac34382387b2bcf9095233efbcc52f4 (diff) | |
| download | HydroRoll-dd84b9d64fb98746a230cd24233ff50a562c39c9.tar.gz HydroRoll-dd84b9d64fb98746a230cd24233ff50a562c39c9.zip | |
Diffstat (limited to 'packages/turbo-codemod/src/runner')
| -rw-r--r-- | packages/turbo-codemod/src/runner/FileTransform.ts | 94 | ||||
| -rw-r--r-- | packages/turbo-codemod/src/runner/Runner.ts | 132 | ||||
| -rw-r--r-- | packages/turbo-codemod/src/runner/index.ts | 3 | ||||
| -rw-r--r-- | packages/turbo-codemod/src/runner/types.ts | 40 |
4 files changed, 269 insertions, 0 deletions
diff --git a/packages/turbo-codemod/src/runner/FileTransform.ts b/packages/turbo-codemod/src/runner/FileTransform.ts new file mode 100644 index 0000000..3b23f73 --- /dev/null +++ b/packages/turbo-codemod/src/runner/FileTransform.ts @@ -0,0 +1,94 @@ +import chalk from "chalk"; +import { diffLines, Change, diffJson } from "diff"; +import fs from "fs-extra"; +import os from "os"; +import path from "path"; + +import type { FileTransformArgs, LogFileArgs } from "./types"; + +export default class FileTransform { + filePath: string; + rootPath: string; + before: string | object; + after?: string | object; + error?: Error; + changes: Array<Change> = []; + + constructor(args: FileTransformArgs) { + this.filePath = args.filePath; + this.rootPath = args.rootPath; + this.after = args.after; + this.error = args.error; + + // load original file for comparison + if (args.before === undefined) { + try { + if (path.extname(args.filePath) === ".json") { + this.before = fs.readJsonSync(args.filePath); + } else { + this.before = fs.readFileSync(args.filePath); + } + } catch (err) { + this.before = ""; + } + } else if (args.before === null) { + this.before = ""; + } else { + this.before = args.before; + } + + // determine diff + if (args.after) { + if (typeof this.before === "object" || typeof args.after === "object") { + this.changes = diffJson(this.before, args.after); + } else { + this.changes = diffLines(this.before, args.after); + } + } else { + this.changes = []; + } + } + + fileName(): string { + return path.relative(this.rootPath, this.filePath); + } + + write(): void { + if (this.after) { + if (typeof this.after === "object") { + fs.writeJsonSync(this.filePath, this.after, { spaces: 2 }); + } else { + fs.writeFileSync(this.filePath, this.after); + } + } + } + + additions(): number { + return this.changes.filter((c) => c.added).length; + } + + deletions(): number { + return this.changes.filter((c) => c.removed).length; + } + + hasChanges(): boolean { + return this.additions() > 0 || this.deletions() > 0; + } + + log(args: LogFileArgs): void { + if (args.diff) { + this.changes.forEach((part) => { + if (part.added) { + process.stdout.write(chalk.green(part.value)); + } else if (part.removed) { + process.stdout.write(chalk.red(part.value)); + } else { + process.stdout.write(chalk.dim(part.value)); + } + }); + console.log(os.EOL); + } else { + console.log(this.after); + } + } +} diff --git a/packages/turbo-codemod/src/runner/Runner.ts b/packages/turbo-codemod/src/runner/Runner.ts new file mode 100644 index 0000000..8f8803d --- /dev/null +++ b/packages/turbo-codemod/src/runner/Runner.ts @@ -0,0 +1,132 @@ +import chalk from "chalk"; + +import FileTransform from "./FileTransform"; +import Logger from "../utils/logger"; +import type { UtilityArgs } from "../types"; +import type { + FileResult, + ModifyFileArgs, + AbortTransformArgs, + TransformerResults, +} from "./types"; + +class Runner { + transform: string; + rootPath: string; + dry: boolean; + print: boolean; + modifications: Record<string, FileTransform> = {}; + logger: Logger; + + constructor(options: UtilityArgs) { + this.transform = options.transformer; + this.rootPath = options.rootPath; + this.dry = options.dry; + this.print = options.print; + this.logger = new Logger(options); + } + + abortTransform(args: AbortTransformArgs): TransformerResults { + this.logger.error(args.reason); + return { + fatalError: new Error(args.reason), + changes: args.changes || {}, + }; + } + + // add a file to be transformed + modifyFile(args: ModifyFileArgs): void { + this.modifications[args.filePath] = new FileTransform({ + rootPath: this.rootPath, + ...args, + }); + } + + // execute all transforms and track results for reporting + finish(): TransformerResults { + const results: TransformerResults = { changes: {} }; + // perform all actions and track results + Object.keys(this.modifications).forEach((filePath) => { + const mod = this.modifications[filePath]; + const result: FileResult = { + action: "unchanged", + additions: mod.additions(), + deletions: mod.deletions(), + }; + + if (mod.hasChanges()) { + if (this.dry) { + result.action = "skipped"; + this.logger.skipped(chalk.dim(mod.fileName())); + } else { + try { + mod.write(); + result.action = "modified"; + this.logger.modified(chalk.bold(mod.fileName())); + } catch (err) { + let message = "Unknown error"; + if (err instanceof Error) { + message = err.message; + } + result.error = new Error(message); + result.action = "error"; + this.logger.error(mod.fileName(), message); + } + } + + if (this.print) { + mod.log({ diff: true }); + } + } else { + this.logger.unchanged(chalk.dim(mod.fileName())); + } + + results.changes[mod.fileName()] = result; + }); + + const encounteredError = Object.keys(results.changes).some((fileName) => { + return results.changes[fileName].action === "error"; + }); + + if (encounteredError) { + return this.abortTransform({ + reason: "Encountered an error while transforming files", + changes: results.changes, + }); + } + + return results; + } + + static logResults(results: TransformerResults): void { + const changedFiles = Object.keys(results.changes); + console.log(); + if (changedFiles.length > 0) { + console.log(chalk.bold(`Results:`)); + const table: Record< + string, + { + action: FileResult["action"]; + additions: FileResult["additions"]; + deletions: FileResult["deletions"]; + error?: string; + } + > = {}; + + changedFiles.forEach((fileName) => { + const fileChanges = results.changes[fileName]; + table[fileName] = { + action: fileChanges.action, + additions: fileChanges.additions, + deletions: fileChanges.deletions, + error: fileChanges.error?.message || "None", + }; + }); + + console.table(table); + console.log(); + } + } +} + +export default Runner; diff --git a/packages/turbo-codemod/src/runner/index.ts b/packages/turbo-codemod/src/runner/index.ts new file mode 100644 index 0000000..2aa323d --- /dev/null +++ b/packages/turbo-codemod/src/runner/index.ts @@ -0,0 +1,3 @@ +export { default as Runner } from "./Runner"; + +export type { TransformerResults, FileDiffer, FileWriter } from "./types"; diff --git a/packages/turbo-codemod/src/runner/types.ts b/packages/turbo-codemod/src/runner/types.ts new file mode 100644 index 0000000..e7c37d4 --- /dev/null +++ b/packages/turbo-codemod/src/runner/types.ts @@ -0,0 +1,40 @@ +import { Change } from "diff"; + +export interface FileResult { + action: "skipped" | "modified" | "unchanged" | "error"; + error?: Error; + additions: number; + deletions: number; +} + +export interface FileTransformArgs extends ModifyFileArgs { + rootPath: string; +} + +export interface ModifyFileArgs { + filePath: string; + before?: string | object; + after?: string | object; + error?: Error; +} + +export interface AbortTransformArgs { + reason: string; + changes?: Record<string, FileResult>; +} + +export interface LogFileArgs { + diff?: boolean; +} + +export type FileWriter = (filePath: string, contents: string | object) => void; + +export type FileDiffer = ( + before: string | object, + after: string | object +) => Array<Change>; + +export interface TransformerResults { + fatalError?: Error; + changes: Record<string, FileResult>; +} |
