zerostarter 0.0.13 → 0.0.14-canary.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.
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import{i as e,n as t,r as n,t as r}from"../convert-qa6VNLLu.mjs";import{basename as i,join as a,resolve as o}from"node:path";import{existsSync as s,readdirSync as c}from"node:fs";import{createInterface as l}from"node:readline/promises";import{parseArgs as u}from"node:util";import{execFileSync as d}from"node:child_process";import{randomBytes as f}from"node:crypto";var p={name:`zerostarter`,version:`0.0.13`,description:`Go from zero to a production-ready SaaS, rebranded and ready to ship.`,keywords:[`cli`,`saas`,`scaffold`,`starter`,`template`,`zerostarter`],homepage:`https://github.com/nrjdalal/zerostarter#readme`,bugs:`https://github.com/nrjdalal/zerostarter/issues`,license:`MIT`,author:{name:`Neeraj Dalal`,email:`admin@nrjdalal.com`,url:`https://nrjdalal.com`},repository:`nrjdalal/zerostarter`,funding:`https://github.com/sponsors/nrjdalal`,bin:{zerostarter:`dist/bin/index.mjs`},files:[`dist`],type:`module`,types:`./dist/index.d.mts`,exports:`./dist/index.mjs`,scripts:{build:`tsdown`,"check-types":`tsc --noEmit`,dev:`tsdown --watch`,test:`bun test`},devDependencies:{"@packages/config":`workspace:*`,"@types/bun":`catalog:`,"@types/node":`catalog:`,tsdown:`catalog:`,typescript:`catalog:`}};const m=()=>!!(process.stdin.isTTY&&process.stdout.isTTY),h=(e,t=e)=>process.stdout.isTTY?`\x1b]8;;${e}\x07${t}\x1b]8;;\x07`:e,g=(e,t=process.stdout)=>n=>t.isTTY?`\x1b[${e}m${n}\x1b[0m`:n,_=g(`38;5;208`),v=g(`38;2;63;185;80`),y=g(`38;2;210;153;34`),b=g(`38;2;248;81;73`,process.stderr),x=async(e,t=``)=>{let n=l({input:process.stdin,output:process.stdout});try{return(await n.question(`${e}${t?` (${t})`:``}: `)).trim()||t}finally{n.close()}},S=async(e,t=!0)=>{let n=l({input:process.stdin,output:process.stdout});try{let r=(await n.question(`${e} (${t?`Y/n`:`y/N`}) `)).trim().toLowerCase();return r?r===`y`||r===`yes`:t}finally{n.close()}},C=(e,t,n)=>d(e,t,{cwd:n,encoding:`utf8`,maxBuffer:1<<26,stdio:[`ignore`,`pipe`,`pipe`]}),w=()=>{try{return d(`docker`,[`info`],{stdio:`ignore`}),!0}catch{return!1}},T=r=>{let i=a(r,`.env`);if(!t(i)){let o=a(r,`.env.example`);e(i,t(o)?n(o):``)}return i},E=(e,t)=>{let r=`${t}=`,i=n(e).split(`
3
- `).find(e=>e.startsWith(r));return i?i.slice(r.length).trim():``},D=(t,r,i)=>{let a=`${r}=`,o=n(t).split(`
4
- `),s=o.findIndex(e=>e.startsWith(a));if(s>=0)o[s]=`${a}${i}`;else{for(;o.length&&o[o.length-1]===``;)o.pop();o.push(`${a}${i}`,``)}e(t,o.join(`
5
- `))},O=e=>{let t=T(e);E(t,`BETTER_AUTH_SECRET`)||D(t,`BETTER_AUTH_SECRET`,f(32).toString(`base64`))},k=e=>{let n=a(e,`.env`);return t(n)&&E(n,`POSTGRES_URL`)!==``},A=e=>{let t=C(`bunx`,[`pglaunch@5.5.7`,`-k`],e).match(/postgres(?:ql)?:\/\/[\w.:@\-/%?=&]+/);if(!t)throw Error(`pglaunch did not print a connection URL`);return t[0]},j=e=>{D(T(e),`POSTGRES_URL`,A(e)),C(`bun`,[`run`,`db:migrate`],e)},M=(e,t,n)=>d(e,t,{cwd:n,encoding:`utf8`,stdio:[`ignore`,`pipe`,`pipe`]}),N=(e,t=`main`)=>{M(`bunx`,[`gitpick@5.4.1`,`https://github.com/nrjdalal/zerostarter/tree/${t}`,e])},P=e=>{M(`bun`,[`install`],e)},F=e=>{M(`git`,[`init`,`-q`,`-b`,`canary`],e)},I=(e,t)=>{M(`git`,[`branch`,t],e)},L=(e,t)=>{M(`git`,[`add`,`-A`],e);try{M(`git`,[`commit`,`--no-verify`,`-q`,`-m`,t],e)}catch{}},R=e=>!s(e)||c(e).filter(e=>e!==`.git`).length===0,z=e=>t(a(e,`packages/config/src/site.ts`)),B=async e=>{let{positionals:n,values:s}=u({allowPositionals:!0,args:e,options:{db:{type:`boolean`},"dry-run":{type:`boolean`},help:{short:`h`,type:`boolean`},yes:{short:`y`,type:`boolean`}}});if(s.help){console.log(`Usage:
2
+ import{a as e,c as t,d as n,f as r,i,l as a,n as o,o as s,r as c,s as l,t as u,u as d}from"../convert-hP2szrh8.mjs";import{basename as f,join as p,resolve as m}from"node:path";import{existsSync as h,readdirSync as g}from"node:fs";import{createInterface as _}from"node:readline/promises";import{parseArgs as v}from"node:util";import{execFileSync as y}from"node:child_process";import{randomBytes as b}from"node:crypto";var ee={name:`zerostarter`,version:`0.0.14-canary.0`,description:`Go from zero to a production-ready SaaS, rebranded and ready to ship.`,keywords:[`cli`,`saas`,`scaffold`,`starter`,`template`,`zerostarter`],homepage:`https://github.com/nrjdalal/zerostarter#readme`,bugs:`https://github.com/nrjdalal/zerostarter/issues`,license:`MIT`,author:{name:`Neeraj Dalal`,email:`admin@nrjdalal.com`,url:`https://nrjdalal.com`},repository:`nrjdalal/zerostarter`,funding:`https://github.com/sponsors/nrjdalal`,bin:{zerostarter:`dist/bin/index.mjs`},files:[`dist`],type:`module`,types:`./dist/index.d.mts`,exports:`./dist/index.mjs`,scripts:{build:`tsdown`,"check-types":`tsc --noEmit`,dev:`tsdown --watch`,test:`bun test`},devDependencies:{"@packages/config":`workspace:*`,"@types/bun":`catalog:`,"@types/node":`catalog:`,tsdown:`catalog:`,typescript:`catalog:`}};const x=()=>!!(process.stdin.isTTY&&process.stdout.isTTY),te=(e,t=e)=>process.stdout.isTTY?`\x1b]8;;${e}\x07${t}\x1b]8;;\x07`:e,S=(e,t=process.stdout)=>n=>t.isTTY?`\x1b[${e}m${n}\x1b[0m`:n,C=S(`38;5;208`),w=S(`38;2;63;185;80`),T=S(`38;2;210;153;34`),E=S(`38;2;248;81;73`,process.stderr),D=async(e,t=``)=>{let n=_({input:process.stdin,output:process.stdout});try{return(await n.question(`${e}${t?` (${t})`:``}: `)).trim()||t}finally{n.close()}},O=async(e,t=!0)=>{let n=_({input:process.stdin,output:process.stdout});try{let r=(await n.question(`${e} (${t?`Y/n`:`y/N`}) `)).trim().toLowerCase();return r?r===`y`||r===`yes`:t}finally{n.close()}},k=(e,t,n)=>y(e,t,{cwd:n,encoding:`utf8`,maxBuffer:1<<26,stdio:[`ignore`,`pipe`,`pipe`]}),A=()=>{try{return y(`docker`,[`info`],{stdio:`ignore`}),!0}catch{return!1}},j=e=>{let r=p(e,`.env`);if(!s(r)){let i=p(e,`.env.example`);n(r,s(i)?t(i):``)}return r},M=(e,n)=>{let r=`${n}=`,i=t(e).split(`
3
+ `).find(e=>e.startsWith(r));return i?i.slice(r.length).trim():``},N=(e,r,i)=>{let a=`${r}=`,o=t(e).split(`
4
+ `),s=o.findIndex(e=>e.startsWith(a));if(s>=0)o[s]=`${a}${i}`;else{for(;o.length&&o[o.length-1]===``;)o.pop();o.push(`${a}${i}`,``)}n(e,o.join(`
5
+ `))},P=e=>{let t=j(e);M(t,`BETTER_AUTH_SECRET`)||N(t,`BETTER_AUTH_SECRET`,b(32).toString(`base64`))},F=e=>{let t=p(e,`.env`);return s(t)&&M(t,`POSTGRES_URL`)!==``},I=e=>{let t=k(`bunx`,[`pglaunch@5.5.7`,`-k`],e).match(/postgres(?:ql)?:\/\/[\w.:@\-/%?=&]+/);if(!t)throw Error(`pglaunch did not print a connection URL`);return t[0]},L=e=>{let t=j(e);N(t,`POSTGRES_URL`,I(e)),k(`bun`,[`run`,`db:migrate`],e)},R=(e,t,n)=>y(e,t,{cwd:n,encoding:`utf8`,stdio:[`ignore`,`pipe`,`pipe`]}),z=(e,t=`main`)=>{R(`bunx`,[`gitpick@6.0.0`,`https://github.com/nrjdalal/zerostarter/tree/${t}`,e])},B=(e,t=`main`)=>{R(`bunx`,[`gitpick@6.0.0`,`https://github.com/nrjdalal/zerostarter/tree/${t}`,e,`-o`])},ne=async(e=`main`)=>{let t=`https://raw.githubusercontent.com/nrjdalal/zerostarter/${e}/.gitpickignore`,n=await fetch(t);if(!n.ok)throw Error(`Could not fetch .gitpickignore from ${e} (HTTP ${n.status}).`);return n.text()},re=e=>R(`git`,[`status`,`--porcelain`],e).trim()===``,ie=(e,t)=>{for(let n of t)try{R(`git`,[`checkout`,`--`,n],e)}catch{}},V=e=>{R(`git`,[`reset`,`--hard`,`HEAD`],e),R(`git`,[`clean`,`-fd`],e)},H=(e,t,n)=>{if(!s(p(e,`.git`)))throw Error(t);if(!re(e))throw Error(n)},U=async(e,t,n)=>{try{await n()}catch(n){throw V(e),console.log(t),n}},W=e=>{R(`bun`,[`install`],e)},G=e=>{R(`git`,[`init`,`-q`,`-b`,`canary`],e)},K=(e,t)=>{R(`git`,[`branch`,t],e)},q=(e,t)=>{R(`git`,[`add`,`-A`],e);try{R(`git`,[`commit`,`--no-verify`,`-q`,`-m`,t],e)}catch{}},J=e=>!h(e)||g(e).filter(e=>e!==`.git`).length===0,Y=e=>s(p(e,`packages/config/src/site.ts`)),ae=async e=>{let{positionals:t,values:n}=v({allowPositionals:!0,args:e,options:{db:{type:`boolean`},"dry-run":{type:`boolean`},help:{short:`h`,type:`boolean`},yes:{short:`y`,type:`boolean`}}});if(n.help){console.log(`Usage:
6
6
  $ bunx zerostarter init [dir] [options]
7
7
 
8
8
  Scaffold ZeroStarter into dir (default .) as a fresh product. The author's
@@ -15,21 +15,41 @@ Options:
15
15
  -y, --yes Skip prompts, taking defaults (provisions Postgres when Docker is running)
16
16
  --db Provision a local Postgres (pglaunch) and migrate; needs Docker
17
17
  --dry-run Print the plan without writing anything
18
- -h, --help Display help`);return}let c=m()&&!s.yes,l=n[0]??`.`,d=o(l),f=z(d);if(!f&&!R(d)){if(!c)throw Error(`Directory is not empty. Run it in an empty directory, or pass a project name: bunx zerostarter init <name>`);let e=await x(`Directory isn't empty. Project name`);if(!e)throw Error(`No directory name provided.`);l=e}let p=o(l),h=i(p),g={name:h};if(s[`dry-run`]){console.log(`bunx zerostarter init (dry run)`),console.log(` target: ${p}`),console.log(` name: ${h}`),console.log(` mode: ${z(p)?`in place`:`fetch first`}`);return}if(f&&c&&!await S(y(`Convert ${p} in place? This rewrites files and commits.`),!1)){console.log(`Aborted.`);return}console.log(),z(p)||(console.log(`Fetching the latest ZeroStarter ...`),N(p)),t(a(p,`.git`))||(F(p),L(p,`ci(init): scaffold from zerostarter`),I(p,`main`)),console.log(`Removing starter content and rebranding ...`),r(p,g),console.log(`Installing dependencies ...`),P(p),L(p,`ci(init): re-baseline as ${h}`),O(p);let b=!1,C=w(),T=k(p),E=!1;if(T?s.db&&console.log(y(` --db ignored: POSTGRES_URL is already set in .env.`)):E=s.db?!0:c?await S(`Provision a local Postgres database now?`,C):C,E&&C)try{console.log(`Provisioning a local database with pglaunch and migrating ...`),j(p),b=!0}catch(e){let t=e.stderr,n=t?String(t).trim():e instanceof Error?e.message:String(e);console.log(y(` Database setup failed; set POSTGRES_URL in .env yourself.`)),n&&console.log(y(` ${n}`))}else E&&console.log(y(` Docker isn't running, so the database wasn't provisioned. Set POSTGRES_URL in .env, or start Docker and re-run for automatic setup.`));let D=[[`packages/config/src/site.ts`,`your brand: name, tagline, links`],[`web/next/content`,`your docs and blog`],[`web/next/public`,`your logo and assets`]];console.log(`\n${v(`✓`)} ${h} is ready.\n`),console.log(`Next steps:`),p!==process.cwd()&&console.log(` ${_(`cd ${l}`)}`),b||(console.log(` ${_(`set POSTGRES_URL in .env`)} # your Postgres connection string`),console.log(` ${_(`bun run db:migrate`)}`)),console.log(` ${_(`bun run dev`)}`),console.log(`
19
- Push to an empty GitHub repo when ready:`),console.log(` ${_(`git push origin canary`)}`),console.log(`canary becomes the default branch; your next push seeds main and opens the release PR.`),console.log(`
20
- Make it yours:`);for(let[e,t]of D)console.log(` ${e.padEnd(29)} ${t}`);console.log(b?`
18
+ -h, --help Display help`);return}let r=x()&&!n.yes,i=t[0]??`.`,a=m(i),o=Y(a);if(!o&&!J(a)){if(!r)throw Error(`Directory is not empty. Run it in an empty directory, or pass a project name: bunx zerostarter init <name>`);let e=await D(`Directory isn't empty. Project name`);if(!e)throw Error(`No directory name provided.`);i=e}let c=m(i),l=f(c),d={name:l};if(n[`dry-run`]){console.log(`bunx zerostarter init (dry run)`),console.log(` target: ${c}`),console.log(` name: ${l}`),console.log(` mode: ${Y(c)?`in place`:`fetch first`}`);return}if(o&&r&&!await O(T(`Convert ${c} in place? This rewrites files and commits.`),!1)){console.log(`Aborted.`);return}console.log(),Y(c)||(console.log(`Fetching the latest ZeroStarter ...`),z(c)),s(p(c,`.git`))||(G(c),q(c,`ci(init): scaffold from zerostarter`),K(c,`main`)),console.log(`Removing starter content and rebranding ...`),u(c,d),console.log(`Installing dependencies ...`),W(c),q(c,`ci(init): re-baseline as ${l}`),P(c);let h=!1,g=A(),_=F(c),y=!1;if(_?n.db&&console.log(T(` --db ignored: POSTGRES_URL is already set in .env.`)):y=n.db?!0:r?await O(`Provision a local Postgres database now?`,g):g,y&&g)try{console.log(`Provisioning a local database with pglaunch and migrating ...`),L(c),h=!0}catch(e){let t=e.stderr,n=t?String(t).trim():e instanceof Error?e.message:String(e);console.log(T(` Database setup failed; set POSTGRES_URL in .env yourself.`)),n&&console.log(T(` ${n}`))}else y&&console.log(T(` Docker isn't running, so the database wasn't provisioned. Set POSTGRES_URL in .env, or start Docker and re-run for automatic setup.`));let b=[[`packages/config/src/site.ts`,`your brand: name, tagline, links`],[`web/next/content`,`your docs and blog`],[`web/next/public`,`your logo and assets`]];console.log(`\n${w(`✓`)} ${l} is ready.\n`),console.log(`Next steps:`),c!==process.cwd()&&console.log(` ${C(`cd ${i}`)}`),h||(console.log(` ${C(`set POSTGRES_URL in .env`)} # your Postgres connection string`),console.log(` ${C(`bun run db:migrate`)}`)),console.log(` ${C(`bun run dev`)}`),console.log(`
19
+ Push to an empty GitHub repo when ready:`),console.log(` ${C(`git push origin canary`)}`),console.log(`canary becomes the default branch; your next push seeds main and opens the release PR.`),console.log(`
20
+ Make it yours:`);for(let[e,t]of b)console.log(` ${e.padEnd(29)} ${t}`);console.log(h?`
21
21
  Everything works out of the box. Try it now; add OAuth or other credentials to .env whenever you like.`:`
22
- It needs a Postgres database to run: a hosted one like Neon works, or a local Docker one. OAuth and other credentials are optional.`)},V=async e=>{console.log(`zerostarter sync is not implemented yet.`),console.log(`Planned: a gitpick overlay of the latest zerostarter onto an existing fork, then a`),console.log(`prune of starter-only artifacts while preserving the fork's product and branding.`)},{author:H,name:U,version:W}=p,G=`Version:
23
- ${U}@${W}
22
+ It needs a Postgres database to run: a hosted one like Neon works, or a local Docker one. OAuth and other credentials are optional.`)},oe=async t=>{let{positionals:n,values:r}=v({allowPositionals:!0,args:t,options:{help:{short:`h`,type:`boolean`},yes:{short:`y`,type:`boolean`}}});if(r.help){console.log(`Usage:
23
+ $ bunx zerostarter reinit [dir] [options]
24
+
25
+ Re-scaffold an existing git repo (default .) as a fresh ZeroStarter: delete every
26
+ file (keeping .git and your .env* files, so history, remote, and secrets survive), fetch the
27
+ latest ZeroStarter, and rebrand to the dir name. The commit lands on the current
28
+ branch; push when ready.
29
+
30
+ Options:
31
+ -y, --yes Skip the confirmation prompt
32
+ -h, --help Display help`);return}let i=m(n[0]??`.`),a=f(i),o=x()&&!r.yes;if(H(i,`No git repository in ${i}. reinit re-scaffolds an existing repo; use init for a new project.`,`Working tree has uncommitted changes. Commit or stash them first; reinit deletes every tracked file.`),o&&!await O(T(`Delete every file in ${i} (keeping .git and .env*) and re-scaffold as ${a}?`),!1)){console.log(`Aborted.`);return}console.log(),console.log(`Removing all files (keeping .git and your .env* files) ...`),await U(i,T(`reinit failed; restored your committed files (deleted gitignored files, except .env*, are gone).`),()=>{e(i),console.log(`Fetching the latest ZeroStarter ...`),B(i),console.log(`Rebranding ...`),u(i,{name:a}),console.log(`Installing dependencies ...`),W(i),P(i),q(i,`ci(reinit): re-baseline as ${a}`)}),console.log(`\n${w(`✓`)} ${a} re-scaffolded; .git history, remote, and .env* files are intact.`),console.log(`Next steps:`),F(i)||(console.log(` ${C(`set POSTGRES_URL in .env`)} # your Postgres connection string`),console.log(` ${C(`bun run db:migrate`)}`)),console.log(` ${C(`bun run dev`)}`),console.log(` ${C(`git push`)} # to your existing remote`)},se=async e=>{let{positionals:t,values:n}=v({allowPositionals:!0,args:e,options:{help:{short:`h`,type:`boolean`}}});if(n.help){console.log(`Usage:
33
+ $ bunx zerostarter sync [dir] [options]
34
+
35
+ Re-baseline an existing fork (default .) on the latest ZeroStarter: a gitpick overlay
36
+ updates the starter files while your content, public/marketing, branding, package.json
37
+ identity, and favicon are preserved. Requires a clean tree; lands as a reviewable diff
38
+ you commit yourself.
39
+
40
+ Options:
41
+ -h, --help Display help`);return}let u=m(t[0]??`.`);H(u,`No git repository in ${u}. Run sync inside an existing fork.`,`Working tree has uncommitted changes. Commit or stash them first so the sync lands as a reviewable diff.`);let f=p(u,`package.json`),h=new Map(l(u).map(e=>[e,a(e)])),g=i(await ne());g.length===0&&console.log(T(`Warning: no PRESERVE_ON_SYNC directive found; fork-owned files may be overwritten.`)),console.log(),console.log(`Overlaying the latest ZeroStarter (content, public/marketing, and site.ts preserved) ...`),await U(u,T(`Sync failed; rolled the working tree back to your last commit.`),()=>{B(u),o(u),d(p(u,`.gitpickignore`));for(let[e,t]of h)s(e)&&r(e,c(t,a(e),e===f));ie(u,g)}),console.log(`Installing dependencies ...`),W(u),console.log(),console.log(C(`Synced to the latest ZeroStarter.`)),console.log(`Starter files were updated (edits to them were overwritten); files you added and your`),console.log(`content, public/marketing, and branding were preserved.`),console.log(T(`Review the diff and commit: git -C ${u} status`))},{author:X,name:Z,version:Q}=ee,$=`Version:
42
+ ${Z}@${Q}
24
43
 
25
44
  Go from zero to a production-ready SaaS, rebranded and ready to ship.
26
45
 
27
46
  Usage:
28
- $ ${U} <command> [options]
47
+ $ ${Z} <command> [options]
29
48
 
30
49
  Commands:
31
50
  init [dir] Scaffold ZeroStarter into dir (default .) as a fresh product.
32
51
  The dir name becomes the project name.
52
+ reinit [dir] Re-scaffold an existing git repo as a fresh ZeroStarter (keeps .git and .env)
33
53
  sync Re-baseline an existing fork on ZeroStarter's latest scaffold
34
54
 
35
55
  Options:
@@ -37,4 +57,4 @@ Options:
37
57
  -h, --help Display help
38
58
 
39
59
  Author:
40
- ${H.name} <${H.email}> (${h(H.url)})`;(async()=>{try{let e=process.argv.slice(2),t=e[0],n=e.slice(1);switch(t){case`init`:return await B(n);case`sync`:return await V(n);case void 0:case`-h`:case`--help`:console.log(G);return;case`-v`:case`--version`:console.log(`${U}@${W}`);return;default:console.error(b(`Unknown command: ${t}\n`)),console.log(G),process.exit(1)}}catch(e){console.error(b(e instanceof Error?e.message:String(e))),process.exit(1)}})();export{};
60
+ ${X.name} <${X.email}> (${te(X.url)})`;(async()=>{try{let e=process.argv.slice(2),t=e[0],n=e.slice(1);switch(t){case`init`:return await ae(n);case`reinit`:return await oe(n);case`sync`:return await se(n);case void 0:case`-h`:case`--help`:console.log($);return;case`-v`:case`--version`:console.log(`${Z}@${Q}`);return;default:console.error(E(`Unknown command: ${t}\n`)),console.log($),process.exit(1)}}catch(e){console.error(E(e instanceof Error?e.message:String(e))),process.exit(1)}})();export{};
@@ -0,0 +1,104 @@
1
+ import{dirname as e,join as t}from"node:path";import{existsSync as n,mkdirSync as r,readFileSync as i,readdirSync as a,rmSync as o,writeFileSync as s}from"node:fs";const c=e=>n(e),l=e=>i(e,`utf8`),u=(t,n)=>{r(e(t),{recursive:!0}),s(t,n)},d=e=>{o(e,{force:!0,recursive:!0})},f=new Set([`node_modules`,`.next`,`.turbo`,`dist`]),p=e=>{for(let n of a(e,{withFileTypes:!0})){if(n.name===`.git`)continue;let r=t(e,n.name);n.isDirectory()?f.has(n.name)?o(r,{force:!0,recursive:!0}):(p(r),a(r).length===0&&o(r,{force:!0,recursive:!0})):n.name.startsWith(`.env`)||o(r,{force:!0})}},m=e=>{let n=[];for(let r of a(e,{withFileTypes:!0})){if(r.name===`.git`||f.has(r.name))continue;let i=t(e,r.name);r.isDirectory()?n.push(...m(i)):r.name===`package.json`&&n.push(i)}return n},h=e=>JSON.parse(l(e)),g=(e,t)=>{u(e,`${JSON.stringify(t,null,2)}\n`)},_=(e,t)=>{if(!n(e))return!1;let r=l(e),i=r.replace(t,``);return i===r?!1:(s(e,i),!0)},v=[`homepage`,`bugs`,`license`,`author`,`repository`,`funding`],y=[`name`,`version`,...v],b=e=>{let t=e.split(`
2
+ `).find(e=>e.trim().startsWith(`# PRESERVE_ON_SYNC`));return t?t.trim().slice(18).replace(/^\s*-\s*/,``).split(`,`).map(e=>e.trim()).filter(Boolean):[]},x=(e,t)=>{let n={...e,...t};return Object.keys(n).length>0?n:void 0},S=(e,t)=>{let n=Array.isArray(e)?e:[],r=Array.isArray(t)?t:[];return n.length||r.length?[...new Set([...r,...n])]:void 0},C=[`dependencies`,`devDependencies`,`optionalDependencies`,`peerDependencies`,`resolutions`,`catalog`,`scripts`,`overrides`],w=(e,t,n)=>{let r={...t};for(let n of Object.keys(e))n in t||(r[n]=e[n]);for(let n of C){let i=x(e[n],t[n]);i&&(r[n]=i)}let i=S(e.workspaces,t.workspaces);if(i&&(r.workspaces=i),n)for(let t of y)t in e?r[t]=e[t]:delete r[t];return r},T=({name:e})=>{let t=e.charAt(0).toUpperCase()+e.slice(1);return`// Brand identity for this app: the single source a fork edits to rebrand. web reads it via lib/config.ts.
3
+ export const site = {
4
+ name: "${t}",
5
+ description: "${t} is just getting started. Tell its story here.",
6
+ tagline: "Your tagline, ready when you are.",
7
+ social: {
8
+ github: "",
9
+ x: "",
10
+ discord: "",
11
+ },
12
+ // Local-only dev agent identity (api/hono agents router).
13
+ agent: {
14
+ name: "LocalAgent",
15
+ email: "agent@local.host",
16
+ },
17
+ // Injectable long-form text blocks. A product sets its own, or leaves them empty.
18
+ apiReferenceDescription: "",
19
+ llmsFullPreamble: "",
20
+ } as const
21
+
22
+ export type Site = typeof site
23
+ `},E=()=>`import { redirect } from "next/navigation"
24
+
25
+ // Fresh fork: redirect to the waitlist until you build your real home page.
26
+ export default function Home() {
27
+ redirect("/waitlist")
28
+ }
29
+ `,D=()=>'# AGENTS.md\n\nGuidance for AI coding agents working in this repository.\n\n## Instructions\n\n- ALWAYS: Use `@/` for imports, and follow the `design` skill for UI and styling conventions.\n- ALWAYS: Keep documentation in sync with every change.\n- NEVER: Include "Co-authored-by" in commit messages.\n- NEVER: Use em-dashes (the long dash, U+2014) in code, comments, docs, or copy. Regular hyphens are fine; for a pause or aside, use a comma, colon, or period.\n\n## Skills\n\nCustom skills live in `.agents/skills` (symlinked to `.claude/skills`). Start with the `codebase-map` skill to orient, then load the task skill that fits (`api-endpoint`, `db-migration`, `dev`, `design`, and more).\n',O=({name:e})=>`# ${e.charAt(0).toUpperCase()+e.slice(1)}
30
+
31
+ Built on top of [ZeroStarter](https://zerostarter.dev).
32
+
33
+ ## Development
34
+
35
+ \`\`\`bash
36
+ bun run dev
37
+ \`\`\`
38
+
39
+ Web runs on http://localhost:3000 and the API on http://localhost:4000.
40
+ `,k=()=>`---
41
+ slug: /docs
42
+ title: Introduction
43
+ description: Documentation.
44
+ ---
45
+
46
+ # Introduction
47
+
48
+ This is your documentation home. Start writing, this page is yours.
49
+ `,A=()=>`---
50
+ slug: /console/docs
51
+ title: Introduction
52
+ description: Internal documentation.
53
+ ---
54
+
55
+ # Introduction
56
+
57
+ Your team's internal docs live here.
58
+ `,j=e=>`---
59
+ title: Blog
60
+ description: Latest articles and updates
61
+ createdAt: ${e}
62
+ ---
63
+
64
+ ## Recent Posts
65
+
66
+ <BlogPostList />
67
+ `,M=e=>`---
68
+ title: Hello World
69
+ description: The first post on your new blog.
70
+ createdAt: ${e}
71
+ publishedAt: ${e}
72
+ ---
73
+
74
+ ## Hello World
75
+
76
+ Your very first post. Replace it with something worth reading.
77
+ `,N=()=>`import type { DocsConfig } from "./src/lib/docs/types"
78
+
79
+ const docsConfig = {
80
+ docs: {
81
+ "Getting Started": [
82
+ {
83
+ "/docs": {
84
+ title: "Introduction",
85
+ description: "Documentation.",
86
+ },
87
+ },
88
+ ],
89
+ },
90
+ console: {
91
+ "Getting Started": [
92
+ {
93
+ "/console/docs": {
94
+ title: "Introduction",
95
+ description: "Internal documentation.",
96
+ },
97
+ },
98
+ ],
99
+ },
100
+ } satisfies DocsConfig
101
+
102
+ export default docsConfig
103
+ `,P=(e,...n)=>t(e,...n),F=e=>e.toLowerCase().replace(/[^a-z0-9]+/g,`-`).replace(/^-+|-+$/g,``)||`app`,I=e=>{let t=P(e,`.gitpickignore`);if(c(t)){for(let n of l(t).split(`
104
+ `)){let t=n.trim();if(!(!t||t.startsWith(`#`))){if(/[*?![\]]/.test(t))throw Error(`.gitpickignore entry "${t}" is not a literal path; the in-place converter only supports literal paths (a glob or negation would diverge from gitpick's fetch).`);d(P(e,t))}}d(t)}},L=/\nexport const caveat = localFont\(\{[\s\S]*?\n\}\)\n/,R=/\nexport const newsreader = localFont\(\{[\s\S]*?\n\}\)\n/,z=/[ \t]*\{[^}\n]*href:[ \t]*["']\/hire["'][^}\n]*\},?[ \t]*\n/,B=(e,t)=>{let n=new Date,r=n.toISOString().slice(0,10),i=[n.getFullYear(),n.getMonth()+1,n.getDate()].map(e=>String(e).padStart(2,`0`)).join(`-`),a=i<r?i:r;u(P(e,`web/next/content/docs/index.mdx`),k()),u(P(e,`web/next/content/blog/index.mdx`),j(a)),u(P(e,`web/next/content/blog/hello-world.mdx`),M(a)),u(P(e,`web/next/content/console/docs/index.mdx`),A()),u(P(e,`web/next/docs.config.ts`),N()),u(P(e,`web/next/public/.gitkeep`),``),u(P(e,`web/next/src/app/page.tsx`),E()),u(P(e,`AGENTS.md`),D()),u(P(e,`README.md`),O(t))},V=e=>{let t=P(e,`web/next/src/lib/fonts.ts`),n=P(e,`web/next/src/components/navbar/home.tsx`);if(_(t,L),_(t,R),_(n,z),c(t)&&l(t).includes(`fonts/marketing/`))throw Error(`fonts.ts still references fonts/marketing/ after fixDangling (regex drift). Update packages/cli/src/convert.ts.`);if(c(n)&&/href:\s*["']\/hire["']/.test(l(n)))throw Error(`navbar/home.tsx still has the /hire entry after fixDangling (regex drift). Update packages/cli/src/convert.ts.`)},H=(e,t)=>{u(P(e,`packages/config/src/site.ts`),T(t));let n=P(e,`package.json`),r=h(n);r.name=F(t.name),r.version=`0.0.0`;for(let e of v)delete r[e];g(n,r)},U=(e,t)=>{I(e),B(e,t),V(e),H(e,t)};export{p as a,l as c,u as d,g as f,b as i,h as l,V as n,c as o,w as r,m as s,U as t,d as u};
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{t as e}from"./convert-qa6VNLLu.mjs";export{e as convertRepo};
1
+ import{t as e}from"./convert-hP2szrh8.mjs";export{e as convertRepo};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zerostarter",
3
- "version": "0.0.13",
3
+ "version": "0.0.14-canary.0",
4
4
  "description": "Go from zero to a production-ready SaaS, rebranded and ready to ship.",
5
5
  "keywords": [
6
6
  "cli",
@@ -1,107 +0,0 @@
1
- import{dirname as e,join as t}from"node:path";import{existsSync as n,mkdirSync as r,readFileSync as i,rmSync as a,writeFileSync as o}from"node:fs";const s=e=>n(e),c=e=>i(e,`utf8`),l=(t,n)=>{r(e(t),{recursive:!0}),o(t,n)},u=e=>{a(e,{force:!0,recursive:!0})},d=e=>JSON.parse(c(e)),f=(e,t)=>{l(e,`${JSON.stringify(t,null,2)}\n`)},p=(e,t)=>{if(!n(e))return!1;let r=c(e),i=r;for(let[e,n]of t)i=i.split(e).join(n);return i===r?!1:(o(e,i),!0)},m=({name:e})=>{let t=e.charAt(0).toUpperCase()+e.slice(1);return`// Brand identity for this app: the single source a fork edits to rebrand. web reads it via lib/config.ts.
2
- export const site = {
3
- name: "${t}",
4
- description: "${t} is just getting started. Tell its story here.",
5
- tagline: "Your tagline, ready when you are.",
6
- social: {
7
- github: "",
8
- x: "",
9
- discord: "",
10
- },
11
- // Local-only dev agent identity (api/hono agents router).
12
- agent: {
13
- name: "LocalAgent",
14
- email: "agent@local.host",
15
- },
16
- // Injectable long-form text blocks. A product sets its own, or leaves them empty.
17
- apiReferenceDescription: "",
18
- llmsFullPreamble: "",
19
- } as const
20
-
21
- export type Site = typeof site
22
- `},h=()=>`import { redirect } from "next/navigation"
23
-
24
- // Fresh fork: redirect to the waitlist until you build your real home page.
25
- export default function Home() {
26
- redirect("/waitlist")
27
- }
28
- `,g=()=>'# AGENTS.md\n\nGuidance for AI coding agents working in this repository.\n\n## Instructions\n\n- ALWAYS: Use `@/` for imports, and follow the `design` skill for UI and styling conventions.\n- ALWAYS: Keep documentation in sync with every change.\n- NEVER: Include "Co-authored-by" in commit messages.\n- NEVER: Use em-dashes (the long dash, U+2014) in code, comments, docs, or copy. Regular hyphens are fine; for a pause or aside, use a comma, colon, or period.\n\n## Skills\n\nCustom skills live in `.agents/skills` (symlinked to `.claude/skills`). Start with the `codebase-map` skill to orient, then load the task skill that fits (`api-endpoint`, `db-migration`, `dev`, `design`, and more).\n',_=()=>`---
29
- slug: /docs
30
- title: Introduction
31
- description: Documentation.
32
- ---
33
-
34
- # Introduction
35
-
36
- This is your documentation home. Start writing, this page is yours.
37
- `,v=()=>`---
38
- slug: /console/docs
39
- title: Introduction
40
- description: Internal documentation.
41
- ---
42
-
43
- # Introduction
44
-
45
- Your team's internal docs live here.
46
- `,y=e=>`---
47
- title: Blog
48
- description: Latest articles and updates
49
- createdAt: ${e}
50
- ---
51
-
52
- ## Recent Posts
53
-
54
- <BlogPostList />
55
- `,b=e=>`---
56
- title: Hello World
57
- description: The first post on your new blog.
58
- createdAt: ${e}
59
- publishedAt: ${e}
60
- ---
61
-
62
- ## Hello World
63
-
64
- Your very first post. Replace it with something worth reading.
65
- `,x=()=>`import type { DocsConfig } from "./src/lib/docs/types"
66
-
67
- const docsConfig = {
68
- docs: {
69
- "Getting Started": [
70
- {
71
- "/docs": {
72
- title: "Introduction",
73
- description: "Documentation.",
74
- },
75
- },
76
- ],
77
- },
78
- console: {
79
- "Getting Started": [
80
- {
81
- "/console/docs": {
82
- title: "Introduction",
83
- description: "Internal documentation.",
84
- },
85
- },
86
- ],
87
- },
88
- } satisfies DocsConfig
89
-
90
- export default docsConfig
91
- `,S=(e,...n)=>t(e,...n),C=e=>e.toLowerCase().replace(/[^a-z0-9]+/g,`-`).replace(/^-+|-+$/g,``)||`app`,w=[`web/next/content`,`web/next/public`],T=[`packages/cli`,`.github/workflows/cli-release.yml`,`.github/audit`,`.infisical.json`,`.coderabbit.yaml`,`.github/assets/graph-build.svg`,`.github/assets/cli.tape`,`.github/assets/setup.sh`,`.github/assets/cli.gif`,`.github/FUNDING.yml`,`LICENSE.md`,`CHANGELOG.md`,`bun.lock`,`web/next/src/app/hire`,`web/next/src/app/resume`,`web/next/src/fonts/caveat-latin-wght-normal.woff2`,`web/next/src/fonts/newsreader-latin-wght-normal.woff2`,`web/next/src/fonts/newsreader-latin-wght-italic.woff2`],E=e=>{let t=new Date,n=t.toISOString().slice(0,10),r=[t.getFullYear(),t.getMonth()+1,t.getDate()].map(e=>String(e).padStart(2,`0`)).join(`-`),i=r<n?r:n;l(S(e,`web/next/content/docs/index.mdx`),_()),l(S(e,`web/next/content/blog/index.mdx`),y(i)),l(S(e,`web/next/content/blog/hello-world.mdx`),b(i)),l(S(e,`web/next/content/console/docs/index.mdx`),v()),l(S(e,`web/next/docs.config.ts`),x()),l(S(e,`web/next/public/.gitkeep`),``),l(S(e,`web/next/src/app/page.tsx`),h()),l(S(e,`AGENTS.md`),g())},D=e=>{let t=p(S(e,`web/next/src/components/navbar/home.tsx`),[[` { href: "/hire", label: "Hire" },
92
- `,``]]),n=p(S(e,`web/next/src/lib/fonts.ts`),[[`
93
- export const caveat = localFont({
94
- src: "../fonts/caveat-latin-wght-normal.woff2",
95
- variable: "--font-caveat",
96
- weight: "400 700",
97
- })
98
- `,``]]),r=p(S(e,`web/next/src/lib/fonts.ts`),[[`
99
- export const newsreader = localFont({
100
- src: [
101
- { path: "../fonts/newsreader-latin-wght-normal.woff2", style: "normal" },
102
- { path: "../fonts/newsreader-latin-wght-italic.woff2", style: "italic" },
103
- ],
104
- variable: "--font-newsreader",
105
- weight: "200 800",
106
- })
107
- `,``]]);if(!n||!r)throw Error(`fonts.ts: caveat/newsreader exports not found, but their woff2 files were removed (template drift). Update packages/cli/src/convert.ts.`);if(!t)throw Error(`navbar/home.tsx: /hire entry not found (template drift). Update packages/cli/src/convert.ts.`)},O=(e,t)=>{l(S(e,`packages/config/src/site.ts`),m(t));let n=S(e,`package.json`),r=d(n);r.name=C(t.name),r.version=`0.0.0`,delete r.homepage,delete r.bugs,delete r.license,delete r.author,delete r.repository,delete r.funding,f(n,r)},k=(e,t)=>{for(let t of[...w,...T])u(S(e,t));E(e),D(e),O(e,t)};export{l as i,s as n,c as r,k as t};