create-100x-mobile 0.4.2 → 0.4.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.
- package/dist/cli.js +10 -9
- package/dist/commands/new/args.js +76 -0
- package/dist/commands/new/scaffold.js +33 -0
- package/dist/commands/new/steps.js +379 -0
- package/dist/commands/new.js +134 -367
- package/dist/lib/clerk.js +3 -0
- package/dist/lib/fs.js +5 -0
- package/dist/lib/run.js +5 -1
- package/dist/templates/app/settingsScreen.js +5 -2
- package/dist/templates/app/signIn.js +25 -23
- package/dist/templates/hooks/useFrameworkReady.js +4 -2
- package/package.json +7 -3
- package/dist/commands/CLAUDE.md +0 -7
- package/dist/templates/app/signUp.js +0 -298
- package/dist/templates/config/CLAUDE.md +0 -7
package/dist/commands/new.js
CHANGED
|
@@ -4,15 +4,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.cmdNew = cmdNew;
|
|
7
|
+
exports.runNewCommand = runNewCommand;
|
|
7
8
|
const node_path_1 = require("node:path");
|
|
8
9
|
const prompts_1 = require("@clack/prompts");
|
|
9
10
|
const picocolors_1 = __importDefault(require("picocolors"));
|
|
11
|
+
const args_1 = require("./new/args");
|
|
12
|
+
const scaffold_1 = require("./new/scaffold");
|
|
13
|
+
const steps_1 = require("./new/steps");
|
|
10
14
|
const fs_1 = require("../lib/fs");
|
|
11
15
|
const projectName_1 = require("../lib/projectName");
|
|
12
|
-
const run_1 = require("../lib/run");
|
|
13
|
-
const dotenv_1 = require("../lib/dotenv");
|
|
14
|
-
const fs_2 = require("../lib/fs");
|
|
15
|
-
const clerk_1 = require("../lib/clerk");
|
|
16
16
|
// Config templates
|
|
17
17
|
const packageJson_1 = require("../templates/config/packageJson");
|
|
18
18
|
const appJson_1 = require("../templates/config/appJson");
|
|
@@ -25,140 +25,25 @@ const readme_1 = require("../templates/config/readme");
|
|
|
25
25
|
// Convex templates
|
|
26
26
|
const schema_1 = require("../templates/convex/schema");
|
|
27
27
|
const todos_1 = require("../templates/convex/todos");
|
|
28
|
-
const users_1 = require("../templates/convex/users");
|
|
29
28
|
const authConfig_1 = require("../templates/convex/authConfig");
|
|
29
|
+
const users_1 = require("../templates/convex/users");
|
|
30
30
|
// App templates
|
|
31
|
-
const rootLayout_1 = require("../templates/app/rootLayout");
|
|
32
|
-
const notFound_1 = require("../templates/app/notFound");
|
|
33
|
-
const authProvider_1 = require("../templates/app/authProvider");
|
|
34
31
|
const authLayout_1 = require("../templates/app/authLayout");
|
|
32
|
+
const authProvider_1 = require("../templates/app/authProvider");
|
|
33
|
+
const notFound_1 = require("../templates/app/notFound");
|
|
34
|
+
const rootLayout_1 = require("../templates/app/rootLayout");
|
|
35
|
+
const settingsScreen_1 = require("../templates/app/settingsScreen");
|
|
35
36
|
const signIn_1 = require("../templates/app/signIn");
|
|
36
37
|
const tabsLayout_1 = require("../templates/app/tabsLayout");
|
|
37
38
|
const todosScreen_1 = require("../templates/app/todosScreen");
|
|
38
|
-
const settingsScreen_1 = require("../templates/app/settingsScreen");
|
|
39
39
|
// Component templates
|
|
40
40
|
const addTodoForm_1 = require("../templates/components/addTodoForm");
|
|
41
|
-
const todoItem_1 = require("../templates/components/todoItem");
|
|
42
|
-
const filterTabs_1 = require("../templates/components/filterTabs");
|
|
43
41
|
const emptyState_1 = require("../templates/components/emptyState");
|
|
42
|
+
const filterTabs_1 = require("../templates/components/filterTabs");
|
|
43
|
+
const todoItem_1 = require("../templates/components/todoItem");
|
|
44
44
|
// Hook templates
|
|
45
45
|
const useFrameworkReady_1 = require("../templates/hooks/useFrameworkReady");
|
|
46
|
-
|
|
47
|
-
(0, prompts_1.intro)(picocolors_1.default.bold(picocolors_1.default.cyan("create-100x-mobile")));
|
|
48
|
-
// ── Step 1: Parse & validate project name ──────────────
|
|
49
|
-
let projectName = args[0];
|
|
50
|
-
if (!projectName) {
|
|
51
|
-
const nameInput = await (0, prompts_1.text)({
|
|
52
|
-
message: "What is your project name?",
|
|
53
|
-
placeholder: "my-app",
|
|
54
|
-
defaultValue: "my-app",
|
|
55
|
-
});
|
|
56
|
-
if ((0, prompts_1.isCancel)(nameInput) || !nameInput) {
|
|
57
|
-
(0, prompts_1.cancel)("Operation cancelled.");
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
projectName = nameInput;
|
|
61
|
-
}
|
|
62
|
-
const sanitizedName = (0, projectName_1.sanitizeProjectName)(projectName);
|
|
63
|
-
if (sanitizedName !== projectName) {
|
|
64
|
-
prompts_1.log.warn(`Project name sanitized: "${projectName}" → "${sanitizedName}"`);
|
|
65
|
-
projectName = sanitizedName;
|
|
66
|
-
}
|
|
67
|
-
if (!projectName) {
|
|
68
|
-
prompts_1.log.error("Invalid project name.");
|
|
69
|
-
process.exitCode = 1;
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
const projectDir = (0, node_path_1.resolve)(process.cwd(), projectName);
|
|
73
|
-
// Check if directory exists
|
|
74
|
-
if (await (0, fs_1.pathExists)(projectDir)) {
|
|
75
|
-
prompts_1.log.warn(`Directory "${projectName}" already exists.`);
|
|
76
|
-
const choice = await (0, prompts_1.select)({
|
|
77
|
-
message: "What would you like to do?",
|
|
78
|
-
options: [
|
|
79
|
-
{ value: "overwrite", label: "Overwrite the existing directory" },
|
|
80
|
-
{ value: "cancel", label: "Cancel" },
|
|
81
|
-
],
|
|
82
|
-
initialValue: "cancel",
|
|
83
|
-
});
|
|
84
|
-
if ((0, prompts_1.isCancel)(choice) || choice === "cancel") {
|
|
85
|
-
(0, prompts_1.cancel)("Operation cancelled.");
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
await (0, fs_1.removeDir)(projectDir);
|
|
89
|
-
}
|
|
90
|
-
// ── Expo SDK version selection ───────────────────────────
|
|
91
|
-
let expoVersion = "latest";
|
|
92
|
-
const sdkSpinner = (0, prompts_1.spinner)();
|
|
93
|
-
sdkSpinner.start("Fetching available Expo SDK versions");
|
|
94
|
-
try {
|
|
95
|
-
const latestVersion = (await (0, run_1.runCapture)("npm", ["view", "expo", "version"])).trim();
|
|
96
|
-
const latestMajor = parseInt(latestVersion.split(".")[0], 10);
|
|
97
|
-
if (!isNaN(latestMajor)) {
|
|
98
|
-
const prevMajor = latestMajor - 1;
|
|
99
|
-
const prevJson = await (0, run_1.runCapture)("npm", [
|
|
100
|
-
"view",
|
|
101
|
-
`expo@^${prevMajor}.0.0`,
|
|
102
|
-
"version",
|
|
103
|
-
"--json",
|
|
104
|
-
]);
|
|
105
|
-
const prevParsed = JSON.parse(prevJson);
|
|
106
|
-
const prevVersion = Array.isArray(prevParsed)
|
|
107
|
-
? prevParsed[prevParsed.length - 1]
|
|
108
|
-
: prevParsed;
|
|
109
|
-
sdkSpinner.stop("Expo SDK versions fetched.");
|
|
110
|
-
const sdkChoice = await (0, prompts_1.select)({
|
|
111
|
-
message: "Which Expo SDK version?",
|
|
112
|
-
options: [
|
|
113
|
-
{
|
|
114
|
-
value: latestVersion,
|
|
115
|
-
label: `SDK ${latestMajor} (latest)`,
|
|
116
|
-
hint: `expo ~${latestVersion}`,
|
|
117
|
-
},
|
|
118
|
-
{
|
|
119
|
-
value: prevVersion,
|
|
120
|
-
label: `SDK ${prevMajor}`,
|
|
121
|
-
hint: `expo ~${prevVersion}`,
|
|
122
|
-
},
|
|
123
|
-
],
|
|
124
|
-
});
|
|
125
|
-
if (!(0, prompts_1.isCancel)(sdkChoice)) {
|
|
126
|
-
expoVersion = `~${sdkChoice}`;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
else {
|
|
130
|
-
sdkSpinner.stop("Could not parse Expo version.");
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
catch {
|
|
134
|
-
sdkSpinner.stop("Could not fetch Expo versions, using latest.");
|
|
135
|
-
}
|
|
136
|
-
// ── Determine total steps ────────────────────────────────
|
|
137
|
-
// Steps: 1=Project structure, 2=Dependencies, 3=Convex setup,
|
|
138
|
-
// 4=Convex env (if clerkDomain), 5=Git init, 6=Health check
|
|
139
|
-
// We'll calculate totalSteps after Clerk prompts, but we create the
|
|
140
|
-
// counter now. We'll adjust totalSteps after Clerk prompts.
|
|
141
|
-
let currentStep = 0;
|
|
142
|
-
let totalSteps = 6; // base: structure, deps, expo deps, convex, git, health check
|
|
143
|
-
// totalSteps += 1 if clerkDomain (convex env step) — adjusted after Clerk prompts
|
|
144
|
-
const stepLabel = () => `(${currentStep}/${totalSteps})`;
|
|
145
|
-
// ── Step: Create directories ─────────────────────────
|
|
146
|
-
const s = (0, prompts_1.spinner)();
|
|
147
|
-
currentStep++;
|
|
148
|
-
s.start(`Creating project structure ${stepLabel()}`);
|
|
149
|
-
const dirs = [
|
|
150
|
-
"app/(auth)",
|
|
151
|
-
"app/(tabs)",
|
|
152
|
-
"app/providers",
|
|
153
|
-
"components",
|
|
154
|
-
"convex",
|
|
155
|
-
"hooks",
|
|
156
|
-
"assets/images",
|
|
157
|
-
];
|
|
158
|
-
for (const dir of dirs) {
|
|
159
|
-
await (0, fs_1.ensureDir)((0, node_path_1.join)(projectDir, dir));
|
|
160
|
-
}
|
|
161
|
-
// ── Step 3: Write all template files ───────────────────
|
|
46
|
+
function buildTemplateFiles(projectName, expoVersion) {
|
|
162
47
|
const files = [
|
|
163
48
|
// Config
|
|
164
49
|
["package.json", (0, packageJson_1.packageJsonTemplate)(projectName, expoVersion)],
|
|
@@ -194,256 +79,136 @@ async function cmdNew(args) {
|
|
|
194
79
|
// Hooks
|
|
195
80
|
["hooks/useFrameworkReady.ts", (0, useFrameworkReady_1.useFrameworkReadyTemplate)()],
|
|
196
81
|
];
|
|
197
|
-
|
|
198
|
-
|
|
82
|
+
return files.map(([path, content]) => ({ path, content }));
|
|
83
|
+
}
|
|
84
|
+
async function cmdNew(rawArgs) {
|
|
85
|
+
(0, prompts_1.intro)(picocolors_1.default.bold(picocolors_1.default.cyan("create-100x-mobile")));
|
|
86
|
+
const { options, projectName: projectNameArg } = (0, args_1.parseNewCommandArgs)(rawArgs);
|
|
87
|
+
let projectName = projectNameArg;
|
|
88
|
+
if (!projectName && options.interactive) {
|
|
89
|
+
const nameInput = await (0, prompts_1.text)({
|
|
90
|
+
message: "What is your project name?",
|
|
91
|
+
placeholder: "my-app",
|
|
92
|
+
defaultValue: "my-app",
|
|
93
|
+
});
|
|
94
|
+
if ((0, prompts_1.isCancel)(nameInput) || !nameInput) {
|
|
95
|
+
(0, prompts_1.cancel)("Operation cancelled.");
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
projectName = nameInput;
|
|
199
99
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
currentStep++;
|
|
203
|
-
prompts_1.log.step(`Installing dependencies ${stepLabel()}`);
|
|
204
|
-
try {
|
|
205
|
-
await (0, run_1.run)("bun", ["install"], { cwd: projectDir });
|
|
100
|
+
if (!projectName) {
|
|
101
|
+
throw new Error("Project name is required in non-interactive mode.");
|
|
206
102
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
catch {
|
|
213
|
-
prompts_1.log.error("Failed to install dependencies. Run `bun install` or `npm install` manually.");
|
|
214
|
-
}
|
|
103
|
+
const sanitizedName = (0, projectName_1.sanitizeProjectName)(projectName);
|
|
104
|
+
if (sanitizedName !== projectName) {
|
|
105
|
+
prompts_1.log.warn(`Project name sanitized: "${projectName}" → "${sanitizedName}"`);
|
|
106
|
+
projectName = sanitizedName;
|
|
215
107
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
"
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
"expo-secure-store",
|
|
233
|
-
"expo-splash-screen",
|
|
234
|
-
"expo-status-bar",
|
|
235
|
-
"expo-system-ui",
|
|
236
|
-
"expo-web-browser",
|
|
237
|
-
"react-native-gesture-handler",
|
|
238
|
-
"react-native-reanimated",
|
|
239
|
-
"react-native-safe-area-context",
|
|
240
|
-
"react-native-screens",
|
|
241
|
-
"react-native-svg",
|
|
242
|
-
"react-native-web",
|
|
243
|
-
];
|
|
244
|
-
try {
|
|
245
|
-
await (0, run_1.run)("npx", ["expo", "install", ...expoPackages], {
|
|
246
|
-
cwd: projectDir,
|
|
108
|
+
if (!projectName) {
|
|
109
|
+
throw new Error("Invalid project name.");
|
|
110
|
+
}
|
|
111
|
+
const projectDir = (0, node_path_1.resolve)(process.cwd(), projectName);
|
|
112
|
+
if (await (0, fs_1.pathExists)(projectDir)) {
|
|
113
|
+
if (!options.interactive) {
|
|
114
|
+
throw new Error(`Directory "${projectName}" already exists. Remove it or choose another name.`);
|
|
115
|
+
}
|
|
116
|
+
prompts_1.log.warn(`Directory "${projectName}" already exists.`);
|
|
117
|
+
const choice = await (0, prompts_1.select)({
|
|
118
|
+
message: "What would you like to do?",
|
|
119
|
+
options: [
|
|
120
|
+
{ value: "overwrite", label: "Overwrite the existing directory" },
|
|
121
|
+
{ value: "cancel", label: "Cancel" },
|
|
122
|
+
],
|
|
123
|
+
initialValue: "cancel",
|
|
247
124
|
});
|
|
248
|
-
prompts_1.
|
|
125
|
+
if ((0, prompts_1.isCancel)(choice) || choice === "cancel") {
|
|
126
|
+
(0, prompts_1.cancel)("Operation cancelled.");
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
await (0, fs_1.removeDir)(projectDir);
|
|
249
130
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
prompts_1.log.info(picocolors_1.default.dim(" Run manually: npx expo install --fix"));
|
|
131
|
+
if (options.clerkDomain) {
|
|
132
|
+
options.clerkDomain = (0, steps_1.normalizeClerkDomain)(options.clerkDomain);
|
|
253
133
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
134
|
+
const expoVersion = await (0, steps_1.resolveExpoVersion)(options);
|
|
135
|
+
const clerk = await (0, steps_1.promptClerkSetup)(options);
|
|
136
|
+
const totalSteps = 2 + // project generation + convex init
|
|
137
|
+
(options.installDependencies ? 2 : 0) +
|
|
138
|
+
(clerk.domain ? 1 : 0) +
|
|
139
|
+
(options.initializeGit ? 1 : 0) +
|
|
140
|
+
1; // health check
|
|
141
|
+
let currentStep = 0;
|
|
142
|
+
const stepLabel = () => `(${currentStep}/${totalSteps})`;
|
|
143
|
+
const structureSpinner = (0, prompts_1.spinner)();
|
|
144
|
+
currentStep++;
|
|
145
|
+
structureSpinner.start(`Creating project structure ${stepLabel()}`);
|
|
146
|
+
await (0, scaffold_1.createProjectDirectories)(projectDir);
|
|
147
|
+
await (0, scaffold_1.writeScaffoldFiles)(projectDir, buildTemplateFiles(projectName, expoVersion));
|
|
148
|
+
await (0, scaffold_1.writeDefaultAssetFiles)(projectDir);
|
|
149
|
+
await (0, steps_1.writeAuthEnvironment)(projectDir, clerk);
|
|
150
|
+
structureSpinner.stop("Project files created.");
|
|
151
|
+
if (options.installDependencies) {
|
|
152
|
+
currentStep++;
|
|
153
|
+
prompts_1.log.step(`Installing dependencies ${stepLabel()}`);
|
|
154
|
+
const installed = await (0, steps_1.installDependencies)(projectDir);
|
|
155
|
+
if (installed) {
|
|
156
|
+
prompts_1.log.success("Dependencies installed.");
|
|
274
157
|
}
|
|
275
158
|
else {
|
|
276
|
-
|
|
277
|
-
message: "Clerk JWT Issuer Domain",
|
|
278
|
-
placeholder: "https://your-app.clerk.accounts.dev",
|
|
279
|
-
defaultValue: "",
|
|
280
|
-
});
|
|
281
|
-
clerkDomain =
|
|
282
|
-
(0, prompts_1.isCancel)(domainInput) || !domainInput ? "" : domainInput.trim();
|
|
159
|
+
prompts_1.log.warn("Dependencies were not installed automatically.");
|
|
283
160
|
}
|
|
284
|
-
|
|
285
|
-
prompts_1.log.
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
message: "Clerk Secret Key",
|
|
290
|
-
placeholder: "sk_test_...",
|
|
291
|
-
defaultValue: "",
|
|
292
|
-
});
|
|
293
|
-
const clerkSecretKey = (0, prompts_1.isCancel)(secretInput) || !secretInput ? "" : secretInput.trim();
|
|
294
|
-
if (clerkSecretKey) {
|
|
295
|
-
const jwtSpinner = (0, prompts_1.spinner)();
|
|
296
|
-
jwtSpinner.start('Creating "convex" JWT template in Clerk');
|
|
297
|
-
const result = await (0, clerk_1.createConvexJwtTemplate)(clerkSecretKey);
|
|
298
|
-
if (result.success) {
|
|
299
|
-
jwtSpinner.stop('JWT template "convex" created in Clerk.');
|
|
300
|
-
jwtCreated = true;
|
|
301
|
-
}
|
|
302
|
-
else {
|
|
303
|
-
jwtSpinner.stop("Could not create JWT template.");
|
|
304
|
-
prompts_1.log.warn(`Clerk API: ${result.error}`);
|
|
305
|
-
prompts_1.log.info(picocolors_1.default.dim('You may need to create a JWT template named "convex" in your Clerk dashboard.'));
|
|
306
|
-
}
|
|
161
|
+
currentStep++;
|
|
162
|
+
prompts_1.log.step(`Installing Expo dependencies ${stepLabel()}`);
|
|
163
|
+
const resolved = await (0, steps_1.resolveExpoDependencies)(projectDir);
|
|
164
|
+
if (resolved) {
|
|
165
|
+
prompts_1.log.success("Expo dependencies resolved.");
|
|
307
166
|
}
|
|
308
167
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
totalSteps = 7; // adds convex env step
|
|
312
|
-
}
|
|
313
|
-
// ── Write Clerk env vars to .env.local ─────────
|
|
314
|
-
const envLocalPath = (0, node_path_1.join)(projectDir, ".env.local");
|
|
315
|
-
let envContents = "";
|
|
316
|
-
try {
|
|
317
|
-
envContents = await (0, fs_2.readTextFile)(envLocalPath);
|
|
318
|
-
}
|
|
319
|
-
catch {
|
|
320
|
-
envContents = "";
|
|
321
|
-
}
|
|
322
|
-
if (clerkKeyValue) {
|
|
323
|
-
envContents = (0, dotenv_1.upsertDotenvVar)(envContents, "EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY", clerkKeyValue);
|
|
324
|
-
}
|
|
325
|
-
if (clerkDomain) {
|
|
326
|
-
envContents = (0, dotenv_1.upsertDotenvVar)(envContents, "CLERK_JWT_ISSUER_DOMAIN", clerkDomain);
|
|
327
|
-
}
|
|
328
|
-
if (envContents) {
|
|
329
|
-
await (0, fs_1.writeTextFile)(envLocalPath, envContents);
|
|
330
|
-
}
|
|
331
|
-
// ── Step 6b: Hardcode domain in auth.config.ts ─────────
|
|
332
|
-
// Convex auth.config.ts is evaluated at deploy time, so process.env
|
|
333
|
-
// won't have the value yet (we set it after deploy). Hardcode it.
|
|
334
|
-
if (clerkDomain) {
|
|
335
|
-
await (0, fs_1.writeTextFile)((0, node_path_1.join)(projectDir, "convex/auth.config.ts"), `export default {\n providers: [\n {\n domain: "${clerkDomain}",\n applicationID: "convex",\n },\n ],\n};\n`);
|
|
168
|
+
else {
|
|
169
|
+
prompts_1.log.info(picocolors_1.default.dim("Skipping dependency installation (--no-install)."));
|
|
336
170
|
}
|
|
337
|
-
// ── Step: Initialize Convex (single run) ─────────────
|
|
338
171
|
currentStep++;
|
|
339
172
|
prompts_1.log.step(`Setting up Convex ${stepLabel()}`);
|
|
340
|
-
|
|
341
|
-
|
|
173
|
+
const convexInitialized = await (0, steps_1.initializeConvex)(projectDir);
|
|
174
|
+
if (convexInitialized) {
|
|
342
175
|
prompts_1.log.success("Convex initialized.");
|
|
343
176
|
}
|
|
344
|
-
|
|
345
|
-
prompts_1.log.
|
|
346
|
-
prompts_1.log.info(picocolors_1.default.dim(" Run manually: cd " + projectName + " && bunx convex dev --once"));
|
|
177
|
+
else {
|
|
178
|
+
prompts_1.log.info(picocolors_1.default.dim(`Run manually: cd ${projectName} && bunx convex dev --once`));
|
|
347
179
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
let updatedEnv = "";
|
|
352
|
-
try {
|
|
353
|
-
updatedEnv = await (0, fs_2.readTextFile)(envLocalPath);
|
|
354
|
-
}
|
|
355
|
-
catch {
|
|
356
|
-
updatedEnv = "";
|
|
357
|
-
}
|
|
358
|
-
if (updatedEnv && !updatedEnv.includes("EXPO_PUBLIC_CONVEX_URL")) {
|
|
359
|
-
const parsed = (0, dotenv_1.parseDotenv)(updatedEnv);
|
|
360
|
-
let convexUrl = parsed["CONVEX_URL"] || "";
|
|
361
|
-
if (!convexUrl) {
|
|
362
|
-
// Try the convex url command as fallback
|
|
363
|
-
try {
|
|
364
|
-
convexUrl = await (0, run_1.runCapture)("bunx", ["convex", "url"], {
|
|
365
|
-
cwd: projectDir,
|
|
366
|
-
});
|
|
367
|
-
}
|
|
368
|
-
catch {
|
|
369
|
-
// Non-critical
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
if (convexUrl) {
|
|
373
|
-
updatedEnv = (0, dotenv_1.upsertDotenvVar)(updatedEnv, "EXPO_PUBLIC_CONVEX_URL", convexUrl);
|
|
374
|
-
await (0, fs_1.writeTextFile)(envLocalPath, updatedEnv);
|
|
375
|
-
prompts_1.log.success(`Convex URL configured for Expo.`);
|
|
376
|
-
}
|
|
377
|
-
else {
|
|
378
|
-
prompts_1.log.info(picocolors_1.default.dim(" Note: Add EXPO_PUBLIC_CONVEX_URL to .env.local with your Convex deployment URL."));
|
|
379
|
-
}
|
|
380
|
-
}
|
|
180
|
+
const convexUrlReady = await (0, steps_1.ensureExpoPublicConvexUrl)(projectDir);
|
|
181
|
+
if (convexUrlReady) {
|
|
182
|
+
prompts_1.log.success("Convex URL configured for Expo.");
|
|
381
183
|
}
|
|
382
|
-
|
|
383
|
-
|
|
184
|
+
else {
|
|
185
|
+
prompts_1.log.info(picocolors_1.default.dim("Add EXPO_PUBLIC_CONVEX_URL to .env.local with your Convex deployment URL."));
|
|
384
186
|
}
|
|
385
|
-
|
|
386
|
-
if (clerkDomain) {
|
|
187
|
+
if (clerk.domain) {
|
|
387
188
|
currentStep++;
|
|
388
189
|
prompts_1.log.step(`Setting Convex environment variable ${stepLabel()}`);
|
|
389
|
-
|
|
390
|
-
|
|
190
|
+
const setEnvOk = await (0, steps_1.setConvexClerkEnv)(projectDir, clerk.domain);
|
|
191
|
+
if (setEnvOk) {
|
|
391
192
|
prompts_1.log.success("Convex environment variable set.");
|
|
392
193
|
}
|
|
393
|
-
|
|
394
|
-
prompts_1.log.info(picocolors_1.default.dim(`
|
|
194
|
+
else {
|
|
195
|
+
prompts_1.log.info(picocolors_1.default.dim(`Run manually: bunx convex env set CLERK_JWT_ISSUER_DOMAIN ${clerk.domain}`));
|
|
395
196
|
}
|
|
396
197
|
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
await (0, run_1.run)("git", ["commit", "-m", "Initial commit from create-100x-mobile"], {
|
|
404
|
-
cwd: projectDir,
|
|
405
|
-
});
|
|
406
|
-
prompts_1.log.success("Git repository initialized.");
|
|
407
|
-
}
|
|
408
|
-
catch {
|
|
409
|
-
prompts_1.log.info(picocolors_1.default.dim(" Note: git is not available. You can initialize a repo later."));
|
|
410
|
-
}
|
|
411
|
-
// ── Step: Health check ──────────────────────────────
|
|
412
|
-
currentStep++;
|
|
413
|
-
prompts_1.log.step(`Running health check ${stepLabel()}`);
|
|
414
|
-
const healthChecks = [];
|
|
415
|
-
// Check .env.local exists
|
|
416
|
-
const envExists = await (0, fs_1.pathExists)(envLocalPath);
|
|
417
|
-
healthChecks.push({ label: ".env.local exists", ok: envExists });
|
|
418
|
-
// Check EXPO_PUBLIC_CONVEX_URL is set
|
|
419
|
-
if (envExists) {
|
|
420
|
-
let envContent = "";
|
|
421
|
-
try {
|
|
422
|
-
envContent = await (0, fs_2.readTextFile)(envLocalPath);
|
|
423
|
-
}
|
|
424
|
-
catch {
|
|
425
|
-
// ignore
|
|
426
|
-
}
|
|
427
|
-
healthChecks.push({
|
|
428
|
-
label: "EXPO_PUBLIC_CONVEX_URL is set",
|
|
429
|
-
ok: envContent.includes("EXPO_PUBLIC_CONVEX_URL"),
|
|
430
|
-
});
|
|
431
|
-
if (clerkKeyValue) {
|
|
432
|
-
healthChecks.push({
|
|
433
|
-
label: "EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY is set",
|
|
434
|
-
ok: envContent.includes("EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY"),
|
|
435
|
-
});
|
|
198
|
+
if (options.initializeGit) {
|
|
199
|
+
currentStep++;
|
|
200
|
+
prompts_1.log.step(`Initializing git repository ${stepLabel()}`);
|
|
201
|
+
const gitReady = await (0, steps_1.initializeGit)(projectDir);
|
|
202
|
+
if (gitReady) {
|
|
203
|
+
prompts_1.log.success("Git repository initialized.");
|
|
436
204
|
}
|
|
437
205
|
}
|
|
438
206
|
else {
|
|
439
|
-
|
|
440
|
-
if (clerkKeyValue) {
|
|
441
|
-
healthChecks.push({
|
|
442
|
-
label: "EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY is set",
|
|
443
|
-
ok: false,
|
|
444
|
-
});
|
|
445
|
-
}
|
|
207
|
+
prompts_1.log.info(picocolors_1.default.dim("Skipping git initialization (--skip-git)."));
|
|
446
208
|
}
|
|
209
|
+
currentStep++;
|
|
210
|
+
prompts_1.log.step(`Running health check ${stepLabel()}`);
|
|
211
|
+
const healthChecks = await (0, steps_1.runHealthChecks)(projectDir, clerk.publishableKey);
|
|
447
212
|
for (const check of healthChecks) {
|
|
448
213
|
if (check.ok) {
|
|
449
214
|
prompts_1.log.info(` ${picocolors_1.default.green("✓")} ${check.label}`);
|
|
@@ -452,39 +217,41 @@ async function cmdNew(args) {
|
|
|
452
217
|
prompts_1.log.info(` ${picocolors_1.default.yellow("⚠")} ${check.label}`);
|
|
453
218
|
}
|
|
454
219
|
}
|
|
455
|
-
// ── Success message ───────────────────────────────
|
|
456
220
|
prompts_1.log.info("");
|
|
457
|
-
if (
|
|
458
|
-
// Clerk is configured — app is ready
|
|
221
|
+
if (clerk.publishableKey && clerk.domain) {
|
|
459
222
|
prompts_1.log.success(picocolors_1.default.bold(picocolors_1.default.green("Your app is ready!")));
|
|
460
223
|
prompts_1.log.info("");
|
|
461
224
|
prompts_1.log.info(" Next steps:");
|
|
462
225
|
prompts_1.log.info(` ${picocolors_1.default.cyan("cd")} ${projectName}`);
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
226
|
+
if (options.installDependencies) {
|
|
227
|
+
prompts_1.log.info(` ${picocolors_1.default.cyan("bunx expo start")}`);
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
prompts_1.log.info(` ${picocolors_1.default.cyan("bun install")} ${picocolors_1.default.dim("# or npm install")}`);
|
|
231
|
+
prompts_1.log.info(` ${picocolors_1.default.cyan("bunx expo start")}`);
|
|
232
|
+
}
|
|
233
|
+
if (!clerk.jwtCreated) {
|
|
466
234
|
prompts_1.log.info(picocolors_1.default.dim(' Note: Ensure a JWT template named "convex" exists in your Clerk dashboard.'));
|
|
467
235
|
}
|
|
468
|
-
prompts_1.log.info(picocolors_1.default.dim(" Remember to enable Google and Apple OAuth providers in your Clerk dashboard."));
|
|
469
236
|
}
|
|
470
237
|
else {
|
|
471
|
-
// No Clerk keys — manual setup needed
|
|
472
238
|
prompts_1.log.success(picocolors_1.default.bold(picocolors_1.default.green("Project scaffolded!")));
|
|
473
239
|
prompts_1.log.info("");
|
|
474
240
|
prompts_1.log.info(" To finish setup:");
|
|
475
|
-
prompts_1.log.info("");
|
|
476
|
-
prompts_1.log.info(` 1. Create a Clerk app at ${picocolors_1.default.cyan("https://dashboard.clerk.com")}`);
|
|
477
|
-
prompts_1.log.info(` 2. Enable Google and Apple OAuth providers`);
|
|
478
|
-
prompts_1.log.info(` 3. Add keys to ${picocolors_1.default.cyan(".env.local")}:`);
|
|
241
|
+
prompts_1.log.info(` 1. Add keys to ${picocolors_1.default.cyan(".env.local")}`);
|
|
479
242
|
prompts_1.log.info(` ${picocolors_1.default.dim("EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...")}`);
|
|
480
243
|
prompts_1.log.info(` ${picocolors_1.default.dim("CLERK_JWT_ISSUER_DOMAIN=https://your-app.clerk.accounts.dev")}`);
|
|
481
|
-
prompts_1.log.info(`
|
|
482
|
-
prompts_1.log.info(`
|
|
483
|
-
prompts_1.log.info(` 5. Create a JWT template named ${picocolors_1.default.bold('"convex"')} in Clerk dashboard`);
|
|
484
|
-
prompts_1.log.info("");
|
|
485
|
-
prompts_1.log.info(" Then start your app:");
|
|
486
|
-
prompts_1.log.info(` ${picocolors_1.default.cyan("cd")} ${projectName}`);
|
|
487
|
-
prompts_1.log.info(` ${picocolors_1.default.cyan("bunx expo start")}`);
|
|
244
|
+
prompts_1.log.info(` 2. ${picocolors_1.default.dim("bunx convex env set CLERK_JWT_ISSUER_DOMAIN <domain>")}`);
|
|
245
|
+
prompts_1.log.info(` 3. Create a JWT template named ${picocolors_1.default.bold('"convex"')} in Clerk dashboard`);
|
|
488
246
|
}
|
|
489
247
|
(0, prompts_1.outro)(picocolors_1.default.dim("Happy building!"));
|
|
490
248
|
}
|
|
249
|
+
async function runNewCommand(rawArgs) {
|
|
250
|
+
try {
|
|
251
|
+
await cmdNew(rawArgs);
|
|
252
|
+
}
|
|
253
|
+
catch (error) {
|
|
254
|
+
prompts_1.log.error((0, steps_1.toErrorMessage)(error));
|
|
255
|
+
process.exitCode = 1;
|
|
256
|
+
}
|
|
257
|
+
}
|
package/dist/lib/clerk.js
CHANGED
package/dist/lib/fs.js
CHANGED
|
@@ -5,6 +5,7 @@ exports.ensureDir = ensureDir;
|
|
|
5
5
|
exports.removeDir = removeDir;
|
|
6
6
|
exports.readTextFile = readTextFile;
|
|
7
7
|
exports.writeTextFile = writeTextFile;
|
|
8
|
+
exports.writeBinaryFile = writeBinaryFile;
|
|
8
9
|
exports.writeTextFileIfChanged = writeTextFileIfChanged;
|
|
9
10
|
const promises_1 = require("node:fs/promises");
|
|
10
11
|
const node_path_1 = require("node:path");
|
|
@@ -30,6 +31,10 @@ async function writeTextFile(filePath, contents) {
|
|
|
30
31
|
await ensureDir((0, node_path_1.dirname)(filePath));
|
|
31
32
|
await (0, promises_1.writeFile)(filePath, contents);
|
|
32
33
|
}
|
|
34
|
+
async function writeBinaryFile(filePath, contents) {
|
|
35
|
+
await ensureDir((0, node_path_1.dirname)(filePath));
|
|
36
|
+
await (0, promises_1.writeFile)(filePath, contents);
|
|
37
|
+
}
|
|
33
38
|
async function writeTextFileIfChanged(filePath, contents) {
|
|
34
39
|
const exists = await pathExists(filePath);
|
|
35
40
|
if (exists) {
|
package/dist/lib/run.js
CHANGED
|
@@ -29,12 +29,16 @@ async function runCapture(cmd, args, options = {}) {
|
|
|
29
29
|
shell: process.platform === "win32",
|
|
30
30
|
});
|
|
31
31
|
let stdout = "";
|
|
32
|
+
let stderr = "";
|
|
32
33
|
child.stdout?.on("data", (data) => (stdout += data));
|
|
34
|
+
child.stderr?.on("data", (data) => (stderr += data));
|
|
33
35
|
child.on("error", reject);
|
|
34
36
|
child.on("exit", (code) => {
|
|
35
37
|
if (code === 0)
|
|
36
38
|
return resolve(stdout.trim());
|
|
37
|
-
|
|
39
|
+
const details = stderr.trim() || stdout.trim();
|
|
40
|
+
const suffix = details ? `: ${details}` : "";
|
|
41
|
+
reject(new Error(`${cmd} exited with code ${code}${suffix}`));
|
|
38
42
|
});
|
|
39
43
|
});
|
|
40
44
|
}
|