Skip to content

refactor(compiler): add support for host directives - #46868

Closed
crisbeto wants to merge 8 commits into
angular:mainfrom
crisbeto:host-directives-compiler
Closed

crisbeto wants to merge 8 commits into
angular:mainfrom
crisbeto:host-directives-compiler

Conversation

@crisbeto

@crisbeto crisbeto commented Jul 18, 2022 •

Copy link
Copy Markdown
Member

This is the compile-time implementation of the hostDirectives feature plus a little bit of runtime code to illustrate how the newly-generated code will plug into the runtime. It works by creating a call to the new ɵɵHostDirectivesFeature feature whenever a directive has a hostDirectives field. Afterwards ɵɵHostDirectivesFeature will patch a new function onto the directive definition that will be invoked during directive matching.

For example, if we take the following definition:

@Directive({
  hostDirectives: [HostA, {directive: HostB, inputs: ['input: alias']}]
})
class MyDir {}

Will compile to:

MyDir.ɵdir = ɵɵdefineComponent({
  features: [ɵɵHostDirectivesFeature([HostA, {
    directive: HostB,
    inputs: ['input', 'alias']
  }])]
});

The template type checking is implemented during directive matching by adding the host directives applied on the host to the array of matched directives whenever the host is matched in a template.

Relates to #8785.

@crisbeto
crisbeto force-pushed the host-directives-compiler branch 4 times, most recently from 50f29c1 to 554e814 Compare July 18, 2022 12:20
@crisbeto crisbeto added action: review The PR is still awaiting reviews from at least one requested reviewer target: minor This PR is targeted for the next minor release labels Jul 18, 2022
@crisbeto
crisbeto marked this pull request as ready for review July 18, 2022 12:41
@pullapprove
pullapprove Bot requested review from atscott and gkalpak July 18, 2022 12:41
@crisbeto
crisbeto requested a review from alxhub July 18, 2022 12:42
@eneajaho

This comment was marked as off-topic.

@crisbeto

This comment was marked as off-topic.

@jessicajaniuk jessicajaniuk added the area: compiler Issues related to `ngc`, Angular's template compiler label Jul 18, 2022
@ngbot ngbot Bot added this to the Backlog milestone Jul 18, 2022
@nartc

This comment was marked as off-topic.

@crisbeto

This comment was marked as off-topic.

@nartc

This comment was marked as off-topic.

@crisbeto

This comment was marked as off-topic.

@alxhub alxhub left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First round of review! Overall this is in pretty good shape.

A few meta-comments about the PR as a whole:

  • Ideally, we would split this up into multiple commits.

For example, one commit could deal with adding the extra metadata to .d.ts files (starting as never always), another could do the parsing, another the template type-checking, etc.

  • This should not be a feat commit if the feature is not actually implemented.

Instead, make it a refactor commit. We don't want any entry in the changelog until the feature is usable to users. For a big implementation like this, we can have multiple PRs adding implementation details that aren't exposed, and then a final feat commit which only makes the API public.

  • On that note, we shouldn't also add hostDirectives to the public @Directive metadata just yet.

That is, we shouldn't change the public API and allow users to access the feature until it's fully implemented and working.

@crisbeto

Copy link
Copy Markdown
Member Author

Regarding splitting it up into multiple commits: I agree that this is a huge change, but I ended up going with the single commit, because the majority of it are the actual tests, not the compiler code, and in order to make some tests work, most of the compiler changes would have to be in place.

Regarding the commit message: I can target this to the main branch so it won't show up in the changelog until we release it. Alternatively I can target it to a feature branch instead.

Regarding exposing the new API in the @Directive metadata: I exposed it, because some of the tests are basically testing the entire pipeline end-to-end and I wanted to avoid compilation errors. The code won't be released for a while and even if it was passed in, the runtime isn't in place so nothing will happen.

@crisbeto crisbeto changed the title feat(compiler): add support for host directives refactor(compiler): add support for host directives Jul 22, 2022
@crisbeto
crisbeto force-pushed the host-directives-compiler branch 3 times, most recently from 1b42abd to 9a0bbf5 Compare July 23, 2022 15:47
@crisbeto

Copy link
Copy Markdown
Member Author

@alxhub I've addressed the feedback above and the things we talked about in person. All the changes are in the second fixup commit. Here's a high level overview of what has changed:

  • The commit message was switched to refactor so it doesn't end up in the changelog until the feature has been implemented completely.
  • Instead of preserving forwardRef calls, the compiler will generate a function when there are forward references.
  • hostDirectives has been removed from the public API of @Directive and @Component until we're done implementing the feature.
  • I've added some logic to filter out the host directives from the component template dependencies, as we discussed.
  • The compiler now produces diagnostics for problems in the host directives definition, instead of throwing errors. Also the validation happens in the validate method of HostDirectivesResolver instead of in resolve.
  • I had to expand the type on a couple of utilities that deal with diagnostics from ts.Expression to ts.Node so that I could produce diagnostics with the correct context for host directives. As far as I can tell, we weren't using anything specific to Expression anyway.
  • The comments around return types and moving literals into dedicated types have been resolved.

crisbeto added a commit to crisbeto/angular that referenced this pull request Jul 31, 2022
`ComponentRef.setInput` works by calling `setInputsForProperty` internally. It can be problematic, because it will set the all inputs on the specific node matching the passed-in name, including other directives that aren't associated with the `ComponentRef`. Currently this isn't a problem, because the only way to get a `ComponentRef` is as a result of `ComponentFactory.create` and there is no way to apply other directives through there, however I see a couple of ways that it can manifest itself in the future:
1. Once the runtime for host directives is implemented (after angular#46868), users will be able to apply directives to the root component.
2. If we were to make it possible to inject `ComponentRef` in the future.

These changes add some extra logic so that the input is only assigned to the component instance that is associated with the `ComponentRef`.
@crisbeto
crisbeto force-pushed the host-directives-compiler branch 2 times, most recently from 4ae9164 to 3bfea17 Compare August 3, 2022 09:29

@alxhub alxhub left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed-for: public-api

@pkozlowski-opensource pkozlowski-opensource left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Reviewed-for: public-api

This is the compile-time implementation of the `hostDirectives` feature plus a little bit of runtime code to illustrate how the newly-generated code will plug into the runtime. It works by creating a call to the new `ɵɵHostDirectivesFeature` feature whenever a directive has a `hostDirectives` field. Afterwards `ɵɵHostDirectivesFeature` will patch a new function onto the directive definition that will be invoked during directive matching.

For example, if we take the following definition:

```ts
@directive({
  hostDirectives: [HostA, {directive: HostB, inputs: ['input: alias']}]
})
class MyDir {}
```

Will compile to:

```js
MyDir.ɵdir = ɵɵdefineComponent({
  features: [ɵɵHostDirectivesFeature([HostA, {
    directive: HostB,
    inputs: {
      input: "alias"
    }
  }])]
});
```

The template type checking is implemented during directive matching by adding the host directives applied on the host to the array of matched directives whenever the host is matched in a template.

Relates to angular#8785.
@crisbeto
crisbeto force-pushed the host-directives-compiler branch from a0dff5f to 454dbd1 Compare August 22, 2022 17:47
@crisbeto crisbeto added target: major This PR is targeted for the next major release action: presubmit The PR is in need of a google3 presubmit and removed target: minor This PR is targeted for the next minor release action: review The PR is still awaiting reviews from at least one requested reviewer labels Aug 22, 2022
@crisbeto crisbeto self-assigned this Aug 22, 2022

@jessicajaniuk jessicajaniuk left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reviewed-for: public-api

@crisbeto crisbeto added action: merge The PR is ready for merge by the caretaker and removed action: presubmit The PR is in need of a google3 presubmit labels Aug 22, 2022
@crisbeto crisbeto removed their assignment Aug 22, 2022
@alxhub alxhub added target: minor This PR is targeted for the next minor release and removed target: major This PR is targeted for the next major release labels Aug 22, 2022
@alxhub

alxhub commented Aug 22, 2022

Copy link
Copy Markdown
Member

This PR was merged into the repository by commit 54ceed5.

@alxhub alxhub closed this in 54ceed5 Aug 22, 2022
@angular-automatic-lock-bot

Copy link
Copy Markdown

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot Bot locked and limited conversation to collaborators Sep 22, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

action: merge The PR is ready for merge by the caretaker area: compiler Issues related to `ngc`, Angular's template compiler target: minor This PR is targeted for the next minor release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants