[6a3a178] | 1 | "use strict";
|
---|
| 2 | /**
|
---|
| 3 | * @license
|
---|
| 4 | * Copyright Google LLC All Rights Reserved.
|
---|
| 5 | *
|
---|
| 6 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 7 | * found in the LICENSE file at https://angular.io/license
|
---|
| 8 | */
|
---|
| 9 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
---|
| 10 | if (k2 === undefined) k2 = k;
|
---|
| 11 | Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
---|
| 12 | }) : (function(o, m, k, k2) {
|
---|
| 13 | if (k2 === undefined) k2 = k;
|
---|
| 14 | o[k2] = m[k];
|
---|
| 15 | }));
|
---|
| 16 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
---|
| 17 | Object.defineProperty(o, "default", { enumerable: true, value: v });
|
---|
| 18 | }) : function(o, v) {
|
---|
| 19 | o["default"] = v;
|
---|
| 20 | });
|
---|
| 21 | var __importStar = (this && this.__importStar) || function (mod) {
|
---|
| 22 | if (mod && mod.__esModule) return mod;
|
---|
| 23 | var result = {};
|
---|
| 24 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
---|
| 25 | __setModuleDefault(result, mod);
|
---|
| 26 | return result;
|
---|
| 27 | };
|
---|
| 28 | var __importDefault = (this && this.__importDefault) || function (mod) {
|
---|
| 29 | return (mod && mod.__esModule) ? mod : { "default": mod };
|
---|
| 30 | };
|
---|
| 31 | Object.defineProperty(exports, "__esModule", { value: true });
|
---|
| 32 | exports.inlineLocales = exports.createI18nPlugins = exports.process = void 0;
|
---|
| 33 | const remapping_1 = __importDefault(require("@ampproject/remapping"));
|
---|
| 34 | const core_1 = require("@babel/core");
|
---|
| 35 | const template_1 = __importDefault(require("@babel/template"));
|
---|
| 36 | const cacache = __importStar(require("cacache"));
|
---|
| 37 | const crypto_1 = require("crypto");
|
---|
| 38 | const fs = __importStar(require("fs"));
|
---|
| 39 | const path = __importStar(require("path"));
|
---|
| 40 | const terser_1 = require("terser");
|
---|
| 41 | const worker_threads_1 = require("worker_threads");
|
---|
| 42 | const environment_options_1 = require("./environment-options");
|
---|
| 43 | // Lazy loaded webpack-sources object
|
---|
| 44 | // Webpack is only imported if needed during the processing
|
---|
| 45 | let webpackSources;
|
---|
| 46 | // If code size is larger than 500KB, consider lower fidelity but faster sourcemap merge
|
---|
| 47 | const FAST_SOURCEMAP_THRESHOLD = 500 * 1024;
|
---|
| 48 | const { cachePath, i18n } = (worker_threads_1.workerData || {});
|
---|
| 49 | async function cachePut(content, key, integrity) {
|
---|
| 50 | if (cachePath && key) {
|
---|
| 51 | await cacache.put(cachePath, key, content, {
|
---|
| 52 | metadata: { integrity },
|
---|
| 53 | });
|
---|
| 54 | }
|
---|
| 55 | }
|
---|
| 56 | async function process(options) {
|
---|
| 57 | var _a;
|
---|
| 58 | if (!options.cacheKeys) {
|
---|
| 59 | options.cacheKeys = [];
|
---|
| 60 | }
|
---|
| 61 | const result = { name: options.name };
|
---|
| 62 | if (options.integrityAlgorithm) {
|
---|
| 63 | // Store unmodified code integrity value -- used for SRI value replacement
|
---|
| 64 | result.integrity = generateIntegrityValue(options.integrityAlgorithm, options.code);
|
---|
| 65 | }
|
---|
| 66 | // Runtime chunk requires specialized handling
|
---|
| 67 | if (options.runtime) {
|
---|
| 68 | return { ...result, ...(await processRuntime(options)) };
|
---|
| 69 | }
|
---|
| 70 | const basePath = path.dirname(options.filename);
|
---|
| 71 | const filename = path.basename(options.filename);
|
---|
| 72 | const downlevelFilename = filename.replace(/\-(es20\d{2}|esnext)/, '-es5');
|
---|
| 73 | const downlevel = !options.optimizeOnly;
|
---|
| 74 | const sourceCode = options.code;
|
---|
| 75 | if (downlevel) {
|
---|
| 76 | const { supportedBrowsers: targets = [] } = options;
|
---|
| 77 | // todo: revisit this in version 10, when we update our defaults browserslist
|
---|
| 78 | // Without this workaround bundles will not be downlevelled because Babel doesn't know handle to 'op_mini all'
|
---|
| 79 | // See: https://github.com/babel/babel/issues/11155
|
---|
| 80 | if (Array.isArray(targets) && targets.includes('op_mini all')) {
|
---|
| 81 | targets.push('ie_mob 11');
|
---|
| 82 | }
|
---|
| 83 | else if ('op_mini' in targets) {
|
---|
| 84 | targets['ie_mob'] = '11';
|
---|
| 85 | }
|
---|
| 86 | // Downlevel the bundle
|
---|
| 87 | const transformResult = await core_1.transformAsync(sourceCode, {
|
---|
| 88 | filename,
|
---|
| 89 | // using false ensures that babel will NOT search and process sourcemap comments (large memory usage)
|
---|
| 90 | // The types do not include the false option even though it is valid
|
---|
| 91 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
---|
| 92 | inputSourceMap: false,
|
---|
| 93 | babelrc: false,
|
---|
| 94 | configFile: false,
|
---|
| 95 | presets: [
|
---|
| 96 | [
|
---|
| 97 | require.resolve('@babel/preset-env'),
|
---|
| 98 | {
|
---|
| 99 | // browserslist-compatible query or object of minimum environment versions to support
|
---|
| 100 | targets,
|
---|
| 101 | // modules aren't needed since the bundles use webpack's custom module loading
|
---|
| 102 | modules: false,
|
---|
| 103 | // 'transform-typeof-symbol' generates slower code
|
---|
| 104 | exclude: ['transform-typeof-symbol'],
|
---|
| 105 | },
|
---|
| 106 | ],
|
---|
| 107 | ],
|
---|
| 108 | plugins: [
|
---|
| 109 | createIifeWrapperPlugin(),
|
---|
| 110 | ...(options.replacements ? [createReplacePlugin(options.replacements)] : []),
|
---|
| 111 | ],
|
---|
| 112 | minified: environment_options_1.allowMinify && !!options.optimize,
|
---|
| 113 | compact: !environment_options_1.shouldBeautify && !!options.optimize,
|
---|
| 114 | sourceMaps: !!options.map,
|
---|
| 115 | });
|
---|
| 116 | if (!transformResult || !transformResult.code) {
|
---|
| 117 | throw new Error(`Unknown error occurred processing bundle for "${options.filename}".`);
|
---|
| 118 | }
|
---|
| 119 | result.downlevel = await processBundle({
|
---|
| 120 | ...options,
|
---|
| 121 | code: transformResult.code,
|
---|
| 122 | downlevelMap: (_a = transformResult.map) !== null && _a !== void 0 ? _a : undefined,
|
---|
| 123 | filename: path.join(basePath, downlevelFilename),
|
---|
| 124 | isOriginal: false,
|
---|
| 125 | });
|
---|
| 126 | }
|
---|
| 127 | if (!result.original && !options.ignoreOriginal) {
|
---|
| 128 | result.original = await processBundle({
|
---|
| 129 | ...options,
|
---|
| 130 | isOriginal: true,
|
---|
| 131 | });
|
---|
| 132 | }
|
---|
| 133 | return result;
|
---|
| 134 | }
|
---|
| 135 | exports.process = process;
|
---|
| 136 | async function processBundle(options) {
|
---|
[e29cc2e] | 137 | const { optimize, isOriginal, code, map, downlevelMap, filename: filepath, hiddenSourceMaps, cacheKeys = [], integrityAlgorithm, memoryMode, } = options;
|
---|
[6a3a178] | 138 | const filename = path.basename(filepath);
|
---|
| 139 | let resultCode = code;
|
---|
| 140 | let optimizeResult;
|
---|
| 141 | if (optimize) {
|
---|
| 142 | optimizeResult = await terserMangle(code, {
|
---|
| 143 | filename,
|
---|
| 144 | sourcemap: !!map,
|
---|
| 145 | compress: !isOriginal,
|
---|
| 146 | ecma: isOriginal ? 2015 : 5,
|
---|
| 147 | });
|
---|
| 148 | resultCode = optimizeResult.code;
|
---|
| 149 | }
|
---|
| 150 | let mapContent;
|
---|
| 151 | if (map) {
|
---|
| 152 | if (!hiddenSourceMaps) {
|
---|
| 153 | resultCode += `\n//# sourceMappingURL=${filename}.map`;
|
---|
| 154 | }
|
---|
| 155 | const partialSourcemaps = [];
|
---|
| 156 | if (optimizeResult && optimizeResult.map) {
|
---|
| 157 | partialSourcemaps.push(optimizeResult.map);
|
---|
| 158 | }
|
---|
| 159 | if (downlevelMap) {
|
---|
| 160 | partialSourcemaps.push(downlevelMap);
|
---|
| 161 | }
|
---|
| 162 | if (partialSourcemaps.length > 0) {
|
---|
| 163 | partialSourcemaps.push(map);
|
---|
| 164 | const fullSourcemap = remapping_1.default(partialSourcemaps, () => null);
|
---|
| 165 | mapContent = JSON.stringify(fullSourcemap);
|
---|
| 166 | }
|
---|
| 167 | else {
|
---|
| 168 | mapContent = map;
|
---|
| 169 | }
|
---|
| 170 | await cachePut(mapContent, cacheKeys[isOriginal ? 1 /* OriginalMap */ : 3 /* DownlevelMap */]);
|
---|
[e29cc2e] | 171 | if (!memoryMode) {
|
---|
| 172 | fs.writeFileSync(filepath + '.map', mapContent);
|
---|
| 173 | }
|
---|
[6a3a178] | 174 | }
|
---|
[e29cc2e] | 175 | const fileResult = createFileEntry(filepath, resultCode, mapContent, memoryMode, integrityAlgorithm);
|
---|
[6a3a178] | 176 | await cachePut(resultCode, cacheKeys[isOriginal ? 0 /* OriginalCode */ : 2 /* DownlevelCode */], fileResult.integrity);
|
---|
[e29cc2e] | 177 | if (!memoryMode) {
|
---|
| 178 | fs.writeFileSync(filepath, resultCode);
|
---|
| 179 | }
|
---|
[6a3a178] | 180 | return fileResult;
|
---|
| 181 | }
|
---|
| 182 | async function terserMangle(code, options = {}) {
|
---|
| 183 | // Note: Investigate converting the AST instead of re-parsing
|
---|
| 184 | // estree -> terser is already supported; need babel -> estree/terser
|
---|
| 185 | // Mangle downlevel code
|
---|
| 186 | const minifyOutput = await terser_1.minify(options.filename ? { [options.filename]: code } : code, {
|
---|
| 187 | compress: environment_options_1.allowMinify && !!options.compress,
|
---|
| 188 | ecma: options.ecma || 5,
|
---|
| 189 | mangle: environment_options_1.allowMangle,
|
---|
| 190 | safari10: true,
|
---|
| 191 | format: {
|
---|
| 192 | ascii_only: true,
|
---|
| 193 | webkit: true,
|
---|
| 194 | beautify: environment_options_1.shouldBeautify,
|
---|
| 195 | wrap_func_args: false,
|
---|
| 196 | },
|
---|
| 197 | sourceMap: !!options.sourcemap &&
|
---|
| 198 | {
|
---|
| 199 | asObject: true,
|
---|
| 200 | // typings don't include asObject option
|
---|
| 201 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
---|
| 202 | },
|
---|
| 203 | });
|
---|
| 204 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
---|
| 205 | return { code: minifyOutput.code, map: minifyOutput.map };
|
---|
| 206 | }
|
---|
[e29cc2e] | 207 | function createFileEntry(filename, code, map, memoryMode, integrityAlgorithm) {
|
---|
[6a3a178] | 208 | return {
|
---|
| 209 | filename: filename,
|
---|
| 210 | size: Buffer.byteLength(code),
|
---|
| 211 | integrity: integrityAlgorithm && generateIntegrityValue(integrityAlgorithm, code),
|
---|
[e29cc2e] | 212 | content: memoryMode ? code : undefined,
|
---|
[6a3a178] | 213 | map: !map
|
---|
| 214 | ? undefined
|
---|
| 215 | : {
|
---|
| 216 | filename: filename + '.map',
|
---|
| 217 | size: Buffer.byteLength(map),
|
---|
[e29cc2e] | 218 | content: memoryMode ? map : undefined,
|
---|
[6a3a178] | 219 | },
|
---|
| 220 | };
|
---|
| 221 | }
|
---|
| 222 | function generateIntegrityValue(hashAlgorithm, code) {
|
---|
| 223 | return hashAlgorithm + '-' + crypto_1.createHash(hashAlgorithm).update(code).digest('base64');
|
---|
| 224 | }
|
---|
| 225 | // The webpack runtime chunk is already ES5.
|
---|
| 226 | // However, two variants are still needed due to lazy routing and SRI differences
|
---|
| 227 | // NOTE: This should eventually be a babel plugin
|
---|
| 228 | async function processRuntime(options) {
|
---|
| 229 | let originalCode = options.code;
|
---|
| 230 | let downlevelCode = options.code;
|
---|
| 231 | // Replace integrity hashes with updated values
|
---|
| 232 | if (options.integrityAlgorithm && options.runtimeData) {
|
---|
| 233 | for (const data of options.runtimeData) {
|
---|
| 234 | if (!data.integrity) {
|
---|
| 235 | continue;
|
---|
| 236 | }
|
---|
| 237 | if (data.original && data.original.integrity) {
|
---|
| 238 | originalCode = originalCode.replace(data.integrity, data.original.integrity);
|
---|
| 239 | }
|
---|
| 240 | if (data.downlevel && data.downlevel.integrity) {
|
---|
| 241 | downlevelCode = downlevelCode.replace(data.integrity, data.downlevel.integrity);
|
---|
| 242 | }
|
---|
| 243 | }
|
---|
| 244 | }
|
---|
| 245 | // Adjust lazy loaded scripts to point to the proper variant
|
---|
| 246 | // Extra spacing is intentional to align source line positions
|
---|
| 247 | downlevelCode = downlevelCode.replace(/"\-(es20\d{2}|esnext)\./, ' "-es5.');
|
---|
| 248 | return {
|
---|
| 249 | original: await processBundle({
|
---|
| 250 | ...options,
|
---|
| 251 | code: originalCode,
|
---|
| 252 | isOriginal: true,
|
---|
| 253 | }),
|
---|
| 254 | downlevel: await processBundle({
|
---|
| 255 | ...options,
|
---|
| 256 | code: downlevelCode,
|
---|
| 257 | filename: options.filename.replace(/\-(es20\d{2}|esnext)/, '-es5'),
|
---|
| 258 | isOriginal: false,
|
---|
| 259 | }),
|
---|
| 260 | };
|
---|
| 261 | }
|
---|
| 262 | function createReplacePlugin(replacements) {
|
---|
| 263 | return {
|
---|
| 264 | visitor: {
|
---|
| 265 | StringLiteral(path) {
|
---|
| 266 | for (const replacement of replacements) {
|
---|
| 267 | if (path.node.value === replacement[0]) {
|
---|
| 268 | path.node.value = replacement[1];
|
---|
| 269 | }
|
---|
| 270 | }
|
---|
| 271 | },
|
---|
| 272 | },
|
---|
| 273 | };
|
---|
| 274 | }
|
---|
| 275 | function createIifeWrapperPlugin() {
|
---|
| 276 | return {
|
---|
| 277 | visitor: {
|
---|
| 278 | Program: {
|
---|
| 279 | exit(path) {
|
---|
| 280 | // Save existing body and directives
|
---|
| 281 | const { body, directives } = path.node;
|
---|
| 282 | // Clear out body and directives for wrapper
|
---|
| 283 | path.node.body = [];
|
---|
| 284 | path.node.directives = [];
|
---|
| 285 | // Create the wrapper - "(function() { ... })();"
|
---|
| 286 | const wrapper = core_1.types.expressionStatement(core_1.types.callExpression(core_1.types.parenthesizedExpression(core_1.types.functionExpression(undefined, [], core_1.types.blockStatement(body, directives))), []));
|
---|
| 287 | // Insert the wrapper
|
---|
| 288 | path.pushContainer('body', wrapper);
|
---|
| 289 | },
|
---|
| 290 | },
|
---|
| 291 | },
|
---|
| 292 | };
|
---|
| 293 | }
|
---|
| 294 | const USE_LOCALIZE_PLUGINS = false;
|
---|
| 295 | async function createI18nPlugins(locale, translation, missingTranslation, shouldInline, localeDataContent) {
|
---|
| 296 | const plugins = [];
|
---|
| 297 | const localizeDiag = await Promise.resolve().then(() => __importStar(require('@angular/localize/src/tools/src/diagnostics')));
|
---|
| 298 | const diagnostics = new localizeDiag.Diagnostics();
|
---|
| 299 | if (shouldInline) {
|
---|
| 300 | const es2015 = await Promise.resolve().then(() => __importStar(require('@angular/localize/src/tools/src/translate/source_files/es2015_translate_plugin')));
|
---|
| 301 | plugins.push(
|
---|
| 302 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
---|
| 303 | es2015.makeEs2015TranslatePlugin(diagnostics, (translation || {}), {
|
---|
| 304 | missingTranslation: translation === undefined ? 'ignore' : missingTranslation,
|
---|
| 305 | }));
|
---|
| 306 | const es5 = await Promise.resolve().then(() => __importStar(require('@angular/localize/src/tools/src/translate/source_files/es5_translate_plugin')));
|
---|
| 307 | plugins.push(
|
---|
| 308 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
---|
| 309 | es5.makeEs5TranslatePlugin(diagnostics, (translation || {}), {
|
---|
| 310 | missingTranslation: translation === undefined ? 'ignore' : missingTranslation,
|
---|
| 311 | }));
|
---|
| 312 | }
|
---|
| 313 | const inlineLocale = await Promise.resolve().then(() => __importStar(require('@angular/localize/src/tools/src/translate/source_files/locale_plugin')));
|
---|
| 314 | plugins.push(inlineLocale.makeLocalePlugin(locale));
|
---|
| 315 | if (localeDataContent) {
|
---|
| 316 | plugins.push({
|
---|
| 317 | visitor: {
|
---|
| 318 | Program(path) {
|
---|
| 319 | path.unshiftContainer('body', template_1.default.ast(localeDataContent));
|
---|
| 320 | },
|
---|
| 321 | },
|
---|
| 322 | });
|
---|
| 323 | }
|
---|
| 324 | return { diagnostics, plugins };
|
---|
| 325 | }
|
---|
| 326 | exports.createI18nPlugins = createI18nPlugins;
|
---|
| 327 | const localizeName = '$localize';
|
---|
| 328 | async function inlineLocales(options) {
|
---|
| 329 | var _a;
|
---|
| 330 | if (!i18n || i18n.inlineLocales.size === 0) {
|
---|
| 331 | return { file: options.filename, diagnostics: [], count: 0 };
|
---|
| 332 | }
|
---|
| 333 | if (i18n.flatOutput && i18n.inlineLocales.size > 1) {
|
---|
| 334 | throw new Error('Flat output is only supported when inlining one locale.');
|
---|
| 335 | }
|
---|
| 336 | const hasLocalizeName = options.code.includes(localizeName);
|
---|
| 337 | if (!hasLocalizeName && !options.setLocale) {
|
---|
| 338 | return inlineCopyOnly(options);
|
---|
| 339 | }
|
---|
| 340 | let ast;
|
---|
| 341 | try {
|
---|
| 342 | ast = core_1.parseSync(options.code, {
|
---|
| 343 | babelrc: false,
|
---|
| 344 | configFile: false,
|
---|
| 345 | sourceType: 'script',
|
---|
| 346 | filename: options.filename,
|
---|
| 347 | });
|
---|
| 348 | }
|
---|
| 349 | catch (error) {
|
---|
| 350 | if (error.message) {
|
---|
| 351 | // Make the error more readable.
|
---|
| 352 | // Same errors will contain the full content of the file as the error message
|
---|
| 353 | // Which makes it hard to find the actual error message.
|
---|
| 354 | const index = error.message.indexOf(')\n');
|
---|
| 355 | const msg = index !== -1 ? error.message.substr(0, index + 1) : error.message;
|
---|
| 356 | throw new Error(`${msg}\nAn error occurred inlining file "${options.filename}"`);
|
---|
| 357 | }
|
---|
| 358 | }
|
---|
| 359 | if (!ast) {
|
---|
| 360 | throw new Error(`Unknown error occurred inlining file "${options.filename}"`);
|
---|
| 361 | }
|
---|
| 362 | if (!USE_LOCALIZE_PLUGINS) {
|
---|
| 363 | return inlineLocalesDirect(ast, options);
|
---|
| 364 | }
|
---|
| 365 | const diagnostics = [];
|
---|
| 366 | for (const locale of i18n.inlineLocales) {
|
---|
| 367 | const isSourceLocale = locale === i18n.sourceLocale;
|
---|
| 368 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
---|
| 369 | const translations = isSourceLocale ? {} : i18n.locales[locale].translation || {};
|
---|
| 370 | let localeDataContent;
|
---|
| 371 | if (options.setLocale) {
|
---|
| 372 | // If locale data is provided, load it and prepend to file
|
---|
| 373 | const localeDataPath = (_a = i18n.locales[locale]) === null || _a === void 0 ? void 0 : _a.dataPath;
|
---|
| 374 | if (localeDataPath) {
|
---|
| 375 | localeDataContent = await loadLocaleData(localeDataPath, true, options.es5);
|
---|
| 376 | }
|
---|
| 377 | }
|
---|
| 378 | const { diagnostics: localeDiagnostics, plugins } = await createI18nPlugins(locale, translations, isSourceLocale ? 'ignore' : options.missingTranslation || 'warning', true, localeDataContent);
|
---|
| 379 | const transformResult = await core_1.transformFromAstSync(ast, options.code, {
|
---|
| 380 | filename: options.filename,
|
---|
| 381 | // using false ensures that babel will NOT search and process sourcemap comments (large memory usage)
|
---|
| 382 | // The types do not include the false option even though it is valid
|
---|
| 383 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
---|
| 384 | inputSourceMap: false,
|
---|
| 385 | babelrc: false,
|
---|
| 386 | configFile: false,
|
---|
| 387 | plugins,
|
---|
| 388 | compact: !environment_options_1.shouldBeautify,
|
---|
| 389 | sourceMaps: !!options.map,
|
---|
| 390 | });
|
---|
| 391 | diagnostics.push(...localeDiagnostics.messages);
|
---|
| 392 | if (!transformResult || !transformResult.code) {
|
---|
| 393 | throw new Error(`Unknown error occurred processing bundle for "${options.filename}".`);
|
---|
| 394 | }
|
---|
| 395 | const outputPath = path.join(options.outputPath, i18n.flatOutput ? '' : locale, options.filename);
|
---|
| 396 | fs.writeFileSync(outputPath, transformResult.code);
|
---|
| 397 | if (options.map && transformResult.map) {
|
---|
| 398 | const outputMap = remapping_1.default([transformResult.map, options.map], () => null);
|
---|
| 399 | fs.writeFileSync(outputPath + '.map', JSON.stringify(outputMap));
|
---|
| 400 | }
|
---|
| 401 | }
|
---|
| 402 | return { file: options.filename, diagnostics };
|
---|
| 403 | }
|
---|
| 404 | exports.inlineLocales = inlineLocales;
|
---|
| 405 | async function inlineLocalesDirect(ast, options) {
|
---|
| 406 | if (!i18n || i18n.inlineLocales.size === 0) {
|
---|
| 407 | return { file: options.filename, diagnostics: [], count: 0 };
|
---|
| 408 | }
|
---|
| 409 | const { default: generate } = await Promise.resolve().then(() => __importStar(require('@babel/generator')));
|
---|
| 410 | const utils = await Promise.resolve().then(() => __importStar(require('@angular/localize/src/tools/src/source_file_utils')));
|
---|
| 411 | const localizeDiag = await Promise.resolve().then(() => __importStar(require('@angular/localize/src/tools/src/diagnostics')));
|
---|
| 412 | const diagnostics = new localizeDiag.Diagnostics();
|
---|
| 413 | const positions = findLocalizePositions(ast, options, utils);
|
---|
| 414 | if (positions.length === 0 && !options.setLocale) {
|
---|
| 415 | return inlineCopyOnly(options);
|
---|
| 416 | }
|
---|
| 417 | const inputMap = !!options.map && JSON.parse(options.map);
|
---|
| 418 | // Cleanup source root otherwise it will be added to each source entry
|
---|
| 419 | const mapSourceRoot = inputMap && inputMap.sourceRoot;
|
---|
| 420 | if (inputMap) {
|
---|
| 421 | delete inputMap.sourceRoot;
|
---|
| 422 | }
|
---|
| 423 | // Load Webpack only when needed
|
---|
| 424 | if (webpackSources === undefined) {
|
---|
| 425 | webpackSources = (await Promise.resolve().then(() => __importStar(require('webpack')))).sources;
|
---|
| 426 | }
|
---|
| 427 | const { ConcatSource, OriginalSource, ReplaceSource, SourceMapSource } = webpackSources;
|
---|
| 428 | for (const locale of i18n.inlineLocales) {
|
---|
| 429 | const content = new ReplaceSource(inputMap
|
---|
| 430 | ? new SourceMapSource(options.code, options.filename, inputMap)
|
---|
| 431 | : new OriginalSource(options.code, options.filename));
|
---|
| 432 | const isSourceLocale = locale === i18n.sourceLocale;
|
---|
| 433 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
---|
| 434 | const translations = isSourceLocale ? {} : i18n.locales[locale].translation || {};
|
---|
| 435 | for (const position of positions) {
|
---|
| 436 | const translated = utils.translate(diagnostics, translations, position.messageParts, position.expressions, isSourceLocale ? 'ignore' : options.missingTranslation || 'warning');
|
---|
| 437 | const expression = utils.buildLocalizeReplacement(translated[0], translated[1]);
|
---|
| 438 | const { code } = generate(expression);
|
---|
| 439 | content.replace(position.start, position.end - 1, code);
|
---|
| 440 | }
|
---|
| 441 | let outputSource = content;
|
---|
| 442 | if (options.setLocale) {
|
---|
| 443 | const setLocaleText = `var $localize=Object.assign(void 0===$localize?{}:$localize,{locale:"${locale}"});\n`;
|
---|
| 444 | // If locale data is provided, load it and prepend to file
|
---|
| 445 | let localeDataSource;
|
---|
| 446 | const localeDataPath = i18n.locales[locale] && i18n.locales[locale].dataPath;
|
---|
| 447 | if (localeDataPath) {
|
---|
| 448 | const localeDataContent = await loadLocaleData(localeDataPath, true, options.es5);
|
---|
| 449 | localeDataSource = new OriginalSource(localeDataContent, path.basename(localeDataPath));
|
---|
| 450 | }
|
---|
| 451 | outputSource = localeDataSource
|
---|
| 452 | ? // The semicolon ensures that there is no syntax error between statements
|
---|
| 453 | new ConcatSource(setLocaleText, localeDataSource, ';\n', content)
|
---|
| 454 | : new ConcatSource(setLocaleText, content);
|
---|
| 455 | }
|
---|
| 456 | const { source: outputCode, map: outputMap } = outputSource.sourceAndMap();
|
---|
| 457 | const outputPath = path.join(options.outputPath, i18n.flatOutput ? '' : locale, options.filename);
|
---|
| 458 | fs.writeFileSync(outputPath, outputCode);
|
---|
| 459 | if (inputMap && outputMap) {
|
---|
| 460 | outputMap.file = options.filename;
|
---|
| 461 | if (mapSourceRoot) {
|
---|
| 462 | outputMap.sourceRoot = mapSourceRoot;
|
---|
| 463 | }
|
---|
| 464 | fs.writeFileSync(outputPath + '.map', JSON.stringify(outputMap));
|
---|
| 465 | }
|
---|
| 466 | }
|
---|
| 467 | return { file: options.filename, diagnostics: diagnostics.messages, count: positions.length };
|
---|
| 468 | }
|
---|
| 469 | function inlineCopyOnly(options) {
|
---|
| 470 | if (!i18n) {
|
---|
| 471 | throw new Error('i18n options are missing');
|
---|
| 472 | }
|
---|
| 473 | for (const locale of i18n.inlineLocales) {
|
---|
| 474 | const outputPath = path.join(options.outputPath, i18n.flatOutput ? '' : locale, options.filename);
|
---|
| 475 | fs.writeFileSync(outputPath, options.code);
|
---|
| 476 | if (options.map) {
|
---|
| 477 | fs.writeFileSync(outputPath + '.map', options.map);
|
---|
| 478 | }
|
---|
| 479 | }
|
---|
| 480 | return { file: options.filename, diagnostics: [], count: 0 };
|
---|
| 481 | }
|
---|
| 482 | function findLocalizePositions(ast, options, utils) {
|
---|
| 483 | const positions = [];
|
---|
| 484 | // Workaround to ensure a path hub is present for traversal
|
---|
| 485 | const { File } = require('@babel/core');
|
---|
| 486 | const file = new File({}, { code: options.code, ast });
|
---|
| 487 | if (options.es5) {
|
---|
| 488 | core_1.traverse(file.ast, {
|
---|
| 489 | CallExpression(path) {
|
---|
| 490 | const callee = path.get('callee');
|
---|
| 491 | if (callee.isIdentifier() &&
|
---|
| 492 | callee.node.name === localizeName &&
|
---|
| 493 | utils.isGlobalIdentifier(callee)) {
|
---|
| 494 | const [messageParts, expressions] = unwrapLocalizeCall(path, utils);
|
---|
| 495 | positions.push({
|
---|
| 496 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
---|
| 497 | start: path.node.start,
|
---|
| 498 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
---|
| 499 | end: path.node.end,
|
---|
| 500 | messageParts,
|
---|
| 501 | expressions,
|
---|
| 502 | });
|
---|
| 503 | }
|
---|
| 504 | },
|
---|
| 505 | });
|
---|
| 506 | }
|
---|
| 507 | else {
|
---|
| 508 | core_1.traverse(file.ast, {
|
---|
| 509 | TaggedTemplateExpression(path) {
|
---|
| 510 | if (core_1.types.isIdentifier(path.node.tag) && path.node.tag.name === localizeName) {
|
---|
| 511 | const [messageParts, expressions] = unwrapTemplateLiteral(path, utils);
|
---|
| 512 | positions.push({
|
---|
| 513 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
---|
| 514 | start: path.node.start,
|
---|
| 515 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
---|
| 516 | end: path.node.end,
|
---|
| 517 | messageParts,
|
---|
| 518 | expressions,
|
---|
| 519 | });
|
---|
| 520 | }
|
---|
| 521 | },
|
---|
| 522 | });
|
---|
| 523 | }
|
---|
| 524 | return positions;
|
---|
| 525 | }
|
---|
| 526 | function unwrapTemplateLiteral(path, utils) {
|
---|
| 527 | const [messageParts] = utils.unwrapMessagePartsFromTemplateLiteral(path.get('quasi').get('quasis'));
|
---|
| 528 | const [expressions] = utils.unwrapExpressionsFromTemplateLiteral(path.get('quasi'));
|
---|
| 529 | return [messageParts, expressions];
|
---|
| 530 | }
|
---|
| 531 | function unwrapLocalizeCall(path, utils) {
|
---|
| 532 | const [messageParts] = utils.unwrapMessagePartsFromLocalizeCall(path);
|
---|
| 533 | const [expressions] = utils.unwrapSubstitutionsFromLocalizeCall(path);
|
---|
| 534 | return [messageParts, expressions];
|
---|
| 535 | }
|
---|
| 536 | async function loadLocaleData(path, optimize, es5) {
|
---|
| 537 | // The path is validated during option processing before the build starts
|
---|
| 538 | const content = fs.readFileSync(path, 'utf8');
|
---|
| 539 | // Downlevel and optimize the data
|
---|
| 540 | const transformResult = await core_1.transformAsync(content, {
|
---|
| 541 | filename: path,
|
---|
| 542 | // The types do not include the false option even though it is valid
|
---|
| 543 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
---|
| 544 | inputSourceMap: false,
|
---|
| 545 | babelrc: false,
|
---|
| 546 | configFile: false,
|
---|
| 547 | presets: [
|
---|
| 548 | [
|
---|
| 549 | require.resolve('@babel/preset-env'),
|
---|
| 550 | {
|
---|
| 551 | bugfixes: true,
|
---|
| 552 | // IE 11 is the oldest supported browser
|
---|
| 553 | targets: es5 ? { ie: '11' } : { esmodules: true },
|
---|
| 554 | },
|
---|
| 555 | ],
|
---|
| 556 | ],
|
---|
| 557 | minified: environment_options_1.allowMinify && optimize,
|
---|
| 558 | compact: !environment_options_1.shouldBeautify && optimize,
|
---|
| 559 | comments: !optimize,
|
---|
| 560 | });
|
---|
| 561 | if (!transformResult || !transformResult.code) {
|
---|
| 562 | throw new Error(`Unknown error occurred processing bundle for "${path}".`);
|
---|
| 563 | }
|
---|
| 564 | return transformResult.code;
|
---|
| 565 | }
|
---|