mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-24 13:54:57 +00:00
- Removed code generators (e2e/generators, storybook/generators) - Created JSON test runner that executes Playwright tests directly from JSON - Created JSON story loader that renders Storybook stories directly from JSON - No intermediate code generation - JSON is executable/renderable at runtime - json-packages.spec.ts auto-discovers and runs all package tests from JSON - DynamicStory component renders stories from JSON definitions - True meta/abstract architecture: configuration itself is executable - Single source of truth: JSON definitions only (no generated .spec.ts or .stories.tsx) - Changes to JSON take effect immediately without regeneration - Added comprehensive READMEs explaining the interpretation approach Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com>
JSON Storybook Story Loader
No code generation - stories are loaded and rendered directly from JSON at runtime.
This is the true meta/abstract approach: the JSON itself defines renderable stories, not templates for code generation.
Philosophy
Instead of generating .stories.tsx files from JSON, we directly load and render the JSON story definitions. This keeps stories as pure data that's interpreted at runtime, staying true to the 95% configuration rule.
How It Works
- Discovery: Scans
packages/*/storybook/stories.jsonfiles - Loading: Reads JSON story definitions at runtime
- Rendering: Renders components directly from JSON via
DynamicStorycomponent - No Intermediate: No code generation step - JSON → Render
Usage
Load Stories from JSON
import { loadAllStoryDefinitions } from './json-loader/storybook-json-loader'
import { DynamicStory } from './json-loader/DynamicStory'
// Load all story definitions
const storyDefs = await loadAllStoryDefinitions(packagesDir)
// Get a specific package's stories
const uiHomeStories = storyDefs.get('ui_home')
// Render a story
<DynamicStory
packageName="ui_home"
storyDef={uiHomeStories}
story={uiHomeStories.stories[0]}
packagesDir={packagesDir}
/>
Example: JSON Story Definition
packages/ui_home/storybook/stories.json:
{
"$schema": "https://metabuilder.dev/schemas/package-storybook.schema.json",
"title": "Home Page Components",
"category": "UI",
"description": "Landing page components",
"stories": [
{
"name": "HomePage",
"render": "home_page",
"description": "Complete home page with all sections"
},
{
"name": "HeroSection",
"render": "hero_section",
"args": {
"title": "Build Anything, Visually",
"subtitle": "A 6-level meta-architecture"
}
}
],
"parameters": {
"layout": "fullscreen"
}
}
Rendered directly - no intermediate code generation!
How Stories Are Rendered
DynamicStorycomponent receives story definition- Loads package components from
packages/{name}/components/ui.json - Finds component by
story.render(matches component id or name) - Passes
story.argsas props toJSONComponentRenderer - Component renders from JSON definition
Story Features
- Args: Pass props to components via
story.args - Parameters: Configure Storybook display via
parameters - ArgTypes: Define controls via
argTypes - Context Variants: Multiple rendering contexts via
contextVariants - Default Context: Shared context via
defaultContext
Benefits of JSON Loading
- True Meta Architecture: Stories are data, not code
- No Build Step: JSON is directly loaded
- Runtime Loading: Changes take effect immediately
- Single Source of Truth: JSON is the only definition
- Package Ownership: Each package owns its story data
- Schema Validated: Stories conform to JSON schema
File Structure
storybook/
├── json-loader/
│ ├── storybook-json-loader.ts # JSON story loader
│ └── DynamicStory.tsx # Story renderer component
└── .storybook/ # Storybook config
Integration with Storybook
Create a .stories.tsx file that uses the JSON loader:
// ui_home.stories.tsx
import type { Meta, StoryObj } from '@storybook/react'
import { loadStoryDefinition } from '../json-loader/storybook-json-loader'
import { DynamicStory } from '../json-loader/DynamicStory'
const storyDef = await loadStoryDefinition('ui_home', packagesDir)
const meta: Meta = {
title: storyDef.title,
parameters: storyDef.parameters,
}
export default meta
// Each story is rendered from JSON
export const HomePage: StoryObj = {
render: () => <DynamicStory
packageName="ui_home"
storyDef={storyDef}
story={storyDef.stories[0]}
packagesDir={packagesDir}
/>
}
Or create a single loader file that auto-discovers all packages:
// all-packages.stories.tsx
import { loadAllStoryDefinitions } from '../json-loader/storybook-json-loader'
const allStories = await loadAllStoryDefinitions(packagesDir)
// Generate stories for each package dynamically
for (const [packageName, storyDef] of allStories) {
// Register stories...
}
vs Code Generation
| Approach | Source of Truth | Runtime | Changes |
|---|---|---|---|
| Code Generation | JSON → Generate .stories.tsx |
Renders React | Requires regeneration |
| JSON Loading ✅ | JSON (only) | Loads & renders JSON | Immediate effect |
JSON loading is more meta/abstract - the configuration itself is renderable!
See Also
schemas/package-schemas/storybook_schema.json- JSON Schemapackages/*/storybook/stories.json- Story definitionsfrontends/nextjs/src/components/JSONComponentRenderer.tsx- Component renderer