From ae790c6a91815221b9dd235c4bc48ee5d23786c3 Mon Sep 17 00:00:00 2001 From: Flo Edelmann Date: Tue, 9 Jul 2024 15:13:02 +0200 Subject: [PATCH 1/3] Fix `vue/require-typed-object-prop` tests `.vue` files need script tags --- tests/lib/rules/require-typed-object-prop.js | 265 +++++++------------ 1 file changed, 96 insertions(+), 169 deletions(-) diff --git a/tests/lib/rules/require-typed-object-prop.js b/tests/lib/rules/require-typed-object-prop.js index 9f5f000a9..8f430273c 100644 --- a/tests/lib/rules/require-typed-object-prop.js +++ b/tests/lib/rules/require-typed-object-prop.js @@ -9,7 +9,12 @@ const rule = require('../../../lib/rules/require-typed-object-prop') const ruleTester = new RuleTester({ languageOptions: { - parser: require('@typescript-eslint/parser') + parser: require('vue-eslint-parser'), + ecmaVersion: 6, + sourceType: 'module', + parserOptions: { + parser: require.resolve('@typescript-eslint/parser') + } } }) @@ -19,20 +24,22 @@ ruleTester.run('require-typed-object-prop', rule, { { filename: 'test.vue', code: ` + + ` }, { filename: 'test.vue', code: ` + + ` }, { filename: 'test.vue', @@ -40,31 +47,28 @@ ruleTester.run('require-typed-object-prop', rule, { - `, - languageOptions: { - parser: require('vue-eslint-parser'), - ecmaVersion: 6, - sourceType: 'module' - } + ` }, // array props { filename: 'test.vue', code: ` + + ` }, { filename: 'test.vue', code: ` + + ` }, { filename: 'test.vue', @@ -72,31 +76,28 @@ ruleTester.run('require-typed-object-prop', rule, { - `, - languageOptions: { - parser: require('vue-eslint-parser'), - ecmaVersion: 6, - sourceType: 'module' - } + ` }, // primitive props { filename: 'test.vue', code: ` + + ` }, { filename: 'test.vue', code: ` + + ` }, { filename: 'test.vue', @@ -104,31 +105,28 @@ ruleTester.run('require-typed-object-prop', rule, { - `, - languageOptions: { - parser: require('vue-eslint-parser'), - ecmaVersion: 6, - sourceType: 'module' - } + ` }, // union { filename: 'test.vue', code: ` + + ` }, { filename: 'test.vue', code: ` + + ` }, { filename: 'test.vue', @@ -136,31 +134,28 @@ ruleTester.run('require-typed-object-prop', rule, { - `, - languageOptions: { - parser: require('vue-eslint-parser'), - ecmaVersion: 6, - sourceType: 'module' - } + ` }, // function { filename: 'test.vue', code: ` + + ` }, { filename: 'test.vue', code: ` + + ` }, { filename: 'test.vue', @@ -168,40 +163,38 @@ ruleTester.run('require-typed-object-prop', rule, { - `, - languageOptions: { - parser: require('vue-eslint-parser'), - ecmaVersion: 6, - sourceType: 'module' - } + ` }, // typed object { filename: 'test.vue', code: ` + + ` }, { filename: 'test.vue', code: ` + + ` }, { filename: 'test.vue', code: ` + + ` }, { filename: 'test.vue', @@ -209,42 +202,28 @@ ruleTester.run('require-typed-object-prop', rule, { - `, - languageOptions: { - parser: require('vue-eslint-parser'), - ecmaVersion: 6, - sourceType: 'module', - parserOptions: { - parser: require.resolve('@typescript-eslint/parser') - } - } + ` }, { filename: 'test.vue', code: ` + + ` }, { filename: 'test.vue', code: ` + + ` }, { filename: 'test.vue', @@ -252,15 +231,7 @@ ruleTester.run('require-typed-object-prop', rule, { - `, - languageOptions: { - parser: require('vue-eslint-parser'), - ecmaVersion: 6, - sourceType: 'module', - parserOptions: { - parser: require.resolve('@typescript-eslint/parser') - } - } + ` }, // any { @@ -269,15 +240,7 @@ ruleTester.run('require-typed-object-prop', rule, { - `, - languageOptions: { - parser: require('vue-eslint-parser'), - ecmaVersion: 6, - sourceType: 'module', - parserOptions: { - parser: require.resolve('@typescript-eslint/parser') - } - } + ` }, { filename: 'test.vue', @@ -289,15 +252,7 @@ ruleTester.run('require-typed-object-prop', rule, { } }; - `, - languageOptions: { - parser: require('vue-eslint-parser'), - ecmaVersion: 6, - sourceType: 'module', - parserOptions: { - parser: require.resolve('@typescript-eslint/parser') - } - } + ` }, { filename: 'test.vue', @@ -309,13 +264,7 @@ ruleTester.run('require-typed-object-prop', rule, { } }); - `, - languageOptions: { - parser: require('vue-eslint-parser'), - ecmaVersion: 6, - sourceType: 'module', - parserOptions: { parser: require.resolve('@typescript-eslint/parser') } - } + ` }, // unknown { @@ -324,13 +273,7 @@ ruleTester.run('require-typed-object-prop', rule, { - `, - languageOptions: { - parser: require('vue-eslint-parser'), - ecmaVersion: 6, - sourceType: 'module', - parserOptions: { parser: require.resolve('@typescript-eslint/parser') } - } + ` }, { filename: 'test.vue', @@ -342,13 +285,7 @@ ruleTester.run('require-typed-object-prop', rule, { } }; - `, - languageOptions: { - parser: require('vue-eslint-parser'), - ecmaVersion: 6, - sourceType: 'module', - parserOptions: { parser: require.resolve('@typescript-eslint/parser') } - } + ` }, { filename: 'test.vue', @@ -360,13 +297,7 @@ ruleTester.run('require-typed-object-prop', rule, { } }); - `, - languageOptions: { - parser: require('vue-eslint-parser'), - ecmaVersion: 6, - sourceType: 'module', - parserOptions: { parser: require.resolve('@typescript-eslint/parser') } - } + ` } ], invalid: [ @@ -377,14 +308,6 @@ ruleTester.run('require-typed-object-prop', rule, { defineProps({ foo: Object }); `, - languageOptions: { - parser: require('vue-eslint-parser'), - ecmaVersion: 6, - sourceType: 'module', - parserOptions: { - parser: require.resolve('@typescript-eslint/parser') - } - }, errors: [ { messageId: 'expectedTypeAnnotation', @@ -422,14 +345,6 @@ ruleTester.run('require-typed-object-prop', rule, { defineProps({ foo: Array }); `, - languageOptions: { - parser: require('vue-eslint-parser'), - ecmaVersion: 6, - sourceType: 'module', - parserOptions: { - parser: require.resolve('@typescript-eslint/parser') - } - }, errors: [ { messageId: 'expectedTypeAnnotation', @@ -463,35 +378,40 @@ ruleTester.run('require-typed-object-prop', rule, { { filename: 'test.vue', code: ` + `, - languageOptions: { ecmaVersion: 6, sourceType: 'module' }, errors: [ { messageId: 'expectedTypeAnnotation', - line: 3, + line: 4, column: 23, - endLine: 3, + endLine: 4, endColumn: 29, suggestions: [ { messageId: 'addTypeAnnotation', data: { type: 'any' }, output: ` + ` }, { messageId: 'addTypeAnnotation', data: { type: 'unknown' }, output: ` + ` } ] @@ -501,35 +421,40 @@ ruleTester.run('require-typed-object-prop', rule, { { filename: 'test.vue', code: ` + `, - languageOptions: { ecmaVersion: 6, sourceType: 'module' }, errors: [ { messageId: 'expectedTypeAnnotation', - line: 3, + line: 4, column: 23, - endLine: 3, + endLine: 4, endColumn: 29, suggestions: [ { messageId: 'addTypeAnnotation', data: { type: 'any' }, output: ` + ` }, { messageId: 'addTypeAnnotation', data: { type: 'unknown' }, output: ` + ` } ] @@ -539,35 +464,40 @@ ruleTester.run('require-typed-object-prop', rule, { { filename: 'test.vue', code: ` + `, - languageOptions: { ecmaVersion: 6, sourceType: 'module' }, errors: [ { messageId: 'expectedTypeAnnotation', - line: 3, + line: 4, column: 31, - endLine: 3, + endLine: 4, endColumn: 37, suggestions: [ { messageId: 'addTypeAnnotation', data: { type: 'any' }, output: ` + ` }, { messageId: 'addTypeAnnotation', data: { type: 'unknown' }, output: ` + ` } ] @@ -577,35 +507,40 @@ ruleTester.run('require-typed-object-prop', rule, { { filename: 'test.vue', code: ` + `, - languageOptions: { ecmaVersion: 6, sourceType: 'module' }, errors: [ { messageId: 'expectedTypeAnnotation', - line: 3, + line: 4, column: 31, - endLine: 3, + endLine: 4, endColumn: 37, suggestions: [ { messageId: 'addTypeAnnotation', data: { type: 'any' }, output: ` + ` }, { messageId: 'addTypeAnnotation', data: { type: 'unknown' }, output: ` + ` } ] @@ -619,14 +554,6 @@ ruleTester.run('require-typed-object-prop', rule, { defineProps({ foo: { type: Object } }); `, - languageOptions: { - parser: require('vue-eslint-parser'), - ecmaVersion: 6, - sourceType: 'module', - parserOptions: { - parser: require.resolve('@typescript-eslint/parser') - } - }, errors: [ { messageId: 'expectedTypeAnnotation', From 7f1a598232f714dda667f67a63987a8e6fd3e2ff Mon Sep 17 00:00:00 2001 From: Flo Edelmann Date: Tue, 9 Jul 2024 15:15:04 +0200 Subject: [PATCH 2/3] Add test to make sure the rule works in `.ts` files --- tests/lib/rules/require-typed-object-prop.js | 38 ++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/lib/rules/require-typed-object-prop.js b/tests/lib/rules/require-typed-object-prop.js index 8f430273c..4a7e53742 100644 --- a/tests/lib/rules/require-typed-object-prop.js +++ b/tests/lib/rules/require-typed-object-prop.js @@ -583,6 +583,44 @@ ruleTester.run('require-typed-object-prop', rule, { ] } ] + }, + { + // `.ts` file + filename: 'test.ts', + code: ` + export default Vue.extend({ + props: { foo: Object } + }); + `, + errors: [ + { + messageId: 'expectedTypeAnnotation', + line: 3, + column: 23, + endLine: 3, + endColumn: 29, + suggestions: [ + { + messageId: 'addTypeAnnotation', + data: { type: 'any' }, + output: ` + export default Vue.extend({ + props: { foo: Object as PropType } + }); + ` + }, + { + messageId: 'addTypeAnnotation', + data: { type: 'unknown' }, + output: ` + export default Vue.extend({ + props: { foo: Object as PropType } + }); + ` + } + ] + } + ] } ] }) From ff97a0b2327776c73c9e4c652ef2f220a3ec6c8a Mon Sep 17 00:00:00 2001 From: Flo Edelmann Date: Tue, 9 Jul 2024 15:18:02 +0200 Subject: [PATCH 3/3] Exclude JavaScript components in `vue/require-typed-object-prop` --- lib/rules/require-typed-object-prop.js | 24 +++++++++++++++++ tests/lib/rules/require-typed-object-prop.js | 27 ++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/lib/rules/require-typed-object-prop.js b/lib/rules/require-typed-object-prop.js index ee88a0f35..eb7a278ae 100644 --- a/lib/rules/require-typed-object-prop.js +++ b/lib/rules/require-typed-object-prop.js @@ -129,6 +129,30 @@ module.exports = { }, /** @param {RuleContext} context */ create(context) { + const filename = context.getFilename() + if (!utils.isVueFile(filename) && !utils.isTypeScriptFile(filename)) { + 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 {} + } + } + return utils.compositingVisitors( utils.defineScriptSetupVisitor(context, { onDefinePropsEnter(_node, props) { diff --git a/tests/lib/rules/require-typed-object-prop.js b/tests/lib/rules/require-typed-object-prop.js index 4a7e53742..8a16fb567 100644 --- a/tests/lib/rules/require-typed-object-prop.js +++ b/tests/lib/rules/require-typed-object-prop.js @@ -298,6 +298,33 @@ ruleTester.run('require-typed-object-prop', rule, { }); ` + }, + // JavaScript components + { + filename: 'test.vue', + code: ` + + ` + }, + { + filename: 'test.vue', + code: ` + + ` + }, + { + filename: 'test.js', + code: ` + export default Vue.extend({ + props: { foo: Object } + }); + ` } ], invalid: [