You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

131 lines
3.5 KiB

/**
* @author Yosuke Ota <https://github.com/ota-meshi>
* See LICENSE file in root directory for full license.
*/
'use strict'
const utils = require('../utils')
module.exports = {
meta: {
type: 'suggestion',
docs: {
description: 'enforce use of `defineOptions` instead of default export',
categories: undefined,
url: 'https://eslint.vuejs.org/rules/prefer-define-options.html'
},
fixable: 'code',
schema: [],
messages: {
preferDefineOptions: 'Use `defineOptions` instead of default export.'
}
},
/**
* @param {RuleContext} context
* @returns {RuleListener}
*/
create(context) {
const scriptSetup = utils.getScriptSetupElement(context)
if (!scriptSetup) {
return {}
}
/** @type {CallExpression | null} */
let defineOptionsNode = null
/** @type {ExportDefaultDeclaration | null} */
let exportDefaultDeclaration = null
/** @type {ImportDeclaration|null} */
let lastImportDeclaration = null
return utils.compositingVisitors(
utils.defineScriptSetupVisitor(context, {
ImportDeclaration(node) {
lastImportDeclaration = node
},
onDefineOptionsEnter(node) {
defineOptionsNode = node
}
}),
{
ExportDefaultDeclaration(node) {
exportDefaultDeclaration = node
},
'Program:exit'() {
if (!exportDefaultDeclaration) {
return
}
context.report({
node: exportDefaultDeclaration,
messageId: 'preferDefineOptions',
fix: defineOptionsNode
? null
: buildFix(exportDefaultDeclaration, scriptSetup)
})
}
}
)
/**
* @param {ExportDefaultDeclaration} node
* @param {VElement} scriptSetup
* @returns {(fixer: RuleFixer) => Fix[]}
*/
function buildFix(node, scriptSetup) {
return (fixer) => {
const sourceCode = context.getSourceCode()
// Calc remove range
/** @type {Range} */
let removeRange = [...node.range]
const script = scriptSetup.parent.children
.filter(utils.isVElement)
.find(
(node) =>
node.name === 'script' && !utils.hasAttribute(node, 'setup')
)
if (
script &&
script.endTag &&
sourceCode
.getTokensBetween(script.startTag, script.endTag, {
includeComments: true
})
.every(
(token) =>
removeRange[0] <= token.range[0] &&
token.range[1] <= removeRange[1]
)
) {
removeRange = [...script.range]
}
const removeStartLoc = sourceCode.getLocFromIndex(removeRange[0])
if (
sourceCode.lines[removeStartLoc.line - 1]
.slice(0, removeStartLoc.column)
.trim() === ''
) {
removeRange[0] =
removeStartLoc.line === 1
? 0
: sourceCode.getIndexFromLoc({
line: removeStartLoc.line - 1,
column: sourceCode.lines[removeStartLoc.line - 2].length
})
}
/** @type {VStartTag | ImportDeclaration} */
const insertAfterTag = lastImportDeclaration || scriptSetup.startTag
return [
fixer.removeRange(removeRange),
fixer.insertTextAfter(
insertAfterTag,
`\ndefineOptions(${sourceCode.getText(node.declaration)})\n`
)
]
}
}
}
}