Poy Chang's Blog

使用 JSHint 檢查 Cordova 專案程式碼

JavaScript 是一個好好先生,總是包容我們程式碼的小瑕疵,不和我們計較語法結尾是否有 ; 或是一些奇怪的打字錯誤,但總不能因為這樣的包容性,就讓程式碼老是髒髒的,不管程式碼品質這件事。

在 Cordova 專案中,可以在 hooks 中自訂一些建置流程,常用的 hooks 類別有下面四個

  • before_prepare
  • after_prepare
  • before_build
  • after_build

詳細的 hooks 類別可以參考 Cordova Hooks

其中官網中有說明 before_prepare 這個階段會在執行下面這四個指令時觸發

  • cordova prepare
  • cordova platform add
  • cordova build
  • cordova run

建立 JSHint 檢查機制

這篇文章希望能在 before_prepare 這個階段中去建立 JSHint 檢查機制。

首先在 Cordova 專案中的 hooks 資料夾中,新增 before_prepare 資料夾,並在該資料夾內新增 010_jshint.js 檔案。

這裡你可能會覺得檔名怪怪的,為什麼前面要加 010 呢?這是因為 Cordova Hooks 會透過 hooks 底下的資料夾名稱來判斷在哪個階段執行,並且該階段執行程序的順序,就是藉由檔名來安排。

依序執行的小測試

因此如果你在 before_prepare 資料夾下建立 010_test01.js020_test01.js030_test01.js 三個 JS 檔,每個檔案內依序寫入下列程式碼

1
2
3
#!/usr/bin/env node
// 不同檔名輸入不同的數字
console.log('test 1/2/3');

然後執行 cordova prepare 時,就會看到依序輸出 test 1/2/3

依序執行的小測試

程式碼

直接將下列程式碼複製至 010_jshint.js 中,之後執行 cordova prepare 時,就會檢查 www/js 這個資料夾底下的所有 JS 程式碼。

如果要檢查其他不在 www/js 這個資料夾的 JS 檔,請在 foldersToProcess 這個陣列中去添加即可。

如果不想每次都遞迴資料夾做檢查,那就把 recursiveFolderSearch 設定成 false 即可。

code

poychang/010_jshint.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#!/usr/bin/env node

var fs = require('fs');
var path = require('path');
var jshint = require('jshint').JSHINT;
var async = require('async');

var recursiveFolderSearch = true;
var foldersToProcess = [
'js'
];

foldersToProcess.forEach(function (folder) {
processFiles("www/" + folder);
});

function processFiles(dir, callback) {
var errorCount = 0;
fs.readdir(dir, function (err, list) {
if (err) {
console.error('Directory Error: ' + err);
return;
}
async.eachSeries(list, function (file, innercallback) {
file = dir + '/' + file;
fs.stat(file, function (err, stat) {
if (!stat.isDirectory()) {
if (path.extname(file) === ".js") {
lintFile(file, function (hasError) {
if (hasError) {
errorCount++;
}
innercallback();
});
} else {
innercallback();
}
} else {
if (stat.isDirectory() && recursiveFolderSearch) {
processFiles(file, callback);
} else {
innercallback();
}
innercallback();
}
});
}, function (error) {
if (errorCount > 0) {
console.error('Get ' + errorCount + ' error(s) by JSHint.');
process.exit(1);
}
});
});
}

function lintFile(file, callback) {
console.log("Linting " + file);
fs.readFile(file, function (err, data) {
if (err) {
console.log('Error: ' + err);
return;
}
if (jshint(data.toString())) {
console.log('File ' + file + ' has no errors.');
console.log('-----------------------------------------');
callback(false);
} else {
console.log('Errors in file ' + file);
var out = jshint.data(),
errors = out.errors;
for (var j = 0; j < errors.length; j++) {
console.log(errors[j].line + ':' + errors[j].character + ' -> ' + errors[j].reason + ' -> ' +
errors[j].evidence);
}
console.log('-----------------------------------------');
callback(true);
}
});
}

檔案編碼的雷,我踩了好幾次

如果在執行過程中,一直出現 Error: Hook failed with error code 1: 這樣的錯誤訊息,很有可能是你的 JS 檔的編碼格式不是用 Unicode (UTF-8 無簽章) 的格式,因此無法正確地執行檔案,變更一下檔案編碼就搞定了。


參考資料: