build: tighten oxlint and repair type checks
This commit is contained in:
parent
6bce49c0e8
commit
07e17b9853
12
package.json
12
package.json
@ -52,9 +52,12 @@
|
||||
"test:coverage": "pnpm run build:all && node --test --experimental-test-coverage --test-coverage-include='dist/**/*.js' --test-coverage-exclude='dist/repair/*.test.js' --test-coverage-lines=49 --test-coverage-branches=66 --test-coverage-functions=57 test/*.test.ts test/repair/*.test.ts dist/repair/*.test.js",
|
||||
"test:coverage:changed": "pnpm run build:all && node --test --experimental-test-coverage --test-coverage-include='dist/repair/fix-prompt-builder.js' --test-coverage-lines=85 --test-coverage-branches=85 --test-coverage-functions=85 test/repair/*.test.ts dist/repair/*.test.js",
|
||||
"check:active-surface": "node scripts/check-active-surface.ts",
|
||||
"lint": "oxlint src --tsconfig tsconfig.json && oxlint src/repair --tsconfig tsconfig.repair.json",
|
||||
"format": "oxfmt --write src scripts test package.json tsconfig.json tsconfig.repair.json .oxfmtrc.json schema .github/workflows",
|
||||
"format:check": "oxfmt --check src scripts test package.json tsconfig.json tsconfig.repair.json .oxfmtrc.json schema .github/workflows",
|
||||
"lint": "pnpm run lint:src && pnpm run lint:repair && pnpm run lint:scripts",
|
||||
"lint:src": "oxlint src/*.ts --tsconfig tsconfig.json --type-aware --deny-warnings --report-unused-disable-directives -D correctness",
|
||||
"lint:repair": "oxlint src/repair --tsconfig tsconfig.repair.json --deny-warnings --report-unused-disable-directives -D correctness",
|
||||
"lint:scripts": "oxlint scripts test --deny-warnings --report-unused-disable-directives -D correctness",
|
||||
"format": "oxfmt --write src scripts test package.json tsconfig.json tsconfig.repair.json .oxfmtrc.json schema .github/actions .github/workflows",
|
||||
"format:check": "oxfmt --check src scripts test package.json tsconfig.json tsconfig.repair.json .oxfmtrc.json schema .github/actions .github/workflows",
|
||||
"oxformat": "pnpm run format",
|
||||
"oxformat:check": "pnpm run format:check",
|
||||
"check": "pnpm run check:active-surface && pnpm run build:all && pnpm run lint && pnpm run test:unit && pnpm run test:repair && pnpm run test:coverage:changed && pnpm run test:coverage && pnpm run format:check"
|
||||
@ -63,7 +66,8 @@
|
||||
"@types/node": "^25.6.0",
|
||||
"@typescript/native-preview": "7.0.0-dev.20260423.1",
|
||||
"oxfmt": "^0.46.0",
|
||||
"oxlint": "^1.61.0"
|
||||
"oxlint": "^1.61.0",
|
||||
"oxlint-tsgolint": "0.22.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=24"
|
||||
|
||||
69
pnpm-lock.yaml
generated
69
pnpm-lock.yaml
generated
@ -19,7 +19,10 @@ importers:
|
||||
version: 0.46.0
|
||||
oxlint:
|
||||
specifier: ^1.61.0
|
||||
version: 1.61.0
|
||||
version: 1.61.0(oxlint-tsgolint@0.22.1)
|
||||
oxlint-tsgolint:
|
||||
specifier: 0.22.1
|
||||
version: 0.22.1
|
||||
|
||||
packages:
|
||||
|
||||
@ -145,6 +148,36 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@oxlint-tsgolint/darwin-arm64@0.22.1':
|
||||
resolution: {integrity: sha512-4150Lpgc1YM09GcjA6GSrra1JoPjC7aOpfywLjWEY4vW0Sd1qKzqHF1WRaiw0/qUZ40OATYdv3aRd7ipPkWQbw==}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@oxlint-tsgolint/darwin-x64@0.22.1':
|
||||
resolution: {integrity: sha512-vFWcPWYOgZs4HWcgS1EjUZg33NLcNfEYU49KGImmCfZWkflENrmBYV4HN/C0YeAPum6ZZ/goPSvQrB/cOD+NfA==}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@oxlint-tsgolint/linux-arm64@0.22.1':
|
||||
resolution: {integrity: sha512-6LiUpP0Zir3+29FvBm7Y28q/dBjSHqTZ5MhG1Ckw4fGhI4cAvbcwXaKvbjx1TP7rRmBNOoq/M5xdpHjTb+GAew==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@oxlint-tsgolint/linux-x64@0.22.1':
|
||||
resolution: {integrity: sha512-fuX1hEQfpHauUbXADsfqVhRzrUrGabzGXbj5wsp2vKhV5uk/Rze8Mba9GdjFGECzvXudMGqHqxB4r6jGRdhxVA==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@oxlint-tsgolint/win32-arm64@0.22.1':
|
||||
resolution: {integrity: sha512-8SZidAj+jrbZf9ZjBEYW0tiNZ+KasqB2zgW26qdiPpQSF/DzURnPmXz651IeA9YsmbVdHGIooEHUmev6QJdquA==}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@oxlint-tsgolint/win32-x64@0.22.1':
|
||||
resolution: {integrity: sha512-QweSk9H5lFh5Y+WUf2Kq/OAN88V6+62ZwGhP38gqdRotI90luXSMkruFTj7Q2rYrzH4ZVNaSqx7NY8JpSfIzqg==}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@oxlint/binding-android-arm-eabi@1.61.0':
|
||||
resolution: {integrity: sha512-6eZBPgiigK5txqoVgRqxbaxiom4lM8AP8CyKPPvpzKnQ3iFRFOIDc+0AapF+qsUSwjOzr5SGk4SxQDpQhkSJMQ==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
@ -322,6 +355,10 @@ packages:
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
hasBin: true
|
||||
|
||||
oxlint-tsgolint@0.22.1:
|
||||
resolution: {integrity: sha512-YUSGSLUnoolsu8gxISEDio3q1rtsCozwfOzASUn3DT2mR2EeQ93uEEnen7s+6LpF+lyTQFln1pQfqwBh/fsVEg==}
|
||||
hasBin: true
|
||||
|
||||
oxlint@1.61.0:
|
||||
resolution: {integrity: sha512-ZC0ALuhDZ6ivOFG+sy0D0pEDN49EvsId98zVlmYdkcXHsEM14m/qTNUEsUpiFiCVbpIxYtVBmmLE87nsbUHohQ==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
@ -398,6 +435,24 @@ snapshots:
|
||||
'@oxfmt/binding-win32-x64-msvc@0.46.0':
|
||||
optional: true
|
||||
|
||||
'@oxlint-tsgolint/darwin-arm64@0.22.1':
|
||||
optional: true
|
||||
|
||||
'@oxlint-tsgolint/darwin-x64@0.22.1':
|
||||
optional: true
|
||||
|
||||
'@oxlint-tsgolint/linux-arm64@0.22.1':
|
||||
optional: true
|
||||
|
||||
'@oxlint-tsgolint/linux-x64@0.22.1':
|
||||
optional: true
|
||||
|
||||
'@oxlint-tsgolint/win32-arm64@0.22.1':
|
||||
optional: true
|
||||
|
||||
'@oxlint-tsgolint/win32-x64@0.22.1':
|
||||
optional: true
|
||||
|
||||
'@oxlint/binding-android-arm-eabi@1.61.0':
|
||||
optional: true
|
||||
|
||||
@ -514,7 +569,16 @@ snapshots:
|
||||
'@oxfmt/binding-win32-ia32-msvc': 0.46.0
|
||||
'@oxfmt/binding-win32-x64-msvc': 0.46.0
|
||||
|
||||
oxlint@1.61.0:
|
||||
oxlint-tsgolint@0.22.1:
|
||||
optionalDependencies:
|
||||
'@oxlint-tsgolint/darwin-arm64': 0.22.1
|
||||
'@oxlint-tsgolint/darwin-x64': 0.22.1
|
||||
'@oxlint-tsgolint/linux-arm64': 0.22.1
|
||||
'@oxlint-tsgolint/linux-x64': 0.22.1
|
||||
'@oxlint-tsgolint/win32-arm64': 0.22.1
|
||||
'@oxlint-tsgolint/win32-x64': 0.22.1
|
||||
|
||||
oxlint@1.61.0(oxlint-tsgolint@0.22.1):
|
||||
optionalDependencies:
|
||||
'@oxlint/binding-android-arm-eabi': 1.61.0
|
||||
'@oxlint/binding-android-arm64': 1.61.0
|
||||
@ -535,6 +599,7 @@ snapshots:
|
||||
'@oxlint/binding-win32-arm64-msvc': 1.61.0
|
||||
'@oxlint/binding-win32-ia32-msvc': 1.61.0
|
||||
'@oxlint/binding-win32-x64-msvc': 1.61.0
|
||||
oxlint-tsgolint: 0.22.1
|
||||
|
||||
tinypool@2.1.0: {}
|
||||
|
||||
|
||||
@ -1436,7 +1436,7 @@ function closingPullRequestsForIssue(number: number): unknown[] {
|
||||
export function openClosingPullRequestApplyReason(pullRequests: readonly unknown[]): string | null {
|
||||
const openPulls = pullRequests
|
||||
.map(asRecord)
|
||||
.filter((pull) => String(pull.state ?? "").toLowerCase() === "open")
|
||||
.filter((pull) => typeof pull.state === "string" && pull.state.toLowerCase() === "open")
|
||||
.map((pull) => ({
|
||||
number: typeof pull.number === "number" ? pull.number : null,
|
||||
title: typeof pull.title === "string" ? pull.title : "",
|
||||
@ -1502,7 +1502,7 @@ function collectRelatedMentions(options: {
|
||||
return mentions;
|
||||
}
|
||||
|
||||
function compactRelatedItem(number: number, mentionedIn: string[]): unknown | null {
|
||||
function compactRelatedItem(number: number, mentionedIn: string[]): Record<string, unknown> | null {
|
||||
try {
|
||||
const issue = ghJson<unknown>(["api", `repos/${targetRepo()}/issues/${number}`]);
|
||||
const issueRecord = asRecord(issue);
|
||||
@ -1748,11 +1748,12 @@ function relatedCounterpartInfo(value: unknown): {
|
||||
const issue = asRecord(record.issue);
|
||||
const pullRequest = asRecord(record.pullRequest);
|
||||
const isPullRequest = Object.keys(pullRequest).length > 0;
|
||||
const state = isPullRequest ? pullRequest.state : issue.state;
|
||||
return {
|
||||
number: typeof issue.number === "number" ? issue.number : null,
|
||||
kind: isPullRequest ? "pull_request" : "issue",
|
||||
author: normalizeAuthorLogin(isPullRequest ? pullRequest.author : issue.author),
|
||||
state: String((isPullRequest ? pullRequest.state : issue.state) ?? "").toLowerCase(),
|
||||
state: typeof state === "string" ? state.toLowerCase() : "",
|
||||
title: typeof issue.title === "string" ? issue.title : "",
|
||||
};
|
||||
}
|
||||
@ -2714,8 +2715,8 @@ function collectItemContext(item: Item): ItemContext {
|
||||
timeline: timeline.length,
|
||||
},
|
||||
};
|
||||
let pullRequest: unknown | undefined;
|
||||
let pullReviewComments: unknown[] | undefined;
|
||||
let pullRequest: unknown = null;
|
||||
let pullReviewComments: unknown[] | null = null;
|
||||
if (item.kind === "issue") {
|
||||
const closingPullRequests = closingPullRequestsForIssue(item.number);
|
||||
if (closingPullRequests.length > 0) {
|
||||
|
||||
@ -33,7 +33,7 @@ function existingTimelineRows(value: JsonValue): string[] {
|
||||
new RegExp(`${escapeRegExp(TIMELINE_START)}([\\s\\S]*?)${escapeRegExp(TIMELINE_END)}`),
|
||||
);
|
||||
if (!match) return [];
|
||||
return match[1]
|
||||
return match[1]!
|
||||
.split(/\r?\n/)
|
||||
.map((line) => line.trimEnd())
|
||||
.filter((line) => line.includes(EVENT_PREFIX));
|
||||
|
||||
@ -2411,7 +2411,7 @@ async function mapLimit<T, R>(
|
||||
while (next < items.length) {
|
||||
const index = next;
|
||||
next += 1;
|
||||
results[index] = await mapper(items[index]);
|
||||
results[index] = await mapper(items[index] as T);
|
||||
}
|
||||
});
|
||||
await Promise.all(workers);
|
||||
|
||||
@ -441,9 +441,9 @@ function findingKinds(markdown: string) {
|
||||
function likelyFilesFromReport(markdown: string) {
|
||||
const out: JsonValue[] = [];
|
||||
for (const match of markdown.matchAll(/^- File:\s*`?([^`\n]+?)`?\s*$/gim))
|
||||
out.push(match[1].trim());
|
||||
out.push(match[1]!.trim());
|
||||
const changed = markdown.match(/^- Changed files:\s*(.+)$/im)?.[1] ?? "";
|
||||
for (const match of changed.matchAll(/`([^`]+)`/g)) out.push(match[1].trim());
|
||||
for (const match of changed.matchAll(/`([^`]+)`/g)) out.push(match[1]!.trim());
|
||||
return unique(
|
||||
out.filter(
|
||||
(file: JsonValue) =>
|
||||
|
||||
@ -155,7 +155,7 @@ function focusedFileExcerpt(content: string, tokens: string[]) {
|
||||
.map((token) => token.toLowerCase())
|
||||
.filter((token) => token.length >= 4);
|
||||
for (let index = 0; index < lines.length; index += 1) {
|
||||
const lower = lines[index].toLowerCase();
|
||||
const lower = lines[index]!.toLowerCase();
|
||||
if (lowerTokens.some((token) => lower.includes(token))) {
|
||||
for (
|
||||
let line = Math.max(0, index - 8);
|
||||
|
||||
@ -12,11 +12,11 @@ export type GitPublishOptions = {
|
||||
message: string;
|
||||
paths: readonly string[];
|
||||
restorePaths?: readonly string[];
|
||||
maxAttempts?: number;
|
||||
pushAttempts?: number;
|
||||
maxAttempts?: number | undefined;
|
||||
pushAttempts?: number | undefined;
|
||||
remote?: string;
|
||||
branch?: string;
|
||||
rebaseStrategy?: RebaseStrategy;
|
||||
rebaseStrategy?: RebaseStrategy | undefined;
|
||||
};
|
||||
|
||||
export type RebaseStrategy = "normal" | "theirs" | "apply-records";
|
||||
|
||||
@ -60,8 +60,8 @@ export function remoteBranchSha({ targetDir, branch }: TargetBranch): string {
|
||||
encoding: "utf8",
|
||||
});
|
||||
if (child.status !== 0) return "";
|
||||
const [sha] = child.stdout.trim().split(/\s+/);
|
||||
return /^[0-9a-f]{40}$/.test(sha ?? "") ? sha : "";
|
||||
const sha = child.stdout.trim().split(/\s+/)[0] ?? "";
|
||||
return /^[0-9a-f]{40}$/.test(sha) ? sha : "";
|
||||
}
|
||||
|
||||
export function branchHasBaseDiff({ targetDir, baseBranch }: TargetBaseBranch): boolean {
|
||||
|
||||
@ -9,10 +9,12 @@ export function parsePullRequestUrl(value: unknown): GitHubRef | null {
|
||||
.trim()
|
||||
.match(/^https:\/\/github\.com\/([^/\s]+\/[^/\s]+)\/pull\/(\d+)(?:[/?#].*)?$/i);
|
||||
if (!match) return null;
|
||||
const repo = match[1]!;
|
||||
const numberText = match[2]!;
|
||||
return {
|
||||
repo: match[1],
|
||||
number: Number(match[2]),
|
||||
url: `https://github.com/${match[1]}/pull/${match[2]}`,
|
||||
repo,
|
||||
number: Number(numberText),
|
||||
url: `https://github.com/${repo}/pull/${numberText}`,
|
||||
};
|
||||
}
|
||||
|
||||
@ -26,19 +28,22 @@ export function parseIssueOrPullRef(value: unknown, defaultRepo = ""): GitHubRef
|
||||
/^https:\/\/github\.com\/([^/]+\/[^/]+)\/(?:issues|pull)\/(\d+)(?:[/?#].*)?$/i,
|
||||
);
|
||||
if (urlMatch) {
|
||||
const repo = urlMatch[1]!;
|
||||
const numberText = urlMatch[2]!;
|
||||
return {
|
||||
repo: urlMatch[1],
|
||||
number: Number(urlMatch[2]),
|
||||
url: `https://github.com/${urlMatch[1]}/issues/${urlMatch[2]}`,
|
||||
repo,
|
||||
number: Number(numberText),
|
||||
url: `https://github.com/${repo}/issues/${numberText}`,
|
||||
};
|
||||
}
|
||||
|
||||
const shorthand = text.match(/^#?(\d+)$/);
|
||||
if (!shorthand || !defaultRepo) return null;
|
||||
const numberText = shorthand[1]!;
|
||||
return {
|
||||
repo: defaultRepo,
|
||||
number: Number(shorthand[1]),
|
||||
url: `https://github.com/${defaultRepo}/issues/${shorthand[1]}`,
|
||||
number: Number(numberText),
|
||||
url: `https://github.com/${defaultRepo}/issues/${numberText}`,
|
||||
};
|
||||
}
|
||||
|
||||
@ -46,7 +51,7 @@ export function issueNumberFromRef(value: unknown, expectedRepo = ""): number {
|
||||
const shorthand = String(value ?? "")
|
||||
.trim()
|
||||
.match(/^#?(\d+)$/);
|
||||
if (shorthand) return Number(shorthand[1]);
|
||||
if (shorthand) return Number(shorthand[1]!);
|
||||
|
||||
const parsed = parseIssueOrPullRef(value);
|
||||
if (!parsed) return 0;
|
||||
|
||||
@ -77,10 +77,8 @@ function repairStem(surface: unknown, issue: unknown) {
|
||||
}
|
||||
|
||||
function firstSentence(value: unknown) {
|
||||
return normalizeTitleText(value)
|
||||
.split(/(?<=[.!?])\s+/)[0]
|
||||
.replace(/[.!?]+$/, "")
|
||||
.trim();
|
||||
const first = normalizeTitleText(value).split(/(?<=[.!?])\s+/)[0]!;
|
||||
return first.replace(/[.!?]+$/, "").trim();
|
||||
}
|
||||
|
||||
function compactTitle(value: unknown, maxLength: number) {
|
||||
|
||||
@ -97,11 +97,12 @@ export function runAllowedValidationCommands(
|
||||
for (const command of requiredValidationCommands(commands, cwd, options)) {
|
||||
const resolvedCommands = resolveAllowedValidationCommands(command, cwd, baseBranch, options);
|
||||
for (const parts of resolvedCommands) {
|
||||
const executable = parts[0]!;
|
||||
const rendered = parts.join(" ");
|
||||
if (executed.includes(rendered)) continue;
|
||||
while (true) {
|
||||
try {
|
||||
run(parts[0], parts.slice(1), { cwd, env: validationEnv });
|
||||
run(executable, parts.slice(1), { cwd, env: validationEnv });
|
||||
executed.push(rendered);
|
||||
break;
|
||||
} catch (error) {
|
||||
@ -114,9 +115,10 @@ export function runAllowedValidationCommands(
|
||||
});
|
||||
if (fallbackCommands.length > 0) {
|
||||
for (const fallbackParts of fallbackCommands) {
|
||||
const fallbackExecutable = fallbackParts[0]!;
|
||||
const fallbackRendered = fallbackParts.join(" ");
|
||||
if (executed.includes(fallbackRendered)) continue;
|
||||
run(fallbackParts[0], fallbackParts.slice(1), { cwd, env: validationEnv });
|
||||
run(fallbackExecutable, fallbackParts.slice(1), { cwd, env: validationEnv });
|
||||
executed.push(fallbackRendered);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -8,6 +8,8 @@
|
||||
"rootDir": "src/repair",
|
||||
"outDir": "dist/repair",
|
||||
"strict": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"exactOptionalPropertyTypes": true,
|
||||
"useUnknownInCatchVariables": false,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user