Skip to content

Commit

Permalink
Improve compatibility with ESLint v9 (take2) (#2338)
Browse files Browse the repository at this point in the history
  • Loading branch information
ota-meshi authored Dec 1, 2023
1 parent 0b2edb7 commit 77af4b4
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 26 deletions.
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

0 comments on commit 77af4b4

Please sign in to comment.