Split out of a Codex review finding on #455. Pre-existing — applies equally to the template locator merged in #449.
Symptom
The @ng/component HMR endpoint resolves a class's own templateUrl and styleUrl(s) by scanning the decorator text. The Rust extractor statically folds same-file constants and interpolated template literals; the text scan cannot.
const DIR = './themes'
@Component({ styleUrls: [`${DIR}/a.css`, SHARED_STYLE] })
Neither entry yields a string literal, so the per-class answer is "unknown" and the endpoint falls back to the file-level styleUrls union. In a single-component file that is harmless. In a multi-component file it reintroduces exactly the cross-class contamination #451 removed.
Current behavior is deliberate, not accidental
#455 makes the fallback explicit and distinguishes three states:
| decorator state |
served |
| not locatable |
file-level fallback |
| locatable, no style field |
nothing |
| field present, no string literal |
file-level fallback |
The third row is the const case. Falling back is strictly better than serving empty — a component keeps working — but it is not per-class correct.
Scope
This is not style-specific. extractTemplateUrlFor (from #449) has the same limitation, and falls back to templateUrls[0].
Suggested fix
Expose per-class resolved resources from the existing AST metadata extraction — extract_component_metadata_sync already returns templateUrl/styleUrls per class with constants folded — and have the endpoint consult that instead of re-scanning source text. That removes the text locators from the resolution path entirely rather than teaching them to fold constants.
Worth checking first how the endpoint would get that metadata cheaply: it currently calls extractComponentUrls per request, and componentMetadataCache exists but stores stripped source, not parsed metadata.
Split out of a Codex review finding on #455. Pre-existing — applies equally to the template locator merged in #449.
Symptom
The
@ng/componentHMR endpoint resolves a class's owntemplateUrlandstyleUrl(s)by scanning the decorator text. The Rust extractor statically folds same-file constants and interpolated template literals; the text scan cannot.Neither entry yields a string literal, so the per-class answer is "unknown" and the endpoint falls back to the file-level
styleUrlsunion. In a single-component file that is harmless. In a multi-component file it reintroduces exactly the cross-class contamination #451 removed.Current behavior is deliberate, not accidental
#455 makes the fallback explicit and distinguishes three states:
The third row is the const case. Falling back is strictly better than serving empty — a component keeps working — but it is not per-class correct.
Scope
This is not style-specific.
extractTemplateUrlFor(from #449) has the same limitation, and falls back totemplateUrls[0].Suggested fix
Expose per-class resolved resources from the existing AST metadata extraction —
extract_component_metadata_syncalready returnstemplateUrl/styleUrlsper class with constants folded — and have the endpoint consult that instead of re-scanning source text. That removes the text locators from the resolution path entirely rather than teaching them to fold constants.Worth checking first how the endpoint would get that metadata cheaply: it currently calls
extractComponentUrlsper request, andcomponentMetadataCacheexists but stores stripped source, not parsed metadata.