- 
          
 - 
                Notifications
    
You must be signed in to change notification settings  - Fork 9.8k
 
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 4 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.
               
          
            Show resolved
            Hide 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
    
   
  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.