8000 Missing `standalone` components declared with `button[custom-button]` are not reported by the compiler · Issue #46351 · angular/angular · GitHub
[go: up one dir, main page]

Skip to content

Missing standalone components declared with button[custom-button] are not reported by the compiler #46351

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
Lonli-Lokli opened this issue Jun 13, 2022 · 43 comments
Labels
area: compiler Issues related to `ngc`, Angular's template compiler area: core Issues related to the framework runtime compiler: template type-checking core: directive matching cross-cutting: standalone Issues related to the NgModule-less world P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent
Milestone

Comments

@Lonli-Lokli
Copy link

Which @angular/* package(s) are the source of the bug?

core

Is this a regression?

No

Description

When standalone component is created with @Component({ selector: 'button[cv-button]' it will be not reported during compile time

Please provide a link to a minimal reproduction of the bug

https://stackblitz.com/edit/angular-standalone-uzrsmn?file=src%2Fmain.ts

Please provide the exception or error you saw

No message is displayed in Console about missing info, uncomment 59 line to make component work

Please provide the environment you discovered this bug in (run ng version)

Angular CLI: 14.0.0
Node: 14.18.2
Package Manager: yarn 3.2.0
OS: win32 x64

Angular: 14.0.0
... animations, cli, common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... router, service-worker

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1400.0
@angular-devkit/build-angular   14.0.0
@angular-devkit/core            14.0.0
@angular-devkit/schematics      14.0.0
@schematics/angular             14.0.0
rxjs                            7.5.5
typescript                      4.7.3

Anything else?

No response

@pkozlowski-opensource
Copy link
Member

Yes, this is the "blind spot" in our template syntax. Essentially we can't know, from the syntax, if <button cv-button> means:

  • button element with the cv-button directive;
  • button element with the cv-button attribute (ex. added for the styling purposes).

There is a longer discussion on the topic in #3425

@pkozlowski-opensource pkozlowski-opensource added area: core Issues related to the framework runtime core: directive matching cross-cutting: standalone Issues related to the NgModule-less world labels Jun 13, 2022
@ngbot ngbot bot added this to the needsTriage milestone Jun 13, 2022
@Lonli-Lokli
Copy link
Author
Lonli-Lokli commented Jun 13, 2022

@pkozlowski-opensource Thanks for clarification.

I am observing similar behaviour with sugar-style directives (*ngIf for example), is it the same reason?

Example - https://stackblitz.com/edit/angular-standalone-4tmeb9?file=src%2Fmain.ts,src%2Fapp%2Fimage.component.ts
Uncomment line 72 to make it work

EDIT:
Also with ()-output directives
Example https://stackblitz.com/edit/angular-standalone-mdcjmm?file=src%2Fmain.ts,src%2Fapp%2Fimage.component.ts
Uncomment linee 96 to make it work

@pkozlowski-opensource
Copy link
Member

Yes, directives matching on even handlers (ex. (foo)="doSth()") will suffer from the same problem as again - we can't say, from the syntax itself, if (foo) is meant to be an event or a directive. In short - there are situations were we are not sure if a user meant "a directive" or just an attribute / event handler.

The *ngIf situation is a bit more complex but sometimes it is equivalent to the attribute case (if you write <div *ngIf> it will de-sugar to <ng-template ngIf><div></div></ng-template>. We are, most probably, going to improve the situation here with some errors specific to the control-flow directives.

cc @AndrewKushnir

@Lonli-Lokli
Copy link
Author
Lonli-Lokli commented Jun 13, 2022

If it's not possible for compiler, maybe it make sense to pass this to developer?
Report such things always as warnings, but allow developer's customization as it's done with eslint/stylelint

preudo code from future .angularlint

"@angular-lint/directives": [
          "error",
          {
            "reportPattern": "cv-**"
          }
        ],

@alxhub
Copy link
Member
alxhub commented Jun 14, 2022

#46146 will be in 14.1 which adds this warning for ngIf & friends.

@jnizet
Copy link
Contributor
jnizet commented Jul 1, 2022

I was asked to copy and paste here the description of a duplicate issue I opened (#46661), so here it goes:

If a directive has an attribute selector and the attribute is used without square brackets, such as routerLink="/users", and we forget to import the directive, or the module defining the directive, then the compiler doesn't complain at all, and we end up with an unnoticed bug.

When using NgModules, such a bug happens rarely, because you rarely forget to import the router module (for example) in your main or feature module. But when using standalone components, it's much more frequent, because the import needs to be repeated on every component that needs the directive, and of course we tend to only import what's absolutely needed.

After migrating two relatively small applications to standalone components, I've introduced such a bug, especially with routerLink, at least twice.

I understand why the compiler doesn't complain, but this, IMHO, is a source of hard to detect bugs: I doubt most people have integration tests checking that each and every link in their components works as expected. And even at runtime, the bug goes unnoticed until you click on the link and realize it doesn't work.
Proposed solution

It would be nice to be able to activate some compiler option that would, for example, check that any non-standard HTML attribute used in a component (such as routerLink) matches with a known directive selector for that component.

Or to be able to provide a list of selectors or attributes that must be checked.

@joergbaier
Copy link
joergbaier commented Aug 31, 2022

+💯 huge bug source... I'd love to see a strict build option that complains about any "unknown attribute". This way we are sure that imports are not missing.

Rather than maintaining a list of all known html attributes (if that is the main concern/difficulty)... at least give us a "allowedAttributes: []" where we can specify which attributes are safe to ignore (do not require an import).

@joergbaier
Copy link

In short - there are situations were we are not sure if a user meant "a directive" or just an attribute / event handler.

To me this a pretty big design flaw 🤔

@JoostK JoostK added compiler: template type-checking area: compiler Issues related to `ngc`, Angular's template compiler labels Sep 7, 2022
@alxhub alxhub added the P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent label Nov 16, 2022
@ngbot ngbot bot modified the milestones: needsTriage, Backlog Nov 16, 2022
@jpduckwo
Copy link

I don't know if this is the same - I think might be another use case. We have lots of custom form control components to use inside mat-form-field. The compiler also doesn't detect these as being missed if they are in the template but not imported in the standalone component. Should I raise a new issue for this or is the underlying problem the same?

Here app-rich-text-form-control isn't imported but the compiler doesn't pick it up. Fails at runtime

  standalone: true,
  imports: [CommonModule, ...MATERIAL_COMMON],
  template: `
    <mat-form-field appearance="fill">
      <app-rich-text-form-control></app-rich-text-form-control>
    </mat-form-field>
    ...

@kyjus25
Copy link
kyjus25 commented Jun 19, 2023

+1 for major pain point with standalone components

@kyjus25
Copy link
kyjus25 commented Jun 21, 2023

Just wanted to mention for everyone here, Webstorm (specifically the EAP version is what I tried) DOES pick up these warnings in the editor. Might save you a headache or two after you convert everything to standalone to load up the 30 day Webstorm trial and comb through your HTML files

Screenshot 2023-06-21 at 11 39 53 AM

@softgandhi
Copy link

I am also feeling sad when migrated but missing the tool support.

thanks @kyjus25 for alternatives.
looking for the official angular plugin to help sort out missing directives and components.

@PowerKiKi
Copy link
Contributor

My suggestion would be to solve this with a new extended diagnosis. The new diagnosis would warns for unknown tags and unknown attributes. Each project can gradually configure the diagnosis with a list of allowed unknown tags and allowed unknown attributes. In addition users can opt-in for this rule to break the build by configuring the diagnostic as an error, to be really strict.

Of course this is not an ideal solution, but it should be relatively simple to implement. And it would be better than runtime errors, and so much better than runtime lack of functionality (as seen with routerLink).

@kyjus25
Copy link
kyjus25 commented Jul 24, 2024

@eneajaho Sorry to bother. I pulled your https://github.com/eneajaho/angular/tree/feat/potential-directives-in-templates branch down and fumbled my way thru getting the proper test to run, but the directives array came up empty. Was this your conclusion as well or did I do something wrong? I was going to see if I could assist but from what I saw you had configured the test right but the function didn't seem to work.

@eneajaho
Copy link
Contributor

@eneajaho Sorry to bother. I pulled your https://github.com/eneajaho/angular/tree/feat/potential-directives-in-templates branch down and fumbled my way thru getting the proper test to run, but the directives array came up empty. Was this your conclusion as well or did I do something wrong? I was going to see if I could assist but from what I saw you had configured the test right but the function didn't seem to work.

I came to same conclusion, but I need to check more on that.

@kyjus25
Copy link
kyjus25 commented Sep 6, 2024

< 8000 a class="d-inline-block" data-hovercard-type="user" data-hovercard-url="/users/jeandat/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="/jeandat">@jeandat
Copy link
jeandat commented Oct 11, 2024

Guys, this ticket seems to be very specific about some selectors but personally I have the same issue with any component. In a standalone component's template, if I forget to add an import, there is no compile time error but it breaks at runtime. I don't find an issue in this repo for that. Am I the only one having this problem for all components (not ones with special selectors)?

@PowerKiKi
Copy link
Contributor

@jeandat, the more generic issue #51064 was merged into this one. It is indeed not about a specific selector, but pretty much anything that may appear in a template.

@blidblid
Copy link
blidblid commented Nov 11, 2024

This seems like an issue beyond directives. If a component hosts a directive, it does not give compilation errors:

      <!-- Compilation error. -->
      <app-component-that-does-not-exist></app-component-that-does-not-exist>

      <!-- Runtime error. -->
      <app-component-that-does-not-exist
        [formControl]="formControl"
      ></app-component-that-does-not-exist>

#51064 should probably be re-opened.

@jpike88
Copy link
jpike88 commented Nov 12, 2024 6D40

@thePunderWoman this is an incredibly urgent problem. I don't get it, the angular team wants everyone to migrate to standalone architecture... yet we should expect that directives of existing libraries should just silently fail like this?

On making new components, using the directive mat-raised-button for example just don't do anything unless we remember to import the right library... this flaw extends to angular components.

We don't use eslint because of how slow it is (we use biome instead) so can't benefit from any 3rd party provided checks here.

angular/vscode-ng-language-service#1859 (comment) seems this problem was picked up at least 18 months ago... nothing was done about it.

@AlbertoMontalesi we have to just be extremely careful when authoring new components or adding directives to existing ones. I have no idea how we can thoroughly catch errors though.

mentioned @devversion from vscode angular extension team to call attention to this

@JeanMeche
Copy link
Member

@jpike88 The team is very aware of this issue. The main problem behind this feature are the false-positives. There are no difference between a random attribute and a directive selector.

@jpike88
Copy link
jpike88 commented Nov 12, 2024

Seems to me that could be a feature not a bug at least in its initial implementation. Using non-directive attributes is something we don't do as a rule.

@nsmithdev
Copy link

What about an opt in flag to enable it.

@kyjus25
Copy link
kyjus25 commented Nov 12, 2024

So far it seems the current approach to the fix is to suggest changing the authoring format so that it is no longer a blind spot

Image

https://x.com/Jean__Meche/status/1854102697370083474

@jpduckwo
Copy link

What about an opt in flag to enable it.

There could be something in this I reckon, maybe a whitelist (inc regex) of general attributes in angular.json, then everything else gets validated. That way you can customise to what you need.

@jpike88
Copy link
jpike88 commented Nov 19, 2024

@jpike88 The team is very aware of this issue. The F438 main problem behind this feature are the false-positives. There are no difference between a random attribute and a directive selector.

@JeanMeche Can you follow up on this comment? In what scenarios is it good/acceptable practice to be peppering elements with attributes that aren't HTML DOM or directives? I can only think of perhaps data- prefixed stuff for easy looking in e2e testing. But we do something like that with our own in-house directive.

Why can't a whitelist/regex override be provided instead as suggested?

@PowerKiKi
Copy link
Contributor

One year ago I suggested to use extended diagnostic to solve this in a "best-effort" kind of way. While it requires some manual configuration, it would at least offers us a relatively high confidence that components/directives code is actually executed. But I don't think the core team gave any feedback about that...

So I'm joining @jpike88 in asking a slightly more elaborated feedback from the core team. Are you actually working on something ? roughly when could we expect a possible solution, 2025 ? is "best-effort" not acceptable ? should the community create a PR ? or solve this outside of the core project ?

@eneajaho your experimental branch seems to be inactive for the past few weeks. Do you still plan to work on that ?

@shamoon
Copy link
shamoon commented Jan 5, 2025

The team is very aware of this issue. The main problem behind this feature are the false-positives. There are no difference between a random attribute and a directive selector.

With my relatively superficial understanding of all that goes into this I can see how false-positives might be a big issue, but I agree with the comments there would still be value in still exposing this information to developers (as suggested behind a flag and / or purely as informational). The current state of missing imports just causing things just break (often silently) is indeed an easy-to-miss source of bugs, as noted above.

A random thought I have (always dangerous saying those in public): if you have a component SweetComponent with selector: 'sweet-component' isn't it the case that the number of unique templates in which <sweet-component> appears should = the number of times SweetComponent is imported? Thinking about seeing if I can write some kind of script to do this in the meantime to at least try to catch some obvious cases...

Thanks to the team for all their work.

@amakhrov
Copy link
amakhrov commented Feb 7, 2025

We just got bitten by that again. After (manually) migrating select components to standalone, we missed the MatButton import. As a result

<button mat-button

ends up unstyled.

@jamie-pate
Copy link

Has anyone suggested flipping this on it's head?

  1. Find all directives/components currently imported by your project and compile a list of selectors for those directives
  2. Match the template against known directive/component selectors
  3. Error out if they are missing from the imports list or auto add them (eslint or schematic?)
  4. ...
  5. Profit!

Logic: If you are using a directive/component for the first time you likely won't forget to add it to imports.. if you are using it in New code or refactoring you are likely to miss it, but it's in your project elsewhere and should be easir to detect from a selector first perspective

@kyjus25
Copy link
kyjus25 commented Feb 25, 2025

Interesting take. Once you have a list of directives you could use a Bloom Filter to check all properties quickly and efficiently

In the case of a Bloom Filter, you would know very quickly if a property is not a directive 🤓

@jamie-pate
Copy link
jamie-pate commented Feb 26, 2025

Note to future readers: make sure to enable strictTemplates

This issue is a specific case where strictTemplates doesn't discover custom directives when they are used in a template without property data binding.

(I was searching for an option like strictTemplates which catches 99% of my issues when converting to standalone, it turns out it does exist!)

this stackblitz with angular 19 Confirms custom directive selectors are still not picked up by strictTemplates:true

The only way it notices that a property isn't a known property of 'button' is when you use data binding ([label]= binds to the label property instead of the label attribute of the element).
Changing your template to bind to the property instead <button cvButton [label]="'Custom Action'"></button> would cause a compiler error. (but this is not ideal)

@kyjus25
Copy link
kyjus25 commented Feb 26, 2025

@jamie-pate I've said it previously in the thread, but after you convert to standalone I recommend downloading the Webstorm trial (or free if using for personal use) which does catch unimported custom directives. VS Code is the only one it seems that doesn't. Worth the peace of mind

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: compiler Issues related to `ngc`, Angular's template compiler area: core Issues related to the framework runtime compiler: template type-checking core: directive matching cross-cutting: standalone Issues related to the NgModule-less world P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent
Projects
None yet
Development

No branches or pull requests

0