diff --git a/lib/rules/component-name-in-template-casing.js b/lib/rules/component-name-in-template-casing.js index 629f51209..d330f60da 100644 --- a/lib/rules/component-name-in-template-casing.js +++ b/lib/rules/component-name-in-template-casing.js @@ -122,9 +122,12 @@ module.exports = { } if ( - (!utils.isHtmlElementNode(node) && !utils.isSvgElementNode(node)) || + (!utils.isHtmlElementNode(node) && + !utils.isSvgElementNode(node) && + !utils.isMathElementNode(node)) || utils.isHtmlWellKnownElementName(node.rawName) || utils.isSvgWellKnownElementName(node.rawName) || + utils.isMathWellKnownElementName(node.rawName) || utils.isVueBuiltInElementName(node.rawName) ) { return false diff --git a/lib/rules/html-self-closing.js b/lib/rules/html-self-closing.js index 531ddfc2b..5b98d8cf7 100644 --- a/lib/rules/html-self-closing.js +++ b/lib/rules/html-self-closing.js @@ -63,7 +63,7 @@ function getElementType(node) { if (utils.isSvgElementNode(node)) { return 'SVG' } - if (utils.isMathMLElementNode(node)) { + if (utils.isMathElementNode(node)) { return 'MATH' } return 'UNKNOWN' diff --git a/lib/rules/no-deprecated-html-element-is.js b/lib/rules/no-deprecated-html-element-is.js index 7a0fd6225..5253ef2b7 100644 --- a/lib/rules/no-deprecated-html-element-is.js +++ b/lib/rules/no-deprecated-html-element-is.js @@ -27,7 +27,8 @@ module.exports = { function isValidElement(node) { return ( !utils.isHtmlWellKnownElementName(node.rawName) && - !utils.isSvgWellKnownElementName(node.rawName) + !utils.isSvgWellKnownElementName(node.rawName) && + !utils.isMathWellKnownElementName(node.rawName) ) } return utils.defineTemplateBodyVisitor(context, { diff --git a/lib/rules/no-undef-components.js b/lib/rules/no-undef-components.js index 9eb74ad05..4371add7c 100644 --- a/lib/rules/no-undef-components.js +++ b/lib/rules/no-undef-components.js @@ -149,6 +149,7 @@ module.exports = { if ( utils.isHtmlWellKnownElementName(rawName) || utils.isSvgWellKnownElementName(rawName) || + utils.isMathWellKnownElementName(rawName) || utils.isBuiltInComponentName(kebabCaseName) ) { return false @@ -177,7 +178,11 @@ module.exports = { /** @type {TemplateListener} */ const templateBodyVisitor = { VElement(node) { - if (!utils.isHtmlElementNode(node) && !utils.isSvgElementNode(node)) { + if ( + !utils.isHtmlElementNode(node) && + !utils.isSvgElementNode(node) && + !utils.isMathElementNode(node) + ) { return } verifyName(node.rawName, node.startTag) diff --git a/lib/rules/no-unused-components.js b/lib/rules/no-unused-components.js index 75b08fa14..9ba889ccc 100644 --- a/lib/rules/no-unused-components.js +++ b/lib/rules/no-unused-components.js @@ -52,9 +52,12 @@ module.exports = { /** @param {VElement} node */ VElement(node) { if ( - (!utils.isHtmlElementNode(node) && !utils.isSvgElementNode(node)) || + (!utils.isHtmlElementNode(node) && + !utils.isSvgElementNode(node) && + !utils.isMathElementNode(node)) || utils.isHtmlWellKnownElementName(node.rawName) || - utils.isSvgWellKnownElementName(node.rawName) + utils.isSvgWellKnownElementName(node.rawName) || + utils.isMathWellKnownElementName(node.rawName) ) { return } diff --git a/lib/rules/require-typed-ref.js b/lib/rules/require-typed-ref.js index 60a82432a..c05e6c829 100644 --- a/lib/rules/require-typed-ref.js +++ b/lib/rules/require-typed-ref.js @@ -44,13 +44,23 @@ module.exports = { return {} } - const scriptSetup = utils.getScriptSetupElement(context) - if ( - scriptSetup && - !utils.hasAttribute(scriptSetup, 'lang', 'ts') && - !utils.hasAttribute(scriptSetup, 'lang', 'typescript') - ) { - return {} + if (utils.isVueFile(filename)) { + const sourceCode = context.getSourceCode() + const documentFragment = + sourceCode.parserServices.getDocumentFragment && + sourceCode.parserServices.getDocumentFragment() + if (!documentFragment) { + return {} + } + const scripts = documentFragment.children.filter( + /** @returns {element is VElement} */ + (element) => utils.isVElement(element) && element.name === 'script' + ) + if ( + scripts.every((script) => !utils.hasAttribute(script, 'lang', 'ts')) + ) { + return {} + } } const defines = iterateDefineRefs( diff --git a/lib/rules/script-setup-uses-vars.js b/lib/rules/script-setup-uses-vars.js index 703d0cb9a..a1d873a6b 100644 --- a/lib/rules/script-setup-uses-vars.js +++ b/lib/rules/script-setup-uses-vars.js @@ -94,10 +94,13 @@ module.exports = { }, VElement(node) { if ( - (!utils.isHtmlElementNode(node) && !utils.isSvgElementNode(node)) || + (!utils.isHtmlElementNode(node) && + !utils.isSvgElementNode(node) && + !utils.isMathElementNode(node)) || (node.rawName === node.name && (utils.isHtmlWellKnownElementName(node.rawName) || - utils.isSvgWellKnownElementName(node.rawName))) || + utils.isSvgWellKnownElementName(node.rawName) || + utils.isMathWellKnownElementName(node.rawName))) || utils.isBuiltInComponentName(node.rawName) ) { return diff --git a/lib/utils/index.js b/lib/utils/index.js index f16bcef2c..9671c00d4 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -51,6 +51,7 @@ const { getScope } = require('./scope') const HTML_ELEMENT_NAMES = new Set(require('./html-elements.json')) const SVG_ELEMENT_NAMES = new Set(require('./svg-elements.json')) +const MATH_ELEMENT_NAMES = new Set(require('./math-elements.json')) const VOID_ELEMENT_NAMES = new Set(require('./void-elements.json')) const VUE2_BUILTIN_COMPONENT_NAMES = new Set( require('./vue2-builtin-components') @@ -948,6 +949,8 @@ module.exports = { !this.isHtmlWellKnownElementName(node.rawName)) || (this.isSvgElementNode(node) && !this.isSvgWellKnownElementName(node.rawName)) || + (this.isMathElementNode(node) && + !this.isMathWellKnownElementName(node.rawName)) || hasAttribute(node, 'is') || hasDirective(node, 'bind', 'is') || hasDirective(node, 'is') @@ -977,7 +980,7 @@ module.exports = { * @param {VElement} node The node to check. * @returns {boolean} `true` if the node is a MathML element. */ - isMathMLElementNode(node) { + isMathElementNode(node) { return node.namespace === NS.MathML }, @@ -999,6 +1002,15 @@ module.exports = { return SVG_ELEMENT_NAMES.has(name) }, + /** + * Check whether the given name is a well-known MathML element or not. + * @param {string} name The name to check. + * @returns {boolean} `true` if the name is a well-known MathML element name. + */ + isMathWellKnownElementName(name) { + return MATH_ELEMENT_NAMES.has(name) + }, + /** * Check whether the given name is a void element name or not. * @param {string} name The name to check. @@ -3121,7 +3133,7 @@ function getWithDefaultsProps(node) { for (const prop of param.properties) { if (prop.type !== 'Property') { - return {} + continue } const name = getStaticPropertyName(prop) if (name != null) { diff --git a/lib/utils/math-elements.json b/lib/utils/math-elements.json new file mode 100644 index 000000000..46ac91ac5 --- /dev/null +++ b/lib/utils/math-elements.json @@ -0,0 +1,34 @@ +[ + "math", + "maction", + "annotation", + "annotation-xml", + "menclose", + "merror", + "mfenced", + "mfrac", + "mi", + "mmultiscripts", + "mn", + "mo", + "mover", + "mpadded", + "mphantom", + "mprescripts", + "mroot", + "mrow", + "ms", + "semantics", + "mspace", + "msqrt", + "mstyle", + "msub", + "msup", + "msubsup", + "mtable", + "mtd", + "mtext", + "mtr", + "munder", + "munderover" +] diff --git a/package.json b/package.json index ee2e37c71..b57a57e83 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "eslint-plugin-vue", - "version": "9.25.0", + "version": "9.26.0", "description": "Official ESLint plugin for Vue.js", "main": "lib/index.js", "scripts": { diff --git a/tests/lib/rules/require-default-prop.js b/tests/lib/rules/require-default-prop.js index 97aea4919..834f319aa 100644 --- a/tests/lib/rules/require-default-prop.js +++ b/tests/lib/rules/require-default-prop.js @@ -329,6 +329,28 @@ ruleTester.run('require-default-prop', rule, { ...languageOptions, parserOptions: { parser: require.resolve('@typescript-eslint/parser') } } + }, + { + filename: 'test.vue', + code: ` + + `, + languageOptions: { + parser: require('vue-eslint-parser'), + ...languageOptions, + parserOptions: { parser: require.resolve('@typescript-eslint/parser') } + } } ], @@ -544,6 +566,33 @@ ruleTester.run('require-default-prop', rule, { ...languageOptions } }, + { + filename: 'test.vue', + code: ` + + `, + errors: [ + { + message: "Prop 'bar' requires default value to be set.", + line: 8 + } + ], + languageOptions: { + parser: require('vue-eslint-parser'), + ...languageOptions, + parserOptions: { parser: require.resolve('@typescript-eslint/parser') } + } + }, ...(semver.lt( require('@typescript-eslint/parser/package.json').version, '4.0.0' diff --git a/tests/lib/rules/require-typed-ref.js b/tests/lib/rules/require-typed-ref.js index 6e8797943..5b18bcb3c 100644 --- a/tests/lib/rules/require-typed-ref.js +++ b/tests/lib/rules/require-typed-ref.js @@ -86,6 +86,20 @@ tester.run('require-typed-ref', rule, { `, languageOptions: { parser: require('vue-eslint-parser') } }, + { + filename: 'test.vue', + code: ` + + `, + languageOptions: { parser: require('vue-eslint-parser') } + }, { filename: 'test.js', code: ` @@ -217,6 +231,30 @@ tester.run('require-typed-ref', rule, { ], languageOptions: { parser: require('vue-eslint-parser') } }, + { + filename: 'test.vue', + code: ` + + } + `, + errors: [ + { + messageId: 'noType', + line: 6, + column: 29, + endLine: 6, + endColumn: 34 + } + ], + languageOptions: { parser: require('vue-eslint-parser') } + }, { filename: 'test.ts', code: ` diff --git a/tools/update-vue3-export-names.js b/tools/update-vue3-export-names.js index e86d77c93..37e07e03d 100644 --- a/tools/update-vue3-export-names.js +++ b/tools/update-vue3-export-names.js @@ -158,6 +158,7 @@ function httpGet(url) { baseUrl.pathname = redirectUrl redirectUrl = String(baseUrl) } + res.destroy() resolve(httpGet(redirectUrl)) return }