vite-plugin-seo-prerender
Version:
`vite-plugin-seo-prerender` 插件是一个用于 `Vite` 构建工具的预渲染插件,它可以将你的单页面应用 (SPA) 在构建时静态预渲染为 HTML 文件,以提高首次加载速度和SEO友好性。适用于对站点少量页面生成静态HTML。支持 `Vue、React`等所有框架
10 lines (9 loc) • 5.43 kB
JavaScript
/**
* name: vite-plugin-seo-prerender
* version: 1.1.1
*/
import q from"child_process";import h from"path";import z from"fs";import*as E from"sass";import L from"puppeteer";import R from"fs";import D from"path";import x from"fs";import H from"path";var f=t=>t&&t.replace(/\\/g,"/"),y=t=>{let e=H.dirname(t);x.existsSync(e)||y(e),x.existsSync(t)||x.mkdirSync(t)};function j(t,e=5){let o=t.length,r=0,n=[],i=(l,a,g)=>(n.push({index:l,result:a,error:g}),s()),s=()=>{if(r>=o)return Promise.resolve();let l=r++;return t[l]().then(a=>i(l,a)).catch(a=>i(l,null,a))},c=Math.max(1,Math.min(e,o)),u=[];for(let l=0;l<c;l++)u.push(s());return Promise.allSettled(u).then(()=>n.sort((l,a)=>l.index-a.index))}import{URL as A}from"url";function W(t){return new Promise(e=>setTimeout(e,t))}var I=async t=>{let e=await L.launch(Object.assign({headless:"new"},t.puppeteer||{})),o="[vite-plugin-seo-prerender:routes]",r={};t.network&&(r={waitUntil:"networkidle0"});let n=new A(t.base,t.local).toString().slice(0,-1),i=t.routes.length,s=0,c=async a=>{let g=await e.newPage();try{let w=n+a;t.hashHistory&&(w=`${n}/#${a}`),await g.goto(w,r),await g.setViewport({width:1024,height:768}),await g.waitForSelector("body"),t.delay&&await W(t.delay);let d=await g.content();t.removeStyle!==!1&&(d=d.replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi,""));let M=new RegExp(t.local,"g");if(d=d.replace(M,""),t.callback&&(d=t.callback(d,a)||d),a.indexOf("?")!==-1)console.log(`${o} ${a} is error,unexpected?`);else{let k=D.join(t.outDir,a);y(k);let v=D.join(k,"index.html");R.writeFileSync(v,d),console.log(`[${s++}/${i}]${o} ${w.replace(t.local,"")} => ${v.replace(/\\/g,"/")} is success!`)}}finally{await g.close()}},u=t.routes.map(a=>()=>c(a)),l=Math.min(Math.max(1,Number(t.concurrency||1)),i);console.log(`${o} \u5F00\u59CB\u9884\u6E32\u67D3\uFF0C\u5E76\u53D1\u6570: ${l}`),await j(u,l),await e.close(),console.log(`${o} is complete`)},$=I;import m from"fs";import p from"path";var b="",O=(t,e,o)=>{let r=m.readFileSync(p.join(t,"index.html"),"utf-8");if(e==="server"){let n=/<script[^>]*?\b\/main\b[^>]*?>.*?<\/script>/gi;return(r.match(n)||[]).join(`
`)}else{let n=/<link[^>]*?rel=['|"]stylesheet['|"][^>]*?\.css[^>]*?>/gi,i=r.match(n),c=m.readFileSync(p.join(t,o||"","index.html"),"utf-8").match(n)||[],u=c;return i&&(u=c.filter(l=>!i.includes(l))),u.join(`
`).replace('"./','"/')}},C=(t,e)=>t.replace(/<!--link\shref="(.*)"-->/gi,function(o,r){let n=p.join(e,r);return m.existsSync(n)?m.readFileSync(n,{encoding:"utf8"}):o}),F=(t,e,o)=>{let n=m.readFileSync(e,"utf-8").replace("</head>",`${b}
</head>`);n=C(n,t),typeof o=="function"&&(n=o(n,e)||n),m.writeFileSync(e,n),console.log("[vite-plugin-seo-prerender:publicHtml] "+f(p.relative(t,e)))},U=(t,e,o,r)=>{if(!m.existsSync(t)){console.log(`${t}\u8DEF\u5F84\u4E0D\u5B58\u5728`);return}m.readdirSync(t).forEach(i=>{let s=p.join(t,i);m.statSync(s).isFile()&&p.extname(s)===".html"&&!f(s).endsWith(e)&&F(o,s,r),m.statSync(s).isDirectory()&&U(s,e,o,r)})},B=async t=>{let{mode:e,root:o,filePath:r,outDir:n=""}=t;if(b||(b=O(o,e,n)),e==="server"){let i=p.join(o,"public",r),s=m.readFileSync(i,"utf-8");return s=s.replace("</body>",`${b}
</body>`),s=C(s,o),typeof t.callback=="function"&&(s=t.callback(s,r)||s),s}else if(typeof r=="boolean"){let i=p.join(o,n),s=f(p.join(n,"index.html"));U(i,s,o,t.callback)}else for(let i in r){let s=p.join(o,n,r[i]);F(o,s,t.callback)}},P=B;var S=t=>{let e=[];return typeof t=="object"&&(e=t||[]),{allUrl:e,isAllUrl:typeof t=="boolean"&&t}},T=(t,e)=>{let o=h.join(t,e.entry),r=E.compile(o),n=h.join(t,e.outDir);y(h.dirname(n)),z.writeFileSync(n,r.css),console.log(`transform scss: ${e.entry} => ${e.outDir}`)},G=t=>{let e={outDir:"",mode:"",root:"",local:"",base:"",isProduction:!1,command:""},o=t.publicHtml||!1;return{name:"vitePluginSeoPrerender",enforce:"post",configResolved(r){e.outDir=r.build.outDir,e.mode=r.mode,e.root=r.root,e.base=r.base,e.isProduction=r.isProduction,e.command=r.command},buildStart(){var r;(r=t==null?void 0:t.scss)!=null&&r.length&&t.scss.forEach(n=>{T(e.root,n)})},configureServer(r){let{allUrl:n,isAllUrl:i}=S(o);(n.length||i)&&r.middlewares.use(async(s,c,u)=>{let l=decodeURIComponent(s.url.replace(e.base,"/"));if(i&&l.endsWith(".html")||n.includes(l)){let a=await P({root:e.root,filePath:l,mode:"server",callback:t.callback});if(a){c.setHeader("Content-Type","text/html"),c.end(a);return}}u()})},handleHotUpdate({file:r,server:n}){var i;if(r.endsWith(".html")){let{allUrl:s,isAllUrl:c}=S(o);if(c||s.length){let u=h.join(e.root,"public"),l=h.relative(u,r);n.ws.send({type:"full-reload",path:"/"+f(l)})}}if((i=t==null?void 0:t.scss)!=null&&i.length&&r.endsWith(".scss")){let s=f(r);t.scss.forEach(c=>{s.includes(c.entry)&&T(e.root,c)})}},async closeBundle(){var c;if(!e.isProduction)return;let{allUrl:r,isAllUrl:n}=S(o);if((n||r.length)&&await P({root:e.root,filePath:n||r,mode:"build",outDir:e.outDir,callback:t.callback}),!((c=t==null?void 0:t.routes)!=null&&c.length))return;console.log("[vite-plugin-seo-prerender:routes] is start..");let i=q.exec("vite preview",u=>{if(u){console.error("\u6267\u884C\u547D\u4EE4\u65F6\u53D1\u751F\u9519\u8BEF\uFF1A",u);return}}),s="";i.stdout.on("data",async u=>{let l=u.match(/http:\/\/(.*?)\//g);l&&l.length&&!s&&(s=l[0].replace(/\x1B\[\d+m/g,"").slice(0,-1),console.log("Local: "+s),e.local=s,await $(Object.assign(e,t)),i.kill("SIGTERM"),process.exit(),s="")})}}},ut=G;export{ut as default};