Previously, ngtsc would resolve forward references while evaluating the
bootstrap, declaration, imports, and exports fields of NgModule types.
However, when generating the resulting ngModuleDef, the forward nature of
these references was not taken into consideration, and so the generated JS
code would incorrectly reference types not yet declared.
This commit fixes this issue by introducing function closures in the
NgModuleDef type, similarly to how NgComponentDef uses them for forward
declarations of its directives and pipes arrays. ngtsc will then generate
closures when required, and the runtime will unwrap them if present.
PR Close#29198
This fixes an issue with commit b6f6b117. In this commit, default imports
processed in a type-to-value conversion were recorded as non-local imports
with a '*' name, and the ImportManager generated a new default import for
them. When transpiled to ES2015 modules, this resulted in the following
correct code:
import i3 from './module';
// somewhere in the file, a value reference of i3:
{type: i3}
However, when the AST with this synthetic import and reference was
transpiled to non-ES2015 modules (for example, to commonjs) an issue
appeared:
var module_1 = require('./module');
{type: i3}
TypeScript renames the imported identifier from i3 to module_1, but doesn't
substitute later references to i3. This is because the import and reference
are both synthetic, and never went through the TypeScript AST step of
"binding" which associates the reference to its import. This association is
important during emit when the identifiers might change.
Synthetic (transformer-added) imports will never be bound properly. The only
possible solution is to reuse the user's original import and the identifier
from it, which will be properly downleveled. The issue with this approach
(which prompted the fix in b6f6b117) is that if the import is only used in a
type position, TypeScript will mark it for deletion in the generated JS,
even though additional non-type usages are added in the transformer. This
again would leave a dangling import.
To work around this, it's necessary for the compiler to keep track of
identifiers that it emits which came from default imports, and tell TS not
to remove those imports during transpilation. A `DefaultImportTracker` class
is implemented to perform this tracking. It implements a
`DefaultImportRecorder` interface, which is used to record two significant
pieces of information:
* when a WrappedNodeExpr is generated which refers to a default imported
value, the ts.Identifier is associated to the ts.ImportDeclaration via
the recorder.
* when that WrappedNodeExpr is later emitted as part of the statement /
expression translators, the fact that the ts.Identifier was used is
also recorded.
Combined, this tracking gives the `DefaultImportTracker` enough information
to implement another TS transformer, which can recognize default imports
which were used in the output of the Ivy transform and can prevent them
from being elided. This is done by creating a new ts.ImportDeclaration for
the imports with the same ts.ImportClause. A test verifies that this works.
PR Close#29266
Prior to this change default selector for Components was not applied in case selector is missing or defined as an empty string. This update aligns this behavior between Ivy and VE: now default selector is used for Components when it's needed. Directives with empty selector are not allowed and trigger a compile-time error in both Ivy and VE.
PR Close#29239
`api-extractor` binary is required for external consumers of `ng_module` that want to use the `bundle_dts` flag.
This also sets a different api-exttractor binary to use for ng_module, based if it's internal or external.
PR Close#29202
Prior to this change the code didn't take into account the fact that decorators can be aliases while importing into a script. As a result, these decorators were not recognized by Angular and various failures happened because of that. Now we take aliases into account and resolve decorator name properly.
PR Close#29195
`getCurrentDirectory` directory doesn't return a posix separated normalized path. While `rootDir` and `rootDirs` should return posix separated paths, it's best to not assume as other paths within the compiler options can be returned not posix separated such as `basePath`
See: https://github.com/Microsoft/TypeScript/blob/master/src/compiler/sys.ts#L635
This partially fixes#29140, however there needs to be a change in the CLI as well to handle this, as at the moment we are leaking devkit paths which is not correct.
Fixes#29140
PR Close#29151
Sometimes declarations are not exported publicly but are exported under
a private name. In this case, rather than adding a completely new
export to the entry point, we should create an export that aliases the
private name back to the original public name.
This is important when the typings files have been rolled-up using a tool
such as the [API Extractor](https://api-extractor.com/). In this case
the internal type of an aliased private export will be removed completely
from the typings file, so there is no "original" type to re-export.
For example:
If there are the following TS files:
**entry-point.ts**
```ts
export {Internal as External} from './internal';
```
**internal.ts**
```ts
export class Internal {
foo(): void;
}
```
Then the API Extractor might roll up the .d.ts files into:
```ts
export declare class External {
foo(): void;
}
```
In this case ngcc should add an export so the file looks like:
```ts
export declare class External {
foo(): void;
}
export {External as Internal};
```
PR Close#28735
ngsummary files were generated with an export for each class declaration.
However, some Angular code declares classes (class Foo) and exports them
(export {Foo}) separately, which was causing incomplete summary files.
This commit expands the set of symbol names for which summary exports will
be generated, fixing this issue.
PR Close#29193
Previously, when the NgModule scope resolver discovered semantic errors
within a users NgModules, it would throw assertion errors. TODOs in the
codebase indicated these should become ts.Diagnostics eventually.
Besides producing better-looking errors, there is another reason to make
this change asap: these assertions were shadowing actual errors, via an
interesting mechanism:
1) a component would produce a ts.Diagnostic during its analyze() step
2) as a result, it wouldn't register component metadata with the scope
resolver
3) the NgModule for the component references it in exports, which was
detected as an invalid export (no metadata registering it as a
component).
4) the resulting assertion error would crash the compiler, hiding the
real cause of the problem (an invalid component).
This commit should mitigate this problem by converting scoping errors to
proper ts.Diagnostics. Additionally, we should consider registering some
marker indicating a class is a directive/component/pipe without actually
requiring full metadata to be produced for it, which would allow suppression
of errors like "invalid export" for such invalid types.
PR Close#29191
At the moment, certain tests relies on resolving the module with an index.d.ts, this root cause might be some implementations are missing from the mocks.
Similar to: 58b4045359
PR Close#28884
This commit refactors and expands ngtsc's support for generating imports of
values from imports of types (this is used for example when importing a
class referenced in a type annotation in a constructor).
Previously, this logic handled "import {Foo} from" and "import * as foo
from" style imports, but failed on imports of default values ("import
Foo from"). This commit moves the type-to-value logic to a separate file and
expands it to cover the default import case. Doing this also required
augmenting the ImportManager to track default as well as non-default import
generation. The APIs were made a little cleaner at the same time.
PR Close#29146
In the TypeScript compiler API, emit() can be performed either on a single
ts.SourceFile or on the entire ts.Program simultaneously.
ngtsc previously used whole-program emit, which was convenient to use while
spinning up the project but has a significant drawback: it causes a type
checking operation to occur for the whole program, including .d.ts files.
In large Bazel environments (such as Google's codebase), an ngtsc invocation
can have a few .ts files and thousands of .d.ts inputs. This unwanted type
checking is therefore a significant drain on performance.
This commit switches ngtsc to emit each .ts file individually, avoiding the
unwanted type checking.
PR Close#29147
When processing a JavaScript program, TS may come across a symbol that has
been imported from a TypeScript typings file.
In this case the compiler may pass the ReflectionHost a `prototype` symbol
as an export of the class.
This pseudo-member symbol has no declarations, which previously caused the
code in `Esm5ReflectionHost.reflectMembers()` to crash.
Now we just quietly ignore such a symbol and leave `Esm2015ReflectionHost`
to deal with it.
(As it happens `Esm2015ReflectionHost` also quietly ignores this symbol).
PR Close#29158
Prior to this commit, i18n instructions (i18n, i18nStart) were generated before listener instructions. As a result, event listeners were attached to the wrong element (text node, not the parent element). This change updates the order of instructions and puts i18n ones after listeners, to make sure listeners are attached to the right elements.
PR Close#29173
For the template type checking to work correctly, it needs to know
what attributes are bound to expressions or directives, which may
require expressions in the template to be evaluated in a different
scope.
In inline templates, there are attributes that are now marked as
"Template" attributes. We need to ensure that the template
type checking code looks at these "bound" attributes as well as the
"input" attributes.
PR Close#29041
The content projection mechanism is static, in that it only looks at the static
template nodes before directives are matched and change detection is run.
When you have a selector-based content projection the selection is based
on nodes that are available in the template.
For example:
```
<ng-content selector="[some-attr]"></ng-content>
```
would match
```
<div some-attr="..."></div>
```
If you have an inline-template in your projected nodes. For example:
```
<div *ngIf="..." some-attr="..."></div>
```
This gets pre-parsed and converted to a canonical form.
For example:
```
<ng-template [ngIf]="...">
<div some-attr=".."></div>
</ng-template>
```
Note that only structural attributes (e.g. `*ngIf`) stay with the `<ng-template>`
node. The other attributes move to the contained element inside the template.
When this happens in ivy, the ng-template content is removed
from the component template function and is compiled into its own
template function. But this means that the information about the
attributes that were on the content are lost and the projection
selection mechanism is unable to match the original
`<div *ngIf="..." some-attr>`.
This commit adds support for this in ivy. Attributes are separated into three
groups (Bindings, Templates and "other"). For inline-templates the Bindings
and "other" types are hoisted back from the contained node to the `template()`
instruction, so that they can be used in content projection matching.
PR Close#29041
- remove individuals from @angular/* package.json, we don't keep them up-to-date
- switch keys in contributors.json to GitHub handles, seems like a better identifier and lets us grab avatar images from GitHub account
- move emeritus ppl to a new Alumni group (won't yet appear on the site)
- add "lead/mentor" keys so we know who is coordinating work
- add a script that generates an "org chart" graphic
PR Close#28930
Prior to this change, the RegExp that was used to check for dashes in field names used "g" (global) flag that retains lastIndex, which might result in skipping some fields that should be wrapped in quotes (since lastIndex advanced beyond the next "-" location). This commit removes this flag and updates the test to make sure there are no regressions.
PR Close#29126
ngtsc occasionally converts a type reference (such as the type of a
parameter in a constructor) to a value reference (argument to a
directiveInject call). TypeScript has a bad habit of sometimes removing
the import statement associated with this type reference, because it's a
type only import when it initially looks at the file.
A solution to this is to always add an import to refer to a type position
value that's imported, and not rely on the existing import.
PR Close#29111
When ngtsc generates a .ngfactory shim, it does so based on the contents of
an original file in the program. Occasionally these original files have
comments at the top which are load-bearing (e.g. they contain jsdoc
annotations which are significant to downstream bundling tools). The
generated factory shims should preserve this comment.
This commit adds a step to the ngfactory generator to preserve the top-level
comment from the original source file.
FW-1006 #resolve
FW-1095 #resolve
PR Close#29065
Prior to this change, keys in "inputs" and "outputs" objects generated by compiler were not checked against unsafe characters. As a result, in some cases the generated code was throwing JS error. Now we check whether a given key contains any unsafe chars and wrap it in quotes if needed.
PR Close#28919
The ngtsc partial evaluator previously would not handle an enum reference
inside a template string expression correctly. Enums are resolved to an
`EnumValue` type, which has a `resolved` property with the actual value.
When effectively toString-ing a `ResolvedValue` as part of visiting a
template expression, the partial evaluator needs to translate `EnumValue`s
to their fully resolved value, which this commit does.
PR Close#29062
Currently, ngtsc has a bug where if you alias the name of a decorator when
importing it, it won't be detected properly. This is because the compiler
uses the aliased name and not the original, declared name of the decorator
for detection.
This commit fixes the compiler to compare against the declared name of
decorators when available, and adds a test to prevent regression.
PR Close#29061
ngtsc has cyclic import detection, to determine when adding an import to a
directive or pipe would create a cycle. However, this detection must also
account for already inserted imports, as it's possible for both directions
of a circular import to be inserted by Ivy (as opposed to at least one of
those edges existing in the user's program).
This commit fixes the circular import detection for components to take into
consideration already added edges. This is difficult for one critical
reason: only edges to files which will *actually* be imported should be
considered. However, that depends on which directives & pipes are used in
a given template, which is currently only known by running the
TemplateDefinitionBuilder during the 'compile' phase. This is too late; the
decision whether to use remote scoping (which consults the import graph) is
made during the 'resolve' phase, before any compilation has taken place.
Thus, the only way to correctly consider synthetic edges is for the compiler
to know exactly which directives & pipes are used in a template during
'resolve'. There are two ways to achieve this:
1) refactor `TemplateDefinitionBuilder` to do its work in two phases, with
directive matching occurring as a separate step which can be performed
earlier.
2) use the `R3TargetBinder` in the 'resolve' phase to independently bind the
template and get information about used directives.
Option 1 is ideal, but option 2 is currently used for practical reasons. The
cost of binding the template can be shared with template-typechecking.
PR Close#29040
In the @Component decorator, the 'host' field is an object which represents
host bindings. The type of this field is complex, but is generally of the
form {[key: string]: string}. Several different kinds of bindings can be
specified, depending on the structure of the key.
For example:
```
@Component({
host: {'[prop]': 'someExpr'}
})
```
will bind an expression 'someExpr' to the property 'prop'. This is known to
be a property binding because of the square brackets in the binding key.
If the binding key is a plain string (no brackets or parentheses), then it
is known as an attribute binding. In this case, the right-hand side is not
interpreted as an expression, but is instead a constant string.
There is no actual requirement that at build time, these constant strings
are known to the compiler, but this was previously enforced as a side effect
of requiring the binding expressions for property and event bindings to be
statically known (as they need to be parsed). This commit breaks that
relationship and allows the attribute bindings to be dynamic. In the case
that they are dynamic, the references to the dynamic values are reflected
into the Ivy instructions for attribute bindings.
PR Close#29033
DynamicValues are generated whenever a partially evaluated expression is
unable to be resolved statically. They contain a reference to the ts.Node
which wasn't resolvable.
They can also be nested. For example, the expression 'a + b' is resolvable
only if 'a' and 'b' are themselves resolvable. If either 'a' or 'b' resolve
to a DynamicValue, the whole expression must also resolve to a DynamicValue.
Previously, if 'a' resolved to a DynamicValue, the entire expression might
have been resolved to the same DynamicValue. This correctly indicated that
the expression wasn't resolvable, but didn't return a reference to the
shallow node that couldn't be resolved (the expression 'a + b'), only a
reference to the deep node that couldn't be resolved ('a').
In certain situations, it's very useful to know the shallow unresolvable
node (for example, to use it verbatim in the output). To support this,
the partial evaluator is updated to always wrap DynamicValue to point to
each unresolvable expression as it's processed, ensuring the receiver can
determine exactly which expression node failed to resolve.
PR Close#29033
This change helps highlight certain misoptimizations with Closure
compiler. It is also stylistically preferable to consistently use index
access on index sig types.
Roughly, when one sees '.foo' they know it is always checked for typos
in the prop name by the type system (unless 'any'), while "['foo']" is
always not.
Once all angular repos are conforming this will become a tsetse.info
check, enforced by bazel.
PR Close#28937
Prior to this change, TypeScript stripped out some imports in case we reference a type that can be represented as a value (for ex. classes). This fix ensures that we use correct symbol identifier, which makes TypeScript retain the necessary import statements.
PR Close#28941
Angular supports using <style> and <link> tags inline in component
templates, but previously such tags were not implemented within the ngtsc
compiler. This commit introduces that support.
FW-1069 #resolve
PR Close#28997
Prior to this change i18n block bindings were converted to Expressions right away (once we first access them), when in non-i18n cases we processed them differently: the actual conversion happens at instructions generation. Because of this discrepancy, the output for bindings in i18n blocks was generated incorrectly (with invalid indicies in pipeBindN fns and invalid references to non-existent local variables). Now the bindings processing is unified and i18nExp instructions should contain right bind expressions.
PR Close#28969
This commit introduces support for the windows paths in the new concrete types mechanism that was introduced in this PR https://github.com/angular/angular/pull/28523
Normalized posix paths that start with either a `/` or `C:/` are considered to be an absolute path.
Note: `C:/` is used as a reference, as other drive letters are also supported.
Fixes#28754
PR Close#28752
Prior to this change, the logic that outputs i18n consts (like `const MSG_XXX = goog.getMsg(...)`) didn't have a check whether a given const that represent a certain i18n message was already included into the generated output. This commit adds the logic to mark corresponding i18n contexts after translation was generated, to avoid duplicate consts in the output.
PR Close#28967
The partial evaluator in ngtsc can handle a shorthand property declaration
in the middle evaluation, but fails if evaluation starts at the shorthand
property itself. This is because evaluation starts at the ts.Identifier
of the property (the ts.Expression representing it), not the ts.Declaration
for the property.
The fix for this is to detect in TypeScriptReflectionHost when a ts.Symbol
refers to a shorthand property, and to use the ts.TypeChecker method
getShorthandAssignmentValueSymbol() to resolve the value of the assignment
instead.
FW-1089 #resolve
PR Close#28936
In certain configurations (such as the g3 repository) which have lots of
small compilation units as well as strict dependency checking on generated
code, ngtsc's default strategy of directly importing directives/pipes into
components will not work. To handle these cases, an additional mode is
introduced, and is enabled when using the FileToModuleHost provided by such
compilation environments.
In this mode, when ngtsc encounters an NgModule which re-exports another
from a different file, it will re-export all the directives it contains at
the ES2015 level. The exports will have a predictable name based on the
FileToModuleHost. For example, if the host says that a directive Foo is
from the 'root/external/foo' module, ngtsc will add:
```
export {Foo as ɵng$root$external$foo$$Foo} from 'root/external/foo';
```
Consumers of the re-exported directive will then import it via this path
instead of directly from root/external/foo, preserving strict dependency
semantics.
PR Close#28852
This commit splits apart selector_scope.ts in ngtsc and extracts the logic
into two separate classes, the LocalModuleScopeRegistry and the
DtsModuleScopeResolver. The logic is cleaned up significantly and new tests
are added to verify behavior.
LocalModuleScopeRegistry implements the NgModule semantics for compilation
scopes, and handles NgModules declared in the current compilation unit.
DtsModuleScopeResolver implements simpler logic for export scopes and
handles NgModules declared in .d.ts files.
This is done in preparation for the addition of re-export logic to solve
StrictDeps issues.
PR Close#28852
Prior to this change presence of HTML comments inside <ng-content> caused compiler to throw an error that <ng-content> is not empty. Now HTML comments are not considered as a meaningful content, thus no error is thrown. This behavior is now aligned in Ivy/VE.
PR Close#28849
Prior to this change absolute file paths (like `/a/b/c/style.css`) were calculated taking current component file location into account. As a result, absolute file paths were calculated using current file as a root. This change updates this logic to ignore current file path in case of absolute paths.
PR Close#28789
Prior to this change, Ivy and VE CSS resource resolution was different: in addition to specified styleUrl (with .scss, .less and .styl extensions), VE also makes an attempt to resolve resource with .css extension. This change introduces similar logic for Ivy to make sure Ivy behavior is backwards compatible.
PR Close#28770
Prior to this change, the @fileoverview annotations added by users in source files or by tsickle during compilation might have change a location due to the fact that Ngtsc may prepend extra imports or constants. As a result, the output file is considered invalid by Closure (misplaced @fileoverview annotation). In order to resolve the problem we relocate @fileoverview annotation if we detect that its host node shifted.
PR Close#28723
This change is kind of similar to #27466, but instead of ensuring that
these shims can be generated, we also need to make sure that developers
are able to also use the factory shims like with `ngc`.
This issue is now surfacing because we have various old examples which
are now also built with `ngtsc` (due to the bazel migration). On case insensitive
platforms (e.g. windows) these examples cannot be built because ngtsc fails
the app imports a generated shim file (such as the factory shim files).
This is because the `GeneratedShimsHostWrapper` TypeScript host uses
the `getCanonicalFileName` method in order to check whether a given
file/module exists in the generator file maps. e.g.
```
// Generator Map:
'C:/users/paul/_bazel_paul/lm3s4mgv/execroot/angular/packages/core/index.ngfactory.ts' =>
'C:/users/paul/_bazel_paul/lm3s4mgv/execroot/angular/packages/core/index.ts',
// Path passed into `fileExists`
C:/users/paul/_bazel_paul/lm3s4mgv/execroot/angular/packages/core/index.ngfactory.ts
// After getCanonicalFileName (notice the **lower-case drive name**)
c:/users/paul/_bazel_paul/lm3s4mgv/execroot/angular/packages/core/index.ngfactory.ts
```
As seen above, the generator map does not use the canonical file names, as well as
TypeScript internally does not pass around canonical file names. We can fix this by removing
the manual call to `getCanonicalFileName` and just following TypeScript internal-semantics.
PR Close#28831
Fixes a minor typo in the `listLazyRoutes` method for `ngtsc`. Also in
addition fixes that a newly introduced test for `listLazyRoutes` broke the
tests in Windows. It's clear that we still don't run tests against
Windows, but we also made all other tests pass (without CI verification),
and it's not a big deal fixing this while being at it.
PR Close#28831
Prior to this fix, using the compiler's ivy_switch mechanism was
only available to core packages. This patch allows for this variable
switching mechanism to work across all other angular packages.
PR Close#28711
This commit adds support for the `static: true` flag in `ContentChild`
queries. Prior to this commit, all `ContentChild` queries were resolved
after change detection ran. This is a problem for backwards
compatibility because View Engine also supported "static" queries which
would resolve before change detection.
Now if users add a `static: true` option, the query will be resolved in
creation mode (before change detection runs). For example:
```ts
@ContentChild(TemplateRef, {static: true}) template !: TemplateRef;
```
This feature will come in handy for components that need
to create components dynamically.
PR Close#28811
This commit adds support for the `static: true` flag in
`ViewChild` queries. Prior to this commit, all `ViewChild`
queries were resolved after change detection ran. This is
a problem for backwards compatibility because View Engine
also supported "static" queries which would resolve before
change detection.
Now if users add a `static: true` option, the query will be
resolved in creation mode (before change detection runs).
For example:
```ts
@ViewChild(TemplateRef, {static: true}) template !: TemplateRef;
```
This feature will come in handy for components that need
to create components dynamically.
PR Close#28811
Currently if developers use call expressions in their static
class members ([like we do in Angular](https://github.com/angular/angular/blob/master/packages/core/src/change_detection/differs/keyvalue_differs.ts#L121)),
the metadata that is generated for flat modules is invalid. This
is because the metadata bundler logic currently does not handle
call expressions in static class members and the symbol references
are not rewritten to avoid relative paths in the bundle.
Static class members using a call expression are not relevant for
the ViewEngine AOT compilation, but it is problematic that the
bundled metadata references modules using their original relative
path. This means that the bundled metadata is no longer encapsulated
and depends on other emitted files to be emitted in the proper place.
These incorrect relative paths can now cause issues where NGC
looks for the referenced symbols in the incorrect path. e.g.
```
src/
| lib/
| index.ts -> References the call expression using `../../di`
```
Now the metadata looks like that:
```
node_modules/
| @angular/
-- | core/
-- -- | core.metadata.json -> Says that the call expr. is in `../../di`.
| di/
```
Now if NGC tries to use the metadata files and create the summary files,
NGC resolves the call expression to the `node_modules/di` module. Since
the "unexpected" module does not contain the desired symbol, NGC will
error out.
We should fix this by ensuring that we don't ship corrupted metadata
to NPM which contains relative references that can cause such
failures (other imports can be affected as well; it depends on what
modules the developer has installed and how we import our call
expressions).
Fixes#28741.
PR Close#28762
Currently setting `enableIvy` to true runs a hybrid mode of `ngc` and `ngtsc`. This is counterintuitive given the name of the flag itself.
This PR makes the `true` value equivalent to the previous `ngtsc`, and `ngtsc` becomes an alias for `true`. Effectively this removes the hybrid mode as well since there's no other way to enable it.
PR Close#28616
Previously, `ngtsc` detected class inheritance in a way that only worked
in TS or ES2015 code. As a result, inheritance would not be detected for
code in ES5 format, such as when running `ngtsc` through `ngcc` to
transform old-style Angular code to ivy format.
This commit fixes it by delegating class inheritance detection to the
current `ReflectionHost`, which is able to correctly interpret the used
code format.
PR Close#28773
Accounts for schemas in when validating properties in Ivy.
This PR resolves FW-819.
A couple of notes:
* I had to rework the test slightly, in order to have it fail when we expect it to. The one in master is passing since Ivy's validation runs during the update phase, rather than creation.
* I had to deviate from the design in FW-819 and not add an `enableSchema` instruction, because the schema is part of the `NgModule` scope, however the scope is only assigned to a component once all of the module's declarations have been resolved and some of them can be async. Instead, I opted to have the `schemas` on the component definition.
PR Close#28637
Since we build and publish the individual packages
using Bazel and `build.sh` has been removed, we can
safely remove the `rollup.config.js` files which are no
longer needed because the `ng_package` bazel rule
automatically handles the rollup settings and globals.
PR Close#28646
The ultimate goal of this commit is to make use of fileNameToModuleName to
get the module specifier to use when generating an import, when that API is
available in the CompilerHost that ngtsc is created with.
As part of getting there, the way in which ngtsc tracks references and
generates import module specifiers is refactored considerably. References
are tracked with the Reference class, and previously ngtsc had several
different kinds of Reference. An AbsoluteReference represented a declaration
which needed to be imported via an absolute module specifier tracked in the
AbsoluteReference, and a RelativeReference represented a declaration from
the local program, imported via relative path or referred to directly by
identifier if possible. Thus, how to refer to a particular declaration was
encoded into the Reference type _at the time of creation of the Reference_.
This commit refactors that logic and reduces Reference to a single class
with no subclasses. A Reference represents a node being referenced, plus
context about how the node was located. This context includes a
"bestGuessOwningModule", the compiler's best guess at which absolute
module specifier has defined this reference. For example, if the compiler
arrives at the declaration of CommonModule via an import to @angular/common,
then any references obtained from CommonModule (e.g. NgIf) will also be
considered to be owned by @angular/common.
A ReferenceEmitter class and accompanying ReferenceEmitStrategy interface
are introduced. To produce an Expression referring to a given Reference'd
node, the ReferenceEmitter consults a sequence of ReferenceEmitStrategy
implementations.
Several different strategies are defined:
- LocalIdentifierStrategy: use local ts.Identifiers if available.
- AbsoluteModuleStrategy: if the Reference has a bestGuessOwningModule,
import the node via an absolute import from that module specifier.
- LogicalProjectStrategy: if the Reference is in the logical project
(is under the project rootDirs), import the node via a relative import.
- FileToModuleStrategy: use a FileToModuleHost to generate the module
specifier by which to import the node.
Depending on the availability of fileNameToModuleName in the CompilerHost,
then, a different collection of these strategies is used for compilation.
PR Close#28523
This commit introduces a new ngtsc sub-library, 'path', which contains
branded string types for the different kind of paths that ngtsc manipulates.
Having static types for these paths will reduce the number of path-related
bugs (especially on Windows) and will eliminate unnecessary defensive
normalizing.
See the README.md file for more detail.
PR Close#28523
Previously, ngtsc would throw an error if two decorators were matched on
the same class simultaneously. However, @Injectable is a special case, and
it appears frequently on component, directive, and pipe classes. For pipes
in particular, it's a common pattern to treat the pipe class also as an
injectable service.
ngtsc actually lacked the capability to compile multiple matching
decorators on a class, so this commit adds support for that. Decorator
handlers (and thus the decorators they match) are classified into three
categories: PRIMARY, SHARED, and WEAK.
PRIMARY handlers compile decorators that cannot coexist with other primary
decorators. The handlers for Component, Directive, Pipe, and NgModule are
marked as PRIMARY. A class may only have one decorator from this group.
SHARED handlers compile decorators that can coexist with others. Injectable
is the only decorator in this category, meaning it's valid to put an
@Injectable decorator on a previously decorated class.
WEAK handlers behave like SHARED, but are dropped if any non-WEAK handler
matches a class. The handler which compiles ngBaseDef is WEAK, since
ngBaseDef is only needed if a class doesn't otherwise have a decorator.
Tests are added to validate that @Injectable can coexist with the other
decorators and that an error is generated when mixing the primaries.
PR Close#28523
In the past, @Injectable had no side effects and existing Angular code is
therefore littered with @Injectable usage on classes which are not intended
to be injected.
A common example is:
@Injectable()
class Foo {
constructor(private notInjectable: string) {}
}
and somewhere else:
providers: [{provide: Foo, useFactory: ...})
Here, there is no need for Foo to be injectable - indeed, it's impossible
for the DI system to create an instance of it, as it has a non-injectable
constructor. The provider configures a factory for the DI system to be
able to create instances of Foo.
Adding @Injectable in Ivy signifies that the class's own constructor, and
not a provider, determines how the class will be created.
This commit adds logic to compile classes which are marked with @Injectable
but are otherwise not injectable, and create an ngInjectableDef field with
a factory function that throws an error. This way, existing code in the wild
continues to compile, but if someone attempts to use the injectable it will
fail with a useful error message.
In the case where strictInjectionParameters is set to true, a compile-time
error is thrown instead of the runtime error, as ngtsc has enough
information to determine when injection couldn't possibly be valid.
PR Close#28523
Translation of WriteKeyExpr expressions was not implemented in the ngtsc
expression translator. This resulted in binding expressions like
"target[key] = $event" not compiling.
This commit fixes the bug by implementing WriteKeyExpr translation.
PR Close#28523
Some applications use enum values in their host bindings:
@Component({
host: {
'[prop]': EnumType.Key,
}, ...
})
This commit changes the resolution of host properties to follow the enum
declaration and extract the correct value for the binding.
PR Close#28523
Testing of Ivy revealed two bugs in the AstMemoryEfficientTransformer
class, a part of existing View Engine compiler infrastructure that's
reused in Ivy. These bugs cause AST expressions not to be transformed
under certain circumstances.
The fix is simple, and tests are added to ensure the specific expression
forms that trigger the issue compile properly under Ivy.
PR Close#28523
Prior to this update we had separate contentQueries and contentQueriesRefresh functions to handle creation and update phases. This approach was inconsistent with View Queries, Host Bindings and Template functions that we generate for Component/Directive defs. Now the mentioned 2 functions are combines into one (contentQueries), creation and update logic is separated with RenderFlags (similar to what we have in other generated functions).
PR Close#28503
With #28594 we refactored the `@angular/compiler` slightly to
allow opting out from external symbol re-exports which are
enabled by default.
Since symbol re-exports only benefit projects which have a
very strict dependency enforcement, external symbols should
not be re-exported by default as this could grow the size of
factory files and cause unexpected behavior with Angular's
AOT symbol resolving (e.g. see: #25644).
Note that the common strict dependency enforcement for source
files does still work with external symbol re-exports disabled,
but there are also strict dependency checks that enforce strict
module dependencies also for _generated files_ (such as the
ngfactory files). This is how Google3 manages it's dependencies
and therefore external symbol re-exports need to be enabled within
Google3.
Also "ngtsc" also does not provide any way of using external symbol
re-exports, so this means that with this change, NGC can partially
match the behavior of "ngtsc" then (unless explicitly opted-out).
As mentioned before, internally at Google symbol re-exports need to
be still enabled, so the `ng_module` Bazel rule will enable the symbol
re-exports by default when running within Blaze.
Fixes#25644.
PR Close#28633
Previously, using a pipe in an input binding on an ng-template would
evaluate the pipe in the context of node that was processed before the
template. This caused the retrieval of e.g. ChangeDetectorRef to be
incorrect, resulting in one of the following bugs depending on the
template's structure:
1. If the template was at the root of a view, the previously processed
node would be the component's host node outside of the current view.
Accessing that node in the context of the current view results in a crash.
2. For templates not at the root, the ChangeDetectorRef injected into the
pipe would correspond with the previously processed node. If that node
hosts a component, the ChangeDetectorRef would not correspond with the
view that the ng-template is part of.
The solution to the above problem is two-fold:
1. Template compilation is adjusted such that the template instruction
is emitted before any instructions produced by input bindings, such as
pipes. This ensures that pipes are evaluated in the context of the
template's container node.
2. A ChangeDetectorRef can be requested for container nodes.
Fixes#28587
PR Close#27565
During analysis, the `ComponentDecoratorHandler` passes the component
template to the `parseTemplate()` function. Previously, there was little or
no information about the original source file, where the template is found,
passed when calling this function.
Now, we correctly compute the URL of the source of the template, both
for external `templateUrl` and in-line `template` cases. Further in the
in-line template case we compute the character range of the template
in its containing source file; *but only in the case that the template is
a simple string literal*. If the template is actually a dynamic value like
an interpolated string or a function call, then we do not try to add the
originating source file information.
The translator that converts Ivy AST nodes to TypeScript now adds these
template specific source mappings, which account for the file where
the template was found, to the templates to support stepping through the
template creation and update code when debugging an Angular application.
Note that some versions of TypeScript have a bug which means they cannot
support external template source-maps. We check for this via the
`canSourceMapExternalTemplates()` helper function and avoid trying to
add template mappings to external templates if not supported.
PR Close#28055
When tokenizing markup (e.g. HTML) element attributes
can have quoted or unquoted values (e.g. `a=b` or `a="b"`).
The `ATTR_VALUE` tokens were capturing the quotes, which
was inconsistent and also affected source-mapping.
Now the tokenizer captures additional `ATTR_QUOTE` tokens,
which the HTML related parsers understand and factor into their
token parsing.
PR Close#28055
This commit consolidates the options that can modify the
parsing of text (e.g. HTML, Angular templates, CSS, i18n)
into an AST for further processing into a single `options`
hash.
This makes the code cleaner and more readable, but also
enables us to support further options to parsing without
triggering wide ranging changes to code that should not
be affected by these new options. Specifically, it will let
us pass information about the placement of a template
that is being parsed in its containing file, which is essential
for accurate SourceMap processing.
PR Close#28055
I don't know of any use of this API with a project-root-relative path
(i.e. the cli will always call it with an absolute path), but keeping
the API backwards compatible just in case.
PR Close#28542
This will make it easier to retrieve routes for specific entry points in
`listLazyRoutes()` (which is necessary for CLI integration but not yet
implemented).
PR Close#28542
Up until now, `[style]` and `[class]` bindings (the map-based ones) have only
worked as template bindings and have not been supported at all inside of host
bindings. This patch ensures that multiple host binding sources (components and
directives) all properly assign style values and merge them correctly in terms
of priority.
Jira: FW-882
PR Close#28246
Previously, it wasn't possible to compile template that contains pipe in context of ternary operator `{{ 1 ? 2 : 0 | myPipe }}` due to the error `Error: Illegal state: Pipes should have been converted into functions. Pipe: async`.
This PR fixes a typo in expression parser so that pipes are correctly converted into functions.
PR Close#28635
Prior to this change in Ivy we had strict check that disabled non-unique #localRefs usage within a given template. While this limitation was technically present in View Engine, in many cases View Engine neglected this restriction and as a result, some apps relied on a fact that multiple non-unique #localRefs can be defined and utilized to query elements via @ViewChild(ren) and @ContentChild(ren). In order to provide better compatibility with View Engine, this commit removes existing restriction.
As a part of this commit, are few tests were added to verify VE and Ivy compatibility in most common use-cases where multiple non-unique #localRefs were used.
PR Close#28627
In https://github.com/angular/angular/pull/27697 the listLazyRoutes was fixed to work with ivy.
Since the entryRoute argument is not supported, it was made to also error.
But by erroring it breaks existing usage with Angular CLI where the entry route is sent in as an argument.
This commit changes listLazyRoutes to not error out, but instead ignore the argument.
PR Close#28372
Prior to this change there was no i18n id sanitization before we output goog.getMsg calls. Due to the fact that message ids are used as a part of const names, some characters were bcausing issues while executing generated code. This commit adds sanitization to i18n ids used to generate i18n-related consts.
PR Close#28522
Note that this fixes `compiler-cli` tests within `compiler-cli/test`,
but there seem to be remaining `ngcc` tests within `compiler-cli/src`
which aren't working on Windows. This is out-of-scope for this commit.
PR Close#28352
Currently the "ngtsc` testing helpers resolve the `fake_core` NPM
package using the `TEST_SRCDIR` variable. This is problematic on Windows
where Bazel runfiles are not symlinked into the runfiles directory.
In order to properly resolve the NPM Bazel tree artifact, we use the
`resolveTreeNpmArtifact` runfile helper that properly resolves the artifact
properly on all platforms.
PR Close#28352
In order to support running "compiler-cli" tests that use the "test_support.ts"
utilities on Windows with Bazel, we need to imporve the logic that resolves NPM
packages and symlinks them into a temporary directory.
A more Bazel idiomatic and windows compatible way of resolving Bazel runfiles
is to use the "RUNFILES_MANIFEST" if present. This ensures that the NPM
packages can be also symlinked on Windows, and tests can execute properly
on Windows. Read more about why this is needed here:
* https://github.com/bazelbuild/bazel/issues/3726#issue-257062364
PR Close#28352
Since we recently removed the `test.sh` script, and now run
all tests with Bazel, we can remove the unused logic that makes
compiler-cli tests pass in non-Bazel.
This cleans up the tests, and also makes it easier to write tests
without worrying about two ways of the Angular package output
(Bazel `ng_package` rules vs. old `build.sh` logic of building)
PR Close#28352
createInjector() is an Ivy-only API that should not have
been exported as part of the public API. This commit removes
the export. It will be re-exported when Ivy is released.
PR Close#28509
This lets us run ngtsc under the tsc_wrapped custom compiler (Used in Bazel)
It also allows others to simply wire ngtsc into an existing typescript compilation binary
PR Close#28435
In View Engine, we supported @Input and @ContentChild annotations
on the same property. This feature was somewhat brittle because
it would only work for static queries, so it would break if a
content child was passed in wrapped in an *ngIf. Due to the
inconsistent behavior and low usage both internally and externally,
we will likely be deprecating it in the next version, and it does
not make sense to perpetuate it in Ivy.
This commit ensures that we now throw in Ivy if we encounter the
two annotations on the same property.
PR Close#28415
Prior to this change we may encounter some errors (like pipes being used where they should not be used) while compiling Host Bindings and Listeners. With this update we move validation logic to the analyze phase and throw an error if something is wrong. This also aligns error messages between Ivy and VE.
PR Close#28356
The TypeTranslatorVisitor visitor returned strings because before it wasn't possible to transform declaration files directly through the TypeScript custom transformer API.
Now that's possible though, so it should return nodes instead.
PR Close#28342
The current DtsFileTransformer works by intercepting file writes and editing the source string directly.
This PR refactors it as a afterDeclaration transform in order to fit better in the TypeScript API.
This is part of a greater effort of converting ngtsc to be usable as a TS transform plugin.
PR Close#28342
This lets us run ngtsc under the tsc_wrapped custom compiler (Used in Bazel)
It also allows others to simply wire ngtsc into an existing typescript compilation binary
PR Close#28431
This lets us run ngtsc under the tsc_wrapped custom compiler (Used in Bazel)
It also allows others to simply wire ngtsc into an existing typescript compilation binary
PR Close#27806
* Improves the `compiler-cli/integrationtest` codegen output test slightly by using a more clear test description and by adding an assertion that ensures that decorators are downleveled.
PR Close#28191
* Fixes that the test logic for `ngtools` in the offline compiler test is no longer working due to being unmaintained for a long time
* Makes the path comparison logic platform agnostic, so that the tests can be also executed on Windows
PR Close#28191
Prior to this change contentQueriesRefresh functions that represent refresh logic for @ContentQuery list were not composable, which caused problems in case one Directive inherits another one and both of them contain Content Queries. Due to the fact that we used indices to reference queries in refresh function, results were placed into wrong Queries. In order to avoid that we no longer use indices to reference queries and instead maintain current content query index while iterating through them. This allows us to compose contentQueriesRefresh functions and make inheritance feature work with Content Queries.
PR Close#28324
Currently `compileNgModule` generates an empty array for optional fields that are omitted from an `NgModule` declaration (e.g. `bootstrap`, `exports`). This isn't necessary, because `defineNgModule` has some code to default these fields to empty arrays at runtime if they aren't defined. The following changes will only output code if there are values for the particular field.
PR Close#28387
In some cases, calling getSourceFile() on a node from within a TS
transform can return undefined (against the signature of the method).
In these cases, getting the original node first will work.
PR Close#28412
By its nature, Ivy alters the import graph of a TS program, adding imports
where template dependencies exist. For example, if ComponentA uses PipeB
in its template, Ivy will insert an import of PipeB into the file in which
ComponentA is declared.
Any insertion of an import into a program has the potential to introduce a
cycle into the import graph. If for some reason the file in which PipeB is
declared imports the file in which ComponentA is declared (maybe it makes
use of a service or utility function that happens to be in the same file as
ComponentA) then this could create an import cycle. This turns out to
happen quite regularly in larger Angular codebases.
TypeScript and the Ivy runtime have no issues with such cycles. However,
other tools are not so accepting. In particular the Closure Compiler is
very anti-cycle.
To mitigate this problem, it's necessary to detect when the insertion of
an import would create a cycle. ngtsc can then use a different strategy,
known as "remote scoping", instead of directly writing a reference from
one component to another. Under remote scoping, a function
'setComponentScope' is called after the declaration of the component's
module, which does not require the addition of new imports.
FW-647 #resolve
PR Close#28169
This commit implements a cycle detector which looks at the import graph of
TypeScript programs and can determine whether the addition of an edge is
sufficient to create a cycle. As part of the implementation, module name
to source file resolution is implemented via a ModuleResolver, using TS
APIs.
PR Close#28169
ngcc's reflection host needs to be able to determine all members of a
class, which it does by using the `ts.Symbol` from TypeScript's
TypeChecker. Such Symbol however may represent multiple class members
in the case of accessors; an equally named getter/setter accessor pair
is combined into a single `ts.Symbol`.
This commit introduces logic to recognize such accessors in order for
both the getter and setter to be considered as class member, similar to
ngtsc's behavior when operating on original TypeScript code.
One difference wrt the TypeScript host is that ngcc cannot see to which
accessor originally had any decorators applied to them, as decorators
are applied to the property descriptor in general, not a specific accessor.
If an accessor has both a setter and getter, any decorators are only
attached to the setter member.
PR Close#28357
Prior to this change, accessor functions for getters and setters would
not be considered as class member, as their declaration is vastly
different from ES2015 syntax.
With this change, the ES5 reflection host has learned to recognize the
downleveled syntax for accessors, allowing for them to be considered as
class member once again.
Fixes#28226
PR Close#28357
Prior to this change the postprocess step relied on the order of placeholders combined in one group (e.g. [�#1�|�*1:1�]). The order is not guaranteed in case we have nested templates (since we use BFS to process templates) and some tags are represented using same placeholders. This change performs postprocessing more accurate by keeping track of currently active template and searching for matching placeholder.
PR Close#28209
Prior to this change `viewQuery` functions that represent @ViewQuery list were not composable, which caused problems in case one Component/Directive inherits another one and both of them contain View Queries. Due to the fact that we used indices to reference queries, resulting query set was corrupted (child component queries were overridden by super class ones). In order to avoid that we no longer use indices assigned at compile time and instead maintain current view query index while iterating through them. This allows us to compose `viewQuery` functions and make inheritance feature work with View Queries.
PR Close#28309
- Wraps the NgOnChangesFeature in a factory such that no side effects occur in the module root
- Adds comments to ngInherit property on feature definition interface to help guide others not to make the same mistake
- Updates compiler to generate the feature properly after the change to it being a factory
- Updates appropriate tests
PR Close#28187
Fixes the template generation function generating an incorrect tag name when the element has a namespace (e.g. `:svg:circle` gets generated rather than `circle`).
PR Close#28298
This commit uses the NgModuleRouteAnalyzer introduced previously to
implement listLazyRoutes() for NgtscProgram. Currently this implementation
is limited to listing routes globally and cannot list routes for a given lazy
module. Testing seems to indicate that the CLI uses the global form, but this
should be verified.
Jira issue: FW-629
PR Close#27697
This commit introduces a new mode for the NgtscTestEnvironment which
builds the NgtscProgram and then asks for the list of lazy routes,
instead of running the TS emit phase.
PR Close#27697
This commit introduces the NgModuleRouteAnalyzer & friends, which given
metadata about the NgModules in a program can extract the list of lazy
routes in the same format that the ngtools API uses.
PR Close#27697
This commit changes the partial evaluation mechanism to propagate
DynamicValue errors internally during evaluation, and not to "poison"
entire data structures when a single value is dynamic. For example,
previously if any entry in an array was dynamic, evaluating the entire
array would return DynamicValue. Now, the array is returned with only
the specific dynamic entry as DynamicValue.
Instances of DynamicValue also report the node that was determined to
be dynamic, as well as a potential reason for the dynamic-ness. These
can be nested, so an expression `a + b` may have a DynamicValue that
indicates the 'a' term was DynamicValue, which will itself contain a
reason for the dynamic-ness.
This work was undertaken for the implementation of listLazyRoutes(),
which needs to partially evaluate provider arrays, parts of which are
dynamic and parts of which contain useful information.
PR Close#27697
`ngtsc` currently fails building a flat module out file on Windows because it generates an invalid flat module TypeScript source file. e.g:
```ts
5 export * from './C:\Users\Paul\Desktop\test\src\export';
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```
This is because `path.posix.relative` does not properly with non-posix paths, and only expects posix paths in order to work.
PR Close#27993
Resources can be loaded in the context of another file, which
means that the path to the resource file must be resolved
before it can be loaded.
Previously the API of this interface did not allow the client
code to get access to the resolved URL which is used to load
the resource.
Now this API has been refactored so that you must do the
resource URL resolving first and the loading expects a
resolved URL.
PR Close#28199
Users might have run the CSS Preprocessor tool *before* the Angular
compiler. For example, we do it that way under Bazel. This means that
the design-time reference is different from the compile-time one - the
input to the Angular compiler is a plain .css file.
We assume that the preprocessor does a trivial 1:1 mapping using the same
basename with a different extension.
PR Close#28166
Due to the fact that animations in Angular are defined in the component metadata,
all animation trigger definitions are localized to the component and are
inaccessible outside of it. Animation host listeners in Ivy are
rendered in the context of the parent component, but the VE renders them
differently. This patch ensures that animation host listeners are
always registered in the sub component's renderer
Jira issue: FW-943
Jira issue: FW-958
PR Close#28210
In Ivy when elements are created a series of static attribute names are provided
over to the construction instruction of that element. Static attribute names
include non-binding attribues (like `<div selected>`) as well as animation bindings
that do not have a RHS value (like `<div @foo>`). Because of this distinction,
value-less animation triggers are rendered first before value-full animation
bindings are and this improper ordering has caused various existing tests to fail.
This patch ensures that animation bindings are evaluated in the order that they
exist within the HTML template code (or host binding code).
PR Close#28165
With the refactoring or how styles/classes are implmented in Ivy,
interpolation has caused the binding code to mess up since interpolation
itself takes up its own slot in Ivy's memory management code. This patch
makes sure that interpolation works as expected with class and style
bindings.
Jira issue: FW-944
PR Close#28190
Prior to this change element's i18n attributes like "i18n-title" were processed after "i18n" ones that placed "i18n" and "i18nAttributes" instructions in wrong order, thus "i18nAttributes" failed to target its host element at runtime. This change updates processing order and puts "i18nAttributes" instructions in front of "i18n" ones to resolve the problem.
PR Close#28163
At the moment, paths stored in `maps` are not normalized and in Windows is causing files not to be found when enabling factory shimming.
For example, the map contents will be
```
Map {
'C:\\git\\cli-repos\\ng-factory-shims\\index.ngfactory.ts' => 'C:\\git\\cli-repos\\ng-factory-shims\\index.ts' }
```
However, ts compiler normalized the paths and is causing;
```
error TS6053: File 'C:/git/cli-repos/ng-factory-shims/index.ngfactory.ts' not found.
error TS6053: File 'C:/git/cli-repos/ng-factory-shims/index.ngsummary.ts' not found.
```
The changes normalized the paths that are stored within the factory and summary maps.
PR Close#28006
This code was throwing if the `deps` array of a provider has several elements, but at the next line it resolves them... With this check `ngtsc` couldn’t compile `ng-bootstrap` for example.
PR Close#28076
Up until this point, all static attribute values (things like `title` and `id`)
defined within the `host` are of a Component/Directive definition were
generated into a `def.attributes` array and then processed at runtime.
This design decision does not lend itself well to tree-shaking and is
inconsistent with other static values such as styles and classes.
This fix ensures that all static attribute values (attributes, classes,
and styles) that exist within a host definition for components and
directives are all assigned via the `elementHostAttrs` instruction.
```
// before
defineDirective({
...
attributes: ['title', 'my title']
...
})
//now
defineDirective({
...
hostBindings: function() {
if (create) {
elementHostAttrs(..., ['title', 'my-title']);
}
...
}
...
})
```
PR Close#28089
This update aligns Ivy behavior with ViewEngine related to empty bindings (for example <div [someProp]></div>): empty bindings are ignored.
PR Close#28059
ngtsc has a hack to add @nocollapse jsdoc annotations to generated static
fields. This hack is currently broken (likely due to a TypeScript change
in the way writeFile() works).
This commit fixes the hack and introduces an ngtsc_spec test to ensure it
does not regress again.
PR Close#28050
In ESM5 decorated classes can be indicated by calls to `__decorate()`.
Previously the `ReflectionHost.findDecoratedClasses()` call would identify
helper calls of the form:
```
SomeClass = tslib_1.__decorate(...);
```
But it was missing calls of the form:
```
SomeClass = SomeClass_1 = tslib_1.__decorate(...);
```
This form is common in `@NgModule()` decorations, where the class
being decorated is referenced inside the decorator or another
member.
This commit now ensures that a chain of assignments, of any length,
is now identified as a class decoration if it results in a call to
`__decorate()`.
Fixes#27841
PR Close#27848
Libraries that create components dynamically using component factories,
such as `@angular/upgrade` need to pass blocks of projected content
through to the `ComponentFactory.create()` method. These blocks
are extracted from the content by matching CSS selectors defined in
`<ng-content select="..">` tags found in the component's template.
The Angular compiler collects these CSS selectors when compiling a component's
template, and exposes them via the `ComponentFactory.ngContentSelectors`
property.
This change ensures that this property is filled correctly when the
component factory is created by compiling a component with the Ivy engine.
PR Close#27867
Prior to this change Component decorator was resolving `encapsulation` value a bit incorrectly, which resulted in `encapsulation: NaN` in compiled code. Now we resolve the value as Enum memeber and throw if it's not the case. As a part of this update, the `changeDetection` field handling is also added, the resolution logic is the same as the one used for `encapsulation` field.
PR Close#27971
exportAs in @Directive metadata supports multiple values, separated by
commas. Previously it was treated as a single value string.
This commit modifies the compiler to understand that exportAs is a
string[]. It stops short of carrying the multiple values through to the
runtime. Instead, it only emits the first one. A future commit will modify
the runtime to accept all the values.
PR Close#28001
Generated factory shims can import from @angular/core. However, we have
special logic in place to rewrite self-imports when generating code for
@angular/core.
This commit leverages the new standalone ImportRewriter interface to
properly rewrite imports in generated factory shims. Before this fix,
a generated factory file for core would look like:
```typescript
import * as i0 from './r3_symbols';
export var ApplicationModuleNgFactory = new ɵNgModuleFactory(...);
```
This is invalid, as ɵNgModuleFactory is just NgModuleFactory when imported
via r3_symbols.
FW-881 #resolve
PR Close#27998
Currently the ImportManager class handles various rewriting actions of
imports when compiling @angular/core. This is required as code compiled
within @angular/core cannot import from '@angular/core'. To work around
this, imports are rewritten to get core symbols from a particular file,
r3_symbols.ts.
In this refactoring, this rewriting logic is moved out of the ImportManager
and put behind an interface, ImportRewriter. There are three implementers
of the interface:
* NoopImportRewriter, used for compiling all non-core packages.
* R3SymbolsImportRewriter, used when ngtsc compiles @angular/core.
* NgccFlatImportRewriter, used when ngcc compiles @angular/core (special
logic is needed because ngcc has to rewrite imports in flat bundles
differently than in non-flat bundles).
This is a precursor to using this rewriting logic in other contexts besides
the ImportManager.
PR Close#27998
A constructor function may have been "synthesized" by TypeScript during
JavaScript emit, in the case no user-defined constructor exists and e.g.
property initializers are used. Those initializers need to be emitted
into a constructor in JavaScript, so the TypeScript compiler generates a
synthetic constructor.
This commit adds identification of such constructors as ngcc needs to be
able to tell if a class did originally have a constructor in the
TypeScript source. When a class has a superclass, a synthesized
constructor must not be considered as a user-defined constructor as that
prevents a base factory call from being created by ngtsc, resulting in a
factory function that does not inject the dependencies of the superclass.
Hence, we identify a default synthesized super call in the constructor
body, according to the structure that TypeScript emits.
PR Close#27897
This commit adds sanitization for `elementProperty` and `elementAttribute` instructions used in `hostBindings` function, similar to what we already have in the `template` function. Main difference is the fact that for some attributes (like "href" and "src") we can't define which SecurityContext they belong to (URL vs RESOURCE_URL) in Compiler, since information in Directive selector may not be enough to calculate it. In order to resolve the problem, Compiler injects slightly different sanitization function which detects proper Security Context at runtime.
PR Close#27939
Previously, ngtsc would assume that a given directive/pipe being imported
from an external package was importable using the same name by which it
was declared. This isn't always true; sometimes a package will export a
directive under a different name. For example, Angular frequently prefixes
directive names with the 'ɵ' character to indicate that they're part of
the package's private API, and not for public consumption.
This commit introduces the TsReferenceResolver class which, given a
declaration to import and a module name to import it from, can determine
the exported name of the declared class within the module. This allows
ngtsc to pick the correct name by which to import the class instead of
making assumptions about how it was exported.
This resolver is used to select a correct symbol name when creating an
AbsoluteReference.
FW-517 #resolve
FW-536 #resolve
PR Close#27743
This commit adds tracking of modules, directives, and pipes which are made
visible to consumers through NgModules exported from the package entrypoint.
ngtsc will now produce a diagnostic if such classes are not themselves
exported via the entrypoint (as this is a requirement for downstream
consumers to use them with Ivy).
To accomplish this, a graph of references is created and populated via the
ReferencesRegistry. Symbols exported via the package entrypoint are compared
against the graph to determine if any publicly visible symbols are not
properly exported. Diagnostics are produced for each one which also show the
path by which they become visible.
This commit also introduces a diagnostic (instead of a hard compiler crash)
if an entrypoint file cannot be correctly determined.
PR Close#27743
Upcoming work to implement import resolution will change the dependencies
of some higher-level classes in ngtsc & ngcc. This necessitates changes in
how these classes are created and the lifecycle of the ts.Program in ngtsc
& ngcc.
To avoid complicating the implementation work with refactoring as a result
of the new dependencies, the refactoring is performed in this commit as a
separate prepatory step.
In ngtsc, the testing harness is modified to allow easier access to some
aspects of the ts.Program.
In ngcc, the main change is that the DecorationAnalyzer is created with the
ts.Program as a constructor parameter. This is not a lifecycle change, as
it was previously created with the ts.TypeChecker which is derived from the
ts.Program anyways. This change requires some reorganization in ngcc to
accommodate, especially in testing harnesses where DecorationAnalyzer is
created manually in a number of specs.
PR Close#27743
This refactoring moves code around between a few of the ngtsc subpackages,
with the goal of having a more logical package structure. Additional
interfaces are also introduced where they make sense.
The 'metadata' package formerly contained both the partial evaluator,
the TypeScriptReflectionHost as well as some other reflection functions,
and the Reference interface and various implementations. This package
was split into 3 parts.
The partial evaluator now has its own package 'partial_evaluator', and
exists behind an interface PartialEvaluator instead of a top-level
function. In the future this will be useful for reducing churn as the
partial evaluator becomes more complicated.
The TypeScriptReflectionHost and other miscellaneous functions have moved
into a new 'reflection' package. The former 'host' package which contained
the ReflectionHost interface and associated types was also merged into this
new 'reflection' package.
Finally, the Reference APIs were moved to the 'imports' package, which will
consolidate all import-related logic in ngtsc.
PR Close#27743
This commit moves the FlatIndexGenerator to its own package, in preparation
to expand its capabilities and support re-exporting of private declarations
from NgModules.
PR Close#27743
This update introduces support for global object (window, document, body) listeners, that can be defined via host listeners on Components and Directives.
PR Close#27772
test.sh is no longer needed... all the tests should now be executed via bazel.
if for whatever reason we need to run the legacy unit test setup, we should should follow the commands that we use to execute those tests in .circle/config.yaml
PR Close#27937
Moving the tests over to CircleCI in pretty much "as-is" state just so that we can drop the dependency on Travis.
In the followup changes we plan to migrate these tests to run on sauce under bazel. @gregmagolan is working on that.
I've previously verified that all the tests executed in legacy-unit-tests-local already under bazel.
Therefore the legacy-unit-tests-local job is strictly not necessary any more, but given how flaky legacy-unit-tests-saucelabs is,
it is good to have the -local job just so that we can quickly determine if any failure is a flake or legit issue
(the bazel version of these tests could theoretically run in a slightly different way and fail or not fail in a different way, so having -lcoal job is just an extra safety check).
This change was coauthored with @devversion
PR Close#27937
Previously, there could be identical template/listener function names
for a component's template, if it had multiple similarly structured
nested sub-templates or listeners.
This resulted in build errors:
`Identifier '<SOME_IDENTIFIER>' has already been declared`
This commit fixes this by ensuring that the template index is included
in the `contextName` passed to the `TemplateDefinitionBuilder`
responsible for processing nested sub-templates.
Similarly, the template or element index is included in the listener
names.
PR Close#27766
Some of the animation tests have been failing because animation gets
triggered multiple times. The reason for this is that the compiler was
generating static attribute bindings in addition to dynamic bindings.
This created multiple writes to the animation render which failed the
tests.
PR Close#27805
Previously ivy code generation was emmiting the projectionDef instruction in
a template where the <ng-content> tag was found. This code generation logic was
incorrect since the ivy runtime expects the projectionDef instruction to be present
in the main template only.
This PR ammends the code generation logic so that the projectionDef instruction is
emmitedin the main template only.
PR Close#27755
Normally functions that return `ModuleWithProvider` objects should parameterize
the return type to include the type of `NgModule` that is being returned. For
example `forRoot(): ModuleWithProviders<RouterModule>`.
But in some cases, especially those generated by nccc, these functions to not
explicitly declare `ModuleWithProviders` as their return type. Instead they
return a "intersection" type, one of whose members is a type literal that
declares the `NgModule` type returned. For example:
`forRoot(): CustomType&{ngModule:RouterModule}`.
This commit changes the `NgModuleDecoratorHandler` so that it can extract
the `NgModule` type from either kind of declaration.
PR Close#27326
Exported functions or static method that return a `ModuleWithProviders`
compatible structure need to provide information about the referenced
`NgModule` type in their return type.
This allows ngtsc to be able to understand the type of `NgModule` that is
being returned from calls to the function, without having to dig into the
internals of the compiled library.
There are two ways to provide this information:
* Add a type parameter to the `ModuleWithProviders` return type. E.g.
```
static forRoot(): ModuleWithProviders<SomeNgModule>;
```
* Convert the return type to a union that includes a literal type. E.g.
```
static forRoot(): (SomeOtherType)&{ngModule:SomeNgModule};
```
This commit updates the rendering of typings files to include this type
information on all matching functions/methods.
PR Close#27326
To support updating `ModuleWithProviders` calls,
we need to be able to map exported functions between
source and typings files, as well as classes.
PR Close#27326
Typescript 3.2 introduced BigInt type, and consequently the
implementation for checkExpressionWorker() in checkers.ts is refactored.
For NumberLiteral and StringLiteral types, 'text' filed must be present
in the Node type, therefore they must be LiteralLikeNode instead of
Node.
PR Close#27536
With the bundle info being assembled into a single object before the
transform is started, we now greedily create a TypeScript program up-front.
If a marker file exists that indicates that the bundle could be skipped
the program creation has already taken place which takes a significant
amount of time. This commit moves the marker check to occur before the
bundle is assembled.
PR Close#27438
ngcc would feed ngtsc with the function declaration inside of an IIFE as
that is considered the class symbol's declaration node, according to
TypeScript's `ts.Symbol.valueDeclaration`. ngtsc however only considered
variable decls and actual class decls as potential class declarations,
so given the function declaration node it would fail to generate the
`setClassMetadata` call.
ngtsc no longer makes its own assumptions about what classes look like,
but always asks the reflection host to yield this kind of information.
PR Close#27438
Prior to this change, we were unable to match directives using `ng-template` tags (for example the following selector would not work even though there might be some <ng-template>s in a template: `ng-template[directiveA]`. As a result, that broke some components that relies on such selectors to work. In order to resolve the problem, we now pass tag name to the `template` instruction (where we passed `null` before) and this tag name is used for matching at runtime. This update should also help support projecting containers, because the tag name is required to properly match such elements.
PR Close#27636
A surprising interaction with the MagicString library caused inserted
Ivy definitions to be dropped during the removal of decorators, iff all
decorators on the class could be removed. In that case, the removal
location corresponds with the exact location where Ivy definitions were
inserted into.
This commit moves the removal of decorators to occur before Ivy
definitions are inserted. This effectively avoids the problem, as later
inserted text fragments will be retained by MagicString.
PR Close#27159
If a template contains specific TypeScript syntax, such as a non-null
assertion, the code that is emitted from ngcc into a JavaScript bundle
should not retain such syntax as it is invalid in JS.
A full-blown TypeScript emit of a complete ts.SourceFile would be
required to be able to emit JS and possibly downlevel into a lower
language target, which is not an option for ngcc as it currently
operates on partial ASTs, not full source files.
Instead, ngtsc no longer produces TypeScript specific syntax in the first
place, such that TypeScript print logic will only generate JS code.
PR Close#27051
In Ivy, a pure call to `setClassMetadata` is inserted to retain the
information that would otherwise be lost while eliding the Angular
decorators. In the past, the Angular constructor decorators were
wrapped inside of an anonymous function which was only evaluated once
`ReflectionCapabilities` was requested for such metadata. This approach
prevents forward references from inside the constructor parameter
decorators from being evaluated before they are available.
In the `setClassMetadata` call, the constructor parameters were not wrapped
within an anonymous function, such that forward references were evaluated
too early, causing runtime errors.
This commit changes the `setClassMetadata` call to pass the constructor
parameter decorators inside of an anonymous function again, such that
forward references are not resolved until requested by
`ReflectionCapabilities`, therefore avoiding the early reads of forward refs.
PR Close#27561
With ngcc's ability to fixup pre-Ivy ModuleWithProviders such that they
include a reference to the NgModule type, the type may become a qualified
name:
```
import {ModuleWithProviders} from '@angular/core';
import * as ngcc0 from './module';
export declare provide(): ModuleWithProviders<ngcc0.Module>;
```
ngtsc now takes this situation into account when reflecting a
ModuleWithProvider's type argument.
PR Close#27562
Closure Compiler doesn't allow non-goo.getMsg const names to start with `MSG_`, so we should use different prefix for const that references a result of the `i18nPostprocess` fn invocation. With this update we also append file-based prefix to i18n constants (via $$ postfix) to ensure the names are unique across codebase of a project (otherwise it might lead to errors while compiling a project with Closure Compiler).
PR Close#27468
The pure functions in host bindings change was merged after the change in
host binding instructions, so it had a new test that wasn't yet updated
with the new generated code. This commit updates the new test.
PR Close#27605
Previously in Ivy, host bindings did not work if they shared a public name
with an Input because they used the `elementProperty` instruction as is.
This instruction was originally built for inside component templates, so it
would either set a directive input OR a native property. This is the
correct behavior for inside a template, but for host bindings, we always
want the native properties to be set regardless of the presence of an Input.
This change adds an extra argument to `elementProperty` so we can tell it to
ignore directive inputs and only set native properties (if it is in the
context of a host binding).
PR Close#27589
Prior to this update, we always returned the number of host vars defined in @Component definition as a value for `allocatePureFunctionsSlot` callback in ValueConverter. As a result, pure function arguments were not accounted for, thus leasing to incorrect slot offsets in `pureFunction` calls. Now we update and return total # of host vars, so the offsets are defined correctly.
PR Close#27587
Prior to this change, animation event names were treated as a regular event names, stripping `@` symbol and event phase. As a result, event listeners were not invoked during animations. Now animation event name is formatted as needed and the necessary callbacks are invoked.
PR Close#27525
Previously, ngtsc did not respect the angularCompilerOptions settings
for generating flat module indices. This commit adds a
FlatIndexGenerator which is used to implement those options.
FW-738 #resolve
PR Close#27497
Previously the ngtsc ShimGenerator interface expected that all shims would
be generated using the contents of existing ts.SourceFiles. This assumption
was true for ngfactory and ngsummary files, but breaks down for flat module
index files, which are standalone.
This commit prepares for flat module index generation by enabling shim
generators which don't require an existing file.
PR Close#27497
While generating attributes for `projection` instruction, we checked whether attribute name is equal to 'select' in lower case. However in other cases we treat 'select' attribute name as case-insensitive. This PR makes 'select' attribute consistently case-insensitive.
PR Close#27500
Prior to this change, animation properties were defined as element attributes, which caused errors at runtime. Now all animation-related attributes are defined as element properties.
Also as a part of this update, we start to account for bindings used in animations, which was previously missing.
PR Close#27496
Analogously to directives, the `ngInjectableDef` field in .d.ts files is
annotated with the type of service that it represents. If the service
contains required generic type arguments, these must be included in
the .d.ts file.
PR Close#27037
Common insensitive platforms are `win32/win64` (see:
[here](3e4c5c95ab/src/compiler/sys.ts (L681-L682)))
Currently when running `bazel build packages/core --define=compile=aot`, the `compiler-cli` will throw because it cannot find the `index.ngfactory.ts` file in the compiler host. This is because the shim host wrapper is not properly generating the requested `ngfactory` file.
This happens because we call `getCanonicalFileName` that returns a path that is different to the actual program filenames that are used to construct a map of generated files. Since the generators always use the paths which are not "canonical" and pases them internally like that, we can just stop manually calling `getCanonicalFileName`.
PR Close#27466
ngfactory files have a ɵNonEmptyModule constant included if there are no
other exported factory symbols. Previously this extra export was added
dynamically in a TS transformer.
However, synthetically constructed exports don't get properly downleveled
during JS emit, and this generated constant caused issues with downstream
tests.
Instead, this commit configures the shim to always have this export to
begin with, and to filter it out if it's not required.
Testing strategy: covered by existing ngtsc_spec tests which verify the
presence of the ɵNonEmptyModule symbol.
PR Close#27483
In ngtsc, files loaded into the ts.Program have a "module name", set via
ts.SourceFile.moduleName, which ends up being written into an AMD module
name triple-slash directive in the generated .js file.
For generated shim files (ngfactories, ngsummaries) that are constructed
synthetically, there was previously no moduleName set, which caused some
issues with downstream tests.
This commit adds logic to compute and set moduleNames for both generated
ngfactory and ngsummary shims.
PR Close#27483
A previous fix to ngtsc opened the door for duplicate directives in
the 'directives' array of a component. This would happen if the directive
was declared in a module which was imported more than once within the
component's module.
This commit adds deduplication when the component's scope is materialized,
so declarations which arrive via more than one module import are coalesced.
PR Close#27462
The method `ts.CompilerHost.directoryExists` is optional, and was not
previously handled by our ts.CompilerHost wrapper for factory and
summary shims (GeneratedShimsHostWrapper).
TypeScript checks for the existence of this method and silently ignores
things like typeRoots if it's not found. This commit adds proper handling
of directoryExists() to the shim.
A test is also added which verifies typeRoots behavior works when shims
are enabled.
PR Close#27470
Previously the ngfactory shim generator in ngtsc would always write two
imports in the factory file shims:
1) an import to @angular/core
2) an import to the base file
If the base file has no exports, import #2 would be empty. This turns out
to cause issues downstream.
This commit changes the generated shim so if there are no exports in the
base file, the generated shim is empty too.
PR Close#27470
PR #27404 introduced additional test case to make sure we generate `elementStyling` instructions with proper set of arguments (first argument was missing in some cases). It looks like that PR was created before we updated host vars count calculation and the `allocHostVars` becomes unnecessary in the test cases introduced in PR #27404. This commit actualizes this test to get rid of unnecessary `allocHostVars` instruction.
PR Close#27473
The logic that generates first argument for the `elementStyling` instruction was missing the check that directive expression is specified. As a result, in some cases first argument was not added, thus making function invocation incorrect. Now the presence of directive expression is taken into account and the `null` expression is generated as needed.
PR Close#27404
Prior to this change `projectDef` instructions were placed to root templates only, thus the necessary information (selectors) in nested templates was missing. This update adds the logic to insert `projectDef` instructions to all templates where <ng-content> is present.
PR Close#27384
Previously ngtsc assumed resource files (templateUrl, styleUrls) would be
physically present in the file system relative to the .ts file which
referenced them. However, ngc previously resolved such references in the
context of ts.CompilerOptions.rootDirs. Material depends on this
functionality in its build.
This commit introduces resolution of resources by leveraging the TypeScript
module resolver, ts.resolveModuleName(). This resolver is used in a way
which will never succeed, but on failure will return a list of locations
checked. This list is then filtered to obtain the correct potential
locations of the resource.
PR Close#27357
This commit adds support for resolution of styleUrls to ngtsc. Previously
this field was never read, and so components with styleUrls would appear
unstyled after compilation.
PR Close#27357
When a single resource is preloaded twice in ngtsc, the second request
would be recognized as in-flight in which case `undefined` would
be returned, which signals to the compilation that is can resume
synchronously. The compilation would then proceed immediately and call
`load`, only to find out that the request is still in-flight which is
not allowed.
This commit caches the Promise of the in-flight fetch requests, such
that subsequent preload requests can return the corresponding Promise
instance.
PR Close#27357
* Currently when building a `ng_module` with Bazel and having the flat module id option set, the flat module files are not being generated because `@angular/compiler-cli` does not properly determine the entry-point file.
Note that this logic is not necessarily specific to Bazel and the same problem can happen without Bazel if multiple TypeScript input files are specified while the `flatModuleIndex` option has been enabled.
PR Close#27200
For ngcc's processing of ES5 bundles, the spread syntax has been
downleveled from `[...ARRAY]` to become `ARRAY.slice()`. This commit
adds basic support for static resolution of such call.
PR Close#27158
The problem was caused by missing `allocateBindingSlots` that led to incorrect # of vars defined for components and as a result, causing errors at runtime. Now all `bind` operation are accounted for and the number of `vars` is correct.
PR Close#27338
The problem was caused by the self-closing i18n instruction that was generated in case we have styling instructions defined for a component. As a result, that caused problems at runtime. This update adds extra check to avoid creating self-closing i18n instructions (create i18nStart and i18nEnd instructions instead) when styling instructions are present.
PR Close#27330
Ngcc will now render additional exports for classes that are referenced in
`NgModule` decorated classes, but which were not publicly exported
from an entry-point of the package.
This is important because when ngtsc compiles libraries processed by ngcc
it needs to be able to publcly access decorated classes that are referenced
by `NgModule` decorated classes in order to build templates that use these
classes.
Doing this re-exporting is not without its risks. There are chances that
the class is not exported correctly: there may already be similarly named
exports from the entry-point or the class may be being aliased. But there
is not much more we can do from the point of view of ngcc to workaround
such scenarios. Generally, packages should have been built so that this
approach works.
PR Close#26906
There are a number of variables that need to be passed around
the program, in particular to the renderers, which benefit from being
stored in well defined objects.
The new `EntryPointBundle` structure is a specific format of an entry-point
and contains the compiled `BundleProgram` objects for the source and typings,
if appropriate.
This change helps with future refactoring, where we may need to add new
properties to this object. It allows us to maintain more stable APIs between
the constituent parts of ngcc, rather than passing lots of primitive values
around throughout the program.
PR Close#26906
The `NgModuleDecoratorHandler` can now register all the references that
it finds in the `NgModule` metadata, such as `declarations`, `imports`,
`exports` etc.
This information can then be used by ngcc to work out if any of these
references are internal only and need to be manually exported from a
library's entry-point.
PR Close#26906
By inverting the relationship between `EntryPointPaths` and
`EntryPointFormat` we can have interfaces rather than types.
Thanks to @gkalpak for this idea.
PR Close#26906
If a decorated class is not publicly exported via an entry-point then the
previous approach to finding the associated typings file failed.
Now we ensure that we extract all the class declarations from the
dtsTypings program, even if they are not exported from the entry-point.
This is achieved by also parsing statements of each source file, rather
than just parsing classes that are exported from the entry-point.
Because we now look at all the files, it is possible for there to be multiple
class declarations with the same local name. In this case, only the first
declaration with a given name is added to the map; subsequent classes are
ignored.
We are most interested in classes that are publicly exported from the
entry-point, so these are added to the map first, to ensure that they are
not ignored.
PR Close#26906
Previously the concept of multiple directives with the same selector was
not supported by ngtsc. This is due to the treatment of directives for a
component as a Map from selector to the directive, which is an erroneous
representation.
Now the directives for a component are stored as an array which supports
multiple directives with the same selector.
Testing strategy: a new ngtsc_spec test asserts that multiple directives
with the same selector are matched on an element.
PR Close#27298
These tests are not relevant to Ivy:
//packages/compiler-cli/test/diagnostics:check_types
//packages/compiler-cli/test/diagnostics:expression_diagnostics
//packages/compiler-cli/test/transformers:test
//packages/compiler-cli/test:extract_i18n
The //packages/compiler-cli/test:ngtools_api test has 2 specs, one of
which passes and the other of which depends on ngtsc supporting lazy
routes. It's now disabled with fixmeIvy().
PR Close#27301
These paths are no longer needed / used.
I had to disable one jit mode spec because it fails now that we actually run it.
I root caused the jit test failure as missing forwardRef support. See FW-645.
PR Close#27278
When ngtsc compiles @angular/core, it rewrites core imports to the
r3_symbols.ts file that exposes all internal symbols under their
external name. When creating the FESM bundle, the r3_symbols.ts file
causes the external symbol names to be rewritten to their internal name.
Under ngcc compilations of FESM bundles, the indirection of
r3_symbols.ts is no longer in place such that the external names are
retained in the bundle. Previously, the external name `ɵdefineNgModule`
was explicitly declared internally to resolve this issue, but the
recently added `setClassMetadata` was not declared as such, causing
runtime errors.
Instead of relying on the r3_symbols.ts file to perform the rewrite of
the external modules to their internal variants, the translation is
moved into the `ImportManager` during the compilation itself. This
avoids the need for providing the external name manually.
PR Close#27055
Now that the Ivy switch transform uses ts.getMutableClone() to copy
statements, there's no need to set .parent pointers on the resulting
updated nodes. Doing this was causing assertion failures deep in
TypeScript in some cases.
PR Close#27170
Make a copy of the ts.SourceFile before modifying it in the ivy_switch
transform. It's suspected that the Bazel tsc_wrapped host's SourceFile
cache has issues when the ts.SourceFiles are mutated.
PR Close#27032
In a more specific scenario: Considering people use a custom TypeScript compiler host with `NGC`, they _could_ expect only posix paths in methods like `writeFile`. This at first glance sounds like a trivial issue that should be just fixed by the actual compiler host, but usually TypeScript internal API's just pass around posix normalized paths, and therefore it would be good to follow the same standards when passing JSON genfiles to the `CompilerHost`.
For normal TypeScript files (and TS genfiles), this is already consistent because those will be handled by the actual TypeScript `Program` (see `emitCallback`).
PR Close#27062
This fixes an issue where packages would be skipped if they contained
e.g. RxJS 5 style imports such as
```
import { observeOn } from 'rxjs/operators/observeOn';
```
Given that no package.json file can be found at the imported path, the
dependency would be reported missing, causing the package to be skipped.
PR Close#27031
Previously the ivy definition calls we going directly after the
class constructor function But this meant that the lifecycle
hooks attached to the prototype were ignored by the ngtsc
compiler.
Now the definitions are written to the end of the IIFE block,
just before the return statement.
Closes#26849
PR Close#26856
For each package entry-point there is only one format that
is used to compile the typings files (.d.ts). This will be
either esm2015 or fesm2015 (preferred). So we would not run
any dts processing in the renderer if we are not compiling
the appropriate format.
PR Close#26403
1) The `DecorationAnalyzer now analyzes all source files, rather than just
the entry-point files, which fixes#26183.
2) The `DecoratorAnalyzer` now runs all the `handler.analyze()` calls
across the whole entry-point *before* running `handler.compile()`. This
ensures that dependencies between the decorated classes *within* an
entry-point are known to the handlers when running the compile process.
3) The `Renderer` now does the transformation of the typings (.d.ts) files
which allows us to support packages that only have flat format
entry-points better, and is faster, since we won't parse `.d.ts` files twice.
PR Close#26403
The rendering of typings is not specific to the package
format, so it doesn't make sense to put it in a specific
renderer.
As a result there is no real difference between esm5 and esm2015
renderers, so there is no point in having separate classes.
PR Close#26403
Previously we always used the non-flat format because we thought
that this was the one that would always be available.
It turns out that this is not the case and that only one of the flat and
non-flat formats may be available.
Therefore we should use whichever is available, defaulting to the flat
format if that exists, since that will be faster to parse.
PR Close#26403
Going forward we need to be able to do the same work on both
flat and non-flat module formats (such as computing arity and
transforming .d.ts files)
PR Close#26403
The Material project uses slightly different properties to the
core Angular project for specifying the different format entry-point.
This commit ensures that we map these properties correctly for both
types of project.
PR Close#26403
The `NgModule` handler generates `R3References` for its declarations, imports,
exports, and bootstrap components, based on the relative import path
between the module and the classes it's referring to. This works fine for
compilation of a .ts Program inside ngtsc, but in ngcc the import needed
in the .d.ts file may be very different to the import needed between .js
files (for example, if the .js files are flattened and the .d.ts is not).
This commit introduces a new API in the `ReflectionHost` for extracting the
.d.ts version of a declaration, and makes use of it in the
`NgModuleDecorationHandler` to write a correct expression for the `NgModule`
definition type.
PR Close#26403
This commit causes a call to setClassMetadata() to be emitted for every
type being compiled by ngtsc (every Angular type). With this metadata,
the TestBed should be able to recompile these classes when overriding
decorator information.
Testing strategy: Tests in the previous commit for
generateSetClassMetadataCall() verify that the metadata as generated is
correct. This commit enables the generation for each DecoratorHandler,
and a test is added to ngtsc_spec to verify all decorated types have
metadata generated for them.
PR Close#26860
This commit introduces generateSetClassMetadataCall(), an API in ngtsc
for generating calls to setClassMetadata() for a given declaration. The
reflection API is used to enumerate Angular decorators on the declaration,
which are converted to a format that ReflectionCapabilities can understand.
The reflection metadata is then patched onto the declared type via a call
to setClassMetadata().
This is simply a utility, a future commit invokes this utility for
each DecoratorHandler.
Testing strategy: tests are included which exercise generateSetClassMetadata
in isolation.
PR Close#26860
This commit introduces the setClassMetadata() private function, which
adds metadata to a type in a way that can be accessed via Angular's
ReflectionCapabilities. Currently, it writes to static fields as if
the metadata being added was downleveled from decorators by tsickle.
The plan is for ngtsc to emit code which calls this function, passing
metadata on to the runtime for testing purposes. Calls to this function
would then be tree-shaken away for production bundles.
Testing strategy: proper operation of this function will be an integral
part of TestBed metadata overriding. Angular core tests will fail if this
is broken.
PR Close#26860
Previously, the Directive, Injectable, and Pipe DecoratorHandlers were
directly returning @angular/compiler metadata from their analyze() steps.
This precludes returning any additional information along with that
metadata. This commit introduces a wrapper interface for these handlers,
opening the door for additional information to be returned from analyze().
Testing strategy: this is a refactor commit, existing test coverage is
sufficient.
PR Close#26860
Previously the ReflectionHost API only returned the names of decorators
and not a reference to their TypeScript Identifier. This commit adds
the identifier itself, so that a consumer can write references to the
decorator.
Testing strategy: this commit is trivial, and the functionality will be
exercised by downstream tests.
PR Close#26860
Uglify and other tree-shakers attempt to determine if the invocation
of a function is side-effectful, and remove it if so (and the result
is unused). A /*@__PURE__*/ annotation on the call site can be used
to hint to the optimizer that the invocation has no side effects and
is safe to tree-shake away.
This commit adds a 'pure' flag to the output AST function call node,
which can be used to signal to downstream emitters that a pure
annotation should be added. It also modifies ngtsc's emitter to
emit an Uglify pure annotation when this flag is set.
Testing strategy: this will be tested via its consumers, by asserting
that pure functions are translated with the correct comment.
PR Close#26860
* No longer depends on a custom CircleCI docker image that comes with Bazel pre-installed. Since Bazel is now available through NPM, we should be able to use the version from `@bazel/bazel` in order to enforce a consistent environment on CI and locally.
* This also reduces the amount of packages that need to be published (ngcontainer is removed)
PR Close#26691
This makes yarn_install of ngdeps under Bazel faster, since we don't need many of the large dependencies.
It's important because downstream angular/bazel users will observe the same install time.
PR Close#26691
These tests were previously not running on CI so they have always been broken,
or got broken just recently :-(.
test(ivy): mark failing test targets with fixme-ivy-jit and fixme-ivy-local tags
PR Close#26735
We are close enough to blacklist a few test targets, rather than whitelist targets to run...
Because bazel rules can be composed of other rules that don't inherit tags automatically,
I had to explicitly mark all of our ts_library and ng_module targes with "ivy-local" and
"ivy-jit" tags so that we can create a query that excludes all fixme- tagged targets even
if those targets are composed of other targets that don't inherit this tag.
This is the updated overview of ivy related bazel tags:
- ivy-only: target that builds or runs only under ivy
- fixme-ivy-jit: target that doesn't yet build or run under ivy with --compile=jit
- fixme-ivy-local: target that doesn't yet build or run under ivy with --compile=local
- no-ivy-jit: target that is not intended to build or run under ivy with --compile=jit
- no-ivy-local: target that is not intended to build or run under ivy with --compile=local
PR Close#26471
This commit adds generation of .ngsummary.js shims alongside .ngfactory.js
shims when generated files are enabled.
Generated .ngsummary shims contain a single, null export for every exported
class with decorators that exists in the original source files. Ivy code
does not depend on summaries, so these exist only as a placeholder to allow
them to be imported and their values passed to old APIs. This preserves
backwards compatibility.
Testing strategy: this commit adds a compiler test to verify the correct
shape and contents of the generated .ngsummary.js files.
PR Close#26495
This commit refactors the shim host to be agnostic to the shims being
generated, and provides an API for generating additional shims besides
the .ngfactory.js. This will be used in a following commit to generate
.ngsummary.js shims.
Testing strategy: this refactor introduces no new behavior, so it's
sufficient that the existing tests for factory shim generation continue
to pass.
PR Close#26495
This simple refactor of the build rules renames the .ngfactory.js shim
generator to 'shims' instead of 'factories', in preparation for adding
.ngsummary.js shim generation.
Testing strategy: this commit does not introduce any new behavior and
merely moves files and symbols around. It's sufficient that the existing
ngtsc tests pass.
PR Close#26495
Originally, the ivy_switch mechanism used Bazel genrules to conditionally
compile one TS file or another depending on whether ngc or ngtsc was the
selected compiler. This was done because we wanted to avoid importing
certain modules (and thus pulling them into the build) if Ivy was on or
off. This mechanism had a major drawback: ivy_switch became a bottleneck
in the import graph, as it both imports from many places in the codebase
and is imported by many modules in the codebase. This frequently resulted
in cyclic imports which caused issues both with TS and Closure compilation.
It turns out ngcc needs both code paths in the bundle to perform the switch
during its operation anyway, so import switching was later abandoned. This
means that there's no real reason why the ivy_switch mechanism needed to
operate at the Bazel level, and for the ivy_switch file to be a bottleneck.
This commit removes the Bazel-level ivy_switch mechanism, and introduces
an additional TypeScript transform in ngtsc (and the pass-through tsc
compiler used for testing JIT) to perform the same operation that ngcc
does, and flip the switch during ngtsc compilation. This allows the
ivy_switch file to be removed, and the individual switches to be located
directly next to their consumers in the codebase, greatly mitigating the
circular import issues and making the mechanism much easier to use.
As part of this commit, the tag for marking switched variables was changed
from __PRE_NGCC__ to __PRE_R3__, since it's no longer just ngcc which
flips these tags. Most variables were renamed from R3_* to SWITCH_* as well,
since they're referenced mostly in render2 code.
Test strategy: existing test coverage is more than sufficient - if this
didn't work correctly it would break the hello world and todo apps.
PR Close#26550
The 'animations' field of @Component metadata should be copied directly
into the ngComponentDef for that component and should not pass through
static resolution.
Previously the animations array was statically resolved and then the
values were translated back when generating ngComponentDef.
PR Close#26322
Previously we only removed assignments to `Class.decorators = [];`
if the array was not empty.
Now we also remove calls to `__decorate([])`, similarly.
PR Close#26236
Previously, classes that were declared via variable declarations,
rather than class declarations, were being excluded from the
parsed classes.
PR Close#26236
The most recent Angular distributions have begun to use __decorate instead of Class.decorators.
This prevents `ngcc` from recognizing the classes and then fails to perform the transform to
ivy format.
Example:
```
var ApplicationModule = /** @class */ (function () {
// Inject ApplicationRef to make it eager...
function ApplicationModule(appRef) {
}
ApplicationModule = __decorate([
NgModule({ providers: APPLICATION_MODULE_PROVIDERS }),
__metadata("design:paramtypes", [ApplicationRef])
], ApplicationModule);
return ApplicationModule;
}());
```
Now `ngcc` recognizes `__decorate([...])` declarations and performs its transform.
See FW-379
PR Close#26236
In some formats variables are declared as `var` or `let` and only
assigned a value later in the code.
The ngtsc resolver still needs to be able to resolve this value,
so the host now provides a `host.getVariableValue(declaration)`
method that can do this resolution based on the format.
The hosts make some assumptions about the layout of the
code, so they may only work in the constrained scenarios that
ngcc expects.
PR Close#26236
This commit builds on the NgtscTestEnvironment helper work before and
introduces template_typecheck_spec.ts, which contains compiler tests
for template type-checking.
PR Close#26203
This commit gets ready for the introduction of ngtsc template
type-checking tests by refactoring test environment setup into a
custom helper. This helper will simplify the authoring of future
ngtsc tests.
Ngtsc tests previously returned a numeric error code (a la ngtsc's CLI
interface) if any TypeScript errors occurred. The helper has the
ability to run ngtsc and return the actual array of ts.Diagnostics, which
greatly increases the ability to write clean tests.
PR Close#26203
This commit enables generation and checking of a type checking ts.Program
whenever the fullTemplateTypeCheck flag is enabled in tsconfig.json. It
puts together all the pieces built previously and causes diagnostics to be
emitted whenever type errors are discovered in a template.
Todos:
* map errors back to template HTML
* expand set of type errors covered in generated type-check blocks
PR Close#26203
Before type checking can be turned on in ngtsc, appropriate metadata for
each component and directive must be determined. This commit adds tracking
of the extra metadata in *DefWithMeta types to the selector scope handling,
allowing for later extraction for type-checking purposes.
PR Close#26203
This commit introduces the template type-checking context API, which manages
inlining of type constructors and type-check blocks into ts.SourceFiles.
This API will be used by ngtsc to generate a type-checking ts.Program.
An TypeCheckProgramHost is provided which can wrap a normal ts.CompilerHost
and intercept getSourceFile() calls. This can be used to provide source
files with type check blocks to a ts.Program for type-checking.
PR Close#26203
This commit introduces the main functionality of the type-check compiler:
generation of type check blocks. Type check blocks are blocks of TypeScript
code which can be inlined into source files, and when processed by the
TypeChecker will give information about any typing errors in template
expressions.
PR Close#26203
Template type-checking will make use of expression and statement
translation as well as the ImportManager, so this code needs to
live in a separate build target which can be depended on by both
the main ngtsc transform as well as the template type-checking
mechanism. This refactor introduces a separate build target
for that code.
PR Close#26203
Previously in Ivy, metadata for directives/components/modules/etc was
carried in .d.ts files inside type information encoded on the
DirectiveDef, ComponentDef, NgModuleDef, etc types of Ivy definition
fields. This works well, but has the side effect of complicating Ivy's
runtime code as these extra generic type parameters had to be specified
as <any> throughout the codebase. *DefInternal types were introduced
previously to mitigate this issue, but that's the wrong way to solve
the problem.
This commit returns *Def types to their original form, with no metadata
attached. Instead, new *DefWithMeta types are introduced that alias the
plain definition types and add extra generic parameters. This way the
only code that needs to deal with the extra metadata parameters is the
compiler code that reads and writes them - the existence of this metadata
is transparent to the runtime, as it should be.
PR Close#26203
This commit introduces //packages/compiler-cli/src/ngtsc/typecheck as a
container for template type-checking code, and implements an initial API:
type constructor generation.
Type constructors are static methods on component/directive types with
no runtime implementation. The methods are used during compilation to
enable inference of a component or directive's generic type parameters
from the types of expressions bound to any of their @Inputs. A type
constructor looks like:
class Directive<T> {
someInput: T;
static ngTypeCtor<T>(init: Partial<Pick<Directive<T>, 'someInput'>>): Directive<T>;
}
It can be used to infer a type for T based on the input:
const _dir = Directive.ngTypeCtor({someInput: 'string'}); // Directive<T>
PR Close#26203
`TypeScript` only supports merging and extending of `compilerOptions`. This is an implementation to support extending and inheriting of `angularCompilerOptions` from multiple files.
Closes: #22684
PR Close#22717
Create getter methods `getXXXDef` for each definition which
uses `hasOwnProperty` to verify that we don't accidently read form the
parent class.
Fixes: #24011Fixes: #25026
PR Close#25736
Previously, if ngtsc encountered a VariableDeclaration without an
initializer, it would assume that the variable was undefined, and
return that result.
However, for symbols exported from external modules that resolve to
.d.ts files, variable declarations are of the form:
export declare let varName: Type;
This form also lacks an initializer, but indicates the presence of an
importable symbol which can be referenced. This commit changes the
static resolver to understand variable declarations with the 'declare'
keyword and to generate references when it encounters them.
PR Close#25775
The bootstrap property of @NgModule was not previously compiled by
the compiler in AOT or JIT modes (in Ivy). This commit adds support
for bootstrap.
PR Close#25775
Closure requires @nocollapse on Ivy definition static fields in order
to not convert them to standalone constants. However tsickle, the tool
which would ordinarily be responsible for adding @nocollapse, doesn't
properly annotate fields which are added synthetically via transforms.
So this commit adds @nocollapse by applying regular expressions against
code during the final write to disk.
PR Close#25775
`ngcc` adds marker files to each folder that has been
compiled, containing the version of the ngcc used.
When compiling, it will ignore folders that contain these
marker files, as long as the version matches.
PR Close#25557
Closure compiler requires that the i18n message constants of the form
const MSG_XYZ = goog.getMessage('...');
have names that are unique across an entire compilation, even if the
variables themselves are local to a given module. This means that in
practice these names must be unique in a codebase.
The best way to guarantee this requirement is met is to encode the
relative file name of the file into which the constant is being written
into the constant name itself. This commit implements that solution.
PR Close#25689
TypeScript has a more modern diagnostic emit function which produces
contextually annotated error information, using colors in the console
to indicate where in the code the error occurs.
This commit swiches ngtsc to use this format for diagnostics when
emitting them after a failed compilation.
PR Close#25647
This commit takes the first steps towards ngtsc producing real
TypeScript diagnostics instead of simply throwing errors when
encountering incorrect code.
A new class is introduced, FatalDiagnosticError, which can be thrown by
handlers whenever a condition in the code is encountered which by
necessity prevents the class from being compiled. This error type is
convertable to a ts.Diagnostic which represents the type and source of
the error.
Error codes are introduced for Angular errors, and are prefixed with -99
(so error code 1001 becomes -991001) to distinguish them from other TS
errors.
A function is provided which will read TS diagnostic output and convert
the TS errors to NG errors if they match this negative error code
format.
PR Close#25647
In tsc 3.0 the check that enables program structure reuse in tryReuseStructureFromOldProgram has changed
and now uses identity comparison on arrays within CompilerOptions. Since we recreate the options
on each incremental compilation, we now fail this check.
After this change the default set of options is reused in between incremental compilations, but we still
allow options to be overriden if needed.
PR Close#25275
This fixes a bug in ngtsc where each @Directive was compiled using a
separate ConstantPool. This resulted in two issues:
* Directive constants were not shared across the file
* Extra statements from directive compilation were dropped instead of
added to the file
This commit fixes both issues and adds a test to verify @Directive is
working properly.
PR Close#25620
This commit adds support for enumeration values. An enumeration value
is now a first-class return value of the resolver, which provides both
a Reference to the enum type itself and the name of the value from that
enum. Resolving an enum itself returns a Map<string, EnumValue>.
PR Close#25619
Ivy definitions in .d.ts files often reference the type of a class.
Sometimes, those classes have generic type parameters. When this is
the case, ngtsc needs to emit generic type parameters in the .d.ts
files (usually by passing 'any').
PR Close#25406
Since non-flat module formats (esm2015, esm5) have different structure
than their flat counterparts (and since we are operating on JS files
inside `node_modules/`, we need to configure TS to include deeply nested
JS files (by specifying a sufficiently high `maxNodeModuleJsDepth`).
Remains to be determined if this has any (noticeable) performance
implications.
PR Close#25406
In some code formats (e.g. ES5) methods can actually be function
expressions. For example:
```js
function MyClass() {}
// this static method is declared as a function expression
MyClass.staticMethod = function() { ... };
```
PR Close#25406
ngtsc's static resolver can evaluate function calls where parameters
have default values. In TypeScript code these default values live on the
function definition, but in ES5 code the default values are represented
by statements in the function body.
A new ReflectionHost method getDefinitionOfFunction() abstracts over
this difference, and allows the static reflector to more accurately
evaluate ES5 code.
PR Close#25406
A small bug caused base factory variable statements for @Component to
not be emitted properly. At the same time as this is fixed, those
statements are now emitted as const.
PR Close#25425
When @angular/core is compiled by ngtsc, a factory file is generated
for ApplicationModule, that is currently invalid because r3_symbols
does not export NgModuleFactory. This change fixes that issue and
ensures the generated ngfactory file for @angular/core is valid.
PR Close#25392
When generating the 'directives:' property of ngComponentDef, ngtsc
needs to be conscious of declaration order. If a directive being
written into the array is declarated after the component currently
being compiled, then the entire directives array needs to be wrapped
in a closure.
This commit fixes ngtsc to pay attention to such ordering issues
within directives arrays.
PR Close#25392
This commit creates an API for factory functions which allows them
to be inherited from one another. To do so, it differentiates between
the factory function as a wrapper for a constructor and the factory
function in ngInjectableDefs which is determined by a default
provider.
The new form is:
factory: (t?) => new (t || SomeType)(inject(Dep1), inject(Dep2))
The 't' parameter allows for constructor inheritance. A subclass with
no declared constructor inherits its constructor from the superclass.
With the 't' parameter, a subclass can call the superclass' factory
function and use it to create an instance of the subclass.
For @Injectables with configured providers, the factory function is
of the form:
factory: (t?) => t ? constructorInject(t) : provider();
where constructorInject(t) creates an instance of 't' using the
naturally declared constructor of the type, and where provider()
creates an instance of the base type using the special declared
provider on @Injectable.
PR Close#25392
Previously, ngtsc used a new ConstantPool for each decorator
compilation. This could result in collisions between constants in the
top-level scope.
Now, ngtsc uses a single ConstantPool for each source file being
compiled, and merges the constant statements into the file after the
import section.
PR Close#25392
The performCompilation() is always called with an undefined oldProgram option (even in watch mode).
This was regression introduced in: 957be960d2
Partial fix, discovered in: #21361
PR Close#21364
Inside of a nested template, an attempt to generate code for a banana-
in-a-box expression would cause a crash in the _AstToIrVisitor, as it
was not handling the case where a write would be generated to a local
variable.
This change supports such a mode of operation.
PR Close#25321
before:
```
Expected to find features 'import * as i0 from "@angular/core";
import { Directive, Input } from '@angular/core';
```
after:
```
Failed to find "template" after "...Component_Factory() { return new
MyComponent(); }," in:
'import * as i0 from "@angular/core";
import { Directive, Input } from '@angular/core';```
```
PR Close#25291
To match the View Engine behavior.
We should make this configurable so that the node injector is tree shaken when
directives do not need to be published.
PR Close#25291
Previously the compiler compliance tests ran and built test code with
real dependencies on @angular/core and @angular/common. This meant that
any changes to the compiler would result in long rebuild processes
for tests to rerun.
This change removes those dependencies and causes test code to be built
against the fake_core stub of @angular/core that the ngtsc tests use.
This change also removes the dependency on @angular/common entirely, as
locality means it's possible to reference *ngIf without needing to link
to an implementation.
PR Close#25248
Existing bootstrap code in the wild depends on the existence of
.ngfactory files, which Ivy does not need. This commit adds the
capability in ngtsc to generate .ngfactory files which bridge
existing bootstrap code with Ivy.
This is an initial step. Remaining work includes complying with
the compiler option to specify a generated file directory, as well
as presumably testing in g3.
PR Close#25176
Before this change bound properties would not be used when matching directives
at runtime.
That is `<ng-template [ngIf]=cond>...</ng-template>` would not trigger the
`ngIf` directive.
PR Close#25272
The optional property on `ts.CompilerHost` is called `realpath` (lower
case), not `realPath` (lower camel case).
It is not clear to me what the impact of this is, but the author's
intent was clearly to override `realpath`.
PR Close#25023