@w5s/conventional-changelog 1.0.0-alpha.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Julien Polo
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,73 @@
1
+ <!-- AUTO-GENERATED-CONTENT:START (PKG_JSON:template=# W5s Commitlint configuration _(${name})_) -->
2
+ # W5s Commitlint configuration _(@w5s/commitlint-config)_
3
+ <!-- AUTO-GENERATED-CONTENT:END -->
4
+
5
+ [![NPM Version][package-version-svg]][package-url]
6
+ [![License][license-image]][license-url]
7
+
8
+ <!-- AUTO-GENERATED-CONTENT:START (PKG_JSON:template=> ${description}&unknownTxt= ) -->
9
+ > Commitlint configuration presets
10
+ <!-- AUTO-GENERATED-CONTENT:END -->
11
+
12
+ ## Installation
13
+
14
+ <!-- AUTO-GENERATED-CONTENT:START (PKG_JSON:template=```console\nnpm install --save-dev ${name} @commitlint/cli\n```) -->
15
+ ```console
16
+ npm install --save-dev @w5s/commitlint-config @commitlint/cli
17
+ ```
18
+ <!-- AUTO-GENERATED-CONTENT:END -->
19
+
20
+ ## Usage
21
+
22
+ In the `package.json` of your project
23
+
24
+ <!-- AUTO-GENERATED-CONTENT:START (PKG_JSON:template=```json\n{\n "commitlint": {\n "extends": ["${name}"]\n }\n}\n```) -->
25
+ ```json
26
+ {
27
+ "commitlint": {
28
+ "extends": ["@w5s/commitlint-config"]
29
+ }
30
+ }
31
+ ```
32
+ <!-- AUTO-GENERATED-CONTENT:END -->
33
+
34
+ Extra rules
35
+
36
+ ```jsonc
37
+ {
38
+ "commitlint": {
39
+ "rules": {
40
+ // Rule to validate gitmoji unicode (🐛) or emoji (:bug:)
41
+ "type-gitmoji-style": ["error", "always", "unicode" /* | 'emoji' */],
42
+ // Rule to validate a gitmoji in the list
43
+ "type-valid-gitmoji": ["error", "always"]
44
+ }
45
+ }
46
+ }
47
+ ```
48
+
49
+ Ensure the `commitlint` is setup on husky commit message hook (default `.husky/commit-msg`)
50
+
51
+ ```console
52
+ commitlint --edit $1
53
+ # -OR-
54
+ npm exec --no -- commitlint --edit $1
55
+ ```
56
+
57
+ ## License
58
+ <!-- AUTO-GENERATED-CONTENT:START (PKG_JSON:template=[${license}][license-url] © ${author}) -->
59
+ [MIT][license-url] © Julien Polo <julien.polo@gmail.com>
60
+ <!-- AUTO-GENERATED-CONTENT:END -->
61
+
62
+ <!-- VARIABLES -->
63
+
64
+ <!-- AUTO-GENERATED-CONTENT:START (PKG_JSON:template=[package-version-svg]: https://img.shields.io/npm/v/${name}.svg?style=flat-square) -->
65
+ [package-version-svg]: https://img.shields.io/npm/v/@w5s/commitlint-config.svg?style=flat-square
66
+ <!-- AUTO-GENERATED-CONTENT:END -->
67
+ <!-- AUTO-GENERATED-CONTENT:START (PKG_JSON:template=[package-url]: https://www.npmjs.com/package/${name}) -->
68
+ [package-url]: https://www.npmjs.com/package/@w5s/commitlint-config
69
+ <!-- AUTO-GENERATED-CONTENT:END -->
70
+ <!-- AUTO-GENERATED-CONTENT:START (PKG_JSON:template=[license-image]: https://img.shields.io/badge/license-${license}-green.svg?style=flat-square) -->
71
+ [license-image]: https://img.shields.io/badge/license-MIT-green.svg?style=flat-square
72
+ <!-- AUTO-GENERATED-CONTENT:END -->
73
+ [license-url]: ../../LICENSE
package/lib/data.d.ts ADDED
@@ -0,0 +1,27 @@
1
+ import type { Commit as CommitDefault } from 'conventional-commits-parser';
2
+ export type Commit = CommitDefault<never> & {
3
+ hash?: string;
4
+ };
5
+ export type CommitConventionalType = 'build' | 'ci' | 'docs' | 'feat' | 'fix' | 'perf' | 'refactor' | 'revert' | 'style' | 'test' | 'wip' | 'chore';
6
+ export declare const CommitConventionalType: {
7
+ hasInstance: (anyValue: unknown) => anyValue is CommitConventionalType;
8
+ getData: (commitType: CommitConventionalType) => CommitConventionalTypeData;
9
+ values: () => readonly CommitConventionalType[];
10
+ parse: (anyValue: string) => CommitConventionalType | undefined;
11
+ Build: "build";
12
+ CI: "ci";
13
+ Docs: "docs";
14
+ Feat: "feat";
15
+ Fix: "fix";
16
+ Perf: "perf";
17
+ Refactor: "refactor";
18
+ Revert: "revert";
19
+ Style: "style";
20
+ Test: "test";
21
+ WIP: "wip";
22
+ Chore: "chore";
23
+ };
24
+ export interface CommitConventionalTypeData {
25
+ emoji: string;
26
+ 'en-US': string;
27
+ }
package/lib/data.js ADDED
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CommitConventionalType = void 0;
4
+ exports.CommitConventionalType = (() => {
5
+ const enumObject = Object.freeze({
6
+ Build: 'build',
7
+ CI: 'ci',
8
+ Docs: 'docs',
9
+ Feat: 'feat',
10
+ Fix: 'fix',
11
+ Perf: 'perf',
12
+ Refactor: 'refactor',
13
+ Revert: 'revert',
14
+ Style: 'style',
15
+ Test: 'test',
16
+ WIP: 'wip',
17
+ Chore: 'chore',
18
+ });
19
+ const enumValues = Object.freeze(Object.values(enumObject).sort());
20
+ const enumValuesSet = new Set(enumValues);
21
+ const typeData = {
22
+ feat: {
23
+ emoji: '✨',
24
+ 'en-US': 'Features',
25
+ },
26
+ fix: {
27
+ emoji: '🐛',
28
+ 'en-US': 'Bug Fixes',
29
+ },
30
+ build: {
31
+ emoji: '👷',
32
+ 'en-US': 'Build System',
33
+ },
34
+ chore: {
35
+ emoji: '🎫',
36
+ 'en-US': 'Chores',
37
+ },
38
+ ci: {
39
+ emoji: '🔧',
40
+ 'en-US': 'Continuous Integration',
41
+ },
42
+ docs: {
43
+ emoji: '📝',
44
+ 'en-US': 'Documentation',
45
+ },
46
+ test: {
47
+ emoji: '✅',
48
+ 'en-US': 'Tests',
49
+ },
50
+ perf: {
51
+ emoji: '⚡',
52
+ 'en-US': 'Performance Improvements',
53
+ },
54
+ refactor: {
55
+ emoji: '♻',
56
+ 'en-US': 'Code Refactoring',
57
+ },
58
+ revert: {
59
+ emoji: '⏪',
60
+ 'en-US': 'Reverts',
61
+ },
62
+ style: {
63
+ emoji: '💄',
64
+ 'en-US': 'Styles',
65
+ },
66
+ wip: {
67
+ emoji: '🚧',
68
+ 'en-US': 'Work in progress',
69
+ },
70
+ };
71
+ function hasInstance(anyValue) {
72
+ return typeof anyValue === 'string' && enumValuesSet.has(anyValue);
73
+ }
74
+ function getData(commitType) {
75
+ return typeData[commitType];
76
+ }
77
+ function parse(anyValue) {
78
+ return hasInstance(anyValue) ? anyValue : undefined;
79
+ }
80
+ function values() {
81
+ return enumValues;
82
+ }
83
+ return { ...enumObject, hasInstance, getData, values, parse };
84
+ })();
@@ -0,0 +1,28 @@
1
+ import type { CommitConventionalType } from './data';
2
+ export type Emoji = Emoji.Unicode | Emoji.Text;
3
+ export declare namespace Emoji {
4
+ const reEmojiUnicode: RegExp;
5
+ const reEmojiText: RegExp;
6
+ type Unicode = string & {
7
+ '@@EmojiStyle': 'unicode';
8
+ };
9
+ type Text = string & {
10
+ '@@EmojiStyle': 'text';
11
+ };
12
+ function isUnicode(anyValue: string): anyValue is Unicode;
13
+ function isText(anyValue: string): anyValue is Text;
14
+ function hasInstance(anyValue: string): anyValue is Emoji;
15
+ }
16
+ export type GitmojiCode = Emoji & {
17
+ '@@Gitmoji': true;
18
+ };
19
+ export declare namespace GitmojiCode {
20
+ type Unicode = Emoji.Unicode & {
21
+ '@@Gitmoji': true;
22
+ };
23
+ type Emoji = Emoji.Text & {
24
+ '@@Gitmoji': true;
25
+ };
26
+ function isValid(anyValue: string): anyValue is GitmojiCode;
27
+ function toConventionalCommitType(gitmoji: GitmojiCode): CommitConventionalType;
28
+ }
package/lib/gitmoji.js ADDED
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.GitmojiCode = exports.Emoji = void 0;
7
+ const emoji_regex_1 = __importDefault(require("emoji-regex"));
8
+ const gitmojis_1 = require("gitmojis");
9
+ var Emoji;
10
+ (function (Emoji) {
11
+ Emoji.reEmojiUnicode = (0, emoji_regex_1.default)();
12
+ Emoji.reEmojiText = /:(\w+):/;
13
+ const reMatchOnly = (input) => new RegExp(`^${input.source}$`, input.flags);
14
+ const _reEmojiUnicode = reMatchOnly(Emoji.reEmojiUnicode);
15
+ const _reEmojiText = reMatchOnly(Emoji.reEmojiText);
16
+ function isUnicode(anyValue) {
17
+ return anyValue.match(_reEmojiUnicode) != null;
18
+ }
19
+ Emoji.isUnicode = isUnicode;
20
+ function isText(anyValue) {
21
+ return anyValue.match(_reEmojiText) != null;
22
+ }
23
+ Emoji.isText = isText;
24
+ function hasInstance(anyValue) {
25
+ return isText(anyValue) || isUnicode(anyValue);
26
+ }
27
+ Emoji.hasInstance = hasInstance;
28
+ })(Emoji = exports.Emoji || (exports.Emoji = {}));
29
+ var GitmojiCode;
30
+ (function (GitmojiCode) {
31
+ // export const reEmoji = emojiRegexp();
32
+ const allGitmojiCodes = new Set(gitmojis_1.gitmojis
33
+ .map((gitmoji) => gitmoji.code)
34
+ .concat(gitmojis_1.gitmojis.map((gitmoji) => gitmoji.emoji)));
35
+ const index = {
36
+ // code: createIndex(gitmojis, 'code'),
37
+ emoji: createIndex(gitmojis_1.gitmojis, 'emoji'),
38
+ };
39
+ function createIndex(list, key) {
40
+ return new Map(list.map((gitmoji) => [gitmoji[key], gitmoji]));
41
+ }
42
+ function isValid(anyValue) {
43
+ return allGitmojiCodes.has(anyValue);
44
+ }
45
+ GitmojiCode.isValid = isValid;
46
+ const defaultType = 'chore';
47
+ const conversionMap = (() => {
48
+ const data = {
49
+ feat: ['✨', '♿️', '🚸'],
50
+ fix: ['🐛'],
51
+ docs: ['📝'],
52
+ style: ['🎨', '🚨'],
53
+ refactor: ['♻️', '🏗️'],
54
+ test: ['✅'],
55
+ perf: ['⚡️'],
56
+ revert: ['⏪️'],
57
+ ci: ['👷', '💚'],
58
+ wip: ['🚧'],
59
+ build: [],
60
+ chore: [],
61
+ };
62
+ const entries = Array.from(
63
+ // @ts-ignore
64
+ Object.entries(data));
65
+ return new Map(entries.reduce((acc, [commitType, gitmojiUnicodeArray]) => acc
66
+ .concat(gitmojiUnicodeArray.map((gitmojiUnicode) => [gitmojiUnicode, commitType]))
67
+ .concat(gitmojiUnicodeArray.map((gitmojiUnicode) => [
68
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
69
+ index.emoji.get(gitmojiUnicode)?.code,
70
+ commitType,
71
+ ])), []));
72
+ })();
73
+ function toConventionalCommitType(gitmoji) {
74
+ return conversionMap.get(gitmoji) ?? defaultType;
75
+ }
76
+ GitmojiCode.toConventionalCommitType = toConventionalCommitType;
77
+ })(GitmojiCode = exports.GitmojiCode || (exports.GitmojiCode = {}));
package/lib/index.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ declare const _default: {
2
+ parserOpts: import("./parser-opts").ParserOptions;
3
+ };
4
+ export default _default;
package/lib/index.js ADDED
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const parser_opts_1 = require("./parser-opts");
4
+ exports.default = {
5
+ parserOpts: parser_opts_1.parserOpts,
6
+ };
@@ -0,0 +1,4 @@
1
+ import type { Options } from 'conventional-commits-parser';
2
+ export interface ParserOptions extends Options {
3
+ }
4
+ export declare const parserOpts: ParserOptions;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parserOpts = void 0;
4
+ const gitmoji_1 = require("./gitmoji");
5
+ exports.parserOpts = {
6
+ headerPattern: new RegExp(`^(:\\w*:|${gitmoji_1.Emoji.reEmojiUnicode.source}) (?:\\((.*)\\):? )?(.*)$`),
7
+ headerCorrespondence: ['type', 'scope', 'subject'],
8
+ revertPattern: /^(?:revert|revert:)\s"?([\S\s]+?)"?\s*this reverts commit (\w*)\./i,
9
+ noteKeywords: ['BREAKING CHANGE', 'BREAKING CHANGES'],
10
+ // revertPattern: /revert:\s([\S\s]*?)\s*this reverts commit (\w*)\./i,
11
+ revertCorrespondence: [`header`, `hash`],
12
+ };
@@ -0,0 +1,20 @@
1
+ import type { Options as WriterOptions } from 'conventional-changelog-writer';
2
+ import { CommitConventionalType } from './data.js';
3
+ export type Language = 'en-US';
4
+ export interface TransformConfig {
5
+ scopeDisplayName?: Record<string, string>;
6
+ displayTypes?: CommitConventionalType[];
7
+ displayScopes?: string[];
8
+ showAuthor?: boolean;
9
+ withEmoji?: boolean;
10
+ language?: Language;
11
+ }
12
+ export declare function displayScope(scope: string | null | undefined, scopeDisplayNameMap: Record<string, string>): string | undefined;
13
+ export declare function displayType(type: CommitConventionalType, options?: displayType.Options): string;
14
+ export declare namespace displayType {
15
+ interface Options {
16
+ readonly withEmoji?: boolean;
17
+ readonly language?: Language;
18
+ }
19
+ }
20
+ export declare function createTransform(config: TransformConfig): WriterOptions.Transform.Function;
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createTransform = exports.displayType = exports.displayScope = void 0;
4
+ const data_js_1 = require("./data.js");
5
+ const gitmoji_js_1 = require("./gitmoji.js");
6
+ function displayScope(scope, scopeDisplayNameMap) {
7
+ return scope == null || scope.length === 0
8
+ ? scopeDisplayNameMap['*']
9
+ : scopeDisplayNameMap[scope] == null
10
+ ? scope
11
+ : scopeDisplayNameMap[scope];
12
+ }
13
+ exports.displayScope = displayScope;
14
+ function displayType(type, options = {}) {
15
+ const { withEmoji = true, language = 'en-US' } = options;
16
+ if (data_js_1.CommitConventionalType.hasInstance(type)) {
17
+ const { emoji, [language]: title } = data_js_1.CommitConventionalType.getData(type);
18
+ return `${withEmoji ? `${emoji} ` : ''}${title}`;
19
+ }
20
+ return type;
21
+ }
22
+ exports.displayType = displayType;
23
+ function createTransform(config) {
24
+ const displayTypes = new Set(config.displayTypes == null ? data_js_1.CommitConventionalType.values() : config.displayTypes);
25
+ const ignoreType = (type) => type == null || !displayTypes.has(type);
26
+ const ignoreScope = (scope) => config.displayScopes == null ? false : scope != null && !config.displayScopes.includes(scope);
27
+ const transform = (commit, { repository, host, owner, repoUrl }) => {
28
+ const discard = commit.notes.length === 0;
29
+ const issues = new Set();
30
+ const notes = commit.notes.map((note) => ({
31
+ ...note,
32
+ title: `${config.withEmoji === false ? '' : '💥 '}BREAKING CHANGES`,
33
+ }));
34
+ const conventionalType = commit.type == null
35
+ ? undefined
36
+ : data_js_1.CommitConventionalType.parse(commit.type) ??
37
+ (gitmoji_js_1.GitmojiCode.isValid(commit.type) ? gitmoji_js_1.GitmojiCode.toConventionalCommitType(commit.type) : undefined);
38
+ if (ignoreType(conventionalType) && discard)
39
+ return false;
40
+ const type = conventionalType == null
41
+ ? conventionalType
42
+ : displayType(conventionalType, {
43
+ withEmoji: config.withEmoji,
44
+ });
45
+ if (ignoreScope(commit.scope))
46
+ return false;
47
+ const scopeIntermediate = commit.scope === '*' ? '' : commit.scope;
48
+ const scope = config.scopeDisplayName == null ? scopeIntermediate : displayScope(scopeIntermediate, config.scopeDisplayName);
49
+ const hash = typeof commit.hash === 'string' ? commit.hash.slice(0, 7) : commit.hash;
50
+ const subject = typeof commit.subject === 'string'
51
+ ? (() => {
52
+ let returnValue = commit.subject;
53
+ const url = repository == null ? repoUrl : [host, owner, repository].filter(Boolean).join('/');
54
+ if (url != null) {
55
+ const issueURL = `${url}/issues/`;
56
+ // Issue URLs.
57
+ returnValue = returnValue.replace(/#(\d+)/g, (_, issue) => {
58
+ issues.add(issue);
59
+ return `[#${issue}](${issueURL}${issue})`;
60
+ });
61
+ }
62
+ if (host != null) {
63
+ // User URLs.
64
+ // eslint-disable-next-line unicorn/no-unsafe-regex
65
+ returnValue = returnValue.replace(/\B@([\da-z](?:-?[\d/a-z]){0,38})/g, (_, username) => username.includes('/') ? `@${username}` : `[@${username}](${host}/${username})`);
66
+ }
67
+ return returnValue;
68
+ })()
69
+ : commit.subject;
70
+ // Remove references that already appear in the subject
71
+ const references = commit.references.filter((reference) => !issues.has(reference.issue));
72
+ return {
73
+ ...commit,
74
+ type,
75
+ hash,
76
+ scope,
77
+ subject,
78
+ references,
79
+ header: commit.header,
80
+ body: commit.body,
81
+ footer: commit.footer,
82
+ merge: commit.merge,
83
+ revert: commit.revert,
84
+ notes,
85
+ mentions: commit.mentions,
86
+ };
87
+ };
88
+ return transform;
89
+ }
90
+ exports.createTransform = createTransform;
@@ -0,0 +1,4 @@
1
+ import type { Options } from 'conventional-changelog-writer';
2
+ export interface WriterOptions extends Options {
3
+ }
4
+ export declare const writerOpts: WriterOptions;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.writerOpts = void 0;
4
+ const node_fs_1 = require("node:fs");
5
+ const node_path_1 = require("node:path");
6
+ const transform_js_1 = require("./transform.js");
7
+ const basePath = (0, node_path_1.resolve)(__dirname, './templates');
8
+ const template = (0, node_fs_1.readFileSync)(`${basePath}/template.hbs`, 'utf8');
9
+ const header = (0, node_fs_1.readFileSync)(`${basePath}/header.hbs`, 'utf8');
10
+ const commit = (0, node_fs_1.readFileSync)(`${basePath}/commit.hbs`, 'utf8');
11
+ const footer = (0, node_fs_1.readFileSync)(`${basePath}/footer.hbs`, 'utf8');
12
+ const author = (0, node_fs_1.readFileSync)(`${basePath}/author.hbs`, 'utf8');
13
+ exports.writerOpts = {
14
+ transform: (0, transform_js_1.createTransform)({}),
15
+ groupBy: 'type',
16
+ commitGroupsSort: 'title',
17
+ commitsSort: ['scope', 'subject'],
18
+ noteGroupsSort: 'title',
19
+ mainTemplate: template,
20
+ headerPartial: header,
21
+ commitPartial: commit.replace(/{{gitUserInfo}}/g, author),
22
+ footerPartial: footer,
23
+ };
package/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "@w5s/conventional-changelog",
3
+ "version": "1.0.0-alpha.1",
4
+ "description": "Conventional changelog plugin for @w5s",
5
+ "keywords": [],
6
+ "homepage": "https://github.com/w5s/project-config/blob/main/packages/conventional-changelog#readme",
7
+ "bugs": {
8
+ "url": "https://github.com/w5s/project-config/issues"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git@github.com:w5s/project-config.git",
13
+ "directory": "packages/conventional-changelog"
14
+ },
15
+ "license": "MIT",
16
+ "author": "Julien Polo <julien.polo@gmail.com>",
17
+ "type": "commonjs",
18
+ "exports": {
19
+ ".": "./lib/index.js",
20
+ "./lib/*": "./lib/*"
21
+ },
22
+ "typings": "./lib/index.d.ts",
23
+ "files": [
24
+ "lib/**/!(*.spec).d.ts",
25
+ "lib/**/!(*.spec).d.ts.map",
26
+ "lib/**/!(*.spec).js.map",
27
+ "lib/**/!(*.spec).js",
28
+ "src/**/!(*.spec).ts",
29
+ "index.js",
30
+ "index.d.ts"
31
+ ],
32
+ "scripts": {
33
+ "build": "concurrently \"npm:build:*\" \":\"",
34
+ "build:src": "tsc",
35
+ "clean": "concurrently \"npm:clean:*\" \":\"",
36
+ "clean:src": "rm -rf lib/*",
37
+ "docs": "md-magic --path '**/*.md' --ignore='node_modules'",
38
+ "format": "concurrently \"npm:format:*\" \":\"",
39
+ "format:src": "eslint . --fix --ext=mjs,cjs,js,jsx,ts,tsx,json,jsonc,json5,yml,yaml",
40
+ "lint": "concurrently \"npm:lint:*\" \":\"",
41
+ "lint:src": "eslint . --ext=mjs,cjs,js,jsx,ts,tsx,json,jsonc,json5,yml,yaml",
42
+ "prepare": "concurrently \"npm:prepare:*\" \":\"",
43
+ "prepublishOnly": "npm run clean;npm run build",
44
+ "spellcheck": "cspell --no-progress '**'",
45
+ "test": "concurrently \"npm:test:*\" ",
46
+ "test:src": "jest"
47
+ },
48
+ "jest": {
49
+ "moduleNameMapper": {
50
+ "^(\\.{1,2}/.*)\\.js$": "$1"
51
+ },
52
+ "preset": "es-jest",
53
+ "testPathIgnorePatterns": [
54
+ "/node_modules/",
55
+ "/lib/",
56
+ "/build/",
57
+ "/.cache/",
58
+ "/docs/",
59
+ "/public/"
60
+ ]
61
+ },
62
+ "dependencies": {
63
+ "@commitlint/types": "^17.0.0",
64
+ "emoji-regex": "^10.2.1",
65
+ "gitmojis": "^3.13.4"
66
+ },
67
+ "devDependencies": {
68
+ "@jest/globals": "29.4.1",
69
+ "@types/conventional-changelog-writer": "4.0.2",
70
+ "@types/conventional-commits-parser": "3.0.3"
71
+ },
72
+ "engines": {
73
+ "node": ">=16.0.0"
74
+ },
75
+ "publishConfig": {
76
+ "access": "public"
77
+ },
78
+ "gitHead": "dd093c2907cf8e136f4adec9a14b176e0851de17"
79
+ }
package/src/data.ts ADDED
@@ -0,0 +1,112 @@
1
+ import type { Commit as CommitDefault } from 'conventional-commits-parser';
2
+
3
+ export type Commit = CommitDefault<never> & {
4
+ hash?: string;
5
+ };
6
+
7
+ export type CommitConventionalType =
8
+ | 'build'
9
+ | 'ci'
10
+ | 'docs'
11
+ | 'feat'
12
+ | 'fix'
13
+ | 'perf'
14
+ | 'refactor'
15
+ | 'revert'
16
+ | 'style'
17
+ | 'test'
18
+ | 'wip'
19
+ | 'chore';
20
+
21
+ export const CommitConventionalType = (() => {
22
+ const enumObject = Object.freeze({
23
+ Build: 'build',
24
+ CI: 'ci',
25
+ Docs: 'docs',
26
+ Feat: 'feat',
27
+ Fix: 'fix',
28
+ Perf: 'perf',
29
+ Refactor: 'refactor',
30
+ Revert: 'revert',
31
+ Style: 'style',
32
+ Test: 'test',
33
+ WIP: 'wip',
34
+ Chore: 'chore',
35
+ });
36
+ const enumValues: readonly CommitConventionalType[] = Object.freeze(Object.values(enumObject).sort());
37
+ const enumValuesSet = new Set(enumValues);
38
+
39
+ const typeData: Record<CommitConventionalType, CommitConventionalTypeData> = {
40
+ feat: {
41
+ emoji: '✨',
42
+ 'en-US': 'Features',
43
+ },
44
+ fix: {
45
+ emoji: '🐛',
46
+ 'en-US': 'Bug Fixes',
47
+ },
48
+ build: {
49
+ emoji: '👷',
50
+ 'en-US': 'Build System',
51
+ },
52
+ chore: {
53
+ emoji: '🎫',
54
+ 'en-US': 'Chores',
55
+ },
56
+ ci: {
57
+ emoji: '🔧',
58
+ 'en-US': 'Continuous Integration',
59
+ },
60
+ docs: {
61
+ emoji: '📝',
62
+ 'en-US': 'Documentation',
63
+ },
64
+ test: {
65
+ emoji: '✅',
66
+ 'en-US': 'Tests',
67
+ },
68
+ perf: {
69
+ emoji: '⚡',
70
+ 'en-US': 'Performance Improvements',
71
+ },
72
+ refactor: {
73
+ emoji: '♻',
74
+ 'en-US': 'Code Refactoring',
75
+ },
76
+ revert: {
77
+ emoji: '⏪',
78
+ 'en-US': 'Reverts',
79
+ },
80
+ style: {
81
+ emoji: '💄',
82
+ 'en-US': 'Styles',
83
+ },
84
+ wip: {
85
+ emoji: '🚧',
86
+ 'en-US': 'Work in progress',
87
+ },
88
+ };
89
+
90
+ function hasInstance(anyValue: unknown): anyValue is CommitConventionalType {
91
+ return typeof anyValue === 'string' && enumValuesSet.has(anyValue as unknown as CommitConventionalType);
92
+ }
93
+
94
+ function getData(commitType: CommitConventionalType): CommitConventionalTypeData {
95
+ return typeData[commitType];
96
+ }
97
+
98
+ function parse(anyValue: string): CommitConventionalType | undefined {
99
+ return hasInstance(anyValue) ? anyValue : undefined;
100
+ }
101
+
102
+ function values() {
103
+ return enumValues;
104
+ }
105
+
106
+ return { ...enumObject, hasInstance, getData, values, parse };
107
+ })();
108
+
109
+ export interface CommitConventionalTypeData {
110
+ emoji: string;
111
+ 'en-US': string;
112
+ }
package/src/gitmoji.ts ADDED
@@ -0,0 +1,98 @@
1
+ import emojiRegexp from 'emoji-regex';
2
+ import { Gitmoji, gitmojis } from 'gitmojis';
3
+ import type { CommitConventionalType } from './data';
4
+
5
+ export type Emoji = Emoji.Unicode | Emoji.Text;
6
+ export namespace Emoji {
7
+ export const reEmojiUnicode = emojiRegexp();
8
+
9
+ export const reEmojiText = /:(\w+):/;
10
+
11
+ const reMatchOnly = (input: RegExp) => new RegExp(`^${input.source}$`, input.flags);
12
+ const _reEmojiUnicode = reMatchOnly(reEmojiUnicode);
13
+ const _reEmojiText = reMatchOnly(reEmojiText);
14
+
15
+ export type Unicode = string & { '@@EmojiStyle': 'unicode' };
16
+ export type Text = string & { '@@EmojiStyle': 'text' };
17
+
18
+ export function isUnicode(anyValue: string): anyValue is Unicode {
19
+ return anyValue.match(_reEmojiUnicode) != null;
20
+ }
21
+
22
+ export function isText(anyValue: string): anyValue is Text {
23
+ return anyValue.match(_reEmojiText) != null;
24
+ }
25
+
26
+ export function hasInstance(anyValue: string): anyValue is Emoji {
27
+ return isText(anyValue) || isUnicode(anyValue);
28
+ }
29
+ }
30
+
31
+ export type GitmojiCode = Emoji & { '@@Gitmoji': true };
32
+ export namespace GitmojiCode {
33
+ export type Unicode = Emoji.Unicode & { '@@Gitmoji': true };
34
+ export type Emoji = Emoji.Text & { '@@Gitmoji': true };
35
+
36
+ // export const reEmoji = emojiRegexp();
37
+
38
+ const allGitmojiCodes = new Set(
39
+ gitmojis
40
+ .map((gitmoji) => gitmoji.code as GitmojiCode)
41
+ .concat(gitmojis.map((gitmoji) => gitmoji.emoji as GitmojiCode))
42
+ );
43
+ const index = {
44
+ // code: createIndex(gitmojis, 'code'),
45
+ emoji: createIndex(gitmojis, 'emoji'),
46
+ };
47
+
48
+ function createIndex<K extends keyof Gitmoji>(list: readonly Gitmoji[], key: K): ReadonlyMap<Gitmoji[K], Gitmoji> {
49
+ return new Map(list.map((gitmoji) => [gitmoji[key], gitmoji]));
50
+ }
51
+
52
+ export function isValid(anyValue: string): anyValue is GitmojiCode {
53
+ return allGitmojiCodes.has(anyValue as GitmojiCode);
54
+ }
55
+
56
+ const defaultType = 'chore';
57
+ const conversionMap: ReadonlyMap<GitmojiCode, CommitConventionalType> = (() => {
58
+ const data: Record<CommitConventionalType, GitmojiCode.Unicode[]> = {
59
+ feat: ['✨', '♿️', '🚸'] as GitmojiCode.Unicode[],
60
+ fix: ['🐛'] as GitmojiCode.Unicode[],
61
+ docs: ['📝'] as GitmojiCode.Unicode[],
62
+ style: ['🎨', '🚨'] as GitmojiCode.Unicode[],
63
+ refactor: ['♻️', '🏗️'] as GitmojiCode.Unicode[],
64
+ test: ['✅'] as GitmojiCode.Unicode[],
65
+ perf: ['⚡️'] as GitmojiCode.Unicode[],
66
+ revert: ['⏪️'] as GitmojiCode.Unicode[],
67
+ ci: ['👷', '💚'] as GitmojiCode.Unicode[],
68
+ wip: ['🚧'] as GitmojiCode.Unicode[],
69
+ build: [] as GitmojiCode.Unicode[],
70
+ chore: [] as GitmojiCode.Unicode[],
71
+ };
72
+
73
+ const entries = Array.from<[CommitConventionalType, GitmojiCode.Unicode[]]>(
74
+ // @ts-ignore
75
+ Object.entries(data)
76
+ );
77
+ return new Map(
78
+ entries.reduce<Array<[GitmojiCode, CommitConventionalType]>>(
79
+ (acc, [commitType, gitmojiUnicodeArray]) =>
80
+ acc
81
+ .concat(gitmojiUnicodeArray.map((gitmojiUnicode) => [gitmojiUnicode, commitType]))
82
+
83
+ .concat(
84
+ gitmojiUnicodeArray.map((gitmojiUnicode) => [
85
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
86
+ index.emoji.get(gitmojiUnicode)?.code! as GitmojiCode,
87
+ commitType,
88
+ ])
89
+ ),
90
+ []
91
+ )
92
+ );
93
+ })();
94
+
95
+ export function toConventionalCommitType(gitmoji: GitmojiCode): CommitConventionalType {
96
+ return conversionMap.get(gitmoji) ?? defaultType;
97
+ }
98
+ }
package/src/index.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { parserOpts } from './parser-opts';
2
+
3
+ export default {
4
+ parserOpts,
5
+ };
@@ -0,0 +1,13 @@
1
+ import type { Options } from 'conventional-commits-parser';
2
+ import { Emoji } from './gitmoji';
3
+
4
+ export interface ParserOptions extends Options {}
5
+
6
+ export const parserOpts: ParserOptions = {
7
+ headerPattern: new RegExp(`^(:\\w*:|${Emoji.reEmojiUnicode.source}) (?:\\((.*)\\):? )?(.*)$`),
8
+ headerCorrespondence: ['type', 'scope', 'subject'],
9
+ revertPattern: /^(?:revert|revert:)\s"?([\S\s]+?)"?\s*this reverts commit (\w*)\./i,
10
+ noteKeywords: ['BREAKING CHANGE', 'BREAKING CHANGES'],
11
+ // revertPattern: /revert:\s([\S\s]*?)\s*this reverts commit (\w*)\./i,
12
+ revertCorrespondence: [`header`, `hash`],
13
+ };
@@ -0,0 +1,122 @@
1
+ import type { Context, Options as WriterOptions } from 'conventional-changelog-writer';
2
+ import { CommitConventionalType, Commit } from './data.js';
3
+ import { GitmojiCode } from './gitmoji.js';
4
+
5
+ export type Language = 'en-US';
6
+
7
+ export interface TransformConfig {
8
+ scopeDisplayName?: Record<string, string>;
9
+ displayTypes?: CommitConventionalType[];
10
+ displayScopes?: string[];
11
+ showAuthor?: boolean;
12
+ withEmoji?: boolean;
13
+ language?: Language;
14
+ }
15
+
16
+ export function displayScope(scope: string | null | undefined, scopeDisplayNameMap: Record<string, string>) {
17
+ return scope == null || scope.length === 0
18
+ ? scopeDisplayNameMap['*']
19
+ : scopeDisplayNameMap[scope] == null
20
+ ? scope
21
+ : scopeDisplayNameMap[scope];
22
+ }
23
+
24
+ export function displayType(type: CommitConventionalType, options: displayType.Options = {}): string {
25
+ const { withEmoji = true, language = 'en-US' } = options;
26
+
27
+ if (CommitConventionalType.hasInstance(type)) {
28
+ const { emoji, [language]: title } = CommitConventionalType.getData(type);
29
+ return `${withEmoji ? `${emoji} ` : ''}${title}`;
30
+ }
31
+
32
+ return type;
33
+ }
34
+ export namespace displayType {
35
+ export interface Options {
36
+ readonly withEmoji?: boolean;
37
+ readonly language?: Language;
38
+ }
39
+ }
40
+
41
+ export function createTransform(config: TransformConfig): WriterOptions.Transform.Function {
42
+ const displayTypes = new Set(config.displayTypes == null ? CommitConventionalType.values() : config.displayTypes);
43
+ const ignoreType = (type: string | undefined) => type == null || !displayTypes.has(type as CommitConventionalType);
44
+ const ignoreScope = (scope: string | undefined | null) =>
45
+ config.displayScopes == null ? false : scope != null && !config.displayScopes.includes(scope);
46
+
47
+ const transform = (commit: Commit, { repository, host, owner, repoUrl }: Context): Commit | false => {
48
+ const discard = commit.notes.length === 0;
49
+ const issues = new Set<string>();
50
+ const notes = commit.notes.map((note) => ({
51
+ ...note,
52
+ title: `${config.withEmoji === false ? '' : '💥 '}BREAKING CHANGES`,
53
+ }));
54
+ const conventionalType =
55
+ commit.type == null
56
+ ? undefined
57
+ : CommitConventionalType.parse(commit.type) ??
58
+ (GitmojiCode.isValid(commit.type) ? GitmojiCode.toConventionalCommitType(commit.type) : undefined);
59
+
60
+ if (ignoreType(conventionalType) && discard) return false;
61
+
62
+ const type =
63
+ conventionalType == null
64
+ ? conventionalType
65
+ : displayType(conventionalType, {
66
+ withEmoji: config.withEmoji,
67
+ });
68
+
69
+ if (ignoreScope(commit.scope)) return false;
70
+
71
+ const scopeIntermediate = commit.scope === '*' ? '' : commit.scope;
72
+ const scope =
73
+ config.scopeDisplayName == null ? scopeIntermediate : displayScope(scopeIntermediate, config.scopeDisplayName);
74
+ const hash = typeof commit.hash === 'string' ? commit.hash.slice(0, 7) : commit.hash;
75
+
76
+ const subject =
77
+ typeof commit.subject === 'string'
78
+ ? (() => {
79
+ let returnValue = commit.subject;
80
+ const url = repository == null ? repoUrl : [host, owner, repository].filter(Boolean).join('/');
81
+ if (url != null) {
82
+ const issueURL = `${url}/issues/`;
83
+ // Issue URLs.
84
+ returnValue = returnValue.replace(/#(\d+)/g, (_, issue: string) => {
85
+ issues.add(issue);
86
+
87
+ return `[#${issue}](${issueURL}${issue})`;
88
+ });
89
+ }
90
+ if (host != null) {
91
+ // User URLs.
92
+ // eslint-disable-next-line unicorn/no-unsafe-regex
93
+ returnValue = returnValue.replace(/\B@([\da-z](?:-?[\d/a-z]){0,38})/g, (_, username: string) =>
94
+ username.includes('/') ? `@${username}` : `[@${username}](${host}/${username})`
95
+ );
96
+ }
97
+ return returnValue;
98
+ })()
99
+ : commit.subject;
100
+
101
+ // Remove references that already appear in the subject
102
+ const references = commit.references.filter((reference) => !issues.has(reference.issue));
103
+
104
+ return {
105
+ ...commit,
106
+ type,
107
+ hash,
108
+ scope,
109
+ subject,
110
+ references,
111
+ header: commit.header,
112
+ body: commit.body,
113
+ footer: commit.footer,
114
+ merge: commit.merge,
115
+ revert: commit.revert,
116
+ notes,
117
+ mentions: commit.mentions,
118
+ };
119
+ };
120
+
121
+ return transform as unknown as WriterOptions.Transform.Function;
122
+ }
@@ -0,0 +1,26 @@
1
+ import type { Options } from 'conventional-changelog-writer';
2
+ import { readFileSync } from 'node:fs';
3
+ import { resolve } from 'node:path';
4
+ import { createTransform } from './transform.js';
5
+
6
+ export interface WriterOptions extends Options {}
7
+
8
+ const basePath = resolve(__dirname, './templates');
9
+
10
+ const template = readFileSync(`${basePath}/template.hbs`, 'utf8');
11
+ const header = readFileSync(`${basePath}/header.hbs`, 'utf8');
12
+ const commit = readFileSync(`${basePath}/commit.hbs`, 'utf8');
13
+ const footer = readFileSync(`${basePath}/footer.hbs`, 'utf8');
14
+ const author = readFileSync(`${basePath}/author.hbs`, 'utf8');
15
+
16
+ export const writerOpts: WriterOptions = {
17
+ transform: createTransform({}),
18
+ groupBy: 'type',
19
+ commitGroupsSort: 'title',
20
+ commitsSort: ['scope', 'subject'],
21
+ noteGroupsSort: 'title',
22
+ mainTemplate: template,
23
+ headerPartial: header,
24
+ commitPartial: commit.replace(/{{gitUserInfo}}/g, author),
25
+ footerPartial: footer,
26
+ };