diff --git a/apps/oxlint/src-js/package/compat.ts b/apps/oxlint/src-js/package/compat.ts new file mode 100644 index 0000000000000..b66180f2be2b2 --- /dev/null +++ b/apps/oxlint/src-js/package/compat.ts @@ -0,0 +1,217 @@ +/* + * `eslintCompatPlugin` function. + * Converts an Oxlint plugin using `createOnce` to a plugin which will run in ESLint. + */ + +import { debugAssertIsNonNull } from "../utils/asserts.ts"; + +import type { Context, FileContext, LanguageOptions } from "../plugins/context.ts"; +import type { CreateOnceRule, Plugin, Rule } from "../plugins/load.ts"; +import type { Settings } from "../plugins/settings.ts"; +import type { SourceCode } from "../plugins/source_code.ts"; +import type { BeforeHook, Visitor, VisitorWithHooks } from "../plugins/types.ts"; +import type { SetNullable } from "../utils/types.ts"; + +// Empty visitor object, returned by `create` when `before` hook returns `false`. +const EMPTY_VISITOR: Visitor = {}; + +/** + * Convert a plugin which used Oxlint's `createOnce` API to also work with ESLint. + * + * If any of the plugin's rules use the Oxlint alternative `createOnce` API, + * add ESLint-compatible `create` methods to those rules, which delegate to `createOnce`. + * This makes the plugin compatible with ESLint. + * + * The `plugin` object passed in is mutated in-place. + * + * @param plugin - Plugin to convert + * @returns Plugin with all rules having `create` method + * @throws {Error} If `plugin` is not an object, or `plugin.rules` is not an object + */ +export function eslintCompatPlugin(plugin: Plugin): Plugin { + // Validate type of `plugin` + if (plugin === null || typeof plugin !== "object") throw new Error("Plugin must be an object"); + + const { rules } = plugin; + if (rules === null || typeof rules !== "object") { + throw new Error("Plugin must have an object as `rules` property"); + } + + // Make each rule in the plugin ESLint-compatible by calling `convertRule` on it + for (const ruleName in rules) { + if (Object.hasOwn(rules, ruleName)) convertRule(rules[ruleName]); + } + + return plugin; +} + +/** + * Convert a rule. + * + * The `rule` object passed in is mutated in-place. + * + * @param rule - Rule to convert + * @throws {Error} If `rule` is not an object + */ +function convertRule(rule: Rule) { + // Validate type of `rule` + if (rule === null || typeof rule !== "object") throw new Error("Rule must be an object"); + + // If rule already has `create` method, no need to convert + if ("create" in rule) return; + + // Add `create` function to `rule` + let context: Context | null = null, + visitor: Visitor | undefined, + beforeHook: BeforeHook | null; + + rule.create = (eslintContext) => { + // Lazily call `createOnce` on first invocation of `create` + if (context === null) { + ({ context, visitor, beforeHook } = createContextAndVisitor(rule)); + } + debugAssertIsNonNull(visitor); + + // Copy properties from ESLint's context object to `context`. + // ESLint's context object is an object of form `{ id, options, report }`, with all other properties + // and methods on another object which is its prototype. + Object.defineProperties(context, { + id: { value: eslintContext.id }, + options: { value: eslintContext.options }, + report: { value: eslintContext.report }, + }); + Object.setPrototypeOf(context, Object.getPrototypeOf(eslintContext)); + + // If `before` hook returns `false`, skip traversal by returning an empty object as visitor + if (beforeHook !== null) { + const shouldRun = beforeHook(); + if (shouldRun === false) return EMPTY_VISITOR; + } + + // Return same visitor each time + return visitor; + }; +} + +// File context object. Used as prototype for `Context` objects for each rule during `createOnce` call. +// When running the rules, ESLint's `context` object's prototype is switched in as prototype for `Context` objects. +// +// Only `extends` method is available in `createOnce`, so only that is implemented here. +// All other getters/methods throw, same as they do in main implementation. +// +// See `FILE_CONTEXT` in `plugins/context.ts` for details of all the getters/methods. +const FILE_CONTEXT: FileContext = Object.freeze({ + get filename(): string { + throw new Error("Cannot access `context.filename` in `createOnce`"); + }, + + getFilename(): string { + throw new Error("Cannot call `context.getFilename` in `createOnce`"); + }, + + get physicalFilename(): string { + throw new Error("Cannot access `context.physicalFilename` in `createOnce`"); + }, + + getPhysicalFilename(): string { + throw new Error("Cannot call `context.getPhysicalFilename` in `createOnce`"); + }, + + get cwd(): string { + throw new Error("Cannot access `context.cwd` in `createOnce`"); + }, + + getCwd(): string { + throw new Error("Cannot call `context.getCwd` in `createOnce`"); + }, + + get sourceCode(): SourceCode { + throw new Error("Cannot access `context.sourceCode` in `createOnce`"); + }, + + getSourceCode(): SourceCode { + throw new Error("Cannot call `context.getSourceCode` in `createOnce`"); + }, + + get languageOptions(): LanguageOptions { + throw new Error("Cannot access `context.languageOptions` in `createOnce`"); + }, + + get settings(): Readonly { + throw new Error("Cannot access `context.settings` in `createOnce`"); + }, + + extend(this: FileContext, extension: Record): FileContext { + // Note: We can allow calling `extend` in `createOnce`, as it involves no file-specific state + return Object.freeze(Object.assign(Object.create(this), extension)); + }, + + get parserOptions(): Record { + throw new Error("Cannot access `context.parserOptions` in `createOnce`"); + }, + + get parserPath(): string | undefined { + throw new Error("Cannot access `context.parserPath` in `createOnce`"); + }, +}); + +/** + * Call `createOnce` method of rule, and return `Context`, `Visitor`, and `beforeHook` (if any). + * + * @param rule - Rule with `createOnce` method + * @returns Object with `context`, `visitor`, and `beforeHook` properties + */ +function createContextAndVisitor(rule: CreateOnceRule): { + context: Context; + visitor: Visitor; + beforeHook: BeforeHook | null; +} { + // Validate type of `createOnce` + const { createOnce } = rule; + if (createOnce == null) { + throw new Error("Rules must define either a `create` or `createOnce` method"); + } + if (typeof createOnce !== "function") { + throw new Error("Rule `createOnce` property must be a function"); + } + + // Call `createOnce` with empty context object. + // Really, accessing `options` or calling `report` should throw, because they're illegal in `createOnce`. + // But any such bugs should have been caught when testing the rule in Oxlint, so should be OK to take this shortcut. + // `FILE_CONTEXT` prototype provides `extends` method, which is available in `createOnce`. + const context: Context = Object.create(FILE_CONTEXT, { + id: { value: "", enumerable: true, configurable: true }, + options: { value: null, enumerable: true, configurable: true }, + report: { value: null, enumerable: true, configurable: true }, + }); + + let { + before: beforeHook, + after: afterHook, + ...visitor + } = createOnce.call(rule, context) as SetNullable; + + if (beforeHook === undefined) { + beforeHook = null; + } else if (beforeHook !== null && typeof beforeHook !== "function") { + throw new Error("`before` property of visitor must be a function if defined"); + } + + // Add `after` hook to `Program:exit` visit fn + if (afterHook != null) { + if (typeof afterHook !== "function") { + throw new Error("`after` property of visitor must be a function if defined"); + } + + const programExit = visitor["Program:exit"]; + visitor["Program:exit"] = + programExit == null + ? (_node) => afterHook() + : (node) => { + programExit(node); + afterHook(); + }; + } + + return { context, visitor, beforeHook }; +} diff --git a/apps/oxlint/src-js/package/define.ts b/apps/oxlint/src-js/package/define.ts index ab935013e23fe..d3fe2e7965879 100644 --- a/apps/oxlint/src-js/package/define.ts +++ b/apps/oxlint/src-js/package/define.ts @@ -2,224 +2,28 @@ * `definePlugin` and `defineRule` functions. */ -import { debugAssertIsNonNull } from "../utils/asserts.ts"; - -import type { Context, FileContext, LanguageOptions } from "../plugins/context.ts"; -import type { CreateOnceRule, Plugin, Rule } from "../plugins/load.ts"; -import type { Settings } from "../plugins/settings.ts"; -import type { SourceCode } from "../plugins/source_code.ts"; -import type { BeforeHook, Visitor, VisitorWithHooks } from "../plugins/types.ts"; -import type { SetNullable } from "../utils/types.ts"; - -// Empty visitor object, returned by `create` when `before` hook returns `false`. -const EMPTY_VISITOR: Visitor = {}; +import type { Plugin, Rule } from "../plugins/load.ts"; /** * Define a plugin. * - * If any of the plugin's rules use the Oxlint alternative `createOnce` API, - * add ESLint-compatible `create` methods to those rules, which delegate to `createOnce`. - * This makes the plugin compatible with ESLint. - * - * The `plugin` object passed in is mutated in-place. + * No-op function, just to provide type safety. Input is passed through unchanged. * * @param plugin - Plugin to define - * @returns Plugin with all rules having `create` method - * @throws {Error} If `plugin` is not an object, or `plugin.rules` is not an object + * @returns Same plugin as passed in */ export function definePlugin(plugin: Plugin): Plugin { - // Validate type of `plugin` - if (plugin === null || typeof plugin !== "object") throw new Error("Plugin must be an object"); - - const { rules } = plugin; - if (rules === null || typeof rules !== "object") { - throw new Error("Plugin must have an object as `rules` property"); - } - - // Make each rule in the plugin ESLint-compatible by calling `defineRule` on it - for (const ruleName in rules) { - if (Object.hasOwn(rules, ruleName)) { - rules[ruleName] = defineRule(rules[ruleName]); - } - } - return plugin; } /** * Define a rule. * - * If `rule` uses the Oxlint alternative `createOnce` API, add an ESLint-compatible - * `create` method to the rule, which delegates to `createOnce`. - * This makes the rule compatible with ESLint. - * - * The `rule` object passed in is mutated in-place. + * No-op function, just to provide type safety. Input is passed through unchanged. * * @param rule - Rule to define - * @returns Rule with `create` method - * @throws {Error} If `rule` is not an object + * @returns Same rule as passed in */ export function defineRule(rule: Rule): Rule { - // Validate type of `rule` - if (rule === null || typeof rule !== "object") throw new Error("Rule must be an object"); - - // If rule already has `create` method, return it as is - if ("create" in rule) return rule; - - // Add `create` function to `rule` - let context: Context | null = null, - visitor: Visitor | undefined, - beforeHook: BeforeHook | null; - - rule.create = (eslintContext) => { - // Lazily call `createOnce` on first invocation of `create` - if (context === null) { - ({ context, visitor, beforeHook } = createContextAndVisitor(rule)); - } - debugAssertIsNonNull(visitor); - - // Copy properties from ESLint's context object to `context`. - // ESLint's context object is an object of form `{ id, options, report }`, with all other properties - // and methods on another object which is its prototype. - Object.defineProperties(context, { - id: { value: eslintContext.id }, - options: { value: eslintContext.options }, - report: { value: eslintContext.report }, - }); - Object.setPrototypeOf(context, Object.getPrototypeOf(eslintContext)); - - // If `before` hook returns `false`, skip traversal by returning an empty object as visitor - if (beforeHook !== null) { - const shouldRun = beforeHook(); - if (shouldRun === false) return EMPTY_VISITOR; - } - - // Return same visitor each time - return visitor; - }; - return rule; } - -// File context object. Used as prototype for `Context` objects for each rule during `createOnce` call. -// When running the rules, ESLint's `context` object is switching in as prototype for `Context` objects. -// -// Only `extends` method is available in `createOnce`, so only that is implemented here. -// All other getters/methods throw, same as they do in main implementation. -// -// See `FILE_CONTEXT` in `plugins/context.ts` for details of all the getters/methods. -const FILE_CONTEXT: FileContext = Object.freeze({ - get filename(): string { - throw new Error("Cannot access `context.filename` in `createOnce`"); - }, - - getFilename(): string { - throw new Error("Cannot call `context.getFilename` in `createOnce`"); - }, - - get physicalFilename(): string { - throw new Error("Cannot access `context.physicalFilename` in `createOnce`"); - }, - - getPhysicalFilename(): string { - throw new Error("Cannot call `context.getPhysicalFilename` in `createOnce`"); - }, - - get cwd(): string { - throw new Error("Cannot access `context.cwd` in `createOnce`"); - }, - - getCwd(): string { - throw new Error("Cannot call `context.getCwd` in `createOnce`"); - }, - - get sourceCode(): SourceCode { - throw new Error("Cannot access `context.sourceCode` in `createOnce`"); - }, - - getSourceCode(): SourceCode { - throw new Error("Cannot call `context.getSourceCode` in `createOnce`"); - }, - - get languageOptions(): LanguageOptions { - throw new Error("Cannot access `context.languageOptions` in `createOnce`"); - }, - - get settings(): Readonly { - throw new Error("Cannot access `context.settings` in `createOnce`"); - }, - - extend(this: FileContext, extension: Record): FileContext { - // Note: We can allow calling `extend` in `createOnce`, as it involves no file-specific state - return Object.freeze(Object.assign(Object.create(this), extension)); - }, - - get parserOptions(): Record { - throw new Error("Cannot access `context.parserOptions` in `createOnce`"); - }, - - get parserPath(): string | undefined { - throw new Error("Cannot access `context.parserPath` in `createOnce`"); - }, -}); - -/** - * Call `createOnce` method of rule, and return `Context`, `Visitor`, and `beforeHook` (if any). - * - * @param rule - Rule with `createOnce` method - * @returns Object with `context`, `visitor`, and `beforeHook` properties - */ -function createContextAndVisitor(rule: CreateOnceRule): { - context: Context; - visitor: Visitor; - beforeHook: BeforeHook | null; -} { - // Validate type of `createOnce` - const { createOnce } = rule; - if (createOnce == null) { - throw new Error("Rules must define either a `create` or `createOnce` method"); - } - if (typeof createOnce !== "function") { - throw new Error("Rule `createOnce` property must be a function"); - } - - // Call `createOnce` with empty context object. - // Really, accessing `options` or calling `report` should throw, because they're illegal in `createOnce`. - // But any such bugs should have been caught when testing the rule in Oxlint, so should be OK to take this shortcut. - // `FILE_CONTEXT` prototype provides `extends` method, which is available in `createOnce`. - const context: Context = Object.create(FILE_CONTEXT, { - id: { value: "", enumerable: true, configurable: true }, - options: { value: null, enumerable: true, configurable: true }, - report: { value: null, enumerable: true, configurable: true }, - }); - - let { - before: beforeHook, - after: afterHook, - ...visitor - } = createOnce.call(rule, context) as SetNullable; - - if (beforeHook === undefined) { - beforeHook = null; - } else if (beforeHook !== null && typeof beforeHook !== "function") { - throw new Error("`before` property of visitor must be a function if defined"); - } - - // Add `after` hook to `Program:exit` visit fn - if (afterHook != null) { - if (typeof afterHook !== "function") { - throw new Error("`after` property of visitor must be a function if defined"); - } - - const programExit = visitor["Program:exit"]; - visitor["Program:exit"] = - programExit == null - ? (_node) => afterHook() - : (node) => { - programExit(node); - afterHook(); - }; - } - - return { context, visitor, beforeHook }; -} diff --git a/apps/oxlint/src-js/plugin.ts b/apps/oxlint/src-js/plugin.ts index 4440e22a442ed..b0c267a84df30 100644 --- a/apps/oxlint/src-js/plugin.ts +++ b/apps/oxlint/src-js/plugin.ts @@ -1,5 +1,6 @@ -// Entry point for definePlugin and defineRule +// Re-export `definePlugin`, `defineRule`, and `eslintCompatPlugin` functions export { definePlugin, defineRule } from "./package/define.ts"; +export { eslintCompatPlugin } from "./package/compat.ts"; // ESTree types export type * as ESTree from "./generated/types.d.ts"; diff --git a/apps/oxlint/src-js/plugins/load.ts b/apps/oxlint/src-js/plugins/load.ts index 92a6c4e098bcb..cb09995bec2cb 100644 --- a/apps/oxlint/src-js/plugins/load.ts +++ b/apps/oxlint/src-js/plugins/load.ts @@ -29,7 +29,7 @@ export interface Plugin { * If `createOnce` method is present, `create` is ignored. * * If defining the rule with `createOnce`, and you want the rule to work with ESLint too, - * you need to wrap the rule with `defineRule`. + * you need to wrap the plugin containing the rule with `eslintCompatPlugin`. */ export type Rule = CreateRule | CreateOnceRule; diff --git a/apps/oxlint/test/fixtures/definePlugin/.oxlintrc.json b/apps/oxlint/test/fixtures/definePlugin/.oxlintrc.json index 8efed9986574e..645c7ec9a8dcf 100644 --- a/apps/oxlint/test/fixtures/definePlugin/.oxlintrc.json +++ b/apps/oxlint/test/fixtures/definePlugin/.oxlintrc.json @@ -3,11 +3,6 @@ "categories": { "correctness": "off" }, "rules": { "define-plugin-plugin/create": "error", - "define-plugin-plugin/create-once": "error", - "define-plugin-plugin/create-once-before-false": "error", - "define-plugin-plugin/create-once-before-only": "error", - "define-plugin-plugin/create-once-after-only": "error", - "define-plugin-plugin/create-once-hooks-only": "error", - "define-plugin-plugin/create-once-no-hooks": "error" + "define-plugin-plugin/create-once": "error" } } diff --git a/apps/oxlint/test/fixtures/definePlugin/eslint.config.js b/apps/oxlint/test/fixtures/definePlugin/eslint.config.js deleted file mode 100644 index 3546931bab668..0000000000000 --- a/apps/oxlint/test/fixtures/definePlugin/eslint.config.js +++ /dev/null @@ -1,19 +0,0 @@ -import plugin from "./plugin.ts"; - -export default [ - { - files: ["files/*.js"], - plugins: { - "define-plugin-plugin": plugin, - }, - rules: { - "define-plugin-plugin/create": "error", - "define-plugin-plugin/create-once": "error", - "define-plugin-plugin/create-once-before-false": "error", - "define-plugin-plugin/create-once-before-only": "error", - "define-plugin-plugin/create-once-after-only": "error", - "define-plugin-plugin/create-once-hooks-only": "error", - "define-plugin-plugin/create-once-no-hooks": "error", - }, - }, -]; diff --git a/apps/oxlint/test/fixtures/definePlugin/eslint.snap.md b/apps/oxlint/test/fixtures/definePlugin/eslint.snap.md deleted file mode 100644 index a26f214da5d71..0000000000000 --- a/apps/oxlint/test/fixtures/definePlugin/eslint.snap.md +++ /dev/null @@ -1,103 +0,0 @@ -# Exit code -1 - -# stdout -``` -/files/1.js - 0:1 error create body: -this === rule: true define-plugin-plugin/create - 0:1 error before hook: -createOnce call count: 1 -this === rule: true -filename: /files/1.js define-plugin-plugin/create-once - 0:1 error before hook: -filename: /files/1.js define-plugin-plugin/create-once-before-false - 0:1 error before hook: -filename: /files/1.js define-plugin-plugin/create-once-before-only - 0:1 error before hook: -filename: /files/1.js define-plugin-plugin/create-once-hooks-only - 0:1 error after hook: -identNum: 2 -filename: /files/1.js define-plugin-plugin/create-once - 0:1 error after hook: -filename: /files/1.js define-plugin-plugin/create-once-after-only - 0:1 error after hook: -filename: /files/1.js define-plugin-plugin/create-once-hooks-only - 1:5 error ident visit fn "a": -filename: /files/1.js define-plugin-plugin/create - 1:5 error ident visit fn "a": -identNum: 1 -filename: /files/1.js define-plugin-plugin/create-once - 1:5 error ident visit fn "a": -filename: /files/1.js define-plugin-plugin/create-once-before-only - 1:5 error ident visit fn "a": -filename: /files/1.js define-plugin-plugin/create-once-after-only - 1:5 error ident visit fn "a": -filename: /files/1.js define-plugin-plugin/create-once-no-hooks - 1:8 error ident visit fn "b": -filename: /files/1.js define-plugin-plugin/create - 1:8 error ident visit fn "b": -identNum: 2 -filename: /files/1.js define-plugin-plugin/create-once - 1:8 error ident visit fn "b": -filename: /files/1.js define-plugin-plugin/create-once-before-only - 1:8 error ident visit fn "b": -filename: /files/1.js define-plugin-plugin/create-once-after-only - 1:8 error ident visit fn "b": -filename: /files/1.js define-plugin-plugin/create-once-no-hooks - -/files/2.js - 0:1 error create body: -this === rule: true define-plugin-plugin/create - 0:1 error before hook: -createOnce call count: 1 -this === rule: true -filename: /files/2.js define-plugin-plugin/create-once - 0:1 error before hook: -filename: /files/2.js define-plugin-plugin/create-once-before-false - 0:1 error before hook: -filename: /files/2.js define-plugin-plugin/create-once-before-only - 0:1 error before hook: -filename: /files/2.js define-plugin-plugin/create-once-hooks-only - 0:1 error after hook: -identNum: 2 -filename: /files/2.js define-plugin-plugin/create-once - 0:1 error after hook: -filename: /files/2.js define-plugin-plugin/create-once-before-false - 0:1 error after hook: -filename: /files/2.js define-plugin-plugin/create-once-after-only - 0:1 error after hook: -filename: /files/2.js define-plugin-plugin/create-once-hooks-only - 1:5 error ident visit fn "c": -filename: /files/2.js define-plugin-plugin/create - 1:5 error ident visit fn "c": -identNum: 1 -filename: /files/2.js define-plugin-plugin/create-once - 1:5 error ident visit fn "c": -filename: /files/2.js define-plugin-plugin/create-once-before-false - 1:5 error ident visit fn "c": -filename: /files/2.js define-plugin-plugin/create-once-before-only - 1:5 error ident visit fn "c": -filename: /files/2.js define-plugin-plugin/create-once-after-only - 1:5 error ident visit fn "c": -filename: /files/2.js define-plugin-plugin/create-once-no-hooks - 1:8 error ident visit fn "d": -filename: /files/2.js define-plugin-plugin/create - 1:8 error ident visit fn "d": -identNum: 2 -filename: /files/2.js define-plugin-plugin/create-once - 1:8 error ident visit fn "d": -filename: /files/2.js define-plugin-plugin/create-once-before-false - 1:8 error ident visit fn "d": -filename: /files/2.js define-plugin-plugin/create-once-before-only - 1:8 error ident visit fn "d": -filename: /files/2.js define-plugin-plugin/create-once-after-only - 1:8 error ident visit fn "d": -filename: /files/2.js define-plugin-plugin/create-once-no-hooks - -✖ 39 problems (39 errors, 0 warnings) -``` - -# stderr -``` -``` diff --git a/apps/oxlint/test/fixtures/definePlugin/output.snap.md b/apps/oxlint/test/fixtures/definePlugin/output.snap.md index 5dedfdbe41ed3..2ffdca28a99c4 100644 --- a/apps/oxlint/test/fixtures/definePlugin/output.snap.md +++ b/apps/oxlint/test/fixtures/definePlugin/output.snap.md @@ -3,51 +3,6 @@ # stdout ``` - x define-plugin-plugin(create): create body: - | this === rule: true - ,-[files/1.js:1:1] - 1 | let a, b; - : ^ - `---- - - x define-plugin-plugin(create-once): before hook: - | createOnce call count: 1 - | this === rule: true - | filename: /files/1.js - ,-[files/1.js:1:1] - 1 | let a, b; - : ^ - `---- - - x define-plugin-plugin(create-once): after hook: - | identNum: 2 - | filename: /files/1.js - ,-[files/1.js:1:1] - 1 | let a, b; - : ^ - `---- - - x define-plugin-plugin(create-once-after-only): after hook: - | filename: /files/1.js - ,-[files/1.js:1:1] - 1 | let a, b; - : ^ - `---- - - x define-plugin-plugin(create-once-before-false): before hook: - | filename: /files/1.js - ,-[files/1.js:1:1] - 1 | let a, b; - : ^ - `---- - - x define-plugin-plugin(create-once-before-only): before hook: - | filename: /files/1.js - ,-[files/1.js:1:1] - 1 | let a, b; - : ^ - `---- - x define-plugin-plugin(create): ident visit fn "a": | filename: /files/1.js ,-[files/1.js:1:5] @@ -56,28 +11,6 @@ `---- x define-plugin-plugin(create-once): ident visit fn "a": - | identNum: 1 - | filename: /files/1.js - ,-[files/1.js:1:5] - 1 | let a, b; - : ^ - `---- - - x define-plugin-plugin(create-once-after-only): ident visit fn "a": - | filename: /files/1.js - ,-[files/1.js:1:5] - 1 | let a, b; - : ^ - `---- - - x define-plugin-plugin(create-once-before-only): ident visit fn "a": - | filename: /files/1.js - ,-[files/1.js:1:5] - 1 | let a, b; - : ^ - `---- - - x define-plugin-plugin(create-once-no-hooks): ident visit fn "a": | filename: /files/1.js ,-[files/1.js:1:5] 1 | let a, b; @@ -92,86 +25,12 @@ `---- x define-plugin-plugin(create-once): ident visit fn "b": - | identNum: 2 | filename: /files/1.js ,-[files/1.js:1:8] 1 | let a, b; : ^ `---- - x define-plugin-plugin(create-once-after-only): ident visit fn "b": - | filename: /files/1.js - ,-[files/1.js:1:8] - 1 | let a, b; - : ^ - `---- - - x define-plugin-plugin(create-once-before-only): ident visit fn "b": - | filename: /files/1.js - ,-[files/1.js:1:8] - 1 | let a, b; - : ^ - `---- - - x define-plugin-plugin(create-once-no-hooks): ident visit fn "b": - | filename: /files/1.js - ,-[files/1.js:1:8] - 1 | let a, b; - : ^ - `---- - - x define-plugin-plugin(create): create body: - | this === rule: true - ,-[files/2.js:1:1] - 1 | let c, d; - : ^ - `---- - - x define-plugin-plugin(create-once): before hook: - | createOnce call count: 1 - | this === rule: true - | filename: /files/2.js - ,-[files/2.js:1:1] - 1 | let c, d; - : ^ - `---- - - x define-plugin-plugin(create-once): after hook: - | identNum: 2 - | filename: /files/2.js - ,-[files/2.js:1:1] - 1 | let c, d; - : ^ - `---- - - x define-plugin-plugin(create-once-after-only): after hook: - | filename: /files/2.js - ,-[files/2.js:1:1] - 1 | let c, d; - : ^ - `---- - - x define-plugin-plugin(create-once-before-false): before hook: - | filename: /files/2.js - ,-[files/2.js:1:1] - 1 | let c, d; - : ^ - `---- - - x define-plugin-plugin(create-once-before-false): after hook: - | filename: /files/2.js - ,-[files/2.js:1:1] - 1 | let c, d; - : ^ - `---- - - x define-plugin-plugin(create-once-before-only): before hook: - | filename: /files/2.js - ,-[files/2.js:1:1] - 1 | let c, d; - : ^ - `---- - x define-plugin-plugin(create): ident visit fn "c": | filename: /files/2.js ,-[files/2.js:1:5] @@ -180,35 +39,6 @@ `---- x define-plugin-plugin(create-once): ident visit fn "c": - | identNum: 1 - | filename: /files/2.js - ,-[files/2.js:1:5] - 1 | let c, d; - : ^ - `---- - - x define-plugin-plugin(create-once-after-only): ident visit fn "c": - | filename: /files/2.js - ,-[files/2.js:1:5] - 1 | let c, d; - : ^ - `---- - - x define-plugin-plugin(create-once-before-false): ident visit fn "c": - | filename: /files/2.js - ,-[files/2.js:1:5] - 1 | let c, d; - : ^ - `---- - - x define-plugin-plugin(create-once-before-only): ident visit fn "c": - | filename: /files/2.js - ,-[files/2.js:1:5] - 1 | let c, d; - : ^ - `---- - - x define-plugin-plugin(create-once-no-hooks): ident visit fn "c": | filename: /files/2.js ,-[files/2.js:1:5] 1 | let c, d; @@ -223,43 +53,14 @@ `---- x define-plugin-plugin(create-once): ident visit fn "d": - | identNum: 2 - | filename: /files/2.js - ,-[files/2.js:1:8] - 1 | let c, d; - : ^ - `---- - - x define-plugin-plugin(create-once-after-only): ident visit fn "d": - | filename: /files/2.js - ,-[files/2.js:1:8] - 1 | let c, d; - : ^ - `---- - - x define-plugin-plugin(create-once-before-false): ident visit fn "d": - | filename: /files/2.js - ,-[files/2.js:1:8] - 1 | let c, d; - : ^ - `---- - - x define-plugin-plugin(create-once-before-only): ident visit fn "d": - | filename: /files/2.js - ,-[files/2.js:1:8] - 1 | let c, d; - : ^ - `---- - - x define-plugin-plugin(create-once-no-hooks): ident visit fn "d": | filename: /files/2.js ,-[files/2.js:1:8] 1 | let c, d; : ^ `---- -Found 0 warnings and 35 errors. -Finished in Xms on 2 files with 7 rules using X threads. +Found 0 warnings and 8 errors. +Finished in Xms on 2 files with 2 rules using X threads. ``` # stderr diff --git a/apps/oxlint/test/fixtures/definePlugin/plugin.ts b/apps/oxlint/test/fixtures/definePlugin/plugin.ts index 115fb0baba32d..1c8621e005b7d 100644 --- a/apps/oxlint/test/fixtures/definePlugin/plugin.ts +++ b/apps/oxlint/test/fixtures/definePlugin/plugin.ts @@ -1,201 +1,21 @@ import { definePlugin } from "#oxlint/plugin"; -import type { Node, Rule } from "#oxlint/plugin"; - -const SPAN: Node = { - start: 0, - end: 0, - range: [0, 0], - loc: { - start: { line: 0, column: 0 }, - end: { line: 0, column: 0 }, - }, -}; +import type { Rule } from "#oxlint/plugin"; const createRule: Rule = { create(context) { - context.report({ message: `create body:\nthis === rule: ${this === createRule}`, node: SPAN }); - return { - Identifier(node) { - context.report({ - message: `ident visit fn "${node.name}":\nfilename: ${context.filename}`, - node, - }); - }, - }; - }, -}; - -// This aims to test that `createOnce` is called once only, and `before` hook is called once per file. -// i.e. Oxlint calls `createOnce` directly, and not the `create` method that `defineRule` (via `definePlugin`) -// adds to the rule. -let createOnceCallCount = 0; - -const createOnceRule: Rule = { - createOnce(context) { - createOnceCallCount++; - - // `fileNum` should be different for each file. - // `identNum` should start at 1 for each file. - let fileNum = 0, - identNum: number; - // Note: Files are processed in unpredictable order, so `files/1.js` may be `fileNum` 1 or 2. - // Therefore, collect all visits and check them in `after` hook of the 2nd file. - const visits: { fileNum: number; identNum: number }[] = []; - - // `this` should be the rule object - // oxlint-disable-next-line typescript-eslint/no-this-alias - const topLevelThis = this; - - return { - before() { - fileNum++; - identNum = 0; - - context.report({ - message: - "before hook:\n" + - `createOnce call count: ${createOnceCallCount}\n` + - `this === rule: ${topLevelThis === createOnceRule}\n` + - `filename: ${context.filename}`, - node: SPAN, - }); - }, - Identifier(node) { - identNum++; - visits.push({ fileNum, identNum }); - - context.report({ - message: - `ident visit fn "${node.name}":\n` + - `identNum: ${identNum}\n` + - `filename: ${context.filename}`, - node, - }); - }, - after() { - context.report({ - message: "after hook:\n" + `identNum: ${identNum}\n` + `filename: ${context.filename}`, - node: SPAN, - }); - - if (fileNum === 2) { - visits.sort((v1, v2) => v1.fileNum - v2.fileNum); - - const expectedVisits = [ - { fileNum: 1, identNum: 1 }, - { fileNum: 1, identNum: 2 }, - { fileNum: 2, identNum: 1 }, - { fileNum: 2, identNum: 2 }, - ]; - - if ( - visits.length !== expectedVisits.length || - visits.some( - (v, i) => - v.fileNum !== expectedVisits[i].fileNum || - v.identNum !== expectedVisits[i].identNum, - ) - ) { - context.report({ message: `Unexpected visits: ${JSON.stringify(visits)}`, node: SPAN }); - } - } - }, - }; - }, -}; - -// Tests that `before` hook returning `false` disables visiting AST for the file. -const createOnceBeforeFalseRule: Rule = { - createOnce(context) { - return { - before() { - context.report({ - message: "before hook:\n" + `filename: ${context.filename}`, - node: SPAN, - }); - - // Only visit AST for `files/2.js` - return context.filename.endsWith("2.js"); - }, Identifier(node) { context.report({ message: `ident visit fn "${node.name}":\n` + `filename: ${context.filename}`, node, }); }, - after() { - context.report({ - message: "after hook:\n" + `filename: ${context.filename}`, - node: SPAN, - }); - }, - }; - }, -}; - -// These 4 rules test that `createOnce` without `before` and `after` hooks works correctly. - -const createOnceBeforeOnlyRule: Rule = { - createOnce(context) { - return { - before() { - context.report({ - message: "before hook:\n" + `filename: ${context.filename}`, - node: SPAN, - }); - }, - Identifier(node) { - context.report({ - message: `ident visit fn "${node.name}":\n` + `filename: ${context.filename}`, - node, - }); - }, - }; - }, -}; - -const createOnceAfterOnlyRule: Rule = { - createOnce(context) { - return { - Identifier(node) { - context.report({ - message: `ident visit fn "${node.name}":\n` + `filename: ${context.filename}`, - node, - }); - }, - after() { - context.report({ - message: "after hook:\n" + `filename: ${context.filename}`, - node: SPAN, - }); - }, }; }, }; -const createOnceHooksOnlyRule: Rule = { - createOnce(context) { - return { - // Neither hook should be called, because no AST node visitor functions - before() { - context.report({ - message: "before hook:\n" + `filename: ${context.filename}`, - node: SPAN, - }); - }, - after() { - context.report({ - message: "after hook:\n" + `filename: ${context.filename}`, - node: SPAN, - }); - }, - }; - }, -}; - -const createOnceNoHooksRule: Rule = { +const createOnceRule: Rule = { createOnce(context) { return { Identifier(node) { @@ -215,10 +35,5 @@ export default definePlugin({ rules: { create: createRule, "create-once": createOnceRule, - "create-once-before-false": createOnceBeforeFalseRule, - "create-once-before-only": createOnceBeforeOnlyRule, - "create-once-after-only": createOnceAfterOnlyRule, - "create-once-hooks-only": createOnceHooksOnlyRule, - "create-once-no-hooks": createOnceNoHooksRule, }, }); diff --git a/apps/oxlint/test/fixtures/definePlugin_and_defineRule/.oxlintrc.json b/apps/oxlint/test/fixtures/definePlugin_and_defineRule/.oxlintrc.json deleted file mode 100644 index 1a0cebc110ea5..0000000000000 --- a/apps/oxlint/test/fixtures/definePlugin_and_defineRule/.oxlintrc.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "jsPlugins": ["./plugin.ts"], - "categories": { "correctness": "off" }, - "rules": { - "define-plugin-and-rule-plugin/create": "error", - "define-plugin-and-rule-plugin/create-once": "error", - "define-plugin-and-rule-plugin/create-once-before-false": "error", - "define-plugin-and-rule-plugin/create-once-before-only": "error", - "define-plugin-and-rule-plugin/create-once-after-only": "error", - "define-plugin-and-rule-plugin/create-once-hooks-only": "error", - "define-plugin-and-rule-plugin/create-once-no-hooks": "error" - } -} diff --git a/apps/oxlint/test/fixtures/definePlugin_and_defineRule/eslint.config.js b/apps/oxlint/test/fixtures/definePlugin_and_defineRule/eslint.config.js deleted file mode 100644 index 315fef87cb166..0000000000000 --- a/apps/oxlint/test/fixtures/definePlugin_and_defineRule/eslint.config.js +++ /dev/null @@ -1,19 +0,0 @@ -import plugin from "./plugin.ts"; - -export default [ - { - files: ["files/*.js"], - plugins: { - "define-plugin-and-rule-plugin": plugin, - }, - rules: { - "define-plugin-and-rule-plugin/create": "error", - "define-plugin-and-rule-plugin/create-once": "error", - "define-plugin-and-rule-plugin/create-once-before-false": "error", - "define-plugin-and-rule-plugin/create-once-before-only": "error", - "define-plugin-and-rule-plugin/create-once-after-only": "error", - "define-plugin-and-rule-plugin/create-once-hooks-only": "error", - "define-plugin-and-rule-plugin/create-once-no-hooks": "error", - }, - }, -]; diff --git a/apps/oxlint/test/fixtures/definePlugin_and_defineRule/eslint.snap.md b/apps/oxlint/test/fixtures/definePlugin_and_defineRule/eslint.snap.md deleted file mode 100644 index 34fea45856490..0000000000000 --- a/apps/oxlint/test/fixtures/definePlugin_and_defineRule/eslint.snap.md +++ /dev/null @@ -1,103 +0,0 @@ -# Exit code -1 - -# stdout -``` -/files/1.js - 0:1 error create body: -this === rule: true define-plugin-and-rule-plugin/create - 0:1 error before hook: -createOnce call count: 1 -this === rule: true -filename: /files/1.js define-plugin-and-rule-plugin/create-once - 0:1 error before hook: -filename: /files/1.js define-plugin-and-rule-plugin/create-once-before-false - 0:1 error before hook: -filename: /files/1.js define-plugin-and-rule-plugin/create-once-before-only - 0:1 error before hook: -filename: /files/1.js define-plugin-and-rule-plugin/create-once-hooks-only - 0:1 error after hook: -identNum: 2 -filename: /files/1.js define-plugin-and-rule-plugin/create-once - 0:1 error after hook: -filename: /files/1.js define-plugin-and-rule-plugin/create-once-after-only - 0:1 error after hook: -filename: /files/1.js define-plugin-and-rule-plugin/create-once-hooks-only - 1:5 error ident visit fn "a": -filename: /files/1.js define-plugin-and-rule-plugin/create - 1:5 error ident visit fn "a": -identNum: 1 -filename: /files/1.js define-plugin-and-rule-plugin/create-once - 1:5 error ident visit fn "a": -filename: /files/1.js define-plugin-and-rule-plugin/create-once-before-only - 1:5 error ident visit fn "a": -filename: /files/1.js define-plugin-and-rule-plugin/create-once-after-only - 1:5 error ident visit fn "a": -filename: /files/1.js define-plugin-and-rule-plugin/create-once-no-hooks - 1:8 error ident visit fn "b": -filename: /files/1.js define-plugin-and-rule-plugin/create - 1:8 error ident visit fn "b": -identNum: 2 -filename: /files/1.js define-plugin-and-rule-plugin/create-once - 1:8 error ident visit fn "b": -filename: /files/1.js define-plugin-and-rule-plugin/create-once-before-only - 1:8 error ident visit fn "b": -filename: /files/1.js define-plugin-and-rule-plugin/create-once-after-only - 1:8 error ident visit fn "b": -filename: /files/1.js define-plugin-and-rule-plugin/create-once-no-hooks - -/files/2.js - 0:1 error create body: -this === rule: true define-plugin-and-rule-plugin/create - 0:1 error before hook: -createOnce call count: 1 -this === rule: true -filename: /files/2.js define-plugin-and-rule-plugin/create-once - 0:1 error before hook: -filename: /files/2.js define-plugin-and-rule-plugin/create-once-before-false - 0:1 error before hook: -filename: /files/2.js define-plugin-and-rule-plugin/create-once-before-only - 0:1 error before hook: -filename: /files/2.js define-plugin-and-rule-plugin/create-once-hooks-only - 0:1 error after hook: -identNum: 2 -filename: /files/2.js define-plugin-and-rule-plugin/create-once - 0:1 error after hook: -filename: /files/2.js define-plugin-and-rule-plugin/create-once-before-false - 0:1 error after hook: -filename: /files/2.js define-plugin-and-rule-plugin/create-once-after-only - 0:1 error after hook: -filename: /files/2.js define-plugin-and-rule-plugin/create-once-hooks-only - 1:5 error ident visit fn "c": -filename: /files/2.js define-plugin-and-rule-plugin/create - 1:5 error ident visit fn "c": -identNum: 1 -filename: /files/2.js define-plugin-and-rule-plugin/create-once - 1:5 error ident visit fn "c": -filename: /files/2.js define-plugin-and-rule-plugin/create-once-before-false - 1:5 error ident visit fn "c": -filename: /files/2.js define-plugin-and-rule-plugin/create-once-before-only - 1:5 error ident visit fn "c": -filename: /files/2.js define-plugin-and-rule-plugin/create-once-after-only - 1:5 error ident visit fn "c": -filename: /files/2.js define-plugin-and-rule-plugin/create-once-no-hooks - 1:8 error ident visit fn "d": -filename: /files/2.js define-plugin-and-rule-plugin/create - 1:8 error ident visit fn "d": -identNum: 2 -filename: /files/2.js define-plugin-and-rule-plugin/create-once - 1:8 error ident visit fn "d": -filename: /files/2.js define-plugin-and-rule-plugin/create-once-before-false - 1:8 error ident visit fn "d": -filename: /files/2.js define-plugin-and-rule-plugin/create-once-before-only - 1:8 error ident visit fn "d": -filename: /files/2.js define-plugin-and-rule-plugin/create-once-after-only - 1:8 error ident visit fn "d": -filename: /files/2.js define-plugin-and-rule-plugin/create-once-no-hooks - -✖ 39 problems (39 errors, 0 warnings) -``` - -# stderr -``` -``` diff --git a/apps/oxlint/test/fixtures/definePlugin_and_defineRule/options.json b/apps/oxlint/test/fixtures/definePlugin_and_defineRule/options.json deleted file mode 100644 index f380a596b9d7a..0000000000000 --- a/apps/oxlint/test/fixtures/definePlugin_and_defineRule/options.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "eslint": true -} diff --git a/apps/oxlint/test/fixtures/defineRule/.oxlintrc.json b/apps/oxlint/test/fixtures/defineRule/.oxlintrc.json index 9a2ce74204b05..d5a9663734b7d 100644 --- a/apps/oxlint/test/fixtures/defineRule/.oxlintrc.json +++ b/apps/oxlint/test/fixtures/defineRule/.oxlintrc.json @@ -3,11 +3,6 @@ "categories": { "correctness": "off" }, "rules": { "define-rule-plugin/create": "error", - "define-rule-plugin/create-once": "error", - "define-rule-plugin/create-once-before-false": "error", - "define-rule-plugin/create-once-before-only": "error", - "define-rule-plugin/create-once-after-only": "error", - "define-rule-plugin/create-once-hooks-only": "error", - "define-rule-plugin/create-once-no-hooks": "error" + "define-rule-plugin/create-once": "error" } } diff --git a/apps/oxlint/test/fixtures/defineRule/eslint.config.js b/apps/oxlint/test/fixtures/defineRule/eslint.config.js deleted file mode 100644 index a17f865b95c38..0000000000000 --- a/apps/oxlint/test/fixtures/defineRule/eslint.config.js +++ /dev/null @@ -1,19 +0,0 @@ -import plugin from "./plugin.ts"; - -export default [ - { - files: ["files/*.js"], - plugins: { - "define-rule-plugin": plugin, - }, - rules: { - "define-rule-plugin/create": "error", - "define-rule-plugin/create-once": "error", - "define-rule-plugin/create-once-before-false": "error", - "define-rule-plugin/create-once-before-only": "error", - "define-rule-plugin/create-once-after-only": "error", - "define-rule-plugin/create-once-hooks-only": "error", - "define-rule-plugin/create-once-no-hooks": "error", - }, - }, -]; diff --git a/apps/oxlint/test/fixtures/defineRule/eslint.snap.md b/apps/oxlint/test/fixtures/defineRule/eslint.snap.md deleted file mode 100644 index 0908bd2eb2082..0000000000000 --- a/apps/oxlint/test/fixtures/defineRule/eslint.snap.md +++ /dev/null @@ -1,103 +0,0 @@ -# Exit code -1 - -# stdout -``` -/files/1.js - 0:1 error create body: -this === rule: true define-rule-plugin/create - 0:1 error before hook: -createOnce call count: 1 -this === rule: true -filename: /files/1.js define-rule-plugin/create-once - 0:1 error before hook: -filename: /files/1.js define-rule-plugin/create-once-before-false - 0:1 error before hook: -filename: /files/1.js define-rule-plugin/create-once-before-only - 0:1 error before hook: -filename: /files/1.js define-rule-plugin/create-once-hooks-only - 0:1 error after hook: -identNum: 2 -filename: /files/1.js define-rule-plugin/create-once - 0:1 error after hook: -filename: /files/1.js define-rule-plugin/create-once-after-only - 0:1 error after hook: -filename: /files/1.js define-rule-plugin/create-once-hooks-only - 1:5 error ident visit fn "a": -filename: /files/1.js define-rule-plugin/create - 1:5 error ident visit fn "a": -identNum: 1 -filename: /files/1.js define-rule-plugin/create-once - 1:5 error ident visit fn "a": -filename: /files/1.js define-rule-plugin/create-once-before-only - 1:5 error ident visit fn "a": -filename: /files/1.js define-rule-plugin/create-once-after-only - 1:5 error ident visit fn "a": -filename: /files/1.js define-rule-plugin/create-once-no-hooks - 1:8 error ident visit fn "b": -filename: /files/1.js define-rule-plugin/create - 1:8 error ident visit fn "b": -identNum: 2 -filename: /files/1.js define-rule-plugin/create-once - 1:8 error ident visit fn "b": -filename: /files/1.js define-rule-plugin/create-once-before-only - 1:8 error ident visit fn "b": -filename: /files/1.js define-rule-plugin/create-once-after-only - 1:8 error ident visit fn "b": -filename: /files/1.js define-rule-plugin/create-once-no-hooks - -/files/2.js - 0:1 error create body: -this === rule: true define-rule-plugin/create - 0:1 error before hook: -createOnce call count: 1 -this === rule: true -filename: /files/2.js define-rule-plugin/create-once - 0:1 error before hook: -filename: /files/2.js define-rule-plugin/create-once-before-false - 0:1 error before hook: -filename: /files/2.js define-rule-plugin/create-once-before-only - 0:1 error before hook: -filename: /files/2.js define-rule-plugin/create-once-hooks-only - 0:1 error after hook: -identNum: 2 -filename: /files/2.js define-rule-plugin/create-once - 0:1 error after hook: -filename: /files/2.js define-rule-plugin/create-once-before-false - 0:1 error after hook: -filename: /files/2.js define-rule-plugin/create-once-after-only - 0:1 error after hook: -filename: /files/2.js define-rule-plugin/create-once-hooks-only - 1:5 error ident visit fn "c": -filename: /files/2.js define-rule-plugin/create - 1:5 error ident visit fn "c": -identNum: 1 -filename: /files/2.js define-rule-plugin/create-once - 1:5 error ident visit fn "c": -filename: /files/2.js define-rule-plugin/create-once-before-false - 1:5 error ident visit fn "c": -filename: /files/2.js define-rule-plugin/create-once-before-only - 1:5 error ident visit fn "c": -filename: /files/2.js define-rule-plugin/create-once-after-only - 1:5 error ident visit fn "c": -filename: /files/2.js define-rule-plugin/create-once-no-hooks - 1:8 error ident visit fn "d": -filename: /files/2.js define-rule-plugin/create - 1:8 error ident visit fn "d": -identNum: 2 -filename: /files/2.js define-rule-plugin/create-once - 1:8 error ident visit fn "d": -filename: /files/2.js define-rule-plugin/create-once-before-false - 1:8 error ident visit fn "d": -filename: /files/2.js define-rule-plugin/create-once-before-only - 1:8 error ident visit fn "d": -filename: /files/2.js define-rule-plugin/create-once-after-only - 1:8 error ident visit fn "d": -filename: /files/2.js define-rule-plugin/create-once-no-hooks - -✖ 39 problems (39 errors, 0 warnings) -``` - -# stderr -``` -``` diff --git a/apps/oxlint/test/fixtures/defineRule/options.json b/apps/oxlint/test/fixtures/defineRule/options.json deleted file mode 100644 index f380a596b9d7a..0000000000000 --- a/apps/oxlint/test/fixtures/defineRule/options.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "eslint": true -} diff --git a/apps/oxlint/test/fixtures/defineRule/output.snap.md b/apps/oxlint/test/fixtures/defineRule/output.snap.md index 4dfc433722a42..b331fd1ac3110 100644 --- a/apps/oxlint/test/fixtures/defineRule/output.snap.md +++ b/apps/oxlint/test/fixtures/defineRule/output.snap.md @@ -3,51 +3,6 @@ # stdout ``` - x define-rule-plugin(create): create body: - | this === rule: true - ,-[files/1.js:1:1] - 1 | let a, b; - : ^ - `---- - - x define-rule-plugin(create-once): before hook: - | createOnce call count: 1 - | this === rule: true - | filename: /files/1.js - ,-[files/1.js:1:1] - 1 | let a, b; - : ^ - `---- - - x define-rule-plugin(create-once): after hook: - | identNum: 2 - | filename: /files/1.js - ,-[files/1.js:1:1] - 1 | let a, b; - : ^ - `---- - - x define-rule-plugin(create-once-after-only): after hook: - | filename: /files/1.js - ,-[files/1.js:1:1] - 1 | let a, b; - : ^ - `---- - - x define-rule-plugin(create-once-before-false): before hook: - | filename: /files/1.js - ,-[files/1.js:1:1] - 1 | let a, b; - : ^ - `---- - - x define-rule-plugin(create-once-before-only): before hook: - | filename: /files/1.js - ,-[files/1.js:1:1] - 1 | let a, b; - : ^ - `---- - x define-rule-plugin(create): ident visit fn "a": | filename: /files/1.js ,-[files/1.js:1:5] @@ -56,28 +11,6 @@ `---- x define-rule-plugin(create-once): ident visit fn "a": - | identNum: 1 - | filename: /files/1.js - ,-[files/1.js:1:5] - 1 | let a, b; - : ^ - `---- - - x define-rule-plugin(create-once-after-only): ident visit fn "a": - | filename: /files/1.js - ,-[files/1.js:1:5] - 1 | let a, b; - : ^ - `---- - - x define-rule-plugin(create-once-before-only): ident visit fn "a": - | filename: /files/1.js - ,-[files/1.js:1:5] - 1 | let a, b; - : ^ - `---- - - x define-rule-plugin(create-once-no-hooks): ident visit fn "a": | filename: /files/1.js ,-[files/1.js:1:5] 1 | let a, b; @@ -92,86 +25,12 @@ `---- x define-rule-plugin(create-once): ident visit fn "b": - | identNum: 2 | filename: /files/1.js ,-[files/1.js:1:8] 1 | let a, b; : ^ `---- - x define-rule-plugin(create-once-after-only): ident visit fn "b": - | filename: /files/1.js - ,-[files/1.js:1:8] - 1 | let a, b; - : ^ - `---- - - x define-rule-plugin(create-once-before-only): ident visit fn "b": - | filename: /files/1.js - ,-[files/1.js:1:8] - 1 | let a, b; - : ^ - `---- - - x define-rule-plugin(create-once-no-hooks): ident visit fn "b": - | filename: /files/1.js - ,-[files/1.js:1:8] - 1 | let a, b; - : ^ - `---- - - x define-rule-plugin(create): create body: - | this === rule: true - ,-[files/2.js:1:1] - 1 | let c, d; - : ^ - `---- - - x define-rule-plugin(create-once): before hook: - | createOnce call count: 1 - | this === rule: true - | filename: /files/2.js - ,-[files/2.js:1:1] - 1 | let c, d; - : ^ - `---- - - x define-rule-plugin(create-once): after hook: - | identNum: 2 - | filename: /files/2.js - ,-[files/2.js:1:1] - 1 | let c, d; - : ^ - `---- - - x define-rule-plugin(create-once-after-only): after hook: - | filename: /files/2.js - ,-[files/2.js:1:1] - 1 | let c, d; - : ^ - `---- - - x define-rule-plugin(create-once-before-false): before hook: - | filename: /files/2.js - ,-[files/2.js:1:1] - 1 | let c, d; - : ^ - `---- - - x define-rule-plugin(create-once-before-false): after hook: - | filename: /files/2.js - ,-[files/2.js:1:1] - 1 | let c, d; - : ^ - `---- - - x define-rule-plugin(create-once-before-only): before hook: - | filename: /files/2.js - ,-[files/2.js:1:1] - 1 | let c, d; - : ^ - `---- - x define-rule-plugin(create): ident visit fn "c": | filename: /files/2.js ,-[files/2.js:1:5] @@ -180,35 +39,6 @@ `---- x define-rule-plugin(create-once): ident visit fn "c": - | identNum: 1 - | filename: /files/2.js - ,-[files/2.js:1:5] - 1 | let c, d; - : ^ - `---- - - x define-rule-plugin(create-once-after-only): ident visit fn "c": - | filename: /files/2.js - ,-[files/2.js:1:5] - 1 | let c, d; - : ^ - `---- - - x define-rule-plugin(create-once-before-false): ident visit fn "c": - | filename: /files/2.js - ,-[files/2.js:1:5] - 1 | let c, d; - : ^ - `---- - - x define-rule-plugin(create-once-before-only): ident visit fn "c": - | filename: /files/2.js - ,-[files/2.js:1:5] - 1 | let c, d; - : ^ - `---- - - x define-rule-plugin(create-once-no-hooks): ident visit fn "c": | filename: /files/2.js ,-[files/2.js:1:5] 1 | let c, d; @@ -223,43 +53,14 @@ `---- x define-rule-plugin(create-once): ident visit fn "d": - | identNum: 2 - | filename: /files/2.js - ,-[files/2.js:1:8] - 1 | let c, d; - : ^ - `---- - - x define-rule-plugin(create-once-after-only): ident visit fn "d": - | filename: /files/2.js - ,-[files/2.js:1:8] - 1 | let c, d; - : ^ - `---- - - x define-rule-plugin(create-once-before-false): ident visit fn "d": - | filename: /files/2.js - ,-[files/2.js:1:8] - 1 | let c, d; - : ^ - `---- - - x define-rule-plugin(create-once-before-only): ident visit fn "d": - | filename: /files/2.js - ,-[files/2.js:1:8] - 1 | let c, d; - : ^ - `---- - - x define-rule-plugin(create-once-no-hooks): ident visit fn "d": | filename: /files/2.js ,-[files/2.js:1:8] 1 | let c, d; : ^ `---- -Found 0 warnings and 35 errors. -Finished in Xms on 2 files with 7 rules using X threads. +Found 0 warnings and 8 errors. +Finished in Xms on 2 files with 2 rules using X threads. ``` # stderr diff --git a/apps/oxlint/test/fixtures/defineRule/plugin.ts b/apps/oxlint/test/fixtures/defineRule/plugin.ts index 0027e6f21ce13..d1f6652c71df3 100644 --- a/apps/oxlint/test/fixtures/defineRule/plugin.ts +++ b/apps/oxlint/test/fixtures/defineRule/plugin.ts @@ -1,150 +1,8 @@ import { defineRule } from "#oxlint/plugin"; -import type { Node } from "#oxlint/plugin"; - -const SPAN: Node = { - start: 0, - end: 0, - range: [0, 0], - loc: { - start: { line: 0, column: 0 }, - end: { line: 0, column: 0 }, - }, -}; - const createRule = defineRule({ create(context) { - context.report({ message: `create body:\nthis === rule: ${this === createRule}`, node: SPAN }); - - return { - Identifier(node) { - context.report({ - message: `ident visit fn "${node.name}":\nfilename: ${context.filename}`, - node, - }); - }, - }; - }, -}); - -// This aims to test that `createOnce` is called once only, and `before` hook is called once per file. -// i.e. Oxlint calls `createOnce` directly, and not the `create` method that `defineRule` adds to the rule. -let createOnceCallCount = 0; - -const createOnceRule = defineRule({ - createOnce(context) { - createOnceCallCount++; - - // `fileNum` should be different for each file. - // `identNum` should start at 1 for each file. - let fileNum = 0, - identNum: number; - // Note: Files are processed in unpredictable order, so `files/1.js` may be `fileNum` 1 or 2. - // Therefore, collect all visits and check them in `after` hook of the 2nd file. - const visits: { fileNum: number; identNum: number }[] = []; - - // `this` should be the rule object returned by `defineRule` - // oxlint-disable-next-line typescript-eslint/no-this-alias - const topLevelThis = this; - - return { - before() { - fileNum++; - identNum = 0; - - context.report({ - message: - "before hook:\n" + - `createOnce call count: ${createOnceCallCount}\n` + - `this === rule: ${topLevelThis === createOnceRule}\n` + - `filename: ${context.filename}`, - node: SPAN, - }); - }, - Identifier(node) { - identNum++; - visits.push({ fileNum, identNum }); - - context.report({ - message: - `ident visit fn "${node.name}":\n` + - `identNum: ${identNum}\n` + - `filename: ${context.filename}`, - node, - }); - }, - after() { - context.report({ - message: "after hook:\n" + `identNum: ${identNum}\n` + `filename: ${context.filename}`, - node: SPAN, - }); - - if (fileNum === 2) { - visits.sort((v1, v2) => v1.fileNum - v2.fileNum); - - const expectedVisits = [ - { fileNum: 1, identNum: 1 }, - { fileNum: 1, identNum: 2 }, - { fileNum: 2, identNum: 1 }, - { fileNum: 2, identNum: 2 }, - ]; - - if ( - visits.length !== expectedVisits.length || - visits.some( - (v, i) => - v.fileNum !== expectedVisits[i].fileNum || - v.identNum !== expectedVisits[i].identNum, - ) - ) { - context.report({ message: `Unexpected visits: ${JSON.stringify(visits)}`, node: SPAN }); - } - } - }, - }; - }, -}); - -// Tests that `before` hook returning `false` disables visiting AST for the file. -const createOnceBeforeFalseRule = defineRule({ - createOnce(context) { - return { - before() { - context.report({ - message: "before hook:\n" + `filename: ${context.filename}`, - node: SPAN, - }); - - // Only visit AST for `files/2.js` - return context.filename.endsWith("2.js"); - }, - Identifier(node) { - context.report({ - message: `ident visit fn "${node.name}":\n` + `filename: ${context.filename}`, - node, - }); - }, - after() { - context.report({ - message: "after hook:\n" + `filename: ${context.filename}`, - node: SPAN, - }); - }, - }; - }, -}); - -// These 4 rules test that `createOnce` without `before` and `after` hooks works correctly. - -const createOnceBeforeOnlyRule = defineRule({ - createOnce(context) { return { - before() { - context.report({ - message: "before hook:\n" + `filename: ${context.filename}`, - node: SPAN, - }); - }, Identifier(node) { context.report({ message: `ident visit fn "${node.name}":\n` + `filename: ${context.filename}`, @@ -155,46 +13,7 @@ const createOnceBeforeOnlyRule = defineRule({ }, }); -const createOnceAfterOnlyRule = defineRule({ - createOnce(context) { - return { - Identifier(node) { - context.report({ - message: `ident visit fn "${node.name}":\n` + `filename: ${context.filename}`, - node, - }); - }, - after() { - context.report({ - message: "after hook:\n" + `filename: ${context.filename}`, - node: SPAN, - }); - }, - }; - }, -}); - -const createOnceHooksOnlyRule = defineRule({ - createOnce(context) { - return { - // Neither hook should be called, because no AST node visitor functions - before() { - context.report({ - message: "before hook:\n" + `filename: ${context.filename}`, - node: SPAN, - }); - }, - after() { - context.report({ - message: "after hook:\n" + `filename: ${context.filename}`, - node: SPAN, - }); - }, - }; - }, -}); - -const createOnceNoHooksRule = defineRule({ +const createOnceRule = defineRule({ createOnce(context) { return { Identifier(node) { @@ -214,10 +33,5 @@ export default { rules: { create: createRule, "create-once": createOnceRule, - "create-once-before-false": createOnceBeforeFalseRule, - "create-once-before-only": createOnceBeforeOnlyRule, - "create-once-after-only": createOnceAfterOnlyRule, - "create-once-hooks-only": createOnceHooksOnlyRule, - "create-once-no-hooks": createOnceNoHooksRule, }, }; diff --git a/apps/oxlint/test/fixtures/eslintCompat/.oxlintrc.json b/apps/oxlint/test/fixtures/eslintCompat/.oxlintrc.json new file mode 100644 index 0000000000000..71f69bdb13a72 --- /dev/null +++ b/apps/oxlint/test/fixtures/eslintCompat/.oxlintrc.json @@ -0,0 +1,13 @@ +{ + "jsPlugins": ["./plugin.ts"], + "categories": { "correctness": "off" }, + "rules": { + "eslint-compat-plugin/create": "error", + "eslint-compat-plugin/create-once": "error", + "eslint-compat-plugin/create-once-before-false": "error", + "eslint-compat-plugin/create-once-before-only": "error", + "eslint-compat-plugin/create-once-after-only": "error", + "eslint-compat-plugin/create-once-hooks-only": "error", + "eslint-compat-plugin/create-once-no-hooks": "error" + } +} diff --git a/apps/oxlint/test/fixtures/eslintCompat/eslint.config.js b/apps/oxlint/test/fixtures/eslintCompat/eslint.config.js new file mode 100644 index 0000000000000..1c2edc11d1a8c --- /dev/null +++ b/apps/oxlint/test/fixtures/eslintCompat/eslint.config.js @@ -0,0 +1,19 @@ +import plugin from "./plugin.ts"; + +export default [ + { + files: ["files/*.js"], + plugins: { + "eslint-compat-plugin": plugin, + }, + rules: { + "eslint-compat-plugin/create": "error", + "eslint-compat-plugin/create-once": "error", + "eslint-compat-plugin/create-once-before-false": "error", + "eslint-compat-plugin/create-once-before-only": "error", + "eslint-compat-plugin/create-once-after-only": "error", + "eslint-compat-plugin/create-once-hooks-only": "error", + "eslint-compat-plugin/create-once-no-hooks": "error", + }, + }, +]; diff --git a/apps/oxlint/test/fixtures/eslintCompat/eslint.snap.md b/apps/oxlint/test/fixtures/eslintCompat/eslint.snap.md new file mode 100644 index 0000000000000..d74c9e727e942 --- /dev/null +++ b/apps/oxlint/test/fixtures/eslintCompat/eslint.snap.md @@ -0,0 +1,103 @@ +# Exit code +1 + +# stdout +``` +/files/1.js + 0:1 error create body: +this === rule: true eslint-compat-plugin/create + 0:1 error before hook: +createOnce call count: 1 +this === rule: true +filename: /files/1.js eslint-compat-plugin/create-once + 0:1 error before hook: +filename: /files/1.js eslint-compat-plugin/create-once-before-false + 0:1 error before hook: +filename: /files/1.js eslint-compat-plugin/create-once-before-only + 0:1 error before hook: +filename: /files/1.js eslint-compat-plugin/create-once-hooks-only + 0:1 error after hook: +identNum: 2 +filename: /files/1.js eslint-compat-plugin/create-once + 0:1 error after hook: +filename: /files/1.js eslint-compat-plugin/create-once-after-only + 0:1 error after hook: +filename: /files/1.js eslint-compat-plugin/create-once-hooks-only + 1:5 error ident visit fn "a": +filename: /files/1.js eslint-compat-plugin/create + 1:5 error ident visit fn "a": +identNum: 1 +filename: /files/1.js eslint-compat-plugin/create-once + 1:5 error ident visit fn "a": +filename: /files/1.js eslint-compat-plugin/create-once-before-only + 1:5 error ident visit fn "a": +filename: /files/1.js eslint-compat-plugin/create-once-after-only + 1:5 error ident visit fn "a": +filename: /files/1.js eslint-compat-plugin/create-once-no-hooks + 1:8 error ident visit fn "b": +filename: /files/1.js eslint-compat-plugin/create + 1:8 error ident visit fn "b": +identNum: 2 +filename: /files/1.js eslint-compat-plugin/create-once + 1:8 error ident visit fn "b": +filename: /files/1.js eslint-compat-plugin/create-once-before-only + 1:8 error ident visit fn "b": +filename: /files/1.js eslint-compat-plugin/create-once-after-only + 1:8 error ident visit fn "b": +filename: /files/1.js eslint-compat-plugin/create-once-no-hooks + +/files/2.js + 0:1 error create body: +this === rule: true eslint-compat-plugin/create + 0:1 error before hook: +createOnce call count: 1 +this === rule: true +filename: /files/2.js eslint-compat-plugin/create-once + 0:1 error before hook: +filename: /files/2.js eslint-compat-plugin/create-once-before-false + 0:1 error before hook: +filename: /files/2.js eslint-compat-plugin/create-once-before-only + 0:1 error before hook: +filename: /files/2.js eslint-compat-plugin/create-once-hooks-only + 0:1 error after hook: +identNum: 2 +filename: /files/2.js eslint-compat-plugin/create-once + 0:1 error after hook: +filename: /files/2.js eslint-compat-plugin/create-once-before-false + 0:1 error after hook: +filename: /files/2.js eslint-compat-plugin/create-once-after-only + 0:1 error after hook: +filename: /files/2.js eslint-compat-plugin/create-once-hooks-only + 1:5 error ident visit fn "c": +filename: /files/2.js eslint-compat-plugin/create + 1:5 error ident visit fn "c": +identNum: 1 +filename: /files/2.js eslint-compat-plugin/create-once + 1:5 error ident visit fn "c": +filename: /files/2.js eslint-compat-plugin/create-once-before-false + 1:5 error ident visit fn "c": +filename: /files/2.js eslint-compat-plugin/create-once-before-only + 1:5 error ident visit fn "c": +filename: /files/2.js eslint-compat-plugin/create-once-after-only + 1:5 error ident visit fn "c": +filename: /files/2.js eslint-compat-plugin/create-once-no-hooks + 1:8 error ident visit fn "d": +filename: /files/2.js eslint-compat-plugin/create + 1:8 error ident visit fn "d": +identNum: 2 +filename: /files/2.js eslint-compat-plugin/create-once + 1:8 error ident visit fn "d": +filename: /files/2.js eslint-compat-plugin/create-once-before-false + 1:8 error ident visit fn "d": +filename: /files/2.js eslint-compat-plugin/create-once-before-only + 1:8 error ident visit fn "d": +filename: /files/2.js eslint-compat-plugin/create-once-after-only + 1:8 error ident visit fn "d": +filename: /files/2.js eslint-compat-plugin/create-once-no-hooks + +✖ 39 problems (39 errors, 0 warnings) +``` + +# stderr +``` +``` diff --git a/apps/oxlint/test/fixtures/definePlugin_and_defineRule/files/1.js b/apps/oxlint/test/fixtures/eslintCompat/files/1.js similarity index 100% rename from apps/oxlint/test/fixtures/definePlugin_and_defineRule/files/1.js rename to apps/oxlint/test/fixtures/eslintCompat/files/1.js diff --git a/apps/oxlint/test/fixtures/definePlugin_and_defineRule/files/2.js b/apps/oxlint/test/fixtures/eslintCompat/files/2.js similarity index 100% rename from apps/oxlint/test/fixtures/definePlugin_and_defineRule/files/2.js rename to apps/oxlint/test/fixtures/eslintCompat/files/2.js diff --git a/apps/oxlint/test/fixtures/definePlugin/options.json b/apps/oxlint/test/fixtures/eslintCompat/options.json similarity index 100% rename from apps/oxlint/test/fixtures/definePlugin/options.json rename to apps/oxlint/test/fixtures/eslintCompat/options.json diff --git a/apps/oxlint/test/fixtures/definePlugin_and_defineRule/output.snap.md b/apps/oxlint/test/fixtures/eslintCompat/output.snap.md similarity index 59% rename from apps/oxlint/test/fixtures/definePlugin_and_defineRule/output.snap.md rename to apps/oxlint/test/fixtures/eslintCompat/output.snap.md index b2bd81109fd52..b38ef26091092 100644 --- a/apps/oxlint/test/fixtures/definePlugin_and_defineRule/output.snap.md +++ b/apps/oxlint/test/fixtures/eslintCompat/output.snap.md @@ -3,14 +3,14 @@ # stdout ``` - x define-plugin-and-rule-plugin(create): create body: + x eslint-compat-plugin(create): create body: | this === rule: true ,-[files/1.js:1:1] 1 | let a, b; : ^ `---- - x define-plugin-and-rule-plugin(create-once): before hook: + x eslint-compat-plugin(create-once): before hook: | createOnce call count: 1 | this === rule: true | filename: /files/1.js @@ -19,7 +19,7 @@ : ^ `---- - x define-plugin-and-rule-plugin(create-once): after hook: + x eslint-compat-plugin(create-once): after hook: | identNum: 2 | filename: /files/1.js ,-[files/1.js:1:1] @@ -27,35 +27,35 @@ : ^ `---- - x define-plugin-and-rule-plugin(create-once-after-only): after hook: + x eslint-compat-plugin(create-once-after-only): after hook: | filename: /files/1.js ,-[files/1.js:1:1] 1 | let a, b; : ^ `---- - x define-plugin-and-rule-plugin(create-once-before-false): before hook: + x eslint-compat-plugin(create-once-before-false): before hook: | filename: /files/1.js ,-[files/1.js:1:1] 1 | let a, b; : ^ `---- - x define-plugin-and-rule-plugin(create-once-before-only): before hook: + x eslint-compat-plugin(create-once-before-only): before hook: | filename: /files/1.js ,-[files/1.js:1:1] 1 | let a, b; : ^ `---- - x define-plugin-and-rule-plugin(create): ident visit fn "a": + x eslint-compat-plugin(create): ident visit fn "a": | filename: /files/1.js ,-[files/1.js:1:5] 1 | let a, b; : ^ `---- - x define-plugin-and-rule-plugin(create-once): ident visit fn "a": + x eslint-compat-plugin(create-once): ident visit fn "a": | identNum: 1 | filename: /files/1.js ,-[files/1.js:1:5] @@ -63,35 +63,35 @@ : ^ `---- - x define-plugin-and-rule-plugin(create-once-after-only): ident visit fn "a": + x eslint-compat-plugin(create-once-after-only): ident visit fn "a": | filename: /files/1.js ,-[files/1.js:1:5] 1 | let a, b; : ^ `---- - x define-plugin-and-rule-plugin(create-once-before-only): ident visit fn "a": + x eslint-compat-plugin(create-once-before-only): ident visit fn "a": | filename: /files/1.js ,-[files/1.js:1:5] 1 | let a, b; : ^ `---- - x define-plugin-and-rule-plugin(create-once-no-hooks): ident visit fn "a": + x eslint-compat-plugin(create-once-no-hooks): ident visit fn "a": | filename: /files/1.js ,-[files/1.js:1:5] 1 | let a, b; : ^ `---- - x define-plugin-and-rule-plugin(create): ident visit fn "b": + x eslint-compat-plugin(create): ident visit fn "b": | filename: /files/1.js ,-[files/1.js:1:8] 1 | let a, b; : ^ `---- - x define-plugin-and-rule-plugin(create-once): ident visit fn "b": + x eslint-compat-plugin(create-once): ident visit fn "b": | identNum: 2 | filename: /files/1.js ,-[files/1.js:1:8] @@ -99,35 +99,35 @@ : ^ `---- - x define-plugin-and-rule-plugin(create-once-after-only): ident visit fn "b": + x eslint-compat-plugin(create-once-after-only): ident visit fn "b": | filename: /files/1.js ,-[files/1.js:1:8] 1 | let a, b; : ^ `---- - x define-plugin-and-rule-plugin(create-once-before-only): ident visit fn "b": + x eslint-compat-plugin(create-once-before-only): ident visit fn "b": | filename: /files/1.js ,-[files/1.js:1:8] 1 | let a, b; : ^ `---- - x define-plugin-and-rule-plugin(create-once-no-hooks): ident visit fn "b": + x eslint-compat-plugin(create-once-no-hooks): ident visit fn "b": | filename: /files/1.js ,-[files/1.js:1:8] 1 | let a, b; : ^ `---- - x define-plugin-and-rule-plugin(create): create body: + x eslint-compat-plugin(create): create body: | this === rule: true ,-[files/2.js:1:1] 1 | let c, d; : ^ `---- - x define-plugin-and-rule-plugin(create-once): before hook: + x eslint-compat-plugin(create-once): before hook: | createOnce call count: 1 | this === rule: true | filename: /files/2.js @@ -136,7 +136,7 @@ : ^ `---- - x define-plugin-and-rule-plugin(create-once): after hook: + x eslint-compat-plugin(create-once): after hook: | identNum: 2 | filename: /files/2.js ,-[files/2.js:1:1] @@ -144,42 +144,42 @@ : ^ `---- - x define-plugin-and-rule-plugin(create-once-after-only): after hook: + x eslint-compat-plugin(create-once-after-only): after hook: | filename: /files/2.js ,-[files/2.js:1:1] 1 | let c, d; : ^ `---- - x define-plugin-and-rule-plugin(create-once-before-false): before hook: + x eslint-compat-plugin(create-once-before-false): before hook: | filename: /files/2.js ,-[files/2.js:1:1] 1 | let c, d; : ^ `---- - x define-plugin-and-rule-plugin(create-once-before-false): after hook: + x eslint-compat-plugin(create-once-before-false): after hook: | filename: /files/2.js ,-[files/2.js:1:1] 1 | let c, d; : ^ `---- - x define-plugin-and-rule-plugin(create-once-before-only): before hook: + x eslint-compat-plugin(create-once-before-only): before hook: | filename: /files/2.js ,-[files/2.js:1:1] 1 | let c, d; : ^ `---- - x define-plugin-and-rule-plugin(create): ident visit fn "c": + x eslint-compat-plugin(create): ident visit fn "c": | filename: /files/2.js ,-[files/2.js:1:5] 1 | let c, d; : ^ `---- - x define-plugin-and-rule-plugin(create-once): ident visit fn "c": + x eslint-compat-plugin(create-once): ident visit fn "c": | identNum: 1 | filename: /files/2.js ,-[files/2.js:1:5] @@ -187,42 +187,42 @@ : ^ `---- - x define-plugin-and-rule-plugin(create-once-after-only): ident visit fn "c": + x eslint-compat-plugin(create-once-after-only): ident visit fn "c": | filename: /files/2.js ,-[files/2.js:1:5] 1 | let c, d; : ^ `---- - x define-plugin-and-rule-plugin(create-once-before-false): ident visit fn "c": + x eslint-compat-plugin(create-once-before-false): ident visit fn "c": | filename: /files/2.js ,-[files/2.js:1:5] 1 | let c, d; : ^ `---- - x define-plugin-and-rule-plugin(create-once-before-only): ident visit fn "c": + x eslint-compat-plugin(create-once-before-only): ident visit fn "c": | filename: /files/2.js ,-[files/2.js:1:5] 1 | let c, d; : ^ `---- - x define-plugin-and-rule-plugin(create-once-no-hooks): ident visit fn "c": + x eslint-compat-plugin(create-once-no-hooks): ident visit fn "c": | filename: /files/2.js ,-[files/2.js:1:5] 1 | let c, d; : ^ `---- - x define-plugin-and-rule-plugin(create): ident visit fn "d": + x eslint-compat-plugin(create): ident visit fn "d": | filename: /files/2.js ,-[files/2.js:1:8] 1 | let c, d; : ^ `---- - x define-plugin-and-rule-plugin(create-once): ident visit fn "d": + x eslint-compat-plugin(create-once): ident visit fn "d": | identNum: 2 | filename: /files/2.js ,-[files/2.js:1:8] @@ -230,28 +230,28 @@ : ^ `---- - x define-plugin-and-rule-plugin(create-once-after-only): ident visit fn "d": + x eslint-compat-plugin(create-once-after-only): ident visit fn "d": | filename: /files/2.js ,-[files/2.js:1:8] 1 | let c, d; : ^ `---- - x define-plugin-and-rule-plugin(create-once-before-false): ident visit fn "d": + x eslint-compat-plugin(create-once-before-false): ident visit fn "d": | filename: /files/2.js ,-[files/2.js:1:8] 1 | let c, d; : ^ `---- - x define-plugin-and-rule-plugin(create-once-before-only): ident visit fn "d": + x eslint-compat-plugin(create-once-before-only): ident visit fn "d": | filename: /files/2.js ,-[files/2.js:1:8] 1 | let c, d; : ^ `---- - x define-plugin-and-rule-plugin(create-once-no-hooks): ident visit fn "d": + x eslint-compat-plugin(create-once-no-hooks): ident visit fn "d": | filename: /files/2.js ,-[files/2.js:1:8] 1 | let c, d; diff --git a/apps/oxlint/test/fixtures/definePlugin_and_defineRule/plugin.ts b/apps/oxlint/test/fixtures/eslintCompat/plugin.ts similarity index 86% rename from apps/oxlint/test/fixtures/definePlugin_and_defineRule/plugin.ts rename to apps/oxlint/test/fixtures/eslintCompat/plugin.ts index f19290d9aef77..c6324395e94df 100644 --- a/apps/oxlint/test/fixtures/definePlugin_and_defineRule/plugin.ts +++ b/apps/oxlint/test/fixtures/eslintCompat/plugin.ts @@ -1,6 +1,6 @@ -import { definePlugin, defineRule } from "#oxlint/plugin"; +import { eslintCompatPlugin } from "#oxlint/plugin"; -import type { Node } from "#oxlint/plugin"; +import type { Node, Rule } from "#oxlint/plugin"; const SPAN: Node = { start: 0, @@ -12,26 +12,29 @@ const SPAN: Node = { }, }; -const createRule = defineRule({ +const createRule: Rule = { create(context) { - context.report({ message: `create body:\nthis === rule: ${this === createRule}`, node: SPAN }); + context.report({ + message: `create body:\n` + `this === rule: ${this === createRule}`, + node: SPAN, + }); return { Identifier(node) { context.report({ - message: `ident visit fn "${node.name}":\nfilename: ${context.filename}`, + message: `ident visit fn "${node.name}":\n` + `filename: ${context.filename}`, node, }); }, }; }, -}); +}; // This aims to test that `createOnce` is called once only, and `before` hook is called once per file. -// i.e. Oxlint calls `createOnce` directly, and not the `create` method that `defineRule` adds to the rule. +// i.e. Oxlint calls `createOnce` directly, and not the `create` method that `eslintCompatPlugin` adds to the rule. let createOnceCallCount = 0; -const createOnceRule = defineRule({ +const createOnceRule: Rule = { createOnce(context) { createOnceCallCount++; @@ -43,7 +46,7 @@ const createOnceRule = defineRule({ // Therefore, collect all visits and check them in `after` hook of the 2nd file. const visits: { fileNum: number; identNum: number }[] = []; - // `this` should be the rule object returned by `defineRule` + // `this` should be the rule object // oxlint-disable-next-line typescript-eslint/no-this-alias const topLevelThis = this; @@ -103,10 +106,10 @@ const createOnceRule = defineRule({ }, }; }, -}); +}; // Tests that `before` hook returning `false` disables visiting AST for the file. -const createOnceBeforeFalseRule = defineRule({ +const createOnceBeforeFalseRule: Rule = { createOnce(context) { return { before() { @@ -132,11 +135,11 @@ const createOnceBeforeFalseRule = defineRule({ }, }; }, -}); +}; // These 4 rules test that `createOnce` without `before` and `after` hooks works correctly. -const createOnceBeforeOnlyRule = defineRule({ +const createOnceBeforeOnlyRule: Rule = { createOnce(context) { return { before() { @@ -153,9 +156,9 @@ const createOnceBeforeOnlyRule = defineRule({ }, }; }, -}); +}; -const createOnceAfterOnlyRule = defineRule({ +const createOnceAfterOnlyRule: Rule = { createOnce(context) { return { Identifier(node) { @@ -172,9 +175,9 @@ const createOnceAfterOnlyRule = defineRule({ }, }; }, -}); +}; -const createOnceHooksOnlyRule = defineRule({ +const createOnceHooksOnlyRule: Rule = { createOnce(context) { return { // Neither hook should be called, because no AST node visitor functions @@ -192,9 +195,9 @@ const createOnceHooksOnlyRule = defineRule({ }, }; }, -}); +}; -const createOnceNoHooksRule = defineRule({ +const createOnceNoHooksRule: Rule = { createOnce(context) { return { Identifier(node) { @@ -205,11 +208,11 @@ const createOnceNoHooksRule = defineRule({ }, }; }, -}); +}; -export default definePlugin({ +export default eslintCompatPlugin({ meta: { - name: "define-plugin-and-rule-plugin", + name: "eslint-compat-plugin", }, rules: { create: createRule,