create-tnt-stack 0.4.2 → 0.4.4-beta.99024cb

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/README.md CHANGED
@@ -19,6 +19,7 @@ Simply run `npm create tnt-stack@latest` to get started!
19
19
 
20
20
  [![PRs-Welcome][contribute-image]][contribute-url]
21
21
  [![NPM version][npm-image]][npm-url]
22
+ [![NPM beta version][npm-beta-image]][npm-beta-url]
22
23
  [![Downloads][downloads-image]][npm-url]<br />
23
24
  [![Release][release-image]][release-url]
24
25
  [![Prepelease][prerelease-image]][prerelease-url]
@@ -132,6 +133,9 @@ so you understand the branching strategy and local development environment.
132
133
  [npm-image]:
133
134
  https://img.shields.io/npm/v/create-tnt-stack?color=0b7285&logoColor=0b7285
134
135
  [npm-url]: https://www.npmjs.com/package/create-tnt-stack
136
+ [npm-beta-image]:
137
+ https://img.shields.io/npm/v/create-tnt-stack/beta?color=orange&logoColor=orange
138
+ [npm-beta-url]: https://www.npmjs.com/package/create-tnt-stack
135
139
  [license-image]:
136
140
  https://img.shields.io/github/license/SlickYeet/create-tnt-stack?color=red
137
141
  [license-url]: https://github.com/SlickYeet/create-tnt-stack/blob/main/LICENSE
package/dist/index.js CHANGED
@@ -1,21 +1,21 @@
1
1
  #!/usr/bin/env node
2
- import Ue from"path";import{execa as vt}from"execa";import $e from"fs-extra";import{confirm as G,input as ue,select as j}from"@inquirer/prompts";import{Command as tt}from"commander";import X from"path";import{fileURLToPath as Be}from"url";var We=Be(import.meta.url),Ve=X.dirname(We),p=X.join(Ve,"../"),Z=` ___ ___ ___ _ _____ ___ _____ _ _ _____ ___ _____ _ ___ _ __
2
+ import Fe from"path";import{execa as wt}from"execa";import Le from"fs-extra";import{confirm as J,input as he,select as S}from"@inquirer/prompts";import{Command as nt}from"commander";import X from"path";import{fileURLToPath as Ve}from"url";var ze=Ve(import.meta.url),Ke=X.dirname(ze),p=X.join(Ke,"../"),Z=` ___ ___ ___ _ _____ ___ _____ _ _ _____ ___ _____ _ ___ _ __
3
3
  / __| _ \\ __| /_\\_ _| __| |_ _| \\| |_ _| / __|_ _/_\\ / __| |/ /
4
4
  | (__| / _| / _ \\| | | _| | | | .\` | | | \\__ \\ | |/ _ \\ (__| ' <
5
5
  \\___|_|_\\___/_/ \\_\\_| |___| |_| |_|\\_| |_| |___/ |_/_/ \\_\\___|_|\\_\\
6
- `,I="my-tnt-stack",$="create-tnt-stack";import u from"path";import k from"fs-extra";import te from"path";import ne from"fs-extra";import ze from"sort-package-json";var ee={"next-auth":"^4.24.11","@auth/prisma-adapter":"^2.8.0",prisma:"^6.5.0","@prisma/client":"^6.5.0","@t3-oss/env-nextjs":"^0.12.0",zod:"^3.24.2",prettier:"^3.5.3","prettier-plugin-tailwindcss":"^0.6.11","@ianvs/prettier-plugin-sort-imports":"^4.4.1",eslint:"^9","eslint-config-next":"^15.2.4","@eslint/eslintrc":"^3.3.1",typescript:"^5.8.2","@types/node":"^22","@types/react":"^19","@types/react-dom":"^19",payload:"^3.33.0","@payloadcms/next":"^3.33.0","@payloadcms/payload-cloud":"^3.33.0","@payloadcms/richtext-lexical":"^3.33.0","@payloadcms/db-vercel-postgres":"^3.33.0","@payloadcms/db-sqlite":"^3.33.0",graphql:"^16.10.0",sharp:"^0.34.1"};var d=e=>{let{dependencies:o,devMode:s,projectDir:n}=e,t=ne.readJsonSync(te.join(n,"package.json"));o.forEach(i=>{let l=ee[i];s&&t.devDependencies?t.devDependencies[i]=l:t.dependencies&&(t.dependencies[i]=l)});let a=ze(t);ne.writeJsonSync(te.join(n,"package.json"),a,{spaces:2})};var ae=({projectDir:e,packages:o,databaseProvider:s})=>{let n=[],t=[];if(o?.payload.inUse)switch(n.push("payload"),n.push("@payloadcms/next"),n.push("@payloadcms/payload-cloud"),n.push("@payloadcms/richtext-lexical"),n.push("graphql"),s){case"sqlite":t.push("@payloadcms/db-sqlite");break;case"postgresql":t.push("@payloadcms/db-vercel-postgres");break}d({projectDir:e,dependencies:n,devMode:!1}),d({projectDir:e,dependencies:t,devMode:!1});let a=u.join(p,"template/packages"),i=u.join(a,"config/payload",`${s==="postgresql"?"with-postgres":"with-sqlite"}.ts`),l=u.join(e,"payload.config.ts");k.copyFileSync(i,l);let m=u.join(a,"src/payload/collections"),f=u.join(e,"src/collections");k.mkdirSync(f,{recursive:!0}),k.copyFileSync(u.join(m,"Media.ts"),u.join(f,"Media.ts")),k.copyFileSync(u.join(m,"Users.ts"),u.join(f,"Users.ts"));let g=u.join(a,"src/app/(payload)"),c=u.join(e,"src/app/(payload)");k.mkdirSync(c,{recursive:!0}),k.copySync(g,c);let h=u.join(e,"package.json"),x=k.readJSONSync(h);x.scripts={...x.scripts,payload:"payload","generate:importmap":"payload generate:importmap","generate:types":"payload generate:types"},k.writeJSONSync(h,x,{spaces:2})};import oe from"path";import Ke from"fs-extra";var se=({projectDir:e,packages:o})=>{let s=o?.typescript.inUse,n=o?.payload.inUse,t=[];s&&(t.push("typescript"),t.push("@types/node"),t.push("@types/react"),t.push("@types/react-dom")),d({projectDir:e,dependencies:t,devMode:!0});let a=oe.join(p,"template/packages/config/tsconfig",`${n?"with-payload":"base"}.json`),i=oe.join(e,"tsconfig.json");Ke.copyFileSync(a,i)};import E from"path";import q from"fs-extra";var ie=({projectDir:e,scopedAppName:o,packages:s,databaseProvider:n})=>{let t=s?.envVariables.inUse,a=s?.nextAuth.inUse,i=s?.prisma.inUse,l=s?.payload.inUse,m=[];t&&(m.push("@t3-oss/env-nextjs"),m.push("zod")),d({projectDir:e,dependencies:m,devMode:!1});let f=i||l,g=He(!!a,!!i,!!l,o,n),c="";if(f?(console.log("Using a database, copying env file"),a?c="with-next-auth-db.js":l?c="with-payload.js":c="with-db.js"):a&&(c="with-next-auth.js"),c!==""){let Je=E.join(p,"template/packages/src/env",c),Le=E.join(e,"src/env.js");q.copyFileSync(Je,Le)}let h=E.join(e,".env"),x=E.join(e,".env.example"),qe=Ye+g,Q=Buffer.from(crypto.getRandomValues(new Uint8Array(32))).toString("base64"),Fe=g.replace('AUTH_SECRET=""',`AUTH_SECRET="${Q}" # Generated by create-tnt-stack`).replace('PAYLOAD_SECRET=""',`PAYLOAD_SECRET="${Q}" # Generated by create-tnt-stack`);q.writeFileSync(h,Fe,"utf-8"),q.writeFileSync(x,qe,"utf-8")};function He(e,o,s,n,t){let a=`
6
+ `,C="my-tnt-app",G="create-tnt-stack";import y from"path";import k from"fs-extra";import te from"path";import ae from"fs-extra";import He from"sort-package-json";var ee={"next-auth":"^4.24.11","@auth/prisma-adapter":"^2.8.0",prisma:"^6.5.0","@prisma/client":"^6.5.0","@t3-oss/env-nextjs":"^0.12.0",zod:"^3.24.2",prettier:"^3.5.3","prettier-plugin-tailwindcss":"^0.6.11","@ianvs/prettier-plugin-sort-imports":"^4.4.1",eslint:"^9","eslint-config-next":"^15.2.4","@eslint/eslintrc":"^3.3.1",typescript:"^5.8.2","@types/node":"^22","@types/react":"^19","@types/react-dom":"^19",payload:"^3.33.0","@payloadcms/next":"^3.33.0","@payloadcms/payload-cloud":"^3.33.0","@payloadcms/richtext-lexical":"^3.33.0","@payloadcms/db-vercel-postgres":"^3.33.0","@payloadcms/db-sqlite":"^3.33.0",graphql:"^16.10.0",sharp:"^0.34.1"};var f=e=>{let{dependencies:n,devMode:s,projectDir:o}=e,t=ae.readJsonSync(te.join(o,"package.json"));n.forEach(i=>{let l=ee[i];s&&t.devDependencies?t.devDependencies[i]=l:t.dependencies&&(t.dependencies[i]=l)});let a=He(t);ae.writeJsonSync(te.join(o,"package.json"),a,{spaces:2})};var ne=({projectDir:e,packages:n,databaseProvider:s})=>{let o=[],t=[];if(n?.payload.inUse)switch(o.push("payload"),o.push("@payloadcms/next"),o.push("@payloadcms/payload-cloud"),o.push("@payloadcms/richtext-lexical"),o.push("graphql"),s){case"sqlite":t.push("@payloadcms/db-sqlite");break;case"postgresql":t.push("@payloadcms/db-vercel-postgres");break}f({projectDir:e,dependencies:o,devMode:!1}),f({projectDir:e,dependencies:t,devMode:!1});let a=y.join(p,"template/packages"),i=y.join(a,"config/payload",`${s==="postgresql"?"with-postgres":"with-sqlite"}.ts`),l=y.join(e,"payload.config.ts");k.copyFileSync(i,l);let d=y.join(a,"src/payload/collections"),g=y.join(e,"src/collections");k.mkdirSync(g,{recursive:!0}),k.copyFileSync(y.join(d,"Media.ts"),y.join(g,"Media.ts")),k.copyFileSync(y.join(d,"Users.ts"),y.join(g,"Users.ts"));let u=y.join(a,"src/app/(payload)"),m=y.join(e,"src/app/(payload)");k.mkdirSync(m,{recursive:!0}),k.copySync(u,m);let b=y.join(e,"package.json"),x=k.readJSONSync(b);x.scripts={...x.scripts,payload:"payload","generate:importmap":"payload generate:importmap","generate:types":"payload generate:types"},k.writeJSONSync(b,x,{spaces:2})};import oe from"path";import Ye from"fs-extra";var se=({projectDir:e,packages:n})=>{let s=n?.typescript.inUse,o=n?.payload.inUse,t=[];s&&(t.push("typescript"),t.push("@types/node"),t.push("@types/react"),t.push("@types/react-dom")),f({projectDir:e,dependencies:t,devMode:!0});let a=oe.join(p,"template/packages/config/tsconfig",`${o?"with-payload":"base"}.json`),i=oe.join(e,"tsconfig.json");Ye.copyFileSync(a,i)};import M from"path";import U from"fs-extra";var ie=({projectDir:e,scopedAppName:n,packages:s,databaseProvider:o})=>{let t=s?.envVariables.inUse,a=s?.nextAuth.inUse,i=s?.prisma.inUse,l=s?.payload.inUse,d=[];t&&(d.push("@t3-oss/env-nextjs"),d.push("zod")),f({projectDir:e,dependencies:d,devMode:!1});let g=i||l,u=Qe(!!a,!!i,!!l,n,o),m="";if(g?a?m="with-next-auth-db.js":l?m="with-payload.js":m="with-db.js":a&&(m="with-next-auth.js"),m!==""){let We=M.join(p,"template/packages/src/env",m),Be=M.join(e,"src/env.js");U.copyFileSync(We,Be)}let b=M.join(e,".env"),x=M.join(e,".env.example"),qe=Xe+u,Q=Buffer.from(crypto.getRandomValues(new Uint8Array(32))).toString("base64"),Je=u.replace('AUTH_SECRET=""',`AUTH_SECRET="${Q}" # Generated by create-tnt-stack`).replace('PAYLOAD_SECRET=""',`PAYLOAD_SECRET="${Q}" # Generated by create-tnt-stack`);U.writeFileSync(b,Je,"utf-8"),U.writeFileSync(x,qe,"utf-8")};function Qe(e,n,s,o,t){let a=`
7
7
  # When adding additional environment variables, the schema in "/src/env.js"
8
8
  # should be updated accordingly.
9
9
  `.trim().concat(`
10
- `);return o&&(a+=`
10
+ `);return n&&(a+=`
11
11
  # Prisma
12
12
  # https://www.prisma.io/docs/reference/database-reference/connection-urls#env
13
- `),o&&(t==="mysql"?a+=`DATABASE_URL="mysql://root:password@localhost:3306/${n}"`:t==="postgresql"?a+=`DATABASE_URL="postgresql://postgres:password@localhost:5432/${n}"`:t==="sqlite"&&(a+='DATABASE_URL="file:./db.sqlite"'),a+=`
13
+ `),n&&(t==="mysql"?a+=`DATABASE_URL="mysql://root:password@localhost:3306/${o}"`:t==="postgresql"?a+=`DATABASE_URL="postgresql://postgres:password@localhost:5432/${o}"`:t==="sqlite"&&(a+='DATABASE_URL="file:./db.sqlite"'),a+=`
14
14
  `),s&&(a+=`
15
15
  # Payload CMS
16
16
  # https://payloadcms.com/docs/database/overview
17
17
  `,a+=`PAYLOAD_SECRET=""
18
- `,t==="postgresql"?a+=`DATABASE_URL="postgresql://postgres:password@localhost:5432/${n}"`:t==="sqlite"&&(a+='DATABASE_URL="file:./db.sqlite"'),a+=`
18
+ `,t==="postgresql"?a+=`DATABASE_URL="postgresql://postgres:password@localhost:5432/${o}"`:t==="sqlite"&&(a+='DATABASE_URL="file:./db.sqlite"'),a+=`
19
19
  `),e&&(a+=`
20
20
  # Next Auth
21
21
  # You can generate a new secret on the command line with:
@@ -26,11 +26,11 @@ AUTH_SECRET=""
26
26
  # Next Auth Discord Provider
27
27
  DISCORD_CLIENT_ID=""
28
28
  DISCORD_CLIENT_SECRET=""
29
- `),!e&&!o&&!s&&(a+=`
29
+ `),!e&&!n&&!s&&(a+=`
30
30
  # Example:
31
31
  # SERVERVAR="foo"
32
32
  # NEXT_PUBLIC_CLIENTVAR="bar"
33
- `),a}var Ye=`
33
+ `),a}var Xe=`
34
34
  # Since the ".env" file is gitignored, you can use the ".env.example" file to
35
35
  # build a new ".env" file when you clone the repo. Keep this file up-to-date
36
36
  # when you add new variables to \`.env\`.
@@ -40,19 +40,19 @@ DISCORD_CLIENT_SECRET=""
40
40
  # ".env" and populate it with your secrets.
41
41
  `.trim().concat(`
42
42
 
43
- `);import F from"path";import J from"fs-extra";var re=({projectDir:e,packages:o})=>{let s=o?.eslint.inUse,n=[];s&&(n.push("eslint"),n.push("eslint-config-next"),n.push("@eslint/eslintrc")),d({projectDir:e,dependencies:n,devMode:!0});let t=F.join(p,"template/packages/config","eslint.config.mjs"),a=F.join(e,"eslint.config.mjs"),i=F.join(e,"package.json"),l=J.readJSONSync(i);l.scripts={...l.scripts,lint:"next lint"},J.copyFileSync(t,a),J.writeJSONSync(i,l,{spaces:2})};import P from"path";import L from"fs-extra";var le=({projectDir:e,packages:o})=>{let s=o?.prisma.inUse,n=["next-auth"];s&&n.push("@auth/prisma-adapter"),d({projectDir:e,dependencies:n,devMode:!1});let t=P.join(p,"template/packages"),a="src/app/api/auth/[...nextauth]/route.ts",i=P.join(t,a),l=P.join(e,a),m=P.join(t,"src/server/auth/config",s?"next-auth-with-prisma.ts":"next-auth.ts"),f=P.join(e,"src/server/auth/config.ts"),g=P.join(t,"src/server/auth/next-auth.ts"),c=P.join(e,"src/server/auth/index.ts");L.copySync(i,l),L.copySync(m,f),L.copySync(g,c)};import pe from"path";import Qe from"fs-extra";var ce=({projectDir:e,packages:o})=>{let s=o?.prettier.inUse,n=[];s&&(n.push("prettier"),n.push("prettier-plugin-tailwindcss"),n.push("@ianvs/prettier-plugin-sort-imports")),d({projectDir:e,dependencies:n,devMode:!0});let t=pe.join(p,"template/packages/config","prettier.config.mjs"),a=pe.join(e,"prettier.config.mjs");Qe.copyFileSync(t,a)};import w from"path";import _ from"fs-extra";var me=({projectDir:e,packages:o,databaseProvider:s})=>{d({projectDir:e,dependencies:["prisma"],devMode:!0}),d({projectDir:e,dependencies:["@prisma/client"],devMode:!1});let n=w.join(p,"template/packages"),t=w.join(n,"prisma/schema",`${o?.nextAuth.inUse?"with-next-auth":"base"}.prisma`),a=_.readFileSync(t,"utf-8");s!=="sqlite"&&(a=a.replace('provider = "sqlite"',`provider = "${{mysql:"mysql",postgresql:"postgresql"}[s]}"`),["mysql"].includes(s)&&(a=a.replace("// @db.Text","@db.Text")));let i=w.join(e,"prisma/schema.prisma");_.mkdirSync(w.dirname(i),{recursive:!0}),_.writeFileSync(i,a);let l=w.join(n,"src/server/db/db-prisma.ts"),m=w.join(e,"src/server/db/index.ts");_.mkdirSync(w.dirname(m),{recursive:!0}),_.writeFileSync(m,_.readFileSync(l,"utf-8"));let f=w.join(e,"package.json"),g=_.readJSONSync(f);g.scripts={...g.scripts,postinstall:"prisma generate","db:push":"prisma db push","db:studio":"prisma studio","db:generate":"prisma migrate dev","db:migrate":"prisma migrate deploy"},_.writeJSONSync(f,g,{spaces:2})};var M=["sqlite","mysql","postgresql"],de=e=>({nextAuth:{inUse:e.includes("nextAuth"),installer:le},prisma:{inUse:e.includes("prisma"),installer:me},envVariables:{inUse:!0,installer:ie},prettier:{inUse:e.includes("prettier"),installer:ce},eslint:{inUse:!0,installer:re},typescript:{inUse:!0,installer:se},payload:{inUse:e.includes("payload"),installer:ae}});import Xe from"path";import Ze from"fs-extra";function A(){let e=Xe.join(p,"package.json");return Ze.readJSONSync(e).version??"1.0.0"}var y=()=>{let e=process.env.npm_config_user_agent;return e?e.startsWith("yarn")?"yarn":e.startsWith("pnpm")?"pnpm":e.startsWith("bun")?"bun":"npm":"npm"};var C=class extends Error{constructor(o){super(o)}};import R from"chalk";var r={error(...e){console.log(R.red(...e))},warn(...e){console.log(R.yellow(...e))},info(...e){console.log(R.cyan(...e))},success(...e){console.log(R.green(...e))}};var N=e=>(e.length>1&&e.endsWith("/")&&(e=e.slice(0,-1)),e);var et=/^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;function fe(e){let o=N(e),s=o.split("/"),n=s.findIndex(a=>a.startsWith("@")),t=s[s.length-1];return s.findIndex(a=>a.startsWith("@"))!==-1&&(t=s.slice(n).join("/")),o=="."||et.test(t??"")?!0:"App name must consist of only lowercase alphanumeric characters, '-', and '_'"}var ge=e=>e.startsWith(".")||e.startsWith("/")?"Import alias can't start with '.' or '/'":!0;var b={appName:I,packages:[],flags:{noGit:!1,noInstall:!1,default:!1,CI:!1,nextAuth:!1,prisma:!1,prettier:!1,importAlias:"@/",dbProvider:"sqlite",backend:"nextjs"},databaseProvider:"sqlite"};async function ye(){let e=b,o=new tt().name($).description("CLI for scaffolding new web apps with the TNT-Powered stack").version(A(),"-v, --version","Output the current version of TNT").argument("[dir]","The name of the application, as well as the name of the directory to create").option("--noGit","Explicitly tell the CLI to not initialize a new git repo in the project",!1).option("--noInstall","Explicitly tell the CLI to not run the package manager's install command",!1).option("-y, --default","Bypass the CLI and use all default options to bootstrap a new tnt-stack",!1).option("--CI","Boolean value if we're running in CI",!1).option("--nextAuth [boolean]","Experimental: Boolean value if we should install NextAuth.js. Must be used in conjunction with `--CI`.",n=>!!n&&n!=="false").option("--prisma [boolean]","Experimental: Boolean value if we should install Prisma. Must be used in conjunction with `--CI`.",n=>!!n&&n!=="false").option("--prettier [boolean]","Experimental: Boolean value if we should install Prettier. Must be used in conjunction with `--CI`.",n=>!!n&&n!=="false").option("-i, --import-alias [alias]","Explicitly tell the CLI to use a custom import alias",b.flags.importAlias).option("--dbProvider [provider]",`Choose a database provider to use. Possible values: ${M.join(", ")}`,b.flags.dbProvider).option("--backend [framework]",`Choose a backend framework to use. Possible values: ${b.flags.backend}`,b.flags.backend).parse(process.argv);process.env.npm_config_user_agent?.startsWith("yarn/3")&&r.warn(` WARNING: It looks like you are using Yarn 3. This is currently not supported,
43
+ `);import F from"path";import L from"fs-extra";var re=({projectDir:e,packages:n})=>{let s=n?.eslint.inUse,o=[];s&&(o.push("eslint"),o.push("eslint-config-next"),o.push("@eslint/eslintrc")),f({projectDir:e,dependencies:o,devMode:!0});let t=F.join(p,"template/packages/config","eslint.config.mjs"),a=F.join(e,"eslint.config.mjs"),i=F.join(e,"package.json"),l=L.readJSONSync(i);l.scripts={...l.scripts,lint:"next lint"},L.copyFileSync(t,a),L.writeJSONSync(i,l,{spaces:2})};import _ from"path";import q from"fs-extra";var le=({projectDir:e,packages:n})=>{let s=n?.prisma.inUse,o=["next-auth"];s&&o.push("@auth/prisma-adapter"),f({projectDir:e,dependencies:o,devMode:!1});let t=_.join(p,"template/packages"),a="src/app/api/auth/[...nextauth]/route.ts",i=_.join(t,a),l=_.join(e,a),d=_.join(t,"src/server/auth/config",s?"next-auth-with-prisma.ts":"next-auth.ts"),g=_.join(e,"src/server/auth/config.ts"),u=_.join(t,"src/server/auth/next-auth.ts"),m=_.join(e,"src/server/auth/index.ts");q.copySync(i,l),q.copySync(d,g),q.copySync(u,m)};import pe from"path";import Ze from"fs-extra";var ce=({projectDir:e,packages:n})=>{let s=n?.prettier.inUse,o=[];s&&(o.push("prettier"),o.push("prettier-plugin-tailwindcss"),o.push("@ianvs/prettier-plugin-sort-imports")),f({projectDir:e,dependencies:o,devMode:!0});let t=pe.join(p,"template/packages/config","prettier.config.mjs"),a=pe.join(e,"prettier.config.mjs");Ze.copyFileSync(t,a)};import w from"path";import P from"fs-extra";var me=({projectDir:e,packages:n,databaseProvider:s})=>{f({projectDir:e,dependencies:["prisma"],devMode:!0}),f({projectDir:e,dependencies:["@prisma/client"],devMode:!1});let o=w.join(p,"template/packages"),t=w.join(o,"prisma/schema",`${n?.nextAuth.inUse?"with-next-auth":"base"}.prisma`),a=P.readFileSync(t,"utf-8");s!=="sqlite"&&(a=a.replace('provider = "sqlite"',`provider = "${{mysql:"mysql",postgresql:"postgresql"}[s]}"`),["mysql"].includes(s)&&(a=a.replace("// @db.Text","@db.Text")));let i=w.join(e,"prisma/schema.prisma");P.mkdirSync(w.dirname(i),{recursive:!0}),P.writeFileSync(i,a);let l=w.join(o,"src/server/db/db-prisma.ts"),d=w.join(e,"src/server/db/index.ts");P.mkdirSync(w.dirname(d),{recursive:!0}),P.writeFileSync(d,P.readFileSync(l,"utf-8"));let g=w.join(e,"package.json"),u=P.readJSONSync(g);u.scripts={...u.scripts,postinstall:"prisma generate","db:push":"prisma db push","db:studio":"prisma studio","db:generate":"prisma migrate dev","db:migrate":"prisma migrate deploy"},P.writeJSONSync(g,u,{spaces:2})};var de=["none","nextAuth"],fe=["none","prisma"],R=["sqlite","mysql","postgresql"];var ge=e=>({nextAuth:{inUse:e.includes("nextAuth"),installer:le},prisma:{inUse:e.includes("prisma"),installer:me},envVariables:{inUse:!0,installer:ie},prettier:{inUse:e.includes("prettier"),installer:ce},eslint:{inUse:e.includes("eslint"),installer:re},typescript:{inUse:!0,installer:se},payload:{inUse:e.includes("payload"),installer:ne}});import et from"path";import tt from"fs-extra";function I(){let e=et.join(p,"package.json");return tt.readJSONSync(e).version??"1.0.0"}var h=()=>{let e=process.env.npm_config_user_agent;return e?e.startsWith("yarn")?"yarn":e.startsWith("pnpm")?"pnpm":e.startsWith("bun")?"bun":"npm":"npm"};var O=class extends Error{constructor(n){super(n)}};import E from"chalk";var r={error(...e){console.log(E.red(...e))},warn(...e){console.log(E.yellow(...e))},info(...e){console.log(E.cyan(...e))},success(...e){console.log(E.green(...e))}};var N=e=>(e.length>1&&e.endsWith("/")&&(e=e.slice(0,-1)),e);var at=/^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;function ue(e){let n=N(e),s=n.split("/"),o=s.findIndex(a=>a.startsWith("@")),t=s[s.length-1];return s.findIndex(a=>a.startsWith("@"))!==-1&&(t=s.slice(o).join("/")),n=="."||at.test(t??"")?!0:"App name must consist of only lowercase alphanumeric characters, '-', and '_'"}var ye=e=>e.startsWith(".")||e.startsWith("/")?"Import alias can't start with '.' or '/'":!0;var c={appName:C,packages:[],flags:{noGit:!1,noInstall:!1,default:!1,CI:!1,authentication:"none",orm:"none",formatter:"prettier",linter:"eslint",importAlias:"@/",dbProvider:"sqlite",backend:"nextjs"},databaseProvider:"sqlite"};async function be(){let e=c,n=new nt().name(G).description("CLI for scaffolding new web apps with the TNT-Powered stack").version(I(),"-v, --version","Output the current version of Create TNT Stack").argument("[dir]","The name of the application, as well as the name of the directory to create").option("--noGit","Explicitly tell the CLI to not initialize a new git repo in the project",!1).option("--noInstall","Explicitly tell the CLI to not run the package manager's install command",!1).option("-y, --default","Bypass the CLI and use all default options to bootstrap a new tnt-stack",!1).option("--CI","Boolean value if we're running in CI",!1).option("--backend [framework]",`Choose a backend framework to use. Possible values: ${c.flags.backend}`,c.flags.backend).option("--authProvider [provider]",`Choose an authentication provider to use. Possible values: ${de.join(", ")}`,c.flags.authentication).option("--databaseORM [orm]",`Choose a database ORM to use. Possible values: ${fe.join(", ")}`,c.flags.orm).option("--formatter [formatter]",`Choose a formatter to use. Possible values: ${c.flags.formatter}`,c.flags.formatter).option("--linter [linter]",`Choose a linter to use. Possible values: ${c.flags.linter}`,c.flags.linter).option("-i, --import-alias [alias]","Explicitly tell the CLI to use a custom import alias",c.flags.importAlias).option("--dbProvider [provider]",`Choose a database provider to use. Possible values: ${R.join(", ")}`,c.flags.dbProvider).parse(process.argv);process.env.npm_config_user_agent?.startsWith("yarn/3")&&r.warn(` WARNING: It looks like you are using Yarn 3. This is currently not supported,
44
44
  and likely to result in a crash. Please run create-tnt-stack with another
45
45
  package manager such as pnpm, npm, or Yarn Classic.
46
- See: https://github.com/t3-oss/create-t3-app/issues/57`);let s=o.args[0];if(s&&(e.appName=s),e.flags=o.opts(),e.flags.CI)return e.packages=[],e.flags.nextAuth&&e.packages.push("nextAuth"),e.flags.prisma&&e.packages.push("prisma"),e.flags.prettier&&e.packages.push("prettier"),M.includes(e.flags.dbProvider)===!1&&(r.warn(`Incompatible database provided. Use: ${M.join(", ")}. Exiting.`),process.exit(0)),e.flags.backend==="payload"&&e.flags.dbProvider==="mysql"&&(r.warn("Payload CMS does not support MySQL. Exiting."),process.exit(0)),e;if(e.flags.default)return e;try{if(process.env.TERM_PROGRAM?.toLowerCase().includes("mintty"))throw r.warn(` WARNING: It looks like you are using MinTTY, which is non-interactive. This is most likely because you are
46
+ See: https://github.com/t3-oss/create-t3-app/issues/57`);let s=n.args[0];if(s&&(e.appName=s),e.flags=n.opts(),e.flags.CI){switch(e.packages=[],e.flags.backend){case"payload":e.packages.push("payload");break;default:break}switch(e.flags.authentication){case"nextAuth":e.packages.push("nextAuth");break;default:break}switch(e.flags.orm){case"prisma":e.packages.push("prisma");break;default:break}switch(e.flags.formatter){case"prettier":e.packages.push("prettier");break;default:break}switch(e.flags.linter){case"eslint":e.packages.push("eslint");break;default:break}return R.includes(e.flags.dbProvider)===!1&&(r.warn(`Incompatible database provided. Use: ${R.join(", ")}. Exiting.`),process.exit(0)),e.flags.backend==="payload"&&e.flags.dbProvider==="mysql"&&(r.warn("Payload CMS does not support MySQL. Exiting."),process.exit(0)),e}if(e.flags.default)return e;try{if(process.env.TERM_PROGRAM?.toLowerCase().includes("mintty"))throw r.warn(` WARNING: It looks like you are using MinTTY, which is non-interactive. This is most likely because you are
47
47
  using Git Bash. If that's that case, please use Git Bash from another terminal, such as Windows Terminal. Alternatively, you
48
- can provide the arguments from the CLI directly: https://create.tntstack.org/installation#experimental-usage to skip the prompts.`),new C("Non-interactive environment");let n=y(),t={};s||(t.name=await ue({message:"What will your project be called?",default:I,validate:i=>fe(i)})),t.backend=await j({message:"What backend framework would you like to use?",choices:[{value:"nextjs",name:"Next.js"},{value:"payload",name:"Payload CMS"}],default:"nextjs"}),t.backend==="payload"&&(t.databaseProvider=await j({message:"What database provider would you like to use?",choices:[{value:"sqlite",name:"SQLite"},{value:"postgresql",name:"PostgreSQL"}],default:"sqlite"})),t.backend==="nextjs"&&(t.authentication=await j({message:"What authentication provider would you like to use?",choices:[{value:"none",name:"None"},{value:"nextAuth",name:"NextAuth.js"}],default:"none"}),t.database=await j({message:"What database ORM would you like to use?",choices:[{value:"none",name:"None"},{value:"prisma",name:"Prisma"}],default:"none"}),t.database!=="none"&&(t.databaseProvider=await j({message:"What database provider would you like to use?",choices:[{value:"sqlite",name:"SQLite"},{value:"mysql",name:"MySQL"},{value:"postgresql",name:"PostgreSQL"}],default:"sqlite"}))),t.prettier=await G({message:"Should we install Prettier?",default:!b.flags.prettier}),e.flags.noGit||(t.noGit=await G({message:"Should we initialize a Git repository and stage the changes?",default:!b.flags.noGit})),e.flags.noInstall||(t.noInstall=await G({message:`Should we run '${n}`+(n==="yarn"?"'?":" install' for you?"),default:!b.flags.noInstall})),t.importAlias=await ue({message:"What import alias would you like to use?",default:b.flags.importAlias,validate:ge});let a=[];return t.authentication==="nextAuth"&&a.push("nextAuth"),t.database==="prisma"&&a.push("prisma"),t.prettier&&a.push("prettier"),t.backend==="payload"&&a.push("payload"),{appName:t.name??e.appName,packages:a,flags:{...e.flags,noGit:!t.noGit||e.flags.noGit,noInstall:!t.noInstall||e.flags.noInstall,importAlias:t.importAlias??e.flags.importAlias},databaseProvider:t.databaseProvider||"sqlite"}}catch(n){if(n instanceof C)r.warn(`${$} needs an interactive terminal to run.`),await G({message:"Continue scaffolding with default options?",default:!0})||(r.info("Exiting..."),process.exit(0)),r.info(`Scaffolding default tnt app in ./${e.appName}`);else throw n}return e}import it from"path";import he from"chalk";import nt from"ora";function be(e){let{packages:o}=e;r.info("Adding boilerplate...");for(let[s,n]of Object.entries(o))if(n.inUse){let t=nt(`Boilerplating ${s}...`).start();n.installer(e),t.succeed(he.green(`${he.green.bold(s)}`))}r.info("")}import B from"path";import{confirm as at,select as ot}from"@inquirer/prompts";import S from"chalk";import T from"fs-extra";import st from"ora";async function ve({projectName:e,projectDir:o,pkgManager:s,noInstall:n}){let t=B.join(p,"template/base");n?r.info(""):r.info(`
49
- Using: ${S.cyan.bold(s)}
50
- `);let a=st(`Scaffolding in: ${o}...
51
- `).start();if(T.existsSync(o))if(T.readdirSync(o).length===0)e!=="."&&a.info(`${S.cyan.bold(e)} exists but is empty, continuing...
52
- `);else{a.stopAndPersist();let l=await ot({message:`${S.redBright.bold("Warning:")} ${S.cyan.bold(e)} already exists and isn't empty. How would you like to proceed?`,choices:[{value:"abort",name:"Abort installation (recommended)"},{value:"clear",name:"Clear the directory and continue installation"},{value:"overwrite",name:"Continue installation and overwrite conflicting files"}],default:"abort"});l==="abort"&&(a.fail("Aborting installation..."),process.exit(1)),await at({message:`Are you sure you want to ${l==="clear"?"clear the directory":"overwrite conflicting files"}`,default:!1})||(a.fail("Aborting installation..."),process.exit(1)),l==="clear"&&(a.info(`Emptying ${S.cyan.bold(e)} and creating tnt app...
53
- `),T.emptyDirSync(o))}a.start(),T.copySync(t,o),T.renameSync(B.join(o,"_gitignore"),B.join(o,".gitignore"));let i=e==="."?"App":S.cyan.bold(e);a.succeed(`${i} ${S.green.bold("scaffolded successfully!")}
54
- `)}import v from"path";import W from"fs-extra";function ke({packages:e,projectDir:o}){let s=v.join(p,"template/packages/src/app/layout"),n=e.payload.inUse,t="base.tsx";n&&(t="with-payload.tsx");let a=v.join(s,t),i=v.join(o,`src/app/${n?"(frontend)":""}/layout.tsx`);W.copySync(a,i)}function we({packages:e,projectDir:o}){let s=v.join(p,"template/packages/src/app/page"),n=e.payload.inUse,t="base.tsx";n&&(t="with-payload.tsx");let a=v.join(s,t),i=v.join(o,`src/app/${n?"(frontend)":""}/page.tsx`);W.copySync(a,i)}function _e({packages:e,projectDir:o}){let s=v.join(p,"template/packages/src/app/globals"),n=e.payload.inUse,t="base.css";n&&(t="with-payload.css");let a=v.join(s,t),i=v.join(o,`src/app/${n?"(frontend)":""}/globals.css`);W.copySync(a,i)}async function xe({projectName:e,scopedAppName:o,packages:s,noInstall:n,databaseProvider:t}){let a=y(),i=it.resolve(process.cwd(),e);return await ve({projectName:e,projectDir:i,pkgManager:a,scopedAppName:o,noInstall:n,databaseProvider:t}),be({projectName:e,scopedAppName:o,projectDir:i,pkgManager:a,packages:s,noInstall:n,databaseProvider:t}),ke({packages:s,projectDir:i}),we({packages:s,projectDir:i}),_e({packages:s,projectDir:i}),i}import{execSync as z}from"child_process";import V from"path";import{confirm as Pe}from"@inquirer/prompts";import O from"chalk";import{execa as D}from"execa";import Se from"fs-extra";import rt from"ora";function lt(e){try{return z("git --version",{cwd:e}),!0}catch{return!1}}function K(e){return Se.existsSync(V.join(e,".git"))}async function H(e){try{return await D("git",["rev-parse","--is-inside-work-tree"],{cwd:e,stdout:"ignore"}),!0}catch{return!1}}function pt(){let o=z("git --version").toString().trim().split(" ")[2],s=o?.split(".")[0],n=o?.split(".")[1];return{major:Number(s),minor:Number(n)}}function ct(){return z("git config --global init.defaultBranch || echo main").toString().trim()}async function Ae(e){if(r.info("Initializing Git..."),!lt(e)){r.warn("Git is not installed. Skipping Git initialization.");return}let o=rt(`Creating a new git repo...
55
- `).start(),s=K(e),n=await H(e),t=V.parse(e).name;if(n&&s){if(o.stop(),!await Pe({message:`${O.redBright.bold("Warning:")} Git is already initialized in "${t}". Initializing a new git repository would delete the previous history. Would you like to continue anyways?`,default:!1})){o.info("Skipping Git initialization.");return}Se.removeSync(V.join(e,".git"))}else if(n&&!s&&(o.stop(),!await Pe({message:`${O.redBright.bold("Warning:")} "${t}" is already in a git worktree. Would you still like to initialize a new git repository in this directory?`,default:!1}))){o.info("Skipping Git initialization.");return}try{let a=ct(),{major:i,minor:l}=pt();i<2||i==2&&l<28?(await D("git",["init"],{cwd:e}),await D("git",["symbolic-ref","HEAD",`refs/heads/${a}`],{cwd:e})):await D("git",["init",`--initial-branch=${a}`],{cwd:e}),await D("git",["add","."],{cwd:e}),o.succeed(`${O.green("Successfully initialized and staged")} ${O.green.bold("git")}
56
- `)}catch{o.fail(`${O.bold.red("Failed:")} could not initialize git. Update git to the latest version!
57
- `)}}import mt from"chalk";import{execa as Ie}from"execa";import Ce from"ora";var Y=async(e,o,s)=>{let{onDataHandle:n,args:t=["install"],stdout:a="pipe"}=s,i=Ce(`Running ${o} install...`).start(),l=Ie(o,t,{cwd:e,stdout:a});return await new Promise((m,f)=>{n&&l.stdout?.on("data",n(i)),l.on("error",g=>f(g)),l.on("close",()=>m())}),i},dt=async(e,o)=>{switch(e){case"npm":return await Ie(e,["install"],{cwd:o,stderr:"inherit"}),null;case"pnpm":return Y(o,e,{onDataHandle:s=>n=>{let t=n.toString();t.includes("Progress")&&(s.text=t.includes("|")?t.split(" | ")[1]??"":t)}});case"yarn":return Y(o,e,{onDataHandle:s=>n=>{s.text=n.toString()}});case"bun":return Y(o,e,{stdout:"ignore"})}},je=async({projectDir:e})=>{r.info("Installing dependencies...");let o=y();(await dt(o,e)??Ce()).succeed(mt.green(`Successfully installed dependencies!
58
- `))};var Te=async({projectName:e=I,packages:o,noInstall:s,projectDir:n,databaseProvider:t})=>{let a=y();r.info("Next steps:"),e!=="."&&r.info(` cd ${e}`),s&&(a==="yarn"?r.info(` ${a}`):r.info(` ${a} install`)),["postgresql","mysql"].includes(t)&&r.info(" Add your database connection string to .env"),o?.nextAuth.inUse&&r.info(" Fill in your .env with necessary values. See https://create.tntstack.org/usage/first-steps for more info."),["npm"].includes(a)?r.info(` ${a} run dev`):r.info(` ${a} dev`),!await H(n)&&!K(n)&&r.info(" git init"),r.info(' git commit -m "initial commit"')};import U from"fs";import ft from"path";function Oe(e,o,s){U.readdirSync(e).forEach(t=>{let a=ft.join(e,t);if(U.statSync(a).isDirectory())Oe(a,o,s);else{let l=U.readFileSync(a,"utf8").replace(new RegExp(o,"g"),s);U.writeFileSync(a,l,"utf8")}})}function De(e,o){let s=o.replace(/\*/g,"").replace(/[^\/]$/,"$&/");Oe(e,"@/",s)}import Ee from"path";function Me(e){let s=N(e).split("/"),n=s[s.length-1];if(n==="."){let i=Ee.resolve(process.cwd());n=Ee.basename(i)}let t=s.findIndex(i=>i.startsWith("@"));s.findIndex(i=>i.startsWith("@"))!==-1&&(n=s.slice(t).join("/"));let a=s.filter(i=>!i.startsWith("@")).join("/");return[n,a]}import gt from"gradient-string";var ut={magenta:"#765bc8",pink:"#a48897",yellow:"#c7b561",green:"#8bb8a0",blue:"#4b97d5",cyan:"#22b6d2"};function Re(){let e=gt(Object.values(ut)),o=y();(o==="yarn"||o==="pnpm")&&console.log(""),console.log(e.multiline(Z))}import{execSync as yt}from"child_process";import ht from"https";function Ne(e){let o=A();o.includes("beta")?(r.warn(" You are using a beta version of create-tnt-stack."),r.warn(" Please report any bugs you encounter.")):o!==e&&(r.warn(" You are using an outdated version of create-tnt-stack."),r.warn(" Your version:",o+".","Latest version in the npm registry:",e),r.warn(" Please run the CLI with @latest to get the latest updates.")),console.log("")}function bt(){return new Promise((e,o)=>{ht.get("https://registry.npmjs.org/-/package/tnt-stack/dist-tags",s=>{if(s.statusCode===200){let n="";s.on("data",t=>n+=t),s.on("end",()=>{e(JSON.parse(n).latest)})}else o()}).on("error",()=>{o()})})}var Ge=()=>bt().catch(()=>{try{return yt("npm view create-tnt-stack version").toString().trim()}catch{return null}});async function kt(){let e=await Ge(),o=y();Re(),e&&Ne(e);let{appName:s,packages:n,flags:{noGit:t,noInstall:a,importAlias:i},databaseProvider:l}=await ye(),m=de(n),[f,g]=Me(s),c=await xe({projectName:g,scopedAppName:f,packages:m,noInstall:a,databaseProvider:l}),h=$e.readJsonSync(Ue.join(c,"package.json"));h.name=f,h.ctntaMetadata={initVersion:A()};let{stdout:x}=await vt(o,["-v"],{cwd:c});h.packageManager=`${o}@${x.trim()}`,$e.writeJSONSync(Ue.join(c,"package.json"),h,{spaces:2}),i!=="@/"&&De(c,i),a||await je({projectDir:c}),t||await Ae(c),await Te({projectDir:c,projectName:g,packages:m,noInstall:a,databaseProvider:l}),process.exit(0)}kt().catch(e=>{r.error("Aborting installation..."),e instanceof Error?r.error(e.message):(r.error("An unknown error occurred. Please open an issue on GitHub with the below:"),console.error(e)),process.exit(1)});
48
+ can provide the arguments from the CLI directly: https://create.tntstack.org/installation#experimental-usage to skip the prompts.`),new O("Non-interactive environment");let o=h(),t={};s||(t.name=await he({message:"What will your project be called?",default:C,validate:i=>ue(i)})),t.backend=await S({message:"What backend framework would you like to use?",choices:[{value:"nextjs",name:"Next.js"},{value:"payload",name:"Payload CMS"}],default:!c.flags.backend}),t.backend==="payload"&&(t.databaseProvider=await S({message:"What database provider would you like to use?",choices:[{value:"sqlite",name:"SQLite"},{value:"postgresql",name:"PostgreSQL"}],default:!c.flags.dbProvider})),t.backend==="nextjs"&&(t.authentication=await S({message:"What authentication provider would you like to use?",choices:[{value:"none",name:"None"},{value:"nextAuth",name:"NextAuth.js"}],default:!c.flags.authentication}),t.orm=await S({message:"What database ORM would you like to use?",choices:[{value:"none",name:"None"},{value:"prisma",name:"Prisma"}],default:!c.flags.orm}),t.orm!=="none"&&(t.databaseProvider=await S({message:"What database provider would you like to use?",choices:[{value:"sqlite",name:"SQLite"},{value:"mysql",name:"MySQL"},{value:"postgresql",name:"PostgreSQL"}],default:!c.flags.dbProvider}))),t.formatter=await S({message:"What formatter would you like to use?",choices:[{value:"none",name:"None"},{value:"prettier",name:"Prettier"}],default:!c.flags.formatter}),t.linter=await S({message:"What linter would you like to use?",choices:[{value:"eslint",name:"ESLint"}],default:!c.flags.linter}),e.flags.noGit||(t.noGit=await J({message:"Should we initialize a Git repository and stage the changes?",default:!c.flags.noGit})),e.flags.noInstall||(t.noInstall=await J({message:`Should we run '${o}`+(o==="yarn"?"'?":" install' for you?"),default:!c.flags.noInstall})),t.importAlias=await he({message:"What import alias would you like to use?",default:c.flags.importAlias,validate:ye});let a=[];switch(t.backend){case"payload":a.push("payload");break;default:break}switch(t.authentication){case"nextAuth":a.push("nextAuth");break;default:break}switch(t.orm){case"prisma":a.push("prisma");break;default:break}switch(t.formatter){case"prettier":a.push("prettier");break;default:break}switch(t.linter){case"eslint":a.push("eslint");break;default:break}return{appName:t.name??e.appName,packages:a,flags:{...e.flags,noGit:!t.noGit||e.flags.noGit,noInstall:!t.noInstall||e.flags.noInstall,importAlias:t.importAlias??e.flags.importAlias},databaseProvider:t.databaseProvider||"sqlite"}}catch(o){if(o instanceof O)r.warn(`${G} needs an interactive terminal to run.`),await J({message:"Continue scaffolding with default options?",default:!0})||(r.info("Exiting..."),process.exit(0)),r.info(`Scaffolding default tnt app in ./${e.appName}`);else throw o}return e}import lt from"path";import ve from"chalk";import ot from"ora";function ke(e){let{packages:n}=e;r.info("Adding boilerplate...");for(let[s,o]of Object.entries(n))if(o.inUse){let t=ot(`Boilerplating ${s}...`).start();o.installer(e),t.succeed(ve.green(`${ve.green.bold(s)}`))}r.info("")}import W from"path";import{confirm as st,select as it}from"@inquirer/prompts";import A from"chalk";import T from"fs-extra";import rt from"ora";async function we({projectName:e,projectDir:n,pkgManager:s,noInstall:o}){let t=W.join(p,"template/base");o?r.info(""):r.info(`
49
+ Using: ${A.cyan.bold(s)}
50
+ `);let a=rt(`Scaffolding in: ${n}...
51
+ `).start();if(T.existsSync(n))if(T.readdirSync(n).length===0)e!=="."&&a.info(`${A.cyan.bold(e)} exists but is empty, continuing...
52
+ `);else{a.stopAndPersist();let l=await it({message:`${A.redBright.bold("Warning:")} ${A.cyan.bold(e)} already exists and isn't empty. How would you like to proceed?`,choices:[{value:"abort",name:"Abort installation (recommended)"},{value:"clear",name:"Clear the directory and continue installation"},{value:"overwrite",name:"Continue installation and overwrite conflicting files"}],default:"abort"});l==="abort"&&(a.fail("Aborting installation..."),process.exit(1)),await st({message:`Are you sure you want to ${l==="clear"?"clear the directory":"overwrite conflicting files"}`,default:!1})||(a.fail("Aborting installation..."),process.exit(1)),l==="clear"&&(a.info(`Emptying ${A.cyan.bold(e)} and creating tnt app...
53
+ `),T.emptyDirSync(n))}a.start(),T.copySync(t,n),T.renameSync(W.join(n,"_gitignore"),W.join(n,".gitignore"));let i=e==="."?"App":A.cyan.bold(e);a.succeed(`${i} ${A.green.bold("scaffolded successfully!")}
54
+ `)}import v from"path";import B from"fs-extra";function Pe({packages:e,projectDir:n}){let s=v.join(p,"template/packages/src/app/layout"),o=e.payload.inUse,t="base.tsx";o&&(t="base.tsx");let a=v.join(s,t),i=v.join(n,`src/app/${o?"(frontend)":""}/layout.tsx`);B.copySync(a,i)}function xe({packages:e,projectDir:n}){let s=v.join(p,"template/packages/src/app/page"),o=e.payload.inUse,t="base.tsx";o&&(t="with-payload.tsx");let a=v.join(s,t),i=v.join(n,`src/app/${o?"(frontend)":""}/page.tsx`);B.copySync(a,i)}function _e({packages:e,projectDir:n}){let s=v.join(p,"template/packages/src/app/globals"),o=e.payload.inUse,t="base.css";o&&(t="base.css");let a=v.join(s,t),i=v.join(n,`src/app/${o?"(frontend)":""}/globals.css`);B.copySync(a,i)}async function Se({projectName:e,scopedAppName:n,packages:s,noInstall:o,databaseProvider:t}){let a=h(),i=lt.resolve(process.cwd(),e);return await we({projectName:e,projectDir:i,pkgManager:a,scopedAppName:n,noInstall:o,databaseProvider:t}),ke({projectName:e,scopedAppName:n,projectDir:i,pkgManager:a,packages:s,noInstall:o,databaseProvider:t}),Pe({packages:s,projectDir:i}),xe({packages:s,projectDir:i}),_e({packages:s,projectDir:i}),i}import{execSync as z}from"child_process";import V from"path";import{confirm as Ae}from"@inquirer/prompts";import j from"chalk";import{execa as D}from"execa";import Ie from"fs-extra";import pt from"ora";function ct(e){try{return z("git --version",{cwd:e}),!0}catch{return!1}}function K(e){return Ie.existsSync(V.join(e,".git"))}async function H(e){try{return await D("git",["rev-parse","--is-inside-work-tree"],{cwd:e,stdout:"ignore"}),!0}catch{return!1}}function mt(){let n=z("git --version").toString().trim().split(" ")[2],s=n?.split(".")[0],o=n?.split(".")[1];return{major:Number(s),minor:Number(o)}}function dt(){return z("git config --global init.defaultBranch || echo main").toString().trim()}async function Ce(e){if(r.info("Initializing Git..."),!ct(e)){r.warn("Git is not installed. Skipping Git initialization.");return}let n=pt(`Creating a new git repo...
55
+ `).start(),s=K(e),o=await H(e),t=V.parse(e).name;if(o&&s){if(n.stop(),!await Ae({message:`${j.redBright.bold("Warning:")} Git is already initialized in "${t}". Initializing a new git repository would delete the previous history. Would you like to continue anyways?`,default:!1})){n.info("Skipping Git initialization.");return}Ie.removeSync(V.join(e,".git"))}else if(o&&!s&&(n.stop(),!await Ae({message:`${j.redBright.bold("Warning:")} "${t}" is already in a git worktree. Would you still like to initialize a new git repository in this directory?`,default:!1}))){n.info("Skipping Git initialization.");return}try{let a=dt(),{major:i,minor:l}=mt();i<2||i==2&&l<28?(await D("git",["init"],{cwd:e}),await D("git",["symbolic-ref","HEAD",`refs/heads/${a}`],{cwd:e})):await D("git",["init",`--initial-branch=${a}`],{cwd:e}),await D("git",["add","."],{cwd:e}),n.succeed(`${j.green("Successfully initialized and staged")} ${j.green.bold("git")}
56
+ `)}catch{n.fail(`${j.bold.red("Failed:")} could not initialize git. Update git to the latest version!
57
+ `)}}import ft from"chalk";import{execa as Oe}from"execa";import Te from"ora";var Y=async(e,n,s)=>{let{onDataHandle:o,args:t=["install"],stdout:a="pipe"}=s,i=Te(`Running ${n} install...`).start(),l=Oe(n,t,{cwd:e,stdout:a});return await new Promise((d,g)=>{o&&l.stdout?.on("data",o(i)),l.on("error",u=>g(u)),l.on("close",()=>d())}),i},gt=async(e,n)=>{switch(e){case"npm":return await Oe(e,["install"],{cwd:n,stderr:"inherit"}),null;case"pnpm":return Y(n,e,{onDataHandle:s=>o=>{let t=o.toString();t.includes("Progress")&&(s.text=t.includes("|")?t.split(" | ")[1]??"":t)}});case"yarn":return Y(n,e,{onDataHandle:s=>o=>{s.text=o.toString()}});case"bun":return Y(n,e,{stdout:"ignore"})}},je=async({projectDir:e})=>{r.info("Installing dependencies...");let n=h();(await gt(n,e)??Te()).succeed(ft.green(`Successfully installed dependencies!
58
+ `))};var De=async({projectName:e=C,packages:n,noInstall:s,projectDir:o,databaseProvider:t})=>{let a=h();r.info("Next steps:"),e!=="."&&r.info(` cd ${e}`),s&&(a==="yarn"?r.info(` ${a}`):r.info(` ${a} install`)),["postgresql","mysql"].includes(t)&&r.info(" Add your database connection string to .env"),n?.nextAuth.inUse&&r.info(" Fill in your .env with necessary values. See https://create.tntstack.org/usage/first-steps for more info."),["npm"].includes(a)?r.info(` ${a} run dev`):r.info(` ${a} dev`),!await H(o)&&!K(o)&&r.info(" git init"),r.info(' git commit -m "initial commit"')};import $ from"fs";import ut from"path";function Me(e,n,s){$.readdirSync(e).forEach(t=>{let a=ut.join(e,t);if($.statSync(a).isDirectory())Me(a,n,s);else{let l=$.readFileSync(a,"utf8").replace(new RegExp(n,"g"),s);$.writeFileSync(a,l,"utf8")}})}function Re(e,n){let s=n.replace(/\*/g,"").replace(/[^\/]$/,"$&/");Me(e,"@/",s)}import Ee from"path";function Ne(e){let s=N(e).split("/"),o=s[s.length-1];if(o==="."){let i=Ee.resolve(process.cwd());o=Ee.basename(i)}let t=s.findIndex(i=>i.startsWith("@"));s.findIndex(i=>i.startsWith("@"))!==-1&&(o=s.slice(t).join("/"));let a=s.filter(i=>!i.startsWith("@")).join("/");return[o,a]}import yt from"gradient-string";var ht={magenta:"#765bc8",pink:"#a48897",yellow:"#c7b561",green:"#8bb8a0",blue:"#4b97d5",cyan:"#22b6d2"};function $e(){let e=yt(Object.values(ht)),n=h();(n==="yarn"||n==="pnpm")&&console.log(""),console.log(e.multiline(Z))}import{execSync as bt}from"child_process";import vt from"https";function Ge(e){let n=I();n.includes("beta")?(r.warn(" You are using a beta version of create-tnt-stack."),r.warn(" Please report any bugs you encounter.")):n!==e&&(r.warn(" You are using an outdated version of create-tnt-stack."),r.warn(" Your version:",n+".","Latest version in the npm registry:",e),r.warn(" Please run the CLI with @latest to get the latest updates.")),console.log("")}function kt(){return new Promise((e,n)=>{vt.get("https://registry.npmjs.org/-/package/tnt-stack/dist-tags",s=>{if(s.statusCode===200){let o="";s.on("data",t=>o+=t),s.on("end",()=>{e(JSON.parse(o).latest)})}else n()}).on("error",()=>{n()})})}var Ue=()=>kt().catch(()=>{try{return bt("npm view create-tnt-stack version").toString().trim()}catch{return null}});async function Pt(){let e=await Ue(),n=h();$e(),e&&Ge(e);let{appName:s,packages:o,flags:{noGit:t,noInstall:a,importAlias:i},databaseProvider:l}=await be(),d=ge(o),[g,u]=Ne(s),m=await Se({projectName:u,scopedAppName:g,packages:d,noInstall:a,databaseProvider:l}),b=Le.readJsonSync(Fe.join(m,"package.json"));b.name=g,b.ctntaMetadata={initVersion:I()};let{stdout:x}=await wt(n,["-v"],{cwd:m});b.packageManager=`${n}@${x.trim()}`,Le.writeJSONSync(Fe.join(m,"package.json"),b,{spaces:2}),i!=="@/"&&Re(m,i),a||await je({projectDir:m}),t||await Ce(m),await De({projectDir:m,projectName:u,packages:d,noInstall:a,databaseProvider:l}),process.exit(0)}Pt().catch(e=>{r.error("Aborting installation..."),e instanceof Error?r.error(e.message):(r.error("An unknown error occurred. Please open an issue on GitHub with the below:"),console.error(e)),process.exit(1)});
package/package.json CHANGED
@@ -1,88 +1,88 @@
1
1
  {
2
- "name": "create-tnt-stack",
3
- "version": "0.4.2",
4
- "description": "Create web application with the TNT-Powered stack",
5
- "license": "MIT",
6
- "repository": {
7
- "type": "git",
8
- "url": "https://github.com/slickyeet/create-tnt-stack",
9
- "directory": "cli"
10
- },
11
- "keywords": [
12
- "create-tnt-stack",
13
- "tnt-stack",
14
- "typescript",
15
- "next.js",
16
- "tailwind"
17
- ],
18
- "type": "module",
19
- "exports": "./dist/index.js",
20
- "bin": {
21
- "create-tnt-stack": "./dist/index.js"
22
- },
23
- "files": [
24
- "dist",
25
- "template",
26
- "README.md",
27
- "LICENSE",
28
- "package.json"
29
- ],
30
- "engines": {
31
- "node": ">=18"
32
- },
33
- "scripts": {
34
- "dev": "tsup --watch",
35
- "build": "tsup",
36
- "start": "node dist/index.js",
37
- "format": "prettier '**/*.{cjs,mjs,ts,tsx,md,json}' --ignore-path ../.gitignore --ignore-unknown --no-error-on-unmatched-pattern --write",
38
- "format:check": "prettier '**/*.{cjs,mjs,ts,tsx,md,json}' --ignore-path ../.gitignore --ignore-unknown --no-error-on-unmatched-pattern --check",
39
- "typecheck": "tsc",
40
- "clean": "rm -rf dist .turbo node_modules",
41
- "release": "changeset version",
42
- "pub:beta": "bun run build && npm publish --tag beta",
43
- "pub:release": "bun run build && npm publish"
44
- },
45
- "dependencies": {
46
- "@inquirer/prompts": "^7.4.0",
47
- "chalk": "^5.4.1",
48
- "commander": "^13.1.0",
49
- "execa": "^9.5.2",
50
- "fs-extra": "^11.3.0",
51
- "gradient-string": "^3.0.0",
52
- "ora": "^8.2.0",
53
- "sort-package-json": "^3.0.0"
54
- },
55
- "devDependencies": {
56
- "@auth/prisma-adapter": "^2.8.0",
57
- "@eslint/eslintrc": "^3.3.1",
58
- "@ianvs/prettier-plugin-sort-imports": "^4.4.1",
59
- "@payloadcms/db-sqlite": "^3.33.0",
60
- "@payloadcms/db-vercel-postgres": "^3.33.0",
61
- "@payloadcms/next": "^3.33.0",
62
- "@payloadcms/payload-cloud": "^3.33.0",
63
- "@payloadcms/richtext-lexical": "^3.33.0",
64
- "@prisma/client": "^6.5.0",
65
- "@t3-oss/env-nextjs": "^0.12.0",
66
- "@types/fs-extra": "^11.0.4",
67
- "@types/node": "^22",
68
- "@types/react": "^19",
69
- "@types/react-dom": "^19",
70
- "eslint": "^9.22.0",
71
- "eslint-config-next": "^15.2.4",
72
- "graphql": "^16.10.0",
73
- "next": "^15.2.4",
74
- "next-auth": "^4.24.11",
75
- "payload": "^3.33.0",
76
- "prettier": "^3.5.3",
77
- "prettier-plugin-tailwindcss": "^0.6.11",
78
- "prisma": "^6.5.0",
79
- "react": "^19.0.0",
80
- "react-dom": "^19.0.0",
81
- "sharp": "^0.34.1",
82
- "tailwindcss": "^4.0.17",
83
- "tsup": "^8.4.0",
84
- "type-fest": "^4.37.0",
85
- "typescript": "^5.8.2",
86
- "zod": "^3.24.2"
87
- }
2
+ "name": "create-tnt-stack",
3
+ "version": "0.4.4-beta.99024cb",
4
+ "description": "Create web application with the TNT-Powered stack",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/slickyeet/create-tnt-stack",
9
+ "directory": "cli"
10
+ },
11
+ "keywords": [
12
+ "create-tnt-stack",
13
+ "tnt-stack",
14
+ "typescript",
15
+ "next.js",
16
+ "tailwind"
17
+ ],
18
+ "type": "module",
19
+ "exports": "./dist/index.js",
20
+ "bin": {
21
+ "create-tnt-stack": "./dist/index.js"
22
+ },
23
+ "files": [
24
+ "dist",
25
+ "template",
26
+ "README.md",
27
+ "LICENSE",
28
+ "package.json"
29
+ ],
30
+ "engines": {
31
+ "node": ">=18"
32
+ },
33
+ "scripts": {
34
+ "dev": "tsup --watch",
35
+ "build": "tsup",
36
+ "start": "node dist/index.js",
37
+ "format": "prettier '**/*.{cjs,mjs,ts,tsx,md,json}' --ignore-path ../.gitignore --ignore-unknown --no-error-on-unmatched-pattern --write",
38
+ "format:check": "prettier '**/*.{cjs,mjs,ts,tsx,md,json}' --ignore-path ../.gitignore --ignore-unknown --no-error-on-unmatched-pattern --check",
39
+ "typecheck": "tsc",
40
+ "clean": "rm -rf dist .turbo node_modules",
41
+ "release": "changeset version",
42
+ "pub:beta": "bun run build && npm publish --tag beta",
43
+ "pub:release": "bun run build && npm publish"
44
+ },
45
+ "dependencies": {
46
+ "@inquirer/prompts": "^7.4.0",
47
+ "chalk": "^5.4.1",
48
+ "commander": "^13.1.0",
49
+ "execa": "^9.5.2",
50
+ "fs-extra": "^11.3.0",
51
+ "gradient-string": "^3.0.0",
52
+ "ora": "^8.2.0",
53
+ "sort-package-json": "^3.0.0"
54
+ },
55
+ "devDependencies": {
56
+ "@auth/prisma-adapter": "^2.8.0",
57
+ "@eslint/eslintrc": "^3.3.1",
58
+ "@ianvs/prettier-plugin-sort-imports": "^4.4.1",
59
+ "@payloadcms/db-sqlite": "^3.33.0",
60
+ "@payloadcms/db-vercel-postgres": "^3.33.0",
61
+ "@payloadcms/next": "^3.33.0",
62
+ "@payloadcms/payload-cloud": "^3.33.0",
63
+ "@payloadcms/richtext-lexical": "^3.33.0",
64
+ "@prisma/client": "^6.5.0",
65
+ "@t3-oss/env-nextjs": "^0.12.0",
66
+ "@types/fs-extra": "^11.0.4",
67
+ "@types/node": "^22",
68
+ "@types/react": "^19",
69
+ "@types/react-dom": "^19",
70
+ "eslint": "^9.22.0",
71
+ "eslint-config-next": "^15.2.4",
72
+ "graphql": "^16.10.0",
73
+ "next": "^15.2.4",
74
+ "next-auth": "^4.24.11",
75
+ "payload": "^3.33.0",
76
+ "prettier": "^3.5.3",
77
+ "prettier-plugin-tailwindcss": "^0.6.11",
78
+ "prisma": "^6.5.0",
79
+ "react": "^19.0.0",
80
+ "react-dom": "^19.0.0",
81
+ "sharp": "^0.34.1",
82
+ "tailwindcss": "^4.0.17",
83
+ "tsup": "^8.4.0",
84
+ "type-fest": "^4.37.0",
85
+ "typescript": "^5.8.2",
86
+ "zod": "^3.24.2"
87
+ }
88
88
  }
@@ -14,8 +14,8 @@ const geistMono = Geist_Mono({
14
14
  })
15
15
 
16
16
  export const metadata: Metadata = {
17
- title: "Create Next App",
18
- description: "Generated by create next app",
17
+ title: "Create TNT Stack",
18
+ description: "Generated by create tnt stack",
19
19
  }
20
20
 
21
21
  export default function RootLayout({
@@ -1,105 +1,97 @@
1
- import Link from "next/link"
1
+ import { fileURLToPath } from "url"
2
2
 
3
3
  export default function HomePage() {
4
+ const fileURL = `vscode://file/${fileURLToPath(import.meta.url)}`
5
+
4
6
  return (
5
- <main className="flex min-h-screen flex-col items-center justify-center px-4 text-center">
6
- {/* Logo */}
7
- <div className="mb-8">
8
- <div className="relative flex h-24 w-24 items-center justify-center">
7
+ <main className="mx-auto flex h-screen max-w-5xl flex-col items-center justify-between overflow-hidden p-6 sm:p-[45px]">
8
+ <div className="flex grow flex-col items-center justify-center">
9
+ {/* Logo */}
10
+ <picture className="relative">
9
11
  <div className="absolute inset-0 animate-pulse rounded-xl bg-gradient-to-r from-purple-500 to-cyan-500 opacity-20 blur-xl dark:from-purple-800 dark:to-cyan-800" />
10
- <svg
11
- xmlns="http://www.w3.org/2000/svg"
12
- width="80"
13
- height="80"
14
- viewBox="0 0 24 24"
15
- fill="none"
16
- stroke="currentColor"
17
- strokeWidth="2"
18
- strokeLinecap="round"
19
- strokeLinejoin="round"
20
- className="rounded-lg bg-gradient-to-r from-purple-500 to-cyan-500 dark:from-purple-800 dark:to-cyan-800"
21
- >
22
- <polyline points="4 17 10 11 4 5" />
23
- <line x1="12" x2="20" y1="19" y2="19" />
24
- </svg>
25
- </div>
26
- </div>
12
+ <source srcSet="https://github.com/SlickYeet/create-tnt-stack/blob/main/docs/public/logo.light.png?raw=true" />
13
+ <img
14
+ src="https://github.com/SlickYeet/create-tnt-stack/blob/main/docs/public/logo.light.png?raw=true"
15
+ alt="Logo"
16
+ width={65}
17
+ height={65}
18
+ className="block h-auto max-w-full"
19
+ />
20
+ </picture>
27
21
 
28
- <div className="max-w-xl text-balance">
29
- <h1 className="mb-8 bg-gradient-to-r from-purple-500 to-cyan-500 bg-clip-text pb-1.5 text-6xl font-bold tracking-tighter text-transparent md:text-7xl lg:text-8xl">
22
+ <h1 className="mt-6 bg-gradient-to-r from-purple-500 to-cyan-500 bg-clip-text text-center text-4xl leading-10 text-transparent sm:text-5xl sm:leading-14 md:text-6xl md:leading-20 lg:mt-10 lg:text-7xl lg:font-bold">
30
23
  TNT-Powered Next.js App
31
24
  </h1>
32
- <p className="mb-12 text-xl text-neutral-700 md:text-2xl dark:text-neutral-300">
33
- Build modern web applications with todat&apos;s most popular tools
25
+ <p className="mt-4 text-center text-lg text-neutral-700 md:text-xl lg:mt-6 dark:text-neutral-300">
26
+ Build modern web applications with today&apos;s most popular tools
34
27
  </p>
35
- </div>
36
28
 
37
- <div className="mb-12 flex flex-col gap-10 sm:flex-row">
38
- <Link
39
- href="https://create.tntstack.org"
40
- target="_blank"
41
- referrerPolicy="no-referrer"
42
- className="hover:text-primary relative flex items-center justify-center gap-2 text-lg font-medium"
43
- >
44
- Website
45
- <svg
46
- xmlns="http://www.w3.org/2000/svg"
47
- viewBox="0 0 24 24"
48
- strokeLinecap="round"
49
- strokeLinejoin="round"
50
- className="absolute top-0 -right-4 size-4 fill-none stroke-current stroke-2"
29
+ <div className="mt-12 flex items-center gap-3">
30
+ <a
31
+ href="https://create.tntstack.org"
32
+ target="_blank"
33
+ rel="noopener noreferrer"
34
+ className="flex items-center rounded-md border border-white px-2 py-1 outline-none focus:opacity-80 active:opacity-70"
51
35
  >
52
- <path d="M7 7h10v10" />
53
- <path d="M7 17 17 7" />
54
- </svg>
55
- </Link>
56
-
57
- <Link
58
- href="https://create.tntstack.org/introduction"
59
- target="_blank"
60
- referrerPolicy="no-referrer"
61
- className="hover:text-primary relative flex items-center justify-center gap-2 text-lg font-medium"
62
- >
63
- Docs
64
- <svg
65
- xmlns="http://www.w3.org/2000/svg"
66
- viewBox="0 0 24 24"
67
- strokeLinecap="round"
68
- strokeLinejoin="round"
69
- className="absolute top-0 -right-4 size-4 fill-none stroke-current stroke-2"
36
+ Website
37
+ <svg
38
+ xmlns="http://www.w3.org/2000/svg"
39
+ viewBox="0 0 24 24"
40
+ strokeLinecap="round"
41
+ strokeLinejoin="round"
42
+ className="mb-1.5 size-4 fill-none stroke-current stroke-2"
43
+ >
44
+ <path d="M7 7h10v10" />
45
+ <path d="M7 17 17 7" />
46
+ </svg>
47
+ </a>
48
+ <a
49
+ href="https://create.tntstack.org/introduction"
50
+ target="_blank"
51
+ rel="noopener noreferrer"
52
+ className="flex items-center rounded-md border border-white px-2 py-1 outline-none focus:opacity-80 active:opacity-70"
70
53
  >
71
- <path d="M7 7h10v10" />
72
- <path d="M7 17 17 7" />
73
- </svg>
74
- </Link>
75
-
76
- <Link
77
- href="https://github.com/SlickYeet/create-tnt-stack"
78
- target="_blank"
79
- referrerPolicy="no-referrer"
80
- className="hover:text-primary relative flex items-center justify-center gap-2 text-lg font-medium"
81
- >
82
- GitHub
83
- <svg
84
- xmlns="http://www.w3.org/2000/svg"
85
- viewBox="0 0 24 24"
86
- strokeLinecap="round"
87
- strokeLinejoin="round"
88
- className="absolute top-0 -right-4 size-4 fill-none stroke-current stroke-2"
54
+ Docs
55
+ <svg
56
+ xmlns="http://www.w3.org/2000/svg"
57
+ viewBox="0 0 24 24"
58
+ strokeLinecap="round"
59
+ strokeLinejoin="round"
60
+ className="mb-1.5 size-4 fill-none stroke-current stroke-2"
61
+ >
62
+ <path d="M7 7h10v10" />
63
+ <path d="M7 17 17 7" />
64
+ </svg>
65
+ </a>
66
+ <a
67
+ href="https://github.com/SlickYeet/create-tnt-stack"
68
+ target="_blank"
69
+ rel="noopener noreferrer"
70
+ className="flex items-center rounded-md border border-white px-2 py-1 outline-none focus:opacity-80 active:opacity-70"
89
71
  >
90
- <path d="M7 7h10v10" />
91
- <path d="M7 17 17 7" />
92
- </svg>
93
- </Link>
72
+ GitHub
73
+ <svg
74
+ xmlns="http://www.w3.org/2000/svg"
75
+ viewBox="0 0 24 24"
76
+ strokeLinecap="round"
77
+ strokeLinejoin="round"
78
+ className="mb-1.5 size-4 fill-none stroke-current stroke-2"
79
+ >
80
+ <path d="M7 7h10v10" />
81
+ <path d="M7 17 17 7" />
82
+ </svg>
83
+ </a>
84
+ </div>
94
85
  </div>
95
86
 
96
- <div className="mt-16 text-sm text-neutral-600 dark:text-neutral-400">
97
- <p>
98
- Get started by editing{" "}
99
- <code className="rounded-md bg-neutral-200 px-2 py-1 dark:bg-neutral-800">
100
- src/app/page.tsx
101
- </code>
102
- </p>
87
+ <div className="flex flex-col items-center gap-1 text-sm text-neutral-600 lg:flex-row lg:gap-2 dark:text-neutral-400">
88
+ <p className="m-0">Get started by editing </p>
89
+ <a
90
+ href={fileURL}
91
+ className="rounded-md bg-neutral-200 px-2 py-1 dark:bg-neutral-800"
92
+ >
93
+ <code>src/app/page.tsx</code>
94
+ </a>
103
95
  </div>
104
96
  </main>
105
97
  )
@@ -1,7 +1,6 @@
1
1
  import { fileURLToPath } from "url"
2
2
  import config from "@payload-config"
3
3
  import { headers as getHeaders } from "next/headers.js"
4
- import Image from "next/image"
5
4
  import { getPayload } from "payload"
6
5
 
7
6
  import "./globals.css"
@@ -15,44 +14,123 @@ export default async function HomePage() {
15
14
  const fileURL = `vscode://file/${fileURLToPath(import.meta.url)}`
16
15
 
17
16
  return (
18
- <div className="home">
19
- <div className="content">
20
- <picture>
21
- <source srcSet="https://raw.githubusercontent.com/payloadcms/payload/main/packages/ui/src/assets/payload-favicon.svg" />
22
- <Image
23
- alt="Payload Logo"
24
- height={65}
25
- src="https://raw.githubusercontent.com/payloadcms/payload/main/packages/ui/src/assets/payload-favicon.svg"
17
+ <main className="mx-auto flex h-screen max-w-5xl flex-col items-center justify-between overflow-hidden p-6 sm:p-[45px]">
18
+ <div className="flex grow flex-col items-center justify-center">
19
+ {/* Logo */}
20
+ <picture className="relative">
21
+ <div className="absolute inset-0 animate-pulse rounded-xl bg-gradient-to-r from-purple-500 to-cyan-500 opacity-20 blur-xl dark:from-purple-800 dark:to-cyan-800" />
22
+ <source srcSet="https://github.com/SlickYeet/create-tnt-stack/blob/main/docs/public/logo.light.png?raw=true" />
23
+ <img
24
+ src="https://github.com/SlickYeet/create-tnt-stack/blob/main/docs/public/logo.light.png?raw=true"
25
+ alt="Logo"
26
26
  width={65}
27
+ height={65}
28
+ className="block h-auto max-w-full"
27
29
  />
28
30
  </picture>
29
- {!user && <h1>Welcome to your new project.</h1>}
30
- {user && <h1>Welcome back, {user.email}</h1>}
31
- <div className="links">
31
+
32
+ {!user && (
33
+ <>
34
+ <h1 className="mt-6 bg-gradient-to-r from-purple-500 to-cyan-500 bg-clip-text text-center text-4xl leading-10 text-transparent sm:text-5xl sm:leading-14 md:text-6xl md:leading-20 lg:mt-10 lg:text-7xl lg:font-bold">
35
+ TNT-Powered Next.js App
36
+ </h1>
37
+ <p className="mt-4 text-center text-lg text-neutral-700 md:text-xl lg:mt-6 dark:text-neutral-300">
38
+ Build modern web applications with today&apos;s most popular tools
39
+ </p>
40
+ </>
41
+ )}
42
+ {user && (
43
+ <h1 className="mt-6 bg-gradient-to-r from-purple-500 to-cyan-500 bg-clip-text text-center text-4xl leading-10 text-transparent sm:text-5xl sm:leading-14 md:text-6xl md:leading-20 lg:mt-10 lg:text-7xl lg:font-bold">
44
+ Welcome back, {user.email}
45
+ </h1>
46
+ )}
47
+
48
+ <div className="mt-12 flex items-center gap-3">
32
49
  <a
33
- className="admin"
34
50
  href={payloadConfig.routes.admin}
35
51
  rel="noopener noreferrer"
36
52
  target="_blank"
53
+ className="rounded-md bg-white px-2 py-1 text-black focus:opacity-80 focus:outline-none active:opacity-70 active:outline-none"
37
54
  >
38
55
  Go to admin panel
39
56
  </a>
40
57
  <a
41
- className="docs"
42
58
  href="https://payloadcms.com/docs"
43
59
  rel="noopener noreferrer"
44
60
  target="_blank"
61
+ className="rounded-md border border-white px-2 py-1 text-white focus:opacity-80 focus:outline-none active:opacity-70 active:outline-none"
62
+ >
63
+ Payload Docs
64
+ </a>
65
+ </div>
66
+
67
+ <div className="mt-12 flex items-center gap-3">
68
+ <a
69
+ href="https://create.tntstack.org"
70
+ target="_blank"
71
+ rel="noopener noreferrer"
72
+ className="flex items-center rounded-md border border-white px-2 py-1 outline-none focus:opacity-80 active:opacity-70"
73
+ >
74
+ Website
75
+ <svg
76
+ xmlns="http://www.w3.org/2000/svg"
77
+ viewBox="0 0 24 24"
78
+ strokeLinecap="round"
79
+ strokeLinejoin="round"
80
+ className="mb-1.5 size-4 fill-none stroke-current stroke-2"
81
+ >
82
+ <path d="M7 7h10v10" />
83
+ <path d="M7 17 17 7" />
84
+ </svg>
85
+ </a>
86
+ <a
87
+ href="https://create.tntstack.org/introduction"
88
+ target="_blank"
89
+ rel="noopener noreferrer"
90
+ className="flex items-center rounded-md border border-white px-2 py-1 outline-none focus:opacity-80 active:opacity-70"
45
91
  >
46
- Documentation
92
+ Docs
93
+ <svg
94
+ xmlns="http://www.w3.org/2000/svg"
95
+ viewBox="0 0 24 24"
96
+ strokeLinecap="round"
97
+ strokeLinejoin="round"
98
+ className="mb-1.5 size-4 fill-none stroke-current stroke-2"
99
+ >
100
+ <path d="M7 7h10v10" />
101
+ <path d="M7 17 17 7" />
102
+ </svg>
103
+ </a>
104
+ <a
105
+ href="https://github.com/SlickYeet/create-tnt-stack"
106
+ target="_blank"
107
+ rel="noopener noreferrer"
108
+ className="flex items-center rounded-md border border-white px-2 py-1 outline-none focus:opacity-80 active:opacity-70"
109
+ >
110
+ GitHub
111
+ <svg
112
+ xmlns="http://www.w3.org/2000/svg"
113
+ viewBox="0 0 24 24"
114
+ strokeLinecap="round"
115
+ strokeLinejoin="round"
116
+ className="mb-1.5 size-4 fill-none stroke-current stroke-2"
117
+ >
118
+ <path d="M7 7h10v10" />
119
+ <path d="M7 17 17 7" />
120
+ </svg>
47
121
  </a>
48
122
  </div>
49
123
  </div>
50
- <div className="footer">
51
- <p>Update this page by editing</p>
52
- <a className="codeLink" href={fileURL}>
53
- <code>app/(frontend)/page.tsx</code>
124
+
125
+ <div className="flex flex-col items-center gap-1 text-sm text-neutral-600 lg:flex-row lg:gap-2 dark:text-neutral-400">
126
+ <p className="m-0">Get started by editing </p>
127
+ <a
128
+ href={fileURL}
129
+ className="rounded-md bg-neutral-200 px-2 py-1 dark:bg-neutral-800"
130
+ >
131
+ <code>src/app/(frontend)/page.tsx</code>
54
132
  </a>
55
133
  </div>
56
- </div>
134
+ </main>
57
135
  )
58
136
  }
@@ -1,166 +0,0 @@
1
- @import "tailwindcss";
2
-
3
- :root {
4
- --font-mono: "Roboto Mono", monospace;
5
- }
6
-
7
- * {
8
- box-sizing: border-box;
9
- }
10
-
11
- html {
12
- font-size: 18px;
13
- line-height: 32px;
14
-
15
- background: rgb(0, 0, 0);
16
- -webkit-font-smoothing: antialiased;
17
- }
18
-
19
- html,
20
- body,
21
- #app {
22
- height: 100%;
23
- }
24
-
25
- body {
26
- font-family: system-ui;
27
- font-size: 18px;
28
- line-height: 32px;
29
-
30
- margin: 0;
31
- color: rgb(1000, 1000, 1000);
32
-
33
- @media (max-width: 1024px) {
34
- font-size: 15px;
35
- line-height: 24px;
36
- }
37
- }
38
-
39
- img {
40
- max-width: 100%;
41
- height: auto;
42
- display: block;
43
- }
44
-
45
- h1 {
46
- margin: 40px 0;
47
- font-size: 64px;
48
- line-height: 70px;
49
- font-weight: bold;
50
-
51
- @media (max-width: 1024px) {
52
- margin: 24px 0;
53
- font-size: 42px;
54
- line-height: 42px;
55
- }
56
-
57
- @media (max-width: 768px) {
58
- font-size: 38px;
59
- line-height: 38px;
60
- }
61
-
62
- @media (max-width: 400px) {
63
- font-size: 32px;
64
- line-height: 32px;
65
- }
66
- }
67
-
68
- p {
69
- margin: 24px 0;
70
-
71
- @media (max-width: 1024px) {
72
- margin: calc(var(--base) * 0.75) 0;
73
- }
74
- }
75
-
76
- a {
77
- color: currentColor;
78
-
79
- &:focus {
80
- opacity: 0.8;
81
- outline: none;
82
- }
83
-
84
- &:active {
85
- opacity: 0.7;
86
- outline: none;
87
- }
88
- }
89
-
90
- svg {
91
- vertical-align: middle;
92
- }
93
-
94
- .home {
95
- display: flex;
96
- flex-direction: column;
97
- justify-content: space-between;
98
- align-items: center;
99
- height: 100vh;
100
- padding: 45px;
101
- max-width: 1024px;
102
- margin: 0 auto;
103
- overflow: hidden;
104
-
105
- @media (max-width: 400px) {
106
- padding: 24px;
107
- }
108
-
109
- .content {
110
- display: flex;
111
- flex-direction: column;
112
- align-items: center;
113
- justify-content: center;
114
- flex-grow: 1;
115
-
116
- h1 {
117
- text-align: center;
118
- }
119
- }
120
-
121
- .links {
122
- display: flex;
123
- align-items: center;
124
- gap: 12px;
125
-
126
- a {
127
- text-decoration: none;
128
- padding: 0.25rem 0.5rem;
129
- border-radius: 4px;
130
- }
131
-
132
- .admin {
133
- color: rgb(0, 0, 0);
134
- background: rgb(1000, 1000, 1000);
135
- border: 1px solid rgb(0, 0, 0);
136
- }
137
-
138
- .docs {
139
- color: rgb(1000, 1000, 1000);
140
- background: rgb(0, 0, 0);
141
- border: 1px solid rgb(1000, 1000, 1000);
142
- }
143
- }
144
-
145
- .footer {
146
- display: flex;
147
- align-items: center;
148
- gap: 8px;
149
-
150
- @media (max-width: 1024px) {
151
- flex-direction: column;
152
- gap: 6px;
153
- }
154
-
155
- p {
156
- margin: 0;
157
- }
158
-
159
- .codeLink {
160
- text-decoration: none;
161
- padding: 0 0.5rem;
162
- background: rgb(60, 60, 60);
163
- border-radius: 4px;
164
- }
165
- }
166
- }
@@ -1,20 +0,0 @@
1
- import type { Metadata } from "next"
2
-
3
- import "./globals.css"
4
-
5
- export const metadata: Metadata = {
6
- description: "A blank template using Payload in a Next.js app.",
7
- title: "Payload Blank Template",
8
- }
9
-
10
- export default function RootLayout({
11
- children,
12
- }: Readonly<{
13
- children: React.ReactNode
14
- }>) {
15
- return (
16
- <html lang="en">
17
- <body className="antialiased">{children}</body>
18
- </html>
19
- )
20
- }