@zohodesk/client_build_tool 0.0.11-exp.26.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.
@@ -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
+ }
@@ -20,19 +20,10 @@ 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();
24
- let numericMapAbsPath = null;
23
+ const callback = this.async(); // Skip files in excluded paths
25
24
 
26
- if (options.numericMapPath) {
27
- try {
28
- numericMapAbsPath = path.isAbsolute(options.numericMapPath) ? options.numericMapPath : path.resolve(this.rootContext || this.context || process.cwd(), options.numericMapPath);
29
- this.addDependency(numericMapAbsPath);
30
- } catch (e) {}
31
- } // Skip files in excluded paths
32
-
33
-
34
- if (options.excludePaths) {
35
- 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));
36
27
 
37
28
  if (shouldExclude) {
38
29
  return callback(null, source, map);
@@ -40,57 +31,35 @@ module.exports = function i18nIdReplaceLoader(source, map) {
40
31
  } // Only process files in included paths if specified
41
32
 
42
33
 
43
- if (options.includePaths && options.includePaths.length > 0) {
44
- 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));
45
36
 
46
37
  if (!shouldInclude) {
47
38
  return callback(null, source, map);
48
39
  }
49
- } // Validate i18n data exists
40
+ } // Basic runtime validation - options should already be validated by config
50
41
 
51
42
 
52
43
  if (!options.allI18nData || Object.keys(options.allI18nData).length === 0) {
53
44
  return callback(new Error(`i18nIdReplaceLoader: 'allI18nData' option is missing or empty`));
54
- } // Load numeric ID mapping (use injected map if provided; fallback to memoized file read)
45
+ } // Load numeric ID mapping - paths already validated by config
55
46
 
56
47
 
57
- let numericIdMap = options.numericIdMap || null;
48
+ let numericIdMap = options.numericIdMap;
58
49
 
59
- try {
60
- if (!numericIdMap && (numericMapAbsPath || options.numericMapPath)) {
61
- // Module-level static cache
62
- if (!global.__CBT_I18N_NUMERIC_MAP_CACHE__) {
63
- global.__CBT_I18N_NUMERIC_MAP_CACHE__ = {};
64
- }
50
+ if (!numericIdMap && options.numericMapPath) {
51
+ try {
52
+ const mapPath = path.isAbsolute(options.numericMapPath) ? options.numericMapPath : path.resolve(this.rootContext || this.context || process.cwd(), options.numericMapPath);
53
+ this.addDependency(mapPath);
65
54
 
66
- const cache = global.__CBT_I18N_NUMERIC_MAP_CACHE__;
67
- const p = numericMapAbsPath || options.numericMapPath;
68
-
69
- try {
70
- const stat = fs.statSync(p);
71
- const key = `${p}`;
72
- const mtime = stat && stat.mtimeMs ? stat.mtimeMs : 0;
73
- const hit = cache[key];
74
-
75
- if (hit && hit.mtime === mtime && hit.map) {
76
- numericIdMap = hit.map;
77
- } else if (fs.existsSync(p)) {
78
- const txt = fs.readFileSync(p, 'utf-8');
79
- const parsed = JSON.parse(txt);
80
- const mapObj = parsed && parsed.originalKeyToNumericId ? parsed.originalKeyToNumericId : parsed;
81
-
82
- if (mapObj && typeof mapObj === 'object') {
83
- cache[key] = {
84
- mtime,
85
- map: mapObj
86
- };
87
- numericIdMap = mapObj;
88
- }
89
- }
90
- } catch (e) {// ignore and proceed without map
55
+ if (fs.existsSync(mapPath)) {
56
+ const mapContent = fs.readFileSync(mapPath, 'utf-8');
57
+ const parsed = JSON.parse(mapContent);
58
+ numericIdMap = parsed.originalKeyToNumericId || parsed;
91
59
  }
60
+ } catch (e) {
61
+ return callback(new Error(`i18nIdReplaceLoader: Failed to load numeric map from ${options.numericMapPath}: ${e.message}`));
92
62
  }
93
- } catch (e) {// ignore
94
63
  } // If no numeric map available, return source as-is
95
64
 
96
65
 
@@ -98,8 +67,6 @@ module.exports = function i18nIdReplaceLoader(source, map) {
98
67
  return callback(null, source, map);
99
68
  }
100
69
 
101
- const isDevMode = options.devMode || process.env.NODE_ENV === 'development';
102
-
103
70
  try {
104
71
  // Parse the JavaScript/TypeScript source code
105
72
  const ast = parser.parse(source, {
@@ -107,23 +74,20 @@ module.exports = function i18nIdReplaceLoader(source, map) {
107
74
  plugins: ['jsx', 'typescript', 'classProperties', 'optionalChaining', 'nullishCoalescingOperator'],
108
75
  sourceFilename: resourcePath
109
76
  });
110
- 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
111
79
 
112
80
  traverse(ast, {
113
81
  StringLiteral(path) {
114
82
  const {
115
83
  node
116
- } = path; // Check if this string is an i18n key
117
-
118
- if (!options.allI18nData.hasOwnProperty(node.value)) {
119
- return;
120
- } // Replace with numeric ID if available
121
-
84
+ } = path; // Check if this string is an i18n key and has numeric mapping
122
85
 
123
- if (numericIdMap.hasOwnProperty(node.value)) {
86
+ if (options.allI18nData.hasOwnProperty(node.value) && numericIdMap.hasOwnProperty(node.value)) {
124
87
  const numericId = String(numericIdMap[node.value]);
125
88
  path.replaceWith(t.stringLiteral(numericId));
126
89
  hasTransformations = true;
90
+ transformationCount++;
127
91
  }
128
92
  }
129
93
 
@@ -141,6 +105,6 @@ module.exports = function i18nIdReplaceLoader(source, map) {
141
105
  callback(null, source, map);
142
106
  }
143
107
  } catch (err) {
144
- callback(err);
108
+ callback(new Error(`i18nIdReplaceLoader: Failed to process ${resourcePath}: ${err.message}`));
145
109
  }
146
110
  };
@@ -23,10 +23,7 @@ function optimizationConfig(options) {
23
23
  } = options;
24
24
  const {
25
25
  chunkSplitEnable
26
- } = options.i18nChunkSplit; // TEMP: Test console logs for minification verification
27
-
28
- console.log('MINIFY_TEST: optimizationConfig called in mode:', options.mode);
29
- console.log('MINIFY_TEST: TerserPlugin and CSS minification active');
26
+ } = options.i18nChunkSplit;
30
27
  const i18nChunkFilename = (0, _nameTemplates.nameTemplates)('i18njs', options);
31
28
  const chunkFilenameHasContentHash = (0, _hashUtils.hasContentHash)(i18nChunkFilename);
32
29
  /**
@@ -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,85 +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
- emitFiles: i18nOpts.emitFiles !== undefined ? i18nOpts.emitFiles : true,
94
- isDevelopment: isDevelopment,
82
+ chunkToGroupMapping: i18nOpts.chunkToGroupMapping || {},
83
+ emitFiles,
95
84
  i18nPublicPathVar: i18nOpts.i18nPublicPathVar
96
- }; // HTML injector options
97
-
98
- const htmlInjectorOptions = {
99
- outputFolder: i18nOpts.outputFolder || 'i18n-chunk',
100
- numericFilenameTemplate: numericTemplate,
101
- dynamicFilenameTemplate: dynamicTemplate,
102
- singleFileTemplate: resolvedSingleFileTemplate,
85
+ };
86
+ const htmlInjectorOptions = { ...sharedOptions,
103
87
  htmlTemplateLabel: i18nOpts.htmlTemplateLabel,
104
- singleFile: i18nOpts.singleFile || false,
105
88
  i18nAssetsPublicPathPrefix: '',
106
- injectI18nUrlInIndex: i18nOpts.injectI18nUrlInIndex !== undefined ? i18nOpts.injectI18nUrlInIndex : true,
107
- // Control HTML injection
108
- isDevelopment: isDevelopment
89
+ injectI18nUrlInIndex: injectHtml
109
90
  };
110
- const i18nNumericPluginInstance = new _I18nNumericIndexPlugin.default(numericIndexPluginOptions);
111
- const htmlInjectorPluginInstance = new _I18nNumericIndexHtmlInjectorPlugin.I18nNumericIndexHtmlInjectorPlugin(htmlInjectorOptions);
112
- return [i18nNumericPluginInstance, htmlInjectorPluginInstance];
91
+ return [new _I18nNumericIndexPlugin.default(numericIndexPluginOptions), new _I18nNumericIndexHtmlInjectorPlugin.I18nNumericIndexHtmlInjectorPlugin(htmlInjectorOptions)];
113
92
  }
@@ -55,9 +55,6 @@ var _configCustomScriptLoadingStrategyPlugin = require("./pluginConfigs/configCu
55
55
  function plugins(options) {
56
56
  const {
57
57
  webpackPlugins
58
- } = options; // TEMP: Test console logs for minification verification
59
-
60
- console.log('MINIFY_TEST: plugins function called with options:', options.mode);
61
- console.log('MINIFY_TEST: This message should be visible if minification is disabled');
58
+ } = options;
62
59
  return [(0, _configEnvVariables.configEnvVariables)(options), (0, _configCustomAttributesPlugin.configCustomAttributesPlugin)(options), (0, _configTPHashMappingPlugin.configTPHashMappingPlugin)(options), (0, _configCopyPublicFolders.configCopyPublicFolders)(options), (0, _configIgnorePlugin.configIgnorePlugin)(options), (0, _configMiniCSSExtractPlugin.configMiniCSSExtractPlugin)(options), (0, _configSelectorWeightPlugin.configSelectorWeightPlugin)(options), (0, _configVariableConversionPlugin.configVariableConversionPlugin)(options), (0, _configI18nSplitPlugin.configI18nSplitPlugin)(options), ...((0, _configI18nNumericIndexPlugin.configI18nNumericIndexPlugin)(options) || []), (0, _configRtlCssPlugin.configRtlCssPlugin)(options), (0, _configHtmlWebpackPlugin.configHtmlWebpackPlugin)(options), (0, _configCustomScriptLoadingStrategyPlugin.configCustomScriptLoadingStrategyPlugin)(options), (0, _configCdnChangePlugin.configCdnChangePlugin)(options), (0, _configServiceWorkerPlugin.configServiceWorkerPlugin)(options), (0, _configEFCTemplatePlugin.configEFCTemplatePlugin)(options), (0, _configResourceHintsPlugin.configResourceHintsPlugin)(options), (0, _configBundleAnalyzer.configBundleAnalyzer)(options), (0, _configManifestJsonPlugin.configManifestJsonPlugin)(options), (0, _configSourceMapPlugin.configSourceMapPlugin)(options), (0, _configProgressPlugin.configProgressPlugin)(options), (0, _configBundleIntegrityReport.configBundleIntegrityReport)(options), (0, _configRuntimeResourceCleanup.configRuntimeResourceCleanup)(options), ...webpackPlugins].filter(Boolean);
63
60
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zohodesk/client_build_tool",
3
- "version": "0.0.11-exp.26.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": {
@@ -1,129 +0,0 @@
1
- # Dynamic singleFileTemplate Configuration
2
-
3
- ## Overview
4
- The `singleFileTemplate` option now supports both static strings and dynamic functions, allowing different filename patterns based on build mode.
5
-
6
- ## Configuration Options
7
-
8
- ### 1. Static Template (Simple)
9
- ```javascript
10
- i18nIndexing: {
11
- singleFileTemplate: '[locale].js' // Always uses this format
12
- }
13
- ```
14
-
15
- ### 2. Dynamic Template with Function
16
- ```javascript
17
- i18nIndexing: {
18
- isDevelopment: isDevelopment, // Pass from your app
19
- singleFileTemplate: (isDev) =>
20
- isDev ? '[locale].js' : '[locale].[contenthash].js'
21
- }
22
- ```
23
-
24
- ### 3. Inline Conditional (Pre-resolved)
25
- ```javascript
26
- i18nIndexing: {
27
- isDevelopment: isDevelopment, // Pass from your app
28
- singleFileTemplate: isDevelopment
29
- ? '[locale].js'
30
- : '[locale].[contenthash].js'
31
- }
32
- ```
33
-
34
- ## Template Placeholders
35
-
36
- - `[locale]` - Replaced with actual locale code (e.g., en_US, fr_FR)
37
- - `[contenthash]` - Replaced with content-based hash for cache busting
38
-
39
- ## Output Examples
40
-
41
- ### Development Mode (isDevelopment = true)
42
- ```
43
- Template: '[locale].js'
44
- Output:
45
- i18n/en_US.js
46
- i18n/fr_FR.js
47
- i18n/de_DE.js
48
- ```
49
-
50
- ### Production Mode (isDevelopment = false)
51
- ```
52
- Template: '[locale].[contenthash].js'
53
- Output:
54
- i18n/en_US.b9ef890a.js
55
- i18n/fr_FR.3452451e.js
56
- i18n/de_DE.e8b84364.js
57
- ```
58
-
59
- ## Complete Example Configuration
60
-
61
- ```javascript
62
- exports.config = {
63
- context: 'src',
64
- output: 'build',
65
- mode: process.env.NODE_ENV || 'production',
66
-
67
- // Pass isDevelopment from your app
68
- isDevelopment: process.env.NODE_ENV === 'development',
69
-
70
- i18nIndexing: {
71
- enable: true,
72
- outputFolder: 'i18n',
73
- jsResourcePath: './resources/ApplicationResources.properties',
74
- propertiesFolderPath: './resources',
75
- numericMapPath: './numericMap.json',
76
-
77
- // Dynamic template based on build mode
78
- singleFileTemplate: function(isDevelopment) {
79
- return isDevelopment ? '[locale].js' : '[locale].[contenthash].js';
80
- },
81
-
82
- // Or using arrow function
83
- // singleFileTemplate: (isDev) => isDev ? '[locale].js' : '[locale].[contenthash].js',
84
-
85
- // Or pre-resolved
86
- // singleFileTemplate: isDevelopment ? '[locale].js' : '[locale].[contenthash].js',
87
-
88
- singleFile: true,
89
- jsonpFunc: 'var imAppI18n=',
90
- htmlTemplateLabel: '{{--user-locale}}',
91
- localeVarName: 'window.userLangCode',
92
- generateManifest: true,
93
- manifestPath: 'i18n/manifest.json'
94
-
95
- // Note: includeContentHash is now optional when using [contenthash] in template
96
- // includeContentHash: true // Not needed with [contenthash] placeholder
97
- }
98
- };
99
- ```
100
-
101
- ## Benefits
102
-
103
- 1. **Development**: Clean, readable filenames without hashes for easier debugging
104
- 2. **Production**: Content-hashed filenames for optimal caching
105
- 3. **Flexibility**: Single configuration handles both environments
106
- 4. **No Redundancy**: Using `[contenthash]` in template eliminates need for `includeContentHash` option
107
-
108
- ## Migration from includeContentHash
109
-
110
- ### Old Way
111
- ```javascript
112
- singleFileTemplate: '[locale].js',
113
- includeContentHash: true // Adds hash before .js
114
- // Output: en_US.abc123.js
115
- ```
116
-
117
- ### New Way (Recommended)
118
- ```javascript
119
- singleFileTemplate: isDevelopment ? '[locale].js' : '[locale].[contenthash].js'
120
- // Output: en_US.abc123.js (production) or en_US.js (development)
121
- ```
122
-
123
- ## Notes
124
-
125
- - The function receives `isDevelopment` as a boolean parameter
126
- - You must pass `isDevelopment` in your main options object
127
- - The `[contenthash]` placeholder is automatically replaced with the actual hash
128
- - For HTML injection, the hash placeholder is removed since the exact hash isn't known at HTML generation time
129
- - The manifest will map clean names to hashed versions for deployment scripts