はじめに
FrontEndのフレームワークに依存せず、JavaScriptとHTML、CSSに対してLintを行い、Visual Studio CodeとJenkinsでLint結果を確認できる開発環境を作ったので、その使い方を書きます。
コンセプト
- JavaScriptのLintにはESLintを使う
- HTMLのLintにはHTMLHintを使う
- CSSのLintにはCSSLintを使う
- 開発環境はVisual Studio Codeを使う
- CIツールはJenkinsを使う
- Jenkinsからは、手間がかからないようにLint結果のレポート形式をCheckStyle形式で統一する
- Visual Studio Codeの設定を合わせるため、EditorConfigの設定も含める
seedプロジェクト
frontend-lint-seedとしてGitHub上に公開しています。
開発環境のセットアップ
- Visual Studio Codeをインストールする。バージョンは1.0以上。
- 次のVisual Studio Code Extensionをインストールする。
- ext install EditorConfig
- ext install ESLint
- ext install HTMLHint
- ext install CSSLint
- プロジェクトをgit cloneし、npm install を行う。
使い方
セットアップが完了すればVisual Studio Code上でJavaScript、HTML、CSSのLintエラーが表示される。Jenkins用にCheckStyle形式のレポートを生成するには次のコマンドを実行する。Lintレポートはreports以下に生成される。
$ npm run lint
プロジェクト構成
app以下はサンプルです。Lintエラーが出るようにルールに違反したコードになっています。
.
├── .csslintrc
├── .editorconfig
├── .eslintrc
├── .htmlhintrc
├── .vscode
│ ├── settings.json
│ └── tasks.json
├── app
│ ├── index.html
│ ├── main.html
│ ├── scripts
│ │ ├── index.js
│ │ └── main.js
│ └── styles
│ ├── index.css
│ └── main.css
├── gulpfile.js
├── package.json
└── reports
├── csslint-checkstyle.xml
├── eslint-checkstyle.xml
└── htmlhint-checkstyle.xml
.vscode/settings.json
内容はほぼデフォルトのままですが、フォントをMac用には"Monaco"、Windows用には"Consolas"を指定しています。
{
// Controls the font family.
"editor.fontFamily": "Monaco, Consolas",
// Controls visibility of line numbers
"editor.lineNumbers": true,
// Controls the rendering size of tabs in characters. Accepted values: "auto", 2, 4, 6, etc. If set to "auto", the value will be guessed when a file is opened.
"editor.tabSize": 2,
// Controls if the editor will insert spaces for tabs. Accepted values: "auto", true, false. If set to "auto", the value will be guessed when a file is opened.
"editor.insertSpaces": true,
// Controls after how many characters the editor will wrap to the next line. Setting this to 0 turns on viewport width wrapping
"editor.wrappingColumn": 200,
// Controls whether the editor should render whitespace characters
"editor.renderWhitespace": true,
//-------- Files configuration --------
// Configure glob patterns for excluding files and folders.
"files.exclude": {
"**/.git": true,
"**/.svn": true,
"**/.DS_Store": true
},
// The default character set encoding to use when reading and writing files.
"files.encoding": "utf8",
//-------- Search configuration --------
// Configure glob patterns for excluding files and folders in searches. Inherits all glob patterns from the file.exclude setting.
"search.exclude": {
"**/node_modules": true,
"**/bower_components": true
}
}
.editorconfig
やろうと思えばJS、HTML、CSSでindent_sizeを変えるなどできるが現状は統一しています。
EditorConfigの設定についてはこちらを参照。
# http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
.eslintrc
ESLintルールのベースは"eslint:recommended"を使用している。必要に応じてカスタマイズすること。
ESLintのルールについてはこちらを参照。
{
"extends": "eslint:recommended",
"env": {
"node": true,
"browser": true
},
"rules": {
"semi": 2,
"quotes": [2, "single", "avoid-escape"]
},
"globals": {}
}
.htmlhintrc
HTMLHintルールはデフォルトから"doctype-first"をfalseに。Component用のHTMLテンプレートを書くことがあるため。
HTMLHintのルールについてはこちらを参照。
{
"tagname-lowercase": true,
"attr-lowercase": true,
"attr-value-double-quotes": true,
"doctype-first": false,
"tag-pair": true,
"tag-self-close": true,
"spec-char-escape": true,
"id-unique": true,
"src-not-empty": true,
"attr-no-duplication": true,
"title-require": true,
"doctype-html5": true,
"space-tab-mixed-disabled": "space"
}
.csslintrc
CSSLintルールはひとまずの仮決めです。今後運用してカスタマイズする。
CSSLintのルールについてはこちらを参照。
{
"adjoining-classes": true,
"box-model": true,
"box-sizing": true,
"bulletproof-font-face": false,
"compatible-vendor-prefixes": true,
"display-property-grouping": true,
"duplicate-background-images": true,
"duplicate-properties": true,
"empty-rules": true,
"fallback-colors": true,
"floats": false,
"font-faces": true,
"font-sizes": true,
"gradients": true,
"ids": false,
"import": false,
"important": true,
"known-properties": true,
"non-link-hover": true,
"outline-none": true,
"overqualified-elements": false,
"qualified-headings": true,
"regex-selectors": false,
"shorthand": false,
"star-property-hack": true,
"text-indent": false,
"underscore-property-hack": true,
"vendor-prefix": true,
"unique-headings": false,
"universal-selector": true,
"unqualified-attributes": false,
"zero-units": false
}
gulpfile.js
Lintの実行にはgulpを使っています。htmlhintの部分はゴリゴリでスマートさに欠けるが、まずは期待する結果を出すことを優先した。
var gulp = require('gulp');
var gulpLoadPlugins = require('gulp-load-plugins');
var $ = gulpLoadPlugins();
var del = require('del');
var vinylPaths = require('vinyl-paths');
var REPORT_DIR = 'reports/';
var ESLINT_FILE_NAME = 'eslint-checkstyle.xml';
var CSSLINT_FILE_NAME = 'csslint-checkstyle.xml';
var HTMLHINT_FILE_NAME = 'htmlhint-checkstyle.xml';
gulp.task('eslint', function () {
var fs = require('fs');
gulp.src('app/scripts/**/*.js')
.pipe($.eslint('.eslintrc'))
.pipe($.eslint.format('checkstyle', fs.createWriteStream(REPORT_DIR + ESLINT_FILE_NAME)));
});
gulp.task('csslint',
$.shell.task('csslint --format=checkstyle-xml app/styles > ' + REPORT_DIR + CSSLINT_FILE_NAME)
);
process.env.HTMLHINT_CHECKSTYLE_FILE = REPORT_DIR + HTMLHINT_FILE_NAME;
gulp.task('htmlhint', function () {
gulp.src('app/**/*.html')
.pipe($.htmlhint('.htmlhintrc'))
.pipe($.htmlhint.reporter('gulp-htmlhint-checkstyle-file-reporter'))
.pipe(gulp.dest(REPORT_DIR + '.tmp'))
.on('end', function () {
gulp.src(REPORT_DIR + '*.tmp.*')
.pipe(vinylPaths(del))
.pipe($.concat(HTMLHINT_FILE_NAME))
.pipe($.header('<?xml version="1.0" encoding="utf-8"?>\n<checkstyle version="4.3">\n'))
.pipe($.footer('\n</checkstyle>'))
.pipe(gulp.dest(REPORT_DIR))
.on('end', function () {
del([REPORT_DIR + '.tmp']);
});
});
});
gulp.task('lint', ['eslint', 'csslint', 'htmlhint'], function () {
gulp.src(REPORT_DIR + '*.xml')
.pipe($.prettyData({ type: 'prettify' }))
.pipe(gulp.dest(REPORT_DIR));
});
gulp.task('lint:clean', function () {
return del([REPORT_DIR + '*.*']);
});
gulp.task('default', ['lint:clean'], function () {
gulp.start('lint');
});
終わりに
- 各種ルールの設定は今後運用しながらカスタマイズする。
- Vidual Studio Codeで開発する際とCIツールで確認する際で同じルールでチェックできるのは便利。
- 一からこのような環境を作ると、各種Lintツール、gulp、Jenkinsとの連携などぞれぞれの要素ではまることになるので、その手間をなくして、本質的な開発に集中したい。
- よりよいやり方があったらぜひコメントやPRをもらえるとありがたいです。