create-expo-super-stack 0.1.8 → 0.1.9

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 CHANGED
@@ -1,17 +1,16 @@
1
1
  #!/usr/bin/env node
2
- import { spawn } from 'node:child_process';
3
- import { access, mkdir, readdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
4
- import path from 'node:path';
5
- import { fileURLToPath, pathToFileURL } from 'node:url';
6
- import { cancel, isCancel, log, text } from '@clack/prompts';
7
- import { SUPER_STACK_SUCCESS_MESSAGE, collectOnboardPlan, defaultOnboardPlan, savePersonalOnboardDefaults, } from '@mr.dj2u/cli/onboarding';
8
- import { scaffoldProjectMemory } from '@mr.dj2u/cli/project-memory';
9
- const DEFAULT_PROJECT_NAME = 'my-expo-app';
10
- const CURRENT_EXPO_SDK_MAJOR = 56;
11
- const CURRENT_EXPO_SDK_VERSION = '~56.0.6';
2
+ import { spawn } from "node:child_process";
3
+ import { access, mkdir, readdir, readFile, rename, rm, writeFile, } from "node:fs/promises";
4
+ import path from "node:path";
5
+ import { fileURLToPath, pathToFileURL } from "node:url";
6
+ import { cancel, isCancel, log, text } from "@clack/prompts";
7
+ import { SUPER_STACK_SUCCESS_MESSAGE, collectOnboardPlan, defaultOnboardPlan, savePersonalOnboardDefaults, } from "@mr.dj2u/cli/onboarding";
8
+ import { scaffoldProjectMemory } from "@mr.dj2u/cli/project-memory";
9
+ import { generateProjectRoadmap, } from "@mr.dj2u/cli/roadmap";
10
+ const DEFAULT_PROJECT_NAME = "my-expo-app";
12
11
  const STYLIST_SYNC_API_ROUTES = [
13
- 'app/exposition/stylist-sync+api.ts',
14
- 'src/app/exposition/stylist-sync+api.ts',
12
+ "app/exposition/stylist-sync+api.ts",
13
+ "src/app/exposition/stylist-sync+api.ts",
15
14
  ];
16
15
  export async function main() {
17
16
  const initialParsed = parseArgs(process.argv.slice(2));
@@ -29,7 +28,7 @@ export async function main() {
29
28
  await runCreateExpoStack(createExpoStackArgs, parsed.mds.createExpoStackBin, projectParentDir);
30
29
  }
31
30
  else {
32
- console.log('Skipping create-expo-stack because --mds-skip-create was passed.');
31
+ console.log("Skipping create-expo-stack because --mds-skip-create was passed.");
33
32
  }
34
33
  const projectPath = await resolveGeneratedProjectPath(projectParentDir, projectName);
35
34
  const easSelected = await detectEasSetup(projectPath, parsed.createExpoStackArgs);
@@ -37,13 +36,15 @@ export async function main() {
37
36
  const plan = parsed.mds.yes
38
37
  ? defaultOnboardPlan(onboardArgv, projectPath)
39
38
  : await collectOnboardPlan(onboardArgv, projectPath);
40
- const movedAppDir = !parsed.mds.skipCreate && plan.answers.appDirectory === 'src'
39
+ const movedAppDir = !parsed.mds.skipCreate && plan.answers.appDirectory === "src"
41
40
  ? await moveRootAppIntoSrc(projectPath)
42
41
  : null;
43
- const consolidatedSourceRepairs = !parsed.mds.skipCreate && plan.answers.appDirectory === 'src'
42
+ const consolidatedSourceRepairs = !parsed.mds.skipCreate && plan.answers.appDirectory === "src"
44
43
  ? await consolidateRootSourceFolders(projectPath)
45
44
  : [];
46
- const movedImportRepairs = movedAppDir ? await repairMovedSrcAppImports(projectPath) : [];
45
+ const movedImportRepairs = movedAppDir
46
+ ? await repairMovedSrcAppImports(projectPath)
47
+ : [];
47
48
  const written = await scaffoldProjectMemory(projectPath, plan.answers, {
48
49
  force: parsed.mds.force,
49
50
  guidelinesTemplate: plan.guidelinesTemplate,
@@ -51,21 +52,24 @@ export async function main() {
51
52
  manageUniwind: parsed.mds.skipCreate,
52
53
  richBoilerplate: plan.richBoilerplate,
53
54
  });
54
- const postScaffoldImportRepairs = movedAppDir ? await repairMovedSrcAppImports(projectPath) : [];
55
+ const postScaffoldImportRepairs = movedAppDir
56
+ ? await repairMovedSrcAppImports(projectPath)
57
+ : [];
55
58
  const identifierRepairs = await repairExpoProjectIdentifiers(projectPath, projectName, plan.answers.targetPlatforms);
56
- const stylistWebOutputRepairs = plan.answers.targetPlatforms.includes('web')
59
+ const stylistWebOutputRepairs = plan.answers.targetPlatforms.includes("web")
57
60
  ? await repairExpoWebOutputForStylistLifecycle(projectPath, plan.answers.webOutput)
58
61
  : [];
59
62
  const hasStylistSyncRoute = await hasStylistSyncApiRoute(projectPath);
60
63
  const typeSupportRepairs = await repairGeneratedTypeSupport(projectPath, {
61
- needsNodeTypes: plan.answers.targetPlatforms.includes('web') && hasStylistSyncRoute,
62
- needsUniwindTypes: plan.answers.defaults.includes('uniwind'),
64
+ needsNodeTypes: hasStylistSyncRoute,
65
+ needsUniwindTypes: plan.answers.defaults.includes("uniwind"),
63
66
  });
64
67
  const nativeWindUiPickerRepairs = await repairGeneratedNativeWindUiPicker(projectPath);
68
+ const eslintConfigRepairs = await repairGeneratedEslintConfig(projectPath);
65
69
  console.log();
66
- console.log('MDS onboarding complete.');
70
+ console.log("MDS onboarding complete.");
67
71
  for (const result of written) {
68
- console.log(`${result.wrote ? 'CREATED' : 'KEPT'} ${path.relative(process.cwd(), result.filePath)}`);
72
+ console.log(`${result.wrote ? "CREATED" : "KEPT"} ${path.relative(process.cwd(), result.filePath)}`);
69
73
  }
70
74
  if (movedAppDir) {
71
75
  console.log(`MOVED ${path.relative(process.cwd(), movedAppDir.from)} -> ${path.relative(process.cwd(), movedAppDir.to)}`);
@@ -91,42 +95,60 @@ export async function main() {
91
95
  for (const result of nativeWindUiPickerRepairs) {
92
96
  console.log(`UPDATED ${path.relative(process.cwd(), result)}`);
93
97
  }
98
+ for (const result of eslintConfigRepairs) {
99
+ console.log(`UPDATED ${path.relative(process.cwd(), result)}`);
100
+ }
94
101
  if (plan.saveDefaults) {
95
102
  const defaultsPath = savePersonalOnboardDefaults(plan.answers);
96
103
  if (defaultsPath) {
97
104
  console.log(`Saved personal onboarding defaults: ${defaultsPath}`);
98
105
  }
99
106
  }
107
+ const roadmapStatus = await generateProjectRoadmap(projectPath, {
108
+ write: false,
109
+ preserveStatus: true,
110
+ });
111
+ if (roadmapStatus.blockedByMarkers) {
112
+ console.log();
113
+ console.log("Roadmap note:");
114
+ console.log("Unresolved # TodoForContext(optional): markers are still present in project/info.md, so MDS intentionally left the scaffolded phase template in place.");
115
+ console.log("Resolve those project/info.md markers first, then run `mds roadmap` or let your agent refresh project/todo.md from project/info.md.");
116
+ }
117
+ else if (roadmapStatus.needsClarification) {
118
+ console.log();
119
+ console.log("Roadmap note:");
120
+ console.log("The project docs are still too generic for a high-confidence derived roadmap, so have your agent ask clarifying questions and then rerun `mds roadmap`.");
121
+ }
100
122
  const packageManager = await detectPackageManager(projectPath, parsed.createExpoStackArgs);
101
123
  const noInstallRequested = hasNoInstallFlag(parsed.createExpoStackArgs);
102
124
  if (shouldRunExpoProjectChecks(parsed, noInstallRequested)) {
103
- await runExpoProjectChecks(projectPath, packageManager, {
104
- useLatestExpoSdk: plan.answers.useLatestExpoSdk,
105
- });
125
+ await runExpoProjectChecks(projectPath, packageManager);
106
126
  }
107
127
  else if (noInstallRequested) {
108
128
  console.log();
109
- console.log('Skipped install and Expo dependency repair because create-expo-stack was run with --no-install.');
129
+ console.log("Skipped install and Expo dependency repair because create-expo-stack was run with --no-install.");
110
130
  }
111
131
  console.log();
112
- console.log('Next steps:');
113
- console.log(` cd ${quoteDisplayArg(path.relative(process.cwd(), projectPath) || '.')}`);
132
+ const nextStepsCommands = [
133
+ `cd ${quoteDisplayArg(path.relative(process.cwd(), projectPath) || ".")}`,
134
+ ];
114
135
  if (noInstallRequested || parsed.mds.skipExpoFix || parsed.mds.skipCreate) {
115
- console.log(` ${buildInstallCommand(packageManager).display}`);
116
- if (plan.answers.useLatestExpoSdk && (await shouldRunExpoLatestSdkCommand(projectPath))) {
117
- console.log(` ${buildExpoLatestSdkCommand(packageManager).display}`);
136
+ nextStepsCommands.push(buildInstallCommand(packageManager).display);
137
+ if (await shouldRunExpoLatestSdkCommand(projectPath)) {
138
+ nextStepsCommands.push(buildExpoLatestSdkCommand(packageManager).display);
118
139
  }
119
- console.log(` ${buildExpoInstallFixCommand(packageManager).display}`);
140
+ nextStepsCommands.push(buildExpoInstallFixCommand(packageManager).display);
120
141
  if (await shouldInstallExpoFontPeer(projectPath)) {
121
- console.log(` ${buildExpoFontInstallCommand(packageManager).display}`);
142
+ nextStepsCommands.push(buildExpoFontInstallCommand(packageManager).display);
122
143
  }
123
- console.log(` ${buildExpoDoctorCommand(packageManager).display}`);
144
+ nextStepsCommands.push(buildExpoDoctorCommand(packageManager).display);
124
145
  }
125
- console.log(` ${buildRunScriptCommand(packageManager, 'clear-expo-start')}`);
146
+ nextStepsCommands.push(buildRunScriptCommand(packageManager, "clear-expo-start"));
147
+ printCopyableCommands("Next steps", nextStepsCommands);
126
148
  console.log();
127
149
  console.log(SUPER_STACK_SUCCESS_MESSAGE);
128
150
  console.log();
129
- console.log('For the full dev-suite locally, use the generated scripts or install @mr.dj2u/cli in the app.');
151
+ console.log("For the full dev-suite locally, use the generated scripts or install @mr.dj2u/cli in the app.");
130
152
  }
131
153
  export function prepareCreateExpoStackArgsForWrapper(args, _skipExpoFix = false) {
132
154
  return args;
@@ -146,8 +168,8 @@ export function parseArgs(args) {
146
168
  if (!arg) {
147
169
  continue;
148
170
  }
149
- if (!arg.startsWith('--') && !projectName) {
150
- if (arg === '-h') {
171
+ if (!arg.startsWith("--") && !projectName) {
172
+ if (arg === "-h") {
151
173
  helpRequested = true;
152
174
  continue;
153
175
  }
@@ -155,192 +177,191 @@ export function parseArgs(args) {
155
177
  createExpoStackArgs.push(arg);
156
178
  continue;
157
179
  }
158
- if (arg === '--help' || arg === '-h' || arg === '--mds-help') {
180
+ if (arg === "--help" || arg === "-h" || arg === "--mds-help") {
159
181
  helpRequested = true;
160
182
  continue;
161
183
  }
162
- if (arg === '--mds-force') {
184
+ if (arg === "--mds-force") {
163
185
  mds.force = true;
164
186
  continue;
165
187
  }
166
- if (arg === '--mds-no-rich') {
188
+ if (arg === "--mds-no-rich") {
167
189
  mds.rich = false;
168
190
  continue;
169
191
  }
170
- if (arg === '--mds-rich') {
192
+ if (arg === "--mds-rich") {
171
193
  mds.rich = true;
172
194
  continue;
173
195
  }
174
- if (arg === '--mds-save-defaults') {
196
+ if (arg === "--mds-save-defaults") {
175
197
  mds.saveDefaults = true;
176
198
  continue;
177
199
  }
178
- if (arg === '--mds-no-save-defaults') {
200
+ if (arg === "--mds-no-save-defaults") {
179
201
  mds.saveDefaults = false;
180
202
  continue;
181
203
  }
182
- if (arg === '--mds-yes' || arg === '--mds-non-interactive') {
204
+ if (arg === "--mds-yes" || arg === "--mds-non-interactive") {
183
205
  mds.yes = true;
184
206
  continue;
185
207
  }
186
- if (arg === '--mds-skip-create') {
208
+ if (arg === "--mds-skip-create") {
187
209
  mds.skipCreate = true;
188
210
  continue;
189
211
  }
190
- if (arg === '--mds-skip-expo-fix' || arg === '--mds-no-expo-fix') {
212
+ if (arg === "--mds-skip-expo-fix" || arg === "--mds-no-expo-fix") {
191
213
  mds.skipExpoFix = true;
192
214
  continue;
193
215
  }
194
- if (arg.startsWith('--mds-create-expo-stack-bin=')) {
195
- mds.createExpoStackBin = arg.slice('--mds-create-expo-stack-bin='.length);
216
+ if (arg.startsWith("--mds-create-expo-stack-bin=")) {
217
+ mds.createExpoStackBin = arg.slice("--mds-create-expo-stack-bin=".length);
196
218
  continue;
197
219
  }
198
- if (arg === '--mds-guidelines-template') {
220
+ if (arg === "--mds-guidelines-template") {
199
221
  mds.guidelinesTemplate = true;
200
222
  continue;
201
223
  }
202
- if (arg === '--mds-no-guidelines-template' || arg === '--mds-guidelines-template=false') {
224
+ if (arg === "--mds-no-guidelines-template" ||
225
+ arg === "--mds-guidelines-template=false") {
203
226
  mds.guidelinesTemplate = false;
204
227
  continue;
205
228
  }
206
- if (arg.startsWith('--mds-guidelines-template=')) {
229
+ if (arg.startsWith("--mds-guidelines-template=")) {
207
230
  mds.guidelinesTemplate = true;
208
- mds.guidelinesTemplatePath = arg.slice('--mds-guidelines-template='.length);
231
+ mds.guidelinesTemplatePath = arg.slice("--mds-guidelines-template=".length);
209
232
  continue;
210
233
  }
211
- if (arg.startsWith('--mds-guidelines-template-path=')) {
234
+ if (arg.startsWith("--mds-guidelines-template-path=")) {
212
235
  mds.guidelinesTemplate = true;
213
- mds.guidelinesTemplatePath = arg.slice('--mds-guidelines-template-path='.length);
236
+ mds.guidelinesTemplatePath = arg.slice("--mds-guidelines-template-path=".length);
214
237
  continue;
215
238
  }
216
- if (arg.startsWith('--mds-defaults=')) {
217
- mds.defaults = splitList(arg.slice('--mds-defaults='.length));
239
+ if (arg.startsWith("--mds-defaults=")) {
240
+ mds.defaults = splitList(arg.slice("--mds-defaults=".length));
218
241
  continue;
219
242
  }
220
- if (arg.startsWith('--mds-app-name=')) {
221
- mds.appName = arg.slice('--mds-app-name='.length);
243
+ if (arg.startsWith("--mds-app-name=")) {
244
+ mds.appName = arg.slice("--mds-app-name=".length);
222
245
  continue;
223
246
  }
224
- if (arg.startsWith('--mds-audience=')) {
225
- mds.audience = arg.slice('--mds-audience='.length);
247
+ if (arg.startsWith("--mds-audience=")) {
248
+ mds.audience = arg.slice("--mds-audience=".length);
226
249
  continue;
227
250
  }
228
- if (arg.startsWith('--mds-core-flows=')) {
229
- mds.coreFlows = arg.slice('--mds-core-flows='.length);
251
+ if (arg.startsWith("--mds-core-flows=")) {
252
+ mds.coreFlows = arg.slice("--mds-core-flows=".length);
230
253
  continue;
231
254
  }
232
- if (arg.startsWith('--mds-screens=')) {
233
- mds.screens = arg.slice('--mds-screens='.length);
255
+ if (arg.startsWith("--mds-screens=")) {
256
+ mds.screens = arg.slice("--mds-screens=".length);
234
257
  continue;
235
258
  }
236
- if (arg.startsWith('--mds-data-needs=')) {
237
- mds.dataNeeds = arg.slice('--mds-data-needs='.length);
259
+ if (arg.startsWith("--mds-data-needs=")) {
260
+ mds.dataNeeds = arg.slice("--mds-data-needs=".length);
238
261
  continue;
239
262
  }
240
- if (arg.startsWith('--mds-data-start=')) {
241
- const value = arg.slice('--mds-data-start='.length);
242
- if (value === 'local' || value === 'supabase') {
263
+ if (arg.startsWith("--mds-data-start=")) {
264
+ const value = arg.slice("--mds-data-start=".length);
265
+ if (value === "local" || value === "supabase") {
243
266
  mds.dataStart = value;
244
267
  }
245
268
  continue;
246
269
  }
247
- if (arg.startsWith('--mds-deployment-target=')) {
248
- mds.deploymentTarget = arg.slice('--mds-deployment-target='.length);
270
+ if (arg.startsWith("--mds-deployment-target=")) {
271
+ mds.deploymentTarget = arg.slice("--mds-deployment-target=".length);
249
272
  continue;
250
273
  }
251
- if (arg === '--mds-test-to-main') {
274
+ if (arg === "--mds-test-to-main") {
252
275
  mds.testToMain = true;
253
276
  continue;
254
277
  }
255
- if (arg === '--mds-no-test-to-main') {
278
+ if (arg === "--mds-no-test-to-main") {
256
279
  mds.testToMain = false;
257
280
  continue;
258
281
  }
259
- if (arg.startsWith('--mds-platforms=')) {
260
- mds.platforms = splitList(arg.slice('--mds-platforms='.length));
282
+ if (arg.startsWith("--mds-platforms=")) {
283
+ mds.platforms = splitList(arg.slice("--mds-platforms=".length));
261
284
  continue;
262
285
  }
263
- if (arg.startsWith('--mds-first-platform=')) {
264
- mds.firstPlatform = arg.slice('--mds-first-platform='.length);
286
+ if (arg.startsWith("--mds-first-platform=")) {
287
+ mds.firstPlatform = arg.slice("--mds-first-platform=".length);
265
288
  continue;
266
289
  }
267
- if (arg.startsWith('--mds-platform-strategy=')) {
268
- const value = arg.slice('--mds-platform-strategy='.length);
269
- if (value === 'folders' || value === 'files-only') {
290
+ if (arg.startsWith("--mds-platform-strategy=")) {
291
+ const value = arg.slice("--mds-platform-strategy=".length);
292
+ if (value === "folders" || value === "files-only") {
270
293
  mds.platformStrategy = value;
271
294
  }
272
295
  continue;
273
296
  }
274
- if (arg.startsWith('--mds-app-directory=')) {
275
- const value = arg.slice('--mds-app-directory='.length);
276
- if (value === 'src' || value === 'root') {
297
+ if (arg.startsWith("--mds-app-directory=")) {
298
+ const value = arg.slice("--mds-app-directory=".length);
299
+ if (value === "src" || value === "root") {
277
300
  mds.appDirectory = value;
278
301
  }
279
302
  continue;
280
303
  }
281
- if (arg.startsWith('--mds-platform-layouts=')) {
282
- const value = arg.slice('--mds-platform-layouts='.length);
283
- if (value === 'shared' || value === 'platform-specific') {
304
+ if (arg.startsWith("--mds-platform-layouts=")) {
305
+ const value = arg.slice("--mds-platform-layouts=".length);
306
+ if (value === "shared" || value === "platform-specific") {
284
307
  mds.platformLayouts = value;
285
308
  }
286
309
  continue;
287
310
  }
288
- if (arg.startsWith('--mds-web-output=')) {
289
- const value = arg.slice('--mds-web-output='.length);
290
- if (value === 'static' || value === 'server' || value === 'spa' || value === 'none') {
311
+ if (arg.startsWith("--mds-web-output=")) {
312
+ const value = arg.slice("--mds-web-output=".length);
313
+ if (value === "static" ||
314
+ value === "server" ||
315
+ value === "spa" ||
316
+ value === "none") {
291
317
  mds.webOutput = value;
292
318
  }
293
319
  continue;
294
320
  }
295
- if (arg.startsWith('--mds-deployed-server=')) {
296
- const value = arg.slice('--mds-deployed-server='.length);
297
- if (value === 'standard-expo' || value === 'custom' || value === 'none') {
321
+ if (arg.startsWith("--mds-deployed-server=")) {
322
+ const value = arg.slice("--mds-deployed-server=".length);
323
+ if (value === "standard-expo" || value === "custom" || value === "none") {
298
324
  mds.deployedServer = value;
299
325
  }
300
326
  continue;
301
327
  }
302
- if (arg === '--mds-create-expo-components') {
328
+ if (arg === "--mds-create-expo-components") {
303
329
  mds.createExpoComponents = true;
304
330
  continue;
305
331
  }
306
- if (arg === '--mds-no-create-expo-components') {
332
+ if (arg === "--mds-no-create-expo-components") {
307
333
  mds.createExpoComponents = false;
308
334
  continue;
309
335
  }
310
- if (arg === '--mds-latest-expo-sdk') {
311
- mds.latestExpoSdk = true;
336
+ if (arg === "--mds-latest-expo-sdk" || arg === "--mds-no-latest-expo-sdk") {
312
337
  continue;
313
338
  }
314
- if (arg === '--mds-no-latest-expo-sdk') {
315
- mds.latestExpoSdk = false;
316
- continue;
317
- }
318
- if (arg === '--mds-expo-ui') {
339
+ if (arg === "--mds-expo-ui") {
319
340
  mds.expoUi = true;
320
341
  continue;
321
342
  }
322
- if (arg === '--mds-no-expo-ui') {
343
+ if (arg === "--mds-no-expo-ui") {
323
344
  mds.expoUi = false;
324
345
  continue;
325
346
  }
326
- if (arg === '--mds-expo-ui-universal') {
347
+ if (arg === "--mds-expo-ui-universal") {
327
348
  mds.expoUiUniversal = true;
328
349
  continue;
329
350
  }
330
- if (arg === '--mds-no-expo-ui-universal') {
351
+ if (arg === "--mds-no-expo-ui-universal") {
331
352
  mds.expoUiUniversal = false;
332
353
  continue;
333
354
  }
334
- if (arg === '--mds-expo-native-tabs') {
355
+ if (arg === "--mds-expo-native-tabs") {
335
356
  mds.expoNativeTabs = true;
336
357
  continue;
337
358
  }
338
- if (arg === '--mds-no-expo-native-tabs') {
359
+ if (arg === "--mds-no-expo-native-tabs") {
339
360
  mds.expoNativeTabs = false;
340
361
  continue;
341
362
  }
342
- if (arg.startsWith('--mds-eas-uses=')) {
343
- mds.easUses = splitList(arg.slice('--mds-eas-uses='.length));
363
+ if (arg.startsWith("--mds-eas-uses=")) {
364
+ mds.easUses = splitList(arg.slice("--mds-eas-uses=".length));
344
365
  continue;
345
366
  }
346
367
  createExpoStackArgs.push(arg);
@@ -354,33 +375,33 @@ export function parseArgs(args) {
354
375
  }
355
376
  export function renderHelpText() {
356
377
  return [
357
- 'create-expo-super-stack',
358
- '',
359
- 'Usage:',
360
- ' create-expo-super-stack [project-name] [create-expo-stack options] [mds options]',
361
- '',
362
- 'Examples:',
363
- ' create-expo-super-stack my-app --expo-router --uniwind',
364
- ' create-expo-super-stack ../MyApp --expo-router --mds-yes',
365
- '',
366
- 'Common mds options:',
367
- ' --mds-yes Run non-interactive onboarding defaults',
368
- ' --mds-save-defaults Save onboarding answers as personal defaults',
369
- ' --mds-no-save-defaults Do not save onboarding answers as personal defaults',
370
- ' --mds-skip-create Skip create-expo-stack and only run onboarding in an existing app',
371
- ' --mds-skip-expo-fix Skip dependency install/fix/doctor repair pass',
372
- ' --mds-guidelines-template Use bundled MDS project/guidelines template',
373
- ' --mds-no-guidelines-template Do not use the bundled MDS project/guidelines template',
374
- ' --mds-app-name=<name> Set display app name for project memory',
375
- ' --mds-screens= List must-include screens for project memory',
376
- ' --mds-expo-ui-universal Use Expo UI Universal components when Expo UI is selected',
377
- '',
378
- 'Help:',
379
- ' -h, --help Show this help and exit',
380
- '',
381
- 'Note:',
382
- ' Unknown non-mds flags are forwarded to create-expo-stack.',
383
- ].join('\n');
378
+ "create-expo-super-stack",
379
+ "",
380
+ "Usage:",
381
+ " create-expo-super-stack [project-name] [create-expo-stack options] [mds options]",
382
+ "",
383
+ "Examples:",
384
+ " create-expo-super-stack my-app --expo-router --uniwind",
385
+ " create-expo-super-stack ../MyApp --expo-router --mds-yes",
386
+ "",
387
+ "Common mds options:",
388
+ " --mds-yes Run non-interactive onboarding defaults",
389
+ " --mds-save-defaults Save onboarding answers as personal defaults",
390
+ " --mds-no-save-defaults Do not save onboarding answers as personal defaults",
391
+ " --mds-skip-create Skip create-expo-stack and only run onboarding in an existing app",
392
+ " --mds-skip-expo-fix Skip dependency install/fix/doctor repair pass",
393
+ " --mds-guidelines-template Use bundled MDS project/guidelines template",
394
+ " --mds-no-guidelines-template Do not use the bundled MDS project/guidelines template",
395
+ " --mds-app-name=<name> Set display app name for project memory",
396
+ " --mds-screens= List must-include screens for project memory",
397
+ " --mds-expo-ui-universal Use Expo UI Universal components when Expo UI is selected",
398
+ "",
399
+ "Help:",
400
+ " -h, --help Show this help and exit",
401
+ "",
402
+ "Note:",
403
+ " Unknown non-mds flags are forwarded to create-expo-stack.",
404
+ ].join("\n");
384
405
  }
385
406
  export function withResolvedProjectName(parsed, projectName) {
386
407
  const target = resolveProjectTarget(parsed.projectName ?? projectName);
@@ -419,18 +440,18 @@ async function promptForMissingProjectName(parsed) {
419
440
  return DEFAULT_PROJECT_NAME;
420
441
  }
421
442
  const answer = await text({
422
- message: 'What do you want to name your Expo app?',
443
+ message: "What do you want to name your Expo app?",
423
444
  placeholder: DEFAULT_PROJECT_NAME,
424
445
  defaultValue: DEFAULT_PROJECT_NAME,
425
446
  validate: (value) => {
426
447
  if (!value.trim()) {
427
- return 'Please enter an app name, or press Enter to use the visible default.';
448
+ return "Please enter an app name, or press Enter to use the visible default.";
428
449
  }
429
450
  return undefined;
430
451
  },
431
452
  });
432
453
  if (isCancel(answer)) {
433
- cancel('Cancelled. You can rerun create-expo-super-stack whenever you are ready.');
454
+ cancel("Cancelled. You can rerun create-expo-super-stack whenever you are ready.");
434
455
  process.exit(0);
435
456
  }
436
457
  const projectName = answer.trim() || DEFAULT_PROJECT_NAME;
@@ -438,9 +459,9 @@ async function promptForMissingProjectName(parsed) {
438
459
  return projectName;
439
460
  }
440
461
  function printIntro(projectName, createExpoStackArgs, projectParentDir = process.cwd()) {
441
- console.log('create-expo-super-stack');
462
+ console.log("create-expo-super-stack");
442
463
  console.log();
443
- console.log('This uses create-expo-stack under the hood, then applies MDS onboarding.');
464
+ console.log("This uses create-expo-stack under the hood, then applies MDS onboarding.");
444
465
  console.log(`Delegating: create-expo-stack ${formatDisplayArgs(createExpoStackArgs)}`);
445
466
  console.log(`Target app: ${projectName}`);
446
467
  if (projectParentDir !== process.cwd()) {
@@ -454,25 +475,27 @@ async function runCreateExpoStack(args, overrideBin, cwd = process.cwd()) {
454
475
  await new Promise((resolve, reject) => {
455
476
  const child = spawn(command.command, [...command.args, ...args], {
456
477
  cwd,
457
- shell: command.shell ?? process.platform === 'win32',
458
- stdio: 'inherit',
478
+ shell: command.shell ?? process.platform === "win32",
479
+ stdio: "inherit",
459
480
  windowsHide: true,
460
481
  });
461
- child.on('error', reject);
462
- child.on('close', (code) => {
482
+ child.on("error", reject);
483
+ child.on("close", (code) => {
463
484
  if (code === 0) {
464
485
  resolve();
465
486
  }
466
487
  else {
467
- reject(new Error(`create-expo-stack exited with code ${code ?? 'unknown'}.`));
488
+ reject(new Error(`create-expo-stack exited with code ${code ?? "unknown"}.`));
468
489
  }
469
490
  });
470
491
  });
471
492
  }
472
493
  export function resolveProjectTarget(rawProjectName, cwd = process.cwd()) {
473
494
  const trimmed = rawProjectName.trim();
474
- const normalized = trimmed.replace(/[\\/]+$/u, '') || DEFAULT_PROJECT_NAME;
475
- const hasPathSyntax = path.isAbsolute(normalized) || normalized.includes('/') || normalized.includes('\\');
495
+ const normalized = trimmed.replace(/[\\/]+$/u, "") || DEFAULT_PROJECT_NAME;
496
+ const hasPathSyntax = path.isAbsolute(normalized) ||
497
+ normalized.includes("/") ||
498
+ normalized.includes("\\");
476
499
  if (!hasPathSyntax) {
477
500
  return {
478
501
  projectName: normalized,
@@ -488,7 +511,7 @@ export function resolveProjectTarget(rawProjectName, cwd = process.cwd()) {
488
511
  }
489
512
  function replaceProjectArg(args, projectName) {
490
513
  const nextArgs = [...args];
491
- const projectArgIndex = nextArgs.findIndex((arg) => !arg.startsWith('--'));
514
+ const projectArgIndex = nextArgs.findIndex((arg) => !arg.startsWith("--"));
492
515
  if (projectArgIndex >= 0) {
493
516
  nextArgs[projectArgIndex] = projectName;
494
517
  return nextArgs;
@@ -496,27 +519,24 @@ function replaceProjectArg(args, projectName) {
496
519
  return [projectName, ...nextArgs];
497
520
  }
498
521
  export function validateCreateExpoStackArgs(args) {
499
- const authFlags = ['--supabase', '--firebase'].filter((flag) => args.some((arg) => arg === flag || arg.startsWith(`${flag}=`)));
522
+ const authFlags = ["--supabase", "--firebase"].filter((flag) => args.some((arg) => arg === flag || arg.startsWith(`${flag}=`)));
500
523
  if (authFlags.length > 1) {
501
- throw new Error(`Choose one create-expo-stack auth provider, not ${authFlags.join(' and ')}. create-expo-stack scaffolds a single auth slice; Super Stack can still document future data/backend plans in project/.`);
524
+ throw new Error(`Choose one create-expo-stack auth provider, not ${authFlags.join(" and ")}. create-expo-stack scaffolds a single auth slice; Super Stack can still document future data/backend plans in project/.`);
502
525
  }
503
526
  }
504
- export async function runExpoProjectChecks(projectPath, packageManager, options = {}) {
527
+ export async function runExpoProjectChecks(projectPath, packageManager) {
505
528
  console.log();
506
- console.log('Installing MDS-added dependencies, then running Expo dependency repair and doctor.');
529
+ console.log("Installing MDS-added dependencies, then running Expo dependency repair and doctor.");
507
530
  await runProjectCommand(projectPath, buildInstallCommand(packageManager));
508
531
  const missingWindowsOxideBinding = await resolveMissingWindowsTailwindOxideBinding(projectPath);
509
532
  if (missingWindowsOxideBinding) {
510
533
  await runProjectCommand(projectPath, buildAddDevDependencyCommand(packageManager, missingWindowsOxideBinding));
511
534
  }
512
- if (options.useLatestExpoSdk && (await shouldRunExpoLatestSdkCommand(projectPath))) {
535
+ if (await shouldRunExpoLatestSdkCommand(projectPath)) {
513
536
  await runProjectCommand(projectPath, buildExpoLatestSdkCommand(packageManager));
514
537
  }
515
- else if (options.useLatestExpoSdk) {
516
- console.log(` Expo SDK already targets SDK ${CURRENT_EXPO_SDK_MAJOR}; skipping expo@latest.`);
517
- }
518
- if (await shouldRunExpoPinnedSdkCommand(projectPath)) {
519
- await runProjectCommand(projectPath, buildExpoPinnedSdkCommand(packageManager));
538
+ else {
539
+ console.log(" Expo already uses the latest release channel; skipping expo@latest.");
520
540
  }
521
541
  await runProjectCommand(projectPath, buildExpoInstallFixCommand(packageManager));
522
542
  if (await shouldInstallExpoFontPeer(projectPath)) {
@@ -530,26 +550,30 @@ async function runProjectCommand(projectPath, spec) {
530
550
  await new Promise((resolve, reject) => {
531
551
  const child = spawn(spec.command, spec.args, {
532
552
  cwd: projectPath,
533
- shell: spec.shell ?? process.platform === 'win32',
534
- stdio: 'inherit',
553
+ shell: spec.shell ?? process.platform === "win32",
554
+ stdio: "inherit",
535
555
  env: spec.env ? { ...process.env, ...spec.env } : process.env,
536
556
  windowsHide: true,
537
557
  });
538
- child.on('error', reject);
539
- child.on('close', (code) => {
558
+ child.on("error", reject);
559
+ child.on("close", (code) => {
540
560
  if (code === 0) {
541
561
  resolve();
542
562
  }
543
563
  else {
544
- reject(new Error(`${spec.display} exited with code ${code ?? 'unknown'}.`));
564
+ reject(new Error(`${spec.display} exited with code ${code ?? "unknown"}.`));
545
565
  }
546
566
  });
547
567
  });
548
568
  }
549
569
  async function resolveCreateExpoStackCommand(overrideBin) {
550
- const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
551
- const executable = process.platform === 'win32' ? 'create-expo-stack.cmd' : 'create-expo-stack';
552
- const override = overrideBin ?? process.env.MRDJ_CREATE_EXPO_STACK_BIN ?? process.env.CREATE_EXPO_STACK_BIN;
570
+ const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
571
+ const executable = process.platform === "win32"
572
+ ? "create-expo-stack.cmd"
573
+ : "create-expo-stack";
574
+ const override = overrideBin ??
575
+ process.env.MRDJ_CREATE_EXPO_STACK_BIN ??
576
+ process.env.CREATE_EXPO_STACK_BIN;
553
577
  if (override) {
554
578
  const overridePath = path.resolve(override);
555
579
  return {
@@ -559,8 +583,8 @@ async function resolveCreateExpoStackCommand(overrideBin) {
559
583
  shell: false,
560
584
  };
561
585
  }
562
- const localForkCliRoot = path.join(packageRoot, '..', '..', '..', 'create-expo-stack', 'cli');
563
- const localForkBin = path.join(localForkCliRoot, 'bin', 'create-expo-stack.js');
586
+ const localForkCliRoot = path.join(packageRoot, "..", "..", "..", "create-expo-stack", "cli");
587
+ const localForkBin = path.join(localForkCliRoot, "bin", "create-expo-stack.js");
564
588
  if (await pathExists(localForkBin)) {
565
589
  await ensureLocalCreateExpoStackBuild(localForkCliRoot);
566
590
  return {
@@ -571,8 +595,8 @@ async function resolveCreateExpoStackCommand(overrideBin) {
571
595
  };
572
596
  }
573
597
  const scopedForkCandidates = [
574
- path.join(packageRoot, 'node_modules', '@mr.dj2u', 'create-expo-stack', 'bin', 'create-expo-stack.js'),
575
- path.join(packageRoot, '..', '..', 'node_modules', '@mr.dj2u', 'create-expo-stack', 'bin', 'create-expo-stack.js'),
598
+ path.join(packageRoot, "node_modules", "@mr.dj2u", "create-expo-stack", "bin", "create-expo-stack.js"),
599
+ path.join(packageRoot, "..", "..", "node_modules", "@mr.dj2u", "create-expo-stack", "bin", "create-expo-stack.js"),
576
600
  ];
577
601
  for (const candidate of scopedForkCandidates) {
578
602
  if (await pathExists(candidate)) {
@@ -585,8 +609,8 @@ async function resolveCreateExpoStackCommand(overrideBin) {
585
609
  }
586
610
  }
587
611
  const candidates = [
588
- path.join(packageRoot, 'node_modules', '.bin', executable),
589
- path.join(packageRoot, '..', '..', 'node_modules', '.bin', executable),
612
+ path.join(packageRoot, "node_modules", ".bin", executable),
613
+ path.join(packageRoot, "..", "..", "node_modules", ".bin", executable),
590
614
  ];
591
615
  for (const candidate of candidates) {
592
616
  if (await pathExists(candidate)) {
@@ -598,58 +622,65 @@ async function resolveCreateExpoStackCommand(overrideBin) {
598
622
  }
599
623
  }
600
624
  return {
601
- command: 'create-expo-stack',
625
+ command: "create-expo-stack",
602
626
  args: [],
603
- display: 'create-expo-stack',
627
+ display: "create-expo-stack",
604
628
  };
605
629
  }
606
630
  async function ensureLocalCreateExpoStackBuild(localForkCliRoot) {
607
- const buildEntry = path.join(localForkCliRoot, 'build', 'cli.js');
631
+ const buildEntry = path.join(localForkCliRoot, "build", "cli.js");
608
632
  if (await pathExists(buildEntry)) {
609
633
  return;
610
634
  }
611
635
  console.log(`Building local create-expo-stack fork: ${localForkCliRoot}`);
612
- if (await commandExists('bun')) {
636
+ if (await commandExists("bun")) {
613
637
  await runProjectCommand(localForkCliRoot, {
614
- command: 'bun',
615
- args: ['run', 'build'],
616
- display: 'bun run build',
638
+ command: "bun",
639
+ args: ["run", "build"],
640
+ display: "bun run build",
617
641
  });
618
642
  return;
619
643
  }
620
- console.log('Bun was not found, so using npm to build the local fork.');
621
- if (!(await pathExists(path.join(localForkCliRoot, 'node_modules')))) {
644
+ console.log("Bun was not found, so using npm to build the local fork.");
645
+ if (!(await pathExists(path.join(localForkCliRoot, "node_modules")))) {
622
646
  await runProjectCommand(localForkCliRoot, {
623
- command: 'npm',
624
- args: ['install'],
625
- display: 'npm install',
647
+ command: "npm",
648
+ args: ["install"],
649
+ display: "npm install",
626
650
  });
627
651
  }
628
652
  await runProjectCommand(localForkCliRoot, {
629
- command: 'npx',
630
- args: ['tsc', '-p', '.'],
631
- display: 'npx tsc -p .',
653
+ command: "npx",
654
+ args: ["tsc", "-p", "."],
655
+ display: "npx tsc -p .",
632
656
  });
633
657
  await runProjectCommand(localForkCliRoot, {
634
- command: 'npx',
635
- args: ['copyfiles', '-u', '2', '-a', './src/templates/**/*', './build/templates'],
658
+ command: "npx",
659
+ args: [
660
+ "copyfiles",
661
+ "-u",
662
+ "2",
663
+ "-a",
664
+ "./src/templates/**/*",
665
+ "./build/templates",
666
+ ],
636
667
  display: 'npx copyfiles -u 2 -a "./src/templates/**/*" ./build/templates',
637
668
  });
638
669
  }
639
670
  async function commandExists(command) {
640
671
  return new Promise((resolve) => {
641
- const child = spawn(command, ['--version'], {
642
- shell: process.platform === 'win32',
643
- stdio: 'ignore',
672
+ const child = spawn(command, ["--version"], {
673
+ shell: process.platform === "win32",
674
+ stdio: "ignore",
644
675
  windowsHide: true,
645
676
  });
646
- child.on('error', () => resolve(false));
647
- child.on('close', (code) => resolve(code === 0));
677
+ child.on("error", () => resolve(false));
678
+ child.on("close", (code) => resolve(code === 0));
648
679
  });
649
680
  }
650
681
  async function resolveGeneratedProjectPath(cwd, projectName) {
651
682
  const directPath = path.resolve(cwd, projectName);
652
- if (await pathExists(path.join(directPath, 'package.json'))) {
683
+ if (await pathExists(path.join(directPath, "package.json"))) {
653
684
  return directPath;
654
685
  }
655
686
  const fromCesConfig = await findProjectFromCesConfig(cwd, projectName);
@@ -659,8 +690,8 @@ async function resolveGeneratedProjectPath(cwd, projectName) {
659
690
  return directPath;
660
691
  }
661
692
  async function moveRootAppIntoSrc(projectPath) {
662
- const rootAppDir = path.join(projectPath, 'app');
663
- const srcAppDir = path.join(projectPath, 'src', 'app');
693
+ const rootAppDir = path.join(projectPath, "app");
694
+ const srcAppDir = path.join(projectPath, "src", "app");
664
695
  if (!(await pathExists(rootAppDir)) || (await pathExists(srcAppDir))) {
665
696
  return null;
666
697
  }
@@ -669,14 +700,14 @@ async function moveRootAppIntoSrc(projectPath) {
669
700
  return { from: rootAppDir, to: srcAppDir };
670
701
  }
671
702
  async function consolidateRootSourceFolders(projectPath) {
672
- const folderNames = ['components', 'theme', 'lib'];
703
+ const folderNames = ["components", "theme", "lib"];
673
704
  const updatedPaths = [];
674
705
  for (const folderName of folderNames) {
675
706
  const rootDir = path.join(projectPath, folderName);
676
707
  if (!(await pathExists(rootDir))) {
677
708
  continue;
678
709
  }
679
- const srcDir = path.join(projectPath, 'src', folderName);
710
+ const srcDir = path.join(projectPath, "src", folderName);
680
711
  if (!(await pathExists(srcDir))) {
681
712
  await mkdir(path.dirname(srcDir), { recursive: true });
682
713
  await rename(rootDir, srcDir);
@@ -710,7 +741,7 @@ async function mergeDirectoryInto(sourceDir, targetDir) {
710
741
  export async function repairMovedSrcAppImports(projectPath) {
711
742
  const layoutPaths = [
712
743
  {
713
- filePath: path.join(projectPath, 'src', 'app', '(tabs)', '_layout.tsx'),
744
+ filePath: path.join(projectPath, "src", "app", "(tabs)", "_layout.tsx"),
714
745
  replacements: [
715
746
  ['"../components/HeaderButton"', '"../../components/HeaderButton"'],
716
747
  ['"../components/TabBarIcon"', '"../../components/TabBarIcon"'],
@@ -719,7 +750,7 @@ export async function repairMovedSrcAppImports(projectPath) {
719
750
  ],
720
751
  },
721
752
  {
722
- filePath: path.join(projectPath, 'src', 'app', '(drawer)', '_layout.tsx'),
753
+ filePath: path.join(projectPath, "src", "app", "(drawer)", "_layout.tsx"),
723
754
  replacements: [
724
755
  ['"../components/HeaderButton"', '"../../components/HeaderButton"'],
725
756
  ['"../components/TabBarIcon"', '"../../components/TabBarIcon"'],
@@ -728,11 +759,17 @@ export async function repairMovedSrcAppImports(projectPath) {
728
759
  ],
729
760
  },
730
761
  {
731
- filePath: path.join(projectPath, 'src', 'app', '(drawer)', '(tabs)', '_layout.tsx'),
762
+ filePath: path.join(projectPath, "src", "app", "(drawer)", "(tabs)", "_layout.tsx"),
732
763
  replacements: [
733
- ['"../../components/HeaderButton"', '"../../../components/HeaderButton"'],
764
+ [
765
+ '"../../components/HeaderButton"',
766
+ '"../../../components/HeaderButton"',
767
+ ],
734
768
  ['"../../components/TabBarIcon"', '"../../../components/TabBarIcon"'],
735
- ["'../../components/HeaderButton'", "'../../../components/HeaderButton'"],
769
+ [
770
+ "'../../components/HeaderButton'",
771
+ "'../../../components/HeaderButton'",
772
+ ],
736
773
  ["'../../components/TabBarIcon'", "'../../../components/TabBarIcon'"],
737
774
  ],
738
775
  },
@@ -750,7 +787,7 @@ export async function repairMovedSrcAppImports(projectPath) {
750
787
  if (updated === raw) {
751
788
  continue;
752
789
  }
753
- await writeFile(layout.filePath, updated, 'utf8');
790
+ await writeFile(layout.filePath, updated, "utf8");
754
791
  updatedPaths.push(layout.filePath);
755
792
  }
756
793
  return updatedPaths;
@@ -759,8 +796,10 @@ export async function repairGeneratedTypeSupport(projectPath, options = {}) {
759
796
  const updatedPaths = new Set();
760
797
  let shouldIncludeUniwindTypes = false;
761
798
  if (options.needsUniwindTypes) {
762
- const packageJson = await readJson(path.join(projectPath, 'package.json'));
763
- const dependencies = isRecord(packageJson.dependencies) ? packageJson.dependencies : {};
799
+ const packageJson = await readJson(path.join(projectPath, "package.json"));
800
+ const dependencies = isRecord(packageJson.dependencies)
801
+ ? packageJson.dependencies
802
+ : {};
764
803
  const devDependencies = isRecord(packageJson.devDependencies)
765
804
  ? packageJson.devDependencies
766
805
  : {};
@@ -768,95 +807,97 @@ export async function repairGeneratedTypeSupport(projectPath, options = {}) {
768
807
  ? packageJson.optionalDependencies
769
808
  : {};
770
809
  shouldIncludeUniwindTypes =
771
- typeof dependencies.uniwind === 'string' ||
772
- typeof devDependencies.uniwind === 'string' ||
773
- typeof optionalDependencies.uniwind === 'string' ||
774
- (await pathExists(path.join(projectPath, 'node_modules', 'uniwind', 'types.d.ts'))) ||
775
- (await pathExists(path.join(projectPath, 'node_modules', 'uniwind', 'types', 'index.d.ts')));
810
+ typeof dependencies.uniwind === "string" ||
811
+ typeof devDependencies.uniwind === "string" ||
812
+ typeof optionalDependencies.uniwind === "string" ||
813
+ (await pathExists(path.join(projectPath, "node_modules", "uniwind", "types.d.ts"))) ||
814
+ (await pathExists(path.join(projectPath, "node_modules", "uniwind", "types", "index.d.ts")));
776
815
  }
777
816
  if (options.needsUniwindTypes) {
778
- const cssEnvPath = path.join(projectPath, 'css-env.d.ts');
779
- const raw = (await readOptionalText(cssEnvPath)) ?? '';
780
- if (shouldIncludeUniwindTypes && !raw.includes('uniwind/types')) {
781
- await writeFile(cssEnvPath, `/// <reference types="uniwind/types" />\n\n${raw}`, 'utf8');
817
+ const cssEnvPath = path.join(projectPath, "css-env.d.ts");
818
+ const raw = (await readOptionalText(cssEnvPath)) ?? "";
819
+ if (shouldIncludeUniwindTypes && !raw.includes("uniwind/types")) {
820
+ await writeFile(cssEnvPath, `/// <reference types="uniwind/types" />\n\n${raw}`, "utf8");
782
821
  updatedPaths.add(cssEnvPath);
783
822
  }
784
- else if (!shouldIncludeUniwindTypes && raw.includes('uniwind/types')) {
823
+ else if (!shouldIncludeUniwindTypes && raw.includes("uniwind/types")) {
785
824
  const updated = raw
786
- .replace(/^\/\/\/ <reference types="uniwind\/types" \/>\r?\n\r?\n?/m, '')
825
+ .replace(/^\/\/\/ <reference types="uniwind\/types" \/>\r?\n\r?\n?/m, "")
787
826
  .trimStart();
788
- await writeFile(cssEnvPath, updated, 'utf8');
827
+ await writeFile(cssEnvPath, updated, "utf8");
789
828
  updatedPaths.add(cssEnvPath);
790
829
  }
791
830
  }
792
831
  if (options.needsNodeTypes || options.needsUniwindTypes) {
793
- const tsconfigPath = path.join(projectPath, 'tsconfig.json');
832
+ const tsconfigPath = path.join(projectPath, "tsconfig.json");
794
833
  const tsconfig = await readJson(tsconfigPath);
795
- const compilerOptions = isRecord(tsconfig.compilerOptions) ? tsconfig.compilerOptions : {};
796
- const pathsConfig = isRecord(compilerOptions.paths) ? compilerOptions.paths : {};
797
- const usingSrcApp = await pathExists(path.join(projectPath, 'src', 'app'));
834
+ const compilerOptions = isRecord(tsconfig.compilerOptions)
835
+ ? tsconfig.compilerOptions
836
+ : {};
837
+ const pathsConfig = isRecord(compilerOptions.paths)
838
+ ? compilerOptions.paths
839
+ : {};
840
+ const usingSrcApp = await pathExists(path.join(projectPath, "src", "app"));
798
841
  const normalizedPathsConfig = usingSrcApp
799
842
  ? await normalizeTsconfigPathsForSrcDirectory(projectPath, pathsConfig)
800
843
  : pathsConfig;
801
- const hasAliasPaths = Object.keys(pathsConfig).some((key) => key.includes('@'));
802
844
  const existingTypes = Array.isArray(compilerOptions.types)
803
- ? compilerOptions.types.filter((item) => typeof item === 'string')
845
+ ? compilerOptions.types.filter((item) => typeof item === "string")
804
846
  : [];
805
847
  const currentTypes = existingTypes
806
- ? existingTypes.filter((item) => shouldIncludeUniwindTypes || item !== 'uniwind/types')
848
+ ? existingTypes.filter((item) => shouldIncludeUniwindTypes || item !== "uniwind/types")
807
849
  : [];
808
850
  const desiredTypes = [
809
851
  ...currentTypes,
810
- ...(options.needsNodeTypes ? ['node'] : []),
811
- ...(shouldIncludeUniwindTypes ? ['uniwind/types'] : []),
852
+ ...(options.needsNodeTypes ? ["node"] : []),
853
+ ...(shouldIncludeUniwindTypes ? ["uniwind/types"] : []),
812
854
  ];
813
855
  const nextTypes = Array.from(new Set(desiredTypes));
814
- const shouldSetBaseUrl = hasAliasPaths && typeof compilerOptions.baseUrl !== 'string';
815
- const shouldSetIgnoreDeprecations = (shouldSetBaseUrl || typeof compilerOptions.baseUrl === 'string') &&
816
- compilerOptions.ignoreDeprecations !== '6.0';
856
+ const shouldRemoveDeprecatedTsconfigOptions = "baseUrl" in compilerOptions || "ignoreDeprecations" in compilerOptions;
817
857
  const shouldUpdatePaths = JSON.stringify(pathsConfig) !== JSON.stringify(normalizedPathsConfig);
818
858
  const shouldUpdateTypes = JSON.stringify(existingTypes) !== JSON.stringify(nextTypes);
819
- if (shouldUpdateTypes || shouldSetBaseUrl || shouldSetIgnoreDeprecations || shouldUpdatePaths) {
859
+ if (shouldUpdateTypes ||
860
+ shouldRemoveDeprecatedTsconfigOptions ||
861
+ shouldUpdatePaths) {
862
+ const { baseUrl: _baseUrl, ignoreDeprecations: _ignoreDeprecations, ...compilerOptionsWithoutDeprecatedOptions } = compilerOptions;
820
863
  tsconfig.compilerOptions = {
821
- ...compilerOptions,
864
+ ...compilerOptionsWithoutDeprecatedOptions,
822
865
  ...(shouldUpdatePaths ? { paths: normalizedPathsConfig } : {}),
823
866
  types: shouldUpdateTypes ? nextTypes : currentTypes,
824
- ...(shouldSetBaseUrl ? { baseUrl: '.' } : {}),
825
- ...(shouldSetIgnoreDeprecations ? { ignoreDeprecations: '6.0' } : {}),
826
867
  };
827
- await writeFile(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}\n`, 'utf8');
868
+ await writeFile(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}\n`, "utf8");
828
869
  updatedPaths.add(tsconfigPath);
829
870
  }
830
871
  }
831
872
  if (options.needsNodeTypes) {
832
- const packageJsonPath = path.join(projectPath, 'package.json');
873
+ const packageJsonPath = path.join(projectPath, "package.json");
833
874
  const packageJson = await readJson(packageJsonPath);
834
875
  const devDependencies = isRecord(packageJson.devDependencies)
835
876
  ? packageJson.devDependencies
836
877
  : {};
837
- if (devDependencies['@types/node'] !== '^25.9.1') {
878
+ if (devDependencies["@types/node"] !== "^25.9.1") {
838
879
  packageJson.devDependencies = {
839
880
  ...devDependencies,
840
- '@types/node': '^25.9.1',
881
+ "@types/node": "^25.9.1",
841
882
  };
842
- await writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`, 'utf8');
883
+ await writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`, "utf8");
843
884
  updatedPaths.add(packageJsonPath);
844
885
  }
845
886
  }
846
887
  const tabBarIconPaths = [
847
- path.join(projectPath, 'components', 'TabBarIcon.tsx'),
848
- path.join(projectPath, 'src', 'components', 'TabBarIcon.tsx'),
888
+ path.join(projectPath, "components", "TabBarIcon.tsx"),
889
+ path.join(projectPath, "src", "components", "TabBarIcon.tsx"),
849
890
  ];
850
891
  for (const tabBarIconPath of tabBarIconPaths) {
851
892
  const tabBarIconRaw = await readOptionalText(tabBarIconPath);
852
- if (!tabBarIconRaw || !tabBarIconRaw.includes('color: string')) {
893
+ if (!tabBarIconRaw || !tabBarIconRaw.includes("color: string")) {
853
894
  continue;
854
895
  }
855
896
  const updated = tabBarIconRaw
856
897
  .replace("import { StyleSheet } from 'react-native';", "import type { ColorValue } from 'react-native';\nimport { StyleSheet } from 'react-native';")
857
- .replace('color: string;', 'color: ColorValue;');
898
+ .replace("color: string;", "color: ColorValue;");
858
899
  if (updated !== tabBarIconRaw) {
859
- await writeFile(tabBarIconPath, updated, 'utf8');
900
+ await writeFile(tabBarIconPath, updated, "utf8");
860
901
  updatedPaths.add(tabBarIconPath);
861
902
  }
862
903
  }
@@ -871,7 +912,7 @@ async function normalizeTsconfigPathsForSrcDirectory(projectPath, pathsConfig) {
871
912
  }
872
913
  const nextTargets = [];
873
914
  for (const target of targets) {
874
- if (typeof target !== 'string') {
915
+ if (typeof target !== "string") {
875
916
  nextTargets.push(target);
876
917
  continue;
877
918
  }
@@ -883,23 +924,27 @@ async function normalizeTsconfigPathsForSrcDirectory(projectPath, pathsConfig) {
883
924
  return nextPaths;
884
925
  }
885
926
  async function normalizeAliasTargetForSrcDirectory(projectPath, alias, target) {
886
- const normalizedTarget = target.replace(/\\/g, '/').trim();
887
- if (normalizedTarget.startsWith('./src/') || normalizedTarget.startsWith('src/')) {
927
+ const normalizedTarget = target.replace(/\\/g, "/").trim();
928
+ if (normalizedTarget.startsWith("./src/") ||
929
+ normalizedTarget.startsWith("src/")) {
888
930
  return target;
889
931
  }
890
- if (alias === '@/*' && (normalizedTarget === './*' || normalizedTarget === '*')) {
891
- return './src/*';
932
+ if (alias === "@/*" &&
933
+ (normalizedTarget === "./*" || normalizedTarget === "*")) {
934
+ return "./src/*";
892
935
  }
893
- const relativeTarget = normalizedTarget.startsWith('./')
936
+ const relativeTarget = normalizedTarget.startsWith("./")
894
937
  ? normalizedTarget.slice(2)
895
938
  : normalizedTarget;
896
- const wildcardSuffix = relativeTarget.endsWith('/*') ? '/*' : '';
897
- const relativeBase = wildcardSuffix ? relativeTarget.slice(0, -2) : relativeTarget;
898
- if (!relativeBase || relativeBase === '*') {
939
+ const wildcardSuffix = relativeTarget.endsWith("/*") ? "/*" : "";
940
+ const relativeBase = wildcardSuffix
941
+ ? relativeTarget.slice(0, -2)
942
+ : relativeTarget;
943
+ if (!relativeBase || relativeBase === "*") {
899
944
  return target;
900
945
  }
901
946
  const rootCandidate = path.join(projectPath, relativeBase);
902
- const srcCandidate = path.join(projectPath, 'src', relativeBase);
947
+ const srcCandidate = path.join(projectPath, "src", relativeBase);
903
948
  const rootExists = await pathExists(rootCandidate);
904
949
  const srcExists = await pathExists(srcCandidate);
905
950
  if (!rootExists && srcExists) {
@@ -909,28 +954,41 @@ async function normalizeAliasTargetForSrcDirectory(projectPath, alias, target) {
909
954
  }
910
955
  export async function repairGeneratedNativeWindUiPicker(projectPath) {
911
956
  const pickerPaths = [
912
- path.join(projectPath, 'components', 'nativewindui', 'Picker.tsx'),
913
- path.join(projectPath, 'src', 'components', 'nativewindui', 'Picker.tsx'),
957
+ path.join(projectPath, "components", "nativewindui", "Picker.tsx"),
958
+ path.join(projectPath, "src", "components", "nativewindui", "Picker.tsx"),
914
959
  ];
915
960
  const updatedPaths = [];
916
961
  for (const pickerPath of pickerPaths) {
917
962
  const raw = await readOptionalText(pickerPath);
918
- if (!raw || !raw.includes('dropdownIconRippleColor=')) {
963
+ if (!raw || !raw.includes("dropdownIconRippleColor=")) {
919
964
  continue;
920
965
  }
921
966
  let updated = raw;
922
967
  updated = updated.replace("import { View } from 'react-native';", "import { Platform, View } from 'react-native';");
923
- updated = updated.replace(' dropdownIconRippleColor={dropdownIconRippleColor ?? colors.foreground}', " {...(Platform.OS === 'web' ? {} : { dropdownIconRippleColor: dropdownIconRippleColor ?? colors.foreground })}");
968
+ updated = updated.replace(" dropdownIconRippleColor={dropdownIconRippleColor ?? colors.foreground}", " {...(Platform.OS === 'web' ? {} : { dropdownIconRippleColor: dropdownIconRippleColor ?? colors.foreground })}");
924
969
  if (updated === raw) {
925
970
  continue;
926
971
  }
927
- await writeFile(pickerPath, updated, 'utf8');
972
+ await writeFile(pickerPath, updated, "utf8");
928
973
  updatedPaths.push(pickerPath);
929
974
  }
930
975
  return updatedPaths;
931
976
  }
977
+ export async function repairGeneratedEslintConfig(projectPath) {
978
+ const eslintConfigPath = path.join(projectPath, "eslint.config.js");
979
+ const raw = await readOptionalText(eslintConfigPath);
980
+ if (!raw) {
981
+ return [];
982
+ }
983
+ const updated = raw.replace(/^\/\* eslint-env node \*\/\r?\n/, "");
984
+ if (updated === raw) {
985
+ return [];
986
+ }
987
+ await writeFile(eslintConfigPath, updated, "utf8");
988
+ return [eslintConfigPath];
989
+ }
932
990
  export async function repairExpoProjectIdentifiers(projectPath, projectName, targetPlatforms = []) {
933
- const appJsonPath = path.join(projectPath, 'app.json');
991
+ const appJsonPath = path.join(projectPath, "app.json");
934
992
  const raw = await readOptionalText(appJsonPath);
935
993
  if (!raw) {
936
994
  return [];
@@ -949,7 +1007,7 @@ export async function repairExpoProjectIdentifiers(projectPath, projectName, tar
949
1007
  }
950
1008
  const nextSlug = toExpoSlug(readString(expo.slug) ?? projectName);
951
1009
  const currentScheme = expo.scheme;
952
- const hasScheme = Object.prototype.hasOwnProperty.call(expo, 'scheme');
1010
+ const hasScheme = Object.prototype.hasOwnProperty.call(expo, "scheme");
953
1011
  const nextScheme = hasScheme
954
1012
  ? Array.isArray(currentScheme)
955
1013
  ? currentScheme.map((item) => toExpoScheme(readString(item) ?? projectName))
@@ -960,54 +1018,59 @@ export async function repairExpoProjectIdentifiers(projectPath, projectName, tar
960
1018
  expo.slug = nextSlug;
961
1019
  changed = true;
962
1020
  }
963
- if (hasScheme && JSON.stringify(currentScheme) !== JSON.stringify(nextScheme)) {
1021
+ if (hasScheme &&
1022
+ JSON.stringify(currentScheme) !== JSON.stringify(nextScheme)) {
964
1023
  expo.scheme = nextScheme;
965
1024
  changed = true;
966
1025
  }
967
- const shouldIncludeWeb = targetPlatforms.includes('web');
968
1026
  const currentPlatforms = Array.isArray(expo.platforms) ? expo.platforms : [];
969
- if (shouldIncludeWeb && !currentPlatforms.includes('web')) {
970
- expo.platforms = [...currentPlatforms, 'web'];
1027
+ const normalizedTargetPlatforms = Array.from(new Set(targetPlatforms
1028
+ .map((platform) => readString(platform) ?? platform)
1029
+ .filter((platform) => Boolean(platform))));
1030
+ if (normalizedTargetPlatforms.length > 0 &&
1031
+ JSON.stringify(currentPlatforms) !== JSON.stringify(normalizedTargetPlatforms)) {
1032
+ expo.platforms = normalizedTargetPlatforms;
971
1033
  changed = true;
972
1034
  }
973
- const shouldIncludeAndroid = targetPlatforms.includes('android');
1035
+ const shouldIncludeAndroid = targetPlatforms.includes("android");
974
1036
  if (shouldIncludeAndroid) {
975
- if (readString(expo.backgroundColor) !== '#f1f0f8') {
976
- expo.backgroundColor = '#f1f0f8';
1037
+ if (readString(expo.backgroundColor) !== "#f1f0f8") {
1038
+ expo.backgroundColor = "#f1f0f8";
977
1039
  changed = true;
978
1040
  }
979
1041
  const androidConfig = isRecord(expo.android) ? { ...expo.android } : {};
980
- if (readString(androidConfig.backgroundColor) !== '#f1f0f8') {
981
- androidConfig.backgroundColor = '#f1f0f8';
1042
+ if (readString(androidConfig.backgroundColor) !== "#f1f0f8") {
1043
+ androidConfig.backgroundColor = "#f1f0f8";
982
1044
  expo.android = androidConfig;
983
1045
  changed = true;
984
1046
  }
985
1047
  const desiredAndroidStatusBar = {
986
- backgroundColor: '#f1f0f8',
987
- barStyle: 'dark-content',
1048
+ backgroundColor: "#f1f0f8",
1049
+ barStyle: "dark-content",
988
1050
  translucent: false,
989
1051
  };
990
- if (JSON.stringify(expo.androidStatusBar) !== JSON.stringify(desiredAndroidStatusBar)) {
1052
+ if (JSON.stringify(expo.androidStatusBar) !==
1053
+ JSON.stringify(desiredAndroidStatusBar)) {
991
1054
  expo.androidStatusBar = desiredAndroidStatusBar;
992
1055
  changed = true;
993
1056
  }
994
- if (Object.prototype.hasOwnProperty.call(expo, 'androidNavigationBar')) {
1057
+ if (Object.prototype.hasOwnProperty.call(expo, "androidNavigationBar")) {
995
1058
  delete expo.androidNavigationBar;
996
1059
  changed = true;
997
1060
  }
998
1061
  const desiredNavigationBarPlugin = [
999
- 'expo-navigation-bar',
1062
+ "expo-navigation-bar",
1000
1063
  {
1001
- style: 'dark',
1064
+ style: "dark",
1002
1065
  enforceContrast: false,
1003
1066
  },
1004
1067
  ];
1005
1068
  const plugins = Array.isArray(expo.plugins) ? [...expo.plugins] : [];
1006
1069
  const existingNavigationBarPluginIndex = plugins.findIndex((plugin) => {
1007
- if (readString(plugin) === 'expo-navigation-bar') {
1070
+ if (readString(plugin) === "expo-navigation-bar") {
1008
1071
  return true;
1009
1072
  }
1010
- return Array.isArray(plugin) && readString(plugin[0]) === 'expo-navigation-bar';
1073
+ return (Array.isArray(plugin) && readString(plugin[0]) === "expo-navigation-bar");
1011
1074
  });
1012
1075
  if (existingNavigationBarPluginIndex === -1 ||
1013
1076
  JSON.stringify(plugins[existingNavigationBarPluginIndex]) !==
@@ -1021,13 +1084,13 @@ export async function repairExpoProjectIdentifiers(projectPath, projectName, tar
1021
1084
  changed = true;
1022
1085
  }
1023
1086
  const hasSystemUiPlugin = plugins.some((plugin) => {
1024
- if (readString(plugin) === 'expo-system-ui') {
1087
+ if (readString(plugin) === "expo-system-ui") {
1025
1088
  return true;
1026
1089
  }
1027
- return Array.isArray(plugin) && readString(plugin[0]) === 'expo-system-ui';
1090
+ return (Array.isArray(plugin) && readString(plugin[0]) === "expo-system-ui");
1028
1091
  });
1029
1092
  if (!hasSystemUiPlugin) {
1030
- plugins.push('expo-system-ui');
1093
+ plugins.push("expo-system-ui");
1031
1094
  changed = true;
1032
1095
  }
1033
1096
  if (JSON.stringify(expo.plugins) !== JSON.stringify(plugins)) {
@@ -1036,34 +1099,35 @@ export async function repairExpoProjectIdentifiers(projectPath, projectName, tar
1036
1099
  }
1037
1100
  }
1038
1101
  else {
1039
- if (Object.prototype.hasOwnProperty.call(expo, 'androidNavigationBar')) {
1102
+ if (Object.prototype.hasOwnProperty.call(expo, "androidNavigationBar")) {
1040
1103
  delete expo.androidNavigationBar;
1041
1104
  changed = true;
1042
1105
  }
1043
- if (Object.prototype.hasOwnProperty.call(expo, 'androidStatusBar')) {
1106
+ if (Object.prototype.hasOwnProperty.call(expo, "androidStatusBar")) {
1044
1107
  delete expo.androidStatusBar;
1045
1108
  changed = true;
1046
1109
  }
1047
1110
  }
1048
1111
  if (shouldIncludeAndroid) {
1049
1112
  const deprecatedNavigationBar = {
1050
- position: 'absolute',
1051
- backgroundColor: '#00000000',
1052
- style: 'dark',
1113
+ position: "absolute",
1114
+ backgroundColor: "#00000000",
1115
+ style: "dark",
1053
1116
  enforceContrast: false,
1054
1117
  };
1055
- if (JSON.stringify(expo.androidNavigationBar) === JSON.stringify(deprecatedNavigationBar)) {
1118
+ if (JSON.stringify(expo.androidNavigationBar) ===
1119
+ JSON.stringify(deprecatedNavigationBar)) {
1056
1120
  delete expo.androidNavigationBar;
1057
1121
  }
1058
1122
  }
1059
1123
  if (!changed) {
1060
1124
  return [];
1061
1125
  }
1062
- await writeFile(appJsonPath, `${JSON.stringify(parsed, null, 2)}\n`, 'utf8');
1126
+ await writeFile(appJsonPath, `${JSON.stringify(parsed, null, 2)}\n`, "utf8");
1063
1127
  return [appJsonPath];
1064
1128
  }
1065
- export async function repairExpoWebOutputForStylistLifecycle(projectPath, preferredWebOutput = 'static') {
1066
- const appJsonPath = path.join(projectPath, 'app.json');
1129
+ export async function repairExpoWebOutputForStylistLifecycle(projectPath, preferredWebOutput = "static") {
1130
+ const appJsonPath = path.join(projectPath, "app.json");
1067
1131
  const raw = await readOptionalText(appJsonPath);
1068
1132
  if (!raw) {
1069
1133
  return [];
@@ -1081,13 +1145,15 @@ export async function repairExpoWebOutputForStylistLifecycle(projectPath, prefer
1081
1145
  return [];
1082
1146
  }
1083
1147
  const hasWebPlatform = !Array.isArray(expo.platforms) ||
1084
- expo.platforms.some((platform) => readString(platform) === 'web');
1148
+ expo.platforms.some((platform) => readString(platform) === "web");
1085
1149
  if (!hasWebPlatform) {
1086
1150
  return [];
1087
1151
  }
1088
1152
  const hasStylistSyncRoute = await hasStylistSyncApiRoute(projectPath);
1089
1153
  const preferred = normalizeExpoWebOutput(preferredWebOutput);
1090
- const desiredOutput = hasStylistSyncRoute ? 'server' : preferred;
1154
+ const desiredOutput = hasStylistSyncRoute
1155
+ ? "server"
1156
+ : preferred;
1091
1157
  const webConfig = isRecord(expo.web) ? expo.web : {};
1092
1158
  const currentOutput = readString(webConfig.output);
1093
1159
  if (currentOutput === desiredOutput) {
@@ -1097,7 +1163,7 @@ export async function repairExpoWebOutputForStylistLifecycle(projectPath, prefer
1097
1163
  ...webConfig,
1098
1164
  output: desiredOutput,
1099
1165
  };
1100
- await writeFile(appJsonPath, `${JSON.stringify(parsed, null, 2)}\n`, 'utf8');
1166
+ await writeFile(appJsonPath, `${JSON.stringify(parsed, null, 2)}\n`, "utf8");
1101
1167
  return [appJsonPath];
1102
1168
  }
1103
1169
  async function hasStylistSyncApiRoute(projectPath) {
@@ -1110,15 +1176,15 @@ async function hasStylistSyncApiRoute(projectPath) {
1110
1176
  }
1111
1177
  function normalizeExpoWebOutput(value) {
1112
1178
  switch (value) {
1113
- case 'server':
1114
- return 'server';
1115
- case 'spa':
1116
- return 'single';
1117
- case 'none':
1118
- return 'static';
1119
- case 'static':
1179
+ case "server":
1180
+ return "server";
1181
+ case "spa":
1182
+ return "single";
1183
+ case "none":
1184
+ return "static";
1185
+ case "static":
1120
1186
  default:
1121
- return 'static';
1187
+ return "static";
1122
1188
  }
1123
1189
  }
1124
1190
  async function findProjectFromCesConfig(cwd, projectName) {
@@ -1129,7 +1195,7 @@ async function findProjectFromCesConfig(cwd, projectName) {
1129
1195
  continue;
1130
1196
  }
1131
1197
  const projectPath = path.join(cwd, entry.name);
1132
- const raw = await readOptionalText(path.join(projectPath, 'cesconfig.jsonc'));
1198
+ const raw = await readOptionalText(path.join(projectPath, "cesconfig.jsonc"));
1133
1199
  if (!raw) {
1134
1200
  continue;
1135
1201
  }
@@ -1145,348 +1211,282 @@ async function findProjectFromCesConfig(cwd, projectName) {
1145
1211
  return null;
1146
1212
  }
1147
1213
  async function detectPackageManager(projectPath, args) {
1148
- if (args.includes('--pnpm'))
1149
- return 'pnpm';
1150
- if (args.includes('--yarn'))
1151
- return 'yarn';
1152
- if (args.includes('--bun'))
1153
- return 'bun';
1154
- if (args.includes('--npm'))
1155
- return 'npm';
1156
- const packageJson = await readJson(path.join(projectPath, 'package.json'));
1214
+ if (args.includes("--pnpm"))
1215
+ return "pnpm";
1216
+ if (args.includes("--yarn"))
1217
+ return "yarn";
1218
+ if (args.includes("--bun"))
1219
+ return "bun";
1220
+ if (args.includes("--npm"))
1221
+ return "npm";
1222
+ const packageJson = await readJson(path.join(projectPath, "package.json"));
1157
1223
  const declared = readString(packageJson.packageManager);
1158
- if (declared?.startsWith('pnpm@'))
1159
- return 'pnpm';
1160
- if (declared?.startsWith('yarn@'))
1161
- return 'yarn';
1162
- if (declared?.startsWith('bun@'))
1163
- return 'bun';
1164
- if (declared?.startsWith('npm@'))
1165
- return 'npm';
1166
- if (await pathExists(path.join(projectPath, 'pnpm-lock.yaml')))
1167
- return 'pnpm';
1168
- if (await pathExists(path.join(projectPath, 'yarn.lock')))
1169
- return 'yarn';
1170
- if ((await pathExists(path.join(projectPath, 'bun.lock'))) ||
1171
- (await pathExists(path.join(projectPath, 'bun.lockb')))) {
1172
- return 'bun';
1173
- }
1174
- return 'npm';
1224
+ if (declared?.startsWith("pnpm@"))
1225
+ return "pnpm";
1226
+ if (declared?.startsWith("yarn@"))
1227
+ return "yarn";
1228
+ if (declared?.startsWith("bun@"))
1229
+ return "bun";
1230
+ if (declared?.startsWith("npm@"))
1231
+ return "npm";
1232
+ if (await pathExists(path.join(projectPath, "pnpm-lock.yaml")))
1233
+ return "pnpm";
1234
+ if (await pathExists(path.join(projectPath, "yarn.lock")))
1235
+ return "yarn";
1236
+ if ((await pathExists(path.join(projectPath, "bun.lock"))) ||
1237
+ (await pathExists(path.join(projectPath, "bun.lockb")))) {
1238
+ return "bun";
1239
+ }
1240
+ return "npm";
1175
1241
  }
1176
1242
  function shouldRunExpoProjectChecks(parsed, noInstallRequested) {
1177
- return !parsed.mds.skipCreate && !parsed.mds.skipExpoFix && !noInstallRequested;
1243
+ return (!parsed.mds.skipCreate && !parsed.mds.skipExpoFix && !noInstallRequested);
1178
1244
  }
1179
1245
  function hasNoInstallFlag(args) {
1180
1246
  return args.some((arg) => {
1181
1247
  const normalized = arg.trim().toLowerCase();
1182
- return (normalized === '--no-install' ||
1183
- normalized === '--noinstall' ||
1184
- normalized === '--no-install=true' ||
1185
- normalized === '--install=false');
1248
+ return (normalized === "--no-install" ||
1249
+ normalized === "--noinstall" ||
1250
+ normalized === "--no-install=true" ||
1251
+ normalized === "--install=false");
1186
1252
  });
1187
1253
  }
1188
1254
  export function buildInstallCommand(packageManager) {
1189
1255
  switch (packageManager) {
1190
- case 'pnpm':
1256
+ case "pnpm":
1191
1257
  return {
1192
- command: 'pnpm',
1193
- args: ['install', '--config.strict-dep-builds=false', '--ignore-workspace'],
1194
- display: 'pnpm install --config.strict-dep-builds=false --ignore-workspace',
1195
- env: { PNPM_CONFIG_STRICT_DEP_BUILDS: 'false' },
1258
+ command: "pnpm",
1259
+ args: [
1260
+ "install",
1261
+ "--config.strict-dep-builds=false",
1262
+ "--ignore-workspace",
1263
+ ],
1264
+ display: "pnpm install --config.strict-dep-builds=false --ignore-workspace",
1265
+ env: { PNPM_CONFIG_STRICT_DEP_BUILDS: "false" },
1196
1266
  };
1197
- case 'yarn':
1267
+ case "yarn":
1198
1268
  return {
1199
- command: 'yarn',
1200
- args: ['install'],
1201
- display: 'yarn install',
1269
+ command: "yarn",
1270
+ args: ["install"],
1271
+ display: "yarn install",
1202
1272
  };
1203
- case 'bun':
1273
+ case "bun":
1204
1274
  return {
1205
- command: 'bun',
1206
- args: ['install'],
1207
- display: 'bun install',
1275
+ command: "bun",
1276
+ args: ["install"],
1277
+ display: "bun install",
1208
1278
  };
1209
- case 'npm':
1279
+ case "npm":
1210
1280
  return {
1211
- command: 'npm',
1212
- args: ['install'],
1213
- display: 'npm install',
1281
+ command: "npm",
1282
+ args: ["install"],
1283
+ display: "npm install",
1214
1284
  };
1215
1285
  }
1216
1286
  }
1217
1287
  export function buildExpoInstallFixCommand(packageManager) {
1218
1288
  switch (packageManager) {
1219
- case 'pnpm':
1289
+ case "pnpm":
1220
1290
  return {
1221
- command: 'pnpm',
1222
- args: ['--ignore-workspace', 'exec', 'expo', 'install', '--fix'],
1223
- display: 'pnpm --ignore-workspace exec expo install --fix',
1224
- env: { PNPM_CONFIG_STRICT_DEP_BUILDS: 'false' },
1291
+ command: "pnpm",
1292
+ args: ["--ignore-workspace", "exec", "expo", "install", "--fix"],
1293
+ display: "pnpm --ignore-workspace exec expo install --fix",
1294
+ env: { PNPM_CONFIG_STRICT_DEP_BUILDS: "false" },
1225
1295
  };
1226
- case 'yarn':
1296
+ case "yarn":
1227
1297
  return {
1228
- command: 'yarn',
1229
- args: ['expo', 'install', '--fix'],
1230
- display: 'yarn expo install --fix',
1298
+ command: "yarn",
1299
+ args: ["expo", "install", "--fix"],
1300
+ display: "yarn expo install --fix",
1231
1301
  };
1232
- case 'bun':
1302
+ case "bun":
1233
1303
  return {
1234
- command: 'bunx',
1235
- args: ['expo', 'install', '--fix'],
1236
- display: 'bunx expo install --fix',
1304
+ command: "bunx",
1305
+ args: ["expo", "install", "--fix"],
1306
+ display: "bunx expo install --fix",
1237
1307
  };
1238
- case 'npm':
1308
+ case "npm":
1239
1309
  return {
1240
- command: 'npx',
1241
- args: ['expo', 'install', '--fix'],
1242
- display: 'npx expo install --fix',
1310
+ command: "npx",
1311
+ args: ["expo", "install", "--fix"],
1312
+ display: "npx expo install --fix",
1243
1313
  };
1244
1314
  }
1245
1315
  }
1246
1316
  export function buildExpoLatestSdkCommand(packageManager) {
1247
1317
  switch (packageManager) {
1248
- case 'pnpm':
1318
+ case "pnpm":
1249
1319
  return {
1250
- command: 'pnpm',
1251
- args: ['--ignore-workspace', 'exec', 'expo', 'install', 'expo@latest'],
1252
- display: 'pnpm --ignore-workspace exec expo install expo@latest',
1253
- env: { PNPM_CONFIG_STRICT_DEP_BUILDS: 'false' },
1320
+ command: "pnpm",
1321
+ args: ["--ignore-workspace", "exec", "expo", "install", "expo@latest"],
1322
+ display: "pnpm --ignore-workspace exec expo install expo@latest",
1323
+ env: { PNPM_CONFIG_STRICT_DEP_BUILDS: "false" },
1254
1324
  };
1255
- case 'yarn':
1325
+ case "yarn":
1256
1326
  return {
1257
- command: 'yarn',
1258
- args: ['expo', 'install', 'expo@latest'],
1259
- display: 'yarn expo install expo@latest',
1327
+ command: "yarn",
1328
+ args: ["expo", "install", "expo@latest"],
1329
+ display: "yarn expo install expo@latest",
1260
1330
  };
1261
- case 'bun':
1331
+ case "bun":
1262
1332
  return {
1263
- command: 'bunx',
1264
- args: ['expo', 'install', 'expo@latest'],
1265
- display: 'bunx expo install expo@latest',
1333
+ command: "bunx",
1334
+ args: ["expo", "install", "expo@latest"],
1335
+ display: "bunx expo install expo@latest",
1266
1336
  };
1267
- case 'npm':
1337
+ case "npm":
1268
1338
  return {
1269
- command: 'npx',
1270
- args: ['expo', 'install', 'expo@latest'],
1271
- display: 'npx expo install expo@latest',
1339
+ command: "npx",
1340
+ args: ["expo", "install", "expo@latest"],
1341
+ display: "npx expo install expo@latest",
1272
1342
  };
1273
1343
  }
1274
1344
  }
1275
- export function buildExpoPinnedSdkCommand(packageManager) {
1276
- const target = `expo@${CURRENT_EXPO_SDK_VERSION}`;
1277
- switch (packageManager) {
1278
- case 'pnpm':
1279
- return {
1280
- command: 'pnpm',
1281
- args: ['--ignore-workspace', 'exec', 'expo', 'install', target],
1282
- display: `pnpm --ignore-workspace exec expo install ${target}`,
1283
- env: { PNPM_CONFIG_STRICT_DEP_BUILDS: 'false' },
1284
- };
1285
- case 'yarn':
1286
- return {
1287
- command: 'yarn',
1288
- args: ['expo', 'install', target],
1289
- display: `yarn expo install ${target}`,
1290
- };
1291
- case 'bun':
1292
- return {
1293
- command: 'bunx',
1294
- args: ['expo', 'install', target],
1295
- display: `bunx expo install ${target}`,
1296
- };
1297
- case 'npm':
1298
- return {
1299
- command: 'npx',
1300
- args: ['expo', 'install', target],
1301
- display: `npx expo install ${target}`,
1302
- };
1303
- }
1304
- }
1305
- export async function shouldRunExpoPinnedSdkCommand(projectPath) {
1306
- const packageJson = await readJson(path.join(projectPath, 'package.json'));
1307
- return shouldRunExpoPinnedSdkCommandFromPackageJson(packageJson);
1308
- }
1309
- export function shouldRunExpoPinnedSdkCommandFromPackageJson(packageJson) {
1310
- const dependencies = isRecord(packageJson.dependencies) ? packageJson.dependencies : {};
1311
- const devDependencies = isRecord(packageJson.devDependencies) ? packageJson.devDependencies : {};
1312
- const version = readString(dependencies.expo) ?? readString(devDependencies.expo);
1313
- const current = version ? readSemverTriplet(version) : undefined;
1314
- const target = readSemverTriplet(CURRENT_EXPO_SDK_VERSION);
1315
- if (!current || !target) {
1316
- return false;
1317
- }
1318
- if (current.major !== target.major || current.minor !== target.minor) {
1319
- return false;
1320
- }
1321
- return current.patch < target.patch;
1322
- }
1323
1345
  export async function shouldRunExpoLatestSdkCommand(projectPath) {
1324
- const packageJson = await readJson(path.join(projectPath, 'package.json'));
1346
+ const packageJson = await readJson(path.join(projectPath, "package.json"));
1325
1347
  return shouldRunExpoLatestSdkCommandFromPackageJson(packageJson);
1326
1348
  }
1327
1349
  export function shouldRunExpoLatestSdkCommandFromPackageJson(packageJson) {
1328
- const dependencies = isRecord(packageJson.dependencies) ? packageJson.dependencies : {};
1329
- const devDependencies = isRecord(packageJson.devDependencies) ? packageJson.devDependencies : {};
1350
+ const dependencies = isRecord(packageJson.dependencies)
1351
+ ? packageJson.dependencies
1352
+ : {};
1353
+ const devDependencies = isRecord(packageJson.devDependencies)
1354
+ ? packageJson.devDependencies
1355
+ : {};
1330
1356
  const version = readString(dependencies.expo) ?? readString(devDependencies.expo);
1331
- if (!version || version === 'latest') {
1332
- return !version;
1333
- }
1334
- const major = readSemverMajor(version);
1335
- return major === undefined || major < CURRENT_EXPO_SDK_MAJOR;
1336
- }
1337
- function readSemverMajor(version) {
1338
- const match = version.match(/\d+/u);
1339
- if (!match) {
1340
- return undefined;
1341
- }
1342
- return Number.parseInt(match[0], 10);
1343
- }
1344
- function readSemverTriplet(version) {
1345
- const match = version.match(/(\d+)\.(\d+)\.(\d+)/u);
1346
- if (!match) {
1347
- return undefined;
1348
- }
1349
- const major = match[1];
1350
- const minor = match[2];
1351
- const patch = match[3];
1352
- if (!major || !minor || !patch) {
1353
- return undefined;
1354
- }
1355
- return {
1356
- major: Number.parseInt(major, 10),
1357
- minor: Number.parseInt(minor, 10),
1358
- patch: Number.parseInt(patch, 10),
1359
- };
1357
+ return version !== "latest";
1360
1358
  }
1361
1359
  export function buildExpoFontInstallCommand(packageManager) {
1362
1360
  switch (packageManager) {
1363
- case 'pnpm':
1361
+ case "pnpm":
1364
1362
  return {
1365
- command: 'pnpm',
1366
- args: ['--ignore-workspace', 'exec', 'expo', 'install', 'expo-font'],
1367
- display: 'pnpm --ignore-workspace exec expo install expo-font',
1368
- env: { PNPM_CONFIG_STRICT_DEP_BUILDS: 'false' },
1363
+ command: "pnpm",
1364
+ args: ["--ignore-workspace", "exec", "expo", "install", "expo-font"],
1365
+ display: "pnpm --ignore-workspace exec expo install expo-font",
1366
+ env: { PNPM_CONFIG_STRICT_DEP_BUILDS: "false" },
1369
1367
  };
1370
- case 'yarn':
1368
+ case "yarn":
1371
1369
  return {
1372
- command: 'yarn',
1373
- args: ['expo', 'install', 'expo-font'],
1374
- display: 'yarn expo install expo-font',
1370
+ command: "yarn",
1371
+ args: ["expo", "install", "expo-font"],
1372
+ display: "yarn expo install expo-font",
1375
1373
  };
1376
- case 'bun':
1374
+ case "bun":
1377
1375
  return {
1378
- command: 'bunx',
1379
- args: ['expo', 'install', 'expo-font'],
1380
- display: 'bunx expo install expo-font',
1376
+ command: "bunx",
1377
+ args: ["expo", "install", "expo-font"],
1378
+ display: "bunx expo install expo-font",
1381
1379
  };
1382
- case 'npm':
1380
+ case "npm":
1383
1381
  return {
1384
- command: 'npx',
1385
- args: ['expo', 'install', 'expo-font'],
1386
- display: 'npx expo install expo-font',
1382
+ command: "npx",
1383
+ args: ["expo", "install", "expo-font"],
1384
+ display: "npx expo install expo-font",
1387
1385
  };
1388
1386
  }
1389
1387
  }
1390
1388
  export function buildExpoDoctorCommand(packageManager) {
1391
1389
  switch (packageManager) {
1392
- case 'pnpm':
1390
+ case "pnpm":
1393
1391
  return {
1394
- command: 'pnpm',
1395
- args: ['--ignore-workspace', 'dlx', 'expo-doctor'],
1396
- display: 'pnpm --ignore-workspace dlx expo-doctor',
1392
+ command: "pnpm",
1393
+ args: ["--ignore-workspace", "dlx", "expo-doctor"],
1394
+ display: "pnpm --ignore-workspace dlx expo-doctor",
1397
1395
  };
1398
- case 'yarn':
1396
+ case "yarn":
1399
1397
  return {
1400
- command: 'yarn',
1401
- args: ['dlx', 'expo-doctor'],
1402
- display: 'yarn dlx expo-doctor',
1398
+ command: "yarn",
1399
+ args: ["dlx", "expo-doctor"],
1400
+ display: "yarn dlx expo-doctor",
1403
1401
  };
1404
- case 'bun':
1402
+ case "bun":
1405
1403
  return {
1406
- command: 'bunx',
1407
- args: ['expo-doctor'],
1408
- display: 'bunx expo-doctor',
1404
+ command: "bunx",
1405
+ args: ["expo-doctor"],
1406
+ display: "bunx expo-doctor",
1409
1407
  };
1410
- case 'npm':
1408
+ case "npm":
1411
1409
  return {
1412
- command: 'npx',
1413
- args: ['expo-doctor'],
1414
- display: 'npx expo-doctor',
1410
+ command: "npx",
1411
+ args: ["expo-doctor"],
1412
+ display: "npx expo-doctor",
1415
1413
  };
1416
1414
  }
1417
1415
  }
1418
1416
  export function buildPrettierWriteCommand(packageManager) {
1419
- const glob = '**/*.{js,jsx,ts,tsx,json}';
1417
+ const glob = "**/*.{js,jsx,ts,tsx,json}";
1420
1418
  switch (packageManager) {
1421
- case 'pnpm':
1419
+ case "pnpm":
1422
1420
  return {
1423
- command: 'pnpm',
1424
- args: ['exec', 'prettier', '--write', glob],
1421
+ command: "pnpm",
1422
+ args: ["exec", "prettier", "--write", glob],
1425
1423
  display: `pnpm exec prettier --write "${glob}"`,
1426
1424
  };
1427
- case 'yarn':
1425
+ case "yarn":
1428
1426
  return {
1429
- command: 'yarn',
1430
- args: ['prettier', '--write', glob],
1427
+ command: "yarn",
1428
+ args: ["prettier", "--write", glob],
1431
1429
  display: `yarn prettier --write "${glob}"`,
1432
1430
  };
1433
- case 'bun':
1431
+ case "bun":
1434
1432
  return {
1435
- command: 'bunx',
1436
- args: ['prettier', '--write', glob],
1433
+ command: "bunx",
1434
+ args: ["prettier", "--write", glob],
1437
1435
  display: `bunx prettier --write "${glob}"`,
1438
1436
  };
1439
- case 'npm':
1437
+ case "npm":
1440
1438
  default:
1441
1439
  return {
1442
- command: 'npx',
1443
- args: ['prettier', '--write', glob],
1440
+ command: "npx",
1441
+ args: ["prettier", "--write", glob],
1444
1442
  display: `npx prettier --write "${glob}"`,
1445
1443
  };
1446
1444
  }
1447
1445
  }
1448
1446
  export function buildAddDevDependencyCommand(packageManager, dependency) {
1449
1447
  switch (packageManager) {
1450
- case 'pnpm':
1448
+ case "pnpm":
1451
1449
  return {
1452
- command: 'pnpm',
1453
- args: ['--ignore-workspace', 'add', '-D', dependency],
1450
+ command: "pnpm",
1451
+ args: ["--ignore-workspace", "add", "-D", dependency],
1454
1452
  display: `pnpm --ignore-workspace add -D ${dependency}`,
1455
- env: { PNPM_CONFIG_STRICT_DEP_BUILDS: 'false' },
1453
+ env: { PNPM_CONFIG_STRICT_DEP_BUILDS: "false" },
1456
1454
  };
1457
- case 'yarn':
1455
+ case "yarn":
1458
1456
  return {
1459
- command: 'yarn',
1460
- args: ['add', '--dev', dependency],
1457
+ command: "yarn",
1458
+ args: ["add", "--dev", dependency],
1461
1459
  display: `yarn add --dev ${dependency}`,
1462
1460
  };
1463
- case 'bun':
1461
+ case "bun":
1464
1462
  return {
1465
- command: 'bun',
1466
- args: ['add', '--dev', dependency],
1463
+ command: "bun",
1464
+ args: ["add", "--dev", dependency],
1467
1465
  display: `bun add --dev ${dependency}`,
1468
1466
  };
1469
- case 'npm':
1467
+ case "npm":
1470
1468
  return {
1471
- command: 'npm',
1472
- args: ['install', '--save-dev', dependency],
1469
+ command: "npm",
1470
+ args: ["install", "--save-dev", dependency],
1473
1471
  display: `npm install --save-dev ${dependency}`,
1474
1472
  };
1475
1473
  }
1476
1474
  }
1477
1475
  export function resolveWindowsTailwindOxidePackage({ platform = process.platform, arch = process.arch, nodeTargetType = process.config?.variables?.node_target_type, shlibSuffix = process.config?.variables?.shlib_suffix, } = {}) {
1478
- if (platform !== 'win32') {
1476
+ if (platform !== "win32") {
1479
1477
  return undefined;
1480
1478
  }
1481
- if (arch === 'x64') {
1482
- const usesGnu = shlibSuffix === 'dll.a' || nodeTargetType === 'shared_library';
1483
- return usesGnu ? '@tailwindcss/oxide-win32-x64-gnu' : '@tailwindcss/oxide-win32-x64-msvc';
1479
+ if (arch === "x64") {
1480
+ const usesGnu = shlibSuffix === "dll.a" || nodeTargetType === "shared_library";
1481
+ return usesGnu
1482
+ ? "@tailwindcss/oxide-win32-x64-gnu"
1483
+ : "@tailwindcss/oxide-win32-x64-msvc";
1484
1484
  }
1485
- if (arch === 'ia32') {
1486
- return '@tailwindcss/oxide-win32-ia32-msvc';
1485
+ if (arch === "ia32") {
1486
+ return "@tailwindcss/oxide-win32-ia32-msvc";
1487
1487
  }
1488
- if (arch === 'arm64') {
1489
- return '@tailwindcss/oxide-win32-arm64-msvc';
1488
+ if (arch === "arm64") {
1489
+ return "@tailwindcss/oxide-win32-arm64-msvc";
1490
1490
  }
1491
1491
  return undefined;
1492
1492
  }
@@ -1495,46 +1495,55 @@ export async function resolveMissingWindowsTailwindOxideBinding(projectPath) {
1495
1495
  if (!packageName) {
1496
1496
  return undefined;
1497
1497
  }
1498
- const packageJson = await readJson(path.join(projectPath, 'package.json'));
1499
- const dependencies = isRecord(packageJson.dependencies) ? packageJson.dependencies : {};
1500
- const devDependencies = isRecord(packageJson.devDependencies) ? packageJson.devDependencies : {};
1501
- const hasUniwind = typeof dependencies.uniwind === 'string' || typeof devDependencies.uniwind === 'string';
1498
+ const packageJson = await readJson(path.join(projectPath, "package.json"));
1499
+ const dependencies = isRecord(packageJson.dependencies)
1500
+ ? packageJson.dependencies
1501
+ : {};
1502
+ const devDependencies = isRecord(packageJson.devDependencies)
1503
+ ? packageJson.devDependencies
1504
+ : {};
1505
+ const hasUniwind = typeof dependencies.uniwind === "string" ||
1506
+ typeof devDependencies.uniwind === "string";
1502
1507
  if (!hasUniwind) {
1503
1508
  return undefined;
1504
1509
  }
1505
- if (await pathExists(path.join(projectPath, 'node_modules', packageName))) {
1510
+ if (await pathExists(path.join(projectPath, "node_modules", packageName))) {
1506
1511
  return undefined;
1507
1512
  }
1508
- const oxidePackageJsonPath = path.join(projectPath, 'node_modules', '@tailwindcss', 'oxide', 'package.json');
1513
+ const oxidePackageJsonPath = path.join(projectPath, "node_modules", "@tailwindcss", "oxide", "package.json");
1509
1514
  const oxidePackage = await readJson(oxidePackageJsonPath);
1510
1515
  const oxideVersion = readString(oxidePackage.version);
1511
1516
  return oxideVersion ? `${packageName}@${oxideVersion}` : packageName;
1512
1517
  }
1513
1518
  export async function shouldInstallExpoFontPeer(projectPath) {
1514
- const packageJson = await readJson(path.join(projectPath, 'package.json'));
1519
+ const packageJson = await readJson(path.join(projectPath, "package.json"));
1515
1520
  return shouldInstallExpoFontPeerFromPackageJson(packageJson);
1516
1521
  }
1517
1522
  export function shouldInstallExpoFontPeerFromPackageJson(packageJson) {
1518
- const dependencies = isRecord(packageJson.dependencies) ? packageJson.dependencies : {};
1519
- const devDependencies = isRecord(packageJson.devDependencies) ? packageJson.devDependencies : {};
1520
- const hasVectorIcons = typeof dependencies['@expo/vector-icons'] === 'string' ||
1521
- typeof devDependencies['@expo/vector-icons'] === 'string';
1522
- const hasExpoFont = typeof dependencies['expo-font'] === 'string' ||
1523
- typeof devDependencies['expo-font'] === 'string';
1523
+ const dependencies = isRecord(packageJson.dependencies)
1524
+ ? packageJson.dependencies
1525
+ : {};
1526
+ const devDependencies = isRecord(packageJson.devDependencies)
1527
+ ? packageJson.devDependencies
1528
+ : {};
1529
+ const hasVectorIcons = typeof dependencies["@expo/vector-icons"] === "string" ||
1530
+ typeof devDependencies["@expo/vector-icons"] === "string";
1531
+ const hasExpoFont = typeof dependencies["expo-font"] === "string" ||
1532
+ typeof devDependencies["expo-font"] === "string";
1524
1533
  return hasVectorIcons && !hasExpoFont;
1525
1534
  }
1526
1535
  export async function detectEasSetup(projectPath, createExpoStackArgs) {
1527
- if (hasFlag(createExpoStackArgs, '--eas')) {
1536
+ if (hasFlag(createExpoStackArgs, "--eas")) {
1528
1537
  return true;
1529
1538
  }
1530
- if (await pathExists(path.join(projectPath, 'eas.json'))) {
1539
+ if (await pathExists(path.join(projectPath, "eas.json"))) {
1531
1540
  return true;
1532
1541
  }
1533
- const appJson = await readJson(path.join(projectPath, 'app.json'));
1542
+ const appJson = await readJson(path.join(projectPath, "app.json"));
1534
1543
  if (hasExpoEasProjectId(appJson)) {
1535
1544
  return true;
1536
1545
  }
1537
- const appConfigJson = await readJson(path.join(projectPath, 'app.config.json'));
1546
+ const appConfigJson = await readJson(path.join(projectPath, "app.config.json"));
1538
1547
  if (hasExpoEasProjectId(appConfigJson)) {
1539
1548
  return true;
1540
1549
  }
@@ -1547,22 +1556,22 @@ function hasExpoEasProjectId(config) {
1547
1556
  const expo = isRecord(config.expo) ? config.expo : config;
1548
1557
  const extra = isRecord(expo.extra) ? expo.extra : undefined;
1549
1558
  const eas = extra && isRecord(extra.eas) ? extra.eas : undefined;
1550
- return typeof eas?.projectId === 'string' && eas.projectId.trim().length > 0;
1559
+ return typeof eas?.projectId === "string" && eas.projectId.trim().length > 0;
1551
1560
  }
1552
1561
  function buildRunScriptCommand(packageManager, script) {
1553
1562
  switch (packageManager) {
1554
- case 'pnpm':
1563
+ case "pnpm":
1555
1564
  return `pnpm ${script}`;
1556
- case 'yarn':
1565
+ case "yarn":
1557
1566
  return `yarn ${script}`;
1558
- case 'bun':
1567
+ case "bun":
1559
1568
  return `bun run ${script}`;
1560
- case 'npm':
1569
+ case "npm":
1561
1570
  return `npm run ${script}`;
1562
1571
  }
1563
1572
  }
1564
1573
  function formatDisplayArgs(args) {
1565
- return args.map(quoteDisplayArg).join(' ');
1574
+ return args.map(quoteDisplayArg).join(" ");
1566
1575
  }
1567
1576
  function quoteDisplayArg(value) {
1568
1577
  if (!value || /[\s"]/u.test(value)) {
@@ -1570,6 +1579,27 @@ function quoteDisplayArg(value) {
1570
1579
  }
1571
1580
  return value;
1572
1581
  }
1582
+ function printCopyableCommands(title, commands) {
1583
+ console.log(`${title} (copy this):`);
1584
+ console.log(renderCommandBox(commands));
1585
+ }
1586
+ function renderCommandBox(commands) {
1587
+ const visibleCommands = commands.filter((command) => command.trim().length > 0);
1588
+ if (visibleCommands.length === 0) {
1589
+ return "";
1590
+ }
1591
+ const formattedCommands = visibleCommands.map((command) => colorizeCommand(`> ${command}`));
1592
+ return ["┌", ...formattedCommands.map((command) => `│ ${command}`), "└"].join("\n");
1593
+ }
1594
+ function colorizeCommand(command) {
1595
+ if (!supportsAnsiColor()) {
1596
+ return command;
1597
+ }
1598
+ return `\x1b[36m${command}\x1b[0m`;
1599
+ }
1600
+ function supportsAnsiColor() {
1601
+ return process.stdout.isTTY === true && process.env.TERM !== "dumb";
1602
+ }
1573
1603
  function buildOnboardArgv(projectPath, parsed, easSelected) {
1574
1604
  return {
1575
1605
  project: projectPath,
@@ -1592,12 +1622,11 @@ function buildOnboardArgv(projectPath, parsed, easSelected) {
1592
1622
  platforms: parsed.mds.platforms,
1593
1623
  firstPlatform: parsed.mds.firstPlatform,
1594
1624
  platformStrategy: parsed.mds.platformStrategy,
1595
- appDirectory: parsed.mds.appDirectory ?? 'src',
1625
+ appDirectory: parsed.mds.appDirectory ?? "src",
1596
1626
  platformLayouts: parsed.mds.platformLayouts,
1597
1627
  webOutput: parsed.mds.webOutput,
1598
1628
  deployedServer: parsed.mds.deployedServer,
1599
1629
  createExpoComponents: parsed.mds.createExpoComponents,
1600
- latestExpoSdk: parsed.mds.latestExpoSdk,
1601
1630
  expoUi: parsed.mds.expoUi,
1602
1631
  expoUiUniversal: parsed.mds.expoUiUniversal,
1603
1632
  expoNativeTabs: parsed.mds.expoNativeTabs,
@@ -1606,7 +1635,7 @@ function buildOnboardArgv(projectPath, parsed, easSelected) {
1606
1635
  }
1607
1636
  function splitList(value) {
1608
1637
  return value
1609
- .split(',')
1638
+ .split(",")
1610
1639
  .map((item) => item.trim())
1611
1640
  .filter(Boolean);
1612
1641
  }
@@ -1621,7 +1650,7 @@ async function pathExists(filePath) {
1621
1650
  }
1622
1651
  async function readOptionalText(filePath) {
1623
1652
  try {
1624
- return await readFile(filePath, 'utf8');
1653
+ return await readFile(filePath, "utf8");
1625
1654
  }
1626
1655
  catch {
1627
1656
  return null;
@@ -1641,32 +1670,32 @@ async function readJson(filePath) {
1641
1670
  }
1642
1671
  }
1643
1672
  function parseJsonc(value) {
1644
- const cleaned = value.replace(/^\s*\/\/.*$/gm, '');
1673
+ const cleaned = value.replace(/^\s*\/\/.*$/gm, "");
1645
1674
  const parsed = JSON.parse(cleaned);
1646
1675
  return isRecord(parsed) ? parsed : {};
1647
1676
  }
1648
1677
  function readString(value) {
1649
- return typeof value === 'string' ? value : undefined;
1678
+ return typeof value === "string" ? value : undefined;
1650
1679
  }
1651
1680
  export function toExpoSlug(value) {
1652
1681
  const slug = value
1653
1682
  .trim()
1654
1683
  .toLowerCase()
1655
- .replace(/[^a-z0-9_-]+/g, '-')
1656
- .replace(/^-+|-+$/g, '');
1657
- return slug || 'expo-app';
1684
+ .replace(/[^a-z0-9_-]+/g, "-")
1685
+ .replace(/^-+|-+$/g, "");
1686
+ return slug || "expo-app";
1658
1687
  }
1659
1688
  export function toExpoScheme(value) {
1660
1689
  const scheme = value
1661
1690
  .trim()
1662
1691
  .toLowerCase()
1663
- .replace(/[^a-z0-9+.-]+/g, '-')
1664
- .replace(/^[^a-z]+/, '')
1665
- .replace(/-+$/g, '');
1666
- return scheme || 'expo-app';
1692
+ .replace(/[^a-z0-9+.-]+/g, "-")
1693
+ .replace(/^[^a-z]+/, "")
1694
+ .replace(/-+$/g, "");
1695
+ return scheme || "expo-app";
1667
1696
  }
1668
1697
  function isRecord(value) {
1669
- return typeof value === 'object' && value !== null && !Array.isArray(value);
1698
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1670
1699
  }
1671
1700
  export function isCliEntryPoint(argv = process.argv) {
1672
1701
  const entry = argv[1];