gtx-cli 2.3.6-alpha.2 → 2.3.6

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 (192) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/api/checkFileTranslations.d.ts +23 -0
  3. package/dist/api/checkFileTranslations.js +236 -0
  4. package/dist/api/downloadFileBatch.d.ts +20 -0
  5. package/dist/api/downloadFileBatch.js +113 -0
  6. package/dist/api/sendFiles.d.ts +17 -0
  7. package/dist/api/sendFiles.js +115 -0
  8. package/dist/api/uploadFiles.d.ts +27 -0
  9. package/dist/api/uploadFiles.js +40 -0
  10. package/dist/cli/base.d.ts +32 -0
  11. package/dist/cli/base.js +335 -0
  12. package/dist/cli/commands/stage.d.ts +5 -0
  13. package/dist/cli/commands/stage.js +100 -0
  14. package/dist/cli/commands/translate.d.ts +6 -0
  15. package/dist/cli/commands/translate.js +63 -0
  16. package/dist/cli/flags.d.ts +3 -0
  17. package/dist/cli/flags.js +38 -0
  18. package/dist/cli/next.d.ts +11 -0
  19. package/dist/cli/next.js +20 -0
  20. package/dist/cli/react.d.ts +18 -0
  21. package/dist/cli/react.js +175 -0
  22. package/dist/config/generateSettings.d.ts +9 -0
  23. package/dist/config/generateSettings.js +176 -0
  24. package/dist/config/optionPresets.d.ts +2 -0
  25. package/dist/config/optionPresets.js +56 -0
  26. package/dist/config/resolveConfig.d.ts +4 -0
  27. package/dist/config/resolveConfig.js +19 -0
  28. package/dist/config/utils.d.ts +2 -0
  29. package/dist/config/utils.js +4 -0
  30. package/dist/config/validateSettings.d.ts +3 -0
  31. package/dist/config/validateSettings.js +32 -0
  32. package/dist/console/colors.d.ts +5 -0
  33. package/dist/console/colors.js +16 -0
  34. package/dist/console/index.d.ts +21 -0
  35. package/dist/console/index.js +24 -0
  36. package/dist/console/logging.d.ts +53 -0
  37. package/dist/console/logging.js +185 -0
  38. package/dist/formats/files/fileMapping.d.ts +11 -0
  39. package/dist/formats/files/fileMapping.js +82 -0
  40. package/dist/formats/files/save.d.ts +5 -0
  41. package/dist/formats/files/save.js +17 -0
  42. package/dist/formats/files/supportedFiles.d.ts +10 -0
  43. package/dist/formats/files/supportedFiles.js +18 -0
  44. package/dist/formats/files/translate.d.ts +4 -0
  45. package/dist/formats/files/translate.js +119 -0
  46. package/dist/formats/files/upload.d.ts +13 -0
  47. package/dist/formats/files/upload.js +136 -0
  48. package/dist/formats/gt/save.d.ts +9 -0
  49. package/dist/formats/gt/save.js +26 -0
  50. package/dist/formats/json/flattenJson.d.ts +14 -0
  51. package/dist/formats/json/flattenJson.js +64 -0
  52. package/dist/formats/json/mergeJson.d.ts +13 -0
  53. package/dist/formats/json/mergeJson.js +257 -0
  54. package/dist/formats/json/parseJson.d.ts +2 -0
  55. package/dist/formats/json/parseJson.js +108 -0
  56. package/dist/formats/json/utils.d.ts +47 -0
  57. package/dist/formats/json/utils.js +149 -0
  58. package/dist/formats/utils.d.ts +2 -0
  59. package/dist/formats/utils.js +24 -0
  60. package/dist/formats/yaml/mergeYaml.d.ts +5 -0
  61. package/dist/formats/yaml/mergeYaml.js +55 -0
  62. package/dist/formats/yaml/parseYaml.d.ts +5 -0
  63. package/dist/formats/yaml/parseYaml.js +23 -0
  64. package/dist/formats/yaml/utils.d.ts +2 -0
  65. package/dist/formats/yaml/utils.js +22 -0
  66. package/dist/fs/config/loadConfig.d.ts +1 -0
  67. package/dist/fs/config/loadConfig.js +9 -0
  68. package/dist/fs/config/parseFilesConfig.d.ts +27 -0
  69. package/dist/fs/config/parseFilesConfig.js +129 -0
  70. package/dist/fs/config/setupConfig.d.ts +17 -0
  71. package/dist/fs/config/setupConfig.js +50 -0
  72. package/dist/fs/config/updateConfig.d.ts +10 -0
  73. package/dist/fs/config/updateConfig.js +36 -0
  74. package/dist/fs/config/updateVersions.d.ts +10 -0
  75. package/dist/fs/config/updateVersions.js +30 -0
  76. package/dist/fs/copyFile.d.ts +7 -0
  77. package/dist/fs/copyFile.js +39 -0
  78. package/dist/fs/createLoadTranslationsFile.d.ts +1 -0
  79. package/dist/fs/createLoadTranslationsFile.js +36 -0
  80. package/dist/fs/determineFramework.d.ts +5 -0
  81. package/dist/fs/determineFramework.js +46 -0
  82. package/dist/fs/findFilepath.d.ts +36 -0
  83. package/dist/fs/findFilepath.js +89 -0
  84. package/dist/fs/getPackageResource.d.ts +1 -0
  85. package/dist/fs/getPackageResource.js +6 -0
  86. package/dist/fs/index.d.ts +1 -0
  87. package/dist/fs/index.js +1 -0
  88. package/dist/fs/loadJSON.d.ts +6 -0
  89. package/dist/fs/loadJSON.js +17 -0
  90. package/dist/fs/matchFiles.d.ts +1 -0
  91. package/dist/fs/matchFiles.js +8 -0
  92. package/dist/fs/saveJSON.d.ts +1 -0
  93. package/dist/fs/saveJSON.js +7 -0
  94. package/dist/fs/utils.d.ts +1 -0
  95. package/dist/fs/utils.js +16 -0
  96. package/dist/hooks/postProcess.d.ts +4 -0
  97. package/dist/hooks/postProcess.js +110 -0
  98. package/dist/index.d.ts +4 -0
  99. package/dist/index.js +20 -0
  100. package/dist/main.d.ts +2 -0
  101. package/dist/main.js +9 -0
  102. package/dist/next/config/parseNextConfig.d.ts +10 -0
  103. package/dist/next/config/parseNextConfig.js +53 -0
  104. package/dist/next/jsx/utils.d.ts +7 -0
  105. package/dist/next/jsx/utils.js +42 -0
  106. package/dist/next/parse/handleInitGT.d.ts +7 -0
  107. package/dist/next/parse/handleInitGT.js +208 -0
  108. package/dist/next/parse/wrapContent.d.ts +11 -0
  109. package/dist/next/parse/wrapContent.js +163 -0
  110. package/dist/react/config/createESBuildConfig.d.ts +2 -0
  111. package/dist/react/config/createESBuildConfig.js +119 -0
  112. package/dist/react/data-_gt/addGTIdentifierToSyntaxTree.d.ts +8 -0
  113. package/dist/react/data-_gt/addGTIdentifierToSyntaxTree.js +111 -0
  114. package/dist/react/jsx/evaluateJsx.d.ts +17 -0
  115. package/dist/react/jsx/evaluateJsx.js +85 -0
  116. package/dist/react/jsx/trimJsxStringChildren.d.ts +7 -0
  117. package/dist/react/jsx/trimJsxStringChildren.js +95 -0
  118. package/dist/react/jsx/utils/constants.d.ts +10 -0
  119. package/dist/react/jsx/utils/constants.js +31 -0
  120. package/dist/react/jsx/utils/parseAst.d.ts +30 -0
  121. package/dist/react/jsx/utils/parseAst.js +277 -0
  122. package/dist/react/jsx/utils/parseJsx.d.ts +21 -0
  123. package/dist/react/jsx/utils/parseJsx.js +244 -0
  124. package/dist/react/jsx/utils/parseStringFunction.d.ts +16 -0
  125. package/dist/react/jsx/utils/parseStringFunction.js +411 -0
  126. package/dist/react/jsx/utils/validateStringFunction.d.ts +7 -0
  127. package/dist/react/jsx/utils/validateStringFunction.js +31 -0
  128. package/dist/react/jsx/wrapJsx.d.ts +51 -0
  129. package/dist/react/jsx/wrapJsx.js +387 -0
  130. package/dist/react/parse/createDictionaryUpdates.d.ts +3 -0
  131. package/dist/react/parse/createDictionaryUpdates.js +169 -0
  132. package/dist/react/parse/createInlineUpdates.d.ts +6 -0
  133. package/dist/react/parse/createInlineUpdates.js +122 -0
  134. package/dist/react/parse/wrapContent.d.ts +11 -0
  135. package/dist/react/parse/wrapContent.js +162 -0
  136. package/dist/react/utils/flattenDictionary.d.ts +20 -0
  137. package/dist/react/utils/flattenDictionary.js +75 -0
  138. package/dist/react/utils/getEntryAndMetadata.d.ts +5 -0
  139. package/dist/react/utils/getEntryAndMetadata.js +11 -0
  140. package/dist/react/utils/getVariableName.d.ts +25 -0
  141. package/dist/react/utils/getVariableName.js +37 -0
  142. package/dist/setup/userInput.d.ts +4 -0
  143. package/dist/setup/userInput.js +29 -0
  144. package/dist/setup/wizard.d.ts +2 -0
  145. package/dist/setup/wizard.js +127 -0
  146. package/dist/translation/parse.d.ts +15 -0
  147. package/dist/translation/parse.js +76 -0
  148. package/dist/translation/stage.d.ts +2 -0
  149. package/dist/translation/stage.js +44 -0
  150. package/dist/translation/validate.d.ts +2 -0
  151. package/dist/translation/validate.js +50 -0
  152. package/dist/types/data/json.d.ts +6 -0
  153. package/dist/types/data/json.js +1 -0
  154. package/dist/types/data.d.ts +30 -0
  155. package/dist/types/data.js +1 -0
  156. package/dist/types/files.d.ts +1 -0
  157. package/dist/types/files.js +1 -0
  158. package/dist/types/index.d.ts +173 -0
  159. package/dist/types/index.js +1 -0
  160. package/dist/utils/addExplicitAnchorIds.d.ts +24 -0
  161. package/dist/utils/addExplicitAnchorIds.js +260 -0
  162. package/dist/utils/constants.d.ts +2 -0
  163. package/dist/utils/constants.js +2 -0
  164. package/dist/utils/credentials.d.ts +12 -0
  165. package/dist/utils/credentials.js +119 -0
  166. package/dist/utils/flattenJsonFiles.d.ts +2 -0
  167. package/dist/utils/flattenJsonFiles.js +36 -0
  168. package/dist/utils/gt.d.ts +2 -0
  169. package/dist/utils/gt.js +2 -0
  170. package/dist/utils/hash.d.ts +6 -0
  171. package/dist/utils/hash.js +11 -0
  172. package/dist/utils/headers.d.ts +1 -0
  173. package/dist/utils/headers.js +14 -0
  174. package/dist/utils/installPackage.d.ts +3 -0
  175. package/dist/utils/installPackage.js +77 -0
  176. package/dist/utils/localizeStaticImports.d.ts +15 -0
  177. package/dist/utils/localizeStaticImports.js +341 -0
  178. package/dist/utils/localizeStaticUrls.d.ts +19 -0
  179. package/dist/utils/localizeStaticUrls.js +432 -0
  180. package/dist/utils/packageInfo.d.ts +3 -0
  181. package/dist/utils/packageInfo.js +17 -0
  182. package/dist/utils/packageJson.d.ts +6 -0
  183. package/dist/utils/packageJson.js +76 -0
  184. package/dist/utils/packageManager.d.ts +28 -0
  185. package/dist/utils/packageManager.js +269 -0
  186. package/dist/utils/processAnchorIds.d.ts +6 -0
  187. package/dist/utils/processAnchorIds.js +47 -0
  188. package/dist/utils/sanitizeFileContent.d.ts +6 -0
  189. package/dist/utils/sanitizeFileContent.js +29 -0
  190. package/dist/utils/validateMdx.d.ts +10 -0
  191. package/dist/utils/validateMdx.js +25 -0
  192. package/package.json +3 -3
@@ -0,0 +1,432 @@
1
+ import * as fs from 'fs';
2
+ import { createFileMapping } from '../formats/files/fileMapping.js';
3
+ import micromatch from 'micromatch';
4
+ import { unified } from 'unified';
5
+ import remarkParse from 'remark-parse';
6
+ import remarkMdx from 'remark-mdx';
7
+ import remarkFrontmatter from 'remark-frontmatter';
8
+ import remarkStringify from 'remark-stringify';
9
+ import { visit } from 'unist-util-visit';
10
+ import escapeHtmlInTextNodes from 'gt-remark';
11
+ const { isMatch } = micromatch;
12
+ /**
13
+ * Localizes static urls in content files.
14
+ * Currently only supported for md and mdx files. (/docs/ -> /[locale]/docs/)
15
+ * @param settings - The settings object containing the project configuration.
16
+ * @returns void
17
+ *
18
+ * @TODO This is an experimental feature, and only works in very specific cases. This needs to be improved before
19
+ * it can be enabled by default.
20
+ *
21
+ * Before this becomes a non-experimental feature, we need to:
22
+ * - Support more file types
23
+ * - Support more complex paths
24
+ */
25
+ export default async function localizeStaticUrls(settings, targetLocales) {
26
+ if (!settings.files ||
27
+ (Object.keys(settings.files.placeholderPaths).length === 1 &&
28
+ settings.files.placeholderPaths.gt)) {
29
+ return;
30
+ }
31
+ const { resolvedPaths: sourceFiles } = settings.files;
32
+ // Use filtered locales if provided, otherwise use all locales
33
+ const locales = targetLocales || settings.locales;
34
+ const fileMapping = createFileMapping(sourceFiles, settings.files.placeholderPaths, settings.files.transformPaths, settings.locales, // Always use all locales for mapping, filter later
35
+ settings.defaultLocale);
36
+ // Process all file types at once with a single call
37
+ const processPromises = [];
38
+ // First, process default locale files (from source files)
39
+ // This is needed because they might not be in the fileMapping if they're not being translated
40
+ // Only process default locale if it's in the target locales filter
41
+ if (!fileMapping[settings.defaultLocale] &&
42
+ locales.includes(settings.defaultLocale)) {
43
+ const defaultLocaleFiles = [];
44
+ // Collect all .md and .mdx files from sourceFiles
45
+ if (sourceFiles.md) {
46
+ defaultLocaleFiles.push(...sourceFiles.md);
47
+ }
48
+ if (sourceFiles.mdx) {
49
+ defaultLocaleFiles.push(...sourceFiles.mdx);
50
+ }
51
+ if (defaultLocaleFiles.length > 0) {
52
+ const defaultPromise = Promise.all(defaultLocaleFiles.map(async (filePath) => {
53
+ // Check if file exists before processing
54
+ if (!fs.existsSync(filePath)) {
55
+ return;
56
+ }
57
+ // Get file content
58
+ const fileContent = await fs.promises.readFile(filePath, 'utf8');
59
+ // Localize the file using default locale
60
+ const result = localizeStaticUrlsForFile(fileContent, settings.defaultLocale, settings.defaultLocale, // Process as default locale
61
+ settings.options?.experimentalHideDefaultLocale || false, settings.options?.docsUrlPattern, settings.options?.excludeStaticUrls, settings.options?.baseDomain);
62
+ // Only write the file if there were changes
63
+ if (result.hasChanges) {
64
+ await fs.promises.writeFile(filePath, result.content);
65
+ }
66
+ }));
67
+ processPromises.push(defaultPromise);
68
+ }
69
+ }
70
+ // Then process all other locales from fileMapping
71
+ const mappingPromises = Object.entries(fileMapping)
72
+ .filter(([locale, filesMap]) => locales.includes(locale)) // Filter by target locales
73
+ .map(async ([locale, filesMap]) => {
74
+ // Get all files that are md or mdx
75
+ const targetFiles = Object.values(filesMap).filter((path) => path.endsWith('.md') || path.endsWith('.mdx'));
76
+ // Replace the placeholder path with the target path
77
+ await Promise.all(targetFiles.map(async (filePath) => {
78
+ // Check if file exists before processing
79
+ if (!fs.existsSync(filePath)) {
80
+ return;
81
+ }
82
+ // Get file content
83
+ const fileContent = await fs.promises.readFile(filePath, 'utf8');
84
+ // Localize the file (handles both URLs and hrefs in single AST pass)
85
+ const result = localizeStaticUrlsForFile(fileContent, settings.defaultLocale, locale, settings.options?.experimentalHideDefaultLocale || false, settings.options?.docsUrlPattern, settings.options?.excludeStaticUrls, settings.options?.baseDomain);
86
+ // Only write the file if there were changes
87
+ if (result.hasChanges) {
88
+ await fs.promises.writeFile(filePath, result.content);
89
+ }
90
+ }));
91
+ });
92
+ processPromises.push(...mappingPromises);
93
+ await Promise.all(processPromises);
94
+ }
95
+ /**
96
+ * Determines if a URL should be processed based on pattern matching
97
+ */
98
+ function shouldProcessUrl(originalUrl, patternHead, targetLocale, defaultLocale, baseDomain) {
99
+ // Check fragment-only URLs like "#id-name"
100
+ if (/^\s*#/.test(originalUrl)) {
101
+ return false;
102
+ }
103
+ const patternWithoutSlash = patternHead.replace(/\/$/, '');
104
+ // Handle absolute URLs with baseDomain
105
+ let urlToCheck = originalUrl;
106
+ if (baseDomain && originalUrl.startsWith(baseDomain)) {
107
+ urlToCheck = originalUrl.substring(baseDomain.length);
108
+ }
109
+ if (targetLocale === defaultLocale) {
110
+ // For default locale processing, check if URL contains the pattern
111
+ return urlToCheck.includes(patternWithoutSlash);
112
+ }
113
+ else {
114
+ // For non-default locales, check if URL starts with pattern
115
+ return urlToCheck.startsWith(patternWithoutSlash);
116
+ }
117
+ }
118
+ /**
119
+ * Determines if a URL should be processed based on the base domain
120
+ */
121
+ function shouldProcessAbsoluteUrl(originalUrl, baseDomain) {
122
+ return originalUrl.startsWith(baseDomain);
123
+ }
124
+ /**
125
+ * Checks if a URL should be excluded based on exclusion patterns
126
+ */
127
+ function isUrlExcluded(originalUrl, exclude, defaultLocale) {
128
+ const excludePatterns = exclude.map((p) => p.replace(/\[locale\]/g, defaultLocale));
129
+ return excludePatterns.some((pattern) => isMatch(originalUrl, pattern));
130
+ }
131
+ /**
132
+ * Main URL transformation function that delegates to specific scenarios
133
+ */
134
+ export function transformUrlPath(originalUrl, patternHead, targetLocale, defaultLocale, hideDefaultLocale) {
135
+ const originalPathArray = originalUrl
136
+ .split('/')
137
+ .filter((path) => path !== '');
138
+ const patternHeadArray = patternHead.split('/').filter((path) => path !== '');
139
+ // check if the pattern head matches the original path
140
+ if (!checkIfPathMatchesPattern(originalPathArray, patternHeadArray)) {
141
+ return null;
142
+ }
143
+ if (patternHeadArray.length > originalPathArray.length) {
144
+ return null; // Pattern is longer than the URL path
145
+ }
146
+ let result = null;
147
+ if (targetLocale === defaultLocale) {
148
+ if (hideDefaultLocale) {
149
+ // check if default locale is already present
150
+ if (originalPathArray?.[patternHeadArray.length] !== defaultLocale) {
151
+ return null;
152
+ }
153
+ // remove default locale
154
+ const newPathArray = [
155
+ ...originalPathArray.slice(0, patternHeadArray.length),
156
+ ...originalPathArray.slice(patternHeadArray.length + 1),
157
+ ];
158
+ result = newPathArray.join('/');
159
+ }
160
+ else {
161
+ // check if default locale is already present
162
+ if (originalPathArray?.[patternHeadArray.length] === defaultLocale) {
163
+ return null;
164
+ }
165
+ // insert default locale
166
+ const newPathArray = [
167
+ ...originalPathArray.slice(0, patternHeadArray.length),
168
+ defaultLocale,
169
+ ...originalPathArray.slice(patternHeadArray.length),
170
+ ];
171
+ result = newPathArray.join('/');
172
+ }
173
+ }
174
+ else if (hideDefaultLocale) {
175
+ const newPathArray = [
176
+ ...originalPathArray.slice(0, patternHeadArray.length),
177
+ targetLocale,
178
+ ...originalPathArray.slice(patternHeadArray.length),
179
+ ];
180
+ result = newPathArray.join('/');
181
+ }
182
+ else {
183
+ // check default locale
184
+ if (originalPathArray?.[patternHeadArray.length] !== defaultLocale) {
185
+ return null;
186
+ }
187
+ // replace default locale with target locale
188
+ const newPathArray = [...originalPathArray];
189
+ newPathArray[patternHeadArray.length] = targetLocale;
190
+ result = newPathArray.join('/');
191
+ }
192
+ // check for leading and trailing slashes
193
+ if (originalUrl.startsWith('/')) {
194
+ result = '/' + result;
195
+ }
196
+ if (originalUrl.endsWith('/')) {
197
+ result = result + '/';
198
+ }
199
+ return result;
200
+ }
201
+ /**
202
+ * AST-based transformation for MDX files using remark-mdx
203
+ */
204
+ function transformMdxUrls(mdxContent, defaultLocale, targetLocale, hideDefaultLocale, pattern = '/[locale]', exclude = [], baseDomain) {
205
+ const transformedUrls = [];
206
+ if (!pattern.startsWith('/')) {
207
+ pattern = '/' + pattern;
208
+ }
209
+ const patternHead = pattern.split('[locale]')[0];
210
+ // Quick check: if the file doesn't contain the pattern, skip expensive AST parsing
211
+ // For default locale processing, we also need to check if content might need adjustment
212
+ if (targetLocale === defaultLocale) {
213
+ // For default locale files, we always need to check as we're looking for either:
214
+ // - paths without locale (when hideDefaultLocale=false)
215
+ // - paths with default locale (when hideDefaultLocale=true)
216
+ const patternWithoutSlash = patternHead.replace(/\/$/, '');
217
+ if (!mdxContent.includes(patternWithoutSlash)) {
218
+ return {
219
+ content: mdxContent,
220
+ hasChanges: false,
221
+ transformedUrls: [],
222
+ };
223
+ }
224
+ }
225
+ else {
226
+ // For non-default locales, use the original logic
227
+ if (!mdxContent.includes(patternHead.replace(/\/$/, ''))) {
228
+ return {
229
+ content: mdxContent,
230
+ hasChanges: false,
231
+ transformedUrls: [],
232
+ };
233
+ }
234
+ }
235
+ // Parse the MDX content into an AST
236
+ let processedAst;
237
+ try {
238
+ const parseProcessor = unified()
239
+ .use(remarkParse)
240
+ .use(remarkFrontmatter, ['yaml', 'toml'])
241
+ .use(remarkMdx);
242
+ const ast = parseProcessor.parse(mdxContent);
243
+ processedAst = parseProcessor.runSync(ast);
244
+ }
245
+ catch (error) {
246
+ console.warn(`Failed to parse MDX content: ${error instanceof Error ? error.message : String(error)}`);
247
+ console.warn('Returning original content unchanged due to parsing error.');
248
+ return {
249
+ content: mdxContent,
250
+ hasChanges: false,
251
+ transformedUrls,
252
+ };
253
+ }
254
+ // Helper function to transform URL based on pattern
255
+ const transformUrl = (originalUrl, linkType) => {
256
+ // Check if URL should be processed
257
+ if (!shouldProcessUrl(originalUrl, patternHead, targetLocale, defaultLocale, baseDomain)) {
258
+ return null;
259
+ }
260
+ // Skip absolute URLs (http://, https://, //, etc.)
261
+ if (baseDomain && shouldProcessAbsoluteUrl(originalUrl, baseDomain)) {
262
+ // Get everything after the base domain
263
+ const afterDomain = originalUrl.substring(baseDomain.length);
264
+ const transformedPath = transformUrlPath(afterDomain, patternHead, targetLocale, defaultLocale, hideDefaultLocale);
265
+ if (!transformedPath) {
266
+ return null;
267
+ }
268
+ transformedUrls.push({
269
+ originalPath: originalUrl,
270
+ newPath: transformedPath,
271
+ type: linkType,
272
+ });
273
+ return transformedPath ? baseDomain + transformedPath : null;
274
+ }
275
+ // Exclude colon-prefixed URLs (http://, https://, //, etc.)
276
+ if (originalUrl.split('?')[0].includes(':')) {
277
+ return null;
278
+ }
279
+ // Transform the URL based on locale and configuration
280
+ const newUrl = transformUrlPath(originalUrl, patternHead, targetLocale, defaultLocale, hideDefaultLocale);
281
+ if (!newUrl) {
282
+ return null;
283
+ }
284
+ // Check exclusions
285
+ if (isUrlExcluded(originalUrl, exclude, defaultLocale)) {
286
+ return null;
287
+ }
288
+ transformedUrls.push({
289
+ originalPath: originalUrl,
290
+ newPath: newUrl,
291
+ type: linkType,
292
+ });
293
+ return newUrl;
294
+ };
295
+ // Visit markdown link nodes: [text](url)
296
+ visit(processedAst, 'link', (node) => {
297
+ if (node.url) {
298
+ const newUrl = transformUrl(node.url, 'markdown');
299
+ if (newUrl) {
300
+ node.url = newUrl;
301
+ }
302
+ }
303
+ });
304
+ // Visit JSX/HTML elements for href attributes: <a href="url"> or <Card href="url">
305
+ visit(processedAst, ['mdxJsxFlowElement', 'mdxJsxTextElement'], (node) => {
306
+ const jsxNode = node;
307
+ if (jsxNode.attributes) {
308
+ for (const attr of jsxNode.attributes) {
309
+ if (attr.type === 'mdxJsxAttribute' &&
310
+ attr.name === 'href' &&
311
+ attr.value) {
312
+ // Handle MdxJsxAttribute with string or MdxJsxAttributeValueExpression
313
+ const hrefValue = typeof attr.value === 'string' ? attr.value : attr.value.value;
314
+ if (typeof hrefValue === 'string') {
315
+ const newUrl = transformUrl(hrefValue, 'href');
316
+ if (newUrl) {
317
+ if (typeof attr.value === 'string') {
318
+ attr.value = newUrl;
319
+ }
320
+ else {
321
+ attr.value.value = newUrl;
322
+ }
323
+ }
324
+ }
325
+ }
326
+ }
327
+ }
328
+ });
329
+ // Visit raw JSX nodes for href attributes in JSX strings
330
+ visit(processedAst, 'jsx', (node) => {
331
+ if (node.value && typeof node.value === 'string') {
332
+ const jsxContent = node.value;
333
+ // Use regex to find href attributes in the JSX string
334
+ const hrefRegex = /href\s*=\s*["']([^"']+)["']/g;
335
+ let match;
336
+ const replacements = [];
337
+ // Reset regex lastIndex to avoid issues with global flag
338
+ hrefRegex.lastIndex = 0;
339
+ while ((match = hrefRegex.exec(jsxContent)) !== null) {
340
+ const originalHref = match[1];
341
+ const newUrl = transformUrl(originalHref, 'href');
342
+ if (newUrl) {
343
+ // Store replacement info
344
+ const oldHrefAttr = match[0]; // The full match like 'href="/quickstart"'
345
+ const quote = oldHrefAttr.includes('"') ? '"' : "'";
346
+ const newHrefAttr = `href=${quote}${newUrl}${quote}`;
347
+ replacements.push({
348
+ start: match.index,
349
+ end: match.index + oldHrefAttr.length,
350
+ oldHrefAttr,
351
+ newHrefAttr,
352
+ });
353
+ }
354
+ }
355
+ // Apply replacements in reverse order (from end to start) to avoid position shifts
356
+ if (replacements.length > 0) {
357
+ let newJsxContent = jsxContent;
358
+ replacements
359
+ .sort((a, b) => b.start - a.start)
360
+ .forEach(({ start, end, newHrefAttr }) => {
361
+ newJsxContent =
362
+ newJsxContent.slice(0, start) +
363
+ newHrefAttr +
364
+ newJsxContent.slice(end);
365
+ });
366
+ node.value = newJsxContent;
367
+ }
368
+ }
369
+ });
370
+ // Convert the modified AST back to MDX string
371
+ let content;
372
+ try {
373
+ const stringifyProcessor = unified()
374
+ .use(remarkFrontmatter, ['yaml', 'toml'])
375
+ .use(remarkMdx)
376
+ .use(escapeHtmlInTextNodes)
377
+ .use(remarkStringify, {
378
+ handlers: {
379
+ // Handler to prevent escaping (avoids '&lt;' -> '\&lt;')
380
+ text(node) {
381
+ return node.value;
382
+ },
383
+ },
384
+ });
385
+ const outTree = stringifyProcessor.runSync(processedAst);
386
+ content = stringifyProcessor.stringify(outTree);
387
+ }
388
+ catch (error) {
389
+ console.warn(`Failed to stringify MDX content: ${error instanceof Error ? error.message : String(error)}`);
390
+ console.warn('Returning original content unchanged due to stringify error.');
391
+ return {
392
+ content: mdxContent,
393
+ hasChanges: false,
394
+ transformedUrls: [],
395
+ };
396
+ }
397
+ // Handle newline formatting to match original input
398
+ if (content.endsWith('\n') && !mdxContent.endsWith('\n')) {
399
+ content = content.slice(0, -1);
400
+ }
401
+ // Preserve leading newlines from original content
402
+ if (mdxContent.startsWith('\n') && !content.startsWith('\n')) {
403
+ content = '\n' + content;
404
+ }
405
+ return {
406
+ content,
407
+ hasChanges: transformedUrls.length > 0,
408
+ transformedUrls,
409
+ };
410
+ }
411
+ // AST-based transformation for MDX files using remark
412
+ function localizeStaticUrlsForFile(file, defaultLocale, targetLocale, hideDefaultLocale, pattern = '/[locale]', // eg /docs/[locale] or /[locale]
413
+ exclude = [], baseDomain) {
414
+ // Use AST-based transformation for MDX files
415
+ return transformMdxUrls(file, defaultLocale, targetLocale, hideDefaultLocale, pattern, exclude, baseDomain || '');
416
+ }
417
+ function cleanPath(path) {
418
+ let cleanedPath = path.startsWith('/') ? path.slice(1) : path;
419
+ cleanedPath = cleanedPath.endsWith('/')
420
+ ? cleanedPath.slice(0, -1)
421
+ : cleanedPath;
422
+ return cleanedPath;
423
+ }
424
+ function checkIfPathMatchesPattern(originalUrlArray, patternHeadArray) {
425
+ // check if the pattern head matches the original path
426
+ for (let i = 0; i < patternHeadArray.length; i++) {
427
+ if (patternHeadArray[i] !== originalUrlArray?.[i]) {
428
+ return false;
429
+ }
430
+ }
431
+ return true;
432
+ }
@@ -0,0 +1,3 @@
1
+ export declare function getPackageInfo(packageName: string): Promise<{
2
+ version: string;
3
+ } | undefined>;
@@ -0,0 +1,17 @@
1
+ import { execSync } from 'child_process';
2
+ export async function getPackageInfo(packageName) {
3
+ try {
4
+ const output = execSync(`npm list -g ${packageName}`, {
5
+ encoding: 'utf8',
6
+ stdio: 'pipe',
7
+ });
8
+ const match = output.match(new RegExp(`${packageName}@([\\d\\.\\w-]+)`));
9
+ if (match && match[1]) {
10
+ return { version: match[1] };
11
+ }
12
+ return undefined;
13
+ }
14
+ catch (error) {
15
+ return undefined;
16
+ }
17
+ }
@@ -0,0 +1,6 @@
1
+ export declare function searchForPackageJson(cwd?: string): Promise<Record<string, any> | null>;
2
+ export declare function getPackageJson(cwd?: string): Promise<Record<string, any> | null>;
3
+ export declare function getCLIVersion(): string;
4
+ export declare function updatePackageJson(packageJson: Record<string, any>, cwd?: string): Promise<void>;
5
+ export declare function isPackageInstalled(packageName: string, packageJson: Record<string, any>, asDevDependency?: boolean, checkBoth?: boolean): boolean;
6
+ export declare function getPackageVersion(packageName: string, packageJson: Record<string, any>): string | undefined;
@@ -0,0 +1,76 @@
1
+ import { logError } from '../console/logging.js';
2
+ import chalk from 'chalk';
3
+ import path from 'node:path';
4
+ import fs from 'node:fs';
5
+ import { fromPackageRoot } from '../fs/getPackageResource.js';
6
+ // search for package.json such that we can run init in non-js projects
7
+ export async function searchForPackageJson(cwd = process.cwd()) {
8
+ // Get the current working directory (where the CLI is being run)
9
+ const packageJsonPath = path.join(cwd, 'package.json');
10
+ // Check if package.json exists
11
+ if (!fs.existsSync(packageJsonPath)) {
12
+ return null;
13
+ }
14
+ try {
15
+ return JSON.parse(await fs.promises.readFile(packageJsonPath, 'utf8'));
16
+ }
17
+ catch (error) {
18
+ return null;
19
+ }
20
+ }
21
+ export async function getPackageJson(cwd = process.cwd()) {
22
+ const packageJsonPath = path.join(cwd, 'package.json');
23
+ // Check if package.json exists
24
+ if (!fs.existsSync(packageJsonPath)) {
25
+ return null;
26
+ }
27
+ try {
28
+ return JSON.parse(await fs.promises.readFile(packageJsonPath, 'utf8'));
29
+ }
30
+ catch (error) {
31
+ return null;
32
+ }
33
+ }
34
+ export function getCLIVersion() {
35
+ const packageJsonPath = fromPackageRoot('package.json');
36
+ if (!fs.existsSync(packageJsonPath)) {
37
+ return 'unknown';
38
+ }
39
+ try {
40
+ return JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')).version;
41
+ }
42
+ catch (error) {
43
+ return 'unknown';
44
+ }
45
+ }
46
+ export async function updatePackageJson(packageJson, cwd = process.cwd()) {
47
+ try {
48
+ await fs.promises.writeFile(path.join(cwd, 'package.json'), JSON.stringify(packageJson, null, 2));
49
+ }
50
+ catch (error) {
51
+ logError(chalk.red('Error updating package.json: ' + String(error)));
52
+ process.exit(1);
53
+ }
54
+ }
55
+ // check if a package is installed in the package.json file
56
+ export function isPackageInstalled(packageName, packageJson, asDevDependency = false, checkBoth = false) {
57
+ const dependencies = checkBoth
58
+ ? {
59
+ ...packageJson.devDependencies,
60
+ ...packageJson.dependencies,
61
+ }
62
+ : asDevDependency
63
+ ? packageJson.devDependencies
64
+ : packageJson.dependencies;
65
+ if (!dependencies) {
66
+ return false;
67
+ }
68
+ return dependencies[packageName] !== undefined;
69
+ }
70
+ export function getPackageVersion(packageName, packageJson) {
71
+ const dependencies = {
72
+ ...packageJson.dependencies,
73
+ ...packageJson.devDependencies,
74
+ };
75
+ return dependencies[packageName] ?? undefined;
76
+ }
@@ -0,0 +1,28 @@
1
+ export interface PackageManager {
2
+ id: string;
3
+ name: string;
4
+ label: string;
5
+ installCommand: string;
6
+ installAllCommand: string;
7
+ buildCommand: string;
8
+ runScriptCommand: string;
9
+ flags: string;
10
+ forceInstallFlag: string;
11
+ devDependencyFlag: string;
12
+ registry?: string;
13
+ detect: (cwd: string) => boolean;
14
+ addOverride: (pkgName: string, pkgVersion: string) => Promise<void>;
15
+ }
16
+ export declare class NoPackageManagerError extends Error {
17
+ constructor(message: string);
18
+ }
19
+ export declare const BUN: PackageManager;
20
+ export declare const DENO: PackageManager;
21
+ export declare const YARN_V1: PackageManager;
22
+ /** YARN V2/3/4 */
23
+ export declare const YARN_V2: PackageManager;
24
+ export declare const PNPM: PackageManager;
25
+ export declare const NPM: PackageManager;
26
+ export declare const packageManagers: PackageManager[];
27
+ export declare function _detectPackageManger(cwd: string): PackageManager | null;
28
+ export declare function getPackageManager(cwd?: string, specifiedPackageManager?: string, errorIfNotFound?: boolean): Promise<PackageManager>;