create-raredays-app 0.1.3 → 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.3",
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>",
@@ -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