@chromatic-com/storybook
Version:
Catch unexpected visual changes & UI bugs in your stories
19 lines (14 loc) • 11 kB
JavaScript
;
var fs = require('fs');
var promises = require('fs/promises');
var path = require('path');
var node = require('chromatic/node');
var coreServer = require('storybook/internal/core-server');
var telemetry = require('storybook/internal/telemetry');
var filesize = require('filesize');
var jsonfile = require('jsonfile');
var S=(t=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(t,{get:(r,e)=>(typeof require<"u"?require:r)[e]}):t)(function(t){if(typeof require<"u")return require.apply(this,arguments);throw new Error('Dynamic require of "'+t+'" is not supported')});var {CHROMATIC_INDEX_URL:fe,CHROMATIC_BASE_URL:_=fe||"https://www.chromatic.com",CHROMATIC_API_URL:ke=`${_}/api`}=process.env,F="@chromatic-com/storybook",l="chromaui/addon-visual-tests",U=`${l}/test-provider`,$=`${l}/configInfo`,j=`${l}/gitInfo`,G=`${l}/gitInfoError`,M=`${l}/projectInfo`,V=`${l}/startBuild`,K=`${l}/stopBuild`,z=`${l}/localBuildProgress`,H=`${l}/telemetry`,J=`${l}/removeAddon`;var Y=`${l}/ChannelFetch/aborted`,Q=`${l}ChannelFetch/request`,k=`${l}ChannelFetch/response`,P={autoAcceptChanges:!1,exitOnceUploaded:!1,exitZeroOnChanges:!0,forceRebuild:!0,fromCI:!1,interactive:!1,isLocalBuild:!0,logPrefix:"\x1B[38;5;202mChromatic\x1B[0m:",skip:!1,skipUpdateCheck:!0,storybookBuildDir:void 0};var b=t=>g.includes(t),q=t=>["upload","snapshot"].includes(t),g=["initialize","build","upload","verify","snapshot"],O={initialize:{key:"initialize",emoji:"\u{1F680}",renderName:()=>"Initialize build",renderProgress:()=>"Initializing build...",renderComplete:()=>"Initialized",estimateDuration:2e3},build:{key:"build",emoji:"\u{1F3D7}",renderName:()=>"Build Storybook",renderProgress:()=>"Building your Storybook...",renderComplete:()=>"Storybook built",estimateDuration:2e4},upload:{key:"upload",emoji:"\u{1F4E1}",renderName:()=>"Publish your Storybook",renderProgress:({stepProgress:t})=>{let{numerator:r,denominator:e}=t.upload;if(!e||!r)return "Uploading files...";let{value:o,exponent:n}=filesize.filesize(e,{output:"object",round:1}),{value:i,symbol:s}=filesize.filesize(r,{exponent:n,output:"object",round:1});return `Uploading files... ${i}/${o} ${s}`},renderComplete:()=>"Publish complete",estimateDuration:2e4},verify:{key:"verify",emoji:"\u{1F50D}",renderName:()=>"Verify your Storybook",renderProgress:()=>"Verifying contents...",renderComplete:()=>"Storybook verified",estimateDuration:2e4},snapshot:{key:"snapshot",emoji:"\u{1F4F8}",renderName:()=>"Run visual tests",renderProgress:({stepProgress:t})=>{let{numerator:r,denominator:e}=t.snapshot;return e?`Running visual tests... ${r}/${e}`:"Running visual tests..."},renderComplete:()=>"Tested your stories",estimateDuration:9e4},aborted:{key:"aborted",emoji:"\u270B",renderName:()=>"Build canceled",renderProgress:()=>"Build canceled",renderComplete:()=>"Build canceled",estimateDuration:0},complete:{key:"complete",emoji:"\u{1F389}",renderName:()=>"Visual tests completed!",renderProgress:()=>"Visual tests completed!",renderComplete:()=>"Visual tests completed!",estimateDuration:0},error:{key:"error",emoji:"\u{1F6A8}",renderName:()=>"Build failed",renderProgress:()=>"Build failed",renderComplete:()=>"Build failed",estimateDuration:0},limited:{key:"error",emoji:"\u{1F6A8}",renderName:()=>"Build limited",renderProgress:()=>"Build limited",renderComplete:()=>"Build limited",estimateDuration:0}},me={buildProgressPercentage:0,currentStep:g[0],stepProgress:Object.fromEntries(g.map(t=>[t,{}]))},X=JSON.stringify(me);var Z=2e3,I,he=(t,r)=>{if(!b(t))throw new Error(`Unknown step: ${t}`);let e=g.map(d=>{let{startedAt:p,completedAt:m}=r?.[d]||{};return p&&m?m-p:O[d].estimateDuration}),o=e.reduce((d,p)=>d+p,0),n=g.indexOf(t),i=e.slice(0,n).reduce((d,p)=>d+p,0),s=i+e[n],a=i/o*100,c=s/o*100;return {...O[t],startPercentage:a,endPercentage:c,stepPercentage:c-a}},L=(t,r)=>(e,{progress:o,total:n}={})=>{if(clearTimeout(r),!b(e.task))return;if(!t.value)throw new Error("Unexpected missing value for localBuildProgress");let{buildProgressPercentage:i,stepProgress:s,previousBuildProgress:a}=t.value;if(s[e.task]?.completedAt)return;let{startPercentage:c,endPercentage:d,stepPercentage:p}=he(e.task,a),m=c;if(o&&n&&(m+=p*(o/n)),!q(e.task)){let{estimateDuration:R}=O[e.task],y=g.indexOf(e.task);m=Math.max(m,i)+Z/R*p,r=setTimeout(()=>{if(!t.value)throw new Error("Unexpected missing value for localBuildProgress");let{currentStep:T}=t.value;b(T)&&g.indexOf(T)<=y&&L(t,r)(e);},Z);}s[e.task]={startedAt:Date.now(),...s[e.task],...o&&n&&{numerator:o,denominator:n}},t.value={buildId:e.announcedBuild?.id,branch:e.git?.branch,buildProgressPercentage:Math.min(m,d),currentStep:e.task,stepProgress:s};},ee=(t,r)=>(e,o)=>{if(clearTimeout(r),!t.value)throw new Error("Unexpected missing value for localBuildProgress");let{buildProgressPercentage:n,stepProgress:i}=t.value,s={buildId:e.announcedBuild?.id,branch:e.git?.branch,buildProgressPercentage:n,stepProgress:i,previousBuildProgress:i};if(o){t.value={...s,currentStep:I?.signal.aborted?"aborted":"error",formattedError:o.formattedError,originalError:o.originalError};return}e.task&&b(e.task)&&(i[e.task]={...i[e.task],completedAt:Date.now()}),e.task==="verify"&&e.build?.wasLimited&&(t.value={...s,currentStep:"limited",stepProgress:i,errorDetailsUrl:e.build?.app.account?.billingUrl}),e.build&&e.task==="snapshot"&&(t.value={...s,buildProgressPercentage:100,currentStep:"complete",stepProgress:i,changeCount:e.build.changeCount,errorCount:e.build.errorCount});},te=async(t,r)=>{if(!r.projectId)throw new Error("Missing projectId");if(!r.userToken)throw new Error("Missing userToken");t.value=JSON.parse(X);let e;I?.abort(),I=new AbortController,process.env.SB_TESTBUILD="true",await node.run({flags:{interactive:!1},options:{...r,...P,experimental_onTaskStart:L(t,e),experimental_onTaskProgress:L(t,e),experimental_onTaskComplete:ee(t,e),experimental_onTaskError:ee(t,e),experimental_abortSignal:I?.signal}});},re=()=>{I?.abort(new Error("Build canceled from Storybook"));};var x=new Map,C=class{constructor(r,e=fetch){this.channel=r,this.abortControllers=new Map,this.channel.on(Y,({requestId:o})=>{this.abortControllers.get(o)?.abort(),this.abortControllers.delete(o);}),this.channel.on(Q,async({requestId:o,input:n,init:i})=>{let s=new AbortController;this.abortControllers.set(o,s);try{let a=await e(n,{...i,signal:s.signal}),c=await a.text(),d=Array.from(a.headers),p={body:c,headers:d,status:a.status,statusText:a.statusText};this.channel.emit(k,{requestId:o,response:p});}catch(a){let c=a instanceof Error?a.message:String(a);this.channel.emit(k,{requestId:o,error:c});}finally{this.abortControllers.delete(o);}});}static subscribe(r,e,o=fetch){let n=x.get(r)||new C(e,o);return x.has(r)||x.set(r,n),n}};var oe="experimental_useSharedState_getValue",D="experimental_useSharedState_setValue",A=new Map,f=class{constructor(r){this.channel=r,this.listeners=[],this.state={},this.channel.on(D,(e,o,n)=>{this.state?.[e]?.index>=n||(this.state[e]={index:n,value:o});}),this.channel.on(oe,e=>{let o=this.state[e]?.index??0,n=this.state[e]?.value;this.channel.emit(D,e,n,o);});}get(r){return this.state[r]||this.channel.emit(oe,r),this.state[r]?.value}set(r,e){let o=(this.state[r]?.index??0)+1;this.state[r]={index:o,value:e},this.channel.emit(D,r,e,o);}static subscribe(r,e){let o=A.get(r)||new f(e);return A.has(r)||(A.set(r,o),o.channel.on(D,(n,i)=>{n===r&&o.listeners.forEach(s=>s(i));})),{get value(){return o.get(r)},set value(n){o.set(r,n);},on(n,i){if(n!=="change")throw new Error("unsupported event");o.listeners.push(i);},off(n,i){if(n!=="change")throw new Error("unsupported event");let s=o.listeners.indexOf(i);s>=0&&o.listeners.splice(s,1);}}}};async function ne(t,r){let e=Object.entries(r).sort((o,n)=>o[0].localeCompare(n[0])).reduce((o,[n,i])=>i===null?o:Object.assign(o,{[n]:i}),{});await jsonfile.writeFile(t,e,{spaces:2});}var ce=node.createLogger(void 0,P);function Pe(t=[]){return [...t,S.resolve("./manager.mjs")]}var ue=async()=>{let t=(async()=>{try{let r=S.resolve("@chromatic-com/storybook/package.json"),e=await promises.readFile(r,"utf-8");return JSON.parse(e).version||null}catch{return null}})();return ue=()=>t,t},se=(t,r,e)=>Object.fromEntries(Object.entries(e).map(([o,n])=>[o,n===r[o]?null:n]).filter(([o,n])=>n!==null||t[o]!==void 0)),ae=async(t,r)=>{let e={storybookBaseDir:".",storybookConfigDir:".storybook"},o={},n={},{repositoryRootDir:i}=await node.getGitInfo({log:ce}),s=i&&path.normalize(path.relative(i,process.cwd()));s!==path.normalize(t.storybookBaseDir??"")&&(o.storybookBaseDir=s);let a=path.normalize(path.relative(process.cwd(),r.configDir));return a!==path.normalize(t.storybookConfigDir??"")&&(o.storybookConfigDir=a),t.onlyChanged===void 0&&(n.onlyChanged=!0),t.zip===void 0&&(n.zip=!0),{configuration:t,problems:se(t,e,o),suggestions:se(t,e,n)}},Oe=(t,r,e,o)=>{let n,i,s,a=async()=>{try{let c=await node.getGitInfo({log:ce});Object.entries(c).some(([d,p])=>n?.[d]!==p)&&r(c,n),n=c,i=void 0,s=setTimeout(a,t);}catch(c){e(c),o&&i?.message!==c.message&&(console.error(`Failed to fetch git info, with error:
${c}`),n=void 0,i=c),s=setTimeout(a,t);}};return a(),{cancel:()=>clearTimeout(s)}},De=async(t,r)=>{let e=await node.getConfiguration(t);await r(e),e.configFile&&fs.watch(e.configFile,async(o,n)=>{n&&await r(await node.getConfiguration(n));});};async function ve(t,r){let{configFile:e,presets:o}=r;C.subscribe(l,t);let n=o.apply("experimental_serverAPI"),i=o.apply("core"),{projectId:s}=await node.getConfiguration(e),a=f.subscribe(M,t);a.value=s?{projectId:s}:{};let c=coreServer.experimental_getTestProviderStore(U),d=s;a.on("change",async({projectId:u}={})=>{if(!u||u===d)return;d=u;let E=e;try{let{configFile:h,...N}=await node.getConfiguration(E),B=h||E||"chromatic.config.json",{problems:de,suggestions:pe}=await ae(N,r);await ne(B,{...N,...de,...pe,projectId:u}),a.value={...a.value,written:!0,dismissed:!1,configFile:B};}catch(h){console.warn(`Failed to update your main configuration:
${h}`),a.value={...a.value,written:!1,dismissed:!1,configFile:E};}});let p=f.subscribe(z,t);t.on(V,async({accessToken:u})=>{let{projectId:E}=a.value||{};c.runWithState(async()=>{try{await te(p,{configFile:e,projectId:E,userToken:u});}catch(h){throw console.error(`Failed to run Chromatic build, with error:
${h}`),h}});}),t.on(K,()=>{c.setState("test-provider-state:succeeded"),re();}),t.on(H,async u=>{(await i).disableTelemetry||telemetry.telemetry("addon-visual-tests",{...u,addonVersion:await ue()});});let m=f.subscribe($,t),R=f.subscribe(j,t),y=f.subscribe(G,t),T=Oe(5e3,u=>{y.value=void 0,R.value=u;},u=>{y.value=u;});return De(e,async u=>{d&&(m.value=await ae(u,r));}),t.on(J,()=>{n.then(u=>u.removeAddon(F)).catch(u=>console.error(u)),T.cancel();}),t}var we={managerEntries:Pe,experimental_serverChannel:ve,staticDirs:async t=>[...t,{from:path.join(path.dirname(S.resolve("@chromatic-com/storybook/package.json")),"assets"),to:"addon-visual-tests-assets"}],env:async(t,{configType:r})=>r==="PRODUCTION"?t:{...t,CHROMATIC_BASE_URL:_}},mt=we;
module.exports = mt;