@zohodesk/client_build_tool 0.0.9-exp.4 → 0.0.9-exp.8

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,94 @@
1
+ "use strict";
2
+
3
+ const path = require('path');
4
+
5
+ const fs = require('fs');
6
+
7
+ let allI18nDataFromPropertiesCache = null;
8
+
9
+ function loadJSResourcesOnce(options) {
10
+ if (allI18nDataFromPropertiesCache) {
11
+ return allI18nDataFromPropertiesCache;
12
+ }
13
+
14
+ let resourcePathToLoad = '';
15
+
16
+ if (options.i18nIndexing && options.i18nIndexing.enable && options.i18nIndexing.jsResourcePath) {
17
+ resourcePathToLoad = options.i18nIndexing.jsResourcePath;
18
+ } else if (options.i18nChunkSplit && options.i18nChunkSplit.jsResource) {
19
+ resourcePathToLoad = options.i18nChunkSplit.jsResource;
20
+ } else {
21
+ allI18nDataFromPropertiesCache = {};
22
+ return allI18nDataFromPropertiesCache;
23
+ }
24
+
25
+ const propertiesFilePath = path.resolve(process.cwd(), resourcePathToLoad);
26
+ const i18nData = {};
27
+
28
+ try {
29
+ const data = fs.readFileSync(propertiesFilePath, {
30
+ encoding: 'utf-8'
31
+ });
32
+ const lines = data.split(/\r?\n/);
33
+ lines.forEach(line => {
34
+ const trimmedLine = line.trim();
35
+
36
+ if (trimmedLine.startsWith('#') || trimmedLine.startsWith('!') || trimmedLine === '') {
37
+ return;
38
+ }
39
+
40
+ let separatorIndex = -1;
41
+
42
+ for (let i = 0; i < trimmedLine.length; i++) {
43
+ if ((trimmedLine[i] === '=' || trimmedLine[i] === ':') && (i === 0 || trimmedLine[i - 1] !== '\\')) {
44
+ separatorIndex = i;
45
+ break;
46
+ }
47
+ }
48
+
49
+ if (separatorIndex > 0) {
50
+ let key = trimmedLine.substring(0, separatorIndex).trim();
51
+ const value = trimmedLine.substring(separatorIndex + 1).trim();
52
+ key = key.replace(/\\ /g, ' ');
53
+
54
+ if (key) {
55
+ i18nData[key] = value;
56
+ }
57
+ }
58
+ });
59
+ } catch (err) {// Silent error handling
60
+ }
61
+
62
+ allI18nDataFromPropertiesCache = i18nData;
63
+ return allI18nDataFromPropertiesCache;
64
+ }
65
+
66
+ function i18nIdReplaceLoaderConfig(options) {
67
+ const allI18nData = loadJSResourcesOnce(options);
68
+
69
+ const i18nKeyReplaceLoaderPath = require.resolve('../loaders/i18nIdReplaceLoader.js');
70
+
71
+ const loaderOptions = {
72
+ allI18nData: allI18nData,
73
+ sourceMaps: !!options.devtool && options.devtool.includes('source-map'),
74
+ isDebug: options.mode === 'development'
75
+ };
76
+
77
+ if (options.i18nIndexing && options.i18nIndexing.enable) {
78
+ loaderOptions.useNumericIndexing = true;
79
+ loaderOptions.numericMapPath = options.i18nIndexing.numericMapPath;
80
+ loaderOptions.fallbackToHash = options.i18nIndexing.fallbackToHash !== undefined ? options.i18nIndexing.fallbackToHash : true;
81
+ } else {
82
+ loaderOptions.useNumericIndexing = false;
83
+ loaderOptions.fallbackToHash = true;
84
+ }
85
+
86
+ return {
87
+ loader: i18nKeyReplaceLoaderPath,
88
+ options: loaderOptions
89
+ };
90
+ }
91
+
92
+ module.exports = {
93
+ i18nIdReplaceLoaderConfig
94
+ };
@@ -0,0 +1,201 @@
1
+ "use strict";
2
+
3
+ const fs = require('fs');
4
+
5
+ const path = require('path');
6
+
7
+ const parser = require('@babel/parser');
8
+
9
+ const generator = require('@babel/generator').default;
10
+
11
+ const {
12
+ walk
13
+ } = require('estree-walker');
14
+
15
+ const {
16
+ getOptions
17
+ } = require('loader-utils');
18
+
19
+ let collectAndCategorizeUsedI18nKeys, generateShortHash;
20
+
21
+ try {
22
+ collectAndCategorizeUsedI18nKeys = require('../custom_plugins/I18nSplitPlugin/utils/collectAstKeys').collectAndCategorizeUsedI18nKeys;
23
+ generateShortHash = require('../common/hashUtils').generateShortHash;
24
+ } catch (e) {
25
+ collectAndCategorizeUsedI18nKeys = () => ({
26
+ literalKeys: new Set(),
27
+ commentKeys: new Set()
28
+ });
29
+
30
+ generateShortHash = key => key;
31
+ }
32
+
33
+ const LOADER_PREFIX = '[i18nIdReplaceLoader]';
34
+ let numericIdMapDataCache = null;
35
+ let mapLoadAttemptedForPath = {};
36
+
37
+ function loadNumericIdMap(loaderContext, mapPath) {
38
+ if (!mapPath) {
39
+ loaderContext.emitWarning(new Error(`${LOADER_PREFIX} Numeric map path not provided in loader options.`));
40
+ return null;
41
+ }
42
+
43
+ if (numericIdMapDataCache && mapLoadAttemptedForPath[mapPath]) {
44
+ return numericIdMapDataCache;
45
+ }
46
+
47
+ mapLoadAttemptedForPath[mapPath] = true;
48
+
49
+ try {
50
+ if (fs.existsSync(mapPath)) {
51
+ const fileContent = fs.readFileSync(mapPath, 'utf-8');
52
+ const parsedData = JSON.parse(fileContent);
53
+
54
+ if (parsedData && parsedData.originalKeyToNumericId && typeof parsedData.originalKeyToNumericId === 'object') {
55
+ numericIdMapDataCache = parsedData.originalKeyToNumericId;
56
+ } else {
57
+ loaderContext.emitError(new Error(`${LOADER_PREFIX} Pre-generated map file (${mapPath}) is invalid or does not contain 'originalKeyToNumericId'.`));
58
+ numericIdMapDataCache = null;
59
+ }
60
+ } else {
61
+ loaderContext.emitError(new Error(`${LOADER_PREFIX} Pre-generated i18n numeric map file NOT FOUND at: ${mapPath}.`));
62
+ numericIdMapDataCache = null;
63
+ }
64
+ } catch (err) {
65
+ loaderContext.emitError(new Error(`${LOADER_PREFIX} Error loading or parsing pre-generated i18n map from ${mapPath}: ${err.message}`));
66
+ numericIdMapDataCache = null;
67
+ }
68
+
69
+ return numericIdMapDataCache;
70
+ }
71
+
72
+ module.exports = function i18nIdReplaceLoader(source, map, meta) {
73
+ const resourcePath = this.resourcePath;
74
+ this.cacheable && this.cacheable();
75
+ const options = getOptions(this) || {};
76
+ const callback = this.async();
77
+ const loaderContext = this;
78
+
79
+ if (!options.allI18nData || typeof options.allI18nData !== 'object' || Object.keys(options.allI18nData).length === 0) {
80
+ this.emitWarning(new Error(`${LOADER_PREFIX} [${resourcePath}] 'allI18nData' option is missing or empty. No replacements will be made.`));
81
+ return callback(null, source, map);
82
+ }
83
+
84
+ let useNumericIndexing = options.useNumericIndexing !== undefined ? options.useNumericIndexing : true;
85
+ let localOriginalKeyToNumericIdMap = null;
86
+
87
+ if (useNumericIndexing) {
88
+ localOriginalKeyToNumericIdMap = loadNumericIdMap(this, options.numericMapPath);
89
+ }
90
+
91
+ const fallbackToHash = options.fallbackToHash !== undefined ? options.fallbackToHash : true;
92
+
93
+ try {
94
+ const parserOptions = {
95
+ sourceType: 'module',
96
+ plugins: ['jsx', 'typescript', 'classProperties', 'optionalChaining', 'nullishCoalescingOperator'],
97
+ attachComment: true,
98
+ sourceFilename: resourcePath
99
+ };
100
+ const astFile = parser.parse(source, parserOptions);
101
+ const astProgram = astFile.program;
102
+ const comments = astFile.comments || [];
103
+ const {
104
+ literalKeys,
105
+ commentKeys
106
+ } = collectAndCategorizeUsedI18nKeys(astProgram, comments, options.allI18nData, options.isDebug);
107
+
108
+ if (this._module) {
109
+ if (!this._module.buildInfo) {
110
+ this._module.buildInfo = {};
111
+ }
112
+
113
+ if (literalKeys.size > 0) {
114
+ this._module.buildInfo.loaderIdentifiedLiteralI18nKeys = Array.from(literalKeys);
115
+ }
116
+
117
+ if (commentKeys.size > 0) {
118
+ this._module.buildInfo.loaderIdentifiedCommentI18nKeys = Array.from(commentKeys);
119
+ }
120
+ }
121
+
122
+ const keysToReplaceInLiterals = literalKeys;
123
+
124
+ if (keysToReplaceInLiterals.size === 0) {
125
+ return callback(null, source, map);
126
+ }
127
+
128
+ let replacementMade = false;
129
+ walk(astProgram, {
130
+ enter: function (node, parent, prop, index) {
131
+ const walkerControl = this;
132
+
133
+ if ((node.type === 'Literal' || node.type === 'StringLiteral') && typeof node.value === 'string') {
134
+ const originalValue = node.value;
135
+
136
+ if (keysToReplaceInLiterals.has(originalValue)) {
137
+ let replaced = false;
138
+
139
+ if (useNumericIndexing && localOriginalKeyToNumericIdMap) {
140
+ const numericId = localOriginalKeyToNumericIdMap[originalValue];
141
+
142
+ if (numericId !== undefined) {
143
+ const numericLiteralNode = {
144
+ type: 'NumericLiteral',
145
+ value: numericId
146
+ };
147
+ let replacementNode = numericLiteralNode;
148
+
149
+ if (parent && parent.type === 'JSXAttribute' && parent.value === node) {
150
+ replacementNode = {
151
+ type: 'JSXExpressionContainer',
152
+ expression: numericLiteralNode
153
+ };
154
+ }
155
+
156
+ walkerControl.replace(replacementNode);
157
+ replacementMade = true;
158
+ replaced = true;
159
+ }
160
+ }
161
+
162
+ if (!replaced && fallbackToHash) {
163
+ try {
164
+ const hashValue = generateShortHash(originalValue);
165
+ const hashedStringLiteralNode = {
166
+ type: 'StringLiteral',
167
+ value: hashValue
168
+ };
169
+ walkerControl.replace(hashedStringLiteralNode);
170
+ replacementMade = true;
171
+ } catch (hashError) {
172
+ loaderContext.emitError(new Error(`${LOADER_PREFIX} [${resourcePath}] Error hashing key "${originalValue}": ${hashError.message}`));
173
+ }
174
+ }
175
+ }
176
+ }
177
+ }
178
+ });
179
+
180
+ if (replacementMade) {
181
+ const generateOptions = {
182
+ sourceMaps: options.sourceMaps,
183
+ sourceFileName: resourcePath,
184
+ retainLines: false,
185
+ comments: true
186
+ };
187
+ const output = generator(astFile, generateOptions, source);
188
+ callback(null, output.code, options.sourceMaps && output.map ? output.map : map);
189
+ } else {
190
+ callback(null, source, map);
191
+ }
192
+ } catch (err) {
193
+ const detailedError = new Error(`${LOADER_PREFIX} [${resourcePath}] AST Processing Error: ${err.message} (Stack: ${err.stack})`);
194
+
195
+ if (err.loc) {
196
+ detailedError.message += ` at line ${err.loc.line}, column ${err.loc.column}`;
197
+ }
198
+
199
+ callback(detailedError);
200
+ }
201
+ };
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.configI18nNumericIndexPlugin = configI18nNumericIndexPlugin;
7
+
8
+ var {
9
+ I18nNumericIndexPlugin
10
+ } = require("@zohodesk/client_build_tool/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/I18nNumericIndexPlugin");
11
+
12
+ var {
13
+ I18nNumericIndexHtmlInjectorPlugin
14
+ } = require("../custom_plugins/I18nNumericIndexPlugin/I18nNumericIndexHtmlInjectorPlugin");
15
+
16
+ var {
17
+ readI18nValues
18
+ } = require('../custom_plugins/I18nSplitPlugin/readI18nValues');
19
+
20
+ const path = require('path');
21
+
22
+ function urlConcat(strPath) {
23
+ if (strPath && typeof strPath === 'string' && !strPath.endsWith('/')) {
24
+ return strPath + '/';
25
+ }
26
+
27
+ return strPath;
28
+ }
29
+
30
+ function urlJoin(...args) {
31
+ return args.map(part => typeof part === 'string' ? part.replace(/(^\/+|\/+$)/g, '') : '').filter(part => part !== '').join('/');
32
+ }
33
+
34
+ function configI18nNumericIndexPlugin(options) {
35
+ if (!(options.i18nIndexing && options.i18nIndexing.enable)) {
36
+ return null;
37
+ }
38
+
39
+ const i18nOpts = options.i18nIndexing;
40
+ const cdnConfig = options.cdnMapping || {};
41
+ const {
42
+ locales,
43
+ allI18nObject
44
+ } = readI18nValues({
45
+ jsResource: i18nOpts.jsResourcePath,
46
+ propertiesFolder: i18nOpts.propertiesFolderPath,
47
+ disableDefault: i18nOpts.disableDefaultMerge !== undefined ? i18nOpts.disableDefaultMerge : false
48
+ });
49
+ const hardcodedDefaultNumericTemplate = 'i18n-chunks/[locale]/numeric.i18n.js';
50
+ const hardcodedDefaultDynamicTemplate = 'i18n-chunks/[locale]/dynamic.i18n.js';
51
+ let numericFilenameTemplate = i18nOpts.numericFilenameTemplate || hardcodedDefaultNumericTemplate;
52
+ let dynamicFilenameTemplate = i18nOpts.dynamicFilenameTemplate || hardcodedDefaultDynamicTemplate;
53
+
54
+ if (i18nOpts.numericFilenameTemplate === undefined) {
55
+ // Check if explicitly undefined or missing
56
+ const i18nSplitFilename = options.i18nChunkSplit && options.i18nChunkSplit.filename;
57
+
58
+ if (i18nSplitFilename && typeof i18nSplitFilename === 'string') {
59
+ // This adaptation logic might need to be more robust
60
+ numericFilenameTemplate = `i18n-chunks/[locale]/${path.basename(i18nSplitFilename).replace(/(\.i18n)?\.js$/, '.numeric.i18n.js')}`;
61
+ } else {
62
+ numericFilenameTemplate = hardcodedDefaultNumericTemplate;
63
+ }
64
+ }
65
+
66
+ if (i18nOpts.dynamicFilenameTemplate === undefined) {
67
+ const i18nSplitFilename = options.i18nChunkSplit && options.i18nChunkSplit.filename;
68
+
69
+ if (i18nSplitFilename && typeof i18nSplitFilename === 'string') {
70
+ dynamicFilenameTemplate = `i18n-chunks/[locale]/${path.basename(i18nSplitFilename).replace(/(\.i18n)?\.js$/, '.dynamic.i18n.js')}`;
71
+ } else {
72
+ dynamicFilenameTemplate = hardcodedDefaultDynamicTemplate;
73
+ }
74
+ }
75
+
76
+ const htmlTemplateLabel = i18nOpts.htmlTemplateLabel || '{{--user-locale}}';
77
+ const defaultJsonpFunc = options.i18nChunkSplit && options.i18nChunkSplit.jsonpFunc || 'window.loadI18nChunk';
78
+ let i18nAssetsPublicPathPrefix = '';
79
+
80
+ if (cdnConfig.isCdnEnabled) {
81
+ if (cdnConfig.i18nTemplate) {
82
+ i18nAssetsPublicPathPrefix = urlConcat(cdnConfig.i18nTemplate);
83
+ } else if (cdnConfig.jsTemplate) {
84
+ i18nAssetsPublicPathPrefix = urlConcat(cdnConfig.jsTemplate);
85
+ } else {
86
+ const defaultCdnDomainPlaceholder = '//{{--js-static-domain}}';
87
+ i18nAssetsPublicPathPrefix = urlConcat(urlJoin(defaultCdnDomainPlaceholder, options.publicPath === undefined ? '' : options.publicPath));
88
+ }
89
+ } else {
90
+ i18nAssetsPublicPathPrefix = urlConcat(options.publicPath === undefined ? '' : options.publicPath);
91
+ }
92
+
93
+ const pluginBaseOptions = {
94
+ locales,
95
+ allI18nObject,
96
+ numericMapPath: i18nOpts.numericMapPath,
97
+ numericJsonpFunc: i18nOpts.numericJsonpFunc || defaultJsonpFunc,
98
+ dynamicJsonpFunc: i18nOpts.dynamicJsonpFunc || defaultJsonpFunc,
99
+ numericFilenameTemplate,
100
+ // This will now include "i18n-chunks/[locale]/"
101
+ dynamicFilenameTemplate,
102
+ // This will now include "i18n-chunks/[locale]/"
103
+ moduleType: 'i18n/mini-extract',
104
+ mainChunkName: options.i18nChunkSplit && options.i18nChunkSplit.mainChunkName || 'main',
105
+ htmlTemplateLabel,
106
+ localeVarName: i18nOpts.localeVarName
107
+ };
108
+ const htmlInjectorOptions = { ...pluginBaseOptions,
109
+ i18nAssetsPublicPathPrefix: i18nAssetsPublicPathPrefix,
110
+ cspNoncePlaceholder: i18nOpts.cspNoncePlaceholder || '{{--CSP-nonce}}'
111
+ };
112
+ return [new I18nNumericIndexPlugin(pluginBaseOptions), new I18nNumericIndexHtmlInjectorPlugin(htmlInjectorOptions)];
113
+ }
@@ -49,10 +49,29 @@ var _configRuntimeResourceCleanup = require("./pluginConfigs/configRuntimeResour
49
49
 
50
50
  var _configCustomScriptLoadingStrategyPlugin = require("./pluginConfigs/configCustomScriptLoadingStrategyPlugin");
51
51
 
52
- // import { IgnorePlugin } from 'webpack';
52
+ var {
53
+ configI18nNumericIndexPlugin
54
+ } = require("./pluginConfigs/configI18nNumericIndexPlugin");
55
+
53
56
  function plugins(options) {
54
57
  const {
55
58
  webpackPlugins
56
59
  } = options;
57
- 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, _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);
60
+ const pluginConfigurations = [(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, _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)];
61
+
62
+ if (options.i18nIndexing && options.i18nIndexing.enable) {
63
+ const newI18nPlugins = configI18nNumericIndexPlugin(options);
64
+
65
+ if (newI18nPlugins) {
66
+ pluginConfigurations.push(...newI18nPlugins);
67
+ }
68
+ } else {
69
+ const configI18nSplitPlugin = (0, _configI18nSplitPlugin.configI18nSplitPlugin)(options);
70
+
71
+ if (configI18nSplitPlugin) {
72
+ pluginConfigurations.push(configI18nSplitPlugin);
73
+ }
74
+ }
75
+
76
+ return [...pluginConfigurations, ...webpackPlugins].filter(Boolean);
58
77
  }
@@ -44,13 +44,6 @@ function handleMockApi(mockEntryFile, app) {
44
44
  const entryFilePath = (0, _constants.joinWithAppPath)(mockEntryFile); // eslint-disable-next-line no-use-before-define
45
45
 
46
46
  const mockFunc = safeRequire(entryFilePath);
47
-
48
- if (typeof mockFunc === 'function') {
49
- // eslint-disable-next-line no-use-before-define
50
- mockFunc(app);
51
- return;
52
- }
53
-
54
47
  mockFunc?.mockApi?.(app);
55
48
  } // function handleMockApi(params) {
56
49
  // }