Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export interface FileTransformResult {

export interface AngularCompilationOptions {
allowJs?: boolean;
declarationMap?: boolean;
isolatedModules?: boolean;
sourceMap?: boolean;
inlineSourceMap?: boolean;
Expand Down Expand Up @@ -52,6 +53,7 @@ export abstract class AngularCompilation {
tsconfig: string,
hostOptions: AngularHostOptions,
compilerOptionOverrides?: CompilerOptionOverrides,
buildType?: 'application' | 'library',
): Promise<AngularCompilationResult>;

emitAffectedFiles(): Iterable<EmitFileResult> | Promise<Iterable<EmitFileResult>> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,12 @@ export class AotCompilation extends TypeScriptCompilation {
super();
}

// eslint-disable-next-line max-lines-per-function
async initialize(
tsconfig: string,
hostOptions: AngularHostOptions,
compilerOptionOverrides?: CompilerOptionOverrides,
buildType: 'application' | 'library' = 'application',
): Promise<AngularCompilationResult> {
// Dynamically load the Angular compiler CLI package
const { NgtscProgram, OptimizeFor } = await TypeScriptCompilation.loadCompilerCli();
Expand All @@ -75,7 +77,7 @@ export class AotCompilation extends TypeScriptCompilation {
rootNames,
errors: configurationDiagnostics,
warnings,
} = await this.loadConfiguration(tsconfig, compilerOptionOverrides);
} = await this.loadConfiguration(tsconfig, compilerOptionOverrides, buildType);

const useTypeScriptTranspilation =
(compilerOptions['_useTypeScriptTranspilation'] as boolean | undefined) ??
Expand Down Expand Up @@ -332,9 +334,11 @@ export class AotCompilation extends TypeScriptCompilation {
useTypeScriptTranspilation,
} = this.#state;
const compilerOptions = typeScriptProgram.getCompilerOptions();
const isLibraryEmit = !!compilerOptions.declaration;
const buildInfoFilename = compilerOptions.tsBuildInfoFile ?? '.tsbuildinfo';

const emittedFiles = new Map<ts.SourceFile, EmitFileResult>();
const emittedFiles = new Map<string, EmitFileResult>();
const emittedSourceFiles = new Set<ts.SourceFile>();
const writeFileCallback: ts.WriteFileCallback = (filename, contents, _a, _b, sourceFiles) => {
if (!sourceFiles?.length && filename.endsWith(buildInfoFilename)) {
// Save builder info contents to specified location
Expand All @@ -350,17 +354,21 @@ export class AotCompilation extends TypeScriptCompilation {
}

angularCompiler.incrementalCompilation.recordSuccessfulEmit(sourceFile);
emittedFiles.set(sourceFile, { filename: sourceFile.fileName, contents });
emittedSourceFiles.add(sourceFile);
const targetFilename = isLibraryEmit ? filename : sourceFile.fileName;
emittedFiles.set(targetFilename, { filename: targetFilename, contents });
};
const transformers = angularCompiler.prepareEmit().transformers;
transformers.before ??= [];
transformers.before.push(
replaceBootstrap(() => typeScriptProgram.getProgram().getTypeChecker()),
webWorkerTransform,
);
if (!isLibraryEmit) {
transformers.before ??= [];
transformers.before.push(
replaceBootstrap(() => typeScriptProgram.getProgram().getTypeChecker()),
webWorkerTransform,
);

if (!this.browserOnlyBuild) {
transformers.before.push(lazyRoutesTransformer(compilerOptions, compilerHost));
if (!this.browserOnlyBuild) {
transformers.before.push(lazyRoutesTransformer(compilerOptions, compilerHost));
}
}

// Emit is handled in write file callback when using TypeScript
Expand Down Expand Up @@ -394,7 +402,7 @@ export class AotCompilation extends TypeScriptCompilation {

// Angular may have files that must be emitted but TypeScript does not consider affected
for (const sourceFile of typeScriptProgram.getSourceFiles()) {
if (emittedFiles.has(sourceFile) || angularCompiler.ignoreForEmit.has(sourceFile)) {
if (emittedSourceFiles.has(sourceFile) || angularCompiler.ignoreForEmit.has(sourceFile)) {
continue;
}

Expand All @@ -410,7 +418,8 @@ export class AotCompilation extends TypeScriptCompilation {
}

if (useTypeScriptTranspilation) {
typeScriptProgram.emit(sourceFile, writeFileCallback, undefined, undefined, transformers);
const emitOnly = affectedFiles.has(sourceFile) ? undefined : false;
typeScriptProgram.emit(sourceFile, writeFileCallback, undefined, emitOnly, transformers);
continue;
}

Expand Down Expand Up @@ -451,13 +460,14 @@ export class AotCompilation extends TypeScriptCompilation {
contents += `\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,${base64Map}`;
} else if (compilerOptions.sourceMap) {
const mapFilename = sourceFile.fileName + '.map';
emittedFiles.set(sourceFile, { filename: mapFilename, contents: printResult.map });
emittedFiles.set(mapFilename, { filename: mapFilename, contents: printResult.map });
}
}
}

angularCompiler.incrementalCompilation.recordSuccessfulEmit(sourceFile);
emittedFiles.set(sourceFile, { filename: sourceFile.fileName, contents });
emittedSourceFiles.add(sourceFile);
emittedFiles.set(sourceFile.fileName, { filename: sourceFile.fileName, contents });
}

return emittedFiles.values();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,21 @@ export interface CompilerOptionOverrides {
includeTestMetadata?: boolean;
customConditions?: string[];
rootFiles?: string[];
declarationMap?: boolean;
compilationMode?: 'full' | 'partial';
paths?: Record<string, string[]>;
}

export function transformCompilerOptions(
typeScript: typeof ts,
baseCompilerOptions: ng.CompilerOptions,
overrides?: CompilerOptionOverrides,
tsconfig?: string,
buildType: 'application' | 'library' = 'application',
): { compilerOptions: ng.CompilerOptions; warnings: PartialMessage[] } {
const compilerOptions = { ...baseCompilerOptions };
const warnings: PartialMessage[] = [];
const isLibrary = buildType === 'library';

if (
compilerOptions.target === undefined ||
Expand All @@ -57,13 +62,15 @@ export function transformCompilerOptions(
});
}

if (compilerOptions.compilationMode === 'partial') {
if (!isLibrary && compilerOptions.compilationMode === 'partial') {
warnings.push({
text: 'Angular partial compilation mode is not supported when building applications.',
location: null,
notes: [{ text: 'Full compilation mode will be used instead.' }],
});
compilerOptions.compilationMode = 'full';
} else if (overrides?.compilationMode) {
compilerOptions.compilationMode = overrides.compilationMode;
}

// Enable incremental compilation by default if caching is enabled and incremental is not explicitly disabled
Expand Down Expand Up @@ -101,6 +108,16 @@ export function transformCompilerOptions(
});
}

if (isLibrary) {
compilerOptions.target = typeScript.ScriptTarget.ES2022;
compilerOptions.module = typeScript.ModuleKind.ES2022;
compilerOptions.moduleResolution = typeScript.ModuleResolutionKind.Bundler;
compilerOptions.importHelpers = true;
compilerOptions.declaration = true;
compilerOptions.declarationMap = overrides?.declarationMap;
compilerOptions.declarationDir = undefined;
}

// Synchronize custom resolve conditions.
// Set if using the supported bundler resolution mode (bundler is the default in new projects)
if (
Expand All @@ -116,21 +133,25 @@ export function transformCompilerOptions(
noEmitOnError: false,
composite: false,
inlineSources: !!overrides?.sourcemap,
inlineSourceMap: !!overrides?.sourcemap,
sourceMap: undefined,
inlineSourceMap: !isLibrary && !!overrides?.sourcemap,
sourceMap: isLibrary ? !!overrides?.sourcemap : undefined,
mapRoot: undefined,
sourceRoot: undefined,
preserveSymlinks: overrides?.preserveSymlinks,
externalRuntimeStyles: overrides?.externalRuntimeStyles,
_enableHmr: !!overrides?.enableHmr,
// TypeScript transpilation is forced if:
// - Building a library (TypeScript emits both .js and .d.ts in a single pass).
// - isolatedModules is disabled (TS needs full module types to emit JS).
// - Karma code coverage is active (the coverage instrumentation transformer is Babel-based
// and cannot parse raw TypeScript code; Vitest handles coverage instrumentation downstream).
_useTypeScriptTranspilation:
!compilerOptions.isolatedModules || !!overrides?.instrumentForCoverage,
supportTestBed: !!overrides?.includeTestMetadata,
supportJitMode: !!overrides?.includeTestMetadata,
isLibrary || !compilerOptions.isolatedModules || !!overrides?.instrumentForCoverage,
supportTestBed: isLibrary ? undefined : !!overrides?.includeTestMetadata,
supportJitMode: isLibrary ? undefined : !!overrides?.includeTestMetadata,
paths: overrides?.paths
? { ...baseCompilerOptions.paths, ...overrides.paths }
: baseCompilerOptions.paths,
},
warnings,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export class JitCompilation extends TypeScriptCompilation {
tsconfig: string,
hostOptions: AngularHostOptions,
compilerOptionOverrides?: CompilerOptionOverrides,
buildType: 'application' | 'library' = 'application',
): Promise<AngularCompilationResult> {
// Dynamically load the Angular compiler CLI package
const { constructorParametersDownlevelTransform } =
Expand All @@ -54,7 +55,7 @@ export class JitCompilation extends TypeScriptCompilation {
rootNames,
errors: configurationDiagnostics,
warnings,
} = await this.loadConfiguration(tsconfig, compilerOptionOverrides);
} = await this.loadConfiguration(tsconfig, compilerOptionOverrides, buildType);

if (hostOptions.modifiedFiles) {
this.invalidateFiles(hostOptions.modifiedFiles);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export class ParallelCompilation extends AngularCompilation {
tsconfig: string,
hostOptions: AngularHostOptions,
compilerOptionOverrides?: CompilerOptionOverrides,
buildType: 'application' | 'library' = 'application',
): Promise<AngularCompilationResult> {
const stylesheetChannel = new MessageChannel();
// The request identifier is required because Angular can issue multiple concurrent requests
Expand Down Expand Up @@ -94,6 +95,7 @@ export class ParallelCompilation extends AngularCompilation {
jit: this.jit,
browserOnlyBuild: this.browserOnlyBuild,
compilerOptionOverrides,
buildType,
stylesheetPort: stylesheetChannel.port2,
webWorkerPort: webWorkerChannel.port2,
webWorkerSignal,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export interface InitRequest {
tsconfig: string;
fileReplacements?: Record<string, string>;
compilerOptionOverrides?: CompilerOptionOverrides;
buildType?: 'application' | 'library';
stylesheetPort: MessagePort;
webWorkerPort: MessagePort;
webWorkerSignal: Int32Array;
Expand Down Expand Up @@ -111,6 +112,7 @@ export async function initialize(request: InitRequest): Promise<AngularCompilati
},
},
request.compilerOptionOverrides,
request.buildType,
Comment thread
alan-agius4 marked this conversation as resolved.
);

success = true;
Expand All @@ -123,6 +125,7 @@ export async function initialize(request: InitRequest): Promise<AngularCompilati
// TODO: Expand? `allowJs`, `isolatedModules`, `sourceMap`, `inlineSourceMap` are the only fields needed currently.
compilerOptions: {
allowJs: compilerOptions.allowJs,
declarationMap: compilerOptions.declarationMap,
isolatedModules: compilerOptions.isolatedModules,
sourceMap: compilerOptions.sourceMap,
inlineSourceMap: compilerOptions.inlineSourceMap,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export abstract class TypeScriptCompilation extends AngularCompilation {
protected async loadConfiguration(
tsconfig: string,
compilerOptionOverrides?: CompilerOptionOverrides,
buildType: 'application' | 'library' = 'application',
): Promise<TransformedConfiguration> {
const { readConfiguration } = await TypeScriptCompilation.loadCompilerCli();

Expand Down Expand Up @@ -78,6 +79,7 @@ export abstract class TypeScriptCompilation extends AngularCompilation {
originalCompilerOptions,
compilerOptionOverrides,
tsconfig,
buildType,
);

return {
Expand Down
Loading