@alfredmouelle/create-stack 0.1.0 → 0.1.1

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
@@ -1,56 +1,122 @@
1
1
  # create-stack
2
2
 
3
- Interactive, **deterministic** installer for the reference stack — the non-LLM
4
- counterpart of the `bootstrap` skill's CREATE mode. It forks a base app
5
- (`apps/tanstack-base` or `apps/next-base`) and strips it down to your selection.
3
+ > `@alfredmouelle/create-stack`
6
4
 
7
- ## Usage
5
+ Interactive, **deterministic** project installer. It forks a fully-wired base app
6
+ (**Next.js App Router** or **TanStack Start**) and strips it down to exactly the
7
+ foundations and provider you pick — Drizzle, tRPC, better-auth, data tables and a
8
+ mailer — then stamps identity, generates `.env`, initializes git and verifies the
9
+ result (typecheck + Biome).
10
+
11
+ No template guesswork: the output is a real, buildable app from day one.
12
+
13
+ ## Quick start
8
14
 
9
15
  ```bash
10
- # from anywhere
11
- node /path/to/stack/cli/index.mjs my-app
16
+ pnpm dlx @alfredmouelle/create-stack my-app
17
+ # or, using the create-* convention:
18
+ pnpm create @alfredmouelle/stack my-app
19
+ # npm / yarn:
20
+ npm create @alfredmouelle/stack my-app
21
+ yarn create @alfredmouelle/stack my-app
22
+ ```
23
+
24
+ Run with no extra flags → an **interactive wizard** asks every question. Pass any
25
+ selection flag → **non-interactive** mode (scriptable / CI).
26
+
27
+ ## Requirements
28
+
29
+ - **Node** ≥ 22
30
+ - **pnpm** (the generated project is a pnpm project)
31
+ - **git** and **rsync** available on `PATH` (macOS/Linux ship both)
32
+
33
+ ## Usage
12
34
 
13
- # from the stack repo
14
- pnpm create-stack my-app
15
35
  ```
36
+ create-stack [project] [flags]
37
+ ```
38
+
39
+ `project` is the target directory (and default package name). It must be empty or
40
+ not exist yet. In non-interactive mode it is required.
16
41
 
17
- The wizard asks for:
42
+ ### Flags
18
43
 
19
- - **Framework** TanStack Start or Next.js (App Router)
20
- - **Foundations** Drizzle, tRPC, better-auth, data-table (hard deps resolved:
21
- tRPC/better-auth Drizzle; better-auth mailer)
22
- - **Mailer provider** Resend, Brevo, Amazon SES (or none, if no better-auth)
23
- - **Extra capabilities** storage, jobs, cache, (added afterwards via the
24
- `add-capability` skill; not baked into the base)
44
+ | Flag | Values | Default | Description |
45
+ | --- | --- | --- | --- |
46
+ | `--framework` | `tanstack` \| `next` | `tanstack` | Base app to fork. |
47
+ | `--foundations` | csv of `drizzle,trpc,better-auth,data-table` | all | Foundations to keep; the rest are stripped. |
48
+ | `--mailer` | `resend` \| `brevo` \| `ses` \| `none` | `resend` | Mailer provider. `none` is rejected when `better-auth` is kept. |
49
+ | `--caps` | csv of capability names | | Extra capabilities to add afterwards via `add-capability` (see below). |
50
+ | `--no-install` | — | install on | Skip `pnpm install` + verification. |
51
+ | `--yes`, `-y` | — | — | Non-interactive with all defaults. |
25
52
 
26
- Then it forks, strips unselected foundations, swaps the mailer adapter, stamps
27
- identity, generates `.env(.example)`, and optionally installs + verifies
28
- (typecheck + Biome).
53
+ Passing any of `--framework`, `--foundations`, `--mailer`, `--caps` or
54
+ `--no-install` (or `--yes`) switches the CLI to non-interactive mode; missing
55
+ values fall back to the defaults above.
29
56
 
30
- ## How it works
57
+ ### Dependency resolution
31
58
 
32
- - **Single source of truth**: the base apps (`apps/*-base`) hold the real code;
33
- the `patterns/*/pattern.json` manifests drive deps/scripts/env/file lists.
34
- - **Deterministic strip**: whole-directory deletes + dep/env/script diffs from the
35
- manifests, plus a few hardcoded code *seams* (tRPC↔auth context, root provider
36
- wiring) handled via shipped reduced variants in `templates/`.
37
- - If you edit a seam file in a base app (`server/api/trpc.ts`, the root
38
- `router.tsx` / `__root.tsx` / `layout.tsx`, the schema barrel), update the
39
- matching transform in `lib/strip.mjs` or the `templates/` variant.
59
+ Selections are normalized for you:
40
60
 
41
- ## Layout
61
+ - `trpc` ⇒ pulls in `drizzle`
62
+ - `better-auth` ⇒ pulls in `drizzle` **and** forces a real mailer (not `none`)
63
+
64
+ ### Examples
65
+
66
+ ```bash
67
+ # Full interactive wizard
68
+ pnpm dlx @alfredmouelle/create-stack my-app
42
69
 
70
+ # Everything, defaults (TanStack Start + all foundations + Resend), no questions
71
+ pnpm dlx @alfredmouelle/create-stack my-app --yes
72
+
73
+ # Next.js with just Drizzle + tRPC, Amazon SES mailer, don't install
74
+ pnpm dlx @alfredmouelle/create-stack api --framework next \
75
+ --foundations drizzle,trpc --mailer ses --no-install
76
+
77
+ # Minimal: Drizzle only, no mailer
78
+ pnpm dlx @alfredmouelle/create-stack db-svc --foundations drizzle --mailer none
79
+
80
+ # TanStack + auth, and queue up extra capabilities for later
81
+ pnpm dlx @alfredmouelle/create-stack app --foundations drizzle,trpc,better-auth \
82
+ --caps storage,jobs
43
83
  ```
44
- cli/
45
- index.mjs wizard (prompts) + install/verify + report
46
- lib/
47
- build.mjs pure build phase (fork strip mailer env identity)
48
- manifests.mjs load patterns + capabilities; logical→manifest mapping
49
- scaffold.mjs fork base app, make it standalone
50
- strip.mjs reverse-strip unselected foundations + code seams
51
- mailer.mjs mailer provider swap
52
- env.mjs rebuild src/env.ts blocks + generate .env files
53
- identity.mjs title/meta + README with the # Author footer
54
- util.mjs fs / exec / package.json helpers
55
- templates/ reduced "no-trpc" root-wiring variants
84
+
85
+ ## What you get
86
+
87
+ - **Framework** Next.js App Router *or* TanStack Start, fully wired (SSR, routing).
88
+ - **Drizzle ORM** Postgres client, schema barrel, keyset pagination, seed harness.
89
+ - **tRPC v11** — typed API, SSR/RSC integration, health router.
90
+ - **better-auth** email+password + verification, optional Google OAuth, session
91
+ guards, auth UI pages.
92
+ - **Mailer** — Resend / Brevo / SES behind one port; React Email templates.
93
+ - **Data tables** TanStack Table primitives (DataTable, InfiniteDataTable, …).
94
+ - **Baseline** Tailwind v4 + shadcn, Geist, theme toggle, strict Biome, typed
95
+ env (`src/env.ts`), Dockerfile, a generated `.gitignore` and `.env`/`.env.example`.
96
+
97
+ Unselected foundations are removed cleanly (files, deps, env vars and wiring),
98
+ and the project is left **bootable and green** (typecheck + Biome).
99
+
100
+ ## Extra capabilities
101
+
102
+ `--caps` lists capabilities that aren't baked into the base (storage, jobs, cache,
103
+ logger, analytics, error-tracking, http). They are **not** scaffolded by this CLI;
104
+ the final report prints the follow-up to add them with the `add-capability` skill.
105
+ Mailer is the exception — it's built in and chosen via `--mailer`.
106
+
107
+ ## After scaffolding
108
+
109
+ ```bash
110
+ cd my-app
111
+ pnpm install # only if you passed --no-install
112
+ cp .env.example .env # fill in the values
113
+ pnpm dev
56
114
  ```
115
+
116
+ ## Notes
117
+
118
+ - The published package is **self-contained**: the base apps, pattern manifests
119
+ and mailer adapters are bundled at publish time, so `pnpm dlx` needs nothing but
120
+ this package.
121
+ - The generated project is a fresh git repo (`git init`, files staged) — make your
122
+ first commit when ready.
@@ -1,3 +1,5 @@
1
+ 'use client'
2
+
1
3
  import { Body, Container, Head, Html, Link, Preview, Section, Tailwind, Text } from 'react-email'
2
4
  import { EmailThemeProvider, useEmailTheme } from './context'
3
5
  import { createEmailTheme, type EmailTheme, type EmailThemeOverride } from './theme'
@@ -1,3 +1,5 @@
1
+ 'use client'
2
+
1
3
  import { createContext, useContext } from 'react'
2
4
  import { defaultTheme, type EmailTheme } from './theme'
3
5
 
@@ -2,8 +2,16 @@ import { createServerFn } from '@tanstack/react-start'
2
2
  import { getRequest } from '@tanstack/react-start/server'
3
3
  import { auth } from '.'
4
4
 
5
- /** Raw session lookup from the current request headers (server-side). */
6
- export const getSession = async () => auth.api.getSession({ headers: getRequest().headers })
7
-
8
- /** Server-function wrapper for route loaders / `beforeLoad`. */
9
- export const getServerSession = createServerFn({ method: 'GET' }).handler(() => getSession())
5
+ /**
6
+ * Server-function: resolves the better-auth session from the current request
7
+ * headers. Use it in route loaders / `beforeLoad`.
8
+ *
9
+ * The server-only `getRequest` call lives INSIDE the handler on purpose: the
10
+ * TanStack Start plugin extracts the handler server-side and strips this import
11
+ * from the client bundle. A standalone module-scope helper that calls
12
+ * `getRequest` would leak `@tanstack/react-start/server` into client code (it's
13
+ * imported transitively by routes) and trip the import-protection plugin.
14
+ */
15
+ export const getServerSession = createServerFn({ method: 'GET' }).handler(() =>
16
+ auth.api.getSession({ headers: getRequest().headers }),
17
+ )
package/index.mjs CHANGED
@@ -173,6 +173,12 @@ function execute(a, patterns) {
173
173
  )
174
174
  }
175
175
 
176
+ // Initialize a fresh repo (also satisfies Biome's vcs.useIgnoreFile).
177
+ if (run('git', ['init', '-q'], { cwd: projectDir })) {
178
+ run('git', ['-C', projectDir, 'add', '-A'])
179
+ p.log.step('git initialized')
180
+ }
181
+
176
182
  const keptMailer = a.mailerProvider !== 'none'
177
183
  const lines = [
178
184
  `Framework: ${a.framework === 'next' ? 'Next.js' : 'TanStack Start'}`,
package/lib/build.mjs CHANGED
@@ -30,7 +30,7 @@ export function buildProject({
30
30
  const keptMailer = mailerProvider !== 'none'
31
31
 
32
32
  forkBase(framework, projectDir)
33
- makeStandalone(projectDir, projectName)
33
+ makeStandalone(projectDir, projectName, framework)
34
34
 
35
35
  const strip = stripFoundations({ projectDir, framework, kept, keptMailer, patterns })
36
36
  const mailer = keptMailer
package/lib/scaffold.mjs CHANGED
@@ -22,6 +22,58 @@ const PNPM_WORKSPACE = `allowBuilds:
22
22
  lightningcss: true
23
23
  `
24
24
 
25
+ // Generated explicitly: npm strips `.gitignore` from published tarballs, so we
26
+ // can't rely on the bundled base app's copy surviving. Both keep `.env.example`.
27
+ const GITIGNORE = {
28
+ tanstack: `node_modules
29
+ .DS_Store
30
+ dist
31
+ dist-ssr
32
+ *.local
33
+ .env
34
+ .nitro
35
+ .tanstack
36
+ .wrangler
37
+ .output
38
+ .vinxi
39
+ __unconfig*
40
+ `,
41
+ next: `# dependencies
42
+ /node_modules
43
+ /.pnp
44
+ .pnp.*
45
+
46
+ # testing
47
+ /coverage
48
+
49
+ # next.js
50
+ /.next/
51
+ /out/
52
+
53
+ # production
54
+ /build
55
+
56
+ # misc
57
+ .DS_Store
58
+ *.pem
59
+
60
+ # debug
61
+ npm-debug.log*
62
+ .pnpm-debug.log*
63
+
64
+ # env files (keep .env.example committed)
65
+ .env
66
+ .env*.local
67
+
68
+ # vercel
69
+ .vercel
70
+
71
+ # typescript
72
+ *.tsbuildinfo
73
+ next-env.d.ts
74
+ `,
75
+ }
76
+
25
77
  /** Copy the base app into projectDir, minus build output & generated files. */
26
78
  export function forkBase(framework, projectDir) {
27
79
  const base = join(STACK_ROOT, 'apps', framework === 'next' ? 'next-base' : 'tanstack-base')
@@ -32,11 +84,14 @@ export function forkBase(framework, projectDir) {
32
84
  if (!run('rsync', args)) throw new Error('rsync failed while forking the base app')
33
85
  }
34
86
 
35
- /** Make the fork standalone (Biome, pnpm workspace, package.json identity). */
36
- export function makeStandalone(projectDir, projectName) {
87
+ /** Make the fork standalone (Biome, pnpm workspace, .gitignore, identity). */
88
+ export function makeStandalone(projectDir, projectName, framework) {
37
89
  // A fork needs its own Biome config (the base inherits the monorepo root's).
38
90
  copy(join(STACK_ROOT, 'patterns/_baseline/biome.jsonc'), join(projectDir, 'biome.jsonc'))
39
91
 
92
+ // Biome's vcs.useIgnoreFile needs a .gitignore; also good project hygiene.
93
+ write(join(projectDir, '.gitignore'), GITIGNORE[framework === 'next' ? 'next' : 'tanstack'])
94
+
40
95
  // Avoid ERR_PNPM_IGNORED_BUILDS on a fresh install (native build scripts).
41
96
  write(join(projectDir, 'pnpm-workspace.yaml'), PNPM_WORKSPACE)
42
97
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alfredmouelle/create-stack",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "type": "module",
5
5
  "description": "Interactive, deterministic installer for the personal reference stack — forks a base app (Next.js / TanStack Start) and strips it to your selection.",
6
6
  "author": {