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.
- package/CHANGELOG.md +10 -0
- package/dist/api/checkFileTranslations.d.ts +23 -0
- package/dist/api/checkFileTranslations.js +236 -0
- package/dist/api/downloadFileBatch.d.ts +20 -0
- package/dist/api/downloadFileBatch.js +113 -0
- package/dist/api/sendFiles.d.ts +17 -0
- package/dist/api/sendFiles.js +115 -0
- package/dist/api/uploadFiles.d.ts +27 -0
- package/dist/api/uploadFiles.js +40 -0
- package/dist/cli/base.d.ts +32 -0
- package/dist/cli/base.js +335 -0
- package/dist/cli/commands/stage.d.ts +5 -0
- package/dist/cli/commands/stage.js +100 -0
- package/dist/cli/commands/translate.d.ts +6 -0
- package/dist/cli/commands/translate.js +63 -0
- package/dist/cli/flags.d.ts +3 -0
- package/dist/cli/flags.js +38 -0
- package/dist/cli/next.d.ts +11 -0
- package/dist/cli/next.js +20 -0
- package/dist/cli/react.d.ts +18 -0
- package/dist/cli/react.js +175 -0
- package/dist/config/generateSettings.d.ts +9 -0
- package/dist/config/generateSettings.js +176 -0
- package/dist/config/optionPresets.d.ts +2 -0
- package/dist/config/optionPresets.js +56 -0
- package/dist/config/resolveConfig.d.ts +4 -0
- package/dist/config/resolveConfig.js +19 -0
- package/dist/config/utils.d.ts +2 -0
- package/dist/config/utils.js +4 -0
- package/dist/config/validateSettings.d.ts +3 -0
- package/dist/config/validateSettings.js +32 -0
- package/dist/console/colors.d.ts +5 -0
- package/dist/console/colors.js +16 -0
- package/dist/console/index.d.ts +21 -0
- package/dist/console/index.js +24 -0
- package/dist/console/logging.d.ts +53 -0
- package/dist/console/logging.js +185 -0
- package/dist/formats/files/fileMapping.d.ts +11 -0
- package/dist/formats/files/fileMapping.js +82 -0
- package/dist/formats/files/save.d.ts +5 -0
- package/dist/formats/files/save.js +17 -0
- package/dist/formats/files/supportedFiles.d.ts +10 -0
- package/dist/formats/files/supportedFiles.js +18 -0
- package/dist/formats/files/translate.d.ts +4 -0
- package/dist/formats/files/translate.js +119 -0
- package/dist/formats/files/upload.d.ts +13 -0
- package/dist/formats/files/upload.js +136 -0
- package/dist/formats/gt/save.d.ts +9 -0
- package/dist/formats/gt/save.js +26 -0
- package/dist/formats/json/flattenJson.d.ts +14 -0
- package/dist/formats/json/flattenJson.js +64 -0
- package/dist/formats/json/mergeJson.d.ts +13 -0
- package/dist/formats/json/mergeJson.js +257 -0
- package/dist/formats/json/parseJson.d.ts +2 -0
- package/dist/formats/json/parseJson.js +108 -0
- package/dist/formats/json/utils.d.ts +47 -0
- package/dist/formats/json/utils.js +149 -0
- package/dist/formats/utils.d.ts +2 -0
- package/dist/formats/utils.js +24 -0
- package/dist/formats/yaml/mergeYaml.d.ts +5 -0
- package/dist/formats/yaml/mergeYaml.js +55 -0
- package/dist/formats/yaml/parseYaml.d.ts +5 -0
- package/dist/formats/yaml/parseYaml.js +23 -0
- package/dist/formats/yaml/utils.d.ts +2 -0
- package/dist/formats/yaml/utils.js +22 -0
- package/dist/fs/config/loadConfig.d.ts +1 -0
- package/dist/fs/config/loadConfig.js +9 -0
- package/dist/fs/config/parseFilesConfig.d.ts +27 -0
- package/dist/fs/config/parseFilesConfig.js +129 -0
- package/dist/fs/config/setupConfig.d.ts +17 -0
- package/dist/fs/config/setupConfig.js +50 -0
- package/dist/fs/config/updateConfig.d.ts +10 -0
- package/dist/fs/config/updateConfig.js +36 -0
- package/dist/fs/config/updateVersions.d.ts +10 -0
- package/dist/fs/config/updateVersions.js +30 -0
- package/dist/fs/copyFile.d.ts +7 -0
- package/dist/fs/copyFile.js +39 -0
- package/dist/fs/createLoadTranslationsFile.d.ts +1 -0
- package/dist/fs/createLoadTranslationsFile.js +36 -0
- package/dist/fs/determineFramework.d.ts +5 -0
- package/dist/fs/determineFramework.js +46 -0
- package/dist/fs/findFilepath.d.ts +36 -0
- package/dist/fs/findFilepath.js +89 -0
- package/dist/fs/getPackageResource.d.ts +1 -0
- package/dist/fs/getPackageResource.js +6 -0
- package/dist/fs/index.d.ts +1 -0
- package/dist/fs/index.js +1 -0
- package/dist/fs/loadJSON.d.ts +6 -0
- package/dist/fs/loadJSON.js +17 -0
- package/dist/fs/matchFiles.d.ts +1 -0
- package/dist/fs/matchFiles.js +8 -0
- package/dist/fs/saveJSON.d.ts +1 -0
- package/dist/fs/saveJSON.js +7 -0
- package/dist/fs/utils.d.ts +1 -0
- package/dist/fs/utils.js +16 -0
- package/dist/hooks/postProcess.d.ts +4 -0
- package/dist/hooks/postProcess.js +110 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +20 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.js +9 -0
- package/dist/next/config/parseNextConfig.d.ts +10 -0
- package/dist/next/config/parseNextConfig.js +53 -0
- package/dist/next/jsx/utils.d.ts +7 -0
- package/dist/next/jsx/utils.js +42 -0
- package/dist/next/parse/handleInitGT.d.ts +7 -0
- package/dist/next/parse/handleInitGT.js +208 -0
- package/dist/next/parse/wrapContent.d.ts +11 -0
- package/dist/next/parse/wrapContent.js +163 -0
- package/dist/react/config/createESBuildConfig.d.ts +2 -0
- package/dist/react/config/createESBuildConfig.js +119 -0
- package/dist/react/data-_gt/addGTIdentifierToSyntaxTree.d.ts +8 -0
- package/dist/react/data-_gt/addGTIdentifierToSyntaxTree.js +111 -0
- package/dist/react/jsx/evaluateJsx.d.ts +17 -0
- package/dist/react/jsx/evaluateJsx.js +85 -0
- package/dist/react/jsx/trimJsxStringChildren.d.ts +7 -0
- package/dist/react/jsx/trimJsxStringChildren.js +95 -0
- package/dist/react/jsx/utils/constants.d.ts +10 -0
- package/dist/react/jsx/utils/constants.js +31 -0
- package/dist/react/jsx/utils/parseAst.d.ts +30 -0
- package/dist/react/jsx/utils/parseAst.js +277 -0
- package/dist/react/jsx/utils/parseJsx.d.ts +21 -0
- package/dist/react/jsx/utils/parseJsx.js +244 -0
- package/dist/react/jsx/utils/parseStringFunction.d.ts +16 -0
- package/dist/react/jsx/utils/parseStringFunction.js +411 -0
- package/dist/react/jsx/utils/validateStringFunction.d.ts +7 -0
- package/dist/react/jsx/utils/validateStringFunction.js +31 -0
- package/dist/react/jsx/wrapJsx.d.ts +51 -0
- package/dist/react/jsx/wrapJsx.js +387 -0
- package/dist/react/parse/createDictionaryUpdates.d.ts +3 -0
- package/dist/react/parse/createDictionaryUpdates.js +169 -0
- package/dist/react/parse/createInlineUpdates.d.ts +6 -0
- package/dist/react/parse/createInlineUpdates.js +122 -0
- package/dist/react/parse/wrapContent.d.ts +11 -0
- package/dist/react/parse/wrapContent.js +162 -0
- package/dist/react/utils/flattenDictionary.d.ts +20 -0
- package/dist/react/utils/flattenDictionary.js +75 -0
- package/dist/react/utils/getEntryAndMetadata.d.ts +5 -0
- package/dist/react/utils/getEntryAndMetadata.js +11 -0
- package/dist/react/utils/getVariableName.d.ts +25 -0
- package/dist/react/utils/getVariableName.js +37 -0
- package/dist/setup/userInput.d.ts +4 -0
- package/dist/setup/userInput.js +29 -0
- package/dist/setup/wizard.d.ts +2 -0
- package/dist/setup/wizard.js +127 -0
- package/dist/translation/parse.d.ts +15 -0
- package/dist/translation/parse.js +76 -0
- package/dist/translation/stage.d.ts +2 -0
- package/dist/translation/stage.js +44 -0
- package/dist/translation/validate.d.ts +2 -0
- package/dist/translation/validate.js +50 -0
- package/dist/types/data/json.d.ts +6 -0
- package/dist/types/data/json.js +1 -0
- package/dist/types/data.d.ts +30 -0
- package/dist/types/data.js +1 -0
- package/dist/types/files.d.ts +1 -0
- package/dist/types/files.js +1 -0
- package/dist/types/index.d.ts +173 -0
- package/dist/types/index.js +1 -0
- package/dist/utils/addExplicitAnchorIds.d.ts +24 -0
- package/dist/utils/addExplicitAnchorIds.js +260 -0
- package/dist/utils/constants.d.ts +2 -0
- package/dist/utils/constants.js +2 -0
- package/dist/utils/credentials.d.ts +12 -0
- package/dist/utils/credentials.js +119 -0
- package/dist/utils/flattenJsonFiles.d.ts +2 -0
- package/dist/utils/flattenJsonFiles.js +36 -0
- package/dist/utils/gt.d.ts +2 -0
- package/dist/utils/gt.js +2 -0
- package/dist/utils/hash.d.ts +6 -0
- package/dist/utils/hash.js +11 -0
- package/dist/utils/headers.d.ts +1 -0
- package/dist/utils/headers.js +14 -0
- package/dist/utils/installPackage.d.ts +3 -0
- package/dist/utils/installPackage.js +77 -0
- package/dist/utils/localizeStaticImports.d.ts +15 -0
- package/dist/utils/localizeStaticImports.js +341 -0
- package/dist/utils/localizeStaticUrls.d.ts +19 -0
- package/dist/utils/localizeStaticUrls.js +432 -0
- package/dist/utils/packageInfo.d.ts +3 -0
- package/dist/utils/packageInfo.js +17 -0
- package/dist/utils/packageJson.d.ts +6 -0
- package/dist/utils/packageJson.js +76 -0
- package/dist/utils/packageManager.d.ts +28 -0
- package/dist/utils/packageManager.js +269 -0
- package/dist/utils/processAnchorIds.d.ts +6 -0
- package/dist/utils/processAnchorIds.js +47 -0
- package/dist/utils/sanitizeFileContent.d.ts +6 -0
- package/dist/utils/sanitizeFileContent.js +29 -0
- package/dist/utils/validateMdx.d.ts +10 -0
- package/dist/utils/validateMdx.js +25 -0
- package/package.json +3 -3
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { parse } from '@babel/parser';
|
|
3
|
+
import generateModule from '@babel/generator';
|
|
4
|
+
import traverseModule from '@babel/traverse';
|
|
5
|
+
// Handle CommonJS/ESM interop
|
|
6
|
+
const traverse = traverseModule.default || traverseModule;
|
|
7
|
+
const generate = generateModule.default || generateModule;
|
|
8
|
+
import * as t from '@babel/types';
|
|
9
|
+
import { logError } from '../../console/logging.js';
|
|
10
|
+
export async function handleInitGT(filepath, errors, warnings, filesUpdated, packageJson, tsconfigJson) {
|
|
11
|
+
const code = await fs.promises.readFile(filepath, 'utf8');
|
|
12
|
+
let ast;
|
|
13
|
+
try {
|
|
14
|
+
ast = parse(code, {
|
|
15
|
+
sourceType: 'module',
|
|
16
|
+
plugins: ['jsx', 'typescript'],
|
|
17
|
+
tokens: true,
|
|
18
|
+
createParenthesizedExpressions: true,
|
|
19
|
+
});
|
|
20
|
+
// Analyze the actual file content to determine module system
|
|
21
|
+
let hasES6Imports = false;
|
|
22
|
+
let hasCommonJSRequire = false;
|
|
23
|
+
traverse(ast, {
|
|
24
|
+
ImportDeclaration() {
|
|
25
|
+
hasES6Imports = true;
|
|
26
|
+
},
|
|
27
|
+
CallExpression(path) {
|
|
28
|
+
if (t.isIdentifier(path.node.callee, { name: 'require' })) {
|
|
29
|
+
hasCommonJSRequire = true;
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
// Determine if we need CommonJS based on actual file content and fallback to config-based logic
|
|
34
|
+
let needsCJS = false;
|
|
35
|
+
if (hasES6Imports && !hasCommonJSRequire) {
|
|
36
|
+
// File uses ES6 imports, so we should use ES6 imports
|
|
37
|
+
needsCJS = false;
|
|
38
|
+
}
|
|
39
|
+
else if (hasCommonJSRequire && !hasES6Imports) {
|
|
40
|
+
// File uses CommonJS require, so we should use CommonJS require
|
|
41
|
+
needsCJS = true;
|
|
42
|
+
}
|
|
43
|
+
else if (hasES6Imports && hasCommonJSRequire) {
|
|
44
|
+
// Mixed usage - this is unusual but we'll default to ES6 imports
|
|
45
|
+
warnings.push(`Mixed ES6 imports and CommonJS require detected in ${filepath}. Defaulting to ES6 imports.`);
|
|
46
|
+
needsCJS = false;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
// No imports/requires found, fall back to configuration-based logic
|
|
50
|
+
if (filepath.endsWith('.ts') || filepath.endsWith('.tsx')) {
|
|
51
|
+
// For TypeScript files, check tsconfig.json compilerOptions.module
|
|
52
|
+
const moduleSetting = tsconfigJson?.compilerOptions?.module;
|
|
53
|
+
if (moduleSetting === 'commonjs' || moduleSetting === 'node') {
|
|
54
|
+
needsCJS = true;
|
|
55
|
+
}
|
|
56
|
+
else if (moduleSetting === 'esnext' ||
|
|
57
|
+
moduleSetting === 'es2022' ||
|
|
58
|
+
moduleSetting === 'es2020' ||
|
|
59
|
+
moduleSetting === 'es2015' ||
|
|
60
|
+
moduleSetting === 'es6' ||
|
|
61
|
+
moduleSetting === 'node16' ||
|
|
62
|
+
moduleSetting === 'nodenext') {
|
|
63
|
+
needsCJS = false;
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
// Default to ESM for TypeScript files if no module setting is specified
|
|
67
|
+
needsCJS = false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else if (filepath.endsWith('.js')) {
|
|
71
|
+
// For JavaScript files, check package.json type
|
|
72
|
+
// If package.json has "type": "module", .js files are treated as ES modules
|
|
73
|
+
needsCJS = packageJson?.type !== 'module';
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
// For other file extensions, default to ESM
|
|
77
|
+
needsCJS = false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Check if withGTConfig or initGT is already imported/required
|
|
81
|
+
let hasGTConfig = false;
|
|
82
|
+
let hasInitGT = false;
|
|
83
|
+
traverse(ast, {
|
|
84
|
+
ImportDeclaration(path) {
|
|
85
|
+
if (path.node.source.value === 'gt-next/config') {
|
|
86
|
+
path.node.specifiers.forEach((spec) => {
|
|
87
|
+
if (t.isImportSpecifier(spec)) {
|
|
88
|
+
if (spec.local.name === 'withGTConfig')
|
|
89
|
+
hasGTConfig = true;
|
|
90
|
+
if (spec.local.name === 'initGT')
|
|
91
|
+
hasInitGT = true;
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
VariableDeclaration(path) {
|
|
97
|
+
path.node.declarations.forEach((dec) => {
|
|
98
|
+
if (t.isVariableDeclarator(dec)) {
|
|
99
|
+
// Handle destructuring: const { withGTConfig } = require('gt-next/config')
|
|
100
|
+
if (t.isCallExpression(dec.init) &&
|
|
101
|
+
t.isIdentifier(dec.init.callee, { name: 'require' }) &&
|
|
102
|
+
t.isStringLiteral(dec.init.arguments[0], {
|
|
103
|
+
value: 'gt-next/config',
|
|
104
|
+
})) {
|
|
105
|
+
// Handle simple assignment: const withGTConfig = require(...)
|
|
106
|
+
if (t.isIdentifier(dec.id, { name: 'withGTConfig' }))
|
|
107
|
+
hasGTConfig = true;
|
|
108
|
+
if (t.isIdentifier(dec.id, { name: 'initGT' }))
|
|
109
|
+
hasInitGT = true;
|
|
110
|
+
// Handle destructuring: const { withGTConfig } = require(...)
|
|
111
|
+
if (t.isObjectPattern(dec.id)) {
|
|
112
|
+
dec.id.properties.forEach((prop) => {
|
|
113
|
+
if (t.isObjectProperty(prop) &&
|
|
114
|
+
t.isIdentifier(prop.key) &&
|
|
115
|
+
t.isIdentifier(prop.value)) {
|
|
116
|
+
if (prop.key.name === 'withGTConfig')
|
|
117
|
+
hasGTConfig = true;
|
|
118
|
+
if (prop.key.name === 'initGT')
|
|
119
|
+
hasInitGT = true;
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// Handle member access: const withGTConfig = require('gt-next/config').withGTConfig
|
|
125
|
+
else if (t.isMemberExpression(dec.init) &&
|
|
126
|
+
t.isCallExpression(dec.init.object) &&
|
|
127
|
+
t.isIdentifier(dec.init.object.callee, { name: 'require' }) &&
|
|
128
|
+
t.isStringLiteral(dec.init.object.arguments[0], {
|
|
129
|
+
value: 'gt-next/config',
|
|
130
|
+
})) {
|
|
131
|
+
if (t.isIdentifier(dec.id, { name: 'withGTConfig' }) &&
|
|
132
|
+
t.isIdentifier(dec.init.property, { name: 'withGTConfig' })) {
|
|
133
|
+
hasGTConfig = true;
|
|
134
|
+
}
|
|
135
|
+
if (t.isIdentifier(dec.id, { name: 'initGT' }) &&
|
|
136
|
+
t.isIdentifier(dec.init.property, { name: 'initGT' })) {
|
|
137
|
+
hasInitGT = true;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
// Return early if either withGTConfig or initGT is already present
|
|
145
|
+
if (hasGTConfig || hasInitGT) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
ast.program.body.unshift(needsCJS
|
|
149
|
+
? t.variableDeclaration('const', [
|
|
150
|
+
t.variableDeclarator(t.identifier('withGTConfig'), t.memberExpression(t.callExpression(t.identifier('require'), [
|
|
151
|
+
t.stringLiteral('gt-next/config'),
|
|
152
|
+
]), t.identifier('withGTConfig'))),
|
|
153
|
+
])
|
|
154
|
+
: t.importDeclaration([
|
|
155
|
+
t.importSpecifier(t.identifier('withGTConfig'), t.identifier('withGTConfig')),
|
|
156
|
+
], t.stringLiteral('gt-next/config')));
|
|
157
|
+
// Find and transform the default export
|
|
158
|
+
traverse(ast, {
|
|
159
|
+
ExportDefaultDeclaration(path) {
|
|
160
|
+
const oldExport = path.node.declaration;
|
|
161
|
+
let exportExpression;
|
|
162
|
+
if (t.isFunctionDeclaration(oldExport)) {
|
|
163
|
+
exportExpression = t.functionExpression(oldExport.id, oldExport.params, oldExport.body, oldExport.generator, oldExport.async);
|
|
164
|
+
}
|
|
165
|
+
else if (t.isClassDeclaration(oldExport)) {
|
|
166
|
+
exportExpression = t.classExpression(oldExport.id, oldExport.superClass, oldExport.body, oldExport.decorators);
|
|
167
|
+
}
|
|
168
|
+
else if (t.isTSDeclareFunction(oldExport)) {
|
|
169
|
+
// For TypeScript declare functions, create an empty function expression
|
|
170
|
+
// since declare functions don't have a runtime implementation
|
|
171
|
+
warnings.push(`Found TypeScript declare function in ${filepath}. Converting to empty function.`);
|
|
172
|
+
exportExpression = t.functionExpression(oldExport.id, oldExport.params, t.blockStatement([]), false, false);
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
exportExpression = oldExport;
|
|
176
|
+
}
|
|
177
|
+
// Validate that we have a valid Next.js config export
|
|
178
|
+
if (!t.isObjectExpression(exportExpression) &&
|
|
179
|
+
!t.isFunctionExpression(exportExpression) &&
|
|
180
|
+
!t.isArrowFunctionExpression(exportExpression)) {
|
|
181
|
+
warnings.push(`Unexpected export type in ${filepath}. Next.js config should export an object or a function returning an object.`);
|
|
182
|
+
}
|
|
183
|
+
path.node.declaration = t.callExpression(t.identifier('withGTConfig'), [
|
|
184
|
+
exportExpression,
|
|
185
|
+
t.objectExpression([]),
|
|
186
|
+
]);
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
// Generate the modified code
|
|
190
|
+
const output = generate(ast, {
|
|
191
|
+
retainLines: true,
|
|
192
|
+
retainFunctionParens: true,
|
|
193
|
+
comments: true,
|
|
194
|
+
compact: 'auto',
|
|
195
|
+
}, code);
|
|
196
|
+
// Post-process the output to fix import spacing
|
|
197
|
+
let processedCode = output.code;
|
|
198
|
+
// Add newline after the comment only
|
|
199
|
+
processedCode = processedCode.replace(/((?:import\s*{\s*withGTConfig\s*}\s*from|const\s*{\s*withGTConfig\s*}\s*=\s*require)\s*['"]gt-next\/config['"];?)/, '$1\n');
|
|
200
|
+
// Write the modified code back to the file
|
|
201
|
+
await fs.promises.writeFile(filepath, processedCode);
|
|
202
|
+
filesUpdated.push(filepath);
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
logError(`Error parsing file ${filepath}: ${error}`);
|
|
206
|
+
errors.push(`Failed to parse ${filepath}: ${error}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { WrapOptions } from '../../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Wraps all JSX elements in the src directory with a <T> tag, with unique ids.
|
|
4
|
+
* - Ignores pure strings
|
|
5
|
+
*
|
|
6
|
+
* @param options - The options object
|
|
7
|
+
* @returns An object containing the updates and errors
|
|
8
|
+
*/
|
|
9
|
+
export declare function wrapContentNext(options: WrapOptions, pkg: 'gt-next', errors: string[], warnings: string[]): Promise<{
|
|
10
|
+
filesUpdated: string[];
|
|
11
|
+
}>;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import * as t from '@babel/types';
|
|
3
|
+
import { parse } from '@babel/parser';
|
|
4
|
+
import traverseModule from '@babel/traverse';
|
|
5
|
+
import generateModule from '@babel/generator';
|
|
6
|
+
// Handle CommonJS/ESM interop
|
|
7
|
+
const traverse = traverseModule.default || traverseModule;
|
|
8
|
+
const generate = generateModule.default || generateModule;
|
|
9
|
+
import { isMeaningful } from '../../react/jsx/evaluateJsx.js';
|
|
10
|
+
import { handleJsxElement } from '../../react/jsx/wrapJsx.js';
|
|
11
|
+
import { getRelativePath } from '../../fs/findFilepath.js';
|
|
12
|
+
import { isHtmlElement, isBodyElement, hasGTProviderChild, addDynamicLangAttribute, makeParentFunctionAsync, } from '../jsx/utils.js';
|
|
13
|
+
import { generateImportMap, createImports, } from '../../react/jsx/utils/parseAst.js';
|
|
14
|
+
import { matchFiles } from '../../fs/matchFiles.js';
|
|
15
|
+
import { DEFAULT_SRC_PATTERNS } from '../../config/generateSettings.js';
|
|
16
|
+
const IMPORT_MAP = {
|
|
17
|
+
T: { name: 'T', source: 'gt-next' },
|
|
18
|
+
Var: { name: 'Var', source: 'gt-next' },
|
|
19
|
+
GTT: { name: 'T', source: 'gt-next' },
|
|
20
|
+
GTVar: { name: 'Var', source: 'gt-next' },
|
|
21
|
+
GTProvider: { name: 'GTProvider', source: 'gt-next' },
|
|
22
|
+
getLocale: { name: 'getLocale', source: 'gt-next/server' },
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Wraps all JSX elements in the src directory with a <T> tag, with unique ids.
|
|
26
|
+
* - Ignores pure strings
|
|
27
|
+
*
|
|
28
|
+
* @param options - The options object
|
|
29
|
+
* @returns An object containing the updates and errors
|
|
30
|
+
*/
|
|
31
|
+
export async function wrapContentNext(options, pkg, errors, warnings) {
|
|
32
|
+
const files = matchFiles(process.cwd(), options.src || DEFAULT_SRC_PATTERNS);
|
|
33
|
+
const filesUpdated = [];
|
|
34
|
+
for (const file of files) {
|
|
35
|
+
const code = await fs.promises.readFile(file, 'utf8');
|
|
36
|
+
// Create relative path from src directory and remove extension
|
|
37
|
+
const relativePath = getRelativePath(file, process.cwd());
|
|
38
|
+
let ast;
|
|
39
|
+
try {
|
|
40
|
+
ast = parse(code, {
|
|
41
|
+
sourceType: 'module',
|
|
42
|
+
plugins: ['jsx', 'typescript'],
|
|
43
|
+
tokens: true,
|
|
44
|
+
createParenthesizedExpressions: true,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
errors.push(`Error: Failed to parse ${file}: ${error}`);
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
let modified = false;
|
|
52
|
+
const usedImports = [];
|
|
53
|
+
const { importAlias, initialImports } = generateImportMap(ast, pkg);
|
|
54
|
+
// If the file already has a T import, skip processing it
|
|
55
|
+
if (initialImports.includes(IMPORT_MAP.T.name)) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
let globalId = 0;
|
|
59
|
+
traverse(ast, {
|
|
60
|
+
JSXElement(path) {
|
|
61
|
+
if (pkg === 'gt-next' &&
|
|
62
|
+
options.addGTProvider &&
|
|
63
|
+
isHtmlElement(path.node.openingElement)) {
|
|
64
|
+
// Find the body element recursively in the HTML tree
|
|
65
|
+
const findBodyElement = (children) => {
|
|
66
|
+
for (const child of children) {
|
|
67
|
+
if (t.isJSXElement(child) &&
|
|
68
|
+
isBodyElement(child.openingElement)) {
|
|
69
|
+
return child;
|
|
70
|
+
}
|
|
71
|
+
if (t.isJSXElement(child)) {
|
|
72
|
+
const bodyInChild = findBodyElement(child.children);
|
|
73
|
+
if (bodyInChild)
|
|
74
|
+
return bodyInChild;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
};
|
|
79
|
+
const bodyElement = findBodyElement(path.node.children);
|
|
80
|
+
if (!bodyElement) {
|
|
81
|
+
warnings.push(`File ${file} has a <html> tag without a <body> tag. Skipping GTProvider insertion.`);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
// Skip if body already has GTProvider
|
|
85
|
+
if (hasGTProviderChild(bodyElement)) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
// Handle lang attribute for html tag
|
|
89
|
+
const langAttr = path.node.openingElement.attributes.find((attr) => t.isJSXAttribute(attr) &&
|
|
90
|
+
t.isJSXIdentifier(attr.name) &&
|
|
91
|
+
t.isStringLiteral(attr.value) &&
|
|
92
|
+
attr.name.name === 'lang');
|
|
93
|
+
if (langAttr) {
|
|
94
|
+
makeParentFunctionAsync(path);
|
|
95
|
+
addDynamicLangAttribute(path.node.openingElement);
|
|
96
|
+
usedImports.push('getLocale');
|
|
97
|
+
}
|
|
98
|
+
// Wrap body children with GTProvider
|
|
99
|
+
const bodyChildren = bodyElement.children;
|
|
100
|
+
const gtProviderElement = t.jsxElement(t.jsxOpeningElement(t.jsxIdentifier('GTProvider'), [], false), t.jsxClosingElement(t.jsxIdentifier('GTProvider')), bodyChildren, false);
|
|
101
|
+
bodyElement.children = [gtProviderElement];
|
|
102
|
+
usedImports.push('GTProvider');
|
|
103
|
+
modified = true;
|
|
104
|
+
path.skip();
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
// If skip wrapping Ts, skip processing this node
|
|
108
|
+
if (options.skipTs) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
// Check if this JSX element has any JSX element ancestors
|
|
112
|
+
if (t.isJSXElement(path.parentPath?.node) ||
|
|
113
|
+
t.isJSXExpressionContainer(path.parentPath?.node)) {
|
|
114
|
+
// If we found a JSX parent, skip processing this node
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
// At this point, we're only processing top-level JSX elements
|
|
118
|
+
const opts = {
|
|
119
|
+
...importAlias,
|
|
120
|
+
idPrefix: relativePath,
|
|
121
|
+
idCount: globalId,
|
|
122
|
+
usedImports,
|
|
123
|
+
modified: false,
|
|
124
|
+
createIds: !options.disableIds,
|
|
125
|
+
warnings,
|
|
126
|
+
file,
|
|
127
|
+
};
|
|
128
|
+
const wrapped = handleJsxElement(path.node, opts, isMeaningful);
|
|
129
|
+
path.replaceWith(wrapped.node);
|
|
130
|
+
// Update global counters
|
|
131
|
+
modified = modified || opts.modified;
|
|
132
|
+
globalId = opts.idCount;
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
if (!modified)
|
|
136
|
+
continue;
|
|
137
|
+
const needsImport = usedImports.filter((imp) => !initialImports.includes(imp));
|
|
138
|
+
if (needsImport.length > 0) {
|
|
139
|
+
createImports(ast, needsImport, IMPORT_MAP);
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
const output = generate(ast, {
|
|
143
|
+
retainLines: true,
|
|
144
|
+
retainFunctionParens: true,
|
|
145
|
+
comments: true,
|
|
146
|
+
compact: 'auto',
|
|
147
|
+
}, code);
|
|
148
|
+
// Post-process the output to fix import spacing
|
|
149
|
+
let processedCode = output.code;
|
|
150
|
+
if (needsImport.length > 0) {
|
|
151
|
+
// Add newline after the comment only
|
|
152
|
+
processedCode = processedCode.replace(/((?:import\s*{\s*(?:T|GTT|Var|GTVar|GTProvider|getLocale)(?:\s*,\s*(?:T|GTT|Var|GTVar|GTProvider|getLocale))*\s*}\s*from|const\s*{\s*(?:T|GTT|Var|GTVar|GTProvider|getLocale)(?:\s*,\s*(?:T|GTT|Var|GTVar|GTProvider|getLocale))*\s*}\s*=\s*require)\s*['"]gt-(?:next|react)(?:\/server)?['"];?)/, '\n$1\n');
|
|
153
|
+
}
|
|
154
|
+
// Write the modified code back to the file
|
|
155
|
+
await fs.promises.writeFile(file, processedCode);
|
|
156
|
+
filesUpdated.push(file);
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
errors.push(`Error: Failed to write ${file}: ${error}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return { filesUpdated };
|
|
163
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { displayResolvedPaths } from '../../console/logging.js';
|
|
4
|
+
export default function createESBuildConfig(config = {}) {
|
|
5
|
+
const esbuildOptions = {
|
|
6
|
+
bundle: true,
|
|
7
|
+
format: 'cjs',
|
|
8
|
+
platform: 'node',
|
|
9
|
+
target: 'es2021',
|
|
10
|
+
loader: {
|
|
11
|
+
'.js': 'jsx',
|
|
12
|
+
'.ts': 'ts',
|
|
13
|
+
'.css': 'css', // Add CSS loader
|
|
14
|
+
},
|
|
15
|
+
sourcemap: 'inline',
|
|
16
|
+
external: ['server-only'],
|
|
17
|
+
define: {
|
|
18
|
+
React: 'global.React',
|
|
19
|
+
},
|
|
20
|
+
plugins: [],
|
|
21
|
+
};
|
|
22
|
+
// Add the custom plugin to handle 'server-only' imports
|
|
23
|
+
esbuildOptions.plugins.push({
|
|
24
|
+
name: 'ignore-server-only',
|
|
25
|
+
setup(build) {
|
|
26
|
+
build.onResolve({ filter: /^server-only$/ }, () => {
|
|
27
|
+
return {
|
|
28
|
+
path: 'server-only',
|
|
29
|
+
namespace: 'ignore-server-only',
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
build.onLoad({ filter: /^server-only$/, namespace: 'ignore-server-only' }, () => {
|
|
33
|
+
return {
|
|
34
|
+
contents: 'module.exports = {};',
|
|
35
|
+
loader: 'js',
|
|
36
|
+
};
|
|
37
|
+
});
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
// Add a plugin to handle CSS imports
|
|
41
|
+
esbuildOptions.plugins.push({
|
|
42
|
+
name: 'css-module',
|
|
43
|
+
setup(build) {
|
|
44
|
+
build.onResolve({ filter: /\.css$/ }, (args) => {
|
|
45
|
+
return {
|
|
46
|
+
path: path.resolve(args.resolveDir, args.path),
|
|
47
|
+
namespace: 'css-module',
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
build.onLoad({ filter: /\.css$/, namespace: 'css-module' }, async (args) => {
|
|
51
|
+
const css = await fs.promises.readFile(args.path, 'utf8');
|
|
52
|
+
const contents = `
|
|
53
|
+
const style = document.createElement('style');
|
|
54
|
+
style.textContent = ${JSON.stringify(css)};
|
|
55
|
+
document.head.appendChild(style);
|
|
56
|
+
`;
|
|
57
|
+
return { contents, loader: 'js' };
|
|
58
|
+
});
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
if (config.compilerOptions) {
|
|
62
|
+
if (config.compilerOptions.paths) {
|
|
63
|
+
const aliases = {};
|
|
64
|
+
const resolvedPaths = [];
|
|
65
|
+
for (const [key, value] of Object.entries(config.compilerOptions.paths)) {
|
|
66
|
+
if (Array.isArray(value) && typeof value[0] === 'string') {
|
|
67
|
+
const resolvedPath = path.resolve(process.cwd(), value[0].replace('/*', ''));
|
|
68
|
+
aliases[key.replace('/*', '')] = resolvedPath;
|
|
69
|
+
resolvedPaths.push([key, resolvedPath]);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (resolvedPaths.length) {
|
|
73
|
+
displayResolvedPaths(resolvedPaths);
|
|
74
|
+
}
|
|
75
|
+
esbuildOptions.plugins = esbuildOptions.plugins || [];
|
|
76
|
+
esbuildOptions.plugins.push({
|
|
77
|
+
name: 'alias',
|
|
78
|
+
setup(build) {
|
|
79
|
+
build.onResolve({ filter: /.*/ }, (args) => {
|
|
80
|
+
for (const [aliasKey, aliasPath] of Object.entries(aliases)) {
|
|
81
|
+
if (args.path.startsWith(`${aliasKey}/`)) {
|
|
82
|
+
const resolvedPath = path.resolve(aliasPath, args.path.slice(aliasKey.length + 1));
|
|
83
|
+
const extensions = ['.js', '.ts', '.css']; // Add .css to extensions
|
|
84
|
+
function resolveWithExtensions(basePath) {
|
|
85
|
+
for (const ext of extensions) {
|
|
86
|
+
const fullPath = `${basePath}${ext}`;
|
|
87
|
+
try {
|
|
88
|
+
const realPath = fs.realpathSync(fullPath);
|
|
89
|
+
return realPath;
|
|
90
|
+
}
|
|
91
|
+
catch (_) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
const realPath = fs.realpathSync(resolvedPath);
|
|
99
|
+
return { path: realPath };
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
const hasExtension = extensions.some((ext) => resolvedPath.endsWith(ext));
|
|
103
|
+
if (!hasExtension) {
|
|
104
|
+
const resolvedWithExt = resolveWithExtensions(resolvedPath);
|
|
105
|
+
if (resolvedWithExt) {
|
|
106
|
+
return { path: resolvedWithExt };
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
throw new Error(`Unable to resolve path: ${resolvedPath}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return esbuildOptions;
|
|
119
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { JsxChildren } from 'generaltranslation/types';
|
|
2
|
+
/**
|
|
3
|
+
* Add GT Identifier and minify the tree (recreates addGTIdentifier and writeChildrenAsObjects)
|
|
4
|
+
* @param tree - The tree to add GT identifiers to
|
|
5
|
+
* @param startingIndex - The starting index for GT IDs
|
|
6
|
+
* @returns The tree with GT identifiers added
|
|
7
|
+
*/
|
|
8
|
+
export default function addGTIdentifierToSyntaxTree(tree: any, startingIndex?: number): JsxChildren;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { HTML_CONTENT_PROPS, } from 'generaltranslation/types';
|
|
2
|
+
import { defaultVariableNames, getVariableName, minifyVariableType, } from '../utils/getVariableName.js';
|
|
3
|
+
import { isAcceptedPluralForm } from 'generaltranslation/internal';
|
|
4
|
+
/**
|
|
5
|
+
* Construct the data-_gt prop
|
|
6
|
+
* @param type - The type of the element
|
|
7
|
+
* @param props - The props of the element
|
|
8
|
+
* @param id - The id of the element
|
|
9
|
+
* @returns The data-_gt prop
|
|
10
|
+
*/
|
|
11
|
+
function constructGTProp(type, props, id) {
|
|
12
|
+
// Add translatable HTML content props
|
|
13
|
+
const generaltranslation = Object.entries(HTML_CONTENT_PROPS).reduce((acc, [minifiedName, fullName]) => {
|
|
14
|
+
if (props[fullName]) {
|
|
15
|
+
acc[minifiedName] = props[fullName];
|
|
16
|
+
}
|
|
17
|
+
return acc;
|
|
18
|
+
}, {});
|
|
19
|
+
// Plural
|
|
20
|
+
if (type === 'Plural') {
|
|
21
|
+
const pluralBranches = Object.entries(props).reduce((acc, [branchName, branch]) => {
|
|
22
|
+
if (isAcceptedPluralForm(branchName)) {
|
|
23
|
+
acc[branchName] = addGTIdentifierToSyntaxTree(branch, id);
|
|
24
|
+
}
|
|
25
|
+
return acc;
|
|
26
|
+
}, {});
|
|
27
|
+
// Add plural branches to the generaltranslation
|
|
28
|
+
if (Object.keys(pluralBranches).length) {
|
|
29
|
+
generaltranslation.t = 'p';
|
|
30
|
+
generaltranslation.b = pluralBranches;
|
|
31
|
+
}
|
|
32
|
+
// Branch
|
|
33
|
+
}
|
|
34
|
+
else if (type === 'Branch') {
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars
|
|
36
|
+
const { children, branch, ...branches } = props;
|
|
37
|
+
const resultBranches = Object.entries(branches).reduce((acc, [branchName, branch]) => {
|
|
38
|
+
acc[branchName] = addGTIdentifierToSyntaxTree(branch, id);
|
|
39
|
+
return acc;
|
|
40
|
+
}, {});
|
|
41
|
+
// Add branches to the generaltranslation
|
|
42
|
+
if (Object.keys(resultBranches).length) {
|
|
43
|
+
generaltranslation.t = 'b';
|
|
44
|
+
generaltranslation.b = resultBranches;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return Object.keys(generaltranslation).length
|
|
48
|
+
? generaltranslation
|
|
49
|
+
: undefined;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Add GT Identifier and minify the tree (recreates addGTIdentifier and writeChildrenAsObjects)
|
|
53
|
+
* @param tree - The tree to add GT identifiers to
|
|
54
|
+
* @param startingIndex - The starting index for GT IDs
|
|
55
|
+
* @returns The tree with GT identifiers added
|
|
56
|
+
*/
|
|
57
|
+
export default function addGTIdentifierToSyntaxTree(tree, startingIndex = 0) {
|
|
58
|
+
// Object to keep track of the current index for GT IDs
|
|
59
|
+
const indexObject = { index: startingIndex };
|
|
60
|
+
/**
|
|
61
|
+
* Handle a single child
|
|
62
|
+
* @param child - The child to handle
|
|
63
|
+
* @returns The handled child
|
|
64
|
+
*/
|
|
65
|
+
const handleSingleChild = (child) => {
|
|
66
|
+
// Handle JSX elements
|
|
67
|
+
if (child && typeof child === 'object') {
|
|
68
|
+
let { type } = child;
|
|
69
|
+
const { props } = child;
|
|
70
|
+
indexObject.index += 1;
|
|
71
|
+
// Handle fragments
|
|
72
|
+
if (type === 'React.Fragment') {
|
|
73
|
+
type = '';
|
|
74
|
+
}
|
|
75
|
+
// Variables
|
|
76
|
+
if (Object.keys(defaultVariableNames).includes(type)) {
|
|
77
|
+
const variableType = minifyVariableType(type);
|
|
78
|
+
const variableName = getVariableName(props, type, indexObject.index);
|
|
79
|
+
return {
|
|
80
|
+
i: indexObject.index,
|
|
81
|
+
k: variableName,
|
|
82
|
+
v: variableType,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
// Construct the data-_gt prop
|
|
86
|
+
const generaltranslation = constructGTProp(type, props, indexObject.index);
|
|
87
|
+
// Return the result
|
|
88
|
+
return {
|
|
89
|
+
t: type || `C${indexObject.index}`,
|
|
90
|
+
i: indexObject.index,
|
|
91
|
+
c: handleChildren(props.children),
|
|
92
|
+
...(generaltranslation && { d: generaltranslation }),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
if (typeof child === 'number') {
|
|
96
|
+
return child.toString();
|
|
97
|
+
}
|
|
98
|
+
return child;
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Handle children
|
|
102
|
+
* @param children - The children to handle
|
|
103
|
+
* @returns The handled children
|
|
104
|
+
*/
|
|
105
|
+
const handleChildren = (children) => {
|
|
106
|
+
return Array.isArray(children)
|
|
107
|
+
? children.map(handleSingleChild)
|
|
108
|
+
: handleSingleChild(children);
|
|
109
|
+
};
|
|
110
|
+
return handleChildren(tree);
|
|
111
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as t from '@babel/types';
|
|
2
|
+
/**
|
|
3
|
+
* Checks if a node is meaningful. Does not recurse into children.
|
|
4
|
+
* @param node - The node to check
|
|
5
|
+
* @returns Whether the node is meaningful
|
|
6
|
+
*/
|
|
7
|
+
export declare function isMeaningful(node: t.Node): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Checks if an expression is static (does not contain any variables which could change at runtime).
|
|
10
|
+
* @param expr - The expression to check
|
|
11
|
+
* @returns An object containing the result of the static check
|
|
12
|
+
*/
|
|
13
|
+
export declare function isStaticExpression(expr: t.Expression | t.JSXEmptyExpression): {
|
|
14
|
+
isStatic: boolean;
|
|
15
|
+
value?: string;
|
|
16
|
+
};
|
|
17
|
+
export declare function isStaticValue(expr: t.Expression | t.JSXEmptyExpression): boolean;
|