@jcoreio/toolchain 4.10.0 → 4.11.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/githooks/runHook.cjs +3 -1
- package/githooks.cjs +38 -0
- package/package.json +1 -1
- package/util/ParseError.cjs +10 -0
- package/util/ParseState.cjs +69 -0
- package/util/parseCommitMessage.cjs +45 -0
package/githooks/runHook.cjs
CHANGED
|
@@ -20,7 +20,7 @@ module.exports = async function runHook(hookName) {
|
|
|
20
20
|
if (!hook) return
|
|
21
21
|
|
|
22
22
|
if (typeof hook === 'function') {
|
|
23
|
-
await hook()
|
|
23
|
+
await hook(...process.argv.slice(2))
|
|
24
24
|
} else if (hook) {
|
|
25
25
|
await execa(hook, {
|
|
26
26
|
cwd: projDir,
|
|
@@ -28,6 +28,8 @@ module.exports = async function runHook(hookName) {
|
|
|
28
28
|
})
|
|
29
29
|
}
|
|
30
30
|
} catch (error) {
|
|
31
|
+
// eslint-disable-next-line no-console
|
|
32
|
+
console.error(error)
|
|
31
33
|
const { exitCode } = error
|
|
32
34
|
process.exit(exitCode != null ? exitCode : 1)
|
|
33
35
|
return
|
package/githooks.cjs
CHANGED
|
@@ -1,3 +1,41 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
'pre-commit': 'lint-staged',
|
|
3
|
+
'commit-msg': async (file) => {
|
|
4
|
+
const fs = require('./util/projectFs.cjs')
|
|
5
|
+
const parseCommitMessage = require('./util/parseCommitMessage.cjs')
|
|
6
|
+
const chalk = require('chalk')
|
|
7
|
+
const { monorepoSubpackageJsons } = require('./util/findUps.cjs')
|
|
8
|
+
|
|
9
|
+
let content
|
|
10
|
+
try {
|
|
11
|
+
content = await fs.readFile(file, 'utf8')
|
|
12
|
+
} catch (error) {
|
|
13
|
+
// eslint-disable-next-line no-console
|
|
14
|
+
console.error(
|
|
15
|
+
chalk`{red ✖} failed to read commit message from ${file}`,
|
|
16
|
+
error.message
|
|
17
|
+
)
|
|
18
|
+
process.exit(1)
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
const parsed = parseCommitMessage(content)
|
|
22
|
+
const scope = parsed.scope && parsed.scope[0]
|
|
23
|
+
if (
|
|
24
|
+
scope &&
|
|
25
|
+
monorepoSubpackageJsons &&
|
|
26
|
+
!monorepoSubpackageJsons.some((j) => scope === j.name)
|
|
27
|
+
) {
|
|
28
|
+
throw new Error(
|
|
29
|
+
`invalid scope: ${scope} - scope must be the name of a package in this monorepo`
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
// eslint-disable-next-line no-console
|
|
33
|
+
console.error(chalk`{green ✔} validated commit message`)
|
|
34
|
+
} catch (error) {
|
|
35
|
+
// eslint-disable-next-line no-console
|
|
36
|
+
console.error(chalk`{red ✖} invalid commit message: ${error.message}`)
|
|
37
|
+
process.exit(1)
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
},
|
|
3
41
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
class ParseState {
|
|
2
|
+
constructor(
|
|
3
|
+
input,
|
|
4
|
+
{
|
|
5
|
+
/**
|
|
6
|
+
* The starting index (defaults to 0)
|
|
7
|
+
*/
|
|
8
|
+
start = 0,
|
|
9
|
+
/**
|
|
10
|
+
* The ending index (defaults to input.length)
|
|
11
|
+
*/
|
|
12
|
+
end = input.length,
|
|
13
|
+
/**
|
|
14
|
+
* Flags to add to all regexes
|
|
15
|
+
*/
|
|
16
|
+
flags = 'g',
|
|
17
|
+
} = {}
|
|
18
|
+
) {
|
|
19
|
+
this.input = input
|
|
20
|
+
this.start = start
|
|
21
|
+
this.index = start
|
|
22
|
+
this.end = end
|
|
23
|
+
this.flags = flags
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get done() {
|
|
27
|
+
return this.index >= this.end
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* If pattern matches the input at the current index, returns the match,
|
|
32
|
+
* but doesn't advance the index.
|
|
33
|
+
*/
|
|
34
|
+
peek(pattern) {
|
|
35
|
+
if (typeof pattern === 'string') pattern = toRegExp(pattern)
|
|
36
|
+
pattern = new RegExp(
|
|
37
|
+
pattern.source,
|
|
38
|
+
`${this.flags}${pattern.flags.replace(
|
|
39
|
+
new RegExp(`[${this.flags}]`, 'g'),
|
|
40
|
+
''
|
|
41
|
+
)}`
|
|
42
|
+
)
|
|
43
|
+
pattern.lastIndex = this.index
|
|
44
|
+
const match = pattern.exec(this.input)
|
|
45
|
+
return match &&
|
|
46
|
+
match.index === this.index &&
|
|
47
|
+
match.index + match[0].length <= this.end
|
|
48
|
+
? match
|
|
49
|
+
: undefined
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* If pattern matches the input at the current index, returns the match,
|
|
54
|
+
* and advances the index to the end of the match.
|
|
55
|
+
*/
|
|
56
|
+
match(pattern) {
|
|
57
|
+
const match = this.peek(pattern)
|
|
58
|
+
if (match) {
|
|
59
|
+
this.index += match[0].length
|
|
60
|
+
}
|
|
61
|
+
return match
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
module.exports = ParseState
|
|
66
|
+
|
|
67
|
+
function toRegExp(s, flags = '') {
|
|
68
|
+
return new RegExp(s.replace(/[/\-\\^$*+?.()|[\]{}]/g, '\\$&'), flags)
|
|
69
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const ParseState = require('./ParseState.cjs')
|
|
2
|
+
const ParseError = require('./ParseError.cjs')
|
|
3
|
+
|
|
4
|
+
function parseCommitMessage(message) {
|
|
5
|
+
const state = new ParseState(message)
|
|
6
|
+
const type = state.match(/[^(:! )]+/)
|
|
7
|
+
if (!type || !/^[-a-z]+$/.test(type)) {
|
|
8
|
+
throw new ParseError(
|
|
9
|
+
'must begin with a type containing lowercase letters or - like `fix` or `feat`',
|
|
10
|
+
0
|
|
11
|
+
)
|
|
12
|
+
}
|
|
13
|
+
let scope
|
|
14
|
+
if (state.match('(')) {
|
|
15
|
+
scope = state.match(/[^)]+/)
|
|
16
|
+
if (!scope) {
|
|
17
|
+
throw new ParseError('a scope is required after (', state.index)
|
|
18
|
+
}
|
|
19
|
+
if (!state.match(')')) {
|
|
20
|
+
throw new ParseError('missing ) after scope', state.index)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const bang = state.match(/!/)
|
|
24
|
+
if (!state.match(':')) {
|
|
25
|
+
throw new ParseError('missing : after type/scope', state.index)
|
|
26
|
+
}
|
|
27
|
+
if (!state.match(/\s+/)) {
|
|
28
|
+
throw new ParseError('missing space after :', state.index)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const description = state.match(/.+$/m)
|
|
32
|
+
if (!description || !/\S/.test(description)) {
|
|
33
|
+
throw new ParseError('missing description', state.index)
|
|
34
|
+
}
|
|
35
|
+
if (!state.match(/\n\s*(\n|$)/) && !state.done) {
|
|
36
|
+
throw new ParseError(
|
|
37
|
+
'there must be a blank line before longer commit body',
|
|
38
|
+
state.index
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
const body = state.match(/(.|\n)+/)
|
|
42
|
+
return { type, scope, bang, description, body }
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
module.exports = parseCommitMessage
|