@zohodesk/client_build_tool 0.0.11-exp.15.1 → 0.0.11-exp.15.2
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/README.md +0 -102
- package/README_backup.md +0 -102
- package/docs/I18N_NUMERIC_INDEXING_PLUGIN.md +225 -0
- package/lib/schemas/defaultConfigValues.js +9 -12
- package/lib/schemas/defaultConfigValuesOnly.js +1 -18
- package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/I18nGroupRuntimeModule.js +124 -0
- package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/I18nNumericIndexPlugin.js +207 -145
- package/lib/shared/bundler/webpack/jsLoaders.js +2 -20
- package/lib/shared/bundler/webpack/pluginConfigs/configI18nIndexingPlugin.js +42 -0
- package/lib/shared/bundler/webpack/pluginConfigs/configI18nNumericHtmlInjector.js +92 -0
- package/lib/shared/bundler/webpack/plugins.js +6 -2
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/init/README.md +0 -170
- package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/I18nNumericIndexHtmlInjectorPlugin.js +0 -49
- package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/utils/i18nDataLoader.js +0 -106
- package/lib/shared/bundler/webpack/custom_plugins/I18nSplitPlugin/utils/collectAstKeys.js +0 -96
- package/lib/shared/bundler/webpack/loaderConfigs/i18nIdReplaceLoaderConfig.js +0 -71
- package/lib/shared/bundler/webpack/loaders/i18nIdReplaceLoader.js +0 -106
- package/lib/shared/bundler/webpack/pluginConfigs/configI18nNumericIndexPlugin.js +0 -84
- package/lib/shared/bundler/webpack/utils/propertiesParser.js +0 -81
package/init/README.md
DELETED
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
# Client Build Tool Init Guide
|
|
2
|
-
|
|
3
|
-
This folder provides a quick-start for integrating `@zohodesk/client_build_tool` (aka `cbt`) into an app. It covers installation, minimal config, and common feature toggles.
|
|
4
|
-
|
|
5
|
-
## Install
|
|
6
|
-
|
|
7
|
-
- As a dev dependency (recommended):
|
|
8
|
-
- npm: `npm i -D @zohodesk/client_build_tool`
|
|
9
|
-
- yarn: `yarn add -D @zohodesk/client_build_tool`
|
|
10
|
-
- Or use via npx: `npx cbt --help`
|
|
11
|
-
|
|
12
|
-
Add convenient scripts to your app’s `package.json`:
|
|
13
|
-
|
|
14
|
-
```
|
|
15
|
-
{
|
|
16
|
-
"scripts": {
|
|
17
|
-
"dev": "cbt start --context=app",
|
|
18
|
-
"build:es": "cbt build:es src es",
|
|
19
|
-
"build:lib": "cbt build:lib src lib",
|
|
20
|
-
"mock": "cbt mock:server --mock_port=3001",
|
|
21
|
-
"pre": "cbt pre:process"
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
## Minimal Config (cbt.config.js)
|
|
27
|
-
|
|
28
|
-
Copy `cbt.config.example.js` to your app root as `cbt.config.js` and edit:
|
|
29
|
-
|
|
30
|
-
```
|
|
31
|
-
module.exports = {
|
|
32
|
-
config: {
|
|
33
|
-
context: 'app',
|
|
34
|
-
mode: 'dev',
|
|
35
|
-
server: {
|
|
36
|
-
port: 9090,
|
|
37
|
-
host: 'localhost',
|
|
38
|
-
httpsCerts: '@zohodesk-private/client_dev_cert',
|
|
39
|
-
disableContextURL: false
|
|
40
|
-
},
|
|
41
|
-
htmlTemplate: { templateFile: 'src/index.html', inject: true },
|
|
42
|
-
app: { entryFile: 'src/index.js' },
|
|
43
|
-
css: {
|
|
44
|
-
plugins: {
|
|
45
|
-
rtlSplit: { enableRTLSplit: false }
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
i18nChunkSplit: { chunkSplitEnable: false },
|
|
49
|
-
i18nIndexing: { enable: false },
|
|
50
|
-
preProcess: { enable: false }
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
Run dev server: `npm run dev` (or `cbt start`).
|
|
56
|
-
|
|
57
|
-
## Common Feature Recipes
|
|
58
|
-
|
|
59
|
-
- Enable i18n chunk split (per-locale chunks):
|
|
60
|
-
```
|
|
61
|
-
config: {
|
|
62
|
-
i18nChunkSplit: {
|
|
63
|
-
chunkSplitEnable: true,
|
|
64
|
-
templateLabel: '{{--user-locale}}',
|
|
65
|
-
localeVarName: 'document.documentElement.lang',
|
|
66
|
-
jsonpFunc: 'window.loadI18nChunk',
|
|
67
|
-
// optional: jsResource/propertiesFolder when using default reader
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
- Enable i18n numeric indexing (per-locale or single-file):
|
|
73
|
-
```
|
|
74
|
-
config: {
|
|
75
|
-
i18nIndexing: {
|
|
76
|
-
enable: true,
|
|
77
|
-
devMode: false,
|
|
78
|
-
jsResourcePath: './deskapp/properties/JSResources.properties',
|
|
79
|
-
propertiesFolderPath: './deskapp/properties',
|
|
80
|
-
numericMapPath: './deskapp/properties/i18n-numeric-map.json',
|
|
81
|
-
// choose one mode
|
|
82
|
-
numericFilenameTemplate: 'i18n-chunk/[locale]/numeric.i18n.js',
|
|
83
|
-
dynamicFilenameTemplate: 'i18n-chunk/[locale]/dynamic.i18n.js',
|
|
84
|
-
// or single-file mode
|
|
85
|
-
// singleFile: true,
|
|
86
|
-
// singleFileTemplate: 'i18n/[locale].js',
|
|
87
|
-
jsonpFunc: 'window.loadI18nChunk',
|
|
88
|
-
htmlTemplateLabel: '{{--user-locale}}',
|
|
89
|
-
localeVarName: 'window.userLangCode',
|
|
90
|
-
includeContentHash: false,
|
|
91
|
-
generateManifest: false
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
- Enable RTL CSS split and path templating:
|
|
97
|
-
```
|
|
98
|
-
config: {
|
|
99
|
-
css: {
|
|
100
|
-
plugins: {
|
|
101
|
-
rtlSplit: {
|
|
102
|
-
enableRTLSplit: true,
|
|
103
|
-
templateLabel: '{{--dir}}',
|
|
104
|
-
disableMinifySelector: false,
|
|
105
|
-
dirVarName: 'document.dir'
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
- EFC SDK output:
|
|
113
|
-
```
|
|
114
|
-
config: {
|
|
115
|
-
efc: {
|
|
116
|
-
hasEFC: true,
|
|
117
|
-
createSDkFile: true,
|
|
118
|
-
entryPointName: 'efc',
|
|
119
|
-
entryFile: 'src/efc.js',
|
|
120
|
-
version: 'v1',
|
|
121
|
-
templateFilePath: 'efcTemplate.js',
|
|
122
|
-
outputFile: 'efc-sdk-[version].js',
|
|
123
|
-
resourceCleanup: true
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
- Service worker:
|
|
129
|
-
```
|
|
130
|
-
config: {
|
|
131
|
-
serviceWorker: {
|
|
132
|
-
enable: true,
|
|
133
|
-
templateFilePath: 'sw.js',
|
|
134
|
-
outputFilePath: '/v1.js',
|
|
135
|
-
replaceText: '//<!--AssetsFromBuild -->'
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
- Custom PostCSS plugins (built-in + custom):
|
|
141
|
-
```
|
|
142
|
-
config: {
|
|
143
|
-
css: {
|
|
144
|
-
plugins: {
|
|
145
|
-
valueReplacer: { enable: true, patterns: ['**/*.css'], config: [
|
|
146
|
-
{ props: ['color'], values: { 'zd-': 'im-' } }
|
|
147
|
-
]},
|
|
148
|
-
selectorReplace: { enable: true, patterns: ['**/*.module.css'], before: ['.old'], after: ['.new'] },
|
|
149
|
-
hoverActive: { enable: true, patterns: ['**/*.css'], hover: '(hover: hover)', active: '(hover: none)' },
|
|
150
|
-
cssVariableReplacement: { enable: false, patterns: [], configFile: null },
|
|
151
|
-
rtl: { enable: false, patterns: [] }
|
|
152
|
-
},
|
|
153
|
-
customPlugins: [
|
|
154
|
-
// { enable: true, patterns: ['**/*.css'], plugin: '/absolute/path/to/custom-postcss-plugin.js' }
|
|
155
|
-
]
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
## Tips & Troubleshooting
|
|
161
|
-
|
|
162
|
-
- Node.js: CBT supports Node 14–18.
|
|
163
|
-
- HTTPS certs: `server.httpsCerts` can be a local or global npm package exposing `{ httpsOptions }`.
|
|
164
|
-
- Public path: Controlled via server extras; disable context prefix with `server.disableContextURL=true`.
|
|
165
|
-
- Proxies: If you have stale npm proxy settings during install: `npm config delete proxy && npm config delete https-proxy`.
|
|
166
|
-
- Verbose logs: set `VERBOSE=true`.
|
|
167
|
-
|
|
168
|
-
---
|
|
169
|
-
|
|
170
|
-
For full option reference and internal architecture, read `../docs/client_build_tool_source_doc.md`.
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
|
4
|
-
|
|
5
|
-
const pluginName = 'I18nNumericIndexHtmlInjectorPlugin';
|
|
6
|
-
|
|
7
|
-
class I18nNumericIndexHtmlInjectorPlugin {
|
|
8
|
-
constructor(options) {
|
|
9
|
-
this.options = options;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
apply(compiler) {
|
|
13
|
-
compiler.hooks.thisCompilation.tap(pluginName, compilation => {
|
|
14
|
-
HtmlWebpackPlugin.getHooks(compilation).beforeAssetTagGeneration.tapAsync(pluginName, (hookData, cb) => {
|
|
15
|
-
const {
|
|
16
|
-
assets
|
|
17
|
-
} = hookData;
|
|
18
|
-
const {
|
|
19
|
-
numericFilenameTemplate,
|
|
20
|
-
dynamicFilenameTemplate,
|
|
21
|
-
htmlTemplateLabel,
|
|
22
|
-
i18nAssetsPublicPathPrefix = ''
|
|
23
|
-
} = this.options;
|
|
24
|
-
const newI18nAssetUrlsToAdd = [];
|
|
25
|
-
|
|
26
|
-
if (numericFilenameTemplate) {
|
|
27
|
-
const numericFilename = numericFilenameTemplate.replace(/\[locale\]/g, htmlTemplateLabel);
|
|
28
|
-
newI18nAssetUrlsToAdd.push(numericFilename);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (dynamicFilenameTemplate) {
|
|
32
|
-
const dynamicFilename = dynamicFilenameTemplate.replace(/\[locale\]/g, htmlTemplateLabel);
|
|
33
|
-
newI18nAssetUrlsToAdd.push(dynamicFilename);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (newI18nAssetUrlsToAdd.length > 0) {
|
|
37
|
-
assets.js = [...assets.js, ...newI18nAssetUrlsToAdd];
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
cb(null, hookData);
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
module.exports = {
|
|
48
|
-
I18nNumericIndexHtmlInjectorPlugin
|
|
49
|
-
};
|
package/lib/shared/bundler/webpack/custom_plugins/I18nNumericIndexPlugin/utils/i18nDataLoader.js
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
|
|
5
|
-
const path = require('path');
|
|
6
|
-
|
|
7
|
-
const {
|
|
8
|
-
parseProperties
|
|
9
|
-
} = require('../../../utils/propertiesParser');
|
|
10
|
-
|
|
11
|
-
function loadPropertiesFile(filePath, compilation, description) {
|
|
12
|
-
try {
|
|
13
|
-
const content = fs.readFileSync(filePath, 'utf-8');
|
|
14
|
-
const parsed = parseProperties(content);
|
|
15
|
-
return parsed;
|
|
16
|
-
} catch (err) {
|
|
17
|
-
if (compilation) {
|
|
18
|
-
compilation.errors.push(new Error(`I18nNumericIndexPlugin: Error loading ${description}: ${err.message}`));
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
return {};
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function loadNumericMap(numericMapPath, compilation) {
|
|
26
|
-
try {
|
|
27
|
-
const fileContent = fs.readFileSync(numericMapPath, 'utf-8');
|
|
28
|
-
const parsedData = JSON.parse(fileContent);
|
|
29
|
-
const sortedKeys = new Array(parsedData.totalKeysInMap);
|
|
30
|
-
Object.entries(parsedData.originalKeyToNumericId).forEach(([key, id]) => {
|
|
31
|
-
sortedKeys[id] = key;
|
|
32
|
-
});
|
|
33
|
-
return {
|
|
34
|
-
sortedKeys,
|
|
35
|
-
totalKeys: parsedData.totalKeysInMap
|
|
36
|
-
};
|
|
37
|
-
} catch (err) {
|
|
38
|
-
if (compilation) {
|
|
39
|
-
compilation.errors.push(new Error(`I18nNumericIndexPlugin: Error loading numeric map: ${err.message}`));
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return {
|
|
43
|
-
sortedKeys: [],
|
|
44
|
-
totalKeys: 0
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function loadAllLocaleFiles(propertiesPath, compilation, jsResourceBase) {
|
|
50
|
-
const allI18n = {};
|
|
51
|
-
const locales = [];
|
|
52
|
-
allI18n['en_US'] = jsResourceBase;
|
|
53
|
-
locales.push('en_US');
|
|
54
|
-
|
|
55
|
-
try {
|
|
56
|
-
const files = fs.readdirSync(propertiesPath);
|
|
57
|
-
files.forEach(file => {
|
|
58
|
-
if (!file.endsWith('.properties')) return;
|
|
59
|
-
const match = file.match(/^ApplicationResources_([a-z]{2}_[A-Z]{2})\.properties$/);
|
|
60
|
-
|
|
61
|
-
if (match) {
|
|
62
|
-
const locale = match[1];
|
|
63
|
-
const filePath = path.join(propertiesPath, file);
|
|
64
|
-
const localeData = loadPropertiesFile(filePath, compilation, `locale ${locale}`);
|
|
65
|
-
allI18n[locale] = { ...jsResourceBase,
|
|
66
|
-
...localeData
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
if (!locales.includes(locale)) {
|
|
70
|
-
locales.push(locale);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
} catch (err) {
|
|
75
|
-
if (compilation) {
|
|
76
|
-
compilation.errors.push(new Error(`I18nNumericIndexPlugin: Error reading properties folder: ${err.message}`));
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return {
|
|
81
|
-
allI18n,
|
|
82
|
-
locales
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function loadI18nData(options, compilation) {
|
|
87
|
-
const jsResourcePath = path.resolve(compilation.compiler.context, options.jsResourcePath);
|
|
88
|
-
const propertiesPath = path.resolve(compilation.compiler.context, options.propertiesFolderPath);
|
|
89
|
-
const jsResourceBase = loadPropertiesFile(jsResourcePath, compilation, 'JS resources');
|
|
90
|
-
const {
|
|
91
|
-
allI18n,
|
|
92
|
-
locales
|
|
93
|
-
} = loadAllLocaleFiles(propertiesPath, compilation, jsResourceBase);
|
|
94
|
-
return {
|
|
95
|
-
jsResourceBase,
|
|
96
|
-
allI18n,
|
|
97
|
-
locales
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
module.exports = {
|
|
102
|
-
loadPropertiesFile,
|
|
103
|
-
loadNumericMap,
|
|
104
|
-
loadAllLocaleFiles,
|
|
105
|
-
loadI18nData
|
|
106
|
-
};
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const {
|
|
4
|
-
walk
|
|
5
|
-
} = require('estree-walker');
|
|
6
|
-
|
|
7
|
-
const PREFIX_I18N_COMMENT = 'I18N';
|
|
8
|
-
const PREFIX_I18N_COMMENT_DYNAMIC = 'dynamic-i18n-key';
|
|
9
|
-
|
|
10
|
-
function getI18nKeysFromSingleComment(commentNode, validKeysSet) {
|
|
11
|
-
const foundKeysInComment = [];
|
|
12
|
-
|
|
13
|
-
if (!commentNode || typeof commentNode.value !== 'string') {
|
|
14
|
-
return foundKeysInComment;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const commentString = commentNode.value.trim();
|
|
18
|
-
let i18nKeyStr;
|
|
19
|
-
|
|
20
|
-
if (commentString.startsWith(PREFIX_I18N_COMMENT)) {
|
|
21
|
-
i18nKeyStr = commentString.slice(PREFIX_I18N_COMMENT.length).trim();
|
|
22
|
-
} else if (commentString.startsWith(PREFIX_I18N_COMMENT_DYNAMIC)) {
|
|
23
|
-
i18nKeyStr = commentString.slice(PREFIX_I18N_COMMENT_DYNAMIC.length).trim();
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (!i18nKeyStr) {
|
|
27
|
-
return foundKeysInComment;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const potentialKeys = i18nKeyStr.split(',').map(key => key.trim()).filter(key => key);
|
|
31
|
-
potentialKeys.forEach(key => {
|
|
32
|
-
if (validKeysSet.has(key)) {
|
|
33
|
-
foundKeysInComment.push(key);
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
return foundKeysInComment;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Traverses an AST and its comments to collect and categorize i18n keys.
|
|
40
|
-
*
|
|
41
|
-
* @param {object} astProgramNode - The Program node of the AST.
|
|
42
|
-
* @param {object[]} commentsArray - An array of comment nodes from the AST.
|
|
43
|
-
* @param {object} allI18nKeysMasterMap - Object map of all valid i18n keys (from JSResources).
|
|
44
|
-
* @param {boolean} [isDebug=false] - Flag for verbose logging.
|
|
45
|
-
* @returns {{literalKeys: Set<string>, commentKeys: Set<string>}}
|
|
46
|
-
* literalKeys: Set of valid i18n keys found as string literals.
|
|
47
|
-
* commentKeys: Set of valid i18n keys found in comments.
|
|
48
|
-
*/
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
function collectAndCategorizeUsedI18nKeys(astProgramNode, commentsArray, allI18nKeysMasterMap, isDebug = false) {
|
|
52
|
-
const foundLiteralKeys = new Set();
|
|
53
|
-
const foundCommentKeys = new Set();
|
|
54
|
-
const validKeysSet = new Set(Object.keys(allI18nKeysMasterMap || {}));
|
|
55
|
-
|
|
56
|
-
if (validKeysSet.size === 0 && isDebug) {
|
|
57
|
-
console.warn('[collectAndCategorizeUsedI18nKeys] allI18nKeysMasterMap is empty. No keys can be collected.');
|
|
58
|
-
} // 1. Collect keys from AST string literals
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
if (astProgramNode) {
|
|
62
|
-
try {
|
|
63
|
-
walk(astProgramNode, {
|
|
64
|
-
enter(node) {
|
|
65
|
-
if ((node.type === 'Literal' || node.type === 'StringLiteral') && typeof node.value === 'string') {
|
|
66
|
-
if (validKeysSet.has(node.value)) {
|
|
67
|
-
foundLiteralKeys.add(node.value);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
});
|
|
73
|
-
} catch (error) {
|
|
74
|
-
console.error('[collectAndCategorizeUsedI18nKeys] Error during AST walk:', error);
|
|
75
|
-
}
|
|
76
|
-
} // 2. Collect keys from comments
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (commentsArray && Array.isArray(commentsArray)) {
|
|
80
|
-
commentsArray.forEach(commentNode => {
|
|
81
|
-
const keysFromComment = getI18nKeysFromSingleComment(commentNode, validKeysSet);
|
|
82
|
-
keysFromComment.forEach(key => {
|
|
83
|
-
foundCommentKeys.add(key);
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return {
|
|
89
|
-
literalKeys: foundLiteralKeys,
|
|
90
|
-
commentKeys: foundCommentKeys
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
module.exports = {
|
|
95
|
-
collectAndCategorizeUsedI18nKeys: collectAndCategorizeUsedI18nKeys
|
|
96
|
-
};
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const {
|
|
4
|
-
getPropertiesAsJSON
|
|
5
|
-
} = require('../custom_plugins/I18nSplitPlugin/utils/propertiesUtils');
|
|
6
|
-
|
|
7
|
-
function loadJSResourcesOnce(options) {
|
|
8
|
-
let jsResourcePath;
|
|
9
|
-
|
|
10
|
-
if (options.i18nIndexing && options.i18nIndexing.enable) {
|
|
11
|
-
jsResourcePath = options.i18nIndexing.jsResourcePath;
|
|
12
|
-
} else if (options.i18nChunkSplit && options.i18nChunkSplit.chunkSplitEnable && options.i18nChunkSplit.useNumericIndexing) {
|
|
13
|
-
jsResourcePath = options.i18nChunkSplit.jsResource;
|
|
14
|
-
} else {
|
|
15
|
-
throw new Error('i18nIdReplaceLoader requires either i18nIndexing to be enabled or i18nChunkSplit with useNumericIndexing');
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (!jsResourcePath) {
|
|
19
|
-
throw new Error('Missing required jsResourcePath in i18n options');
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
try {
|
|
23
|
-
const i18nData = getPropertiesAsJSON(jsResourcePath);
|
|
24
|
-
|
|
25
|
-
if (Object.keys(i18nData).length === 0) {
|
|
26
|
-
console.warn(`[i18nIdReplaceLoaderConfig] Warning: No i18n data found in JSResource file: ${jsResourcePath}`);
|
|
27
|
-
return {};
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return i18nData;
|
|
31
|
-
} catch (err) {
|
|
32
|
-
throw new Error(`Error reading JSResource file ${jsResourcePath}: ${err.message}`);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function i18nIdReplaceLoaderConfig(options, webpackContext) {
|
|
37
|
-
let numericMapPath;
|
|
38
|
-
|
|
39
|
-
if (options.i18nIndexing && options.i18nIndexing.enable) {
|
|
40
|
-
numericMapPath = options.i18nIndexing.numericMapPath;
|
|
41
|
-
} else if (options.i18nChunkSplit && options.i18nChunkSplit.chunkSplitEnable && options.i18nChunkSplit.useNumericIndexing) {
|
|
42
|
-
numericMapPath = options.i18nChunkSplit.numericMapPath;
|
|
43
|
-
} else {
|
|
44
|
-
throw new Error('i18nIdReplaceLoader requires either i18nIndexing to be enabled or i18nChunkSplit with useNumericIndexing');
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (!numericMapPath) {
|
|
48
|
-
throw new Error('numericMapPath is required in i18nIndexing or i18nChunkSplit config');
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const allI18nData = loadJSResourcesOnce(options);
|
|
52
|
-
|
|
53
|
-
const i18nKeyReplaceLoaderPath = require.resolve('../loaders/i18nIdReplaceLoader.js');
|
|
54
|
-
|
|
55
|
-
const loaderOptions = {
|
|
56
|
-
allI18nData: allI18nData,
|
|
57
|
-
sourceMaps: false,
|
|
58
|
-
numericMapPath: numericMapPath,
|
|
59
|
-
devMode: options.i18nIndexing?.devMode || false,
|
|
60
|
-
includePaths: options.i18nIndexing?.loaderOptions?.includePaths || [],
|
|
61
|
-
excludePaths: options.i18nIndexing?.loaderOptions?.excludePaths || ['node_modules', 'tests']
|
|
62
|
-
};
|
|
63
|
-
return {
|
|
64
|
-
loader: i18nKeyReplaceLoaderPath,
|
|
65
|
-
options: loaderOptions
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
module.exports = {
|
|
70
|
-
i18nIdReplaceLoaderConfig
|
|
71
|
-
};
|
|
@@ -1,106 +0,0 @@
|
|
|
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 traverse = require('@babel/traverse').default;
|
|
10
|
-
|
|
11
|
-
const generator = require('@babel/generator').default;
|
|
12
|
-
|
|
13
|
-
const t = require('@babel/types');
|
|
14
|
-
|
|
15
|
-
const {
|
|
16
|
-
getOptions
|
|
17
|
-
} = require('loader-utils');
|
|
18
|
-
|
|
19
|
-
module.exports = function i18nIdReplaceLoader(source, map) {
|
|
20
|
-
const resourcePath = this.resourcePath;
|
|
21
|
-
this.cacheable && this.cacheable();
|
|
22
|
-
const options = getOptions(this) || {};
|
|
23
|
-
const callback = this.async();
|
|
24
|
-
|
|
25
|
-
if (options.excludePaths) {
|
|
26
|
-
const shouldExclude = options.excludePaths.some(excludePath => resourcePath.includes(excludePath));
|
|
27
|
-
|
|
28
|
-
if (shouldExclude) {
|
|
29
|
-
return callback(null, source, map);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (options.includePaths && options.includePaths.length > 0) {
|
|
34
|
-
const shouldInclude = options.includePaths.some(includePath => resourcePath.includes(includePath));
|
|
35
|
-
|
|
36
|
-
if (!shouldInclude) {
|
|
37
|
-
return callback(null, source, map);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (!options.allI18nData || Object.keys(options.allI18nData).length === 0) {
|
|
42
|
-
return callback(new Error(`i18nIdReplaceLoader: 'allI18nData' option is missing or empty`));
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
let numericIdMap = null;
|
|
46
|
-
|
|
47
|
-
if (options.numericMapPath) {
|
|
48
|
-
try {
|
|
49
|
-
if (fs.existsSync(options.numericMapPath)) {
|
|
50
|
-
const fileContent = fs.readFileSync(options.numericMapPath, 'utf-8');
|
|
51
|
-
const parsedData = JSON.parse(fileContent);
|
|
52
|
-
|
|
53
|
-
if (parsedData && parsedData.originalKeyToNumericId) {
|
|
54
|
-
numericIdMap = parsedData.originalKeyToNumericId;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
} catch (err) {}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (!numericIdMap) {
|
|
61
|
-
return callback(null, source, map);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const isDevMode = options.devMode || process.env.NODE_ENV === 'development';
|
|
65
|
-
|
|
66
|
-
try {
|
|
67
|
-
const ast = parser.parse(source, {
|
|
68
|
-
sourceType: 'module',
|
|
69
|
-
plugins: ['jsx', 'typescript', 'classProperties', 'optionalChaining', 'nullishCoalescingOperator'],
|
|
70
|
-
sourceFilename: resourcePath
|
|
71
|
-
});
|
|
72
|
-
let hasTransformations = false;
|
|
73
|
-
traverse(ast, {
|
|
74
|
-
StringLiteral(path) {
|
|
75
|
-
const {
|
|
76
|
-
node
|
|
77
|
-
} = path;
|
|
78
|
-
|
|
79
|
-
if (!options.allI18nData.hasOwnProperty(node.value)) {
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (numericIdMap.hasOwnProperty(node.value)) {
|
|
84
|
-
const numericId = String(numericIdMap[node.value]);
|
|
85
|
-
path.replaceWith(t.stringLiteral(numericId));
|
|
86
|
-
hasTransformations = true;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
if (hasTransformations) {
|
|
93
|
-
const output = generator(ast, {
|
|
94
|
-
sourceMaps: !!options.sourceMaps,
|
|
95
|
-
sourceFileName: resourcePath,
|
|
96
|
-
retainLines: false,
|
|
97
|
-
comments: true
|
|
98
|
-
}, source);
|
|
99
|
-
callback(null, output.code, output.map);
|
|
100
|
-
} else {
|
|
101
|
-
callback(null, source, map);
|
|
102
|
-
}
|
|
103
|
-
} catch (err) {
|
|
104
|
-
callback(err);
|
|
105
|
-
}
|
|
106
|
-
};
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.configI18nNumericIndexPlugin = configI18nNumericIndexPlugin;
|
|
7
|
-
|
|
8
|
-
var _I18nNumericIndexPlugin = require("../custom_plugins/I18nNumericIndexPlugin/I18nNumericIndexPlugin");
|
|
9
|
-
|
|
10
|
-
var _I18nNumericIndexHtmlInjectorPlugin = require("../custom_plugins/I18nNumericIndexPlugin/I18nNumericIndexHtmlInjectorPlugin");
|
|
11
|
-
|
|
12
|
-
var _readI18nValues = require("../custom_plugins/I18nSplitPlugin/readI18nValues");
|
|
13
|
-
|
|
14
|
-
function configI18nNumericIndexPlugin(options) {
|
|
15
|
-
if (!(options.i18nIndexing && options.i18nIndexing.enable)) {
|
|
16
|
-
return null;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const i18nOpts = options.i18nIndexing; // Check for required options based on singleFile mode
|
|
20
|
-
|
|
21
|
-
const baseRequiredOptions = ['jsResourcePath', 'propertiesFolderPath', 'numericMapPath', 'jsonpFunc', 'htmlTemplateLabel', 'localeVarName']; // Add template requirements based on mode
|
|
22
|
-
|
|
23
|
-
const requiredOptions = i18nOpts.singleFile ? [...baseRequiredOptions, 'singleFileTemplate'] : [...baseRequiredOptions, 'numericFilenameTemplate', 'dynamicFilenameTemplate'];
|
|
24
|
-
const missingOptions = requiredOptions.filter(opt => !i18nOpts[opt]);
|
|
25
|
-
|
|
26
|
-
if (missingOptions.length > 0) {
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const {
|
|
31
|
-
locales,
|
|
32
|
-
allI18nObject
|
|
33
|
-
} = (0, _readI18nValues.readI18nValues)({
|
|
34
|
-
jsResource: i18nOpts.jsResourcePath,
|
|
35
|
-
propertiesFolder: i18nOpts.propertiesFolderPath,
|
|
36
|
-
disableDefault: false
|
|
37
|
-
}); // Validate template patterns based on mode
|
|
38
|
-
|
|
39
|
-
if (i18nOpts.singleFile) {
|
|
40
|
-
if (i18nOpts.singleFileTemplate && !i18nOpts.singleFileTemplate.includes('[locale]')) {
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
} else {
|
|
44
|
-
if (!i18nOpts.numericFilenameTemplate?.includes('[locale]')) {
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (!i18nOpts.dynamicFilenameTemplate?.includes('[locale]')) {
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const i18nAssetsPublicPathPrefix = '';
|
|
54
|
-
const hasContentHashInNumeric = i18nOpts.numericFilenameTemplate.includes('[contenthash]');
|
|
55
|
-
const hasContentHashInDynamic = i18nOpts.dynamicFilenameTemplate.includes('[contenthash]');
|
|
56
|
-
const hasContentHashInSingle = i18nOpts.singleFileTemplate?.includes('[contenthash]');
|
|
57
|
-
const shouldIncludeContentHash = i18nOpts.singleFile ? hasContentHashInSingle : hasContentHashInNumeric || hasContentHashInDynamic;
|
|
58
|
-
const finalIncludeContentHash = i18nOpts.includeContentHash !== undefined ? i18nOpts.includeContentHash : shouldIncludeContentHash;
|
|
59
|
-
const numericIndexPluginOptions = {
|
|
60
|
-
enable: i18nOpts.enable,
|
|
61
|
-
jsResourcePath: i18nOpts.jsResourcePath,
|
|
62
|
-
propertiesFolderPath: i18nOpts.propertiesFolderPath,
|
|
63
|
-
numericMapPath: i18nOpts.numericMapPath,
|
|
64
|
-
locales,
|
|
65
|
-
allI18nObject,
|
|
66
|
-
numericFilenameTemplate: i18nOpts.numericFilenameTemplate,
|
|
67
|
-
dynamicFilenameTemplate: i18nOpts.dynamicFilenameTemplate,
|
|
68
|
-
singleFileTemplate: i18nOpts.singleFileTemplate,
|
|
69
|
-
jsonpFunc: i18nOpts.jsonpFunc,
|
|
70
|
-
singleFile: i18nOpts.singleFile || false,
|
|
71
|
-
includeContentHash: finalIncludeContentHash,
|
|
72
|
-
generateManifest: i18nOpts.generateManifest || false,
|
|
73
|
-
manifestPath: i18nOpts.manifestPath
|
|
74
|
-
};
|
|
75
|
-
const htmlInjectorOptions = {
|
|
76
|
-
numericFilenameTemplate: i18nOpts.numericFilenameTemplate,
|
|
77
|
-
dynamicFilenameTemplate: i18nOpts.dynamicFilenameTemplate,
|
|
78
|
-
htmlTemplateLabel: i18nOpts.htmlTemplateLabel,
|
|
79
|
-
i18nAssetsPublicPathPrefix
|
|
80
|
-
};
|
|
81
|
-
const i18nNumericPluginInstance = new _I18nNumericIndexPlugin.I18nNumericIndexPlugin(numericIndexPluginOptions);
|
|
82
|
-
const htmlInjectorPluginInstance = new _I18nNumericIndexHtmlInjectorPlugin.I18nNumericIndexHtmlInjectorPlugin(htmlInjectorOptions);
|
|
83
|
-
return [i18nNumericPluginInstance, htmlInjectorPluginInstance];
|
|
84
|
-
}
|