@zohodesk/client_build_tool 0.0.11-exp.25.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.
@@ -18,30 +18,26 @@ class I18nGroupRuntimeModule extends _webpack.RuntimeModule {
18
18
  generate() {
19
19
  const {
20
20
  customGroups,
21
+ chunkIdToGroupMapping,
21
22
  localeVarName,
22
- jsonpFunc,
23
- i18nPublicPathVar
24
- } = this.options; // Build chunk-to-group mapping from config
25
-
26
- const chunkToGroup = {};
27
- Object.entries(customGroups || {}).forEach(([groupName, config]) => {
28
- const chunkNames = config.chunks || [];
29
- chunkNames.forEach(chunkName => {
30
- chunkToGroup[chunkName] = groupName;
31
- });
32
- });
23
+ i18nPublicPathVar,
24
+ groupAssetUrls,
25
+ includeContentHash
26
+ } = this.options;
27
+ const chunkIdToGroup = chunkIdToGroupMapping || {};
28
+ const hasGroupAssetOverrides = !!groupAssetUrls && Object.values(groupAssetUrls).some(localeMap => localeMap && Object.keys(localeMap).length > 0);
29
+ const hashAware = includeContentHash || hasGroupAssetOverrides;
33
30
  return `
34
31
  // I18n Group Loading Runtime
35
32
  (function() {
36
33
  var loadedGroups = {};
37
- var chunkNameToGroup = ${JSON.stringify(chunkToGroup)};
34
+ var chunkIdToGroup = ${JSON.stringify(chunkIdToGroup)};
35
+ var groupAssetUrls = ${JSON.stringify(groupAssetUrls || {})};
36
+ var hashAware = ${hashAware ? 'true' : 'false'};
38
37
  var cachedI18nBase;
39
- var loadReasonKey = '__i18nGroupLoadReason';
40
38
 
41
39
  function ensureTrailingSlash(path) {
42
- if (!path) {
43
- return '';
44
- }
40
+ if (!path) return '';
45
41
  return path.charAt(path.length - 1) === '/' ? path : path + '/';
46
42
  }
47
43
 
@@ -52,39 +48,11 @@ class I18nGroupRuntimeModule extends _webpack.RuntimeModule {
52
48
  return candidate;
53
49
  }
54
50
  } catch (err) {
55
- // ignore – fall back to DOM/publicPath detection
51
+ // Fall back to DOM/publicPath detection
56
52
  }
57
53
  return '';
58
54
  }
59
55
 
60
- function withLoadReason(reason, fn) {
61
- var previous = __webpack_require__[loadReasonKey];
62
- __webpack_require__[loadReasonKey] = reason;
63
- try {
64
- return fn();
65
- } finally {
66
- __webpack_require__[loadReasonKey] = previous;
67
- }
68
- }
69
-
70
- if (typeof __webpack_require__.pfc === 'function') {
71
- var originalPrefetchChunk = __webpack_require__.pfc;
72
- __webpack_require__.pfc = function(prefetchChunkId) {
73
- return withLoadReason('prefetch', function() {
74
- return originalPrefetchChunk(prefetchChunkId);
75
- });
76
- };
77
- }
78
-
79
- if (typeof __webpack_require__.plc === 'function') {
80
- var originalPreloadChunk = __webpack_require__.plc;
81
- __webpack_require__.plc = function(preloadChunkId) {
82
- return withLoadReason('preload', function() {
83
- return originalPreloadChunk(preloadChunkId);
84
- });
85
- };
86
- }
87
-
88
56
  function resolveI18nBase() {
89
57
  if (cachedI18nBase !== undefined) {
90
58
  return cachedI18nBase;
@@ -112,24 +80,18 @@ class I18nGroupRuntimeModule extends _webpack.RuntimeModule {
112
80
  }
113
81
 
114
82
  cachedI18nBase = ensureTrailingSlash(base);
115
- if (typeof console !== 'undefined' && console.log) {
116
- console.log('[i18n-group:base]', cachedI18nBase || '<empty>');
117
- }
118
83
  return cachedI18nBase;
119
84
  }
120
85
 
121
86
  function buildI18nUrl(relativePath) {
122
87
  var base = resolveI18nBase();
123
- if (!relativePath) {
124
- return base;
125
- }
88
+ if (!relativePath) return base;
126
89
  if (relativePath.charAt(0) === '/') {
127
90
  relativePath = relativePath.slice(1);
128
91
  }
129
92
  return base + relativePath;
130
93
  }
131
-
132
- // Function to load i18n group
94
+
133
95
  function loadI18nGroup(groupName, loadReason) {
134
96
  if (loadedGroups[groupName]) {
135
97
  return Promise.resolve();
@@ -148,167 +110,139 @@ class I18nGroupRuntimeModule extends _webpack.RuntimeModule {
148
110
  (loadReason === 'preload' && config.preload === false);
149
111
 
150
112
  if (shouldSkip) {
151
- if (typeof console !== 'undefined' && console.log) {
152
- console.log('[i18n-group:skip]', groupName, loadReason, 'prefetch/preload disabled');
153
- }
154
113
  return Promise.resolve();
155
114
  }
156
115
 
157
116
  return new Promise(function(resolve, reject) {
158
- var relativePath = config.filenameTemplate
159
- .replace('[locale]', locale);
160
- var i18nUrl = buildI18nUrl(relativePath);
117
+ var relativePath = config.filenameTemplate.replace('[locale]', locale);
161
118
 
162
- if (typeof console !== 'undefined' && console.log) {
163
- console.log('[i18n-group:load]', groupName, loadReason || 'require', i18nUrl);
119
+ if (hashAware && groupAssetUrls[groupName] && groupAssetUrls[groupName][locale]) {
120
+ relativePath = groupAssetUrls[groupName][locale];
164
121
  }
165
122
 
123
+ var i18nUrl = buildI18nUrl(relativePath);
124
+
166
125
  var script = document.createElement('script');
167
126
  script.src = i18nUrl;
168
127
  script.async = true;
169
-
128
+
170
129
  script.onload = function() {
171
130
  loadedGroups[groupName] = true;
172
- if (typeof console !== 'undefined' && console.log) {
173
- console.log('[i18n-group:loaded]', groupName, loadReason || 'require', i18nUrl);
174
- }
131
+ console.log('[i18n-group:loaded]', groupName, i18nUrl);
175
132
  resolve();
176
133
  };
177
134
 
178
135
  script.onerror = function() {
179
- if (typeof console !== 'undefined' && console.error) {
180
- console.error('[i18n-group:error]', groupName, i18nUrl);
181
- }
136
+ console.error('[i18n-group:error]', groupName, i18nUrl);
182
137
  reject(new Error('Failed to load i18n group: ' + groupName));
183
138
  };
184
-
139
+
185
140
  document.head.appendChild(script);
186
141
  });
187
142
  }
188
143
 
189
144
  function findGroupByChunkId(chunkId) {
190
- var directGroup = chunkNameToGroup[chunkId];
191
- if (directGroup) {
192
- return directGroup;
193
- }
194
-
195
- var stringId = '' + chunkId;
196
- if (chunkNameToGroup[stringId]) {
197
- return chunkNameToGroup[stringId];
198
- }
199
-
200
- return undefined;
145
+ return chunkIdToGroup[chunkId];
201
146
  }
202
-
203
- // Store original webpack chunk loading function
147
+
204
148
  var originalEnsureChunk = __webpack_require__.e;
205
-
206
- // Override webpack's chunk loading if it exists
149
+
207
150
  if (originalEnsureChunk) {
208
151
  __webpack_require__.e = function(chunkId) {
209
- var promise = originalEnsureChunk.apply(this, arguments);
210
-
211
- // Check if this chunk needs an i18n group
152
+ var args = arguments;
153
+ var self = this;
212
154
  var groupName = findGroupByChunkId(chunkId);
155
+
213
156
  if (groupName && !loadedGroups[groupName]) {
214
- if (typeof console !== 'undefined' && console.log) {
215
- var chunkFilename = typeof __webpack_require__ !== 'undefined' && typeof __webpack_require__.u === 'function'
216
- ? __webpack_require__.u(chunkId)
217
- : '<no __webpack_require__.u>';
218
- console.log('[i18n-group:hook]', chunkId, '→', groupName, '| reason:', (__webpack_require__[loadReasonKey] || 'require'), '| file:', chunkFilename);
219
- }
220
- // Load the i18n group before the chunk
221
- var loadReason = __webpack_require__[loadReasonKey] || 'require';
222
- var i18nPromise = loadI18nGroup(groupName, loadReason);
223
- // Chain the promises so i18n loads first
224
- promise = Promise.all([promise, i18nPromise]).then(function(results) {
225
- return results[0]; // Return the original chunk promise result
157
+ var i18nPromise = loadI18nGroup(groupName, 'require');
158
+
159
+ return i18nPromise.then(function() {
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));
226
164
  });
165
+ } else {
166
+ return originalEnsureChunk.apply(this, arguments);
227
167
  }
228
-
229
- return promise;
230
168
  };
231
169
  }
232
-
233
- // Also check for webpackI18nGroup comments in dynamic imports
234
- function wrapLoadScript(originalLoadScript) {
235
- return function(url, done, key, chunkId) {
236
- var groupName = findGroupByChunkId(chunkId);
237
- if (groupName && !loadedGroups[groupName]) {
238
- var locale = ${localeVarName} || 'en_US';
239
- var groupConfig = ${JSON.stringify(customGroups)};
240
- var config = groupConfig[groupName];
241
170
 
242
- if (config) {
243
- var relativePath = config.filenameTemplate
244
- .replace('[locale]', locale);
245
- var i18nUrl = buildI18nUrl(relativePath);
171
+ // Simple URL-based detection for setup pages
172
+ function detectGroupFromUrl() {
173
+ if (typeof window === 'undefined') return null;
246
174
 
247
- if (config.prefetch === false) {
248
- if (typeof console !== 'undefined' && console.log) {
249
- console.log('[i18n-group:skip-prefetch-loader]', groupName, chunkId, i18nUrl);
250
- }
251
- return originalLoadScript.call(__webpack_require__, url, done, key, chunkId);
252
- }
175
+ var url = window.location.href;
176
+ var groupConfig = ${JSON.stringify(customGroups)};
253
177
 
254
- var i18nScript = document.createElement('script');
255
- i18nScript.src = i18nUrl;
256
- i18nScript.onload = function() {
257
- loadedGroups[groupName] = true;
258
- originalLoadScript.call(__webpack_require__, url, done, key, chunkId);
259
- };
260
- i18nScript.onerror = function() {
261
- if (typeof console !== 'undefined' && console.error) {
262
- console.error('[i18n-group:error]', groupName, i18nUrl);
263
- }
264
- originalLoadScript.call(__webpack_require__, url, done, key, chunkId);
265
- };
266
- document.head.appendChild(i18nScript);
267
- 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
+ }
268
186
  }
269
187
  }
188
+ }
270
189
 
271
- return originalLoadScript.call(__webpack_require__, url, done, key, chunkId);
272
- };
190
+ return null;
273
191
  }
274
192
 
275
- function installLoadScriptHook(loadScript) {
276
- if (typeof loadScript !== 'function') {
277
- Object.defineProperty(__webpack_require__, 'l', {
278
- configurable: true,
279
- enumerable: true,
280
- get: function() {
281
- return loadScript;
282
- },
283
- set: function(newLoader) {
284
- installLoadScriptHook(newLoader);
285
- }
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);
286
199
  });
287
- return;
288
200
  }
289
-
290
- Object.defineProperty(__webpack_require__, 'l', {
291
- configurable: true,
292
- enumerable: true,
293
- writable: true,
294
- value: wrapLoadScript(loadScript)
295
- });
296
201
  }
297
202
 
298
- if (typeof __webpack_require__.l === 'function') {
299
- installLoadScriptHook(__webpack_require__.l);
300
- } else {
301
- Object.defineProperty(__webpack_require__, 'l', {
302
- configurable: true,
303
- enumerable: true,
304
- set: function(newLoader) {
305
- installLoadScriptHook(newLoader);
306
- },
307
- get: function() {
308
- 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
+ });
309
217
  }
310
- });
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();
311
242
  }
243
+
244
+ // Initialize loading mechanisms
245
+ initializeGroupLoading();
312
246
  })();
313
247
  `;
314
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
  }