@pylonsync/create-pylon 0.3.18 → 0.3.20

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.
Files changed (2) hide show
  1. package/bin/create-pylon.js +105 -11
  2. package/package.json +1 -1
@@ -25,7 +25,7 @@ import { stdin, stdout, exit, argv, cwd } from "node:process";
25
25
  // of the pylon stack).
26
26
  // ---------------------------------------------------------------------------
27
27
 
28
- const PYLON_VERSION = "0.3.17";
28
+ const PYLON_VERSION = "0.3.20";
29
29
 
30
30
  // ---------------------------------------------------------------------------
31
31
  // CLI args + interactive prompt
@@ -35,9 +35,9 @@ const args = argv.slice(2);
35
35
  let projectName = args.find((a) => !a.startsWith("--"));
36
36
 
37
37
  const flags = {
38
- pm:
39
- args.find((a) => a === "--bun" || a === "--pnpm" || a === "--yarn" || a === "--npm")?.slice(2) ??
40
- detectPackageManager(),
38
+ pm: args.find(
39
+ (a) => a === "--bun" || a === "--pnpm" || a === "--yarn" || a === "--npm",
40
+ )?.slice(2),
41
41
  skipInstall: args.includes("--skip-install"),
42
42
  help: args.includes("--help") || args.includes("-h"),
43
43
  };
@@ -47,11 +47,35 @@ if (flags.help) {
47
47
  exit(0);
48
48
  }
49
49
 
50
+ // Interactive prompts for project name + package manager. Default
51
+ // PM to bun: it handles `workspace:*` correctly out of the box,
52
+ // installs faster than the alternatives, and is what the
53
+ // @pylonsync/* packages are tested against. The user can pick
54
+ // anything though.
55
+ const rl = createInterface({ input: stdin, output: stdout });
50
56
  if (!projectName) {
51
- const rl = createInterface({ input: stdin, output: stdout });
52
57
  projectName = (await rl.question("Project name: ")).trim() || "my-pylon-app";
53
- rl.close();
54
58
  }
59
+ if (!flags.pm) {
60
+ const detected = detectPackageManager();
61
+ const def = detected ?? "bun";
62
+ const choice = (
63
+ await rl.question(`Package manager (bun, pnpm, yarn, npm) [${def}]: `)
64
+ )
65
+ .trim()
66
+ .toLowerCase();
67
+ flags.pm = ["bun", "pnpm", "yarn", "npm"].includes(choice) ? choice : def;
68
+ }
69
+ rl.close();
70
+
71
+ // Some PMs reject the `workspace:` protocol. Bun/pnpm/yarn berry
72
+ // understand it and rewrite to the local sibling version at install
73
+ // time. npm errors EUNSUPPORTEDPROTOCOL ("Unsupported URL Type").
74
+ // For npm, emit "*" — npm's own workspaces feature still resolves
75
+ // it to the local sibling because the workspace package is in the
76
+ // root's `workspaces` list.
77
+ const usesWorkspaceProtocol = flags.pm !== "npm";
78
+ const workspaceDepSpec = usesWorkspaceProtocol ? "workspace:*" : "*";
55
79
 
56
80
  const root = resolve(cwd(), projectName);
57
81
 
@@ -82,6 +106,12 @@ function writeJson(path, value) {
82
106
  // Root workspace
83
107
  // ---------------------------------------------------------------------------
84
108
 
109
+ // Per-PM script syntax: bun has its own --filter, pnpm uses --filter,
110
+ // npm/yarn use --workspace. Picking the right shape at scaffold time
111
+ // means `npm run dev` (or whichever PM) works without the user
112
+ // learning each PM's flag dialect.
113
+ const wsScripts = pmScripts(flags.pm);
114
+
85
115
  writeJson("package.json", {
86
116
  name: projectName,
87
117
  private: true,
@@ -89,9 +119,9 @@ writeJson("package.json", {
89
119
  workspaces: ["apps/*", "packages/*"],
90
120
  scripts: {
91
121
  dev: "npm-run-all --parallel dev:api dev:web",
92
- "dev:api": "npm --workspace apps/api run dev",
93
- "dev:web": "npm --workspace apps/web run dev",
94
- build: "npm --workspaces run build --if-present",
122
+ "dev:api": wsScripts.devApi,
123
+ "dev:web": wsScripts.devWeb,
124
+ build: wsScripts.build,
95
125
  },
96
126
  devDependencies: {
97
127
  "npm-run-all": "^4.1.5",
@@ -526,7 +556,7 @@ writeJson("apps/web/package.json", {
526
556
  lint: "next lint",
527
557
  },
528
558
  dependencies: {
529
- [`@${projectName}/ui`]: "workspace:*",
559
+ [`@${projectName}/ui`]: workspaceDepSpec,
530
560
  "@pylonsync/sdk": `^${PYLON_VERSION}`,
531
561
  "@pylonsync/react": `^${PYLON_VERSION}`,
532
562
  "@pylonsync/next": `^${PYLON_VERSION}`,
@@ -574,7 +604,20 @@ write(
574
604
  * Pylon's typed client + functions packages re-export across the
575
605
  * server/client boundary AND the workspace UI package ships TSX.
576
606
  * \`transpilePackages\` makes Next bundle them cleanly.
607
+ *
608
+ * \`rewrites\` proxies every Pylon-owned path (\`/api/fn/*\`,
609
+ * \`/api/auth/*\`, \`/api/sync/*\`, …) to the Pylon binary running
610
+ * on \`PYLON_API_URL\` (default http://localhost:4321). Without this,
611
+ * Next.js sees \`/api/fn/addTodo\` as a missing route and 404s before
612
+ * the request ever reaches Pylon.
613
+ *
614
+ * In production set \`PYLON_API_URL\` to wherever you've deployed the
615
+ * Pylon binary (Fly, Render, Railway, your own box). The browser
616
+ * still hits same-origin paths under your Next deployment, and Next
617
+ * forwards them server-side — no CORS, no extra DNS.
577
618
  */
619
+ const PYLON_API_URL = process.env.PYLON_API_URL ?? "http://localhost:4321";
620
+
578
621
  const config: NextConfig = {
579
622
  \ttranspilePackages: [
580
623
  \t\t"@${projectName}/ui",
@@ -584,6 +627,14 @@ const config: NextConfig = {
584
627
  \t\t"@pylonsync/functions",
585
628
  \t\t"@pylonsync/sync",
586
629
  \t],
630
+ \tasync rewrites() {
631
+ \t\treturn [
632
+ \t\t\t{ source: "/api/fn/:path*", destination: \`\${PYLON_API_URL}/api/fn/:path*\` },
633
+ \t\t\t{ source: "/api/auth/:path*", destination: \`\${PYLON_API_URL}/api/auth/:path*\` },
634
+ \t\t\t{ source: "/api/sync/:path*", destination: \`\${PYLON_API_URL}/api/sync/:path*\` },
635
+ \t\t\t{ source: "/api/:path*", destination: \`\${PYLON_API_URL}/api/:path*\` },
636
+ \t\t];
637
+ \t},
587
638
  };
588
639
 
589
640
  export default config;
@@ -816,7 +867,50 @@ function detectPackageManager() {
816
867
  if (ua.startsWith("bun")) return "bun";
817
868
  if (ua.startsWith("pnpm")) return "pnpm";
818
869
  if (ua.startsWith("yarn")) return "yarn";
819
- return "npm";
870
+ if (ua.startsWith("npm")) return "npm";
871
+ return null;
872
+ }
873
+
874
+ /**
875
+ * Per-package-manager workspace script syntax. Each PM exposes
876
+ * "run X in workspace Y" differently:
877
+ * bun bun run --filter ./apps/api dev
878
+ * pnpm pnpm --filter ./apps/api run dev
879
+ * yarn yarn workspace @<name>/api run dev
880
+ * npm npm --workspace apps/api run dev
881
+ *
882
+ * The scaffold doesn't try to abstract the PM — it bakes the right
883
+ * syntax into the generated scripts so `<pm> run dev` works
884
+ * everywhere with no further config.
885
+ */
886
+ function pmScripts(pm) {
887
+ switch (pm) {
888
+ case "bun":
889
+ return {
890
+ devApi: "bun run --filter './apps/api' dev",
891
+ devWeb: "bun run --filter './apps/web' dev",
892
+ build: "bun run --filter '*' build",
893
+ };
894
+ case "pnpm":
895
+ return {
896
+ devApi: "pnpm --filter './apps/api' run dev",
897
+ devWeb: "pnpm --filter './apps/web' run dev",
898
+ build: "pnpm --filter '*' run build",
899
+ };
900
+ case "yarn":
901
+ return {
902
+ devApi: `yarn workspace @${projectName}/api run dev`,
903
+ devWeb: `yarn workspace @${projectName}/web run dev`,
904
+ build: "yarn workspaces foreach -A run build",
905
+ };
906
+ case "npm":
907
+ default:
908
+ return {
909
+ devApi: "npm --workspace apps/api run dev",
910
+ devWeb: "npm --workspace apps/web run dev",
911
+ build: "npm --workspaces run build --if-present",
912
+ };
913
+ }
820
914
  }
821
915
 
822
916
  // ---------------------------------------------------------------------------
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pylonsync/create-pylon",
3
- "version": "0.3.18",
3
+ "version": "0.3.20",
4
4
  "description": "Scaffold a new Pylon app — realtime backend + Next.js frontend in one command. Run via `npm create @pylonsync/pylon@latest`.",
5
5
  "publishConfig": {
6
6
  "access": "public"