fg-devkit 1.1.2 → 1.3.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/README.md +25 -0
- package/configs/dependency-policy.vue-workspace.json +1 -21
- package/configs/lefthook.yml +4 -0
- package/package.json +7 -1
- package/scripts/fg-devkit.mjs +107 -0
- package/scripts/prune-consumer-deps.mjs +99 -0
- package/scripts/refresh-dependency-policy-from-repos.mjs +13 -0
- package/scripts/sync-consumer-lefthook.mjs +67 -0
- package/scripts/sync-consumer-scripts.mjs +61 -0
package/README.md
CHANGED
|
@@ -45,6 +45,31 @@ pnpm exec fgd typecheck
|
|
|
45
45
|
pnpm exec fgd typecheck-watch
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
+
To remove redundant tooling dependencies from a consuming repo:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pnpm exec fgd prune-consumer-deps
|
|
52
|
+
pnpm install
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
To sync standard consumer scripts to `fgd` commands:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pnpm exec fgd sync-consumer-scripts
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
To migrate from Husky + lint-staged to Lefthook:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pnpm exec fgd setup-lefthook
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Run full consumer setup in one command:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
pnpm exec fgd full-consumer-setup
|
|
71
|
+
```
|
|
72
|
+
|
|
48
73
|
## tsconfig in consuming repos
|
|
49
74
|
|
|
50
75
|
Use `fgd sync-project-configs` (or `fgd sync-tooling-configs`) to write the local `tsconfig.json` template from `fg-devkit`.
|
|
@@ -49,10 +49,8 @@
|
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@chromatic-com/storybook": "5.0.1",
|
|
51
51
|
"@eslint/js": "9.39.1",
|
|
52
|
-
"@intlify/eslint-plugin-vue-i18n": "4.3.0",
|
|
53
52
|
"@intlify/unplugin-vue-i18n": "11.0.7",
|
|
54
53
|
"@playwright/test": "1.59.1",
|
|
55
|
-
"@rushstack/eslint-patch": "1.16.1",
|
|
56
54
|
"@storybook/addon-a11y": "10.1.10",
|
|
57
55
|
"@storybook/addon-docs": "10.1.10",
|
|
58
56
|
"@storybook/addon-vitest": "10.2.12",
|
|
@@ -62,45 +60,27 @@
|
|
|
62
60
|
"@tsconfig/node22": "22.0.5",
|
|
63
61
|
"@types/lodash-es": "4.17.12",
|
|
64
62
|
"@types/node": "22.16.5",
|
|
65
|
-
"@typescript-eslint/eslint-plugin": "8.57.2",
|
|
66
|
-
"@typescript-eslint/parser": "8.57.2",
|
|
67
63
|
"@vitest/browser": "3.2.4",
|
|
68
|
-
"@vitest/eslint-plugin": "1.6.14",
|
|
69
64
|
"@vue/compiler-dom": "3.5.29",
|
|
70
|
-
"@vue/eslint-config-prettier": "10.2.0",
|
|
71
65
|
"@vue/eslint-config-standard": "8.0.1",
|
|
72
|
-
"@vue/eslint-config-typescript": "14.7.0",
|
|
73
66
|
"@vue/server-renderer": "3.5.29",
|
|
74
67
|
"@vue/test-utils": "2.4.6",
|
|
75
|
-
"@vue/tsconfig": "0.8.1",
|
|
76
68
|
"@vuepic/vue-datepicker": "6.0.3",
|
|
77
69
|
"arg": "5.0.2",
|
|
78
70
|
"babel-eslint": "10.1.0",
|
|
79
|
-
"commitizen": "4.3.1",
|
|
80
|
-
"eslint": "10.0.2",
|
|
81
|
-
"eslint-plugin-prettier": "5.5.5",
|
|
82
71
|
"eslint-plugin-storybook": "10.1.10",
|
|
83
|
-
"eslint-plugin-vue": "10.8.0",
|
|
84
72
|
"globals": "15.11.0",
|
|
85
|
-
"husky": "9.1.6",
|
|
86
73
|
"jsdom": "26.1.0",
|
|
87
|
-
"jsonc-eslint-parser": "2.4.1",
|
|
88
|
-
"lint-staged": "16.4.0",
|
|
89
74
|
"postcss": "8.5.6",
|
|
90
|
-
"prettier": "3.8.1",
|
|
91
75
|
"sass": "1.97.3",
|
|
92
76
|
"start-server-and-test": "2.1.3",
|
|
93
77
|
"storybook": "10.2.12",
|
|
94
78
|
"tsx": "4.21.0",
|
|
95
|
-
"typescript": "5.9.3",
|
|
96
79
|
"vega": "6.2.0",
|
|
97
80
|
"vite-plugin-dts": "4.5.4",
|
|
98
81
|
"vite-plugin-vue-devtools": "8.0.7",
|
|
99
82
|
"vitest": "3.2.4",
|
|
100
|
-
"
|
|
101
|
-
"vue-tsc": "3.2.6",
|
|
102
|
-
"yaml": "2.8.3",
|
|
103
|
-
"yaml-eslint-parser": "1.3.1"
|
|
83
|
+
"yaml": "2.8.3"
|
|
104
84
|
}
|
|
105
85
|
},
|
|
106
86
|
"managedAnywhere": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fg-devkit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Shared configs, scripts, and DX tooling for Vue / TypeScript frontend repos",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"./tsconfig.app.sync.json": "./configs/tsconfig.app.sync.json",
|
|
16
16
|
"./eslint.config.base.js": "./configs/eslint.config.base.js",
|
|
17
17
|
"./prettier.config.js": "./configs/prettier.config.js",
|
|
18
|
+
"./lefthook.yml": "./configs/lefthook.yml",
|
|
18
19
|
"./vscode-settings.json": "./configs/vscode-settings.json",
|
|
19
20
|
"./dependency-policy.vue-workspace.json": "./configs/dependency-policy.vue-workspace.json",
|
|
20
21
|
"./vuln-commit": "./scripts/vuln-commit.mjs",
|
|
@@ -24,6 +25,9 @@
|
|
|
24
25
|
"./scripts/sync-vscode-settings.mjs": "./scripts/sync-vscode-settings.mjs",
|
|
25
26
|
"./scripts/sync-tooling-configs.mjs": "./scripts/sync-tooling-configs.mjs",
|
|
26
27
|
"./scripts/sync-project-configs.mjs": "./scripts/sync-project-configs.mjs",
|
|
28
|
+
"./scripts/sync-consumer-scripts.mjs": "./scripts/sync-consumer-scripts.mjs",
|
|
29
|
+
"./scripts/sync-consumer-lefthook.mjs": "./scripts/sync-consumer-lefthook.mjs",
|
|
30
|
+
"./scripts/prune-consumer-deps.mjs": "./scripts/prune-consumer-deps.mjs",
|
|
27
31
|
"./scripts/check-dependency-policy.mjs": "./scripts/check-dependency-policy.mjs",
|
|
28
32
|
"./scripts/sync-dependency-policy.mjs": "./scripts/sync-dependency-policy.mjs",
|
|
29
33
|
"./scripts/resolve-dependency-policy-path.mjs": "./scripts/resolve-dependency-policy-path.mjs",
|
|
@@ -59,11 +63,13 @@
|
|
|
59
63
|
"@typescript-eslint/parser": "8.58.2",
|
|
60
64
|
"@vitest/eslint-plugin": "1.6.16",
|
|
61
65
|
"@vue/eslint-config-prettier": "10.2.0",
|
|
66
|
+
"@vue/eslint-config-typescript": "14.7.0",
|
|
62
67
|
"@vue/tsconfig": "0.9.1",
|
|
63
68
|
"commitizen": "4.3.1",
|
|
64
69
|
"eslint": "10.2.1",
|
|
65
70
|
"eslint-plugin-prettier": "5.5.5",
|
|
66
71
|
"eslint-plugin-vue": "10.8.0",
|
|
72
|
+
"lefthook": "2.1.6",
|
|
67
73
|
"prettier": "3.8.3",
|
|
68
74
|
"typescript": "5.9.3",
|
|
69
75
|
"vue-eslint-parser": "10.4.0",
|
package/scripts/fg-devkit.mjs
CHANGED
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
* pnpm exec fg-devkit sync-project-configs (VS Code + tooling sync; overwrites config files)
|
|
9
9
|
* pnpm exec fg-devkit sync-tooling-configs (tooling sync only; overwrites config files)
|
|
10
10
|
* pnpm exec fg-devkit sync-vscode-settings
|
|
11
|
+
* pnpm exec fg-devkit sync-consumer-scripts
|
|
12
|
+
* pnpm exec fg-devkit setup-lefthook
|
|
13
|
+
* pnpm exec fg-devkit prune-consumer-deps [--dry-run]
|
|
14
|
+
* pnpm exec fg-devkit full-consumer-setup
|
|
11
15
|
* pnpm exec fg-devkit create-release [patch|minor|major]
|
|
12
16
|
* pnpm exec fg-devkit commit [cz-args...] (Commitizen; uses fg-devkit bundled cz adapter)
|
|
13
17
|
* pnpm exec fg-devkit commit-vuln (staged package.json version bumps → vuln() commit)
|
|
@@ -76,6 +80,79 @@ function runReleaseScript(args = []) {
|
|
|
76
80
|
return r.status ?? 1
|
|
77
81
|
}
|
|
78
82
|
|
|
83
|
+
/** Run pnpm install in the consuming repo after setup steps. */
|
|
84
|
+
function runPnpmInstall() {
|
|
85
|
+
const r = spawnSync('pnpm', ['install'], {
|
|
86
|
+
stdio: 'inherit',
|
|
87
|
+
cwd: process.cwd(),
|
|
88
|
+
env: process.env,
|
|
89
|
+
})
|
|
90
|
+
if (r.error) {
|
|
91
|
+
console.error('[fg-devkit]', r.error.message)
|
|
92
|
+
return 1
|
|
93
|
+
}
|
|
94
|
+
return r.status ?? 1
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Run Lefthook from fg-devkit bundled dependency set. */
|
|
98
|
+
function runBundledLefthook(args = []) {
|
|
99
|
+
let lefthookBin
|
|
100
|
+
try {
|
|
101
|
+
lefthookBin = require.resolve('lefthook/bin/index.js')
|
|
102
|
+
} catch {
|
|
103
|
+
console.error('[fg-devkit] Could not resolve bundled lefthook. Reinstall fg-devkit and retry.')
|
|
104
|
+
return 1
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const r = spawnSync(process.execPath, [lefthookBin, ...args], {
|
|
108
|
+
stdio: 'inherit',
|
|
109
|
+
cwd: process.cwd(),
|
|
110
|
+
env: process.env,
|
|
111
|
+
})
|
|
112
|
+
if (r.error) {
|
|
113
|
+
console.error('[fg-devkit]', r.error.message)
|
|
114
|
+
return 1
|
|
115
|
+
}
|
|
116
|
+
return r.status ?? 1
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Setup lefthook config and install git hooks in the consumer repository. */
|
|
120
|
+
function runSetupLefthook() {
|
|
121
|
+
const syncCode = runNode('sync-consumer-lefthook.mjs')
|
|
122
|
+
if (syncCode !== 0) return syncCode
|
|
123
|
+
return runBundledLefthook(['install', '--reset-hooks-path'])
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Full consumer setup flow:
|
|
128
|
+
* - Step 7: remove unnecessary deps
|
|
129
|
+
* - Sync required consumer scripts for fgd commands
|
|
130
|
+
* - Setup lefthook and migrate from husky/lint-staged
|
|
131
|
+
* - Step 3: sync project configs
|
|
132
|
+
* - Step 2: sync dependency policy
|
|
133
|
+
* - Then run pnpm install
|
|
134
|
+
*/
|
|
135
|
+
function runFullConsumerSetup() {
|
|
136
|
+
console.log('[fg-devkit] Running full consumer sync and setup')
|
|
137
|
+
|
|
138
|
+
const step7 = runNode('prune-consumer-deps.mjs')
|
|
139
|
+
if (step7 !== 0) return step7
|
|
140
|
+
|
|
141
|
+
const scriptsStep = runNode('sync-consumer-scripts.mjs')
|
|
142
|
+
if (scriptsStep !== 0) return scriptsStep
|
|
143
|
+
|
|
144
|
+
const hooksStep = runSetupLefthook()
|
|
145
|
+
if (hooksStep !== 0) return hooksStep
|
|
146
|
+
|
|
147
|
+
const step3 = runNode('sync-project-configs.mjs')
|
|
148
|
+
if (step3 !== 0) return step3
|
|
149
|
+
|
|
150
|
+
const step2 = runNode('sync-dependency-policy.mjs')
|
|
151
|
+
if (step2 !== 0) return step2
|
|
152
|
+
|
|
153
|
+
return runPnpmInstall()
|
|
154
|
+
}
|
|
155
|
+
|
|
79
156
|
/** Prompt release type in CLI before invoking release.sh to avoid a "stuck" terminal feeling. */
|
|
80
157
|
async function promptReleaseType(rl) {
|
|
81
158
|
console.log('\nSelect release type:')
|
|
@@ -219,6 +296,9 @@ const DIRECT_COMMANDS = {
|
|
|
219
296
|
'sync-project-configs': 'sync-project-configs.mjs',
|
|
220
297
|
'sync-tooling-configs': 'sync-tooling-configs.mjs',
|
|
221
298
|
'sync-vscode-settings': 'sync-vscode-settings.mjs',
|
|
299
|
+
'sync-consumer-scripts': 'sync-consumer-scripts.mjs',
|
|
300
|
+
'setup-lefthook': 'setup-lefthook',
|
|
301
|
+
'prune-consumer-deps': 'prune-consumer-deps.mjs',
|
|
222
302
|
}
|
|
223
303
|
|
|
224
304
|
function printHelp() {
|
|
@@ -231,6 +311,10 @@ Commands:
|
|
|
231
311
|
sync-project-configs VS Code + tooling sync (overwrites config files)
|
|
232
312
|
sync-tooling-configs Tooling sync only (overwrites config files)
|
|
233
313
|
sync-vscode-settings Copy shared VS Code settings into .vscode/settings.json
|
|
314
|
+
sync-consumer-scripts Enforce standard fgd scripts in consumer package.json
|
|
315
|
+
setup-lefthook Setup lefthook and migrate from husky/lint-staged
|
|
316
|
+
prune-consumer-deps [--dry-run] Remove consumer tooling deps bundled by fg-devkit
|
|
317
|
+
full-consumer-setup Run: step 7 -> scripts -> lefthook -> step 3 -> step 2 -> pnpm install
|
|
234
318
|
create-release [bump] Version bump, git push, GitHub release (patch|minor|major; omit = prompt)
|
|
235
319
|
(alias: createRelease)
|
|
236
320
|
commit [args...] Commitizen (git-cz) with fg-devkit bundled adapter
|
|
@@ -280,6 +364,21 @@ const MENU = [
|
|
|
280
364
|
label: 'Commit vulnerability (Commitizen)',
|
|
281
365
|
run: () => runCommitizen([], { FG_DEVKIT_CZ_DEFAULT_TYPE: 'vuln' }),
|
|
282
366
|
},
|
|
367
|
+
{
|
|
368
|
+
key: '7',
|
|
369
|
+
label: 'Remove unnecessary deps in package.json',
|
|
370
|
+
run: () => runNode('prune-consumer-deps.mjs'),
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
key: '8',
|
|
374
|
+
label: 'Full consumer setup (7 -> scripts -> lefthook -> 3 -> 2 -> pnpm install)',
|
|
375
|
+
run: () => runFullConsumerSetup(),
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
key: '9',
|
|
379
|
+
label: 'Setup lefthook (migrate from husky/lint-staged)',
|
|
380
|
+
run: () => runSetupLefthook(),
|
|
381
|
+
},
|
|
283
382
|
]
|
|
284
383
|
|
|
285
384
|
async function runInteractiveMenu() {
|
|
@@ -340,6 +439,14 @@ async function main() {
|
|
|
340
439
|
}
|
|
341
440
|
|
|
342
441
|
if (sub) {
|
|
442
|
+
if (sub === 'setup-lefthook') {
|
|
443
|
+
process.exitCode = runSetupLefthook()
|
|
444
|
+
return
|
|
445
|
+
}
|
|
446
|
+
if (sub === 'full-consumer-setup') {
|
|
447
|
+
process.exitCode = runFullConsumerSetup()
|
|
448
|
+
return
|
|
449
|
+
}
|
|
343
450
|
if (sub === 'create-release' || sub === 'createRelease') {
|
|
344
451
|
process.exitCode = runReleaseScript(rest)
|
|
345
452
|
return
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Removes tooling dependencies from a consuming repo that are now bundled by fg-devkit.
|
|
4
|
+
*
|
|
5
|
+
* By default this script edits package.json in the current working directory and removes
|
|
6
|
+
* known ESLint/Prettier/TypeScript/Commitizen packages from dependencies/devDependencies.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* node node_modules/fg-devkit/scripts/prune-consumer-deps.mjs
|
|
10
|
+
* pnpm exec fg-devkit prune-consumer-deps
|
|
11
|
+
* pnpm exec fg-devkit prune-consumer-deps --dry-run
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import fs from 'node:fs'
|
|
15
|
+
import path from 'node:path'
|
|
16
|
+
|
|
17
|
+
const BUNDLED_TOOLING_PACKAGES = new Set([
|
|
18
|
+
'@intlify/eslint-plugin-vue-i18n',
|
|
19
|
+
'@typescript-eslint/eslint-plugin',
|
|
20
|
+
'@typescript-eslint/parser',
|
|
21
|
+
'@vitest/eslint-plugin',
|
|
22
|
+
'@vue/eslint-config-prettier',
|
|
23
|
+
'@vue/eslint-config-typescript',
|
|
24
|
+
'@vue/tsconfig',
|
|
25
|
+
'@rushstack/eslint-patch',
|
|
26
|
+
'commitizen',
|
|
27
|
+
'eslint',
|
|
28
|
+
'eslint-plugin-prettier',
|
|
29
|
+
'eslint-plugin-vue',
|
|
30
|
+
'husky',
|
|
31
|
+
'jsonc-eslint-parser',
|
|
32
|
+
'lint-staged',
|
|
33
|
+
'lefthook',
|
|
34
|
+
'prettier',
|
|
35
|
+
'typescript',
|
|
36
|
+
'yaml-eslint-parser',
|
|
37
|
+
'vue-eslint-parser',
|
|
38
|
+
'vue-tsc',
|
|
39
|
+
])
|
|
40
|
+
|
|
41
|
+
function readPackageJson(packageJsonPath) {
|
|
42
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
43
|
+
console.error(`[prune-consumer-deps] No package.json found at ${packageJsonPath}`)
|
|
44
|
+
process.exit(1)
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
return JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
|
|
48
|
+
} catch (err) {
|
|
49
|
+
console.error(`[prune-consumer-deps] Failed to parse package.json: ${err.message}`)
|
|
50
|
+
process.exit(1)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function removeFromSection(pkg, sectionName, removed) {
|
|
55
|
+
const section = pkg[sectionName]
|
|
56
|
+
if (!section || typeof section !== 'object') {
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
for (const name of Object.keys(section)) {
|
|
60
|
+
if (BUNDLED_TOOLING_PACKAGES.has(name)) {
|
|
61
|
+
removed.push(`${sectionName}.${name}@${section[name]}`)
|
|
62
|
+
delete section[name]
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (Object.keys(section).length === 0) {
|
|
66
|
+
delete pkg[sectionName]
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function main() {
|
|
71
|
+
const dryRun = process.argv.includes('--dry-run')
|
|
72
|
+
const projectRoot = process.cwd()
|
|
73
|
+
const packageJsonPath = path.join(projectRoot, 'package.json')
|
|
74
|
+
const pkg = readPackageJson(packageJsonPath)
|
|
75
|
+
|
|
76
|
+
const removed = []
|
|
77
|
+
removeFromSection(pkg, 'dependencies', removed)
|
|
78
|
+
removeFromSection(pkg, 'devDependencies', removed)
|
|
79
|
+
|
|
80
|
+
if (removed.length === 0) {
|
|
81
|
+
console.log('[prune-consumer-deps] No bundled tooling dependencies found to remove.')
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
console.log('[prune-consumer-deps] Removed entries:')
|
|
86
|
+
for (const line of removed) {
|
|
87
|
+
console.log(` - ${line}`)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (dryRun) {
|
|
91
|
+
console.log('\n[prune-consumer-deps] Dry run only; package.json was not modified.')
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
fs.writeFileSync(packageJsonPath, `${JSON.stringify(pkg, null, 2)}\n`, 'utf8')
|
|
96
|
+
console.log('\n[prune-consumer-deps] Updated package.json. Run pnpm install to refresh lockfile.')
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
main()
|
|
@@ -29,6 +29,7 @@ import { collectPinViolations } from './dependency-policy-pins.mjs'
|
|
|
29
29
|
|
|
30
30
|
const scriptDir = path.dirname(fileURLToPath(import.meta.url))
|
|
31
31
|
const fgDevkitRoot = path.resolve(scriptDir, '..')
|
|
32
|
+
const fgDevkitPackageJsonPath = path.join(fgDevkitRoot, 'package.json')
|
|
32
33
|
|
|
33
34
|
const projectsRoot = process.argv[2]
|
|
34
35
|
? path.resolve(process.cwd(), process.argv[2])
|
|
@@ -41,6 +42,15 @@ const policyPath = process.argv[3]
|
|
|
41
42
|
/** Relative paths under projectsRoot whose package.json files are ignored (maintainer choice). */
|
|
42
43
|
/** Relative path prefixes under projectsRoot to skip when scanning (add repo names as needed). */
|
|
43
44
|
const SKIP_DIR_PREFIXES = []
|
|
45
|
+
const EXTRA_TOOLING_EXCLUDES = new Set(['husky', 'lint-staged', '@rushstack/eslint-patch', 'jsonc-eslint-parser', 'yaml-eslint-parser'])
|
|
46
|
+
const EXCLUDED_POLICY_PACKAGES = (() => {
|
|
47
|
+
try {
|
|
48
|
+
const pkg = JSON.parse(fs.readFileSync(fgDevkitPackageJsonPath, 'utf8'))
|
|
49
|
+
return new Set([...Object.keys(pkg.dependencies ?? {}), ...EXTRA_TOOLING_EXCLUDES])
|
|
50
|
+
} catch {
|
|
51
|
+
return EXTRA_TOOLING_EXCLUDES
|
|
52
|
+
}
|
|
53
|
+
})()
|
|
44
54
|
|
|
45
55
|
/** Recursively collect every package.json path under dir, skipping node_modules and .git. */
|
|
46
56
|
function walk(dir, acc = []) {
|
|
@@ -147,6 +157,9 @@ const outAny = {}
|
|
|
147
157
|
|
|
148
158
|
// Pick highest semver per package and assign to dependencies, devDependencies, or managedAnywhere.
|
|
149
159
|
for (const [name, u] of usage) {
|
|
160
|
+
if (EXCLUDED_POLICY_PACKAGES.has(name)) {
|
|
161
|
+
continue
|
|
162
|
+
}
|
|
150
163
|
if (u.partsList.length === 0) {
|
|
151
164
|
continue
|
|
152
165
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Syncs Lefthook setup into a consuming repo:
|
|
4
|
+
* - writes lefthook.yml from fg-devkit template
|
|
5
|
+
* - removes legacy .husky directory (if present)
|
|
6
|
+
* - removes legacy lint-staged config block from package.json
|
|
7
|
+
* - removes legacy prepare hook scripts for husky/lefthook bootstrap
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* node node_modules/fg-devkit/scripts/sync-consumer-lefthook.mjs
|
|
11
|
+
* pnpm exec fg-devkit setup-lefthook
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import fs from 'node:fs'
|
|
15
|
+
import path from 'node:path'
|
|
16
|
+
import { fileURLToPath } from 'node:url'
|
|
17
|
+
|
|
18
|
+
const projectRoot = process.cwd()
|
|
19
|
+
const scriptDir = path.dirname(fileURLToPath(import.meta.url))
|
|
20
|
+
const fgRoot = path.resolve(scriptDir, '..')
|
|
21
|
+
|
|
22
|
+
const lefthookTemplate = path.join(fgRoot, 'configs', 'lefthook.yml')
|
|
23
|
+
const lefthookTarget = path.join(projectRoot, 'lefthook.yml')
|
|
24
|
+
const huskyDir = path.join(projectRoot, '.husky')
|
|
25
|
+
const packageJsonPath = path.join(projectRoot, 'package.json')
|
|
26
|
+
|
|
27
|
+
if (!fs.existsSync(lefthookTemplate)) {
|
|
28
|
+
console.error(`[sync-consumer-lefthook] Missing template: ${lefthookTemplate}`)
|
|
29
|
+
process.exit(1)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
33
|
+
console.error(`[sync-consumer-lefthook] package.json not found at ${packageJsonPath}`)
|
|
34
|
+
process.exit(1)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let pkg
|
|
38
|
+
try {
|
|
39
|
+
pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
|
|
40
|
+
} catch (err) {
|
|
41
|
+
console.error(`[sync-consumer-lefthook] Failed to parse package.json: ${err.message}`)
|
|
42
|
+
process.exit(1)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
pkg.scripts = pkg.scripts && typeof pkg.scripts === 'object' ? pkg.scripts : {}
|
|
46
|
+
if (
|
|
47
|
+
pkg.scripts.prepare &&
|
|
48
|
+
(pkg.scripts.prepare.includes('husky') || pkg.scripts.prepare.includes('setup-lefthook'))
|
|
49
|
+
) {
|
|
50
|
+
delete pkg.scripts.prepare
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (pkg['lint-staged'] !== undefined) {
|
|
54
|
+
delete pkg['lint-staged']
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
fs.copyFileSync(lefthookTemplate, lefthookTarget)
|
|
59
|
+
fs.writeFileSync(packageJsonPath, `${JSON.stringify(pkg, null, 2)}\n`, 'utf8')
|
|
60
|
+
if (fs.existsSync(huskyDir)) {
|
|
61
|
+
fs.rmSync(huskyDir, { recursive: true, force: true })
|
|
62
|
+
}
|
|
63
|
+
console.log('[sync-consumer-lefthook] Synced lefthook.yml and removed .husky/lint-staged legacy setup.')
|
|
64
|
+
} catch (err) {
|
|
65
|
+
console.error(`[sync-consumer-lefthook] ${err.message}`)
|
|
66
|
+
process.exit(1)
|
|
67
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Syncs standard fg-devkit npm scripts into a consuming repo package.json.
|
|
4
|
+
*
|
|
5
|
+
* This updates only known script keys and leaves all other scripts untouched.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* node node_modules/fg-devkit/scripts/sync-consumer-scripts.mjs
|
|
9
|
+
* pnpm exec fg-devkit sync-consumer-scripts
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import fs from 'node:fs'
|
|
13
|
+
import path from 'node:path'
|
|
14
|
+
|
|
15
|
+
const REQUIRED_SCRIPTS = {
|
|
16
|
+
'lint:ts': 'pnpm exec fgd typecheck',
|
|
17
|
+
lint: 'pnpm exec fgd lint --fix .',
|
|
18
|
+
'lint-check': 'pnpm exec fgd lint .',
|
|
19
|
+
'sync:vscode:settings': 'pnpm exec fgd sync-vscode-settings',
|
|
20
|
+
'create:release': 'pnpm exec fgd create-release',
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function main() {
|
|
24
|
+
const packageJsonPath = path.join(process.cwd(), 'package.json')
|
|
25
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
26
|
+
console.error(`[sync-consumer-scripts] package.json not found at ${packageJsonPath}`)
|
|
27
|
+
process.exit(1)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let pkg
|
|
31
|
+
try {
|
|
32
|
+
pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
|
|
33
|
+
} catch (err) {
|
|
34
|
+
console.error(`[sync-consumer-scripts] Failed to parse package.json: ${err.message}`)
|
|
35
|
+
process.exit(1)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
pkg.scripts = pkg.scripts && typeof pkg.scripts === 'object' ? pkg.scripts : {}
|
|
39
|
+
|
|
40
|
+
const changed = []
|
|
41
|
+
for (const [name, value] of Object.entries(REQUIRED_SCRIPTS)) {
|
|
42
|
+
if (pkg.scripts[name] !== value) {
|
|
43
|
+
pkg.scripts[name] = value
|
|
44
|
+
changed.push(name)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
fs.writeFileSync(packageJsonPath, `${JSON.stringify(pkg, null, 2)}\n`, 'utf8')
|
|
49
|
+
|
|
50
|
+
if (changed.length === 0) {
|
|
51
|
+
console.log('[sync-consumer-scripts] package.json scripts already up to date.')
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
console.log('[sync-consumer-scripts] Updated scripts:')
|
|
56
|
+
for (const name of changed) {
|
|
57
|
+
console.log(` - ${name}`)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
main()
|