@zohodesk/client_build_tool 0.0.11-exp.28.0 → 0.0.11-exp.30.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/lib/schemas/defaultConfigValues.js +1 -2
- package/lib/schemas/defaultConfigValuesOnly.js +1 -2
- package/lib/shared/bundler/webpack/common/i18nOptionsValidator.js +1 -24
- package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/I18nGroupRuntimeModule.js +11 -124
- package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/I18nNumericIndexPlugin.js +15 -88
- package/lib/shared/bundler/webpack/custom_plugins/I18nSplitPlugin/utils/propertiesUtils.js +2 -15
- package/lib/shared/bundler/webpack/jsLoaders.js +3 -6
- package/lib/shared/bundler/webpack/loaderConfigs/i18nIdReplaceLoaderConfig.js +6 -16
- package/lib/shared/bundler/webpack/loaders/i18nIdReplaceLoader.js +31 -15
- package/lib/shared/bundler/webpack/pluginConfigs/configI18nNumericHtmlInjector.js +9 -19
- package/lib/shared/bundler/webpack/pluginConfigs/configI18nNumericIndexPlugin.js +1 -2
- package/package.json +1 -1
|
@@ -6,21 +6,10 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.validateI18nChunkSplitOptions = validateI18nChunkSplitOptions;
|
|
7
7
|
exports.validateI18nIndexingOptions = validateI18nIndexingOptions;
|
|
8
8
|
exports.validateI18nOptions = validateI18nOptions;
|
|
9
|
-
exports.validatePathSecurity = validatePathSecurity;
|
|
10
9
|
exports.validateRequiredFields = validateRequiredFields;
|
|
11
10
|
exports.validateTemplateFormat = validateTemplateFormat;
|
|
12
11
|
const LOCALE_PLACEHOLDER = '[locale]';
|
|
13
|
-
const REQUIRED_OPTIONS = ['jsResourcePath', 'propertiesFolderPath', 'numericMapPath'
|
|
14
|
-
|
|
15
|
-
function validatePathSecurity(path, fieldName) {
|
|
16
|
-
if (!path || typeof path !== 'string') {
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
if (path.includes('..')) {
|
|
21
|
-
throw new Error(`[I18nOptionsValidator] Invalid ${fieldName} contains '..': ${path}`);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
12
|
+
const REQUIRED_OPTIONS = ['jsResourcePath', 'propertiesFolderPath', 'numericMapPath'];
|
|
24
13
|
|
|
25
14
|
function validateRequiredFields(options, requiredFields) {
|
|
26
15
|
const missingOptions = requiredFields.filter(opt => {
|
|
@@ -41,8 +30,6 @@ function validateTemplateFormat(template, templateName) {
|
|
|
41
30
|
if (!template.includes(LOCALE_PLACEHOLDER)) {
|
|
42
31
|
throw new Error(`[I18nOptionsValidator] ${templateName} must include ${LOCALE_PLACEHOLDER} placeholder`);
|
|
43
32
|
}
|
|
44
|
-
|
|
45
|
-
validatePathSecurity(template, templateName);
|
|
46
33
|
}
|
|
47
34
|
|
|
48
35
|
function validateI18nIndexingOptions(i18nOpts) {
|
|
@@ -57,9 +44,6 @@ function validateI18nIndexingOptions(i18nOpts) {
|
|
|
57
44
|
}
|
|
58
45
|
|
|
59
46
|
validateRequiredFields(i18nOpts, REQUIRED_OPTIONS);
|
|
60
|
-
validatePathSecurity(i18nOpts.jsResourcePath, 'jsResourcePath');
|
|
61
|
-
validatePathSecurity(i18nOpts.propertiesFolderPath, 'propertiesFolderPath');
|
|
62
|
-
validatePathSecurity(i18nOpts.numericMapPath, 'numericMapPath');
|
|
63
47
|
const numericTemplate = i18nOpts.numericFilenameTemplate || '[locale]/numeric.i18n.js';
|
|
64
48
|
const dynamicTemplate = i18nOpts.dynamicFilenameTemplate || '[locale]/dynamic.i18n.js';
|
|
65
49
|
validateTemplateFormat(numericTemplate, 'numericFilenameTemplate');
|
|
@@ -69,11 +53,6 @@ function validateI18nIndexingOptions(i18nOpts) {
|
|
|
69
53
|
validateTemplateFormat(i18nOpts.singleFileTemplate, 'singleFileTemplate');
|
|
70
54
|
}
|
|
71
55
|
|
|
72
|
-
if (i18nOpts.outputFolder) {
|
|
73
|
-
validatePathSecurity(i18nOpts.outputFolder, 'outputFolder');
|
|
74
|
-
} // Validate conflicting options
|
|
75
|
-
|
|
76
|
-
|
|
77
56
|
const includeContentHash = i18nOpts.includeContentHash;
|
|
78
57
|
const injectHtml = i18nOpts.injectI18nUrlInIndex !== undefined ? i18nOpts.injectI18nUrlInIndex : true;
|
|
79
58
|
|
|
@@ -105,8 +84,6 @@ function validateI18nChunkSplitOptions(chunkSplitOpts) {
|
|
|
105
84
|
throw new Error('[I18nOptionsValidator] Missing required numericMapPath in i18nChunkSplit options');
|
|
106
85
|
}
|
|
107
86
|
|
|
108
|
-
validatePathSecurity(chunkSplitOpts.jsResource, 'jsResource');
|
|
109
|
-
validatePathSecurity(chunkSplitOpts.numericMapPath, 'numericMapPath');
|
|
110
87
|
return {
|
|
111
88
|
skipValidation: false
|
|
112
89
|
};
|
package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/I18nGroupRuntimeModule.js
CHANGED
|
@@ -10,8 +10,7 @@ var _webpack = require("webpack");
|
|
|
10
10
|
class I18nGroupRuntimeModule extends _webpack.RuntimeModule {
|
|
11
11
|
constructor(options) {
|
|
12
12
|
super('i18n-group-loader');
|
|
13
|
-
this.options = { ...options
|
|
14
|
-
i18nPublicPathVar: options.i18nPublicPathVar || '(typeof window !== "undefined" && window.__I18N_CDN__)'
|
|
13
|
+
this.options = { ...options
|
|
15
14
|
};
|
|
16
15
|
}
|
|
17
16
|
|
|
@@ -20,23 +19,15 @@ class I18nGroupRuntimeModule extends _webpack.RuntimeModule {
|
|
|
20
19
|
customGroups,
|
|
21
20
|
chunkIdToGroupMapping,
|
|
22
21
|
localeVarName,
|
|
23
|
-
|
|
24
|
-
groupAssetUrls,
|
|
25
|
-
includeContentHash,
|
|
26
|
-
mode
|
|
22
|
+
groupAssetUrls
|
|
27
23
|
} = this.options;
|
|
28
24
|
const chunkIdToGroup = chunkIdToGroupMapping || {};
|
|
29
|
-
const hasGroupAssetOverrides = !!groupAssetUrls && Object.values(groupAssetUrls).some(localeMap => localeMap && Object.keys(localeMap).length > 0);
|
|
30
|
-
const hashAware = includeContentHash || hasGroupAssetOverrides;
|
|
31
|
-
const isDevelopment = mode === 'development';
|
|
32
25
|
return `
|
|
33
26
|
// I18n Group Loading Runtime
|
|
34
27
|
(function() {
|
|
35
28
|
var loadedGroups = {};
|
|
36
29
|
var chunkIdToGroup = ${JSON.stringify(chunkIdToGroup)};
|
|
37
30
|
var groupAssetUrls = ${JSON.stringify(groupAssetUrls || {})};
|
|
38
|
-
var hashAware = ${hashAware ? 'true' : 'false'};
|
|
39
|
-
var isDev = ${isDevelopment ? 'true' : 'false'};
|
|
40
31
|
var cachedI18nBase;
|
|
41
32
|
var scriptCache = null;
|
|
42
33
|
|
|
@@ -45,46 +36,12 @@ class I18nGroupRuntimeModule extends _webpack.RuntimeModule {
|
|
|
45
36
|
return path.charAt(path.length - 1) === '/' ? path : path + '/';
|
|
46
37
|
}
|
|
47
38
|
|
|
48
|
-
function resolveConfiguredBase() {
|
|
49
|
-
try {
|
|
50
|
-
var candidate = (${i18nPublicPathVar});
|
|
51
|
-
if (typeof candidate === 'string' && candidate.length) {
|
|
52
|
-
return candidate;
|
|
53
|
-
}
|
|
54
|
-
} catch (err) {
|
|
55
|
-
// Fall back to DOM/publicPath detection
|
|
56
|
-
}
|
|
57
|
-
return '';
|
|
58
|
-
}
|
|
59
|
-
|
|
60
39
|
function resolveI18nBase() {
|
|
61
40
|
if (cachedI18nBase !== undefined) {
|
|
62
41
|
return cachedI18nBase;
|
|
63
42
|
}
|
|
64
43
|
|
|
65
|
-
var base =
|
|
66
|
-
|
|
67
|
-
if (!base && typeof document !== 'undefined') {
|
|
68
|
-
if (!scriptCache) {
|
|
69
|
-
scriptCache = Array.from(document.getElementsByTagName('script'));
|
|
70
|
-
}
|
|
71
|
-
for (var i = 0; i < scriptCache.length; i++) {
|
|
72
|
-
var src = scriptCache[i].getAttribute('data-src') || scriptCache[i].getAttribute('src') || '';
|
|
73
|
-
var markerIndex = src.indexOf('i18n-chunk/');
|
|
74
|
-
if (markerIndex !== -1) {
|
|
75
|
-
base = src.slice(0, markerIndex);
|
|
76
|
-
break;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (!base) {
|
|
82
|
-
base = __webpack_require__.p || '';
|
|
83
|
-
if (base && base.indexOf('/smap/') !== -1 && base.slice(-6) === '/smap/') {
|
|
84
|
-
base = base.slice(0, -6) + '/';
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
44
|
+
var base = __webpack_require__.p || '';
|
|
88
45
|
cachedI18nBase = ensureTrailingSlash(base);
|
|
89
46
|
return cachedI18nBase;
|
|
90
47
|
}
|
|
@@ -111,19 +68,14 @@ class I18nGroupRuntimeModule extends _webpack.RuntimeModule {
|
|
|
111
68
|
return Promise.resolve();
|
|
112
69
|
}
|
|
113
70
|
|
|
114
|
-
var shouldSkip =
|
|
115
|
-
(loadReason === 'prefetch' && config.prefetch === false) ||
|
|
116
|
-
(loadReason === 'preload' && config.preload === false);
|
|
117
|
-
|
|
118
|
-
if (shouldSkip) {
|
|
119
|
-
return Promise.resolve();
|
|
120
|
-
}
|
|
121
71
|
|
|
122
72
|
return new Promise(function(resolve, reject) {
|
|
123
|
-
var relativePath
|
|
73
|
+
var relativePath;
|
|
124
74
|
|
|
125
|
-
if (
|
|
75
|
+
if (groupAssetUrls[groupName] && groupAssetUrls[groupName][locale]) {
|
|
126
76
|
relativePath = groupAssetUrls[groupName][locale];
|
|
77
|
+
} else {
|
|
78
|
+
relativePath = config.filenameTemplate.replace('[locale]', locale);
|
|
127
79
|
}
|
|
128
80
|
|
|
129
81
|
var i18nUrl = buildI18nUrl(relativePath);
|
|
@@ -140,17 +92,11 @@ class I18nGroupRuntimeModule extends _webpack.RuntimeModule {
|
|
|
140
92
|
|
|
141
93
|
script.onload = function() {
|
|
142
94
|
loadedGroups[groupName] = true;
|
|
143
|
-
if (isDev) {
|
|
144
|
-
console.log('[i18n-group:loaded]', groupName, i18nUrl);
|
|
145
|
-
}
|
|
146
95
|
cleanup();
|
|
147
96
|
resolve();
|
|
148
97
|
};
|
|
149
98
|
|
|
150
99
|
script.onerror = function() {
|
|
151
|
-
if (isDev) {
|
|
152
|
-
console.error('[i18n-group:error]', groupName, i18nUrl);
|
|
153
|
-
}
|
|
154
100
|
cleanup();
|
|
155
101
|
loadedGroups[groupName] = true;
|
|
156
102
|
resolve();
|
|
@@ -178,9 +124,6 @@ class I18nGroupRuntimeModule extends _webpack.RuntimeModule {
|
|
|
178
124
|
return i18nPromise.then(function() {
|
|
179
125
|
return originalEnsureChunk.apply(self, args);
|
|
180
126
|
}).catch(function(err) {
|
|
181
|
-
if (isDev) {
|
|
182
|
-
console.error('[i18n-group:critical] i18n failed, proceeding without:', err);
|
|
183
|
-
}
|
|
184
127
|
return originalEnsureChunk.apply(self, args);
|
|
185
128
|
});
|
|
186
129
|
} else {
|
|
@@ -215,73 +158,17 @@ class I18nGroupRuntimeModule extends _webpack.RuntimeModule {
|
|
|
215
158
|
try {
|
|
216
159
|
var detectedGroup = detectGroupFromUrl();
|
|
217
160
|
if (detectedGroup && !loadedGroups[detectedGroup]) {
|
|
218
|
-
if (isDev) {
|
|
219
|
-
console.log('[i18n-group:url-detect]', detectedGroup, 'loading for current URL');
|
|
220
|
-
}
|
|
221
161
|
loadI18nGroup(detectedGroup, 'url-detection').catch(function(err) {
|
|
222
|
-
|
|
223
|
-
console.error('[i18n-group:url-detect] Failed to load', detectedGroup, ':', err);
|
|
224
|
-
}
|
|
162
|
+
// Silent error handling
|
|
225
163
|
});
|
|
226
164
|
}
|
|
227
165
|
} catch (err) {
|
|
228
|
-
|
|
229
|
-
console.error('[i18n-group:url-detect] Error in URL detection:', err);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// Initialize preload and prefetch mechanisms
|
|
235
|
-
function initializeGroupLoading() {
|
|
236
|
-
if (typeof window === 'undefined') return;
|
|
237
|
-
|
|
238
|
-
var groupConfig = ${JSON.stringify(customGroups)};
|
|
239
|
-
|
|
240
|
-
// 1. Immediate preload for critical groups
|
|
241
|
-
for (var groupName in groupConfig) {
|
|
242
|
-
var config = groupConfig[groupName];
|
|
243
|
-
if (config.preload === true && !loadedGroups[groupName]) {
|
|
244
|
-
if (isDev) {
|
|
245
|
-
console.log('[i18n-group:preload]', groupName, 'loading immediately');
|
|
246
|
-
}
|
|
247
|
-
loadI18nGroup(groupName, 'preload').catch(function(err) {
|
|
248
|
-
if (isDev) {
|
|
249
|
-
console.error('[i18n-group:preload] Failed to preload', groupName, ':', err);
|
|
250
|
-
}
|
|
251
|
-
});
|
|
252
|
-
}
|
|
166
|
+
// Silent error handling
|
|
253
167
|
}
|
|
254
|
-
|
|
255
|
-
// 2. Schedule prefetch for background loading
|
|
256
|
-
function schedulePrefetch() {
|
|
257
|
-
for (var groupName in groupConfig) {
|
|
258
|
-
var config = groupConfig[groupName];
|
|
259
|
-
if (config.prefetch === true && !loadedGroups[groupName] && config.preload !== true) {
|
|
260
|
-
if (isDev) {
|
|
261
|
-
console.log('[i18n-group:prefetch]', groupName, 'scheduling background load');
|
|
262
|
-
}
|
|
263
|
-
loadI18nGroup(groupName, 'prefetch').catch(function(err) {
|
|
264
|
-
if (isDev) {
|
|
265
|
-
console.error('[i18n-group:prefetch] Failed to prefetch', groupName, ':', err);
|
|
266
|
-
}
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// Use requestIdleCallback for prefetch if available, otherwise setTimeout
|
|
273
|
-
if (window.requestIdleCallback) {
|
|
274
|
-
window.requestIdleCallback(schedulePrefetch, { timeout: 5000 });
|
|
275
|
-
} else {
|
|
276
|
-
setTimeout(schedulePrefetch, 100);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// 3. Check current URL for immediate loading
|
|
280
|
-
checkAndLoadGroupFromUrl();
|
|
281
168
|
}
|
|
282
169
|
|
|
283
|
-
//
|
|
284
|
-
|
|
170
|
+
// Check current URL for immediate loading
|
|
171
|
+
checkAndLoadGroupFromUrl();
|
|
285
172
|
})();
|
|
286
173
|
`;
|
|
287
174
|
}
|
package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/I18nNumericIndexPlugin.js
CHANGED
|
@@ -66,20 +66,16 @@ class I18nNumericIndexPlugin {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
apply(compiler) {
|
|
69
|
-
// Build-time validation for critical file paths
|
|
70
|
-
this.validateCriticalFilePaths(compiler); // Detect webpackI18nGroup comments in code
|
|
71
|
-
|
|
72
69
|
this.detectI18nGroupComments(compiler);
|
|
73
70
|
compiler.hooks.thisCompilation.tap(pluginName, compilation => {
|
|
74
71
|
this.groupAssetUrls = {};
|
|
75
72
|
this.detectedGroups = {};
|
|
76
73
|
this.preparedAssets = [];
|
|
77
74
|
this.assetsPrepared = false;
|
|
78
|
-
this.assetsEmitted = false;
|
|
75
|
+
this.assetsEmitted = false;
|
|
79
76
|
|
|
80
77
|
if (this.options.customGroups) {
|
|
81
78
|
compilation.hooks.additionalTreeRuntimeRequirements.tap(pluginName, (chunk, set) => {
|
|
82
|
-
// Only add to the main/entry chunk to avoid duplication
|
|
83
79
|
if (chunk.name === 'main' || chunk.hasRuntime()) {
|
|
84
80
|
this.ensureAssetsPrepared(compilation);
|
|
85
81
|
const chunkMapping = this.getChunkIdToGroupMapping(compilation);
|
|
@@ -88,7 +84,6 @@ class I18nNumericIndexPlugin {
|
|
|
88
84
|
chunkIdToGroupMapping: chunkMapping,
|
|
89
85
|
localeVarName: this.options.localeVarName,
|
|
90
86
|
jsonpFunc: this.options.jsonpFunc,
|
|
91
|
-
i18nPublicPathVar: this.options.i18nPublicPathVar,
|
|
92
87
|
groupAssetUrls: this.groupAssetUrls,
|
|
93
88
|
includeContentHash: this.options.includeContentHash
|
|
94
89
|
}));
|
|
@@ -106,66 +101,6 @@ class I18nNumericIndexPlugin {
|
|
|
106
101
|
});
|
|
107
102
|
}
|
|
108
103
|
|
|
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
|
-
|
|
169
104
|
detectI18nGroupComments(compiler) {
|
|
170
105
|
compiler.hooks.normalModuleFactory.tap(pluginName, factory => {
|
|
171
106
|
factory.hooks.parser.for('javascript/auto').tap(pluginName, parser => {
|
|
@@ -203,11 +138,10 @@ class I18nNumericIndexPlugin {
|
|
|
203
138
|
const chunkIdToGroup = {};
|
|
204
139
|
const configuredMappings = { ...this.groupChunkMapping,
|
|
205
140
|
...(this.options.chunkToGroupMapping || {})
|
|
206
|
-
};
|
|
141
|
+
};
|
|
207
142
|
|
|
208
143
|
for (const chunk of compilation.chunks) {
|
|
209
|
-
const chunkName = chunk.name;
|
|
210
|
-
|
|
144
|
+
const chunkName = chunk.name;
|
|
211
145
|
let groupName = null;
|
|
212
146
|
|
|
213
147
|
if (this.detectedGroups && this.detectedGroups[chunkName]) {
|
|
@@ -239,12 +173,12 @@ class I18nNumericIndexPlugin {
|
|
|
239
173
|
|
|
240
174
|
if (!jsResourcePath || !propertiesFolderPath) {
|
|
241
175
|
return;
|
|
242
|
-
} // Reset
|
|
176
|
+
} // Reset caches for incremental builds
|
|
243
177
|
|
|
244
178
|
|
|
245
179
|
this.numericMap = {};
|
|
246
180
|
this.manifest = {};
|
|
247
|
-
this.customGroups = {};
|
|
181
|
+
this.customGroups = {};
|
|
248
182
|
|
|
249
183
|
if (numericMapPath) {
|
|
250
184
|
const mapData = (0, _i18nDataLoader.loadNumericMap)(numericMapPath, compilation);
|
|
@@ -256,28 +190,24 @@ class I18nNumericIndexPlugin {
|
|
|
256
190
|
}
|
|
257
191
|
});
|
|
258
192
|
}
|
|
259
|
-
}
|
|
260
|
-
|
|
193
|
+
}
|
|
261
194
|
|
|
262
195
|
let jsResourceKeys = (0, _propertiesUtils.getPropertiesAsJSON)(jsResourcePath);
|
|
263
|
-
jsResourceKeys = this.normalizeObjectValues(jsResourceKeys);
|
|
196
|
+
jsResourceKeys = this.normalizeObjectValues(jsResourceKeys);
|
|
264
197
|
|
|
265
198
|
if (customGroups) {
|
|
266
199
|
this.parseCustomGroups(jsResourcePath, customGroups);
|
|
267
|
-
}
|
|
268
|
-
|
|
200
|
+
}
|
|
269
201
|
|
|
270
202
|
const allI18nObject = (0, _propertiesUtils.getAllI18n)({
|
|
271
203
|
folderPath: propertiesFolderPath,
|
|
272
204
|
disableDefault: false,
|
|
273
205
|
jsResourceI18nKeys: jsResourceKeys
|
|
274
|
-
});
|
|
275
|
-
|
|
206
|
+
});
|
|
276
207
|
Object.keys(allI18nObject).forEach(loc => {
|
|
277
208
|
allI18nObject[loc] = this.normalizeObjectValues(allI18nObject[loc]);
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
allI18nObject['en_US'] = jsResourceKeys; // If requested, restrict all locales to base (English) keys only
|
|
209
|
+
});
|
|
210
|
+
allI18nObject['en_US'] = jsResourceKeys;
|
|
281
211
|
|
|
282
212
|
if (this.options.restrictToBaseKeys) {
|
|
283
213
|
const baseKeys = Object.keys(jsResourceKeys);
|
|
@@ -293,14 +223,12 @@ class I18nNumericIndexPlugin {
|
|
|
293
223
|
});
|
|
294
224
|
}
|
|
295
225
|
|
|
296
|
-
const locales = Object.keys(allI18nObject);
|
|
297
|
-
|
|
226
|
+
const locales = Object.keys(allI18nObject);
|
|
298
227
|
locales.forEach(locale => {
|
|
299
228
|
const localeData = allI18nObject[locale];
|
|
300
229
|
const numericData = {};
|
|
301
230
|
const dynamicData = {};
|
|
302
|
-
const groupData = {};
|
|
303
|
-
|
|
231
|
+
const groupData = {};
|
|
304
232
|
Object.keys(customGroups || {}).forEach(groupName => {
|
|
305
233
|
groupData[groupName] = {};
|
|
306
234
|
}); // Process each key
|
|
@@ -448,12 +376,11 @@ class I18nNumericIndexPlugin {
|
|
|
448
376
|
const {
|
|
449
377
|
outputFolder
|
|
450
378
|
} = this.options;
|
|
451
|
-
const filePath = template.replace(/\[locale\]/g, locale);
|
|
379
|
+
const filePath = template.replace(/\[locale\]/g, locale);
|
|
452
380
|
|
|
453
381
|
if (filePath.includes(outputFolder) || filePath.startsWith('/')) {
|
|
454
382
|
return filePath;
|
|
455
|
-
}
|
|
456
|
-
|
|
383
|
+
}
|
|
457
384
|
|
|
458
385
|
return _path.default.join(outputFolder, filePath).replace(/\\/g, '/');
|
|
459
386
|
}
|
|
@@ -59,8 +59,7 @@ function getPropertiesAsJSON(filePath) {
|
|
|
59
59
|
} catch (err) {
|
|
60
60
|
return {};
|
|
61
61
|
}
|
|
62
|
-
}
|
|
63
|
-
|
|
62
|
+
}
|
|
64
63
|
|
|
65
64
|
function getLang(file) {
|
|
66
65
|
const underScoreIndex = file.indexOf('_');
|
|
@@ -132,16 +131,4 @@ function jsonToString(json, keySeperator) {
|
|
|
132
131
|
});
|
|
133
132
|
str += '}';
|
|
134
133
|
return str;
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* not useable due to special charector
|
|
138
|
-
*
|
|
139
|
-
return `{${Object.keys(data).map(key => `"${key}": "${data[key]}"`).join(",")}}`
|
|
140
|
-
// return JSON.stringify(data);
|
|
141
|
-
let str = "{";
|
|
142
|
-
for (const key of Object.keys(data)) {
|
|
143
|
-
str+= `"${key}": "${data[key]}",`
|
|
144
|
-
}
|
|
145
|
-
str+="}";
|
|
146
|
-
return str;
|
|
147
|
-
*/
|
|
134
|
+
}
|
|
@@ -12,11 +12,9 @@ const {
|
|
|
12
12
|
} = require('./loaderConfigs/i18nIdReplaceLoaderConfig');
|
|
13
13
|
|
|
14
14
|
function jsLoaders(options) {
|
|
15
|
-
const useLoaders = [];
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const shouldUseNumericIndexing = options.i18nIndexing && options.i18nIndexing.enable || options.i18nChunkSplit && options.i18nChunkSplit.chunkSplitEnable && options.i18nChunkSplit.useNumericIndexing;
|
|
15
|
+
const useLoaders = [];
|
|
16
|
+
useLoaders.push((0, _babelLoaderConfig.babelLoaderConfig)(options));
|
|
17
|
+
const shouldUseNumericIndexing = options.i18nIndexing && options.i18nIndexing.enable;
|
|
20
18
|
|
|
21
19
|
if (shouldUseNumericIndexing) {
|
|
22
20
|
try {
|
|
@@ -26,7 +24,6 @@ function jsLoaders(options) {
|
|
|
26
24
|
useLoaders.push(loaderConfig);
|
|
27
25
|
}
|
|
28
26
|
} catch (err) {
|
|
29
|
-
// Silently skip if configuration fails
|
|
30
27
|
console.warn('[jsLoaders] Failed to configure i18n ID replace loader:', err.message);
|
|
31
28
|
}
|
|
32
29
|
}
|
|
@@ -14,13 +14,7 @@ var _fs = _interopRequireDefault(require("fs"));
|
|
|
14
14
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
15
15
|
|
|
16
16
|
function loadJSResourcesOnce(options) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (options.i18nIndexing && options.i18nIndexing.enable) {
|
|
20
|
-
jsResourcePath = options.i18nIndexing.jsResourcePath;
|
|
21
|
-
} else if (options.i18nChunkSplit && options.i18nChunkSplit.chunkSplitEnable && options.i18nChunkSplit.useNumericIndexing) {
|
|
22
|
-
jsResourcePath = options.i18nChunkSplit.jsResource;
|
|
23
|
-
}
|
|
17
|
+
const jsResourcePath = options.i18nIndexing.jsResourcePath;
|
|
24
18
|
|
|
25
19
|
try {
|
|
26
20
|
const i18nData = (0, _propertiesUtils.getPropertiesAsJSON)(jsResourcePath);
|
|
@@ -45,16 +39,11 @@ function readNumericMapOnce(numericMapPath) {
|
|
|
45
39
|
|
|
46
40
|
function i18nIdReplaceLoaderConfig(options) {
|
|
47
41
|
(0, _i18nOptionsValidator.validateI18nOptions)(options);
|
|
48
|
-
|
|
42
|
+
const numericMapPath = options.i18nIndexing.numericMapPath;
|
|
43
|
+
const allI18nData = loadJSResourcesOnce(options);
|
|
49
44
|
|
|
50
|
-
|
|
51
|
-
numericMapPath = options.i18nIndexing.numericMapPath;
|
|
52
|
-
} else if (options.i18nChunkSplit && options.i18nChunkSplit.chunkSplitEnable && options.i18nChunkSplit.useNumericIndexing) {
|
|
53
|
-
numericMapPath = options.i18nChunkSplit.numericMapPath;
|
|
54
|
-
}
|
|
45
|
+
const i18nKeyReplaceLoaderPath = require.resolve('../loaders/i18nIdReplaceLoader.js');
|
|
55
46
|
|
|
56
|
-
const allI18nData = loadJSResourcesOnce(options);
|
|
57
|
-
const i18nKeyReplaceLoaderPath = new URL('../loaders/i18nIdReplaceLoader.js', import.meta.url).pathname;
|
|
58
47
|
let numericIdMap;
|
|
59
48
|
|
|
60
49
|
try {
|
|
@@ -70,7 +59,8 @@ function i18nIdReplaceLoaderConfig(options) {
|
|
|
70
59
|
numericIdMap: numericIdMap || undefined,
|
|
71
60
|
devMode: options.i18nIndexing?.devMode || false,
|
|
72
61
|
includePaths: options.i18nIndexing?.loaderOptions?.includePaths || [],
|
|
73
|
-
excludePaths: options.i18nIndexing?.loaderOptions?.excludePaths || ['node_modules', 'tests']
|
|
62
|
+
excludePaths: options.i18nIndexing?.loaderOptions?.excludePaths || ['node_modules', 'tests'],
|
|
63
|
+
i18nIndexingEnabled: true
|
|
74
64
|
};
|
|
75
65
|
return {
|
|
76
66
|
loader: i18nKeyReplaceLoaderPath,
|
|
@@ -20,7 +20,11 @@ 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(); //
|
|
23
|
+
const callback = this.async(); // Early exit if i18n indexing is not enabled
|
|
24
|
+
|
|
25
|
+
if (!options.i18nIndexingEnabled) {
|
|
26
|
+
return callback(null, source, map);
|
|
27
|
+
}
|
|
24
28
|
|
|
25
29
|
if (options.excludePaths && Array.isArray(options.excludePaths)) {
|
|
26
30
|
const shouldExclude = options.excludePaths.some(excludePath => typeof excludePath === 'string' && resourcePath.includes(excludePath));
|
|
@@ -28,8 +32,7 @@ module.exports = function i18nIdReplaceLoader(source, map) {
|
|
|
28
32
|
if (shouldExclude) {
|
|
29
33
|
return callback(null, source, map);
|
|
30
34
|
}
|
|
31
|
-
}
|
|
32
|
-
|
|
35
|
+
}
|
|
33
36
|
|
|
34
37
|
if (options.includePaths && Array.isArray(options.includePaths) && options.includePaths.length > 0) {
|
|
35
38
|
const shouldInclude = options.includePaths.some(includePath => typeof includePath === 'string' && resourcePath.includes(includePath));
|
|
@@ -37,13 +40,11 @@ module.exports = function i18nIdReplaceLoader(source, map) {
|
|
|
37
40
|
if (!shouldInclude) {
|
|
38
41
|
return callback(null, source, map);
|
|
39
42
|
}
|
|
40
|
-
}
|
|
41
|
-
|
|
43
|
+
}
|
|
42
44
|
|
|
43
45
|
if (!options.allI18nData || Object.keys(options.allI18nData).length === 0) {
|
|
44
46
|
return callback(new Error(`i18nIdReplaceLoader: 'allI18nData' option is missing or empty`));
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
+
}
|
|
47
48
|
|
|
48
49
|
let numericIdMap = options.numericIdMap;
|
|
49
50
|
|
|
@@ -60,28 +61,43 @@ module.exports = function i18nIdReplaceLoader(source, map) {
|
|
|
60
61
|
} catch (e) {
|
|
61
62
|
return callback(new Error(`i18nIdReplaceLoader: Failed to load numeric map from ${options.numericMapPath}: ${e.message}`));
|
|
62
63
|
}
|
|
63
|
-
}
|
|
64
|
-
|
|
64
|
+
}
|
|
65
65
|
|
|
66
66
|
if (!numericIdMap) {
|
|
67
67
|
return callback(null, source, map);
|
|
68
|
+
} // Pre-filter: Skip files without valid i18n keys from numericIdMap
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
function hasValidI18nKeys(source, numericIdMap) {
|
|
72
|
+
const keyPattern = /['"`]([\w-_]+(?:\.[\w-_]+){2,})['"`]/g;
|
|
73
|
+
let match;
|
|
74
|
+
|
|
75
|
+
while ((match = keyPattern.exec(source)) !== null) {
|
|
76
|
+
if (numericIdMap.hasOwnProperty(match[1])) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!hasValidI18nKeys(source, numericIdMap)) {
|
|
85
|
+
return callback(null, source, map);
|
|
68
86
|
}
|
|
69
87
|
|
|
70
88
|
try {
|
|
71
|
-
// Parse the JavaScript/TypeScript source code
|
|
72
89
|
const ast = parser.parse(source, {
|
|
73
90
|
sourceType: 'module',
|
|
74
|
-
plugins: ['jsx', 'typescript', '
|
|
91
|
+
plugins: ['jsx', 'typescript', 'nullishCoalescingOperator'],
|
|
75
92
|
sourceFilename: resourcePath
|
|
76
93
|
});
|
|
77
94
|
let hasTransformations = false;
|
|
78
|
-
let transformationCount = 0;
|
|
79
|
-
|
|
95
|
+
let transformationCount = 0;
|
|
80
96
|
traverse(ast, {
|
|
81
97
|
StringLiteral(path) {
|
|
82
98
|
const {
|
|
83
99
|
node
|
|
84
|
-
} = path;
|
|
100
|
+
} = path;
|
|
85
101
|
|
|
86
102
|
if (options.allI18nData.hasOwnProperty(node.value) && numericIdMap.hasOwnProperty(node.value)) {
|
|
87
103
|
const numericId = String(numericIdMap[node.value]);
|
|
@@ -91,7 +107,7 @@ module.exports = function i18nIdReplaceLoader(source, map) {
|
|
|
91
107
|
}
|
|
92
108
|
}
|
|
93
109
|
|
|
94
|
-
});
|
|
110
|
+
});
|
|
95
111
|
|
|
96
112
|
if (hasTransformations) {
|
|
97
113
|
const output = generator(ast, {
|
|
@@ -18,7 +18,6 @@ class I18nNumericHtmlInjectorPlugin {
|
|
|
18
18
|
|
|
19
19
|
apply(compiler) {
|
|
20
20
|
compiler.hooks.thisCompilation.tap(pluginName, compilation => {
|
|
21
|
-
// Hook into HtmlWebpackPlugin to inject i18n script tags
|
|
22
21
|
_htmlWebpackPlugin.default.getHooks(compilation).beforeAssetTagGeneration.tap(pluginName, hookData => {
|
|
23
22
|
const {
|
|
24
23
|
assets
|
|
@@ -27,36 +26,28 @@ class I18nNumericHtmlInjectorPlugin {
|
|
|
27
26
|
numericFilenameTemplate,
|
|
28
27
|
dynamicFilenameTemplate,
|
|
29
28
|
htmlTemplateLabel,
|
|
30
|
-
i18nCdnTemplate,
|
|
31
29
|
customGroups
|
|
32
30
|
} = this.options;
|
|
33
|
-
const newI18nAssets = [];
|
|
31
|
+
const newI18nAssets = [];
|
|
34
32
|
|
|
35
33
|
if (numericFilenameTemplate) {
|
|
36
|
-
const numericFilename = numericFilenameTemplate.replace(/\[locale\]/g, htmlTemplateLabel).replace(/%5Blocale%5D/g, htmlTemplateLabel);
|
|
37
|
-
|
|
34
|
+
const numericFilename = numericFilenameTemplate.replace(/\[locale\]/g, htmlTemplateLabel).replace(/%5Blocale%5D/g, htmlTemplateLabel);
|
|
38
35
|
newI18nAssets.push(numericFilename);
|
|
39
|
-
}
|
|
40
|
-
|
|
36
|
+
}
|
|
41
37
|
|
|
42
38
|
if (dynamicFilenameTemplate) {
|
|
43
|
-
const dynamicFilename = dynamicFilenameTemplate.replace(/\[locale\]/g, htmlTemplateLabel).replace(/%5Blocale%5D/g, htmlTemplateLabel);
|
|
44
|
-
|
|
39
|
+
const dynamicFilename = dynamicFilenameTemplate.replace(/\[locale\]/g, htmlTemplateLabel).replace(/%5Blocale%5D/g, htmlTemplateLabel);
|
|
45
40
|
newI18nAssets.push(dynamicFilename);
|
|
46
|
-
}
|
|
47
|
-
|
|
41
|
+
}
|
|
48
42
|
|
|
49
43
|
if (customGroups) {
|
|
50
44
|
Object.entries(customGroups).forEach(([groupName, groupConfig]) => {
|
|
51
|
-
// Only add to initial HTML if preload is true
|
|
52
45
|
if (groupConfig.preload && groupConfig.filenameTemplate) {
|
|
53
|
-
const groupFilename = groupConfig.filenameTemplate.replace(/\[locale\]/g, htmlTemplateLabel).replace(/%5Blocale%5D/g, htmlTemplateLabel);
|
|
54
|
-
|
|
46
|
+
const groupFilename = groupConfig.filenameTemplate.replace(/\[locale\]/g, htmlTemplateLabel).replace(/%5Blocale%5D/g, htmlTemplateLabel);
|
|
55
47
|
newI18nAssets.push(groupFilename);
|
|
56
48
|
}
|
|
57
49
|
});
|
|
58
|
-
}
|
|
59
|
-
|
|
50
|
+
}
|
|
60
51
|
|
|
61
52
|
if (newI18nAssets.length > 0) {
|
|
62
53
|
assets.js = [...newI18nAssets, ...assets.js];
|
|
@@ -73,12 +64,11 @@ function configI18nNumericHtmlInjector(config) {
|
|
|
73
64
|
const {
|
|
74
65
|
i18nIndexing,
|
|
75
66
|
cdnMapping
|
|
76
|
-
} = config;
|
|
67
|
+
} = config;
|
|
77
68
|
|
|
78
69
|
if (!i18nIndexing || !i18nIndexing.enable) {
|
|
79
70
|
return null;
|
|
80
|
-
}
|
|
81
|
-
|
|
71
|
+
}
|
|
82
72
|
|
|
83
73
|
const i18nCdnTemplate = cdnMapping && cdnMapping.isCdnEnabled ? cdnMapping.i18nTemplate || cdnMapping.jsTemplate : '';
|
|
84
74
|
const options = {
|
|
@@ -80,8 +80,7 @@ function configI18nNumericIndexPlugin(options) {
|
|
|
80
80
|
manifestPath: i18nOpts.manifestPath || null,
|
|
81
81
|
customGroups: i18nOpts.customGroups || null,
|
|
82
82
|
chunkToGroupMapping: i18nOpts.chunkToGroupMapping || {},
|
|
83
|
-
emitFiles
|
|
84
|
-
i18nPublicPathVar: i18nOpts.i18nPublicPathVar
|
|
83
|
+
emitFiles
|
|
85
84
|
};
|
|
86
85
|
const htmlInjectorOptions = { ...sharedOptions,
|
|
87
86
|
htmlTemplateLabel: i18nOpts.htmlTemplateLabel,
|