diff --git a/commitlint.config.js b/commitlint.config.js new file mode 100644 index 0000000000..e5762bf628 --- /dev/null +++ b/commitlint.config.js @@ -0,0 +1,60 @@ +'use strict'; +const message = process.env['HUSKY_GIT_PARAMS']; +const fs = require('fs'); + +const types = [ + 'build', + "chore", + 'ci', + 'docs', + 'feat', + 'fix', + 'perf', + 'refactor', + "release", + 'revert', + 'style', + 'test' +]; + +const scopes = [ + "showcase", + "packaging", + "changelog", + "schematics", + "module:*" +]; + +function parseMessage(message) { + const PATTERN = /^(\w+)(?:\(([^)]+)\))?\: (.+)$/; + const match = PATTERN.exec(message); + if (!match) { + return null; + } + return { + type: match[1] || null, + scope: match[2] || null, + } +} + +function getScopesRule() { + const messages = fs.readFileSync(message, {encoding: 'utf-8'}); + const parsed = parseMessage(messages.split('\n')[0]); + if (!parsed) { + return [2, 'always', scopes] + } + const { scope, type } = parsed; + if (scope && !scopes.includes(scope) && type !== 'release' && !/module:.+/.test(scope)) { + return [2, 'always', scopes] + } else { + return [2, 'always', []] + } +} + +module.exports = { + extends: ['@commitlint/config-angular'], + rules: { + 'type-enum': [2, 'always', types], + 'scope-enum': getScopesRule + } +}; diff --git a/package.json b/package.json index cc1ac75123..0d1d012556 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,8 @@ "@angular/pwa": "^0.800.1", "@angular/router": "~8.0.0", "@angular/service-worker": "~8.0.0", + "@commitlint/cli": "^8.0.0", + "@commitlint/config-angular": "^8.0.0", "@nguniversal/module-map-ngfactory-loader": "^7.1.1", "@schematics/angular": "~8.0.1", "@stackblitz/sdk": "^1.1.1", @@ -123,7 +125,7 @@ }, "husky": { "hooks": { - "commit-msg": "node ./scripts/git/commit-msg.js -E HUSKY_GIT_PARAMS", + "commit-msg": "commitlint -E HUSKY_GIT_PARAMS", "pre-commit": "lint-staged" } } diff --git a/scripts/git/commit-message.json b/scripts/git/commit-message.json deleted file mode 100644 index 3f9c6885b8..0000000000 --- a/scripts/git/commit-message.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "maxLength": 100, - "types": [ - "build", - "ci", - "docs", - "feat", - "fix", - "perf", - "refactor", - "release", - "style", - "test", - "chore", - "revert" - ], - "scopes": [ - "showcase", - "packaging", - "changelog", - "schematics", - "module:*" - ] -} diff --git a/scripts/git/commit-msg.js b/scripts/git/commit-msg.js deleted file mode 100755 index 1c8079e8e9..0000000000 --- a/scripts/git/commit-msg.js +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env node - -// from https://github.com/angular/angular/blob/master/scripts/git/commit-msg.js - -'use strict'; - -const fs = require('fs'); -const checkMsg = require('./validate-commit-message'); -const msgFile = process.env['HUSKY_GIT_PARAMS']; - -let isValid = true; -if (msgFile || true) { - const commitMsg = fs.readFileSync(msgFile, {encoding: 'utf-8'}); - const firstLine = commitMsg.split('\n')[0]; - isValid = checkMsg(firstLine); - if (!isValid) { - console.error('\x1b[36mCheck CONTRIBUTING.md at the root of the repo for more information.(请查看根目录下的 CONTRIBUTING.md 获取更多信息)\x1b[0m\n'); - } -} - -process.exit(isValid ? 0 : 1); diff --git a/scripts/git/validate-commit-message.js b/scripts/git/validate-commit-message.js deleted file mode 100644 index 26dfd200ad..0000000000 --- a/scripts/git/validate-commit-message.js +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env node - -// from https://github.com/angular/angular/blob/master/tools/validate-commit-message/validate-commit-message.js - -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const configPath = path.resolve(__dirname, './commit-message.json'); -const config = JSON.parse(fs.readFileSync(configPath, 'utf8')); -const PATTERN = /^(\w+)(?:\(([^)]+)\))?\: (.+)$/; -const FIXUP_SQUASH = /^(fixup|squash)\! /i; -const REVERT = /^revert:? /i; - -module.exports = function (commitSubject) { - - const subject = commitSubject.replace(FIXUP_SQUASH, ''); - - if (subject.match(REVERT)) { - return true; - } - - if (subject.length > config['maxLength']) { - error(`The commit message is longer than ${config['maxLength']} characters`, commitSubject); - error(`commit 信息不能超过 ${config['maxLength']} 字符`, commitSubject, 'zh'); - return false; - } - - const match = PATTERN.exec(subject); - if (!match) { - error(`The commit message does not match the format of '(): ' OR ': '`, commitSubject); - error(`这条 commit 信息格式不正确 '(): ' 或 ': `, commitSubject, 'zh'); - return false; - } - - const type = match[1]; - - if (type.toLowerCase() === 'wip') { - error(`wip are not allowed in a commit, you can change this PR title`, commitSubject); - error(`wip 不允许出现在 commit 中,你可以在 PR 中修改它的标题`, commitSubject, 'zh'); - return false; - } - - if (config['types'].indexOf(type) === -1) { - error( - `${type} is not an allowed type.\n => TYPES: ${config['types'].join(', ')}`, commitSubject); - error( - `${type} 是不允许的 type.\n => TYPES: ${config['types'].join(', ')}`, commitSubject, 'zh'); - return false; - } - - const scope = match[2]; - - if (scope && !config['scopes'].includes(scope) && type !== 'release' && !/module:.+/.test(scope)) { - error( - `"${scope}" is not an allowed scope.\n => SCOPES: ${config['scopes'].join(', ')}`, commitSubject); - error( - `"${scope}" 是不允许的 scope.\n => SCOPES: ${config['scopes'].join(', ')}`, commitSubject, 'zh'); - return false; - } - - return true; -}; - -function error(errorMessage, commitMessage, lang) { - if (lang === 'zh') { - console.error(`\x1b[33m无效的 COMMIT 信息: "${commitMessage}"\x1b[0m\n\x1b[31m => 错误: ${errorMessage}\x1b[0m\n`); - } else { - console.error(`\x1b[33mINVALID COMMIT MSG: "${commitMessage}"\x1b[0m\n\x1b[31m => ERROR: ${errorMessage}\x1b[0m\n`); - } -} - -module.exports.config = config;