Currently the `OutOfBandDiagnosticRecorder` is tied to producing TypeScript diagnostics, however some of our use cases might call for a different form.
These changes decouple the recorder from TypeScript and make the diagnostic type generic.
Add NG8023 extended diagnostic to report duplicate component selectors
during compilation.
This replaces the former NG0300 runtime error, ensuring the failure
occurs at build time instead of runtime.
Closes angular#48377
BREAKING CHANGE: Elements with multiple matching selectors will now throw at compile time.
`<input type="number">` often does not provide the desired user experience when editing numbers in
a form. MDN even [describes](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/input/number#using_number_inputs)
how text inputs should be used in many cases instead, via `<input type="text" inputmode="numeric">`
or similar configurations. Previously, this did not work with Signal Forms without a custom input
component/directive.
This PR builds support for binding `number|null` models directly to `<input type="text">` native
controls via `[formField]`. When a model has a number or `null` value, signal forms will preserve
that status when the user makes edits/changes. Empty string values are converted to `null`, other
values are parsed as numbers, and a parse error is raised when a non-numeric value is entered.
Note that it's up to the UI developer to configure additional UI affordances such as setting an
appropriate `inputmode`, rejecting non-numeric keypresses, etc.
Fixes#66903Fixes#66157
Fixes a regression caused by the recent TCB changes where we moved the type parameter processing earlier in the pipeline and stopped properly accounting for the `TcbGenericContextBehavior`.
Fixes#67704.
Optimization Goals:
The primary goals of this optimization are to dramatically reduce the execution time of the language-service test suite and stabilize the mock file system infrastructure. Previously, the suite suffered from significant overhead due to recreating the `MockFileSystem`, `MockServerHost`, and TypeScript `ProjectService` for every single test block, leading to redundant parsing operations, slow test initialization, and reduced spec performance.
How the Goals Were Achieved:
1. **Mock File System Optimizations (Shared State)**:
- Evaluated that standard test files (e.g., TS/Angular lib definitions) are immutable across tests.
- Introduced and utilized `lockMockFileSystem()` to initialize the mock `FileSystem` with `loadStandardTestFiles()` only once per test suite run rather than repeatedly per test.
- Refactored `LanguageServiceTestEnv.setup()` to reuse the singleton file system, completely skipping redundant module loading by eagerly flagging `fsInitialized = true`.
2. **Language Service Test Environment Enhancements (TypeScript Project Reuse)**:
- Implemented partial configuration reloads in the `Project` class via the `update()` method, removing the need to tear down and rebuild the entire `MockServerHost` and TypeScript `ProjectService` from scratch when minimal file changes (like HTML templates or local TS edits) are made dynamically by a test.
- Applied `projectService.reloadProjects()` and `scriptInfo.reloadFromFile()` to synchronously push mock file tree invalidations to the active TS program, skipping expensive environment initialization and saving considerable latency across tests.
- Added `projectName` identifiers inside complex isolate tests (e.g. module alias aliasing) so custom environment injections can sandbox safely without invalidating the global default environment cache.
3. **Test Suite Unification**:
- Flattened fragmented test groups (`grp1`, `grp3`, `grp4`) into a cohesive single directory at `packages/language-service/test/`. This simplifies execution config, improves test runner concurrency, and unifies local development targeting.
- Cleaned out broken inline debug logging and unneeded config reloading loops.
4. **Maintaining Test Isolation**:
- **Explicit TypeScript Configuration**: While the underlying `MockFileSystem` ("disk") is aggressively reused across tests, the TypeScript `ProjectService` and its execution environment are entirely recreated for every test run to ensure isolated ASTs and module resolution caches.
- **Strict tsconfig.json Files Array**: When a project is initialized, it explicitly defines its boundary using the strict `files: [ ... ]` array in `tsconfig.json`. This ensures that any leftover files physically on the mock disk from an older test run are completely invisible to the TS Compiler.
- **Namespace Sandboxing**: For tests doing custom modifications (e.g., overriding module resolution paths), they utilize localized `projectName` arguments (like `"test_alias_completions"`) to configure sandboxed working directories.
In some cases the `debugName` transform generates a spread into the signal function parameters. This can cause compiler errors, because the functions don't have rest parameters.
These changes work around it by adding a `@ts-ignore` above it.
This commit refactors the template type checking metadata interfaces to use detached, serializable metadata rather than retaining direct references to ts.Node or ts.Declaration instances.
A new tcb_adapter translates traditional TypeScript AST-bound metadata into these decoupled structures. This abstraction lays the groundwork for supporting native preprocessors (such as Rust or ts-go) which serialize metadata over JSON rather than passing live TypeScript objects.
Key changes:
- Introduced TcbDirectiveMetadata, TcbComponentMetadata, TcbReferenceMetadata, and TcbPipeMetadata to replace TypeCheckableDirectiveMeta where appropriate.
- Substituted deep TS compilation AST references with string module names and source spans to preserve out-of-band diagnostic capabilities.
- Detached generic typeParameters and transformType properties into synthesized, standalone TS mappings.
- Updated generateTypeCheckBlock and corresponding Operations to consume the new metadata.
The Angular compiler unconditionally adds a debug name transform for signals
which generates a conditional on `ngDevMode` (e.g., `ngDevMode ? { debugName: "xyz" } : []`).
During testing, `ngDevMode` is true, so the true branch executes but the
false branch is never executed. Consequently, coverage tools report the
false branch as an untested line/branch, preventing 100% test coverage.
This commit adds a synthetic `/* istanbul ignore next */` comment to the
generated false branch so that Istanbul ignores it. We only include the
istanbul comment (instead of additionally including c8) to focus on the
established standard for Angular CLI/Karma coverage while maintaining
compatibility with modern Vitest setups, since @vitest/coverage-v8 now
natively respects the fallback istanbul comment.
Fixes#64583
This is a follow-up to #67381 which introduced a subtle bug where depending on the type checking configuration, we may put an object literal directly in the TCB body which the TS compiler ends up interpreting as a block. These changes resolve the issue by always wrapping the literal in parentheses.
TypeScript has functionality that automatically escapes quotes in string literals. These changes update the places where we may need to do the same ourselves.
inputs & outputs cannot be binded to 2 different directives/components properties
Eg
```
data = model();
dataChange = output(); // throws because model already emits on the `dataChange` output
userSomething = input({alias 'user'});
user = input(); // throws because userSomething already binds to the `user` input
````
fixes#65844
BREAKING CHANGE: The compiler will throw when there a when inputs, outputs or model are binding to the same input/outputs.
Add help link to extended template diagnostic messages to provide
users with additional guidance and documentation resources. This
enhancement improves developer experience by making it easier to
understand and resolve complex template issues through direct
access to relevant Angular documentation with detailed examples
and explanations for each diagnostic type.
Wrap `@defer` trigger expressions (`when`, `prefetch when`, `hydrate when`)
in a conditional context within the TCB to enable TypeScript's TS2774
diagnostic for detecting functions used without invocation.
Previously, signals and functions passed to `when` triggers without
parentheses would silently evaluate to truthy, causing unexpected behavior.
Now the compiler reports an error when a function is used as a condition
without being called.
Supports binding `null` to a `<input type=number>`.
- Binding in `null` clears the input
- Binding in `NaN` also clears the input
- When the user clears the input, the model is set to `null`
- The model is _never_ set to `NaN` based on user interaction. It is
either set to `null` if the user cleared the input, or is unchanged
and a parse error added if the user entered an invalid number like
"42e"
PR Close#66917
The `isTypeOnly` property was deprecated in TypeScript and replaced by `phaseModifier`.
Updates the check to use `phaseModifier`, which is the recommended API for detecting type-only imports.
Additionally, removes the unused `metadata` parameter from the `NgModuleExtractor`
Based on the discussion in #66294: now that we support arrow functions in event listeners, developers may write out something like `(click)="() => expr"` which will be a no-op. These changes update the existing diagnostic for uninvoked expressions in listeners to account for it.
Refactors the `ɵɵcontrolCreate` and `ɵɵcontrol` instructions to delegate control logic to the forms package via new `ɵngControlCreate` and `ɵngControlUpdate` lifecycle hooks. Previously, the logic for binding form state to native elements and custom controls was hardcoded within `@angular/core`.
**Compiler Changes:**
- Introduces a new compilation phase `specializeControlProperties` (in `control_directives.ts`).
- This phase detects properties named `formField` and specializes them into `ControlCreate` and `Control` IR opcodes.
- These opcodes emit `ɵɵcontrolCreate` and `ɵɵcontrol` instructions, respectively.
**Runtime Changes:**
- `ɵɵcontrolCreate` acts as the creation phase. It locates the control directive and invokes its `ɵngControlCreate` method.
- `ɵɵcontrol` acts as the update phase, and invokes the control directive's `ɵngControlUpdate` method (if present).
- Introduces a `passThroughInput` configuration in `ControlFeature`. This specifies the input name (e.g., `formField`) that triggers the control. If the runtime detects that this input is bound to multiple targets (e.g., the `FormField` directive *and* the host component), the control is flagged as "pass-through". In this state, `ɵngControlCreate` returns a no-op update function, deferring responsibility to the other consumer (e.g., the component managing the field itself).
**Forms Changes:**
- `FormField` directive implements `ɵngControlCreate` and `ɵngControlUpdate`.
- Inside this hook, `FormField` determines the type of control it is attached to (Native, CVA, or Custom Signal Control) and delegates to the appropriate handler (`nativeControlCreate`, `cvaControlCreate`, or `customControlCreate`).
- Consolidates all form binding logic within `@angular/forms/signals`, enabling support for new `FormValueControl` and `FormCheckboxControl` interfaces.
- Reorganizes the codebase by moving `FormField` from `api/` to `directive/` and splitting the binding logic into semantic pieces:
- `control_native.ts`, `control_cva.ts`, and `control_custom.ts` contain the specific handlers for each control type.
- `native.ts` and `select.ts` provide helpers for native element discovery and select-specific synchronization.
- `bindings.ts` manages the tracking and application of property/attribute bindings.