Commit Graph

96 Commits

Author SHA1 Message Date
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
Alex Rickabaugh 3613e7c4e5 test(compiler-cli): move testing utils to separate package (#39594)
ngtsc has a robust suite of testing utilities, designed for in-memory
testing of a TypeScript compiler. Previously these utilities lived in the
`test` directory for the compiler-cli package.

This commit moves those utilities to an `ngtsc/testing` package, enabling
them to be depended on separately and opening the door for using them from
the upcoming language server testing infrastructure.

As part of this refactoring, the `fake_core` package (a lightweight API
replacement for @angular/core) is expanded to include functionality needed
for Language Service test use cases.

PR Close #39594
2020-11-17 11:59:56 -08: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
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 2c0282f4c2 test(ngcc): use `isNamedFunctionDeclaration()` in UMD tests (#38959)
This makes these tests more resilient to changes in the test code
structure. For example switching from

```
var SomeClass = <implementation>;
exports.SomeClass = SomeClass;
```

to

```
exports.SomeClass = <implementation>;
```

PR Close #38959
2020-10-12 08:32:46 -07:00
Pete Bacon Darwin 6650d71fe2 test(compiler-cli): make the `getDeclaration()` utility more resilient to code format (#38959)
Previously `getDeclaration()` would only return the first node that matched
the name passed in and then assert the predicate on this single node.
It also only considered a subset of possible declaration types that we might
care about.

Now the function will parse the whole tree collecting an array of all the
nodes that match the name. It then filters this array based on the predicate
and only errors if the filtered array is empty.

This makes this function much more resilient to more esoteric code formats
such as UMD.

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 acfce0ba1b test(ngcc): fix incorrect test setup (#38959)
The `SIMPLE_CLASS_FILE` contained a `ChildClass` that had an
internal aliases implementation and extended a `SuperClass` base
class. The call to `__extends` was using the wrong argument for
the child class.

PR Close #38959
2020-10-12 08:32:45 -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 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
Paul Gschwendtner 3b9c802dee fix(ngcc): detect synthesized delegate constructors for downleveled ES2015 classes (#38463)
Similarly to the change we landed in the `@angular/core` reflection
capabilities, we need to make sure that ngcc can detect pass-through
delegate constructors for classes using downleveled ES2015 output.

More details can be found in the preceding commit, and in the issue
outlining the problem: #38453.

Fixes #38453.

PR Close #38463
2020-08-17 10:55:40 -07:00
JoostK 18098d38b8 fix(compiler-cli): avoid creating value expressions for symbols from type-only imports (#37912)
In TypeScript 3.8 support was added for type-only imports, which only brings in
the symbol as a type, not their value. The Angular compiler did not yet take
the type-only keyword into account when representing symbols in type positions
as value expressions. The class metadata that the compiler emits would include
the value expression for its parameter types, generating actual imports as
necessary. For type-only imports this should not be done, as it introduces an
actual import of the module that was originally just a type-only import.

This commit lets the compiler deal with type-only imports specially, preventing
a value expression from being created.

Fixes #37900

PR Close #37912
2020-08-11 09:53:25 -07:00
Pete Bacon Darwin 6de5a12a9d refactor(ngcc): move `logging` code into `ngtsc` (#37114)
The `Logger` interface and its related classes are general purpose
and could be used by other tooling. Moving it into ngtsc is a more
suitable place from which to share it - similar to the FileSystem stuff.

PR Close #37114
2020-06-22 13:38:47 -07:00
Pete Bacon Darwin 818d93d7e9 fix(ngcc): find decorated constructor params on IIFE wrapped classes (#37436)
Now in TS 3.9, classes in ES2015 can be wrapped in an IIFE.
This commit ensures that we still find the static properties that contain
decorator information, even if they are attached to the adjacent node
of the class, rather than the implementation or declaration.

Fixes #37330

PR Close #37436
2020-06-05 09:22:04 -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 97e13991c5 fix(ngcc): identifier ModuleWithProviders functions in IIFE wrapped classes (#37206)
In ES2015 IIFE wrapped classes, the identifier that would reference the class
of the NgModule may be an alias variable. Previously the `Esm2015ReflectionHost`
was not able to match this alias to the original class declaration. This resulted
in failing to identify some `ModuleWithProviders` functions in such case.

These IIFE wrapped classes were introduced in TypeScript 3.9, which is why
this issue is only recently appearing. Since 9.1.x does not support TS 3.9
there is no reason to backport this commit to that branch.

Fixes #37189

PR Close #37206
2020-05-20 13:30:32 -07:00
Pete Bacon Darwin 03fef736d6 test(ngcc): give adjacent class identifier a distinct name (#37206)
To better check that the code is working, this commit gives a
distinct name (`DecoratedWrappedClass_1`) to the "adjacent"
class declaration in the tests.

PR Close #37206
2020-05-20 13:30:31 -07:00
Pete Bacon Darwin 91092f668e fix(ngcc): support `defineProperty()` re-exports in CommonJS and UMD (#36989)
In TypeScript 3.9 some re-export syntaxes have changed to be getter
functions (created by calls to `Object.defineProperty()`) rather than
simple property accessors.

This commit adds support into the CommonJS and UMD reflection hosts
for this style of re-export syntax.

PR Close #36989
2020-05-14 10:50:29 -07:00
Pete Bacon Darwin d268d2ad85 fix(ngcc): `viaModule` should be `null` for local imports (#36989)
In the CommonJS and UMD reflection hosts, the logic for computing the
`viaModule` property of `Declaration` objects was not correct for some
cases when getting the exports of modules.

In these cases it was setting `viaModule` to the path of the local module
rather than `null`.

PR Close #36989
2020-05-14 10:50:29 -07:00
Pete Bacon Darwin 9846b19986 test(ngcc): reformat some subject code for tests (#36989)
Using backtick multiline strings leads to confusing layout
that does not fit with the surrounding indentation. Also it
can lead to test fragility due to automated code formatting.

This commit changes just one set of subject code to use
a more resilient string concatenation approach.

PR Close #36989
2020-05-14 10:50:29 -07:00
Pete Bacon Darwin 2956f21d82 test(ngcc): move specs to correct describe block (#36989)
The recent tests for Enum handling were added to the
incorrect describe block. This commit moves them back
to the correct block.

PR Close #36989
2020-05-14 10:50:29 -07:00
Pete Bacon Darwin d7440c452a fix(ngcc): ensure reflection hosts can handle TS 3.9 IIFE wrapped classes (#36989)
In TS 3.9, ES2015 output can contain ES classes that are wrapped in an
IIFE. So now ES2015 class declarations can look like one of:

```
class OuterClass1 {}
```

```
let OuterClass = class InnerClass {};
```

```
var AliasClass;
let OuterClass = AliasClass = class InnerClass {};
```

```
let OuterClass = (() => class InnerClass {}};
```

```
var AliasClass;
let OuterClass = AliasClass = (() => class InnerClass {})();
```

```
let OuterClass = (() => {
  let AdjacentClass = class InnerClass {};
  // ... static properties or decorators attached to `AdjacentClass`
  return AdjacentClass;
})();
```

```
var AliasClass;
let OuterClass = AliasClass = (() => {
  let AdjacentClass = class InnerClass {};
  // ... static properties or decorators attached to `AdjacentClass`
  return AdjacentClass;
})();
```

The `Esm5ReflectionHost` already handles slightly different IIFE wrappers
around function-based classes. This can be substantially reused when
fixing `Esm2015ReflectionHost`, since there is a lot of commonality
between the two.

This commit moves code from the `Esm5ReflectionHost` into the `Esm2015ReflectionHost`
and looks to share as much as possible between the two hosts.

PR Close #36989
2020-05-14 10:50:29 -07:00
Pete Bacon Darwin 7ebf35c5f8 refactor(ngcc): remove unused import (#36989)
The `Import` import from `src/ngtsc/reflection` is not being used.
This commit simply removes this import from the code.

PR Close #36989
2020-05-14 10:50:28 -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
Pete Bacon Darwin db4c59dad9 fix(ngcc): support TS 3.9 wrapped ES2015 classes (#36884)
In TS 3.9 the compiler will start to wrap ES2015 classes in an IIFE to help with
tree-shaking when the class has "associated" statements.

E.g.

```ts
let PlatformLocation = /** @class */ (() => {
    ...
    class PlatformLocation {
    }
    ...
    return PlatformLocation;
})();
```

This commit updates `Esm2015ReflectionHost` to support this format.

PR Close #36884
2020-05-04 12:48:26 -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
JoostK 4aa4e6fd03 fix(compiler): handle type references to namespaced symbols correctly (#36106)
When the compiler needs to convert a type reference to a value
expression, it may encounter a type that refers to a namespaced symbol.
Such namespaces need to be handled specially as there's various forms
available. Consider a namespace named "ns":

1. One can refer to a namespace by itself: `ns`. A namespace is only
   allowed to be used in a type position if it has been merged with a
   class, but even if this is the case it may not be possible to convert
   that type into a value expression depending on the import form. More
   on this later (case a below)
2. One can refer to a type within the namespace: `ns.Foo`. An import
   needs to be generated to `ns`, from which the `Foo` property can then
   be read.
3. One can refer to a type in a nested namespace within `ns`:
   `ns.Foo.Bar` and possibly even deeper nested. The value
   representation is similar to case 2, but includes additional property
   accesses.

The exact strategy of how to deal with these cases depends on the type
of import used. There's two flavors available:

a. A namespaced import like `import * as ns from 'ns';` that creates
   a local namespace that is irrelevant to the import that needs to be
   generated (as said import would be used instead of the original
   import).

   If the local namespace "ns" itself is referred to in a type position,
   it is invalid to convert it into a value expression. Some JavaScript
   libraries publish a value as default export using `export = MyClass;`
   syntax, however it is illegal to refer to that value using "ns".
   Consequently, such usage in a type position *must* be accompanied by
   an `@Inject` decorator to provide an explicit token.

b. An explicit namespace declaration within a module, that can be
   imported using a named import like `import {ns} from 'ns';` where the
   "ns" module declares a namespace using `declare namespace ns {}`.
   In this case, it's the namespace itself that needs to be imported,
   after which any qualified references into the namespace are converted
   into property accesses.

Before this change, support for namespaces in the type-to-value
conversion was limited and only worked  correctly for a single qualified
name using a namespace import (case 2a). All other cases were either
producing incorrect code or would crash the compiler (case 1a).

Crashing the compiler is not desirable as it does not indicate where
the issue is. Moreover, the result of a type-to-value conversion is
irrelevant when an explicit injection token is provided using `@Inject`,
so referring to a namespace in a type position (case 1) could still be
valid.

This commit introduces logic to the type-to-value conversion to be able
to properly deal with all type references to namespaced symbols.

Fixes #36006
Resolves FW-1995

PR Close #36106
2020-04-09 11:32:21 -07:00
George Kalpakas 5fa7b8ba56 fix(ngcc): detect non-emitted, non-imported TypeScript helpers (#36418)
When TypeScript downlevels ES2015+ code to ES5, it uses some helper
functions to emulate some ES2015+ features, such as spread syntax. The
TypeScript compiler can be configured to emit these helpers into the
transpiled code (which is controlled by the `noEmitHelpers` option -
false by default). It can also be configured to import these helpers
from the `tslib` module (which is controlled by the `importHelpers`
option - false by default).

While most of the time the helpers will be either emitted or imported,
it is possible that one configures their app to neither emit nor import
them. In that case, the helpers could, for example, be made available on
the global object. This is what `@nativescript/angular`
v9.0.0-next-2019-11-12-155500-01 does. See, for example, [common.js][1].

Ngcc must be able to detect and statically evaluate these helpers.
Previously, it was only able to detect emitted or imported helpers.

This commit adds support for detecting these helpers if they are neither
emitted nor imported. It does this by checking identifiers for which no
declaration (either concrete or inline) can be found against a list of
known TypeScript helper function names.

[1]: https://unpkg.com/browse/@nativescript/angular@9.0.0-next-2019-11-12-155500-01/common.js

PR Close #36418
2020-04-07 10:19:22 -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
George Kalpakas 93f07aee6c test(ngcc): use `DelegatingReflectionHost` for testing `NgccReflectionHost`s (#36284)
In #36089, `DelegatingReflectionHost` was introduced. Under the hood, it
delegates another `NgccReflectionHost` in order to reflect over the
program's source files, while using a different TypeScript
`ReflectionHost` to reflect over `.d.ts` files (which is how external
dependencies are represented in the program).

Previously, the `NgccReflectionHost`s were used directly in tests. This
does not exercise them in the way they are exercised in the actual
program, because (when used directly) they will also reflect on `.d.ts`
files too (instead of delegating to the TypeScript `ReflectionHost`).
This could hide bugs that would happen on the actual program.

This commit fixes this by using the `DelegatingReflectionHost` in the
various `NgccReflectionHost` tests.

NOTE:
This change will cause some of the existing tests to start failing.
These failures demonstrate pre-existing bugs in ngcc, that were hidden
due to the tests' being inconsistent with how the `ReflectionHost`s are
used in the actual program. They will be fixed in the next commit.

PR Close #36284
2020-04-03 11:08:46 -07:00
JoostK 9e70bcb34f fix(ngcc): consistently delegate to TypeScript host for typing files (#36089)
When ngcc is compiling an entry-point, it uses a `ReflectionHost` that
is specific to its format, e.g. ES2015, ES5, UMD or CommonJS. During the
compilation of that entry-point however, the reflector may be used to
reflect into external libraries using their declaration files.

Up until now this was achieved by letting all `ReflectionHost` classes
consider their parent class for reflector queries, thereby ending up in
the `TypeScriptReflectionHost` that is a common base class for all
reflector hosts. This approach has proven to be prone to bugs, as
failing to call into the base class would cause incompatibilities with
reading from declaration files.

The observation can be made that there's only two distinct kinds of
reflection host queries:
1. the reflector query is about code that is part of the entry-point
   that is being compiled, or
2. the reflector query is for an external library that the entry-point
   depends on, in which case the information is reflected
   from the declaration files.

The `ReflectionHost` that was chosen for the entry-point should serve
only reflector queries for the first case, whereas a regular
`TypeScriptReflectionHost` should be used for the second case. This
avoids the problem where a format-specific `ReflectionHost` fails to
handle the second case correctly, as it isn't even considered for such
reflector queries.

This commit introduces a `ReflectionHost` that delegates to the
`TypeScriptReflectionHost` for AST nodes within declaration files,
otherwise delegating to the format-specific `ReflectionHost`.

Fixes #35078
Resolves FW-1859

PR Close #36089
2020-03-17 13:34:04 -07:00
Alex Rickabaugh 95c729f5d1 build: typescript 3.8 support (#35864)
This commit adds support in the Angular monorepo and in the Angular
compiler(s) for TypeScript 3.8. All packages can now compile with
TS 3.8.

For most of the repo, only a handful few typings adjustments were needed:

* TS 3.8 has a new `CustomElementConstructor` DOM type, which enforces a
  zero-argument constructor. The `NgElementConstructor` type previously
  declared a required `injector` argument despite the fact that its
  implementation allowed `injector` to be optional. The interface type was
  updated to reflect the optionality of the argument.
* Certain error messages were changed, and expectations in tests were
  updated as a result.
* tsserver (part of language server) now returns performance information in
  responses, so test expectations were changed to only assert on the actual
  body content of responses.

For compiler-cli and schematics (which use the TypeScript AST) a major
breaking change was the introduction of the export form:

```typescript
export * as foo from 'bar';
```

This is a `ts.NamespaceExport`, and the `exportClause` of a
`ts.ExportDeclaration` can now take this type as well as `ts.NamedExports`.
This broke a lot of places where `exportClause` was assumed to be
`ts.NamedExports`.

For the most part these breakages were in cases where it is not necessary
to handle the new `ts.NamedExports` anyway. ngtsc's design uses the
`ts.TypeChecker` APIs to understand syntax and so automatically supports the
new form of exports.

The View Engine compiler on the other hand extracts TS structures into
metadata.json files, and that format was not designed for namespaced
exports. As a result it will take a nontrivial amount of work if we want to
support such exports in View Engine. For now, these new exports are not
accounted for in metadata.json, and so using them in "folded" Angular
expressions will result in errors (probably claiming that the referenced
exported namespace doesn't exist).

Care was taken to only use TS APIs which are present in 3.7/3.6, as Angular
needs to remain compatible with these for the time being.

This commit does not update angular.io.

PR Close #35864
2020-03-10 17:51:20 -04:00
George Kalpakas bd6a39c364 fix(ngcc): correctly detect emitted TS helpers in ES5 (#35191)
In ES5 code, TypeScript requires certain helpers (such as
`__spreadArrays()`) to be able to support ES2015+ features. These
helpers can be either imported from `tslib` (by setting the
`importHelpers` TS compiler option to `true`) or emitted inline (by
setting the `importHelpers` and `noEmitHelpers` TS compiler options to
`false`, which is the default value for both).

Ngtsc's `StaticInterpreter` (which is also used during ngcc processing)
is able to statically evaluate some of these helpers (currently
`__assign()`, `__spread()` and `__spreadArrays()`), as long as
`ReflectionHost#getDefinitionOfFunction()` correctly detects the
declaration of the helper. For this to happen, the left-hand side of the
corresponding call expression (i.e. `__spread(...)` or
`tslib.__spread(...)`) must be evaluated as a function declaration for
`getDefinitionOfFunction()` to be called with.

In the case of imported helpers, the `tslib.__someHelper` expression was
resolved to a function declaration of the form
`export declare function __someHelper(...args: any[][]): any[];`, which
allows `getDefinitionOfFunction()` to correctly map it to a TS helper.

In contrast, in the case of emitted helpers (and regardless of the
module format: `CommonJS`, `ESNext`, `UMD`, etc.)), the `__someHelper`
identifier was resolved to a variable declaration of the form
`var __someHelper = (this && this.__someHelper) || function () { ... }`,
which upon further evaluation was categorized as a `DynamicValue`
(prohibiting further evaluation by the `getDefinitionOfFunction()`).

As a result of the above, emitted TypeScript helpers were not evaluated
in ES5 code.

---
This commit changes the detection of TS helpers to leverage the existing
`KnownFn` feature (previously only used for built-in functions).
`Esm5ReflectionHost` is changed to always return `KnownDeclaration`s for
TS helpers, both imported (`getExportsOfModule()`) as well as emitted
(`getDeclarationOfIdentifier()`).

Similar changes are made to `CommonJsReflectionHost` and
`UmdReflectionHost`.

The `KnownDeclaration`s are then mapped to `KnownFn`s in
`StaticInterpreter`, allowing it to statically evaluate call expressions
involving any kind of TS helpers.

Jira issue: https://angular-team.atlassian.net/browse/FW-1689

PR Close #35191
2020-02-21 09:06:46 -08:00
George Kalpakas b6e8847967 fix(ngcc): handle imports in dts files when processing CommonJS (#35191)
When statically evaluating CommonJS code it is possible to find that we
are looking for the declaration of an identifier that actually came from
a typings file (rather than a CommonJS file).

Previously, the CommonJS reflection host would always try to use a
CommonJS specific algorithm for finding identifier declarations, but
when the id is actually in a typings file this resulted in the returned
declaration being the containing file of the declaration rather than the
declaration itself.

Now the CommonJS reflection host will check to see if the file
containing the identifier is a typings file and use the appropriate
stategy.

(Note: This is the equivalent of #34356 but for CommonJS.)

PR Close #35191
2020-02-21 09:06:46 -08:00
George Kalpakas fde89156fa fix(ngcc): correctly detect outer aliased class identifiers in ES5 (#35527)
In ES5 and ES2015, class identifiers may have aliases. Previously, the
`NgccReflectionHost`s recognized the following formats:
- ES5:
    ```js
    var MyClass = (function () {
      function InnerClass() {}
      InnerClass_1 = InnerClass;
      ...
    }());
    ```
- ES2015:
    ```js
    let MyClass = MyClass_1 = class MyClass { ... };
    ```

In addition to the above, this commit adds support for recognizing an
alias outside the IIFE in ES5 classes (which was previously not
supported):
```js
var MyClass = MyClass_1 = (function () { ... }());
```

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

Partially addresses #35399.

PR Close #35527
2020-02-20 15:12:07 -08:00
George Kalpakas 5f57376899 test(ngcc): add missing `UmdReflectionHost#getExportsOfModule()` tests (#35312)
Support for re-exports in UMD were added in e9fb5fdb8. This commit adds
some tests for re-exports (similar to the ones used for
`CommonJsReflectionHost`).

PR Close #35312
2020-02-10 16:13:41 -08:00
George Kalpakas 5b42084912 fix(ngcc): do not collect private declarations from external packages (#34811)
Previously, while trying to build an `NgccReflectionHost`'s
`privateDtsDeclarationMap`, `computePrivateDtsDeclarationMap()` would
try to collect exported declarations from all source files of the
program (i.e. without checking whether they were within the target
package, as happens for declarations in `.d.ts` files).

Most of the time, that would not be a problem, because external packages
would be represented as `.d.ts` files in the program. But when an
external package had no typings, the JS files would be used instead. As
a result, the `ReflectionHost` would try to (unnecessarilly) parse the
file in order to extract exported declarations, which in turn would be
harmless in most cases.

There are certain cases, though, where the `ReflectionHost` would throw
an error, because it cannot parse the external package's JS file. This
could happen, for example, in `UmdReflectionHost`, which expects the
file to contain exactly one statement. See #34544 for more details on a
real-world failure.

This commit fixes the issue by ensuring that
`computePrivateDtsDeclarationMap()` will only collect exported
declarations from files within the target package.

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

Fixes #34544

PR Close #34811
2020-01-23 13:58:37 -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
George Kalpakas c3271ac22a fix(ngcc): recognize re-exports with imported TS helpers in CommonJS and UMD (#34527)
Previously, the `CommonJsReflectionHost` and `UmdReflectionHost` would
only recognize re-exports of the form `__export(...)`. This is what
re-exports look like, when the TypeScript helpers are emitted inline
(i.e. when compiling with the default [TypeScript compiler options][1]
that include `noEmitHelpers: false` and `importHelpers: false`).

However, when compiling with `importHelpers: true` and [tslib][2] (which
is the recommended way for optimized bundles), the re-exports will look
like: `tslib_1.__exportStar(..., exports)`
These types of re-exports were previously not recognized by the
CommonJS/UMD `ReflectionHost`s and thus ignored.

This commit fixes this by ensuring both re-export formats are
recognized.

[1]: https://www.typescriptlang.org/docs/handbook/compiler-options.html
[2]: https://www.npmjs.com/package/tslib

PR Close #34527
2020-01-10 08:28:50 -08:00
George Kalpakas d5fd742763 fix(ngcc): recognize re-exports with `require()` calls in UMD (#34512)
Previously, `UmdReflectionHost` would only recognize re-exports of the
form `__export(someIdentifier)` and not `__export(require('...'))`.
However, it is possible in some UMD variations to have the latter format
as well. See discussion in https://github.com/angular/angular/pull/34254/files#r359515373

This commit adds support for re-export of the form
`__export(require('...'))` in UMD.

PR Close #34512
2020-01-08 15:00:49 -08:00
George Kalpakas 6654f82522 fix(ngcc): correctly handle inline exports in UMD (#34512)
This fix was part of a broader `ngtsc`/`ngcc` fix in 02bab8cf9 (see
there for details). In 02bab8cf9, the fix was only applied to
`CommonJsReflectionHost`, but it is equally applicable to
`UmdReflectionHost`. Later in #34254, the fix was partially ported to
`UmdReflectionHost` by fixing the `extractUmdReexports()` method.

This commit fully fixes `ngcc`'s handling of inline exports for code in
UMD format.

PR Close #34512
2020-01-08 15:00:49 -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
Pete Bacon Darwin e9fb5fdb89 fix(ngcc): handle UMD re-exports (#34254)
In TS we can re-export imports using statements of the form:

```
export * from 'some-import';
```

This is downleveled in UMD to:

```
function factory(exports, someImport) {
  function __export(m) {
    for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
  }
  __export(someImport);
}
```

This commit adds support for this.

PR Close #34254
2019-12-18 11:25:01 -08:00
Pete Bacon Darwin 47666f548c fix(ngcc): handle CommonJS re-exports by reference (#34254)
In TS we can re-export imports using statements of the form:

```
export * from 'some-import';
```

This can be downleveled in CommonJS to either:

```
__export(require('some-import'));
```

or

```
var someImport = require('some-import');
__export(someImport);
```

Previously we only supported the first downleveled version.
This commit adds support for the second version.

PR Close #34254
2019-12-18 11:25:01 -08:00
Pete Bacon Darwin 0b837e2f0d refactor(ngcc): use bundle src to create reflection hosts (#34254)
Previously individual properties of the src bundle program were
passed to the reflection host constructors. But going forward,
more properties will be required. To prevent the signature getting
continually larger and more unwieldy, this change just passes the
whole src bundle to the constructor, allowing it to extract what it
needs.

PR Close #34254
2019-12-18 11:25:01 -08:00
George Kalpakas 9cabd6638e refactor(ngcc): un-nest accidentally nested `describe()` blocks (#34437)
PR Close #34437
2019-12-17 11:39:18 -08:00
Pete Bacon Darwin c77656e2dd fix(ngcc): handle imports in dts files when processing UMD (#34356)
When statically evalulating UMD code it is possible to find
that we are looking for the declaration of an identifier that
actually came from a typings file (rather than a UMD file).

Previously, the UMD reflection host would always try to use
a UMD specific algorithm for finding identifier declarations,
but when the id is actually in a typings file this resulted in the
returned declaration being the containing file of the declaration
rather than the declaration itself.

Now the UMD reflection host will check to see if the file containing
the identifier is a typings file and use the appropriate stategy.

PR Close #34356
2019-12-11 13:20:49 -08:00