123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299 |
- 'use strict'
- const utils = require('../utils')
- const STYLE_OPTIONS = [
- 'script-setup',
- 'composition',
- 'composition-vue2',
- 'options'
- ]
- function parseOptions(options) {
-
- const opts = { allowsSFC: {}, allowsOther: {} }
-
- const preferOptions = options[0] || ['script-setup', 'composition']
- for (const prefer of preferOptions) {
- if (prefer === 'script-setup') {
- opts.allowsSFC.scriptSetup = true
- } else if (prefer === 'composition') {
- opts.allowsSFC.composition = true
- opts.allowsOther.composition = true
- } else if (prefer === 'composition-vue2') {
- opts.allowsSFC.compositionVue2 = true
- opts.allowsOther.compositionVue2 = true
- } else if (prefer === 'options') {
- opts.allowsSFC.options = true
- opts.allowsOther.options = true
- }
- }
- if (
- !opts.allowsOther.composition &&
- !opts.allowsOther.compositionVue2 &&
- !opts.allowsOther.options
- ) {
- opts.allowsOther.composition = true
- opts.allowsOther.compositionVue2 = true
- opts.allowsOther.options = true
- }
- return opts
- }
- const OPTIONS_API_OPTIONS = new Set([
- 'mixins',
- 'extends',
-
- 'data',
- 'computed',
- 'methods',
- 'watch',
- 'provide',
- 'inject',
-
- 'beforeCreate',
- 'created',
- 'beforeMount',
- 'mounted',
- 'beforeUpdate',
- 'updated',
- 'activated',
- 'deactivated',
- 'beforeDestroy',
- 'beforeUnmount',
- 'destroyed',
- 'unmounted',
- 'render',
- 'renderTracked',
- 'renderTriggered',
- 'errorCaptured',
-
- 'expose'
- ])
- const COMPOSITION_API_OPTIONS = new Set(['setup'])
- const COMPOSITION_API_VUE2_OPTIONS = new Set([
- 'setup',
- 'render',
- 'renderTracked',
- 'renderTriggered'
- ])
- const LIFECYCLE_HOOK_OPTIONS = new Set([
- 'beforeCreate',
- 'created',
- 'beforeMount',
- 'mounted',
- 'beforeUpdate',
- 'updated',
- 'activated',
- 'deactivated',
- 'beforeDestroy',
- 'beforeUnmount',
- 'destroyed',
- 'unmounted',
- 'renderTracked',
- 'renderTriggered',
- 'errorCaptured'
- ])
- function buildAllowedPhrase(allowsOpt) {
- const phrases = []
- if (allowsOpt.scriptSetup) {
- phrases.push('`<script setup>`')
- }
- if (allowsOpt.composition) {
- phrases.push('Composition API')
- }
- if (allowsOpt.compositionVue2) {
- phrases.push('Composition API (Vue 2)')
- }
- if (allowsOpt.options) {
- phrases.push('Options API')
- }
- return phrases.length > 2
- ? `${phrases.slice(0, -1).join(',')} or ${phrases.slice(-1)[0]}`
- : phrases.join(' or ')
- }
- function isPreferScriptSetup(allowsOpt) {
- if (
- !allowsOpt.scriptSetup ||
- allowsOpt.composition ||
- allowsOpt.compositionVue2 ||
- allowsOpt.options
- ) {
- return false
- }
- return true
- }
- function buildOptionPhrase(name) {
- return LIFECYCLE_HOOK_OPTIONS.has(name)
- ? `\`${name}\` lifecycle hook`
- : name === 'setup' || name === 'render'
- ? `\`${name}\` function`
- : `\`${name}\` option`
- }
- module.exports = {
- meta: {
- type: 'suggestion',
- docs: {
- description: 'enforce component API style',
- categories: undefined,
- url: 'https://eslint.vuejs.org/rules/component-api-style.html'
- },
- fixable: null,
- schema: [
- {
- type: 'array',
- items: {
- enum: STYLE_OPTIONS,
- uniqueItems: true,
- additionalItems: false
- },
- minItems: 1
- }
- ],
- messages: {
- disallowScriptSetup:
- '`<script setup>` is not allowed in your project. Use {{allowedApis}} instead.',
- disallowComponentOption:
- '{{disallowedApi}} is not allowed in your project. {{optionPhrase}} is part of the {{disallowedApi}}. Use {{allowedApis}} instead.',
- disallowComponentOptionPreferScriptSetup:
- '{{disallowedApi}} is not allowed in your project. Use `<script setup>` instead.'
- }
- },
-
- create(context) {
- const options = parseOptions(context.options)
- return utils.compositingVisitors(
- {
- Program() {
- if (options.allowsSFC.scriptSetup) {
- return
- }
- const scriptSetup = utils.getScriptSetupElement(context)
- if (scriptSetup) {
- context.report({
- node: scriptSetup.startTag,
- messageId: 'disallowScriptSetup',
- data: {
- allowedApis: buildAllowedPhrase(options.allowsSFC)
- }
- })
- }
- }
- },
- utils.defineVueVisitor(context, {
- onVueObjectEnter(node) {
- const allows = utils.isSFCObject(context, node)
- ? options.allowsSFC
- : options.allowsOther
- if (
- (allows.composition || allows.compositionVue2) &&
- allows.options
- ) {
- return
- }
- const apis = [
- {
- allow: allows.composition,
- options: COMPOSITION_API_OPTIONS,
- apiName: 'Composition API'
- },
- {
- allow: allows.options,
- options: OPTIONS_API_OPTIONS,
- apiName: 'Options API'
- },
- {
- allow: allows.compositionVue2,
- options: COMPOSITION_API_VUE2_OPTIONS,
- apiName: 'Composition API (Vue 2)'
- }
- ]
- for (const prop of node.properties) {
- if (prop.type !== 'Property') {
- continue
- }
- const name = utils.getStaticPropertyName(prop)
- if (!name) {
- continue
- }
- const disallowApi =
- !apis.some((api) => api.allow && api.options.has(name)) &&
- apis.find((api) => !api.allow && api.options.has(name))
- if (disallowApi) {
- context.report({
- node: prop.key,
- messageId: isPreferScriptSetup(allows)
- ? 'disallowComponentOptionPreferScriptSetup'
- : 'disallowComponentOption',
- data: {
- disallowedApi: disallowApi.apiName,
- optionPhrase: buildOptionPhrase(name),
- allowedApis: buildAllowedPhrase(allows)
- }
- })
- }
- }
- }
- })
- )
- }
- }
|