This is the compile-time implementation of the `hostDirectives` feature plus a little bit of runtime code to illustrate how the newly-generated code will plug into the runtime. It works by creating a call to the new `ɵɵHostDirectivesFeature` feature whenever a directive has a `hostDirectives` field. Afterwards `ɵɵHostDirectivesFeature` will patch a new function onto the directive definition that will be invoked during directive matching.
For example, if we take the following definition:
```ts
@Directive({
hostDirectives: [HostA, {directive: HostB, inputs: ['input: alias']}]
})
class MyDir {}
```
Will compile to:
```js
MyDir.ɵdir = ɵɵdefineComponent({
features: [ɵɵHostDirectivesFeature([HostA, {
directive: HostB,
inputs: {
input: "alias"
}
}])]
});
```
The template type checking is implemented during directive matching by adding the host directives applied on the host to the array of matched directives whenever the host is matched in a template.
Relates to #8785.
PR Close#46868
When parsing interpolations, the input string is _decoded_ from what was
in the orginal template. This means that we cannot soley rely on the input
string to compute source spans because it does not necessarily reflect
the exact content of the original template. Specifically, when there is
an HTML entity (i.e. ` `), this will show up in its decoded form
when processing the interpolation (' '). We need to compute offsets
using the original _encoded_ string.
Note that this problem only surfaces in the splitting of interpolations.
The spans to this point have already been tracked accurately. For
example, given the template ` <div></div>`, the source span for the
`div` is already correctly determined to be 6. Only when we encounter
interpolations with many parts do we run into situations where we need
to compute new spans for the individual parts of the interpolation.
PR Close#44811
For two-way-bindings that use the banana-in-a-box syntax, the compiler
synthesizes an event assignment expression from the primary expression.
It is valid for the primary expression to be terminated by the non-null
operator, however naive string substitution is used for the synthesized
expression, such that the `!` would immediately precede the `=` token,
resulting in the valid `!=` operator token. The expression would still
parse correctly but it doesn't implement the proper semantics, resulting
in incorrect runtime behavior.
Changing the expression substitution to force a space between the
primary expression and the assignment avoids this mistake, but it
uncovers a new issue. The grammar does not allow for the LHS of an
assignment to be the non-null operator, so the synthesized expression
would fail to parse. To alleviate this, the synthesized expression is
parsed with a special parser flag to allow for this syntax.
Fixes#36551
PR Close#37809
Refs http://b/214103351.
This happens if a user writes `<span i18n>Message</span>`. This is accepted as an internationalized message, but without a description. JSCompiler will throw an error in this situation because descriptions are generally required. Now, the Angular compiler will generate a suppression annotation so JSCompiler allows the syntax. This will ease an internal migration to JSCompiler-based i18n.
PR Close#44787
So-called "Quote expressions" were added in b6ec2387b3
to support foreign syntax to be used in Angular templates, requiring a custom
template transform to convert them somehow during compilation. Support for template
transforms was originally implemented in a43ed79ee7 but
has since been dropped. Since the compiler is not public API the quote expressions
should not have any usages anymore. Removing support for them can improve error
reporting for expressions that contain a `:`, e.g. binding to a URL without quotes:
```html
<a [href]="http://google.com">Click me</a>
```
Here, `http` would be parsed as foreign "http" quote expression with `//google.com` as
value, later reporting the error "Quotes are not supported for evaluation!" because
there was no template transform to convert that code.
Closes#40398
PR Close#44915
An `ng-template` with an inline template (i.e. has a structural
directive) would previously not get an `undefined` `tagName` because the
logic assumed the element would be `t.Element` or `t.Content` and read
the tag name from the `name` property. For a `t.Template`, this exists
instead on the `t.tagName`. The final result would be an `tagName` of `undefined`
for the parent `t.Template`, causing failures in the indexer downstream.
This `undefined` value is actually expected in the renderer code, even
though the type does not specify this possibility. This change updates
the type of `tagName` to be `string|null` and explicitly handles the
case where there is a structural directive on an `ng-template`. You can
see how the two are differentiated in the compliance code that was
modified in this commit.
PR Close#44788
The previous fix for correcting spans with comments in
59eef29a6c
had the unfortunate side effect of _breaking_ the spans with comments
when there was leading whitespace. This happened because the previous
fix was testing one without a comment, identifying that the offset shouldn't
have anything added to it, and then removing that offset adjustment
(`offsets[i] + (expressionText.length - sourceToLex.length)`).
Upon further investigation, this offset adjustment _was actually
necessary_ for when the input had comments, but this was only because
the `stripComments` function used `trim` to remove whitespace for these
cases. This is the real problem -- not only does it create a ton of confusion
but also it means that the behavior of the lexer and resulting spans is
different between inputs with comments and inputs without comments.
After reviewing how the `inputLength` of `_ParseAST` was used, it
appears that the correct behavior would be to _not_ trim the input. The
`inputLength` is used to advance the current index beyond points which
have been processed. This _should_ include any whitespace. Additionally,
`inputLength` doesn't appear to be needed at all. When there was no
comment in the input, it was always equal to the `input.length` anyways.
When there _is_ a comment, it should include that comment anyways to
advance the index beyond the comment.
PR Close#44785
When parsing a binding with a comment at the end of the expression, the
parser previously had logic to offset the parsed spans by the length of the
comment. This logic seemed not to serve any useful purpose, and instead
resulted in the corruption of the spans. For example, in the expression
`{{foo // comment}}`, the parser would map the parsed `foo` `PropertyRead`
node at the location of the characters 'ent' from the comment suffix.
This commit removes that logic, correcting the parsed spans of such nodes in
the presence of comments. Removing this logic does not seem to have caused
any tests to fail.
PR Close#44678
In `language-service`, the `checker.getDirectiveMetadata` doesn't return the animations meta of the `Component`.
but it's useful for animation completion.
PR Close#44630
To make our test output i.e. devmode output more aligned
with what we produce in the NPM packages, or to be more
aligned with what Angular applications will usually consume,
the devmode output is switched from ES5 to ES2015.
Additionally various tsconfigs (outside of Bazel) have been
updated to match with the other parts of the build. The rules
are:
ES2015 for test configurations, ES2020 for actual code that will
end up being shipped (this includes the IDE-only tsconfigs).
PR Close#44505
This commit finishes the removal of View Engine from the codebase, deleting
those pieces of @angular/compiler which were only used for VE.
Co-Authored-By: JoostK <joost.koehoorn@gmail.com>
PR Close#44368
This commit removes most tests that were designated as only covering View
Engine code. It also removes tag filters from CI and local commands to run
tests.
In a few cases (such as with the packages/compiler tests), this tag was
improperly applied, and certain test cases have been added back running in
Ivy mode.
This commit also empties `@angular/compiler/testing` as it is no longer
necessary (this is safe since compiler packages are not public API). It can
be deleted in the future.
PR Close#43884
When extracting i18n messages from templates, ICU messages are split out from the
message that contains them. This can make it difficult in the translation files to match up
the two messages, especially if the ICU is reused in multiple placeholders.
This commit builds on top of the previous one to expose the message ID of ICU messages
from the ICU placeholders as additional metadata in the `$localize` tagged strings.
Now the metablock following any placeholder can also contain the associated ID
delimited from the placeholder name by `@@`.
Fixes#17506
PR Close#43534
After updating to a more recent version of rollup, rollup started to
complain because the `TreeParseResult` class is being re-exported
twice in the `index.ts -> public-api.ts -> compiler.ts` entry-point.
Rollup threw errors like:
```
Error: "ParseTreeResult" cannot be exported from
<..>/ml_parser/parser.mjs as it is a re-export that references itself.
```
It seems like Rollup ideally would not throw here, similar to TypeScript
which detects that these exports are the same and just dedupes them, but
it's low-effort fixing this for now and actually is a good opportunity
to make the public API a little more easy understand (when looking at
the `compiler.ts` file).
PR Close#43431
Currently the compiler has three different classes to represent a "call to something":
1. `MethodCall` - `foo.bar()`
2. `SafeMethodCall` - `foo?.bar()`.
3. `FunctionCall` - Any calls that don't fit into the first two classes. E.g. `foo.bar()()`.
There are a few problems with this approach:
1. It is inconistent with the TypeScript AST which only has one node: `CallExpression`.
2. It means that we have to maintain more code, because the various parts of the compiler need to know about three node types.
3. It doesn't allow us to easily implement some new JS features like safe calls (e.g. `foo.bar?.())`).
These changes rework the compiler so that it produces only one node: `Call`. The new node behaves similarly to the TypeScript `CallExpression` whose `receiver` can be any expression.
There was a similar situation in the output AST where we had an `InvokeMethodExpression` and `InvokeFunctionExpression`. I've combined both of them into `InvokeFunctionExpression`.
PR Close#42882
Previously, the way templates were tokenized meant that we lost information
about the location of interpolations if the template contained encoded HTML
entities. This meant that the mapping back to the source interpolated strings
could be offset incorrectly.
Also, the source-span assigned to an i18n message did not include leading
whitespace. This confused the output source-mappings so that the first text
nodes of the message stopped at the first non-whitespace character.
This commit makes use of the previous refactorings, where more fine grain
information was provided in text tokens, to enable the parser to identify
the location of the interpolations in the original source more accurately.
Fixes#41034
PR Close#43132
Previously, the way templates were tokenized meant that we lost information
about the location of interpolations if the template contained encoded HTML
entities. This meant that the mapping back to the source interpolated strings
could be offset incorrectly.
Also, the source-span assigned to an i18n message did not include leading
whitespace. This confused the output source-mappings so that the first text
nodes of the message stopped at the first non-whitespace character.
This commit makes use of the previous refactorings, where more fine grain
information was provided in text tokens, to enable the parser to identify
the location of the interpolations in the original source more accurately.
Fixes#41034
PR Close#42062
Adds support for shorthand property declarations inside Angular templates. E.g. doing `{foo, bar}` instead of `{foo: foo, bar: bar}`.
Fixes#10277.
PR Close#42421
Currently we support safe property (`a?.b`) and method (`a?.b()`) accesses, but we don't handle safe keyed reads (`a?.[0]`) which is inconsistent. These changes expand the compiler in order to support safe key read expressions as well.
PR Close#41911
Adds a `collectCommentNodes` option on `ParseTemplateOptions` which will cause the returned `ParsedTemplate` to include an array of all html comments found in the template.
PR Close#41251
The current logic in the compiler is to bail when there are errors when
parsing a template into an HTML AST or when there are errors in the i18n
metadata. As a result, a template with these types of parse errors
_will not have any information for the language service_. This is because we
never attempt to conver the HTML AST to a template AST in these
scenarios, so there are no template AST nodes for the language service
to look at for information. In addition, this also means that the errors
are never displayed in the template to the user because there are no
nodes to map the error to.
This commit adds an option to the template parser to temporarily ignore
the html parse and i18n meta errors and always perform the template AST
conversion. At the end, the i18n and HTML parse errors are appended to
the returned errors list. While this seems risky, it at least provides
us with more information than we had before (which was 0) and it's only
done in the context of the language service, when the compiler is
configured to use poisoned data (HTML parse and i18n meta errors can be
interpreted as a "poisoned" template).
fixes angular/vscode-ng-language-service#1140
PR Close#41068
Adds an error if a reference is used more than once on the same element (e.g. `<div #a #a>`).
We used to have this error in ViewEngine, but it wasn't ported over to Ivy.
Fixes#40536.
PR Close#40538
If the template parse option `leadingTriviaChars` is configured to
consider whitespace as trivia, any trailing whitespace of an element
would be considered as leading trivia of the subsequent element, such
that its `start` span would start _after_ the whitespace. This means
that the start span cannot be used to mark the end of the current
element, as its trailing whitespace would then be included in its span.
Instead, the full start of the subsequent element should be used.
To harden the tests that for the Ivy parser, the test utility `parseR3`
has been adjusted to use the same configuration for `leadingTriviaChars`
as would be the case in its production counterpart `parseTemplate`. This
uncovered another bug in offset handling of the interpolation parser,
where the absolute offset was computed from the start source span
(which excludes leading trivia) whereas the interpolation expression
would include the leading trivia. As such, the absolute offset now also
uses the full start span.
Fixes#39148
PR Close#40513
We should provide the completion when the cursor is in the attribute
name after the `@` and `animate-`, but now the `KeySpan` starts from the
`@` or `animate-`. For example, the animation event `(@name.done)="v"`,
we can know where the cursor is by the `KeySpan` of `name.done` exactly,
it's in the event name or in the phase name.
PR Close#40347
Now when the animation trigger output event is missing its phase value name, the `BoundEvent` will be ignored,
but it's useful for completion in language service.
PR Close#39925
This commit introduces an `isStructural` flag on directive metadata, which
is `true` if the directive injects `TemplateRef` (and thus is at least
theoretically usable as a structural directive). The flag is not used for
anything currently, but will be utilized by the Language Service to offer
better autocompletion results for structural directives.
PR Close#40032
Similar to #39613, #39609, and #38898, we should store the `keySpan` for
Reference nodes so that we can accurately map from a template node to a
span in the original file. This is most notably an issue at the moment
for directive references `#ref="exportAs"`. The current behavior for the
language service when requesting information for the reference
is that it will return a text span that results in
highlighting the entire source when it should only highlight "ref" (test
added for this case as well).
PR Close#39616
Though we currently have the knowledge of where the `key` for an
event binding appears during parsing, we do not propagate this
information to the output AST. This means that once we produce the
template AST, we have no way of mapping a template position to the key
span alone. The best we can currently do is map back to the
`sourceSpan`. This presents problems downstream, specifically for the
language service, where we cannot provide correct information about a
position in a template because the AST is not granular enough.
This is essentially identical to the change from #38898, but for event
bindings rather than input bindings.
PR Close#39609
Similar to #39609 and #38898, though we currently have the knowledge of where the key for an
attribute appears during parsing, we do not propagate this
information to the output AST. This means that once we produce the
template AST, we have no way of mapping a template position to the key
span alone. The best we can currently do is map back to the
sourceSpan. This presents problems downstream, specifically for the
language service, where we cannot provide correct information about a
position in a template because the AST is not granular enough.
PR Close#39613
Tokenized text node may have leading whitespace skipped from their
source-span. But the source-span is used to compute where there are
interpolated blocks, resulting in placeholder nodes whose source-spans
are offset by the amount of skipped characters.
This fix uses the `fullStart` location of text source-spans for computing
the source-span of placeholders, so that they are accurate.
Fixes#39195
PR Close#39486
In an i18n message, two placeholders next to each other must have
an "empty" message-part to separate them. Previously, the source-span
for this message-part was pointing to the wrong original location.
This caused problems in the generated source-maps and lead to extracted
i18n messages from being rendered incorrectly.
PR Close#39486
Prior to this change, expressions within ICUs would have a source span
corresponding with the whole ICU. This commit narrows down the source
spans of these expressions to the exact location in the source file, as
a prerequisite for reporting type check errors within these expressions.
PR Close#39072
Currently it is impossible to determine the source of a binding that
generates `BoundAttribute` because all bound attributes generated from a
microsyntax expression share the same source span.
For example, in
```html
<div *ngFor="let item of items; trackBy: trackByFn"></div>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
source span for all `BoundAttribute`s generated from microsyntax
```
the `BoundAttribute` for both `ngForOf` and `ngForTrackBy`
share the same source span.
A lot of hacks were necessary in View Engine language service to work
around this limitation. It was done by inspecting the whole source span
then figuring out the relative position of the cursor.
With this change, we introduce a flag to set the binding span as the
source span of the `ParsedProperty` in Ivy AST.
This flag is needed so that we don't have to change VE ASTs.
Note that in the binding parser, we already set `bindingSpan` as the
source span for a `ParsedVariable`, and `keySpan` as the source span for
a literal attribute. This change makes the Ivy AST more consistent by
propagating the binding span to `ParsedProperty` as well.
PR Close#39036
This commit updates the symbols in the TemplateTypeCheck API and methods
for retrieving them:
* Include `isComponent` and `selector` for directives so callers can determine which
attributes on an element map to the matched directives.
* Add a new `TextAttributeSymbol` and return this when requesting a symbol for a `TextAttribute`.
* When requesting a symbol for `PropertyWrite` and `MethodCall`, use the
`nameSpan` to retrieve symbols.
* Add fix to retrieve generic directives attached to elements/templates.
PR Close#38844
Now that we have `keySpan` for `BoundAttribute` (implemented in
https://github.com/angular/angular/pull/38898) we could do the same
for `Variable`.
This would allow us to distinguish the LHS and RHS from the whole source
span.
PR Close#38965
Though we currently have the knowledge of where the `key` for an
attribute binding appears during parsing, we do not propagate this
information to the output AST. This means that once we produce the
template AST, we have no way of mapping a template position to the key
span alone. The best we can currently do is map back to the
`sourceSpan`. This presents problems downstream, specifically for the
language service, where we cannot provide correct information about a
position in a template because the AST is not granular enough.
PR Close#38898
Using an interface makes the code cleaner and more readable.
This change also adds the `range` property to the type to be used
for source-mapping.
PR Close#38775
The current tests print out the span numbers, which are really difficult to verify
since it requires manually going to the template string and looking at what
characters appear within those indexes. The better humanization would be
to use the toString method of the spans, which prints the span text itself
PR Close#38902
Recent work on compiler internals in #38539 led to an unexpected failure,
where a pipe used exclusively inside of an ICU would no longer be
emitted into the compilation output. This caused runtime errors due to
missing pipes.
The issue occurred because the change in #38539 would determine the set
of used pipes up-front, independent from the template compilation using
the `R3TargetBinder`. However, `R3TargetBinder` did not consider
expressions within ICUs, so any pipe usages within those expressions
would not be detected. This fix unblocks #38539 and also concerns
upcoming linker work, given that prelink compilations would not go
through full template compilation but only `R3TargetBinder`.
PR Close#38810
In a microsyntax expressions, some attributes are not bound after
desugaring. For example,
```html
<div *ngFor="let item of items">
</div>
```
gets desugared to
```html
<ng-template ngFor let-items [ngForOf]="items">
</ngtemplate>
```
In this case, `ngFor` should be a literal attribute with no RHS value.
Therefore, its source span should be just the `keySpan` and not the
source span of the original template node.
This allows language service to precisely pinpoint different spans in a
microsyntax to provide accurate information.
PR Close#38766
The `MessagePiece` and derived classes, `LiteralPiece` and `PlaceholderPiece`
need to be referenced in the `LocalizedString` output AST class, so that we
can render the source-spans of each piece.
PR Close#38645
The `R3TargetBinder` accepts an interface for directive metadata which
declares types for `input` and `output` objects. These types convey the
mapping between the property names for an input or output and the
corresponding property name on the component class. Due to
`R3TargetBinder`'s requirements, this mapping was specified with property
names as keys and field names as values.
However, because of duck typing, this interface was accidentally satisifed
by the opposite mapping, of field names to property names, that was produced
in other parts of the compiler. This form more naturally represents the data
model for inputs.
Rather than accept the field -> property mapping and invert it, this commit
introduces a new abstraction for such mappings which is bidirectional,
eliminating the ambiguous plain object type. This mapping uses new,
unambiguous terminology ("class property name" and "binding property name")
and can be used to satisfy both the needs of the binder as well as those of
the template type-checker (field -> property).
A new test ensures that the input/output metadata produced by the compiler
during analysis is directly compatible with the binder via this unambiguous
new interface.
PR Close#38685
Previously, the compiler was not able to display template parsing errors as
true `ts.Diagnostic`s that point inside the template. Instead, it would
throw an actual `Error`, and "crash" with a stack trace containing the
template errors.
Not only is this a poor user experience, but it causes the Language Service
to also crash as the user is editing a template (in actuality the LS has to
work around this bug).
With this commit, such parsing errors are converted to true template
diagnostics with appropriate span information to be displayed contextually
along with all other diagnostics. This majorly improves the user experience
and unblocks the Language Service from having to deal with the compiler
"crashing" to report errors.
PR Close#38576
Previously, the `sourceSpan` and `startSourceSpan` were the same
object, which meant that you had the following situation:
```
element = <div>some content</div>
sourceSpan = <div>
startSourceSpan = <div>
endSourceSpan = </div>
```
This made `sourceSpan` redundant and meant that if you
wanted a span for the whole element including its content
and closing tag, it had to be computed.
Now `sourceSpan` is separated from `startSourceSpan`
resulting in:
```
element = <div>some content</div>
sourceSpan = <div>some content</div>
startSourceSpan = <div>
endSourceSpan = </div>
```
PR Close#38581
ASTs for property read and method calls contain information about
the entire span of the expression, including its receiver. Use cases
like a language service and compile error messages may be more
interested in the span of the direct identifier for which the
expression is constructed (i.e. an accessed property). To support this,
this commit adds a `nameSpan` property on
- `PropertyRead`s
- `SafePropertyRead`s
- `PropertyWrite`s
- `MethodCall`s
- `SafeMethodCall`s
The `nameSpan` property already existed for `BindingPipe`s.
This commit also updates usages of these expressions' `sourceSpan`s in
Ngtsc and the langauge service to use `nameSpan`s where appropriate.
PR Close#36826