@codyswann/lisa 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (322) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +867 -0
  3. package/all/copy-overwrite/.claude/README.md +205 -0
  4. package/all/copy-overwrite/.claude/agents/agent-architect.md +311 -0
  5. package/all/copy-overwrite/.claude/agents/codebase-analyzer.md +146 -0
  6. package/all/copy-overwrite/.claude/agents/codebase-locator.md +125 -0
  7. package/all/copy-overwrite/.claude/agents/codebase-pattern-finder.md +237 -0
  8. package/all/copy-overwrite/.claude/agents/git-history-analyzer.md +183 -0
  9. package/all/copy-overwrite/.claude/agents/hooks-expert.md +74 -0
  10. package/all/copy-overwrite/.claude/agents/skill-evaluator.md +246 -0
  11. package/all/copy-overwrite/.claude/agents/slash-command-architect.md +87 -0
  12. package/all/copy-overwrite/.claude/agents/web-search-researcher.md +112 -0
  13. package/all/copy-overwrite/.claude/commands/git/commit-and-submit-pr.md +8 -0
  14. package/all/copy-overwrite/.claude/commands/git/commit.md +44 -0
  15. package/all/copy-overwrite/.claude/commands/git/prune.md +34 -0
  16. package/all/copy-overwrite/.claude/commands/git/submit-pr.md +50 -0
  17. package/all/copy-overwrite/.claude/commands/jira/create.md +50 -0
  18. package/all/copy-overwrite/.claude/commands/jira/verify.md +34 -0
  19. package/all/copy-overwrite/.claude/commands/project/archive.md +8 -0
  20. package/all/copy-overwrite/.claude/commands/project/bootstrap.md +49 -0
  21. package/all/copy-overwrite/.claude/commands/project/complete-task.md +7 -0
  22. package/all/copy-overwrite/.claude/commands/project/debrief.md +65 -0
  23. package/all/copy-overwrite/.claude/commands/project/execute.md +94 -0
  24. package/all/copy-overwrite/.claude/commands/project/implement.md +42 -0
  25. package/all/copy-overwrite/.claude/commands/project/local-code-review.md +88 -0
  26. package/all/copy-overwrite/.claude/commands/project/lower-code-complexity.md +74 -0
  27. package/all/copy-overwrite/.claude/commands/project/plan.md +314 -0
  28. package/all/copy-overwrite/.claude/commands/project/research.md +248 -0
  29. package/all/copy-overwrite/.claude/commands/project/review.md +63 -0
  30. package/all/copy-overwrite/.claude/commands/project/setup.md +19 -0
  31. package/all/copy-overwrite/.claude/commands/project/verify.md +38 -0
  32. package/all/copy-overwrite/.claude/commands/pull-request/review.md +12 -0
  33. package/all/copy-overwrite/.claude/commands/rules/format-md.md +72 -0
  34. package/all/copy-overwrite/.claude/commands/sonarqube/check.md +6 -0
  35. package/all/copy-overwrite/.claude/commands/sonarqube/fix.md +3 -0
  36. package/all/copy-overwrite/.claude/hooks/README.md +301 -0
  37. package/all/copy-overwrite/.claude/hooks/notify-ntfy.sh +181 -0
  38. package/all/copy-overwrite/.claude/settings.json +41 -0
  39. package/all/copy-overwrite/.claude/settings.local.json.example +14 -0
  40. package/all/copy-overwrite/.claude/skills/coding-philosophy/SKILL.md +405 -0
  41. package/all/copy-overwrite/.claude/skills/coding-philosophy/references/function-structure.md +416 -0
  42. package/all/copy-overwrite/.claude/skills/coding-philosophy/references/immutable-patterns.md +316 -0
  43. package/all/copy-overwrite/.claude/skills/prompt-complexity-scorer/SKILL.md +118 -0
  44. package/all/copy-overwrite/.claude/skills/skill-creator/LICENSE.txt +202 -0
  45. package/all/copy-overwrite/.claude/skills/skill-creator/SKILL.md +210 -0
  46. package/all/copy-overwrite/.claude/skills/skill-creator/scripts/__pycache__/quick_validate.cpython-312.pyc +0 -0
  47. package/all/copy-overwrite/.claude/skills/skill-creator/scripts/init_skill.py +303 -0
  48. package/all/copy-overwrite/.claude/skills/skill-creator/scripts/package_skill.py +110 -0
  49. package/all/copy-overwrite/.claude/skills/skill-creator/scripts/quick_validate.py +65 -0
  50. package/all/copy-overwrite/CLAUDE.md +77 -0
  51. package/all/copy-overwrite/HUMAN.md +17 -0
  52. package/all/copy-overwrite/specs/.keep +0 -0
  53. package/all/create-only/PROJECT_RULES.md +0 -0
  54. package/cdk/merge/package.json +20 -0
  55. package/dist/cli/index.d.ts +7 -0
  56. package/dist/cli/index.d.ts.map +1 -0
  57. package/dist/cli/index.js +107 -0
  58. package/dist/cli/index.js.map +1 -0
  59. package/dist/cli/prompts.d.ts +45 -0
  60. package/dist/cli/prompts.d.ts.map +1 -0
  61. package/dist/cli/prompts.js +58 -0
  62. package/dist/cli/prompts.js.map +1 -0
  63. package/dist/core/config.d.ts +73 -0
  64. package/dist/core/config.d.ts.map +1 -0
  65. package/dist/core/config.js +36 -0
  66. package/dist/core/config.js.map +1 -0
  67. package/dist/core/index.d.ts +4 -0
  68. package/dist/core/index.d.ts.map +1 -0
  69. package/dist/core/index.js +4 -0
  70. package/dist/core/index.js.map +1 -0
  71. package/dist/core/lisa.d.ts +81 -0
  72. package/dist/core/lisa.d.ts.map +1 -0
  73. package/dist/core/lisa.js +459 -0
  74. package/dist/core/lisa.js.map +1 -0
  75. package/dist/core/manifest.d.ts +58 -0
  76. package/dist/core/manifest.d.ts.map +1 -0
  77. package/dist/core/manifest.js +104 -0
  78. package/dist/core/manifest.js.map +1 -0
  79. package/dist/detection/detector.interface.d.ts +15 -0
  80. package/dist/detection/detector.interface.d.ts.map +1 -0
  81. package/dist/detection/detector.interface.js +2 -0
  82. package/dist/detection/detector.interface.js.map +1 -0
  83. package/dist/detection/detectors/cdk.d.ts +10 -0
  84. package/dist/detection/detectors/cdk.d.ts.map +1 -0
  85. package/dist/detection/detectors/cdk.js +34 -0
  86. package/dist/detection/detectors/cdk.js.map +1 -0
  87. package/dist/detection/detectors/expo.d.ts +10 -0
  88. package/dist/detection/detectors/expo.d.ts.map +1 -0
  89. package/dist/detection/detectors/expo.js +30 -0
  90. package/dist/detection/detectors/expo.js.map +1 -0
  91. package/dist/detection/detectors/nestjs.d.ts +10 -0
  92. package/dist/detection/detectors/nestjs.d.ts.map +1 -0
  93. package/dist/detection/detectors/nestjs.js +34 -0
  94. package/dist/detection/detectors/nestjs.js.map +1 -0
  95. package/dist/detection/detectors/npm-package.d.ts +13 -0
  96. package/dist/detection/detectors/npm-package.d.ts.map +1 -0
  97. package/dist/detection/detectors/npm-package.js +30 -0
  98. package/dist/detection/detectors/npm-package.js.map +1 -0
  99. package/dist/detection/detectors/typescript.d.ts +10 -0
  100. package/dist/detection/detectors/typescript.d.ts.map +1 -0
  101. package/dist/detection/detectors/typescript.js +25 -0
  102. package/dist/detection/detectors/typescript.js.map +1 -0
  103. package/dist/detection/index.d.ts +24 -0
  104. package/dist/detection/index.d.ts.map +1 -0
  105. package/dist/detection/index.js +57 -0
  106. package/dist/detection/index.js.map +1 -0
  107. package/dist/errors/index.d.ts +69 -0
  108. package/dist/errors/index.d.ts.map +1 -0
  109. package/dist/errors/index.js +110 -0
  110. package/dist/errors/index.js.map +1 -0
  111. package/dist/index.d.ts +3 -0
  112. package/dist/index.d.ts.map +1 -0
  113. package/dist/index.js +8 -0
  114. package/dist/index.js.map +1 -0
  115. package/dist/logging/console-logger.d.ts +12 -0
  116. package/dist/logging/console-logger.d.ts.map +1 -0
  117. package/dist/logging/console-logger.js +22 -0
  118. package/dist/logging/console-logger.js.map +1 -0
  119. package/dist/logging/index.d.ts +4 -0
  120. package/dist/logging/index.d.ts.map +1 -0
  121. package/dist/logging/index.js +3 -0
  122. package/dist/logging/index.js.map +1 -0
  123. package/dist/logging/logger.interface.d.ts +20 -0
  124. package/dist/logging/logger.interface.d.ts.map +1 -0
  125. package/dist/logging/logger.interface.js +2 -0
  126. package/dist/logging/logger.interface.js.map +1 -0
  127. package/dist/logging/silent-logger.d.ts +12 -0
  128. package/dist/logging/silent-logger.d.ts.map +1 -0
  129. package/dist/logging/silent-logger.js +21 -0
  130. package/dist/logging/silent-logger.js.map +1 -0
  131. package/dist/strategies/copy-contents.d.ts +14 -0
  132. package/dist/strategies/copy-contents.d.ts.map +1 -0
  133. package/dist/strategies/copy-contents.js +69 -0
  134. package/dist/strategies/copy-contents.js.map +1 -0
  135. package/dist/strategies/copy-overwrite.d.ts +14 -0
  136. package/dist/strategies/copy-overwrite.d.ts.map +1 -0
  137. package/dist/strategies/copy-overwrite.js +47 -0
  138. package/dist/strategies/copy-overwrite.js.map +1 -0
  139. package/dist/strategies/create-only.d.ts +13 -0
  140. package/dist/strategies/create-only.d.ts.map +1 -0
  141. package/dist/strategies/create-only.js +30 -0
  142. package/dist/strategies/create-only.js.map +1 -0
  143. package/dist/strategies/index.d.ts +31 -0
  144. package/dist/strategies/index.d.ts.map +1 -0
  145. package/dist/strategies/index.js +52 -0
  146. package/dist/strategies/index.js.map +1 -0
  147. package/dist/strategies/merge.d.ts +13 -0
  148. package/dist/strategies/merge.d.ts.map +1 -0
  149. package/dist/strategies/merge.js +60 -0
  150. package/dist/strategies/merge.js.map +1 -0
  151. package/dist/strategies/strategy.interface.d.ts +31 -0
  152. package/dist/strategies/strategy.interface.d.ts.map +1 -0
  153. package/dist/strategies/strategy.interface.js +2 -0
  154. package/dist/strategies/strategy.interface.js.map +1 -0
  155. package/dist/transaction/backup.d.ts +38 -0
  156. package/dist/transaction/backup.d.ts.map +1 -0
  157. package/dist/transaction/backup.js +97 -0
  158. package/dist/transaction/backup.js.map +1 -0
  159. package/dist/transaction/index.d.ts +4 -0
  160. package/dist/transaction/index.d.ts.map +1 -0
  161. package/dist/transaction/index.js +3 -0
  162. package/dist/transaction/index.js.map +1 -0
  163. package/dist/transaction/transaction.d.ts +34 -0
  164. package/dist/transaction/transaction.d.ts.map +1 -0
  165. package/dist/transaction/transaction.js +68 -0
  166. package/dist/transaction/transaction.js.map +1 -0
  167. package/dist/utils/file-operations.d.ts +29 -0
  168. package/dist/utils/file-operations.d.ts.map +1 -0
  169. package/dist/utils/file-operations.js +84 -0
  170. package/dist/utils/file-operations.js.map +1 -0
  171. package/dist/utils/index.d.ts +4 -0
  172. package/dist/utils/index.d.ts.map +1 -0
  173. package/dist/utils/index.js +4 -0
  174. package/dist/utils/index.js.map +1 -0
  175. package/dist/utils/json-utils.d.ts +22 -0
  176. package/dist/utils/json-utils.d.ts.map +1 -0
  177. package/dist/utils/json-utils.js +57 -0
  178. package/dist/utils/json-utils.js.map +1 -0
  179. package/dist/utils/path-utils.d.ts +21 -0
  180. package/dist/utils/path-utils.d.ts.map +1 -0
  181. package/dist/utils/path-utils.js +35 -0
  182. package/dist/utils/path-utils.js.map +1 -0
  183. package/eslint-plugin-code-organization/README.md +149 -0
  184. package/eslint-plugin-code-organization/__tests__/enforce-statement-order.test.js +468 -0
  185. package/eslint-plugin-code-organization/index.js +23 -0
  186. package/eslint-plugin-code-organization/package.json +10 -0
  187. package/eslint-plugin-code-organization/rules/enforce-statement-order.js +157 -0
  188. package/expo/copy-overwrite/.claude/skills/apollo-client/SKILL.md +238 -0
  189. package/expo/copy-overwrite/.claude/skills/apollo-client/references/mutation-patterns.md +360 -0
  190. package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/SKILL.md +360 -0
  191. package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/references/atomic-levels.md +417 -0
  192. package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/references/folder-structure.md +257 -0
  193. package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/references/gluestack-mapping.md +233 -0
  194. package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/scripts/validate_atomic_structure.py +327 -0
  195. package/expo/copy-overwrite/.claude/skills/container-view-pattern/SKILL.md +299 -0
  196. package/expo/copy-overwrite/.claude/skills/container-view-pattern/references/examples.md +749 -0
  197. package/expo/copy-overwrite/.claude/skills/container-view-pattern/references/patterns.md +318 -0
  198. package/expo/copy-overwrite/.claude/skills/container-view-pattern/scripts/create_component.py +198 -0
  199. package/expo/copy-overwrite/.claude/skills/container-view-pattern/scripts/validate_component.py +207 -0
  200. package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/SKILL.md +268 -0
  201. package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/references/common-issues.md +619 -0
  202. package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/references/file-extensions.md +340 -0
  203. package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/references/platform-api.md +276 -0
  204. package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/scripts/validate_cross_platform.py +414 -0
  205. package/expo/copy-overwrite/.claude/skills/directory-structure/SKILL.md +202 -0
  206. package/expo/copy-overwrite/.claude/skills/directory-structure/scripts/validate_structure.py +443 -0
  207. package/expo/copy-overwrite/.claude/skills/expo-env-config/SKILL.md +309 -0
  208. package/expo/copy-overwrite/.claude/skills/expo-env-config/references/validation-patterns.md +417 -0
  209. package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/SKILL.md +431 -0
  210. package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/references/official-docs.md +290 -0
  211. package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/scripts/generate-route.py +169 -0
  212. package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/SKILL.md +411 -0
  213. package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/references/color-tokens.md +343 -0
  214. package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/references/component-mapping.md +307 -0
  215. package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/references/spacing-scale.md +300 -0
  216. package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/scripts/validate_styling.py +354 -0
  217. package/expo/copy-overwrite/.claude/skills/local-state/SKILL.md +362 -0
  218. package/expo/copy-overwrite/.claude/skills/local-state/references/async-storage.md +505 -0
  219. package/expo/copy-overwrite/.claude/skills/local-state/references/persistence-patterns.md +711 -0
  220. package/expo/copy-overwrite/.claude/skills/local-state/references/reactive-variables.md +446 -0
  221. package/expo/copy-overwrite/.claude/skills/playwright-selectors/SKILL.md +223 -0
  222. package/expo/copy-overwrite/.claude/skills/testing-library/SKILL.md +319 -0
  223. package/expo/copy-overwrite/.claude/skills/testing-library/references/async-patterns.md +420 -0
  224. package/expo/copy-overwrite/.claude/skills/testing-library/references/expo-router-testing.md +556 -0
  225. package/expo/copy-overwrite/.claude/skills/testing-library/references/mocking-patterns.md +590 -0
  226. package/expo/copy-overwrite/.claude/skills/testing-library/references/query-priority.md +291 -0
  227. package/expo/copy-overwrite/.easignore.extra +2 -0
  228. package/expo/copy-overwrite/.mcp.json +33 -0
  229. package/expo/copy-overwrite/eslint-plugin-component-structure/README.md +234 -0
  230. package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/plugin-index.test.js +84 -0
  231. package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/require-memo-in-view.test.js +196 -0
  232. package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/single-component-per-file.test.js +289 -0
  233. package/expo/copy-overwrite/eslint-plugin-component-structure/index.js +32 -0
  234. package/expo/copy-overwrite/eslint-plugin-component-structure/package.json +10 -0
  235. package/expo/copy-overwrite/eslint-plugin-component-structure/rules/enforce-component-structure.js +230 -0
  236. package/expo/copy-overwrite/eslint-plugin-component-structure/rules/no-return-in-view.js +91 -0
  237. package/expo/copy-overwrite/eslint-plugin-component-structure/rules/require-memo-in-view.js +178 -0
  238. package/expo/copy-overwrite/eslint-plugin-component-structure/rules/single-component-per-file.js +238 -0
  239. package/expo/copy-overwrite/eslint-plugin-ui-standards/README.md +260 -0
  240. package/expo/copy-overwrite/eslint-plugin-ui-standards/index.js +29 -0
  241. package/expo/copy-overwrite/eslint-plugin-ui-standards/package.json +10 -0
  242. package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-classname-outside-ui.js +51 -0
  243. package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-direct-rn-imports.js +55 -0
  244. package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-inline-styles.js +73 -0
  245. package/expo/copy-overwrite/eslint.config.mjs +560 -0
  246. package/expo/copy-overwrite/lighthouserc.js +194 -0
  247. package/expo/create-only/lighthouserc-config.json +28 -0
  248. package/expo/merge/package.json +132 -0
  249. package/lisa.sh +35 -0
  250. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/SKILL.md +176 -0
  251. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/advanced-features.md +527 -0
  252. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/project-patterns.md +483 -0
  253. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/quick-start.md +257 -0
  254. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/resolvers-mutations.md +413 -0
  255. package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/types-scalars.md +513 -0
  256. package/nestjs/copy-overwrite/.claude/skills/nestjs-rules/SKILL.md +536 -0
  257. package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/SKILL.md +275 -0
  258. package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/references/configuration-patterns.md +487 -0
  259. package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/references/entity-patterns.md +450 -0
  260. package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/references/observability-patterns.md +536 -0
  261. package/nestjs/merge/package.json +75 -0
  262. package/package.json +124 -0
  263. package/typescript/copy-contents/.husky/commit-msg +91 -0
  264. package/typescript/copy-contents/.husky/pre-commit +96 -0
  265. package/typescript/copy-contents/.husky/pre-push +211 -0
  266. package/typescript/copy-overwrite/.claude/hooks/format-on-edit.sh +74 -0
  267. package/typescript/copy-overwrite/.claude/hooks/install_pkgs.sh +59 -0
  268. package/typescript/copy-overwrite/.claude/hooks/lint-on-edit.sh +103 -0
  269. package/typescript/copy-overwrite/.claude/skills/jsdoc-best-practices/SKILL.md +388 -0
  270. package/typescript/copy-overwrite/.github/README.md +455 -0
  271. package/typescript/copy-overwrite/.github/dependabot.yml +40 -0
  272. package/typescript/copy-overwrite/.github/k6/BROWSER_TESTING_NOTE.md +129 -0
  273. package/typescript/copy-overwrite/.github/k6/INTEGRATION_GUIDE.md +354 -0
  274. package/typescript/copy-overwrite/.github/k6/README.md +386 -0
  275. package/typescript/copy-overwrite/.github/k6/SCENARIO_SELECTION_GUIDE.md +264 -0
  276. package/typescript/copy-overwrite/.github/k6/examples/customer-deploy-integration.yml +115 -0
  277. package/typescript/copy-overwrite/.github/k6/examples/data-driven-test.js +268 -0
  278. package/typescript/copy-overwrite/.github/k6/scenarios/load.js +142 -0
  279. package/typescript/copy-overwrite/.github/k6/scenarios/load.json +27 -0
  280. package/typescript/copy-overwrite/.github/k6/scenarios/smoke.js +26 -0
  281. package/typescript/copy-overwrite/.github/k6/scenarios/smoke.json +20 -0
  282. package/typescript/copy-overwrite/.github/k6/scenarios/soak.js +244 -0
  283. package/typescript/copy-overwrite/.github/k6/scenarios/soak.json +29 -0
  284. package/typescript/copy-overwrite/.github/k6/scenarios/spike.js +180 -0
  285. package/typescript/copy-overwrite/.github/k6/scenarios/spike.json +32 -0
  286. package/typescript/copy-overwrite/.github/k6/scenarios/stress.js +206 -0
  287. package/typescript/copy-overwrite/.github/k6/scenarios/stress.json +38 -0
  288. package/typescript/copy-overwrite/.github/k6/scripts/api-test.js +452 -0
  289. package/typescript/copy-overwrite/.github/k6/scripts/default-test.js +185 -0
  290. package/typescript/copy-overwrite/.github/k6/thresholds/normal.json +30 -0
  291. package/typescript/copy-overwrite/.github/k6/thresholds/relaxed.json +21 -0
  292. package/typescript/copy-overwrite/.github/k6/thresholds/strict.json +29 -0
  293. package/typescript/copy-overwrite/.github/workflows/build.yml +72 -0
  294. package/typescript/copy-overwrite/.github/workflows/ci.yml +49 -0
  295. package/typescript/copy-overwrite/.github/workflows/claude.yml +51 -0
  296. package/typescript/copy-overwrite/.github/workflows/create-github-issue-on-failure.yml +113 -0
  297. package/typescript/copy-overwrite/.github/workflows/create-jira-issue-on-failure.yml +195 -0
  298. package/typescript/copy-overwrite/.github/workflows/create-sentry-issue-on-failure.yml +267 -0
  299. package/typescript/copy-overwrite/.github/workflows/deploy.yml +228 -0
  300. package/typescript/copy-overwrite/.github/workflows/k6-load-test-README.md +230 -0
  301. package/typescript/copy-overwrite/.github/workflows/lighthouse.yml +68 -0
  302. package/typescript/copy-overwrite/.github/workflows/load-test.yml +282 -0
  303. package/typescript/copy-overwrite/.github/workflows/quality.yml +1737 -0
  304. package/typescript/copy-overwrite/.github/workflows/release.yml +1599 -0
  305. package/typescript/copy-overwrite/.gitleaksignore +28 -0
  306. package/typescript/copy-overwrite/.nvmrc +1 -0
  307. package/typescript/copy-overwrite/.prettierignore +23 -0
  308. package/typescript/copy-overwrite/.prettierrc.json +22 -0
  309. package/typescript/copy-overwrite/.versionrc +42 -0
  310. package/typescript/copy-overwrite/.yamllint +20 -0
  311. package/typescript/copy-overwrite/commitlint.config.js +11 -0
  312. package/typescript/copy-overwrite/eslint-plugin-code-organization/README.md +149 -0
  313. package/typescript/copy-overwrite/eslint-plugin-code-organization/__tests__/enforce-statement-order.test.js +468 -0
  314. package/typescript/copy-overwrite/eslint-plugin-code-organization/index.js +23 -0
  315. package/typescript/copy-overwrite/eslint-plugin-code-organization/package.json +10 -0
  316. package/typescript/copy-overwrite/eslint-plugin-code-organization/rules/enforce-statement-order.js +157 -0
  317. package/typescript/copy-overwrite/eslint.config.mjs +390 -0
  318. package/typescript/copy-overwrite/eslint.ignore.config.json +57 -0
  319. package/typescript/copy-overwrite/eslint.thresholds.config.json +5 -0
  320. package/typescript/github-rulesets/base.json +106 -0
  321. package/typescript/merge/.claude/settings.json +28 -0
  322. package/typescript/merge/package.json +71 -0
@@ -0,0 +1,431 @@
1
+ ---
2
+ name: expo-router-best-practices
3
+ description: This skill should be used when creating new routes, configuring navigation layouts, implementing deep linking, or organizing the app/ directory structure in Expo Router projects. It provides best practices for file-based routing patterns.
4
+ ---
5
+
6
+ # Expo Router Best Practices
7
+
8
+ This skill provides guidance for implementing file-based routing with Expo Router following established best practices and official documentation patterns.
9
+
10
+ ## Core Principles
11
+
12
+ ### 1. Routes Are Thin Wrappers
13
+
14
+ Route files in the `app/` directory should be minimal pass-throughs to feature screen components. Business logic and complex UI components belong in feature directories, not route files.
15
+
16
+ ```typescript
17
+ // app/players/[playerId]/compare.tsx - CORRECT
18
+ import { Main } from "@/features/compare-players/screens/Main";
19
+
20
+ /**
21
+ * Compare players route.
22
+ * URL: /players/[playerId]/compare
23
+ */
24
+ export default function CompareScreen() {
25
+ return <Main />;
26
+ }
27
+ ```
28
+
29
+ ```typescript
30
+ // app/players/[playerId]/compare.tsx - INCORRECT
31
+ export default function CompareScreen() {
32
+ const { playerId } = useLocalSearchParams();
33
+ const [data, setData] = useState(null);
34
+ // ... 200 lines of business logic
35
+ return <ComplexUI />;
36
+ }
37
+ ```
38
+
39
+ ### 2. Descriptive Component Names
40
+
41
+ Use descriptive names for route components, not generic names.
42
+
43
+ ```typescript
44
+ // CORRECT
45
+ export default function CompareScreen() { ... }
46
+ export default function PlayerDetailScreen() { ... }
47
+ export default function SettingsScreen() { ... }
48
+
49
+ // INCORRECT
50
+ export default function Screen() { ... }
51
+ export default function Page() { ... }
52
+ export default function Index() { ... } // only acceptable for index.tsx files
53
+ ```
54
+
55
+ ### 3. Document Route URLs in JSDoc
56
+
57
+ Include the URL pattern in route file documentation.
58
+
59
+ ```typescript
60
+ /**
61
+ * Player detail route.
62
+ * URL: /players/[playerId]
63
+ */
64
+ export default function PlayerDetailScreen() {
65
+ return <Main />;
66
+ }
67
+ ```
68
+
69
+ ## File Structure Patterns
70
+
71
+ ### Directory Organization
72
+
73
+ ```
74
+ app/
75
+ ├── _layout.tsx # Root layout (initialization, providers)
76
+ ├── index.tsx # Default route (/)
77
+ ├── +not-found.tsx # 404 handling
78
+ ├── +html.tsx # Web HTML customization (optional)
79
+ ├── (tabs)/ # Tab navigator group
80
+ │ ├── _layout.tsx # Tab configuration
81
+ │ ├── index.tsx # Default tab
82
+ │ ├── feed/ # Stack within tab
83
+ │ │ ├── _layout.tsx
84
+ │ │ ├── index.tsx
85
+ │ │ └── [postId].tsx
86
+ │ └── settings.tsx
87
+ ├── (auth)/ # Auth screens group
88
+ │ ├── sign-in.tsx
89
+ │ └── create-account.tsx
90
+ └── modal.tsx # Modal route
91
+ ```
92
+
93
+ ### Route Notation Reference
94
+
95
+ | Notation | Purpose | Example | URL |
96
+ | ---------------- | --------------------------- | -------------------- | -------- |
97
+ | `file.tsx` | Static route | `about.tsx` | `/about` |
98
+ | `[param].tsx` | Dynamic route | `[userId].tsx` | `/123` |
99
+ | `[...slug].tsx` | Catch-all route | `[...path].tsx` | `/a/b/c` |
100
+ | `(group)/` | Route group (no URL impact) | `(tabs)/` | `/` |
101
+ | `index.tsx` | Default route | `feed/index.tsx` | `/feed` |
102
+ | `_layout.tsx` | Layout definition | `(tabs)/_layout.tsx` | - |
103
+ | `+not-found.tsx` | 404 handler | `+not-found.tsx` | - |
104
+
105
+ ## Layout Patterns
106
+
107
+ ### Root Layout
108
+
109
+ The root `_layout.tsx` replaces `App.jsx/tsx`. Place initialization code here.
110
+
111
+ ```typescript
112
+ // app/_layout.tsx
113
+ import { useFonts } from "expo-font";
114
+ import { Stack } from "expo-router";
115
+ import * as SplashScreen from "expo-splash-screen";
116
+ import { useEffect } from "react";
117
+
118
+ SplashScreen.preventAutoHideAsync();
119
+
120
+ export default function RootLayout() {
121
+ const [loaded] = useFonts({
122
+ SpaceMono: require("../assets/fonts/SpaceMono-Regular.ttf"),
123
+ });
124
+
125
+ useEffect(() => {
126
+ if (loaded) {
127
+ SplashScreen.hide();
128
+ }
129
+ }, [loaded]);
130
+
131
+ if (!loaded) {
132
+ return null;
133
+ }
134
+
135
+ return <Stack />;
136
+ }
137
+ ```
138
+
139
+ ### Stack Layout
140
+
141
+ ```typescript
142
+ // app/products/_layout.tsx
143
+ import { Stack } from "expo-router";
144
+
145
+ export const unstable_settings = {
146
+ initialRouteName: "index",
147
+ };
148
+
149
+ export default function ProductsLayout() {
150
+ return (
151
+ <Stack>
152
+ <Stack.Screen name="index" options={{ title: "Products" }} />
153
+ <Stack.Screen name="[productId]" options={{ headerShown: false }} />
154
+ </Stack>
155
+ );
156
+ }
157
+ ```
158
+
159
+ ### Tab Layout
160
+
161
+ ```typescript
162
+ // app/(tabs)/_layout.tsx
163
+ import { Tabs } from "expo-router";
164
+ import MaterialIcons from "@expo/vector-icons/MaterialIcons";
165
+
166
+ export default function TabLayout() {
167
+ return (
168
+ <Tabs screenOptions={{ headerShown: false }}>
169
+ <Tabs.Screen
170
+ name="index"
171
+ options={{
172
+ title: "Home",
173
+ tabBarIcon: ({ color }) => (
174
+ <MaterialIcons size={28} name="home" color={color} />
175
+ ),
176
+ }}
177
+ />
178
+ <Tabs.Screen name="feed" options={{ title: "Feed" }} />
179
+ <Tabs.Screen name="settings" options={{ title: "Settings" }} />
180
+ </Tabs>
181
+ );
182
+ }
183
+ ```
184
+
185
+ ### Protected Routes (SDK 53+)
186
+
187
+ ```typescript
188
+ // app/_layout.tsx
189
+ import { Stack } from "expo-router";
190
+ import { useAuthState } from "@/hooks/useAuthState";
191
+
192
+ export default function RootLayout() {
193
+ const { isLoggedIn } = useAuthState();
194
+
195
+ return (
196
+ <Stack>
197
+ <Stack.Protected guard={isLoggedIn}>
198
+ <Stack.Screen name="(tabs)" />
199
+ <Stack.Screen name="modal" options={{ presentation: "modal" }} />
200
+ </Stack.Protected>
201
+
202
+ <Stack.Protected guard={!isLoggedIn}>
203
+ <Stack.Screen name="sign-in" />
204
+ <Stack.Screen name="create-account" />
205
+ </Stack.Protected>
206
+ </Stack>
207
+ );
208
+ }
209
+ ```
210
+
211
+ ## Navigation Patterns
212
+
213
+ ### Declarative Navigation (Preferred)
214
+
215
+ ```typescript
216
+ import { Link } from "expo-router";
217
+
218
+ // Basic link
219
+ <Link href="/about">About</Link>
220
+
221
+ // With custom component
222
+ <Link href="/profile" asChild>
223
+ <Pressable>
224
+ <Text>Profile</Text>
225
+ </Pressable>
226
+ </Link>
227
+
228
+ // Dynamic route
229
+ <Link href={{ pathname: "/user/[id]", params: { id: "123" } }}>
230
+ View User
231
+ </Link>
232
+
233
+ // With prefetching
234
+ <Link href="/heavy-page" prefetch>Heavy Page</Link>
235
+ ```
236
+
237
+ ### Imperative Navigation
238
+
239
+ ```typescript
240
+ import { useRouter } from "expo-router";
241
+
242
+ export default function Component() {
243
+ const router = useRouter();
244
+
245
+ const handleNavigate = () => {
246
+ // Navigate (adds to history)
247
+ router.navigate("/about");
248
+
249
+ // Push (always adds to stack)
250
+ router.push("/details");
251
+
252
+ // Replace (no back navigation)
253
+ router.replace("/home");
254
+
255
+ // Back
256
+ router.back();
257
+
258
+ // Dynamic route
259
+ router.navigate({
260
+ pathname: "/user/[id]",
261
+ params: { id: "123" },
262
+ });
263
+ };
264
+
265
+ return <Button onPress={handleNavigate} title="Navigate" />;
266
+ }
267
+ ```
268
+
269
+ ### Defensive Navigation Guards
270
+
271
+ Always validate parameters before navigation to prevent broken URLs.
272
+
273
+ ```typescript
274
+ const handleNavigation = useCallback(() => {
275
+ if (!entityId) {
276
+ console.error("Cannot navigate: entity ID is missing");
277
+ return;
278
+ }
279
+ router.push(`/players/${entityId}`);
280
+ }, [entityId, router]);
281
+ ```
282
+
283
+ ### Reading Route Parameters
284
+
285
+ ```typescript
286
+ import { useLocalSearchParams, useGlobalSearchParams } from "expo-router";
287
+
288
+ export default function UserPage() {
289
+ // Local params (current route only)
290
+ const { id, tab } = useLocalSearchParams<{ id: string; tab?: string }>();
291
+
292
+ // Global params (entire URL)
293
+ const globalParams = useGlobalSearchParams();
294
+
295
+ return <Text>User ID: {id}</Text>;
296
+ }
297
+ ```
298
+
299
+ ## Deep Linking
300
+
301
+ ### Configure URL Scheme
302
+
303
+ In `app.json` or `app.config.js`:
304
+
305
+ ```json
306
+ {
307
+ "expo": {
308
+ "scheme": "myapp"
309
+ }
310
+ }
311
+ ```
312
+
313
+ ### Initial Route for Deep Links
314
+
315
+ Ensure proper back navigation when deep linking.
316
+
317
+ ```typescript
318
+ // app/feed/_layout.tsx
319
+ export const unstable_settings = {
320
+ initialRouteName: "index",
321
+ };
322
+
323
+ export default function FeedLayout() {
324
+ return <Stack />;
325
+ }
326
+ ```
327
+
328
+ ### Deep Link with Anchor
329
+
330
+ ```typescript
331
+ // Forces initial route to load first
332
+ <Link href="/feed/post/123" withAnchor>
333
+ View Post
334
+ </Link>
335
+ ```
336
+
337
+ ## Common Patterns
338
+
339
+ ### Stacks Inside Tabs
340
+
341
+ ```
342
+ app/
343
+ ├── (tabs)/
344
+ │ ├── _layout.tsx # Tab navigator
345
+ │ ├── index.tsx # Home tab
346
+ │ ├── feed/ # Feed tab with stack
347
+ │ │ ├── _layout.tsx # Stack navigator
348
+ │ │ ├── index.tsx # Feed list
349
+ │ │ └── [postId].tsx # Post detail
350
+ │ └── settings.tsx # Settings tab
351
+ ```
352
+
353
+ ### Shared Routes Between Tabs
354
+
355
+ ```
356
+ app/
357
+ ├── (tabs)/
358
+ │ ├── _layout.tsx
359
+ │ ├── (feed)/ # Feed tab group
360
+ │ │ └── index.tsx
361
+ │ ├── (search)/ # Search tab group
362
+ │ │ └── index.tsx
363
+ │ └── (feed,search)/ # Shared between both
364
+ │ └── users/
365
+ │ └── [userId].tsx
366
+ ```
367
+
368
+ ### Modal Routes
369
+
370
+ ```typescript
371
+ // app/_layout.tsx
372
+ <Stack>
373
+ <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
374
+ <Stack.Screen
375
+ name="modal"
376
+ options={{
377
+ presentation: "modal",
378
+ animation: "slide_from_bottom",
379
+ }}
380
+ />
381
+ </Stack>
382
+ ```
383
+
384
+ ## Anti-Patterns to Avoid
385
+
386
+ ### 1. Business Logic in Route Files
387
+
388
+ Route files should only import and render feature components.
389
+
390
+ ### 2. Deeply Nested Navigators
391
+
392
+ Avoid nesting stacks within stacks unnecessarily. Use route groups instead.
393
+
394
+ ### 3. Missing initialRouteName
395
+
396
+ Always set `initialRouteName` in stack layouts for proper deep link behavior.
397
+
398
+ ### 4. Hardcoded Navigation Paths
399
+
400
+ Use typed routes or constants instead of string literals.
401
+
402
+ ```typescript
403
+ // AVOID
404
+ router.push("/players/123/compare");
405
+
406
+ // PREFER
407
+ router.push({
408
+ pathname: "/players/[playerId]/compare",
409
+ params: { playerId: "123" },
410
+ });
411
+ ```
412
+
413
+ ### 5. Using window APIs Without Platform Checks
414
+
415
+ ```typescript
416
+ // AVOID
417
+ const width = window.innerWidth;
418
+
419
+ // PREFER
420
+ import { useWindowDimensions } from "react-native";
421
+ const { width } = useWindowDimensions();
422
+ ```
423
+
424
+ ## Resources
425
+
426
+ For detailed documentation on specific topics, refer to:
427
+
428
+ - `references/official-docs.md` - Condensed official Expo Router documentation
429
+ - `scripts/generate-route.py` - Route scaffolding script
430
+
431
+ Official Documentation: https://docs.expo.dev/router/introduction/
@@ -0,0 +1,290 @@
1
+ # Expo Router Official Documentation Reference
2
+
3
+ This reference contains condensed official documentation from Expo Router.
4
+
5
+ ## Core Concepts
6
+
7
+ ### The Six Rules of Expo Router
8
+
9
+ 1. **All screens/pages are files inside the app directory** - Every file inside `app/` has a default export that defines a distinct page (except `_layout` files).
10
+
11
+ 2. **All pages have a URL** - All pages have a URL path matching the file's location, enabling universal deep-linking across platforms.
12
+
13
+ 3. **First index.tsx is the initial route** - Expo Router looks for the first `index.tsx` file matching the `/` URL. Use route groups `(groupName)` to organize without affecting URLs.
14
+
15
+ 4. **Root \_layout.tsx replaces App.jsx/tsx** - The root layout is rendered before any other route and contains initialization code (fonts, splash screen, providers).
16
+
17
+ 5. **Non-navigation components live outside app directory** - Components, hooks, utilities belong in other top-level directories. Alternatively, use `src/app/` with `src/components/`, `src/utils/`, etc.
18
+
19
+ 6. **It's still React Navigation under the hood** - Expo Router translates file structure into React Navigation components. Same options apply for styling/configuration.
20
+
21
+ ## Navigation Methods
22
+
23
+ ### Link Component (Declarative)
24
+
25
+ ```typescript
26
+ import { Link } from "expo-router";
27
+
28
+ // Basic
29
+ <Link href="/about">About</Link>
30
+
31
+ // With custom component
32
+ <Link href="/profile" asChild>
33
+ <Pressable><Text>Profile</Text></Pressable>
34
+ </Link>
35
+
36
+ // Dynamic route with params object
37
+ <Link href={{ pathname: "/user/[id]", params: { id: "bacon" } }}>
38
+ View User
39
+ </Link>
40
+
41
+ // Prefetching
42
+ <Link href="/about" prefetch />
43
+ ```
44
+
45
+ ### useRouter Hook (Imperative)
46
+
47
+ ```typescript
48
+ import { useRouter } from "expo-router";
49
+
50
+ const router = useRouter();
51
+
52
+ // Navigate (adds to history, won't duplicate)
53
+ router.navigate("/about");
54
+
55
+ // Push (always adds to stack)
56
+ router.push("/details");
57
+
58
+ // Replace (no back navigation)
59
+ router.replace("/home");
60
+
61
+ // Back
62
+ router.back();
63
+
64
+ // Dismiss modal
65
+ router.dismiss();
66
+
67
+ // Dismiss all modals
68
+ router.dismissAll();
69
+
70
+ // Can go back check
71
+ router.canGoBack();
72
+ ```
73
+
74
+ ### Redirect Component
75
+
76
+ ```typescript
77
+ import { Redirect } from "expo-router";
78
+
79
+ export default function Page() {
80
+ if (!isAuthenticated) {
81
+ return <Redirect href="/sign-in" />;
82
+ }
83
+ return <Content />;
84
+ }
85
+ ```
86
+
87
+ ## Route Parameters
88
+
89
+ ### Reading Parameters
90
+
91
+ ```typescript
92
+ import { useLocalSearchParams, useGlobalSearchParams } from "expo-router";
93
+
94
+ // Local params (current route only)
95
+ const { id, tab } = useLocalSearchParams<{ id: string; tab?: string }>();
96
+
97
+ // Global params (entire URL tree)
98
+ const params = useGlobalSearchParams();
99
+ ```
100
+
101
+ ### Updating Parameters Without Navigation
102
+
103
+ ```typescript
104
+ // Via Link
105
+ <Link href={{ pathname: "/current", params: { filter: "new" } }}>
106
+ Update Filter
107
+ </Link>
108
+
109
+ // Via router
110
+ router.setParams({ filter: "new" });
111
+ ```
112
+
113
+ ## Layout Components
114
+
115
+ ### Stack Navigator
116
+
117
+ ```typescript
118
+ import { Stack } from "expo-router";
119
+
120
+ export default function Layout() {
121
+ return (
122
+ <Stack
123
+ screenOptions={{
124
+ headerStyle: { backgroundColor: "#f4511e" },
125
+ headerTintColor: "#fff",
126
+ }}
127
+ >
128
+ <Stack.Screen name="index" options={{ title: "Home" }} />
129
+ <Stack.Screen name="[id]" options={{ headerShown: false }} />
130
+ </Stack>
131
+ );
132
+ }
133
+ ```
134
+
135
+ ### Tab Navigator
136
+
137
+ ```typescript
138
+ import { Tabs } from "expo-router";
139
+
140
+ export default function Layout() {
141
+ return (
142
+ <Tabs screenOptions={{ tabBarActiveTintColor: "blue" }}>
143
+ <Tabs.Screen
144
+ name="index"
145
+ options={{
146
+ title: "Home",
147
+ tabBarIcon: ({ color }) => <Icon name="home" color={color} />,
148
+ }}
149
+ />
150
+ </Tabs>
151
+ );
152
+ }
153
+ ```
154
+
155
+ ### Drawer Navigator
156
+
157
+ ```typescript
158
+ import { Drawer } from "expo-router/drawer";
159
+
160
+ export default function Layout() {
161
+ return (
162
+ <Drawer>
163
+ <Drawer.Screen name="index" options={{ title: "Home" }} />
164
+ <Drawer.Screen name="settings" options={{ title: "Settings" }} />
165
+ </Drawer>
166
+ );
167
+ }
168
+ ```
169
+
170
+ ### Slot (No Navigator)
171
+
172
+ ```typescript
173
+ import { Slot } from "expo-router";
174
+
175
+ export default function Layout() {
176
+ return (
177
+ <>
178
+ <Header />
179
+ <Slot />
180
+ <Footer />
181
+ </>
182
+ );
183
+ }
184
+ ```
185
+
186
+ ## Protected Routes (SDK 53+)
187
+
188
+ ```typescript
189
+ import { Stack } from "expo-router";
190
+
191
+ export default function RootLayout() {
192
+ const { isLoggedIn } = useAuth();
193
+
194
+ return (
195
+ <Stack>
196
+ <Stack.Protected guard={isLoggedIn}>
197
+ <Stack.Screen name="(app)" />
198
+ </Stack.Protected>
199
+ <Stack.Protected guard={!isLoggedIn}>
200
+ <Stack.Screen name="sign-in" />
201
+ </Stack.Protected>
202
+ </Stack>
203
+ );
204
+ }
205
+ ```
206
+
207
+ ## Special Files
208
+
209
+ | File | Purpose |
210
+ | -------------------- | ------------------------------ |
211
+ | `_layout.tsx` | Define navigator for directory |
212
+ | `index.tsx` | Default route for directory |
213
+ | `+not-found.tsx` | 404 error page |
214
+ | `+html.tsx` | Custom HTML wrapper (web) |
215
+ | `+native-intent.tsx` | Handle unmatched deep links |
216
+
217
+ ## unstable_settings
218
+
219
+ ```typescript
220
+ export const unstable_settings = {
221
+ // Initial route for deep links
222
+ initialRouteName: "index",
223
+
224
+ // Anchor for route groups
225
+ anchor: "(root)",
226
+ };
227
+ ```
228
+
229
+ ## Testing
230
+
231
+ ```typescript
232
+ import { renderRouter, screen } from "expo-router/testing-library";
233
+
234
+ it("navigates correctly", async () => {
235
+ renderRouter(
236
+ {
237
+ index: () => <Home />,
238
+ "user/[id]": () => <User />,
239
+ },
240
+ { initialUrl: "/" }
241
+ );
242
+
243
+ expect(screen).toHavePathname("/");
244
+ });
245
+ ```
246
+
247
+ ### Test Matchers
248
+
249
+ - `expect(screen).toHavePathname("/path")`
250
+ - `expect(screen).toHavePathnameWithParams("/path?q=test")`
251
+ - `expect(screen).toHaveSegments(["[id]"])`
252
+ - `expect(screen).useLocalSearchParams({ id: "123" })`
253
+
254
+ ## Hooks Reference
255
+
256
+ | Hook | Purpose |
257
+ | ----------------------------- | ----------------------- |
258
+ | `useRouter()` | Imperative navigation |
259
+ | `useLocalSearchParams()` | Current route params |
260
+ | `useGlobalSearchParams()` | All URL params |
261
+ | `useSegments()` | Current route segments |
262
+ | `usePathname()` | Current pathname |
263
+ | `useNavigation()` | React Navigation object |
264
+ | `useFocusEffect()` | Run effect on focus |
265
+ | `useNavigationContainerRef()` | Navigation ref |
266
+
267
+ ## URL Scheme Configuration
268
+
269
+ ```json
270
+ // app.json
271
+ {
272
+ "expo": {
273
+ "scheme": "myapp",
274
+ "web": {
275
+ "bundler": "metro"
276
+ }
277
+ }
278
+ }
279
+ ```
280
+
281
+ ## Sources
282
+
283
+ - [Introduction to Expo Router](https://docs.expo.dev/router/introduction/)
284
+ - [Core Concepts](https://docs.expo.dev/router/basics/core-concepts/)
285
+ - [Router Notation](https://docs.expo.dev/router/basics/notation/)
286
+ - [Navigation Layouts](https://docs.expo.dev/router/basics/layout/)
287
+ - [Navigation](https://docs.expo.dev/router/basics/navigation/)
288
+ - [Common Patterns](https://docs.expo.dev/router/basics/common-navigation-patterns/)
289
+ - [Authentication](https://docs.expo.dev/router/advanced/authentication/)
290
+ - [Testing](https://docs.expo.dev/router/reference/testing/)