@zohodesk/client_build_tool 0.0.11-exp.25.0 → 0.0.11-exp.27.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/README.md +0 -102
- package/README_backup.md +0 -102
- package/lib/schemas/defaultConfigValues.js +13 -29
- package/lib/schemas/defaultConfigValuesOnly.js +10 -14
- package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/I18nGroupRuntimeModule.js +101 -167
- package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/I18nNumericIndexHtmlInjectorPlugin.js +39 -24
- package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/I18nNumericIndexPlugin.js +233 -86
- package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/utils/i18nDataLoader.js +18 -1
- package/lib/shared/bundler/webpack/loaders/i18nIdReplaceLoader.js +15 -54
- package/lib/shared/bundler/webpack/optimizationConfig.js +1 -4
- package/lib/shared/bundler/webpack/pluginConfigs/configI18nNumericIndexPlugin.js +3 -1
- package/lib/shared/bundler/webpack/plugins.js +1 -4
- package/package.json +1 -1
- package/docs/DYNAMIC_TEMPLATE_EXAMPLE.md +0 -129
- package/docs/I18N_NUMERIC_INDEXING_PLUGIN.md +0 -225
- package/docs/I18N_SINGLE_FILE_MODE.md +0 -126
- package/docs/client_build_tool_source_doc.md +0 -390
package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/I18nNumericIndexPlugin.js
CHANGED
|
@@ -22,8 +22,27 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
22
22
|
const {
|
|
23
23
|
RawSource
|
|
24
24
|
} = _webpack.sources;
|
|
25
|
+
const assetStoreKey = Symbol.for('I18nNumericIndexPluginAssets');
|
|
25
26
|
const pluginName = 'I18nNumericIndexPlugin';
|
|
26
27
|
|
|
28
|
+
function buildChunkMappingFromGroups(customGroups) {
|
|
29
|
+
if (!customGroups) {
|
|
30
|
+
return {};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const mapping = {};
|
|
34
|
+
Object.entries(customGroups).forEach(([groupName, config]) => {
|
|
35
|
+
if (config && Array.isArray(config.chunks)) {
|
|
36
|
+
config.chunks.forEach(chunkName => {
|
|
37
|
+
if (chunkName && typeof chunkName === 'string') {
|
|
38
|
+
mapping[chunkName] = groupName;
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
return mapping;
|
|
44
|
+
}
|
|
45
|
+
|
|
27
46
|
class I18nNumericIndexPlugin {
|
|
28
47
|
constructor(options) {
|
|
29
48
|
this.options = { ...options,
|
|
@@ -33,27 +52,45 @@ class I18nNumericIndexPlugin {
|
|
|
33
52
|
generateManifest: options.generateManifest || false,
|
|
34
53
|
outputFolder: options.outputFolder || 'i18n-chunk',
|
|
35
54
|
manifestPath: options.manifestPath || null,
|
|
36
|
-
emitFiles: options.emitFiles !== undefined ? options.emitFiles : true
|
|
55
|
+
emitFiles: options.emitFiles !== undefined ? options.emitFiles : true,
|
|
56
|
+
chunkToGroupMapping: options.chunkToGroupMapping || {}
|
|
37
57
|
};
|
|
38
58
|
this.numericMap = {};
|
|
39
59
|
this.customGroups = {};
|
|
40
60
|
this.manifest = {};
|
|
61
|
+
this.groupAssetUrls = {};
|
|
62
|
+
this.preparedAssets = [];
|
|
63
|
+
this.assetsPrepared = false;
|
|
64
|
+
this.assetsEmitted = false;
|
|
65
|
+
this.groupChunkMapping = buildChunkMappingFromGroups(this.options.customGroups);
|
|
41
66
|
}
|
|
42
67
|
|
|
43
68
|
apply(compiler) {
|
|
44
|
-
//
|
|
69
|
+
// Build-time validation for critical file paths
|
|
70
|
+
this.validateCriticalFilePaths(compiler); // Detect webpackI18nGroup comments in code
|
|
71
|
+
|
|
45
72
|
this.detectI18nGroupComments(compiler);
|
|
46
73
|
compiler.hooks.thisCompilation.tap(pluginName, compilation => {
|
|
47
|
-
|
|
74
|
+
this.groupAssetUrls = {};
|
|
75
|
+
this.detectedGroups = {};
|
|
76
|
+
this.preparedAssets = [];
|
|
77
|
+
this.assetsPrepared = false;
|
|
78
|
+
this.assetsEmitted = false; // Add runtime module for group-based loading
|
|
79
|
+
|
|
48
80
|
if (this.options.customGroups) {
|
|
49
81
|
compilation.hooks.additionalTreeRuntimeRequirements.tap(pluginName, (chunk, set) => {
|
|
50
82
|
// Only add to the main/entry chunk to avoid duplication
|
|
51
83
|
if (chunk.name === 'main' || chunk.hasRuntime()) {
|
|
84
|
+
this.ensureAssetsPrepared(compilation);
|
|
85
|
+
const chunkMapping = this.getChunkIdToGroupMapping(compilation);
|
|
52
86
|
compilation.addRuntimeModule(chunk, new _I18nGroupRuntimeModule.I18nGroupRuntimeModule({
|
|
53
87
|
customGroups: this.options.customGroups,
|
|
88
|
+
chunkIdToGroupMapping: chunkMapping,
|
|
54
89
|
localeVarName: this.options.localeVarName,
|
|
55
90
|
jsonpFunc: this.options.jsonpFunc,
|
|
56
|
-
i18nPublicPathVar: this.options.i18nPublicPathVar
|
|
91
|
+
i18nPublicPathVar: this.options.i18nPublicPathVar,
|
|
92
|
+
groupAssetUrls: this.groupAssetUrls,
|
|
93
|
+
includeContentHash: this.options.includeContentHash
|
|
57
94
|
}));
|
|
58
95
|
}
|
|
59
96
|
});
|
|
@@ -63,44 +100,133 @@ class I18nNumericIndexPlugin {
|
|
|
63
100
|
name: pluginName,
|
|
64
101
|
stage: compilation.PROCESS_ASSETS_STAGE_OPTIMIZE
|
|
65
102
|
}, () => {
|
|
66
|
-
this.
|
|
103
|
+
this.ensureAssetsPrepared(compilation);
|
|
104
|
+
this.emitPreparedAssets(compilation);
|
|
67
105
|
});
|
|
68
106
|
});
|
|
69
107
|
}
|
|
70
108
|
|
|
109
|
+
validateCriticalFilePaths(compiler) {
|
|
110
|
+
const {
|
|
111
|
+
jsResourcePath,
|
|
112
|
+
propertiesFolderPath,
|
|
113
|
+
numericMapPath
|
|
114
|
+
} = this.options;
|
|
115
|
+
const missingPaths = [];
|
|
116
|
+
const resolvedPaths = []; // Check jsResourcePath (must exist)
|
|
117
|
+
|
|
118
|
+
if (jsResourcePath) {
|
|
119
|
+
const resolvedJsPath = _path.default.resolve(compiler.context, jsResourcePath);
|
|
120
|
+
|
|
121
|
+
resolvedPaths.push(`jsResourcePath: ${resolvedJsPath}`);
|
|
122
|
+
|
|
123
|
+
if (!_fs.default.existsSync(resolvedJsPath)) {
|
|
124
|
+
missingPaths.push(`jsResourcePath: ${resolvedJsPath}`);
|
|
125
|
+
}
|
|
126
|
+
} else {
|
|
127
|
+
missingPaths.push('jsResourcePath: [not configured]');
|
|
128
|
+
} // Check propertiesFolderPath (must exist)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
if (propertiesFolderPath) {
|
|
132
|
+
const resolvedPropsPath = _path.default.resolve(compiler.context, propertiesFolderPath);
|
|
133
|
+
|
|
134
|
+
resolvedPaths.push(`propertiesFolderPath: ${resolvedPropsPath}`);
|
|
135
|
+
|
|
136
|
+
if (!_fs.default.existsSync(resolvedPropsPath)) {
|
|
137
|
+
missingPaths.push(`propertiesFolderPath: ${resolvedPropsPath}`);
|
|
138
|
+
}
|
|
139
|
+
} else {
|
|
140
|
+
missingPaths.push('propertiesFolderPath: [not configured]');
|
|
141
|
+
} // Check numericMapPath (must exist)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
if (numericMapPath) {
|
|
145
|
+
const resolvedMapPath = _path.default.resolve(compiler.context, numericMapPath);
|
|
146
|
+
|
|
147
|
+
resolvedPaths.push(`numericMapPath: ${resolvedMapPath}`);
|
|
148
|
+
|
|
149
|
+
if (!_fs.default.existsSync(resolvedMapPath)) {
|
|
150
|
+
missingPaths.push(`numericMapPath: ${resolvedMapPath}`);
|
|
151
|
+
}
|
|
152
|
+
} else {
|
|
153
|
+
missingPaths.push('numericMapPath: [not configured]');
|
|
154
|
+
} // If any critical files are missing, fail the build immediately
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
if (missingPaths.length > 0) {
|
|
158
|
+
const errorMessage = ['', '🚨 I18N BUILD FAILURE: Critical i18n files are missing!', '', 'The following required files could not be found:', ...missingPaths.map(path => ` ❌ ${path}`), '', 'These files are essential for i18n functionality. Please ensure they exist before building.', '', 'Resolved paths checked:', ...resolvedPaths.map(path => ` 📁 ${path}`), ''].join('\n');
|
|
159
|
+
throw new Error(errorMessage);
|
|
160
|
+
} // Log successful validation in development mode
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
if (compiler.options.mode === 'development') {
|
|
164
|
+
console.log('✅ I18n critical file validation passed:');
|
|
165
|
+
resolvedPaths.forEach(path => console.log(` 📁 ${path}`));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
71
169
|
detectI18nGroupComments(compiler) {
|
|
72
170
|
compiler.hooks.normalModuleFactory.tap(pluginName, factory => {
|
|
73
171
|
factory.hooks.parser.for('javascript/auto').tap(pluginName, parser => {
|
|
74
172
|
parser.hooks.importCall.tap(pluginName, expr => {
|
|
75
|
-
// Check for webpackI18nGroup comment
|
|
76
173
|
const comments = expr.leadingComments || [];
|
|
77
|
-
comments.forEach(comment => {
|
|
78
|
-
if (comment.value && comment.value.includes('webpackI18nGroup')) {
|
|
79
|
-
const match = comment.value.match(/webpackI18nGroup:\s*["']([^"']+)["']/);
|
|
80
174
|
|
|
81
|
-
|
|
82
|
-
|
|
175
|
+
if (comments.length > 0) {
|
|
176
|
+
comments.forEach(comment => {
|
|
177
|
+
if (comment.value && comment.value.includes('webpackI18nGroup')) {
|
|
178
|
+
const match = comment.value.match(/webpackI18nGroup:\s*["']([^"']+)["']/);
|
|
83
179
|
|
|
84
|
-
if (
|
|
85
|
-
|
|
86
|
-
} // Extract chunk name from webpackChunkName comment
|
|
180
|
+
if (match) {
|
|
181
|
+
const groupName = match[1];
|
|
87
182
|
|
|
183
|
+
if (!this.detectedGroups) {
|
|
184
|
+
this.detectedGroups = {};
|
|
185
|
+
}
|
|
88
186
|
|
|
89
|
-
|
|
187
|
+
const chunkNameMatch = comment.value.match(/webpackChunkName:\s*["']([^"']+)["']/);
|
|
90
188
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
189
|
+
if (chunkNameMatch) {
|
|
190
|
+
const chunkName = chunkNameMatch[1];
|
|
191
|
+
this.detectedGroups[chunkName] = groupName;
|
|
192
|
+
}
|
|
94
193
|
}
|
|
95
194
|
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
195
|
+
});
|
|
196
|
+
}
|
|
98
197
|
});
|
|
99
198
|
});
|
|
100
199
|
});
|
|
101
200
|
}
|
|
102
201
|
|
|
202
|
+
getChunkIdToGroupMapping(compilation) {
|
|
203
|
+
const chunkIdToGroup = {};
|
|
204
|
+
const configuredMappings = { ...this.groupChunkMapping,
|
|
205
|
+
...(this.options.chunkToGroupMapping || {})
|
|
206
|
+
}; // Process chunks to get their actual IDs and map to groups
|
|
207
|
+
|
|
208
|
+
for (const chunk of compilation.chunks) {
|
|
209
|
+
const chunkName = chunk.name; // First try detected groups, then fall back to configured mappings
|
|
210
|
+
|
|
211
|
+
let groupName = null;
|
|
212
|
+
|
|
213
|
+
if (this.detectedGroups && this.detectedGroups[chunkName]) {
|
|
214
|
+
groupName = this.detectedGroups[chunkName];
|
|
215
|
+
} else if (configuredMappings[chunkName]) {
|
|
216
|
+
groupName = configuredMappings[chunkName];
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (groupName) {
|
|
220
|
+
chunkIdToGroup[chunk.id] = groupName;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return chunkIdToGroup;
|
|
225
|
+
}
|
|
226
|
+
|
|
103
227
|
processI18nFiles(compilation) {
|
|
228
|
+
this.preparedAssets = [];
|
|
229
|
+
this.groupAssetUrls = {};
|
|
104
230
|
const {
|
|
105
231
|
jsResourcePath,
|
|
106
232
|
propertiesFolderPath,
|
|
@@ -113,14 +239,17 @@ class I18nNumericIndexPlugin {
|
|
|
113
239
|
|
|
114
240
|
if (!jsResourcePath || !propertiesFolderPath) {
|
|
115
241
|
return;
|
|
116
|
-
} //
|
|
242
|
+
} // Reset build-scoped caches so incremental builds don't carry stale data
|
|
243
|
+
|
|
117
244
|
|
|
245
|
+
this.numericMap = {};
|
|
246
|
+
this.manifest = {};
|
|
247
|
+
this.customGroups = {}; // Load existing numeric map if available
|
|
118
248
|
|
|
119
249
|
if (numericMapPath) {
|
|
120
250
|
const mapData = (0, _i18nDataLoader.loadNumericMap)(numericMapPath, compilation);
|
|
121
251
|
|
|
122
252
|
if (mapData && mapData.sortedKeys) {
|
|
123
|
-
// Initialize numericMap from existing data
|
|
124
253
|
mapData.sortedKeys.forEach((key, id) => {
|
|
125
254
|
if (key) {
|
|
126
255
|
this.numericMap[key] = id;
|
|
@@ -146,15 +275,9 @@ class I18nNumericIndexPlugin {
|
|
|
146
275
|
|
|
147
276
|
Object.keys(allI18nObject).forEach(loc => {
|
|
148
277
|
allI18nObject[loc] = this.normalizeObjectValues(allI18nObject[loc]);
|
|
149
|
-
}); // For en_US, use only JSResources
|
|
150
|
-
|
|
151
|
-
if (allI18nObject['en_US']) {
|
|
152
|
-
allI18nObject['en_US'] = jsResourceKeys;
|
|
153
|
-
} else {
|
|
154
|
-
// If en_US doesn't exist in the folder, create it from JSResources
|
|
155
|
-
allI18nObject['en_US'] = jsResourceKeys;
|
|
156
|
-
} // If requested, restrict all locales to base (English) keys only
|
|
278
|
+
}); // For en_US, use only JSResources
|
|
157
279
|
|
|
280
|
+
allI18nObject['en_US'] = jsResourceKeys; // If requested, restrict all locales to base (English) keys only
|
|
158
281
|
|
|
159
282
|
if (this.options.restrictToBaseKeys) {
|
|
160
283
|
const baseKeys = Object.keys(jsResourceKeys);
|
|
@@ -185,8 +308,11 @@ class I18nNumericIndexPlugin {
|
|
|
185
308
|
Object.keys(localeData).forEach(key => {
|
|
186
309
|
const value = localeData[key]; // Simple logic: if has numeric ID use it, otherwise it's dynamic
|
|
187
310
|
|
|
188
|
-
|
|
189
|
-
|
|
311
|
+
const numericId = this.numericMap[key];
|
|
312
|
+
const hasNumericId = numericId !== undefined && numericId !== null;
|
|
313
|
+
|
|
314
|
+
if (hasNumericId) {
|
|
315
|
+
const numericKey = String(numericId); // Check if belongs to a custom group
|
|
190
316
|
|
|
191
317
|
const belongsToGroup = this.getKeyGroup(key);
|
|
192
318
|
|
|
@@ -196,31 +322,29 @@ class I18nNumericIndexPlugin {
|
|
|
196
322
|
numericData[numericKey] = value;
|
|
197
323
|
}
|
|
198
324
|
} else {
|
|
199
|
-
// No numeric ID = dynamic key
|
|
325
|
+
// No numeric ID = dynamic key
|
|
200
326
|
dynamicData[key] = value;
|
|
201
327
|
}
|
|
202
328
|
}); // Handle single-file mode or separate files
|
|
203
329
|
|
|
204
330
|
if (this.options.singleFile) {
|
|
205
|
-
// Combine numeric and dynamic data into a single file
|
|
206
331
|
const combinedData = { ...numericData,
|
|
207
332
|
...dynamicData
|
|
208
333
|
};
|
|
209
334
|
|
|
210
335
|
if (Object.keys(combinedData).length > 0) {
|
|
211
|
-
// Use singleFileTemplate for combined file - no fileType suffix needed
|
|
212
336
|
const singleFileTemplate = this.options.singleFileTemplate || '[locale].js';
|
|
213
|
-
this.
|
|
337
|
+
this.prepareChunkAsset(compilation, singleFileTemplate, locale, combinedData, jsonpFunc, null, null);
|
|
214
338
|
}
|
|
215
339
|
} else {
|
|
216
340
|
// Emit numeric chunk
|
|
217
341
|
if (Object.keys(numericData).length > 0) {
|
|
218
|
-
this.
|
|
342
|
+
this.prepareChunkAsset(compilation, numericFilenameTemplate, locale, numericData, jsonpFunc, null, 'numeric');
|
|
219
343
|
} // Emit dynamic chunk
|
|
220
344
|
|
|
221
345
|
|
|
222
346
|
if (Object.keys(dynamicData).length > 0) {
|
|
223
|
-
this.
|
|
347
|
+
this.prepareChunkAsset(compilation, dynamicFilenameTemplate, locale, dynamicData, jsonpFunc, null, 'dynamic');
|
|
224
348
|
}
|
|
225
349
|
} // Emit custom group chunks (always separate)
|
|
226
350
|
|
|
@@ -230,7 +354,7 @@ class I18nNumericIndexPlugin {
|
|
|
230
354
|
|
|
231
355
|
if (groupConfig && Object.keys(data).length > 0) {
|
|
232
356
|
const groupTemplate = groupConfig.filenameTemplate || `[locale]/${groupName}.i18n.js`;
|
|
233
|
-
this.
|
|
357
|
+
this.prepareChunkAsset(compilation, groupTemplate, locale, data, jsonpFunc, groupName, `group-${groupName}`);
|
|
234
358
|
}
|
|
235
359
|
});
|
|
236
360
|
}); // Generate manifest file if enabled
|
|
@@ -239,9 +363,34 @@ class I18nNumericIndexPlugin {
|
|
|
239
363
|
const manifestPath = this.options.manifestPath || _path.default.join(this.options.outputFolder, 'manifest.json');
|
|
240
364
|
|
|
241
365
|
const manifestContent = JSON.stringify(this.manifest, null, 2);
|
|
242
|
-
|
|
243
|
-
|
|
366
|
+
this.preparedAssets.push({
|
|
367
|
+
outputPath: manifestPath,
|
|
368
|
+
source: new RawSource(manifestContent),
|
|
369
|
+
shouldEmit: true
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
ensureAssetsPrepared(compilation) {
|
|
375
|
+
if (this.assetsPrepared) {
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
this.processI18nFiles(compilation);
|
|
380
|
+
this.assetsPrepared = true;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
emitPreparedAssets(compilation) {
|
|
384
|
+
if (this.assetsEmitted) {
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
244
387
|
|
|
388
|
+
this.preparedAssets.forEach(asset => {
|
|
389
|
+
if (asset.shouldEmit !== false) {
|
|
390
|
+
compilation.emitAsset(asset.outputPath, asset.source);
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
this.assetsEmitted = true;
|
|
245
394
|
}
|
|
246
395
|
|
|
247
396
|
parseCustomGroups(jsResourcePath, customGroups) {
|
|
@@ -282,16 +431,6 @@ class I18nNumericIndexPlugin {
|
|
|
282
431
|
return null;
|
|
283
432
|
}
|
|
284
433
|
|
|
285
|
-
isDynamicKey(value) {
|
|
286
|
-
// Check if value contains placeholders like {0}, {1}, etc.
|
|
287
|
-
return /\{\d+\}/.test(value);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
getNumericKey(key) {
|
|
291
|
-
// Return numeric ID if it exists, null otherwise
|
|
292
|
-
return this.numericMap[key] ? String(this.numericMap[key]) : null;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
434
|
generateContentHash(content, compilation) {
|
|
296
435
|
const {
|
|
297
436
|
hashFunction,
|
|
@@ -307,31 +446,22 @@ class I18nNumericIndexPlugin {
|
|
|
307
446
|
|
|
308
447
|
constructFilePath(template, locale) {
|
|
309
448
|
const {
|
|
310
|
-
outputFolder
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
let filePath = template.replace(/\[locale\]/g, locale); // If template already contains outputFolder or starts with a path separator, use as-is
|
|
449
|
+
outputFolder
|
|
450
|
+
} = this.options;
|
|
451
|
+
const filePath = template.replace(/\[locale\]/g, locale); // If template already contains outputFolder or starts with path separator, use as-is
|
|
315
452
|
|
|
316
453
|
if (filePath.includes(outputFolder) || filePath.startsWith('/')) {
|
|
317
|
-
return filePath
|
|
318
|
-
} //
|
|
319
|
-
// put it directly in outputFolder without subdirectories
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
if (singleFile && !filePath.includes('/')) {
|
|
323
|
-
return _path.default.join(outputFolder, filePath).replace(/\\/g, '/');
|
|
324
|
-
} // For other cases, if template contains subdirectories, preserve them
|
|
454
|
+
return filePath;
|
|
455
|
+
} // Otherwise, join with outputFolder
|
|
325
456
|
|
|
326
457
|
|
|
327
458
|
return _path.default.join(outputFolder, filePath).replace(/\\/g, '/');
|
|
328
459
|
}
|
|
329
460
|
|
|
330
|
-
|
|
461
|
+
prepareChunkAsset(compilation, filenameTemplate, locale, data, jsonpFunc, groupName = null, fileType = null) {
|
|
331
462
|
if (!filenameTemplate || Object.keys(data).length === 0) {
|
|
332
463
|
return null;
|
|
333
|
-
}
|
|
334
|
-
|
|
464
|
+
}
|
|
335
465
|
|
|
336
466
|
const content = this.generateChunkContent(data, jsonpFunc, groupName);
|
|
337
467
|
let outputPath = this.constructFilePath(filenameTemplate, locale); // Handle [contenthash] placeholder in template
|
|
@@ -340,42 +470,59 @@ class I18nNumericIndexPlugin {
|
|
|
340
470
|
const contentHash = this.generateContentHash(content, compilation);
|
|
341
471
|
outputPath = outputPath.replace(/\[contenthash\]/g, contentHash);
|
|
342
472
|
} else if (this.options.includeContentHash) {
|
|
343
|
-
// Legacy: Add content hash before .js extension if includeContentHash is true
|
|
344
473
|
const contentHash = this.generateContentHash(content, compilation);
|
|
345
474
|
outputPath = outputPath.replace(/\.js$/, `.${contentHash}.js`);
|
|
346
475
|
} // Track in manifest if enabled
|
|
347
476
|
|
|
348
477
|
|
|
349
478
|
if (this.options.generateManifest) {
|
|
350
|
-
|
|
351
|
-
let manifestKey;
|
|
352
|
-
|
|
353
|
-
if (this.options.singleFile) {
|
|
354
|
-
// Just use locale.js without path or type suffix
|
|
355
|
-
manifestKey = `${locale}.js`;
|
|
356
|
-
} else {
|
|
357
|
-
// For multi-file mode, include the full path
|
|
358
|
-
const cleanName = this.constructFilePath(filenameTemplate, locale);
|
|
359
|
-
manifestKey = fileType ? cleanName.replace(/\.js$/, `.${fileType}.js`) : cleanName;
|
|
360
|
-
}
|
|
361
|
-
|
|
479
|
+
const manifestKey = this.options.singleFile ? `${locale}.js` : outputPath;
|
|
362
480
|
this.manifest[manifestKey] = _path.default.basename(outputPath);
|
|
363
|
-
}
|
|
481
|
+
}
|
|
364
482
|
|
|
483
|
+
const source = new RawSource(content);
|
|
484
|
+
const assetInfo = {
|
|
485
|
+
locale,
|
|
486
|
+
fileType,
|
|
487
|
+
outputPath,
|
|
488
|
+
filenameTemplate
|
|
489
|
+
};
|
|
490
|
+
this.registerEmittedAsset(compilation, assetInfo);
|
|
491
|
+
this.preparedAssets.push({
|
|
492
|
+
outputPath,
|
|
493
|
+
source,
|
|
494
|
+
shouldEmit: this.options.emitFiles,
|
|
495
|
+
info: assetInfo
|
|
496
|
+
});
|
|
497
|
+
return outputPath;
|
|
498
|
+
}
|
|
365
499
|
|
|
366
|
-
|
|
367
|
-
|
|
500
|
+
registerEmittedAsset(compilation, assetInfo) {
|
|
501
|
+
if (!Object.prototype.hasOwnProperty.call(compilation, assetStoreKey)) {
|
|
502
|
+
Object.defineProperty(compilation, assetStoreKey, {
|
|
503
|
+
configurable: true,
|
|
504
|
+
enumerable: false,
|
|
505
|
+
value: []
|
|
506
|
+
});
|
|
368
507
|
}
|
|
369
508
|
|
|
370
|
-
|
|
509
|
+
compilation[assetStoreKey].push(assetInfo);
|
|
510
|
+
|
|
511
|
+
if (assetInfo.fileType && assetInfo.fileType.startsWith('group-')) {
|
|
512
|
+
const groupName = assetInfo.fileType.slice('group-'.length);
|
|
513
|
+
|
|
514
|
+
if (!this.groupAssetUrls[groupName]) {
|
|
515
|
+
this.groupAssetUrls[groupName] = {};
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
this.groupAssetUrls[groupName][assetInfo.locale] = assetInfo.outputPath;
|
|
519
|
+
}
|
|
371
520
|
}
|
|
372
521
|
|
|
373
522
|
generateChunkContent(data, jsonpFunc, groupName) {
|
|
374
|
-
// Decode Unicode escapes to convert \uXXXX to actual characters
|
|
375
523
|
const jsonString = (0, _propertiesUtils.decodeUnicodeEscapes)(JSON.stringify(data));
|
|
376
524
|
|
|
377
525
|
if (groupName) {
|
|
378
|
-
// Include group name for lazy loading identification
|
|
379
526
|
return `${jsonpFunc}(${jsonString}, "${groupName}");`;
|
|
380
527
|
}
|
|
381
528
|
|
package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/utils/i18nDataLoader.js
CHANGED
|
@@ -47,7 +47,24 @@ function loadNumericMap(numericMapPath, compilation) {
|
|
|
47
47
|
} // Create sorted array for numeric ID lookups
|
|
48
48
|
|
|
49
49
|
|
|
50
|
-
const
|
|
50
|
+
const values = Object.values(numericMap);
|
|
51
|
+
|
|
52
|
+
if (values.length === 0) {
|
|
53
|
+
throw new Error('numeric map is empty');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let maxId = 0;
|
|
57
|
+
values.forEach(id => {
|
|
58
|
+
const numericId = typeof id === 'number' ? id : Number(id);
|
|
59
|
+
|
|
60
|
+
if (Number.isNaN(numericId)) {
|
|
61
|
+
throw new Error(`invalid numeric map entry value: ${id}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (numericId > maxId) {
|
|
65
|
+
maxId = numericId;
|
|
66
|
+
}
|
|
67
|
+
});
|
|
51
68
|
const sortedKeys = new Array(maxId + 1);
|
|
52
69
|
Object.entries(numericMap).forEach(([key, id]) => {
|
|
53
70
|
sortedKeys[id] = key;
|
|
@@ -20,16 +20,7 @@ module.exports = function i18nIdReplaceLoader(source, map) {
|
|
|
20
20
|
const resourcePath = this.resourcePath;
|
|
21
21
|
this.cacheable && this.cacheable();
|
|
22
22
|
const options = getOptions(this) || {};
|
|
23
|
-
const callback = this.async();
|
|
24
|
-
let numericMapAbsPath = null;
|
|
25
|
-
|
|
26
|
-
if (options.numericMapPath) {
|
|
27
|
-
try {
|
|
28
|
-
numericMapAbsPath = path.isAbsolute(options.numericMapPath) ? options.numericMapPath : path.resolve(this.rootContext || this.context || process.cwd(), options.numericMapPath);
|
|
29
|
-
this.addDependency(numericMapAbsPath);
|
|
30
|
-
} catch (e) {}
|
|
31
|
-
} // Skip files in excluded paths
|
|
32
|
-
|
|
23
|
+
const callback = this.async(); // Skip files in excluded paths
|
|
33
24
|
|
|
34
25
|
if (options.excludePaths) {
|
|
35
26
|
const shouldExclude = options.excludePaths.some(excludePath => resourcePath.includes(excludePath));
|
|
@@ -51,46 +42,23 @@ module.exports = function i18nIdReplaceLoader(source, map) {
|
|
|
51
42
|
|
|
52
43
|
if (!options.allI18nData || Object.keys(options.allI18nData).length === 0) {
|
|
53
44
|
return callback(new Error(`i18nIdReplaceLoader: 'allI18nData' option is missing or empty`));
|
|
54
|
-
} // Load numeric ID mapping
|
|
45
|
+
} // Load numeric ID mapping
|
|
55
46
|
|
|
56
47
|
|
|
57
|
-
let numericIdMap = options.numericIdMap
|
|
48
|
+
let numericIdMap = options.numericIdMap;
|
|
58
49
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
global.__CBT_I18N_NUMERIC_MAP_CACHE__ = {};
|
|
64
|
-
}
|
|
50
|
+
if (!numericIdMap && options.numericMapPath) {
|
|
51
|
+
try {
|
|
52
|
+
const mapPath = path.isAbsolute(options.numericMapPath) ? options.numericMapPath : path.resolve(this.rootContext || this.context || process.cwd(), options.numericMapPath);
|
|
53
|
+
this.addDependency(mapPath);
|
|
65
54
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const stat = fs.statSync(p);
|
|
71
|
-
const key = `${p}`;
|
|
72
|
-
const mtime = stat && stat.mtimeMs ? stat.mtimeMs : 0;
|
|
73
|
-
const hit = cache[key];
|
|
74
|
-
|
|
75
|
-
if (hit && hit.mtime === mtime && hit.map) {
|
|
76
|
-
numericIdMap = hit.map;
|
|
77
|
-
} else if (fs.existsSync(p)) {
|
|
78
|
-
const txt = fs.readFileSync(p, 'utf-8');
|
|
79
|
-
const parsed = JSON.parse(txt);
|
|
80
|
-
const mapObj = parsed && parsed.originalKeyToNumericId ? parsed.originalKeyToNumericId : parsed;
|
|
81
|
-
|
|
82
|
-
if (mapObj && typeof mapObj === 'object') {
|
|
83
|
-
cache[key] = {
|
|
84
|
-
mtime,
|
|
85
|
-
map: mapObj
|
|
86
|
-
};
|
|
87
|
-
numericIdMap = mapObj;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
} catch (e) {// ignore and proceed without map
|
|
55
|
+
if (fs.existsSync(mapPath)) {
|
|
56
|
+
const mapContent = fs.readFileSync(mapPath, 'utf-8');
|
|
57
|
+
const parsed = JSON.parse(mapContent);
|
|
58
|
+
numericIdMap = parsed.originalKeyToNumericId || parsed;
|
|
91
59
|
}
|
|
60
|
+
} catch (e) {// Silently continue without numeric map
|
|
92
61
|
}
|
|
93
|
-
} catch (e) {// ignore
|
|
94
62
|
} // If no numeric map available, return source as-is
|
|
95
63
|
|
|
96
64
|
|
|
@@ -98,13 +66,11 @@ module.exports = function i18nIdReplaceLoader(source, map) {
|
|
|
98
66
|
return callback(null, source, map);
|
|
99
67
|
}
|
|
100
68
|
|
|
101
|
-
const isDevMode = options.devMode || process.env.NODE_ENV === 'development';
|
|
102
|
-
|
|
103
69
|
try {
|
|
104
70
|
// Parse the JavaScript/TypeScript source code
|
|
105
71
|
const ast = parser.parse(source, {
|
|
106
72
|
sourceType: 'module',
|
|
107
|
-
plugins: ['jsx', 'typescript'
|
|
73
|
+
plugins: ['jsx', 'typescript'],
|
|
108
74
|
sourceFilename: resourcePath
|
|
109
75
|
});
|
|
110
76
|
let hasTransformations = false; // Traverse AST and replace i18n keys with numeric IDs
|
|
@@ -113,14 +79,9 @@ module.exports = function i18nIdReplaceLoader(source, map) {
|
|
|
113
79
|
StringLiteral(path) {
|
|
114
80
|
const {
|
|
115
81
|
node
|
|
116
|
-
} = path; // Check if this string is an i18n key
|
|
117
|
-
|
|
118
|
-
if (!options.allI18nData.hasOwnProperty(node.value)) {
|
|
119
|
-
return;
|
|
120
|
-
} // Replace with numeric ID if available
|
|
121
|
-
|
|
82
|
+
} = path; // Check if this string is an i18n key and has numeric mapping
|
|
122
83
|
|
|
123
|
-
if (numericIdMap.hasOwnProperty(node.value)) {
|
|
84
|
+
if (options.allI18nData.hasOwnProperty(node.value) && numericIdMap.hasOwnProperty(node.value)) {
|
|
124
85
|
const numericId = String(numericIdMap[node.value]);
|
|
125
86
|
path.replaceWith(t.stringLiteral(numericId));
|
|
126
87
|
hasTransformations = true;
|
|
@@ -23,10 +23,7 @@ function optimizationConfig(options) {
|
|
|
23
23
|
} = options;
|
|
24
24
|
const {
|
|
25
25
|
chunkSplitEnable
|
|
26
|
-
} = options.i18nChunkSplit;
|
|
27
|
-
|
|
28
|
-
console.log('MINIFY_TEST: optimizationConfig called in mode:', options.mode);
|
|
29
|
-
console.log('MINIFY_TEST: TerserPlugin and CSS minification active');
|
|
26
|
+
} = options.i18nChunkSplit;
|
|
30
27
|
const i18nChunkFilename = (0, _nameTemplates.nameTemplates)('i18njs', options);
|
|
31
28
|
const chunkFilenameHasContentHash = (0, _hashUtils.hasContentHash)(i18nChunkFilename);
|
|
32
29
|
/**
|
|
@@ -90,6 +90,7 @@ function configI18nNumericIndexPlugin(options) {
|
|
|
90
90
|
generateManifest: i18nOpts.generateManifest || false,
|
|
91
91
|
manifestPath: i18nOpts.manifestPath || null,
|
|
92
92
|
customGroups: i18nOpts.customGroups || null,
|
|
93
|
+
chunkToGroupMapping: i18nOpts.chunkToGroupMapping || {},
|
|
93
94
|
emitFiles: i18nOpts.emitFiles !== undefined ? i18nOpts.emitFiles : true,
|
|
94
95
|
isDevelopment: isDevelopment,
|
|
95
96
|
i18nPublicPathVar: i18nOpts.i18nPublicPathVar
|
|
@@ -105,7 +106,8 @@ function configI18nNumericIndexPlugin(options) {
|
|
|
105
106
|
i18nAssetsPublicPathPrefix: '',
|
|
106
107
|
injectI18nUrlInIndex: i18nOpts.injectI18nUrlInIndex !== undefined ? i18nOpts.injectI18nUrlInIndex : true,
|
|
107
108
|
// Control HTML injection
|
|
108
|
-
isDevelopment: isDevelopment
|
|
109
|
+
isDevelopment: isDevelopment,
|
|
110
|
+
includeContentHash: i18nOpts.includeContentHash || false
|
|
109
111
|
};
|
|
110
112
|
const i18nNumericPluginInstance = new _I18nNumericIndexPlugin.default(numericIndexPluginOptions);
|
|
111
113
|
const htmlInjectorPluginInstance = new _I18nNumericIndexHtmlInjectorPlugin.I18nNumericIndexHtmlInjectorPlugin(htmlInjectorOptions);
|
|
@@ -55,9 +55,6 @@ var _configCustomScriptLoadingStrategyPlugin = require("./pluginConfigs/configCu
|
|
|
55
55
|
function plugins(options) {
|
|
56
56
|
const {
|
|
57
57
|
webpackPlugins
|
|
58
|
-
} = options;
|
|
59
|
-
|
|
60
|
-
console.log('MINIFY_TEST: plugins function called with options:', options.mode);
|
|
61
|
-
console.log('MINIFY_TEST: This message should be visible if minification is disabled');
|
|
58
|
+
} = options;
|
|
62
59
|
return [(0, _configEnvVariables.configEnvVariables)(options), (0, _configCustomAttributesPlugin.configCustomAttributesPlugin)(options), (0, _configTPHashMappingPlugin.configTPHashMappingPlugin)(options), (0, _configCopyPublicFolders.configCopyPublicFolders)(options), (0, _configIgnorePlugin.configIgnorePlugin)(options), (0, _configMiniCSSExtractPlugin.configMiniCSSExtractPlugin)(options), (0, _configSelectorWeightPlugin.configSelectorWeightPlugin)(options), (0, _configVariableConversionPlugin.configVariableConversionPlugin)(options), (0, _configI18nSplitPlugin.configI18nSplitPlugin)(options), ...((0, _configI18nNumericIndexPlugin.configI18nNumericIndexPlugin)(options) || []), (0, _configRtlCssPlugin.configRtlCssPlugin)(options), (0, _configHtmlWebpackPlugin.configHtmlWebpackPlugin)(options), (0, _configCustomScriptLoadingStrategyPlugin.configCustomScriptLoadingStrategyPlugin)(options), (0, _configCdnChangePlugin.configCdnChangePlugin)(options), (0, _configServiceWorkerPlugin.configServiceWorkerPlugin)(options), (0, _configEFCTemplatePlugin.configEFCTemplatePlugin)(options), (0, _configResourceHintsPlugin.configResourceHintsPlugin)(options), (0, _configBundleAnalyzer.configBundleAnalyzer)(options), (0, _configManifestJsonPlugin.configManifestJsonPlugin)(options), (0, _configSourceMapPlugin.configSourceMapPlugin)(options), (0, _configProgressPlugin.configProgressPlugin)(options), (0, _configBundleIntegrityReport.configBundleIntegrityReport)(options), (0, _configRuntimeResourceCleanup.configRuntimeResourceCleanup)(options), ...webpackPlugins].filter(Boolean);
|
|
63
60
|
}
|