@brickflow/cli 0.0.57 → 0.0.59
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/CHANGELOG.md +12 -0
- package/README.md +10 -0
- package/package.json +9 -9
- package/src/translate/index.js +2 -1
- package/src/translate/runtime-config.js +10 -0
- package/src/translate/utils.js +58 -21
- package/src/translate-context/ai-context.js +8 -4
- package/src/translate-sync/index.js +23 -7
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -32,3 +32,13 @@ If a stroked element also has a visible fill, its original fill geometry is kept
|
|
|
32
32
|
Run `brick svg` first for general SVGO optimization and `brick svg-fix` last for stroke expansion. After all strokes are expanded, later `svg-fix` runs do nothing; the two commands no longer alternate between competing canonical formats.
|
|
33
33
|
|
|
34
34
|
Geometry and stroke lengths must be unitless or use `px`. Non-scaling strokes are rejected because expanding them correctly would require changing transforms; the original file is left untouched on failure.
|
|
35
|
+
|
|
36
|
+
## Translation directories
|
|
37
|
+
|
|
38
|
+
`brick translate-sync`, `brick translate-context` and `brick translate` look for translatable sources in `packages/brick` and `apps/*` by default. Pass `--dirs` to use other directories:
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
brick translate-sync --dirs "packages/stem,apps/*"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Directories are comma-separated, relative to the workspace root, and may contain globs. Quote the value so the shell does not expand `*`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brickflow/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.59",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.mjs",
|
|
@@ -22,16 +22,16 @@
|
|
|
22
22
|
"format:check": "pnpm exec prettier . --ignore-path ../../.prettierignore --check"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@babel/parser": "8.0.
|
|
26
|
-
"@babel/traverse": "8.0.
|
|
27
|
-
"@babel/types": "8.0.
|
|
28
|
-
"@google/genai": "2.
|
|
29
|
-
"@napi-rs/canvas": "1.0
|
|
30
|
-
"@vue/compiler-sfc": "3.5.
|
|
25
|
+
"@babel/parser": "8.0.10",
|
|
26
|
+
"@babel/traverse": "8.0.10",
|
|
27
|
+
"@babel/types": "8.0.10",
|
|
28
|
+
"@google/genai": "2.32.0",
|
|
29
|
+
"@napi-rs/canvas": "1.1.0",
|
|
30
|
+
"@vue/compiler-sfc": "3.5.44",
|
|
31
31
|
"fantasticon": "4.1.0",
|
|
32
|
-
"glob": "
|
|
32
|
+
"glob": "14.0.3",
|
|
33
33
|
"looks-same": "10.0.1",
|
|
34
|
-
"sharp": "0.35.
|
|
34
|
+
"sharp": "0.35.5",
|
|
35
35
|
"svgo": "4.1.0",
|
|
36
36
|
"svgpath": "2.6.0",
|
|
37
37
|
"transformation-matrix": "3.1.0"
|
package/src/translate/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { resolveWorkspaceRoot } from '../shared/workspace-root.js'
|
|
|
5
5
|
import { getContextFilePath, readAiContextDescription } from '../translate-context/ai-context.js'
|
|
6
6
|
import { translateBatch } from './ai.js'
|
|
7
7
|
import { buildTranslateHelp, parseTranslateRuntimeArgs, setTranslateRuntimeConfig } from './runtime-config.js'
|
|
8
|
-
import { listTranslationTargets, sortObjectKeys, stringifySortedJson } from './utils.js'
|
|
8
|
+
import { listTranslationTargets, setTranslateDirs, sortObjectKeys, stringifySortedJson } from './utils.js'
|
|
9
9
|
|
|
10
10
|
const workspaceRoot = resolveWorkspaceRoot()
|
|
11
11
|
const rawArgs = process.argv.slice(3)
|
|
@@ -20,6 +20,7 @@ let requestedLanguageCodes
|
|
|
20
20
|
|
|
21
21
|
try {
|
|
22
22
|
setTranslateRuntimeConfig(options)
|
|
23
|
+
setTranslateDirs(options.dirs, workspaceRoot)
|
|
23
24
|
requestedLanguageCodes = parseLanguageCodes(options.locales)
|
|
24
25
|
} catch (error) {
|
|
25
26
|
console.error(error instanceof Error ? error.message : String(error))
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { DEFAULT_TRANSLATE_DIRS } from './utils.js'
|
|
2
|
+
|
|
1
3
|
export const DEFAULT_CONTEXT_MODEL = 'gemini-3.1-flash-lite-preview'
|
|
2
4
|
|
|
3
5
|
let runtimeConfig = null
|
|
@@ -14,6 +16,7 @@ Required options:
|
|
|
14
16
|
|
|
15
17
|
Optional:
|
|
16
18
|
--context-model "<model>" Default: ${DEFAULT_CONTEXT_MODEL}
|
|
19
|
+
--dirs "<dir,dir>" Default: ${DEFAULT_TRANSLATE_DIRS.join(',')}
|
|
17
20
|
|
|
18
21
|
Example:
|
|
19
22
|
${command} \\
|
|
@@ -34,6 +37,7 @@ export function parseTranslateRuntimeArgs(rawArgs) {
|
|
|
34
37
|
const options = {
|
|
35
38
|
apiKey: null,
|
|
36
39
|
contextModel: DEFAULT_CONTEXT_MODEL,
|
|
40
|
+
dirs: undefined,
|
|
37
41
|
locales: undefined,
|
|
38
42
|
productContext: null,
|
|
39
43
|
}
|
|
@@ -60,6 +64,12 @@ export function parseTranslateRuntimeArgs(rawArgs) {
|
|
|
60
64
|
continue
|
|
61
65
|
}
|
|
62
66
|
|
|
67
|
+
if (value === '--dirs') {
|
|
68
|
+
options.dirs = rawArgs[index + 1] ?? null
|
|
69
|
+
index += 1
|
|
70
|
+
continue
|
|
71
|
+
}
|
|
72
|
+
|
|
63
73
|
if (value === '--locales') {
|
|
64
74
|
options.locales = rawArgs[index + 1] ?? null
|
|
65
75
|
index += 1
|
package/src/translate/utils.js
CHANGED
|
@@ -4,9 +4,11 @@ import { isIdentifier, isMemberExpression, isStringLiteral, isTemplateLiteral }
|
|
|
4
4
|
import { compileTemplate, parse as parseSFC } from '@vue/compiler-sfc'
|
|
5
5
|
import { execFileSync } from 'child_process'
|
|
6
6
|
import { createHash } from 'crypto'
|
|
7
|
-
import { existsSync } from 'fs'
|
|
7
|
+
import { existsSync, statSync } from 'fs'
|
|
8
8
|
import { globSync } from 'glob'
|
|
9
|
-
import { join, resolve } from 'path'
|
|
9
|
+
import { dirname, join, relative, resolve } from 'path'
|
|
10
|
+
|
|
11
|
+
import { resolveWorkspaceRoot } from '../shared/workspace-root.js'
|
|
10
12
|
|
|
11
13
|
const tTraverse = traverseModule.default || traverseModule
|
|
12
14
|
const IGNORED_GLOB_PATTERNS = [
|
|
@@ -18,8 +20,12 @@ const IGNORED_GLOB_PATTERNS = [
|
|
|
18
20
|
'**/public/**',
|
|
19
21
|
]
|
|
20
22
|
const IGNORED_SOURCE_SEGMENTS = ['/node_modules/', '/.nuxt/', '/dist/', '/.output/', '/coverage/', '/public/']
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
+
export const DEFAULT_TRANSLATE_DIRS = ['packages/brick', 'apps/*']
|
|
24
|
+
const TRANSLATION_DIRS_GLOB = '{components/**/translate,pages-translate/*,layouts-translate/**,script-translate/*}'
|
|
25
|
+
|
|
26
|
+
let translateDirs = DEFAULT_TRANSLATE_DIRS
|
|
27
|
+
let translateRoots = null
|
|
28
|
+
let workspaceRootDir = null
|
|
23
29
|
|
|
24
30
|
export function compileVueToJS(code, filePath) {
|
|
25
31
|
const { descriptor } = parseSFC(code)
|
|
@@ -127,23 +133,30 @@ export function getProjectName(id) {
|
|
|
127
133
|
|
|
128
134
|
export function getProjectRoot(id) {
|
|
129
135
|
const normalizedId = id.replace(/[?#].*$/, '')
|
|
130
|
-
const brickMatch = normalizedId.match(/^(.*\/packages\/brick)(?:\/|$)/)
|
|
131
136
|
|
|
132
|
-
if (
|
|
133
|
-
|
|
137
|
+
if (!translateRoots) {
|
|
138
|
+
setTranslateDirs(undefined, resolveWorkspaceRoot())
|
|
134
139
|
}
|
|
135
140
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
return appMatch?.[1] ?? null
|
|
141
|
+
return translateRoots.find((rootDir) => normalizedId.startsWith(`${rootDir}/`)) ?? null
|
|
139
142
|
}
|
|
140
143
|
|
|
141
144
|
export function getScriptName(id) {
|
|
142
|
-
const
|
|
145
|
+
const projectRoot = getProjectRoot(id)
|
|
146
|
+
const baseDir = projectRoot ? dirname(projectRoot) : (workspaceRootDir ?? resolveWorkspaceRoot())
|
|
147
|
+
const relativePath = relative(baseDir, id).replace(/\\/g, '/')
|
|
143
148
|
|
|
144
149
|
return toPageName(relativePath.replace(/\.\w+$/, '').replace(/\//g, '_'))
|
|
145
150
|
}
|
|
146
151
|
|
|
152
|
+
export function getTranslateDirs() {
|
|
153
|
+
return translateDirs
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function getTranslationGlobs(fileName) {
|
|
157
|
+
return translateDirs.map((dir) => `${dir}/${TRANSLATION_DIRS_GLOB}/${fileName}`)
|
|
158
|
+
}
|
|
159
|
+
|
|
147
160
|
export function getTranslationPaths(id) {
|
|
148
161
|
if (isVueSubResourceId(id)) {
|
|
149
162
|
return null
|
|
@@ -158,18 +171,16 @@ export function getTranslationPaths(id) {
|
|
|
158
171
|
return null
|
|
159
172
|
}
|
|
160
173
|
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
174
|
+
const projectRoot = getProjectRoot(normalizedId)
|
|
175
|
+
if (!projectRoot) {
|
|
176
|
+
return null
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const isComponent = normalizedId.endsWith('.vue') && normalizedId.includes('/components/')
|
|
165
180
|
const isPage = normalizedId.endsWith('.vue') && normalizedId.includes('/pages/')
|
|
166
181
|
const isLayout = normalizedId.endsWith('.vue') && normalizedId.includes('/layouts/')
|
|
167
|
-
const projectRoot = getProjectRoot(normalizedId)
|
|
168
182
|
const isScript =
|
|
169
|
-
!normalizedId.endsWith('.vue') &&
|
|
170
|
-
Boolean(projectRoot) &&
|
|
171
|
-
/\.(?:js|ts)$/.test(normalizedId) &&
|
|
172
|
-
!normalizedId.endsWith('.d.ts')
|
|
183
|
+
!normalizedId.endsWith('.vue') && /\.(?:js|ts)$/.test(normalizedId) && !normalizedId.endsWith('.d.ts')
|
|
173
184
|
|
|
174
185
|
if (isComponent && !normalizedId.endsWith('/index.vue')) {
|
|
175
186
|
return null
|
|
@@ -235,7 +246,7 @@ export function getTranslationPaths(id) {
|
|
|
235
246
|
}
|
|
236
247
|
|
|
237
248
|
export function listTranslationSamplePaths(workspaceRoot) {
|
|
238
|
-
return globSync(
|
|
249
|
+
return globSync(getTranslationGlobs('sample.json'), {
|
|
239
250
|
absolute: true,
|
|
240
251
|
cwd: workspaceRoot,
|
|
241
252
|
ignore: IGNORED_GLOB_PATTERNS,
|
|
@@ -289,6 +300,32 @@ export function normalize(input) {
|
|
|
289
300
|
return input.trim().toLowerCase().replace(/\s+/g, ' ')
|
|
290
301
|
}
|
|
291
302
|
|
|
303
|
+
export function setTranslateDirs(value, workspaceRoot) {
|
|
304
|
+
const dirs =
|
|
305
|
+
value === undefined
|
|
306
|
+
? DEFAULT_TRANSLATE_DIRS
|
|
307
|
+
: String(value ?? '')
|
|
308
|
+
.split(',')
|
|
309
|
+
.map((dir) => dir.trim().replace(/\\/g, '/').replace(/^\.\//, '').replace(/\/+$/, ''))
|
|
310
|
+
.filter(Boolean)
|
|
311
|
+
|
|
312
|
+
if (dirs.length === 0) {
|
|
313
|
+
throw new Error(
|
|
314
|
+
'Invalid --dirs value. Use comma-separated directories, for example: --dirs "packages/stem,apps/*"',
|
|
315
|
+
)
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
translateDirs = dirs
|
|
319
|
+
workspaceRootDir = workspaceRoot
|
|
320
|
+
translateRoots = globSync(dirs, {
|
|
321
|
+
absolute: true,
|
|
322
|
+
cwd: workspaceRoot,
|
|
323
|
+
ignore: IGNORED_GLOB_PATTERNS,
|
|
324
|
+
})
|
|
325
|
+
.filter((dirPath) => statSync(dirPath).isDirectory())
|
|
326
|
+
.sort((left, right) => right.length - left.length)
|
|
327
|
+
}
|
|
328
|
+
|
|
292
329
|
export function shortHash(input) {
|
|
293
330
|
return createHash('md5').update(input).digest('hex').slice(0, 4)
|
|
294
331
|
}
|
|
@@ -14,12 +14,15 @@ import {
|
|
|
14
14
|
parseTranslateRuntimeArgs,
|
|
15
15
|
setTranslateRuntimeConfig,
|
|
16
16
|
} from '../translate/runtime-config.js'
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
getTranslationGlobs,
|
|
19
|
+
listTranslationTargets,
|
|
20
|
+
setTranslateDirs,
|
|
21
|
+
stringifySortedJson,
|
|
22
|
+
} from '../translate/utils.js'
|
|
18
23
|
|
|
19
24
|
const workspaceRoot = resolveWorkspaceRoot()
|
|
20
25
|
const CONTEXT_FILE_NAME = 'ai-context.json'
|
|
21
|
-
const CONTEXT_FILE_GLOB =
|
|
22
|
-
'{packages/brick,apps/*}/{components/**/translate,pages-translate/*,layouts-translate/**,script-translate/*}/ai-context.json'
|
|
23
26
|
const CHANGE_THRESHOLD = readFloat('TRANSLATE_CONTEXT_MIN_CHANGE', 0.3)
|
|
24
27
|
const SOURCE_MAX_CHARS = readPositiveInt('TRANSLATE_CONTEXT_SOURCE_MAX_CHARS', 16000)
|
|
25
28
|
|
|
@@ -122,7 +125,7 @@ export function readAiContextDescription(samplePath) {
|
|
|
122
125
|
|
|
123
126
|
export function removeObsoleteAiContexts(targets, rootDir = workspaceRoot) {
|
|
124
127
|
const activeContextPaths = new Set(targets.map(({ samplePath }) => getContextFilePath(samplePath)))
|
|
125
|
-
const contextPaths = globSync(
|
|
128
|
+
const contextPaths = globSync(getTranslationGlobs(CONTEXT_FILE_NAME), {
|
|
126
129
|
absolute: true,
|
|
127
130
|
cwd: rootDir,
|
|
128
131
|
nodir: true,
|
|
@@ -152,6 +155,7 @@ export async function runAiContextCli(rawArgs = process.argv.slice(3), command =
|
|
|
152
155
|
|
|
153
156
|
try {
|
|
154
157
|
setTranslateRuntimeConfig(options)
|
|
158
|
+
setTranslateDirs(options.dirs, workspaceRoot)
|
|
155
159
|
} catch (error) {
|
|
156
160
|
console.error(error instanceof Error ? error.message : String(error))
|
|
157
161
|
console.error('')
|
|
@@ -4,9 +4,12 @@ import path from 'path'
|
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
6
|
compileVueToJS,
|
|
7
|
+
DEFAULT_TRANSLATE_DIRS,
|
|
7
8
|
extractStrings,
|
|
9
|
+
getTranslateDirs,
|
|
8
10
|
getTranslationPaths,
|
|
9
11
|
listWorkspaceFiles,
|
|
12
|
+
setTranslateDirs,
|
|
10
13
|
sortObjectKeys,
|
|
11
14
|
stringifySortedJson,
|
|
12
15
|
} from '../translate/utils.js'
|
|
@@ -20,21 +23,31 @@ if (args.includes('--help') || args.includes('-h')) {
|
|
|
20
23
|
process.exit(0)
|
|
21
24
|
}
|
|
22
25
|
|
|
23
|
-
|
|
26
|
+
const dirsIndex = args.indexOf('--dirs')
|
|
27
|
+
const isValidArgs = args.length === 0 || (args.length === 2 && dirsIndex === 0)
|
|
28
|
+
|
|
29
|
+
if (!isValidArgs) {
|
|
24
30
|
printHelp()
|
|
25
31
|
process.exit(1)
|
|
26
32
|
}
|
|
27
33
|
|
|
28
34
|
const workspaceRoot = resolveWorkspaceRoot()
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
setTranslateDirs(dirsIndex === 0 ? args[1] : undefined, workspaceRoot)
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.error(error instanceof Error ? error.message : String(error))
|
|
40
|
+
process.exit(1)
|
|
41
|
+
}
|
|
29
42
|
const activeTranslationDirs = new Set()
|
|
30
43
|
const cleanupRoots = new Set()
|
|
31
|
-
const staticCleanupRoots =
|
|
32
|
-
|
|
33
|
-
|
|
44
|
+
const staticCleanupRoots = globSync(
|
|
45
|
+
getTranslateDirs().map((dir) => `${dir}/script-translate`),
|
|
46
|
+
{
|
|
34
47
|
absolute: true,
|
|
35
48
|
cwd: workspaceRoot,
|
|
36
|
-
}
|
|
37
|
-
|
|
49
|
+
},
|
|
50
|
+
)
|
|
38
51
|
|
|
39
52
|
const files = listWorkspaceFiles(workspaceRoot).filter(
|
|
40
53
|
(file) => /\.(?:js|ts|vue)$/.test(file) && !file.endsWith('.d.ts'),
|
|
@@ -152,7 +165,10 @@ function printHelp() {
|
|
|
152
165
|
console.log(`brick translate-sync
|
|
153
166
|
|
|
154
167
|
Usage:
|
|
155
|
-
brick translate-sync
|
|
168
|
+
brick translate-sync [--dirs "<dir,dir>"]
|
|
169
|
+
|
|
170
|
+
Options:
|
|
171
|
+
--dirs Comma-separated translation directories. Default: ${DEFAULT_TRANSLATE_DIRS.join(',')}
|
|
156
172
|
|
|
157
173
|
Notes:
|
|
158
174
|
Scans source files and regenerates translation sample.json files
|