@rushstack/webpack5-localization-plugin 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. package/LICENSE +24 -0
  2. package/README.md +209 -0
  3. package/dist/tsdoc-metadata.json +11 -0
  4. package/dist/webpack5-localization-plugin.d.ts +274 -0
  5. package/lib/AssetProcessor.d.ts +26 -0
  6. package/lib/AssetProcessor.d.ts.map +1 -0
  7. package/lib/AssetProcessor.js +243 -0
  8. package/lib/AssetProcessor.js.map +1 -0
  9. package/lib/LocalizationPlugin.d.ts +79 -0
  10. package/lib/LocalizationPlugin.d.ts.map +1 -0
  11. package/lib/LocalizationPlugin.js +586 -0
  12. package/lib/LocalizationPlugin.js.map +1 -0
  13. package/lib/index.d.ts +4 -0
  14. package/lib/index.d.ts.map +1 -0
  15. package/lib/index.js +8 -0
  16. package/lib/index.js.map +1 -0
  17. package/lib/interfaces.d.ts +173 -0
  18. package/lib/interfaces.d.ts.map +1 -0
  19. package/lib/interfaces.js +5 -0
  20. package/lib/interfaces.js.map +1 -0
  21. package/lib/loaders/IResxLoaderOptions.d.ts +12 -0
  22. package/lib/loaders/IResxLoaderOptions.d.ts.map +1 -0
  23. package/lib/loaders/IResxLoaderOptions.js +5 -0
  24. package/lib/loaders/IResxLoaderOptions.js.map +1 -0
  25. package/lib/loaders/LoaderFactory.d.ts +6 -0
  26. package/lib/loaders/LoaderFactory.d.ts.map +1 -0
  27. package/lib/loaders/LoaderFactory.js +28 -0
  28. package/lib/loaders/LoaderFactory.js.map +1 -0
  29. package/lib/loaders/default-locale-loader.d.ts +8 -0
  30. package/lib/loaders/default-locale-loader.d.ts.map +1 -0
  31. package/lib/loaders/default-locale-loader.js +26 -0
  32. package/lib/loaders/default-locale-loader.js.map +1 -0
  33. package/lib/loaders/loc-loader.d.ts +15 -0
  34. package/lib/loaders/loc-loader.d.ts.map +1 -0
  35. package/lib/loaders/loc-loader.js +23 -0
  36. package/lib/loaders/loc-loader.js.map +1 -0
  37. package/lib/loaders/locjson-loader.d.ts +5 -0
  38. package/lib/loaders/locjson-loader.d.ts.map +1 -0
  39. package/lib/loaders/locjson-loader.js +14 -0
  40. package/lib/loaders/locjson-loader.js.map +1 -0
  41. package/lib/loaders/resjson-loader.d.ts +5 -0
  42. package/lib/loaders/resjson-loader.d.ts.map +1 -0
  43. package/lib/loaders/resjson-loader.js +14 -0
  44. package/lib/loaders/resjson-loader.js.map +1 -0
  45. package/lib/loaders/resx-loader.d.ts +5 -0
  46. package/lib/loaders/resx-loader.d.ts.map +1 -0
  47. package/lib/loaders/resx-loader.js +21 -0
  48. package/lib/loaders/resx-loader.js.map +1 -0
  49. package/lib/utilities/Constants.d.ts +12 -0
  50. package/lib/utilities/Constants.d.ts.map +1 -0
  51. package/lib/utilities/Constants.js +17 -0
  52. package/lib/utilities/Constants.js.map +1 -0
  53. package/lib/utilities/EntityMarker.d.ts +9 -0
  54. package/lib/utilities/EntityMarker.d.ts.map +1 -0
  55. package/lib/utilities/EntityMarker.js +21 -0
  56. package/lib/utilities/EntityMarker.js.map +1 -0
  57. package/lib/utilities/LoaderTerminalProvider.d.ts +6 -0
  58. package/lib/utilities/LoaderTerminalProvider.d.ts.map +1 -0
  59. package/lib/utilities/LoaderTerminalProvider.js +28 -0
  60. package/lib/utilities/LoaderTerminalProvider.js.map +1 -0
  61. package/lib/webpackInterfaces.d.ts +13 -0
  62. package/lib/webpackInterfaces.d.ts.map +1 -0
  63. package/lib/webpackInterfaces.js +5 -0
  64. package/lib/webpackInterfaces.js.map +1 -0
  65. package/package.json +35 -0
@@ -0,0 +1,243 @@
1
+ "use strict";
2
+ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
3
+ // See LICENSE in the project root for license information.
4
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
5
+ if (k2 === undefined) k2 = k;
6
+ var desc = Object.getOwnPropertyDescriptor(m, k);
7
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
8
+ desc = { enumerable: true, get: function() { return m[k]; } };
9
+ }
10
+ Object.defineProperty(o, k2, desc);
11
+ }) : (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ o[k2] = m[k];
14
+ }));
15
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
16
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
17
+ }) : function(o, v) {
18
+ o["default"] = v;
19
+ });
20
+ var __importStar = (this && this.__importStar) || function (mod) {
21
+ if (mod && mod.__esModule) return mod;
22
+ var result = {};
23
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
24
+ __setModuleDefault(result, mod);
25
+ return result;
26
+ };
27
+ Object.defineProperty(exports, "__esModule", { value: true });
28
+ exports.processNonLocalizedAsset = exports.processLocalizedAsset = exports.PLACEHOLDER_REGEX = void 0;
29
+ const Constants = __importStar(require("./utilities/Constants"));
30
+ exports.PLACEHOLDER_REGEX = new RegExp(`${Constants.STRING_PLACEHOLDER_PREFIX}_(\\\\*)_([A-C])_([0-9a-f]+)`, 'g');
31
+ function processLocalizedAsset(options) {
32
+ const { compilation, asset, chunk, filenameTemplate, locales } = options;
33
+ const { sources, WebpackError } = compilation.compiler.webpack;
34
+ const rawSource = new sources.CachedSource(asset.source);
35
+ const assetSource = rawSource.source().toString();
36
+ const parsedAsset = _parseStringToReconstructionSequence(options.plugin, assetSource);
37
+ const { issues } = parsedAsset;
38
+ const localizedFiles = {};
39
+ chunk.localizedFiles = localizedFiles;
40
+ const { info: originInfo, name: originName } = asset;
41
+ if (!originInfo.related) {
42
+ originInfo.related = {};
43
+ }
44
+ for (const locale of locales) {
45
+ const { issues: localeIssues, result: localeResult } = _reconstructLocalized(new sources.ReplaceSource(rawSource, locale), parsedAsset.reconstructionSeries, locale, options.fillMissingTranslationStrings ? options.defaultLocale : undefined);
46
+ for (const issue of localeIssues) {
47
+ issues.push(issue);
48
+ }
49
+ const data = {
50
+ chunk,
51
+ contentHashType: 'javascript',
52
+ // The locale property will get processed by the extension to the getAssetPath hook
53
+ locale
54
+ };
55
+ const fileName = compilation.getAssetPath(filenameTemplate, data);
56
+ originInfo.related[locale] = fileName;
57
+ const info = {
58
+ ...originInfo,
59
+ locale
60
+ };
61
+ const wrapped = new sources.CachedSource(localeResult);
62
+ localizedFiles[locale] = fileName;
63
+ if (originName === fileName) {
64
+ // This helper throws if the asset doesn't already exist
65
+ compilation.updateAsset(fileName, wrapped, info);
66
+ }
67
+ else {
68
+ // This helper throws if the asset already exists
69
+ compilation.emitAsset(fileName, wrapped, info);
70
+ }
71
+ }
72
+ if (issues.length > 0) {
73
+ compilation.errors.push(new WebpackError(`localization:\n${issues.map((issue) => ` ${issue}`).join('\n')}`));
74
+ }
75
+ return localizedFiles;
76
+ }
77
+ exports.processLocalizedAsset = processLocalizedAsset;
78
+ function processNonLocalizedAsset(options) {
79
+ const { asset, fileName, compilation } = options;
80
+ const { sources, WebpackError } = compilation.compiler.webpack;
81
+ const rawSource = new sources.CachedSource(asset.source);
82
+ const assetSource = rawSource.source().toString();
83
+ const parsedAsset = _parseStringToReconstructionSequence(options.plugin, assetSource);
84
+ const { info: originInfo } = asset;
85
+ const { issues } = parsedAsset;
86
+ const locale = options.noStringsLocaleName;
87
+ const { issues: localeIssues, result } = _reconstructNonLocalized(new sources.ReplaceSource(rawSource, locale), parsedAsset.reconstructionSeries, locale);
88
+ for (const issue of localeIssues) {
89
+ issues.push(issue);
90
+ }
91
+ const info = {
92
+ ...originInfo,
93
+ locale
94
+ };
95
+ const wrapped = new sources.CachedSource(result);
96
+ compilation.updateAsset(fileName, wrapped, info);
97
+ if (issues.length > 0) {
98
+ options.compilation.errors.push(new WebpackError(`localization:\n${issues.map((issue) => ` ${issue}`).join('\n')}`));
99
+ }
100
+ }
101
+ exports.processNonLocalizedAsset = processNonLocalizedAsset;
102
+ const ESCAPE_MAP = new Map([
103
+ ['\r', 'r'],
104
+ ['\n', 'n'],
105
+ ['\t', 't'],
106
+ ['"', 'u0022'],
107
+ ["'", 'u0027']
108
+ ]);
109
+ const BACKSLASH_REGEX = /\\/g;
110
+ const ESCAPE_REGEX = /[\r\n\t"']/g;
111
+ function _reconstructLocalized(result, reconstructionSeries, locale, fallbackLocale) {
112
+ const issues = [];
113
+ for (const element of reconstructionSeries) {
114
+ switch (element.kind) {
115
+ case 'localized': {
116
+ const { data } = element;
117
+ let newValue = data.valuesByLocale.get(locale);
118
+ if (newValue === undefined) {
119
+ if (fallbackLocale) {
120
+ newValue = data.valuesByLocale.get(fallbackLocale);
121
+ }
122
+ else {
123
+ issues.push(`The string "${data.stringName}" in "${data.locFilePath}" is missing in ` +
124
+ `the locale ${locale}`);
125
+ newValue = '-- MISSING STRING --';
126
+ }
127
+ }
128
+ const escapedBackslash = element.escapedBackslash || '\\';
129
+ // Replace backslashes with the properly escaped backslash
130
+ BACKSLASH_REGEX.lastIndex = -1;
131
+ newValue = newValue.replace(BACKSLASH_REGEX, escapedBackslash);
132
+ // @todo: look into using JSON.parse(...) to get the escaping characters
133
+ const escapingCharacterSequence = escapedBackslash.slice(escapedBackslash.length / 2);
134
+ // Ensure the the quotemark, apostrophe, tab, and newline characters are properly escaped
135
+ ESCAPE_REGEX.lastIndex = -1;
136
+ newValue = newValue.replace(ESCAPE_REGEX, (match) => `${escapingCharacterSequence}${ESCAPE_MAP.get(match)}`);
137
+ result.replace(element.start, element.end - 1, newValue);
138
+ break;
139
+ }
140
+ case 'dynamic': {
141
+ const newValue = element.valueFn(locale);
142
+ result.replace(element.start, element.end - 1, newValue);
143
+ break;
144
+ }
145
+ }
146
+ }
147
+ return {
148
+ issues,
149
+ result
150
+ };
151
+ }
152
+ function _reconstructNonLocalized(result, reconstructionSeries, noStringsLocaleName) {
153
+ const issues = [];
154
+ for (const element of reconstructionSeries) {
155
+ switch (element.kind) {
156
+ case 'localized': {
157
+ issues.push(`The string "${element.data.stringName}" in "${element.data.locFilePath}" appeared in an asset ` +
158
+ 'that is not expected to contain localized resources.');
159
+ const newValue = '-- NOT EXPECTED TO BE LOCALIZED --';
160
+ result.replace(element.start, element.end - 1, newValue);
161
+ break;
162
+ }
163
+ case 'dynamic': {
164
+ const newValue = element.valueFn(noStringsLocaleName);
165
+ result.replace(element.start, element.end - 1, newValue);
166
+ break;
167
+ }
168
+ }
169
+ }
170
+ return {
171
+ issues,
172
+ result
173
+ };
174
+ }
175
+ function _rawLocaleToken(locale) {
176
+ return locale;
177
+ }
178
+ function _jsonLocaleToken(locale) {
179
+ return JSON.stringify(locale);
180
+ }
181
+ function _parseStringToReconstructionSequence(plugin, source) {
182
+ const issues = [];
183
+ const reconstructionSeries = [];
184
+ let regexResult;
185
+ exports.PLACEHOLDER_REGEX.lastIndex = -1;
186
+ while ((regexResult = exports.PLACEHOLDER_REGEX.exec(source))) {
187
+ const [placeholder, escapedBackslash, elementLabel, placeholderSerialNumber] = regexResult;
188
+ const start = regexResult.index;
189
+ const end = start + placeholder.length;
190
+ let localizedReconstructionElement;
191
+ switch (elementLabel) {
192
+ case Constants.STRING_PLACEHOLDER_LABEL: {
193
+ const stringData = plugin.getDataForSerialNumber(placeholderSerialNumber);
194
+ if (!stringData) {
195
+ issues.push(`Missing placeholder ${placeholder}`);
196
+ continue;
197
+ }
198
+ else {
199
+ const localizedElement = {
200
+ kind: 'localized',
201
+ start,
202
+ end,
203
+ escapedBackslash,
204
+ data: stringData
205
+ };
206
+ localizedReconstructionElement = localizedElement;
207
+ }
208
+ break;
209
+ }
210
+ case Constants.LOCALE_NAME_PLACEHOLDER_LABEL: {
211
+ const dynamicElement = {
212
+ kind: 'dynamic',
213
+ start,
214
+ end,
215
+ escapedBackslash,
216
+ valueFn: _rawLocaleToken
217
+ };
218
+ localizedReconstructionElement = dynamicElement;
219
+ break;
220
+ }
221
+ case Constants.JSONP_PLACEHOLDER_LABEL: {
222
+ const dynamicElement = {
223
+ kind: 'dynamic',
224
+ start,
225
+ end,
226
+ escapedBackslash,
227
+ valueFn: _jsonLocaleToken
228
+ };
229
+ localizedReconstructionElement = dynamicElement;
230
+ break;
231
+ }
232
+ default: {
233
+ throw new Error(`Unexpected label ${elementLabel}`);
234
+ }
235
+ }
236
+ reconstructionSeries.push(localizedReconstructionElement);
237
+ }
238
+ return {
239
+ issues,
240
+ reconstructionSeries
241
+ };
242
+ }
243
+ //# sourceMappingURL=AssetProcessor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AssetProcessor.js","sourceRoot":"","sources":["../src/AssetProcessor.ts"],"names":[],"mappings":";AAAA,4FAA4F;AAC5F,2DAA2D;;;;;;;;;;;;;;;;;;;;;;;;;;AAI3D,iEAAmD;AA6DtC,QAAA,iBAAiB,GAAW,IAAI,MAAM,CACjD,GAAG,SAAS,CAAC,yBAAyB,8BAA8B,EACpE,GAAG,CACJ,CAAC;AAEF,SAAgB,qBAAqB,CAAC,OAAsC;IAC1E,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAEzE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC;IAE/D,MAAM,SAAS,GAAyB,IAAI,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/E,MAAM,WAAW,GAAW,SAAS,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC;IAE1D,MAAM,WAAW,GAAiB,oCAAoC,CAAC,OAAO,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAEpG,MAAM,EAAE,MAAM,EAAE,GAAG,WAAW,CAAC;IAE/B,MAAM,cAAc,GAA2B,EAAE,CAAC;IACjD,KAAgC,CAAC,cAAc,GAAG,cAAc,CAAC;IAElE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,KAAK,CAAC;IACrD,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE;QACvB,UAAU,CAAC,OAAO,GAAG,EAAE,CAAC;KACzB;IAED,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE;QAC5B,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,GAAG,qBAAqB,CAC1E,IAAI,OAAO,CAAC,aAAa,CAAC,SAAS,EAAE,MAAM,CAAC,EAC5C,WAAW,CAAC,oBAAoB,EAChC,MAAM,EACN,OAAO,CAAC,6BAA6B,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAC1E,CAAC;QAEF,KAAK,MAAM,KAAK,IAAI,YAAY,EAAE;YAChC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;SACpB;QAED,MAAM,IAAI,GAAsB;YAC9B,KAAK;YACL,eAAe,EAAE,YAAY;YAC7B,mFAAmF;YACnF,MAAM;SACP,CAAC;QAEF,MAAM,QAAQ,GAAW,WAAW,CAAC,YAAY,CAAC,gBAAgB,EAAE,IAAI,CAAC,CAAC;QAE1E,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC;QAEtC,MAAM,IAAI,GAAc;YACtB,GAAG,UAAU;YACb,MAAM;SACP,CAAC;QAEF,MAAM,OAAO,GAAyB,IAAI,OAAO,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;QAC7E,cAAc,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC;QAElC,IAAI,UAAU,KAAK,QAAQ,EAAE;YAC3B,wDAAwD;YACxD,WAAW,CAAC,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;SAClD;aAAM;YACL,iDAAiD;YACjD,WAAW,CAAC,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;SAChD;KACF;IAED,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE;QACrB,WAAW,CAAC,MAAM,CAAC,IAAI,CACrB,IAAI,YAAY,CAAC,kBAAkB,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CACrF,CAAC;KACH;IAED,OAAO,cAAc,CAAC;AACxB,CAAC;AAnED,sDAmEC;AAED,SAAgB,wBAAwB,CAAC,OAAyC;IAChF,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC;IAEjD,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC;IAE/D,MAAM,SAAS,GAAyB,IAAI,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/E,MAAM,WAAW,GAAW,SAAS,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC;IAE1D,MAAM,WAAW,GAAiB,oCAAoC,CAAC,OAAO,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAEpG,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,KAAK,CAAC;IACnC,MAAM,EAAE,MAAM,EAAE,GAAG,WAAW,CAAC;IAE/B,MAAM,MAAM,GAAW,OAAO,CAAC,mBAAmB,CAAC;IACnD,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,wBAAwB,CAC/D,IAAI,OAAO,CAAC,aAAa,CAAC,SAAS,EAAE,MAAM,CAAC,EAC5C,WAAW,CAAC,oBAAoB,EAChC,MAAM,CACP,CAAC;IAEF,KAAK,MAAM,KAAK,IAAI,YAAY,EAAE;QAChC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;KACpB;IAED,MAAM,IAAI,GAAc;QACtB,GAAG,UAAU;QACb,MAAM;KACP,CAAC;IAEF,MAAM,OAAO,GAAyB,IAAI,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IACvE,WAAW,CAAC,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IAEjD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE;QACrB,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,CAC7B,IAAI,YAAY,CAAC,kBAAkB,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CACrF,CAAC;KACH;AACH,CAAC;AArCD,4DAqCC;AAED,MAAM,UAAU,GAAwB,IAAI,GAAG,CAAC;IAC9C,CAAC,IAAI,EAAE,GAAG,CAAC;IACX,CAAC,IAAI,EAAE,GAAG,CAAC;IACX,CAAC,IAAI,EAAE,GAAG,CAAC;IACX,CAAC,GAAG,EAAE,OAAO,CAAC;IACd,CAAC,GAAG,EAAE,OAAO,CAAC;CACf,CAAC,CAAC;AAEH,MAAM,eAAe,GAAW,KAAK,CAAC;AACtC,MAAM,YAAY,GAAW,aAAa,CAAC;AAE3C,SAAS,qBAAqB,CAC5B,MAA6B,EAC7B,oBAA8C,EAC9C,MAAc,EACd,cAAkC;IAElC,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,KAAK,MAAM,OAAO,IAAI,oBAAoB,EAAE;QAC1C,QAAQ,OAAO,CAAC,IAAI,EAAE;YACpB,KAAK,WAAW,CAAC,CAAC;gBAChB,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;gBACzB,IAAI,QAAQ,GAAuB,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBACnE,IAAI,QAAQ,KAAK,SAAS,EAAE;oBAC1B,IAAI,cAAc,EAAE;wBAClB,QAAQ,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,cAAc,CAAE,CAAC;qBACrD;yBAAM;wBACL,MAAM,CAAC,IAAI,CACT,eAAe,IAAI,CAAC,UAAU,SAAS,IAAI,CAAC,WAAW,kBAAkB;4BACvE,cAAc,MAAM,EAAE,CACzB,CAAC;wBAEF,QAAQ,GAAG,sBAAsB,CAAC;qBACnC;iBACF;gBAED,MAAM,gBAAgB,GAAW,OAAO,CAAC,gBAAgB,IAAI,IAAI,CAAC;gBAElE,0DAA0D;gBAC1D,eAAe,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;gBAC/B,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,eAAe,EAAE,gBAAgB,CAAC,CAAC;gBAE/D,wEAAwE;gBACxE,MAAM,yBAAyB,GAAW,gBAAgB,CAAC,KAAK,CAAC,gBAAgB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;gBAE9F,yFAAyF;gBACzF,YAAY,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;gBAC5B,QAAQ,GAAG,QAAQ,CAAC,OAAO,CACzB,YAAY,EACZ,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,yBAAyB,GAAG,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAClE,CAAC;gBAEF,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,GAAG,CAAC,EAAE,QAAQ,CAAC,CAAC;gBACzD,MAAM;aACP;YAED,KAAK,SAAS,CAAC,CAAC;gBACd,MAAM,QAAQ,GAAW,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;gBACjD,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,GAAG,CAAC,EAAE,QAAQ,CAAC,CAAC;gBACzD,MAAM;aACP;SACF;KACF;IAED,OAAO;QACL,MAAM;QACN,MAAM;KACP,CAAC;AACJ,CAAC;AAED,SAAS,wBAAwB,CAC/B,MAA6B,EAC7B,oBAA8C,EAC9C,mBAA2B;IAE3B,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,KAAK,MAAM,OAAO,IAAI,oBAAoB,EAAE;QAC1C,QAAQ,OAAO,CAAC,IAAI,EAAE;YACpB,KAAK,WAAW,CAAC,CAAC;gBAChB,MAAM,CAAC,IAAI,CACT,eAAe,OAAO,CAAC,IAAI,CAAC,UAAU,SAAS,OAAO,CAAC,IAAI,CAAC,WAAW,yBAAyB;oBAC9F,sDAAsD,CACzD,CAAC;gBAEF,MAAM,QAAQ,GAAW,oCAAoC,CAAC;gBAC9D,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,GAAG,CAAC,EAAE,QAAQ,CAAC,CAAC;gBACzD,MAAM;aACP;YAED,KAAK,SAAS,CAAC,CAAC;gBACd,MAAM,QAAQ,GAAW,OAAO,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC;gBAC9D,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,GAAG,CAAC,EAAE,QAAQ,CAAC,CAAC;gBACzD,MAAM;aACP;SACF;KACF;IAED,OAAO;QACL,MAAM;QACN,MAAM;KACP,CAAC;AACJ,CAAC;AAED,SAAS,eAAe,CAAC,MAAc;IACrC,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,gBAAgB,CAAC,MAAc;IACtC,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;AAChC,CAAC;AAED,SAAS,oCAAoC,CAAC,MAA0B,EAAE,MAAc;IACtF,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,oBAAoB,GAA6B,EAAE,CAAC;IAE1D,IAAI,WAAmC,CAAC;IACxC,yBAAiB,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;IACjC,OAAO,CAAC,WAAW,GAAG,yBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE;QACrD,MAAM,CAAC,WAAW,EAAE,gBAAgB,EAAE,YAAY,EAAE,uBAAuB,CAAC,GAAG,WAAW,CAAC;QAC3F,MAAM,KAAK,GAAW,WAAW,CAAC,KAAK,CAAC;QACxC,MAAM,GAAG,GAAW,KAAK,GAAG,WAAW,CAAC,MAAM,CAAC;QAE/C,IAAI,8BAAsD,CAAC;QAC3D,QAAQ,YAAY,EAAE;YACpB,KAAK,SAAS,CAAC,wBAAwB,CAAC,CAAC;gBACvC,MAAM,UAAU,GACd,MAAM,CAAC,sBAAsB,CAAC,uBAAuB,CAAC,CAAC;gBACzD,IAAI,CAAC,UAAU,EAAE;oBACf,MAAM,CAAC,IAAI,CAAC,uBAAuB,WAAW,EAAE,CAAC,CAAC;oBAClD,SAAS;iBACV;qBAAM;oBACL,MAAM,gBAAgB,GAAoC;wBACxD,IAAI,EAAE,WAAW;wBACjB,KAAK;wBACL,GAAG;wBACH,gBAAgB;wBAChB,IAAI,EAAE,UAAU;qBACjB,CAAC;oBACF,8BAA8B,GAAG,gBAAgB,CAAC;iBACnD;gBACD,MAAM;aACP;YAED,KAAK,SAAS,CAAC,6BAA6B,CAAC,CAAC;gBAC5C,MAAM,cAAc,GAAkC;oBACpD,IAAI,EAAE,SAAS;oBACf,KAAK;oBACL,GAAG;oBACH,gBAAgB;oBAChB,OAAO,EAAE,eAAe;iBACzB,CAAC;gBACF,8BAA8B,GAAG,cAAc,CAAC;gBAChD,MAAM;aACP;YAED,KAAK,SAAS,CAAC,uBAAuB,CAAC,CAAC;gBACtC,MAAM,cAAc,GAAkC;oBACpD,IAAI,EAAE,SAAS;oBACf,KAAK;oBACL,GAAG;oBACH,gBAAgB;oBAChB,OAAO,EAAE,gBAAgB;iBAC1B,CAAC;gBACF,8BAA8B,GAAG,cAAc,CAAC;gBAChD,MAAM;aACP;YAED,OAAO,CAAC,CAAC;gBACP,MAAM,IAAI,KAAK,CAAC,oBAAoB,YAAY,EAAE,CAAC,CAAC;aACrD;SACF;QAED,oBAAoB,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;KAC3D;IAED,OAAO;QACL,MAAM;QACN,oBAAoB;KACrB,CAAC;AACJ,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport type { Asset, AssetInfo, Chunk, Compilation, sources } from 'webpack';\n\nimport * as Constants from './utilities/Constants';\nimport type { LocalizationPlugin, IStringPlaceholder } from './LocalizationPlugin';\nimport type { ILocalizedWebpackChunk, IAssetPathOptions } from './webpackInterfaces';\n\ninterface ILocalizedReconstructionElement {\n kind: 'localized';\n start: number;\n end: number;\n escapedBackslash: string;\n data: IStringPlaceholder;\n}\n\ninterface IDynamicReconstructionElement {\n kind: 'dynamic';\n start: number;\n end: number;\n escapedBackslash: string;\n valueFn: (locale: string) => string;\n}\n\ntype IReconstructionElement = ILocalizedReconstructionElement | IDynamicReconstructionElement;\n\ninterface IParseResult {\n issues: string[];\n reconstructionSeries: IReconstructionElement[];\n}\n\ninterface ILocalizedReconstructionResult {\n result: sources.ReplaceSource;\n issues: string[];\n}\n\ninterface INonLocalizedReconstructionResult {\n result: sources.ReplaceSource;\n issues: string[];\n}\n\nexport interface IProcessAssetOptionsBase {\n plugin: LocalizationPlugin;\n compilation: Compilation;\n chunk: Chunk;\n asset: Asset;\n}\n\nexport interface IProcessNonLocalizedAssetOptions extends IProcessAssetOptionsBase {\n fileName: string;\n noStringsLocaleName: string;\n}\n\nexport interface IProcessLocalizedAssetOptions extends IProcessAssetOptionsBase {\n locales: Set<string>;\n fillMissingTranslationStrings: boolean;\n defaultLocale: string;\n filenameTemplate: Parameters<typeof Compilation.prototype.getAssetPath>[0];\n}\n\nexport interface IProcessAssetResult {\n filename: string;\n asset: sources.Source;\n}\n\nexport const PLACEHOLDER_REGEX: RegExp = new RegExp(\n `${Constants.STRING_PLACEHOLDER_PREFIX}_(\\\\\\\\*)_([A-C])_([0-9a-f]+)`,\n 'g'\n);\n\nexport function processLocalizedAsset(options: IProcessLocalizedAssetOptions): Record<string, string> {\n const { compilation, asset, chunk, filenameTemplate, locales } = options;\n\n const { sources, WebpackError } = compilation.compiler.webpack;\n\n const rawSource: sources.CachedSource = new sources.CachedSource(asset.source);\n const assetSource: string = rawSource.source().toString();\n\n const parsedAsset: IParseResult = _parseStringToReconstructionSequence(options.plugin, assetSource);\n\n const { issues } = parsedAsset;\n\n const localizedFiles: Record<string, string> = {};\n (chunk as ILocalizedWebpackChunk).localizedFiles = localizedFiles;\n\n const { info: originInfo, name: originName } = asset;\n if (!originInfo.related) {\n originInfo.related = {};\n }\n\n for (const locale of locales) {\n const { issues: localeIssues, result: localeResult } = _reconstructLocalized(\n new sources.ReplaceSource(rawSource, locale),\n parsedAsset.reconstructionSeries,\n locale,\n options.fillMissingTranslationStrings ? options.defaultLocale : undefined\n );\n\n for (const issue of localeIssues) {\n issues.push(issue);\n }\n\n const data: IAssetPathOptions = {\n chunk,\n contentHashType: 'javascript',\n // The locale property will get processed by the extension to the getAssetPath hook\n locale\n };\n\n const fileName: string = compilation.getAssetPath(filenameTemplate, data);\n\n originInfo.related[locale] = fileName;\n\n const info: AssetInfo = {\n ...originInfo,\n locale\n };\n\n const wrapped: sources.CachedSource = new sources.CachedSource(localeResult);\n localizedFiles[locale] = fileName;\n\n if (originName === fileName) {\n // This helper throws if the asset doesn't already exist\n compilation.updateAsset(fileName, wrapped, info);\n } else {\n // This helper throws if the asset already exists\n compilation.emitAsset(fileName, wrapped, info);\n }\n }\n\n if (issues.length > 0) {\n compilation.errors.push(\n new WebpackError(`localization:\\n${issues.map((issue) => ` ${issue}`).join('\\n')}`)\n );\n }\n\n return localizedFiles;\n}\n\nexport function processNonLocalizedAsset(options: IProcessNonLocalizedAssetOptions): void {\n const { asset, fileName, compilation } = options;\n\n const { sources, WebpackError } = compilation.compiler.webpack;\n\n const rawSource: sources.CachedSource = new sources.CachedSource(asset.source);\n const assetSource: string = rawSource.source().toString();\n\n const parsedAsset: IParseResult = _parseStringToReconstructionSequence(options.plugin, assetSource);\n\n const { info: originInfo } = asset;\n const { issues } = parsedAsset;\n\n const locale: string = options.noStringsLocaleName;\n const { issues: localeIssues, result } = _reconstructNonLocalized(\n new sources.ReplaceSource(rawSource, locale),\n parsedAsset.reconstructionSeries,\n locale\n );\n\n for (const issue of localeIssues) {\n issues.push(issue);\n }\n\n const info: AssetInfo = {\n ...originInfo,\n locale\n };\n\n const wrapped: sources.CachedSource = new sources.CachedSource(result);\n compilation.updateAsset(fileName, wrapped, info);\n\n if (issues.length > 0) {\n options.compilation.errors.push(\n new WebpackError(`localization:\\n${issues.map((issue) => ` ${issue}`).join('\\n')}`)\n );\n }\n}\n\nconst ESCAPE_MAP: Map<string, string> = new Map([\n ['\\r', 'r'],\n ['\\n', 'n'],\n ['\\t', 't'],\n ['\"', 'u0022'],\n [\"'\", 'u0027']\n]);\n\nconst BACKSLASH_REGEX: RegExp = /\\\\/g;\nconst ESCAPE_REGEX: RegExp = /[\\r\\n\\t\"']/g;\n\nfunction _reconstructLocalized(\n result: sources.ReplaceSource,\n reconstructionSeries: IReconstructionElement[],\n locale: string,\n fallbackLocale: string | undefined\n): ILocalizedReconstructionResult {\n const issues: string[] = [];\n\n for (const element of reconstructionSeries) {\n switch (element.kind) {\n case 'localized': {\n const { data } = element;\n let newValue: string | undefined = data.valuesByLocale.get(locale);\n if (newValue === undefined) {\n if (fallbackLocale) {\n newValue = data.valuesByLocale.get(fallbackLocale)!;\n } else {\n issues.push(\n `The string \"${data.stringName}\" in \"${data.locFilePath}\" is missing in ` +\n `the locale ${locale}`\n );\n\n newValue = '-- MISSING STRING --';\n }\n }\n\n const escapedBackslash: string = element.escapedBackslash || '\\\\';\n\n // Replace backslashes with the properly escaped backslash\n BACKSLASH_REGEX.lastIndex = -1;\n newValue = newValue.replace(BACKSLASH_REGEX, escapedBackslash);\n\n // @todo: look into using JSON.parse(...) to get the escaping characters\n const escapingCharacterSequence: string = escapedBackslash.slice(escapedBackslash.length / 2);\n\n // Ensure the the quotemark, apostrophe, tab, and newline characters are properly escaped\n ESCAPE_REGEX.lastIndex = -1;\n newValue = newValue.replace(\n ESCAPE_REGEX,\n (match) => `${escapingCharacterSequence}${ESCAPE_MAP.get(match)}`\n );\n\n result.replace(element.start, element.end - 1, newValue);\n break;\n }\n\n case 'dynamic': {\n const newValue: string = element.valueFn(locale);\n result.replace(element.start, element.end - 1, newValue);\n break;\n }\n }\n }\n\n return {\n issues,\n result\n };\n}\n\nfunction _reconstructNonLocalized(\n result: sources.ReplaceSource,\n reconstructionSeries: IReconstructionElement[],\n noStringsLocaleName: string\n): INonLocalizedReconstructionResult {\n const issues: string[] = [];\n\n for (const element of reconstructionSeries) {\n switch (element.kind) {\n case 'localized': {\n issues.push(\n `The string \"${element.data.stringName}\" in \"${element.data.locFilePath}\" appeared in an asset ` +\n 'that is not expected to contain localized resources.'\n );\n\n const newValue: string = '-- NOT EXPECTED TO BE LOCALIZED --';\n result.replace(element.start, element.end - 1, newValue);\n break;\n }\n\n case 'dynamic': {\n const newValue: string = element.valueFn(noStringsLocaleName);\n result.replace(element.start, element.end - 1, newValue);\n break;\n }\n }\n }\n\n return {\n issues,\n result\n };\n}\n\nfunction _rawLocaleToken(locale: string): string {\n return locale;\n}\n\nfunction _jsonLocaleToken(locale: string): string {\n return JSON.stringify(locale);\n}\n\nfunction _parseStringToReconstructionSequence(plugin: LocalizationPlugin, source: string): IParseResult {\n const issues: string[] = [];\n const reconstructionSeries: IReconstructionElement[] = [];\n\n let regexResult: RegExpExecArray | null;\n PLACEHOLDER_REGEX.lastIndex = -1;\n while ((regexResult = PLACEHOLDER_REGEX.exec(source))) {\n const [placeholder, escapedBackslash, elementLabel, placeholderSerialNumber] = regexResult;\n const start: number = regexResult.index;\n const end: number = start + placeholder.length;\n\n let localizedReconstructionElement: IReconstructionElement;\n switch (elementLabel) {\n case Constants.STRING_PLACEHOLDER_LABEL: {\n const stringData: IStringPlaceholder | undefined =\n plugin.getDataForSerialNumber(placeholderSerialNumber);\n if (!stringData) {\n issues.push(`Missing placeholder ${placeholder}`);\n continue;\n } else {\n const localizedElement: ILocalizedReconstructionElement = {\n kind: 'localized',\n start,\n end,\n escapedBackslash,\n data: stringData\n };\n localizedReconstructionElement = localizedElement;\n }\n break;\n }\n\n case Constants.LOCALE_NAME_PLACEHOLDER_LABEL: {\n const dynamicElement: IDynamicReconstructionElement = {\n kind: 'dynamic',\n start,\n end,\n escapedBackslash,\n valueFn: _rawLocaleToken\n };\n localizedReconstructionElement = dynamicElement;\n break;\n }\n\n case Constants.JSONP_PLACEHOLDER_LABEL: {\n const dynamicElement: IDynamicReconstructionElement = {\n kind: 'dynamic',\n start,\n end,\n escapedBackslash,\n valueFn: _jsonLocaleToken\n };\n localizedReconstructionElement = dynamicElement;\n break;\n }\n\n default: {\n throw new Error(`Unexpected label ${elementLabel}`);\n }\n }\n\n reconstructionSeries.push(localizedReconstructionElement);\n }\n\n return {\n issues,\n reconstructionSeries\n };\n}\n"]}
@@ -0,0 +1,79 @@
1
+ import type { Compiler, LoaderContext, WebpackPluginInstance } from 'webpack';
2
+ import { ILocalizationFile } from '@rushstack/localization-utilities';
3
+ import type { ILocalizationPluginOptions } from './interfaces';
4
+ /**
5
+ * @public
6
+ */
7
+ export interface IStringPlaceholder {
8
+ /**
9
+ * The literal string that will be injected for later replacement.
10
+ */
11
+ value: string;
12
+ /**
13
+ * The identifier for this particular placeholder, for lookup.
14
+ */
15
+ suffix: string;
16
+ /**
17
+ * The values of this string in each output locale.
18
+ */
19
+ valuesByLocale: Map<string, string>;
20
+ /**
21
+ * The key used to identify the source file containing the string.
22
+ */
23
+ locFilePath: string;
24
+ /**
25
+ * The identifier of the string within its original source file.
26
+ */
27
+ stringName: string;
28
+ }
29
+ /**
30
+ * Gets the instance of the LocalizationPlugin bound to the specified webpack compiler.
31
+ * Used by loaders.
32
+ */
33
+ export declare function getPluginInstance(compiler: Compiler | undefined): LocalizationPlugin;
34
+ /**
35
+ * This plugin facilitates localization in webpack.
36
+ *
37
+ * @public
38
+ */
39
+ export declare class LocalizationPlugin implements WebpackPluginInstance {
40
+ private readonly _stringKeys;
41
+ private readonly _options;
42
+ private readonly _resolvedTranslatedStringsFromOptions;
43
+ private _stringPlaceholderCounter;
44
+ private readonly _stringPlaceholderMap;
45
+ private _passthroughLocaleName;
46
+ private _defaultLocale;
47
+ private _noStringsLocaleName;
48
+ private _fillMissingTranslationStrings;
49
+ private readonly _pseudolocalizers;
50
+ /**
51
+ * The outermost map's keys are the locale names.
52
+ * The middle map's keys are the resolved, file names.
53
+ * The innermost map's keys are the string identifiers and its values are the string values.
54
+ */
55
+ private _resolvedLocalizedStrings;
56
+ constructor(options: ILocalizationPluginOptions);
57
+ /**
58
+ * Apply this plugin to the specified webpack compiler.
59
+ */
60
+ apply(compiler: Compiler): void;
61
+ /**
62
+ * @public
63
+ *
64
+ * @returns An object mapping the string keys to placeholders
65
+ */
66
+ addDefaultLocFileAsync(context: LoaderContext<{}>, localizedFileKey: string, localizedResourceData: ILocalizationFile): Promise<Record<string, string>>;
67
+ /**
68
+ * @public
69
+ */
70
+ getPlaceholder(localizedFileKey: string, stringName: string): IStringPlaceholder | undefined;
71
+ /**
72
+ * @internal
73
+ */
74
+ getDataForSerialNumber(serialNumber: string): IStringPlaceholder | undefined;
75
+ private _addLocFileAndGetPlaceholders;
76
+ private _addTranslations;
77
+ private _initializeAndValidateOptions;
78
+ }
79
+ //# sourceMappingURL=LocalizationPlugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LocalizationPlugin.d.ts","sourceRoot":"","sources":["../src/LocalizationPlugin.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAKV,QAAQ,EACR,aAAa,EAIb,qBAAqB,EACtB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAsB,iBAAiB,EAAgB,MAAM,mCAAmC,CAAC;AAGxG,OAAO,KAAK,EACV,0BAA0B,EAK3B,MAAM,cAAc,CAAC;AAKtB;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAMD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,GAAG,kBAAkB,CAMpF;AAED;;;;GAIG;AACH,qBAAa,kBAAmB,YAAW,qBAAqB;IAC9D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA8C;IAE1E,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;IACtD,OAAO,CAAC,QAAQ,CAAC,qCAAqC,CAGxC;IACd,OAAO,CAAC,yBAAyB,CAAa;IAC9C,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAA8C;IACpF,OAAO,CAAC,sBAAsB,CAAU;IACxC,OAAO,CAAC,cAAc,CAAU;IAChC,OAAO,CAAC,oBAAoB,CAAU;IACtC,OAAO,CAAC,8BAA8B,CAAW;IACjD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAmD;IAErF;;;;OAIG;IACH,OAAO,CAAC,yBAAyB,CAA4D;gBAE1E,OAAO,EAAE,0BAA0B;IAItD;;OAEG;IACI,KAAK,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI;IA8QtC;;;;OAIG;IACU,sBAAsB,CACjC,OAAO,EAAE,aAAa,CAAC,EAAE,CAAC,EAC1B,gBAAgB,EAAE,MAAM,EACxB,qBAAqB,EAAE,iBAAiB,GACvC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAsElC;;OAEG;IACI,cAAc,CAAC,gBAAgB,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAKnG;;OAEG;IACI,sBAAsB,CAAC,YAAY,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAInF,OAAO,CAAC,6BAA6B;IA0CrC,OAAO,CAAC,gBAAgB;IAcxB,OAAO,CAAC,6BAA6B;CA4KtC"}