13
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

precommitで行うeslintのストレスをなくす

Posted at

なにができるか

  • commit前にcommitされるファイルで差分のあるものだけをチェックする
  • eslintの進捗が確認できる

前提知識

静的コード解析を取り入れている現場は多い。
そして、JSだとeslint --fix + lint-stagedで、変更分に対して、静的コード解析+コード修正を行うことが割とスタンダードだ。
そして、huskyを使って、コミット前にコードチェックを強制する光景もよく見る。

コミット前に Lint を強制するなら lint-staged が便利」はもろにその話。

不満

lint-stagedは、対象のプロジェクト全体に対し、インデックスにのっていない差分まですべてチェックしているようなので、機能を作ってからcommitを分けるスタイルの自分だとcommitのたびに時間がかかる。1

そして、そのときの進捗もわからないので、ちょっと別のことをやったりしていて集中が途切れたりした。

こうしたかった

  • commitをされる分だけ、チェックしたい
    • ファイル指定でcommitする場合はファイル単位でチェック
  • commitがどれくらいで終わるかを確認したい

こんなスクリプトを書いた

precommit.js

const child_process = require('child_process');
const path = require('path');

let exitCode = 0;

const errorLog = (stdout) => {
  const error = stdout.toString('utf8');
  if (error.length > 0) {
    console.log('\x1b[31m', error);
    exitCode = 1;
  }
};

const getChangedFiles = () => {
  const changedFiles = [];
  try {
    const str = child_process
      .execSync('git diff --cached --name-status | grep "\\.jsx\\?$"')
      .toString('utf8');
    // delimiter newline
    const changedLines = str
      .split(/(\r?\n)/g)
      .filter(line => !(line === '\n' || line === '\r' || line.length < 1));

    changedLines.forEach((l) => {
      // data = [gitStatus, fileName];
      const data = l.split('\t');
      // exclude `Delete` status files. To avoid `checkFileChanged` error
      if (data[0] !== 'D') {
        changedFiles.push(data[1]);
      }
    });
  } catch (e) {
    errorLog(e.stdout);
  }
  return changedFiles;
};

const lintFiles = (files) => {
  let progress = 0;
  files.forEach((file) => {
    progress += 1;
    console.log(`${progress}/${files.length} : ${file}`);
    try {
      child_process
        .execSync(
          `${path.join(__dirname, 'node_modules/.bin/eslint')} \
              -c ${path.join(__dirname, '.eslintrc')} \
              --ignore-path ${path.join(__dirname, '.eslintignore')} \
              --color --fix ${file}`
        )
        .toString('utf8');
    } catch (e) {
      errorLog(e.stdout);
    }
  });
};

// Stop commit if file changed by `eslint:fix`
const checkFileChanged = (files) => {
  try {
    const changedFiles = files.join(' ');
    const str = child_process
      .execSync(`git diff --name-only ${changedFiles}`)
      .toString('utf8');
    if (str.length > 0) {
      errorLog(`eslint fix changed following files : ${str}`);
      errorLog('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
      errorLog('!!!!!! Please commit again !!!!!!');
      errorLog('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
    }
  } catch (e) {
    errorLog(e.stdout);
  }
};

const changedFiles = getChangedFiles();
if (changedFiles.length > 0) {
  lintFiles(changedFiles);
  checkFileChanged(changedFiles);
}

process.exit(exitCode);

huskyを利用しているのであれば、package.jsonに以下のコードを入れるだけ。

"scripts": {
  "precommit" : "node ./precommit.js"
}

実行結果

eslint --fix の実行でファイルが変更された場合、commitが失敗するようになっている。失敗したら、整形後のファイルに対して再度commitが必要。

ローカルにてprettierを利用して、コード整形をしており、fixによる修正があまり発生しない前提で利用している。チェックするファイルが全4ファイルで、残り何ファイルチェックする必要があるかわかる。

% git commit -m 'commit message' .
Found '/Users/amachi/programs/project/.nvmrc' with version <v6.11.5>
Now using node v6.11.5 (npm v3.10.10)
husky > npm run -s precommit (node v6.11.5)

1/4 : app/js/containers/BaseRouter.jsx
...(中略)...
3/4 : app/js/lib/__tests__/getSubDomain.test.js
4/4 : app/js/lib/getSubDomain.js
[index_design fa9d7c2] commit message
 4 files changed, 97 insertions(+)
 create mode 100644 app/js/lib/__tests__/getModuleName.test.js
 create mode 100644 app/js/lib/__tests__/getSubDomain.test.js
 create mode 100644 app/js/lib/getSubDomain.js

本当は、precommitの間に変更されたファイルをそのままHEADに反映したいが、綺麗に実装する方法がわからないので、詳しい人に教えて欲しい。

取り入れた結果

体感速度があがったので、コミット中に他の用事をすることが減った。集中も途切れないで済むので、効率は上がったと信じている。

lint-stagedで同じようなことをしたかったが、現在そのようなオプションが見当たらなかったので、本当にないのであれば作ろうかと思っている。


  1. 挙動からみただけなので正しいかは不明だが、addするまえのステージにないファイルもチェックされる。lint-stagedという名前がついているのに、ステージに乗ってないものもチェックされているのがよく分かっていない...

13
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?