@zohodesk/client_build_tool 0.0.11-exp.28.0 → 0.0.11-exp.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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,21 +6,10 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.validateI18nChunkSplitOptions = validateI18nChunkSplitOptions;
7
7
  exports.validateI18nIndexingOptions = validateI18nIndexingOptions;
8
8
  exports.validateI18nOptions = validateI18nOptions;
9
- exports.validatePathSecurity = validatePathSecurity;
10
9
  exports.validateRequiredFields = validateRequiredFields;
11
10
  exports.validateTemplateFormat = validateTemplateFormat;
12
11
  const LOCALE_PLACEHOLDER = '[locale]';
13
- const REQUIRED_OPTIONS = ['jsResourcePath', 'propertiesFolderPath', 'numericMapPath', 'jsonpFunc', 'htmlTemplateLabel'];
14
-
15
- function validatePathSecurity(path, fieldName) {
16
- if (!path || typeof path !== 'string') {
17
- return;
18
- }
19
-
20
- if (path.includes('..')) {
21
- throw new Error(`[I18nOptionsValidator] Invalid ${fieldName} contains '..': ${path}`);
22
- }
23
- }
12
+ const REQUIRED_OPTIONS = ['jsResourcePath', 'propertiesFolderPath', 'numericMapPath'];
24
13
 
25
14
  function validateRequiredFields(options, requiredFields) {
26
15
  const missingOptions = requiredFields.filter(opt => {
@@ -41,8 +30,6 @@ function validateTemplateFormat(template, templateName) {
41
30
  if (!template.includes(LOCALE_PLACEHOLDER)) {
42
31
  throw new Error(`[I18nOptionsValidator] ${templateName} must include ${LOCALE_PLACEHOLDER} placeholder`);
43
32
  }
44
-
45
- validatePathSecurity(template, templateName);
46
33
  }
47
34
 
48
35
  function validateI18nIndexingOptions(i18nOpts) {
@@ -57,9 +44,6 @@ function validateI18nIndexingOptions(i18nOpts) {
57
44
  }
58
45
 
59
46
  validateRequiredFields(i18nOpts, REQUIRED_OPTIONS);
60
- validatePathSecurity(i18nOpts.jsResourcePath, 'jsResourcePath');
61
- validatePathSecurity(i18nOpts.propertiesFolderPath, 'propertiesFolderPath');
62
- validatePathSecurity(i18nOpts.numericMapPath, 'numericMapPath');
63
47
  const numericTemplate = i18nOpts.numericFilenameTemplate || '[locale]/numeric.i18n.js';
64
48
  const dynamicTemplate = i18nOpts.dynamicFilenameTemplate || '[locale]/dynamic.i18n.js';
65
49
  validateTemplateFormat(numericTemplate, 'numericFilenameTemplate');
@@ -69,11 +53,6 @@ function validateI18nIndexingOptions(i18nOpts) {
69
53
  validateTemplateFormat(i18nOpts.singleFileTemplate, 'singleFileTemplate');
70
54
  }
71
55
 
72
- if (i18nOpts.outputFolder) {
73
- validatePathSecurity(i18nOpts.outputFolder, 'outputFolder');
74
- } // Validate conflicting options
75
-
76
-
77
56
  const includeContentHash = i18nOpts.includeContentHash;
78
57
  const injectHtml = i18nOpts.injectI18nUrlInIndex !== undefined ? i18nOpts.injectI18nUrlInIndex : true;
79
58
 
@@ -105,8 +84,6 @@ function validateI18nChunkSplitOptions(chunkSplitOpts) {
105
84
  throw new Error('[I18nOptionsValidator] Missing required numericMapPath in i18nChunkSplit options');
106
85
  }
107
86
 
108
- validatePathSecurity(chunkSplitOpts.jsResource, 'jsResource');
109
- validatePathSecurity(chunkSplitOpts.numericMapPath, 'numericMapPath');
110
87
  return {
111
88
  skipValidation: false
112
89
  };
@@ -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,66 +101,6 @@ class I18nNumericIndexPlugin {
106
101
  });
107
102
  }
108
103
 
109
- validateCriticalFilePaths(compiler) {
110
- const {
111
- jsResourcePath,
112
- propertiesFolderPath,
113
- numericMapPath
114
- } = this.options;
115
- const missingPaths = [];
116
- const resolvedPaths = []; // Check jsResourcePath (must exist)
117
-
118
- if (jsResourcePath) {
119
- const resolvedJsPath = _path.default.resolve(compiler.context, jsResourcePath);
120
-
121
- resolvedPaths.push(`jsResourcePath: ${resolvedJsPath}`);
122
-
123
- if (!_fs.default.existsSync(resolvedJsPath)) {
124
- missingPaths.push(`jsResourcePath: ${resolvedJsPath}`);
125
- }
126
- } else {
127
- missingPaths.push('jsResourcePath: [not configured]');
128
- } // Check propertiesFolderPath (must exist)
129
-
130
-
131
- if (propertiesFolderPath) {
132
- const resolvedPropsPath = _path.default.resolve(compiler.context, propertiesFolderPath);
133
-
134
- resolvedPaths.push(`propertiesFolderPath: ${resolvedPropsPath}`);
135
-
136
- if (!_fs.default.existsSync(resolvedPropsPath)) {
137
- missingPaths.push(`propertiesFolderPath: ${resolvedPropsPath}`);
138
- }
139
- } else {
140
- missingPaths.push('propertiesFolderPath: [not configured]');
141
- } // Check numericMapPath (must exist)
142
-
143
-
144
- if (numericMapPath) {
145
- const resolvedMapPath = _path.default.resolve(compiler.context, numericMapPath);
146
-
147
- resolvedPaths.push(`numericMapPath: ${resolvedMapPath}`);
148
-
149
- if (!_fs.default.existsSync(resolvedMapPath)) {
150
- missingPaths.push(`numericMapPath: ${resolvedMapPath}`);
151
- }
152
- } else {
153
- missingPaths.push('numericMapPath: [not configured]');
154
- } // If any critical files are missing, fail the build immediately
155
-
156
-
157
- if (missingPaths.length > 0) {
158
- const errorMessage = ['', '🚨 I18N BUILD FAILURE: Critical i18n files are missing!', '', 'The following required files could not be found:', ...missingPaths.map(path => ` ❌ ${path}`), '', 'These files are essential for i18n functionality. Please ensure they exist before building.', '', 'Resolved paths checked:', ...resolvedPaths.map(path => ` 📁 ${path}`), ''].join('\n');
159
- throw new Error(errorMessage);
160
- } // Log successful validation in development mode
161
-
162
-
163
- if (compiler.options.mode === 'development') {
164
- console.log('✅ I18n critical file validation passed:');
165
- resolvedPaths.forEach(path => console.log(` 📁 ${path}`));
166
- }
167
- }
168
-
169
104
  detectI18nGroupComments(compiler) {
170
105
  compiler.hooks.normalModuleFactory.tap(pluginName, factory => {
171
106
  factory.hooks.parser.for('javascript/auto').tap(pluginName, parser => {
@@ -203,11 +138,10 @@ class I18nNumericIndexPlugin {
203
138
  const chunkIdToGroup = {};
204
139
  const configuredMappings = { ...this.groupChunkMapping,
205
140
  ...(this.options.chunkToGroupMapping || {})
206
- }; // Process chunks to get their actual IDs and map to groups
141
+ };
207
142
 
208
143
  for (const chunk of compilation.chunks) {
209
- const chunkName = chunk.name; // First try detected groups, then fall back to configured mappings
210
-
144
+ const chunkName = chunk.name;
211
145
  let groupName = null;
212
146
 
213
147
  if (this.detectedGroups && this.detectedGroups[chunkName]) {
@@ -239,12 +173,12 @@ class I18nNumericIndexPlugin {
239
173
 
240
174
  if (!jsResourcePath || !propertiesFolderPath) {
241
175
  return;
242
- } // Reset build-scoped caches so incremental builds don't carry stale data
176
+ } // Reset caches for incremental builds
243
177
 
244
178
 
245
179
  this.numericMap = {};
246
180
  this.manifest = {};
247
- this.customGroups = {}; // Load existing numeric map if available
181
+ this.customGroups = {};
248
182
 
249
183
  if (numericMapPath) {
250
184
  const mapData = (0, _i18nDataLoader.loadNumericMap)(numericMapPath, compilation);
@@ -256,28 +190,24 @@ class I18nNumericIndexPlugin {
256
190
  }
257
191
  });
258
192
  }
259
- } // Read JSResources.properties
260
-
193
+ }
261
194
 
262
195
  let jsResourceKeys = (0, _propertiesUtils.getPropertiesAsJSON)(jsResourcePath);
263
- jsResourceKeys = this.normalizeObjectValues(jsResourceKeys); // Parse custom groups from banner markers
196
+ jsResourceKeys = this.normalizeObjectValues(jsResourceKeys);
264
197
 
265
198
  if (customGroups) {
266
199
  this.parseCustomGroups(jsResourcePath, customGroups);
267
- } // Get all locale translations
268
-
200
+ }
269
201
 
270
202
  const allI18nObject = (0, _propertiesUtils.getAllI18n)({
271
203
  folderPath: propertiesFolderPath,
272
204
  disableDefault: false,
273
205
  jsResourceI18nKeys: jsResourceKeys
274
- }); // Normalize locale values
275
-
206
+ });
276
207
  Object.keys(allI18nObject).forEach(loc => {
277
208
  allI18nObject[loc] = this.normalizeObjectValues(allI18nObject[loc]);
278
- }); // For en_US, use only JSResources
279
-
280
- allI18nObject['en_US'] = jsResourceKeys; // If requested, restrict all locales to base (English) keys only
209
+ });
210
+ allI18nObject['en_US'] = jsResourceKeys;
281
211
 
282
212
  if (this.options.restrictToBaseKeys) {
283
213
  const baseKeys = Object.keys(jsResourceKeys);
@@ -293,14 +223,12 @@ class I18nNumericIndexPlugin {
293
223
  });
294
224
  }
295
225
 
296
- const locales = Object.keys(allI18nObject); // Process each locale
297
-
226
+ const locales = Object.keys(allI18nObject);
298
227
  locales.forEach(locale => {
299
228
  const localeData = allI18nObject[locale];
300
229
  const numericData = {};
301
230
  const dynamicData = {};
302
- const groupData = {}; // Initialize custom groups
303
-
231
+ const groupData = {};
304
232
  Object.keys(customGroups || {}).forEach(groupName => {
305
233
  groupData[groupName] = {};
306
234
  }); // Process each key
@@ -448,12 +376,11 @@ class I18nNumericIndexPlugin {
448
376
  const {
449
377
  outputFolder
450
378
  } = this.options;
451
- const filePath = template.replace(/\[locale\]/g, locale); // If template already contains outputFolder or starts with path separator, use as-is
379
+ const filePath = template.replace(/\[locale\]/g, locale);
452
380
 
453
381
  if (filePath.includes(outputFolder) || filePath.startsWith('/')) {
454
382
  return filePath;
455
- } // Otherwise, join with outputFolder
456
-
383
+ }
457
384
 
458
385
  return _path.default.join(outputFolder, filePath).replace(/\\/g, '/');
459
386
  }
@@ -59,8 +59,7 @@ function getPropertiesAsJSON(filePath) {
59
59
  } catch (err) {
60
60
  return {};
61
61
  }
62
- } // 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,16 +39,11 @@ function readNumericMapOnce(numericMapPath) {
45
39
 
46
40
  function i18nIdReplaceLoaderConfig(options) {
47
41
  (0, _i18nOptionsValidator.validateI18nOptions)(options);
48
- let numericMapPath;
42
+ const numericMapPath = options.i18nIndexing.numericMapPath;
43
+ const allI18nData = loadJSResourcesOnce(options);
49
44
 
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
- }
45
+ const i18nKeyReplaceLoaderPath = require.resolve('../loaders/i18nIdReplaceLoader.js');
55
46
 
56
- const allI18nData = loadJSResourcesOnce(options);
57
- const i18nKeyReplaceLoaderPath = new URL('../loaders/i18nIdReplaceLoader.js', import.meta.url).pathname;
58
47
  let numericIdMap;
59
48
 
60
49
  try {
@@ -70,7 +59,8 @@ function i18nIdReplaceLoaderConfig(options) {
70
59
  numericIdMap: numericIdMap || undefined,
71
60
  devMode: options.i18nIndexing?.devMode || false,
72
61
  includePaths: options.i18nIndexing?.loaderOptions?.includePaths || [],
73
- excludePaths: options.i18nIndexing?.loaderOptions?.excludePaths || ['node_modules', 'tests']
62
+ excludePaths: options.i18nIndexing?.loaderOptions?.excludePaths || ['node_modules', 'tests'],
63
+ i18nIndexingEnabled: true
74
64
  };
75
65
  return {
76
66
  loader: i18nKeyReplaceLoaderPath,
@@ -20,7 +20,11 @@ module.exports = function i18nIdReplaceLoader(source, map) {
20
20
  const resourcePath = this.resourcePath;
21
21
  this.cacheable && this.cacheable();
22
22
  const options = getOptions(this) || {};
23
- const callback = this.async(); // 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.28.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": {