fg-devkit 1.25.0 → 1.26.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.
@@ -40,7 +40,7 @@
40
40
  "pinia": "4.1.0",
41
41
  "playwright": "1.64.1",
42
42
  "quill": "2.0.3",
43
- "randexp": "0.5.3",
43
+ "randexp": "0.6.1",
44
44
  "register-service-worker": "1.7.2",
45
45
  "vega-embed": "7.3.0",
46
46
  "vega-lite": "6.5.0",
@@ -77,7 +77,7 @@
77
77
  "eslint-plugin-storybook": "10.6.3",
78
78
  "globals": "15.11.0",
79
79
  "jsdom": "30.1.3",
80
- "sass": "1.106.0",
80
+ "sass": "1.106.1",
81
81
  "storybook": "10.6.3",
82
82
  "tsx": "4.23.15",
83
83
  "vega": "6.4.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fg-devkit",
3
- "version": "1.25.0",
3
+ "version": "1.26.0",
4
4
  "description": "Shared configs, scripts, and DX tooling for Vue / TypeScript frontend repos",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -59,7 +59,7 @@
59
59
  "eslint": "10.13.0",
60
60
  "eslint-plugin-prettier": "5.5.7",
61
61
  "eslint-plugin-vue": "10.11.1",
62
- "eslint-plugin-vuejs-accessibility": "2.6.2",
62
+ "eslint-plugin-vuejs-accessibility": "2.6.3",
63
63
  "fallow": "3.34.1",
64
64
  "git-cliff": "2.14.2",
65
65
  "lefthook": "2.2.2",
@@ -32,7 +32,7 @@
32
32
  "@intlify/unplugin-vue-i18n": "11.2.7",
33
33
  "@tanstack/vue-query-devtools": "6.3.5",
34
34
  "@types/node": "22.16.5",
35
- "fg-devkit": "1.25.0",
35
+ "fg-devkit": "1.26.0",
36
36
  "@vitejs/plugin-vue": "6.0.10",
37
37
  "tailwindcss": "4.3.3",
38
38
  "tsx": "4.23.15",
@@ -57,6 +57,7 @@ import { fileURLToPath } from 'node:url'
57
57
  import { createRequire } from 'node:module'
58
58
  import { styleText } from 'node:util'
59
59
  import { fgDevkitRoot } from './lib/common.mjs'
60
+ import { removeUnconfiguredGhostHook } from './lib/lefthook-ghost-hook.mjs'
60
61
 
61
62
  const scriptDir = path.dirname(fileURLToPath(import.meta.url))
62
63
  const require = createRequire(import.meta.url)
@@ -80,6 +81,42 @@ function runNode(scriptRelative, args = [], options = {}) {
80
81
  return r.status ?? 1
81
82
  }
82
83
 
84
+ function readNextReleaseVersions(releaseKind) {
85
+ let currentVersion
86
+ try {
87
+ currentVersion = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8')).version
88
+ } catch {
89
+ return null
90
+ }
91
+ const match = typeof currentVersion === 'string' && currentVersion.match(/^(\d+)\.(\d+)\.(\d+)(-.+)?$/)
92
+ if (!match) return null
93
+
94
+ const bumpTypes = ['patch', 'minor', 'major']
95
+ if (releaseKind === 'candidate') {
96
+ const nextVersions = {}
97
+ for (const bumpType of bumpTypes) {
98
+ const result = spawnSync(process.execPath, [path.join(scriptDir, 'resolve-candidate-version.mjs'), bumpType], {
99
+ cwd: process.cwd(),
100
+ encoding: 'utf8',
101
+ })
102
+ if (result.status !== 0) return null
103
+ nextVersions[bumpType] = result.stdout.trim()
104
+ }
105
+ return { currentVersion, nextVersions }
106
+ }
107
+
108
+ const [major, minor, patch] = match.slice(1, 4).map(Number)
109
+ const isPrerelease = Boolean(match[4])
110
+ return {
111
+ currentVersion,
112
+ nextVersions: {
113
+ patch: isPrerelease ? `${major}.${minor}.${patch}` : `${major}.${minor}.${patch + 1}`,
114
+ minor: isPrerelease && patch === 0 ? `${major}.${minor}.0` : `${major}.${minor + 1}.0`,
115
+ major: isPrerelease && minor === 0 && patch === 0 ? `${major}.0.0` : `${major + 1}.0.0`,
116
+ },
117
+ }
118
+ }
119
+
83
120
  /**
84
121
  * Menu label with dimmed tags: `Upgrade packages [interactive, auto-commit]`.
85
122
  * interactive: the action stops and asks for input while it runs. autoCommit: it commits on its own.
@@ -736,12 +773,15 @@ async function runInteractiveMenu() {
736
773
  })
737
774
  if (releaseMode === 'cancel') continue
738
775
 
776
+ const releaseVersions = readNextReleaseVersions(releaseKind)
777
+ const versionChange = (bumpType) =>
778
+ releaseVersions ? `: ${releaseVersions.currentVersion} → ${releaseVersions.nextVersions[bumpType]}` : ''
739
779
  const bumpChoice = await selectPrompt({
740
780
  message: releaseKind === 'candidate' ? 'Release candidate' : 'Release',
741
781
  choices: [
742
- { name: 'Patch', value: 'patch', description: 'Bug fixes only: 1.2.3 → 1.2.4' },
743
- { name: 'Minor', value: 'minor', description: 'New features, backwards compatible: 1.2.3 → 1.3.0' },
744
- { name: 'Major', value: 'major', description: 'Breaking changes: 1.2.3 → 2.0.0' },
782
+ { name: 'Patch', value: 'patch', description: `Bug fixes only${versionChange('patch')}` },
783
+ { name: 'Minor', value: 'minor', description: `New features, backwards compatible${versionChange('minor')}` },
784
+ { name: 'Major', value: 'major', description: `Breaking changes${versionChange('major')}` },
745
785
  { name: 'Cancel', value: 'cancel' },
746
786
  ],
747
787
  default: 'patch',
@@ -1241,6 +1281,8 @@ async function runInteractiveMenu() {
1241
1281
  async function main() {
1242
1282
  const [, , sub, ...rest] = process.argv
1243
1283
 
1284
+ removeUnconfiguredGhostHook(process.cwd())
1285
+
1244
1286
  if (sub === '--help' || sub === '-h') {
1245
1287
  printHelp()
1246
1288
  return
@@ -0,0 +1,26 @@
1
+ import fs from 'node:fs'
2
+ import path from 'node:path'
3
+ import { runGit } from './common.mjs'
4
+
5
+ const ghostHookName = 'prepare-commit-msg'
6
+ const lefthookConfigFiles = ['lefthook.yml', '.lefthook.yml', 'lefthook-local.yml', '.lefthook-local.yml']
7
+
8
+ export function removeUnconfiguredGhostHook(cwd) {
9
+ const options = { cwd, tag: '[fg-devkit]', capture: true }
10
+ const repositoryRoot = runGit(['rev-parse', '--show-toplevel'], options)
11
+ const hooksDirectory = runGit(['rev-parse', '--git-path', 'hooks'], options)
12
+ if (repositoryRoot.status !== 0 || hooksDirectory.status !== 0) return
13
+
14
+ const configuresGhostHook = lefthookConfigFiles.some((fileName) => {
15
+ const configPath = path.join(repositoryRoot.stdout.trim(), fileName)
16
+ return fs.existsSync(configPath) && fs.readFileSync(configPath, 'utf8').includes(`${ghostHookName}:`)
17
+ })
18
+ if (configuresGhostHook) return
19
+
20
+ const ghostHookPath = path.resolve(cwd, hooksDirectory.stdout.trim(), ghostHookName)
21
+ if (!fs.existsSync(ghostHookPath)) return
22
+ if (!fs.readFileSync(ghostHookPath, 'utf8').includes('call_lefthook')) return
23
+
24
+ fs.rmSync(ghostHookPath)
25
+ console.log(`[fg-devkit] Removed unused lefthook ${ghostHookName} hook.`)
26
+ }