expo-bbase 1.0.0 → 1.0.2
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 +6 -18
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -38,6 +38,7 @@ var import_chalk = __toESM(require("chalk"));
|
|
|
38
38
|
var import_prompts = __toESM(require("prompts"));
|
|
39
39
|
var import_ora = __toESM(require("ora"));
|
|
40
40
|
var import_path2 = __toESM(require("path"));
|
|
41
|
+
var import_fs_extra2 = __toESM(require("fs-extra"));
|
|
41
42
|
var import_commander = require("commander");
|
|
42
43
|
|
|
43
44
|
// src/commands/create.ts
|
|
@@ -608,17 +609,7 @@ var storageModule = {
|
|
|
608
609
|
"export default storage;"
|
|
609
610
|
)
|
|
610
611
|
}
|
|
611
|
-
]
|
|
612
|
-
appConfig: {
|
|
613
|
-
plugins: [
|
|
614
|
-
[
|
|
615
|
-
"react-native-mmkv",
|
|
616
|
-
{
|
|
617
|
-
MMKV_APP_ID: "app-storage"
|
|
618
|
-
}
|
|
619
|
-
]
|
|
620
|
-
]
|
|
621
|
-
}
|
|
612
|
+
]
|
|
622
613
|
};
|
|
623
614
|
var storage_default = storageModule;
|
|
624
615
|
|
|
@@ -3510,9 +3501,7 @@ async function createProject(projectName) {
|
|
|
3510
3501
|
}
|
|
3511
3502
|
async function updateAppJson(targetDir, selectedModules, projectName) {
|
|
3512
3503
|
const appJsonPath = import_path2.default.join(targetDir, "app.json");
|
|
3513
|
-
const appJson = await
|
|
3514
|
-
(fs2) => fs2.readJson(appJsonPath)
|
|
3515
|
-
);
|
|
3504
|
+
const appJson = await import_fs_extra2.default.readJson(appJsonPath);
|
|
3516
3505
|
const existingPlugins = appJson.expo?.plugins || [];
|
|
3517
3506
|
for (const mod of selectedModules) {
|
|
3518
3507
|
if (mod.appConfig?.plugins) {
|
|
@@ -3533,8 +3522,7 @@ async function updateAppJson(targetDir, selectedModules, projectName) {
|
|
|
3533
3522
|
}
|
|
3534
3523
|
async function updateBabelConfig(targetDir, selectedModules) {
|
|
3535
3524
|
const babelPath = import_path2.default.join(targetDir, "babel.config.js");
|
|
3536
|
-
|
|
3537
|
-
let content = await fs2.readFile(babelPath, "utf-8");
|
|
3525
|
+
let content = await import_fs_extra2.default.readFile(babelPath, "utf-8");
|
|
3538
3526
|
const extraPlugins = [];
|
|
3539
3527
|
for (const mod of selectedModules) {
|
|
3540
3528
|
if (mod.babelPlugins) {
|
|
@@ -3547,12 +3535,12 @@ async function updateBabelConfig(targetDir, selectedModules) {
|
|
|
3547
3535
|
/plugins:\s*\[([^\]]*)\]/,
|
|
3548
3536
|
`plugins: [$1${pluginStrings ? ",\n" + pluginStrings : ""}]`
|
|
3549
3537
|
);
|
|
3550
|
-
await
|
|
3538
|
+
await import_fs_extra2.default.writeFile(babelPath, content, "utf-8");
|
|
3551
3539
|
}
|
|
3552
3540
|
}
|
|
3553
3541
|
async function updateLayoutFile(targetDir, selectedModules) {
|
|
3554
3542
|
const layoutPath = import_path2.default.join(targetDir, "app/_layout.tsx");
|
|
3555
|
-
const fs2 =
|
|
3543
|
+
const fs2 = import_fs_extra2.default;
|
|
3556
3544
|
let content = await fs2.readFile(layoutPath, "utf-8");
|
|
3557
3545
|
const extraImports = [];
|
|
3558
3546
|
const extraProviderPairs = [];
|
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/utils/file.ts","../src/utils/package.ts"],"sourcesContent":["import chalk from \"chalk\";\nimport prompts from \"prompts\";\nimport ora from \"ora\";\nimport path from \"path\";\nimport { Command } from \"commander\";\nimport { registerCreateCommand } from \"./commands/create\";\nimport { modules, getModulesByIds } from \"./modules\";\nimport { generateBaseTemplates } from \"./templates/base\";\nimport { writeFile, writeJson, replaceTemplateVars } from \"./utils/file\";\nimport { generateBasePackageJson } from \"./utils/package\";\nimport type { ModuleDef } from \"./types\";\nimport { execa } from \"execa\";\n\n/**\n * Main entry point for the CLI.\n */\nexport async function run(): Promise<void> {\n const program = new Command();\n\n program\n .name(\"expo-bbase\")\n .description(\"Expo SDK 54+ 脚手架 CLI 工具\")\n .version(\"1.0.0\");\n\n // Register the 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\n await program.parseAsync(process.argv);\n}\n\n/**\n * Create a new Expo project with interactive module selection.\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 脚手架工具 ║\")\n );\n console.log(\n chalk.bold.cyan(\" ╚══════════════════════════════════════╝\")\n );\n console.log();\n\n // ─── Step 1: Interactive module selection ──────────────────────────\n const choices = modules.map((m) => ({\n title: `${chalk.bold(m.name)} — ${chalk.gray(m.description)}`,\n value: m.id,\n selected: m.defaultChecked,\n }));\n\n const { selectedModules } = await prompts({\n type: \"multiselect\",\n name: \"selectedModules\",\n message: \"选择需要的功能模块(空格切换,回车确认)\",\n choices,\n hint: \"- 空格切换选择 · a 全选/取消 · 回车确认\",\n instructions: false,\n });\n\n // User cancelled the prompt\n if (selectedModules === undefined) {\n console.log(chalk.yellow(\"\\n已取消创建项目。\"));\n process.exit(0);\n }\n\n const selectedModuleDefs = getModulesByIds(selectedModules as string[]);\n const targetDir = path.resolve(process.cwd(), projectName);\n\n console.log();\n console.log(chalk.white(` 📦 项目名称: ${chalk.bold(projectName)}`));\n console.log(\n chalk.white(` 📂 目标路径: ${chalk.gray(targetDir)}`)\n );\n console.log(\n chalk.white(\n ` 🧩 选择模块: ${chalk.green(selectedModuleDefs.map((m) => m.name).join(\", \") || \"无\")}`\n )\n );\n console.log();\n\n // ─── Step 2: Create project directory and write base files ─────────\n const spinner = ora(\"正在创建项目...\").start();\n\n try {\n // Generate base template files\n const baseTemplates = generateBaseTemplates(projectName);\n\n // Write all base files\n for (const file of baseTemplates) {\n const filePath = path.join(targetDir, file.path);\n const content = replaceTemplateVars(file.content, {\n projectName,\n });\n await writeFile(filePath, content);\n }\n\n spinner.text = \"正在写入模块文件...\";\n\n // ─── Step 3: 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, {\n projectName,\n });\n await writeFile(filePath, content);\n }\n }\n\n // ─── Step 4: Generate and merge package.json ──────────────────────\n spinner.text = \"正在生成 package.json...\";\n const pkgJson = generateBasePackageJson(projectName);\n\n // Collect all dependencies from selected modules\n const allDeps: Record<string, string> = {};\n const allDevDeps: Record<string, string> = {};\n for (const mod of selectedModuleDefs) {\n Object.assign(allDeps, mod.dependencies);\n Object.assign(allDevDeps, mod.devDependencies);\n }\n\n // Merge into base package.json\n Object.assign(pkgJson.dependencies as Record<string, string>, allDeps);\n Object.assign(\n pkgJson.devDependencies as Record<string, string>,\n allDevDeps\n );\n\n const pkgPath = path.join(targetDir, \"package.json\");\n await writeJson(pkgPath, pkgJson);\n\n // ─── Step 5: Update app.json with plugin configurations ──────────\n spinner.text = \"正在配置 app.json...\";\n await updateAppJson(targetDir, selectedModuleDefs, projectName);\n\n // ─── Step 6: Update babel.config.js with additional plugins ───────\n spinner.text = \"正在配置 Babel...\";\n await updateBabelConfig(targetDir, selectedModuleDefs);\n\n // ─── Step 7: Update app/_layout.tsx with module providers/imports ─\n spinner.text = \"正在配置入口文件...\";\n await updateLayoutFile(targetDir, selectedModuleDefs);\n\n // ─── Step 8: Run npm install ──────────────────────────────────────\n spinner.text = \"正在安装依赖 (npm install)...\";\n try {\n await execa(\"npm\", [\"install\"], {\n cwd: targetDir,\n timeout: 300_000, // 5 minute timeout\n });\n } catch (installError) {\n spinner.warn(\"npm install 失败,请手动运行 npm install\");\n console.log(\n chalk.gray(` cd ${projectName} && npm install`)\n );\n }\n\n // ─── Done! ────────────────────────────────────────────────────────\n spinner.succeed(chalk.green(\"项目创建成功!\"));\n\n console.log();\n console.log(chalk.bold(\" 🎉 下一步:\"));\n console.log(chalk.white(` cd ${projectName}`));\n console.log(chalk.white(\" npx expo start\"));\n console.log();\n\n if (selectedModuleDefs.length > 0) {\n console.log(chalk.bold(\" 📋 已选模块:\"));\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(\"项目创建失败\"));\n console.error(error);\n process.exit(1);\n }\n}\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 const appJson = await import(\"fs-extra\").then((fs) =>\n fs.readJson(appJsonPath)\n );\n\n // Collect all plugin configs\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 // Avoid duplicate plugin entries\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 const fs = await import(\"fs-extra\");\n\n let content = await fs.readFile(babelPath, \"utf-8\");\n\n const extraPlugins: string[] = [];\n for (const mod of selectedModules) {\n if (mod.babelPlugins) {\n extraPlugins.push(...mod.babelPlugins);\n }\n }\n\n if (extraPlugins.length > 0) {\n // Insert additional plugins before the closing bracket of the plugins array\n const pluginStrings = extraPlugins\n .map((p) => ` \"${p}\"`)\n .join(\",\\n\");\n content = content.replace(\n /plugins:\\s*\\[([^\\]]*)\\]/,\n `plugins: [$1${pluginStrings ? \",\\n\" + pluginStrings : \"\"}]`\n );\n await fs.writeFile(babelPath, content, \"utf-8\");\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 const fs = await import(\"fs-extra\");\n\n let content = await fs.readFile(layoutPath, \"utf-8\");\n\n // Collect imports and providers\n const extraImports: string[] = [];\n const extraProviderPairs: { open: string; close: string }[] = [];\n\n for (const mod of selectedModules) {\n if (mod.layoutImports) {\n extraImports.push(...mod.layoutImports);\n }\n if (mod.layoutProviders) {\n for (const provider of mod.layoutProviders) {\n // Parse opening and closing tags\n const match = provider.match(/^<(\\w+)/);\n if (match) {\n const tagName = match[1];\n extraProviderPairs.push({\n open: ` ${provider}`,\n close: ` </${tagName}>`,\n });\n }\n }\n }\n }\n\n if (extraImports.length === 0 && extraProviderPairs.length === 0) {\n return;\n }\n\n // Add imports after the existing import block\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 // Wrap the return statement content with providers\n if (extraProviderPairs.length > 0) {\n // Find the innermost returned JSX (the <Stack> component)\n const returnMatch = content.indexOf(\"return (\");\n if (returnMatch !== -1) {\n // Build provider wrappers\n const openingProviders = extraProviderPairs.map((p) => p.open).join(\"\\n\");\n const closingProviders = extraProviderPairs\n .reverse()\n .map((p) => p.close)\n .join(\"\\n\");\n\n // Find <ThemeProvider> opening and closing\n const themeProviderOpen = content.indexOf(\"<ThemeProvider\");\n const themeProviderClose = content.lastIndexOf(\"</ThemeProvider>\");\n\n if (themeProviderOpen !== -1 && themeProviderClose !== -1) {\n // Insert opening providers before <ThemeProvider>\n content =\n content.slice(0, themeProviderOpen) +\n openingProviders +\n \"\\n\" +\n content.slice(themeProviderOpen);\n\n // Recalculate position after insertion\n const newThemeProviderClose =\n content.lastIndexOf(\"</ThemeProvider>\");\n const afterClose = newThemeProviderClose + \"</ThemeProvider>\".length;\n content =\n content.slice(0, afterClose) +\n \"\\n\" +\n closingProviders +\n content.slice(afterClose);\n }\n }\n }\n\n await fs.writeFile(layoutPath, content, \"utf-8\");\n}\n\n// Run the CLI when executed directly\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\";\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 * 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: \"src/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: \"src/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: \"src/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: \"src/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: \"src/stores/index.ts\",\n content: lines('export { useUserStore } from \"./slices/userSlice\";'),\n },\n {\n path: \"src/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: \"src/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 appConfig: {\n plugins: [\n [\n \"react-native-mmkv\",\n {\n MMKV_APP_ID: \"app-storage\",\n },\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: \"src/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: \"src/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: \"src/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: \"src/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: \"src/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\": \"~2.0.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"src/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\": \"~15.0.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"src/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: \"src/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-facebook\",\n defaultChecked: false,\n dependencies: {\n \"expo-facebook\": \"~13.0.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"src/modules/auth/facebook.ts\",\n content: lines(\n 'import * as Facebook from \"expo-facebook\";',\n \"\",\n \"export interface FacebookAuthConfig {\",\n \" appId: string;\",\n \" appName: string;\",\n \"}\",\n \"\",\n \"export async function setupFacebookAuth(config: FacebookAuthConfig): Promise<void> {\",\n \" await Facebook.initializeAsync({\",\n \" appId: config.appId,\",\n \" appName: config.appName,\",\n \" });\",\n \"}\",\n \"\",\n \"export async function signInWithFacebook(): Promise<{\",\n \" token: string | null;\",\n \" userId: string | null;\",\n \" userName: string | null;\",\n \" error: string | null;\",\n \"}> {\",\n \" try {\",\n \" const result = await Facebook.logInWithReadPermissionsAsync({\",\n ' permissions: [\"public_profile\", \"email\"],',\n \" });\",\n \"\",\n ' if (result.type === \"success\") {',\n \" const response = await fetch(\",\n ' `https://graph.facebook.com/me?access_token=${result.token}&fields=id,name,email`',\n \" );\",\n \" const userData = await response.json();\",\n \"\",\n \" return {\",\n \" token: result.token,\",\n \" userId: userData.id,\",\n \" userName: userData.name,\",\n \" error: null,\",\n \" };\",\n \" } else {\",\n \" return {\",\n \" token: null,\",\n \" userId: null,\",\n \" userName: null,\",\n ' error: \"用户取消了 Facebook 登录\",',\n \" };\",\n \" }\",\n \" } catch (error) {\",\n ' console.error(\"[FacebookAuth] Error:\", error);',\n \" return {\",\n \" token: null,\",\n \" userId: null,\",\n \" userName: null,\",\n ' error: \"Facebook 登录失败\",',\n \" };\",\n \" }\",\n \"}\",\n \"\",\n \"export async function signOutFacebook(): Promise<void> {\",\n \" try {\",\n \" await Facebook.logOutAsync();\",\n \" } catch (error) {\",\n ' console.error(\"[FacebookAuth] Sign out error:\", error);',\n \" }\",\n \"}\"\n ),\n },\n ],\n appConfig: {\n plugins: [\n [\n \"expo-facebook\",\n {\n appId: \"YOUR_FACEBOOK_APP_ID\",\n },\n ],\n ],\n },\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.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"src/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.12.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"src/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: \"src/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\": \"~16.0.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"src/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: \"src/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: \"src/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 3 + Gesture Handler\",\n defaultChecked: false,\n dependencies: {\n \"react-native-reanimated\": \"~3.17.0\",\n \"react-native-gesture-handler\": \"~2.22.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"src/modules/animation/index.ts\",\n content: lines(\n 'export { FadeIn, FadeOut, SlideIn, SlideOut, ScaleIn, ScaleOut } from \"./transitions\";'\n ),\n },\n {\n path: \"src/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: [\"react-native-reanimated/plugin\"],\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\": \"~7.0.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"src/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.30.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"src/modules/notification/index.ts\",\n content: lines(\n 'export { InAppNotification, useInAppNotification } from \"./InAppNotification\";',\n 'export { setupNotificationHandlers } from \"./InAppNotification\";'\n ),\n },\n {\n path: \"src/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\": \"~16.0.0\",\n \"expo-camera\": \"~16.0.0\",\n \"expo-location\": \"~18.0.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"src/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: \"src/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\": \"^1.7.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"src/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 组件\",\n defaultChecked: false,\n dependencies: {\n \"reactnative.reusables\": \"^0.1.0\",\n \"react-native-svg\": \"^15.8.0\",\n \"@rn-primitives/slot\": \"^1.1.0\",\n \"@rn-primitives/types\": \"^1.1.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"src/components/ui/button.tsx\",\n content: lines(\n 'import React from \"react\";',\n 'import { Text, Pressable, type PressableProps, type StyleProp, type ViewStyle, type TextStyle } from \"react-native\";',\n 'import { Slot } from \"@rn-primitives/slot\";',\n \"\",\n 'type ButtonVariant = \"default\" | \"destructive\" | \"outline\" | \"secondary\" | \"ghost\" | \"link\";',\n 'type ButtonSize = \"default\" | \"sm\" | \"lg\" | \"icon\";',\n \"\",\n \"interface ButtonProps extends PressableProps {\",\n \" variant?: ButtonVariant;\",\n \" size?: ButtonSize;\",\n \" asChild?: boolean;\",\n \" style?: StyleProp<ViewStyle>;\",\n \" textStyle?: StyleProp<TextStyle>;\",\n \" children: React.ReactNode;\",\n \"}\",\n \"\",\n \"const variantStyles: Record<ButtonVariant, ViewStyle> = {\",\n ' default: { backgroundColor: \"#0f172a\" },',\n ' destructive: { backgroundColor: \"#ef4444\" },',\n ' outline: { backgroundColor: \"transparent\", borderWidth: 1, borderColor: \"#d4d4d8\" },',\n ' secondary: { backgroundColor: \"#f4f4f5\" },',\n ' ghost: { backgroundColor: \"transparent\" },',\n ' link: { backgroundColor: \"transparent\" },',\n \"};\",\n \"\",\n \"const variantTextStyles: Record<ButtonVariant, TextStyle> = {\",\n ' default: { color: \"#fafafa\" },',\n ' destructive: { color: \"#fafafa\" },',\n ' outline: { color: \"#18181b\" },',\n ' secondary: { color: \"#18181b\" },',\n ' ghost: { color: \"#18181b\" },',\n ' link: { color: \"#2563eb\", textDecorationLine: \"underline\" },',\n \"};\",\n \"\",\n \"const sizeStyles: Record<ButtonSize, ViewStyle> = {\",\n \" default: { height: 44, paddingHorizontal: 16 },\",\n \" sm: { height: 36, paddingHorizontal: 12 },\",\n \" lg: { height: 52, paddingHorizontal: 24 },\",\n \" icon: { height: 44, width: 44 },\",\n \"};\",\n \"\",\n \"const sizeTextStyles: Record<ButtonSize, TextStyle> = {\",\n \" default: { fontSize: 16 },\",\n \" sm: { fontSize: 14 },\",\n \" lg: { fontSize: 18 },\",\n \" icon: { fontSize: 16 },\",\n \"};\",\n \"\",\n \"export function Button({\",\n ' variant = \"default\",',\n ' size = \"default\",',\n \" asChild = false,\",\n \" style,\",\n \" textStyle,\",\n \" children,\",\n \" ...rest\",\n \"}: ButtonProps) {\",\n \" const containerStyle: StyleProp<ViewStyle> = [\",\n \" {\",\n \" borderRadius: 8,\",\n ' flexDirection: \"row\",',\n ' alignItems: \"center\",',\n ' justifyContent: \"center\",',\n \" },\",\n \" variantStyles[variant],\",\n \" sizeStyles[size],\",\n \" style,\",\n \" ];\",\n \"\",\n \" const textStyling: StyleProp<TextStyle> = [\",\n \" {\",\n ' fontWeight: \"600\",',\n ' textAlign: \"center\",',\n \" },\",\n \" variantTextStyles[variant],\",\n \" sizeTextStyles[size],\",\n \" textStyle,\",\n \" ];\",\n \"\",\n \" if (asChild && React.isValidElement(children)) {\",\n \" return (\",\n \" <Pressable style={containerStyle} {...rest}>\",\n \" <Slot>{children}</Slot>\",\n \" </Pressable>\",\n \" );\",\n \" }\",\n \"\",\n \" return (\",\n \" <Pressable style={containerStyle} {...rest}>\",\n \" {typeof children === \\\"string\\\" ? (\",\n \" <Text style={textStyling}>{children}</Text>\",\n \" ) : (\",\n \" children\",\n \" )}\",\n \" </Pressable>\",\n \" );\",\n \"}\",\n \"\",\n \"export default Button;\"\n ),\n },\n {\n path: \"src/components/ui/input.tsx\",\n content: lines(\n 'import React, { forwardRef } from \"react\";',\n 'import {',\n \" TextInput,\",\n \" type TextInputProps,\",\n \" type StyleProp,\",\n \" type ViewStyle,\",\n \" type TextStyle,\",\n \" View,\",\n \" Text,\",\n '} from \"react-native\";',\n \"\",\n \"interface InputProps extends TextInputProps {\",\n \" label?: string;\",\n \" error?: string;\",\n \" containerStyle?: StyleProp<ViewStyle>;\",\n \" inputStyle?: StyleProp<TextStyle>;\",\n \"}\",\n \"\",\n \"export const Input = forwardRef<TextInput, InputProps>(\",\n \" ({ label, error, containerStyle, inputStyle, ...rest }, ref) => {\",\n \" return (\",\n \" <View style={containerStyle}>\",\n \" {label && <Text style={styles.label}>{label}</Text>}\",\n \" <TextInput\",\n \" ref={ref}\",\n \" style={[\",\n \" styles.input,\",\n \" error && styles.inputError,\",\n \" inputStyle,\",\n \" ]}\",\n ' placeholderTextColor=\"#a1a1aa\"',\n \" {...rest}\",\n \" />\",\n \" {error && <Text style={styles.errorText}>{error}</Text>}\",\n \" </View>\",\n \" );\",\n \" }\",\n \");\",\n \"\",\n 'Input.displayName = \"Input\";',\n \"\",\n \"const styles = {\",\n \" label: {\",\n \" fontSize: 14,\",\n ' fontWeight: \"500\",',\n ' color: \"#18181b\",',\n \" marginBottom: 6,\",\n \" } as TextStyle,\",\n \" input: {\",\n \" height: 44,\",\n \" borderWidth: 1,\",\n ' borderColor: \"#d4d4d8\",',\n \" borderRadius: 8,\",\n \" paddingHorizontal: 12,\",\n \" fontSize: 16,\",\n ' color: \"#18181b\",',\n ' backgroundColor: \"#ffffff\",',\n \" } as TextStyle,\",\n \" inputError: {\",\n ' borderColor: \"#ef4444\",',\n \" } as TextStyle,\",\n \" errorText: {\",\n \" fontSize: 12,\",\n ' color: \"#ef4444\",',\n \" marginTop: 4,\",\n \" } as TextStyle,\",\n \"};\",\n \"\",\n \"export default Input;\"\n ),\n },\n {\n path: \"src/components/ui/card.tsx\",\n content: lines(\n 'import React from \"react\";',\n 'import { View, Text, type StyleProp, type ViewStyle, type TextStyle } from \"react-native\";',\n \"\",\n \"interface CardProps {\",\n \" title?: string;\",\n \" description?: string;\",\n \" children: React.ReactNode;\",\n \" style?: StyleProp<ViewStyle>;\",\n \" titleStyle?: StyleProp<TextStyle>;\",\n \" descriptionStyle?: StyleProp<TextStyle>;\",\n ' padding?: \"none\" | \"sm\" | \"md\" | \"lg\";',\n \"}\",\n \"\",\n \"const paddingMap: Record<NonNullable<CardProps[\\\"padding\\\"]>, number> = {\",\n \" none: 0,\",\n \" sm: 8,\",\n \" md: 16,\",\n \" lg: 24,\",\n \"};\",\n \"\",\n \"export function Card({\",\n \" title,\",\n \" description,\",\n \" children,\",\n \" style,\",\n \" titleStyle,\",\n \" descriptionStyle,\",\n ' padding = \"md\",',\n \"}: CardProps) {\",\n \" return (\",\n \" <View\",\n \" style={[\",\n \" {\",\n ' backgroundColor: \"#ffffff\",',\n \" borderRadius: 12,\",\n \" borderWidth: 1,\",\n ' borderColor: \"#e4e4e7\",',\n \" padding: paddingMap[padding],\",\n \" },\",\n \" style,\",\n \" ]}\",\n \" >\",\n \" {title && (\",\n \" <Text style={[styles.title, titleStyle]}>{title}</Text>\",\n \" )}\",\n \" {description && (\",\n \" <Text style={[styles.description, descriptionStyle]}>\",\n \" {description}\",\n \" </Text>\",\n \" )}\",\n \" {children}\",\n \" </View>\",\n \" );\",\n \"}\",\n \"\",\n \"const styles = {\",\n \" title: {\",\n \" fontSize: 18,\",\n ' fontWeight: \"600\",',\n ' color: \"#18181b\",',\n \" marginBottom: 4,\",\n \" } as TextStyle,\",\n \" description: {\",\n \" fontSize: 14,\",\n ' color: \"#71717a\",',\n \" marginBottom: 12,\",\n \" } as TextStyle,\",\n \"};\",\n \"\",\n \"export default Card;\"\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 * These form the core Expo Router + NativeWind project structure.\n */\nexport function generateBaseTemplates(projectName: string): TemplateFile[] {\n return [\n // ─── app/_layout.tsx ────────────────────────────────────────────────\n {\n path: \"app/_layout.tsx\",\n content: `import { 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\";\n\nimport { Colors } from \"@/src/constants/Colors\";\n\nSplashScreen.preventAutoHideAsync();\n\nexport default function RootLayout() {\n const colorScheme = useColorScheme();\n const [loaded] = useFonts({\n SpaceMono: require(\"../assets/fonts/SpaceMono-Regular.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\" ? DarkTheme : DefaultTheme}>\n <Stack>\n <Stack.Screen name=\"(tabs)\" options={{ headerShown: false }} />\n <Stack.Screen name=\"+not-found\" />\n </Stack>\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\nimport { Colors } from \"@/src/constants/Colors\";\nimport { useColorScheme } from \"@/src/hooks/useColorScheme\";\n\nexport default function TabLayout() {\n const colorScheme = useColorScheme();\n\n return (\n <Tabs\n screenOptions={{\n tabBarActiveTintColor: Colors[colorScheme ?? \"light\"].tint,\n headerStyle: {\n backgroundColor: Colors[colorScheme ?? \"light\"].background,\n },\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: () => null,\n }}\n />\n <Tabs.Screen\n name=\"explore\"\n options={{\n title: \"Explore\",\n tabBarIcon: () => null,\n }}\n />\n </Tabs>\n );\n}\n`,\n },\n\n // ─── app/(tabs)/index.tsx ───────────────────────────────────────────\n {\n path: \"app/(tabs)/index.tsx\",\n content: `import { Image, StyleSheet, Platform } from \"react-native\";\nimport { HelloWave } from \"@/src/components/HelloWave\";\nimport { ThemedText } from \"@/src/components/Themed\";\nimport { ThemedView } from \"@/src/components/Themed\";\nimport { Link } from \"expo-router\";\n\nexport default function HomeScreen() {\n return (\n <ThemedView style={styles.container}>\n <ThemedView style={styles.titleContainer}>\n <ThemedText type=\"title\">${projectName}</ThemedText>\n <HelloWave />\n </ThemedView>\n <ThemedView style={styles.stepContainer}>\n <ThemedText type=\"subtitle\">Step 1: Try it</ThemedText>\n <ThemedText>\n Edit <ThemedText type=\"defaultSemiBold\">app/(tabs)/index.tsx</ThemedText> to see changes.\n Press{\" \"}\n <ThemedText type=\"defaultSemiBold\">\n {Platform.select({ ios: \"cmd + d\", android: \"cmd + m\" })}\n </ThemedText>{\" \"}\n to open developer tools.\n </ThemedText>\n </ThemedView>\n <ThemedView style={styles.stepContainer}>\n <ThemedText type=\"subtitle\">Step 2: Explore</ThemedText>\n <ThemedText>\n Tap the Explore tab to learn more about what's included in this starter.\n </ThemedText>\n <Link href=\"/explore\">\n <ThemedText type=\"link\">Go to Explore →</ThemedText>\n </Link>\n </ThemedView>\n </ThemedView>\n );\n}\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n alignItems: \"center\",\n justifyContent: \"center\",\n },\n titleContainer: {\n flexDirection: \"row\",\n alignItems: \"center\",\n gap: 8,\n },\n stepContainer: {\n gap: 8,\n marginBottom: 8,\n },\n});\n`,\n },\n\n // ─── app/(tabs)/explore.tsx ─────────────────────────────────────────\n {\n path: \"app/(tabs)/explore.tsx\",\n content: `import { StyleSheet, Image, Platform } from \"react-native\";\nimport { ThemedText } from \"@/src/components/Themed\";\nimport { ThemedView } from \"@/src/components/Themed\";\n\nexport default function ExploreScreen() {\n return (\n <ThemedView style={styles.container}>\n <ThemedText type=\"title\">Explore</ThemedText>\n <ThemedText style={styles.subtitle}>\n This screen shows what you can do with this scaffolded project.\n </ThemedText>\n </ThemedView>\n );\n}\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n alignItems: \"center\",\n justifyContent: \"center\",\n },\n subtitle: {\n fontSize: 16,\n textAlign: \"center\",\n marginTop: 8,\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 { StyleSheet } from \"react-native\";\nimport { ThemedText } from \"@/src/components/Themed\";\nimport { ThemedView } from \"@/src/components/Themed\";\n\nexport default function NotFoundScreen() {\n return (\n <>\n <Stack.Screen options={{ title: \"Oops!\" }} />\n <ThemedView style={styles.container}>\n <ThemedText type=\"title\">This screen doesn't exist.</ThemedText>\n <Link href=\"/\" style={styles.link}>\n <ThemedText type=\"link\">Go to home screen!</ThemedText>\n </Link>\n </ThemedView>\n </>\n );\n}\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: 20,\n },\n link: {\n marginTop: 15,\n paddingVertical: 15,\n },\n});\n`,\n },\n\n // ─── src/components/Themed.tsx ───────────────────────────────────────\n {\n path: \"src/components/Themed.tsx\",\n content: `import { Text, type TextProps, View, type ViewProps } from \"react-native\";\nimport { useColorScheme } from \"@/src/hooks/useColorScheme\";\nimport { Colors } from \"@/src/constants/Colors\";\n\n/** Themed text component that adapts to light/dark mode */\nexport function ThemedText({\n style,\n type = \"default\",\n ...rest\n}: TextProps & { type?: \"default\" | \"title\" | \"defaultSemiBold\" | \"subtitle\" | \"link\" }) {\n const colorScheme = useColorScheme();\n const color = Colors[colorScheme ?? \"light\"].text;\n\n return (\n <Text\n style={[\n { color },\n type === \"default\" ? { fontSize: 16, lineHeight: 24 } : undefined,\n type === \"title\" ? { fontSize: 28, fontWeight: \"bold\", lineHeight: 32 } : undefined,\n type === \"defaultSemiBold\" ? { fontSize: 16, lineHeight: 24, fontWeight: \"600\" } : undefined,\n type === \"subtitle\" ? { fontSize: 20, fontWeight: \"bold\" } : undefined,\n type === \"link\" ? { fontSize: 16, lineHeight: 24, color: Colors[colorScheme ?? \"light\"].tint } : undefined,\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Themed view component that adapts to light/dark mode */\nexport function ThemedView({ style, ...rest }: ViewProps) {\n const colorScheme = useColorScheme();\n const backgroundColor = Colors[colorScheme ?? \"light\"].background;\n\n return <View style={[{ backgroundColor }, style]} {...rest} />;\n}\n`,\n },\n\n // ─── src/components/HelloWave.tsx ────────────────────────────────────\n {\n path: \"src/components/HelloWave.tsx\",\n content: `import { useEffect } from \"react\";\nimport { Animated, Easing } from \"react-native\";\nimport { ThemedText } from \"./Themed\";\n\nexport function HelloWave() {\n const rotationAnim = new Animated.Value(0);\n\n useEffect(() => {\n Animated.loop(\n Animated.sequence([\n Animated.timing(rotationAnim, {\n toValue: 1,\n duration: 150,\n easing: Easing.linear,\n useNativeDriver: true,\n }),\n Animated.timing(rotationAnim, {\n toValue: 0,\n duration: 150,\n easing: Easing.linear,\n useNativeDriver: true,\n }),\n ])\n ).start();\n }, [rotationAnim]);\n\n return (\n <Animated.View\n style={{\n transform: [\n {\n rotate: rotationAnim.interpolate({\n inputRange: [0, 1],\n outputRange: [\"0deg\", \"14deg\"],\n }),\n },\n ],\n }}\n >\n <ThemedText style={{ fontSize: 28 }}>👋</ThemedText>\n </Animated.View>\n );\n}\n`,\n },\n\n // ─── src/hooks/useColorScheme.ts ─────────────────────────────────────\n {\n path: \"src/hooks/useColorScheme.ts\",\n content: `import { useColorScheme as useRNColorScheme } from \"react-native\";\n\n/**\n * Returns the current color scheme (light or dark).\n * Defaults to \"light\" if the system preference is not available.\n */\nexport function useColorScheme(): \"light\" | \"dark\" {\n return useRNColorScheme() ?? \"light\";\n}\n`,\n },\n\n // ─── src/constants/Colors.ts ─────────────────────────────────────────\n {\n path: \"src/constants/Colors.ts\",\n content: `/**\n * Color tokens for light and dark themes.\n * Used by Themed components and navigation theming.\n */\nexport const Colors = {\n light: {\n text: \"#11181C\",\n background: \"#fff\",\n tint: \"#0a7ea4\",\n tabIconDefault: \"#687076\",\n tabIconSelected: \"#0a7ea4\",\n },\n dark: {\n text: \"#ECEDEE\",\n background: \"#151718\",\n tint: \"#fff\",\n tabIconDefault: \"#9BA1A6\",\n tabIconSelected: \"#fff\",\n },\n};\n`,\n },\n\n // ─── src/types/index.ts ─────────────────────────────────────────────\n {\n path: \"src/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 \"icon\": \"./assets/icon.png\",\n \"userInterfaceStyle\": \"light\",\n \"splash\": {\n \"image\": \"./assets/splash.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 \"compilerOptions\": {\n \"target\": \"ESNext\",\n \"module\": \"ESNext\",\n \"moduleResolution\": \"bundler\",\n \"lib\": [\"ESNext\"],\n \"strict\": true,\n \"jsx\": \"react-jsx\",\n \"esModuleInterop\": true,\n \"skipLibCheck\": true,\n \"forceConsistentCasingInFileNames\": true,\n \"resolveJsonModule\": true,\n \"isolatedModules\": true,\n \"noEmit\": true,\n \"paths\": {\n \"@/*\": [\"./*\"]\n }\n },\n \"include\": [\"**/*.ts\", \"**/*.tsx\", \".expo/types/**/*.ts\", \"expo-env.d.ts\"],\n \"exclude\": [\"node_modules\"]\n}\n`,\n },\n\n // ─── tailwind.config.js ──────────────────────────────────────────────\n {\n path: \"tailwind.config.js\",\n content: `/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n content: [\n \"./app/**/*.{js,jsx,ts,tsx}\",\n \"./src/**/*.{js,jsx,ts,tsx}\",\n ],\n theme: {\n extend: {},\n },\n plugins: [],\n};\n`,\n },\n\n // ─── metro.config.js ─────────────────────────────────────────────────\n {\n path: \"metro.config.js\",\n content: `const { getDefaultConfig } = require(\"expo/metro-config\");\nconst { withTailwind } = require(\"@expo/metro-config/tailwind\");\n\nconst config = getDefaultConfig(__dirname);\n\nmodule.exports = withTailwind(config);\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: [\"babel-preset-expo\"],\n plugins: [\"nativewind/babel\"],\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 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 */\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 android: \"expo start --android\",\n ios: \"expo start --ios\",\n web: \"expo start --web\",\n lint: \"eslint .\",\n },\n dependencies: {\n expo: \"~54.0.0\",\n \"expo-router\": \"~5.0.0\",\n \"expo-linking\": \"~7.0.0\",\n \"expo-constants\": \"~18.0.0\",\n \"expo-status-bar\": \"~2.0.0\",\n \"expo-splash-screen\": \"~1.0.0\",\n react: \"19.0.0\",\n \"react-native\": \"0.79.0\",\n \"react-native-safe-area-context\": \"5.4.0\",\n \"react-native-screens\": \"~4.6.0\",\n nativewind: \"^4.1.0\",\n tailwindcss: \"^3.4.0\",\n },\n devDependencies: {\n \"@types/react\": \"~19.0.0\",\n typescript: \"^5.5.0\",\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAkB;AAClB,qBAAoB;AACpB,iBAAgB;AAChB,IAAAA,eAAiB;AACjB,uBAAwB;;;ACEjB,SAAS,sBAAsB,SAAwB;AAC5D,UACG,QAAQ,uBAAuB,EAC/B,YAAY,iDAAiD,EAC7D,OAAO,OAAO,gBAAwB;AACrC,UAAM,cAAc,WAAW;AAAA,EACjC,CAAC;AACL;;;ACTO,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;AAAA,EACA,WAAW;AAAA,IACT,SAAS;AAAA,MACP;AAAA,QACE;AAAA,QACA;AAAA,UACE,aAAa;AAAA,QACf;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAO,kBAAQ;;;AC7Gf,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;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,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;;;ACxMf,IAAM,aAAwB;AAAA,EAC5B,IA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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,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,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;AAAA,MACA;AAAA,QACE;AAAA,QACA;AAAA,UACE,kBAAkB;AAAA,QACpB;AAAA,MACF;AAAA,MACA;AAAA,QACE;AAAA,QACA;AAAA,UACE,sCACE;AAAA,QACJ;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAO,qBAAQ;;;AC9Jf,IAAM,oBAA+B;AAAA,EACnC,IA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,oBAAoB;AAAA,IACpB,uBAAuB;AAAA,IACvB,wBAAwB;AAAA,EAC1B;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,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,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,QA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","names":["import_path","path","fs","path","fs","chalk","prompts","path","ora","fs"]}
|
|
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/utils/file.ts","../src/utils/package.ts"],"sourcesContent":["import chalk from \"chalk\";\nimport prompts from \"prompts\";\nimport ora from \"ora\";\nimport path from \"path\";\nimport fse from \"fs-extra\";\nimport { Command } from \"commander\";\nimport { registerCreateCommand } from \"./commands/create\";\nimport { modules, getModulesByIds } from \"./modules\";\nimport { generateBaseTemplates } from \"./templates/base\";\nimport { writeFile, writeJson, replaceTemplateVars } from \"./utils/file\";\nimport { generateBasePackageJson } from \"./utils/package\";\nimport type { ModuleDef } from \"./types\";\nimport { execa } from \"execa\";\n\n/**\n * Main entry point for the CLI.\n */\nexport async function run(): Promise<void> {\n const program = new Command();\n\n program\n .name(\"expo-bbase\")\n .description(\"Expo SDK 54+ 脚手架 CLI 工具\")\n .version(\"1.0.0\");\n\n // Register the 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\n await program.parseAsync(process.argv);\n}\n\n/**\n * Create a new Expo project with interactive module selection.\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 脚手架工具 ║\")\n );\n console.log(\n chalk.bold.cyan(\" ╚══════════════════════════════════════╝\")\n );\n console.log();\n\n // ─── Step 1: Interactive module selection ──────────────────────────\n const choices = modules.map((m) => ({\n title: `${chalk.bold(m.name)} — ${chalk.gray(m.description)}`,\n value: m.id,\n selected: m.defaultChecked,\n }));\n\n const { selectedModules } = await prompts({\n type: \"multiselect\",\n name: \"selectedModules\",\n message: \"选择需要的功能模块(空格切换,回车确认)\",\n choices,\n hint: \"- 空格切换选择 · a 全选/取消 · 回车确认\",\n instructions: false,\n });\n\n // User cancelled the prompt\n if (selectedModules === undefined) {\n console.log(chalk.yellow(\"\\n已取消创建项目。\"));\n process.exit(0);\n }\n\n const selectedModuleDefs = getModulesByIds(selectedModules as string[]);\n const targetDir = path.resolve(process.cwd(), projectName);\n\n console.log();\n console.log(chalk.white(` 📦 项目名称: ${chalk.bold(projectName)}`));\n console.log(\n chalk.white(` 📂 目标路径: ${chalk.gray(targetDir)}`)\n );\n console.log(\n chalk.white(\n ` 🧩 选择模块: ${chalk.green(selectedModuleDefs.map((m) => m.name).join(\", \") || \"无\")}`\n )\n );\n console.log();\n\n // ─── Step 2: Create project directory and write base files ─────────\n const spinner = ora(\"正在创建项目...\").start();\n\n try {\n // Generate base template files\n const baseTemplates = generateBaseTemplates(projectName);\n\n // Write all base files\n for (const file of baseTemplates) {\n const filePath = path.join(targetDir, file.path);\n const content = replaceTemplateVars(file.content, {\n projectName,\n });\n await writeFile(filePath, content);\n }\n\n spinner.text = \"正在写入模块文件...\";\n\n // ─── Step 3: 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, {\n projectName,\n });\n await writeFile(filePath, content);\n }\n }\n\n // ─── Step 4: Generate and merge package.json ──────────────────────\n spinner.text = \"正在生成 package.json...\";\n const pkgJson = generateBasePackageJson(projectName);\n\n // Collect all dependencies from selected modules\n const allDeps: Record<string, string> = {};\n const allDevDeps: Record<string, string> = {};\n for (const mod of selectedModuleDefs) {\n Object.assign(allDeps, mod.dependencies);\n Object.assign(allDevDeps, mod.devDependencies);\n }\n\n // Merge into base package.json\n Object.assign(pkgJson.dependencies as Record<string, string>, allDeps);\n Object.assign(\n pkgJson.devDependencies as Record<string, string>,\n allDevDeps\n );\n\n const pkgPath = path.join(targetDir, \"package.json\");\n await writeJson(pkgPath, pkgJson);\n\n // ─── Step 5: Update app.json with plugin configurations ──────────\n spinner.text = \"正在配置 app.json...\";\n await updateAppJson(targetDir, selectedModuleDefs, projectName);\n\n // ─── Step 6: Update babel.config.js with additional plugins ───────\n spinner.text = \"正在配置 Babel...\";\n await updateBabelConfig(targetDir, selectedModuleDefs);\n\n // ─── Step 7: Update app/_layout.tsx with module providers/imports ─\n spinner.text = \"正在配置入口文件...\";\n await updateLayoutFile(targetDir, selectedModuleDefs);\n\n // ─── Step 8: Run npm install ──────────────────────────────────────\n spinner.text = \"正在安装依赖 (npm install)...\";\n try {\n await execa(\"npm\", [\"install\"], {\n cwd: targetDir,\n timeout: 300_000, // 5 minute timeout\n });\n } catch (installError) {\n spinner.warn(\"npm install 失败,请手动运行 npm install\");\n console.log(\n chalk.gray(` cd ${projectName} && npm install`)\n );\n }\n\n // ─── Done! ────────────────────────────────────────────────────────\n spinner.succeed(chalk.green(\"项目创建成功!\"));\n\n console.log();\n console.log(chalk.bold(\" 🎉 下一步:\"));\n console.log(chalk.white(` cd ${projectName}`));\n console.log(chalk.white(\" npx expo start\"));\n console.log();\n\n if (selectedModuleDefs.length > 0) {\n console.log(chalk.bold(\" 📋 已选模块:\"));\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(\"项目创建失败\"));\n console.error(error);\n process.exit(1);\n }\n}\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 const appJson = await fse.readJson(appJsonPath);\n\n // Collect all plugin configs\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 // Avoid duplicate plugin entries\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\n let content = await fse.readFile(babelPath, \"utf-8\");\n\n const extraPlugins: string[] = [];\n for (const mod of selectedModules) {\n if (mod.babelPlugins) {\n extraPlugins.push(...mod.babelPlugins);\n }\n }\n\n if (extraPlugins.length > 0) {\n // Insert additional plugins before the closing bracket of the plugins array\n const pluginStrings = extraPlugins\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 * 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 const fs = fse;\n\n let content = await fs.readFile(layoutPath, \"utf-8\");\n\n // Collect imports and providers\n const extraImports: string[] = [];\n const extraProviderPairs: { open: string; close: string }[] = [];\n\n for (const mod of selectedModules) {\n if (mod.layoutImports) {\n extraImports.push(...mod.layoutImports);\n }\n if (mod.layoutProviders) {\n for (const provider of mod.layoutProviders) {\n // Parse opening and closing tags\n const match = provider.match(/^<(\\w+)/);\n if (match) {\n const tagName = match[1];\n extraProviderPairs.push({\n open: ` ${provider}`,\n close: ` </${tagName}>`,\n });\n }\n }\n }\n }\n\n if (extraImports.length === 0 && extraProviderPairs.length === 0) {\n return;\n }\n\n // Add imports after the existing import block\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 // Wrap the return statement content with providers\n if (extraProviderPairs.length > 0) {\n // Find the innermost returned JSX (the <Stack> component)\n const returnMatch = content.indexOf(\"return (\");\n if (returnMatch !== -1) {\n // Build provider wrappers\n const openingProviders = extraProviderPairs.map((p) => p.open).join(\"\\n\");\n const closingProviders = extraProviderPairs\n .reverse()\n .map((p) => p.close)\n .join(\"\\n\");\n\n // Find <ThemeProvider> opening and closing\n const themeProviderOpen = content.indexOf(\"<ThemeProvider\");\n const themeProviderClose = content.lastIndexOf(\"</ThemeProvider>\");\n\n if (themeProviderOpen !== -1 && themeProviderClose !== -1) {\n // Insert opening providers before <ThemeProvider>\n content =\n content.slice(0, themeProviderOpen) +\n openingProviders +\n \"\\n\" +\n content.slice(themeProviderOpen);\n\n // Recalculate position after insertion\n const newThemeProviderClose =\n content.lastIndexOf(\"</ThemeProvider>\");\n const afterClose = newThemeProviderClose + \"</ThemeProvider>\".length;\n content =\n content.slice(0, afterClose) +\n \"\\n\" +\n closingProviders +\n content.slice(afterClose);\n }\n }\n }\n\n await fs.writeFile(layoutPath, content, \"utf-8\");\n}\n\n// Run the CLI when executed directly\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\";\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 * 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: \"src/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: \"src/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: \"src/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: \"src/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: \"src/stores/index.ts\",\n content: lines('export { useUserStore } from \"./slices/userSlice\";'),\n },\n {\n path: \"src/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: \"src/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: \"src/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: \"src/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: \"src/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: \"src/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: \"src/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\": \"~2.0.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"src/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\": \"~15.0.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"src/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: \"src/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-facebook\",\n defaultChecked: false,\n dependencies: {\n \"expo-facebook\": \"~13.0.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"src/modules/auth/facebook.ts\",\n content: lines(\n 'import * as Facebook from \"expo-facebook\";',\n \"\",\n \"export interface FacebookAuthConfig {\",\n \" appId: string;\",\n \" appName: string;\",\n \"}\",\n \"\",\n \"export async function setupFacebookAuth(config: FacebookAuthConfig): Promise<void> {\",\n \" await Facebook.initializeAsync({\",\n \" appId: config.appId,\",\n \" appName: config.appName,\",\n \" });\",\n \"}\",\n \"\",\n \"export async function signInWithFacebook(): Promise<{\",\n \" token: string | null;\",\n \" userId: string | null;\",\n \" userName: string | null;\",\n \" error: string | null;\",\n \"}> {\",\n \" try {\",\n \" const result = await Facebook.logInWithReadPermissionsAsync({\",\n ' permissions: [\"public_profile\", \"email\"],',\n \" });\",\n \"\",\n ' if (result.type === \"success\") {',\n \" const response = await fetch(\",\n ' `https://graph.facebook.com/me?access_token=${result.token}&fields=id,name,email`',\n \" );\",\n \" const userData = await response.json();\",\n \"\",\n \" return {\",\n \" token: result.token,\",\n \" userId: userData.id,\",\n \" userName: userData.name,\",\n \" error: null,\",\n \" };\",\n \" } else {\",\n \" return {\",\n \" token: null,\",\n \" userId: null,\",\n \" userName: null,\",\n ' error: \"用户取消了 Facebook 登录\",',\n \" };\",\n \" }\",\n \" } catch (error) {\",\n ' console.error(\"[FacebookAuth] Error:\", error);',\n \" return {\",\n \" token: null,\",\n \" userId: null,\",\n \" userName: null,\",\n ' error: \"Facebook 登录失败\",',\n \" };\",\n \" }\",\n \"}\",\n \"\",\n \"export async function signOutFacebook(): Promise<void> {\",\n \" try {\",\n \" await Facebook.logOutAsync();\",\n \" } catch (error) {\",\n ' console.error(\"[FacebookAuth] Sign out error:\", error);',\n \" }\",\n \"}\"\n ),\n },\n ],\n appConfig: {\n plugins: [\n [\n \"expo-facebook\",\n {\n appId: \"YOUR_FACEBOOK_APP_ID\",\n },\n ],\n ],\n },\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.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"src/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.12.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"src/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: \"src/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\": \"~16.0.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"src/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: \"src/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: \"src/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 3 + Gesture Handler\",\n defaultChecked: false,\n dependencies: {\n \"react-native-reanimated\": \"~3.17.0\",\n \"react-native-gesture-handler\": \"~2.22.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"src/modules/animation/index.ts\",\n content: lines(\n 'export { FadeIn, FadeOut, SlideIn, SlideOut, ScaleIn, ScaleOut } from \"./transitions\";'\n ),\n },\n {\n path: \"src/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: [\"react-native-reanimated/plugin\"],\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\": \"~7.0.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"src/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.30.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"src/modules/notification/index.ts\",\n content: lines(\n 'export { InAppNotification, useInAppNotification } from \"./InAppNotification\";',\n 'export { setupNotificationHandlers } from \"./InAppNotification\";'\n ),\n },\n {\n path: \"src/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\": \"~16.0.0\",\n \"expo-camera\": \"~16.0.0\",\n \"expo-location\": \"~18.0.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"src/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: \"src/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\": \"^1.7.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"src/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 组件\",\n defaultChecked: false,\n dependencies: {\n \"reactnative.reusables\": \"^0.1.0\",\n \"react-native-svg\": \"^15.8.0\",\n \"@rn-primitives/slot\": \"^1.1.0\",\n \"@rn-primitives/types\": \"^1.1.0\",\n },\n devDependencies: {},\n files: [\n {\n path: \"src/components/ui/button.tsx\",\n content: lines(\n 'import React from \"react\";',\n 'import { Text, Pressable, type PressableProps, type StyleProp, type ViewStyle, type TextStyle } from \"react-native\";',\n 'import { Slot } from \"@rn-primitives/slot\";',\n \"\",\n 'type ButtonVariant = \"default\" | \"destructive\" | \"outline\" | \"secondary\" | \"ghost\" | \"link\";',\n 'type ButtonSize = \"default\" | \"sm\" | \"lg\" | \"icon\";',\n \"\",\n \"interface ButtonProps extends PressableProps {\",\n \" variant?: ButtonVariant;\",\n \" size?: ButtonSize;\",\n \" asChild?: boolean;\",\n \" style?: StyleProp<ViewStyle>;\",\n \" textStyle?: StyleProp<TextStyle>;\",\n \" children: React.ReactNode;\",\n \"}\",\n \"\",\n \"const variantStyles: Record<ButtonVariant, ViewStyle> = {\",\n ' default: { backgroundColor: \"#0f172a\" },',\n ' destructive: { backgroundColor: \"#ef4444\" },',\n ' outline: { backgroundColor: \"transparent\", borderWidth: 1, borderColor: \"#d4d4d8\" },',\n ' secondary: { backgroundColor: \"#f4f4f5\" },',\n ' ghost: { backgroundColor: \"transparent\" },',\n ' link: { backgroundColor: \"transparent\" },',\n \"};\",\n \"\",\n \"const variantTextStyles: Record<ButtonVariant, TextStyle> = {\",\n ' default: { color: \"#fafafa\" },',\n ' destructive: { color: \"#fafafa\" },',\n ' outline: { color: \"#18181b\" },',\n ' secondary: { color: \"#18181b\" },',\n ' ghost: { color: \"#18181b\" },',\n ' link: { color: \"#2563eb\", textDecorationLine: \"underline\" },',\n \"};\",\n \"\",\n \"const sizeStyles: Record<ButtonSize, ViewStyle> = {\",\n \" default: { height: 44, paddingHorizontal: 16 },\",\n \" sm: { height: 36, paddingHorizontal: 12 },\",\n \" lg: { height: 52, paddingHorizontal: 24 },\",\n \" icon: { height: 44, width: 44 },\",\n \"};\",\n \"\",\n \"const sizeTextStyles: Record<ButtonSize, TextStyle> = {\",\n \" default: { fontSize: 16 },\",\n \" sm: { fontSize: 14 },\",\n \" lg: { fontSize: 18 },\",\n \" icon: { fontSize: 16 },\",\n \"};\",\n \"\",\n \"export function Button({\",\n ' variant = \"default\",',\n ' size = \"default\",',\n \" asChild = false,\",\n \" style,\",\n \" textStyle,\",\n \" children,\",\n \" ...rest\",\n \"}: ButtonProps) {\",\n \" const containerStyle: StyleProp<ViewStyle> = [\",\n \" {\",\n \" borderRadius: 8,\",\n ' flexDirection: \"row\",',\n ' alignItems: \"center\",',\n ' justifyContent: \"center\",',\n \" },\",\n \" variantStyles[variant],\",\n \" sizeStyles[size],\",\n \" style,\",\n \" ];\",\n \"\",\n \" const textStyling: StyleProp<TextStyle> = [\",\n \" {\",\n ' fontWeight: \"600\",',\n ' textAlign: \"center\",',\n \" },\",\n \" variantTextStyles[variant],\",\n \" sizeTextStyles[size],\",\n \" textStyle,\",\n \" ];\",\n \"\",\n \" if (asChild && React.isValidElement(children)) {\",\n \" return (\",\n \" <Pressable style={containerStyle} {...rest}>\",\n \" <Slot>{children}</Slot>\",\n \" </Pressable>\",\n \" );\",\n \" }\",\n \"\",\n \" return (\",\n \" <Pressable style={containerStyle} {...rest}>\",\n \" {typeof children === \\\"string\\\" ? (\",\n \" <Text style={textStyling}>{children}</Text>\",\n \" ) : (\",\n \" children\",\n \" )}\",\n \" </Pressable>\",\n \" );\",\n \"}\",\n \"\",\n \"export default Button;\"\n ),\n },\n {\n path: \"src/components/ui/input.tsx\",\n content: lines(\n 'import React, { forwardRef } from \"react\";',\n 'import {',\n \" TextInput,\",\n \" type TextInputProps,\",\n \" type StyleProp,\",\n \" type ViewStyle,\",\n \" type TextStyle,\",\n \" View,\",\n \" Text,\",\n '} from \"react-native\";',\n \"\",\n \"interface InputProps extends TextInputProps {\",\n \" label?: string;\",\n \" error?: string;\",\n \" containerStyle?: StyleProp<ViewStyle>;\",\n \" inputStyle?: StyleProp<TextStyle>;\",\n \"}\",\n \"\",\n \"export const Input = forwardRef<TextInput, InputProps>(\",\n \" ({ label, error, containerStyle, inputStyle, ...rest }, ref) => {\",\n \" return (\",\n \" <View style={containerStyle}>\",\n \" {label && <Text style={styles.label}>{label}</Text>}\",\n \" <TextInput\",\n \" ref={ref}\",\n \" style={[\",\n \" styles.input,\",\n \" error && styles.inputError,\",\n \" inputStyle,\",\n \" ]}\",\n ' placeholderTextColor=\"#a1a1aa\"',\n \" {...rest}\",\n \" />\",\n \" {error && <Text style={styles.errorText}>{error}</Text>}\",\n \" </View>\",\n \" );\",\n \" }\",\n \");\",\n \"\",\n 'Input.displayName = \"Input\";',\n \"\",\n \"const styles = {\",\n \" label: {\",\n \" fontSize: 14,\",\n ' fontWeight: \"500\",',\n ' color: \"#18181b\",',\n \" marginBottom: 6,\",\n \" } as TextStyle,\",\n \" input: {\",\n \" height: 44,\",\n \" borderWidth: 1,\",\n ' borderColor: \"#d4d4d8\",',\n \" borderRadius: 8,\",\n \" paddingHorizontal: 12,\",\n \" fontSize: 16,\",\n ' color: \"#18181b\",',\n ' backgroundColor: \"#ffffff\",',\n \" } as TextStyle,\",\n \" inputError: {\",\n ' borderColor: \"#ef4444\",',\n \" } as TextStyle,\",\n \" errorText: {\",\n \" fontSize: 12,\",\n ' color: \"#ef4444\",',\n \" marginTop: 4,\",\n \" } as TextStyle,\",\n \"};\",\n \"\",\n \"export default Input;\"\n ),\n },\n {\n path: \"src/components/ui/card.tsx\",\n content: lines(\n 'import React from \"react\";',\n 'import { View, Text, type StyleProp, type ViewStyle, type TextStyle } from \"react-native\";',\n \"\",\n \"interface CardProps {\",\n \" title?: string;\",\n \" description?: string;\",\n \" children: React.ReactNode;\",\n \" style?: StyleProp<ViewStyle>;\",\n \" titleStyle?: StyleProp<TextStyle>;\",\n \" descriptionStyle?: StyleProp<TextStyle>;\",\n ' padding?: \"none\" | \"sm\" | \"md\" | \"lg\";',\n \"}\",\n \"\",\n \"const paddingMap: Record<NonNullable<CardProps[\\\"padding\\\"]>, number> = {\",\n \" none: 0,\",\n \" sm: 8,\",\n \" md: 16,\",\n \" lg: 24,\",\n \"};\",\n \"\",\n \"export function Card({\",\n \" title,\",\n \" description,\",\n \" children,\",\n \" style,\",\n \" titleStyle,\",\n \" descriptionStyle,\",\n ' padding = \"md\",',\n \"}: CardProps) {\",\n \" return (\",\n \" <View\",\n \" style={[\",\n \" {\",\n ' backgroundColor: \"#ffffff\",',\n \" borderRadius: 12,\",\n \" borderWidth: 1,\",\n ' borderColor: \"#e4e4e7\",',\n \" padding: paddingMap[padding],\",\n \" },\",\n \" style,\",\n \" ]}\",\n \" >\",\n \" {title && (\",\n \" <Text style={[styles.title, titleStyle]}>{title}</Text>\",\n \" )}\",\n \" {description && (\",\n \" <Text style={[styles.description, descriptionStyle]}>\",\n \" {description}\",\n \" </Text>\",\n \" )}\",\n \" {children}\",\n \" </View>\",\n \" );\",\n \"}\",\n \"\",\n \"const styles = {\",\n \" title: {\",\n \" fontSize: 18,\",\n ' fontWeight: \"600\",',\n ' color: \"#18181b\",',\n \" marginBottom: 4,\",\n \" } as TextStyle,\",\n \" description: {\",\n \" fontSize: 14,\",\n ' color: \"#71717a\",',\n \" marginBottom: 12,\",\n \" } as TextStyle,\",\n \"};\",\n \"\",\n \"export default Card;\"\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 * These form the core Expo Router + NativeWind project structure.\n */\nexport function generateBaseTemplates(projectName: string): TemplateFile[] {\n return [\n // ─── app/_layout.tsx ────────────────────────────────────────────────\n {\n path: \"app/_layout.tsx\",\n content: `import { 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\";\n\nimport { Colors } from \"@/src/constants/Colors\";\n\nSplashScreen.preventAutoHideAsync();\n\nexport default function RootLayout() {\n const colorScheme = useColorScheme();\n const [loaded] = useFonts({\n SpaceMono: require(\"../assets/fonts/SpaceMono-Regular.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\" ? DarkTheme : DefaultTheme}>\n <Stack>\n <Stack.Screen name=\"(tabs)\" options={{ headerShown: false }} />\n <Stack.Screen name=\"+not-found\" />\n </Stack>\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\nimport { Colors } from \"@/src/constants/Colors\";\nimport { useColorScheme } from \"@/src/hooks/useColorScheme\";\n\nexport default function TabLayout() {\n const colorScheme = useColorScheme();\n\n return (\n <Tabs\n screenOptions={{\n tabBarActiveTintColor: Colors[colorScheme ?? \"light\"].tint,\n headerStyle: {\n backgroundColor: Colors[colorScheme ?? \"light\"].background,\n },\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: () => null,\n }}\n />\n <Tabs.Screen\n name=\"explore\"\n options={{\n title: \"Explore\",\n tabBarIcon: () => null,\n }}\n />\n </Tabs>\n );\n}\n`,\n },\n\n // ─── app/(tabs)/index.tsx ───────────────────────────────────────────\n {\n path: \"app/(tabs)/index.tsx\",\n content: `import { Image, StyleSheet, Platform } from \"react-native\";\nimport { HelloWave } from \"@/src/components/HelloWave\";\nimport { ThemedText } from \"@/src/components/Themed\";\nimport { ThemedView } from \"@/src/components/Themed\";\nimport { Link } from \"expo-router\";\n\nexport default function HomeScreen() {\n return (\n <ThemedView style={styles.container}>\n <ThemedView style={styles.titleContainer}>\n <ThemedText type=\"title\">${projectName}</ThemedText>\n <HelloWave />\n </ThemedView>\n <ThemedView style={styles.stepContainer}>\n <ThemedText type=\"subtitle\">Step 1: Try it</ThemedText>\n <ThemedText>\n Edit <ThemedText type=\"defaultSemiBold\">app/(tabs)/index.tsx</ThemedText> to see changes.\n Press{\" \"}\n <ThemedText type=\"defaultSemiBold\">\n {Platform.select({ ios: \"cmd + d\", android: \"cmd + m\" })}\n </ThemedText>{\" \"}\n to open developer tools.\n </ThemedText>\n </ThemedView>\n <ThemedView style={styles.stepContainer}>\n <ThemedText type=\"subtitle\">Step 2: Explore</ThemedText>\n <ThemedText>\n Tap the Explore tab to learn more about what's included in this starter.\n </ThemedText>\n <Link href=\"/explore\">\n <ThemedText type=\"link\">Go to Explore →</ThemedText>\n </Link>\n </ThemedView>\n </ThemedView>\n );\n}\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n alignItems: \"center\",\n justifyContent: \"center\",\n },\n titleContainer: {\n flexDirection: \"row\",\n alignItems: \"center\",\n gap: 8,\n },\n stepContainer: {\n gap: 8,\n marginBottom: 8,\n },\n});\n`,\n },\n\n // ─── app/(tabs)/explore.tsx ─────────────────────────────────────────\n {\n path: \"app/(tabs)/explore.tsx\",\n content: `import { StyleSheet, Image, Platform } from \"react-native\";\nimport { ThemedText } from \"@/src/components/Themed\";\nimport { ThemedView } from \"@/src/components/Themed\";\n\nexport default function ExploreScreen() {\n return (\n <ThemedView style={styles.container}>\n <ThemedText type=\"title\">Explore</ThemedText>\n <ThemedText style={styles.subtitle}>\n This screen shows what you can do with this scaffolded project.\n </ThemedText>\n </ThemedView>\n );\n}\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n alignItems: \"center\",\n justifyContent: \"center\",\n },\n subtitle: {\n fontSize: 16,\n textAlign: \"center\",\n marginTop: 8,\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 { StyleSheet } from \"react-native\";\nimport { ThemedText } from \"@/src/components/Themed\";\nimport { ThemedView } from \"@/src/components/Themed\";\n\nexport default function NotFoundScreen() {\n return (\n <>\n <Stack.Screen options={{ title: \"Oops!\" }} />\n <ThemedView style={styles.container}>\n <ThemedText type=\"title\">This screen doesn't exist.</ThemedText>\n <Link href=\"/\" style={styles.link}>\n <ThemedText type=\"link\">Go to home screen!</ThemedText>\n </Link>\n </ThemedView>\n </>\n );\n}\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: 20,\n },\n link: {\n marginTop: 15,\n paddingVertical: 15,\n },\n});\n`,\n },\n\n // ─── src/components/Themed.tsx ───────────────────────────────────────\n {\n path: \"src/components/Themed.tsx\",\n content: `import { Text, type TextProps, View, type ViewProps } from \"react-native\";\nimport { useColorScheme } from \"@/src/hooks/useColorScheme\";\nimport { Colors } from \"@/src/constants/Colors\";\n\n/** Themed text component that adapts to light/dark mode */\nexport function ThemedText({\n style,\n type = \"default\",\n ...rest\n}: TextProps & { type?: \"default\" | \"title\" | \"defaultSemiBold\" | \"subtitle\" | \"link\" }) {\n const colorScheme = useColorScheme();\n const color = Colors[colorScheme ?? \"light\"].text;\n\n return (\n <Text\n style={[\n { color },\n type === \"default\" ? { fontSize: 16, lineHeight: 24 } : undefined,\n type === \"title\" ? { fontSize: 28, fontWeight: \"bold\", lineHeight: 32 } : undefined,\n type === \"defaultSemiBold\" ? { fontSize: 16, lineHeight: 24, fontWeight: \"600\" } : undefined,\n type === \"subtitle\" ? { fontSize: 20, fontWeight: \"bold\" } : undefined,\n type === \"link\" ? { fontSize: 16, lineHeight: 24, color: Colors[colorScheme ?? \"light\"].tint } : undefined,\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Themed view component that adapts to light/dark mode */\nexport function ThemedView({ style, ...rest }: ViewProps) {\n const colorScheme = useColorScheme();\n const backgroundColor = Colors[colorScheme ?? \"light\"].background;\n\n return <View style={[{ backgroundColor }, style]} {...rest} />;\n}\n`,\n },\n\n // ─── src/components/HelloWave.tsx ────────────────────────────────────\n {\n path: \"src/components/HelloWave.tsx\",\n content: `import { useEffect } from \"react\";\nimport { Animated, Easing } from \"react-native\";\nimport { ThemedText } from \"./Themed\";\n\nexport function HelloWave() {\n const rotationAnim = new Animated.Value(0);\n\n useEffect(() => {\n Animated.loop(\n Animated.sequence([\n Animated.timing(rotationAnim, {\n toValue: 1,\n duration: 150,\n easing: Easing.linear,\n useNativeDriver: true,\n }),\n Animated.timing(rotationAnim, {\n toValue: 0,\n duration: 150,\n easing: Easing.linear,\n useNativeDriver: true,\n }),\n ])\n ).start();\n }, [rotationAnim]);\n\n return (\n <Animated.View\n style={{\n transform: [\n {\n rotate: rotationAnim.interpolate({\n inputRange: [0, 1],\n outputRange: [\"0deg\", \"14deg\"],\n }),\n },\n ],\n }}\n >\n <ThemedText style={{ fontSize: 28 }}>👋</ThemedText>\n </Animated.View>\n );\n}\n`,\n },\n\n // ─── src/hooks/useColorScheme.ts ─────────────────────────────────────\n {\n path: \"src/hooks/useColorScheme.ts\",\n content: `import { useColorScheme as useRNColorScheme } from \"react-native\";\n\n/**\n * Returns the current color scheme (light or dark).\n * Defaults to \"light\" if the system preference is not available.\n */\nexport function useColorScheme(): \"light\" | \"dark\" {\n return useRNColorScheme() ?? \"light\";\n}\n`,\n },\n\n // ─── src/constants/Colors.ts ─────────────────────────────────────────\n {\n path: \"src/constants/Colors.ts\",\n content: `/**\n * Color tokens for light and dark themes.\n * Used by Themed components and navigation theming.\n */\nexport const Colors = {\n light: {\n text: \"#11181C\",\n background: \"#fff\",\n tint: \"#0a7ea4\",\n tabIconDefault: \"#687076\",\n tabIconSelected: \"#0a7ea4\",\n },\n dark: {\n text: \"#ECEDEE\",\n background: \"#151718\",\n tint: \"#fff\",\n tabIconDefault: \"#9BA1A6\",\n tabIconSelected: \"#fff\",\n },\n};\n`,\n },\n\n // ─── src/types/index.ts ─────────────────────────────────────────────\n {\n path: \"src/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 \"icon\": \"./assets/icon.png\",\n \"userInterfaceStyle\": \"light\",\n \"splash\": {\n \"image\": \"./assets/splash.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 \"compilerOptions\": {\n \"target\": \"ESNext\",\n \"module\": \"ESNext\",\n \"moduleResolution\": \"bundler\",\n \"lib\": [\"ESNext\"],\n \"strict\": true,\n \"jsx\": \"react-jsx\",\n \"esModuleInterop\": true,\n \"skipLibCheck\": true,\n \"forceConsistentCasingInFileNames\": true,\n \"resolveJsonModule\": true,\n \"isolatedModules\": true,\n \"noEmit\": true,\n \"paths\": {\n \"@/*\": [\"./*\"]\n }\n },\n \"include\": [\"**/*.ts\", \"**/*.tsx\", \".expo/types/**/*.ts\", \"expo-env.d.ts\"],\n \"exclude\": [\"node_modules\"]\n}\n`,\n },\n\n // ─── tailwind.config.js ──────────────────────────────────────────────\n {\n path: \"tailwind.config.js\",\n content: `/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n content: [\n \"./app/**/*.{js,jsx,ts,tsx}\",\n \"./src/**/*.{js,jsx,ts,tsx}\",\n ],\n theme: {\n extend: {},\n },\n plugins: [],\n};\n`,\n },\n\n // ─── metro.config.js ─────────────────────────────────────────────────\n {\n path: \"metro.config.js\",\n content: `const { getDefaultConfig } = require(\"expo/metro-config\");\nconst { withTailwind } = require(\"@expo/metro-config/tailwind\");\n\nconst config = getDefaultConfig(__dirname);\n\nmodule.exports = withTailwind(config);\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: [\"babel-preset-expo\"],\n plugins: [\"nativewind/babel\"],\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 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 */\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 android: \"expo start --android\",\n ios: \"expo start --ios\",\n web: \"expo start --web\",\n lint: \"eslint .\",\n },\n dependencies: {\n expo: \"~54.0.0\",\n \"expo-router\": \"~5.0.0\",\n \"expo-linking\": \"~7.0.0\",\n \"expo-constants\": \"~18.0.0\",\n \"expo-status-bar\": \"~2.0.0\",\n \"expo-splash-screen\": \"~1.0.0\",\n react: \"19.0.0\",\n \"react-native\": \"0.79.0\",\n \"react-native-safe-area-context\": \"5.4.0\",\n \"react-native-screens\": \"~4.6.0\",\n nativewind: \"^4.1.0\",\n tailwindcss: \"^3.4.0\",\n },\n devDependencies: {\n \"@types/react\": \"~19.0.0\",\n typescript: \"^5.5.0\",\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAkB;AAClB,qBAAoB;AACpB,iBAAgB;AAChB,IAAAA,eAAiB;AACjB,IAAAC,mBAAgB;AAChB,uBAAwB;;;ACCjB,SAAS,sBAAsB,SAAwB;AAC5D,UACG,QAAQ,uBAAuB,EAC/B,YAAY,iDAAiD,EAC7D,OAAO,OAAO,gBAAwB;AACrC,UAAM,cAAc,WAAW;AAAA,EACjC,CAAC;AACL;;;ACTO,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;A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ACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,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;AAAA,MACA;AAAA,QACE;AAAA,QACA;AAAA,UACE,kBAAkB;AAAA,QACpB;AAAA,MACF;AAAA,MACA;AAAA,QACE;AAAA,QACA;AAAA,UACE,sCACE;AAAA,QACJ;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAO,qBAAQ;;;AC9Jf,IAAM,oBAA+B;AAAA,EACnC,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,cAAc;AAAA,IACZ,wBAAwB;AAAA,EAC1B;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,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA,iBAAiB,CAAC,4BAA4B;AAAA,EAC9C,eAAe;AAAA,IACb;AAAA,EACF;AACF;AAEA,IAAO,uBAAQ;;;AC3Gf,IAAM,kBAA6B;AAAA,EACjC,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,cAAc;AAAA,IACZ,uBAAuB;AAAA,EACzB;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,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAO,oBAAQ;;;AC1Gf,IAAM,oBAA+B;AAAA,EACnC,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,cAAc;AAAA,IACZ,yBAAyB;AAAA,IACzB,oBAAoB;AAAA,IACpB,uBAAuB;AAAA,IACvB,wBAAwB;AAAA,EAC1B;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,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,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;AAA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","names":["import_path","import_fs_extra","path","fs","path","fs","chalk","prompts","path","ora","fse","fs"]}
|