create-raredays-app 0.1.1 → 0.1.4

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.
@@ -0,0 +1,47 @@
1
+ // Merge per-module build-script approvals into a template's pnpm-workspace.yaml.
2
+ //
3
+ // pnpm 11 blocks dependency build scripts by default and only honors approvals
4
+ // declared as `allowBuilds` in pnpm-workspace.yaml (the package.json `pnpm`
5
+ // field and .npmrc are ignored). Each scaffold fragment (module or vendor) may
6
+ // declare its own `allowBuilds` map in module.json; the scaffolder unions those
7
+ // onto the template's base so a generated app pre-approves exactly the build
8
+ // scripts its selected modules need — no matter which modules are chosen.
9
+ //
10
+ // Pure string transform over the template's pnpm-workspace.yaml: parse the
11
+ // existing ` <pkg>: true|false` entries, union the fragment-contributed ones
12
+ // (later wins, so a fragment can also opt a build OUT with `false`), and
13
+ // re-emit a sorted block while preserving the file's header comment. We avoid a
14
+ // YAML parser dependency on purpose — the block is a flat name->boolean map.
15
+
16
+ const ENTRY = /^ {2}["']?([^"':\s]+)["']?:\s*(true|false)\s*$/gm;
17
+
18
+ /**
19
+ * @param {string} yamlSource - the template's pnpm-workspace.yaml contents
20
+ * @param {Record<string, boolean>} extra - build approvals contributed by selected fragments
21
+ * @returns {string} the rewritten pnpm-workspace.yaml contents
22
+ */
23
+ export function mergeAllowBuilds(yamlSource, extra) {
24
+ const merged = {};
25
+ ENTRY.lastIndex = 0;
26
+ let match;
27
+ while ((match = ENTRY.exec(yamlSource)) !== null) {
28
+ merged[match[1]] = match[2] === "true";
29
+ }
30
+ for (const [name, value] of Object.entries(extra)) {
31
+ merged[name] = value === true || value === "true";
32
+ }
33
+
34
+ // Preserve everything before `allowBuilds:` (the header comment block).
35
+ const marker = yamlSource.indexOf("allowBuilds:");
36
+ const header = marker > 0 ? yamlSource.slice(0, marker) : "";
37
+
38
+ const lines = Object.keys(merged)
39
+ .sort()
40
+ .map((name) => {
41
+ // Quote names that aren't bare identifiers (e.g. scoped "@sentry/cli").
42
+ const key = /^[a-zA-Z0-9_-]+$/.test(name) ? name : `"${name}"`;
43
+ return ` ${key}: ${merged[name] ? "true" : "false"}`;
44
+ });
45
+
46
+ return `${header}allowBuilds:\n${lines.join("\n")}\n`;
47
+ }
package/bin/index.js CHANGED
@@ -7,6 +7,8 @@ import { fileURLToPath } from "node:url";
7
7
  import { parseArgs } from "node:util";
8
8
  import * as p from "@clack/prompts";
9
9
 
10
+ import { mergeAllowBuilds } from "./compose-allow-builds.mjs";
11
+
10
12
  const __dirname = dirname(fileURLToPath(import.meta.url));
11
13
  const packageRoot = resolve(__dirname, "..");
12
14
  const templatesDir = resolve(packageRoot, "templates");
@@ -520,6 +522,7 @@ async function scaffold(opts) {
520
522
  // Load + apply selected fragments. Static templates don't have a
521
523
  // composition root, so skip module composition entirely there.
522
524
  const collectedDeps = {};
525
+ const collectedBuilds = {};
523
526
  const envVars = [];
524
527
  const vendorMocks = [];
525
528
  if (opts.template !== "static" && opts.selectedModules.length > 0) {
@@ -528,6 +531,7 @@ async function scaffold(opts) {
528
531
 
529
532
  for (const f of valid) {
530
533
  Object.assign(collectedDeps, f.deps ?? {});
534
+ Object.assign(collectedBuilds, f.allowBuilds ?? {});
531
535
  if (f.filesDir) await cp(f.filesDir, targetDir, { recursive: true });
532
536
  }
533
537
 
@@ -537,6 +541,7 @@ async function scaffold(opts) {
537
541
  const vendor = f?.vendors?.find((v) => v.key === vendorKey);
538
542
  if (!vendor) continue;
539
543
  Object.assign(collectedDeps, vendor.deps ?? {});
544
+ Object.assign(collectedBuilds, vendor.allowBuilds ?? {});
540
545
  for (const v of vendor.env ?? []) envVars.push(v);
541
546
  if (vendor.mocks) {
542
547
  vendorMocks.push(vendor.mocks);
@@ -550,6 +555,16 @@ async function scaffold(opts) {
550
555
  await writeFile(runtimePath, composeRuntime(source, valid));
551
556
  }
552
557
 
558
+ // Union module/vendor build-script approvals onto the template's
559
+ // pnpm-workspace.yaml so the generated app pre-approves exactly what its
560
+ // selected modules need under pnpm 11. No-op when no fragment declares any.
561
+ if (Object.keys(collectedBuilds).length > 0) {
562
+ const wsPath = resolve(targetDir, "pnpm-workspace.yaml");
563
+ if (existsSync(wsPath)) {
564
+ await writeFile(wsPath, mergeAllowBuilds(await readFile(wsPath, "utf8"), collectedBuilds));
565
+ }
566
+ }
567
+
553
568
  // Compose mocks/handlers.ts for any template that ships it (full-stack + client).
554
569
  if (opts.template !== "static") {
555
570
  const handlersPath = resolve(targetDir, "mocks/handlers.ts");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-raredays-app",
3
- "version": "0.1.1",
3
+ "version": "0.1.4",
4
4
  "description": "Scaffold a new RareDays Next.js app.",
5
5
  "license": "UNLICENSED",
6
6
  "author": "Jeremy Harper <jeremy@raredays.com>",
@@ -3,14 +3,14 @@
3
3
  "label": "Billing (subscriptions + webhooks)",
4
4
  "description": "Plan registry, subscription state, webhook handler with idempotency + event-bus fan-out.",
5
5
  "deps": {
6
- "@raredays/billing": "0.0.1"
6
+ "@raredays/billing": "^0.1.0"
7
7
  },
8
8
  "vendors": [
9
9
  {
10
10
  "key": "stripe",
11
11
  "label": "Stripe",
12
12
  "deps": {
13
- "@raredays/billing-stripe": "0.0.1"
13
+ "@raredays/billing-stripe": "^0.1.0"
14
14
  },
15
15
  "env": [
16
16
  "STRIPE_SECRET_KEY",
@@ -3,14 +3,14 @@
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.0.1"
6
+ "@raredays/cms": "^0.1.0"
7
7
  },
8
8
  "vendors": [
9
9
  {
10
10
  "key": "hygraph",
11
11
  "label": "Hygraph",
12
12
  "deps": {
13
- "@raredays/cms-hygraph": "0.0.1"
13
+ "@raredays/cms-hygraph": "^0.1.0"
14
14
  },
15
15
  "env": [
16
16
  "HYGRAPH_ENDPOINT",
@@ -3,14 +3,14 @@
3
3
  "label": "Transactional email",
4
4
  "description": "Vendor-neutral email port. Console adapter for dev; swap to Resend for production.",
5
5
  "deps": {
6
- "@raredays/email": "0.0.1"
6
+ "@raredays/email": "^0.1.0"
7
7
  },
8
8
  "vendors": [
9
9
  {
10
10
  "key": "resend",
11
11
  "label": "Resend",
12
12
  "deps": {
13
- "@raredays/email-resend": "0.0.1"
13
+ "@raredays/email-resend": "^0.1.0"
14
14
  },
15
15
  "env": [
16
16
  "RESEND_API_KEY"
@@ -3,7 +3,7 @@
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": "0.0.1"
6
+ "@raredays/i18n": "^0.1.0"
7
7
  },
8
8
  "templates": ["full-stack", "client", "static"]
9
9
  }
@@ -3,14 +3,14 @@
3
3
  "label": "Identity (auth, sessions, organizations, roles)",
4
4
  "description": "Identity port is always wired with an in-memory dev provider. Select a vendor to add the production adapter, env vars, mock handlers, and Next.js auth route stub.",
5
5
  "deps": {
6
- "@raredays/identity": "0.0.1"
6
+ "@raredays/identity": "^0.1.0"
7
7
  },
8
8
  "vendors": [
9
9
  {
10
10
  "key": "better-auth",
11
11
  "label": "Better Auth (self-hosted)",
12
12
  "deps": {
13
- "@raredays/identity-better-auth": "0.0.1",
13
+ "@raredays/identity-better-auth": "^0.1.0",
14
14
  "better-auth": "^1.2.0"
15
15
  },
16
16
  "env": [
@@ -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.0.1"
6
+ "@raredays/jobs": "^0.1.0"
7
7
  },
8
8
  "vendors": [],
9
9
  "templates": [
@@ -3,7 +3,7 @@
3
3
  "label": "Notifications (multi-channel send)",
4
4
  "description": "Notification definitions, channels (email + in-app), user preferences, dedupe, delivery logging.",
5
5
  "deps": {
6
- "@raredays/notifications": "0.0.1"
6
+ "@raredays/notifications": "^0.1.0"
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.0.1"
6
+ "@raredays/organizations": "^0.1.0"
7
7
  },
8
8
  "vendors": [],
9
9
  "templates": [
@@ -3,14 +3,14 @@
3
3
  "label": "Telemetry (logging, errors, metrics, events)",
4
4
  "description": "Vendor-neutral observability port. Console adapters for dev; swap to Sentry + Axiom for production.",
5
5
  "deps": {
6
- "@raredays/telemetry": "0.0.1"
6
+ "@raredays/telemetry": "^0.1.0"
7
7
  },
8
8
  "vendors": [
9
9
  {
10
10
  "key": "sentry",
11
11
  "label": "Sentry (error reporting)",
12
12
  "deps": {
13
- "@raredays/telemetry-sentry": "0.0.1"
13
+ "@raredays/telemetry-sentry": "^0.1.0"
14
14
  },
15
15
  "env": [
16
16
  "SENTRY_DSN"
@@ -24,7 +24,7 @@
24
24
  "key": "axiom",
25
25
  "label": "Axiom (logging)",
26
26
  "deps": {
27
- "@raredays/telemetry-axiom": "0.0.1"
27
+ "@raredays/telemetry-axiom": "^0.1.0"
28
28
  },
29
29
  "env": [
30
30
  "AXIOM_TOKEN",
@@ -0,0 +1,9 @@
1
+ # pnpm 11 blocks dependency build scripts by default, and its pre-run deps
2
+ # check then aborts every pnpm command (install/build/exec) with
3
+ # ERR_PNPM_IGNORED_BUILDS. Pre-approve the builds this app actually needs so a
4
+ # fresh scaffold works out of the box. (In pnpm 11 `allowBuilds` replaced
5
+ # `onlyBuiltDependencies`; values are booleans, not a list.)
6
+ allowBuilds:
7
+ esbuild: true
8
+ msw: true
9
+ sharp: true
@@ -0,0 +1,9 @@
1
+ # pnpm 11 blocks dependency build scripts by default, and its pre-run deps
2
+ # check then aborts every pnpm command (install/build/exec) with
3
+ # ERR_PNPM_IGNORED_BUILDS. Pre-approve the builds this app actually needs so a
4
+ # fresh scaffold works out of the box. (In pnpm 11 `allowBuilds` replaced
5
+ # `onlyBuiltDependencies`; values are booleans, not a list.)
6
+ allowBuilds:
7
+ esbuild: true
8
+ msw: true
9
+ sharp: true
@@ -0,0 +1,8 @@
1
+ # pnpm 11 blocks dependency build scripts by default, and its pre-run deps
2
+ # check then aborts every pnpm command (install/build/exec) with
3
+ # ERR_PNPM_IGNORED_BUILDS. Pre-approve the builds this app actually needs so a
4
+ # fresh scaffold works out of the box. (In pnpm 11 `allowBuilds` replaced
5
+ # `onlyBuiltDependencies`; values are booleans, not a list.)
6
+ allowBuilds:
7
+ esbuild: true
8
+ sharp: true