@thinksharpe/react-compiler-unmemo 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@thinksharpe/react-compiler-unmemo",
3
+ "version": "0.5.0",
4
+ "description": "Remove useMemo and useCallback hooks from React codebases to leverage React Compiler automatic optimization",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "author": "Thinksharpe",
8
+ "homepage": "https://github.com/thinksharpe/react-compiler-unmemo",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/thinksharpe/react-compiler-unmemo.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/thinksharpe/react-compiler-unmemo/issues"
15
+ },
16
+ "keywords": [
17
+ "react",
18
+ "react-compiler",
19
+ "react-19",
20
+ "useMemo",
21
+ "useCallback",
22
+ "codemod",
23
+ "migration",
24
+ "performance",
25
+ "automatic-memoization"
26
+ ],
27
+ "bin": {
28
+ "react-compiler-unmemo": "react-compiler-unmemo.mjs"
29
+ },
30
+ "main": "./react-compiler-unmemo.mjs",
31
+ "exports": {
32
+ ".": "./react-compiler-unmemo.mjs"
33
+ },
34
+ "files": [
35
+ "react-compiler-unmemo.mjs",
36
+ "helpers/",
37
+ "docs/",
38
+ "README.md",
39
+ "LICENSE"
40
+ ],
41
+ "scripts": {
42
+ "start": "node react-compiler-unmemo.mjs"
43
+ },
44
+ "engines": {
45
+ "node": ">=18"
46
+ },
47
+ "dependencies": {
48
+ "glob": "^11.0.0"
49
+ },
50
+ "devDependencies": {}
51
+ }
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * react-compiler-unmemo.mjs
5
+ *
6
+ * Runner that executes the full React Compiler hook removal pipeline:
7
+ * 1. Remove useMemo/useCallback hooks
8
+ * 2. Fix type annotations lost during removal
9
+ *
10
+ * Usage:
11
+ * npx react-compiler-unmemo <directory> [options]
12
+ *
13
+ * Options:
14
+ * --write Apply changes to files (default is dry-run / preview)
15
+ * --verbose Show detailed output for each transformation
16
+ * --files <glob> File glob pattern (default: helpers/**\/*.{tsx,ts})
17
+ * --skip-fix Skip the type annotation fix step
18
+ *
19
+ * Examples:
20
+ * npx react-compiler-unmemo ./my-react-app # preview (dry-run)
21
+ * npx react-compiler-unmemo ./my-react-app --write # apply changes
22
+ * npx react-compiler-unmemo ./my-react-app --write --verbose
23
+ * npx react-compiler-unmemo /absolute/path/to/project --files "app/**\/*.tsx"
24
+ */
25
+
26
+ import path from "path";
27
+ import { run as fixTypes } from "./helpers/fix-type-annotations.mjs";
28
+ import { run as removeHooks } from "./helpers/remove-hooks.mjs";
29
+
30
+ // ─── CLI ─────────────────────────────────────────────────────────────────────
31
+
32
+ const args = process.argv.slice(2);
33
+ const write = args.includes("--write");
34
+ const dryRun = !write;
35
+ const verbose = args.includes("--verbose");
36
+ const skipFix = args.includes("--skip-fix");
37
+
38
+ const filesIdx = args.indexOf("--files");
39
+ const fileGlob = filesIdx !== -1 && args[filesIdx + 1] ? args[filesIdx + 1] : undefined;
40
+
41
+ // First positional arg is the directory
42
+ const positionalArgs = args.filter(
43
+ (a) => !a.startsWith("--") && (args.indexOf(a) === 0 || !["--files", "--dir"].includes(args[args.indexOf(a) - 1])),
44
+ );
45
+ const targetDir = positionalArgs[0] ? path.resolve(positionalArgs[0]) : null;
46
+
47
+ if (!targetDir) {
48
+ console.log("Usage: npx react-compiler-unmemo <directory> [options]");
49
+ console.log();
50
+ console.log("Options:");
51
+ console.log(" --write Apply changes to files (default is preview/dry-run)");
52
+ console.log(" --verbose Show detailed output per transformation");
53
+ console.log(" --files <glob> File glob pattern (default: src/**/*.{tsx,ts})");
54
+ console.log(" --skip-fix Skip the type annotation fix step");
55
+ console.log();
56
+ console.log("Examples:");
57
+ console.log(" npx react-compiler-unmemo ./my-react-app # preview (safe default)");
58
+ console.log(" npx react-compiler-unmemo ./my-react-app --write # apply changes");
59
+ console.log(" npx react-compiler-unmemo ./my-react-app --write --verbose");
60
+ console.log(' npx react-compiler-unmemo /path/to/project --files "app/**/*.tsx"');
61
+ process.exit(1);
62
+ }
63
+
64
+ // ─── Step 1: Remove hooks ────────────────────────────────────────────────────
65
+
66
+ console.log("━".repeat(60));
67
+ console.log(" Step 1/2: Remove useMemo & useCallback");
68
+ console.log("━".repeat(60));
69
+ console.log();
70
+
71
+ const hookResult = removeHooks(targetDir, { fileGlob, dryRun, verbose });
72
+
73
+ // ─── Step 2: Fix type annotations ────────────────────────────────────────────
74
+
75
+ if (!skipFix) {
76
+ console.log();
77
+ console.log("━".repeat(60));
78
+ console.log(" Step 2/2: Fix type annotations");
79
+ console.log("━".repeat(60));
80
+ console.log();
81
+
82
+ const fixResult = fixTypes(targetDir, { fileGlob, dryRun });
83
+
84
+ // ─── Summary ─────────────────────────────────────────────────────────────
85
+
86
+ console.log();
87
+ console.log("═".repeat(60));
88
+ console.log(" Summary");
89
+ console.log("═".repeat(60));
90
+ console.log();
91
+ console.log(` Files modified: ${hookResult.changedCount}`);
92
+ console.log(` Hooks removed: ${hookResult.totalTransformations}`);
93
+ console.log(` Types fixed: ${fixResult.fixCount}`);
94
+ console.log(` Errors: ${hookResult.errors.length + fixResult.errors.length}`);
95
+ if (hookResult.remaining.length > 0) {
96
+ console.log(` Remaining refs: ${hookResult.remaining.length} files`);
97
+ }
98
+ if (dryRun) {
99
+ console.log();
100
+ console.log(" (dry run — no files were written)");
101
+ console.log(" Run with --write to apply changes.");
102
+ } else {
103
+ console.log();
104
+ console.log(" Next steps:");
105
+ console.log(" 1. Run your build tool to check for type errors");
106
+ console.log(" 2. Review docs/edge-cases.md for known manual fixes");
107
+ console.log(" 3. Run again if new files are added");
108
+ }
109
+ console.log();
110
+
111
+ const hasErrors = hookResult.errors.length + fixResult.errors.length > 0;
112
+ process.exit(hasErrors ? 1 : 0);
113
+ } else {
114
+ console.log();
115
+ console.log("═".repeat(60));
116
+ console.log(" Summary (type fix skipped)");
117
+ console.log("═".repeat(60));
118
+ console.log();
119
+ console.log(` Files modified: ${hookResult.changedCount}`);
120
+ console.log(` Hooks removed: ${hookResult.totalTransformations}`);
121
+ console.log(` Errors: ${hookResult.errors.length}`);
122
+ if (hookResult.remaining.length > 0) {
123
+ console.log(` Remaining refs: ${hookResult.remaining.length} files`);
124
+ }
125
+ console.log();
126
+
127
+ process.exit(hookResult.errors.length > 0 ? 1 : 0);
128
+ }