Skip to content

Commit 7f6d381

Browse files
committed
refactor(linter/plugins): enable TS strictNullChecks
1 parent 6d14c8b commit 7f6d381

File tree

23 files changed

+131
-70
lines changed

23 files changed

+131
-70
lines changed

apps/oxlint/src-js/cli.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ function loadPluginWrapper(path: string, packageName: string | null): Promise<st
2424
return loadPlugin(path, packageName);
2525
});
2626
}
27-
return loadPlugin(path, packageName);
27+
return loadPlugin!(path, packageName);
2828
}
2929

3030
/**

apps/oxlint/src-js/index.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
import { assertIsNonNull } from './plugins/utils.js';
2+
13
import type { Context, FileContext, LanguageOptions } from './plugins/context.ts';
24
import type { CreateOnceRule, Plugin, Rule } from './plugins/load.ts';
35
import type { Settings } from './plugins/settings.ts';
46
import type { SourceCode } from './plugins/source_code.ts';
57
import type { BeforeHook, Visitor, VisitorWithHooks } from './plugins/types.ts';
8+
import type { SetNullable } from './plugins/utils.ts';
69

710
export type * as ESTree from './generated/types.d.ts';
811
export type { Context, LanguageOptions } from './plugins/context.ts';
@@ -114,6 +117,7 @@ export function defineRule(rule: Rule): Rule {
114117
if (context === null) {
115118
({ context, visitor, beforeHook } = createContextAndVisitor(rule));
116119
}
120+
assertIsNonNull(visitor);
117121

118122
// Copy properties from ESLint's context object to `context`.
119123
// ESLint's context object is an object of form `{ id, options, report }`, with all other properties
@@ -131,7 +135,7 @@ export function defineRule(rule: Rule): Rule {
131135
}
132136

133137
// Return same visitor each time
134-
return visitor!;
138+
return visitor;
135139
};
136140

137141
return rule;
@@ -231,9 +235,13 @@ function createContextAndVisitor(rule: CreateOnceRule): {
231235
report: { value: null, enumerable: true, configurable: true },
232236
});
233237

234-
let { before: beforeHook, after: afterHook, ...visitor } = createOnce.call(rule, context) as VisitorWithHooks;
238+
let {
239+
before: beforeHook,
240+
after: afterHook,
241+
...visitor
242+
} = createOnce.call(rule, context) as SetNullable<VisitorWithHooks, 'before' | 'after'>;
235243

236-
if (beforeHook === void 0) {
244+
if (beforeHook === undefined) {
237245
beforeHook = null;
238246
} else if (beforeHook !== null && typeof beforeHook !== 'function') {
239247
throw new Error('`before` property of visitor must be a function if defined');

apps/oxlint/src-js/plugins/comments.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
*/
44

55
import { ast, initAst, sourceText } from './source_code.js';
6+
import { assertIsNonNull } from './utils.js';
67

78
import type { Comment, Node, NodeOrToken } from './types.ts';
89

@@ -15,6 +16,8 @@ const WHITESPACE_ONLY_REGEXP = /^\s*$/;
1516
*/
1617
export function getAllComments(): Comment[] {
1718
if (ast === null) initAst();
19+
assertIsNonNull(ast);
20+
1821
// `comments` property is a getter. Comments are deserialized lazily.
1922
return ast.comments;
2023
}
@@ -38,6 +41,8 @@ export function getAllComments(): Comment[] {
3841
*/
3942
export function getCommentsBefore(nodeOrToken: NodeOrToken): Comment[] {
4043
if (ast === null) initAst();
44+
assertIsNonNull(ast);
45+
assertIsNonNull(sourceText);
4146

4247
const { comments } = ast,
4348
commentsLength = comments.length;
@@ -93,6 +98,8 @@ export function getCommentsBefore(nodeOrToken: NodeOrToken): Comment[] {
9398
*/
9499
export function getCommentsAfter(nodeOrToken: NodeOrToken): Comment[] {
95100
if (ast === null) initAst();
101+
assertIsNonNull(ast);
102+
assertIsNonNull(sourceText);
96103

97104
const { comments } = ast,
98105
commentsLength = comments.length;
@@ -135,6 +142,7 @@ export function getCommentsAfter(nodeOrToken: NodeOrToken): Comment[] {
135142
*/
136143
export function getCommentsInside(node: Node): Comment[] {
137144
if (ast === null) initAst();
145+
assertIsNonNull(ast);
138146

139147
const { comments } = ast,
140148
commentsLength = comments.length;
@@ -178,6 +186,7 @@ export function getCommentsInside(node: Node): Comment[] {
178186
*/
179187
export function commentsExistBetween(nodeOrToken1: NodeOrToken, nodeOrToken2: NodeOrToken): boolean {
180188
if (ast === null) initAst();
189+
assertIsNonNull(ast);
181190

182191
// Find the first comment after `nodeOrToken1` ends.
183192
const { comments } = ast,

apps/oxlint/src-js/plugins/context.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import { ast, initAst, SOURCE_CODE } from './source_code.js';
3030
import { report } from './report.js';
3131
import { settings, initSettings } from './settings.js';
32+
import { assertIsNonNull } from './utils.js';
3233

3334
import type { Options, RuleDetails } from './load.ts';
3435
import type { Diagnostic } from './report.ts';
@@ -78,6 +79,8 @@ const PARSER_OPTIONS = freeze({
7879
// in case it's used in `create` to return an empty visitor if wrong type.
7980
// TODO: ESLint also has `commonjs` option.
8081
if (ast === null) initAst();
82+
assertIsNonNull(ast);
83+
8184
return ast.sourceType;
8285
},
8386
});
@@ -92,6 +95,8 @@ const LANGUAGE_OPTIONS = freeze({
9295
// in case it's used in `create` to return an empty visitor if wrong type.
9396
// TODO: ESLint also has `commonjs` option.
9497
if (ast === null) initAst();
98+
assertIsNonNull(ast);
99+
95100
return ast.sourceType;
96101
},
97102

@@ -250,7 +255,10 @@ const FILE_CONTEXT = freeze({
250255
*/
251256
get settings(): Readonly<Settings> {
252257
if (filePath === null) throw new Error('Cannot access `context.settings` in `createOnce`');
258+
253259
if (settings === null) initSettings();
260+
assertIsNonNull(settings);
261+
254262
return settings;
255263
},
256264

apps/oxlint/src-js/plugins/lint.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { registeredRules } from './load.js';
33
import { diagnostics } from './report.js';
44
import { setSettingsForFile, resetSettings } from './settings.js';
55
import { ast, initAst, resetSourceAndAst, setupSourceForFile } from './source_code.js';
6-
import { assertIs, getErrorMessage } from './utils.js';
6+
import { assertIs, assertIsNonNull, getErrorMessage } from './utils.js';
77
import { addVisitorToCompiled, compiledVisitor, finalizeCompiledVisitor, initCompiledVisitor } from './visitor.js';
88

99
// Lazy implementation
@@ -136,6 +136,7 @@ function lintFileImpl(
136136
let { visitor } = ruleDetails;
137137
if (visitor === null) {
138138
// Rule defined with `create` method
139+
assertIsNonNull(ruleDetails.rule.create);
139140
visitor = ruleDetails.rule.create(ruleDetails.context);
140141
} else {
141142
// Rule defined with `createOnce` method

apps/oxlint/src-js/plugins/load.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type { Context } from './context.ts';
88
import type { JsonValue } from './json.ts';
99
import type { RuleMeta } from './rule_meta.ts';
1010
import type { AfterHook, BeforeHook, Visitor, VisitorWithHooks } from './types.ts';
11+
import type { SetNullable } from './utils.ts';
1112

1213
const ObjectKeys = Object.keys;
1314

@@ -181,7 +182,7 @@ async function loadPluginImpl(path: string, packageName: string | null): Promise
181182
// Create `RuleDetails` object for rule.
182183
const ruleDetails: RuleDetails = {
183184
rule: rule as CreateRule, // Could also be `CreateOnceRule`, but just to satisfy type checker
184-
context: null as Readonly<Context>, // Filled in below
185+
context: null!, // Filled in below
185186
isFixable,
186187
messages,
187188
ruleIndex: 0,
@@ -197,7 +198,7 @@ async function loadPluginImpl(path: string, packageName: string | null): Promise
197198

198199
if ('createOnce' in rule) {
199200
// TODO: Compile visitor object to array here, instead of repeating compilation on each file
200-
let visitorWithHooks = rule.createOnce(context);
201+
let visitorWithHooks = rule.createOnce(context) as SetNullable<VisitorWithHooks, 'before' | 'after'>;
201202
if (typeof visitorWithHooks !== 'object' || visitorWithHooks === null) {
202203
throw new TypeError('`createOnce` must return an object');
203204
}
@@ -219,9 +220,9 @@ async function loadPluginImpl(path: string, packageName: string | null): Promise
219220
afterHook = null;
220221
}
221222

222-
(ruleDetails as Writable<CreateOnceRuleDetails>).visitor = visitor;
223-
(ruleDetails as Writable<CreateOnceRuleDetails>).beforeHook = beforeHook;
224-
(ruleDetails as Writable<CreateOnceRuleDetails>).afterHook = afterHook;
223+
(ruleDetails as unknown as Writable<CreateOnceRuleDetails>).visitor = visitor;
224+
(ruleDetails as unknown as Writable<CreateOnceRuleDetails>).beforeHook = beforeHook;
225+
(ruleDetails as unknown as Writable<CreateOnceRuleDetails>).afterHook = afterHook;
225226
}
226227

227228
registeredRules.push(ruleDetails);

apps/oxlint/src-js/plugins/location.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55

66
import { initSourceText, sourceText } from './source_code.js';
7+
import { assertIsNonNull } from './utils.js';
78

89
import type { Node } from './types.ts';
910

@@ -60,6 +61,7 @@ const lineStartOffsets: number[] = [0];
6061
*/
6162
export function initLines(): void {
6263
if (sourceText === null) initSourceText();
64+
assertIsNonNull(sourceText);
6365

6466
// This implementation is based on the one in ESLint.
6567
// TODO: Investigate if using `String.prototype.matchAll` is faster.
@@ -109,6 +111,7 @@ export function getLineColumnFromOffset(offset: number): LineColumn {
109111
// Build `lines` and `lineStartOffsets` tables if they haven't been already.
110112
// This also decodes `sourceText` if it wasn't already.
111113
if (lines.length === 0) initLines();
114+
assertIsNonNull(sourceText);
112115

113116
if (offset > sourceText.length) {
114117
throw new RangeError(
@@ -158,6 +161,7 @@ export function getOffsetFromLineColumn(loc: LineColumn): number {
158161
// Build `lines` and `lineStartOffsets` tables if they haven't been already.
159162
// This also decodes `sourceText` if it wasn't already.
160163
if (lines.length === 0) initLines();
164+
assertIsNonNull(sourceText);
161165

162166
const linesCount = lineStartOffsets.length;
163167
if (line <= 0 || line > linesCount) {

apps/oxlint/src-js/plugins/report.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ export function report(diagnostic: Diagnostic, ruleDetails: RuleDetails): void {
8484
}
8585

8686
// TODO: Validate `diagnostic`
87-
let start: number, end: number, loc: Location;
87+
let start: number, end: number, loc: Location | undefined;
8888

8989
if (hasOwn(diagnostic, 'loc') && (loc = diagnostic.loc) != null) {
9090
// `loc`

apps/oxlint/src-js/plugins/scope.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
type ScopeManager as TSESLintScopeManager,
99
} from '@typescript-eslint/scope-manager';
1010
import { ast, initAst } from './source_code.js';
11-
import { assertIs } from './utils.js';
11+
import { assertIs, assertIsNonNull } from './utils.js';
1212

1313
import type * as ESTree from '../generated/types.d.ts';
1414
import type { SetNullable } from './utils.ts';
@@ -109,6 +109,7 @@ const analyzeOptions: SetNullable<AnalyzeOptions, 'sourceType'> = {
109109
*/
110110
function initTsScopeManager() {
111111
if (ast === null) initAst();
112+
assertIsNonNull(ast);
112113

113114
analyzeOptions.sourceType = ast.sourceType;
114115
assertIs<AnalyzeOptions>(analyzeOptions);
@@ -199,6 +200,7 @@ export function isGlobalReference(node: ESTree.Node): boolean {
199200
if (node.type !== 'Identifier') return false;
200201

201202
if (tsScopeManager === null) initTsScopeManager();
203+
assertIsNonNull(tsScopeManager);
202204

203205
const { scopes } = tsScopeManager;
204206
if (scopes.length === 0) return false;
@@ -229,6 +231,8 @@ export function isGlobalReference(node: ESTree.Node): boolean {
229231
export function getDeclaredVariables(node: ESTree.Node): Variable[] {
230232
// ref: https://github.com/eslint/eslint/blob/e7cda3bdf1bdd664e6033503a3315ad81736b200/lib/languages/js/source-code/source-code.js#L904
231233
if (tsScopeManager === null) initTsScopeManager();
234+
assertIsNonNull(tsScopeManager);
235+
232236
// @ts-expect-error // TODO: Our types don't quite align yet
233237
return tsScopeManager.getDeclaredVariables(node);
234238
}
@@ -243,6 +247,7 @@ export function getScope(node: ESTree.Node): Scope {
243247
if (!node) throw new TypeError('Missing required argument: `node`');
244248

245249
if (tsScopeManager === null) initTsScopeManager();
250+
assertIsNonNull(tsScopeManager);
246251

247252
const inner = node.type !== 'Program';
248253

@@ -254,6 +259,7 @@ export function getScope(node: ESTree.Node): Scope {
254259
return scope.type === 'function-expression-name' ? scope.childScopes[0] : scope;
255260
}
256261

262+
// @ts-expect-error - Don't want to create a new variable just to make it nullable
257263
node = node.parent;
258264
} while (node !== null);
259265

apps/oxlint/src-js/plugins/selector.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ const EMPTY_TYPE_IDS_ARRAY: NodeTypeId[] = [];
5454
export function parseSelector(key: string): Selector {
5555
// Used cached object if we've parsed this key before
5656
let selector = cache.get(key);
57-
if (selector !== void 0) return selector;
57+
if (selector !== undefined) return selector;
5858

5959
// Parse with `esquery` and analyse
6060
const esquerySelector = esqueryParse(key);
@@ -97,7 +97,7 @@ function analyzeSelector(esquerySelector: EsquerySelector, selector: Selector):
9797
// If the type is invalid, just treat this selector as not matching any types.
9898
// But still increment `identifierCount`.
9999
// This matches ESLint's behavior.
100-
return typeId === void 0 ? EMPTY_TYPE_IDS_ARRAY : [typeId];
100+
return typeId === undefined ? EMPTY_TYPE_IDS_ARRAY : [typeId];
101101
}
102102

103103
case 'not':
@@ -144,7 +144,7 @@ function analyzeSelector(esquerySelector: EsquerySelector, selector: Selector):
144144
} else {
145145
// Selector only matches intersection of all child selectors.
146146
// TODO: Could make this faster if `analyzeSelector` always returned an ordered array.
147-
nodeTypes = childNodeTypes.filter((nodeType) => nodeTypes.includes(nodeType));
147+
nodeTypes = childNodeTypes.filter((nodeType) => nodeTypes!.includes(nodeType));
148148
}
149149
}
150150
return nodeTypes;

0 commit comments

Comments
 (0)