Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apps/oxlint/src-js/plugins/lint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,8 @@ function lintFileImpl(
// Set `options` for rule
const optionsId = optionsIds[i];
debugAssert(optionsId < allOptions.length, "Options ID out of bounds");
ruleDetails.options = allOptions[optionsId];
ruleDetails.options =
optionsId === DEFAULT_OPTIONS_ID ? ruleDetails.defaultOptions : allOptions[optionsId];

let { visitor } = ruleDetails;
if (visitor === null) {
Expand Down
18 changes: 16 additions & 2 deletions apps/oxlint/src-js/plugins/load.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createContext } from "./context.js";
import { DEFAULT_OPTIONS } from "./options.js";
import { getErrorMessage } from "../utils/utils.js";

import type { Writable } from "type-fest";
Expand All @@ -8,7 +9,8 @@ import type { RuleMeta } from "./rule_meta.ts";
import type { AfterHook, BeforeHook, Visitor, VisitorWithHooks } from "./types.ts";
import type { SetNullable } from "../utils/types.ts";

const ObjectKeys = Object.keys;
const ObjectKeys = Object.keys,
{ isArray } = Array;

/**
* Linter plugin, comprising multiple rules
Expand Down Expand Up @@ -55,6 +57,7 @@ interface RuleDetailsBase {
readonly context: Readonly<Context>;
readonly isFixable: boolean;
readonly messages: Readonly<Record<string, string>> | null;
readonly defaultOptions: Readonly<Options>;
// Updated for each file
ruleIndex: number;
options: Readonly<Options> | null; // Initially `null`, set to options object before linting a file
Expand Down Expand Up @@ -147,7 +150,8 @@ async function loadPluginImpl(url: string, packageName: string | null): Promise<

// Validate `rule.meta` and convert to vars with standardized shape
let isFixable = false,
messages: Record<string, string> | null = null;
messages: Record<string, string> | null = null,
defaultOptions: Readonly<Options> = DEFAULT_OPTIONS;
const ruleMeta = rule.meta;
if (ruleMeta != null) {
if (typeof ruleMeta !== "object") throw new TypeError("Invalid `rule.meta`");
Expand All @@ -159,6 +163,15 @@ async function loadPluginImpl(url: string, packageName: string | null): Promise<
isFixable = true;
}

const inputDefaultOptions = ruleMeta.defaultOptions;
if (inputDefaultOptions != null) {
// TODO: Validate is JSON-serializable, and validate against provided options schema
if (!isArray(inputDefaultOptions)) {
throw new TypeError("`rule.meta.defaultOptions` must be an array if provided");
}
defaultOptions = inputDefaultOptions;
}

// Extract messages for messageId support
const inputMessages = ruleMeta.messages;
if (inputMessages != null) {
Expand All @@ -175,6 +188,7 @@ async function loadPluginImpl(url: string, packageName: string | null): Promise<
context: null!, // Filled in below
isFixable,
messages,
defaultOptions,
ruleIndex: 0,
options: null,
visitor: null,
Expand Down
2 changes: 1 addition & 1 deletion apps/oxlint/src-js/plugins/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type { JsonValue } from "./json.ts";
export type Options = JsonValue[];

// Default rule options
const DEFAULT_OPTIONS: Readonly<Options> = Object.freeze([]);
export const DEFAULT_OPTIONS: Readonly<Options> = Object.freeze([]);

// All rule options
export const allOptions: Readonly<Options>[] = [DEFAULT_OPTIONS];
Expand Down
10 changes: 10 additions & 0 deletions apps/oxlint/test/fixtures/options/.oxlintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"jsPlugins": ["./plugin.ts"],
"categories": {
"correctness": "off"
},
"rules": {
"options-plugin/options": "error",
"options-plugin/default-options": "error"
}
}
1 change: 1 addition & 0 deletions apps/oxlint/test/fixtures/options/files/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
debugger;
26 changes: 26 additions & 0 deletions apps/oxlint/test/fixtures/options/output.snap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Exit code
1

# stdout
```
x options-plugin(default-options): options: ["string",123,true,{"toBe":false,"notToBe":true}]
,-[files/index.js:1:1]
1 | debugger;
: ^
`----

x options-plugin(options): options: []
,-[files/index.js:1:1]
1 | debugger;
: ^
`----

Found 0 warnings and 2 errors.
Finished in Xms on 1 file using X threads.
```

# stderr
```
WARNING: JS plugins are experimental and not subject to semver.
Breaking changes are possible while JS plugins support is under development.
```
42 changes: 42 additions & 0 deletions apps/oxlint/test/fixtures/options/plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import type { Node, Plugin } from "#oxlint";

const SPAN: Node = {
start: 0,
end: 0,
range: [0, 0],
loc: {
start: { line: 0, column: 0 },
end: { line: 0, column: 0 },
},
};

const plugin: Plugin = {
meta: {
name: "options-plugin",
},
rules: {
options: {
create(context) {
context.report({
message: `options: ${JSON.stringify(context.options)}`,
node: SPAN,
});
return {};
},
},
"default-options": {
meta: {
defaultOptions: ["string", 123, true, { toBe: false, notToBe: true }],
},
create(context) {
context.report({
message: `options: ${JSON.stringify(context.options)}`,
node: SPAN,
});
return {};
},
},
},
};

export default plugin;
Loading