ngtsc previously could attempt to reuse the main ts.Program twice. This
occurred when template type-checking was enabled and then an incremental
build was performed. This breaks a TypeScript invariant - ts.Programs can
only be reused once.
The creation of the template type-checking program reuses the main program,
rendering it moot. Then, on the next incremental build the main program
would be subject to reuse again, which would crash inside TypeScript.
This commit fixes the issue by reusing the template type-checking program
from the previous run on the next incremental build. Since under normal
circumstances the files in the type-checking program aren't changed, this
should be just as fast.
Testing strategy: a test is added in the incremental_spec which validates
that program reuse with type-checking turned on does not crash the compiler.
Fixes#30079
PR Close#30090
The compiler uses metadata to represent what it statically knows about
various expressions in a program. Occasionally, expressions in the program
for which metadata is extracted may contain sub-expressions which are not
representable in metadata. One such construct is an arrow function.
The compiler does not always need to understand such expressions completely.
For example, for a provider defined with `useValue`, the compiler does not
need to understand the value at all, only the outer provider definition. In
this case, the compiler employs a technique known as "expression lowering",
where it rewrites the provider expression into one that can be represented
in metadata. Chiefly, this involves extracting out the dynamic part (the
`useValue` expression) into an exported constant.
Lowering is applied through a heuristic, which considers the containing
statement as well as the field name of the expression.
Previously, this heuristic was not completely accurate in the case of
route definitions and the `loadChildren` field, which is lowered. If the
route definition using `loadChildren` existed inside a decorator invocation,
lowering was performed correctly. However, if it existed inside a standalone
variable declaration with an export keyword, the heuristic would conclude
that lowering was unnecessary. For ordinary providers this is true; however
the compiler attempts to fully understand the ROUTES token and thus even if
an array of routes is declared in an exported variable, any `loadChildren`
expressions within still need to be lowered.
This commit enables lowering of already exported variables under a limited
set of conditions (where the initializer expression is of a specific form).
This should enable the use of `loadChildren` in route definitions.
PR Close#30038
Template type-checking is enabled by default in the View Engine compiler.
The feature in Ivy is not quite ready for this yet, so this flag will
temporarily control whether templates are type-checked in ngtsc.
The goal is to remove this flag after rolling out template type-checking in
google3 in Ivy mode, and making sure the feature is as compatible with the
View Engine implementation as possible.
Initially, the default value of the flag will leave checking disabled.
PR Close#29698
This commit adds support for compiling the same program repeatedly in a way
that's similar to how incremental builds work in a tool such as the CLI.
* support is added to the compiler entrypoint for reuse of the Program
object between compilations. This is the basis of the compiler's
incremental compilation model.
* support is added to wrap the CompilerHost the compiler creates and cache
ts.SourceFiles in between compilations.
* support is added to track when files are emitted, for assertion purposes.
* an 'exclude' section is added to the base tsconfig to prevent .d.ts
outputs from the first compilation from becoming inputs to any subsequent
compilations.
PR Close#29380
This commit adds a `tracePerformance` option for tsconfig.json. When
specified, it causes a JSON file with timing information from the ngtsc
compiler to be emitted at the specified path.
This tracing system is used to instrument the analysis/emit phases of
compilation, and will be useful in debugging future integration work with
@angular/cli.
See ngtsc/perf/README.md for more details.
PR Close#29380
This change helps highlight certain misoptimizations with Closure
compiler. It is also stylistically preferable to consistently use index
access on index sig types.
Roughly, when one sees '.foo' they know it is always checked for typos
in the prop name by the type system (unless 'any'), while "['foo']" is
always not.
Once all angular repos are conforming this will become a tsetse.info
check, enforced by bazel.
PR Close#28937
In certain configurations (such as the g3 repository) which have lots of
small compilation units as well as strict dependency checking on generated
code, ngtsc's default strategy of directly importing directives/pipes into
components will not work. To handle these cases, an additional mode is
introduced, and is enabled when using the FileToModuleHost provided by such
compilation environments.
In this mode, when ngtsc encounters an NgModule which re-exports another
from a different file, it will re-export all the directives it contains at
the ES2015 level. The exports will have a predictable name based on the
FileToModuleHost. For example, if the host says that a directive Foo is
from the 'root/external/foo' module, ngtsc will add:
```
export {Foo as ɵng$root$external$foo$$Foo} from 'root/external/foo';
```
Consumers of the re-exported directive will then import it via this path
instead of directly from root/external/foo, preserving strict dependency
semantics.
PR Close#28852
Currently setting `enableIvy` to true runs a hybrid mode of `ngc` and `ngtsc`. This is counterintuitive given the name of the flag itself.
This PR makes the `true` value equivalent to the previous `ngtsc`, and `ngtsc` becomes an alias for `true`. Effectively this removes the hybrid mode as well since there's no other way to enable it.
PR Close#28616
The ultimate goal of this commit is to make use of fileNameToModuleName to
get the module specifier to use when generating an import, when that API is
available in the CompilerHost that ngtsc is created with.
As part of getting there, the way in which ngtsc tracks references and
generates import module specifiers is refactored considerably. References
are tracked with the Reference class, and previously ngtsc had several
different kinds of Reference. An AbsoluteReference represented a declaration
which needed to be imported via an absolute module specifier tracked in the
AbsoluteReference, and a RelativeReference represented a declaration from
the local program, imported via relative path or referred to directly by
identifier if possible. Thus, how to refer to a particular declaration was
encoded into the Reference type _at the time of creation of the Reference_.
This commit refactors that logic and reduces Reference to a single class
with no subclasses. A Reference represents a node being referenced, plus
context about how the node was located. This context includes a
"bestGuessOwningModule", the compiler's best guess at which absolute
module specifier has defined this reference. For example, if the compiler
arrives at the declaration of CommonModule via an import to @angular/common,
then any references obtained from CommonModule (e.g. NgIf) will also be
considered to be owned by @angular/common.
A ReferenceEmitter class and accompanying ReferenceEmitStrategy interface
are introduced. To produce an Expression referring to a given Reference'd
node, the ReferenceEmitter consults a sequence of ReferenceEmitStrategy
implementations.
Several different strategies are defined:
- LocalIdentifierStrategy: use local ts.Identifiers if available.
- AbsoluteModuleStrategy: if the Reference has a bestGuessOwningModule,
import the node via an absolute import from that module specifier.
- LogicalProjectStrategy: if the Reference is in the logical project
(is under the project rootDirs), import the node via a relative import.
- FileToModuleStrategy: use a FileToModuleHost to generate the module
specifier by which to import the node.
Depending on the availability of fileNameToModuleName in the CompilerHost,
then, a different collection of these strategies is used for compilation.
PR Close#28523
With #28594 we refactored the `@angular/compiler` slightly to
allow opting out from external symbol re-exports which are
enabled by default.
Since symbol re-exports only benefit projects which have a
very strict dependency enforcement, external symbols should
not be re-exported by default as this could grow the size of
factory files and cause unexpected behavior with Angular's
AOT symbol resolving (e.g. see: #25644).
Note that the common strict dependency enforcement for source
files does still work with external symbol re-exports disabled,
but there are also strict dependency checks that enforce strict
module dependencies also for _generated files_ (such as the
ngfactory files). This is how Google3 manages it's dependencies
and therefore external symbol re-exports need to be enabled within
Google3.
Also "ngtsc" also does not provide any way of using external symbol
re-exports, so this means that with this change, NGC can partially
match the behavior of "ngtsc" then (unless explicitly opted-out).
As mentioned before, internally at Google symbol re-exports need to
be still enabled, so the `ng_module` Bazel rule will enable the symbol
re-exports by default when running within Blaze.
Fixes#25644.
PR Close#28633
Users might have run the CSS Preprocessor tool *before* the Angular
compiler. For example, we do it that way under Bazel. This means that
the design-time reference is different from the compile-time one - the
input to the Angular compiler is a plain .css file.
We assume that the preprocessor does a trivial 1:1 mapping using the same
basename with a different extension.
PR Close#28166
This commit adds tracking of modules, directives, and pipes which are made
visible to consumers through NgModules exported from the package entrypoint.
ngtsc will now produce a diagnostic if such classes are not themselves
exported via the entrypoint (as this is a requirement for downstream
consumers to use them with Ivy).
To accomplish this, a graph of references is created and populated via the
ReferencesRegistry. Symbols exported via the package entrypoint are compared
against the graph to determine if any publicly visible symbols are not
properly exported. Diagnostics are produced for each one which also show the
path by which they become visible.
This commit also introduces a diagnostic (instead of a hard compiler crash)
if an entrypoint file cannot be correctly determined.
PR Close#27743
In a more specific scenario: Considering people use a custom TypeScript compiler host with `NGC`, they _could_ expect only posix paths in methods like `writeFile`. This at first glance sounds like a trivial issue that should be just fixed by the actual compiler host, but usually TypeScript internal API's just pass around posix normalized paths, and therefore it would be good to follow the same standards when passing JSON genfiles to the `CompilerHost`.
For normal TypeScript files (and TS genfiles), this is already consistent because those will be handled by the actual TypeScript `Program` (see `emitCallback`).
PR Close#27062
Originally, the ivy_switch mechanism used Bazel genrules to conditionally
compile one TS file or another depending on whether ngc or ngtsc was the
selected compiler. This was done because we wanted to avoid importing
certain modules (and thus pulling them into the build) if Ivy was on or
off. This mechanism had a major drawback: ivy_switch became a bottleneck
in the import graph, as it both imports from many places in the codebase
and is imported by many modules in the codebase. This frequently resulted
in cyclic imports which caused issues both with TS and Closure compilation.
It turns out ngcc needs both code paths in the bundle to perform the switch
during its operation anyway, so import switching was later abandoned. This
means that there's no real reason why the ivy_switch mechanism needed to
operate at the Bazel level, and for the ivy_switch file to be a bottleneck.
This commit removes the Bazel-level ivy_switch mechanism, and introduces
an additional TypeScript transform in ngtsc (and the pass-through tsc
compiler used for testing JIT) to perform the same operation that ngcc
does, and flip the switch during ngtsc compilation. This allows the
ivy_switch file to be removed, and the individual switches to be located
directly next to their consumers in the codebase, greatly mitigating the
circular import issues and making the mechanism much easier to use.
As part of this commit, the tag for marking switched variables was changed
from __PRE_NGCC__ to __PRE_R3__, since it's no longer just ngcc which
flips these tags. Most variables were renamed from R3_* to SWITCH_* as well,
since they're referenced mostly in render2 code.
Test strategy: existing test coverage is more than sufficient - if this
didn't work correctly it would break the hello world and todo apps.
PR Close#26550
Closure requires @nocollapse on Ivy definition static fields in order
to not convert them to standalone constants. However tsickle, the tool
which would ordinarily be responsible for adding @nocollapse, doesn't
properly annotate fields which are added synthetically via transforms.
So this commit adds @nocollapse by applying regular expressions against
code during the final write to disk.
PR Close#25775
This commit takes the first steps towards ngtsc producing real
TypeScript diagnostics instead of simply throwing errors when
encountering incorrect code.
A new class is introduced, FatalDiagnosticError, which can be thrown by
handlers whenever a condition in the code is encountered which by
necessity prevents the class from being compiled. This error type is
convertable to a ts.Diagnostic which represents the type and source of
the error.
Error codes are introduced for Angular errors, and are prefixed with -99
(so error code 1001 becomes -991001) to distinguish them from other TS
errors.
A function is provided which will read TS diagnostic output and convert
the TS errors to NG errors if they match this negative error code
format.
PR Close#25647
In tsc 3.0 the check that enables program structure reuse in tryReuseStructureFromOldProgram has changed
and now uses identity comparison on arrays within CompilerOptions. Since we recreate the options
on each incremental compilation, we now fail this check.
After this change the default set of options is reused in between incremental compilations, but we still
allow options to be overriden if needed.
PR Close#25275