react-doctor 0.9.7-dev.13138a4 → 0.9.7

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.
@@ -1,32 +0,0 @@
1
-
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="da236116-fa98-5814-9d4e-dcc2503a7045")}catch(e){}}();
3
- import { Yn as runSync, t as recordCount, y as METRIC } from "./record-metric-CtlQma6P.js";
4
- import { o as isExpectedUserError } from "./unref-stdin-B5RnVeLa.js";
5
- import { n as handleError, r as handleUserError, t as reportErrorToSentry } from "./report-error-DUoFKR7d.js";
6
- import { t as printBrandedHeader } from "./print-branded-header-Cw5AlicW.js";
7
- import { n as runInstallReactDoctor } from "./install-react-doctor-BhfvyNZB.js";
8
- //#region src/cli/commands/install.ts
9
- const installAction = async (options, command) => {
10
- recordCount(METRIC.cliInvoked, 1, { command: "install" });
11
- runSync(printBrandedHeader);
12
- try {
13
- const parentOptions = command?.parent?.opts?.();
14
- await runInstallReactDoctor({
15
- yes: options.yes ?? parentOptions?.yes,
16
- dryRun: options.dryRun,
17
- agentHooks: options.agentHooks,
18
- projectRoot: options.cwd ?? process.cwd()
19
- });
20
- } catch (error) {
21
- if (isExpectedUserError(error)) {
22
- handleUserError(error);
23
- return;
24
- }
25
- handleError(error, { sentryEventId: await reportErrorToSentry(error) });
26
- }
27
- };
28
- //#endregion
29
- export { installAction };
30
-
31
- //# sourceMappingURL=install-ffJ108LC.js.map
32
- //# debugId=da236116-fa98-5814-9d4e-dcc2503a7045
@@ -1,267 +0,0 @@
1
-
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="1f395a85-8fa9-5c73-af2c-14352d85877d")}catch(e){}}();
3
- import { v as GH_DEFAULT_BRANCH_PROBE_TIMEOUT_MS } from "./record-metric-CtlQma6P.js";
4
- import { t as unrefStdin } from "./unref-stdin-B5RnVeLa.js";
5
- import { L as cliLogger } from "./install-doctor-script-DxrteuEw.js";
6
- import { createRequire } from "node:module";
7
- import { execFile } from "node:child_process";
8
- import * as path$1 from "node:path";
9
- import * as fs$1 from "node:fs";
10
- import { promisify } from "node:util";
11
- import basePrompts from "prompts";
12
- //#region src/cli/utils/run-command.ts
13
- const execFileAsync = promisify(execFile);
14
- const runCommand = async (command, args, cwd, timeoutMs) => {
15
- try {
16
- const { stdout, stderr } = await execFileAsync(command, [...args], {
17
- cwd,
18
- encoding: "utf8",
19
- timeout: timeoutMs
20
- });
21
- return {
22
- success: true,
23
- stdout: stdout.trim(),
24
- stderr: stderr.trim()
25
- };
26
- } catch (error) {
27
- const failure = error;
28
- return {
29
- success: false,
30
- stdout: (failure.stdout ?? "").trim(),
31
- stderr: (failure.stderr ?? "").trim()
32
- };
33
- }
34
- };
35
- //#endregion
36
- //#region src/cli/utils/detect-default-branch.ts
37
- const detectDefaultBranch = async (cwd, run = runCommand) => {
38
- const repoView = await run("gh", [
39
- "repo",
40
- "view",
41
- "--json",
42
- "defaultBranchRef",
43
- "--jq",
44
- ".defaultBranchRef.name"
45
- ], cwd, GH_DEFAULT_BRANCH_PROBE_TIMEOUT_MS);
46
- if (repoView.success && repoView.stdout !== "" && repoView.stdout !== "null") return repoView.stdout;
47
- const symRef = await run("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], cwd);
48
- if (symRef.success) {
49
- const branchMatch = symRef.stdout.match(/refs\/remotes\/origin\/(.+)$/);
50
- if (branchMatch) return branchMatch[1];
51
- }
52
- if ((await run("git", [
53
- "rev-parse",
54
- "--verify",
55
- "origin/main"
56
- ], cwd)).success) return "main";
57
- if ((await run("git", [
58
- "rev-parse",
59
- "--verify",
60
- "origin/master"
61
- ], cwd)).success) return "master";
62
- return null;
63
- };
64
- //#endregion
65
- //#region src/cli/utils/is-command-available.ts
66
- const isWindows = process.platform === "win32";
67
- const candidateFileNames = (command) => {
68
- if (!isWindows || path$1.extname(command)) return [command];
69
- return [command, ...(process.env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").map((extension) => extension.trim()).filter(Boolean).map((extension) => `${command}${extension}`)];
70
- };
71
- const isCommandAvailable = (command) => {
72
- const pathDirectories = (process.env.PATH ?? "").split(path$1.delimiter).filter(Boolean);
73
- for (const directory of pathDirectories) for (const fileName of candidateFileNames(command)) {
74
- const binaryPath = path$1.join(directory, fileName);
75
- try {
76
- if (!fs$1.statSync(binaryPath).isFile()) continue;
77
- if (!isWindows) fs$1.accessSync(binaryPath, fs$1.constants.X_OK);
78
- return true;
79
- } catch {}
80
- }
81
- return false;
82
- };
83
- //#endregion
84
- //#region src/cli/utils/should-auto-select-current-choice.ts
85
- const shouldAutoSelectCurrentChoice = (choiceStates, cursor) => {
86
- if (choiceStates.some((choiceState) => choiceState.selected)) return false;
87
- const currentChoice = choiceStates[cursor];
88
- return Boolean(currentChoice) && !currentChoice.disabled;
89
- };
90
- //#endregion
91
- //#region src/cli/utils/should-select-all-choices.ts
92
- const shouldSelectAllChoices = (choiceStates) => {
93
- return choiceStates.filter((choiceState) => !choiceState.disabled).some((choiceState) => choiceState.selected !== true);
94
- };
95
- //#endregion
96
- //#region src/cli/utils/prompts.ts
97
- const require = createRequire(import.meta.url);
98
- const PROMPTS_MULTISELECT_MODULE_PATH = "prompts/lib/elements/multiselect";
99
- let didPatchMultiselectToggleAll = false;
100
- let didPatchMultiselectSubmit = false;
101
- const onCancel = () => {
102
- cliLogger.break();
103
- cliLogger.log("Cancelled.");
104
- cliLogger.break();
105
- process.exit(0);
106
- };
107
- const patchMultiselectToggleAll = () => {
108
- if (didPatchMultiselectToggleAll) return;
109
- didPatchMultiselectToggleAll = true;
110
- const multiselectPromptConstructor = require(PROMPTS_MULTISELECT_MODULE_PATH);
111
- multiselectPromptConstructor.prototype.toggleAll = function() {
112
- const isCurrentChoiceDisabled = Boolean(this.value[this.cursor]?.disabled);
113
- if (this.maxChoices !== void 0 || isCurrentChoiceDisabled) {
114
- this.bell();
115
- return;
116
- }
117
- const shouldSelectAllEnabledChoices = shouldSelectAllChoices(this.value);
118
- for (const choiceState of this.value) {
119
- if (choiceState.disabled) continue;
120
- choiceState.selected = shouldSelectAllEnabledChoices;
121
- }
122
- this.render();
123
- };
124
- };
125
- const patchMultiselectSubmit = () => {
126
- if (didPatchMultiselectSubmit) return;
127
- didPatchMultiselectSubmit = true;
128
- const multiselectPromptConstructor = require(PROMPTS_MULTISELECT_MODULE_PATH);
129
- const originalSubmit = multiselectPromptConstructor.prototype.submit;
130
- multiselectPromptConstructor.prototype.submit = function() {
131
- if (shouldAutoSelectCurrentChoice(this.value, this.cursor)) this.value[this.cursor].selected = true;
132
- originalSubmit.call(this);
133
- };
134
- };
135
- const prompts = (questions, options = {}) => {
136
- patchMultiselectToggleAll();
137
- patchMultiselectSubmit();
138
- return basePrompts(questions, { onCancel: options.onCancel ?? onCancel }).finally(unrefStdin);
139
- };
140
- //#endregion
141
- //#region src/cli/utils/install-github-workflow.ts
142
- const buildWorkflowContent = (defaultBranch, actionRef = "v2") => `# React Doctor — finds security, performance, correctness, accessibility,
143
- # bundle-size, and architecture issues in React codebases.
144
- #
145
- # Docs: https://www.react.doctor/ci
146
- # Source: https://github.com/millionco/react-doctor
147
-
148
- name: React Doctor
149
-
150
- on:
151
- # Scans the PR's changed files and posts a sticky summary comment listing only the new issues introduced relative to the merge base of the target branch.
152
- pull_request:
153
- types: [opened, synchronize, reopened, ready_for_review]
154
- # Scans \`${defaultBranch}\` on every push to track the health-score trend and catch regressions that slipped past PR review.
155
- push:
156
- branches: ["${defaultBranch}"]
157
-
158
- permissions:
159
- contents: read
160
- pull-requests: write
161
- issues: write
162
- statuses: write
163
-
164
- # Cancels any in-flight scan for the same PR (or branch, on push) the moment a new commit arrives, so reviewers only ever see the latest run.
165
- concurrency:
166
- group: react-doctor-\${{ github.event.pull_request.number || github.ref }}
167
- cancel-in-progress: true
168
-
169
- jobs:
170
- react-doctor:
171
- runs-on: ubuntu-latest
172
- steps:
173
- # fetch-depth: 0 gives React Doctor the full git history it needs to find the merge base with the target branch. Without it a shallow checkout has no merge base, so PR runs can't compare against the base and fall back to reporting every issue in the changed files (pre-existing ones included) instead of only the ones the PR introduced.
174
- - uses: actions/checkout@v5
175
- with:
176
- fetch-depth: 0
177
-
178
- - uses: millionco/react-doctor@${actionRef}
179
- # Advisory by default: React Doctor reports findings on every PR — a
180
- # sticky summary comment, inline review comments, and a commit status
181
- # with the health score — but never fails the check, so it won't red-X
182
- # a teammate's PR on day one. When your team trusts the signal, graduate
183
- # the gate: uncomment the block below and set blocking to "error" (fail
184
- # on new error-severity findings) or "warning" (fail on any finding).
185
- # Full reference: https://www.react.doctor/ci
186
- # with:
187
- # blocking: error # Gate level: "none" (advisory, the default) | "warning" | "error"
188
- # scope: full # On PRs, scan the whole project instead of just changed files
189
- # comment: false # Disable the sticky PR summary comment
190
- # review-comments: false # Disable inline review comments on changed lines
191
- # commit-status: false # Disable the commit status (score + counts, links to the run)
192
- # version: "0.4.0" # Pin to a specific react-doctor version instead of "latest"
193
- # directory: apps/web # Scan a sub-directory (default: ".")
194
- # project: "web,admin" # In a monorepo, scan specific workspace project(s)
195
- `;
196
- const getReactDoctorWorkflowPath = (projectRoot) => path$1.join(projectRoot, ".github", "workflows", "react-doctor.yml");
197
- const isReactDoctorWorkflowInstalled = (projectRoot) => fs$1.existsSync(getReactDoctorWorkflowPath(projectRoot));
198
- const V1_FLOATING_ACTION_REF = String.raw`millionco/react-doctor@v1(?![\w.])`;
199
- const V2_FLOATING_ACTION_REF = "millionco/react-doctor@v2";
200
- const readReactDoctorWorkflow = (projectRoot) => {
201
- const workflowPath = getReactDoctorWorkflowPath(projectRoot);
202
- try {
203
- return {
204
- workflowPath,
205
- content: fs$1.readFileSync(workflowPath, "utf8")
206
- };
207
- } catch {
208
- return null;
209
- }
210
- };
211
- const workflowUsesV1Action = (content) => new RegExp(V1_FLOATING_ACTION_REF).test(content);
212
- const upgradeWorkflowActionToV2 = (content) => {
213
- const upgraded = content.replace(new RegExp(V1_FLOATING_ACTION_REF, "g"), V2_FLOATING_ACTION_REF);
214
- return {
215
- content: upgraded,
216
- changed: upgraded !== content
217
- };
218
- };
219
- const installReactDoctorWorkflow = (projectRoot, defaultBranch = "main") => {
220
- const workflowPath = getReactDoctorWorkflowPath(projectRoot);
221
- if (fs$1.existsSync(workflowPath)) return {
222
- status: "exists",
223
- workflowPath
224
- };
225
- try {
226
- fs$1.mkdirSync(path$1.dirname(workflowPath), { recursive: true });
227
- fs$1.writeFileSync(workflowPath, buildWorkflowContent(defaultBranch));
228
- return {
229
- status: "created",
230
- workflowPath
231
- };
232
- } catch {
233
- return {
234
- status: "failed",
235
- workflowPath
236
- };
237
- }
238
- };
239
- const upgradeReactDoctorWorkflowInPlace = (projectRoot) => {
240
- const workflow = readReactDoctorWorkflow(projectRoot);
241
- if (!workflow) return {
242
- status: "not-needed",
243
- workflowPath: getReactDoctorWorkflowPath(projectRoot)
244
- };
245
- const { content, changed } = upgradeWorkflowActionToV2(workflow.content);
246
- if (!changed) return {
247
- status: "not-needed",
248
- workflowPath: workflow.workflowPath
249
- };
250
- try {
251
- fs$1.writeFileSync(workflow.workflowPath, content);
252
- return {
253
- status: "upgraded",
254
- workflowPath: workflow.workflowPath
255
- };
256
- } catch {
257
- return {
258
- status: "failed",
259
- workflowPath: workflow.workflowPath
260
- };
261
- }
262
- };
263
- //#endregion
264
- export { readReactDoctorWorkflow as a, workflowUsesV1Action as c, detectDefaultBranch as d, runCommand as f, isReactDoctorWorkflowInstalled as i, prompts as l, getReactDoctorWorkflowPath as n, upgradeReactDoctorWorkflowInPlace as o, installReactDoctorWorkflow as r, upgradeWorkflowActionToV2 as s, buildWorkflowContent as t, isCommandAvailable as u };
265
-
266
- //# sourceMappingURL=install-github-workflow-BP6t3YBA.js.map
267
- //# debugId=1f395a85-8fa9-5c73-af2c-14352d85877d