-
Notifications
You must be signed in to change notification settings - Fork 994
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Storybook (Vite) framework package (#10064)
Co-authored-by: Josh GM Walker <[email protected]> Co-authored-by: Tobbe Lundberg <[email protected]>
- Loading branch information
1 parent
16b3e9c
commit 285d657
Showing
43 changed files
with
1,730 additions
and
367 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
- Add Storybook (Vite) framework package (#10064) by @arimendelow | ||
|
||
Adds: | ||
- Storybook framework package for using Storybook with Vite. | ||
- CLI package (command: `yarn rw sbv`) for running Storybook using Vite. CLI package additionally creates Mock Service Worker, and, on first run, the project-side Storybook config files. | ||
|
||
Current Storybook (Webpack) users will need to manually migrate any additional Storybook config (mocks, etc.). The primary user-facing difference between the old and new Storybook integrations is that the config used by the old one lives in the `@redwoodjs/testing` package, and the config used by this new one lives in the user's `web/.storybook` directory. |
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# CLI Packages - Storybook Vite | ||
|
||
This CLI package is intended to be used with the [Storybook Framework package](../../storybook/README.md). We are still finalizing usage details. | ||
For now, get started as follows: | ||
- Run `yarn rw sbv` from your project. This will: | ||
- Add the necessary config files, if they don't already exist: `web/.storybook/{main.ts + preview-body.html}`. | ||
- Create the Mock Service Worker, which is needed for all Cell mocking. | ||
- Run Storybook. |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import fs from 'node:fs/promises' | ||
import path from 'node:path' | ||
import { fileURLToPath } from 'node:url' | ||
|
||
import { build, defaultIgnorePatterns } from '@redwoodjs/framework-tools' | ||
|
||
import fg from 'fast-glob' | ||
|
||
await build() | ||
|
||
/** | ||
* We need template files, which esbuild won't copy over, | ||
* so we do it manually here. | ||
*/ | ||
async function copyAssets() { | ||
const cliRootDirPath = path.dirname(fileURLToPath(import.meta.url)) | ||
const cliSrcDirPath = path.join(cliRootDirPath, 'src') | ||
const cliDistDirPath = path.join(cliRootDirPath, 'dist') | ||
|
||
let pathnames = await fg( | ||
[ | ||
'**/*.template', | ||
], | ||
{ | ||
absolute: true, | ||
cwd: cliSrcDirPath, | ||
ignore: defaultIgnorePatterns, | ||
} | ||
) | ||
|
||
// For Windows. | ||
pathnames = pathnames.map(p => path.normalize(p)) | ||
|
||
for (const pathname of pathnames) { | ||
const distPathname = pathname.replace(cliSrcDirPath, cliDistDirPath) | ||
|
||
try { | ||
await fs.cp(pathname, distPathname) | ||
} catch (error) { | ||
console.error( | ||
`Couldn't copy ${pathname} to ${distPathname}. ` + | ||
`(Replaced ${cliSrcDirPath} with ${cliDistDirPath} to get the dist pathname.)` | ||
) | ||
throw error | ||
} | ||
} | ||
} | ||
|
||
await copyAssets() |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
{ | ||
"name": "@redwoodjs/cli-storybook-vite", | ||
"version": "7.0.0", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/redwoodjs/redwood.git", | ||
"directory": "packages/cli-packages/storybook-vite" | ||
}, | ||
"license": "MIT", | ||
"main": "./dist/index.js", | ||
"types": "./dist/index.d.ts", | ||
"files": [ | ||
"dist" | ||
], | ||
"scripts": { | ||
"build": "tsx ./build.mts && yarn build:types", | ||
"build:pack": "yarn pack -o redwoodjs-cli-storybook-vite.tgz", | ||
"build:types": "tsc --build --verbose", | ||
"build:watch": "nodemon --watch src --ext \"js,jsx,ts,tsx\" --ignore dist --exec \"yarn build\"", | ||
"prepublishOnly": "NODE_ENV=production yarn build" | ||
}, | ||
"dependencies": { | ||
"@redwoodjs/cli-helpers": "workspace:*", | ||
"@redwoodjs/project-config": "workspace:*", | ||
"@redwoodjs/telemetry": "workspace:*", | ||
"chalk": "4.1.2", | ||
"execa": "5.1.1", | ||
"storybook": "7.6.17", | ||
"storybook-framework-redwoodjs-vite": "workspace:*", | ||
"terminal-link": "2.1.1", | ||
"yargs": "17.7.2" | ||
}, | ||
"devDependencies": { | ||
"@redwoodjs/framework-tools": "workspace:*", | ||
"@types/yargs": "17.0.32", | ||
"tsx": "4.10.3", | ||
"typescript": "5.4.5" | ||
}, | ||
"gitHead": "3905ed045508b861b495f8d5630d76c7a157d8f1" | ||
} |
82 changes: 82 additions & 0 deletions
82
packages/cli-packages/storybook-vite/src/commands/storybook.ts
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import terminalLink from 'terminal-link' | ||
import type { Argv } from 'yargs' | ||
|
||
import { recordTelemetryAttributes } from '@redwoodjs/cli-helpers' | ||
|
||
import type { StorybookYargsOptions } from '../types' | ||
|
||
export const command = 'storybook-vite' | ||
export const aliases = ['sbv'] | ||
export const description = | ||
'Launch Storybook: a tool for building UI components and pages in isolation (now, with Vite)!' | ||
|
||
export const defaultOptions: StorybookYargsOptions = { | ||
open: true, | ||
build: false, | ||
ci: false, | ||
port: 7910, | ||
buildDirectory: 'public/storybook', | ||
smokeTest: false, | ||
} | ||
|
||
export function builder( | ||
yargs: Argv<StorybookYargsOptions>, | ||
): Argv<StorybookYargsOptions> { | ||
return yargs | ||
.option('build', { | ||
describe: 'Build Storybook', | ||
type: 'boolean', | ||
default: defaultOptions.build, | ||
}) | ||
.option('build-directory', { | ||
describe: 'Directory in web/ to store static files', | ||
type: 'string', | ||
default: defaultOptions.buildDirectory, | ||
}) | ||
.option('ci', { | ||
describe: 'Start server in CI mode, with no interactive prompts', | ||
type: 'boolean', | ||
default: defaultOptions.ci, | ||
}) | ||
.option('open', { | ||
describe: 'Open storybook in your browser on start', | ||
type: 'boolean', | ||
default: defaultOptions.open, | ||
}) | ||
.option('port', { | ||
describe: 'Which port to run storybook on', | ||
type: 'number', | ||
default: defaultOptions.port, | ||
}) | ||
.option('smoke-test', { | ||
describe: | ||
"CI mode plus smoke-test (skip prompts; don't open browser; exit after successful start)", | ||
type: 'boolean', | ||
default: defaultOptions.smokeTest, | ||
}) | ||
|
||
.epilogue( | ||
`Also see the ${terminalLink( | ||
'Redwood CLI Reference', | ||
'https://redwoodjs.com/docs/cli-commands#storybook', | ||
)}`, | ||
) | ||
} | ||
|
||
export async function handler(options: StorybookYargsOptions): Promise<void> { | ||
// NOTE: We should provide some visual output before the import to increase | ||
// the perceived performance of the command as there will be delay while we | ||
// load the handler. | ||
recordTelemetryAttributes({ | ||
command: 'storybook-vite', | ||
build: options.build, | ||
ci: options.ci, | ||
open: options.open, | ||
smokeTest: options.smokeTest, | ||
}) | ||
// @ts-expect-error - Custom workaround for storybook telemetry | ||
process.emit('shutdown-telemetry') | ||
|
||
const { handler: storybookHandler } = await import('./storybookHandler.js') | ||
await storybookHandler(options) | ||
} |
147 changes: 147 additions & 0 deletions
147
packages/cli-packages/storybook-vite/src/commands/storybookHandler.ts
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
import fs from 'node:fs' | ||
import path from 'node:path' | ||
|
||
import type { ExecaError } from 'execa' | ||
import execa from 'execa' | ||
|
||
import { getPaths } from '@redwoodjs/project-config' | ||
import { errorTelemetry } from '@redwoodjs/telemetry' | ||
|
||
import c from '../lib/colors' | ||
import type { StorybookYargsOptions } from '../types' | ||
|
||
const readFile = (target: fs.PathOrFileDescriptor) => | ||
fs.readFileSync(target, { encoding: 'utf8' }) | ||
|
||
const writeFile = (target: string, contents: any) => { | ||
const { base } = getPaths() | ||
if (fs.existsSync(target)) { | ||
throw new Error(`${target} already exists.`) | ||
} | ||
|
||
const filename = path.basename(target) | ||
const targetDir = target.replace(filename, '') | ||
fs.mkdirSync(targetDir, { recursive: true }) | ||
fs.writeFileSync(target, contents) | ||
console.log(`Successfully wrote file \`./${path.relative(base, target)}\``) | ||
} | ||
|
||
export async function handler({ | ||
build, | ||
buildDirectory, | ||
ci, | ||
open, | ||
port, | ||
smokeTest, | ||
}: StorybookYargsOptions) { | ||
// We add a stub file to type generation because users don't have Storybook | ||
// installed when they first start a project. We need to remove the file once | ||
// they install Storybook so that the real types come through. | ||
fs.rmSync( | ||
path.join(getPaths().generated.types.includes, 'web-storybook.d.ts'), | ||
{ force: true }, | ||
) | ||
|
||
// Check for conflicting options | ||
if (build && smokeTest) { | ||
throw new Error('Can not provide both "--build" and "--smoke-test"') | ||
} | ||
|
||
if (build && open) { | ||
console.warn( | ||
c.warning( | ||
'Warning: --open option has no effect when running Storybook build', | ||
), | ||
) | ||
} | ||
|
||
const cwd = getPaths().web.base | ||
const staticAssetsFolder = path.join(cwd, 'public') | ||
const execaOptions: Partial<execa.Options> = { | ||
stdio: 'inherit', | ||
shell: true, | ||
cwd, | ||
} | ||
|
||
// Create the `MockServiceWorker.js` file. See https://v1.mswjs.io/docs/cli/init | ||
await execa.command( | ||
`yarn msw init "${staticAssetsFolder}" --no-save`, | ||
execaOptions, | ||
) | ||
|
||
const redwoodProjectPaths = getPaths() | ||
const storybookConfigPath = path.dirname( | ||
`${redwoodProjectPaths.web.storybook}/main.ts`, | ||
) | ||
|
||
const storybookMainFilePath = path.join(storybookConfigPath, 'main.ts') | ||
const storybookPreviewBodyFilePath = path.join( | ||
storybookConfigPath, | ||
'preview-body.html', | ||
) | ||
|
||
// Check if the config files exists yet. If they don't, create 'em! | ||
if (!fs.existsSync(storybookMainFilePath)) { | ||
console.log("Storybook's main.ts not found. Creating it now...") | ||
const mainConfigTemplatePath = path.join( | ||
__dirname, | ||
'templates/main.ts.template', | ||
) | ||
const mainConfigContent = readFile(mainConfigTemplatePath) | ||
writeFile(storybookMainFilePath, mainConfigContent) | ||
console.log('main.ts created!') | ||
} | ||
|
||
if (!fs.existsSync(storybookPreviewBodyFilePath)) { | ||
console.log("Storybook's preview-body.html not found. Creating it now...") | ||
const previewBodyTemplatePath = path.join( | ||
__dirname, | ||
'templates/preview-body.html.template', | ||
) | ||
const previewBodyConfigContent = readFile(previewBodyTemplatePath) | ||
writeFile(storybookPreviewBodyFilePath, previewBodyConfigContent) | ||
console.log('preview-body.html created!') | ||
} | ||
|
||
let command = '' | ||
const flags = [`--config-dir "${storybookConfigPath}"`] | ||
|
||
if (build) { | ||
command = `yarn storybook build ${[ | ||
...flags, | ||
`--output-dir "${buildDirectory}"`, | ||
] | ||
.filter(Boolean) | ||
.join(' ')}` | ||
} else if (smokeTest) { | ||
command = `yarn storybook dev ${[ | ||
...flags, | ||
`--port ${port}`, | ||
`--smoke-test`, | ||
`--ci`, | ||
`--no-version-updates`, | ||
] | ||
.filter(Boolean) | ||
.join(' ')}` | ||
} else { | ||
command = `yarn storybook dev ${[ | ||
...flags, | ||
`--port ${port}`, | ||
`--no-version-updates`, | ||
ci && '--ci', | ||
!open && `--no-open`, | ||
] | ||
.filter(Boolean) | ||
.join(' ')}` | ||
} | ||
|
||
try { | ||
await execa.command(command, execaOptions) | ||
} catch (e) { | ||
if ((e as ExecaError).signal !== 'SIGINT') { | ||
console.log(c.error((e as Error).message)) | ||
errorTelemetry(process.argv, (e as Error).message) | ||
} | ||
process.exit((e as ExecaError).exitCode ?? 1) | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
packages/cli-packages/storybook-vite/src/commands/templates/main.ts.template
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import type { StorybookConfig } from 'storybook-framework-redwoodjs-vite' | ||
|
||
import { getPaths, importStatementPath } from '@redwoodjs/project-config' | ||
|
||
const redwoodProjectPaths = getPaths() | ||
|
||
const config: StorybookConfig = { | ||
framework: 'storybook-framework-redwoodjs-vite', | ||
|
||
stories: [ | ||
`${importStatementPath( | ||
redwoodProjectPaths.web.src | ||
)}/**/*.stories.@(js|jsx|ts|tsx|mdx)`, | ||
], | ||
|
||
addons: ['@storybook/addon-essentials'], | ||
} | ||
|
||
export default config |
1 change: 1 addition & 0 deletions
1
packages/cli-packages/storybook-vite/src/commands/templates/preview-body.html.template
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
<div id="redwood-app"></div> |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { | ||
command, | ||
aliases, | ||
description, | ||
builder, | ||
handler, | ||
} from './commands/storybook' | ||
|
||
export const commands = [ | ||
{ | ||
command, | ||
aliases, | ||
description, | ||
builder, | ||
handler, | ||
}, | ||
] |
Oops, something went wrong.