1+ import { readFileSync } from 'fs' ;
2+ import { resolve } from 'path' ;
3+ import type { HtmlTagDescriptor , Plugin , ResolvedConfig } from 'vite' ;
4+
5+ interface Preset {
6+ id : string ;
7+ label : string ;
8+ token : string ;
9+ destination : string ;
10+ }
11+
12+ export function storyScaffoldPlugin ( env : Record < string , string > ) : Plugin {
13+ const presets : Preset [ ] = [
14+ env . VITE_C2C_TOKEN && {
15+ id : 'c2c' ,
16+ label : 'C2C' ,
17+ token : env . VITE_C2C_TOKEN ,
18+ destination : env . VITE_C2C_DEST || '' ,
19+ } ,
20+ env . VITE_C2T_TOKEN && {
21+ id : 'c2t' ,
22+ label : 'C2T' ,
23+ token : env . VITE_C2T_TOKEN ,
24+ destination : env . VITE_C2T_DEST || '' ,
25+ } ,
26+ env . VITE_SAT_TOKEN && {
27+ id : 'sat' ,
28+ label : 'SAT' ,
29+ token : env . VITE_SAT_TOKEN ,
30+ destination : env . VITE_SAT_DEST || '' ,
31+ } ,
32+ ] . filter ( Boolean ) as Preset [ ] ;
33+
34+ const tokenRewrites : [ RegExp , string ] [ ] = [
35+ [ / i m p o r t \. m e t a \. e n v \. V I T E _ C 2 C _ T O K E N / g, `(window.__SW_CREDS?.token??${ JSON . stringify ( env . VITE_C2C_TOKEN || '' ) } )` ] ,
36+ [ / i m p o r t \. m e t a \. e n v \. V I T E _ C 2 T _ T O K E N / g, `(window.__SW_CREDS?.token??${ JSON . stringify ( env . VITE_C2T_TOKEN || '' ) } )` ] ,
37+ [ / i m p o r t \. m e t a \. e n v \. V I T E _ S A T _ T O K E N / g, `(window.__SW_CREDS?.token??${ JSON . stringify ( env . VITE_SAT_TOKEN || '' ) } )` ] ,
38+ [ / i m p o r t \. m e t a \. e n v \. V I T E _ C 2 C _ D E S T / g, `(window.__SW_CREDS?.destination??${ JSON . stringify ( env . VITE_C2C_DEST || '' ) } )` ] ,
39+ [ / i m p o r t \. m e t a \. e n v \. V I T E _ C 2 T _ D E S T / g, `(window.__SW_CREDS?.destination??${ JSON . stringify ( env . VITE_C2T_DEST || '' ) } )` ] ,
40+ [ / i m p o r t \. m e t a \. e n v \. V I T E _ S A T _ D E S T / g, `(window.__SW_CREDS?.destination??${ JSON . stringify ( env . VITE_SAT_DEST || '' ) } )` ] ,
41+ ] ;
42+
43+ let config : ResolvedConfig ;
44+
45+ return {
46+ name : 'story-scaffold' ,
47+ configResolved ( c ) {
48+ config = c ;
49+ } ,
50+ transformIndexHtml : {
51+ order : 'pre' ,
52+ handler ( html , ctx ) {
53+ const raw = ctx . path || ctx . filename || '' ;
54+ const p = raw . startsWith ( '/' ) ? raw : '/' + raw ;
55+ const isMain = p === '/' || p === '/index.html' || p . endsWith ( '/index.html' ) && ! p . includes ( '/stories/' ) ;
56+ const isStory = p . includes ( '/stories/' ) && p . endsWith ( '.story.html' ) ;
57+
58+ if ( ! isMain && ! isStory ) return ;
59+
60+ let processed = html ;
61+ if ( isStory ) {
62+ for ( const [ re , rep ] of tokenRewrites ) processed = processed . replace ( re , rep ) ;
63+ if ( config ?. command === 'build' ) {
64+ // Dev serves the embed IIFE straight from the package's /dist/embed.
65+ // Prod-built site carries its own copy under /embed/.
66+ processed = processed . replace (
67+ / \/ d i s t \/ e m b e d \/ s i g n a l w i r e - w e b - c o m p o n e n t s - e m b e d \. i i f e \. j s / g,
68+ '/embed/signalwire-web-components-embed.iife.js' ,
69+ ) ;
70+ }
71+ }
72+
73+ const tags : HtmlTagDescriptor [ ] = [
74+ {
75+ tag : 'script' ,
76+ injectTo : 'head-prepend' ,
77+ children : `window.__SW_PRESETS=${ JSON . stringify ( presets ) } ;` ,
78+ } ,
79+ ] ;
80+
81+ if ( isMain ) {
82+ if ( config ?. command === 'build' ) {
83+ // Inline scaffold.js so the built site doesn't need a /stories/ route.
84+ const scaffoldSrc = readFileSync (
85+ resolve ( config . root , 'stories/scaffold.js' ) ,
86+ 'utf-8' ,
87+ ) ;
88+ tags . push ( { tag : 'script' , injectTo : 'head-prepend' , children : scaffoldSrc } ) ;
89+ } else {
90+ tags . push ( {
91+ tag : 'script' ,
92+ attrs : { src : '/stories/scaffold.js' } ,
93+ injectTo : 'head-prepend' ,
94+ } ) ;
95+ }
96+ } else {
97+ tags . push ( {
98+ tag : 'script' ,
99+ injectTo : 'head-prepend' ,
100+ children : [
101+ '(function(){' ,
102+ ' try{' ,
103+ " var r=localStorage.getItem('sw-story-creds');" ,
104+ ' if(r){var p=JSON.parse(r);if(p&&p.token!==undefined){window.__SW_CREDS=p;}}' ,
105+ " var t=localStorage.getItem('sw-story-theme');" ,
106+ " if(t==='dark'||t==='light')document.documentElement.setAttribute('data-theme',t);" ,
107+ ' }catch(_){}' ,
108+ ' if(!window.__SW_CREDS)window.__SW_CREDS=(window.__SW_PRESETS&&window.__SW_PRESETS[0])||' ,
109+ " {id:'custom',label:'Custom',token:'',destination:''};" ,
110+ '})();' ,
111+ ] . join ( '' ) ,
112+ } ) ;
113+ }
114+
115+ return { html : processed , tags } ;
116+ } ,
117+ } ,
118+ } ;
119+ }
0 commit comments