fix(ivy): use globally unique names for i18n constants (#25689)
Closure compiler requires that the i18n message constants of the form const MSG_XYZ = goog.getMessage('...'); have names that are unique across an entire compilation, even if the variables themselves are local to a given module. This means that in practice these names must be unique in a codebase. The best way to guarantee this requirement is met is to encode the relative file name of the file into which the constant is being written into the constant name itself. This commit implements that solution. PR Close #25689
This commit is contained in:

committed by
Misko Hevery

parent
bd0eb0d1d4
commit
cc29b9cf93
@ -49,14 +49,16 @@ export class Analyzer {
|
||||
handlers: DecoratorHandler<any, any>[] = [
|
||||
new BaseDefDecoratorHandler(this.typeChecker, this.host),
|
||||
new ComponentDecoratorHandler(
|
||||
this.typeChecker, this.host, this.scopeRegistry, false, this.resourceLoader),
|
||||
this.typeChecker, this.host, this.scopeRegistry, false, this.resourceLoader, this.rootDirs),
|
||||
new DirectiveDecoratorHandler(this.typeChecker, this.host, this.scopeRegistry, false),
|
||||
new InjectableDecoratorHandler(this.host, false),
|
||||
new NgModuleDecoratorHandler(this.typeChecker, this.host, this.scopeRegistry, false),
|
||||
new PipeDecoratorHandler(this.typeChecker, this.host, this.scopeRegistry, false),
|
||||
];
|
||||
|
||||
constructor(private typeChecker: ts.TypeChecker, private host: NgccReflectionHost) {}
|
||||
constructor(
|
||||
private typeChecker: ts.TypeChecker, private host: NgccReflectionHost,
|
||||
private rootDirs: string[]) {}
|
||||
|
||||
/**
|
||||
* Analyize a parsed file to generate the information about decorated classes that
|
||||
|
@ -65,13 +65,21 @@ export class PackageTransformer {
|
||||
// Create the TS program and necessary helpers.
|
||||
// TODO : create a custom compiler host that reads from .bak files if available.
|
||||
const host = ts.createCompilerHost(options);
|
||||
let rootDirs: string[]|undefined = undefined;
|
||||
if (options.rootDirs !== undefined) {
|
||||
rootDirs = options.rootDirs;
|
||||
} else if (options.rootDir !== undefined) {
|
||||
rootDirs = [options.rootDir];
|
||||
} else {
|
||||
rootDirs = [host.getCurrentDirectory()];
|
||||
}
|
||||
const packageProgram = ts.createProgram([entryPoint.entryFileName], options, host);
|
||||
const typeChecker = packageProgram.getTypeChecker();
|
||||
const dtsMapper = new DtsMapper(entryPoint.entryRoot, entryPoint.dtsEntryRoot);
|
||||
const reflectionHost = this.getHost(format, packageProgram, dtsMapper);
|
||||
|
||||
const parser = this.getFileParser(format, packageProgram, reflectionHost);
|
||||
const analyzer = new Analyzer(typeChecker, reflectionHost);
|
||||
const analyzer = new Analyzer(typeChecker, reflectionHost, rootDirs);
|
||||
const renderer = this.getRenderer(format, packageProgram, reflectionHost);
|
||||
|
||||
// Parse and analyze the files.
|
||||
|
@ -80,7 +80,7 @@ describe('Analyzer', () => {
|
||||
program = makeProgram(TEST_PROGRAM);
|
||||
const file = createParsedFile(program);
|
||||
const analyzer = new Analyzer(
|
||||
program.getTypeChecker(), new Fesm2015ReflectionHost(program.getTypeChecker()));
|
||||
program.getTypeChecker(), new Fesm2015ReflectionHost(program.getTypeChecker()), ['']);
|
||||
testHandler = createTestHandler();
|
||||
analyzer.handlers = [testHandler];
|
||||
result = analyzer.analyzeFile(file);
|
||||
|
@ -17,7 +17,7 @@ function setup(file: {name: string, contents: string}) {
|
||||
const program = makeProgram(file);
|
||||
const host = new Fesm2015ReflectionHost(program.getTypeChecker());
|
||||
const parser = new Esm2015FileParser(program, host);
|
||||
const analyzer = new Analyzer(program.getTypeChecker(), host);
|
||||
const analyzer = new Analyzer(program.getTypeChecker(), host, ['']);
|
||||
const renderer = new Esm2015Renderer(host);
|
||||
return {analyzer, host, parser, program, renderer};
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ function setup(file: {name: string, contents: string}) {
|
||||
const program = makeProgram(file);
|
||||
const host = new Esm5ReflectionHost(program.getTypeChecker());
|
||||
const parser = new Esm5FileParser(program, host);
|
||||
const analyzer = new Analyzer(program.getTypeChecker(), host);
|
||||
const analyzer = new Analyzer(program.getTypeChecker(), host, ['']);
|
||||
const renderer = new Esm5Renderer(host);
|
||||
return {analyzer, host, parser, program, renderer};
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ function analyze(file: {name: string, contents: string}) {
|
||||
const program = makeProgram(file);
|
||||
const host = new Fesm2015ReflectionHost(program.getTypeChecker());
|
||||
const parser = new Esm2015FileParser(program, host);
|
||||
const analyzer = new Analyzer(program.getTypeChecker(), host);
|
||||
const analyzer = new Analyzer(program.getTypeChecker(), host, ['']);
|
||||
|
||||
const parsedFiles = parser.parseFile(program.getSourceFile(file.name) !);
|
||||
return parsedFiles.map(file => analyzer.analyzeFile(file));
|
||||
|
@ -29,7 +29,7 @@ export class ComponentDecoratorHandler implements DecoratorHandler<R3ComponentMe
|
||||
constructor(
|
||||
private checker: ts.TypeChecker, private reflector: ReflectionHost,
|
||||
private scopeRegistry: SelectorScopeRegistry, private isCore: boolean,
|
||||
private resourceLoader: ResourceLoader) {}
|
||||
private resourceLoader: ResourceLoader, private rootDirs: string[]) {}
|
||||
|
||||
private literalCache = new Map<Decorator, ts.ObjectLiteralExpression>();
|
||||
|
||||
@ -111,9 +111,21 @@ export class ComponentDecoratorHandler implements DecoratorHandler<R3ComponentMe
|
||||
preserveWhitespaces = value;
|
||||
}
|
||||
|
||||
// Go through the root directories for this project, and select the one with the smallest
|
||||
// relative path representation.
|
||||
const filePath = node.getSourceFile().fileName;
|
||||
const relativeFilePath = this.rootDirs.reduce<string|undefined>((previous, rootDir) => {
|
||||
const candidate = path.posix.relative(rootDir, filePath);
|
||||
if (previous === undefined || candidate.length < previous.length) {
|
||||
return candidate;
|
||||
} else {
|
||||
return previous;
|
||||
}
|
||||
}, undefined) !;
|
||||
|
||||
const template = parseTemplate(
|
||||
templateStr, `${node.getSourceFile().fileName}#${node.name!.text}/template.html`,
|
||||
{preserveWhitespaces});
|
||||
{preserveWhitespaces}, relativeFilePath);
|
||||
if (template.errors !== undefined) {
|
||||
throw new Error(
|
||||
`Errors parsing template: ${template.errors.map(e => e.toString()).join(', ')}`);
|
||||
|
@ -39,7 +39,8 @@ describe('ComponentDecoratorHandler', () => {
|
||||
const checker = program.getTypeChecker();
|
||||
const host = new TypeScriptReflectionHost(checker);
|
||||
const handler = new ComponentDecoratorHandler(
|
||||
checker, host, new SelectorScopeRegistry(checker, host), false, new NoopResourceLoader());
|
||||
checker, host, new SelectorScopeRegistry(checker, host), false, new NoopResourceLoader(),
|
||||
['']);
|
||||
const TestCmp = getDeclaration(program, 'entry.ts', 'TestCmp', ts.isClassDeclaration);
|
||||
const detected = handler.detect(TestCmp, host.getDecoratorsOfDeclaration(TestCmp));
|
||||
if (detected === undefined) {
|
||||
|
@ -29,11 +29,20 @@ export class NgtscProgram implements api.Program {
|
||||
private _coreImportsFrom: ts.SourceFile|null|undefined = undefined;
|
||||
private _reflector: TypeScriptReflectionHost|undefined = undefined;
|
||||
private _isCore: boolean|undefined = undefined;
|
||||
private rootDirs: string[];
|
||||
|
||||
|
||||
constructor(
|
||||
rootNames: ReadonlyArray<string>, private options: api.CompilerOptions,
|
||||
host: api.CompilerHost, oldProgram?: api.Program) {
|
||||
this.rootDirs = [];
|
||||
if (options.rootDirs !== undefined) {
|
||||
this.rootDirs.push(...options.rootDirs);
|
||||
} else if (options.rootDir !== undefined) {
|
||||
this.rootDirs.push(options.rootDir);
|
||||
} else {
|
||||
this.rootDirs.push(host.getCurrentDirectory());
|
||||
}
|
||||
this.resourceLoader = host.readResource !== undefined ?
|
||||
new HostResourceLoader(host.readResource.bind(host)) :
|
||||
new FileResourceLoader();
|
||||
@ -177,7 +186,7 @@ export class NgtscProgram implements api.Program {
|
||||
const handlers = [
|
||||
new BaseDefDecoratorHandler(checker, this.reflector),
|
||||
new ComponentDecoratorHandler(
|
||||
checker, this.reflector, scopeRegistry, this.isCore, this.resourceLoader),
|
||||
checker, this.reflector, scopeRegistry, this.isCore, this.resourceLoader, this.rootDirs),
|
||||
new DirectiveDecoratorHandler(checker, this.reflector, scopeRegistry, this.isCore),
|
||||
new InjectableDecoratorHandler(this.reflector, this.isCore),
|
||||
new NgModuleDecoratorHandler(checker, this.reflector, scopeRegistry, this.isCore),
|
||||
|
Reference in New Issue
Block a user