feat(ivy): generate flat module index files (#27497)
Previously, ngtsc did not respect the angularCompilerOptions settings for generating flat module indices. This commit adds a FlatIndexGenerator which is used to implement those options. FW-738 #resolve PR Close #27497
This commit is contained in:
parent
352c582f98
commit
aa48810d80
|
@ -17,7 +17,7 @@ import {ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecorato
|
|||
import {BaseDefDecoratorHandler} from './annotations/src/base_def';
|
||||
import {TypeScriptReflectionHost} from './metadata';
|
||||
import {FileResourceLoader, HostResourceLoader} from './resource_loader';
|
||||
import {FactoryGenerator, FactoryInfo, GeneratedShimsHostWrapper, SummaryGenerator, generatedFactoryTransform} from './shims';
|
||||
import {FactoryGenerator, FactoryInfo, FlatIndexGenerator, GeneratedShimsHostWrapper, ShimGenerator, SummaryGenerator, generatedFactoryTransform} from './shims';
|
||||
import {ivySwitchTransform} from './switch';
|
||||
import {IvyCompilation, ivyTransformFactory} from './transform';
|
||||
import {TypeCheckContext, TypeCheckProgramHost} from './typecheck';
|
||||
|
@ -56,6 +56,8 @@ export class NgtscProgram implements api.Program {
|
|||
const shouldGenerateShims = options.allowEmptyCodegenFiles || false;
|
||||
this.host = host;
|
||||
let rootFiles = [...rootNames];
|
||||
|
||||
const generators: ShimGenerator[] = [];
|
||||
if (shouldGenerateShims) {
|
||||
// Summary generation.
|
||||
const summaryGenerator = SummaryGenerator.forRootFiles(rootNames);
|
||||
|
@ -73,7 +75,32 @@ export class NgtscProgram implements api.Program {
|
|||
|
||||
const factoryFileNames = Array.from(factoryFileMap.keys());
|
||||
rootFiles.push(...factoryFileNames, ...summaryGenerator.getSummaryFileNames());
|
||||
this.host = new GeneratedShimsHostWrapper(host, [summaryGenerator, factoryGenerator]);
|
||||
generators.push(summaryGenerator, factoryGenerator);
|
||||
}
|
||||
|
||||
if (options.flatModuleOutFile !== undefined) {
|
||||
const flatModuleId = options.flatModuleId || null;
|
||||
const flatIndexGenerator =
|
||||
FlatIndexGenerator.forRootFiles(options.flatModuleOutFile, rootNames, flatModuleId);
|
||||
if (flatIndexGenerator !== null) {
|
||||
generators.push(flatIndexGenerator);
|
||||
rootFiles.push(flatIndexGenerator.flatIndexPath);
|
||||
} else {
|
||||
// This error message talks specifically about having a single .ts file in "files". However
|
||||
// the actual logic is a bit more permissive. If a single file exists, that will be taken,
|
||||
// otherwise the highest level (shortest path) "index.ts" file will be used as the flat
|
||||
// module entry point instead. If neither of these conditions apply, the error below is
|
||||
// given.
|
||||
//
|
||||
// The user is not informed about the "index.ts" option as this behavior is deprecated -
|
||||
// an explicit entrypoint should always be specified.
|
||||
throw new Error(
|
||||
'Angular compiler option "flatModuleIndex" requires one and only one .ts file in the "files" field.');
|
||||
}
|
||||
}
|
||||
|
||||
if (generators.length > 0) {
|
||||
this.host = new GeneratedShimsHostWrapper(host, generators);
|
||||
}
|
||||
|
||||
this.tsProgram =
|
||||
|
|
|
@ -9,5 +9,6 @@
|
|||
/// <reference types="node" />
|
||||
|
||||
export {FactoryGenerator, FactoryInfo, generatedFactoryTransform} from './src/factory_generator';
|
||||
export {GeneratedShimsHostWrapper} from './src/host';
|
||||
export {FlatIndexGenerator} from './src/flat_index_generator';
|
||||
export {GeneratedShimsHostWrapper, ShimGenerator} from './src/host';
|
||||
export {SummaryGenerator} from './src/summary_generator';
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/**
|
||||
* @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 * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {ShimGenerator} from './host';
|
||||
import {isNonDeclarationTsFile} from './util';
|
||||
|
||||
export class FlatIndexGenerator implements ShimGenerator {
|
||||
readonly flatIndexPath: string;
|
||||
|
||||
private constructor(
|
||||
relativeFlatIndexPath: string, readonly entryPoint: string,
|
||||
readonly moduleName: string|null) {
|
||||
this.flatIndexPath = path.posix.join(path.posix.dirname(entryPoint), relativeFlatIndexPath)
|
||||
.replace(/\.js$/, '') +
|
||||
'.ts';
|
||||
}
|
||||
|
||||
static forRootFiles(flatIndexPath: string, files: ReadonlyArray<string>, moduleName: string|null):
|
||||
FlatIndexGenerator|null {
|
||||
// If there's only one .ts file in the program, it's the entry. Otherwise, look for the shortest
|
||||
// (in terms of characters in the filename) file that ends in /index.ts. The second behavior is
|
||||
// deprecated; users should always explicitly specify a single .ts entrypoint.
|
||||
const tsFiles = files.filter(isNonDeclarationTsFile);
|
||||
if (tsFiles.length === 1) {
|
||||
return new FlatIndexGenerator(flatIndexPath, tsFiles[0], moduleName);
|
||||
} else {
|
||||
let indexFile: string|null = null;
|
||||
for (const tsFile of tsFiles) {
|
||||
if (tsFile.endsWith('/index.ts') &&
|
||||
(indexFile === null || tsFile.length <= indexFile.length)) {
|
||||
indexFile = tsFile;
|
||||
}
|
||||
}
|
||||
if (indexFile !== null) {
|
||||
return new FlatIndexGenerator(flatIndexPath, indexFile, moduleName);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
recognize(fileName: string): boolean { return fileName === this.flatIndexPath; }
|
||||
|
||||
generate(): ts.SourceFile {
|
||||
const relativeEntryPoint = './' +
|
||||
path.posix.relative(path.posix.dirname(this.flatIndexPath), this.entryPoint)
|
||||
.replace(/\.tsx?$/, '');
|
||||
|
||||
const contents = `/**
|
||||
* Generated bundle index. Do not edit.
|
||||
*/
|
||||
|
||||
export * from '${relativeEntryPoint}';
|
||||
`;
|
||||
const genFile = ts.createSourceFile(
|
||||
this.flatIndexPath, contents, ts.ScriptTarget.ES2015, true, ts.ScriptKind.TS);
|
||||
if (this.moduleName !== null) {
|
||||
genFile.moduleName = this.moduleName;
|
||||
}
|
||||
return genFile;
|
||||
}
|
||||
}
|
|
@ -1132,4 +1132,29 @@ describe('ngtsc behavioral tests', () => {
|
|||
const jsContents = env.getContents('test.js');
|
||||
expect(jsContents).toMatch(/directives: \[i1\.ExternalDir\]/);
|
||||
});
|
||||
|
||||
describe('flat module indices', () => {
|
||||
it('should generate a basic flat module index', () => {
|
||||
env.tsconfig({
|
||||
'flatModuleOutFile': 'flat.js',
|
||||
});
|
||||
env.write('test.ts', 'export const TEST = "this is a test";');
|
||||
|
||||
env.driveMain();
|
||||
const jsContents = env.getContents('flat.js');
|
||||
expect(jsContents).toContain('export * from \'./test\';');
|
||||
});
|
||||
|
||||
it('should generate a flat module with an id', () => {
|
||||
env.tsconfig({
|
||||
'flatModuleOutFile': 'flat.js',
|
||||
'flatModuleId': '@mymodule',
|
||||
});
|
||||
env.write('test.ts', 'export const TEST = "this is a test";');
|
||||
|
||||
env.driveMain();
|
||||
const dtsContents = env.getContents('flat.d.ts');
|
||||
expect(dtsContents).toContain('/// <amd-module name="@mymodule" />');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue