diff --git a/MODULE.bazel b/MODULE.bazel index 0ab12b84fba..5360488d2e8 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -25,7 +25,7 @@ git_override( bazel_dep(name = "devinfra") git_override( module_name = "devinfra", - commit = "80db036355181684ed07021a175e9f039190d3d6", + commit = "00a4cf14cdc374867673a52ecb556f356da8bec0", remote = "https://github.com/angular/dev-infra.git", ) diff --git a/adev/shared-docs/pipeline/api-gen/rendering/BUILD.bazel b/adev/shared-docs/pipeline/api-gen/rendering/BUILD.bazel index 27e4986405c..36457fa2e53 100644 --- a/adev/shared-docs/pipeline/api-gen/rendering/BUILD.bazel +++ b/adev/shared-docs/pipeline/api-gen/rendering/BUILD.bazel @@ -27,9 +27,12 @@ ts_project( ), deps = [ ":entities", + "//adev:node_modules/marked", "//adev:node_modules/preact", "//adev:node_modules/preact-render-to-string", "//adev:node_modules/prettier", + "//adev:node_modules/shiki", + "//adev/shared-docs/pipeline/shared:linking", "//adev/shared-docs/pipeline/shared:shiki", "//adev/shared-docs/pipeline/shared/marked", "//adev/shared-docs/pipeline/shared/regions", diff --git a/adev/shared-docs/pipeline/api-gen/rendering/test/BUILD.bazel b/adev/shared-docs/pipeline/api-gen/rendering/test/BUILD.bazel index f592a260099..921012a3c59 100644 --- a/adev/shared-docs/pipeline/api-gen/rendering/test/BUILD.bazel +++ b/adev/shared-docs/pipeline/api-gen/rendering/test/BUILD.bazel @@ -21,6 +21,7 @@ ts_project( "//adev:node_modules/@types/jsdom", "//adev:node_modules/jsdom", "//adev/shared-docs/pipeline/api-gen/rendering:render_api_to_html_lib", + "//adev/shared-docs/pipeline/shared:shiki", ], ) diff --git a/adev/shared-docs/pipeline/guides/BUILD.bazel b/adev/shared-docs/pipeline/guides/BUILD.bazel index 00b6a32161c..70b405ded21 100644 --- a/adev/shared-docs/pipeline/guides/BUILD.bazel +++ b/adev/shared-docs/pipeline/guides/BUILD.bazel @@ -76,5 +76,7 @@ ts_project( deps = [ ":guides_lib", "//adev:node_modules/@types/node", + "//adev/shared-docs/pipeline/shared:shiki", + "//adev/shared-docs/pipeline/shared/marked", ], ) diff --git a/adev/shared-docs/pipeline/shared/marked/BUILD.bazel b/adev/shared-docs/pipeline/shared/marked/BUILD.bazel index 37ba04c7b52..796e9d11571 100644 --- a/adev/shared-docs/pipeline/shared/marked/BUILD.bazel +++ b/adev/shared-docs/pipeline/shared/marked/BUILD.bazel @@ -18,6 +18,7 @@ ts_project( "//adev:node_modules/marked", "//adev:node_modules/mermaid", "//adev:node_modules/playwright-core", + "//adev:node_modules/shiki", "//adev/shared-docs/pipeline/shared:linking", "//adev/shared-docs/pipeline/shared:shiki", "//adev/shared-docs/pipeline/shared/regions", diff --git a/adev/shared-docs/pipeline/shared/marked/test/BUILD.bazel b/adev/shared-docs/pipeline/shared/marked/test/BUILD.bazel index c117f1e707f..31e1091b227 100644 --- a/adev/shared-docs/pipeline/shared/marked/test/BUILD.bazel +++ b/adev/shared-docs/pipeline/shared/marked/test/BUILD.bazel @@ -4,5 +4,8 @@ ts_project( name = "renderer_context", srcs = ["renderer-context.mts"], visibility = ["//adev/shared-docs/pipeline/shared/marked/test:__subpackages__"], - deps = ["//adev/shared-docs/pipeline/shared/marked"], + deps = [ + "//adev/shared-docs/pipeline/shared:shiki", + "//adev/shared-docs/pipeline/shared/marked", + ], ) diff --git a/devtools/projects/ng-devtools/src/lib/application-providers/settings_provider.ts b/devtools/projects/ng-devtools/src/lib/application-providers/settings_provider.ts index f54dc694f4a..1445a886e46 100644 --- a/devtools/projects/ng-devtools/src/lib/application-providers/settings_provider.ts +++ b/devtools/projects/ng-devtools/src/lib/application-providers/settings_provider.ts @@ -36,7 +36,7 @@ export function provideSettings(): (Provider | EnvironmentProviders)[] { } /** - * Migrrates the provided data to the latest data format, if needed. + * Migrates the provided data to the latest data format, if needed. * Returns a new object with the migrated data. * * @param data Non-migrated data diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/devtools-tabs.component.html b/devtools/projects/ng-devtools/src/lib/devtools-tabs/devtools-tabs.component.html index 9e7e54132b6..bd62ef75487 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/devtools-tabs.component.html +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/devtools-tabs.component.html @@ -67,7 +67,6 @@ [showCommentNodes]="showCommentNodes()" [isHydrationEnabled]="isHydrationEnabled()" (toggleInspector)="toggleInspector()" - [signalGraphEnabled]="signalGraphEnabled()" /> @let profilerVisible = activeTab() === 'Profiler'; diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.component.html b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.component.html index a9e2bdb3dbb..1a0d2a46b0a 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.component.html +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.component.html @@ -43,7 +43,6 @@ @if (currentSelectedElement(); as currentSelectedElement) { (); - readonly currentSelectedElement = signal(null); readonly forest = signal([]); readonly splitDirection = signal<'horizontal' | 'vertical'>('horizontal'); diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/BUILD.bazel b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/BUILD.bazel index b321fa20958..2c307cfff8f 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/BUILD.bazel +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/BUILD.bazel @@ -41,6 +41,7 @@ ng_project( "//:node_modules/@angular/core", "//:node_modules/@angular/material", "//:node_modules/rxjs", + "//devtools/projects/ng-devtools/src/lib/application-services:settings", "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/index-forest", "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver", "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/defer-view", diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab-header.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab-header.component.ts index 78b1082fed8..e62575561f8 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab-header.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab-header.component.ts @@ -6,13 +6,14 @@ * found in the LICENSE file at https://angular.dev/license */ -import {ChangeDetectionStrategy, Component, input, output, signal} from '@angular/core'; +import {ChangeDetectionStrategy, Component, input, output, signal, inject} from '@angular/core'; import {MatExpansionModule} from '@angular/material/expansion'; import {MatIcon} from '@angular/material/icon'; import {IndexedNode} from '../directive-forest/index-forest'; import {ComponentMetadataComponent} from './component-metadata.component'; import {ButtonComponent} from '../../../shared/button/button.component'; +import {Settings} from '../../../application-services/settings'; @Component({ templateUrl: './property-tab-header.component.html', @@ -22,9 +23,12 @@ import {ButtonComponent} from '../../../shared/button/button.component'; imports: [MatExpansionModule, MatIcon, ComponentMetadataComponent, ButtonComponent], }) export class PropertyTabHeaderComponent { + private readonly settings = inject(Settings); + protected readonly currentSelectedElement = input.required(); - protected readonly signalGraphEnabled = input.required(); protected readonly showSignalGraph = output(); protected readonly expanded = signal(false); + + protected readonly signalGraphEnabled = this.settings.signalGraphEnabled; } diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab.component.html b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab.component.html index be039b86e65..0f0a25ee626 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab.component.html +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab.component.html @@ -3,7 +3,6 @@ @if (currentSelectedElement) { @let hydration = currentSelectedElement.hydration; diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab.component.ts index fa4f7c7ffc7..911f08e5c2a 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab.component.ts @@ -24,7 +24,6 @@ import {DeferViewComponent} from './defer-view/defer-view.component'; }) export class PropertyTabComponent { readonly currentSelectedElement = input.required(); - readonly signalGraphEnabled = input.required(); readonly viewSource = output(); readonly inspect = output<{node: FlatNode; directivePosition: DirectivePosition}>(); diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/BUILD.bazel b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/BUILD.bazel index 00deb7ffa32..25184bc3a1a 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/BUILD.bazel +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/BUILD.bazel @@ -61,6 +61,7 @@ ng_project( "//devtools/projects/ng-devtools/src/lib/application-environment", "//devtools/projects/ng-devtools/src/lib/application-providers:supported_apis", "//devtools/projects/ng-devtools/src/lib/application-services:frame_manager", + "//devtools/projects/ng-devtools/src/lib/application-services:settings", "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/index-forest", "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver", "//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/resolution-path", diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-tab-body.component.html b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-tab-body.component.html index 58a6ee86ef5..3ab3efd95b0 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-tab-body.component.html +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-tab-body.component.html @@ -5,7 +5,6 @@ (viewSource)="viewSource.emit(directive.name)" (showSignalGraph)="showSignalGraph.emit($event)" [directive]="directive" - [signalGraphEnabled]="signalGraphEnabled()" /> } diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-tab-body.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-tab-body.component.ts index 445dc89f957..277622179fe 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-tab-body.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-tab-body.component.ts @@ -22,7 +22,6 @@ import {PropertyViewComponent} from './property-view.component'; }) export class PropertyTabBodyComponent { readonly currentSelectedElement = input.required(); - readonly signalGraphEnabled = input.required(); readonly inspect = output<{node: FlatNode; directivePosition: DirectivePosition}>(); readonly viewSource = output(); diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body.component.html b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body.component.html index 16e9487e8a6..769921c1bd6 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body.component.html +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-body.component.html @@ -24,7 +24,6 @@ (); readonly directiveOutputControls = input.required(); readonly directiveStateControls = input.required(); - readonly signalGraphEnabled = input.required(); readonly inspect = output<{node: FlatNode; directivePosition: DirectivePosition}>(); readonly showSignalGraph = output(); diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-tree.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-tree.component.ts index 0b685781531..18166d703c3 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-tree.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view-tree.component.ts @@ -20,6 +20,7 @@ import {MatTree, MatTreeNode, MatTreeNodeDef, MatTreeNodePadding} from '@angular import {SUPPORTED_APIS} from '../../../../application-providers/supported_apis'; import {SignalGraphManager} from '../../signal-graph/signal-graph-manager'; import {DebugSignalGraphNode} from '../../../../../../../protocol'; +import {Settings} from '../../../../application-services/settings'; @Component({ selector: 'ng-property-view-tree', @@ -41,14 +42,16 @@ import {DebugSignalGraphNode} from '../../../../../../../protocol'; export class PropertyViewTreeComponent { protected readonly supportedApis = inject(SUPPORTED_APIS); private readonly signalGraph = inject(SignalGraphManager); + private readonly settings = inject(Settings); readonly dataSource = input.required(); readonly treeControl = input.required>(); - readonly signalGraphEnabled = input.required(); readonly updateValue = output(); readonly inspect = output(); readonly showSignalGraph = output(); + protected readonly signalGraphEnabled = this.settings.signalGraphEnabled; + hasChild = (_: number, node: FlatNode): boolean => node.expandable; toggle(node: FlatNode): void { diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view.component.html b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view.component.html index 3a6ea1a407f..0dada92df13 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view.component.html +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view.component.html @@ -8,7 +8,6 @@ [directivePropControls]="directivePropControls()!" [directiveOutputControls]="directiveOutputControls()!" [directiveStateControls]="directiveStateControls()!" - [signalGraphEnabled]="signalGraphEnabled()" (inspect)="inspect.emit($event)" (showSignalGraph)="showSignalGraph.emit($event)" > diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view.component.ts index 745fbfaba01..9d228d5ee3c 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-view/property-view.component.ts @@ -22,7 +22,6 @@ import {PropertyViewHeaderComponent} from './property-view-header.component'; }) export class PropertyViewComponent { readonly directive = input.required<{name: string}>(); - readonly signalGraphEnabled = input.required(); readonly inspect = output<{node: FlatNode; directivePosition: DirectivePosition}>(); readonly viewSource = output(); diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/recording-timeline/frame-selector/frame-selector.component.html b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/recording-timeline/frame-selector/frame-selector.component.html index e4db7b65bbb..af4dc255b81 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/recording-timeline/frame-selector/frame-selector.component.html +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/profiler/recording-timeline/frame-selector/frame-selector.component.html @@ -23,7 +23,7 @@ [class.drag-scrolling]="dragScrolling()" >
; + +// @public (undocumented) +export interface BaseEffectNode extends ReactiveNode { + // (undocumented) + cleanup(): void; + // (undocumented) + destroy(): void; + // (undocumented) + fn: () => void; + // (undocumented) + hasRun: boolean; + // (undocumented) + run(): void; +} + // @public (undocumented) export type ComputationFn = (source: S, previous?: { source: S; @@ -134,6 +151,9 @@ export interface ReactiveNode { version: Version; } +// @public (undocumented) +export function runEffect(node: BaseEffectNode): void; + // @public (undocumented) export function runPostProducerCreatedFn(node: ReactiveNode): void; diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/animations/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/animations/GOLDEN_PARTIAL.js index 4d482625d8f..424982f3c14 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/animations/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/animations/GOLDEN_PARTIAL.js @@ -32,6 +32,57 @@ export declare class MyComponent { static ɵcmp: i0.ɵɵComponentDeclaration; } +/**************************************************************************************************** + * PARTIAL FILE: animate_enter_with_structural_directive.js + ****************************************************************************************************/ +import { Component, Directive } from '@angular/core'; +import * as i0 from "@angular/core"; +export class AnyStructuralDirective { +} +AnyStructuralDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AnyStructuralDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); +AnyStructuralDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: AnyStructuralDirective, isStandalone: true, selector: "[any-structural-directive]", ngImport: i0 }); +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AnyStructuralDirective, decorators: [{ + type: Directive, + args: [{ + selector: '[any-structural-directive]', + standalone: true, + }] + }] }); +export class MyComponent { +} +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyComponent, isStandalone: true, selector: "my-component", ngImport: i0, template: ` +
+

Sliding Content

+
+ `, isInline: true, dependencies: [{ kind: "directive", type: AnyStructuralDirective, selector: "[any-structural-directive]" }] }); +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, decorators: [{ + type: Component, + args: [{ + selector: 'my-component', + imports: [AnyStructuralDirective], + standalone: true, + template: ` +
+

Sliding Content

+
+ `, + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: animate_enter_with_structural_directive.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class AnyStructuralDirective { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵdir: i0.ɵɵDirectiveDeclaration; +} +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} + /**************************************************************************************************** * PARTIAL FILE: animate_enter_with_string_host_bindings.js ****************************************************************************************************/ diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/animations/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/animations/TEST_CASES.json index 271db5ba2a4..0e0a16cddf1 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/animations/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/animations/TEST_CASES.json @@ -18,6 +18,23 @@ } ] }, + { + "description": "should generate animate enter instructions on element with a structural directive", + "inputFiles": [ + "animate_enter_with_structural_directive.ts" + ], + "expectations": [ + { + "files": [ + { + "expected": "animate_enter_with_structural_directive_template.js", + "generated": "animate_enter_with_structural_directive.js" + } + ], + "failureMessage": "Incorrect ɵɵanimateEnter() call" + } + ] + }, { "description": "should generate animate enter instructions with host binding and simple string", "inputFiles": [ diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/animations/animate_enter_with_structural_directive.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/animations/animate_enter_with_structural_directive.ts new file mode 100644 index 00000000000..0ea5d5293da --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/animations/animate_enter_with_structural_directive.ts @@ -0,0 +1,21 @@ +import {Component, Directive} from '@angular/core'; + +@Directive({ + selector: '[any-structural-directive]', + standalone: true, +}) +export class AnyStructuralDirective {} + + +@Component({ + selector: 'my-component', + imports: [AnyStructuralDirective], + standalone: true, + template: ` +
+

Sliding Content

+
+ `, +}) +export class MyComponent { +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/animations/animate_enter_with_structural_directive_template.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/animations/animate_enter_with_structural_directive_template.js new file mode 100644 index 00000000000..eeb1bb65f7d --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/animations/animate_enter_with_structural_directive_template.js @@ -0,0 +1,13 @@ +import * as i0 from "@angular/core"; +function MyComponent_p_1_Template(rf, ctx) { if (rf & 1) { + i0.ɵɵelementStart(0, "p"); + i0.ɵɵanimateEnter("slide"); + i0.ɵɵtext(1, "Sliding Content"); + i0.ɵɵelementEnd(); +} } +… +MyComponent.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 2, vars: 0, consts: [[4, "any-structural-directive"]], template: function MyComponent_Template(rf, ctx) { if (rf & 1) { + i0.ɵɵelementStart(0, "div"); + i0.ɵɵtemplate(1, MyComponent_p_1_Template, 2, 0, "p", 0); + i0.ɵɵelementEnd(); +} }, dependencies: [AnyStructuralDirective], encapsulation: 2 }); diff --git a/packages/compiler/src/render3/r3_template_transform.ts b/packages/compiler/src/render3/r3_template_transform.ts index b311e15a22f..d740a75dca9 100644 --- a/packages/compiler/src/render3/r3_template_transform.ts +++ b/packages/compiler/src/render3/r3_template_transform.ts @@ -961,6 +961,14 @@ class HtmlAstToIvyAst implements html.Visitor { return directives; } + private filterAnimationAttributes(attributes: t.TextAttribute[]): t.TextAttribute[] { + return attributes.filter((a) => !a.name.startsWith('animate.')); + } + + private filterAnimationInputs(attributes: t.BoundAttribute[]): t.BoundAttribute[] { + return attributes.filter((a) => a.type !== BindingType.Animation); + } + private wrapInTemplate( node: t.Element | t.Component | t.Content | t.Template, templateProperties: ParsedProperty[], @@ -986,8 +994,8 @@ class HtmlAstToIvyAst implements html.Visitor { }; if (node instanceof t.Element || node instanceof t.Component) { - hoistedAttrs.attributes.push(...node.attributes); - hoistedAttrs.inputs.push(...node.inputs); + hoistedAttrs.attributes.push(...this.filterAnimationAttributes(node.attributes)); + hoistedAttrs.inputs.push(...this.filterAnimationInputs(node.inputs)); hoistedAttrs.outputs.push(...node.outputs); } diff --git a/packages/core/primitives/signals/index.ts b/packages/core/primitives/signals/index.ts index bbf486238e2..4f9544b2c75 100644 --- a/packages/core/primitives/signals/index.ts +++ b/packages/core/primitives/signals/index.ts @@ -55,3 +55,4 @@ export { export {Watch, WatchCleanupFn, WatchCleanupRegisterFn, createWatch} from './src/watch'; export {setAlternateWeakRefImpl} from './src/weak_ref'; export {untracked} from './src/untracked'; +export {runEffect, BASE_EFFECT_NODE, BaseEffectNode} from './src/effect'; diff --git a/packages/core/primitives/signals/src/effect.ts b/packages/core/primitives/signals/src/effect.ts new file mode 100644 index 00000000000..acd9f484428 --- /dev/null +++ b/packages/core/primitives/signals/src/effect.ts @@ -0,0 +1,60 @@ +/** + * @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.dev/license + */ + +import { + consumerAfterComputation, + consumerBeforeComputation, + consumerPollProducersForChange, + REACTIVE_NODE, + ReactiveNode, +} from './graph'; + +/** + * An effect can, optionally, register a cleanup function. If registered, the cleanup is executed + * before the next effect run. The cleanup function makes it possible to "cancel" any work that the + * previous effect run might have started. + */ +export type EffectCleanupFn = () => void; + +/** + * A callback passed to the effect function that makes it possible to register cleanup logic. + */ +export type EffectCleanupRegisterFn = (cleanupFn: EffectCleanupFn) => void; + +export interface BaseEffectNode extends ReactiveNode { + hasRun: boolean; + fn: () => void; + destroy(): void; + cleanup(): void; + run(): void; +} + +export const BASE_EFFECT_NODE: Omit = + /* @__PURE__ */ (() => ({ + ...REACTIVE_NODE, + consumerIsAlwaysLive: true, + consumerAllowSignalWrites: true, + dirty: true, + hasRun: false, + kind: 'effect', + }))(); + +export function runEffect(node: BaseEffectNode) { + node.dirty = false; + if (node.hasRun && !consumerPollProducersForChange(node)) { + return; + } + node.hasRun = true; + const prevNode = consumerBeforeComputation(node); + try { + node.cleanup(); + node.fn(); + } finally { + consumerAfterComputation(node, prevNode); + } +} diff --git a/packages/core/src/render3/instructions/animation.ts b/packages/core/src/render3/instructions/animation.ts index 79048883338..d530e74d873 100644 --- a/packages/core/src/render3/instructions/animation.ts +++ b/packages/core/src/render3/instructions/animation.ts @@ -166,6 +166,9 @@ export function ɵɵanimateEnter(value: string | Function): typeof ɵɵanimateEn const tNode = getCurrentTNode()!; const nativeElement = getNativeByTNode(tNode, lView) as HTMLElement; + + ngDevMode && assertElementNodes(nativeElement, 'animate.enter'); + const renderer = lView[RENDERER]; const ngZone = lView[INJECTOR]!.get(NgZone); @@ -270,6 +273,8 @@ export function ɵɵanimateEnterListener(value: AnimationFunction): typeof ɵɵa const tNode = getCurrentTNode()!; const nativeElement = getNativeByTNode(tNode, lView) as HTMLElement; + ngDevMode && assertElementNodes(nativeElement, 'animate.enter'); + cancelLeavingNodes(tNode, lView); value.call(lView[CONTEXT], {target: nativeElement, animationComplete: noOpAnimationComplete}); @@ -306,6 +311,8 @@ export function ɵɵanimateLeave(value: string | Function): typeof ɵɵanimateLe const tNode = getCurrentTNode()!; const nativeElement = getNativeByTNode(tNode, lView) as Element; + ngDevMode && assertElementNodes(nativeElement, 'animate.leave'); + // This instruction is called in the update pass. const renderer = lView[RENDERER]; const elementRegistry = getAnimationElementRemovalRegistry(); @@ -374,9 +381,7 @@ export function ɵɵanimateLeaveListener(value: AnimationFunction): typeof ɵɵa const tView = getTView(); const nativeElement = getNativeByTNode(tNode, lView) as Element; - if ((nativeElement as Node).nodeType !== Node.ELEMENT_NODE) { - return ɵɵanimateLeaveListener; - } + ngDevMode && assertElementNodes(nativeElement, 'animate.leave'); const elementRegistry = getAnimationElementRemovalRegistry(); ngDevMode && @@ -524,6 +529,15 @@ function assertAnimationTypes(value: string | Function, instruction: string) { } } +function assertElementNodes(nativeElement: Element, instruction: string) { + if ((nativeElement as Node).nodeType !== Node.ELEMENT_NODE) { + throw new RuntimeError( + RuntimeErrorCode.ANIMATE_INVALID_VALUE, + `'${instruction}' can only be used on an element node, got ${stringify((nativeElement as Node).nodeType)}`, + ); + } +} + /** * This function actually adds the classes that animate element that's leaving the DOM. * Once it finishes, it calls the remove function that was provided by the DOM renderer. diff --git a/packages/core/src/render3/reactivity/after_render_effect.ts b/packages/core/src/render3/reactivity/after_render_effect.ts index 5c41d678eeb..11954d3423c 100644 --- a/packages/core/src/render3/reactivity/after_render_effect.ts +++ b/packages/core/src/render3/reactivity/after_render_effect.ts @@ -16,10 +16,8 @@ import { SIGNAL_NODE, type SignalNode, } from '../../../primitives/signals'; - -import {type Signal} from '../reactivity/api'; import {type EffectCleanupFn, type EffectCleanupRegisterFn} from './effect'; - +import {type Signal} from '../reactivity/api'; import {TracingService, TracingSnapshot} from '../../application/tracing'; import { ChangeDetectionScheduler, diff --git a/packages/core/src/render3/reactivity/effect.ts b/packages/core/src/render3/reactivity/effect.ts index 8d869be118d..8aa7eefb02e 100644 --- a/packages/core/src/render3/reactivity/effect.ts +++ b/packages/core/src/render3/reactivity/effect.ts @@ -7,15 +7,13 @@ */ import { - REACTIVE_NODE, - ReactiveNode, SIGNAL, - consumerAfterComputation, - consumerBeforeComputation, consumerDestroy, - consumerPollProducersForChange, isInNotificationPhase, setActiveConsumer, + BaseEffectNode, + BASE_EFFECT_NODE, + runEffect, } from '../../../primitives/signals'; import {FLAGS, LViewFlags, LView, EFFECTS} from '../interfaces/view'; import {markAncestorsForTraversal} from '../util/view_utils'; @@ -191,17 +189,12 @@ export function effect( return effectRef; } -export interface EffectNode extends ReactiveNode, SchedulableEffect { - hasRun: boolean; +export interface EffectNode extends BaseEffectNode, SchedulableEffect { cleanupFns: EffectCleanupFn[] | undefined; injector: Injector; notifier: ChangeDetectionScheduler; onDestroyFn: () => void; - fn: (cleanupFn: EffectCleanupRegisterFn) => void; - run(): void; - destroy(): void; - maybeCleanup(): void; } export interface ViewEffectNode extends EffectNode { @@ -212,47 +205,27 @@ export interface RootEffectNode extends EffectNode { scheduler: EffectScheduler; } -export const BASE_EFFECT_NODE: Omit = +export const EFFECT_NODE: Omit = /* @__PURE__ */ (() => ({ - ...REACTIVE_NODE, - consumerIsAlwaysLive: true, - consumerAllowSignalWrites: true, - dirty: true, - hasRun: false, + ...BASE_EFFECT_NODE, cleanupFns: undefined, zone: null, - kind: 'effect', onDestroyFn: noop, run(this: EffectNode): void { - this.dirty = false; - if (ngDevMode && isInNotificationPhase()) { throw new Error(`Schedulers cannot synchronously execute watches while scheduling.`); } - - if (this.hasRun && !consumerPollProducersForChange(this)) { - return; - } - this.hasRun = true; - - const registerCleanupFn: EffectCleanupRegisterFn = (cleanupFn) => - (this.cleanupFns ??= []).push(cleanupFn); - - const prevNode = consumerBeforeComputation(this); - // We clear `setIsRefreshingViews` so that `markForCheck()` within the body of an effect will // cause CD to reach the component in question. const prevRefreshingViews = setIsRefreshingViews(false); try { - this.maybeCleanup(); - this.fn(registerCleanupFn); + runEffect(this); } finally { setIsRefreshingViews(prevRefreshingViews); - consumerAfterComputation(this, prevNode); } }, - maybeCleanup(this: EffectNode): void { + cleanup(this: EffectNode): void { if (!this.cleanupFns?.length) { return; } @@ -273,7 +246,7 @@ export const BASE_EFFECT_NODE: Omit = /* @__PURE__ */ (() => ({ - ...BASE_EFFECT_NODE, + ...EFFECT_NODE, consumerMarkedDirty(this: RootEffectNode) { this.scheduler.schedule(this); this.notifier.notify(NotificationSource.RootEffect); @@ -281,14 +254,14 @@ export const ROOT_EFFECT_NODE: Omit = /* @__PURE__ */ (() => ({ - ...BASE_EFFECT_NODE, + ...EFFECT_NODE, consumerMarkedDirty(this: ViewEffectNode): void { this.view[FLAGS] |= LViewFlags.HasChildViewsToRefresh; markAncestorsForTraversal(this.view); @@ -297,7 +270,7 @@ export const VIEW_EFFECT_NODE: Omit void) { + return () => { + fn((cleanupFn) => (node.cleanupFns ??= []).push(cleanupFn)); + }; +} diff --git a/tools/bazel/ts_project_interop.bzl b/tools/bazel/ts_project_interop.bzl index 729e838fef7..f78e2490505 100644 --- a/tools/bazel/ts_project_interop.bzl +++ b/tools/bazel/ts_project_interop.bzl @@ -4,11 +4,10 @@ load("@rules_angular//src/ts_project:index.bzl", _ts_project = "ts_project") def ts_project( name, deps = [], + srcs = [], tsconfig = None, testonly = False, visibility = None, - # TODO: Enable this for all `ts_project` targets at end of migration. - ignore_strict_deps = True, rule_impl = _ts_project, **kwargs): rule_impl( @@ -17,13 +16,14 @@ def ts_project( declaration = True, tsconfig = tsconfig, visibility = visibility, + srcs = srcs, deps = deps, **kwargs ) - if not ignore_strict_deps: - strict_deps_test( - name = "%s_strict_deps_test" % name, - srcs = kwargs.get("srcs", []), - deps = deps, - ) + strict_deps_test( + name = "%s_strict_deps_test" % name, + srcs = srcs, + tsconfig = tsconfig, + deps = deps, + )