fg-devkit 1.5.2 → 1.6.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/configs/dependency-policy.vue-workspace.json +6 -6
- package/package.json +9 -1
- package/scaffold/vue/.browserslistrc +3 -0
- package/scaffold/vue/.editorconfig +5 -0
- package/scaffold/vue/Dockerfile +18 -0
- package/scaffold/vue/Dockerfile.local +16 -0
- package/scaffold/vue/Jenkinsfile +130 -0
- package/scaffold/vue/LICENSE +19 -0
- package/scaffold/vue/env-adjustment.sh +19 -0
- package/scaffold/vue/index.html +16 -0
- package/scaffold/vue/nginx/default.conf +23 -0
- package/scaffold/vue/nginx/gzip.conf +10 -0
- package/scaffold/vue/nginx/security.conf +6 -0
- package/scaffold/vue/package.json +44 -0
- package/scaffold/vue/poeditor/.poeditor.json +8 -0
- package/scaffold/vue/public/config.json +8 -0
- package/scaffold/vue/src/App.vue +10 -0
- package/scaffold/vue/src/api/client.ts +114 -0
- package/scaffold/vue/src/api/runtimeConfig.ts +10 -0
- package/scaffold/vue/src/assets/.gitkeep +0 -0
- package/scaffold/vue/src/components/.gitkeep +0 -0
- package/scaffold/vue/src/composables/.gitkeep +0 -0
- package/scaffold/vue/src/config/env.ts +45 -0
- package/scaffold/vue/src/i18n.ts +11 -0
- package/scaffold/vue/src/lang/sv.json +1 -0
- package/scaffold/vue/src/main.ts +29 -0
- package/scaffold/vue/src/queries/.gitkeep +0 -0
- package/scaffold/vue/src/router/index.ts +22 -0
- package/scaffold/vue/src/style.css +7 -0
- package/scaffold/vue/src/types/.gitkeep +0 -0
- package/scaffold/vue/src/views/HomeView.vue +10 -0
- package/scaffold/vue/src/vite-env.d.ts +1 -0
- package/scaffold/vue/start-container.sh +15 -0
- package/scaffold/vue/tailwind.config.js +21 -0
- package/scaffold/vue/vite.config.ts +41 -0
- package/scripts/fg-devkit.mjs +28 -3
- package/scripts/fleet-bump-fg-devkit.mjs +135 -0
- package/scripts/poeditor-common.mjs +118 -0
- package/scripts/poeditor-download.mjs +101 -0
- package/scripts/poeditor-upload.mjs +58 -0
- package/scripts/policy-upgrade.mjs +3 -0
- package/scripts/refresh-dependency-policy-from-repos.mjs +7 -0
- package/scripts/scaffold-vue.mjs +183 -0
- package/scripts/sync-scaffold-vue-deps.mjs +68 -0
- package/tsconfig.json +13 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { createRouter, createWebHistory } from 'vue-router'
|
|
2
|
+
|
|
3
|
+
const routes = [
|
|
4
|
+
{
|
|
5
|
+
path: '/',
|
|
6
|
+
name: 'home',
|
|
7
|
+
meta: { title: 'Home' },
|
|
8
|
+
component: () => import('@/views/HomeView.vue'),
|
|
9
|
+
},
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
const router = createRouter({
|
|
13
|
+
history: createWebHistory(import.meta.env.BASE_URL),
|
|
14
|
+
routes,
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
router.afterEach((to) => {
|
|
18
|
+
const title = typeof to.meta.title === 'string' ? to.meta.title : 'App'
|
|
19
|
+
document.title = `${title} | PLACEHOLDER_APP_NAME`
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
export default router
|
|
File without changes
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import env from '@/config/env'
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<template>
|
|
6
|
+
<main class="p-6">
|
|
7
|
+
<h1 class="text-xl font-bold">PLACEHOLDER_APP_NAME</h1>
|
|
8
|
+
<p class="mt-2 text-sm text-gray-700">Environment: {{ env.environment }}</p>
|
|
9
|
+
</main>
|
|
10
|
+
</template>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** @type {import('tailwindcss').Config} */
|
|
2
|
+
import preset from 'fds-vue-core/tailwind.preset.cjs'
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
presets: [preset],
|
|
6
|
+
content: [
|
|
7
|
+
'./index.html',
|
|
8
|
+
'./src/**/*.{vue,js,ts,jsx,tsx}',
|
|
9
|
+
],
|
|
10
|
+
safelist: [
|
|
11
|
+
'max-w-screen-xs',
|
|
12
|
+
'max-w-screen-sm',
|
|
13
|
+
'max-w-screen-md',
|
|
14
|
+
'max-w-screen-lg',
|
|
15
|
+
'max-w-screen-xl',
|
|
16
|
+
'max-w-screen-2xl',
|
|
17
|
+
'max-w-screen-3xl',
|
|
18
|
+
'max-w-screen-4xl',
|
|
19
|
+
],
|
|
20
|
+
plugins: [],
|
|
21
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { defineConfig } from 'vite'
|
|
2
|
+
import path, { dirname, resolve } from 'node:path'
|
|
3
|
+
import vue from '@vitejs/plugin-vue'
|
|
4
|
+
import { fileURLToPath } from 'node:url'
|
|
5
|
+
import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite'
|
|
6
|
+
import tailwindcss from '@tailwindcss/vite'
|
|
7
|
+
|
|
8
|
+
export default defineConfig({
|
|
9
|
+
plugins: [
|
|
10
|
+
vue({
|
|
11
|
+
template: {
|
|
12
|
+
compilerOptions: {
|
|
13
|
+
isCustomElement: (tag: string) => tag.startsWith('fds-'),
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
}),
|
|
17
|
+
VueI18nPlugin({
|
|
18
|
+
include: resolve(dirname(fileURLToPath(import.meta.url)), './src/lang/**'),
|
|
19
|
+
strictMessage: false,
|
|
20
|
+
}),
|
|
21
|
+
tailwindcss(),
|
|
22
|
+
],
|
|
23
|
+
resolve: {
|
|
24
|
+
alias: {
|
|
25
|
+
'@': path.resolve(__dirname, 'src'),
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
server: {
|
|
29
|
+
port: 5173,
|
|
30
|
+
strictPort: true,
|
|
31
|
+
watch: {
|
|
32
|
+
usePolling: true,
|
|
33
|
+
},
|
|
34
|
+
fs: {
|
|
35
|
+
allow: ['..'],
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
css: {
|
|
39
|
+
devSourcemap: true,
|
|
40
|
+
},
|
|
41
|
+
})
|
package/scripts/fg-devkit.mjs
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* pnpm exec fg-devkit policy-vulns [policy-file]
|
|
10
10
|
* pnpm exec fg-devkit repo-vulns
|
|
11
11
|
* pnpm exec fg-devkit fleet-vulns [projects-root] [--summary-only] [--delay-ms=<ms>]
|
|
12
|
+
* pnpm exec fg-devkit fleet-bump-fg-devkit [projects-root] [--dry-run]
|
|
12
13
|
* pnpm exec fg-devkit policy-upgrade [policy-file] [ncu-args...]
|
|
13
14
|
* pnpm exec fg-devkit sync-project-configs (VS Code + tooling sync; overwrites config files)
|
|
14
15
|
* pnpm exec fg-devkit sync-tooling-configs (tooling sync only; overwrites config files)
|
|
@@ -26,11 +27,14 @@
|
|
|
26
27
|
* pnpm exec fg-devkit format-check [prettier-args...] (same as format plus --check)
|
|
27
28
|
* pnpm exec fg-devkit typecheck [vue-tsc-args...] (run vue-tsc with fg-devkit bundled package)
|
|
28
29
|
* pnpm exec fg-devkit typecheck-watch [vue-tsc-args...] (same as typecheck plus --watch)
|
|
30
|
+
* pnpm exec fg-devkit poeditor-download [args...] (download translations using poeditor/.poeditor.json + env token)
|
|
31
|
+
* pnpm exec fg-devkit poeditor-upload [args...] (upload translations using poeditor/.poeditor.json + env token)
|
|
29
32
|
* Interactive menu (no subcommand):
|
|
30
33
|
* pnpm exec fg-devkit
|
|
31
34
|
*
|
|
32
35
|
* Maintainer-only policy regeneration is not exposed here; run
|
|
33
|
-
* node node_modules/fg-devkit/scripts/refresh-dependency-policy-from-repos.mjs from a checkout when needed
|
|
36
|
+
* node node_modules/fg-devkit/scripts/refresh-dependency-policy-from-repos.mjs from a checkout when needed
|
|
37
|
+
* (that script also refreshes scaffold/vue/package.json versions from the policy).
|
|
34
38
|
*/
|
|
35
39
|
|
|
36
40
|
import { spawnSync } from 'node:child_process'
|
|
@@ -250,7 +254,7 @@ function runMaintainerPolicyUpgradeAndCommit() {
|
|
|
250
254
|
}
|
|
251
255
|
|
|
252
256
|
const [only] = changed
|
|
253
|
-
const stageCode = stageFiles(['configs/dependency-policy.vue-workspace.json'])
|
|
257
|
+
const stageCode = stageFiles(['configs/dependency-policy.vue-workspace.json', 'scaffold/vue/package.json'])
|
|
254
258
|
if (stageCode !== 0) return stageCode
|
|
255
259
|
|
|
256
260
|
const commitMessage = `dep(${only.name}): bump to ${only.to}`
|
|
@@ -408,6 +412,7 @@ const DIRECT_COMMANDS = {
|
|
|
408
412
|
'policy-vulns': 'policy-vulns.mjs',
|
|
409
413
|
'repo-vulns': 'repo-vulns.mjs',
|
|
410
414
|
'fleet-vulns': 'fleet-vulns.mjs',
|
|
415
|
+
'fleet-bump-fg-devkit': 'fleet-bump-fg-devkit.mjs',
|
|
411
416
|
'policy-upgrade': 'policy-upgrade.mjs',
|
|
412
417
|
'sync-project-configs': 'sync-project-configs.mjs',
|
|
413
418
|
'sync-tooling-configs': 'sync-tooling-configs.mjs',
|
|
@@ -415,6 +420,9 @@ const DIRECT_COMMANDS = {
|
|
|
415
420
|
'sync-consumer-scripts': 'sync-consumer-scripts.mjs',
|
|
416
421
|
'setup-lefthook': 'setup-lefthook',
|
|
417
422
|
'prune-consumer-deps': 'prune-consumer-deps.mjs',
|
|
423
|
+
'sync-scaffold-vue-deps': 'sync-scaffold-vue-deps.mjs',
|
|
424
|
+
'poeditor-download': 'poeditor-download.mjs',
|
|
425
|
+
'poeditor-upload': 'poeditor-upload.mjs',
|
|
418
426
|
}
|
|
419
427
|
|
|
420
428
|
function printHelp() {
|
|
@@ -429,6 +437,8 @@ Commands:
|
|
|
429
437
|
repo-vulns Check all repo dependencies for vulnerabilities via pnpm audit
|
|
430
438
|
fleet-vulns [projects-root] [--summary-only] [--delay-ms=<ms>]
|
|
431
439
|
Run pnpm audit across Vue repos (full output + combined summary)
|
|
440
|
+
fleet-bump-fg-devkit [projects-root] [--dry-run]
|
|
441
|
+
Bump fg-devkit to latest across Vue repos, then run pnpm install
|
|
432
442
|
policy-upgrade [policy-file] [ncu-args...]
|
|
433
443
|
Upgrade policy packages via npm-check-updates (default interactive)
|
|
434
444
|
sync-project-configs VS Code + tooling sync (overwrites config files)
|
|
@@ -448,6 +458,12 @@ Commands:
|
|
|
448
458
|
format-check [prettier-args...] Run Prettier with --check using fg-devkit bundled package
|
|
449
459
|
typecheck [vue-tsc-args...] Run vue-tsc using fg-devkit bundled package
|
|
450
460
|
typecheck-watch [vue-tsc-args...] Run vue-tsc with --watch using fg-devkit bundled package
|
|
461
|
+
poeditor-download [args...] Download locale files via PoEditor (reads poeditor/.poeditor.json by default)
|
|
462
|
+
poeditor-upload [args...] Upload locale file via PoEditor (reads poeditor/.poeditor.json by default)
|
|
463
|
+
scaffold-vue <target-dir> [--name …] [--slug …] [--force] …
|
|
464
|
+
Copy the bundled Vue scaffold into target-dir (see script header)
|
|
465
|
+
sync-scaffold-vue-deps [policy-file]
|
|
466
|
+
Align scaffold/vue/package.json versions to the dependency policy
|
|
451
467
|
|
|
452
468
|
With no command, opens an interactive menu.
|
|
453
469
|
|
|
@@ -517,7 +533,7 @@ const MAINTAINER_MENU = [
|
|
|
517
533
|
},
|
|
518
534
|
{
|
|
519
535
|
key: 'm3',
|
|
520
|
-
label: 'Maintainer: Refresh dependency policy from repos',
|
|
536
|
+
label: 'Maintainer: Refresh dependency policy from repos (and scaffold/vue package.json)',
|
|
521
537
|
run: () => runNode('refresh-dependency-policy-from-repos.mjs'),
|
|
522
538
|
},
|
|
523
539
|
{
|
|
@@ -540,6 +556,11 @@ const MAINTAINER_MENU = [
|
|
|
540
556
|
label: 'Maintainer: Check vulnerabilities across all Vue repos',
|
|
541
557
|
run: () => runNode('fleet-vulns.mjs'),
|
|
542
558
|
},
|
|
559
|
+
{
|
|
560
|
+
key: 'm8',
|
|
561
|
+
label: 'Maintainer: Bump fg-devkit in all Vue repos and run pnpm install',
|
|
562
|
+
run: () => runNode('fleet-bump-fg-devkit.mjs'),
|
|
563
|
+
},
|
|
543
564
|
]
|
|
544
565
|
|
|
545
566
|
function isFgDevkitMaintainerContext() {
|
|
@@ -638,6 +659,10 @@ async function main() {
|
|
|
638
659
|
process.exitCode = runNode('vuln-commit.mjs', rest)
|
|
639
660
|
return
|
|
640
661
|
}
|
|
662
|
+
if (sub === 'scaffold-vue') {
|
|
663
|
+
process.exitCode = runNode('scaffold-vue.mjs', rest)
|
|
664
|
+
return
|
|
665
|
+
}
|
|
641
666
|
if (sub === 'lint') {
|
|
642
667
|
process.exitCode = runBundledEslint(rest.length > 0 ? rest : ['.'])
|
|
643
668
|
return
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Bumps fg-devkit to latest in Vue repositories and runs pnpm install.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* pnpm exec fg-devkit fleet-bump-fg-devkit [projects-root] [--dry-run]
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import fs from 'node:fs'
|
|
10
|
+
import path from 'node:path'
|
|
11
|
+
import { spawnSync } from 'node:child_process'
|
|
12
|
+
import { fileURLToPath } from 'node:url'
|
|
13
|
+
|
|
14
|
+
const scriptDir = path.dirname(fileURLToPath(import.meta.url))
|
|
15
|
+
const cliArgs = process.argv.slice(2)
|
|
16
|
+
const dryRun = cliArgs.includes('--dry-run')
|
|
17
|
+
const projectsRootArg = cliArgs.find((a) => !a.startsWith('-'))
|
|
18
|
+
const projectsRoot = projectsRootArg ? path.resolve(process.cwd(), projectsRootArg) : path.resolve(scriptDir, '../..')
|
|
19
|
+
|
|
20
|
+
function walk(dir, acc = []) {
|
|
21
|
+
for (const item of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
22
|
+
if (item.name === 'node_modules' || item.name === '.git') continue
|
|
23
|
+
const p = path.join(dir, item.name)
|
|
24
|
+
if (item.isDirectory()) walk(p, acc)
|
|
25
|
+
else if (item.name === 'package.json') acc.push(p)
|
|
26
|
+
}
|
|
27
|
+
return acc
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function readJson(p) {
|
|
31
|
+
return JSON.parse(fs.readFileSync(p, 'utf8'))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function isVueProject(pkg) {
|
|
35
|
+
return Boolean(pkg.dependencies?.vue || pkg.devDependencies?.vue)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function hasFgDevkit(pkg) {
|
|
39
|
+
return Boolean(pkg.dependencies?.['fg-devkit'] || pkg.devDependencies?.['fg-devkit'])
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function isFgDevkitRepo(pkg) {
|
|
43
|
+
return pkg?.name === 'fg-devkit'
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const packageJsonFiles = walk(projectsRoot)
|
|
47
|
+
const repos = []
|
|
48
|
+
for (const packageJsonPath of packageJsonFiles) {
|
|
49
|
+
try {
|
|
50
|
+
const pkg = readJson(packageJsonPath)
|
|
51
|
+
if (!isVueProject(pkg)) continue
|
|
52
|
+
if (isFgDevkitRepo(pkg)) continue
|
|
53
|
+
if (!hasFgDevkit(pkg)) continue
|
|
54
|
+
repos.push(path.dirname(packageJsonPath))
|
|
55
|
+
} catch {
|
|
56
|
+
// Ignore invalid package.json files.
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const uniqueRepos = [...new Set(repos)].sort((a, b) => a.localeCompare(b))
|
|
61
|
+
if (uniqueRepos.length === 0) {
|
|
62
|
+
console.log(`[fleet-bump-fg-devkit] No Vue repositories with fg-devkit found under ${projectsRoot}`)
|
|
63
|
+
process.exit(0)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
console.log(`[fleet-bump-fg-devkit] projectsRoot=${projectsRoot}`)
|
|
67
|
+
console.log(`[fleet-bump-fg-devkit] repositories=${uniqueRepos.length}`)
|
|
68
|
+
|
|
69
|
+
let failures = 0
|
|
70
|
+
let updated = 0
|
|
71
|
+
|
|
72
|
+
for (const repoDir of uniqueRepos) {
|
|
73
|
+
const repoName = path.basename(repoDir)
|
|
74
|
+
const bumpArgs = ['up', 'fg-devkit@latest']
|
|
75
|
+
const installArgs = ['install']
|
|
76
|
+
|
|
77
|
+
if (dryRun) {
|
|
78
|
+
console.log(`[fleet-bump-fg-devkit] DRY RUN ${repoName}: pnpm ${bumpArgs.join(' ')}`)
|
|
79
|
+
console.log(`[fleet-bump-fg-devkit] DRY RUN ${repoName}: pnpm ${installArgs.join(' ')}`)
|
|
80
|
+
continue
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
console.log(`\n[fleet-bump-fg-devkit] Updating ${repoName}`)
|
|
84
|
+
const bump = spawnSync('pnpm', bumpArgs, {
|
|
85
|
+
cwd: repoDir,
|
|
86
|
+
stdio: 'inherit',
|
|
87
|
+
env: {
|
|
88
|
+
...process.env,
|
|
89
|
+
npm_config_save_prefix: '',
|
|
90
|
+
npm_config_save_exact: 'true',
|
|
91
|
+
},
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
if (bump.error) {
|
|
95
|
+
failures += 1
|
|
96
|
+
console.error(`[fleet-bump-fg-devkit] FAIL ${repoName}: ${bump.error.message}`)
|
|
97
|
+
continue
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const bumpStatus = bump.status ?? 1
|
|
101
|
+
if (bumpStatus !== 0) {
|
|
102
|
+
failures += 1
|
|
103
|
+
console.error(`[fleet-bump-fg-devkit] FAIL ${repoName}: pnpm up exited with code ${bumpStatus}`)
|
|
104
|
+
continue
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const install = spawnSync('pnpm', installArgs, {
|
|
108
|
+
cwd: repoDir,
|
|
109
|
+
stdio: 'inherit',
|
|
110
|
+
env: {
|
|
111
|
+
...process.env,
|
|
112
|
+
npm_config_save_prefix: '',
|
|
113
|
+
npm_config_save_exact: 'true',
|
|
114
|
+
},
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
if (install.error) {
|
|
118
|
+
failures += 1
|
|
119
|
+
console.error(`[fleet-bump-fg-devkit] FAIL ${repoName}: ${install.error.message}`)
|
|
120
|
+
continue
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const status = install.status ?? 1
|
|
124
|
+
if (status !== 0) {
|
|
125
|
+
failures += 1
|
|
126
|
+
console.error(`[fleet-bump-fg-devkit] FAIL ${repoName}: pnpm install exited with code ${status}`)
|
|
127
|
+
continue
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
updated += 1
|
|
131
|
+
console.log(`[fleet-bump-fg-devkit] OK ${repoName}`)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
console.log(`\n[fleet-bump-fg-devkit] Done: updated=${updated}, failed=${failures}`)
|
|
135
|
+
process.exit(failures > 0 ? 1 : 0)
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
|
|
4
|
+
function readJsonIfExists(filePath) {
|
|
5
|
+
if (!fs.existsSync(filePath)) return null
|
|
6
|
+
try {
|
|
7
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'))
|
|
8
|
+
} catch {
|
|
9
|
+
return null
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function resolveFromCwd(p) {
|
|
14
|
+
return path.isAbsolute(p) ? p : path.resolve(process.cwd(), p)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function resolveLegacyConfig(raw) {
|
|
18
|
+
// Legacy format: { "<projectId>": { "path": "./src/lang/" } }
|
|
19
|
+
const [firstKey] = Object.keys(raw ?? {})
|
|
20
|
+
if (!firstKey) return null
|
|
21
|
+
const value = raw[firstKey]
|
|
22
|
+
if (!value || typeof value !== 'object') return null
|
|
23
|
+
return {
|
|
24
|
+
projectId: firstKey,
|
|
25
|
+
downloadPath: typeof value.path === 'string' ? value.path : './src/lang/',
|
|
26
|
+
languages: Array.isArray(value.languages) ? value.languages.filter((x) => typeof x === 'string' && x.trim() !== '') : undefined,
|
|
27
|
+
defaultLanguage: typeof value.defaultLanguage === 'string' ? value.defaultLanguage : 'sv',
|
|
28
|
+
defaultFormat: typeof value.defaultFormat === 'string' ? value.defaultFormat : 'json',
|
|
29
|
+
uploadFile: typeof value.uploadFile === 'string' ? value.uploadFile : './src/lang/sv.json',
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function resolveObjectConfig(raw) {
|
|
34
|
+
if (!raw || typeof raw !== 'object') return null
|
|
35
|
+
if (typeof raw.projectId !== 'string') return null
|
|
36
|
+
return {
|
|
37
|
+
projectId: raw.projectId,
|
|
38
|
+
downloadPath: typeof raw.downloadPath === 'string' ? raw.downloadPath : './src/lang/',
|
|
39
|
+
languages: Array.isArray(raw.languages) ? raw.languages.filter((x) => typeof x === 'string' && x.trim() !== '') : undefined,
|
|
40
|
+
defaultLanguage: typeof raw.defaultLanguage === 'string' ? raw.defaultLanguage : 'sv',
|
|
41
|
+
defaultFormat: typeof raw.defaultFormat === 'string' ? raw.defaultFormat : 'json',
|
|
42
|
+
uploadFile: typeof raw.uploadFile === 'string' ? raw.uploadFile : './src/lang/sv.json',
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function parseFlags(argv) {
|
|
47
|
+
const positional = []
|
|
48
|
+
const flags = new Map()
|
|
49
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
50
|
+
const a = argv[i]
|
|
51
|
+
if (!a.startsWith('--')) {
|
|
52
|
+
positional.push(a)
|
|
53
|
+
continue
|
|
54
|
+
}
|
|
55
|
+
const key = a.slice(2)
|
|
56
|
+
const next = argv[i + 1]
|
|
57
|
+
if (next && !next.startsWith('--')) {
|
|
58
|
+
if (flags.has(key)) {
|
|
59
|
+
const prev = flags.get(key)
|
|
60
|
+
flags.set(key, Array.isArray(prev) ? [...prev, next] : [prev, next])
|
|
61
|
+
} else {
|
|
62
|
+
flags.set(key, next)
|
|
63
|
+
}
|
|
64
|
+
i += 1
|
|
65
|
+
} else {
|
|
66
|
+
flags.set(key, 'true')
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return { positional, flags }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function getFlagValue(flags, key, fallback = undefined) {
|
|
73
|
+
const value = flags.get(key)
|
|
74
|
+
if (value === undefined) return fallback
|
|
75
|
+
if (Array.isArray(value)) return value[value.length - 1]
|
|
76
|
+
return value
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function getFlagList(flags, key) {
|
|
80
|
+
const value = flags.get(key)
|
|
81
|
+
if (value === undefined) return []
|
|
82
|
+
return Array.isArray(value) ? value : [value]
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function loadPoeditorConfig(flags) {
|
|
86
|
+
const configPath = resolveFromCwd(getFlagValue(flags, 'config', './poeditor/.poeditor.json'))
|
|
87
|
+
const raw = readJsonIfExists(configPath) ?? {}
|
|
88
|
+
const resolved = resolveObjectConfig(raw) ?? resolveLegacyConfig(raw) ?? {}
|
|
89
|
+
return {
|
|
90
|
+
configPath,
|
|
91
|
+
projectId: resolved.projectId ?? '000000',
|
|
92
|
+
downloadPath: resolved.downloadPath ?? './src/lang/',
|
|
93
|
+
languages: resolved.languages ?? undefined,
|
|
94
|
+
defaultLanguage: resolved.defaultLanguage ?? 'sv',
|
|
95
|
+
defaultFormat: resolved.defaultFormat ?? 'json',
|
|
96
|
+
uploadFile: resolved.uploadFile ?? './src/lang/sv.json',
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function getPoeditorToken(flags) {
|
|
101
|
+
const fromFlag = getFlagValue(flags, 'api-token')
|
|
102
|
+
if (fromFlag) return fromFlag
|
|
103
|
+
if (process.env.POEDITOR_API_KEY) return process.env.POEDITOR_API_KEY
|
|
104
|
+
const publicConfigPath = resolveFromCwd('./public/config.json')
|
|
105
|
+
const publicConfig = readJsonIfExists(publicConfigPath)
|
|
106
|
+
if (typeof publicConfig?.POEDITOR_API_KEY === 'string') {
|
|
107
|
+
return publicConfig.POEDITOR_API_KEY
|
|
108
|
+
}
|
|
109
|
+
return ''
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function ensureDir(dirPath) {
|
|
113
|
+
fs.mkdirSync(dirPath, { recursive: true })
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function resolvePathFromCwd(p) {
|
|
117
|
+
return resolveFromCwd(p)
|
|
118
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Download translation files from PoEditor.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* pnpm exec fg-devkit poeditor-download [--config ./poeditor/.poeditor.json] [--project-id 123]
|
|
7
|
+
* [--language sv --language en] [--format json] [--output-dir ./src/lang] [--api-token xxx]
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import fs from 'node:fs'
|
|
11
|
+
import path from 'node:path'
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
ensureDir,
|
|
15
|
+
getFlagList,
|
|
16
|
+
getFlagValue,
|
|
17
|
+
getPoeditorToken,
|
|
18
|
+
loadPoeditorConfig,
|
|
19
|
+
parseFlags,
|
|
20
|
+
resolvePathFromCwd,
|
|
21
|
+
} from './poeditor-common.mjs'
|
|
22
|
+
|
|
23
|
+
async function exportLanguage({ token, projectId, language, format }) {
|
|
24
|
+
const params = new URLSearchParams({
|
|
25
|
+
api_token: token,
|
|
26
|
+
id: String(projectId),
|
|
27
|
+
language,
|
|
28
|
+
type: format === 'json' ? 'key_value_json' : format,
|
|
29
|
+
})
|
|
30
|
+
const res = await fetch('https://api.poeditor.com/v2/projects/export', {
|
|
31
|
+
method: 'POST',
|
|
32
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
33
|
+
body: params,
|
|
34
|
+
})
|
|
35
|
+
if (!res.ok) {
|
|
36
|
+
throw new Error(`PoEditor export failed (${res.status})`)
|
|
37
|
+
}
|
|
38
|
+
const data = await res.json()
|
|
39
|
+
const url = data?.result?.url
|
|
40
|
+
if (!url) {
|
|
41
|
+
throw new Error(`PoEditor export missing URL for language ${language}`)
|
|
42
|
+
}
|
|
43
|
+
return url
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function prettifyJsonFile(filePath) {
|
|
47
|
+
let raw = fs.readFileSync(filePath, 'utf8')
|
|
48
|
+
if (raw.charCodeAt(0) === 0xfeff) raw = raw.slice(1)
|
|
49
|
+
const parsed = JSON.parse(raw)
|
|
50
|
+
const sorted = Object.keys(parsed)
|
|
51
|
+
.sort((a, b) => a.localeCompare(b))
|
|
52
|
+
.reduce((acc, key) => {
|
|
53
|
+
acc[key] = parsed[key]
|
|
54
|
+
return acc
|
|
55
|
+
}, {})
|
|
56
|
+
fs.writeFileSync(filePath, `${JSON.stringify(sorted, null, 2)}\n`, 'utf8')
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function downloadToFile(url, filePath, format) {
|
|
60
|
+
const res = await fetch(url)
|
|
61
|
+
if (!res.ok) {
|
|
62
|
+
throw new Error(`Failed to download ${url} (${res.status})`)
|
|
63
|
+
}
|
|
64
|
+
const text = await res.text()
|
|
65
|
+
fs.writeFileSync(filePath, text, 'utf8')
|
|
66
|
+
if (format === 'json') {
|
|
67
|
+
prettifyJsonFile(filePath)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function main() {
|
|
72
|
+
const { flags } = parseFlags(process.argv.slice(2))
|
|
73
|
+
const cfg = loadPoeditorConfig(flags)
|
|
74
|
+
|
|
75
|
+
const token = getPoeditorToken(flags)
|
|
76
|
+
if (!token) {
|
|
77
|
+
console.error('[poeditor-download] Missing POEDITOR_API_KEY (env), --api-token, or public/config.json value.')
|
|
78
|
+
process.exit(1)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const projectId = getFlagValue(flags, 'project-id', cfg.projectId)
|
|
82
|
+
const format = getFlagValue(flags, 'format', cfg.defaultFormat)
|
|
83
|
+
const outputDir = resolvePathFromCwd(getFlagValue(flags, 'output-dir', cfg.downloadPath))
|
|
84
|
+
const requested = getFlagList(flags, 'language')
|
|
85
|
+
const fromConfig = Array.isArray(cfg.languages) ? cfg.languages : []
|
|
86
|
+
const languages = requested.length > 0 ? requested : fromConfig.length > 0 ? fromConfig : [cfg.defaultLanguage]
|
|
87
|
+
|
|
88
|
+
ensureDir(outputDir)
|
|
89
|
+
|
|
90
|
+
for (const language of languages) {
|
|
91
|
+
const url = await exportLanguage({ token, projectId, language, format })
|
|
92
|
+
const outPath = path.resolve(outputDir, `${language}.${format}`)
|
|
93
|
+
await downloadToFile(url, outPath, format)
|
|
94
|
+
console.log(`[poeditor-download] Saved ${outPath}`)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
main().catch((err) => {
|
|
99
|
+
console.error('[poeditor-download]', err.message)
|
|
100
|
+
process.exit(1)
|
|
101
|
+
})
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Upload translation files to PoEditor.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* pnpm exec fg-devkit poeditor-upload [--config ./poeditor/.poeditor.json] [--project-id 123]
|
|
7
|
+
* [--language sv] [--file ./src/lang/sv.json] [--api-token xxx]
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import fs from 'node:fs'
|
|
11
|
+
|
|
12
|
+
import { getFlagValue, getPoeditorToken, loadPoeditorConfig, parseFlags, resolvePathFromCwd } from './poeditor-common.mjs'
|
|
13
|
+
|
|
14
|
+
async function uploadFile({ token, projectId, language, updating }) {
|
|
15
|
+
const params = new URLSearchParams({
|
|
16
|
+
api_token: token,
|
|
17
|
+
id: String(projectId),
|
|
18
|
+
language,
|
|
19
|
+
updating,
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
const res = await fetch('https://api.poeditor.com/v2/projects/upload', {
|
|
23
|
+
method: 'POST',
|
|
24
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
25
|
+
body: params,
|
|
26
|
+
})
|
|
27
|
+
if (!res.ok) {
|
|
28
|
+
throw new Error(`PoEditor upload failed (${res.status})`)
|
|
29
|
+
}
|
|
30
|
+
const data = await res.json()
|
|
31
|
+
if (data?.response?.status !== 'success') {
|
|
32
|
+
throw new Error(`Upload failed: ${JSON.stringify(data)}`)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function main() {
|
|
37
|
+
const { flags } = parseFlags(process.argv.slice(2))
|
|
38
|
+
const cfg = loadPoeditorConfig(flags)
|
|
39
|
+
|
|
40
|
+
const token = getPoeditorToken(flags)
|
|
41
|
+
if (!token) {
|
|
42
|
+
console.error('[poeditor-upload] Missing POEDITOR_API_KEY (env), --api-token, or public/config.json value.')
|
|
43
|
+
process.exit(1)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const projectId = getFlagValue(flags, 'project-id', cfg.projectId)
|
|
47
|
+
const language = getFlagValue(flags, 'language', cfg.defaultLanguage)
|
|
48
|
+
const filePath = resolvePathFromCwd(getFlagValue(flags, 'file', cfg.uploadFile))
|
|
49
|
+
const updating = fs.readFileSync(filePath, 'utf8')
|
|
50
|
+
|
|
51
|
+
await uploadFile({ token, projectId, language, updating })
|
|
52
|
+
console.log(`[poeditor-upload] Upload completed for ${filePath}`)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
main().catch((err) => {
|
|
56
|
+
console.error('[poeditor-upload]', err.message)
|
|
57
|
+
process.exit(1)
|
|
58
|
+
})
|
|
@@ -18,6 +18,7 @@ import { spawnSync } from 'node:child_process'
|
|
|
18
18
|
import { fileURLToPath } from 'node:url'
|
|
19
19
|
import { createRequire } from 'node:module'
|
|
20
20
|
import { resolveDependencyPolicyPath } from './resolve-dependency-policy-path.mjs'
|
|
21
|
+
import { syncScaffoldVueDepsFromPolicy } from './sync-scaffold-vue-deps.mjs'
|
|
21
22
|
|
|
22
23
|
const require = createRequire(import.meta.url)
|
|
23
24
|
const scriptDir = path.dirname(fileURLToPath(import.meta.url))
|
|
@@ -114,4 +115,6 @@ for (const [name, version] of Object.entries(updatedDevDeps)) {
|
|
|
114
115
|
}
|
|
115
116
|
|
|
116
117
|
fs.writeFileSync(policyPath, `${JSON.stringify(policy, null, 2)}\n`, 'utf8')
|
|
118
|
+
const fgDevkitRoot = path.resolve(scriptDir, '..')
|
|
119
|
+
syncScaffoldVueDepsFromPolicy({ fgDevkitRoot, policyPath })
|
|
117
120
|
console.log('[policy-upgrade] Updated dependency policy from selected ncu changes.')
|
|
@@ -17,6 +17,9 @@
|
|
|
17
17
|
*
|
|
18
18
|
* Runs entirely on the local filesystem (no registry or network).
|
|
19
19
|
*
|
|
20
|
+
* After writing the policy, also updates scaffold/vue/package.json dependency pins from that policy
|
|
21
|
+
* (see sync-scaffold-vue-deps.mjs).
|
|
22
|
+
*
|
|
20
23
|
* Usage:
|
|
21
24
|
* node scripts/refresh-dependency-policy-from-repos.mjs [projectsRoot] [policyFile]
|
|
22
25
|
*/
|
|
@@ -26,6 +29,7 @@ import path from 'node:path'
|
|
|
26
29
|
import { fileURLToPath } from 'node:url'
|
|
27
30
|
|
|
28
31
|
import { collectPinViolations } from './dependency-policy-pins.mjs'
|
|
32
|
+
import { syncScaffoldVueDepsFromPolicy } from './sync-scaffold-vue-deps.mjs'
|
|
29
33
|
|
|
30
34
|
const scriptDir = path.dirname(fileURLToPath(import.meta.url))
|
|
31
35
|
const fgDevkitRoot = path.resolve(scriptDir, '..')
|
|
@@ -207,6 +211,9 @@ if (policyErrors.length > 0) {
|
|
|
207
211
|
|
|
208
212
|
fs.writeFileSync(policyPath, `${JSON.stringify(policy, null, 2)}\n`)
|
|
209
213
|
|
|
214
|
+
const fgDevkitRoot = path.resolve(scriptDir, '..')
|
|
215
|
+
syncScaffoldVueDepsFromPolicy({ fgDevkitRoot, policyPath })
|
|
216
|
+
|
|
210
217
|
console.log(`[refresh-policy] projectsRoot=${projectsRoot}`)
|
|
211
218
|
console.log(`[refresh-policy] policy=${policyPath}`)
|
|
212
219
|
console.log(
|