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

Commit 09eaa50

Browse files
committedDec 6, 2018
feat(rule): add no-globals-in-created
1 parent b7478f8 commit 09eaa50

7 files changed

+257
-6
lines changed
 

‎docs/rules/no-globals-in-created.md

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# nuxt/no-globals-in-created
2+
3+
> disallow `window/document` in `created/beforeCreate`
4+
5+
- :gear: This rule is included in `"plugin:nuxt/ssr"`.
6+
7+
## Rule Details
8+
9+
This rule is for preventing using `window/document` in `created/beforeCreate`, since `created/beforeCreate` may be executed in server side in SSR.
10+
11+
Examples of **incorrect** code for this rule:
12+
13+
```js
14+
15+
export default {
16+
created() {
17+
window.foo = 'bar'
18+
}
19+
}
20+
21+
```
22+
23+
Examples of **correct** code for this rule:
24+
25+
```js
26+
27+
export default {
28+
created() {
29+
const foo = 'bar'
30+
}
31+
}
32+
33+
```
34+
35+
## :mag: Implementation
36+
37+
- [Rule source](https://github.com/nuxt/eslint-plugin-nuxt/blob/master/lib/rules/no-globals-in-created.js)
38+
- [Test source](https://github.com/nuxt/eslint-plugin-nuxt/blob/master/lib/rules/__test__/no-globals-in-created.test.js)

‎lib/configs/ssr.js

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module.exports = {
2+
extends: require.resolve('./base.js'),
3+
rules: {
4+
'nuxt/no-globals-in-created': 'warn'
5+
}
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/**
2+
* @fileoverview disallow `window/document` in `created/beforeCreate`
3+
* @author Clark Du
4+
*/
5+
'use strict'
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
var rule = require('../no-globals-in-created')
12+
13+
var RuleTester = require('eslint').RuleTester
14+
15+
const parserOptions = {
16+
ecmaVersion: 2018,
17+
sourceType: 'module'
18+
}
19+
20+
// ------------------------------------------------------------------------------
21+
// Tests
22+
// ------------------------------------------------------------------------------
23+
24+
var ruleTester = new RuleTester()
25+
ruleTester.run('no-globals-in-created', rule, {
26+
27+
valid: [
28+
{
29+
filename: 'test.vue',
30+
code: `
31+
export default {
32+
created() {
33+
const path = this.$route.path
34+
},
35+
beforeCreate() {
36+
const path = this.$route.params.foo
37+
}
38+
}
39+
`,
40+
parserOptions
41+
}
42+
],
43+
44+
invalid: [
45+
{
46+
filename: 'test.vue',
47+
code: `
48+
export default {
49+
created() {
50+
const path = window.location.pathname
51+
},
52+
beforeCreate() {
53+
const foo = document.foo
54+
}
55+
}
56+
`,
57+
errors: [{
58+
message: 'Unexpected window in created.',
59+
type: 'MemberExpression'
60+
}, {
61+
message: 'Unexpected document in beforeCreate.',
62+
type: 'MemberExpression'
63+
}],
64+
parserOptions
65+
},
66+
{
67+
filename: 'test.vue',
68+
code: `
69+
export default {
70+
created() {
71+
document.foo = 'bar'
72+
},
73+
beforeCreate() {
74+
window.foo = 'bar'
75+
}
76+
}
77+
`,
78+
errors: [{
79+
message: 'Unexpected document in created.',
80+
type: 'MemberExpression'
81+
}, {
82+
message: 'Unexpected window in beforeCreate.',
83+
type: 'MemberExpression'
84+
}],
85+
parserOptions
86+
},
87+
{
88+
filename: 'test.vue',
89+
code: `
90+
export default {
91+
created() {
92+
return window.foo
93+
},
94+
beforeCreate() {
95+
return document.foo
96+
}
97+
}
98+
`,
99+
errors: [{
100+
message: 'Unexpected window in created.',
101+
type: 'MemberExpression'
102+
}, {
103+
message: 'Unexpected document in beforeCreate.',
104+
type: 'MemberExpression'
105+
}],
106+
parserOptions
107+
}
108+
]
109+
})

‎lib/rules/__test__/no-this-in-async-data.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* @fileoverview Prevent using this in asyncData
2+
* @fileoverview disallow `this` in asyncData
33
* @author Clark Du
44
*/
55
'use strict'

‎lib/rules/__test__/no-this-in-fetch.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* @fileoverview Prevent using this in fetch
2+
* @fileoverview disallow `this` in fetch
33
* @author Clark Du
44
*/
55
'use strict'

‎lib/rules/no-globals-in-created.js

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/**
2+
* @fileoverview disallow `window/document` in `created/beforeCreate`
3+
* @author Clark Du
4+
*/
5+
'use strict'
6+
7+
const utils = require('../utils')
8+
9+
// ------------------------------------------------------------------------------
10+
// Rule Definition
11+
// ------------------------------------------------------------------------------
12+
13+
module.exports = {
14+
meta: {
15+
docs: {
16+
description: 'disallow `window/document` in `created/beforeCreate`',
17+
category: 'ssr'
18+
},
19+
fixable: null, // or "code" or "whitespace"
20+
schema: [
21+
// fill in your schema
22+
],
23+
messages: {
24+
noGlobals: 'Unexpected {{name}} in {{funcName}}.'
25+
}
26+
},
27+
28+
create: function (context) {
29+
const forbiddenNodes = []
30+
const options = context.options[0] || {}
31+
32+
const HOOKS = new Set(
33+
['created', 'beforeCreate'].concat(options.methods || [])
34+
)
35+
const GLOBALS = ['window', 'document']
36+
37+
function isGlobals (name) {
38+
return GLOBALS.includes(name)
39+
}
40+
41+
return {
42+
MemberExpression (node) {
43+
if (!node.object) return
44+
45+
const name = node.object.name
46+
47+
if (isGlobals(name)) {
48+
forbiddenNodes.push({ name, node })
49+
}
50+
},
51+
VariableDeclarator (node) {
52+
if (!node.init) return
53+
54+
const name = node.init.name
55+
56+
if (isGlobals(name)) {
57+
forbiddenNodes.push({ name, node })
58+
}
59+
},
60+
...utils.executeOnVue(context, obj => {
61+
for (const { funcName, name, node } of utils.getParentFuncs(obj, HOOKS, forbiddenNodes)) {
62+
context.report({
63+
node,
64+
messageId: 'noGlobals',
65+
data: {
66+
name,
67+
funcName
68+
}
69+
})
70+
}
71+
})
72+
}
73+
}
74+
}

‎lib/utils/index.js

+28-4
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,35 @@ const utils = require('eslint-plugin-vue/lib/utils')
22

33
module.exports = Object.assign(
44
{
5-
getFuncNodeWithName (node, name) {
6-
return node.properties.find(item => item.type === 'Property' &&
7-
utils.getStaticPropertyName(item) === name &&
8-
(item.value.type === 'ArrowFunctionExpression' || item.value.type === 'FunctionExpression')
5+
getProperties (node, names) {
6+
return node.properties.filter(
7+
p => p.type === 'Property' && (!names.size || names.has(utils.getStaticPropertyName(p.key)))
98
)
9+
},
10+
getFuncNodeWithName (rootNode, name) {
11+
return this.getProperties(rootNode, [name]).filter(
12+
item => item.value.type === 'ArrowFunctionExpression' || item.value.type === 'FunctionExpression'
13+
)
14+
},
15+
* getParentFuncs (rootNode, parentNames, childNodes) {
16+
const nodes = this.getProperties(rootNode, parentNames)
17+
18+
for (const item of nodes) {
19+
for (const { name, node: child } of childNodes) {
20+
const funcName = utils.getStaticPropertyName(item.key)
21+
if (!funcName) continue
22+
23+
if (item.value.type === 'FunctionExpression') {
24+
if (
25+
child &&
26+
child.loc.start.line >= item.value.loc.start.line &&
27+
child.loc.end.line <= item.value.loc.end.line
28+
) {
29+
yield { name, node: child, funcName }
30+
}
31+
}
32+
}
33+
}
1034
}
1135
},
1236
utils

0 commit comments

Comments
 (0)
This repository has been archived.