@zohodesk/client_build_tool 0.0.11-exp.26.0 → 0.0.11-exp.27.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.
@@ -20,29 +20,24 @@ class I18nGroupRuntimeModule extends _webpack.RuntimeModule {
20
20
  customGroups,
21
21
  chunkIdToGroupMapping,
22
22
  localeVarName,
23
- jsonpFunc,
24
- i18nPublicPathVar
25
- } = this.options; // Use the pre-computed chunk ID to group mapping
26
-
23
+ i18nPublicPathVar,
24
+ groupAssetUrls,
25
+ includeContentHash
26
+ } = this.options;
27
27
  const chunkIdToGroup = chunkIdToGroupMapping || {};
28
+ const hasGroupAssetOverrides = !!groupAssetUrls && Object.values(groupAssetUrls).some(localeMap => localeMap && Object.keys(localeMap).length > 0);
29
+ const hashAware = includeContentHash || hasGroupAssetOverrides;
28
30
  return `
29
31
  // I18n Group Loading Runtime
30
32
  (function() {
31
33
  var loadedGroups = {};
32
34
  var chunkIdToGroup = ${JSON.stringify(chunkIdToGroup)};
35
+ var groupAssetUrls = ${JSON.stringify(groupAssetUrls || {})};
36
+ var hashAware = ${hashAware ? 'true' : 'false'};
33
37
  var cachedI18nBase;
34
- var loadReasonKey = '__i18nGroupLoadReason';
35
-
36
- // Only log if we have actual mappings to track
37
- if (Object.keys(chunkIdToGroup).length > 0) {
38
- console.log('[i18n-group:runtime] I18n Group Runtime Module loaded');
39
- console.log('[i18n-group:runtime] Chunk ID to Group mapping:', chunkIdToGroup);
40
- }
41
38
 
42
39
  function ensureTrailingSlash(path) {
43
- if (!path) {
44
- return '';
45
- }
40
+ if (!path) return '';
46
41
  return path.charAt(path.length - 1) === '/' ? path : path + '/';
47
42
  }
48
43
 
@@ -53,39 +48,11 @@ class I18nGroupRuntimeModule extends _webpack.RuntimeModule {
53
48
  return candidate;
54
49
  }
55
50
  } catch (err) {
56
- // ignore – fall back to DOM/publicPath detection
51
+ // Fall back to DOM/publicPath detection
57
52
  }
58
53
  return '';
59
54
  }
60
55
 
61
- function withLoadReason(reason, fn) {
62
- var previous = __webpack_require__[loadReasonKey];
63
- __webpack_require__[loadReasonKey] = reason;
64
- try {
65
- return fn();
66
- } finally {
67
- __webpack_require__[loadReasonKey] = previous;
68
- }
69
- }
70
-
71
- if (typeof __webpack_require__.pfc === 'function') {
72
- var originalPrefetchChunk = __webpack_require__.pfc;
73
- __webpack_require__.pfc = function(prefetchChunkId) {
74
- return withLoadReason('prefetch', function() {
75
- return originalPrefetchChunk(prefetchChunkId);
76
- });
77
- };
78
- }
79
-
80
- if (typeof __webpack_require__.plc === 'function') {
81
- var originalPreloadChunk = __webpack_require__.plc;
82
- __webpack_require__.plc = function(preloadChunkId) {
83
- return withLoadReason('preload', function() {
84
- return originalPreloadChunk(preloadChunkId);
85
- });
86
- };
87
- }
88
-
89
56
  function resolveI18nBase() {
90
57
  if (cachedI18nBase !== undefined) {
91
58
  return cachedI18nBase;
@@ -113,24 +80,18 @@ class I18nGroupRuntimeModule extends _webpack.RuntimeModule {
113
80
  }
114
81
 
115
82
  cachedI18nBase = ensureTrailingSlash(base);
116
- if (typeof console !== 'undefined' && console.log) {
117
- console.log('[i18n-group:base]', cachedI18nBase || '<empty>');
118
- }
119
83
  return cachedI18nBase;
120
84
  }
121
85
 
122
86
  function buildI18nUrl(relativePath) {
123
87
  var base = resolveI18nBase();
124
- if (!relativePath) {
125
- return base;
126
- }
88
+ if (!relativePath) return base;
127
89
  if (relativePath.charAt(0) === '/') {
128
90
  relativePath = relativePath.slice(1);
129
91
  }
130
92
  return base + relativePath;
131
93
  }
132
-
133
- // Function to load i18n group
94
+
134
95
  function loadI18nGroup(groupName, loadReason) {
135
96
  if (loadedGroups[groupName]) {
136
97
  return Promise.resolve();
@@ -149,178 +110,139 @@ class I18nGroupRuntimeModule extends _webpack.RuntimeModule {
149
110
  (loadReason === 'preload' && config.preload === false);
150
111
 
151
112
  if (shouldSkip) {
152
- if (typeof console !== 'undefined' && console.log) {
153
- console.log('[i18n-group:skip]', groupName, loadReason, 'prefetch/preload disabled');
154
- }
155
113
  return Promise.resolve();
156
114
  }
157
115
 
158
116
  return new Promise(function(resolve, reject) {
159
- var relativePath = config.filenameTemplate
160
- .replace('[locale]', locale);
161
- var i18nUrl = buildI18nUrl(relativePath);
117
+ var relativePath = config.filenameTemplate.replace('[locale]', locale);
162
118
 
163
- if (typeof console !== 'undefined' && console.log) {
164
- console.log('[i18n-group:load]', groupName, loadReason || 'require', i18nUrl);
119
+ if (hashAware && groupAssetUrls[groupName] && groupAssetUrls[groupName][locale]) {
120
+ relativePath = groupAssetUrls[groupName][locale];
165
121
  }
166
122
 
123
+ var i18nUrl = buildI18nUrl(relativePath);
124
+
167
125
  var script = document.createElement('script');
168
126
  script.src = i18nUrl;
169
127
  script.async = true;
170
-
128
+
171
129
  script.onload = function() {
172
130
  loadedGroups[groupName] = true;
173
- if (typeof console !== 'undefined' && console.log) {
174
- console.log('[i18n-group:loaded]', groupName, loadReason || 'require', i18nUrl);
175
- }
131
+ console.log('[i18n-group:loaded]', groupName, i18nUrl);
176
132
  resolve();
177
133
  };
178
134
 
179
135
  script.onerror = function() {
180
- if (typeof console !== 'undefined' && console.error) {
181
- console.error('[i18n-group:error]', groupName, i18nUrl);
182
- }
136
+ console.error('[i18n-group:error]', groupName, i18nUrl);
183
137
  reject(new Error('Failed to load i18n group: ' + groupName));
184
138
  };
185
-
139
+
186
140
  document.head.appendChild(script);
187
141
  });
188
142
  }
189
143
 
190
144
  function findGroupByChunkId(chunkId) {
191
- // Direct numeric chunk ID lookup
192
145
  return chunkIdToGroup[chunkId];
193
146
  }
194
-
195
- // Store original webpack chunk loading function
147
+
196
148
  var originalEnsureChunk = __webpack_require__.e;
197
-
198
- // Override webpack's chunk loading if it exists
149
+
199
150
  if (originalEnsureChunk) {
200
151
  __webpack_require__.e = function(chunkId) {
201
- if (typeof console !== 'undefined' && console.log) {
202
- console.log('[i18n-group:debug] Chunk loading called for ID:', chunkId);
203
- }
204
-
205
- // Capture the arguments for later use
206
152
  var args = arguments;
207
153
  var self = this;
208
-
209
- // Check if this chunk needs an i18n group
210
154
  var groupName = findGroupByChunkId(chunkId);
211
- if (typeof console !== 'undefined' && console.log) {
212
- console.log('[i18n-group:debug] Found group for chunk', chunkId, ':', groupName);
213
- console.log('[i18n-group:debug] Chunk mapping:', chunkIdToGroup);
214
- }
215
155
 
216
156
  if (groupName && !loadedGroups[groupName]) {
217
- if (typeof console !== 'undefined' && console.log) {
218
- var chunkFilename = typeof __webpack_require__ !== 'undefined' && typeof __webpack_require__.u === 'function'
219
- ? __webpack_require__.u(chunkId)
220
- : '<no __webpack_require__.u>';
221
- console.log('[i18n-group:hook]', chunkId, '→', groupName, '| reason:', (__webpack_require__[loadReasonKey] || 'require'), '| file:', chunkFilename);
222
- }
223
- // Load the i18n group FIRST, then load the chunk
224
- var loadReason = __webpack_require__[loadReasonKey] || 'require';
225
- var i18nPromise = loadI18nGroup(groupName, loadReason);
226
- // Chain: i18n loads first, then chunk loads
157
+ var i18nPromise = loadI18nGroup(groupName, 'require');
158
+
227
159
  return i18nPromise.then(function() {
228
- // Only start loading the actual chunk after i18n is loaded
229
160
  return originalEnsureChunk.apply(self, args);
161
+ }).catch(function(err) {
162
+ console.error('[i18n-group:critical] i18n failed, blocking chunk loading:', err);
163
+ return Promise.reject(new Error('Cannot load setup without i18n: ' + err.message));
230
164
  });
231
165
  } else {
232
- if (typeof console !== 'undefined' && console.log) {
233
- if (!groupName) {
234
- console.log('[i18n-group:debug] No group found for chunk', chunkId);
235
- } else if (loadedGroups[groupName]) {
236
- console.log('[i18n-group:debug] Group', groupName, 'already loaded for chunk', chunkId);
237
- }
238
- }
239
- // No i18n needed, just load the chunk normally
240
166
  return originalEnsureChunk.apply(this, arguments);
241
167
  }
242
168
  };
243
169
  }
244
-
245
- // Also check for webpackI18nGroup comments in dynamic imports
246
- function wrapLoadScript(originalLoadScript) {
247
- return function(url, done, key, chunkId) {
248
- var groupName = findGroupByChunkId(chunkId);
249
- if (groupName && !loadedGroups[groupName]) {
250
- var locale = ${localeVarName} || 'en_US';
251
- var groupConfig = ${JSON.stringify(customGroups)};
252
- var config = groupConfig[groupName];
253
170
 
254
- if (config) {
255
- var relativePath = config.filenameTemplate
256
- .replace('[locale]', locale);
257
- var i18nUrl = buildI18nUrl(relativePath);
171
+ // Simple URL-based detection for setup pages
172
+ function detectGroupFromUrl() {
173
+ if (typeof window === 'undefined') return null;
258
174
 
259
- if (config.prefetch === false) {
260
- if (typeof console !== 'undefined' && console.log) {
261
- console.log('[i18n-group:skip-prefetch-loader]', groupName, chunkId, i18nUrl);
262
- }
263
- return originalLoadScript.call(__webpack_require__, url, done, key, chunkId);
264
- }
175
+ var url = window.location.href;
176
+ var groupConfig = ${JSON.stringify(customGroups)};
265
177
 
266
- var i18nScript = document.createElement('script');
267
- i18nScript.src = i18nUrl;
268
- i18nScript.onload = function() {
269
- loadedGroups[groupName] = true;
270
- originalLoadScript.call(__webpack_require__, url, done, key, chunkId);
271
- };
272
- i18nScript.onerror = function() {
273
- if (typeof console !== 'undefined' && console.error) {
274
- console.error('[i18n-group:error]', groupName, i18nUrl);
275
- }
276
- originalLoadScript.call(__webpack_require__, url, done, key, chunkId);
277
- };
278
- document.head.appendChild(i18nScript);
279
- return;
178
+ for (var groupName in groupConfig) {
179
+ var config = groupConfig[groupName];
180
+ if (config.urlPatterns && Array.isArray(config.urlPatterns)) {
181
+ for (var i = 0; i < config.urlPatterns.length; i++) {
182
+ var pattern = config.urlPatterns[i];
183
+ if (url.indexOf(pattern) !== -1) {
184
+ return groupName;
185
+ }
280
186
  }
281
187
  }
188
+ }
282
189
 
283
- return originalLoadScript.call(__webpack_require__, url, done, key, chunkId);
284
- };
190
+ return null;
285
191
  }
286
192
 
287
- function installLoadScriptHook(loadScript) {
288
- if (typeof loadScript !== 'function') {
289
- Object.defineProperty(__webpack_require__, 'l', {
290
- configurable: true,
291
- enumerable: true,
292
- get: function() {
293
- return loadScript;
294
- },
295
- set: function(newLoader) {
296
- installLoadScriptHook(newLoader);
297
- }
193
+ function checkAndLoadGroupFromUrl() {
194
+ var detectedGroup = detectGroupFromUrl();
195
+ if (detectedGroup && !loadedGroups[detectedGroup]) {
196
+ console.log('[i18n-group:url-detect]', detectedGroup, 'loading for current URL');
197
+ loadI18nGroup(detectedGroup, 'url-detection').catch(function(err) {
198
+ console.error('[i18n-group:url-detect] Failed to load', detectedGroup, ':', err);
298
199
  });
299
- return;
300
200
  }
301
-
302
- Object.defineProperty(__webpack_require__, 'l', {
303
- configurable: true,
304
- enumerable: true,
305
- writable: true,
306
- value: wrapLoadScript(loadScript)
307
- });
308
201
  }
309
202
 
310
- if (typeof __webpack_require__.l === 'function') {
311
- installLoadScriptHook(__webpack_require__.l);
312
- } else {
313
- Object.defineProperty(__webpack_require__, 'l', {
314
- configurable: true,
315
- enumerable: true,
316
- set: function(newLoader) {
317
- installLoadScriptHook(newLoader);
318
- },
319
- get: function() {
320
- return undefined;
203
+ // Initialize preload and prefetch mechanisms
204
+ function initializeGroupLoading() {
205
+ if (typeof window === 'undefined') return;
206
+
207
+ var groupConfig = ${JSON.stringify(customGroups)};
208
+
209
+ // 1. Immediate preload for critical groups
210
+ for (var groupName in groupConfig) {
211
+ var config = groupConfig[groupName];
212
+ if (config.preload === true && !loadedGroups[groupName]) {
213
+ console.log('[i18n-group:preload]', groupName, 'loading immediately');
214
+ loadI18nGroup(groupName, 'preload').catch(function(err) {
215
+ console.error('[i18n-group:preload] Failed to preload', groupName, ':', err);
216
+ });
321
217
  }
322
- });
218
+ }
219
+
220
+ // 2. Schedule prefetch for background loading
221
+ function schedulePrefetch() {
222
+ for (var groupName in groupConfig) {
223
+ var config = groupConfig[groupName];
224
+ if (config.prefetch === true && !loadedGroups[groupName] && config.preload !== true) {
225
+ console.log('[i18n-group:prefetch]', groupName, 'scheduling background load');
226
+ loadI18nGroup(groupName, 'prefetch').catch(function(err) {
227
+ console.error('[i18n-group:prefetch] Failed to prefetch', groupName, ':', err);
228
+ });
229
+ }
230
+ }
231
+ }
232
+
233
+ // Use requestIdleCallback for prefetch if available, otherwise setTimeout
234
+ if (window.requestIdleCallback) {
235
+ window.requestIdleCallback(schedulePrefetch, { timeout: 5000 });
236
+ } else {
237
+ setTimeout(schedulePrefetch, 100);
238
+ }
239
+
240
+ // 3. Check current URL for immediate loading
241
+ checkAndLoadGroupFromUrl();
323
242
  }
243
+
244
+ // Initialize loading mechanisms
245
+ initializeGroupLoading();
324
246
  })();
325
247
  `;
326
248
  }
@@ -5,19 +5,20 @@ const HtmlWebpackPlugin = require('html-webpack-plugin');
5
5
  const path = require('path');
6
6
 
7
7
  const pluginName = 'I18nNumericIndexHtmlInjectorPlugin';
8
+ const assetStoreKey = Symbol.for('I18nNumericIndexPluginAssets');
8
9
 
9
10
  class I18nNumericIndexHtmlInjectorPlugin {
10
11
  constructor(options) {
11
12
  this.options = { ...options,
12
- injectI18nUrlInIndex: options.injectI18nUrlInIndex !== undefined ? options.injectI18nUrlInIndex : true // Default to true
13
-
13
+ injectI18nUrlInIndex: options.injectI18nUrlInIndex !== undefined ? options.injectI18nUrlInIndex : true,
14
+ includeContentHash: options.includeContentHash || false
14
15
  };
15
16
  }
16
17
 
17
18
  apply(compiler) {
18
19
  compiler.hooks.thisCompilation.tap(pluginName, compilation => {
19
20
  HtmlWebpackPlugin.getHooks(compilation).beforeAssetTagGeneration.tapAsync(pluginName, (hookData, cb) => {
20
- // Skip HTML injection if injectI18nUrlInIndex is false
21
+ // Skip HTML injection if disabled
21
22
  if (!this.options.injectI18nUrlInIndex) {
22
23
  return cb(null, hookData);
23
24
  }
@@ -26,7 +27,7 @@ class I18nNumericIndexHtmlInjectorPlugin {
26
27
  assets
27
28
  } = hookData;
28
29
  const {
29
- outputFolder,
30
+ outputFolder = 'i18n-chunk',
30
31
  numericFilenameTemplate,
31
32
  dynamicFilenameTemplate,
32
33
  singleFileTemplate,
@@ -34,47 +35,61 @@ class I18nNumericIndexHtmlInjectorPlugin {
34
35
  singleFile,
35
36
  i18nAssetsPublicPathPrefix = ''
36
37
  } = this.options;
37
- const newI18nAssetUrlsToAdd = []; // Construct full paths using outputFolder
38
+ const newI18nAssetUrlsToAdd = [];
39
+ const emittedAssetNames = compilation.getAssets().map(asset => asset.name);
40
+ const recordedAssets = compilation[assetStoreKey] || []; // Simple asset resolution using recorded assets first, then fallback to template
41
+
42
+ const resolveAssetPath = (template, fileType) => {
43
+ if (!template) return null; // Try to find from recorded assets first
44
+
45
+ const recorded = recordedAssets.find(asset => {
46
+ if (fileType === 'single') return !asset.fileType;
47
+ return asset.fileType === fileType;
48
+ });
49
+
50
+ if (recorded) {
51
+ // Use recorded asset with correct locale
52
+ const assetPath = recorded.outputPath.replace(recorded.locale, htmlTemplateLabel);
53
+ return i18nAssetsPublicPathPrefix + assetPath;
54
+ } // Fallback to template-based construction
38
55
 
39
- const constructFullPath = (template, isSingleFile = false) => {
40
- if (!template) return null; // Replace locale placeholder
41
56
 
42
- let filePath = template.replace(/\[locale\]/g, htmlTemplateLabel); // Remove [contenthash] placeholder for HTML injection
43
- // The actual hash will be determined at build time
57
+ const filePath = template.replace(/\[locale\]/g, htmlTemplateLabel);
58
+ const fullPath = filePath.includes(outputFolder) || filePath.startsWith('/') ? filePath : path.join(outputFolder, filePath); // Check if the exact asset exists
44
59
 
45
- filePath = filePath.replace(/\.\[contenthash\]/g, ''); // If template already contains outputFolder or starts with a path separator, use as-is
60
+ if (emittedAssetNames.includes(fullPath)) {
61
+ return i18nAssetsPublicPathPrefix + fullPath;
62
+ } // For content hash templates, find matching asset
46
63
 
47
- if (filePath.includes(outputFolder) || filePath.startsWith('/')) {
48
- return filePath.replace(/\\/g, '/');
49
- } // For single-file mode with a simple template like '[locale].js',
50
- // put it directly in outputFolder without subdirectories
51
64
 
65
+ if (fullPath.includes('[contenthash]')) {
66
+ const pattern = fullPath.replace('[contenthash]', '*');
67
+ const matchingAsset = emittedAssetNames.find(name => name.startsWith(pattern.split('*')[0]) && name.endsWith(pattern.split('*')[1]));
52
68
 
53
- if (isSingleFile && !filePath.includes('/')) {
54
- return path.join(outputFolder || 'i18n-chunk', filePath).replace(/\\/g, '/');
55
- } // For other cases, preserve subdirectories
69
+ if (matchingAsset) {
70
+ return i18nAssetsPublicPathPrefix + matchingAsset;
71
+ }
72
+ } // Return as-is for basic cases
56
73
 
57
74
 
58
- return path.join(outputFolder || 'i18n-chunk', filePath).replace(/\\/g, '/');
75
+ return i18nAssetsPublicPathPrefix + fullPath;
59
76
  };
60
77
 
61
78
  if (singleFile) {
62
- // In single file mode, use singleFileTemplate
63
79
  const singleTemplate = singleFileTemplate || '[locale].js';
64
- const combinedFilename = constructFullPath(singleTemplate, true);
80
+ const combinedFilename = resolveAssetPath(singleTemplate, 'single');
65
81
 
66
82
  if (combinedFilename) {
67
83
  newI18nAssetUrlsToAdd.push(combinedFilename);
68
84
  }
69
85
  } else {
70
- // Add both numeric and dynamic files
71
- const numericFilename = constructFullPath(numericFilenameTemplate);
86
+ const numericFilename = resolveAssetPath(numericFilenameTemplate, 'numeric');
72
87
 
73
88
  if (numericFilename) {
74
89
  newI18nAssetUrlsToAdd.push(numericFilename);
75
90
  }
76
91
 
77
- const dynamicFilename = constructFullPath(dynamicFilenameTemplate);
92
+ const dynamicFilename = resolveAssetPath(dynamicFilenameTemplate, 'dynamic');
78
93
 
79
94
  if (dynamicFilename) {
80
95
  newI18nAssetUrlsToAdd.push(dynamicFilename);
@@ -86,7 +101,7 @@ class I18nNumericIndexHtmlInjectorPlugin {
86
101
  assets.js = [...newI18nAssetUrlsToAdd, ...assets.js];
87
102
  }
88
103
 
89
- cb(null, hookData);
104
+ return cb(null, hookData);
90
105
  });
91
106
  });
92
107
  }