-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.js
More file actions
178 lines (145 loc) · 5.61 KB
/
index.js
File metadata and controls
178 lines (145 loc) · 5.61 KB
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
'use strict';
class gitCommitMsg {
constructor() {
this.fs = require('fs');
this.path = require('path');
this.root = this.path.resolve(__dirname, '..', '..');
this.package = require(this.path.join(this.root, 'package.json'));
this.message = this.fs.readFileSync(this.path.join(this.root, '.git/COMMIT_EDITMSG'), 'utf8');
this.config = {
types: [
'feat',
'fix',
'chore',
'docs',
'refactor',
'style',
'perf',
'test',
'revert'
],
lineLength: 72,
scope: {
mandatory: false,
rules: ""
}
}
this.init();
}
init() {
// Check if config exists and if we need to use the default one
if (this.package['git-commit-hook']) {
this.populateConfig();
}
// Breaking the message by line
this.message = this.message.split('\n');
// Check if heading is in the message
if (!this.checkTypeSubject()) {
this.stop();
}
if (!this.checkScope()) {
this.stop();
}
// Check if lines length is fine, check only if lineLength is integer and not boolean false
if (this.config.lineLength !== 0 && !this.checkLength()) {
this.stop();
}
process.exit(0);
}
// Replace default config with config from package.json
populateConfig() {
const config = this.package['git-commit-hook'];
// In case there is config.types available in package.json, check if it's array and if set, if set, replace default one
if (config.types && !Array.isArray(config.types)) {
console.error('git-commit-hook: config.types is suppose to be array');
this.stop();
} else if (config.types) {
this.config.types = config.types;
}
// In case there is config.lineLength available in package.json, check if it's number and if set, if set, replace default one
if (config.lineLength && typeof config.lineLength !== 'number') {
console.error('git-commit-hook: config.lineLength is suppose to be number, remove the rule or set to 0 if you don\'t want to force it');
this.stop();
} else if (config.lineLength) {
this.config.lineLength = config.lineLength;
}
// In case there is config.scope, replace default one
if (config.scope) {
this.config.scope = config.scope;
}
}
// Check length of every line of commit message and validate that there are no long lines in it
checkLength() {
// Start line with number one, to make it less confusing
let line = 1;
for (const message of this.message) {
// If line is longer than allowed, show error message and exit the process
if (message.length > this.config.lineLength) {
console.error(`git-commit-hook: Commit message in line ${line} have more characters than allowed in a single line, please break it down, maximum is ${this.config.lineLength}`);
return false;
}
line++;
}
return true;
}
// Check first line of commit message, if there is [Type]([optional scope]): [Subject]
// Semicolon with space is always mandatory after the Type
checkTypeSubject() {
const types = this.config.types.join('|');
let regStr = `(${types})(\(.*\))?\\:`;
const regExpType = new RegExp(regStr);
// Check type and semicolon
if (this.message[0].search(regExpType) === -1) {
console.error(`git-commit-hook: Type should follow the rules "${types}(scope/filename): Subject"`);
return false;
}
regStr += ' .*';
const regExpSubject = new RegExp(regStr);
// Check if there is subject after semicolon
if (this.message[0].search(regExpSubject) === -1) {
console.error('git-commit-hook: Subject is not set or there is no space after semicolon');
return false;
}
return true;
}
// Check if scope after type is mandatory
checkScope() {
// Check first if config for scope is set up
// If not, return true as default or old rules are set
if (!this.config.scope) {
return true;
}
// Check if rule for mandatory scope is set
// If not, return true
if (!this.config.scope.mandatory) {
return true;
}
let rules = this.config.scope.rules;
// Check if rules already have brackets
// If yes, strip them
if (rules.substr(0, 1) === '(' && rules.substr(-1) === ')') {
rules = rules.slice(1, -1);
}
// Add proper regex round brackets
// Add additional backslashes for string to work properly with regex
const regStr = '\\(' + rules + '\\)';
// Check if we have scope in round brackets
const regExpType = new RegExp(regStr);
// Check type and semicolon
if (this.message[0].search(regExpType) === -1) {
console.error(`git-commit-hook: Scope is not following the regex rules "${regStr}"`);
console.error(`git-commit-hook: Don't forget to put 2 backslashes instead of one`);
return false;
}
return true;
}
// Killing the process
stop() {
process.exit(1);
}
}
module.exports = gitCommitMsg;
if (module !== require.main) {
return;
}
new gitCommitMsg();