create-kofi-stack 1.2.3 → 1.2.5
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 +90 -603
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -66,11 +66,6 @@ async function promptMarketingSite(defaultValue) {
|
|
|
66
66
|
label: "Next.js",
|
|
67
67
|
hint: "Simple static/SSG site"
|
|
68
68
|
},
|
|
69
|
-
{
|
|
70
|
-
value: "astro",
|
|
71
|
-
label: "Astro",
|
|
72
|
-
hint: "Lightweight, content-focused"
|
|
73
|
-
},
|
|
74
69
|
{
|
|
75
70
|
value: "none",
|
|
76
71
|
label: "None",
|
|
@@ -713,7 +708,7 @@ ${extras.length > 0 ? `${pc.bold("Extras:")} ${extras.join(", ")}` : ""}`,
|
|
|
713
708
|
}
|
|
714
709
|
|
|
715
710
|
// src/generators/index.ts
|
|
716
|
-
import
|
|
711
|
+
import path19 from "path";
|
|
717
712
|
import * as p7 from "@clack/prompts";
|
|
718
713
|
import pc3 from "picocolors";
|
|
719
714
|
import ora from "ora";
|
|
@@ -811,11 +806,11 @@ async function generatePackageJson(config, appDir) {
|
|
|
811
806
|
next: "^16.0.0",
|
|
812
807
|
react: "^19.0.0",
|
|
813
808
|
"react-dom": "^19.0.0",
|
|
814
|
-
"@convex-dev/better-auth": "^0.10.
|
|
815
|
-
"better-auth": "
|
|
809
|
+
"@convex-dev/better-auth": "^0.10.10",
|
|
810
|
+
"better-auth": "1.4.9",
|
|
816
811
|
convex: "^1.25.0",
|
|
817
|
-
"@t3-oss/env-nextjs": "^0.
|
|
818
|
-
zod: "^
|
|
812
|
+
"@t3-oss/env-nextjs": "^0.13.0",
|
|
813
|
+
zod: "^4.0.0",
|
|
819
814
|
"date-fns": "^4.0.0",
|
|
820
815
|
clsx: "^2.1.0",
|
|
821
816
|
"tailwind-merge": "^2.5.0",
|
|
@@ -1996,7 +1991,7 @@ async function generateConvexPackageJson(convexDir) {
|
|
|
1996
1991
|
types: "./convex/index.ts",
|
|
1997
1992
|
dependencies: {
|
|
1998
1993
|
convex: "^1.25.0",
|
|
1999
|
-
"@convex-dev/better-auth": "^0.10.
|
|
1994
|
+
"@convex-dev/better-auth": "^0.10.10"
|
|
2000
1995
|
},
|
|
2001
1996
|
devDependencies: {
|
|
2002
1997
|
typescript: "^5.0.0"
|
|
@@ -3257,10 +3252,10 @@ async function generatePayloadPackageJson(marketingDir) {
|
|
|
3257
3252
|
"db:seed": "tsx src/seed.ts"
|
|
3258
3253
|
},
|
|
3259
3254
|
dependencies: {
|
|
3260
|
-
next: "^
|
|
3255
|
+
next: "^15.4.10",
|
|
3261
3256
|
react: "^19.0.0",
|
|
3262
3257
|
"react-dom": "^19.0.0",
|
|
3263
|
-
payload: "^3.
|
|
3258
|
+
payload: "^3.70.0",
|
|
3264
3259
|
"@payloadcms/db-postgres": "^3.0.0",
|
|
3265
3260
|
"@payloadcms/next": "^3.0.0",
|
|
3266
3261
|
"@payloadcms/richtext-lexical": "^3.0.0",
|
|
@@ -4643,539 +4638,8 @@ See the [Convex documentation](https://docs.convex.dev) for database API referen
|
|
|
4643
4638
|
);
|
|
4644
4639
|
}
|
|
4645
4640
|
|
|
4646
|
-
// src/generators/astro.ts
|
|
4647
|
-
import path17 from "path";
|
|
4648
|
-
async function generateAstro(config, marketingDir) {
|
|
4649
|
-
await ensureDir(path17.join(marketingDir, "src/components"));
|
|
4650
|
-
await ensureDir(path17.join(marketingDir, "src/layouts"));
|
|
4651
|
-
await ensureDir(path17.join(marketingDir, "src/pages"));
|
|
4652
|
-
await ensureDir(path17.join(marketingDir, "src/content/blog"));
|
|
4653
|
-
await ensureDir(path17.join(marketingDir, "src/styles"));
|
|
4654
|
-
await ensureDir(path17.join(marketingDir, "public"));
|
|
4655
|
-
await generateAstroPackageJson(marketingDir);
|
|
4656
|
-
await generateAstroConfig(marketingDir);
|
|
4657
|
-
await generateAstroTsConfig(marketingDir);
|
|
4658
|
-
await generateAstroLayouts(config, marketingDir);
|
|
4659
|
-
await generateAstroPages(config, marketingDir);
|
|
4660
|
-
await generateAstroComponents(config, marketingDir);
|
|
4661
|
-
await generateAstroStyles(marketingDir);
|
|
4662
|
-
await generateAstroContent(config, marketingDir);
|
|
4663
|
-
}
|
|
4664
|
-
async function generateAstroPackageJson(marketingDir) {
|
|
4665
|
-
const packageJson = {
|
|
4666
|
-
name: "@repo/marketing",
|
|
4667
|
-
version: "0.1.0",
|
|
4668
|
-
private: true,
|
|
4669
|
-
type: "module",
|
|
4670
|
-
scripts: {
|
|
4671
|
-
dev: "astro dev --port 3001",
|
|
4672
|
-
build: "astro build",
|
|
4673
|
-
preview: "astro preview",
|
|
4674
|
-
lint: "biome check .",
|
|
4675
|
-
"lint:fix": "biome check --write .",
|
|
4676
|
-
typecheck: "astro check && tsc --noEmit"
|
|
4677
|
-
},
|
|
4678
|
-
dependencies: {
|
|
4679
|
-
astro: "^5.0.0",
|
|
4680
|
-
"@astrojs/mdx": "^4.0.0",
|
|
4681
|
-
"@astrojs/sitemap": "^3.0.0",
|
|
4682
|
-
"@astrojs/tailwind": "^6.0.0",
|
|
4683
|
-
"@astrojs/react": "^4.0.0",
|
|
4684
|
-
react: "^19.0.0",
|
|
4685
|
-
"react-dom": "^19.0.0"
|
|
4686
|
-
},
|
|
4687
|
-
devDependencies: {
|
|
4688
|
-
"@repo/config-typescript": "workspace:*",
|
|
4689
|
-
"@types/react": "^19.0.0",
|
|
4690
|
-
"@types/react-dom": "^19.0.0",
|
|
4691
|
-
tailwindcss: "^4.0.0",
|
|
4692
|
-
typescript: "^5.0.0"
|
|
4693
|
-
}
|
|
4694
|
-
};
|
|
4695
|
-
await writeJSON(path17.join(marketingDir, "package.json"), packageJson);
|
|
4696
|
-
}
|
|
4697
|
-
async function generateAstroConfig(marketingDir) {
|
|
4698
|
-
const content = `import { defineConfig } from 'astro/config'
|
|
4699
|
-
import mdx from '@astrojs/mdx'
|
|
4700
|
-
import sitemap from '@astrojs/sitemap'
|
|
4701
|
-
import tailwind from '@astrojs/tailwind'
|
|
4702
|
-
import react from '@astrojs/react'
|
|
4703
|
-
|
|
4704
|
-
export default defineConfig({
|
|
4705
|
-
site: 'https://example.com',
|
|
4706
|
-
integrations: [
|
|
4707
|
-
mdx(),
|
|
4708
|
-
sitemap(),
|
|
4709
|
-
tailwind(),
|
|
4710
|
-
react(),
|
|
4711
|
-
],
|
|
4712
|
-
markdown: {
|
|
4713
|
-
shikiConfig: {
|
|
4714
|
-
theme: 'github-dark',
|
|
4715
|
-
wrap: true,
|
|
4716
|
-
},
|
|
4717
|
-
},
|
|
4718
|
-
})
|
|
4719
|
-
`;
|
|
4720
|
-
await writeFile(path17.join(marketingDir, "astro.config.mjs"), content);
|
|
4721
|
-
}
|
|
4722
|
-
async function generateAstroTsConfig(marketingDir) {
|
|
4723
|
-
const tsConfig = {
|
|
4724
|
-
extends: "astro/tsconfigs/strict",
|
|
4725
|
-
compilerOptions: {
|
|
4726
|
-
jsx: "react-jsx",
|
|
4727
|
-
jsxImportSource: "react",
|
|
4728
|
-
paths: {
|
|
4729
|
-
"@/*": ["./src/*"]
|
|
4730
|
-
}
|
|
4731
|
-
}
|
|
4732
|
-
};
|
|
4733
|
-
await writeJSON(path17.join(marketingDir, "tsconfig.json"), tsConfig);
|
|
4734
|
-
}
|
|
4735
|
-
async function generateAstroLayouts(config, marketingDir) {
|
|
4736
|
-
const baseLayoutContent = `---
|
|
4737
|
-
interface Props {
|
|
4738
|
-
title: string
|
|
4739
|
-
description?: string
|
|
4740
|
-
}
|
|
4741
|
-
|
|
4742
|
-
const { title, description = '${config.name} - Built with create-kofi-stack' } = Astro.props
|
|
4743
|
-
---
|
|
4744
|
-
|
|
4745
|
-
<!doctype html>
|
|
4746
|
-
<html lang="en">
|
|
4747
|
-
<head>
|
|
4748
|
-
<meta charset="UTF-8" />
|
|
4749
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
4750
|
-
<meta name="description" content={description} />
|
|
4751
|
-
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
4752
|
-
<title>{title}</title>
|
|
4753
|
-
</head>
|
|
4754
|
-
<body class="min-h-screen bg-background text-foreground">
|
|
4755
|
-
<slot />
|
|
4756
|
-
</body>
|
|
4757
|
-
</html>
|
|
4758
|
-
|
|
4759
|
-
<style is:global>
|
|
4760
|
-
@import '../styles/global.css';
|
|
4761
|
-
</style>
|
|
4762
|
-
`;
|
|
4763
|
-
await writeFile(
|
|
4764
|
-
path17.join(marketingDir, "src/layouts/BaseLayout.astro"),
|
|
4765
|
-
baseLayoutContent
|
|
4766
|
-
);
|
|
4767
|
-
const blogLayoutContent = `---
|
|
4768
|
-
import BaseLayout from './BaseLayout.astro'
|
|
4769
|
-
import { format } from 'date-fns'
|
|
4770
|
-
|
|
4771
|
-
interface Props {
|
|
4772
|
-
frontmatter: {
|
|
4773
|
-
title: string
|
|
4774
|
-
description: string
|
|
4775
|
-
pubDate: Date
|
|
4776
|
-
author?: string
|
|
4777
|
-
}
|
|
4778
|
-
}
|
|
4779
|
-
|
|
4780
|
-
const { frontmatter } = Astro.props
|
|
4781
|
-
---
|
|
4782
|
-
|
|
4783
|
-
<BaseLayout title={frontmatter.title} description={frontmatter.description}>
|
|
4784
|
-
<article class="container mx-auto px-4 py-16 max-w-3xl">
|
|
4785
|
-
<header class="mb-8">
|
|
4786
|
-
<h1 class="text-4xl font-bold mb-4">{frontmatter.title}</h1>
|
|
4787
|
-
<div class="text-muted-foreground">
|
|
4788
|
-
<time datetime={frontmatter.pubDate.toISOString()}>
|
|
4789
|
-
{format(frontmatter.pubDate, 'MMMM d, yyyy')}
|
|
4790
|
-
</time>
|
|
4791
|
-
{frontmatter.author && <span> \xB7 {frontmatter.author}</span>}
|
|
4792
|
-
</div>
|
|
4793
|
-
</header>
|
|
4794
|
-
<div class="prose prose-lg dark:prose-invert max-w-none">
|
|
4795
|
-
<slot />
|
|
4796
|
-
</div>
|
|
4797
|
-
</article>
|
|
4798
|
-
</BaseLayout>
|
|
4799
|
-
`;
|
|
4800
|
-
await writeFile(
|
|
4801
|
-
path17.join(marketingDir, "src/layouts/BlogLayout.astro"),
|
|
4802
|
-
blogLayoutContent
|
|
4803
|
-
);
|
|
4804
|
-
}
|
|
4805
|
-
async function generateAstroPages(config, marketingDir) {
|
|
4806
|
-
const homePageContent = `---
|
|
4807
|
-
import BaseLayout from '../layouts/BaseLayout.astro'
|
|
4808
|
-
import Hero from '../components/Hero.astro'
|
|
4809
|
-
import Features from '../components/Features.astro'
|
|
4810
|
-
import CTA from '../components/CTA.astro'
|
|
4811
|
-
---
|
|
4812
|
-
|
|
4813
|
-
<BaseLayout title="${config.name} - Home">
|
|
4814
|
-
<main>
|
|
4815
|
-
<Hero />
|
|
4816
|
-
<Features />
|
|
4817
|
-
<CTA />
|
|
4818
|
-
</main>
|
|
4819
|
-
</BaseLayout>
|
|
4820
|
-
`;
|
|
4821
|
-
await writeFile(
|
|
4822
|
-
path17.join(marketingDir, "src/pages/index.astro"),
|
|
4823
|
-
homePageContent
|
|
4824
|
-
);
|
|
4825
|
-
const blogIndexContent = `---
|
|
4826
|
-
import BaseLayout from '../layouts/BaseLayout.astro'
|
|
4827
|
-
import { getCollection } from 'astro:content'
|
|
4828
|
-
import { format } from 'date-fns'
|
|
4829
|
-
|
|
4830
|
-
const posts = (await getCollection('blog')).sort(
|
|
4831
|
-
(a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf()
|
|
4832
|
-
)
|
|
4833
|
-
---
|
|
4834
|
-
|
|
4835
|
-
<BaseLayout title="Blog - ${config.name}">
|
|
4836
|
-
<main class="container mx-auto px-4 py-16">
|
|
4837
|
-
<h1 class="text-4xl font-bold mb-8">Blog</h1>
|
|
4838
|
-
<div class="grid gap-8">
|
|
4839
|
-
{
|
|
4840
|
-
posts.map((post) => (
|
|
4841
|
-
<article class="border rounded-lg p-6 hover:shadow-md transition-shadow">
|
|
4842
|
-
<a href={\`/blog/\${post.slug}\`}>
|
|
4843
|
-
<h2 class="text-2xl font-semibold mb-2">{post.data.title}</h2>
|
|
4844
|
-
<p class="text-muted-foreground mb-4">{post.data.description}</p>
|
|
4845
|
-
<time class="text-sm text-muted-foreground">
|
|
4846
|
-
{format(post.data.pubDate, 'MMMM d, yyyy')}
|
|
4847
|
-
</time>
|
|
4848
|
-
</a>
|
|
4849
|
-
</article>
|
|
4850
|
-
))
|
|
4851
|
-
}
|
|
4852
|
-
</div>
|
|
4853
|
-
</main>
|
|
4854
|
-
</BaseLayout>
|
|
4855
|
-
`;
|
|
4856
|
-
await writeFile(
|
|
4857
|
-
path17.join(marketingDir, "src/pages/blog/index.astro"),
|
|
4858
|
-
blogIndexContent
|
|
4859
|
-
);
|
|
4860
|
-
await ensureDir(path17.join(marketingDir, "src/pages/blog"));
|
|
4861
|
-
const blogPostContent = `---
|
|
4862
|
-
import { getCollection } from 'astro:content'
|
|
4863
|
-
import BlogLayout from '../../layouts/BlogLayout.astro'
|
|
4864
|
-
|
|
4865
|
-
export async function getStaticPaths() {
|
|
4866
|
-
const posts = await getCollection('blog')
|
|
4867
|
-
return posts.map((post) => ({
|
|
4868
|
-
params: { slug: post.slug },
|
|
4869
|
-
props: post,
|
|
4870
|
-
}))
|
|
4871
|
-
}
|
|
4872
|
-
|
|
4873
|
-
const post = Astro.props
|
|
4874
|
-
const { Content } = await post.render()
|
|
4875
|
-
---
|
|
4876
|
-
|
|
4877
|
-
<BlogLayout frontmatter={post.data}>
|
|
4878
|
-
<Content />
|
|
4879
|
-
</BlogLayout>
|
|
4880
|
-
`;
|
|
4881
|
-
await writeFile(
|
|
4882
|
-
path17.join(marketingDir, "src/pages/blog/[slug].astro"),
|
|
4883
|
-
blogPostContent
|
|
4884
|
-
);
|
|
4885
|
-
const notFoundContent = `---
|
|
4886
|
-
import BaseLayout from '../layouts/BaseLayout.astro'
|
|
4887
|
-
---
|
|
4888
|
-
|
|
4889
|
-
<BaseLayout title="404 - Page Not Found">
|
|
4890
|
-
<main class="container mx-auto px-4 py-16 text-center">
|
|
4891
|
-
<h1 class="text-6xl font-bold mb-4">404</h1>
|
|
4892
|
-
<p class="text-xl text-muted-foreground mb-8">Page not found</p>
|
|
4893
|
-
<a href="/" class="inline-block px-6 py-3 bg-primary text-primary-foreground rounded-lg">
|
|
4894
|
-
Go Home
|
|
4895
|
-
</a>
|
|
4896
|
-
</main>
|
|
4897
|
-
</BaseLayout>
|
|
4898
|
-
`;
|
|
4899
|
-
await writeFile(
|
|
4900
|
-
path17.join(marketingDir, "src/pages/404.astro"),
|
|
4901
|
-
notFoundContent
|
|
4902
|
-
);
|
|
4903
|
-
}
|
|
4904
|
-
async function generateAstroComponents(config, marketingDir) {
|
|
4905
|
-
const heroContent = `---
|
|
4906
|
-
|
|
4907
|
-
---
|
|
4908
|
-
|
|
4909
|
-
<section class="py-20 lg:py-32">
|
|
4910
|
-
<div class="container mx-auto px-4 text-center">
|
|
4911
|
-
<h1 class="text-5xl lg:text-7xl font-bold mb-6">
|
|
4912
|
-
Welcome to <span class="text-primary">${config.name}</span>
|
|
4913
|
-
</h1>
|
|
4914
|
-
<p class="text-xl lg:text-2xl text-muted-foreground mb-8 max-w-2xl mx-auto">
|
|
4915
|
-
Build your next SaaS application with our modern, full-stack starter template.
|
|
4916
|
-
</p>
|
|
4917
|
-
<div class="flex flex-col sm:flex-row gap-4 justify-center">
|
|
4918
|
-
<a
|
|
4919
|
-
href="/docs"
|
|
4920
|
-
class="px-8 py-3 bg-primary text-primary-foreground rounded-lg font-semibold hover:opacity-90 transition-opacity"
|
|
4921
|
-
>
|
|
4922
|
-
Get Started
|
|
4923
|
-
</a>
|
|
4924
|
-
<a
|
|
4925
|
-
href="https://github.com"
|
|
4926
|
-
class="px-8 py-3 border rounded-lg font-semibold hover:bg-accent transition-colors"
|
|
4927
|
-
>
|
|
4928
|
-
View on GitHub
|
|
4929
|
-
</a>
|
|
4930
|
-
</div>
|
|
4931
|
-
</div>
|
|
4932
|
-
</section>
|
|
4933
|
-
`;
|
|
4934
|
-
await writeFile(
|
|
4935
|
-
path17.join(marketingDir, "src/components/Hero.astro"),
|
|
4936
|
-
heroContent
|
|
4937
|
-
);
|
|
4938
|
-
const featuresContent = `---
|
|
4939
|
-
const features = [
|
|
4940
|
-
{
|
|
4941
|
-
title: 'Next.js 15',
|
|
4942
|
-
description: 'Built on the latest Next.js with App Router and React 19.',
|
|
4943
|
-
},
|
|
4944
|
-
{
|
|
4945
|
-
title: 'Convex Database',
|
|
4946
|
-
description: 'Real-time database with automatic syncing and TypeScript support.',
|
|
4947
|
-
},
|
|
4948
|
-
{
|
|
4949
|
-
title: 'Better-Auth',
|
|
4950
|
-
description: 'Flexible authentication with email, OAuth, and more.',
|
|
4951
|
-
},
|
|
4952
|
-
{
|
|
4953
|
-
title: 'shadcn/ui',
|
|
4954
|
-
description: 'Beautiful, accessible components built with Radix UI and Tailwind.',
|
|
4955
|
-
},
|
|
4956
|
-
{
|
|
4957
|
-
title: 'Tailwind CSS v4',
|
|
4958
|
-
description: 'CSS-first configuration with modern theming capabilities.',
|
|
4959
|
-
},
|
|
4960
|
-
{
|
|
4961
|
-
title: 'TypeScript',
|
|
4962
|
-
description: 'Full type safety from database to frontend.',
|
|
4963
|
-
},
|
|
4964
|
-
]
|
|
4965
|
-
---
|
|
4966
|
-
|
|
4967
|
-
<section class="py-20 bg-muted/50">
|
|
4968
|
-
<div class="container mx-auto px-4">
|
|
4969
|
-
<h2 class="text-3xl lg:text-4xl font-bold text-center mb-12">Features</h2>
|
|
4970
|
-
<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
|
|
4971
|
-
{
|
|
4972
|
-
features.map((feature) => (
|
|
4973
|
-
<div class="p-6 bg-background rounded-lg border">
|
|
4974
|
-
<h3 class="text-xl font-semibold mb-2">{feature.title}</h3>
|
|
4975
|
-
<p class="text-muted-foreground">{feature.description}</p>
|
|
4976
|
-
</div>
|
|
4977
|
-
))
|
|
4978
|
-
}
|
|
4979
|
-
</div>
|
|
4980
|
-
</div>
|
|
4981
|
-
</section>
|
|
4982
|
-
`;
|
|
4983
|
-
await writeFile(
|
|
4984
|
-
path17.join(marketingDir, "src/components/Features.astro"),
|
|
4985
|
-
featuresContent
|
|
4986
|
-
);
|
|
4987
|
-
const ctaContent = `---
|
|
4988
|
-
|
|
4989
|
-
---
|
|
4990
|
-
|
|
4991
|
-
<section class="py-20">
|
|
4992
|
-
<div class="container mx-auto px-4 text-center">
|
|
4993
|
-
<h2 class="text-3xl lg:text-4xl font-bold mb-4">Ready to get started?</h2>
|
|
4994
|
-
<p class="text-xl text-muted-foreground mb-8 max-w-xl mx-auto">
|
|
4995
|
-
Start building your SaaS application today with our fully-featured starter template.
|
|
4996
|
-
</p>
|
|
4997
|
-
<a
|
|
4998
|
-
href="https://app.example.com"
|
|
4999
|
-
class="inline-block px-8 py-3 bg-primary text-primary-foreground rounded-lg font-semibold hover:opacity-90 transition-opacity"
|
|
5000
|
-
>
|
|
5001
|
-
Launch App
|
|
5002
|
-
</a>
|
|
5003
|
-
</div>
|
|
5004
|
-
</section>
|
|
5005
|
-
`;
|
|
5006
|
-
await writeFile(
|
|
5007
|
-
path17.join(marketingDir, "src/components/CTA.astro"),
|
|
5008
|
-
ctaContent
|
|
5009
|
-
);
|
|
5010
|
-
const headerContent = `---
|
|
5011
|
-
const navItems = [
|
|
5012
|
-
{ label: 'Features', href: '/#features' },
|
|
5013
|
-
{ label: 'Blog', href: '/blog' },
|
|
5014
|
-
{ label: 'Docs', href: '/docs' },
|
|
5015
|
-
]
|
|
5016
|
-
---
|
|
5017
|
-
|
|
5018
|
-
<header class="sticky top-0 z-50 bg-background/80 backdrop-blur border-b">
|
|
5019
|
-
<div class="container mx-auto px-4">
|
|
5020
|
-
<nav class="flex items-center justify-between h-16">
|
|
5021
|
-
<a href="/" class="text-xl font-bold">${config.name}</a>
|
|
5022
|
-
<div class="hidden md:flex items-center gap-6">
|
|
5023
|
-
{
|
|
5024
|
-
navItems.map((item) => (
|
|
5025
|
-
<a href={item.href} class="text-muted-foreground hover:text-foreground transition-colors">
|
|
5026
|
-
{item.label}
|
|
5027
|
-
</a>
|
|
5028
|
-
))
|
|
5029
|
-
}
|
|
5030
|
-
<a
|
|
5031
|
-
href="https://app.example.com"
|
|
5032
|
-
class="px-4 py-2 bg-primary text-primary-foreground rounded-lg font-medium"
|
|
5033
|
-
>
|
|
5034
|
-
Launch App
|
|
5035
|
-
</a>
|
|
5036
|
-
</div>
|
|
5037
|
-
</nav>
|
|
5038
|
-
</div>
|
|
5039
|
-
</header>
|
|
5040
|
-
`;
|
|
5041
|
-
await writeFile(
|
|
5042
|
-
path17.join(marketingDir, "src/components/Header.astro"),
|
|
5043
|
-
headerContent
|
|
5044
|
-
);
|
|
5045
|
-
const footerContent = `---
|
|
5046
|
-
const currentYear = new Date().getFullYear()
|
|
5047
|
-
---
|
|
5048
|
-
|
|
5049
|
-
<footer class="py-12 border-t">
|
|
5050
|
-
<div class="container mx-auto px-4">
|
|
5051
|
-
<div class="flex flex-col md:flex-row justify-between items-center gap-4">
|
|
5052
|
-
<p class="text-muted-foreground">
|
|
5053
|
-
© {currentYear} ${config.name}. All rights reserved.
|
|
5054
|
-
</p>
|
|
5055
|
-
<div class="flex gap-6">
|
|
5056
|
-
<a href="/privacy" class="text-muted-foreground hover:text-foreground transition-colors">
|
|
5057
|
-
Privacy
|
|
5058
|
-
</a>
|
|
5059
|
-
<a href="/terms" class="text-muted-foreground hover:text-foreground transition-colors">
|
|
5060
|
-
Terms
|
|
5061
|
-
</a>
|
|
5062
|
-
</div>
|
|
5063
|
-
</div>
|
|
5064
|
-
</div>
|
|
5065
|
-
</footer>
|
|
5066
|
-
`;
|
|
5067
|
-
await writeFile(
|
|
5068
|
-
path17.join(marketingDir, "src/components/Footer.astro"),
|
|
5069
|
-
footerContent
|
|
5070
|
-
);
|
|
5071
|
-
}
|
|
5072
|
-
async function generateAstroStyles(marketingDir) {
|
|
5073
|
-
const globalStyles = `@import "tailwindcss";
|
|
5074
|
-
|
|
5075
|
-
:root {
|
|
5076
|
-
--background: 0 0% 100%;
|
|
5077
|
-
--foreground: 0 0% 3.9%;
|
|
5078
|
-
--muted: 0 0% 96.1%;
|
|
5079
|
-
--muted-foreground: 0 0% 45.1%;
|
|
5080
|
-
--primary: 0 0% 9%;
|
|
5081
|
-
--primary-foreground: 0 0% 98%;
|
|
5082
|
-
--accent: 0 0% 96.1%;
|
|
5083
|
-
--accent-foreground: 0 0% 9%;
|
|
5084
|
-
--border: 0 0% 89.8%;
|
|
5085
|
-
}
|
|
5086
|
-
|
|
5087
|
-
.dark {
|
|
5088
|
-
--background: 0 0% 3.9%;
|
|
5089
|
-
--foreground: 0 0% 98%;
|
|
5090
|
-
--muted: 0 0% 14.9%;
|
|
5091
|
-
--muted-foreground: 0 0% 63.9%;
|
|
5092
|
-
--primary: 0 0% 98%;
|
|
5093
|
-
--primary-foreground: 0 0% 9%;
|
|
5094
|
-
--accent: 0 0% 14.9%;
|
|
5095
|
-
--accent-foreground: 0 0% 98%;
|
|
5096
|
-
--border: 0 0% 14.9%;
|
|
5097
|
-
}
|
|
5098
|
-
|
|
5099
|
-
@theme {
|
|
5100
|
-
--color-background: hsl(var(--background));
|
|
5101
|
-
--color-foreground: hsl(var(--foreground));
|
|
5102
|
-
--color-muted: hsl(var(--muted));
|
|
5103
|
-
--color-muted-foreground: hsl(var(--muted-foreground));
|
|
5104
|
-
--color-primary: hsl(var(--primary));
|
|
5105
|
-
--color-primary-foreground: hsl(var(--primary-foreground));
|
|
5106
|
-
--color-accent: hsl(var(--accent));
|
|
5107
|
-
--color-accent-foreground: hsl(var(--accent-foreground));
|
|
5108
|
-
--color-border: hsl(var(--border));
|
|
5109
|
-
}
|
|
5110
|
-
|
|
5111
|
-
@layer base {
|
|
5112
|
-
body {
|
|
5113
|
-
@apply bg-background text-foreground;
|
|
5114
|
-
}
|
|
5115
|
-
}
|
|
5116
|
-
`;
|
|
5117
|
-
await writeFile(
|
|
5118
|
-
path17.join(marketingDir, "src/styles/global.css"),
|
|
5119
|
-
globalStyles
|
|
5120
|
-
);
|
|
5121
|
-
}
|
|
5122
|
-
async function generateAstroContent(config, marketingDir) {
|
|
5123
|
-
const contentConfigContent = `import { defineCollection, z } from 'astro:content'
|
|
5124
|
-
|
|
5125
|
-
const blog = defineCollection({
|
|
5126
|
-
type: 'content',
|
|
5127
|
-
schema: z.object({
|
|
5128
|
-
title: z.string(),
|
|
5129
|
-
description: z.string(),
|
|
5130
|
-
pubDate: z.coerce.date(),
|
|
5131
|
-
updatedDate: z.coerce.date().optional(),
|
|
5132
|
-
author: z.string().optional(),
|
|
5133
|
-
heroImage: z.string().optional(),
|
|
5134
|
-
}),
|
|
5135
|
-
})
|
|
5136
|
-
|
|
5137
|
-
export const collections = { blog }
|
|
5138
|
-
`;
|
|
5139
|
-
await writeFile(
|
|
5140
|
-
path17.join(marketingDir, "src/content/config.ts"),
|
|
5141
|
-
contentConfigContent
|
|
5142
|
-
);
|
|
5143
|
-
const samplePostContent = `---
|
|
5144
|
-
title: 'Welcome to ${config.name}'
|
|
5145
|
-
description: 'Learn about the features and capabilities of ${config.name}.'
|
|
5146
|
-
pubDate: '${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}'
|
|
5147
|
-
author: 'The Team'
|
|
5148
|
-
---
|
|
5149
|
-
|
|
5150
|
-
# Welcome to ${config.name}
|
|
5151
|
-
|
|
5152
|
-
This is a sample blog post to help you get started with your marketing site.
|
|
5153
|
-
|
|
5154
|
-
## Features
|
|
5155
|
-
|
|
5156
|
-
Our platform includes:
|
|
5157
|
-
|
|
5158
|
-
- **Modern Stack**: Built with Next.js 15 and React 19
|
|
5159
|
-
- **Real-time Database**: Powered by Convex
|
|
5160
|
-
- **Authentication**: Secure auth with Better-Auth
|
|
5161
|
-
- **Beautiful UI**: Components from shadcn/ui
|
|
5162
|
-
|
|
5163
|
-
## Getting Started
|
|
5164
|
-
|
|
5165
|
-
To get started, check out our [documentation](/docs) or [launch the app](https://app.example.com).
|
|
5166
|
-
|
|
5167
|
-
## Stay Updated
|
|
5168
|
-
|
|
5169
|
-
Follow our blog for updates on new features and best practices.
|
|
5170
|
-
`;
|
|
5171
|
-
await writeFile(
|
|
5172
|
-
path17.join(marketingDir, "src/content/blog/welcome.md"),
|
|
5173
|
-
samplePostContent
|
|
5174
|
-
);
|
|
5175
|
-
}
|
|
5176
|
-
|
|
5177
4641
|
// src/generators/design-system.ts
|
|
5178
|
-
import
|
|
4642
|
+
import path17 from "path";
|
|
5179
4643
|
var fontImports = {
|
|
5180
4644
|
inter: {
|
|
5181
4645
|
import: "import { Inter } from 'next/font/google'",
|
|
@@ -5243,16 +4707,16 @@ var spacingMultipliers = {
|
|
|
5243
4707
|
relaxed: 1.125
|
|
5244
4708
|
};
|
|
5245
4709
|
async function generateDesignSystemApp(config, targetDir) {
|
|
5246
|
-
const appDir =
|
|
5247
|
-
await ensureDir(
|
|
5248
|
-
await ensureDir(
|
|
5249
|
-
await ensureDir(
|
|
5250
|
-
await ensureDir(
|
|
5251
|
-
await ensureDir(
|
|
5252
|
-
await ensureDir(
|
|
5253
|
-
await ensureDir(
|
|
5254
|
-
await ensureDir(
|
|
5255
|
-
await ensureDir(
|
|
4710
|
+
const appDir = path17.join(targetDir, "apps/design-system");
|
|
4711
|
+
await ensureDir(path17.join(appDir, "src/app"));
|
|
4712
|
+
await ensureDir(path17.join(appDir, "src/app/colors"));
|
|
4713
|
+
await ensureDir(path17.join(appDir, "src/app/typography"));
|
|
4714
|
+
await ensureDir(path17.join(appDir, "src/app/spacing"));
|
|
4715
|
+
await ensureDir(path17.join(appDir, "src/app/brand"));
|
|
4716
|
+
await ensureDir(path17.join(appDir, "src/app/blocks"));
|
|
4717
|
+
await ensureDir(path17.join(appDir, "src/app/components"));
|
|
4718
|
+
await ensureDir(path17.join(appDir, "src/lib"));
|
|
4719
|
+
await ensureDir(path17.join(appDir, "public/brand"));
|
|
5256
4720
|
await generatePackageJson2(config, appDir);
|
|
5257
4721
|
await generateTsConfig2(appDir);
|
|
5258
4722
|
await generateNextConfig2(appDir);
|
|
@@ -5304,7 +4768,7 @@ async function generatePackageJson2(config, appDir) {
|
|
|
5304
4768
|
typescript: "^5.0.0"
|
|
5305
4769
|
}
|
|
5306
4770
|
};
|
|
5307
|
-
await writeJSON(
|
|
4771
|
+
await writeJSON(path17.join(appDir, "package.json"), packageJson);
|
|
5308
4772
|
}
|
|
5309
4773
|
async function generateTsConfig2(appDir) {
|
|
5310
4774
|
const tsConfig = {
|
|
@@ -5320,7 +4784,7 @@ async function generateTsConfig2(appDir) {
|
|
|
5320
4784
|
include: ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
5321
4785
|
exclude: ["node_modules"]
|
|
5322
4786
|
};
|
|
5323
|
-
await writeJSON(
|
|
4787
|
+
await writeJSON(path17.join(appDir, "tsconfig.json"), tsConfig);
|
|
5324
4788
|
}
|
|
5325
4789
|
async function generateNextConfig2(appDir) {
|
|
5326
4790
|
const content = `import type { NextConfig } from 'next'
|
|
@@ -5331,7 +4795,7 @@ const nextConfig: NextConfig = {
|
|
|
5331
4795
|
|
|
5332
4796
|
export default nextConfig
|
|
5333
4797
|
`;
|
|
5334
|
-
await writeFile(
|
|
4798
|
+
await writeFile(path17.join(appDir, "next.config.ts"), content);
|
|
5335
4799
|
}
|
|
5336
4800
|
async function generatePostCssConfig2(appDir) {
|
|
5337
4801
|
const content = `export default {
|
|
@@ -5340,7 +4804,7 @@ async function generatePostCssConfig2(appDir) {
|
|
|
5340
4804
|
},
|
|
5341
4805
|
}
|
|
5342
4806
|
`;
|
|
5343
|
-
await writeFile(
|
|
4807
|
+
await writeFile(path17.join(appDir, "postcss.config.mjs"), content);
|
|
5344
4808
|
}
|
|
5345
4809
|
async function generateGlobalsCss2(config, appDir) {
|
|
5346
4810
|
const { designSystem } = config;
|
|
@@ -5600,7 +5064,7 @@ h1, h2, h3, h4, h5, h6 {
|
|
|
5600
5064
|
}
|
|
5601
5065
|
}
|
|
5602
5066
|
`;
|
|
5603
|
-
await writeFile(
|
|
5067
|
+
await writeFile(path17.join(appDir, "src/app/globals.css"), content);
|
|
5604
5068
|
}
|
|
5605
5069
|
async function generateLayoutTsx(config, appDir) {
|
|
5606
5070
|
const { designSystem } = config;
|
|
@@ -5799,7 +5263,7 @@ function ThemeToggle() {
|
|
|
5799
5263
|
)
|
|
5800
5264
|
}
|
|
5801
5265
|
`;
|
|
5802
|
-
await writeFile(
|
|
5266
|
+
await writeFile(path17.join(appDir, "src/app/layout.tsx"), content);
|
|
5803
5267
|
}
|
|
5804
5268
|
async function generateHomePage(config, appDir) {
|
|
5805
5269
|
const content = `export default function DesignSystemHome() {
|
|
@@ -5965,7 +5429,7 @@ function Principle({
|
|
|
5965
5429
|
)
|
|
5966
5430
|
}
|
|
5967
5431
|
`;
|
|
5968
|
-
await writeFile(
|
|
5432
|
+
await writeFile(path17.join(appDir, "src/app/page.tsx"), content);
|
|
5969
5433
|
}
|
|
5970
5434
|
async function generateColorsPage(config, appDir) {
|
|
5971
5435
|
const content = `export default function ColorsPage() {
|
|
@@ -6134,7 +5598,7 @@ function ColorCard({
|
|
|
6134
5598
|
)
|
|
6135
5599
|
}
|
|
6136
5600
|
`;
|
|
6137
|
-
await writeFile(
|
|
5601
|
+
await writeFile(path17.join(appDir, "src/app/colors/page.tsx"), content);
|
|
6138
5602
|
}
|
|
6139
5603
|
async function generateTypographyPage(config, appDir) {
|
|
6140
5604
|
const { designSystem } = config;
|
|
@@ -6291,7 +5755,7 @@ function WeightCard({ weight, name }: { weight: string; name: string }) {
|
|
|
6291
5755
|
)
|
|
6292
5756
|
}
|
|
6293
5757
|
`;
|
|
6294
|
-
await writeFile(
|
|
5758
|
+
await writeFile(path17.join(appDir, "src/app/typography/page.tsx"), content);
|
|
6295
5759
|
}
|
|
6296
5760
|
async function generateSpacingPage(config, appDir) {
|
|
6297
5761
|
const { designSystem } = config;
|
|
@@ -6424,7 +5888,7 @@ function PatternCard({
|
|
|
6424
5888
|
)
|
|
6425
5889
|
}
|
|
6426
5890
|
`;
|
|
6427
|
-
await writeFile(
|
|
5891
|
+
await writeFile(path17.join(appDir, "src/app/spacing/page.tsx"), content);
|
|
6428
5892
|
}
|
|
6429
5893
|
async function generateBrandPage(config, appDir) {
|
|
6430
5894
|
const content = `export default function BrandPage() {
|
|
@@ -6562,7 +6026,7 @@ function DownloadCard({
|
|
|
6562
6026
|
)
|
|
6563
6027
|
}
|
|
6564
6028
|
`;
|
|
6565
|
-
await writeFile(
|
|
6029
|
+
await writeFile(path17.join(appDir, "src/app/brand/page.tsx"), content);
|
|
6566
6030
|
}
|
|
6567
6031
|
async function generateBlocksPage(appDir) {
|
|
6568
6032
|
const content = `export default function BlocksPage() {
|
|
@@ -6624,7 +6088,7 @@ function BlockCard({ title, description }: { title: string; description: string
|
|
|
6624
6088
|
)
|
|
6625
6089
|
}
|
|
6626
6090
|
`;
|
|
6627
|
-
await writeFile(
|
|
6091
|
+
await writeFile(path17.join(appDir, "src/app/blocks/page.tsx"), content);
|
|
6628
6092
|
}
|
|
6629
6093
|
async function generateComponentsPage(appDir) {
|
|
6630
6094
|
const content = `export default function ComponentsPage() {
|
|
@@ -6704,7 +6168,7 @@ function CategoryCard({
|
|
|
6704
6168
|
)
|
|
6705
6169
|
}
|
|
6706
6170
|
`;
|
|
6707
|
-
await writeFile(
|
|
6171
|
+
await writeFile(path17.join(appDir, "src/app/components/page.tsx"), content);
|
|
6708
6172
|
}
|
|
6709
6173
|
async function generateUtils(appDir) {
|
|
6710
6174
|
const content = `import { clsx, type ClassValue } from 'clsx'
|
|
@@ -6714,7 +6178,7 @@ export function cn(...inputs: ClassValue[]) {
|
|
|
6714
6178
|
return twMerge(clsx(inputs))
|
|
6715
6179
|
}
|
|
6716
6180
|
`;
|
|
6717
|
-
await writeFile(
|
|
6181
|
+
await writeFile(path17.join(appDir, "src/lib/utils.ts"), content);
|
|
6718
6182
|
}
|
|
6719
6183
|
|
|
6720
6184
|
// src/setup/env-wizard.ts
|
|
@@ -6722,7 +6186,7 @@ import * as p6 from "@clack/prompts";
|
|
|
6722
6186
|
import pc2 from "picocolors";
|
|
6723
6187
|
import { exec } from "child_process";
|
|
6724
6188
|
import { promisify } from "util";
|
|
6725
|
-
import
|
|
6189
|
+
import path18 from "path";
|
|
6726
6190
|
var execAsync = promisify(exec);
|
|
6727
6191
|
async function runEnvSetupWizard(config) {
|
|
6728
6192
|
console.log();
|
|
@@ -6797,15 +6261,40 @@ async function setupConvex(config, envValues) {
|
|
|
6797
6261
|
}
|
|
6798
6262
|
if (convexChoice === "new") {
|
|
6799
6263
|
p6.log.info(`
|
|
6800
|
-
${pc2.bold("
|
|
6801
|
-
|
|
6802
|
-
1. Run ${pc2.cyan("pnpm convex dev")} in your project directory
|
|
6803
|
-
2. It will open your browser to create a new project
|
|
6804
|
-
3. Follow the prompts to set up your Convex project
|
|
6805
|
-
4. The CLI will automatically add the deployment URL to .env.local
|
|
6264
|
+
${pc2.bold("Creating a new Convex project...")}
|
|
6806
6265
|
|
|
6266
|
+
This will open your browser to create a new project on Convex.
|
|
6807
6267
|
${pc2.dim("Note: Make sure you have a Convex account at https://convex.dev")}
|
|
6808
6268
|
`);
|
|
6269
|
+
try {
|
|
6270
|
+
const convexDir = config.structure === "monorepo" ? path18.join(config.targetDir, "packages/backend") : config.targetDir;
|
|
6271
|
+
p6.log.info(`Running ${pc2.cyan("npx convex dev --once")} in ${pc2.dim(convexDir)}...`);
|
|
6272
|
+
const { spawn } = await import("child_process");
|
|
6273
|
+
await new Promise((resolve) => {
|
|
6274
|
+
const child = spawn("npx", ["convex", "dev", "--once"], {
|
|
6275
|
+
cwd: convexDir,
|
|
6276
|
+
stdio: "inherit",
|
|
6277
|
+
shell: true
|
|
6278
|
+
});
|
|
6279
|
+
child.on("close", (code) => {
|
|
6280
|
+
if (code === 0) {
|
|
6281
|
+
p6.log.success("Convex project created successfully!");
|
|
6282
|
+
resolve();
|
|
6283
|
+
} else {
|
|
6284
|
+
p6.log.warn(`Convex setup exited with code ${code}. You can run 'pnpm convex dev' manually later.`);
|
|
6285
|
+
resolve();
|
|
6286
|
+
}
|
|
6287
|
+
});
|
|
6288
|
+
child.on("error", (error) => {
|
|
6289
|
+
p6.log.warn(`Could not run Convex setup: ${error.message}`);
|
|
6290
|
+
p6.log.info(`Run ${pc2.cyan("pnpm convex dev")} manually in your project directory.`);
|
|
6291
|
+
resolve();
|
|
6292
|
+
});
|
|
6293
|
+
});
|
|
6294
|
+
} catch (error) {
|
|
6295
|
+
p6.log.warn("Could not run Convex setup automatically.");
|
|
6296
|
+
p6.log.info(`Run ${pc2.cyan("pnpm convex dev")} manually in your project directory.`);
|
|
6297
|
+
}
|
|
6809
6298
|
} else if (convexChoice === "existing") {
|
|
6810
6299
|
const deploymentName = await p6.text({
|
|
6811
6300
|
message: "Enter your Convex deployment name (e.g., my-app-123):",
|
|
@@ -7029,7 +6518,7 @@ async function writeEnvFiles(config, envValues) {
|
|
|
7029
6518
|
return;
|
|
7030
6519
|
}
|
|
7031
6520
|
p6.log.step("Writing environment files...");
|
|
7032
|
-
const webEnvPath = config.structure === "monorepo" ?
|
|
6521
|
+
const webEnvPath = config.structure === "monorepo" ? path18.join(config.targetDir, "apps/web/.env.local") : path18.join(config.targetDir, ".env.local");
|
|
7033
6522
|
if (await pathExists(webEnvPath)) {
|
|
7034
6523
|
let content = await readFile(webEnvPath);
|
|
7035
6524
|
for (const [key, value] of Object.entries(envValues)) {
|
|
@@ -7048,7 +6537,7 @@ ${key}="${value}"`;
|
|
|
7048
6537
|
p6.log.success(`Updated ${config.structure === "monorepo" ? "apps/web/.env.local" : ".env.local"}`);
|
|
7049
6538
|
}
|
|
7050
6539
|
if (config.marketingSite === "payload" && config.structure === "monorepo") {
|
|
7051
|
-
const marketingEnvPath =
|
|
6540
|
+
const marketingEnvPath = path18.join(config.targetDir, "apps/marketing/.env.local");
|
|
7052
6541
|
if (await pathExists(marketingEnvPath)) {
|
|
7053
6542
|
let content = await readFile(marketingEnvPath);
|
|
7054
6543
|
const payloadEnvKeys = ["PAYLOAD_SECRET", "DATABASE_URL", "S3_ENDPOINT", "S3_REGION", "S3_BUCKET", "S3_ACCESS_KEY_ID", "S3_SECRET_ACCESS_KEY", "CRON_SECRET", "PREVIEW_SECRET"];
|
|
@@ -7109,7 +6598,7 @@ async function generateSingleProject(config, spinner) {
|
|
|
7109
6598
|
await generateShadcn(config, targetDir);
|
|
7110
6599
|
spinner.succeed("shadcn/ui configured");
|
|
7111
6600
|
spinner.start("Setting up Convex...");
|
|
7112
|
-
await generateConvex(config,
|
|
6601
|
+
await generateConvex(config, path19.join(targetDir, "convex"));
|
|
7113
6602
|
spinner.succeed("Convex configured");
|
|
7114
6603
|
spinner.start("Configuring Better-Auth...");
|
|
7115
6604
|
await generateBetterAuth(config, targetDir);
|
|
@@ -7152,7 +6641,7 @@ async function generateMonorepoProject(config, spinner) {
|
|
|
7152
6641
|
spinner.start("Creating shared UI package...");
|
|
7153
6642
|
await generateUIPackage(config, targetDir);
|
|
7154
6643
|
spinner.succeed("Shared UI package created");
|
|
7155
|
-
const webDir =
|
|
6644
|
+
const webDir = path19.join(targetDir, "apps/web");
|
|
7156
6645
|
spinner.start("Generating web application...");
|
|
7157
6646
|
await generateBaseNextjs(config, webDir);
|
|
7158
6647
|
await generateTailwind(config, webDir);
|
|
@@ -7162,10 +6651,10 @@ async function generateMonorepoProject(config, spinner) {
|
|
|
7162
6651
|
await generateEmail(config, webDir);
|
|
7163
6652
|
await updateWebTsConfig(webDir);
|
|
7164
6653
|
spinner.succeed("Web application generated");
|
|
7165
|
-
const backendDir =
|
|
6654
|
+
const backendDir = path19.join(targetDir, "packages/backend");
|
|
7166
6655
|
spinner.start("Setting up Convex backend...");
|
|
7167
6656
|
await ensureDir(backendDir);
|
|
7168
|
-
await generateConvex(config,
|
|
6657
|
+
await generateConvex(config, path19.join(backendDir, "convex"));
|
|
7169
6658
|
spinner.succeed("Convex backend configured");
|
|
7170
6659
|
if (config.integrations.analytics !== "none") {
|
|
7171
6660
|
spinner.start(`Setting up ${config.integrations.analytics} analytics...`);
|
|
@@ -7188,19 +6677,17 @@ async function generateMonorepoProject(config, spinner) {
|
|
|
7188
6677
|
spinner.succeed(`${config.integrations.monitoring} monitoring configured`);
|
|
7189
6678
|
}
|
|
7190
6679
|
if (config.marketingSite !== "none") {
|
|
7191
|
-
const marketingDir =
|
|
6680
|
+
const marketingDir = path19.join(targetDir, "apps/marketing");
|
|
7192
6681
|
spinner.start(`Generating ${config.marketingSite} marketing site...`);
|
|
7193
6682
|
if (config.marketingSite === "payload") {
|
|
7194
6683
|
await generatePayload(config, marketingDir);
|
|
7195
|
-
} else if (config.marketingSite === "astro") {
|
|
7196
|
-
await generateAstro(config, marketingDir);
|
|
7197
6684
|
} else if (config.marketingSite === "nextjs") {
|
|
7198
6685
|
await generateNextjsMarketing(config, marketingDir);
|
|
7199
6686
|
}
|
|
7200
6687
|
spinner.succeed(`${config.marketingSite} marketing site generated`);
|
|
7201
6688
|
}
|
|
7202
6689
|
if (config.includeDocs) {
|
|
7203
|
-
const docsDir =
|
|
6690
|
+
const docsDir = path19.join(targetDir, "apps/docs");
|
|
7204
6691
|
spinner.start("Generating Fumadocs documentation site...");
|
|
7205
6692
|
await generateFumadocs(config, docsDir);
|
|
7206
6693
|
spinner.succeed("Fumadocs documentation site generated");
|
|
@@ -7221,7 +6708,7 @@ async function runPostGenerationSteps(config, spinner) {
|
|
|
7221
6708
|
}
|
|
7222
6709
|
spinner.start("Installing shadcn/ui components...");
|
|
7223
6710
|
try {
|
|
7224
|
-
const shadcnDir = config.structure === "monorepo" ?
|
|
6711
|
+
const shadcnDir = config.structure === "monorepo" ? path19.join(targetDir, "packages/ui") : targetDir;
|
|
7225
6712
|
await runShadcnAdd(shadcnDir);
|
|
7226
6713
|
spinner.succeed("shadcn/ui components installed");
|
|
7227
6714
|
} catch (error) {
|
|
@@ -7281,7 +6768,7 @@ async function generateBiomeConfig(targetDir) {
|
|
|
7281
6768
|
}
|
|
7282
6769
|
};
|
|
7283
6770
|
await writeFile(
|
|
7284
|
-
|
|
6771
|
+
path19.join(targetDir, "biome.json"),
|
|
7285
6772
|
JSON.stringify(biomeJson, null, 2)
|
|
7286
6773
|
);
|
|
7287
6774
|
}
|
|
@@ -7332,13 +6819,13 @@ yarn-error.log*
|
|
|
7332
6819
|
*.pem
|
|
7333
6820
|
.cache
|
|
7334
6821
|
`;
|
|
7335
|
-
await writeFile(
|
|
6822
|
+
await writeFile(path19.join(targetDir, ".gitignore"), gitignoreContent);
|
|
7336
6823
|
}
|
|
7337
6824
|
async function generateHuskyHooks(targetDir) {
|
|
7338
6825
|
const preCommitContent = `pnpm lint-staged
|
|
7339
6826
|
`;
|
|
7340
|
-
await ensureDir(
|
|
7341
|
-
await writeFile(
|
|
6827
|
+
await ensureDir(path19.join(targetDir, ".husky"));
|
|
6828
|
+
await writeFile(path19.join(targetDir, ".husky/pre-commit"), preCommitContent);
|
|
7342
6829
|
}
|
|
7343
6830
|
async function updateWebTsConfig(webDir) {
|
|
7344
6831
|
const tsConfig = {
|
|
@@ -7352,14 +6839,14 @@ async function updateWebTsConfig(webDir) {
|
|
|
7352
6839
|
exclude: ["node_modules"]
|
|
7353
6840
|
};
|
|
7354
6841
|
await writeFile(
|
|
7355
|
-
|
|
6842
|
+
path19.join(webDir, "tsconfig.json"),
|
|
7356
6843
|
JSON.stringify(tsConfig, null, 2)
|
|
7357
6844
|
);
|
|
7358
6845
|
}
|
|
7359
6846
|
async function generateNextjsMarketing(config, marketingDir) {
|
|
7360
|
-
await ensureDir(
|
|
7361
|
-
await ensureDir(
|
|
7362
|
-
await ensureDir(
|
|
6847
|
+
await ensureDir(path19.join(marketingDir, "src/app"));
|
|
6848
|
+
await ensureDir(path19.join(marketingDir, "src/components"));
|
|
6849
|
+
await ensureDir(path19.join(marketingDir, "public"));
|
|
7363
6850
|
const packageJson = {
|
|
7364
6851
|
name: "@repo/marketing",
|
|
7365
6852
|
version: "0.1.0",
|
|
@@ -7390,7 +6877,7 @@ async function generateNextjsMarketing(config, marketingDir) {
|
|
|
7390
6877
|
}
|
|
7391
6878
|
};
|
|
7392
6879
|
await writeFile(
|
|
7393
|
-
|
|
6880
|
+
path19.join(marketingDir, "package.json"),
|
|
7394
6881
|
JSON.stringify(packageJson, null, 2)
|
|
7395
6882
|
);
|
|
7396
6883
|
const homePageContent = `export default function HomePage() {
|
|
@@ -7413,7 +6900,7 @@ async function generateNextjsMarketing(config, marketingDir) {
|
|
|
7413
6900
|
}
|
|
7414
6901
|
`;
|
|
7415
6902
|
await writeFile(
|
|
7416
|
-
|
|
6903
|
+
path19.join(marketingDir, "src/app/page.tsx"),
|
|
7417
6904
|
homePageContent
|
|
7418
6905
|
);
|
|
7419
6906
|
const layoutContent = `import type { Metadata } from 'next'
|
|
@@ -7437,7 +6924,7 @@ export default function RootLayout({
|
|
|
7437
6924
|
}
|
|
7438
6925
|
`;
|
|
7439
6926
|
await writeFile(
|
|
7440
|
-
|
|
6927
|
+
path19.join(marketingDir, "src/app/layout.tsx"),
|
|
7441
6928
|
layoutContent
|
|
7442
6929
|
);
|
|
7443
6930
|
const globalsCss = `@import "tailwindcss";
|
|
@@ -7449,7 +6936,7 @@ export default function RootLayout({
|
|
|
7449
6936
|
}
|
|
7450
6937
|
`;
|
|
7451
6938
|
await writeFile(
|
|
7452
|
-
|
|
6939
|
+
path19.join(marketingDir, "src/app/globals.css"),
|
|
7453
6940
|
globalsCss
|
|
7454
6941
|
);
|
|
7455
6942
|
const tsConfig = {
|
|
@@ -7463,7 +6950,7 @@ export default function RootLayout({
|
|
|
7463
6950
|
exclude: ["node_modules"]
|
|
7464
6951
|
};
|
|
7465
6952
|
await writeFile(
|
|
7466
|
-
|
|
6953
|
+
path19.join(marketingDir, "tsconfig.json"),
|
|
7467
6954
|
JSON.stringify(tsConfig, null, 2)
|
|
7468
6955
|
);
|
|
7469
6956
|
const nextConfig = `import type { NextConfig } from 'next'
|
|
@@ -7474,14 +6961,14 @@ const nextConfig: NextConfig = {
|
|
|
7474
6961
|
|
|
7475
6962
|
export default nextConfig
|
|
7476
6963
|
`;
|
|
7477
|
-
await writeFile(
|
|
6964
|
+
await writeFile(path19.join(marketingDir, "next.config.ts"), nextConfig);
|
|
7478
6965
|
const postcssConfig = `export default {
|
|
7479
6966
|
plugins: {
|
|
7480
6967
|
'@tailwindcss/postcss': {},
|
|
7481
6968
|
},
|
|
7482
6969
|
}
|
|
7483
6970
|
`;
|
|
7484
|
-
await writeFile(
|
|
6971
|
+
await writeFile(path19.join(marketingDir, "postcss.config.mjs"), postcssConfig);
|
|
7485
6972
|
}
|
|
7486
6973
|
function displaySuccessMessage(config) {
|
|
7487
6974
|
const apps = ["web"];
|
|
@@ -7551,7 +7038,7 @@ program.name("create-kofi-stack").description(
|
|
|
7551
7038
|
"Scaffold opinionated full-stack projects with Next.js, Convex, Better-Auth, and more"
|
|
7552
7039
|
).version("1.0.0").argument("[project-name]", "Name of the project").option("--monorepo", "Use monorepo structure with Turborepo").option("--single", "Use single app structure").option(
|
|
7553
7040
|
"--marketing <type>",
|
|
7554
|
-
"Marketing site type: payload, nextjs,
|
|
7041
|
+
"Marketing site type: payload, nextjs, none"
|
|
7555
7042
|
).option("--docs", "Include documentation site (Fumadocs)").option("--no-docs", "Exclude documentation site").option(
|
|
7556
7043
|
"--component-library <library>",
|
|
7557
7044
|
"Component library: base (Base UI), radix (Radix UI)"
|