-
-
Notifications
You must be signed in to change notification settings - Fork 10
ESLintの実装サンプル #29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
ESLintの実装サンプル #29
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
df717bd
feat(ESLint): ESLintのシンプルな実装を追加
azu 4205236
chore(ESLint) rename MyLint to MyLinter
azu 1744708
test(example): test to run *-example.js
azu a2444cd
refactor(test): move `--recursive` to mocha.opts
azu cb5b318
chore(npm): add babel
azu a178012
refactor(npm): move to modules relates with MyLinter tot dependencies
azu b001c43
feat(ESLint): 実装についてを追加
azu 4397275
fix(ESLint): fix inline code
azu ac809c1
refactor(ESLint): 文章の細かい内容を修正
azu 0ca956a
fix(ESLint): fix example
azu c3ec702
fix(ESLint): add no-console to config for Markdown
azu File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
{ | ||
"rules": { | ||
"no-undef": 0 | ||
"no-undef": 0, | ||
"no-console": 0 | ||
}, | ||
"plugins": [ | ||
"markdown" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
# ESLint | ||
|
||
[ESLint](http://eslint.org/ "ESLint")はJavaScriptのコードをJavaScriptで書かれたルールによって検証するLintツールです。 | ||
|
||
大まかな動作としては、検証したいJavaScriptのコードをパースしてできたAST(抽象構文木)を、ルールで検証し、エラーや警告を出力します。 | ||
|
||
このルールがプラグインとして書けるようになっていて、ESLintの全てのルールがプラグインとして実装されています。 | ||
|
||
> The pluggable linting utility for JavaScript and JSX | ||
|
||
ESLintサイト上には、上記のように書かれていることからもわかりますが、プラグインに重きを置いた設計となっているので、 | ||
今回はESLintのプラグインアーキテクチャについてを見て行きましょう | ||
|
||
## どう書ける? | ||
|
||
[import, no-console.js](../../src/ESLint/no-console.js) | ||
|
||
## どういう仕組み? | ||
## どういう用途に向いている? | ||
## どういう用途に向いていない? | ||
## この仕組みを使ってるもの | ||
## 実装してみよう | ||
|
||
今回は、ESLintのルールを解釈できるシンプルなLintの処理を書いてみます。 | ||
|
||
利用するルールは先ほども出てきた[no-console.js](#no-console.js)をそのまま使い、 | ||
このルールを使って同じようにJavaScriptのコードを検証できる`MyLinter`を書いてみます。 | ||
|
||
### MyLinter | ||
|
||
MyLinterは単純な2つのメソッドを持つクラスとして実装しました。 | ||
|
||
- `MyLinter#loadRule(rule): void` | ||
- 利用するルールを登録する処理 | ||
- `rule`は[no-console.js](#no-console.js)がexportしたもの | ||
- `MyLinter#lint(code): string[]` | ||
- `code`を受け取りルールによってLintした結果を返す | ||
- Lint結果はエラーメッセージの配列とする | ||
|
||
実装したものが以下のようになっています。 | ||
|
||
[import, src/ESLint/MyLinter.js](../../src/ESLint/MyLinter.js) | ||
|
||
このMyLinterを使って、`MyLinter#load`で[no-console.js](#no-console.js)を読み込ませて、 | ||
|
||
```js | ||
function add(x, y){ | ||
console.log(x, y); | ||
return x + y; | ||
} | ||
add(1, 3); | ||
``` | ||
|
||
というコードをLintしてみます。 | ||
|
||
[import, src/ESLint/MyLinter-example.js](../../src/ESLint/MyLinter-example.js) | ||
|
||
コードには`console`という名前のオブジェクトが含まれているので、 _"Unexpected console statement."_ というエラーメッセージが取得出来ました。 | ||
|
||
### RuleContext | ||
|
||
もう一度、[MyLinter.js](#MyLinter.js)を見てみると、`RuleContext`というシンプルなクラスがあることに気づくと思います。 | ||
|
||
この`RuleContext`はルールから使えるユーティリティメソッドをまとめたもので、 | ||
今回は`RuleContext#report`というエラーメッセージをルールからMyLinterへ通知するものだけを実装しています。 | ||
|
||
ルールの実装の方を見てみると、直接オブジェクトをexportしてる訳ではなく、 | ||
`context` つまり`RuleContext`のインスタンスを受け取っていることが分かると思います。 | ||
|
||
[import, no-console.js](../../src/ESLint/no-console.js) | ||
|
||
このようにして、ルールは `context` という与えられたものだけを使うので、ルールができることを制御しやすくなり、 | ||
ルールがMyLinter本体の実装の詳細を知らなくても良くなります。 | ||
|
||
## エコシステム |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
"use strict"; | ||
import assert from "assert"; | ||
import MyLinter from "./MyLinter"; | ||
import noConsole from "./no-console"; | ||
|
||
let linter = new MyLinter(); | ||
linter.loadRule(noConsole); | ||
var code = ` | ||
function add(x, y){ | ||
console.log(x, y); | ||
return x + y; | ||
} | ||
add(1, 3); | ||
`; | ||
var results = linter.lint(code); | ||
assert(results.length > 0); | ||
assert.equal(results[0], "Unexpected console statement."); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
"use strict"; | ||
import {parse} from "esprima"; | ||
import {traverse} from "estraverse"; | ||
import {EventEmitter} from "events"; | ||
class RuleContext extends EventEmitter { | ||
report(node, message) { | ||
this.emit("report", message); | ||
} | ||
} | ||
export default class MyLinter { | ||
constructor() { | ||
this._emitter = new EventEmitter(); | ||
this._ruleContext = new RuleContext(); | ||
} | ||
|
||
loadRule(rule) { | ||
var ruleExports = rule(this._ruleContext); | ||
// on(nodeType, nodeTypeCallback); | ||
Object.keys(ruleExports).forEach(nodeType => { | ||
this._emitter.on(nodeType, ruleExports[nodeType]); | ||
}); | ||
} | ||
|
||
|
||
lint(code) { | ||
var messages = []; | ||
var addMessage = (message)=> { | ||
messages.push(message); | ||
}; | ||
this._ruleContext.on("report", addMessage); | ||
var ast = parse(code); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. これパースしてから |
||
traverse(ast, { | ||
enter: (node) => { | ||
this._emitter.emit(node.type, node); | ||
}, | ||
leave: (node) => { | ||
this._emitter.emit(`${node.type}:exit`, node); | ||
} | ||
}); | ||
this._ruleContext.removeListener("report", addMessage); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. クラスにstate持つほどでもないと思ったので、lintし終わった開放してる。 |
||
return messages; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
"use strict"; | ||
module.exports = function (context) { | ||
return { | ||
"MemberExpression": function (node) { | ||
if (node.object.name === "console") { | ||
context.report(node, "Unexpected console statement."); | ||
} | ||
} | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// LICENSE : MIT | ||
"use strict"; | ||
import assert from "power-assert"; | ||
import MyLinter from "../../src/ESLint/MyLinter"; | ||
import noConsole from "../../src/ESLint/no-console"; | ||
describe("MyLint", function () { | ||
it("should load and lint", function () { | ||
let linter = new MyLinter(); | ||
linter.loadRule(noConsole); | ||
var results = linter.lint(`console.log("test")`); | ||
assert(results.length > 0); | ||
assert.equal(results[0], "Unexpected console statement."); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
--recursive | ||
--compilers js:espower-babel/guess |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
*-example.jsを実行するテストを追加