Skip to content

Commit f3aa4cd

Browse files
committed
refactor(linter/plugins): debug assert that values passed via NAPI are not undefined
1 parent 5f8e97f commit f3aa4cd

2 files changed

Lines changed: 45 additions & 5 deletions

File tree

apps/oxlint/src-js/cli.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { lint } from "./bindings.js";
2-
import { debugAssertIsNonNull } from "./utils/asserts.ts";
2+
import { debugAssertIsNonNull, debugAssertIsNotUndefined } from "./utils/asserts.ts";
33

44
// Lazy-loaded JS plugin-related functions.
55
// The type annotations below use `typeof import("./plugins/index.ts").<fn>` so that the lazy-loaded variables
@@ -29,15 +29,20 @@ function loadPluginWrapper(
2929
pluginNameIsAlias: boolean,
3030
workspaceUri: string | null | undefined,
3131
): Promise<string> {
32+
// Type generated by NAPI-RS for `lint` is inaccurate.
33+
// `Option::None` on Rust side becomes `null` on JS side, not `undefined`.
34+
debugAssertIsNotUndefined(pluginName, "`pluginName` should not be `undefined`");
35+
debugAssertIsNotUndefined(workspaceUri, "`workspaceUri` should not be `undefined`");
36+
3237
if (loadPlugin === null) {
3338
// Use promises here instead of making `loadPluginWrapper` an async function,
3439
// to avoid a micro-tick and extra wrapper `Promise` in all later calls to `loadPluginWrapper`
3540
return import("./plugins/index.ts").then((mod) => {
3641
({ loadPlugin, lintFile, setupRuleConfigs } = mod);
37-
return loadPlugin(path, pluginName ?? null, pluginNameIsAlias, workspaceUri ?? null);
42+
return loadPlugin(path, pluginName, pluginNameIsAlias, workspaceUri);
3843
});
3944
}
40-
return loadPlugin(path, pluginName ?? null, pluginNameIsAlias, workspaceUri ?? null);
45+
return loadPlugin(path, pluginName, pluginNameIsAlias, workspaceUri);
4146
}
4247

4348
/**
@@ -78,18 +83,23 @@ function lintFileWrapper(
7883
globalsJSON: string,
7984
workspaceUri: string | null | undefined,
8085
): string | null {
86+
// Type generated by NAPI-RS for `lint` is inaccurate.
87+
// `Option::None` on Rust side becomes `null` on JS side, not `undefined`.
88+
debugAssertIsNotUndefined(buffer, "`buffer` should not be `undefined`");
89+
debugAssertIsNotUndefined(workspaceUri, "`workspaceUri` should not be `undefined`");
90+
8191
// `lintFileWrapper` is never called without `loadPluginWrapper` being called first,
8292
// so `lintFile` must be defined here
8393
debugAssertIsNonNull(lintFile);
8494
return lintFile(
8595
filePath,
8696
bufferId,
87-
buffer ?? null,
97+
buffer,
8898
ruleIds,
8999
optionsIds,
90100
settingsJSON,
91101
globalsJSON,
92-
workspaceUri ?? null,
102+
workspaceUri,
93103
);
94104
}
95105

apps/oxlint/src-js/utils/asserts.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,36 @@ export function debugAssertIsNonNull<T>(
4747
}
4848
}
4949

50+
/**
51+
* Assert a value is not `undefined`.
52+
*
53+
* In release builds, is a no-op. Only does runtime checks in debug builds.
54+
* Minification removes this function and all calls to it in release builds, so it has zero runtime cost.
55+
*
56+
* Use this for testing conditions which would indicate a bug in the code.
57+
* Do NOT use this for validating user input.
58+
*
59+
* @param value - Value
60+
* @param message - Message to include in error if `value` is `undefined`, or a function which returns the message
61+
* to include in error (optional).
62+
*/
63+
export function debugAssertIsNotUndefined<T>(
64+
value: T | undefined,
65+
message?: string | (() => string),
66+
): asserts value is T {
67+
if (!DEBUG) return;
68+
69+
if (value === undefined) {
70+
if (typeof message === "function") {
71+
message = message();
72+
} else if (message === undefined) {
73+
message = "Expected value not to be undefined";
74+
}
75+
76+
throw new Error(message);
77+
}
78+
}
79+
5080
/**
5181
* Debug assert that `fn` is a function.
5282
*

0 commit comments

Comments
 (0)