create-convex-monorepo 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.
Files changed (93) hide show
  1. package/CONTRIBUTING.md +55 -0
  2. package/LICENSE +21 -0
  3. package/README.md +213 -0
  4. package/assets/backend/PROVENANCE.md +3 -0
  5. package/assets/backend/convex/_generated/api.d.ts +51 -0
  6. package/assets/backend/convex/_generated/api.js +23 -0
  7. package/assets/backend/convex/_generated/dataModel.d.ts +60 -0
  8. package/assets/backend/convex/_generated/server.d.ts +162 -0
  9. package/assets/backend/convex/_generated/server.js +101 -0
  10. package/assets/backend/convex/messages.ts +38 -0
  11. package/assets/backend/convex/schema.ts +9 -0
  12. package/assets/backend/convex/tsconfig.json +26 -0
  13. package/assets/backend-blank/PROVENANCE.md +3 -0
  14. package/assets/backend-blank/convex/_generated/api.d.ts +45 -0
  15. package/assets/backend-blank/convex/_generated/api.js +23 -0
  16. package/assets/backend-blank/convex/_generated/dataModel.d.ts +60 -0
  17. package/assets/backend-blank/convex/_generated/server.d.ts +162 -0
  18. package/assets/backend-blank/convex/_generated/server.js +101 -0
  19. package/assets/backend-blank/convex/schema.ts +3 -0
  20. package/assets/setup/convex-setup.mjs +250 -0
  21. package/dist/cli/index.d.ts +2 -0
  22. package/dist/cli/index.js +19 -0
  23. package/dist/cli/index.js.map +1 -0
  24. package/dist/commands/create.d.ts +8 -0
  25. package/dist/commands/create.js +182 -0
  26. package/dist/commands/create.js.map +1 -0
  27. package/dist/generator/context.d.ts +3 -0
  28. package/dist/generator/context.js +81 -0
  29. package/dist/generator/context.js.map +1 -0
  30. package/dist/generator/index.d.ts +12 -0
  31. package/dist/generator/index.js +123 -0
  32. package/dist/generator/index.js.map +1 -0
  33. package/dist/generator/options.d.ts +15 -0
  34. package/dist/generator/options.js +85 -0
  35. package/dist/generator/options.js.map +1 -0
  36. package/dist/generator/types.d.ts +47 -0
  37. package/dist/generator/types.js +2 -0
  38. package/dist/generator/types.js.map +1 -0
  39. package/dist/index.d.ts +5 -0
  40. package/dist/index.js +3 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/integrations/auth/clerk/index.d.ts +2 -0
  43. package/dist/integrations/auth/clerk/index.js +114 -0
  44. package/dist/integrations/auth/clerk/index.js.map +1 -0
  45. package/dist/integrations/auth/index.d.ts +2 -0
  46. package/dist/integrations/auth/index.js +7 -0
  47. package/dist/integrations/auth/index.js.map +1 -0
  48. package/dist/integrations/auth/none/index.d.ts +2 -0
  49. package/dist/integrations/auth/none/index.js +18 -0
  50. package/dist/integrations/auth/none/index.js.map +1 -0
  51. package/dist/integrations/auth/shared.d.ts +11 -0
  52. package/dist/integrations/auth/shared.js +40 -0
  53. package/dist/integrations/auth/shared.js.map +1 -0
  54. package/dist/package-manager/index.d.ts +6 -0
  55. package/dist/package-manager/index.js +22 -0
  56. package/dist/package-manager/index.js.map +1 -0
  57. package/dist/templates/apps/expo/index.d.ts +2 -0
  58. package/dist/templates/apps/expo/index.js +91 -0
  59. package/dist/templates/apps/expo/index.js.map +1 -0
  60. package/dist/templates/apps/index.d.ts +2 -0
  61. package/dist/templates/apps/index.js +11 -0
  62. package/dist/templates/apps/index.js.map +1 -0
  63. package/dist/templates/apps/next/index.d.ts +2 -0
  64. package/dist/templates/apps/next/index.js +66 -0
  65. package/dist/templates/apps/next/index.js.map +1 -0
  66. package/dist/templates/apps/shared.d.ts +24 -0
  67. package/dist/templates/apps/shared.js +124 -0
  68. package/dist/templates/apps/shared.js.map +1 -0
  69. package/dist/templates/apps/tanstack-start/index.d.ts +2 -0
  70. package/dist/templates/apps/tanstack-start/index.js +82 -0
  71. package/dist/templates/apps/tanstack-start/index.js.map +1 -0
  72. package/dist/templates/apps/vite/index.d.ts +2 -0
  73. package/dist/templates/apps/vite/index.js +42 -0
  74. package/dist/templates/apps/vite/index.js.map +1 -0
  75. package/dist/templates/backend/index.d.ts +2 -0
  76. package/dist/templates/backend/index.js +58 -0
  77. package/dist/templates/backend/index.js.map +1 -0
  78. package/dist/templates/root/index.d.ts +2 -0
  79. package/dist/templates/root/index.js +178 -0
  80. package/dist/templates/root/index.js.map +1 -0
  81. package/dist/templates/versions.d.ts +18 -0
  82. package/dist/templates/versions.js +19 -0
  83. package/dist/templates/versions.js.map +1 -0
  84. package/dist/version.d.ts +1 -0
  85. package/dist/version.js +11 -0
  86. package/dist/version.js.map +1 -0
  87. package/docs/adding-a-framework.md +27 -0
  88. package/docs/adding-an-auth-provider.md +31 -0
  89. package/docs/architecture.md +49 -0
  90. package/docs/releases.md +78 -0
  91. package/docs/research.md +76 -0
  92. package/docs/verification.md +78 -0
  93. package/package.json +77 -0
@@ -0,0 +1,250 @@
1
+ // @ts-check
2
+ import { spawn } from 'node:child_process';
3
+ import { lstat, readFile, writeFile } from 'node:fs/promises';
4
+ import { createRequire } from 'node:module';
5
+ import { dirname, join, resolve, relative, sep } from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+ import { parseEnv } from 'node:util';
8
+
9
+ /** @param {string} framework */
10
+ export function publicVariable(framework) {
11
+ switch (framework) {
12
+ case 'next':
13
+ return 'NEXT_PUBLIC_CONVEX_URL';
14
+ case 'vite':
15
+ case 'tanstack-start':
16
+ return 'VITE_CONVEX_URL';
17
+ case 'expo':
18
+ return 'EXPO_PUBLIC_CONVEX_URL';
19
+ default:
20
+ throw new Error(
21
+ `Unknown framework in convex-monorepo.json: ${framework}`,
22
+ );
23
+ }
24
+ }
25
+
26
+ /** Reject links before reading or updating local configuration.
27
+ * @param {string} root @param {string} file
28
+ */
29
+ async function safeFile(root, file) {
30
+ const path = relative(root, file);
31
+ if (!path || path.startsWith(`..${sep}`) || path === '..')
32
+ throw new Error('Invalid environment file path.');
33
+ let current = root;
34
+ for (const part of path.split(sep)) {
35
+ current = join(current, part);
36
+ try {
37
+ if ((await lstat(current)).isSymbolicLink())
38
+ throw new Error(`Refusing symlink: ${current}`);
39
+ } catch (error) {
40
+ if (
41
+ !(error instanceof Error && 'code' in error && error.code === 'ENOENT')
42
+ )
43
+ throw error;
44
+ }
45
+ }
46
+ }
47
+
48
+ /** @param {string} file */
49
+ async function readOptional(file) {
50
+ try {
51
+ return await readFile(file, 'utf8');
52
+ } catch (error) {
53
+ if (error instanceof Error && 'code' in error && error.code === 'ENOENT')
54
+ return '';
55
+ throw error;
56
+ }
57
+ }
58
+
59
+ /** @param {string | undefined} value */
60
+ export function deploymentUrl(value) {
61
+ if (!value)
62
+ throw new Error(
63
+ 'CONVEX_URL is missing from packages/backend/.env.local or .env. Run pnpm convex:setup first.',
64
+ );
65
+ let url;
66
+ try {
67
+ url = new URL(value);
68
+ } catch {
69
+ throw new Error('The backend CONVEX_URL is not a valid deployment URL.');
70
+ }
71
+ if (
72
+ !['http:', 'https:'].includes(url.protocol) ||
73
+ url.username ||
74
+ url.password ||
75
+ url.search ||
76
+ url.hash ||
77
+ url.pathname !== '/' ||
78
+ /\s/.test(value)
79
+ ) {
80
+ throw new Error(
81
+ 'CONVEX_URL must be an HTTP(S) origin without credentials, a path, query parameters or a fragment.',
82
+ );
83
+ }
84
+ return url.origin;
85
+ }
86
+
87
+ /** Preserve existing text, including multiline values and comments. dotenv uses
88
+ * the last assignment. Re-running with the same URL makes no changes.
89
+ * @param {string} contents @param {string} variable @param {string} url
90
+ */
91
+ export function linkEnvironment(contents, variable, url) {
92
+ if (parseEnv(contents)[variable] === url) return contents;
93
+ const newline = contents.includes('\r\n') ? '\r\n' : '\n';
94
+ const separator = contents && !contents.endsWith('\n') ? newline : '';
95
+ return `${contents}${separator}${variable}=${url}${newline}`;
96
+ }
97
+
98
+ /** @param {string} root @param {AbortSignal} [signal] */
99
+ export async function linkFrontends(root, signal) {
100
+ signal?.throwIfAborted();
101
+ root = resolve(root);
102
+ const configFile = join(root, 'convex-monorepo.json');
103
+ await safeFile(root, configFile);
104
+ /** @type {unknown} */
105
+ const config = JSON.parse(await readFile(configFile, 'utf8'));
106
+ if (
107
+ !config ||
108
+ typeof config !== 'object' ||
109
+ !('apps' in config) ||
110
+ !Array.isArray(config.apps) ||
111
+ config.apps.length === 0
112
+ ) {
113
+ throw new Error('convex-monorepo.json must list the applications to link.');
114
+ }
115
+ /** @type {Record<string, string | undefined>} */
116
+ let backend = {};
117
+ for (const name of ['.env', '.env.local']) {
118
+ const file = join(root, 'packages/backend', name);
119
+ await safeFile(root, file);
120
+ backend = { ...backend, ...parseEnv(await readOptional(file)) };
121
+ }
122
+ const url = deploymentUrl(backend.CONVEX_URL);
123
+ /** @type {Array<{ file: string, before: string, after: string, name: string }>} */
124
+ const updates = [];
125
+ const names = new Set();
126
+ let hasExpo = false;
127
+ // Preflight every app before changing any environment file.
128
+ for (const app of /** @type {unknown[]} */ (config.apps)) {
129
+ if (
130
+ !app ||
131
+ typeof app !== 'object' ||
132
+ !('name' in app) ||
133
+ typeof app.name !== 'string' ||
134
+ !/^[a-z0-9][a-z0-9-]{0,99}$/.test(app.name) ||
135
+ !('framework' in app) ||
136
+ typeof app.framework !== 'string' ||
137
+ names.has(app.name)
138
+ ) {
139
+ throw new Error(
140
+ 'Invalid or duplicate application in convex-monorepo.json.',
141
+ );
142
+ }
143
+ signal?.throwIfAborted();
144
+ hasExpo ||= app.framework === 'expo';
145
+ names.add(app.name);
146
+ const variable = publicVariable(app.framework);
147
+ const file = join(root, 'apps', app.name, '.env.local');
148
+ await safeFile(root, file);
149
+ if (!(await lstat(dirname(file))).isDirectory())
150
+ throw new Error(`Application directory is missing: ${app.name}`);
151
+ const before = await readOptional(file);
152
+ updates.push({
153
+ file,
154
+ before,
155
+ after: linkEnvironment(before, variable, url),
156
+ name: app.name,
157
+ });
158
+ }
159
+ for (const update of updates) {
160
+ signal?.throwIfAborted();
161
+ await safeFile(root, update.file);
162
+ if ((await readOptional(update.file)) !== update.before)
163
+ throw new Error(
164
+ `Environment changed while linking ${update.name}. Run pnpm convex:link again.`,
165
+ );
166
+ signal?.throwIfAborted();
167
+ if (update.after !== update.before)
168
+ await writeFile(update.file, update.after, { mode: 0o600 });
169
+ signal?.throwIfAborted();
170
+ console.log(`Linked apps/${update.name}/.env.local`);
171
+ }
172
+ signal?.throwIfAborted();
173
+ if (
174
+ hasExpo &&
175
+ ['localhost', '127.0.0.1', '[::1]'].includes(new URL(url).hostname)
176
+ ) {
177
+ console.log(
178
+ 'Local Convex URL linked. Physical phones need a reachable development URL; localhost points to the phone itself.',
179
+ );
180
+ }
181
+ }
182
+
183
+ /** @param {string} root @param {AbortSignal} [signal] */
184
+ export async function initializeConvex(root, signal) {
185
+ const backend = join(root, 'packages/backend');
186
+ const require = createRequire(join(backend, 'package.json'));
187
+ let cli;
188
+ try {
189
+ cli = join(dirname(require.resolve('convex/package.json')), 'bin/main.js');
190
+ } catch {
191
+ throw new Error(
192
+ 'Convex is not installed. Run pnpm install, then pnpm convex:setup.',
193
+ );
194
+ }
195
+ console.log('Initializing Convex. Complete the Convex CLI prompts below.');
196
+ await new Promise((resolve, reject) => {
197
+ const child = spawn(process.execPath, [cli, 'dev', '--once'], {
198
+ cwd: backend,
199
+ stdio: 'inherit',
200
+ ...(signal ? { signal } : {}),
201
+ });
202
+ /** @type {Error | undefined} */
203
+ let processError;
204
+ child.once('error', (error) => {
205
+ processError = error;
206
+ });
207
+ // Abort errors arrive before shutdown. Wait until the child releases its files.
208
+ child.once('close', (code, interrupted) =>
209
+ processError
210
+ ? reject(processError)
211
+ : code === 0
212
+ ? resolve(undefined)
213
+ : reject(
214
+ new Error(
215
+ `Convex setup stopped (${interrupted ?? `exit ${code}`}). Project files are preserved; frontend URLs were not changed. Resolve the error above and run pnpm convex:setup again. For Clerk, configure CLERK_JWT_ISSUER_DOMAIN on the deployment as described in README.md.`,
216
+ ),
217
+ ),
218
+ );
219
+ });
220
+ signal?.throwIfAborted();
221
+ await linkFrontends(root, signal);
222
+ }
223
+
224
+ if (
225
+ process.argv[1] &&
226
+ resolve(process.argv[1]) === fileURLToPath(import.meta.url)
227
+ ) {
228
+ const controller = new AbortController();
229
+ const interrupt = () =>
230
+ controller.abort(new Error('Convex setup cancelled.'));
231
+ process.once('SIGINT', interrupt);
232
+ process.once('SIGTERM', interrupt);
233
+ try {
234
+ const args = process.argv.slice(2);
235
+ if (args.length && !(args.length === 1 && args[0] === '--link-only'))
236
+ throw new Error('Usage: pnpm convex:setup or pnpm convex:link');
237
+ const root = fileURLToPath(new URL('../', import.meta.url));
238
+ if (args[0] === '--link-only') await linkFrontends(root, controller.signal);
239
+ else await initializeConvex(root, controller.signal);
240
+ controller.signal.throwIfAborted();
241
+ } catch (error) {
242
+ console.error(
243
+ error instanceof Error ? error.message : 'Convex setup failed.',
244
+ );
245
+ process.exitCode = controller.signal.aborted ? 130 : 1;
246
+ } finally {
247
+ process.removeListener('SIGINT', interrupt);
248
+ process.removeListener('SIGTERM', interrupt);
249
+ }
250
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ import { getPackageVersion } from '../version.js';
3
+ import { runCreate } from '../commands/create.js';
4
+ const controller = new AbortController();
5
+ const interrupt = () => controller.abort(new Error('Generation interrupted.'));
6
+ process.once('SIGINT', interrupt);
7
+ process.once('SIGTERM', interrupt);
8
+ try {
9
+ await runCreate(process.argv.slice(2), await getPackageVersion(), controller.signal);
10
+ }
11
+ catch (error) {
12
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
13
+ process.exitCode = controller.signal.aborted ? 130 : 1;
14
+ }
15
+ finally {
16
+ process.removeListener('SIGINT', interrupt);
17
+ process.removeListener('SIGTERM', interrupt);
18
+ }
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;AACzC,MAAM,SAAS,GAAG,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC,CAAC;AAC/E,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;AAClC,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;AACnC,IAAI,CAAC;IACH,MAAM,SAAS,CACb,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EACrB,MAAM,iBAAiB,EAAE,EACzB,UAAU,CAAC,MAAM,CAClB,CAAC;AACJ,CAAC;AAAC,OAAO,KAAK,EAAE,CAAC;IACf,OAAO,CAAC,KAAK,CACX,UAAU,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CACnE,CAAC;IACF,OAAO,CAAC,QAAQ,GAAG,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AACzD,CAAC;QAAS,CAAC;IACT,OAAO,CAAC,cAAc,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAC5C,OAAO,CAAC,cAAc,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { type RawOptions } from '../generator/options.js';
2
+ export declare const help = "create-convex-monorepo [project-name] [options]\n\nGenerate a pnpm + Turborepo workspace sharing one Convex backend.\n\n --apps <list> next,vite,tanstack-start,expo or web:next,admin:vite\n --example <name> messages (default) or none for blank apps\n --auth <provider> none (default) or clerk\n --package-manager <name> pnpm (v0.1)\n --install / --no-install Install generated dependencies\n --init-convex / --no-init-convex Set up Convex and link URLs (requires install)\n --git / --no-git Initialize a git repository\n --yes, -y Accept defaults, including installation and git\n --help, -h Show usage\n --version, -v Show version\n\nWithout a terminal, prompts are disabled. Installation and git default to off\nunless explicitly enabled or --yes is passed. Convex setup requires --init-convex\nwhen prompts are skipped; --yes does not opt into Convex account setup.\nNamed apps may repeat a framework.\n";
3
+ export declare function parseCommand(args: string[]): {
4
+ raw: RawOptions;
5
+ help: boolean;
6
+ version: boolean;
7
+ };
8
+ export declare function runCreate(args: string[], version: string, signal?: AbortSignal): Promise<void>;
@@ -0,0 +1,182 @@
1
+ import * as prompts from '@clack/prompts';
2
+ import { parseArgs } from 'node:util';
3
+ import { generateProject } from '../generator/index.js';
4
+ import { frameworks, normalizeOptions, validateProjectName, } from '../generator/options.js';
5
+ export const help = `create-convex-monorepo [project-name] [options]
6
+
7
+ Generate a pnpm + Turborepo workspace sharing one Convex backend.
8
+
9
+ --apps <list> next,vite,tanstack-start,expo or web:next,admin:vite
10
+ --example <name> messages (default) or none for blank apps
11
+ --auth <provider> none (default) or clerk
12
+ --package-manager <name> pnpm (v0.1)
13
+ --install / --no-install Install generated dependencies
14
+ --init-convex / --no-init-convex Set up Convex and link URLs (requires install)
15
+ --git / --no-git Initialize a git repository
16
+ --yes, -y Accept defaults, including installation and git
17
+ --help, -h Show usage
18
+ --version, -v Show version
19
+
20
+ Without a terminal, prompts are disabled. Installation and git default to off
21
+ unless explicitly enabled or --yes is passed. Convex setup requires --init-convex
22
+ when prompts are skipped; --yes does not opt into Convex account setup.
23
+ Named apps may repeat a framework.
24
+ `;
25
+ export function parseCommand(args) {
26
+ const { values, positionals } = parseArgs({
27
+ args,
28
+ allowPositionals: true,
29
+ options: {
30
+ apps: { type: 'string' },
31
+ auth: { type: 'string' },
32
+ example: { type: 'string' },
33
+ 'package-manager': { type: 'string' },
34
+ 'init-convex': { type: 'boolean' },
35
+ 'no-init-convex': { type: 'boolean' },
36
+ install: { type: 'boolean' },
37
+ 'no-install': { type: 'boolean' },
38
+ git: { type: 'boolean' },
39
+ 'no-git': { type: 'boolean' },
40
+ yes: { type: 'boolean', short: 'y' },
41
+ help: { type: 'boolean', short: 'h' },
42
+ version: { type: 'boolean', short: 'v' },
43
+ },
44
+ });
45
+ if (positionals.length > 1)
46
+ throw new Error('Expected one project name. Use --apps to select applications.');
47
+ if (values.install && values['no-install'])
48
+ throw new Error('Choose either --install or --no-install.');
49
+ if (values.git && values['no-git'])
50
+ throw new Error('Choose either --git or --no-git.');
51
+ if (values['init-convex'] && values['no-init-convex'])
52
+ throw new Error('Choose either --init-convex or --no-init-convex.');
53
+ const raw = {};
54
+ if (values['init-convex'] !== undefined ||
55
+ values['no-init-convex'] !== undefined)
56
+ raw.initConvex = !values['no-init-convex'] && !!values['init-convex'];
57
+ if (positionals[0] !== undefined)
58
+ raw.name = positionals[0];
59
+ if (values.apps !== undefined)
60
+ raw.apps = values.apps;
61
+ if (values.auth !== undefined)
62
+ raw.auth = values.auth;
63
+ if (values.example !== undefined)
64
+ raw.example = values.example;
65
+ if (values['package-manager'] !== undefined)
66
+ raw.packageManager = values['package-manager'];
67
+ if (values.yes !== undefined)
68
+ raw.yes = values.yes;
69
+ if (values.install !== undefined || values['no-install'] !== undefined)
70
+ raw.install = values['no-install'] ? false : (values.install ?? false);
71
+ if (values.git !== undefined || values['no-git'] !== undefined)
72
+ raw.git = values['no-git'] ? false : (values.git ?? false);
73
+ return { raw, help: values.help ?? false, version: values.version ?? false };
74
+ }
75
+ function answer(value) {
76
+ if (prompts.isCancel(value))
77
+ throw new Error('Generation cancelled. No project was created.');
78
+ return value;
79
+ }
80
+ export async function runCreate(args, version, signal) {
81
+ const parsed = parseCommand(args);
82
+ if (parsed.help) {
83
+ console.log(help);
84
+ return;
85
+ }
86
+ if (parsed.version) {
87
+ console.log(version);
88
+ return;
89
+ }
90
+ const raw = parsed.raw;
91
+ if (process.stdin.isTTY && process.stdout.isTTY && !raw.yes) {
92
+ prompts.intro('create-convex-monorepo');
93
+ if (!raw.name)
94
+ raw.name = answer(await prompts.text({
95
+ message: 'Project name?',
96
+ placeholder: 'my-app',
97
+ defaultValue: 'my-app',
98
+ validate: (value) => validateProjectName(value ?? ''),
99
+ }));
100
+ if (!raw.packageManager)
101
+ raw.packageManager = answer(await prompts.select({
102
+ message: 'Package manager?',
103
+ options: [{ value: 'pnpm', label: 'pnpm' }],
104
+ }));
105
+ if (!raw.apps) {
106
+ const apps = [];
107
+ do {
108
+ const framework = answer(await prompts.select({
109
+ message: 'Application framework?',
110
+ options: frameworks.map((value) => ({
111
+ value,
112
+ label: {
113
+ next: 'Next.js',
114
+ vite: 'Vite + React',
115
+ 'tanstack-start': 'TanStack Start',
116
+ expo: 'Expo / React Native',
117
+ }[value],
118
+ })),
119
+ }));
120
+ const defaultName = framework === 'expo' ? 'mobile' : apps.length === 0 ? 'web' : 'admin';
121
+ const name = answer(await prompts.text({
122
+ message: 'Application name?',
123
+ placeholder: defaultName,
124
+ defaultValue: defaultName,
125
+ validate(value) {
126
+ return (validateProjectName(value ?? '') ??
127
+ (apps.some((app) => app.name === value)
128
+ ? 'Choose a unique application name.'
129
+ : undefined));
130
+ },
131
+ }));
132
+ apps.push({ name, framework });
133
+ } while (answer(await prompts.confirm({
134
+ message: 'Add another frontend?',
135
+ initialValue: false,
136
+ })));
137
+ raw.apps = apps;
138
+ }
139
+ if (!raw.auth)
140
+ raw.auth = answer(await prompts.select({
141
+ message: 'Authentication?',
142
+ options: [
143
+ { value: 'none', label: 'None' },
144
+ { value: 'clerk', label: 'Clerk' },
145
+ ],
146
+ }));
147
+ if (raw.example === undefined)
148
+ raw.example = answer(await prompts.select({
149
+ message: 'Starter content?',
150
+ initialValue: 'messages',
151
+ options: [
152
+ { value: 'messages', label: 'Messages example' },
153
+ { value: 'none', label: 'Blank project' },
154
+ ],
155
+ }));
156
+ if (raw.git === undefined)
157
+ raw.git = answer(await prompts.confirm({
158
+ message: 'Initialize git?',
159
+ initialValue: true,
160
+ }));
161
+ if (raw.initConvex === undefined && raw.install !== false) {
162
+ raw.initConvex = answer(await prompts.confirm({
163
+ message: 'Initialize Convex and link frontend URLs? (installs dependencies)',
164
+ initialValue: true,
165
+ }));
166
+ }
167
+ if (raw.initConvex && raw.install !== false)
168
+ raw.install = true;
169
+ if (raw.install === undefined)
170
+ raw.install = answer(await prompts.confirm({
171
+ message: 'Install dependencies?',
172
+ initialValue: true,
173
+ }));
174
+ }
175
+ const options = normalizeOptions(raw);
176
+ await generateProject(options, {
177
+ ...(signal ? { signal } : {}),
178
+ onProgress: (message) => console.log(`✓ ${message}`),
179
+ });
180
+ console.log(`✓ Created ${options.name}\n\nNext:\n\n cd ${options.name}\n${options.install ? '' : ' pnpm install\n'}${options.initConvex ? ' pnpm dev' : ' pnpm convex:setup\n pnpm dev'}\n\n${options.initConvex ? 'Frontend Convex URLs are linked.' : 'convex:setup initializes the backend and links its public URL to every frontend.'}${options.auth === 'clerk' ? '\nAdd Clerk keys from .env.clerk.example and complete the auth setup in README.md.' : ''}`);
181
+ }
182
+ //# sourceMappingURL=create.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.js","sourceRoot":"","sources":["../../src/commands/create.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { GeneratorContext, PackageManifest, ProjectOptions } from './types.js';
2
+ export declare function mergeManifests(base: PackageManifest, patch: Partial<PackageManifest>): PackageManifest;
3
+ export declare function createContext(root: string, options: ProjectOptions): GeneratorContext;
@@ -0,0 +1,81 @@
1
+ import { mkdir, readFile, writeFile, lstat } from 'node:fs/promises';
2
+ import { dirname, isAbsolute, relative, resolve, sep } from 'node:path';
3
+ export function mergeManifests(base, patch) {
4
+ const result = { ...base, ...patch };
5
+ for (const field of [
6
+ 'scripts',
7
+ 'dependencies',
8
+ 'devDependencies',
9
+ 'peerDependencies',
10
+ 'optionalDependencies',
11
+ ]) {
12
+ const previous = base[field];
13
+ const next = patch[field];
14
+ if (!previous && !next)
15
+ continue;
16
+ for (const [name, value] of Object.entries(next ?? {})) {
17
+ if (field !== 'scripts' &&
18
+ previous?.[name] !== undefined &&
19
+ previous[name] !== value)
20
+ throw new Error(`Conflicting ${field} entry for ${name}: ${previous[name]} versus ${value}.`);
21
+ }
22
+ result[field] = { ...previous, ...next };
23
+ }
24
+ for (const [name, version] of Object.entries(result.dependencies ?? {})) {
25
+ if (result.devDependencies?.[name] !== undefined &&
26
+ result.devDependencies[name] !== version)
27
+ throw new Error(`Conflicting dependency versions for ${name}.`);
28
+ }
29
+ return result;
30
+ }
31
+ export function createContext(root, options) {
32
+ const owned = new Set();
33
+ async function safePath(path) {
34
+ if (!path ||
35
+ isAbsolute(path) ||
36
+ path.includes('\\') ||
37
+ path.split('/').includes('..'))
38
+ throw new Error(`Unsafe output path: ${path}`);
39
+ const target = resolve(root, path);
40
+ const rel = relative(root, target);
41
+ if (!rel || rel.startsWith(`..${sep}`) || isAbsolute(rel))
42
+ throw new Error(`Unsafe output path: ${path}`);
43
+ let current = root;
44
+ for (const part of rel.split(sep)) {
45
+ current = resolve(current, part);
46
+ try {
47
+ if ((await lstat(current)).isSymbolicLink())
48
+ throw new Error(`Refusing symlink in output path: ${path}`);
49
+ }
50
+ catch (error) {
51
+ if (error.code !== 'ENOENT')
52
+ throw error;
53
+ }
54
+ }
55
+ return target;
56
+ }
57
+ return {
58
+ options,
59
+ root,
60
+ scope: options.name,
61
+ async write(path, contents) {
62
+ const target = await safePath(path);
63
+ if (owned.has(target))
64
+ throw new Error(`Template output collision: ${path}`);
65
+ await mkdir(dirname(target), { recursive: true });
66
+ await writeFile(target, contents, { flag: 'wx' });
67
+ owned.add(target);
68
+ },
69
+ async json(path, value) {
70
+ await this.write(path, `${JSON.stringify(value, null, 2)}\n`);
71
+ },
72
+ async mergePackage(path, patch) {
73
+ const target = await safePath(path);
74
+ if (!owned.has(target))
75
+ throw new Error(`Cannot patch a package not generated by this context: ${path}`);
76
+ const base = JSON.parse(await readFile(target, 'utf8'));
77
+ await writeFile(target, `${JSON.stringify(mergeManifests(base, patch), null, 2)}\n`);
78
+ },
79
+ };
80
+ }
81
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/generator/context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { type RawOptions } from './options.js';
2
+ import type { AppTemplate, Framework } from './types.js';
3
+ export { normalizeOptions, validateProjectName } from './options.js';
4
+ export type { RawOptions } from './options.js';
5
+ export type * from './types.js';
6
+ export declare function selectTemplate(framework: Framework): AppTemplate;
7
+ export interface GenerateSettings {
8
+ cwd?: string;
9
+ onProgress?: (message: string) => void;
10
+ signal?: AbortSignal;
11
+ }
12
+ export declare function generateProject(raw: RawOptions, settings?: GenerateSettings): Promise<string>;