@vercel/geistdocs 1.0.6 → 1.0.7

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