83 Commits

Author SHA1 Message Date
Alex Rickabaugh
94ec0af582 refactor(compiler-cli): replace the IncrementalDriver with a new design (#41475)
This commit replaces the `IncrementalDriver` abstraction which powered
incremental compilation in the compiler with a new `IncrementalCompilation`
design. Principally, it separates two concerns which were tied together in
the previous implementation:

1. Tracking the reusable state of a compilation at any given point that
   could be reused in a subsequent future compilation.

2. Making use of a prior compilation's state to accelerate the current one.

The new abstraction adds explicit tracking and types to deal with both of
these concerns separately, which greatly reduces the complexity of the state
tracking that `IncrementalDriver` used to perform.

PR Close #41475
2021-04-13 13:05:35 -07:00
JoostK
1381301afe refactor(compiler-cli): track a dependency on a default import on WrappedNodeExpr (#41557)
Previously, the `DefaultImportRecorder` interface was used as follows:

1. During the analysis phase, the default import declaration of an
   identifier was recorded.

2. During the emit phase each emitted identifier would be recorded.
   The information from step 1 would then be used to determine the
   default import declaration of the identifier which would be
   registered as used.

3. A TypeScript transform would taint all default imports that were
   registered as used in step 2 such that the imports are not elided
   by TypeScript.

In incremental compilations, a file may have to be emitted even if its
analysis data has been reused from the prior compilation. This would
mean that step 1 is not executed, resulting in a mismatch in step 2 and
ultimately in incorrectly eliding the default. This was mitigated by
storing the mapping from identifier to import declaration on the
`ts.SourceFile` instead of a member of `DefaultImportTracker` such that
it would also be visible to the `DefaultImportRecorder` of subsequent
compiles even if step 1 had not been executed.

Ultimately however, the information that is being recorded into the
`DefaultImportRecorder` has a longer lifetime than a single
`DefaultImportRecorder` instance, as that is only valid during a single
compilation whereas the identifier to import declaration mapping
outlives a single compilation. This commit replaces the registration of
this mapping by attaching the default import declaration on the output
AST node that captures the identifier. This enables the removal of
all of the `DefaultImportRecorder` usages throughout the analysis phase
together with the `DefaultImportRecorder` interface itself.

PR Close #41557
2021-04-12 17:05:10 -07:00
Charles Lyding
1de04b124e feat(compiler-cli): support transforming component style resources (#41307)
This change introduces a new hook on the `ResourceHost` interface named `transformResource`.
Resource transformation allows both external and inline resources to be transformed prior to
compilation by the AOT compiler. This provides support for tooling integrations to enable
features such as preprocessor support for inline styles.
Only style resources are currently supported. However, the infrastructure is in place to add
template support in the future.

PR Close #41307
2021-04-02 15:48:45 -07:00
Alex Rickabaugh
48fec08c95 perf(compiler-cli): refactor the performance tracing infrastructure (#41125)
ngtsc has an internal performance tracing package, which previously has not
really seen much use. It used to track performance statistics on a very
granular basis (microseconds per actual class analysis, for example). This
had two problems:

* it produced voluminous amounts of data, complicating the analysis of such
  results and providing dubious value.
* it added nontrivial overhead to compilation when used (which also affected
  the very performance of the operations being measured).

This commit replaces the old system with a streamlined performance tracing
setup which is lightweight and designed to be always-on. The new system
tracks 3 metrics:

* time taken by various phases and operations within the compiler
* events (counters) which measure the shape and size of the compilation
* memory usage measured at various points of the compilation process

If the compiler option `tracePerformance` is set, the compiler will
serialize these metrics to a JSON file at that location after compilation is
complete.

PR Close #41125
2021-03-24 13:42:24 -07:00
JoostK
9cec94a008 perf(compiler-cli): use bound symbol in import graph in favor of module resolution (#40948)
The import graph scans source files for its import and export statements
to extract the source files that it imports/exports. Such statements
contain a module specifier string and this module specifier used to be
resolved to the actual source file using an explicit module resolution
step. This is especially expensive in incremental rebuilds, as the
module resolution cache has not been primed during program creation
(assuming that the incremental program was able to reuse the module
resolution results from a prior compilation). This meant that all module
resolution requests would have to hit the filesystem, which is
relatively slow.

This commit is able to replace the module resolution with TypeScript's
bound symbol of the module specifier. This symbol corresponds with the
`ts.SourceFile` that is being imported/exported, which is exactly what
the import graph was interested in. As a result, no filesystem accesses
are done anymore.

PR Close #40948
2021-03-08 12:05:48 -08:00
JoostK
fed6a7ce7d perf(compiler-cli): detect semantic changes and their effect on an incremental rebuild (#40947)
In Angular programs, changing a file may require other files to be
emitted as well due to implicit NgModule dependencies. For example, if
the selector of a directive is changed then all components that have
that directive in their compilation scope need to be recompiled, as the
change of selector may affect the directive matching results.

Until now, the compiler solved this problem using a single dependency
graph. The implicit NgModule dependencies were represented in this
graph, such that a changed file would correctly also cause other files
to be re-emitted. This approach is limited in a few ways:

1. The file dependency graph is used to determine whether it is safe to
   reuse the analysis data of an Angular decorated class. This analysis
   data is invariant to unrelated changes to the NgModule scope, but
   because the single dependency graph also tracked the implicit
   NgModule dependencies the compiler had to consider analysis data as
   stale far more often than necessary.
2. It is typical for a change to e.g. a directive to not affect its
   public API—its selector, inputs, outputs, or exportAs clause—in which
   case there is no need to re-emit all declarations in scope, as their
   compilation output wouldn't have changed.

This commit implements a mechanism by which the compiler is able to
determine the impact of a change by comparing it to the prior
compilation. To achieve this, a new graph is maintained that tracks all
public API information of all Angular decorated symbols. During an
incremental compilation this information is compared to the information
that was captured in the most recently succeeded compilation. This
determines the exact impact of the changes to the public API, which
is then used to determine which files need to be re-emitted.

Note that the file dependency graph remains, as it is still used to
track the dependencies of analysis data. This graph does no longer track
the implicit NgModule dependencies, which allows for better reuse of
analysis data.

These changes also fix a bug where template type-checking would fail to
incorporate changes made to a transitive base class of a
directive/component. This used to be a problem because transitive base
classes were not recorded as a transitive dependency in the file
dependency graph, such that prior type-check blocks would erroneously
be reused.

This commit also fixes an incorrectness where a change to a declaration
in NgModule `A` would not cause the declarations in NgModules that
import from NgModule `A` to be re-emitted. This was intentionally
incorrect as otherwise the performance of incremental rebuilds would
have been far worse. This is no longer a concern, as the compiler is now
able to only re-emit when actually necessary.

Fixes #34867
Fixes #40635
Closes #40728

PR Close #40947
2021-03-08 08:41:19 -08:00
Pete Bacon Darwin
322951af49 refactor(compiler-cli): error on cyclic imports in partial compilation (#40782)
Our approach for handling cyclic imports results in code that is
not easy to tree-shake, so it is not suitable for publishing in a
library.

When compiling in partial compilation mode, we are targeting
such library publication, so we now create a fatal diagnostic
error instead of trying to handle the cyclic import situation.

Closes #40678

PR Close #40782
2021-02-17 06:53:38 -08:00
Pete Bacon Darwin
d100a15998 refactor(compiler-cli): update to use new file-system interfaces (#40281)
Now that `ReadonlyFileSystem` and `PathManipulation` interfaces are
available, this commit updates the compiler-cli to use these more
focussed interfaces.

PR Close #40281
2021-01-08 09:34:44 -08:00
Alex Rickabaugh
a543e69497 refactor(compiler-cli): make TypeCheckingScopeRegistry a general utility (#40032)
The `annotations` package in the compiler previously contained a registry
which tracks NgModule scopes for template type-checking, including unifying
all type-checking metadata across class inheritance lines.

This commit generalizes this utility and prepares it for use in the
`TemplateTypeChecker` as well, to back APIs used by the language service.

PR Close #40032
2020-12-14 12:08:41 -08:00
Alex Rickabaugh
c7c5b2fc1e fix(compiler-cli): correct incremental behavior even with broken imports (#39923)
When the compiler is invoked via ngc or the Angular CLI, its APIs are used
under the assumption that Angular analysis/diagnostics are only requested if
the program has no TypeScript-level errors. A result of this assumption is
that the incremental engine has not needed to resolve changes via its
dependency graph when the program contained broken imports, since broken
imports are a TypeScript error.

The Angular Language Service for Ivy is using the compiler as a backend, and
exercising its incremental compilation APIs without enforcing this
assumption. As a result, the Language Service has run into issues where
broken imports cause incremental compilation to fail and produce incorrect
results.

This commit introduces a mechanism within the compiler to keep track of
files for which dependency analysis has failed, and to always treat such
files as potentially affected by future incremental steps. This is tested
via the Language Service infrastructure to ensure that the compiler is doing
the right thing in the case of invalid imports.

PR Close #39923
2020-12-03 13:42:13 -08:00
Alex Rickabaugh
0823622202 fix(compiler-cli): track poisoned scopes with a flag (#39923)
To avoid overwhelming a user with secondary diagnostics that derive from a
"root cause" error, the compiler has the notion of a "poisoned" NgModule.
An NgModule becomes poisoned when its declaration contains semantic errors:
declarations which are not components or pipes, imports which are not other
NgModules, etc. An NgModule also becomes poisoned if it imports or exports
another poisoned NgModule.

Previously, the compiler tracked this poisoned status as an alternate state
for each scope. Either a correct scope could be produced, or the entire
scope would be set to a sentinel error value. This meant that the compiler
would not track any information about a scope that was determined to be in
error.

This method presents several issues:

1. The compiler is unable to support the language service and return results
when a component or its module scope is poisoned.

This is fine for compilation, since diagnostics will be produced showing the
error(s), but the language service needs to still work for incorrect code.

2. `getComponentScopes()` does not return components with a poisoned scope,
which interferes with resource tracking of incremental builds.

If the component isn't included in that list, then the NgModule for it will
not have its dependencies properly tracked, and this can cause future
incremental build steps to produce incorrect results.

This commit changes the tracking of poisoned module scopes to use a flag on
the scope itself, rather than a sentinel value that replaces the scope. This
means that the scope itself will still be tracked, even if it contains
semantic errors. A test is added to the language service which verifies that
poisoned scopes can still be used in template type-checking.

PR Close #39923
2020-12-03 13:42:13 -08:00
Alex Rickabaugh
6d42954327 fix(compiler-cli): remove the concept of an errored trait (#39923)
Previously, if a trait's analysis step resulted in diagnostics, the trait
would be considered "errored" and no further operations, including register,
would be performed. Effectively, this meant that the compiler would pretend
the class in question was actually undecorated.

However, this behavior is problematic for several reasons:

1. It leads to inaccurate diagnostics being reported downstream.

For example, if a component is put into the error state, for example due to
a template error, the NgModule which declares the component would produce a
diagnostic claiming that the declaration is neither a directive nor a pipe.
This happened because the compiler wouldn't register() the component trait,
so the component would not be recorded as actually being a directive.

2. It can cause incorrect behavior on incremental builds.

This bug is more complex, but the general issue is that if the compiler
fails to associate a component and its module, then incremental builds will
not correctly re-analyze the module when the component's template changes.
Failing to register the component as such is one link in the larger chain of
issues that result in these kinds of issues.

3. It lumps together diagnostics produced during analysis and resolve steps.

This is not causing issues currently as the dependency graph ensures the
right classes are re-analyzed when needed, instead of showing stale
diagnostics. However, the dependency graph was not intended to serve this
role, and could potentially be optimized in ways that would break this
functionality.

This commit removes the concept of an "errored" trait entirely from the
trait system. Instead, analyzed and resolved traits have corresponding (and
separate) diagnostics, in addition to potentially `null` analysis results.
Analysis (but not resolution) diagnostics are carried forward during
incremental build operations. Compilation (emit) is only performed when
a trait reaches the resolved state with no diagnostics.

This change is functionally different than before as the `register` step is
now performed even in the presence of analysis errors, as long as analysis
results are also produced. This fixes problem 1 above, and is part of the
larger solution to problem 2.

PR Close #39923
2020-12-03 13:42:13 -08:00
Andrew Scott
371fb9a955 refactor(compiler-cli): Track external component resources in ResourceRegistry (#39373)
In addition to the template mapping that already existed, we want to also track the mapping for external
style files. We also store the `ts.Expression` in the registry so external tools can look up a resource
on a component by expression and avoid reading the value.

PR Close #39373
2020-10-28 10:57:14 -07:00
Pete Bacon Darwin
0accd1e68d refactor(compiler-cli): implement DeclarationNode node type (#38959)
Previously the `ConcreteDeclaration` and `InlineDeclaration` had
different properties for the underlying node type. And the `InlineDeclaration`
did not store a value that represented its declaration.

It turns out that a natural declaration node for an inline type is the
expression. For example in UMD/CommonJS this would be the `exports.<name>`
property access node.

So this expression is now used for the `node` of `InlineDeclaration` types
and the `expression` property is dropped.

To support this the codebase has been refactored to use a new `DeclarationNode`
type which is a union of `ts.Declaration|ts.Expression` instead of `ts.Declaration`
throughout.

PR Close #38959
2020-10-12 08:32:46 -07:00
JoostK
9d04b95166 refactor(compiler-cli): setup compilation mode to enable generating linker code (#38938)
This is a precursor to introducing the Angular linker. As an initial
step, a compiler option to configure the compilation mode is introduced.
This option is initially internal until the linker is considered ready.

PR Close #38938
2020-09-30 12:49:16 -07:00
Alex Rickabaugh
8f11b516f8 refactor(compiler-cli): API for getting components from a template file (#39002)
This commit adds an API to `NgCompiler`, a method called
`getComponentsWithTemplateFile`. Given a filesystem path to an external
template file, it retrieves a `Set` (actually a `ReadonlySet`) of component
declarations which are using this template. In most cases, this will only be
a single component.

This information is easily determined by the compiler during analysis, but
is hard for a lot of Angular tooling (e.g. the language service) to infer
independently. Therefore, it makes sense to expose this as a compiler API.

PR Close #39002
2020-09-30 09:26:05 -04:00
Alex Rickabaugh
16c7441c2f refactor(compiler-cli): introduce the TemplateTypeChecker abstraction (#38105)
This commit significantly refactors the 'typecheck' package to introduce a
new abstraction, the `TemplateTypeChecker`. To achieve this:

* a 'typecheck:api' package is introduced, containing common interfaces that
  consumers of the template type-checking infrastructure can depend on
  without incurring a dependency on the template type-checking machinery as
  a whole.
* interfaces for `TemplateTypeChecker` and `TypeCheckContext` are introduced
  which contain the abstract operations supported by the implementation
  classes `TemplateTypeCheckerImpl` and `TypeCheckContextImpl` respectively.
* the `TemplateTypeChecker` interface supports diagnostics on a whole
  program basis to start with, but the implementation is purposefully
  designed to support incremental diagnostics at a per-file or per-component
  level.
* `TemplateTypeChecker` supports direct access to the type check block of a
  component.
* the testing utility is refactored to be a lot more useful, and new tests
  are added for the new abstraction.

PR Close #38105
2020-07-29 10:31:20 -07:00
Pete Bacon Darwin
6b311552f0 fix(compiler-cli): ensure file_system handles mixed Windows drives (#37959)
The `fs.relative()` method assumed that the file-system is a single tree,
which is not the case in Windows, where you can have multiple drives,
e.g. `C:`, `D:` etc.

This commit changes `fs.relative()` so that it no longer forces the result
to be a `PathSegment` and then flows that refactoring through the rest of
the compiler-cli (and ngcc).  The main difference is that now, in some cases,
we needed to check whether the result is "rooted", i.e an `AbsoluteFsPath`,
rather than a `PathSegment`, before using it.

Fixes #36777

PR Close #37959
2020-07-13 12:05:21 -07:00
JoostK
6b565ba8f2 refactor(ngcc): let isWithinPackage operate on paths instead of source files (#37596)
Changes `isWithinPackage` to take an `AbsoluteFsPath` instead of `ts.SourceFile`,
to allow for an upcoming change to use it when no `ts.SourceFile` is available,
but just a path.

PR Close #37596
2020-06-29 12:21:22 -07:00
George Kalpakas
8197557fcf refactor(ngcc): rename EntryPoint#package to EntryPoint#packagePath (#37040)
Rename the `package` property to `packagePath` on the `EntryPoint`
interface. This makes it more clear that the `packagePath` property
holds the absolute path to the containing package (similar to how `path`
holds the path to the entry-point). This will also align with the
`packageName` property that will be added in a subsequent commit.

This commit also re-orders the `EntryPoint` properties to group related
properties together and to match the order of properties on instances
with that on the interface.

PR Close #37040
2020-06-11 18:58:36 -07:00
David Neil
8c682c52b1 fix(ngcc): use annotateForClosureCompiler option (#36652)
Adds @nocollapse to static properties added by ngcc
iff annotateForClosureCompiler is true.

The Closure Compiler will collapse static properties
into the global namespace.  Adding this annotation keeps
the properties attached to their respective object, which
allows them to be referenced via a class's constructor.
The annotation is already added by ngtsc and ngc under the
same option, this commit extends the functionality to ngcc.

Closes #36618.

PR Close #36652
2020-06-11 11:12:56 -07:00
Joey Perrott
d1ea1f4c7f build: update license headers to reference Google LLC (#37205)
Update the license headers throughout the repository to reference Google LLC
rather than Google Inc, for the required license headers.

PR Close #37205
2020-05-26 14:26:58 -04:00
Pete Bacon Darwin
53a8459d5f fix(compiler-cli): ensure LogicalFileSystem handles case-sensitivity (#36859)
The `LogicalFileSystem` was not taking into account the
case-sensitivity of the file-system when caching logical
file paths.

PR Close #36859
2020-05-06 15:23:16 -07:00
Paul Gschwendtner
4c92cf43cf feat(compiler-cli): report error if undecorated class with Angular features is discovered (#36921)
Previously in v9, we deprecated the pattern of undecorated base classes
that rely on Angular features. We ran a migration for this in version 9
and will run the same on in version 10 again.

To ensure that projects do not regress and start using the unsupported
pattern again, we report an error in ngtsc if such undecorated classes
are discovered.

We keep the compatibility code enabled in ngcc so that libraries
can be still be consumed, even if they have not been migrated yet.

Resolves FW-2130.

PR Close #36921
2020-05-06 15:06:10 -07:00
Pete Bacon Darwin
fafa50d97f fix(ngcc): support ModuleWithProviders functions that delegate (#36948)
In #36892 the `ModuleWithProviders` type parameter becomes required.
This exposes a bug in ngcc, where it can only handle functions that have a
specific form:

```
function forRoot() {
  return { ... };
}
```

In other words, it only accepts functions that return an object literal.

In some libraries, the function instead returns a call to another function.
For example in `angular-in-memory-web-api`:

```
InMemoryWebApiModule.forFeature = function (dbCreator, options) {
  return InMemoryWebApiModule_1.forRoot(dbCreator, options);
};
```

This commit changes the parsing of such functions to use the
`PartialEvaluator`, which can evaluate these more complex function
bodies.

PR Close #36948
2020-05-06 13:35:48 -07:00
Pete Bacon Darwin
e010f2ca54 refactor(ngcc): move getModuleWithProvidersFunctions() into the analyzer (#36948)
Previously this method was implemented on the `NgccReflectionHost`,
but really it is asking too much of the host, since it actually needs to do
some static evaluation of the code to be able to support a wider range
of function shapes. Also there was only one implementation of the method
in the `Esm2015ReflectionHost` since it has no format specific code in
in.

This commit moves the whole function (and supporting helpers) into the
`ModuleWithProvidersAnalyzer`, which is the only place it was being used.
This class will be able to do further static evaluation of the function bodies
in order to support more function shapes than the host can do on its own.

The commit removes a whole set of reflection host tests but these are
already covered by the tests of the analyzer.

PR Close #36948
2020-05-06 13:35:48 -07:00
Alex Rickabaugh
ecffc3557f perf(compiler-cli): perform template type-checking incrementally (#36211)
This optimization builds on a lot of prior work to finally make type-
checking of templates incremental.

Incrementality requires two main components:
- the ability to reuse work from a prior compilation.
- the ability to know when changes in the current program invalidate that
  prior work.

Prior to this commit, on every type-checking pass the compiler would
generate new .ngtypecheck files for each original input file in the program.

1. (Build #1 main program): empty .ngtypecheck files generated for each
   original input file.

2. (Build #1 type-check program): .ngtypecheck contents overridden for those
   which have corresponding components that need type-checked.

3. (Build #2 main program): throw away old .ngtypecheck files and generate
   new empty ones.

4. (Build #2 type-check program): same as step 2.

With this commit, the `IncrementalDriver` now tracks template type-checking
_metadata_ for each input file. The metadata contains information about
source mappings for generated type-checking code, as well as some
diagnostics which were discovered at type-check analysis time. The actual
type-checking code is stored in the TypeScript AST for type-checking files,
which is now re-used between programs as follows:

1. (Build #1 main program): empty .ngtypecheck files generated for each
   original input file.

2. (Build #1 type-check program): .ngtypecheck contents overridden for those
   which have corresponding components that need type-checked, and the
   metadata registered in the `IncrementalDriver`.

3. (Build #2 main program): The `TypeCheckShimGenerator` now reuses _all_
   .ngtypecheck `ts.SourceFile` shims from build #1's type-check program in
   the construction of build #2's main program. Some of the contents of
   these files might be stale (if a component's template changed, for
   example), but wholesale reuse here prevents unnecessary changes in the
   contents of the program at this point and makes TypeScript's job a lot
   easier.

4. (Build #2 type-check program): For those input files which have not
   "logically changed" (meaning components within are semantically the same
   as they were before), the compiler will re-use the type-check file
   metadata from build #1, and _not_ generate a new .ngtypecheck shim.
   For components which have logically changed or where the previous
   .ngtypecheck contents cannot otherwise be reused, code generation happens
   as before.

PR Close #36211
2020-05-05 18:40:42 -07:00
JoostK
89c589085d fix(ngcc): recognize enum declarations emitted in JavaScript (#36550)
An enum declaration in TypeScript code will be emitted into JavaScript
as a regular variable declaration, with the enum members being declared
inside an IIFE. For ngcc to support interpreting such variable
declarations as enum declarations with its members, ngcc needs to
recognize the enum declaration emit structure and extract all member
from the statements in the IIFE.

This commit extends the `ConcreteDeclaration` structure in the
`ReflectionHost` abstraction to be able to capture the enum members
on a variable declaration, as a substitute for the original
`ts.EnumDeclaration` as it existed in TypeScript code. The static
interpreter has been extended to handle the extracted enum members
as it would have done for `ts.EnumDeclaration`.

Fixes #35584
Resolves FW-2069

PR Close #36550
2020-04-28 15:59:57 -07:00
Pete Bacon Darwin
70dd27ffd8 fix(compiler): normalize line endings in ICU expansions (#36741)
The html parser already normalizes line endings (converting `\r\n` to `\n`)
for most text in templates but it was missing the expressions of ICU expansions.

In ViewEngine backticked literal strings, used to define inline templates,
were already normalized by the TypeScript parser.
In Ivy we are parsing the raw text of the source file directly so the line
endings need to be manually normalized.

This change ensures that inline templates have the line endings of ICU
expression normalized correctly, which matches the ViewEngine.

In ViewEngine external templates, defined in HTML files, the behavior was
different, since TypeScript was not normalizing the line endings.
Specifically, ICU expansion "expressions" are not being normalized.
This is a problem because it means that i18n message ids can be different on
different machines that are setup with different line ending handling,
or if the developer moves a template from inline to external or vice versa.

The goal is always to normalize line endings, whether inline or external.
But this would be a breaking change since it would change i18n message ids
that have been previously computed. Therefore this commit aligns the ivy
template parsing to have the same "buggy" behavior for external templates.

There is now a compiler option `i18nNormalizeLineEndingsInICUs`, which
if set to `true` will ensure the correct non-buggy behavior. For the time
being this option defaults to `false` to ensure backward compatibility while
allowing opt-in to the desired behavior. This option's default will be
flipped in a future breaking change release.

Further, when this option is set to `false`, any ICU expression tokens,
which have not been normalized, are added to the `ParseResult` from the
`HtmlParser.parse()` method. In the future, this collection of tokens could
be used to diagnose and encourage developers to migrate their i18n message
ids. See FW-2106.

Closes #36725

PR Close #36741
2020-04-28 12:22:40 -07:00
George Kalpakas
6ab43d7335 fix(ngcc): correctly detect external files from nested node_modules/ (#36559)
Previously, when we needed to detect whether a file is external to a
package, we only checked whether the relative path to the file from the
package's root started with `..`. This would detect external imports
when the packages were siblings (e.g. peer dependencies or hoisted to
the top of `node_modules/` by the package manager), but would fail to
detect imports from packages located in nested `node_modules/` as
external. For example, importing `node_modules/foo/node_modules/bar`
from a file in `node_modules/foo/` would be considered internal to the
`foo` package.

This could result in processing/analyzing more files than necessary.
More importantly it could lead to errors due to trying to analyze
non-Angular packages that were direct dependencies of Angular packages.

This commit fixes it by also verifying that the relative path to a file
does not start with `node_modules/`.

Jira issue: [FW-2068](https://angular-team.atlassian.net/browse/FW-2068)

Fixes #36526

PR Close #36559
2020-04-10 09:10:26 -07:00
Pete Bacon Darwin
8be8466a00 style(ngcc): reformat of ngcc after clang update (#36447)
PR Close #36447
2020-04-06 09:26:57 -07:00
Pete Bacon Darwin
b8e9a30d3b fix(ngcc): use preserve whitespaces from tsconfig if provided (#36189)
Previously ngcc never preserved whitespaces but this is at odds
with how the ViewEngine compiler works. In ViewEngine, library
templates are recompiled with the current application's tsconfig
settings, which meant that whitespace preservation could be set
in the application tsconfig file.

This commit allows ngcc to use the `preserveWhitespaces` setting
from tsconfig when compiling library templates. One should be aware
that this disallows different projects with different tsconfig settings
to share the same node_modules folder, with regard to whitespace
preservation. But this is already the case in the current ngcc since
this configuration is hard coded right now.

Fixes #35871

PR Close #36189
2020-03-24 14:25:06 -07:00
JoostK
7659f2e24b fix(ngcc): do not attempt compilation when analysis fails (#34889)
In #34288, ngtsc was refactored to separate the result of the analysis
and resolve phase for more granular incremental rebuilds. In this model,
any errors in one phase transition the trait into an error state, which
prevents it from being ran through subsequent phases. The ngcc compiler
on the other hand did not adopt this strict error model, which would
cause incomplete metadata—due to errors in earlier phases—to be offered
for compilation that could result in a hard crash.

This commit updates ngcc to take advantage of ngtsc's `TraitCompiler`,
that internally manages all Ivy classes that are part of the
compilation. This effectively replaces ngcc's own `AnalyzedFile` and
`AnalyzedClass` types, together with all of the logic to drive the
`DecoratorHandler`s. All of this is now handled in the `TraitCompiler`,
benefiting from its explicit state transitions of `Trait`s so that the
ngcc crash is a thing of the past.

Fixes #34500
Resolves FW-1788

PR Close #34889
2020-01-23 14:47:03 -08:00
Paul Gschwendtner
6b468f9b2e fix(ngcc): libraries using spread in object literals cannot be processed (#34661)
Consider a library that uses a shared constant for host bindings. e.g.

```ts
export const BASE_BINDINGS= {
  '[class.mat-themed]': '_isThemed',
}

----

@Directive({
  host: {...BASE_BINDINGS, '(click)': '...'}
})
export class Dir1 {}

@Directive({
  host: {...BASE_BINDINGS, '(click)': '...'}
})
export class Dir2 {}
```

Previously when these components were shipped as part of the
library to NPM, consumers were able to consume `Dir1` and `Dir2`.
No errors showed up.

Now with Ivy, when ngcc tries to process the library, an error
will be thrown. The error is stating that the host bindings should
be an object (which they obviously are). This happens because
TypeScript transforms the object spread to individual
`Object.assign` calls (for compatibility).

The partial evaluator used by the `@Directive` annotation handler
is unable to process this expression because there is no
integrated support for `Object.assign`. In View Engine, this was
not a problem because the `metadata.json` files from the library
were used to compute the host bindings.

Fixes #34659

PR Close #34661
2020-01-23 10:29:57 -08:00
crisbeto
6d534f10e6 fix(ivy): don't run decorator handlers against declaration files (#34557)
Currently the decorator handlers are run against all `SourceFile`s in the compilation, but we shouldn't be doing it against declaration files. This initially came up as a CI issue in #33264 where it was worked around only for the `DirectiveDecoratorHandler`. These changes move the logic into the `TraitCompiler` and `DecorationAnalyzer` so that it applies to all of the handlers.

PR Close #34557
2020-01-10 15:54:51 -08:00
crisbeto
cf37c003ff feat(ivy): error in ivy when inheriting a ctor from an undecorated base (#34460)
Angular View Engine uses global knowledge to compile the following code:

```typescript
export class Base {
  constructor(private vcr: ViewContainerRef) {}
}

@Directive({...})
export class Dir extends Base {
  // constructor inherited from base
}
```

Here, `Dir` extends `Base` and inherits its constructor. To create a `Dir`
the arguments to this inherited constructor must be obtained via dependency
injection. View Engine is able to generate a correct factory for `Dir` to do
this because via metadata it knows the arguments of `Base`'s constructor,
even if `Base` is declared in a different library.

In Ivy, DI is entirely a runtime concept. Currently `Dir` is compiled with
an ngDirectiveDef field that delegates its factory to `getInheritedFactory`.
This looks for some kind of factory function on `Base`, which comes up
empty. This case looks identical to an inheritance chain with no
constructors, which works today in Ivy.

Both of these cases will now become an error in this commit. If a decorated
class inherits from an undecorated base class, a diagnostic is produced
informing the user of the need to either explicitly declare a constructor or
to decorate the base class.

PR Close #34460
2019-12-18 15:04:49 -08:00
crisbeto
dcc8ff4ce7 feat(ivy): throw compilation error when providing undecorated classes (#34460)
Adds a compilation error if the consumer tries to pass in an undecorated class into the `providers` of an `NgModule`, or the `providers`/`viewProviders` arrays of a `Directive`/`Component`.

PR Close #34460
2019-12-18 15:04:49 -08:00
Pete Bacon Darwin
9264f43511 refactor(ngcc): remove private declaration aliases (#34254)
Now that the source to typings matching is able to handle
aliasing of exports, there is no need to handle aliases in private
declarations analysis.

These were originally added to cope when the typings files had
to use the name that the original source files used when exporting.

PR Close #34254
2019-12-18 11:25:01 -08:00
Pete Bacon Darwin
f22a6eb00e fix(ngcc): correctly match aliased classes between src and dts files (#34254)
The naïve matching algorithm we previously used to match declarations in
source files to declarations in typings files was based only on the name
of the thing being declared.  This did not handle cases where the declared
item had been exported via an alias - a common scenario when one of the two
file sets (source or typings) has been flattened, while the other has not.

The new algorithm tries to overcome this by creating two maps of export
name to declaration (i.e. `Map<string, ts.Declaration>`).
One for the source files and one for the typings files.
It then joins these two together by matching export names, resulting in a
new map that maps source declarations to typings declarations directly
(i.e. `Map<ts.Declaration, ts.Declaration>`).

This new map can handle the declaration names being different between the
source and typings as long as they are ultimately both exported with the
same alias name.

Further more, there is one map for "public exports", i.e. exported via the
root of the source tree (the entry-point), and another map for "private
exports", which are exported from individual files in the source tree but
not necessarily from the root. This second map can be used to "guess"
the mapping between exports in a deep (non-flat) file tree, which can be
used by ngcc to add required private exports to the entry-point.

Fixes #33593

PR Close #34254
2019-12-18 11:25:01 -08:00
Alex Rickabaugh
763f8d470a fix(ivy): validate the NgModule declarations field (#34404)
This commit adds three previously missing validations to
NgModule.declarations:

1. It checks that declared classes are actually within the current
   compilation.

2. It checks that declared classes are directives, components, or pipes.

3. It checks that classes are declared in at most one NgModule.

PR Close #34404
2019-12-17 11:39:48 -08:00
Alex Rickabaugh
af95dddd7e perf(ivy): eagerly parse the template twice during analysis (#34334)
A quirk of the Angular template parser is that when parsing templates in the
"default" mode, with options specified by the user, the source mapping
information in the template AST may be inaccurate. As a result, the compiler
parses the template twice: once for "emit" and once to produce an AST with
accurate sourcemaps for diagnostic production.

Previously, only the first parse was performed during analysis. The second
parse occurred during the template type-checking phase, just in time to
produce the template type-checking file.

However, with the reuse of analysis results during incremental builds, it
makes more sense to do the diagnostic parse eagerly during analysis so that
the work isn't unnecessarily repeated in subsequent builds. This commit
refactors the `ComponentDecoratorHandler` to do both parses eagerly, which
actually cleans up some complexity around template parsing as well.

PR Close #34334
2019-12-12 14:13:16 -08:00
JoostK
8c2cbdd385 perf(ivy): use module resolution cache (#34332)
During TypeScript module resolution, a lot of filesystem requests are
done. This is quite an expensive operation, so a module resolution cache
can be used to speed up the process significantly.

This commit lets the Ivy compiler perform all module resolution with a
module resolution cache. Note that the module resolution behavior can be
changed with a custom compiler host, in which case that custom host
implementation is responsible for caching. In the case of the Angular
CLI a custom compiler host with proper module resolution caching is
already in place, so the CLI already has this optimization.

PR Close #34332
2019-12-12 14:06:37 -08:00
Alex Rickabaugh
74edde0a94 perf(ivy): reuse prior analysis work during incremental builds (#34288)
Previously, the compiler performed an incremental build by analyzing and
resolving all classes in the program (even unchanged ones) and then using
the dependency graph information to determine which .js files were stale and
needed to be re-emitted. This algorithm produced "correct" rebuilds, but the
cost of re-analyzing the entire program turned out to be higher than
anticipated, especially for component-heavy compilations.

To achieve performant rebuilds, it is necessary to reuse previous analysis
results if possible. Doing this safely requires knowing when prior work is
viable and when it is stale and needs to be re-done.

The new algorithm implemented by this commit is such:

1) Each incremental build starts with knowledge of the last known good
   dependency graph and analysis results from the last successful build,
   plus of course information about the set of files changed.

2) The previous dependency graph's information is used to determine the
   set of source files which have "logically" changed. A source file is
   considered logically changed if it or any of its dependencies have
   physically changed (on disk) since the last successful compilation. Any
   logically unchanged dependencies have their dependency information copied
   over to the new dependency graph.

3) During the `TraitCompiler`'s loop to consider all source files in the
   program, if a source file is logically unchanged then its previous
   analyses are "adopted" (and their 'register' steps are run). If the file
   is logically changed, then it is re-analyzed as usual.

4) Then, incremental build proceeds as before, with the new dependency graph
   being used to determine the set of files which require re-emitting.

This analysis reuse avoids template parsing operations in many circumstances
and significantly reduces the time it takes ngtsc to rebuild a large
application.

Future work will increase performance even more, by tackling a variety of
other opportunities to reuse or avoid work.

PR Close #34288
2019-12-12 13:11:45 -08:00
Alex Rickabaugh
50cdc0ac1b refactor(ivy): move analysis side effects into a register phase (#34288)
Previously 'analyze' in the various `DecoratorHandler`s not only extracts
information from the decorators on the classes being analyzed, but also has
several side effects within the compiler:

* it can register metadata about the types involved in global metadata
  trackers.
* it can register information about which .ngfactory symbols are actually
  needed.

In this commit, these side-effects are moved into a new 'register' phase,
which runs after the 'analyze' step. Currently this is a no-op refactoring
as 'register' is always called directly after 'analyze'. In the future this
opens the door for re-use of prior analysis work (with only 'register' being
called, to apply the above side effects).

Also as part of this refactoring, the reification of NgModule scope
information into the incremental dependency graph is moved to the
`NgtscProgram` instead of the `TraitCompiler` (which now only manages trait
compilation and does not have other side effects).

PR Close #34288
2019-12-12 13:11:45 -08:00
Alex Rickabaugh
252e3e9487 refactor(ivy): formalize the compilation process for matched handlers (#34288)
Prior to this commit, the `IvyCompilation` tracked the state of each matched
`DecoratorHandler` on each class in the `ts.Program`, and how they
progressed through the compilation process. This tracking was originally
simple, but had grown more complicated as the compiler evolved. The state of
each specific "target" of compilation was determined by the nullability of
a number of fields on the object which tracked it.

This commit formalizes the process of compilation of each matched handler
into a new "trait" concept. A trait is some aspect of a class which gets
created when a `DecoratorHandler` matches the class. It represents an Ivy
aspect that needs to go through the compilation process.

Traits begin in a "pending" state and undergo transitions as various steps
of compilation take place. The `IvyCompilation` class is renamed to the
`TraitCompiler`, which manages the state of all of the traits in the active
program.

Making the trait concept explicit will support future work to incrementalize
the expensive analysis process of compilation.

PR Close #34288
2019-12-12 13:11:45 -08:00
JoostK
0f0fd25038 fix(ngcc): report diagnostics from migrations (#34014)
When ngcc is analyzing synthetically inserted decorators from a
migration, it is typically not expected that any diagnostics are
produced. In the situation where a diagnostic is produced, however, the
diagnostic would not be reported at all. This commit ensures that
diagnostics in migrations are reported.

PR Close #34014
2019-12-09 16:13:08 -08:00
Pete Bacon Darwin
cebe49d3c9 refactor(ngcc): store whether to render legacy i18n message ids in the bundle (#34135)
Placing this configuration in to the bundle avoids having to pass the
value around through lots of function calls, but also could enable
support for different behaviour per bundle in the future.

PR Close #34135
2019-12-03 10:15:53 -08:00
Kara Erickson
755d2d572f refactor(ivy): remove unnecessary fac wrapper (#34076)
For injectables, we currently generate a factory function in the
injectable def (prov) that delegates to the factory function in
the factory def (fac). It looks something like this:

```
factory: function(t) { return Svc.fac(t); }
```

The extra wrapper function is unnecessary since the args for
the factory functions are the same. This commit changes the
compiler to generate this instead:

```
factory: Svc.fac
```

Because we are generating less code for each injectable, we
should see some modest code size savings. AIO's main bundle
is about 1 KB smaller.

PR Close #34076
2019-12-02 11:35:24 -08:00
Pete Bacon Darwin
2fb9b7ff1b fix(ngcc): do not output duplicate ɵprov properties (#34085)
Previously, the Angular AOT compiler would always add a
`ɵprov` to injectables. But in ngcc this resulted in duplicate `ɵprov`
properties since published libraries already have this property.

Now in ngtsc, trying to add a duplicate `ɵprov` property is an error,
while in ngcc the additional property is silently not added.

// FW-1750

PR Close #34085
2019-11-27 12:46:37 -08:00
Pete Bacon Darwin
715d02aa14 fix(ngcc): report errors from analyze and resolve processing (#33964)
Previously, these errors were being swallowed, which made it
hard to debug problems with packages.

See https://github.com/angular/angular/issues/33685#issuecomment-557091719

PR Close #33964
2019-11-21 10:44:24 -08:00