@vv0rkz/js-template 1.8.0 → 1.8.3

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/package.json CHANGED
@@ -1,41 +1,42 @@
1
- {
2
- "name": "@vv0rkz/js-template",
3
- "version": "1.8.0",
4
- "description": "Reusable setup for JS projects with husky, changelog, gh tools",
5
- "type": "module",
6
- "bin": {
7
- "jst": "./bin/cli.js",
8
- "js-template": "./bin/cli.js"
9
- },
10
- "files": [
11
- "bin/",
12
- "templates/",
13
- "tools-gh/"
14
- ],
15
- "scripts": {
16
- "test": "echo 'Tests not implemented yet' && exit 0"
17
- },
18
- "keywords": [
19
- "template",
20
- "husky",
21
- "changelog",
22
- "github",
23
- "workflow",
24
- "commitlint"
25
- ],
26
- "author": "vv0rkz",
27
- "license": "MIT",
28
- "repository": {
29
- "type": "git",
30
- "url": "https://github.com/vv0rkz/js-template.git"
31
- },
32
- "dependencies": {
33
- "@commitlint/cli": "^20.1.0",
34
- "@commitlint/config-conventional": "^20.0.0",
35
- "changelogen": "^0.6.2",
36
- "husky": "^9.1.7"
37
- },
38
- "devDependencies": {
39
- "browser-sync": "^3.0.4"
40
- }
41
- }
1
+ {
2
+ "name": "@vv0rkz/js-template",
3
+ "version": "1.8.3",
4
+ "description": "Reusable setup for JS projects with husky, changelog, gh tools",
5
+ "type": "module",
6
+ "bin": {
7
+ "jst": "./bin/cli.js",
8
+ "js-template": "./bin/cli.js"
9
+ },
10
+ "files": [
11
+ "bin/",
12
+ "templates/",
13
+ "tools-gh/"
14
+ ],
15
+ "scripts": {
16
+ "test": "echo 'Tests not implemented yet' && exit 0"
17
+ },
18
+ "keywords": [
19
+ "template",
20
+ "husky",
21
+ "changelog",
22
+ "github",
23
+ "workflow",
24
+ "commitlint"
25
+ ],
26
+ "author": "vv0rkz",
27
+ "license": "MIT",
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "https://github.com/vv0rkz/js-template.git"
31
+ },
32
+ "dependencies": {
33
+ "@commitlint/cli": "^20.1.0",
34
+ "@commitlint/config-conventional": "^20.0.0",
35
+ "changelogen": "^0.6.2",
36
+ "husky": "^9.1.7",
37
+ "sharp": "^0.34.5"
38
+ },
39
+ "devDependencies": {
40
+ "browser-sync": "^3.0.4"
41
+ }
42
+ }
@@ -83,14 +83,26 @@ export default {
83
83
  * Настройки релиза (`jst release`)
84
84
  */
85
85
  release: {
86
- /** Требовать GIF/PNG демо перед релизом (true | false) */
87
- requireDemo: true,
86
+ /**
87
+ * Настройки демо
88
+ */
89
+ demo: {
90
+ /** Требовать демо перед релизом (true | false) */
91
+ enable: true,
92
+
93
+ /** Директория для демо-файлов */
94
+ dir: 'docs',
88
95
 
89
- /** Директория для демо-файлов */
90
- demoDir: 'docs',
96
+ /** Допустимые форматы демо-файлов */
97
+ formats: ['gif', 'png'],
91
98
 
92
- /** Допустимые форматы демо-файлов */
93
- demoFormats: ['gif', 'png'],
99
+ /**
100
+ * Способ отображения демо в README
101
+ * 'click' — PNG превью, клик открывает GIF (быстрая загрузка)
102
+ * 'side-by-side' — PNG + GIF рядом (GIF грузится автоматически)
103
+ */
104
+ style: 'click',
105
+ },
94
106
  },
95
107
 
96
108
  /**
@@ -117,9 +129,4 @@ export default {
117
129
  */
118
130
  depUpdater: false,
119
131
 
120
- /**
121
- * Репозиторий JST для команды `report-issue`
122
- * Формат: 'owner/repo'
123
- */
124
- jstRepo: 'vv0rkz/js-template',
125
132
  }
@@ -5,7 +5,7 @@ import { loadConfig } from './config.js'
5
5
 
6
6
  const config = await loadConfig()
7
7
 
8
- if (!config.release.requireDemo) {
8
+ if (!config.release.demo.enable) {
9
9
  console.log('⏭️ Проверка демо отключена')
10
10
  process.exit(0)
11
11
  }
@@ -24,7 +24,7 @@ if (commitMessages.includes('feat:')) {
24
24
 
25
25
  console.log(`📦 Предполагаемая следующая версия: ${nextVersion}`)
26
26
 
27
- const { demoDir, demoFormats } = config.release
27
+ const { dir: demoDir, formats: demoFormats } = config.release.demo
28
28
  const hasDemo = demoFormats.some((fmt) => existsSync(`${demoDir}/${nextVersion}.${fmt}`))
29
29
 
30
30
  if (!hasDemo) {
@@ -19,9 +19,12 @@ const DEFAULTS = {
19
19
  closeKeyword: 'close',
20
20
  },
21
21
  release: {
22
- requireDemo: true,
23
- demoDir: 'docs',
24
- demoFormats: ['gif', 'png'],
22
+ demo: {
23
+ enable: true,
24
+ dir: 'docs',
25
+ formats: ['gif', 'png'],
26
+ style: 'click',
27
+ },
25
28
  },
26
29
  changelog: {
27
30
  types: {
@@ -32,7 +35,6 @@ const DEFAULTS = {
32
35
  },
33
36
  },
34
37
  depUpdater: false,
35
- jstRepo: 'vv0rkz/js-template',
36
38
  }
37
39
 
38
40
  // --- Branch pattern template engine ---
@@ -15,10 +15,14 @@ console.log('🚀 Запуск релиза...\n')
15
15
 
16
16
  // 1. Current version
17
17
  const currentVersion = JSON.parse(readFileSync('package.json', 'utf8')).version
18
+ if (!currentVersion) {
19
+ console.log('❌ Нет поля version в package.json. Добавь: "version": "X.Y.Z"')
20
+ process.exit(1)
21
+ }
18
22
  console.log(`📦 Текущая версия: ${currentVersion}`)
19
23
 
20
24
  // 2. Demo check (configurable)
21
- if (config.release.requireDemo) {
25
+ if (config.release.demo.enable) {
22
26
  const [major, minor, patch] = currentVersion.split('.').map(Number)
23
27
  const recentCommits = execSync('git log --oneline -10', { encoding: 'utf8' })
24
28
  const nextVersion = recentCommits.includes('feat:')
@@ -27,7 +31,7 @@ if (config.release.requireDemo) {
27
31
 
28
32
  console.log(`📦 Предполагаемая следующая версия: ${nextVersion}`)
29
33
 
30
- const { demoDir, demoFormats } = config.release
34
+ const { dir: demoDir, formats: demoFormats } = config.release.demo
31
35
  const hasDemo = demoFormats.some((fmt) => existsSync(`${demoDir}/${nextVersion}.${fmt}`))
32
36
 
33
37
  if (!hasDemo) {
@@ -38,7 +42,7 @@ if (config.release.requireDemo) {
38
42
 
39
43
  console.log(`✅ Демо для ${nextVersion} готово!`)
40
44
  } else {
41
- console.log('⏭️ Проверка демо отключена (release.requireDemo = false)')
45
+ console.log('⏭️ Проверка демо отключена (release.demo.enable = false)')
42
46
  }
43
47
 
44
48
  // 3. Warning for 0.x.x
@@ -1,10 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { spawnSync } from 'child_process'
3
3
  import { platform } from 'os'
4
- import { loadConfig } from './config.js'
5
4
 
6
- const config = await loadConfig()
7
- const jstRepo = config.jstRepo
5
+ const jstRepo = 'vv0rkz/js-template'
8
6
  const isWin = platform() === 'win32'
9
7
  const ghCmd = isWin ? 'gh.exe' : 'gh'
10
8
 
@@ -1,10 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import { execSync } from 'child_process'
3
3
  import { existsSync, readFileSync, writeFileSync } from 'fs'
4
+ import sharp from 'sharp'
5
+ import { loadConfig } from './config.js'
6
+
7
+ const config = await loadConfig()
8
+ const { dir: demoDir, style: demoStyle } = config.release.demo
4
9
 
5
10
  console.log('🎨 Обновляю README релизами с демо...')
6
11
 
7
- // Проверяем наличие файлов
8
12
  if (!existsSync('CHANGELOG.md')) {
9
13
  console.log('❌ CHANGELOG.md не найден')
10
14
  console.log('💡 Сначала запусти: npm run _ changelog')
@@ -19,45 +23,97 @@ if (!existsSync('README.md')) {
19
23
  const changelog = readFileSync('CHANGELOG.md', 'utf8')
20
24
  let readme = readFileSync('README.md', 'utf8')
21
25
 
22
- // Получаем информацию о репозитории
26
+ // Получаем URL репозитория
23
27
  let repoUrl
24
28
  try {
25
29
  const remoteUrl = execSync('git config --get remote.origin.url').toString().trim()
26
30
  if (remoteUrl.includes('github.com')) {
27
31
  repoUrl = remoteUrl.replace('git@github.com:', 'https://github.com/').replace('.git', '')
28
32
  }
29
- } catch (error) {
33
+ } catch {
30
34
  console.log('⚠️ Не удалось определить URL репозитория')
31
35
  }
32
36
 
33
- // Парсим changelog - ТОЛЬКО версии с демо
37
+ /**
38
+ * Извлекает первый кадр GIF в PNG через sharp.
39
+ * Возвращает true если PNG создан успешно.
40
+ */
41
+ async function tryGeneratePng(gifPath, pngPath) {
42
+ try {
43
+ await sharp(gifPath).png().toFile(pngPath)
44
+ console.log(`🖼️ PNG превью создан: ${pngPath}`)
45
+ return true
46
+ } catch {
47
+ return false
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Генерирует HTML блок демо в зависимости от demoStyle.
53
+ */
54
+ function renderDemo(version, gifPath, pngPath, hasGif, hasPng) {
55
+ if (demoStyle === 'side-by-side') {
56
+ let html = ''
57
+ if (hasPng) html += `<img src="${pngPath}" alt="${version} demo preview" width="400" />`
58
+ if (hasGif) html += `<img src="${gifPath}" alt="${version} demo animation" width="400" />`
59
+ return `**Демо работы** \n${html}\n\n`
60
+ }
61
+
62
+ // 'click' (default) — PNG превью, клик открывает GIF
63
+ if (hasGif && hasPng) {
64
+ return (
65
+ `**Демо работы** \n` +
66
+ `<a href="${gifPath}"><img src="${pngPath}" alt="${version} demo preview" width="400" /></a>\n\n` +
67
+ `*${version} — нажми на превью чтобы увидеть анимацию*\n\n`
68
+ )
69
+ }
70
+ if (hasPng) {
71
+ return `**Демо работы** \n<img src="${pngPath}" alt="${version} demo preview" width="400" />\n\n`
72
+ }
73
+ return `**Демо работы** \n<img src="${gifPath}" alt="${version} demo" width="400" />\n\n`
74
+ }
75
+
76
+ // Парсим changelog — только версии с демо
34
77
  const versionBlocks = changelog.split('## v').slice(1)
35
78
  let prettyChangelog = '## 📋 История версий\n\n'
36
79
  const processedVersions = new Set()
37
80
 
38
- versionBlocks.forEach((versionBlock) => {
39
- const versionMatch = versionBlock.match(/^(\d+\.\d+\.\d+)/)
40
- if (!versionMatch) return
81
+ for (const versionBlock of versionBlocks) {
82
+ // Поддержка заголовков вида "v1.4.0...v2.0.0" (changelogen диапазон) и обычных "v2.0.0"
83
+ const rangeMatch = versionBlock.match(/^\d+\.\d+\.\d+\.\.\.v(\d+\.\d+\.\d+)/)
84
+ const simpleMatch = versionBlock.match(/^(\d+\.\d+\.\d+)/)
85
+ const versionMatch = rangeMatch || simpleMatch
86
+ if (!versionMatch) continue
41
87
 
42
88
  const version = `v${versionMatch[1]}`
43
- if (processedVersions.has(version)) return
89
+ if (processedVersions.has(version)) continue
44
90
  processedVersions.add(version)
45
91
 
46
- // ПРОВЕРЯЕМ ДЕМО - если нет демо, пропускаем
47
- const hasDemo = existsSync(`docs/${version}.gif`) || existsSync(`docs/${version}.png`)
92
+ const gifPath = `${demoDir}/${version}.gif`
93
+ const pngPath = `${demoDir}/${version}.png`
94
+
95
+ const hasGif = existsSync(gifPath)
96
+ let hasPng = existsSync(pngPath)
97
+
98
+ // Если есть GIF но нет PNG — пытаемся сгенерировать PNG через sharp
99
+ if (hasGif && !hasPng) {
100
+ hasPng = await tryGeneratePng(gifPath, pngPath)
101
+ }
102
+
103
+ const hasDemo = hasGif || hasPng
48
104
  if (!hasDemo) {
49
105
  console.log(`⏭️ Пропускаем ${version} - нет демо`)
50
- return
106
+ continue
51
107
  }
52
108
 
53
- // Пропускаем если нет фич (поддержка разных форматов заголовков)
109
+ // Пропускаем если нет фич
54
110
  if (
55
111
  !versionBlock.includes('### ✨ Новые фичи') &&
56
112
  !versionBlock.includes('### ✨ Фичи') &&
57
113
  !versionBlock.includes('### 🚀')
58
114
  ) {
59
115
  console.log(`⏭️ Пропускаем ${version} - нет фич`)
60
- return
116
+ continue
61
117
  }
62
118
 
63
119
  // Извлекаем фичи
@@ -66,7 +122,6 @@ versionBlocks.forEach((versionBlock) => {
66
122
  let inFeaturesSection = false
67
123
 
68
124
  for (const line of lines) {
69
- // Поддержка разных форматов заголовков фич
70
125
  if (line.includes('### ✨ Новые фичи') || line.includes('### ✨ Фичи') || line.includes('### 🚀')) {
71
126
  inFeaturesSection = true
72
127
  continue
@@ -87,33 +142,24 @@ versionBlocks.forEach((versionBlock) => {
87
142
  }
88
143
  }
89
144
 
90
- if (features.length === 0) return
145
+ if (features.length === 0) continue
91
146
 
92
147
  console.log(`✅ Добавляем ${version} - есть демо и ${features.length} фич`)
93
148
 
94
- // Форматируем версию
95
149
  prettyChangelog += `### 🟢 ${version}\n\n`
150
+ prettyChangelog += renderDemo(version, gifPath, pngPath, hasGif, hasPng)
96
151
 
97
- // Добавляем демо (гарантированно есть)
98
- if (existsSync(`docs/${version}.gif`)) {
99
- prettyChangelog += `**Демо работы** \n<img src="docs/${version}.gif" width="400" />\n\n`
100
- } else {
101
- prettyChangelog += `**Демо работы** \n<img src="docs/${version}.png" width="400" />\n\n`
102
- }
103
-
104
- // Добавляем функционал
105
152
  prettyChangelog += `**Функционал:**\n`
106
153
  features.forEach((feature) => {
107
154
  prettyChangelog += `- ${feature}\n`
108
155
  })
109
156
 
110
- // Добавляем ссылку на релиз (если есть URL репозитория)
111
157
  if (repoUrl) {
112
158
  prettyChangelog += `\n**Релиз:** ${repoUrl}/releases/tag/${version}\n\n`
113
159
  }
114
160
 
115
161
  prettyChangelog += `---\n\n`
116
- })
162
+ }
117
163
 
118
164
  // Заменяем секцию между маркерами
119
165
  if (readme.includes('<!-- AUTOGENERATED_SECTION START -->')) {
@@ -135,7 +181,6 @@ if (readme.includes('<!-- AUTOGENERATED_SECTION START -->')) {
135
181
  process.exit(1)
136
182
  }
137
183
 
138
- // Сохраняем
139
184
  writeFileSync('README.md', readme)
140
185
  console.log('✅ README обновлён с релизами, у которых есть демо!')
141
186
 
@@ -150,6 +195,6 @@ try {
150
195
  } else {
151
196
  console.log('💡 README не изменился')
152
197
  }
153
- } catch (error) {
198
+ } catch {
154
199
  console.log('💡 README обновлён локально (не удалось запушить автоматически)')
155
200
  }