@rushstack/webpack5-localization-plugin 0.1.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.
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":"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","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"}