diff --git a/@commitlint/read/package.json b/@commitlint/read/package.json index de7a556d71..02ed2ea446 100644 --- a/@commitlint/read/package.json +++ b/@commitlint/read/package.json @@ -2,16 +2,14 @@ "name": "@commitlint/read", "version": "8.3.4", "description": "Read commit messages from a specified range or last edit", - "main": "lib/index.js", + "main": "lib/read.js", + "types": "lib/read.d.ts", "files": [ "lib/" ], "scripts": { - "build": "cross-env NODE_ENV=production babel src --out-dir lib --source-maps", "deps": "dep-check", - "pkg": "pkg-check --skip-import", - "start": "yarn run watch", - "watch": "babel src --out-dir lib --watch --source-maps" + "pkg": "pkg-check --skip-import" }, "babel": { "presets": [ @@ -41,13 +39,9 @@ }, "license": "MIT", "devDependencies": { - "@babel/core": "7.7.7", - "@babel/cli": "7.7.7", "@commitlint/test": "8.2.0", "@commitlint/utils": "^8.3.4", - "babel-preset-commitlint": "^8.2.0", - "cross-env": "6.0.3", - "execa": "0.11.0" + "@types/git-raw-commits": "^2.0.0" }, "dependencies": { "@commitlint/top-level": "^8.3.4", diff --git a/@commitlint/read/src/get-edit-commit.ts b/@commitlint/read/src/get-edit-commit.ts new file mode 100644 index 0000000000..a36a7ba0ea --- /dev/null +++ b/@commitlint/read/src/get-edit-commit.ts @@ -0,0 +1,21 @@ +import toplevel from '@commitlint/top-level'; +import {getEditFilePath} from './get-edit-file-path'; + +const sander = require('@marionebl/sander'); + +// Get recently edited commit message +export async function getEditCommit( + cwd?: string, + edit?: boolean | string +): Promise { + const top = await toplevel(cwd); + + if (typeof top !== 'string') { + throw new TypeError(`Could not find git root from ${cwd}`); + } + + const editFilePath = await getEditFilePath(top, edit); + + const editFile: Buffer = await sander.readFile(editFilePath); + return [`${editFile.toString('utf-8')}\n`]; +} diff --git a/@commitlint/read/src/get-edit-file-path.ts b/@commitlint/read/src/get-edit-file-path.ts new file mode 100644 index 0000000000..3b8ecf37aa --- /dev/null +++ b/@commitlint/read/src/get-edit-file-path.ts @@ -0,0 +1,27 @@ +import path from 'path'; +import {Stats} from 'fs'; + +const sander = require('@marionebl/sander'); + +// Get path to recently edited commit message file +export async function getEditFilePath( + top: string, + edit?: boolean | string +): Promise { + if (typeof edit === 'string') { + return path.resolve(top, edit); + } + + const dotgitPath = path.join(top, '.git'); + const dotgitStats: Stats = sander.lstatSync(dotgitPath); + + if (dotgitStats.isDirectory()) { + return path.join(top, '.git/COMMIT_EDITMSG'); + } + + const gitFile: string = await sander.readFile(dotgitPath, { + encoding: 'utf-8' + }); + const relativeGitPath = gitFile.replace('gitdir: ', '').replace('\n', ''); + return path.resolve(top, relativeGitPath, 'COMMIT_EDITMSG'); +} diff --git a/@commitlint/read/src/get-history-commits.ts b/@commitlint/read/src/get-history-commits.ts new file mode 100644 index 0000000000..caf2fae197 --- /dev/null +++ b/@commitlint/read/src/get-history-commits.ts @@ -0,0 +1,10 @@ +import gitRawCommits from 'git-raw-commits'; +import {streamToPromise} from './stream-to-promise'; + +// Get commit messages from history +export async function getHistoryCommits( + options: {from?: string; to?: string}, + opts: {cwd?: string} = {} +): Promise { + return streamToPromise(gitRawCommits(options, {cwd: opts.cwd})); +} diff --git a/@commitlint/read/src/index.js b/@commitlint/read/src/index.js deleted file mode 100644 index 4d09b3fd36..0000000000 --- a/@commitlint/read/src/index.js +++ /dev/null @@ -1,67 +0,0 @@ -import path from 'path'; -import gitRawCommits from 'git-raw-commits'; -import * as sander from '@marionebl/sander'; - -import toplevel from '@commitlint/top-level'; - -// Get commit messages -// Object => Promise> -export default async function getCommitMessages(settings) { - const {cwd, from, to, edit} = settings; - - if (edit) { - return getEditCommit(cwd, edit); - } - - return getHistoryCommits({from, to}, {cwd}); -} - -// Get commit messages from history -// Object => Promise -function getHistoryCommits(options, opts = {}) { - return new Promise((resolve, reject) => { - const data = []; - gitRawCommits(options, {cwd: opts.cwd}) - .on('data', chunk => data.push(chunk.toString('utf-8'))) - .on('error', reject) - .on('end', () => { - resolve(data); - }); - }); -} - -// Get recently edited commit message -// (cwd: string, edit: any) => Promise> -async function getEditCommit(cwd, edit) { - const top = await toplevel(cwd); - - if (typeof top !== 'string') { - throw new TypeError(`Could not find git root from ${cwd}`); - } - - const editFilePath = await getEditFilePath(top, edit); - - const editFile = await sander.readFile(editFilePath); - return [`${editFile.toString('utf-8')}\n`]; -} - -// Get path to recently edited commit message file -// (top: string, edit: any) => Promise -async function getEditFilePath(top, edit) { - let editFilePath; - if (typeof edit === 'string') { - editFilePath = path.resolve(top, edit); - } else { - const dotgitPath = path.join(top, '.git'); - const dotgitStats = sander.lstatSync(dotgitPath); - if (dotgitStats.isDirectory()) { - editFilePath = path.join(top, '.git/COMMIT_EDITMSG'); - } else { - const gitFile = await sander.readFile(dotgitPath, {encoding: 'utf-8'}); - const relativeGitPath = gitFile.replace('gitdir: ', '').replace('\n', ''); - editFilePath = path.resolve(top, relativeGitPath, 'COMMIT_EDITMSG'); - } - } - - return editFilePath; -} diff --git a/@commitlint/read/src/index.test.js b/@commitlint/read/src/read.test.ts similarity index 85% rename from @commitlint/read/src/index.test.js rename to @commitlint/read/src/read.test.ts index 87a4fbb133..98d445a446 100644 --- a/@commitlint/read/src/index.test.js +++ b/@commitlint/read/src/read.test.ts @@ -1,11 +1,12 @@ import {git} from '@commitlint/test'; import execa from 'execa'; -import * as sander from '@marionebl/sander'; -import read from '.'; +const sander = require('@marionebl/sander'); + +import read from './read'; test('get edit commit message specified by the `edit` flag', async () => { - const cwd = await git.bootstrap(); + const cwd: string = await git.bootstrap(); await sander.writeFile(cwd, 'commit-msg-file', 'foo'); @@ -15,7 +16,7 @@ test('get edit commit message specified by the `edit` flag', async () => { }); test('get edit commit message from git root', async () => { - const cwd = await git.bootstrap(); + const cwd: string = await git.bootstrap(); await sander.writeFile(cwd, 'alpha.txt', 'alpha'); await execa('git', ['add', '.'], {cwd}); @@ -26,7 +27,7 @@ test('get edit commit message from git root', async () => { }); test('get history commit messages', async () => { - const cwd = await git.bootstrap(); + const cwd: string = await git.bootstrap(); await sander.writeFile(cwd, 'alpha.txt', 'alpha'); await execa('git', ['add', 'alpha.txt'], {cwd}); await execa('git', ['commit', '-m', 'alpha'], {cwd}); @@ -39,7 +40,7 @@ test('get history commit messages', async () => { }); test('get edit commit message from git subdirectory', async () => { - const cwd = await git.bootstrap(); + const cwd: string = await git.bootstrap(); await sander.mkdir(cwd, 'beta'); await sander.writeFile(cwd, 'beta/beta.txt', 'beta'); diff --git a/@commitlint/read/src/read.ts b/@commitlint/read/src/read.ts new file mode 100644 index 0000000000..6074d9d9b4 --- /dev/null +++ b/@commitlint/read/src/read.ts @@ -0,0 +1,22 @@ +import {getHistoryCommits} from './get-history-commits'; +import {getEditCommit} from './get-edit-commit'; + +interface GetCommitMessageOptions { + cwd?: string; + from?: string; + to?: string; + edit?: boolean | string; +} + +// Get commit messages +export default async function getCommitMessages( + settings: GetCommitMessageOptions +): Promise { + const {cwd, from, to, edit} = settings; + + if (edit) { + return getEditCommit(cwd, edit); + } + + return getHistoryCommits({from, to}, {cwd}); +} diff --git a/@commitlint/read/src/stream-to-promise.ts b/@commitlint/read/src/stream-to-promise.ts new file mode 100644 index 0000000000..d5c60b6a66 --- /dev/null +++ b/@commitlint/read/src/stream-to-promise.ts @@ -0,0 +1,11 @@ +import {Readable} from 'stream'; + +export function streamToPromise(stream: Readable): Promise { + const data: string[] = []; + return new Promise((resolve, reject) => + stream + .on('data', chunk => data.push(chunk.toString('utf-8'))) + .on('error', reject) + .on('end', () => resolve(data)) + ); +} diff --git a/@commitlint/read/tsconfig.json b/@commitlint/read/tsconfig.json new file mode 100644 index 0000000000..5eb707a721 --- /dev/null +++ b/@commitlint/read/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../tsconfig.shared.json", + "compilerOptions": { + "composite": true, + "rootDir": "./src", + "outDir": "./lib" + }, + "include": [ + "./src" + ], + "exclude": [ + "./src/**/*.test.ts", + "./lib/**/*" + ], + "references": [ + { "path": "../top-level" } + ] +} diff --git a/@commitlint/top-level/src/index.ts b/@commitlint/top-level/src/index.ts index 1ea308ea39..4fb04043e4 100644 --- a/@commitlint/top-level/src/index.ts +++ b/@commitlint/top-level/src/index.ts @@ -13,7 +13,7 @@ export default toplevel; /** * Find the next git root */ -async function toplevel(cwd: string) { +async function toplevel(cwd?: string) { const found = await searchDotGit(cwd); if (typeof found !== 'string') { @@ -26,7 +26,7 @@ async function toplevel(cwd: string) { /** * Search .git, the '.git' can be a file(submodule), also can be a directory(normal) */ -async function searchDotGit(cwd: string) { +async function searchDotGit(cwd?: string) { const foundFile = await up('.git', {cwd, type: 'file'}); const foundDir = await up('.git', {cwd, type: 'directory'}); diff --git a/tsconfig.json b/tsconfig.json index 5a4d1574a4..0065cef64f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,7 +13,8 @@ { "path": "@commitlint/parse" }, { "path": "@commitlint/resolve-extends" }, { "path": "@commitlint/to-lines" }, - { "path": "@commitlint/top-level" }, + { "path": "@commitlint/top-level" }, + { "path": "@commitlint/read" }, { "path": "@commitlint/rules" } ] } diff --git a/yarn.lock b/yarn.lock index 2e321b3d26..8d8b3e804d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -691,17 +691,6 @@ js-levenshtein "^1.1.3" semver "^5.5.0" -"@babel/register@7.7.7", "@babel/register@^7.7.7": - version "7.7.7" - resolved "https://registry.npmjs.org/@babel/register/-/register-7.7.7.tgz#46910c4d1926b9c6096421b23d1f9e159c1dcee1" - integrity sha512-S2mv9a5dc2pcpg/ConlKZx/6wXaEwHeqfo7x/QbXsdCAZm+WJC1ekVvL1TVxNsedTs5y/gG63MhJTEsmwmjtiA== - dependencies: - find-cache-dir "^2.0.0" - lodash "^4.17.13" - make-dir "^2.1.0" - pirates "^4.0.0" - source-map-support "^0.5.16" - "@babel/runtime@^7.0.0": version "7.7.7" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.7.tgz#194769ca8d6d7790ec23605af9ee3e42a0aa79cf" @@ -1837,6 +1826,13 @@ dependencies: "@types/node" "*" +"@types/git-raw-commits@^2.0.0": + version "2.0.0" + resolved "https://packages.atlassian.com/api/npm/npm-remote/@types/git-raw-commits/-/git-raw-commits-2.0.0.tgz#157e9e4709db0748fb1aa623f8927ddd4864bac6" + integrity sha1-FX6eRwnbB0j7GqYj+JJ93UhkusY= + dependencies: + "@types/node" "*" + "@types/glob@^7.1.1": version "7.1.1" resolved "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575" @@ -3163,11 +3159,6 @@ common-path-prefix@^1.0.0: resolved "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-1.0.0.tgz#cd52f6f0712e0baab97d6f9732874f22f47752c0" integrity sha1-zVL28HEuC6q5fW+XModPIvR3UsA= -commondir@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" - integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= - compare-func@^1.3.1: version "1.3.2" resolved "https://registry.npmjs.org/compare-func/-/compare-func-1.3.2.tgz#99dd0ba457e1f9bc722b12c08ec33eeab31fa648" @@ -4415,15 +4406,6 @@ finalhandler@1.1.2: statuses "~1.5.0" unpipe "~1.0.0" -find-cache-dir@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" - integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== - dependencies: - commondir "^1.0.1" - make-dir "^2.0.0" - pkg-dir "^3.0.0" - find-node-modules@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/find-node-modules/-/find-node-modules-2.0.0.tgz#5db1fb9e668a3d451db3d618cd167cdd59e41b69" @@ -6780,7 +6762,7 @@ make-dir@^1.0.0: dependencies: pify "^3.0.0" -make-dir@^2.0.0, make-dir@^2.1.0: +make-dir@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== @@ -7998,7 +7980,7 @@ pinkie@^2.0.0: resolved "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= -pirates@^4.0.0, pirates@^4.0.1: +pirates@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== @@ -9108,7 +9090,7 @@ source-map-resolve@^0.5.0: source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@^0.5.13, source-map-support@^0.5.16, source-map-support@^0.5.6: +source-map-support@^0.5.13, source-map-support@^0.5.6: version "0.5.16" resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==