@zohodesk/client_build_tool 0.0.1-0.exp.0.0.3 → 0.0.1-0.exp.0.0.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.
- package/lib/schemas/defaultConfigValues.js +39 -19
- package/lib/schemas/defaultConfigValuesOnly.js +14 -8
- package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/CLAUDE.md +0 -0
- package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/I18nNumericIndexPlugin.js +382 -75
- package/lib/shared/bundler/webpack/custom_plugins/I18nSplitPlugin/I18nFilesEmitPlugin.js +66 -5
- package/lib/shared/bundler/webpack/custom_plugins/I18nSplitPlugin/optionsHandler.js +3 -0
- package/lib/shared/bundler/webpack/custom_plugins/getInitialI18nAssetsArrayStr.js +6 -1
- package/lib/shared/bundler/webpack/loaderConfigs/i18nIdReplaceLoaderConfig.js +92 -61
- package/lib/shared/bundler/webpack/loaders/i18nIdReplaceLoader.js +151 -123
- package/lib/shared/bundler/webpack/pluginConfigs/configI18nNumericIndexPlugin.js +74 -45
- package/lib/shared/bundler/webpack/pluginConfigs/configI18nSplitPlugin.js +4 -1
- package/lib/shared/bundler/webpack/utils/propertiesParser.js +103 -0
- package/npm-shrinkwrap.json +8086 -21
- package/package.json +1 -1
|
@@ -40,6 +40,9 @@ function optionsHandler(options) {
|
|
|
40
40
|
jsResourceI18nKeys,
|
|
41
41
|
allI18nObject,
|
|
42
42
|
locales,
|
|
43
|
+
// NEW OPTIONS FOR NUMERIC INDEXING
|
|
44
|
+
useNumericIndexing: options.useNumericIndexing,
|
|
45
|
+
numericMapPath: options.numericMapPath,
|
|
43
46
|
// template: (object, locale) => `window.loadI18n(${JSON.stringify(object)}, ${JSON.stringify(locale)})`,
|
|
44
47
|
runtime: true,
|
|
45
48
|
runtimeOptions: {
|
|
@@ -36,7 +36,7 @@ function getI18nAssetsForChunkAsArrayStr({
|
|
|
36
36
|
chunkSplitEnable,
|
|
37
37
|
i18nFileNameTemplate
|
|
38
38
|
}) {
|
|
39
|
-
if (!chunkSplitEnable) {
|
|
39
|
+
if (!chunkSplitEnable || !i18nStore) {
|
|
40
40
|
// NOTE: we have used lang variable inside
|
|
41
41
|
// NOTE: below code for full i18n for now it is not implemented
|
|
42
42
|
// if (!chunkSplitEnable) {
|
|
@@ -71,6 +71,11 @@ function getI18nAssetForChunkAsStr({
|
|
|
71
71
|
i18nStore,
|
|
72
72
|
i18nFileNameTemplate
|
|
73
73
|
}) {
|
|
74
|
+
// Handle case where i18nStore is undefined (when i18nChunkSplit is disabled or not initialized)
|
|
75
|
+
if (!i18nStore || !i18nStore.isChunkHasI18n) {
|
|
76
|
+
return '';
|
|
77
|
+
}
|
|
78
|
+
|
|
74
79
|
if (!i18nStore.isChunkHasI18n(chunk)) {
|
|
75
80
|
return '';
|
|
76
81
|
}
|
|
@@ -4,91 +4,122 @@ const path = require('path');
|
|
|
4
4
|
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
const {
|
|
8
|
+
parseProperties
|
|
9
|
+
} = require('../utils/propertiesParser'); // Improved caching with proper cleanup
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
|
|
12
|
+
class ConfigCache {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.i18nDataCache = new Map();
|
|
15
|
+
this.maxCacheSize = 10; // Limit cache size
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
getI18nData(filePath) {
|
|
19
|
+
return this.i18nDataCache.get(filePath);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
setI18nData(filePath, data) {
|
|
23
|
+
if (this.i18nDataCache.size >= this.maxCacheSize) {
|
|
24
|
+
// Clear oldest entries
|
|
25
|
+
const firstKey = this.i18nDataCache.keys().next().value;
|
|
26
|
+
this.i18nDataCache.delete(firstKey);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
this.i18nDataCache.set(filePath, data);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
clear() {
|
|
33
|
+
this.i18nDataCache.clear();
|
|
12
34
|
}
|
|
13
35
|
|
|
14
|
-
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const configCache = new ConfigCache();
|
|
39
|
+
|
|
40
|
+
function loadJSResourcesOnce(options, webpackContext) {
|
|
41
|
+
if (!options.i18nIndexing || !options.i18nIndexing.enable) {
|
|
42
|
+
throw new Error('i18nIdReplaceLoader requires i18nIndexing to be enabled');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!options.i18nIndexing.jsResourcePath) {
|
|
46
|
+
throw new Error('Missing required jsResourcePath in i18nIndexing options');
|
|
47
|
+
} // Use webpack context instead of process.cwd() for better reliability
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
const contextPath = webpackContext || process.cwd();
|
|
51
|
+
const propertiesFilePath = path.isAbsolute(options.i18nIndexing.jsResourcePath) ? options.i18nIndexing.jsResourcePath : path.resolve(contextPath, options.i18nIndexing.jsResourcePath); // Check cache first
|
|
52
|
+
|
|
53
|
+
const cached = configCache.getI18nData(propertiesFilePath);
|
|
15
54
|
|
|
16
|
-
if (
|
|
17
|
-
|
|
18
|
-
} else if (options.i18nChunkSplit && options.i18nChunkSplit.jsResource) {
|
|
19
|
-
resourcePathToLoad = options.i18nChunkSplit.jsResource;
|
|
20
|
-
} else {
|
|
21
|
-
allI18nDataFromPropertiesCache = {};
|
|
22
|
-
return allI18nDataFromPropertiesCache;
|
|
55
|
+
if (cached) {
|
|
56
|
+
return cached;
|
|
23
57
|
}
|
|
24
58
|
|
|
25
|
-
|
|
26
|
-
|
|
59
|
+
if (!fs.existsSync(propertiesFilePath)) {
|
|
60
|
+
throw new Error(`JSResource file not found at: ${propertiesFilePath}`);
|
|
61
|
+
}
|
|
27
62
|
|
|
28
63
|
try {
|
|
29
64
|
const data = fs.readFileSync(propertiesFilePath, {
|
|
30
65
|
encoding: 'utf-8'
|
|
31
66
|
});
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
}
|
|
67
|
+
const i18nData = parseProperties(data);
|
|
68
|
+
|
|
69
|
+
if (Object.keys(i18nData).length === 0) {
|
|
70
|
+
throw new Error(`No i18n data found in JSResource file: ${propertiesFilePath}`);
|
|
71
|
+
} // Cache the result
|
|
61
72
|
|
|
62
|
-
|
|
63
|
-
|
|
73
|
+
|
|
74
|
+
configCache.setI18nData(propertiesFilePath, i18nData);
|
|
75
|
+
return i18nData;
|
|
76
|
+
} catch (err) {
|
|
77
|
+
throw new Error(`Error reading JSResource file ${propertiesFilePath}: ${err.message}`);
|
|
78
|
+
}
|
|
64
79
|
}
|
|
65
80
|
|
|
66
|
-
function i18nIdReplaceLoaderConfig(options) {
|
|
67
|
-
|
|
81
|
+
function i18nIdReplaceLoaderConfig(options, webpackContext) {
|
|
82
|
+
// Validate required options
|
|
83
|
+
if (!options.i18nIndexing || !options.i18nIndexing.enable) {
|
|
84
|
+
throw new Error('i18nIdReplaceLoader requires i18nIndexing to be enabled');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (!options.i18nIndexing.numericMapPath) {
|
|
88
|
+
throw new Error('Missing required numericMapPath in i18nIndexing options');
|
|
89
|
+
} // Load i18n data with proper context
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
const allI18nData = loadJSResourcesOnce(options, webpackContext);
|
|
93
|
+
|
|
94
|
+
const i18nKeyReplaceLoaderPath = require.resolve('../loaders/i18nIdReplaceLoader.js'); // Enhanced loader options with better defaults
|
|
68
95
|
|
|
69
|
-
const i18nKeyReplaceLoaderPath = require.resolve('../loaders/i18nIdReplaceLoader.js');
|
|
70
96
|
|
|
71
97
|
const loaderOptions = {
|
|
72
98
|
allI18nData: allI18nData,
|
|
73
|
-
sourceMaps: !!options.devtool && options.devtool.includes('source-map'),
|
|
74
|
-
isDebug: options.mode === 'development'
|
|
99
|
+
sourceMaps: !!(options.devtool && options.devtool.includes('source-map')),
|
|
100
|
+
isDebug: options.mode === 'development',
|
|
101
|
+
useNumericIndexing: true,
|
|
102
|
+
numericMapPath: options.i18nIndexing.numericMapPath,
|
|
103
|
+
// Additional configurable options
|
|
104
|
+
retainLines: options.i18nIndexing.retainLines || false,
|
|
105
|
+
preserveComments: options.i18nIndexing.preserveComments !== false,
|
|
106
|
+
compact: options.mode === 'production',
|
|
107
|
+
minified: options.mode === 'production',
|
|
108
|
+
// Allow custom babel plugins
|
|
109
|
+
babelPlugins: options.i18nIndexing.babelPlugins || undefined
|
|
75
110
|
};
|
|
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
111
|
return {
|
|
87
112
|
loader: i18nKeyReplaceLoaderPath,
|
|
88
113
|
options: loaderOptions
|
|
89
114
|
};
|
|
115
|
+
} // Export cache for potential cleanup
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
function clearCache() {
|
|
119
|
+
configCache.clear();
|
|
90
120
|
}
|
|
91
121
|
|
|
92
122
|
module.exports = {
|
|
93
|
-
i18nIdReplaceLoaderConfig
|
|
123
|
+
i18nIdReplaceLoaderConfig,
|
|
124
|
+
clearCache
|
|
94
125
|
};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
const fs = require('fs');
|
|
3
|
+
const fs = require('fs').promises;
|
|
4
|
+
|
|
5
|
+
const fsSync = require('fs');
|
|
4
6
|
|
|
5
7
|
const path = require('path');
|
|
6
8
|
|
|
@@ -16,57 +18,80 @@ const {
|
|
|
16
18
|
getOptions
|
|
17
19
|
} = require('loader-utils');
|
|
18
20
|
|
|
19
|
-
let collectAndCategorizeUsedI18nKeys
|
|
21
|
+
let collectAndCategorizeUsedI18nKeys;
|
|
20
22
|
|
|
21
23
|
try {
|
|
22
24
|
collectAndCategorizeUsedI18nKeys = require('../custom_plugins/I18nSplitPlugin/utils/collectAstKeys').collectAndCategorizeUsedI18nKeys;
|
|
23
|
-
generateShortHash = require('../common/hashUtils').generateShortHash;
|
|
24
25
|
} catch (e) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
throw new Error('[i18nIdReplaceLoader] Required dependency not found: ' + e.message);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const LOADER_PREFIX = '[i18nIdReplaceLoader]'; // Improved caching with proper cleanup
|
|
30
|
+
|
|
31
|
+
class LoaderCache {
|
|
32
|
+
constructor() {
|
|
33
|
+
this.numericMapCache = new Map();
|
|
34
|
+
this.astCache = new Map();
|
|
35
|
+
this.maxCacheSize = 100; // Prevent memory leaks
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
getNumericMap(mapPath) {
|
|
39
|
+
return this.numericMapCache.get(mapPath);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
setNumericMap(mapPath, data) {
|
|
43
|
+
if (this.numericMapCache.size >= this.maxCacheSize) {
|
|
44
|
+
// Clear oldest entries
|
|
45
|
+
const firstKey = this.numericMapCache.keys().next().value;
|
|
46
|
+
this.numericMapCache.delete(firstKey);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
this.numericMapCache.set(mapPath, data);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
clear() {
|
|
53
|
+
this.numericMapCache.clear();
|
|
54
|
+
this.astCache.clear();
|
|
55
|
+
}
|
|
29
56
|
|
|
30
|
-
generateShortHash = key => key;
|
|
31
57
|
}
|
|
32
58
|
|
|
33
|
-
const
|
|
34
|
-
let numericIdMapDataCache = null;
|
|
35
|
-
let mapLoadAttemptedForPath = {};
|
|
59
|
+
const loaderCache = new LoaderCache();
|
|
36
60
|
|
|
37
|
-
function loadNumericIdMap(loaderContext, mapPath) {
|
|
61
|
+
async function loadNumericIdMap(loaderContext, mapPath) {
|
|
38
62
|
if (!mapPath) {
|
|
39
|
-
|
|
40
|
-
return null;
|
|
63
|
+
throw new Error(`${LOADER_PREFIX} Numeric map path not provided in loader options.`);
|
|
41
64
|
}
|
|
42
65
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
66
|
+
const absoluteMapPath = path.isAbsolute(mapPath) ? mapPath : path.resolve(loaderContext.rootContext || process.cwd(), mapPath); // Check cache first
|
|
67
|
+
|
|
68
|
+
const cached = loaderCache.getNumericMap(absoluteMapPath);
|
|
46
69
|
|
|
47
|
-
|
|
70
|
+
if (cached) {
|
|
71
|
+
return cached;
|
|
72
|
+
}
|
|
48
73
|
|
|
49
74
|
try {
|
|
50
|
-
if
|
|
51
|
-
|
|
52
|
-
|
|
75
|
+
// Check if file exists
|
|
76
|
+
if (!fsSync.existsSync(absoluteMapPath)) {
|
|
77
|
+
throw new Error(`Pre-generated i18n numeric map file NOT FOUND at: ${absoluteMapPath}`);
|
|
78
|
+
} // Read file asynchronously for better performance
|
|
53
79
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
} else {
|
|
61
|
-
loaderContext.emitError(new Error(`${LOADER_PREFIX} Pre-generated i18n numeric map file NOT FOUND at: ${mapPath}.`));
|
|
62
|
-
numericIdMapDataCache = null;
|
|
80
|
+
|
|
81
|
+
const fileContent = await fs.readFile(absoluteMapPath, 'utf-8');
|
|
82
|
+
const parsedData = JSON.parse(fileContent); // Validate map structure
|
|
83
|
+
|
|
84
|
+
if (!parsedData || !parsedData.originalKeyToNumericId || typeof parsedData.originalKeyToNumericId !== 'object') {
|
|
85
|
+
throw new Error(`Pre-generated map file (${absoluteMapPath}) is invalid or does not contain 'originalKeyToNumericId'`);
|
|
63
86
|
}
|
|
87
|
+
|
|
88
|
+
const numericIdMap = parsedData.originalKeyToNumericId; // Cache the result
|
|
89
|
+
|
|
90
|
+
loaderCache.setNumericMap(absoluteMapPath, numericIdMap);
|
|
91
|
+
return numericIdMap;
|
|
64
92
|
} catch (err) {
|
|
65
|
-
|
|
66
|
-
numericIdMapDataCache = null;
|
|
93
|
+
throw new Error(`${LOADER_PREFIX} Error loading or parsing pre-generated i18n map from ${absoluteMapPath}: ${err.message}`);
|
|
67
94
|
}
|
|
68
|
-
|
|
69
|
-
return numericIdMapDataCache;
|
|
70
95
|
}
|
|
71
96
|
|
|
72
97
|
module.exports = function i18nIdReplaceLoader(source, map, meta) {
|
|
@@ -74,77 +99,78 @@ module.exports = function i18nIdReplaceLoader(source, map, meta) {
|
|
|
74
99
|
this.cacheable && this.cacheable();
|
|
75
100
|
const options = getOptions(this) || {};
|
|
76
101
|
const callback = this.async();
|
|
77
|
-
const loaderContext = this;
|
|
102
|
+
const loaderContext = this; // Validate required options
|
|
78
103
|
|
|
79
104
|
if (!options.allI18nData || typeof options.allI18nData !== 'object' || Object.keys(options.allI18nData).length === 0) {
|
|
80
|
-
|
|
81
|
-
return callback(null, source, map);
|
|
105
|
+
return callback(new Error(`${LOADER_PREFIX} [${resourcePath}] 'allI18nData' option is missing or empty.`));
|
|
82
106
|
}
|
|
83
107
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
108
|
+
if (!options.useNumericIndexing) {
|
|
109
|
+
return callback(new Error(`${LOADER_PREFIX} [${resourcePath}] 'useNumericIndexing' must be enabled.`));
|
|
110
|
+
} // Load numeric map asynchronously
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
loadNumericIdMap(this, options.numericMapPath).then(numericIdMap => {
|
|
114
|
+
try {
|
|
115
|
+
// Configure parser with better defaults and configurability
|
|
116
|
+
const parserOptions = {
|
|
117
|
+
sourceType: 'module',
|
|
118
|
+
plugins: options.babelPlugins || ['jsx', 'typescript', 'classProperties', 'optionalChaining', 'nullishCoalescingOperator', 'objectRestSpread', 'dynamicImport', 'decorators-legacy', 'asyncGenerators', 'bigInt', 'dynamicImport', 'exportDefaultFrom', 'exportNamespaceFrom', 'functionBind', 'importMeta', 'numericSeparator', 'optionalCatchBinding', 'throwExpressions', 'topLevelAwait'],
|
|
119
|
+
attachComment: true,
|
|
120
|
+
sourceFilename: resourcePath,
|
|
121
|
+
allowImportExportEverywhere: true,
|
|
122
|
+
allowAwaitOutsideFunction: true,
|
|
123
|
+
allowReturnOutsideFunction: true,
|
|
124
|
+
ranges: false,
|
|
125
|
+
tokens: false
|
|
126
|
+
};
|
|
127
|
+
const astFile = parser.parse(source, parserOptions);
|
|
128
|
+
const astProgram = astFile.program;
|
|
129
|
+
const comments = astFile.comments || [];
|
|
130
|
+
const {
|
|
131
|
+
literalKeys,
|
|
132
|
+
commentKeys
|
|
133
|
+
} = collectAndCategorizeUsedI18nKeys(astProgram, comments, options.allI18nData, options.isDebug); // Store keys in module build info for plugin consumption
|
|
134
|
+
|
|
135
|
+
if (this._module) {
|
|
136
|
+
if (!this._module.buildInfo) {
|
|
137
|
+
this._module.buildInfo = {};
|
|
138
|
+
}
|
|
90
139
|
|
|
91
|
-
|
|
140
|
+
if (literalKeys.size > 0) {
|
|
141
|
+
this._module.buildInfo.loaderIdentifiedLiteralI18nKeys = Array.from(literalKeys);
|
|
142
|
+
}
|
|
92
143
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
}
|
|
144
|
+
if (commentKeys.size > 0) {
|
|
145
|
+
this._module.buildInfo.loaderIdentifiedCommentI18nKeys = Array.from(commentKeys);
|
|
146
|
+
}
|
|
147
|
+
} // Early return if no replacements needed
|
|
112
148
|
|
|
113
|
-
if (literalKeys.size > 0) {
|
|
114
|
-
this._module.buildInfo.loaderIdentifiedLiteralI18nKeys = Array.from(literalKeys);
|
|
115
|
-
}
|
|
116
149
|
|
|
117
|
-
if (
|
|
118
|
-
|
|
150
|
+
if (literalKeys.size === 0) {
|
|
151
|
+
return callback(null, source, map);
|
|
119
152
|
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const keysToReplaceInLiterals = literalKeys;
|
|
123
153
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
let replacementMade = false;
|
|
129
|
-
walk(astProgram, {
|
|
130
|
-
enter: function (node, parent, prop, index) {
|
|
131
|
-
const walkerControl = this;
|
|
154
|
+
let replacementMade = false;
|
|
155
|
+
let replacementCount = 0; // Process AST and replace string literals with numeric IDs
|
|
132
156
|
|
|
133
|
-
|
|
134
|
-
|
|
157
|
+
walk(astProgram, {
|
|
158
|
+
enter: function (node, parent, prop, index) {
|
|
159
|
+
const walkerControl = this;
|
|
135
160
|
|
|
136
|
-
if (
|
|
137
|
-
|
|
161
|
+
if ((node.type === 'Literal' || node.type === 'StringLiteral') && typeof node.value === 'string') {
|
|
162
|
+
const originalValue = node.value;
|
|
138
163
|
|
|
139
|
-
if (
|
|
140
|
-
const numericId =
|
|
164
|
+
if (literalKeys.has(originalValue)) {
|
|
165
|
+
const numericId = numericIdMap[originalValue];
|
|
141
166
|
|
|
142
167
|
if (numericId !== undefined) {
|
|
143
168
|
const numericLiteralNode = {
|
|
144
169
|
type: 'NumericLiteral',
|
|
145
|
-
value: numericId
|
|
170
|
+
value: numericId,
|
|
171
|
+
raw: String(numericId)
|
|
146
172
|
};
|
|
147
|
-
let replacementNode = numericLiteralNode;
|
|
173
|
+
let replacementNode = numericLiteralNode; // Handle JSX attributes specially
|
|
148
174
|
|
|
149
175
|
if (parent && parent.type === 'JSXAttribute' && parent.value === node) {
|
|
150
176
|
replacementNode = {
|
|
@@ -155,47 +181,49 @@ module.exports = function i18nIdReplaceLoader(source, map, meta) {
|
|
|
155
181
|
|
|
156
182
|
walkerControl.replace(replacementNode);
|
|
157
183
|
replacementMade = true;
|
|
158
|
-
|
|
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}`));
|
|
184
|
+
replacementCount++;
|
|
173
185
|
}
|
|
174
186
|
}
|
|
175
187
|
}
|
|
176
188
|
}
|
|
189
|
+
}); // Generate output only if replacements were made
|
|
190
|
+
|
|
191
|
+
if (replacementMade) {
|
|
192
|
+
const generateOptions = {
|
|
193
|
+
sourceMaps: !!options.sourceMaps,
|
|
194
|
+
sourceFileName: resourcePath,
|
|
195
|
+
retainLines: options.retainLines || false,
|
|
196
|
+
comments: options.preserveComments !== false,
|
|
197
|
+
compact: options.compact || false,
|
|
198
|
+
minified: options.minified || false
|
|
199
|
+
};
|
|
200
|
+
const output = generator(astFile, generateOptions, source); // Debug logging if enabled
|
|
201
|
+
|
|
202
|
+
if (options.isDebug) {
|
|
203
|
+
console.log(`${LOADER_PREFIX} [${resourcePath}] Replaced ${replacementCount} i18n keys with numeric IDs`);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
callback(null, output.code, options.sourceMaps && output.map ? output.map : map);
|
|
207
|
+
} else {
|
|
208
|
+
callback(null, source, map);
|
|
177
209
|
}
|
|
178
|
-
})
|
|
179
|
-
|
|
180
|
-
|
|
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})`);
|
|
210
|
+
} catch (err) {
|
|
211
|
+
// Enhanced error handling with better context
|
|
212
|
+
const detailedError = new Error(`${LOADER_PREFIX} [${resourcePath}] AST Processing Error: ${err.message}`);
|
|
194
213
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
214
|
+
if (err.loc && err.loc.line) {
|
|
215
|
+
detailedError.message += ` at line ${err.loc.line}, column ${err.loc.column}`;
|
|
216
|
+
} // Add stack trace in debug mode
|
|
198
217
|
|
|
199
|
-
|
|
200
|
-
|
|
218
|
+
|
|
219
|
+
if (options.isDebug && err.stack) {
|
|
220
|
+
detailedError.message += `\nStack: ${err.stack}`;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
callback(detailedError);
|
|
224
|
+
}
|
|
225
|
+
}).catch(err => {
|
|
226
|
+
// Handle async errors from map loading
|
|
227
|
+
callback(new Error(`${LOADER_PREFIX} [${resourcePath}] Failed to load numeric map: ${err.message}`));
|
|
228
|
+
});
|
|
201
229
|
};
|