@thisismanta/semantic-version 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/README.md ADDED
@@ -0,0 +1,20 @@
1
+ Once installed, this package verifies your upcoming **Git commit messages** against the convention and automatically runs `npm version` after a commit if needed.
2
+
3
+ ## Git commit message convention
4
+
5
+ ```
6
+ <type>[!]: <subject>
7
+ ```
8
+ Where
9
+ - `<type>` can be either `feat`, `fix`, `test`, `refactor` or `chore`.
10
+ - `!` indicates that the commit contains breaking changes.
11
+ - `<subject>` is the actual commit message where the first word must be written in lower cases.
12
+
13
+ ## Versioning
14
+
15
+ |Commit message type|Post-commit command|
16
+ |---|---|
17
+ |`!`|`npm version major`|
18
+ |`feat`|`npm version minor`|
19
+ |`fix`|`npm version patch`|
20
+ |Others|Does not run `npm version`|
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ require('../lib/auto-npm-version')
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ require('../lib/lint-commit-message')
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const execa_1 = require("execa");
4
+ const index_1 = require("./index");
5
+ main();
6
+ async function main() {
7
+ const message = await run('git log -1 HEAD --format=%s');
8
+ console.log('message »', message);
9
+ const { type, breaking } = (0, index_1.checkConventionalMessage)(message, console);
10
+ const releaseType = (() => {
11
+ if (breaking) {
12
+ return 'major';
13
+ }
14
+ else if (type === 'feat') {
15
+ return 'minor';
16
+ }
17
+ else if (type === 'fix') {
18
+ return 'patch';
19
+ }
20
+ // Indicate no version bump
21
+ return '';
22
+ })();
23
+ console.log('releaseType »', releaseType);
24
+ if (!releaseType) {
25
+ return;
26
+ }
27
+ const newVersion = await run(`npm version --json --no-commit-hooks ${releaseType}`);
28
+ await run(`git push origin refs/tags/${newVersion}`);
29
+ }
30
+ async function run(command) {
31
+ console.log(command);
32
+ const { stdout } = await (0, execa_1.execaCommand)(command);
33
+ console.log(stdout);
34
+ return stdout;
35
+ }
package/lib/index.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ export declare const allowedTypes: string[];
2
+ export declare function checkConventionalMessage(message: string, { debug }: Pick<Console, 'debug'>): {
3
+ type: string | undefined;
4
+ breaking: boolean;
5
+ subject: string | undefined;
6
+ errors: string[];
7
+ };
package/lib/index.js ADDED
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkConventionalMessage = exports.allowedTypes = void 0;
4
+ const titlePattern = /^(?<type>\w+)(?<scope>\(.*?\))?(?<breaking>\!)?:(?<subject>.+)/;
5
+ exports.allowedTypes = ['feat', 'fix', 'test', 'refactor', 'chore'];
6
+ function checkConventionalMessage(message, { debug }) {
7
+ const pattern = (message.match(titlePattern)?.groups || {});
8
+ debug(JSON.stringify(pattern, null, 2));
9
+ const { type, scope, breaking, subject } = pattern;
10
+ const errors = [
11
+ !type &&
12
+ 'The pull request title must match the pattern of "<type>[!]: <subject>" which is a reduced set of https://www.conventionalcommits.org/en/v1.0.0/',
13
+ typeof type === 'string' && exports.allowedTypes.includes(type.toLowerCase()) === false &&
14
+ 'The type in a pull request title must be one of ' + exports.allowedTypes.map(name => '"' + name + '"').join(', ') + '.',
15
+ typeof type === 'string' && /^[a-z]+$/.test(type) === false &&
16
+ 'The type in a pull request title must be in lower case only.',
17
+ scope &&
18
+ 'A scope in a pull request title is never allowed.',
19
+ typeof type === 'string' && typeof subject !== 'string' &&
20
+ 'The subject in a pull request title must be provided.',
21
+ typeof subject === 'string' && (subject.match(/^ +/)?.[0].length || 0) !== 1 &&
22
+ 'A single space must be after ":" symbol.',
23
+ typeof subject === 'string' && /^[a-z]/.test(subject.trim()) === false &&
24
+ 'The subject must start with a lower case latin alphabet.',
25
+ ].filter((error) => typeof error === 'string');
26
+ return {
27
+ type,
28
+ breaking: !!breaking,
29
+ subject: typeof subject === 'string' ? subject.trim() : subject,
30
+ errors
31
+ };
32
+ }
33
+ exports.checkConventionalMessage = checkConventionalMessage;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const index_1 = require("./index");
4
+ const debug = jest.fn();
5
+ it('does not return any error, given a valid pattern', () => {
6
+ for (const type of index_1.allowedTypes) {
7
+ expect((0, index_1.checkConventionalMessage)(type + ': xxx', { debug })).toEqual({
8
+ type: type,
9
+ breaking: false,
10
+ subject: 'xxx',
11
+ errors: []
12
+ });
13
+ }
14
+ expect((0, index_1.checkConventionalMessage)('chore!: xxx', { debug })).toEqual({
15
+ type: 'chore',
16
+ breaking: true,
17
+ subject: 'xxx',
18
+ errors: []
19
+ });
20
+ });
21
+ it('returns the error, given an invalid pattern', () => {
22
+ expect((0, index_1.checkConventionalMessage)('xxx', { debug })).toMatchObject({
23
+ errors: [
24
+ 'The pull request title must match the pattern of "<type>[!]: <subject>" which is a reduced set of https://www.conventionalcommits.org/en/v1.0.0/'
25
+ ]
26
+ });
27
+ });
28
+ it('returns the error, given an unknown type', () => {
29
+ expect((0, index_1.checkConventionalMessage)('unknown: xxx', { debug })).toMatchObject({
30
+ type: 'unknown',
31
+ errors: [
32
+ 'The type in a pull request title must be one of \"feat\", \"fix\", \"test\", \"refactor\", \"chore\".'
33
+ ]
34
+ });
35
+ });
36
+ it('returns the error, given a non-lower-case type', () => {
37
+ expect((0, index_1.checkConventionalMessage)('CHORE: xxx', { debug })).toMatchObject({
38
+ errors: [
39
+ 'The type in a pull request title must be in lower case only.'
40
+ ]
41
+ });
42
+ });
43
+ it('returns the error, given a scope', () => {
44
+ expect((0, index_1.checkConventionalMessage)('chore(scope): xxx', { debug })).toMatchObject({
45
+ errors: [
46
+ 'A scope in a pull request title is never allowed.'
47
+ ]
48
+ });
49
+ });
50
+ it('returns the error, given zero or more-than-one spaces after ":" symbol', () => {
51
+ expect((0, index_1.checkConventionalMessage)('chore:xxx', { debug })).toMatchObject({
52
+ subject: 'xxx',
53
+ errors: [
54
+ 'A single space must be after ":" symbol.'
55
+ ]
56
+ });
57
+ expect((0, index_1.checkConventionalMessage)('chore: xxx', { debug })).toMatchObject({
58
+ subject: 'xxx',
59
+ errors: [
60
+ 'A single space must be after ":" symbol.'
61
+ ]
62
+ });
63
+ });
64
+ it('returns the error, given the first word in non-lower-case for the subject', () => {
65
+ expect((0, index_1.checkConventionalMessage)('chore: Xxx', { debug })).toMatchObject({
66
+ errors: [
67
+ 'The subject must start with a lower case latin alphabet.'
68
+ ]
69
+ });
70
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ const fs = __importStar(require("fs/promises"));
27
+ const fp = __importStar(require("path"));
28
+ main();
29
+ async function main() {
30
+ const huskyDirectoryPath = fp.resolve(process.cwd(), '.husky');
31
+ await upsert(fp.join(huskyDirectoryPath, 'commit-msg'), 'npx lint-commit-message ${1}');
32
+ await upsert(fp.join(huskyDirectoryPath, 'post-commit'), 'npx auto-npm-version');
33
+ }
34
+ async function upsert(filePath, text) {
35
+ try {
36
+ await fs.access(filePath);
37
+ }
38
+ catch (error) {
39
+ console.log('Created', filePath);
40
+ await fs.writeFile(filePath, '#!/usr/bin/env sh' + '\n' +
41
+ '. "$(dirname -- "$0")/_/husky.sh"' + '\n', 'utf-8');
42
+ }
43
+ const fileText = await fs.readFile(filePath, 'utf-8');
44
+ const lines = fileText.trim().split('\n');
45
+ const index = lines
46
+ .findIndex(line => line.trim().includes(text));
47
+ if (index === -1) {
48
+ await fs.appendFile(filePath, '\n' + text + '\n', 'utf-8');
49
+ console.log('Added', text, 'to', filePath);
50
+ }
51
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ const fs = __importStar(require("fs/promises"));
27
+ const index_1 = require("./index");
28
+ main();
29
+ async function main() {
30
+ const [messageFilePath] = process.argv.slice(2);
31
+ const message = (await fs.readFile(messageFilePath, 'utf-8')).trim();
32
+ const { errors } = (0, index_1.checkConventionalMessage)(message, console);
33
+ if (errors.length > 0) {
34
+ throw new Error(errors[0]);
35
+ }
36
+ }
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@thisismanta/semantic-version",
3
+ "version": "1.0.0",
4
+ "author": "Anantachai Saothong <thisismanta@gmail.com>",
5
+ "license": "ISC",
6
+ "engines": {
7
+ "node": ">=16.0.0"
8
+ },
9
+ "main": "./lib/index.js",
10
+ "types": "./lib/index.d.ts",
11
+ "bin": {
12
+ "lint-commit-message": "./bin/lint-commit-message",
13
+ "auto-npm-version": "./bin/auto-npm-version"
14
+ },
15
+ "files": [
16
+ "./lib",
17
+ "./bin"
18
+ ],
19
+ "scripts": {
20
+ "test": "jest",
21
+ "build": "tsc",
22
+ "preversion": "npm run test && npm run build && git add lib/*",
23
+ "version": "npm publish --access public",
24
+ "postversion": "git push --tags origin master",
25
+ "postinstall": "husky install && node ./lib/install-git-hooks.js",
26
+ "prepare": "husky uninstall && rm -rf .husky"
27
+ },
28
+ "devDependencies": {
29
+ "@types/jest": "^29.4.0",
30
+ "@types/node": "^16.0.0",
31
+ "jest": "^29.4.3",
32
+ "ts-jest": "^29.0.5",
33
+ "typescript": "^4.9.5"
34
+ },
35
+ "dependencies": {
36
+ "execa": "npm:@esm2cjs/execa@^6.1.1-cjs.1",
37
+ "husky": "^8.0.3"
38
+ },
39
+ "jest": {
40
+ "preset": "ts-jest",
41
+ "resetMocks": true
42
+ }
43
+ }