Commit Graph

444 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 8d3da56eda fix(ngcc): detect synthesized constructors that have been downleveled using TS 4.2 (#41305)
TypeScript 4.2 has changed its emitted syntax for synthetic constructors
when using `downlevelIteration`, which affects ES5 bundles that have
been downleveled from ES2015 bundles. This is typically the case for UMD
bundles in the APF spec, as they are generated by downleveling the
ESM2015 bundle into ES5. ngcc needs to detect the new syntax in order to
correctly identify synthesized constructor functions in ES5 bundles.

Fixes #41298

PR Close #41305
2021-03-23 11:23:04 -07:00
JoostK 66e9970691 feat(ngcc): support `__read` helper as used by TypeScript 4.2 (#41201)
This commit complements the support for the `__spreadArray` helper that
was added in microsoft/TypeScript#41523. The prior helpers `__spread`
and `__spreadArrays` used the `__read` helper internally, but the helper
is now emitted as an argument to `__spreadArray` so ngcc now needs to
support evaluating it statically. The real implementation of `__read`
reads an iterable into an array, but for ngcc's static evaluation
support it is sufficient to only deal with arrays as is. Additionally,
the optional `n` parameter is not supported as that is only emitted for
array destructuring syntax, which ngcc does not have to support.

PR Close #41201
2021-03-16 11:06:31 -07:00
JoostK 7b1214eca2 feat(ngcc): support `__spreadArray` helper as used by TypeScript 4.2 (#41201)
In TypeScript 4.2 the `__spread` and `__spreadArrays` helpers were both
replaced by the new helper function `__spreadArray` in
microsoft/TypeScript#41523. These helpers may be used in downleveled
JavaScript bundles that ngcc has to process, so ngcc has the ability to
statically detect these helpers and provide evaluation logic for them.
Because Angular is adopting support for TypeScript 4.2 it becomes
possible for libraries to be compiled by TypeScript 4.2 and thus ngcc
has to add support for the `__spreadArray` helper. The deprecated
`__spread` and `__spreadArrays` helpers are not affected by this change.

Closes #40394

PR Close #41201
2021-03-16 11:06:31 -07:00
JoostK 0093b3b19f fix(ngcc): do not compile JavaScript sources if typings-only processing is repeated (#41209)
The recently introduced typings-only mode in ngcc would incorrectly
write compiled JavaScript files if typings-only mode was requested, in
case the typings of the entry-point had already been processed in a
prior run of ngcc. The corresponding format property for which the
JavaScript files were written were not marked as processed, though, as
the typings-only mode excluded the format property itself from being
marked as processed. Consequently, subsequent runs of ngcc would not
consider the entry-point to have been processed and recompile the
JavaScript bundle once more, resulting in duplicate ngcc imports.

Fixes #41198

PR Close #41209
2021-03-16 09:33:53 -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 0b69fabcf5 fix(compiler-cli): ensure ngcc can handle wildcard base-paths (#41033)
Ngcc uses the `paths` property to compute the potential base-paths
for packages that are being processed. If the `paths` contain a wildcard
`*` within a path segment, ngcc was not finding the base-path correctly.

Now when a wildcard is found, there is an additional search to look for
paths that might match the wildcard.

Fixes #41014

PR Close #41033
2021-03-01 15:25:44 -08:00
George Kalpakas 284af7308b fix(ngcc): do not fail hard when a format-path points to a non-existing or empty file (#40985)
Previously, when `ngcc` encountered an entry-point with a format-path
that pointed to a non-existing or empty file it would throw an error and
stop processing the remaining tasks.

In the past, we used to ignore such format-paths and continue processing
the rest of the tasks ([see code][1]). This was changed to a hard
failure in 2954d1b5ca. Looking at the code
history, the reason for changing the behavior was an (incorrect)
assumption that the condition could not fail. This assumption failed to
take into account the case where a 3rd-party library has an invalid
format-path in its `package.json`. This is an issue with the library,
but it should not prevent `ngcc` from processing other
packages/entry-points/formats.

This commit fixes this by reporting the task as failed but not throwing
an error, thus allowing `ngcc` to continue processing other tasks.

[1]: https://github.com/angular/angular/blob/3077c9a1f89c5bd75fb96c16e/packages/compiler-cli/ngcc/src/main.ts#L124

Fixes #40965

PR Close #40985
2021-02-26 08:26:33 -08:00
Pete Bacon Darwin 8d13f631d9 refactor(ngcc): support processing only the typings files of packages (#40976)
Some tools (such as Language Server and ng-packagr) only care about
the processed typings generated by ngcc. Forcing these tools to process
the JavaScript files as well has two disadvantages:

First, unnecessary work is being done, which is time consuming.

But more importantly, it is not always possible to know how the final bundling
tools will want the processed JavaScript to be configured. For example,
the CLI would prefer the `--create-ivy-entry-points` option but this would
break non-CLI build tooling.

This commit adds a new option (`--typings-only` on the command line, and
`typingsOnly` via programmatic API) that instructs ngcc to only render changes
to the typings files for the entry-points that it finds, and not to write any
JavaScript files.

In order to process the typings, a JavaScript format will need to be analysed, but
it will not be rendered to disk. When using this option, it is best to offer ngcc a
wide range of possible JavaScript formats to choose from, and it will use the
first format that it finds. Ideally you would configure it to try the `ES2015` FESM
format first, since this will be the most performant.

Fixes #40969

PR Close #40976
2021-02-24 14:23:14 -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
Alex Rickabaugh 80f4ff3338 fix(compiler-cli): set TS original node on imported namespace identifiers (#40711)
This commit causes imports added by ngtsc's `ImportManager` to have their
TypeScript "original node" set to the generated `ts.ImportDeclaration`
statement.

In g3, the tsickle transformer runs after the Angular transformer and post-
processes Angular's compilation output. One of its post-processing tasks is
to transform generated imports and references to imported symbols from the
commonjs module system to the g3 module system. Part of this transformation
involves recognizing modules with specific metadata and altering references
to symbols from those modules accordingly.

Normally, tsickle can rely on TypeScript's binding for an imported symbol to
find its origin module and thus the correct metadata for the symbol. However
the Angular transform generates new synthetic imports which don't have such
binding information. Angular's imports are always namespace imports of the
form:

```
import * as qualifier 'module/specifier';
```

References to such an import are then of the form `qualifier.SymbolName`.

To process such imports properly, tsickle needs to be able to associate the
reference to `qualifier` in the expression `qualifer.SymbolName` with the
`ts.ImportDeclaration` statement that defines it. It expects to do this by
looking at the `ts.getOriginalNode()` for the `qualifier` reference, which
should be the `ts.ImportDeclaration`. This commit changes ngtsc's import
generation mechanism to set the original node on `qualifier` identifiers
according to this expectation.

This commit is not tested in the direct compiler tests, since:

1) there is no observable behavior externally from setting the original node
2) we don't have tests that intercept transformer operations (which could be
   used to directly assert against the AST nodes)
3) tsickle's published version does not (yet) contain the g3-specific
   transformations which rely on the original node and would thus allow the
   behavior to be observed.

Instead, we rely on the g3 testing suite to validate the correctness of this
fix. Breaking this functionality would cause g3 compilation errors for
targets, since tsickle would be unable to transform imports correctly.

PR Close #40711
2021-02-11 15:58:25 -08:00
Joey Perrott b75d7cb11f fix(compiler-cli): update type castings for JSON.parse usage (#40710)
Update usages of JSON.parse to be cast as specific types.

PR Close #40710
2021-02-09 10:48:43 -08:00
Pete Bacon Darwin dc06873c72 fix(compiler-cli): handle pseudo cycles in inline source-maps (#40435)
When a source-map has an inline source, any source-map linked from
that source should only be loaded if itself is also inline; it should not
attempt to load a source-map from the file-system. Otherwise we can
find ourselves with inadvertent infinite cyclic dependencies.

For example, if a transpiler takes a file (e.g. index.js) and generates
a new file overwriting the original file - capturing the original
source inline in the new source-map (index.js.map) - the source
file loader might read the inline original file (also index.js) and
then try to load the `index.js.map` file from disk - ad infinitum.

Note that the first call to `loadSourceFile()` is special, since you can
pass in the source-file and source-map contents directly as in-memory
strrngs. This is common if the transpiler has just generated these and has
not yet written them to disk.
When the contents are passed into `loadSourceFile()` directly, they are
not treated as "inline" for the purposes described above since there is
no chance of these "in-memory" source and source-map contents being caught
up in a cyclic dependency.

Fixes #40408

PR Close #40435
2021-01-21 14:06:57 -08:00
Alexey Elin cf02cf1e18 docs: remove duplicated the (#40434)
PR Close #40434
2021-01-14 11:33:57 -08:00
Pete Bacon Darwin b971bc6f70 perf(ngcc): do not copy files that have been processed (#40429)
When using the `NewEntryPointWriter`, we must copy over all files from the
entry-point bundle to the new entry-point. But since we are going to
write out the modified files directly, there is no need to copy those.
This commit skips copying the files that have been modified.

PR Close #40429
2021-01-14 11:30:39 -08:00
Pete Bacon Darwin ad0fb9c6bb fix(ngcc): copy (and update) source-maps for unmodified source files (#40429)
When using the `NewEntryPointWriter` we copy unmodified files over to the new
entry-point in addition to writing out the source files that are processed by ngcc.
But we were not copying over associated source-map files for these unmodified
source files, leading to warnings in downstream tooling.

Now we will also copy over source-maps that reside as siblings of unmodified
source files. We have to make sure that the sources of the source-map point
to the correct files, so we also update the `sourceRoot` property of the copied
source-map.

Fixes #40358

PR Close #40429
2021-01-14 11:30:39 -08:00
Pete Bacon Darwin afd11662a3 fix(ngcc): compute the correct package paths for target entry-points (#40376)
Previously, if there were path-mapped entry-points, where one contaied the
string of another - for example `worker-client` and `worker` - then the
base paths were incorrectly computed resulting in the wrong package path
for the longer entry-point. This was because, when searching for a matching
base path, the strings were tested using `startsWith()`, whereas we should
only match if the path was contained in a directory from a file-system
point of view.

Now we not only check whether the target path "starts with" the base path
but then also whether the target path is actually contained in the base path
using `fs.relative()`.

Fixes #40352
Fixes #40357

PR Close #40376
2021-01-11 10:38:16 -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
Pete Bacon Darwin 7c167629b8 fix(compiler-cli): ngcc - remove outdated link (#40285)
The link to the "speeding-up-ngcc-compilation" URL does not exist,
it was removed shortly after it was added, but the link in the ngcc
error message was not updated.

Fixes #39837

PR Close #40285
2021-01-06 07:10:39 -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
Bjarki ef892743ec feat(compiler): support tagged template literals in code generator (#39122)
Add a TaggedTemplateExpr to represent tagged template literals in
Angular's syntax tree (more specifically Expression in output_ast.ts).
Also update classes that implement ExpressionVisitor to add support for
tagged template literals in different contexts, such as JIT compilation
and conversion to JS.

Partial support for tagged template literals had already been
implemented to support the $localize tag used by Angular's i18n
framework. Where applicable, this code was refactored to support
arbitrary tags, although completely replacing the i18n-specific support
for the $localize tag with the new generic support for tagged template
literals may not be completely trivial, and is left as future work.

PR Close #39122
2020-12-07 16:20:04 -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
Charles Lyding 318255a5f8 build: support building with TypeScript 4.1 (#39571)
TypeScript 4.1 is now used to build and test within the repository.

PR Close #39571
2020-11-25 11:10:01 -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 0a63eeaff1 Revert "perf(ngcc): allow immediately reporting a stale lock file (#37250)" (#39435)
This reverts commit 561c0f81a0.

The original commit provided a quick escape from an already terminal
situation by killing the process if the PID in the lockfile was not
found in the list of processes running on the current machine.

But this broke use-cases where the node_modules was being shared between
multiple machines (or more commonly Docker containers on the same actual
machine).

Fixes #38875

PR Close #39435
2020-10-27 13:36:27 -07:00
Pete Bacon Darwin 413b55273b fix(ngcc): capture UMD/CommonJS inner class implementation node correctly (#39346)
Previously, UMD/CommonJS class inline declarations of the form:

```ts
exports.Foo = (function() { function Foo(); return Foo; })();
```

were capturing the whole IIFE as the implementation, rather than
the inner class (i.e. `function Foo() {}` in this case). This caused
the interpreter to break when it was trying to access such an export,
since it would try to evaluate the IIFE rather than treating it as a class
declaration.

PR Close #39346
2020-10-23 15:17:11 -07:00
Pete Bacon Darwin 822b838fbc fix(ngcc): ensure that "inline exports" can be interpreted correctly (#39267)
Previously, inline exports of the form `exports.foo = <implementation>;` were
being interpreted (by the ngtsc `PartialInterpeter`) as `Reference` objects.
This is not what is desired since it prevents the value of the export
from being unpacked, such as when analyzing `NgModule` declarations:

```
exports.directives = [Directive1, Directive2];

@NgImport({declarations: [exports.directives]})
class AppModule {}
```

In this example the interpreter would think that `exports.directives`
was a reference rather than an array that needs to be unpacked.

This bug was picked up by the ngcc-validation repository. See
https://github.com/angular/ngcc-validation/pull/1990 and
https://circleci.com/gh/angular/ngcc-validation/17130

PR Close #39267
2020-10-14 14:11:45 -07:00
JoostK 898be92f70 perf(ngcc): do not rescan program source files when referenced from multiple root files (#39254)
When ngcc is configured to run with the `--use-program-dependencies`
flag, as is the case in the CLI's asynchronous processing, it will scan
all source files in the program, starting from the program's root files
as configured in the tsconfig. Each individual root file could
potentially rescan files that had already been scanned for an earlier
root file, causing a severe performance penalty if the number of root
files is large. This would be the case if glob patterns are used in the
"include" specification of a tsconfig file.

This commit avoids the performance penalty by keeping track of the files
that have been scanned across all root files, such that no source file
is scanned multiple times.

Fixes #39240

PR Close #39254
2020-10-14 09:34:11 -07:00
Pete Bacon Darwin f4fee86f77 fix(ngcc): support inline export declarations in UMD files (#38959)
Previously, any declarations that were defined "inline" were not
recognised by the `UmdReflectionHost`.

For example, the following syntax was completely unrecognized:

```ts
var Foo_1;
exports.Foo = Foo_1 = (function() {
  function Foo() {}
  return Foo;
})();
exports.Foo = Foo_1 = __decorate(SomeDecorator, Foo);
```

Such inline classes were ignored and not processed by ngcc.

This lack of processing led to failures in Ivy applications that relied
on UMD formats of libraries such as `syncfusion/ej2-angular-ui-components`.

Now all known inline UMD exports are recognized and processed accordingly.

Fixes #38947

PR Close #38959
2020-10-12 08:32:46 -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
Pete Bacon Darwin 65997c0649 refactor(ngcc): simplify and break up ES2015 functions with helpers (#38959)
The protected helper functions can then be overridden by subclasses of the
Esm2015ReflectionHost.

PR Close #38959
2020-10-12 08:32:46 -07:00
Pete Bacon Darwin 1d6e67478e refactor(ngcc): simplify and rename `getClassDeclarationFromInnerDeclaration()` (#38959)
The new function does not try to restrict the kind of AST node that it
finds, leaving that to the caller. This will make it more resuable in the
UMD reflection host.

PR Close #38959
2020-10-12 08:32:46 -07:00
Pete Bacon Darwin 5038e5741b fix(ngcc): handle aliases in UMD export declarations (#38959)
Sometimes UMD exports appear in the following form:

```
exports.MyClass = alias1 = alias2 = <<declaration>>
```

Previously the declaration of the export would have been captured
as `alias1 = alias2 = <<declaration>>`, which the `PartialInterpreter`
would have failed on, since it cannot handle assignments.

Now we skip over these aliases capturing only the `<<declaration>>`
expression.

Fixes #38947

PR Close #38959
2020-10-12 08:32:46 -07:00
Pete Bacon Darwin 11485d96fb fix(ngcc): map `exports` to the current module in UMD files (#38959)
UMD files export values by assigning them to an `exports` variable.
When evaluating expressions ngcc was failing to cope with expressions
like `exports.MyComponent`.

This commit fixes the `UmdReflectionHost.getDeclarationOfIdentifier()`
method to map the `exports` variable to the current source file.

PR Close #38959
2020-10-12 08:32:46 -07:00
Pete Bacon Darwin 87274e3eec refactor(ngcc): rename `ExportStatement` to `ExportsStatement` (#38959)
This clarifies that this is specifically about statements of the form
`exports.<name> = <declaration>`, rather than a general export
statement such as `export class <ClassName> { ... }`.

PR Close #38959
2020-10-12 08:32:45 -07:00
Pete Bacon Darwin a5a7845593 refactor(ngcc): remove unused imports (#38959)
The `isAssignment` and `isAssignmentStatement` are not used in this file.

PR Close #38959
2020-10-12 08:32:45 -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
Pete Bacon Darwin 297b123151 refactor(compiler-cli): make the output AST translator generic (#38775)
This commit refactors the `ExpressionTranslatorVisitor` so that it
is not tied directly to the TypeScript AST. Instead it uses generic
`TExpression` and `TStatement` types that are then converted
to concrete types by the `TypeScriptAstFactory`.

This paves the way for a `BabelAstFactory` that can be used to
generate Babel AST nodes instead of TypeScript, which will be
part of the new linker tool.

PR Close #38775
2020-09-21 12:27:27 -07:00
JoostK e4424863c2 fix(ngcc): fix compilation of `ChangeDetectorRef` in pipe constructors (#38892)
In #38666 we changed how ngcc deals with type expressions, where it
would now always emit the original type expression into the generated
code as a "local" type value reference instead of synthesizing new
imports using an "imported" type value reference. This was done as a fix
to properly deal with renamed symbols, however it turns out that the
compiler has special handling for certain imported symbols, e.g.
`ChangeDetectorRef` from `@angular/core`. The "local" type value
reference prevented this special logic from being hit, resulting in
incorrect compilation of pipe factories.

This commit fixes the issue by manually inspecting the import of the
type expression, in order to return an "imported" type value reference.
By manually inspecting the import we continue to handle renamed symbols.

Fixes #38883

PR Close #38892
2020-09-18 08:02:46 -07:00
Pete Bacon Darwin d795a00137 refactor(compiler): replace Comment nodes with leadingComments property (#38811)
Common AST formats such as TS and Babel do not use a separate
node for comments, but instead attach comments to other AST nodes.
Previously this was worked around in TS by creating a `NotEmittedStatement`
AST node to attach the comment to. But Babel does not have this facility,
so it will not be a viable approach for the linker.

This commit refactors the output AST, to remove the `CommentStmt` and
`JSDocCommentStmt` nodes. Instead statements have a collection of
`leadingComments` that are rendered/attached to the final AST nodes
when being translated or printed.

PR Close #38811
2020-09-18 08:01:25 -07:00
JoostK fd44d84a33 perf(ngcc): reduce maximum worker count (#38840)
Recent optimizations to ngcc have significantly reduced the total time
it takes to process `node_modules`, to such extend that sharding across
multiple processes has become less effective. Previously, running
ngcc asynchronously would allow for up to 8 workers to be allocated,
however these workers have to repeat work that could otherwise be shared.
Because ngcc is now able to reuse more shared computations, the overhead
of multiple workers is increased and therefore becomes less effective.
As an additional benefit, having fewer workers requires less memory and
less startup time.

To give an idea, using the following test setup:

```bash
npx @angular/cli new perf-test
cd perf-test
yarn ng add @angular/material
./node_modules/.bin/ngcc --properties es2015 module main \
  --first-only --create-ivy-entry-points
```

We observe the following figures on CI:

|                   | 10.1.1    | PR #38840 |
| ----------------- | --------- | --------- |
| Sync              | 85s       | 25s       |
| Async (8 workers) | 22s       | 16s       |
| Async (4 workers) | -         | 11s       |

In addition to changing the default number of workers, ngcc will now
use the environment variable `NGCC_MAX_WORKERS` that may be configured
to either reduce or increase the number of workers.

PR Close #38840
2020-09-15 11:23:09 -07:00
JoostK f0688b4d18 perf(ngcc): introduce cache for sharing data across entry-points (#38840)
ngcc creates typically two `ts.Program` instances for each entry-point,
one for processing sources and another one for processing the typings.
The creation of these programs is somewhat expensive, as it concerns
module resolution and parsing of source files.

This commit implements several layers of caching to optimize the
creation of programs:

1. A shared module resolution cache across all entry-points within a
   single invocation of ngcc. Both the sources and typings program
   benefit from this cache.
2. Sharing the parsed `ts.SourceFile` for a single entry-point between
   the sources and typings program.
3. Sharing parsed `ts.SourceFile`s of TypeScript's default libraries
   across all entry-points within a single invocation. Some of these
   default library typings are large and therefore expensive to parse,
   so sharing the parsed source files across all entry-points offers
   a significant performance improvement.

Using a bare CLI app created using `ng new` + `ng add @angular/material`,
the above changes offer a 3-4x improvement in ngcc's processing time
when running synchronously and ~2x improvement for asynchronous runs.

PR Close #38840
2020-09-15 11:23:04 -07:00
Pete Bacon Darwin 7869de6136 fix(ngcc): use aliased exported types correctly (#38666)
If a type has been renamed when it was exported, we need to
reference the external public alias name rather than the internal
original name for the type. Otherwise we will try to import the
type by its internal name, which is not publicly accessible.

Fixes #38238

PR Close #38666
2020-09-08 11:41:21 -07:00