chore: run oxlint type-aware in lint

This commit is contained in:
Peter Steinberger
2025-12-31 19:59:31 +01:00
parent 94de794588
commit 2a2a3a7c59
7 changed files with 192 additions and 17 deletions
+1
View File
@@ -9,6 +9,7 @@
- Cookie extraction now uses `@steipete/sweet-cookie` (drops `sqlite3` CLI + custom browser readers in `bird`). - Cookie extraction now uses `@steipete/sweet-cookie` (drops `sqlite3` CLI + custom browser readers in `bird`).
- Query ID updater now tracks the Bookmarks GraphQL operation. - Query ID updater now tracks the Bookmarks GraphQL operation.
- Lint rules stricter (block statements, no-negation-else, useConst/useTemplate, top-level regex, import extension enforcement). - Lint rules stricter (block statements, no-negation-else, useConst/useTemplate, top-level regex, import extension enforcement).
- `pnpm lint` now runs both Biome and oxlint (type-aware).
### Tests ### Tests
- Coverage thresholds raised to 90% statements/lines/functions (80% branches). - Coverage thresholds raised to 90% statements/lines/functions (80% branches).
+8 -2
View File
@@ -21,8 +21,12 @@
"bird": "pnpm run build:dist && node dist/index.js", "bird": "pnpm run build:dist && node dist/index.js",
"test": "vitest run", "test": "vitest run",
"test:watch": "vitest", "test:watch": "vitest",
"lint": "biome check .", "lint": "pnpm run lint:biome && pnpm run lint:oxlint",
"lint:fix": "biome check --write .", "lint:biome": "biome check .",
"lint:oxlint": "oxlint --type-aware --tsconfig tsconfig.oxlint.json --import-plugin --node-plugin --vitest-plugin --deny-warnings src tests scripts",
"lint:fix": "pnpm run lint:biome:fix && pnpm run lint:oxlint:fix",
"lint:biome:fix": "biome check --write .",
"lint:oxlint:fix": "oxlint --type-aware --tsconfig tsconfig.oxlint.json --import-plugin --node-plugin --vitest-plugin --deny-warnings --fix src tests scripts",
"format": "biome format --write .", "format": "biome format --write .",
"binary": "pnpm run build:binary", "binary": "pnpm run build:binary",
"graphql:update": "tsx scripts/update-query-ids.ts" "graphql:update": "tsx scripts/update-query-ids.ts"
@@ -37,6 +41,8 @@
"@biomejs/biome": "^2.3.10", "@biomejs/biome": "^2.3.10",
"@types/node": "^25.0.3", "@types/node": "^25.0.3",
"@vitest/coverage-v8": "4.0.16", "@vitest/coverage-v8": "4.0.16",
"oxlint": "^1.36.0",
"oxlint-tsgolint": "^0.10.0",
"tsx": "^4.21.0", "tsx": "^4.21.0",
"typescript": "^5.9.3", "typescript": "^5.9.3",
"vitest": "^4.0.16" "vitest": "^4.0.16"
+153
View File
@@ -30,6 +30,12 @@ importers:
'@vitest/coverage-v8': '@vitest/coverage-v8':
specifier: 4.0.16 specifier: 4.0.16
version: 4.0.16([email protected](@types/[email protected])([email protected])) version: 4.0.16([email protected](@types/[email protected])([email protected]))
oxlint:
specifier: ^1.36.0
version: 1.36.0([email protected])
oxlint-tsgolint:
specifier: ^0.10.0
version: 0.10.0
tsx: tsx:
specifier: ^4.21.0 specifier: ^4.21.0
version: 4.21.0 version: 4.21.0
@@ -282,6 +288,76 @@ packages:
'@jridgewell/[email protected]': '@jridgewell/[email protected]':
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
'@oxlint-tsgolint/[email protected]':
resolution: {integrity: sha512-mhBF/pjey0UdLL1ocU46Fqta+uJuRfqrLfDpcViRg17BtDiUNd8JY9iN2FOoS2HGSCAgCUjZ0AZkwkHwFs/VTw==}
cpu: [arm64]
os: [darwin]
'@oxlint-tsgolint/[email protected]':
resolution: {integrity: sha512-roLi34mw/i1z+NS7luboix55SXyhVv38dNUTcRDkk+0lNPzI9ngrM+1y1N2oBSUmz5o9OZGnfJJ7BSGCw/fFEQ==}
cpu: [x64]
os: [darwin]
'@oxlint-tsgolint/[email protected]':
resolution: {integrity: sha512-HL9NThPH1V2F6l9XhwNmhQZUknN4m4yQYEvQFFGfZTYN6cvEEBIiqfF4KvBUg8c0xadMbQlW+Ug7/ybA9Nn+CA==}
cpu: [arm64]
os: [linux]
'@oxlint-tsgolint/[email protected]':
resolution: {integrity: sha512-Tw8QNq8ab+4+qE5krvJyMA66v6XE3GoiISRD5WmJ7YOxUnu//jSw/bBm7OYf/TNEZyeV0BTR7zXzhT5R+VFWlQ==}
cpu: [x64]
os: [linux]
'@oxlint-tsgolint/[email protected]':
resolution: {integrity: sha512-LTogmTRwpwQqVaH1Ama8Wd5/VVZWBSF8v5qTbeT628+1F5Kt1V5eHBvyFh4oN18UCZlgqrh7DqkDhsieXUaC8Q==}
cpu: [arm64]
os: [win32]
'@oxlint-tsgolint/[email protected]':
resolution: {integrity: sha512-ygqxx8EmNWy9/wCQS5uXq9k/o2EyYNwNxY1ZHNzlmZC/kV06Aemx5OBDafefawBNqH7xTZPfccUrjdiy+QlTrw==}
cpu: [x64]
os: [win32]
'@oxlint/[email protected]':
resolution: {integrity: sha512-MJkj82GH+nhvWKJhSIM6KlZ8tyGKdogSQXtNdpIyP02r/tTayFJQaAEWayG2Jhsn93kske+nimg5MYFhwO/rlg==}
cpu: [arm64]
os: [darwin]
'@oxlint/[email protected]':
resolution: {integrity: sha512-VvEhfkqj/99dCTqOcfkyFXOSbx4lIy5u2m2GHbK4WCMDySokOcMTNRHGw8fH/WgQ5cDrDMSTYIGQTmnBGi9tiQ==}
cpu: [x64]
os: [darwin]
'@oxlint/[email protected]':
resolution: {integrity: sha512-EMx92X5q+hHc3olTuj/kgkx9+yP0p/AVs4yvHbUfzZhBekXNpUWxWvg4hIKmQWn+Ee2j4o80/0ACGO0hDYJ9mg==}
cpu: [arm64]
os: [linux]
'@oxlint/[email protected]':
resolution: {integrity: sha512-7YCxtrPIctVYLqWrWkk8pahdCxch6PtsaucfMLC7TOlDt4nODhnQd4yzEscKqJ8Gjrw1bF4g+Ngob1gB+Qr9Fw==}
cpu: [arm64]
os: [linux]
'@oxlint/[email protected]':
resolution: {integrity: sha512-lnaJVlx5r3NWmoOMesfQXJSf78jHTn8Z+sdAf795Kgteo72+qGC1Uax2SToCJVN2J8PNG3oRV5bLriiCNR2i6Q==}
cpu: [x64]
os: [linux]
'@oxlint/[email protected]':
resolution: {integrity: sha512-AhuEU2Qdl66lSfTGu/Htirq8r/8q2YnZoG3yEXLMQWnPMn7efy8spD/N1NA7kH0Hll+cdfwgQkQqC2G4MS2lPQ==}
cpu: [x64]
os: [linux]
'@oxlint/[email protected]':
resolution: {integrity: sha512-GlWCBjUJY2QgvBFuNRkiRJu7K/djLmM0UQKfZV8IN+UXbP/JbjZHWKRdd4LXlQmzoz7M5Hd6p+ElCej8/90FCg==}
cpu: [arm64]
os: [win32]
'@oxlint/[email protected]':
resolution: {integrity: sha512-J+Vc00Utcf8p77lZPruQgb0QnQXuKnFogN88kCnOqs2a83I+vTBB8ILr0+L9sTwVRvIDMSC0pLdeQH4svWGFZg==}
cpu: [x64]
os: [win32]
'@rollup/[email protected]': '@rollup/[email protected]':
resolution: {integrity: sha512-iDGS/h7D8t7tvZ1t6+WPK04KD0MwzLZrG0se1hzBjSi5fyxlsiggoJHwh18PCFNn7tG43OWb6pdZ6Y+rMlmyNQ==} resolution: {integrity: sha512-iDGS/h7D8t7tvZ1t6+WPK04KD0MwzLZrG0se1hzBjSi5fyxlsiggoJHwh18PCFNn7tG43OWb6pdZ6Y+rMlmyNQ==}
cpu: [arm] cpu: [arm]
@@ -561,6 +637,20 @@ packages:
[email protected]: [email protected]:
resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==}
[email protected]:
resolution: {integrity: sha512-LDDSIu5J/4D4gFUuQQIEQpAC6maNEbMg4nC8JL/+Pe0cUDR86dtVZ09E2x5MwCh8f9yfktoaxt5x6UIVyzrajg==}
hasBin: true
[email protected]:
resolution: {integrity: sha512-IicUdXfXgI8OKrDPnoSjvBfeEF8PkKtm+CoLlg4LYe4ypc8U+T4r7730XYshdBGZdelg+JRw8GtCb2w/KaaZvw==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
oxlint-tsgolint: '>=0.10.0'
peerDependenciesMeta:
oxlint-tsgolint:
optional: true
[email protected]: [email protected]:
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
@@ -851,6 +941,48 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2 '@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.5 '@jridgewell/sourcemap-codec': 1.5.5
'@oxlint-tsgolint/[email protected]':
optional: true
'@oxlint-tsgolint/[email protected]':
optional: true
'@oxlint-tsgolint/[email protected]':
optional: true
'@oxlint-tsgolint/[email protected]':
optional: true
'@oxlint-tsgolint/[email protected]':
optional: true
'@oxlint-tsgolint/[email protected]':
optional: true
'@oxlint/[email protected]':
optional: true
'@oxlint/[email protected]':
optional: true
'@oxlint/[email protected]':
optional: true
'@oxlint/[email protected]':
optional: true
'@oxlint/[email protected]':
optional: true
'@oxlint/[email protected]':
optional: true
'@oxlint/[email protected]':
optional: true
'@oxlint/[email protected]':
optional: true
'@rollup/[email protected]': '@rollup/[email protected]':
optional: true optional: true
@@ -1105,6 +1237,27 @@ snapshots:
[email protected]: {} [email protected]: {}
[email protected]:
optionalDependencies:
'@oxlint-tsgolint/darwin-arm64': 0.10.0
'@oxlint-tsgolint/darwin-x64': 0.10.0
'@oxlint-tsgolint/linux-arm64': 0.10.0
'@oxlint-tsgolint/linux-x64': 0.10.0
'@oxlint-tsgolint/win32-arm64': 0.10.0
'@oxlint-tsgolint/win32-x64': 0.10.0
[email protected]([email protected]):
optionalDependencies:
'@oxlint/darwin-arm64': 1.36.0
'@oxlint/darwin-x64': 1.36.0
'@oxlint/linux-arm64-gnu': 1.36.0
'@oxlint/linux-arm64-musl': 1.36.0
'@oxlint/linux-x64-gnu': 1.36.0
'@oxlint/linux-x64-musl': 1.36.0
'@oxlint/win32-arm64': 1.36.0
'@oxlint/win32-x64': 1.36.0
oxlint-tsgolint: 0.10.0
[email protected]: {} [email protected]: {}
[email protected]: {} [email protected]: {}
+1 -5
View File
@@ -136,11 +136,7 @@ async function readSnapshotFromDisk(cachePath: string): Promise<RuntimeQueryIdSn
try { try {
const raw = await readFile(cachePath, 'utf8'); const raw = await readFile(cachePath, 'utf8');
return parseSnapshot(JSON.parse(raw)); return parseSnapshot(JSON.parse(raw));
} catch (error) { } catch {
const code = error && typeof error === 'object' ? String((error as { code?: unknown }).code ?? '') : '';
if (code !== 'ENOENT') {
return null;
}
return null; return null;
} }
} }
+8 -5
View File
@@ -512,7 +512,10 @@ export class TwitterClient {
const delaySecs = Number.isFinite(info.check_after_secs) ? Math.max(1, info.check_after_secs as number) : 2; const delaySecs = Number.isFinite(info.check_after_secs) ? Math.max(1, info.check_after_secs as number) : 2;
await this.sleep(delaySecs * 1000); await this.sleep(delaySecs * 1000);
const statusUrl = `${TWITTER_UPLOAD_URL}?${new URLSearchParams({ command: 'STATUS', media_id: mediaId })}`; const statusUrl = `${TWITTER_UPLOAD_URL}?${new URLSearchParams({
command: 'STATUS',
media_id: mediaId,
}).toString()}`;
const statusResp = await this.fetchWithTimeout(statusUrl, { const statusResp = await this.fetchWithTimeout(statusUrl, {
method: 'GET', method: 'GET',
headers: this.getUploadHeaders(), headers: this.getUploadHeaders(),
@@ -996,7 +999,7 @@ export class TwitterClient {
}); });
const queryId = await this.getQueryId('UserArticlesTweets'); const queryId = await this.getQueryId('UserArticlesTweets');
const url = `${TWITTER_API_BASE}/${queryId}/UserArticlesTweets?${params}`; const url = `${TWITTER_API_BASE}/${queryId}/UserArticlesTweets?${params.toString()}`;
try { try {
const response = await this.fetchWithTimeout(url, { method: 'GET', headers: this.getHeaders() }); const response = await this.fetchWithTimeout(url, { method: 'GET', headers: this.getHeaders() });
@@ -1141,7 +1144,7 @@ export class TwitterClient {
const queryIds = await this.getTweetDetailQueryIds(); const queryIds = await this.getTweetDetailQueryIds();
for (const queryId of queryIds) { for (const queryId of queryIds) {
const url = `${TWITTER_API_BASE}/${queryId}/TweetDetail?${params}`; const url = `${TWITTER_API_BASE}/${queryId}/TweetDetail?${params.toString()}`;
const response = await this.fetchWithTimeout(url, { const response = await this.fetchWithTimeout(url, {
method: 'GET', method: 'GET',
headers: this.getHeaders(), headers: this.getHeaders(),
@@ -1604,7 +1607,7 @@ export class TwitterClient {
const queryIds = await this.getSearchTimelineQueryIds(); const queryIds = await this.getSearchTimelineQueryIds();
for (const queryId of queryIds) { for (const queryId of queryIds) {
const url = `${TWITTER_API_BASE}/${queryId}/SearchTimeline?${params}`; const url = `${TWITTER_API_BASE}/${queryId}/SearchTimeline?${params.toString()}`;
try { try {
const response = await this.fetchWithTimeout(url, { const response = await this.fetchWithTimeout(url, {
@@ -1912,7 +1915,7 @@ export class TwitterClient {
const queryIds = await this.getBookmarksQueryIds(); const queryIds = await this.getBookmarksQueryIds();
for (const queryId of queryIds) { for (const queryId of queryIds) {
const url = `${TWITTER_API_BASE}/${queryId}/Bookmarks?${params}`; const url = `${TWITTER_API_BASE}/${queryId}/Bookmarks?${params.toString()}`;
try { try {
const response = await this.fetchWithTimeout(url, { const response = await this.fetchWithTimeout(url, {
+12 -5
View File
@@ -18,8 +18,6 @@ vi.mock('@steipete/sweet-cookie', () => ({
}), }),
})); }));
const itIfDarwin = process.platform === 'darwin' ? it : it.skip;
describe('cookies', () => { describe('cookies', () => {
const originalEnv = process.env; const originalEnv = process.env;
@@ -63,7 +61,10 @@ describe('cookies', () => {
expect(result.cookies.source).toContain('Firefox'); expect(result.cookies.source).toContain('Firefox');
}); });
itIfDarwin('honors cookieSource=safari', async () => { it('honors cookieSource=safari', async () => {
if (process.platform !== 'darwin') {
return;
}
sweet.results.set('safari', { sweet.results.set('safari', {
cookies: [ cookies: [
{ name: 'auth_token', value: 'safari_auth', domain: 'x.com' }, { name: 'auth_token', value: 'safari_auth', domain: 'x.com' },
@@ -245,7 +246,10 @@ describe('cookies', () => {
}); });
describe('extractCookiesFromSafari', () => { describe('extractCookiesFromSafari', () => {
itIfDarwin('returns cookies from Safari', async () => { it('returns cookies from Safari', async () => {
if (process.platform !== 'darwin') {
return;
}
sweet.results.set('safari', { sweet.results.set('safari', {
cookies: [ cookies: [
{ name: 'auth_token', value: 'safari_auth', domain: 'x.com' }, { name: 'auth_token', value: 'safari_auth', domain: 'x.com' },
@@ -262,7 +266,10 @@ describe('cookies', () => {
expect(result.cookies.source).toBe('Safari'); expect(result.cookies.source).toBe('Safari');
}); });
itIfDarwin('prefers Safari over Chrome when both are available', async () => { it('prefers Safari over Chrome when both are available', async () => {
if (process.platform !== 'darwin') {
return;
}
sweet.results.set('safari', { sweet.results.set('safari', {
cookies: [ cookies: [
{ name: 'auth_token', value: 'safari_auth', domain: 'x.com' }, { name: 'auth_token', value: 'safari_auth', domain: 'x.com' },
+9
View File
@@ -0,0 +1,9 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": true,
"rootDir": "."
},
"include": ["src/**/*.ts", "tests/**/*.ts", "scripts/**/*.ts"],
"exclude": ["node_modules", "dist", "coverage"]
}