mirror of
https://github.com/cypress-io/cypress.git
synced 2026-02-23 07:39:52 -06:00
import cypress-io/eslint-plugin-dev to npm/eslint-plugin-dev/
This commit is contained in:
3
npm/eslint-plugin-dev/.eslintignore
Normal file
3
npm/eslint-plugin-dev/.eslintignore
Normal file
@@ -0,0 +1,3 @@
|
||||
!.*
|
||||
**/package-lock.json
|
||||
test/fixtures
|
||||
11
npm/eslint-plugin-dev/.eslintrc.json
Normal file
11
npm/eslint-plugin-dev/.eslintrc.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"plugins": [
|
||||
"@cypress/dev",
|
||||
"promise"
|
||||
],
|
||||
"extends": [
|
||||
"plugin:promise/recommended",
|
||||
"plugin:@cypress/dev/general"
|
||||
],
|
||||
"rules": {}
|
||||
}
|
||||
5
npm/eslint-plugin-dev/.gitignore
vendored
Normal file
5
npm/eslint-plugin-dev/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
*.log
|
||||
package-lock.json
|
||||
*.tgz
|
||||
2
npm/eslint-plugin-dev/.npmignore
Normal file
2
npm/eslint-plugin-dev/.npmignore
Normal file
@@ -0,0 +1,2 @@
|
||||
/*
|
||||
!lib/
|
||||
29
npm/eslint-plugin-dev/.vscode/settings.json
vendored
Normal file
29
npm/eslint-plugin-dev/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"eslint.alwaysShowStatus": true,
|
||||
"eslint.validate": [
|
||||
{
|
||||
"language": "javascript",
|
||||
"autoFix": true
|
||||
},
|
||||
{
|
||||
"language": "javascriptreact",
|
||||
"autoFix": true
|
||||
},
|
||||
{
|
||||
"language": "typescript",
|
||||
"autoFix": true
|
||||
},
|
||||
{
|
||||
"language": "typescriptreact",
|
||||
"autoFix": true
|
||||
},
|
||||
{
|
||||
"language": "json",
|
||||
"autoFix": true
|
||||
},
|
||||
{
|
||||
"language": "coffeescript",
|
||||
"autoFix": false
|
||||
},
|
||||
],
|
||||
}
|
||||
24
npm/eslint-plugin-dev/LICENSE.md
Normal file
24
npm/eslint-plugin-dev/LICENSE.md
Normal file
@@ -0,0 +1,24 @@
|
||||
## MIT License
|
||||
|
||||
Copyright (c) 2017 Cypress.io https://cypress.io
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
208
npm/eslint-plugin-dev/README.md
Normal file
208
npm/eslint-plugin-dev/README.md
Normal file
@@ -0,0 +1,208 @@
|
||||
|
||||
<div>
|
||||
<!-- <img src="docs/readme-logo.png"> -->
|
||||
<h1>[Internal] Cypress Developer ESLint Plugin</h1>
|
||||
<a href="https://www.npmjs.com/package/@cypress/eslint-plugin-dev"><img src="https://img.shields.io/npm/v/@cypress/eslint-plugin-dev.svg?style=flat"></a>
|
||||
<a href="https://circleci.com/gh/cypress-io/eslint-plugin-dev/tree/master"><img src="https://img.shields.io/circleci/build/gh/cypress-io/eslint-plugin-dev.svg"></a>
|
||||
|
||||
<p>Common ESLint rules shared by Cypress packages.</p>
|
||||
|
||||
</div>
|
||||
|
||||
> ⚠️ This package for _internal development_ of Cypress. Here's the [**Official Cypress ESLint Plugin**](https://github.com/cypress-io/eslint-plugin-cypress) meant for users of Cypress.
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
npm install --save-dev @cypress/eslint-plugin-dev
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
1) install the following `devDependencies`:
|
||||
```sh
|
||||
@cypress/eslint-plugin-dev
|
||||
eslint-plugin-json-format
|
||||
@typescript-eslint/parser
|
||||
@typescript-eslint/eslint-plugin
|
||||
eslint-plugin-mocha
|
||||
|
||||
# if you have coffeescript files
|
||||
@fellow/eslint-plugin-coffee
|
||||
babel-eslint
|
||||
|
||||
# if you have react/jsx files
|
||||
eslint-plugin-react
|
||||
babel-eslint
|
||||
```
|
||||
|
||||
2) add the following to your root level `.eslintrc.json`:
|
||||
```json
|
||||
{
|
||||
"plugins": [
|
||||
"@cypress/dev"
|
||||
],
|
||||
"extends": [
|
||||
"plugin:@cypress/dev/general",
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
> Note: also add `"plugin:@cypress/dev/react"`, if you are using `React`
|
||||
|
||||
> Note: if you have a `test/` directory, you should create a `.eslintrc.json` file inside of it, and add:
|
||||
```json
|
||||
{
|
||||
"extends": [
|
||||
"plugin:@cypress/dev/tests",
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
3) add the following to your `.eslintignore`:
|
||||
```sh
|
||||
# don't ignore hidden files, useful for formatting json config files
|
||||
!.*
|
||||
```
|
||||
|
||||
4) (optional) Install and configure your text editor's ESLint Plugin Extension to lint and auto-fix files using ESLint, [detailed below](#editors)
|
||||
|
||||
5) (optional) Install [`husky`](https://github.com/typicode/husky) and enable the lint `pre-commit` hook:
|
||||
|
||||
`package.json`:
|
||||
```json
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-pre-commit"
|
||||
}
|
||||
},
|
||||
```
|
||||
> Note: the `lint-pre-commit` hook will automatically lint your staged files, and only `--fix` and `git add` them if there are no unstaged changes existing in that file (this protects partially staged files from being added in the hook).
|
||||
To auto-fix all staged & unstaged files, run `./node_modules/.bin/lint-changed --fix`
|
||||
|
||||
## Presets
|
||||
|
||||
### general
|
||||
|
||||
_Should usually be used at the root of the package._
|
||||
- The majority of the rules.
|
||||
- auto-fixes `json` files and sorts your `package.json` via [`eslint-plugin-json-format`](https://github.com/bkucera/eslint-plugin-json-format)
|
||||
|
||||
|
||||
**requires you to install the following `devDependencies`**:
|
||||
```sh
|
||||
eslint-plugin-json-format
|
||||
@typescript-eslint/parser
|
||||
@typescript-eslint/eslint-plugin
|
||||
```
|
||||
|
||||
### tests
|
||||
|
||||
Test-specific configuration and rules. Should be used within the `test/` directory.
|
||||
|
||||
**requires you to install the following `devDependencies`**:
|
||||
```sh
|
||||
eslint-plugin-mocha
|
||||
```
|
||||
|
||||
### react
|
||||
|
||||
React and JSX-specific configuration and rules.
|
||||
|
||||
**requires you to install the following `devDependencies`**:
|
||||
```sh
|
||||
babel-eslint
|
||||
eslint-plugin-react
|
||||
```
|
||||
|
||||
## Configuration Examples
|
||||
|
||||
Change some linting rules:
|
||||
```js
|
||||
// .eslintrc.json
|
||||
{
|
||||
"extends": [
|
||||
"plugin:@cypress/dev/general"
|
||||
],
|
||||
"rules": {
|
||||
"comma-dangle": "off",
|
||||
"no-debugger": "warn"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Stop your `package.json` from being formatted:
|
||||
```json
|
||||
{
|
||||
"settings": {
|
||||
"json/sort-package-json": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Rules:
|
||||
name | description | options | example
|
||||
-|-|-|-
|
||||
`@cypress/dev/arrow-body-multiline-braces` | Enforces braces in arrow functions ONLY IN multiline function definitions | [`[always|never] always set this to 'always'`] | `'@cypress/dev/arrow-body-multiline-braces': ['error', 'always']`
|
||||
`@cypress/dev/skip-comment` | Enforces a comment (`// NOTE:`) explaining a `.skip` added to `it`, `describe`, or `context` test blocks | { commentTokens: `[array] tokens that indicate .skip explanation (default: ['NOTE:', 'TODO:', 'FIXME:']`)} | `'@cypress/dev/skip-comment': ['error', { commentTokens: ['TODO:'] }]`
|
||||
`@cypress/dev/no-return-before` | Disallows `return` statements before certain configurable tokens | { tokens: `[array] tokens that cannot be preceded by 'return' (default: ['it', 'describe', 'context', 'expect']`)} | `'@cypress/dev/no-return-before': ['error', { tokens: ['myfn'] }]`
|
||||
|
||||
## <a name="editors"></a>Editors
|
||||
|
||||
### VSCode
|
||||
|
||||
Use plugin [ESLint by Dirk Baeumer](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) to lint and auto fix JS files using ESLint.
|
||||
After installing, add the following to your User or Workspace (`.vscode/settings.json`) settings:
|
||||
```json
|
||||
{
|
||||
"eslint.validate": [
|
||||
{
|
||||
"language": "javascript",
|
||||
"autoFix": true
|
||||
},
|
||||
{
|
||||
"language": "javascriptreact",
|
||||
"autoFix": true
|
||||
},
|
||||
{
|
||||
"language": "typescript",
|
||||
"autoFix": true
|
||||
},
|
||||
{
|
||||
"language": "typescriptreact",
|
||||
"autoFix": true
|
||||
},
|
||||
{
|
||||
"language": "json",
|
||||
"autoFix": true
|
||||
},
|
||||
{
|
||||
"language": "coffeescript",
|
||||
"autoFix": false
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
### Atom
|
||||
|
||||
Install package [linter-eslint](https://atom.io/packages/linter-eslint)
|
||||
(and its dependencies) to enable linting. Go into the settings for this package
|
||||
and enable "Fix on save" option to auto-fix white space issues and other things.
|
||||
|
||||
### Sublime Text
|
||||
|
||||
Install [ESLint-Formatter](https://packagecontrol.io/packages/ESLint-Formatter),
|
||||
then set the following settings:
|
||||
|
||||
```json
|
||||
{
|
||||
"format_on_save": true,
|
||||
"debug": true
|
||||
}
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the terms of the [MIT license](/LICENSE.md).
|
||||
29
npm/eslint-plugin-dev/circle.yml
Normal file
29
npm/eslint-plugin-dev/circle.yml
Normal file
@@ -0,0 +1,29 @@
|
||||
jobs:
|
||||
test:
|
||||
docker:
|
||||
- image: cypress/base:10
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
keys:
|
||||
- cache-{{ arch }}-{{ .Branch }}-{{ checksum "package.json" }}
|
||||
- run:
|
||||
name: Yarn install
|
||||
command: yarn install --frozen-lockfile
|
||||
- save_cache:
|
||||
key: cache-{{ arch }}-{{ .Branch }}-{{ checksum "package.json" }}
|
||||
paths:
|
||||
- ~/.cache
|
||||
- run:
|
||||
# test with installed eslint
|
||||
command: yarn run test
|
||||
- run:
|
||||
# test with eslint@5
|
||||
command: yarn add -D eslint@5.16.0 && yarn run test
|
||||
- run:
|
||||
command: yarn run semantic-release
|
||||
workflows:
|
||||
build:
|
||||
jobs:
|
||||
- test
|
||||
version: 2
|
||||
@@ -0,0 +1,16 @@
|
||||
const ruleComposer = require('eslint-rule-composer')
|
||||
const arrowBodyStyle = require('eslint/lib/rules/arrow-body-style')
|
||||
|
||||
module.exports = ruleComposer.filterReports(
|
||||
arrowBodyStyle,
|
||||
(problem, metadata) => {
|
||||
const problemIndex = metadata.sourceCode.getIndexFromLoc(problem.loc.start)
|
||||
const reportedToken = metadata.sourceCode.getTokenByRangeStart(problemIndex, { includeComments: true })
|
||||
|
||||
if (problem.node.loc.start.line === problem.node.loc.end.line) {
|
||||
return
|
||||
}
|
||||
|
||||
return !(reportedToken && reportedToken.type === 'Line' && /^-{2,}$/u.test(reportedToken.value))
|
||||
}
|
||||
)
|
||||
7
npm/eslint-plugin-dev/lib/custom-rules/index.js
Normal file
7
npm/eslint-plugin-dev/lib/custom-rules/index.js
Normal file
@@ -0,0 +1,7 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
module.exports =
|
||||
Object.assign({}, ...fs.readdirSync(__dirname)
|
||||
.filter((filename) => filename.endsWith('.js') && filename !== 'index.js')
|
||||
.map((filename) => ({ [filename.replace(/\.js$/u, '')]: require(path.resolve(__dirname, filename)) })))
|
||||
59
npm/eslint-plugin-dev/lib/custom-rules/no-only.js
Normal file
59
npm/eslint-plugin-dev/lib/custom-rules/no-only.js
Normal file
@@ -0,0 +1,59 @@
|
||||
let astUtils
|
||||
|
||||
try {
|
||||
astUtils = require('eslint/lib/util/ast-utils')
|
||||
} catch (e) {
|
||||
astUtils = require('eslint/lib/shared/ast-utils')
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: 'problem',
|
||||
docs: {
|
||||
description: 'stop .only\'s in spec files',
|
||||
category: 'Spec Issues',
|
||||
},
|
||||
messages: {
|
||||
noOnly: 'Found only: `{{callee}}`.',
|
||||
},
|
||||
|
||||
// uncomment to enable autoFix
|
||||
// fixable: 'code',
|
||||
},
|
||||
|
||||
create (context) {
|
||||
|
||||
const sourceCode = context.getSourceCode()
|
||||
|
||||
function getPropertyText (node) {
|
||||
const lines = sourceCode.getText(node).split(astUtils.LINEBREAK_MATCHER)
|
||||
|
||||
return lines[0]
|
||||
}
|
||||
|
||||
return {
|
||||
'CallExpression:exit' (node) {
|
||||
|
||||
const callee = node.callee
|
||||
|
||||
if (node.type === 'CallExpression' && callee.type === 'MemberExpression' && callee.property.name === 'only') {
|
||||
if (['it', 'describe', 'context'].includes(callee.object.name)) {
|
||||
context.report({
|
||||
node: callee.property,
|
||||
loc: callee.property.loc.start,
|
||||
messageId: 'noOnly',
|
||||
data: {
|
||||
callee: getPropertyText(callee.parent),
|
||||
},
|
||||
// uncomment to enable autoFix
|
||||
// fix(fixer) {
|
||||
// return fixer.replaceTextRange([callee.property.start - 1, callee.property.end], '')
|
||||
// }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
76
npm/eslint-plugin-dev/lib/custom-rules/no-return-before.js
Normal file
76
npm/eslint-plugin-dev/lib/custom-rules/no-return-before.js
Normal file
@@ -0,0 +1,76 @@
|
||||
const defaultTokens = ['it', 'describe', 'context', 'expect']
|
||||
// const debug = require('debug')('@cypress/dev')
|
||||
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: 'problem',
|
||||
docs: {
|
||||
description: 'Enforce no return before certain token names',
|
||||
category: 'Misc',
|
||||
},
|
||||
messages: {
|
||||
errorMessage: `\
|
||||
Found a 'return' after '{{token}}'\
|
||||
`,
|
||||
},
|
||||
|
||||
schema: [
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
tokens: {
|
||||
type: 'array',
|
||||
default: defaultTokens,
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
],
|
||||
// uncomment to enable autoFix
|
||||
fixable: 'code',
|
||||
|
||||
},
|
||||
|
||||
create (context) {
|
||||
|
||||
let tokens = defaultTokens
|
||||
|
||||
if (context.options.length) {
|
||||
tokens = typeof context.options[0].tokens === 'object' ? context.options[0].tokens : tokens
|
||||
}
|
||||
|
||||
return {
|
||||
'CallExpression:exit' (node) {
|
||||
|
||||
const callee = node.callee
|
||||
|
||||
if (
|
||||
(callee.type === 'Identifier')
|
||||
&& tokens.includes(callee.name)
|
||||
) {
|
||||
const t = context.getSourceCode().getTokenBefore(node)
|
||||
|
||||
// debug(t)
|
||||
|
||||
if (!(t && t.type === 'Keyword' && t.value === 'return')) return
|
||||
|
||||
const returnNode = t
|
||||
|
||||
context.report({
|
||||
node: callee,
|
||||
loc: callee.loc.start,
|
||||
messageId: 'errorMessage',
|
||||
data: {
|
||||
token: callee.name,
|
||||
},
|
||||
// uncomment to enable autoFix
|
||||
fix (fixer) {
|
||||
return fixer.replaceTextRange([returnNode.range[0], returnNode.range[1] + 1], '')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
87
npm/eslint-plugin-dev/lib/custom-rules/skip-comment.js
Normal file
87
npm/eslint-plugin-dev/lib/custom-rules/skip-comment.js
Normal file
@@ -0,0 +1,87 @@
|
||||
const defaultCommentTokens = ['NOTE:', 'TODO:', 'FIXME:']
|
||||
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: 'problem',
|
||||
docs: {
|
||||
description: 'stop .skip\'s in spec files',
|
||||
category: 'Spec Issues',
|
||||
},
|
||||
messages: {
|
||||
noOnly: `\
|
||||
Found a {{test-scope}}.skip(⋯) without an explanation.
|
||||
Add a comment above the '{{test-scope}}' starting with one of:
|
||||
{{commentTokens}}
|
||||
|
||||
e.g.
|
||||
// {{exampleCommentToken}} <reason test was skipped>
|
||||
{{test-scope}}.skip(⋯)
|
||||
|
||||
`,
|
||||
},
|
||||
|
||||
schema: [
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
commentTokens: {
|
||||
type: 'array',
|
||||
default: defaultCommentTokens,
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
],
|
||||
// uncomment to enable autoFix
|
||||
// fixable: 'code',
|
||||
|
||||
},
|
||||
|
||||
create (context) {
|
||||
|
||||
let commentTokens = defaultCommentTokens
|
||||
|
||||
if (context.options.length) {
|
||||
commentTokens = typeof context.options[0].commentTokens === 'object' ? context.options[0].commentTokens : commentTokens
|
||||
}
|
||||
|
||||
const sourceCode = context.getSourceCode()
|
||||
|
||||
return {
|
||||
'CallExpression:exit' (node) {
|
||||
|
||||
const callee = node.callee
|
||||
|
||||
const commentBefore = sourceCode.getCommentsBefore(node)
|
||||
|
||||
const hasExplain = commentBefore && commentBefore.map(
|
||||
(v) => commentTokens.concat(commentTokens.map((v) => `# ${v}`)).map((commentToken) => v.value.trim().startsWith(commentToken)).filter(Boolean)[0]
|
||||
).filter(Boolean)[0]
|
||||
|
||||
if (hasExplain) {
|
||||
return
|
||||
}
|
||||
|
||||
if (node.type === 'CallExpression' && callee.type === 'MemberExpression' && callee.property.name === 'skip') {
|
||||
if (['it', 'describe', 'context'].includes(callee.object.name)) {
|
||||
context.report({
|
||||
node: callee.property,
|
||||
loc: callee.property.loc.start,
|
||||
messageId: 'noOnly',
|
||||
data: {
|
||||
'test-scope': callee.object.name,
|
||||
commentTokens: commentTokens.join(' '),
|
||||
exampleCommentToken: commentTokens[0],
|
||||
},
|
||||
// uncomment to enable autoFix
|
||||
// fix(fixer) {
|
||||
// return fixer.replaceTextRange([callee.property.start - 1, callee.property.end], '')
|
||||
// }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
338
npm/eslint-plugin-dev/lib/index.js
Normal file
338
npm/eslint-plugin-dev/lib/index.js
Normal file
@@ -0,0 +1,338 @@
|
||||
const customRules = require('./custom-rules')
|
||||
const baseRules = {
|
||||
'@cypress/dev/arrow-body-multiline-braces': [
|
||||
'error',
|
||||
'always',
|
||||
],
|
||||
'array-bracket-newline': [
|
||||
'error',
|
||||
'consistent',
|
||||
],
|
||||
'array-bracket-spacing': [
|
||||
'error',
|
||||
'never',
|
||||
],
|
||||
'arrow-parens': [
|
||||
'error',
|
||||
'always',
|
||||
],
|
||||
'arrow-spacing': 'error',
|
||||
'block-spacing': 'error',
|
||||
'brace-style': [
|
||||
'error',
|
||||
'1tbs',
|
||||
{
|
||||
'allowSingleLine': false,
|
||||
},
|
||||
],
|
||||
'function-paren-newline': [
|
||||
'error',
|
||||
'consistent',
|
||||
],
|
||||
'comma-dangle': [
|
||||
'error',
|
||||
'always-multiline',
|
||||
],
|
||||
'comma-spacing': 'error',
|
||||
'curly': [
|
||||
'error',
|
||||
'multi-line',
|
||||
'consistent',
|
||||
],
|
||||
'constructor-super': 'error',
|
||||
'default-case': 'error',
|
||||
'eol-last': 'error',
|
||||
'eqeqeq': [
|
||||
'error',
|
||||
'allow-null',
|
||||
],
|
||||
'indent': [
|
||||
'error',
|
||||
2,
|
||||
{
|
||||
'SwitchCase': 1,
|
||||
'MemberExpression': 0,
|
||||
},
|
||||
],
|
||||
'key-spacing': 'error',
|
||||
'keyword-spacing': 'error',
|
||||
'no-buffer-constructor': 'error',
|
||||
'no-case-declarations': 'error',
|
||||
'no-class-assign': 'error',
|
||||
'no-cond-assign': 'error',
|
||||
'no-console': 'error',
|
||||
'no-const-assign': 'error',
|
||||
'no-constant-condition': 'error',
|
||||
'no-control-regex': 'error',
|
||||
'no-debugger': 'error',
|
||||
'no-delete-var': 'error',
|
||||
'no-dupe-class-members': 'error',
|
||||
'no-dupe-keys': 'error',
|
||||
'no-dupe-args': 'error',
|
||||
'no-duplicate-case': 'error',
|
||||
'no-duplicate-imports': 'error',
|
||||
'no-else-return': [
|
||||
'error',
|
||||
{
|
||||
'allowElseIf': false,
|
||||
},
|
||||
],
|
||||
'no-empty': 'error',
|
||||
'no-empty-character-class': 'error',
|
||||
'no-empty-pattern': 'error',
|
||||
'no-ex-assign': 'error',
|
||||
'no-extra-boolean-cast': 'error',
|
||||
'no-extra-semi': 'error',
|
||||
'no-fallthrough': 'error',
|
||||
'no-func-assign': 'error',
|
||||
'no-inner-declarations': 'error',
|
||||
'no-invalid-regexp': 'error',
|
||||
'no-irregular-whitespace': 'error',
|
||||
'no-mixed-spaces-and-tabs': 'error',
|
||||
'no-multiple-empty-lines': [
|
||||
'error',
|
||||
{
|
||||
'max': 1,
|
||||
'maxEOF': 0,
|
||||
'maxBOF': 0,
|
||||
},
|
||||
],
|
||||
'no-multi-spaces': 'error',
|
||||
'no-negated-in-lhs': 'error',
|
||||
'no-new-symbol': 'error',
|
||||
'no-obj-calls': 'error',
|
||||
'no-octal': 'error',
|
||||
'no-redeclare': 'error',
|
||||
'no-regex-spaces': 'error',
|
||||
'no-self-assign': 'error',
|
||||
'no-spaced-func': 'error',
|
||||
'no-sparse-arrays': 'error',
|
||||
'no-this-before-super': 'error',
|
||||
'no-trailing-spaces': 'error',
|
||||
'no-undef': 'error',
|
||||
'no-unexpected-multiline': 'error',
|
||||
'no-unneeded-ternary': 'error',
|
||||
'no-unreachable': 'error',
|
||||
'no-unused-labels': 'error',
|
||||
'no-unused-vars': ['error', { args: 'none' }],
|
||||
'no-useless-concat': 'error',
|
||||
'no-useless-constructor': 'error',
|
||||
'no-var': 'error',
|
||||
'no-whitespace-before-property': 'error',
|
||||
'object-curly-spacing': [
|
||||
'error',
|
||||
'always',
|
||||
],
|
||||
'object-shorthand': 'error',
|
||||
'one-var': [
|
||||
'error',
|
||||
'never',
|
||||
],
|
||||
'padded-blocks': ['error', 'never'],
|
||||
'padding-line-between-statements': [
|
||||
'error',
|
||||
{
|
||||
'blankLine': 'always',
|
||||
'prev': '*',
|
||||
'next': 'return',
|
||||
},
|
||||
{
|
||||
'blankLine': 'always',
|
||||
'prev': [
|
||||
'const',
|
||||
'let',
|
||||
'var',
|
||||
'if',
|
||||
'while',
|
||||
'export',
|
||||
'cjs-export',
|
||||
'import',
|
||||
'cjs-import',
|
||||
'multiline-expression',
|
||||
],
|
||||
'next': '*',
|
||||
},
|
||||
{
|
||||
'blankLine': 'any',
|
||||
'prev': [
|
||||
'const',
|
||||
'let',
|
||||
'var',
|
||||
'import',
|
||||
'cjs-import',
|
||||
],
|
||||
'next': [
|
||||
'const',
|
||||
'let',
|
||||
'var',
|
||||
'import',
|
||||
'cjs-import',
|
||||
],
|
||||
},
|
||||
],
|
||||
'prefer-rest-params': 'error',
|
||||
'prefer-spread': 'error',
|
||||
'prefer-template': 'error',
|
||||
'quotes': [
|
||||
'error',
|
||||
'single',
|
||||
{
|
||||
'allowTemplateLiterals': true,
|
||||
},
|
||||
],
|
||||
'semi': [
|
||||
'error',
|
||||
'never',
|
||||
],
|
||||
'semi-spacing': 'error',
|
||||
'space-before-blocks': 'error',
|
||||
'space-before-function-paren': 'error',
|
||||
'space-in-parens': [
|
||||
'error',
|
||||
'never',
|
||||
],
|
||||
'space-infix-ops': 'error',
|
||||
'space-unary-ops': 'error',
|
||||
'template-curly-spacing': 'error',
|
||||
'use-isnan': 'error',
|
||||
'valid-typeof': 'error',
|
||||
}
|
||||
|
||||
// '@cypress/dev/no-only': 'error',
|
||||
|
||||
module.exports = {
|
||||
configs: {
|
||||
general: {
|
||||
parserOptions: {
|
||||
ecmaVersion: 2018,
|
||||
sourceType: 'module',
|
||||
},
|
||||
plugins: [
|
||||
'json-format',
|
||||
],
|
||||
settings: {
|
||||
json: {
|
||||
'sort-package-json': 'pro',
|
||||
},
|
||||
},
|
||||
env: {
|
||||
node: true,
|
||||
es6: true,
|
||||
},
|
||||
rules: {
|
||||
...baseRules,
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: [
|
||||
'*.jsx',
|
||||
'*.tsx',
|
||||
],
|
||||
rules: {
|
||||
'@cypress/dev/arrow-body-multiline-braces': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: '*.coffee',
|
||||
parser: '@fellow/eslint-plugin-coffee',
|
||||
parserOptions: {
|
||||
parser: 'babel-eslint',
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 2018,
|
||||
},
|
||||
plugins: [
|
||||
'@fellow/eslint-plugin-coffee',
|
||||
],
|
||||
rules: {
|
||||
...Object.assign({}, ...Object.keys(baseRules).map((key) => ({ [key]: 'off' }))),
|
||||
'@fellow/coffee/coffeescript-error': [
|
||||
'error',
|
||||
{},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: '*.ts',
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: [
|
||||
'@typescript-eslint',
|
||||
],
|
||||
rules: {
|
||||
'no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
'args': 'none',
|
||||
},
|
||||
],
|
||||
'@typescript-eslint/type-annotation-spacing': 'error',
|
||||
'@typescript-eslint/no-useless-constructor': [
|
||||
'error',
|
||||
],
|
||||
'@typescript-eslint/member-delimiter-style': [
|
||||
'error',
|
||||
{
|
||||
'multiline': {
|
||||
'delimiter': 'none',
|
||||
},
|
||||
'singleline': {
|
||||
'delimiter': 'comma',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
tests: {
|
||||
env: {
|
||||
mocha: true,
|
||||
},
|
||||
globals: {
|
||||
expect: true,
|
||||
},
|
||||
plugins: ['mocha'],
|
||||
rules: {
|
||||
'mocha/handle-done-callback': 'error',
|
||||
'mocha/no-exclusive-tests': 'error',
|
||||
'mocha/no-global-tests': 'error',
|
||||
'@cypress/dev/skip-comment': 'error',
|
||||
},
|
||||
},
|
||||
|
||||
react: {
|
||||
env: {
|
||||
browser: true,
|
||||
},
|
||||
parser: 'babel-eslint',
|
||||
parserOptions: {
|
||||
ecmaVersion: 2018,
|
||||
sourceType: 'module',
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
legacyDecorators: true,
|
||||
},
|
||||
},
|
||||
plugins: ['react'],
|
||||
rules: {
|
||||
'react/jsx-curly-spacing': 'error',
|
||||
'react/jsx-equals-spacing': 'error',
|
||||
'react/jsx-filename-extension': 'error',
|
||||
'react/jsx-no-duplicate-props': 'error',
|
||||
'react/jsx-no-undef': 'error',
|
||||
'react/jsx-pascal-case': 'error',
|
||||
'react/jsx-uses-react': 'error',
|
||||
'react/jsx-uses-vars': 'error',
|
||||
'react/jsx-wrap-multilines': 'error',
|
||||
'react/no-unknown-property': 'error',
|
||||
'react/prefer-es6-class': 'error',
|
||||
'react/react-in-jsx-scope': 'error',
|
||||
'react/require-render-return': 'error',
|
||||
},
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
...customRules,
|
||||
},
|
||||
}
|
||||
5
npm/eslint-plugin-dev/lib/scripts/.eslintrc.json
Normal file
5
npm/eslint-plugin-dev/lib/scripts/.eslintrc.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"extends": [
|
||||
"plugin:@cypress/dev/tests"
|
||||
]
|
||||
}
|
||||
38
npm/eslint-plugin-dev/lib/scripts/lint-changed.js
Executable file
38
npm/eslint-plugin-dev/lib/scripts/lint-changed.js
Executable file
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const sh = require('shelljs')
|
||||
const utils = require('./utils')
|
||||
const _ = require('lodash')
|
||||
const chalk = require('chalk')
|
||||
|
||||
const start = () => {
|
||||
|
||||
const fix = process.argv.slice(2).includes('--fix')
|
||||
|
||||
return utils.lintFilesByName({
|
||||
// list only modified files
|
||||
getFilenames: () => {
|
||||
return _.union(
|
||||
sh.exec(`git diff --name-only --diff-filter=M`).split('\n'),
|
||||
sh.exec(`git diff --name-only --diff-filter=MA --staged`).split('\n')
|
||||
)
|
||||
},
|
||||
fix,
|
||||
})
|
||||
.then(({ failed, filenames }) => {
|
||||
if (failed) {
|
||||
process.exit(failed)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(chalk.bold(`${chalk.green(filenames.length)} files linted successfully`))
|
||||
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
if (!module.parent) {
|
||||
start()
|
||||
}
|
||||
|
||||
module.exports = { start }
|
||||
68
npm/eslint-plugin-dev/lib/scripts/lint-pre-commit.js
Executable file
68
npm/eslint-plugin-dev/lib/scripts/lint-pre-commit.js
Executable file
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const _ = require('lodash')
|
||||
const utils = require('./utils')
|
||||
const sh = require('shelljs')
|
||||
const chalk = require('chalk')
|
||||
|
||||
const start = () => {
|
||||
|
||||
const filesStaged = sh.exec(`git diff --name-only --diff-filter=MA --staged`).split('\n').filter(Boolean)
|
||||
const filesUnstaged = sh.exec(`git diff --name-only --diff-filter=M`).split('\n').filter(Boolean)
|
||||
const filesPartiallyStaged = _.intersection(filesStaged, filesUnstaged)
|
||||
const filesFullyStaged = _.difference(filesStaged, filesPartiallyStaged)
|
||||
|
||||
let fail = false
|
||||
let lintedFilesCount = 0
|
||||
|
||||
return utils.lintFilesByName({
|
||||
getFilenames: () => filesFullyStaged,
|
||||
fix: true,
|
||||
})
|
||||
.then(({ failed, filenames }) => {
|
||||
sh.exec(`git add ${sh.ShellString(filenames.join(' '))}`)
|
||||
|
||||
if (failed) {
|
||||
fail = true
|
||||
}
|
||||
|
||||
lintedFilesCount += filenames.length
|
||||
|
||||
return
|
||||
|
||||
})
|
||||
.then(() => {
|
||||
return utils.lintFilesByText({
|
||||
getFilenames: () => filesPartiallyStaged,
|
||||
getFileText: (f) => sh.exec(`git show :${sh.ShellString(f)}`),
|
||||
})
|
||||
})
|
||||
.then(({ failCount, filenames }) => {
|
||||
|
||||
if (failCount) {
|
||||
fail = true
|
||||
}
|
||||
|
||||
lintedFilesCount += filenames.length
|
||||
|
||||
return
|
||||
})
|
||||
.then(() => {
|
||||
|
||||
if (fail) {
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(chalk.bold(`${chalk.green(lintedFilesCount)} files linted successfully`))
|
||||
|
||||
return
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
if (!module.parent) {
|
||||
start()
|
||||
}
|
||||
|
||||
module.exports = { start }
|
||||
45
npm/eslint-plugin-dev/lib/scripts/lint-pre-push.js
Executable file
45
npm/eslint-plugin-dev/lib/scripts/lint-pre-push.js
Executable file
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const sh = require('shelljs')
|
||||
const utils = require('./utils')
|
||||
const chalk = require('chalk')
|
||||
const debug = require('debug')('lint-pre-push')
|
||||
|
||||
const start = () => {
|
||||
|
||||
const getFilenames = () => {
|
||||
const GIT_PARAMS = (process.env.HUSKY_GIT_PARAMS || 'origin').split(' ')
|
||||
const gitRemote = GIT_PARAMS[0]
|
||||
const gitBranch = sh.exec(`git branch`).grep(/\*/).split(/\s/)[1]
|
||||
const gitRemoteBranch = `${gitRemote}/${gitBranch}`
|
||||
|
||||
debug({ gitRemote })
|
||||
debug({ gitBranch })
|
||||
|
||||
return sh
|
||||
.exec(`git diff HEAD ${sh.ShellString(gitRemoteBranch)} --name-only`)
|
||||
.split('\n')
|
||||
|
||||
}
|
||||
|
||||
return utils.lintFilesByText({
|
||||
getFilenames,
|
||||
getFileText: (f) => sh.exec(`git show :${sh.ShellString(f)}`),
|
||||
})
|
||||
.then(({ failCount, filenames }) => {
|
||||
if (failCount) {
|
||||
process.exit(failCount)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(chalk.bold(`${chalk.green(filenames.length)} files linted successfully`))
|
||||
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
if (!module.parent) {
|
||||
start()
|
||||
}
|
||||
|
||||
module.exports = { start }
|
||||
30
npm/eslint-plugin-dev/lib/scripts/lint-staged.js
Executable file
30
npm/eslint-plugin-dev/lib/scripts/lint-staged.js
Executable file
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/* eslint-disable quotes */
|
||||
const sh = require('shelljs')
|
||||
const utils = require('./utils')
|
||||
const chalk = require('chalk')
|
||||
|
||||
const start = () => {
|
||||
return utils.lintFilesByText({
|
||||
// list only modified and added files
|
||||
getFilenames: () => sh.exec(`git diff --name-only --diff-filter=MA --staged`).split('\n'),
|
||||
getFileText: (f) => sh.exec(`git show :${sh.ShellString(f)}`),
|
||||
})
|
||||
.then(({ failCount, filenames }) => {
|
||||
if (failCount) {
|
||||
process.exit(failCount)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(chalk.bold(`${chalk.green(filenames.length)} files linted successfully`))
|
||||
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
if (!module.parent) {
|
||||
start()
|
||||
}
|
||||
|
||||
module.exports = { start }
|
||||
166
npm/eslint-plugin-dev/lib/scripts/lint.spec.js
Normal file
166
npm/eslint-plugin-dev/lib/scripts/lint.spec.js
Normal file
@@ -0,0 +1,166 @@
|
||||
const sh = require('shelljs')
|
||||
const sinon = require('sinon')
|
||||
const lintStaged = require('./lint-staged')
|
||||
const lintChanged = require('./lint-changed')
|
||||
const lintPrePush = require('./lint-pre-push')
|
||||
const lintPreCommit = require('./lint-pre-commit')
|
||||
const chai = require('chai')
|
||||
const debug = require('debug')('lint.spec')
|
||||
|
||||
const { expect } = chai
|
||||
|
||||
chai.use(require('sinon-chai'))
|
||||
|
||||
const _env = process.env
|
||||
const _argv = process.argv
|
||||
|
||||
const getStagedFiles = () => sh.ShellString('foo.js\nbar.js')
|
||||
const getUnstagedFiles = () => sh.ShellString('bar.js\nbaz.js')
|
||||
const getCommittedFiles = () => sh.ShellString('baz.js\nquux.js')
|
||||
|
||||
const eslintSuccess = (...args) => {
|
||||
debug('eslintSuccess:', args)
|
||||
const ret = sh.ShellString(`GOOD JS`)
|
||||
|
||||
ret.exec = sinon.stub().yields(null, 'success')
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
const eslintFailure = (...args) => {
|
||||
debug('eslintFailure:', args)
|
||||
const ret = sh.ShellString(`BAD JS`)
|
||||
|
||||
ret.exec = sinon.stub().yields('foo error')
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
sinon.stub(sh, 'exec')
|
||||
sinon.stub(sh, 'cat')
|
||||
sinon.stub(process, 'exit')
|
||||
|
||||
sh.exec
|
||||
.withArgs(`git branch`).returns(sh.ShellString('* mybranch'))
|
||||
|
||||
.withArgs(`git diff --name-only --diff-filter=MA --staged`)
|
||||
.returns(getStagedFiles())
|
||||
|
||||
.withArgs(`git diff --name-only --diff-filter=M`)
|
||||
.returns(getUnstagedFiles())
|
||||
|
||||
.withArgs(`git diff HEAD origin/mybranch --name-only`)
|
||||
.returns(getCommittedFiles())
|
||||
|
||||
sh.exec.callsFake(eslintSuccess)
|
||||
|
||||
})
|
||||
|
||||
describe('lint-staged', () => {
|
||||
it('lint success', async () => {
|
||||
await lintStaged.start()
|
||||
expect(process.exit).not.calledOnce
|
||||
})
|
||||
|
||||
it('lint failures', async () => {
|
||||
sh.exec.callsFake(eslintFailure)
|
||||
|
||||
await lintStaged.start()
|
||||
expect(process.exit).calledOnce
|
||||
})
|
||||
})
|
||||
|
||||
describe('lint-changed', () => {
|
||||
const filenames = 'bar.js baz.js foo.js'
|
||||
|
||||
beforeEach(() => {
|
||||
sh.exec
|
||||
.withArgs(`./node_modules/.bin/eslint --color=true '' ${filenames}`)
|
||||
.yields(null, 'success')
|
||||
})
|
||||
|
||||
it('lint success', async () => {
|
||||
await lintChanged.start()
|
||||
expect(process.exit).not.calledOnce
|
||||
})
|
||||
|
||||
it('lint failures', async () => {
|
||||
sh.exec
|
||||
.withArgs(`./node_modules/.bin/eslint --color=true '' ${filenames}`)
|
||||
.yields('foo error')
|
||||
|
||||
await lintChanged.start()
|
||||
expect(process.exit).calledOnce
|
||||
})
|
||||
|
||||
it('lint with --fix', async () => {
|
||||
process.argv = ['_', '_', '--fix']
|
||||
sh.exec
|
||||
.withArgs(`./node_modules/.bin/eslint --color=true --fix '' ${filenames}`)
|
||||
.yields(null, 'success')
|
||||
|
||||
await lintChanged.start()
|
||||
expect(process.exit).not.calledOnce
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('lint-pre-push', () => {
|
||||
beforeEach(() => {
|
||||
process.env.HUSKY_GIT_PARAMS = 'origin git@github.com:cypress-io/cypress.git'
|
||||
})
|
||||
|
||||
it('lint success', async () => {
|
||||
await lintPrePush.start()
|
||||
expect(process.exit).not.calledOnce
|
||||
})
|
||||
|
||||
it('lint failures', async () => {
|
||||
sh.exec.callsFake(eslintFailure)
|
||||
|
||||
await lintPrePush.start()
|
||||
expect(process.exit).calledOnce
|
||||
})
|
||||
})
|
||||
|
||||
describe('lint-pre-commit', () => {
|
||||
beforeEach(() => {
|
||||
sh.exec
|
||||
.withArgs(`./node_modules/.bin/eslint --color=true --fix '' foo.js`)
|
||||
.yields(null, 'success')
|
||||
|
||||
})
|
||||
|
||||
it('lint success', async () => {
|
||||
await lintPreCommit.start()
|
||||
expect(process.exit).not.calledOnce
|
||||
|
||||
expect(sh.exec.withArgs('git add foo.js')).calledOnce
|
||||
})
|
||||
|
||||
it('lint failures', async () => {
|
||||
sh.exec.callsFake(eslintFailure)
|
||||
|
||||
await lintPreCommit.start()
|
||||
expect(process.exit).calledOnce
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
process.argv = _argv
|
||||
process.env = _env
|
||||
sinon.restore()
|
||||
})
|
||||
|
||||
// sinon.addBehavior('withArgIncludes', (stub, str) => {
|
||||
|
||||
// })
|
||||
|
||||
// function withArgsInclude() {
|
||||
// this.
|
||||
// .callsFake((...args) => {
|
||||
// args[0].includes()
|
||||
|
||||
// })
|
||||
// }
|
||||
113
npm/eslint-plugin-dev/lib/scripts/utils.js
Normal file
113
npm/eslint-plugin-dev/lib/scripts/utils.js
Normal file
@@ -0,0 +1,113 @@
|
||||
const _ = require('lodash')
|
||||
const EE = require('events')
|
||||
const sh = require('shelljs')
|
||||
// const chalk = require('chalk')
|
||||
const Promise = require('bluebird')
|
||||
const debug = require('debug')('lint/util')
|
||||
|
||||
const filesRegex = /\.(js|jsx|ts|tsx|coffee|json|eslintrc)$/
|
||||
|
||||
Promise.config({
|
||||
warnings: true,
|
||||
longStackTraces: true,
|
||||
})
|
||||
|
||||
module.exports = {
|
||||
lintFilesByText: (options) => {
|
||||
sh.config.silent = true
|
||||
EE.defaultMaxListeners = 100
|
||||
|
||||
const opts = _.defaults(options, {
|
||||
getFilenames: null,
|
||||
getFileText: null,
|
||||
})
|
||||
|
||||
const filenames = opts.getFilenames().filter((v) => filesRegex.test(v))
|
||||
|
||||
debug(`linting:
|
||||
${filenames.join('\n\t')}
|
||||
`)
|
||||
|
||||
return Promise.map(filenames, (f) => {
|
||||
|
||||
debug('started linting', f)
|
||||
|
||||
const fileText = opts.getFileText(f)
|
||||
|
||||
debugTerse('file text:', fileText)
|
||||
|
||||
if (!fileText.toString()) return
|
||||
|
||||
const lintCommand = `./node_modules/.bin/eslint --stdin --stdin-filename ${sh.ShellString(f)} --color=true`
|
||||
|
||||
return Promise.promisify(fileText.exec)(
|
||||
lintCommand,
|
||||
{ silent: false, async: true }
|
||||
)
|
||||
.tapCatch(debugTerse)
|
||||
.return(false)
|
||||
.catchReturn(true)
|
||||
.finally(() => {
|
||||
debug('finished linting ', f)
|
||||
})
|
||||
}, { concurrency: 0 })
|
||||
.then((results) => {
|
||||
const failCount = _.filter(results).length
|
||||
|
||||
debug({ failCount })
|
||||
|
||||
return { failCount, filenames }
|
||||
})
|
||||
|
||||
},
|
||||
lintFilesByName: (options) => {
|
||||
sh.config.silent = true
|
||||
|
||||
const opts = _.defaults(options, {
|
||||
getFilenames: null,
|
||||
fix: false,
|
||||
})
|
||||
|
||||
const filenames = opts.getFilenames().filter((v) => filesRegex.test(v))
|
||||
|
||||
debug(`linting:
|
||||
${filenames.join('\n\t')}
|
||||
`)
|
||||
|
||||
const filenamesString = sh.ShellString(filenames.join(' '))
|
||||
|
||||
const lintCommand = opts.fix ?
|
||||
`./node_modules/.bin/eslint --color=true --fix '' ${filenamesString}`
|
||||
: `./node_modules/.bin/eslint --color=true '' ${filenamesString}`
|
||||
|
||||
return Promise.promisify(sh.exec)(
|
||||
lintCommand,
|
||||
{ silent: false, async: true }
|
||||
)
|
||||
.tapCatch(debugTerse)
|
||||
.return(false)
|
||||
.catchReturn(true)
|
||||
.then((failed) => {
|
||||
return {
|
||||
failed,
|
||||
filenames,
|
||||
}
|
||||
})
|
||||
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
const debugTerse = (...args) => {
|
||||
args = args.map((arg) => {
|
||||
let truncated = arg.toString().slice(0, 15)
|
||||
|
||||
if (truncated !== arg.toString()) {
|
||||
truncated = `${truncated}...`
|
||||
}
|
||||
|
||||
return truncated
|
||||
})
|
||||
|
||||
debug(...args)
|
||||
}
|
||||
81
npm/eslint-plugin-dev/package.json
Normal file
81
npm/eslint-plugin-dev/package.json
Normal file
@@ -0,0 +1,81 @@
|
||||
{
|
||||
"name": "@cypress/eslint-plugin-dev",
|
||||
"version": "0.0.0-development",
|
||||
"description": "Common ESLint rules shared by Internal Cypress packages",
|
||||
"main": "./lib",
|
||||
"scripts": {
|
||||
"lint": "eslint --ext .js,json,.eslintrc .",
|
||||
"lint-changed": "node ./lib/scripts/lint-changed",
|
||||
"lint-fix": "npm run lint -- --fix",
|
||||
"semantic-release": "semantic-release",
|
||||
"test": "jest"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"bluebird": "^3.5.5",
|
||||
"chalk": "^2.4.2",
|
||||
"eslint-rule-composer": "^0.3.0",
|
||||
"lodash": "^4.17.15",
|
||||
"shelljs": "^0.8.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cypress/eslint-plugin-dev": "file:./shim",
|
||||
"@fellow/eslint-plugin-coffee": "^0.4.13",
|
||||
"@types/chai": "^4.1.7",
|
||||
"@types/jest": "^24.0.15",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/sinon": "^7.0.13",
|
||||
"@types/sinon-chai": "^3.2.2",
|
||||
"chai": "^4.2.0",
|
||||
"eslint": "^6.1.0",
|
||||
"eslint-plugin-json-format": "^2.0.0",
|
||||
"eslint-plugin-mocha": "^5.3.0",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"husky": "^2.7.0",
|
||||
"jest": "^24.8.0",
|
||||
"jest-cli": "^24.8.0",
|
||||
"lint-staged": "^9.2.0",
|
||||
"mocha": "^6.1.4",
|
||||
"semantic-release": "^15.13.17",
|
||||
"sinon": "^7.3.2",
|
||||
"sinon-chai": "^3.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": ">= 1.11.0",
|
||||
"@typescript-eslint/parser": ">= 1.11.0",
|
||||
"babel-eslint": "^7.2.3",
|
||||
"eslint": ">= 3.2.1",
|
||||
"eslint-plugin-json-format": ">= 2.0.0",
|
||||
"eslint-plugin-mocha": "^4.11.0",
|
||||
"eslint-plugin-react": "^7.2.1"
|
||||
},
|
||||
"bin": {
|
||||
"lint-changed": "./lib/scripts/lint-changed.js",
|
||||
"lint-pre-commit": "./lib/scripts/lint-pre-commit.js"
|
||||
},
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/cypress-io/eslint-plugin-dev.git"
|
||||
},
|
||||
"homepage": "https://github.com/cypress-io/eslint-plugin-dev#readme",
|
||||
"author": "Chris Breiding (chris@cypress.io)",
|
||||
"bugs": {
|
||||
"url": "https://github.com/cypress-io/eslint-plugin-dev/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"cypress",
|
||||
"eslint",
|
||||
"eslintplugin"
|
||||
],
|
||||
"lint-staged": {
|
||||
"*.{js,jsx,ts,tsx,json,eslintrc}": [
|
||||
"eslint --fix",
|
||||
"git add"
|
||||
]
|
||||
}
|
||||
}
|
||||
5
npm/eslint-plugin-dev/shim/package.json
Normal file
5
npm/eslint-plugin-dev/shim/package.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "@cypress/eslint-plugin-dev",
|
||||
"version": "0.0.0",
|
||||
"main": "../../../lib"
|
||||
}
|
||||
8
npm/eslint-plugin-dev/test/.eslintrc
Normal file
8
npm/eslint-plugin-dev/test/.eslintrc
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": [
|
||||
"plugin:@cypress/dev/tests"
|
||||
],
|
||||
"env": {
|
||||
"jest": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
const path = require('path')
|
||||
const CLIEngine = require('eslint').CLIEngine
|
||||
const plugin = require('..')
|
||||
const _ = require('lodash')
|
||||
|
||||
const pluginName = '__plugin__'
|
||||
|
||||
function execute (file, options = {}) {
|
||||
const opts = _.defaultsDeep(options, {
|
||||
fix: true,
|
||||
config: {
|
||||
parserOptions: {
|
||||
ecmaVersion: 2018,
|
||||
sourceType: 'module',
|
||||
},
|
||||
},
|
||||
|
||||
})
|
||||
|
||||
const cli = new CLIEngine({
|
||||
parserOptions: {
|
||||
ecmaVersion: 2018,
|
||||
sourceType: 'module',
|
||||
},
|
||||
rules: {
|
||||
[`${pluginName}/arrow-body-multiline-braces`]: ['error', 'always'],
|
||||
},
|
||||
...opts,
|
||||
ignore: false,
|
||||
useEslintrc: false,
|
||||
plugins: [pluginName],
|
||||
})
|
||||
|
||||
cli.addPlugin(pluginName, plugin)
|
||||
const results = cli.executeOnFiles([path.join(__dirname, file)]).results[0]
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
describe('arrow-body-multiline-braces', () => {
|
||||
|
||||
it('lint multiline js', async () => {
|
||||
const filename = './fixtures/multiline.js'
|
||||
const result = execute(filename, {
|
||||
fix: true,
|
||||
})
|
||||
|
||||
expect(result.output).toContain('{')
|
||||
})
|
||||
|
||||
it('lint oneline js', async () => {
|
||||
const filename = './fixtures/oneline.js'
|
||||
const result = execute(filename, { fix: false })
|
||||
|
||||
expect(result.output).not.ok
|
||||
expect(result).toHaveProperty('errorCount', 0)
|
||||
})
|
||||
})
|
||||
6
npm/eslint-plugin-dev/test/fixtures/.eslintrc
vendored
Normal file
6
npm/eslint-plugin-dev/test/fixtures/.eslintrc
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2018,
|
||||
"sourceType": "module"
|
||||
}
|
||||
}
|
||||
9
npm/eslint-plugin-dev/test/fixtures/multiline.js
vendored
Normal file
9
npm/eslint-plugin-dev/test/fixtures/multiline.js
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
const foo = (fn) =>
|
||||
fn(
|
||||
'foo',
|
||||
'bar',
|
||||
)
|
||||
|
||||
foo()
|
||||
|
||||
10
npm/eslint-plugin-dev/test/fixtures/no-return-before-fail.js
vendored
Normal file
10
npm/eslint-plugin-dev/test/fixtures/no-return-before-fail.js
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
describe('outer', ()=>{
|
||||
return describe('some test', ()=>{
|
||||
return context('some test', ()=>{
|
||||
return it('some test', ()=>{
|
||||
return expect('foo').to.eq('bar')
|
||||
})
|
||||
return someFn()
|
||||
})
|
||||
})
|
||||
})
|
||||
10
npm/eslint-plugin-dev/test/fixtures/no-return-before-pass.js
vendored
Normal file
10
npm/eslint-plugin-dev/test/fixtures/no-return-before-pass.js
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
describe('outer', ()=>{
|
||||
describe('some test', ()=>{
|
||||
context('some test', ()=>{
|
||||
it('some test', ()=>{
|
||||
expect('foo').to.eq('bar')
|
||||
})
|
||||
return someFn()
|
||||
})
|
||||
})
|
||||
})
|
||||
5
npm/eslint-plugin-dev/test/fixtures/oneline.js
vendored
Normal file
5
npm/eslint-plugin-dev/test/fixtures/oneline.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
const foo = () => console.log('foo')
|
||||
|
||||
|
||||
foo()
|
||||
|
||||
10
npm/eslint-plugin-dev/test/fixtures/skip-comment-config.js
vendored
Normal file
10
npm/eslint-plugin-dev/test/fixtures/skip-comment-config.js
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// FOOBAR: im skipping this for good reason
|
||||
it.skip('some test', ()=>{
|
||||
|
||||
})
|
||||
|
||||
// NOTE: im skipping this for good reason
|
||||
it.skip('some test', ()=>{
|
||||
|
||||
})
|
||||
|
||||
11
npm/eslint-plugin-dev/test/fixtures/skip-comment-fail.js
vendored
Normal file
11
npm/eslint-plugin-dev/test/fixtures/skip-comment-fail.js
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
it.skip('some test', ()=>{
|
||||
|
||||
})
|
||||
|
||||
describe.skip('some test', ()=>{
|
||||
|
||||
})
|
||||
|
||||
context.skip('some test', ()=>{
|
||||
|
||||
})
|
||||
25
npm/eslint-plugin-dev/test/fixtures/skip-comment-pass.js
vendored
Normal file
25
npm/eslint-plugin-dev/test/fixtures/skip-comment-pass.js
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
// NOTE: im skipping this for good reason
|
||||
|
||||
it.skip('some test', ()=>{
|
||||
|
||||
})
|
||||
|
||||
// NOTE: im skipping this for good reason
|
||||
// some other line
|
||||
describe.skip('some test', ()=>{
|
||||
|
||||
})
|
||||
|
||||
/* NOTE: im skipping this for good reason */
|
||||
context.skip('some test', ()=>{
|
||||
|
||||
})
|
||||
|
||||
// TODO: im skipping this for good reason
|
||||
it.skip('some test', ()=>{
|
||||
|
||||
})
|
||||
//# TODO: im skipping this for good reason
|
||||
it.skip('some test', ()=>{
|
||||
|
||||
})
|
||||
11
npm/eslint-plugin-dev/test/fixtures/with-only.js
vendored
Normal file
11
npm/eslint-plugin-dev/test/fixtures/with-only.js
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
it.only('foo', () => {
|
||||
'foo'
|
||||
})
|
||||
|
||||
describe.only('foo', () => {
|
||||
'foo'
|
||||
})
|
||||
|
||||
context.only('foo', () => {
|
||||
'foo'
|
||||
})
|
||||
56
npm/eslint-plugin-dev/test/no-only.spec.js
Normal file
56
npm/eslint-plugin-dev/test/no-only.spec.js
Normal file
@@ -0,0 +1,56 @@
|
||||
const path = require('path')
|
||||
const CLIEngine = require('eslint').CLIEngine
|
||||
const plugin = require('..')
|
||||
const _ = require('lodash')
|
||||
|
||||
const ruleName = 'no-only'
|
||||
const pluginName = '__plugin__'
|
||||
|
||||
function execute (file, options = {}) {
|
||||
const opts = _.defaultsDeep(options, {
|
||||
fix: true,
|
||||
config: {
|
||||
parserOptions: {
|
||||
ecmaVersion: 2018,
|
||||
sourceType: 'module',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const cli = new CLIEngine({
|
||||
parserOptions: {
|
||||
ecmaVersion: 2018,
|
||||
sourceType: 'module',
|
||||
},
|
||||
rules: {
|
||||
[`${pluginName}/${ruleName}`]: ['error'],
|
||||
},
|
||||
...opts,
|
||||
ignore: false,
|
||||
useEslintrc: false,
|
||||
plugins: [pluginName],
|
||||
})
|
||||
|
||||
cli.addPlugin(pluginName, plugin)
|
||||
const results = cli.executeOnFiles([path.join(__dirname, file)]).results[0]
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
describe('no-only', () => {
|
||||
|
||||
it('lint js with only', async () => {
|
||||
const filename = './fixtures/with-only.js'
|
||||
const result = execute(filename, {
|
||||
fix: true,
|
||||
})
|
||||
|
||||
expect(result.errorCount).toBe(3)
|
||||
expect(result.messages[0].message).toContain('it')
|
||||
expect(result.messages[1].message).toContain('describe')
|
||||
expect(result.messages[2].message).toContain('context')
|
||||
|
||||
expect(result.output).not.toBeTruthy()
|
||||
})
|
||||
|
||||
})
|
||||
104
npm/eslint-plugin-dev/test/no-return-before.spec.js
Normal file
104
npm/eslint-plugin-dev/test/no-return-before.spec.js
Normal file
@@ -0,0 +1,104 @@
|
||||
const path = require('path')
|
||||
const CLIEngine = require('eslint').CLIEngine
|
||||
const plugin = require('..')
|
||||
const _ = require('lodash')
|
||||
const { stripIndent } = require('common-tags')
|
||||
|
||||
const ruleName = 'no-return-before'
|
||||
const pluginName = '__plugin__'
|
||||
|
||||
function execute (file, options = {}) {
|
||||
const opts = _.defaultsDeep(options, {
|
||||
fix: true,
|
||||
config: {
|
||||
parserOptions: {
|
||||
ecmaVersion: 2018,
|
||||
sourceType: 'module',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const cli = new CLIEngine({
|
||||
parserOptions: {
|
||||
ecmaVersion: 2018,
|
||||
sourceType: 'module',
|
||||
},
|
||||
rules: {
|
||||
[`${pluginName}/${ruleName}`]: ['error'],
|
||||
},
|
||||
...opts,
|
||||
ignore: false,
|
||||
useEslintrc: false,
|
||||
plugins: [pluginName],
|
||||
|
||||
})
|
||||
|
||||
cli.addPlugin(pluginName, plugin)
|
||||
const results = cli.executeOnFiles([path.join(__dirname, file)]).results[0]
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
describe(ruleName, () => {
|
||||
|
||||
it('pass', async () => {
|
||||
const filename = './fixtures/no-return-before-pass.js'
|
||||
const result = execute(filename)
|
||||
|
||||
expect(result.errorCount).toBe(0)
|
||||
})
|
||||
|
||||
it('fail', async () => {
|
||||
const filename = './fixtures/no-return-before-fail.js'
|
||||
const result = execute(filename, {
|
||||
fix: false,
|
||||
})
|
||||
|
||||
expect(result.errorCount).toBe(4)
|
||||
expect(result.messages[0].message).toContain(`after 'describe'`)
|
||||
})
|
||||
|
||||
it('fix fail', async () => {
|
||||
const filename = './fixtures/no-return-before-fail.js'
|
||||
const result = execute(filename)
|
||||
|
||||
expect(result.output).toEqual(`${stripIndent`
|
||||
describe('outer', ()=>{
|
||||
describe('some test', ()=>{
|
||||
context('some test', ()=>{
|
||||
it('some test', ()=>{
|
||||
expect('foo').to.eq('bar')
|
||||
})
|
||||
return someFn()
|
||||
})
|
||||
})
|
||||
})
|
||||
`}\n`)
|
||||
|
||||
})
|
||||
|
||||
describe('config', () => {
|
||||
it('config [tokens]', async () => {
|
||||
const filename = './fixtures/no-return-before-fail.js'
|
||||
const result = execute(filename, {
|
||||
fix: false,
|
||||
rules: {
|
||||
[`${pluginName}/${ruleName}`]: [
|
||||
'error', {
|
||||
tokens: ['someFn'],
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
expect(result.errorCount).toBe(1)
|
||||
// console.log(result.messages[0].message)
|
||||
|
||||
expect(result.messages[0].message).toContain('someFn')
|
||||
|
||||
expect(result.output).not.toBeTruthy()
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
96
npm/eslint-plugin-dev/test/skip-comment.spec.js
Normal file
96
npm/eslint-plugin-dev/test/skip-comment.spec.js
Normal file
@@ -0,0 +1,96 @@
|
||||
const path = require('path')
|
||||
const CLIEngine = require('eslint').CLIEngine
|
||||
const plugin = require('..')
|
||||
const _ = require('lodash')
|
||||
|
||||
const ruleName = 'skip-comment'
|
||||
const pluginName = '__plugin__'
|
||||
|
||||
function execute (file, options = {}) {
|
||||
const opts = _.defaultsDeep(options, {
|
||||
fix: true,
|
||||
config: {
|
||||
parserOptions: {
|
||||
ecmaVersion: 2018,
|
||||
sourceType: 'module',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const cli = new CLIEngine({
|
||||
parserOptions: {
|
||||
ecmaVersion: 2018,
|
||||
sourceType: 'module',
|
||||
},
|
||||
rules: {
|
||||
[`${pluginName}/${ruleName}`]: ['error'],
|
||||
},
|
||||
...opts,
|
||||
ignore: false,
|
||||
useEslintrc: false,
|
||||
plugins: [pluginName],
|
||||
})
|
||||
|
||||
cli.addPlugin(pluginName, plugin)
|
||||
const results = cli.executeOnFiles([path.join(__dirname, file)]).results[0]
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
describe('skip-comment', () => {
|
||||
|
||||
it('skip test with comment', async () => {
|
||||
const filename = './fixtures/skip-comment-pass.js'
|
||||
const result = execute(filename, {
|
||||
fix: true,
|
||||
})
|
||||
|
||||
expect(result.errorCount).toBe(0)
|
||||
})
|
||||
|
||||
it('skip test without comment', async () => {
|
||||
const filename = './fixtures/skip-comment-fail.js'
|
||||
const result = execute(filename, {
|
||||
fix: true,
|
||||
})
|
||||
|
||||
expect(result.errorCount).toBe(3)
|
||||
// console.log(result.messages[0].message)
|
||||
|
||||
expect(result.messages[0].message).toContain('it')
|
||||
expect(result.messages[0].message).toContain('NOTE:')
|
||||
expect(result.messages[0].message).toContain('TODO:')
|
||||
expect(result.messages[1].message).toContain('describe')
|
||||
expect(result.messages[1].message).toContain('NOTE:')
|
||||
expect(result.messages[2].message).toContain('context')
|
||||
expect(result.messages[2].message).toContain('NOTE:')
|
||||
|
||||
expect(result.output).not.toBeTruthy()
|
||||
})
|
||||
|
||||
describe('config', () => {
|
||||
it('skip test without comment', async () => {
|
||||
const filename = './fixtures/skip-comment-config.js'
|
||||
const result = execute(filename, {
|
||||
fix: true,
|
||||
rules: {
|
||||
[`${pluginName}/${ruleName}`]: [
|
||||
'error', {
|
||||
commentTokens: ['FOOBAR:'],
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
expect(result.errorCount).toBe(1)
|
||||
// console.log(result.messages[0].message)
|
||||
|
||||
expect(result.messages[0].message).toContain('it')
|
||||
expect(result.messages[0].message).toContain('FOOBAR:')
|
||||
|
||||
expect(result.output).not.toBeTruthy()
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
9053
npm/eslint-plugin-dev/yarn.lock
Normal file
9053
npm/eslint-plugin-dev/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user