@chromatic-com/storybook
Version:
Catch unexpected visual changes & UI bugs in your stories
22 lines (17 loc) • 13.4 kB
JavaScript
;
var fs = require('fs');
var promises = require('fs/promises');
var module$1 = require('module');
var path = require('path');
var node = require('chromatic/node');
var coreServer = require('storybook/internal/core-server');
var telemetry = require('storybook/internal/telemetry');
var crypto$1 = require('crypto');
var jsonfile = require('jsonfile');
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
var M="@chromatic-com/storybook";var l="chromaui/addon-visual-tests",j=`${l}/test-provider`,V=`${l}/configInfo`,K=`${l}/gitInfo`,Y=`${l}/gitInfoError`,J=`${l}/projectInfo`,z=`${l}/startBuild`,W=`${l}/stopBuild`,q=`${l}/localBuildProgress`,Q=`${l}/startShare`,Z=`${l}/cancelShare`,X=`${l}/shareProgress`,ee=`${l}/telemetry`,te=`${l}/removeAddon`;var re=`${l}/ChannelFetch/aborted`,oe=`${l}ChannelFetch/request`,N=`${l}ChannelFetch/response`,v={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 {CHROMATIC_INDEX_URL:Se,CHROMATIC_BASE_URL:w=Se||"https://www.chromatic.com",CHROMATIC_API_URL:at=`${w}/api`}=process.env;var R=["B","kB","MB","GB","TB","PB","EB","ZB","YB"];function U(e,r){let t=r?.round??1;if(!Number.isFinite(e)||e<=0)return {value:0,symbol:R[0],exponent:0};let o=Math.min(R.length-1,Math.max(0,Math.floor(Math.log10(e)/3))),n=Math.min(R.length-1,Math.max(0,r?.exponent??o)),i=e/Math.pow(1e3,n),s=Math.pow(10,t),a=Math.round(i*s)/s,c=r?.exponent===void 0&&a>=1e3&&n<R.length-1,d=c?n+1:n;return {value:c?Math.round(e/Math.pow(1e3,d)*s)/s:a,symbol:R[d],exponent:d}}var P=e=>I.includes(e),ne=e=>["upload","snapshot"].includes(e),I=["initialize","build","upload","verify","snapshot"],x={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:e})=>{let{numerator:r,denominator:t}=e.upload;if(t==null||r==null||t<=0)return "Uploading files...";let{value:o,exponent:n}=U(t,{round:1}),{value:i,symbol:s}=U(r,{exponent:n,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:e})=>{let{numerator:r,denominator:t}=e.snapshot;return t?`Running visual tests... ${r}/${t}`:"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}},Te={buildProgressPercentage:0,currentStep:I[0],stepProgress:Object.fromEntries(I.map(e=>[e,{}]))},se=JSON.stringify(Te);var ie=2e3,D,Re=(e,r)=>{if(!P(e))throw new Error(`Unknown step: ${e}`);let t=I.map(d=>{let{startedAt:p,completedAt:f}=r?.[d]||{};return p&&f?f-p:x[d].estimateDuration}),o=t.reduce((d,p)=>d+p,0),n=I.indexOf(e),i=t.slice(0,n).reduce((d,p)=>d+p,0),s=i+t[n],a=i/o*100,c=s/o*100;return {...x[e],startPercentage:a,endPercentage:c,stepPercentage:c-a}},F=(e,r)=>(t,{progress:o,total:n}={})=>{if(clearTimeout(r),!P(t.task))return;if(!e.value)throw new Error("Unexpected missing value for localBuildProgress");let{buildProgressPercentage:i,stepProgress:s,previousBuildProgress:a}=e.value;if(s[t.task]?.completedAt)return;let{startPercentage:c,endPercentage:d,stepPercentage:p}=Re(t.task,a),f=c;if(o&&n&&(f+=p*(o/n)),!ne(t.task)){let{estimateDuration:S}=x[t.task],E=I.indexOf(t.task);f=Math.max(f,i)+ie/S*p,r=setTimeout(()=>{if(!e.value)throw new Error("Unexpected missing value for localBuildProgress");let{currentStep:_}=e.value;P(_)&&I.indexOf(_)<=E&&F(e,r)(t);},ie);}s[t.task]={startedAt:Date.now(),...s[t.task],...o&&n&&{numerator:o,denominator:n}},e.value={buildId:t.announcedBuild?.id,branch:t.git?.branch,commit:t.git?.commit,uncommittedHash:t.git?.uncommittedHash,buildProgressPercentage:Math.min(f,d),currentStep:t.task,stepProgress:s};},ae=(e,r)=>(t,o)=>{if(clearTimeout(r),!e.value)throw new Error("Unexpected missing value for localBuildProgress");let{buildProgressPercentage:n,stepProgress:i}=e.value,s={buildId:t.announcedBuild?.id,branch:t.git?.branch,buildProgressPercentage:n,stepProgress:i,previousBuildProgress:i};if(o){e.value={...s,currentStep:D?.signal.aborted?"aborted":"error",formattedError:o.formattedError,originalError:o.originalError};return}t.task&&P(t.task)&&(i[t.task]={...i[t.task],completedAt:Date.now()}),t.task==="verify"&&t.build?.wasLimited&&(e.value={...s,currentStep:"limited",stepProgress:i,errorDetailsUrl:t.build?.app.account?.billingUrl}),t.build&&t.task==="snapshot"&&(e.value={...s,buildProgressPercentage:100,currentStep:"complete",stepProgress:i,changeCount:t.build.changeCount,errorCount:t.build.errorCount});},le=async(e,r)=>{if(!r.projectId)throw new Error("Missing projectId");if(!r.userToken)throw new Error("Missing userToken");e.value=JSON.parse(se);let t;D?.abort(),D=new AbortController,process.env.SB_TESTBUILD="true",await node.run({flags:{interactive:!1},options:{...r,...v,experimental_onTaskStart:F(e,t),experimental_onTaskProgress:F(e,t),experimental_onTaskComplete:ae(e,t),experimental_onTaskError:ae(e,t),experimental_abortSignal:D?.signal}});},ce=()=>{D?.abort(new Error("Build canceled from Storybook"));};var G=new Map,b=class{constructor(r,t=fetch){this.channel=r,this.abortControllers=new Map,this.channel.on(re,({requestId:o})=>{this.abortControllers.get(o)?.abort(),this.abortControllers.delete(o);}),this.channel.on(oe,async({requestId:o,input:n,init:i})=>{let s=new AbortController;this.abortControllers.set(o,s);try{let a=await t(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(N,{requestId:o,response:p});}catch(a){let c=a instanceof Error?a.message:String(a);this.channel.emit(N,{requestId:o,error:c});}finally{this.abortControllers.delete(o);}});}static subscribe(r,t,o=fetch){let n=G.get(r)||new b(t,o);return G.has(r)||G.set(r,n),n}};var ve=e=>{let o=e.trim().replace(/#.*$/,"").replace(/^.*@/,"").replace(/^.*\/\//,"");return (o.endsWith(".git")?o:`${o}.git`).replace(":","/")},we=e=>e.replace(/\\/g,"/"),xe=(e,r)=>{if(!e)return null;let t=`${ve(e)}${we(r)}`;return crypto$1.createHash("sha256").update("chromatic-storybook-id-salt").update(t).digest("hex")},ue=async()=>{try{let{executeCommandSync:e,getProjectRoot:r}=await import('storybook/internal/common'),t=path.relative(r(),process.cwd()),o=e({command:"git",args:["config","--get","remote.origin.url"],timeout:1e3});return xe(o,t)}catch{return null}};var de="experimental_useSharedState_getValue",A="experimental_useSharedState_setValue",$=new Map,g=class{constructor(r){this.channel=r,this.listeners=[],this.state={},this.channel.on(A,(t,o,n)=>{this.state?.[t]?.index>=n||(this.state[t]={index:n,value:o});}),this.channel.on(de,t=>{let o=this.state[t]?.index??0,n=this.state[t]?.value;this.channel.emit(A,t,n,o);});}get(r){return this.state[r]||this.channel.emit(de,r),this.state[r]?.value}set(r,t){let o=(this.state[r]?.index??0)+1;this.state[r]={index:o,value:t},this.channel.emit(A,r,t,o);}static subscribe(r,t){let o=$.get(r)||new g(t);return $.has(r)||($.set(r,o),o.channel.on(A,(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 pe(e,r){let t=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(e,t,{spaces:2});}var B=module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('out.js', document.baseURI).href))),Ie=node.createLogger(void 0,v);function Me(e=[]){return [...e,B.resolve("./manager.mjs")]}function je(e=[]){return [...e,B.resolve("./preview.mjs")]}var Ee=async()=>{let e=(async()=>{try{let r=B.resolve("@chromatic-com/storybook/package.json"),t=await promises.readFile(r,"utf-8");return JSON.parse(t).version||null}catch{return null}})();return Ee=()=>e,e},fe=(e,r,t)=>Object.fromEntries(Object.entries(t).map(([o,n])=>[o,n===r[o]?null:n]).filter(([o,n])=>n!==null||e[o]!==void 0)),ge=async(e,r)=>{let t={storybookBaseDir:".",storybookConfigDir:".storybook"},o={},n={},{repositoryRootDir:i}=await node.getGitInfo({log:Ie}),s=i&&path.normalize(path.relative(i,process.cwd()));s!==path.normalize(e.storybookBaseDir??"")&&(o.storybookBaseDir=s);let a=path.normalize(path.relative(process.cwd(),r.configDir));return a!==path.normalize(e.storybookConfigDir??"")&&(o.storybookConfigDir=a),e.onlyChanged===void 0&&(n.onlyChanged=!0),e.zip===void 0&&(n.zip=!0),{configuration:e,problems:fe(e,t,o),suggestions:fe(e,t,n)}},Ve=(e,r,t,o)=>{let n,i,s,a=async()=>{try{let c=await node.getGitInfo({log:Ie});Object.entries(c).some(([d,p])=>n?.[d]!==p)&&r(c,n),n=c,i=void 0,s=setTimeout(a,e);}catch(c){t(c),o&&i?.message!==c.message&&(console.error(`Failed to fetch git info, with error:
${c}`),n=void 0,i=c),s=setTimeout(a,e);}};return a(),{cancel:()=>clearTimeout(s)}},Ke=async(e,r)=>{let t=await node.getConfiguration(e);await r(t),t.configFile&&fs.watch(t.configFile,async(o,n)=>{n&&await r(await node.getConfiguration(n));});};async function Ye(e,r){let{configFile:t,presets:o}=r;b.subscribe(l,e);let n=o.apply("experimental_serverAPI"),i=o.apply("core"),{projectId:s}=await node.getConfiguration(t),a=g.subscribe(J,e);a.value=s?{projectId:s}:{};let c=coreServer.experimental_getTestProviderStore(j),d=s;a.on("change",async({projectId:u}={})=>{if(!u||u===d)return;d=u;let h=t;try{let{configFile:m,...C}=await node.getConfiguration(h),T=m||h||"chromatic.config.json",{problems:y,suggestions:O}=await ge(C,r);await pe(T,{...C,...y,...O,projectId:u}),a.value={...a.value,written:!0,dismissed:!1,configFile:T};}catch(m){console.warn(`Failed to update your main configuration:
${m}`),a.value={...a.value,written:!1,dismissed:!1,configFile:h};}});let p=g.subscribe(q,e);e.on(z,async({accessToken:u})=>{let{projectId:h}=a.value||{};c.runWithState(async()=>{try{await le(p,{configFile:t,projectId:h,userToken:u});}catch(m){throw console.error(`Failed to run Chromatic build, with error:
${m}`),m}});}),e.on(W,()=>{c.setState("test-provider-state:succeeded"),ce();});let f=g.subscribe(X,e),S=null,E=null,_=null;e.on(Q,async({accessToken:u,shareRequestId:h})=>{if(E||h&&h===_)return;let m=h??crypto.randomUUID();E=m;let C=new AbortController;S=C;let T=!1;f.value={status:"pending",shareRequestId:m};try{let y=await node.share({userToken:u,abortSignal:C.signal,onUrl:O=>{f.value={status:"uploading",shareUrl:O,shareRequestId:m};},onError:O=>{T=!0,f.value={status:"error",error:O.message,shareRequestId:m};}});C.signal.aborted?f.value={status:"error",error:"canceled",canceled:!0,shareRequestId:m}:T||(f.value={status:"complete",shareUrl:y.shareUrl,daysToExpire:y.daysToExpire,shareRequestId:m},_=m);}catch(y){C.signal.aborted?f.value={status:"error",error:"canceled",canceled:!0,shareRequestId:m}:f.value={status:"error",error:y?.message||"Share failed",shareRequestId:m};}finally{S=null,E=null;}}),e.on(Z,({shareRequestId:u}={})=>{u&&E&&u!==E||S?.abort();}),e.on(ee,async u=>{(await i).disableTelemetry||telemetry.telemetry("addon-visual-tests",{...u,addonVersion:await Ee()});});let Ce=g.subscribe(V,e),ye=g.subscribe(K,e),H=g.subscribe(Y,e),be=Ve(5e3,u=>{H.value=void 0,ye.value=u;},u=>{H.value=u;});return Ke(t,async u=>{d&&(Ce.value=await ge(u,r));}),e.on(te,()=>{n.then(u=>u.removeAddon(M)).catch(u=>console.error(u)),be.cancel();}),e}async function Je(e){let r=await ue();return r?`${e}
<script>window.__CHROMATIC_STORYBOOK_ID__ = ${JSON.stringify(r)};</script>`:e}var ze={managerEntries:Me,managerHead:Je,previewAnnotations:je,experimental_serverChannel:Ye,staticDirs:async e=>[...e,{from:path.join(path.dirname(B.resolve("@chromatic-com/storybook/package.json")),"assets"),to:"addon-visual-tests-assets"}],env:async(e,{configType:r})=>r==="PRODUCTION"?e:{...e,CHROMATIC_BASE_URL:w}},Gt=ze;
module.exports = Gt;