-
-
Notifications
You must be signed in to change notification settings - Fork 10.3k
React: Create a components manifest html page #32882
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
f30520c
3b1c417
b361d37
b36905f
f93f388
6f33595
303372a
1d2ff16
da371a0
eb3381a
5c7725f
569fb1b
20049de
15b6805
8f03c22
2d50a45
074c9b5
f09df14
a7160e6
059bed1
d65d0e7
cf22079
813ff59
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,14 +1,15 @@ | ||||||||||||||||||||||||||||||
| import { logConfig } from 'storybook/internal/common'; | ||||||||||||||||||||||||||||||
| import { logger } from 'storybook/internal/node-logger'; | ||||||||||||||||||||||||||||||
| import { MissingBuilderError } from 'storybook/internal/server-errors'; | ||||||||||||||||||||||||||||||
| import type { Options } from 'storybook/internal/types'; | ||||||||||||||||||||||||||||||
| import type { ComponentsManifest, Options } from 'storybook/internal/types'; | ||||||||||||||||||||||||||||||
| import { type ComponentManifestGenerator } from 'storybook/internal/types'; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| import compression from '@polka/compression'; | ||||||||||||||||||||||||||||||
| import polka from 'polka'; | ||||||||||||||||||||||||||||||
| import invariant from 'tiny-invariant'; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| import { telemetry } from '../telemetry'; | ||||||||||||||||||||||||||||||
| import { renderManifestComponentsPage } from './manifest'; | ||||||||||||||||||||||||||||||
| import { type StoryIndexGenerator } from './utils/StoryIndexGenerator'; | ||||||||||||||||||||||||||||||
| import { doTelemetry } from './utils/doTelemetry'; | ||||||||||||||||||||||||||||||
| import { getManagerBuilder, getPreviewBuilder } from './utils/get-builders'; | ||||||||||||||||||||||||||||||
|
|
@@ -165,6 +166,34 @@ export async function storybookDevServer(options: Options) { | |||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| app.get('/manifests/components.html', async (req, res) => { | ||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||
| const componentManifestGenerator: ComponentManifestGenerator = await options.presets.apply( | ||||||||||||||||||||||||||||||
| 'experimental_componentManifestGenerator' | ||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||
| const indexGenerator = await initializedStoryIndexGenerator; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| if (!componentManifestGenerator || !indexGenerator) { | ||||||||||||||||||||||||||||||
| res.statusCode = 400; | ||||||||||||||||||||||||||||||
| res.setHeader('Content-Type', 'text/html; charset=utf-8'); | ||||||||||||||||||||||||||||||
| res.end(`<pre>No component manifest generator configured.</pre>`); | ||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| const manifest = (await componentManifestGenerator( | ||||||||||||||||||||||||||||||
| indexGenerator as unknown as import('storybook/internal/core-server').StoryIndexGenerator | ||||||||||||||||||||||||||||||
| )) as ComponentsManifest; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| res.setHeader('Content-Type', 'text/html; charset=utf-8'); | ||||||||||||||||||||||||||||||
| res.end(renderManifestComponentsPage(manifest)); | ||||||||||||||||||||||||||||||
|
JReinhold marked this conversation as resolved.
|
||||||||||||||||||||||||||||||
| } catch (e) { | ||||||||||||||||||||||||||||||
| // logger?.error?.(e instanceof Error ? e : String(e)); | ||||||||||||||||||||||||||||||
| res.statusCode = 500; | ||||||||||||||||||||||||||||||
| res.setHeader('Content-Type', 'text/html; charset=utf-8'); | ||||||||||||||||||||||||||||||
| res.end(`<pre>${e instanceof Error ? e.toString() : String(e)}</pre>`); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
Comment on lines
+191
to
+195
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Escape HTML in error responses to avoid XSS; log the error. Interpolating raw error text into HTML is unsafe. Also, keep server logs. Two options:
Suggested patch (HTML + escape + logging): - } catch (e) {
- // logger?.error?.(e instanceof Error ? e : String(e));
- res.statusCode = 500;
- res.setHeader('Content-Type', 'text/html; charset=utf-8');
- res.end(`<pre>${e instanceof Error ? e.toString() : String(e)}</pre>`);
- }
+ } catch (e) {
+ logger.error(e instanceof Error ? e : String(e));
+ const esc = (s: unknown) =>
+ String(s ?? '').replace(/[&<>"']/g, (c) => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c] as string));
+ res.statusCode = 500;
+ res.setHeader('Content-Type', 'text/html; charset=utf-8');
+ const msg = e instanceof Error ? e.toString() : String(e);
+ res.end(`<pre><code>${esc(msg)}</code></pre>`);
+ }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| // Now the preview has successfully started, we can count this as a 'dev' event. | ||||||||||||||||||||||||||||||
| doTelemetry(app, core, initializedStoryIndexGenerator as Promise<StoryIndexGenerator>, options); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be nice to also support
'/manifests/components', without the html extension. just callget()two times with the same handler.