aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/packages/turbo-codemod/src/runner
diff options
context:
space:
mode:
author简律纯 <hsiangnianian@outlook.com>2023-04-28 01:36:44 +0800
committer简律纯 <hsiangnianian@outlook.com>2023-04-28 01:36:44 +0800
commitdd84b9d64fb98746a230cd24233ff50a562c39c9 (patch)
treeb583261ef00b3afe72ec4d6dacb31e57779a6faf /packages/turbo-codemod/src/runner
parent0b46fcd72ac34382387b2bcf9095233efbcc52f4 (diff)
downloadHydroRoll-dd84b9d64fb98746a230cd24233ff50a562c39c9.tar.gz
HydroRoll-dd84b9d64fb98746a230cd24233ff50a562c39c9.zip
Diffstat (limited to 'packages/turbo-codemod/src/runner')
-rw-r--r--packages/turbo-codemod/src/runner/FileTransform.ts94
-rw-r--r--packages/turbo-codemod/src/runner/Runner.ts132
-rw-r--r--packages/turbo-codemod/src/runner/index.ts3
-rw-r--r--packages/turbo-codemod/src/runner/types.ts40
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>;
+}