expo-bbase 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -30,8 +30,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
+ addModule: () => addModule,
33
34
  createProject: () => createProject,
34
- run: () => run
35
+ run: () => run,
36
+ upgradeProject: () => upgradeProject
35
37
  });
36
38
  module.exports = __toCommonJS(src_exports);
37
39
  var import_chalk = __toESM(require("chalk"));
@@ -47,6 +49,16 @@ function registerCreateCommand(program) {
47
49
  await createProject(projectName);
48
50
  });
49
51
  }
52
+ function registerUpgradeCommand(program) {
53
+ program.command("upgrade").description("Upgrade an existing expo-bbase project to the latest module versions").option("--dir <path>", "Project directory (default: current directory)", process.cwd()).action(async (options) => {
54
+ await upgradeProject(options.dir);
55
+ });
56
+ }
57
+ function registerAddCommand(program) {
58
+ program.command("add [modules...]").description("Add one or more modules to an existing expo-bbase project").option("--dir <path>", "Project directory (default: current directory)", process.cwd()).action(async (moduleIds, options) => {
59
+ await addModule(moduleIds, options.dir);
60
+ });
61
+ }
50
62
 
51
63
  // src/utils/lines.ts
52
64
  function lines(...args) {
@@ -1711,11 +1723,12 @@ var i18n_default = i18nModule;
1711
1723
  var animationModule = {
1712
1724
  id: "animation",
1713
1725
  name: "\u52A8\u753B/\u624B\u52BF",
1714
- description: "Reanimated 3 + Gesture Handler",
1726
+ description: "Reanimated 4 + Gesture Handler + Worklets",
1715
1727
  defaultChecked: false,
1716
1728
  dependencies: {
1717
1729
  "react-native-reanimated": "~4.1.1",
1718
- "react-native-gesture-handler": "~2.28.0"
1730
+ "react-native-gesture-handler": "~2.28.0",
1731
+ "react-native-worklets": "0.5.1"
1719
1732
  },
1720
1733
  devDependencies: {},
1721
1734
  files: [
@@ -1775,7 +1788,9 @@ var animationModule = {
1775
1788
  )
1776
1789
  }
1777
1790
  ],
1778
- babelPlugins: ["react-native-reanimated/plugin"]
1791
+ // Reanimated v4 Babel plugin is auto-managed by babel-preset-expo (SDK 54+)
1792
+ // No need to manually add react-native-reanimated/plugin
1793
+ babelPlugins: []
1779
1794
  };
1780
1795
  var animation_default = animationModule;
1781
1796
 
@@ -3319,6 +3334,9 @@ async function writeFile(filePath, content) {
3319
3334
  await import_fs_extra.default.ensureDir(dir);
3320
3335
  await import_fs_extra.default.writeFile(filePath, content, "utf-8");
3321
3336
  }
3337
+ async function readJson(filePath) {
3338
+ return import_fs_extra.default.readJson(filePath);
3339
+ }
3322
3340
  async function writeJson(filePath, data, spaces = 2) {
3323
3341
  const dir = import_path.default.dirname(filePath);
3324
3342
  await import_fs_extra.default.ensureDir(dir);
@@ -3334,6 +3352,18 @@ function replaceTemplateVars(content, vars) {
3334
3352
  }
3335
3353
 
3336
3354
  // src/utils/package.ts
3355
+ async function mergeDependencies(pkgPath, deps, devDeps) {
3356
+ const pkg = await readJson(pkgPath);
3357
+ pkg.dependencies = {
3358
+ ...pkg.dependencies || {},
3359
+ ...deps
3360
+ };
3361
+ pkg.devDependencies = {
3362
+ ...pkg.devDependencies || {},
3363
+ ...devDeps
3364
+ };
3365
+ await writeJson(pkgPath, pkg);
3366
+ }
3337
3367
  function generateBasePackageJson(projectName) {
3338
3368
  return {
3339
3369
  name: projectName,
@@ -3369,9 +3399,11 @@ function generateBasePackageJson(projectName) {
3369
3399
 
3370
3400
  // src/index.ts
3371
3401
  var import_execa = require("execa");
3402
+ var CLI_VERSION = "1.2.0";
3403
+ var CONFIG_FILE = ".expo-bbase.json";
3372
3404
  async function run() {
3373
3405
  const program = new import_commander.Command();
3374
- program.name("expo-bbase").description("Expo SDK 54+ \u811A\u624B\u67B6 CLI \u5DE5\u5177").version("1.0.0");
3406
+ program.name("expo-bbase").description("Expo SDK 54+ scaffolding CLI tool").version(CLI_VERSION);
3375
3407
  program.argument("[project-name]", "Name of the project to create").action(async (projectName) => {
3376
3408
  if (!projectName) {
3377
3409
  console.error(import_chalk.default.red("Error: Please provide a project name."));
@@ -3381,15 +3413,28 @@ async function run() {
3381
3413
  await createProject(projectName);
3382
3414
  });
3383
3415
  registerCreateCommand(program);
3416
+ registerUpgradeCommand(program);
3417
+ registerAddCommand(program);
3384
3418
  await program.parseAsync(process.argv);
3385
3419
  }
3420
+ async function readProjectConfig(targetDir) {
3421
+ const configPath = import_path2.default.join(targetDir, CONFIG_FILE);
3422
+ if (!await import_fs_extra2.default.pathExists(configPath)) {
3423
+ return null;
3424
+ }
3425
+ return import_fs_extra2.default.readJson(configPath);
3426
+ }
3427
+ async function writeProjectConfig(targetDir, config) {
3428
+ const configPath = import_path2.default.join(targetDir, CONFIG_FILE);
3429
+ await writeJson(configPath, config);
3430
+ }
3386
3431
  async function createProject(projectName) {
3387
3432
  console.log();
3388
3433
  console.log(
3389
3434
  import_chalk.default.bold.cyan(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557")
3390
3435
  );
3391
3436
  console.log(
3392
- import_chalk.default.bold.cyan(" \u2551 expo-bbase \u2014 Expo \u811A\u624B\u67B6\u5DE5\u5177 \u2551")
3437
+ import_chalk.default.bold.cyan(" \u2551 expo-bbase \u2014 Expo Scaffolding \u2551")
3393
3438
  );
3394
3439
  console.log(
3395
3440
  import_chalk.default.bold.cyan(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D")
@@ -3403,49 +3448,43 @@ async function createProject(projectName) {
3403
3448
  const { selectedModules } = await (0, import_prompts.default)({
3404
3449
  type: "multiselect",
3405
3450
  name: "selectedModules",
3406
- message: "\u9009\u62E9\u9700\u8981\u7684\u529F\u80FD\u6A21\u5757\uFF08\u7A7A\u683C\u5207\u6362\uFF0C\u56DE\u8F66\u786E\u8BA4\uFF09",
3451
+ message: "Select modules (Space to toggle, Enter to confirm)",
3407
3452
  choices,
3408
- hint: "- \u7A7A\u683C\u5207\u6362\u9009\u62E9 \xB7 a \u5168\u9009/\u53D6\u6D88 \xB7 \u56DE\u8F66\u786E\u8BA4",
3453
+ hint: "- Space toggle \xB7 a select all/none \xB7 Enter confirm",
3409
3454
  instructions: false
3410
3455
  });
3411
3456
  if (selectedModules === void 0) {
3412
- console.log(import_chalk.default.yellow("\n\u5DF2\u53D6\u6D88\u521B\u5EFA\u9879\u76EE\u3002"));
3457
+ console.log(import_chalk.default.yellow("\nCancelled."));
3413
3458
  process.exit(0);
3414
3459
  }
3415
3460
  const selectedModuleDefs = getModulesByIds(selectedModules);
3416
3461
  const targetDir = import_path2.default.resolve(process.cwd(), projectName);
3417
3462
  console.log();
3418
- console.log(import_chalk.default.white(` \u{1F4E6} \u9879\u76EE\u540D\u79F0: ${import_chalk.default.bold(projectName)}`));
3419
- console.log(
3420
- import_chalk.default.white(` \u{1F4C2} \u76EE\u6807\u8DEF\u5F84: ${import_chalk.default.gray(targetDir)}`)
3421
- );
3463
+ console.log(import_chalk.default.white(` \u{1F4E6} Project: ${import_chalk.default.bold(projectName)}`));
3464
+ console.log(import_chalk.default.white(` \u{1F4C2} Path: ${import_chalk.default.gray(targetDir)}`));
3422
3465
  console.log(
3423
3466
  import_chalk.default.white(
3424
- ` \u{1F9E9} \u9009\u62E9\u6A21\u5757: ${import_chalk.default.green(selectedModuleDefs.map((m) => m.name).join(", ") || "\u65E0")}`
3467
+ ` \u{1F9E9} Modules: ${import_chalk.default.green(selectedModuleDefs.map((m) => m.name).join(", ") || "none")}`
3425
3468
  )
3426
3469
  );
3427
3470
  console.log();
3428
- const spinner = (0, import_ora.default)("\u6B63\u5728\u521B\u5EFA\u9879\u76EE...").start();
3471
+ const spinner = (0, import_ora.default)("Creating project...").start();
3429
3472
  try {
3430
3473
  const baseTemplates = generateBaseTemplates(projectName);
3431
3474
  for (const file of baseTemplates) {
3432
3475
  const filePath = import_path2.default.join(targetDir, file.path);
3433
- const content = replaceTemplateVars(file.content, {
3434
- projectName
3435
- });
3476
+ const content = replaceTemplateVars(file.content, { projectName });
3436
3477
  await writeFile(filePath, content);
3437
3478
  }
3438
- spinner.text = "\u6B63\u5728\u5199\u5165\u6A21\u5757\u6587\u4EF6...";
3479
+ spinner.text = "Writing module files...";
3439
3480
  for (const mod of selectedModuleDefs) {
3440
3481
  for (const file of mod.files) {
3441
3482
  const filePath = import_path2.default.join(targetDir, file.path);
3442
- const content = replaceTemplateVars(file.content, {
3443
- projectName
3444
- });
3483
+ const content = replaceTemplateVars(file.content, { projectName });
3445
3484
  await writeFile(filePath, content);
3446
3485
  }
3447
3486
  }
3448
- spinner.text = "\u6B63\u5728\u751F\u6210 package.json...";
3487
+ spinner.text = "Generating package.json...";
3449
3488
  const pkgJson = generateBasePackageJson(projectName);
3450
3489
  const allDeps = {};
3451
3490
  const allDevDeps = {};
@@ -3460,48 +3499,298 @@ async function createProject(projectName) {
3460
3499
  );
3461
3500
  const pkgPath = import_path2.default.join(targetDir, "package.json");
3462
3501
  await writeJson(pkgPath, pkgJson);
3463
- spinner.text = "\u6B63\u5728\u914D\u7F6E app.json...";
3502
+ spinner.text = "Configuring app.json...";
3464
3503
  await updateAppJson(targetDir, selectedModuleDefs, projectName);
3465
- spinner.text = "\u6B63\u5728\u914D\u7F6E Babel...";
3504
+ spinner.text = "Configuring Babel...";
3466
3505
  await updateBabelConfig(targetDir, selectedModuleDefs);
3467
- spinner.text = "\u6B63\u5728\u914D\u7F6E\u5165\u53E3\u6587\u4EF6...";
3506
+ spinner.text = "Configuring layout...";
3468
3507
  await updateLayoutFile(targetDir, selectedModuleDefs);
3469
- spinner.text = "\u6B63\u5728\u5B89\u88C5\u4F9D\u8D56 (yarn install)...";
3508
+ await writeProjectConfig(targetDir, {
3509
+ projectName,
3510
+ selectedModules,
3511
+ cliVersion: CLI_VERSION
3512
+ });
3513
+ spinner.text = "Installing dependencies (yarn install)...";
3470
3514
  try {
3471
3515
  await (0, import_execa.execa)("yarn", ["install"], {
3472
3516
  cwd: targetDir,
3473
3517
  timeout: 3e5
3474
- // 5 minute timeout
3475
3518
  });
3476
3519
  } catch (installError) {
3477
3520
  const errMsg = installError instanceof Error ? installError.message : String(installError);
3478
- spinner.warn("yarn install \u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u5B89\u88C5\u4F9D\u8D56");
3479
- console.log(import_chalk.default.red(` \u9519\u8BEF: ${errMsg}`));
3480
- console.log(
3481
- import_chalk.default.gray(` cd ${projectName} && yarn install`)
3482
- );
3521
+ spinner.warn("yarn install failed, please install manually");
3522
+ console.log(import_chalk.default.red(` Error: ${errMsg}`));
3523
+ console.log(import_chalk.default.gray(` cd ${projectName} && yarn install`));
3483
3524
  }
3484
- spinner.succeed(import_chalk.default.green("\u9879\u76EE\u521B\u5EFA\u6210\u529F\uFF01"));
3525
+ spinner.succeed(import_chalk.default.green("Project created!"));
3485
3526
  console.log();
3486
- console.log(import_chalk.default.bold(" \u{1F389} \u4E0B\u4E00\u6B65\uFF1A"));
3527
+ console.log(import_chalk.default.bold(" \u{1F389} Next steps:"));
3487
3528
  console.log(import_chalk.default.white(` cd ${projectName}`));
3488
3529
  console.log(import_chalk.default.white(" npx expo start"));
3489
3530
  console.log();
3490
3531
  if (selectedModuleDefs.length > 0) {
3491
- console.log(import_chalk.default.bold(" \u{1F4CB} \u5DF2\u9009\u6A21\u5757\uFF1A"));
3532
+ console.log(import_chalk.default.bold(" \u{1F4CB} Selected modules:"));
3492
3533
  for (const mod of selectedModuleDefs) {
3493
3534
  console.log(import_chalk.default.white(` \u2713 ${mod.name}`));
3494
3535
  }
3495
3536
  console.log();
3496
3537
  }
3497
3538
  } catch (error) {
3498
- spinner.fail(import_chalk.default.red("\u9879\u76EE\u521B\u5EFA\u5931\u8D25"));
3539
+ spinner.fail(import_chalk.default.red("Project creation failed"));
3499
3540
  console.error(error);
3500
3541
  process.exit(1);
3501
3542
  }
3502
3543
  }
3503
- async function updateAppJson(targetDir, selectedModules, projectName) {
3544
+ async function upgradeProject(targetDir) {
3545
+ console.log();
3546
+ console.log(
3547
+ import_chalk.default.bold.cyan(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557")
3548
+ );
3549
+ console.log(
3550
+ import_chalk.default.bold.cyan(" \u2551 expo-bbase \u2014 Upgrade \u2551")
3551
+ );
3552
+ console.log(
3553
+ import_chalk.default.bold.cyan(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D")
3554
+ );
3555
+ console.log();
3556
+ const absDir = import_path2.default.resolve(targetDir);
3557
+ const config = await readProjectConfig(absDir);
3558
+ if (!config) {
3559
+ console.error(
3560
+ import_chalk.default.red(
3561
+ ` \u2716 No ${CONFIG_FILE} found in ${absDir}
3562
+ This directory doesn't appear to be an expo-bbase project.
3563
+ If it is, run "expo-bbase add" to register modules.`
3564
+ )
3565
+ );
3566
+ process.exit(1);
3567
+ }
3568
+ console.log(
3569
+ import_chalk.default.white(` \u{1F4C2} Project: ${import_chalk.default.bold(config.projectName)}`)
3570
+ );
3571
+ console.log(
3572
+ import_chalk.default.white(` \u{1F4CB} CLI version: ${import_chalk.default.gray(config.cliVersion || "unknown")} \u2192 ${import_chalk.default.green(CLI_VERSION)}`)
3573
+ );
3574
+ console.log(
3575
+ import_chalk.default.white(
3576
+ ` \u{1F9E9} Modules: ${import_chalk.default.green(config.selectedModules.join(", ") || "none")}`
3577
+ )
3578
+ );
3579
+ console.log();
3580
+ const spinner = (0, import_ora.default)("Upgrading project...").start();
3581
+ try {
3582
+ const selectedModuleDefs = getModulesByIds(config.selectedModules);
3583
+ spinner.text = "Updating module files...";
3584
+ for (const mod of selectedModuleDefs) {
3585
+ for (const file of mod.files) {
3586
+ const filePath = import_path2.default.join(absDir, file.path);
3587
+ const content = replaceTemplateVars(file.content, {
3588
+ projectName: config.projectName
3589
+ });
3590
+ await writeFile(filePath, content);
3591
+ }
3592
+ }
3593
+ spinner.text = "Updating dependencies...";
3594
+ const allDeps = {};
3595
+ const allDevDeps = {};
3596
+ for (const mod of selectedModuleDefs) {
3597
+ Object.assign(allDeps, mod.dependencies);
3598
+ Object.assign(allDevDeps, mod.devDependencies);
3599
+ }
3600
+ const basePkg = generateBasePackageJson(config.projectName);
3601
+ Object.assign(allDeps, basePkg.dependencies);
3602
+ Object.assign(
3603
+ allDevDeps,
3604
+ basePkg.devDependencies
3605
+ );
3606
+ const pkgPath = import_path2.default.join(absDir, "package.json");
3607
+ await mergeDependencies(pkgPath, allDeps, allDevDeps);
3608
+ spinner.text = "Updating app.json...";
3609
+ await updateAppJson(absDir, selectedModuleDefs, config.projectName);
3610
+ spinner.text = "Updating Babel config...";
3611
+ await updateBabelConfig(absDir, selectedModuleDefs);
3612
+ spinner.text = "Updating layout...";
3613
+ await updateLayoutFile(absDir, selectedModuleDefs);
3614
+ config.cliVersion = CLI_VERSION;
3615
+ await writeProjectConfig(absDir, config);
3616
+ spinner.text = "Installing updated dependencies (yarn install)...";
3617
+ try {
3618
+ await (0, import_execa.execa)("yarn", ["install"], {
3619
+ cwd: absDir,
3620
+ timeout: 3e5
3621
+ });
3622
+ } catch (installError) {
3623
+ const errMsg = installError instanceof Error ? installError.message : String(installError);
3624
+ spinner.warn("yarn install failed, please install manually");
3625
+ console.log(import_chalk.default.red(` Error: ${errMsg}`));
3626
+ }
3627
+ spinner.succeed(import_chalk.default.green("Project upgraded!"));
3628
+ console.log();
3629
+ console.log(import_chalk.default.bold(" \u{1F4CB} Upgrade summary:"));
3630
+ console.log(import_chalk.default.white(` CLI: ${import_chalk.default.gray(config.cliVersion)} (before)`));
3631
+ console.log(import_chalk.default.white(` Modules: ${import_chalk.default.green(config.selectedModules.join(", "))}`));
3632
+ console.log();
3633
+ console.log(import_chalk.default.bold(" \u{1F389} Run your project:"));
3634
+ console.log(import_chalk.default.white(" npx expo start"));
3635
+ console.log();
3636
+ } catch (error) {
3637
+ spinner.fail(import_chalk.default.red("Upgrade failed"));
3638
+ console.error(error);
3639
+ process.exit(1);
3640
+ }
3641
+ }
3642
+ async function addModule(moduleIds, targetDir) {
3643
+ console.log();
3644
+ const absDir = import_path2.default.resolve(targetDir);
3645
+ let config = await readProjectConfig(absDir);
3646
+ if (!config) {
3647
+ const pkgPath = import_path2.default.join(absDir, "package.json");
3648
+ if (!await import_fs_extra2.default.pathExists(pkgPath)) {
3649
+ console.error(
3650
+ import_chalk.default.red(` \u2716 No package.json found in ${absDir}`)
3651
+ );
3652
+ process.exit(1);
3653
+ }
3654
+ const pkg = await import_fs_extra2.default.readJson(pkgPath);
3655
+ config = {
3656
+ projectName: pkg.name || import_path2.default.basename(absDir),
3657
+ selectedModules: [],
3658
+ cliVersion: CLI_VERSION
3659
+ };
3660
+ console.log(
3661
+ import_chalk.default.yellow(
3662
+ ` \u26A0 No ${CONFIG_FILE} found. Creating one for project "${config.projectName}".`
3663
+ )
3664
+ );
3665
+ }
3666
+ if (moduleIds.length === 0) {
3667
+ const availableModules = modules.filter(
3668
+ (m) => !config.selectedModules.includes(m.id)
3669
+ );
3670
+ if (availableModules.length === 0) {
3671
+ console.log(import_chalk.default.green(" \u2713 All modules are already installed!"));
3672
+ process.exit(0);
3673
+ }
3674
+ const choices = availableModules.map((m) => ({
3675
+ title: `${import_chalk.default.bold(m.name)} \u2014 ${import_chalk.default.gray(m.description)}`,
3676
+ value: m.id,
3677
+ selected: false
3678
+ }));
3679
+ const { selected } = await (0, import_prompts.default)({
3680
+ type: "multiselect",
3681
+ name: "selected",
3682
+ message: "Select modules to add (Space to toggle, Enter to confirm)",
3683
+ choices,
3684
+ hint: "- Space toggle \xB7 a select all/none \xB7 Enter confirm",
3685
+ instructions: false
3686
+ });
3687
+ if (selected === void 0 || selected.length === 0) {
3688
+ console.log(import_chalk.default.yellow(" No modules selected."));
3689
+ process.exit(0);
3690
+ }
3691
+ moduleIds = selected;
3692
+ }
3693
+ const invalidIds = moduleIds.filter((id) => !getModuleById(id));
3694
+ if (invalidIds.length > 0) {
3695
+ console.error(
3696
+ import_chalk.default.red(` \u2716 Unknown module(s): ${invalidIds.join(", ")}`)
3697
+ );
3698
+ console.log(
3699
+ import_chalk.default.gray(
3700
+ ` Available: ${modules.map((m) => m.id).join(", ")}`
3701
+ )
3702
+ );
3703
+ process.exit(1);
3704
+ }
3705
+ const newModuleIds = moduleIds.filter(
3706
+ (id) => !config.selectedModules.includes(id)
3707
+ );
3708
+ if (newModuleIds.length === 0) {
3709
+ console.log(
3710
+ import_chalk.default.yellow(" All specified modules are already installed.")
3711
+ );
3712
+ process.exit(0);
3713
+ }
3714
+ const newModuleDefs = getModulesByIds(newModuleIds);
3715
+ console.log(
3716
+ import_chalk.default.white(` \u{1F4C2} Project: ${import_chalk.default.bold(config.projectName)}`)
3717
+ );
3718
+ console.log(
3719
+ import_chalk.default.white(
3720
+ ` \u2795 Adding: ${import_chalk.default.green(newModuleDefs.map((m) => m.name).join(", "))}`
3721
+ )
3722
+ );
3723
+ console.log();
3724
+ const spinner = (0, import_ora.default)("Adding modules...").start();
3725
+ try {
3726
+ spinner.text = "Writing module files...";
3727
+ for (const mod of newModuleDefs) {
3728
+ for (const file of mod.files) {
3729
+ const filePath = import_path2.default.join(absDir, file.path);
3730
+ const content = replaceTemplateVars(file.content, {
3731
+ projectName: config.projectName
3732
+ });
3733
+ await writeFile(filePath, content);
3734
+ }
3735
+ }
3736
+ spinner.text = "Updating dependencies...";
3737
+ const allDeps = {};
3738
+ const allDevDeps = {};
3739
+ for (const mod of newModuleDefs) {
3740
+ Object.assign(allDeps, mod.dependencies);
3741
+ Object.assign(allDevDeps, mod.devDependencies);
3742
+ }
3743
+ const pkgPath = import_path2.default.join(absDir, "package.json");
3744
+ await mergeDependencies(pkgPath, allDeps, allDevDeps);
3745
+ spinner.text = "Updating app.json...";
3746
+ const existingModules = getModulesByIds(config.selectedModules);
3747
+ await updateAppJson(
3748
+ absDir,
3749
+ [...existingModules, ...newModuleDefs],
3750
+ config.projectName
3751
+ );
3752
+ spinner.text = "Updating Babel config...";
3753
+ await updateBabelConfig(absDir, newModuleDefs);
3754
+ spinner.text = "Updating layout...";
3755
+ await updateLayoutFile(absDir, newModuleDefs);
3756
+ config.selectedModules.push(...newModuleIds);
3757
+ config.cliVersion = CLI_VERSION;
3758
+ await writeProjectConfig(absDir, config);
3759
+ spinner.text = "Installing dependencies (yarn install)...";
3760
+ try {
3761
+ await (0, import_execa.execa)("yarn", ["install"], {
3762
+ cwd: absDir,
3763
+ timeout: 3e5
3764
+ });
3765
+ } catch (installError) {
3766
+ const errMsg = installError instanceof Error ? installError.message : String(installError);
3767
+ spinner.warn("yarn install failed, please install manually");
3768
+ console.log(import_chalk.default.red(` Error: ${errMsg}`));
3769
+ }
3770
+ spinner.succeed(import_chalk.default.green("Modules added!"));
3771
+ console.log();
3772
+ console.log(import_chalk.default.bold(" \u{1F4CB} Added modules:"));
3773
+ for (const mod of newModuleDefs) {
3774
+ console.log(import_chalk.default.white(` \u2713 ${mod.name} (${mod.id})`));
3775
+ }
3776
+ console.log();
3777
+ console.log(import_chalk.default.bold(" \u{1F9E9} All installed modules:"));
3778
+ for (const id of config.selectedModules) {
3779
+ const m = getModuleById(id);
3780
+ console.log(import_chalk.default.white(` \u2022 ${m?.name || id}`));
3781
+ }
3782
+ console.log();
3783
+ } catch (error) {
3784
+ spinner.fail(import_chalk.default.red("Failed to add modules"));
3785
+ console.error(error);
3786
+ process.exit(1);
3787
+ }
3788
+ }
3789
+ async function updateAppJson(targetDir, selectedModules, _projectName) {
3504
3790
  const appJsonPath = import_path2.default.join(targetDir, "app.json");
3791
+ if (!await import_fs_extra2.default.pathExists(appJsonPath)) {
3792
+ return;
3793
+ }
3505
3794
  const appJson = await import_fs_extra2.default.readJson(appJsonPath);
3506
3795
  const existingPlugins = appJson.expo?.plugins || [];
3507
3796
  for (const mod of selectedModules) {
@@ -3523,41 +3812,55 @@ async function updateAppJson(targetDir, selectedModules, projectName) {
3523
3812
  }
3524
3813
  async function updateBabelConfig(targetDir, selectedModules) {
3525
3814
  const babelPath = import_path2.default.join(targetDir, "babel.config.js");
3815
+ if (!await import_fs_extra2.default.pathExists(babelPath)) {
3816
+ return;
3817
+ }
3526
3818
  let content = await import_fs_extra2.default.readFile(babelPath, "utf-8");
3527
3819
  const extraPlugins = [];
3528
3820
  for (const mod of selectedModules) {
3529
- if (mod.babelPlugins) {
3821
+ if (mod.babelPlugins && mod.babelPlugins.length > 0) {
3530
3822
  extraPlugins.push(...mod.babelPlugins);
3531
3823
  }
3532
3824
  }
3533
3825
  if (extraPlugins.length > 0) {
3534
- const pluginStrings = extraPlugins.map((p) => ` "${p}"`).join(",\n");
3535
- content = content.replace(
3536
- /plugins:\s*\[([^\]]*)\]/,
3537
- `plugins: [$1${pluginStrings ? ",\n" + pluginStrings : ""}]`
3538
- );
3539
- await import_fs_extra2.default.writeFile(babelPath, content, "utf-8");
3826
+ const pluginsToAdd = extraPlugins.filter((p) => !content.includes(p));
3827
+ if (pluginsToAdd.length > 0) {
3828
+ const pluginStrings = pluginsToAdd.map((p) => ` "${p}"`).join(",\n");
3829
+ content = content.replace(
3830
+ /plugins:\s*\[([^\]]*)\]/,
3831
+ `plugins: [$1${pluginStrings ? ",\n" + pluginStrings : ""}]`
3832
+ );
3833
+ await import_fs_extra2.default.writeFile(babelPath, content, "utf-8");
3834
+ }
3540
3835
  }
3541
3836
  }
3542
3837
  async function updateLayoutFile(targetDir, selectedModules) {
3543
3838
  const layoutPath = import_path2.default.join(targetDir, "app/_layout.tsx");
3544
- const fs2 = import_fs_extra2.default;
3545
- let content = await fs2.readFile(layoutPath, "utf-8");
3839
+ if (!await import_fs_extra2.default.pathExists(layoutPath)) {
3840
+ return;
3841
+ }
3842
+ let content = await import_fs_extra2.default.readFile(layoutPath, "utf-8");
3546
3843
  const extraImports = [];
3547
3844
  const extraProviderPairs = [];
3548
3845
  for (const mod of selectedModules) {
3549
3846
  if (mod.layoutImports) {
3550
- extraImports.push(...mod.layoutImports);
3847
+ for (const imp of mod.layoutImports) {
3848
+ if (!content.includes(imp)) {
3849
+ extraImports.push(imp);
3850
+ }
3851
+ }
3551
3852
  }
3552
3853
  if (mod.layoutProviders) {
3553
3854
  for (const provider of mod.layoutProviders) {
3554
3855
  const match = provider.match(/^<(\w+)/);
3555
3856
  if (match) {
3556
3857
  const tagName = match[1];
3557
- extraProviderPairs.push({
3558
- open: ` ${provider}`,
3559
- close: ` </${tagName}>`
3560
- });
3858
+ if (!content.includes(`<${tagName}`)) {
3859
+ extraProviderPairs.push({
3860
+ open: ` ${provider}`,
3861
+ close: ` </${tagName}>`
3862
+ });
3863
+ }
3561
3864
  }
3562
3865
  }
3563
3866
  }
@@ -3586,7 +3889,7 @@ async function updateLayoutFile(targetDir, selectedModules) {
3586
3889
  }
3587
3890
  }
3588
3891
  }
3589
- await fs2.writeFile(layoutPath, content, "utf-8");
3892
+ await import_fs_extra2.default.writeFile(layoutPath, content, "utf-8");
3590
3893
  }
3591
3894
  run().catch((error) => {
3592
3895
  console.error(import_chalk.default.red("Fatal error:"), error);
@@ -3594,7 +3897,9 @@ run().catch((error) => {
3594
3897
  });
3595
3898
  // Annotate the CommonJS export names for ESM import in node:
3596
3899
  0 && (module.exports = {
3900
+ addModule,
3597
3901
  createProject,
3598
- run
3902
+ run,
3903
+ upgradeProject
3599
3904
  });
3600
3905
  //# sourceMappingURL=index.js.map