expo-bbase 1.7.1 → 1.7.3
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 +19 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/commands/create.ts","../src/utils/lines.ts","../src/modules/network.ts","../src/modules/state.ts","../src/modules/storage.ts","../src/modules/payment.ts","../src/modules/form.ts","../src/modules/image.ts","../src/modules/video.ts","../src/modules/auth-google.ts","../src/modules/auth-facebook.ts","../src/modules/auth-apple.ts","../src/modules/webview.ts","../src/modules/i18n.ts","../src/modules/animation.ts","../src/modules/ota.ts","../src/modules/notification.ts","../src/modules/permission.ts","../src/modules/bottom-sheet.ts","../src/modules/flashlist.ts","../src/modules/ui-reusables.ts","../src/modules/index.ts","../src/templates/base.ts","../src/templates/login-tabs.ts","../src/utils/file.ts","../src/utils/package.ts"],"sourcesContent":["import chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { execa } from \"execa\";\nimport fse from \"fs-extra\";\nimport ora from \"ora\";\nimport path from \"path\";\nimport prompts from \"prompts\";\nimport {\n registerAddCommand,\n registerCreateCommand,\n registerUpgradeCommand,\n} from \"./commands/create\";\nimport { getModuleById, getModulesByIds, modules } from \"./modules\";\nimport { generateBaseTemplates } from \"./templates/base\";\nimport { generateLoginTabsTemplates } from \"./templates/login-tabs\";\nimport type { ModuleDef, ProjectConfig } from \"./types\";\nimport { replaceTemplateVars, writeFile, writeJson } from \"./utils/file\";\nimport { generateBasePackageJson, mergeDependencies } from \"./utils/package\";\n\n/** CLI version — bump this when publishing */\nconst CLI_VERSION = \"1.7.1\";\n\n/** Config file name stored in project root */\nconst CONFIG_FILE = \".expo-bbase.json\";\n\n// ─── CLI Entry Point ──────────────────────────────────────────────────────\n\nexport async function run(): Promise<void> {\n const program = new Command();\n\n program\n .name(\"expo-bbase\")\n .description(\"Expo SDK 54+ scaffolding CLI tool\")\n .version(CLI_VERSION);\n\n // Default action: npx expo-bbase <project-name>\n program\n .argument(\"[project-name]\", \"Name of the project to create\")\n .action(async (projectName?: string) => {\n if (!projectName) {\n console.error(chalk.red(\"Error: Please provide a project name.\"));\n console.log(chalk.gray(\"Usage: npx expo-bbase <project-name>\"));\n process.exit(1);\n }\n await createProject(projectName);\n });\n\n registerCreateCommand(program);\n registerUpgradeCommand(program);\n registerAddCommand(program);\n\n await program.parseAsync(process.argv);\n}\n\n// ─── Project Config Helpers ───────────────────────────────────────────────\n\nasync function readProjectConfig(\n targetDir: string\n): Promise<ProjectConfig | null> {\n const configPath = path.join(targetDir, CONFIG_FILE);\n if (!(await fse.pathExists(configPath))) {\n return null;\n }\n return fse.readJson(configPath) as Promise<ProjectConfig>;\n}\n\nasync function writeProjectConfig(\n targetDir: string,\n config: ProjectConfig\n): Promise<void> {\n const configPath = path.join(targetDir, CONFIG_FILE);\n await writeJson(configPath, config);\n}\n\n// ─── Create Project ───────────────────────────────────────────────────────\n\nexport async function createProject(projectName: string): Promise<void> {\n console.log();\n console.log(\n chalk.bold.cyan(\" ╔══════════════════════════════════════╗\")\n );\n console.log(\n chalk.bold.cyan(\" ║ expo-bbase — Expo Scaffolding ║\")\n );\n console.log(\n chalk.bold.cyan(\" ╚══════════════════════════════════════╝\")\n );\n console.log();\n\n // ─── Step 1: UI template selection ──────────────────────────────────\n const { uiTemplate } = await prompts({\n type: \"select\",\n name: \"uiTemplate\",\n message: \"Choose a UI template\",\n choices: [\n {\n title: `${chalk.bold(\"Login + Tabs\")} — Login page, Index/Explore/Mine tabs with rnr components`,\n value: \"login-tabs\",\n description: \"Pre-built login form, 3-tab layout with Button & AlertDialog demos\",\n },\n {\n title: `${chalk.bold(\"Default\")} — Blank tabs (Index + Explore)`,\n value: \"default\",\n description: \"Minimal starter with basic tab navigation\",\n },\n ],\n initial: 0,\n });\n\n if (uiTemplate === undefined) {\n console.log(chalk.yellow(\"\\nCancelled.\"));\n process.exit(0);\n }\n\n const isLoginTabs = uiTemplate === \"login-tabs\";\n\n // ─── Step 2: Interactive module selection ──────────────────────────\n const choices = modules.map((m) => ({\n title: `${chalk.bold(m.name)} — ${chalk.gray(m.description)}`,\n value: m.id,\n // Auto-select ui-reusables when login-tabs template is chosen\n selected: isLoginTabs && m.id === \"ui-reusables\" ? true : m.defaultChecked,\n }));\n\n const { selectedModules } = await prompts({\n type: \"multiselect\",\n name: \"selectedModules\",\n message: \"Select modules (Space to toggle, Enter to confirm)\",\n choices,\n hint: \"- Space toggle · a select all/none · Enter confirm\",\n instructions: false,\n });\n\n if (selectedModules === undefined) {\n console.log(chalk.yellow(\"\\nCancelled.\"));\n process.exit(0);\n }\n\n // Ensure ui-reusables is included when login-tabs is selected\n let finalModuleIds = selectedModules as string[];\n if (isLoginTabs && !finalModuleIds.includes(\"ui-reusables\")) {\n finalModuleIds = [\"ui-reusables\", ...finalModuleIds];\n }\n\n const selectedModuleDefs = getModulesByIds(finalModuleIds);\n const targetDir = path.resolve(process.cwd(), projectName);\n\n console.log();\n console.log(chalk.white(` 📦 Project: ${chalk.bold(projectName)}`));\n console.log(chalk.white(` 📂 Path: ${chalk.gray(targetDir)}`));\n console.log(\n chalk.white(\n ` 🎨 Template: ${chalk.green(isLoginTabs ? \"Login + Tabs\" : \"Default\")}`\n )\n );\n console.log(\n chalk.white(\n ` 🧩 Modules: ${chalk.green(selectedModuleDefs.map((m) => m.name).join(\", \") || \"none\")}`\n )\n );\n console.log();\n\n // ─── Step 3: Create project directory and write base files ─────────\n const spinner = ora(\"Creating project...\").start();\n\n try {\n const baseTemplates = generateBaseTemplates(projectName);\n\n for (const file of baseTemplates) {\n const filePath = path.join(targetDir, file.path);\n const content = replaceTemplateVars(file.content, { projectName });\n await writeFile(filePath, content);\n }\n\n spinner.text = \"Writing module files...\";\n\n // ─── Step 4: Write module template files ──────────────────────────\n for (const mod of selectedModuleDefs) {\n for (const file of mod.files) {\n const filePath = path.join(targetDir, file.path);\n const content = replaceTemplateVars(file.content, { projectName });\n await writeFile(filePath, content);\n }\n }\n\n // ─── Step 4.5: Write UI template files (override base files) ──────\n if (isLoginTabs) {\n spinner.text = \"Writing UI template files...\";\n const loginTabsTemplates = generateLoginTabsTemplates(projectName);\n for (const file of loginTabsTemplates) {\n const filePath = path.join(targetDir, file.path);\n const content = replaceTemplateVars(file.content, { projectName });\n await writeFile(filePath, content);\n }\n }\n\n // ─── Step 4.6: Copy assets directory (icon, splash, fonts) ──────\n spinner.text = \"Copying assets...\";\n const assetsSource = path.join(__dirname, \"..\", \"templates\", \"assets\");\n const assetsTarget = path.join(targetDir, \"assets\");\n if (fse.existsSync(assetsSource)) {\n fse.copySync(assetsSource, assetsTarget);\n }\n\n // ─── Step 5: Generate package.json (all deps in base) ──────────────\n spinner.text = \"Generating package.json...\";\n const pkgJson = generateBasePackageJson(projectName);\n\n const pkgPath = path.join(targetDir, \"package.json\");\n await writeJson(pkgPath, pkgJson);\n\n // ─── Step 6: Update app.json with plugin configurations ──────────\n spinner.text = \"Configuring app.json...\";\n await updateAppJson(targetDir, selectedModuleDefs, projectName);\n\n // ─── Step 7: Update babel.config.js with additional plugins ───────\n spinner.text = \"Configuring Babel...\";\n await updateBabelConfig(targetDir, selectedModuleDefs);\n\n // ─── Step 8: Update app/_layout.tsx with module providers/imports ─\n spinner.text = \"Configuring layout...\";\n await updateLayoutFile(targetDir, selectedModuleDefs);\n\n // ─── Step 9: Write .expo-bbase.json config ───────────────────────\n await writeProjectConfig(targetDir, {\n projectName,\n selectedModules: finalModuleIds,\n cliVersion: CLI_VERSION,\n uiTemplate: isLoginTabs ? \"login-tabs\" : \"default\",\n });\n\n // ─── Step 10: Run yarn install ────────────────────────────────────\n spinner.text = \"Installing dependencies (yarn install)...\";\n try {\n await execa(\"yarn\", [\"install\"], {\n cwd: targetDir,\n timeout: 300_000,\n });\n } catch (installError: unknown) {\n const errMsg =\n installError instanceof Error\n ? installError.message\n : String(installError);\n spinner.warn(\"yarn install failed, please install manually\");\n console.log(chalk.red(` Error: ${errMsg}`));\n console.log(chalk.gray(` cd ${projectName} && yarn install`));\n }\n\n // ─── Done! ────────────────────────────────────────────────────────\n spinner.succeed(chalk.green(\"Project created!\"));\n\n console.log();\n console.log(chalk.bold(\" 🎉 Next steps:\"));\n console.log(chalk.white(` cd ${projectName}`));\n console.log(chalk.white(\" npx expo start\"));\n if (isLoginTabs) {\n console.log(chalk.gray(\" → App starts at login page, sign in to see tabs\"));\n }\n console.log();\n\n if (selectedModuleDefs.length > 0) {\n console.log(chalk.bold(\" 📋 Selected modules:\"));\n for (const mod of selectedModuleDefs) {\n console.log(chalk.white(` ✓ ${mod.name}`));\n }\n console.log();\n }\n } catch (error) {\n spinner.fail(chalk.red(\"Project creation failed\"));\n console.error(error);\n process.exit(1);\n }\n}\n\n// ─── Upgrade Project ─────────────────────────────────────────────────────\n\nexport async function upgradeProject(targetDir: string): Promise<void> {\n console.log();\n console.log(\n chalk.bold.cyan(\" ╔══════════════════════════════════════╗\")\n );\n console.log(\n chalk.bold.cyan(\" ║ expo-bbase — Upgrade ║\")\n );\n console.log(\n chalk.bold.cyan(\" ╚══════════════════════════════════════╝\")\n );\n console.log();\n\n const absDir = path.resolve(targetDir);\n const config = await readProjectConfig(absDir);\n\n if (!config) {\n console.error(\n chalk.red(\n ` ✖ No ${CONFIG_FILE} found in ${absDir}\\n` +\n ` This directory doesn't appear to be an expo-bbase project.\\n` +\n ` If it is, run \"expo-bbase add\" to register modules.`\n )\n );\n process.exit(1);\n }\n\n console.log(\n chalk.white(` 📂 Project: ${chalk.bold(config.projectName)}`)\n );\n console.log(\n chalk.white(` 📋 CLI version: ${chalk.gray(config.cliVersion || \"unknown\")} → ${chalk.green(CLI_VERSION)}`)\n );\n console.log(\n chalk.white(\n ` 🧩 Modules: ${chalk.green(config.selectedModules.join(\", \") || \"none\")}`\n )\n );\n console.log();\n\n const spinner = ora(\"Upgrading project...\").start();\n\n try {\n const selectedModuleDefs = getModulesByIds(config.selectedModules);\n\n // ─── Step 1: Update module files (overwrite) ──────────────────────\n spinner.text = \"Updating module files...\";\n for (const mod of selectedModuleDefs) {\n for (const file of mod.files) {\n const filePath = path.join(absDir, file.path);\n const content = replaceTemplateVars(file.content, {\n projectName: config.projectName,\n });\n await writeFile(filePath, content);\n }\n }\n\n // ─── Step 2: Update dependencies in package.json ─────────────────\n spinner.text = \"Updating dependencies...\";\n // All deps are now in base — just merge the full base deps\n const basePkg = generateBasePackageJson(config.projectName);\n const allDeps = basePkg.dependencies as Record<string, string>;\n const allDevDeps = basePkg.devDependencies as Record<string, string>;\n\n const pkgPath = path.join(absDir, \"package.json\");\n await mergeDependencies(pkgPath, allDeps, allDevDeps);\n\n // ─── Step 3: Update app.json plugins ─────────────────────────────\n spinner.text = \"Updating app.json...\";\n await updateAppJson(absDir, selectedModuleDefs, config.projectName);\n\n // ─── Step 4: Update babel.config.js ───────────────────────────────\n spinner.text = \"Updating Babel config...\";\n await updateBabelConfig(absDir, selectedModuleDefs);\n\n // ─── Step 5: Update _layout.tsx ──────────────────────────────────\n spinner.text = \"Updating layout...\";\n await updateLayoutFile(absDir, selectedModuleDefs);\n\n // ─── Step 6: Update .expo-bbase.json ─────────────────────────────\n config.cliVersion = CLI_VERSION;\n await writeProjectConfig(absDir, config);\n\n // ─── Step 7: yarn install ─────────────────────────────────────────\n spinner.text = \"Installing updated dependencies (yarn install)...\";\n try {\n await execa(\"yarn\", [\"install\"], {\n cwd: absDir,\n timeout: 300_000,\n });\n } catch (installError: unknown) {\n const errMsg =\n installError instanceof Error\n ? installError.message\n : String(installError);\n spinner.warn(\"yarn install failed, please install manually\");\n console.log(chalk.red(` Error: ${errMsg}`));\n }\n\n spinner.succeed(chalk.green(\"Project upgraded!\"));\n\n console.log();\n console.log(chalk.bold(\" 📋 Upgrade summary:\"));\n console.log(chalk.white(` CLI: ${chalk.gray(config.cliVersion)} (before)`));\n console.log(chalk.white(` Modules: ${chalk.green(config.selectedModules.join(\", \"))}`));\n console.log();\n console.log(chalk.bold(\" 🎉 Run your project:\"));\n console.log(chalk.white(\" npx expo start\"));\n console.log();\n } catch (error) {\n spinner.fail(chalk.red(\"Upgrade failed\"));\n console.error(error);\n process.exit(1);\n }\n}\n\n// ─── Add Modules ─────────────────────────────────────────────────────────\n\nexport async function addModule(\n moduleIds: string[],\n targetDir: string\n): Promise<void> {\n console.log();\n\n const absDir = path.resolve(targetDir);\n let config = await readProjectConfig(absDir);\n\n // If no config exists, try to infer from package.json\n if (!config) {\n const pkgPath = path.join(absDir, \"package.json\");\n if (!(await fse.pathExists(pkgPath))) {\n console.error(\n chalk.red(` ✖ No package.json found in ${absDir}`)\n );\n process.exit(1);\n }\n const pkg = await fse.readJson(pkgPath);\n config = {\n projectName: pkg.name || path.basename(absDir),\n selectedModules: [],\n cliVersion: CLI_VERSION,\n };\n console.log(\n chalk.yellow(\n ` ⚠ No ${CONFIG_FILE} found. Creating one for project \"${config.projectName}\".`\n )\n );\n }\n\n // Interactive module selection if no module IDs provided\n if (moduleIds.length === 0) {\n const availableModules = modules.filter(\n (m) => !config!.selectedModules.includes(m.id)\n );\n\n if (availableModules.length === 0) {\n console.log(chalk.green(\" ✓ All modules are already installed!\"));\n process.exit(0);\n }\n\n const choices = availableModules.map((m) => ({\n title: `${chalk.bold(m.name)} — ${chalk.gray(m.description)}`,\n value: m.id,\n selected: false,\n }));\n\n const { selected } = await prompts({\n type: \"multiselect\",\n name: \"selected\",\n message: \"Select modules to add (Space to toggle, Enter to confirm)\",\n choices,\n hint: \"- Space toggle · a select all/none · Enter confirm\",\n instructions: false,\n });\n\n if (selected === undefined || selected.length === 0) {\n console.log(chalk.yellow(\" No modules selected.\"));\n process.exit(0);\n }\n\n moduleIds = selected as string[];\n }\n\n // Validate module IDs\n const invalidIds = moduleIds.filter((id) => !getModuleById(id));\n if (invalidIds.length > 0) {\n console.error(\n chalk.red(` ✖ Unknown module(s): ${invalidIds.join(\", \")}`)\n );\n console.log(\n chalk.gray(\n ` Available: ${modules.map((m) => m.id).join(\", \")}`\n )\n );\n process.exit(1);\n }\n\n // Filter out already installed modules\n const newModuleIds = moduleIds.filter(\n (id) => !config!.selectedModules.includes(id)\n );\n if (newModuleIds.length === 0) {\n console.log(\n chalk.yellow(\" All specified modules are already installed.\")\n );\n process.exit(0);\n }\n\n const newModuleDefs = getModulesByIds(newModuleIds);\n\n console.log(\n chalk.white(` 📂 Project: ${chalk.bold(config.projectName)}`)\n );\n console.log(\n chalk.white(\n ` ➕ Adding: ${chalk.green(newModuleDefs.map((m) => m.name).join(\", \"))}`\n )\n );\n console.log();\n\n const spinner = ora(\"Adding modules...\").start();\n\n try {\n // ─── Step 1: Write module files ───────────────────────────────────\n spinner.text = \"Writing module files...\";\n for (const mod of newModuleDefs) {\n for (const file of mod.files) {\n const filePath = path.join(absDir, file.path);\n const content = replaceTemplateVars(file.content, {\n projectName: config!.projectName,\n });\n await writeFile(filePath, content);\n }\n }\n\n // ─── Step 2: No need to merge deps — all deps are already in base ──\n spinner.text = \"Updating dependencies...\";\n // All module dependencies are included by default in the base package.json.\n // The \"add\" command only needs to write the code/template files.\n\n // ─── Step 3: Update app.json plugins ─────────────────────────────\n spinner.text = \"Updating app.json...\";\n const existingModules = getModulesByIds(config!.selectedModules);\n await updateAppJson(\n absDir,\n [...existingModules, ...newModuleDefs],\n config!.projectName\n );\n\n // ─── Step 4: Update babel.config.js ─────────────────────────────\n spinner.text = \"Updating Babel config...\";\n await updateBabelConfig(absDir, newModuleDefs);\n\n // ─── Step 5: Update _layout.tsx ──────────────────────────────────\n spinner.text = \"Updating layout...\";\n await updateLayoutFile(absDir, newModuleDefs);\n\n // ─── Step 6: Update .expo-bbase.json ─────────────────────────────\n config!.selectedModules.push(...newModuleIds);\n config!.cliVersion = CLI_VERSION;\n await writeProjectConfig(absDir, config!);\n\n // ─── Step 7: yarn install ─────────────────────────────────────────\n spinner.text = \"Installing dependencies (yarn install)...\";\n try {\n await execa(\"yarn\", [\"install\"], {\n cwd: absDir,\n timeout: 300_000,\n });\n } catch (installError: unknown) {\n const errMsg =\n installError instanceof Error\n ? installError.message\n : String(installError);\n spinner.warn(\"yarn install failed, please install manually\");\n console.log(chalk.red(` Error: ${errMsg}`));\n }\n\n spinner.succeed(chalk.green(\"Modules added!\"));\n\n console.log();\n console.log(chalk.bold(\" 📋 Added modules:\"));\n for (const mod of newModuleDefs) {\n console.log(chalk.white(` ✓ ${mod.name} (${mod.id})`));\n }\n console.log();\n console.log(chalk.bold(\" 🧩 All installed modules:\"));\n for (const id of config!.selectedModules) {\n const m = getModuleById(id);\n console.log(chalk.white(` • ${m?.name || id}`));\n }\n console.log();\n } catch (error) {\n spinner.fail(chalk.red(\"Failed to add modules\"));\n console.error(error);\n process.exit(1);\n }\n}\n\n// ─── Shared Helpers ───────────────────────────────────────────────────────\n\n/**\n * Update the generated app.json with plugin configurations from selected modules.\n */\nasync function updateAppJson(\n targetDir: string,\n selectedModules: ModuleDef[],\n _projectName: string\n): Promise<void> {\n const appJsonPath = path.join(targetDir, \"app.json\");\n if (!(await fse.pathExists(appJsonPath))) {\n return;\n }\n const appJson = await fse.readJson(appJsonPath);\n\n const existingPlugins: (string | [string, Record<string, unknown>])[] =\n appJson.expo?.plugins || [];\n\n for (const mod of selectedModules) {\n if (mod.appConfig?.plugins) {\n const modulePlugins = mod.appConfig.plugins as (\n | string\n | [string, Record<string, unknown>]\n )[];\n for (const plugin of modulePlugins) {\n const pluginName =\n typeof plugin === \"string\" ? plugin : plugin[0];\n const exists = existingPlugins.some((p) =>\n typeof p === \"string\" ? p === pluginName : p[0] === pluginName\n );\n if (!exists) {\n existingPlugins.push(plugin);\n }\n }\n }\n }\n\n appJson.expo.plugins = existingPlugins;\n await writeJson(appJsonPath, appJson);\n}\n\n/**\n * Update babel.config.js with additional plugins from selected modules.\n */\nasync function updateBabelConfig(\n targetDir: string,\n selectedModules: ModuleDef[]\n): Promise<void> {\n const babelPath = path.join(targetDir, \"babel.config.js\");\n if (!(await fse.pathExists(babelPath))) {\n return;\n }\n\n let content = await fse.readFile(babelPath, \"utf-8\");\n\n const extraPlugins: string[] = [];\n for (const mod of selectedModules) {\n if (mod.babelPlugins && mod.babelPlugins.length > 0) {\n extraPlugins.push(...mod.babelPlugins);\n }\n }\n\n if (extraPlugins.length > 0) {\n // Filter out plugins that are already in the config\n const pluginsToAdd = extraPlugins.filter((p) => !content.includes(p));\n if (pluginsToAdd.length > 0) {\n const pluginStrings = pluginsToAdd\n .map((p) => ` \"${p}\"`)\n .join(\",\\n\");\n content = content.replace(\n /plugins:\\s*\\[([^\\]]*)\\]/,\n `plugins: [$1${pluginStrings ? \",\\n\" + pluginStrings : \"\"}]`\n );\n await fse.writeFile(babelPath, content, \"utf-8\");\n }\n }\n}\n\n/**\n * Update app/_layout.tsx with imports and providers from selected modules.\n */\nasync function updateLayoutFile(\n targetDir: string,\n selectedModules: ModuleDef[]\n): Promise<void> {\n const layoutPath = path.join(targetDir, \"app/_layout.tsx\");\n if (!(await fse.pathExists(layoutPath))) {\n return;\n }\n\n let content = await fse.readFile(layoutPath, \"utf-8\");\n\n const extraImports: string[] = [];\n const extraProviderPairs: { open: string; close: string }[] = [];\n\n for (const mod of selectedModules) {\n if (mod.layoutImports) {\n // Only add imports that don't already exist\n for (const imp of mod.layoutImports) {\n if (!content.includes(imp)) {\n extraImports.push(imp);\n }\n }\n }\n if (mod.layoutProviders) {\n for (const provider of mod.layoutProviders) {\n const match = provider.match(/^<(\\w+)/);\n if (match) {\n const tagName = match[1];\n // Only add providers that don't already exist\n if (!content.includes(`<${tagName}`)) {\n extraProviderPairs.push({\n open: ` ${provider}`,\n close: ` </${tagName}>`,\n });\n }\n }\n }\n }\n }\n\n if (extraImports.length === 0 && extraProviderPairs.length === 0) {\n return;\n }\n\n if (extraImports.length > 0) {\n const lastImportIndex = content.lastIndexOf(\"import \");\n const lineEndIndex = content.indexOf(\"\\n\", lastImportIndex);\n const importBlock = \"\\n\" + extraImports.join(\"\\n\");\n content =\n content.slice(0, lineEndIndex + 1) +\n importBlock +\n content.slice(lineEndIndex + 1);\n }\n\n if (extraProviderPairs.length > 0) {\n const returnMatch = content.indexOf(\"return (\");\n if (returnMatch !== -1) {\n const openingProviders = extraProviderPairs\n .map((p) => p.open)\n .join(\"\\n\");\n const closingProviders = extraProviderPairs\n .reverse()\n .map((p) => p.close)\n .join(\"\\n\");\n\n const themeProviderOpen = content.indexOf(\"<ThemeProvider\");\n const themeProviderClose = content.lastIndexOf(\"</ThemeProvider>\");\n\n if (themeProviderOpen !== -1 && themeProviderClose !== -1) {\n content =\n content.slice(0, themeProviderOpen) +\n openingProviders +\n \"\\n\" +\n content.slice(themeProviderOpen);\n\n const newThemeProviderClose =\n content.lastIndexOf(\"</ThemeProvider>\");\n const afterClose =\n newThemeProviderClose + \"</ThemeProvider>\".length;\n content =\n content.slice(0, afterClose) +\n \"\\n\" +\n closingProviders +\n content.slice(afterClose);\n }\n }\n }\n\n await fse.writeFile(layoutPath, content, \"utf-8\");\n}\n\n// ─── Run ───────────────────────────────────────────────────────────────────\n\nrun().catch((error) => {\n console.error(chalk.red(\"Fatal error:\"), error);\n process.exit(1);\n});\n","import { Command } from \"commander\";\nimport { createProject } from \"../index\";\nimport { upgradeProject } from \"../index\";\nimport { addModule } from \"../index\";\n\n/**\n * Register the \"create\" command with the CLI program.\n */\nexport function registerCreateCommand(program: Command): void {\n program\n .command(\"create <project-name>\")\n .description(\"Create a new Expo project with selected modules\")\n .action(async (projectName: string) => {\n await createProject(projectName);\n });\n}\n\n/**\n * Register the \"upgrade\" command with the CLI program.\n */\nexport function registerUpgradeCommand(program: Command): void {\n program\n .command(\"upgrade\")\n .description(\"Upgrade an existing expo-bbase project to the latest module versions\")\n .option(\"--dir <path>\", \"Project directory (default: current directory)\", process.cwd())\n .action(async (options: { dir: string }) => {\n await upgradeProject(options.dir);\n });\n}\n\n/**\n * Register the \"add\" command with the CLI program.\n */\nexport function registerAddCommand(program: Command): void {\n program\n .command(\"add [modules...]\")\n .description(\"Add one or more modules to an existing expo-bbase project\")\n .option(\"--dir <path>\", \"Project directory (default: current directory)\", process.cwd())\n .action(async (moduleIds: string[], options: { dir: string }) => {\n await addModule(moduleIds, options.dir);\n });\n}\n","/**\n * Join an array of strings with newlines.\n * This helper avoids template literal escaping issues in module content strings.\n */\nexport function lines(...args: string[]): string {\n return args.join(\"\\n\");\n}\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst networkModule: ModuleDef = {\n id: \"network\",\n name: \"网络请求\",\n description: \"TanStack Query 封装,统一拦截器、错误处理\",\n defaultChecked: true,\n dependencies: {\n \"@tanstack/react-query\": \"^5.60.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"api/client.ts\",\n content: lines(\n 'import { Alert } from \"react-native\";',\n \"\",\n 'const API_BASE_URL = process.env.EXPO_PUBLIC_API_URL || \"https://api.example.com\";',\n \"\",\n \"interface RequestConfig {\",\n ' method: \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";',\n \" url: string;\",\n \" data?: unknown;\",\n \" headers?: Record<string, string>;\",\n \" params?: Record<string, string>;\",\n \"}\",\n \"\",\n \"interface ApiResponse<T> {\",\n \" data: T;\",\n \" message: string;\",\n \" code: number;\",\n \"}\",\n \"\",\n \"class ApiClient {\",\n \" private baseUrl: string;\",\n \" private defaultHeaders: Record<string, string>;\",\n \" private requestInterceptors: Array<(config: RequestConfig) => RequestConfig> = [];\",\n \" private responseInterceptors: Array<(response: Response) => Response> = [];\",\n \"\",\n \" constructor(baseUrl: string) {\",\n \" this.baseUrl = baseUrl;\",\n \" this.defaultHeaders = {\",\n ' \"Content-Type\": \"application/json\",',\n ' Accept: \"application/json\",',\n \" };\",\n \" }\",\n \"\",\n \" /** Add a request interceptor */\",\n \" addRequestInterceptor(interceptor: (config: RequestConfig) => RequestConfig): void {\",\n \" this.requestInterceptors.push(interceptor);\",\n \" }\",\n \"\",\n \" /** Add a response interceptor */\",\n \" addResponseInterceptor(interceptor: (response: Response) => Response): void {\",\n \" this.responseInterceptors.push(interceptor);\",\n \" }\",\n \"\",\n \" /** Set the authorization token */\",\n \" setAuthToken(token: string): void {\",\n ' this.defaultHeaders[\"Authorization\"] = `Bearer ${token}`;',\n \" }\",\n \"\",\n \" /** Clear the authorization token */\",\n \" clearAuthToken(): void {\",\n ' delete this.defaultHeaders[\"Authorization\"];',\n \" }\",\n \"\",\n \" /** Apply request interceptors */\",\n \" private applyRequestInterceptors(config: RequestConfig): RequestConfig {\",\n \" return this.requestInterceptors.reduce(\",\n \" (acc, interceptor) => interceptor(acc),\",\n \" config\",\n \" );\",\n \" }\",\n \"\",\n \" /** Build URL with query params */\",\n \" private buildUrl(url: string, params?: Record<string, string>): string {\",\n \" const fullUrl = new URL(url, this.baseUrl);\",\n \" if (params) {\",\n ' Object.entries(params).forEach(([key, value]) => {',\n \" fullUrl.searchParams.append(key, value);\",\n \" });\",\n \" }\",\n \" return fullUrl.toString();\",\n \" }\",\n \"\",\n \" /** Core request method */\",\n \" async request<T>(config: RequestConfig): Promise<ApiResponse<T>> {\",\n \" const interceptedConfig = this.applyRequestInterceptors(config);\",\n \" const url = this.buildUrl(interceptedConfig.url, interceptedConfig.params);\",\n \"\",\n \" try {\",\n \" const response = await fetch(url, {\",\n \" method: interceptedConfig.method,\",\n \" headers: {\",\n \" ...this.defaultHeaders,\",\n \" ...interceptedConfig.headers,\",\n \" },\",\n \" body: interceptedConfig.data\",\n \" ? JSON.stringify(interceptedConfig.data)\",\n \" : undefined,\",\n \" });\",\n \"\",\n \" if (!response.ok) {\",\n \" const errorData = await response.json().catch(() => null);\",\n \" throw new ApiError(\",\n \" response.status,\",\n \" errorData?.message || response.statusText,\",\n \" errorData\",\n \" );\",\n \" }\",\n \"\",\n \" const data: ApiResponse<T> = await response.json();\",\n \" return data;\",\n \" } catch (error) {\",\n \" if (error instanceof ApiError) {\",\n \" throw error;\",\n \" }\",\n ' throw new ApiError(0, \"网络请求失败,请检查网络连接\", null);',\n \" }\",\n \" }\",\n \"\",\n \" /** GET request */\",\n \" async get<T>(url: string, params?: Record<string, string>): Promise<ApiResponse<T>> {\",\n ' return this.request<T>({ method: \"GET\", url, params });',\n \" }\",\n \"\",\n \" /** POST request */\",\n \" async post<T>(url: string, data?: unknown): Promise<ApiResponse<T>> {\",\n ' return this.request<T>({ method: \"POST\", url, data });',\n \" }\",\n \"\",\n \" /** PUT request */\",\n \" async put<T>(url: string, data?: unknown): Promise<ApiResponse<T>> {\",\n ' return this.request<T>({ method: \"PUT\", url, data });',\n \" }\",\n \"\",\n \" /** PATCH request */\",\n \" async patch<T>(url: string, data?: unknown): Promise<ApiResponse<T>> {\",\n ' return this.request<T>({ method: \"PATCH\", url, data });',\n \" }\",\n \"\",\n \" /** DELETE request */\",\n \" async delete<T>(url: string): Promise<ApiResponse<T>> {\",\n ' return this.request<T>({ method: \"DELETE\", url });',\n \" }\",\n \"}\",\n \"\",\n \"/** Custom API error class */\",\n \"export class ApiError extends Error {\",\n \" code: number;\",\n \" detail: unknown;\",\n \"\",\n \" constructor(code: number, message: string, detail: unknown) {\",\n \" super(message);\",\n ' this.name = \"ApiError\";',\n \" this.code = code;\",\n \" this.detail = detail;\",\n \" }\",\n \"}\",\n \"\",\n \"/** Global error handler */\",\n \"export function handleApiError(error: unknown): void {\",\n \" if (error instanceof ApiError) {\",\n \" if (error.code === 401) {\",\n ' Alert.alert(\"认证失败\", \"请重新登录\");',\n \" } else if (error.code === 403) {\",\n ' Alert.alert(\"权限不足\", \"您没有权限执行此操作\");',\n \" } else if (error.code === 0) {\",\n ' Alert.alert(\"网络错误\", error.message);',\n \" } else {\",\n ' Alert.alert(\"请求失败\", error.message);',\n \" }\",\n \" } else {\",\n ' Alert.alert(\"未知错误\", \"请稍后重试\");',\n \" }\",\n \"}\",\n \"\",\n \"/** Singleton API client instance */\",\n \"export const apiClient = new ApiClient(API_BASE_URL);\",\n \"\",\n \"export default apiClient;\"\n ),\n },\n {\n path: \"api/interceptors.ts\",\n content: lines(\n 'import type { RequestConfig } from \"./client\";',\n 'import { apiClient } from \"./client\";',\n \"\",\n \"/**\",\n \" * Initialize all request interceptors.\",\n \" * Called once during app startup.\",\n \" */\",\n \"export function setupRequestInterceptors(): void {\",\n \" apiClient.addRequestInterceptor((config: RequestConfig) => {\",\n \" return config;\",\n \" });\",\n \"\",\n \" if (__DEV__) {\",\n \" apiClient.addRequestInterceptor((config: RequestConfig) => {\",\n \" console.log(\",\n ' `[API Request] ${config.method} ${config.url}`,',\n ' config.data ? config.data : \"\"',\n \" );\",\n \" return config;\",\n \" });\",\n \" }\",\n \"}\",\n \"\",\n \"/**\",\n \" * Initialize all response interceptors.\",\n \" * Called once during app startup.\",\n \" */\",\n \"export function setupResponseInterceptors(): void {\",\n \" if (__DEV__) {\",\n \" apiClient.addResponseInterceptor((response: Response) => {\",\n ' console.log(`[API Response] ${response.status} ${response.url}`);',\n \" return response;\",\n \" });\",\n \" }\",\n \"}\",\n \"\",\n \"/**\",\n \" * Setup all interceptors at once.\",\n \" */\",\n \"export function setupInterceptors(): void {\",\n \" setupRequestInterceptors();\",\n \" setupResponseInterceptors();\",\n \"}\"\n ),\n },\n {\n path: \"api/types.ts\",\n content: lines(\n \"/** Common API response types */\",\n \"\",\n \"/** Paginated response wrapper */\",\n \"export interface PaginatedResponse<T> {\",\n \" data: T[];\",\n \" total: number;\",\n \" page: number;\",\n \" pageSize: number;\",\n \" totalPages: number;\",\n \"}\",\n \"\",\n \"/** Standard API response */\",\n \"export interface ApiResponse<T> {\",\n \" data: T;\",\n \" message: string;\",\n \" code: number;\",\n \"}\",\n \"\",\n \"/** Common error response */\",\n \"export interface ErrorResponse {\",\n \" message: string;\",\n \" code: number;\",\n \" errors?: Record<string, string[]>;\",\n \"}\",\n \"\",\n \"/** User type example */\",\n \"export interface User {\",\n \" id: string;\",\n \" email: string;\",\n \" name: string;\",\n \" avatar?: string;\",\n \" createdAt: string;\",\n \" updatedAt: string;\",\n \"}\",\n \"\",\n \"/** Auth response */\",\n \"export interface AuthResponse {\",\n \" user: User;\",\n \" token: string;\",\n \" refreshToken: string;\",\n \"}\"\n ),\n },\n {\n path: \"api/queries.ts\",\n content: lines(\n 'import { useQuery, useMutation, useQueryClient } from \"@tanstack/react-query\";',\n 'import { apiClient, handleApiError } from \"./client\";',\n 'import type { ApiResponse } from \"./types\";',\n \"\",\n \"/**\",\n \" * Example query hook for fetching data.\",\n \" */\",\n \"export function useApiQuery<T>(\",\n \" key: string[],\",\n \" url: string,\",\n \" params?: Record<string, string>\",\n \") {\",\n \" return useQuery({\",\n \" queryKey: key,\",\n \" queryFn: () => apiClient.get<T>(url, params),\",\n \" retry: 2,\",\n \" staleTime: 5 * 60 * 1000,\",\n \" });\",\n \"}\",\n \"\",\n \"/**\",\n \" * Example mutation hook for posting data.\",\n \" */\",\n \"export function useApiMutation<TData, TVariables>(\",\n \" url: string,\",\n ' method: \"POST\" | \"PUT\" | \"PATCH\" = \"POST\"',\n \") {\",\n \" const queryClient = useQueryClient();\",\n \"\",\n \" return useMutation({\",\n \" mutationFn: (variables: TVariables) => {\",\n \" switch (method) {\",\n ' case \"PUT\":',\n \" return apiClient.put<TData>(url, variables);\",\n ' case \"PATCH\":',\n \" return apiClient.patch<TData>(url, variables);\",\n \" default:\",\n \" return apiClient.post<TData>(url, variables);\",\n \" }\",\n \" },\",\n \" onSuccess: () => {\",\n \" queryClient.invalidateQueries({ queryKey: [url] });\",\n \" },\",\n \" onError: (error) => {\",\n \" handleApiError(error);\",\n \" },\",\n \" });\",\n \"}\",\n \"\",\n \"/**\",\n \" * Example: Fetch current user profile\",\n \" */\",\n \"export function useUserProfile() {\",\n \" return useApiQuery<ApiResponse<{ name: string; email: string }>>(\",\n ' [\"user\", \"profile\"],',\n ' \"/user/profile\"',\n \" );\",\n \"}\"\n ),\n },\n ],\n layoutProviders: [\"<QueryClientProvider client={queryClient}>\"],\n layoutImports: [\n 'import { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";',\n \"const queryClient = new QueryClient({ defaultOptions: { queries: { retry: 2, staleTime: 5 * 60 * 1000 } } });\",\n ],\n};\n\nexport default networkModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst stateModule: ModuleDef = {\n id: \"state\",\n name: \"状态管理\",\n description: \"Zustand stores 模板,persist 中间件\",\n defaultChecked: true,\n dependencies: {\n zustand: \"^5.0.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"stores/index.ts\",\n content: lines('export { useUserStore } from \"./slices/userSlice\";'),\n },\n {\n path: \"stores/slices/userSlice.ts\",\n content: lines(\n 'import { create } from \"zustand\";',\n 'import { persist, createJSONStorage } from \"zustand/middleware\";',\n \"\",\n \"interface User {\",\n \" id: string;\",\n \" email: string;\",\n \" name: string;\",\n \" avatar?: string;\",\n \"}\",\n \"\",\n \"interface UserState {\",\n \" user: User | null;\",\n \" token: string | null;\",\n \" isAuthenticated: boolean;\",\n \" hasHydrated: boolean;\",\n \"\",\n \" setUser: (user: User, token: string) => void;\",\n \" updateUser: (partial: Partial<User>) => void;\",\n \" clearUser: () => void;\",\n \" setHasHydrated: (value: boolean) => void;\",\n \"}\",\n \"\",\n \"export const useUserStore = create<UserState>()(\",\n \" persist(\",\n \" (set, get) => ({\",\n \" user: null,\",\n \" token: null,\",\n \" isAuthenticated: false,\",\n \" hasHydrated: false,\",\n \"\",\n \" setUser: (user: User, token: string) => {\",\n \" set({ user, token, isAuthenticated: true });\",\n \" },\",\n \"\",\n \" updateUser: (partial: Partial<User>) => {\",\n \" const currentUser = get().user;\",\n \" if (currentUser) {\",\n \" set({ user: { ...currentUser, ...partial } });\",\n \" }\",\n \" },\",\n \"\",\n \" clearUser: () => {\",\n \" set({ user: null, token: null, isAuthenticated: false });\",\n \" },\",\n \"\",\n \" setHasHydrated: (value: boolean) => {\",\n \" set({ hasHydrated: value });\",\n \" },\",\n \" }),\",\n \" {\",\n ' name: \"user-store\",',\n \" storage: createJSONStorage(() => {\",\n \" try {\",\n ' const { MMKV } = require(\"react-native-mmkv\");',\n \" const storage = new MMKV();\",\n \" return {\",\n \" getItem: (name: string) => {\",\n \" const value = storage.getString(name);\",\n \" return value ?? null;\",\n \" },\",\n \" setItem: (name: string, value: string) => {\",\n \" storage.set(name, value);\",\n \" },\",\n \" removeItem: (name: string) => {\",\n \" storage.delete(name);\",\n \" },\",\n \" };\",\n \" } catch {\",\n \" return {\",\n \" getItem: () => null,\",\n \" setItem: () => {},\",\n \" removeItem: () => {},\",\n \" };\",\n \" }\",\n \" }),\",\n \" partialize: (state) => ({\",\n \" user: state.user,\",\n \" token: state.token,\",\n \" isAuthenticated: state.isAuthenticated,\",\n \" }),\",\n \" onRehydrateStorage: () => (state) => {\",\n \" state?.setHasHydrated(true);\",\n \" },\",\n \" }\",\n \" )\",\n \");\"\n ),\n },\n ],\n};\n\nexport default stateModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst storageModule: ModuleDef = {\n id: \"storage\",\n name: \"本地存储\",\n description: \"MMKV 封装,类型安全 API\",\n defaultChecked: true,\n dependencies: {\n \"react-native-mmkv\": \"^3.1.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"storage/index.ts\",\n content: lines(\n 'import { MMKV } from \"react-native-mmkv\";',\n \"\",\n \"/**\",\n \" * Centralized MMKV storage instance.\",\n \" */\",\n \"const storage = new MMKV({\",\n ' id: \"app-storage\",',\n \" encryptionKey: process.env.EXPO_PUBLIC_STORAGE_KEY || undefined,\",\n \"});\",\n \"\",\n \"/** Storage keys */\",\n \"export const StorageKeys = {\",\n ' AUTH_TOKEN: \"auth_token\",',\n ' REFRESH_TOKEN: \"refresh_token\",',\n ' USER_DATA: \"user_data\",',\n ' THEME: \"theme\",',\n ' LANGUAGE: \"language\",',\n ' ONBOARDING_COMPLETED: \"onboarding_completed\",',\n ' LAST_SYNC_TIME: \"last_sync_time\",',\n ' CACHE_VERSION: \"cache_version\",',\n \"} as const;\",\n \"\",\n \"export type StorageKey = (typeof StorageKeys)[keyof typeof StorageKeys];\",\n \"\",\n \"export function getString(key: StorageKey): string | null {\",\n \" return storage.getString(key) ?? null;\",\n \"}\",\n \"\",\n \"export function setString(key: StorageKey, value: string): void {\",\n \" storage.set(key, value);\",\n \"}\",\n \"\",\n \"export function getNumber(key: StorageKey): number | null {\",\n \" const value = storage.getNumber(key);\",\n \" return value ?? null;\",\n \"}\",\n \"\",\n \"export function setNumber(key: StorageKey, value: number): void {\",\n \" storage.set(key, value);\",\n \"}\",\n \"\",\n \"export function getBoolean(key: StorageKey): boolean | null {\",\n \" const value = storage.getBoolean(key);\",\n \" return value ?? null;\",\n \"}\",\n \"\",\n \"export function setBoolean(key: StorageKey, value: boolean): void {\",\n \" storage.set(key, value);\",\n \"}\",\n \"\",\n \"export function getJson<T>(key: StorageKey): T | null {\",\n \" const raw = storage.getString(key);\",\n \" if (raw === undefined) return null;\",\n \" try {\",\n \" return JSON.parse(raw) as T;\",\n \" } catch {\",\n \" return null;\",\n \" }\",\n \"}\",\n \"\",\n \"export function setJson<T>(key: StorageKey, value: T): void {\",\n \" storage.set(key, JSON.stringify(value));\",\n \"}\",\n \"\",\n \"export function remove(key: StorageKey): void {\",\n \" storage.delete(key);\",\n \"}\",\n \"\",\n \"export function contains(key: StorageKey): boolean {\",\n \" return storage.contains(key);\",\n \"}\",\n \"\",\n \"export function clearAll(): void {\",\n \" storage.clearAll();\",\n \"}\",\n \"\",\n \"export function getAllKeys(): string[] {\",\n \" return storage.getAllKeys();\",\n \"}\",\n \"\",\n \"export default storage;\"\n ),\n },\n ],\n};\n\nexport default storageModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst paymentModule: ModuleDef = {\n id: \"payment\",\n name: \"Apple/iOS 支付\",\n description: \"react-native-iap 封装\",\n defaultChecked: false,\n dependencies: {\n \"react-native-iap\": \"^12.15.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"modules/payment/index.ts\",\n content: lines(\n 'export { usePayment } from \"./usePayment\";',\n 'export type { ProductInfo, PurchaseResult, PaymentError } from \"./types\";'\n ),\n },\n {\n path: \"modules/payment/types.ts\",\n content: lines(\n 'import type { Product, Purchase } from \"react-native-iap\";',\n \"\",\n \"export interface ProductInfo {\",\n \" productId: string;\",\n \" title: string;\",\n \" description: string;\",\n \" price: string;\",\n \" currency: string;\",\n \"}\",\n \"\",\n \"export interface PurchaseResult {\",\n \" success: boolean;\",\n \" purchase?: Purchase;\",\n \" error?: PaymentError;\",\n \"}\",\n \"\",\n \"export interface PaymentError {\",\n \" code: string;\",\n \" message: string;\",\n \" nativeError?: unknown;\",\n \"}\",\n \"\",\n \"export interface PaymentConfig {\",\n \" productIds: string[];\",\n \" subscriptionIds?: string[];\",\n \"}\"\n ),\n },\n {\n path: \"modules/payment/usePayment.ts\",\n content: lines(\n 'import { useCallback, useEffect, useRef, useState } from \"react\";',\n 'import {',\n \" initConnection,\",\n \" endConnection,\",\n \" getProducts,\",\n \" requestPurchase,\",\n \" requestSubscription,\",\n \" finishTransaction,\",\n \" purchaseUpdatedListener,\",\n \" purchaseErrorListener,\",\n \" Product,\",\n \" Purchase,\",\n \" PurchaseError,\",\n '} from \"react-native-iap\";',\n 'import type { PaymentConfig, ProductInfo, PurchaseResult, PaymentError as AppPaymentError } from \"./types\";',\n \"\",\n \"export function usePayment(config: PaymentConfig) {\",\n \" const [products, setProducts] = useState<ProductInfo[]>([]);\",\n \" const [isLoading, setIsLoading] = useState(false);\",\n \" const [isConnected, setIsConnected] = useState(false);\",\n \" const purchaseUpdateSubscription = useRef<ReturnType<typeof purchaseUpdatedListener> | null>(null);\",\n \" const purchaseErrorSubscription = useRef<ReturnType<typeof purchaseErrorListener> | null>(null);\",\n \"\",\n \" const initialize = useCallback(async () => {\",\n \" try {\",\n \" setIsLoading(true);\",\n \" const connected = await initConnection();\",\n \" setIsConnected(connected);\",\n \"\",\n \" if (connected) {\",\n \" const allIds = [...config.productIds, ...(config.subscriptionIds || [])];\",\n \" if (allIds.length > 0) {\",\n ' const fetchedProducts = await getProducts({ skus: allIds });',\n \" const mapped = fetchedProducts.map(mapProductToInfo);\",\n \" setProducts(mapped);\",\n \" }\",\n \" }\",\n \" } catch (error) {\",\n ' console.error(\"[Payment] Initialization failed:\", error);',\n \" } finally {\",\n \" setIsLoading(false);\",\n \" }\",\n \" }, [config.productIds, config.subscriptionIds]);\",\n \"\",\n \" const handlePurchase = useCallback(\",\n \" async (purchase: Purchase): Promise<void> => {\",\n \" const receipt = purchase.transactionReceipt;\",\n \" if (receipt) {\",\n \" // TODO: Validate receipt with your backend server\",\n \" await finishTransaction({ purchase, isConsumable: false });\",\n \" }\",\n \" },\",\n \" []\",\n \" );\",\n \"\",\n \" const buyProduct = useCallback(\",\n \" async (productId: string): Promise<PurchaseResult> => {\",\n \" try {\",\n ' const purchase = await requestPurchase({ sku: productId });',\n \" if (purchase && purchase.length > 0) {\",\n \" await handlePurchase(purchase[0]);\",\n \" return { success: true, purchase: purchase[0] };\",\n \" }\",\n \" return { success: false };\",\n \" } catch (error) {\",\n \" const paymentError = mapError(error);\",\n \" return { success: false, error: paymentError };\",\n \" }\",\n \" },\",\n \" [handlePurchase]\",\n \" );\",\n \"\",\n \" const buySubscription = useCallback(\",\n \" async (subscriptionId: string): Promise<PurchaseResult> => {\",\n \" try {\",\n ' const purchase = await requestSubscription({ sku: subscriptionId });',\n \" if (purchase && purchase.length > 0) {\",\n \" await handlePurchase(purchase[0]);\",\n \" return { success: true, purchase: purchase[0] };\",\n \" }\",\n \" return { success: false };\",\n \" } catch (error) {\",\n \" const paymentError = mapError(error);\",\n \" return { success: false, error: paymentError };\",\n \" }\",\n \" },\",\n \" [handlePurchase]\",\n \" );\",\n \"\",\n \" useEffect(() => {\",\n \" initialize();\",\n \"\",\n \" purchaseUpdateSubscription.current = purchaseUpdatedListener(\",\n \" async (purchase: Purchase) => {\",\n ' console.log(\"[Payment] Purchase updated:\", purchase.transactionId);',\n \" await handlePurchase(purchase);\",\n \" }\",\n \" );\",\n \"\",\n \" purchaseErrorSubscription.current = purchaseErrorListener(\",\n \" (error: PurchaseError) => {\",\n ' console.error(\"[Payment] Purchase error:\", error.message);',\n \" }\",\n \" );\",\n \"\",\n \" return () => {\",\n \" purchaseUpdateSubscription.current?.remove();\",\n \" purchaseErrorSubscription.current?.remove();\",\n \" endConnection();\",\n \" };\",\n \" }, [initialize, handlePurchase]);\",\n \"\",\n \" return {\",\n \" products,\",\n \" isLoading,\",\n \" isConnected,\",\n \" buyProduct,\",\n \" buySubscription,\",\n \" refresh: initialize,\",\n \" };\",\n \"}\",\n \"\",\n \"function mapProductToInfo(product: Product): ProductInfo {\",\n \" return {\",\n \" productId: product.productId,\",\n \" title: product.title,\",\n \" description: product.description,\",\n \" price: product.price,\",\n ' currency: product.currency || \"USD\",',\n \" };\",\n \"}\",\n \"\",\n \"function mapError(error: unknown): AppPaymentError {\",\n \" if (error instanceof Error) {\",\n \" return {\",\n ' code: \"PURCHASE_ERROR\",',\n \" message: error.message,\",\n \" };\",\n \" }\",\n \" return {\",\n ' code: \"UNKNOWN_ERROR\",',\n ' message: \"An unknown error occurred\",',\n \" };\",\n \"}\"\n ),\n },\n ],\n};\n\nexport default paymentModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst formModule: ModuleDef = {\n id: \"form\",\n name: \"表单验证\",\n description: \"React Hook Form + Zod\",\n defaultChecked: false,\n dependencies: {\n \"react-hook-form\": \"^7.54.0\",\n \"@hookform/resolvers\": \"^3.9.0\",\n zod: \"^3.24.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"modules/form/index.ts\",\n content: lines(\n 'export { useForm } from \"react-hook-form\";',\n 'export { z } from \"zod\";',\n 'export { zodResolver } from \"@hookform/resolvers/zod\";',\n 'export * from \"./schemas/auth\";'\n ),\n },\n {\n path: \"modules/form/schemas/auth.ts\",\n content: lines(\n 'import { z } from \"zod\";',\n \"\",\n \"export const loginSchema = z.object({\",\n \" email: z\",\n ' .string()',\n ' .min(1, \"请输入邮箱地址\")',\n ' .email(\"请输入有效的邮箱地址\"),',\n \" password: z\",\n ' .string()',\n ' .min(6, \"密码至少6位\")',\n ' .max(50, \"密码最多50位\"),',\n \"});\",\n \"\",\n \"export const registerSchema = z\",\n \" .object({\",\n \" name: z\",\n ' .string()',\n ' .min(2, \"姓名至少2个字符\")',\n ' .max(50, \"姓名最多50个字符\"),',\n \" email: z\",\n ' .string()',\n ' .min(1, \"请输入邮箱地址\")',\n ' .email(\"请输入有效的邮箱地址\"),',\n \" password: z\",\n ' .string()',\n ' .min(6, \"密码至少6位\")',\n ' .max(50, \"密码最多50位\"),',\n ' confirmPassword: z.string().min(1, \"请确认密码\"),',\n \" })\",\n \" .refine((data) => data.password === data.confirmPassword, {\",\n ' message: \"两次密码输入不一致\",',\n ' path: [\"confirmPassword\"],',\n \" });\",\n \"\",\n \"export const resetPasswordSchema = z.object({\",\n \" email: z\",\n ' .string()',\n ' .min(1, \"请输入邮箱地址\")',\n ' .email(\"请输入有效的邮箱地址\"),',\n \"});\",\n \"\",\n \"export const changePasswordSchema = z\",\n \" .object({\",\n ' currentPassword: z.string().min(1, \"请输入当前密码\"),',\n \" newPassword: z\",\n ' .string()',\n ' .min(6, \"新密码至少6位\")',\n ' .max(50, \"新密码最多50位\"),',\n ' confirmPassword: z.string().min(1, \"请确认新密码\"),',\n \" })\",\n \" .refine((data) => data.newPassword === data.confirmPassword, {\",\n ' message: \"两次密码输入不一致\",',\n ' path: [\"confirmPassword\"],',\n \" });\",\n \"\",\n \"export type LoginFormData = z.infer<typeof loginSchema>;\",\n \"export type RegisterFormData = z.infer<typeof registerSchema>;\",\n \"export type ResetPasswordFormData = z.infer<typeof resetPasswordSchema>;\",\n \"export type ChangePasswordFormData = z.infer<typeof changePasswordSchema>;\"\n ),\n },\n ],\n};\n\nexport default formModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst imageModule: ModuleDef = {\n id: \"image\",\n name: \"图片\",\n description: \"expo-image 封装\",\n defaultChecked: false,\n dependencies: {\n \"expo-image\": \"~3.0.11\",\n },\n devDependencies: {},\n files: [\n {\n path: \"modules/media/CachedImage.tsx\",\n content: lines(\n 'import React, { useMemo } from \"react\";',\n 'import { Image, type ImageProps } from \"expo-image\";',\n 'import { StyleSheet, View, ActivityIndicator } from \"react-native\";',\n \"\",\n \"interface CachedImageProps extends Omit<ImageProps, \\\"source\\\"> {\",\n \" uri: string;\",\n \" width?: number;\",\n \" height?: number;\",\n \" borderRadius?: number;\",\n ' placeholderColor?: string;',\n \" showLoading?: boolean;\",\n \"}\",\n \"\",\n \"export function CachedImage({\",\n \" uri,\",\n \" width,\",\n \" height,\",\n \" borderRadius,\",\n ' placeholderColor = \"#f0f0f0\",',\n \" showLoading = true,\",\n \" style,\",\n \" ...rest\",\n \"}: CachedImageProps) {\",\n \" const containerStyle = useMemo(\",\n \" () =>\",\n \" StyleSheet.compose(\",\n \" {\",\n \" width,\",\n \" height,\",\n \" borderRadius,\",\n \" backgroundColor: placeholderColor,\",\n \" overflow: \\\"hidden\\\",\",\n \" },\",\n \" style as any\",\n \" ),\",\n \" [width, height, borderRadius, placeholderColor, style]\",\n \" );\",\n \"\",\n \" return (\",\n \" <View style={containerStyle}>\",\n \" <Image\",\n \" source={{ uri }}\",\n \" style={StyleSheet.absoluteFillObject}\",\n ' contentFit=\"cover\"',\n \" transition={200}\",\n \" placeholder={{\",\n ' blurhash: \"LEHV6nWB2yk8pyo0adR*.7kCMdnj\",',\n \" }}\",\n \" {...rest}\",\n \" />\",\n \" {showLoading && (\",\n \" <Image.LoadingIndicatorContainer\",\n \" style={StyleSheet.absoluteFillObject}\",\n \" >\",\n ' <ActivityIndicator color=\"#999\" />',\n \" </Image.LoadingIndicatorContainer>\",\n \" )}\",\n \" </View>\",\n \" );\",\n \"}\",\n \"\",\n \"export default CachedImage;\"\n ),\n },\n ],\n};\n\nexport default imageModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst videoModule: ModuleDef = {\n id: \"video\",\n name: \"视频\",\n description: \"expo-av 封装\",\n defaultChecked: false,\n dependencies: {\n \"expo-av\": \"~16.0.8\",\n },\n devDependencies: {},\n files: [\n {\n path: \"modules/media/VideoPlayer.tsx\",\n content: lines(\n 'import React, { useCallback, useRef, useState } from \"react\";',\n 'import {',\n \" Video,\",\n \" ResizeMode,\",\n \" type AVPlaybackStatus,\",\n \" type VideoProps,\",\n '} from \"expo-av\";',\n 'import { StyleSheet, View, TouchableOpacity, ActivityIndicator } from \"react-native\";',\n \"\",\n \"interface VideoPlayerProps extends Omit<VideoProps, \\\"source\\\"> {\",\n \" uri: string;\",\n \" autoPlay?: boolean;\",\n \" loop?: boolean;\",\n \" showControls?: boolean;\",\n \" width?: number;\",\n \" height?: number;\",\n \"}\",\n \"\",\n \"export function VideoPlayer({\",\n \" uri,\",\n \" autoPlay = false,\",\n \" loop = false,\",\n \" showControls = true,\",\n \" width,\",\n \" height,\",\n \" style,\",\n \" ...rest\",\n \"}: VideoPlayerProps) {\",\n \" const videoRef = useRef<Video>(null);\",\n \" const [isPlaying, setIsPlaying] = useState(autoPlay);\",\n \" const [isLoading, setIsLoading] = useState(true);\",\n \"\",\n \" const handlePlaybackStatusUpdate = useCallback(\",\n \" (status: AVPlaybackStatus) => {\",\n \" if (status.isLoaded) {\",\n \" setIsLoading(status.isBuffering);\",\n \" setIsPlaying(status.isPlaying);\",\n \" }\",\n \" },\",\n \" []\",\n \" );\",\n \"\",\n \" const togglePlayback = useCallback(async () => {\",\n \" if (!videoRef.current) return;\",\n \" if (isPlaying) {\",\n \" await videoRef.current.pauseAsync();\",\n \" } else {\",\n \" await videoRef.current.playAsync();\",\n \" }\",\n \" }, [isPlaying]);\",\n \"\",\n \" const containerStyle = StyleSheet.compose(\",\n ' { width, height, backgroundColor: \"#000\", borderRadius: 8, overflow: \"hidden\" },',\n \" style as any\",\n \" );\",\n \"\",\n \" return (\",\n \" <View style={containerStyle}>\",\n \" <Video\",\n \" ref={videoRef}\",\n \" source={{ uri }}\",\n \" style={StyleSheet.absoluteFillObject}\",\n \" resizeMode={ResizeMode.CONTAIN}\",\n \" shouldPlay={autoPlay}\",\n \" isLooping={loop}\",\n \" onPlaybackStatusUpdate={handlePlaybackStatusUpdate}\",\n \" {...rest}\",\n \" />\",\n \"\",\n \" {isLoading && (\",\n ' <View style={styles.overlay}>',\n ' <ActivityIndicator color=\"#fff\" size=\"large\" />',\n \" </View>\",\n \" )}\",\n \"\",\n \" {showControls && !isLoading && (\",\n \" <TouchableOpacity\",\n \" style={styles.overlay}\",\n \" onPress={togglePlayback}\",\n \" activeOpacity={0.7}\",\n \" />\",\n \" )}\",\n \" </View>\",\n \" );\",\n \"}\",\n \"\",\n \"const styles = StyleSheet.create({\",\n \" overlay: {\",\n \" ...StyleSheet.absoluteFillObject,\",\n ' justifyContent: \"center\",',\n ' alignItems: \"center\",',\n ' backgroundColor: \"rgba(0, 0, 0, 0.3)\",',\n \" },\",\n \"});\",\n \"\",\n \"export default VideoPlayer;\"\n ),\n },\n ],\n};\n\nexport default videoModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst authGoogleModule: ModuleDef = {\n id: \"auth-google\",\n name: \"Google 登录\",\n description: \"@react-native-google-signin/google-signin\",\n defaultChecked: false,\n dependencies: {\n \"@react-native-google-signin/google-signin\": \"^13.1.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"modules/auth/google.ts\",\n content: lines(\n 'import {',\n \" GoogleSignin,\",\n \" statusCodes,\",\n \" type User,\",\n '} from \"@react-native-google-signin/google-signin\";',\n \"\",\n \"export interface GoogleAuthConfig {\",\n \" iosClientId?: string;\",\n \" webClientId?: string;\",\n \" offlineAccess?: boolean;\",\n \" forceCodeForRefreshToken?: boolean;\",\n \"}\",\n \"\",\n \"export function setupGoogleAuth(config: GoogleAuthConfig): void {\",\n \" GoogleSignin.configure({\",\n \" iosClientId: config.iosClientId,\",\n \" webClientId: config.webClientId,\",\n \" offlineAccess: config.offlineAccess ?? false,\",\n \" forceCodeForRefreshToken: config.forceCodeForRefreshToken ?? true,\",\n \" });\",\n \"}\",\n \"\",\n \"export async function signInWithGoogle(): Promise<{\",\n \" user: User | null;\",\n \" idToken: string | null;\",\n \" error: string | null;\",\n \"}> {\",\n \" try {\",\n \" await GoogleSignin.hasPlayServices({\",\n \" showPlayServicesUpdateDialog: true,\",\n \" });\",\n \"\",\n \" const userInfo = await GoogleSignin.signIn();\",\n \" const idToken = userInfo.data?.idToken ?? null;\",\n \"\",\n \" return {\",\n \" user: userInfo.data ?? null,\",\n \" idToken,\",\n \" error: null,\",\n \" };\",\n \" } catch (error: any) {\",\n ' let errorMessage = \"Google 登录失败\";',\n \"\",\n \" if (error.code === statusCodes.SIGN_IN_CANCELLED) {\",\n ' errorMessage = \"用户取消了登录\";',\n \" } else if (error.code === statusCodes.IN_PROGRESS) {\",\n ' errorMessage = \"登录正在进行中\";',\n \" } else if (error.code === statusCodes.PLAY_SERVICES_NOT_AVAILABLE) {\",\n ' errorMessage = \"设备不支持 Google Play 服务\";',\n \" }\",\n \"\",\n ' console.error(\"[GoogleAuth] Error:\", errorMessage, error);',\n \" return { user: null, idToken: null, error: errorMessage };\",\n \" }\",\n \"}\",\n \"\",\n \"export async function signOutGoogle(): Promise<void> {\",\n \" try {\",\n \" await GoogleSignin.signOut();\",\n \" } catch (error) {\",\n ' console.error(\"[GoogleAuth] Sign out error:\", error);',\n \" }\",\n \"}\",\n \"\",\n \"export async function getCurrentGoogleUser(): Promise<User | null> {\",\n \" try {\",\n \" const user = await GoogleSignin.getCurrentUser();\",\n \" return user;\",\n \" } catch {\",\n \" return null;\",\n \" }\",\n \"}\",\n \"\",\n \"export async function isGoogleSignedIn(): Promise<boolean> {\",\n \" try {\",\n \" return GoogleSignin.hasPreviousSignIn();\",\n \" } catch {\",\n \" return false;\",\n \" }\",\n \"}\"\n ),\n },\n ],\n};\n\nexport default authGoogleModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst authFacebookModule: ModuleDef = {\n id: \"auth-facebook\",\n name: \"Facebook 登录\",\n description: \"expo-auth-session + expo-web-browser(SDK 54 不再支持 expo-facebook)\",\n defaultChecked: false,\n dependencies: {\n \"expo-auth-session\": \"~7.0.11\",\n \"expo-web-browser\": \"~15.0.11\",\n \"expo-crypto\": \"~15.0.9\",\n },\n devDependencies: {},\n files: [\n {\n path: \"modules/auth/facebook.ts\",\n content: lines(\n 'import { responseTypes } from \"expo-auth-session\";',\n 'import { makeRedirectUri, useAuthRequest, useAutoDiscovery } from \"expo-auth-session\";',\n 'import * as WebBrowser from \"expo-web-browser\";',\n \"\",\n \"// Facebook OAuth 配置\",\n \"export interface FacebookAuthConfig {\",\n \" clientId: string; // Facebook App ID\",\n \"}\",\n \"\",\n \"// Facebook OAuth 端点\",\n \"const FACEBOOK_AUTH_ENDPOINT = \\\"https://www.facebook.com/v18.0/dialog/oauth\\\";\",\n \"const FACEBOOK_TOKEN_ENDPOINT = \\\"https://graph.facebook.com/v18.0/oauth/access_token\\\";\",\n \"\",\n \"// 确保 WebBrowser 回调完成时关闭\",\n \"WebBrowser.maybeCompleteAuthSession();\",\n \"\",\n \"/**\",\n \" * 使用 expo-auth-session 实现 Facebook 登录\",\n \" *\",\n \" * 用法:\",\n \" * 1. 在 Facebook Developer Console 创建应用,获取 App ID\",\n \" * 2. 配置 OAuth 重定向 URI\",\n \" * 3. 在组件中使用 useFacebookAuth hook\",\n \" *\",\n \" * 注意:SDK 54 不再支持 expo-facebook,改用 expo-auth-session\",\n \" */\",\n \"\",\n \"export function useFacebookAuth(config: FacebookAuthConfig) {\",\n \" const redirectUri = makeRedirectUri({\",\n \" scheme: 'your-app-scheme', // 替换为你的 app scheme\",\n \" });\",\n \"\",\n \" const [request, response, promptAsync] = useAuthRequest(\",\n \" {\",\n \" clientId: config.clientId,\",\n \" scopes: ['public_profile', 'email'],\",\n \" redirectUri,\",\n \" },\",\n \" {\",\n \" authorizationEndpoint: FACEBOOK_AUTH_ENDPOINT,\",\n \" tokenEndpoint: FACEBOOK_TOKEN_ENDPOINT,\",\n \" }\",\n \" );\",\n \"\",\n \" return { request, response, promptAsync };\",\n \"}\",\n \"\",\n \"/**\",\n \" * 从 Facebook OAuth 响应中提取用户信息\",\n \" */\",\n \"export async function getFacebookUserInfo(accessToken: string): Promise<{\",\n \" userId: string;\",\n \" userName: string;\",\n \" email: string | null;\",\n \"}> {\",\n \" const response = await fetch(\",\n \" `https://graph.facebook.com/me?access_token=${accessToken}&fields=id,name,email`\",\n \" );\",\n \" const userData = await response.json();\",\n \"\",\n \" return {\",\n \" userId: userData.id,\",\n \" userName: userData.name,\",\n \" email: userData.email ?? null,\",\n \" };\",\n \"}\",\n ),\n },\n ],\n appConfig: {},\n};\n\nexport default authFacebookModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst authAppleModule: ModuleDef = {\n id: \"auth-apple\",\n name: \"Apple 登录\",\n description: \"expo-apple-authentication\",\n defaultChecked: false,\n dependencies: {\n \"expo-apple-authentication\": \"~8.0.8\",\n },\n devDependencies: {},\n files: [\n {\n path: \"modules/auth/apple.ts\",\n content: lines(\n 'import * as AppleAuthentication from \"expo-apple-authentication\";',\n 'import { Platform } from \"react-native\";',\n \"\",\n \"export interface AppleAuthResult {\",\n \" user: string;\",\n \" email: string | null;\",\n \" fullName: AppleAuthentication.AppleAuthenticationFullName | null;\",\n \" identityToken: string | null;\",\n \" authorizationCode: string | null;\",\n \" error: string | null;\",\n \"}\",\n \"\",\n \"export async function isAppleAuthAvailable(): Promise<boolean> {\",\n ' if (Platform.OS !== \"ios\") return false;',\n \" return AppleAuthentication.isAvailableAsync();\",\n \"}\",\n \"\",\n \"export async function signInWithApple(): Promise<AppleAuthResult> {\",\n \" try {\",\n \" const credential = await AppleAuthentication.signInAsync({\",\n \" requestedScopes: [\",\n \" AppleAuthentication.AppleAuthenticationScope.FULL_NAME,\",\n \" AppleAuthentication.AppleAuthenticationScope.EMAIL,\",\n \" ],\",\n \" });\",\n \"\",\n \" return {\",\n \" user: credential.user,\",\n \" email: credential.email ?? null,\",\n \" fullName: credential.fullName ?? null,\",\n \" identityToken: credential.identityToken ?? null,\",\n \" authorizationCode: credential.authorizationCode ?? null,\",\n \" error: null,\",\n \" };\",\n \" } catch (error: any) {\",\n ' if (error.code === \"ERR_CANCELED\") {',\n \" return {\",\n ' user: \"\",',\n \" email: null,\",\n \" fullName: null,\",\n \" identityToken: null,\",\n \" authorizationCode: null,\",\n ' error: \"用户取消了 Apple 登录\",',\n \" };\",\n \" }\",\n \"\",\n ' console.error(\"[AppleAuth] Error:\", error);',\n \" return {\",\n ' user: \"\",',\n \" email: null,\",\n \" fullName: null,\",\n \" identityToken: null,\",\n \" authorizationCode: null,\",\n ' error: \"Apple 登录失败\",',\n \" };\",\n \" }\",\n \"}\",\n \"\",\n \"export async function getAppleCredentialState(\",\n \" userId: string\",\n \"): Promise<AppleAuthentication.AppleAuthenticationCredentialState> {\",\n \" return AppleAuthentication.getCredentialStateAsync(userId);\",\n \"}\",\n \"\",\n \"export async function signOutApple(userId: string): Promise<boolean> {\",\n \" try {\",\n \" const state = await getAppleCredentialState(userId);\",\n \" return state === AppleAuthentication.AppleAuthenticationCredentialState.REVOKED;\",\n \" } catch {\",\n \" return false;\",\n \" }\",\n \"}\"\n ),\n },\n ],\n appConfig: {\n plugins: [\"expo-apple-authentication\"],\n },\n};\n\nexport default authAppleModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst webviewModule: ModuleDef = {\n id: \"webview\",\n name: \"WebView 容器\",\n description: \"react-native-webview + JS Bridge\",\n defaultChecked: false,\n dependencies: {\n \"react-native-webview\": \"~13.15.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"modules/webview/WebViewContainer.tsx\",\n content: lines(\n 'import React, { useCallback, useRef } from \"react\";',\n 'import { View, StyleSheet, ActivityIndicator } from \"react-native\";',\n 'import WebView, {',\n \" type WebViewMessageEvent,\",\n \" type WebViewProps,\",\n '} from \"react-native-webview\";',\n 'import { bridge, type BridgeMessage } from \"./bridge\";',\n \"\",\n \"interface WebViewContainerProps extends Omit<WebViewProps, \\\"source\\\"> {\",\n \" uri: string;\",\n \" showLoading?: boolean;\",\n \" onBridgeMessage?: (message: BridgeMessage) => void;\",\n \"}\",\n \"\",\n \"export function WebViewContainer({\",\n \" uri,\",\n \" showLoading = true,\",\n \" onBridgeMessage,\",\n \" style,\",\n \" ...rest\",\n \"}: WebViewContainerProps) {\",\n \" const webViewRef = useRef<WebView>(null);\",\n \"\",\n \" const handleMessage = useCallback(\",\n \" (event: WebViewMessageEvent) => {\",\n \" try {\",\n \" const data = JSON.parse(event.nativeEvent.data) as BridgeMessage;\",\n \" const processed = bridge.receive(data);\",\n \" if (onBridgeMessage) {\",\n \" onBridgeMessage(processed);\",\n \" }\",\n \" } catch (error) {\",\n ' console.warn(\"[WebView] Failed to parse message:\", error);',\n \" }\",\n \" },\",\n \" [onBridgeMessage]\",\n \" );\",\n \"\",\n \" const sendToWebView = useCallback(\",\n \" (message: BridgeMessage) => {\",\n \" const script = bridge.buildSendScript(message);\",\n \" webViewRef.current?.injectJavaScript(script);\",\n \" },\",\n \" []\",\n \" );\",\n \"\",\n \" const injectedJavaScript = [\",\n \" \\\"(function() {\\\",\",\n \" \\\" window.ReactNativeWebView = window.ReactNativeWebView || {};\\\",\",\n \" \\\" window.NativeBridge = {\\\",\",\n \" \\\" send: function(type, payload) {\\\",\",\n ' \" window.ReactNativeWebView.postMessage(JSON.stringify({ type: type, payload: payload }));\",',\n \" \\\" }\\\",\\,\",\n \" \\\" };\\,\",\n \" \\\"})();\\,\",\n \" \\\"true;\\,\",\n \" ].join(\\\"\\\\\\\\n\\\");\",\n \"\",\n \" return (\",\n \" <View style={styles.container}>\",\n \" <WebView\",\n \" ref={webViewRef}\",\n \" source={{ uri }}\",\n \" style={[styles.webview, style]}\",\n ' originWhitelist={[\"*\"]}',\n \" javaScriptEnabled\",\n \" domStorageEnabled\",\n \" injectedJavaScript={injectedJavaScript}\",\n \" onMessage={handleMessage}\",\n \" startInLoadingState={showLoading}\",\n \" renderLoading={() =>\",\n \" showLoading ? (\",\n ' <View style={styles.loadingContainer}>',\n ' <ActivityIndicator size=\"large\" color=\"#007AFF\" />',\n \" </View>\",\n \" ) : null\",\n \" }\",\n \" {...rest}\",\n \" />\",\n \" </View>\",\n \" );\",\n \"}\",\n \"\",\n \"const styles = StyleSheet.create({\",\n \" container: {\",\n \" flex: 1,\",\n \" },\",\n \" webview: {\",\n \" flex: 1,\",\n \" },\",\n \" loadingContainer: {\",\n \" position: \\\"absolute\\\",\",\n \" top: 0,\",\n \" left: 0,\",\n \" right: 0,\",\n \" bottom: 0,\",\n \" justifyContent: \\\"center\\\",\",\n \" alignItems: \\\"center\\\",\",\n ' backgroundColor: \"rgba(255, 255, 255, 0.8)\",',\n \" },\",\n \"});\",\n \"\",\n \"export default WebViewContainer;\"\n ),\n },\n {\n path: \"modules/webview/bridge.ts\",\n content: lines(\n \"export interface BridgeMessage {\",\n \" type: string;\",\n \" payload: Record<string, unknown>;\",\n \"}\",\n \"\",\n \"export const BridgeMessageType = {\",\n ' NAVIGATE: \"NAVIGATE\",',\n ' SHARE: \"SHARE\",',\n ' PAYMENT: \"PAYMENT\",',\n ' AUTH: \"AUTH\",',\n ' TOAST: \"TOAST\",',\n ' CLOSE: \"CLOSE\",',\n ' CUSTOM: \"CUSTOM\",',\n \"} as const;\",\n \"\",\n \"export type BridgeMessageTypeValue =\",\n \" (typeof BridgeMessageType)[keyof typeof BridgeMessageType];\",\n \"\",\n \"export const bridge = {\",\n \" receive(message: BridgeMessage): BridgeMessage {\",\n \" if (!message.type) {\",\n ' throw new Error(\"Bridge message must have a \\'type\\' field\");',\n \" }\",\n \" return {\",\n \" type: message.type,\",\n \" payload: message.payload || {},\",\n \" };\",\n \" },\",\n \"\",\n \" buildSendScript(message: BridgeMessage): string {\",\n \" const payload = JSON.stringify(message.payload);\",\n \" return [\",\n \" \\\"(function() {\\\",\",\n \" \\\" if (typeof window.onNativeMessage === \\'function\\') {\\\",\",\n \" \\\" window.onNativeMessage(\\'\\\" + message.type + \\\"\\', \\\" + payload + \\\");\\\",\",\n \" \\\" }\\\",\",\n \" \\\"})();\\\",\",\n \" \\\"true;\\\",\",\n \" ].join(\\\"\\\\\\\\n\\\");\",\n \" },\",\n \"\",\n \" createMessage(\",\n \" type: BridgeMessageTypeValue | string,\",\n \" payload: Record<string, unknown> = {}\",\n \" ): BridgeMessage {\",\n \" return { type, payload };\",\n \" },\",\n \"};\"\n ),\n },\n ],\n};\n\nexport default webviewModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst i18nModule: ModuleDef = {\n id: \"i18n\",\n name: \"多语言\",\n description: \"i18next + react-i18next\",\n defaultChecked: false,\n dependencies: {\n i18next: \"^24.2.0\",\n \"react-i18next\": \"^15.2.0\",\n \"expo-localization\": \"~17.0.8\",\n },\n devDependencies: {},\n files: [\n {\n path: \"modules/i18n/index.ts\",\n content: lines(\n 'import i18n from \"i18next\";',\n 'import { initReactI18next } from \"react-i18next\";',\n 'import { getLocales } from \"expo-localization\";',\n 'import en from \"./locales/en.json\";',\n 'import zh from \"./locales/zh.json\";',\n \"\",\n \"const resources = {\",\n \" en: { translation: en },\",\n \" zh: { translation: zh },\",\n \"};\",\n \"\",\n \"function getDeviceLanguage(): string {\",\n \" const locales = getLocales();\",\n \" const deviceLang = locales[0]?.languageCode ?? \\\"en\\\";\",\n \" return deviceLang in resources ? deviceLang : \\\"en\\\";\",\n \"}\",\n \"\",\n \"i18n.use(initReactI18next).init({\",\n \" resources,\",\n \" lng: getDeviceLanguage(),\",\n ' fallbackLng: \"en\",',\n \" interpolation: {\",\n \" escapeValue: false,\",\n \" },\",\n ' compatibilityJSON: \"v4\",',\n \"});\",\n \"\",\n \"export default i18n;\",\n \"\",\n 'export { useTranslation } from \"react-i18next\";'\n ),\n },\n {\n path: \"modules/i18n/locales/en.json\",\n content: [\n \"{\",\n ' \"common\": {',\n ' \"ok\": \"OK\",',\n ' \"cancel\": \"Cancel\",',\n ' \"confirm\": \"Confirm\",',\n ' \"save\": \"Save\",',\n ' \"delete\": \"Delete\",',\n ' \"edit\": \"Edit\",',\n ' \"loading\": \"Loading...\",',\n ' \"error\": \"Something went wrong\",',\n ' \"retry\": \"Retry\",',\n ' \"search\": \"Search\"',\n \" },\",\n ' \"auth\": {',\n ' \"login\": \"Sign In\",',\n ' \"register\": \"Sign Up\",',\n ' \"logout\": \"Sign Out\",',\n ' \"email\": \"Email\",',\n ' \"password\": \"Password\",',\n ' \"forgotPassword\": \"Forgot Password?\"',\n \" },\",\n ' \"home\": {',\n ' \"title\": \"Home\",',\n ' \"welcome\": \"Welcome\"',\n \" },\",\n ' \"settings\": {',\n ' \"title\": \"Settings\",',\n ' \"language\": \"Language\",',\n ' \"theme\": \"Theme\",',\n ' \"about\": \"About\"',\n \" },\",\n ' \"errors\": {',\n ' \"network\": \"Network error. Please check your connection.\",',\n ' \"unauthorized\": \"Session expired. Please sign in again.\",',\n ' \"forbidden\": \"You don\\'t have permission to do this.\",',\n ' \"notFound\": \"The requested resource was not found.\",',\n ' \"server\": \"Server error. Please try again later.\"',\n \" }\",\n \"}\",\n ].join(\"\\n\"),\n },\n {\n path: \"modules/i18n/locales/zh.json\",\n content: [\n \"{\",\n ' \"common\": {',\n ' \"ok\": \"确定\",',\n ' \"cancel\": \"取消\",',\n ' \"confirm\": \"确认\",',\n ' \"save\": \"保存\",',\n ' \"delete\": \"删除\",',\n ' \"edit\": \"编辑\",',\n ' \"loading\": \"加载中...\",',\n ' \"error\": \"出了点问题\",',\n ' \"retry\": \"重试\",',\n ' \"search\": \"搜索\"',\n \" },\",\n ' \"auth\": {',\n ' \"login\": \"登录\",',\n ' \"register\": \"注册\",',\n ' \"logout\": \"退出登录\",',\n ' \"email\": \"邮箱\",',\n ' \"password\": \"密码\",',\n ' \"forgotPassword\": \"忘记密码?\"',\n \" },\",\n ' \"home\": {',\n ' \"title\": \"首页\",',\n ' \"welcome\": \"欢迎\"',\n \" },\",\n ' \"settings\": {',\n ' \"title\": \"设置\",',\n ' \"language\": \"语言\",',\n ' \"theme\": \"主题\",',\n ' \"about\": \"关于\"',\n \" },\",\n ' \"errors\": {',\n ' \"network\": \"网络错误,请检查网络连接。\",',\n ' \"unauthorized\": \"登录已过期,请重新登录。\",',\n ' \"forbidden\": \"您没有权限执行此操作。\",',\n ' \"notFound\": \"未找到请求的资源。\",',\n ' \"server\": \"服务器错误,请稍后重试。\"',\n \" }\",\n \"}\",\n ].join(\"\\n\"),\n },\n ],\n};\n\nexport default i18nModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst animationModule: ModuleDef = {\n id: \"animation\",\n name: \"动画/手势\",\n description: \"Reanimated 4 + Gesture Handler + Worklets(已默认包含,此模块仅添加封装代码)\",\n defaultChecked: false,\n // Dependencies are already included in base package.json\n dependencies: {},\n devDependencies: {},\n files: [\n {\n path: \"modules/animation/index.ts\",\n content: lines(\n 'export { FadeIn, FadeOut, SlideIn, SlideOut, ScaleIn, ScaleOut } from \"./transitions\";'\n ),\n },\n {\n path: \"modules/animation/transitions.ts\",\n content: lines(\n 'import Animated, {',\n \" FadeIn as ReanimatedFadeIn,\",\n \" FadeOut as ReanimatedFadeOut,\",\n \" SlideInUp as ReanimatedSlideInUp,\",\n \" SlideInDown as ReanimatedSlideInDown,\",\n \" SlideInLeft as ReanimatedSlideInLeft,\",\n \" SlideInRight as ReanimatedSlideInRight,\",\n \" SlideOutUp as ReanimatedSlideOutUp,\",\n \" SlideOutDown as ReanimatedSlideOutDown,\",\n \" SlideOutLeft as ReanimatedSlideOutLeft,\",\n \" SlideOutRight as ReanimatedSlideOutRight,\",\n \" ZoomIn as ReanimatedZoomIn,\",\n \" ZoomOut as ReanimatedZoomOut,\",\n \" type EntryExitAnimationFunction,\",\n '} from \"react-native-reanimated\";',\n \"\",\n \"export const FadeIn = ReanimatedFadeIn.duration(300);\",\n \"export const FadeOut = ReanimatedFadeOut.duration(300);\",\n \"export const SlideIn = ReanimatedSlideInDown.duration(300).springify();\",\n \"export const SlideInTop = ReanimatedSlideInUp.duration(300).springify();\",\n \"export const SlideInLeft = ReanimatedSlideInLeft.duration(300).springify();\",\n \"export const SlideInRight = ReanimatedSlideInRight.duration(300).springify();\",\n \"export const SlideOut = ReanimatedSlideOutDown.duration(300).springify();\",\n \"export const SlideOutTop = ReanimatedSlideOutUp.duration(300).springify();\",\n \"export const SlideOutLeft = ReanimatedSlideOutLeft.duration(300).springify();\",\n \"export const SlideOutRight = ReanimatedSlideOutRight.duration(300).springify();\",\n \"export const ScaleIn = ReanimatedZoomIn.duration(200).springify();\",\n \"export const ScaleOut = ReanimatedZoomOut.duration(200).springify();\",\n \"\",\n \"export function staggerFadeIn(\",\n \" index: number,\",\n \" baseDelay: number = 50\",\n \"): EntryExitAnimationFunction {\",\n \" return ReanimatedFadeIn.duration(300).delay(index * baseDelay);\",\n \"}\",\n \"\",\n \"export function staggerSlideIn(\",\n \" index: number,\",\n \" baseDelay: number = 80\",\n \"): EntryExitAnimationFunction {\",\n \" return ReanimatedSlideInDown.duration(400).springify().delay(index * baseDelay);\",\n \"}\",\n \"\",\n \"export { Animated };\"\n ),\n },\n ],\n babelPlugins: [],\n};\n\nexport default animationModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst otaModule: ModuleDef = {\n id: \"ota\",\n name: \"OTA 更新\",\n description: \"expo-updates\",\n defaultChecked: false,\n dependencies: {\n \"expo-updates\": \"~29.0.17\",\n },\n devDependencies: {},\n files: [\n {\n path: \"modules/ota/useOTAUpdate.ts\",\n content: lines(\n 'import { useCallback, useEffect, useState } from \"react\";',\n 'import * as Updates from \"expo-updates\";',\n 'import { Alert, Platform } from \"react-native\";',\n \"\",\n \"interface OTAUpdateState {\",\n \" isUpdateAvailable: boolean;\",\n \" isDownloading: boolean;\",\n \" progress: number;\",\n \" error: string | null;\",\n \"}\",\n \"\",\n \"export function useOTAUpdate() {\",\n \" const [state, setState] = useState<OTAUpdateState>({\",\n \" isUpdateAvailable: false,\",\n \" isDownloading: false,\",\n \" progress: 0,\",\n \" error: null,\",\n \" });\",\n \"\",\n \" const checkForUpdate = useCallback(async () => {\",\n \" if (__DEV__ || Platform.OS === \\\"web\\\") {\",\n \" return;\",\n \" }\",\n \"\",\n \" try {\",\n \" const update = await Updates.checkForUpdateAsync();\",\n \" setState((prev) => ({\",\n \" ...prev,\",\n \" isUpdateAvailable: update.isAvailable,\",\n \" error: null,\",\n \" }));\",\n \" return update.isAvailable;\",\n \" } catch (error) {\",\n \" const message = error instanceof Error ? error.message : \\\"检查更新失败\\\";\",\n \" setState((prev) => ({ ...prev, error: message }));\",\n \" return false;\",\n \" }\",\n \" }, []);\",\n \"\",\n \" const downloadUpdate = useCallback(async () => {\",\n \" if (__DEV__ || Platform.OS === \\\"web\\\") return null;\",\n \"\",\n \" try {\",\n \" setState((prev) => ({ ...prev, isDownloading: true, progress: 0 }));\",\n \" const result = await Updates.fetchUpdateAsync();\",\n \" setState((prev) => ({\",\n \" ...prev,\",\n \" isDownloading: false,\",\n \" isUpdateAvailable: false,\",\n \" progress: 1,\",\n \" }));\",\n \" return result;\",\n \" } catch (error) {\",\n \" const message = error instanceof Error ? error.message : \\\"下载更新失败\\\";\",\n \" setState((prev) => ({\",\n \" ...prev,\",\n \" isDownloading: false,\",\n \" error: message,\",\n \" }));\",\n \" return null;\",\n \" }\",\n \" }, []);\",\n \"\",\n \" const downloadAndRestart = useCallback(async () => {\",\n \" const result = await downloadUpdate();\",\n \" if (result?.isNew) {\",\n \" Alert.alert(\",\n ' \"更新已下载\",',\n ' \"应用需要重启以完成更新,是否立即重启?\",',\n \" [\",\n ' { text: \"稍后\", style: \"cancel\" },',\n \" {\",\n ' text: \"立即重启\",',\n ' style: \"default\",',\n \" onPress: async () => {\",\n \" await Updates.reloadAsync();\",\n \" },\",\n \" },\",\n \" ]\",\n \" );\",\n \" }\",\n \" }, [downloadUpdate]);\",\n \"\",\n \" const restartApp = useCallback(async () => {\",\n \" await Updates.reloadAsync();\",\n \" }, []);\",\n \"\",\n \" useEffect(() => {\",\n \" checkForUpdate();\",\n \" }, [checkForUpdate]);\",\n \"\",\n \" return {\",\n \" ...state,\",\n \" checkForUpdate,\",\n \" downloadUpdate,\",\n \" downloadAndRestart,\",\n \" restartApp,\",\n \" };\",\n \"}\"\n ),\n },\n ],\n};\n\nexport default otaModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst notificationModule: ModuleDef = {\n id: \"notification\",\n name: \"应用内通知\",\n description: \"expo-notifications + 自定义组件\",\n defaultChecked: false,\n dependencies: {\n \"expo-notifications\": \"~0.32.17\",\n },\n devDependencies: {},\n files: [\n {\n path: \"modules/notification/index.ts\",\n content: lines(\n 'export { InAppNotification, useInAppNotification } from \"./InAppNotification\";',\n 'export { setupNotificationHandlers } from \"./InAppNotification\";'\n ),\n },\n {\n path: \"modules/notification/InAppNotification.tsx\",\n content: lines(\n 'import React, {',\n \" createContext,\",\n \" useCallback,\",\n \" useContext,\",\n \" useEffect,\",\n \" useMemo,\",\n \" useRef,\",\n \" useState,\",\n '} from \"react\";',\n 'import { Animated, StyleSheet, Text, TouchableOpacity, View } from \"react-native\";',\n 'import * as Notifications from \"expo-notifications\";',\n 'import type { Subscription } from \"expo-notifications\";',\n \"\",\n \"export function setupNotificationHandlers(): void {\",\n \" Notifications.setNotificationHandler({\",\n \" handleNotification: async () => ({\",\n \" shouldShowAlert: true,\",\n \" shouldPlaySound: true,\",\n \" shouldSetBadge: true,\",\n \" }),\",\n \" });\",\n \"}\",\n \"\",\n \"interface NotificationData {\",\n \" id: string;\",\n \" title: string;\",\n \" message: string;\",\n ' type: \"info\" | \"success\" | \"warning\" | \"error\";',\n \" duration?: number;\",\n \"}\",\n \"\",\n \"interface InAppNotificationContextType {\",\n \" showNotification: (data: Omit<NotificationData, \\\"id\\\">) => void;\",\n \"}\",\n \"\",\n \"const InAppNotificationContext = createContext<InAppNotificationContextType>({\",\n \" showNotification: () => {},\",\n \"});\",\n \"\",\n \"export function useInAppNotification(): InAppNotificationContextType {\",\n \" return useContext(InAppNotificationContext);\",\n \"}\",\n \"\",\n \"let notificationIdCounter = 0;\",\n \"\",\n \"export function InAppNotificationProvider({\",\n \" children,\",\n \"}: {\",\n \" children: React.ReactNode;\",\n \"}) {\",\n \" const [notifications, setNotifications] = useState<NotificationData[]>([]);\",\n \" const timersRef = useRef<Map<string, ReturnType<typeof setTimeout>>>(new Map());\",\n \"\",\n \" const removeNotification = useCallback((id: string) => {\",\n \" setNotifications((prev) => prev.filter((n) => n.id !== id));\",\n \" const timer = timersRef.current.get(id);\",\n \" if (timer) {\",\n \" clearTimeout(timer);\",\n \" timersRef.current.delete(id);\",\n \" }\",\n \" }, []);\",\n \"\",\n \" const showNotification = useCallback(\",\n \" (data: Omit<NotificationData, \\\"id\\\">) => {\",\n ' const id = `notification-${++notificationIdCounter}`;',\n \" const notification: NotificationData = { ...data, id };\",\n \" setNotifications((prev) => [...prev, notification]);\",\n \"\",\n \" const duration = data.duration ?? 3000;\",\n \" const timer = setTimeout(() => {\",\n \" removeNotification(id);\",\n \" }, duration);\",\n \" timersRef.current.set(id, timer);\",\n \" },\",\n \" [removeNotification]\",\n \" );\",\n \"\",\n \" const contextValue = useMemo(\",\n \" () => ({ showNotification }),\",\n \" [showNotification]\",\n \" );\",\n \"\",\n \" return (\",\n \" <InAppNotificationContext.Provider value={contextValue}>\",\n \" {children}\",\n ' <View style={styles.container} pointerEvents=\"box-none\">',\n \" {notifications.map((notification) => (\",\n \" <NotificationCard\",\n \" key={notification.id}\",\n \" notification={notification}\",\n \" onDismiss={() => removeNotification(notification.id)}\",\n \" />\",\n \" ))}\",\n \" </View>\",\n \" </InAppNotificationContext.Provider>\",\n \" );\",\n \"}\",\n \"\",\n \"interface NotificationCardProps {\",\n \" notification: NotificationData;\",\n \" onDismiss: () => void;\",\n \"}\",\n \"\",\n \"const typeColors: Record<NotificationData[\\\"type\\\"], string> = {\",\n ' info: \"#007AFF\",',\n ' success: \"#34C759\",',\n ' warning: \"#FF9500\",',\n ' error: \"#FF3B30\",',\n \"};\",\n \"\",\n \"function NotificationCard({ notification, onDismiss }: NotificationCardProps) {\",\n \" const opacity = useRef(new Animated.Value(0)).current;\",\n \"\",\n \" useEffect(() => {\",\n \" Animated.timing(opacity, {\",\n \" toValue: 1,\",\n \" duration: 300,\",\n \" useNativeDriver: true,\",\n \" }).start();\",\n \" }, [opacity]);\",\n \"\",\n \" const handleDismiss = useCallback(() => {\",\n \" Animated.timing(opacity, {\",\n \" toValue: 0,\",\n \" duration: 200,\",\n \" useNativeDriver: true,\",\n \" }).start(() => onDismiss());\",\n \" }, [opacity, onDismiss]);\",\n \"\",\n \" return (\",\n ' <Animated.View style={[styles.card, { opacity }]}>',\n \" <View\",\n \" style={[styles.accent, { backgroundColor: typeColors[notification.type] }]}\",\n \" />\",\n \" <View style={styles.content}>\",\n \" <Text style={styles.title}>{notification.title}</Text>\",\n \" <Text style={styles.message}>{notification.message}</Text>\",\n \" </View>\",\n \" <TouchableOpacity onPress={handleDismiss} style={styles.closeButton}>\",\n ' <Text style={styles.closeButtonText}>X</Text>',\n \" </TouchableOpacity>\",\n \" </Animated.View>\",\n \" );\",\n \"}\",\n \"\",\n \"const styles = StyleSheet.create({\",\n \" container: {\",\n \" position: \\\"absolute\\\",\",\n \" top: 60,\",\n \" left: 16,\",\n \" right: 16,\",\n \" zIndex: 9999,\",\n \" gap: 8,\",\n \" },\",\n \" card: {\",\n \" flexDirection: \\\"row\\\",\",\n ' alignItems: \"center\",',\n ' backgroundColor: \"#fff\",',\n \" borderRadius: 12,\",\n ' shadowColor: \"#000\",',\n \" shadowOffset: { width: 0, height: 2 },\",\n \" shadowOpacity: 0.15,\",\n \" shadowRadius: 8,\",\n \" elevation: 5,\",\n ' overflow: \"hidden\",',\n \" },\",\n \" accent: {\",\n \" width: 4,\",\n ' height: \"100%\",',\n \" minHeight: 48,\",\n \" },\",\n \" content: {\",\n \" flex: 1,\",\n \" paddingVertical: 12,\",\n \" paddingHorizontal: 12,\",\n \" },\",\n \" title: {\",\n \" fontSize: 14,\",\n ' fontWeight: \"600\",',\n ' color: \"#1a1a1a\",',\n \" },\",\n \" message: {\",\n \" fontSize: 13,\",\n ' color: \"#666\",',\n \" marginTop: 2,\",\n \" },\",\n \" closeButton: {\",\n \" padding: 12,\",\n \" },\",\n \" closeButtonText: {\",\n \" fontSize: 14,\",\n ' color: \"#999\",',\n \" },\",\n \"});\",\n \"\",\n \"export const InAppNotification = InAppNotificationProvider;\"\n ),\n },\n ],\n layoutProviders: [\"<InAppNotification>\"],\n layoutImports: [\n 'import { InAppNotification } from \"../modules/notification/InAppNotification\";',\n ],\n};\n\nexport default notificationModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst permissionModule: ModuleDef = {\n id: \"permission\",\n name: \"用户权限管理\",\n description: \"权限请求/检查封装\",\n defaultChecked: false,\n dependencies: {\n \"expo-image-picker\": \"~17.0.11\",\n \"expo-camera\": \"~17.0.10\",\n \"expo-location\": \"~19.0.8\",\n },\n devDependencies: {},\n files: [\n {\n path: \"modules/permission/index.ts\",\n content: lines(\n 'import * as ImagePicker from \"expo-image-picker\";',\n 'import * as Camera from \"expo-camera\";',\n 'import * as Location from \"expo-location\";',\n 'import { Alert, Platform } from \"react-native\";',\n \"\",\n 'export type PermissionStatus = \"granted\" | \"denied\" | \"undetermined\";',\n \"\",\n \"export interface PermissionResult {\",\n \" status: PermissionStatus;\",\n \" canAskAgain: boolean;\",\n \"}\",\n \"\",\n \"export async function checkCameraPermission(): Promise<PermissionResult> {\",\n \" const { status, canAskAgain } = await Camera.getCameraPermissionsAsync();\",\n \" return { status: status as PermissionStatus, canAskAgain };\",\n \"}\",\n \"\",\n \"export async function requestCameraPermission(): Promise<PermissionResult> {\",\n \" const { status, canAskAgain } = await Camera.requestCameraPermissionsAsync();\",\n \" return { status: status as PermissionStatus, canAskAgain };\",\n \"}\",\n \"\",\n \"export async function checkPhotoLibraryPermission(): Promise<PermissionResult> {\",\n \" const { status, canAskAgain } =\",\n \" await ImagePicker.getMediaLibraryPermissionsAsync();\",\n \" return { status: status as PermissionStatus, canAskAgain };\",\n \"}\",\n \"\",\n \"export async function requestPhotoLibraryPermission(): Promise<PermissionResult> {\",\n \" const { status, canAskAgain } =\",\n \" await ImagePicker.requestMediaLibraryPermissionsAsync();\",\n \" return { status: status as PermissionStatus, canAskAgain };\",\n \"}\",\n \"\",\n \"export async function checkLocationPermission(): Promise<PermissionResult> {\",\n \" const { status, canAskAgain } = await Location.getForegroundPermissionsAsync();\",\n \" return { status: status as PermissionStatus, canAskAgain };\",\n \"}\",\n \"\",\n \"export async function requestLocationPermission(): Promise<PermissionResult> {\",\n \" const { status, canAskAgain } =\",\n \" await Location.requestForegroundPermissionsAsync();\",\n \" return { status: status as PermissionStatus, canAskAgain };\",\n \"}\",\n \"\",\n \"export async function requestBackgroundLocationPermission(): Promise<PermissionResult> {\",\n \" const { status, canAskAgain } =\",\n \" await Location.requestBackgroundPermissionsAsync();\",\n \" return { status: status as PermissionStatus, canAskAgain };\",\n \"}\",\n \"\",\n \"export async function requestPermissionWithAlert(\",\n \" permissionName: string,\",\n \" requestFn: () => Promise<PermissionResult>,\",\n \" onGranted?: () => void,\",\n \" onDenied?: () => void\",\n \"): Promise<PermissionResult> {\",\n \" const result = await requestFn();\",\n \"\",\n ' if (result.status === \"granted\") {',\n \" onGranted?.();\",\n \" } else {\",\n \" const message = result.canAskAgain\",\n ' ? `您需要授予${permissionName}权限才能使用此功能`',\n ' : `${permissionName}权限已被拒绝,请在系统设置中手动开启`;',\n \"\",\n ' Alert.alert(\"需要权限\", message, [',\n ' { text: \"取消\", style: \"cancel\", onPress: onDenied },',\n \" ...(result.canAskAgain\",\n \" ? [\",\n \" {\",\n ' text: \"再次请求\",',\n ' style: \"default\" as const,',\n \" onPress: () => requestFn(),\",\n \" },\",\n \" ]\",\n \" : []),\",\n \" ]);\",\n \" }\",\n \"\",\n \" return result;\",\n \"}\",\n \"\",\n \"export async function pickImageWithPermission(): Promise<ImagePicker.ImagePickerResult | null> {\",\n \" const permission = await requestPhotoLibraryPermission();\",\n ' if (permission.status !== \"granted\") {',\n ' Alert.alert(\"需要权限\", \"请授予相册访问权限\");',\n \" return null;\",\n \" }\",\n \"\",\n \" const result = await ImagePicker.launchImageLibraryAsync({\",\n \" mediaTypes: ImagePicker.MediaTypeOptions.Images,\",\n \" allowsEditing: true,\",\n \" aspect: [1, 1],\",\n \" quality: 0.8,\",\n \" });\",\n \"\",\n \" return result;\",\n \"}\",\n \"\",\n \"export async function takePhotoWithPermission(): Promise<ImagePicker.ImagePickerResult | null> {\",\n \" const permission = await requestCameraPermission();\",\n ' if (permission.status !== \"granted\") {',\n ' Alert.alert(\"需要权限\", \"请授予相机访问权限\");',\n \" return null;\",\n \" }\",\n \"\",\n \" const result = await ImagePicker.launchCameraAsync({\",\n \" allowsEditing: true,\",\n \" aspect: [1, 1],\",\n \" quality: 0.8,\",\n \" });\",\n \"\",\n \" return result;\",\n \"}\"\n ),\n },\n ],\n appConfig: {\n plugins: [\n [\n \"expo-camera\",\n {\n cameraPermission: \"Allow $(PRODUCT_NAME) to access your camera\",\n },\n ],\n [\n \"expo-image-picker\",\n {\n photosPermission: \"Allow $(PRODUCT_NAME) to access your photos\",\n },\n ],\n [\n \"expo-location\",\n {\n locationAlwaysAndWhenInUsePermission:\n \"Allow $(PRODUCT_NAME) to use your location\",\n },\n ],\n ],\n },\n};\n\nexport default permissionModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst bottomSheetModule: ModuleDef = {\n id: \"bottom-sheet\",\n name: \"Bottom Sheet\",\n description: \"@gorhom/bottom-sheet\",\n defaultChecked: false,\n dependencies: {\n \"@gorhom/bottom-sheet\": \"^5.1.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"components/AppBottomSheet.tsx\",\n content: lines(\n 'import React, { useCallback, useRef } from \"react\";',\n 'import { View, Text, StyleSheet } from \"react-native\";',\n 'import BottomSheet, {',\n \" BottomSheetBackdrop,\",\n \" BottomSheetView,\",\n \" type BottomSheetBackdropProps,\",\n \" type BottomSheetProps,\",\n '} from \"@gorhom/bottom-sheet\";',\n \"\",\n \"interface AppBottomSheetProps extends Omit<BottomSheetProps, \\\"children\\\"> {\",\n \" title?: string;\",\n \" children: React.ReactNode;\",\n \" onClose?: () => void;\",\n \"}\",\n \"\",\n \"export const AppBottomSheet = React.forwardRef<BottomSheet, AppBottomSheetProps>(\",\n \" ({ title, children, onClose, snapPoints = [\\\"50%\\\"], ...rest }, ref) => {\",\n \" const localRef = useRef<BottomSheet>(null);\",\n \" const sheetRef = (ref as React.RefObject<BottomSheet>) || localRef;\",\n \"\",\n \" const renderBackdrop = useCallback(\",\n \" (props: BottomSheetBackdropProps) => (\",\n \" <BottomSheetBackdrop\",\n \" {...props}\",\n \" disappearsOnIndex={-1}\",\n \" appearsOnIndex={0}\",\n ' pressBehavior=\"close\"',\n \" />\",\n \" ),\",\n \" []\",\n \" );\",\n \"\",\n \" const handleSheetChange = useCallback(\",\n \" (index: number) => {\",\n \" if (index === -1 && onClose) {\",\n \" onClose();\",\n \" }\",\n \" },\",\n \" [onClose]\",\n \" );\",\n \"\",\n \" return (\",\n \" <BottomSheet\",\n \" ref={sheetRef}\",\n \" index={-1}\",\n \" snapPoints={snapPoints}\",\n \" backdropComponent={renderBackdrop}\",\n \" onChange={handleSheetChange}\",\n \" enablePanDownToClose\",\n \" backgroundStyle={styles.background}\",\n \" handleIndicatorStyle={styles.handleIndicator}\",\n \" {...rest}\",\n \" >\",\n \" <BottomSheetView style={styles.content}>\",\n \" {title && <Text style={styles.title}>{title}</Text>}\",\n \" {children}\",\n \" </BottomSheetView>\",\n \" </BottomSheet>\",\n \" );\",\n \" }\",\n \");\",\n \"\",\n 'AppBottomSheet.displayName = \"AppBottomSheet\";',\n \"\",\n \"const styles = StyleSheet.create({\",\n \" background: {\",\n \" borderRadius: 16,\",\n \" },\",\n \" handleIndicator: {\",\n ' backgroundColor: \"#d1d5db\",',\n \" width: 40,\",\n \" },\",\n \" content: {\",\n \" paddingHorizontal: 16,\",\n \" paddingBottom: 24,\",\n \" },\",\n \" title: {\",\n \" fontSize: 18,\",\n ' fontWeight: \"600\",',\n ' color: \"#1a1a1a\",',\n \" marginBottom: 16,\",\n \" },\",\n \"});\",\n \"\",\n \"export default AppBottomSheet;\"\n ),\n },\n ],\n layoutProviders: [\"<BottomSheetModalProvider>\"],\n layoutImports: [\n 'import { BottomSheetModalProvider } from \"@gorhom/bottom-sheet\";',\n ],\n};\n\nexport default bottomSheetModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst flashlistModule: ModuleDef = {\n id: \"flashlist\",\n name: \"FlashList\",\n description: \"@shopify/flash-list\",\n defaultChecked: false,\n dependencies: {\n \"@shopify/flash-list\": \"2.0.2\",\n },\n devDependencies: {},\n files: [\n {\n path: \"components/AppFlashList.tsx\",\n content: lines(\n 'import React, { useCallback } from \"react\";',\n 'import { View, Text, StyleSheet, type ListRenderItemInfo } from \"react-native\";',\n 'import {',\n \" FlashList,\",\n \" type FlashListProps,\",\n '} from \"@shopify/flash-list\";',\n \"\",\n \"export interface ListItem {\",\n \" id: string;\",\n \" [key: string]: unknown;\",\n \"}\",\n \"\",\n \"interface AppFlashListProps<T extends ListItem>\",\n \" extends Omit<FlashListProps<T>, \\\"renderItem\\\" | \\\"estimatedItemSize\\\"> {\",\n \" renderItem: (info: ListRenderItemInfo<T>) => React.ReactElement | null;\",\n \" estimatedItemSize?: number;\",\n \" ListEmptyComponent?: React.ReactElement;\",\n \" isLoading?: boolean;\",\n \" LoadingComponent?: React.ReactElement;\",\n \"}\",\n \"\",\n \"export function AppFlashList<T extends ListItem>({\",\n \" data,\",\n \" renderItem,\",\n \" estimatedItemSize = 50,\",\n \" ListEmptyComponent,\",\n \" isLoading,\",\n \" LoadingComponent,\",\n \" ...rest\",\n \"}: AppFlashListProps<T>) {\",\n \" const defaultEmptyComponent = React.useMemo(\",\n \" () => (\",\n \" <View style={styles.emptyContainer}>\",\n ' <Text style={styles.emptyText}>暂无数据</Text>',\n \" </View>\",\n \" ),\",\n \" []\",\n \" );\",\n \"\",\n \" const defaultLoadingComponent = React.useMemo(\",\n \" () => (\",\n \" <View style={styles.loadingContainer}>\",\n ' <Text style={styles.loadingText}>加载中...</Text>',\n \" </View>\",\n \" ),\",\n \" []\",\n \" );\",\n \"\",\n \" if (isLoading) {\",\n \" return LoadingComponent ?? defaultLoadingComponent;\",\n \" }\",\n \"\",\n \" return (\",\n \" <FlashList\",\n \" data={data}\",\n \" renderItem={renderItem}\",\n \" estimatedItemSize={estimatedItemSize}\",\n \" ListEmptyComponent={ListEmptyComponent ?? defaultEmptyComponent}\",\n \" drawDistance={200}\",\n \" {...rest}\",\n \" />\",\n \" );\",\n \"}\",\n \"\",\n \"const styles = StyleSheet.create({\",\n \" emptyContainer: {\",\n \" flex: 1,\",\n \" justifyContent: \\\"center\\\",\",\n \" alignItems: \\\"center\\\",\",\n \" paddingVertical: 40,\",\n \" },\",\n \" emptyText: {\",\n \" fontSize: 16,\",\n ' color: \"#999\",',\n \" },\",\n \" loadingContainer: {\",\n \" flex: 1,\",\n \" justifyContent: \\\"center\\\",\",\n \" alignItems: \\\"center\\\",\",\n \" paddingVertical: 40,\",\n \" },\",\n \" loadingText: {\",\n \" fontSize: 16,\",\n ' color: \"#999\",',\n \" },\",\n \"});\",\n \"\",\n \"export default AppFlashList;\"\n ),\n },\n ],\n};\n\nexport default flashlistModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst uiReusablesModule: ModuleDef = {\n id: \"ui-reusables\",\n name: \"reactnative.reusables UI\",\n description: \"预置 UI 组件 (Button, AlertDialog, Card, Input, Label, Text, Separator)\",\n defaultChecked: false,\n dependencies: {\n \"class-variance-authority\": \"^0.7.1\",\n \"clsx\": \"^2.1.1\",\n \"tailwind-merge\": \"^2.6.0\",\n \"@rn-primitives/slot\": \"^1.1.0\",\n \"@rn-primitives/types\": \"^1.1.0\",\n \"@rn-primitives/label\": \"^1.1.0\",\n \"@rn-primitives/separator\": \"^1.1.0\",\n \"@rn-primitives/alert-dialog\": \"^1.1.0\",\n \"@rn-primitives/portal\": \"^1.1.0\",\n \"react-native-svg\": \"^15.8.0\",\n },\n devDependencies: {},\n files: [\n // ─── components/ui/text.tsx ──────────────────────────────────────────\n {\n path: \"components/ui/text.tsx\",\n content: lines(\n 'import { cn } from \"@/lib/utils\";',\n 'import { Slot } from \"@rn-primitives/slot\";',\n 'import { cva, type VariantProps } from \"class-variance-authority\";',\n 'import * as React from \"react\";',\n 'import { Platform, Text as RNText, type Role } from \"react-native\";',\n \"\",\n \"const textVariants = cva(\",\n \" cn(\",\n ' \"text-foreground text-base\",',\n \" Platform.select({\",\n ' web: \"select-text\",',\n \" })\",\n \" ),\",\n \" {\",\n \" variants: {\",\n \" variant: {\",\n \" default: \\\"\\\",\",\n \" h1: cn(\",\n ' \"text-center text-4xl font-extrabold tracking-tight\",',\n \" Platform.select({ web: \\\"scroll-m-20 text-balance\\\" })\",\n \" ),\",\n \" h2: cn(\",\n ' \"border-border border-b pb-2 text-3xl font-semibold tracking-tight\",',\n ' Platform.select({ web: \"scroll-m-20 first:mt-0\" })',\n \" ),\",\n \" h3: cn(\",\n ' \"text-2xl font-semibold tracking-tight\",',\n ' Platform.select({ web: \"scroll-m-20\" })',\n \" ),\",\n \" h4: cn(\",\n ' \"text-xl font-semibold tracking-tight\",',\n ' Platform.select({ web: \"scroll-m-20\" })',\n \" ),\",\n ' p: \"mt-3 leading-7 sm:mt-6\",',\n ' blockquote: \"mt-4 border-l-2 pl-3 italic sm:mt-6 sm:pl-6\",',\n \" code: cn(\",\n ' \"bg-muted relative rounded px-[0.3rem] py-[0.2rem] font-mono text-sm font-semibold\"',\n \" ),\",\n ' lead: \"text-muted-foreground text-xl\",',\n ' large: \"text-lg font-semibold\",',\n ' small: \"text-sm font-medium leading-none\",',\n ' muted: \"text-muted-foreground text-sm\",',\n \" },\",\n \" },\",\n \" defaultVariants: {\",\n \" variant: \\\"default\\\",\",\n \" },\",\n \" }\",\n \");\",\n \"\",\n \"type TextVariantProps = VariantProps<typeof textVariants>;\",\n \"\",\n \"type TextVariant = NonNullable<TextVariantProps[\\\"variant\\\"]>;\",\n \"\",\n \"const ROLE: Partial<Record<TextVariant, Role>> = {\",\n ' h1: \"heading\",',\n ' h2: \"heading\",',\n ' h3: \"heading\",',\n ' h4: \"heading\",',\n \" blockquote: Platform.select({ web: \\\"blockquote\\\" as Role }),\",\n \" code: Platform.select({ web: \\\"code\\\" as Role }),\",\n \"};\",\n \"\",\n \"const ARIA_LEVEL: Partial<Record<TextVariant, string>> = {\",\n ' h1: \"1\",',\n ' h2: \"2\",',\n ' h3: \"3\",',\n ' h4: \"4\",',\n \"};\",\n \"\",\n \"const TextClassContext = React.createContext<string | undefined>(undefined);\",\n \"\",\n \"function Text({\",\n \" className,\",\n \" asChild = false,\",\n ' variant = \"default\",',\n \" ...props\",\n \"}: React.ComponentProps<typeof RNText> &\",\n \" React.RefAttributes<typeof RNText> &\",\n \" TextVariantProps & { asChild?: boolean }) {\",\n \" const textClass = React.useContext(TextClassContext);\",\n \" const Component = asChild ? Slot : RNText;\",\n \" return (\",\n \" <Component\",\n \" className={cn(textVariants({ variant }), textClass, className)}\",\n \" role={variant ? ROLE[variant] : undefined}\",\n \" aria-level={variant ? ARIA_LEVEL[variant] : undefined}\",\n \" {...props}\",\n \" />\",\n \" );\",\n \"}\",\n \"\",\n \"export { Text, TextClassContext };\",\n \"\"\n ),\n },\n\n // ─── components/ui/button.tsx ────────────────────────────────────────\n {\n path: \"components/ui/button.tsx\",\n content: lines(\n 'import { TextClassContext } from \"@/components/ui/text\";',\n 'import { cn } from \"@/lib/utils\";',\n 'import { cva, type VariantProps } from \"class-variance-authority\";',\n 'import { Platform, Pressable } from \"react-native\";',\n \"\",\n \"const buttonVariants = cva(\",\n \" cn(\",\n ' \"group shrink-0 flex-row items-center justify-center gap-2 rounded-md shadow-none\",',\n \" Platform.select({\",\n ' web: \"focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive whitespace-nowrap outline-none transition-all focus-visible:ring-[3px] disabled:pointer-events-none [&_svg:not([class*=\\'size-\\'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0\",',\n \" })\",\n \" ),\",\n \" {\",\n \" variants: {\",\n \" variant: {\",\n \" default: cn(\",\n ' \"bg-primary active:bg-primary/90 shadow-sm shadow-black/5\",',\n ' Platform.select({ web: \"hover:bg-primary/90\" })',\n \" ),\",\n \" destructive: cn(\",\n ' \"bg-destructive active:bg-destructive/90 dark:bg-destructive/60 shadow-sm shadow-black/5\",',\n \" Platform.select({\",\n ' web: \"hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40\",',\n \" })\",\n \" ),\",\n \" outline: cn(\",\n ' \"border-border bg-background active:bg-accent dark:bg-input/30 dark:border-input dark:active:bg-input/50 border shadow-sm shadow-black/5\",',\n \" Platform.select({\",\n ' web: \"hover:bg-accent dark:hover:bg-input/50\",',\n \" })\",\n \" ),\",\n \" secondary: cn(\",\n ' \"bg-secondary active:bg-secondary/80 shadow-sm shadow-black/5\",',\n ' Platform.select({ web: \"hover:bg-secondary/80\" })',\n \" ),\",\n \" ghost: cn(\",\n ' \"active:bg-accent dark:active:bg-accent/50\",',\n ' Platform.select({ web: \"hover:bg-accent dark:hover:bg-accent/50\" })',\n \" ),\",\n ' link: \"\",',\n \" },\",\n \" size: {\",\n \" default: cn(\",\n ' \"h-10 px-4 py-2 sm:h-9\",',\n ' Platform.select({ web: \"has-[>svg]:px-3\" })',\n \" ),\",\n \" sm: cn(\",\n ' \"h-9 gap-1.5 rounded-md px-3 sm:h-8\",',\n ' Platform.select({ web: \"has-[>svg]:px-2.5\" })',\n \" ),\",\n \" lg: cn(\",\n ' \"h-11 rounded-md px-6 sm:h-10\",',\n ' Platform.select({ web: \"has-[>svg]:px-4\" })',\n \" ),\",\n ' icon: \"h-10 w-10 sm:h-9 sm:w-9\",',\n \" },\",\n \" },\",\n \" defaultVariants: {\",\n ' variant: \"default\",',\n ' size: \"default\",',\n \" },\",\n \" }\",\n \");\",\n \"\",\n \"const buttonTextVariants = cva(\",\n \" cn(\",\n ' \"text-foreground text-sm font-medium\",',\n ' Platform.select({ web: \"pointer-events-none transition-colors\" })',\n \" ),\",\n \" {\",\n \" variants: {\",\n \" variant: {\",\n ' default: \"text-primary-foreground\",',\n ' destructive: \"text-white\",',\n \" outline: cn(\",\n ' \"group-active:text-accent-foreground\",',\n ' Platform.select({ web: \"group-hover:text-accent-foreground\" })',\n \" ),\",\n ' secondary: \"text-secondary-foreground\",',\n ' ghost: \"group-active:text-accent-foreground\",',\n \" link: cn(\",\n ' \"text-primary group-active:underline\",',\n ' Platform.select({ web: \"underline-offset-4 hover:underline group-hover:underline\" })',\n \" ),\",\n \" },\",\n \" size: {\",\n ' default: \"\",',\n ' sm: \"\",',\n ' lg: \"\",',\n ' icon: \"\",',\n \" },\",\n \" },\",\n \" defaultVariants: {\",\n ' variant: \"default\",',\n ' size: \"default\",',\n \" },\",\n \" }\",\n \");\",\n \"\",\n \"type ButtonProps = React.ComponentProps<typeof Pressable> &\",\n \" React.RefAttributes<typeof Pressable> &\",\n \" VariantProps<typeof buttonVariants>;\",\n \"\",\n \"function Button({ className, variant, size, ...props }: ButtonProps) {\",\n \" return (\",\n \" <TextClassContext.Provider value={buttonTextVariants({ variant, size })}>\",\n \" <Pressable\",\n ' className={cn(props.disabled && \"opacity-50\", buttonVariants({ variant, size }), className)}',\n ' role=\"button\"',\n \" {...props}\",\n \" />\",\n \" </TextClassContext.Provider>\",\n \" );\",\n \"}\",\n \"\",\n \"export { Button, buttonTextVariants, buttonVariants };\",\n \"export type { ButtonProps };\",\n \"\"\n ),\n },\n\n // ─── components/ui/input.tsx ──────────────────────────────────────────\n {\n path: \"components/ui/input.tsx\",\n content: lines(\n 'import { cn } from \"@/lib/utils\";',\n 'import { Platform, TextInput } from \"react-native\";',\n \"\",\n \"function Input({\",\n \" className,\",\n \" ...props\",\n \"}: React.ComponentProps<typeof TextInput> & React.RefAttributes<TextInput>) {\",\n \" return (\",\n \" <TextInput\",\n \" className={cn(\",\n ' \"dark:bg-input/30 border-input bg-background text-foreground flex h-10 w-full min-w-0 flex-row items-center rounded-md border px-3 py-1 text-base leading-5 shadow-sm shadow-black/5 sm:h-9\",',\n \" props.editable === false &&\",\n \" cn(\",\n ' \"opacity-50\",',\n ' Platform.select({ web: \"disabled:pointer-events-none disabled:cursor-not-allowed\" })',\n \" ),\",\n \" Platform.select({\",\n \" web: cn(\",\n ' \"placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground outline-none transition-[color,box-shadow] md:text-sm\",',\n ' \"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]\",',\n ' \"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive\"',\n \" ),\",\n ' native: \"placeholder:text-muted-foreground/50\",',\n \" }),\",\n \" className\",\n \" )}\",\n \" {...props}\",\n \" />\",\n \" );\",\n \"}\",\n \"\",\n \"export { Input };\",\n \"\"\n ),\n },\n\n // ─── components/ui/label.tsx ─────────────────────────────────────────\n {\n path: \"components/ui/label.tsx\",\n content: lines(\n 'import { cn } from \"@/lib/utils\";',\n 'import * as LabelPrimitive from \"@rn-primitives/label\";',\n 'import { Platform } from \"react-native\";',\n \"\",\n \"function Label({\",\n \" className,\",\n \" onPress,\",\n \" onLongPress,\",\n \" onPressIn,\",\n \" onPressOut,\",\n \" disabled,\",\n \" ...props\",\n \"}: React.ComponentProps<typeof LabelPrimitive.Text>) {\",\n \" return (\",\n \" <LabelPrimitive.Root\",\n \" className={cn(\",\n ' \"flex select-none flex-row items-center gap-2\",',\n \" Platform.select({\",\n ' web: \"cursor-default leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-50 group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50\",',\n \" }),\",\n ' disabled && \"opacity-50\"',\n \" )}\",\n \" onPress={onPress}\",\n \" onLongPress={onLongPress}\",\n \" onPressIn={onPressIn}\",\n \" onPressOut={onPressOut}\",\n \" disabled={disabled}\",\n \" >\",\n \" <LabelPrimitive.Text\",\n \" className={cn(\",\n ' \"text-foreground text-sm font-medium\",',\n ' Platform.select({ web: \"leading-none\" }),',\n \" className\",\n \" )}\",\n \" {...props}\",\n \" />\",\n \" </LabelPrimitive.Root>\",\n \" );\",\n \"}\",\n \"\",\n \"export { Label };\",\n \"\"\n ),\n },\n\n // ─── components/ui/card.tsx ──────────────────────────────────────────\n {\n path: \"components/ui/card.tsx\",\n content: lines(\n 'import { Text, TextClassContext } from \"@/components/ui/text\";',\n 'import { cn } from \"@/lib/utils\";',\n 'import { View } from \"react-native\";',\n \"\",\n \"function Card({\",\n \" className,\",\n \" ...props\",\n \"}: React.ComponentProps<typeof View> & React.RefAttributes<View>) {\",\n \" return (\",\n ' <TextClassContext.Provider value=\"text-card-foreground\">',\n \" <View\",\n \" className={cn(\",\n ' \"bg-card border-border flex flex-col gap-6 rounded-xl border py-6 shadow-sm shadow-black/5\",',\n \" className\",\n \" )}\",\n \" {...props}\",\n \" />\",\n \" </TextClassContext.Provider>\",\n \" );\",\n \"}\",\n \"\",\n \"function CardHeader({\",\n \" className,\",\n \" ...props\",\n \"}: React.ComponentProps<typeof View> & React.RefAttributes<View>) {\",\n \" return (\",\n ' <View className={cn(\"flex flex-col gap-1.5 px-6\", className)} {...props} />',\n \" );\",\n \"}\",\n \"\",\n \"function CardTitle({\",\n \" className,\",\n \" ref,\",\n \" ...props\",\n \"}: React.ComponentProps<typeof Text> & React.RefAttributes<typeof Text>) {\",\n \" return (\",\n \" <Text\",\n \" ref={ref}\",\n ' role=\"heading\"',\n \" aria-level={3}\",\n ' className={cn(\"font-semibold leading-none\", className)}',\n \" {...props}\",\n \" />\",\n \" );\",\n \"}\",\n \"\",\n \"function CardDescription({\",\n \" className,\",\n \" ...props\",\n \"}: React.ComponentProps<typeof Text> & React.RefAttributes<typeof Text>) {\",\n \" return (\",\n ' <Text className={cn(\"text-muted-foreground text-sm\", className)} {...props} />',\n \" );\",\n \"}\",\n \"\",\n \"function CardContent({\",\n \" className,\",\n \" ...props\",\n \"}: React.ComponentProps<typeof View> & React.RefAttributes<View>) {\",\n \" return (\",\n ' <View className={cn(\"px-6\", className)} {...props} />',\n \" );\",\n \"}\",\n \"\",\n \"function CardFooter({\",\n \" className,\",\n \" ...props\",\n \"}: React.ComponentProps<typeof View> & React.RefAttributes<View>) {\",\n \" return (\",\n ' <View className={cn(\"flex flex-row items-center px-6\", className)} {...props} />',\n \" );\",\n \"}\",\n \"\",\n \"export { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle };\",\n \"\"\n ),\n },\n\n // ─── components/ui/separator.tsx ──────────────────────────────────────\n {\n path: \"components/ui/separator.tsx\",\n content: lines(\n 'import { cn } from \"@/lib/utils\";',\n 'import * as SeparatorPrimitive from \"@rn-primitives/separator\";',\n \"\",\n \"function Separator({\",\n \" className,\",\n ' orientation = \"horizontal\",',\n \" decorative = true,\",\n \" ...props\",\n \"}: React.ComponentProps<typeof SeparatorPrimitive.Root>) {\",\n \" return (\",\n \" <SeparatorPrimitive.Root\",\n \" decorative={decorative}\",\n \" orientation={orientation}\",\n \" className={cn(\",\n ' \"bg-border shrink-0\",',\n ' orientation === \"horizontal\" ? \"h-[1px] w-full\" : \"h-full w-[1px]\",',\n \" className\",\n \" )}\",\n \" {...props}\",\n \" />\",\n \" );\",\n \"}\",\n \"\",\n \"export { Separator };\",\n \"\"\n ),\n },\n\n // ─── components/ui/alert-dialog.tsx ──────────────────────────────────\n {\n path: \"components/ui/alert-dialog.tsx\",\n content: lines(\n 'import { buttonTextVariants, buttonVariants } from \"@/components/ui/button\";',\n 'import { TextClassContext } from \"@/components/ui/text\";',\n 'import { cn } from \"@/lib/utils\";',\n 'import * as AlertDialogPrimitive from \"@rn-primitives/alert-dialog\";',\n 'import * as React from \"react\";',\n 'import { Platform, View, type ViewProps } from \"react-native\";',\n \"\",\n \"const AlertDialog = AlertDialogPrimitive.Root;\",\n \"\",\n \"const AlertDialogTrigger = AlertDialogPrimitive.Trigger;\",\n \"\",\n \"const AlertDialogPortal = AlertDialogPrimitive.Portal;\",\n \"\",\n \"function AlertDialogOverlay({\",\n \" className,\",\n \" ...props\",\n \"}: Omit<React.ComponentProps<typeof AlertDialogPrimitive.Overlay>, \\\"asChild\\\"> & {\",\n \" children?: React.ReactNode;\",\n \"}) {\",\n \" return (\",\n \" <AlertDialogPrimitive.Overlay\",\n \" className={cn(\",\n ' \"absolute bottom-0 left-0 right-0 top-0 z-50 flex items-center justify-center bg-black/50 p-2\",',\n \" Platform.select({\",\n ' web: \"animate-in fade-in-0 fixed\",',\n \" }),\",\n \" className\",\n \" )}\",\n \" {...props}\",\n \" />\",\n \" );\",\n \"}\",\n \"\",\n \"function AlertDialogContent({\",\n \" className,\",\n \" portalHost,\",\n \" ...props\",\n \"}: React.ComponentProps<typeof AlertDialogPrimitive.Content> & {\",\n \" portalHost?: string;\",\n \"}) {\",\n \" return (\",\n \" <AlertDialogPortal hostName={portalHost}>\",\n \" <AlertDialogOverlay>\",\n \" <AlertDialogPrimitive.Content\",\n \" className={cn(\",\n ' \"bg-background border-border z-50 flex w-full max-w-[calc(100%-2rem)] flex-col gap-4 rounded-lg border p-6 shadow-lg shadow-black/5 sm:max-w-lg\",',\n \" Platform.select({\",\n ' web: \"animate-in fade-in-0 zoom-in-95 duration-200\",',\n \" }),\",\n \" className\",\n \" )}\",\n \" {...props}\",\n \" />\",\n \" </AlertDialogOverlay>\",\n \" </AlertDialogPortal>\",\n \" );\",\n \"}\",\n \"\",\n \"function AlertDialogHeader({ className, ...props }: ViewProps) {\",\n \" return (\",\n ' <TextClassContext.Provider value=\"text-center sm:text-left\">',\n ' <View className={cn(\"flex flex-col gap-2\", className)} {...props} />',\n \" </TextClassContext.Provider>\",\n \" );\",\n \"}\",\n \"\",\n \"function AlertDialogFooter({ className, ...props }: ViewProps) {\",\n \" return (\",\n ' <View className={cn(\"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\", className)} {...props} />',\n \" );\",\n \"}\",\n \"\",\n \"function AlertDialogTitle({\",\n \" className,\",\n \" ...props\",\n \"}: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {\",\n \" return (\",\n \" <AlertDialogPrimitive.Title\",\n ' className={cn(\"text-foreground text-lg font-semibold\", className)}',\n \" {...props}\",\n \" />\",\n \" );\",\n \"}\",\n \"\",\n \"function AlertDialogDescription({\",\n \" className,\",\n \" ...props\",\n \"}: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {\",\n \" return (\",\n \" <AlertDialogPrimitive.Description\",\n ' className={cn(\"text-muted-foreground text-sm\", className)}',\n \" {...props}\",\n \" />\",\n \" );\",\n \"}\",\n \"\",\n \"function AlertDialogAction({\",\n \" className,\",\n \" ...props\",\n \"}: React.ComponentProps<typeof AlertDialogPrimitive.Action>) {\",\n \" return (\",\n \" <TextClassContext.Provider value={buttonTextVariants({ className })}>\",\n ' <AlertDialogPrimitive.Action className={cn(buttonVariants(), className)} {...props} />',\n \" </TextClassContext.Provider>\",\n \" );\",\n \"}\",\n \"\",\n \"function AlertDialogCancel({\",\n \" className,\",\n \" ...props\",\n \"}: React.ComponentProps<typeof AlertDialogPrimitive.Cancel>) {\",\n \" return (\",\n \" <TextClassContext.Provider value={buttonTextVariants({ className, variant: \\\"outline\\\" })}>\",\n \" <AlertDialogPrimitive.Cancel\",\n \" className={cn(buttonVariants({ variant: \\\"outline\\\" }), className)} {...props} />\",\n \" </TextClassContext.Provider>\",\n \" );\",\n \"}\",\n \"\",\n \"export {\",\n \" AlertDialog,\",\n \" AlertDialogAction,\",\n \" AlertDialogCancel,\",\n \" AlertDialogContent,\",\n \" AlertDialogDescription,\",\n \" AlertDialogFooter,\",\n \" AlertDialogHeader,\",\n \" AlertDialogOverlay,\",\n \" AlertDialogPortal,\",\n \" AlertDialogTitle,\",\n \" AlertDialogTrigger,\",\n \"};\",\n \"\"\n ),\n },\n ],\n};\n\nexport default uiReusablesModule;\n","import type { ModuleDef } from \"../types\";\nimport networkModule from \"./network\";\nimport stateModule from \"./state\";\nimport storageModule from \"./storage\";\nimport paymentModule from \"./payment\";\nimport formModule from \"./form\";\nimport imageModule from \"./image\";\nimport videoModule from \"./video\";\nimport authGoogleModule from \"./auth-google\";\nimport authFacebookModule from \"./auth-facebook\";\nimport authAppleModule from \"./auth-apple\";\nimport webviewModule from \"./webview\";\nimport i18nModule from \"./i18n\";\nimport animationModule from \"./animation\";\nimport otaModule from \"./ota\";\nimport notificationModule from \"./notification\";\nimport permissionModule from \"./permission\";\nimport bottomSheetModule from \"./bottom-sheet\";\nimport flashlistModule from \"./flashlist\";\nimport uiReusablesModule from \"./ui-reusables\";\n\n/** All available modules, in display order */\nexport const modules: ModuleDef[] = [\n // P0 — Core (default checked)\n networkModule,\n stateModule,\n storageModule,\n // Note: expo-router and nativewind are always included in the base template\n\n // P1 — Feature modules\n paymentModule,\n formModule,\n imageModule,\n videoModule,\n authGoogleModule,\n authFacebookModule,\n authAppleModule,\n webviewModule,\n i18nModule,\n animationModule,\n\n // P2 — Additional modules\n otaModule,\n notificationModule,\n permissionModule,\n bottomSheetModule,\n flashlistModule,\n uiReusablesModule,\n];\n\n/**\n * Get a module by its ID.\n */\nexport function getModuleById(id: string): ModuleDef | undefined {\n return modules.find((m) => m.id === id);\n}\n\n/**\n * Get all module IDs.\n */\nexport function getModuleIds(): string[] {\n return modules.map((m) => m.id);\n}\n\n/**\n * Get modules by a list of IDs.\n */\nexport function getModulesByIds(ids: string[]): ModuleDef[] {\n return ids\n .map((id) => getModuleById(id))\n .filter((m): m is ModuleDef => m !== undefined);\n}\n","import type { TemplateFile } from \"../types\";\n\n/**\n * Generate base template files that are included in every project.\n * Follows reactnativereusables official installation guide.\n * https://reactnativereusables.com/docs/installation/manual\n */\nexport function generateBaseTemplates(projectName: string): TemplateFile[] {\n return [\n // ─── app/_layout.tsx ────────────────────────────────────────────────\n {\n path: \"app/_layout.tsx\",\n content: `import \"../global.css\";\nimport { DarkTheme, DefaultTheme, ThemeProvider } from \"@react-navigation/native\";\nimport { useFonts } from \"expo-font\";\nimport { Stack } from \"expo-router\";\nimport * as SplashScreen from \"expo-splash-screen\";\nimport { useEffect } from \"react\";\nimport { useColorScheme } from \"react-native\";\nimport { PortalHost } from \"@rn-primitives/portal\";\nimport { NAV_THEME } from \"@/lib/theme\";\n\nSplashScreen.preventAutoHideAsync();\n\nexport default function RootLayout() {\n const colorScheme = useColorScheme();\n const [loaded] = useFonts({\n NunitoBold: require(\"../assets/fonts/Nunito-Bold.ttf\"),\n });\n\n useEffect(() => {\n if (loaded) {\n SplashScreen.hideAsync();\n }\n }, [loaded]);\n\n if (!loaded) {\n return null;\n }\n\n return (\n <ThemeProvider value={colorScheme === \"dark\" ? NAV_THEME.dark : NAV_THEME.light}>\n <Stack>\n <Stack.Screen name=\"(tabs)\" options={{ headerShown: false }} />\n <Stack.Screen name=\"+not-found\" />\n </Stack>\n <PortalHost />\n </ThemeProvider>\n );\n}\n`,\n },\n\n // ─── app/(tabs)/_layout.tsx ─────────────────────────────────────────\n {\n path: \"app/(tabs)/_layout.tsx\",\n content: `import { Tabs } from \"expo-router\";\nimport { Platform } from \"react-native\";\n\nexport default function TabLayout() {\n return (\n <Tabs\n screenOptions={{\n headerShadowVisible: false,\n tabBarStyle: Platform.select({\n ios: {\n position: \"absolute\",\n },\n default: {},\n }),\n }}\n >\n <Tabs.Screen\n name=\"index\"\n options={{\n title: \"Home\",\n tabBarIcon: ({ color }) => <IconSymbol size={28} name=\"house.fill\" color={color} />,\n }}\n />\n <Tabs.Screen\n name=\"explore\"\n options={{\n title: \"Explore\",\n tabBarIcon: ({ color }) => <IconSymbol size={28} name=\"paperplane.fill\" color={color} />,\n }}\n />\n </Tabs>\n );\n}\n`,\n },\n\n // ─── app/(tabs)/index.tsx ───────────────────────────────────────────\n {\n path: \"app/(tabs)/index.tsx\",\n content: `import { Text, View } from \"react-native\";\n\nexport default function HomeScreen() {\n return (\n <View className=\"flex-1 items-center justify-center p-6\">\n <Text className=\"text-foreground text-2xl font-bold\">${projectName}</Text>\n <Text className=\"text-muted-foreground mt-2\">Welcome to your new app</Text>\n </View>\n );\n}\n`,\n },\n\n // ─── app/(tabs)/explore.tsx ─────────────────────────────────────────\n {\n path: \"app/(tabs)/explore.tsx\",\n content: `import { Text, View } from \"react-native\";\n\nexport default function ExploreScreen() {\n return (\n <View className=\"flex-1 items-center justify-center p-6\">\n <Text className=\"text-foreground text-2xl font-bold\">Explore</Text>\n <Text className=\"text-muted-foreground mt-2\">Discover new features</Text>\n </View>\n );\n}\n`,\n },\n\n // ─── app/+not-found.tsx ─────────────────────────────────────────────\n {\n path: \"app/+not-found.tsx\",\n content: `import { Link, Stack } from \"expo-router\";\nimport { Text, View } from \"react-native\";\n\nexport default function NotFoundScreen() {\n return (\n <>\n <Stack.Screen options={{ title: \"Oops!\" }} />\n <View className=\"flex-1 items-center justify-center p-5\">\n <Text className=\"text-foreground text-2xl font-bold\">This screen doesn't exist.</Text>\n <Link href=\"/\" className=\"mt-4 py-4\">\n <Text className=\"text-primary underline\">Go to home screen!</Text>\n </Link>\n </View>\n </>\n );\n}\n`,\n },\n\n // ─── lib/utils.ts (rnr official cn helper) ───────────────────────────\n {\n path: \"lib/utils.ts\",\n content: `import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n`,\n },\n\n // ─── lib/theme.ts (rnr official theme) ───────────────────────────────\n {\n path: \"lib/theme.ts\",\n content: `import { DarkTheme, DefaultTheme, type Theme } from '@react-navigation/native';\n\nexport const THEME = {\n light: {\n background: 'hsl(0 0% 100%)',\n foreground: 'hsl(0 0% 3.9%)',\n card: 'hsl(0 0% 100%)',\n cardForeground: 'hsl(0 0% 3.9%)',\n popover: 'hsl(0 0% 100%)',\n popoverForeground: 'hsl(0 0% 3.9%)',\n primary: 'hsl(0 0% 9%)',\n primaryForeground: 'hsl(0 0% 98%)',\n secondary: 'hsl(0 0% 96.1%)',\n secondaryForeground: 'hsl(0 0% 9%)',\n muted: 'hsl(0 0% 96.1%)',\n mutedForeground: 'hsl(0 0% 45.1%)',\n accent: 'hsl(0 0% 96.1%)',\n accentForeground: 'hsl(0 0% 9%)',\n destructive: 'hsl(0 84.2% 60.2%)',\n border: 'hsl(0 0% 89.8%)',\n input: 'hsl(0 0% 89.8%)',\n ring: 'hsl(0 0% 63%)',\n radius: '0.625rem',\n chart1: 'hsl(12 76% 61%)',\n chart2: 'hsl(173 58% 39%)',\n chart3: 'hsl(197 37% 24%)',\n chart4: 'hsl(43 74% 66%)',\n chart5: 'hsl(27 87% 67%)',\n },\n dark: {\n background: 'hsl(0 0% 3.9%)',\n foreground: 'hsl(0 0% 98%)',\n card: 'hsl(0 0% 3.9%)',\n cardForeground: 'hsl(0 0% 98%)',\n popover: 'hsl(0 0% 3.9%)',\n popoverForeground: 'hsl(0 0% 98%)',\n primary: 'hsl(0 0% 98%)',\n primaryForeground: 'hsl(0 0% 9%)',\n secondary: 'hsl(0 0% 14.9%)',\n secondaryForeground: 'hsl(0 0% 98%)',\n muted: 'hsl(0 0% 14.9%)',\n mutedForeground: 'hsl(0 0% 63.9%)',\n accent: 'hsl(0 0% 14.9%)',\n accentForeground: 'hsl(0 0% 98%)',\n destructive: 'hsl(0 70.9% 59.4%)',\n border: 'hsl(0 0% 14.9%)',\n input: 'hsl(0 0% 14.9%)',\n ring: 'hsl(300 0% 45%)',\n radius: '0.625rem',\n chart1: 'hsl(220 70% 50%)',\n chart2: 'hsl(160 60% 45%)',\n chart3: 'hsl(30 80% 55%)',\n chart4: 'hsl(280 65% 60%)',\n chart5: 'hsl(340 75% 55%)',\n },\n};\n\nexport const NAV_THEME: Record<'light' | 'dark', Theme> = {\n light: {\n ...DefaultTheme,\n colors: {\n background: THEME.light.background,\n border: THEME.light.border,\n card: THEME.light.card,\n notification: THEME.light.destructive,\n primary: THEME.light.primary,\n text: THEME.light.foreground,\n },\n },\n dark: {\n ...DarkTheme,\n colors: {\n background: THEME.dark.background,\n border: THEME.dark.border,\n card: THEME.dark.card,\n notification: THEME.dark.destructive,\n primary: THEME.dark.primary,\n text: THEME.dark.foreground,\n },\n },\n};\n`,\n },\n\n // ─── types/index.ts ─────────────────────────────────────────────────\n {\n path: \"types/index.ts\",\n content: `/** Global type definitions */\n\n/** Extend this to declare module-specific types */\ndeclare global {\n // Add global type augmentations here\n}\n\nexport {};\n`,\n },\n\n // ─── app.json ────────────────────────────────────────────────────────\n {\n path: \"app.json\",\n content: `{\n \"expo\": {\n \"name\": \"${projectName}\",\n \"slug\": \"${projectName}\",\n \"version\": \"1.0.0\",\n \"orientation\": \"portrait\",\n \"userInterfaceStyle\": \"light\",\n \"icon\": \"./assets/icon.png\",\n \"splash\": {\n \"image\": \"./assets/splash-icon.png\",\n \"resizeMode\": \"contain\",\n \"backgroundColor\": \"#ffffff\"\n },\n \"ios\": {\n \"supportsTablet\": true,\n \"bundleIdentifier\": \"com.${projectName}.app\"\n },\n \"android\": {\n \"adaptiveIcon\": {\n \"foregroundImage\": \"./assets/adaptive-icon.png\",\n \"backgroundColor\": \"#ffffff\"\n },\n \"package\": \"com.${projectName}.app\"\n },\n \"web\": {\n \"favicon\": \"./assets/favicon.png\"\n },\n \"plugins\": [\n \"expo-router\",\n \"expo-splash-screen\"\n ]\n }\n}\n`,\n },\n\n // ─── tsconfig.json ───────────────────────────────────────────────────\n {\n path: \"tsconfig.json\",\n content: `{\n \"extends\": \"expo/tsconfig.base\",\n \"compilerOptions\": {\n \"strict\": true,\n \"paths\": {\n \"@/*\": [\"./*\"]\n }\n },\n \"include\": [\"**/*.ts\", \"**/*.tsx\", \".expo/types/**/*.ts\", \"expo-env.d.ts\", \"nativewind-env.d.ts\"]\n}\n`,\n },\n\n // ─── components.json (rnr CLI compatibility) ─────────────────────────\n {\n path: \"components.json\",\n content: `{\n \"$schema\": \"https://ui.shadcn.com/schema.json\",\n \"style\": \"new-york\",\n \"rsc\": false,\n \"tsx\": true,\n \"tailwind\": {\n \"config\": \"tailwind.config.js\",\n \"css\": \"global.css\",\n \"baseColor\": \"neutral\",\n \"cssVariables\": true\n },\n \"aliases\": {\n \"components\": \"@/components\",\n \"utils\": \"@/lib/utils\",\n \"ui\": \"@/components/ui\",\n \"lib\": \"@/lib\",\n \"hooks\": \"@/hooks\"\n }\n}\n`,\n },\n\n // ─── tailwind.config.js (rnr official) ──────────────────────────────\n {\n path: \"tailwind.config.js\",\n content: `const { hairlineWidth } = require('nativewind/theme');\n\n/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n darkMode: 'class',\n content: ['./app/**/*.{ts,tsx}', './components/**/*.{ts,tsx}'],\n presets: [require('nativewind/preset')],\n theme: {\n extend: {\n colors: {\n border: 'hsl(var(--border))',\n input: 'hsl(var(--input))',\n ring: 'hsl(var(--ring))',\n background: 'hsl(var(--background))',\n foreground: 'hsl(var(--foreground))',\n primary: {\n DEFAULT: 'hsl(var(--primary))',\n foreground: 'hsl(var(--primary-foreground))',\n },\n secondary: {\n DEFAULT: 'hsl(var(--secondary))',\n foreground: 'hsl(var(--secondary-foreground))',\n },\n destructive: {\n DEFAULT: 'hsl(var(--destructive))',\n foreground: 'hsl(var(--destructive-foreground))',\n },\n muted: {\n DEFAULT: 'hsl(var(--muted))',\n foreground: 'hsl(var(--muted-foreground))',\n },\n accent: {\n DEFAULT: 'hsl(var(--accent))',\n foreground: 'hsl(var(--accent-foreground))',\n },\n popover: {\n DEFAULT: 'hsl(var(--popover))',\n foreground: 'hsl(var(--popover-foreground))',\n },\n card: {\n DEFAULT: 'hsl(var(--card))',\n foreground: 'hsl(var(--card-foreground))',\n },\n },\n borderRadius: {\n lg: 'var(--radius)',\n md: 'calc(var(--radius) - 2px)',\n sm: 'calc(var(--radius) - 4px)',\n },\n borderWidth: {\n hairline: hairlineWidth(),\n },\n keyframes: {\n 'accordion-down': {\n from: { height: '0' },\n to: { height: 'var(--radix-accordion-content-height)' },\n },\n 'accordion-up': {\n from: { height: 'var(--radix-accordion-content-height)' },\n to: { height: '0' },\n },\n },\n animation: {\n 'accordion-down': 'accordion-down 0.2s ease-out',\n 'accordion-up': 'accordion-up 0.2s ease-out',\n },\n },\n },\n future: {\n hoverOnlyWhenSupported: true,\n },\n plugins: [require('tailwindcss-animate')],\n};\n`,\n },\n\n // ─── metro.config.js (rnr official: inlineRem: 16) ──────────────────\n {\n path: \"metro.config.js\",\n content: `const { getDefaultConfig } = require(\"expo/metro-config\");\nconst { withNativeWind } = require(\"nativewind/metro\");\n\nconst config = getDefaultConfig(__dirname);\n\nmodule.exports = withNativeWind(config, { input: \"./global.css\", inlineRem: 16 });\n`,\n },\n\n // ─── babel.config.js ─────────────────────────────────────────────────\n {\n path: \"babel.config.js\",\n content: `module.exports = function (api) {\n api.cache(true);\n return {\n presets: [\n [\"babel-preset-expo\", { jsxImportSource: \"nativewind\" }],\n \"nativewind/babel\",\n ],\n plugins: [],\n };\n};\n`,\n },\n\n // ─── global.css (rnr official CSS variables) ─────────────────────────\n {\n path: \"global.css\",\n content: `@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n@layer base {\n :root {\n --background: 0 0% 100%;\n --foreground: 0 0% 3.9%;\n --card: 0 0% 100%;\n --card-foreground: 0 0% 3.9%;\n --popover: 0 0% 100%;\n --popover-foreground: 0 0% 3.9%;\n --primary: 0 0% 9%;\n --primary-foreground: 0 0% 98%;\n --secondary: 0 0% 96.1%;\n --secondary-foreground: 0 0% 9%;\n --muted: 0 0% 96.1%;\n --muted-foreground: 0 0% 45.1%;\n --accent: 0 0% 96.1%;\n --accent-foreground: 0 0% 9%;\n --destructive: 0 84.2% 60.2%;\n --border: 0 0% 89.8%;\n --input: 0 0% 89.8%;\n --ring: 0 0% 63%;\n --radius: 0.625rem;\n --chart-1: 12 76% 61%;\n --chart-2: 173 58% 39%;\n --chart-3: 197 37% 24%;\n --chart-4: 43 74% 66%;\n --chart-5: 27 87% 67%;\n }\n\n .dark:root {\n --background: 0 0% 3.9%;\n --foreground: 0 0% 98%;\n --card: 0 0% 3.9%;\n --card-foreground: 0 0% 98%;\n --popover: 0 0% 3.9%;\n --popover-foreground: 0 0% 98%;\n --primary: 0 0% 98%;\n --primary-foreground: 0 0% 9%;\n --secondary: 0 0% 14.9%;\n --secondary-foreground: 0 0% 98%;\n --muted: 0 0% 14.9%;\n --muted-foreground: 0 0% 63.9%;\n --accent: 0 0% 14.9%;\n --accent-foreground: 0 0% 98%;\n --destructive: 0 70.9% 59.4%;\n --border: 0 0% 14.9%;\n --input: 0 0% 14.9%;\n --ring: 300 0% 45%;\n --chart-1: 220 70% 50%;\n --chart-2: 160 60% 45%;\n --chart-3: 30 80% 55%;\n --chart-4: 280 65% 60%;\n --chart-5: 340 75% 55%;\n }\n}\n`,\n },\n\n // ─── .gitignore ─────────────────────────────────────────────────────\n {\n path: \".gitignore\",\n content: `# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files\n\n# dependencies\nnode_modules/\n\n# Expo\n.expo/\ndist/\nweb-build/\n\n# Native\n*.orig.*\n*.jks\n*.p8\n*.p12\n*.key\n*.mobileprovision\n\n# Metro\n.metro-health-check*\n\n# debug\nnpm-debug.*\nyarn-debug.*\nyarn-error.*\n\n# macOS\n.DS_Store\n*.pem\n\n# local env files\n.env*.local\n\n# typescript\n*.tsbuildinfo\n\n# generated files\nexpo-env.d.ts\n`,\n },\n ];\n}\n","import type { TemplateFile } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\n/**\n * Generate UI template files for the \"login-tabs\" preset.\n * Uses reactnativereusables official patterns (NAV_THEME, PortalHost, cn()).\n * Tabs: Index + Explore + Mine\n */\nexport function generateLoginTabsTemplates(projectName: string): TemplateFile[] {\n return [\n // ─── app/_layout.tsx (overwrite base) ────────────────────────────────\n {\n path: \"app/_layout.tsx\",\n content: lines(\n 'import \"../global.css\";',\n 'import { DarkTheme, DefaultTheme, ThemeProvider } from \"@react-navigation/native\";',\n 'import { useFonts } from \"expo-font\";',\n 'import { Stack } from \"expo-router\";',\n 'import * as SplashScreen from \"expo-splash-screen\";',\n 'import { useEffect } from \"react\";',\n 'import { useColorScheme } from \"react-native\";',\n 'import { PortalHost } from \"@rn-primitives/portal\";',\n 'import { NAV_THEME } from \"@/lib/theme\";',\n \"\",\n \"SplashScreen.preventAutoHideAsync();\",\n \"\",\n \"export default function RootLayout() {\",\n \" const colorScheme = useColorScheme();\",\n \" const [loaded] = useFonts({\",\n \" NunitoBold: require(\\\"../assets/fonts/Nunito-Bold.ttf\\\"),\",\n \" });\",\n \"\",\n \" useEffect(() => {\",\n \" if (loaded) {\",\n \" SplashScreen.hideAsync();\",\n \" }\",\n \" }, [loaded]);\",\n \"\",\n \" if (!loaded) {\",\n \" return null;\",\n \" }\",\n \"\",\n \" return (\",\n \" <ThemeProvider value={colorScheme === \\\"dark\\\" ? NAV_THEME.dark : NAV_THEME.light}>\",\n \" <Stack>\",\n ' <Stack.Screen name=\"login\" options={{ headerShown: false }} />',\n ' <Stack.Screen name=\"(tabs)\" options={{ headerShown: false }} />',\n ' <Stack.Screen name=\"+not-found\" />',\n \" </Stack>\",\n \" <PortalHost />\",\n \" </ThemeProvider>\",\n \" );\",\n \"}\",\n \"\"\n ),\n },\n\n // ─── app/login.tsx ───────────────────────────────────────────────────\n {\n path: \"app/login.tsx\",\n content: lines(\n 'import { SignInForm } from \"@/components/SignInForm\";',\n 'import { View } from \"react-native\";',\n \"\",\n \"export default function LoginScreen() {\",\n \" return (\",\n ' <View className=\"flex-1 items-center justify-center p-4\">',\n \" <SignInForm />\",\n \" </View>\",\n \" );\",\n \"}\",\n \"\"\n ),\n },\n\n // ─── app/(tabs)/_layout.tsx (overwrite base) ───────────────────────\n {\n path: \"app/(tabs)/_layout.tsx\",\n content: lines(\n 'import { Tabs } from \"expo-router\";',\n 'import { Platform } from \"react-native\";',\n \"\",\n \"export default function TabLayout() {\",\n \" return (\",\n \" <Tabs\",\n \" screenOptions={{\",\n \" headerShadowVisible: false,\",\n \" tabBarStyle: Platform.select({\",\n \" ios: {\",\n \" position: \\\"absolute\\\",\",\n \" },\",\n \" default: {},\",\n \" }),\",\n \" }}\",\n \" >\",\n \" <Tabs.Screen\",\n ' name=\"index\"',\n \" options={{\",\n ' title: \"Home\",',\n ' tabBarIcon: ({ color }) => <IconSymbol size={28} name=\"house.fill\" color={color} />,',\n \" }}\",\n \" />\",\n \" <Tabs.Screen\",\n ' name=\"explore\"',\n \" options={{\",\n ' title: \"Explore\",',\n ' tabBarIcon: ({ color }) => <IconSymbol size={28} name=\"paperplane.fill\" color={color} />',\n \" }}\",\n \" />\",\n \" <Tabs.Screen\",\n ' name=\"mine\"',\n \" options={{\",\n ' title: \"Mine\",',\n ' tabBarIcon: ({ color }) => <IconSymbol size={28} name=\"chevron.right\" color={color} />',\n \" }}\",\n \" />\",\n \" </Tabs>\",\n \" );\",\n \"}\",\n \"\"\n ),\n },\n\n // ─── app/(tabs)/index.tsx ──────────────────────────────────────────\n {\n path: \"app/(tabs)/index.tsx\",\n content: lines(\n 'import { Button } from \"@/components/ui/button\";',\n 'import {',\n ' AlertDialog,',\n ' AlertDialogAction,',\n ' AlertDialogCancel,',\n ' AlertDialogContent,',\n ' AlertDialogDescription,',\n ' AlertDialogFooter,',\n ' AlertDialogHeader,',\n ' AlertDialogTitle,',\n ' AlertDialogTrigger,',\n '} from \"@/components/ui/alert-dialog\";',\n 'import { Text } from \"@/components/ui/text\";',\n 'import { View } from \"react-native\";',\n \"\",\n \"export default function HomeScreen() {\",\n \" return (\",\n ' <View className=\"flex-1 gap-6 p-6\">',\n ' <Text variant=\"h3\">UI Components</Text>',\n ' <Text variant=\"muted\">React Native Reusables components showcase</Text>',\n \"\",\n \" {/* Button variants */}\",\n ' <View className=\"gap-3\">',\n ' <Text variant=\"large\">Buttons</Text>',\n \" <View className=\\\"flex-row flex-wrap gap-2\\\">\",\n ' <Button onPress={() => {}}>',\n ' <Text>Default</Text>',\n \" </Button>\",\n ' <Button variant=\"secondary\" onPress={() => {}}>',\n ' <Text>Secondary</Text>',\n \" </Button>\",\n ' <Button variant=\"destructive\" onPress={() => {}}>',\n ' <Text>Destructive</Text>',\n \" </Button>\",\n ' <Button variant=\"outline\" onPress={() => {}}>',\n ' <Text>Outline</Text>',\n \" </Button>\",\n ' <Button variant=\"ghost\" onPress={() => {}}>',\n ' <Text>Ghost</Text>',\n \" </Button>\",\n ' <Button variant=\"link\" onPress={() => {}}>',\n ' <Text>Link</Text>',\n \" </Button>\",\n \" </View>\",\n \" </View>\",\n \"\",\n \" {/* AlertDialog */}\",\n ' <View className=\"gap-3\">',\n ' <Text variant=\"large\">Alert Dialog</Text>',\n \" <AlertDialog>\",\n \" <AlertDialogTrigger asChild>\",\n ' <Button variant=\"outline\">',\n ' <Text>Show Alert</Text>',\n \" </Button>\",\n \" </AlertDialogTrigger>\",\n \" <AlertDialogContent>\",\n \" <AlertDialogHeader>\",\n ' <AlertDialogTitle>Are you sure?</AlertDialogTitle>',\n \" <AlertDialogDescription>\",\n \" This action cannot be undone. This will permanently delete your account and remove your data from our servers.\",\n \" </AlertDialogDescription>\",\n \" </AlertDialogHeader>\",\n \" <AlertDialogFooter>\",\n \" <AlertDialogCancel>\",\n ' <Text>Cancel</Text>',\n \" </AlertDialogCancel>\",\n \" <AlertDialogAction>\",\n ' <Text>Continue</Text>',\n \" </AlertDialogAction>\",\n \" </AlertDialogFooter>\",\n \" </AlertDialogContent>\",\n \" </AlertDialog>\",\n \" </View>\",\n \" </View>\",\n \" );\",\n \"}\",\n \"\"\n ),\n },\n\n // ─── app/(tabs)/explore.tsx ────────────────────────────────────────\n {\n path: \"app/(tabs)/explore.tsx\",\n content: lines(\n 'import { Text } from \"@/components/ui/text\";',\n 'import { Card, CardContent, CardDescription, CardHeader, CardTitle } from \"@/components/ui/card\";',\n 'import { View, ScrollView } from \"react-native\";',\n \"\",\n \"const ITEMS = Array.from({ length: 20 }, (_, i) => ({\",\n \" id: String(i + 1),\",\n \" title: `Item ${i + 1}`,\",\n \" description: `Description for item ${i + 1}`,\",\n \"}));\",\n \"\",\n \"export default function ExploreScreen() {\",\n \" return (\",\n ' <ScrollView className=\"flex-1 p-4\">',\n ' <Text variant=\"h3\" className=\"mb-4\">Explore</Text>',\n ' <View className=\"gap-3\">',\n \" {ITEMS.map((item) => (\",\n ' <Card key={item.id}>',\n \" <CardHeader>\",\n \" <CardTitle>{item.title}</CardTitle>\",\n \" <CardDescription>{item.description}</CardDescription>\",\n \" </CardHeader>\",\n \" <CardContent />\",\n \" </Card>\",\n \" ))}\",\n \" </View>\",\n \" </ScrollView>\",\n \" );\",\n \"}\",\n \"\"\n ),\n },\n\n // ─── app/(tabs)/mine.tsx ────────────────────────────────────────────\n {\n path: \"app/(tabs)/mine.tsx\",\n content: lines(\n 'import { Button } from \"@/components/ui/button\";',\n 'import { Card, CardContent, CardHeader, CardTitle } from \"@/components/ui/card\";',\n 'import { Text } from \"@/components/ui/text\";',\n 'import { View } from \"react-native\";',\n 'import { router } from \"expo-router\";',\n \"\",\n \"export default function MineScreen() {\",\n \" return (\",\n ' <View className=\"flex-1 p-6\">',\n ' <Text variant=\"h3\" className=\"mb-6\">Mine</Text>',\n \"\",\n ' <Card className=\"mb-6\">',\n ' <CardHeader>',\n ' <CardTitle>Profile</CardTitle>',\n \" </CardHeader>\",\n \" <CardContent>\",\n ' <View className=\"gap-2\">',\n ' <Text variant=\"muted\">User Name</Text>',\n ' <Text>user@example.com</Text>',\n \" </View>\",\n \" </CardContent>\",\n \" </Card>\",\n \"\",\n ' <Button',\n ' variant=\"destructive\"',\n \" className=\\\"w-full\\\"\",\n \" onPress={() => {\",\n ' router.replace(\"/login\");',\n \" }}\",\n \" >\",\n ' <Text>Sign Out</Text>',\n \" </Button>\",\n \" </View>\",\n \" );\",\n \"}\",\n \"\"\n ),\n },\n\n // ─── components/SignInForm.tsx ────────────────────────────────────\n {\n path: \"components/SignInForm.tsx\",\n content: lines(\n 'import { Button } from \"@/components/ui/button\";',\n 'import {',\n ' Card,',\n ' CardContent,',\n ' CardDescription,',\n ' CardHeader,',\n ' CardTitle,',\n '} from \"@/components/ui/card\";',\n 'import { Input } from \"@/components/ui/input\";',\n 'import { Label } from \"@/components/ui/label\";',\n 'import { Separator } from \"@/components/ui/separator\";',\n 'import { Text } from \"@/components/ui/text\";',\n 'import * as React from \"react\";',\n 'import { Pressable, type TextInput, View } from \"react-native\";',\n 'import { router } from \"expo-router\";',\n \"\",\n \"export function SignInForm() {\",\n \" const passwordInputRef = React.useRef<TextInput>(null);\",\n \"\",\n \" function onEmailSubmitEditing() {\",\n \" passwordInputRef.current?.focus();\",\n \" }\",\n \"\",\n \" function onSubmit() {\",\n \" // TODO: Submit form and navigate to protected screen if successful\",\n ' router.replace(\"/(tabs)\");',\n \" }\",\n \"\",\n \" return (\",\n ' <View className=\"gap-6 w-full max-w-sm\">',\n ' <Card className=\"shadow-none sm:shadow-sm sm:shadow-black/5\">',\n \" <CardHeader>\",\n ' <CardTitle className=\"text-center text-xl sm:text-left\">Sign in to your app</CardTitle>',\n ' <CardDescription className=\"text-center sm:text-left\">',\n \" Welcome back! Please sign in to continue\",\n \" </CardDescription>\",\n \" </CardHeader>\",\n ' <CardContent className=\"gap-6\">',\n ' <View className=\"gap-6\">',\n ' <View className=\"gap-1.5\">',\n ' <Label nativeID=\"email\">Email</Label>',\n \" <Input\",\n ' placeholder=\"m@example.com\"',\n ' keyboardType=\"email-address\"',\n ' autoComplete=\"email\"',\n ' autoCapitalize=\"none\"',\n \" onSubmitEditing={onEmailSubmitEditing}\",\n ' returnKeyType=\"next\"',\n ' submitBehavior=\"submit\"',\n \" />\",\n \" </View>\",\n ' <View className=\"gap-1.5\">',\n ' <View className=\"flex-row items-center\">',\n ' <Label nativeID=\"password\">Password</Label>',\n \" <Button\",\n ' variant=\"link\"',\n ' size=\"sm\"',\n ' className=\"ml-auto h-4 px-1 py-0 sm:h-4\"',\n \" onPress={() => {\",\n \" // TODO: Navigate to forgot password screen\",\n \" }}\",\n \" >\",\n ' <Text className=\"font-normal leading-4\">Forgot your password?</Text>',\n \" </Button>\",\n \" </View>\",\n \" <Input\",\n \" ref={passwordInputRef}\",\n \" secureTextEntry\",\n ' returnKeyType=\"send\"',\n \" onSubmitEditing={onSubmit}\",\n \" />\",\n \" </View>\",\n ' <Button className=\"w-full\" onPress={onSubmit}>',\n ' <Text>Continue</Text>',\n \" </Button>\",\n \" </View>\",\n ' <Text className=\"text-center text-sm\">',\n \" Don't have an account?{\\\" \\\"}\",\n \" <Pressable\",\n \" onPress={() => {\",\n \" // TODO: Navigate to sign up screen\",\n \" }}\",\n \" >\",\n ' <Text className=\"text-sm underline underline-offset-4\">Sign up</Text>',\n \" </Pressable>\",\n \" </Text>\",\n ' <View className=\"flex-row items-center\">',\n ' <Separator className=\"flex-1\" />',\n ' <Text className=\"text-muted-foreground px-4 text-sm\">or</Text>',\n ' <Separator className=\"flex-1\" />',\n \" </View>\",\n \" </CardContent>\",\n \" </Card>\",\n \" </View>\",\n \" );\",\n \"}\",\n \"\"\n ),\n },\n ];\n}\n","import fs from \"fs-extra\";\nimport path from \"path\";\n\n/**\n * Recursively copy all files from a source directory to a destination directory.\n */\nexport async function copyDirectory(\n src: string,\n dest: string\n): Promise<void> {\n await fs.copy(src, dest, { overwrite: true });\n}\n\n/**\n * Write a file, creating parent directories as needed.\n */\nexport async function writeFile(\n filePath: string,\n content: string\n): Promise<void> {\n const dir = path.dirname(filePath);\n await fs.ensureDir(dir);\n await fs.writeFile(filePath, content, \"utf-8\");\n}\n\n/**\n * Check if a path exists.\n */\nexport async function pathExists(filePath: string): Promise<boolean> {\n return fs.pathExists(filePath);\n}\n\n/**\n * Read a JSON file and parse it.\n */\nexport async function readJson<T = Record<string, unknown>>(\n filePath: string\n): Promise<T> {\n return fs.readJson(filePath) as Promise<T>;\n}\n\n/**\n * Write a JSON object to a file with formatting.\n */\nexport async function writeJson(\n filePath: string,\n data: unknown,\n spaces: number = 2\n): Promise<void> {\n const dir = path.dirname(filePath);\n await fs.ensureDir(dir);\n await fs.writeJson(filePath, data, { spaces });\n}\n\n/**\n * Replace template variables in content string.\n * Supports {{variableName}} syntax.\n */\nexport function replaceTemplateVars(\n content: string,\n vars: Record<string, string>\n): string {\n let result = content;\n for (const [key, value] of Object.entries(vars)) {\n const regex = new RegExp(`\\\\{\\\\{\\\\s*${key}\\\\s*\\\\}\\\\}`, \"g\");\n result = result.replace(regex, value);\n }\n return result;\n}\n","import { writeJson, readJson } from \"./file\";\n\ninterface PackageJsonDeps {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n}\n\n/**\n * Merge dependencies into a package.json file.\n */\nexport async function mergeDependencies(\n pkgPath: string,\n deps: Record<string, string>,\n devDeps: Record<string, string>\n): Promise<void> {\n const pkg = await readJson<PackageJsonDeps>(pkgPath);\n\n pkg.dependencies = {\n ...(pkg.dependencies || {}),\n ...deps,\n };\n\n pkg.devDependencies = {\n ...(pkg.devDependencies || {}),\n ...devDeps,\n };\n\n await writeJson(pkgPath, pkg);\n}\n\n/**\n * Generate base package.json content for a new Expo project.\n *\n * ALL module dependencies are included by default — module selection only\n * controls which code/template files are generated, not which packages are\n * installed. This avoids version-mismatch issues and ensures every project\n * is ready to use any module without extra installs.\n */\nexport function generateBasePackageJson(\n projectName: string\n): Record<string, unknown> {\n return {\n name: projectName,\n version: \"1.0.0\",\n main: \"expo-router/entry\",\n scripts: {\n start: \"expo start\",\n \"reset-project\": \"node ./scripts/reset-project.js\",\n android: \"expo start --android\",\n ios: \"expo start --ios\",\n web: \"expo start --web\",\n lint: \"expo lint\",\n },\n dependencies: {\n // ─── Expo core (aligned with create-expo-app SDK 54) ───────────\n expo: \"~54.0.33\",\n \"expo-router\": \"~6.0.23\",\n \"expo-linking\": \"~8.0.12\",\n \"expo-constants\": \"~18.0.13\",\n \"expo-status-bar\": \"~3.0.9\",\n \"expo-splash-screen\": \"~31.0.13\",\n \"expo-font\": \"~14.0.11\",\n \"expo-haptics\": \"~15.0.8\",\n \"expo-image\": \"~3.0.11\",\n \"expo-symbols\": \"~1.0.8\",\n \"expo-system-ui\": \"~6.0.9\",\n \"expo-web-browser\": \"~15.0.10\",\n\n // ─── React & React Native ─────────────────────────────────────\n react: \"19.1.0\",\n \"react-dom\": \"19.1.0\",\n \"react-native\": \"0.81.5\",\n \"react-native-web\": \"~0.21.0\",\n \"@expo/vector-icons\": \"^15.0.3\",\n \"@react-navigation/bottom-tabs\": \"^7.4.0\",\n \"@react-navigation/elements\": \"^2.6.3\",\n \"@react-navigation/native\": \"^7.1.8\",\n \"react-native-safe-area-context\": \"~5.6.0\",\n \"react-native-screens\": \"~4.16.0\",\n\n // ─── Animation (default in all projects) ──────────────────────\n \"react-native-reanimated\": \"~4.1.1\",\n \"react-native-worklets\": \"~0.5.1\",\n \"react-native-gesture-handler\": \"~2.28.0\",\n\n // ─── Styling ──────────────────────────────────────────────────\n nativewind: \"^4.1.0\",\n tailwindcss: \"^3.4.0\",\n \"tailwindcss-animate\": \"^1.0.7\",\n \"react-native-svg\": \"15.12.1\",\n\n // ─── Network ─────────────────────────────────────────────────\n \"@tanstack/react-query\": \"^5.60.0\",\n\n // ─── State ───────────────────────────────────────────────────\n zustand: \"^5.0.0\",\n\n // ─── Storage ─────────────────────────────────────────────────\n \"react-native-mmkv\": \"^3.1.0\",\n\n // ─── Payment ─────────────────────────────────────────────────\n \"react-native-iap\": \"^12.15.0\",\n\n // ─── Form ────────────────────────────────────────────────────\n \"react-hook-form\": \"^7.54.0\",\n \"@hookform/resolvers\": \"^3.9.0\",\n zod: \"^3.24.0\",\n\n // ─── Video ───────────────────────────────────────────────────\n \"expo-av\": \"~16.0.8\",\n\n // ─── Auth — Google ───────────────────────────────────────────\n \"@react-native-google-signin/google-signin\": \"^13.1.0\",\n\n // ─── Auth — Facebook ─────────────────────────────────────────\n \"expo-auth-session\": \"~7.0.11\",\n \"expo-crypto\": \"~15.0.9\",\n\n // ─── Auth — Apple ────────────────────────────────────────────\n \"expo-apple-authentication\": \"~8.0.8\",\n\n // ─── WebView ─────────────────────────────────────────────────\n \"react-native-webview\": \"~13.15.0\",\n\n // ─── i18n ────────────────────────────────────────────────────\n i18next: \"^24.2.0\",\n \"react-i18next\": \"^15.2.0\",\n \"expo-localization\": \"~17.0.8\",\n\n // ─── OTA ─────────────────────────────────────────────────────\n \"expo-updates\": \"~29.0.17\",\n\n // ─── Notifications ───────────────────────────────────────────\n \"expo-notifications\": \"~0.32.17\",\n\n // ─── Permissions ─────────────────────────────────────────────\n \"expo-image-picker\": \"~17.0.11\",\n \"expo-camera\": \"~17.0.10\",\n \"expo-location\": \"~19.0.8\",\n\n // ─── Bottom Sheet ────────────────────────────────────────────\n \"@gorhom/bottom-sheet\": \"^5.1.0\",\n\n // ─── FlashList ───────────────────────────────────────────────\n \"@shopify/flash-list\": \"2.0.2\",\n\n // ─── UI — reactnative.reusables ──────────────────────────────\n \"class-variance-authority\": \"^0.7.1\",\n clsx: \"^2.1.1\",\n \"tailwind-merge\": \"^2.6.0\",\n \"@rn-primitives/slot\": \"^1.1.0\",\n \"@rn-primitives/types\": \"^1.1.0\",\n \"@rn-primitives/label\": \"^1.1.0\",\n \"@rn-primitives/separator\": \"^1.1.0\",\n \"@rn-primitives/alert-dialog\": \"^1.1.0\",\n \"@rn-primitives/portal\": \"^1.1.0\",\n },\n devDependencies: {\n \"@types/react\": \"~19.1.0\",\n typescript: \"~5.9.2\",\n eslint: \"^9.25.0\",\n \"eslint-config-expo\": \"~10.0.0\",\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAkB;AAClB,uBAAwB;AACxB,mBAAsB;AACtB,IAAAA,mBAAgB;AAChB,iBAAgB;AAChB,IAAAC,eAAiB;AACjB,qBAAoB;;;ACEb,SAAS,sBAAsB,SAAwB;AAC5D,UACG,QAAQ,uBAAuB,EAC/B,YAAY,iDAAiD,EAC7D,OAAO,OAAO,gBAAwB;AACrC,UAAM,cAAc,WAAW;AAAA,EACjC,CAAC;AACL;AAKO,SAAS,uBAAuB,SAAwB;AAC7D,UACG,QAAQ,SAAS,EACjB,YAAY,sEAAsE,EAClF,OAAO,gBAAgB,kDAAkD,QAAQ,IAAI,CAAC,EACtF,OAAO,OAAO,YAA6B;AAC1C,UAAM,eAAe,QAAQ,GAAG;AAAA,EAClC,CAAC;AACL;AAKO,SAAS,mBAAmB,SAAwB;AACzD,UACG,QAAQ,kBAAkB,EAC1B,YAAY,2DAA2D,EACvE,OAAO,gBAAgB,kDAAkD,QAAQ,IAAI,CAAC,EACtF,OAAO,OAAO,WAAqB,YAA6B;AAC/D,UAAM,UAAU,WAAW,QAAQ,GAAG;AAAA,EACxC,CAAC;AACL;;;ACrCO,SAAS,SAAS,MAAwB;AAC/C,SAAO,KAAK,KAAK,IAAI;AACvB;;;ACHA,IAAM,gBAA2B;AAAA,EAC/B,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,cAAc;AAAA,IACZ,yBAAyB;AAAA,EAC3B;AAAA,EACA,iBAAiB,CAAC;AAAA,EAClB,OAAO;AAAA,IACL;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,iBAAiB,CAAC,4CAA4C;AAAA,EAC9D,eAAe;AAAA,IACb;AAAA,IACA;AAAA,EACF;AACF;AAEA,IAAO,kBAAQ;;;AC3Vf,IAAM,cAAyB;AAAA,EAC7B,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,cAAc;AAAA,IACZ,SAAS;AAAA,EACX;AAAA,EACA,iBAAiB,CAAC;AAAA,EAClB,OAAO;AAAA,IACL;AAAA,MACE,MAAM;AAAA,MACN,SAAS,MAAM,oDAAoD;AAAA,IACrE;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAO,gBAAQ;;;AC5Gf,IAAM,gBAA2B;AAAA,EAC/B,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,cAAc;AAAA,IACZ,qBAAqB;AAAA,EACvB;AAAA,EACA,iBAAiB,CAAC;AAAA,EAClB,OAAO;AAAA,IACL;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAO,kBAAQ;;;ACnGf,IAAM,gBAA2B;AAAA,EAC/B,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,cAAc;AAAA,IACZ,oBAAoB;AAAA,EACtB;AAAA,EACA,iBAAiB,CAAC;AAAA,EAClB,OAAO;AAAA,IACL;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;A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AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,EAAE,KAAK,IAAI;AAAA,IACb;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,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CN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,iBAAiB,CAAC,qBAAqB;AAAA,EACvC,eAAe;AAAA,IACb;AAAA,EACF;AACF;AAEA,IAAO,uBAAQ;;;ACjOf,IAAM,mBAA8B;AAAA,EAClC,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,cAAc;AAAA,IACZ,qBAAqB;AAAA,IACrB,eAAe;AAAA,IACf,iBAAiB;AAAA,EACnB;AAAA,EACA,iBAAiB,CAAC;AAAA,EAClB,OAAO;AAAA,IACL;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,WAAW;AAAA,IACT,SAAS;AAAA,MACP;AAAA,QACE;AAAA,QACA;AAAA,UACE,kBAAkB;AAAA,QACpB;AAAA,MACF;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;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA;AAAA,IAGA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA;AAAA,IAGA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA;AAAA,IAGA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA;AAAA,IAGA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA;AAAA,IAGA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA;AAAA,IAGA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA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CA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA;AAAA,IAGA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA;AAAA,IAGA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA;AAAA,IAGA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA;AAAA,IAGA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QA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","names":["import_fs_extra","import_path","path","fs","fs","path","chalk","path","fse","prompts","ora"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/commands/create.ts","../src/utils/lines.ts","../src/modules/network.ts","../src/modules/state.ts","../src/modules/storage.ts","../src/modules/payment.ts","../src/modules/form.ts","../src/modules/image.ts","../src/modules/video.ts","../src/modules/auth-google.ts","../src/modules/auth-facebook.ts","../src/modules/auth-apple.ts","../src/modules/webview.ts","../src/modules/i18n.ts","../src/modules/animation.ts","../src/modules/ota.ts","../src/modules/notification.ts","../src/modules/permission.ts","../src/modules/bottom-sheet.ts","../src/modules/flashlist.ts","../src/modules/ui-reusables.ts","../src/modules/index.ts","../src/templates/base.ts","../src/templates/login-tabs.ts","../src/utils/file.ts","../src/utils/package.ts"],"sourcesContent":["import chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { execa } from \"execa\";\nimport fse from \"fs-extra\";\nimport ora from \"ora\";\nimport path from \"path\";\nimport prompts from \"prompts\";\nimport {\n registerAddCommand,\n registerCreateCommand,\n registerUpgradeCommand,\n} from \"./commands/create\";\nimport { getModuleById, getModulesByIds, modules } from \"./modules\";\nimport { generateBaseTemplates } from \"./templates/base\";\nimport { generateLoginTabsTemplates } from \"./templates/login-tabs\";\nimport type { ModuleDef, ProjectConfig } from \"./types\";\nimport { replaceTemplateVars, writeFile, writeJson } from \"./utils/file\";\nimport { generateBasePackageJson, mergeDependencies } from \"./utils/package\";\n\n/** CLI version — bump this when publishing */\nconst CLI_VERSION = \"1.7.3\";\n\n/** Config file name stored in project root */\nconst CONFIG_FILE = \".expo-bbase.json\";\n\n// ─── CLI Entry Point ──────────────────────────────────────────────────────\n\nexport async function run(): Promise<void> {\n const program = new Command();\n\n program\n .name(\"expo-bbase\")\n .description(\"Expo SDK 54+ scaffolding CLI tool\")\n .version(CLI_VERSION);\n\n // Default action: npx expo-bbase <project-name>\n program\n .argument(\"[project-name]\", \"Name of the project to create\")\n .action(async (projectName?: string) => {\n if (!projectName) {\n console.error(chalk.red(\"Error: Please provide a project name.\"));\n console.log(chalk.gray(\"Usage: npx expo-bbase <project-name>\"));\n process.exit(1);\n }\n await createProject(projectName);\n });\n\n registerCreateCommand(program);\n registerUpgradeCommand(program);\n registerAddCommand(program);\n\n await program.parseAsync(process.argv);\n}\n\n// ─── Project Config Helpers ───────────────────────────────────────────────\n\nasync function readProjectConfig(\n targetDir: string\n): Promise<ProjectConfig | null> {\n const configPath = path.join(targetDir, CONFIG_FILE);\n if (!(await fse.pathExists(configPath))) {\n return null;\n }\n return fse.readJson(configPath) as Promise<ProjectConfig>;\n}\n\nasync function writeProjectConfig(\n targetDir: string,\n config: ProjectConfig\n): Promise<void> {\n const configPath = path.join(targetDir, CONFIG_FILE);\n await writeJson(configPath, config);\n}\n\n// ─── Create Project ───────────────────────────────────────────────────────\n\nexport async function createProject(projectName: string): Promise<void> {\n console.log();\n console.log(\n chalk.bold.cyan(\" ╔══════════════════════════════════════╗\")\n );\n console.log(\n chalk.bold.cyan(\" ║ expo-bbase — Expo Scaffolding ║\")\n );\n console.log(\n chalk.bold.cyan(\" ╚══════════════════════════════════════╝\")\n );\n console.log();\n\n // ─── Step 1: UI template selection ──────────────────────────────────\n const { uiTemplate } = await prompts({\n type: \"select\",\n name: \"uiTemplate\",\n message: \"Choose a UI template\",\n choices: [\n {\n title: `${chalk.bold(\"Login + Tabs\")} — Login page, Index/Explore/Mine tabs with rnr components`,\n value: \"login-tabs\",\n description: \"Pre-built login form, 3-tab layout with Button & AlertDialog demos\",\n },\n {\n title: `${chalk.bold(\"Default\")} — Blank tabs (Index + Explore)`,\n value: \"default\",\n description: \"Minimal starter with basic tab navigation\",\n },\n ],\n initial: 0,\n });\n\n if (uiTemplate === undefined) {\n console.log(chalk.yellow(\"\\nCancelled.\"));\n process.exit(0);\n }\n\n const isLoginTabs = uiTemplate === \"login-tabs\";\n\n // ─── Step 2: Interactive module selection ──────────────────────────\n const choices = modules.map((m) => ({\n title: `${chalk.bold(m.name)} — ${chalk.gray(m.description)}`,\n value: m.id,\n // Auto-select ui-reusables when login-tabs template is chosen\n selected: isLoginTabs && m.id === \"ui-reusables\" ? true : m.defaultChecked,\n }));\n\n const { selectedModules } = await prompts({\n type: \"multiselect\",\n name: \"selectedModules\",\n message: \"Select modules (Space to toggle, Enter to confirm)\",\n choices,\n hint: \"- Space toggle · a select all/none · Enter confirm\",\n instructions: false,\n });\n\n if (selectedModules === undefined) {\n console.log(chalk.yellow(\"\\nCancelled.\"));\n process.exit(0);\n }\n\n // Ensure ui-reusables is included when login-tabs is selected\n let finalModuleIds = selectedModules as string[];\n if (isLoginTabs && !finalModuleIds.includes(\"ui-reusables\")) {\n finalModuleIds = [\"ui-reusables\", ...finalModuleIds];\n }\n\n const selectedModuleDefs = getModulesByIds(finalModuleIds);\n const targetDir = path.resolve(process.cwd(), projectName);\n\n console.log();\n console.log(chalk.white(` 📦 Project: ${chalk.bold(projectName)}`));\n console.log(chalk.white(` 📂 Path: ${chalk.gray(targetDir)}`));\n console.log(\n chalk.white(\n ` 🎨 Template: ${chalk.green(isLoginTabs ? \"Login + Tabs\" : \"Default\")}`\n )\n );\n console.log(\n chalk.white(\n ` 🧩 Modules: ${chalk.green(selectedModuleDefs.map((m) => m.name).join(\", \") || \"none\")}`\n )\n );\n console.log();\n\n // ─── Step 3: Create project directory and write base files ─────────\n const spinner = ora(\"Creating project...\").start();\n\n try {\n const baseTemplates = generateBaseTemplates(projectName);\n\n for (const file of baseTemplates) {\n const filePath = path.join(targetDir, file.path);\n const content = replaceTemplateVars(file.content, { projectName });\n await writeFile(filePath, content);\n }\n\n spinner.text = \"Writing module files...\";\n\n // ─── Step 4: Write module template files ──────────────────────────\n for (const mod of selectedModuleDefs) {\n for (const file of mod.files) {\n const filePath = path.join(targetDir, file.path);\n const content = replaceTemplateVars(file.content, { projectName });\n await writeFile(filePath, content);\n }\n }\n\n // ─── Step 4.5: Write UI template files (override base files) ──────\n if (isLoginTabs) {\n spinner.text = \"Writing UI template files...\";\n const loginTabsTemplates = generateLoginTabsTemplates(projectName);\n for (const file of loginTabsTemplates) {\n const filePath = path.join(targetDir, file.path);\n const content = replaceTemplateVars(file.content, { projectName });\n await writeFile(filePath, content);\n }\n }\n\n // ─── Step 4.6: Copy assets directory (icon, splash, fonts) ──────\n spinner.text = \"Copying assets...\";\n const assetsSource = path.join(__dirname, \"..\", \"templates\", \"assets\");\n const assetsTarget = path.join(targetDir, \"assets\");\n if (fse.existsSync(assetsSource)) {\n fse.copySync(assetsSource, assetsTarget);\n }\n\n // ─── Step 5: Generate package.json (all deps in base) ──────────────\n spinner.text = \"Generating package.json...\";\n const pkgJson = generateBasePackageJson(projectName);\n\n const pkgPath = path.join(targetDir, \"package.json\");\n await writeJson(pkgPath, pkgJson);\n\n // ─── Step 6: Update app.json with plugin configurations ──────────\n spinner.text = \"Configuring app.json...\";\n await updateAppJson(targetDir, selectedModuleDefs, projectName);\n\n // ─── Step 7: Update babel.config.js with additional plugins ───────\n spinner.text = \"Configuring Babel...\";\n await updateBabelConfig(targetDir, selectedModuleDefs);\n\n // ─── Step 8: Update app/_layout.tsx with module providers/imports ─\n spinner.text = \"Configuring layout...\";\n await updateLayoutFile(targetDir, selectedModuleDefs);\n\n // ─── Step 9: Write .expo-bbase.json config ───────────────────────\n await writeProjectConfig(targetDir, {\n projectName,\n selectedModules: finalModuleIds,\n cliVersion: CLI_VERSION,\n uiTemplate: isLoginTabs ? \"login-tabs\" : \"default\",\n });\n\n // ─── Step 10: Run yarn install ────────────────────────────────────\n spinner.text = \"Installing dependencies (yarn install)...\";\n try {\n await execa(\"yarn\", [\"install\"], {\n cwd: targetDir,\n timeout: 300_000,\n });\n } catch (installError: unknown) {\n const errMsg =\n installError instanceof Error\n ? installError.message\n : String(installError);\n spinner.warn(\"yarn install failed, please install manually\");\n console.log(chalk.red(` Error: ${errMsg}`));\n console.log(chalk.gray(` cd ${projectName} && yarn install`));\n }\n\n // ─── Done! ────────────────────────────────────────────────────────\n spinner.succeed(chalk.green(\"Project created!\"));\n\n console.log();\n console.log(chalk.bold(\" 🎉 Next steps:\"));\n console.log(chalk.white(` cd ${projectName}`));\n console.log(chalk.white(\" npx expo start\"));\n if (isLoginTabs) {\n console.log(chalk.gray(\" → App starts at login page, sign in to see tabs\"));\n }\n console.log();\n\n if (selectedModuleDefs.length > 0) {\n console.log(chalk.bold(\" 📋 Selected modules:\"));\n for (const mod of selectedModuleDefs) {\n console.log(chalk.white(` ✓ ${mod.name}`));\n }\n console.log();\n }\n } catch (error) {\n spinner.fail(chalk.red(\"Project creation failed\"));\n console.error(error);\n process.exit(1);\n }\n}\n\n// ─── Upgrade Project ─────────────────────────────────────────────────────\n\nexport async function upgradeProject(targetDir: string): Promise<void> {\n console.log();\n console.log(\n chalk.bold.cyan(\" ╔══════════════════════════════════════╗\")\n );\n console.log(\n chalk.bold.cyan(\" ║ expo-bbase — Upgrade ║\")\n );\n console.log(\n chalk.bold.cyan(\" ╚══════════════════════════════════════╝\")\n );\n console.log();\n\n const absDir = path.resolve(targetDir);\n const config = await readProjectConfig(absDir);\n\n if (!config) {\n console.error(\n chalk.red(\n ` ✖ No ${CONFIG_FILE} found in ${absDir}\\n` +\n ` This directory doesn't appear to be an expo-bbase project.\\n` +\n ` If it is, run \"expo-bbase add\" to register modules.`\n )\n );\n process.exit(1);\n }\n\n console.log(\n chalk.white(` 📂 Project: ${chalk.bold(config.projectName)}`)\n );\n console.log(\n chalk.white(` 📋 CLI version: ${chalk.gray(config.cliVersion || \"unknown\")} → ${chalk.green(CLI_VERSION)}`)\n );\n console.log(\n chalk.white(\n ` 🧩 Modules: ${chalk.green(config.selectedModules.join(\", \") || \"none\")}`\n )\n );\n console.log();\n\n const spinner = ora(\"Upgrading project...\").start();\n\n try {\n const selectedModuleDefs = getModulesByIds(config.selectedModules);\n\n // ─── Step 1: Update module files (overwrite) ──────────────────────\n spinner.text = \"Updating module files...\";\n for (const mod of selectedModuleDefs) {\n for (const file of mod.files) {\n const filePath = path.join(absDir, file.path);\n const content = replaceTemplateVars(file.content, {\n projectName: config.projectName,\n });\n await writeFile(filePath, content);\n }\n }\n\n // ─── Step 2: Update dependencies in package.json ─────────────────\n spinner.text = \"Updating dependencies...\";\n // All deps are now in base — just merge the full base deps\n const basePkg = generateBasePackageJson(config.projectName);\n const allDeps = basePkg.dependencies as Record<string, string>;\n const allDevDeps = basePkg.devDependencies as Record<string, string>;\n\n const pkgPath = path.join(absDir, \"package.json\");\n await mergeDependencies(pkgPath, allDeps, allDevDeps);\n\n // ─── Step 3: Update app.json plugins ─────────────────────────────\n spinner.text = \"Updating app.json...\";\n await updateAppJson(absDir, selectedModuleDefs, config.projectName);\n\n // ─── Step 4: Update babel.config.js ───────────────────────────────\n spinner.text = \"Updating Babel config...\";\n await updateBabelConfig(absDir, selectedModuleDefs);\n\n // ─── Step 5: Update _layout.tsx ──────────────────────────────────\n spinner.text = \"Updating layout...\";\n await updateLayoutFile(absDir, selectedModuleDefs);\n\n // ─── Step 6: Update .expo-bbase.json ─────────────────────────────\n config.cliVersion = CLI_VERSION;\n await writeProjectConfig(absDir, config);\n\n // ─── Step 7: yarn install ─────────────────────────────────────────\n spinner.text = \"Installing updated dependencies (yarn install)...\";\n try {\n await execa(\"yarn\", [\"install\"], {\n cwd: absDir,\n timeout: 300_000,\n });\n } catch (installError: unknown) {\n const errMsg =\n installError instanceof Error\n ? installError.message\n : String(installError);\n spinner.warn(\"yarn install failed, please install manually\");\n console.log(chalk.red(` Error: ${errMsg}`));\n }\n\n spinner.succeed(chalk.green(\"Project upgraded!\"));\n\n console.log();\n console.log(chalk.bold(\" 📋 Upgrade summary:\"));\n console.log(chalk.white(` CLI: ${chalk.gray(config.cliVersion)} (before)`));\n console.log(chalk.white(` Modules: ${chalk.green(config.selectedModules.join(\", \"))}`));\n console.log();\n console.log(chalk.bold(\" 🎉 Run your project:\"));\n console.log(chalk.white(\" npx expo start\"));\n console.log();\n } catch (error) {\n spinner.fail(chalk.red(\"Upgrade failed\"));\n console.error(error);\n process.exit(1);\n }\n}\n\n// ─── Add Modules ─────────────────────────────────────────────────────────\n\nexport async function addModule(\n moduleIds: string[],\n targetDir: string\n): Promise<void> {\n console.log();\n\n const absDir = path.resolve(targetDir);\n let config = await readProjectConfig(absDir);\n\n // If no config exists, try to infer from package.json\n if (!config) {\n const pkgPath = path.join(absDir, \"package.json\");\n if (!(await fse.pathExists(pkgPath))) {\n console.error(\n chalk.red(` ✖ No package.json found in ${absDir}`)\n );\n process.exit(1);\n }\n const pkg = await fse.readJson(pkgPath);\n config = {\n projectName: pkg.name || path.basename(absDir),\n selectedModules: [],\n cliVersion: CLI_VERSION,\n };\n console.log(\n chalk.yellow(\n ` ⚠ No ${CONFIG_FILE} found. Creating one for project \"${config.projectName}\".`\n )\n );\n }\n\n // Interactive module selection if no module IDs provided\n if (moduleIds.length === 0) {\n const availableModules = modules.filter(\n (m) => !config!.selectedModules.includes(m.id)\n );\n\n if (availableModules.length === 0) {\n console.log(chalk.green(\" ✓ All modules are already installed!\"));\n process.exit(0);\n }\n\n const choices = availableModules.map((m) => ({\n title: `${chalk.bold(m.name)} — ${chalk.gray(m.description)}`,\n value: m.id,\n selected: false,\n }));\n\n const { selected } = await prompts({\n type: \"multiselect\",\n name: \"selected\",\n message: \"Select modules to add (Space to toggle, Enter to confirm)\",\n choices,\n hint: \"- Space toggle · a select all/none · Enter confirm\",\n instructions: false,\n });\n\n if (selected === undefined || selected.length === 0) {\n console.log(chalk.yellow(\" No modules selected.\"));\n process.exit(0);\n }\n\n moduleIds = selected as string[];\n }\n\n // Validate module IDs\n const invalidIds = moduleIds.filter((id) => !getModuleById(id));\n if (invalidIds.length > 0) {\n console.error(\n chalk.red(` ✖ Unknown module(s): ${invalidIds.join(\", \")}`)\n );\n console.log(\n chalk.gray(\n ` Available: ${modules.map((m) => m.id).join(\", \")}`\n )\n );\n process.exit(1);\n }\n\n // Filter out already installed modules\n const newModuleIds = moduleIds.filter(\n (id) => !config!.selectedModules.includes(id)\n );\n if (newModuleIds.length === 0) {\n console.log(\n chalk.yellow(\" All specified modules are already installed.\")\n );\n process.exit(0);\n }\n\n const newModuleDefs = getModulesByIds(newModuleIds);\n\n console.log(\n chalk.white(` 📂 Project: ${chalk.bold(config.projectName)}`)\n );\n console.log(\n chalk.white(\n ` ➕ Adding: ${chalk.green(newModuleDefs.map((m) => m.name).join(\", \"))}`\n )\n );\n console.log();\n\n const spinner = ora(\"Adding modules...\").start();\n\n try {\n // ─── Step 1: Write module files ───────────────────────────────────\n spinner.text = \"Writing module files...\";\n for (const mod of newModuleDefs) {\n for (const file of mod.files) {\n const filePath = path.join(absDir, file.path);\n const content = replaceTemplateVars(file.content, {\n projectName: config!.projectName,\n });\n await writeFile(filePath, content);\n }\n }\n\n // ─── Step 2: No need to merge deps — all deps are already in base ──\n spinner.text = \"Updating dependencies...\";\n // All module dependencies are included by default in the base package.json.\n // The \"add\" command only needs to write the code/template files.\n\n // ─── Step 3: Update app.json plugins ─────────────────────────────\n spinner.text = \"Updating app.json...\";\n const existingModules = getModulesByIds(config!.selectedModules);\n await updateAppJson(\n absDir,\n [...existingModules, ...newModuleDefs],\n config!.projectName\n );\n\n // ─── Step 4: Update babel.config.js ─────────────────────────────\n spinner.text = \"Updating Babel config...\";\n await updateBabelConfig(absDir, newModuleDefs);\n\n // ─── Step 5: Update _layout.tsx ──────────────────────────────────\n spinner.text = \"Updating layout...\";\n await updateLayoutFile(absDir, newModuleDefs);\n\n // ─── Step 6: Update .expo-bbase.json ─────────────────────────────\n config!.selectedModules.push(...newModuleIds);\n config!.cliVersion = CLI_VERSION;\n await writeProjectConfig(absDir, config!);\n\n // ─── Step 7: yarn install ─────────────────────────────────────────\n spinner.text = \"Installing dependencies (yarn install)...\";\n try {\n await execa(\"yarn\", [\"install\"], {\n cwd: absDir,\n timeout: 300_000,\n });\n } catch (installError: unknown) {\n const errMsg =\n installError instanceof Error\n ? installError.message\n : String(installError);\n spinner.warn(\"yarn install failed, please install manually\");\n console.log(chalk.red(` Error: ${errMsg}`));\n }\n\n spinner.succeed(chalk.green(\"Modules added!\"));\n\n console.log();\n console.log(chalk.bold(\" 📋 Added modules:\"));\n for (const mod of newModuleDefs) {\n console.log(chalk.white(` ✓ ${mod.name} (${mod.id})`));\n }\n console.log();\n console.log(chalk.bold(\" 🧩 All installed modules:\"));\n for (const id of config!.selectedModules) {\n const m = getModuleById(id);\n console.log(chalk.white(` • ${m?.name || id}`));\n }\n console.log();\n } catch (error) {\n spinner.fail(chalk.red(\"Failed to add modules\"));\n console.error(error);\n process.exit(1);\n }\n}\n\n// ─── Shared Helpers ───────────────────────────────────────────────────────\n\n/**\n * Update the generated app.json with plugin configurations from selected modules.\n */\nasync function updateAppJson(\n targetDir: string,\n selectedModules: ModuleDef[],\n _projectName: string\n): Promise<void> {\n const appJsonPath = path.join(targetDir, \"app.json\");\n if (!(await fse.pathExists(appJsonPath))) {\n return;\n }\n const appJson = await fse.readJson(appJsonPath);\n\n const existingPlugins: (string | [string, Record<string, unknown>])[] =\n appJson.expo?.plugins || [];\n\n for (const mod of selectedModules) {\n if (mod.appConfig?.plugins) {\n const modulePlugins = mod.appConfig.plugins as (\n | string\n | [string, Record<string, unknown>]\n )[];\n for (const plugin of modulePlugins) {\n const pluginName =\n typeof plugin === \"string\" ? plugin : plugin[0];\n const exists = existingPlugins.some((p) =>\n typeof p === \"string\" ? p === pluginName : p[0] === pluginName\n );\n if (!exists) {\n existingPlugins.push(plugin);\n }\n }\n }\n }\n\n appJson.expo.plugins = existingPlugins;\n await writeJson(appJsonPath, appJson);\n}\n\n/**\n * Update babel.config.js with additional plugins from selected modules.\n */\nasync function updateBabelConfig(\n targetDir: string,\n selectedModules: ModuleDef[]\n): Promise<void> {\n const babelPath = path.join(targetDir, \"babel.config.js\");\n if (!(await fse.pathExists(babelPath))) {\n return;\n }\n\n let content = await fse.readFile(babelPath, \"utf-8\");\n\n const extraPlugins: string[] = [];\n for (const mod of selectedModules) {\n if (mod.babelPlugins && mod.babelPlugins.length > 0) {\n extraPlugins.push(...mod.babelPlugins);\n }\n }\n\n if (extraPlugins.length > 0) {\n // Filter out plugins that are already in the config\n const pluginsToAdd = extraPlugins.filter((p) => !content.includes(p));\n if (pluginsToAdd.length > 0) {\n const pluginStrings = pluginsToAdd\n .map((p) => ` \"${p}\"`)\n .join(\",\\n\");\n content = content.replace(\n /plugins:\\s*\\[([^\\]]*)\\]/,\n `plugins: [$1${pluginStrings ? \",\\n\" + pluginStrings : \"\"}]`\n );\n await fse.writeFile(babelPath, content, \"utf-8\");\n }\n }\n}\n\n/**\n * Update app/_layout.tsx with imports and providers from selected modules.\n */\nasync function updateLayoutFile(\n targetDir: string,\n selectedModules: ModuleDef[]\n): Promise<void> {\n const layoutPath = path.join(targetDir, \"app/_layout.tsx\");\n if (!(await fse.pathExists(layoutPath))) {\n return;\n }\n\n let content = await fse.readFile(layoutPath, \"utf-8\");\n\n const extraImports: string[] = [];\n const extraProviderPairs: { open: string; close: string }[] = [];\n\n for (const mod of selectedModules) {\n if (mod.layoutImports) {\n // Only add imports that don't already exist\n for (const imp of mod.layoutImports) {\n if (!content.includes(imp)) {\n extraImports.push(imp);\n }\n }\n }\n if (mod.layoutProviders) {\n for (const provider of mod.layoutProviders) {\n const match = provider.match(/^<(\\w+)/);\n if (match) {\n const tagName = match[1];\n // Only add providers that don't already exist\n if (!content.includes(`<${tagName}`)) {\n extraProviderPairs.push({\n open: ` ${provider}`,\n close: ` </${tagName}>`,\n });\n }\n }\n }\n }\n }\n\n if (extraImports.length === 0 && extraProviderPairs.length === 0) {\n return;\n }\n\n if (extraImports.length > 0) {\n const lastImportIndex = content.lastIndexOf(\"import \");\n const lineEndIndex = content.indexOf(\"\\n\", lastImportIndex);\n const importBlock = \"\\n\" + extraImports.join(\"\\n\");\n content =\n content.slice(0, lineEndIndex + 1) +\n importBlock +\n content.slice(lineEndIndex + 1);\n }\n\n if (extraProviderPairs.length > 0) {\n const returnMatch = content.indexOf(\"return (\");\n if (returnMatch !== -1) {\n const openingProviders = extraProviderPairs\n .map((p) => p.open)\n .join(\"\\n\");\n const closingProviders = extraProviderPairs\n .reverse()\n .map((p) => p.close)\n .join(\"\\n\");\n\n const themeProviderOpen = content.indexOf(\"<ThemeProvider\");\n const themeProviderClose = content.lastIndexOf(\"</ThemeProvider>\");\n\n if (themeProviderOpen !== -1 && themeProviderClose !== -1) {\n content =\n content.slice(0, themeProviderOpen) +\n openingProviders +\n \"\\n\" +\n content.slice(themeProviderOpen);\n\n const newThemeProviderClose =\n content.lastIndexOf(\"</ThemeProvider>\");\n const afterClose =\n newThemeProviderClose + \"</ThemeProvider>\".length;\n content =\n content.slice(0, afterClose) +\n \"\\n\" +\n closingProviders +\n content.slice(afterClose);\n }\n }\n }\n\n await fse.writeFile(layoutPath, content, \"utf-8\");\n}\n\n// ─── Run ───────────────────────────────────────────────────────────────────\n\nrun().catch((error) => {\n console.error(chalk.red(\"Fatal error:\"), error);\n process.exit(1);\n});\n","import { Command } from \"commander\";\nimport { createProject } from \"../index\";\nimport { upgradeProject } from \"../index\";\nimport { addModule } from \"../index\";\n\n/**\n * Register the \"create\" command with the CLI program.\n */\nexport function registerCreateCommand(program: Command): void {\n program\n .command(\"create <project-name>\")\n .description(\"Create a new Expo project with selected modules\")\n .action(async (projectName: string) => {\n await createProject(projectName);\n });\n}\n\n/**\n * Register the \"upgrade\" command with the CLI program.\n */\nexport function registerUpgradeCommand(program: Command): void {\n program\n .command(\"upgrade\")\n .description(\"Upgrade an existing expo-bbase project to the latest module versions\")\n .option(\"--dir <path>\", \"Project directory (default: current directory)\", process.cwd())\n .action(async (options: { dir: string }) => {\n await upgradeProject(options.dir);\n });\n}\n\n/**\n * Register the \"add\" command with the CLI program.\n */\nexport function registerAddCommand(program: Command): void {\n program\n .command(\"add [modules...]\")\n .description(\"Add one or more modules to an existing expo-bbase project\")\n .option(\"--dir <path>\", \"Project directory (default: current directory)\", process.cwd())\n .action(async (moduleIds: string[], options: { dir: string }) => {\n await addModule(moduleIds, options.dir);\n });\n}\n","/**\n * Join an array of strings with newlines.\n * This helper avoids template literal escaping issues in module content strings.\n */\nexport function lines(...args: string[]): string {\n return args.join(\"\\n\");\n}\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst networkModule: ModuleDef = {\n id: \"network\",\n name: \"网络请求\",\n description: \"TanStack Query 封装,统一拦截器、错误处理\",\n defaultChecked: true,\n dependencies: {\n \"@tanstack/react-query\": \"^5.60.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"api/client.ts\",\n content: lines(\n 'import { Alert } from \"react-native\";',\n \"\",\n 'const API_BASE_URL = process.env.EXPO_PUBLIC_API_URL || \"https://api.example.com\";',\n \"\",\n \"interface RequestConfig {\",\n ' method: \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";',\n \" url: string;\",\n \" data?: unknown;\",\n \" headers?: Record<string, string>;\",\n \" params?: Record<string, string>;\",\n \"}\",\n \"\",\n \"interface ApiResponse<T> {\",\n \" data: T;\",\n \" message: string;\",\n \" code: number;\",\n \"}\",\n \"\",\n \"class ApiClient {\",\n \" private baseUrl: string;\",\n \" private defaultHeaders: Record<string, string>;\",\n \" private requestInterceptors: Array<(config: RequestConfig) => RequestConfig> = [];\",\n \" private responseInterceptors: Array<(response: Response) => Response> = [];\",\n \"\",\n \" constructor(baseUrl: string) {\",\n \" this.baseUrl = baseUrl;\",\n \" this.defaultHeaders = {\",\n ' \"Content-Type\": \"application/json\",',\n ' Accept: \"application/json\",',\n \" };\",\n \" }\",\n \"\",\n \" /** Add a request interceptor */\",\n \" addRequestInterceptor(interceptor: (config: RequestConfig) => RequestConfig): void {\",\n \" this.requestInterceptors.push(interceptor);\",\n \" }\",\n \"\",\n \" /** Add a response interceptor */\",\n \" addResponseInterceptor(interceptor: (response: Response) => Response): void {\",\n \" this.responseInterceptors.push(interceptor);\",\n \" }\",\n \"\",\n \" /** Set the authorization token */\",\n \" setAuthToken(token: string): void {\",\n ' this.defaultHeaders[\"Authorization\"] = `Bearer ${token}`;',\n \" }\",\n \"\",\n \" /** Clear the authorization token */\",\n \" clearAuthToken(): void {\",\n ' delete this.defaultHeaders[\"Authorization\"];',\n \" }\",\n \"\",\n \" /** Apply request interceptors */\",\n \" private applyRequestInterceptors(config: RequestConfig): RequestConfig {\",\n \" return this.requestInterceptors.reduce(\",\n \" (acc, interceptor) => interceptor(acc),\",\n \" config\",\n \" );\",\n \" }\",\n \"\",\n \" /** Build URL with query params */\",\n \" private buildUrl(url: string, params?: Record<string, string>): string {\",\n \" const fullUrl = new URL(url, this.baseUrl);\",\n \" if (params) {\",\n ' Object.entries(params).forEach(([key, value]) => {',\n \" fullUrl.searchParams.append(key, value);\",\n \" });\",\n \" }\",\n \" return fullUrl.toString();\",\n \" }\",\n \"\",\n \" /** Core request method */\",\n \" async request<T>(config: RequestConfig): Promise<ApiResponse<T>> {\",\n \" const interceptedConfig = this.applyRequestInterceptors(config);\",\n \" const url = this.buildUrl(interceptedConfig.url, interceptedConfig.params);\",\n \"\",\n \" try {\",\n \" const response = await fetch(url, {\",\n \" method: interceptedConfig.method,\",\n \" headers: {\",\n \" ...this.defaultHeaders,\",\n \" ...interceptedConfig.headers,\",\n \" },\",\n \" body: interceptedConfig.data\",\n \" ? JSON.stringify(interceptedConfig.data)\",\n \" : undefined,\",\n \" });\",\n \"\",\n \" if (!response.ok) {\",\n \" const errorData = await response.json().catch(() => null);\",\n \" throw new ApiError(\",\n \" response.status,\",\n \" errorData?.message || response.statusText,\",\n \" errorData\",\n \" );\",\n \" }\",\n \"\",\n \" const data: ApiResponse<T> = await response.json();\",\n \" return data;\",\n \" } catch (error) {\",\n \" if (error instanceof ApiError) {\",\n \" throw error;\",\n \" }\",\n ' throw new ApiError(0, \"网络请求失败,请检查网络连接\", null);',\n \" }\",\n \" }\",\n \"\",\n \" /** GET request */\",\n \" async get<T>(url: string, params?: Record<string, string>): Promise<ApiResponse<T>> {\",\n ' return this.request<T>({ method: \"GET\", url, params });',\n \" }\",\n \"\",\n \" /** POST request */\",\n \" async post<T>(url: string, data?: unknown): Promise<ApiResponse<T>> {\",\n ' return this.request<T>({ method: \"POST\", url, data });',\n \" }\",\n \"\",\n \" /** PUT request */\",\n \" async put<T>(url: string, data?: unknown): Promise<ApiResponse<T>> {\",\n ' return this.request<T>({ method: \"PUT\", url, data });',\n \" }\",\n \"\",\n \" /** PATCH request */\",\n \" async patch<T>(url: string, data?: unknown): Promise<ApiResponse<T>> {\",\n ' return this.request<T>({ method: \"PATCH\", url, data });',\n \" }\",\n \"\",\n \" /** DELETE request */\",\n \" async delete<T>(url: string): Promise<ApiResponse<T>> {\",\n ' return this.request<T>({ method: \"DELETE\", url });',\n \" }\",\n \"}\",\n \"\",\n \"/** Custom API error class */\",\n \"export class ApiError extends Error {\",\n \" code: number;\",\n \" detail: unknown;\",\n \"\",\n \" constructor(code: number, message: string, detail: unknown) {\",\n \" super(message);\",\n ' this.name = \"ApiError\";',\n \" this.code = code;\",\n \" this.detail = detail;\",\n \" }\",\n \"}\",\n \"\",\n \"/** Global error handler */\",\n \"export function handleApiError(error: unknown): void {\",\n \" if (error instanceof ApiError) {\",\n \" if (error.code === 401) {\",\n ' Alert.alert(\"认证失败\", \"请重新登录\");',\n \" } else if (error.code === 403) {\",\n ' Alert.alert(\"权限不足\", \"您没有权限执行此操作\");',\n \" } else if (error.code === 0) {\",\n ' Alert.alert(\"网络错误\", error.message);',\n \" } else {\",\n ' Alert.alert(\"请求失败\", error.message);',\n \" }\",\n \" } else {\",\n ' Alert.alert(\"未知错误\", \"请稍后重试\");',\n \" }\",\n \"}\",\n \"\",\n \"/** Singleton API client instance */\",\n \"export const apiClient = new ApiClient(API_BASE_URL);\",\n \"\",\n \"export default apiClient;\"\n ),\n },\n {\n path: \"api/interceptors.ts\",\n content: lines(\n 'import type { RequestConfig } from \"./client\";',\n 'import { apiClient } from \"./client\";',\n \"\",\n \"/**\",\n \" * Initialize all request interceptors.\",\n \" * Called once during app startup.\",\n \" */\",\n \"export function setupRequestInterceptors(): void {\",\n \" apiClient.addRequestInterceptor((config: RequestConfig) => {\",\n \" return config;\",\n \" });\",\n \"\",\n \" if (__DEV__) {\",\n \" apiClient.addRequestInterceptor((config: RequestConfig) => {\",\n \" console.log(\",\n ' `[API Request] ${config.method} ${config.url}`,',\n ' config.data ? config.data : \"\"',\n \" );\",\n \" return config;\",\n \" });\",\n \" }\",\n \"}\",\n \"\",\n \"/**\",\n \" * Initialize all response interceptors.\",\n \" * Called once during app startup.\",\n \" */\",\n \"export function setupResponseInterceptors(): void {\",\n \" if (__DEV__) {\",\n \" apiClient.addResponseInterceptor((response: Response) => {\",\n ' console.log(`[API Response] ${response.status} ${response.url}`);',\n \" return response;\",\n \" });\",\n \" }\",\n \"}\",\n \"\",\n \"/**\",\n \" * Setup all interceptors at once.\",\n \" */\",\n \"export function setupInterceptors(): void {\",\n \" setupRequestInterceptors();\",\n \" setupResponseInterceptors();\",\n \"}\"\n ),\n },\n {\n path: \"api/types.ts\",\n content: lines(\n \"/** Common API response types */\",\n \"\",\n \"/** Paginated response wrapper */\",\n \"export interface PaginatedResponse<T> {\",\n \" data: T[];\",\n \" total: number;\",\n \" page: number;\",\n \" pageSize: number;\",\n \" totalPages: number;\",\n \"}\",\n \"\",\n \"/** Standard API response */\",\n \"export interface ApiResponse<T> {\",\n \" data: T;\",\n \" message: string;\",\n \" code: number;\",\n \"}\",\n \"\",\n \"/** Common error response */\",\n \"export interface ErrorResponse {\",\n \" message: string;\",\n \" code: number;\",\n \" errors?: Record<string, string[]>;\",\n \"}\",\n \"\",\n \"/** User type example */\",\n \"export interface User {\",\n \" id: string;\",\n \" email: string;\",\n \" name: string;\",\n \" avatar?: string;\",\n \" createdAt: string;\",\n \" updatedAt: string;\",\n \"}\",\n \"\",\n \"/** Auth response */\",\n \"export interface AuthResponse {\",\n \" user: User;\",\n \" token: string;\",\n \" refreshToken: string;\",\n \"}\"\n ),\n },\n {\n path: \"api/queries.ts\",\n content: lines(\n 'import { useQuery, useMutation, useQueryClient } from \"@tanstack/react-query\";',\n 'import { apiClient, handleApiError } from \"./client\";',\n 'import type { ApiResponse } from \"./types\";',\n \"\",\n \"/**\",\n \" * Example query hook for fetching data.\",\n \" */\",\n \"export function useApiQuery<T>(\",\n \" key: string[],\",\n \" url: string,\",\n \" params?: Record<string, string>\",\n \") {\",\n \" return useQuery({\",\n \" queryKey: key,\",\n \" queryFn: () => apiClient.get<T>(url, params),\",\n \" retry: 2,\",\n \" staleTime: 5 * 60 * 1000,\",\n \" });\",\n \"}\",\n \"\",\n \"/**\",\n \" * Example mutation hook for posting data.\",\n \" */\",\n \"export function useApiMutation<TData, TVariables>(\",\n \" url: string,\",\n ' method: \"POST\" | \"PUT\" | \"PATCH\" = \"POST\"',\n \") {\",\n \" const queryClient = useQueryClient();\",\n \"\",\n \" return useMutation({\",\n \" mutationFn: (variables: TVariables) => {\",\n \" switch (method) {\",\n ' case \"PUT\":',\n \" return apiClient.put<TData>(url, variables);\",\n ' case \"PATCH\":',\n \" return apiClient.patch<TData>(url, variables);\",\n \" default:\",\n \" return apiClient.post<TData>(url, variables);\",\n \" }\",\n \" },\",\n \" onSuccess: () => {\",\n \" queryClient.invalidateQueries({ queryKey: [url] });\",\n \" },\",\n \" onError: (error) => {\",\n \" handleApiError(error);\",\n \" },\",\n \" });\",\n \"}\",\n \"\",\n \"/**\",\n \" * Example: Fetch current user profile\",\n \" */\",\n \"export function useUserProfile() {\",\n \" return useApiQuery<ApiResponse<{ name: string; email: string }>>(\",\n ' [\"user\", \"profile\"],',\n ' \"/user/profile\"',\n \" );\",\n \"}\"\n ),\n },\n ],\n layoutProviders: [\"<QueryClientProvider client={queryClient}>\"],\n layoutImports: [\n 'import { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";',\n \"const queryClient = new QueryClient({ defaultOptions: { queries: { retry: 2, staleTime: 5 * 60 * 1000 } } });\",\n ],\n};\n\nexport default networkModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst stateModule: ModuleDef = {\n id: \"state\",\n name: \"状态管理\",\n description: \"Zustand stores 模板,persist 中间件\",\n defaultChecked: true,\n dependencies: {\n zustand: \"^5.0.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"stores/index.ts\",\n content: lines('export { useUserStore } from \"./slices/userSlice\";'),\n },\n {\n path: \"stores/slices/userSlice.ts\",\n content: lines(\n 'import { create } from \"zustand\";',\n 'import { persist, createJSONStorage } from \"zustand/middleware\";',\n \"\",\n \"interface User {\",\n \" id: string;\",\n \" email: string;\",\n \" name: string;\",\n \" avatar?: string;\",\n \"}\",\n \"\",\n \"interface UserState {\",\n \" user: User | null;\",\n \" token: string | null;\",\n \" isAuthenticated: boolean;\",\n \" hasHydrated: boolean;\",\n \"\",\n \" setUser: (user: User, token: string) => void;\",\n \" updateUser: (partial: Partial<User>) => void;\",\n \" clearUser: () => void;\",\n \" setHasHydrated: (value: boolean) => void;\",\n \"}\",\n \"\",\n \"export const useUserStore = create<UserState>()(\",\n \" persist(\",\n \" (set, get) => ({\",\n \" user: null,\",\n \" token: null,\",\n \" isAuthenticated: false,\",\n \" hasHydrated: false,\",\n \"\",\n \" setUser: (user: User, token: string) => {\",\n \" set({ user, token, isAuthenticated: true });\",\n \" },\",\n \"\",\n \" updateUser: (partial: Partial<User>) => {\",\n \" const currentUser = get().user;\",\n \" if (currentUser) {\",\n \" set({ user: { ...currentUser, ...partial } });\",\n \" }\",\n \" },\",\n \"\",\n \" clearUser: () => {\",\n \" set({ user: null, token: null, isAuthenticated: false });\",\n \" },\",\n \"\",\n \" setHasHydrated: (value: boolean) => {\",\n \" set({ hasHydrated: value });\",\n \" },\",\n \" }),\",\n \" {\",\n ' name: \"user-store\",',\n \" storage: createJSONStorage(() => {\",\n \" try {\",\n ' const { MMKV } = require(\"react-native-mmkv\");',\n \" const storage = new MMKV();\",\n \" return {\",\n \" getItem: (name: string) => {\",\n \" const value = storage.getString(name);\",\n \" return value ?? null;\",\n \" },\",\n \" setItem: (name: string, value: string) => {\",\n \" storage.set(name, value);\",\n \" },\",\n \" removeItem: (name: string) => {\",\n \" storage.delete(name);\",\n \" },\",\n \" };\",\n \" } catch {\",\n \" return {\",\n \" getItem: () => null,\",\n \" setItem: () => {},\",\n \" removeItem: () => {},\",\n \" };\",\n \" }\",\n \" }),\",\n \" partialize: (state) => ({\",\n \" user: state.user,\",\n \" token: state.token,\",\n \" isAuthenticated: state.isAuthenticated,\",\n \" }),\",\n \" onRehydrateStorage: () => (state) => {\",\n \" state?.setHasHydrated(true);\",\n \" },\",\n \" }\",\n \" )\",\n \");\"\n ),\n },\n ],\n};\n\nexport default stateModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst storageModule: ModuleDef = {\n id: \"storage\",\n name: \"本地存储\",\n description: \"MMKV 封装,类型安全 API\",\n defaultChecked: true,\n dependencies: {\n \"react-native-mmkv\": \"^3.1.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"storage/index.ts\",\n content: lines(\n 'import { MMKV } from \"react-native-mmkv\";',\n \"\",\n \"/**\",\n \" * Centralized MMKV storage instance.\",\n \" */\",\n \"const storage = new MMKV({\",\n ' id: \"app-storage\",',\n \" encryptionKey: process.env.EXPO_PUBLIC_STORAGE_KEY || undefined,\",\n \"});\",\n \"\",\n \"/** Storage keys */\",\n \"export const StorageKeys = {\",\n ' AUTH_TOKEN: \"auth_token\",',\n ' REFRESH_TOKEN: \"refresh_token\",',\n ' USER_DATA: \"user_data\",',\n ' THEME: \"theme\",',\n ' LANGUAGE: \"language\",',\n ' ONBOARDING_COMPLETED: \"onboarding_completed\",',\n ' LAST_SYNC_TIME: \"last_sync_time\",',\n ' CACHE_VERSION: \"cache_version\",',\n \"} as const;\",\n \"\",\n \"export type StorageKey = (typeof StorageKeys)[keyof typeof StorageKeys];\",\n \"\",\n \"export function getString(key: StorageKey): string | null {\",\n \" return storage.getString(key) ?? null;\",\n \"}\",\n \"\",\n \"export function setString(key: StorageKey, value: string): void {\",\n \" storage.set(key, value);\",\n \"}\",\n \"\",\n \"export function getNumber(key: StorageKey): number | null {\",\n \" const value = storage.getNumber(key);\",\n \" return value ?? null;\",\n \"}\",\n \"\",\n \"export function setNumber(key: StorageKey, value: number): void {\",\n \" storage.set(key, value);\",\n \"}\",\n \"\",\n \"export function getBoolean(key: StorageKey): boolean | null {\",\n \" const value = storage.getBoolean(key);\",\n \" return value ?? null;\",\n \"}\",\n \"\",\n \"export function setBoolean(key: StorageKey, value: boolean): void {\",\n \" storage.set(key, value);\",\n \"}\",\n \"\",\n \"export function getJson<T>(key: StorageKey): T | null {\",\n \" const raw = storage.getString(key);\",\n \" if (raw === undefined) return null;\",\n \" try {\",\n \" return JSON.parse(raw) as T;\",\n \" } catch {\",\n \" return null;\",\n \" }\",\n \"}\",\n \"\",\n \"export function setJson<T>(key: StorageKey, value: T): void {\",\n \" storage.set(key, JSON.stringify(value));\",\n \"}\",\n \"\",\n \"export function remove(key: StorageKey): void {\",\n \" storage.delete(key);\",\n \"}\",\n \"\",\n \"export function contains(key: StorageKey): boolean {\",\n \" return storage.contains(key);\",\n \"}\",\n \"\",\n \"export function clearAll(): void {\",\n \" storage.clearAll();\",\n \"}\",\n \"\",\n \"export function getAllKeys(): string[] {\",\n \" return storage.getAllKeys();\",\n \"}\",\n \"\",\n \"export default storage;\"\n ),\n },\n ],\n};\n\nexport default storageModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst paymentModule: ModuleDef = {\n id: \"payment\",\n name: \"Apple/iOS 支付\",\n description: \"react-native-iap 封装\",\n defaultChecked: false,\n dependencies: {\n \"react-native-iap\": \"^12.15.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"modules/payment/index.ts\",\n content: lines(\n 'export { usePayment } from \"./usePayment\";',\n 'export type { ProductInfo, PurchaseResult, PaymentError } from \"./types\";'\n ),\n },\n {\n path: \"modules/payment/types.ts\",\n content: lines(\n 'import type { Product, Purchase } from \"react-native-iap\";',\n \"\",\n \"export interface ProductInfo {\",\n \" productId: string;\",\n \" title: string;\",\n \" description: string;\",\n \" price: string;\",\n \" currency: string;\",\n \"}\",\n \"\",\n \"export interface PurchaseResult {\",\n \" success: boolean;\",\n \" purchase?: Purchase;\",\n \" error?: PaymentError;\",\n \"}\",\n \"\",\n \"export interface PaymentError {\",\n \" code: string;\",\n \" message: string;\",\n \" nativeError?: unknown;\",\n \"}\",\n \"\",\n \"export interface PaymentConfig {\",\n \" productIds: string[];\",\n \" subscriptionIds?: string[];\",\n \"}\"\n ),\n },\n {\n path: \"modules/payment/usePayment.ts\",\n content: lines(\n 'import { useCallback, useEffect, useRef, useState } from \"react\";',\n 'import {',\n \" initConnection,\",\n \" endConnection,\",\n \" getProducts,\",\n \" requestPurchase,\",\n \" requestSubscription,\",\n \" finishTransaction,\",\n \" purchaseUpdatedListener,\",\n \" purchaseErrorListener,\",\n \" Product,\",\n \" Purchase,\",\n \" PurchaseError,\",\n '} from \"react-native-iap\";',\n 'import type { PaymentConfig, ProductInfo, PurchaseResult, PaymentError as AppPaymentError } from \"./types\";',\n \"\",\n \"export function usePayment(config: PaymentConfig) {\",\n \" const [products, setProducts] = useState<ProductInfo[]>([]);\",\n \" const [isLoading, setIsLoading] = useState(false);\",\n \" const [isConnected, setIsConnected] = useState(false);\",\n \" const purchaseUpdateSubscription = useRef<ReturnType<typeof purchaseUpdatedListener> | null>(null);\",\n \" const purchaseErrorSubscription = useRef<ReturnType<typeof purchaseErrorListener> | null>(null);\",\n \"\",\n \" const initialize = useCallback(async () => {\",\n \" try {\",\n \" setIsLoading(true);\",\n \" const connected = await initConnection();\",\n \" setIsConnected(connected);\",\n \"\",\n \" if (connected) {\",\n \" const allIds = [...config.productIds, ...(config.subscriptionIds || [])];\",\n \" if (allIds.length > 0) {\",\n ' const fetchedProducts = await getProducts({ skus: allIds });',\n \" const mapped = fetchedProducts.map(mapProductToInfo);\",\n \" setProducts(mapped);\",\n \" }\",\n \" }\",\n \" } catch (error) {\",\n ' console.error(\"[Payment] Initialization failed:\", error);',\n \" } finally {\",\n \" setIsLoading(false);\",\n \" }\",\n \" }, [config.productIds, config.subscriptionIds]);\",\n \"\",\n \" const handlePurchase = useCallback(\",\n \" async (purchase: Purchase): Promise<void> => {\",\n \" const receipt = purchase.transactionReceipt;\",\n \" if (receipt) {\",\n \" // TODO: Validate receipt with your backend server\",\n \" await finishTransaction({ purchase, isConsumable: false });\",\n \" }\",\n \" },\",\n \" []\",\n \" );\",\n \"\",\n \" const buyProduct = useCallback(\",\n \" async (productId: string): Promise<PurchaseResult> => {\",\n \" try {\",\n ' const purchase = await requestPurchase({ sku: productId });',\n \" if (purchase && purchase.length > 0) {\",\n \" await handlePurchase(purchase[0]);\",\n \" return { success: true, purchase: purchase[0] };\",\n \" }\",\n \" return { success: false };\",\n \" } catch (error) {\",\n \" const paymentError = mapError(error);\",\n \" return { success: false, error: paymentError };\",\n \" }\",\n \" },\",\n \" [handlePurchase]\",\n \" );\",\n \"\",\n \" const buySubscription = useCallback(\",\n \" async (subscriptionId: string): Promise<PurchaseResult> => {\",\n \" try {\",\n ' const purchase = await requestSubscription({ sku: subscriptionId });',\n \" if (purchase && purchase.length > 0) {\",\n \" await handlePurchase(purchase[0]);\",\n \" return { success: true, purchase: purchase[0] };\",\n \" }\",\n \" return { success: false };\",\n \" } catch (error) {\",\n \" const paymentError = mapError(error);\",\n \" return { success: false, error: paymentError };\",\n \" }\",\n \" },\",\n \" [handlePurchase]\",\n \" );\",\n \"\",\n \" useEffect(() => {\",\n \" initialize();\",\n \"\",\n \" purchaseUpdateSubscription.current = purchaseUpdatedListener(\",\n \" async (purchase: Purchase) => {\",\n ' console.log(\"[Payment] Purchase updated:\", purchase.transactionId);',\n \" await handlePurchase(purchase);\",\n \" }\",\n \" );\",\n \"\",\n \" purchaseErrorSubscription.current = purchaseErrorListener(\",\n \" (error: PurchaseError) => {\",\n ' console.error(\"[Payment] Purchase error:\", error.message);',\n \" }\",\n \" );\",\n \"\",\n \" return () => {\",\n \" purchaseUpdateSubscription.current?.remove();\",\n \" purchaseErrorSubscription.current?.remove();\",\n \" endConnection();\",\n \" };\",\n \" }, [initialize, handlePurchase]);\",\n \"\",\n \" return {\",\n \" products,\",\n \" isLoading,\",\n \" isConnected,\",\n \" buyProduct,\",\n \" buySubscription,\",\n \" refresh: initialize,\",\n \" };\",\n \"}\",\n \"\",\n \"function mapProductToInfo(product: Product): ProductInfo {\",\n \" return {\",\n \" productId: product.productId,\",\n \" title: product.title,\",\n \" description: product.description,\",\n \" price: product.price,\",\n ' currency: product.currency || \"USD\",',\n \" };\",\n \"}\",\n \"\",\n \"function mapError(error: unknown): AppPaymentError {\",\n \" if (error instanceof Error) {\",\n \" return {\",\n ' code: \"PURCHASE_ERROR\",',\n \" message: error.message,\",\n \" };\",\n \" }\",\n \" return {\",\n ' code: \"UNKNOWN_ERROR\",',\n ' message: \"An unknown error occurred\",',\n \" };\",\n \"}\"\n ),\n },\n ],\n};\n\nexport default paymentModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst formModule: ModuleDef = {\n id: \"form\",\n name: \"表单验证\",\n description: \"React Hook Form + Zod\",\n defaultChecked: false,\n dependencies: {\n \"react-hook-form\": \"^7.54.0\",\n \"@hookform/resolvers\": \"^3.9.0\",\n zod: \"^3.24.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"modules/form/index.ts\",\n content: lines(\n 'export { useForm } from \"react-hook-form\";',\n 'export { z } from \"zod\";',\n 'export { zodResolver } from \"@hookform/resolvers/zod\";',\n 'export * from \"./schemas/auth\";'\n ),\n },\n {\n path: \"modules/form/schemas/auth.ts\",\n content: lines(\n 'import { z } from \"zod\";',\n \"\",\n \"export const loginSchema = z.object({\",\n \" email: z\",\n ' .string()',\n ' .min(1, \"请输入邮箱地址\")',\n ' .email(\"请输入有效的邮箱地址\"),',\n \" password: z\",\n ' .string()',\n ' .min(6, \"密码至少6位\")',\n ' .max(50, \"密码最多50位\"),',\n \"});\",\n \"\",\n \"export const registerSchema = z\",\n \" .object({\",\n \" name: z\",\n ' .string()',\n ' .min(2, \"姓名至少2个字符\")',\n ' .max(50, \"姓名最多50个字符\"),',\n \" email: z\",\n ' .string()',\n ' .min(1, \"请输入邮箱地址\")',\n ' .email(\"请输入有效的邮箱地址\"),',\n \" password: z\",\n ' .string()',\n ' .min(6, \"密码至少6位\")',\n ' .max(50, \"密码最多50位\"),',\n ' confirmPassword: z.string().min(1, \"请确认密码\"),',\n \" })\",\n \" .refine((data) => data.password === data.confirmPassword, {\",\n ' message: \"两次密码输入不一致\",',\n ' path: [\"confirmPassword\"],',\n \" });\",\n \"\",\n \"export const resetPasswordSchema = z.object({\",\n \" email: z\",\n ' .string()',\n ' .min(1, \"请输入邮箱地址\")',\n ' .email(\"请输入有效的邮箱地址\"),',\n \"});\",\n \"\",\n \"export const changePasswordSchema = z\",\n \" .object({\",\n ' currentPassword: z.string().min(1, \"请输入当前密码\"),',\n \" newPassword: z\",\n ' .string()',\n ' .min(6, \"新密码至少6位\")',\n ' .max(50, \"新密码最多50位\"),',\n ' confirmPassword: z.string().min(1, \"请确认新密码\"),',\n \" })\",\n \" .refine((data) => data.newPassword === data.confirmPassword, {\",\n ' message: \"两次密码输入不一致\",',\n ' path: [\"confirmPassword\"],',\n \" });\",\n \"\",\n \"export type LoginFormData = z.infer<typeof loginSchema>;\",\n \"export type RegisterFormData = z.infer<typeof registerSchema>;\",\n \"export type ResetPasswordFormData = z.infer<typeof resetPasswordSchema>;\",\n \"export type ChangePasswordFormData = z.infer<typeof changePasswordSchema>;\"\n ),\n },\n ],\n};\n\nexport default formModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst imageModule: ModuleDef = {\n id: \"image\",\n name: \"图片\",\n description: \"expo-image 封装\",\n defaultChecked: false,\n dependencies: {\n \"expo-image\": \"~3.0.11\",\n },\n devDependencies: {},\n files: [\n {\n path: \"modules/media/CachedImage.tsx\",\n content: lines(\n 'import React, { useMemo } from \"react\";',\n 'import { Image, type ImageProps } from \"expo-image\";',\n 'import { StyleSheet, View, ActivityIndicator } from \"react-native\";',\n \"\",\n \"interface CachedImageProps extends Omit<ImageProps, \\\"source\\\"> {\",\n \" uri: string;\",\n \" width?: number;\",\n \" height?: number;\",\n \" borderRadius?: number;\",\n ' placeholderColor?: string;',\n \" showLoading?: boolean;\",\n \"}\",\n \"\",\n \"export function CachedImage({\",\n \" uri,\",\n \" width,\",\n \" height,\",\n \" borderRadius,\",\n ' placeholderColor = \"#f0f0f0\",',\n \" showLoading = true,\",\n \" style,\",\n \" ...rest\",\n \"}: CachedImageProps) {\",\n \" const containerStyle = useMemo(\",\n \" () =>\",\n \" StyleSheet.compose(\",\n \" {\",\n \" width,\",\n \" height,\",\n \" borderRadius,\",\n \" backgroundColor: placeholderColor,\",\n \" overflow: \\\"hidden\\\",\",\n \" },\",\n \" style as any\",\n \" ),\",\n \" [width, height, borderRadius, placeholderColor, style]\",\n \" );\",\n \"\",\n \" return (\",\n \" <View style={containerStyle}>\",\n \" <Image\",\n \" source={{ uri }}\",\n \" style={StyleSheet.absoluteFillObject}\",\n ' contentFit=\"cover\"',\n \" transition={200}\",\n \" placeholder={{\",\n ' blurhash: \"LEHV6nWB2yk8pyo0adR*.7kCMdnj\",',\n \" }}\",\n \" {...rest}\",\n \" />\",\n \" {showLoading && (\",\n \" <Image.LoadingIndicatorContainer\",\n \" style={StyleSheet.absoluteFillObject}\",\n \" >\",\n ' <ActivityIndicator color=\"#999\" />',\n \" </Image.LoadingIndicatorContainer>\",\n \" )}\",\n \" </View>\",\n \" );\",\n \"}\",\n \"\",\n \"export default CachedImage;\"\n ),\n },\n ],\n};\n\nexport default imageModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst videoModule: ModuleDef = {\n id: \"video\",\n name: \"视频\",\n description: \"expo-av 封装\",\n defaultChecked: false,\n dependencies: {\n \"expo-av\": \"~16.0.8\",\n },\n devDependencies: {},\n files: [\n {\n path: \"modules/media/VideoPlayer.tsx\",\n content: lines(\n 'import React, { useCallback, useRef, useState } from \"react\";',\n 'import {',\n \" Video,\",\n \" ResizeMode,\",\n \" type AVPlaybackStatus,\",\n \" type VideoProps,\",\n '} from \"expo-av\";',\n 'import { StyleSheet, View, TouchableOpacity, ActivityIndicator } from \"react-native\";',\n \"\",\n \"interface VideoPlayerProps extends Omit<VideoProps, \\\"source\\\"> {\",\n \" uri: string;\",\n \" autoPlay?: boolean;\",\n \" loop?: boolean;\",\n \" showControls?: boolean;\",\n \" width?: number;\",\n \" height?: number;\",\n \"}\",\n \"\",\n \"export function VideoPlayer({\",\n \" uri,\",\n \" autoPlay = false,\",\n \" loop = false,\",\n \" showControls = true,\",\n \" width,\",\n \" height,\",\n \" style,\",\n \" ...rest\",\n \"}: VideoPlayerProps) {\",\n \" const videoRef = useRef<Video>(null);\",\n \" const [isPlaying, setIsPlaying] = useState(autoPlay);\",\n \" const [isLoading, setIsLoading] = useState(true);\",\n \"\",\n \" const handlePlaybackStatusUpdate = useCallback(\",\n \" (status: AVPlaybackStatus) => {\",\n \" if (status.isLoaded) {\",\n \" setIsLoading(status.isBuffering);\",\n \" setIsPlaying(status.isPlaying);\",\n \" }\",\n \" },\",\n \" []\",\n \" );\",\n \"\",\n \" const togglePlayback = useCallback(async () => {\",\n \" if (!videoRef.current) return;\",\n \" if (isPlaying) {\",\n \" await videoRef.current.pauseAsync();\",\n \" } else {\",\n \" await videoRef.current.playAsync();\",\n \" }\",\n \" }, [isPlaying]);\",\n \"\",\n \" const containerStyle = StyleSheet.compose(\",\n ' { width, height, backgroundColor: \"#000\", borderRadius: 8, overflow: \"hidden\" },',\n \" style as any\",\n \" );\",\n \"\",\n \" return (\",\n \" <View style={containerStyle}>\",\n \" <Video\",\n \" ref={videoRef}\",\n \" source={{ uri }}\",\n \" style={StyleSheet.absoluteFillObject}\",\n \" resizeMode={ResizeMode.CONTAIN}\",\n \" shouldPlay={autoPlay}\",\n \" isLooping={loop}\",\n \" onPlaybackStatusUpdate={handlePlaybackStatusUpdate}\",\n \" {...rest}\",\n \" />\",\n \"\",\n \" {isLoading && (\",\n ' <View style={styles.overlay}>',\n ' <ActivityIndicator color=\"#fff\" size=\"large\" />',\n \" </View>\",\n \" )}\",\n \"\",\n \" {showControls && !isLoading && (\",\n \" <TouchableOpacity\",\n \" style={styles.overlay}\",\n \" onPress={togglePlayback}\",\n \" activeOpacity={0.7}\",\n \" />\",\n \" )}\",\n \" </View>\",\n \" );\",\n \"}\",\n \"\",\n \"const styles = StyleSheet.create({\",\n \" overlay: {\",\n \" ...StyleSheet.absoluteFillObject,\",\n ' justifyContent: \"center\",',\n ' alignItems: \"center\",',\n ' backgroundColor: \"rgba(0, 0, 0, 0.3)\",',\n \" },\",\n \"});\",\n \"\",\n \"export default VideoPlayer;\"\n ),\n },\n ],\n};\n\nexport default videoModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst authGoogleModule: ModuleDef = {\n id: \"auth-google\",\n name: \"Google 登录\",\n description: \"@react-native-google-signin/google-signin\",\n defaultChecked: false,\n dependencies: {\n \"@react-native-google-signin/google-signin\": \"^13.1.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"modules/auth/google.ts\",\n content: lines(\n 'import {',\n \" GoogleSignin,\",\n \" statusCodes,\",\n \" type User,\",\n '} from \"@react-native-google-signin/google-signin\";',\n \"\",\n \"export interface GoogleAuthConfig {\",\n \" iosClientId?: string;\",\n \" webClientId?: string;\",\n \" offlineAccess?: boolean;\",\n \" forceCodeForRefreshToken?: boolean;\",\n \"}\",\n \"\",\n \"export function setupGoogleAuth(config: GoogleAuthConfig): void {\",\n \" GoogleSignin.configure({\",\n \" iosClientId: config.iosClientId,\",\n \" webClientId: config.webClientId,\",\n \" offlineAccess: config.offlineAccess ?? false,\",\n \" forceCodeForRefreshToken: config.forceCodeForRefreshToken ?? true,\",\n \" });\",\n \"}\",\n \"\",\n \"export async function signInWithGoogle(): Promise<{\",\n \" user: User | null;\",\n \" idToken: string | null;\",\n \" error: string | null;\",\n \"}> {\",\n \" try {\",\n \" await GoogleSignin.hasPlayServices({\",\n \" showPlayServicesUpdateDialog: true,\",\n \" });\",\n \"\",\n \" const userInfo = await GoogleSignin.signIn();\",\n \" const idToken = userInfo.data?.idToken ?? null;\",\n \"\",\n \" return {\",\n \" user: userInfo.data ?? null,\",\n \" idToken,\",\n \" error: null,\",\n \" };\",\n \" } catch (error: any) {\",\n ' let errorMessage = \"Google 登录失败\";',\n \"\",\n \" if (error.code === statusCodes.SIGN_IN_CANCELLED) {\",\n ' errorMessage = \"用户取消了登录\";',\n \" } else if (error.code === statusCodes.IN_PROGRESS) {\",\n ' errorMessage = \"登录正在进行中\";',\n \" } else if (error.code === statusCodes.PLAY_SERVICES_NOT_AVAILABLE) {\",\n ' errorMessage = \"设备不支持 Google Play 服务\";',\n \" }\",\n \"\",\n ' console.error(\"[GoogleAuth] Error:\", errorMessage, error);',\n \" return { user: null, idToken: null, error: errorMessage };\",\n \" }\",\n \"}\",\n \"\",\n \"export async function signOutGoogle(): Promise<void> {\",\n \" try {\",\n \" await GoogleSignin.signOut();\",\n \" } catch (error) {\",\n ' console.error(\"[GoogleAuth] Sign out error:\", error);',\n \" }\",\n \"}\",\n \"\",\n \"export async function getCurrentGoogleUser(): Promise<User | null> {\",\n \" try {\",\n \" const user = await GoogleSignin.getCurrentUser();\",\n \" return user;\",\n \" } catch {\",\n \" return null;\",\n \" }\",\n \"}\",\n \"\",\n \"export async function isGoogleSignedIn(): Promise<boolean> {\",\n \" try {\",\n \" return GoogleSignin.hasPreviousSignIn();\",\n \" } catch {\",\n \" return false;\",\n \" }\",\n \"}\"\n ),\n },\n ],\n};\n\nexport default authGoogleModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst authFacebookModule: ModuleDef = {\n id: \"auth-facebook\",\n name: \"Facebook 登录\",\n description: \"expo-auth-session + expo-web-browser(SDK 54 不再支持 expo-facebook)\",\n defaultChecked: false,\n dependencies: {\n \"expo-auth-session\": \"~7.0.11\",\n \"expo-web-browser\": \"~15.0.11\",\n \"expo-crypto\": \"~15.0.9\",\n },\n devDependencies: {},\n files: [\n {\n path: \"modules/auth/facebook.ts\",\n content: lines(\n 'import { responseTypes } from \"expo-auth-session\";',\n 'import { makeRedirectUri, useAuthRequest, useAutoDiscovery } from \"expo-auth-session\";',\n 'import * as WebBrowser from \"expo-web-browser\";',\n \"\",\n \"// Facebook OAuth 配置\",\n \"export interface FacebookAuthConfig {\",\n \" clientId: string; // Facebook App ID\",\n \"}\",\n \"\",\n \"// Facebook OAuth 端点\",\n \"const FACEBOOK_AUTH_ENDPOINT = \\\"https://www.facebook.com/v18.0/dialog/oauth\\\";\",\n \"const FACEBOOK_TOKEN_ENDPOINT = \\\"https://graph.facebook.com/v18.0/oauth/access_token\\\";\",\n \"\",\n \"// 确保 WebBrowser 回调完成时关闭\",\n \"WebBrowser.maybeCompleteAuthSession();\",\n \"\",\n \"/**\",\n \" * 使用 expo-auth-session 实现 Facebook 登录\",\n \" *\",\n \" * 用法:\",\n \" * 1. 在 Facebook Developer Console 创建应用,获取 App ID\",\n \" * 2. 配置 OAuth 重定向 URI\",\n \" * 3. 在组件中使用 useFacebookAuth hook\",\n \" *\",\n \" * 注意:SDK 54 不再支持 expo-facebook,改用 expo-auth-session\",\n \" */\",\n \"\",\n \"export function useFacebookAuth(config: FacebookAuthConfig) {\",\n \" const redirectUri = makeRedirectUri({\",\n \" scheme: 'your-app-scheme', // 替换为你的 app scheme\",\n \" });\",\n \"\",\n \" const [request, response, promptAsync] = useAuthRequest(\",\n \" {\",\n \" clientId: config.clientId,\",\n \" scopes: ['public_profile', 'email'],\",\n \" redirectUri,\",\n \" },\",\n \" {\",\n \" authorizationEndpoint: FACEBOOK_AUTH_ENDPOINT,\",\n \" tokenEndpoint: FACEBOOK_TOKEN_ENDPOINT,\",\n \" }\",\n \" );\",\n \"\",\n \" return { request, response, promptAsync };\",\n \"}\",\n \"\",\n \"/**\",\n \" * 从 Facebook OAuth 响应中提取用户信息\",\n \" */\",\n \"export async function getFacebookUserInfo(accessToken: string): Promise<{\",\n \" userId: string;\",\n \" userName: string;\",\n \" email: string | null;\",\n \"}> {\",\n \" const response = await fetch(\",\n \" `https://graph.facebook.com/me?access_token=${accessToken}&fields=id,name,email`\",\n \" );\",\n \" const userData = await response.json();\",\n \"\",\n \" return {\",\n \" userId: userData.id,\",\n \" userName: userData.name,\",\n \" email: userData.email ?? null,\",\n \" };\",\n \"}\",\n ),\n },\n ],\n appConfig: {},\n};\n\nexport default authFacebookModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst authAppleModule: ModuleDef = {\n id: \"auth-apple\",\n name: \"Apple 登录\",\n description: \"expo-apple-authentication\",\n defaultChecked: false,\n dependencies: {\n \"expo-apple-authentication\": \"~8.0.8\",\n },\n devDependencies: {},\n files: [\n {\n path: \"modules/auth/apple.ts\",\n content: lines(\n 'import * as AppleAuthentication from \"expo-apple-authentication\";',\n 'import { Platform } from \"react-native\";',\n \"\",\n \"export interface AppleAuthResult {\",\n \" user: string;\",\n \" email: string | null;\",\n \" fullName: AppleAuthentication.AppleAuthenticationFullName | null;\",\n \" identityToken: string | null;\",\n \" authorizationCode: string | null;\",\n \" error: string | null;\",\n \"}\",\n \"\",\n \"export async function isAppleAuthAvailable(): Promise<boolean> {\",\n ' if (Platform.OS !== \"ios\") return false;',\n \" return AppleAuthentication.isAvailableAsync();\",\n \"}\",\n \"\",\n \"export async function signInWithApple(): Promise<AppleAuthResult> {\",\n \" try {\",\n \" const credential = await AppleAuthentication.signInAsync({\",\n \" requestedScopes: [\",\n \" AppleAuthentication.AppleAuthenticationScope.FULL_NAME,\",\n \" AppleAuthentication.AppleAuthenticationScope.EMAIL,\",\n \" ],\",\n \" });\",\n \"\",\n \" return {\",\n \" user: credential.user,\",\n \" email: credential.email ?? null,\",\n \" fullName: credential.fullName ?? null,\",\n \" identityToken: credential.identityToken ?? null,\",\n \" authorizationCode: credential.authorizationCode ?? null,\",\n \" error: null,\",\n \" };\",\n \" } catch (error: any) {\",\n ' if (error.code === \"ERR_CANCELED\") {',\n \" return {\",\n ' user: \"\",',\n \" email: null,\",\n \" fullName: null,\",\n \" identityToken: null,\",\n \" authorizationCode: null,\",\n ' error: \"用户取消了 Apple 登录\",',\n \" };\",\n \" }\",\n \"\",\n ' console.error(\"[AppleAuth] Error:\", error);',\n \" return {\",\n ' user: \"\",',\n \" email: null,\",\n \" fullName: null,\",\n \" identityToken: null,\",\n \" authorizationCode: null,\",\n ' error: \"Apple 登录失败\",',\n \" };\",\n \" }\",\n \"}\",\n \"\",\n \"export async function getAppleCredentialState(\",\n \" userId: string\",\n \"): Promise<AppleAuthentication.AppleAuthenticationCredentialState> {\",\n \" return AppleAuthentication.getCredentialStateAsync(userId);\",\n \"}\",\n \"\",\n \"export async function signOutApple(userId: string): Promise<boolean> {\",\n \" try {\",\n \" const state = await getAppleCredentialState(userId);\",\n \" return state === AppleAuthentication.AppleAuthenticationCredentialState.REVOKED;\",\n \" } catch {\",\n \" return false;\",\n \" }\",\n \"}\"\n ),\n },\n ],\n appConfig: {\n plugins: [\"expo-apple-authentication\"],\n },\n};\n\nexport default authAppleModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst webviewModule: ModuleDef = {\n id: \"webview\",\n name: \"WebView 容器\",\n description: \"react-native-webview + JS Bridge\",\n defaultChecked: false,\n dependencies: {\n \"react-native-webview\": \"~13.15.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"modules/webview/WebViewContainer.tsx\",\n content: lines(\n 'import React, { useCallback, useRef } from \"react\";',\n 'import { View, StyleSheet, ActivityIndicator } from \"react-native\";',\n 'import WebView, {',\n \" type WebViewMessageEvent,\",\n \" type WebViewProps,\",\n '} from \"react-native-webview\";',\n 'import { bridge, type BridgeMessage } from \"./bridge\";',\n \"\",\n \"interface WebViewContainerProps extends Omit<WebViewProps, \\\"source\\\"> {\",\n \" uri: string;\",\n \" showLoading?: boolean;\",\n \" onBridgeMessage?: (message: BridgeMessage) => void;\",\n \"}\",\n \"\",\n \"export function WebViewContainer({\",\n \" uri,\",\n \" showLoading = true,\",\n \" onBridgeMessage,\",\n \" style,\",\n \" ...rest\",\n \"}: WebViewContainerProps) {\",\n \" const webViewRef = useRef<WebView>(null);\",\n \"\",\n \" const handleMessage = useCallback(\",\n \" (event: WebViewMessageEvent) => {\",\n \" try {\",\n \" const data = JSON.parse(event.nativeEvent.data) as BridgeMessage;\",\n \" const processed = bridge.receive(data);\",\n \" if (onBridgeMessage) {\",\n \" onBridgeMessage(processed);\",\n \" }\",\n \" } catch (error) {\",\n ' console.warn(\"[WebView] Failed to parse message:\", error);',\n \" }\",\n \" },\",\n \" [onBridgeMessage]\",\n \" );\",\n \"\",\n \" const sendToWebView = useCallback(\",\n \" (message: BridgeMessage) => {\",\n \" const script = bridge.buildSendScript(message);\",\n \" webViewRef.current?.injectJavaScript(script);\",\n \" },\",\n \" []\",\n \" );\",\n \"\",\n \" const injectedJavaScript = [\",\n \" \\\"(function() {\\\",\",\n \" \\\" window.ReactNativeWebView = window.ReactNativeWebView || {};\\\",\",\n \" \\\" window.NativeBridge = {\\\",\",\n \" \\\" send: function(type, payload) {\\\",\",\n ' \" window.ReactNativeWebView.postMessage(JSON.stringify({ type: type, payload: payload }));\",',\n \" \\\" }\\\",\\,\",\n \" \\\" };\\,\",\n \" \\\"})();\\,\",\n \" \\\"true;\\,\",\n \" ].join(\\\"\\\\\\\\n\\\");\",\n \"\",\n \" return (\",\n \" <View style={styles.container}>\",\n \" <WebView\",\n \" ref={webViewRef}\",\n \" source={{ uri }}\",\n \" style={[styles.webview, style]}\",\n ' originWhitelist={[\"*\"]}',\n \" javaScriptEnabled\",\n \" domStorageEnabled\",\n \" injectedJavaScript={injectedJavaScript}\",\n \" onMessage={handleMessage}\",\n \" startInLoadingState={showLoading}\",\n \" renderLoading={() =>\",\n \" showLoading ? (\",\n ' <View style={styles.loadingContainer}>',\n ' <ActivityIndicator size=\"large\" color=\"#007AFF\" />',\n \" </View>\",\n \" ) : null\",\n \" }\",\n \" {...rest}\",\n \" />\",\n \" </View>\",\n \" );\",\n \"}\",\n \"\",\n \"const styles = StyleSheet.create({\",\n \" container: {\",\n \" flex: 1,\",\n \" },\",\n \" webview: {\",\n \" flex: 1,\",\n \" },\",\n \" loadingContainer: {\",\n \" position: \\\"absolute\\\",\",\n \" top: 0,\",\n \" left: 0,\",\n \" right: 0,\",\n \" bottom: 0,\",\n \" justifyContent: \\\"center\\\",\",\n \" alignItems: \\\"center\\\",\",\n ' backgroundColor: \"rgba(255, 255, 255, 0.8)\",',\n \" },\",\n \"});\",\n \"\",\n \"export default WebViewContainer;\"\n ),\n },\n {\n path: \"modules/webview/bridge.ts\",\n content: lines(\n \"export interface BridgeMessage {\",\n \" type: string;\",\n \" payload: Record<string, unknown>;\",\n \"}\",\n \"\",\n \"export const BridgeMessageType = {\",\n ' NAVIGATE: \"NAVIGATE\",',\n ' SHARE: \"SHARE\",',\n ' PAYMENT: \"PAYMENT\",',\n ' AUTH: \"AUTH\",',\n ' TOAST: \"TOAST\",',\n ' CLOSE: \"CLOSE\",',\n ' CUSTOM: \"CUSTOM\",',\n \"} as const;\",\n \"\",\n \"export type BridgeMessageTypeValue =\",\n \" (typeof BridgeMessageType)[keyof typeof BridgeMessageType];\",\n \"\",\n \"export const bridge = {\",\n \" receive(message: BridgeMessage): BridgeMessage {\",\n \" if (!message.type) {\",\n ' throw new Error(\"Bridge message must have a \\'type\\' field\");',\n \" }\",\n \" return {\",\n \" type: message.type,\",\n \" payload: message.payload || {},\",\n \" };\",\n \" },\",\n \"\",\n \" buildSendScript(message: BridgeMessage): string {\",\n \" const payload = JSON.stringify(message.payload);\",\n \" return [\",\n \" \\\"(function() {\\\",\",\n \" \\\" if (typeof window.onNativeMessage === \\'function\\') {\\\",\",\n \" \\\" window.onNativeMessage(\\'\\\" + message.type + \\\"\\', \\\" + payload + \\\");\\\",\",\n \" \\\" }\\\",\",\n \" \\\"})();\\\",\",\n \" \\\"true;\\\",\",\n \" ].join(\\\"\\\\\\\\n\\\");\",\n \" },\",\n \"\",\n \" createMessage(\",\n \" type: BridgeMessageTypeValue | string,\",\n \" payload: Record<string, unknown> = {}\",\n \" ): BridgeMessage {\",\n \" return { type, payload };\",\n \" },\",\n \"};\"\n ),\n },\n ],\n};\n\nexport default webviewModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst i18nModule: ModuleDef = {\n id: \"i18n\",\n name: \"多语言\",\n description: \"i18next + react-i18next\",\n defaultChecked: false,\n dependencies: {\n i18next: \"^24.2.0\",\n \"react-i18next\": \"^15.2.0\",\n \"expo-localization\": \"~17.0.8\",\n },\n devDependencies: {},\n files: [\n {\n path: \"modules/i18n/index.ts\",\n content: lines(\n 'import i18n from \"i18next\";',\n 'import { initReactI18next } from \"react-i18next\";',\n 'import { getLocales } from \"expo-localization\";',\n 'import en from \"./locales/en.json\";',\n 'import zh from \"./locales/zh.json\";',\n \"\",\n \"const resources = {\",\n \" en: { translation: en },\",\n \" zh: { translation: zh },\",\n \"};\",\n \"\",\n \"function getDeviceLanguage(): string {\",\n \" const locales = getLocales();\",\n \" const deviceLang = locales[0]?.languageCode ?? \\\"en\\\";\",\n \" return deviceLang in resources ? deviceLang : \\\"en\\\";\",\n \"}\",\n \"\",\n \"i18n.use(initReactI18next).init({\",\n \" resources,\",\n \" lng: getDeviceLanguage(),\",\n ' fallbackLng: \"en\",',\n \" interpolation: {\",\n \" escapeValue: false,\",\n \" },\",\n ' compatibilityJSON: \"v4\",',\n \"});\",\n \"\",\n \"export default i18n;\",\n \"\",\n 'export { useTranslation } from \"react-i18next\";'\n ),\n },\n {\n path: \"modules/i18n/locales/en.json\",\n content: [\n \"{\",\n ' \"common\": {',\n ' \"ok\": \"OK\",',\n ' \"cancel\": \"Cancel\",',\n ' \"confirm\": \"Confirm\",',\n ' \"save\": \"Save\",',\n ' \"delete\": \"Delete\",',\n ' \"edit\": \"Edit\",',\n ' \"loading\": \"Loading...\",',\n ' \"error\": \"Something went wrong\",',\n ' \"retry\": \"Retry\",',\n ' \"search\": \"Search\"',\n \" },\",\n ' \"auth\": {',\n ' \"login\": \"Sign In\",',\n ' \"register\": \"Sign Up\",',\n ' \"logout\": \"Sign Out\",',\n ' \"email\": \"Email\",',\n ' \"password\": \"Password\",',\n ' \"forgotPassword\": \"Forgot Password?\"',\n \" },\",\n ' \"home\": {',\n ' \"title\": \"Home\",',\n ' \"welcome\": \"Welcome\"',\n \" },\",\n ' \"settings\": {',\n ' \"title\": \"Settings\",',\n ' \"language\": \"Language\",',\n ' \"theme\": \"Theme\",',\n ' \"about\": \"About\"',\n \" },\",\n ' \"errors\": {',\n ' \"network\": \"Network error. Please check your connection.\",',\n ' \"unauthorized\": \"Session expired. Please sign in again.\",',\n ' \"forbidden\": \"You don\\'t have permission to do this.\",',\n ' \"notFound\": \"The requested resource was not found.\",',\n ' \"server\": \"Server error. Please try again later.\"',\n \" }\",\n \"}\",\n ].join(\"\\n\"),\n },\n {\n path: \"modules/i18n/locales/zh.json\",\n content: [\n \"{\",\n ' \"common\": {',\n ' \"ok\": \"确定\",',\n ' \"cancel\": \"取消\",',\n ' \"confirm\": \"确认\",',\n ' \"save\": \"保存\",',\n ' \"delete\": \"删除\",',\n ' \"edit\": \"编辑\",',\n ' \"loading\": \"加载中...\",',\n ' \"error\": \"出了点问题\",',\n ' \"retry\": \"重试\",',\n ' \"search\": \"搜索\"',\n \" },\",\n ' \"auth\": {',\n ' \"login\": \"登录\",',\n ' \"register\": \"注册\",',\n ' \"logout\": \"退出登录\",',\n ' \"email\": \"邮箱\",',\n ' \"password\": \"密码\",',\n ' \"forgotPassword\": \"忘记密码?\"',\n \" },\",\n ' \"home\": {',\n ' \"title\": \"首页\",',\n ' \"welcome\": \"欢迎\"',\n \" },\",\n ' \"settings\": {',\n ' \"title\": \"设置\",',\n ' \"language\": \"语言\",',\n ' \"theme\": \"主题\",',\n ' \"about\": \"关于\"',\n \" },\",\n ' \"errors\": {',\n ' \"network\": \"网络错误,请检查网络连接。\",',\n ' \"unauthorized\": \"登录已过期,请重新登录。\",',\n ' \"forbidden\": \"您没有权限执行此操作。\",',\n ' \"notFound\": \"未找到请求的资源。\",',\n ' \"server\": \"服务器错误,请稍后重试。\"',\n \" }\",\n \"}\",\n ].join(\"\\n\"),\n },\n ],\n};\n\nexport default i18nModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst animationModule: ModuleDef = {\n id: \"animation\",\n name: \"动画/手势\",\n description: \"Reanimated 4 + Gesture Handler + Worklets(已默认包含,此模块仅添加封装代码)\",\n defaultChecked: false,\n // Dependencies are already included in base package.json\n dependencies: {},\n devDependencies: {},\n files: [\n {\n path: \"modules/animation/index.ts\",\n content: lines(\n 'export { FadeIn, FadeOut, SlideIn, SlideOut, ScaleIn, ScaleOut } from \"./transitions\";'\n ),\n },\n {\n path: \"modules/animation/transitions.ts\",\n content: lines(\n 'import Animated, {',\n \" FadeIn as ReanimatedFadeIn,\",\n \" FadeOut as ReanimatedFadeOut,\",\n \" SlideInUp as ReanimatedSlideInUp,\",\n \" SlideInDown as ReanimatedSlideInDown,\",\n \" SlideInLeft as ReanimatedSlideInLeft,\",\n \" SlideInRight as ReanimatedSlideInRight,\",\n \" SlideOutUp as ReanimatedSlideOutUp,\",\n \" SlideOutDown as ReanimatedSlideOutDown,\",\n \" SlideOutLeft as ReanimatedSlideOutLeft,\",\n \" SlideOutRight as ReanimatedSlideOutRight,\",\n \" ZoomIn as ReanimatedZoomIn,\",\n \" ZoomOut as ReanimatedZoomOut,\",\n \" type EntryExitAnimationFunction,\",\n '} from \"react-native-reanimated\";',\n \"\",\n \"export const FadeIn = ReanimatedFadeIn.duration(300);\",\n \"export const FadeOut = ReanimatedFadeOut.duration(300);\",\n \"export const SlideIn = ReanimatedSlideInDown.duration(300).springify();\",\n \"export const SlideInTop = ReanimatedSlideInUp.duration(300).springify();\",\n \"export const SlideInLeft = ReanimatedSlideInLeft.duration(300).springify();\",\n \"export const SlideInRight = ReanimatedSlideInRight.duration(300).springify();\",\n \"export const SlideOut = ReanimatedSlideOutDown.duration(300).springify();\",\n \"export const SlideOutTop = ReanimatedSlideOutUp.duration(300).springify();\",\n \"export const SlideOutLeft = ReanimatedSlideOutLeft.duration(300).springify();\",\n \"export const SlideOutRight = ReanimatedSlideOutRight.duration(300).springify();\",\n \"export const ScaleIn = ReanimatedZoomIn.duration(200).springify();\",\n \"export const ScaleOut = ReanimatedZoomOut.duration(200).springify();\",\n \"\",\n \"export function staggerFadeIn(\",\n \" index: number,\",\n \" baseDelay: number = 50\",\n \"): EntryExitAnimationFunction {\",\n \" return ReanimatedFadeIn.duration(300).delay(index * baseDelay);\",\n \"}\",\n \"\",\n \"export function staggerSlideIn(\",\n \" index: number,\",\n \" baseDelay: number = 80\",\n \"): EntryExitAnimationFunction {\",\n \" return ReanimatedSlideInDown.duration(400).springify().delay(index * baseDelay);\",\n \"}\",\n \"\",\n \"export { Animated };\"\n ),\n },\n ],\n babelPlugins: [],\n};\n\nexport default animationModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst otaModule: ModuleDef = {\n id: \"ota\",\n name: \"OTA 更新\",\n description: \"expo-updates\",\n defaultChecked: false,\n dependencies: {\n \"expo-updates\": \"~29.0.17\",\n },\n devDependencies: {},\n files: [\n {\n path: \"modules/ota/useOTAUpdate.ts\",\n content: lines(\n 'import { useCallback, useEffect, useState } from \"react\";',\n 'import * as Updates from \"expo-updates\";',\n 'import { Alert, Platform } from \"react-native\";',\n \"\",\n \"interface OTAUpdateState {\",\n \" isUpdateAvailable: boolean;\",\n \" isDownloading: boolean;\",\n \" progress: number;\",\n \" error: string | null;\",\n \"}\",\n \"\",\n \"export function useOTAUpdate() {\",\n \" const [state, setState] = useState<OTAUpdateState>({\",\n \" isUpdateAvailable: false,\",\n \" isDownloading: false,\",\n \" progress: 0,\",\n \" error: null,\",\n \" });\",\n \"\",\n \" const checkForUpdate = useCallback(async () => {\",\n \" if (__DEV__ || Platform.OS === \\\"web\\\") {\",\n \" return;\",\n \" }\",\n \"\",\n \" try {\",\n \" const update = await Updates.checkForUpdateAsync();\",\n \" setState((prev) => ({\",\n \" ...prev,\",\n \" isUpdateAvailable: update.isAvailable,\",\n \" error: null,\",\n \" }));\",\n \" return update.isAvailable;\",\n \" } catch (error) {\",\n \" const message = error instanceof Error ? error.message : \\\"检查更新失败\\\";\",\n \" setState((prev) => ({ ...prev, error: message }));\",\n \" return false;\",\n \" }\",\n \" }, []);\",\n \"\",\n \" const downloadUpdate = useCallback(async () => {\",\n \" if (__DEV__ || Platform.OS === \\\"web\\\") return null;\",\n \"\",\n \" try {\",\n \" setState((prev) => ({ ...prev, isDownloading: true, progress: 0 }));\",\n \" const result = await Updates.fetchUpdateAsync();\",\n \" setState((prev) => ({\",\n \" ...prev,\",\n \" isDownloading: false,\",\n \" isUpdateAvailable: false,\",\n \" progress: 1,\",\n \" }));\",\n \" return result;\",\n \" } catch (error) {\",\n \" const message = error instanceof Error ? error.message : \\\"下载更新失败\\\";\",\n \" setState((prev) => ({\",\n \" ...prev,\",\n \" isDownloading: false,\",\n \" error: message,\",\n \" }));\",\n \" return null;\",\n \" }\",\n \" }, []);\",\n \"\",\n \" const downloadAndRestart = useCallback(async () => {\",\n \" const result = await downloadUpdate();\",\n \" if (result?.isNew) {\",\n \" Alert.alert(\",\n ' \"更新已下载\",',\n ' \"应用需要重启以完成更新,是否立即重启?\",',\n \" [\",\n ' { text: \"稍后\", style: \"cancel\" },',\n \" {\",\n ' text: \"立即重启\",',\n ' style: \"default\",',\n \" onPress: async () => {\",\n \" await Updates.reloadAsync();\",\n \" },\",\n \" },\",\n \" ]\",\n \" );\",\n \" }\",\n \" }, [downloadUpdate]);\",\n \"\",\n \" const restartApp = useCallback(async () => {\",\n \" await Updates.reloadAsync();\",\n \" }, []);\",\n \"\",\n \" useEffect(() => {\",\n \" checkForUpdate();\",\n \" }, [checkForUpdate]);\",\n \"\",\n \" return {\",\n \" ...state,\",\n \" checkForUpdate,\",\n \" downloadUpdate,\",\n \" downloadAndRestart,\",\n \" restartApp,\",\n \" };\",\n \"}\"\n ),\n },\n ],\n};\n\nexport default otaModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst notificationModule: ModuleDef = {\n id: \"notification\",\n name: \"应用内通知\",\n description: \"expo-notifications + 自定义组件\",\n defaultChecked: false,\n dependencies: {\n \"expo-notifications\": \"~0.32.17\",\n },\n devDependencies: {},\n files: [\n {\n path: \"modules/notification/index.ts\",\n content: lines(\n 'export { InAppNotification, useInAppNotification } from \"./InAppNotification\";',\n 'export { setupNotificationHandlers } from \"./InAppNotification\";'\n ),\n },\n {\n path: \"modules/notification/InAppNotification.tsx\",\n content: lines(\n 'import React, {',\n \" createContext,\",\n \" useCallback,\",\n \" useContext,\",\n \" useEffect,\",\n \" useMemo,\",\n \" useRef,\",\n \" useState,\",\n '} from \"react\";',\n 'import { Animated, StyleSheet, Text, TouchableOpacity, View } from \"react-native\";',\n 'import * as Notifications from \"expo-notifications\";',\n 'import type { Subscription } from \"expo-notifications\";',\n \"\",\n \"export function setupNotificationHandlers(): void {\",\n \" Notifications.setNotificationHandler({\",\n \" handleNotification: async () => ({\",\n \" shouldShowAlert: true,\",\n \" shouldPlaySound: true,\",\n \" shouldSetBadge: true,\",\n \" }),\",\n \" });\",\n \"}\",\n \"\",\n \"interface NotificationData {\",\n \" id: string;\",\n \" title: string;\",\n \" message: string;\",\n ' type: \"info\" | \"success\" | \"warning\" | \"error\";',\n \" duration?: number;\",\n \"}\",\n \"\",\n \"interface InAppNotificationContextType {\",\n \" showNotification: (data: Omit<NotificationData, \\\"id\\\">) => void;\",\n \"}\",\n \"\",\n \"const InAppNotificationContext = createContext<InAppNotificationContextType>({\",\n \" showNotification: () => {},\",\n \"});\",\n \"\",\n \"export function useInAppNotification(): InAppNotificationContextType {\",\n \" return useContext(InAppNotificationContext);\",\n \"}\",\n \"\",\n \"let notificationIdCounter = 0;\",\n \"\",\n \"export function InAppNotificationProvider({\",\n \" children,\",\n \"}: {\",\n \" children: React.ReactNode;\",\n \"}) {\",\n \" const [notifications, setNotifications] = useState<NotificationData[]>([]);\",\n \" const timersRef = useRef<Map<string, ReturnType<typeof setTimeout>>>(new Map());\",\n \"\",\n \" const removeNotification = useCallback((id: string) => {\",\n \" setNotifications((prev) => prev.filter((n) => n.id !== id));\",\n \" const timer = timersRef.current.get(id);\",\n \" if (timer) {\",\n \" clearTimeout(timer);\",\n \" timersRef.current.delete(id);\",\n \" }\",\n \" }, []);\",\n \"\",\n \" const showNotification = useCallback(\",\n \" (data: Omit<NotificationData, \\\"id\\\">) => {\",\n ' const id = `notification-${++notificationIdCounter}`;',\n \" const notification: NotificationData = { ...data, id };\",\n \" setNotifications((prev) => [...prev, notification]);\",\n \"\",\n \" const duration = data.duration ?? 3000;\",\n \" const timer = setTimeout(() => {\",\n \" removeNotification(id);\",\n \" }, duration);\",\n \" timersRef.current.set(id, timer);\",\n \" },\",\n \" [removeNotification]\",\n \" );\",\n \"\",\n \" const contextValue = useMemo(\",\n \" () => ({ showNotification }),\",\n \" [showNotification]\",\n \" );\",\n \"\",\n \" return (\",\n \" <InAppNotificationContext.Provider value={contextValue}>\",\n \" {children}\",\n ' <View style={styles.container} pointerEvents=\"box-none\">',\n \" {notifications.map((notification) => (\",\n \" <NotificationCard\",\n \" key={notification.id}\",\n \" notification={notification}\",\n \" onDismiss={() => removeNotification(notification.id)}\",\n \" />\",\n \" ))}\",\n \" </View>\",\n \" </InAppNotificationContext.Provider>\",\n \" );\",\n \"}\",\n \"\",\n \"interface NotificationCardProps {\",\n \" notification: NotificationData;\",\n \" onDismiss: () => void;\",\n \"}\",\n \"\",\n \"const typeColors: Record<NotificationData[\\\"type\\\"], string> = {\",\n ' info: \"#007AFF\",',\n ' success: \"#34C759\",',\n ' warning: \"#FF9500\",',\n ' error: \"#FF3B30\",',\n \"};\",\n \"\",\n \"function NotificationCard({ notification, onDismiss }: NotificationCardProps) {\",\n \" const opacity = useRef(new Animated.Value(0)).current;\",\n \"\",\n \" useEffect(() => {\",\n \" Animated.timing(opacity, {\",\n \" toValue: 1,\",\n \" duration: 300,\",\n \" useNativeDriver: true,\",\n \" }).start();\",\n \" }, [opacity]);\",\n \"\",\n \" const handleDismiss = useCallback(() => {\",\n \" Animated.timing(opacity, {\",\n \" toValue: 0,\",\n \" duration: 200,\",\n \" useNativeDriver: true,\",\n \" }).start(() => onDismiss());\",\n \" }, [opacity, onDismiss]);\",\n \"\",\n \" return (\",\n ' <Animated.View style={[styles.card, { opacity }]}>',\n \" <View\",\n \" style={[styles.accent, { backgroundColor: typeColors[notification.type] }]}\",\n \" />\",\n \" <View style={styles.content}>\",\n \" <Text style={styles.title}>{notification.title}</Text>\",\n \" <Text style={styles.message}>{notification.message}</Text>\",\n \" </View>\",\n \" <TouchableOpacity onPress={handleDismiss} style={styles.closeButton}>\",\n ' <Text style={styles.closeButtonText}>X</Text>',\n \" </TouchableOpacity>\",\n \" </Animated.View>\",\n \" );\",\n \"}\",\n \"\",\n \"const styles = StyleSheet.create({\",\n \" container: {\",\n \" position: \\\"absolute\\\",\",\n \" top: 60,\",\n \" left: 16,\",\n \" right: 16,\",\n \" zIndex: 9999,\",\n \" gap: 8,\",\n \" },\",\n \" card: {\",\n \" flexDirection: \\\"row\\\",\",\n ' alignItems: \"center\",',\n ' backgroundColor: \"#fff\",',\n \" borderRadius: 12,\",\n ' shadowColor: \"#000\",',\n \" shadowOffset: { width: 0, height: 2 },\",\n \" shadowOpacity: 0.15,\",\n \" shadowRadius: 8,\",\n \" elevation: 5,\",\n ' overflow: \"hidden\",',\n \" },\",\n \" accent: {\",\n \" width: 4,\",\n ' height: \"100%\",',\n \" minHeight: 48,\",\n \" },\",\n \" content: {\",\n \" flex: 1,\",\n \" paddingVertical: 12,\",\n \" paddingHorizontal: 12,\",\n \" },\",\n \" title: {\",\n \" fontSize: 14,\",\n ' fontWeight: \"600\",',\n ' color: \"#1a1a1a\",',\n \" },\",\n \" message: {\",\n \" fontSize: 13,\",\n ' color: \"#666\",',\n \" marginTop: 2,\",\n \" },\",\n \" closeButton: {\",\n \" padding: 12,\",\n \" },\",\n \" closeButtonText: {\",\n \" fontSize: 14,\",\n ' color: \"#999\",',\n \" },\",\n \"});\",\n \"\",\n \"export const InAppNotification = InAppNotificationProvider;\"\n ),\n },\n ],\n layoutProviders: [\"<InAppNotification>\"],\n layoutImports: [\n 'import { InAppNotification } from \"../modules/notification/InAppNotification\";',\n ],\n};\n\nexport default notificationModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst permissionModule: ModuleDef = {\n id: \"permission\",\n name: \"用户权限管理\",\n description: \"权限请求/检查封装\",\n defaultChecked: false,\n dependencies: {\n \"expo-image-picker\": \"~17.0.11\",\n \"expo-camera\": \"~17.0.10\",\n \"expo-location\": \"~19.0.8\",\n },\n devDependencies: {},\n files: [\n {\n path: \"modules/permission/index.ts\",\n content: lines(\n 'import * as ImagePicker from \"expo-image-picker\";',\n 'import * as Camera from \"expo-camera\";',\n 'import * as Location from \"expo-location\";',\n 'import { Alert, Platform } from \"react-native\";',\n \"\",\n 'export type PermissionStatus = \"granted\" | \"denied\" | \"undetermined\";',\n \"\",\n \"export interface PermissionResult {\",\n \" status: PermissionStatus;\",\n \" canAskAgain: boolean;\",\n \"}\",\n \"\",\n \"export async function checkCameraPermission(): Promise<PermissionResult> {\",\n \" const { status, canAskAgain } = await Camera.getCameraPermissionsAsync();\",\n \" return { status: status as PermissionStatus, canAskAgain };\",\n \"}\",\n \"\",\n \"export async function requestCameraPermission(): Promise<PermissionResult> {\",\n \" const { status, canAskAgain } = await Camera.requestCameraPermissionsAsync();\",\n \" return { status: status as PermissionStatus, canAskAgain };\",\n \"}\",\n \"\",\n \"export async function checkPhotoLibraryPermission(): Promise<PermissionResult> {\",\n \" const { status, canAskAgain } =\",\n \" await ImagePicker.getMediaLibraryPermissionsAsync();\",\n \" return { status: status as PermissionStatus, canAskAgain };\",\n \"}\",\n \"\",\n \"export async function requestPhotoLibraryPermission(): Promise<PermissionResult> {\",\n \" const { status, canAskAgain } =\",\n \" await ImagePicker.requestMediaLibraryPermissionsAsync();\",\n \" return { status: status as PermissionStatus, canAskAgain };\",\n \"}\",\n \"\",\n \"export async function checkLocationPermission(): Promise<PermissionResult> {\",\n \" const { status, canAskAgain } = await Location.getForegroundPermissionsAsync();\",\n \" return { status: status as PermissionStatus, canAskAgain };\",\n \"}\",\n \"\",\n \"export async function requestLocationPermission(): Promise<PermissionResult> {\",\n \" const { status, canAskAgain } =\",\n \" await Location.requestForegroundPermissionsAsync();\",\n \" return { status: status as PermissionStatus, canAskAgain };\",\n \"}\",\n \"\",\n \"export async function requestBackgroundLocationPermission(): Promise<PermissionResult> {\",\n \" const { status, canAskAgain } =\",\n \" await Location.requestBackgroundPermissionsAsync();\",\n \" return { status: status as PermissionStatus, canAskAgain };\",\n \"}\",\n \"\",\n \"export async function requestPermissionWithAlert(\",\n \" permissionName: string,\",\n \" requestFn: () => Promise<PermissionResult>,\",\n \" onGranted?: () => void,\",\n \" onDenied?: () => void\",\n \"): Promise<PermissionResult> {\",\n \" const result = await requestFn();\",\n \"\",\n ' if (result.status === \"granted\") {',\n \" onGranted?.();\",\n \" } else {\",\n \" const message = result.canAskAgain\",\n ' ? `您需要授予${permissionName}权限才能使用此功能`',\n ' : `${permissionName}权限已被拒绝,请在系统设置中手动开启`;',\n \"\",\n ' Alert.alert(\"需要权限\", message, [',\n ' { text: \"取消\", style: \"cancel\", onPress: onDenied },',\n \" ...(result.canAskAgain\",\n \" ? [\",\n \" {\",\n ' text: \"再次请求\",',\n ' style: \"default\" as const,',\n \" onPress: () => requestFn(),\",\n \" },\",\n \" ]\",\n \" : []),\",\n \" ]);\",\n \" }\",\n \"\",\n \" return result;\",\n \"}\",\n \"\",\n \"export async function pickImageWithPermission(): Promise<ImagePicker.ImagePickerResult | null> {\",\n \" const permission = await requestPhotoLibraryPermission();\",\n ' if (permission.status !== \"granted\") {',\n ' Alert.alert(\"需要权限\", \"请授予相册访问权限\");',\n \" return null;\",\n \" }\",\n \"\",\n \" const result = await ImagePicker.launchImageLibraryAsync({\",\n \" mediaTypes: ImagePicker.MediaTypeOptions.Images,\",\n \" allowsEditing: true,\",\n \" aspect: [1, 1],\",\n \" quality: 0.8,\",\n \" });\",\n \"\",\n \" return result;\",\n \"}\",\n \"\",\n \"export async function takePhotoWithPermission(): Promise<ImagePicker.ImagePickerResult | null> {\",\n \" const permission = await requestCameraPermission();\",\n ' if (permission.status !== \"granted\") {',\n ' Alert.alert(\"需要权限\", \"请授予相机访问权限\");',\n \" return null;\",\n \" }\",\n \"\",\n \" const result = await ImagePicker.launchCameraAsync({\",\n \" allowsEditing: true,\",\n \" aspect: [1, 1],\",\n \" quality: 0.8,\",\n \" });\",\n \"\",\n \" return result;\",\n \"}\"\n ),\n },\n ],\n appConfig: {\n plugins: [\n [\n \"expo-camera\",\n {\n cameraPermission: \"Allow $(PRODUCT_NAME) to access your camera\",\n },\n ],\n [\n \"expo-image-picker\",\n {\n photosPermission: \"Allow $(PRODUCT_NAME) to access your photos\",\n },\n ],\n [\n \"expo-location\",\n {\n locationAlwaysAndWhenInUsePermission:\n \"Allow $(PRODUCT_NAME) to use your location\",\n },\n ],\n ],\n },\n};\n\nexport default permissionModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst bottomSheetModule: ModuleDef = {\n id: \"bottom-sheet\",\n name: \"Bottom Sheet\",\n description: \"@gorhom/bottom-sheet\",\n defaultChecked: false,\n dependencies: {\n \"@gorhom/bottom-sheet\": \"^5.1.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"components/AppBottomSheet.tsx\",\n content: lines(\n 'import React, { useCallback, useRef } from \"react\";',\n 'import { View, Text, StyleSheet } from \"react-native\";',\n 'import BottomSheet, {',\n \" BottomSheetBackdrop,\",\n \" BottomSheetView,\",\n \" type BottomSheetBackdropProps,\",\n \" type BottomSheetProps,\",\n '} from \"@gorhom/bottom-sheet\";',\n \"\",\n \"interface AppBottomSheetProps extends Omit<BottomSheetProps, \\\"children\\\"> {\",\n \" title?: string;\",\n \" children: React.ReactNode;\",\n \" onClose?: () => void;\",\n \"}\",\n \"\",\n \"export const AppBottomSheet = React.forwardRef<BottomSheet, AppBottomSheetProps>(\",\n \" ({ title, children, onClose, snapPoints = [\\\"50%\\\"], ...rest }, ref) => {\",\n \" const localRef = useRef<BottomSheet>(null);\",\n \" const sheetRef = (ref as React.RefObject<BottomSheet>) || localRef;\",\n \"\",\n \" const renderBackdrop = useCallback(\",\n \" (props: BottomSheetBackdropProps) => (\",\n \" <BottomSheetBackdrop\",\n \" {...props}\",\n \" disappearsOnIndex={-1}\",\n \" appearsOnIndex={0}\",\n ' pressBehavior=\"close\"',\n \" />\",\n \" ),\",\n \" []\",\n \" );\",\n \"\",\n \" const handleSheetChange = useCallback(\",\n \" (index: number) => {\",\n \" if (index === -1 && onClose) {\",\n \" onClose();\",\n \" }\",\n \" },\",\n \" [onClose]\",\n \" );\",\n \"\",\n \" return (\",\n \" <BottomSheet\",\n \" ref={sheetRef}\",\n \" index={-1}\",\n \" snapPoints={snapPoints}\",\n \" backdropComponent={renderBackdrop}\",\n \" onChange={handleSheetChange}\",\n \" enablePanDownToClose\",\n \" backgroundStyle={styles.background}\",\n \" handleIndicatorStyle={styles.handleIndicator}\",\n \" {...rest}\",\n \" >\",\n \" <BottomSheetView style={styles.content}>\",\n \" {title && <Text style={styles.title}>{title}</Text>}\",\n \" {children}\",\n \" </BottomSheetView>\",\n \" </BottomSheet>\",\n \" );\",\n \" }\",\n \");\",\n \"\",\n 'AppBottomSheet.displayName = \"AppBottomSheet\";',\n \"\",\n \"const styles = StyleSheet.create({\",\n \" background: {\",\n \" borderRadius: 16,\",\n \" },\",\n \" handleIndicator: {\",\n ' backgroundColor: \"#d1d5db\",',\n \" width: 40,\",\n \" },\",\n \" content: {\",\n \" paddingHorizontal: 16,\",\n \" paddingBottom: 24,\",\n \" },\",\n \" title: {\",\n \" fontSize: 18,\",\n ' fontWeight: \"600\",',\n ' color: \"#1a1a1a\",',\n \" marginBottom: 16,\",\n \" },\",\n \"});\",\n \"\",\n \"export default AppBottomSheet;\"\n ),\n },\n ],\n layoutProviders: [\"<BottomSheetModalProvider>\"],\n layoutImports: [\n 'import { BottomSheetModalProvider } from \"@gorhom/bottom-sheet\";',\n ],\n};\n\nexport default bottomSheetModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst flashlistModule: ModuleDef = {\n id: \"flashlist\",\n name: \"FlashList\",\n description: \"@shopify/flash-list\",\n defaultChecked: false,\n dependencies: {\n \"@shopify/flash-list\": \"2.0.2\",\n },\n devDependencies: {},\n files: [\n {\n path: \"components/AppFlashList.tsx\",\n content: lines(\n 'import React, { useCallback } from \"react\";',\n 'import { View, Text, StyleSheet, type ListRenderItemInfo } from \"react-native\";',\n 'import {',\n \" FlashList,\",\n \" type FlashListProps,\",\n '} from \"@shopify/flash-list\";',\n \"\",\n \"export interface ListItem {\",\n \" id: string;\",\n \" [key: string]: unknown;\",\n \"}\",\n \"\",\n \"interface AppFlashListProps<T extends ListItem>\",\n \" extends Omit<FlashListProps<T>, \\\"renderItem\\\" | \\\"estimatedItemSize\\\"> {\",\n \" renderItem: (info: ListRenderItemInfo<T>) => React.ReactElement | null;\",\n \" estimatedItemSize?: number;\",\n \" ListEmptyComponent?: React.ReactElement;\",\n \" isLoading?: boolean;\",\n \" LoadingComponent?: React.ReactElement;\",\n \"}\",\n \"\",\n \"export function AppFlashList<T extends ListItem>({\",\n \" data,\",\n \" renderItem,\",\n \" estimatedItemSize = 50,\",\n \" ListEmptyComponent,\",\n \" isLoading,\",\n \" LoadingComponent,\",\n \" ...rest\",\n \"}: AppFlashListProps<T>) {\",\n \" const defaultEmptyComponent = React.useMemo(\",\n \" () => (\",\n \" <View style={styles.emptyContainer}>\",\n ' <Text style={styles.emptyText}>暂无数据</Text>',\n \" </View>\",\n \" ),\",\n \" []\",\n \" );\",\n \"\",\n \" const defaultLoadingComponent = React.useMemo(\",\n \" () => (\",\n \" <View style={styles.loadingContainer}>\",\n ' <Text style={styles.loadingText}>加载中...</Text>',\n \" </View>\",\n \" ),\",\n \" []\",\n \" );\",\n \"\",\n \" if (isLoading) {\",\n \" return LoadingComponent ?? defaultLoadingComponent;\",\n \" }\",\n \"\",\n \" return (\",\n \" <FlashList\",\n \" data={data}\",\n \" renderItem={renderItem}\",\n \" estimatedItemSize={estimatedItemSize}\",\n \" ListEmptyComponent={ListEmptyComponent ?? defaultEmptyComponent}\",\n \" drawDistance={200}\",\n \" {...rest}\",\n \" />\",\n \" );\",\n \"}\",\n \"\",\n \"const styles = StyleSheet.create({\",\n \" emptyContainer: {\",\n \" flex: 1,\",\n \" justifyContent: \\\"center\\\",\",\n \" alignItems: \\\"center\\\",\",\n \" paddingVertical: 40,\",\n \" },\",\n \" emptyText: {\",\n \" fontSize: 16,\",\n ' color: \"#999\",',\n \" },\",\n \" loadingContainer: {\",\n \" flex: 1,\",\n \" justifyContent: \\\"center\\\",\",\n \" alignItems: \\\"center\\\",\",\n \" paddingVertical: 40,\",\n \" },\",\n \" loadingText: {\",\n \" fontSize: 16,\",\n ' color: \"#999\",',\n \" },\",\n \"});\",\n \"\",\n \"export default AppFlashList;\"\n ),\n },\n ],\n};\n\nexport default flashlistModule;\n","import type { ModuleDef } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\nconst uiReusablesModule: ModuleDef = {\n id: \"ui-reusables\",\n name: \"reactnative.reusables UI\",\n description: \"预置 UI 组件 (Button, AlertDialog, Card, Input, Label, Text, Separator)\",\n defaultChecked: false,\n dependencies: {\n \"class-variance-authority\": \"^0.7.1\",\n \"clsx\": \"^2.1.1\",\n \"tailwind-merge\": \"^2.6.0\",\n \"@rn-primitives/slot\": \"^1.1.0\",\n \"@rn-primitives/types\": \"^1.1.0\",\n \"@rn-primitives/label\": \"^1.1.0\",\n \"@rn-primitives/separator\": \"^1.1.0\",\n \"@rn-primitives/alert-dialog\": \"^1.1.0\",\n \"@rn-primitives/portal\": \"^1.1.0\",\n \"react-native-svg\": \"^15.8.0\",\n },\n devDependencies: {},\n files: [\n // ─── components/ui/text.tsx ──────────────────────────────────────────\n {\n path: \"components/ui/text.tsx\",\n content: lines(\n 'import { cn } from \"@/lib/utils\";',\n 'import { Slot } from \"@rn-primitives/slot\";',\n 'import { cva, type VariantProps } from \"class-variance-authority\";',\n 'import * as React from \"react\";',\n 'import { Platform, Text as RNText, type Role } from \"react-native\";',\n \"\",\n \"const textVariants = cva(\",\n \" cn(\",\n ' \"text-foreground text-base\",',\n \" Platform.select({\",\n ' web: \"select-text\",',\n \" })\",\n \" ),\",\n \" {\",\n \" variants: {\",\n \" variant: {\",\n \" default: \\\"\\\",\",\n \" h1: cn(\",\n ' \"text-center text-4xl font-extrabold tracking-tight\",',\n \" Platform.select({ web: \\\"scroll-m-20 text-balance\\\" })\",\n \" ),\",\n \" h2: cn(\",\n ' \"border-border border-b pb-2 text-3xl font-semibold tracking-tight\",',\n ' Platform.select({ web: \"scroll-m-20 first:mt-0\" })',\n \" ),\",\n \" h3: cn(\",\n ' \"text-2xl font-semibold tracking-tight\",',\n ' Platform.select({ web: \"scroll-m-20\" })',\n \" ),\",\n \" h4: cn(\",\n ' \"text-xl font-semibold tracking-tight\",',\n ' Platform.select({ web: \"scroll-m-20\" })',\n \" ),\",\n ' p: \"mt-3 leading-7 sm:mt-6\",',\n ' blockquote: \"mt-4 border-l-2 pl-3 italic sm:mt-6 sm:pl-6\",',\n \" code: cn(\",\n ' \"bg-muted relative rounded px-[0.3rem] py-[0.2rem] font-mono text-sm font-semibold\"',\n \" ),\",\n ' lead: \"text-muted-foreground text-xl\",',\n ' large: \"text-lg font-semibold\",',\n ' small: \"text-sm font-medium leading-none\",',\n ' muted: \"text-muted-foreground text-sm\",',\n \" },\",\n \" },\",\n \" defaultVariants: {\",\n \" variant: \\\"default\\\",\",\n \" },\",\n \" }\",\n \");\",\n \"\",\n \"type TextVariantProps = VariantProps<typeof textVariants>;\",\n \"\",\n \"type TextVariant = NonNullable<TextVariantProps[\\\"variant\\\"]>;\",\n \"\",\n \"const ROLE: Partial<Record<TextVariant, Role>> = {\",\n ' h1: \"heading\",',\n ' h2: \"heading\",',\n ' h3: \"heading\",',\n ' h4: \"heading\",',\n \" blockquote: Platform.select({ web: \\\"blockquote\\\" as Role }),\",\n \" code: Platform.select({ web: \\\"code\\\" as Role }),\",\n \"};\",\n \"\",\n \"const ARIA_LEVEL: Partial<Record<TextVariant, string>> = {\",\n ' h1: \"1\",',\n ' h2: \"2\",',\n ' h3: \"3\",',\n ' h4: \"4\",',\n \"};\",\n \"\",\n \"const TextClassContext = React.createContext<string | undefined>(undefined);\",\n \"\",\n \"function Text({\",\n \" className,\",\n \" asChild = false,\",\n ' variant = \"default\",',\n \" ...props\",\n \"}: React.ComponentProps<typeof RNText> &\",\n \" React.RefAttributes<typeof RNText> &\",\n \" TextVariantProps & { asChild?: boolean }) {\",\n \" const textClass = React.useContext(TextClassContext);\",\n \" const Component = asChild ? Slot : RNText;\",\n \" return (\",\n \" <Component\",\n \" className={cn(textVariants({ variant }), textClass, className)}\",\n \" role={variant ? ROLE[variant] : undefined}\",\n \" aria-level={variant ? ARIA_LEVEL[variant] : undefined}\",\n \" {...props}\",\n \" />\",\n \" );\",\n \"}\",\n \"\",\n \"export { Text, TextClassContext };\",\n \"\"\n ),\n },\n\n // ─── components/ui/button.tsx ────────────────────────────────────────\n {\n path: \"components/ui/button.tsx\",\n content: lines(\n 'import { TextClassContext } from \"@/components/ui/text\";',\n 'import { cn } from \"@/lib/utils\";',\n 'import { cva, type VariantProps } from \"class-variance-authority\";',\n 'import { Platform, Pressable } from \"react-native\";',\n \"\",\n \"const buttonVariants = cva(\",\n \" cn(\",\n ' \"group shrink-0 flex-row items-center justify-center gap-2 rounded-md shadow-none\",',\n \" Platform.select({\",\n ' web: \"focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive whitespace-nowrap outline-none transition-all focus-visible:ring-[3px] disabled:pointer-events-none [&_svg:not([class*=\\'size-\\'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0\",',\n \" })\",\n \" ),\",\n \" {\",\n \" variants: {\",\n \" variant: {\",\n \" default: cn(\",\n ' \"bg-primary active:bg-primary/90 shadow-sm shadow-black/5\",',\n ' Platform.select({ web: \"hover:bg-primary/90\" })',\n \" ),\",\n \" destructive: cn(\",\n ' \"bg-destructive active:bg-destructive/90 dark:bg-destructive/60 shadow-sm shadow-black/5\",',\n \" Platform.select({\",\n ' web: \"hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40\",',\n \" })\",\n \" ),\",\n \" outline: cn(\",\n ' \"border-border bg-background active:bg-accent dark:bg-input/30 dark:border-input dark:active:bg-input/50 border shadow-sm shadow-black/5\",',\n \" Platform.select({\",\n ' web: \"hover:bg-accent dark:hover:bg-input/50\",',\n \" })\",\n \" ),\",\n \" secondary: cn(\",\n ' \"bg-secondary active:bg-secondary/80 shadow-sm shadow-black/5\",',\n ' Platform.select({ web: \"hover:bg-secondary/80\" })',\n \" ),\",\n \" ghost: cn(\",\n ' \"active:bg-accent dark:active:bg-accent/50\",',\n ' Platform.select({ web: \"hover:bg-accent dark:hover:bg-accent/50\" })',\n \" ),\",\n ' link: \"\",',\n \" },\",\n \" size: {\",\n \" default: cn(\",\n ' \"h-10 px-4 py-2 sm:h-9\",',\n ' Platform.select({ web: \"has-[>svg]:px-3\" })',\n \" ),\",\n \" sm: cn(\",\n ' \"h-9 gap-1.5 rounded-md px-3 sm:h-8\",',\n ' Platform.select({ web: \"has-[>svg]:px-2.5\" })',\n \" ),\",\n \" lg: cn(\",\n ' \"h-11 rounded-md px-6 sm:h-10\",',\n ' Platform.select({ web: \"has-[>svg]:px-4\" })',\n \" ),\",\n ' icon: \"h-10 w-10 sm:h-9 sm:w-9\",',\n \" },\",\n \" },\",\n \" defaultVariants: {\",\n ' variant: \"default\",',\n ' size: \"default\",',\n \" },\",\n \" }\",\n \");\",\n \"\",\n \"const buttonTextVariants = cva(\",\n \" cn(\",\n ' \"text-foreground text-sm font-medium\",',\n ' Platform.select({ web: \"pointer-events-none transition-colors\" })',\n \" ),\",\n \" {\",\n \" variants: {\",\n \" variant: {\",\n ' default: \"text-primary-foreground\",',\n ' destructive: \"text-white\",',\n \" outline: cn(\",\n ' \"group-active:text-accent-foreground\",',\n ' Platform.select({ web: \"group-hover:text-accent-foreground\" })',\n \" ),\",\n ' secondary: \"text-secondary-foreground\",',\n ' ghost: \"group-active:text-accent-foreground\",',\n \" link: cn(\",\n ' \"text-primary group-active:underline\",',\n ' Platform.select({ web: \"underline-offset-4 hover:underline group-hover:underline\" })',\n \" ),\",\n \" },\",\n \" size: {\",\n ' default: \"\",',\n ' sm: \"\",',\n ' lg: \"\",',\n ' icon: \"\",',\n \" },\",\n \" },\",\n \" defaultVariants: {\",\n ' variant: \"default\",',\n ' size: \"default\",',\n \" },\",\n \" }\",\n \");\",\n \"\",\n \"type ButtonProps = React.ComponentProps<typeof Pressable> &\",\n \" React.RefAttributes<typeof Pressable> &\",\n \" VariantProps<typeof buttonVariants>;\",\n \"\",\n \"function Button({ className, variant, size, ...props }: ButtonProps) {\",\n \" return (\",\n \" <TextClassContext.Provider value={buttonTextVariants({ variant, size })}>\",\n \" <Pressable\",\n ' className={cn(props.disabled && \"opacity-50\", buttonVariants({ variant, size }), className)}',\n ' role=\"button\"',\n \" {...props}\",\n \" />\",\n \" </TextClassContext.Provider>\",\n \" );\",\n \"}\",\n \"\",\n \"export { Button, buttonTextVariants, buttonVariants };\",\n \"export type { ButtonProps };\",\n \"\"\n ),\n },\n\n // ─── components/ui/input.tsx ──────────────────────────────────────────\n {\n path: \"components/ui/input.tsx\",\n content: lines(\n 'import { cn } from \"@/lib/utils\";',\n 'import { Platform, TextInput } from \"react-native\";',\n \"\",\n \"function Input({\",\n \" className,\",\n \" ...props\",\n \"}: React.ComponentProps<typeof TextInput> & React.RefAttributes<TextInput>) {\",\n \" return (\",\n \" <TextInput\",\n \" className={cn(\",\n ' \"dark:bg-input/30 border-input bg-background text-foreground flex h-10 w-full min-w-0 flex-row items-center rounded-md border px-3 py-1 text-base leading-5 shadow-sm shadow-black/5 sm:h-9\",',\n \" props.editable === false &&\",\n \" cn(\",\n ' \"opacity-50\",',\n ' Platform.select({ web: \"disabled:pointer-events-none disabled:cursor-not-allowed\" })',\n \" ),\",\n \" Platform.select({\",\n \" web: cn(\",\n ' \"placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground outline-none transition-[color,box-shadow] md:text-sm\",',\n ' \"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]\",',\n ' \"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive\"',\n \" ),\",\n ' native: \"placeholder:text-muted-foreground/50\",',\n \" }),\",\n \" className\",\n \" )}\",\n \" {...props}\",\n \" />\",\n \" );\",\n \"}\",\n \"\",\n \"export { Input };\",\n \"\"\n ),\n },\n\n // ─── components/ui/label.tsx ─────────────────────────────────────────\n {\n path: \"components/ui/label.tsx\",\n content: lines(\n 'import { cn } from \"@/lib/utils\";',\n 'import * as LabelPrimitive from \"@rn-primitives/label\";',\n 'import { Platform } from \"react-native\";',\n \"\",\n \"function Label({\",\n \" className,\",\n \" onPress,\",\n \" onLongPress,\",\n \" onPressIn,\",\n \" onPressOut,\",\n \" disabled,\",\n \" ...props\",\n \"}: React.ComponentProps<typeof LabelPrimitive.Text>) {\",\n \" return (\",\n \" <LabelPrimitive.Root\",\n \" className={cn(\",\n ' \"flex select-none flex-row items-center gap-2\",',\n \" Platform.select({\",\n ' web: \"cursor-default leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-50 group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50\",',\n \" }),\",\n ' disabled && \"opacity-50\"',\n \" )}\",\n \" onPress={onPress}\",\n \" onLongPress={onLongPress}\",\n \" onPressIn={onPressIn}\",\n \" onPressOut={onPressOut}\",\n \" disabled={disabled}\",\n \" >\",\n \" <LabelPrimitive.Text\",\n \" className={cn(\",\n ' \"text-foreground text-sm font-medium\",',\n ' Platform.select({ web: \"leading-none\" }),',\n \" className\",\n \" )}\",\n \" {...props}\",\n \" />\",\n \" </LabelPrimitive.Root>\",\n \" );\",\n \"}\",\n \"\",\n \"export { Label };\",\n \"\"\n ),\n },\n\n // ─── components/ui/card.tsx ──────────────────────────────────────────\n {\n path: \"components/ui/card.tsx\",\n content: lines(\n 'import { Text, TextClassContext } from \"@/components/ui/text\";',\n 'import { cn } from \"@/lib/utils\";',\n 'import { View } from \"react-native\";',\n \"\",\n \"function Card({\",\n \" className,\",\n \" ...props\",\n \"}: React.ComponentProps<typeof View> & React.RefAttributes<View>) {\",\n \" return (\",\n ' <TextClassContext.Provider value=\"text-card-foreground\">',\n \" <View\",\n \" className={cn(\",\n ' \"bg-card border-border flex flex-col gap-6 rounded-xl border py-6 shadow-sm shadow-black/5\",',\n \" className\",\n \" )}\",\n \" {...props}\",\n \" />\",\n \" </TextClassContext.Provider>\",\n \" );\",\n \"}\",\n \"\",\n \"function CardHeader({\",\n \" className,\",\n \" ...props\",\n \"}: React.ComponentProps<typeof View> & React.RefAttributes<View>) {\",\n \" return (\",\n ' <View className={cn(\"flex flex-col gap-1.5 px-6\", className)} {...props} />',\n \" );\",\n \"}\",\n \"\",\n \"function CardTitle({\",\n \" className,\",\n \" ref,\",\n \" ...props\",\n \"}: React.ComponentProps<typeof Text> & React.RefAttributes<typeof Text>) {\",\n \" return (\",\n \" <Text\",\n \" ref={ref}\",\n ' role=\"heading\"',\n \" aria-level={3}\",\n ' className={cn(\"font-semibold leading-none\", className)}',\n \" {...props}\",\n \" />\",\n \" );\",\n \"}\",\n \"\",\n \"function CardDescription({\",\n \" className,\",\n \" ...props\",\n \"}: React.ComponentProps<typeof Text> & React.RefAttributes<typeof Text>) {\",\n \" return (\",\n ' <Text className={cn(\"text-muted-foreground text-sm\", className)} {...props} />',\n \" );\",\n \"}\",\n \"\",\n \"function CardContent({\",\n \" className,\",\n \" ...props\",\n \"}: React.ComponentProps<typeof View> & React.RefAttributes<View>) {\",\n \" return (\",\n ' <View className={cn(\"px-6\", className)} {...props} />',\n \" );\",\n \"}\",\n \"\",\n \"function CardFooter({\",\n \" className,\",\n \" ...props\",\n \"}: React.ComponentProps<typeof View> & React.RefAttributes<View>) {\",\n \" return (\",\n ' <View className={cn(\"flex flex-row items-center px-6\", className)} {...props} />',\n \" );\",\n \"}\",\n \"\",\n \"export { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle };\",\n \"\"\n ),\n },\n\n // ─── components/ui/separator.tsx ──────────────────────────────────────\n {\n path: \"components/ui/separator.tsx\",\n content: lines(\n 'import { cn } from \"@/lib/utils\";',\n 'import * as SeparatorPrimitive from \"@rn-primitives/separator\";',\n \"\",\n \"function Separator({\",\n \" className,\",\n ' orientation = \"horizontal\",',\n \" decorative = true,\",\n \" ...props\",\n \"}: React.ComponentProps<typeof SeparatorPrimitive.Root>) {\",\n \" return (\",\n \" <SeparatorPrimitive.Root\",\n \" decorative={decorative}\",\n \" orientation={orientation}\",\n \" className={cn(\",\n ' \"bg-border shrink-0\",',\n ' orientation === \"horizontal\" ? \"h-[1px] w-full\" : \"h-full w-[1px]\",',\n \" className\",\n \" )}\",\n \" {...props}\",\n \" />\",\n \" );\",\n \"}\",\n \"\",\n \"export { Separator };\",\n \"\"\n ),\n },\n\n // ─── components/ui/alert-dialog.tsx ──────────────────────────────────\n {\n path: \"components/ui/alert-dialog.tsx\",\n content: lines(\n 'import { buttonTextVariants, buttonVariants } from \"@/components/ui/button\";',\n 'import { TextClassContext } from \"@/components/ui/text\";',\n 'import { cn } from \"@/lib/utils\";',\n 'import * as AlertDialogPrimitive from \"@rn-primitives/alert-dialog\";',\n 'import * as React from \"react\";',\n 'import { Platform, View, type ViewProps } from \"react-native\";',\n \"\",\n \"const AlertDialog = AlertDialogPrimitive.Root;\",\n \"\",\n \"const AlertDialogTrigger = AlertDialogPrimitive.Trigger;\",\n \"\",\n \"const AlertDialogPortal = AlertDialogPrimitive.Portal;\",\n \"\",\n \"function AlertDialogOverlay({\",\n \" className,\",\n \" ...props\",\n \"}: Omit<React.ComponentProps<typeof AlertDialogPrimitive.Overlay>, \\\"asChild\\\"> & {\",\n \" children?: React.ReactNode;\",\n \"}) {\",\n \" return (\",\n \" <AlertDialogPrimitive.Overlay\",\n \" className={cn(\",\n ' \"absolute bottom-0 left-0 right-0 top-0 z-50 flex items-center justify-center bg-black/50 p-2\",',\n \" Platform.select({\",\n ' web: \"animate-in fade-in-0 fixed\",',\n \" }),\",\n \" className\",\n \" )}\",\n \" {...props}\",\n \" />\",\n \" );\",\n \"}\",\n \"\",\n \"function AlertDialogContent({\",\n \" className,\",\n \" portalHost,\",\n \" ...props\",\n \"}: React.ComponentProps<typeof AlertDialogPrimitive.Content> & {\",\n \" portalHost?: string;\",\n \"}) {\",\n \" return (\",\n \" <AlertDialogPortal hostName={portalHost}>\",\n \" <AlertDialogOverlay>\",\n \" <AlertDialogPrimitive.Content\",\n \" className={cn(\",\n ' \"bg-background border-border z-50 flex w-full max-w-[calc(100%-2rem)] flex-col gap-4 rounded-lg border p-6 shadow-lg shadow-black/5 sm:max-w-lg\",',\n \" Platform.select({\",\n ' web: \"animate-in fade-in-0 zoom-in-95 duration-200\",',\n \" }),\",\n \" className\",\n \" )}\",\n \" {...props}\",\n \" />\",\n \" </AlertDialogOverlay>\",\n \" </AlertDialogPortal>\",\n \" );\",\n \"}\",\n \"\",\n \"function AlertDialogHeader({ className, ...props }: ViewProps) {\",\n \" return (\",\n ' <TextClassContext.Provider value=\"text-center sm:text-left\">',\n ' <View className={cn(\"flex flex-col gap-2\", className)} {...props} />',\n \" </TextClassContext.Provider>\",\n \" );\",\n \"}\",\n \"\",\n \"function AlertDialogFooter({ className, ...props }: ViewProps) {\",\n \" return (\",\n ' <View className={cn(\"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\", className)} {...props} />',\n \" );\",\n \"}\",\n \"\",\n \"function AlertDialogTitle({\",\n \" className,\",\n \" ...props\",\n \"}: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {\",\n \" return (\",\n \" <AlertDialogPrimitive.Title\",\n ' className={cn(\"text-foreground text-lg font-semibold\", className)}',\n \" {...props}\",\n \" />\",\n \" );\",\n \"}\",\n \"\",\n \"function AlertDialogDescription({\",\n \" className,\",\n \" ...props\",\n \"}: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {\",\n \" return (\",\n \" <AlertDialogPrimitive.Description\",\n ' className={cn(\"text-muted-foreground text-sm\", className)}',\n \" {...props}\",\n \" />\",\n \" );\",\n \"}\",\n \"\",\n \"function AlertDialogAction({\",\n \" className,\",\n \" ...props\",\n \"}: React.ComponentProps<typeof AlertDialogPrimitive.Action>) {\",\n \" return (\",\n \" <TextClassContext.Provider value={buttonTextVariants({ className })}>\",\n ' <AlertDialogPrimitive.Action className={cn(buttonVariants(), className)} {...props} />',\n \" </TextClassContext.Provider>\",\n \" );\",\n \"}\",\n \"\",\n \"function AlertDialogCancel({\",\n \" className,\",\n \" ...props\",\n \"}: React.ComponentProps<typeof AlertDialogPrimitive.Cancel>) {\",\n \" return (\",\n \" <TextClassContext.Provider value={buttonTextVariants({ className, variant: \\\"outline\\\" })}>\",\n \" <AlertDialogPrimitive.Cancel\",\n \" className={cn(buttonVariants({ variant: \\\"outline\\\" }), className)} {...props} />\",\n \" </TextClassContext.Provider>\",\n \" );\",\n \"}\",\n \"\",\n \"export {\",\n \" AlertDialog,\",\n \" AlertDialogAction,\",\n \" AlertDialogCancel,\",\n \" AlertDialogContent,\",\n \" AlertDialogDescription,\",\n \" AlertDialogFooter,\",\n \" AlertDialogHeader,\",\n \" AlertDialogOverlay,\",\n \" AlertDialogPortal,\",\n \" AlertDialogTitle,\",\n \" AlertDialogTrigger,\",\n \"};\",\n \"\"\n ),\n },\n ],\n};\n\nexport default uiReusablesModule;\n","import type { ModuleDef } from \"../types\";\nimport networkModule from \"./network\";\nimport stateModule from \"./state\";\nimport storageModule from \"./storage\";\nimport paymentModule from \"./payment\";\nimport formModule from \"./form\";\nimport imageModule from \"./image\";\nimport videoModule from \"./video\";\nimport authGoogleModule from \"./auth-google\";\nimport authFacebookModule from \"./auth-facebook\";\nimport authAppleModule from \"./auth-apple\";\nimport webviewModule from \"./webview\";\nimport i18nModule from \"./i18n\";\nimport animationModule from \"./animation\";\nimport otaModule from \"./ota\";\nimport notificationModule from \"./notification\";\nimport permissionModule from \"./permission\";\nimport bottomSheetModule from \"./bottom-sheet\";\nimport flashlistModule from \"./flashlist\";\nimport uiReusablesModule from \"./ui-reusables\";\n\n/** All available modules, in display order */\nexport const modules: ModuleDef[] = [\n // P0 — Core (default checked)\n networkModule,\n stateModule,\n storageModule,\n // Note: expo-router and nativewind are always included in the base template\n\n // P1 — Feature modules\n paymentModule,\n formModule,\n imageModule,\n videoModule,\n authGoogleModule,\n authFacebookModule,\n authAppleModule,\n webviewModule,\n i18nModule,\n animationModule,\n\n // P2 — Additional modules\n otaModule,\n notificationModule,\n permissionModule,\n bottomSheetModule,\n flashlistModule,\n uiReusablesModule,\n];\n\n/**\n * Get a module by its ID.\n */\nexport function getModuleById(id: string): ModuleDef | undefined {\n return modules.find((m) => m.id === id);\n}\n\n/**\n * Get all module IDs.\n */\nexport function getModuleIds(): string[] {\n return modules.map((m) => m.id);\n}\n\n/**\n * Get modules by a list of IDs.\n */\nexport function getModulesByIds(ids: string[]): ModuleDef[] {\n return ids\n .map((id) => getModuleById(id))\n .filter((m): m is ModuleDef => m !== undefined);\n}\n","import type { TemplateFile } from \"../types\";\n\n/**\n * Generate base template files that are included in every project.\n * Follows reactnativereusables official installation guide.\n * https://reactnativereusables.com/docs/installation/manual\n */\nexport function generateBaseTemplates(projectName: string): TemplateFile[] {\n return [\n // ─── app/_layout.tsx ────────────────────────────────────────────────\n {\n path: \"app/_layout.tsx\",\n content: `import \"../global.css\";\nimport { DarkTheme, DefaultTheme, ThemeProvider } from \"@react-navigation/native\";\nimport { useFonts } from \"expo-font\";\nimport { Stack } from \"expo-router\";\nimport * as SplashScreen from \"expo-splash-screen\";\nimport { useEffect } from \"react\";\nimport { useColorScheme } from \"react-native\";\nimport { PortalHost } from \"@rn-primitives/portal\";\nimport { NAV_THEME } from \"@/lib/theme\";\n\nSplashScreen.preventAutoHideAsync();\n\nexport default function RootLayout() {\n const colorScheme = useColorScheme();\n const [loaded] = useFonts({\n NunitoBold: require(\"../assets/fonts/Nunito-Bold.ttf\"),\n });\n\n useEffect(() => {\n if (loaded) {\n SplashScreen.hideAsync();\n }\n }, [loaded]);\n\n if (!loaded) {\n return null;\n }\n\n return (\n <ThemeProvider value={colorScheme === \"dark\" ? NAV_THEME.dark : NAV_THEME.light}>\n <Stack>\n <Stack.Screen name=\"(tabs)\" options={{ headerShown: false }} />\n <Stack.Screen name=\"+not-found\" />\n </Stack>\n <PortalHost />\n </ThemeProvider>\n );\n}\n`,\n },\n\n // ─── app/(tabs)/_layout.tsx ─────────────────────────────────────────\n {\n path: \"app/(tabs)/_layout.tsx\",\n content: `import { Tabs } from \"expo-router\";\nimport { Platform } from \"react-native\";\nimport { Ionicons } from \"@expo/vector-icons\";\n\nexport default function TabLayout() {\n return (\n <Tabs\n screenOptions={{\n headerShadowVisible: false,\n tabBarStyle: Platform.select({\n ios: {\n position: \"absolute\",\n },\n default: {},\n }),\n }}\n >\n <Tabs.Screen\n name=\"index\"\n options={{\n title: \"Home\",\n tabBarIcon: ({ color, size }) => (\n <Ionicons name=\"home\" size={size} color={color} />\n ),\n }}\n />\n <Tabs.Screen\n name=\"explore\"\n options={{\n title: \"Explore\",\n tabBarIcon: ({ color, size }) => (\n <Ionicons name=\"search\" size={size} color={color} />\n ),\n }}\n />\n </Tabs>\n );\n}\n`,\n },\n\n // ─── app/(tabs)/index.tsx ───────────────────────────────────────────\n {\n path: \"app/(tabs)/index.tsx\",\n content: `import { Text, View } from \"react-native\";\n\nexport default function HomeScreen() {\n return (\n <View className=\"flex-1 items-center justify-center p-6\">\n <Text className=\"text-foreground text-2xl font-bold\">${projectName}</Text>\n <Text className=\"text-muted-foreground mt-2\">Welcome to your new app</Text>\n </View>\n );\n}\n`,\n },\n\n // ─── app/(tabs)/explore.tsx ─────────────────────────────────────────\n {\n path: \"app/(tabs)/explore.tsx\",\n content: `import { Text, View } from \"react-native\";\n\nexport default function ExploreScreen() {\n return (\n <View className=\"flex-1 items-center justify-center p-6\">\n <Text className=\"text-foreground text-2xl font-bold\">Explore</Text>\n <Text className=\"text-muted-foreground mt-2\">Discover new features</Text>\n </View>\n );\n}\n`,\n },\n\n // ─── app/+not-found.tsx ─────────────────────────────────────────────\n {\n path: \"app/+not-found.tsx\",\n content: `import { Link, Stack } from \"expo-router\";\nimport { Text, View } from \"react-native\";\n\nexport default function NotFoundScreen() {\n return (\n <>\n <Stack.Screen options={{ title: \"Oops!\" }} />\n <View className=\"flex-1 items-center justify-center p-5\">\n <Text className=\"text-foreground text-2xl font-bold\">This screen doesn't exist.</Text>\n <Link href=\"/\" className=\"mt-4 py-4\">\n <Text className=\"text-primary underline\">Go to home screen!</Text>\n </Link>\n </View>\n </>\n );\n}\n`,\n },\n\n // ─── lib/utils.ts (rnr official cn helper) ───────────────────────────\n {\n path: \"lib/utils.ts\",\n content: `import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n`,\n },\n\n // ─── lib/theme.ts (rnr official theme) ───────────────────────────────\n {\n path: \"lib/theme.ts\",\n content: `import { DarkTheme, DefaultTheme, type Theme } from '@react-navigation/native';\n\nexport const THEME = {\n light: {\n background: 'hsl(0 0% 100%)',\n foreground: 'hsl(0 0% 3.9%)',\n card: 'hsl(0 0% 100%)',\n cardForeground: 'hsl(0 0% 3.9%)',\n popover: 'hsl(0 0% 100%)',\n popoverForeground: 'hsl(0 0% 3.9%)',\n primary: 'hsl(0 0% 9%)',\n primaryForeground: 'hsl(0 0% 98%)',\n secondary: 'hsl(0 0% 96.1%)',\n secondaryForeground: 'hsl(0 0% 9%)',\n muted: 'hsl(0 0% 96.1%)',\n mutedForeground: 'hsl(0 0% 45.1%)',\n accent: 'hsl(0 0% 96.1%)',\n accentForeground: 'hsl(0 0% 9%)',\n destructive: 'hsl(0 84.2% 60.2%)',\n border: 'hsl(0 0% 89.8%)',\n input: 'hsl(0 0% 89.8%)',\n ring: 'hsl(0 0% 63%)',\n radius: '0.625rem',\n chart1: 'hsl(12 76% 61%)',\n chart2: 'hsl(173 58% 39%)',\n chart3: 'hsl(197 37% 24%)',\n chart4: 'hsl(43 74% 66%)',\n chart5: 'hsl(27 87% 67%)',\n },\n dark: {\n background: 'hsl(0 0% 3.9%)',\n foreground: 'hsl(0 0% 98%)',\n card: 'hsl(0 0% 3.9%)',\n cardForeground: 'hsl(0 0% 98%)',\n popover: 'hsl(0 0% 3.9%)',\n popoverForeground: 'hsl(0 0% 98%)',\n primary: 'hsl(0 0% 98%)',\n primaryForeground: 'hsl(0 0% 9%)',\n secondary: 'hsl(0 0% 14.9%)',\n secondaryForeground: 'hsl(0 0% 98%)',\n muted: 'hsl(0 0% 14.9%)',\n mutedForeground: 'hsl(0 0% 63.9%)',\n accent: 'hsl(0 0% 14.9%)',\n accentForeground: 'hsl(0 0% 98%)',\n destructive: 'hsl(0 70.9% 59.4%)',\n border: 'hsl(0 0% 14.9%)',\n input: 'hsl(0 0% 14.9%)',\n ring: 'hsl(300 0% 45%)',\n radius: '0.625rem',\n chart1: 'hsl(220 70% 50%)',\n chart2: 'hsl(160 60% 45%)',\n chart3: 'hsl(30 80% 55%)',\n chart4: 'hsl(280 65% 60%)',\n chart5: 'hsl(340 75% 55%)',\n },\n};\n\nexport const NAV_THEME: Record<'light' | 'dark', Theme> = {\n light: {\n ...DefaultTheme,\n colors: {\n background: THEME.light.background,\n border: THEME.light.border,\n card: THEME.light.card,\n notification: THEME.light.destructive,\n primary: THEME.light.primary,\n text: THEME.light.foreground,\n },\n },\n dark: {\n ...DarkTheme,\n colors: {\n background: THEME.dark.background,\n border: THEME.dark.border,\n card: THEME.dark.card,\n notification: THEME.dark.destructive,\n primary: THEME.dark.primary,\n text: THEME.dark.foreground,\n },\n },\n};\n`,\n },\n\n // ─── types/index.ts ─────────────────────────────────────────────────\n {\n path: \"types/index.ts\",\n content: `/** Global type definitions */\n\n/** Extend this to declare module-specific types */\ndeclare global {\n // Add global type augmentations here\n}\n\nexport {};\n`,\n },\n\n // ─── app.json ────────────────────────────────────────────────────────\n {\n path: \"app.json\",\n content: `{\n \"expo\": {\n \"name\": \"${projectName}\",\n \"slug\": \"${projectName}\",\n \"version\": \"1.0.0\",\n \"orientation\": \"portrait\",\n \"userInterfaceStyle\": \"light\",\n \"icon\": \"./assets/icon.png\",\n \"splash\": {\n \"image\": \"./assets/splash-icon.png\",\n \"resizeMode\": \"contain\",\n \"backgroundColor\": \"#ffffff\"\n },\n \"ios\": {\n \"supportsTablet\": true,\n \"bundleIdentifier\": \"com.${projectName}.app\"\n },\n \"android\": {\n \"adaptiveIcon\": {\n \"foregroundImage\": \"./assets/adaptive-icon.png\",\n \"backgroundColor\": \"#ffffff\"\n },\n \"package\": \"com.${projectName}.app\"\n },\n \"web\": {\n \"favicon\": \"./assets/favicon.png\"\n },\n \"plugins\": [\n \"expo-router\",\n \"expo-splash-screen\"\n ]\n }\n}\n`,\n },\n\n // ─── tsconfig.json ───────────────────────────────────────────────────\n {\n path: \"tsconfig.json\",\n content: `{\n \"extends\": \"expo/tsconfig.base\",\n \"compilerOptions\": {\n \"strict\": true,\n \"baseUrl\": \".\",\n \"paths\": {\n \"@/*\": [\"./*\"]\n }\n },\n \"include\": [\"**/*.ts\", \"**/*.tsx\", \".expo/types/**/*.ts\", \"expo-env.d.ts\", \"nativewind-env.d.ts\"]\n}\n`,\n },\n\n // ─── components.json (rnr CLI compatibility) ─────────────────────────\n {\n path: \"components.json\",\n content: `{\n \"$schema\": \"https://ui.shadcn.com/schema.json\",\n \"style\": \"new-york\",\n \"rsc\": false,\n \"tsx\": true,\n \"tailwind\": {\n \"config\": \"tailwind.config.js\",\n \"css\": \"global.css\",\n \"baseColor\": \"neutral\",\n \"cssVariables\": true\n },\n \"aliases\": {\n \"components\": \"@/components\",\n \"utils\": \"@/lib/utils\",\n \"ui\": \"@/components/ui\",\n \"lib\": \"@/lib\",\n \"hooks\": \"@/hooks\"\n }\n}\n`,\n },\n\n // ─── tailwind.config.js (rnr official) ──────────────────────────────\n {\n path: \"tailwind.config.js\",\n content: `const { hairlineWidth } = require('nativewind/theme');\n\n/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n darkMode: 'class',\n content: ['./app/**/*.{ts,tsx}', './components/**/*.{ts,tsx}'],\n presets: [require('nativewind/preset')],\n theme: {\n extend: {\n colors: {\n border: 'hsl(var(--border))',\n input: 'hsl(var(--input))',\n ring: 'hsl(var(--ring))',\n background: 'hsl(var(--background))',\n foreground: 'hsl(var(--foreground))',\n primary: {\n DEFAULT: 'hsl(var(--primary))',\n foreground: 'hsl(var(--primary-foreground))',\n },\n secondary: {\n DEFAULT: 'hsl(var(--secondary))',\n foreground: 'hsl(var(--secondary-foreground))',\n },\n destructive: {\n DEFAULT: 'hsl(var(--destructive))',\n foreground: 'hsl(var(--destructive-foreground))',\n },\n muted: {\n DEFAULT: 'hsl(var(--muted))',\n foreground: 'hsl(var(--muted-foreground))',\n },\n accent: {\n DEFAULT: 'hsl(var(--accent))',\n foreground: 'hsl(var(--accent-foreground))',\n },\n popover: {\n DEFAULT: 'hsl(var(--popover))',\n foreground: 'hsl(var(--popover-foreground))',\n },\n card: {\n DEFAULT: 'hsl(var(--card))',\n foreground: 'hsl(var(--card-foreground))',\n },\n },\n borderRadius: {\n lg: 'var(--radius)',\n md: 'calc(var(--radius) - 2px)',\n sm: 'calc(var(--radius) - 4px)',\n },\n borderWidth: {\n hairline: hairlineWidth(),\n },\n keyframes: {\n 'accordion-down': {\n from: { height: '0' },\n to: { height: 'var(--radix-accordion-content-height)' },\n },\n 'accordion-up': {\n from: { height: 'var(--radix-accordion-content-height)' },\n to: { height: '0' },\n },\n },\n animation: {\n 'accordion-down': 'accordion-down 0.2s ease-out',\n 'accordion-up': 'accordion-up 0.2s ease-out',\n },\n },\n },\n future: {\n hoverOnlyWhenSupported: true,\n },\n plugins: [require('tailwindcss-animate')],\n};\n`,\n },\n\n // ─── metro.config.js (rnr official: inlineRem: 16) ──────────────────\n {\n path: \"metro.config.js\",\n content: `const { getDefaultConfig } = require(\"expo/metro-config\");\nconst { withNativeWind } = require(\"nativewind/metro\");\n\nconst config = getDefaultConfig(__dirname);\n\nmodule.exports = withNativeWind(config, { input: \"./global.css\", inlineRem: 16 });\n`,\n },\n\n // ─── babel.config.js ─────────────────────────────────────────────────\n {\n path: \"babel.config.js\",\n content: `module.exports = function (api) {\n api.cache(true);\n return {\n presets: [\n [\"babel-preset-expo\", { jsxImportSource: \"nativewind\" }],\n \"nativewind/babel\",\n ],\n plugins: [],\n };\n};\n`,\n },\n\n // ─── global.css (rnr official CSS variables) ─────────────────────────\n {\n path: \"global.css\",\n content: `@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n@layer base {\n :root {\n --background: 0 0% 100%;\n --foreground: 0 0% 3.9%;\n --card: 0 0% 100%;\n --card-foreground: 0 0% 3.9%;\n --popover: 0 0% 100%;\n --popover-foreground: 0 0% 3.9%;\n --primary: 0 0% 9%;\n --primary-foreground: 0 0% 98%;\n --secondary: 0 0% 96.1%;\n --secondary-foreground: 0 0% 9%;\n --muted: 0 0% 96.1%;\n --muted-foreground: 0 0% 45.1%;\n --accent: 0 0% 96.1%;\n --accent-foreground: 0 0% 9%;\n --destructive: 0 84.2% 60.2%;\n --border: 0 0% 89.8%;\n --input: 0 0% 89.8%;\n --ring: 0 0% 63%;\n --radius: 0.625rem;\n --chart-1: 12 76% 61%;\n --chart-2: 173 58% 39%;\n --chart-3: 197 37% 24%;\n --chart-4: 43 74% 66%;\n --chart-5: 27 87% 67%;\n }\n\n .dark:root {\n --background: 0 0% 3.9%;\n --foreground: 0 0% 98%;\n --card: 0 0% 3.9%;\n --card-foreground: 0 0% 98%;\n --popover: 0 0% 3.9%;\n --popover-foreground: 0 0% 98%;\n --primary: 0 0% 98%;\n --primary-foreground: 0 0% 9%;\n --secondary: 0 0% 14.9%;\n --secondary-foreground: 0 0% 98%;\n --muted: 0 0% 14.9%;\n --muted-foreground: 0 0% 63.9%;\n --accent: 0 0% 14.9%;\n --accent-foreground: 0 0% 98%;\n --destructive: 0 70.9% 59.4%;\n --border: 0 0% 14.9%;\n --input: 0 0% 14.9%;\n --ring: 300 0% 45%;\n --chart-1: 220 70% 50%;\n --chart-2: 160 60% 45%;\n --chart-3: 30 80% 55%;\n --chart-4: 280 65% 60%;\n --chart-5: 340 75% 55%;\n }\n}\n`,\n },\n\n // ─── .gitignore ─────────────────────────────────────────────────────\n {\n path: \".gitignore\",\n content: `# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files\n\n# dependencies\nnode_modules/\n\n# Expo\n.expo/\ndist/\nweb-build/\n\n# Native\n*.orig.*\n*.jks\n*.p8\n*.p12\n*.key\n*.mobileprovision\n\n# Metro\n.metro-health-check*\n\n# debug\nnpm-debug.*\nyarn-debug.*\nyarn-error.*\n\n# macOS\n.DS_Store\n*.pem\n\n# local env files\n.env*.local\n\n# typescript\n*.tsbuildinfo\n\n# generated files\nexpo-env.d.ts\n`,\n },\n ];\n}\n","import type { TemplateFile } from \"../types\";\nimport { lines } from \"../utils/lines\";\n\n/**\n * Generate UI template files for the \"login-tabs\" preset.\n * Uses reactnativereusables official patterns (NAV_THEME, PortalHost, cn()).\n * Tabs: Index + Explore + Mine\n */\nexport function generateLoginTabsTemplates(projectName: string): TemplateFile[] {\n return [\n // ─── app/_layout.tsx (overwrite base) ────────────────────────────────\n {\n path: \"app/_layout.tsx\",\n content: lines(\n 'import \"../global.css\";',\n 'import { DarkTheme, DefaultTheme, ThemeProvider } from \"@react-navigation/native\";',\n 'import { useFonts } from \"expo-font\";',\n 'import { Stack } from \"expo-router\";',\n 'import * as SplashScreen from \"expo-splash-screen\";',\n 'import { useEffect } from \"react\";',\n 'import { useColorScheme } from \"react-native\";',\n 'import { PortalHost } from \"@rn-primitives/portal\";',\n 'import { NAV_THEME } from \"@/lib/theme\";',\n \"\",\n \"SplashScreen.preventAutoHideAsync();\",\n \"\",\n \"export default function RootLayout() {\",\n \" const colorScheme = useColorScheme();\",\n \" const [loaded] = useFonts({\",\n \" NunitoBold: require(\\\"../assets/fonts/Nunito-Bold.ttf\\\"),\",\n \" });\",\n \"\",\n \" useEffect(() => {\",\n \" if (loaded) {\",\n \" SplashScreen.hideAsync();\",\n \" }\",\n \" }, [loaded]);\",\n \"\",\n \" if (!loaded) {\",\n \" return null;\",\n \" }\",\n \"\",\n \" return (\",\n \" <ThemeProvider value={colorScheme === \\\"dark\\\" ? NAV_THEME.dark : NAV_THEME.light}>\",\n \" <Stack>\",\n ' <Stack.Screen name=\"login\" options={{ headerShown: false }} />',\n ' <Stack.Screen name=\"(tabs)\" options={{ headerShown: false }} />',\n ' <Stack.Screen name=\"+not-found\" />',\n \" </Stack>\",\n \" <PortalHost />\",\n \" </ThemeProvider>\",\n \" );\",\n \"}\",\n \"\"\n ),\n },\n\n // ─── app/login.tsx ───────────────────────────────────────────────────\n {\n path: \"app/login.tsx\",\n content: lines(\n 'import { SignInForm } from \"@/components/SignInForm\";',\n 'import { View } from \"react-native\";',\n \"\",\n \"export default function LoginScreen() {\",\n \" return (\",\n ' <View className=\"flex-1 items-center justify-center p-4\">',\n \" <SignInForm />\",\n \" </View>\",\n \" );\",\n \"}\",\n \"\"\n ),\n },\n\n // ─── app/(tabs)/_layout.tsx (overwrite base) ───────────────────────\n {\n path: \"app/(tabs)/_layout.tsx\",\n content: lines(\n 'import { Tabs } from \"expo-router\";',\n 'import { Platform } from \"react-native\";',\n 'import { Ionicons } from \"@expo/vector-icons\";',\n \"\",\n \"export default function TabLayout() {\",\n \" return (\",\n \" <Tabs\",\n \" screenOptions={{\",\n \" headerShadowVisible: false,\",\n \" tabBarStyle: Platform.select({\",\n \" ios: {\",\n \" position: \\\"absolute\\\",\",\n \" },\",\n \" default: {},\",\n \" }),\",\n \" }}\",\n \" >\",\n \" <Tabs.Screen\",\n ' name=\"index\"',\n \" options={{\",\n ' title: \"Home\",',\n \" tabBarIcon: ({ color, size }) => (\",\n ' <Ionicons name=\"home\" size={size} color={color} />',\n \" ),\",\n \" }}\",\n \" />\",\n \" <Tabs.Screen\",\n ' name=\"explore\"',\n \" options={{\",\n ' title: \"Explore\",',\n \" tabBarIcon: ({ color, size }) => (\",\n ' <Ionicons name=\"search\" size={size} color={color} />',\n \" ),\",\n \" }}\",\n \" />\",\n \" <Tabs.Screen\",\n ' name=\"mine\"',\n \" options={{\",\n ' title: \"Mine\",',\n \" tabBarIcon: ({ color, size }) => (\",\n ' <Ionicons name=\"person\" size={size} color={color} />',\n \" ),\",\n \" }}\",\n \" />\",\n \" </Tabs>\",\n \" );\",\n \"}\",\n \"\"\n ),\n },\n\n // ─── app/(tabs)/index.tsx ──────────────────────────────────────────\n {\n path: \"app/(tabs)/index.tsx\",\n content: lines(\n 'import { Button } from \"@/components/ui/button\";',\n 'import {',\n ' AlertDialog,',\n ' AlertDialogAction,',\n ' AlertDialogCancel,',\n ' AlertDialogContent,',\n ' AlertDialogDescription,',\n ' AlertDialogFooter,',\n ' AlertDialogHeader,',\n ' AlertDialogTitle,',\n ' AlertDialogTrigger,',\n '} from \"@/components/ui/alert-dialog\";',\n 'import { Text } from \"@/components/ui/text\";',\n 'import { View } from \"react-native\";',\n \"\",\n \"export default function HomeScreen() {\",\n \" return (\",\n ' <View className=\"flex-1 gap-6 p-6\">',\n ' <Text variant=\"h3\">UI Components</Text>',\n ' <Text variant=\"muted\">React Native Reusables components showcase</Text>',\n \"\",\n \" {/* Button variants */}\",\n ' <View className=\"gap-3\">',\n ' <Text variant=\"large\">Buttons</Text>',\n \" <View className=\\\"flex-row flex-wrap gap-2\\\">\",\n ' <Button onPress={() => {}}>',\n ' <Text>Default</Text>',\n \" </Button>\",\n ' <Button variant=\"secondary\" onPress={() => {}}>',\n ' <Text>Secondary</Text>',\n \" </Button>\",\n ' <Button variant=\"destructive\" onPress={() => {}}>',\n ' <Text>Destructive</Text>',\n \" </Button>\",\n ' <Button variant=\"outline\" onPress={() => {}}>',\n ' <Text>Outline</Text>',\n \" </Button>\",\n ' <Button variant=\"ghost\" onPress={() => {}}>',\n ' <Text>Ghost</Text>',\n \" </Button>\",\n ' <Button variant=\"link\" onPress={() => {}}>',\n ' <Text>Link</Text>',\n \" </Button>\",\n \" </View>\",\n \" </View>\",\n \"\",\n \" {/* AlertDialog */}\",\n ' <View className=\"gap-3\">',\n ' <Text variant=\"large\">Alert Dialog</Text>',\n \" <AlertDialog>\",\n \" <AlertDialogTrigger asChild>\",\n ' <Button variant=\"outline\">',\n ' <Text>Show Alert</Text>',\n \" </Button>\",\n \" </AlertDialogTrigger>\",\n \" <AlertDialogContent>\",\n \" <AlertDialogHeader>\",\n ' <AlertDialogTitle>Are you sure?</AlertDialogTitle>',\n \" <AlertDialogDescription>\",\n \" This action cannot be undone. This will permanently delete your account and remove your data from our servers.\",\n \" </AlertDialogDescription>\",\n \" </AlertDialogHeader>\",\n \" <AlertDialogFooter>\",\n \" <AlertDialogCancel>\",\n ' <Text>Cancel</Text>',\n \" </AlertDialogCancel>\",\n \" <AlertDialogAction>\",\n ' <Text>Continue</Text>',\n \" </AlertDialogAction>\",\n \" </AlertDialogFooter>\",\n \" </AlertDialogContent>\",\n \" </AlertDialog>\",\n \" </View>\",\n \" </View>\",\n \" );\",\n \"}\",\n \"\"\n ),\n },\n\n // ─── app/(tabs)/explore.tsx ────────────────────────────────────────\n {\n path: \"app/(tabs)/explore.tsx\",\n content: lines(\n 'import { Text } from \"@/components/ui/text\";',\n 'import { Card, CardContent, CardDescription, CardHeader, CardTitle } from \"@/components/ui/card\";',\n 'import { View, ScrollView } from \"react-native\";',\n \"\",\n \"const ITEMS = Array.from({ length: 20 }, (_, i) => ({\",\n \" id: String(i + 1),\",\n \" title: `Item ${i + 1}`,\",\n \" description: `Description for item ${i + 1}`,\",\n \"}));\",\n \"\",\n \"export default function ExploreScreen() {\",\n \" return (\",\n ' <ScrollView className=\"flex-1 p-4\">',\n ' <Text variant=\"h3\" className=\"mb-4\">Explore</Text>',\n ' <View className=\"gap-3\">',\n \" {ITEMS.map((item) => (\",\n ' <Card key={item.id}>',\n \" <CardHeader>\",\n \" <CardTitle>{item.title}</CardTitle>\",\n \" <CardDescription>{item.description}</CardDescription>\",\n \" </CardHeader>\",\n \" <CardContent />\",\n \" </Card>\",\n \" ))}\",\n \" </View>\",\n \" </ScrollView>\",\n \" );\",\n \"}\",\n \"\"\n ),\n },\n\n // ─── app/(tabs)/mine.tsx ────────────────────────────────────────────\n {\n path: \"app/(tabs)/mine.tsx\",\n content: lines(\n 'import { Button } from \"@/components/ui/button\";',\n 'import { Card, CardContent, CardHeader, CardTitle } from \"@/components/ui/card\";',\n 'import { Text } from \"@/components/ui/text\";',\n 'import { View } from \"react-native\";',\n 'import { router } from \"expo-router\";',\n \"\",\n \"export default function MineScreen() {\",\n \" return (\",\n ' <View className=\"flex-1 p-6\">',\n ' <Text variant=\"h3\" className=\"mb-6\">Mine</Text>',\n \"\",\n ' <Card className=\"mb-6\">',\n ' <CardHeader>',\n ' <CardTitle>Profile</CardTitle>',\n \" </CardHeader>\",\n \" <CardContent>\",\n ' <View className=\"gap-2\">',\n ' <Text variant=\"muted\">User Name</Text>',\n ' <Text>user@example.com</Text>',\n \" </View>\",\n \" </CardContent>\",\n \" </Card>\",\n \"\",\n ' <Button',\n ' variant=\"destructive\"',\n \" className=\\\"w-full\\\"\",\n \" onPress={() => {\",\n ' router.replace(\"/login\");',\n \" }}\",\n \" >\",\n ' <Text>Sign Out</Text>',\n \" </Button>\",\n \" </View>\",\n \" );\",\n \"}\",\n \"\"\n ),\n },\n\n // ─── components/SignInForm.tsx ────────────────────────────────────\n {\n path: \"components/SignInForm.tsx\",\n content: lines(\n 'import { Button } from \"@/components/ui/button\";',\n 'import {',\n ' Card,',\n ' CardContent,',\n ' CardDescription,',\n ' CardHeader,',\n ' CardTitle,',\n '} from \"@/components/ui/card\";',\n 'import { Input } from \"@/components/ui/input\";',\n 'import { Label } from \"@/components/ui/label\";',\n 'import { Separator } from \"@/components/ui/separator\";',\n 'import { Text } from \"@/components/ui/text\";',\n 'import * as React from \"react\";',\n 'import { Pressable, type TextInput, View } from \"react-native\";',\n 'import { router } from \"expo-router\";',\n \"\",\n \"export function SignInForm() {\",\n \" const passwordInputRef = React.useRef<TextInput>(null);\",\n \"\",\n \" function onEmailSubmitEditing() {\",\n \" passwordInputRef.current?.focus();\",\n \" }\",\n \"\",\n \" function onSubmit() {\",\n \" // TODO: Submit form and navigate to protected screen if successful\",\n ' router.replace(\"/(tabs)\");',\n \" }\",\n \"\",\n \" return (\",\n ' <View className=\"gap-6 w-full max-w-sm\">',\n ' <Card className=\"shadow-none sm:shadow-sm sm:shadow-black/5\">',\n \" <CardHeader>\",\n ' <CardTitle className=\"text-center text-xl sm:text-left\">Sign in to your app</CardTitle>',\n ' <CardDescription className=\"text-center sm:text-left\">',\n \" Welcome back! Please sign in to continue\",\n \" </CardDescription>\",\n \" </CardHeader>\",\n ' <CardContent className=\"gap-6\">',\n ' <View className=\"gap-6\">',\n ' <View className=\"gap-1.5\">',\n ' <Label nativeID=\"email\">Email</Label>',\n \" <Input\",\n ' placeholder=\"m@example.com\"',\n ' keyboardType=\"email-address\"',\n ' autoComplete=\"email\"',\n ' autoCapitalize=\"none\"',\n \" onSubmitEditing={onEmailSubmitEditing}\",\n ' returnKeyType=\"next\"',\n ' submitBehavior=\"submit\"',\n \" />\",\n \" </View>\",\n ' <View className=\"gap-1.5\">',\n ' <View className=\"flex-row items-center\">',\n ' <Label nativeID=\"password\">Password</Label>',\n \" <Button\",\n ' variant=\"link\"',\n ' size=\"sm\"',\n ' className=\"ml-auto h-4 px-1 py-0 sm:h-4\"',\n \" onPress={() => {\",\n \" // TODO: Navigate to forgot password screen\",\n \" }}\",\n \" >\",\n ' <Text className=\"font-normal leading-4\">Forgot your password?</Text>',\n \" </Button>\",\n \" </View>\",\n \" <Input\",\n \" ref={passwordInputRef}\",\n \" secureTextEntry\",\n ' returnKeyType=\"send\"',\n \" onSubmitEditing={onSubmit}\",\n \" />\",\n \" </View>\",\n ' <Button className=\"w-full\" onPress={onSubmit}>',\n ' <Text>Continue</Text>',\n \" </Button>\",\n \" </View>\",\n ' <Text className=\"text-center text-sm\">',\n \" Don't have an account?{\\\" \\\"}\",\n \" <Pressable\",\n \" onPress={() => {\",\n \" // TODO: Navigate to sign up screen\",\n \" }}\",\n \" >\",\n ' <Text className=\"text-sm underline underline-offset-4\">Sign up</Text>',\n \" </Pressable>\",\n \" </Text>\",\n ' <View className=\"flex-row items-center\">',\n ' <Separator className=\"flex-1\" />',\n ' <Text className=\"text-muted-foreground px-4 text-sm\">or</Text>',\n ' <Separator className=\"flex-1\" />',\n \" </View>\",\n \" </CardContent>\",\n \" </Card>\",\n \" </View>\",\n \" );\",\n \"}\",\n \"\"\n ),\n },\n ];\n}\n","import fs from \"fs-extra\";\nimport path from \"path\";\n\n/**\n * Recursively copy all files from a source directory to a destination directory.\n */\nexport async function copyDirectory(\n src: string,\n dest: string\n): Promise<void> {\n await fs.copy(src, dest, { overwrite: true });\n}\n\n/**\n * Write a file, creating parent directories as needed.\n */\nexport async function writeFile(\n filePath: string,\n content: string\n): Promise<void> {\n const dir = path.dirname(filePath);\n await fs.ensureDir(dir);\n await fs.writeFile(filePath, content, \"utf-8\");\n}\n\n/**\n * Check if a path exists.\n */\nexport async function pathExists(filePath: string): Promise<boolean> {\n return fs.pathExists(filePath);\n}\n\n/**\n * Read a JSON file and parse it.\n */\nexport async function readJson<T = Record<string, unknown>>(\n filePath: string\n): Promise<T> {\n return fs.readJson(filePath) as Promise<T>;\n}\n\n/**\n * Write a JSON object to a file with formatting.\n */\nexport async function writeJson(\n filePath: string,\n data: unknown,\n spaces: number = 2\n): Promise<void> {\n const dir = path.dirname(filePath);\n await fs.ensureDir(dir);\n await fs.writeJson(filePath, data, { spaces });\n}\n\n/**\n * Replace template variables in content string.\n * Supports {{variableName}} syntax.\n */\nexport function replaceTemplateVars(\n content: string,\n vars: Record<string, string>\n): string {\n let result = content;\n for (const [key, value] of Object.entries(vars)) {\n const regex = new RegExp(`\\\\{\\\\{\\\\s*${key}\\\\s*\\\\}\\\\}`, \"g\");\n result = result.replace(regex, value);\n }\n return result;\n}\n","import { writeJson, readJson } from \"./file\";\n\ninterface PackageJsonDeps {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n}\n\n/**\n * Merge dependencies into a package.json file.\n */\nexport async function mergeDependencies(\n pkgPath: string,\n deps: Record<string, string>,\n devDeps: Record<string, string>\n): Promise<void> {\n const pkg = await readJson<PackageJsonDeps>(pkgPath);\n\n pkg.dependencies = {\n ...(pkg.dependencies || {}),\n ...deps,\n };\n\n pkg.devDependencies = {\n ...(pkg.devDependencies || {}),\n ...devDeps,\n };\n\n await writeJson(pkgPath, pkg);\n}\n\n/**\n * Generate base package.json content for a new Expo project.\n *\n * ALL module dependencies are included by default — module selection only\n * controls which code/template files are generated, not which packages are\n * installed. This avoids version-mismatch issues and ensures every project\n * is ready to use any module without extra installs.\n */\nexport function generateBasePackageJson(\n projectName: string\n): Record<string, unknown> {\n return {\n name: projectName,\n version: \"1.0.0\",\n main: \"expo-router/entry\",\n scripts: {\n start: \"expo start\",\n \"reset-project\": \"node ./scripts/reset-project.js\",\n android: \"expo start --android\",\n ios: \"expo start --ios\",\n web: \"expo start --web\",\n lint: \"expo lint\",\n },\n dependencies: {\n // ─── Expo core (aligned with create-expo-app SDK 54) ───────────\n expo: \"~54.0.33\",\n \"expo-router\": \"~6.0.23\",\n \"expo-linking\": \"~8.0.12\",\n \"expo-constants\": \"~18.0.13\",\n \"expo-status-bar\": \"~3.0.9\",\n \"expo-splash-screen\": \"~31.0.13\",\n \"expo-font\": \"~14.0.11\",\n \"expo-haptics\": \"~15.0.8\",\n \"expo-image\": \"~3.0.11\",\n \"expo-symbols\": \"~1.0.8\",\n \"expo-system-ui\": \"~6.0.9\",\n \"expo-web-browser\": \"~15.0.10\",\n\n // ─── React & React Native ─────────────────────────────────────\n react: \"19.1.0\",\n \"react-dom\": \"19.1.0\",\n \"react-native\": \"0.81.5\",\n \"react-native-web\": \"~0.21.0\",\n \"@expo/vector-icons\": \"^15.0.3\",\n \"@react-navigation/bottom-tabs\": \"^7.4.0\",\n \"@react-navigation/elements\": \"^2.6.3\",\n \"@react-navigation/native\": \"^7.1.8\",\n \"react-native-safe-area-context\": \"~5.6.0\",\n \"react-native-screens\": \"~4.16.0\",\n\n // ─── Animation (default in all projects) ──────────────────────\n \"react-native-reanimated\": \"~4.1.1\",\n \"react-native-worklets\": \"~0.5.1\",\n \"react-native-gesture-handler\": \"~2.28.0\",\n\n // ─── Styling ──────────────────────────────────────────────────\n nativewind: \"^4.1.0\",\n tailwindcss: \"^3.4.0\",\n \"tailwindcss-animate\": \"^1.0.7\",\n \"react-native-svg\": \"15.12.1\",\n\n // ─── Network ─────────────────────────────────────────────────\n \"@tanstack/react-query\": \"^5.60.0\",\n\n // ─── State ───────────────────────────────────────────────────\n zustand: \"^5.0.0\",\n\n // ─── Storage ─────────────────────────────────────────────────\n \"react-native-mmkv\": \"^3.1.0\",\n\n // ─── Payment ─────────────────────────────────────────────────\n \"react-native-iap\": \"^12.15.0\",\n\n // ─── Form ────────────────────────────────────────────────────\n \"react-hook-form\": \"^7.54.0\",\n \"@hookform/resolvers\": \"^3.9.0\",\n zod: \"^3.24.0\",\n\n // ─── Video ───────────────────────────────────────────────────\n \"expo-av\": \"~16.0.8\",\n\n // ─── Auth — Google ───────────────────────────────────────────\n \"@react-native-google-signin/google-signin\": \"^13.1.0\",\n\n // ─── Auth — Facebook ─────────────────────────────────────────\n \"expo-auth-session\": \"~7.0.11\",\n \"expo-crypto\": \"~15.0.9\",\n\n // ─── Auth — Apple ────────────────────────────────────────────\n \"expo-apple-authentication\": \"~8.0.8\",\n\n // ─── WebView ─────────────────────────────────────────────────\n \"react-native-webview\": \"~13.15.0\",\n\n // ─── i18n ────────────────────────────────────────────────────\n i18next: \"^24.2.0\",\n \"react-i18next\": \"^15.2.0\",\n \"expo-localization\": \"~17.0.8\",\n\n // ─── OTA ─────────────────────────────────────────────────────\n \"expo-updates\": \"~29.0.17\",\n\n // ─── Notifications ───────────────────────────────────────────\n \"expo-notifications\": \"~0.32.17\",\n\n // ─── Permissions ─────────────────────────────────────────────\n \"expo-image-picker\": \"~17.0.11\",\n \"expo-camera\": \"~17.0.10\",\n \"expo-location\": \"~19.0.8\",\n\n // ─── Bottom Sheet ────────────────────────────────────────────\n \"@gorhom/bottom-sheet\": \"^5.1.0\",\n\n // ─── FlashList ───────────────────────────────────────────────\n \"@shopify/flash-list\": \"2.0.2\",\n\n // ─── UI — reactnative.reusables ──────────────────────────────\n \"class-variance-authority\": \"^0.7.1\",\n clsx: \"^2.1.1\",\n \"tailwind-merge\": \"^2.6.0\",\n \"@rn-primitives/slot\": \"^1.1.0\",\n \"@rn-primitives/types\": \"^1.1.0\",\n \"@rn-primitives/label\": \"^1.1.0\",\n \"@rn-primitives/separator\": \"^1.1.0\",\n \"@rn-primitives/alert-dialog\": \"^1.1.0\",\n \"@rn-primitives/portal\": \"^1.1.0\",\n },\n devDependencies: {\n \"@types/react\": \"~19.1.0\",\n typescript: \"~5.9.2\",\n eslint: \"^9.25.0\",\n \"eslint-config-expo\": \"~10.0.0\",\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAkB;AAClB,uBAAwB;AACxB,mBAAsB;AACtB,IAAAA,mBAAgB;AAChB,iBAAgB;AAChB,IAAAC,eAAiB;AACjB,qBAAoB;;;ACEb,SAAS,sBAAsB,SAAwB;AAC5D,UACG,QAAQ,uBAAuB,EAC/B,YAAY,iDAAiD,EAC7D,OAAO,OAAO,gBAAwB;AACrC,UAAM,cAAc,WAAW;AAAA,EACjC,CAAC;AACL;AAKO,SAAS,uBAAuB,SAAwB;AAC7D,UACG,QAAQ,SAAS,EACjB,YAAY,sEAAsE,EAClF,OAAO,gBAAgB,kDAAkD,QAAQ,IAAI,CAAC,EACtF,OAAO,OAAO,YAA6B;AAC1C,UAAM,eAAe,QAAQ,GAAG;AAAA,EAClC,CAAC;AACL;AAKO,SAAS,mBAAmB,SAAwB;AACzD,UACG,QAAQ,kBAAkB,EAC1B,YAAY,2DAA2D,EACvE,OAAO,gBAAgB,kDAAkD,QAAQ,IAAI,CAAC,EACtF,OAAO,OAAO,WAAqB,YAA6B;AAC/D,UAAM,UAAU,WAAW,QAAQ,GAAG;AAAA,EACxC,CAAC;AACL;;;ACrCO,SAAS,SAAS,MAAwB;AAC/C,SAAO,KAAK,KAAK,IAAI;AACvB;;;ACHA,IAAM,gBAA2B;AAAA,EAC/B,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,cAAc;AAAA,IACZ,yBAAyB;AAAA,EAC3B;AAAA,EACA,iBAAiB,CAAC;AAAA,EAClB,OAAO;AAAA,IACL;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MAC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AAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;A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AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,EAAE,KAAK,IAAI;AAAA,IACb;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,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CN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,iBAAiB,CAAC,qBAAqB;AAAA,EACvC,eAAe;AAAA,IACb;AAAA,EACF;AACF;AAEA,IAAO,uBAAQ;;;ACjOf,IAAM,mBAA8B;AAAA,EAClC,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,cAAc;AAAA,IACZ,qBAAqB;AAAA,IACrB,eAAe;AAAA,IACf,iBAAiB;AAAA,EACnB;AAAA,EACA,iBAAiB,CAAC;AAAA,EAClB,OAAO;AAAA,IACL;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,WAAW;AAAA,IACT,SAAS;AAAA,MACP;AAAA,QACE;AAAA,QACA;AAAA,UACE,kBAAkB;AAAA,QACpB;AAAA,MACF;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;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA;AAAA,IAGA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA;AAAA,IAGA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA;AAAA,IAGA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA;AAAA,IAGA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA;AAAA,IAGA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA;AAAA,IAGA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA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CA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA;AAAA,IAGA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA;AAAA,IAGA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA;AAAA,IAGA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA;AAAA,IAGA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QA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","names":["import_fs_extra","import_path","path","fs","fs","path","chalk","path","fse","prompts","ora"]}
|