create-apollo-monorepo 0.9.3 → 0.9.5
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/index.mjs +76 -114
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -62,7 +62,9 @@ ${COLORS.bold}Flags:${COLORS.reset}
|
|
|
62
62
|
--backend-url <url> Backend git URL (default: ${BACKEND_REPO_URL})
|
|
63
63
|
--backend-branch <name> Backend branch to track (default: ${BACKEND_BRANCH})
|
|
64
64
|
-d, --db <url> DATABASE_URL for backend
|
|
65
|
-
-u, --url <url> NEXT_PUBLIC_SITE_URL (
|
|
65
|
+
-u, --url <url> NEXT_PUBLIC_SITE_URL (optional — left blank so the CMS
|
|
66
|
+
respects the incoming request origin. Set this only when
|
|
67
|
+
you need a fixed origin for background jobs / cron / emails.)
|
|
66
68
|
-l, --locale <code> NEXT_PUBLIC_DEFAULT_LOCALE (default: en)
|
|
67
69
|
--admin-prefix <path> Backend prefix in single-origin mode — sets Next.js
|
|
68
70
|
assetPrefix on the backend AND the path the frontend
|
|
@@ -124,6 +126,26 @@ function commandExists(cmd) {
|
|
|
124
126
|
|
|
125
127
|
// ─── Arg Parsing ─────────────────────────────────────────────────────────────
|
|
126
128
|
|
|
129
|
+
// Flag dispatch table. Each entry: aliases → either { key } (takes a value)
|
|
130
|
+
// or { key, value } (boolean flag). `--asset-prefix` is a legacy alias.
|
|
131
|
+
const FLAG_TABLE = {
|
|
132
|
+
"-h": { key: "help", value: true },
|
|
133
|
+
"--help": { key: "help", value: true },
|
|
134
|
+
"--skip-install": { key: "skipInstall", value: true },
|
|
135
|
+
"--skip-submodule": { key: "skipSubmodule", value: true },
|
|
136
|
+
"--frontend-name": { key: "frontendName" },
|
|
137
|
+
"--backend-url": { key: "backendUrl" },
|
|
138
|
+
"--backend-branch": { key: "backendBranch" },
|
|
139
|
+
"-d": { key: "db" },
|
|
140
|
+
"--db": { key: "db" },
|
|
141
|
+
"-u": { key: "url" },
|
|
142
|
+
"--url": { key: "url" },
|
|
143
|
+
"-l": { key: "locale" },
|
|
144
|
+
"--locale": { key: "locale" },
|
|
145
|
+
"--admin-prefix": { key: "adminPrefix" },
|
|
146
|
+
"--asset-prefix": { key: "adminPrefix" },
|
|
147
|
+
};
|
|
148
|
+
|
|
127
149
|
function parseArgs(argv) {
|
|
128
150
|
const args = argv.slice(2);
|
|
129
151
|
const flags = {
|
|
@@ -140,55 +162,16 @@ function parseArgs(argv) {
|
|
|
140
162
|
help: false,
|
|
141
163
|
};
|
|
142
164
|
|
|
143
|
-
let i = 0;
|
|
144
|
-
while (i < args.length) {
|
|
165
|
+
for (let i = 0; i < args.length; i++) {
|
|
145
166
|
const arg = args[i];
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
break;
|
|
151
|
-
case "--frontend-name":
|
|
152
|
-
flags.frontendName = args[++i];
|
|
153
|
-
break;
|
|
154
|
-
case "--backend-url":
|
|
155
|
-
flags.backendUrl = args[++i];
|
|
156
|
-
break;
|
|
157
|
-
case "--backend-branch":
|
|
158
|
-
flags.backendBranch = args[++i];
|
|
159
|
-
break;
|
|
160
|
-
case "-d":
|
|
161
|
-
case "--db":
|
|
162
|
-
flags.db = args[++i];
|
|
163
|
-
break;
|
|
164
|
-
case "-u":
|
|
165
|
-
case "--url":
|
|
166
|
-
flags.url = args[++i];
|
|
167
|
-
break;
|
|
168
|
-
case "-l":
|
|
169
|
-
case "--locale":
|
|
170
|
-
flags.locale = args[++i];
|
|
171
|
-
break;
|
|
172
|
-
case "--admin-prefix":
|
|
173
|
-
case "--asset-prefix": // legacy alias
|
|
174
|
-
flags.adminPrefix = args[++i];
|
|
175
|
-
break;
|
|
176
|
-
case "--skip-install":
|
|
177
|
-
flags.skipInstall = true;
|
|
178
|
-
break;
|
|
179
|
-
case "--skip-submodule":
|
|
180
|
-
flags.skipSubmodule = true;
|
|
181
|
-
break;
|
|
182
|
-
default:
|
|
183
|
-
if (arg.startsWith("-")) {
|
|
184
|
-
fatal(`Unknown flag: ${arg}\nRun with --help for usage.`);
|
|
185
|
-
}
|
|
186
|
-
if (flags.directory) {
|
|
187
|
-
fatal(`Unexpected argument: ${arg}\nOnly one directory name is allowed.`);
|
|
188
|
-
}
|
|
189
|
-
flags.directory = arg;
|
|
167
|
+
const spec = FLAG_TABLE[arg];
|
|
168
|
+
if (spec) {
|
|
169
|
+
flags[spec.key] = "value" in spec ? spec.value : args[++i];
|
|
170
|
+
continue;
|
|
190
171
|
}
|
|
191
|
-
|
|
172
|
+
if (arg.startsWith("-")) fatal(`Unknown flag: ${arg}\nRun with --help for usage.`);
|
|
173
|
+
if (flags.directory) fatal(`Unexpected argument: ${arg}\nOnly one directory name is allowed.`);
|
|
174
|
+
flags.directory = arg;
|
|
192
175
|
}
|
|
193
176
|
|
|
194
177
|
return flags;
|
|
@@ -243,29 +226,22 @@ function preflight(flags) {
|
|
|
243
226
|
}
|
|
244
227
|
success(`Node.js ${process.versions.node}`);
|
|
245
228
|
|
|
246
|
-
if (!commandExists("git")) {
|
|
247
|
-
fatal("git is required but not found.\n Install: https://git-scm.com/");
|
|
248
|
-
}
|
|
249
|
-
success("git found");
|
|
250
|
-
|
|
251
|
-
if (!commandExists("pnpm")) {
|
|
252
|
-
warn("pnpm not found — install with: npm i -g pnpm (required for workspaces)");
|
|
253
|
-
} else {
|
|
254
|
-
success("pnpm found");
|
|
255
|
-
}
|
|
256
|
-
|
|
257
229
|
// bun is required at runtime by apps/backend (apollo-cms): db:push, db:seed,
|
|
258
230
|
// dev:cron, plugins:build, the upgrade pipeline, and pre-commit hooks all
|
|
259
231
|
// shell out to `bun`. The scaffold itself works without bun, but `pnpm dev`,
|
|
260
232
|
// `pnpm backend:setup`, and `pnpm backend:upgrade` will fail without it.
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
233
|
+
const TOOLS = [
|
|
234
|
+
{ cmd: "git", required: true, missing: "git is required but not found.\n Install: https://git-scm.com/" },
|
|
235
|
+
{ cmd: "pnpm", required: false, missing: "pnpm not found — install with: npm i -g pnpm (required for workspaces)" },
|
|
236
|
+
{ cmd: "bun", required: false, missing:
|
|
237
|
+
"bun not found — required by apps/backend for db:push, db:seed, dev:cron,\n" +
|
|
264
238
|
" plugins:build, and the upgrade pipeline.\n" +
|
|
265
|
-
" Install: curl -fsSL https://bun.sh/install | bash (or: brew install bun)",
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
success(
|
|
239
|
+
" Install: curl -fsSL https://bun.sh/install | bash (or: brew install bun)" },
|
|
240
|
+
];
|
|
241
|
+
for (const t of TOOLS) {
|
|
242
|
+
if (commandExists(t.cmd)) success(`${t.cmd} found`);
|
|
243
|
+
else if (t.required) fatal(t.missing);
|
|
244
|
+
else warn(t.missing);
|
|
269
245
|
}
|
|
270
246
|
|
|
271
247
|
const targetDir = resolve(flags.directory);
|
|
@@ -288,24 +264,21 @@ function createPrompt() {
|
|
|
288
264
|
|
|
289
265
|
async function gatherEnv(flags, { singleOrigin }) {
|
|
290
266
|
let dbUrl = flags.db;
|
|
291
|
-
//
|
|
292
|
-
//
|
|
293
|
-
//
|
|
294
|
-
//
|
|
295
|
-
//
|
|
296
|
-
//
|
|
297
|
-
//
|
|
298
|
-
|
|
299
|
-
? `http://localhost:${DEFAULT_FRONTEND_PORT}`
|
|
300
|
-
: `http://localhost:${DEFAULT_BACKEND_PORT}`;
|
|
301
|
-
let siteUrl = flags.url ?? defaultSiteUrl;
|
|
267
|
+
// NEXT_PUBLIC_SITE_URL is intentionally NOT prompted. Apollo CMS resolves
|
|
268
|
+
// the public origin in this priority order: NEXT_PUBLIC_SITE_URL >
|
|
269
|
+
// incoming request origin. Leaving it blank lets the runtime respect the
|
|
270
|
+
// actual request origin (works for localhost, preview URLs, prod domains,
|
|
271
|
+
// and reverse proxies without any reconfiguration). Override with --url
|
|
272
|
+
// only when you need a fixed origin for background jobs (cron / triggered
|
|
273
|
+
// emails) that run outside a request scope.
|
|
274
|
+
let siteUrl = flags.url ?? "";
|
|
302
275
|
let locale = flags.locale ?? "en";
|
|
303
276
|
|
|
304
277
|
if (flags.db && !isValidDbUrl(flags.db)) fatal("--db must start with postgresql:// or postgres://");
|
|
305
278
|
if (flags.url && !isValidUrl(flags.url)) fatal("--url must start with http:// or https://");
|
|
306
279
|
if (flags.locale && !isValidLocale(flags.locale)) fatal("--locale must be a 2-5 character code (e.g., en, th)");
|
|
307
280
|
|
|
308
|
-
const needsPrompt = !dbUrl || !flags.
|
|
281
|
+
const needsPrompt = !dbUrl || !flags.locale;
|
|
309
282
|
if (!needsPrompt) return { dbUrl, siteUrl, locale };
|
|
310
283
|
|
|
311
284
|
const { ask, close } = createPrompt();
|
|
@@ -321,16 +294,6 @@ async function gatherEnv(flags, { singleOrigin }) {
|
|
|
321
294
|
}
|
|
322
295
|
}
|
|
323
296
|
|
|
324
|
-
if (!flags.url) {
|
|
325
|
-
const ans = await ask(
|
|
326
|
-
`\n ${COLORS.bold}NEXT_PUBLIC_SITE_URL${COLORS.reset} ${COLORS.dim}[${siteUrl}]${COLORS.reset}\n > `,
|
|
327
|
-
);
|
|
328
|
-
if (ans) {
|
|
329
|
-
if (isValidUrl(ans)) siteUrl = ans;
|
|
330
|
-
else warn(`Invalid URL, using default: ${siteUrl}`);
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
|
|
334
297
|
if (!flags.locale) {
|
|
335
298
|
const ans = await ask(
|
|
336
299
|
`\n ${COLORS.bold}Default locale${COLORS.reset} ${COLORS.dim}[${locale}]${COLORS.reset}\n > `,
|
|
@@ -966,50 +929,49 @@ function writeRootEnv(targetDir, { dbUrl, siteUrl, locale, authSecret, cronSecre
|
|
|
966
929
|
`DATABASE_URL=${dbUrl}`,
|
|
967
930
|
`APOLLO_SECRET=${authSecret}`,
|
|
968
931
|
`CRON_SECRET=${cronSecret}`,
|
|
969
|
-
"# Public origin.
|
|
970
|
-
"#
|
|
971
|
-
"#
|
|
932
|
+
"# Public origin. Leave blank so the CMS respects the incoming request",
|
|
933
|
+
"# origin (works for localhost, preview URLs, and prod domains automatically).",
|
|
934
|
+
"# Set this ONLY when background jobs (cron / triggered emails) need a fixed",
|
|
935
|
+
"# origin outside a request scope — e.g. NEXT_PUBLIC_SITE_URL=https://cms.example.com",
|
|
972
936
|
`NEXT_PUBLIC_SITE_URL=${siteUrl}`,
|
|
973
937
|
`NEXT_PUBLIC_DEFAULT_LOCALE=${locale}`,
|
|
974
938
|
"",
|
|
975
939
|
];
|
|
976
940
|
|
|
941
|
+
lines.push("# ── Backend (apps/backend) ───────────────────────────────────────");
|
|
977
942
|
if (adminPrefix) {
|
|
978
943
|
lines.push(
|
|
979
|
-
"# ── Backend (apps/backend) ───────────────────────────────────────",
|
|
980
944
|
"# Single-origin admin/asset prefix. The frontend rewrites this path",
|
|
981
945
|
"# to the backend, so backend chunks (served at <prefix>/_next/static)",
|
|
982
946
|
"# and admin pages share one rewrite.",
|
|
983
947
|
`APOLLO_ASSET_PREFIX=${adminPrefix}`,
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
948
|
+
);
|
|
949
|
+
}
|
|
950
|
+
lines.push(
|
|
951
|
+
"# Project-specific plugins — keeps the apollo-cms submodule clean.",
|
|
952
|
+
`APOLLO_EXTRA_PLUGINS_DIR=../cms-plugins`,
|
|
953
|
+
"# PM2 namespace — groups this project's processes so you can",
|
|
954
|
+
"# `pm2 stop <namespace>` / `pm2 restart <namespace>` independently of",
|
|
955
|
+
"# other projects on the same host. Consumed by ecosystem.config.cjs.",
|
|
956
|
+
`PM2_NAMESPACE=${pm2Namespace}`,
|
|
957
|
+
"",
|
|
958
|
+
"# ── Frontend (apps/frontend) ─────────────────────────────────────",
|
|
959
|
+
);
|
|
960
|
+
if (adminPrefix) {
|
|
961
|
+
lines.push(
|
|
992
962
|
"# Where the frontend's rewrites point internally. In dev, leave blank",
|
|
993
963
|
"# to auto-derive http://127.0.0.1:${BACKEND_PORT} via scripts/with-env.mjs.",
|
|
994
964
|
"# In prod set to a private hostname (e.g. http://backend.internal:3000).",
|
|
995
965
|
`BACKEND_INTERNAL_URL=${backendInternalUrl}`,
|
|
996
|
-
"",
|
|
997
966
|
);
|
|
998
967
|
} else {
|
|
999
968
|
lines.push(
|
|
1000
|
-
"#
|
|
1001
|
-
"#
|
|
1002
|
-
`
|
|
1003
|
-
"# PM2 namespace — groups this project's processes so you can",
|
|
1004
|
-
"# `pm2 stop <namespace>` / `pm2 restart <namespace>` independently of",
|
|
1005
|
-
"# other projects on the same host. Consumed by ecosystem.config.cjs.",
|
|
1006
|
-
`PM2_NAMESPACE=${pm2Namespace}`,
|
|
1007
|
-
"",
|
|
1008
|
-
"# ── Frontend (apps/frontend) ─────────────────────────────────────",
|
|
1009
|
-
`NEXT_PUBLIC_BACKEND_URL=${siteUrl}`,
|
|
1010
|
-
"",
|
|
969
|
+
"# Where the frontend reaches the backend in separate-origins mode.",
|
|
970
|
+
"# Defaults to the backend's dev port; override in prod to the real CMS host.",
|
|
971
|
+
`NEXT_PUBLIC_BACKEND_URL=${siteUrl || `http://localhost:${DEFAULT_BACKEND_PORT}`}`,
|
|
1011
972
|
);
|
|
1012
973
|
}
|
|
974
|
+
lines.push("");
|
|
1013
975
|
|
|
1014
976
|
writeFileSync(resolve(targetDir, ".env.local"), lines.join("\n"));
|
|
1015
977
|
}
|
|
@@ -1143,7 +1105,7 @@ export default config;
|
|
|
1143
1105
|
]
|
|
1144
1106
|
: [
|
|
1145
1107
|
`# Public URL of the backend (used by your client code)`,
|
|
1146
|
-
`NEXT_PUBLIC_BACKEND_URL=${siteUrl}`,
|
|
1108
|
+
`NEXT_PUBLIC_BACKEND_URL=${siteUrl || `http://localhost:${DEFAULT_BACKEND_PORT}`}`,
|
|
1147
1109
|
"",
|
|
1148
1110
|
];
|
|
1149
1111
|
writeFileSync(resolve(dir, ".env.local.example"), envLocalLines.join("\n"));
|
|
@@ -2117,7 +2079,7 @@ async function main() {
|
|
|
2117
2079
|
// BACKEND_INTERNAL_URL for the rewrites destination.
|
|
2118
2080
|
const frontendFallback = adminPrefix
|
|
2119
2081
|
? [`BACKEND_INTERNAL_URL=${backendInternalUrl}`]
|
|
2120
|
-
: [`NEXT_PUBLIC_BACKEND_URL=${siteUrl}`];
|
|
2082
|
+
: [`NEXT_PUBLIC_BACKEND_URL=${siteUrl || `http://localhost:${DEFAULT_BACKEND_PORT}`}`];
|
|
2121
2083
|
linkRootEnvLocal(targetDir, FRONTEND_PATH, frontendFallback);
|
|
2122
2084
|
|
|
2123
2085
|
// ── Step 7: Install ──
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-apollo-monorepo",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.5",
|
|
4
4
|
"description": "Scaffold a monorepo with a frontend app and Apollo CMS as a git submodule backend (single-origin via Next.js rewrites + assetPrefix)",
|
|
5
5
|
"bin": {
|
|
6
6
|
"create-apollo-monorepo": "index.mjs"
|