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,339 +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]="6d1dde0f-d9fe-5053-b24c-3e5de5579599")}catch(e){}}();
3
- import { Bt as filterSourceFiles, an as isPathInsideDirectory, jn as toCanonicalPath } from "./record-metric-CtlQma6P.js";
4
- import { L as cliLogger } from "./install-doctor-script-DxrteuEw.js";
5
- import { d as detectDefaultBranch, f as runCommand, l as prompts, u as isCommandAvailable } from "./install-github-workflow-BP6t3YBA.js";
6
- import * as path$1 from "node:path";
7
- //#region src/cli/utils/path-format.ts
8
- const toForwardSlashes = (filePath) => filePath.replaceAll("\\", "/");
9
- //#endregion
10
- //#region src/cli/utils/open-workflow-pull-request.ts
11
- const NEW_BRANCH_PREFIX = "react-doctor/add-github-actions";
12
- const DEFAULT_COMMIT_MESSAGE = "ci: add React Doctor GitHub Actions workflow";
13
- const DEFAULT_PR_TITLE = "Add React Doctor to GitHub Actions";
14
- const DEFAULT_PR_BODY = `Adds a [React Doctor](https://www.react.doctor) scan to every pull request and every push to the default branch. The workflow file is documented inline.
15
-
16
- Docs: https://www.react.doctor/ci`;
17
- const resolveWorkflowRepositoryLocation = (workflowPath, repositoryRoot) => {
18
- const canonicalWorkflowPath = toCanonicalPath(workflowPath);
19
- const canonicalRepositoryRoot = toCanonicalPath(repositoryRoot);
20
- if (!isPathInsideDirectory(canonicalWorkflowPath, canonicalRepositoryRoot)) return null;
21
- return {
22
- repositoryRoot: canonicalRepositoryRoot,
23
- workflowRelativePath: toForwardSlashes(path$1.relative(canonicalRepositoryRoot, canonicalWorkflowPath))
24
- };
25
- };
26
- const findUniqueBranchName = async (cwd, run) => {
27
- if (!(await run("git", [
28
- "rev-parse",
29
- "--verify",
30
- NEW_BRANCH_PREFIX
31
- ], cwd)).success) return NEW_BRANCH_PREFIX;
32
- return `${NEW_BRANCH_PREFIX}-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16).replace(/[-:T]/g, "")}`;
33
- };
34
- const findExistingSetupPullRequest = async (cwd, run) => {
35
- const prList = await run("gh", [
36
- "pr",
37
- "list",
38
- "--state",
39
- "open",
40
- "--json",
41
- "headRefName,url",
42
- "--limit",
43
- String(100)
44
- ], cwd);
45
- if (!prList.success) return null;
46
- try {
47
- return JSON.parse(prList.stdout).find((pullRequest) => (pullRequest.headRefName ?? "").startsWith(NEW_BRANCH_PREFIX)) ?? null;
48
- } catch {
49
- return null;
50
- }
51
- };
52
- const hasUnrelatedTrackedChanges = async (cwd, workflowRelative, run) => {
53
- const statusProbe = await run("git", [
54
- "status",
55
- "--porcelain",
56
- "--",
57
- ".",
58
- `:!${workflowRelative}`
59
- ], cwd);
60
- if (!statusProbe.success) return true;
61
- return statusProbe.stdout.split(/\r?\n/).filter(Boolean).some((statusLine) => !statusLine.startsWith("??"));
62
- };
63
- const openWorkflowPullRequest = async (input) => {
64
- const workflowPath = path$1.resolve(input.workflowPath);
65
- const commitMessage = input.commitMessage ?? DEFAULT_COMMIT_MESSAGE;
66
- const prTitle = input.prTitle ?? DEFAULT_PR_TITLE;
67
- const prBody = input.prBody ?? DEFAULT_PR_BODY;
68
- const run = input.run ?? runCommand;
69
- const checkCommandAvailable = input.checkCommandAvailable ?? isCommandAvailable;
70
- const repoRootProbe = await run("git", ["rev-parse", "--show-toplevel"], path$1.dirname(workflowPath));
71
- if (!repoRootProbe.success) return {
72
- status: "not-attempted",
73
- reason: "not-a-git-repo"
74
- };
75
- const repositoryLocation = resolveWorkflowRepositoryLocation(workflowPath, repoRootProbe.stdout);
76
- if (repositoryLocation === null) return {
77
- status: "not-attempted",
78
- reason: "workflow-outside-repository"
79
- };
80
- const cwd = repositoryLocation.repositoryRoot;
81
- const workflowRelative = repositoryLocation.workflowRelativePath;
82
- if (!checkCommandAvailable("gh")) return {
83
- status: "not-attempted",
84
- reason: "gh-not-installed"
85
- };
86
- if (!(await run("gh", ["auth", "status"], cwd)).success) return {
87
- status: "not-attempted",
88
- reason: "gh-not-authenticated"
89
- };
90
- const existingSetupPullRequest = await findExistingSetupPullRequest(cwd, run);
91
- if (existingSetupPullRequest) return {
92
- status: "pr-exists",
93
- url: existingSetupPullRequest.url ?? ""
94
- };
95
- if (await hasUnrelatedTrackedChanges(cwd, workflowRelative, run)) return {
96
- status: "not-attempted",
97
- reason: "working-tree-dirty"
98
- };
99
- const defaultBranch = input.baseBranch ?? await detectDefaultBranch(cwd, run);
100
- if (!defaultBranch) return {
101
- status: "not-attempted",
102
- reason: "no-default-branch"
103
- };
104
- const previousBranchProbe = await run("git", [
105
- "rev-parse",
106
- "--abbrev-ref",
107
- "HEAD"
108
- ], cwd);
109
- if (!previousBranchProbe.success || previousBranchProbe.stdout === "HEAD") return {
110
- status: "not-attempted",
111
- reason: "detached-head"
112
- };
113
- const previousBranch = previousBranchProbe.stdout;
114
- await run("git", [
115
- "fetch",
116
- "origin",
117
- defaultBranch
118
- ], cwd);
119
- const newBranch = await findUniqueBranchName(cwd, run);
120
- if (!(await run("git", [
121
- "checkout",
122
- "-b",
123
- newBranch,
124
- `origin/${defaultBranch}`
125
- ], cwd)).success) return {
126
- status: "not-attempted",
127
- reason: "checkout-failed"
128
- };
129
- const restoreToPreviousBranch = async (deleteNewBranch) => {
130
- await run("git", ["checkout", previousBranch], cwd);
131
- if (deleteNewBranch) await run("git", [
132
- "branch",
133
- "-D",
134
- newBranch
135
- ], cwd);
136
- };
137
- if (!(await run("git", [
138
- "add",
139
- "--",
140
- workflowRelative
141
- ], cwd)).success) {
142
- await restoreToPreviousBranch(true);
143
- return {
144
- status: "not-attempted",
145
- reason: "git-add-failed"
146
- };
147
- }
148
- if (!(await run("git", [
149
- "commit",
150
- "-m",
151
- commitMessage
152
- ], cwd)).success) {
153
- await restoreToPreviousBranch(true);
154
- return {
155
- status: "not-attempted",
156
- reason: "git-commit-failed"
157
- };
158
- }
159
- if (!(await run("git", [
160
- "push",
161
- "-u",
162
- "origin",
163
- newBranch
164
- ], cwd)).success) {
165
- await restoreToPreviousBranch(true);
166
- return {
167
- status: "not-attempted",
168
- reason: "git-push-failed"
169
- };
170
- }
171
- const prCreate = await run("gh", [
172
- "pr",
173
- "create",
174
- "--title",
175
- prTitle,
176
- "--body",
177
- prBody,
178
- "--base",
179
- defaultBranch,
180
- "--head",
181
- newBranch
182
- ], cwd);
183
- await restoreToPreviousBranch(false);
184
- if (!prCreate.success) return {
185
- status: "branch-pushed",
186
- branch: newBranch
187
- };
188
- return {
189
- status: "pr-opened",
190
- url: prCreate.stdout.split(/\r?\n/).filter(Boolean).pop() ?? ""
191
- };
192
- };
193
- const stageWorkflowFile = async (input) => {
194
- const workflowPath = path$1.resolve(input.workflowPath);
195
- const run = input.run ?? runCommand;
196
- const repoRootProbe = await run("git", ["rev-parse", "--show-toplevel"], path$1.dirname(workflowPath));
197
- if (!repoRootProbe.success) return false;
198
- const repositoryLocation = resolveWorkflowRepositoryLocation(workflowPath, repoRootProbe.stdout);
199
- if (repositoryLocation === null) return false;
200
- return (await run("git", [
201
- "add",
202
- "--",
203
- repositoryLocation.workflowRelativePath
204
- ], repositoryLocation.repositoryRoot)).success;
205
- };
206
- //#endregion
207
- //#region src/cli/utils/resolve-blocking-level.ts
208
- const VALID_BLOCKING_LEVELS = new Set([
209
- "error",
210
- "warning",
211
- "none"
212
- ]);
213
- const DEFAULT_BLOCKING_LEVEL = "error";
214
- const isValidBlockingLevel = (level) => VALID_BLOCKING_LEVELS.has(level);
215
- const pickBlockingLevel = (flags, userConfig) => flags.blocking ?? flags.failOn ?? userConfig?.blocking ?? userConfig?.failOn;
216
- const resolveBlockingLevel = (flags, userConfig) => {
217
- const sourceValue = pickBlockingLevel(flags, userConfig) ?? DEFAULT_BLOCKING_LEVEL;
218
- if (isValidBlockingLevel(sourceValue)) return sourceValue;
219
- cliLogger.warn(`Invalid blocking level "${sourceValue}". Expected one of: error, warning, none. Falling back to "${DEFAULT_BLOCKING_LEVEL}".`);
220
- return DEFAULT_BLOCKING_LEVEL;
221
- };
222
- //#endregion
223
- //#region src/cli/utils/resolve-scope.ts
224
- const SCOPE_VALUES = new Set([
225
- "full",
226
- "files",
227
- "changed",
228
- "lines"
229
- ]);
230
- const isScopeValue = (value) => SCOPE_VALUES.has(value);
231
- const coerceDeprecatedDiff = (value) => {
232
- if (value === void 0) return void 0;
233
- if (value === false || value === "false") return { scope: "full" };
234
- if (value === true || value === "true") return { scope: "changed" };
235
- if (value === "") return void 0;
236
- return {
237
- scope: "changed",
238
- base: value
239
- };
240
- };
241
- /**
242
- * Resolves the *requested* scope from flags + config without touching git.
243
- * Precedence mirrors `pickBlockingLevel`: flags win over config, and the new
244
- * `--scope` wins over the deprecated `--diff` alias on each side
245
- * (`flags.scope` > `flags.diff` > `config.scope` > `config.diff`). Returns
246
- * `scope: undefined` when nothing is set so the caller can default to `full`
247
- * or offer the interactive prompt.
248
- */
249
- const resolveScope = (flags, userConfig) => {
250
- const base = flags.base ?? userConfig?.base;
251
- if (typeof flags.scope === "string" && flags.scope.length > 0) {
252
- if (isScopeValue(flags.scope)) return {
253
- scope: flags.scope,
254
- base,
255
- usedDeprecatedDiff: false
256
- };
257
- cliLogger.warn(`Invalid --scope "${flags.scope}". Expected one of: full, files, changed, lines. Ignoring.`);
258
- }
259
- const flagDiff = coerceDeprecatedDiff(flags.diff);
260
- if (flagDiff !== void 0) return {
261
- scope: flagDiff.scope,
262
- base: base ?? flagDiff.base,
263
- usedDeprecatedDiff: true
264
- };
265
- if (userConfig?.scope !== void 0) {
266
- if (isScopeValue(userConfig.scope)) return {
267
- scope: userConfig.scope,
268
- base,
269
- usedDeprecatedDiff: false
270
- };
271
- cliLogger.warn(`Invalid scope "${userConfig.scope}" in config. Expected one of: full, files, changed, lines. Ignoring.`);
272
- }
273
- const configDiff = coerceDeprecatedDiff(userConfig?.diff);
274
- if (configDiff !== void 0) return {
275
- scope: configDiff.scope,
276
- base: base ?? configDiff.base,
277
- usedDeprecatedDiff: true
278
- };
279
- return {
280
- scope: void 0,
281
- base,
282
- usedDeprecatedDiff: false
283
- };
284
- };
285
- const warnDeprecatedDiff = (flags, userConfig) => {
286
- const usedFlag = flags.diff !== void 0;
287
- if (!usedFlag && userConfig?.diff === void 0) return;
288
- const targetScope = coerceDeprecatedDiff(usedFlag ? flags.diff : userConfig?.diff)?.scope ?? "changed";
289
- if (usedFlag) {
290
- const baseHint = targetScope === "changed" ? " (add `--base <ref>` to pin the base)" : "";
291
- cliLogger.warn(`The \`--diff\` flag is deprecated; use \`--scope ${targetScope}\`${baseHint} instead.`);
292
- return;
293
- }
294
- cliLogger.warn(`The \`diff\` config option is deprecated; use \`scope: "${targetScope}"\` instead.`);
295
- };
296
- const warnDiffUnavailable = (requested, isQuiet) => {
297
- if (isQuiet) return;
298
- if (typeof requested.base === "string") cliLogger.warn(`Could not compute diff against "${requested.base}" (git unavailable, ref not found, or merge-base failed). Running full scan.`);
299
- else cliLogger.warn("No feature branch or uncommitted changes detected. Running full scan.");
300
- cliLogger.break();
301
- };
302
- /**
303
- * Resolves the requested scope against the detected diff into the final scope.
304
- * An explicit non-`full` scope needs a usable `diffInfo`; without one it warns
305
- * and falls back to `full`. When nothing was requested, an interactive run on a
306
- * branch with changed source files is offered the "full vs changed" prompt.
307
- */
308
- const finalizeScope = async ({ requested, diffInfo, skipPrompts, isQuiet }) => {
309
- if (requested.scope !== void 0) {
310
- if (requested.scope === "full") return "full";
311
- if (diffInfo !== null) return requested.scope;
312
- warnDiffUnavailable(requested, isQuiet);
313
- return "full";
314
- }
315
- if (diffInfo === null || skipPrompts || isQuiet) return "full";
316
- const changedSourceFiles = filterSourceFiles([...diffInfo.changedFiles]);
317
- if (changedSourceFiles.length === 0) return "full";
318
- const { scanScope } = await prompts({
319
- type: "select",
320
- name: "scanScope",
321
- message: "Choose what to scan",
322
- choices: [{
323
- title: "Full codebase",
324
- description: "Scan every source file",
325
- value: "full"
326
- }, {
327
- title: diffInfo.isCurrentChanges ? `Uncommitted changes (${changedSourceFiles.length})` : `Changed files on ${diffInfo.currentBranch ?? "this branch"} (${changedSourceFiles.length})`,
328
- description: diffInfo.isCurrentChanges ? "Compare working tree changes against HEAD" : `Compare against ${diffInfo.baseBranch} from the branch merge-base`,
329
- value: "changed"
330
- }],
331
- initial: diffInfo.isCurrentChanges ? 0 : 1
332
- });
333
- return scanScope === "changed" ? "changed" : "full";
334
- };
335
- //#endregion
336
- export { isValidBlockingLevel as a, openWorkflowPullRequest as c, warnDeprecatedDiff as i, stageWorkflowFile as l, isScopeValue as n, pickBlockingLevel as o, resolveScope as r, resolveBlockingLevel as s, finalizeScope as t, toForwardSlashes as u };
337
-
338
- //# sourceMappingURL=resolve-scope-gy72JH3h.js.map
339
- //# debugId=6d1dde0f-d9fe-5053-b24c-3e5de5579599