gt 2.14.58 → 2.14.60
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 +279 -0
- package/dist/cli/base.d.ts +8 -0
- package/dist/cli/base.js +44 -1
- package/dist/cli/base.js.map +1 -1
- package/dist/cli/commands/download.js +1 -2
- package/dist/cli/commands/download.js.map +1 -1
- package/dist/cli/commands/enqueue.js +1 -2
- package/dist/cli/commands/enqueue.js.map +1 -1
- package/dist/cli/commands/stage.js +6 -2
- package/dist/cli/commands/stage.js.map +1 -1
- package/dist/cli/flags.js +1 -1
- package/dist/cli/flags.js.map +1 -1
- package/dist/cli/inline.js +2 -3
- package/dist/cli/inline.js.map +1 -1
- package/dist/cli/node.js +2 -1
- package/dist/cli/node.js.map +1 -1
- package/dist/cli/react.js +2 -1
- package/dist/cli/react.js.map +1 -1
- package/dist/config/defaults.js +2 -1
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/generateSettings.d.ts +1 -0
- package/dist/config/generateSettings.js +2 -0
- package/dist/config/generateSettings.js.map +1 -1
- package/dist/console/index.d.ts +0 -2
- package/dist/console/index.js +4 -6
- package/dist/console/index.js.map +1 -1
- package/dist/extraction/postProcess.js +0 -1
- package/dist/extraction/postProcess.js.map +1 -1
- package/dist/formats/files/aggregateFiles.d.ts +0 -1
- package/dist/formats/files/aggregateFiles.js +1 -6
- package/dist/formats/files/aggregateFiles.js.map +1 -1
- package/dist/formats/files/collectFiles.js +2 -5
- package/dist/formats/files/collectFiles.js.map +1 -1
- package/dist/fs/config/parseFilesConfig.js +0 -4
- package/dist/fs/config/parseFilesConfig.js.map +1 -1
- package/dist/fs/config/updateConfig.d.ts +1 -0
- package/dist/fs/config/updateConfig.js +3 -2
- package/dist/fs/config/updateConfig.js.map +1 -1
- package/dist/generated/version.d.ts +1 -1
- package/dist/generated/version.js +1 -1
- package/dist/generated/version.js.map +1 -1
- package/dist/git/mergeDrivers.d.ts +11 -0
- package/dist/git/mergeDrivers.js +281 -0
- package/dist/git/mergeDrivers.js.map +1 -0
- package/dist/git/setupMergeDrivers.d.ts +27 -0
- package/dist/git/setupMergeDrivers.js +147 -0
- package/dist/git/setupMergeDrivers.js.map +1 -0
- package/dist/next/parse/handleInitGT.js +1 -6
- package/dist/next/parse/handleInitGT.js.map +1 -1
- package/dist/react/jsx/utils/constants.d.ts +3 -9
- package/dist/react/jsx/utils/constants.js +7 -14
- package/dist/react/jsx/utils/constants.js.map +1 -1
- package/dist/react/jsx/utils/jsxParsing/autoInsertion.d.ts +2 -2
- package/dist/react/jsx/utils/jsxParsing/autoInsertion.js +7 -7
- package/dist/react/jsx/utils/jsxParsing/autoInsertion.js.map +1 -1
- package/dist/react/jsx/utils/jsxParsing/parseJsx.js +2 -2
- package/dist/react/jsx/utils/jsxParsing/parseJsx.js.map +1 -1
- package/dist/react/jsx/utils/stringParsing/derivation/containsDeriveCall.d.ts +1 -1
- package/dist/react/jsx/utils/stringParsing/derivation/containsDeriveCall.js +1 -1
- package/dist/react/jsx/utils/stringParsing/derivation/containsDeriveCall.js.map +1 -1
- package/dist/react/jsx/utils/stringParsing/derivation/isDeriveCall.js.map +1 -1
- package/dist/react/parse/createDictionaryUpdates.js +0 -1
- package/dist/react/parse/createDictionaryUpdates.js.map +1 -1
- package/dist/react/parse/createInlineUpdates.js +1 -1
- package/dist/react/parse/createInlineUpdates.js.map +1 -1
- package/dist/translation/parse.js.map +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/parsing.d.ts +2 -0
- package/dist/types/parsing.js.map +1 -1
- package/dist/utils/fetch.js +1 -1
- package/dist/utils/fetch.js.map +1 -1
- package/package.json +7 -7
- package/dist/extraction/index.d.ts +0 -3
- package/dist/extraction/index.js +0 -3
- package/dist/extraction/types.d.ts +0 -1
- package/dist/extraction/types.js +0 -1
- package/dist/utils/headers.d.ts +0 -1
- package/dist/utils/headers.js +0 -10
- package/dist/utils/headers.js.map +0 -1
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import stringify from "fast-json-stable-stringify";
|
|
3
|
+
//#region src/git/mergeDrivers.ts
|
|
4
|
+
const LOCK_TOP_LEVEL_KEYS = new Set([
|
|
5
|
+
"version",
|
|
6
|
+
"branchId",
|
|
7
|
+
"entries"
|
|
8
|
+
]);
|
|
9
|
+
const LOCK_ENTRY_KEYS = new Set([
|
|
10
|
+
"fileId",
|
|
11
|
+
"versionId",
|
|
12
|
+
"fileName",
|
|
13
|
+
"staged",
|
|
14
|
+
"translations"
|
|
15
|
+
]);
|
|
16
|
+
const LOCK_TRANSLATION_KEYS = new Set([
|
|
17
|
+
"updatedAt",
|
|
18
|
+
"postProcessHash",
|
|
19
|
+
"fileName"
|
|
20
|
+
]);
|
|
21
|
+
function runMergeDriver(driverName, basePath, oursPath, theirsPath) {
|
|
22
|
+
const base = readMergeFile(basePath);
|
|
23
|
+
const ours = readMergeFile(oursPath);
|
|
24
|
+
const theirs = readMergeFile(theirsPath);
|
|
25
|
+
const result = driverName === "gt-lock" ? mergeGtLockJson(base, ours, theirs) : mergeGtJson(base, ours, theirs);
|
|
26
|
+
if (result.ok) fs.writeFileSync(oursPath, result.content, "utf8");
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
function mergeGtJson(baseRaw, oursRaw, theirsRaw) {
|
|
30
|
+
const base = parseRootObject(baseRaw, "base");
|
|
31
|
+
const ours = parseRootObject(oursRaw, "ours");
|
|
32
|
+
const theirs = parseRootObject(theirsRaw, "theirs");
|
|
33
|
+
if (!base.ok) return base;
|
|
34
|
+
if (!ours.ok) return ours;
|
|
35
|
+
if (!theirs.ok) return theirs;
|
|
36
|
+
const merged = {};
|
|
37
|
+
const keys = [...Object.keys(ours.value), ...Object.keys(theirs.value).filter((key) => !Object.prototype.hasOwnProperty.call(ours.value, key))];
|
|
38
|
+
for (const key of keys) {
|
|
39
|
+
const baseValue = getValueState(base.value, key);
|
|
40
|
+
const oursValue = getValueState(ours.value, key);
|
|
41
|
+
const theirsValue = getValueState(theirs.value, key);
|
|
42
|
+
if (sameValueState(oursValue, theirsValue)) {
|
|
43
|
+
if (oursValue.exists) merged[key] = oursValue.value;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (sameValueState(oursValue, baseValue)) {
|
|
47
|
+
if (theirsValue.exists) merged[key] = theirsValue.value;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (sameValueState(theirsValue, baseValue)) {
|
|
51
|
+
if (oursValue.exists) merged[key] = oursValue.value;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
ok: false,
|
|
56
|
+
reason: `Conflicting GTJSON changes for key "${key}"`
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
ok: true,
|
|
61
|
+
content: `${JSON.stringify(merged, null, 2)}\n`
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function mergeGtLockJson(baseRaw, oursRaw, theirsRaw) {
|
|
65
|
+
const base = parseLockfile(baseRaw, "base");
|
|
66
|
+
const ours = parseLockfile(oursRaw, "ours");
|
|
67
|
+
const theirs = parseLockfile(theirsRaw, "theirs");
|
|
68
|
+
if (!base.ok) return base;
|
|
69
|
+
if (!ours.ok) return ours;
|
|
70
|
+
if (!theirs.ok) return theirs;
|
|
71
|
+
const baseMap = buildEntryMap(base.value.entries);
|
|
72
|
+
const oursMap = buildEntryMap(ours.value.entries);
|
|
73
|
+
const theirsMap = buildEntryMap(theirs.value.entries);
|
|
74
|
+
if (!baseMap.ok) return baseMap;
|
|
75
|
+
if (!oursMap.ok) return oursMap;
|
|
76
|
+
if (!theirsMap.ok) return theirsMap;
|
|
77
|
+
const fileIds = new Set([
|
|
78
|
+
...baseMap.value.keys(),
|
|
79
|
+
...oursMap.value.keys(),
|
|
80
|
+
...theirsMap.value.keys()
|
|
81
|
+
]);
|
|
82
|
+
const entries = [];
|
|
83
|
+
for (const fileId of Array.from(fileIds).sort()) {
|
|
84
|
+
const entry = mergeLockEntry(baseMap.value.get(fileId), oursMap.value.get(fileId), theirsMap.value.get(fileId));
|
|
85
|
+
if (entry) entries.push(normalizeEntry(entry));
|
|
86
|
+
}
|
|
87
|
+
const merged = {
|
|
88
|
+
version: 2,
|
|
89
|
+
branchId: ours.value.branchId || theirs.value.branchId,
|
|
90
|
+
entries
|
|
91
|
+
};
|
|
92
|
+
return {
|
|
93
|
+
ok: true,
|
|
94
|
+
content: `${JSON.stringify(merged, null, 2)}\n`
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function normalizeEntry(entry) {
|
|
98
|
+
const normalized = {
|
|
99
|
+
fileId: entry.fileId,
|
|
100
|
+
versionId: entry.versionId,
|
|
101
|
+
translations: entry.translations
|
|
102
|
+
};
|
|
103
|
+
if (entry.fileName !== void 0) normalized.fileName = entry.fileName;
|
|
104
|
+
if (entry.staged !== void 0) normalized.staged = entry.staged;
|
|
105
|
+
return normalized;
|
|
106
|
+
}
|
|
107
|
+
function mergeLockEntry(base, ours, theirs) {
|
|
108
|
+
if (sameJson(ours, theirs)) return clone(ours);
|
|
109
|
+
if (!ours) return sameJson(theirs, base) ? void 0 : clone(theirs);
|
|
110
|
+
if (!theirs) return sameJson(ours, base) ? void 0 : clone(ours);
|
|
111
|
+
if (ours.versionId !== theirs.versionId) {
|
|
112
|
+
if (sameJson(ours, base)) return clone(theirs);
|
|
113
|
+
if (sameJson(theirs, base)) return clone(ours);
|
|
114
|
+
return clone(selectLatestEntry(ours, theirs));
|
|
115
|
+
}
|
|
116
|
+
const latest = selectLatestEntry(ours, theirs);
|
|
117
|
+
const merged = {
|
|
118
|
+
fileId: ours.fileId,
|
|
119
|
+
versionId: ours.versionId,
|
|
120
|
+
translations: mergeTranslations(base?.translations ?? {}, ours.translations, theirs.translations)
|
|
121
|
+
};
|
|
122
|
+
const fileName = mergeScalarField(base?.fileName, ours.fileName, theirs.fileName, latest.fileName);
|
|
123
|
+
if (fileName !== void 0) merged.fileName = fileName;
|
|
124
|
+
const staged = mergeScalarField(base?.staged, ours.staged, theirs.staged, latest.staged);
|
|
125
|
+
if (staged !== void 0) merged.staged = staged;
|
|
126
|
+
return merged;
|
|
127
|
+
}
|
|
128
|
+
function mergeScalarField(base, ours, theirs, fallback) {
|
|
129
|
+
if (ours === theirs) return ours;
|
|
130
|
+
if (ours === base) return theirs;
|
|
131
|
+
if (theirs === base) return ours;
|
|
132
|
+
return fallback;
|
|
133
|
+
}
|
|
134
|
+
function mergeTranslations(base, ours, theirs) {
|
|
135
|
+
const result = {};
|
|
136
|
+
const locales = new Set([
|
|
137
|
+
...Object.keys(base),
|
|
138
|
+
...Object.keys(ours),
|
|
139
|
+
...Object.keys(theirs)
|
|
140
|
+
]);
|
|
141
|
+
for (const locale of Array.from(locales).sort()) {
|
|
142
|
+
const baseValue = base[locale];
|
|
143
|
+
const oursValue = ours[locale];
|
|
144
|
+
const theirsValue = theirs[locale];
|
|
145
|
+
if (oursValue && theirsValue) result[locale] = sameJson(oursValue, theirsValue) ? clone(oursValue) : clone(selectLatestTranslation(oursValue, theirsValue));
|
|
146
|
+
else if (oursValue) {
|
|
147
|
+
if (!(baseValue && sameJson(oursValue, baseValue))) result[locale] = clone(oursValue);
|
|
148
|
+
} else if (theirsValue) {
|
|
149
|
+
if (!(baseValue && sameJson(theirsValue, baseValue))) result[locale] = clone(theirsValue);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return result;
|
|
153
|
+
}
|
|
154
|
+
function parseRootObject(raw, label) {
|
|
155
|
+
if (!raw.trim()) return {
|
|
156
|
+
ok: true,
|
|
157
|
+
value: {}
|
|
158
|
+
};
|
|
159
|
+
let value;
|
|
160
|
+
try {
|
|
161
|
+
value = JSON.parse(raw);
|
|
162
|
+
} catch {
|
|
163
|
+
return {
|
|
164
|
+
ok: false,
|
|
165
|
+
reason: `Could not parse ${label} JSON`
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
if (!isRecord(value)) return {
|
|
169
|
+
ok: false,
|
|
170
|
+
reason: `${label} JSON must be an object`
|
|
171
|
+
};
|
|
172
|
+
return {
|
|
173
|
+
ok: true,
|
|
174
|
+
value
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
function parseLockfile(raw, label) {
|
|
178
|
+
if (!raw.trim()) return {
|
|
179
|
+
ok: true,
|
|
180
|
+
value: {
|
|
181
|
+
version: 2,
|
|
182
|
+
branchId: "",
|
|
183
|
+
entries: []
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
const parsed = parseRootObject(raw, label);
|
|
187
|
+
if (!parsed.ok) return parsed;
|
|
188
|
+
const validationError = validateLockfile(parsed.value, label);
|
|
189
|
+
if (validationError) return {
|
|
190
|
+
ok: false,
|
|
191
|
+
reason: validationError
|
|
192
|
+
};
|
|
193
|
+
return {
|
|
194
|
+
ok: true,
|
|
195
|
+
value: parsed.value
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function validateLockfile(value, label) {
|
|
199
|
+
if (!hasOnlyKeys(value, LOCK_TOP_LEVEL_KEYS)) return `${label} gt-lock.json contains unsupported top-level fields`;
|
|
200
|
+
if (value.version !== 2) return `${label} gt-lock.json must use version 2`;
|
|
201
|
+
if (typeof value.branchId !== "string") return `${label} gt-lock.json must contain a branchId string`;
|
|
202
|
+
if (!Array.isArray(value.entries)) return `${label} gt-lock.json must contain an entries array`;
|
|
203
|
+
for (const entry of value.entries) {
|
|
204
|
+
if (!isRecord(entry)) return `${label} gt-lock.json contains a non-object entry`;
|
|
205
|
+
if (!hasOnlyKeys(entry, LOCK_ENTRY_KEYS)) return `${label} gt-lock.json entry contains unsupported fields`;
|
|
206
|
+
if (typeof entry.fileId !== "string") return `${label} gt-lock.json entry must contain a fileId string`;
|
|
207
|
+
if (typeof entry.versionId !== "string") return `${label} gt-lock.json entry must contain a versionId string`;
|
|
208
|
+
if (entry.fileName !== void 0 && typeof entry.fileName !== "string") return `${label} gt-lock.json entry fileName must be a string`;
|
|
209
|
+
if (entry.staged !== void 0 && typeof entry.staged !== "boolean") return `${label} gt-lock.json entry staged must be a boolean`;
|
|
210
|
+
if (!isRecord(entry.translations)) return `${label} gt-lock.json entry translations must be an object`;
|
|
211
|
+
for (const translation of Object.values(entry.translations)) {
|
|
212
|
+
if (!isRecord(translation)) return `${label} gt-lock.json translation must be an object`;
|
|
213
|
+
if (!hasOnlyKeys(translation, LOCK_TRANSLATION_KEYS)) return `${label} gt-lock.json translation contains unsupported fields`;
|
|
214
|
+
for (const field of LOCK_TRANSLATION_KEYS) if (translation[field] !== void 0 && typeof translation[field] !== "string") return `${label} gt-lock.json translation ${field} must be a string`;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
function buildEntryMap(entries) {
|
|
220
|
+
const map = /* @__PURE__ */ new Map();
|
|
221
|
+
for (const entry of entries) {
|
|
222
|
+
if (map.has(entry.fileId)) return {
|
|
223
|
+
ok: false,
|
|
224
|
+
reason: `gt-lock.json contains duplicate fileId "${entry.fileId}"`
|
|
225
|
+
};
|
|
226
|
+
map.set(entry.fileId, entry);
|
|
227
|
+
}
|
|
228
|
+
return {
|
|
229
|
+
ok: true,
|
|
230
|
+
value: map
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
function selectLatestEntry(ours, theirs) {
|
|
234
|
+
return maxEntryTimestamp(theirs) > maxEntryTimestamp(ours) ? theirs : ours;
|
|
235
|
+
}
|
|
236
|
+
function maxEntryTimestamp(entry) {
|
|
237
|
+
return Math.max(0, ...Object.values(entry.translations).map((translation) => parseTimestamp(translation.updatedAt)));
|
|
238
|
+
}
|
|
239
|
+
function selectLatestTranslation(ours, theirs) {
|
|
240
|
+
return parseTimestamp(theirs.updatedAt) > parseTimestamp(ours.updatedAt) ? theirs : ours;
|
|
241
|
+
}
|
|
242
|
+
function parseTimestamp(value) {
|
|
243
|
+
if (!value) return 0;
|
|
244
|
+
const parsed = Date.parse(value);
|
|
245
|
+
return Number.isNaN(parsed) ? 0 : parsed;
|
|
246
|
+
}
|
|
247
|
+
function readMergeFile(filepath) {
|
|
248
|
+
try {
|
|
249
|
+
return fs.readFileSync(filepath, "utf8");
|
|
250
|
+
} catch {
|
|
251
|
+
return "";
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
function getValueState(record, key) {
|
|
255
|
+
return Object.prototype.hasOwnProperty.call(record, key) ? {
|
|
256
|
+
exists: true,
|
|
257
|
+
value: record[key]
|
|
258
|
+
} : { exists: false };
|
|
259
|
+
}
|
|
260
|
+
function sameValueState(left, right) {
|
|
261
|
+
if (left.exists !== right.exists) return false;
|
|
262
|
+
if (!left.exists) return true;
|
|
263
|
+
return sameJson(left.value, right.value);
|
|
264
|
+
}
|
|
265
|
+
function sameJson(left, right) {
|
|
266
|
+
return stringify(left) === stringify(right);
|
|
267
|
+
}
|
|
268
|
+
function clone(value) {
|
|
269
|
+
if (value === void 0) return void 0;
|
|
270
|
+
return JSON.parse(JSON.stringify(value));
|
|
271
|
+
}
|
|
272
|
+
function isRecord(value) {
|
|
273
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
274
|
+
}
|
|
275
|
+
function hasOnlyKeys(value, allowedKeys) {
|
|
276
|
+
return Object.keys(value).every((key) => allowedKeys.has(key));
|
|
277
|
+
}
|
|
278
|
+
//#endregion
|
|
279
|
+
export { mergeGtJson, mergeGtLockJson, runMergeDriver };
|
|
280
|
+
|
|
281
|
+
//# sourceMappingURL=mergeDrivers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mergeDrivers.js","names":[],"sources":["../../src/git/mergeDrivers.ts"],"sourcesContent":["import fs from 'node:fs';\nimport stringify from 'fast-json-stable-stringify';\nimport type {\n DownloadedTranslation,\n DownloadedVersionEntry,\n DownloadedVersions,\n} from '../fs/config/downloadedVersions.js';\n\nexport type MergeDriverName = 'gt-lock' | 'gtjson';\n\nexport type MergeDriverResult =\n | {\n ok: true;\n content: string;\n }\n | {\n ok: false;\n reason: string;\n };\n\ntype ValueState = {\n exists: boolean;\n value?: unknown;\n};\n\nconst LOCK_TOP_LEVEL_KEYS = new Set(['version', 'branchId', 'entries']);\nconst LOCK_ENTRY_KEYS = new Set([\n 'fileId',\n 'versionId',\n 'fileName',\n 'staged',\n 'translations',\n]);\nconst LOCK_TRANSLATION_KEYS = new Set([\n 'updatedAt',\n 'postProcessHash',\n 'fileName',\n]);\n\nexport function runMergeDriver(\n driverName: MergeDriverName,\n basePath: string,\n oursPath: string,\n theirsPath: string\n): MergeDriverResult {\n const base = readMergeFile(basePath);\n const ours = readMergeFile(oursPath);\n const theirs = readMergeFile(theirsPath);\n\n const result =\n driverName === 'gt-lock'\n ? mergeGtLockJson(base, ours, theirs)\n : mergeGtJson(base, ours, theirs);\n\n if (result.ok) {\n fs.writeFileSync(oursPath, result.content, 'utf8');\n }\n return result;\n}\n\nexport function mergeGtJson(\n baseRaw: string,\n oursRaw: string,\n theirsRaw: string\n): MergeDriverResult {\n const base = parseRootObject(baseRaw, 'base');\n const ours = parseRootObject(oursRaw, 'ours');\n const theirs = parseRootObject(theirsRaw, 'theirs');\n\n if (!base.ok) return base;\n if (!ours.ok) return ours;\n if (!theirs.ok) return theirs;\n\n const merged: Record<string, unknown> = {};\n // Preserve ours-side key order to avoid reordering the whole file on merge.\n // Keys present only in base were deleted on both sides and never survive.\n const keys = [\n ...Object.keys(ours.value),\n ...Object.keys(theirs.value).filter(\n (key) => !Object.prototype.hasOwnProperty.call(ours.value, key)\n ),\n ];\n\n for (const key of keys) {\n const baseValue = getValueState(base.value, key);\n const oursValue = getValueState(ours.value, key);\n const theirsValue = getValueState(theirs.value, key);\n\n if (sameValueState(oursValue, theirsValue)) {\n if (oursValue.exists) merged[key] = oursValue.value;\n continue;\n }\n\n if (sameValueState(oursValue, baseValue)) {\n if (theirsValue.exists) merged[key] = theirsValue.value;\n continue;\n }\n\n if (sameValueState(theirsValue, baseValue)) {\n if (oursValue.exists) merged[key] = oursValue.value;\n continue;\n }\n\n return {\n ok: false,\n reason: `Conflicting GTJSON changes for key \"${key}\"`,\n };\n }\n\n return { ok: true, content: `${JSON.stringify(merged, null, 2)}\\n` };\n}\n\nexport function mergeGtLockJson(\n baseRaw: string,\n oursRaw: string,\n theirsRaw: string\n): MergeDriverResult {\n const base = parseLockfile(baseRaw, 'base');\n const ours = parseLockfile(oursRaw, 'ours');\n const theirs = parseLockfile(theirsRaw, 'theirs');\n\n if (!base.ok) return base;\n if (!ours.ok) return ours;\n if (!theirs.ok) return theirs;\n\n const baseMap = buildEntryMap(base.value.entries);\n const oursMap = buildEntryMap(ours.value.entries);\n const theirsMap = buildEntryMap(theirs.value.entries);\n\n if (!baseMap.ok) return baseMap;\n if (!oursMap.ok) return oursMap;\n if (!theirsMap.ok) return theirsMap;\n\n const fileIds = new Set([\n ...baseMap.value.keys(),\n ...oursMap.value.keys(),\n ...theirsMap.value.keys(),\n ]);\n\n const entries: DownloadedVersionEntry[] = [];\n for (const fileId of Array.from(fileIds).sort()) {\n const entry = mergeLockEntry(\n baseMap.value.get(fileId),\n oursMap.value.get(fileId),\n theirsMap.value.get(fileId)\n );\n if (entry) entries.push(normalizeEntry(entry));\n }\n\n const merged: DownloadedVersions = {\n version: 2,\n branchId: ours.value.branchId || theirs.value.branchId,\n entries,\n };\n // Plain stringify keeps the same key order writeLockfile produces, so the\n // next gt run does not rewrite the merged file into a noisy diff\n return { ok: true, content: `${JSON.stringify(merged, null, 2)}\\n` };\n}\n\n// Matches the key order findOrCreateEntry/writeStagedEntries produce\nfunction normalizeEntry(entry: DownloadedVersionEntry): DownloadedVersionEntry {\n const normalized: DownloadedVersionEntry = {\n fileId: entry.fileId,\n versionId: entry.versionId,\n translations: entry.translations,\n };\n if (entry.fileName !== undefined) normalized.fileName = entry.fileName;\n if (entry.staged !== undefined) normalized.staged = entry.staged;\n return normalized;\n}\n\nfunction mergeLockEntry(\n base: DownloadedVersionEntry | undefined,\n ours: DownloadedVersionEntry | undefined,\n theirs: DownloadedVersionEntry | undefined\n): DownloadedVersionEntry | undefined {\n if (sameJson(ours, theirs)) return clone(ours);\n // Entry missing on one side: the deletion wins only when the surviving\n // side still matches base; a changed entry beats a deletion\n if (!ours) return sameJson(theirs, base) ? undefined : clone(theirs);\n if (!theirs) return sameJson(ours, base) ? undefined : clone(ours);\n\n if (ours.versionId !== theirs.versionId) {\n if (sameJson(ours, base)) return clone(theirs);\n if (sameJson(theirs, base)) return clone(ours);\n return clone(selectLatestEntry(ours, theirs));\n }\n\n // Resolve scalar fields three-way against base individually; taking them\n // from the latest-timestamp entry would let a download on one side (which\n // bumps timestamps) silently drop a fresh stage on the other\n const latest = selectLatestEntry(ours, theirs);\n const merged: DownloadedVersionEntry = {\n fileId: ours.fileId,\n versionId: ours.versionId,\n translations: mergeTranslations(\n base?.translations ?? {},\n ours.translations,\n theirs.translations\n ),\n };\n\n const fileName = mergeScalarField(\n base?.fileName,\n ours.fileName,\n theirs.fileName,\n latest.fileName\n );\n if (fileName !== undefined) merged.fileName = fileName;\n\n const staged = mergeScalarField(\n base?.staged,\n ours.staged,\n theirs.staged,\n latest.staged\n );\n if (staged !== undefined) merged.staged = staged;\n\n return merged;\n}\n\n// The side that changed from base wins; both changed differently → fallback\nfunction mergeScalarField<T extends string | boolean>(\n base: T | undefined,\n ours: T | undefined,\n theirs: T | undefined,\n fallback: T | undefined\n): T | undefined {\n if (ours === theirs) return ours;\n if (ours === base) return theirs;\n if (theirs === base) return ours;\n return fallback;\n}\n\nfunction mergeTranslations(\n base: Record<string, DownloadedTranslation>,\n ours: Record<string, DownloadedTranslation>,\n theirs: Record<string, DownloadedTranslation>\n): Record<string, DownloadedTranslation> {\n const result: Record<string, DownloadedTranslation> = {};\n const locales = new Set([\n ...Object.keys(base),\n ...Object.keys(ours),\n ...Object.keys(theirs),\n ]);\n\n for (const locale of Array.from(locales).sort()) {\n const baseValue = base[locale];\n const oursValue = ours[locale];\n const theirsValue = theirs[locale];\n\n if (oursValue && theirsValue) {\n result[locale] = sameJson(oursValue, theirsValue)\n ? clone(oursValue)!\n : clone(selectLatestTranslation(oursValue, theirsValue))!;\n } else if (oursValue) {\n // Locale missing on theirs: honor the deletion only when ours kept base\n if (!(baseValue && sameJson(oursValue, baseValue))) {\n result[locale] = clone(oursValue)!;\n }\n } else if (theirsValue) {\n if (!(baseValue && sameJson(theirsValue, baseValue))) {\n result[locale] = clone(theirsValue)!;\n }\n }\n }\n\n return result;\n}\n\nfunction parseRootObject(\n raw: string,\n label: string\n):\n | {\n ok: true;\n value: Record<string, unknown>;\n }\n | {\n ok: false;\n reason: string;\n } {\n if (!raw.trim()) return { ok: true, value: {} };\n\n let value: unknown;\n try {\n value = JSON.parse(raw);\n } catch {\n return { ok: false, reason: `Could not parse ${label} JSON` };\n }\n\n if (!isRecord(value)) {\n return { ok: false, reason: `${label} JSON must be an object` };\n }\n\n return { ok: true, value };\n}\n\nfunction parseLockfile(\n raw: string,\n label: string\n):\n | {\n ok: true;\n value: DownloadedVersions;\n }\n | {\n ok: false;\n reason: string;\n } {\n if (!raw.trim()) {\n return { ok: true, value: { version: 2, branchId: '', entries: [] } };\n }\n\n const parsed = parseRootObject(raw, label);\n if (!parsed.ok) return parsed;\n\n const validationError = validateLockfile(parsed.value, label);\n if (validationError) {\n return { ok: false, reason: validationError };\n }\n\n return { ok: true, value: parsed.value as DownloadedVersions };\n}\n\nfunction validateLockfile(\n value: Record<string, unknown>,\n label: string\n): string | null {\n if (!hasOnlyKeys(value, LOCK_TOP_LEVEL_KEYS)) {\n return `${label} gt-lock.json contains unsupported top-level fields`;\n }\n if (value.version !== 2) {\n return `${label} gt-lock.json must use version 2`;\n }\n if (typeof value.branchId !== 'string') {\n return `${label} gt-lock.json must contain a branchId string`;\n }\n if (!Array.isArray(value.entries)) {\n return `${label} gt-lock.json must contain an entries array`;\n }\n\n for (const entry of value.entries) {\n if (!isRecord(entry)) {\n return `${label} gt-lock.json contains a non-object entry`;\n }\n if (!hasOnlyKeys(entry, LOCK_ENTRY_KEYS)) {\n return `${label} gt-lock.json entry contains unsupported fields`;\n }\n if (typeof entry.fileId !== 'string') {\n return `${label} gt-lock.json entry must contain a fileId string`;\n }\n if (typeof entry.versionId !== 'string') {\n return `${label} gt-lock.json entry must contain a versionId string`;\n }\n if (entry.fileName !== undefined && typeof entry.fileName !== 'string') {\n return `${label} gt-lock.json entry fileName must be a string`;\n }\n if (entry.staged !== undefined && typeof entry.staged !== 'boolean') {\n return `${label} gt-lock.json entry staged must be a boolean`;\n }\n if (!isRecord(entry.translations)) {\n return `${label} gt-lock.json entry translations must be an object`;\n }\n for (const translation of Object.values(entry.translations)) {\n if (!isRecord(translation)) {\n return `${label} gt-lock.json translation must be an object`;\n }\n if (!hasOnlyKeys(translation, LOCK_TRANSLATION_KEYS)) {\n return `${label} gt-lock.json translation contains unsupported fields`;\n }\n for (const field of LOCK_TRANSLATION_KEYS) {\n if (\n translation[field] !== undefined &&\n typeof translation[field] !== 'string'\n ) {\n return `${label} gt-lock.json translation ${field} must be a string`;\n }\n }\n }\n }\n\n return null;\n}\n\nfunction buildEntryMap(entries: DownloadedVersionEntry[]):\n | {\n ok: true;\n value: Map<string, DownloadedVersionEntry>;\n }\n | {\n ok: false;\n reason: string;\n } {\n const map = new Map<string, DownloadedVersionEntry>();\n for (const entry of entries) {\n if (map.has(entry.fileId)) {\n return {\n ok: false,\n reason: `gt-lock.json contains duplicate fileId \"${entry.fileId}\"`,\n };\n }\n map.set(entry.fileId, entry);\n }\n return { ok: true, value: map };\n}\n\nfunction selectLatestEntry(\n ours: DownloadedVersionEntry,\n theirs: DownloadedVersionEntry\n): DownloadedVersionEntry {\n return maxEntryTimestamp(theirs) > maxEntryTimestamp(ours) ? theirs : ours;\n}\n\nfunction maxEntryTimestamp(entry: DownloadedVersionEntry): number {\n return Math.max(\n 0,\n ...Object.values(entry.translations).map((translation) =>\n parseTimestamp(translation.updatedAt)\n )\n );\n}\n\nfunction selectLatestTranslation(\n ours: DownloadedTranslation,\n theirs: DownloadedTranslation\n): DownloadedTranslation {\n return parseTimestamp(theirs.updatedAt) > parseTimestamp(ours.updatedAt)\n ? theirs\n : ours;\n}\n\nfunction parseTimestamp(value: string | undefined): number {\n if (!value) return 0;\n const parsed = Date.parse(value);\n return Number.isNaN(parsed) ? 0 : parsed;\n}\n\nfunction readMergeFile(filepath: string): string {\n try {\n return fs.readFileSync(filepath, 'utf8');\n } catch {\n return '';\n }\n}\n\nfunction getValueState(\n record: Record<string, unknown>,\n key: string\n): ValueState {\n return Object.prototype.hasOwnProperty.call(record, key)\n ? { exists: true, value: record[key] }\n : { exists: false };\n}\n\nfunction sameValueState(left: ValueState, right: ValueState): boolean {\n if (left.exists !== right.exists) return false;\n if (!left.exists) return true;\n return sameJson(left.value, right.value);\n}\n\nfunction sameJson(left: unknown, right: unknown): boolean {\n return stringify(left) === stringify(right);\n}\n\nfunction clone<T>(value: T | undefined): T | undefined {\n if (value === undefined) return undefined;\n return JSON.parse(JSON.stringify(value)) as T;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction hasOnlyKeys(\n value: Record<string, unknown>,\n allowedKeys: Set<string>\n): boolean {\n return Object.keys(value).every((key) => allowedKeys.has(key));\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Settings } from '../types/index.js';
|
|
2
|
+
export type GitMergeDriverSetupOptions = {
|
|
3
|
+
cwd?: string;
|
|
4
|
+
dryRun?: boolean;
|
|
5
|
+
omitConfigIds?: boolean;
|
|
6
|
+
driverCommand?: string;
|
|
7
|
+
};
|
|
8
|
+
export type GitMergeDriverSetupResult = {
|
|
9
|
+
gitRoot: string;
|
|
10
|
+
gitattributesPath: string;
|
|
11
|
+
addedAttributes: string[];
|
|
12
|
+
gitConfigCommands: string[][];
|
|
13
|
+
updatedConfig: boolean;
|
|
14
|
+
dryRun: boolean;
|
|
15
|
+
warnings: string[];
|
|
16
|
+
};
|
|
17
|
+
export declare function setupGitMergeDrivers(settings: Settings, options?: GitMergeDriverSetupOptions): Promise<GitMergeDriverSetupResult>;
|
|
18
|
+
export declare function getGitAttributesEntries(settings: Settings, gitRoot: string, cwd?: string): {
|
|
19
|
+
pattern: string;
|
|
20
|
+
driver: string;
|
|
21
|
+
}[];
|
|
22
|
+
export declare function mergeGitAttributes(existingContent: string, linesToAdd: string[]): {
|
|
23
|
+
content: string;
|
|
24
|
+
added: string[];
|
|
25
|
+
};
|
|
26
|
+
export declare function getGitConfigCommands(driverCommand: string): string[][];
|
|
27
|
+
export declare function getDefaultDriverCommand(cwd?: string): string;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import updateConfig from "../fs/config/updateConfig.js";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import { createDiagnosticMessage } from "generaltranslation/internal";
|
|
5
|
+
import { execFile } from "node:child_process";
|
|
6
|
+
import { promisify } from "node:util";
|
|
7
|
+
//#region src/git/setupMergeDrivers.ts
|
|
8
|
+
const execFileAsync = promisify(execFile);
|
|
9
|
+
const GT_LOCK_DRIVER = "merge=gt-lock";
|
|
10
|
+
const GTJSON_DRIVER = "merge=gtjson";
|
|
11
|
+
async function setupGitMergeDrivers(settings, options = {}) {
|
|
12
|
+
const cwd = options.cwd ?? process.cwd();
|
|
13
|
+
const gitRoot = await getGitRoot(cwd);
|
|
14
|
+
const gitattributesPath = path.join(gitRoot, ".gitattributes");
|
|
15
|
+
const entries = getGitAttributesEntries(settings, gitRoot, cwd);
|
|
16
|
+
const attributesUpdate = mergeGitAttributes(fs.existsSync(gitattributesPath) ? fs.readFileSync(gitattributesPath, "utf8") : "", entries.map(formatAttributeEntry));
|
|
17
|
+
const gitConfigCommands = getGitConfigCommands(options.driverCommand ?? getDefaultDriverCommand(cwd));
|
|
18
|
+
if (!options.dryRun) {
|
|
19
|
+
if (attributesUpdate.added.length > 0) fs.writeFileSync(gitattributesPath, attributesUpdate.content, "utf8");
|
|
20
|
+
for (const args of gitConfigCommands) await execFileAsync("git", [
|
|
21
|
+
"config",
|
|
22
|
+
"--local",
|
|
23
|
+
...args
|
|
24
|
+
], {
|
|
25
|
+
cwd: gitRoot,
|
|
26
|
+
windowsHide: true
|
|
27
|
+
});
|
|
28
|
+
if (options.omitConfigIds) await updateConfig(settings.config, {
|
|
29
|
+
omitConfigIds: true,
|
|
30
|
+
_versionId: null,
|
|
31
|
+
_branchId: null
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
gitRoot,
|
|
36
|
+
gitattributesPath,
|
|
37
|
+
addedAttributes: attributesUpdate.added,
|
|
38
|
+
gitConfigCommands,
|
|
39
|
+
updatedConfig: options.omitConfigIds === true,
|
|
40
|
+
dryRun: options.dryRun === true,
|
|
41
|
+
warnings: getLockfileWarnings(cwd)
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function getLockfileWarnings(cwd) {
|
|
45
|
+
const lockfilePath = path.join(cwd, "gt-lock.json");
|
|
46
|
+
if (!fs.existsSync(lockfilePath)) return [];
|
|
47
|
+
let version;
|
|
48
|
+
try {
|
|
49
|
+
version = JSON.parse(fs.readFileSync(lockfilePath, "utf8"))?.version;
|
|
50
|
+
} catch {
|
|
51
|
+
version = void 0;
|
|
52
|
+
}
|
|
53
|
+
if (version === 2) return [];
|
|
54
|
+
return [createDiagnosticMessage({
|
|
55
|
+
whatHappened: "The gt-lock merge driver will fall back to a normal Git conflict",
|
|
56
|
+
why: "gt-lock.json is not in the version 2 lockfile format",
|
|
57
|
+
fix: "Run gt translate or gt stage to regenerate the lockfile"
|
|
58
|
+
})];
|
|
59
|
+
}
|
|
60
|
+
function getGitAttributesEntries(settings, gitRoot, cwd = process.cwd()) {
|
|
61
|
+
const entries = [];
|
|
62
|
+
const normalizedGitRoot = normalizePathForGitAttributes(gitRoot);
|
|
63
|
+
const normalizedCwd = normalizePathForGitAttributes(cwd);
|
|
64
|
+
const lockPattern = toGitAttributePattern(path.relative(normalizedGitRoot, path.join(normalizedCwd, "gt-lock.json")));
|
|
65
|
+
if (lockPattern) entries.push({
|
|
66
|
+
pattern: lockPattern,
|
|
67
|
+
driver: GT_LOCK_DRIVER
|
|
68
|
+
});
|
|
69
|
+
const gtJsonPath = settings.files?.placeholderPaths.gt;
|
|
70
|
+
if (gtJsonPath) {
|
|
71
|
+
const normalizedGtJsonPath = normalizePathForGitAttributes(gtJsonPath);
|
|
72
|
+
const gtJsonPattern = toGitAttributePattern(path.relative(normalizedGitRoot, normalizedGtJsonPath).replace(/\[locale\]/g, "*"));
|
|
73
|
+
if (gtJsonPattern) entries.push({
|
|
74
|
+
pattern: gtJsonPattern,
|
|
75
|
+
driver: GTJSON_DRIVER
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return entries;
|
|
79
|
+
}
|
|
80
|
+
function mergeGitAttributes(existingContent, linesToAdd) {
|
|
81
|
+
const existingLines = existingContent.split(/\r?\n/).filter((line) => line.trim().length > 0);
|
|
82
|
+
const existing = new Set(existingLines.map((line) => line.trim()));
|
|
83
|
+
const added = linesToAdd.filter((line) => !existing.has(line.trim()));
|
|
84
|
+
if (added.length === 0) return {
|
|
85
|
+
content: existingContent,
|
|
86
|
+
added
|
|
87
|
+
};
|
|
88
|
+
return {
|
|
89
|
+
content: `${existingContent}${existingContent.length > 0 && !existingContent.endsWith("\n") ? "\n" : ""}${added.join("\n")}\n`,
|
|
90
|
+
added
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function getGitConfigCommands(driverCommand) {
|
|
94
|
+
return [
|
|
95
|
+
["merge.gt-lock.name", "GT lockfile merge driver"],
|
|
96
|
+
["merge.gt-lock.driver", `${driverCommand} git merge-driver gt-lock %O %A %B %P`],
|
|
97
|
+
["merge.gtjson.name", "GTJSON merge driver"],
|
|
98
|
+
["merge.gtjson.driver", `${driverCommand} git merge-driver gtjson %O %A %B %P`]
|
|
99
|
+
];
|
|
100
|
+
}
|
|
101
|
+
function getDefaultDriverCommand(cwd = process.cwd()) {
|
|
102
|
+
const localBin = path.join(cwd, "node_modules", ".bin", "gt");
|
|
103
|
+
if (fs.existsSync(localBin)) return quoteShellArg(localBin.split(path.sep).join("/"));
|
|
104
|
+
return "gt";
|
|
105
|
+
}
|
|
106
|
+
function formatAttributeEntry(entry) {
|
|
107
|
+
return `${entry.pattern} ${entry.driver}`;
|
|
108
|
+
}
|
|
109
|
+
function toGitAttributePattern(relativePath) {
|
|
110
|
+
if (!relativePath || relativePath.startsWith("..") || path.isAbsolute(relativePath)) return null;
|
|
111
|
+
const pattern = relativePath.split(path.sep).join("/");
|
|
112
|
+
if (!/[\s"]/.test(pattern)) return pattern.replace(/\\/g, "\\\\");
|
|
113
|
+
return `"${pattern.replace(/["\\]/g, (char) => char === "\\" ? "\\\\\\\\" : "\\\"")}"`;
|
|
114
|
+
}
|
|
115
|
+
function quoteShellArg(value) {
|
|
116
|
+
return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
|
|
117
|
+
}
|
|
118
|
+
function normalizePathForGitAttributes(filepath) {
|
|
119
|
+
const absolutePath = path.resolve(filepath);
|
|
120
|
+
let existingPath = absolutePath;
|
|
121
|
+
while (!fs.existsSync(existingPath)) {
|
|
122
|
+
const parentPath = path.dirname(existingPath);
|
|
123
|
+
if (parentPath === existingPath) return absolutePath;
|
|
124
|
+
existingPath = parentPath;
|
|
125
|
+
}
|
|
126
|
+
try {
|
|
127
|
+
return path.join(fs.realpathSync(existingPath), path.relative(existingPath, absolutePath));
|
|
128
|
+
} catch {
|
|
129
|
+
return absolutePath;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async function getGitRoot(cwd) {
|
|
133
|
+
try {
|
|
134
|
+
const { stdout } = await execFileAsync("git", ["rev-parse", "--show-toplevel"], {
|
|
135
|
+
cwd,
|
|
136
|
+
encoding: "utf8",
|
|
137
|
+
windowsHide: true
|
|
138
|
+
});
|
|
139
|
+
return stdout.trim();
|
|
140
|
+
} catch {
|
|
141
|
+
throw new Error("No Git repository found. Run this command from a Git worktree.");
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
//#endregion
|
|
145
|
+
export { getDefaultDriverCommand, getGitAttributesEntries, getGitConfigCommands, mergeGitAttributes, setupGitMergeDrivers };
|
|
146
|
+
|
|
147
|
+
//# sourceMappingURL=setupMergeDrivers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setupMergeDrivers.js","names":[],"sources":["../../src/git/setupMergeDrivers.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { createDiagnosticMessage } from 'generaltranslation/internal';\nimport type { Settings } from '../types/index.js';\nimport updateConfig from '../fs/config/updateConfig.js';\n\nconst execFileAsync = promisify(execFile);\n\nexport type GitMergeDriverSetupOptions = {\n cwd?: string;\n dryRun?: boolean;\n omitConfigIds?: boolean;\n driverCommand?: string;\n};\n\nexport type GitMergeDriverSetupResult = {\n gitRoot: string;\n gitattributesPath: string;\n addedAttributes: string[];\n gitConfigCommands: string[][];\n updatedConfig: boolean;\n dryRun: boolean;\n warnings: string[];\n};\n\nconst GT_LOCK_DRIVER = 'merge=gt-lock';\nconst GTJSON_DRIVER = 'merge=gtjson';\n\nexport async function setupGitMergeDrivers(\n settings: Settings,\n options: GitMergeDriverSetupOptions = {}\n): Promise<GitMergeDriverSetupResult> {\n const cwd = options.cwd ?? process.cwd();\n const gitRoot = await getGitRoot(cwd);\n const gitattributesPath = path.join(gitRoot, '.gitattributes');\n const entries = getGitAttributesEntries(settings, gitRoot, cwd);\n const existingAttributes = fs.existsSync(gitattributesPath)\n ? fs.readFileSync(gitattributesPath, 'utf8')\n : '';\n const attributesUpdate = mergeGitAttributes(\n existingAttributes,\n entries.map(formatAttributeEntry)\n );\n const driverCommand = options.driverCommand ?? getDefaultDriverCommand(cwd);\n const gitConfigCommands = getGitConfigCommands(driverCommand);\n\n if (!options.dryRun) {\n if (attributesUpdate.added.length > 0) {\n fs.writeFileSync(gitattributesPath, attributesUpdate.content, 'utf8');\n }\n\n for (const args of gitConfigCommands) {\n await execFileAsync('git', ['config', '--local', ...args], {\n cwd: gitRoot,\n windowsHide: true,\n });\n }\n\n if (options.omitConfigIds) {\n await updateConfig(settings.config, {\n omitConfigIds: true,\n _versionId: null,\n _branchId: null,\n });\n }\n }\n\n return {\n gitRoot,\n gitattributesPath,\n addedAttributes: attributesUpdate.added,\n gitConfigCommands,\n updatedConfig: options.omitConfigIds === true,\n dryRun: options.dryRun === true,\n warnings: getLockfileWarnings(cwd),\n };\n}\n\nfunction getLockfileWarnings(cwd: string): string[] {\n const lockfilePath = path.join(cwd, 'gt-lock.json');\n if (!fs.existsSync(lockfilePath)) return [];\n\n let version: unknown;\n try {\n version = JSON.parse(fs.readFileSync(lockfilePath, 'utf8'))?.version;\n } catch {\n version = undefined;\n }\n if (version === 2) return [];\n\n return [\n createDiagnosticMessage({\n whatHappened:\n 'The gt-lock merge driver will fall back to a normal Git conflict',\n why: 'gt-lock.json is not in the version 2 lockfile format',\n fix: 'Run gt translate or gt stage to regenerate the lockfile',\n }),\n ];\n}\n\nexport function getGitAttributesEntries(\n settings: Settings,\n gitRoot: string,\n cwd: string = process.cwd()\n): { pattern: string; driver: string }[] {\n const entries: { pattern: string; driver: string }[] = [];\n const normalizedGitRoot = normalizePathForGitAttributes(gitRoot);\n const normalizedCwd = normalizePathForGitAttributes(cwd);\n const lockPattern = toGitAttributePattern(\n path.relative(normalizedGitRoot, path.join(normalizedCwd, 'gt-lock.json'))\n );\n if (lockPattern) {\n entries.push({ pattern: lockPattern, driver: GT_LOCK_DRIVER });\n }\n\n const gtJsonPath = settings.files?.placeholderPaths.gt;\n if (gtJsonPath) {\n const normalizedGtJsonPath = normalizePathForGitAttributes(gtJsonPath);\n const gtJsonPattern = toGitAttributePattern(\n path\n .relative(normalizedGitRoot, normalizedGtJsonPath)\n .replace(/\\[locale\\]/g, '*')\n );\n if (gtJsonPattern) {\n entries.push({ pattern: gtJsonPattern, driver: GTJSON_DRIVER });\n }\n }\n\n return entries;\n}\n\nexport function mergeGitAttributes(\n existingContent: string,\n linesToAdd: string[]\n): { content: string; added: string[] } {\n const existingLines = existingContent\n .split(/\\r?\\n/)\n .filter((line) => line.trim().length > 0);\n const existing = new Set(existingLines.map((line) => line.trim()));\n const added = linesToAdd.filter((line) => !existing.has(line.trim()));\n\n if (added.length === 0) {\n return { content: existingContent, added };\n }\n\n const prefix =\n existingContent.length > 0 && !existingContent.endsWith('\\n') ? '\\n' : '';\n const content = `${existingContent}${prefix}${added.join('\\n')}\\n`;\n return { content, added };\n}\n\nexport function getGitConfigCommands(driverCommand: string): string[][] {\n return [\n ['merge.gt-lock.name', 'GT lockfile merge driver'],\n [\n 'merge.gt-lock.driver',\n `${driverCommand} git merge-driver gt-lock %O %A %B %P`,\n ],\n ['merge.gtjson.name', 'GTJSON merge driver'],\n [\n 'merge.gtjson.driver',\n `${driverCommand} git merge-driver gtjson %O %A %B %P`,\n ],\n ];\n}\n\nexport function getDefaultDriverCommand(cwd: string = process.cwd()): string {\n // Git invokes merge drivers through sh on every platform, including\n // Windows, so use the extensionless sh shim with forward slashes rather\n // than gt.cmd.\n const localBin = path.join(cwd, 'node_modules', '.bin', 'gt');\n if (fs.existsSync(localBin)) {\n return quoteShellArg(localBin.split(path.sep).join('/'));\n }\n return 'gt';\n}\n\nfunction formatAttributeEntry(entry: {\n pattern: string;\n driver: string;\n}): string {\n return `${entry.pattern} ${entry.driver}`;\n}\n\nfunction toGitAttributePattern(relativePath: string): string | null {\n if (\n !relativePath ||\n relativePath.startsWith('..') ||\n path.isAbsolute(relativePath)\n ) {\n return null;\n }\n const pattern = relativePath.split(path.sep).join('/');\n if (!/[\\s\"]/.test(pattern)) {\n // Escape literal backslashes for the glob layer\n return pattern.replace(/\\\\/g, '\\\\\\\\');\n }\n // gitattributes splits fields on whitespace without honoring backslash\n // escapes, so patterns containing whitespace must be C-style double-quoted\n // (supported since Git 2.32). Quoted patterns decode twice — C-style\n // unquoting, then glob matching — so one literal backslash becomes four.\n const quoted = pattern.replace(/[\"\\\\]/g, (char) =>\n char === '\\\\' ? '\\\\\\\\\\\\\\\\' : '\\\\\"'\n );\n return `\"${quoted}\"`;\n}\n\nfunction quoteShellArg(value: string): string {\n return `\"${value.replace(/([\"\\\\$`])/g, '\\\\$1')}\"`;\n}\n\nfunction normalizePathForGitAttributes(filepath: string): string {\n const absolutePath = path.resolve(filepath);\n let existingPath = absolutePath;\n\n while (!fs.existsSync(existingPath)) {\n const parentPath = path.dirname(existingPath);\n if (parentPath === existingPath) {\n return absolutePath;\n }\n existingPath = parentPath;\n }\n\n try {\n return path.join(\n fs.realpathSync(existingPath),\n path.relative(existingPath, absolutePath)\n );\n } catch {\n return absolutePath;\n }\n}\n\nasync function getGitRoot(cwd: string): Promise<string> {\n try {\n const { stdout } = await execFileAsync(\n 'git',\n ['rev-parse', '--show-toplevel'],\n {\n cwd,\n encoding: 'utf8',\n windowsHide: true,\n }\n );\n return stdout.trim();\n } catch {\n throw new Error(\n 'No Git repository found. Run this command from a Git worktree.'\n );\n }\n}\n"],"mappings":"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"}
|
|
@@ -26,13 +26,11 @@ async function handleInitGT(filepath, errors, warnings, filesUpdated, packageJso
|
|
|
26
26
|
tsconfigJson
|
|
27
27
|
});
|
|
28
28
|
let hasGTConfig = false;
|
|
29
|
-
let hasInitGT = false;
|
|
30
29
|
traverse(ast, {
|
|
31
30
|
ImportDeclaration(path) {
|
|
32
31
|
if (path.node.source.value === "gt-next/config") path.node.specifiers.forEach((spec) => {
|
|
33
32
|
if (t.isImportSpecifier(spec)) {
|
|
34
33
|
if (spec.local.name === "withGTConfig") hasGTConfig = true;
|
|
35
|
-
if (spec.local.name === "initGT") hasInitGT = true;
|
|
36
34
|
}
|
|
37
35
|
});
|
|
38
36
|
},
|
|
@@ -41,22 +39,19 @@ async function handleInitGT(filepath, errors, warnings, filesUpdated, packageJso
|
|
|
41
39
|
if (t.isVariableDeclarator(dec)) {
|
|
42
40
|
if (t.isCallExpression(dec.init) && t.isIdentifier(dec.init.callee, { name: "require" }) && t.isStringLiteral(dec.init.arguments[0], { value: "gt-next/config" })) {
|
|
43
41
|
if (t.isIdentifier(dec.id, { name: "withGTConfig" })) hasGTConfig = true;
|
|
44
|
-
if (t.isIdentifier(dec.id, { name: "initGT" })) hasInitGT = true;
|
|
45
42
|
if (t.isObjectPattern(dec.id)) dec.id.properties.forEach((prop) => {
|
|
46
43
|
if (t.isObjectProperty(prop) && t.isIdentifier(prop.key) && t.isIdentifier(prop.value)) {
|
|
47
44
|
if (prop.key.name === "withGTConfig") hasGTConfig = true;
|
|
48
|
-
if (prop.key.name === "initGT") hasInitGT = true;
|
|
49
45
|
}
|
|
50
46
|
});
|
|
51
47
|
} else if (t.isMemberExpression(dec.init) && t.isCallExpression(dec.init.object) && t.isIdentifier(dec.init.object.callee, { name: "require" }) && t.isStringLiteral(dec.init.object.arguments[0], { value: "gt-next/config" })) {
|
|
52
48
|
if (t.isIdentifier(dec.id, { name: "withGTConfig" }) && t.isIdentifier(dec.init.property, { name: "withGTConfig" })) hasGTConfig = true;
|
|
53
|
-
if (t.isIdentifier(dec.id, { name: "initGT" }) && t.isIdentifier(dec.init.property, { name: "initGT" })) hasInitGT = true;
|
|
54
49
|
}
|
|
55
50
|
}
|
|
56
51
|
});
|
|
57
52
|
}
|
|
58
53
|
});
|
|
59
|
-
if (hasGTConfig
|
|
54
|
+
if (hasGTConfig) return;
|
|
60
55
|
ast.program.body.unshift(cjsEnabled ? t.variableDeclaration("const", [t.variableDeclarator(t.identifier("withGTConfig"), t.memberExpression(t.callExpression(t.identifier("require"), [t.stringLiteral("gt-next/config")]), t.identifier("withGTConfig")))]) : t.importDeclaration([t.importSpecifier(t.identifier("withGTConfig"), t.identifier("withGTConfig"))], t.stringLiteral("gt-next/config")));
|
|
61
56
|
traverse(ast, { ExportDefaultDeclaration(path) {
|
|
62
57
|
const oldExport = path.node.declaration;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handleInitGT.js","names":[],"sources":["../../../src/next/parse/handleInitGT.ts"],"sourcesContent":["import fs from 'node:fs';\nimport { parse } from '@babel/parser';\nimport generateModule from '@babel/generator';\nimport traverseModule from '@babel/traverse';\n\n// Handle CommonJS/ESM interop\nconst traverse = traverseModule.default || traverseModule;\nconst generate = generateModule.default || generateModule;\n\nimport * as t from '@babel/types';\nimport { logger } from '../../console/logger.js';\nimport { needsCJS } from '../../utils/parse/needsCJS.js';\n\nexport async function handleInitGT(\n filepath: string,\n errors: string[],\n warnings: string[],\n filesUpdated: string[],\n packageJson?: { type?: string },\n tsconfigJson?: { compilerOptions?: { module?: string } }\n) {\n const code = await fs.promises.readFile(filepath, 'utf8');\n\n let ast;\n try {\n ast = parse(code, {\n sourceType: 'module',\n plugins: ['jsx', 'typescript'],\n tokens: true,\n createParenthesizedExpressions: true,\n });\n\n // Get cjs or esm\n const cjsEnabled = needsCJS({\n ast,\n warnings,\n filepath,\n packageJson,\n tsconfigJson,\n });\n\n // Check if withGTConfig or initGT is already imported/required\n let hasGTConfig = false;\n let hasInitGT = false;\n traverse(ast, {\n ImportDeclaration(path) {\n if (path.node.source.value === 'gt-next/config') {\n path.node.specifiers.forEach((spec) => {\n if (t.isImportSpecifier(spec)) {\n if (spec.local.name === 'withGTConfig') hasGTConfig = true;\n if (spec.local.name === 'initGT') hasInitGT = true;\n }\n });\n }\n },\n VariableDeclaration(path) {\n path.node.declarations.forEach((dec) => {\n if (t.isVariableDeclarator(dec)) {\n // Handle destructuring: const { withGTConfig } = require('gt-next/config')\n if (\n t.isCallExpression(dec.init) &&\n t.isIdentifier(dec.init.callee, { name: 'require' }) &&\n t.isStringLiteral(dec.init.arguments[0], {\n value: 'gt-next/config',\n })\n ) {\n // Handle simple assignment: const withGTConfig = require(...)\n if (t.isIdentifier(dec.id, { name: 'withGTConfig' }))\n hasGTConfig = true;\n if (t.isIdentifier(dec.id, { name: 'initGT' })) hasInitGT = true;\n\n // Handle destructuring: const { withGTConfig } = require(...)\n if (t.isObjectPattern(dec.id)) {\n dec.id.properties.forEach((prop) => {\n if (\n t.isObjectProperty(prop) &&\n t.isIdentifier(prop.key) &&\n t.isIdentifier(prop.value)\n ) {\n if (prop.key.name === 'withGTConfig') hasGTConfig = true;\n if (prop.key.name === 'initGT') hasInitGT = true;\n }\n });\n }\n }\n // Handle member access: const withGTConfig = require('gt-next/config').withGTConfig\n else if (\n t.isMemberExpression(dec.init) &&\n t.isCallExpression(dec.init.object) &&\n t.isIdentifier(dec.init.object.callee, { name: 'require' }) &&\n t.isStringLiteral(dec.init.object.arguments[0], {\n value: 'gt-next/config',\n })\n ) {\n if (\n t.isIdentifier(dec.id, { name: 'withGTConfig' }) &&\n t.isIdentifier(dec.init.property, { name: 'withGTConfig' })\n ) {\n hasGTConfig = true;\n }\n if (\n t.isIdentifier(dec.id, { name: 'initGT' }) &&\n t.isIdentifier(dec.init.property, { name: 'initGT' })\n ) {\n hasInitGT = true;\n }\n }\n }\n });\n },\n });\n\n // Return early if either withGTConfig or initGT is already present\n if (hasGTConfig || hasInitGT) {\n return;\n }\n\n ast.program.body.unshift(\n cjsEnabled\n ? t.variableDeclaration('const', [\n t.variableDeclarator(\n t.identifier('withGTConfig'),\n t.memberExpression(\n t.callExpression(t.identifier('require'), [\n t.stringLiteral('gt-next/config'),\n ]),\n t.identifier('withGTConfig')\n )\n ),\n ])\n : t.importDeclaration(\n [\n t.importSpecifier(\n t.identifier('withGTConfig'),\n t.identifier('withGTConfig')\n ),\n ],\n t.stringLiteral('gt-next/config')\n )\n );\n\n // Find and transform the default export\n traverse(ast, {\n ExportDefaultDeclaration(path) {\n const oldExport = path.node.declaration;\n\n let exportExpression;\n if (t.isFunctionDeclaration(oldExport)) {\n exportExpression = t.functionExpression(\n oldExport.id,\n oldExport.params,\n oldExport.body,\n oldExport.generator,\n oldExport.async\n );\n } else if (t.isClassDeclaration(oldExport)) {\n exportExpression = t.classExpression(\n oldExport.id,\n oldExport.superClass,\n oldExport.body,\n oldExport.decorators\n );\n } else if (t.isTSDeclareFunction(oldExport)) {\n // For TypeScript declare functions, create an empty function expression\n // since declare functions don't have a runtime implementation\n warnings.push(\n `Found TypeScript declare function in ${filepath}. Converting to empty function.`\n );\n exportExpression = t.functionExpression(\n oldExport.id,\n oldExport.params,\n t.blockStatement([]),\n false,\n false\n );\n } else {\n exportExpression = oldExport;\n }\n\n // Validate that we have a valid Next.js config export\n if (\n !t.isObjectExpression(exportExpression) &&\n !t.isFunctionExpression(exportExpression) &&\n !t.isArrowFunctionExpression(exportExpression)\n ) {\n warnings.push(\n `Unexpected export type in ${filepath}. Next.js config should export an object or a function returning an object.`\n );\n }\n\n path.node.declaration = t.callExpression(t.identifier('withGTConfig'), [\n exportExpression,\n t.objectExpression([]),\n ]);\n },\n });\n\n // Generate the modified code\n const output = generate(\n ast,\n {\n retainLines: true,\n retainFunctionParens: true,\n comments: true,\n compact: 'auto',\n },\n code\n );\n\n // Post-process the output to fix import spacing\n let processedCode = output.code;\n // Add newline after the comment only\n processedCode = processedCode.replace(\n /((?:import\\s*{\\s*withGTConfig\\s*}\\s*from|const\\s*{\\s*withGTConfig\\s*}\\s*=\\s*require)\\s*['\"]gt-next\\/config['\"];?)/,\n '$1\\n'\n );\n\n // Write the modified code back to the file\n await fs.promises.writeFile(filepath, processedCode);\n filesUpdated.push(filepath);\n } catch (error) {\n logger.error(`Error parsing file ${filepath}: ${error}`);\n errors.push(`Failed to parse ${filepath}: ${error}`);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"handleInitGT.js","names":[],"sources":["../../../src/next/parse/handleInitGT.ts"],"sourcesContent":["import fs from 'node:fs';\nimport { parse } from '@babel/parser';\nimport generateModule from '@babel/generator';\nimport traverseModule from '@babel/traverse';\n\n// Handle CommonJS/ESM interop\nconst traverse = traverseModule.default || traverseModule;\nconst generate = generateModule.default || generateModule;\n\nimport * as t from '@babel/types';\nimport { logger } from '../../console/logger.js';\nimport { needsCJS } from '../../utils/parse/needsCJS.js';\n\nexport async function handleInitGT(\n filepath: string,\n errors: string[],\n warnings: string[],\n filesUpdated: string[],\n packageJson?: { type?: string },\n tsconfigJson?: { compilerOptions?: { module?: string } }\n) {\n const code = await fs.promises.readFile(filepath, 'utf8');\n\n let ast;\n try {\n ast = parse(code, {\n sourceType: 'module',\n plugins: ['jsx', 'typescript'],\n tokens: true,\n createParenthesizedExpressions: true,\n });\n\n // Get cjs or esm\n const cjsEnabled = needsCJS({\n ast,\n warnings,\n filepath,\n packageJson,\n tsconfigJson,\n });\n\n // Check if withGTConfig is already imported/required\n let hasGTConfig = false;\n traverse(ast, {\n ImportDeclaration(path) {\n if (path.node.source.value === 'gt-next/config') {\n path.node.specifiers.forEach((spec) => {\n if (t.isImportSpecifier(spec)) {\n if (spec.local.name === 'withGTConfig') hasGTConfig = true;\n }\n });\n }\n },\n VariableDeclaration(path) {\n path.node.declarations.forEach((dec) => {\n if (t.isVariableDeclarator(dec)) {\n // Handle destructuring: const { withGTConfig } = require('gt-next/config')\n if (\n t.isCallExpression(dec.init) &&\n t.isIdentifier(dec.init.callee, { name: 'require' }) &&\n t.isStringLiteral(dec.init.arguments[0], {\n value: 'gt-next/config',\n })\n ) {\n // Handle simple assignment: const withGTConfig = require(...)\n if (t.isIdentifier(dec.id, { name: 'withGTConfig' }))\n hasGTConfig = true;\n\n // Handle destructuring: const { withGTConfig } = require(...)\n if (t.isObjectPattern(dec.id)) {\n dec.id.properties.forEach((prop) => {\n if (\n t.isObjectProperty(prop) &&\n t.isIdentifier(prop.key) &&\n t.isIdentifier(prop.value)\n ) {\n if (prop.key.name === 'withGTConfig') hasGTConfig = true;\n }\n });\n }\n }\n // Handle member access: const withGTConfig = require('gt-next/config').withGTConfig\n else if (\n t.isMemberExpression(dec.init) &&\n t.isCallExpression(dec.init.object) &&\n t.isIdentifier(dec.init.object.callee, { name: 'require' }) &&\n t.isStringLiteral(dec.init.object.arguments[0], {\n value: 'gt-next/config',\n })\n ) {\n if (\n t.isIdentifier(dec.id, { name: 'withGTConfig' }) &&\n t.isIdentifier(dec.init.property, { name: 'withGTConfig' })\n ) {\n hasGTConfig = true;\n }\n }\n }\n });\n },\n });\n\n // Return early if withGTConfig is already present\n if (hasGTConfig) {\n return;\n }\n\n ast.program.body.unshift(\n cjsEnabled\n ? t.variableDeclaration('const', [\n t.variableDeclarator(\n t.identifier('withGTConfig'),\n t.memberExpression(\n t.callExpression(t.identifier('require'), [\n t.stringLiteral('gt-next/config'),\n ]),\n t.identifier('withGTConfig')\n )\n ),\n ])\n : t.importDeclaration(\n [\n t.importSpecifier(\n t.identifier('withGTConfig'),\n t.identifier('withGTConfig')\n ),\n ],\n t.stringLiteral('gt-next/config')\n )\n );\n\n // Find and transform the default export\n traverse(ast, {\n ExportDefaultDeclaration(path) {\n const oldExport = path.node.declaration;\n\n let exportExpression;\n if (t.isFunctionDeclaration(oldExport)) {\n exportExpression = t.functionExpression(\n oldExport.id,\n oldExport.params,\n oldExport.body,\n oldExport.generator,\n oldExport.async\n );\n } else if (t.isClassDeclaration(oldExport)) {\n exportExpression = t.classExpression(\n oldExport.id,\n oldExport.superClass,\n oldExport.body,\n oldExport.decorators\n );\n } else if (t.isTSDeclareFunction(oldExport)) {\n // For TypeScript declare functions, create an empty function expression\n // since declare functions don't have a runtime implementation\n warnings.push(\n `Found TypeScript declare function in ${filepath}. Converting to empty function.`\n );\n exportExpression = t.functionExpression(\n oldExport.id,\n oldExport.params,\n t.blockStatement([]),\n false,\n false\n );\n } else {\n exportExpression = oldExport;\n }\n\n // Validate that we have a valid Next.js config export\n if (\n !t.isObjectExpression(exportExpression) &&\n !t.isFunctionExpression(exportExpression) &&\n !t.isArrowFunctionExpression(exportExpression)\n ) {\n warnings.push(\n `Unexpected export type in ${filepath}. Next.js config should export an object or a function returning an object.`\n );\n }\n\n path.node.declaration = t.callExpression(t.identifier('withGTConfig'), [\n exportExpression,\n t.objectExpression([]),\n ]);\n },\n });\n\n // Generate the modified code\n const output = generate(\n ast,\n {\n retainLines: true,\n retainFunctionParens: true,\n comments: true,\n compact: 'auto',\n },\n code\n );\n\n // Post-process the output to fix import spacing\n let processedCode = output.code;\n // Add newline after the comment only\n processedCode = processedCode.replace(\n /((?:import\\s*{\\s*withGTConfig\\s*}\\s*from|const\\s*{\\s*withGTConfig\\s*}\\s*=\\s*require)\\s*['\"]gt-next\\/config['\"];?)/,\n '$1\\n'\n );\n\n // Write the modified code back to the file\n await fs.promises.writeFile(filepath, processedCode);\n filesUpdated.push(filepath);\n } catch (error) {\n logger.error(`Error parsing file ${filepath}: ${error}`);\n errors.push(`Failed to parse ${filepath}: ${error}`);\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
export declare const DECLARE_VAR_FUNCTION = "declareVar";
|
|
2
|
-
/**
|
|
3
|
-
* COMPAT: Legacy support for declareStatic function.
|
|
4
|
-
*/
|
|
5
|
-
export declare const DECLARE_STATIC_FUNCTION = "declareStatic";
|
|
6
2
|
export declare const DERIVE_FUNCTION = "derive";
|
|
7
3
|
export declare const MSG_REGISTRATION_FUNCTION = "msg";
|
|
8
4
|
export declare const T_REGISTRATION_FUNCTION = "t";
|
|
@@ -12,15 +8,13 @@ export declare const INLINE_TRANSLATION_HOOK_ASYNC = "getGT";
|
|
|
12
8
|
export declare const INLINE_MESSAGE_HOOK = "useMessages";
|
|
13
9
|
export declare const INLINE_MESSAGE_HOOK_ASYNC = "getMessages";
|
|
14
10
|
export declare const TRANSLATION_COMPONENT = "T";
|
|
15
|
-
/**
|
|
16
|
-
* COMPAT: Legacy support for Static component.
|
|
17
|
-
*/
|
|
18
|
-
export declare const STATIC_COMPONENT = "Static";
|
|
19
11
|
export declare const DERIVE_COMPONENT = "Derive";
|
|
20
12
|
export declare const BRANCH_COMPONENT = "Branch";
|
|
21
|
-
export declare const DEFAULT_GT_IMPORT_SOURCE = "gt-react
|
|
13
|
+
export declare const DEFAULT_GT_IMPORT_SOURCE = "gt-react";
|
|
14
|
+
export declare const LEGACY_GT_IMPORT_SOURCE = "gt-react/browser";
|
|
22
15
|
export declare const INTERNAL_TRANSLATION_COMPONENT = "GtInternalTranslateJsx";
|
|
23
16
|
export declare const INTERNAL_VAR_COMPONENT = "GtInternalVar";
|
|
17
|
+
export declare function getGtReactImportSource(legacyGtReactImportSource: boolean): typeof DEFAULT_GT_IMPORT_SOURCE | typeof LEGACY_GT_IMPORT_SOURCE;
|
|
24
18
|
export declare const VAR_COMPONENT = "Var";
|
|
25
19
|
export declare const DATETIME_COMPONENT = "DateTime";
|
|
26
20
|
export declare const RELATIVE_TIME_COMPONENT = "RelativeTime";
|