@zohodesk/client_build_tool 0.0.11-exp.27.0 → 0.0.11-exp.28.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.
@@ -0,0 +1,144 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.validateI18nChunkSplitOptions = validateI18nChunkSplitOptions;
7
+ exports.validateI18nIndexingOptions = validateI18nIndexingOptions;
8
+ exports.validateI18nOptions = validateI18nOptions;
9
+ exports.validatePathSecurity = validatePathSecurity;
10
+ exports.validateRequiredFields = validateRequiredFields;
11
+ exports.validateTemplateFormat = validateTemplateFormat;
12
+ 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
+ }
24
+
25
+ function validateRequiredFields(options, requiredFields) {
26
+ const missingOptions = requiredFields.filter(opt => {
27
+ const value = options[opt];
28
+ return value === undefined || value === null || typeof value === 'string' && value.trim() === '';
29
+ });
30
+
31
+ if (missingOptions.length > 0) {
32
+ throw new Error(`[I18nOptionsValidator] Missing required i18n options: ${missingOptions.join(', ')}`);
33
+ }
34
+ }
35
+
36
+ function validateTemplateFormat(template, templateName) {
37
+ if (!template || typeof template !== 'string') {
38
+ throw new Error(`[I18nOptionsValidator] ${templateName} must be a non-empty string`);
39
+ }
40
+
41
+ if (!template.includes(LOCALE_PLACEHOLDER)) {
42
+ throw new Error(`[I18nOptionsValidator] ${templateName} must include ${LOCALE_PLACEHOLDER} placeholder`);
43
+ }
44
+
45
+ validatePathSecurity(template, templateName);
46
+ }
47
+
48
+ function validateI18nIndexingOptions(i18nOpts) {
49
+ if (!i18nOpts || typeof i18nOpts !== 'object') {
50
+ throw new Error('[I18nOptionsValidator] i18nIndexing configuration must be a valid object');
51
+ }
52
+
53
+ if (!i18nOpts.enable) {
54
+ return {
55
+ skipValidation: true
56
+ };
57
+ }
58
+
59
+ validateRequiredFields(i18nOpts, REQUIRED_OPTIONS);
60
+ validatePathSecurity(i18nOpts.jsResourcePath, 'jsResourcePath');
61
+ validatePathSecurity(i18nOpts.propertiesFolderPath, 'propertiesFolderPath');
62
+ validatePathSecurity(i18nOpts.numericMapPath, 'numericMapPath');
63
+ const numericTemplate = i18nOpts.numericFilenameTemplate || '[locale]/numeric.i18n.js';
64
+ const dynamicTemplate = i18nOpts.dynamicFilenameTemplate || '[locale]/dynamic.i18n.js';
65
+ validateTemplateFormat(numericTemplate, 'numericFilenameTemplate');
66
+ validateTemplateFormat(dynamicTemplate, 'dynamicFilenameTemplate');
67
+
68
+ if (i18nOpts.singleFileTemplate) {
69
+ validateTemplateFormat(i18nOpts.singleFileTemplate, 'singleFileTemplate');
70
+ }
71
+
72
+ if (i18nOpts.outputFolder) {
73
+ validatePathSecurity(i18nOpts.outputFolder, 'outputFolder');
74
+ } // Validate conflicting options
75
+
76
+
77
+ const includeContentHash = i18nOpts.includeContentHash;
78
+ const injectHtml = i18nOpts.injectI18nUrlInIndex !== undefined ? i18nOpts.injectI18nUrlInIndex : true;
79
+
80
+ if (includeContentHash && injectHtml) {
81
+ throw new Error('[I18nOptionsValidator] includeContentHash and injectI18nUrlInIndex cannot both be true - content hash changes filenames dynamically but HTML injection needs static filenames');
82
+ }
83
+
84
+ return {
85
+ skipValidation: false
86
+ };
87
+ }
88
+
89
+ function validateI18nChunkSplitOptions(chunkSplitOpts) {
90
+ if (!chunkSplitOpts || typeof chunkSplitOpts !== 'object') {
91
+ throw new Error('[I18nOptionsValidator] i18nChunkSplit configuration must be a valid object');
92
+ }
93
+
94
+ if (!chunkSplitOpts.chunkSplitEnable || !chunkSplitOpts.useNumericIndexing) {
95
+ return {
96
+ skipValidation: true
97
+ };
98
+ }
99
+
100
+ if (!chunkSplitOpts.jsResource) {
101
+ throw new Error('[I18nOptionsValidator] Missing required jsResource in i18nChunkSplit options');
102
+ }
103
+
104
+ if (!chunkSplitOpts.numericMapPath) {
105
+ throw new Error('[I18nOptionsValidator] Missing required numericMapPath in i18nChunkSplit options');
106
+ }
107
+
108
+ validatePathSecurity(chunkSplitOpts.jsResource, 'jsResource');
109
+ validatePathSecurity(chunkSplitOpts.numericMapPath, 'numericMapPath');
110
+ return {
111
+ skipValidation: false
112
+ };
113
+ }
114
+
115
+ function validateI18nOptions(options) {
116
+ if (!options || typeof options !== 'object') {
117
+ throw new Error('[I18nOptionsValidator] Options must be a valid object');
118
+ }
119
+
120
+ const hasIndexing = options.i18nIndexing && options.i18nIndexing.enable;
121
+ const hasChunkSplit = options.i18nChunkSplit && options.i18nChunkSplit.chunkSplitEnable && options.i18nChunkSplit.useNumericIndexing;
122
+
123
+ if (!hasIndexing && !hasChunkSplit) {
124
+ throw new Error('[I18nOptionsValidator] i18n validation requires either i18nIndexing to be enabled or i18nChunkSplit with useNumericIndexing');
125
+ }
126
+
127
+ if (hasIndexing) {
128
+ const result = validateI18nIndexingOptions(options.i18nIndexing);
129
+
130
+ if (result.skipValidation) {
131
+ throw new Error('[I18nOptionsValidator] i18nIndexing is not properly enabled');
132
+ }
133
+ }
134
+
135
+ if (hasChunkSplit) {
136
+ const result = validateI18nChunkSplitOptions(options.i18nChunkSplit);
137
+
138
+ if (result.skipValidation) {
139
+ throw new Error('[I18nOptionsValidator] i18nChunkSplit is not properly configured');
140
+ }
141
+ }
142
+
143
+ return true;
144
+ }
@@ -22,11 +22,13 @@ class I18nGroupRuntimeModule extends _webpack.RuntimeModule {
22
22
  localeVarName,
23
23
  i18nPublicPathVar,
24
24
  groupAssetUrls,
25
- includeContentHash
25
+ includeContentHash,
26
+ mode
26
27
  } = this.options;
27
28
  const chunkIdToGroup = chunkIdToGroupMapping || {};
28
29
  const hasGroupAssetOverrides = !!groupAssetUrls && Object.values(groupAssetUrls).some(localeMap => localeMap && Object.keys(localeMap).length > 0);
29
30
  const hashAware = includeContentHash || hasGroupAssetOverrides;
31
+ const isDevelopment = mode === 'development';
30
32
  return `
31
33
  // I18n Group Loading Runtime
32
34
  (function() {
@@ -34,7 +36,9 @@ class I18nGroupRuntimeModule extends _webpack.RuntimeModule {
34
36
  var chunkIdToGroup = ${JSON.stringify(chunkIdToGroup)};
35
37
  var groupAssetUrls = ${JSON.stringify(groupAssetUrls || {})};
36
38
  var hashAware = ${hashAware ? 'true' : 'false'};
39
+ var isDev = ${isDevelopment ? 'true' : 'false'};
37
40
  var cachedI18nBase;
41
+ var scriptCache = null;
38
42
 
39
43
  function ensureTrailingSlash(path) {
40
44
  if (!path) return '';
@@ -61,9 +65,11 @@ class I18nGroupRuntimeModule extends _webpack.RuntimeModule {
61
65
  var base = resolveConfiguredBase();
62
66
 
63
67
  if (!base && typeof document !== 'undefined') {
64
- var scripts = document.getElementsByTagName('script');
65
- for (var i = 0; i < scripts.length; i++) {
66
- var src = scripts[i].getAttribute('data-src') || scripts[i].getAttribute('src') || '';
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') || '';
67
73
  var markerIndex = src.indexOf('i18n-chunk/');
68
74
  if (markerIndex !== -1) {
69
75
  base = src.slice(0, markerIndex);
@@ -126,15 +132,28 @@ class I18nGroupRuntimeModule extends _webpack.RuntimeModule {
126
132
  script.src = i18nUrl;
127
133
  script.async = true;
128
134
 
135
+ var cleanup = function() {
136
+ if (script.parentNode) {
137
+ script.parentNode.removeChild(script);
138
+ }
139
+ };
140
+
129
141
  script.onload = function() {
130
142
  loadedGroups[groupName] = true;
131
- console.log('[i18n-group:loaded]', groupName, i18nUrl);
143
+ if (isDev) {
144
+ console.log('[i18n-group:loaded]', groupName, i18nUrl);
145
+ }
146
+ cleanup();
132
147
  resolve();
133
148
  };
134
149
 
135
150
  script.onerror = function() {
136
- console.error('[i18n-group:error]', groupName, i18nUrl);
137
- reject(new Error('Failed to load i18n group: ' + groupName));
151
+ if (isDev) {
152
+ console.error('[i18n-group:error]', groupName, i18nUrl);
153
+ }
154
+ cleanup();
155
+ loadedGroups[groupName] = true;
156
+ resolve();
138
157
  };
139
158
 
140
159
  document.head.appendChild(script);
@@ -159,8 +178,10 @@ class I18nGroupRuntimeModule extends _webpack.RuntimeModule {
159
178
  return i18nPromise.then(function() {
160
179
  return originalEnsureChunk.apply(self, args);
161
180
  }).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));
181
+ if (isDev) {
182
+ console.error('[i18n-group:critical] i18n failed, proceeding without:', err);
183
+ }
184
+ return originalEnsureChunk.apply(self, args);
164
185
  });
165
186
  } else {
166
187
  return originalEnsureChunk.apply(this, arguments);
@@ -180,7 +201,7 @@ class I18nGroupRuntimeModule extends _webpack.RuntimeModule {
180
201
  if (config.urlPatterns && Array.isArray(config.urlPatterns)) {
181
202
  for (var i = 0; i < config.urlPatterns.length; i++) {
182
203
  var pattern = config.urlPatterns[i];
183
- if (url.indexOf(pattern) !== -1) {
204
+ if (url.includes(pattern)) {
184
205
  return groupName;
185
206
  }
186
207
  }
@@ -191,12 +212,22 @@ class I18nGroupRuntimeModule extends _webpack.RuntimeModule {
191
212
  }
192
213
 
193
214
  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);
199
- });
215
+ try {
216
+ var detectedGroup = detectGroupFromUrl();
217
+ if (detectedGroup && !loadedGroups[detectedGroup]) {
218
+ if (isDev) {
219
+ console.log('[i18n-group:url-detect]', detectedGroup, 'loading for current URL');
220
+ }
221
+ loadI18nGroup(detectedGroup, 'url-detection').catch(function(err) {
222
+ if (isDev) {
223
+ console.error('[i18n-group:url-detect] Failed to load', detectedGroup, ':', err);
224
+ }
225
+ });
226
+ }
227
+ } catch (err) {
228
+ if (isDev) {
229
+ console.error('[i18n-group:url-detect] Error in URL detection:', err);
230
+ }
200
231
  }
201
232
  }
202
233
 
@@ -210,9 +241,13 @@ class I18nGroupRuntimeModule extends _webpack.RuntimeModule {
210
241
  for (var groupName in groupConfig) {
211
242
  var config = groupConfig[groupName];
212
243
  if (config.preload === true && !loadedGroups[groupName]) {
213
- console.log('[i18n-group:preload]', groupName, 'loading immediately');
244
+ if (isDev) {
245
+ console.log('[i18n-group:preload]', groupName, 'loading immediately');
246
+ }
214
247
  loadI18nGroup(groupName, 'preload').catch(function(err) {
215
- console.error('[i18n-group:preload] Failed to preload', groupName, ':', err);
248
+ if (isDev) {
249
+ console.error('[i18n-group:preload] Failed to preload', groupName, ':', err);
250
+ }
216
251
  });
217
252
  }
218
253
  }
@@ -222,9 +257,13 @@ class I18nGroupRuntimeModule extends _webpack.RuntimeModule {
222
257
  for (var groupName in groupConfig) {
223
258
  var config = groupConfig[groupName];
224
259
  if (config.prefetch === true && !loadedGroups[groupName] && config.preload !== true) {
225
- console.log('[i18n-group:prefetch]', groupName, 'scheduling background load');
260
+ if (isDev) {
261
+ console.log('[i18n-group:prefetch]', groupName, 'scheduling background load');
262
+ }
226
263
  loadI18nGroup(groupName, 'prefetch').catch(function(err) {
227
- console.error('[i18n-group:prefetch] Failed to prefetch', groupName, ':', err);
264
+ if (isDev) {
265
+ console.error('[i18n-group:prefetch] Failed to prefetch', groupName, ':', err);
266
+ }
228
267
  });
229
268
  }
230
269
  }
@@ -1,8 +1,15 @@
1
1
  "use strict";
2
2
 
3
- const HtmlWebpackPlugin = require('html-webpack-plugin');
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.I18nNumericIndexHtmlInjectorPlugin = void 0;
4
7
 
5
- const path = require('path');
8
+ var _htmlWebpackPlugin = _interopRequireDefault(require("html-webpack-plugin"));
9
+
10
+ var _path = _interopRequireDefault(require("path"));
11
+
12
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
6
13
 
7
14
  const pluginName = 'I18nNumericIndexHtmlInjectorPlugin';
8
15
  const assetStoreKey = Symbol.for('I18nNumericIndexPluginAssets');
@@ -17,8 +24,7 @@ class I18nNumericIndexHtmlInjectorPlugin {
17
24
 
18
25
  apply(compiler) {
19
26
  compiler.hooks.thisCompilation.tap(pluginName, compilation => {
20
- HtmlWebpackPlugin.getHooks(compilation).beforeAssetTagGeneration.tapAsync(pluginName, (hookData, cb) => {
21
- // Skip HTML injection if disabled
27
+ _htmlWebpackPlugin.default.getHooks(compilation).beforeAssetTagGeneration.tapAsync(pluginName, (hookData, cb) => {
22
28
  if (!this.options.injectI18nUrlInIndex) {
23
29
  return cb(null, hookData);
24
30
  }
@@ -37,30 +43,26 @@ class I18nNumericIndexHtmlInjectorPlugin {
37
43
  } = this.options;
38
44
  const newI18nAssetUrlsToAdd = [];
39
45
  const emittedAssetNames = compilation.getAssets().map(asset => asset.name);
40
- const recordedAssets = compilation[assetStoreKey] || []; // Simple asset resolution using recorded assets first, then fallback to template
46
+ const recordedAssets = compilation[assetStoreKey] || [];
41
47
 
42
48
  const resolveAssetPath = (template, fileType) => {
43
- if (!template) return null; // Try to find from recorded assets first
44
-
49
+ if (!template) return null;
45
50
  const recorded = recordedAssets.find(asset => {
46
51
  if (fileType === 'single') return !asset.fileType;
47
52
  return asset.fileType === fileType;
48
53
  });
49
54
 
50
55
  if (recorded) {
51
- // Use recorded asset with correct locale
52
56
  const assetPath = recorded.outputPath.replace(recorded.locale, htmlTemplateLabel);
53
57
  return i18nAssetsPublicPathPrefix + assetPath;
54
- } // Fallback to template-based construction
55
-
58
+ }
56
59
 
57
60
  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
61
+ const fullPath = filePath.includes(outputFolder) || filePath.startsWith('/') ? filePath : _path.default.join(outputFolder, filePath);
59
62
 
60
63
  if (emittedAssetNames.includes(fullPath)) {
61
64
  return i18nAssetsPublicPathPrefix + fullPath;
62
- } // For content hash templates, find matching asset
63
-
65
+ }
64
66
 
65
67
  if (fullPath.includes('[contenthash]')) {
66
68
  const pattern = fullPath.replace('[contenthash]', '*');
@@ -69,15 +71,13 @@ class I18nNumericIndexHtmlInjectorPlugin {
69
71
  if (matchingAsset) {
70
72
  return i18nAssetsPublicPathPrefix + matchingAsset;
71
73
  }
72
- } // Return as-is for basic cases
73
-
74
+ }
74
75
 
75
76
  return i18nAssetsPublicPathPrefix + fullPath;
76
77
  };
77
78
 
78
79
  if (singleFile) {
79
- const singleTemplate = singleFileTemplate || '[locale].js';
80
- const combinedFilename = resolveAssetPath(singleTemplate, 'single');
80
+ const combinedFilename = resolveAssetPath(singleFileTemplate, 'single');
81
81
 
82
82
  if (combinedFilename) {
83
83
  newI18nAssetUrlsToAdd.push(combinedFilename);
@@ -97,7 +97,6 @@ class I18nNumericIndexHtmlInjectorPlugin {
97
97
  }
98
98
 
99
99
  if (newI18nAssetUrlsToAdd.length > 0) {
100
- // Add i18n assets to the beginning of JS assets for early loading
101
100
  assets.js = [...newI18nAssetUrlsToAdd, ...assets.js];
102
101
  }
103
102
 
@@ -108,6 +107,4 @@ class I18nNumericIndexHtmlInjectorPlugin {
108
107
 
109
108
  }
110
109
 
111
- module.exports = {
112
- I18nNumericIndexHtmlInjectorPlugin
113
- };
110
+ exports.I18nNumericIndexHtmlInjectorPlugin = I18nNumericIndexHtmlInjectorPlugin;
@@ -15,7 +15,7 @@ var _propertiesUtils = require("../I18nSplitPlugin/utils/propertiesUtils");
15
15
 
16
16
  var _I18nGroupRuntimeModule = require("./I18nGroupRuntimeModule");
17
17
 
18
- var _i18nDataLoader = require("./utils/i18nDataLoader");
18
+ var _i18nDataLoader = require("./utils/i18nDataLoader.js");
19
19
 
20
20
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
21
21
 
@@ -1,21 +1,24 @@
1
1
  "use strict";
2
2
 
3
- const fs = require('fs');
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.loadAllLocaleFiles = loadAllLocaleFiles;
7
+ exports.loadI18nData = loadI18nData;
8
+ exports.loadNumericMap = loadNumericMap;
9
+ exports.loadPropertiesFile = loadPropertiesFile;
4
10
 
5
- const path = require('path');
11
+ var _fs = _interopRequireDefault(require("fs"));
6
12
 
7
- const {
8
- getPropertiesAsJSON
9
- } = require('../../I18nSplitPlugin/utils/propertiesUtils');
10
- /**
11
- * Load and parse a properties file
12
- */
13
+ var _path = _interopRequireDefault(require("path"));
13
14
 
15
+ var _propertiesUtils = require("../../I18nSplitPlugin/utils/propertiesUtils.js");
16
+
17
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
14
18
 
15
19
  function loadPropertiesFile(filePath, compilation, description) {
16
20
  try {
17
- const parsed = getPropertiesAsJSON(filePath);
18
- return parsed;
21
+ return (0, _propertiesUtils.getPropertiesAsJSON)(filePath);
19
22
  } catch (err) {
20
23
  if (compilation) {
21
24
  compilation.errors.push(new Error(`I18nNumericIndexPlugin: Error loading ${description}: ${err.message}`));
@@ -24,47 +27,16 @@ function loadPropertiesFile(filePath, compilation, description) {
24
27
  return {};
25
28
  }
26
29
  }
27
- /**
28
- * Load numeric mapping from JSON file
29
- */
30
-
31
30
 
32
31
  function loadNumericMap(numericMapPath, compilation) {
33
32
  try {
34
- const fileContent = fs.readFileSync(numericMapPath, 'utf-8');
35
- const parsedData = JSON.parse(fileContent);
36
- let numericMap;
37
- let totalKeys; // Handle both wrapped and flat formats
38
-
39
- if (parsedData.originalKeyToNumericId) {
40
- // New format with metadata
41
- numericMap = parsedData.originalKeyToNumericId;
42
- totalKeys = parsedData.totalKeysInMap || Object.keys(numericMap).length;
43
- } else {
44
- // Flat format - use directly
45
- numericMap = parsedData;
46
- totalKeys = Object.keys(numericMap).length;
47
- } // Create sorted array for numeric ID lookups
48
-
33
+ const fileContent = _fs.default.readFileSync(numericMapPath, 'utf-8');
49
34
 
35
+ const parsedData = JSON.parse(fileContent);
36
+ const numericMap = parsedData.originalKeyToNumericId || parsedData;
37
+ const totalKeys = parsedData.totalKeysInMap || Object.keys(numericMap).length;
50
38
  const values = Object.values(numericMap);
51
-
52
- if (values.length === 0) {
53
- throw new Error('numeric map is empty');
54
- }
55
-
56
- let maxId = 0;
57
- values.forEach(id => {
58
- const numericId = typeof id === 'number' ? id : Number(id);
59
-
60
- if (Number.isNaN(numericId)) {
61
- throw new Error(`invalid numeric map entry value: ${id}`);
62
- }
63
-
64
- if (numericId > maxId) {
65
- maxId = numericId;
66
- }
67
- });
39
+ const maxId = Math.max(...values.map(id => typeof id === 'number' ? id : Number(id)));
68
40
  const sortedKeys = new Array(maxId + 1);
69
41
  Object.entries(numericMap).forEach(([key, id]) => {
70
42
  sortedKeys[id] = key;
@@ -84,30 +56,26 @@ function loadNumericMap(numericMapPath, compilation) {
84
56
  };
85
57
  }
86
58
  }
87
- /**
88
- * Load all locale files from properties directory
89
- */
90
-
91
59
 
92
60
  function loadAllLocaleFiles(propertiesPath, compilation, jsResourceBase) {
93
- const allI18n = {};
94
- const locales = []; // Start with English base
95
-
96
- allI18n['en_US'] = jsResourceBase;
97
- locales.push('en_US');
61
+ const allI18n = {
62
+ en_US: jsResourceBase
63
+ };
64
+ const locales = ['en_US'];
98
65
 
99
66
  try {
100
- const files = fs.readdirSync(propertiesPath);
101
- files.forEach(file => {
102
- if (!file.endsWith('.properties')) return; // Match locale-specific property files
67
+ const files = _fs.default.readdirSync(propertiesPath);
103
68
 
69
+ files.forEach(file => {
70
+ if (!file.endsWith('.properties')) return;
104
71
  const match = file.match(/^ApplicationResources_([a-z]{2}_[A-Z]{2})\.properties$/);
105
72
 
106
73
  if (match) {
107
74
  const locale = match[1];
108
- const filePath = path.join(propertiesPath, file);
109
- const localeData = loadPropertiesFile(filePath, compilation, `locale ${locale}`); // Merge with base resources
110
75
 
76
+ const filePath = _path.default.join(propertiesPath, file);
77
+
78
+ const localeData = loadPropertiesFile(filePath, compilation, `locale ${locale}`);
111
79
  allI18n[locale] = { ...jsResourceBase,
112
80
  ...localeData
113
81
  };
@@ -128,17 +96,13 @@ function loadAllLocaleFiles(propertiesPath, compilation, jsResourceBase) {
128
96
  locales
129
97
  };
130
98
  }
131
- /**
132
- * Main loader function for i18n data
133
- */
134
-
135
99
 
136
100
  function loadI18nData(options, compilation) {
137
- const jsResourcePath = path.resolve(compilation.compiler.context, options.jsResourcePath);
138
- const propertiesPath = path.resolve(compilation.compiler.context, options.propertiesFolderPath); // Load base JS resources
101
+ const jsResourcePath = _path.default.resolve(compilation.compiler.context, options.jsResourcePath);
139
102
 
140
- const jsResourceBase = loadPropertiesFile(jsResourcePath, compilation, 'JS resources'); // Load all locale files
103
+ const propertiesPath = _path.default.resolve(compilation.compiler.context, options.propertiesFolderPath);
141
104
 
105
+ const jsResourceBase = loadPropertiesFile(jsResourcePath, compilation, 'JS resources');
142
106
  const {
143
107
  allI18n,
144
108
  locales
@@ -148,11 +112,4 @@ function loadI18nData(options, compilation) {
148
112
  allI18n,
149
113
  locales
150
114
  };
151
- }
152
-
153
- module.exports = {
154
- loadPropertiesFile,
155
- loadNumericMap,
156
- loadAllLocaleFiles,
157
- loadI18nData
158
- };
115
+ }
@@ -1,37 +1,32 @@
1
1
  "use strict";
2
2
 
3
- const {
4
- getPropertiesAsJSON
5
- } = require('../custom_plugins/I18nSplitPlugin/utils/propertiesUtils');
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.i18nIdReplaceLoaderConfig = i18nIdReplaceLoaderConfig;
6
7
 
7
- const fs = require('fs');
8
+ var _propertiesUtils = require("../custom_plugins/I18nSplitPlugin/utils/propertiesUtils.js");
9
+
10
+ var _i18nOptionsValidator = require("../common/i18nOptionsValidator.js");
11
+
12
+ var _fs = _interopRequireDefault(require("fs"));
13
+
14
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
8
15
 
9
16
  function loadJSResourcesOnce(options) {
10
- let jsResourcePath; // Determine the JSResource path based on configuration
17
+ let jsResourcePath;
11
18
 
12
19
  if (options.i18nIndexing && options.i18nIndexing.enable) {
13
20
  jsResourcePath = options.i18nIndexing.jsResourcePath;
14
21
  } else if (options.i18nChunkSplit && options.i18nChunkSplit.chunkSplitEnable && options.i18nChunkSplit.useNumericIndexing) {
15
22
  jsResourcePath = options.i18nChunkSplit.jsResource;
16
- } else {
17
- throw new Error('i18nIdReplaceLoader requires either i18nIndexing to be enabled or i18nChunkSplit with useNumericIndexing');
18
- }
19
-
20
- if (!jsResourcePath) {
21
- throw new Error('Missing required jsResourcePath in i18n options');
22
23
  }
23
24
 
24
25
  try {
25
- const i18nData = getPropertiesAsJSON(jsResourcePath);
26
-
27
- if (Object.keys(i18nData).length === 0) {
28
- console.warn(`[i18nIdReplaceLoaderConfig] Warning: No i18n data found in JSResource file: ${jsResourcePath}`);
29
- return {};
30
- }
31
-
26
+ const i18nData = (0, _propertiesUtils.getPropertiesAsJSON)(jsResourcePath);
32
27
  return i18nData;
33
28
  } catch (err) {
34
- throw new Error(`Error reading JSResource file ${jsResourcePath}: ${err.message}`);
29
+ throw new Error(`[I18nIdReplaceLoaderConfig] Error reading JSResource file ${jsResourcePath}: ${err.message}`);
35
30
  }
36
31
  }
37
32
 
@@ -39,37 +34,35 @@ function readNumericMapOnce(numericMapPath) {
39
34
  if (!numericMapPath) return null;
40
35
 
41
36
  try {
42
- if (!fs.existsSync(numericMapPath)) return null;
43
- const fileContent = fs.readFileSync(numericMapPath, 'utf-8');
37
+ const fileContent = _fs.default.readFileSync(numericMapPath, 'utf-8');
38
+
44
39
  const parsed = JSON.parse(fileContent);
45
- if (!parsed) return null;
46
40
  return parsed.originalKeyToNumericId ? parsed.originalKeyToNumericId : parsed;
47
41
  } catch (err) {
48
- return null;
42
+ throw new Error(`[I18nIdReplaceLoaderConfig] Failed to read numeric map from ${numericMapPath}: ${err.message}`);
49
43
  }
50
44
  }
51
45
 
52
- function i18nIdReplaceLoaderConfig(options, webpackContext) {
53
- let numericMapPath; // Determine the numeric map path based on configuration
46
+ function i18nIdReplaceLoaderConfig(options) {
47
+ (0, _i18nOptionsValidator.validateI18nOptions)(options);
48
+ let numericMapPath;
54
49
 
55
50
  if (options.i18nIndexing && options.i18nIndexing.enable) {
56
51
  numericMapPath = options.i18nIndexing.numericMapPath;
57
52
  } else if (options.i18nChunkSplit && options.i18nChunkSplit.chunkSplitEnable && options.i18nChunkSplit.useNumericIndexing) {
58
53
  numericMapPath = options.i18nChunkSplit.numericMapPath;
59
- } else {
60
- throw new Error('i18nIdReplaceLoader requires either i18nIndexing to be enabled or i18nChunkSplit with useNumericIndexing');
61
54
  }
62
55
 
63
- if (!numericMapPath) {
64
- throw new Error('numericMapPath is required in i18nIndexing or i18nChunkSplit config');
65
- } // Load all i18n data for key detection
66
-
67
-
68
56
  const allI18nData = loadJSResourcesOnce(options);
57
+ const i18nKeyReplaceLoaderPath = new URL('../loaders/i18nIdReplaceLoader.js', import.meta.url).pathname;
58
+ let numericIdMap;
69
59
 
70
- const i18nKeyReplaceLoaderPath = require.resolve('../loaders/i18nIdReplaceLoader.js');
60
+ try {
61
+ numericIdMap = readNumericMapOnce(numericMapPath);
62
+ } catch (err) {
63
+ throw new Error(`[I18nIdReplaceLoaderConfig] Failed to load numeric map: ${err.message}`);
64
+ }
71
65
 
72
- const numericIdMap = readNumericMapOnce(numericMapPath);
73
66
  const loaderOptions = {
74
67
  allI18nData: allI18nData,
75
68
  sourceMaps: false,
@@ -83,8 +76,4 @@ function i18nIdReplaceLoaderConfig(options, webpackContext) {
83
76
  loader: i18nKeyReplaceLoaderPath,
84
77
  options: loaderOptions
85
78
  };
86
- }
87
-
88
- module.exports = {
89
- i18nIdReplaceLoaderConfig
90
- };
79
+ }
@@ -22,8 +22,8 @@ module.exports = function i18nIdReplaceLoader(source, map) {
22
22
  const options = getOptions(this) || {};
23
23
  const callback = this.async(); // Skip files in excluded paths
24
24
 
25
- if (options.excludePaths) {
26
- const shouldExclude = options.excludePaths.some(excludePath => resourcePath.includes(excludePath));
25
+ if (options.excludePaths && Array.isArray(options.excludePaths)) {
26
+ const shouldExclude = options.excludePaths.some(excludePath => typeof excludePath === 'string' && resourcePath.includes(excludePath));
27
27
 
28
28
  if (shouldExclude) {
29
29
  return callback(null, source, map);
@@ -31,18 +31,18 @@ module.exports = function i18nIdReplaceLoader(source, map) {
31
31
  } // Only process files in included paths if specified
32
32
 
33
33
 
34
- if (options.includePaths && options.includePaths.length > 0) {
35
- const shouldInclude = options.includePaths.some(includePath => resourcePath.includes(includePath));
34
+ if (options.includePaths && Array.isArray(options.includePaths) && options.includePaths.length > 0) {
35
+ const shouldInclude = options.includePaths.some(includePath => typeof includePath === 'string' && resourcePath.includes(includePath));
36
36
 
37
37
  if (!shouldInclude) {
38
38
  return callback(null, source, map);
39
39
  }
40
- } // Validate i18n data exists
40
+ } // Basic runtime validation - options should already be validated by config
41
41
 
42
42
 
43
43
  if (!options.allI18nData || Object.keys(options.allI18nData).length === 0) {
44
44
  return callback(new Error(`i18nIdReplaceLoader: 'allI18nData' option is missing or empty`));
45
- } // Load numeric ID mapping
45
+ } // Load numeric ID mapping - paths already validated by config
46
46
 
47
47
 
48
48
  let numericIdMap = options.numericIdMap;
@@ -57,7 +57,8 @@ module.exports = function i18nIdReplaceLoader(source, map) {
57
57
  const parsed = JSON.parse(mapContent);
58
58
  numericIdMap = parsed.originalKeyToNumericId || parsed;
59
59
  }
60
- } catch (e) {// Silently continue without numeric map
60
+ } catch (e) {
61
+ return callback(new Error(`i18nIdReplaceLoader: Failed to load numeric map from ${options.numericMapPath}: ${e.message}`));
61
62
  }
62
63
  } // If no numeric map available, return source as-is
63
64
 
@@ -70,10 +71,11 @@ module.exports = function i18nIdReplaceLoader(source, map) {
70
71
  // Parse the JavaScript/TypeScript source code
71
72
  const ast = parser.parse(source, {
72
73
  sourceType: 'module',
73
- plugins: ['jsx', 'typescript'],
74
+ plugins: ['jsx', 'typescript', 'classProperties', 'optionalChaining', 'nullishCoalescingOperator'],
74
75
  sourceFilename: resourcePath
75
76
  });
76
- let hasTransformations = false; // Traverse AST and replace i18n keys with numeric IDs
77
+ let hasTransformations = false;
78
+ let transformationCount = 0; // Traverse AST and replace i18n keys with numeric IDs
77
79
 
78
80
  traverse(ast, {
79
81
  StringLiteral(path) {
@@ -85,6 +87,7 @@ module.exports = function i18nIdReplaceLoader(source, map) {
85
87
  const numericId = String(numericIdMap[node.value]);
86
88
  path.replaceWith(t.stringLiteral(numericId));
87
89
  hasTransformations = true;
90
+ transformationCount++;
88
91
  }
89
92
  }
90
93
 
@@ -102,6 +105,6 @@ module.exports = function i18nIdReplaceLoader(source, map) {
102
105
  callback(null, source, map);
103
106
  }
104
107
  } catch (err) {
105
- callback(err);
108
+ callback(new Error(`i18nIdReplaceLoader: Failed to process ${resourcePath}: ${err.message}`));
106
109
  }
107
110
  };
@@ -11,17 +11,26 @@ var _I18nNumericIndexHtmlInjectorPlugin = require("../custom_plugins/I18nNumeric
11
11
 
12
12
  var _readI18nValues = require("../custom_plugins/I18nSplitPlugin/readI18nValues");
13
13
 
14
+ var _i18nOptionsValidator = require("../common/i18nOptionsValidator.js");
15
+
14
16
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15
17
 
16
18
  function configI18nNumericIndexPlugin(options) {
17
- if (!(options.i18nIndexing && options.i18nIndexing.enable)) {
19
+ if (!options.i18nIndexing) {
18
20
  return null;
19
21
  }
20
22
 
21
23
  const i18nOpts = options.i18nIndexing;
22
- const isDevelopment = options.isDevelopment || false; // Get isDevelopment from options
23
- // If we are only doing numeric ID replacement (no files to emit and no HTML injection, and no custom groups),
24
- // we can skip creating plugin instances to save build time.
24
+
25
+ try {
26
+ const result = (0, _i18nOptionsValidator.validateI18nIndexingOptions)(i18nOpts);
27
+
28
+ if (result.skipValidation) {
29
+ return null;
30
+ }
31
+ } catch (error) {
32
+ throw new Error(`[I18nNumericIndexPlugin] ${error.message}`);
33
+ }
25
34
 
26
35
  const emitFiles = i18nOpts.emitFiles !== undefined ? i18nOpts.emitFiles : true;
27
36
  const injectHtml = i18nOpts.injectI18nUrlInIndex !== undefined ? i18nOpts.injectI18nUrlInIndex : true;
@@ -29,87 +38,55 @@ function configI18nNumericIndexPlugin(options) {
29
38
 
30
39
  if (!emitFiles && !injectHtml && !hasCustomGroups) {
31
40
  return [];
32
- } // Validate required options
33
-
34
-
35
- const requiredOptions = ['jsResourcePath', 'propertiesFolderPath', 'numericMapPath', 'jsonpFunc', 'htmlTemplateLabel', 'localeVarName'];
36
- const missingOptions = requiredOptions.filter(opt => !i18nOpts[opt]);
41
+ }
37
42
 
38
- if (missingOptions.length > 0) {
39
- console.warn(`[I18nNumericIndexPlugin] Missing required options: ${missingOptions.join(', ')}`);
40
- return null;
41
- } // Read i18n values
43
+ let i18nData;
42
44
 
45
+ try {
46
+ i18nData = (0, _readI18nValues.readI18nValues)({
47
+ jsResource: i18nOpts.jsResourcePath,
48
+ propertiesFolder: i18nOpts.propertiesFolderPath,
49
+ disableDefault: false
50
+ });
51
+ } catch (error) {
52
+ throw new Error(`[I18nNumericIndexPlugin] Failed to read i18n data: ${error.message}`);
53
+ }
43
54
 
44
55
  const {
45
56
  locales,
46
57
  allI18nObject
47
- } = (0, _readI18nValues.readI18nValues)({
48
- jsResource: i18nOpts.jsResourcePath,
49
- propertiesFolder: i18nOpts.propertiesFolderPath,
50
- disableDefault: false
51
- }); // Ensure templates have [locale] placeholder
52
-
58
+ } = i18nData;
53
59
  const numericTemplate = i18nOpts.numericFilenameTemplate || '[locale]/numeric.i18n.js';
54
60
  const dynamicTemplate = i18nOpts.dynamicFilenameTemplate || '[locale]/dynamic.i18n.js';
55
-
56
- if (!numericTemplate.includes('[locale]')) {
57
- console.warn('[I18nNumericIndexPlugin] numericFilenameTemplate must include [locale] placeholder');
58
- return null;
59
- }
60
-
61
- if (!dynamicTemplate.includes('[locale]')) {
62
- console.warn('[I18nNumericIndexPlugin] dynamicFilenameTemplate must include [locale] placeholder');
63
- return null;
64
- } // Resolve singleFileTemplate if it's a function
65
-
66
-
67
- let resolvedSingleFileTemplate = i18nOpts.singleFileTemplate || '[locale].js';
68
-
69
- if (typeof i18nOpts.singleFileTemplate === 'function') {
70
- resolvedSingleFileTemplate = i18nOpts.singleFileTemplate(isDevelopment);
71
- } // Plugin options
72
-
73
-
74
- const numericIndexPluginOptions = {
75
- enable: i18nOpts.enable,
61
+ const sharedOptions = {
76
62
  outputFolder: i18nOpts.outputFolder || 'i18n-chunk',
63
+ numericFilenameTemplate: numericTemplate,
64
+ dynamicFilenameTemplate: dynamicTemplate,
65
+ singleFileTemplate: i18nOpts.singleFileTemplate,
66
+ singleFile: i18nOpts.singleFile || false,
67
+ includeContentHash: i18nOpts.includeContentHash || false
68
+ };
69
+ const numericIndexPluginOptions = { ...sharedOptions,
70
+ enable: i18nOpts.enable,
77
71
  jsResourcePath: i18nOpts.jsResourcePath,
78
72
  propertiesFolderPath: i18nOpts.propertiesFolderPath,
79
73
  numericMapPath: i18nOpts.numericMapPath,
80
74
  locales,
81
75
  allI18nObject,
82
- numericFilenameTemplate: numericTemplate,
83
- dynamicFilenameTemplate: dynamicTemplate,
84
- singleFileTemplate: resolvedSingleFileTemplate,
85
76
  jsonpFunc: i18nOpts.jsonpFunc,
86
77
  localeVarName: i18nOpts.localeVarName,
87
- singleFile: i18nOpts.singleFile || false,
88
78
  restrictToBaseKeys: i18nOpts.restrictToBaseKeys || false,
89
- includeContentHash: i18nOpts.includeContentHash || false,
90
79
  generateManifest: i18nOpts.generateManifest || false,
91
80
  manifestPath: i18nOpts.manifestPath || null,
92
81
  customGroups: i18nOpts.customGroups || null,
93
82
  chunkToGroupMapping: i18nOpts.chunkToGroupMapping || {},
94
- emitFiles: i18nOpts.emitFiles !== undefined ? i18nOpts.emitFiles : true,
95
- isDevelopment: isDevelopment,
83
+ emitFiles,
96
84
  i18nPublicPathVar: i18nOpts.i18nPublicPathVar
97
- }; // HTML injector options
98
-
99
- const htmlInjectorOptions = {
100
- outputFolder: i18nOpts.outputFolder || 'i18n-chunk',
101
- numericFilenameTemplate: numericTemplate,
102
- dynamicFilenameTemplate: dynamicTemplate,
103
- singleFileTemplate: resolvedSingleFileTemplate,
85
+ };
86
+ const htmlInjectorOptions = { ...sharedOptions,
104
87
  htmlTemplateLabel: i18nOpts.htmlTemplateLabel,
105
- singleFile: i18nOpts.singleFile || false,
106
88
  i18nAssetsPublicPathPrefix: '',
107
- injectI18nUrlInIndex: i18nOpts.injectI18nUrlInIndex !== undefined ? i18nOpts.injectI18nUrlInIndex : true,
108
- // Control HTML injection
109
- isDevelopment: isDevelopment,
110
- includeContentHash: i18nOpts.includeContentHash || false
89
+ injectI18nUrlInIndex: injectHtml
111
90
  };
112
- const i18nNumericPluginInstance = new _I18nNumericIndexPlugin.default(numericIndexPluginOptions);
113
- const htmlInjectorPluginInstance = new _I18nNumericIndexHtmlInjectorPlugin.I18nNumericIndexHtmlInjectorPlugin(htmlInjectorOptions);
114
- return [i18nNumericPluginInstance, htmlInjectorPluginInstance];
91
+ return [new _I18nNumericIndexPlugin.default(numericIndexPluginOptions), new _I18nNumericIndexHtmlInjectorPlugin.I18nNumericIndexHtmlInjectorPlugin(htmlInjectorOptions)];
115
92
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zohodesk/client_build_tool",
3
- "version": "0.0.11-exp.27.0",
3
+ "version": "0.0.11-exp.28.0",
4
4
  "description": "A CLI tool to build web applications and client libraries",
5
5
  "main": "lib/index.js",
6
6
  "bin": {