create-sailor 0.1.0 → 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.js +66 -19
- package/package.json +7 -2
package/dist/index.js
CHANGED
|
@@ -1,16 +1,52 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
3
|
-
`)}
|
|
4
|
-
DATABASE_URL="${String(c.databaseUrl)}"
|
|
2
|
+
import we from"fs";import ee from"path";import*as b from"@clack/prompts";import{Command as ke}from"commander";import w from"picocolors";import Pe from"update-notifier";import le from"cfonts";import ue from"picocolors";var B="1.0.0";function U(){if(process.env.NO_COLOR||!process.stdout.isTTY){process.stdout.write([`Sailor v${B}`,"AI-Native SaaS Unicorn Template","by Nebutra \xB7 https://nebutra.com","",""].join(`
|
|
3
|
+
`));return}le.say("Sailor",{font:"block",align:"left",colors:["#0033FE","#0BF1C3"],gradient:["#0033FE","#0BF1C3"],transitionGradient:!0,space:!1}),process.stdout.write(ue.dim(` AI-Native SaaS Template \xB7 v${B} \xB7 nebutra.com
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
`))}import x from"picocolors";function me(){return!process.env.NO_COLOR&&!!process.stdout.isTTY}function W(t){let e=me(),i=e?"\u2693":"-",s=e?"\u25B8":"->",r=e?"\u2605":"*",y=e?"\u{1F4E7}":"*",u=["",` ${e?x.bold(`${i} Done in ${t.elapsedSec}s \xB7 ${t.targetDir}/`):`${i} Done in ${t.elapsedSec}s \xB7 ${t.targetDir}/`}`,"",` ${e?x.bold("Next:"):"Next:"}`,` ${s} cd ${t.targetDir}`];t.skippedInstall&&u.push(` ${s} pnpm install (if --no-install was set)`),u.push(` ${s} pnpm dev ${e?x.dim("\u2192 http://localhost:3000"):"-> http://localhost:3000"}`,"",` ${e?x.bold("Customize:"):"Customize:"}`,` ${s} pnpm sailor brand init ${e?x.dim("\u2192 your colors, logo, domain"):"-> your colors, logo, domain"}`,` ${s} pnpm sailor preset apply ${e?x.dim("\u2192 toggle features"):"-> toggle features"}`,` ${s} pnpm sailor add <feat> ${e?x.dim("\u2192 add queue/search/cache/..."):"-> add queue/search/cache/..."}`,"",` ${e?x.bold("Ship faster:"):"Ship faster:"}`,` ${r} Star us https://github.com/nebutra/nebutra-sailor`,` ${y} Free license https://nebutra.com/get-license`,""),process.stdout.write(u.join(`
|
|
6
|
+
`)+`
|
|
7
|
+
`)}var de=`Usage: create-sailor [name] [options]
|
|
8
|
+
|
|
9
|
+
Arguments:
|
|
10
|
+
name project directory (default: ./my-saas-app)
|
|
11
|
+
|
|
12
|
+
Options:
|
|
13
|
+
-t, --template <id> saas | full | ecommerce | web3 | ai-saas
|
|
14
|
+
-p, --pm <id> npm | pnpm | yarn | bun (auto-detected)
|
|
15
|
+
--orm <id> prisma | drizzle | none
|
|
16
|
+
--db <id> postgres | mysql | sqlite | none
|
|
17
|
+
--auth <id> clerk | betterauth | none
|
|
18
|
+
--payment <id> stripe | lemon | wechat | alipay | none
|
|
19
|
+
--ai <ids> comma-separated provider ids (e.g. openai,anthropic)
|
|
20
|
+
--deploy <target> vercel | railway | cloudflare | selfhost
|
|
21
|
+
--i18n enable i18n (default: true)
|
|
22
|
+
--no-i18n disable
|
|
23
|
+
--no-install skip package install
|
|
24
|
+
--no-git skip git init
|
|
25
|
+
-y, --yes accept all defaults (non-interactive)
|
|
26
|
+
--dry-run preview actions without writing files
|
|
27
|
+
--json machine-readable output
|
|
28
|
+
--no-color disable color output
|
|
29
|
+
-h, --help show this help
|
|
30
|
+
-v, --version show version
|
|
31
|
+
|
|
32
|
+
Examples:
|
|
33
|
+
$ npm create sailor@latest
|
|
34
|
+
$ npm create sailor@latest my-app -y
|
|
35
|
+
$ npm create sailor@latest my-app --template=ai-saas --auth=clerk
|
|
36
|
+
$ npm create sailor@latest --dry-run
|
|
37
|
+
`;function _(){process.stdout.write(de)}import R from"picocolors";function J(){return!process.env.NO_COLOR&&!!process.stdout.isTTY}function ye(t){let e=J()?"\u25B8":"->",i=`[${t.index}/${t.total}]`,s=t.label.padEnd(18," ");return J()?`${R.dim(i)} ${R.bold(s)} ${R.dim(e)} ${R.cyan(t.value)}`:`${i} ${s} ${e} ${t.value}`}function M(t){process.stdout.write(ye(t)+`
|
|
38
|
+
`)}import fe from"fs";import ge from"path";async function V(t,e){let i=ge.join(t,"nebutra.config.json");fe.writeFileSync(i,JSON.stringify(e,null,2)+`
|
|
39
|
+
`)}import O from"fs";import Y from"path";async function G(t,e){let i=`# Automatically injected by create-sailor
|
|
40
|
+
DATABASE_URL="${String(e.databaseUrl)}"
|
|
41
|
+
|
|
42
|
+
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="${String(e.clerkPublishable)}"
|
|
43
|
+
CLERK_SECRET_KEY="${String(e.clerkSecret)}"
|
|
8
44
|
|
|
9
45
|
NEXT_PUBLIC_SITE_URL="http://localhost:3000"
|
|
10
|
-
`,
|
|
11
|
-
`+
|
|
12
|
-
`)}}import
|
|
13
|
-
`)}let
|
|
46
|
+
`,s=Y.join(t,".env"),r=Y.join(t,".env.local");!O.existsSync(s)&&!O.existsSync(r)?O.writeFileSync(r,i):O.appendFileSync(r,`
|
|
47
|
+
`+i)}import{execSync as he}from"child_process";import v from"fs";import N from"path";import be from"ignore";var xe="https://github.com/Nebutra/Nebutra-Sailor/archive/refs/heads/main.tar.gz";function K(t,e,i){let s=v.readdirSync(e,{withFileTypes:!0});for(let r of s){if(r.name==="node_modules"||r.name===".git")continue;let y=N.join(e,r.name),f=N.relative(t,y).split(N.sep).join("/");r.isDirectory()?(i.push(f+"/"),K(t,y,i)):i.push(f)}}function ve(t){let e=N.join(t,".templateignore");if(!v.existsSync(e))return;let i=v.readFileSync(e,"utf8"),s=be().add(i),r=[];K(t,t,r);let y=r.map(a=>a.endsWith("/")?a.slice(0,-1):a),f=new Set(s.filter(y)),u=y.filter(a=>!f.has(a)).sort((a,h)=>h.split("/").length-a.split("/").length);for(let a of u){let h=N.join(t,a);try{v.existsSync(h)&&v.rmSync(h,{recursive:!0,force:!0})}catch{}}try{v.existsSync(e)&&v.rmSync(e,{force:!0})}catch{}}async function H(t){try{v.mkdirSync(t,{recursive:!0}),he(`set -o pipefail; curl -sSL ${xe} | tar -xz -C "${t}" --strip-components=1`,{stdio:"ignore",shell:"bash"})}catch{throw new Error("Failed to download the template repository. Please ensure you have internet access and curl/tar installed.")}try{ve(t)}catch(e){throw new Error(`Template cloned, but .templateignore processing failed: ${e instanceof Error?e.message:String(e)}`)}}import I from"fs";import Se from"path";async function Q(t,e){let i=Se.join(t,"package.json");if(I.existsSync(i)){let s=I.readFileSync(i,"utf8"),r=JSON.parse(s);r.name=e,r.version="0.1.0",I.writeFileSync(i,JSON.stringify(r,null,2)+`
|
|
48
|
+
`)}}import o from"fs";import l from"path";function X(t,e){if(!o.existsSync(t))return;let i=o.readdirSync(t);for(let s of i){let r=l.join(t,s);o.statSync(r).isDirectory()?X(r,e):e(r)}}async function Z(t,e){let i=l.join(t,"package.json");if(!o.existsSync(i))return;let s=JSON.parse(o.readFileSync(i,"utf8"));if(e.orm!=="prisma"){let r=l.join(t,"packages","db");if(o.existsSync(r)){let y=l.join(r,"prisma");o.existsSync(y)&&o.rmSync(y,{recursive:!0,force:!0});let f=l.join(r,"package.json");if(o.existsSync(f)){let a=JSON.parse(o.readFileSync(f,"utf8"));a.dependencies&&delete a.dependencies["@prisma/client"],a.devDependencies&&delete a.devDependencies.prisma,o.writeFileSync(f,JSON.stringify(a,null,2)+`
|
|
49
|
+
`)}let u=l.join(r,"src","index.ts");o.existsSync(u)&&o.writeFileSync(u,`
|
|
14
50
|
export const prisma = {} as any;
|
|
15
51
|
export const db = {} as any;
|
|
16
52
|
export type PrismaClient = any;
|
|
@@ -56,7 +92,7 @@ export type UsageAggregate = any;
|
|
|
56
92
|
export type UsageLedgerEntry = any;
|
|
57
93
|
export type UsageType = any;
|
|
58
94
|
export type UsageLedgerSource = any;
|
|
59
|
-
`)}
|
|
95
|
+
`)}s.scripts?.postinstall&&(s.scripts.postinstall=s.scripts.postinstall.replace(/prisma generate/g,'echo "ORM Disabled"'))}if(!e.i18n){let r=l.join(t,"apps");if(o.existsSync(r)){let y=o.readdirSync(r);for(let f of y){let u=l.join(r,f);if(!o.statSync(u).isDirectory())continue;let a=l.join(u,"src","app");if(!o.existsSync(a))continue;["[lang]","[locale]"].forEach(c=>{let n=l.join(a,c);if(o.existsSync(n)){let d=o.readdirSync(n);for(let S of d)o.renameSync(l.join(n,S),l.join(a,S));o.rmdirSync(n)}}),["src/middleware.ts","middleware.ts"].forEach(c=>{let n=l.join(u,c);o.existsSync(n)&&o.rmSync(n)});let h=l.join(u,"src","lib");o.existsSync(h)||o.mkdirSync(h,{recursive:!0});let q=l.join(h,"mock-i18n.ts");o.writeFileSync(q,`
|
|
60
96
|
import React from 'react';
|
|
61
97
|
export function useTranslations(namespace?: string): any { return (key: string) => key; }
|
|
62
98
|
export function useLocale() { return 'en'; }
|
|
@@ -74,7 +110,7 @@ export const useRouter = () => ({ push: (...args: any[]) => {}, replace: (...arg
|
|
|
74
110
|
export default function createMiddleware(...args: any[]) { return (req: any) => req; }
|
|
75
111
|
export function createNavigation(...args: any[]) { return { Link, redirect, usePathname, useRouter, getPathname: () => '/' }; }
|
|
76
112
|
export function defineRouting<T>(config: T): T { return config; }
|
|
77
|
-
`);let
|
|
113
|
+
`);let T=l.join(h,"mock-i18n.d.ts");o.writeFileSync(T,`declare module '@/lib/mock-i18n' {
|
|
78
114
|
export function useTranslations(namespace?: string): any;
|
|
79
115
|
export function useLocale(): string;
|
|
80
116
|
export const NextIntlClientProvider: React.FC<any>;
|
|
@@ -92,14 +128,25 @@ export function defineRouting<T>(config: T): T { return config; }
|
|
|
92
128
|
export function createNavigation(...args: any[]): any;
|
|
93
129
|
export function defineRouting<T>(config: T): T;
|
|
94
130
|
}
|
|
95
|
-
`),
|
|
131
|
+
`),o.writeFileSync(l.join(h,"mock-i18n-index.ts"),`
|
|
96
132
|
export * from './mock-i18n';
|
|
97
133
|
export { default } from './mock-i18n';
|
|
98
|
-
`);let
|
|
134
|
+
`);let k=l.join(u,"tsconfig.json");if(o.existsSync(k))try{let c=o.readFileSync(k,"utf8");if(c.includes('"paths": {')){let n=c.replace(/"paths":\s*\{/,`"paths": {
|
|
99
135
|
"next-intl": ["./src/lib/mock-i18n"],
|
|
100
|
-
"next-intl/*": ["./src/lib/mock-i18n"],`);
|
|
101
|
-
`)}}}}if(
|
|
102
|
-
`)}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
136
|
+
"next-intl/*": ["./src/lib/mock-i18n"],`);o.writeFileSync(k,n)}}catch{}let C=l.join(u,"src");o.existsSync(C)&&X(C,c=>{if(!c.endsWith(".ts")&&!c.endsWith(".tsx"))return;let n=o.readFileSync(c,"utf8"),d=!1;c.endsWith("src/app/layout.tsx")&&n.includes("../providers")&&(n=n.replace(/from\s+['"]\.\.\/providers['"]/g,"from './providers'"),d=!0),n=n.replace(/params:\s*Promise<\{[^}]*(lang|locale)[^}]*\}>/g,"params: any"),n=n.replace(/\(\{\s*params\s*\}\s*:\s*\{\s*params:\s*any\s*\}\)/g,"({ params }: any)"),n.match(/locale\s*:\s*any/)&&(n=n.replace(/locale\s*:\s*any/g,"locale: string"),d=!0),n.match(/lang\s*:\s*any/)&&(n=n.replace(/lang\s*:\s*any/g,"lang: string"),d=!0),n.match(/requestLocale\s*:\s*any/)&&(n=n.replace(/requestLocale\s*:\s*any/g,"requestLocale: string"),d=!0),n.match(/\(locale\s*=\s*(.*)\)/)&&(n=n.replace(/\(locale\s*=\s*(.*)\)/g,"() "),d=!0),n.match(/export\s+default\s+async\s+function\s+LangLayout/)&&(n=n.replace(/export\s+default\s+async\s+function\s+LangLayout\s*\(\s*\{\s*children\s*,\s*params\s*\}\s*:\s*LangLayoutProps\s*\)/g,"export default async function LangLayout({ children }: { children: React.ReactNode })"),d=!0),(c.endsWith("src/app/layout.tsx")||c.endsWith("src/app/sitemap.ts")||c.endsWith("src/app/robots.ts")||c.endsWith("src/components/ui/locale-switcher.tsx")||c.endsWith("src/app/(marketing)/blog/[slug]/page.tsx"))&&(n=n.replace(/const\s+\{\s*lang\s*\}\s*=\s*(await\s+)?params;?/g,'const lang = "en";'),n=n.replace(/const\s+\{\s*locale\s*\}\s*=\s*(await\s+)?params;?/g,'const locale = "en";'),n=n.replace(/const\s+locale\s*=\s*lang\s+as\s+Locale;?/g,'const locale = "en";'),n=n.replace(/\(l\)\s*=>/g,"(l: any) =>"),d=!0),c.endsWith("src/i18n/request.ts")&&(n=n.replace(/async\s*\(\{\s*requestLocale\s*\}\)/g,"async ({ requestLocale }: any)"),d=!0),(n.match(/locale\s*:/)||n.match(/lang\s*:/))&&(d=!0),d&&o.writeFileSync(c,n)});let P=l.join(u,"package.json");if(o.existsSync(P)){let c=JSON.parse(o.readFileSync(P,"utf8"));c.dependencies&&delete c.dependencies["next-intl"],o.writeFileSync(P,JSON.stringify(c,null,2)+`
|
|
137
|
+
`)}}}}if(e.applicationType!=="full"){let r=[];e.applicationType==="saas"&&r.push("contracts");for(let y of r){let f=l.join(t,"packages",y);o.existsSync(f)&&o.rmSync(f,{recursive:!0,force:!0})}}o.writeFileSync(i,JSON.stringify(s,null,2)+`
|
|
138
|
+
`)}var A="1.0.0",te="create-sailor";function g(t,e){t&&process.stdout.write(JSON.stringify(e)+`
|
|
139
|
+
`)}function Ce(){let t=process.env.npm_config_user_agent??"";return t.startsWith("pnpm")?"pnpm":t.startsWith("yarn")?"yarn":t.startsWith("bun")?"bun":"npm"}function ne(t){switch(t){case"full":return"full";case"ecommerce":return"ecommerce";case"web3":return"web3";default:return"saas"}}function se(t){switch(t){case"postgres":case"postgresql":return"postgresql";case"mysql":return"mysql";case"sqlite":return"sqlite";case"none":return"none";default:return"postgresql"}}function oe(t){return t==="drizzle"?"drizzle":t==="none"?"none":"prisma"}function re(t){return t==="lemon"||t==="lemonsqueezy"?"lemonsqueezy":t==="none"?"none":"stripe"}function ae(t){if(!t)return"openai";let e=t.split(",").map(i=>i.trim().toLowerCase());return e.includes("anthropic")?"anthropic":e.includes("none")?"none":"openai"}async function Ne(){let t=new ke;t.name(te).description("Nebutra-Sailor \u2014 AI-Native SaaS template").version(A,"-v, --version").helpOption(!1).argument("[name]","project directory",void 0).option("-t, --template <id>","saas | full | ecommerce | web3 | ai-saas").option("-p, --pm <id>","npm | pnpm | yarn | bun").option("--orm <id>","prisma | drizzle | none").option("--db <id>","postgres | mysql | sqlite | none").option("--auth <id>","clerk | betterauth | none").option("--payment <id>","stripe | lemon | wechat | alipay | none").option("--ai <ids>","comma-separated provider ids").option("--deploy <target>","vercel | railway | cloudflare | selfhost").option("--i18n","enable i18n").option("--no-i18n","disable i18n").option("--no-install","skip package install").option("--no-git","skip git init").option("-y, --yes","accept all defaults (non-interactive)").option("--dry-run","preview actions without writing files").option("--json","machine-readable output").option("--no-color","disable color output").option("-h, --help","show help"),t.parse(process.argv);let e=t.opts(),[i]=t.args;e.help&&(_(),process.exit(0));let s=!!e.json,r=!!e.dryRun,y=!!e.yes,f=y||!process.stdin.isTTY;s||U(),g(s,{event:"start",version:A});let u=i??(y?"./my-saas-app":void 0),a;if(u)a=u;else if(f)a="./my-saas-app";else{let p=await b.group({name:()=>b.text({message:"Where should we create your project?",placeholder:"./my-saas-app",defaultValue:"./my-saas-app",validate:m=>{if(m.length===0)return"Please enter a path."}})},{onCancel:()=>{process.stdout.write(w.red(`\u2718 Cancelled
|
|
140
|
+
`)),process.exit(130)}});a=String(p.name)}let h=ee.basename(ee.resolve(a)),q=e.pm??Ce(),F=!!e.template,T=!!e.orm,k=!!e.db,C=!!e.payment,P=!!e.ai,c=e.i18n!==void 0,n,d,S,j,$,L;if(f)n=ne(e.template),d=oe(e.orm),S=se(e.db),j=re(e.payment),$=ae(e.ai),L=c?!!e.i18n:!0;else{let p={};F||(p.applicationType=()=>b.select({message:"What kind of application are you building?",options:[{value:"saas",label:"Standard B2B/B2C SaaS"},{value:"full",label:"Full Monorepo"},{value:"ecommerce",label:"E-Commerce Suite"},{value:"web3",label:"Web3 DApp"}],initialValue:"saas"})),T||(p.orm=()=>b.select({message:"Which ORM?",options:[{value:"prisma",label:"Prisma"},{value:"drizzle",label:"Drizzle"},{value:"none",label:"None"}],initialValue:"prisma"})),k||(p.database=()=>b.select({message:"Database?",options:[{value:"postgresql",label:"PostgreSQL"},{value:"mysql",label:"MySQL"},{value:"sqlite",label:"SQLite"},{value:"none",label:"None"}],initialValue:"postgresql"})),C||(p.payment=()=>b.select({message:"Payment?",options:[{value:"stripe",label:"Stripe"},{value:"lemonsqueezy",label:"Lemon Squeezy"},{value:"none",label:"None"}],initialValue:"stripe"})),P||(p.aiProvider=()=>b.select({message:"Default AI Provider?",options:[{value:"openai",label:"OpenAI"},{value:"anthropic",label:"Anthropic"},{value:"none",label:"None"}],initialValue:"openai"})),c||(p.i18n=()=>b.confirm({message:"Enable i18n?",initialValue:!0}));let m=Object.keys(p).length>0?await b.group(p,{onCancel:()=>{process.stdout.write(w.red(`\u2718 Cancelled
|
|
141
|
+
`)),process.exit(130)}}):{};n=F?ne(e.template):m.applicationType,d=T?oe(e.orm):m.orm,S=k?se(e.db):m.database,j=C?re(e.payment):m.payment,$=P?ae(e.ai):m.aiProvider,L=c?!!e.i18n:!!m.i18n}let z={applicationType:n,orm:d,database:S,payment:j,aiProvider:$,i18n:L},E=[["Project name",h],["Template",n],["ORM",d],["Database",S],["Payment",j],["AI",$]];s?E.forEach(([p,m],D)=>{g(!0,{event:"step",step:p,value:m,index:D+1,total:E.length})}):E.forEach(([p,m],D)=>{M({index:D+1,total:E.length,label:p,value:m})});let ie={databaseUrl:"postgresql://postgres:postgres@localhost:5432/nebutra",clerkPublishable:"",clerkSecret:""},ce=Date.now();if(r){let p=[`clone template \u2192 ${a}`,"write nebutra.config.json",`prune template (orm=${d}, i18n=${L}, app=${n})`,"inject .env.local",e.install===!1?"skip install":`run ${q} install`,e.git===!1?"skip git init":"run git init"];if(s)p.forEach(m=>g(!0,{event:"plan",action:m})),g(!0,{event:"done",dryRun:!0});else{process.stdout.write(`
|
|
142
|
+
`+w.bold(`Dry run \u2014 planned actions:
|
|
143
|
+
`));for(let m of p)process.stdout.write(` \u2022 ${m}
|
|
144
|
+
`);process.stdout.write(w.dim(`
|
|
145
|
+
No files were written.
|
|
146
|
+
`))}process.exit(0)}let pe=()=>{process.stdout.write(`
|
|
147
|
+
`+w.red(`\u2718 Cancelled
|
|
148
|
+
`));try{we.existsSync(a)&&process.stdout.write(w.dim(` Partial directory left at ${a} \u2014 remove manually if needed.
|
|
149
|
+
`))}catch{}process.exit(130)};process.on("SIGINT",pe);try{g(s,{event:"step",step:"clone",status:"start"}),await H(a),g(s,{event:"step",step:"clone",status:"ok"}),g(s,{event:"step",step:"package",status:"start"}),await Q(a,h),g(s,{event:"step",step:"package",status:"ok"}),g(s,{event:"step",step:"config",status:"start"}),await V(a,z),g(s,{event:"step",step:"config",status:"ok"}),g(s,{event:"step",step:"prune",status:"start"}),await Z(a,z),g(s,{event:"step",step:"prune",status:"ok"}),g(s,{event:"step",step:"env",status:"start"}),await G(a,ie),g(s,{event:"step",step:"env",status:"ok"});let p=Math.max(1,Math.round((Date.now()-ce)/1e3));s?g(!0,{event:"done",status:"ok",elapsedSec:p,targetDir:a}):W({elapsedSec:p,targetDir:a,skippedInstall:e.install===!1});try{Pe({pkg:{name:te,version:A},updateCheckInterval:1e3*60*60*24}).notify({defer:!1,isGlobal:!0})}catch{}process.exit(0)}catch(p){let m=p instanceof Error?p.message:String(p);s?g(!0,{event:"error",message:m}):process.stdout.write(w.red(`
|
|
150
|
+
\u2718 Failed: ${m}
|
|
151
|
+
`)),process.exit(1)}}Ne().catch(t=>{process.stderr.write(String(t)+`
|
|
152
|
+
`),process.exit(1)});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-sailor",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "AI-Native SaaS unicorn template. Bootstrap a production-ready Next.js + Hono + Prisma monorepo with Multi-Agent, white-labeling, billing, auth, and 50+ engineering primitives in seconds.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -20,11 +20,16 @@
|
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@clack/prompts": "^0.7.0",
|
|
23
|
+
"cfonts": "^3.3.0",
|
|
23
24
|
"commander": "^12.1.0",
|
|
24
|
-
"
|
|
25
|
+
"gradient-string": "^3.0.0",
|
|
26
|
+
"ignore": "^5.3.2",
|
|
27
|
+
"picocolors": "^1.1.0",
|
|
28
|
+
"update-notifier": "^7.3.1"
|
|
25
29
|
},
|
|
26
30
|
"devDependencies": {
|
|
27
31
|
"@types/node": "^20.0.0",
|
|
32
|
+
"@types/update-notifier": "^6.0.8",
|
|
28
33
|
"tsup": "^8.0.0",
|
|
29
34
|
"typescript": "^5.4.0"
|
|
30
35
|
},
|