Fold the separate `if (isStandalone === undefined)` fallback into the
declaration as `?? true` per review feedback. The `??` operand is
parenthesized because nullish coalescing cannot be mixed with `&&`
without parentheses; semantics are unchanged (false is not nullish).
Also link the Angular update guide (https://angular.dev/update-guide)
from the MIGRATION.md section, mirroring the Next.js section.
The experimentalZoneless version checks in build-storybook and
start-storybook were constant-true under the 21 floor; both now default
to true, matching the PropertyExtractor collapse. Explicit
experimentalZoneless: false in project options is still honored.
tsconfig-paths treats a missing baseUrl as the leaf tsconfig directory, so Nx-style path maps in tsconfig.base.json fail under webpack, the indexer, and react-docgen. Pass TypeScript's pathsBasePath instead so aliases work without restoring baseUrl.
Compodoc reports a `model()` under the same bare name in both its input
and output lists, so the parser treated a name in both lists as two-way:
it dropped the output and synthesized `${name}Change`.
An `@Input('shared')` declared next to an `@Output('shared')` collides the
same way with no `model()` involved, so a real output was being deleted
and a phantom one invented. Requiring the declaration line to match too
separates the cases, since a `model()` is one property on one line.
The rule is lifted into a named function and the quirk is written up,
rather than resting on a comment that claimed the name alone was
sufficient. A collision now costs the aliased input its row instead of
its output, which the README records as a known limitation.
Compodoc reports whether an input is required, and we never read it. We
computed requiredness from the TypeScript-optional marker instead, so every
signal input rendered as required regardless of how it was declared.
Requiredness now comes from Compodoc's flag, with the optional marker still
consulted. Both are needed: Compodoc derives its flag from the presence of the
`required` key in an `@Input({...})` argument rather than its value, so
`@Input({ required: false })` reports itself as required and optional at once,
and only requiring the two to agree keeps that case right.
A `model()` signal's synthesized change output is now always optional. It used
to inherit the requiredness of the input it derives from, which disagreed with
how every real output reads.
Plain decorator inputs still read as required. Compodoc emits neither flag for
them, which is an upstream gap; when it is fixed they correct themselves.
The code that turns Compodoc's output into argTypes existed twice, once in
each of the two Angular framework packages, as byte-identical copies. Any
fix applied to one had to be remembered for the other, and in practice they
drifted. It now lives in one place both of them share.
The extracted module no longer assumes a browser. What differs between
environments is passed in rather than reached for: the Compodoc data, the
feature flag, a logger, and the helper that unwraps Compodoc's
HTML-rendered JSDoc. Each framework's preview supplies its own.
The shared package is private and compiled into both framework packages
rather than published, so nothing new appears on npm and neither package
gains a runtime dependency.
No behaviour changes. Both frameworks' existing test suites pass untouched,
and the preview still unwraps HTML with DOMParser exactly as before.
oxlint reports these directives as unused: they suppress rules that are
either not enabled in the oxlint config (type-aware rules, dropped
plugins) or implemented with different heuristics. Verified with
`oxlint --report-unused-disable-directives` reporting zero findings in
both code/ and scripts/ afterwards.
- Remove the CHANGELOG.prerelease.md entry (reverts to next).
- public-types.test-d.ts: drop "Layer A"/"AC-X3" transcript prose,
tighten the transform-input comment.
- public-types.ts: condense the TransformComponentType JSDoc to the
load-bearing do-NOT-reorder constraint.
- compodoc.ts: broaden the model() false-positive note (inherited /
accessor splits) and document the aliased-model() autodocs gap.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Cut comments that restated the code or repeated the rationale across call site
and JSDoc; kept the load-bearing ones (ɵcmp shape, type-composition order,
compodoc both-arrays heuristic + its limitation).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Address review feedback (Karpathy + Pragmatic):
- NgComponentAnalyzer: drop the new-instance fallback (runInInjectionContext +
new component()) and its orphaned imports. Signal I/O is now read purely
statically from the compiled component definition (ɵcmp), so the analysis
path never executes user constructor code. Verified the fallback was
test-harness-only (ComputesTemplateFromComponent/compodoc/angular-properties
unaffected).
- Tests: assert the production ɵcmp reader via a synthetic ɵcmp mirroring the
AOT shape (now also covers aliased model() resolution). Drop the
over-engineered throws-if-called resolveComponentFactory guard; restore
pre-existing imports.
- Strip internal investigation jargon (Probe/AC/R6/Lxxx) from comments in
NgComponentAnalyzer, compodoc, public-types, and the model-signal stories.
- CHANGELOG: collapse to the repo one-line convention.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Refs https://github.com/storybookjs/storybook/issues/34831
Angular `model()` two-way binding signals were not surfaced in compodoc
autodocs. compodoc (verified against the captured v1.2.1 output) emits a
`model()` member as an IDENTICAL entry — same bare name, no decorators/
jsdoctags, ModelSignal<T> wrapper erased — in BOTH `inputsClass` AND
`outputsClass`. Plain @Input/input() only land in inputsClass; plain
@Output/output()/EventEmitter only in outputsClass. The reliable,
version-tolerant discriminator is therefore a property whose name appears
in BOTH arrays of the same component (the both-arrays discriminator);
compodoc emits no model() marker, so compodoc-types.ts Property is unchanged.
- extractArgTypesFromData now detects model props via the both-arrays
discriminator, suppresses compodoc's spurious bare-name outputsClass
duplicate (model surfaces as an input control), and synthesizes a
`${name}Change` output (action: '${name}Change') reusing the per-item
output shape.
- Deterministic angularFilterNonInputControls re-surface branch: the
synthesized `${name}Change` output + companion input are present with the
flag OFF, and still re-surfaced with the flag ON despite iteration being
restricted to ['inputsClass'].
- New __testfixtures__/doc-model fixture (mirrors doc-button) with the
captured compodoc JSON; angular-properties.test.ts now asserts the
synthesized colorChange/showTextChange rows for both filter states.
- doc-button EventEmitter fixture regression-guarded (untouched, still green).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extends getComponentInputsOutputs with an additive dual detection path so a
model() field surfaces BOTH its `color` input and the compiler-synthesized
`colorChange` output, without altering @Input/@Output/input()/output()/
EventEmitter results from the decorator path.
Strategy (Probe C: esbuild/JIT strips signal AOT metadata in the unit-test
harness, so `ecmp` I/O maps are empty for signal members / `signals===false`):
- Primary: read the Angular component def via ɵgetComponentDef; ɵcmp keys the
I/O maps by template name -> propName (verified empirically), so aliased
model(x,{alias}) and model.required() resolve to their real binding names.
- Fallback: synthesize from the component instance brand (writable+subscribable
signal => model() input + ${name}Change output) for non-AOT/JIT classes.
All 3 consumers verified (no edits needed): computesTemplateFromComponent and
computesTemplateSourceFromComponent emit [color]+(colorChange) via the existing
pure I/O builders; StorybookWrapperComponent filter-inversion confirmed -- the
model input now reaches the instance through the template Input binding
(initial render + live storyProps$ updates) instead of the dropped
getNonInputsOutputsProps direct-assignment path.
Adds a NEW factory-free test block covering @Input/@Output, input(), output(),
EventEmitter, model(), model.required(), aliased model(), plus a throws-if-called
ComponentFactoryResolver guard proving zero factory invocation in model()
detection. L50-212 kept commented with a tracked TODO(angular-22).
Refs https://github.com/storybookjs/storybook/issues/34831
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds Layer A native detection of Angular model() signal outputs in the
@storybook/angular public type inference.
Refs https://github.com/storybookjs/storybook/issues/34831
- Adds AngularModelSignal / AngularHasModelSignal / ModelSignal aliases
mirroring the existing InputSignal/OutputEmitterRef conditional style.
- Adds TransformModelSignalType<T>: maps each ModelSignal<E> field to E and
synthesizes an intersection member `${prop}Change`: (e: E) => void (the
xChange member is compiler-synthesized, never a real keyof T member).
- Pins TransformComponentType composition with TransformModelSignalType as the
INNERMOST wrapper (do not reorder): the synthesized `${prop}Change` is
(e:E)=>void so it passes the outer Input/Output/Event transforms unchanged,
and since ModelSignal extends InputSignal the model value field is
idempotently re-collapsed by the outer TransformInputSignalType (no
double-transform divergence).
- Adds public-types.test-d.ts asserting the FINAL composed
TransformComponentType<C> (composed, NOT TransformModelSignalType in
isolation) for color/colorChange + model.required() + full no-regression
coverage (input(), transform input(), output(), EventEmitter, @Input,
@Output) resolving simultaneously in one type.
Documented limitation (for the AC-X3 changelog): aliased
model(prop, { alias: 'a' }) produces aChange at runtime, but Layer A can only
synthesize ${propName}Change because TypeScript cannot observe the runtime
alias. Runtime detection (Layer C) handles the alias via the resolved binding
name on ɵcmp. model.required() is fully covered.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* next: (719 commits)
Bump version from 10.4.0-alpha.14 to 10.4.0-alpha.15 [skip ci]
Write changelog for 10.4.0-alpha.15 [skip ci]
move agent detection to config hook instead of configureVitest
Bump version from 10.4.0-alpha.13 to 10.4.0-alpha.14 [skip ci]
Write changelog for 10.4.0-alpha.14 [skip ci]
Bump version from 10.4.0-alpha.12 to 10.4.0-alpha.13 [skip ci]
Revert revert
Write changelog for 10.4.0-alpha.13 [skip ci]
fix tests
revert
fix steps collection
fix formatting
Use same icon for modified and related
Replace icon for related changes
always resolve telemetry state immediately
add addon-mcp to internal UI Storybook
await the call
cleanup
fix init telemetry being delayed
Bump version from 10.4.0-alpha.11 to 10.4.0-alpha.12 [skip ci]
...
The dependencies `@angular-devkit/build-angular`, `@storybook/builder-webpack5` and `webpack` are only needed if you use webpack.
But if you use https://analogjs.org/docs/integrations/storybook to use angular + storybook via vite, then you only need `@angular/build` and `@storybook/vite` instead.
The analogjs plugin stil relies on `@storybook/angular`, but loading webpack dependencies on demand allows one to drop webpack dependencies.