@zohodesk/client_build_tool 0.0.13 → 0.0.14

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,478 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+
8
+ var _fs = _interopRequireDefault(require("fs"));
9
+
10
+ var _path = _interopRequireDefault(require("path"));
11
+
12
+ var _webpack = require("webpack");
13
+
14
+ var _propertiesUtils = require("../I18nSplitPlugin/utils/propertiesUtils");
15
+
16
+ var _I18nGroupRuntimeModule = require("./I18nGroupRuntimeModule");
17
+
18
+ var _i18nDataLoader = require("./utils/i18nDataLoader.js");
19
+
20
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
21
+
22
+ const {
23
+ RawSource
24
+ } = _webpack.sources;
25
+ const assetStoreKey = Symbol.for('I18nNumericIndexPluginAssets');
26
+ const pluginName = 'I18nNumericIndexPlugin';
27
+
28
+ function buildChunkMappingFromGroups(customGroups) {
29
+ if (!customGroups) {
30
+ return {};
31
+ }
32
+
33
+ const mapping = {};
34
+ Object.entries(customGroups).forEach(([groupName, config]) => {
35
+ if (config && Array.isArray(config.chunks)) {
36
+ config.chunks.forEach(chunkName => {
37
+ if (chunkName && typeof chunkName === 'string') {
38
+ mapping[chunkName] = groupName;
39
+ }
40
+ });
41
+ }
42
+ });
43
+ return mapping;
44
+ }
45
+
46
+ class I18nNumericIndexPlugin {
47
+ constructor(options) {
48
+ this.options = { ...options,
49
+ singleFile: options.singleFile || false,
50
+ singleFileTemplate: options.singleFileTemplate || '[locale].js',
51
+ includeContentHash: options.includeContentHash || false,
52
+ generateManifest: options.generateManifest || false,
53
+ outputFolder: options.outputFolder || 'i18n-chunk',
54
+ manifestPath: options.manifestPath || null,
55
+ emitFiles: options.emitFiles !== undefined ? options.emitFiles : true,
56
+ chunkToGroupMapping: options.chunkToGroupMapping || {},
57
+ groupPublicPathPrefix: options.groupPublicPathPrefix || '',
58
+ groupPublicPathRuntimeExpression: options.groupPublicPathRuntimeExpression || ''
59
+ };
60
+ this.numericMap = {};
61
+ this.customGroups = {};
62
+ this.manifest = {};
63
+ this.groupAssetUrls = {};
64
+ this.preparedAssets = [];
65
+ this.assetsPrepared = false;
66
+ this.assetsEmitted = false;
67
+ this.groupChunkMapping = buildChunkMappingFromGroups(this.options.customGroups);
68
+ }
69
+
70
+ apply(compiler) {
71
+ this.detectI18nGroupComments(compiler);
72
+ compiler.hooks.thisCompilation.tap(pluginName, compilation => {
73
+ this.groupAssetUrls = {};
74
+ this.detectedGroups = {};
75
+ this.preparedAssets = [];
76
+ this.assetsPrepared = false;
77
+ this.assetsEmitted = false;
78
+
79
+ if (this.options.customGroups) {
80
+ compilation.hooks.additionalTreeRuntimeRequirements.tap(pluginName, (chunk, set) => {
81
+ if (chunk.name === 'main' || chunk.hasRuntime()) {
82
+ this.ensureAssetsPrepared(compilation);
83
+ const chunkMapping = this.getChunkIdToGroupMapping(compilation);
84
+ compilation.addRuntimeModule(chunk, new _I18nGroupRuntimeModule.I18nGroupRuntimeModule({
85
+ customGroups: this.options.customGroups,
86
+ chunkIdToGroupMapping: chunkMapping,
87
+ localeVarName: this.options.localeVarName,
88
+ jsonpFunc: this.options.jsonpFunc,
89
+ groupAssetUrls: this.groupAssetUrls,
90
+ includeContentHash: this.options.includeContentHash,
91
+ publicPathPrefix: this.options.groupPublicPathPrefix,
92
+ publicPathRuntimeExpression: this.options.groupPublicPathRuntimeExpression
93
+ }));
94
+ }
95
+ });
96
+ }
97
+
98
+ compilation.hooks.processAssets.tap({
99
+ name: pluginName,
100
+ stage: compilation.PROCESS_ASSETS_STAGE_OPTIMIZE
101
+ }, () => {
102
+ this.ensureAssetsPrepared(compilation);
103
+ this.emitPreparedAssets(compilation);
104
+ });
105
+ });
106
+ }
107
+
108
+ detectI18nGroupComments(compiler) {
109
+ compiler.hooks.normalModuleFactory.tap(pluginName, factory => {
110
+ factory.hooks.parser.for('javascript/auto').tap(pluginName, parser => {
111
+ parser.hooks.importCall.tap(pluginName, expr => {
112
+ const comments = expr.leadingComments || [];
113
+
114
+ if (comments.length > 0) {
115
+ comments.forEach(comment => {
116
+ if (comment.value && comment.value.includes('webpackI18nGroup')) {
117
+ const match = comment.value.match(/webpackI18nGroup:\s*["']([^"']+)["']/);
118
+
119
+ if (match) {
120
+ const groupName = match[1];
121
+
122
+ if (!this.detectedGroups) {
123
+ this.detectedGroups = {};
124
+ }
125
+
126
+ const chunkNameMatch = comment.value.match(/webpackChunkName:\s*["']([^"']+)["']/);
127
+
128
+ if (chunkNameMatch) {
129
+ const chunkName = chunkNameMatch[1];
130
+ this.detectedGroups[chunkName] = groupName;
131
+ }
132
+ }
133
+ }
134
+ });
135
+ }
136
+ });
137
+ });
138
+ });
139
+ }
140
+
141
+ getChunkIdToGroupMapping(compilation) {
142
+ const chunkIdToGroup = {};
143
+ const configuredMappings = { ...this.groupChunkMapping,
144
+ ...(this.options.chunkToGroupMapping || {})
145
+ };
146
+
147
+ for (const chunk of compilation.chunks) {
148
+ const chunkName = chunk.name;
149
+ let groupName = null;
150
+
151
+ if (this.detectedGroups && this.detectedGroups[chunkName]) {
152
+ groupName = this.detectedGroups[chunkName];
153
+ } else if (configuredMappings[chunkName]) {
154
+ groupName = configuredMappings[chunkName];
155
+ }
156
+
157
+ if (groupName) {
158
+ chunkIdToGroup[chunk.id] = groupName;
159
+ }
160
+ }
161
+
162
+ return chunkIdToGroup;
163
+ }
164
+
165
+ processI18nFiles(compilation) {
166
+ this.preparedAssets = [];
167
+ this.groupAssetUrls = {};
168
+ const {
169
+ jsResourcePath,
170
+ propertiesFolderPath,
171
+ numericMapPath,
172
+ customGroups,
173
+ jsonpFunc,
174
+ numericFilenameTemplate,
175
+ dynamicFilenameTemplate
176
+ } = this.options;
177
+
178
+ if (!jsResourcePath || !propertiesFolderPath) {
179
+ return;
180
+ } // Reset caches for incremental builds
181
+
182
+
183
+ this.numericMap = {};
184
+ this.manifest = {};
185
+ this.customGroups = {};
186
+
187
+ if (numericMapPath) {
188
+ const mapData = (0, _i18nDataLoader.loadNumericMap)(numericMapPath, compilation);
189
+
190
+ if (mapData && mapData.sortedKeys) {
191
+ mapData.sortedKeys.forEach((key, id) => {
192
+ if (key) {
193
+ this.numericMap[key] = id;
194
+ }
195
+ });
196
+ }
197
+ }
198
+
199
+ let jsResourceKeys = (0, _propertiesUtils.getPropertiesAsJSON)(jsResourcePath);
200
+ jsResourceKeys = this.normalizeObjectValues(jsResourceKeys);
201
+
202
+ if (customGroups) {
203
+ this.parseCustomGroups(jsResourcePath, customGroups);
204
+ }
205
+
206
+ const allI18nObject = (0, _propertiesUtils.getAllI18n)({
207
+ folderPath: propertiesFolderPath,
208
+ disableDefault: false,
209
+ jsResourceI18nKeys: jsResourceKeys
210
+ });
211
+ Object.keys(allI18nObject).forEach(loc => {
212
+ allI18nObject[loc] = this.normalizeObjectValues(allI18nObject[loc]);
213
+ });
214
+ allI18nObject['en_US'] = jsResourceKeys;
215
+
216
+ if (this.options.restrictToBaseKeys) {
217
+ const baseKeys = Object.keys(jsResourceKeys);
218
+ Object.keys(allI18nObject).forEach(locale => {
219
+ const merged = { ...jsResourceKeys,
220
+ ...allI18nObject[locale]
221
+ };
222
+ const filtered = {};
223
+ baseKeys.forEach(k => {
224
+ filtered[k] = merged[k];
225
+ });
226
+ allI18nObject[locale] = filtered;
227
+ });
228
+ }
229
+
230
+ const locales = Object.keys(allI18nObject);
231
+ locales.forEach(locale => {
232
+ const localeData = allI18nObject[locale];
233
+ const numericData = {};
234
+ const dynamicData = {};
235
+ const groupData = {};
236
+ Object.keys(customGroups || {}).forEach(groupName => {
237
+ groupData[groupName] = {};
238
+ }); // Process each key
239
+
240
+ Object.keys(localeData).forEach(key => {
241
+ const value = localeData[key]; // Simple logic: if has numeric ID use it, otherwise it's dynamic
242
+
243
+ const numericId = this.numericMap[key];
244
+ const hasNumericId = numericId !== undefined && numericId !== null;
245
+
246
+ if (hasNumericId) {
247
+ const numericKey = String(numericId); // Check if belongs to a custom group
248
+
249
+ const belongsToGroup = this.getKeyGroup(key);
250
+
251
+ if (belongsToGroup) {
252
+ groupData[belongsToGroup][numericKey] = value;
253
+ } else {
254
+ numericData[numericKey] = value;
255
+ }
256
+ } else {
257
+ // No numeric ID = dynamic key
258
+ dynamicData[key] = value;
259
+ }
260
+ }); // Handle single-file mode or separate files
261
+
262
+ if (this.options.singleFile) {
263
+ const combinedData = { ...numericData,
264
+ ...dynamicData
265
+ };
266
+
267
+ if (Object.keys(combinedData).length > 0) {
268
+ const singleFileTemplate = this.options.singleFileTemplate || '[locale].js';
269
+ this.prepareChunkAsset(compilation, singleFileTemplate, locale, combinedData, jsonpFunc, null, null);
270
+ }
271
+ } else {
272
+ // Emit numeric chunk
273
+ if (Object.keys(numericData).length > 0) {
274
+ this.prepareChunkAsset(compilation, numericFilenameTemplate, locale, numericData, jsonpFunc, null, 'numeric');
275
+ } // Emit dynamic chunk
276
+
277
+
278
+ if (Object.keys(dynamicData).length > 0) {
279
+ this.prepareChunkAsset(compilation, dynamicFilenameTemplate, locale, dynamicData, jsonpFunc, null, 'dynamic');
280
+ }
281
+ } // Emit custom group chunks (always separate)
282
+
283
+
284
+ Object.entries(groupData).forEach(([groupName, data]) => {
285
+ const groupConfig = customGroups[groupName];
286
+
287
+ if (groupConfig && Object.keys(data).length > 0) {
288
+ const groupTemplate = groupConfig.filenameTemplate || `[locale]/${groupName}.i18n.js`;
289
+ this.prepareChunkAsset(compilation, groupTemplate, locale, data, jsonpFunc, groupName, `group-${groupName}`);
290
+ }
291
+ });
292
+ }); // Generate manifest file if enabled
293
+
294
+ if (this.options.generateManifest && Object.keys(this.manifest).length > 0) {
295
+ const manifestPath = this.options.manifestPath || _path.default.join(this.options.outputFolder, 'manifest.json');
296
+
297
+ const manifestContent = JSON.stringify(this.manifest, null, 2);
298
+ this.preparedAssets.push({
299
+ outputPath: manifestPath,
300
+ source: new RawSource(manifestContent),
301
+ shouldEmit: true
302
+ });
303
+ }
304
+ }
305
+
306
+ ensureAssetsPrepared(compilation) {
307
+ if (this.assetsPrepared) {
308
+ return;
309
+ }
310
+
311
+ this.processI18nFiles(compilation);
312
+ this.assetsPrepared = true;
313
+ }
314
+
315
+ emitPreparedAssets(compilation) {
316
+ if (this.assetsEmitted) {
317
+ return;
318
+ }
319
+
320
+ this.preparedAssets.forEach(asset => {
321
+ if (asset.shouldEmit !== false) {
322
+ compilation.emitAsset(asset.outputPath, asset.source);
323
+ }
324
+ });
325
+ this.assetsEmitted = true;
326
+ }
327
+
328
+ parseCustomGroups(jsResourcePath, customGroups) {
329
+ const content = _fs.default.readFileSync(jsResourcePath, 'utf-8');
330
+
331
+ const lines = content.split('\n');
332
+ Object.entries(customGroups).forEach(([groupName, config]) => {
333
+ const {
334
+ bannerStart,
335
+ bannerEnd
336
+ } = config;
337
+ let inGroup = false;
338
+ const groupKeys = [];
339
+ lines.forEach(line => {
340
+ if (line.includes(bannerStart)) {
341
+ inGroup = true;
342
+ } else if (line.includes(bannerEnd)) {
343
+ inGroup = false;
344
+ } else if (inGroup && line.includes('=')) {
345
+ const key = line.split('=')[0].trim();
346
+
347
+ if (key && !key.startsWith('#')) {
348
+ groupKeys.push(key);
349
+ }
350
+ }
351
+ });
352
+ this.customGroups[groupName] = groupKeys;
353
+ });
354
+ }
355
+
356
+ getKeyGroup(key) {
357
+ for (const [groupName, keys] of Object.entries(this.customGroups)) {
358
+ if (keys.includes(key)) {
359
+ return groupName;
360
+ }
361
+ }
362
+
363
+ return null;
364
+ }
365
+
366
+ generateContentHash(content, compilation) {
367
+ const {
368
+ hashFunction,
369
+ hashDigest,
370
+ hashDigestLength
371
+ } = compilation.outputOptions;
372
+
373
+ const hash = _webpack.util.createHash(hashFunction || 'xxhash64');
374
+
375
+ hash.update(content);
376
+ return hash.digest(hashDigest || 'hex').substring(0, hashDigestLength || 8);
377
+ }
378
+
379
+ constructFilePath(template, locale) {
380
+ const {
381
+ outputFolder
382
+ } = this.options;
383
+ const filePath = template.replace(/\[locale\]/g, locale);
384
+
385
+ if (filePath.includes(outputFolder) || filePath.startsWith('/')) {
386
+ return filePath;
387
+ }
388
+
389
+ return _path.default.join(outputFolder, filePath).replace(/\\/g, '/');
390
+ }
391
+
392
+ prepareChunkAsset(compilation, filenameTemplate, locale, data, jsonpFunc, groupName = null, fileType = null) {
393
+ if (!filenameTemplate || Object.keys(data).length === 0) {
394
+ return null;
395
+ }
396
+
397
+ const content = this.generateChunkContent(data, jsonpFunc, groupName);
398
+ let outputPath = this.constructFilePath(filenameTemplate, locale); // Handle [contenthash] placeholder in template
399
+
400
+ if (outputPath.includes('[contenthash]')) {
401
+ const contentHash = this.generateContentHash(content, compilation);
402
+ outputPath = outputPath.replace(/\[contenthash\]/g, contentHash);
403
+ } else if (this.options.includeContentHash) {
404
+ const contentHash = this.generateContentHash(content, compilation);
405
+ outputPath = outputPath.replace(/\.js$/, `.${contentHash}.js`);
406
+ } // Track in manifest if enabled
407
+
408
+
409
+ if (this.options.generateManifest) {
410
+ const manifestKey = this.options.singleFile ? `${locale}.js` : outputPath;
411
+ this.manifest[manifestKey] = _path.default.basename(outputPath);
412
+ }
413
+
414
+ const source = new RawSource(content);
415
+ const assetInfo = {
416
+ locale,
417
+ fileType,
418
+ outputPath,
419
+ filenameTemplate
420
+ };
421
+ this.registerEmittedAsset(compilation, assetInfo);
422
+ this.preparedAssets.push({
423
+ outputPath,
424
+ source,
425
+ shouldEmit: this.options.emitFiles,
426
+ info: assetInfo
427
+ });
428
+ return outputPath;
429
+ }
430
+
431
+ registerEmittedAsset(compilation, assetInfo) {
432
+ if (!Object.prototype.hasOwnProperty.call(compilation, assetStoreKey)) {
433
+ Object.defineProperty(compilation, assetStoreKey, {
434
+ configurable: true,
435
+ enumerable: false,
436
+ value: []
437
+ });
438
+ }
439
+
440
+ compilation[assetStoreKey].push(assetInfo);
441
+
442
+ if (assetInfo.fileType && assetInfo.fileType.startsWith('group-')) {
443
+ const groupName = assetInfo.fileType.slice('group-'.length);
444
+
445
+ if (!this.groupAssetUrls[groupName]) {
446
+ this.groupAssetUrls[groupName] = {};
447
+ }
448
+
449
+ this.groupAssetUrls[groupName][assetInfo.locale] = assetInfo.outputPath;
450
+ }
451
+ }
452
+
453
+ generateChunkContent(data, jsonpFunc, groupName) {
454
+ const jsonString = (0, _propertiesUtils.decodeUnicodeEscapes)(JSON.stringify(data));
455
+
456
+ if (groupName) {
457
+ return `${jsonpFunc}(${jsonString}, "${groupName}");`;
458
+ }
459
+
460
+ return `${jsonpFunc}(${jsonString});`;
461
+ }
462
+
463
+ normalizeValue(value) {
464
+ if (typeof value !== 'string') return value;
465
+ return value.replace(/\\t/g, '\t').replace(/\\n/g, '\n').replace(/\\r/g, '\r').replace(/\\f/g, '\f').replace(/\\:/g, ':').replace(/\\=/g, '=').replace(/\\ /g, ' ');
466
+ }
467
+
468
+ normalizeObjectValues(obj) {
469
+ const out = {};
470
+ Object.keys(obj || {}).forEach(k => {
471
+ out[k] = this.normalizeValue(obj[k]);
472
+ });
473
+ return out;
474
+ }
475
+
476
+ }
477
+
478
+ exports.default = I18nNumericIndexPlugin;
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.loadAllLocaleFiles = loadAllLocaleFiles;
7
+ exports.loadI18nData = loadI18nData;
8
+ exports.loadNumericMap = loadNumericMap;
9
+ exports.loadPropertiesFile = loadPropertiesFile;
10
+
11
+ var _fs = _interopRequireDefault(require("fs"));
12
+
13
+ var _path = _interopRequireDefault(require("path"));
14
+
15
+ var _propertiesUtils = require("../../I18nSplitPlugin/utils/propertiesUtils.js");
16
+
17
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
18
+
19
+ function loadPropertiesFile(filePath, compilation, description) {
20
+ try {
21
+ return (0, _propertiesUtils.getPropertiesAsJSON)(filePath);
22
+ } catch (err) {
23
+ if (compilation) {
24
+ compilation.errors.push(new Error(`I18nNumericIndexPlugin: Error loading ${description}: ${err.message}`));
25
+ }
26
+
27
+ return {};
28
+ }
29
+ }
30
+
31
+ function loadNumericMap(numericMapPath, compilation) {
32
+ try {
33
+ const fileContent = _fs.default.readFileSync(numericMapPath, 'utf-8');
34
+
35
+ const parsedData = JSON.parse(fileContent);
36
+ const numericMap = parsedData.originalKeyToNumericId || parsedData;
37
+ const totalKeys = parsedData.totalKeysInMap || Object.keys(numericMap).length;
38
+ const values = Object.values(numericMap);
39
+ const maxId = Math.max(...values.map(id => typeof id === 'number' ? id : Number(id)));
40
+ const sortedKeys = new Array(maxId + 1);
41
+ Object.entries(numericMap).forEach(([key, id]) => {
42
+ sortedKeys[id] = key;
43
+ });
44
+ return {
45
+ sortedKeys,
46
+ totalKeys
47
+ };
48
+ } catch (err) {
49
+ if (compilation) {
50
+ compilation.errors.push(new Error(`I18nNumericIndexPlugin: Error loading numeric map: ${err.message}`));
51
+ }
52
+
53
+ return {
54
+ sortedKeys: [],
55
+ totalKeys: 0
56
+ };
57
+ }
58
+ }
59
+
60
+ function loadAllLocaleFiles(propertiesPath, compilation, jsResourceBase) {
61
+ const allI18n = {
62
+ en_US: jsResourceBase
63
+ };
64
+ const locales = ['en_US'];
65
+
66
+ try {
67
+ const files = _fs.default.readdirSync(propertiesPath);
68
+
69
+ files.forEach(file => {
70
+ if (!file.endsWith('.properties')) return;
71
+ const match = file.match(/^ApplicationResources_([a-z]{2}_[A-Z]{2})\.properties$/);
72
+
73
+ if (match) {
74
+ const locale = match[1];
75
+
76
+ const filePath = _path.default.join(propertiesPath, file);
77
+
78
+ const localeData = loadPropertiesFile(filePath, compilation, `locale ${locale}`);
79
+ allI18n[locale] = { ...jsResourceBase,
80
+ ...localeData
81
+ };
82
+
83
+ if (!locales.includes(locale)) {
84
+ locales.push(locale);
85
+ }
86
+ }
87
+ });
88
+ } catch (err) {
89
+ if (compilation) {
90
+ compilation.errors.push(new Error(`I18nNumericIndexPlugin: Error reading properties folder: ${err.message}`));
91
+ }
92
+ }
93
+
94
+ return {
95
+ allI18n,
96
+ locales
97
+ };
98
+ }
99
+
100
+ function loadI18nData(options, compilation) {
101
+ const jsResourcePath = _path.default.resolve(compilation.compiler.context, options.jsResourcePath);
102
+
103
+ const propertiesPath = _path.default.resolve(compilation.compiler.context, options.propertiesFolderPath);
104
+
105
+ const jsResourceBase = loadPropertiesFile(jsResourcePath, compilation, 'JS resources');
106
+ const {
107
+ allI18n,
108
+ locales
109
+ } = loadAllLocaleFiles(propertiesPath, compilation, jsResourceBase);
110
+ return {
111
+ jsResourceBase,
112
+ allI18n,
113
+ locales
114
+ };
115
+ }
@@ -16,13 +16,28 @@ class InitialHtmlPlugin {
16
16
  this.options = options;
17
17
  }
18
18
 
19
+ getBaseName(str) {
20
+ const fileName = str.split("/").pop();
21
+ const parts = fileName.split("."); // Find index of the hash-like segment
22
+
23
+ const hashIndex = parts.findIndex(p => /^[0-9a-f_]{20,}$/i.test(p)); // If a hash is found, return everything before it
24
+
25
+ if (hashIndex < 0) {
26
+ console.error('this initial file dont have a hash');
27
+ return parts.slice(0, -1).join(".");
28
+ }
29
+
30
+ return parts.slice(0, hashIndex).join("."); // Otherwise fallback: remove only ".js"
31
+ }
32
+
19
33
  apply(compiler) {
20
34
  const {
21
35
  filename,
22
36
  template,
23
37
  minify,
24
38
  inject,
25
- mainChunkName
39
+ mainChunkName,
40
+ enableSubResourceIntegrity
26
41
  } = this.options;
27
42
  new _htmlWebpackPlugin.default({
28
43
  filename,
@@ -37,13 +52,36 @@ class InitialHtmlPlugin {
37
52
  const headTags = [];
38
53
  const bodyTags = [];
39
54
  data.headTags.forEach(tag => {
55
+ const url = tag.attributes?.src || tag.attributes?.href;
56
+ let chunkName;
57
+
58
+ const addIntegrity = (tag, suffix) => {
59
+ if (enableSubResourceIntegrity && chunkName) {
60
+ Object.assign(tag.attributes, {
61
+ integrity: `{{--${chunkName}-${suffix}-integrity}}`
62
+ });
63
+ }
64
+ };
65
+
66
+ if (enableSubResourceIntegrity) {
67
+ chunkName = this.getBaseName(url);
68
+ }
69
+
40
70
  Object.assign(tag.attributes, {
41
- nonce: '{{--CSP-nonce}}'
71
+ nonce: '{{--CSP-nonce}}',
72
+ crossorigin: "anonymous"
42
73
  });
43
74
 
44
75
  if (tag.tagName === 'link') {
76
+ addIntegrity(tag, 'css');
45
77
  headTags.push(tag);
46
78
  } else {
79
+ if (url.endsWith('.i18n.js')) {
80
+ addIntegrity(tag, 'i18n');
81
+ } else {
82
+ addIntegrity(tag, 'js');
83
+ }
84
+
47
85
  bodyTags.push(tag);
48
86
  }
49
87
  });