Skip to content

Support for ESLint v8 #49

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 37 additions & 5 deletions .github/workflows/NodeCI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,50 @@ jobs:
run: npm install
- name: Lint
run: npm run lint
test:
runs-on: ubuntu-latest
test-for-eslint7:
name: "Test for ESLint ${{ matrix.eslint }} on ${{ matrix.node }} OS: ${{matrix.os}}"
runs-on: ${{ matrix.os }}
strategy:
matrix:
node-version: [10.x, 12.x, 13.x, 14.x, 15.x]
os: [ubuntu-latest]
node: [10, 12, 14, 16]
eslint: [7]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
- name: Use Node.js ${{ matrix.node }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
node-version: ${{ matrix.node }}
- name: Install ESLint ${{ matrix.eslint }}
run: |+
npm i -D eslint@${{ matrix.eslint }} --legacy-peer-deps
npx rimraf node_modules
if: matrix.eslint != 7
- name: Install Packages
run: npm install --legacy-peer-deps
- name: Test
run: npm test
test-for-eslint8:
name: "Test for ESLint ${{ matrix.eslint }} on ${{ matrix.node }} OS: ${{matrix.os}}"
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
eslint: [^8.0.0-0]
node: [14, 16]
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Node.js ${{ matrix.node }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node }}
- name: Install ESLint ${{ matrix.eslint }}
run: |+
npm i -D eslint@${{ matrix.eslint }} --legacy-peer-deps
npx rimraf node_modules
if: matrix.eslint != 7
- name: Install Packages
run: npm install --legacy-peer-deps
- name: Test
run: yarn test
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"svelte-eslint-parser": "^0.4.3"
},
"peerDependencies": {
"eslint": "^7.0.0",
"eslint": "^7.0.0 || ^8.0.0-0",
"svelte": "^3.37.0"
},
"peerDependenciesMeta": {
Expand Down
162 changes: 162 additions & 0 deletions tests/utils/source-code-fixer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/**
* Copy from https://github.com/eslint/eslint/blob/master/lib/linter/source-code-fixer.js
* @see https://github.com/eslint/eslint/issues/14936
*/
/**
* @fileoverview An object that caches and applies source code fixes.
* @author Nicholas C. Zakas
*/

import type { Linter } from "eslint"

// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------

// const debug = require("debug")("eslint:source-code-fixer");
type Message = {
fix?: Linter.LintMessage["fix"]
}

type HasFixMessage = Message & {
fix: NonNullable<Message["fix"]>
}

function hasFixMessage(m: Message): m is HasFixMessage {
return Boolean(m.fix)
}
// ------------------------------------------------------------------------------
// Helpers
// ------------------------------------------------------------------------------

const BOM = "\uFEFF"

/**
* Compares items in a messages array by range.
* @param {Message} a The first message.
* @param {Message} b The second message.
* @returns {int} -1 if a comes before b, 1 if a comes after b, 0 if equal.
* @private
*/
function compareMessagesByFixRange(a: HasFixMessage, b: HasFixMessage) {
return a.fix.range[0] - b.fix.range[0] || a.fix.range[1] - b.fix.range[1]
}

/**
* Compares items in a messages array by line and column.
* @param {Message} a The first message.
* @param {Message} b The second message.
* @returns {int} -1 if a comes before b, 1 if a comes after b, 0 if equal.
* @private
*/
function compareMessagesByLocation(a: any, b: any) {
return a.line - b.line || a.column - b.column
}

// ------------------------------------------------------------------------------
// Public Interface
// ------------------------------------------------------------------------------

/**
* Applies the fixes specified by the messages to the given text. Tries to be
* smart about the fixes and won't apply fixes over the same area in the text.
* @param {string} sourceText The text to apply the changes to.
* @param {Message[]} messages The array of messages reported by ESLint.
* @param {boolean|Function} [shouldFix=true] Determines whether each message should be fixed
* @returns {Object} An object containing the fixed text and any unfixed messages.
*/
export function applyFixes<M extends Message>(
sourceText: string,
messages: M[],
shouldFix: boolean | ((m: M) => boolean) = true,
): {
fixed: boolean
messages: M[]
output: string
} {
// debug("Applying fixes");

if (shouldFix === false) {
// debug("shouldFix parameter was false, not attempting fixes");
return {
fixed: false,
messages,
output: sourceText,
}
}

// clone the array
const remainingMessages = []
const fixes: (HasFixMessage & M)[] = []
const bom = sourceText.startsWith(BOM) ? BOM : ""
const text = bom ? sourceText.slice(1) : sourceText
let lastPos = Number.NEGATIVE_INFINITY
let output = bom

/**
* Try to use the 'fix' from a problem.
* @param {Message} problem The message object to apply fixes from
* @returns {boolean} Whether fix was successfully applied
*/
function attemptFix(problem: HasFixMessage) {
const fix = problem.fix
const start = fix.range[0]
const end = fix.range[1]

// Remain it as a problem if it's overlapped or it's a negative range
if (lastPos >= start || start > end) {
remainingMessages.push(problem)
return false
}

// Remove BOM.
if ((start < 0 && end >= 0) || (start === 0 && fix.text.startsWith(BOM))) {
output = ""
}

// Make output to this fix.
output += text.slice(Math.max(0, lastPos), Math.max(0, start))
output += fix.text
lastPos = end
return true
}

messages.forEach((problem) => {
if (hasFixMessage(problem)) {
fixes.push(problem)
} else {
remainingMessages.push(problem)
}
})

if (fixes.length) {
// debug("Found fixes to apply");
let fixesWereApplied = false

for (const problem of fixes.sort(compareMessagesByFixRange)) {
if (typeof shouldFix !== "function" || shouldFix(problem)) {
attemptFix(problem)

// The only time attemptFix will fail is if a previous fix was
// applied which conflicts with it. So we can mark this as true.
fixesWereApplied = true
} else {
remainingMessages.push(problem)
}
}
output += text.slice(Math.max(0, lastPos))

return {
fixed: fixesWereApplied,
messages: remainingMessages.sort(compareMessagesByLocation),
output,
}
}

// debug("No fixes to apply");
return {
fixed: false,
messages,
output: bom + text,
}
}
5 changes: 2 additions & 3 deletions tests/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ import fs from "fs"
import path from "path"
import type { RuleTester } from "eslint"
import { Linter } from "eslint"
// @ts-expect-error for test
import { SourceCodeFixer } from "eslint/lib/linter"
import * as svelteESLintParser from "svelte-eslint-parser"
// eslint-disable-next-line @typescript-eslint/no-require-imports -- tests
import plugin = require("../../src/index")
import { applyFixes } from "./source-code-fixer"

/**
* Prevents leading spaces in a multiline template literal from appearing in the resulting string
Expand Down Expand Up @@ -208,7 +207,7 @@ function writeFixtures(
}

if (force || !exists(outputFile)) {
const output = SourceCodeFixer.applyFixes(config.code, result).output
const output = applyFixes(config.code, result).output

if (plugin.rules[ruleName].meta.fixable != null) {
fs.writeFileSync(outputFile, output, "utf8")
Expand Down