@zohodesk/client_build_tool 0.0.11-exp.29.0 → 0.0.11-exp.31.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 -1
- package/lib/schemas/defaultConfigValuesOnly.js +1 -1
- package/lib/shared/bundler/webpack/common/i18nOptionsValidator.js +1 -26
- package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/I18nGroupRuntimeModule.js +34 -126
- package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/I18nNumericIndexPlugin.js +21 -84
- 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 +4 -1
- 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,82 +19,61 @@ class I18nGroupRuntimeModule extends _webpack.RuntimeModule {
|
|
|
20
19
|
customGroups,
|
|
21
20
|
chunkIdToGroupMapping,
|
|
22
21
|
localeVarName,
|
|
23
|
-
i18nPublicPathVar,
|
|
24
22
|
groupAssetUrls,
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
publicPathPrefix,
|
|
24
|
+
publicPathRuntimeExpression
|
|
27
25
|
} = this.options;
|
|
28
26
|
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
27
|
return `
|
|
33
28
|
// I18n Group Loading Runtime
|
|
34
29
|
(function() {
|
|
35
30
|
var loadedGroups = {};
|
|
36
31
|
var chunkIdToGroup = ${JSON.stringify(chunkIdToGroup)};
|
|
37
32
|
var groupAssetUrls = ${JSON.stringify(groupAssetUrls || {})};
|
|
38
|
-
var hashAware = ${hashAware ? 'true' : 'false'};
|
|
39
|
-
var isDev = ${isDevelopment ? 'true' : 'false'};
|
|
40
33
|
var cachedI18nBase;
|
|
41
34
|
var scriptCache = null;
|
|
35
|
+
var runtimePublicPathPrefixResolver = ${publicPathRuntimeExpression ? `function(){ try { return ${publicPathRuntimeExpression}; } catch (err) { return undefined; } }` : 'function(){ return undefined; }'};
|
|
36
|
+
var runtimePublicPathPrefix = runtimePublicPathPrefixResolver();
|
|
37
|
+
if (runtimePublicPathPrefix === undefined || runtimePublicPathPrefix === null || runtimePublicPathPrefix === '') {
|
|
38
|
+
runtimePublicPathPrefix = ${JSON.stringify(publicPathPrefix || '')};
|
|
39
|
+
}
|
|
40
|
+
if (typeof runtimePublicPathPrefix !== 'string') {
|
|
41
|
+
runtimePublicPathPrefix = runtimePublicPathPrefix != null ? String(runtimePublicPathPrefix) : '';
|
|
42
|
+
}
|
|
42
43
|
|
|
43
44
|
function ensureTrailingSlash(path) {
|
|
44
45
|
if (!path) return '';
|
|
45
46
|
return path.charAt(path.length - 1) === '/' ? path : path + '/';
|
|
46
47
|
}
|
|
47
48
|
|
|
48
|
-
|
|
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
|
-
}
|
|
49
|
+
runtimePublicPathPrefix = ensureTrailingSlash(runtimePublicPathPrefix);
|
|
59
50
|
|
|
60
51
|
function resolveI18nBase() {
|
|
61
52
|
if (cachedI18nBase !== undefined) {
|
|
62
53
|
return cachedI18nBase;
|
|
63
54
|
}
|
|
64
55
|
|
|
65
|
-
var base =
|
|
56
|
+
var base = __webpack_require__.p || '';
|
|
57
|
+
cachedI18nBase = ensureTrailingSlash(base);
|
|
58
|
+
return cachedI18nBase;
|
|
59
|
+
}
|
|
66
60
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
}
|
|
61
|
+
function buildI18nUrl(relativePath) {
|
|
62
|
+
if (!relativePath) {
|
|
63
|
+
return runtimePublicPathPrefix || resolveI18nBase();
|
|
79
64
|
}
|
|
80
65
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
base = base.slice(0, -6) + '/';
|
|
85
|
-
}
|
|
66
|
+
var normalizedPath = relativePath;
|
|
67
|
+
if (normalizedPath.charAt(0) === '/') {
|
|
68
|
+
normalizedPath = normalizedPath.slice(1);
|
|
86
69
|
}
|
|
87
70
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
71
|
+
if (runtimePublicPathPrefix) {
|
|
72
|
+
return runtimePublicPathPrefix + normalizedPath;
|
|
73
|
+
}
|
|
91
74
|
|
|
92
|
-
function buildI18nUrl(relativePath) {
|
|
93
75
|
var base = resolveI18nBase();
|
|
94
|
-
|
|
95
|
-
if (relativePath.charAt(0) === '/') {
|
|
96
|
-
relativePath = relativePath.slice(1);
|
|
97
|
-
}
|
|
98
|
-
return base + relativePath;
|
|
76
|
+
return base + normalizedPath;
|
|
99
77
|
}
|
|
100
78
|
|
|
101
79
|
function loadI18nGroup(groupName, loadReason) {
|
|
@@ -111,19 +89,14 @@ class I18nGroupRuntimeModule extends _webpack.RuntimeModule {
|
|
|
111
89
|
return Promise.resolve();
|
|
112
90
|
}
|
|
113
91
|
|
|
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
92
|
|
|
122
93
|
return new Promise(function(resolve, reject) {
|
|
123
|
-
var relativePath
|
|
94
|
+
var relativePath;
|
|
124
95
|
|
|
125
|
-
if (
|
|
96
|
+
if (groupAssetUrls[groupName] && groupAssetUrls[groupName][locale]) {
|
|
126
97
|
relativePath = groupAssetUrls[groupName][locale];
|
|
98
|
+
} else {
|
|
99
|
+
relativePath = config.filenameTemplate.replace('[locale]', locale);
|
|
127
100
|
}
|
|
128
101
|
|
|
129
102
|
var i18nUrl = buildI18nUrl(relativePath);
|
|
@@ -140,17 +113,11 @@ class I18nGroupRuntimeModule extends _webpack.RuntimeModule {
|
|
|
140
113
|
|
|
141
114
|
script.onload = function() {
|
|
142
115
|
loadedGroups[groupName] = true;
|
|
143
|
-
if (isDev) {
|
|
144
|
-
console.log('[i18n-group:loaded]', groupName, i18nUrl);
|
|
145
|
-
}
|
|
146
116
|
cleanup();
|
|
147
117
|
resolve();
|
|
148
118
|
};
|
|
149
119
|
|
|
150
120
|
script.onerror = function() {
|
|
151
|
-
if (isDev) {
|
|
152
|
-
console.error('[i18n-group:error]', groupName, i18nUrl);
|
|
153
|
-
}
|
|
154
121
|
cleanup();
|
|
155
122
|
loadedGroups[groupName] = true;
|
|
156
123
|
resolve();
|
|
@@ -178,9 +145,6 @@ class I18nGroupRuntimeModule extends _webpack.RuntimeModule {
|
|
|
178
145
|
return i18nPromise.then(function() {
|
|
179
146
|
return originalEnsureChunk.apply(self, args);
|
|
180
147
|
}).catch(function(err) {
|
|
181
|
-
if (isDev) {
|
|
182
|
-
console.error('[i18n-group:critical] i18n failed, proceeding without:', err);
|
|
183
|
-
}
|
|
184
148
|
return originalEnsureChunk.apply(self, args);
|
|
185
149
|
});
|
|
186
150
|
} else {
|
|
@@ -215,73 +179,17 @@ class I18nGroupRuntimeModule extends _webpack.RuntimeModule {
|
|
|
215
179
|
try {
|
|
216
180
|
var detectedGroup = detectGroupFromUrl();
|
|
217
181
|
if (detectedGroup && !loadedGroups[detectedGroup]) {
|
|
218
|
-
if (isDev) {
|
|
219
|
-
console.log('[i18n-group:url-detect]', detectedGroup, 'loading for current URL');
|
|
220
|
-
}
|
|
221
182
|
loadI18nGroup(detectedGroup, 'url-detection').catch(function(err) {
|
|
222
|
-
|
|
223
|
-
console.error('[i18n-group:url-detect] Failed to load', detectedGroup, ':', err);
|
|
224
|
-
}
|
|
183
|
+
// Silent error handling
|
|
225
184
|
});
|
|
226
185
|
}
|
|
227
186
|
} catch (err) {
|
|
228
|
-
|
|
229
|
-
console.error('[i18n-group:url-detect] Error in URL detection:', err);
|
|
230
|
-
}
|
|
187
|
+
// Silent error handling
|
|
231
188
|
}
|
|
232
189
|
}
|
|
233
190
|
|
|
234
|
-
//
|
|
235
|
-
|
|
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
|
-
}
|
|
253
|
-
}
|
|
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
|
-
}
|
|
282
|
-
|
|
283
|
-
// Initialize loading mechanisms
|
|
284
|
-
initializeGroupLoading();
|
|
191
|
+
// Check current URL for immediate loading
|
|
192
|
+
checkAndLoadGroupFromUrl();
|
|
285
193
|
})();
|
|
286
194
|
`;
|
|
287
195
|
}
|
package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/I18nNumericIndexPlugin.js
CHANGED
|
@@ -53,7 +53,9 @@ class I18nNumericIndexPlugin {
|
|
|
53
53
|
outputFolder: options.outputFolder || 'i18n-chunk',
|
|
54
54
|
manifestPath: options.manifestPath || null,
|
|
55
55
|
emitFiles: options.emitFiles !== undefined ? options.emitFiles : true,
|
|
56
|
-
chunkToGroupMapping: options.chunkToGroupMapping || {}
|
|
56
|
+
chunkToGroupMapping: options.chunkToGroupMapping || {},
|
|
57
|
+
groupPublicPathPrefix: options.groupPublicPathPrefix || '',
|
|
58
|
+
groupPublicPathRuntimeExpression: options.groupPublicPathRuntimeExpression || ''
|
|
57
59
|
};
|
|
58
60
|
this.numericMap = {};
|
|
59
61
|
this.customGroups = {};
|
|
@@ -66,20 +68,16 @@ class I18nNumericIndexPlugin {
|
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
apply(compiler) {
|
|
69
|
-
// Build-time validation for critical file paths
|
|
70
|
-
this.validateCriticalFilePaths(compiler); // Detect webpackI18nGroup comments in code
|
|
71
|
-
|
|
72
71
|
this.detectI18nGroupComments(compiler);
|
|
73
72
|
compiler.hooks.thisCompilation.tap(pluginName, compilation => {
|
|
74
73
|
this.groupAssetUrls = {};
|
|
75
74
|
this.detectedGroups = {};
|
|
76
75
|
this.preparedAssets = [];
|
|
77
76
|
this.assetsPrepared = false;
|
|
78
|
-
this.assetsEmitted = false;
|
|
77
|
+
this.assetsEmitted = false;
|
|
79
78
|
|
|
80
79
|
if (this.options.customGroups) {
|
|
81
80
|
compilation.hooks.additionalTreeRuntimeRequirements.tap(pluginName, (chunk, set) => {
|
|
82
|
-
// Only add to the main/entry chunk to avoid duplication
|
|
83
81
|
if (chunk.name === 'main' || chunk.hasRuntime()) {
|
|
84
82
|
this.ensureAssetsPrepared(compilation);
|
|
85
83
|
const chunkMapping = this.getChunkIdToGroupMapping(compilation);
|
|
@@ -88,9 +86,10 @@ class I18nNumericIndexPlugin {
|
|
|
88
86
|
chunkIdToGroupMapping: chunkMapping,
|
|
89
87
|
localeVarName: this.options.localeVarName,
|
|
90
88
|
jsonpFunc: this.options.jsonpFunc,
|
|
91
|
-
i18nPublicPathVar: this.options.i18nPublicPathVar,
|
|
92
89
|
groupAssetUrls: this.groupAssetUrls,
|
|
93
|
-
includeContentHash: this.options.includeContentHash
|
|
90
|
+
includeContentHash: this.options.includeContentHash,
|
|
91
|
+
publicPathPrefix: this.options.groupPublicPathPrefix,
|
|
92
|
+
publicPathRuntimeExpression: this.options.groupPublicPathRuntimeExpression
|
|
94
93
|
}));
|
|
95
94
|
}
|
|
96
95
|
});
|
|
@@ -106,60 +105,6 @@ class I18nNumericIndexPlugin {
|
|
|
106
105
|
});
|
|
107
106
|
}
|
|
108
107
|
|
|
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
108
|
detectI18nGroupComments(compiler) {
|
|
164
109
|
compiler.hooks.normalModuleFactory.tap(pluginName, factory => {
|
|
165
110
|
factory.hooks.parser.for('javascript/auto').tap(pluginName, parser => {
|
|
@@ -197,11 +142,10 @@ class I18nNumericIndexPlugin {
|
|
|
197
142
|
const chunkIdToGroup = {};
|
|
198
143
|
const configuredMappings = { ...this.groupChunkMapping,
|
|
199
144
|
...(this.options.chunkToGroupMapping || {})
|
|
200
|
-
};
|
|
145
|
+
};
|
|
201
146
|
|
|
202
147
|
for (const chunk of compilation.chunks) {
|
|
203
|
-
const chunkName = chunk.name;
|
|
204
|
-
|
|
148
|
+
const chunkName = chunk.name;
|
|
205
149
|
let groupName = null;
|
|
206
150
|
|
|
207
151
|
if (this.detectedGroups && this.detectedGroups[chunkName]) {
|
|
@@ -233,12 +177,12 @@ class I18nNumericIndexPlugin {
|
|
|
233
177
|
|
|
234
178
|
if (!jsResourcePath || !propertiesFolderPath) {
|
|
235
179
|
return;
|
|
236
|
-
} // Reset
|
|
180
|
+
} // Reset caches for incremental builds
|
|
237
181
|
|
|
238
182
|
|
|
239
183
|
this.numericMap = {};
|
|
240
184
|
this.manifest = {};
|
|
241
|
-
this.customGroups = {};
|
|
185
|
+
this.customGroups = {};
|
|
242
186
|
|
|
243
187
|
if (numericMapPath) {
|
|
244
188
|
const mapData = (0, _i18nDataLoader.loadNumericMap)(numericMapPath, compilation);
|
|
@@ -250,28 +194,24 @@ class I18nNumericIndexPlugin {
|
|
|
250
194
|
}
|
|
251
195
|
});
|
|
252
196
|
}
|
|
253
|
-
}
|
|
254
|
-
|
|
197
|
+
}
|
|
255
198
|
|
|
256
199
|
let jsResourceKeys = (0, _propertiesUtils.getPropertiesAsJSON)(jsResourcePath);
|
|
257
|
-
jsResourceKeys = this.normalizeObjectValues(jsResourceKeys);
|
|
200
|
+
jsResourceKeys = this.normalizeObjectValues(jsResourceKeys);
|
|
258
201
|
|
|
259
202
|
if (customGroups) {
|
|
260
203
|
this.parseCustomGroups(jsResourcePath, customGroups);
|
|
261
|
-
}
|
|
262
|
-
|
|
204
|
+
}
|
|
263
205
|
|
|
264
206
|
const allI18nObject = (0, _propertiesUtils.getAllI18n)({
|
|
265
207
|
folderPath: propertiesFolderPath,
|
|
266
208
|
disableDefault: false,
|
|
267
209
|
jsResourceI18nKeys: jsResourceKeys
|
|
268
|
-
});
|
|
269
|
-
|
|
210
|
+
});
|
|
270
211
|
Object.keys(allI18nObject).forEach(loc => {
|
|
271
212
|
allI18nObject[loc] = this.normalizeObjectValues(allI18nObject[loc]);
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
allI18nObject['en_US'] = jsResourceKeys; // If requested, restrict all locales to base (English) keys only
|
|
213
|
+
});
|
|
214
|
+
allI18nObject['en_US'] = jsResourceKeys;
|
|
275
215
|
|
|
276
216
|
if (this.options.restrictToBaseKeys) {
|
|
277
217
|
const baseKeys = Object.keys(jsResourceKeys);
|
|
@@ -287,14 +227,12 @@ class I18nNumericIndexPlugin {
|
|
|
287
227
|
});
|
|
288
228
|
}
|
|
289
229
|
|
|
290
|
-
const locales = Object.keys(allI18nObject);
|
|
291
|
-
|
|
230
|
+
const locales = Object.keys(allI18nObject);
|
|
292
231
|
locales.forEach(locale => {
|
|
293
232
|
const localeData = allI18nObject[locale];
|
|
294
233
|
const numericData = {};
|
|
295
234
|
const dynamicData = {};
|
|
296
|
-
const groupData = {};
|
|
297
|
-
|
|
235
|
+
const groupData = {};
|
|
298
236
|
Object.keys(customGroups || {}).forEach(groupName => {
|
|
299
237
|
groupData[groupName] = {};
|
|
300
238
|
}); // Process each key
|
|
@@ -442,12 +380,11 @@ class I18nNumericIndexPlugin {
|
|
|
442
380
|
const {
|
|
443
381
|
outputFolder
|
|
444
382
|
} = this.options;
|
|
445
|
-
const filePath = template.replace(/\[locale\]/g, locale);
|
|
383
|
+
const filePath = template.replace(/\[locale\]/g, locale);
|
|
446
384
|
|
|
447
385
|
if (filePath.includes(outputFolder) || filePath.startsWith('/')) {
|
|
448
386
|
return filePath;
|
|
449
|
-
}
|
|
450
|
-
|
|
387
|
+
}
|
|
451
388
|
|
|
452
389
|
return _path.default.join(outputFolder, filePath).replace(/\\/g, '/');
|
|
453
390
|
}
|
|
@@ -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 = {
|
|
@@ -21,6 +21,8 @@ function configI18nNumericIndexPlugin(options) {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
const i18nOpts = options.i18nIndexing;
|
|
24
|
+
const groupPublicPathPrefix = i18nOpts.groupPublicPathPrefix || '';
|
|
25
|
+
const groupPublicPathRuntimeExpression = i18nOpts.groupPublicPathRuntimeExpression !== undefined ? i18nOpts.groupPublicPathRuntimeExpression : 'window.__SMAP_PATH__';
|
|
24
26
|
|
|
25
27
|
try {
|
|
26
28
|
const result = (0, _i18nOptionsValidator.validateI18nIndexingOptions)(i18nOpts);
|
|
@@ -81,7 +83,8 @@ function configI18nNumericIndexPlugin(options) {
|
|
|
81
83
|
customGroups: i18nOpts.customGroups || null,
|
|
82
84
|
chunkToGroupMapping: i18nOpts.chunkToGroupMapping || {},
|
|
83
85
|
emitFiles,
|
|
84
|
-
|
|
86
|
+
groupPublicPathPrefix,
|
|
87
|
+
groupPublicPathRuntimeExpression
|
|
85
88
|
};
|
|
86
89
|
const htmlInjectorOptions = { ...sharedOptions,
|
|
87
90
|
htmlTemplateLabel: i18nOpts.htmlTemplateLabel,
|