Skip to content

Commit 1d9ef62

Browse files
committed
package: initial implementation
Initial set of custom rules to enforce codestyle/patterns we've adopted at LogDNA * enforce sorted require declarations within modules * enforce file extension for local modules/files * enforce consistent aliases for tap assertions Semver: major
1 parent 349574f commit 1d9ef62

20 files changed

+1004
-1
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules/
2+
coverage/
3+
.nyc_output/
4+
.tap

.npmignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
*.log
2+
npm-debug.log*
3+
coverage
4+
.nyc_output
5+
*.tgz
6+
.env
7+
*.swp
8+
*.vim
9+
.npm
10+
Jenkinsfile

.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package-lock=false

.taprc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
esm: false
2+
ts: false
3+
jsx: false
4+
check-coverage: true
5+
100: true
6+
reporter: classic
7+
nyc-arg:
8+
- --all=true
9+
- --exclude=test/
10+
output-file: .tap
11+
browser: false
12+
coverage-report:
13+
- text
14+
- text-summary
15+
- html
16+
files:
17+
- test/lib

Jenkinsfile

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
library 'magic-butler-catalogue'
2+
3+
def PROJECT_NAME = "eslint-plugin-logdna"
4+
def REPO = "logdna/${PROJECT_NAME}"
5+
def TRIGGER_PATTERN = ".*@logdnabot.*"
6+
7+
pipeline {
8+
agent none
9+
10+
options {
11+
timestamps()
12+
ansiColor 'xterm'
13+
}
14+
15+
triggers {
16+
issueCommentTrigger(TRIGGER_PATTERN)
17+
}
18+
19+
stages {
20+
stage('Validate PR Source') {
21+
when {
22+
expression { env.CHANGE_FORK }
23+
not {
24+
triggeredBy 'issueCommentCause'
25+
}
26+
}
27+
steps {
28+
error("A maintainer needs to approve this PR for CI by commenting")
29+
}
30+
}
31+
32+
stage('Test Suite') {
33+
matrix {
34+
axes {
35+
axis {
36+
name 'NODE_VERSION'
37+
values '10', '12', '14'
38+
}
39+
}
40+
41+
agent {
42+
docker {
43+
image "us.gcr.io/logdna-k8s/node:${NODE_VERSION}-ci"
44+
}
45+
}
46+
47+
environment {
48+
NPM_CONFIG_CACHE = '.npm'
49+
NPM_CONFIG_USERCONFIG = '.npm/rc'
50+
SPAWN_WRAP_SHIM_ROOT = '.npm'
51+
}
52+
53+
stages {
54+
stage('Install') {
55+
56+
steps {
57+
sh 'mkdir -p .npm'
58+
sh """
59+
npm install
60+
"""
61+
}
62+
}
63+
64+
stage('Test') {
65+
steps {
66+
sh 'node -v'
67+
sh 'npm -v'
68+
sh 'npm test'
69+
}
70+
71+
post {
72+
always {
73+
sh 'cat .tap | ./node_modules/.bin/tap-mocha-reporter xunit > coverage/test.xml'
74+
75+
junit 'coverage/test.xml'
76+
77+
publishHTML target: [
78+
allowMissing: false,
79+
alwaysLinkToLastBuild: false,
80+
keepAll: true,
81+
reportDir: 'coverage/lcov-report',
82+
reportFiles: 'index.html',
83+
reportName: "coverage-node-v${NODE_VERSION}"
84+
]
85+
}
86+
}
87+
}
88+
}
89+
}
90+
}
91+
92+
stage('Test Release') {
93+
when {
94+
beforeAgent true
95+
not {
96+
branch 'master'
97+
}
98+
}
99+
100+
agent {
101+
docker {
102+
image "us.gcr.io/logdna-k8s/node:12-ci"
103+
customWorkspace "${PROJECT_NAME}-${BUILD_NUMBER}"
104+
}
105+
}
106+
107+
environment {
108+
GITHUB_PACKAGES_TOKEN = credentials('github-api-token')
109+
NPM_CONFIG_CACHE = '.npm'
110+
NPM_CONFIG_USERCONFIG = '.npm/rc'
111+
SPAWN_WRAP_SHIM_ROOT = '.npm'
112+
}
113+
114+
steps {
115+
sh 'mkdir -p .npm'
116+
versioner(
117+
token: "${GITHUB_PACKAGES_TOKEN}"
118+
, dry: true
119+
, repo: REPO
120+
, branch: "master"
121+
)
122+
}
123+
}
124+
125+
stage('Release') {
126+
when {
127+
beforeAgent true
128+
branch 'master'
129+
}
130+
131+
agent {
132+
docker {
133+
image "us.gcr.io/logdna-k8s/node:12-ci"
134+
customWorkspace "${PROJECT_NAME}-${BUILD_NUMBER}"
135+
}
136+
}
137+
138+
environment {
139+
NPM_CONFIG_CACHE = '.npm'
140+
NPM_CONFIG_USERCONFIG = '.npm/rc'
141+
SPAWN_WRAP_SHIM_ROOT = '.npm'
142+
GITHUB_PACKAGES_TOKEN = credentials('github-api-token')
143+
NPM_PUBLISH_TOKEN = credentials('npm-publish-token')
144+
}
145+
146+
steps {
147+
sh 'mkdir -p .npm'
148+
sh "git checkout -b ${GIT_BRANCH} origin/${GIT_BRANCH}"
149+
150+
versioner(
151+
token: "${GITHUB_PACKAGES_TOKEN}"
152+
, dry: false
153+
, repo: REPO
154+
, NPM_PUBLISH_TOKEN: "${NPM_PUBLISH_TOKEN}"
155+
, branch: "master"
156+
)
157+
}
158+
}
159+
}
160+
}

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright © 2020 LogDNA
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 120 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,121 @@
1-
# eslint-plugin-logdna
1+
# `eslint-plugin-logdna`
22

3+
> ESlint plugin containing a collection of rules for enforcing code style at LogDNA
4+
5+
## Installation
6+
7+
**Requires eslint also**
8+
9+
We do not use peer dependencies, so make sure that `eslint` is also installed as a dev dependency.
10+
11+
```shell
12+
npm install eslint-plugin-logdna eslint --save-dev
13+
```
14+
15+
## Usage
16+
17+
Add `logdna` to the plugins section of your ESLint configuration. You can omit
18+
the `eslint-plugin-` prefix. Then you can configure the rules you want to use:
19+
20+
```json
21+
{
22+
"plugins": [
23+
"logdna"
24+
],
25+
"rules": {
26+
"logdna/grouped-require": "error",
27+
"logdna/require-file-extension": "error",
28+
"logdna/tap-consistent-assertions": {
29+
"preferredMap": {
30+
"equal": "strictEqual"
31+
}
32+
}
33+
}
34+
}
35+
```
36+
37+
## Rules
38+
39+
### `logdna/grouped-require`
40+
41+
Enforce sorted require declarations within modules
42+
43+
```js
44+
// Bad
45+
const foo = require('./lib/foo.js') //local
46+
const http = require('http') // builtin
47+
require('foo') // static
48+
const logger = require('@logdna/logger') // scoped
49+
const tap = require('tap') // contrib
50+
51+
// Good
52+
require('foo') // static
53+
const http = require('http') // builtin
54+
const tap = require('tap') // contrib
55+
const logger = require('@logdna/logger') // scoped
56+
const foo = require('./lib/foo.js') //local
57+
```
58+
59+
#### Options
60+
61+
* `typeOrder` [`<Array>`][] - sort order of require types
62+
(default: `['static', 'builtin', 'contrib', 'scoped', 'local']`)
63+
64+
### `logdna/tap-consistent-assertions`
65+
66+
Enforce consistent aliases for tap assertions
67+
68+
```js
69+
// {
70+
// "plugins": [
71+
// "logdna"
72+
// ],
73+
// "rules": {
74+
// "logdna/tap-consistent-assertions": {
75+
// "preferredMap": {
76+
// "equal": "strictEqual"
77+
// }
78+
// }
79+
// }
80+
// }
81+
82+
// Bad
83+
test('foo', async (t) => {
84+
t.is_equal(1, 1)
85+
t.equal(1, 1)
86+
t.identical(1, 1)
87+
})
88+
89+
// Good
90+
test('foo', async (t) => {
91+
t.strictEqual(1, 1)
92+
t.strictEqual(1, 1)
93+
t.strictEqual(1, 1)
94+
})
95+
```
96+
97+
#### Options
98+
* `preferredMap` [`<Object>`][] - maps the "primary" assertion method to the preferred alias
99+
* `calleePattern` [`<String>`][] - pattern to match for tap's `Test` object (default: `/^t+$/`)
100+
101+
### `logdna/require-file-extension`
102+
103+
Enforce file extension for local modules/files
104+
105+
```js
106+
// Bad
107+
const foo = require('./lib/foo')
108+
109+
// Good
110+
const foo = require('./lib/foo.js')
111+
```
112+
113+
## License
114+
115+
Copyright © [LogDNA](https://logdna.com), released under an MIT license. See the [LICENSE](./LICENSE) file and https://opensource.org/licenses/MIT
116+
117+
*Happy Logging!*
118+
119+
[`<Object>`]: https://mdn.io/object
120+
[`<String>`]: https://mdn.io/string
121+
[`<Array>`]: https://mdn.io/array

lib/common/require-type.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
'use strict'
2+
3+
const isBuiltinModule = require('is-builtin-module')
4+
const {get} = require('dot-prop')
5+
6+
module.exports = {
7+
isRequire
8+
, isStaticRequire
9+
, isBuiltin
10+
, isScoped
11+
, isLocal
12+
, isTopLevel
13+
}
14+
15+
function isRequire(node) {
16+
const value = get(node, 'declarations.0.init.callee.name')
17+
return !!(value && value === 'require')
18+
}
19+
20+
function isStaticRequire(node) {
21+
if (!node || node.type !== 'CallExpression') return false
22+
const calleeType = get(node, 'callee.type')
23+
const calleeName = get(node, 'callee.name')
24+
25+
return (
26+
node.arguments.length === 1
27+
&& calleeType === 'Identifier'
28+
&& calleeName === 'require'
29+
)
30+
}
31+
32+
function isScoped(node) {
33+
const value = get(node, 'declarations.0.init.arguments.0.value')
34+
return !!(value && value.startsWith('@'))
35+
}
36+
37+
function isBuiltin(node) {
38+
const value = get(node, 'declarations.0.init.arguments.0.value')
39+
return !!(value && isBuiltinModule(value))
40+
}
41+
42+
function isLocal(node) {
43+
const value = get(node, 'declarations.0.init.arguments.0.value')
44+
return !!(value && value.startsWith('.'))
45+
}
46+
47+
function isTopLevel(node) {
48+
const value = get(node, 'parent.type')
49+
return !!(value && value === 'Program')
50+
}

0 commit comments

Comments
 (0)