Historically, the Angular Compiler has produced both native TypeScript
diagnostics (called ts.Diagnostics) and its own internal Diagnostic format
(called an api.Diagnostic). This was done because TypeScript ts.Diagnostics
cannot be produced for files not in the ts.Program, and template type-
checking diagnostics are naturally produced for external .html template
files.
This design isn't optimal for several reasons:
1) Downstream tooling (such as the CLI) must support multiple formats of
diagnostics, adding to the maintenance burden.
2) ts.Diagnostics have gotten a lot better in recent releases, with support
for suggested changes, highlighting of the code in question, etc. None of
these changes have been of any benefit for api.Diagnostics, which have
continued to be reported in a very primitive fashion.
3) A future plugin model will not support anything but ts.Diagnostics, so
generating api.Diagnostics is a blocker for ngtsc-as-a-plugin.
4) The split complicates both the typings and the testing of ngtsc.
To fix this issue, this commit changes template type-checking to produce
ts.Diagnostics instead. Instead of reporting a special kind of diagnostic
for external template files, errors in a template are always reported in
a ts.Diagnostic that highlights the portion of the template which contains
the error. When this template text is distinct from the source .ts file
(for example, when the template is parsed from an external resource file),
additional contextual information links the error back to the originating
component.
A template error can thus be reported in 3 separate ways, depending on how
the template was configured:
1) For inline template strings which can be directly mapped to offsets in
the TS code, ts.Diagnostics point to real ranges in the source.
This is the case if an inline template is used with a string literal or a
"no-substitution" string. For example:
```typescript
@Component({..., template: `
<p>Bar: {{baz}}</p>
`})
export class TestCmp {
bar: string;
}
```
The above template contains an error (no 'baz' property of `TestCmp`). The
error produced by TS will look like:
```
<p>Bar: {{baz}}</p>
~~~
test.ts:2:11 - error TS2339: Property 'baz' does not exist on type 'TestCmp'. Did you mean 'bar'?
```
2) For template strings which cannot be directly mapped to offsets in the
TS code, a logical offset into the template string will be included in
the error message. For example:
```typescript
const SOME_TEMPLATE = '<p>Bar: {{baz}}</p>';
@Component({..., template: SOME_TEMPLATE})
export class TestCmp {
bar: string;
}
```
Because the template is a reference to another variable and is not an
inline string constant, the compiler will not be able to use "absolute"
positions when parsing the template. As a result, errors will report logical
offsets into the template string:
```
<p>Bar: {{baz}}</p>
~~~
test.ts (TestCmp template):2:15 - error TS2339: Property 'baz' does not exist on type 'TestCmp'.
test.ts:3:28
@Component({..., template: TEMPLATE})
~~~~~~~~
Error occurs in the template of component TestCmp.
```
This error message uses logical offsets into the template string, and also
gives a reference to the `TEMPLATE` expression from which the template was
parsed. This helps in locating the component which contains the error.
3) For external templates (templateUrl), the error message is delivered
within the HTML template file (testcmp.html) instead, and additional
information contextualizes the error on the templateUrl expression from
which the template file was determined:
```
<p>Bar: {{baz}}</p>
~~~
testcmp.html:2:15 - error TS2339: Property 'baz' does not exist on type 'TestCmp'.
test.ts:10:31
@Component({..., templateUrl: './testcmp.html'})
~~~~~~~~~~~~~~~~
Error occurs in the template of component TestCmp.
```
PR Close#31952
Updates the SHA that will be tested against in the `material-unit-tests` job
to the latest commit in the components repository. SHA 18b9ef3f5529f0fa8f034944681486447af7b879
is needed in order to make the newly introduced material-ci test blocklist effective.
PR Close#32243
When a template contains a binding without a value, the template parser
creates an `EmptyExpr` node. This would previously be translated into
an `undefined` value, which would cause a crash downstream as `undefined`
is not included in the allowed type, so it was not handled properly.
This commit prevents the crash by returning an actual expression for empty
bindings.
Fixes#30076Fixes#30929
PR Close#31594
Initially the blocklist has been removed because there were
no remaining disabled tests that failed. Also the blocklist
logic didn't work anymore because the `material-unit-tests` CI
job now runs against `angular/components#master` with Bazel.
388578fec9 tried to revert the removal
of the blocklist in favor of a new upcoming breaking change with
HammerJS, but the revert doesn't help since the blocklist still
doesn't work with Bazel.
In order to make the blocklist work with the unit tests running
with Bazel, a PR has been submitted on the components repository.
See: https://github.com/angular/components/pull/16833.
This commit updates the blocklist logic on the framework side to
work with the new logic on the components repo side.
PR Close#32239
This commit switches the default value of the enableIvy flag to true.
Applications that run ngc will now by default receive an Ivy build!
This does not affect the way Bazel builds in the Angular repo work, since
those are still switched based on the value of the --define=compile flag.
Additionally, projects using @angular/bazel still use View Engine builds
by default.
Since most of the Angular repo tests are still written against View Engine
(particularly because we still publish VE packages to NPM), this switch
also requires lots of `enableIvy: false` flags in tsconfigs throughout the
repo.
Congrats to the team for reaching this milestone!
PR Close#32219
This option makes ngc behave as tsc, and was originally implemented before
ngtsc existed. It was designed so we could build JIT-only versions of
Angular packages to begin testing Ivy early, and is not used at all in our
current setup.
PR Close#32219
We don't need to build this branch as it's informative for the purposes of figuring out
the diff between the master and what's synced into google3.
PR Close#32202
Previously, `ngcc` assumed that if a format property was defined in
`package.json` it would point to a valid format-path (i.e. a file that
is an entry-point for a specific format). This is generally the case,
except if a format property is set to a non-string value (such as
`package.json`) - either directly in the `package.json` (which is unusual)
or in ngcc.config.js (which is a valid usecase, when one wants a
format property to be ignored by `ngcc`).
For example, the following config file would cause `ngcc` to throw:
```
module.exports = {
packages: {
'test-package': {
entryPoints: {
'.': {
override: {
fesm2015: undefined,
},
},
},
},
},
};
```
This commit fixes it by ensuring that only format properties whose value
is a string are considered by `ngcc`.
For reference, this regression was introduced in #32052.
Fixes#32188
PR Close#32205
Initially the plan was to have a migration that adds `@Injectable()` to
all pipes in a CLI project so that the pipes can be injected in Ivy
similarly to how it worked in view engine.
Due to the planned refactorings which ensure that `@Directive`, `@Component`
and `@Pipe` also have a factory definition, this migration is no longer
needed for Ivy. Additionally since it is already disabled (due to
572b54967c) and we have a more generic
migration (known as `missing-injectable)` that could do the same as
`injectable-pipe`, we remove the migration from the code-base.
PR Close#32184
The API of `@microsoft/api-extractor` changed in a minor version which is causes an error when using dts flattening downstream.
API wil be updated on master https://github.com/angular/angular/pull/32185
PR Close#32187
Fixes an error that is thrown by `ngTemplateOutlet` under Ivy when switching from a template to null and back to a template. The error is thrown because the reference to the previous ViewRef is never cleared and the directive tries to detach a view that has already been detached.
Fixes#32060.
PR Close#32160
During the dependency analysis phase of ngcc, imports are resolved to
files on disk according to certain module resolution rules. Since module
specifiers are typically missing extensions, or can refer to index.js
barrel files within a directory, the module resolver attempts several
postfixes when searching for a module import on disk. Module specifiers
that already include an extension, however, would fail to be resolved as
ngcc's module resolver failed to check the location on disk without
adding any postfixes.
Closes#32097
PR Close#32181
During the recursive processing of dependencies, ngcc resolves the
requested file to an actual location on disk, by testing various
extensions. For recursive calls however, the path is known to have been
resolved in the module resolver. Therefore, it is safe to move the path
resolution to the initial caller into the recursive process.
Note that this is not expected to improve the performance of ngcc, as
the call to `resolveFileWithPostfixes` is known to succeed immediately,
as the provided path is known to exist without needing to add any
postfixes. Furthermore, the FileSystem caches whether files exist, so
the additional check that we used to do was cheap.
PR Close#32181
This commit fixes many diagnostic tests have have incorrect/uncaught assertions.
Also added more assertions to make sure TS diagnostics are clear.
A few test util methods are removed to reduce clutter and improve readability.
PR Close#32161
Remove unnecessary private method `getDeclarationFromNode` and moved
some logic to utils instead so that it can be tested in isolation of the
Language Service infrastructure.
The use of typechecker to check the directive is also not necessary,
since resolve.getNonNormalizedDirectiveMetadata() will check if the
directive is actually an Angular entity.
PR Close#32156
TypeScript downlevels `for-of` loops for ES5 targets. As a result, generated output contains extra code, including a try-catch block, which has code size and performance implications. This is especially important for runtime code where we want to keep it as small as possible. This commit changes `for-of` loops in runtime code to regular `for` loops.
PR Close#32157
Bundle size changed in both zone.js(legacy) and zone-evergreen.js
- zone.js(legacy) package increased a little because the following feature and fixes.
1. #31699, handle MSPointer events PR
2. https://github.com/angular/zone.js/pull/1219 to add __zone_symbol__ customization support
- zone-evergreen.js package decreased because
1. the MSPointer PR only for legacy
2. the Object.defineProperty patch is moved to legacy #31660
PR Close#31975
ngcc needs to solve a unique problem when compiling typings for an
entrypoint: it must resolve a declaration within a .js file to its
representation in a .d.ts file. Since such .d.ts files can be used in deep
imports without ever being referenced from the "root" .d.ts, it's not enough
to simply match exported types to the root .d.ts. ngcc must build an index
of all .d.ts files.
Previously, this operation had a bug: it scanned all .d.ts files in the
.d.ts program, not only those within the package. Thus, if a class in the
program happened to share a name with a class exported from a dependency's
.d.ts, ngcc might accidentally modify the wrong .d.ts file, causing a
variety of issues downstream.
To fix this issue, ngcc's .d.ts scanner now limits the .d.ts files it
indexes to only those declared in the current package.
PR Close#32129
One of the compiler's tasks is to enumerate the exports of a given ES
module. This can happen for example to resolve `foo.bar` where `foo` is a
namespace import:
```typescript
import * as foo from './foo';
@NgModule({
directives: [foo.DIRECTIVES],
})
```
In this case, the compiler must enumerate the exports of `foo.ts` in order
to evaluate the expression `foo.DIRECTIVES`.
When this operation occurs under ngcc, it must deal with the different
module formats and types of exports that occur. In commonjs code, a problem
arises when certain exports are downleveled.
```typescript
export const DIRECTIVES = [
FooDir,
BarDir,
];
```
can be downleveled to:
```javascript
exports.DIRECTIVES = [
FooDir,
BarDir,
```
Previously, ngtsc and ngcc expected that any export would have an associated
`ts.Declaration` node. `export class`, `export function`, etc. all retain
`ts.Declaration`s even when downleveled. But the `export const` construct
above does not. Therefore, ngcc would not detect `DIRECTIVES` as an export
of `foo.ts`, and the evaluation of `foo.DIRECTIVES` would therefore fail.
To solve this problem, the core concept of an exported `Declaration`
according to the `ReflectionHost` API is split into a `ConcreteDeclaration`
which has a `ts.Declaration`, and an `InlineDeclaration` which instead has
a `ts.Expression`. Differentiating between these allows ngcc to return an
`InlineDeclaration` for `DIRECTIVES` and correctly keep track of this
export.
PR Close#32129
Instead of destroying and recreating MetadataResolver every time the
program changes, create one instance and reuse it throughout the
lifetime of the language service.
Since Angular StaticSymbols are invalidated when program gets
out-of-date, this should be safe.
This should make the language service more more performant.
PR Close#32145
In VE the `Sanitizer` is always available in `BrowserModule` because the VE retrieves it using injection.
In Ivy the injection is optional and we have instructions instead of component definition arrays. The implication of this is that in Ivy the instructions can pull in the sanitizer only when they are working with a property which is known to be unsafe. Because the Injection is optional this works even if no Sanitizer is present. So in Ivy we first use the sanitizer which is pulled in by the instruction, unless one is available through the `Injector` then we use that one instead.
This PR does few things:
1) It makes `Sanitizer` optional in Ivy.
2) It makes `DomSanitizer` tree shakable.
3) It aligns the semantics of Ivy `Sanitizer` with that of the Ivy sanitization rules.
4) It refactors `DomSanitizer` to use same functions as Ivy sanitization for consistency.
PR Close#31934
This commit creates two concrete classes Inline and External
TemplateSource to differentiate between templates in TS file and
HTML file.
Knowing the template type makes the code much more explicit which
filetype we are dealing with.
With these two classes, there is no need for `getTemplateAt()` method in
TypeScriptHost. Removing this method is safe since it is not used in the
extension. This reduces the API surface of TypescriptHost.
PR Close#32127