@jcoreio/toolchain 4.9.1 → 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/scripts/create.cjs +1 -1
- package/scripts/init.cjs +1 -1
- package/scripts/install-optional-deps.cjs +66 -0
- package/scripts/toolchain.cjs +1 -0
- package/util/ParseError.cjs +10 -0
- package/util/ParseState.cjs +69 -0
- package/util/confirm.cjs +1 -1
- package/util/hasJSSources.cjs +6 -0
- package/util/parseCommitMessage.cjs +45 -0
- package/util/prompt.cjs +15 -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
package/scripts/create.cjs
CHANGED
|
@@ -7,7 +7,7 @@ const parseRepositoryUrl = require('../util/parseRepositoryUrl.cjs')
|
|
|
7
7
|
const markdownBadges = require('../util/markdownBadges.cjs')
|
|
8
8
|
|
|
9
9
|
async function create(args = []) {
|
|
10
|
-
const prompt = require('
|
|
10
|
+
const prompt = require('../util/prompt.cjs')
|
|
11
11
|
|
|
12
12
|
let monorepoPackageJson, monorepoProjectDir
|
|
13
13
|
try {
|
package/scripts/init.cjs
CHANGED
|
@@ -35,7 +35,7 @@ async function init(args = []) {
|
|
|
35
35
|
|
|
36
36
|
let selectedToolchains = [...toolchains]
|
|
37
37
|
if (isInteractive) {
|
|
38
|
-
;({ selectedToolchains } = await require('
|
|
38
|
+
;({ selectedToolchains } = await require('../uitl/prompt')({
|
|
39
39
|
name: 'selectedToolchains',
|
|
40
40
|
type: 'multiselect',
|
|
41
41
|
message: 'Select toolchains to install',
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
const execa = require('../util/execa.cjs')
|
|
2
|
+
const {
|
|
3
|
+
packageJson,
|
|
4
|
+
isMonorepoRoot,
|
|
5
|
+
toolchainManaged,
|
|
6
|
+
} = require('../util/findUps.cjs')
|
|
7
|
+
|
|
8
|
+
async function installOptionalDeps() {
|
|
9
|
+
const choices = new Set()
|
|
10
|
+
const { dependencies = {}, devDependencies = {} } = packageJson
|
|
11
|
+
for (const dep of Object.keys({
|
|
12
|
+
...toolchainManaged.optionalDependencies,
|
|
13
|
+
...toolchainManaged.optionalDevDependencies,
|
|
14
|
+
})) {
|
|
15
|
+
if (!dependencies[dep] && !devDependencies[dep]) {
|
|
16
|
+
choices.add(dep)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
if (!choices.size) {
|
|
20
|
+
// eslint-disable-next-line no-console
|
|
21
|
+
console.error('There are no additional optional dependencies to install')
|
|
22
|
+
process.exit(0)
|
|
23
|
+
}
|
|
24
|
+
const finalChoices = [...choices].sort()
|
|
25
|
+
const { selected } = await require('../util/prompt.cjs')({
|
|
26
|
+
name: 'selected',
|
|
27
|
+
type: 'multiselect',
|
|
28
|
+
message: 'Select optional dependencies to install',
|
|
29
|
+
choices: finalChoices,
|
|
30
|
+
})
|
|
31
|
+
const selectedSet = new Set(selected.map((i) => finalChoices[i]))
|
|
32
|
+
|
|
33
|
+
const selectedDeps = Object.keys(
|
|
34
|
+
toolchainManaged.optionalDependencies || {}
|
|
35
|
+
).filter((dep) => selectedSet.has(dep))
|
|
36
|
+
const selectedDevDeps = Object.keys(
|
|
37
|
+
toolchainManaged.optionalDevDependencies || {}
|
|
38
|
+
).filter((dep) => selectedSet.has(dep))
|
|
39
|
+
|
|
40
|
+
if (selectedDeps.length) {
|
|
41
|
+
await execa('pnpm', [
|
|
42
|
+
'add',
|
|
43
|
+
'--prefer-offline',
|
|
44
|
+
...(isMonorepoRoot ? ['-w'] : []),
|
|
45
|
+
...selectedDeps.map(
|
|
46
|
+
(dep) => `${dep}@${toolchainManaged.optionalDependencies[dep]}`
|
|
47
|
+
),
|
|
48
|
+
])
|
|
49
|
+
}
|
|
50
|
+
if (selectedDevDeps.length) {
|
|
51
|
+
await execa('pnpm', [
|
|
52
|
+
'add',
|
|
53
|
+
'-D',
|
|
54
|
+
'--prefer-offline',
|
|
55
|
+
...(isMonorepoRoot ? ['-w'] : []),
|
|
56
|
+
...selectedDevDeps.map(
|
|
57
|
+
(dep) => `${dep}@${toolchainManaged.optionalDevDependencies[dep]}`
|
|
58
|
+
),
|
|
59
|
+
])
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
exports.description =
|
|
64
|
+
'select and install optional toolchain-managed dependencies'
|
|
65
|
+
|
|
66
|
+
exports.run = installOptionalDeps
|
package/scripts/toolchain.cjs
CHANGED
|
@@ -37,6 +37,7 @@ const scripts = toolchainConfig
|
|
|
37
37
|
},
|
|
38
38
|
},
|
|
39
39
|
'install-git-hooks': require('./install-git-hooks.cjs'),
|
|
40
|
+
'install-optional-deps': require('./install-optional-deps.cjs'),
|
|
40
41
|
...(isMonorepoRoot ? { create: require('./create.cjs') } : {}),
|
|
41
42
|
...getPluginsObjectSync('scripts'),
|
|
42
43
|
...Object.fromEntries(
|
|
@@ -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
|
+
}
|
package/util/confirm.cjs
CHANGED
|
@@ -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
|
package/util/prompt.cjs
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const prompts = require('prompts')
|
|
2
|
+
|
|
3
|
+
module.exports = async function prompt(...args) {
|
|
4
|
+
const handleKeypress = function (_chunk, key) {
|
|
5
|
+
if (key && key.name === 'c' && key.ctrl) {
|
|
6
|
+
process.kill(process.pid, 'SIGINT')
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
try {
|
|
10
|
+
process.stdin.on('keypress', handleKeypress)
|
|
11
|
+
return await prompts(...args)
|
|
12
|
+
} finally {
|
|
13
|
+
process.stdin.off('keypress', handleKeypress)
|
|
14
|
+
}
|
|
15
|
+
}
|