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