Repository navigation
Vue3 Vite: Enable Schema Extraction for Vue Component Meta - #35565
Conversation
📝 WalkthroughWalkthroughVue component metadata now enables schema support, and Vue arg type conversion distinguishes literal union schemas from TypeScript enum member references. Tests cover enum conversion and fallback behavior. ChangesVue component schema support
Estimated code review effort: 2 (Simple) | ~10 minutes Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Could a maintainer please add the appropriate labels for this PR? Thanks! |
|
Hey @dmitriy-prokoshev, thanks a lot for this PR! ❤️ I evaluated it against our new docgen baseline harness (25 Vue fixtures recorded through the vue-component-meta path, see #35574), and the results are pretty convincing: 15 of 25 fixtures improve. Literal unions become structured enum sbTypes again (so Controls shows a dropdown), imported union aliases unfold to their members, and the One degradation showed up, though, and I would like to ask whether you are interested in fixing it as part of this PR (or a follow-up, whatever you prefer): TS enums map to member names instead of runtime values. For export enum Severity {
Info = 'info',
Warning = 'warning',
Error = 'error',
}
{ name: 'enum', value: ['Severity.Info', 'Severity.Warning', 'Severity.Error'] }Since an enum The fix would live in the enum branch of |
|
I looked into how a proper fix could look, documentation included, and argTypes already support everything we need. 🙂 The idea: keep the runtime values in options (that's what Controls passes to the component) and use severity: {
// what controls actually pass to the component
options: ['info', 'warning', 'error'],
control: {
type: 'select',
// display-only: dropdown shows the enum member names
labels: {
info: 'Severity.Info',
warning: 'Severity.Warning',
error: 'Severity.Error',
},
},
table: {
type: {
summary: 'Severity',
detail: "Severity.Info = 'info'\nSeverity.Warning = 'warning'\nSeverity.Error = 'error'",
},
},
type: { name: 'enum', value: ['info', 'warning', 'error'] },
}The dropdown shows Two notes for the implementation:
|
With schema extraction on, TS enum members arrive as reference strings (Severity.Info) without their runtime values. An enum sbType makes Controls infer a select whose options inject those name strings into the component instead of the member values. Fall back to the readable type string until the values are available; literal unions keep their structured enum.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@code/renderers/vue3/src/extractArgTypes.ts`:
- Around line 217-220: Update the literal handling in isLiteralUnionSchema
within extractArgTypes to decode each schema literal rather than removing every
quote character, preserving escaped quotes and other valid literal content. Use
the decoded values for the enum result and add a regression case covering a
literal such as "say \"hi\"" so Controls receives the correct value.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: e98c3578-d8eb-4162-aa72-76d981ff4b9e
⛔ Files ignored due to path filters (1)
code/renderers/vue3/src/__snapshots__/extractArgTypes.test.ts.snapis excluded by!**/*.snap
📒 Files selected for processing (2)
code/renderers/vue3/src/extractArgTypes.test.tscode/renderers/vue3/src/extractArgTypes.ts
Package BenchmarksCommit: The following packages have significant changes to their size or dependencies:
|
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 0 | 0 | 0 |
| Self size | 24 KB | 21 KB | 🎉 -3 KB 🎉 |
| Dependency size | 686 B | 689 B | 🚨 +3 B 🚨 |
| Bundle Size Analyzer | Link | Link |
@storybook/addon-vitest
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 2 | 2 | 0 |
| Self size | 466 KB | 427 KB | 🎉 -38 KB 🎉 |
| Dependency size | 350 KB | 350 KB | 🚨 +3 B 🚨 |
| Bundle Size Analyzer | Link | Link |
@storybook/builder-vite
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 1 | 11 | 🚨 +10 🚨 |
| Self size | 136 KB | 130 KB | 🎉 -6 KB 🎉 |
| Dependency size | 28 KB | 1.33 MB | 🚨 +1.30 MB 🚨 |
| Bundle Size Analyzer | Link | Link |
@storybook/builder-webpack5
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 182 | 182 | 0 |
| Self size | 92 KB | 79 KB | 🎉 -13 KB 🎉 |
| Dependency size | 37.43 MB | 37.81 MB | 🚨 +386 KB 🚨 |
| Bundle Size Analyzer | Link | Link |
storybook
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 73 | 73 | 0 |
| Self size | 22.04 MB | 21.31 MB | 🎉 -736 KB 🎉 |
| Dependency size | 31.20 MB | 30.98 MB | 🎉 -218 KB 🎉 |
| Bundle Size Analyzer | Link | Link |
@storybook/angular
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 185 | 185 | 0 |
| Self size | 267 KB | 160 KB | 🎉 -107 KB 🎉 |
| Dependency size | 30.29 MB | 30.66 MB | 🚨 +374 KB 🚨 |
| Bundle Size Analyzer | Link | Link |
@storybook/angular-vite
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 20 | 29 | 🚨 +9 🚨 |
| Self size | 23.08 MB | 22.83 MB | 🎉 -249 KB 🎉 |
| Dependency size | 11.49 MB | 12.69 MB | 🚨 +1.20 MB 🚨 |
| Bundle Size Analyzer | Link | Link |
@storybook/ember
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 181 | 181 | 0 |
| Self size | 13 KB | 13 KB | 🚨 +3 B 🚨 |
| Dependency size | 32.79 MB | 33.16 MB | 🚨 +374 KB 🚨 |
| Bundle Size Analyzer | Link | Link |
@storybook/html-vite
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 4 | 14 | 🚨 +10 🚨 |
| Self size | 22 KB | 22 KB | 🚨 +4 B 🚨 |
| Dependency size | 199 KB | 1.49 MB | 🚨 +1.29 MB 🚨 |
| Bundle Size Analyzer | Link | Link |
@storybook/nextjs
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 536 | 527 | 🎉 -9 🎉 |
| Self size | 642 KB | 640 KB | 🎉 -2 KB 🎉 |
| Dependency size | 64.69 MB | 64.54 MB | 🎉 -151 KB 🎉 |
| Bundle Size Analyzer | Link | Link |
@storybook/nextjs-vite
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 101 | 93 | 🎉 -8 🎉 |
| Self size | 1.42 MB | 1.37 MB | 🎉 -51 KB 🎉 |
| Dependency size | 23.32 MB | 23.83 MB | 🚨 +512 KB 🚨 |
| Bundle Size Analyzer | Link | Link |
@storybook/preact-vite
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 4 | 14 | 🚨 +10 🚨 |
| Self size | 12 KB | 12 KB | 🚨 +4 B 🚨 |
| Dependency size | 217 KB | 1.51 MB | 🚨 +1.29 MB 🚨 |
| Bundle Size Analyzer | Link | Link |
@storybook/react-native-web-vite
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 122 | 122 | 0 |
| Self size | 29 KB | 29 KB | 🚨 +5 B 🚨 |
| Dependency size | 25.71 MB | 24.93 MB | 🎉 -784 KB 🎉 |
| Bundle Size Analyzer | Link | Link |
@storybook/react-vite
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 79 | 83 | 🚨 +4 🚨 |
| Self size | 32 KB | 32 KB | 🎉 -237 B 🎉 |
| Dependency size | 20.44 MB | 21.60 MB | 🚨 +1.16 MB 🚨 |
| Bundle Size Analyzer | Link | Link |
@storybook/react-webpack5
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 268 | 268 | 0 |
| Self size | 23 KB | 23 KB | 🚨 +17 B 🚨 |
| Dependency size | 49.88 MB | 50.28 MB | 🚨 +403 KB 🚨 |
| Bundle Size Analyzer | Link | Link |
@storybook/server-webpack5
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 194 | 194 | 0 |
| Self size | 15 KB | 15 KB | 🎉 -7 B 🎉 |
| Dependency size | 38.70 MB | 39.08 MB | 🚨 +374 KB 🚨 |
| Bundle Size Analyzer | Link | Link |
@storybook/svelte-vite
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 11 | 20 | 🚨 +9 🚨 |
| Self size | 54 KB | 54 KB | 🎉 -18 B 🎉 |
| Dependency size | 25.47 MB | 27.05 MB | 🚨 +1.58 MB 🚨 |
| Bundle Size Analyzer | Link | Link |
@storybook/sveltekit
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 12 | 21 | 🚨 +9 🚨 |
| Self size | 56 KB | 56 KB | 🚨 +29 B 🚨 |
| Dependency size | 25.52 MB | 27.11 MB | 🚨 +1.58 MB 🚨 |
| Bundle Size Analyzer | Link | Link |
@storybook/tanstack-react
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 80 | 84 | 🚨 +4 🚨 |
| Self size | 132 KB | 112 KB | 🎉 -20 KB 🎉 |
| Dependency size | 20.47 MB | 21.64 MB | 🚨 +1.16 MB 🚨 |
| Bundle Size Analyzer | Link | Link |
@storybook/vue3-vite
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 94 | 102 | 🚨 +8 🚨 |
| Self size | 32 KB | 34 KB | 🚨 +3 KB 🚨 |
| Dependency size | 18.54 MB | 43.07 MB | 🚨 +24.54 MB 🚨 |
| Bundle Size Analyzer | Link | Link |
@storybook/web-components-vite
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 5 | 15 | 🚨 +10 🚨 |
| Self size | 19 KB | 19 KB | 🚨 +4 B 🚨 |
| Dependency size | 266 KB | 1.56 MB | 🚨 +1.29 MB 🚨 |
| Bundle Size Analyzer | Link | Link |
@storybook/cli
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 205 | 205 | 0 |
| Self size | 869 KB | 827 KB | 🎉 -42 KB 🎉 |
| Dependency size | 87.61 MB | 86.65 MB | 🎉 -965 KB 🎉 |
| Bundle Size Analyzer | Link | Link |
@storybook/codemod
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 198 | 198 | 0 |
| Self size | 44 KB | 32 KB | 🎉 -12 KB 🎉 |
| Dependency size | 86.08 MB | 85.12 MB | 🎉 -954 KB 🎉 |
| Bundle Size Analyzer | Link | Link |
create-storybook
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 74 | 74 | 0 |
| Self size | 1.09 MB | 1.09 MB | 🚨 +980 B 🚨 |
| Dependency size | 53.24 MB | 52.29 MB | 🎉 -954 KB 🎉 |
| Bundle Size Analyzer | node | node |
@storybook/csf-plugin
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 0 | 9 | 🚨 +9 🚨 |
| Self size | 0 B | 7 KB | 🚨 +7 KB 🚨 |
| Dependency size | 0 B | 1.29 MB | 🚨 +1.29 MB 🚨 |
| Bundle Size Analyzer | Link | Link |
@storybook/preset-react-webpack
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 157 | 157 | 0 |
| Self size | 18 KB | 19 KB | 🚨 +810 B 🚨 |
| Dependency size | 34.39 MB | 34.78 MB | 🚨 +386 KB 🚨 |
| Bundle Size Analyzer | Link | Link |
@storybook/react
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 59 | 59 | 0 |
| Self size | 1.46 MB | 1.49 MB | 🚨 +28 KB 🚨 |
| Dependency size | 12.28 MB | 12.28 MB | 🚨 +7 B 🚨 |
| Bundle Size Analyzer | Link | Link |
@storybook/vue3
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 90 | 4 | 🎉 -86 🎉 |
| Self size | 184 KB | 67 KB | 🎉 -117 KB 🎉 |
| Dependency size | 18.11 MB | 584 KB | 🎉 -17.53 MB 🎉 |
| Bundle Size Analyzer | Link | Link |
|
Alright, question 2 from my last comment is answered — and it changed the plan a bit, so here's the full picture. 🙂 Verified: the runtime values are not in the schema payload. For the props-ts-enum fixture, vue-component-meta returns { kind: 'enum', type: 'Severity', schema: ['Severity.Info', 'Severity.Warning', 'Severity.Error'] }member reference strings only. I traced it into Volar's resolveSchema (packages/component-meta/lib/schemaResolvers.ts): enum members fall through to a typeToString fallback, and the literal type's subtype.value ('info', 0) is available right there but gets discarded. Hence, the options + control.labels fix from my previous comment is impossible in extractArgTypes alone. That reduces the fixable part in this PR to a small guard, so instead of asking you to do it, I pushed it directly to your branch (86a1ea5). I hope that's alright with you: TS enum member references now fall back to a documented other type (docs table keeps Severity, no select that would inject "Severity.Info" as a string), while literal unions keep their structured enum. The main win of your PR stays fully intact. Three unit tests for For the proper fix, I opened vuejs/language-tools#6131 upstream: it adds a literal schema node carrying the runtime value ( |
|
Hi @valentinpalkovic! I noticed that the patch:yes label was added and then removed before this PR was merged. Since this fixes a regression introduced by the vue-component-meta 3.x upgrade in Storybook 10.5, would it be possible to backport this change to the 10.5.x line, for example in a potential 10.5.6 release? The fix is relatively small, already covered by tests, and without it Vue users need to wait for Storybook 10.6.0 to restore structured union metadata and select controls. No problem if there are compatibility or release-policy reasons for keeping it in 10.6 only — I would just like to understand whether a backport is still being considered. |
#35565 landed schema: true in the vue3-vite checker options, so the recorder now mirrors it. 17 of the 25 cm-argtypes baselines change - literal unions become structured enum sbTypes, imported union aliases unfold to their members, runtime [String, Number] and PropType casts become unions, and the "| undefined" noise disappears from optional primitives. Every cm-snippet baseline stays byte-identical. The recorder also joins the comparator, matching what next established for every other *-baselines.test.ts: it self-compares each committed baseline so a checker or plugin change that loses extraction quality fails with named violations rather than landing as an unremarkable snapshot diff.
vue-component-meta stringifies a TS enum member to its qualified name
("Severity.Info"), which says nothing about what gets passed to the
component. storybookjs#35565 therefore had to keep enums out of Controls entirely:
an enum sbType would have made a select inject the literal string
"Severity.Info" instead of 'info'.
vuejs/language-tools#6131, released in vue-component-meta 3.3.9, adds a
"literal" schema node carrying the runtime value next to the member name.
Consume it: TS enums now resolve to an enum sbType of their runtime values,
with control.labels mapping each value back to the member name it is
written as. Picking "Severity.Info" in the dropdown passes 'info'; numeric
enums pass 0 and 1. table.type.summary keeps the enum name either way.
The dotted-string guard stays, narrowed to what it actually catches now:
unresolved qualified type names like "typeof Config.alpha", which
stringify with a dot but stand for no value.
LogDetails |
Closes #35552
Closes #35564
Summary
Enable
schema: truein the defaultvue-component-metachecker options.Motivation
After upgrading to
vue-component-meta3.x, metadata for some type aliases and unions may be returned as a string, for example:"Color | undefined"instead of an expanded schema object.
Enabling
schema: truerestores the expanded schema, allowing consumers to access enum and union metadata consistently.Changes
schema: trueto the defaultMetaCheckerOptionsused by the Vue 3 Vite framework.Testing
Tested with a Vue component using a type alias:
Before:
schema: "Color | undefined"After:
Manual testing
Create a Vue component with a prop using a string union type:
Enable
vue-component-metain Storybook.Verify that the generated metadata for the
colorprop contains a structured enum schema instead of a string representation.Before:
schema: "Color | undefined"After:
Related discussion: #35552
Fixes #35564
AI assistance
I used ChatGPT to help investigate the issue and prepare the initial patch. I reviewed and tested the final change myself.
Summary by CodeRabbit
Summary by CodeRabbit