Skip to content

Commit 35db628

Browse files
authored
Enable type-aware linting with oxlint (#2175)
1 parent 40f2862 commit 35db628

23 files changed

+185
-63
lines changed

.changeset/loud-laws-approve.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@sanity/pkg-utils": patch
3+
---
4+
5+
Fix issues uncovered by type-aware linting with oxlint

.oxlintrc.json

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,30 @@
2727
}
2828
},
2929
{
30-
"files": ["playground/**", "test/**", "packages/**/test/**"],
30+
"files": [
31+
"playground/**",
32+
"test/**",
33+
"packages/**/test/**",
34+
"**/*.test.ts",
35+
"**/*.test.js",
36+
"**/*.test.tsx",
37+
"**/*.test.jsx",
38+
"**/test.cjs"
39+
],
3140
"rules": {
3241
"eslint/no-console": "off",
3342
"eslint/no-unused-vars": "off",
34-
"triple-slash-reference": "off"
43+
"triple-slash-reference": "off",
44+
"await-thenable": "off",
45+
"no-floating-promises": "off",
46+
"no-unnecessary-boolean-literal-compare": "off",
47+
"no-unnecessary-template-expression": "off",
48+
"no-unnecessary-type-arguments": "off",
49+
"no-unnecessary-type-assertion": "off",
50+
"no-unsafe-enum-comparison": "off",
51+
"no-unsafe-type-assertion": "off",
52+
"restrict-template-expressions": "off",
53+
"unbound-method": "off"
3554
}
3655
}
3756
]

package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
"type": "module",
66
"scripts": {
77
"build": "pnpm --filter '@sanity/pkg-utils' build",
8-
"format": "prettier --write --cache --ignore-unknown . && oxlint --fix --quiet",
9-
"lint": "oxlint",
8+
"format": "prettier --write --cache --ignore-unknown . && oxlint --type-aware --fix --quiet",
9+
"lint": "oxlint --type-aware",
1010
"playground:build": "pnpm --filter './playground/**' build",
1111
"playground:clean": "pnpm --filter './playground/**' --silent clean",
1212
"playground:typecheck": "pnpm --filter './playground/**' typecheck",
@@ -19,7 +19,7 @@
1919
"lint-staged": {
2020
"*.{js,jsx,ts,tsx,mjs,cjs}": [
2121
"prettier --cache --write",
22-
"oxlint --fix --quiet"
22+
"oxlint --type-aware --fix --quiet"
2323
],
2424
"*": [
2525
"prettier --write --cache --ignore-unknown"
@@ -36,6 +36,7 @@
3636
"lint-staged": "^16.2.6",
3737
"npm-run-all2": "^8.0.4",
3838
"oxlint": "^1.26.0",
39+
"oxlint-tsgolint": "^0.5.0",
3940
"prettier": "^3.6.2",
4041
"rimraf": "^6.1.0",
4142
"typescript": "catalog:"

packages/@sanity/pkg-utils/src/node/build.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ export async function build(options: {
7474
const buildTasks = resolveBuildTasks(ctx)
7575

7676
for (const task of buildTasks) {
77+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- TypeScript can't infer the correct handler type from discriminated union
7778
const handler = buildTaskHandlers[task.type] as TaskHandler<BuildTask>
7879
const taskName = handler.name(ctx, task)
7980

@@ -90,7 +91,7 @@ export async function build(options: {
9091
spinner.error()
9192

9293
if (err instanceof Error) {
93-
const RE_CWD = new RegExp(`${cwd}`, 'g')
94+
const RE_CWD = new RegExp(cwd, 'g')
9495

9596
ctx.logger.error(err.message.replace(RE_CWD, '.'))
9697
ctx.logger.log()

packages/@sanity/pkg-utils/src/node/check.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,17 @@ export async function check(options: {
7474

7575
const consoleSpy = createConsoleSpy()
7676

77+
const checks = []
7778
if (exportPaths.import.length) {
78-
checkExports(exportPaths.import, {cwd, external, format: 'esm', logger})
79+
checks.push(checkExports(exportPaths.import, {cwd, external, format: 'esm', logger}))
7980
}
8081

8182
if (exportPaths.require.length) {
82-
checkExports(exportPaths.require, {cwd, external, format: 'cjs', logger})
83+
checks.push(checkExports(exportPaths.require, {cwd, external, format: 'cjs', logger}))
8384
}
8485

86+
await Promise.all(checks)
87+
8588
consoleSpy.restore()
8689
}
8790

@@ -94,7 +97,7 @@ export async function check(options: {
9497
spinner.error()
9598

9699
if (err instanceof Error) {
97-
const RE_CWD = new RegExp(`${cwd}`, 'g')
100+
const RE_CWD = new RegExp(cwd, 'g')
98101

99102
logger.error((err.stack || err.message).replace(RE_CWD, '.'))
100103
logger.log()
@@ -166,7 +169,7 @@ async function checkExports(
166169

167170
logger.log()
168171
} else {
169-
logger.error(`${err}`)
172+
logger.error(String(err))
170173

171174
logger.log()
172175
}

packages/@sanity/pkg-utils/src/node/core/config/loadConfig.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export async function loadConfig(options: {cwd: string}): Promise<PkgConfigOptio
1515

1616
const root = path.dirname(pkgPath)
1717

18-
const configFile = await findConfigFile(root)
18+
const configFile = findConfigFile(root)
1919

2020
if (!configFile) {
2121
return undefined

packages/@sanity/pkg-utils/src/node/core/config/resolveConfigProperty.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
import type {PkgConfigProperty, PkgConfigPropertyResolver} from './types.ts'
22

3+
function isPkgConfigPropertyResolver<T>(
4+
prop: PkgConfigProperty<T>,
5+
): prop is PkgConfigPropertyResolver<T> {
6+
return typeof prop === 'function'
7+
}
8+
39
/** @internal */
410
export function resolveConfigProperty<T>(
511
prop: PkgConfigProperty<T> | undefined,
612
initialValue: T,
713
): T {
814
if (!prop) return initialValue
915

10-
if (typeof prop === 'function') {
11-
return (prop as PkgConfigPropertyResolver<T>)(initialValue)
16+
if (isPkgConfigPropertyResolver(prop)) {
17+
return prop(initialValue)
1218
}
1319

1420
return prop

packages/@sanity/pkg-utils/src/node/core/pkg/parseExports.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ import {pkgExtMap} from './pkgExt.ts'
99
import type {PackageJSON} from './types.ts'
1010
import {validateExports} from './validateExports.ts'
1111

12+
// Type guard to filter out falsy values
13+
function isTruthy<T>(value: T | false | null | undefined | 0 | ''): value is T {
14+
return Boolean(value)
15+
}
16+
1217
/** @alpha */
1318
export function parseExports(options: {
1419
cwd: string
@@ -89,7 +94,7 @@ export function parseExports(options: {
8994
}
9095

9196
errors.push(
92-
...([
97+
...[
9398
'package.json: `exports` are missing, it should be:',
9499
`"exports": {`,
95100
` ".": {`,
@@ -102,7 +107,7 @@ export function parseExports(options: {
102107
` },`,
103108
` "./package.json": "./package.json"`,
104109
`}`,
105-
].filter(Boolean) as string[]),
110+
].filter(isTruthy),
106111
)
107112
}
108113
}

packages/@sanity/pkg-utils/src/node/core/pkg/validatePkg.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ const pkgSchema = z.object({
1818
exports: z.optional(
1919
z.record(
2020
z.union([
21-
z.custom<`./${string}.json`>((val) => /^\.\/.*\.json$/.test(val as string)),
22-
z.custom<`./${string}.css`>((val) => /^\.\/.*\.css$/.test(val as string)),
21+
z.custom<`./${string}.json`>(
22+
(val) => typeof val === 'string' && /^\.\/.*\.json$/.test(val),
23+
),
24+
z.custom<`./${string}.css`>((val) => typeof val === 'string' && /^\.\/.*\.css$/.test(val)),
2325
z.object({
2426
types: z.optional(z.string()),
2527
source: z.optional(z.string()),
@@ -65,6 +67,7 @@ for (const key of pkgSchema.keyof()._def.values) {
6567
export function validatePkg(input: unknown): PackageJSON {
6668
const pkg = pkgSchema.parse(input)
6769

70+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Need to check raw input for typos
6871
const invalidKey = Object.keys(input as PackageJSON).find((key) => {
6972
const needle = key.toUpperCase()
7073

packages/@sanity/pkg-utils/src/node/core/ts/loadTSConfig.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ export async function loadTSConfig(options: {
77
}): Promise<ts.ParsedCommandLine | undefined> {
88
const {cwd, tsconfigPath} = options
99

10-
const configPath = ts.findConfigFile(cwd, ts.sys.fileExists, tsconfigPath)
10+
const configPath = ts.findConfigFile(cwd, (fileName) => ts.sys.fileExists(fileName), tsconfigPath)
1111

1212
if (!configPath) {
1313
return undefined
1414
}
1515

16-
const configFile = ts.readConfigFile(configPath, ts.sys.readFile)
16+
const configFile = ts.readConfigFile(configPath, (path) => ts.sys.readFile(path))
1717

1818
return ts.parseJsonConfigFileContent(configFile.config, ts.sys, cwd)
1919
}

0 commit comments

Comments
 (0)