From 46596a3a49b4e884f6c7b8146e5c34e1124b3f34 Mon Sep 17 00:00:00 2001 From: Cedric van Putten Date: Sat, 27 Jul 2019 10:42:24 +0000 Subject: [PATCH 1/6] refactor: port parse to typescript --- @commitlint/parse/package.json | 31 +---- .../src/{index.test.js => index.test.ts} | 125 ++++++++++-------- @commitlint/parse/src/{index.js => index.ts} | 11 +- @commitlint/parse/tsconfig.json | 15 +++ tsconfig.json | 3 +- yarn.lock | 4 +- 6 files changed, 99 insertions(+), 90 deletions(-) rename @commitlint/parse/src/{index.test.js => index.test.ts} (61%) rename @commitlint/parse/src/{index.js => index.ts} (59%) create mode 100644 @commitlint/parse/tsconfig.json diff --git a/@commitlint/parse/package.json b/@commitlint/parse/package.json index 5af437cce1..52b4905c6f 100644 --- a/@commitlint/parse/package.json +++ b/@commitlint/parse/package.json @@ -3,35 +3,13 @@ "version": "8.1.0", "description": "Lint your commit messages", "main": "lib/index.js", + "types": "lib/index.d.ts", "files": [ "lib/" ], "scripts": { - "build": "cross-env NODE_ENV=production babel src --out-dir lib --source-maps", "deps": "dep-check", - "pkg": "pkg-check", - "start": "concurrently \"ava -c 4 --verbose --watch\" \"yarn run watch\"", - "test": "ava -c 4 --verbose", - "watch": "babel src --out-dir lib --watch --source-maps" - }, - "ava": { - "files": [ - "src/**/*.test.js", - "!lib/**/*" - ], - "source": [ - "src/**/*.js", - "!lib/**/*" - ], - "babel": "inherit", - "require": [ - "babel-register" - ] - }, - "babel": { - "presets": [ - "babel-preset-commitlint" - ] + "pkg": "pkg-check" }, "engines": { "node": ">=4" @@ -58,13 +36,14 @@ "devDependencies": { "@commitlint/test": "8.0.0", "@commitlint/utils": "^8.1.0", - "ava": "0.22.0", + "@types/lodash": "^4.14.136", "babel-cli": "6.26.0", "babel-preset-commitlint": "^8.0.0", "babel-register": "6.26.0", "concurrently": "3.6.1", "cross-env": "5.1.1", - "import-from": "3.0.0" + "import-from": "3.0.0", + "typescript": "^3.5.3" }, "dependencies": { "conventional-changelog-angular": "^1.3.3", diff --git a/@commitlint/parse/src/index.test.js b/@commitlint/parse/src/index.test.ts similarity index 61% rename from @commitlint/parse/src/index.test.js rename to @commitlint/parse/src/index.test.ts index f44a1182bb..58e9a8853f 100644 --- a/@commitlint/parse/src/index.test.js +++ b/@commitlint/parse/src/index.test.ts @@ -1,40 +1,40 @@ import importFrom from 'import-from'; -import test from 'ava'; import parse from '.'; -test('throws when called without params', async t => { - const error = await t.throws(parse()); - t.is(error.message, 'Expected a raw commit'); +test('throws when called without params', () => { + expect(parse()).rejects.toThrowError('Expected a raw commit'); }); -test('throws when called with empty message', async t => { - const error = await t.throws(parse()); - t.is(error.message, 'Expected a raw commit'); +test('throws when called with empty message', () => { + expect(parse()).rejects.toThrowError('Expected a raw commit'); }); -test('returns object with raw message', async t => { +test('returns object with raw message', async () => { const message = 'type(scope): subject'; const actual = await parse(message); - t.is(actual.raw, message); + + expect(actual).toHaveProperty('raw', message); }); -test('calls parser with message and passed options', async t => { +test('calls parser with message and passed options', async () => { const message = 'message'; - await parse(message, m => { - t.is(message, m); + expect.assertions(1); + await parse(message, (m: string) => { + expect(m).toBe(message); return {}; }); }); -test('passes object up from parser function', async t => { +test('passes object up from parser function', async () => { const message = 'message'; const result = {}; const actual = await parse(message, () => result); - t.is(actual, result); + + expect(actual).toBe(result); }); -test('returns object with expected keys', async t => { +test('returns object with expected keys', async () => { const message = 'message'; const actual = await parse(message); const expected = { @@ -51,10 +51,11 @@ test('returns object with expected keys', async t => { subject: null, type: null }; - t.deepEqual(actual, expected); + + expect(actual).toMatchObject(expected); }); -test('uses angular grammar', async t => { +test('uses angular grammar', async () => { const message = 'type(scope): subject'; const actual = await parse(message); const expected = { @@ -71,14 +72,15 @@ test('uses angular grammar', async t => { subject: 'subject', type: 'type' }; - t.deepEqual(actual, expected); + + expect(actual).toMatchObject(expected); }); -test('uses custom opts parser', async t => { +test('uses custom opts parser', async () => { const message = 'type(scope)-subject'; - const changelogOpts = await importFrom( - process.cwd(), - './fixtures/parser-preset/conventional-changelog-custom' + const changelogOpts: any = await importFrom( + __dirname, + '../fixtures/parser-preset/conventional-changelog-custom.js' ); const actual = await parse(message, undefined, changelogOpts.parserOpts); const expected = { @@ -95,10 +97,11 @@ test('uses custom opts parser', async t => { subject: 'subject', type: 'type' }; - t.deepEqual(actual, expected); + + expect(actual).toMatchObject(expected); }); -test('does not merge array properties with custom opts', async t => { +test('does not merge array properties with custom opts', async () => { const message = 'type: subject'; const actual = await parse(message, undefined, { headerPattern: /^(.*):\s(.*)$/, @@ -117,26 +120,29 @@ test('does not merge array properties with custom opts', async t => { subject: 'subject', type: 'type' }; - t.deepEqual(actual, expected); + + expect(actual).toMatchObject(expected); }); -test('supports scopes with /', async t => { +test('supports scopes with /', async () => { const message = 'type(some/scope): subject'; const actual = await parse(message); - t.is(actual.scope, 'some/scope'); - t.is(actual.subject, 'subject'); + + expect(actual.scope).toBe('some/scope'); + expect(actual.subject).toBe('subject'); }); -test('supports scopes with / and empty parserOpts', async t => { +test('supports scopes with / and empty parserOpts', async () => { const message = 'type(some/scope): subject'; const actual = await parse(message, undefined, {}); - t.is(actual.scope, 'some/scope'); - t.is(actual.subject, 'subject'); + + expect(actual.scope).toBe('some/scope'); + expect(actual.subject).toBe('subject'); }); -test('ignores comments', async t => { +test('ignores comments', async () => { const message = 'type(some/scope): subject\n# some comment'; - const changelogOpts = await importFrom( + const changelogOpts: any = await importFrom( process.cwd(), 'conventional-changelog-angular' ); @@ -144,15 +150,16 @@ test('ignores comments', async t => { commentChar: '#' }); const actual = await parse(message, undefined, opts); - t.is(actual.body, null); - t.is(actual.footer, null); - t.is(actual.subject, 'subject'); + + expect(actual.body).toBe(null); + expect(actual.footer).toBe(null); + expect(actual.subject).toBe('subject'); }); -test('registers inline #', async t => { +test('registers inline #', async () => { const message = 'type(some/scope): subject #reference\n# some comment\nthings #reference'; - const changelogOpts = await importFrom( + const changelogOpts: any = await importFrom( process.cwd(), 'conventional-changelog-angular' ); @@ -160,11 +167,12 @@ test('registers inline #', async t => { commentChar: '#' }); const actual = await parse(message, undefined, opts); - t.is(actual.subject, 'subject #reference'); - t.is(actual.body, 'things #reference'); + + expect(actual.subject).toBe('subject #reference'); + expect(actual.body).toBe('things #reference'); }); -test('parses references leading subject', async t => { +test('parses references leading subject', async () => { const message = '#1 some subject'; const opts = await importFrom( process.cwd(), @@ -173,17 +181,18 @@ test('parses references leading subject', async t => { const { references: [actual] } = await parse(message, undefined, opts); - t.is(actual.issue, '1'); + + expect(actual.issue).toBe('1'); }); -test('parses custom references', async t => { +test('parses custom references', async () => { const message = '#1 some subject PREFIX-2'; const {references} = await parse(message, undefined, { issuePrefixes: ['PREFIX-'] }); - t.falsy(references.find(ref => ref.issue === '1')); - t.deepEqual(references.find(ref => ref.issue === '2'), { + expect(references.find((ref: any) => ref.issue === '1')).toBeFalsy(); + expect(references.find((ref: any) => ref.issue === '2')).toMatchObject({ action: null, issue: '2', owner: null, @@ -193,44 +202,44 @@ test('parses custom references', async t => { }); }); -test('uses permissive default regex without parser opts', async t => { +test('uses permissive default regex without parser opts', async () => { const message = 'chore(component,demo): bump'; const actual = await parse(message); - t.is(actual.scope, 'component,demo'); + expect(actual.scope).toBe('component,demo'); }); -test('uses permissive default regex with other parser opts', async t => { +test('uses permissive default regex with other parser opts', async () => { const message = 'chore(component,demo): bump'; const actual = await parse(message, undefined, {commentChar: '#'}); - t.is(actual.scope, 'component,demo'); + expect(actual.scope).toBe('component,demo'); }); -test('uses restrictive default regex in passed parser opts', async t => { +test('uses restrictive default regex in passed parser opts', async () => { const message = 'chore(component,demo): bump'; const actual = await parse(message, undefined, { headerPattern: /^(\w*)(?:\(([a-z]*)\))?: (.*)$/ }); - t.is(actual.subject, null); - t.is(actual.scope, null); + expect(actual.subject).toBe(null); + expect(actual.scope).toBe(null); }); -test('works with chinese scope by default', async t => { +test('works with chinese scope by default', async () => { const message = 'fix(面试评价): 测试'; const actual = await parse(message, undefined, {commentChar: '#'}); - t.not(actual.subject, null); - t.not(actual.scope, null); + expect(actual.subject).not.toBe(null); + expect(actual.scope).not.toBe(null); }); -test('does not work with chinese scopes with incompatible pattern', async t => { +test('does not work with chinese scopes with incompatible pattern', async () => { const message = 'fix(面试评价): 测试'; const actual = await parse(message, undefined, { headerPattern: /^(\w*)(?:\(([a-z]*)\))?: (.*)$/ }); - t.is(actual.subject, null); - t.is(actual.scope, null); + expect(actual.subject).toBe(null); + expect(actual.scope).toBe(null); }); diff --git a/@commitlint/parse/src/index.js b/@commitlint/parse/src/index.ts similarity index 59% rename from @commitlint/parse/src/index.js rename to @commitlint/parse/src/index.ts index ee4f7742ce..92ae71641e 100644 --- a/@commitlint/parse/src/index.js +++ b/@commitlint/parse/src/index.ts @@ -1,10 +1,15 @@ -import {sync} from 'conventional-commits-parser'; -import defaultChangelogOpts from 'conventional-changelog-angular'; import {isArray, mergeWith} from 'lodash'; +const {sync} = require('conventional-commits-parser'); +const defaultChangelogOpts = require('conventional-changelog-angular'); + export default parse; -async function parse(message, parser = sync, parserOpts = undefined) { +async function parse( + message?: any, + parser: any = sync, + parserOpts: any = undefined +) { const defaultOpts = (await defaultChangelogOpts).parserOpts; const parsed = parser( message, diff --git a/@commitlint/parse/tsconfig.json b/@commitlint/parse/tsconfig.json new file mode 100644 index 0000000000..f4a57643f0 --- /dev/null +++ b/@commitlint/parse/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../../tsconfig.shared.json", + "compilerOptions": { + "composite": true, + "rootDir": "./src", + "outDir": "./lib" + }, + "include": [ + "./src" + ], + "exclude": [ + "./src/**/*.test.ts", + "./lib/**/*" + ] +} diff --git a/tsconfig.json b/tsconfig.json index 48225a91cc..e710a7f3bd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,8 +7,9 @@ { "path": "@commitlint/execute-rule" }, { "path": "@commitlint/format" }, { "path": "@commitlint/is-ignored" }, + { "path": "@commitlint/parse" }, { "path": "@commitlint/resolve-extends" }, { "path": "@commitlint/to-lines" }, { "path": "@commitlint/top-level" }, ] -} \ No newline at end of file +} diff --git a/yarn.lock b/yarn.lock index 922c12fd4e..f829f9b116 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1285,7 +1285,7 @@ dependencies: "@types/jest-diff" "*" -"@types/lodash@4.14.136": +"@types/lodash@4.14.136", "@types/lodash@^4.14.136": version "4.14.136" resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.136.tgz#413e85089046b865d960c9ff1d400e04c31ab60f" integrity sha512-0GJhzBdvsW2RUccNHOBkabI8HZVdOXmXbXhuKlDEd5Vv12P7oAVGfomGp3Ne21o5D/qu1WmthlNKFaoZJJeErA== @@ -10913,7 +10913,7 @@ typedarray@^0.0.6: resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@3.5.3: +typescript@3.5.3, typescript@^3.5.3: version "3.5.3" resolved "https://registry.npmjs.org/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977" integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g== From 0974153a18440959b78dd4ba1a4ff8e0cc589192 Mon Sep 17 00:00:00 2001 From: Cedric van Putten Date: Sat, 27 Jul 2019 11:09:53 +0000 Subject: [PATCH 2/6] refactor: add commit interface to parse return type --- @commitlint/parse/src/index.test.ts | 4 ++-- @commitlint/parse/src/index.ts | 4 +++- @commitlint/parse/src/types.ts | 28 ++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 @commitlint/parse/src/types.ts diff --git a/@commitlint/parse/src/index.test.ts b/@commitlint/parse/src/index.test.ts index 58e9a8853f..7064dca92d 100644 --- a/@commitlint/parse/src/index.test.ts +++ b/@commitlint/parse/src/index.test.ts @@ -191,8 +191,8 @@ test('parses custom references', async () => { issuePrefixes: ['PREFIX-'] }); - expect(references.find((ref: any) => ref.issue === '1')).toBeFalsy(); - expect(references.find((ref: any) => ref.issue === '2')).toMatchObject({ + expect(references.find(ref => ref.issue === '1')).toBeFalsy(); + expect(references.find(ref => ref.issue === '2')).toMatchObject({ action: null, issue: '2', owner: null, diff --git a/@commitlint/parse/src/index.ts b/@commitlint/parse/src/index.ts index 92ae71641e..0722b34bbc 100644 --- a/@commitlint/parse/src/index.ts +++ b/@commitlint/parse/src/index.ts @@ -1,15 +1,17 @@ import {isArray, mergeWith} from 'lodash'; +import {Commit} from './types'; const {sync} = require('conventional-commits-parser'); const defaultChangelogOpts = require('conventional-changelog-angular'); export default parse; +export * from './types'; async function parse( message?: any, parser: any = sync, parserOpts: any = undefined -) { +): Promise { const defaultOpts = (await defaultChangelogOpts).parserOpts; const parsed = parser( message, diff --git a/@commitlint/parse/src/types.ts b/@commitlint/parse/src/types.ts new file mode 100644 index 0000000000..c26644be02 --- /dev/null +++ b/@commitlint/parse/src/types.ts @@ -0,0 +1,28 @@ +export interface Commit { + raw: string; + header: string; + type: string | null; + scope: string | null; + subject: string | null; + body: string | null; + footer: string | null; + mentions: string[]; + notes: CommitNote[]; + references: CommitReference[]; + revert: any; + merge: any; +} + +export interface CommitNote { + title: string; + text: string; +} + +export interface CommitReference { + raw: string; + prefix: string; + action: string | null; + owner: string | null; + repository: string | null; + issue: string | null; +} From 8cd30517943bd0a1bc16f1149751bf75ff314bde Mon Sep 17 00:00:00 2001 From: Cedric van Putten Date: Sat, 27 Jul 2019 11:36:36 +0000 Subject: [PATCH 3/6] refactor: remove replaced dependencies from parse --- @commitlint/parse/package.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/@commitlint/parse/package.json b/@commitlint/parse/package.json index 52b4905c6f..774f031c00 100644 --- a/@commitlint/parse/package.json +++ b/@commitlint/parse/package.json @@ -37,11 +37,6 @@ "@commitlint/test": "8.0.0", "@commitlint/utils": "^8.1.0", "@types/lodash": "^4.14.136", - "babel-cli": "6.26.0", - "babel-preset-commitlint": "^8.0.0", - "babel-register": "6.26.0", - "concurrently": "3.6.1", - "cross-env": "5.1.1", "import-from": "3.0.0", "typescript": "^3.5.3" }, From 8f08e676ef61d1305fbf616284790cedbabc806f Mon Sep 17 00:00:00 2001 From: Cedric van Putten Date: Mon, 29 Jul 2019 19:56:47 +0000 Subject: [PATCH 4/6] refactor(parse): add strict options interface --- @commitlint/parse/src/index.test.ts | 2 +- @commitlint/parse/src/index.ts | 6 +++--- @commitlint/parse/src/types.ts | 12 ++++++++++++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/@commitlint/parse/src/index.test.ts b/@commitlint/parse/src/index.test.ts index 7064dca92d..5506b765f4 100644 --- a/@commitlint/parse/src/index.test.ts +++ b/@commitlint/parse/src/index.test.ts @@ -180,7 +180,7 @@ test('parses references leading subject', async () => { ); const { references: [actual] - } = await parse(message, undefined, opts); + } = await parse(message, undefined, opts as any); expect(actual.issue).toBe('1'); }); diff --git a/@commitlint/parse/src/index.ts b/@commitlint/parse/src/index.ts index 0722b34bbc..2251fe5228 100644 --- a/@commitlint/parse/src/index.ts +++ b/@commitlint/parse/src/index.ts @@ -1,5 +1,5 @@ import {isArray, mergeWith} from 'lodash'; -import {Commit} from './types'; +import {Commit, ParserOptions} from './types'; const {sync} = require('conventional-commits-parser'); const defaultChangelogOpts = require('conventional-changelog-angular'); @@ -8,9 +8,9 @@ export default parse; export * from './types'; async function parse( - message?: any, + message?: string, parser: any = sync, - parserOpts: any = undefined + parserOpts?: ParserOptions ): Promise { const defaultOpts = (await defaultChangelogOpts).parserOpts; const parsed = parser( diff --git a/@commitlint/parse/src/types.ts b/@commitlint/parse/src/types.ts index c26644be02..10dcdd71f9 100644 --- a/@commitlint/parse/src/types.ts +++ b/@commitlint/parse/src/types.ts @@ -26,3 +26,15 @@ export interface CommitReference { repository: string | null; issue: string | null; } + +export interface ParserOptions { + commentChar?: string; + headerCorrespondence?: string[]; + headerPattern?: RegExp; + issuePrefixes?: string[]; + mergeCorrespondence?: string[]; + mergePattern?: RegExp; + noteKeywords?: string[]; + revertCorrespondence?: string[]; + revertPattern?: RegExp; +} From c161bb03b6919d9fa4d586ee6d6175dcc6c3fe1e Mon Sep 17 00:00:00 2001 From: Cedric van Putten Date: Mon, 29 Jul 2019 20:53:49 +0000 Subject: [PATCH 5/6] refactor(parse): add strict parser type --- @commitlint/parse/src/index.test.ts | 19 +++++++++---------- @commitlint/parse/src/index.ts | 16 +++++++--------- @commitlint/parse/src/types.ts | 5 +++++ 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/@commitlint/parse/src/index.test.ts b/@commitlint/parse/src/index.test.ts index 5506b765f4..3b85892bdb 100644 --- a/@commitlint/parse/src/index.test.ts +++ b/@commitlint/parse/src/index.test.ts @@ -2,11 +2,7 @@ import importFrom from 'import-from'; import parse from '.'; test('throws when called without params', () => { - expect(parse()).rejects.toThrowError('Expected a raw commit'); -}); - -test('throws when called with empty message', () => { - expect(parse()).rejects.toThrowError('Expected a raw commit'); + expect(parse('')).rejects.toThrowError('Expected a raw commit'); }); test('returns object with raw message', async () => { @@ -20,15 +16,18 @@ test('calls parser with message and passed options', async () => { const message = 'message'; expect.assertions(1); - await parse(message, (m: string) => { - expect(m).toBe(message); - return {}; - }); + await parse( + message, + (m: string): any => { + expect(m).toBe(message); + return {}; + } + ); }); test('passes object up from parser function', async () => { const message = 'message'; - const result = {}; + const result: any = {}; const actual = await parse(message, () => result); expect(actual).toBe(result); diff --git a/@commitlint/parse/src/index.ts b/@commitlint/parse/src/index.ts index 2251fe5228..3f2a8fd72c 100644 --- a/@commitlint/parse/src/index.ts +++ b/@commitlint/parse/src/index.ts @@ -1,5 +1,5 @@ import {isArray, mergeWith} from 'lodash'; -import {Commit, ParserOptions} from './types'; +import {Commit, Parser, ParserOptions} from './types'; const {sync} = require('conventional-commits-parser'); const defaultChangelogOpts = require('conventional-changelog-angular'); @@ -8,17 +8,15 @@ export default parse; export * from './types'; async function parse( - message?: string, - parser: any = sync, + message: string, + parser: Parser = sync, parserOpts?: ParserOptions ): Promise { const defaultOpts = (await defaultChangelogOpts).parserOpts; - const parsed = parser( - message, - mergeWith({}, defaultOpts, parserOpts, (objValue, srcValue) => { - if (isArray(objValue)) return srcValue; - }) - ); + const opts = mergeWith({}, defaultOpts, parserOpts, (objValue, srcValue) => { + if (isArray(objValue)) return srcValue; + }); + const parsed = parser(message, opts) as Commit; parsed.raw = message; return parsed; } diff --git a/@commitlint/parse/src/types.ts b/@commitlint/parse/src/types.ts index 10dcdd71f9..2654086d66 100644 --- a/@commitlint/parse/src/types.ts +++ b/@commitlint/parse/src/types.ts @@ -27,6 +27,11 @@ export interface CommitReference { issue: string | null; } +export type Parser = ( + message: string, + options: ParserOptions +) => Omit; + export interface ParserOptions { commentChar?: string; headerCorrespondence?: string[]; From 7767d519eea96d9d83d325c0bb22cc429de9b0ba Mon Sep 17 00:00:00 2001 From: Cedric van Putten Date: Tue, 30 Jul 2019 17:29:39 +0000 Subject: [PATCH 6/6] test(parse): add empty and no commit message tests --- @commitlint/parse/src/index.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/@commitlint/parse/src/index.test.ts b/@commitlint/parse/src/index.test.ts index 3b85892bdb..5e0f980026 100644 --- a/@commitlint/parse/src/index.test.ts +++ b/@commitlint/parse/src/index.test.ts @@ -2,6 +2,10 @@ import importFrom from 'import-from'; import parse from '.'; test('throws when called without params', () => { + expect((parse as any)()).rejects.toThrowError('Expected a raw commit'); +}); + +test('throws when called with empty message', () => { expect(parse('')).rejects.toThrowError('Expected a raw commit'); });