Currently the expressions used in a template string are automatically named
`PH_1`, `PH_2`, etc. Whereas interpolations used in i18n templates generate
placeholders automatically named `INTERPOLATION`, `INTERPOLATION_1`, etc.
This commit aligns the behaviors by starting the generated placeholder
names for expressions at `PH`, then `PH_1`, etc.
It also documents this behavior in the documentation of `$localize` as
it was not mentioned before.
PR Close#32493
Prior to this patch if any backwards-compatible Angular code was using
Ivy then the built-in `window.ng` debug utilies would not be exposed.
PR Close#32725
Now that the `$localize` translations are `MessageId` based the
compiler must render `MessageId`s in its generated `$localize` code.
This is because the `MessageId` used by the compiler is computed
from information that does not get passed through to the `$localize`
tagged string.
For example, the generated code for the following template
```html
<div id="static" i18n-title="m|d" title="introduction"></div>
```
will contain these localization statements
```ts
if (ngI18nClosureMode) {
/**
* @desc d
* @meaning m
*/
const MSG_EXTERNAL_8809028065680254561$$APP_SPEC_TS_1 = goog.getMsg("introduction");
I18N_1 = MSG_EXTERNAL_8809028065680254561$$APP_SPEC_TS_1;
}
else {
I18N_1 = $localize \`:m|d@@8809028065680254561:introduction\`;
}
```
Since `$localize` is not able to accurately regenerate the source-message
(and so the `MessageId`) from the generated code, it must rely upon the
`MessageId` being provided explicitly in the generated code.
The compiler now prepends all localized messages with a "metadata block"
containing the id (and the meaning and description if defined).
Note that this metadata block will also allow translation file extraction
from the compiled code - rather than relying on the legacy ViewEngine
extraction code. (This will be implemented post-v9).
Although these metadata blocks add to the initial code size, compile-time
inlining will completely remove these strings and so will not impact on
production bundle size.
PR Close#32594
As discussed in https://hackmd.io/33M5Wb-JT7-0fneA0JuHPA `SourceMessage`
strings are not sufficient for matching translations.
This commit updates `@angular/localize` to use `MessageId`s for translation
matching instead.
Also the run-time translation will now log a warning to the console if a
translation is missing.
BREAKING CHANGE:
Translations (loaded via the `loadTranslations()` function) must now use
`MessageId` for the translation key rather than the previous `SourceMessage`
string.
PR Close#32594
Previously the translation key used for translations was the `SourceMessage`
but it turns out that this is insufficient because "meaning" and "custom-id"
metadata affect the translation key.
Now run-time translation is keyed off the `MessageId`.
PR Close#32594
This commit documents and extends the basic `$localize`
implementation to support adding a metadata block to the
start of a tagged message.
All the "pass-though" version does is to strip this block out,
similar to what it does to placeholder name blocks.
PR Close#32594
The `packages/localize/test/utils` folder was not being
included in the unit tests because the glob for the spec
files was only looking in the top level folder.
PR Close#32594
Previously we were looking for a global factory call that looks like:
```ts
(factory((global.ng = global.ng || {}, global.ng.common = {}), global.ng.core))"
```
but in some cases it looks like:
```ts
(global = global || self, factory((global.ng = global.ng || {}, global.ng.common = {}), global.ng.core))"
```
Note the `global = global || self` at the start of the statement.
This commit makes the test when finding the global factory
function call resilient to being in a comma list.
PR Close#32709
Removes the selection strategies for `AngularTemplateCompile` and `TypeScriptCompile` causing
them to use the automatic selection strategies instead.
PR Close#32678
Prior to this patch, each time `advance()` would run (or when a
templateFn or hostBindings code exits) then the core change detection
code would check to see whether the styling data needs to be reset. This
patch removes that functionality and places everything inside of the
scheduled styling exit function. This means that each time one or more
styling bindings run (even if the value hasn't changed) then an exit
function will be scheduled and that will do all the cleanup.
PR Close#32591
This patch is a final major refactor in styling Angular.
This PR includes three main fixes:
All temporary state taht is persisted between template style/class application
and style/class application in host bindings is now removed.
Removes the styling() and stylingApply() instructions.
Introduces a "direct apply" mode that is used apply prop-based
style/class in the event that there are no map-based bindings as
well as property collisions.
PR Close#32259
PR Close#32591
Currently all property instructions eventually call into `elementPropertyInternal` which in turn calls to `getLView`, however all of the instructions already have access to the LView. These changes switch to passing in the LView as a parameter.
PR Close#32681
Within an Angular package, it can happen that there are
entry-points which do not contain features that belong into
an `@NgModule` or need metadata files to be generated.
For example: the `cdk`, `cdk/testing` and `cdk/coercion`
entry-points. Besides other entry-points in the `cdk`
package, those entry-points do not need metadata to
be generated and no not use the `ng_module` rule.
Currently the "ng_package" rule properly picks up such
entry-points and builds bundles, does downleveling etc.
The only thing it misses is that no `package.json` files
are generated for the entry-point. This means that consumers
will not be able to use these entry-points built with "ts_library"
(except accessing the individual bundlings explicitly).
The "ng_package" rule should follow the full APF specification
for such entry-points. Partially building bundles and doing the
downleveling is confusing and a breaking issue.
The motifivation of supporting this (besides making the
rule behavior consistent; the incomplete output is not
acceptable), is that using the "ng_module" rule does
not make sense to be used for non-Angular entry-points.
Especially since it depends on Angular packages to
be specified as Bazel action inputs just to compile
vanilla TypeScript with `@angular/compiler-cli`.
PR Close#32610
If an <ng-template> contains a structural directive (for example *ngIf), Ngtsc generates extra template function with 1 template instruction call. When <ng-template> tag also contains i18n attribute on it, we generate i18nStart and i18nEnd instructions around it, which is unnecessary and breaking runtime. This commit adds a logic to make sure we do not generate i18n instructions in case only `template` is present.
PR Close#32623
This change aims to align the documented members in `CODEOWNERS` with
the actual members of the secret `framework-global-approvers` GitHub
team.
PR Close#32667
Removes `addCodeAndCallback` function, opting instead to add code to a
file and then testing expectations on that fileName. Also renames
`contains` to `expectContains` to clarify its expectations.
PR Close#32656
The ModuleResolutionHost implementation inside ReflectorHost currently
relies on reading the snapshot to determine if a file exists, and use
the snapshot to retrieve the file content.
It is more straightforward and efficient to use the already existing
method fileExists() instead.
At runtime, the TypeScript LanguageServiceHost is really a Project, so
both fileExists() and readFile() methods are defined.
As a micro-optimization, skip fs lookup for tsx files.
PR Close#32642
This is a refactoring that moves the source code around to provide a better
platform for adding the compile-time inlining.
1. Move the global side-effect import from the primary entry-point to a
secondary entry-point @angular/localize/init.
This has two benefits: first it allows the top level entry-point to
contain tree-shakable shareable code; second it gives the side-effect
import more of an "action" oriented name, which indicates that importing
it does something tangible
2. Move all the source code into the top src folder, and import the localize
related functions into the localize/init/index.ts entry-point.
This allows the different parts of the package to share code without
a proliferation of secondary entry-points (i.e. localize/utils).
3. Avoid publicly exporting any utilities at this time - the only public
API at this point are the global `$localize` function and the two runtime
helpers `loadTranslations()` and `clearTranslations()`.
This does not mean that we will not expose additional helpers for 3rd
party tooling in the future, but it avoid us preemptively exposing
something that we might want to change in the near future.
Notes:
It is not possible to have the `$localize` code in the same Bazel package
as the rest of the code. If we did this, then the bundled `@angular/localize/init`
entry-point code contains all of the helper code, even though most of it is not used.
Equally it is not possible to have the `$localize` types (i.e. `LocalizeFn`
and `TranslateFn`) defined in the `@angular/localize/init` entry-point because
these types are needed for the runtime code, which is inside the primary
entry-point. Importing them from `@angular/localize/init` would run the
side-effect.
The solution is to have a Bazel sub-package at `//packages/localize/src/localize`
which contains these types and the `$localize` function implementation.
The primary `//packages/localize` entry-point imports the types without
any side-effect.
The secondary `//packages/localize/init` entry-point imports the `$localize`
function and attaches it to the global scope as a side-effect, without
bringing with it all the other utility functions.
BREAKING CHANGES:
The entry-points have changed:
* To attach the `$localize` function to the global scope import from
`@angular/localize/init`. Previously it was `@angular/localize`.
* To access the `loadTranslations()` and `clearTranslations()` functions,
import from `@angular/localize`. Previously it was `@angular/localize/run_time`.
PR Close#32488
This PR adds loggin methods to TypeScriptHost so that proper logging
to file could be done.
Three new methods are added: log(), error(), and debug().
PR Close#32645
In ngcc's reflection host for UMD and CommonJS bundles, custom logic is
present to resolve import details of an identifier. However, this custom
logic is unable to resolve an import for an identifier inside of
declaration files, as such files use the regular ESM import syntax.
As a consequence of this limitation, ngtsc is unable to resolve
`ModuleWithProviders` imports that are declared in an external library.
In that situation, ngtsc determines the type of the actual `NgModule`
that is imported, by looking in the library's declaration files for the
generic type argument on `ModuleWithProviders`. In this process, ngtsc
resolves the import for the `ModuleWithProviders` identifier to verify
that it is indeed the `ModuleWithProviders` type from `@angular/core`.
So, when the UMD reflection host was in use this resolution would fail,
therefore no `NgModule` type could be detected.
This commit fixes the bug by using the regular import resolution logic
in addition to the custom resolution logic that is required for UMD
and CommonJS bundles.
Fixes#31791
PR Close#32619
In ESM2015 bundles, a class with decorators may be emitted as follows:
```javascript
var MyClass_1;
let MyClass = MyClass_1 = class MyClass {};
MyClass.decorators = [/* here be decorators */];
```
Such a class has two declarations: the publicly visible `let MyClass`
and the implementation `class MyClass {}` node. In #32539 a refactoring
took place to handle such classes more consistently, however the logic
to find static properties was mistakenly kept identical to its broken
state before the refactor, by looking for static properties on the
implementation symbol (the one for `class MyClass {}`) whereas the
static properties need to be obtained from the symbol corresponding with
the `let MyClass` declaration, as that is where the `decorators`
property is assigned to in the example above.
This commit fixes the behavior by looking for static properties on the
public declaration symbol. This fixes an issue where decorators were not
found for classes that do in fact have decorators, therefore preventing
the classes from being compiled for Ivy.
Fixes#31791
PR Close#32619
This perf-focused refactoring moves the TNode's input / output initialization
logic to the first template pass - close to the place where directives are
matched and resolved.
This code change makes it possible to update-mode checks for both property
bindings and listeners registration.
PR Close#32608
`templateUrls` that do not point to actual files are now diagnosed as such
by the Language Service. Support for `styleUrls` will come in a next PR.
This introduces a utility method `getPropertyValueOfType` that scans
TypeScript ASTs until a property assignment whose initializer of a
certain type is found. This PR also notices a couple of things that
could be improved in the language-service implementation, such as
enumerating directive properties and unifying common logic, that will be
fixed in future PRs.
Part of #32564.
PR Close#32586
In ngcc's reflection hosts for compiled JS bundles, such as ESM2015,
special care needs to be taken for classes as there may be an outer
declaration (referred to as "declaration") and an inner declaration
(referred to as "implementation") for a given class. Therefore, there
will also be two `ts.Symbol`s bound per class, and ngcc needs to switch
between those declarations and symbols depending on where certain
information can be found.
Prior to this commit, the `NgccReflectionHost` interface had methods
`getClassSymbol` and `findClassSymbols` that would return a `ts.Symbol`.
These class symbols would be used to kick off compilation of components
using ngtsc, so it is important for these symbols to correspond with the
publicly visible outer declaration of the class. However, the ESM2015
reflection host used to return the `ts.Symbol` for the inner
declaration, if the class was declared as follows:
```javascript
var MyClass = class MyClass {};
```
For the above code, `Esm2015ReflectionHost.getClassSymbol` would return
the `ts.Symbol` corresponding with the `class MyClass {}` declaration,
whereas it should have corresponded with the `var MyClass` declaration.
As a consequence, no `NgModule` could be resolved for the component, so
no components/directives would be in scope for the component. This
resulted in errors during runtime.
This commit resolves the issue by introducing a `NgccClassSymbol` that
contains references to both the outer and inner `ts.Symbol`, instead of
just a single `ts.Symbol`. This avoids the unclarity of whether a
`ts.Symbol` corresponds with the outer or inner declaration.
More details can be found here: https://hackmd.io/7nkgWOFWQlSRAuIW_8KPPwFixes#32078
Closes FW-1507
PR Close#32539