react-doctor 0.9.1-dev.5d2f66d → 0.9.1-dev.8534d86
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/README.md +2 -0
- package/dist/cli.js +334 -45
- package/dist/index.d.ts +2 -2
- package/dist/index.js +329 -41
- package/dist/lsp.js +331 -42
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -13,6 +13,8 @@ React Doctor deterministically scans your codebase and finds issues across state
|
|
|
13
13
|
|
|
14
14
|
Works for all React frameworks and libraries - Next.js, Vite, TanStack, React Native, Expo, you name it.
|
|
15
15
|
|
|
16
|
+
Scans JavaScript and TypeScript source files plus JavaScript in standard inline `<script>` blocks in `.html` files.
|
|
17
|
+
|
|
16
18
|
[Website →](https://react.doctor/docs)
|
|
17
19
|
|
|
18
20
|
## Install
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
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]="
|
|
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]="68199c0f-9834-55ef-9753-efce9ed982ad")}catch(e){}}();
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import * as NodeChildProcess from "node:child_process";
|
|
5
5
|
import { execFile, execFileSync, spawn, spawnSync } from "node:child_process";
|
|
@@ -64616,7 +64616,8 @@ var AmbiguousProjectError = class extends Error {
|
|
|
64616
64616
|
}
|
|
64617
64617
|
};
|
|
64618
64618
|
const isProjectDiscoveryError = (value) => value instanceof ProjectNotFoundError || value instanceof NoReactDependencyError || value instanceof PackageJsonNotFoundError || value instanceof NotADirectoryError || value instanceof AmbiguousProjectError;
|
|
64619
|
-
const
|
|
64619
|
+
const HTML_FILE_PATTERN = /\.html$/i;
|
|
64620
|
+
const SOURCE_FILE_PATTERN = /\.(?:tsx?|jsx?|mts|mjs|[hH][tT][mM][lL])$/;
|
|
64620
64621
|
const GENERATED_BUNDLE_FILE_PATTERN = /\.(iife|umd|global|min)\.m?js$/i;
|
|
64621
64622
|
const GENERATED_SOURCE_DIRECTORY_PATTERN = /(?:^|\/)__generated__\//;
|
|
64622
64623
|
const MINIFIED_SNIFF_BYTES = 65536;
|
|
@@ -73835,6 +73836,12 @@ const appendReanimatedSharedValueHint = (help, rule, project) => {
|
|
|
73835
73836
|
if (!help) return REANIMATED_SHARED_VALUE_HINT;
|
|
73836
73837
|
return `${help}\n\n${REANIMATED_SHARED_VALUE_HINT}`;
|
|
73837
73838
|
};
|
|
73839
|
+
const columnOfUtf8Offset = (sourceBuffer, utf8Offset) => {
|
|
73840
|
+
const boundedOffset = Math.min(Math.max(utf8Offset, 0), sourceBuffer.length);
|
|
73841
|
+
let lineStartOffset = boundedOffset;
|
|
73842
|
+
while (lineStartOffset > 0 && sourceBuffer[lineStartOffset - 1] !== 10) lineStartOffset--;
|
|
73843
|
+
return sourceBuffer.subarray(lineStartOffset, boundedOffset).toString("utf8").length + 1;
|
|
73844
|
+
};
|
|
73838
73845
|
const REDACTED_PLACEHOLDER = "<redacted>";
|
|
73839
73846
|
const KEEP_PREFIX = `$1${REDACTED_PLACEHOLDER}`;
|
|
73840
73847
|
const KNOWN_SECRET_RULES = [
|
|
@@ -74550,7 +74557,7 @@ const resolveMatchByOccurrence = (rule, category) => OCCURRENCE_MATCHED_CATEGORI
|
|
|
74550
74557
|
* with empty text still carry a useful jump target, so they're kept
|
|
74551
74558
|
* with a neutral message.
|
|
74552
74559
|
*/
|
|
74553
|
-
const buildRelatedLocations = (labels, filePath) => {
|
|
74560
|
+
const buildRelatedLocations = (labels, filePath, sourceBuffer) => {
|
|
74554
74561
|
if (labels.length <= 1) return [];
|
|
74555
74562
|
const related = [];
|
|
74556
74563
|
for (let labelIndex = 1; labelIndex < labels.length; labelIndex++) {
|
|
@@ -74558,8 +74565,8 @@ const buildRelatedLocations = (labels, filePath) => {
|
|
|
74558
74565
|
if (!label?.span) continue;
|
|
74559
74566
|
related.push({
|
|
74560
74567
|
filePath,
|
|
74561
|
-
line: label.span.line ?? 0,
|
|
74562
|
-
column: label.span.column ?? 0,
|
|
74568
|
+
line: sourceBuffer === null ? label.span.line ?? 0 : lineOfUtf8Offset(sourceBuffer, label.span.offset),
|
|
74569
|
+
column: sourceBuffer === null ? label.span.column ?? 0 : columnOfUtf8Offset(sourceBuffer, label.span.offset),
|
|
74563
74570
|
offset: label.span.offset,
|
|
74564
74571
|
length: label.span.length,
|
|
74565
74572
|
message: label.label ?? ""
|
|
@@ -74579,7 +74586,7 @@ const isOxlintOutput = (value) => isRecord$1(value) && Array.isArray(value.diagn
|
|
|
74579
74586
|
* to the first `{`. Raises `OxlintOutputUnparseable` when the
|
|
74580
74587
|
* stdout isn't valid JSON or doesn't carry a `diagnostics` array.
|
|
74581
74588
|
*/
|
|
74582
|
-
const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
74589
|
+
const parseOxlintOutput = (stdout, project, rootDirectory, sourcePathByLintPath) => {
|
|
74583
74590
|
if (!stdout) return [];
|
|
74584
74591
|
const jsonStart = stdout.indexOf("{");
|
|
74585
74592
|
const sanitizedStdout = jsonStart > 0 ? stdout.slice(jsonStart) : stdout;
|
|
@@ -74596,8 +74603,10 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
74596
74603
|
const sourceBufferCache = /* @__PURE__ */ new Map();
|
|
74597
74604
|
const sourceLinesCache = /* @__PURE__ */ new Map();
|
|
74598
74605
|
const resolveAbsolutePath = (filename) => Path.isAbsolute(filename) ? filename : Path.resolve(rootDirectory || ".", filename);
|
|
74606
|
+
const resolveMappedSourceFilename = (filename) => sourcePathByLintPath?.get(Path.normalize(resolveAbsolutePath(filename)));
|
|
74607
|
+
const resolveSourceFilename = (filename) => resolveMappedSourceFilename(filename) ?? filename;
|
|
74599
74608
|
const readSourceBuffer = (filename) => {
|
|
74600
|
-
const absolutePath = resolveAbsolutePath(filename);
|
|
74609
|
+
const absolutePath = resolveAbsolutePath(resolveSourceFilename(filename));
|
|
74601
74610
|
const cached = sourceBufferCache.get(absolutePath);
|
|
74602
74611
|
if (cached !== void 0) return cached;
|
|
74603
74612
|
let sourceBuffer;
|
|
@@ -74610,7 +74619,7 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
74610
74619
|
return sourceBuffer;
|
|
74611
74620
|
};
|
|
74612
74621
|
const readSourceLines = (filename) => {
|
|
74613
|
-
const absolutePath = resolveAbsolutePath(filename);
|
|
74622
|
+
const absolutePath = resolveAbsolutePath(resolveSourceFilename(filename));
|
|
74614
74623
|
const cached = sourceLinesCache.get(absolutePath);
|
|
74615
74624
|
if (cached !== void 0) return cached;
|
|
74616
74625
|
const sourceBuffer = readSourceBuffer(filename);
|
|
@@ -74619,27 +74628,30 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
74619
74628
|
return sourceLines;
|
|
74620
74629
|
};
|
|
74621
74630
|
const isMinifiedDiagnosticFile = (filename) => {
|
|
74622
|
-
const absolutePath = resolveAbsolutePath(filename);
|
|
74631
|
+
const absolutePath = resolveAbsolutePath(resolveSourceFilename(filename));
|
|
74623
74632
|
const cached = minifiedFileCache.get(absolutePath);
|
|
74624
74633
|
if (cached !== void 0) return cached;
|
|
74625
74634
|
const minified = isMinifiedSource(absolutePath);
|
|
74626
74635
|
minifiedFileCache.set(absolutePath, minified);
|
|
74627
74636
|
return minified;
|
|
74628
74637
|
};
|
|
74629
|
-
return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => isMappableOxlintDiagnostic(diagnostic) && isLintableSourceFile(diagnostic.filename) && !(TYPESCRIPT_DECLARATION_FILE_PATTERN.test(diagnostic.filename) && OXLINT_IGNORED_DECLARATION_DIAGNOSTIC_CODES.has(diagnostic.code)) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory) && !shouldSuppressCompilerFindingInWorklet(diagnostic, project, rootDirectory)).map((diagnostic) => {
|
|
74638
|
+
return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => isMappableOxlintDiagnostic(diagnostic) && isLintableSourceFile(resolveSourceFilename(diagnostic.filename)) && !(TYPESCRIPT_DECLARATION_FILE_PATTERN.test(diagnostic.filename) && OXLINT_IGNORED_DECLARATION_DIAGNOSTIC_CODES.has(diagnostic.code)) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory) && !shouldSuppressCompilerFindingInWorklet(diagnostic, project, rootDirectory)).map((diagnostic) => {
|
|
74630
74639
|
const { plugin, rule } = parseRuleCode(diagnostic.code);
|
|
74631
74640
|
const primaryLabel = diagnostic.labels[0];
|
|
74632
74641
|
const cleaned = cleanDiagnosticMessage(diagnostic.message, diagnostic.help, plugin, rule, project);
|
|
74633
|
-
const
|
|
74642
|
+
const mappedSourceFilename = resolveMappedSourceFilename(diagnostic.filename);
|
|
74643
|
+
const normalizedFilePath = (mappedSourceFilename ?? diagnostic.filename).replaceAll("\\", "/");
|
|
74634
74644
|
const primarySpan = primaryLabel?.span;
|
|
74635
74645
|
const sourceBuffer = primarySpan ? readSourceBuffer(diagnostic.filename) : null;
|
|
74636
|
-
const relatedLocations = buildRelatedLocations(diagnostic.labels, normalizedFilePath);
|
|
74646
|
+
const relatedLocations = buildRelatedLocations(diagnostic.labels, normalizedFilePath, mappedSourceFilename === void 0 ? null : sourceBuffer);
|
|
74637
74647
|
const category = resolveDiagnosticCategory(plugin, rule);
|
|
74638
74648
|
const matchByOccurrence = resolveMatchByOccurrence(rule, category);
|
|
74639
74649
|
const sourceLines = primarySpan ? readSourceLines(diagnostic.filename) : null;
|
|
74640
|
-
const
|
|
74650
|
+
const primaryLineNumber = primarySpan && mappedSourceFilename !== void 0 && sourceBuffer !== null ? lineOfUtf8Offset(sourceBuffer, primarySpan.offset) : primarySpan?.line ?? 0;
|
|
74651
|
+
const primaryColumnNumber = primarySpan && mappedSourceFilename !== void 0 && sourceBuffer !== null ? columnOfUtf8Offset(sourceBuffer, primarySpan.offset) : primarySpan?.column ?? 0;
|
|
74652
|
+
const primaryLineIndex = primarySpan ? primaryLineNumber - 1 : -1;
|
|
74641
74653
|
const primaryLine = sourceLines?.[primaryLineIndex];
|
|
74642
|
-
const primaryColumnIndex = primarySpan ?
|
|
74654
|
+
const primaryColumnIndex = primarySpan ? primaryColumnNumber - 1 : -1;
|
|
74643
74655
|
const isJsxTagLabel = primaryLine !== void 0 && primaryColumnIndex >= 0 && (primaryLine[primaryColumnIndex] === "<" || primaryLine[primaryColumnIndex - 1] === "<");
|
|
74644
74656
|
const jsxOpenerEndLineIndex = sourceLines && isJsxTagLabel ? findJsxOpenerSpan(sourceLines, primaryLineIndex) : null;
|
|
74645
74657
|
const primarySpanEndLine = jsxOpenerEndLineIndex !== null ? jsxOpenerEndLineIndex + 1 : primarySpan && sourceBuffer ? lineOfUtf8Offset(sourceBuffer, primarySpan.offset + Math.max(primarySpan.length - 1, 0)) : void 0;
|
|
@@ -74652,8 +74664,8 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
74652
74664
|
message: cleaned.message,
|
|
74653
74665
|
help: cleaned.help,
|
|
74654
74666
|
url: diagnostic.url,
|
|
74655
|
-
line:
|
|
74656
|
-
column:
|
|
74667
|
+
line: primaryLineNumber,
|
|
74668
|
+
column: primaryColumnNumber,
|
|
74657
74669
|
...primarySpan ? {
|
|
74658
74670
|
offset: primarySpan.offset,
|
|
74659
74671
|
length: primarySpan.length,
|
|
@@ -74841,7 +74853,11 @@ const isParallelismRelatedSpawnError = (error) => {
|
|
|
74841
74853
|
* loop with a slimmer config in that case.
|
|
74842
74854
|
*/
|
|
74843
74855
|
const spawnLintBatches = async (input) => {
|
|
74844
|
-
const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth = 8, signal, deadlineEpochMs } = input;
|
|
74856
|
+
const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, sourcePathByLintPath, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth = 8, signal, deadlineEpochMs } = input;
|
|
74857
|
+
const resolveSourcePath = (filePath) => {
|
|
74858
|
+
const absoluteFilePath = Path.isAbsolute(filePath) ? filePath : Path.resolve(rootDirectory, filePath);
|
|
74859
|
+
return sourcePathByLintPath?.get(Path.normalize(absoluteFilePath)) ?? filePath;
|
|
74860
|
+
};
|
|
74845
74861
|
const requestedConcurrency = resolveScanConcurrency(input.concurrency ?? 1);
|
|
74846
74862
|
let rescueDeadlineEpochMs;
|
|
74847
74863
|
const isPastDeadline = () => deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(deadlineEpochMs) === 0 || rescueDeadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(rescueDeadlineEpochMs) === 0;
|
|
@@ -74861,7 +74877,7 @@ const spawnLintBatches = async (input) => {
|
|
|
74861
74877
|
}
|
|
74862
74878
|
const batchArgs = [...baseArgs, ...batch];
|
|
74863
74879
|
try {
|
|
74864
|
-
return parseOxlintOutput(await spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath, spawnTimeoutMs, outputMaxBytes, signal), project, rootDirectory);
|
|
74880
|
+
return parseOxlintOutput(await spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath, spawnTimeoutMs, outputMaxBytes, signal), project, rootDirectory, sourcePathByLintPath);
|
|
74865
74881
|
} catch (error) {
|
|
74866
74882
|
if (!isSplittableReactDoctorError(error)) throw error;
|
|
74867
74883
|
if (isPastDeadline()) {
|
|
@@ -74967,10 +74983,12 @@ const spawnLintBatches = async (input) => {
|
|
|
74967
74983
|
onPartialFailure(buildMessage(`${skippedFiles.slice(0, 3).join(", ")}${skippedFiles.length > 3 ? `, +${skippedFiles.length - 3} more` : ""}`));
|
|
74968
74984
|
};
|
|
74969
74985
|
const reasonHint = firstDropReason ? ` — first failure: ${firstDropReason}` : "";
|
|
74970
|
-
|
|
74971
|
-
|
|
74986
|
+
const droppedSourceFiles = [...new Set(droppedFiles.map(resolveSourcePath))];
|
|
74987
|
+
const deadlineSkippedSourceFiles = [...new Set(outcome.deadlineSkippedFiles.map(resolveSourcePath))];
|
|
74988
|
+
reportSkippedFiles(droppedSourceFiles, (fileListPreview) => `${droppedSourceFiles.length} file(s) failed to lint and were skipped (${fileListPreview})${reasonHint}`);
|
|
74989
|
+
reportSkippedFiles(deadlineSkippedSourceFiles, (fileListPreview) => `${deadlineSkippedSourceFiles.length} file(s) skipped — max scan duration reached before they were linted (${fileListPreview})`);
|
|
74972
74990
|
const unavailableFiles = new Set([...droppedFiles, ...outcome.deadlineSkippedFiles]);
|
|
74973
|
-
input.onAnalyzedFiles?.([...new Set(fileBatches.flat().filter((filePath) => !unavailableFiles.has(filePath)))].sort());
|
|
74991
|
+
input.onAnalyzedFiles?.([...new Set(fileBatches.flat().filter((filePath) => !unavailableFiles.has(filePath)).map(resolveSourcePath))].sort());
|
|
74974
74992
|
return dedupeDiagnostics(diagnostics);
|
|
74975
74993
|
};
|
|
74976
74994
|
let didValidate = false;
|
|
@@ -75069,6 +75087,266 @@ const planLintBatches = ({ baseArgs, files, sizeByFile }) => {
|
|
|
75069
75087
|
}
|
|
75070
75088
|
return batches.filter((batch) => batch.fileIndexes.length > 0).map((batch) => batch.fileIndexes.sort((leftIndex, rightIndex) => leftIndex - rightIndex).map((fileIndex) => files[fileIndex]));
|
|
75071
75089
|
};
|
|
75090
|
+
const THREE_MODULE_IMPORT_PATTERN = /\b(?:from\s*|import\s*(?:\(\s*)?|require\s*\(\s*)["']three(?:\/[^"']*)?["']/;
|
|
75091
|
+
const containsThreeModuleImport = (sourceText) => THREE_MODULE_IMPORT_PATTERN.test(sourceText);
|
|
75092
|
+
const COMMENT_OPEN = "<!--";
|
|
75093
|
+
const COMMENT_CLOSE = "-->";
|
|
75094
|
+
const FRONTMATTER_FENCE = "---";
|
|
75095
|
+
const SCRIPT_TAG_NAME = "script";
|
|
75096
|
+
const SCRIPT_OPEN = `<${SCRIPT_TAG_NAME}`;
|
|
75097
|
+
const SCRIPT_CLOSE = `</${SCRIPT_TAG_NAME}`;
|
|
75098
|
+
const TEMPLATE_TAG_NAME = "template";
|
|
75099
|
+
const TEMPLATE_CLOSE = `</${TEMPLATE_TAG_NAME}`;
|
|
75100
|
+
const JAVASCRIPT_MIME_TYPES = new Set([
|
|
75101
|
+
"application/ecmascript",
|
|
75102
|
+
"application/javascript",
|
|
75103
|
+
"application/x-ecmascript",
|
|
75104
|
+
"application/x-javascript",
|
|
75105
|
+
"text/ecmascript",
|
|
75106
|
+
"text/javascript",
|
|
75107
|
+
"text/javascript1.0",
|
|
75108
|
+
"text/javascript1.1",
|
|
75109
|
+
"text/javascript1.2",
|
|
75110
|
+
"text/javascript1.3",
|
|
75111
|
+
"text/javascript1.4",
|
|
75112
|
+
"text/javascript1.5",
|
|
75113
|
+
"text/jscript",
|
|
75114
|
+
"text/livescript",
|
|
75115
|
+
"text/x-ecmascript",
|
|
75116
|
+
"text/x-javascript"
|
|
75117
|
+
]);
|
|
75118
|
+
const RAW_TEXT_TAG_NAMES = new Set([
|
|
75119
|
+
"iframe",
|
|
75120
|
+
"noembed",
|
|
75121
|
+
"noframes",
|
|
75122
|
+
"noscript",
|
|
75123
|
+
"style",
|
|
75124
|
+
"textarea",
|
|
75125
|
+
"title",
|
|
75126
|
+
"xmp"
|
|
75127
|
+
]);
|
|
75128
|
+
const PLAINTEXT_TAG_NAME = "plaintext";
|
|
75129
|
+
const TAG_OPEN_UTF8_BYTE = "<".charCodeAt(0);
|
|
75130
|
+
const TAG_CLOSE_UTF8_BYTE = ">".charCodeAt(0);
|
|
75131
|
+
const SINGLE_QUOTE_UTF8_BYTE = "'".charCodeAt(0);
|
|
75132
|
+
const DOUBLE_QUOTE_UTF8_BYTE = "\"".charCodeAt(0);
|
|
75133
|
+
const ATTRIBUTE_ASSIGNMENT_UTF8_BYTE = "=".charCodeAt(0);
|
|
75134
|
+
const TAG_SELF_CLOSE_UTF8_BYTE = "/".charCodeAt(0);
|
|
75135
|
+
const TAB_UTF8_BYTE = " ".charCodeAt(0);
|
|
75136
|
+
const FORM_FEED_UTF8_BYTE = "\f".charCodeAt(0);
|
|
75137
|
+
const isHtmlWhitespace = (byte) => byte === TAB_UTF8_BYTE || byte === 10 || byte === FORM_FEED_UTF8_BYTE || byte === 13 || byte === 32;
|
|
75138
|
+
const matchesAsciiCaseInsensitive = (sourceBuffer, startByte, expected) => {
|
|
75139
|
+
if (startByte + expected.length > sourceBuffer.length) return false;
|
|
75140
|
+
for (let expectedIndex = 0; expectedIndex < expected.length; expectedIndex++) {
|
|
75141
|
+
const sourceByte = sourceBuffer[startByte + expectedIndex];
|
|
75142
|
+
if (sourceByte === void 0 || String.fromCharCode(sourceByte).toLowerCase() !== expected[expectedIndex]) return false;
|
|
75143
|
+
}
|
|
75144
|
+
return true;
|
|
75145
|
+
};
|
|
75146
|
+
const isTagBoundary = (byte) => byte === TAG_CLOSE_UTF8_BYTE || byte === TAG_SELF_CLOSE_UTF8_BYTE || isHtmlWhitespace(byte);
|
|
75147
|
+
const isPotentialTagStart = (byte) => {
|
|
75148
|
+
if (byte === void 0) return false;
|
|
75149
|
+
return /[A-Za-z!/?]/.test(String.fromCharCode(byte));
|
|
75150
|
+
};
|
|
75151
|
+
const findLineEnd = (sourceBuffer, lineStartByte) => {
|
|
75152
|
+
let byteIndex = lineStartByte;
|
|
75153
|
+
while (byteIndex < sourceBuffer.length && sourceBuffer[byteIndex] !== 10 && sourceBuffer[byteIndex] !== 13) byteIndex++;
|
|
75154
|
+
return byteIndex;
|
|
75155
|
+
};
|
|
75156
|
+
const findNextLineStart = (sourceBuffer, lineStartByte) => {
|
|
75157
|
+
const lineEndByte = findLineEnd(sourceBuffer, lineStartByte);
|
|
75158
|
+
if (sourceBuffer[lineEndByte] === 13 && sourceBuffer[lineEndByte + 1] === 10) return lineEndByte + 2;
|
|
75159
|
+
return Math.min(lineEndByte + 1, sourceBuffer.length);
|
|
75160
|
+
};
|
|
75161
|
+
const isFrontmatterFenceLine = (sourceBuffer, lineStartByte) => {
|
|
75162
|
+
if (!matchesAsciiCaseInsensitive(sourceBuffer, lineStartByte, FRONTMATTER_FENCE)) return false;
|
|
75163
|
+
const lineEndByte = findLineEnd(sourceBuffer, lineStartByte);
|
|
75164
|
+
for (let byteIndex = lineStartByte + 3; byteIndex < lineEndByte; byteIndex++) if (!isHtmlWhitespace(sourceBuffer[byteIndex])) return false;
|
|
75165
|
+
return true;
|
|
75166
|
+
};
|
|
75167
|
+
const findFrontmatterEnd = (sourceBuffer) => {
|
|
75168
|
+
if (!isFrontmatterFenceLine(sourceBuffer, 0)) return 0;
|
|
75169
|
+
const openingFenceEndByte = findLineEnd(sourceBuffer, 0);
|
|
75170
|
+
let lineStartByte = findNextLineStart(sourceBuffer, 0);
|
|
75171
|
+
while (lineStartByte < sourceBuffer.length) {
|
|
75172
|
+
if (isFrontmatterFenceLine(sourceBuffer, lineStartByte)) return findLineEnd(sourceBuffer, lineStartByte);
|
|
75173
|
+
lineStartByte = findNextLineStart(sourceBuffer, lineStartByte);
|
|
75174
|
+
}
|
|
75175
|
+
return openingFenceEndByte;
|
|
75176
|
+
};
|
|
75177
|
+
const maskByteRange = (maskedBuffer, startByte, endByte) => {
|
|
75178
|
+
for (let byteIndex = startByte; byteIndex < endByte; byteIndex++) {
|
|
75179
|
+
const byte = maskedBuffer[byteIndex];
|
|
75180
|
+
if (byte !== 10 && byte !== 13) maskedBuffer[byteIndex] = 32;
|
|
75181
|
+
}
|
|
75182
|
+
};
|
|
75183
|
+
const readStartTagName = (sourceBuffer, tagStartByte) => {
|
|
75184
|
+
const firstNameByte = sourceBuffer[tagStartByte + 1];
|
|
75185
|
+
if (firstNameByte === void 0 || !/[A-Za-z]/.test(String.fromCharCode(firstNameByte))) return null;
|
|
75186
|
+
let nameEndByte = tagStartByte + 2;
|
|
75187
|
+
while (nameEndByte < sourceBuffer.length && /[A-Za-z0-9:-]/.test(String.fromCharCode(sourceBuffer[nameEndByte] ?? 32))) nameEndByte++;
|
|
75188
|
+
return sourceBuffer.subarray(tagStartByte + 1, nameEndByte).toString("ascii").toLowerCase();
|
|
75189
|
+
};
|
|
75190
|
+
const findTagEnd = (sourceBuffer, startByte) => {
|
|
75191
|
+
let quoteByte = null;
|
|
75192
|
+
for (let byteIndex = startByte; byteIndex < sourceBuffer.length; byteIndex++) {
|
|
75193
|
+
const byte = sourceBuffer[byteIndex];
|
|
75194
|
+
if (quoteByte !== null) {
|
|
75195
|
+
if (byte === quoteByte) quoteByte = null;
|
|
75196
|
+
continue;
|
|
75197
|
+
}
|
|
75198
|
+
if (byte === SINGLE_QUOTE_UTF8_BYTE || byte === DOUBLE_QUOTE_UTF8_BYTE) {
|
|
75199
|
+
quoteByte = byte;
|
|
75200
|
+
continue;
|
|
75201
|
+
}
|
|
75202
|
+
if (byte === TAG_CLOSE_UTF8_BYTE) return byteIndex;
|
|
75203
|
+
}
|
|
75204
|
+
return sourceBuffer.length;
|
|
75205
|
+
};
|
|
75206
|
+
const parseScriptAttributes = (sourceBuffer, attributesStartByte, tagEndByte) => {
|
|
75207
|
+
let byteIndex = attributesStartByte;
|
|
75208
|
+
let hasSource = false;
|
|
75209
|
+
let language = null;
|
|
75210
|
+
let scriptType = null;
|
|
75211
|
+
while (byteIndex < tagEndByte) {
|
|
75212
|
+
while (byteIndex < tagEndByte && (isHtmlWhitespace(sourceBuffer[byteIndex]) || sourceBuffer[byteIndex] === TAG_SELF_CLOSE_UTF8_BYTE)) byteIndex++;
|
|
75213
|
+
const attributeNameStartByte = byteIndex;
|
|
75214
|
+
while (byteIndex < tagEndByte && !isHtmlWhitespace(sourceBuffer[byteIndex]) && sourceBuffer[byteIndex] !== ATTRIBUTE_ASSIGNMENT_UTF8_BYTE && sourceBuffer[byteIndex] !== TAG_SELF_CLOSE_UTF8_BYTE) byteIndex++;
|
|
75215
|
+
const attributeName = sourceBuffer.subarray(attributeNameStartByte, byteIndex).toString("ascii").toLowerCase();
|
|
75216
|
+
while (byteIndex < tagEndByte && isHtmlWhitespace(sourceBuffer[byteIndex])) byteIndex++;
|
|
75217
|
+
let attributeValue = "";
|
|
75218
|
+
if (sourceBuffer[byteIndex] === ATTRIBUTE_ASSIGNMENT_UTF8_BYTE) {
|
|
75219
|
+
byteIndex++;
|
|
75220
|
+
while (byteIndex < tagEndByte && isHtmlWhitespace(sourceBuffer[byteIndex])) byteIndex++;
|
|
75221
|
+
const quoteByte = sourceBuffer[byteIndex];
|
|
75222
|
+
if (quoteByte === SINGLE_QUOTE_UTF8_BYTE || quoteByte === DOUBLE_QUOTE_UTF8_BYTE) {
|
|
75223
|
+
const valueStartByte = byteIndex + 1;
|
|
75224
|
+
byteIndex = valueStartByte;
|
|
75225
|
+
while (byteIndex < tagEndByte && sourceBuffer[byteIndex] !== quoteByte) byteIndex++;
|
|
75226
|
+
attributeValue = sourceBuffer.subarray(valueStartByte, byteIndex).toString("utf8");
|
|
75227
|
+
if (byteIndex < tagEndByte) byteIndex++;
|
|
75228
|
+
} else {
|
|
75229
|
+
const valueStartByte = byteIndex;
|
|
75230
|
+
while (byteIndex < tagEndByte && !isHtmlWhitespace(sourceBuffer[byteIndex]) && sourceBuffer[byteIndex] !== TAG_CLOSE_UTF8_BYTE) byteIndex++;
|
|
75231
|
+
attributeValue = sourceBuffer.subarray(valueStartByte, byteIndex).toString("utf8");
|
|
75232
|
+
}
|
|
75233
|
+
}
|
|
75234
|
+
if (attributeName === "src") hasSource = true;
|
|
75235
|
+
else if (attributeName === "type" && scriptType === null) scriptType = attributeValue;
|
|
75236
|
+
else if (attributeName === "language" && language === null) language = attributeValue;
|
|
75237
|
+
}
|
|
75238
|
+
return {
|
|
75239
|
+
hasSource,
|
|
75240
|
+
language,
|
|
75241
|
+
type: scriptType
|
|
75242
|
+
};
|
|
75243
|
+
};
|
|
75244
|
+
const isExecutableScript = (attributes) => {
|
|
75245
|
+
if (attributes.hasSource) return false;
|
|
75246
|
+
const type = attributes.type?.trim().toLowerCase() ?? "";
|
|
75247
|
+
if (type !== "") {
|
|
75248
|
+
const mimeType = type.split(";")[0]?.trim() ?? "";
|
|
75249
|
+
return mimeType === "module" || JAVASCRIPT_MIME_TYPES.has(mimeType);
|
|
75250
|
+
}
|
|
75251
|
+
const language = attributes.language?.trim().toLowerCase() ?? "";
|
|
75252
|
+
return language === "" || JAVASCRIPT_MIME_TYPES.has(`text/${language}`);
|
|
75253
|
+
};
|
|
75254
|
+
const findElementClose = (sourceBuffer, startByte, tagName) => {
|
|
75255
|
+
const closeTagStart = `</${tagName}`;
|
|
75256
|
+
for (let byteIndex = startByte; byteIndex < sourceBuffer.length; byteIndex++) if (sourceBuffer[byteIndex] === TAG_OPEN_UTF8_BYTE && matchesAsciiCaseInsensitive(sourceBuffer, byteIndex, closeTagStart) && isTagBoundary(sourceBuffer[byteIndex + closeTagStart.length])) return byteIndex;
|
|
75257
|
+
return sourceBuffer.length;
|
|
75258
|
+
};
|
|
75259
|
+
const prepareHtmlScriptSource = (sourceBuffer) => {
|
|
75260
|
+
const frontmatterEndByte = findFrontmatterEnd(sourceBuffer);
|
|
75261
|
+
let maskedBuffer = frontmatterEndByte > 0 ? Buffer.from(sourceBuffer) : null;
|
|
75262
|
+
if (maskedBuffer !== null) maskByteRange(maskedBuffer, 0, frontmatterEndByte);
|
|
75263
|
+
const executableScriptBodies = [];
|
|
75264
|
+
let byteIndex = frontmatterEndByte;
|
|
75265
|
+
let templateDepth = 0;
|
|
75266
|
+
while (byteIndex < sourceBuffer.length) {
|
|
75267
|
+
if (sourceBuffer[byteIndex] !== TAG_OPEN_UTF8_BYTE) {
|
|
75268
|
+
byteIndex++;
|
|
75269
|
+
continue;
|
|
75270
|
+
}
|
|
75271
|
+
if (matchesAsciiCaseInsensitive(sourceBuffer, byteIndex, COMMENT_OPEN)) {
|
|
75272
|
+
const commentEndByte = sourceBuffer.indexOf(COMMENT_CLOSE, byteIndex + 4);
|
|
75273
|
+
byteIndex = commentEndByte === -1 ? sourceBuffer.length : commentEndByte + 3;
|
|
75274
|
+
continue;
|
|
75275
|
+
}
|
|
75276
|
+
if (matchesAsciiCaseInsensitive(sourceBuffer, byteIndex, TEMPLATE_CLOSE) && isTagBoundary(sourceBuffer[byteIndex + TEMPLATE_CLOSE.length])) {
|
|
75277
|
+
const tagEndByte = findTagEnd(sourceBuffer, byteIndex + TEMPLATE_CLOSE.length);
|
|
75278
|
+
if (templateDepth > 0) templateDepth--;
|
|
75279
|
+
byteIndex = Math.min(tagEndByte + 1, sourceBuffer.length);
|
|
75280
|
+
continue;
|
|
75281
|
+
}
|
|
75282
|
+
if (!matchesAsciiCaseInsensitive(sourceBuffer, byteIndex, SCRIPT_OPEN) || !isTagBoundary(sourceBuffer[byteIndex + SCRIPT_OPEN.length])) {
|
|
75283
|
+
if (!isPotentialTagStart(sourceBuffer[byteIndex + 1])) {
|
|
75284
|
+
byteIndex++;
|
|
75285
|
+
continue;
|
|
75286
|
+
}
|
|
75287
|
+
const tagEndByte = findTagEnd(sourceBuffer, byteIndex + 1);
|
|
75288
|
+
const tagName = readStartTagName(sourceBuffer, byteIndex);
|
|
75289
|
+
if (tagName === PLAINTEXT_TAG_NAME) break;
|
|
75290
|
+
if (tagName === TEMPLATE_TAG_NAME) templateDepth++;
|
|
75291
|
+
if (tagName !== null && RAW_TEXT_TAG_NAMES.has(tagName)) {
|
|
75292
|
+
const closeTagStartByte = findElementClose(sourceBuffer, tagEndByte + 1, tagName);
|
|
75293
|
+
if (closeTagStartByte === sourceBuffer.length) break;
|
|
75294
|
+
const closeTagEndByte = findTagEnd(sourceBuffer, closeTagStartByte + tagName.length + 2);
|
|
75295
|
+
byteIndex = Math.min(closeTagEndByte + 1, sourceBuffer.length);
|
|
75296
|
+
continue;
|
|
75297
|
+
}
|
|
75298
|
+
byteIndex = Math.min(tagEndByte + 1, sourceBuffer.length);
|
|
75299
|
+
continue;
|
|
75300
|
+
}
|
|
75301
|
+
const tagEndByte = findTagEnd(sourceBuffer, byteIndex + SCRIPT_OPEN.length);
|
|
75302
|
+
if (tagEndByte === sourceBuffer.length) break;
|
|
75303
|
+
const bodyStartByte = tagEndByte + 1;
|
|
75304
|
+
const bodyEndByte = findElementClose(sourceBuffer, bodyStartByte, SCRIPT_TAG_NAME);
|
|
75305
|
+
const attributes = parseScriptAttributes(sourceBuffer, byteIndex + SCRIPT_OPEN.length, tagEndByte);
|
|
75306
|
+
if (attributes.hasSource || templateDepth > 0) {
|
|
75307
|
+
maskedBuffer ??= Buffer.from(sourceBuffer);
|
|
75308
|
+
maskByteRange(maskedBuffer, bodyStartByte, bodyEndByte);
|
|
75309
|
+
} else if (isExecutableScript(attributes)) executableScriptBodies.push(sourceBuffer.subarray(bodyStartByte, bodyEndByte));
|
|
75310
|
+
if (bodyEndByte === sourceBuffer.length) break;
|
|
75311
|
+
const closeTagEndByte = findTagEnd(sourceBuffer, bodyEndByte + SCRIPT_CLOSE.length);
|
|
75312
|
+
byteIndex = Math.min(closeTagEndByte + 1, sourceBuffer.length);
|
|
75313
|
+
}
|
|
75314
|
+
return {
|
|
75315
|
+
executableScriptBodies,
|
|
75316
|
+
lintBuffer: maskedBuffer ?? sourceBuffer
|
|
75317
|
+
};
|
|
75318
|
+
};
|
|
75319
|
+
const prepareHtmlLintSources = (rootDirectory, temporaryDirectory, candidateFiles) => {
|
|
75320
|
+
const lintFiles = [];
|
|
75321
|
+
const sourcePathByLintPath = /* @__PURE__ */ new Map();
|
|
75322
|
+
const sizeByLintPath = /* @__PURE__ */ new Map();
|
|
75323
|
+
const htmlSourcesDirectory = Path.join(temporaryDirectory, "html-sources");
|
|
75324
|
+
let htmlFileIndex = 0;
|
|
75325
|
+
let hasThreeModuleImport = false;
|
|
75326
|
+
for (const candidateFile of candidateFiles) {
|
|
75327
|
+
if (!HTML_FILE_PATTERN.test(candidateFile)) {
|
|
75328
|
+
lintFiles.push(candidateFile);
|
|
75329
|
+
continue;
|
|
75330
|
+
}
|
|
75331
|
+
const absoluteSourcePath = Path.isAbsolute(candidateFile) ? candidateFile : Path.resolve(rootDirectory, candidateFile);
|
|
75332
|
+
const sourceBuffer = NFS.readFileSync(absoluteSourcePath);
|
|
75333
|
+
const { executableScriptBodies, lintBuffer } = prepareHtmlScriptSource(sourceBuffer);
|
|
75334
|
+
if (htmlFileIndex === 0) NFS.mkdirSync(htmlSourcesDirectory, { recursive: true });
|
|
75335
|
+
const lintPath = Path.join(htmlSourcesDirectory, `${htmlFileIndex}.astro`);
|
|
75336
|
+
NFS.writeFileSync(lintPath, lintBuffer);
|
|
75337
|
+
lintFiles.push(lintPath);
|
|
75338
|
+
sourcePathByLintPath.set(Path.resolve(lintPath), candidateFile);
|
|
75339
|
+
sizeByLintPath.set(lintPath, sourceBuffer.length);
|
|
75340
|
+
hasThreeModuleImport ||= executableScriptBodies.some((scriptBody) => containsThreeModuleImport(scriptBody.toString("utf8")));
|
|
75341
|
+
htmlFileIndex++;
|
|
75342
|
+
}
|
|
75343
|
+
return {
|
|
75344
|
+
lintFiles,
|
|
75345
|
+
sourcePathByLintPath,
|
|
75346
|
+
sizeByLintPath,
|
|
75347
|
+
hasThreeModuleImport
|
|
75348
|
+
};
|
|
75349
|
+
};
|
|
75072
75350
|
/**
|
|
75073
75351
|
* Atomically (re)writes the generated oxlintrc.json. Used twice in
|
|
75074
75352
|
* the runner: once for the primary scan, once for the
|
|
@@ -75242,21 +75520,6 @@ const runOxlint = async (options) => {
|
|
|
75242
75520
|
const pluginPath = resolvePluginPath();
|
|
75243
75521
|
const extendsPaths = (adoptExistingLintConfig && !customRulesOnly ? detectUserLintConfigPaths(rootDirectory) : []).filter(canOxlintExtendConfig);
|
|
75244
75522
|
const userPlugins = includedTags.size > 0 ? [] : resolveUserPlugins(userConfig?.plugins, configSourceDirectory);
|
|
75245
|
-
const buildConfig = (overrides) => createOxlintConfig({
|
|
75246
|
-
pluginPath,
|
|
75247
|
-
project,
|
|
75248
|
-
customRulesOnly,
|
|
75249
|
-
extendsPaths: overrides.extendsPaths,
|
|
75250
|
-
ignoredTags,
|
|
75251
|
-
includedTags,
|
|
75252
|
-
includeTagDefaults,
|
|
75253
|
-
serverAuthFunctionNames,
|
|
75254
|
-
severityControls,
|
|
75255
|
-
userPlugins,
|
|
75256
|
-
disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin,
|
|
75257
|
-
ruleSelection: overrides.ruleSelection,
|
|
75258
|
-
sidecarRuleIdFilter: overrides.sidecarRuleIdFilter
|
|
75259
|
-
});
|
|
75260
75523
|
const restoreDisableDirectives = respectInlineDisables ? () => {} : await neutralizeDisableDirectives(rootDirectory, includePaths);
|
|
75261
75524
|
const configDirectory = NFS.mkdtempSync(Path.join(os.tmpdir(), "react-doctor-oxlintrc-"));
|
|
75262
75525
|
const configPath = Path.join(configDirectory, "oxlintrc.json");
|
|
@@ -75292,7 +75555,30 @@ const runOxlint = async (options) => {
|
|
|
75292
75555
|
await yieldToEventLoop();
|
|
75293
75556
|
const sizedScanFiles = includePaths === void 0 ? listSourceFilesWithSize(rootDirectory) : null;
|
|
75294
75557
|
const candidateFiles = includePaths !== void 0 ? includePaths : (sizedScanFiles ?? []).map((entry) => entry.path);
|
|
75558
|
+
const preparedHtmlSources = prepareHtmlLintSources(rootDirectory, configDirectory, candidateFiles);
|
|
75559
|
+
const lintFiles = preparedHtmlSources.lintFiles;
|
|
75560
|
+
const hasHtmlFiles = preparedHtmlSources.sourcePathByLintPath.size > 0;
|
|
75561
|
+
const lintProject = !project.hasThree && preparedHtmlSources.hasThreeModuleImport ? {
|
|
75562
|
+
...project,
|
|
75563
|
+
hasThree: true
|
|
75564
|
+
} : project;
|
|
75565
|
+
const buildConfig = (overrides) => createOxlintConfig({
|
|
75566
|
+
pluginPath,
|
|
75567
|
+
project: lintProject,
|
|
75568
|
+
customRulesOnly,
|
|
75569
|
+
extendsPaths: overrides.extendsPaths,
|
|
75570
|
+
ignoredTags,
|
|
75571
|
+
includedTags,
|
|
75572
|
+
includeTagDefaults,
|
|
75573
|
+
serverAuthFunctionNames,
|
|
75574
|
+
severityControls,
|
|
75575
|
+
userPlugins,
|
|
75576
|
+
disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin,
|
|
75577
|
+
ruleSelection: overrides.ruleSelection,
|
|
75578
|
+
sidecarRuleIdFilter: overrides.sidecarRuleIdFilter
|
|
75579
|
+
});
|
|
75295
75580
|
const sizeByFile = sizedScanFiles !== null && lintBatchOrdering === "cost" ? new Map(sizedScanFiles.map((entry) => [entry.path, entry.sizeBytes])) : null;
|
|
75581
|
+
if (sizeByFile !== null) for (const [lintPath, sizeBytes] of preparedHtmlSources.sizeByLintPath) sizeByFile.set(lintPath, sizeBytes);
|
|
75296
75582
|
const buildFileBatches = (passBaseArgs, passFiles) => sizeByFile !== null ? planLintBatches({
|
|
75297
75583
|
baseArgs: passBaseArgs,
|
|
75298
75584
|
files: passFiles,
|
|
@@ -75321,7 +75607,8 @@ const runOxlint = async (options) => {
|
|
|
75321
75607
|
fileBatches: passFileBatches,
|
|
75322
75608
|
rootDirectory,
|
|
75323
75609
|
nodeBinaryPath,
|
|
75324
|
-
project,
|
|
75610
|
+
project: lintProject,
|
|
75611
|
+
sourcePathByLintPath: preparedHtmlSources.sourcePathByLintPath,
|
|
75325
75612
|
onPartialFailure: reportPartialFailure,
|
|
75326
75613
|
onAnalyzedFiles: (filePaths) => {
|
|
75327
75614
|
analyzedFiles = filePaths;
|
|
@@ -75356,7 +75643,7 @@ const runOxlint = async (options) => {
|
|
|
75356
75643
|
};
|
|
75357
75644
|
}
|
|
75358
75645
|
};
|
|
75359
|
-
if (perFileLintCacheEnabled && !project.hasReactCompiler && project.hasReactCompilerLintPlugin !== true && extendsPaths.length === 0 && userPlugins.length === 0) {
|
|
75646
|
+
if (perFileLintCacheEnabled && !project.hasReactCompiler && project.hasReactCompilerLintPlugin !== true && extendsPaths.length === 0 && userPlugins.length === 0 && !hasHtmlFiles) {
|
|
75360
75647
|
const rulesetHash = computeRulesetHash({
|
|
75361
75648
|
config: buildConfig({
|
|
75362
75649
|
extendsPaths: [],
|
|
@@ -75514,14 +75801,15 @@ const runOxlint = async (options) => {
|
|
|
75514
75801
|
]);
|
|
75515
75802
|
}
|
|
75516
75803
|
const baseArgs = makeBaseArgs(configPath);
|
|
75517
|
-
const fileBatches = buildFileBatches(baseArgs,
|
|
75804
|
+
const fileBatches = buildFileBatches(baseArgs, lintFiles);
|
|
75518
75805
|
let analyzedFiles = [];
|
|
75519
75806
|
const runBatches = () => spawnLintBatches({
|
|
75520
75807
|
baseArgs,
|
|
75521
75808
|
fileBatches,
|
|
75522
75809
|
rootDirectory,
|
|
75523
75810
|
nodeBinaryPath,
|
|
75524
|
-
project,
|
|
75811
|
+
project: lintProject,
|
|
75812
|
+
sourcePathByLintPath: preparedHtmlSources.sourcePathByLintPath,
|
|
75525
75813
|
onPartialFailure,
|
|
75526
75814
|
onAnalyzedFiles: (filePaths) => {
|
|
75527
75815
|
analyzedFiles = filePaths;
|
|
@@ -77728,7 +78016,7 @@ const makeNoopConsole = () => ({
|
|
|
77728
78016
|
});
|
|
77729
78017
|
//#endregion
|
|
77730
78018
|
//#region src/cli/utils/version.ts
|
|
77731
|
-
const VERSION = "0.9.1-dev.
|
|
78019
|
+
const VERSION = "0.9.1-dev.8534d86";
|
|
77732
78020
|
//#endregion
|
|
77733
78021
|
//#region src/cli/utils/json-mode.ts
|
|
77734
78022
|
let context = null;
|
|
@@ -78086,13 +78374,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
|
|
|
78086
78374
|
* uploads source-map artifacts under, so stack frames symbolicate. Honors the
|
|
78087
78375
|
* standard `SENTRY_RELEASE` override.
|
|
78088
78376
|
*/
|
|
78089
|
-
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.9.1-dev.
|
|
78377
|
+
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.9.1-dev.8534d86`;
|
|
78090
78378
|
/**
|
|
78091
78379
|
* Deployment environment shown in Sentry's environment filter. Defaults to
|
|
78092
78380
|
* `production` for tagged releases and `development` for dev/unbuilt versions,
|
|
78093
78381
|
* overridable via the standard `SENTRY_ENVIRONMENT` env var.
|
|
78094
78382
|
*/
|
|
78095
|
-
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.9.1-dev.
|
|
78383
|
+
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.9.1-dev.8534d86") ? "development" : "production");
|
|
78096
78384
|
/**
|
|
78097
78385
|
* Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
|
|
78098
78386
|
* (set to `0` to disable tracing) and falls back to
|
|
@@ -90757,6 +91045,7 @@ const buildScanAttributes = (input) => {
|
|
|
90757
91045
|
rulesDisabled: ruleKeys.filter((key) => ruleOverrides[key] === "off").length,
|
|
90758
91046
|
fileCount: input.result?.scannedFileCount ?? null,
|
|
90759
91047
|
nonJsxFileCount: input.result?.analyzedFiles?.filter((filePath) => !JSX_FILE_PATTERN.test(filePath)).length ?? null,
|
|
91048
|
+
htmlFileCount: input.result?.analyzedFiles?.filter((filePath) => HTML_FILE_PATTERN.test(filePath)).length ?? null,
|
|
90760
91049
|
multilineDiagnosticCount: input.result?.diagnostics.filter((diagnostic) => (diagnostic.endLine ?? diagnostic.line) > diagnostic.line).length ?? null
|
|
90761
91050
|
});
|
|
90762
91051
|
};
|
|
@@ -96810,7 +97099,7 @@ ${highlighter.dim("Learn more:")}
|
|
|
96810
97099
|
${highlighter.info(CI_URL)}
|
|
96811
97100
|
`;
|
|
96812
97101
|
const collectCategoryOption = (value, previousValues) => [...previousValues ?? [], value];
|
|
96813
|
-
const program = new Command().name("react-doctor").description("Diagnose React codebase health").version(VERSION, "-v, --version", "display the version number").argument("[directory]", "project directory to scan", ".").option("--lint", "enable linting").option("--no-lint", "skip linting").option("--dead-code", "enable dead-code analysis (default)").option("--no-dead-code", "skip dead-code analysis (unused files / exports / dependencies, circular imports)").option("--supply-chain", "enable the dependency supply-chain scan (default)").option("--no-supply-chain", "skip the dependency supply-chain scan (Socket.dev dependency health checks)").option("--verbose", "show every rule and per-file details (default shows top 3 rules)").option("--debug", "force a Sentry trace and print its id at the end (paste it into a bug report)").option("--output-dir <dir>", "directory for the full diagnostics dump (default: a temp folder)").option("--score", "output only the score").option("--json", "output a single structured JSON report (suppresses other output)").option("--json-compact", "with --json, emit compact JSON (no indentation)").option("--json-out <path>", "with --json, write the report to a file instead of stdout").option("-y, --yes", "skip prompts, scan all workspace projects").option("--no-parallel", "lint serially with one worker (default: parallel across CPU cores; set the worker count with REACT_DOCTOR_PARALLEL)").option("--project <name>", "select projects: workspace names or directory paths (comma-separated for multiple); overrides the `projects` config field").option("--scope <value>", "how much supported JS/TS source to scan/report: full (default), files, changed (only new issues vs base), or lines (issues whose source spans touch changed lines)").option("--base <ref>", "base git ref for files/changed/lines scope (auto-detected when omitted)").option("--include-untracked", "with --scope files/changed/lines, also scan ordinary untracked files (respects .gitignore)").addOption(new Option("--diff [base]", "[deprecated] alias for --scope changed (pass `false` to force a full scan)").hideHelp()).addOption(new Option("--changed-files-from <file>", "scan source files listed in a newline-delimited changed-files file").hideHelp()).option("--no-score", "skip the score API, the share URL, and crash reporting").addOption(new Option("--category <category>", "only show diagnostics in a category (repeatable; e.g. Security)").argParser(collectCategoryOption)).option("--no-telemetry", "alias for --no-score (skip the score API, share URL, and crash reporting)").option("--staged", "scan only staged (git index) files for pre-commit hooks").option("--max-duration <seconds>", "scan time budget for the whole run, shared across workspace projects: past it, remaining lint batches and dead-code are skipped and partial results are reported (skipped files are listed in the JSON report)").option("--blocking <level>", "severity that fails CI: error (default), warning, or none (advisory)").addOption(new Option("--fail-on <level>", "[deprecated] alias for --blocking <level>").hideHelp()).option("--no-respect-inline-disables", "audit mode: neutralize inline lint suppressions before scanning").option("--warnings", "show warning-severity diagnostics (default)").option("--no-warnings", "hide warning-severity diagnostics (errors only)").option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").addHelpText("after", renderRootHelpEpilog);
|
|
97102
|
+
const program = new Command().name("react-doctor").description("Diagnose React codebase health").version(VERSION, "-v, --version", "display the version number").argument("[directory]", "project directory to scan", ".").option("--lint", "enable linting").option("--no-lint", "skip linting").option("--dead-code", "enable dead-code analysis (default)").option("--no-dead-code", "skip dead-code analysis (unused files / exports / dependencies, circular imports)").option("--supply-chain", "enable the dependency supply-chain scan (default)").option("--no-supply-chain", "skip the dependency supply-chain scan (Socket.dev dependency health checks)").option("--verbose", "show every rule and per-file details (default shows top 3 rules)").option("--debug", "force a Sentry trace and print its id at the end (paste it into a bug report)").option("--output-dir <dir>", "directory for the full diagnostics dump (default: a temp folder)").option("--score", "output only the score").option("--json", "output a single structured JSON report (suppresses other output)").option("--json-compact", "with --json, emit compact JSON (no indentation)").option("--json-out <path>", "with --json, write the report to a file instead of stdout").option("-y, --yes", "skip prompts, scan all workspace projects").option("--no-parallel", "lint serially with one worker (default: parallel across CPU cores; set the worker count with REACT_DOCTOR_PARALLEL)").option("--project <name>", "select projects: workspace names or directory paths (comma-separated for multiple); overrides the `projects` config field").option("--scope <value>", "how much supported JS/TS and inline HTML script source to scan/report: full (default), files, changed (only new issues vs base), or lines (issues whose source spans touch changed lines)").option("--base <ref>", "base git ref for files/changed/lines scope (auto-detected when omitted)").option("--include-untracked", "with --scope files/changed/lines, also scan ordinary untracked files (respects .gitignore)").addOption(new Option("--diff [base]", "[deprecated] alias for --scope changed (pass `false` to force a full scan)").hideHelp()).addOption(new Option("--changed-files-from <file>", "scan source files listed in a newline-delimited changed-files file").hideHelp()).option("--no-score", "skip the score API, the share URL, and crash reporting").addOption(new Option("--category <category>", "only show diagnostics in a category (repeatable; e.g. Security)").argParser(collectCategoryOption)).option("--no-telemetry", "alias for --no-score (skip the score API, share URL, and crash reporting)").option("--staged", "scan only staged (git index) files for pre-commit hooks").option("--max-duration <seconds>", "scan time budget for the whole run, shared across workspace projects: past it, remaining lint batches and dead-code are skipped and partial results are reported (skipped files are listed in the JSON report)").option("--blocking <level>", "severity that fails CI: error (default), warning, or none (advisory)").addOption(new Option("--fail-on <level>", "[deprecated] alias for --blocking <level>").hideHelp()).option("--no-respect-inline-disables", "audit mode: neutralize inline lint suppressions before scanning").option("--warnings", "show warning-severity diagnostics (default)").option("--no-warnings", "hide warning-severity diagnostics (errors only)").option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").addHelpText("after", renderRootHelpEpilog);
|
|
96814
97103
|
program.action(inspectAction);
|
|
96815
97104
|
program.command("design [directory]").description("Run only the focused UI design diagnostics").addHelpText("after", renderDesignHelpEpilog).action((directory, _options, command) => designAction(directory ?? ".", command.optsWithGlobals()));
|
|
96816
97105
|
program.command("why <location>").description("Explain why a rule fired (or why a suppression didn't apply) at a file:line").option("--project <name>", "select projects: workspace names or directory paths (comma-separated for multiple)").option("-c, --cwd <cwd>", "working directory", process.cwd()).option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").action((location, options) => whyAction(location, options));
|
|
@@ -96890,4 +97179,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
|
|
|
96890
97179
|
export { toForwardSlashes as A, highlighter as B, buildRulePriorityMap as C, resolveBlockingLevel as D, formatLearnMoreLine as E, isCiEnvironment as F, sync as G, mergeReactDoctorConfigs as H, DIAGNOSTIC_CATEGORY_BUCKETS as I, void_ as K, Progress as L, isUnicodeSupported as M, METRIC as N, isReactDoctorWorkflowInstalled as O, TUI_REPORT_DETAIL_WIDTH_FRACTION as P, Reporter as R, hasLintHardFailure as S, formatFixRecipeLine as T, resolveScanTarget as U, mapWithConcurrency as V, runPromise as W, canAnimateOnboarding as _, resolveCliInspectOptions as a, buildCodeFrame as b, detectLaunchableAgents as c, launchCliAgent as d, isShareOptedOut as f, doctorFace as g, easeOutCubic as h, selectProjects as i, findNearestPackageDirectory as j, recordCount as k, CLI_AGENT_BINARIES as l, printFooter as m, shouldFailScanGate as n, countUniqueScannedFiles as o, inspect as p, succeed$3 as q, discoverWorkspacePackages as r, setUpGitHubActions as s, exitGracefully as t, copyToClipboard as u, computeProjectedScore as v, buildSortedRuleGroups as w, buildNoScoreMessage as x, formatElapsedTime as y, getCategoryImpact as z };
|
|
96891
97180
|
|
|
96892
97181
|
//# sourceMappingURL=cli.js.map
|
|
96893
|
-
//# debugId=
|
|
97182
|
+
//# debugId=68199c0f-9834-55ef-9753-efce9ed982ad
|
package/dist/index.d.ts
CHANGED
|
@@ -9706,7 +9706,7 @@ interface DiagnoseOptions {
|
|
|
9706
9706
|
/** See `ReactDoctorConfig.deadCode`. Ignored in diff mode. */
|
|
9707
9707
|
deadCode?: boolean;
|
|
9708
9708
|
verbose?: boolean;
|
|
9709
|
-
/** Restrict linting to
|
|
9709
|
+
/** Restrict linting to supported JS/TS files and inline scripts in HTML files. */
|
|
9710
9710
|
includePaths?: string[];
|
|
9711
9711
|
/**
|
|
9712
9712
|
* Per-call override for `ReactDoctorConfig.respectInlineDisables`.
|
|
@@ -9969,7 +9969,7 @@ interface JsonReportProjectEntry {
|
|
|
9969
9969
|
skippedCheckReasons?: Record<string, string>;
|
|
9970
9970
|
/**
|
|
9971
9971
|
* Number of source files this scan's linter examined. In diff / changed
|
|
9972
|
-
* mode it's the count of changed supported JS/TS source files; in a full
|
|
9972
|
+
* mode it's the count of changed supported JS/TS and HTML source files; in a full
|
|
9973
9973
|
* scan it's the whole source tree. `0` in a diff scan means the changed
|
|
9974
9974
|
* files held nothing React Doctor lints —
|
|
9975
9975
|
* the GitHub Action reads that as "nothing to report" (skips the PR comment;
|