@vercel/geistdocs 1.0.3 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +1 -1
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{program as T}from"commander";import{copyFile as M,rm as _}from"fs/promises";import{join as E}from"path";import{cancel as k,intro as K,isCancel as X,log as B,outro as J,spinner as V,text as W}from"@clack/prompts";import{exec as G}from"child_process";import{promisify as I}from"util";var j="https://github.com/vercel/geistdocs";var m={stdio:"ignore"};var x="geistdocs-update",g=I(G);var q="https://github.com/vercel/geistdocs.git",H=async t=>{await g(`git clone --depth 1 --filter=blob:none --sparse ${q} ${t}`,m),await g("git sparse-checkout set apps/template",{...m,cwd:t}),await g("mv apps/template/* apps/template/.[!.]* . 2>/dev/null || true",{...m,cwd:t,shell:"/bin/bash"}),await g("rm -rf apps .git",{...m,cwd:t})},Z=async()=>{await M(".env.example",".env.local")},Q=async()=>{await _(E("content","docs"),{recursive:!0,force:!0})},Y=async()=>{await g("pnpm install",m)},tt=async()=>{await g("git init",m),await g("git add .",m),await g('git commit -m "\u2728 Initial commit"',m)},et=async()=>{let t=await W({message:"What is your project named?",placeholder:"my-app",validate(s){if(s.length===0)return"Please enter a project name."}});return X(t)&&(k("Operation cancelled."),process.exit(0)),t.toString()},S=async t=>{try{K("Let's start a Geistdocs project!");let s=process.cwd(),e=t.name||await et(),o=V(),r=E(s,e);o.start("Cloning template..."),await H(e),o.message("Moving into project..."),process.chdir(r),o.message("Setting up environment variables..."),await Z(),o.message("Deleting demo content..."),await Q(),o.message("Installing dependencies..."),await Y(),t.disableGit||(o.message("Initializing Git repository..."),await tt()),o.stop("Project initialized successfully!"),J("Run `pnpm dev` to start the development server.")}catch(s){let e=s instanceof Error?s.message:`Failed to initialize project: ${s}`;B.error(e),process.exit(1)}};import{readFile as st,writeFile as ot}from"fs/promises";import{dirname as rt,parse as R,resolve as D}from"path";import{intro as nt,isCancel as it,log as u,outro as h,spinner as at,text as ct}from"@clack/prompts";import{glob as lt}from"glob";import{Project as pt,SyntaxKind as $}from"ts-morph";var F=10,gt="content/docs/**/*.mdx",dt=/\.[a-z]{2}\.mdx$/,mt=async t=>(await lt(t,{cwd:process.cwd(),absolute:!0,nodir:!0})).filter(e=>e.endsWith(".mdx")).filter(e=>!dt.test(e)),ft=t=>{let s=D(process.cwd(),t),r=new pt({skipAddingFilesFromTsConfig:!0}).addSourceFileAtPath(s).getVariableDeclaration("translations");if(!r)throw new Error("Could not find translations export in config file");let a=r.getInitializer();if(!a||a.getKind()!==$.ObjectLiteralExpression)throw new Error("translations must be an object literal");let c=a.asKind($.ObjectLiteralExpression);if(!c)throw new Error("Could not parse translations object");let l=c.getProperties().filter(n=>n.getKind()===$.PropertyAssignment).map(n=>n.asKind($.PropertyAssignment)?.getName()).filter(n=>n!==void 0);if(l.length===0)throw new Error("No locales found in translations object");let[i,...p]=l;return p},ut=async(t,s,e)=>{let o=await st(t,"utf-8"),r=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({content:o,locales:s})}),a=await r.text();if(!r.ok){let c=`Translation failed with status ${r.status}`;try{let l=JSON.parse(a);c=l.error||l.message||c}catch{c=a||c}throw new Error(c)}try{return JSON.parse(a)}catch{throw new Error(`Failed to parse response: ${a.slice(0,200)}`)}},wt=async(t,s)=>{let e=rt(t),{name:o,ext:r}=R(t);for(let[a,c]of Object.entries(s)){let l=D(e,`${o}.${a}${r}`);await ot(l,c,"utf-8")}},ht=async(t,s,e,o,r,a)=>{let c=new Map,l=0,i=()=>{a.message(`Batch ${o+1}/${r}: ${l}/${t.length} files translated`)};i();let p=await Promise.all(t.map(async n=>{let d=await ut(n,s,e);return l++,i(),{file:n,result:d}}));for(let{file:n,result:d}of p)c.set(n,d);return c},C=async(t,s)=>{nt("Translate MDX content");let e=t??gt,o=[],r=s.url||process.env.GEISTDOCS_TRANSLATE_URL||"https://geistdocs.com/translate",a=s.config??"geistdocs.tsx";if(a)try{o=ft(a),u.info(`Loaded ${o.length} locale(s) from config: ${o.join(", ")}`)}catch(i){u.error(`Failed to load config: ${i instanceof Error?i.message:String(i)}`),h("Translation cancelled"),process.exit(1)}let c=await mt(e);if(c.length===0&&(u.error(`No MDX files found matching pattern: ${e}`),h("Translation cancelled"),process.exit(1)),u.info(`Found ${c.length} MDX file(s) matching pattern`),o.length===0){let i=await ct({message:"Enter target locales (comma-separated):",placeholder:"cn,fr,es",validate:p=>{if(!p)return"At least one locale is required"}});it(i)&&(h("Translation cancelled"),process.exit(0)),o=i.split(",").map(p=>p.trim())}let l=at();try{let i=[];for(let n=0;n<c.length;n+=F)i.push(c.slice(n,n+F));u.info(`Processing ${c.length} file(s) in ${i.length} batch(es) of up to ${F}`);let p=new Map;l.start("Starting translation...");for(let n=0;n<i.length;n++){let d=i[n],b=await ht(d,o,r,n,i.length,l);for(let[w,v]of b)p.set(w,v);n<i.length-1&&(l.message(`Batch ${n+1}/${i.length} complete. Pausing...`),await new Promise(w=>setTimeout(w,1e3)))}l.message("Saving translations...");for(let[n,d]of p)await wt(n,d);l.stop("Translation complete!");for(let[n,d]of p){let{name:b,ext:w}=R(n);for(let v of Object.keys(d))u.success(`Saved: ${b}.${v}${w}`)}h(`Translated ${p.size} file(s) to ${o.join(", ")}`)}catch(i){let p=i instanceof Error?i.message:String(i);l.stop(`Translation failed: ${p}`),h("Exiting"),process.exit(1)}};import{copyFile as yt,mkdir as L,rm as N}from"fs/promises";import{dirname as xt,join as y}from"path";import{intro as $t,log as f,outro as O,spinner as Tt}from"@clack/prompts";import{glob as bt}from"glob";var A="apps/template",U=["**/*","!content/docs"],vt=async t=>{let s=process.cwd(),e=y(s,t);await N(e,{recursive:!0,force:!0}),await L(e,{recursive:!0})},jt=async t=>await g(`git clone --depth 1 ${j} ${t}`),P=async t=>await N(t,{recursive:!0,force:!0}),Ft=async(t,s)=>(await bt(t,{cwd:s,nodir:!0,dot:!0,ignore:".git/**"})).sort(),Pt=async(t,s)=>{let e=process.cwd(),o=y(s,A,t),r=y(e,t);await L(xt(r),{recursive:!0}),await yt(o,r)},Et=async(t,s,e)=>{let o={success:[],failed:[]};for(let r of t)try{e.message(`Updating ${r}...`),await Pt(r,s),o.success.push(r)}catch(a){let c=a instanceof Error?a.message:`${a}`;o.failed.push({file:r,error:c})}return o},St=t=>{if(t.success.length>0){f.success(`Successfully updated ${t.success.length} files:`);for(let s of t.success)f.info(` \u2713 ${s}`)}if(t.failed.length>0){f.warn(`Failed to update ${t.failed.length} files:`);for(let{file:s,error:e}of t.failed)f.error(` \u2717 ${s}: ${e}`)}},z=async()=>{let t=process.cwd(),s=y(t,x);try{$t("Let's update your Geistdocs project!"),f.info(`Repository: ${j}`);let e=Tt();e.start("Creating temporary directory..."),await vt(x),e.message("Cloning repository..."),await jt(x),e.message("Scanning for files...");let o=y(s,A),r=await Ft(U,o);if(e.stop(`Found ${r.length} files to update`),r.length===0){f.warn("No files matched the patterns. Nothing to update."),await P(s),O("Update cancelled.");return}f.info(`Patterns: ${U.length}`),f.info(`Files to update: ${r.length}`),e.start("Updating files...");let a=await Et(r,s,e);e.message("Cleaning up..."),await P(s),e.stop("Update complete!"),St(a),O("Please review and test the changes carefully.")}catch(e){let o=e instanceof Error?e.message:`${e}`;try{await P(s)}catch{}f.error(`Failed to update project: ${o}`),process.exit(1)}};T.command("init").description("Initialize a new Geistdocs project").option("--name <name>","Name of the project").option("--disable-git","Disable git initialization").action(S);T.command("update").description("Update a Geistdocs project").action(z);T.command("translate [pattern]").description("Translate MDX file(s) to one or more locales (default: content/docs/**/*.mdx)").option("--config <path>","Path to config file to load locales from (defaults to geistdocs.tsx)").option("--url <url>","Custom translation API URL (defaults to GEISTDOCS_TRANSLATE_URL or https://geistdocs.com/translate)").action(C);T.parse(process.argv);
2
+ import{program as O}from"commander";import{copyFile as st,rm as nt}from"fs/promises";import{join as _}from"path";import{cancel as ot,intro as at,isCancel as rt,log as it,outro as ct,spinner as lt,text as pt}from"@clack/prompts";import{exec as tt}from"child_process";import{promisify as et}from"util";var A="https://github.com/vercel/geistdocs";var m={stdio:"ignore"};var E="geistdocs-update",p=et(tt);var gt="https://github.com/vercel/geistdocs.git",dt=async t=>{await p(`git clone --depth 1 --filter=blob:none --sparse ${gt} ${t}`,m),await p("git sparse-checkout set apps/template",{...m,cwd:t}),await p("mv apps/template/* apps/template/.[!.]* . 2>/dev/null || true",{...m,cwd:t,shell:"/bin/bash"}),await p("rm -rf apps .git",{...m,cwd:t})},ft=async()=>{await st(".env.example",".env.local")},mt=async()=>{await nt(_("content","docs"),{recursive:!0,force:!0})},ut=async()=>{await p("pnpm install",m)},ht=async()=>{await p("git init",m),await p("git add .",m),await p('git commit -m "\u2728 Initial commit"',m)},wt=async()=>{let t=await pt({message:"What is your project named?",placeholder:"my-app",validate(s){if(s.length===0)return"Please enter a project name."}});return rt(t)&&(ot("Operation cancelled."),process.exit(0)),t.toString()},H=async t=>{try{at("Let's start a Geistdocs project!");let s=process.cwd(),e=t.name||await wt(),n=lt(),a=_(s,e);n.start("Cloning template..."),await dt(e),n.message("Moving into project..."),process.chdir(a),n.message("Setting up environment variables..."),await ft(),n.message("Deleting demo content..."),await mt(),n.message("Installing dependencies..."),await ut(),t.disableGit||(n.message("Initializing Git repository..."),await ht()),n.stop("Project initialized successfully!"),ct("Run `pnpm dev` to start the development server.")}catch(s){let e=s instanceof Error?s.message:`Failed to initialize project: ${s}`;it.error(e),process.exit(1)}};import{readFile as k,writeFile as J}from"fs/promises";import{dirname as yt,parse as U,resolve as F}from"path";import{intro as Tt,isCancel as $t,log as d,outro as x,spinner as xt,text as Pt}from"@clack/prompts";import{glob as bt}from"glob";import{Project as vt,SyntaxKind as D}from"ts-morph";var N=10,St="content/docs/**/*.mdx",L=".translation-cache.json",Ft=/\.[a-z]{2}\.mdx$/,Ct=async t=>(await bt(t,{cwd:process.cwd(),absolute:!0,nodir:!0})).filter(e=>e.endsWith(".mdx")).filter(e=>!Ft.test(e)),jt=t=>{let s=F(process.cwd(),t),a=new vt({skipAddingFilesFromTsConfig:!0}).addSourceFileAtPath(s).getVariableDeclaration("translations");if(!a)throw new Error("Could not find translations export in config file");let r=a.getInitializer();if(!r||r.getKind()!==D.ObjectLiteralExpression)throw new Error("translations must be an object literal");let i=r.asKind(D.ObjectLiteralExpression);if(!i)throw new Error("Could not parse translations object");let g=i.getProperties().filter(l=>l.getKind()===D.PropertyAssignment).map(l=>l.asKind(D.PropertyAssignment)?.getName()).filter(l=>l!==void 0);if(g.length===0)throw new Error("No locales found in translations object");let[w,...y]=g;return y},Et=t=>createHash("sha256").update(t).digest("hex"),Dt=async()=>{let t=F(process.cwd(),L);if(!existsSync(t))return{};try{let s=await k(t,"utf-8");return JSON.parse(s)}catch{return d.warn("Failed to load translation cache, starting fresh"),{}}},Ot=async t=>{let s=F(process.cwd(),L);await J(s,JSON.stringify(t,null,2),"utf-8")},Rt=(t,s,e,n)=>{let a=t[s];if(!a)return!0;let r=a[e];return r?r.hash!==n:!0},At=async(t,s,e)=>{let n=await k(t,"utf-8"),a=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({content:n,locales:s})}),r=await a.text();if(!a.ok){let i=`Translation failed with status ${a.status}`;try{let g=JSON.parse(r);i=g.error||g.message||i}catch{i=r||i}throw new Error(i)}try{return JSON.parse(r)}catch{throw new Error(`Failed to parse response: ${r.slice(0,200)}`)}},Nt=async(t,s,e,n)=>{let a=yt(t),{name:r,ext:i}=U(t);for(let[g,w]of Object.entries(s)){let y=F(a,`${r}.${g}${i}`);await J(y,w,"utf-8"),e[t]||(e[t]={}),e[t][g]={hash:n,translatedAt:new Date().toISOString()}}},K=async(t,s)=>{Tt("Translate MDX content");let e=t??St,n=[],a=s.url||process.env.GEISTDOCS_TRANSLATE_URL||"https://geistdocs.com/translate",r=s.config??"geistdocs.tsx";if(r)try{n=jt(r),d.info(`Loaded ${n.length} locale(s) from config: ${n.join(", ")}`)}catch(o){d.error(`Failed to load config: ${o instanceof Error?o.message:String(o)}`),x("Translation cancelled"),process.exit(1)}let i=await Ct(e);if(i.length===0&&(d.error(`No MDX files found matching pattern: ${e}`),x("Translation cancelled"),process.exit(1)),d.info(`Found ${i.length} MDX file(s) matching pattern`),n.length===0){let o=await Pt({message:"Enter target locales (comma-separated):",placeholder:"cn,fr,es",validate:c=>{if(!c)return"At least one locale is required"}});$t(o)&&(x("Translation cancelled"),process.exit(0)),n=o.split(",").map(c=>c.trim())}let g=F(process.cwd(),L),w=await Dt();d.info(`Using cache: ${g}`);let y=new Map;for(let o of i){let c=await k(o,"utf-8");y.set(o,Et(c))}let l=[],T=0;for(let o of n)for(let c of i){let v=y.get(c);s.force||Rt(w,c,o,v)?l.push({filePath:c,locale:o,hash:v}):T++}let Xt=i.length*n.length;if(l.length===0){d.success("All translations are up to date!"),x(`Skipped ${T} file(s) (unchanged)`);return}let P=new Map;for(let o of l)P.has(o.filePath)||P.set(o.filePath,[]),P.get(o.filePath).push(o);let z=Array.from(P.keys()),$=[];for(let o=0;o<z.length;o+=N)$.push(z.slice(o,o+N));d.info(`Processing ${l.length} file(s) (${i.length} \xD7 ${n.length} locales) in ${$.length} batch(es) of up to ${N}`),T>0&&d.info(`Skipped ${T} file(s) (unchanged)`);let b=xt();try{let o=new Map;b.start("Starting translation...");let c=0;for(let h=0;h<$.length;h++){let R=$[h];for(let f of R){let j=P.get(f),S=j.map(Y=>Y.locale),Q=j[0].hash;b.message(`Translating ${U(f).name} (${S.length} locales) - ${c}/${l.length} complete`);let M=await At(f,S,a);o.set(f,M),await Nt(f,M,w,Q),await Ot(w),c+=S.length}h<$.length-1&&(b.message(`Batch ${h+1}/${$.length} complete. Pausing...`),await new Promise(f=>setTimeout(f,1e3)))}b.stop("Translation complete!");let v=0;for(let[h,R]of o){let{name:f,ext:j}=U(h);for(let S of Object.keys(R))d.success(`Saved: ${f}.${S}${j}`),v++}let G=[`Translated ${v} file(s)`];T>0&&G.push(`Skipped ${T} unchanged`),x(G.join(" \u2022 "))}catch(o){let c=o instanceof Error?o.message:String(o);b.stop(`Translation failed: ${c}`),x("Exiting"),process.exit(1)}};import{copyFile as Ut,mkdir as B,rm as W}from"fs/promises";import{dirname as kt,join as C}from"path";import{intro as Lt,log as u,outro as X,spinner as It}from"@clack/prompts";import{glob as zt}from"glob";var q="apps/template",V=["**/*","!content/docs/**"],Gt=async t=>{let s=process.cwd(),e=C(s,t);await W(e,{recursive:!0,force:!0}),await B(e,{recursive:!0})},Mt=async t=>await p(`git clone --depth 1 ${A} ${t}`),I=async t=>await W(t,{recursive:!0,force:!0}),_t=async(t,s)=>(await zt(t,{cwd:s,nodir:!0,dot:!0,ignore:".git/**"})).sort(),Ht=async(t,s)=>{let e=process.cwd(),n=C(s,q,t),a=C(e,t);await B(kt(a),{recursive:!0}),await Ut(n,a)},Jt=async(t,s,e)=>{let n={success:[],failed:[]};for(let a of t)try{e.message(`Updating ${a}...`),await Ht(a,s),n.success.push(a)}catch(r){let i=r instanceof Error?r.message:`${r}`;n.failed.push({file:a,error:i})}return n},Kt=t=>{if(t.success.length>0){u.success(`Successfully updated ${t.success.length} files:`);for(let s of t.success)u.info(` \u2713 ${s}`)}if(t.failed.length>0){u.warn(`Failed to update ${t.failed.length} files:`);for(let{file:s,error:e}of t.failed)u.error(` \u2717 ${s}: ${e}`)}},Z=async()=>{let t=process.cwd(),s=C(t,E);try{Lt("Let's update your Geistdocs project!"),u.info(`Repository: ${A}`);let e=It();e.start("Creating temporary directory..."),await Gt(E),e.message("Cloning repository..."),await Mt(E),e.message("Scanning for files...");let n=C(s,q),a=await _t(V,n);if(e.stop(`Found ${a.length} files to update`),a.length===0){u.warn("No files matched the patterns. Nothing to update."),await I(s),X("Update cancelled.");return}u.info(`Patterns: ${V.length}`),u.info(`Files to update: ${a.length}`),e.start("Updating files...");let r=await Jt(a,s,e);e.message("Cleaning up..."),await I(s),e.stop("Update complete!"),Kt(r),X("Please review and test the changes carefully.")}catch(e){let n=e instanceof Error?e.message:`${e}`;try{await I(s)}catch{}u.error(`Failed to update project: ${n}`),process.exit(1)}};O.command("init").description("Initialize a new Geistdocs project").option("--name <name>","Name of the project").option("--disable-git","Disable git initialization").action(H);O.command("update").description("Update a Geistdocs project").action(Z);O.command("translate [pattern]").description("Translate MDX file(s) to one or more locales (default: content/docs/**/*.mdx)").option("--config <path>","Path to config file to load locales from (defaults to geistdocs.tsx)").option("--url <url>","Custom translation API URL (defaults to GEISTDOCS_TRANSLATE_URL or https://geistdocs.com/translate)").option("--force","Force re-translation of all files, ignoring cache").action(K);O.parse(process.argv);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vercel/geistdocs",
3
3
  "description": "CLI for Geistdocs projects, including initialization and updating.",
4
- "version": "1.0.3",
4
+ "version": "1.0.4",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "geistdocs": "dist/index.js"