gtx-cli 2.6.14 → 2.6.15

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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # gtx-cli
2
2
 
3
+ ## 2.6.15
4
+
5
+ ### Patch Changes
6
+
7
+ - [#999](https://github.com/generaltranslation/gt/pull/999) [`09f3e68`](https://github.com/generaltranslation/gt/commit/09f3e6829243decffa795251d46c9e6a0d1ed878) Thanks [@fernando-aviles](https://github.com/fernando-aviles)! - Allow `sharedStaticAssets` configuration to mirror shared asset structure
8
+
3
9
  ## 2.6.14
4
10
 
5
11
  ### Patch Changes
package/dist/cli/base.js CHANGED
@@ -25,7 +25,7 @@ import { displayTranslateSummary } from '../console/displayTranslateSummary.js';
25
25
  import updateConfig from '../fs/config/updateConfig.js';
26
26
  import { createLoadTranslationsFile } from '../fs/createLoadTranslationsFile.js';
27
27
  import { saveLocalEdits } from '../api/saveLocalEdits.js';
28
- import processSharedStaticAssets from '../utils/sharedStaticAssets.js';
28
+ import processSharedStaticAssets, { mirrorAssetsToLocales, } from '../utils/sharedStaticAssets.js';
29
29
  import { setupLocadex } from '../locadex/setupFlow.js';
30
30
  import { detectFramework } from '../setup/detectFramework.js';
31
31
  import { getFrameworkDisplayName, getReactFrameworkLibrary, } from '../setup/frameworkUtils.js';
@@ -131,6 +131,8 @@ export class BaseCLI {
131
131
  if (include.size > 0) {
132
132
  await postProcessTranslations(settings, include);
133
133
  }
134
+ // Mirror assets after translations are downloaded and locale dirs are populated
135
+ await mirrorAssetsToLocales(settings);
134
136
  clearDownloaded();
135
137
  displayTranslateSummary();
136
138
  clearWarnings();
@@ -1 +1 @@
1
- export declare const PACKAGE_VERSION = "2.6.14";
1
+ export declare const PACKAGE_VERSION = "2.6.15";
@@ -1,2 +1,2 @@
1
1
  // This file is auto-generated. Do not edit manually.
2
- export const PACKAGE_VERSION = '2.6.14';
2
+ export const PACKAGE_VERSION = '2.6.15';
@@ -213,8 +213,9 @@ export type AdditionalOptions = {
213
213
  };
214
214
  export type SharedStaticAssetsConfig = {
215
215
  include: string | string[];
216
- outDir: string;
216
+ outDir?: string;
217
217
  publicPath?: string;
218
+ mirrorToLocales?: boolean;
218
219
  };
219
220
  export type JsonSchema = {
220
221
  preset?: 'mintlify' | 'openapi';
@@ -113,8 +113,7 @@ export function extractHeadingInfo(mdxContent) {
113
113
  const ast = parseProcessor.parse(mdxContent);
114
114
  processedAst = parseProcessor.runSync(ast);
115
115
  }
116
- catch (error) {
117
- console.warn(`Failed to parse MDX content: ${error instanceof Error ? error.message : String(error)}`);
116
+ catch {
118
117
  // Fallback: line-by-line extraction skipping fenced code blocks
119
118
  return extractHeadingsWithFallback(mdxContent);
120
119
  }
@@ -220,8 +219,7 @@ function applyInlineIds(translatedContent, idMappings, escapeAnchors) {
220
219
  const ast = parseProcessor.parse(translatedContent);
221
220
  processedAst = parseProcessor.runSync(ast);
222
221
  }
223
- catch (error) {
224
- console.warn(`Failed to parse translated MDX content: ${error instanceof Error ? error.message : String(error)}`);
222
+ catch {
225
223
  return applyInlineIdsStringFallback(translatedContent, idMappings, escapeAnchors);
226
224
  }
227
225
  // Apply IDs to headings based on position
@@ -296,8 +294,7 @@ function applyInlineIds(translatedContent, idMappings, escapeAnchors) {
296
294
  }
297
295
  return content;
298
296
  }
299
- catch (error) {
300
- console.warn(`Failed to stringify translated MDX content: ${error instanceof Error ? error.message : String(error)}`);
297
+ catch {
301
298
  return translatedContent;
302
299
  }
303
300
  }
@@ -291,9 +291,7 @@ function transformMdxImports(mdxContent, defaultLocale, targetLocale, hideDefaul
291
291
  const ast = parseProcessor.parse(mdxContent);
292
292
  processedAst = parseProcessor.runSync(ast);
293
293
  }
294
- catch (error) {
295
- console.warn(`Failed to parse MDX content: ${error instanceof Error ? error.message : String(error)}`);
296
- console.warn('Falling back to string-based import localization.');
294
+ catch {
297
295
  return transformImportsStringFallback(mdxContent, defaultLocale, targetLocale, hideDefaultLocale, pattern, exclude, currentFilePath, options);
298
296
  }
299
297
  let content = mdxContent;
@@ -249,9 +249,7 @@ function transformMdxUrls(mdxContent, defaultLocale, targetLocale, hideDefaultLo
249
249
  const ast = parseProcessor.parse(mdxContent);
250
250
  processedAst = parseProcessor.runSync(ast);
251
251
  }
252
- catch (error) {
253
- console.warn(`Failed to parse MDX content: ${error instanceof Error ? error.message : String(error)}`);
254
- console.warn('Returning original content unchanged due to parsing error.');
252
+ catch {
255
253
  return {
256
254
  content: mdxContent,
257
255
  hasChanges: false,
@@ -1,2 +1,3 @@
1
1
  import type { Settings } from '../types/index.js';
2
+ export declare function mirrorAssetsToLocales(settings: Settings): Promise<void>;
2
3
  export default function processSharedStaticAssets(settings: Settings): Promise<void>;
@@ -8,6 +8,8 @@ import remarkFrontmatter from 'remark-frontmatter';
8
8
  import remarkStringify from 'remark-stringify';
9
9
  import { visit } from 'unist-util-visit';
10
10
  import escapeHtmlInTextNodes from 'gt-remark';
11
+ import { createFileMapping } from '../formats/files/fileMapping.js';
12
+ import { TEMPLATE_FILE_NAME } from './constants.js';
11
13
  function derivePublicPath(outDir, provided) {
12
14
  if (provided)
13
15
  return provided;
@@ -199,26 +201,127 @@ function rewriteMdxContent(content, filePath, pathMap) {
199
201
  return { content, changed: false };
200
202
  }
201
203
  }
204
+ function resolveAssetPaths(include, cwd) {
205
+ const assetPaths = new Set();
206
+ for (let pattern of include) {
207
+ if (pattern.startsWith('/'))
208
+ pattern = pattern.slice(1);
209
+ const matches = fg.sync(path.resolve(cwd, pattern), { absolute: true });
210
+ for (const m of matches)
211
+ assetPaths.add(path.normalize(m));
212
+ }
213
+ return assetPaths;
214
+ }
215
+ export async function mirrorAssetsToLocales(settings) {
216
+ const cfg = settings.sharedStaticAssets;
217
+ if (!cfg?.mirrorToLocales)
218
+ return;
219
+ if (!settings.files)
220
+ return;
221
+ const cwd = process.cwd();
222
+ const include = toArray(cfg.include);
223
+ if (include.length === 0)
224
+ return;
225
+ const assetPaths = resolveAssetPaths(include, cwd);
226
+ if (assetPaths.size === 0)
227
+ return;
228
+ const { resolvedPaths, placeholderPaths, transformPaths } = settings.files;
229
+ const targetLocales = settings.locales.filter((l) => l !== settings.defaultLocale);
230
+ if (targetLocales.length === 0)
231
+ return;
232
+ const fileMapping = createFileMapping(resolvedPaths, placeholderPaths, transformPaths, targetLocales, settings.defaultLocale);
233
+ for (const locale of targetLocales) {
234
+ const filesMap = fileMapping[locale];
235
+ if (!filesMap)
236
+ continue;
237
+ // Extract unique (sourceDir, targetDir) pairs from the file mapping
238
+ const dirPairs = new Map();
239
+ for (const [sourcePath, targetPath] of Object.entries(filesMap)) {
240
+ if (sourcePath === TEMPLATE_FILE_NAME)
241
+ continue;
242
+ const sourceDir = path.dirname(path.resolve(cwd, sourcePath));
243
+ const targetDir = path.dirname(path.resolve(cwd, targetPath));
244
+ if (sourceDir !== targetDir) {
245
+ dirPairs.set(sourceDir, targetDir);
246
+ }
247
+ }
248
+ if (dirPairs.size === 0)
249
+ continue;
250
+ // Derive ancestor directory pairs by walking up from each known pair.
251
+ // e.g. if docs/guide → ja/docs/guide, infer docs → ja/docs.
252
+ // Stop at cwd or when an existing pair conflicts.
253
+ const ancestorPairs = new Map();
254
+ for (const [sourceDir, targetDir] of dirPairs) {
255
+ let s = path.dirname(sourceDir);
256
+ let t = path.dirname(targetDir);
257
+ while (s.startsWith(cwd) && s !== cwd) {
258
+ const existing = dirPairs.get(s) ?? ancestorPairs.get(s);
259
+ if (existing !== undefined) {
260
+ if (existing !== t)
261
+ break; // conflict — different transforms
262
+ }
263
+ else {
264
+ ancestorPairs.set(s, t);
265
+ }
266
+ s = path.dirname(s);
267
+ t = path.dirname(t);
268
+ }
269
+ }
270
+ for (const [s, t] of ancestorPairs) {
271
+ dirPairs.set(s, t);
272
+ }
273
+ // Sort source dirs by length descending so longest prefix matches first
274
+ const sortedPairs = [...dirPairs.entries()].sort((a, b) => b[0].length - a[0].length);
275
+ for (const assetAbs of assetPaths) {
276
+ // Find the directory pair whose sourceDir is the longest prefix of the asset
277
+ let bestSource;
278
+ let bestTarget;
279
+ for (const [sourceDir, targetDir] of sortedPairs) {
280
+ if (assetAbs.startsWith(sourceDir + path.sep) ||
281
+ assetAbs.startsWith(sourceDir + '/')) {
282
+ bestSource = sourceDir;
283
+ bestTarget = targetDir;
284
+ break;
285
+ }
286
+ }
287
+ if (!bestSource || !bestTarget)
288
+ continue;
289
+ const relFromSource = path.relative(bestSource, assetAbs);
290
+ const targetAsset = path.resolve(bestTarget, relFromSource);
291
+ // Skip if target already exists with same size
292
+ try {
293
+ const [srcStat, dstStat] = await Promise.all([
294
+ fs.promises.stat(assetAbs),
295
+ fs.promises.stat(targetAsset),
296
+ ]);
297
+ if (dstStat.isFile() && srcStat.size === dstStat.size)
298
+ continue;
299
+ }
300
+ catch {
301
+ // target doesn't exist, proceed with copy
302
+ }
303
+ await ensureDir(path.dirname(targetAsset));
304
+ await fs.promises.copyFile(assetAbs, targetAsset);
305
+ }
306
+ }
307
+ }
202
308
  export default async function processSharedStaticAssets(settings) {
203
309
  const cfg = settings.sharedStaticAssets;
204
310
  if (!cfg)
205
311
  return;
312
+ // mirrorToLocales is handled separately after translations are downloaded
313
+ if (cfg.mirrorToLocales)
314
+ return;
206
315
  const cwd = process.cwd();
207
316
  const include = toArray(cfg.include);
208
317
  if (include.length === 0)
209
318
  return;
210
319
  // Resolve assets
211
- const assetPaths = new Set();
212
- for (let pattern of include) {
213
- // Treat leading '/' as repo-relative, not filesystem root
214
- if (pattern.startsWith('/'))
215
- pattern = pattern.slice(1);
216
- const matches = fg.sync(path.resolve(cwd, pattern), { absolute: true });
217
- for (const m of matches)
218
- assetPaths.add(path.normalize(m));
219
- }
320
+ const assetPaths = resolveAssetPaths(include, cwd);
220
321
  if (assetPaths.size === 0)
221
322
  return;
323
+ if (!cfg.outDir)
324
+ return;
222
325
  const outDirInput = cfg.outDir.startsWith('/')
223
326
  ? cfg.outDir.slice(1)
224
327
  : cfg.outDir;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gtx-cli",
3
- "version": "2.6.14",
3
+ "version": "2.6.15",
4
4
  "main": "dist/index.js",
5
5
  "bin": "dist/main.js",
6
6
  "files": [