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
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}')
]
}
})
})
})
}
}