@zohodesk/client_build_tool 0.0.11-exp.29.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 -26
- package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/I18nGroupRuntimeModule.js +11 -124
- package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/I18nNumericIndexPlugin.js +15 -82
- 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 +4 -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,23 +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
|
-
} // Allow relative paths within project structure (../assets, etc.)
|
|
19
|
-
// Block dangerous patterns like ../../etc/passwd
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (path.includes('../..') || path.startsWith('/') && path.includes('..')) {
|
|
23
|
-
throw new Error(`[I18nOptionsValidator] Invalid ${fieldName} contains suspicious path: ${path}`);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
12
|
+
const REQUIRED_OPTIONS = ['jsResourcePath', 'propertiesFolderPath', 'numericMapPath'];
|
|
26
13
|
|
|
27
14
|
function validateRequiredFields(options, requiredFields) {
|
|
28
15
|
const missingOptions = requiredFields.filter(opt => {
|
|
@@ -43,8 +30,6 @@ function validateTemplateFormat(template, templateName) {
|
|
|
43
30
|
if (!template.includes(LOCALE_PLACEHOLDER)) {
|
|
44
31
|
throw new Error(`[I18nOptionsValidator] ${templateName} must include ${LOCALE_PLACEHOLDER} placeholder`);
|
|
45
32
|
}
|
|
46
|
-
|
|
47
|
-
validatePathSecurity(template, templateName);
|
|
48
33
|
}
|
|
49
34
|
|
|
50
35
|
function validateI18nIndexingOptions(i18nOpts) {
|
|
@@ -59,9 +44,6 @@ function validateI18nIndexingOptions(i18nOpts) {
|
|
|
59
44
|
}
|
|
60
45
|
|
|
61
46
|
validateRequiredFields(i18nOpts, REQUIRED_OPTIONS);
|
|
62
|
-
validatePathSecurity(i18nOpts.jsResourcePath, 'jsResourcePath');
|
|
63
|
-
validatePathSecurity(i18nOpts.propertiesFolderPath, 'propertiesFolderPath');
|
|
64
|
-
validatePathSecurity(i18nOpts.numericMapPath, 'numericMapPath');
|
|
65
47
|
const numericTemplate = i18nOpts.numericFilenameTemplate || '[locale]/numeric.i18n.js';
|
|
66
48
|
const dynamicTemplate = i18nOpts.dynamicFilenameTemplate || '[locale]/dynamic.i18n.js';
|
|
67
49
|
validateTemplateFormat(numericTemplate, 'numericFilenameTemplate');
|
|
@@ -71,11 +53,6 @@ function validateI18nIndexingOptions(i18nOpts) {
|
|
|
71
53
|
validateTemplateFormat(i18nOpts.singleFileTemplate, 'singleFileTemplate');
|
|
72
54
|
}
|
|
73
55
|
|
|
74
|
-
if (i18nOpts.outputFolder) {
|
|
75
|
-
validatePathSecurity(i18nOpts.outputFolder, 'outputFolder');
|
|
76
|
-
} // Validate conflicting options
|
|
77
|
-
|
|
78
|
-
|
|
79
56
|
const includeContentHash = i18nOpts.includeContentHash;
|
|
80
57
|
const injectHtml = i18nOpts.injectI18nUrlInIndex !== undefined ? i18nOpts.injectI18nUrlInIndex : true;
|
|
81
58
|
|
|
@@ -107,8 +84,6 @@ function validateI18nChunkSplitOptions(chunkSplitOpts) {
|
|
|
107
84
|
throw new Error('[I18nOptionsValidator] Missing required numericMapPath in i18nChunkSplit options');
|
|
108
85
|
}
|
|
109
86
|
|
|
110
|
-
validatePathSecurity(chunkSplitOpts.jsResource, 'jsResource');
|
|
111
|
-
validatePathSecurity(chunkSplitOpts.numericMapPath, 'numericMapPath');
|
|
112
87
|
return {
|
|
113
88
|
skipValidation: false
|
|
114
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,60 +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
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
104
|
detectI18nGroupComments(compiler) {
|
|
164
105
|
compiler.hooks.normalModuleFactory.tap(pluginName, factory => {
|
|
165
106
|
factory.hooks.parser.for('javascript/auto').tap(pluginName, parser => {
|
|
@@ -197,11 +138,10 @@ class I18nNumericIndexPlugin {
|
|
|
197
138
|
const chunkIdToGroup = {};
|
|
198
139
|
const configuredMappings = { ...this.groupChunkMapping,
|
|
199
140
|
...(this.options.chunkToGroupMapping || {})
|
|
200
|
-
};
|
|
141
|
+
};
|
|
201
142
|
|
|
202
143
|
for (const chunk of compilation.chunks) {
|
|
203
|
-
const chunkName = chunk.name;
|
|
204
|
-
|
|
144
|
+
const chunkName = chunk.name;
|
|
205
145
|
let groupName = null;
|
|
206
146
|
|
|
207
147
|
if (this.detectedGroups && this.detectedGroups[chunkName]) {
|
|
@@ -233,12 +173,12 @@ class I18nNumericIndexPlugin {
|
|
|
233
173
|
|
|
234
174
|
if (!jsResourcePath || !propertiesFolderPath) {
|
|
235
175
|
return;
|
|
236
|
-
} // Reset
|
|
176
|
+
} // Reset caches for incremental builds
|
|
237
177
|
|
|
238
178
|
|
|
239
179
|
this.numericMap = {};
|
|
240
180
|
this.manifest = {};
|
|
241
|
-
this.customGroups = {};
|
|
181
|
+
this.customGroups = {};
|
|
242
182
|
|
|
243
183
|
if (numericMapPath) {
|
|
244
184
|
const mapData = (0, _i18nDataLoader.loadNumericMap)(numericMapPath, compilation);
|
|
@@ -250,28 +190,24 @@ class I18nNumericIndexPlugin {
|
|
|
250
190
|
}
|
|
251
191
|
});
|
|
252
192
|
}
|
|
253
|
-
}
|
|
254
|
-
|
|
193
|
+
}
|
|
255
194
|
|
|
256
195
|
let jsResourceKeys = (0, _propertiesUtils.getPropertiesAsJSON)(jsResourcePath);
|
|
257
|
-
jsResourceKeys = this.normalizeObjectValues(jsResourceKeys);
|
|
196
|
+
jsResourceKeys = this.normalizeObjectValues(jsResourceKeys);
|
|
258
197
|
|
|
259
198
|
if (customGroups) {
|
|
260
199
|
this.parseCustomGroups(jsResourcePath, customGroups);
|
|
261
|
-
}
|
|
262
|
-
|
|
200
|
+
}
|
|
263
201
|
|
|
264
202
|
const allI18nObject = (0, _propertiesUtils.getAllI18n)({
|
|
265
203
|
folderPath: propertiesFolderPath,
|
|
266
204
|
disableDefault: false,
|
|
267
205
|
jsResourceI18nKeys: jsResourceKeys
|
|
268
|
-
});
|
|
269
|
-
|
|
206
|
+
});
|
|
270
207
|
Object.keys(allI18nObject).forEach(loc => {
|
|
271
208
|
allI18nObject[loc] = this.normalizeObjectValues(allI18nObject[loc]);
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
allI18nObject['en_US'] = jsResourceKeys; // If requested, restrict all locales to base (English) keys only
|
|
209
|
+
});
|
|
210
|
+
allI18nObject['en_US'] = jsResourceKeys;
|
|
275
211
|
|
|
276
212
|
if (this.options.restrictToBaseKeys) {
|
|
277
213
|
const baseKeys = Object.keys(jsResourceKeys);
|
|
@@ -287,14 +223,12 @@ class I18nNumericIndexPlugin {
|
|
|
287
223
|
});
|
|
288
224
|
}
|
|
289
225
|
|
|
290
|
-
const locales = Object.keys(allI18nObject);
|
|
291
|
-
|
|
226
|
+
const locales = Object.keys(allI18nObject);
|
|
292
227
|
locales.forEach(locale => {
|
|
293
228
|
const localeData = allI18nObject[locale];
|
|
294
229
|
const numericData = {};
|
|
295
230
|
const dynamicData = {};
|
|
296
|
-
const groupData = {};
|
|
297
|
-
|
|
231
|
+
const groupData = {};
|
|
298
232
|
Object.keys(customGroups || {}).forEach(groupName => {
|
|
299
233
|
groupData[groupName] = {};
|
|
300
234
|
}); // Process each key
|
|
@@ -442,12 +376,11 @@ class I18nNumericIndexPlugin {
|
|
|
442
376
|
const {
|
|
443
377
|
outputFolder
|
|
444
378
|
} = this.options;
|
|
445
|
-
const filePath = template.replace(/\[locale\]/g, locale);
|
|
379
|
+
const filePath = template.replace(/\[locale\]/g, locale);
|
|
446
380
|
|
|
447
381
|
if (filePath.includes(outputFolder) || filePath.startsWith('/')) {
|
|
448
382
|
return filePath;
|
|
449
|
-
}
|
|
450
|
-
|
|
383
|
+
}
|
|
451
384
|
|
|
452
385
|
return _path.default.join(outputFolder, filePath).replace(/\\/g, '/');
|
|
453
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,14 +39,7 @@ function readNumericMapOnce(numericMapPath) {
|
|
|
45
39
|
|
|
46
40
|
function i18nIdReplaceLoaderConfig(options) {
|
|
47
41
|
(0, _i18nOptionsValidator.validateI18nOptions)(options);
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (options.i18nIndexing && options.i18nIndexing.enable) {
|
|
51
|
-
numericMapPath = options.i18nIndexing.numericMapPath;
|
|
52
|
-
} else if (options.i18nChunkSplit && options.i18nChunkSplit.chunkSplitEnable && options.i18nChunkSplit.useNumericIndexing) {
|
|
53
|
-
numericMapPath = options.i18nChunkSplit.numericMapPath;
|
|
54
|
-
}
|
|
55
|
-
|
|
42
|
+
const numericMapPath = options.i18nIndexing.numericMapPath;
|
|
56
43
|
const allI18nData = loadJSResourcesOnce(options);
|
|
57
44
|
|
|
58
45
|
const i18nKeyReplaceLoaderPath = require.resolve('../loaders/i18nIdReplaceLoader.js');
|
|
@@ -72,7 +59,8 @@ function i18nIdReplaceLoaderConfig(options) {
|
|
|
72
59
|
numericIdMap: numericIdMap || undefined,
|
|
73
60
|
devMode: options.i18nIndexing?.devMode || false,
|
|
74
61
|
includePaths: options.i18nIndexing?.loaderOptions?.includePaths || [],
|
|
75
|
-
excludePaths: options.i18nIndexing?.loaderOptions?.excludePaths || ['node_modules', 'tests']
|
|
62
|
+
excludePaths: options.i18nIndexing?.loaderOptions?.excludePaths || ['node_modules', 'tests'],
|
|
63
|
+
i18nIndexingEnabled: true
|
|
76
64
|
};
|
|
77
65
|
return {
|
|
78
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,
|