Skip to content
This repository has been archived by the owner on Mar 19, 2024. It is now read-only.

Commit

Permalink
feat(rule): add no-globals-in-created
Browse files Browse the repository at this point in the history
  • Loading branch information
clarkdo committed Dec 6, 2018
1 parent b7478f8 commit 09eaa50
Show file tree
Hide file tree
Showing 7 changed files with 257 additions and 6 deletions.
38 changes: 38 additions & 0 deletions docs/rules/no-globals-in-created.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# nuxt/no-globals-in-created

> disallow `window/document` in `created/beforeCreate`
- :gear: This rule is included in `"plugin:nuxt/ssr"`.

## Rule Details

This rule is for preventing using `window/document` in `created/beforeCreate`, since `created/beforeCreate` may be executed in server side in SSR.

Examples of **incorrect** code for this rule:

```js

export default {
created() {
window.foo = 'bar'
}
}

```

Examples of **correct** code for this rule:

```js

export default {
created() {
const foo = 'bar'
}
}

```

## :mag: Implementation

- [Rule source](https://github.com/nuxt/eslint-plugin-nuxt/blob/master/lib/rules/no-globals-in-created.js)
- [Test source](https://github.com/nuxt/eslint-plugin-nuxt/blob/master/lib/rules/__test__/no-globals-in-created.test.js)
6 changes: 6 additions & 0 deletions lib/configs/ssr.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
extends: require.resolve('./base.js'),
rules: {
'nuxt/no-globals-in-created': 'warn'
}
}
109 changes: 109 additions & 0 deletions lib/rules/__test__/no-globals-in-created.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/**
* @fileoverview disallow `window/document` in `created/beforeCreate`
* @author Clark Du
*/
'use strict'

// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------

var rule = require('../no-globals-in-created')

var RuleTester = require('eslint').RuleTester

const parserOptions = {
ecmaVersion: 2018,
sourceType: 'module'
}

// ------------------------------------------------------------------------------
// Tests
// ------------------------------------------------------------------------------

var ruleTester = new RuleTester()
ruleTester.run('no-globals-in-created', rule, {

valid: [
{
filename: 'test.vue',
code: `
export default {
created() {
const path = this.$route.path
},
beforeCreate() {
const path = this.$route.params.foo
}
}
`,
parserOptions
}
],

invalid: [
{
filename: 'test.vue',
code: `
export default {
created() {
const path = window.location.pathname
},
beforeCreate() {
const foo = document.foo
}
}
`,
errors: [{
message: 'Unexpected window in created.',
type: 'MemberExpression'
}, {
message: 'Unexpected document in beforeCreate.',
type: 'MemberExpression'
}],
parserOptions
},
{
filename: 'test.vue',
code: `
export default {
created() {
document.foo = 'bar'
},
beforeCreate() {
window.foo = 'bar'
}
}
`,
errors: [{
message: 'Unexpected document in created.',
type: 'MemberExpression'
}, {
message: 'Unexpected window in beforeCreate.',
type: 'MemberExpression'
}],
parserOptions
},
{
filename: 'test.vue',
code: `
export default {
created() {
return window.foo
},
beforeCreate() {
return document.foo
}
}
`,
errors: [{
message: 'Unexpected window in created.',
type: 'MemberExpression'
}, {
message: 'Unexpected document in beforeCreate.',
type: 'MemberExpression'
}],
parserOptions
}
]
})
2 changes: 1 addition & 1 deletion lib/rules/__test__/no-this-in-async-data.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* @fileoverview Prevent using this in asyncData
* @fileoverview disallow `this` in asyncData
* @author Clark Du
*/
'use strict'
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/__test__/no-this-in-fetch.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* @fileoverview Prevent using this in fetch
* @fileoverview disallow `this` in fetch
* @author Clark Du
*/
'use strict'
Expand Down
74 changes: 74 additions & 0 deletions lib/rules/no-globals-in-created.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* @fileoverview disallow `window/document` in `created/beforeCreate`
* @author Clark Du
*/
'use strict'

const utils = require('../utils')

// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------

module.exports = {
meta: {
docs: {
description: 'disallow `window/document` in `created/beforeCreate`',
category: 'ssr'
},
fixable: null, // or "code" or "whitespace"
schema: [
// fill in your schema
],
messages: {
noGlobals: 'Unexpected {{name}} in {{funcName}}.'
}
},

create: function (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.getParentFuncs(obj, HOOKS, forbiddenNodes)) {
context.report({
node,
messageId: 'noGlobals',
data: {
name,
funcName
}
})
}
})
}
}
}
32 changes: 28 additions & 4 deletions lib/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,35 @@ const utils = require('eslint-plugin-vue/lib/utils')

module.exports = Object.assign(
{
getFuncNodeWithName (node, name) {
return node.properties.find(item => item.type === 'Property' &&
utils.getStaticPropertyName(item) === name &&
(item.value.type === 'ArrowFunctionExpression' || item.value.type === 'FunctionExpression')
getProperties (node, names) {
return node.properties.filter(
p => p.type === 'Property' && (!names.size || names.has(utils.getStaticPropertyName(p.key)))
)
},
getFuncNodeWithName (rootNode, name) {
return this.getProperties(rootNode, [name]).filter(
item => item.value.type === 'ArrowFunctionExpression' || item.value.type === 'FunctionExpression'
)
},
* getParentFuncs (rootNode, parentNames, childNodes) {
const nodes = this.getProperties(rootNode, parentNames)

for (const item of nodes) {
for (const { name, node: child } of childNodes) {
const funcName = utils.getStaticPropertyName(item.key)
if (!funcName) continue

if (item.value.type === 'FunctionExpression') {
if (
child &&
child.loc.start.line >= item.value.loc.start.line &&
child.loc.end.line <= item.value.loc.end.line
) {
yield { name, node: child, funcName }
}
}
}
}
}
},
utils
Expand Down

0 comments on commit 09eaa50

Please sign in to comment.