@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.
- package/LICENSE +21 -0
- package/README.md +867 -0
- package/all/copy-overwrite/.claude/README.md +205 -0
- package/all/copy-overwrite/.claude/agents/agent-architect.md +311 -0
- package/all/copy-overwrite/.claude/agents/codebase-analyzer.md +146 -0
- package/all/copy-overwrite/.claude/agents/codebase-locator.md +125 -0
- package/all/copy-overwrite/.claude/agents/codebase-pattern-finder.md +237 -0
- package/all/copy-overwrite/.claude/agents/git-history-analyzer.md +183 -0
- package/all/copy-overwrite/.claude/agents/hooks-expert.md +74 -0
- package/all/copy-overwrite/.claude/agents/skill-evaluator.md +246 -0
- package/all/copy-overwrite/.claude/agents/slash-command-architect.md +87 -0
- package/all/copy-overwrite/.claude/agents/web-search-researcher.md +112 -0
- package/all/copy-overwrite/.claude/commands/git/commit-and-submit-pr.md +8 -0
- package/all/copy-overwrite/.claude/commands/git/commit.md +44 -0
- package/all/copy-overwrite/.claude/commands/git/prune.md +34 -0
- package/all/copy-overwrite/.claude/commands/git/submit-pr.md +50 -0
- package/all/copy-overwrite/.claude/commands/jira/create.md +50 -0
- package/all/copy-overwrite/.claude/commands/jira/verify.md +34 -0
- package/all/copy-overwrite/.claude/commands/project/archive.md +8 -0
- package/all/copy-overwrite/.claude/commands/project/bootstrap.md +49 -0
- package/all/copy-overwrite/.claude/commands/project/complete-task.md +7 -0
- package/all/copy-overwrite/.claude/commands/project/debrief.md +65 -0
- package/all/copy-overwrite/.claude/commands/project/execute.md +94 -0
- package/all/copy-overwrite/.claude/commands/project/implement.md +42 -0
- package/all/copy-overwrite/.claude/commands/project/local-code-review.md +88 -0
- package/all/copy-overwrite/.claude/commands/project/lower-code-complexity.md +74 -0
- package/all/copy-overwrite/.claude/commands/project/plan.md +314 -0
- package/all/copy-overwrite/.claude/commands/project/research.md +248 -0
- package/all/copy-overwrite/.claude/commands/project/review.md +63 -0
- package/all/copy-overwrite/.claude/commands/project/setup.md +19 -0
- package/all/copy-overwrite/.claude/commands/project/verify.md +38 -0
- package/all/copy-overwrite/.claude/commands/pull-request/review.md +12 -0
- package/all/copy-overwrite/.claude/commands/rules/format-md.md +72 -0
- package/all/copy-overwrite/.claude/commands/sonarqube/check.md +6 -0
- package/all/copy-overwrite/.claude/commands/sonarqube/fix.md +3 -0
- package/all/copy-overwrite/.claude/hooks/README.md +301 -0
- package/all/copy-overwrite/.claude/hooks/notify-ntfy.sh +181 -0
- package/all/copy-overwrite/.claude/settings.json +41 -0
- package/all/copy-overwrite/.claude/settings.local.json.example +14 -0
- package/all/copy-overwrite/.claude/skills/coding-philosophy/SKILL.md +405 -0
- package/all/copy-overwrite/.claude/skills/coding-philosophy/references/function-structure.md +416 -0
- package/all/copy-overwrite/.claude/skills/coding-philosophy/references/immutable-patterns.md +316 -0
- package/all/copy-overwrite/.claude/skills/prompt-complexity-scorer/SKILL.md +118 -0
- package/all/copy-overwrite/.claude/skills/skill-creator/LICENSE.txt +202 -0
- package/all/copy-overwrite/.claude/skills/skill-creator/SKILL.md +210 -0
- package/all/copy-overwrite/.claude/skills/skill-creator/scripts/__pycache__/quick_validate.cpython-312.pyc +0 -0
- package/all/copy-overwrite/.claude/skills/skill-creator/scripts/init_skill.py +303 -0
- package/all/copy-overwrite/.claude/skills/skill-creator/scripts/package_skill.py +110 -0
- package/all/copy-overwrite/.claude/skills/skill-creator/scripts/quick_validate.py +65 -0
- package/all/copy-overwrite/CLAUDE.md +77 -0
- package/all/copy-overwrite/HUMAN.md +17 -0
- package/all/copy-overwrite/specs/.keep +0 -0
- package/all/create-only/PROJECT_RULES.md +0 -0
- package/cdk/merge/package.json +20 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +107 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/prompts.d.ts +45 -0
- package/dist/cli/prompts.d.ts.map +1 -0
- package/dist/cli/prompts.js +58 -0
- package/dist/cli/prompts.js.map +1 -0
- package/dist/core/config.d.ts +73 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +36 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +4 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/lisa.d.ts +81 -0
- package/dist/core/lisa.d.ts.map +1 -0
- package/dist/core/lisa.js +459 -0
- package/dist/core/lisa.js.map +1 -0
- package/dist/core/manifest.d.ts +58 -0
- package/dist/core/manifest.d.ts.map +1 -0
- package/dist/core/manifest.js +104 -0
- package/dist/core/manifest.js.map +1 -0
- package/dist/detection/detector.interface.d.ts +15 -0
- package/dist/detection/detector.interface.d.ts.map +1 -0
- package/dist/detection/detector.interface.js +2 -0
- package/dist/detection/detector.interface.js.map +1 -0
- package/dist/detection/detectors/cdk.d.ts +10 -0
- package/dist/detection/detectors/cdk.d.ts.map +1 -0
- package/dist/detection/detectors/cdk.js +34 -0
- package/dist/detection/detectors/cdk.js.map +1 -0
- package/dist/detection/detectors/expo.d.ts +10 -0
- package/dist/detection/detectors/expo.d.ts.map +1 -0
- package/dist/detection/detectors/expo.js +30 -0
- package/dist/detection/detectors/expo.js.map +1 -0
- package/dist/detection/detectors/nestjs.d.ts +10 -0
- package/dist/detection/detectors/nestjs.d.ts.map +1 -0
- package/dist/detection/detectors/nestjs.js +34 -0
- package/dist/detection/detectors/nestjs.js.map +1 -0
- package/dist/detection/detectors/npm-package.d.ts +13 -0
- package/dist/detection/detectors/npm-package.d.ts.map +1 -0
- package/dist/detection/detectors/npm-package.js +30 -0
- package/dist/detection/detectors/npm-package.js.map +1 -0
- package/dist/detection/detectors/typescript.d.ts +10 -0
- package/dist/detection/detectors/typescript.d.ts.map +1 -0
- package/dist/detection/detectors/typescript.js +25 -0
- package/dist/detection/detectors/typescript.js.map +1 -0
- package/dist/detection/index.d.ts +24 -0
- package/dist/detection/index.d.ts.map +1 -0
- package/dist/detection/index.js +57 -0
- package/dist/detection/index.js.map +1 -0
- package/dist/errors/index.d.ts +69 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +110 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/logging/console-logger.d.ts +12 -0
- package/dist/logging/console-logger.d.ts.map +1 -0
- package/dist/logging/console-logger.js +22 -0
- package/dist/logging/console-logger.js.map +1 -0
- package/dist/logging/index.d.ts +4 -0
- package/dist/logging/index.d.ts.map +1 -0
- package/dist/logging/index.js +3 -0
- package/dist/logging/index.js.map +1 -0
- package/dist/logging/logger.interface.d.ts +20 -0
- package/dist/logging/logger.interface.d.ts.map +1 -0
- package/dist/logging/logger.interface.js +2 -0
- package/dist/logging/logger.interface.js.map +1 -0
- package/dist/logging/silent-logger.d.ts +12 -0
- package/dist/logging/silent-logger.d.ts.map +1 -0
- package/dist/logging/silent-logger.js +21 -0
- package/dist/logging/silent-logger.js.map +1 -0
- package/dist/strategies/copy-contents.d.ts +14 -0
- package/dist/strategies/copy-contents.d.ts.map +1 -0
- package/dist/strategies/copy-contents.js +69 -0
- package/dist/strategies/copy-contents.js.map +1 -0
- package/dist/strategies/copy-overwrite.d.ts +14 -0
- package/dist/strategies/copy-overwrite.d.ts.map +1 -0
- package/dist/strategies/copy-overwrite.js +47 -0
- package/dist/strategies/copy-overwrite.js.map +1 -0
- package/dist/strategies/create-only.d.ts +13 -0
- package/dist/strategies/create-only.d.ts.map +1 -0
- package/dist/strategies/create-only.js +30 -0
- package/dist/strategies/create-only.js.map +1 -0
- package/dist/strategies/index.d.ts +31 -0
- package/dist/strategies/index.d.ts.map +1 -0
- package/dist/strategies/index.js +52 -0
- package/dist/strategies/index.js.map +1 -0
- package/dist/strategies/merge.d.ts +13 -0
- package/dist/strategies/merge.d.ts.map +1 -0
- package/dist/strategies/merge.js +60 -0
- package/dist/strategies/merge.js.map +1 -0
- package/dist/strategies/strategy.interface.d.ts +31 -0
- package/dist/strategies/strategy.interface.d.ts.map +1 -0
- package/dist/strategies/strategy.interface.js +2 -0
- package/dist/strategies/strategy.interface.js.map +1 -0
- package/dist/transaction/backup.d.ts +38 -0
- package/dist/transaction/backup.d.ts.map +1 -0
- package/dist/transaction/backup.js +97 -0
- package/dist/transaction/backup.js.map +1 -0
- package/dist/transaction/index.d.ts +4 -0
- package/dist/transaction/index.d.ts.map +1 -0
- package/dist/transaction/index.js +3 -0
- package/dist/transaction/index.js.map +1 -0
- package/dist/transaction/transaction.d.ts +34 -0
- package/dist/transaction/transaction.d.ts.map +1 -0
- package/dist/transaction/transaction.js +68 -0
- package/dist/transaction/transaction.js.map +1 -0
- package/dist/utils/file-operations.d.ts +29 -0
- package/dist/utils/file-operations.d.ts.map +1 -0
- package/dist/utils/file-operations.js +84 -0
- package/dist/utils/file-operations.js.map +1 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +4 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/json-utils.d.ts +22 -0
- package/dist/utils/json-utils.d.ts.map +1 -0
- package/dist/utils/json-utils.js +57 -0
- package/dist/utils/json-utils.js.map +1 -0
- package/dist/utils/path-utils.d.ts +21 -0
- package/dist/utils/path-utils.d.ts.map +1 -0
- package/dist/utils/path-utils.js +35 -0
- package/dist/utils/path-utils.js.map +1 -0
- package/eslint-plugin-code-organization/README.md +149 -0
- package/eslint-plugin-code-organization/__tests__/enforce-statement-order.test.js +468 -0
- package/eslint-plugin-code-organization/index.js +23 -0
- package/eslint-plugin-code-organization/package.json +10 -0
- package/eslint-plugin-code-organization/rules/enforce-statement-order.js +157 -0
- package/expo/copy-overwrite/.claude/skills/apollo-client/SKILL.md +238 -0
- package/expo/copy-overwrite/.claude/skills/apollo-client/references/mutation-patterns.md +360 -0
- package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/SKILL.md +360 -0
- package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/references/atomic-levels.md +417 -0
- package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/references/folder-structure.md +257 -0
- package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/references/gluestack-mapping.md +233 -0
- package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/scripts/validate_atomic_structure.py +327 -0
- package/expo/copy-overwrite/.claude/skills/container-view-pattern/SKILL.md +299 -0
- package/expo/copy-overwrite/.claude/skills/container-view-pattern/references/examples.md +749 -0
- package/expo/copy-overwrite/.claude/skills/container-view-pattern/references/patterns.md +318 -0
- package/expo/copy-overwrite/.claude/skills/container-view-pattern/scripts/create_component.py +198 -0
- package/expo/copy-overwrite/.claude/skills/container-view-pattern/scripts/validate_component.py +207 -0
- package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/SKILL.md +268 -0
- package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/references/common-issues.md +619 -0
- package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/references/file-extensions.md +340 -0
- package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/references/platform-api.md +276 -0
- package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/scripts/validate_cross_platform.py +414 -0
- package/expo/copy-overwrite/.claude/skills/directory-structure/SKILL.md +202 -0
- package/expo/copy-overwrite/.claude/skills/directory-structure/scripts/validate_structure.py +443 -0
- package/expo/copy-overwrite/.claude/skills/expo-env-config/SKILL.md +309 -0
- package/expo/copy-overwrite/.claude/skills/expo-env-config/references/validation-patterns.md +417 -0
- package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/SKILL.md +431 -0
- package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/references/official-docs.md +290 -0
- package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/scripts/generate-route.py +169 -0
- package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/SKILL.md +411 -0
- package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/references/color-tokens.md +343 -0
- package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/references/component-mapping.md +307 -0
- package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/references/spacing-scale.md +300 -0
- package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/scripts/validate_styling.py +354 -0
- package/expo/copy-overwrite/.claude/skills/local-state/SKILL.md +362 -0
- package/expo/copy-overwrite/.claude/skills/local-state/references/async-storage.md +505 -0
- package/expo/copy-overwrite/.claude/skills/local-state/references/persistence-patterns.md +711 -0
- package/expo/copy-overwrite/.claude/skills/local-state/references/reactive-variables.md +446 -0
- package/expo/copy-overwrite/.claude/skills/playwright-selectors/SKILL.md +223 -0
- package/expo/copy-overwrite/.claude/skills/testing-library/SKILL.md +319 -0
- package/expo/copy-overwrite/.claude/skills/testing-library/references/async-patterns.md +420 -0
- package/expo/copy-overwrite/.claude/skills/testing-library/references/expo-router-testing.md +556 -0
- package/expo/copy-overwrite/.claude/skills/testing-library/references/mocking-patterns.md +590 -0
- package/expo/copy-overwrite/.claude/skills/testing-library/references/query-priority.md +291 -0
- package/expo/copy-overwrite/.easignore.extra +2 -0
- package/expo/copy-overwrite/.mcp.json +33 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/README.md +234 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/plugin-index.test.js +84 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/require-memo-in-view.test.js +196 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/single-component-per-file.test.js +289 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/index.js +32 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/package.json +10 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/rules/enforce-component-structure.js +230 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/rules/no-return-in-view.js +91 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/rules/require-memo-in-view.js +178 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/rules/single-component-per-file.js +238 -0
- package/expo/copy-overwrite/eslint-plugin-ui-standards/README.md +260 -0
- package/expo/copy-overwrite/eslint-plugin-ui-standards/index.js +29 -0
- package/expo/copy-overwrite/eslint-plugin-ui-standards/package.json +10 -0
- package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-classname-outside-ui.js +51 -0
- package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-direct-rn-imports.js +55 -0
- package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-inline-styles.js +73 -0
- package/expo/copy-overwrite/eslint.config.mjs +560 -0
- package/expo/copy-overwrite/lighthouserc.js +194 -0
- package/expo/create-only/lighthouserc-config.json +28 -0
- package/expo/merge/package.json +132 -0
- package/lisa.sh +35 -0
- package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/SKILL.md +176 -0
- package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/advanced-features.md +527 -0
- package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/project-patterns.md +483 -0
- package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/quick-start.md +257 -0
- package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/resolvers-mutations.md +413 -0
- package/nestjs/copy-overwrite/.claude/skills/nestjs-graphql/references/types-scalars.md +513 -0
- package/nestjs/copy-overwrite/.claude/skills/nestjs-rules/SKILL.md +536 -0
- package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/SKILL.md +275 -0
- package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/references/configuration-patterns.md +487 -0
- package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/references/entity-patterns.md +450 -0
- package/nestjs/copy-overwrite/.claude/skills/typeorm-patterns/references/observability-patterns.md +536 -0
- package/nestjs/merge/package.json +75 -0
- package/package.json +124 -0
- package/typescript/copy-contents/.husky/commit-msg +91 -0
- package/typescript/copy-contents/.husky/pre-commit +96 -0
- package/typescript/copy-contents/.husky/pre-push +211 -0
- package/typescript/copy-overwrite/.claude/hooks/format-on-edit.sh +74 -0
- package/typescript/copy-overwrite/.claude/hooks/install_pkgs.sh +59 -0
- package/typescript/copy-overwrite/.claude/hooks/lint-on-edit.sh +103 -0
- package/typescript/copy-overwrite/.claude/skills/jsdoc-best-practices/SKILL.md +388 -0
- package/typescript/copy-overwrite/.github/README.md +455 -0
- package/typescript/copy-overwrite/.github/dependabot.yml +40 -0
- package/typescript/copy-overwrite/.github/k6/BROWSER_TESTING_NOTE.md +129 -0
- package/typescript/copy-overwrite/.github/k6/INTEGRATION_GUIDE.md +354 -0
- package/typescript/copy-overwrite/.github/k6/README.md +386 -0
- package/typescript/copy-overwrite/.github/k6/SCENARIO_SELECTION_GUIDE.md +264 -0
- package/typescript/copy-overwrite/.github/k6/examples/customer-deploy-integration.yml +115 -0
- package/typescript/copy-overwrite/.github/k6/examples/data-driven-test.js +268 -0
- package/typescript/copy-overwrite/.github/k6/scenarios/load.js +142 -0
- package/typescript/copy-overwrite/.github/k6/scenarios/load.json +27 -0
- package/typescript/copy-overwrite/.github/k6/scenarios/smoke.js +26 -0
- package/typescript/copy-overwrite/.github/k6/scenarios/smoke.json +20 -0
- package/typescript/copy-overwrite/.github/k6/scenarios/soak.js +244 -0
- package/typescript/copy-overwrite/.github/k6/scenarios/soak.json +29 -0
- package/typescript/copy-overwrite/.github/k6/scenarios/spike.js +180 -0
- package/typescript/copy-overwrite/.github/k6/scenarios/spike.json +32 -0
- package/typescript/copy-overwrite/.github/k6/scenarios/stress.js +206 -0
- package/typescript/copy-overwrite/.github/k6/scenarios/stress.json +38 -0
- package/typescript/copy-overwrite/.github/k6/scripts/api-test.js +452 -0
- package/typescript/copy-overwrite/.github/k6/scripts/default-test.js +185 -0
- package/typescript/copy-overwrite/.github/k6/thresholds/normal.json +30 -0
- package/typescript/copy-overwrite/.github/k6/thresholds/relaxed.json +21 -0
- package/typescript/copy-overwrite/.github/k6/thresholds/strict.json +29 -0
- package/typescript/copy-overwrite/.github/workflows/build.yml +72 -0
- package/typescript/copy-overwrite/.github/workflows/ci.yml +49 -0
- package/typescript/copy-overwrite/.github/workflows/claude.yml +51 -0
- package/typescript/copy-overwrite/.github/workflows/create-github-issue-on-failure.yml +113 -0
- package/typescript/copy-overwrite/.github/workflows/create-jira-issue-on-failure.yml +195 -0
- package/typescript/copy-overwrite/.github/workflows/create-sentry-issue-on-failure.yml +267 -0
- package/typescript/copy-overwrite/.github/workflows/deploy.yml +228 -0
- package/typescript/copy-overwrite/.github/workflows/k6-load-test-README.md +230 -0
- package/typescript/copy-overwrite/.github/workflows/lighthouse.yml +68 -0
- package/typescript/copy-overwrite/.github/workflows/load-test.yml +282 -0
- package/typescript/copy-overwrite/.github/workflows/quality.yml +1737 -0
- package/typescript/copy-overwrite/.github/workflows/release.yml +1599 -0
- package/typescript/copy-overwrite/.gitleaksignore +28 -0
- package/typescript/copy-overwrite/.nvmrc +1 -0
- package/typescript/copy-overwrite/.prettierignore +23 -0
- package/typescript/copy-overwrite/.prettierrc.json +22 -0
- package/typescript/copy-overwrite/.versionrc +42 -0
- package/typescript/copy-overwrite/.yamllint +20 -0
- package/typescript/copy-overwrite/commitlint.config.js +11 -0
- package/typescript/copy-overwrite/eslint-plugin-code-organization/README.md +149 -0
- package/typescript/copy-overwrite/eslint-plugin-code-organization/__tests__/enforce-statement-order.test.js +468 -0
- package/typescript/copy-overwrite/eslint-plugin-code-organization/index.js +23 -0
- package/typescript/copy-overwrite/eslint-plugin-code-organization/package.json +10 -0
- package/typescript/copy-overwrite/eslint-plugin-code-organization/rules/enforce-statement-order.js +157 -0
- package/typescript/copy-overwrite/eslint.config.mjs +390 -0
- package/typescript/copy-overwrite/eslint.ignore.config.json +57 -0
- package/typescript/copy-overwrite/eslint.thresholds.config.json +5 -0
- package/typescript/github-rulesets/base.json +106 -0
- package/typescript/merge/.claude/settings.json +28 -0
- 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/
|
package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/references/official-docs.md
ADDED
|
@@ -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/)
|