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

Improve compatibility with ESLint v9 (take2) #2338

Merged
merged 2 commits into from
Dec 1, 2023
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
4 changes: 3 additions & 1 deletion lib/rules/jsx-uses-vars.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ SOFTWARE.
*/
'use strict'

const utils = require('../utils')

module.exports = {
// eslint-disable-next-line eslint-plugin/prefer-message-ids
meta: {
Expand Down Expand Up @@ -63,7 +65,7 @@ module.exports = {
return
}

context.markVariableAsUsed(name)
utils.markVariableAsUsed(context, name, node)
}
}
}
Expand Down
20 changes: 13 additions & 7 deletions lib/rules/script-setup-uses-vars.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,10 @@ module.exports = {
if (!utils.isScriptSetup(context)) {
return {}
}
const sourceCode = context.getSourceCode()
/** @type {Set<string>} */
const scriptVariableNames = new Set()
const globalScope = context.getSourceCode().scopeManager.globalScope
const globalScope = sourceCode.scopeManager.globalScope
if (globalScope) {
for (const variable of globalScope.variables) {
scriptVariableNames.add(variable.name)
Expand All @@ -54,23 +55,28 @@ module.exports = {
}
}

/** @param {string} name */
function markVariableAsUsed(name) {
utils.markVariableAsUsed(context, name, sourceCode.ast)
}

/**
* @see https://github.com/vuejs/vue-next/blob/2749c15170ad4913e6530a257db485d4e7ed2283/packages/compiler-core/src/transforms/transformElement.ts#L333
* @param {string} name
*/
function markSetupReferenceVariableAsUsed(name) {
if (scriptVariableNames.has(name)) {
context.markVariableAsUsed(name)
markVariableAsUsed(name)
return true
}
const camelName = camelize(name)
if (scriptVariableNames.has(camelName)) {
context.markVariableAsUsed(camelName)
markVariableAsUsed(camelName)
return true
}
const pascalName = casing.capitalize(camelName)
if (scriptVariableNames.has(pascalName)) {
context.markVariableAsUsed(pascalName)
markVariableAsUsed(pascalName)
return true
}
return false
Expand All @@ -83,7 +89,7 @@ module.exports = {
for (const ref of node.references.filter(
(ref) => ref.variable == null
)) {
context.markVariableAsUsed(ref.id.name)
markVariableAsUsed(ref.id.name)
}
},
VElement(node) {
Expand Down Expand Up @@ -115,7 +121,7 @@ module.exports = {
/** @param {VAttribute} node */
'VAttribute[directive=false]'(node) {
if (node.key.name === 'ref' && node.value) {
context.markVariableAsUsed(node.value.value)
markVariableAsUsed(node.value.value)
}
}
},
Expand All @@ -124,7 +130,7 @@ module.exports = {
const styleVars = getStyleVariablesContext(context)
if (styleVars) {
for (const ref of styleVars.references) {
context.markVariableAsUsed(ref.id.name)
markVariableAsUsed(ref.id.name)
}
}
}
Expand Down
43 changes: 35 additions & 8 deletions lib/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -245,10 +245,12 @@ function wrapContextToOverrideTokenMethods(context, tokenStore, options) {
tokenStore
)

/** @type {WeakMap<ASTNode, import('eslint').Scope.ScopeManager>} */
const containerScopes = new WeakMap()

/**
* @param {ASTNode} node
* @returns {import('eslint').Scope.ScopeManager|null}
*/
function getContainerScope(node) {
const exprContainer = getVExpressionContainer(node)
Expand All @@ -260,9 +262,11 @@ function wrapContextToOverrideTokenMethods(context, tokenStore, options) {
return cache
}
const programNode = eslintSourceCode.ast
const parserOptions = context.parserOptions || {}
const parserOptions =
context.languageOptions?.parserOptions ?? context.parserOptions ?? {}
const ecmaFeatures = parserOptions.ecmaFeatures || {}
const ecmaVersion = parserOptions.ecmaVersion || 2020
const ecmaVersion =
context.languageOptions?.ecmaVersion ?? parserOptions.ecmaVersion ?? 2020
const sourceType = programNode.sourceType
try {
const eslintScope = createRequire(require.resolve('eslint'))(
Expand Down Expand Up @@ -297,7 +301,6 @@ function wrapContextToOverrideTokenMethods(context, tokenStore, options) {
getSourceCode() {
return sourceCode
},
// @ts-expect-error -- Added in ESLint v8.40
get sourceCode() {
return sourceCode
},
Expand All @@ -310,11 +313,11 @@ function wrapContextToOverrideTokenMethods(context, tokenStore, options) {
*/
function getDeclaredVariables(node) {
const scope = getContainerScope(node)
if (scope) {
return scope.getDeclaredVariables(node)
}

return context.getDeclaredVariables(node)
return (
scope?.getDeclaredVariables?.(node) ??
context.getDeclaredVariables?.(node) ??
[]
)
}
}

Expand Down Expand Up @@ -1939,6 +1942,10 @@ module.exports = {
withinTypeNode,
findVariableByIdentifier,
getScope,
/**
* Marks a variable with the given name in the current scope as used. This affects the no-unused-vars rule.
*/
markVariableAsUsed,
/**
* Checks whether the given node is in export default.
* @param {ASTNode} node
Expand Down Expand Up @@ -2562,6 +2569,26 @@ function isTypeScriptFile(path) {
return path.endsWith('.ts') || path.endsWith('.tsx') || path.endsWith('.mts')
}

// ------------------------------------------------------------------------------
// ESLint Helpers
// ------------------------------------------------------------------------------
/**
* Marks a variable with the given name in the current scope as used. This affects the no-unused-vars rule.
* @param {RuleContext} context
* @param {string} name
* @param {ASTNode} node The node to get the current scope.
*/
function markVariableAsUsed(context, name, node) {
const sourceCode = context.getSourceCode()
if (sourceCode.markVariableAsUsed) {
sourceCode.markVariableAsUsed(name, node)
} else {
// This function does not use the given node, but the currently visited node.
// If we need to determine the scope of a given node, we need to implement it yourself.
context.markVariableAsUsed?.(name)
}
}

// ------------------------------------------------------------------------------
// Vue Helpers
// ------------------------------------------------------------------------------
Expand Down
17 changes: 17 additions & 0 deletions tests/lib/rules/jsx-uses-vars.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,23 @@ const ruleTester = new RuleTester({
const linter = ruleTester.linter || eslint.linter
linter.defineRule('jsx-uses-vars', rule)

ruleTester.run('jsx-uses-vars', rule, {
// Visually check that there are no warnings in the console.
valid: [
`
import SomeComponent from './SomeComponent.jsx';
export default {
render () {
return (
<SomeComponent msg="Hello world"></SomeComponent>
)
},
};
`
],
invalid: []
})

describe('jsx-uses-vars', () => {
ruleTester.run('no-unused-vars', ruleNoUnusedVars, {
valid: [
Expand Down
15 changes: 15 additions & 0 deletions tests/lib/rules/script-setup-uses-vars.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,21 @@ const ruleTester = new RuleTester({
const linter = ruleTester.linter || eslint.linter
linter.defineRule('script-setup-uses-vars', rule)

ruleTester.run('script-setup-uses-vars', rule, {
// Visually check that there are no warnings in the console.
valid: [
`
<script setup>
import Foo from './Foo.vue'
</script>

<template>
<Foo />
</template>
`
],
invalid: []
})
describe('script-setup-uses-vars', () => {
ruleTester.run('no-unused-vars', ruleNoUnusedVars, {
valid: [
Expand Down
40 changes: 30 additions & 10 deletions typings/eslint/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ export namespace Scope {
scopes: Scope[]
globalScope: Scope | null
acquire(node: VAST.ESNode | VAST.Program, inner?: boolean): Scope | null
getDeclaredVariables(node: VAST.ESNode): Variable[]
/** @since ESLint v8.38.0 */
getDeclaredVariables?(node: VAST.ESNode): Variable[]
}
interface Scope {
type:
Expand Down Expand Up @@ -230,6 +231,11 @@ export class SourceCode /*extends ESLintSourceCode*/ {
getCommentsBefore(nodeOrToken: VNODE.HasLocation): VNODE.Comment[]
getCommentsAfter(nodeOrToken: VNODE.HasLocation): VNODE.Comment[]
getCommentsInside(node: VNODE.HasLocation): VNODE.Comment[]

/** @since ESLint v8.39.0 */
markVariableAsUsed?(name: string, node?: VNODE.HasLocation): void
/** @since ESLint v8.37.0 */
getScope?(node: VNODE.HasLocation): Scope.Scope
}
export namespace SourceCode {
interface Config {
Expand Down Expand Up @@ -317,21 +323,35 @@ export namespace Rule {
id: string
options: ESLintRule.RuleContext['options']
settings: { [name: string]: any }
parserPath: string
parserOptions: any
parserServices: parserServices.ParserServices

getAncestors(): VAST.ESNode[]

getDeclaredVariables(node: VAST.ESNode): Scope.Variable[]
/** @deprecated removed in ESLint v10? */
parserPath?: string
/** @deprecated removed in ESLint v10? */
parserOptions?: ESLintLinter.ParserOptions
/** For flat config */
languageOptions?: ESLintLinter.FlatConfig['languageOptions']
/** @deprecated removed in ESLint v9 */
parserServices?: parserServices.ParserServices

/** @deprecated removed in ESLint v9 */
getAncestors?(): VAST.ESNode[]
/** @deprecated removed in ESLint v9 */
getDeclaredVariables?(node: VAST.ESNode): Scope.Variable[]
getFilename(): string
getScope(): Scope.Scope
/** @since ESLint v8.40.0 */
filename?: string
/** @deprecated removed in ESLint v9 */
getScope?(): Scope.Scope
getSourceCode(): SourceCode
markVariableAsUsed(name: string): boolean
/** @since ESLint v8.40.0 */
sourceCode?: SourceCode
/** @deprecated removed in ESLint v9 */
markVariableAsUsed?(name: string): boolean
report(descriptor: ReportDescriptor): void

// eslint@6 does not have this method.
getCwd?: () => string
/** @since ESLint v8.40.0 */
cwd?: string
}

type ReportDescriptor =
Expand Down
Loading