mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
In declaration-only emission mode, the compiler extracts the type declarations (.d.ts) files without full type-checking, which is possible with sufficient type annotations on exports that can be ensured by the `isolatedDeclarations` TS compiler option. This allows us to decouple type declaration emission from the actual full compilation doing the type-checking, thereby removing the edge between dependent TS files in the build action graph. In other words, the compilation of a TS file no longer indirectly depends on the compilation of all the TS files it imports through its dependency on their type declarations, because the type declarations themselves no longer depend on the compilation of their associated TS file. Without the coupling between type declaration emission and compilation, compilation time of a TS project is no longer bound dependent on the depth of the TS dependency tree as we can now build the entire project with just two entirely parallel phases: 1) emit the type declarations of all TS files in parallel and 2) compile all TS files in parallel. Since the Angular compiler adds static metadata fields to components, directives, modules, pipes and services based on their respective class annotations, it needs to actively partake in the type declaration emission in order to provide the types for these static fields in the declaration. In this change, we add experimental support for a declaration-only emission mode based on the local compilation mode, which already operates without type-checking and access to external type information, i.e. the same environment as is required for declaration-only emisssion. Apart from the same restrictions applied in local compilation mode, there are a few more restrictions imposed on code being compatible with this initial and experimental implementation: * No support for `@NgModule`s using external references. * No support for `hostDirectives` in `@Component`s and `@Directive`s using external references * No support for `@Input` annotations with `transform`. PR Close #61334
106 lines
3.7 KiB
TypeScript
106 lines
3.7 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright Google LLC 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.dev/license
|
|
*/
|
|
import {FileSystem} from '../../../src/ngtsc/file_system';
|
|
|
|
import {checkErrors, checkNoUnexpectedErrors} from './check_errors';
|
|
import {checkExpectations} from './check_expectations';
|
|
import {checkTypeDeclarations} from './check_type_declarations';
|
|
import {CompileResult, initMockTestFileSystem} from './compile_test';
|
|
import {
|
|
CompilationMode,
|
|
ComplianceTest,
|
|
Expectation,
|
|
getAllComplianceTests,
|
|
} from './get_compliance_tests';
|
|
|
|
function transformExpectation(expectation: Expectation, isLocalCompilation: boolean): void {
|
|
expectation.files = expectation.files.map((pair) => ({
|
|
expected: pair.expected,
|
|
generated: pair.generated,
|
|
}));
|
|
|
|
if (isLocalCompilation) {
|
|
expectation.files = expectation.files.map((pair) => ({
|
|
expected: getFilenameForLocalCompilation(pair.expected),
|
|
generated: pair.generated,
|
|
}));
|
|
}
|
|
}
|
|
|
|
/** Adds a '.local' pre-extension, e.g., basic_full.js -> basic_full.local.js */
|
|
function getFilenameForLocalCompilation(fileName: string): string {
|
|
return fileName.replace(/\.([cm]?js)$/, '.local.$1');
|
|
}
|
|
|
|
/**
|
|
* Set up jasmine specs for each of the compliance tests.
|
|
*
|
|
* @param type A description of the type of tests being run.
|
|
* @param compileFn The function that will do the compilation of the source files
|
|
* @param options Extra options. Currently the only option is the flag `isLocalCompilation` which
|
|
* indicates whether we are testing in local compilation mode.
|
|
*/
|
|
export function runTests(
|
|
type: CompilationMode,
|
|
compileFn: (fs: FileSystem, test: ComplianceTest) => CompileResult,
|
|
options: {
|
|
isLocalCompilation?: boolean;
|
|
emitDeclarationOnly?: boolean;
|
|
skipMappingChecks?: boolean;
|
|
} = {},
|
|
) {
|
|
describe(`compliance tests (${type})`, () => {
|
|
for (const test of getAllComplianceTests()) {
|
|
if (!test.compilationModeFilter.includes(type)) {
|
|
continue;
|
|
}
|
|
if (test.skipForTemplatePipeline) {
|
|
continue;
|
|
}
|
|
|
|
describe(`[${test.relativePath}]`, () => {
|
|
const itFn = test.focusTest ? fit : test.excludeTest ? xit : it;
|
|
itFn(test.description, () => {
|
|
if (type === 'linked compile' && test.compilerOptions?.['target'] === 'ES5') {
|
|
throw new Error(
|
|
`The "${type}" scenario does not support ES5 output.\n` +
|
|
`Did you mean to set \`"compilationModeFilter": ["full compile"]\` in "${test.relativePath}"?`,
|
|
);
|
|
}
|
|
|
|
const fs = initMockTestFileSystem(test.realTestPath);
|
|
const {errors, emittedFiles} = compileFn(fs, test);
|
|
for (const expectation of test.expectations) {
|
|
transformExpectation(expectation, !!options.isLocalCompilation);
|
|
if (expectation.expectedErrors.length > 0) {
|
|
checkErrors(
|
|
test.relativePath,
|
|
expectation.failureMessage,
|
|
expectation.expectedErrors,
|
|
errors,
|
|
);
|
|
} else if (!!options.emitDeclarationOnly) {
|
|
checkNoUnexpectedErrors(test.relativePath, errors);
|
|
checkTypeDeclarations(fs, emittedFiles);
|
|
} else {
|
|
checkNoUnexpectedErrors(test.relativePath, errors);
|
|
checkExpectations(
|
|
fs,
|
|
test.relativePath,
|
|
expectation.failureMessage,
|
|
expectation.files,
|
|
expectation.extraChecks,
|
|
options.skipMappingChecks,
|
|
);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
}
|
|
});
|
|
}
|