Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: ota-meshi/eslint-compat-utils
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v0.1.2
Choose a base ref
...
head repository: ota-meshi/eslint-compat-utils
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v0.2.0
Choose a head ref
  • 5 commits
  • 14 files changed
  • 4 contributors

Commits on Dec 2, 2023

  1. test: add tests (#15)

    * test: add tests
    
    * test: add test
    ota-meshi authored Dec 2, 2023

    Verified

    This commit was signed with the committer’s verified signature.
    zhukovgreen zhukovgreen
    Copy the full SHA
    35131ae View commit details
  2. chore(deps): update actions/setup-node action to v4 (#13)

    Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
    renovate[bot] authored Dec 2, 2023
    Copy the full SHA
    42c9449 View commit details
  3. chore(deps): update dependency @types/node to v20 (#14)

    Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
    renovate[bot] authored Dec 2, 2023
    Copy the full SHA
    22a9d93 View commit details

Commits on Jan 15, 2024

  1. feat!: improve getESLint (#16)

    * feat!: move getESLint to `./eslint` namespace
    
    * Create gorgeous-needles-shop.md
    
    * feat: improve getESLint and add getLinter
    
    * Create perfect-pianos-kiss.md
    
    * Create sixty-buttons-pay.md
    
    * fix
    
    * add test
    
    * add test
    
    * fix
    
    * fix
    
    * fix
    
    * fix
    
    * refactor
    
    * fix
    
    * add test
    ota-meshi authored Jan 15, 2024
    Copy the full SHA
    7570045 View commit details
  2. chore: release eslint-compat-utils (#17)

    Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
    github-actions[bot] and github-actions[bot] authored Jan 15, 2024
    Copy the full SHA
    f0231fe View commit details
Showing with 553 additions and 35 deletions.
  1. +6 −6 .github/workflows/NodeCI.yml
  2. +2 −2 .github/workflows/Release.yml
  3. +1 −1 .vscode/settings.json
  4. +10 −0 CHANGELOG.md
  5. +1 −1 README.md
  6. +22 −4 package.json
  7. +71 −8 src/eslint.ts
  8. +0 −1 src/index.ts
  9. +109 −0 src/lib/convert-config.ts
  10. +38 −0 src/linter.ts
  11. +60 −0 src/rule-tester.ts
  12. +93 −0 src/v8-props.ts
  13. +54 −0 tests/src/eslint.ts
  14. +86 −12 tests/src/get-source-code.ts
12 changes: 6 additions & 6 deletions .github/workflows/NodeCI.yml
Original file line number Diff line number Diff line change
@@ -11,11 +11,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
node-version: 18
- uses: actions/setup-node@v4
- name: Install Packages
run: npm i
run: npm i -f
- name: Build
run: npm run build
- name: Lint
@@ -25,6 +23,8 @@ jobs:
strategy:
matrix:
include:
- eslint: ^9.0.0-0
node: 20
- eslint: 8
node: 20
- eslint: 8
@@ -44,11 +44,11 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node }}
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- name: Install Packages
run: npm i
run: npm i -f
- name: Install ESLint ${{ matrix.eslint }}
run: |+
npm i -D eslint@${{ matrix.eslint }} -f
4 changes: 2 additions & 2 deletions .github/workflows/Release.yml
Original file line number Diff line number Diff line change
@@ -21,9 +21,9 @@ jobs:
# This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v3
uses: actions/setup-node@v4
- name: Install Dependencies
run: npm i
run: npm i -f
- name: Create Release Pull Request or Publish to npm
id: changesets
uses: changesets/action@v1
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -11,6 +11,6 @@
],
"typescript.tsdk": "./node_modules/typescript/lib",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
"source.fixAll.eslint": "explicit"
}
}
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# eslint-compat-utils

## 0.2.0

### Minor Changes

- feat!: move `getESLint` to `./eslint` namespace ([#16](https://github.com/ota-meshi/eslint-compat-utils/pull/16))

- feat: improve getESLint ([#16](https://github.com/ota-meshi/eslint-compat-utils/pull/16))

- feat: add getLinter ([#16](https://github.com/ota-meshi/eslint-compat-utils/pull/16))

## 0.1.2

### Patch Changes
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ Provides an API for ESLint custom rules that is compatible with the latest ESLin
## Installation

```bash
npm install --save-dev eslint-compat-utils
npm install eslint-compat-utils
```

## Usage
26 changes: 22 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,31 @@
{
"name": "eslint-compat-utils",
"version": "0.1.2",
"version": "0.2.0",
"description": "Provides an API for ESLint custom rules that is compatible with the latest ESLint even when using older ESLint.",
"engines": {
"node": ">=12"
},
"exports": {
".": {
"types": "./index.d.ts",
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
},
"./eslint": {
"types": "./dist/eslint.d.ts",
"import": "./dist/eslint.mjs",
"require": "./dist/eslint.cjs"
},
"./linter": {
"types": "./dist/linter.d.ts",
"import": "./dist/linter.mjs",
"require": "./dist/linter.cjs"
},
"./rule-tester": {
"types": "./dist/rule-tester.d.ts",
"import": "./dist/rule-tester.mjs",
"require": "./dist/rule-tester.cjs"
},
"./package.json": "./package.json"
},
"main": "./dist/index.cjs",
@@ -59,11 +74,11 @@
"@svitejs/changesets-changelog-github-compact": "^1.1.0",
"@types/eslint": "^8.44.3",
"@types/mocha": "^10.0.0",
"@types/node": "^18.11.0",
"@types/node": "^20.0.0",
"@typescript-eslint/eslint-plugin": "^6.7.4",
"@typescript-eslint/parser": "^6.7.4",
"esbuild-register": "^3.5.0",
"eslint": "^8.51.0",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-json-schema-validator": "^4.6.0",
"eslint-plugin-jsonc": "^2.9.0",
@@ -75,5 +90,8 @@
"nyc": "^15.1.0",
"prettier": "^3.0.3",
"unbuild": "^2.0.0"
},
"dependencies": {
"semver": "^7.5.4"
}
}
79 changes: 71 additions & 8 deletions src/eslint.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,79 @@
import * as eslint from "eslint";
import * as semver from "semver";
import { convertConfigToRc } from "./lib/convert-config";

let cacheESLint: typeof eslint.ESLint | undefined;
/**
* Get ESLint class
*/
export function getESLint(): typeof eslint.ESLint {
return eslint.ESLint ?? getESLintClassForV6();
return (cacheESLint ??= getESLintInternal());

/** Internal */
function getESLintInternal(): typeof eslint.ESLint {
if (semver.gte(eslint.Linter.version, "9.0.0-0")) {
return (cacheESLint = eslint.ESLint);
}
return (cacheESLint = eslint.ESLint
? getESLintClassForV8()
: getESLintClassForV6());
}
}

/** Create compat ESLint class for eslint v8 */
function getESLintClassForV8(
// eslint-disable-next-line @typescript-eslint/naming-convention -- class name
BaseESLintClass = eslint.ESLint,
): typeof eslint.ESLint {
return class ESLintForV8 extends BaseESLintClass {
public static get version() {
return BaseESLintClass.version;
}

public constructor(options: any) {
super(adjustOptions(options));
}
};

/** Adjust options */
function adjustOptions(options: any) {
const {
baseConfig,
overrideConfig: originalOverrideConfig,
overrideConfigFile,
...newOptions
} = options || {};

if (baseConfig) {
newOptions.baseConfig = convertConfigToRc(baseConfig);
}
if (originalOverrideConfig) {
const { plugins, ...overrideConfig } = originalOverrideConfig;
// Remove unsupported options
delete overrideConfig.files;
delete overrideConfig.processor;

newOptions.overrideConfig = convertConfigToRc(overrideConfig);

if (plugins) {
newOptions.overrideConfig.plugins = Object.keys(plugins);
newOptions.plugins = plugins;
}
}
if (overrideConfigFile) {
if (overrideConfigFile === true) {
newOptions.useEslintrc = false;
} else {
newOptions.overrideConfigFile = overrideConfigFile;
}
}
return newOptions;
}
}

/** @returns {typeof eslint.ESLint} */
function getESLintClassForV6() {
// eslint-disable-next-line @typescript-eslint/naming-convention -- calss name
/** Create compat ESLint class for eslint v6 */
function getESLintClassForV6(): typeof eslint.ESLint {
// eslint-disable-next-line @typescript-eslint/naming-convention -- class name
const CLIEngine = (eslint as any).CLIEngine;
class ESLintForV6 {
private readonly engine: any;
@@ -31,8 +95,7 @@ function getESLintClassForV6() {
...otherOptions
} = options || {};

/** @type {eslint.CLIEngine.Options} */
const newOptions = {
const cliEngineOptions = {
fix: Boolean(fix),
reportUnusedDisableDirectives: reportUnusedDisableDirectives
? reportUnusedDisableDirectives !== "off"
@@ -52,7 +115,7 @@ function getESLintClassForV6() {
: undefined,
...overrideConfig,
};
this.engine = new CLIEngine(newOptions);
this.engine = new CLIEngine(cliEngineOptions);

for (const [name, plugin] of Object.entries(pluginsMap || {})) {
this.engine.addPlugin(name, plugin);
@@ -87,5 +150,5 @@ function getESLintClassForV6() {
}
}

return ESLintForV6;
return getESLintClassForV8(ESLintForV6 as any);
}
1 change: 0 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -2,4 +2,3 @@ export { getSourceCode } from "./get-source-code";
export { getCwd } from "./get-cwd";
export { getFilename } from "./get-filename";
export { getPhysicalFilename } from "./get-physical-filename";
export { getESLint } from "./eslint";
109 changes: 109 additions & 0 deletions src/lib/convert-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import * as eslint from "eslint";
import * as semver from "semver";
import type { LinterConfigForV8 } from "../v8-props";
import { createRequire } from "module";

/** Convert to eslintrc config from v9 config */
export function convertConfigToRc(
config: eslint.Linter.FlatConfig | eslint.Linter.FlatConfig[],
linter?: {
defineRule?: (typeof eslint.Linter)["prototype"]["defineRule"];
defineParser?: (typeof eslint.Linter)["prototype"]["defineParser"];
},
): LinterConfigForV8 {
if (Array.isArray(config)) {
throw new Error("Array config is not supported.");
}
const {
languageOptions: originalLanguageOptions,
plugins,
...newConfig
} = config;
if (originalLanguageOptions) {
const {
parser,
globals,
parserOptions,
ecmaVersion,
sourceType,
...languageOptions
} = originalLanguageOptions;
(newConfig as LinterConfigForV8).parserOptions = {
...(!ecmaVersion || ecmaVersion === "latest"
? { ecmaVersion: getLatestEcmaVersion() }
: { ecmaVersion }),
...(sourceType ? { sourceType } : { sourceType: "module" }),
...languageOptions,
...parserOptions,
...(newConfig as LinterConfigForV8).parserOptions,
};
if (globals) {
(newConfig as LinterConfigForV8).globals = {
...globals,
...(newConfig as LinterConfigForV8).globals,
};
}
if (parser && !(newConfig as LinterConfigForV8).parser) {
const parserName = getParserName(parser);
(newConfig as LinterConfigForV8).parser = parserName;
linter?.defineParser?.(parserName, parser);
}
}
if (plugins) {
for (const [pluginName, plugin] of Object.entries(plugins)) {
for (const [ruleName, rule] of Object.entries(plugin.rules || {})) {
linter?.defineRule?.(`${pluginName}/${ruleName}`, rule as any);
}
}
}
(newConfig as LinterConfigForV8).env = {
es6: true,
...(newConfig as LinterConfigForV8).env,
};
return newConfig as LinterConfigForV8;
}

/** Resolve parser name */
function getParserName(parser: any) {
const name = parser.meta?.name || parser.name;
if (name === "typescript-eslint/parser") {
return safeRequireResolve("@typescript-eslint/parser");
} else if (
name == null &&
parser === safeRequire("@typescript-eslint/parser")
)
return safeRequireResolve("@typescript-eslint/parser");
return safeRequireResolve(name);
}

/** Get module */
function safeRequire(name: string) {
try {
return createRequire(`${process.cwd()}/__placeholder__.js`)(name);
} catch {
return undefined;
}
}

/** Get module path */
function safeRequireResolve(name: string) {
try {
return createRequire(`${process.cwd()}/__placeholder__.js`).resolve(name);
} catch {
return name;
}
}

/** Get latest ecmaVersion */
function getLatestEcmaVersion() {
const eslintVersion = eslint.Linter.version;
return semver.gte(eslintVersion, "8.0.0")
? "latest"
: semver.gte(eslintVersion, "7.8.0")
? 2021
: semver.gte(eslintVersion, "6.2.0")
? 2020
: semver.gte(eslintVersion, "5.0.0")
? 2019
: 2018;
}
Loading