@vv0rkz/js-template 1.7.0 → 1.8.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.
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, mkdirSync, writeFileSync } from 'fs'
3
+ import { join } from 'path'
4
+ import { loadConfig } from './config.js'
5
+
6
+ const config = await loadConfig()
7
+ const { depUpdater } = config
8
+
9
+ if (!depUpdater) {
10
+ console.log('⏭️ Автообновление зависимостей отключено (depUpdater = false)')
11
+ process.exit(0)
12
+ }
13
+
14
+ if (depUpdater === 'dependabot') {
15
+ const dir = join(process.cwd(), '.github')
16
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true })
17
+
18
+ const filePath = join(dir, 'dependabot.yml')
19
+ if (existsSync(filePath)) {
20
+ console.log('⏭️ .github/dependabot.yml уже существует')
21
+ process.exit(0)
22
+ }
23
+
24
+ writeFileSync(
25
+ filePath,
26
+ `version: 2
27
+ updates:
28
+ - package-ecosystem: "npm"
29
+ directory: "/"
30
+ schedule:
31
+ interval: "weekly"
32
+ open-pull-requests-limit: 10
33
+ `,
34
+ )
35
+ console.log('✅ Создан .github/dependabot.yml')
36
+ } else if (depUpdater === 'renovate') {
37
+ const filePath = join(process.cwd(), 'renovate.json')
38
+ if (existsSync(filePath)) {
39
+ console.log('⏭️ renovate.json уже существует')
40
+ process.exit(0)
41
+ }
42
+
43
+ writeFileSync(
44
+ filePath,
45
+ JSON.stringify(
46
+ {
47
+ $schema: 'https://docs.renovatebot.com/renovate-schema.json',
48
+ extends: ['config:base'],
49
+ },
50
+ null,
51
+ 2,
52
+ ) + '\n',
53
+ )
54
+ console.log('✅ Создан renovate.json')
55
+ } else {
56
+ console.log(`⚠️ Неизвестный depUpdater: "${depUpdater}"`)
57
+ console.log('💡 Допустимые значения: "dependabot", "renovate", false')
58
+ process.exit(1)
59
+ }
@@ -1,58 +1,53 @@
1
- #!/usr/bin/env node
2
- import { execSync } from 'child_process'
3
-
4
- console.log('🏷️ Настройка GitHub labels...\n')
5
-
6
- const labels = [
7
- { name: 'task', color: '0E8A16', description: 'Новая фича' },
8
- { name: 'bug', color: 'D73A4A', description: 'Баг' },
9
- { name: 'refactor', color: 'FEF2C0', description: 'Рефакторинг/техдолг' },
10
- { name: 'perf', color: '007bff', description: 'Оптимизация производительности' },
11
- ]
12
-
13
- try {
14
- // Проверяем что gh установлен и авторизован
15
- execSync('gh auth status', { stdio: 'ignore' })
16
- } catch (error) {
17
- console.error('❌ GitHub CLI не установлен или не авторизован')
18
- console.log('💡 Установи: https://cli.github.com/')
19
- console.log(' И выполни: gh auth login')
20
- process.exit(1)
21
- }
22
-
23
- // Получаем существующие labels
24
- let existingLabels = []
25
- try {
26
- const output = execSync('gh label list --json name', { encoding: 'utf8' })
27
- existingLabels = JSON.parse(output).map((l) => l.name)
28
- } catch (error) {
29
- console.log('⚠️ Не удалось получить список labels (возможно репозиторий пустой)')
30
- }
31
-
32
- // Создаём недостающие labels
33
- let created = 0
34
- let skipped = 0
35
-
36
- for (const label of labels) {
37
- if (existingLabels.includes(label.name)) {
38
- console.log(` ⏭️ ${label.name} (уже существует)`)
39
- skipped++
40
- continue
41
- }
42
-
43
- try {
44
- execSync(`gh label create "${label.name}" --color "${label.color}" --description "${label.description}"`, {
45
- stdio: 'ignore',
46
- })
47
- console.log(` ✅ ${label.name}`)
48
- created++
49
- } catch (error) {
50
- console.log(` ⚠️ ${label.name} (ошибка создания)`)
51
- }
52
- }
53
-
54
- console.log(`\n📊 Итого: создано ${created}, пропущено ${skipped}`)
55
-
56
- if (created > 0) {
57
- console.log('✅ Labels настроены!')
58
- }
1
+ #!/usr/bin/env node
2
+ import { execSync } from 'child_process'
3
+ import { loadConfig } from './config.js'
4
+
5
+ const config = await loadConfig()
6
+
7
+ console.log('🏷️ Настройка GitHub labels...\n')
8
+
9
+ const labels = config.labels
10
+
11
+ try {
12
+ execSync('gh auth status', { stdio: 'ignore' })
13
+ } catch (error) {
14
+ console.error('❌ GitHub CLI не установлен или не авторизован')
15
+ console.log('💡 Установи: https://cli.github.com/')
16
+ console.log(' И выполни: gh auth login')
17
+ process.exit(1)
18
+ }
19
+
20
+ let existingLabels = []
21
+ try {
22
+ const output = execSync('gh label list --json name', { encoding: 'utf8' })
23
+ existingLabels = JSON.parse(output).map((l) => l.name)
24
+ } catch (error) {
25
+ console.log('⚠️ Не удалось получить список labels (возможно репозиторий пустой)')
26
+ }
27
+
28
+ let created = 0
29
+ let skipped = 0
30
+
31
+ for (const label of labels) {
32
+ if (existingLabels.includes(label.name)) {
33
+ console.log(` ⏭️ ${label.name} (уже существует)`)
34
+ skipped++
35
+ continue
36
+ }
37
+
38
+ try {
39
+ execSync(`gh label create "${label.name}" --color "${label.color}" --description "${label.description}"`, {
40
+ stdio: 'ignore',
41
+ })
42
+ console.log(` ✅ ${label.name}`)
43
+ created++
44
+ } catch (error) {
45
+ console.log(` ⚠️ ${label.name} (ошибка создания)`)
46
+ }
47
+ }
48
+
49
+ console.log(`\n📊 Итого: создано ${created}, пропущено ${skipped}`)
50
+
51
+ if (created > 0) {
52
+ console.log('✅ Labels настроены!')
53
+ }
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env node
2
+ import { copyFileSync, existsSync, mkdirSync, readdirSync, statSync } from 'fs'
3
+ import { spawnSync } from 'child_process'
4
+ import { dirname, join } from 'path'
5
+ import { fileURLToPath } from 'url'
6
+
7
+ const __dirname = dirname(fileURLToPath(import.meta.url))
8
+ const templateDir = join(__dirname, '../templates')
9
+ const targetDir = process.cwd()
10
+
11
+ console.log('⬆️ JST Upgrade — обновление конфигов и хуков\n')
12
+
13
+ // 1. jst.config.js — только если нет (не перезаписываем пользовательские настройки)
14
+ const configSrc = join(templateDir, 'jst.config.js')
15
+ const configDest = join(targetDir, 'jst.config.js')
16
+
17
+ if (!existsSync(configDest)) {
18
+ copyFileSync(configSrc, configDest)
19
+ console.log('✅ jst.config.js создан (настрой под свой проект)')
20
+ } else {
21
+ console.log('⏭️ jst.config.js уже существует (не перезаписан)')
22
+ }
23
+
24
+ // 2. Husky hooks — всегда обновляем (это тонкие обёртки, кастомизация в jst.config.js)
25
+ console.log('\n🐶 Обновление husky хуков...')
26
+ const huskyDir = join(targetDir, '.husky')
27
+ const huskyTemplateDir = join(templateDir, '.husky')
28
+
29
+ if (!existsSync(huskyDir)) mkdirSync(huskyDir, { recursive: true })
30
+
31
+ if (existsSync(huskyTemplateDir)) {
32
+ const copyDir = (src, dest) => {
33
+ if (!existsSync(dest)) mkdirSync(dest, { recursive: true })
34
+ for (const entry of readdirSync(src)) {
35
+ const srcPath = join(src, entry)
36
+ const destPath = join(dest, entry)
37
+ if (statSync(srcPath).isDirectory()) {
38
+ copyDir(srcPath, destPath)
39
+ } else {
40
+ copyFileSync(srcPath, destPath)
41
+ }
42
+ }
43
+ }
44
+ copyDir(huskyTemplateDir, huskyDir)
45
+ console.log('✅ Хуки обновлены')
46
+ }
47
+
48
+ // 3. commitlint + changelog — обновляем (кастомизация теперь через jst.config.js)
49
+ const configs = ['commitlint.config.js', 'changelog.config.js']
50
+
51
+ for (const name of configs) {
52
+ const src = join(templateDir, name)
53
+ const dest = join(targetDir, name)
54
+ if (existsSync(src)) {
55
+ copyFileSync(src, dest)
56
+ console.log(`✅ ${name} обновлён`)
57
+ }
58
+ }
59
+
60
+ // 4. Применяем конфиг (labels + deps)
61
+ console.log('\n⚙️ Применение конфига...')
62
+
63
+ try {
64
+ spawnSync('node', [join(__dirname, 'setup-labels.js')], { stdio: 'inherit' })
65
+ } catch {
66
+ console.log('⏭️ Labels пропущены (нет gh)')
67
+ }
68
+
69
+ spawnSync('node', [join(__dirname, 'setup-deps.js')], { stdio: 'inherit' })
70
+
71
+ console.log('\n✅ Upgrade завершён!')
72
+ console.log('💡 Проверь jst.config.js и настрой под свой проект')
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env node
2
+ import { execSync } from 'child_process'
3
+ import { loadConfig, compileBranchRegex } from './config.js'
4
+
5
+ const config = await loadConfig()
6
+ const { main: mainBranch, patterns } = config.branch
7
+
8
+ const currentBranch = execSync('git branch --show-current', { encoding: 'utf8' }).trim()
9
+
10
+ if (currentBranch === mainBranch) {
11
+ console.log(`✅ Разрешённая ветка: ${currentBranch}`)
12
+ process.exit(0)
13
+ }
14
+
15
+ const branchRegex = compileBranchRegex(config.branch)
16
+
17
+ if (!branchRegex.test(currentBranch)) {
18
+ console.log(`❌ Неправильный формат ветки: ${currentBranch}`)
19
+ console.log('')
20
+
21
+ if (patterns && patterns.length) {
22
+ console.log('✅ Допустимые форматы:')
23
+ for (const p of patterns) {
24
+ console.log(` ${p}`)
25
+ }
26
+ } else {
27
+ console.log(`✅ Паттерн: ${config.branch.pattern}`)
28
+ }
29
+
30
+ process.exit(1)
31
+ }
32
+
33
+ console.log(`✅ Формат ветки правильный: ${currentBranch}`)
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync } from 'fs'
3
+ import { spawnSync } from 'child_process'
4
+ import { platform } from 'os'
5
+ import { loadConfig } from './config.js'
6
+
7
+ const config = await loadConfig()
8
+ const commitMsgFile = process.argv[2]
9
+ const commitMsg = readFileSync(commitMsgFile, 'utf8').split('\n')[0].trim()
10
+
11
+ const { types, requireIssue, closeKeyword } = config.commits
12
+
13
+ if (/^chore\(release\): v\d+\.\d+\.\d+$/.test(commitMsg)) {
14
+ console.log(`✅ Авто-релизный коммит: ${commitMsg}`)
15
+ process.exit(0)
16
+ }
17
+
18
+ const escClose = closeKeyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
19
+
20
+ const issuePatterns = requireIssue
21
+ .map((t) => `${t}(\\(.+\\))?: (${escClose} )?#[0-9]+ .+`)
22
+ .join('|')
23
+
24
+ const freePatterns = types
25
+ .filter((t) => !requireIssue.includes(t))
26
+ .map((t) => `${t}(\\(.+\\))?: .+`)
27
+ .join('|')
28
+
29
+ const fullPattern = [issuePatterns, freePatterns].filter(Boolean).join('|')
30
+ const regex = new RegExp(`^(${fullPattern})$`)
31
+
32
+ if (!regex.test(commitMsg)) {
33
+ console.log(`❌ Неправильный формат коммита: '${commitMsg}'`)
34
+ showHelp()
35
+ process.exit(1)
36
+ }
37
+
38
+ for (const type of requireIssue) {
39
+ const typeMatch = new RegExp(`^${type}(\\(.+\\))?:`)
40
+ if (typeMatch.test(commitMsg)) {
41
+ const issueMatch = new RegExp(`^${type}(\\(.+\\))?: (${escClose} )?#[0-9]`)
42
+ if (!issueMatch.test(commitMsg)) {
43
+ console.log(`❌ ${type} коммиты должны содержать номер задачи`)
44
+ console.log(`💡 Твой коммит: ${commitMsg}`)
45
+ console.log(`✅ Пример: ${type}: #9 описание`)
46
+ console.log(`✅ Закрыть: ${type}: ${closeKeyword} #9 описание`)
47
+ process.exit(1)
48
+ }
49
+ }
50
+ }
51
+
52
+ const isWin = platform() === 'win32'
53
+ const npx = isWin ? 'npx.cmd' : 'npx'
54
+ const result = spawnSync(npx, ['--no', '--', 'commitlint', '--edit', commitMsgFile], {
55
+ stdio: 'inherit',
56
+ })
57
+
58
+ process.exit(result.status || 0)
59
+
60
+ function showHelp() {
61
+ console.log('')
62
+ console.log('🎯 РАЗРЕШЕННЫЕ ФОРМАТЫ КОММИТОВ:')
63
+ console.log('────────────────────────────────────')
64
+
65
+ for (const type of types) {
66
+ if (requireIssue.includes(type)) {
67
+ console.log(` ${type}: #номер описание`)
68
+ console.log(` ${type}(scope): #номер описание`)
69
+ console.log(` ${type}: ${closeKeyword} #номер описание (закрыть issue)`)
70
+ } else {
71
+ console.log(` ${type}: описание`)
72
+ console.log(` ${type}(scope): описание`)
73
+ }
74
+ }
75
+
76
+ console.log(' chore(release): vX.X.X')
77
+ console.log('')
78
+ console.log('📝 ПРИМЕРЫ:')
79
+ console.log(' feat: #9 добавить нормализацию')
80
+ console.log(' feat(Date): #9 добавить форматирование дат')
81
+ console.log(` feat: ${closeKeyword} #9 добавить нормализацию (закрыть issue)`)
82
+ console.log(` feat(parser): ${closeKeyword} #9 финализировать (scope + закрыть)`)
83
+ console.log(' fix: #10 исправить валидацию')
84
+ console.log(' refactor(utils): оптимизировать хелперы')
85
+ console.log('────────────────────────────────────')
86
+ }