feat(session): Add rule

#3
This commit is contained in:
Dominik Ferber
2015-10-25 15:10:58 +01:00
parent dfc95e5abc
commit 8d44769f06
5 changed files with 311 additions and 0 deletions

View File

@@ -83,6 +83,7 @@ For a more thorough introduction, read [setting up a Meteor project](docs/SETUP_
* [check](docs/rules/check.md): Core API for check and Match
* [connections](docs/rules/connections.md): Core API for connections
* [collections](docs/rules/collections.md): Core API for collections
* [session](docs/rules/session.md): Core API for Session
## Best Practices
* [audit-argument-checks](docs/rules/audit-argument-checks.md): Enforce check on all arguments passed to methods and publish functions

114
docs/rules/session.md Normal file
View File

@@ -0,0 +1,114 @@
# Core API for Session (session)
Prevent misusage of [Session](http://docs.meteor.com/#/full/session).
## Rule Details
This rule aims to prevent errors when using Publications and Subscriptions. It verifies `Session` is used in the correct environments.
The following patterns are considered warnings:
```js
Session.set('foo')
```
```js
Session.setDefault('foo')
```
```js
Session.set('foo', true, 'bar')
```
```js
if (Meteor.isServer) {
Session.set('foo')
}
```
```js
Session.get('foo', true)
```
```js
Session.get()
```
```js
Session.equals('foo')
```
The following patterns are not warnings:
```js
Session.set('foo', true)
```
```js
Session.setDefault('foo', true)
```
```js
Session.get('foo')
```
```js
Session.equals('foo', true)
```
### Options
#### no-equal
By default this rule does not warn when trying to call `Session.equal`. Usually a call to `Session.equals` is meant instead.
To warn when using `Session.equal`, configure the rule as
```
session: [2, "no-equal"]
```
With this configuration, the rule will warn on this pattern:
```js
Session.equal('foo', 'bar')
```
## When Not To Use It
Disable this rule if you are using [no-session](./no-session.md).
## Further Reading
- http://docs.meteor.com/#/full/session

View File

@@ -22,6 +22,7 @@ module.exports = {
check: unpack('./rules/check'),
connections: unpack('./rules/connections'),
collections: unpack('./rules/collections'),
session: unpack('./rules/session'),
// Best Practices
'audit-argument-checks': unpack('./rules/audit-argument-checks'),
@@ -39,6 +40,7 @@ module.exports = {
check: 0,
connections: 0,
collections: 0,
session: 0,
// Best Practices
'audit-argument-checks': 0,

73
lib/rules/session.js Normal file
View File

@@ -0,0 +1,73 @@
/**
* @fileoverview Core API for Session
* @author Dominik Ferber
* @copyright 2015 Dominik Ferber. All rights reserved.
* See LICENSE file in root directory for full license.
*/
// -----------------------------------------------------------------------------
// Rule Definition
// -----------------------------------------------------------------------------
import {NON_METEOR, SERVER} from '../util/environment'
import {getExecutors} from '../util'
import {getPropertyName} from '../util/ast'
module.exports = getMeta => context => {
const {env} = getMeta(context)
// ---------------------------------------------------------------------------
// Public
// ---------------------------------------------------------------------------
if (env === NON_METEOR || env === SERVER) {
return {}
}
return {
CallExpression: function (node) {
if (
node.callee.type === 'MemberExpression' &&
node.callee.object.type === 'Identifier' &&
node.callee.object.name === 'Session'
) {
const executors = getExecutors(env, context.getAncestors())
if (executors.size === 0) {
return
}
if (executors.has('server')) {
context.report(node, 'Allowed on client only')
return
}
switch (getPropertyName(node.callee.property)) {
case 'set':
case 'setDefault':
case 'equals':
if (node.arguments.length !== 2) {
context.report(node, 'Expected two arguments')
}
break
case 'get':
if (node.arguments.length !== 1) {
context.report(node, 'Expected one argument')
}
break
case 'equal':
if (context.options.length > 0 && context.options[0] === 'no-equal') {
context.report(node.callee.property, 'Did you mean "Session.equals" instead?')
}
break
}
}
}
}
}
module.exports.schema = [
{
enum: ['equal', 'no-equal']
}
]

121
tests/lib/rules/session.js Normal file
View File

@@ -0,0 +1,121 @@
/**
* @fileoverview Core API for Session
* @author Dominik Ferber
* @copyright 2015 Dominik Ferber. All rights reserved.
* See LICENSE file in root directory for full license.
*/
// -----------------------------------------------------------------------------
// Requirements
// -----------------------------------------------------------------------------
import {NON_METEOR, UNIVERSAL, CLIENT, SERVER} from '../../../dist/util/environment'
const rule = require('../../../dist/rules/session')
const RuleTester = require('eslint').RuleTester
const commonValidCode = [
'x()',
'Session.set("foo", true)',
'Session.setDefault("foo", true)',
'Session.get("foo")',
'Session.equals("foo", true)',
{
code: `Session.equal('foo', 'bar')`,
options: ['equal']
},
`
if (Meteor.isServer) {
Session.set('foo')
}
`
]
const commonInvalidCode = [
{
code: `Session.set('foo')`,
errors: [{message: 'Expected two arguments', type: 'CallExpression'}]
},
{
code: `Session.setDefault('foo')`,
errors: [{message: 'Expected two arguments', type: 'CallExpression'}]
},
{
code: `Session.set('foo', true, 'bar')`,
errors: [{message: 'Expected two arguments', type: 'CallExpression'}]
},
{
code: `
Session.get('foo', true)
`,
errors: [{message: 'Expected one argument', type: 'CallExpression'}]
},
{
code: `Session.get()`,
errors: [{message: 'Expected one argument', type: 'CallExpression'}]
},
{
code: `Session.equals('foo')`,
errors: [{message: 'Expected two arguments', type: 'CallExpression'}]
},
{
code: `Session.equal('foo', 'bar')`,
options: ['no-equal'],
errors: [{message: 'Did you mean "Session.equals" instead?', type: 'Identifier'}]
}
]
// -----------------------------------------------------------------------------
// Tests
// -----------------------------------------------------------------------------
const ruleTester = new RuleTester()
ruleTester.run('session', rule(() => ({env: CLIENT})), {
valid: [
...commonValidCode
],
invalid: [
...commonInvalidCode
]
})
ruleTester.run('session', rule(() => ({env: UNIVERSAL})), {
valid: [
`
if (Meteor.isClient) {
Session.set('foo', true)
}
`,
`
if (Meteor.isCordova) {
Session.set('foo', true)
}
`
],
invalid: [
{
code: `Session.set('foo', true)`,
errors: [{message: 'Allowed on client only', type: 'CallExpression'}]
}
]
})
ruleTester.run('session', rule(() => ({env: SERVER})), {
valid: [
...commonValidCode,
...commonInvalidCode
],
invalid: []
})
ruleTester.run('session', rule(() => ({env: NON_METEOR})), {
valid: [
...commonValidCode,
...commonInvalidCode
],
invalid: []
})