-
Notifications
You must be signed in to change notification settings - Fork 25.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(compiler): switch to 'referencedFiles' for shim generation (#36211)
Shim generation was built on a lie. Shims are files added to the program which aren't original files authored by the user, but files authored effectively by the compiler. These fall into two categories: files which will be generated (like the .ngfactory shims we generate for View Engine compatibility) as well as files used internally in compilation (like the __ng_typecheck__.ts file). Previously, shim generation was driven by the `rootFiles` passed to the compiler as input. These are effectively the `files` listed in the `tsconfig.json`. Each shim generator (e.g. the `FactoryGenerator`) would examine the `rootFiles` and produce a list of shim file names which it would be responsible for generating. These names would then be added to the `rootFiles` when the program was created. The fatal flaw here is that `rootFiles` does not always account for all of the files in the program. In fact, it's quite rare that it does. Users don't typically specify every file directly in `files`. Instead, they rely on TypeScript, during program creation, starting with a few root files and transitively discovering all of the files in the program. This happens, however, during `ts.createProgram`, which is too late to add new files to the `rootFiles` list. As a result, shim generation was only including shims for files actually listed in the `tsconfig.json` file, and not for the transitive set of files in the user's program as it should. This commit completely rewrites shim generation to use a different technique for adding files to the program, inspired by View Engine's shim generator. In this new technique, as the program is being created and `ts.SourceFile`s are being requested from the `NgCompilerHost`, shims for those files are generated and a reference to them is patched onto the original file's `ts.SourceFile.referencedFiles`. This causes TS to think that the original file references the shim, and causes the shim to be included in the program. The original `referencedFiles` array is saved and restored after program creation, hiding this little hack from the rest of the system. The new shim generation engine differentiates between two kinds of shims: top-level shims (such as the flat module entrypoint file and __ng_typecheck__.ts) and per-file shims such as ngfactory or ngsummary files. The former are included via `rootFiles` as before, the latter are included via the `referencedFiles` of their corresponding original files. As a result of this change, shims are now correctly generated for all files in the program, not just the ones named in `tsconfig.json`. A few mitigating factors prevented this bug from being realized until now: * in g3, `files` does include the transitive closure of files in the program * in CLI apps, shims are not really used This change also makes use of a novel technique for associating information with source files: the use of an `NgExtension` `Symbol` to patch the information directly onto the AST object. This is used in several circumstances: * For shims, metadata about a `ts.SourceFile`'s status as a shim and its origins are held in the extension data. * For original files, the original `referencedFiles` are stashed in the extension data for later restoration. The main benefit of this technique is a lot less bookkeeping around `Map`s of `ts.SourceFile`s to various kinds of data, which need to be tracked/ invalidated as part of incremental builds. This technique is based on designs used internally in the TypeScript compiler and is serving as a prototype of this design in ngtsc. If it works well, it could have benefits across the rest of the compiler. PR Close #36211
- Loading branch information
Showing
30 changed files
with
1,082 additions
and
238 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
/** | ||
* @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 {absoluteFrom as _, FileSystem, getFileSystem, NgtscCompilerHost} from '../../file_system'; | ||
import {runInEachFileSystem} from '../../file_system/testing'; | ||
import {NgCompilerOptions} from '../api'; | ||
import {NgCompilerHost} from '../src/host'; | ||
|
||
runInEachFileSystem(() => { | ||
let fs!: FileSystem; | ||
beforeEach(() => { | ||
fs = getFileSystem(); | ||
fs.ensureDir(_('/')); | ||
}); | ||
|
||
describe('NgCompilerHost', () => { | ||
it('should add factory shims for all root files', () => { | ||
// This test verifies that per-file shims are guaranteed to be generated for each file in the | ||
// "root" input files, regardless of whether `referencedFiles`-based shimming is enabled. As | ||
// it turns out, setting `noResolve` in TypeScript's options disables this kind of shimming, | ||
// so `NgCompilerHost` needs to ensure at least the root files still get shims directly. | ||
|
||
// File is named index.ts to trigger flat module entrypoint logic | ||
// (which adds a top-level shim). | ||
const fileName = _('/index.ts'); | ||
fs.writeFile(fileName, `export class Test {}`); | ||
|
||
const options: NgCompilerOptions = { | ||
// Using noResolve means that TS will not follow `referencedFiles`, which is how shims are | ||
// normally included in the program. | ||
noResolve: true, | ||
rootDir: _('/'), | ||
|
||
// Both a top-level and a per-file shim are enabled. | ||
flatModuleOutFile: './entry', | ||
generateNgFactoryShims: true, | ||
}; | ||
const baseHost = new NgtscCompilerHost(fs, options); | ||
const host = NgCompilerHost.wrap(baseHost, [fileName], options, null); | ||
|
||
// A shim file should be included for the index.ts ngfactory, but not entry.ts since that | ||
// itself is a shim. | ||
expect(host.inputFiles).toContain(_('/index.ngfactory.ts')); | ||
expect(host.inputFiles).not.toContain(_('/entry.ngfactory.ts')); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.