This commit is contained in:
darenhsu
2022-07-17 13:16:16 +08:00
parent 84759556ff
commit befd344ab0
28070 changed files with 4008428 additions and 1 deletions
+24
View File
@@ -0,0 +1,24 @@
module.exports = {
parser: require.resolve('vue-eslint-parser'),
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
ecmaFeatures: {
jsx: true
}
},
env: {
browser: true,
es6: true
},
plugins: [
'nuxt'
],
rules: {
'nuxt/no-env-in-context': 'error',
'nuxt/no-env-in-hooks': 'error',
'nuxt/no-globals-in-created': 'error',
'nuxt/no-this-in-fetch-data': 'error',
'nuxt/no-cjs-in-config': 'error'
}
}
+6
View File
@@ -0,0 +1,6 @@
module.exports = {
extends: require.resolve('./base.js'),
rules: {
'nuxt/no-timing-in-fetch-data': 'error'
}
}
+18
View File
@@ -0,0 +1,18 @@
module.exports = {
rules: {
'no-env-in-context': require('./rules/no-env-in-context'),
'no-env-in-hooks': require('./rules/no-env-in-hooks'),
'no-globals-in-created': require('./rules/no-globals-in-created'),
'no-this-in-fetch-data': require('./rules/no-this-in-fetch-data'),
'no-timing-in-fetch-data': require('./rules/no-timing-in-fetch-data'),
'no-cjs-in-config': require('./rules/no-cjs-in-config'),
'require-func-head': require('./rules/require-func-head')
},
configs: {
base: require('./configs/base'),
recommended: require('./configs/recommended')
},
processors: {
'.vue': require('./processors')
}
}
View File
+1
View File
@@ -0,0 +1 @@
module.exports = require('eslint-plugin-vue/lib/processor')
View File
+103
View File
@@ -0,0 +1,103 @@
/**
* @fileoverview Disallow `require/modules.exports/exports` in `nuxt.config.js`
* @author Xin Du <clark.duxin@gmail.com>
*/
'use strict'
const path = require('path')
// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------
module.exports = {
meta: {
docs: {
description:
'disallow commonjs module api `require/modules.exports/exports` in `nuxt.config.js`',
category: 'base'
},
messages: {
noCjs: 'Unexpected {{cjs}}, please use {{esm}} instead.'
}
},
create (context) {
// variables should be defined here
const options = context.options[0] || {}
const configFile = options.file || 'nuxt.config.js'
let isNuxtConfig = false
// ----------------------------------------------------------------------
// Public
// ----------------------------------------------------------------------
return {
Program (node) {
const filename = path.basename(context.getFilename())
if (filename === configFile) {
isNuxtConfig = true
}
},
MemberExpression: function (node) {
if (!isNuxtConfig) {
return
}
// module.exports
if (node.object.name === 'module' && node.property.name === 'exports') {
context.report({
node,
messageId: 'noCjs',
data: {
cjs: 'module.exports',
esm: 'export default'
}
})
}
// exports.
if (node.object.name === 'exports') {
const isInScope = context.getScope()
.variables
.some(variable => variable.name === 'exports')
if (!isInScope) {
context.report({
node,
messageId: 'noCjs',
data: {
cjs: 'exports',
esm: 'export default'
}
})
}
}
},
CallExpression: function (call) {
const module = call.arguments[0]
if (
!isNuxtConfig ||
context.getScope().type !== 'module' ||
!['ExpressionStatement', 'VariableDeclarator'].includes(call.parent.type) ||
call.callee.type !== 'Identifier' ||
call.callee.name !== 'require' ||
call.arguments.length !== 1 ||
module.type !== 'Literal' ||
typeof module.value !== 'string'
) {
return
}
context.report({
node: call.callee,
messageId: 'noCjs',
data: {
cjs: 'require',
esm: 'import'
}
})
}
}
}
}
+83
View File
@@ -0,0 +1,83 @@
/**
* @fileoverview disallow `context.isServer/context.isClient` in `asyncData/fetch/nuxtServerInit`
* @author Xin Du <clark.duxin@gmail.com>
*/
'use strict'
const utils = require('../utils')
// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------
module.exports = {
meta: {
docs: {
description:
'disallow `context.isServer/context.isClient` in `asyncData/fetch/nuxtServerInit`',
category: 'base'
},
messages: {
noEnv: 'Unexpected {{env}} in {{funcName}}.'
}
},
create (context) {
// variables should be defined here
const forbiddenNodes = []
const options = context.options[0] || {}
const ENV = ['isServer', 'isClient']
const HOOKS = new Set(['asyncData', 'fetch'].concat(options.methods || []))
// ----------------------------------------------------------------------
// Public
// ----------------------------------------------------------------------
return {
MemberExpression (node) {
const propertyName = node.computed ? node.property.value : node.property.name
if (propertyName && ENV.includes(propertyName)) {
forbiddenNodes.push({ name: propertyName, node })
}
},
...utils.executeOnVue(context, obj => {
for (const funcName of HOOKS) {
const func = utils.getFunctionWithName(obj, funcName)
const param = func && func.value ? func.value.params && func.value.params[0] : false
if (param) {
if (param.type === 'ObjectPattern') {
for (const prop of param.properties) {
if (prop.key && prop.key.name && ENV.includes(prop.key.name)) {
context.report({
node: prop,
messageId: 'noEnv',
data: {
env: prop.key.name,
funcName
}
})
}
}
} else {
for (const { name, node: child } of forbiddenNodes) {
if (utils.isInFunction(func, child)) {
if (param.name === child.object.name) {
context.report({
node: child,
messageId: 'noEnv',
data: {
env: name,
funcName
}
})
}
}
}
}
}
}
})
}
}
}
+68
View File
@@ -0,0 +1,68 @@
/**
* @fileoverview disallow process.server and process.client in the following lifecycle hooks: beforeMount, mounted, beforeUpdate, updated, activated, deactivated, beforeDestroy and destroyed
* @author Xin Du <clark.duxin@gmail.com>
*/
'use strict'
const utils = require('../utils')
// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------
module.exports = {
meta: {
docs: {
description:
'disallow process.server and process.client in the following lifecycle hooks: beforeMount, mounted, beforeUpdate, updated, activated, deactivated, beforeDestroy and destroyed',
category: 'base'
},
messages: {
noEnv: 'Unexpected {{name}} in {{funcName}}.'
}
},
create (context) {
// variables should be defined here
const forbiddenNodes = []
const options = context.options[0] || {}
const ENV = ['server', 'client']
const HOOKS = new Set(['beforeMount', 'mounted', 'beforeUpdate', 'updated', 'activated', 'deactivated', 'beforeDestroy', 'destroyed'].concat(options.methods || []))
// ----------------------------------------------------------------------
// Public
// ----------------------------------------------------------------------
return {
MemberExpression (node) {
const objectName = node.object.name
if (objectName === 'process') {
const propertyName = node.computed ? node.property.value : node.property.name
if (propertyName && ENV.includes(propertyName)) {
forbiddenNodes.push({ name: 'process.' + propertyName, node })
}
}
},
...utils.executeOnVue(context, obj => {
for (const funcName of HOOKS) {
const func = utils.getFunctionWithName(obj, funcName)
if (func) {
for (const { name, node: child } of forbiddenNodes) {
if (utils.isInFunction(func, child)) {
context.report({
node: child,
messageId: 'noEnv',
data: {
name,
funcName
}
})
}
}
}
}
})
}
}
}
+70
View File
@@ -0,0 +1,70 @@
/**
* @fileoverview disallow `window/document` in `created/beforeCreate`
* @author Xin Du <clark.duxin@gmail.com>
*/
'use strict'
const utils = require('../utils')
// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------
module.exports = {
meta: {
docs: {
description: 'disallow `window/document` in `created/beforeCreate`',
category: 'base'
},
messages: {
noGlobals: 'Unexpected {{name}} in {{funcName}}.'
}
},
create (context) {
const forbiddenNodes = []
const options = context.options[0] || {}
const HOOKS = new Set(
['created', 'beforeCreate'].concat(options.methods || [])
)
const GLOBALS = ['window', 'document']
function isGlobals (name) {
return GLOBALS.includes(name)
}
return {
MemberExpression (node) {
if (!node.object) return
const name = node.object.name
if (isGlobals(name)) {
forbiddenNodes.push({ name, node })
}
},
VariableDeclarator (node) {
if (!node.init) return
const name = node.init.name
if (isGlobals(name)) {
forbiddenNodes.push({ name, node })
}
},
...utils.executeOnVue(context, obj => {
for (const { funcName, name, node } of utils.getFunctionWithChild(obj, HOOKS, forbiddenNodes)) {
context.report({
node,
messageId: 'noGlobals',
data: {
name,
funcName
}
})
}
})
}
}
}
+90
View File
@@ -0,0 +1,90 @@
/**
* @fileoverview disallow `this` in `asyncData/fetch`
* @author Xin Du <clark.duxin@gmail.com>
*/
'use strict'
const satisfies = require('semver/ranges/intersects')
const utils = require('../utils')
const resolver = require('../utils/resolver')
// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------
module.exports = {
meta: {
docs: {
description: 'disallow `this` in `asyncData/fetch`',
category: 'base'
},
messages: {
noThis: 'Unexpected this in {{funcName}}.'
}
},
create (context) {
// variables should be defined here
const forbiddenNodes = new Map()
const options = context.options[0] || {}
const HOOKS = new Set(
['asyncData'].concat(options.methods || [])
)
const { version } = resolver
// new fetch API can use `this` since 2.12.0
if (version && satisfies(version, '<2.12.0')) {
HOOKS.add('fetch')
}
let nodeUsingThis = []
// ----------------------------------------------------------------------
// Helpers
// ----------------------------------------------------------------------
function enterFunction () {
nodeUsingThis = []
}
function exitFunction (node) {
if (nodeUsingThis.length > 0) {
forbiddenNodes.set(node, nodeUsingThis)
}
}
function markThisUsed (node) {
nodeUsingThis.push(node)
}
// ----------------------------------------------------------------------
// Public
// ----------------------------------------------------------------------
return {
FunctionExpression: enterFunction,
'FunctionExpression:exit': exitFunction,
ArrowFunctionExpression: enterFunction,
'ArrowFunctionExpression:exit': exitFunction,
ThisExpression: markThisUsed,
Super: markThisUsed,
...utils.executeOnVue(context, obj => {
for (const funcName of HOOKS) {
const prop = utils.getFunctionWithName(obj, funcName)
if (prop && forbiddenNodes.has(prop.value)) {
for (const node of forbiddenNodes.get(prop.value)) {
context.report({
node: node,
messageId: 'noThis',
data: {
funcName
}
})
}
}
}
})
}
}
}
+70
View File
@@ -0,0 +1,70 @@
/**
* @fileoverview disallow `setTimeout/setInterval` in `asyncData/fetch`
* @author Xin Du <clark.duxin@gmail.com>
*/
'use strict'
const utils = require('../utils')
// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------
module.exports = {
meta: {
docs: {
description: 'disallow `setTimeout/setInterval` in `asyncData/fetch`',
category: 'recommended'
},
messages: {
noTiming: 'Unexpected {{name}} in {{funcName}}.'
}
},
create (context) {
const forbiddenNodes = []
const options = context.options[0] || {}
const HOOKS = new Set(
['fetch', 'asyncData'].concat(options.methods || [])
)
const TIMING = ['setTimeout', 'setInterval']
function isTiming (name) {
return TIMING.includes(name)
}
return {
CallExpression (node) {
if (!node.callee) return
const name = node.callee.name
if (isTiming(name)) {
forbiddenNodes.push({ name, node })
}
},
VariableDeclarator (node) {
if (!node.init) return
const name = node.init.name
if (isTiming(name)) {
forbiddenNodes.push({ name, node })
}
},
...utils.executeOnVue(context, obj => {
for (const { funcName, name, node } of utils.getFunctionWithChild(obj, HOOKS, forbiddenNodes)) {
context.report({
node,
messageId: 'noTiming',
data: {
name,
funcName
}
})
}
})
}
}
}
+55
View File
@@ -0,0 +1,55 @@
/**
* @fileoverview enforce component's head property to be a function.
* @author Xin Du <clark.duxin@gmail.com>
*/
'use strict'
const utils = require('../utils')
// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------
module.exports = {
meta: {
docs: {
description: "enforce component's head property to be a function",
category: 'recommended'
},
fixable: 'code',
messages: {
head: '`head` property in component must be a function.'
}
},
create (context) {
const sourceCode = context.getSourceCode()
return utils.executeOnVueComponent(context, (obj) => {
obj.properties
.filter(p =>
p.type === 'Property' &&
p.key.type === 'Identifier' &&
p.key.name === 'head' &&
p.value.type !== 'FunctionExpression' &&
p.value.type !== 'ArrowFunctionExpression' &&
p.value.type !== 'Identifier' &&
p.value.type !== 'CallExpression'
)
.forEach(p => {
context.report({
node: p,
messageId: 'head',
fix (fixer) {
const tokens = utils.getFirstAndLastTokens(p.value, sourceCode)
return [
fixer.insertTextBefore(tokens.first, 'function() {\nreturn '),
fixer.insertTextAfter(tokens.last, ';\n}')
]
}
})
})
})
}
}
+71
View File
@@ -0,0 +1,71 @@
const utils = require('eslint-plugin-vue/lib/utils')
module.exports = Object.assign(
{
getProperty (node, name, condition) {
return node.properties.find(
p => p.type === 'Property' && name === utils.getStaticPropertyName(p.key) && condition(p)
)
},
getProperties (node, names) {
return node.properties.filter(
p => p.type === 'Property' && (!names.size || names.has(utils.getStaticPropertyName(p.key)))
)
},
getFunctionWithName (rootNode, name) {
return this.getProperty(
rootNode,
name,
item => item.value.type === 'ArrowFunctionExpression' || item.value.type === 'FunctionExpression'
)
},
isInFunction (func, child) {
if (func.value.type === 'FunctionExpression') {
if (
child &&
child.loc.start.line >= func.value.loc.start.line &&
child.loc.end.line <= func.value.loc.end.line
) {
return true
}
}
},
* getFunctionWithChild (rootNode, funcNames, childNodes) {
const funcNodes = this.getProperties(rootNode, funcNames)
for (const func of funcNodes) {
for (const { name, node: child } of childNodes) {
const funcName = utils.getStaticPropertyName(func.key)
if (!funcName) continue
if (this.isInFunction(func, child)) {
yield { name, node: child, func, funcName }
}
}
}
},
isOpenParen (token) {
return token.type === 'Punctuator' && token.value === '('
},
isCloseParen (token) {
return token.type === 'Punctuator' && token.value === ')'
},
getFirstAndLastTokens (node, sourceCode) {
let first = sourceCode.getFirstToken(node)
let last = sourceCode.getLastToken(node)
// If the value enclosed by parentheses, update the 'first' and 'last' by the parentheses.
while (true) {
const prev = sourceCode.getTokenBefore(first)
const next = sourceCode.getTokenAfter(last)
if (this.isOpenParen(prev) && this.isCloseParen(next)) {
first = prev
last = next
} else {
return { first, last }
}
}
}
},
utils
)
+21
View File
@@ -0,0 +1,21 @@
module.exports = {
__version: undefined,
get version () {
if (this.__version === undefined) {
return this.loadNuxtPkg()
}
return this.__version
},
loadPkg (pkgName) {
try {
return require(`${pkgName}/package.json`)
} catch (e) {
return {}
}
},
loadNuxtPkg () {
const { version } = this.loadPkg('nuxt') || this.loadPkg('nuxt-edge')
this.__version = version || false
return this.__version
}
}