@cmtlyt/git-down
Version:
3 lines (2 loc) • 4.28 kB
JavaScript
import{extname as F,resolve as g}from"node:path";import{exec as q}from"node:child_process";import{existsSync as b}from"node:fs";import{mkdir as I,rm as M,cp as O,rename as R,readFile as W,writeFile as P}from"node:fs/promises";import{promisify as T}from"node:util";const p=T(q),E=new Map;async function C(t){if(!b(t))return I(t,{recursive:!0})}async function k(t){if(b(t))return M(t,{recursive:!0,force:!0}).catch(()=>{})}function z(t){const r=t?.branch?.trim();return{output:t?.output??"./git-down",branch:r??""}}function G(t){return r=>{if(r){if(!t)throw r;t(r)}t&&t(null)}}function D(t){const r=l=>l.replace(/\.git$/,""),o=t.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+)(?:\/(tree|blob)\/(.+))?/),e=t.match(/^git@github\.com:([^/]+)\/([^/]+)(?:\/(tree|blob)\/(.+))?/),n=o??e;if(!n)return{href:t,owner:"",project:r(t),isRepo:!0,sourceType:"dir",branch:"",pathname:""};const[,i,a,c,u=""]=n,h=r(a),s=u?u.split(/[?#]/)[0]:"";let f="",$="";if(s){const l=s.indexOf("/");l===-1?f=s:(f=s.slice(0,l),$=s.slice(l+1))}const y=!c,d=$.replace(/^\//,""),m=c==="blob"||d&&F(d)?"file":"dir";return{href:t,owner:i,project:h,isRepo:y,sourceType:m,branch:f,pathname:d}}function A(t){return t.replace(/^\/+/,"")}function w(t){return t.replace(/^\/+|\/+$/g,"")}function U(t,r){if(!r)return;const o=w(t.branch),e=w(r);t.branch=e;const n=A(t.pathname);if(!n)return;if(n===e){t.pathname="";return}if(n.startsWith(`${e}/`)){t.pathname=n.slice(e.length+1);return}if(!o||o===e||!e.startsWith(`${o}/`))return;const i=e.slice(o.length),a=w(i);if(a){if(n===a){t.pathname="";return}n.startsWith(`${a}/`)&&(t.pathname=n.slice(a.length+1))}}async function H(t,r){const o=`${t}/${r}`;if(E.has(o))return E.get(o);const e=p(`git ls-remote --heads https://github.com/${t}/${r}`).then(({stdout:n})=>{const i=new Set,a=/^refs\/heads\/(.+)$/;return n.split(/\r?\n/).map(c=>c.trim()).filter(Boolean).forEach(c=>{const[,u]=c.split(/\s+/);if(!u)return;const h=u.match(a);h?.[1]&&i.add(h[1])}),i}).catch(()=>new Set);return E.set(o,e),e}async function J(t,r,o=H){const e=r?.trim();if(e)return e;const n=w(t.branch);if(!n||!t.owner||!t.project||!t.pathname)return t.branch;const i=t.pathname.split("/").filter(Boolean);if(!i.length)return t.branch;let a;try{a=await o(t.owner,t.project)}catch{return t.branch}if(!a.size)return t.branch;for(let c=i.length-1;c>=0;c--){const u=w([n,...i.slice(0,c)].join("/"));if(!u||!a.has(u))continue;const h=i.slice(c);if(h.length)return t.branch=u,t.pathname=h.join("/"),t.branch}return t.branch}async function v(t,r){return b(r)?O(t,r,{recursive:!0}):R(t,r)}function K(t){const{gitInfo:r,option:o,callback:e}=t,{branch:n,output:i}=o,{owner:a,project:c}=r,u={cwd:g(i)};return p("git init --quiet",u).then(()=>p(`git remote add origin https://github.com/${a}/${c}`,u),e).then(()=>p(`git pull origin --quiet ${n} --depth 1`,u),e).then(()=>k(g(i,".git")),e)}async function L(t){const{gitInfo:r,callback:o,option:e}=t,{output:n}=e,{pathname:i,sourceType:a,branch:c,owner:u,project:h}=r,s=g(n,`./.git-down-temp-folder-${Date.now()}-${Math.random().toString(36).slice(2)}`);b(s)&&k(s),await C(s).catch(o);const f=g(s,".git/info/sparse-checkout"),$=a==="file"?i:`${i}/*`,y=`${b(f)?await W(f,{encoding:"utf-8"}):""}
${$}`,d=g(n,i.split("/").pop()),m={cwd:s},l=`remote-${Date.now()}-${Math.random().toString(36).slice(2)}`;return p("git init --quiet",m).then(()=>p(`git remote add ${l} https://github.com/${u}/${h}`,m),o).then(async()=>{try{if((await p("git config --get core.sparseCheckout",m)).stdout.trim()==="true")return}catch{}return p("git config core.sparseCheckout true",m)},o).then(()=>P(f,y),o).then(()=>p(`git pull ${l} --quiet ${c} --depth 1`,m),o).then(()=>{const j=g(s,i);if(b(j))return v(j,d);const x=g(s,i.split("/").pop());if(b(x))return v(x,d);throw new Error(`\u65E0\u6CD5\u5728\u4E0B\u8F7D\u76EE\u5F55\u4E2D\u627E\u5230\u76EE\u6807\u8D44\u6E90: ${i}`)},o).then(()=>k(s),o)}function S(t,r,o){const e=z(r),n=G(o),i=D(t);J(i,e.branch).then(a=>{let c=a||e.branch||i.branch;c||(c="main"),U(i,c),e.branch=i.branch;const u={option:e,callback:n,gitInfo:i};return C(e.output).then(()=>(i.isRepo?K:L)(u),n).then(()=>n(null),n)},n).catch(n)}function B(t,r){return new Promise((o,e)=>{S(t,r,n=>{n?e(n):o()})})}export{B as default,B as gitDown,S as gitDownWithCallback,D as parseGitUrl};