gt 2.20.4 → 2.21.0
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 +16 -0
- package/dist/api/collectUserEditDiffs.d.ts +2 -0
- package/dist/api/collectUserEditDiffs.js +31 -8
- package/dist/api/collectUserEditDiffs.js.map +1 -1
- package/dist/api/downloadFileBatch.js +13 -6
- package/dist/api/downloadFileBatch.js.map +1 -1
- package/dist/cli/commands/upload.js +30 -11
- package/dist/cli/commands/upload.js.map +1 -1
- package/dist/console/index.d.ts +4 -0
- package/dist/console/index.js +2 -1
- package/dist/console/index.js.map +1 -1
- package/dist/formats/files/aggregateFiles.js +39 -2
- package/dist/formats/files/aggregateFiles.js.map +1 -1
- package/dist/formats/files/localeContent.d.ts +18 -0
- package/dist/formats/files/localeContent.js +34 -0
- package/dist/formats/files/localeContent.js.map +1 -0
- package/dist/formats/files/supportedFiles.d.ts +2 -1
- package/dist/formats/files/supportedFiles.js +4 -2
- package/dist/formats/files/supportedFiles.js.map +1 -1
- package/dist/formats/files/transformFormat.d.ts +2 -0
- package/dist/formats/files/transformFormat.js +6 -3
- package/dist/formats/files/transformFormat.js.map +1 -1
- package/dist/formats/xcstrings/mergeXcstrings.d.ts +13 -0
- package/dist/formats/xcstrings/mergeXcstrings.js +71 -0
- package/dist/formats/xcstrings/mergeXcstrings.js.map +1 -0
- package/dist/formats/xcstrings/parseXcstrings.d.ts +43 -0
- package/dist/formats/xcstrings/parseXcstrings.js +123 -0
- package/dist/formats/xcstrings/parseXcstrings.js.map +1 -0
- package/dist/fs/config/parseFilesConfig.d.ts +2 -2
- package/dist/fs/config/parseFilesConfig.js +17 -3
- package/dist/fs/config/parseFilesConfig.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/state/recentDownloads.d.ts +1 -1
- package/dist/state/recentDownloads.js +3 -1
- package/dist/state/recentDownloads.js.map +1 -1
- package/dist/utils/persistPostprocessHashes.d.ts +1 -1
- package/dist/utils/persistPostprocessHashes.js +18 -14
- package/dist/utils/persistPostprocessHashes.js.map +1 -1
- package/package.json +7 -7
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
//#region src/formats/xcstrings/parseXcstrings.ts
|
|
2
|
+
function isPlainObject(value) {
|
|
3
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4
|
+
}
|
|
5
|
+
function invalid(path, expected) {
|
|
6
|
+
return /* @__PURE__ */ new Error(`Invalid .xcstrings content: ${path} must be ${expected}`);
|
|
7
|
+
}
|
|
8
|
+
const RESERVED_KEY_NAME = "__proto__";
|
|
9
|
+
function assertSafeKey(name, path) {
|
|
10
|
+
if (name === RESERVED_KEY_NAME) throw new Error(`Invalid .xcstrings content: ${path} uses the reserved name "${name}"`);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Parses a raw .xcstrings document and validates the structure the slicer
|
|
14
|
+
* traverses. The `strings` and `localizations` containers are re-keyed onto
|
|
15
|
+
* prototype-less records so lookups by catalog-chosen names cannot resolve to
|
|
16
|
+
* inherited properties. Throws on invalid content.
|
|
17
|
+
*/
|
|
18
|
+
function parseXcstringsCatalog(content) {
|
|
19
|
+
let parsed;
|
|
20
|
+
try {
|
|
21
|
+
parsed = JSON.parse(content);
|
|
22
|
+
} catch {
|
|
23
|
+
throw new Error("Invalid .xcstrings content: not valid JSON");
|
|
24
|
+
}
|
|
25
|
+
if (!isPlainObject(parsed)) throw invalid("document root", "an object");
|
|
26
|
+
if (typeof parsed.sourceLanguage !== "string" || !parsed.sourceLanguage) throw invalid("sourceLanguage", "a non-empty string");
|
|
27
|
+
assertSafeKey(parsed.sourceLanguage, "sourceLanguage");
|
|
28
|
+
if (!isPlainObject(parsed.strings)) throw invalid("strings", "an object");
|
|
29
|
+
const strings = Object.create(null);
|
|
30
|
+
for (const [key, entry] of Object.entries(parsed.strings)) {
|
|
31
|
+
const path = `strings[${JSON.stringify(key)}]`;
|
|
32
|
+
assertSafeKey(key, path);
|
|
33
|
+
if (!isPlainObject(entry)) throw invalid(path, "an object");
|
|
34
|
+
if (entry.localizations === void 0) {
|
|
35
|
+
strings[key] = entry;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (!isPlainObject(entry.localizations)) throw invalid(`${path}.localizations`, "an object");
|
|
39
|
+
const localizations = Object.create(null);
|
|
40
|
+
for (const [locale, localization] of Object.entries(entry.localizations)) {
|
|
41
|
+
assertSafeKey(locale, `${path}.localizations[${JSON.stringify(locale)}]`);
|
|
42
|
+
localizations[locale] = localization;
|
|
43
|
+
}
|
|
44
|
+
strings[key] = {
|
|
45
|
+
...entry,
|
|
46
|
+
localizations
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
...parsed,
|
|
51
|
+
strings
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* PINNED SERIALIZATION — DO NOT CHANGE.
|
|
56
|
+
*
|
|
57
|
+
* The source slice is hashed into versionId (see aggregateFiles), so any
|
|
58
|
+
* change to these bytes re-versions every customer .xcstrings file and
|
|
59
|
+
* re-triggers translation fleet-wide. The byte-exact tests on this format are
|
|
60
|
+
* the contract.
|
|
61
|
+
*/
|
|
62
|
+
function serializeXcstringsSlice(catalog) {
|
|
63
|
+
return JSON.stringify(catalog, null, 2) + "\n";
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Clones a validated catalog keeping, per entry, only `localizations[locale]`.
|
|
67
|
+
* An entry without that localization is dropped, or kept without a
|
|
68
|
+
* `localizations` key when `keepEntriesWithoutLocale` is set: an entry whose
|
|
69
|
+
* localizations hold only other locales then slices to the same shape as one
|
|
70
|
+
* with none, so translations added to the catalog leave the source slice,
|
|
71
|
+
* and with it versionId, unchanged. Nodes are cloned, never rebuilt, so
|
|
72
|
+
* unknown fields and key order survive at every level.
|
|
73
|
+
*/
|
|
74
|
+
function sliceCatalog(catalog, locale, keepEntriesWithoutLocale) {
|
|
75
|
+
const strings = Object.create(null);
|
|
76
|
+
for (const [key, entry] of Object.entries(catalog.strings)) {
|
|
77
|
+
if (entry.localizations === void 0 || !Object.hasOwn(entry.localizations, locale)) {
|
|
78
|
+
if (!keepEntriesWithoutLocale) continue;
|
|
79
|
+
const { localizations: _otherLocales, ...withoutLocalizations } = entry;
|
|
80
|
+
strings[key] = withoutLocalizations;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
const localizations = Object.create(null);
|
|
84
|
+
localizations[locale] = entry.localizations[locale];
|
|
85
|
+
strings[key] = {
|
|
86
|
+
...entry,
|
|
87
|
+
localizations
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
...catalog,
|
|
92
|
+
strings
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Produces the source-language slice of a validated catalog: a single-locale
|
|
97
|
+
* catalog holding, per entry, only the source-language localization. Entries
|
|
98
|
+
* without a source localization (the key itself is the source) keep their
|
|
99
|
+
* other fields and carry no `localizations`.
|
|
100
|
+
*/
|
|
101
|
+
function sliceSourceCatalog(catalog) {
|
|
102
|
+
return sliceCatalog(catalog, catalog.sourceLanguage, true);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Produces one locale's translation slice: the entries that carry
|
|
106
|
+
* `localizations[locale]`, each holding only that localization. Returns
|
|
107
|
+
* undefined when the catalog carries no translation for the locale.
|
|
108
|
+
*/
|
|
109
|
+
function sliceTranslationCatalog(catalog, locale) {
|
|
110
|
+
const slice = sliceCatalog(catalog, locale, false);
|
|
111
|
+
return Object.keys(slice.strings).length > 0 ? slice : void 0;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Parses, slices to the catalog's source language, and serializes with the
|
|
115
|
+
* pinned byte layout. Throws on invalid content.
|
|
116
|
+
*/
|
|
117
|
+
function parseXcstrings(content) {
|
|
118
|
+
return serializeXcstringsSlice(sliceSourceCatalog(parseXcstringsCatalog(content)));
|
|
119
|
+
}
|
|
120
|
+
//#endregion
|
|
121
|
+
export { parseXcstrings, parseXcstringsCatalog, serializeXcstringsSlice, sliceSourceCatalog, sliceTranslationCatalog };
|
|
122
|
+
|
|
123
|
+
//# sourceMappingURL=parseXcstrings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parseXcstrings.js","names":[],"sources":["../../../src/formats/xcstrings/parseXcstrings.ts"],"sourcesContent":["// Slicing for Apple .xcstrings catalogs. One on-disk catalog holds every\n// locale; the upload carries the source-language slice as the source document\n// and one single-locale slice per translated locale. Slices clone nodes and\n// keep only the wanted locale key, so unknown fields survive verbatim at every\n// level and a later download-merge can fold per-locale translations back into\n// the same on-disk catalog.\n\nexport type XcstringsEntry = {\n localizations?: Record<string, unknown>;\n [key: string]: unknown;\n};\n\nexport type XcstringsCatalog = {\n sourceLanguage: string;\n strings: Record<string, XcstringsEntry>;\n [key: string]: unknown;\n};\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction invalid(path: string, expected: string): Error {\n return new Error(`Invalid .xcstrings content: ${path} must be ${expected}`);\n}\n\n// Catalogs are untrusted input. Every container keyed by catalog-chosen names\n// (string keys, locales) is built without a prototype, so `constructor` and\n// `prototype` are ordinary properties and stay uploadable, matching the\n// server's validator. `__proto__` is still rejected: assigning it on a plain\n// object reassigns the prototype instead of creating a property.\nconst RESERVED_KEY_NAME = '__proto__';\n\nfunction assertSafeKey(name: string, path: string): void {\n if (name === RESERVED_KEY_NAME) {\n throw new Error(\n `Invalid .xcstrings content: ${path} uses the reserved name \"${name}\"`\n );\n }\n}\n\n/**\n * Parses a raw .xcstrings document and validates the structure the slicer\n * traverses. The `strings` and `localizations` containers are re-keyed onto\n * prototype-less records so lookups by catalog-chosen names cannot resolve to\n * inherited properties. Throws on invalid content.\n */\nexport function parseXcstringsCatalog(content: string): XcstringsCatalog {\n let parsed: unknown;\n try {\n parsed = JSON.parse(content);\n } catch {\n throw new Error('Invalid .xcstrings content: not valid JSON');\n }\n if (!isPlainObject(parsed)) throw invalid('document root', 'an object');\n if (typeof parsed.sourceLanguage !== 'string' || !parsed.sourceLanguage) {\n throw invalid('sourceLanguage', 'a non-empty string');\n }\n assertSafeKey(parsed.sourceLanguage, 'sourceLanguage');\n if (!isPlainObject(parsed.strings)) throw invalid('strings', 'an object');\n const strings: Record<string, XcstringsEntry> = Object.create(null);\n for (const [key, entry] of Object.entries(parsed.strings)) {\n const path = `strings[${JSON.stringify(key)}]`;\n assertSafeKey(key, path);\n if (!isPlainObject(entry)) throw invalid(path, 'an object');\n if (entry.localizations === undefined) {\n strings[key] = entry;\n continue;\n }\n if (!isPlainObject(entry.localizations)) {\n throw invalid(`${path}.localizations`, 'an object');\n }\n const localizations: Record<string, unknown> = Object.create(null);\n for (const [locale, localization] of Object.entries(entry.localizations)) {\n assertSafeKey(locale, `${path}.localizations[${JSON.stringify(locale)}]`);\n localizations[locale] = localization;\n }\n strings[key] = { ...entry, localizations };\n }\n return { ...parsed, strings } as XcstringsCatalog;\n}\n\n/**\n * PINNED SERIALIZATION — DO NOT CHANGE.\n *\n * The source slice is hashed into versionId (see aggregateFiles), so any\n * change to these bytes re-versions every customer .xcstrings file and\n * re-triggers translation fleet-wide. The byte-exact tests on this format are\n * the contract.\n */\nexport function serializeXcstringsSlice(catalog: XcstringsCatalog): string {\n return JSON.stringify(catalog, null, 2) + '\\n';\n}\n\n/**\n * Clones a validated catalog keeping, per entry, only `localizations[locale]`.\n * An entry without that localization is dropped, or kept without a\n * `localizations` key when `keepEntriesWithoutLocale` is set: an entry whose\n * localizations hold only other locales then slices to the same shape as one\n * with none, so translations added to the catalog leave the source slice,\n * and with it versionId, unchanged. Nodes are cloned, never rebuilt, so\n * unknown fields and key order survive at every level.\n */\nfunction sliceCatalog(\n catalog: XcstringsCatalog,\n locale: string,\n keepEntriesWithoutLocale: boolean\n): XcstringsCatalog {\n const strings: Record<string, XcstringsEntry> = Object.create(null);\n for (const [key, entry] of Object.entries(catalog.strings)) {\n if (\n entry.localizations === undefined ||\n !Object.hasOwn(entry.localizations, locale)\n ) {\n if (!keepEntriesWithoutLocale) continue;\n const { localizations: _otherLocales, ...withoutLocalizations } = entry;\n strings[key] = withoutLocalizations;\n continue;\n }\n const localizations: Record<string, unknown> = Object.create(null);\n localizations[locale] = entry.localizations[locale];\n strings[key] = { ...entry, localizations };\n }\n return { ...catalog, strings };\n}\n\n/**\n * Produces the source-language slice of a validated catalog: a single-locale\n * catalog holding, per entry, only the source-language localization. Entries\n * without a source localization (the key itself is the source) keep their\n * other fields and carry no `localizations`.\n */\nexport function sliceSourceCatalog(\n catalog: XcstringsCatalog\n): XcstringsCatalog {\n return sliceCatalog(catalog, catalog.sourceLanguage, true);\n}\n\n/**\n * Produces one locale's translation slice: the entries that carry\n * `localizations[locale]`, each holding only that localization. Returns\n * undefined when the catalog carries no translation for the locale.\n */\nexport function sliceTranslationCatalog(\n catalog: XcstringsCatalog,\n locale: string\n): XcstringsCatalog | undefined {\n const slice = sliceCatalog(catalog, locale, false);\n return Object.keys(slice.strings).length > 0 ? slice : undefined;\n}\n\n/**\n * Parses, slices to the catalog's source language, and serializes with the\n * pinned byte layout. Throws on invalid content.\n */\nexport function parseXcstrings(content: string): string {\n const catalog = parseXcstringsCatalog(content);\n return serializeXcstringsSlice(sliceSourceCatalog(catalog));\n}\n"],"mappings":";AAkBA,SAAS,cAAc,OAAkD;AACvE,QAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,MAAM;;AAG7E,SAAS,QAAQ,MAAc,UAAyB;AACtD,wBAAO,IAAI,MAAM,+BAA+B,KAAK,WAAW,WAAW;;AAQ7E,MAAM,oBAAoB;AAE1B,SAAS,cAAc,MAAc,MAAoB;AACvD,KAAI,SAAS,kBACX,OAAM,IAAI,MACR,+BAA+B,KAAK,2BAA2B,KAAK,GACrE;;;;;;;;AAUL,SAAgB,sBAAsB,SAAmC;CACvE,IAAI;AACJ,KAAI;AACF,WAAS,KAAK,MAAM,QAAQ;SACtB;AACN,QAAM,IAAI,MAAM,6CAA6C;;AAE/D,KAAI,CAAC,cAAc,OAAO,CAAE,OAAM,QAAQ,iBAAiB,YAAY;AACvE,KAAI,OAAO,OAAO,mBAAmB,YAAY,CAAC,OAAO,eACvD,OAAM,QAAQ,kBAAkB,qBAAqB;AAEvD,eAAc,OAAO,gBAAgB,iBAAiB;AACtD,KAAI,CAAC,cAAc,OAAO,QAAQ,CAAE,OAAM,QAAQ,WAAW,YAAY;CACzE,MAAM,UAA0C,OAAO,OAAO,KAAK;AACnE,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,QAAQ,EAAE;EACzD,MAAM,OAAO,WAAW,KAAK,UAAU,IAAI,CAAC;AAC5C,gBAAc,KAAK,KAAK;AACxB,MAAI,CAAC,cAAc,MAAM,CAAE,OAAM,QAAQ,MAAM,YAAY;AAC3D,MAAI,MAAM,kBAAkB,KAAA,GAAW;AACrC,WAAQ,OAAO;AACf;;AAEF,MAAI,CAAC,cAAc,MAAM,cAAc,CACrC,OAAM,QAAQ,GAAG,KAAK,iBAAiB,YAAY;EAErD,MAAM,gBAAyC,OAAO,OAAO,KAAK;AAClE,OAAK,MAAM,CAAC,QAAQ,iBAAiB,OAAO,QAAQ,MAAM,cAAc,EAAE;AACxE,iBAAc,QAAQ,GAAG,KAAK,iBAAiB,KAAK,UAAU,OAAO,CAAC,GAAG;AACzE,iBAAc,UAAU;;AAE1B,UAAQ,OAAO;GAAE,GAAG;GAAO;GAAe;;AAE5C,QAAO;EAAE,GAAG;EAAQ;EAAS;;;;;;;;;;AAW/B,SAAgB,wBAAwB,SAAmC;AACzE,QAAO,KAAK,UAAU,SAAS,MAAM,EAAE,GAAG;;;;;;;;;;;AAY5C,SAAS,aACP,SACA,QACA,0BACkB;CAClB,MAAM,UAA0C,OAAO,OAAO,KAAK;AACnE,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,QAAQ,EAAE;AAC1D,MACE,MAAM,kBAAkB,KAAA,KACxB,CAAC,OAAO,OAAO,MAAM,eAAe,OAAO,EAC3C;AACA,OAAI,CAAC,yBAA0B;GAC/B,MAAM,EAAE,eAAe,eAAe,GAAG,yBAAyB;AAClE,WAAQ,OAAO;AACf;;EAEF,MAAM,gBAAyC,OAAO,OAAO,KAAK;AAClE,gBAAc,UAAU,MAAM,cAAc;AAC5C,UAAQ,OAAO;GAAE,GAAG;GAAO;GAAe;;AAE5C,QAAO;EAAE,GAAG;EAAS;EAAS;;;;;;;;AAShC,SAAgB,mBACd,SACkB;AAClB,QAAO,aAAa,SAAS,QAAQ,gBAAgB,KAAK;;;;;;;AAQ5D,SAAgB,wBACd,SACA,QAC8B;CAC9B,MAAM,QAAQ,aAAa,SAAS,QAAQ,MAAM;AAClD,QAAO,OAAO,KAAK,MAAM,QAAQ,CAAC,SAAS,IAAI,QAAQ,KAAA;;;;;;AAOzD,SAAgB,eAAe,SAAyB;AAEtD,QAAO,wBAAwB,mBADf,sBAAsB,QACmB,CAAC,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FilesOptions, IncludePattern, ResolvedFiles, Settings, TransformOption } from '../../types/index.js';
|
|
1
|
+
import { FilesOptions, IncludePattern, ResolvedFiles, Settings, SupportedFileExtension, TransformOption } from '../../types/index.js';
|
|
2
2
|
/**
|
|
3
3
|
* Resolves the files from the files object
|
|
4
4
|
* Replaces [locale] with the actual locale in the files
|
|
@@ -26,7 +26,7 @@ export declare function normalizeIncludePatterns(patterns: IncludePattern[]): {
|
|
|
26
26
|
* @returns The resolved files
|
|
27
27
|
*/
|
|
28
28
|
export declare function resolveFiles(files: FilesOptions, locale: string, locales: string[], cwd: string, compositePatterns?: string[], requiresReviewDefault?: boolean): Settings['files'];
|
|
29
|
-
export declare function expandGlobPatterns(cwd: string, includePatterns: string[], excludePatterns: string[], locale: string, locales: string[], transformPatterns?: TransformOption | string | TransformOption[], compositePatterns?: string[]): {
|
|
29
|
+
export declare function expandGlobPatterns(cwd: string, includePatterns: string[], excludePatterns: string[], locale: string, locales: string[], transformPatterns?: TransformOption | string | TransformOption[], compositePatterns?: string[], fileType?: SupportedFileExtension): {
|
|
30
30
|
resolvedPaths: string[];
|
|
31
31
|
placeholderPaths: string[];
|
|
32
32
|
};
|
|
@@ -65,13 +65,14 @@ function resolveFiles(files, locale, locales, cwd, compositePatterns, requiresRe
|
|
|
65
65
|
const parsingFlags = {};
|
|
66
66
|
if (files.gt?.output) placeholderResult.gt = path.resolve(cwd, files.gt.output);
|
|
67
67
|
for (const fileType of SUPPORTED_FILE_EXTENSIONS) {
|
|
68
|
+
if (fileType === "xcstrings") validateXcstringsInPlace(files.xcstrings);
|
|
68
69
|
const transform = files[fileType]?.transform;
|
|
69
70
|
if (transform && (typeof transform === "string" || typeof transform === "object" || Array.isArray(transform))) transformPaths[fileType] = transform;
|
|
70
71
|
const transformFormat = resolveTransformationFormat(fileType, files[fileType]?.transformationFormat);
|
|
71
72
|
if (transformFormat) transformFormats[fileType] = transformFormat;
|
|
72
73
|
if (files[fileType]?.include) {
|
|
73
74
|
const { paths, publishPatterns, unpublishPatterns } = normalizeIncludePatterns(files[fileType].include);
|
|
74
|
-
const filePaths = expandGlobPatterns(cwd, paths, files[fileType]?.exclude || [], locale, locales, transformPaths[fileType] || void 0, compositePatterns);
|
|
75
|
+
const filePaths = expandGlobPatterns(cwd, paths, files[fileType]?.exclude || [], locale, locales, transformPaths[fileType] || void 0, compositePatterns, fileType);
|
|
75
76
|
resolvedPaths[fileType] = filePaths.resolvedPaths;
|
|
76
77
|
placeholderResult[fileType] = filePaths.placeholderPaths;
|
|
77
78
|
classifyPublishPaths(filePaths.resolvedPaths, publishPatterns, unpublishPatterns, cwd, locale, publishPaths, unpublishPaths);
|
|
@@ -103,11 +104,11 @@ function resolveFiles(files, locale, locales, cwd, compositePatterns, requiresRe
|
|
|
103
104
|
})()
|
|
104
105
|
};
|
|
105
106
|
}
|
|
106
|
-
function expandGlobPatterns(cwd, includePatterns, excludePatterns, locale, locales, transformPatterns, compositePatterns) {
|
|
107
|
+
function expandGlobPatterns(cwd, includePatterns, excludePatterns, locale, locales, transformPatterns, compositePatterns, fileType) {
|
|
107
108
|
const resolvedPaths = [];
|
|
108
109
|
const placeholderPaths = [];
|
|
109
110
|
for (const pattern of includePatterns) {
|
|
110
|
-
if (!pattern.includes("[locale]") && !transformPatterns && !compositePatterns?.includes(pattern)) logger.warn(chalk.yellow(`Pattern "${pattern}" does not include [locale], so the CLI tool may incorrectly save translated files.`));
|
|
111
|
+
if (!pattern.includes("[locale]") && !transformPatterns && !compositePatterns?.includes(pattern) && fileType !== "xcstrings") logger.warn(chalk.yellow(`Pattern "${pattern}" does not include [locale], so the CLI tool may incorrectly save translated files.`));
|
|
111
112
|
const localePositions = [];
|
|
112
113
|
let searchIndex = 0;
|
|
113
114
|
const localeTag = "[locale]";
|
|
@@ -179,6 +180,19 @@ function classifyPublishPaths(resolvedPaths, publishPatterns, unpublishPatterns,
|
|
|
179
180
|
}
|
|
180
181
|
}
|
|
181
182
|
/**
|
|
183
|
+
* An .xcstrings catalog holds every locale in one file and is updated in
|
|
184
|
+
* place: each locale's download is merged into the source catalog. A path
|
|
185
|
+
* transform or a `[locale]` placeholder would map each locale to a separate
|
|
186
|
+
* output, and every write would start from the source catalog and discard the
|
|
187
|
+
* locales written before it.
|
|
188
|
+
*/
|
|
189
|
+
function validateXcstringsInPlace(config) {
|
|
190
|
+
if (!config) return;
|
|
191
|
+
if (config.transform) logErrorAndExit("files.xcstrings.transform is not supported. An .xcstrings catalog holds every locale in one file and is updated in place, so remove the transform.");
|
|
192
|
+
const localePatterns = normalizeIncludePatterns(config.include ?? []).paths.filter((pattern) => pattern.includes("[locale]"));
|
|
193
|
+
if (localePatterns.length > 0) logErrorAndExit(`files.xcstrings.include must not contain [locale]: ${localePatterns.map((pattern) => `"${pattern}"`).join(", ")}. An .xcstrings catalog holds every locale in one file and is updated in place, so point the pattern at the catalog itself.`);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
182
196
|
* Validates a file-type requiresReview config value. Only a boolean or an
|
|
183
197
|
* object of include/exclude glob string arrays is accepted — notably not
|
|
184
198
|
* string "true"/"false", so a misquoted boolean fails loudly instead of
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parseFilesConfig.js","names":[],"sources":["../../../src/fs/config/parseFilesConfig.ts"],"sourcesContent":["import path from 'node:path';\nimport {\n FilesOptions,\n IncludePattern,\n RequiresReviewConfig,\n ResolvedFiles,\n Settings,\n TransformFormats,\n TransformFiles,\n TransformOption,\n} from '../../types/index.js';\nimport { logErrorAndExit } from '../../console/logging.js';\nimport fg from 'fast-glob';\nimport { SUPPORTED_FILE_EXTENSIONS } from '../../formats/files/supportedFiles.js';\nimport { logger } from '../../console/logger.js';\nimport chalk from 'chalk';\nimport micromatch from 'micromatch';\nimport { ParseFlagsByFileType } from '../../types/parsing.js';\nimport {\n BASE_PARSING_FLAGS_DEFAULT,\n GT_PARSING_FLAGS_DEFAULT,\n} from '../../config/defaults.js';\nimport { resolveTransformationFormat } from '../../formats/files/transformFormat.js';\nimport { localeForFilePath } from '../../formats/files/localePath.js';\n\n/**\n * Resolves the files from the files object\n * Replaces [locale] with the actual locale in the files\n *\n * @param files - The files object\n * @param locale - The locale to replace [locale] with\n * @returns The resolved files\n */\nexport function resolveLocaleFiles(\n files: ResolvedFiles,\n locale: string\n): ResolvedFiles {\n const result: ResolvedFiles = {};\n\n for (const fileType of SUPPORTED_FILE_EXTENSIONS) {\n const replacement = localeForFilePath(fileType, locale);\n result[fileType] = files[fileType]?.map((filepath) =>\n filepath.replace(/\\[locale\\]/g, replacement)\n );\n }\n\n // Replace [locale] with locale in all paths\n result.gt = files.gt?.replace(/\\[locale\\]/g, locale);\n\n return result;\n}\n/**\n * Normalizes include patterns into plain path strings and tracks which\n * patterns have explicit publish flags.\n */\nexport function normalizeIncludePatterns(patterns: IncludePattern[]): {\n paths: string[];\n publishPatterns: string[];\n unpublishPatterns: string[];\n} {\n const paths: string[] = [];\n const publishPatterns: string[] = [];\n const unpublishPatterns: string[] = [];\n\n for (const pattern of patterns) {\n if (typeof pattern === 'string') {\n paths.push(pattern);\n } else {\n paths.push(pattern.pattern);\n if (pattern.publish === true) {\n publishPatterns.push(pattern.pattern);\n } else if (pattern.publish === false) {\n unpublishPatterns.push(pattern.pattern);\n }\n }\n }\n\n return { paths, publishPatterns, unpublishPatterns };\n}\n\n/**\n * Resolves the files from the files object.\n * Performs glob pattern expansion on the files.\n * Replaces [locale] with the actual locale in the files.\n *\n * @param files - The files object\n * @returns The resolved files\n */\nexport function resolveFiles(\n files: FilesOptions,\n locale: string,\n locales: string[],\n cwd: string,\n compositePatterns?: string[],\n requiresReviewDefault: boolean = false\n): Settings['files'] {\n // Initialize result object with empty arrays for each file type\n const resolvedPaths: ResolvedFiles = {};\n const placeholderResult: ResolvedFiles = {};\n const transformPaths: TransformFiles = {};\n // Output format transforms are tracked separately from path transforms.\n const transformFormats: TransformFormats = {};\n const publishPaths = new Set<string>();\n const unpublishPaths = new Set<string>();\n const requiresReviewPaths = new Set<string>();\n const parsingFlags: ParseFlagsByFileType = {};\n\n // Process GT files\n if (files.gt?.output) {\n placeholderResult.gt = path.resolve(cwd, files.gt.output);\n }\n\n for (const fileType of SUPPORTED_FILE_EXTENSIONS) {\n // ==== TRANSFORMS ==== //\n const transform = files[fileType]?.transform;\n if (\n transform &&\n (typeof transform === 'string' ||\n typeof transform === 'object' ||\n Array.isArray(transform))\n ) {\n transformPaths[fileType] = transform;\n }\n // Validate source -> output format transforms during settings generation.\n const transformFormat = resolveTransformationFormat(\n fileType,\n files[fileType]?.transformationFormat\n );\n if (transformFormat) {\n transformFormats[fileType] = transformFormat;\n }\n // ==== PLACEHOLDERS ==== //\n if (files[fileType]?.include) {\n const { paths, publishPatterns, unpublishPatterns } =\n normalizeIncludePatterns(files[fileType].include);\n\n const filePaths = expandGlobPatterns(\n cwd,\n paths,\n files[fileType]?.exclude || [],\n locale,\n locales,\n transformPaths[fileType] || undefined,\n compositePatterns\n );\n resolvedPaths[fileType] = filePaths.resolvedPaths;\n placeholderResult[fileType] = filePaths.placeholderPaths;\n\n // Classify resolved paths into publish/unpublish sets\n classifyPublishPaths(\n filePaths.resolvedPaths,\n publishPatterns,\n unpublishPatterns,\n cwd,\n locale,\n publishPaths,\n unpublishPaths\n );\n\n // Classify resolved paths by effective requiresReview policy\n classifyRequiresReviewPaths(\n filePaths.resolvedPaths,\n validateRequiresReviewConfig(files[fileType]?.requiresReview, fileType),\n requiresReviewDefault,\n cwd,\n locale,\n requiresReviewPaths\n );\n }\n // ==== OTHER ==== //\n if (files[fileType]?.parsingFlags) {\n parsingFlags[fileType] = {\n ...BASE_PARSING_FLAGS_DEFAULT,\n ...files[fileType].parsingFlags,\n };\n }\n }\n\n return {\n resolvedPaths,\n placeholderPaths: placeholderResult,\n transformPaths: transformPaths,\n transformFormats,\n publishPaths,\n unpublishPaths,\n requiresReviewPaths,\n parsingFlags,\n gtJson: (() => {\n const rawGtFlags = (files.gt?.parsingFlags || {}) as Record<\n string,\n unknown\n >;\n return {\n publish: files.gt?.publish,\n parsingFlags: {\n ...GT_PARSING_FLAGS_DEFAULT,\n ...rawGtFlags,\n },\n };\n })(),\n };\n}\n\n// Helper function to expand glob patterns\nexport function expandGlobPatterns(\n cwd: string,\n includePatterns: string[],\n excludePatterns: string[],\n locale: string,\n locales: string[],\n transformPatterns?: TransformOption | string | TransformOption[],\n compositePatterns?: string[]\n): {\n resolvedPaths: string[];\n placeholderPaths: string[];\n} {\n // Expand glob patterns to include all matching files\n const resolvedPaths: string[] = [];\n const placeholderPaths: string[] = [];\n\n // Process include patterns\n for (const pattern of includePatterns) {\n // Track positions where [locale] appears in the original pattern\n // It must be included in the pattern, otherwise the CLI tool will not be able to find the correct output path\n // Warn if it's not included\n // Ignore if is composite pattern\n if (\n !pattern.includes('[locale]') &&\n !transformPatterns &&\n !compositePatterns?.includes(pattern)\n ) {\n logger.warn(\n chalk.yellow(\n `Pattern \"${pattern}\" does not include [locale], so the CLI tool may incorrectly save translated files.`\n )\n );\n }\n const localePositions: number[] = [];\n let searchIndex = 0;\n const localeTag = '[locale]';\n\n while (true) {\n const foundIndex = pattern.indexOf(localeTag, searchIndex);\n if (foundIndex === -1) break;\n localePositions.push(foundIndex);\n searchIndex = foundIndex + localeTag.length;\n }\n\n const expandedPattern = pattern.replace(/\\[locale\\]/g, locale);\n\n // Resolve the absolute pattern path\n const absolutePattern = path.resolve(cwd, expandedPattern);\n\n // Prepare exclude patterns with locale replaced\n const expandedExcludePatterns = Array.from(\n new Set(\n excludePatterns.flatMap((p) =>\n locales.map((targetLocale) =>\n path.resolve(\n cwd,\n p\n .replace(/\\[locale\\]/g, locale)\n .replace(/\\[locales\\]/g, targetLocale)\n )\n )\n )\n )\n );\n\n // Use fast-glob to find all matching files, excluding the patterns\n const matches = fg.sync(absolutePattern, {\n absolute: true,\n ignore: expandedExcludePatterns,\n });\n\n resolvedPaths.push(...matches);\n\n // For each match, create a version with [locale] in the correct positions\n matches.forEach((match) => {\n const absolutePath = path.resolve(cwd, match);\n const patternPath = path.resolve(cwd, pattern);\n let originalAbsolutePath = absolutePath;\n\n if (localePositions.length > 0) {\n const placeholderPath = buildPlaceholderPathFromPattern(\n patternPath,\n absolutePath,\n localeTag\n );\n originalAbsolutePath = placeholderPath;\n }\n\n placeholderPaths.push(originalAbsolutePath);\n });\n }\n\n return { resolvedPaths, placeholderPaths };\n}\n\nfunction buildPlaceholderPathFromPattern(\n patternPath: string,\n absolutePath: string,\n localeTag: string\n): string {\n if (!patternPath.includes(localeTag)) {\n return absolutePath;\n }\n\n const posixPattern = toPosixPath(patternPath);\n const posixPath = toPosixPath(absolutePath);\n\n const baseRegex = micromatch.makeRe(posixPattern, {\n literalBrackets: true,\n });\n const localeRegexSource = baseRegex.source.replace(\n /\\\\\\[locale\\\\\\]/g,\n '([^/]+)'\n );\n const flags = baseRegex.flags.includes('d')\n ? baseRegex.flags\n : `${baseRegex.flags}d`;\n const matcher = new RegExp(localeRegexSource, flags);\n const match = matcher.exec(posixPath);\n\n const matchWithIndices = match as RegExpExecArray & {\n indices?: Array<[number, number]>;\n };\n\n if (!match || !matchWithIndices.indices) {\n return absolutePath;\n }\n\n let placeholderPosixPath = posixPath;\n const indices = matchWithIndices.indices;\n\n for (let i = indices.length - 1; i >= 1; i--) {\n const [start, end] = indices[i];\n if (start === -1 || end === -1) continue;\n placeholderPosixPath =\n placeholderPosixPath.slice(0, start) +\n localeTag +\n placeholderPosixPath.slice(end);\n }\n\n return path.normalize(placeholderPosixPath);\n}\n\nfunction toPosixPath(value: string): string {\n return value.split(path.sep).join(path.posix.sep);\n}\n\n/**\n * Classifies resolved file paths into publish/unpublish sets by matching\n * them against the given glob patterns. Uses POSIX paths for micromatch\n * compatibility but stores platform-native paths in the output sets.\n */\nfunction classifyPublishPaths(\n resolvedPaths: string[],\n publishPatterns: string[],\n unpublishPatterns: string[],\n cwd: string,\n locale: string,\n publishPaths: Set<string>,\n unpublishPaths: Set<string>\n): void {\n if (publishPatterns.length === 0 && unpublishPatterns.length === 0) return;\n\n const posixPaths = resolvedPaths.map(toPosixPath);\n const toAbsoluteGlob = (p: string) =>\n toPosixPath(path.resolve(cwd, p.replace(/\\[locale\\]/g, locale)));\n\n for (const pattern of publishPatterns) {\n const matched = new Set(micromatch(posixPaths, toAbsoluteGlob(pattern)));\n for (let i = 0; i < posixPaths.length; i++) {\n if (matched.has(posixPaths[i])) {\n publishPaths.add(resolvedPaths[i]);\n }\n }\n }\n\n for (const pattern of unpublishPatterns) {\n const matched = new Set(micromatch(posixPaths, toAbsoluteGlob(pattern)));\n for (let i = 0; i < posixPaths.length; i++) {\n if (matched.has(posixPaths[i])) {\n unpublishPaths.add(resolvedPaths[i]);\n }\n }\n }\n}\n\n/**\n * Validates a file-type requiresReview config value. Only a boolean or an\n * object of include/exclude glob string arrays is accepted — notably not\n * string \"true\"/\"false\", so a misquoted boolean fails loudly instead of\n * silently changing review policy (and with it, version identity).\n */\nfunction validateRequiresReviewConfig(\n config: unknown,\n fileType: string\n): RequiresReviewConfig | undefined {\n if (config === undefined || typeof config === 'boolean') {\n return config;\n }\n const isStringArray = (value: unknown): value is string[] =>\n Array.isArray(value) && value.every((item) => typeof item === 'string');\n if (\n config !== null &&\n typeof config === 'object' &&\n !Array.isArray(config) &&\n Object.keys(config).every((key) => key === 'include' || key === 'exclude')\n ) {\n const { include, exclude } = config as Record<string, unknown>;\n if (\n (include === undefined || isStringArray(include)) &&\n (exclude === undefined || isStringArray(exclude))\n ) {\n return config as RequiresReviewConfig;\n }\n }\n return logErrorAndExit(\n `files.${fileType}.requiresReview must be a boolean or an object of glob string arrays: { include?: string[], exclude?: string[] }`\n );\n}\n\n/**\n * Classifies resolved file paths by effective requiresReview policy and adds\n * paths whose policy is true to requiresReviewPaths. Precedence: file-type\n * include/exclude globs (exclude wins) > file-type boolean > top-level default.\n */\nfunction classifyRequiresReviewPaths(\n resolvedPaths: string[],\n config: RequiresReviewConfig | undefined,\n requiresReviewDefault: boolean,\n cwd: string,\n locale: string,\n requiresReviewPaths: Set<string>\n): void {\n if (typeof config === 'boolean' || config === undefined) {\n if (config ?? requiresReviewDefault) {\n for (const resolvedPath of resolvedPaths) {\n requiresReviewPaths.add(resolvedPath);\n }\n }\n return;\n }\n\n const posixPaths = resolvedPaths.map(toPosixPath);\n const toAbsoluteGlob = (p: string) =>\n toPosixPath(path.resolve(cwd, p.replace(/\\[locale\\]/g, locale)));\n const matchAny = (patterns: string[]) => {\n const matched = new Set<string>();\n for (const pattern of patterns) {\n for (const match of micromatch(posixPaths, toAbsoluteGlob(pattern))) {\n matched.add(match);\n }\n }\n return matched;\n };\n\n const included = matchAny(config.include ?? []);\n const excluded = matchAny(config.exclude ?? []);\n\n for (let i = 0; i < posixPaths.length; i++) {\n const requiresReview = excluded.has(posixPaths[i])\n ? false\n : included.has(posixPaths[i]) || requiresReviewDefault;\n if (requiresReview) {\n requiresReviewPaths.add(resolvedPaths[i]);\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"parseFilesConfig.js","names":[],"sources":["../../../src/fs/config/parseFilesConfig.ts"],"sourcesContent":["import path from 'node:path';\nimport {\n FilesOptions,\n IncludePattern,\n RequiresReviewConfig,\n ResolvedFiles,\n Settings,\n SupportedFileExtension,\n TransformFormats,\n TransformFiles,\n TransformOption,\n} from '../../types/index.js';\nimport { logErrorAndExit } from '../../console/logging.js';\nimport fg from 'fast-glob';\nimport { SUPPORTED_FILE_EXTENSIONS } from '../../formats/files/supportedFiles.js';\nimport { logger } from '../../console/logger.js';\nimport chalk from 'chalk';\nimport micromatch from 'micromatch';\nimport { ParseFlagsByFileType } from '../../types/parsing.js';\nimport {\n BASE_PARSING_FLAGS_DEFAULT,\n GT_PARSING_FLAGS_DEFAULT,\n} from '../../config/defaults.js';\nimport { resolveTransformationFormat } from '../../formats/files/transformFormat.js';\nimport { localeForFilePath } from '../../formats/files/localePath.js';\n\n/**\n * Resolves the files from the files object\n * Replaces [locale] with the actual locale in the files\n *\n * @param files - The files object\n * @param locale - The locale to replace [locale] with\n * @returns The resolved files\n */\nexport function resolveLocaleFiles(\n files: ResolvedFiles,\n locale: string\n): ResolvedFiles {\n const result: ResolvedFiles = {};\n\n for (const fileType of SUPPORTED_FILE_EXTENSIONS) {\n const replacement = localeForFilePath(fileType, locale);\n result[fileType] = files[fileType]?.map((filepath) =>\n filepath.replace(/\\[locale\\]/g, replacement)\n );\n }\n\n // Replace [locale] with locale in all paths\n result.gt = files.gt?.replace(/\\[locale\\]/g, locale);\n\n return result;\n}\n/**\n * Normalizes include patterns into plain path strings and tracks which\n * patterns have explicit publish flags.\n */\nexport function normalizeIncludePatterns(patterns: IncludePattern[]): {\n paths: string[];\n publishPatterns: string[];\n unpublishPatterns: string[];\n} {\n const paths: string[] = [];\n const publishPatterns: string[] = [];\n const unpublishPatterns: string[] = [];\n\n for (const pattern of patterns) {\n if (typeof pattern === 'string') {\n paths.push(pattern);\n } else {\n paths.push(pattern.pattern);\n if (pattern.publish === true) {\n publishPatterns.push(pattern.pattern);\n } else if (pattern.publish === false) {\n unpublishPatterns.push(pattern.pattern);\n }\n }\n }\n\n return { paths, publishPatterns, unpublishPatterns };\n}\n\n/**\n * Resolves the files from the files object.\n * Performs glob pattern expansion on the files.\n * Replaces [locale] with the actual locale in the files.\n *\n * @param files - The files object\n * @returns The resolved files\n */\nexport function resolveFiles(\n files: FilesOptions,\n locale: string,\n locales: string[],\n cwd: string,\n compositePatterns?: string[],\n requiresReviewDefault: boolean = false\n): Settings['files'] {\n // Initialize result object with empty arrays for each file type\n const resolvedPaths: ResolvedFiles = {};\n const placeholderResult: ResolvedFiles = {};\n const transformPaths: TransformFiles = {};\n // Output format transforms are tracked separately from path transforms.\n const transformFormats: TransformFormats = {};\n const publishPaths = new Set<string>();\n const unpublishPaths = new Set<string>();\n const requiresReviewPaths = new Set<string>();\n const parsingFlags: ParseFlagsByFileType = {};\n\n // Process GT files\n if (files.gt?.output) {\n placeholderResult.gt = path.resolve(cwd, files.gt.output);\n }\n\n for (const fileType of SUPPORTED_FILE_EXTENSIONS) {\n if (fileType === 'xcstrings') validateXcstringsInPlace(files.xcstrings);\n // ==== TRANSFORMS ==== //\n const transform = files[fileType]?.transform;\n if (\n transform &&\n (typeof transform === 'string' ||\n typeof transform === 'object' ||\n Array.isArray(transform))\n ) {\n transformPaths[fileType] = transform;\n }\n // Validate source -> output format transforms during settings generation.\n const transformFormat = resolveTransformationFormat(\n fileType,\n files[fileType]?.transformationFormat\n );\n if (transformFormat) {\n transformFormats[fileType] = transformFormat;\n }\n // ==== PLACEHOLDERS ==== //\n if (files[fileType]?.include) {\n const { paths, publishPatterns, unpublishPatterns } =\n normalizeIncludePatterns(files[fileType].include);\n\n const filePaths = expandGlobPatterns(\n cwd,\n paths,\n files[fileType]?.exclude || [],\n locale,\n locales,\n transformPaths[fileType] || undefined,\n compositePatterns,\n fileType\n );\n resolvedPaths[fileType] = filePaths.resolvedPaths;\n placeholderResult[fileType] = filePaths.placeholderPaths;\n\n // Classify resolved paths into publish/unpublish sets\n classifyPublishPaths(\n filePaths.resolvedPaths,\n publishPatterns,\n unpublishPatterns,\n cwd,\n locale,\n publishPaths,\n unpublishPaths\n );\n\n // Classify resolved paths by effective requiresReview policy\n classifyRequiresReviewPaths(\n filePaths.resolvedPaths,\n validateRequiresReviewConfig(files[fileType]?.requiresReview, fileType),\n requiresReviewDefault,\n cwd,\n locale,\n requiresReviewPaths\n );\n }\n // ==== OTHER ==== //\n if (files[fileType]?.parsingFlags) {\n parsingFlags[fileType] = {\n ...BASE_PARSING_FLAGS_DEFAULT,\n ...files[fileType].parsingFlags,\n };\n }\n }\n\n return {\n resolvedPaths,\n placeholderPaths: placeholderResult,\n transformPaths: transformPaths,\n transformFormats,\n publishPaths,\n unpublishPaths,\n requiresReviewPaths,\n parsingFlags,\n gtJson: (() => {\n const rawGtFlags = (files.gt?.parsingFlags || {}) as Record<\n string,\n unknown\n >;\n return {\n publish: files.gt?.publish,\n parsingFlags: {\n ...GT_PARSING_FLAGS_DEFAULT,\n ...rawGtFlags,\n },\n };\n })(),\n };\n}\n\n// Helper function to expand glob patterns\nexport function expandGlobPatterns(\n cwd: string,\n includePatterns: string[],\n excludePatterns: string[],\n locale: string,\n locales: string[],\n transformPatterns?: TransformOption | string | TransformOption[],\n compositePatterns?: string[],\n fileType?: SupportedFileExtension\n): {\n resolvedPaths: string[];\n placeholderPaths: string[];\n} {\n // Expand glob patterns to include all matching files\n const resolvedPaths: string[] = [];\n const placeholderPaths: string[] = [];\n\n // Process include patterns\n for (const pattern of includePatterns) {\n // Track positions where [locale] appears in the original pattern\n // It must be included in the pattern, otherwise the CLI tool will not be able to find the correct output path\n // Warn if it's not included\n // Ignore if is composite pattern\n // xcstrings catalogs hold every locale in one shared file, so a pattern\n // without [locale] is the expected layout there, not a misconfiguration\n if (\n !pattern.includes('[locale]') &&\n !transformPatterns &&\n !compositePatterns?.includes(pattern) &&\n fileType !== 'xcstrings'\n ) {\n logger.warn(\n chalk.yellow(\n `Pattern \"${pattern}\" does not include [locale], so the CLI tool may incorrectly save translated files.`\n )\n );\n }\n const localePositions: number[] = [];\n let searchIndex = 0;\n const localeTag = '[locale]';\n\n while (true) {\n const foundIndex = pattern.indexOf(localeTag, searchIndex);\n if (foundIndex === -1) break;\n localePositions.push(foundIndex);\n searchIndex = foundIndex + localeTag.length;\n }\n\n const expandedPattern = pattern.replace(/\\[locale\\]/g, locale);\n\n // Resolve the absolute pattern path\n const absolutePattern = path.resolve(cwd, expandedPattern);\n\n // Prepare exclude patterns with locale replaced\n const expandedExcludePatterns = Array.from(\n new Set(\n excludePatterns.flatMap((p) =>\n locales.map((targetLocale) =>\n path.resolve(\n cwd,\n p\n .replace(/\\[locale\\]/g, locale)\n .replace(/\\[locales\\]/g, targetLocale)\n )\n )\n )\n )\n );\n\n // Use fast-glob to find all matching files, excluding the patterns\n const matches = fg.sync(absolutePattern, {\n absolute: true,\n ignore: expandedExcludePatterns,\n });\n\n resolvedPaths.push(...matches);\n\n // For each match, create a version with [locale] in the correct positions\n matches.forEach((match) => {\n const absolutePath = path.resolve(cwd, match);\n const patternPath = path.resolve(cwd, pattern);\n let originalAbsolutePath = absolutePath;\n\n if (localePositions.length > 0) {\n const placeholderPath = buildPlaceholderPathFromPattern(\n patternPath,\n absolutePath,\n localeTag\n );\n originalAbsolutePath = placeholderPath;\n }\n\n placeholderPaths.push(originalAbsolutePath);\n });\n }\n\n return { resolvedPaths, placeholderPaths };\n}\n\nfunction buildPlaceholderPathFromPattern(\n patternPath: string,\n absolutePath: string,\n localeTag: string\n): string {\n if (!patternPath.includes(localeTag)) {\n return absolutePath;\n }\n\n const posixPattern = toPosixPath(patternPath);\n const posixPath = toPosixPath(absolutePath);\n\n const baseRegex = micromatch.makeRe(posixPattern, {\n literalBrackets: true,\n });\n const localeRegexSource = baseRegex.source.replace(\n /\\\\\\[locale\\\\\\]/g,\n '([^/]+)'\n );\n const flags = baseRegex.flags.includes('d')\n ? baseRegex.flags\n : `${baseRegex.flags}d`;\n const matcher = new RegExp(localeRegexSource, flags);\n const match = matcher.exec(posixPath);\n\n const matchWithIndices = match as RegExpExecArray & {\n indices?: Array<[number, number]>;\n };\n\n if (!match || !matchWithIndices.indices) {\n return absolutePath;\n }\n\n let placeholderPosixPath = posixPath;\n const indices = matchWithIndices.indices;\n\n for (let i = indices.length - 1; i >= 1; i--) {\n const [start, end] = indices[i];\n if (start === -1 || end === -1) continue;\n placeholderPosixPath =\n placeholderPosixPath.slice(0, start) +\n localeTag +\n placeholderPosixPath.slice(end);\n }\n\n return path.normalize(placeholderPosixPath);\n}\n\nfunction toPosixPath(value: string): string {\n return value.split(path.sep).join(path.posix.sep);\n}\n\n/**\n * Classifies resolved file paths into publish/unpublish sets by matching\n * them against the given glob patterns. Uses POSIX paths for micromatch\n * compatibility but stores platform-native paths in the output sets.\n */\nfunction classifyPublishPaths(\n resolvedPaths: string[],\n publishPatterns: string[],\n unpublishPatterns: string[],\n cwd: string,\n locale: string,\n publishPaths: Set<string>,\n unpublishPaths: Set<string>\n): void {\n if (publishPatterns.length === 0 && unpublishPatterns.length === 0) return;\n\n const posixPaths = resolvedPaths.map(toPosixPath);\n const toAbsoluteGlob = (p: string) =>\n toPosixPath(path.resolve(cwd, p.replace(/\\[locale\\]/g, locale)));\n\n for (const pattern of publishPatterns) {\n const matched = new Set(micromatch(posixPaths, toAbsoluteGlob(pattern)));\n for (let i = 0; i < posixPaths.length; i++) {\n if (matched.has(posixPaths[i])) {\n publishPaths.add(resolvedPaths[i]);\n }\n }\n }\n\n for (const pattern of unpublishPatterns) {\n const matched = new Set(micromatch(posixPaths, toAbsoluteGlob(pattern)));\n for (let i = 0; i < posixPaths.length; i++) {\n if (matched.has(posixPaths[i])) {\n unpublishPaths.add(resolvedPaths[i]);\n }\n }\n }\n}\n\n/**\n * An .xcstrings catalog holds every locale in one file and is updated in\n * place: each locale's download is merged into the source catalog. A path\n * transform or a `[locale]` placeholder would map each locale to a separate\n * output, and every write would start from the source catalog and discard the\n * locales written before it.\n */\nfunction validateXcstringsInPlace(config: FilesOptions['xcstrings']): void {\n if (!config) return;\n if (config.transform) {\n logErrorAndExit(\n 'files.xcstrings.transform is not supported. An .xcstrings catalog holds every locale in one file and is updated in place, so remove the transform.'\n );\n }\n const localePatterns = normalizeIncludePatterns(\n config.include ?? []\n ).paths.filter((pattern) => pattern.includes('[locale]'));\n if (localePatterns.length > 0) {\n logErrorAndExit(\n `files.xcstrings.include must not contain [locale]: ${localePatterns\n .map((pattern) => `\"${pattern}\"`)\n .join(\n ', '\n )}. An .xcstrings catalog holds every locale in one file and is updated in place, so point the pattern at the catalog itself.`\n );\n }\n}\n\n/**\n * Validates a file-type requiresReview config value. Only a boolean or an\n * object of include/exclude glob string arrays is accepted — notably not\n * string \"true\"/\"false\", so a misquoted boolean fails loudly instead of\n * silently changing review policy (and with it, version identity).\n */\nfunction validateRequiresReviewConfig(\n config: unknown,\n fileType: string\n): RequiresReviewConfig | undefined {\n if (config === undefined || typeof config === 'boolean') {\n return config;\n }\n const isStringArray = (value: unknown): value is string[] =>\n Array.isArray(value) && value.every((item) => typeof item === 'string');\n if (\n config !== null &&\n typeof config === 'object' &&\n !Array.isArray(config) &&\n Object.keys(config).every((key) => key === 'include' || key === 'exclude')\n ) {\n const { include, exclude } = config as Record<string, unknown>;\n if (\n (include === undefined || isStringArray(include)) &&\n (exclude === undefined || isStringArray(exclude))\n ) {\n return config as RequiresReviewConfig;\n }\n }\n return logErrorAndExit(\n `files.${fileType}.requiresReview must be a boolean or an object of glob string arrays: { include?: string[], exclude?: string[] }`\n );\n}\n\n/**\n * Classifies resolved file paths by effective requiresReview policy and adds\n * paths whose policy is true to requiresReviewPaths. Precedence: file-type\n * include/exclude globs (exclude wins) > file-type boolean > top-level default.\n */\nfunction classifyRequiresReviewPaths(\n resolvedPaths: string[],\n config: RequiresReviewConfig | undefined,\n requiresReviewDefault: boolean,\n cwd: string,\n locale: string,\n requiresReviewPaths: Set<string>\n): void {\n if (typeof config === 'boolean' || config === undefined) {\n if (config ?? requiresReviewDefault) {\n for (const resolvedPath of resolvedPaths) {\n requiresReviewPaths.add(resolvedPath);\n }\n }\n return;\n }\n\n const posixPaths = resolvedPaths.map(toPosixPath);\n const toAbsoluteGlob = (p: string) =>\n toPosixPath(path.resolve(cwd, p.replace(/\\[locale\\]/g, locale)));\n const matchAny = (patterns: string[]) => {\n const matched = new Set<string>();\n for (const pattern of patterns) {\n for (const match of micromatch(posixPaths, toAbsoluteGlob(pattern))) {\n matched.add(match);\n }\n }\n return matched;\n };\n\n const included = matchAny(config.include ?? []);\n const excluded = matchAny(config.exclude ?? []);\n\n for (let i = 0; i < posixPaths.length; i++) {\n const requiresReview = excluded.has(posixPaths[i])\n ? false\n : included.has(posixPaths[i]) || requiresReviewDefault;\n if (requiresReview) {\n requiresReviewPaths.add(resolvedPaths[i]);\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const PACKAGE_VERSION = "2.
|
|
1
|
+
export declare const PACKAGE_VERSION = "2.21.0";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","names":[],"sources":["../../src/generated/version.ts"],"sourcesContent":["// This file is auto-generated. Do not edit manually.\nexport const PACKAGE_VERSION = '2.
|
|
1
|
+
{"version":3,"file":"version.js","names":[],"sources":["../../src/generated/version.ts"],"sourcesContent":["// This file is auto-generated. Do not edit manually.\nexport const PACKAGE_VERSION = '2.21.0';\n"],"mappings":";AACA,MAAa,kBAAkB"}
|
|
@@ -16,5 +16,5 @@ export declare function recordRemerged(filePath: string): void;
|
|
|
16
16
|
export declare function getDownloaded(): Set<string>;
|
|
17
17
|
/** Files that need postprocessing: downloaded OR re-merged */
|
|
18
18
|
export declare function getNeedsPostprocessing(): Set<string>;
|
|
19
|
-
export declare function getDownloadedMeta(): Map<string, DownloadMeta>;
|
|
19
|
+
export declare function getDownloadedMeta(): Map<string, DownloadMeta[]>;
|
|
20
20
|
export declare function clearDownloaded(): void;
|
|
@@ -4,7 +4,9 @@ const recentMeta = /* @__PURE__ */ new Map();
|
|
|
4
4
|
const remerged = /* @__PURE__ */ new Set();
|
|
5
5
|
function recordDownloaded(filePath, meta) {
|
|
6
6
|
recent.add(filePath);
|
|
7
|
-
if (meta)
|
|
7
|
+
if (!meta) return;
|
|
8
|
+
const metas = recentMeta.get(filePath) ?? [];
|
|
9
|
+
recentMeta.set(filePath, [...metas.filter((existing) => existing.locale !== meta.locale), meta]);
|
|
8
10
|
}
|
|
9
11
|
/**
|
|
10
12
|
* Track a file that was re-merged with the source
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recentDownloads.js","names":[],"sources":["../../src/state/recentDownloads.ts"],"sourcesContent":["import type { FileFormat } from 'generaltranslation/types';\n\nexport type DownloadMeta = {\n branchId: string;\n fileId: string;\n versionId: string;\n locale: string;\n // The format the file was written as, so it can be read back the same way.\n fileFormat: FileFormat;\n inputPath?: string;\n};\n\nconst recent = new Set<string>();\nconst recentMeta = new Map<string, DownloadMeta>();\nconst remerged = new Set<string>();\n\nexport function recordDownloaded(filePath: string, meta?: DownloadMeta) {\n recent.add(filePath);\n if (meta)
|
|
1
|
+
{"version":3,"file":"recentDownloads.js","names":[],"sources":["../../src/state/recentDownloads.ts"],"sourcesContent":["import type { FileFormat } from 'generaltranslation/types';\n\nexport type DownloadMeta = {\n branchId: string;\n fileId: string;\n versionId: string;\n locale: string;\n // The format the file was written as, so it can be read back the same way.\n fileFormat: FileFormat;\n inputPath?: string;\n};\n\nconst recent = new Set<string>();\n// A file that holds every locale (a Mintlify composite docs.json) is written once per\n// locale, so a path keeps one entry per locale rather than the last one.\nconst recentMeta = new Map<string, DownloadMeta[]>();\nconst remerged = new Set<string>();\n\nexport function recordDownloaded(filePath: string, meta?: DownloadMeta) {\n recent.add(filePath);\n if (!meta) return;\n const metas = recentMeta.get(filePath) ?? [];\n recentMeta.set(filePath, [\n ...metas.filter((existing) => existing.locale !== meta.locale),\n meta,\n ]);\n}\n\n/**\n * Track a file that was re-merged with the source\n * so that postprocessing still runs on it.\n */\nexport function recordRemerged(filePath: string) {\n remerged.add(filePath);\n}\n\nexport function getDownloaded(): Set<string> {\n return recent;\n}\n\n/** Files that need postprocessing: downloaded OR re-merged */\nexport function getNeedsPostprocessing(): Set<string> {\n return new Set([...recent, ...remerged]);\n}\n\nexport function getDownloadedMeta(): Map<string, DownloadMeta[]> {\n return recentMeta;\n}\n\nexport function clearDownloaded() {\n recent.clear();\n recentMeta.clear();\n remerged.clear();\n}\n"],"mappings":";AAYA,MAAM,yBAAS,IAAI,KAAa;AAGhC,MAAM,6BAAa,IAAI,KAA6B;AACpD,MAAM,2BAAW,IAAI,KAAa;AAElC,SAAgB,iBAAiB,UAAkB,MAAqB;AACtE,QAAO,IAAI,SAAS;AACpB,KAAI,CAAC,KAAM;CACX,MAAM,QAAQ,WAAW,IAAI,SAAS,IAAI,EAAE;AAC5C,YAAW,IAAI,UAAU,CACvB,GAAG,MAAM,QAAQ,aAAa,SAAS,WAAW,KAAK,OAAO,EAC9D,KACD,CAAC;;;;;;AAOJ,SAAgB,eAAe,UAAkB;AAC/C,UAAS,IAAI,SAAS;;AAGxB,SAAgB,gBAA6B;AAC3C,QAAO;;;AAIT,SAAgB,yBAAsC;AACpD,QAAO,IAAI,IAAI,CAAC,GAAG,QAAQ,GAAG,SAAS,CAAC;;AAG1C,SAAgB,oBAAiD;AAC/D,QAAO;;AAGT,SAAgB,kBAAkB;AAChC,QAAO,OAAO;AACd,YAAW,OAAO;AAClB,UAAS,OAAO"}
|
|
@@ -3,4 +3,4 @@ import type { Settings } from '../types/index.js';
|
|
|
3
3
|
/**
|
|
4
4
|
* Persist postprocessed content hashes for recently downloaded files into gt-lock.json.
|
|
5
5
|
*/
|
|
6
|
-
export declare function persistPostProcessHashes(settings: Settings, includeFiles: Set<string> | undefined, downloadedMeta: Map<string, DownloadMeta>): void;
|
|
6
|
+
export declare function persistPostProcessHashes(settings: Settings, includeFiles: Set<string> | undefined, downloadedMeta: Map<string, DownloadMeta[]>): void;
|
|
@@ -5,6 +5,7 @@ import { fileEncodingSkipReason } from "../console/index.js";
|
|
|
5
5
|
import { readFileContent } from "../fs/fileContent.js";
|
|
6
6
|
import { recordWarning } from "../state/translateWarnings.js";
|
|
7
7
|
import { findOrCreateEntry, readLockfile, writeLockfile } from "../fs/config/downloadedVersions.js";
|
|
8
|
+
import { emptyLocaleContent, localeContent } from "../formats/files/localeContent.js";
|
|
8
9
|
import * as fs$1 from "node:fs";
|
|
9
10
|
//#region src/utils/persistPostprocessHashes.ts
|
|
10
11
|
/**
|
|
@@ -20,12 +21,13 @@ function persistPostProcessHashes(settings, includeFiles, downloadedMeta) {
|
|
|
20
21
|
});
|
|
21
22
|
let lockUpdated = false;
|
|
22
23
|
for (const filePath of includeFiles) {
|
|
23
|
-
const
|
|
24
|
-
if (!
|
|
24
|
+
const metas = downloadedMeta.get(filePath);
|
|
25
|
+
if (!metas) continue;
|
|
25
26
|
if (!fs$1.existsSync(filePath)) continue;
|
|
26
|
-
|
|
27
|
+
const hashes = [];
|
|
27
28
|
try {
|
|
28
|
-
|
|
29
|
+
const content = readFileContent(filePath, metas[0].fileFormat);
|
|
30
|
+
for (const meta of metas) hashes.push([meta, hashStringSync(localeContent(content, meta.fileFormat, meta.locale) ?? emptyLocaleContent(content, meta.fileFormat))]);
|
|
29
31
|
} catch (error) {
|
|
30
32
|
const relativePath = getRelative(filePath);
|
|
31
33
|
const reason = fileEncodingSkipReason(error);
|
|
@@ -33,22 +35,24 @@ function persistPostProcessHashes(settings, includeFiles, downloadedMeta) {
|
|
|
33
35
|
recordWarning("skipped_file", relativePath, reason);
|
|
34
36
|
continue;
|
|
35
37
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
for (const [meta, hash] of hashes) {
|
|
39
|
+
const entry = findOrCreateEntry(entryMap, data.entries, meta.fileId, meta.versionId);
|
|
40
|
+
const existing = entry.translations[meta.locale] || {};
|
|
41
|
+
if (existing.postProcessHash !== hash) {
|
|
42
|
+
entry.translations[meta.locale] = {
|
|
43
|
+
...existing,
|
|
44
|
+
postProcessHash: hash
|
|
45
|
+
};
|
|
46
|
+
lockUpdated = true;
|
|
47
|
+
}
|
|
44
48
|
}
|
|
45
49
|
}
|
|
46
50
|
if (lockUpdated) writeLockfile(data, originalV1);
|
|
47
51
|
}
|
|
48
52
|
function findDownloadedBranchId(includeFiles, downloadedMeta) {
|
|
49
53
|
for (const filePath of includeFiles) {
|
|
50
|
-
const
|
|
51
|
-
if (
|
|
54
|
+
const branchId = downloadedMeta.get(filePath)?.[0]?.branchId;
|
|
55
|
+
if (branchId) return branchId;
|
|
52
56
|
}
|
|
53
57
|
}
|
|
54
58
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persistPostprocessHashes.js","names":["fs"],"sources":["../../src/utils/persistPostprocessHashes.ts"],"sourcesContent":["import * as fs from 'node:fs';\nimport {\n findOrCreateEntry,\n readLockfile,\n writeLockfile,\n} from '../fs/config/downloadedVersions.js';\nimport { hashStringSync } from './hash.js';\nimport { fileEncodingSkipReason } from '../console/index.js';\nimport { logger } from '../console/logger.js';\nimport { getRelative } from '../fs/findFilepath.js';\nimport { recordWarning } from '../state/translateWarnings.js';\nimport { readFileContent } from '../fs/fileContent.js';\nimport type { DownloadMeta } from '../state/recentDownloads.js';\nimport type { Settings } from '../types/index.js';\n\n/**\n * Persist postprocessed content hashes for recently downloaded files into gt-lock.json.\n */\nexport function persistPostProcessHashes(\n settings: Settings,\n includeFiles: Set<string> | undefined,\n downloadedMeta: Map<string, DownloadMeta>\n): void {\n if (!includeFiles || includeFiles.size === 0 || downloadedMeta.size === 0) {\n return;\n }\n\n const branchId = findDownloadedBranchId(includeFiles, downloadedMeta);\n if (!branchId) return;\n\n const { data, entryMap, originalV1 } = readLockfile({\n ...settings,\n _branchId: branchId,\n });\n let lockUpdated = false;\n\n for (const filePath of includeFiles) {\n const
|
|
1
|
+
{"version":3,"file":"persistPostprocessHashes.js","names":["fs"],"sources":["../../src/utils/persistPostprocessHashes.ts"],"sourcesContent":["import * as fs from 'node:fs';\nimport {\n findOrCreateEntry,\n readLockfile,\n writeLockfile,\n} from '../fs/config/downloadedVersions.js';\nimport { hashStringSync } from './hash.js';\nimport { fileEncodingSkipReason } from '../console/index.js';\nimport { logger } from '../console/logger.js';\nimport { getRelative } from '../fs/findFilepath.js';\nimport { recordWarning } from '../state/translateWarnings.js';\nimport { readFileContent } from '../fs/fileContent.js';\nimport {\n emptyLocaleContent,\n localeContent,\n} from '../formats/files/localeContent.js';\nimport type { DownloadMeta } from '../state/recentDownloads.js';\nimport type { Settings } from '../types/index.js';\n\n/**\n * Persist postprocessed content hashes for recently downloaded files into gt-lock.json.\n */\nexport function persistPostProcessHashes(\n settings: Settings,\n includeFiles: Set<string> | undefined,\n downloadedMeta: Map<string, DownloadMeta[]>\n): void {\n if (!includeFiles || includeFiles.size === 0 || downloadedMeta.size === 0) {\n return;\n }\n\n const branchId = findDownloadedBranchId(includeFiles, downloadedMeta);\n if (!branchId) return;\n\n const { data, entryMap, originalV1 } = readLockfile({\n ...settings,\n _branchId: branchId,\n });\n let lockUpdated = false;\n\n for (const filePath of includeFiles) {\n const metas = downloadedMeta.get(filePath);\n if (!metas) continue;\n if (!fs.existsSync(filePath)) continue;\n\n // Each hash stands for the locale's share of the file's pipeline content,\n // which is what upload records and user-edit detection compares against.\n const hashes: [DownloadMeta, string][] = [];\n try {\n const content = readFileContent(filePath, metas[0].fileFormat);\n for (const meta of metas) {\n hashes.push([\n meta,\n hashStringSync(\n localeContent(content, meta.fileFormat, meta.locale) ??\n emptyLocaleContent(content, meta.fileFormat)\n ),\n ]);\n }\n } catch (error) {\n // The translation is already written; failing here would lose the whole\n // run's lockfile update over one unreadable file. Skip it and report it\n const relativePath = getRelative(filePath);\n const reason = fileEncodingSkipReason(error);\n logger.warn(`Skipping ${relativePath}: ${reason}`);\n recordWarning('skipped_file', relativePath, reason);\n continue;\n }\n\n for (const [meta, hash] of hashes) {\n const entry = findOrCreateEntry(\n entryMap,\n data.entries,\n meta.fileId,\n meta.versionId\n );\n\n const existing = entry.translations[meta.locale] || {};\n\n if (existing.postProcessHash !== hash) {\n entry.translations[meta.locale] = {\n ...existing,\n postProcessHash: hash,\n };\n lockUpdated = true;\n }\n }\n }\n\n if (lockUpdated) {\n writeLockfile(data, originalV1);\n }\n}\n\nfunction findDownloadedBranchId(\n includeFiles: Set<string>,\n downloadedMeta: Map<string, DownloadMeta[]>\n): string | undefined {\n for (const filePath of includeFiles) {\n const branchId = downloadedMeta.get(filePath)?.[0]?.branchId;\n if (branchId) return branchId;\n }\n return undefined;\n}\n"],"mappings":";;;;;;;;;;;;;AAsBA,SAAgB,yBACd,UACA,cACA,gBACM;AACN,KAAI,CAAC,gBAAgB,aAAa,SAAS,KAAK,eAAe,SAAS,EACtE;CAGF,MAAM,WAAW,uBAAuB,cAAc,eAAe;AACrE,KAAI,CAAC,SAAU;CAEf,MAAM,EAAE,MAAM,UAAU,eAAe,aAAa;EAClD,GAAG;EACH,WAAW;EACZ,CAAC;CACF,IAAI,cAAc;AAElB,MAAK,MAAM,YAAY,cAAc;EACnC,MAAM,QAAQ,eAAe,IAAI,SAAS;AAC1C,MAAI,CAAC,MAAO;AACZ,MAAI,CAACA,KAAG,WAAW,SAAS,CAAE;EAI9B,MAAM,SAAmC,EAAE;AAC3C,MAAI;GACF,MAAM,UAAU,gBAAgB,UAAU,MAAM,GAAG,WAAW;AAC9D,QAAK,MAAM,QAAQ,MACjB,QAAO,KAAK,CACV,MACA,eACE,cAAc,SAAS,KAAK,YAAY,KAAK,OAAO,IAClD,mBAAmB,SAAS,KAAK,WAAW,CAC/C,CACF,CAAC;WAEG,OAAO;GAGd,MAAM,eAAe,YAAY,SAAS;GAC1C,MAAM,SAAS,uBAAuB,MAAM;AAC5C,UAAO,KAAK,YAAY,aAAa,IAAI,SAAS;AAClD,iBAAc,gBAAgB,cAAc,OAAO;AACnD;;AAGF,OAAK,MAAM,CAAC,MAAM,SAAS,QAAQ;GACjC,MAAM,QAAQ,kBACZ,UACA,KAAK,SACL,KAAK,QACL,KAAK,UACN;GAED,MAAM,WAAW,MAAM,aAAa,KAAK,WAAW,EAAE;AAEtD,OAAI,SAAS,oBAAoB,MAAM;AACrC,UAAM,aAAa,KAAK,UAAU;KAChC,GAAG;KACH,iBAAiB;KAClB;AACD,kBAAc;;;;AAKpB,KAAI,YACF,eAAc,MAAM,WAAW;;AAInC,SAAS,uBACP,cACA,gBACoB;AACpB,MAAK,MAAM,YAAY,cAAc;EACnC,MAAM,WAAW,eAAe,IAAI,SAAS,GAAG,IAAI;AACpD,MAAI,SAAU,QAAO"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gt",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.21.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"bin": "bin/main.js",
|
|
6
6
|
"files": [
|
|
@@ -115,13 +115,13 @@
|
|
|
115
115
|
"unified": "^11.0.5",
|
|
116
116
|
"unist-util-visit": "^5.0.0",
|
|
117
117
|
"yaml": "^2.8.0",
|
|
118
|
+
"@generaltranslation/icu": "0.1.2",
|
|
118
119
|
"@generaltranslation/format": "0.1.8",
|
|
119
|
-
"@generaltranslation/
|
|
120
|
-
"generaltranslation": "
|
|
121
|
-
"@generaltranslation/vue-extractor": "0.1.
|
|
122
|
-
"
|
|
123
|
-
"
|
|
124
|
-
"@generaltranslation/icu": "0.1.2"
|
|
120
|
+
"@generaltranslation/python-extractor": "0.2.48",
|
|
121
|
+
"@generaltranslation/supported-locales": "2.1.28",
|
|
122
|
+
"@generaltranslation/vue-extractor": "0.1.8",
|
|
123
|
+
"generaltranslation": "9.3.0",
|
|
124
|
+
"gt-remark": "1.0.12"
|
|
125
125
|
},
|
|
126
126
|
"devDependencies": {
|
|
127
127
|
"@types/babel__generator": "^7.27.0",
|