mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
refactor(compiler): integrate let declarations into the template pipeline (#56299)
These changes integrate let declarations into the template pipeline. This involves a few operations: * Producing a `declareLet` instruction call at creation time to initialize the declaration. * Producing a `storeLet` instruction call in the place of the let declaration, including the necessary `advance` calls beforehand. * For let declarations used within their declaration view, moving the `const` to be placed right after the `storeLet` call to ensure the their value has been computed. * For let declarations that are _only_ used in their declaration view, removing the `storeLet` call and inlining the expression into the constant statement. PR Close #56299
This commit is contained in:
parent
cc6cd08ca3
commit
bc655bf309
62 changed files with 2286 additions and 26 deletions
|
|
@ -104,6 +104,7 @@ export class PartialComponentLinkerVersion1<TStatement, TExpression>
|
|||
// Enable the new block syntax if compiled with v17 and
|
||||
// above, or when using the local placeholder version.
|
||||
const enableBlockSyntax = semver.major(version) >= 17 || version === PLACEHOLDER_VERSION;
|
||||
const enableLetSyntax = version === PLACEHOLDER_VERSION;
|
||||
|
||||
const template = parseTemplate(templateInfo.code, templateInfo.sourceUrl, {
|
||||
escapedString: templateInfo.isEscaped,
|
||||
|
|
@ -116,6 +117,7 @@ export class PartialComponentLinkerVersion1<TStatement, TExpression>
|
|||
// We normalize line endings if the template is was inline.
|
||||
i18nNormalizeLineEndingsInICUs: isInline,
|
||||
enableBlockSyntax,
|
||||
enableLetSyntax,
|
||||
});
|
||||
if (template.errors !== null) {
|
||||
const errors = template.errors.map((err) => err.toString()).join('\n');
|
||||
|
|
|
|||
|
|
@ -0,0 +1,717 @@
|
|||
/****************************************************************************************************
|
||||
* PARTIAL FILE: simple_let.js
|
||||
****************************************************************************************************/
|
||||
import { Component } from '@angular/core';
|
||||
import * as i0 from "@angular/core";
|
||||
export class MyApp {
|
||||
constructor() {
|
||||
this.value = 1;
|
||||
}
|
||||
}
|
||||
MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
||||
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, isStandalone: true, selector: "ng-component", ngImport: i0, template: `
|
||||
@let result = value * 2;
|
||||
The result is {{result}}
|
||||
`, isInline: true });
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, decorators: [{
|
||||
type: Component,
|
||||
args: [{
|
||||
template: `
|
||||
@let result = value * 2;
|
||||
The result is {{result}}
|
||||
`,
|
||||
standalone: true
|
||||
}]
|
||||
}] });
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: simple_let.d.ts
|
||||
****************************************************************************************************/
|
||||
import * as i0 from "@angular/core";
|
||||
export declare class MyApp {
|
||||
value: number;
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<MyApp, never>;
|
||||
static ɵcmp: i0.ɵɵComponentDeclaration<MyApp, "ng-component", never, {}, {}, never, never, true, never>;
|
||||
}
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: multiple_let.js
|
||||
****************************************************************************************************/
|
||||
import { Component } from '@angular/core';
|
||||
import * as i0 from "@angular/core";
|
||||
export class MyApp {
|
||||
constructor() {
|
||||
this.value = 1;
|
||||
}
|
||||
}
|
||||
MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
||||
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, isStandalone: true, selector: "ng-component", ngImport: i0, template: `
|
||||
@let one = value + 1;
|
||||
@let two = one + 1;
|
||||
@let result = two + 1;
|
||||
The result is {{result}}
|
||||
`, isInline: true });
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, decorators: [{
|
||||
type: Component,
|
||||
args: [{
|
||||
template: `
|
||||
@let one = value + 1;
|
||||
@let two = one + 1;
|
||||
@let result = two + 1;
|
||||
The result is {{result}}
|
||||
`,
|
||||
standalone: true
|
||||
}]
|
||||
}] });
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: multiple_let.d.ts
|
||||
****************************************************************************************************/
|
||||
import * as i0 from "@angular/core";
|
||||
export declare class MyApp {
|
||||
value: number;
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<MyApp, never>;
|
||||
static ɵcmp: i0.ɵɵComponentDeclaration<MyApp, "ng-component", never, {}, {}, never, never, true, never>;
|
||||
}
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: let_with_pipe.js
|
||||
****************************************************************************************************/
|
||||
import { Component, Pipe } from '@angular/core';
|
||||
import * as i0 from "@angular/core";
|
||||
export class DoublePipe {
|
||||
transform(value) {
|
||||
return value * 2;
|
||||
}
|
||||
}
|
||||
DoublePipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: DoublePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
||||
DoublePipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: DoublePipe, isStandalone: true, name: "double" });
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: DoublePipe, decorators: [{
|
||||
type: Pipe,
|
||||
args: [{
|
||||
name: 'double',
|
||||
standalone: true
|
||||
}]
|
||||
}] });
|
||||
export class MyApp {
|
||||
constructor() {
|
||||
this.value = 1;
|
||||
}
|
||||
}
|
||||
MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
||||
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, isStandalone: true, selector: "ng-component", ngImport: i0, template: `
|
||||
@let one = value + 1;
|
||||
@let result = one | double;
|
||||
The result is {{result}}
|
||||
`, isInline: true, dependencies: [{ kind: "pipe", type: DoublePipe, name: "double" }] });
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, decorators: [{
|
||||
type: Component,
|
||||
args: [{
|
||||
template: `
|
||||
@let one = value + 1;
|
||||
@let result = one | double;
|
||||
The result is {{result}}
|
||||
`,
|
||||
standalone: true,
|
||||
imports: [DoublePipe]
|
||||
}]
|
||||
}] });
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: let_with_pipe.d.ts
|
||||
****************************************************************************************************/
|
||||
import { PipeTransform } from '@angular/core';
|
||||
import * as i0 from "@angular/core";
|
||||
export declare class DoublePipe implements PipeTransform {
|
||||
transform(value: number): number;
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<DoublePipe, never>;
|
||||
static ɵpipe: i0.ɵɵPipeDeclaration<DoublePipe, "double", true>;
|
||||
}
|
||||
export declare class MyApp {
|
||||
value: number;
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<MyApp, never>;
|
||||
static ɵcmp: i0.ɵɵComponentDeclaration<MyApp, "ng-component", never, {}, {}, never, never, true, never>;
|
||||
}
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: let_in_listener.js
|
||||
****************************************************************************************************/
|
||||
import { Component } from '@angular/core';
|
||||
import * as i0 from "@angular/core";
|
||||
export class MyApp {
|
||||
constructor() {
|
||||
this.value = 1;
|
||||
}
|
||||
callback(one, two) {
|
||||
console.log(one, two);
|
||||
}
|
||||
}
|
||||
MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
||||
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, isStandalone: true, selector: "ng-component", ngImport: i0, template: `
|
||||
@let one = value + 1;
|
||||
@let two = one + 1;
|
||||
|
||||
<button (click)="callback(one, two)"></button>
|
||||
`, isInline: true });
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, decorators: [{
|
||||
type: Component,
|
||||
args: [{
|
||||
template: `
|
||||
@let one = value + 1;
|
||||
@let two = one + 1;
|
||||
|
||||
<button (click)="callback(one, two)"></button>
|
||||
`,
|
||||
standalone: true
|
||||
}]
|
||||
}] });
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: let_in_listener.d.ts
|
||||
****************************************************************************************************/
|
||||
import * as i0 from "@angular/core";
|
||||
export declare class MyApp {
|
||||
value: number;
|
||||
callback(one: number, two: number): void;
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<MyApp, never>;
|
||||
static ɵcmp: i0.ɵɵComponentDeclaration<MyApp, "ng-component", never, {}, {}, never, never, true, never>;
|
||||
}
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: let_in_child_view.js
|
||||
****************************************************************************************************/
|
||||
import { Component } from '@angular/core';
|
||||
import * as i0 from "@angular/core";
|
||||
export class MyApp {
|
||||
}
|
||||
MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
||||
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, isStandalone: true, selector: "ng-component", ngImport: i0, template: `
|
||||
@if (true) {
|
||||
@if (true) {
|
||||
@let three = two + 1;
|
||||
{{three}}
|
||||
}
|
||||
@let two = one + 1;
|
||||
}
|
||||
|
||||
@let one = 1;
|
||||
`, isInline: true });
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, decorators: [{
|
||||
type: Component,
|
||||
args: [{
|
||||
template: `
|
||||
@if (true) {
|
||||
@if (true) {
|
||||
@let three = two + 1;
|
||||
{{three}}
|
||||
}
|
||||
@let two = one + 1;
|
||||
}
|
||||
|
||||
@let one = 1;
|
||||
`,
|
||||
standalone: true
|
||||
}]
|
||||
}] });
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: let_in_child_view.d.ts
|
||||
****************************************************************************************************/
|
||||
import * as i0 from "@angular/core";
|
||||
export declare class MyApp {
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<MyApp, never>;
|
||||
static ɵcmp: i0.ɵɵComponentDeclaration<MyApp, "ng-component", never, {}, {}, never, never, true, never>;
|
||||
}
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: let_shared_with_child_view.js
|
||||
****************************************************************************************************/
|
||||
import { Component } from '@angular/core';
|
||||
import * as i0 from "@angular/core";
|
||||
export class MyApp {
|
||||
}
|
||||
MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
||||
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, isStandalone: true, selector: "ng-component", ngImport: i0, template: `
|
||||
@let value = 123;
|
||||
{{value}}
|
||||
<ng-template>{{value}}</ng-template>
|
||||
`, isInline: true });
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, decorators: [{
|
||||
type: Component,
|
||||
args: [{
|
||||
template: `
|
||||
@let value = 123;
|
||||
{{value}}
|
||||
<ng-template>{{value}}</ng-template>
|
||||
`,
|
||||
standalone: true
|
||||
}]
|
||||
}] });
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: let_shared_with_child_view.d.ts
|
||||
****************************************************************************************************/
|
||||
import * as i0 from "@angular/core";
|
||||
export declare class MyApp {
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<MyApp, never>;
|
||||
static ɵcmp: i0.ɵɵComponentDeclaration<MyApp, "ng-component", never, {}, {}, never, never, true, never>;
|
||||
}
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: let_in_child_view_listener.js
|
||||
****************************************************************************************************/
|
||||
import { Component } from '@angular/core';
|
||||
import * as i0 from "@angular/core";
|
||||
export class MyApp {
|
||||
constructor() {
|
||||
this.value = 1;
|
||||
}
|
||||
callback(one, two, three, four) {
|
||||
console.log(one, two, three, four);
|
||||
}
|
||||
}
|
||||
MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
||||
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, isStandalone: true, selector: "ng-component", ngImport: i0, template: `
|
||||
@let one = value + 1;
|
||||
|
||||
<ng-template>
|
||||
@let two = one + 1;
|
||||
|
||||
@if (true) {
|
||||
@let three = two + 1;
|
||||
|
||||
@switch (1) {
|
||||
@case (1) {
|
||||
@let four = three + 1;
|
||||
<button (click)="callback(one, two, three, four)"></button>
|
||||
}
|
||||
}
|
||||
}
|
||||
</ng-template>
|
||||
`, isInline: true });
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, decorators: [{
|
||||
type: Component,
|
||||
args: [{
|
||||
template: `
|
||||
@let one = value + 1;
|
||||
|
||||
<ng-template>
|
||||
@let two = one + 1;
|
||||
|
||||
@if (true) {
|
||||
@let three = two + 1;
|
||||
|
||||
@switch (1) {
|
||||
@case (1) {
|
||||
@let four = three + 1;
|
||||
<button (click)="callback(one, two, three, four)"></button>
|
||||
}
|
||||
}
|
||||
}
|
||||
</ng-template>
|
||||
`,
|
||||
standalone: true
|
||||
}]
|
||||
}] });
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: let_in_child_view_listener.d.ts
|
||||
****************************************************************************************************/
|
||||
import * as i0 from "@angular/core";
|
||||
export declare class MyApp {
|
||||
value: number;
|
||||
callback(one: number, two: number, three: number, four: number): void;
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<MyApp, never>;
|
||||
static ɵcmp: i0.ɵɵComponentDeclaration<MyApp, "ng-component", never, {}, {}, never, never, true, never>;
|
||||
}
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: let_local_refs.js
|
||||
****************************************************************************************************/
|
||||
import { Component } from '@angular/core';
|
||||
import * as i0 from "@angular/core";
|
||||
export class MyApp {
|
||||
}
|
||||
MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
||||
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, isStandalone: true, selector: "ng-component", ngImport: i0, template: `
|
||||
<input #name>
|
||||
<input #lastName>
|
||||
|
||||
@let fullName = name.value + ' ' + lastName.value;
|
||||
Hello, {{fullName}}
|
||||
`, isInline: true });
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, decorators: [{
|
||||
type: Component,
|
||||
args: [{
|
||||
template: `
|
||||
<input #name>
|
||||
<input #lastName>
|
||||
|
||||
@let fullName = name.value + ' ' + lastName.value;
|
||||
Hello, {{fullName}}
|
||||
`,
|
||||
standalone: true
|
||||
}]
|
||||
}] });
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: let_local_refs.d.ts
|
||||
****************************************************************************************************/
|
||||
import * as i0 from "@angular/core";
|
||||
export declare class MyApp {
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<MyApp, never>;
|
||||
static ɵcmp: i0.ɵɵComponentDeclaration<MyApp, "ng-component", never, {}, {}, never, never, true, never>;
|
||||
}
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: let_local_forward_refs.js
|
||||
****************************************************************************************************/
|
||||
import { Component } from '@angular/core';
|
||||
import * as i0 from "@angular/core";
|
||||
export class MyApp {
|
||||
}
|
||||
MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
||||
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, isStandalone: true, selector: "ng-component", ngImport: i0, template: `
|
||||
@let message = 'Hello, ' + name.value;
|
||||
{{message}}
|
||||
<input #name>
|
||||
`, isInline: true });
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, decorators: [{
|
||||
type: Component,
|
||||
args: [{
|
||||
template: `
|
||||
@let message = 'Hello, ' + name.value;
|
||||
{{message}}
|
||||
<input #name>
|
||||
`,
|
||||
standalone: true
|
||||
}]
|
||||
}] });
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: let_local_forward_refs.d.ts
|
||||
****************************************************************************************************/
|
||||
import * as i0 from "@angular/core";
|
||||
export declare class MyApp {
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<MyApp, never>;
|
||||
static ɵcmp: i0.ɵɵComponentDeclaration<MyApp, "ng-component", never, {}, {}, never, never, true, never>;
|
||||
}
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: let_for_loop.js
|
||||
****************************************************************************************************/
|
||||
import { Component } from '@angular/core';
|
||||
import * as i0 from "@angular/core";
|
||||
export class MyApp {
|
||||
constructor() {
|
||||
this.items = [];
|
||||
}
|
||||
}
|
||||
MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
||||
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, isStandalone: true, selector: "ng-component", ngImport: i0, template: `
|
||||
@for (item of items; track item) {
|
||||
@let outerFirst = $first;
|
||||
|
||||
@for (subitem of item.children; track subitem) {
|
||||
@let innerFirst = $first;
|
||||
|
||||
{{outerFirst || innerFirst}}
|
||||
}
|
||||
}
|
||||
`, isInline: true });
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, decorators: [{
|
||||
type: Component,
|
||||
args: [{
|
||||
template: `
|
||||
@for (item of items; track item) {
|
||||
@let outerFirst = $first;
|
||||
|
||||
@for (subitem of item.children; track subitem) {
|
||||
@let innerFirst = $first;
|
||||
|
||||
{{outerFirst || innerFirst}}
|
||||
}
|
||||
}
|
||||
`,
|
||||
standalone: true
|
||||
}]
|
||||
}] });
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: let_for_loop.d.ts
|
||||
****************************************************************************************************/
|
||||
import * as i0 from "@angular/core";
|
||||
export declare class MyApp {
|
||||
items: {
|
||||
children: any[];
|
||||
}[];
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<MyApp, never>;
|
||||
static ɵcmp: i0.ɵɵComponentDeclaration<MyApp, "ng-component", never, {}, {}, never, never, true, never>;
|
||||
}
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: let_invalid_forward_ref.js
|
||||
****************************************************************************************************/
|
||||
import { Component } from '@angular/core';
|
||||
import * as i0 from "@angular/core";
|
||||
export class MyApp {
|
||||
constructor() {
|
||||
this.value = 1;
|
||||
}
|
||||
}
|
||||
MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
||||
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, isStandalone: true, selector: "ng-component", ngImport: i0, template: `
|
||||
<ng-template>
|
||||
{{result}}
|
||||
@let result = value * 2;
|
||||
</ng-template>
|
||||
`, isInline: true });
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, decorators: [{
|
||||
type: Component,
|
||||
args: [{
|
||||
template: `
|
||||
<ng-template>
|
||||
{{result}}
|
||||
@let result = value * 2;
|
||||
</ng-template>
|
||||
`,
|
||||
standalone: true
|
||||
}]
|
||||
}] });
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: let_invalid_forward_ref.d.ts
|
||||
****************************************************************************************************/
|
||||
import * as i0 from "@angular/core";
|
||||
export declare class MyApp {
|
||||
value: number;
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<MyApp, never>;
|
||||
static ɵcmp: i0.ɵɵComponentDeclaration<MyApp, "ng-component", never, {}, {}, never, never, true, never>;
|
||||
}
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: let_single_optimization.js
|
||||
****************************************************************************************************/
|
||||
import { Component } from '@angular/core';
|
||||
import * as i0 from "@angular/core";
|
||||
export class MyApp {
|
||||
constructor() {
|
||||
this.value = 0;
|
||||
}
|
||||
}
|
||||
MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
||||
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, isStandalone: true, selector: "ng-component", ngImport: i0, template: `
|
||||
{{value}}
|
||||
@let result = value * 2;
|
||||
{{value}}
|
||||
`, isInline: true });
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, decorators: [{
|
||||
type: Component,
|
||||
args: [{
|
||||
template: `
|
||||
{{value}}
|
||||
@let result = value * 2;
|
||||
{{value}}
|
||||
`,
|
||||
standalone: true
|
||||
}]
|
||||
}] });
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: let_single_optimization.d.ts
|
||||
****************************************************************************************************/
|
||||
import * as i0 from "@angular/core";
|
||||
export declare class MyApp {
|
||||
value: number;
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<MyApp, never>;
|
||||
static ɵcmp: i0.ɵɵComponentDeclaration<MyApp, "ng-component", never, {}, {}, never, never, true, never>;
|
||||
}
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: let_multiple_optimization.js
|
||||
****************************************************************************************************/
|
||||
import { Component } from '@angular/core';
|
||||
import * as i0 from "@angular/core";
|
||||
export class MyApp {
|
||||
constructor() {
|
||||
this.value = 0;
|
||||
}
|
||||
}
|
||||
MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
||||
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, isStandalone: true, selector: "ng-component", ngImport: i0, template: `
|
||||
{{value}}
|
||||
@let one = value + 1;
|
||||
@let two = one + 1;
|
||||
@let three = two + 1;
|
||||
@let four = three + 1;
|
||||
{{value}}
|
||||
`, isInline: true });
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, decorators: [{
|
||||
type: Component,
|
||||
args: [{
|
||||
template: `
|
||||
{{value}}
|
||||
@let one = value + 1;
|
||||
@let two = one + 1;
|
||||
@let three = two + 1;
|
||||
@let four = three + 1;
|
||||
{{value}}
|
||||
`,
|
||||
standalone: true
|
||||
}]
|
||||
}] });
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: let_multiple_optimization.d.ts
|
||||
****************************************************************************************************/
|
||||
import * as i0 from "@angular/core";
|
||||
export declare class MyApp {
|
||||
value: number;
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<MyApp, never>;
|
||||
static ɵcmp: i0.ɵɵComponentDeclaration<MyApp, "ng-component", never, {}, {}, never, never, true, never>;
|
||||
}
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: let_partial_optimization.js
|
||||
****************************************************************************************************/
|
||||
import { Component } from '@angular/core';
|
||||
import * as i0 from "@angular/core";
|
||||
export class MyApp {
|
||||
constructor() {
|
||||
this.value = 0;
|
||||
}
|
||||
}
|
||||
MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
||||
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, isStandalone: true, selector: "ng-component", ngImport: i0, template: `
|
||||
{{value}}
|
||||
@let one = value + 1;
|
||||
@let two = one + 1;
|
||||
@let three = two + 1;
|
||||
@let four = three + 1;
|
||||
{{two}}
|
||||
`, isInline: true });
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, decorators: [{
|
||||
type: Component,
|
||||
args: [{
|
||||
template: `
|
||||
{{value}}
|
||||
@let one = value + 1;
|
||||
@let two = one + 1;
|
||||
@let three = two + 1;
|
||||
@let four = three + 1;
|
||||
{{two}}
|
||||
`,
|
||||
standalone: true
|
||||
}]
|
||||
}] });
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: let_partial_optimization.d.ts
|
||||
****************************************************************************************************/
|
||||
import * as i0 from "@angular/core";
|
||||
export declare class MyApp {
|
||||
value: number;
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<MyApp, never>;
|
||||
static ɵcmp: i0.ɵɵComponentDeclaration<MyApp, "ng-component", never, {}, {}, never, never, true, never>;
|
||||
}
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: let_optimization_listener.js
|
||||
****************************************************************************************************/
|
||||
import { Component } from '@angular/core';
|
||||
import * as i0 from "@angular/core";
|
||||
export class MyApp {
|
||||
constructor() {
|
||||
this.value = 0;
|
||||
}
|
||||
callback(value) {
|
||||
console.log(value);
|
||||
}
|
||||
}
|
||||
MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
||||
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, isStandalone: true, selector: "ng-component", ngImport: i0, template: `
|
||||
{{value}}
|
||||
@let one = value + 1;
|
||||
@let two = one + 1;
|
||||
@let three = two + 1;
|
||||
@let four = three + 1;
|
||||
{{value}}
|
||||
<button (click)="callback(three)"></button>
|
||||
`, isInline: true });
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, decorators: [{
|
||||
type: Component,
|
||||
args: [{
|
||||
template: `
|
||||
{{value}}
|
||||
@let one = value + 1;
|
||||
@let two = one + 1;
|
||||
@let three = two + 1;
|
||||
@let four = three + 1;
|
||||
{{value}}
|
||||
<button (click)="callback(three)"></button>
|
||||
`,
|
||||
standalone: true
|
||||
}]
|
||||
}] });
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: let_optimization_listener.d.ts
|
||||
****************************************************************************************************/
|
||||
import * as i0 from "@angular/core";
|
||||
export declare class MyApp {
|
||||
value: number;
|
||||
callback(value: number): void;
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<MyApp, never>;
|
||||
static ɵcmp: i0.ɵɵComponentDeclaration<MyApp, "ng-component", never, {}, {}, never, never, true, never>;
|
||||
}
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: let_optimization_child_view.js
|
||||
****************************************************************************************************/
|
||||
import { Component } from '@angular/core';
|
||||
import * as i0 from "@angular/core";
|
||||
export class MyApp {
|
||||
constructor() {
|
||||
this.value = 0;
|
||||
}
|
||||
}
|
||||
MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
||||
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, isStandalone: true, selector: "ng-component", ngImport: i0, template: `
|
||||
{{value}}
|
||||
@let one = value + 1;
|
||||
@let two = one + 1;
|
||||
@let three = two + 1;
|
||||
@let four = three + 1;
|
||||
{{value}}
|
||||
@if (true) {
|
||||
{{three}}
|
||||
}
|
||||
`, isInline: true });
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, decorators: [{
|
||||
type: Component,
|
||||
args: [{
|
||||
template: `
|
||||
{{value}}
|
||||
@let one = value + 1;
|
||||
@let two = one + 1;
|
||||
@let three = two + 1;
|
||||
@let four = three + 1;
|
||||
{{value}}
|
||||
@if (true) {
|
||||
{{three}}
|
||||
}
|
||||
`,
|
||||
standalone: true
|
||||
}]
|
||||
}] });
|
||||
|
||||
/****************************************************************************************************
|
||||
* PARTIAL FILE: let_optimization_child_view.d.ts
|
||||
****************************************************************************************************/
|
||||
import * as i0 from "@angular/core";
|
||||
export declare class MyApp {
|
||||
value: number;
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<MyApp, never>;
|
||||
static ɵcmp: i0.ɵɵComponentDeclaration<MyApp, "ng-component", never, {}, {}, never, never, true, never>;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,326 @@
|
|||
{
|
||||
"$schema": "../test_case_schema.json",
|
||||
"cases": [
|
||||
{
|
||||
"description": "should create a simple @let declaration",
|
||||
"angularCompilerOptions": {
|
||||
"_enableLetSyntax": true
|
||||
},
|
||||
"inputFiles": [
|
||||
"simple_let.ts"
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"expected": "simple_let_template.js",
|
||||
"generated": "simple_let.js"
|
||||
}
|
||||
],
|
||||
"failureMessage": "Incorrect template"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "should create multiple @let declarations that depend on each other",
|
||||
"angularCompilerOptions": {
|
||||
"_enableLetSyntax": true
|
||||
},
|
||||
"inputFiles": [
|
||||
"multiple_let.ts"
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"expected": "multiple_let_template.js",
|
||||
"generated": "multiple_let.js"
|
||||
}
|
||||
],
|
||||
"failureMessage": "Incorrect template"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "should create a let using a pipe",
|
||||
"angularCompilerOptions": {
|
||||
"_enableLetSyntax": true
|
||||
},
|
||||
"inputFiles": [
|
||||
"let_with_pipe.ts"
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"expected": "let_with_pipe_template.js",
|
||||
"generated": "let_with_pipe.js"
|
||||
}
|
||||
],
|
||||
"failureMessage": "Incorrect template"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "should be able to use let declarations in event listeners",
|
||||
"angularCompilerOptions": {
|
||||
"_enableLetSyntax": true
|
||||
},
|
||||
"inputFiles": [
|
||||
"let_in_listener.ts"
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"expected": "let_in_listener_template.js",
|
||||
"generated": "let_in_listener.js"
|
||||
}
|
||||
],
|
||||
"failureMessage": "Incorrect template"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "should be able to use let declarations in child views",
|
||||
"angularCompilerOptions": {
|
||||
"_enableLetSyntax": true
|
||||
},
|
||||
"inputFiles": [
|
||||
"let_in_child_view.ts"
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"expected": "let_in_child_view_template.js",
|
||||
"generated": "let_in_child_view.js"
|
||||
}
|
||||
],
|
||||
"failureMessage": "Incorrect template"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "should share let declarations between parent and child views",
|
||||
"angularCompilerOptions": {
|
||||
"_enableLetSyntax": true
|
||||
},
|
||||
"inputFiles": [
|
||||
"let_shared_with_child_view.ts"
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"expected": "let_shared_with_child_view_template.js",
|
||||
"generated": "let_shared_with_child_view.js"
|
||||
}
|
||||
],
|
||||
"failureMessage": "Incorrect template"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "should be able to use let declarations in event listeners inside child views",
|
||||
"angularCompilerOptions": {
|
||||
"_enableLetSyntax": true
|
||||
},
|
||||
"inputFiles": [
|
||||
"let_in_child_view_listener.ts"
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"expected": "let_in_child_view_listener_template.js",
|
||||
"generated": "let_in_child_view_listener.js"
|
||||
}
|
||||
],
|
||||
"failureMessage": "Incorrect template"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "should be able to use local references in let declarations",
|
||||
"angularCompilerOptions": {
|
||||
"_enableLetSyntax": true
|
||||
},
|
||||
"inputFiles": [
|
||||
"let_local_refs.ts"
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"expected": "let_local_refs_template.js",
|
||||
"generated": "let_local_refs.js"
|
||||
}
|
||||
],
|
||||
"failureMessage": "Incorrect template"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "should be able to use forward references defined after the let declaration",
|
||||
"angularCompilerOptions": {
|
||||
"_enableLetSyntax": true
|
||||
},
|
||||
"inputFiles": [
|
||||
"let_local_forward_refs.ts"
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"expected": "let_local_forward_refs_template.js",
|
||||
"generated": "let_local_forward_refs.js"
|
||||
}
|
||||
],
|
||||
"failureMessage": "Incorrect template"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "should be able to use for loop variables in let declarations",
|
||||
"angularCompilerOptions": {
|
||||
"_enableLetSyntax": true
|
||||
},
|
||||
"inputFiles": [
|
||||
"let_for_loop.ts"
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"expected": "let_for_loop_template.js",
|
||||
"generated": "let_for_loop.js"
|
||||
}
|
||||
],
|
||||
"failureMessage": "Incorrect template"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "should transform an invalid let reference to undefined",
|
||||
"angularCompilerOptions": {
|
||||
"_enableLetSyntax": true,
|
||||
"checkTemplateBodies": false
|
||||
},
|
||||
"inputFiles": [
|
||||
"let_invalid_forward_ref.ts"
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"expected": "let_invalid_forward_ref_template.js",
|
||||
"generated": "let_invalid_forward_ref.js"
|
||||
}
|
||||
],
|
||||
"failureMessage": "Incorrect template"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "should remove a single unused let declaration",
|
||||
"angularCompilerOptions": {
|
||||
"_enableLetSyntax": true
|
||||
},
|
||||
"inputFiles": [
|
||||
"let_single_optimization.ts"
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"expected": "let_single_optimization_template.js",
|
||||
"generated": "let_single_optimization.js"
|
||||
}
|
||||
],
|
||||
"failureMessage": "Incorrect template"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "should remove a chain of unused let declarations",
|
||||
"angularCompilerOptions": {
|
||||
"_enableLetSyntax": true
|
||||
},
|
||||
"inputFiles": [
|
||||
"let_multiple_optimization.ts"
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"expected": "let_multiple_optimization_template.js",
|
||||
"generated": "let_multiple_optimization.js"
|
||||
}
|
||||
],
|
||||
"failureMessage": "Incorrect template"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "should remove only the unused let declarations from the middle of a chain of declarations",
|
||||
"angularCompilerOptions": {
|
||||
"_enableLetSyntax": true
|
||||
},
|
||||
"inputFiles": [
|
||||
"let_partial_optimization.ts"
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"expected": "let_partial_optimization_template.js",
|
||||
"generated": "let_partial_optimization.js"
|
||||
}
|
||||
],
|
||||
"failureMessage": "Incorrect template"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "should not remove let declarations that are only used in an event listener",
|
||||
"angularCompilerOptions": {
|
||||
"_enableLetSyntax": true
|
||||
},
|
||||
"inputFiles": [
|
||||
"let_optimization_listener.ts"
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"expected": "let_optimization_listener_template.js",
|
||||
"generated": "let_optimization_listener.js"
|
||||
}
|
||||
],
|
||||
"failureMessage": "Incorrect template"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "should not remove let declarations that are only used in a child view",
|
||||
"angularCompilerOptions": {
|
||||
"_enableLetSyntax": true
|
||||
},
|
||||
"inputFiles": [
|
||||
"let_optimization_child_view.ts"
|
||||
],
|
||||
"expectations": [
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"expected": "let_optimization_child_view_template.js",
|
||||
"generated": "let_optimization_child_view.js"
|
||||
}
|
||||
],
|
||||
"failureMessage": "Incorrect template"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
@for (item of items; track item) {
|
||||
@let outerFirst = $first;
|
||||
|
||||
@for (subitem of item.children; track subitem) {
|
||||
@let innerFirst = $first;
|
||||
|
||||
{{outerFirst || innerFirst}}
|
||||
}
|
||||
}
|
||||
`,
|
||||
standalone: true,
|
||||
})
|
||||
export class MyApp {
|
||||
items: {children: any[]}[] = [];
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
function MyApp_For_1_For_2_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵdeclareLet(0);
|
||||
$r3$.ɵɵtext(1);
|
||||
}
|
||||
if (rf & 2) {
|
||||
const ɵ$index_3_r1 = ctx.$index;
|
||||
$r3$.ɵɵnextContext();
|
||||
const $outerFirst_1$ = $r3$.ɵɵreadContextLet(0);
|
||||
const $innerFirst_2$ = ɵ$index_3_r1 === 0;
|
||||
$r3$.ɵɵadvance();
|
||||
$r3$.ɵɵtextInterpolate1(" ", $outerFirst_1$ || $innerFirst_2$, " ");
|
||||
}
|
||||
}
|
||||
|
||||
…
|
||||
|
||||
function MyApp_For_1_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵdeclareLet(0);
|
||||
$r3$.ɵɵrepeaterCreate(1, MyApp_For_1_For_2_Template, 2, 1, null, null, $r3$.ɵɵrepeaterTrackByIdentity);
|
||||
}
|
||||
if (rf & 2) {
|
||||
const $item_r4$ = ctx.$implicit;
|
||||
const ɵ$index_1_r5 = ctx.$index;
|
||||
$r3$.ɵɵstoreLet(ɵ$index_1_r5 === 0);
|
||||
$r3$.ɵɵadvance();
|
||||
$r3$.ɵɵrepeater($item_r4$.children);
|
||||
}
|
||||
}
|
||||
|
||||
…
|
||||
|
||||
$r3$.ɵɵdefineComponent({
|
||||
…
|
||||
decls: 2,
|
||||
vars: 0,
|
||||
template: function MyApp_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵrepeaterCreate(0, MyApp_For_1_Template, 3, 1, null, null, $r3$.ɵɵrepeaterTrackByIdentity);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵrepeater(ctx.items);
|
||||
}
|
||||
},
|
||||
…
|
||||
});
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
@if (true) {
|
||||
@if (true) {
|
||||
@let three = two + 1;
|
||||
{{three}}
|
||||
}
|
||||
@let two = one + 1;
|
||||
}
|
||||
|
||||
@let one = 1;
|
||||
`,
|
||||
standalone: true,
|
||||
})
|
||||
export class MyApp {}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
@let one = value + 1;
|
||||
|
||||
<ng-template>
|
||||
@let two = one + 1;
|
||||
|
||||
@if (true) {
|
||||
@let three = two + 1;
|
||||
|
||||
@switch (1) {
|
||||
@case (1) {
|
||||
@let four = three + 1;
|
||||
<button (click)="callback(one, two, three, four)"></button>
|
||||
}
|
||||
}
|
||||
}
|
||||
</ng-template>
|
||||
`,
|
||||
standalone: true,
|
||||
})
|
||||
export class MyApp {
|
||||
value = 1;
|
||||
|
||||
callback(one: number, two: number, three: number, four: number) {
|
||||
console.log(one, two, three, four);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
function MyApp_ng_template_1_Conditional_1_Case_1_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
const $_r1$ = $r3$.ɵɵgetCurrentView();
|
||||
$r3$.ɵɵdeclareLet(0);
|
||||
$r3$.ɵɵelementStart(1, "button", 0);
|
||||
$r3$.ɵɵlistener(
|
||||
"click",
|
||||
function MyApp_ng_template_1_Conditional_1_Case_1_Template_button_click_1_listener() {
|
||||
$r3$.ɵɵrestoreView($_r1$);
|
||||
const $four_1$ = $r3$.ɵɵreadContextLet(0);
|
||||
$r3$.ɵɵnextContext();
|
||||
const $three_2$ = $r3$.ɵɵreadContextLet(0);
|
||||
$r3$.ɵɵnextContext();
|
||||
const $two_3$ = $r3$.ɵɵreadContextLet(0);
|
||||
const $ctx_r4$ = $r3$.ɵɵnextContext();
|
||||
const $one_5$ = $r3$.ɵɵreadContextLet(0);
|
||||
return $r3$.ɵɵresetView($ctx_r4$.callback($one_5$, $two_3$, $three_2$, $four_1$));
|
||||
}
|
||||
);
|
||||
$r3$.ɵɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵnextContext();
|
||||
const $three_2$ = $r3$.ɵɵreadContextLet(0);
|
||||
$r3$.ɵɵstoreLet($three_2$ + 1);
|
||||
}
|
||||
}
|
||||
|
||||
…
|
||||
|
||||
function MyApp_ng_template_1_Conditional_1_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵdeclareLet(0);
|
||||
$r3$.ɵɵtemplate(1, MyApp_ng_template_1_Conditional_1_Case_1_Template, 2, 1, "button");
|
||||
}
|
||||
if (rf & 2) {
|
||||
let $tmp_5_0$;
|
||||
$r3$.ɵɵnextContext();
|
||||
const $two_3$ = $r3$.ɵɵreadContextLet(0);
|
||||
$r3$.ɵɵstoreLet($two_3$ + 1);
|
||||
$r3$.ɵɵadvance();
|
||||
$r3$.ɵɵconditional(($tmp_5_0$ = 1) === 1 ? 1 : -1);
|
||||
}
|
||||
}
|
||||
|
||||
…
|
||||
|
||||
function MyApp_ng_template_1_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵdeclareLet(0);
|
||||
$r3$.ɵɵtemplate(1, MyApp_ng_template_1_Conditional_1_Template, 2, 2);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵnextContext();
|
||||
const $one_5$ = $r3$.ɵɵreadContextLet(0);
|
||||
$r3$.ɵɵstoreLet($one_5$ + 1);
|
||||
$r3$.ɵɵadvance();
|
||||
$r3$.ɵɵconditional(true ? 1 : -1);
|
||||
}
|
||||
}
|
||||
|
||||
…
|
||||
|
||||
$r3$.ɵɵdefineComponent({
|
||||
…
|
||||
decls: 2,
|
||||
vars: 1,
|
||||
…
|
||||
template: function MyApp_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵdeclareLet(0);
|
||||
$r3$.ɵɵtemplate(1, MyApp_ng_template_1_Template, 2, 2, "ng-template");
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵstoreLet(ctx.value + 1);
|
||||
}
|
||||
},
|
||||
…
|
||||
});
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
function MyApp_Conditional_0_Conditional_0_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵdeclareLet(0);
|
||||
$r3$.ɵɵtext(1);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵnextContext();
|
||||
const $two_0$ = $r3$.ɵɵreadContextLet(1);
|
||||
const $three_1$ = $two_0$ + 1;
|
||||
$r3$.ɵɵadvance();
|
||||
$r3$.ɵɵtextInterpolate1(" ", $three_1$, " ");
|
||||
}
|
||||
}
|
||||
|
||||
…
|
||||
|
||||
function MyApp_Conditional_0_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵtemplate(0, MyApp_Conditional_0_Conditional_0_Template, 2, 1);
|
||||
$r3$.ɵɵdeclareLet(1);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵnextContext();
|
||||
const $one_2$ = $r3$.ɵɵreadContextLet(1);
|
||||
$r3$.ɵɵconditional(true ? 0 : -1);
|
||||
$r3$.ɵɵadvance();
|
||||
$r3$.ɵɵstoreLet($one_2$ + 1);
|
||||
}
|
||||
}
|
||||
|
||||
…
|
||||
|
||||
$r3$.ɵɵdefineComponent({
|
||||
…
|
||||
decls: 2,
|
||||
vars: 2,
|
||||
template: function MyApp_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵtemplate(0, MyApp_Conditional_0_Template, 2, 2);
|
||||
$r3$.ɵɵdeclareLet(1);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵconditional(true ? 0 : -1);
|
||||
$r3$.ɵɵadvance();
|
||||
$r3$.ɵɵstoreLet(1);
|
||||
}
|
||||
},
|
||||
…
|
||||
});
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
@let one = value + 1;
|
||||
@let two = one + 1;
|
||||
|
||||
<button (click)="callback(one, two)"></button>
|
||||
`,
|
||||
standalone: true,
|
||||
})
|
||||
export class MyApp {
|
||||
value = 1;
|
||||
|
||||
callback(one: number, two: number) {
|
||||
console.log(one, two);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
$r3$.ɵɵdefineComponent({
|
||||
…
|
||||
decls: 3,
|
||||
vars: 2,
|
||||
…
|
||||
template: function MyApp_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
const $_r1$ = $r3$.ɵɵgetCurrentView();
|
||||
$r3$.ɵɵdeclareLet(0)(1);
|
||||
$r3$.ɵɵelementStart(2, "button", 0);
|
||||
$r3$.ɵɵlistener("click", function MyApp_Template_button_click_2_listener() {
|
||||
$r3$.ɵɵrestoreView($_r1$);
|
||||
const $one_1$ = $r3$.ɵɵreadContextLet(0);
|
||||
const $two_2$ = $r3$.ɵɵreadContextLet(1);
|
||||
return $r3$.ɵɵresetView(ctx.callback($one_1$, $two_2$));
|
||||
});
|
||||
$r3$.ɵɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
const $one_r1$ = $r3$.ɵɵstoreLet(ctx.value + 1);
|
||||
$r3$.ɵɵadvance();
|
||||
$r3$.ɵɵstoreLet($one_r1$ + 1);
|
||||
}
|
||||
},
|
||||
…
|
||||
});
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<ng-template>
|
||||
{{result}}
|
||||
@let result = value * 2;
|
||||
</ng-template>
|
||||
`,
|
||||
standalone: true,
|
||||
})
|
||||
export class MyApp {
|
||||
value = 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
function MyApp_ng_template_0_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵtext(0);
|
||||
$r3$.ɵɵdeclareLet(1);
|
||||
}
|
||||
if (rf & 2) {
|
||||
const $ctx_r0$ = $r3$.ɵɵnextContext();
|
||||
$r3$.ɵɵtextInterpolate1(" ", undefined, " ");
|
||||
$ctx_r0$.value * 2;
|
||||
}
|
||||
}
|
||||
|
||||
…
|
||||
|
||||
function MyApp_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵtemplate(0, MyApp_ng_template_0_Template, 2, 1, "ng-template");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
@let message = 'Hello, ' + name.value;
|
||||
{{message}}
|
||||
<input #name>
|
||||
`,
|
||||
standalone: true,
|
||||
})
|
||||
export class MyApp {}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
$r3$.ɵɵdefineComponent({
|
||||
…
|
||||
decls: 4,
|
||||
vars: 1,
|
||||
…
|
||||
template: function MyApp_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵdeclareLet(0);
|
||||
$r3$.ɵɵtext(1);
|
||||
$r3$.ɵɵelement(2, "input", null, 0);
|
||||
}
|
||||
if (rf & 2) {
|
||||
const $name_r1$ = $r3$.ɵɵreference(3);
|
||||
const $message_1$ = "Hello, " + $name_r1$.value;
|
||||
$r3$.ɵɵadvance();
|
||||
$r3$.ɵɵtextInterpolate1(" ", $message_1$, " ");
|
||||
}
|
||||
},
|
||||
…
|
||||
});
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<input #name>
|
||||
<input #lastName>
|
||||
|
||||
@let fullName = name.value + ' ' + lastName.value;
|
||||
Hello, {{fullName}}
|
||||
`,
|
||||
standalone: true,
|
||||
})
|
||||
export class MyApp {}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
$r3$.ɵɵdefineComponent({
|
||||
…
|
||||
decls: 6,
|
||||
vars: 1,
|
||||
…
|
||||
template: function MyApp_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵelement(0, "input", null, 0)(2, "input", null, 1);
|
||||
$r3$.ɵɵdeclareLet(4);
|
||||
$r3$.ɵɵtext(5);
|
||||
}
|
||||
if (rf & 2) {
|
||||
const $name_r1$ = $r3$.ɵɵreference(1);
|
||||
const $lastName_r2$ = $r3$.ɵɵreference(3);
|
||||
const $fullName_2$ = $name_r1$.value + " " + $lastName_r2$.value;
|
||||
$r3$.ɵɵadvance(5);
|
||||
$r3$.ɵɵtextInterpolate1(" Hello, ", $fullName_2$, " ");
|
||||
}
|
||||
},
|
||||
…
|
||||
});
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
{{value}}
|
||||
@let one = value + 1;
|
||||
@let two = one + 1;
|
||||
@let three = two + 1;
|
||||
@let four = three + 1;
|
||||
{{value}}
|
||||
`,
|
||||
standalone: true,
|
||||
})
|
||||
export class MyApp {
|
||||
value = 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
$r3$.ɵɵdefineComponent({
|
||||
…
|
||||
decls: 6,
|
||||
vars: 2,
|
||||
template: function MyApp_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵtext(0);
|
||||
$r3$.ɵɵdeclareLet(1)(2)(3)(4);
|
||||
$r3$.ɵɵtext(5);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵtextInterpolate1(" ", ctx.value, " ");
|
||||
const $one_1$ = ctx.value + 1;
|
||||
const $two_2$ = $one_1$ + 1;
|
||||
const $three_3$ = $two_2$ + 1;
|
||||
$three_3$ + 1;
|
||||
$r3$.ɵɵadvance(5);
|
||||
$r3$.ɵɵtextInterpolate1(" ", ctx.value, " ");
|
||||
}
|
||||
},
|
||||
…
|
||||
});
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
{{value}}
|
||||
@let one = value + 1;
|
||||
@let two = one + 1;
|
||||
@let three = two + 1;
|
||||
@let four = three + 1;
|
||||
{{value}}
|
||||
@if (true) {
|
||||
{{three}}
|
||||
}
|
||||
`,
|
||||
standalone: true,
|
||||
})
|
||||
export class MyApp {
|
||||
value = 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
function MyApp_Conditional_6_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵtext(0);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵnextContext();
|
||||
const $three_0$ = $r3$.ɵɵreadContextLet(3);
|
||||
$r3$.ɵɵtextInterpolate1(" ", $three_0$, " ");
|
||||
}
|
||||
}
|
||||
|
||||
…
|
||||
|
||||
$r3$.ɵɵdefineComponent({
|
||||
…
|
||||
decls: 7,
|
||||
vars: 4,
|
||||
template: function MyApp_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵtext(0);
|
||||
$r3$.ɵɵdeclareLet(1)(2)(3)(4);
|
||||
$r3$.ɵɵtext(5);
|
||||
$r3$.ɵɵtemplate(6, MyApp_Conditional_6_Template, 1, 1);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵtextInterpolate1(" ", ctx.value, " ");
|
||||
const $one_1$ = ctx.value + 1;
|
||||
const $two_2$ = $one_1$ + 1;
|
||||
$r3$.ɵɵadvance(3);
|
||||
const $three_3$ = i0.ɵɵstoreLet($two_2$ + 1);
|
||||
$three_3$ + 1;
|
||||
$r3$.ɵɵadvance(2);
|
||||
$r3$.ɵɵtextInterpolate1(" ", ctx.value, " ");
|
||||
$r3$.ɵɵadvance();
|
||||
$r3$.ɵɵconditional(true ? 6 : -1);
|
||||
}
|
||||
},
|
||||
…
|
||||
});
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
{{value}}
|
||||
@let one = value + 1;
|
||||
@let two = one + 1;
|
||||
@let three = two + 1;
|
||||
@let four = three + 1;
|
||||
{{value}}
|
||||
<button (click)="callback(three)"></button>
|
||||
`,
|
||||
standalone: true,
|
||||
})
|
||||
export class MyApp {
|
||||
value = 0;
|
||||
|
||||
callback(value: number) {
|
||||
console.log(value);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
$r3$.ɵɵdefineComponent({
|
||||
…
|
||||
decls: 7,
|
||||
vars: 3,
|
||||
…
|
||||
template: function MyApp_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
const $_r1$ = $r3$.ɵɵgetCurrentView();
|
||||
$r3$.ɵɵtext(0);
|
||||
$r3$.ɵɵdeclareLet(1)(2)(3)(4);
|
||||
$r3$.ɵɵtext(5);
|
||||
$r3$.ɵɵelementStart(6, "button", 0);
|
||||
$r3$.ɵɵlistener("click", function MyApp_Template_button_click_6_listener() {
|
||||
$r3$.ɵɵrestoreView($_r1$);
|
||||
const $three_1$ = $r3$.ɵɵreadContextLet(3);
|
||||
return $r3$.ɵɵresetView(ctx.callback($three_1$));
|
||||
});
|
||||
$r3$.ɵɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵtextInterpolate1(" ", ctx.value, " ");
|
||||
const $one_2$ = ctx.value + 1;
|
||||
const $two_3$ = $one_2$ + 1;
|
||||
$r3$.ɵɵadvance(3);
|
||||
const $three_5$ = $r3$.ɵɵstoreLet($two_3$ + 1);
|
||||
$three_5$ + 1;
|
||||
$r3$.ɵɵadvance(2);
|
||||
$r3$.ɵɵtextInterpolate1(" ", ctx.value, " ");
|
||||
}
|
||||
},
|
||||
…
|
||||
});
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
{{value}}
|
||||
@let one = value + 1;
|
||||
@let two = one + 1;
|
||||
@let three = two + 1;
|
||||
@let four = three + 1;
|
||||
{{two}}
|
||||
`,
|
||||
standalone: true,
|
||||
})
|
||||
export class MyApp {
|
||||
value = 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
$r3$.ɵɵdefineComponent({
|
||||
…
|
||||
decls: 6,
|
||||
vars: 2,
|
||||
template: function MyApp_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵtext(0);
|
||||
$r3$.ɵɵdeclareLet(1)(2)(3)(4);
|
||||
$r3$.ɵɵtext(5);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵtextInterpolate1(" ", ctx.value, " ");
|
||||
const $one_0$ = ctx.value + 1;
|
||||
const $two_1$ = $one_0$ + 1;
|
||||
const $three_2$ = $two_1$ + 1;
|
||||
$three_2$ + 1;
|
||||
$r3$.ɵɵadvance(5);
|
||||
$r3$.ɵɵtextInterpolate1(" ", $two_1$, " ");
|
||||
}
|
||||
},
|
||||
…
|
||||
});
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
@let value = 123;
|
||||
{{value}}
|
||||
<ng-template>{{value}}</ng-template>
|
||||
`,
|
||||
standalone: true,
|
||||
})
|
||||
export class MyApp {}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
function MyApp_ng_template_2_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵtext(0);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵnextContext();
|
||||
const $value_0$ = $r3$.ɵɵreadContextLet(0);
|
||||
$r3$.ɵɵtextInterpolate($value_0$);
|
||||
}
|
||||
}
|
||||
|
||||
…
|
||||
|
||||
$r3$.ɵɵdefineComponent({
|
||||
…
|
||||
decls: 3,
|
||||
vars: 2,
|
||||
template: function MyApp_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵdeclareLet(0);
|
||||
$r3$.ɵɵtext(1);
|
||||
$r3$.ɵɵtemplate(2, MyApp_ng_template_2_Template, 1, 1, "ng-template");
|
||||
}
|
||||
if (rf & 2) {
|
||||
const $value_r0$ = $r3$.ɵɵstoreLet(123);
|
||||
$r3$.ɵɵadvance();
|
||||
$r3$.ɵɵtextInterpolate1(" ", $value_r0$, " ");
|
||||
}
|
||||
},
|
||||
…
|
||||
});
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
{{value}}
|
||||
@let result = value * 2;
|
||||
{{value}}
|
||||
`,
|
||||
standalone: true,
|
||||
})
|
||||
export class MyApp {
|
||||
value = 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
$r3$.ɵɵdefineComponent({
|
||||
…
|
||||
decls: 3,
|
||||
vars: 2,
|
||||
template: function MyApp_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵtext(0);
|
||||
$r3$.ɵɵdeclareLet(1);
|
||||
$r3$.ɵɵtext(2);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵtextInterpolate1(" ", ctx.value, " ");
|
||||
ctx.value * 2;
|
||||
$r3$.ɵɵadvance(2);
|
||||
$r3$.ɵɵtextInterpolate1(" ", ctx.value, " ");
|
||||
}
|
||||
},
|
||||
…
|
||||
});
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import {Component, Pipe, PipeTransform} from '@angular/core';
|
||||
|
||||
@Pipe({
|
||||
name: 'double',
|
||||
standalone: true,
|
||||
})
|
||||
export class DoublePipe implements PipeTransform {
|
||||
transform(value: number) {
|
||||
return value * 2;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
@let one = value + 1;
|
||||
@let result = one | double;
|
||||
The result is {{result}}
|
||||
`,
|
||||
standalone: true,
|
||||
imports: [DoublePipe],
|
||||
})
|
||||
export class MyApp {
|
||||
value = 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
$r3$.ɵɵdefineComponent({
|
||||
…
|
||||
decls: 4,
|
||||
vars: 3,
|
||||
template: function MyApp_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵdeclareLet(0)(1);
|
||||
$r3$.ɵɵpipe(2, "double");
|
||||
$r3$.ɵɵtext(3);
|
||||
}
|
||||
if (rf & 2) {
|
||||
const $one_0$ = ctx.value + 1;
|
||||
const $result_1$ = $r3$.ɵɵpipeBind1(2, 1, $one_0$);
|
||||
$r3$.ɵɵadvance(3);
|
||||
$r3$.ɵɵtextInterpolate1(" The result is ", $result_1$, " ");
|
||||
}
|
||||
},
|
||||
…
|
||||
});
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
@let one = value + 1;
|
||||
@let two = one + 1;
|
||||
@let result = two + 1;
|
||||
The result is {{result}}
|
||||
`,
|
||||
standalone: true,
|
||||
})
|
||||
export class MyApp {
|
||||
value = 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
$r3$.ɵɵdefineComponent({
|
||||
…
|
||||
decls: 4,
|
||||
vars: 1,
|
||||
template: function MyApp_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵdeclareLet(0)(1)(2);
|
||||
$r3$.ɵɵtext(3);
|
||||
}
|
||||
if (rf & 2) {
|
||||
const $one_0$ = ctx.value + 1;
|
||||
const $two_1$ = $one_0$ + 1;
|
||||
const $result_2$ = $two_1$ + 1;
|
||||
$r3$.ɵɵadvance(3);
|
||||
$r3$.ɵɵtextInterpolate1(" The result is ", $result_2$, " ");
|
||||
}
|
||||
},
|
||||
…
|
||||
});
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
@let result = value * 2;
|
||||
The result is {{result}}
|
||||
`,
|
||||
standalone: true,
|
||||
})
|
||||
export class MyApp {
|
||||
value = 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
$r3$.ɵɵdefineComponent({
|
||||
…
|
||||
decls: 2,
|
||||
vars: 1,
|
||||
template: function MyApp_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵdeclareLet(0);
|
||||
$r3$.ɵɵtext(1);
|
||||
}
|
||||
if (rf & 2) {
|
||||
const $result_0$ = ctx.value * 2;
|
||||
$r3$.ɵɵadvance();
|
||||
$r3$.ɵɵtextInterpolate1(" The result is ", $result_0$, " ");
|
||||
}
|
||||
},
|
||||
…
|
||||
});
|
||||
|
|
@ -495,6 +495,10 @@ export class Identifiers {
|
|||
static twoWayBindingSet: o.ExternalReference = {name: 'ɵɵtwoWayBindingSet', moduleName: CORE};
|
||||
static twoWayListener: o.ExternalReference = {name: 'ɵɵtwoWayListener', moduleName: CORE};
|
||||
|
||||
static declareLet: o.ExternalReference = {name: 'ɵɵdeclareLet', moduleName: CORE};
|
||||
static storeLet: o.ExternalReference = {name: 'ɵɵstoreLet', moduleName: CORE};
|
||||
static readContextLet: o.ExternalReference = {name: 'ɵɵreadContextLet', moduleName: CORE};
|
||||
|
||||
static NgOnChangesFeature: o.ExternalReference = {name: 'ɵɵNgOnChangesFeature', moduleName: CORE};
|
||||
|
||||
static InheritDefinitionFeature: o.ExternalReference = {
|
||||
|
|
|
|||
|
|
@ -205,6 +205,16 @@ export enum OpKind {
|
|||
*/
|
||||
TwoWayListener,
|
||||
|
||||
/**
|
||||
* A creation-time operation that initializes the slot for a `@let` declaration.
|
||||
*/
|
||||
DeclareLet,
|
||||
|
||||
/**
|
||||
* An update-time operation that stores the current value of a `@let` declaration.
|
||||
*/
|
||||
StoreLet,
|
||||
|
||||
/**
|
||||
* The start of an i18n block.
|
||||
*/
|
||||
|
|
@ -290,6 +300,16 @@ export enum ExpressionKind {
|
|||
*/
|
||||
Reference,
|
||||
|
||||
/**
|
||||
* A call storing the value of a `@let` declaration.
|
||||
*/
|
||||
StoreLet,
|
||||
|
||||
/**
|
||||
* A reference to a `@let` declaration read from the context view.
|
||||
*/
|
||||
ContextLetReference,
|
||||
|
||||
/**
|
||||
* Runtime operation to snapshot the current view context.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -15,7 +15,13 @@ import {SlotHandle} from './handle';
|
|||
import type {XrefId} from './operations';
|
||||
import type {CreateOp} from './ops/create';
|
||||
import {Interpolation, type UpdateOp} from './ops/update';
|
||||
import {ConsumesVarsTrait, UsesVarOffset, UsesVarOffsetTrait} from './traits';
|
||||
import {
|
||||
ConsumesVarsTrait,
|
||||
DependsOnSlotContext,
|
||||
DependsOnSlotContextOpTrait,
|
||||
UsesVarOffset,
|
||||
UsesVarOffsetTrait,
|
||||
} from './traits';
|
||||
|
||||
/**
|
||||
* An `o.Expression` subtype representing a logical expression in the intermediate representation.
|
||||
|
|
@ -42,7 +48,9 @@ export type Expression =
|
|||
| SlotLiteralExpr
|
||||
| ConditionalCaseExpr
|
||||
| ConstCollectedExpr
|
||||
| TwoWayBindingSetExpr;
|
||||
| TwoWayBindingSetExpr
|
||||
| ContextLetReferenceExpr
|
||||
| StoreLetExpr;
|
||||
|
||||
/**
|
||||
* Transformer type which converts expressions into general `o.Expression`s (which may be an
|
||||
|
|
@ -138,6 +146,73 @@ export class ReferenceExpr extends ExpressionBase {
|
|||
}
|
||||
}
|
||||
|
||||
export class StoreLetExpr
|
||||
extends ExpressionBase
|
||||
implements ConsumesVarsTrait, DependsOnSlotContextOpTrait
|
||||
{
|
||||
override readonly kind = ExpressionKind.StoreLet;
|
||||
readonly [ConsumesVarsTrait] = true;
|
||||
readonly [DependsOnSlotContext] = true;
|
||||
|
||||
constructor(
|
||||
readonly target: XrefId,
|
||||
public value: o.Expression,
|
||||
override sourceSpan: ParseSourceSpan,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
override visitExpression(): void {}
|
||||
|
||||
override isEquivalent(e: o.Expression): boolean {
|
||||
return (
|
||||
e instanceof StoreLetExpr && e.target === this.target && e.value.isEquivalent(this.value)
|
||||
);
|
||||
}
|
||||
|
||||
override isConstant(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
override transformInternalExpressions(
|
||||
transform: ExpressionTransform,
|
||||
flags: VisitorContextFlag,
|
||||
): void {
|
||||
this.value = transformExpressionsInExpression(this.value, transform, flags);
|
||||
}
|
||||
|
||||
override clone(): StoreLetExpr {
|
||||
return new StoreLetExpr(this.target, this.value, this.sourceSpan);
|
||||
}
|
||||
}
|
||||
|
||||
export class ContextLetReferenceExpr extends ExpressionBase {
|
||||
override readonly kind = ExpressionKind.ContextLetReference;
|
||||
|
||||
constructor(
|
||||
readonly target: XrefId,
|
||||
readonly targetSlot: SlotHandle,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
override visitExpression(): void {}
|
||||
|
||||
override isEquivalent(e: o.Expression): boolean {
|
||||
return e instanceof ContextLetReferenceExpr && e.target === this.target;
|
||||
}
|
||||
|
||||
override isConstant(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
override transformInternalExpressions(): void {}
|
||||
|
||||
override clone(): ContextLetReferenceExpr {
|
||||
return new ContextLetReferenceExpr(this.target, this.targetSlot);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A reference to the current view context (usually the `ctx` variable in a template function).
|
||||
*/
|
||||
|
|
@ -1115,6 +1190,9 @@ export function transformExpressionsInOp(
|
|||
case OpKind.DeferWhen:
|
||||
op.expr = transformExpressionsInExpression(op.expr, transform, flags);
|
||||
break;
|
||||
case OpKind.StoreLet:
|
||||
op.value = transformExpressionsInExpression(op.value, transform, flags);
|
||||
break;
|
||||
case OpKind.Advance:
|
||||
case OpKind.Container:
|
||||
case OpKind.ContainerEnd:
|
||||
|
|
@ -1140,6 +1218,7 @@ export function transformExpressionsInOp(
|
|||
case OpKind.Text:
|
||||
case OpKind.I18nAttributes:
|
||||
case OpKind.IcuPlaceholder:
|
||||
case OpKind.DeclareLet:
|
||||
// These operations contain no expressions.
|
||||
break;
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -67,7 +67,8 @@ export type CreateOp =
|
|||
| IcuEndOp
|
||||
| IcuPlaceholderOp
|
||||
| I18nContextOp
|
||||
| I18nAttributesOp;
|
||||
| I18nAttributesOp
|
||||
| DeclareLetOp;
|
||||
|
||||
/**
|
||||
* An operation representing the creation of an element or container.
|
||||
|
|
@ -1072,6 +1073,35 @@ export function createDeferOnOp(
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Op that reserves a slot during creation time for a `@let` declaration.
|
||||
*/
|
||||
export interface DeclareLetOp extends Op<CreateOp>, ConsumesSlotOpTrait {
|
||||
kind: OpKind.DeclareLet;
|
||||
xref: XrefId;
|
||||
sourceSpan: ParseSourceSpan;
|
||||
declaredName: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a `DeclareLetOp`.
|
||||
*/
|
||||
export function createDeclareLetOp(
|
||||
xref: XrefId,
|
||||
declaredName: string,
|
||||
sourceSpan: ParseSourceSpan,
|
||||
): DeclareLetOp {
|
||||
return {
|
||||
kind: OpKind.DeclareLet,
|
||||
xref,
|
||||
declaredName,
|
||||
sourceSpan,
|
||||
handle: new SlotHandle(),
|
||||
...TRAIT_CONSUMES_SLOT,
|
||||
...NEW_OP,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a single value in an i18n param map. Each placeholder in the map may have multiple of
|
||||
* these values associated with it.
|
||||
|
|
|
|||
|
|
@ -51,7 +51,8 @@ export type UpdateOp =
|
|||
| I18nExpressionOp
|
||||
| I18nApplyOp
|
||||
| RepeaterOp
|
||||
| DeferWhenOp;
|
||||
| DeferWhenOp
|
||||
| StoreLetOp;
|
||||
|
||||
/**
|
||||
* A logical operation to perform string interpolation on a text node.
|
||||
|
|
@ -949,3 +950,41 @@ export function createI18nApplyOp(
|
|||
...NEW_OP,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Op to store the current value of a `@let` declaration.
|
||||
*/
|
||||
export interface StoreLetOp extends Op<UpdateOp>, ConsumesVarsTrait {
|
||||
kind: OpKind.StoreLet;
|
||||
sourceSpan: ParseSourceSpan;
|
||||
|
||||
/** Name that the user set when declaring the `@let`. */
|
||||
declaredName: string;
|
||||
|
||||
/** XrefId of the slot in which the call may write its value. */
|
||||
target: XrefId;
|
||||
|
||||
/** Value of the `@let` declaration. */
|
||||
value: o.Expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a `StoreLetOp`.
|
||||
*/
|
||||
export function createStoreLetOp(
|
||||
target: XrefId,
|
||||
declaredName: string,
|
||||
value: o.Expression,
|
||||
sourceSpan: ParseSourceSpan,
|
||||
): StoreLetOp {
|
||||
return {
|
||||
kind: OpKind.StoreLet,
|
||||
target,
|
||||
declaredName,
|
||||
value,
|
||||
sourceSpan,
|
||||
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
|
||||
...TRAIT_CONSUMES_VARS,
|
||||
...NEW_OP,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import type {ParseSourceSpan} from '../../../../parse_util';
|
||||
import type {Expression} from './expression';
|
||||
import * as o from '../../../../output/output_ast';
|
||||
import type {Op, XrefId} from './operations';
|
||||
import {SlotHandle} from './handle';
|
||||
|
||||
|
|
@ -135,10 +136,14 @@ export function hasConsumesSlotTrait<OpT extends Op<OpT>>(
|
|||
/**
|
||||
* Test whether an operation implements `DependsOnSlotContextOpTrait`.
|
||||
*/
|
||||
export function hasDependsOnSlotContextTrait<ExprT extends o.Expression>(
|
||||
expr: ExprT,
|
||||
): expr is ExprT & DependsOnSlotContextOpTrait;
|
||||
export function hasDependsOnSlotContextTrait<OpT extends Op<OpT>>(
|
||||
op: OpT,
|
||||
): op is OpT & DependsOnSlotContextOpTrait {
|
||||
return (op as Partial<DependsOnSlotContextOpTrait>)[DependsOnSlotContext] === true;
|
||||
): op is OpT & DependsOnSlotContextOpTrait;
|
||||
export function hasDependsOnSlotContextTrait(value: any): boolean {
|
||||
return (value as Partial<DependsOnSlotContextOpTrait>)[DependsOnSlotContext] === true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -80,6 +80,9 @@ import {generateTrackVariables} from './phases/track_variables';
|
|||
import {countVariables} from './phases/var_counting';
|
||||
import {optimizeVariables} from './phases/variable_optimization';
|
||||
import {wrapI18nIcus} from './phases/wrap_icus';
|
||||
import {optimizeStoreLet} from './phases/store_let_optimization';
|
||||
import {removeIllegalLetReferences} from './phases/remove_illegal_let_references';
|
||||
import {generateLocalLetReferences} from './phases/generate_local_let_references';
|
||||
|
||||
type Phase =
|
||||
| {
|
||||
|
|
@ -121,11 +124,13 @@ const phases: Phase[] = [
|
|||
{kind: Kind.Tmpl, fn: createVariadicPipes},
|
||||
{kind: Kind.Both, fn: generatePureLiteralStructures},
|
||||
{kind: Kind.Tmpl, fn: generateProjectionDefs},
|
||||
{kind: Kind.Tmpl, fn: generateLocalLetReferences},
|
||||
{kind: Kind.Tmpl, fn: generateVariables},
|
||||
{kind: Kind.Tmpl, fn: saveAndRestoreView},
|
||||
{kind: Kind.Both, fn: deleteAnyCasts},
|
||||
{kind: Kind.Both, fn: resolveDollarEvent},
|
||||
{kind: Kind.Tmpl, fn: generateTrackVariables},
|
||||
{kind: Kind.Tmpl, fn: removeIllegalLetReferences},
|
||||
{kind: Kind.Both, fn: resolveNames},
|
||||
{kind: Kind.Tmpl, fn: resolveDeferTargetNames},
|
||||
{kind: Kind.Tmpl, fn: transformTwoWayBindingSet},
|
||||
|
|
@ -137,6 +142,7 @@ const phases: Phase[] = [
|
|||
{kind: Kind.Both, fn: expandSafeReads},
|
||||
{kind: Kind.Both, fn: generateTemporaryVariables},
|
||||
{kind: Kind.Both, fn: optimizeVariables},
|
||||
{kind: Kind.Both, fn: optimizeStoreLet},
|
||||
{kind: Kind.Tmpl, fn: allocateSlots},
|
||||
{kind: Kind.Tmpl, fn: resolveI18nElementPlaceholders},
|
||||
{kind: Kind.Tmpl, fn: resolveI18nExpressionPlaceholders},
|
||||
|
|
|
|||
|
|
@ -226,7 +226,7 @@ function ingestNodes(unit: ViewCompilationUnit, template: t.Node[]): void {
|
|||
} else if (node instanceof t.ForLoopBlock) {
|
||||
ingestForBlock(unit, node);
|
||||
} else if (node instanceof t.LetDeclaration) {
|
||||
// TODO(crisbeto): needs further integration
|
||||
ingestLetDeclaration(unit, node);
|
||||
} else {
|
||||
throw new Error(`Unsupported template node: ${node.constructor.name}`);
|
||||
}
|
||||
|
|
@ -926,6 +926,20 @@ function getComputedForLoopVariableExpression(
|
|||
}
|
||||
}
|
||||
|
||||
function ingestLetDeclaration(unit: ViewCompilationUnit, node: t.LetDeclaration) {
|
||||
const target = unit.job.allocateXrefId();
|
||||
|
||||
unit.create.push(ir.createDeclareLetOp(target, node.name, node.sourceSpan));
|
||||
unit.update.push(
|
||||
ir.createStoreLetOp(
|
||||
target,
|
||||
node.name,
|
||||
convertAst(node.value, unit.job, node.valueSpan),
|
||||
node.sourceSpan,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a template AST expression into an output AST expression.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -401,6 +401,18 @@ export function deferWhen(
|
|||
return call(prefetch ? Identifiers.deferPrefetchWhen : Identifiers.deferWhen, [expr], sourceSpan);
|
||||
}
|
||||
|
||||
export function declareLet(slot: number, sourceSpan: ParseSourceSpan): ir.CreateOp {
|
||||
return call(Identifiers.declareLet, [o.literal(slot)], sourceSpan);
|
||||
}
|
||||
|
||||
export function storeLet(value: o.Expression, sourceSpan: ParseSourceSpan): o.Expression {
|
||||
return o.importExpr(Identifiers.storeLet).callFn([value], sourceSpan);
|
||||
}
|
||||
|
||||
export function readContextLet(slot: number): o.Expression {
|
||||
return o.importExpr(Identifiers.readContextLet).callFn([o.literal(slot)]);
|
||||
}
|
||||
|
||||
export function i18n(
|
||||
slot: number,
|
||||
constIndex: number,
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ const CHAINABLE = new Set([
|
|||
R3.templateCreate,
|
||||
R3.twoWayProperty,
|
||||
R3.twoWayListener,
|
||||
R3.declareLet,
|
||||
]);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -36,16 +36,29 @@ export function generateAdvance(job: CompilationJob): void {
|
|||
// To do that, we track what the runtime's slot counter will be through the update operations.
|
||||
let slotContext = 0;
|
||||
for (const op of unit.update) {
|
||||
if (!ir.hasDependsOnSlotContextTrait(op)) {
|
||||
// `op` doesn't depend on the slot counter, so it can be skipped.
|
||||
continue;
|
||||
} else if (!slotMap.has(op.target)) {
|
||||
// We expect ops that _do_ depend on the slot counter to point at declarations that exist in
|
||||
// the `slotMap`.
|
||||
throw new Error(`AssertionError: reference to unknown slot for target ${op.target}`);
|
||||
let consumer: ir.DependsOnSlotContextOpTrait | null = null;
|
||||
|
||||
if (ir.hasDependsOnSlotContextTrait(op)) {
|
||||
consumer = op;
|
||||
} else {
|
||||
ir.visitExpressionsInOp(op, (expr) => {
|
||||
if (consumer === null && ir.hasDependsOnSlotContextTrait(expr)) {
|
||||
consumer = expr;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const slot = slotMap.get(op.target)!;
|
||||
if (consumer === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!slotMap.has(consumer.target)) {
|
||||
// We expect ops that _do_ depend on the slot counter to point at declarations that exist in
|
||||
// the `slotMap`.
|
||||
throw new Error(`AssertionError: reference to unknown slot for target ${consumer.target}`);
|
||||
}
|
||||
|
||||
const slot = slotMap.get(consumer.target)!;
|
||||
|
||||
// Does the slot counter need to be adjusted?
|
||||
if (slotContext !== slot) {
|
||||
|
|
@ -55,10 +68,7 @@ export function generateAdvance(job: CompilationJob): void {
|
|||
throw new Error(`AssertionError: slot counter should never need to move backwards`);
|
||||
}
|
||||
|
||||
ir.OpList.insertBefore<ir.UpdateOp>(
|
||||
ir.createAdvanceOp(delta, (op as ir.DependsOnSlotContextOpTrait).sourceSpan),
|
||||
op,
|
||||
);
|
||||
ir.OpList.insertBefore<ir.UpdateOp>(ir.createAdvanceOp(delta, consumer.sourceSpan), op);
|
||||
slotContext = slot;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import * as ir from '../../ir';
|
||||
|
||||
import type {ComponentCompilationJob} from '../compilation';
|
||||
|
||||
/**
|
||||
* Replaces the `storeLet` ops with variables that can be
|
||||
* used to reference the value within the same view.
|
||||
*/
|
||||
export function generateLocalLetReferences(job: ComponentCompilationJob): void {
|
||||
for (const unit of job.units) {
|
||||
for (const op of unit.update) {
|
||||
if (op.kind !== ir.OpKind.StoreLet) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const variable: ir.IdentifierVariable = {
|
||||
kind: ir.SemanticVariableKind.Identifier,
|
||||
name: null,
|
||||
identifier: op.declaredName,
|
||||
};
|
||||
|
||||
ir.OpList.replace<ir.UpdateOp>(
|
||||
op,
|
||||
ir.createVariableOp<ir.UpdateOp>(
|
||||
job.allocateXrefId(),
|
||||
variable,
|
||||
new ir.StoreLetExpr(op.target, op.value, op.sourceSpan),
|
||||
ir.VariableFlags.None,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -61,14 +61,12 @@ function recursivelyProcessView(view: ViewCompilationUnit, parentScope: Scope |
|
|||
case ir.OpKind.Listener:
|
||||
case ir.OpKind.TwoWayListener:
|
||||
// Prepend variables to listener handler functions.
|
||||
op.handlerOps.prepend(generateVariablesInScopeForView(view, scope));
|
||||
op.handlerOps.prepend(generateVariablesInScopeForView(view, scope, true));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Prepend the declarations for all available variables in scope to the `update` block.
|
||||
const preambleOps = generateVariablesInScopeForView(view, scope);
|
||||
view.update.prepend(preambleOps);
|
||||
view.update.prepend(generateVariablesInScopeForView(view, scope, false));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -91,6 +89,11 @@ interface Scope {
|
|||
*/
|
||||
references: Reference[];
|
||||
|
||||
/**
|
||||
* `@let` declarations collected from the view.
|
||||
*/
|
||||
letDeclarations: LetDeclaration[];
|
||||
|
||||
/**
|
||||
* `Scope` of the parent view, if any.
|
||||
*/
|
||||
|
|
@ -126,6 +129,20 @@ interface Reference {
|
|||
variable: ir.SemanticVariable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Information about `@let` declaration collected from a view.
|
||||
*/
|
||||
interface LetDeclaration {
|
||||
/** `XrefId` of the `@let` declaration that the reference is pointing to. */
|
||||
targetId: ir.XrefId;
|
||||
|
||||
/** Slot in which the declaration is stored. */
|
||||
targetSlot: ir.SlotHandle;
|
||||
|
||||
/** Variable referring to the declaration. */
|
||||
variable: ir.IdentifierVariable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a view and generate a `Scope` representing the variables available for reference within
|
||||
* that view.
|
||||
|
|
@ -141,6 +158,7 @@ function getScopeForView(view: ViewCompilationUnit, parent: Scope | null): Scope
|
|||
contextVariables: new Map<string, ir.SemanticVariable>(),
|
||||
aliases: view.aliases,
|
||||
references: [],
|
||||
letDeclarations: [],
|
||||
parent,
|
||||
};
|
||||
|
||||
|
|
@ -175,6 +193,18 @@ function getScopeForView(view: ViewCompilationUnit, parent: Scope | null): Scope
|
|||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case ir.OpKind.DeclareLet:
|
||||
scope.letDeclarations.push({
|
||||
targetId: op.xref,
|
||||
targetSlot: op.handle,
|
||||
variable: {
|
||||
kind: ir.SemanticVariableKind.Identifier,
|
||||
name: null,
|
||||
identifier: op.declaredName,
|
||||
},
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -190,6 +220,7 @@ function getScopeForView(view: ViewCompilationUnit, parent: Scope | null): Scope
|
|||
function generateVariablesInScopeForView(
|
||||
view: ViewCompilationUnit,
|
||||
scope: Scope,
|
||||
isListener: boolean,
|
||||
): ir.VariableOp<ir.UpdateOp>[] {
|
||||
const newOps: ir.VariableOp<ir.UpdateOp>[] = [];
|
||||
|
||||
|
|
@ -247,9 +278,22 @@ function generateVariablesInScopeForView(
|
|||
);
|
||||
}
|
||||
|
||||
if (scope.view !== view.xref || isListener) {
|
||||
for (const decl of scope.letDeclarations) {
|
||||
newOps.push(
|
||||
ir.createVariableOp<ir.UpdateOp>(
|
||||
view.job.allocateXrefId(),
|
||||
decl.variable,
|
||||
new ir.ContextLetReferenceExpr(decl.targetId, decl.targetSlot),
|
||||
ir.VariableFlags.None,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (scope.parent !== null) {
|
||||
// Recursively add variables from the parent scope.
|
||||
newOps.push(...generateVariablesInScopeForView(view, scope.parent));
|
||||
newOps.push(...generateVariablesInScopeForView(view, scope.parent, false));
|
||||
}
|
||||
return newOps;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ function mergeNextContextsInOps(ops: ir.OpList<ir.UpdateOp>): void {
|
|||
break;
|
||||
case ir.ExpressionKind.GetCurrentView:
|
||||
case ir.ExpressionKind.Reference:
|
||||
case ir.ExpressionKind.ContextLetReference:
|
||||
// Can't merge past a dependency on the context.
|
||||
tryToMerge = false;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -184,6 +184,9 @@ function reifyCreateOperations(unit: CompilationUnit, ops: ir.OpList<ir.CreateOp
|
|||
case ir.OpKind.Pipe:
|
||||
ir.OpList.replace(op, ng.pipe(op.handle.slot!, op.name));
|
||||
break;
|
||||
case ir.OpKind.DeclareLet:
|
||||
ir.OpList.replace(op, ng.declareLet(op.handle.slot!, op.sourceSpan));
|
||||
break;
|
||||
case ir.OpKind.Listener:
|
||||
const listenerFn = reifyListenerHandler(
|
||||
unit,
|
||||
|
|
@ -537,6 +540,8 @@ function reifyUpdateOperations(_unit: CompilationUnit, ops: ir.OpList<ir.UpdateO
|
|||
case ir.OpKind.DeferWhen:
|
||||
ir.OpList.replace(op, ng.deferWhen(op.prefetch, op.expr, op.sourceSpan));
|
||||
break;
|
||||
case ir.OpKind.StoreLet:
|
||||
throw new Error(`AssertionError: unexpected storeLet ${op.declaredName}`);
|
||||
case ir.OpKind.Statement:
|
||||
// Pass statement operations directly through.
|
||||
break;
|
||||
|
|
@ -599,6 +604,10 @@ function reifyIrExpression(expr: o.Expression): o.Expression {
|
|||
return ng.pipeBindV(expr.targetSlot.slot!, expr.varOffset!, expr.args);
|
||||
case ir.ExpressionKind.SlotLiteralExpr:
|
||||
return o.literal(expr.slot.slot!);
|
||||
case ir.ExpressionKind.ContextLetReference:
|
||||
return ng.readContextLet(expr.targetSlot.slot!);
|
||||
case ir.ExpressionKind.StoreLet:
|
||||
return ng.storeLet(expr.value, expr.sourceSpan);
|
||||
default:
|
||||
throw new Error(
|
||||
`AssertionError: Unsupported reification of ir.Expression kind: ${
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import * as o from '../../../../output/output_ast';
|
||||
import * as ir from '../../ir';
|
||||
import {CompilationJob} from '../compilation';
|
||||
|
||||
/**
|
||||
* It's not allowed to access a `@let` declaration before it has been defined. This is enforced
|
||||
* already via template type checking, however it can trip some of the assertions in the pipeline.
|
||||
* E.g. the naming phase can fail because we resolved the variable here, but the variable doesn't
|
||||
* exist anymore because the optimization phase removed it since it's invalid. To avoid surfacing
|
||||
* confusing errors to users in the case where template type checking isn't running (e.g. in JIT
|
||||
* mode) this phase detects illegal forward references and replaces them with `undefined`.
|
||||
* Eventually users will see the proper error from the template type checker.
|
||||
*/
|
||||
export function removeIllegalLetReferences(job: CompilationJob): void {
|
||||
for (const unit of job.units) {
|
||||
for (const op of unit.update) {
|
||||
if (
|
||||
op.kind !== ir.OpKind.Variable ||
|
||||
op.variable.kind !== ir.SemanticVariableKind.Identifier ||
|
||||
!(op.initializer instanceof ir.StoreLetExpr)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const name = op.variable.identifier;
|
||||
let current: ir.UpdateOp | null = op;
|
||||
while (current && current.kind !== ir.OpKind.ListEnd) {
|
||||
ir.transformExpressionsInOp(
|
||||
current,
|
||||
(expr) =>
|
||||
expr instanceof ir.LexicalReadExpr && expr.name === name ? o.literal(undefined) : expr,
|
||||
ir.VisitorContextFlag.None,
|
||||
);
|
||||
current = current.prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import * as o from '../../../../output/output_ast';
|
||||
import * as ir from '../../ir';
|
||||
import {CompilationJob, CompilationUnit, ViewCompilationUnit} from '../compilation';
|
||||
import {CompilationJob, CompilationUnit} from '../compilation';
|
||||
|
||||
/**
|
||||
* Resolves lexical references in views (`ir.LexicalReadExpr`) to either a target variable or to
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ export function saveAndRestoreView(job: ComponentCompilationJob): void {
|
|||
if (!needsRestoreView) {
|
||||
for (const handlerOp of op.handlerOps) {
|
||||
ir.visitExpressionsInOp(handlerOp, (expr) => {
|
||||
if (expr instanceof ir.ReferenceExpr) {
|
||||
if (expr instanceof ir.ReferenceExpr || expr instanceof ir.ContextLetReferenceExpr) {
|
||||
// Listeners that reference() a local ref need the save/restore view operation.
|
||||
needsRestoreView = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
/*!
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import * as o from '../../../../output/output_ast';
|
||||
import * as ir from '../../ir';
|
||||
import {CompilationJob} from '../compilation';
|
||||
|
||||
/**
|
||||
* Removes any `storeLet` calls that aren't referenced outside of the current view.
|
||||
*/
|
||||
export function optimizeStoreLet(job: CompilationJob): void {
|
||||
const letUsedExternally = new Set<ir.XrefId>();
|
||||
|
||||
// Since `@let` declarations can be referenced in child views, both in
|
||||
// the creation block (via listeners) and in the update block, we have
|
||||
// to look through all the ops to find the references.
|
||||
for (const unit of job.units) {
|
||||
for (const op of unit.ops()) {
|
||||
ir.visitExpressionsInOp(op, (expr) => {
|
||||
if (expr instanceof ir.ContextLetReferenceExpr) {
|
||||
letUsedExternally.add(expr.target);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(crisbeto): potentially remove the unused calls completely, pending discussion.
|
||||
for (const unit of job.units) {
|
||||
for (const op of unit.update) {
|
||||
ir.transformExpressionsInOp(
|
||||
op,
|
||||
(expression) =>
|
||||
expression instanceof ir.StoreLetExpr && !letUsedExternally.has(expression.target)
|
||||
? expression.value
|
||||
: expression,
|
||||
ir.VisitorContextFlag.None,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -134,6 +134,7 @@ function varsUsedByOp(op: (ir.CreateOp | ir.UpdateOp) & ir.ConsumesVarsTrait): n
|
|||
case ir.OpKind.I18nExpression:
|
||||
case ir.OpKind.Conditional:
|
||||
case ir.OpKind.DeferWhen:
|
||||
case ir.OpKind.StoreLet:
|
||||
return 1;
|
||||
case ir.OpKind.RepeaterCreate:
|
||||
// Repeaters may require an extra variable binding slot, if they have an empty view, for the
|
||||
|
|
@ -154,6 +155,8 @@ export function varsUsedByIrExpression(expr: ir.Expression & ir.ConsumesVarsTrai
|
|||
return 1 + expr.args.length;
|
||||
case ir.ExpressionKind.PipeBindingVariadic:
|
||||
return 1 + expr.numArgs;
|
||||
case ir.ExpressionKind.StoreLet:
|
||||
return 1;
|
||||
default:
|
||||
throw new Error(
|
||||
`AssertionError: unhandled ConsumesVarsTrait expression ${expr.constructor.name}`,
|
||||
|
|
|
|||
|
|
@ -320,7 +320,10 @@ function fencesForIrExpression(expr: ir.Expression): Fence {
|
|||
return Fence.ViewContextRead | Fence.ViewContextWrite;
|
||||
case ir.ExpressionKind.RestoreView:
|
||||
return Fence.ViewContextRead | Fence.ViewContextWrite | Fence.SideEffectful;
|
||||
case ir.ExpressionKind.StoreLet:
|
||||
return Fence.SideEffectful;
|
||||
case ir.ExpressionKind.Reference:
|
||||
case ir.ExpressionKind.ContextLetReference:
|
||||
return Fence.ViewContextRead;
|
||||
default:
|
||||
return Fence.None;
|
||||
|
|
|
|||
|
|
@ -237,6 +237,9 @@ export {
|
|||
ɵsetUnknownElementStrictMode,
|
||||
ɵgetUnknownPropertyStrictMode,
|
||||
ɵsetUnknownPropertyStrictMode,
|
||||
ɵɵdeclareLet,
|
||||
ɵɵstoreLet,
|
||||
ɵɵreadContextLet,
|
||||
} from './render3/index';
|
||||
export {CONTAINER_HEADER_OFFSET as ɵCONTAINER_HEADER_OFFSET} from './render3/interfaces/container';
|
||||
export {LContext as ɵLContext} from './render3/interfaces/context';
|
||||
|
|
|
|||
|
|
@ -169,6 +169,9 @@ export {
|
|||
ɵsetUnknownElementStrictMode,
|
||||
ɵgetUnknownPropertyStrictMode,
|
||||
ɵsetUnknownPropertyStrictMode,
|
||||
ɵɵdeclareLet,
|
||||
ɵɵstoreLet,
|
||||
ɵɵreadContextLet,
|
||||
} from './instructions/all';
|
||||
export {
|
||||
DEFER_BLOCK_DEPENDENCY_INTERCEPTOR as ɵDEFER_BLOCK_DEPENDENCY_INTERCEPTOR,
|
||||
|
|
|
|||
|
|
@ -62,3 +62,4 @@ export * from './template';
|
|||
export * from './text';
|
||||
export * from './text_interpolation';
|
||||
export * from './two_way';
|
||||
export * from './let_declaration';
|
||||
|
|
|
|||
41
packages/core/src/render3/instructions/let_declaration.ts
Normal file
41
packages/core/src/render3/instructions/let_declaration.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/*!
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* Declares an `@let` at a specific data slot.
|
||||
*
|
||||
* @param index Index at which to declare the `@let`.
|
||||
*
|
||||
* @codeGenApi
|
||||
*/
|
||||
export function ɵɵdeclareLet(index: number): typeof ɵɵdeclareLet {
|
||||
// TODO(crisbeto): implement this
|
||||
return ɵɵdeclareLet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instruction that stores the value of a `@let` declaration on the current view.
|
||||
*
|
||||
* @codeGenApi
|
||||
*/
|
||||
export function ɵɵstoreLet<T>(value: T): T {
|
||||
// TODO(crisbeto): implement this
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the value of a `@let` declaration defined within the same view.
|
||||
*
|
||||
* @param index Index of the declaration within the view.
|
||||
*
|
||||
* @codeGenApi
|
||||
*/
|
||||
export function ɵɵreadContextLet<T>(index: number): T {
|
||||
// TODO(crisbeto): implement this
|
||||
return null as any;
|
||||
}
|
||||
|
|
@ -187,6 +187,9 @@ export const angularCoreEnv: {[name: string]: unknown} = (() => ({
|
|||
'ɵɵregisterNgModuleType': registerNgModuleType,
|
||||
'ɵɵgetComponentDepsFactory': r3.ɵɵgetComponentDepsFactory,
|
||||
'ɵsetClassDebugInfo': r3.ɵsetClassDebugInfo,
|
||||
'ɵɵdeclareLet': r3.ɵɵdeclareLet,
|
||||
'ɵɵstoreLet': r3.ɵɵstoreLet,
|
||||
'ɵɵreadContextLet': r3.ɵɵreadContextLet,
|
||||
|
||||
'ɵɵsanitizeHtml': sanitization.ɵɵsanitizeHtml,
|
||||
'ɵɵsanitizeStyle': sanitization.ɵɵsanitizeStyle,
|
||||
|
|
|
|||
|
|
@ -1628,6 +1628,9 @@
|
|||
{
|
||||
"name": "init_lang"
|
||||
},
|
||||
{
|
||||
"name": "init_let_declaration"
|
||||
},
|
||||
{
|
||||
"name": "init_lift"
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in a new issue