fg-devkit 1.5.3 → 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.
Files changed (45) hide show
  1. package/configs/dependency-policy.vue-workspace.json +2 -2
  2. package/package.json +9 -1
  3. package/scaffold/vue/.browserslistrc +3 -0
  4. package/scaffold/vue/.editorconfig +5 -0
  5. package/scaffold/vue/Dockerfile +18 -0
  6. package/scaffold/vue/Dockerfile.local +16 -0
  7. package/scaffold/vue/Jenkinsfile +130 -0
  8. package/scaffold/vue/LICENSE +19 -0
  9. package/scaffold/vue/env-adjustment.sh +19 -0
  10. package/scaffold/vue/index.html +16 -0
  11. package/scaffold/vue/nginx/default.conf +23 -0
  12. package/scaffold/vue/nginx/gzip.conf +10 -0
  13. package/scaffold/vue/nginx/security.conf +6 -0
  14. package/scaffold/vue/package.json +44 -0
  15. package/scaffold/vue/poeditor/.poeditor.json +8 -0
  16. package/scaffold/vue/public/config.json +8 -0
  17. package/scaffold/vue/src/App.vue +10 -0
  18. package/scaffold/vue/src/api/client.ts +114 -0
  19. package/scaffold/vue/src/api/runtimeConfig.ts +10 -0
  20. package/scaffold/vue/src/assets/.gitkeep +0 -0
  21. package/scaffold/vue/src/components/.gitkeep +0 -0
  22. package/scaffold/vue/src/composables/.gitkeep +0 -0
  23. package/scaffold/vue/src/config/env.ts +45 -0
  24. package/scaffold/vue/src/i18n.ts +11 -0
  25. package/scaffold/vue/src/lang/sv.json +1 -0
  26. package/scaffold/vue/src/main.ts +29 -0
  27. package/scaffold/vue/src/queries/.gitkeep +0 -0
  28. package/scaffold/vue/src/router/index.ts +22 -0
  29. package/scaffold/vue/src/style.css +7 -0
  30. package/scaffold/vue/src/types/.gitkeep +0 -0
  31. package/scaffold/vue/src/views/HomeView.vue +10 -0
  32. package/scaffold/vue/src/vite-env.d.ts +1 -0
  33. package/scaffold/vue/start-container.sh +15 -0
  34. package/scaffold/vue/tailwind.config.js +21 -0
  35. package/scaffold/vue/vite.config.ts +41 -0
  36. package/scripts/fg-devkit.mjs +28 -3
  37. package/scripts/fleet-bump-fg-devkit.mjs +135 -0
  38. package/scripts/poeditor-common.mjs +118 -0
  39. package/scripts/poeditor-download.mjs +101 -0
  40. package/scripts/poeditor-upload.mjs +58 -0
  41. package/scripts/policy-upgrade.mjs +3 -0
  42. package/scripts/refresh-dependency-policy-from-repos.mjs +7 -0
  43. package/scripts/scaffold-vue.mjs +183 -0
  44. package/scripts/sync-scaffold-vue-deps.mjs +68 -0
  45. package/tsconfig.json +13 -0
@@ -0,0 +1,183 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Copies fg-devkit/scaffold/vue into a new directory and replaces PLACEHOLDER_* tokens.
4
+ *
5
+ * Usage:
6
+ * pnpm exec fg-devkit scaffold-vue <target-dir> [options]
7
+ *
8
+ * Options (all optional):
9
+ * --name <string> Human-readable app name (default: My App)
10
+ * --slug <string> package.json name / slug (default: my-app)
11
+ * --docker-image <string> Docker registry image without tag (default: myorg/my-app-ui)
12
+ * --jenkins-library <string> Jenkins shared library name (default: my-shared-library)
13
+ * --jenkins-node-image <string> CI Docker image for Node (default: node:22-bookworm)
14
+ * --jenkins-docker-label <string> Jenkins label for docker-build agents (default: docker)
15
+ * --jenkins-registry-cred <string> Jenkins credentialsId for Docker registry (default: docker-registry)
16
+ * --jenkins-poeditor-cred <string> Jenkins credentialsId for POEditor API key (default: poeditor_api_key)
17
+ * --dev-branch <string> Primary non-master dev branch name (default: develop)
18
+ * --poeditor-project-id <string> POEditor numeric project id (default: 000000)
19
+ * --env-dev-host <string> window.location.host value for development (default: dev.app.local:5173)
20
+ * --env-preprod-host <string> host for preprod (default: preprod.app.example)
21
+ * --env-prod-host <string> host for production (default: app.example)
22
+ * --copyright-holder <string> Legal name in LICENSE (default: Your organization)
23
+ * --copyright-year <string> Year in LICENSE (default: current calendar year)
24
+ * --force Allow a non-empty target (overwrites same paths as in the template)
25
+ */
26
+
27
+ import fs from 'node:fs'
28
+ import path from 'node:path'
29
+ import { fileURLToPath } from 'node:url'
30
+
31
+ const scriptDir = path.dirname(fileURLToPath(import.meta.url))
32
+ const fgDevkitRoot = path.resolve(scriptDir, '..')
33
+ const scaffoldRoot = path.join(fgDevkitRoot, 'scaffold/vue')
34
+
35
+ /** True if the directory does not exist, is empty, or only contains a fresh clone (.git). */
36
+ function isEffectivelyEmptyForScaffold(dir) {
37
+ if (!fs.existsSync(dir)) return true
38
+ const names = fs.readdirSync(dir).filter((n) => n !== '.DS_Store')
39
+ if (names.length === 0) return true
40
+ if (names.length === 1 && names[0] === '.git') return true
41
+ return false
42
+ }
43
+
44
+ function parseArgs(argv) {
45
+ const positional = []
46
+ const flags = new Map()
47
+ for (let i = 0; i < argv.length; i += 1) {
48
+ const a = argv[i]
49
+ if (a.startsWith('--')) {
50
+ const key = a.slice(2)
51
+ const next = argv[i + 1]
52
+ if (next && !next.startsWith('--')) {
53
+ flags.set(key, next)
54
+ i += 1
55
+ } else {
56
+ flags.set(key, 'true')
57
+ }
58
+ } else {
59
+ positional.push(a)
60
+ }
61
+ }
62
+ return { positional, flags }
63
+ }
64
+
65
+ function shouldTreatAsBinary(relPath) {
66
+ return /\.(png|jpe?g|gif|webp|ico|woff2?|eot|ttf|zip|pdf)$/i.test(relPath)
67
+ }
68
+
69
+ function replacePlaceholders(content, map) {
70
+ let out = content
71
+ for (const [token, value] of Object.entries(map)) {
72
+ out = out.split(token).join(value)
73
+ }
74
+ return out
75
+ }
76
+
77
+ function walk(dir, callback) {
78
+ for (const name of fs.readdirSync(dir, { withFileTypes: true })) {
79
+ const p = path.join(dir, name.name)
80
+ if (name.isDirectory()) {
81
+ walk(p, callback)
82
+ } else {
83
+ callback(p)
84
+ }
85
+ }
86
+ }
87
+
88
+ function copyScaffold(targetDir, replacements) {
89
+ fs.mkdirSync(targetDir, { recursive: true })
90
+
91
+ const copyRecursive = (src, dest) => {
92
+ for (const name of fs.readdirSync(src, { withFileTypes: true })) {
93
+ const from = path.join(src, name.name)
94
+ const to = path.join(dest, name.name)
95
+ if (name.isDirectory()) {
96
+ fs.mkdirSync(to, { recursive: true })
97
+ copyRecursive(from, to)
98
+ } else {
99
+ const rel = path.relative(scaffoldRoot, from)
100
+ if (shouldTreatAsBinary(rel)) {
101
+ fs.copyFileSync(from, to)
102
+ } else {
103
+ const raw = fs.readFileSync(from, 'utf8')
104
+ fs.writeFileSync(to, replacePlaceholders(raw, replacements), 'utf8')
105
+ }
106
+ }
107
+ }
108
+ }
109
+
110
+ copyRecursive(scaffoldRoot, targetDir)
111
+
112
+ walk(targetDir, (filePath) => {
113
+ if (filePath.endsWith('.sh')) {
114
+ try {
115
+ fs.chmodSync(filePath, 0o755)
116
+ } catch {
117
+ /* ignore */
118
+ }
119
+ }
120
+ })
121
+ }
122
+
123
+ function main() {
124
+ const { positional, flags } = parseArgs(process.argv.slice(2))
125
+ const targetDirArg = positional[0]
126
+ if (!targetDirArg) {
127
+ console.error('[scaffold-vue] Missing target directory.')
128
+ console.error('Usage: pnpm exec fg-devkit scaffold-vue <target-dir> [--name …] [--slug …] …')
129
+ process.exit(1)
130
+ }
131
+
132
+ const targetDir = path.resolve(process.cwd(), targetDirArg)
133
+ const force = flags.get('force') === 'true'
134
+ if (!force && !isEffectivelyEmptyForScaffold(targetDir)) {
135
+ console.error('[scaffold-vue] Target directory is not empty:', targetDir)
136
+ console.error('[scaffold-vue] Use an empty directory, a clone with only .git, or pass --force (overwrites paths that exist in the template).')
137
+ process.exit(1)
138
+ }
139
+
140
+ if (!fs.existsSync(scaffoldRoot)) {
141
+ console.error('[scaffold-vue] Scaffold template missing:', scaffoldRoot)
142
+ process.exit(1)
143
+ }
144
+
145
+ const name = flags.get('name') ?? 'My App'
146
+ const slug = flags.get('slug') ?? 'my-app'
147
+ const dockerImage = flags.get('docker-image') ?? 'myorg/my-app-ui'
148
+ const jenkinsLibrary = flags.get('jenkins-library') ?? 'my-shared-library'
149
+ const jenkinsNodeImage = flags.get('jenkins-node-image') ?? 'node:22-bookworm'
150
+ const jenkinsDockerLabel = flags.get('jenkins-docker-label') ?? 'docker'
151
+ const jenkinsRegistryCred = flags.get('jenkins-registry-cred') ?? 'docker-registry'
152
+ const jenkinsPoeditorCred = flags.get('jenkins-poeditor-cred') ?? 'poeditor_api_key'
153
+ const devBranch = flags.get('dev-branch') ?? 'develop'
154
+ const poeditorProjectId = flags.get('poeditor-project-id') ?? '000000'
155
+ const envDevHost = flags.get('env-dev-host') ?? 'dev.app.local:5173'
156
+ const envPreprodHost = flags.get('env-preprod-host') ?? 'preprod.app.example'
157
+ const envProdHost = flags.get('env-prod-host') ?? 'app.example'
158
+ const copyrightHolder = flags.get('copyright-holder') ?? 'Your organization'
159
+ const copyrightYear = flags.get('copyright-year') ?? String(new Date().getFullYear())
160
+
161
+ const replacements = {
162
+ PLACEHOLDER_APP_NAME: name,
163
+ PLACEHOLDER_APP_SLUG: slug,
164
+ PLACEHOLDER_DOCKER_REGISTRY_IMAGE: dockerImage,
165
+ PLACEHOLDER_JENKINS_SHARED_LIBRARY: jenkinsLibrary,
166
+ PLACEHOLDER_JENKINS_NODE_IMAGE: jenkinsNodeImage,
167
+ PLACEHOLDER_JENKINS_DOCKER_BUILD_LABEL: jenkinsDockerLabel,
168
+ PLACEHOLDER_DOCKER_REGISTRY_CREDENTIAL_ID: jenkinsRegistryCred,
169
+ PLACEHOLDER_POEDITOR_CREDENTIAL_ID: jenkinsPoeditorCred,
170
+ PLACEHOLDER_PRIMARY_DEV_BRANCH: devBranch,
171
+ PLACEHOLDER_POEDITOR_PROJECT_ID: poeditorProjectId,
172
+ PLACEHOLDER_ENV_DEV_HOST: envDevHost,
173
+ PLACEHOLDER_ENV_PREPROD_HOST: envPreprodHost,
174
+ PLACEHOLDER_ENV_PROD_HOST: envProdHost,
175
+ PLACEHOLDER_COPYRIGHT_HOLDER: copyrightHolder,
176
+ PLACEHOLDER_COPYRIGHT_YEAR: copyrightYear,
177
+ }
178
+
179
+ copyScaffold(targetDir, replacements)
180
+ console.log('[scaffold-vue] Created project at', targetDir)
181
+ }
182
+
183
+ main()
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Aligns scaffold/vue/package.json dependency versions with dependency-policy.vue-workspace.json.
4
+ * Also pins scaffold devDependency `fg-devkit` to the fg-devkit package version at publish time.
5
+ *
6
+ * Invoked automatically after refresh-dependency-policy-from-repos.mjs writes the policy, and
7
+ * can be run manually: node …/sync-scaffold-vue-deps.mjs [policy-file]
8
+ */
9
+
10
+ import fs from 'node:fs'
11
+ import path from 'node:path'
12
+ import { fileURLToPath } from 'node:url'
13
+
14
+ import { resolveDependencyPolicyPath } from './resolve-dependency-policy-path.mjs'
15
+
16
+ /**
17
+ * @param {{ fgDevkitRoot: string, policyPath: string }} options
18
+ */
19
+ export function syncScaffoldVueDepsFromPolicy(options) {
20
+ const { fgDevkitRoot, policyPath } = options
21
+ const policy = JSON.parse(fs.readFileSync(policyPath, 'utf8'))
22
+ const scaffoldPkgPath = path.join(fgDevkitRoot, 'scaffold/vue/package.json')
23
+ if (!fs.existsSync(scaffoldPkgPath)) {
24
+ console.warn('[sync-scaffold-vue-deps] No scaffold package at', scaffoldPkgPath)
25
+ return
26
+ }
27
+ const pkg = JSON.parse(fs.readFileSync(scaffoldPkgPath, 'utf8'))
28
+
29
+ const resolveVersion = (name) =>
30
+ policy.managedDependencies?.dependencies?.[name] ??
31
+ policy.managedDependencies?.devDependencies?.[name] ??
32
+ policy.managedAnywhere?.[name]
33
+
34
+ const fgDevkitPkgPath = path.join(fgDevkitRoot, 'package.json')
35
+ const fgDevkitPkg = JSON.parse(fs.readFileSync(fgDevkitPkgPath, 'utf8'))
36
+ const fgDevkitVersion = fgDevkitPkg.version
37
+
38
+ const applySection = (section) => {
39
+ const deps = pkg[section]
40
+ if (!deps) return
41
+ for (const name of Object.keys(deps)) {
42
+ if (name === 'fg-devkit' && section === 'devDependencies') {
43
+ deps[name] = fgDevkitVersion
44
+ continue
45
+ }
46
+ const v = resolveVersion(name)
47
+ if (v !== undefined) {
48
+ deps[name] = v
49
+ }
50
+ }
51
+ }
52
+
53
+ applySection('dependencies')
54
+ applySection('devDependencies')
55
+
56
+ fs.writeFileSync(scaffoldPkgPath, `${JSON.stringify(pkg, null, 2)}\n`)
57
+ console.log('[sync-scaffold-vue-deps] Updated', scaffoldPkgPath)
58
+ }
59
+
60
+ const scriptDir = path.dirname(fileURLToPath(import.meta.url))
61
+ const fgDevkitRoot = path.resolve(scriptDir, '..')
62
+ const invokedPath = path.resolve(process.argv[1] ?? '')
63
+ const selfPath = path.resolve(fileURLToPath(import.meta.url))
64
+
65
+ if (invokedPath === selfPath) {
66
+ const policyPath = resolveDependencyPolicyPath(scriptDir, process.cwd(), process.argv)
67
+ syncScaffoldVueDepsFromPolicy({ fgDevkitRoot, policyPath })
68
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "compilerOptions": {
4
+ "noEmit": true,
5
+ "skipLibCheck": true,
6
+ "allowJs": true,
7
+ "checkJs": false,
8
+ "moduleResolution": "bundler",
9
+ "target": "ES2022",
10
+ "module": "ES2022"
11
+ },
12
+ "exclude": ["node_modules", "scaffold"]
13
+ }