In the past, the legacy (VE-based) language service would use a
`UrlResolver` instance to resolve file paths, primarily for compiler
resources like external templates. The problem with this is that the
UrlResolver is designed to resolve URLs in general, and so for a path
like `/a/b/#c`, `#c` is treated as hash/fragment rather than as part
of the path, which can lead to unexpected path resolution (f.x.,
`resolve('a/b/#c/d.ts', './d.html')` would produce `'a/b/d.html'` rather
than the expected `'a/b/#c/d.html'`).
This commit resolves the issue by using Node's `path` module to resolve
file paths directly, which aligns more with how resources are resolved
in the Ivy compiler.
The testing story here is not great, and the API for validating a file
path could be a little bit prettier/robust. However, since the VE-based
language service is going into more of a "maintenance mode" now that
there is a clear path for the Ivy-based LS moving forward, I think it is
okay not to spend too much time here.
Closes https://github.com/angular/vscode-ng-language-service/issues/892
Closes https://github.com/angular/vscode-ng-language-service/issues/1001
PR Close#39917
Create stubs for `findRenameLocations` for both VE and Ivy Language Service
implementations. This will prevent failed requests when it is implemented on the vscode plugin side.
PR Close#39919
At the moment, when creating a root module, a subscription to the
`onError` subject is also created. It captures the scope where `NgModuleRef`
is created and prevents it from being garbage collected. Also note that this
`NgModuleRef` has a reference to the root module instance (e.g. `AppModule`),
which also prevents it from being GC'd.
PR Close#39940
When the compiler is invoked via ngc or the Angular CLI, its APIs are used
under the assumption that Angular analysis/diagnostics are only requested if
the program has no TypeScript-level errors. A result of this assumption is
that the incremental engine has not needed to resolve changes via its
dependency graph when the program contained broken imports, since broken
imports are a TypeScript error.
The Angular Language Service for Ivy is using the compiler as a backend, and
exercising its incremental compilation APIs without enforcing this
assumption. As a result, the Language Service has run into issues where
broken imports cause incremental compilation to fail and produce incorrect
results.
This commit introduces a mechanism within the compiler to keep track of
files for which dependency analysis has failed, and to always treat such
files as potentially affected by future incremental steps. This is tested
via the Language Service infrastructure to ensure that the compiler is doing
the right thing in the case of invalid imports.
PR Close#39923
Previously, if a component had an external template with a hard error, the
compiler would "forget" the link between that component and its NgModule.
Additionally, the NgModule would be marked as being in error, because the
template issue would prevent the compiler from registering the component
class as a component, so from the NgModule it would look like a declaration
of a non-directive/pipe class. As a combined result, the next incremental
step could fix the template error, but would not refresh diagnostics for the
NgModule, leading to an incrementality issue.
The various facets of this problem were fixed in prior commits. This commit
adds a test verifying the above case works now as expected.
PR Close#39923
To avoid overwhelming a user with secondary diagnostics that derive from a
"root cause" error, the compiler has the notion of a "poisoned" NgModule.
An NgModule becomes poisoned when its declaration contains semantic errors:
declarations which are not components or pipes, imports which are not other
NgModules, etc. An NgModule also becomes poisoned if it imports or exports
another poisoned NgModule.
Previously, the compiler tracked this poisoned status as an alternate state
for each scope. Either a correct scope could be produced, or the entire
scope would be set to a sentinel error value. This meant that the compiler
would not track any information about a scope that was determined to be in
error.
This method presents several issues:
1. The compiler is unable to support the language service and return results
when a component or its module scope is poisoned.
This is fine for compilation, since diagnostics will be produced showing the
error(s), but the language service needs to still work for incorrect code.
2. `getComponentScopes()` does not return components with a poisoned scope,
which interferes with resource tracking of incremental builds.
If the component isn't included in that list, then the NgModule for it will
not have its dependencies properly tracked, and this can cause future
incremental build steps to produce incorrect results.
This commit changes the tracking of poisoned module scopes to use a flag on
the scope itself, rather than a sentinel value that replaces the scope. This
means that the scope itself will still be tracked, even if it contains
semantic errors. A test is added to the language service which verifies that
poisoned scopes can still be used in template type-checking.
PR Close#39923
Previously, if a trait's analysis step resulted in diagnostics, the trait
would be considered "errored" and no further operations, including register,
would be performed. Effectively, this meant that the compiler would pretend
the class in question was actually undecorated.
However, this behavior is problematic for several reasons:
1. It leads to inaccurate diagnostics being reported downstream.
For example, if a component is put into the error state, for example due to
a template error, the NgModule which declares the component would produce a
diagnostic claiming that the declaration is neither a directive nor a pipe.
This happened because the compiler wouldn't register() the component trait,
so the component would not be recorded as actually being a directive.
2. It can cause incorrect behavior on incremental builds.
This bug is more complex, but the general issue is that if the compiler
fails to associate a component and its module, then incremental builds will
not correctly re-analyze the module when the component's template changes.
Failing to register the component as such is one link in the larger chain of
issues that result in these kinds of issues.
3. It lumps together diagnostics produced during analysis and resolve steps.
This is not causing issues currently as the dependency graph ensures the
right classes are re-analyzed when needed, instead of showing stale
diagnostics. However, the dependency graph was not intended to serve this
role, and could potentially be optimized in ways that would break this
functionality.
This commit removes the concept of an "errored" trait entirely from the
trait system. Instead, analyzed and resolved traits have corresponding (and
separate) diagnostics, in addition to potentially `null` analysis results.
Analysis (but not resolution) diagnostics are carried forward during
incremental build operations. Compilation (emit) is only performed when
a trait reaches the resolved state with no diagnostics.
This change is functionally different than before as the `register` step is
now performed even in the presence of analysis errors, as long as analysis
results are also produced. This fixes problem 1 above, and is part of the
larger solution to problem 2.
PR Close#39923
If the testcase has not specified that errors were expected, then any
errors that have occurred should be reported. These errors may have
prevented an output file from being generated, which resulted in hard
to debug test failures due to missing files.
PR Close#39862
This commit adds a few tests to verify that the `onDestroy` callbacks are invoked when `ComponentRef` instance
is destroyed and the logic is consistent between ViewEngine and Ivy.
PR Close#39876
In the new behavior Angular removes applications from the testability registry when the
root view gets destroyed. This eliminates a memory leak, because before that the
TestabilityRegistry holds references to HTML elements, thus they cannot be GCed.
PR Close#22106
PR Close#39876
The Language Service "find references" currently uses the
`ngtypecheck.ts` suffix to determine if a file is a shim file. Instead,
a better API would be to expose a method in the template type checker
that does this verification so that the LS does not have to "know" about
the typecheck suffix. This also fixes an issue (albeit unlikely) whereby a file
in the user's program that _actually_ is named with the `ngtypecheck.ts`
suffix would have been interpreted as a shim file.
PR Close#39768
This commit adds "find references" functionality to the Ivy integrated
language service. The basic approach is as follows:
1. Generate shims for all files to ensure we find references in shims
throughout the entire program
2. Determine if the position for the reference request is within a
template.
* Yes, it is in a template: Find which node in the template AST the
position refers to. Then find the position in the shim file for that
template node. Pass the shim file and position in the shim file along
to step 3.
* No, the request for references was made outside a template: Forward
the file and position to step 3.
3. (`getReferencesAtTypescriptPosition`): Call the native TypeScript LS
`getReferencesAtPosition`. For each reference that is in a shim file, map those
back to a template location, otherwise return it as-is.
PR Close#39768
There were two issues with the current TCB:
1. The logic for only wrapping the right hand side of the property write
if it was not already a parenthesized expression was incorrect. A
parenthesized expression could still have a trailing comment, and if
that were the case, that span comment would still be ambiguous, as explained
by the comment in the code before `wrapForTypeChecker`.
2. The right hand side of keyed writes was not wrapped in parens at all
PR Close#39768
In order to map the a safe property read's method access in the type check block
directly back to the property in the template source, we need to
include the `SafePropertyRead`'s `nameSpan` with the `ts.propertyAccess` for
the pipe's transform method.
Note that this is specifically relevant to the Language Service's "find
references" feature. As an example, with something like `{{a?.value}}`,
when calling "find references" on the 'value' we want the text
span of the reference to just be `value` rather than the entire source
`a?.value`.
PR Close#39768
In order to map the pipe's `transform` method in the type check block
directly back to the pipe name in the template source, we need to
include the `BindingPipe`'s `nameSpan` with the `ts.methodAccess` for
the pipe's transform method.
Note that this is specifically relevant to the Language Service's "find
references" feature. As an example, with something like `-2.5 | number:'1.0-0'`,,
when calling "find references" on the 'number' pipe we want the text
span of the reference to just be `number` rather than the entire binding
pipe's source `-2.5 | number:'1.0-0'`.
PR Close#39768
Fix a case where, if the parent class had already been patched, it would
not patch the child class. In addition to checking if the method is
defined in the prototype, and not inherited, it also does the same for
the unpatched method.
PR Close#39850
Currently we convert objects to strings using `'' + value` which is quickest,
but it stringifies the value using its `valueOf`, rather than `toString`. These
changes switch to using `String(value)` which has identical performance
and calls the `toString` method as expected. Note that another option
was calling `toString` directly, but benchmarking showed it to be slower.
I've included the benchmark I used to verify the performance so we have it
for future reference and we can reuse it when making changes to `renderStringify`
in the future.
Also for reference, here are the results of the benchmark:
```
Benchmark: renderStringify
concat: 2.006 ns(0%)
concat with toString: 2.201 ns(-10%)
toString: 237.494 ns(-11741%)
toString with toString: 121.072 ns(-5937%)
constructor: 2.201 ns(-10%)
constructor with toString: 2.201 ns(-10%)
toString mono: 14.536 ns(-625%)
toString with toString mono: 9.757 ns(-386%)
```
Fixes#38839.
PR Close#39843
Previously this would have just printed that `false` was not equal to
`true`, which, although true, is not very helpful. This commit adds
details about which special check failed together with the generated
code, for easier debugging.
PR Close#39863
We currently only wrap the event listener in the function which ensures
ancestors are marked for check when the listener is placed on an element
that has a native method for listening to an event. We actually need to do
this wrapping in all cases so that events that are attached to non-rendered
template items (`ng-template` and `ng-container`) also mark ancestors for check
when they receive the event.
fixes#39832
PR Close#39833
DEPRECATION:
Mark the {[key: string]: any} type for the options property of the FormBuilder.group method as deprecated.
Using AbstractControlOptions gives the same functionality and is type-safe.
PR Close#39769
`@angular/core` support zone.js `^0.10.2 and ^0.11.3`, so this PR updates the
peerDependencies to `^0.10.2 || ^ 0.11.3`, so the app will not show warning about
peer denepdency not consistent when using zone.js 0.10.x version.
PR Close#39809
This commit provides the machinery for the new file-based compliance test
approach for i18n tests, and migrates the i18n tests to this new format.
PR Close#39661
For element queries that return sufficiently large NodeList
objects, using spread syntax to populate the results array
causes a RangeError due to the call stack limit being reached.
This commit updates the code to use regular "for" loop instead.
Fixes#38551.
PR Close#39646
The ARB format is a JSON file containing an object where the keys are the
message ids and the values are the translations.
It is extensible because it can also contain metadata about each message.
For example:
```
{
"@@locale": "...",
"message-id": "Translated message string",
"@message-id": {
"type": "text",
"description": "Some description text",
"x-locations": [{ "start": {"line": 23, "column": 145}, "file": "some/file.ts" }]
},
}
```
For more information, see:
https://github.com/google/app-resource-bundle/wiki/ApplicationResourceBundleSpecification
PR Close#36795
Create stubs for getTypeDefinitionAtPosition for both VE and Ivy Language Service
implementations. This will prevent failed requests when it is implemented on the vscode plugin side.
PR Close#39829
This commit implements partial compilation of components, together with
linking the partial declaration into its full AOT output.
This commit does not yet enable accurate source maps into external
templates. This requires additional work to account for escape sequences
which is non-trivial. Inline templates that were represented using a
string or template literal are transplated into the partial declaration
output, so their source maps should be accurate. Note, however, that
the accuracy of source maps is not currently verified in tests; this is
also left as future work.
The golden files of partial compilation output have been updated to
reflect the generated code for components. Please note that the current
output should not yet be considered stable.
PR Close#39707
In production mode this flag defaults to `true`, but the compliance
tests override this to `false` unless it is provided. As such, the
linker should also adhere to this default as otherwise the compilation
output would not align with the output of the full tests.
There are still tests that exercise the value of this flag, together
with it being `undefined` to verify the behavior of the actual default
value.
PR Close#39707
The linker does not currently support outputting ES5 syntax, so any
compliance tests that request ES5 output cannot be run in partial
compilation mode. This commit marks these tests as pending.
PR Close#39707
This commit adds the `i18nUseExternalIds` option to the linker options,
as the compliance tests exercise compilation results with and without
this flag enabled. We therefore need to configure the linker to take
this option into account, as otherwise the compliance test output would
not be identical.
Additionally, this commit switches away from spread syntax to set
the default options. This introduced a problem when the user-provided
options object did specify the keys, but with an undefined value. This
would have prevented the default options from being applied.
PR Close#39707