From 783fcd66a13464a62ff762559219ca3c1d74b28b Mon Sep 17 00:00:00 2001 From: ota-meshi Date: Wed, 10 Jan 2024 14:40:35 +0900 Subject: [PATCH 1/4] Add support for defineModel --- docs/rules/define-macros-order.md | 13 ++-- docs/rules/no-unsupported-features.md | 1 + lib/rules/define-macros-order.js | 12 ++- lib/rules/no-undef-properties.js | 20 +++++ lib/rules/no-unsupported-features.js | 3 + lib/rules/no-unused-emit-declarations.js | 11 +++ lib/rules/no-unused-properties.js | 35 ++++++++- lib/rules/require-prop-types.js | 57 +++++++++----- lib/rules/syntaxes/define-model.js | 22 ++++++ lib/utils/index.js | 53 +++++++++++++ tests/lib/rules/define-macros-order.js | 34 ++++++++- tests/lib/rules/no-undef-properties.js | 27 +++++++ .../no-unsupported-features/define-model.js | 59 +++++++++++++++ .../lib/rules/no-unused-emit-declarations.js | 26 +++++++ tests/lib/rules/no-unused-properties.js | 35 +++++++++ tests/lib/rules/require-prop-types.js | 74 +++++++++++++++++++ typings/eslint-plugin-vue/util-types/utils.ts | 13 ++++ 17 files changed, 465 insertions(+), 30 deletions(-) create mode 100644 lib/rules/syntaxes/define-model.js create mode 100644 tests/lib/rules/no-unsupported-features/define-model.js diff --git a/docs/rules/define-macros-order.md b/docs/rules/define-macros-order.md index d46fc5108..7df8f42c7 100644 --- a/docs/rules/define-macros-order.md +++ b/docs/rules/define-macros-order.md @@ -27,7 +27,7 @@ This rule reports the `defineProps` and `defineEmits` compiler macros when they } ``` -- `order` (`string[]`) ... The order of defineEmits and defineProps macros. You can also add `"defineOptions"` and `"defineSlots"`. +- `order` (`string[]`) ... The order of defineEmits and defineProps macros. You can also add `"defineOptions"`, `"defineSlots"`, and `"defineModel"`. - `defineExposeLast` (`boolean`) ... Force `defineExpose` at the end. ### `{ "order": ["defineProps", "defineEmits"] }` (default) @@ -69,14 +69,15 @@ defineEmits(/* ... */) -### `{ "order": ["defineOptions", "defineProps", "defineEmits", "defineSlots"] }` +### `{ "order": ["defineOptions", "defineModel", "defineProps", "defineEmits", "defineSlots"] }` - + ```vue ``` - + ```vue `, output: ` @@ -618,6 +628,8 @@ tester.run('define-macros-order', rule, { import Foo from 'foo' /** options */ defineOptions({}) + /** model */ + const model = defineModel() /** emits */ defineEmits(['update:foo']) /** props */ @@ -629,7 +641,13 @@ tester.run('define-macros-order', rule, { `, options: [ { - order: ['defineOptions', 'defineEmits', 'defineProps', 'defineSlots'] + order: [ + 'defineOptions', + 'defineModel', + 'defineEmits', + 'defineProps', + 'defineSlots' + ] } ], errors: [ @@ -651,12 +669,16 @@ tester.run('define-macros-order', rule, { defineEmits(['update:foo']) /** props */ const props = defineProps(['foo']) + /** model */ + const model = defineModel() `, output: ` + + `, + errors: [ + { + message: "'undef' is not defined.", + line: 14 + } + ] } ] }) diff --git a/tests/lib/rules/no-unsupported-features/define-model.js b/tests/lib/rules/no-unsupported-features/define-model.js new file mode 100644 index 000000000..0307d1f9f --- /dev/null +++ b/tests/lib/rules/no-unsupported-features/define-model.js @@ -0,0 +1,59 @@ +/** + * @author Yosuke Ota + * See LICENSE file in root directory for full license. + */ +'use strict' + +const RuleTester = require('eslint').RuleTester +const rule = require('../../../../lib/rules/no-unsupported-features') +const utils = require('./utils') + +const buildOptions = utils.optionsBuilder('define-model', '^3.3.0') +const tester = new RuleTester({ + parser: require.resolve('vue-eslint-parser'), + parserOptions: { + ecmaVersion: 2019 + } +}) + +tester.run('no-unsupported-features/define-model', rule, { + valid: [ + { + code: ` + `, + options: buildOptions({ version: '^3.4.0' }) + }, + { + code: ` + `, + options: buildOptions() + }, + { + code: ` + `, + options: buildOptions({ version: '^3.3.0', ignores: ['define-model'] }) + } + ], + invalid: [ + { + code: ` + `, + options: buildOptions(), + errors: [ + { + message: + '`defineModel()` macros are not supported until Vue.js "3.4.0".', + line: 3 + } + ] + } + ] +}) diff --git a/tests/lib/rules/no-unused-emit-declarations.js b/tests/lib/rules/no-unused-emit-declarations.js index 10bf3b33a..f3189ea82 100644 --- a/tests/lib/rules/no-unused-emit-declarations.js +++ b/tests/lib/rules/no-unused-emit-declarations.js @@ -357,6 +357,16 @@ tester.run('no-unused-emit-declarations', rule, { const change = () => emit('foo'); `, ...getTypeScriptFixtureTestOptions() + }, + { + // defineModel + filename: 'test.vue', + code: ` + + ` } ], invalid: [ @@ -718,6 +728,22 @@ tester.run('no-unused-emit-declarations', rule, { } ], ...getTypeScriptFixtureTestOptions() + }, + { + // defineModel + filename: 'test.vue', + code: ` + + `, + errors: [ + { + message: '`update:foo` is defined as emit but never used.', + line: 3 + } + ] } ] }) diff --git a/tests/lib/rules/no-unused-properties.js b/tests/lib/rules/no-unused-properties.js index f71a26519..e47f346aa 100644 --- a/tests/lib/rules/no-unused-properties.js +++ b/tests/lib/rules/no-unused-properties.js @@ -2152,6 +2152,19 @@ tester.run('no-unused-properties', rule, { }, }; ` + }, + { + // defineModel + filename: 'test.vue', + code: ` + + + ` } ], invalid: [ @@ -3811,6 +3824,28 @@ tester.run('no-unused-properties', rule, { } ], ...getTypeScriptFixtureTestOptions() + }, + + { + // defineModel + filename: 'test.vue', + code: ` + + + `, + errors: [ + { + message: "'unused' of property found, but never used.", + line: 6 + } + ] } ] }) diff --git a/tests/lib/rules/require-prop-types.js b/tests/lib/rules/require-prop-types.js index fdb91dd30..f50b4b116 100644 --- a/tests/lib/rules/require-prop-types.js +++ b/tests/lib/rules/require-prop-types.js @@ -187,6 +187,40 @@ ruleTester.run('require-prop-types', rule, { defineProps() `, ...getTypeScriptFixtureTestOptions() + }, + { + // defineModel + code: ` + + `, + parser: require.resolve('vue-eslint-parser') + }, + { + // defineModel + code: ` + + `, + parser: require.resolve('vue-eslint-parser') + }, + { + code: ` + + `, + parser: require.resolve('vue-eslint-parser'), + parserOptions: { + ecmaVersion: 6, + sourceType: 'module', + parser: require.resolve('@typescript-eslint/parser') + } } ], @@ -368,6 +402,46 @@ ruleTester.run('require-prop-types', rule, { line: 3 } ] + }, + { + // defineModel + code: ` + + `, + parser: require.resolve('vue-eslint-parser'), + errors: [ + { + message: 'Prop "modelValue" should define at least its type.', + line: 3 + }, + { + message: 'Prop "foo" should define at least its type.', + line: 4 + } + ] + }, + { + // defineModel + code: ` + + `, + parser: require.resolve('vue-eslint-parser'), + errors: [ + { + message: 'Prop "modelValue" should define at least its type.', + line: 3 + }, + { + message: 'Prop "foo" should define at least its type.', + line: 4 + } + ] } ] }) diff --git a/typings/eslint-plugin-vue/util-types/utils.ts b/typings/eslint-plugin-vue/util-types/utils.ts index 4d8384d66..3e9184262 100644 --- a/typings/eslint-plugin-vue/util-types/utils.ts +++ b/typings/eslint-plugin-vue/util-types/utils.ts @@ -46,10 +46,13 @@ export interface ScriptSetupVisitor extends ScriptSetupVisitorBase { onDefineSlotsExit?(node: CallExpression): void onDefineExposeEnter?(node: CallExpression): void onDefineExposeExit?(node: CallExpression): void + onDefineModelEnter?(node: CallExpression, model: ComponentModel): void + onDefineModelExit?(node: CallExpression, model: ComponentModel): void [query: string]: | ((node: VAST.ParamNode) => void) | ((node: CallExpression, props: ComponentProp[]) => void) | ((node: CallExpression, emits: ComponentEmit[]) => void) + | ((node: CallExpression, model: ComponentModel) => void) | undefined } @@ -187,3 +190,13 @@ export type ComponentEmit = | ComponentTypeEmit | ComponentInferTypeEmit | ComponentUnknownEmit + +export type ComponentModelName = { + modelName: string + node: Literal | null +} +export type ComponentModel = { + name: ComponentModelName + options: Expression | null + typeNode: TypeNode | null +} From bea21a78af23fe335efca0cd60f07f42092079a0 Mon Sep 17 00:00:00 2001 From: ota-meshi Date: Wed, 10 Jan 2024 15:57:15 +0900 Subject: [PATCH 2/4] fixed rules related to ref --- lib/utils/ref-object-references.js | 84 ++++++++++++++- .../ref-objects/defineModel01/result.vue | 24 +++++ .../ref-objects/defineModel01/source.vue | 24 +++++ .../ref-objects/defineModel02/result.vue | 16 +++ .../ref-objects/defineModel02/source.vue | 16 +++ tests/lib/rules/no-ref-as-operand.js | 100 ++++++++++++++++++ .../rules/no-ref-object-reactivity-loss.js | 25 +++++ tests/lib/utils/ref-object-references.js | 86 +++++++++------ .../util-types/ast/es-ast.ts | 2 +- 9 files changed, 342 insertions(+), 35 deletions(-) create mode 100644 tests/fixtures/utils/ref-object-references/ref-objects/defineModel01/result.vue create mode 100644 tests/fixtures/utils/ref-object-references/ref-objects/defineModel01/source.vue create mode 100644 tests/fixtures/utils/ref-object-references/ref-objects/defineModel02/result.vue create mode 100644 tests/fixtures/utils/ref-object-references/ref-objects/defineModel02/source.vue diff --git a/lib/utils/ref-object-references.js b/lib/utils/ref-object-references.js index 49404dad0..60bfda665 100644 --- a/lib/utils/ref-object-references.js +++ b/lib/utils/ref-object-references.js @@ -112,6 +112,54 @@ function* iterateDefineRefs(globalScope) { } } +/** + * Iterate the call expressions that defineModel() macro. + * @param {import('eslint').Scope.Scope} globalScope + * @returns {Iterable<{ node: CallExpression }>} + */ +function* iterateDefineModels(globalScope) { + for (const { identifier } of iterateMacroReferences()) { + if ( + identifier.parent.type === 'CallExpression' && + identifier.parent.callee === identifier + ) { + yield { + node: identifier.parent + } + } + } + + /** + * Iterate macro reference. + * @returns {Iterable} + */ + function* iterateMacroReferences() { + const variable = globalScope.set.get('defineModel') + if ( + variable && + variable.defs.length === 0 /* It was automatically defined. */ + ) { + yield* variable.references + } + for (const ref of globalScope.through) { + if (ref.identifier.name === 'defineModel') { + yield ref + } + } + } + const tracker = new ReferenceTracker(globalScope) + for (const { node } of tracker.iterateGlobalReferences({ + defineModel: { + [ReferenceTracker.CALL]: true + } + })) { + const expr = /** @type {CallExpression} */ (node) + yield { + node: expr + } + } +} + /** * Iterate the call expressions that define the reactive variables. * @param {import('eslint').Scope.Scope} globalScope @@ -350,6 +398,36 @@ class RefObjectReferenceExtractor { } } + /** + * @param {CallExpression} node + */ + processDefineModel(node) { + const parent = node.parent + /** @type {Pattern | null} */ + let pattern = null + if (parent.type === 'VariableDeclarator') { + pattern = parent.id + } else if ( + parent.type === 'AssignmentExpression' && + parent.operator === '=' + ) { + pattern = parent.left + } else { + return + } + + const ctx = { + method: 'defineModel', + define: node, + defineChain: [node] + } + + if (pattern.type === 'ArrayPattern' && pattern.elements[0]) { + pattern = pattern.elements[0] + } + this.processPattern(pattern, ctx) + } + /** * @param {MemberExpression | Identifier} node * @param {RefObjectReferenceContext} ctx @@ -474,9 +552,13 @@ function extractRefObjectReferences(context) { } const references = new RefObjectReferenceExtractor(context) - for (const { node, name } of iterateDefineRefs(getGlobalScope(context))) { + const globalScope = getGlobalScope(context) + for (const { node, name } of iterateDefineRefs(globalScope)) { references.processDefineRef(node, name) } + for (const { node } of iterateDefineModels(globalScope)) { + references.processDefineModel(node) + } cacheForRefObjectReferences.set(sourceCode.ast, references) diff --git a/tests/fixtures/utils/ref-object-references/ref-objects/defineModel01/result.vue b/tests/fixtures/utils/ref-object-references/ref-objects/defineModel01/result.vue new file mode 100644 index 000000000..a5d4293d8 --- /dev/null +++ b/tests/fixtures/utils/ref-object-references/ref-objects/defineModel01/result.vue @@ -0,0 +1,24 @@ + diff --git a/tests/fixtures/utils/ref-object-references/ref-objects/defineModel01/source.vue b/tests/fixtures/utils/ref-object-references/ref-objects/defineModel01/source.vue new file mode 100644 index 000000000..7cca9156c --- /dev/null +++ b/tests/fixtures/utils/ref-object-references/ref-objects/defineModel01/source.vue @@ -0,0 +1,24 @@ + diff --git a/tests/fixtures/utils/ref-object-references/ref-objects/defineModel02/result.vue b/tests/fixtures/utils/ref-object-references/ref-objects/defineModel02/result.vue new file mode 100644 index 000000000..c2903d01f --- /dev/null +++ b/tests/fixtures/utils/ref-object-references/ref-objects/defineModel02/result.vue @@ -0,0 +1,16 @@ + diff --git a/tests/fixtures/utils/ref-object-references/ref-objects/defineModel02/source.vue b/tests/fixtures/utils/ref-object-references/ref-objects/defineModel02/source.vue new file mode 100644 index 000000000..012cb9349 --- /dev/null +++ b/tests/fixtures/utils/ref-object-references/ref-objects/defineModel02/source.vue @@ -0,0 +1,16 @@ + diff --git a/tests/lib/rules/no-ref-as-operand.js b/tests/lib/rules/no-ref-as-operand.js index a932ff1c2..ef74cd4f4 100644 --- a/tests/lib/rules/no-ref-as-operand.js +++ b/tests/lib/rules/no-ref-as-operand.js @@ -164,6 +164,30 @@ tester.run('no-ref-as-operand', rule, { foo = ref(5); } + `, + ` + + `, + ` + ` ], invalid: [ @@ -719,6 +743,82 @@ tester.run('no-ref-as-operand', rule, { column: 7 } ] + }, + { + code: ` + + `, + output: ` + + `, + errors: [ + { + message: + 'Must use `.value` to read or write the value wrapped by `defineModel()`.', + line: 6 + }, + { + message: + 'Must use `.value` to read or write the value wrapped by `defineModel()`.', + line: 9 + } + ] + }, + { + code: ` + + `, + output: ` + + `, + errors: [ + { + message: + 'Must use `.value` to read or write the value wrapped by `defineModel()`.', + line: 6 + }, + { + message: + 'Must use `.value` to read or write the value wrapped by `defineModel()`.', + line: 9 + } + ] } ] }) diff --git a/tests/lib/rules/no-ref-object-reactivity-loss.js b/tests/lib/rules/no-ref-object-reactivity-loss.js index a40f8619f..ef946188b 100644 --- a/tests/lib/rules/no-ref-object-reactivity-loss.js +++ b/tests/lib/rules/no-ref-object-reactivity-loss.js @@ -308,6 +308,31 @@ tester.run('no-ref-object-reactivity-loss', rule, { } ] }, + { + code: ` + `, + parser: require.resolve('vue-eslint-parser'), + errors: [ + { + message: + 'Getting a value from the ref object in the same scope will cause the value to lose reactivity.', + line: 6 + }, + { + message: + 'Getting a value from the ref object in the same scope will cause the value to lose reactivity.', + line: 7 + } + ] + }, // Reactivity Transform { code: ` diff --git a/tests/lib/utils/ref-object-references.js b/tests/lib/utils/ref-object-references.js index 66ee89d0c..5cd473f8f 100644 --- a/tests/lib/utils/ref-object-references.js +++ b/tests/lib/utils/ref-object-references.js @@ -3,6 +3,7 @@ const fs = require('fs') const path = require('path') const assert = require('assert') +const vueESLintParser = require('vue-eslint-parser') const Linter = require('eslint').Linter @@ -18,23 +19,45 @@ const FIXTURE_ROOT = path.resolve( const REF_OBJECTS_FIXTURE_ROOT = path.resolve(FIXTURE_ROOT, 'ref-objects') const REACTIVE_VARS_FIXTURE_ROOT = path.resolve(FIXTURE_ROOT, 'reactive-vars') +/** + * @typedef {object} LoadedPattern + * @property {string} code The code to test. + * @property {string} name The name of the pattern. + * @property {string} sourceFilePath + * @property {string} resultFilePath + * @property {object} [options] + * @property {string} [options.parser] + */ /** * Load test patterns from fixtures. * - * @returns {object} The loaded patterns. + * @returns {LoadedPattern[]} The loaded patterns. */ function loadPatterns(rootDir) { return fs.readdirSync(rootDir).map((name) => { - const code0 = fs.readFileSync(path.join(rootDir, name, 'source.js'), 'utf8') - const code = code0.replace(/^/, ``) - return { code, name } + for (const [sourceFile, resultFile, options] of [ + ['source.js', 'result.js'], + ['source.vue', 'result.vue', { parser: 'vue-eslint-parser' }] + ]) { + const sourceFilePath = path.join(rootDir, name, sourceFile) + if (fs.existsSync(sourceFilePath)) { + return { + code: fs.readFileSync(sourceFilePath, 'utf8'), + name, + sourceFilePath, + resultFilePath: path.join(rootDir, name, resultFile), + options + } + } + } }) } -function extractRefs(code, extract) { +function extractRefs(code, extract, options) { const linter = new Linter() const references = [] + linter.defineParser('vue-eslint-parser', vueESLintParser) linter.defineRule('vue/extract-test', (context) => { const refs = extract(context) @@ -56,6 +79,7 @@ function extractRefs(code, extract) { const messages = linter.verify( code, { + ...options, parserOptions: { ecmaVersion: 2020, sourceType: 'module' }, rules: { 'vue/extract-test': 'error' }, globals: { @@ -81,11 +105,15 @@ function extractRefs(code, extract) { } describe('extractRefObjectReferences()', () => { - for (const { name, code } of loadPatterns(REF_OBJECTS_FIXTURE_ROOT)) { - describe(`'test/fixtures/utils/ref-object-references/ref-objects/${name}/source.vue'`, () => { + for (const { code, sourceFilePath, resultFilePath, options } of loadPatterns( + REF_OBJECTS_FIXTURE_ROOT + )) { + describe(sourceFilePath, () => { it('should to extract the references to match the expected references.', () => { /** @type {import('../../../lib/utils/ref-object-references').RefObjectReference[]} */ - const references = [...extractRefs(code, extractRefObjectReferences)] + const references = [ + ...extractRefs(code, extractRefObjectReferences, options) + ] let result = '' let start = 0 @@ -104,29 +132,26 @@ describe('extractRefObjectReferences()', () => { const actual = result - // update fixture - // fs.writeFileSync( - // path.join(REF_OBJECTS_FIXTURE_ROOT, name, 'result.js'), - // actual, - // 'utf8' - // ) - - const expected = fs.readFileSync( - path.join(REF_OBJECTS_FIXTURE_ROOT, name, 'result.js'), - 'utf8' - ) + if (!fs.existsSync(resultFilePath)) { + // update fixture + fs.writeFileSync(resultFilePath, actual, 'utf8') + } + + const expected = fs.readFileSync(resultFilePath, 'utf8') assert.strictEqual(actual, expected) }) }) } }) describe('extractReactiveVariableReferences()', () => { - for (const { name, code } of loadPatterns(REACTIVE_VARS_FIXTURE_ROOT)) { - describe(`'test/fixtures/utils/ref-object-references/reactive-vars/${name}/source.vue'`, () => { + for (const { code, sourceFilePath, resultFilePath, options } of loadPatterns( + REACTIVE_VARS_FIXTURE_ROOT + )) { + describe(sourceFilePath, () => { it('should to extract the references to match the expected references.', () => { /** @type {import('../../../lib/utils/ref-object-references').ReactiveVariableReference[]} */ const references = [ - ...extractRefs(code, extractReactiveVariableReferences) + ...extractRefs(code, extractReactiveVariableReferences, options) ] let result = '' @@ -146,17 +171,12 @@ describe('extractReactiveVariableReferences()', () => { const actual = result - // update fixture - // fs.writeFileSync( - // path.join(REACTIVE_VARS_FIXTURE_ROOT, name, 'result.js'), - // actual, - // 'utf8' - // ) - - const expected = fs.readFileSync( - path.join(REACTIVE_VARS_FIXTURE_ROOT, name, 'result.js'), - 'utf8' - ) + if (!fs.existsSync(resultFilePath)) { + // update fixture + fs.writeFileSync(resultFilePath, actual, 'utf8') + } + + const expected = fs.readFileSync(resultFilePath, 'utf8') assert.strictEqual(actual, expected) }) }) diff --git a/typings/eslint-plugin-vue/util-types/ast/es-ast.ts b/typings/eslint-plugin-vue/util-types/ast/es-ast.ts index fc3676a56..3205dc74e 100644 --- a/typings/eslint-plugin-vue/util-types/ast/es-ast.ts +++ b/typings/eslint-plugin-vue/util-types/ast/es-ast.ts @@ -641,7 +641,7 @@ export type AssignmentProperty = | AssignmentPropertyComputedName export interface ArrayPattern extends HasParentNode { type: 'ArrayPattern' - elements: Pattern[] + elements: (Pattern | null)[] } export interface RestElement extends HasParentNode { type: 'RestElement' From 56bb7594c9ef8827ca5aa3e5590004b5c29b8c9e Mon Sep 17 00:00:00 2001 From: ota-meshi Date: Wed, 10 Jan 2024 18:44:26 +0900 Subject: [PATCH 3/4] fix require-prop-types.js --- lib/rules/require-prop-types.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/lib/rules/require-prop-types.js b/lib/rules/require-prop-types.js index 9a394084a..b658ab918 100644 --- a/lib/rules/require-prop-types.js +++ b/lib/rules/require-prop-types.js @@ -74,14 +74,8 @@ module.exports = { if (prop.type !== 'object' && prop.type !== 'array') { return } - let hasType - - if (prop.type === 'array') { - hasType = false - } else { - const { value } = prop - hasType = optionHasType(value) ?? true - } + const hasType = + prop.type === 'array' ? false : optionHasType(prop.value) ?? true if (!hasType) { const { node, propName } = prop From 5064748afef8c93ddfefb9d88a9ca5dfdb7c6651 Mon Sep 17 00:00:00 2001 From: Yosuke Ota Date: Wed, 10 Jan 2024 18:45:10 +0900 Subject: [PATCH 4/4] Apply suggestions from code review Co-authored-by: Flo Edelmann --- lib/rules/no-unused-emit-declarations.js | 4 ++-- lib/rules/no-unused-properties.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/rules/no-unused-emit-declarations.js b/lib/rules/no-unused-emit-declarations.js index 5704775de..1a88d2417 100644 --- a/lib/rules/no-unused-emit-declarations.js +++ b/lib/rules/no-unused-emit-declarations.js @@ -338,8 +338,8 @@ module.exports = { node.parent.type === 'VariableDeclarator' && node.parent.init === node ) { - // If store the return of defineModel() in a variable, we can mark the 'update:modelName' event as used if use that variable. - // If that variable is unused it will already be reported by `no-unused-var` rule. + // If the return value of defineModel() is stored in a variable, we can mark the 'update:modelName' event as used if that that variable is used. + // If that variable is unused, it will already be reported by `no-unused-var` rule. emitCalls.set(`update:${model.name.modelName}`, node) } }, diff --git a/lib/rules/no-unused-properties.js b/lib/rules/no-unused-properties.js index 0a5c1fe6d..46099267f 100644 --- a/lib/rules/no-unused-properties.js +++ b/lib/rules/no-unused-properties.js @@ -467,8 +467,8 @@ module.exports = { node.parent.type === 'VariableDeclarator' && node.parent.init === node ) { - // If store the return of defineModel() in a variable, we can mark the model prop as used if use that variable. - // If that variable is unused it will already be reported by `no-unused-var` rule. + // If the return value of defineModel() is stored in a variable, we can mark the model prop as used if that that variable is used. + // If that variable is unused, it will already be reported by `no-unused-var` rule. container.propertyReferences.push( propertyReferenceExtractor.extractFromName( model.name.modelName,