mirror of
https://github.com/angular/angular-cli.git
synced 2025-05-18 03:23:57 +08:00
refactor(@angular-devkit/build-angular): move around i18n methods to make them re-usable
This commit is contained in:
parent
3163a4391e
commit
936a9512ae
@ -82,10 +82,14 @@ export function installTempPackage(
|
||||
logger: logging.Logger,
|
||||
packageManager: PackageManager = PackageManager.Npm,
|
||||
): string {
|
||||
const tempPath = mkdtempSync(join(realpathSync(tmpdir()), '.ng-temp-packages-'));
|
||||
const tempPath = mkdtempSync(join(realpathSync(tmpdir()), 'angular-cli-packages-'));
|
||||
|
||||
// clean up temp directory on process exit
|
||||
process.on('exit', () => rimraf.sync(tempPath));
|
||||
process.on('exit', () => {
|
||||
try {
|
||||
rimraf.sync(tempPath);
|
||||
} catch { }
|
||||
});
|
||||
|
||||
// setup prefix/global modules path
|
||||
const packageManagerArgs = getPackageManagerArguments(packageManager);
|
||||
|
@ -44,6 +44,7 @@
|
||||
"postcss-loader": "3.0.0",
|
||||
"raw-loader": "3.1.0",
|
||||
"regenerator-runtime": "0.13.3",
|
||||
"rimraf": "3.0.0",
|
||||
"rollup": "1.25.2",
|
||||
"rxjs": "6.5.3",
|
||||
"sass": "1.23.1",
|
||||
|
@ -10,7 +10,6 @@ import { EmittedFiles, WebpackLoggingCallback, runWebpack } from '@angular-devki
|
||||
import { join, json, logging, normalize, tags, virtualFs } from '@angular-devkit/core';
|
||||
import { NodeJsSyncHost } from '@angular-devkit/core/node';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import { Observable, from, of } from 'rxjs';
|
||||
import { concatMap, map, switchMap } from 'rxjs/operators';
|
||||
@ -49,12 +48,13 @@ import {
|
||||
normalizeOptimization,
|
||||
normalizeSourceMaps,
|
||||
} from '../utils';
|
||||
import { BundleActionExecutor } from '../utils/action-executor';
|
||||
import { findCachePath } from '../utils/cache-path';
|
||||
import { copyAssets } from '../utils/copy-assets';
|
||||
import { cachingDisabled } from '../utils/environment-options';
|
||||
import { emittedFilesToInlineOptions } from '../utils/i18n-inlining';
|
||||
import { I18nOptions, createI18nOptions, mergeDeprecatedI18nOptions } from '../utils/i18n-options';
|
||||
import { createTranslationLoader } from '../utils/load-translations';
|
||||
import { i18nInlineEmittedFiles } from '../utils/i18n-inlining';
|
||||
import { I18nOptions } from '../utils/i18n-options';
|
||||
import { ensureOutputPaths } from '../utils/output-paths';
|
||||
import {
|
||||
InlineOptions,
|
||||
ProcessBundleFile,
|
||||
@ -63,11 +63,12 @@ import {
|
||||
} from '../utils/process-bundle';
|
||||
import { assertCompatibleAngularVersion } from '../utils/version';
|
||||
import {
|
||||
BrowserWebpackConfigOptions,
|
||||
generateBrowserWebpackConfigFromContext,
|
||||
generateI18nBrowserWebpackConfigFromContext,
|
||||
getIndexInputFile,
|
||||
getIndexOutputFile,
|
||||
} from '../utils/webpack-browser-config';
|
||||
import { BundleActionExecutor } from './action-executor';
|
||||
import { Schema as BrowserBuilderSchema } from './schema';
|
||||
|
||||
const cacheDownlevelPath = cachingDisabled ? undefined : findCachePath('angular-build-dl');
|
||||
@ -99,23 +100,53 @@ export function createBrowserLoggingCallback(
|
||||
};
|
||||
}
|
||||
|
||||
// todo: the below should be cleaned once dev-server support the new i18n
|
||||
interface ConfigFromContextReturn {
|
||||
config: webpack.Configuration;
|
||||
projectRoot: string;
|
||||
projectSourceRoot?: string;
|
||||
}
|
||||
|
||||
export async function buildBrowserWebpackConfigFromContext(
|
||||
options: BrowserBuilderSchema,
|
||||
context: BuilderContext,
|
||||
host: virtualFs.Host<fs.Stats>,
|
||||
i18n: boolean,
|
||||
): Promise<ConfigFromContextReturn & { i18n: I18nOptions }>;
|
||||
export async function buildBrowserWebpackConfigFromContext(
|
||||
options: BrowserBuilderSchema,
|
||||
context: BuilderContext,
|
||||
host?: virtualFs.Host<fs.Stats>,
|
||||
): Promise<ConfigFromContextReturn>;
|
||||
export async function buildBrowserWebpackConfigFromContext(
|
||||
options: BrowserBuilderSchema,
|
||||
context: BuilderContext,
|
||||
host: virtualFs.Host<fs.Stats> = new NodeJsSyncHost(),
|
||||
): Promise<{ config: webpack.Configuration; projectRoot: string; projectSourceRoot?: string }> {
|
||||
i18n = false,
|
||||
): Promise<ConfigFromContextReturn & { i18n?: I18nOptions }> {
|
||||
const webpackPartialGenerator = (wco: BrowserWebpackConfigOptions) => [
|
||||
getCommonConfig(wco),
|
||||
getBrowserConfig(wco),
|
||||
getStylesConfig(wco),
|
||||
getStatsConfig(wco),
|
||||
getAnalyticsConfig(wco, context),
|
||||
getCompilerConfig(wco),
|
||||
wco.buildOptions.webWorkerTsConfig ? getWorkerConfig(wco) : {},
|
||||
];
|
||||
|
||||
if (i18n) {
|
||||
return generateI18nBrowserWebpackConfigFromContext(
|
||||
options,
|
||||
context,
|
||||
webpackPartialGenerator,
|
||||
host,
|
||||
);
|
||||
}
|
||||
|
||||
return generateBrowserWebpackConfigFromContext(
|
||||
options,
|
||||
context,
|
||||
wco => [
|
||||
getCommonConfig(wco),
|
||||
getBrowserConfig(wco),
|
||||
getStylesConfig(wco),
|
||||
getStatsConfig(wco),
|
||||
getAnalyticsConfig(wco, context),
|
||||
getCompilerConfig(wco),
|
||||
wco.buildOptions.webWorkerTsConfig ? getWorkerConfig(wco) : {},
|
||||
],
|
||||
webpackPartialGenerator,
|
||||
host,
|
||||
);
|
||||
}
|
||||
@ -161,89 +192,24 @@ async function initialize(
|
||||
projectSourceRoot?: string;
|
||||
i18n: I18nOptions;
|
||||
}> {
|
||||
if (!context.target) {
|
||||
throw new Error('The builder requires a target.');
|
||||
}
|
||||
|
||||
const tsConfig = readTsconfig(options.tsConfig, context.workspaceRoot);
|
||||
const usingIvy = tsConfig.options.enableIvy !== false;
|
||||
const metadata = await context.getProjectMetadata(context.target);
|
||||
const projectRoot = path.join(context.workspaceRoot, (metadata.root as string) || '');
|
||||
const i18n = createI18nOptions(metadata, options.localize);
|
||||
|
||||
// Until 11.0, support deprecated i18n options when not using new localize option
|
||||
// i18nFormat is automatically calculated
|
||||
if (options.localize === undefined && usingIvy) {
|
||||
mergeDeprecatedI18nOptions(i18n, options.i18nLocale, options.i18nFile);
|
||||
} else if (options.localize !== undefined && !usingIvy) {
|
||||
options.localize = undefined;
|
||||
|
||||
context.logger.warn(`Option 'localize' is not supported with View Engine.`);
|
||||
}
|
||||
|
||||
if (i18n.shouldInline) {
|
||||
// Load locales
|
||||
const loader = await createTranslationLoader();
|
||||
|
||||
const usedFormats = new Set<string>();
|
||||
for (const [locale, desc] of Object.entries(i18n.locales)) {
|
||||
if (i18n.inlineLocales.has(locale)) {
|
||||
const result = loader(path.join(projectRoot, desc.file));
|
||||
|
||||
usedFormats.add(result.format);
|
||||
if (usedFormats.size > 1 && tsConfig.options.enableI18nLegacyMessageIdFormat !== false) {
|
||||
// This limitation is only for legacy message id support (defaults to true as of 9.0)
|
||||
throw new Error(
|
||||
'Localization currently only supports using one type of translation file format for the entire application.',
|
||||
);
|
||||
}
|
||||
|
||||
desc.format = result.format;
|
||||
desc.translation = result.translation;
|
||||
}
|
||||
}
|
||||
|
||||
// Legacy message id's require the format of the translations
|
||||
if (usedFormats.size > 0) {
|
||||
options.i18nFormat = [...usedFormats][0];
|
||||
}
|
||||
}
|
||||
|
||||
const originalOutputPath = options.outputPath;
|
||||
|
||||
// If inlining store the output in a temporary location to facilitate post-processing
|
||||
if (i18n.shouldInline) {
|
||||
options.outputPath = fs.mkdtempSync(path.join(fs.realpathSync(os.tmpdir()), 'angular-cli-'));
|
||||
}
|
||||
|
||||
const { config, projectSourceRoot } = await buildBrowserWebpackConfigFromContext(
|
||||
const { config, projectRoot, projectSourceRoot, i18n } = await buildBrowserWebpackConfigFromContext(
|
||||
options,
|
||||
context,
|
||||
host,
|
||||
true,
|
||||
);
|
||||
|
||||
if (i18n.shouldInline) {
|
||||
// Remove localize "polyfill"
|
||||
if (!config.resolve) {
|
||||
config.resolve = {};
|
||||
}
|
||||
if (!config.resolve.alias) {
|
||||
config.resolve.alias = {};
|
||||
}
|
||||
config.resolve.alias['@angular/localize/init'] = require.resolve('./empty.js');
|
||||
}
|
||||
|
||||
let transformedConfig;
|
||||
if (webpackConfigurationTransform) {
|
||||
transformedConfig = await webpackConfigurationTransform(config);
|
||||
}
|
||||
|
||||
if (options.deleteOutputPath) {
|
||||
await deleteOutputDir(
|
||||
normalize(context.workspaceRoot),
|
||||
normalize(originalOutputPath),
|
||||
host,
|
||||
).toPromise();
|
||||
deleteOutputDir(
|
||||
context.workspaceRoot,
|
||||
originalOutputPath,
|
||||
);
|
||||
}
|
||||
|
||||
return { config: transformedConfig || config, projectRoot, projectSourceRoot, i18n };
|
||||
@ -312,15 +278,7 @@ export function buildWebpackBrowser(
|
||||
|
||||
return { success };
|
||||
} else if (success) {
|
||||
const outputPaths =
|
||||
i18n.shouldInline && !i18n.flatOutput
|
||||
? [...i18n.inlineLocales].map(l => path.join(baseOutputPath, l))
|
||||
: [baseOutputPath];
|
||||
for (const outputPath of outputPaths) {
|
||||
if (!fs.existsSync(outputPath)) {
|
||||
fs.mkdirSync(outputPath, { recursive: true });
|
||||
}
|
||||
}
|
||||
const outputPaths = ensureOutputPaths(baseOutputPath, i18n);
|
||||
|
||||
let noModuleFiles: EmittedFiles[] | undefined;
|
||||
let moduleFiles: EmittedFiles[] | undefined;
|
||||
@ -586,14 +544,6 @@ export function buildWebpackBrowser(
|
||||
}
|
||||
} finally {
|
||||
executor.stop();
|
||||
|
||||
if (i18n.shouldInline) {
|
||||
try {
|
||||
// Remove temporary directory used for i18n processing
|
||||
// tslint:disable-next-line: no-non-null-assertion
|
||||
await host.delete(normalize(webpackStats.outputPath!)).toPromise();
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy assets
|
||||
@ -787,70 +737,6 @@ function generateIndex(
|
||||
}).toPromise();
|
||||
}
|
||||
|
||||
async function i18nInlineEmittedFiles(
|
||||
context: BuilderContext,
|
||||
emittedFiles: EmittedFiles[],
|
||||
i18n: I18nOptions,
|
||||
baseOutputPath: string,
|
||||
outputPaths: string[],
|
||||
scriptsEntryPointName: string[],
|
||||
emittedPath: string,
|
||||
es5: boolean,
|
||||
missingTranslation: 'error' | 'warning' | 'ignore' | undefined,
|
||||
) {
|
||||
const executor = new BundleActionExecutor({ i18n });
|
||||
let hasErrors = false;
|
||||
try {
|
||||
const { options, originalFiles: processedFiles } = emittedFilesToInlineOptions(
|
||||
emittedFiles,
|
||||
scriptsEntryPointName,
|
||||
emittedPath,
|
||||
baseOutputPath,
|
||||
es5,
|
||||
missingTranslation,
|
||||
);
|
||||
|
||||
for await (const result of executor.inlineAll(options)) {
|
||||
for (const diagnostic of result.diagnostics) {
|
||||
if (diagnostic.type === 'error') {
|
||||
hasErrors = true;
|
||||
context.logger.error(diagnostic.message);
|
||||
} else {
|
||||
context.logger.warn(diagnostic.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy any non-processed files into the output locations
|
||||
await copyAssets(
|
||||
[
|
||||
{
|
||||
glob: '**/*',
|
||||
input: emittedPath,
|
||||
output: '',
|
||||
ignore: [...processedFiles].map(f => path.relative(emittedPath, f)),
|
||||
},
|
||||
],
|
||||
outputPaths,
|
||||
'',
|
||||
);
|
||||
} catch (err) {
|
||||
context.logger.error('Localized bundle generation failed: ' + err.message);
|
||||
|
||||
return false;
|
||||
} finally {
|
||||
executor.stop();
|
||||
}
|
||||
|
||||
context.logger.info(`Localized bundle generation ${hasErrors ? 'failed' : 'complete'}.`);
|
||||
|
||||
if (hasErrors) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function mapErrorToMessage(error: unknown): string | undefined {
|
||||
if (error instanceof Error) {
|
||||
return error.message;
|
||||
|
@ -7,9 +7,9 @@
|
||||
*/
|
||||
import { createHash } from 'crypto';
|
||||
import * as fs from 'fs';
|
||||
import { copyFile } from '../utils/copy-file';
|
||||
import { manglingDisabled } from '../utils/environment-options';
|
||||
import { CacheKey, ProcessBundleOptions, ProcessBundleResult } from '../utils/process-bundle';
|
||||
import { copyFile } from './copy-file';
|
||||
import { manglingDisabled } from './environment-options';
|
||||
import { CacheKey, ProcessBundleOptions, ProcessBundleResult } from './process-bundle';
|
||||
|
||||
const cacache = require('cacache');
|
||||
const packageVersion = require('../../package.json').version;
|
@ -9,9 +9,9 @@ import JestWorker from 'jest-worker';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import * as v8 from 'v8';
|
||||
import { I18nOptions } from '../utils/i18n-options';
|
||||
import { InlineOptions, ProcessBundleOptions, ProcessBundleResult } from '../utils/process-bundle';
|
||||
import { BundleActionCache } from './action-cache';
|
||||
import { I18nOptions } from './i18n-options';
|
||||
import { InlineOptions, ProcessBundleOptions, ProcessBundleResult } from './process-bundle';
|
||||
|
||||
const hasThreadSupport = (() => {
|
||||
try {
|
||||
@ -28,10 +28,10 @@ const hasThreadSupport = (() => {
|
||||
// Processes use JSON which is much more limited
|
||||
const serialize = ((v8 as unknown) as { serialize(value: unknown): Buffer }).serialize;
|
||||
|
||||
let workerFile = require.resolve('../utils/process-bundle');
|
||||
let workerFile = require.resolve('./process-bundle');
|
||||
workerFile =
|
||||
path.extname(workerFile) === '.ts'
|
||||
? require.resolve('../utils/process-bundle-bootstrap')
|
||||
? require.resolve('./process-bundle-bootstrap')
|
||||
: workerFile;
|
||||
|
||||
export class BundleActionExecutor {
|
@ -5,22 +5,17 @@
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import { Path, resolve, virtualFs } from '@angular-devkit/core';
|
||||
import { EMPTY, Observable } from 'rxjs';
|
||||
import { concatMap, last, map } from 'rxjs/operators';
|
||||
import { resolve } from 'path';
|
||||
import * as rimraf from 'rimraf';
|
||||
|
||||
/**
|
||||
* Delete an output directory, but error out if it's the root of the project.
|
||||
*/
|
||||
export function deleteOutputDir(root: Path, outputPath: Path, host: virtualFs.Host): Observable<void> {
|
||||
export function deleteOutputDir(root: string, outputPath: string) {
|
||||
const resolvedOutputPath = resolve(root, outputPath);
|
||||
if (resolvedOutputPath === root) {
|
||||
throw new Error('Output path MUST not be project root directory!');
|
||||
}
|
||||
|
||||
return host.exists(resolvedOutputPath).pipe(
|
||||
concatMap(exists => exists ? host.delete(resolvedOutputPath) : EMPTY),
|
||||
last(null, null),
|
||||
map(() => undefined),
|
||||
);
|
||||
rimraf.sync(resolvedOutputPath);
|
||||
}
|
||||
|
@ -5,12 +5,16 @@
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import { BuilderContext } from '@angular-devkit/architect';
|
||||
import { EmittedFiles } from '@angular-devkit/build-webpack';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { BundleActionExecutor } from './action-executor';
|
||||
import { copyAssets } from './copy-assets';
|
||||
import { I18nOptions } from './i18n-options';
|
||||
import { InlineOptions } from './process-bundle';
|
||||
|
||||
export function emittedFilesToInlineOptions(
|
||||
function emittedFilesToInlineOptions(
|
||||
emittedFiles: EmittedFiles[],
|
||||
scriptsEntryPointName: string[],
|
||||
emittedPath: string,
|
||||
@ -55,3 +59,68 @@ export function emittedFilesToInlineOptions(
|
||||
|
||||
return { options, originalFiles };
|
||||
}
|
||||
|
||||
|
||||
export async function i18nInlineEmittedFiles(
|
||||
context: BuilderContext,
|
||||
emittedFiles: EmittedFiles[],
|
||||
i18n: I18nOptions,
|
||||
baseOutputPath: string,
|
||||
outputPaths: string[],
|
||||
scriptsEntryPointName: string[],
|
||||
emittedPath: string,
|
||||
es5: boolean,
|
||||
missingTranslation: 'error' | 'warning' | 'ignore' | undefined,
|
||||
): Promise<boolean> {
|
||||
const executor = new BundleActionExecutor({ i18n });
|
||||
let hasErrors = false;
|
||||
try {
|
||||
const { options, originalFiles: processedFiles } = emittedFilesToInlineOptions(
|
||||
emittedFiles,
|
||||
scriptsEntryPointName,
|
||||
emittedPath,
|
||||
baseOutputPath,
|
||||
es5,
|
||||
missingTranslation,
|
||||
);
|
||||
|
||||
for await (const result of executor.inlineAll(options)) {
|
||||
for (const diagnostic of result.diagnostics) {
|
||||
if (diagnostic.type === 'error') {
|
||||
hasErrors = true;
|
||||
context.logger.error(diagnostic.message);
|
||||
} else {
|
||||
context.logger.warn(diagnostic.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy any non-processed files into the output locations
|
||||
await copyAssets(
|
||||
[
|
||||
{
|
||||
glob: '**/*',
|
||||
input: emittedPath,
|
||||
output: '',
|
||||
ignore: [...processedFiles].map(f => path.relative(emittedPath, f)),
|
||||
},
|
||||
],
|
||||
outputPaths,
|
||||
'',
|
||||
);
|
||||
} catch (err) {
|
||||
context.logger.error('Localized bundle generation failed: ' + err.message);
|
||||
|
||||
return false;
|
||||
} finally {
|
||||
executor.stop();
|
||||
}
|
||||
|
||||
if (hasErrors) {
|
||||
context.logger.error('Localized bundle generation failed.');
|
||||
} else {
|
||||
context.logger.info('Localized bundle generation complete.');
|
||||
}
|
||||
|
||||
return !hasErrors;
|
||||
}
|
||||
|
@ -5,7 +5,16 @@
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import { json } from '@angular-devkit/core';
|
||||
import { BuilderContext } from '@angular-devkit/architect';
|
||||
import { json, virtualFs } from '@angular-devkit/core';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import * as rimraf from 'rimraf';
|
||||
import { readTsconfig } from '../angular-cli-files/utilities/read-tsconfig';
|
||||
import { Schema as BrowserBuilderSchema } from '../browser/schema';
|
||||
import { Schema as ServerBuilderSchema } from '../server/schema';
|
||||
import { createTranslationLoader } from './load-translations';
|
||||
|
||||
export interface I18nOptions {
|
||||
inlineLocales: Set<string>;
|
||||
@ -82,7 +91,80 @@ export function createI18nOptions(
|
||||
return i18n;
|
||||
}
|
||||
|
||||
export function mergeDeprecatedI18nOptions(i18n: I18nOptions, i18nLocale: string | undefined, i18nFile: string | undefined): I18nOptions {
|
||||
export async function configureI18nBuild<T extends BrowserBuilderSchema | ServerBuilderSchema>(
|
||||
context: BuilderContext,
|
||||
host: virtualFs.Host<fs.Stats>,
|
||||
options: T,
|
||||
): Promise<{
|
||||
buildOptions: T,
|
||||
i18n: I18nOptions,
|
||||
}> {
|
||||
if (!context.target) {
|
||||
throw new Error('The builder requires a target.');
|
||||
}
|
||||
|
||||
const buildOptions = { ... options };
|
||||
|
||||
const tsConfig = readTsconfig(buildOptions.tsConfig, context.workspaceRoot);
|
||||
const usingIvy = tsConfig.options.enableIvy !== false;
|
||||
const metadata = await context.getProjectMetadata(context.target);
|
||||
const i18n = createI18nOptions(metadata, buildOptions.localize);
|
||||
|
||||
// Until 11.0, support deprecated i18n options when not using new localize option
|
||||
// i18nFormat is automatically calculated
|
||||
if (buildOptions.localize === undefined && usingIvy) {
|
||||
mergeDeprecatedI18nOptions(i18n, buildOptions.i18nLocale, buildOptions.i18nFile);
|
||||
} else if (buildOptions.localize !== undefined && !usingIvy) {
|
||||
buildOptions.localize = undefined;
|
||||
|
||||
context.logger.warn(`Option 'localize' is not supported with View Engine.`);
|
||||
}
|
||||
|
||||
if (i18n.inlineLocales.size > 0) {
|
||||
// Load locales
|
||||
const loader = await createTranslationLoader();
|
||||
const projectRoot = path.join(context.workspaceRoot, (metadata.root as string) || '');
|
||||
const usedFormats = new Set<string>();
|
||||
for (const [locale, desc] of Object.entries(i18n.locales)) {
|
||||
if (i18n.inlineLocales.has(locale)) {
|
||||
const result = loader(path.join(projectRoot, desc.file));
|
||||
|
||||
usedFormats.add(result.format);
|
||||
if (usedFormats.size > 1 && tsConfig.options.enableI18nLegacyMessageIdFormat !== false) {
|
||||
// This limitation is only for legacy message id support (defaults to true as of 9.0)
|
||||
throw new Error(
|
||||
'Localization currently only supports using one type of translation file format for the entire application.',
|
||||
);
|
||||
}
|
||||
|
||||
desc.format = result.format;
|
||||
desc.translation = result.translation;
|
||||
}
|
||||
}
|
||||
|
||||
// Legacy message id's require the format of the translations
|
||||
if (usedFormats.size > 0) {
|
||||
buildOptions.i18nFormat = [...usedFormats][0];
|
||||
}
|
||||
}
|
||||
|
||||
// If inlining store the output in a temporary location to facilitate post-processing
|
||||
if (i18n.shouldInline) {
|
||||
const tempPath = fs.mkdtempSync(path.join(fs.realpathSync(os.tmpdir()), 'angular-cli-i18n-'));
|
||||
buildOptions.outputPath = tempPath;
|
||||
|
||||
// Remove temporary directory used for i18n processing
|
||||
process.on('exit', () => {
|
||||
try {
|
||||
rimraf.sync(tempPath);
|
||||
} catch { }
|
||||
});
|
||||
}
|
||||
|
||||
return { buildOptions, i18n };
|
||||
}
|
||||
|
||||
function mergeDeprecatedI18nOptions(i18n: I18nOptions, i18nLocale: string | undefined, i18nFile: string | undefined): I18nOptions {
|
||||
if (i18nFile !== undefined && i18nLocale === undefined) {
|
||||
throw new Error(`Option 'i18nFile' cannot be used without the 'i18nLocale' option.`);
|
||||
}
|
||||
|
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import { existsSync, mkdirSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { I18nOptions } from './i18n-options';
|
||||
|
||||
export function ensureOutputPaths(baseOutputPath: string, i18n: I18nOptions): string[] {
|
||||
const outputPaths = i18n.shouldInline && !i18n.flatOutput
|
||||
? [...i18n.inlineLocales].map(l => join(baseOutputPath, l))
|
||||
: [baseOutputPath];
|
||||
|
||||
for (const outputPath of outputPaths) {
|
||||
if (!existsSync(outputPath)) {
|
||||
mkdirSync(outputPath, { recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
return outputPaths;
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import { NodePath, parseSync, transformAsync, traverse, types } from '@babel/core';
|
||||
import { NodePath, ParseResult, parseSync, transformAsync, traverse, types } from '@babel/core';
|
||||
import { createHash } from 'crypto';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
@ -454,7 +454,9 @@ async function processRuntime(
|
||||
(options.cacheKeys && options.cacheKeys[CacheKey.DownlevelMap]) || null,
|
||||
);
|
||||
fs.writeFileSync(downlevelFilePath + '.map', downlevelMap);
|
||||
downlevelCode += `\n//# sourceMappingURL=${path.basename(downlevelFilePath)}.map`;
|
||||
if (!options.hiddenSourceMaps) {
|
||||
downlevelCode += `\n//# sourceMappingURL=${path.basename(downlevelFilePath)}.map`;
|
||||
}
|
||||
}
|
||||
await cachePut(
|
||||
downlevelCode,
|
||||
@ -594,13 +596,29 @@ function findLocalizePositions(
|
||||
options: InlineOptions,
|
||||
utils: typeof import('@angular/localize/src/tools/src/translate/source_files/source_file_utils'),
|
||||
): LocalizePosition[] {
|
||||
const ast = parseSync(options.code, { babelrc: false });
|
||||
let ast: ParseResult | undefined | null;
|
||||
|
||||
try {
|
||||
ast = parseSync(options.code, {
|
||||
babelrc: false,
|
||||
sourceType: 'script',
|
||||
});
|
||||
} catch (error) {
|
||||
if (error.message) {
|
||||
// Make the error more readable.
|
||||
// Same errors will contain the full content of the file as the error message
|
||||
// Which makes it hard to find the actual error message.
|
||||
const index = error.message.indexOf(')\n');
|
||||
const msg = index !== -1 ? error.message.substr(0, index + 1) : error.message;
|
||||
throw new Error(`${msg}\nAn error occurred inlining file "${options.filename}"`);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ast) {
|
||||
throw new Error(`Unknown error occurred inlining file "${options.filename}"`);
|
||||
}
|
||||
|
||||
const positions: LocalizePosition[] = [];
|
||||
|
||||
if (options.es5) {
|
||||
traverse(ast, {
|
||||
CallExpression(path: NodePath<types.CallExpression>) {
|
||||
|
@ -93,7 +93,11 @@ allTests = allTests
|
||||
// Disabled on rc.0 due to needed sync with devkit for changes.
|
||||
.filter(name => !name.endsWith('/service-worker.ts'));
|
||||
|
||||
if (!argv.ve) {
|
||||
if (argv.ve) {
|
||||
// Remove Ivy specific tests
|
||||
allTests = allTests
|
||||
.filter(name => !name.includes('tests/i18n/ivy-localize-'));
|
||||
} else {
|
||||
// These tests are disabled on the Ivy CI jobs because:
|
||||
// - Ivy doesn't support the functionality yet
|
||||
// - The test itself is not applicable to Ivy
|
||||
|
Loading…
x
Reference in New Issue
Block a user