@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.
@@ -199,8 +199,7 @@ var _default = {
199
199
  emitFiles: true,
200
200
  injectI18nUrlInIndex: true,
201
201
  customGroups: null,
202
- chunkToGroupMapping: {},
203
- i18nPublicPathVar: null
202
+ chunkToGroupMapping: {}
204
203
  },
205
204
  publicFolders: {
206
205
  dev: ['...'],
@@ -116,8 +116,7 @@ var _default = {
116
116
  emitFiles: true,
117
117
  injectI18nUrlInIndex: true,
118
118
  customGroups: null,
119
- chunkToGroupMapping: {},
120
- i18nPublicPathVar: null
119
+ chunkToGroupMapping: {}
121
120
  },
122
121
  publicFolders: {
123
122
  dev: ['...'],
@@ -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', 'jsonpFunc', 'htmlTemplateLabel'];
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
  };
@@ -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
- i18nPublicPathVar,
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 = resolveConfiguredBase();
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 = config.filenameTemplate.replace('[locale]', locale);
73
+ var relativePath;
124
74
 
125
- if (hashAware && groupAssetUrls[groupName] && groupAssetUrls[groupName][locale]) {
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
- if (isDev) {
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
- if (isDev) {
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
- // Initialize loading mechanisms
284
- initializeGroupLoading();
170
+ // Check current URL for immediate loading
171
+ checkAndLoadGroupFromUrl();
285
172
  })();
286
173
  `;
287
174
  }
@@ -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; // Add runtime module for group-based loading
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
- }; // Process chunks to get their actual IDs and map to groups
141
+ };
201
142
 
202
143
  for (const chunk of compilation.chunks) {
203
- const chunkName = chunk.name; // First try detected groups, then fall back to configured mappings
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 build-scoped caches so incremental builds don't carry stale data
176
+ } // Reset caches for incremental builds
237
177
 
238
178
 
239
179
  this.numericMap = {};
240
180
  this.manifest = {};
241
- this.customGroups = {}; // Load existing numeric map if available
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
- } // Read JSResources.properties
254
-
193
+ }
255
194
 
256
195
  let jsResourceKeys = (0, _propertiesUtils.getPropertiesAsJSON)(jsResourcePath);
257
- jsResourceKeys = this.normalizeObjectValues(jsResourceKeys); // Parse custom groups from banner markers
196
+ jsResourceKeys = this.normalizeObjectValues(jsResourceKeys);
258
197
 
259
198
  if (customGroups) {
260
199
  this.parseCustomGroups(jsResourcePath, customGroups);
261
- } // Get all locale translations
262
-
200
+ }
263
201
 
264
202
  const allI18nObject = (0, _propertiesUtils.getAllI18n)({
265
203
  folderPath: propertiesFolderPath,
266
204
  disableDefault: false,
267
205
  jsResourceI18nKeys: jsResourceKeys
268
- }); // Normalize locale values
269
-
206
+ });
270
207
  Object.keys(allI18nObject).forEach(loc => {
271
208
  allI18nObject[loc] = this.normalizeObjectValues(allI18nObject[loc]);
272
- }); // For en_US, use only JSResources
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); // Process each locale
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 = {}; // Initialize custom groups
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); // If template already contains outputFolder or starts with path separator, use as-is
379
+ const filePath = template.replace(/\[locale\]/g, locale);
446
380
 
447
381
  if (filePath.includes(outputFolder) || filePath.startsWith('/')) {
448
382
  return filePath;
449
- } // Otherwise, join with outputFolder
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
- } // TODO: need to make it as custom option and dynamic
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 = []; // Always add babel loader first
16
-
17
- useLoaders.push((0, _babelLoaderConfig.babelLoaderConfig)(options)); // Add i18n ID replace loader if numeric indexing is enabled
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
- let jsResourcePath;
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
- let numericMapPath;
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(); // Skip files in excluded paths
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
- } // Only process files in included paths if specified
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
- } // Basic runtime validation - options should already be validated by config
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
- } // Load numeric ID mapping - paths already validated by config
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
- } // If no numeric map available, return source as-is
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', 'classProperties', 'optionalChaining', 'nullishCoalescingOperator'],
91
+ plugins: ['jsx', 'typescript', 'nullishCoalescingOperator'],
75
92
  sourceFilename: resourcePath
76
93
  });
77
94
  let hasTransformations = false;
78
- let transformationCount = 0; // Traverse AST and replace i18n keys with numeric IDs
79
-
95
+ let transformationCount = 0;
80
96
  traverse(ast, {
81
97
  StringLiteral(path) {
82
98
  const {
83
99
  node
84
- } = path; // Check if this string is an i18n key and has numeric mapping
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
- }); // Generate code if transformations were made
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 = []; // Add numeric i18n chunk
31
+ const newI18nAssets = [];
34
32
 
35
33
  if (numericFilenameTemplate) {
36
- const numericFilename = numericFilenameTemplate.replace(/\[locale\]/g, htmlTemplateLabel).replace(/%5Blocale%5D/g, htmlTemplateLabel); // Don't add CDN template - HtmlWebpackPlugin handles it
37
-
34
+ const numericFilename = numericFilenameTemplate.replace(/\[locale\]/g, htmlTemplateLabel).replace(/%5Blocale%5D/g, htmlTemplateLabel);
38
35
  newI18nAssets.push(numericFilename);
39
- } // Add dynamic i18n chunk
40
-
36
+ }
41
37
 
42
38
  if (dynamicFilenameTemplate) {
43
- const dynamicFilename = dynamicFilenameTemplate.replace(/\[locale\]/g, htmlTemplateLabel).replace(/%5Blocale%5D/g, htmlTemplateLabel); // Don't add CDN template - HtmlWebpackPlugin handles it
44
-
39
+ const dynamicFilename = dynamicFilenameTemplate.replace(/\[locale\]/g, htmlTemplateLabel).replace(/%5Blocale%5D/g, htmlTemplateLabel);
45
40
  newI18nAssets.push(dynamicFilename);
46
- } // Add custom group chunks if they should be in initial HTML
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); // Don't add CDN template - HtmlWebpackPlugin handles it
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
- } // Prepend i18n assets to ensure they load before main bundle
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; // Only create this plugin if i18nIndexing is enabled
67
+ } = config;
77
68
 
78
69
  if (!i18nIndexing || !i18nIndexing.enable) {
79
70
  return null;
80
- } // Get the CDN template for i18n resources if CDN is enabled
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zohodesk/client_build_tool",
3
- "version": "0.0.11-exp.29.0",
3
+ "version": "0.0.11-exp.30.0",
4
4
  "description": "A CLI tool to build web applications and client libraries",
5
5
  "main": "lib/index.js",
6
6
  "bin": {