[healthcheck] Refactor checks into separate files
Makes it easier to extend later, if we want to add more checks. ghstack-source-id: 6fb3435555f1b988e1a185bfda8be9418eb622c5 Pull Request resolved: https://github.com/facebook/react-forget/pull/2924
Sathya Gunsasekaran committed
May 1, 2024 at 16:05 UTC
4ce5c56fee9e960b8b0702dc114455cbfa5d4e95
4 files changed
+177
-120
compiler/packages/healthcheck/src/checks/libraryCompat.ts
new
+37
@@ -0,0 +1,37 @@
1
+/**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+import chalk from "chalk";
9
+import { config } from "../config";
10
+
11
+const packageJsonRE = /package\.json$/;
12
+const knownIncompatibleLibrariesUsage = new Set();
13
+
14
+export default {
15
+ run(source: string, path: string): void {
16
+ if (packageJsonRE.exec(path) !== null) {
17
+ const contents = JSON.parse(source);
18
+ const deps = contents.dependencies;
19
+ for (const library of config.knownIncompatibleLibraries) {
20
+ if (Object.hasOwn(deps, library)) {
21
+ knownIncompatibleLibrariesUsage.add(library);
22
+ }
23
+ }
24
+ }
25
+ },
26
+
27
+ report(): void {
28
+ if (knownIncompatibleLibrariesUsage.size > 0) {
29
+ console.log(chalk.red(`Found the following incompatible libraries:`));
30
+ for (const library of knownIncompatibleLibrariesUsage) {
31
+ console.log(library);
32
+ }
33
+ } else {
34
+ console.log(chalk.green(`Found no usage of incompatible libraries.`));
35
+ }
36
+ },
37
+};
compiler/packages/healthcheck/src/checks/reactCompiler.ts
new
+97
@@ -0,0 +1,97 @@
1
+/**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+import {
9
+ ErrorSeverity,
10
+ runReactForgetBabelPlugin,
11
+ type CompilerErrorDetailOptions,
12
+ type PluginOptions,
13
+} from "babel-plugin-react-forget/src";
14
+import { LoggerEvent } from "babel-plugin-react-forget/src/Entrypoint";
15
+import chalk from "chalk";
16
+
17
+const SucessfulCompilation: Array<LoggerEvent> = [];
18
+const ActionableFailures: Array<LoggerEvent> = [];
19
+const OtherFailures: Array<LoggerEvent> = [];
20
+
21
+const logger = {
22
+ logEvent(_: string | null, event: LoggerEvent) {
23
+ switch (event.kind) {
24
+ case "CompileSuccess": {
25
+ SucessfulCompilation.push(event);
26
+ return;
27
+ }
28
+ case "CompileError": {
29
+ if (isActionableDiagnostic(event.detail)) {
30
+ ActionableFailures.push(event);
31
+ return;
32
+ }
33
+ OtherFailures.push(event);
34
+ return;
35
+ }
36
+ case "CompileDiagnostic":
37
+ case "PipelineError":
38
+ OtherFailures.push(event);
39
+ return;
40
+ }
41
+ },
42
+};
43
+
44
+const COMPILER_OPTIONS: Partial<PluginOptions> = {
45
+ noEmit: true,
46
+ compilationMode: "infer",
47
+ panicThreshold: "critical_errors",
48
+ logger,
49
+};
50
+
51
+function isActionableDiagnostic(detail: CompilerErrorDetailOptions) {
52
+ switch (detail.severity) {
53
+ case ErrorSeverity.InvalidReact:
54
+ case ErrorSeverity.InvalidJS:
55
+ return true;
56
+ case ErrorSeverity.InvalidConfig:
57
+ case ErrorSeverity.Invariant:
58
+ case ErrorSeverity.CannotPreserveMemoization:
59
+ case ErrorSeverity.Todo:
60
+ return false;
61
+ default:
62
+ throw new Error("Unhandled error severity");
63
+ }
64
+}
65
+
66
+function compile(sourceCode: string, filename: string) {
67
+ try {
68
+ runReactForgetBabelPlugin(
69
+ sourceCode,
70
+ filename,
71
+ "typescript",
72
+ COMPILER_OPTIONS
73
+ );
74
+ } catch {}
75
+}
76
+
77
+const JsFileExtensionRE = /(js|ts|jsx|tsx|mjs)$/;
78
+
79
+export default {
80
+ run(source: string, path: string): void {
81
+ if (JsFileExtensionRE.exec(path) !== null) {
82
+ compile(source, path);
83
+ }
84
+ },
85
+
86
+ report(): void {
87
+ const totalComponents =
88
+ SucessfulCompilation.length +
89
+ OtherFailures.length +
90
+ ActionableFailures.length;
91
+ console.log(
92
+ chalk.green(
93
+ `Successfully compiled ${SucessfulCompilation.length} out of ${totalComponents} components.`
94
+ )
95
+ );
96
+ },
97
+};
compiler/packages/healthcheck/src/checks/strictMode.ts
new
+32
@@ -0,0 +1,32 @@
1
+/**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+import chalk from "chalk";
9
+
10
+const JsFileExtensionRE = /(js|ts|jsx|tsx|mjs)$/;
11
+const StrictModeRE = /\<StrictMode\>/;
12
+let StrictModeUsage = false;
13
+
14
+export default {
15
+ run(source: string, path: string): void {
16
+ if (JsFileExtensionRE.exec(path) === null) {
17
+ return;
18
+ }
19
+
20
+ if (!StrictModeUsage) {
21
+ StrictModeUsage = StrictModeRE.exec(source) !== null;
22
+ }
23
+ },
24
+
25
+ report(): void {
26
+ if (StrictModeUsage) {
27
+ console.log(chalk.green("StrictMode usage found."));
28
+ } else {
29
+ console.log(chalk.red("StrictMode usage not found."));
30
+ }
31
+ },
32
+};
compiler/packages/healthcheck/src/index.ts
+11
-120
@@ -5,82 +5,13 @@
5
* LICENSE file in the root directory of this source tree.
6
*/
7
8
-import {
9
- ErrorSeverity,
10
- runReactForgetBabelPlugin,
11
- type CompilerErrorDetailOptions,
12
- type PluginOptions,
13
-} from "babel-plugin-react-forget/src";
14
-import { LoggerEvent } from "babel-plugin-react-forget/src/Entrypoint";
15
-import chalk from "chalk";
8
import { glob } from "fast-glob";
9
import * as fs from "fs/promises";
10
import ora from "ora";
11
import yargs from "yargs/yargs";
20
-import { config } from "./config";
21
-
22
-const SUCCESS: Array<LoggerEvent> = [];
23
-const ACTIONABLE_FAILURES: Array<LoggerEvent> = [];
24
-const OTHER_FAILURES: Array<LoggerEvent> = [];
25
-let STRICT_MODE_USAGE = false;
26
-
27
-const StrictModeRE = /\<StrictMode\>/;
28
-
29
-const logger = {
30
- logEvent(_: string | null, event: LoggerEvent) {
31
- switch (event.kind) {
32
- case "CompileSuccess": {
33
- SUCCESS.push(event);
34
- return;
35
- }
36
- case "CompileError": {
37
- if (isActionableDiagnostic(event.detail)) {
38
- ACTIONABLE_FAILURES.push(event);
39
- return;
40
- }
41
- OTHER_FAILURES.push(event);
42
- return;
43
- }
44
- case "CompileDiagnostic":
45
- case "PipelineError":
46
- OTHER_FAILURES.push(event);
47
- return;
48
- }
49
- },
50
-};
51
-
52
-const COMPILER_OPTIONS: Partial<PluginOptions> = {
53
- noEmit: true,
54
- compilationMode: "infer",
55
- panicThreshold: "critical_errors",
56
- logger,
57
-};
58
-
59
-function isActionableDiagnostic(detail: CompilerErrorDetailOptions) {
60
- switch (detail.severity) {
61
- case ErrorSeverity.InvalidReact:
62
- case ErrorSeverity.InvalidJS:
63
- return true;
64
- case ErrorSeverity.InvalidConfig:
65
- case ErrorSeverity.Invariant:
66
- case ErrorSeverity.CannotPreserveMemoization:
67
- case ErrorSeverity.Todo:
68
- return false;
69
- default:
70
- throw new Error("Unhandled error severity");
71
- }
72
-}
73
-
74
-function compile(sourceCode: string, filename: string) {
75
- try {
76
- runReactForgetBabelPlugin(
77
- sourceCode,
78
- filename,
79
- "typescript",
80
- COMPILER_OPTIONS
81
- );
82
- } catch {}
83
-}
12
+import libraryCompatCheck from "./checks/libraryCompat";
13
+import reactCompilerCheck from "./checks/reactCompiler";
14
+import strictModeCheck from "./checks/strictMode";
15
16
async function main() {
17
const argv = yargs(process.argv.slice(2))
@@ -93,14 +24,9 @@ async function main() {
24
})
25
.parseSync();
26
96
- const spinner = ora("Compiling").start();
27
+ const spinner = ora("Checking").start();
28
let src = argv.src;
29
99
- // no file extension specified
100
- if (!src.includes(".")) {
101
- src = src;
102
- }
103
-
30
const globOptions = {
31
onlyFiles: true,
32
ignore: [
@@ -115,53 +41,18 @@ async function main() {
41
],
42
};
43
118
- const jsFileExtensionRE = /(js|ts|jsx|tsx|mjs)$/;
119
- const packageJsonRE = /package\.json$/;
120
- const knownIncompatibleLibrariesUsage = new Set();
121
-
44
for (const path of await glob(src, globOptions)) {
45
const source = await fs.readFile(path, "utf-8");
124
- if (jsFileExtensionRE.exec(path) !== null) {
125
- spinner.text = `Compiling ${path}`;
126
- compile(source, path);
127
-
128
- if (!STRICT_MODE_USAGE) {
129
- STRICT_MODE_USAGE = StrictModeRE.exec(source) !== null;
130
- }
131
- } else if (packageJsonRE.exec(path) !== null) {
132
- const contents = JSON.parse(source);
133
- const deps = contents.dependencies;
134
- for (const library of config.knownIncompatibleLibraries) {
135
- if (Object.hasOwn(deps, library)) {
136
- knownIncompatibleLibrariesUsage.add(library);
137
- }
138
- }
139
- }
46
+ spinner.text = `Checking ${path}`;
47
+ reactCompilerCheck.run(source, path);
48
+ strictModeCheck.run(source, path);
49
+ libraryCompatCheck.run(source, path);
50
}
51
spinner.stop();
52
143
- const totalComponents =
144
- SUCCESS.length + OTHER_FAILURES.length + ACTIONABLE_FAILURES.length;
145
- console.log(
146
- chalk.green(
147
- `Successfully compiled ${SUCCESS.length} out of ${totalComponents} components.`
148
- )
149
- );
150
-
151
- if (STRICT_MODE_USAGE) {
152
- console.log(chalk.green("StrictMode usage found."));
153
- } else {
154
- console.log(chalk.red("StrictMode usage not found."));
155
- }
156
-
157
- if (knownIncompatibleLibrariesUsage.size > 0) {
158
- console.log(chalk.red(`Found the following incompatible libraries:`));
159
- for (const library of knownIncompatibleLibrariesUsage) {
160
- console.log(library);
161
- }
162
- } else {
163
- console.log(chalk.green(`Found no usage of incompatible libraries.`));
164
- }
53
+ reactCompilerCheck.report();
54
+ strictModeCheck.report();
55
+ libraryCompatCheck.report();
56
}
57
58
main();