create-raredays-app 0.1.11 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -19
- package/bin/index.js +53 -7
- package/package.json +5 -3
- package/scaffold-fragments/billing/module.json +1 -1
- package/scaffold-fragments/cms/module.json +1 -1
- package/scaffold-fragments/email/module.json +1 -1
- package/scaffold-fragments/i18n/module.json +5 -2
- package/scaffold-fragments/identity/module.json +2 -2
- package/scaffold-fragments/identity/vendors/better-auth/files/app/lib/auth.ts +19 -0
- package/scaffold-fragments/jobs/module.json +1 -1
- package/scaffold-fragments/organizations/module.json +1 -1
- package/scaffold-fragments/telemetry/module.json +1 -1
- package/skillex/private/contribute.md +41 -12
- package/skillex/public/consumer.md +39 -24
- package/templates/client/app/theme.override.css +4 -2
- package/templates/client/package.json +12 -11
- package/templates/full-stack/app/theme.override.css +4 -2
- package/templates/full-stack/package.json +15 -14
- package/templates/static/app/theme.override.css +4 -2
- package/templates/static/package.json +9 -8
package/README.md
CHANGED
|
@@ -37,9 +37,11 @@ Presets set the template, modules, and brand color. You can still customise disp
|
|
|
37
37
|
## Options
|
|
38
38
|
|
|
39
39
|
- `--template <name>` -- `full-stack` (default), `client`, or `static`.
|
|
40
|
-
- `--preset <name>` -- `courseware`, `internal-tool`, or `demo`. Overrides `--template
|
|
41
|
-
- `--modules <list>` -- comma-separated modules to wire
|
|
42
|
-
- `--vendors <list>` -- comma-separated vendor
|
|
40
|
+
- `--preset <name>` -- `courseware`, `internal-tool`, or `demo`. **Overrides** `--template`, `--modules`, and `--vendors` (it does not extend them).
|
|
41
|
+
- `--modules <list>` -- comma-separated modules to wire. Discovered dynamically from `scaffold-fragments/`: `billing`, `cms`, `email`, `feature-flags`, `i18n`, `identity`, `jobs`, `notifications`, `organizations`, `telemetry`. Modules apply to `full-stack` and `client` templates only.
|
|
42
|
+
- `--vendors <list>` -- comma-separated **`module:vendor` pairs**, e.g. `billing:stripe,cms:hygraph,email:resend,identity:better-auth`. A bare vendor name (`stripe`) is not a valid pair and is skipped with a warning.
|
|
43
|
+
- `--harness <name>` -- wire `@raredays/mcp` into an MCP-aware IDE: `claude-code`, `cursor`, `windsurf`, or `none` (default). Adds `@raredays/mcp` to devDependencies and writes the IDE config (`.mcp.json` at root for Claude Code; `.cursor/mcp.json` / `.windsurf/mcp.json` otherwise).
|
|
44
|
+
- `--deploy <target>` -- scaffold deploy config: `vercel` or `none` (default). Writes `vercel.json`, `.env.example`, and `docs/DEPLOY.md`.
|
|
43
45
|
- `--remote-url <url>` -- set git remote origin after scaffolding.
|
|
44
46
|
- `--no-git` -- skip `git init`.
|
|
45
47
|
- `--no-install` -- skip `pnpm install`.
|
|
@@ -49,27 +51,41 @@ Presets set the template, modules, and brand color. You can still customise disp
|
|
|
49
51
|
## What it does
|
|
50
52
|
|
|
51
53
|
1. Copies `templates/<name>/` into the target directory.
|
|
52
|
-
2. Renames stub files (`_gitignore` -> `.gitignore`, `_github` -> `.github`).
|
|
54
|
+
2. Renames stub files (`_gitignore` -> `.gitignore`, `_npmrc` -> `.npmrc`, `_claude` -> `.claude`, `_github` -> `.github`).
|
|
53
55
|
3. Sets `package.json:name` to the project slug.
|
|
54
56
|
4. Substitutes `__SLUG__` in `site.spec.ts` and applies display name, domain, and brand color.
|
|
55
|
-
5. Loads scaffold fragments for selected modules (imports, config, ports, file overlays).
|
|
56
|
-
6. Composes `app/lib/runtime.ts` with module wiring and `mocks/handlers.ts` with vendor mocks.
|
|
57
|
-
7. Merges fragment dependencies into `package.json`.
|
|
58
|
-
8.
|
|
59
|
-
9.
|
|
57
|
+
5. Loads scaffold fragments for selected modules (imports, config, ports, file overlays) and applies vendor overlays for each `module:vendor` pair.
|
|
58
|
+
6. Composes `app/lib/runtime.ts` with module wiring and `mocks/handlers.ts` with vendor mocks (skipped for the `static` template, which has no composition root).
|
|
59
|
+
7. Merges fragment + vendor dependencies into `package.json`.
|
|
60
|
+
8. Applies `--harness` (MCP config) and `--deploy` (deploy config) when requested.
|
|
61
|
+
9. Writes `.env.example` **only** if any vendor or deploy env vars are needed.
|
|
62
|
+
10. Optionally runs `git init` and `pnpm install`.
|
|
60
63
|
|
|
61
64
|
## Scaffold fragments
|
|
62
65
|
|
|
63
|
-
The `scaffold-fragments/` directory contains per-module overlays:
|
|
64
|
-
|
|
65
|
-
- `billing/` --
|
|
66
|
-
- `
|
|
67
|
-
- `
|
|
68
|
-
- `
|
|
69
|
-
- `
|
|
70
|
-
- `
|
|
71
|
-
|
|
72
|
-
|
|
66
|
+
The `scaffold-fragments/` directory contains per-module overlays (10 total):
|
|
67
|
+
|
|
68
|
+
- `billing/` -- billing integration; vendor: `stripe`
|
|
69
|
+
- `cms/` -- CMS integration; vendor: `hygraph`
|
|
70
|
+
- `email/` -- transactional email; vendor: `resend`
|
|
71
|
+
- `feature-flags/` -- feature-flag wiring
|
|
72
|
+
- `i18n/` -- internationalisation
|
|
73
|
+
- `identity/` -- auth/sessions/roles; vendor: `better-auth`
|
|
74
|
+
- `jobs/` -- background job processing
|
|
75
|
+
- `notifications/` -- multi-channel notification delivery
|
|
76
|
+
- `organizations/` -- multi-tenant organizations
|
|
77
|
+
- `telemetry/` -- telemetry; vendors: `sentry`, `axiom`
|
|
78
|
+
|
|
79
|
+
Each fragment has a `module.json` manifest, runtime slot injections, and optional per-vendor file overlays.
|
|
80
|
+
|
|
81
|
+
## Keeping template pins current
|
|
82
|
+
|
|
83
|
+
Every template and fragment pins `@raredays/*` deps and `MCP_VERSION`. These are
|
|
84
|
+
the framework's public surface — a non-`--dev` scaffold installs exactly what
|
|
85
|
+
they pin — and do not move automatically when the workspace releases. Run
|
|
86
|
+
`pnpm --filter create-raredays-app sync-versions` (also run as `prepublishOnly`)
|
|
87
|
+
to refresh them to `^<current workspace version>`. The guard test
|
|
88
|
+
`test/template-workspace-pins.test.ts` fails in CI when a pin drifts out of range.
|
|
73
89
|
|
|
74
90
|
## How it fits in the ecosystem
|
|
75
91
|
|
package/bin/index.js
CHANGED
|
@@ -635,12 +635,14 @@ async function scaffold(opts) {
|
|
|
635
635
|
// ─── MCP harness wiring ──────────────────────────────────────────────────
|
|
636
636
|
|
|
637
637
|
const HARNESS_CONFIGS = {
|
|
638
|
-
|
|
638
|
+
// Claude Code reads project-scope MCP servers from `.mcp.json` at the repo
|
|
639
|
+
// root (not `.claude/mcp.json`), as this monorepo's own `.mcp.json` does.
|
|
640
|
+
"claude-code": ".mcp.json",
|
|
639
641
|
cursor: ".cursor/mcp.json",
|
|
640
642
|
windsurf: ".windsurf/mcp.json",
|
|
641
643
|
};
|
|
642
644
|
|
|
643
|
-
const MCP_VERSION = "0.0
|
|
645
|
+
const MCP_VERSION = "0.2.0";
|
|
644
646
|
|
|
645
647
|
async function applyHarness(targetDir, pkg, harness, dev) {
|
|
646
648
|
if (!harness || harness === "none") return;
|
|
@@ -944,26 +946,70 @@ async function main() {
|
|
|
944
946
|
`Unknown preset "${values.preset}". Available presets: ${Object.keys(PRESETS).join(", ")}`,
|
|
945
947
|
);
|
|
946
948
|
}
|
|
949
|
+
const resolvedTemplate = preset ? preset.template : values.template;
|
|
947
950
|
const requestedModules = preset ? preset.modules : parseCsv(values.modules);
|
|
948
951
|
const availableModules = availableModuleKeys();
|
|
949
952
|
const known = [];
|
|
950
953
|
for (const m of requestedModules) {
|
|
951
|
-
if (availableModules.includes(m)) {
|
|
952
|
-
known.push(m);
|
|
953
|
-
} else {
|
|
954
|
+
if (!availableModules.includes(m)) {
|
|
954
955
|
console.warn(
|
|
955
956
|
`warning: --modules "${m}" has no scaffold-fragment under scaffold-fragments/${m}/, skipping. Available: ${availableModules.join(", ")}`,
|
|
956
957
|
);
|
|
958
|
+
continue;
|
|
957
959
|
}
|
|
960
|
+
// Mirror the interactive path's template-compatibility filter: a module
|
|
961
|
+
// whose fragment does not declare the resolved template is skipped (its
|
|
962
|
+
// runtime/files assume a composition root that template lacks).
|
|
963
|
+
const fragment = allFragments.find((f) => f.key === m);
|
|
964
|
+
const supported = fragment?.templates ?? ["full-stack"];
|
|
965
|
+
if (!supported.includes(resolvedTemplate)) {
|
|
966
|
+
console.warn(
|
|
967
|
+
`warning: --modules "${m}" is not compatible with the "${resolvedTemplate}" template (supports: ${supported.join(", ")}), skipping.`,
|
|
968
|
+
);
|
|
969
|
+
continue;
|
|
970
|
+
}
|
|
971
|
+
known.push(m);
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
// Validate --vendors pairs (module:vendor) and warn on unknown ones, the way
|
|
975
|
+
// the module list already warns. A silently-dropped vendor is worse than a
|
|
976
|
+
// loud one.
|
|
977
|
+
const requestedVendors = preset ? preset.vendors : parseCsv(values.vendors);
|
|
978
|
+
const knownVendors = [];
|
|
979
|
+
for (const pair of requestedVendors) {
|
|
980
|
+
const [moduleKey, vendorKey] = pair.split(":");
|
|
981
|
+
if (!vendorKey) {
|
|
982
|
+
console.warn(
|
|
983
|
+
`warning: --vendors "${pair}" is not a "module:vendor" pair (e.g. "billing:stripe"), skipping.`,
|
|
984
|
+
);
|
|
985
|
+
continue;
|
|
986
|
+
}
|
|
987
|
+
const fragment = allFragments.find((f) => f.key === moduleKey);
|
|
988
|
+
const vendor = fragment?.vendors?.find((v) => v.key === vendorKey);
|
|
989
|
+
if (!known.includes(moduleKey)) {
|
|
990
|
+
console.warn(
|
|
991
|
+
`warning: --vendors "${pair}" names module "${moduleKey}", which is not in the selected/compatible module set, skipping.`,
|
|
992
|
+
);
|
|
993
|
+
continue;
|
|
994
|
+
}
|
|
995
|
+
if (!vendor) {
|
|
996
|
+
const vendorKeys = (fragment?.vendors ?? []).map((v) => v.key);
|
|
997
|
+
console.warn(
|
|
998
|
+
`warning: --vendors "${pair}" has no vendor "${vendorKey}" for module "${moduleKey}"${vendorKeys.length ? ` (available: ${vendorKeys.join(", ")})` : ""}, skipping.`,
|
|
999
|
+
);
|
|
1000
|
+
continue;
|
|
1001
|
+
}
|
|
1002
|
+
knownVendors.push(pair);
|
|
958
1003
|
}
|
|
1004
|
+
|
|
959
1005
|
opts = {
|
|
960
1006
|
projectName: target,
|
|
961
|
-
template:
|
|
1007
|
+
template: resolvedTemplate,
|
|
962
1008
|
displayName: undefined,
|
|
963
1009
|
canonicalDomain: undefined,
|
|
964
1010
|
brandPrimary: preset?.brandPrimary ?? undefined,
|
|
965
1011
|
selectedModules: known,
|
|
966
|
-
selectedVendors:
|
|
1012
|
+
selectedVendors: knownVendors,
|
|
967
1013
|
remoteUrl: values["remote-url"],
|
|
968
1014
|
gitInit: !values["no-git"],
|
|
969
1015
|
runInstall: !values["no-install"],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-raredays-app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Scaffold a new RareDays Next.js app.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"author": "Jeremy Harper <jeremy@raredays.com>",
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"typescript": "5.9.3",
|
|
25
|
-
"vitest": "4.1.
|
|
26
|
-
"@raredays/lint": "0.2.
|
|
25
|
+
"vitest": "4.1.10",
|
|
26
|
+
"@raredays/lint": "0.2.2"
|
|
27
27
|
},
|
|
28
28
|
"engines": {
|
|
29
29
|
"node": ">=20"
|
|
@@ -41,6 +41,8 @@
|
|
|
41
41
|
"check": "raredays check",
|
|
42
42
|
"test": "vitest run",
|
|
43
43
|
"test:watch": "vitest",
|
|
44
|
+
"sync-versions": "node scripts/sync-template-versions.mjs",
|
|
45
|
+
"sync-versions:check": "node scripts/sync-template-versions.mjs --check",
|
|
44
46
|
"dev": "mkdir -p .sandbox && cd .sandbox && node ../bin/index.js",
|
|
45
47
|
"clean": "rm -rf .sandbox"
|
|
46
48
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"label": "CMS (headless content + rich text)",
|
|
4
4
|
"description": "Vendor-neutral CMS port. In-memory dev driver for development; swap to Hygraph (or another adapter) for production.",
|
|
5
5
|
"deps": {
|
|
6
|
-
"@raredays/cms": "^0.1.
|
|
6
|
+
"@raredays/cms": "^0.1.1"
|
|
7
7
|
},
|
|
8
8
|
"vendors": [
|
|
9
9
|
{
|
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
"label": "Internationalisation (i18n)",
|
|
4
4
|
"description": "Locale resolution, ICU message formatting, date/number/currency formatters, and React hooks.",
|
|
5
5
|
"deps": {
|
|
6
|
-
"@raredays/i18n": "^
|
|
6
|
+
"@raredays/i18n": "^1.0.0"
|
|
7
7
|
},
|
|
8
|
-
"templates": [
|
|
8
|
+
"templates": [
|
|
9
|
+
"full-stack",
|
|
10
|
+
"client"
|
|
11
|
+
]
|
|
9
12
|
}
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
"key": "better-auth",
|
|
11
11
|
"label": "Better Auth (self-hosted)",
|
|
12
12
|
"deps": {
|
|
13
|
-
"@raredays/identity-better-auth": "^0.
|
|
14
|
-
"better-auth": "
|
|
13
|
+
"@raredays/identity-better-auth": "^0.2.0",
|
|
14
|
+
"better-auth": "1.6.11"
|
|
15
15
|
},
|
|
16
16
|
"env": [
|
|
17
17
|
"BETTER_AUTH_SECRET",
|
|
@@ -14,7 +14,26 @@
|
|
|
14
14
|
// See @raredays/identity-better-auth README for the full pattern.
|
|
15
15
|
|
|
16
16
|
import { betterAuth } from "better-auth";
|
|
17
|
+
import { resolveRedirectorConfig } from "@raredays/identity-better-auth/oauth-redirector";
|
|
18
|
+
|
|
19
|
+
// Preview-deployment OAuth. Inert unless OAUTH_REDIRECTOR_URL + SECRET are set
|
|
20
|
+
// (Preview env only), so production, staging, and local dev use the direct flow
|
|
21
|
+
// unchanged. When set, previews with dynamic hostnames (per-branch, per-dev)
|
|
22
|
+
// complete OAuth against their OWN database by bouncing through this app's
|
|
23
|
+
// redirector (oauth.<zone>) — the single callback URL you register with the
|
|
24
|
+
// provider (@legoland/project-ops provisions this per project). Set the social
|
|
25
|
+
// provider's `redirectURI` to `redirector.redirectURI` too (see the commented
|
|
26
|
+
// Google block). @raredays/identity-better-auth "Preview OAuth".
|
|
27
|
+
const redirector = resolveRedirectorConfig();
|
|
17
28
|
|
|
18
29
|
export const auth = betterAuth({
|
|
19
30
|
emailAndPassword: { enabled: true },
|
|
31
|
+
// socialProviders: {
|
|
32
|
+
// google: {
|
|
33
|
+
// clientId: process.env.GOOGLE_CLIENT_ID!,
|
|
34
|
+
// clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
|
|
35
|
+
// ...(redirector.redirectURI && { redirectURI: redirector.redirectURI }),
|
|
36
|
+
// },
|
|
37
|
+
// },
|
|
38
|
+
plugins: [...redirector.plugins],
|
|
20
39
|
});
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"label": "Background jobs (in-process runner)",
|
|
4
4
|
"description": "Type-safe job definitions, retries, idempotency. In-process runner by default; swap to Inngest/Trigger.dev for durable execution.",
|
|
5
5
|
"deps": {
|
|
6
|
-
"@raredays/jobs": "^0.1.
|
|
6
|
+
"@raredays/jobs": "^0.1.1"
|
|
7
7
|
},
|
|
8
8
|
"vendors": [],
|
|
9
9
|
"templates": [
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"label": "Organizations (multi-tenancy)",
|
|
4
4
|
"description": "Domain types + role-based permissions for organizations, memberships, and invitations. Pairs with a postgres provider for storage.",
|
|
5
5
|
"deps": {
|
|
6
|
-
"@raredays/organizations": "^0.1.
|
|
6
|
+
"@raredays/organizations": "^0.1.1"
|
|
7
7
|
},
|
|
8
8
|
"vendors": [],
|
|
9
9
|
"templates": [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: "Contributing to create-raredays-app"
|
|
3
|
-
description: Deep guide to the app scaffolder — CLI flow, CRDA placeholder tokens, composeRuntime/composeHandlers internals, template file trees, customiseSiteSpec regex,
|
|
3
|
+
description: Deep guide to the app scaffolder — CLI flow, presets, CRDA placeholder tokens, composeRuntime/composeHandlers internals, template file trees, customiseSiteSpec regex, ten fragments + vendor overlays, harness/deploy wiring, env generation, --dev flag, version-pin sync script, and the test suite.
|
|
4
4
|
topics: [scaffolding, contributing]
|
|
5
5
|
tags: [scaffolding, cli, contributing]
|
|
6
6
|
---
|
|
@@ -42,16 +42,18 @@ Prompts in order via `@clack/prompts`:
|
|
|
42
42
|
|
|
43
43
|
1. Error if target dir exists
|
|
44
44
|
2. Copy `templates/{template}/` recursively
|
|
45
|
-
3. Rename stub files: `_gitignore` → `.gitignore`, `_npmrc` → `.npmrc`
|
|
45
|
+
3. Rename stub files: `_gitignore` → `.gitignore`, `_npmrc` → `.npmrc`, `_claude` → `.claude`, `_github` → `.github`
|
|
46
46
|
4. Derive slug: trim, lowercase, replace `[^a-z0-9]+` with `-`, strip leading/trailing `-`
|
|
47
47
|
5. Substitute `__SLUG__` in `site.spec.ts`
|
|
48
48
|
6. Apply `customiseSiteSpec()` regex replacements
|
|
49
|
-
7. Load fragments, merge deps, copy fragment `files/` dirs, compose runtime
|
|
49
|
+
7. Load fragments (template-compatibility filtered), merge deps + vendor overlays, copy fragment `files/` dirs, compose runtime
|
|
50
50
|
8. Compose mock handlers if template ≠ `static`
|
|
51
51
|
9. Merge deps into `package.json`, apply `--dev` rewrite if flagged
|
|
52
|
-
10.
|
|
53
|
-
11. `
|
|
54
|
-
12.
|
|
52
|
+
10. `applyHarness()` (`--harness`) — add `@raredays/mcp` devDep + write the IDE MCP config (`.mcp.json` at root for Claude Code)
|
|
53
|
+
11. `applyDeploy()` (`--deploy vercel`) — write `vercel.json` + `docs/DEPLOY.md`, collect deploy env vars
|
|
54
|
+
12. Write `.env.example` only if any vendor/deploy env vars were collected
|
|
55
|
+
13. `git init --quiet --initial-branch=main` (+ remote if specified)
|
|
56
|
+
14. `pnpm install` with `stdio: "inherit"`
|
|
55
57
|
|
|
56
58
|
## CRDA Placeholder Tokens
|
|
57
59
|
|
|
@@ -135,12 +137,12 @@ scaffold-fragments/{key}/
|
|
|
135
137
|
"key": "billing",
|
|
136
138
|
"label": "Billing (subscriptions + webhooks)",
|
|
137
139
|
"description": "...",
|
|
138
|
-
"deps": { "@raredays/billing": "0.0
|
|
140
|
+
"deps": { "@raredays/billing": "^0.2.0" },
|
|
139
141
|
"vendors": [
|
|
140
142
|
{
|
|
141
143
|
"key": "stripe",
|
|
142
144
|
"label": "Stripe",
|
|
143
|
-
"deps": { "@raredays/billing-stripe": "0.0
|
|
145
|
+
"deps": { "@raredays/billing-stripe": "^0.1.0" },
|
|
144
146
|
"env": ["STRIPE_SECRET_KEY", "STRIPE_WEBHOOK_SECRET"],
|
|
145
147
|
"mocks": { "package": "@raredays/billing-stripe/mocks", "named": "stripeMocks" }
|
|
146
148
|
}
|
|
@@ -149,16 +151,24 @@ scaffold-fragments/{key}/
|
|
|
149
151
|
}
|
|
150
152
|
```
|
|
151
153
|
|
|
152
|
-
### Available Fragments
|
|
154
|
+
### Available Fragments (10)
|
|
153
155
|
|
|
154
156
|
| Fragment | Templates | Vendors | Env Vars |
|
|
155
157
|
| --------------- | ------------------ | ------------- | -------------------------------------------- |
|
|
156
158
|
| `billing` | full-stack | Stripe | `STRIPE_SECRET_KEY`, `STRIPE_WEBHOOK_SECRET` |
|
|
159
|
+
| `cms` | full-stack, client | Hygraph | `HYGRAPH_ENDPOINT`, `HYGRAPH_TOKEN` |
|
|
157
160
|
| `email` | full-stack | Resend | `RESEND_API_KEY` |
|
|
161
|
+
| `feature-flags` | full-stack, client | None | — |
|
|
162
|
+
| `i18n` | full-stack, client | None | — |
|
|
163
|
+
| `identity` | full-stack, client | Better Auth | `BETTER_AUTH_SECRET`, `BETTER_AUTH_URL` |
|
|
158
164
|
| `jobs` | full-stack | None | — |
|
|
159
165
|
| `notifications` | full-stack | None | — |
|
|
166
|
+
| `organizations` | full-stack | None | — |
|
|
160
167
|
| `telemetry` | full-stack, client | Sentry, Axiom | `SENTRY_DSN`, `AXIOM_TOKEN`, `AXIOM_DATASET` |
|
|
161
168
|
|
|
169
|
+
Pins in `deps`/`vendors[].deps` are `^<workspace version>` for `@raredays/*`
|
|
170
|
+
packages, kept current by `scripts/sync-template-versions.mjs` (see below).
|
|
171
|
+
|
|
162
172
|
## .env.example Generation
|
|
163
173
|
|
|
164
174
|
Only written if `envVars.length > 0`. Format:
|
|
@@ -178,12 +188,31 @@ STRIPE_WEBHOOK_SECRET=
|
|
|
178
188
|
|
|
179
189
|
Rewrites every `@raredays/*` in both `dependencies` and `devDependencies` to `workspace:*`. Used for in-repo dogfooding (playground scaffolds).
|
|
180
190
|
|
|
191
|
+
## Version pins (`scripts/sync-template-versions.mjs`)
|
|
192
|
+
|
|
193
|
+
Templates + fragments pin `@raredays/*` and `bin/index.js` holds `MCP_VERSION`.
|
|
194
|
+
These are the framework's public surface and do NOT move when the workspace
|
|
195
|
+
releases. Run `pnpm --filter create-raredays-app sync-versions` (also
|
|
196
|
+
`prepublishOnly`) to refresh them to `^<workspace version>`; `sync-versions:check`
|
|
197
|
+
is the CI dry-run. The guard test `test/template-workspace-pins.test.ts` fails
|
|
198
|
+
when a pin's range excludes the current workspace version.
|
|
199
|
+
|
|
181
200
|
## Testing
|
|
182
201
|
|
|
183
|
-
|
|
202
|
+
The package ships Vitest guard/unit tests under `test/`:
|
|
203
|
+
|
|
204
|
+
- **Template lint guards**: every-import-is-declared, secure next/react floors,
|
|
205
|
+
pnpm-11 build approvals, registry auth, `.ts`-import specifiers, skill
|
|
206
|
+
frontmatter (v4), workflow lint, no duplicate migration workflow.
|
|
207
|
+
- **`template-workspace-pins.test.ts`**: `@raredays/*` pins (incl. `MCP_VERSION`)
|
|
208
|
+
resolve the current workspace version.
|
|
209
|
+
- **`scaffold-smoke.test.ts`**: runs `bin/index.js` into a temp dir
|
|
210
|
+
(`--no-git --no-install --dev`) and asserts the harness config path, merged
|
|
211
|
+
deps, and composed runtime.
|
|
212
|
+
- **`compose-allow-builds.test.ts`**: `mergeAllowBuilds` unit tests.
|
|
184
213
|
|
|
185
|
-
-
|
|
186
|
-
|
|
214
|
+
Full scaffold-build-serve coverage lives in the e2e scenarios (`/e2e/`); use
|
|
215
|
+
`pnpm playground:reset && pnpm playground` for manual checks.
|
|
187
216
|
|
|
188
217
|
## Build and Test
|
|
189
218
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: "Consuming create-raredays-app"
|
|
3
|
-
description: How to scaffold new apps — interactive prompts sequence, CLI flags, three templates (full-stack/client/static) with output files,
|
|
3
|
+
description: How to scaffold new apps — presets, interactive prompts sequence, CLI flags (including --preset/--harness/--deploy), three templates (full-stack/client/static) with output files, ten available modules with vendor options, --dev flag for monorepo dogfooding.
|
|
4
4
|
topics: [scaffolding, consuming]
|
|
5
5
|
tags: [scaffolding, cli, consuming]
|
|
6
6
|
---
|
|
@@ -13,18 +13,21 @@ tags: [scaffolding, cli, consuming]
|
|
|
13
13
|
pnpm create raredays-app
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
Prompts in order:
|
|
16
|
+
Prompts in roughly this order:
|
|
17
17
|
|
|
18
|
-
1. **
|
|
19
|
-
2. **
|
|
20
|
-
3. **
|
|
21
|
-
4. **
|
|
22
|
-
5. **
|
|
23
|
-
6. **
|
|
24
|
-
7. **
|
|
25
|
-
8. **
|
|
26
|
-
9. **
|
|
27
|
-
10. **
|
|
18
|
+
1. **What are you building?** — pick a preset (courseware / internal-tool / demo) or "custom"
|
|
19
|
+
2. **Project name** — directory name and package slug (kebab-case, 1–64 chars)
|
|
20
|
+
3. **Template** — `full-stack`, `client`, or `static` (skipped when a preset is chosen)
|
|
21
|
+
4. **Display name** — human-readable (defaults to project name)
|
|
22
|
+
5. **Canonical domain** — optional, hostname only (e.g., `example.com`)
|
|
23
|
+
6. **Brand primary color** — hex value (defaults to `#10b981`)
|
|
24
|
+
7. **Modules** — multiselect (not shown for `static` or when a preset is chosen)
|
|
25
|
+
8. **Vendors** — multiselect for selected modules
|
|
26
|
+
9. **Deploy target** — optional (`vercel` / none)
|
|
27
|
+
10. **Git remote URL** — optional
|
|
28
|
+
11. **Initialize git? / Run pnpm install?** — default yes
|
|
29
|
+
|
|
30
|
+
`--harness` (MCP/IDE wiring) is flag-only today; it is not offered interactively.
|
|
28
31
|
|
|
29
32
|
## Non-Interactive Mode
|
|
30
33
|
|
|
@@ -37,15 +40,18 @@ pnpm create raredays-app my-app \
|
|
|
37
40
|
|
|
38
41
|
## CLI Flags
|
|
39
42
|
|
|
40
|
-
| Flag | Description
|
|
41
|
-
| -------------- |
|
|
42
|
-
| `--template` | `full-stack` (default), `client`, or `static`
|
|
43
|
-
| `--
|
|
44
|
-
| `--
|
|
45
|
-
| `--
|
|
46
|
-
| `--
|
|
47
|
-
| `--
|
|
48
|
-
| `--
|
|
43
|
+
| Flag | Description |
|
|
44
|
+
| -------------- | ----------------------------------------------------------------------------------- |
|
|
45
|
+
| `--template` | `full-stack` (default), `client`, or `static` |
|
|
46
|
+
| `--preset` | `courseware`, `internal-tool`, `demo` — **overrides** template/modules/vendors |
|
|
47
|
+
| `--modules` | Comma-separated module keys (ignored when `--preset` is given) |
|
|
48
|
+
| `--vendors` | Comma-separated `MODULE:VENDOR` selectors (a bare vendor is skipped with a warning) |
|
|
49
|
+
| `--harness` | `claude-code` / `cursor` / `windsurf` / none — wire `@raredays/mcp` into the IDE |
|
|
50
|
+
| `--deploy` | `vercel` / none — scaffold deploy config (`vercel.json`, `docs/DEPLOY.md`) |
|
|
51
|
+
| `--remote-url` | Git remote origin URL |
|
|
52
|
+
| `--no-git` | Skip git initialization |
|
|
53
|
+
| `--no-install` | Skip `pnpm install` |
|
|
54
|
+
| `--dev` | Rewrite all `@raredays/*` deps to `workspace:*` (monorepo dogfooding) |
|
|
49
55
|
|
|
50
56
|
## Templates
|
|
51
57
|
|
|
@@ -89,14 +95,21 @@ Key output files:
|
|
|
89
95
|
|
|
90
96
|
## Available Modules
|
|
91
97
|
|
|
92
|
-
|
|
98
|
+
Ten fragments live under `scaffold-fragments/`. Each declares which templates it
|
|
99
|
+
supports (`module.json:templates`); a module incompatible with the chosen
|
|
100
|
+
template is skipped with a warning.
|
|
101
|
+
|
|
102
|
+
| Module | Label | Templates | Vendor Options |
|
|
93
103
|
| --------------- | --------------------------- | ------------------ | -------------- |
|
|
94
104
|
| `billing` | Subscriptions + webhooks | full-stack | Stripe |
|
|
95
105
|
| `cms` | Headless CMS + rich text | full-stack, client | Hygraph |
|
|
96
106
|
| `email` | Transactional email | full-stack | Resend |
|
|
107
|
+
| `feature-flags` | Feature-flag wiring | full-stack, client | — |
|
|
108
|
+
| `i18n` | Internationalisation | full-stack, client | — |
|
|
97
109
|
| `identity` | Auth, sessions, orgs, roles | full-stack, client | Better Auth |
|
|
98
110
|
| `jobs` | Background jobs | full-stack | — (in-memory) |
|
|
99
111
|
| `notifications` | Multi-channel notifications | full-stack | — (in-memory) |
|
|
112
|
+
| `organizations` | Multi-tenant organizations | full-stack | — (in-memory) |
|
|
100
113
|
| `telemetry` | Logging, errors, metrics | full-stack, client | Sentry, Axiom |
|
|
101
114
|
|
|
102
115
|
Identity is always wired with an in-memory dev provider (the template hard-codes
|
|
@@ -140,11 +153,13 @@ A fully configured project with:
|
|
|
140
153
|
|
|
141
154
|
- `site.spec.ts` — SiteSpec with your name, domain, and brand color substituted
|
|
142
155
|
- `app/lib/runtime.ts` — composition root with selected modules wired via slot injection
|
|
143
|
-
- `app/theme.css` —
|
|
156
|
+
- `app/theme.override.css` — build-time `@theme` overrides from branding config
|
|
144
157
|
- `next.config.ts` — wraps `withRaredays()`
|
|
145
158
|
- `postcss.config.mjs` — re-exports from `@raredays/style/postcss`
|
|
146
159
|
- `mocks/` — MSW setup with vendor-specific handlers (client/full-stack)
|
|
147
|
-
- `.env.example` —
|
|
160
|
+
- `.env.example` — written only when a selected vendor or `--deploy` target needs env vars
|
|
161
|
+
- With `--harness`: `.mcp.json` (Claude Code, at root) / `.cursor/mcp.json` / `.windsurf/mcp.json` + `@raredays/mcp` devDep
|
|
162
|
+
- With `--deploy vercel`: `vercel.json` + `docs/DEPLOY.md`
|
|
148
163
|
|
|
149
164
|
## After Scaffolding
|
|
150
165
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
*
|
|
1
|
+
/**
|
|
2
|
+
* AUTO-GENERATED — DO NOT EDIT.
|
|
3
|
+
* Regenerated from site.spec.ts by `pnpm theme:build`.
|
|
4
|
+
* For per-app CSS overrides, edit app/globals.css instead.
|
|
3
5
|
* Source: site.spec.ts
|
|
4
6
|
* To regenerate: pnpm theme:build
|
|
5
7
|
*/
|
|
@@ -26,11 +26,11 @@
|
|
|
26
26
|
"check": "raredays check && tsc --noEmit"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@raredays/components": "^0.
|
|
30
|
-
"@raredays/core": "^0.
|
|
29
|
+
"@raredays/components": "^0.3.0",
|
|
30
|
+
"@raredays/core": "^0.2.2",
|
|
31
31
|
"@raredays/identity": "^0.1.0",
|
|
32
|
-
"@raredays/mocks": "^0.
|
|
33
|
-
"@raredays/style": "^0.
|
|
32
|
+
"@raredays/mocks": "^0.2.0",
|
|
33
|
+
"@raredays/style": "^0.3.0",
|
|
34
34
|
"lucide-react": "1.14.0",
|
|
35
35
|
"msw": "2.11.5",
|
|
36
36
|
"next": "16.2.9",
|
|
@@ -39,11 +39,12 @@
|
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@atheory-ai/skillex": "0.7.2",
|
|
42
|
-
"@raredays/dev": "^0.
|
|
43
|
-
"@raredays/lint": "^0.
|
|
44
|
-
"@raredays/site-config": "^0.
|
|
45
|
-
"@raredays/
|
|
46
|
-
"@raredays/
|
|
42
|
+
"@raredays/dev": "^0.2.0",
|
|
43
|
+
"@raredays/lint": "^0.2.2",
|
|
44
|
+
"@raredays/site-config": "^0.3.0",
|
|
45
|
+
"@raredays/skills": "^0.2.0",
|
|
46
|
+
"@raredays/theme-build": "^0.3.0",
|
|
47
|
+
"@raredays/typescript": "^0.2.1",
|
|
47
48
|
"@storybook/react-vite": "10.3.6",
|
|
48
49
|
"@tailwindcss/postcss": "4.2.4",
|
|
49
50
|
"@tailwindcss/vite": "4.2.4",
|
|
@@ -51,13 +52,13 @@
|
|
|
51
52
|
"@types/react": "19.2.14",
|
|
52
53
|
"@types/react-dom": "19.2.3",
|
|
53
54
|
"@vitejs/plugin-react": "6.0.1",
|
|
54
|
-
"@vitest/coverage-v8": "4.1.
|
|
55
|
+
"@vitest/coverage-v8": "4.1.9",
|
|
55
56
|
"happy-dom": "15.11.7",
|
|
56
57
|
"storybook": "10.3.6",
|
|
57
58
|
"tailwindcss": "4.2.4",
|
|
58
59
|
"typescript": "5.9.3",
|
|
59
60
|
"vite": "8.0.10",
|
|
60
|
-
"vitest": "4.1.
|
|
61
|
+
"vitest": "4.1.9"
|
|
61
62
|
},
|
|
62
63
|
"engines": {
|
|
63
64
|
"node": ">=24"
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
*
|
|
1
|
+
/**
|
|
2
|
+
* AUTO-GENERATED — DO NOT EDIT.
|
|
3
|
+
* Regenerated from site.spec.ts by `pnpm theme:build`.
|
|
4
|
+
* For per-app CSS overrides, edit app/globals.css instead.
|
|
3
5
|
* Source: site.spec.ts
|
|
4
6
|
* To regenerate: pnpm theme:build
|
|
5
7
|
*/
|
|
@@ -32,14 +32,14 @@
|
|
|
32
32
|
"check": "raredays check && tsc --noEmit"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@raredays/components": "^0.
|
|
36
|
-
"@raredays/core": "^0.
|
|
35
|
+
"@raredays/components": "^0.3.0",
|
|
36
|
+
"@raredays/core": "^0.2.2",
|
|
37
37
|
"@raredays/identity": "^0.1.0",
|
|
38
|
-
"@raredays/mocks": "^0.
|
|
39
|
-
"@raredays/postgres": "^0.
|
|
40
|
-
"@raredays/postgres-neon": "^0.
|
|
41
|
-
"@raredays/postgres-pglite": "^0.
|
|
42
|
-
"@raredays/style": "^0.
|
|
38
|
+
"@raredays/mocks": "^0.2.0",
|
|
39
|
+
"@raredays/postgres": "^0.2.0",
|
|
40
|
+
"@raredays/postgres-neon": "^0.2.0",
|
|
41
|
+
"@raredays/postgres-pglite": "^0.2.0",
|
|
42
|
+
"@raredays/style": "^0.3.0",
|
|
43
43
|
"drizzle-orm": "0.45.2",
|
|
44
44
|
"lucide-react": "1.14.0",
|
|
45
45
|
"msw": "2.11.5",
|
|
@@ -49,11 +49,12 @@
|
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@atheory-ai/skillex": "0.7.2",
|
|
52
|
-
"@raredays/dev": "^0.
|
|
53
|
-
"@raredays/lint": "^0.
|
|
54
|
-
"@raredays/site-config": "^0.
|
|
55
|
-
"@raredays/
|
|
56
|
-
"@raredays/
|
|
52
|
+
"@raredays/dev": "^0.2.0",
|
|
53
|
+
"@raredays/lint": "^0.2.2",
|
|
54
|
+
"@raredays/site-config": "^0.3.0",
|
|
55
|
+
"@raredays/skills": "^0.2.0",
|
|
56
|
+
"@raredays/theme-build": "^0.3.0",
|
|
57
|
+
"@raredays/typescript": "^0.2.1",
|
|
57
58
|
"@tailwindcss/postcss": "4.2.4",
|
|
58
59
|
"@types/node": "22.19.17",
|
|
59
60
|
"@types/react": "19.2.14",
|
|
@@ -61,14 +62,14 @@
|
|
|
61
62
|
"@storybook/react-vite": "10.3.6",
|
|
62
63
|
"@tailwindcss/vite": "4.2.4",
|
|
63
64
|
"@vitejs/plugin-react": "6.0.1",
|
|
64
|
-
"@vitest/coverage-v8": "4.1.
|
|
65
|
+
"@vitest/coverage-v8": "4.1.9",
|
|
65
66
|
"drizzle-kit": "0.31.10",
|
|
66
67
|
"happy-dom": "15.11.7",
|
|
67
68
|
"storybook": "10.3.6",
|
|
68
69
|
"tailwindcss": "4.2.4",
|
|
69
70
|
"typescript": "5.9.3",
|
|
70
71
|
"vite": "8.0.10",
|
|
71
|
-
"vitest": "4.1.
|
|
72
|
+
"vitest": "4.1.9"
|
|
72
73
|
},
|
|
73
74
|
"engines": {
|
|
74
75
|
"node": ">=24"
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
*
|
|
1
|
+
/**
|
|
2
|
+
* AUTO-GENERATED — DO NOT EDIT.
|
|
3
|
+
* Regenerated from site.spec.ts by `pnpm theme:build`.
|
|
4
|
+
* For per-app CSS overrides, edit app/globals.css instead.
|
|
3
5
|
* Source: site.spec.ts
|
|
4
6
|
* To regenerate: pnpm theme:build
|
|
5
7
|
*/
|
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
"check": "raredays check && tsc --noEmit"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@raredays/components": "^0.
|
|
26
|
-
"@raredays/core": "^0.
|
|
27
|
-
"@raredays/style": "^0.
|
|
25
|
+
"@raredays/components": "^0.3.0",
|
|
26
|
+
"@raredays/core": "^0.2.2",
|
|
27
|
+
"@raredays/style": "^0.3.0",
|
|
28
28
|
"lucide-react": "1.14.0",
|
|
29
29
|
"next": "16.2.9",
|
|
30
30
|
"react": "19.2.7",
|
|
@@ -32,11 +32,12 @@
|
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@atheory-ai/skillex": "0.7.2",
|
|
35
|
-
"@raredays/dev": "^0.
|
|
36
|
-
"@raredays/lint": "^0.
|
|
37
|
-
"@raredays/site-config": "^0.
|
|
38
|
-
"@raredays/
|
|
39
|
-
"@raredays/
|
|
35
|
+
"@raredays/dev": "^0.2.0",
|
|
36
|
+
"@raredays/lint": "^0.2.2",
|
|
37
|
+
"@raredays/site-config": "^0.3.0",
|
|
38
|
+
"@raredays/skills": "^0.2.0",
|
|
39
|
+
"@raredays/theme-build": "^0.3.0",
|
|
40
|
+
"@raredays/typescript": "^0.2.1",
|
|
40
41
|
"@storybook/react-vite": "10.3.6",
|
|
41
42
|
"@tailwindcss/postcss": "4.2.4",
|
|
42
43
|
"@tailwindcss/vite": "4.2.4",
|