Skip to content
Closed
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
8 changes: 8 additions & 0 deletions .github/workflows/release_apps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ jobs:
env:
package_path: npm/oxlint
plugins_package_path: npm/oxlint-plugins
plugin_eslint_package_path: npm/oxlint-plugin-eslint
npm_dir: npm/oxlint-release
PUBLISH_FLAGS: "--provenance --access public --no-git-checks"
steps:
Expand Down Expand Up @@ -303,12 +304,17 @@ jobs:
run: |
cp apps/oxlint/dist-pkg-plugins/* ${plugins_package_path}/

- name: Copy dist files to oxlint-plugin-eslint npm package
run: |
cp -r apps/oxlint/dist-pkg-plugin-eslint/. ${plugin_eslint_package_path}/

- run: npm install -g npm@latest # For trusted publishing support

- name: Check Publish
run: |
node .github/scripts/check-npm-packages.js "${npm_dir}/*" "${package_path}"
node .github/scripts/check-npm-packages.js "${plugins_package_path}"
node .github/scripts/check-npm-packages.js "${plugin_eslint_package_path}"

- name: Trusted Publish
run: |
Expand All @@ -319,6 +325,8 @@ jobs:
pnpm publish ${package_path}/ ${PUBLISH_FLAGS}
# Publish `@oxlint/plugins` package
pnpm publish ${plugins_package_path}/ ${PUBLISH_FLAGS}
# Publish `oxlint-plugin-eslint` package
pnpm publish ${plugin_eslint_package_path}/ ${PUBLISH_FLAGS}

build-oxfmt:
needs: check
Expand Down
2 changes: 2 additions & 0 deletions apps/oxlint/.gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
/node_modules/
/dist/
/dist-pkg-plugins/
/dist-pkg-plugin-eslint/
/src-js/generated/plugin-eslint/
*.node
14 changes: 13 additions & 1 deletion apps/oxlint/scripts/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,24 @@ import { join } from "node:path";
const oxlintDirPath = join(import.meta.dirname, ".."),
srcDirPath = join(oxlintDirPath, "src-js"),
distDirPath = join(oxlintDirPath, "dist"),
distPkgPluginsDirPath = join(oxlintDirPath, "dist-pkg-plugins");
distPkgPluginsDirPath = join(oxlintDirPath, "dist-pkg-plugins"),
distPkgPluginEslintDirPath = join(oxlintDirPath, "dist-pkg-plugin-eslint");

// Delete `dist-pkg-plugins` directory
console.log("Deleting `dist-pkg-plugins` directory...");
rmSync(distPkgPluginsDirPath, { recursive: true, force: true });

// Delete `dist-pkg-plugin-eslint` directory
console.log("Deleting `dist-pkg-plugin-eslint` directory...");
rmSync(distPkgPluginEslintDirPath, { recursive: true, force: true });

// Generate plugin-eslint files
console.log("Generating plugin-eslint files...");
execSync("node scripts/generate-plugin-eslint.ts", {
stdio: "inherit",
cwd: oxlintDirPath,
});

// Build with tsdown
console.log("Building with tsdown...");
execSync("pnpm tsdown", { stdio: "inherit", cwd: oxlintDirPath });
Expand Down
57 changes: 57 additions & 0 deletions apps/oxlint/scripts/generate-plugin-eslint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { readdirSync, mkdirSync, writeFileSync } from "node:fs";
import { join, basename } from "node:path";
import { createRequire } from "node:module";

const require = createRequire(import.meta.url);

const oxlintDirPath = join(import.meta.dirname, "..");
const eslintRulesDir = join(require.resolve("eslint/package.json"), "../lib/rules");
const generatedDirPath = join(oxlintDirPath, "src-js/generated/plugin-eslint");
const generatedRulesDirPath = join(generatedDirPath, "rules");

// Get all ESLint rule names (exclude index.js which is the registry, not a rule)
const ruleNames = readdirSync(eslintRulesDir)
.filter((f) => f.endsWith(".js") && f !== "index.js")
.map((f) => basename(f, ".js"))
.sort();

// oxlint-disable-next-line no-console
console.log(`Found ${ruleNames.length} ESLint rules`);

// Create generated directories
mkdirSync(generatedRulesDirPath, { recursive: true });

// Generate a CJS wrapper file for each rule
for (const ruleName of ruleNames) {
const content = `module.exports = require("../../../../node_modules/eslint/lib/rules/${ruleName}.js");`;
writeFileSync(join(generatedRulesDirPath, `${ruleName}.cjs`), content);
}

// Generate the plugin rules index (ESM with lazy getters)
const indexLines = [
`import { createRequire } from "node:module";`,
`const require = createRequire(import.meta.url);`,
``,
`export default {`,
];
for (const ruleName of ruleNames) {
indexLines.push(
` get ${JSON.stringify(ruleName)}() { return require("./rules/${ruleName}.cjs"); },`,
);
}
indexLines.push(`};`, ``);

writeFileSync(join(generatedDirPath, "index.ts"), indexLines.join("\n"));

// Generate the rule_names.ts file for use in tsdown config
const ruleNamesLines = [
`export default [`,
...ruleNames.map((name) => ` ${JSON.stringify(name)},`),
`] as const;`,
``,
];

writeFileSync(join(generatedDirPath, "rule_names.ts"), ruleNamesLines.join("\n"));

// oxlint-disable-next-line no-console
console.log("Generated plugin-eslint files.");
8 changes: 8 additions & 0 deletions apps/oxlint/src-js/plugin-eslint/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import rules from "../generated/plugin-eslint/index.ts";

export default {
meta: {
name: "eslint-js",
},
rules,
};
32 changes: 32 additions & 0 deletions apps/oxlint/tsdown.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import fs from "node:fs";
import { join as pathJoin, relative as pathRelative, dirname } from "node:path";
import { defineConfig } from "tsdown";
import { parseSync, Visitor } from "oxc-parser";
import ruleNames from "./src-js/generated/plugin-eslint/rule_names.ts";

import type { Plugin } from "rolldown";

Expand Down Expand Up @@ -63,6 +64,25 @@ const pluginsPkgConfig = defineConfig({
define: definedGlobals,
});

// Build entries for `oxlint-plugin-eslint` rule files.
// Each rule is a separate CJS file, lazy-loaded on demand.
const pluginEslintRulesEntries: Record<string, string> = {};
for (const ruleName of ruleNames) {
pluginEslintRulesEntries[`rules/${ruleName}`] =
`src-js/generated/plugin-eslint/rules/${ruleName}.cjs`;
}

// Base config for `oxlint-plugin-eslint` package.
// "node12" target to match `engines` field of last ESLint 8 release (8.57.1).
const pluginEslintPkgConfig = defineConfig({
...commonConfig,
outDir: "dist-pkg-plugin-eslint",
// `build.ts` deletes the directory before TSDown runs.
// This allows generating the ESM and CommonJS builds in the same directory.
clean: false,
dts: false,
});

// Plugins.
// Only remove debug assertions in release build.
const plugins = [createReplaceGlobalsPlugin()];
Expand Down Expand Up @@ -103,6 +123,18 @@ export default defineConfig([
format: "commonjs",
dts: false,
},

// `oxlint-plugin-eslint` package
{
...pluginEslintPkgConfig,
entry: { index: "src-js/plugin-eslint/index.ts" },
format: "esm",
},
{
...pluginEslintPkgConfig,
entry: pluginEslintRulesEntries,
format: "commonjs",
},
]);

/**
Expand Down
3 changes: 3 additions & 0 deletions npm/oxlint-plugin-eslint/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Changelog

All notable changes to this package will be documented in this file.
37 changes: 37 additions & 0 deletions npm/oxlint-plugin-eslint/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# oxlint-plugin-eslint

ESLint's built-in rules as an Oxlint plugin.

This package exports all of ESLint's built-in rules as a JS plugin that Oxlint users can use.

Allows using ESLint rules that Oxlint doesn't implement natively yet.

More details in [Oxlint docs](https://oxc.rs/docs/guide/usage/linter/js-plugins).

## Usage

Install the package:

```sh
npm install --save-dev oxlint-plugin-eslint
```

Add to your Oxlint config:

```json
{
"jsPlugins": ["oxlint-plugin-eslint"],
"rules": {
"eslint-js/no-restricted-syntax": [
"error",
{
"selector": "ThrowStatement > CallExpression[callee.name=/Error$/]",
"message": "Use `new` keyword when throwing an `Error`."
}
]
}
}
```

All rules are prefixed with `eslint-js/`, to distinguish them from the native implementation of many ESLint rules
in Oxlint.
33 changes: 33 additions & 0 deletions npm/oxlint-plugin-eslint/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "oxlint-plugin-eslint",
"version": "1.51.0",
"description": "ESLint's built-in rules as an Oxlint plugin",
"keywords": [
"eslint",
"linter",
"oxlint",
"plugin"
],
"homepage": "https://oxc.rs/docs/guide/usage/linter/js-plugins",
"bugs": "https://github.com/oxc-project/oxc/issues",
"license": "MIT",
"author": "Boshen and oxc contributors",
"repository": {
"type": "git",
"url": "git+https://github.com/oxc-project/oxc.git",
"directory": "npm/oxlint-plugin-eslint"
},
"funding": {
"url": "https://github.com/sponsors/Boshen"
},
"files": [
"index.js",
"rules",
"README.md"
],
"type": "module",
"main": "index.js",
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
}
1 change: 1 addition & 0 deletions oxc_release.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ versioned_files = [
"apps/oxlint/package.json",
"crates/oxc_linter/Cargo.toml",
"npm/oxlint/package.json",
"npm/oxlint-plugin-eslint/package.json",
"npm/oxlint-plugins/package.json",
]

Expand Down
6 changes: 6 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading