Skip to content
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

feat: support ESLint v9 #2996

Draft
wants to merge 17 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 13 additions & 0 deletions .github/workflows/node-4+.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ jobs:
- macos-latest
node-version: ${{ fromJson(needs.matrix.outputs.latest) }}
eslint:
- 9
- 8
- 7
- 6
Expand Down Expand Up @@ -63,6 +64,18 @@ jobs:
env:
TS_PARSER: 2
exclude:
- node-version: 16
eslint: 9
- node-version: 15
eslint: 9
- node-version: 13
eslint: 9
- node-version: 11
eslint: 9
- node-version: 10
eslint: 9
- node-version: 9
eslint: 9
- node-version: 15
eslint: 8
- node-version: 13
Expand Down
62 changes: 62 additions & 0 deletions src/context.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
function getFilename(context) {
if ('filename' in context) {
ljharb marked this conversation as resolved.
Show resolved Hide resolved
return context.filename;
}

return context.getFilename();
}

function getPhysicalFilename(context) {
if (context.getPhysicalFilename) {
return context.getPhysicalFilename();
}

return getFilename(context);
}

function getSourceCode(context) {
if ('sourceCode' in context) {
return context.sourceCode;
}

return context.getSourceCode();
}

function getScope(context, node) {
const sourceCode = getSourceCode(context);

if (sourceCode && sourceCode.getScope) {
return sourceCode.getScope(node);
}

return context.getScope();
}

function getAncestors(context, node) {
const sourceCode = getSourceCode(context);

if (sourceCode && sourceCode.getAncestors) {
return sourceCode.getAncestors(node);
}

return context.getAncestors();
}

function getDeclaredVariables(context, node) {
const sourceCode = getSourceCode(context);

if (sourceCode && sourceCode.getDeclaredVariables) {
return sourceCode.getDeclaredVariables(node);
}

return context.getDeclaredVariables(node);
}

module.exports = {
getFilename,
getPhysicalFilename,
getSourceCode,
getScope,
getAncestors,
getDeclaredVariables,
};
3 changes: 2 additions & 1 deletion src/core/packagePath.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { dirname } from 'path';
import pkgUp from 'eslint-module-utils/pkgUp';
import readPkgUp from 'eslint-module-utils/readPkgUp';
import { getPhysicalFilename } from '../context';

export function getFilePackagePath(filePath) {
const fp = pkgUp({ cwd: filePath });
return dirname(fp);
}

export function getContextPackagePath(context) {
return getFilePackagePath(context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename());
return getFilePackagePath(getPhysicalFilename(context));
}

export function getFilePackageName(filePath) {
Expand Down
6 changes: 4 additions & 2 deletions src/importDeclaration.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export default function importDeclaration(context) {
const ancestors = context.getAncestors();
import { getAncestors } from './context';

export default function importDeclaration(context, node) {
const ancestors = getAncestors(context, node);
return ancestors[ancestors.length - 1];
}
3 changes: 2 additions & 1 deletion src/rules/consistent-type-specifier-style.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import docsUrl from '../docsUrl';
import { getSourceCode } from '../context';

function isComma(token) {
return token.type === 'Punctuator' && token.value === ',';
Expand Down Expand Up @@ -55,7 +56,7 @@ module.exports = {
},

create(context) {
const sourceCode = context.getSourceCode();
const sourceCode = getSourceCode(context);

if (context.options[0] === 'prefer-inline') {
return {
Expand Down
3 changes: 2 additions & 1 deletion src/rules/dynamic-import-chunkname.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import vm from 'vm';
import docsUrl from '../docsUrl';
import { getSourceCode } from '../context';

module.exports = {
meta: {
Expand Down Expand Up @@ -40,7 +41,7 @@ module.exports = {
const chunkSubstrRegex = new RegExp(chunkSubstrFormat);

function run(node, arg) {
const sourceCode = context.getSourceCode();
const sourceCode = getSourceCode(context);
const leadingComments = sourceCode.getCommentsBefore
? sourceCode.getCommentsBefore(arg) // This method is available in ESLint >= 4.
: sourceCode.getComments(arg).leading; // This method is deprecated in ESLint 7.
Expand Down
5 changes: 3 additions & 2 deletions src/rules/first.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import docsUrl from '../docsUrl';
import { getDeclaredVariables, getSourceCode } from '../context';

function getImportValue(node) {
return node.type === 'ImportDeclaration'
Expand Down Expand Up @@ -38,7 +39,7 @@ module.exports = {
}
const absoluteFirst = context.options[0] === 'absolute-first';
const message = 'Import in body of module; reorder to top.';
const sourceCode = context.getSourceCode();
const sourceCode = getSourceCode(context);
const originSourceCode = sourceCode.getText();
let nonImportCount = 0;
let anyExpressions = false;
Expand Down Expand Up @@ -66,7 +67,7 @@ module.exports = {
}
}
if (nonImportCount > 0) {
for (const variable of context.getDeclaredVariables(node)) {
for (const variable of getDeclaredVariables(context, node)) {
if (!shouldSort) { break; }
const references = variable.references;
if (references.length) {
Expand Down
5 changes: 3 additions & 2 deletions src/rules/named.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as path from 'path';
import ExportMapBuilder from '../exportMap/builder';
import docsUrl from '../docsUrl';
import { getFilename, getPhysicalFilename } from '../context';

module.exports = {
meta: {
Expand Down Expand Up @@ -67,7 +68,7 @@ module.exports = {
if (!deepLookup.found) {
if (deepLookup.path.length > 1) {
const deepPath = deepLookup.path
.map((i) => path.relative(path.dirname(context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename()), i.path))
.map((i) => path.relative(path.dirname(getPhysicalFilename(context)), i.path))
.join(' -> ');

context.report(im[key], `${name} not found via ${deepPath}`);
Expand Down Expand Up @@ -121,7 +122,7 @@ module.exports = {
if (!deepLookup.found) {
if (deepLookup.path.length > 1) {
const deepPath = deepLookup.path
.map((i) => path.relative(path.dirname(context.getFilename()), i.path))
.map((i) => path.relative(path.dirname(getFilename(context)), i.path))
.join(' -> ');

context.report(im.key, `${im.key.name} not found via ${deepPath}`);
Expand Down
2 changes: 1 addition & 1 deletion src/rules/namespace.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ module.exports = {

// same as above, but does not add names to local map
ExportNamespaceSpecifier(namespace) {
const declaration = importDeclaration(context);
const declaration = importDeclaration(context, namespace);

const imports = ExportMapBuilder.get(declaration.source.value, context);
if (imports == null) { return null; }
Expand Down
10 changes: 7 additions & 3 deletions src/rules/newline-after-import.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import isStaticRequire from '../core/staticRequire';
import docsUrl from '../docsUrl';

import debug from 'debug';
import { getPhysicalFilename, getScope } from '../context';

const log = debug('eslint-plugin-import:rules:newline-after-import');

//------------------------------------------------------------------------------
Expand Down Expand Up @@ -193,11 +195,13 @@ module.exports = {
}
},
'Program:exit'() {
log('exit processing for', context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename());
const scopeBody = getScopeBody(context.getScope());
log('got scope:', scopeBody);
log('exit processing for', getPhysicalFilename(context));

requireCalls.forEach((node, index) => {
// todo: this probably isn't correct...
const scopeBody = getScopeBody(getScope(context, node));
log('got scope:', scopeBody);
Comment on lines +202 to +203
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the scope is for the program, i think, so it should be gotten without the node, or with the entire program's node?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sweet, I'll try passing in the program node and see what happens


const nodePosition = findNodeIndexInScopeBody(scopeBody, node);
log('node position in scope:', nodePosition);

Expand Down
3 changes: 2 additions & 1 deletion src/rules/no-absolute-path.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import path from 'path';
import moduleVisitor, { makeOptionsSchema } from 'eslint-module-utils/moduleVisitor';
import { isAbsolute } from '../core/importType';
import docsUrl from '../docsUrl';
import { getPhysicalFilename } from '../context';

module.exports = {
meta: {
Expand All @@ -22,7 +23,7 @@ module.exports = {
node: source,
message: 'Do not import modules using an absolute path',
fix(fixer) {
const resolvedContext = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename();
const resolvedContext = getPhysicalFilename(context);
// node.js and web imports work with posix style paths ("/")
let relativePath = path.posix.relative(path.dirname(resolvedContext), source.value);
if (!relativePath.startsWith('.')) {
Expand Down
3 changes: 2 additions & 1 deletion src/rules/no-amd.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/

import docsUrl from '../docsUrl';
import { getScope } from '../context';

//------------------------------------------------------------------------------
// Rule Definition
Expand All @@ -23,7 +24,7 @@ module.exports = {
create(context) {
return {
CallExpression(node) {
if (context.getScope().type !== 'module') { return; }
if (getScope(context, node).type !== 'module') { return; }

if (node.callee.type !== 'Identifier') { return; }
if (node.callee.name !== 'require' && node.callee.name !== 'define') { return; }
Expand Down
5 changes: 3 additions & 2 deletions src/rules/no-commonjs.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/

import docsUrl from '../docsUrl';
import { getScope } from '../context';

const EXPORT_MESSAGE = 'Expected "export" or "export default"';
const IMPORT_MESSAGE = 'Expected "import" instead of "require()"';
Expand Down Expand Up @@ -107,7 +108,7 @@ module.exports = {

// exports.
if (node.object.name === 'exports') {
const isInScope = context.getScope()
const isInScope = getScope(context, node)
.variables
.some((variable) => variable.name === 'exports');
if (!isInScope) {
Expand All @@ -117,7 +118,7 @@ module.exports = {

},
CallExpression(call) {
if (!validateScope(context.getScope())) { return; }
if (!validateScope(getScope(context, call))) { return; }

if (call.callee.type !== 'Identifier') { return; }
if (call.callee.name !== 'require') { return; }
Expand Down
3 changes: 2 additions & 1 deletion src/rules/no-cycle.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import ExportMapBuilder from '../exportMap/builder';
import { isExternalModule } from '../core/importType';
import moduleVisitor, { makeOptionsSchema } from 'eslint-module-utils/moduleVisitor';
import docsUrl from '../docsUrl';
import { getPhysicalFilename } from '../context';

const traversed = new Set();

Expand Down Expand Up @@ -51,7 +52,7 @@ module.exports = {
},

create(context) {
const myPath = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename();
const myPath = getPhysicalFilename(context);
if (myPath === '<text>') { return {}; } // can't cycle-check a non-file

const options = context.options[0] || {};
Expand Down
5 changes: 3 additions & 2 deletions src/rules/no-default-export.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import docsUrl from '../docsUrl';
import { getSourceCode } from '../context';

module.exports = {
meta: {
Expand All @@ -22,15 +23,15 @@ module.exports = {

return {
ExportDefaultDeclaration(node) {
const { loc } = context.getSourceCode().getFirstTokens(node)[1] || {};
const { loc } = getSourceCode(context).getFirstTokens(node)[1] || {};
context.report({ node, message: preferNamed, loc });
},

ExportNamedDeclaration(node) {
node.specifiers
.filter((specifier) => (specifier.exported.name || specifier.exported.value) === 'default')
.forEach((specifier) => {
const { loc } = context.getSourceCode().getFirstTokens(node)[1] || {};
const { loc } = getSourceCode(context).getFirstTokens(node)[1] || {};
if (specifier.type === 'ExportDefaultSpecifier') {
context.report({ node, message: preferNamed, loc });
} else if (specifier.type === 'ExportSpecifier') {
Expand Down
3 changes: 2 additions & 1 deletion src/rules/no-duplicates.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import semver from 'semver';
import flatMap from 'array.prototype.flatmap';

import docsUrl from '../docsUrl';
import { getSourceCode } from '../context';

let typescriptPkg;
try {
Expand Down Expand Up @@ -248,7 +249,7 @@ function checkImports(imported, context) {
if (nodes.length > 1) {
const message = `'${module}' imported multiple times.`;
const [first, ...rest] = nodes;
const sourceCode = context.getSourceCode();
const sourceCode = getSourceCode(context);
const fix = getFix(first, rest, sourceCode, context);

context.report({
Expand Down
3 changes: 2 additions & 1 deletion src/rules/no-empty-named-blocks.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import docsUrl from '../docsUrl';
import { getSourceCode } from '../context';

function getEmptyBlockRange(tokens, index) {
const token = tokens[index];
Expand Down Expand Up @@ -72,7 +73,7 @@ module.exports = {
fix(fixer) {
// Remove the empty block and the 'from' token, leaving the import only for its side
// effects, e.g. `import 'mod'`
const sourceCode = context.getSourceCode();
const sourceCode = getSourceCode(context);
const fromToken = program.tokens.find((t) => t.value === 'from');
const importToken = program.tokens.find((t) => t.value === 'import');
const hasSpaceAfterFrom = sourceCode.isSpaceBetween(fromToken, sourceCode.getTokenAfter(fromToken));
Expand Down
5 changes: 3 additions & 2 deletions src/rules/no-extraneous-dependencies.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import moduleVisitor from 'eslint-module-utils/moduleVisitor';
import importType from '../core/importType';
import { getFilePackageName } from '../core/packagePath';
import docsUrl from '../docsUrl';
import { getPhysicalFilename } from '../context';

const depFieldCache = new Map();

Expand Down Expand Up @@ -79,7 +80,7 @@ function getDependencies(context, packageDir) {
});
} else {
const packageJsonPath = pkgUp({
cwd: context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(),
cwd: getPhysicalFilename(context),
normalize: false,
});

Expand Down Expand Up @@ -278,7 +279,7 @@ module.exports = {

create(context) {
const options = context.options[0] || {};
const filename = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename();
const filename = getPhysicalFilename(context);
const deps = getDependencies(context, options.packageDir) || extractDepFields({});

const depsOptions = {
Expand Down