feat(babel-preset-storybook-full-source): add opt-in per-story fullSource slicing#36404
Draft
Hotell wants to merge 14 commits into
Draft
feat(babel-preset-storybook-full-source): add opt-in per-story fullSource slicing#36404Hotell wants to merge 14 commits into
Hotell wants to merge 14 commits into
Conversation
…urce slicing Adds a 'storyGranularity: story' option that emits a per-story sliced fullSource for files with multiple story exports (standard CSF), instead of attaching the whole file to the last export. - New sliceStory.ts: reachability-based slicer keeping only the imports/helpers a story references; normalizes CSF3 object stories (render, render-with-args, args-only synth via meta.component) and spread stories into renderable functions; omits play/parameters from the sample only. - fullsource.ts collects all story exports and branches by granularity; 'file' mode unchanged. - Guards args-only synth to genuine story-shaped objects so capitalized data exports are ignored. - Fixtures + tests for CSF2 multi-story, CSF3 render/args/spread, and data-export cases.
…ing in per-story slices Unify the slicer output with 'file' granularity so single-story slices are byte-identical to the whole-file path (no cosmetic churn): - sliceStorySource now mutates the original AST in place and prints with retainLines + preserved comments, instead of regenerating via @babel/generator. - CSF2 stories/helpers keep their original nodes (only stripping a story-type annotation) so authored formatting survives; only CSF3 transforms build new nodes. - story-mode second pass uses retainLines to match file mode. - Fix: reachability now follows type references (TSTypeReference/TSTypeQuery) and resolves local type/interface/enum declarations, so type aliases and their imports are no longer wrongly pruned (dangling type refs). - Strip locations from synthesized CSF3 merged args for clean formatting. Corpus effect: 916/939 single-story fullSource outputs become byte-identical to file mode; the rest are beneficial pruning (meta + dead argTypes/@storybook config).
…e; fix sibling comment leak - Add csf3-multi-story fixture proving per-story reachability across multiple CSF3 object stories that pull in different subsets of shared imports/helpers (Primary -> Button+useStyles, Loading -> Spinner only, Group -> all). - Fix: comments belonging to removed nodes (sibling stories, meta) no longer leak onto retained neighbours in a slice — track each comment's owning node and drop comments whose owner was pruned.
…n csf3-multi-story fixture Exercises per-story reachability for function-form story exports in a CSF3-meta file with a module-scoped React component: Primary -> Button, Loading -> Spinner (meta.component Button not leaked), Group -> local ButtonRow.
…ine custom components Multiple custom React components (Card/CardHeader/CardMedia/CardFooter) in the module; CSF3 object stories each use a subset. Verifies each story's fullSource preserves only the inline custom components it references (Basic -> Card+CardHeader, WithMedia -> Card+CardMedia, Full -> all four).
…ture to multi-story-csf3 Align naming with the sibling multi-story-csf2 fixture (same purpose, CSF3 variant).
…fixture for type preservation Verifies per-story slicing preserves/prunes TypeScript types: type aliases, interfaces, type imports, generics (React.FC<T>) and indexed-access types (ButtonProps['appearance']). Simple keeps CardProps; WithAction keeps ActionProps + ButtonProps; neither leaks the other's types. Adds @babel/preset-typescript to the per-story test preset to parse the .tsx fixture.
…lib/spec tsconfigs Fixture code/output files (now including .tsx) are test inputs, not source — exclude them from type-checking to avoid build failures.
…r in merged imports
- modifyImports now tracks type-only specifiers (import type / inline type) and re-emits them as a separate 'import type { … }' from the mapped package, instead of merging them into a value import (which breaks under verbatimModuleSyntax/isolatedModules).
- Per-story test parses TS via parserOpts (jsx + typescript) instead of @babel/preset-typescript, so the emitted module retains its types — demonstrating the preset is pass-through and does not strip types from source.
…in modifyImports
Add type-import-declaration fixture asserting that import type { X } and inline import { type X } from mapped packages are merged into a single 'import type { … }' (value and type imports kept separate). Refresh multi-story-typescript output.
… deterministic The outer-compiled fixture section printed TS type-member separators non-deterministically (, vs ;) when parsing-without-stripping types. Compile the fixture with @babel/preset-typescript (types stripped in the module, as in a real Storybook build); type preservation remains asserted on the deterministic, prettier-normalized fullSource strings.
…type modifier change Revert the modifyImports import-type preservation change and its fixture — out of scope for the per-story fullSource branch. The multi-story-typescript fixture stays (tests per-story TS type reachability) with the pre-existing merged-import behavior.
…ogic Extract buildFullSource() (transform + prettier) and emitStorySource() (parameters + fullSource + cssModuleSources) helpers shared by both granularity branches. Collect CSS module imports once (file-level, was re-scanned per story). Unify the has-parameters check on storiesWithParameters and drop the redundant parametersAssignment tracking. No output change (all fixtures unchanged).
… output as .js The fixture input is code.tsx but the compiled module output is JS (as in a real Storybook build). Set fixtureOutputExt to '.js' via options.json so the emitted output file is output.js.
📊 Bundle size report✅ No changes found |
|
Pull request demo site: URL |
Contributor
There was a problem hiding this comment.
Pull request overview
This PR enhances @fluentui/babel-preset-storybook-full-source by adding an opt-in storyGranularity: 'story' mode that generates a per-story, sliced context.parameters.fullSource for multi-story CSF files, while preserving the existing 'file' default behavior.
Changes:
- Adds
storyGranularity?: 'file' | 'story'to plugin options and wires it into thefullSourceBabel plugin. - Introduces a new
sliceStorySource()implementation that slices to a single target story and prunes unused imports/helpers while normalizing CSF3 stories into renderable functions. - Adds fixture coverage for CSF2/CSF3 multi-story cases, spreads, args-only stories, data exports, and TS type reachability; excludes fixtures from lib/spec tsconfigs.
Reviewed changes
Copilot reviewed 22 out of 22 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/react-components/babel-preset-storybook-full-source/tsconfig.spec.json | Excludes fixture directories from spec compilation. |
| packages/react-components/babel-preset-storybook-full-source/tsconfig.lib.json | Excludes fixture directories from library compilation. |
| packages/react-components/babel-preset-storybook-full-source/src/types.ts | Adds the new storyGranularity option to the public options type. |
| packages/react-components/babel-preset-storybook-full-source/src/sliceStory.ts | Adds per-story slicing + CSF normalization logic used by 'story' mode. |
| packages/react-components/babel-preset-storybook-full-source/src/fullsource.ts | Implements 'file' vs 'story' handling and emits per-story fullSource. |
| packages/react-components/babel-preset-storybook-full-source/src/fullsource.test.ts | Adds a new plugin-tester pass for per-story slicing fixtures. |
| packages/react-components/babel-preset-storybook-full-source/src/fixtures/storybook-stories-fullsource-per-story/multi-story-typescript/output.js | Fixture output asserting TS type reachability and pruning per story. |
| packages/react-components/babel-preset-storybook-full-source/src/fixtures/storybook-stories-fullsource-per-story/multi-story-typescript/options.json | Fixture config for .tsx input / .js output. |
| packages/react-components/babel-preset-storybook-full-source/src/fixtures/storybook-stories-fullsource-per-story/multi-story-typescript/code.tsx | Fixture input for TS + multiple CSF3 stories with different reachable types. |
| packages/react-components/babel-preset-storybook-full-source/src/fixtures/storybook-stories-fullsource-per-story/multi-story-csf3/output.js | Fixture output for multi-story CSF3 slicing/pruning. |
| packages/react-components/babel-preset-storybook-full-source/src/fixtures/storybook-stories-fullsource-per-story/multi-story-csf3/code.js | Fixture input for multi-story CSF3 slicing/pruning. |
| packages/react-components/babel-preset-storybook-full-source/src/fixtures/storybook-stories-fullsource-per-story/multi-story-csf2/output.js | Fixture output for CSF2 multi-story slicing/pruning. |
| packages/react-components/babel-preset-storybook-full-source/src/fixtures/storybook-stories-fullsource-per-story/multi-story-csf2/code.js | Fixture input for CSF2 multi-story slicing/pruning. |
| packages/react-components/babel-preset-storybook-full-source/src/fixtures/storybook-stories-fullsource-per-story/csf3-spread/output.js | Fixture output for CSF3 { ...Base } spread resolution. |
| packages/react-components/babel-preset-storybook-full-source/src/fixtures/storybook-stories-fullsource-per-story/csf3-spread/code.js | Fixture input for CSF3 { ...Base } spread resolution. |
| packages/react-components/babel-preset-storybook-full-source/src/fixtures/storybook-stories-fullsource-per-story/csf3-render-and-args/output.js | Fixture output for CSF3 render, merged args, and args-only synthesis. |
| packages/react-components/babel-preset-storybook-full-source/src/fixtures/storybook-stories-fullsource-per-story/csf3-render-and-args/code.js | Fixture input for CSF3 render, meta args, story args, and args-only story. |
| packages/react-components/babel-preset-storybook-full-source/src/fixtures/storybook-stories-fullsource-per-story/csf3-data-export/output.js | Fixture output ensuring capitalized non-story data exports are ignored. |
| packages/react-components/babel-preset-storybook-full-source/src/fixtures/storybook-stories-fullsource-per-story/csf3-data-export/code.js | Fixture input containing both a story export and a capitalized data export. |
| packages/react-components/babel-preset-storybook-full-source/README.md | Documents the new opt-in per-story mode and its behavior. |
| packages/react-components/babel-preset-storybook-full-source/etc/babel-preset-storybook-full-source.api.md | Updates public API report for the new option. |
| change/@fluentui-babel-preset-storybook-full-source-perstory.json | Adds a patch change file describing the new opt-in behavior. |
Comment on lines
+432
to
+442
| const argsParam = render.params[0]; | ||
| const body = render.body; | ||
|
|
||
| // No args parameter — keep the body as-is. | ||
| if (!argsParam) { | ||
| return t.arrowFunctionExpression([], body); | ||
| } | ||
|
|
||
| const argsDeclaration = t.variableDeclaration('const', [ | ||
| t.variableDeclarator(argsParam as Babel.types.LVal, mergedArgs ?? t.objectExpression([])), | ||
| ]); |
Comment on lines
+716
to
+726
| function isStoryAnnotationAssignment(t: typeof Babel.types, statementPath: Babel.NodePath): boolean { | ||
| if (!statementPath.isExpressionStatement()) { | ||
| return false; | ||
| } | ||
| const expression = statementPath.node.expression; | ||
| return ( | ||
| t.isAssignmentExpression(expression) && | ||
| t.isMemberExpression(expression.left) && | ||
| t.isIdentifier(expression.left.object) | ||
| ); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
@fluentui/babel-preset-storybook-full-sourceattachescontext.parameters.fullSourceto stories (consumed by the docs Show code panel and Open in Sandbox). Today it works per file: it reads the whole story file and attaches it to the last story export.That fits the one-story-per-file convention, but not the standard CSF pattern of multiple story exports per file:
fullSource; sibling stories getundefined;export const X = { render }) aren't attached in a form the sandbox can render.This PR adds an opt-in per-story mode that gives every story its own minimal, renderable
fullSource.What's added
storyGranularity: 'file' | 'story'— defaults to'file'(current behavior, unchanged).'story'mode — for each story export, emit its ownfullSourcecontaining only the imports and module-level helper declarations that story actually references, plus the story normalized to a renderable function:{ render },args-only (rendered via the metacomponent), and spread ({ ...Base });play/parametersomitted from the emitted sample string only (source files and runtime story objects are never modified — interaction tests are untouched);retainLines+ preserved comments, so per-story output matches the authored source rather than a re-generated approximation.Backward compatibility
'file'→ no behavior change unless a consumer opts into'story'.'file'mode output is byte-identical tomaster;'story'vs'file': 0 coverage regressions, multi-story files gain proper per-story coverage, and single-story output is byte-identical or differs only by beneficial pruning of dead story configuration.Testing
__fixtures__excluded fromtsconfig.lib/spec.patch).Notes / follow-ups
'story'the default is intentionally deferred to a follow-up (needs a docs/output re-baseline).parameters.fullSourceper story).