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,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
|
+
}
|