@vercel/geistdocs 1.0.0
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.
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/package.json +28 -0
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{program as T}from"commander";import{copyFile as _,rm as B}from"fs/promises";import{join as S}from"path";import{cancel as k,intro as K,isCancel as X,log as J,outro as V,spinner as W,text as q}from"@clack/prompts";import{exec as I}from"child_process";import{promisify as M}from"util";var P="https://github.com/vercel/geistdocs";var m={stdio:"ignore"};var x="geistdocs-update",g=M(I);var H="https://github.com/vercel/geistdocs.git",Z=async t=>{await g(`git clone --depth 1 --filter=blob:none --sparse ${H} ${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})},Q=async()=>{await _(".env.example",".env.local")},Y=async()=>{await B(S("content","docs"),{recursive:!0,force:!0})},tt=async()=>{await g("pnpm install",m)},et=async()=>{await g("git init",m),await g("git add .",m),await g('git commit -m "\u2728 Initial commit"',m)},st=async()=>{let t=await q({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()},j=async t=>{try{K("Let's start a Geistdocs project!");let s=process.cwd(),e=t.name||await st(),r=W(),o=S(s,e);r.start("Cloning template..."),await Z(e),r.message("Moving into project..."),process.chdir(o),r.message("Setting up environment variables..."),await Q(),r.message("Deleting demo content..."),await Y(),r.message("Installing dependencies..."),await tt(),t.disableGit||(r.message("Initializing Git repository..."),await et()),r.stop("Project initialized successfully!"),V("Run `pnpm dev` to start the development server.")}catch(s){let e=s instanceof Error?s.message:`Failed to initialize project: ${s}`;J.error(e),process.exit(1)}};import{readFile as rt,writeFile as ot}from"fs/promises";import{dirname as nt,parse as R,resolve as D}from"path";import{intro as it,isCancel as at,log as h,outro as y,spinner as ct,text as lt}from"@clack/prompts";import{glob as pt}from"glob";import{Project as gt,SyntaxKind as $}from"ts-morph";var F=10,dt="content/docs/**/*.mdx",mt=/\.[a-z]{2}\.mdx$/,ft=async t=>(await pt(t,{cwd:process.cwd(),absolute:!0,nodir:!0})).filter(e=>e.endsWith(".mdx")).filter(e=>!mt.test(e)),ut=t=>{let s=D(process.cwd(),t),o=new gt({skipAddingFilesFromTsConfig:!0}).addSourceFileAtPath(s).getVariableDeclaration("translations");if(!o)throw new Error("Could not find translations export in config file");let a=o.getInitializer();if(!a||a.getKind()!==$.ObjectLiteralExpression)throw new Error("translations must be an object literal");let n=a.asKind($.ObjectLiteralExpression);if(!n)throw new Error("Could not parse translations object");let l=n.getProperties().filter(i=>i.getKind()===$.PropertyAssignment).map(i=>i.asKind($.PropertyAssignment)?.getName()).filter(i=>i!==void 0);if(l.length===0)throw new Error("No locales found in translations object");let[c,...p]=l;return p},ht=async(t,s,e)=>{let r=await rt(t,"utf-8"),o=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({content:r,locales:s})}),a=await o.text();if(!o.ok){let n=`Translation failed with status ${o.status}`;try{let l=JSON.parse(a);n=l.error||l.message||n}catch{n=a||n}throw new Error(n)}try{return JSON.parse(a)}catch{throw new Error(`Failed to parse response: ${a.slice(0,200)}`)}},wt=async(t,s)=>{let e=nt(t),{name:r,ext:o}=R(t);for(let[a,n]of Object.entries(s)){let l=D(e,`${r}.${a}${o}`);await ot(l,n,"utf-8")}},yt=async(t,s,e,r,o,a)=>{let n=new Map,l=0,c=()=>{a.message(`Batch ${r+1}/${o}: ${l}/${t.length} files translated`)};c();let p=await Promise.all(t.map(async i=>{let d=await ht(i,s,e);return l++,c(),{file:i,result:d}}));for(let{file:i,result:d}of p)n.set(i,d);return n},O=async(t,s)=>{it("Translate MDX content");let e=t??dt,r=[],o=s.url||process.env.GEISTDOCS_TRANSLATE_URL||"https://geistdocs.com/translate",a=s.config??"geistdocs.tsx";if(a)try{r=ut(a),h.info(`Loaded ${r.length} locale(s) from config: ${r.join(", ")}`)}catch(c){h.error(`Failed to load config: ${c instanceof Error?c.message:String(c)}`),y("Translation cancelled"),process.exit(1)}let n=await ft(e);if(n.length===0&&(h.error(`No MDX files found matching pattern: ${e}`),y("Translation cancelled"),process.exit(1)),h.info(`Found ${n.length} MDX file(s) matching pattern`),r.length===0){let c=await lt({message:"Enter target locales (comma-separated):",placeholder:"cn,fr,es",validate:p=>{if(!p)return"At least one locale is required"}});at(c)&&(y("Translation cancelled"),process.exit(0)),r=c.split(",").map(p=>p.trim())}let l=ct();try{let c=[];for(let i=0;i<n.length;i+=F)c.push(n.slice(i,i+F));h.info(`Processing ${n.length} file(s) in ${c.length} batch(es) of up to ${F}`);let p=new Map;l.start("Starting translation...");for(let i=0;i<c.length;i++){let d=c[i],b=await yt(d,r,o,i,c.length,l);for(let[w,v]of b)p.set(w,v);i<c.length-1&&(l.message(`Batch ${i+1}/${c.length} complete. Pausing...`),await new Promise(w=>setTimeout(w,1e3)))}l.message("Saving translations...");for(let[i,d]of p)await wt(i,d);l.stop("Translation complete!");for(let[i,d]of p){let{name:b,ext:w}=R(i);for(let v of Object.keys(d))h.success(`Saved: ${b}.${v}${w}`)}y(`Translated ${p.size} file(s) to ${r.join(", ")}`)}catch(c){let p=c instanceof Error?c.message:String(c);l.stop(`Translation failed: ${p}`),y("Exiting"),process.exit(1)}};import{copyFile as xt,mkdir as C,readdir as $t,rm as L,stat as Tt}from"fs/promises";import{dirname as bt,join as u}from"path";import{intro as vt,log as f,outro as U,spinner as Pt}from"@clack/prompts";var N="apps/template",A=["**/*"],Ft=async t=>{let s=process.cwd(),e=u(s,t);await L(e,{recursive:!0,force:!0}),await C(e,{recursive:!0})},Et=async t=>await g(`git clone --depth 1 ${P} ${t}`),E=async t=>await L(t,{recursive:!0,force:!0}),St=(t,s)=>{let e=t.replace(/\./g,"\\.").replace(/\*\*/g,"<!DOUBLE_STAR!>").replace(/\*/g,"[^/]*").replace(/<!DOUBLE_STAR!>/g,".*");return e=`^${e}$`,new RegExp(e).test(s)},G=async(t,s=t)=>{let e=[],r=await $t(t,{withFileTypes:!0});for(let o of r){let a=u(t,o.name),n=a.substring(s.length+1);n.startsWith(".git")||(o.isDirectory()?e.push(...await G(a,s)):e.push(n))}return e},jt=async(t,s)=>{let e=await G(s),r=new Set;for(let o of t)if(o.includes("*"))for(let n of e)St(o,n)&&r.add(n);else try{let n=u(s,o);await Tt(n),r.add(o)}catch{}return Array.from(r).sort()},Rt=async(t,s)=>{let e=process.cwd(),r=u(s,N,t),o=u(e,t);await C(bt(o),{recursive:!0}),await xt(r,o)},Dt=async(t,s,e)=>{let r={success:[],failed:[]};for(let o of t)try{e.message(`Updating ${o}...`),await Rt(o,s),r.success.push(o)}catch(a){let n=a instanceof Error?a.message:`${a}`;r.failed.push({file:o,error:n})}return r},Ot=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=u(t,x);try{vt("Let's update your Geistdocs project!"),f.info(`Repository: ${P}`);let e=Pt();e.start("Creating temporary directory..."),await Ft(x),e.message("Cloning repository..."),await Et(x),e.message("Scanning for files...");let r=u(s,N),o=await jt(A,r);if(e.stop(`Found ${o.length} files to update`),o.length===0){f.warn("No files matched the patterns. Nothing to update."),await E(s),U("Update cancelled.");return}f.info(`Patterns: ${A.length}`),f.info(`Files to update: ${o.length}`),e.start("Updating files...");let a=await Dt(o,s,e);e.message("Cleaning up..."),await E(s),e.stop("Update complete!"),Ot(a),U("Please review and test the changes carefully.")}catch(e){let r=e instanceof Error?e.message:`${e}`;try{await E(s)}catch{}f.error(`Failed to update project: ${r}`),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(j);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(O);T.parse(process.argv);
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vercel/geistdocs",
|
|
3
|
+
"description": "CLI for Geistdocs projects, including initialization and updating.",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"geistdocs": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsup"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@clack/prompts": "^0.11.0",
|
|
17
|
+
"commander": "^14.0.2",
|
|
18
|
+
"glob": "^11.0.0",
|
|
19
|
+
"ts-morph": "^27.0.2"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@biomejs/biome": "^2.3.8",
|
|
23
|
+
"@types/glob": "^9.0.0",
|
|
24
|
+
"@types/node": "^24.10.1",
|
|
25
|
+
"tsup": "^8.5.1",
|
|
26
|
+
"typescript": "^5.9.3"
|
|
27
|
+
}
|
|
28
|
+
}
|