@polygontech/commit-template 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.czrc.json +5 -0
- package/CZRC.js +131 -0
- package/CZRCLoader.js +117 -0
- package/LICENSE +21 -0
- package/README.md +6 -0
- package/dto/author.js +6 -0
- package/dto/issue.js +6 -0
- package/dto/message.js +121 -0
- package/dto/type.js +8 -0
- package/formatter/errors/decomposing_error.js +8 -0
- package/formatter/errors/invalid_author.js +9 -0
- package/formatter/errors/invalid_scope.js +9 -0
- package/formatter/errors/invalid_type.js +9 -0
- package/formatter/errors/malformed_string.js +10 -0
- package/formatter/errors/max_length_exceeded.js +9 -0
- package/formatter/errors/validation_error.js +8 -0
- package/formatter/helpers.js +16 -0
- package/formatter/to_object.js +128 -0
- package/formatter/to_string.js +105 -0
- package/index.js +40 -0
- package/package.json +27 -0
package/.czrc.json
ADDED
package/CZRC.js
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
const pad = require("pad");
|
|
2
|
+
const fuzzy = require("fuzzy");
|
|
3
|
+
|
|
4
|
+
function CZRC() {
|
|
5
|
+
this.types = [];
|
|
6
|
+
this.issueTrackers = [];
|
|
7
|
+
this.defaultIssueTracker = "";
|
|
8
|
+
this.authors = [];
|
|
9
|
+
this.scopes = [];
|
|
10
|
+
this.subjectMaxLength = 72;
|
|
11
|
+
this.bodyMaxLength = 80;
|
|
12
|
+
this.sectionHeaders = {};
|
|
13
|
+
this.bullet = "- ";
|
|
14
|
+
this.inlineSeparator = ", ";
|
|
15
|
+
this.issueTrackerIdSeparator = ": ";
|
|
16
|
+
this.authorEmailTag = { start: " <", end: ">" };
|
|
17
|
+
this.blockNonImperativeSubject = true;
|
|
18
|
+
this.extraImperativeVerbs = [];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
CZRC.prototype.overwrite = function (overwrite) {
|
|
22
|
+
if (overwrite === null) return;
|
|
23
|
+
|
|
24
|
+
this.types = overwrite.types || this.types;
|
|
25
|
+
this.issueTrackers = overwrite.issueTrackers || this.issueTrackers;
|
|
26
|
+
this.defaultIssueTracker = this.hasIssueTracker() ? overwrite.defaultIssueTracker || this.defaultIssueTracker : "";
|
|
27
|
+
this.authors = overwrite.authors || this.authors;
|
|
28
|
+
this.scopes = overwrite.scopes || this.scopes;
|
|
29
|
+
this.subjectMaxLength = overwrite.subjectMaxLength || this.subjectMaxLength;
|
|
30
|
+
this.bodyMaxLength = overwrite.bodyMaxLength || this.bodyMaxLength;
|
|
31
|
+
this.sectionHeaders = overwrite.sectionHeaders || this.sectionHeaders;
|
|
32
|
+
this.bullet = overwrite.bullet || this.bullet;
|
|
33
|
+
this.inlineSeparator = overwrite.inlineSeparator || this.inlineSeparator;
|
|
34
|
+
this.issueTrackerIdSeparator = overwrite.issueTrackerIdSeparator || this.issueTrackerIdSeparator;
|
|
35
|
+
this.authorEmailTag = overwrite.authorEmailTag || this.authorEmailTag;
|
|
36
|
+
this.blockNonImperativeSubject = overwrite.blockNonImperativeSubject === false ? false : true;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
CZRC.prototype.pushExtraImperativeVerbs = function (extraImperativeVerbs) {
|
|
40
|
+
if (!Array.isArray(extraImperativeVerbs)) return;
|
|
41
|
+
if (extraImperativeVerbs.length == 0) return;
|
|
42
|
+
this.extraImperativeVerbs = [...this.extraImperativeVerbs, ...extraImperativeVerbs];
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
CZRC.prototype.getPromise = function () {
|
|
46
|
+
return new Promise((resolve) => resolve(this));
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
CZRC.prototype.formatTypesWithEmoji = function () {
|
|
50
|
+
let types = this.types;
|
|
51
|
+
const max_name_length = types.reduce((max, type) => (type.name.length > max ? type.name.length : max), 0);
|
|
52
|
+
const max_emoji_length = types.reduce((max, type) => (type.emoji.length > max ? type.emoji.length : max), 0);
|
|
53
|
+
|
|
54
|
+
return types.map((type) => ({
|
|
55
|
+
name: `${pad(type.name, max_name_length)} ${pad(type.emoji, max_emoji_length)} ${type.description.trim()}`,
|
|
56
|
+
value: type,
|
|
57
|
+
code: type.code,
|
|
58
|
+
}));
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
CZRC.prototype.searchAuthor = function (answers, input) {
|
|
62
|
+
input = input || "";
|
|
63
|
+
let authors = this.authors.map((author) => author.name);
|
|
64
|
+
return new Promise(function (resolve) {
|
|
65
|
+
setTimeout(function () {
|
|
66
|
+
var fuzzy_result = fuzzy.filter(input, authors);
|
|
67
|
+
resolve(fuzzy_result.map((el) => el.original));
|
|
68
|
+
}, Math.random() * (500 - 30) + 30);
|
|
69
|
+
});
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
CZRC.prototype.getAuthorByName = function (name) {
|
|
73
|
+
for (let i = 0; i < this.authors.length; i++) {
|
|
74
|
+
if (this.authors[i].name === name) return this.authors[i];
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
CZRC.prototype.getTypeByName = function (name) {
|
|
80
|
+
for (let i = 0; i < this.types.length; i++) {
|
|
81
|
+
if (this.types[i].name === name) return this.types[i];
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
CZRC.prototype.getTypeByEmoji = function (emoji) {
|
|
87
|
+
for (let i = 0; i < this.types.length; i++) {
|
|
88
|
+
if (this.types[i].emoji === emoji) return this.types[i];
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
CZRC.prototype.isValidTypeName = function (type) {
|
|
94
|
+
return this.getTypeByName(type) !== null;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
CZRC.prototype.isValidTypeEmoji = function (emoji) {
|
|
98
|
+
return this.getTypeByEmoji(emoji) !== null;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
CZRC.prototype.isValidType = function (name, emoji) {
|
|
102
|
+
let type = this.getTypeByName(name);
|
|
103
|
+
if (type === null) return false;
|
|
104
|
+
return type.emoji === emoji;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
CZRC.prototype.isValidAuthor = function (name, email) {
|
|
108
|
+
let author = this.getAuthorByName(name);
|
|
109
|
+
if (author === null) return false;
|
|
110
|
+
return author.email === email;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
CZRC.prototype.isValidScope = function (scope) {
|
|
114
|
+
return this.scopes.includes(scope);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
CZRC.prototype.doesNotHaveIssueTracker = function () {
|
|
118
|
+
return !this.hasIssueTracker();
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
CZRC.prototype.hasIssueTracker = function () {
|
|
122
|
+
return this.issueTrackers.length > 0;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
CZRC.prototype.isValidIssueTracker = function (name) {
|
|
126
|
+
if (this.doesNotHaveIssueTracker()) return true;
|
|
127
|
+
|
|
128
|
+
return this.issueTrackers.includes(name);
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
module.exports = CZRC;
|
package/CZRCLoader.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const download = require("download");
|
|
3
|
+
const homeDir = require("home-dir");
|
|
4
|
+
const CZRC = require("./CZRC");
|
|
5
|
+
|
|
6
|
+
const read = (file) => fs.readFileSync(file, "utf8");
|
|
7
|
+
const write = fs.writeFileSync;
|
|
8
|
+
const rc_folder_name = homeDir(".czrc.d");
|
|
9
|
+
const deafult_rc_file = rc_folder_name + "/default.json";
|
|
10
|
+
const project_rc_url_file_name = ".czrc.url";
|
|
11
|
+
const project_rc_file_name = ".czrc.json";
|
|
12
|
+
const default_file_hosted_url = "https://raw.githubusercontent.com/ShafiqIslam/dotfiles/master/git/.czrc.json";
|
|
13
|
+
|
|
14
|
+
function CZRCLoader() {
|
|
15
|
+
this.czrc = new CZRC();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
CZRCLoader.prototype.load = async function () {
|
|
19
|
+
ensureRCFolder();
|
|
20
|
+
await this.loadDefaultRC();
|
|
21
|
+
await this.overwriteFromProject();
|
|
22
|
+
return this.czrc;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
CZRCLoader.prototype.loadDefaultRC = async function () {
|
|
26
|
+
await this.ensureAndLoadFile(deafult_rc_file, default_file_hosted_url);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
CZRCLoader.prototype.ensureAndLoadFile = async function (file_fqn, file_url) {
|
|
30
|
+
await ensureFile(file_fqn, file_url);
|
|
31
|
+
this.loadFromFile(file_fqn);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
CZRCLoader.prototype.loadFromFile = function (file) {
|
|
35
|
+
let czrc = read(file);
|
|
36
|
+
czrc = (czrc && JSON.parse(czrc)) || null;
|
|
37
|
+
|
|
38
|
+
if (czrc === null) return;
|
|
39
|
+
|
|
40
|
+
czrc.issueTrackers = czrc.issue_trackers;
|
|
41
|
+
czrc.defaultIssueTracker = czrc.default_issue_tracker;
|
|
42
|
+
czrc.subjectMaxLength = czrc.subject_max_length;
|
|
43
|
+
czrc.bodyMaxLength = czrc.body_max_length;
|
|
44
|
+
czrc.sectionHeaders = czrc.section_headers;
|
|
45
|
+
czrc.inlineSeparator = czrc.inline_separator;
|
|
46
|
+
czrc.issueTrackerIdSeparator = czrc.issue_tracker_id_separator;
|
|
47
|
+
czrc.authorEmailTag = czrc.author_email_tag;
|
|
48
|
+
czrc.blockNonImperativeSubject = czrc.block_non_imperative_subject;
|
|
49
|
+
|
|
50
|
+
this.czrc.overwrite(czrc);
|
|
51
|
+
this.czrc.pushExtraImperativeVerbs(czrc.extra_imperative_verbs);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
CZRCLoader.prototype.overwriteFromProject = async function () {
|
|
55
|
+
await this.loadFromProjectURL();
|
|
56
|
+
this.loadFromProjectRC();
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
CZRCLoader.prototype.loadFromProjectURL = async function () {
|
|
60
|
+
let urlFile = process.cwd() + "/" + project_rc_url_file_name;
|
|
61
|
+
|
|
62
|
+
if (!fs.existsSync(urlFile)) return;
|
|
63
|
+
|
|
64
|
+
await this.ensureAndLoadFile(getProjectRCDownloadFileFQN(), getUrlFromFile(urlFile));
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
CZRCLoader.prototype.loadFromProjectRC = function () {
|
|
68
|
+
let project_rc_file_fqn = process.cwd() + "/" + project_rc_file_name;
|
|
69
|
+
|
|
70
|
+
if (!fs.existsSync(project_rc_file_fqn)) return;
|
|
71
|
+
|
|
72
|
+
this.loadFromFile(project_rc_file_fqn);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
function ensureRCFolder() {
|
|
76
|
+
if (!fs.existsSync(rc_folder_name)) {
|
|
77
|
+
createRCFolder();
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (fs.lstatSync(rc_folder_name).isDirectory()) return;
|
|
82
|
+
|
|
83
|
+
fs.unlinkSync(rc_folder_name);
|
|
84
|
+
createRCFolder();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function createRCFolder() {
|
|
88
|
+
fs.mkdirSync(rc_folder_name, { recursive: true });
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function ensureFile(file_fqn, file_url) {
|
|
92
|
+
let _download = (async () => {
|
|
93
|
+
write(file_fqn, await download(file_url));
|
|
94
|
+
})();
|
|
95
|
+
|
|
96
|
+
if (fs.existsSync(file_fqn)) {
|
|
97
|
+
_download.catch(function (e) {
|
|
98
|
+
console.error(e);
|
|
99
|
+
});
|
|
100
|
+
} else {
|
|
101
|
+
await _download;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function getUrlFromFile(urlFile) {
|
|
106
|
+
return read(urlFile)
|
|
107
|
+
.split(/(?:\r\n|\r|\n)/g)
|
|
108
|
+
.reduce((prev, cur) => {
|
|
109
|
+
if (cur.startsWith("URL=")) return cur.replace("URL=", "");
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function getProjectRCDownloadFileFQN() {
|
|
114
|
+
return rc_folder_name + "/" + process.cwd().replace("/", "").replaceAll("/", "_").replaceAll("-", "_") + ".json";
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
module.exports = CZRCLoader;
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2015-2018 Commitizen Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# commit-template
|
|
2
|
+
JS wrapper for the [commit template](https://github.com/ShafiqIslam/dotfiles/blob/master/.gitmessage).
|
|
3
|
+
|
|
4
|
+
# usage
|
|
5
|
+
Not for open use. Meant to be used with
|
|
6
|
+
[@sheba/cz-convention](https://github.com/ShafiqIslam/cz-convention) and [@sheba/commitlint](https://github.com/ShafiqIslam/commitlint)
|
package/dto/author.js
ADDED
package/dto/issue.js
ADDED
package/dto/message.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
const nlp = require("compromise");
|
|
2
|
+
const util = require("node:util");
|
|
3
|
+
|
|
4
|
+
const ValidationError = require("../formatter/errors/validation_error");
|
|
5
|
+
|
|
6
|
+
let _czrc = null;
|
|
7
|
+
function Message() {
|
|
8
|
+
this.subject = null;
|
|
9
|
+
this.types = [];
|
|
10
|
+
this.scopes = [];
|
|
11
|
+
this.what = null;
|
|
12
|
+
this.why = null;
|
|
13
|
+
this.issues = [];
|
|
14
|
+
this.coAuthors = [];
|
|
15
|
+
this.references = [];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param string
|
|
20
|
+
*/
|
|
21
|
+
Message.prototype.setSubject = function (subject) {
|
|
22
|
+
this.subject = subject;
|
|
23
|
+
return this;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param array<Type>
|
|
28
|
+
*/
|
|
29
|
+
Message.prototype.setTypes = function (types) {
|
|
30
|
+
this.types = types;
|
|
31
|
+
return this;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param array<string>
|
|
36
|
+
*/
|
|
37
|
+
Message.prototype.setScopes = function (scopes) {
|
|
38
|
+
this.scopes = scopes;
|
|
39
|
+
return this;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @param string
|
|
44
|
+
*/
|
|
45
|
+
Message.prototype.setWhat = function (what) {
|
|
46
|
+
this.what = what;
|
|
47
|
+
return this;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @param string
|
|
52
|
+
*/
|
|
53
|
+
Message.prototype.setWhy = function (why) {
|
|
54
|
+
this.why = why;
|
|
55
|
+
return this;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @param array<Issue>
|
|
60
|
+
*/
|
|
61
|
+
Message.prototype.setIssues = function (issues) {
|
|
62
|
+
this.issues = issues;
|
|
63
|
+
return this;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @param array<string>
|
|
68
|
+
*/
|
|
69
|
+
Message.prototype.setReferences = function (references) {
|
|
70
|
+
this.references = references;
|
|
71
|
+
return this;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @param array<Author>
|
|
76
|
+
*/
|
|
77
|
+
Message.prototype.setCoAuthors = function (co_authors) {
|
|
78
|
+
this.coAuthors = co_authors;
|
|
79
|
+
return this;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
Message.prototype.validate = function () {
|
|
83
|
+
validateSubject(this.subject);
|
|
84
|
+
validateIssues(this.issues);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
function validateSubject(subject) {
|
|
88
|
+
if (!subject) throw new ValidationError("Subject can't be empty");
|
|
89
|
+
|
|
90
|
+
if (isImperative(subject)) return;
|
|
91
|
+
|
|
92
|
+
let message =
|
|
93
|
+
'Subject %s be in imperative mood, and should complete "This commit will ... ", e.g. "... Do something".';
|
|
94
|
+
|
|
95
|
+
if (_czrc.blockNonImperativeSubject) {
|
|
96
|
+
throw new ValidationError(util.format(message, "must"));
|
|
97
|
+
} else {
|
|
98
|
+
console.log(`\n\x1b[33m${util.format(message, "should")}`);
|
|
99
|
+
console.log("However, this is allowed for the time being, please be aware next time. \x1b[0m\n");
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function isImperative(sentence) {
|
|
104
|
+
let first_word = sentence.split(" ")[0];
|
|
105
|
+
let imperative = nlp(first_word).verbs().isImperative().text();
|
|
106
|
+
if (first_word == imperative) return true;
|
|
107
|
+
|
|
108
|
+
if (_czrc.extraImperativeVerbs.includes(first_word)) return true;
|
|
109
|
+
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function validateIssues(issues) {
|
|
114
|
+
if (_czrc.doesNotHaveIssueTracker()) return;
|
|
115
|
+
if (!issues || issues.length == 0) throw new ValidationError("Issues can't be empty");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
module.exports = function (czrc) {
|
|
119
|
+
_czrc = czrc;
|
|
120
|
+
return Message;
|
|
121
|
+
};
|
package/dto/type.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
let DecomposingError = require('./decomposing_error.js');
|
|
2
|
+
class InvalidAuthor extends DecomposingError {
|
|
3
|
+
constructor(author, message) {
|
|
4
|
+
super("Invalid author: " + author + ". " + (message || ""));
|
|
5
|
+
this.name = "InvalidAuthor";
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
module.exports = InvalidAuthor;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
let DecomposingError = require('./decomposing_error.js');
|
|
2
|
+
class MalformedString extends DecomposingError {
|
|
3
|
+
constructor(message) {
|
|
4
|
+
message = message || "The given message string is malformed";
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = "MalformedString";
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
module.exports = MalformedString;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
let DecomposingError = require('./decomposing_error.js');
|
|
2
|
+
class MaxLengthExceeded extends DecomposingError {
|
|
3
|
+
constructor(line, expected_length) {
|
|
4
|
+
super("Max length exceeded in \"" + line + "\". Expected " + expected_length + " found " + line.length);
|
|
5
|
+
this.name = "MaxLengthExceeded";
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
module.exports = MaxLengthExceeded;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
String.prototype.ucFirst = function () {
|
|
2
|
+
return this.charAt(0).toUpperCase() + this.slice(1);
|
|
3
|
+
};
|
|
4
|
+
|
|
5
|
+
String.prototype.trimAny = function (s) {
|
|
6
|
+
if (s === " ") return this.trim();
|
|
7
|
+
if (s === "]") s = "\\]";
|
|
8
|
+
if (s === "\\") s = "\\\\";
|
|
9
|
+
let regex = new RegExp("^[" + s + "]+|[" + s + "]+$", "g");
|
|
10
|
+
return this.replace(regex, "");
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
String.prototype.replaceAll = function(search, replacement) {
|
|
14
|
+
var target = this;
|
|
15
|
+
return target.replace(new RegExp(search, 'g'), replacement);
|
|
16
|
+
};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
require('./helpers.js');
|
|
2
|
+
let Message = require('../dto/message.js');
|
|
3
|
+
let Type = require('../dto/type.js');
|
|
4
|
+
let Author = require('../dto/author.js');
|
|
5
|
+
let Issue = require('../dto/issue.js');
|
|
6
|
+
let DecomposingError = require('./errors/decomposing_error.js');
|
|
7
|
+
let MaxLengthExceeded = require('./errors/max_length_exceeded.js');
|
|
8
|
+
let MalformedString = require('./errors/malformed_string.js');
|
|
9
|
+
let InvalidType = require('./errors/invalid_type.js');
|
|
10
|
+
let InvalidScope = require('./errors/invalid_scope.js');
|
|
11
|
+
let InvalidAuthor = require('./errors/invalid_author.js');
|
|
12
|
+
|
|
13
|
+
let _czrc = null;
|
|
14
|
+
let _headers = null;
|
|
15
|
+
let _message_splits = null;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Format the git commit message from given answers.
|
|
19
|
+
*
|
|
20
|
+
* @param {String} Formated git commit message
|
|
21
|
+
* @return {Message} Message object containing message segments
|
|
22
|
+
*/
|
|
23
|
+
function format(message_string) {
|
|
24
|
+
_message_splits = message_string.replaceAll('\r\n', '\n').trimAny('\n').split('\n\n');
|
|
25
|
+
let message = new Message();
|
|
26
|
+
checkRequired();
|
|
27
|
+
let types = getTypes();
|
|
28
|
+
message.setSubject(getSubject(types)).setTypes(types);
|
|
29
|
+
for(let i=2; i<_message_splits.length; i++) {
|
|
30
|
+
let split = _message_splits[i];
|
|
31
|
+
if(split.startsWith(_headers.scopes)) message.setScopes(getScopes(split));
|
|
32
|
+
else if(split.startsWith(_headers.why)) message.setWhy(split.replace(_headers.why, ''));
|
|
33
|
+
else if(split.startsWith(_headers.what)) message.setWhat(split.replace(_headers.what, ''));
|
|
34
|
+
else if(split.startsWith(_headers.issues + _czrc.bullet)) message.setIssues(getIssues(split));
|
|
35
|
+
else if(split.startsWith(_headers.references + _czrc.bullet)) message.setReferences(getReferences(split));
|
|
36
|
+
else if(split.startsWith(_headers.co_authors + _czrc.bullet)) message.setCoAuthors(getAuthors(split));
|
|
37
|
+
else throw new MalformedString("Invalid section containing: " + split);
|
|
38
|
+
}
|
|
39
|
+
return message;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function checkRequired() {
|
|
43
|
+
if(_message_splits.length < 2) {
|
|
44
|
+
throw new MalformedString("At least 2 section (subject and types) must be present");
|
|
45
|
+
}
|
|
46
|
+
if(!_message_splits[1].startsWith(_headers.types)) {
|
|
47
|
+
throw new MalformedString("Type(s) must be present in second section, found: " + _message_splits[1]);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function getSubject(types) {
|
|
52
|
+
let subject = _message_splits[0];
|
|
53
|
+
if(subject.length > _czrc.subjectMaxLength) throw new MaxLengthExceeded(subject, _czrc.subjectMaxLength);
|
|
54
|
+
if(subject.endsWith('.') || subject.endsWith(',')) throw new DecomposingError("Punctuations not allowed for subject line ending.");
|
|
55
|
+
/*for(let i=0; i<types.length; i++) {
|
|
56
|
+
if(!subject.startsWith(types[i].emoji)) throw new DecomposingError("Type emoji does not match for type: " + types[i].name);
|
|
57
|
+
subject = subject.replace(types[i].emoji, '').trim();
|
|
58
|
+
}
|
|
59
|
+
if(subject[0] !== subject[0].toUpperCase()) throw new DecomposingError("Subject must be capitalized");*/
|
|
60
|
+
return subject;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function getTypes() {
|
|
64
|
+
let types = _message_splits[1].replace(_headers.types, '').split(_czrc.inlineSeparator);
|
|
65
|
+
for(let i=0; i<types.length; i++) {
|
|
66
|
+
let type = _czrc.getTypeByName(types[i]);
|
|
67
|
+
if(type == null) throw new InvalidType(types[i]);
|
|
68
|
+
types[i] = new Type(type);
|
|
69
|
+
}
|
|
70
|
+
return types;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function getScopes(split) {
|
|
74
|
+
let scopes = split.replace(_headers.scopes, '').split(_czrc.inlineSeparator);
|
|
75
|
+
for(let i=0; i<scopes.length; i++) {
|
|
76
|
+
if(!_czrc.isValidScope(scopes[i])) throw new InvalidScope(scopes[i]);
|
|
77
|
+
}
|
|
78
|
+
return scopes;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function getReferences(split) {
|
|
82
|
+
let references = split.replace(_headers.references + _czrc.bullet, '').split('\n' + _czrc.bullet);
|
|
83
|
+
for(let i=0; i<references.length; i++) {
|
|
84
|
+
if(!references[i]) throw new DecomposingError("Empty reference is not allowed.");
|
|
85
|
+
}
|
|
86
|
+
return references;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function getIssues(split) {
|
|
90
|
+
let issues = split.replace(_headers.issues + _czrc.bullet, '').split('\n' + _czrc.bullet);
|
|
91
|
+
let issue_objects = [];
|
|
92
|
+
for(let i=0; i<issues.length; i++) {
|
|
93
|
+
let issues_splited = issues[i].split(_czrc.issueTrackerIdSeparator);
|
|
94
|
+
if(issues_splited.length != 2) throw new MalformedString("Could not parse issue: " + issues[i]);
|
|
95
|
+
let tracker = issues_splited[0];
|
|
96
|
+
if(!_czrc.isValidIssueTracker(tracker)) throw new InvalidIssueTracker(tracker);
|
|
97
|
+
let issue_ids = issues_splited[1].split(_czrc.inlineSeparator);
|
|
98
|
+
for(let j=0; j<issue_ids.length; j++) {
|
|
99
|
+
if(!issue_ids[j]) throw new DecomposingError("Empty issue id is not allowed.");
|
|
100
|
+
issue_objects.push(new Issue(tracker, issue_ids[i]));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return issue_objects;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function getAuthors(split) {
|
|
107
|
+
let authors = split.replace(_headers.co_authors + _czrc.bullet, '').split('\n' + _czrc.bullet);
|
|
108
|
+
for(let i=0; i<authors.length; i++) {
|
|
109
|
+
try {
|
|
110
|
+
let author_split = authors[i].split(_czrc.authorEmailTag.start);
|
|
111
|
+
let author = _czrc.getAuthorByName(author_split[0]);
|
|
112
|
+
if(author.email != author_split[1].trimAny(_czrc.authorEmailTag.end)) {
|
|
113
|
+
throw new InvalidAuthor(authors[i], "Name and email does not match.");
|
|
114
|
+
}
|
|
115
|
+
authors[i] = new Author(author.name, author.email);
|
|
116
|
+
} catch (e) {
|
|
117
|
+
if (e instanceof InvalidAuthor) throw e;
|
|
118
|
+
else throw new InvalidAuthor(authors[i], "Not found.");
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return authors;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
module.exports = function(czrc) {
|
|
125
|
+
_czrc = czrc;
|
|
126
|
+
_headers = czrc.sectionHeaders;
|
|
127
|
+
return format;
|
|
128
|
+
};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
const truncate = require('cli-truncate');
|
|
2
|
+
const wrap = require('wrap-ansi');
|
|
3
|
+
require('./helpers.js');
|
|
4
|
+
|
|
5
|
+
let _czrc = null;
|
|
6
|
+
let _message = null;
|
|
7
|
+
|
|
8
|
+
let wrap_body = function(s) {
|
|
9
|
+
return wrap(s, _czrc.bodyMaxLength);
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Format the git commit message from given answers.
|
|
14
|
+
*
|
|
15
|
+
* @param {Message} Message object containing message segments
|
|
16
|
+
* @return {String} Formated git commit message
|
|
17
|
+
*/
|
|
18
|
+
function format(message) {
|
|
19
|
+
_message = message;
|
|
20
|
+
let message_segments = [];
|
|
21
|
+
let headers = _czrc.sectionHeaders;
|
|
22
|
+
message_segments.push(formatSubject());
|
|
23
|
+
message_segments.push(headers.types + formatTypes());
|
|
24
|
+
if(_message.scopes && _message.scopes.length) {
|
|
25
|
+
let s = formatScopes();
|
|
26
|
+
if(s) message_segments.push(headers.scopes + s);
|
|
27
|
+
}
|
|
28
|
+
if(_message.why) message_segments.push(headers.why + wrap_body(_message.why));
|
|
29
|
+
if(_message.what) message_segments.push(headers.what + wrap_body(_message.what));
|
|
30
|
+
|
|
31
|
+
if(_message.issues && _message.issues.length) {
|
|
32
|
+
let t = formatIssues();
|
|
33
|
+
if(t) message_segments.push(headers.issues.trimAny('\n') + t);
|
|
34
|
+
}
|
|
35
|
+
if(_message.references && _message.references.length) {
|
|
36
|
+
let r = formatReferences();
|
|
37
|
+
if(r) message_segments.push(headers.references.trimAny('\n') + r);
|
|
38
|
+
}
|
|
39
|
+
if(_message.coAuthors && _message.coAuthors.length) {
|
|
40
|
+
let a = formatCoAuthors();
|
|
41
|
+
if(a) message_segments.push(headers.co_authors.trimAny('\n') + a);
|
|
42
|
+
}
|
|
43
|
+
return message_segments.join('\n\n');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function formatSubject() {
|
|
47
|
+
let emojis = '';
|
|
48
|
+
_message.types.forEach(function(type) {
|
|
49
|
+
emojis += type.emoji + (type.emoji.length == 1 ? ' ' : ' ');
|
|
50
|
+
});
|
|
51
|
+
let subject = emojis + _message.subject.trimAny('. ').toLowerCase().ucFirst();
|
|
52
|
+
return truncate(subject, _czrc.subjectMaxLength);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function formatTypes() {
|
|
56
|
+
let types = '';
|
|
57
|
+
_message.types.forEach(function(type) {
|
|
58
|
+
types += type.name + _czrc.inlineSeparator;
|
|
59
|
+
});
|
|
60
|
+
return types.trimAny(_czrc.inlineSeparator);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function formatScopes() {
|
|
64
|
+
let scopes = '';
|
|
65
|
+
_message.scopes.forEach(function(scope) {
|
|
66
|
+
if(scope) scopes += scope + _czrc.inlineSeparator;
|
|
67
|
+
});
|
|
68
|
+
return scopes.trimAny(_czrc.inlineSeparator);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function formatIssues() {
|
|
72
|
+
let trackers = {};
|
|
73
|
+
_message.issues.forEach(function(issue) {
|
|
74
|
+
let tracker = issue.tracker;
|
|
75
|
+
if(!trackers.hasOwnProperty(tracker)) trackers[tracker] = '';
|
|
76
|
+
trackers[tracker] += issue.issueId + _czrc.inlineSeparator;
|
|
77
|
+
});
|
|
78
|
+
let issues = '';
|
|
79
|
+
for(tracker in trackers) {
|
|
80
|
+
issues += '\n' + _czrc.bullet + tracker + _czrc.issueTrackerIdSeparator + trackers[tracker].trimAny(_czrc.inlineSeparator);
|
|
81
|
+
}
|
|
82
|
+
return issues;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function formatCoAuthors() {
|
|
86
|
+
let co_authors = '';
|
|
87
|
+
let email_tag = _czrc.authorEmailTag;
|
|
88
|
+
_message.coAuthors.forEach(function(author) {
|
|
89
|
+
co_authors += '\n' + _czrc.bullet + author.name + email_tag.start + author.email + email_tag.end;
|
|
90
|
+
});
|
|
91
|
+
return co_authors;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function formatReferences() {
|
|
95
|
+
let references = '';
|
|
96
|
+
_message.references.forEach(function(reference) {
|
|
97
|
+
references += '\n' + wrap_body(_czrc.bullet + reference);
|
|
98
|
+
});
|
|
99
|
+
return references;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
module.exports = function(czrc) {
|
|
103
|
+
_czrc = czrc;
|
|
104
|
+
return format;
|
|
105
|
+
};
|
package/index.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const commitTemplate = async function () {
|
|
2
|
+
let loader = new (require("./CZRCLoader.js"))();
|
|
3
|
+
|
|
4
|
+
let czrc = await loader.load();
|
|
5
|
+
|
|
6
|
+
let message = require("./dto/message.js")(czrc);
|
|
7
|
+
let type = require("./dto/type.js");
|
|
8
|
+
let author = require("./dto/author.js");
|
|
9
|
+
let issue = require("./dto/issue.js");
|
|
10
|
+
|
|
11
|
+
let format_string = require("./formatter/to_string.js")(czrc);
|
|
12
|
+
let format_object = require("./formatter/to_object.js")(czrc);
|
|
13
|
+
|
|
14
|
+
let DecomposingError = require("./formatter/errors/decomposing_error.js");
|
|
15
|
+
let ValidationError = require("./formatter/errors/validation_error.js");
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
czrc: czrc,
|
|
19
|
+
dto: {
|
|
20
|
+
message: message,
|
|
21
|
+
type: type,
|
|
22
|
+
author: author,
|
|
23
|
+
issue: issue,
|
|
24
|
+
},
|
|
25
|
+
formatter: {
|
|
26
|
+
toString: format_string,
|
|
27
|
+
toObject: format_object,
|
|
28
|
+
},
|
|
29
|
+
errors: {
|
|
30
|
+
decompose: DecomposingError,
|
|
31
|
+
validation: ValidationError,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
module.exports = commitTemplate;
|
|
37
|
+
|
|
38
|
+
// commitTemplate().then(function (v) {
|
|
39
|
+
// console.dir(v);
|
|
40
|
+
// });
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@polygontech/commit-template",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "JS wrapper of https://github.com/ShafiqIslam/dotfiles/blob/master/git/.gitmessage.",
|
|
5
|
+
"homepage": "https://github.com/ShafiqIslam/commit-template",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/ShafiqIslam/commit-template.git"
|
|
10
|
+
},
|
|
11
|
+
"author": "Shafiqul Islam <islamshafiq003@gmail.com>",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"cli-truncate": "^1.0.0",
|
|
15
|
+
"compromise": "^13.11.4",
|
|
16
|
+
"download": "^8.0.0",
|
|
17
|
+
"fuse.js": "^3.2.0",
|
|
18
|
+
"fuzzy": "^0.1.3",
|
|
19
|
+
"home-dir": "^1.0.0",
|
|
20
|
+
"pad": "^2.0.1",
|
|
21
|
+
"wrap-ansi": "^3.0.0"
|
|
22
|
+
},
|
|
23
|
+
"czrcScopes": [
|
|
24
|
+
"czrc",
|
|
25
|
+
"formatting"
|
|
26
|
+
]
|
|
27
|
+
}
|