@vv0rkz/js-template 1.7.0 → 1.8.2

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.
@@ -1,25 +1,3 @@
1
- #!/usr/bin/env sh
2
-
3
- COMMIT_MSG=$(git log -1 --pretty=%B)
4
- COMMIT_HASH=$(git rev-parse --short HEAD)
5
- REPO_URL=$(git remote get-url origin)
6
-
7
- # Исправляем URL
8
- if echo "$REPO_URL" | grep -q "git@github.com"; then
9
- REPO_URL=$(echo "$REPO_URL" | sed 's/git@github.com:/https:\/\/github.com\//' | sed 's/\.git$//')
10
- else
11
- REPO_URL=$(echo "$REPO_URL" | sed 's/\.git$//')
12
- fi
13
-
14
- # Пуш коммита
15
- git push origin HEAD
16
-
17
- # Закрытие issues при feat: #номер И fix: #номер
18
- if echo "$COMMIT_MSG" | grep -q "^\(feat\|fix\): #[0-9]"; then
19
- ISSUE_NUMBER=$(echo "$COMMIT_MSG" | grep -o '#[0-9]*' | sed 's/#//')
20
- echo "🔧 Закрываю issue #$ISSUE_NUMBER..."
21
-
22
- COMMIT_LINK="$REPO_URL/commit/$COMMIT_HASH"
23
-
24
- gh issue close "$ISSUE_NUMBER" --comment "✅ Завершено в коммите: [$COMMIT_HASH]($COMMIT_LINK) - $COMMIT_MSG"
25
- fi
1
+ #!/usr/bin/env sh
2
+
3
+ node ./node_modules/@vv0rkz/js-template/tools-gh/post-commit-hook.js
@@ -1,17 +1,3 @@
1
- #!/usr/bin/env sh
2
-
3
- CURRENT_BRANCH=$(git branch --show-current)
4
- if [ "$CURRENT_BRANCH" = "main" ]; then
5
- echo "✅ Разрешённая ветка: $CURRENT_BRANCH"
6
- exit 0
7
- fi
8
-
9
- # Простая проверка: начинается с v и есть дефис
10
- if ! echo "$CURRENT_BRANCH" | grep -q "^v.*-.*"; then
11
- echo "❌ Неправильный формат ветки: $CURRENT_BRANCH"
12
- echo "✅ Должен быть: vX.Y.Z-description"
13
- echo "📝 Пример: v2.3.0-normalize-operators"
14
- exit 1
15
- fi
16
-
17
- echo "✅ Формат ветки правильный: $CURRENT_BRANCH"
1
+ #!/usr/bin/env sh
2
+
3
+ node ./node_modules/@vv0rkz/js-template/tools-gh/validate-branch.js
@@ -1,9 +1,16 @@
1
- export default {
2
- types: {
3
- feat: { title: ' Новые фичи', semver: 'minor' },
4
- fix: { title: '🐛 Исправления', semver: 'patch' },
5
- refactor: { title: '♻️ Рефакторинг', semver: 'patch' },
6
- perf: { title: '⚡ Оптимизация', semver: 'patch' },
7
- },
8
- contributors: false,
9
- }
1
+ let changelogTypes = {
2
+ feat: { title: '✨ Новые фичи', semver: 'minor' },
3
+ fix: { title: '🐛 Исправления', semver: 'patch' },
4
+ refactor: { title: '♻️ Рефакторинг', semver: 'patch' },
5
+ perf: { title: ' Оптимизация', semver: 'patch' },
6
+ }
7
+
8
+ try {
9
+ const { default: config } = await import('./jst.config.js')
10
+ if (config.changelog?.types) changelogTypes = config.changelog.types
11
+ } catch {}
12
+
13
+ export default {
14
+ types: changelogTypes,
15
+ contributors: false,
16
+ }
@@ -1,6 +1,13 @@
1
- export default {
2
- extends: ["@commitlint/config-conventional"],
3
- rules: {
4
- "type-enum": [2, "always", ["feat", "fix", "refactor", "build", "chore", "docs"]],
5
- },
6
- }
1
+ let types = ['feat', 'fix', 'refactor', 'build', 'chore', 'docs', 'perf']
2
+
3
+ try {
4
+ const { default: config } = await import('./jst.config.js')
5
+ if (config.commits?.types) types = config.commits.types
6
+ } catch {}
7
+
8
+ export default {
9
+ extends: ['@commitlint/config-conventional'],
10
+ rules: {
11
+ 'type-enum': [2, 'always', types],
12
+ },
13
+ }
@@ -0,0 +1,120 @@
1
+ /**
2
+ * JST Configuration
3
+ * Документация: https://github.com/vv0rkz/js-template
4
+ *
5
+ * Все значения ниже — дефолтные. Измени нужные или закомментируй.
6
+ */
7
+ export default {
8
+ /**
9
+ * Правила именования веток
10
+ */
11
+ branch: {
12
+ /** Имя главной ветки ('main' | 'master') */
13
+ main: 'main',
14
+
15
+ /**
16
+ * Допустимые форматы веток (массив шаблонов)
17
+ *
18
+ * Плейсхолдеры:
19
+ * {version} → семвер (X.Y.Z)
20
+ * {issue} → номер issue (цифры)
21
+ * {name} → описание (буквы, цифры, дефисы, подчёркивания)
22
+ * * → что угодно
23
+ *
24
+ * Примеры наборов:
25
+ *
26
+ * ['v{version}-{name}']
27
+ * ✅ v2.3.0-normalize-operators
28
+ *
29
+ * ['release/v{version}', 'feature/#{issue}-{name}', 'fix/#{issue}-{name}']
30
+ * ✅ release/v2.3.0
31
+ * ✅ feature/#12-dark-theme
32
+ * ✅ fix/#7-validation-bug
33
+ *
34
+ * ['feature/*', 'fix/*', 'release/*']
35
+ * ✅ feature/anything-goes-here
36
+ * ✅ fix/urgent-patch
37
+ *
38
+ * ['*']
39
+ * ✅ любое имя (без ограничений)
40
+ */
41
+ patterns: ['v{version}-{name}'],
42
+ },
43
+
44
+ /**
45
+ * GitHub Labels
46
+ * Используются при `jst setup-labels` и `jst create-*`
47
+ *
48
+ * Цвета — 6-символьный hex без #
49
+ */
50
+ labels: [
51
+ { name: 'task', color: '0E8A16', description: 'Новая фича', emoji: '✨' },
52
+ { name: 'bug', color: 'D73A4A', description: 'Баг', emoji: '🐛' },
53
+ { name: 'refactor', color: 'FEF2C0', description: 'Рефакторинг/техдолг', emoji: '♻️' },
54
+ { name: 'perf', color: '007bff', description: 'Оптимизация производительности', emoji: '⚡' },
55
+ ],
56
+
57
+ /**
58
+ * Правила коммитов
59
+ */
60
+ commits: {
61
+ /**
62
+ * Разрешённые типы коммитов
63
+ * Должны совпадать с commitlint и changelog
64
+ */
65
+ types: ['feat', 'fix', 'refactor', 'build', 'chore', 'docs', 'perf'],
66
+
67
+ /**
68
+ * Типы, требующие номер issue (#N)
69
+ * Остальные типы — свободный формат
70
+ */
71
+ requireIssue: ['feat', 'fix'],
72
+
73
+ /**
74
+ * Ключевое слово для закрытия issue в коммите
75
+ *
76
+ * С ним: feat: close #9 описание → закроет issue #9
77
+ * Без: feat: #9 описание → просто ссылка, issue остаётся открытым
78
+ */
79
+ closeKeyword: 'close',
80
+ },
81
+
82
+ /**
83
+ * Настройки релиза (`jst release`)
84
+ */
85
+ release: {
86
+ /** Требовать GIF/PNG демо перед релизом (true | false) */
87
+ requireDemo: true,
88
+
89
+ /** Директория для демо-файлов */
90
+ demoDir: 'docs',
91
+
92
+ /** Допустимые форматы демо-файлов */
93
+ demoFormats: ['gif', 'png'],
94
+ },
95
+
96
+ /**
97
+ * Настройки changelog (changelogen)
98
+ * title — заголовок секции в CHANGELOG.md
99
+ * semver — тип версионирования ('major' | 'minor' | 'patch')
100
+ */
101
+ changelog: {
102
+ types: {
103
+ feat: { title: '✨ Новые фичи', semver: 'minor' },
104
+ fix: { title: '🐛 Исправления', semver: 'patch' },
105
+ refactor: { title: '♻️ Рефакторинг', semver: 'patch' },
106
+ perf: { title: '⚡ Оптимизация', semver: 'patch' },
107
+ },
108
+ },
109
+
110
+ /**
111
+ * Автообновление зависимостей
112
+ * @type {'dependabot' | 'renovate' | false}
113
+ *
114
+ * 'dependabot' — создаст .github/dependabot.yml
115
+ * 'renovate' — создаст renovate.json
116
+ * false — отключено
117
+ */
118
+ depUpdater: false,
119
+
120
+ }
@@ -1,30 +1,36 @@
1
- #!/usr/bin/env node
2
- import { readFileSync, existsSync } from "fs"
3
- import { execSync } from "child_process"
4
-
5
- // Читаем текущую версию
6
- const packageJson = JSON.parse(readFileSync("package.json", "utf8"))
7
- const [major, minor, patch] = packageJson.version.split(".").map(Number)
8
-
9
- // Анализируем коммиты чтобы определить тип версии
10
- const commitMessages = execSync("git log --oneline -10", { encoding: "utf8" })
11
-
12
- let nextVersion
13
- if (commitMessages.includes("feat:")) {
14
- nextVersion = `v${major}.${minor + 1}.0` // minor release
15
- } else {
16
- nextVersion = `v${major}.${minor}.${patch + 1}` // patch release
17
- }
18
-
19
- console.log(`📦 Предполагаемая следующая версия: ${nextVersion}`)
20
-
21
- // Проверяем демо
22
- const hasDemo = existsSync(`docs/${nextVersion}.gif`) || existsSync(`docs/${nextVersion}.png`)
23
-
24
- if (!hasDemo) {
25
- console.log(`❌ Релиз ${nextVersion} требует демо!`)
26
- console.log(`📸 Создай: docs/${nextVersion}.gif`)
27
- process.exit(1)
28
- }
29
-
30
- console.log(`✅ Демо для ${nextVersion} готово!`)
1
+ #!/usr/bin/env node
2
+ import { existsSync, readFileSync } from 'fs'
3
+ import { execSync } from 'child_process'
4
+ import { loadConfig } from './config.js'
5
+
6
+ const config = await loadConfig()
7
+
8
+ if (!config.release.requireDemo) {
9
+ console.log('⏭️ Проверка демо отключена')
10
+ process.exit(0)
11
+ }
12
+
13
+ const packageJson = JSON.parse(readFileSync('package.json', 'utf8'))
14
+ const [major, minor, patch] = packageJson.version.split('.').map(Number)
15
+
16
+ const commitMessages = execSync('git log --oneline -10', { encoding: 'utf8' })
17
+
18
+ let nextVersion
19
+ if (commitMessages.includes('feat:')) {
20
+ nextVersion = `v${major}.${minor + 1}.0`
21
+ } else {
22
+ nextVersion = `v${major}.${minor}.${patch + 1}`
23
+ }
24
+
25
+ console.log(`📦 Предполагаемая следующая версия: ${nextVersion}`)
26
+
27
+ const { demoDir, demoFormats } = config.release
28
+ const hasDemo = demoFormats.some((fmt) => existsSync(`${demoDir}/${nextVersion}.${fmt}`))
29
+
30
+ if (!hasDemo) {
31
+ console.log(`❌ Релиз ${nextVersion} требует демо!`)
32
+ console.log(`📸 Создай: ${demoDir}/${nextVersion}.${demoFormats[0]}`)
33
+ process.exit(1)
34
+ }
35
+
36
+ console.log(`✅ Демо для ${nextVersion} готово!`)
@@ -0,0 +1,125 @@
1
+ import { existsSync, readFileSync } from 'fs'
2
+ import { join } from 'path'
3
+ import { pathToFileURL } from 'url'
4
+
5
+ const DEFAULTS = {
6
+ branch: {
7
+ main: 'main',
8
+ patterns: ['v{version}-{name}'],
9
+ },
10
+ labels: [
11
+ { name: 'task', color: '0E8A16', description: 'Новая фича', emoji: '✨' },
12
+ { name: 'bug', color: 'D73A4A', description: 'Баг', emoji: '🐛' },
13
+ { name: 'refactor', color: 'FEF2C0', description: 'Рефакторинг/техдолг', emoji: '♻️' },
14
+ { name: 'perf', color: '007bff', description: 'Оптимизация производительности', emoji: '⚡' },
15
+ ],
16
+ commits: {
17
+ types: ['feat', 'fix', 'refactor', 'build', 'chore', 'docs', 'perf'],
18
+ requireIssue: ['feat', 'fix'],
19
+ closeKeyword: 'close',
20
+ },
21
+ release: {
22
+ requireDemo: true,
23
+ demoDir: 'docs',
24
+ demoFormats: ['gif', 'png'],
25
+ },
26
+ changelog: {
27
+ types: {
28
+ feat: { title: '✨ Новые фичи', semver: 'minor' },
29
+ fix: { title: '🐛 Исправления', semver: 'patch' },
30
+ refactor: { title: '♻️ Рефакторинг', semver: 'patch' },
31
+ perf: { title: '⚡ Оптимизация', semver: 'patch' },
32
+ },
33
+ },
34
+ depUpdater: false,
35
+ }
36
+
37
+ // --- Branch pattern template engine ---
38
+
39
+ const BRANCH_PLACEHOLDERS = {
40
+ '{version}': '\\d+\\.\\d+\\.\\d+',
41
+ '{issue}': '\\d+',
42
+ '{name}': '[a-zA-Z0-9_-]+',
43
+ }
44
+
45
+ function compileTemplate(template) {
46
+ const parts = template.split(/(\{[a-z]+\}|\*)/g)
47
+ return parts
48
+ .map((part) => {
49
+ if (BRANCH_PLACEHOLDERS[part]) return BRANCH_PLACEHOLDERS[part]
50
+ if (part === '*') return '.+'
51
+ return part.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
52
+ })
53
+ .join('')
54
+ }
55
+
56
+ /**
57
+ * Compiles branch config into a RegExp.
58
+ * Supports template patterns (new) and raw regex (legacy).
59
+ *
60
+ * Templates:
61
+ * {version} → X.Y.Z (semver)
62
+ * {issue} → issue number (digits)
63
+ * {name} → branch description (letters, digits, hyphens, underscores)
64
+ * * → anything
65
+ */
66
+ export function compileBranchRegex(branchConfig) {
67
+ if (branchConfig.patterns && Array.isArray(branchConfig.patterns)) {
68
+ const compiled = branchConfig.patterns.map(compileTemplate)
69
+ return new RegExp(`^(${compiled.join('|')})$`)
70
+ }
71
+
72
+ if (branchConfig.pattern) {
73
+ return new RegExp(branchConfig.pattern)
74
+ }
75
+
76
+ return new RegExp('^v.*-.*')
77
+ }
78
+
79
+ // --- Deep merge ---
80
+
81
+ function deepMerge(target, source) {
82
+ const result = { ...target }
83
+ for (const key of Object.keys(source)) {
84
+ if (
85
+ source[key] &&
86
+ typeof source[key] === 'object' &&
87
+ !Array.isArray(source[key]) &&
88
+ target[key] &&
89
+ typeof target[key] === 'object' &&
90
+ !Array.isArray(target[key])
91
+ ) {
92
+ result[key] = deepMerge(target[key], source[key])
93
+ } else {
94
+ result[key] = source[key]
95
+ }
96
+ }
97
+ return result
98
+ }
99
+
100
+ /**
101
+ * Loads config from jst.config.js (preferred) or jst.config.json (fallback)
102
+ * Deep-merges user config with defaults
103
+ */
104
+ export async function loadConfig() {
105
+ const jsPath = join(process.cwd(), 'jst.config.js')
106
+ const jsonPath = join(process.cwd(), 'jst.config.json')
107
+ let userConfig = {}
108
+
109
+ if (existsSync(jsPath)) {
110
+ try {
111
+ const mod = await import(pathToFileURL(jsPath).href)
112
+ userConfig = mod.default || {}
113
+ } catch (e) {
114
+ console.warn('⚠️ Ошибка чтения jst.config.js:', e.message)
115
+ }
116
+ } else if (existsSync(jsonPath)) {
117
+ try {
118
+ userConfig = JSON.parse(readFileSync(jsonPath, 'utf8'))
119
+ } catch (e) {
120
+ console.warn('⚠️ Ошибка чтения jst.config.json:', e.message)
121
+ }
122
+ }
123
+
124
+ return deepMerge(DEFAULTS, userConfig)
125
+ }
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env node
2
+ import { execSync } from 'child_process'
3
+ import { loadConfig } from './config.js'
4
+
5
+ const config = await loadConfig()
6
+ const { closeKeyword, requireIssue } = config.commits
7
+
8
+ const commitMsg = execSync('git log -1 --pretty=%B', { encoding: 'utf8' }).trim()
9
+ const commitHash = execSync('git rev-parse --short HEAD', { encoding: 'utf8' }).trim()
10
+
11
+ let repoUrl = execSync('git remote get-url origin', { encoding: 'utf8' }).trim()
12
+ if (repoUrl.startsWith('git@github.com:')) {
13
+ repoUrl = repoUrl.replace('git@github.com:', 'https://github.com/').replace(/\.git$/, '')
14
+ } else {
15
+ repoUrl = repoUrl.replace(/\.git$/, '')
16
+ }
17
+
18
+ execSync('git push origin HEAD', { stdio: 'inherit' })
19
+
20
+ const escClose = closeKeyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
21
+ const typesGroup = requireIssue.join('|')
22
+ const closePattern = new RegExp(`^(${typesGroup})(\\(.+\\))?: ${escClose} #(\\d+)`)
23
+ const match = commitMsg.match(closePattern)
24
+
25
+ if (match) {
26
+ const issueNumber = match[3]
27
+ const commitLink = `${repoUrl}/commit/${commitHash}`
28
+ const firstLine = commitMsg.split('\n')[0]
29
+
30
+ console.log(`🔧 Закрываю issue #${issueNumber}...`)
31
+
32
+ try {
33
+ execSync(
34
+ `gh issue close "${issueNumber}" --comment "✅ Завершено в коммите: [${commitHash}](${commitLink}) - ${firstLine}"`,
35
+ { stdio: 'inherit' },
36
+ )
37
+ } catch {
38
+ console.log(`⚠️ Не удалось закрыть issue #${issueNumber}`)
39
+ }
40
+ }