@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,340 @@
|
|
|
1
|
+
# Platform-Specific File Extensions
|
|
2
|
+
|
|
3
|
+
This reference documents Metro bundler's platform-specific file extension resolution for Expo/React Native projects.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Available Extensions](#available-extensions)
|
|
8
|
+
- [Resolution Order](#resolution-order)
|
|
9
|
+
- [Usage in app/ Directory](#usage-in-app-directory)
|
|
10
|
+
- [Usage Outside app/ Directory](#usage-outside-app-directory)
|
|
11
|
+
- [Patterns and Examples](#patterns-and-examples)
|
|
12
|
+
- [TypeScript Considerations](#typescript-considerations)
|
|
13
|
+
|
|
14
|
+
## Available Extensions
|
|
15
|
+
|
|
16
|
+
| Extension | Platforms | Description |
|
|
17
|
+
| -------------- | ------------- | ---------------------------- |
|
|
18
|
+
| `.ios.tsx` | iOS only | iPhone and iPad |
|
|
19
|
+
| `.android.tsx` | Android only | Android phones and tablets |
|
|
20
|
+
| `.native.tsx` | iOS + Android | Shared native implementation |
|
|
21
|
+
| `.web.tsx` | Web only | Browser environments |
|
|
22
|
+
| `.tsx` | All platforms | Universal fallback |
|
|
23
|
+
|
|
24
|
+
## Resolution Order
|
|
25
|
+
|
|
26
|
+
Metro resolves files in this priority order:
|
|
27
|
+
|
|
28
|
+
### For iOS Build:
|
|
29
|
+
|
|
30
|
+
1. `Component.ios.tsx`
|
|
31
|
+
2. `Component.native.tsx`
|
|
32
|
+
3. `Component.tsx`
|
|
33
|
+
|
|
34
|
+
### For Android Build:
|
|
35
|
+
|
|
36
|
+
1. `Component.android.tsx`
|
|
37
|
+
2. `Component.native.tsx`
|
|
38
|
+
3. `Component.tsx`
|
|
39
|
+
|
|
40
|
+
### For Web Build:
|
|
41
|
+
|
|
42
|
+
1. `Component.web.tsx`
|
|
43
|
+
2. `Component.tsx`
|
|
44
|
+
|
|
45
|
+
**Note:** `.native.tsx` is NOT resolved on web. Web only looks for `.web.tsx` or the base `.tsx`.
|
|
46
|
+
|
|
47
|
+
## Usage in app/ Directory
|
|
48
|
+
|
|
49
|
+
### Requirement: Base Version Required
|
|
50
|
+
|
|
51
|
+
Platform-specific files in Expo Router's `app/` directory **must have a corresponding base version** to ensure route universality for deep linking.
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
app/
|
|
55
|
+
├── _layout.tsx ✅ Required base
|
|
56
|
+
├── _layout.web.tsx ✅ Web override
|
|
57
|
+
├── index.tsx ✅ Required base
|
|
58
|
+
├── settings.tsx ✅ Required base
|
|
59
|
+
├── settings.native.tsx ✅ Native override
|
|
60
|
+
└── about.web.tsx ❌ Missing base! Will cause issues
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Valid Combinations
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
# Layout with web override
|
|
67
|
+
_layout.tsx + _layout.web.tsx ✅
|
|
68
|
+
|
|
69
|
+
# Screen with native override
|
|
70
|
+
profile.tsx + profile.native.tsx ✅
|
|
71
|
+
|
|
72
|
+
# Screen with all platforms
|
|
73
|
+
dashboard.tsx ✅ (base only)
|
|
74
|
+
dashboard.tsx + dashboard.web.tsx ✅
|
|
75
|
+
dashboard.tsx + dashboard.ios.tsx + dashboard.android.tsx ✅
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Invalid Combinations
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
# Missing base file
|
|
82
|
+
_layout.web.tsx (no _layout.tsx) ❌
|
|
83
|
+
|
|
84
|
+
# Orphaned platform file
|
|
85
|
+
settings.ios.tsx (no settings.tsx) ❌
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Usage Outside app/ Directory
|
|
89
|
+
|
|
90
|
+
Files outside the `app/` directory have more flexibility and do not require base versions.
|
|
91
|
+
|
|
92
|
+
### Components Directory
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
components/
|
|
96
|
+
├── Button/
|
|
97
|
+
│ ├── ButtonContainer.tsx # Shared container logic
|
|
98
|
+
│ ├── ButtonView.tsx # Default/fallback view
|
|
99
|
+
│ ├── ButtonView.web.tsx # Web-specific view
|
|
100
|
+
│ └── index.tsx # Exports container
|
|
101
|
+
│
|
|
102
|
+
├── DatePicker/
|
|
103
|
+
│ ├── DatePickerContainer.tsx
|
|
104
|
+
│ ├── DatePickerView.native.tsx # iOS + Android
|
|
105
|
+
│ ├── DatePickerView.web.tsx # Web
|
|
106
|
+
│ └── index.tsx
|
|
107
|
+
│
|
|
108
|
+
└── Camera/
|
|
109
|
+
├── CameraContainer.tsx
|
|
110
|
+
├── CameraView.ios.tsx # iOS only
|
|
111
|
+
├── CameraView.android.tsx # Android only
|
|
112
|
+
├── CameraView.web.tsx # Web only
|
|
113
|
+
└── index.tsx
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Hooks Directory
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
hooks/
|
|
120
|
+
├── useCamera.ts # Shared hook interface
|
|
121
|
+
├── useCamera.native.ts # Native implementation
|
|
122
|
+
├── useCamera.web.ts # Web implementation
|
|
123
|
+
│
|
|
124
|
+
├── useHaptics.ts # Base (may be no-op on web)
|
|
125
|
+
└── useHaptics.native.ts # Native with haptic feedback
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Utils Directory
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
utils/
|
|
132
|
+
├── storage.ts # Interface
|
|
133
|
+
├── storage.native.ts # AsyncStorage implementation
|
|
134
|
+
└── storage.web.ts # localStorage implementation
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Patterns and Examples
|
|
138
|
+
|
|
139
|
+
### Pattern 1: Re-export from app/ Directory
|
|
140
|
+
|
|
141
|
+
When you need platform-specific routes, create the implementation outside `app/` and re-export:
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
// components/about/index.tsx
|
|
145
|
+
// Platform-specific implementations live here
|
|
146
|
+
|
|
147
|
+
// components/about/AboutScreen.tsx
|
|
148
|
+
export const AboutScreen = () => <CommonAboutContent />;
|
|
149
|
+
|
|
150
|
+
// components/about/AboutScreen.web.tsx
|
|
151
|
+
export const AboutScreen = () => <WebSpecificAboutContent />;
|
|
152
|
+
|
|
153
|
+
// app/about.tsx
|
|
154
|
+
export { AboutScreen as default } from "@/components/about/AboutScreen";
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Pattern 2: Shared Container, Platform Views
|
|
158
|
+
|
|
159
|
+
Following the Container/View pattern with platform-specific views:
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
// components/FileUploader/FileUploaderContainer.tsx
|
|
163
|
+
import FileUploaderView from "./FileUploaderView";
|
|
164
|
+
|
|
165
|
+
const FileUploaderContainer = () => {
|
|
166
|
+
const [file, setFile] = useState<File | null>(null);
|
|
167
|
+
|
|
168
|
+
const handleUpload = useCallback(async () => {
|
|
169
|
+
// Shared upload logic
|
|
170
|
+
}, [file]);
|
|
171
|
+
|
|
172
|
+
return <FileUploaderView file={file} onUpload={handleUpload} />;
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
// components/FileUploader/FileUploaderView.native.tsx
|
|
176
|
+
const FileUploaderView = ({ file, onUpload }: Props) => (
|
|
177
|
+
<TouchableOpacity onPress={onUpload}>
|
|
178
|
+
<Text>Upload from Device</Text>
|
|
179
|
+
</TouchableOpacity>
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
// components/FileUploader/FileUploaderView.web.tsx
|
|
183
|
+
const FileUploaderView = ({ file, onUpload }: Props) => (
|
|
184
|
+
<input type="file" onChange={onUpload} />
|
|
185
|
+
);
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Pattern 3: Platform-Specific Hooks
|
|
189
|
+
|
|
190
|
+
```tsx
|
|
191
|
+
// hooks/useClipboard.ts (interface/types)
|
|
192
|
+
export interface UseClipboardResult {
|
|
193
|
+
readonly copy: (text: string) => Promise<void>;
|
|
194
|
+
readonly paste: () => Promise<string>;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// hooks/useClipboard.native.ts
|
|
198
|
+
import * as Clipboard from "expo-clipboard";
|
|
199
|
+
|
|
200
|
+
export const useClipboard = (): UseClipboardResult => ({
|
|
201
|
+
copy: async text => await Clipboard.setStringAsync(text),
|
|
202
|
+
paste: async () => await Clipboard.getStringAsync(),
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// hooks/useClipboard.web.ts
|
|
206
|
+
export const useClipboard = (): UseClipboardResult => ({
|
|
207
|
+
copy: async text => await navigator.clipboard.writeText(text),
|
|
208
|
+
paste: async () => await navigator.clipboard.readText(),
|
|
209
|
+
});
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Pattern 4: Conditional Feature Files
|
|
213
|
+
|
|
214
|
+
```tsx
|
|
215
|
+
// features/ar-viewer/index.ts (base export)
|
|
216
|
+
export { ARViewer } from "./ARViewer";
|
|
217
|
+
|
|
218
|
+
// features/ar-viewer/ARViewer.tsx (fallback)
|
|
219
|
+
export const ARViewer = () => <Text>AR is not supported on this platform</Text>;
|
|
220
|
+
|
|
221
|
+
// features/ar-viewer/ARViewer.native.tsx
|
|
222
|
+
import { ARView } from "react-native-ar";
|
|
223
|
+
|
|
224
|
+
export const ARViewer = () => <ARView />;
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## TypeScript Considerations
|
|
228
|
+
|
|
229
|
+
### Shared Type Definitions
|
|
230
|
+
|
|
231
|
+
Create a shared types file that all platform versions import:
|
|
232
|
+
|
|
233
|
+
```tsx
|
|
234
|
+
// components/Modal/types.ts
|
|
235
|
+
export interface ModalProps {
|
|
236
|
+
readonly visible: boolean;
|
|
237
|
+
readonly onClose: () => void;
|
|
238
|
+
readonly children: React.ReactNode;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// components/Modal/ModalView.native.tsx
|
|
242
|
+
import type { ModalProps } from "./types";
|
|
243
|
+
|
|
244
|
+
const ModalView = ({ visible, onClose, children }: ModalProps) => (
|
|
245
|
+
// Native implementation
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
// components/Modal/ModalView.web.tsx
|
|
249
|
+
import type { ModalProps } from "./types";
|
|
250
|
+
|
|
251
|
+
const ModalView = ({ visible, onClose, children }: ModalProps) => (
|
|
252
|
+
// Web implementation
|
|
253
|
+
);
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Module Declaration for Web APIs
|
|
257
|
+
|
|
258
|
+
When using web-only libraries:
|
|
259
|
+
|
|
260
|
+
```tsx
|
|
261
|
+
// types/dom-to-image.d.ts
|
|
262
|
+
declare module "dom-to-image" {
|
|
263
|
+
export function toJpeg(
|
|
264
|
+
node: HTMLElement | null,
|
|
265
|
+
options?: { quality?: number; width?: number; height?: number }
|
|
266
|
+
): Promise<string>;
|
|
267
|
+
|
|
268
|
+
export function toPng(
|
|
269
|
+
node: HTMLElement | null,
|
|
270
|
+
options?: { width?: number; height?: number }
|
|
271
|
+
): Promise<string>;
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Platform-Specific Type Guards
|
|
276
|
+
|
|
277
|
+
```tsx
|
|
278
|
+
// types/platform.ts
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Type for web-specific window properties.
|
|
282
|
+
*/
|
|
283
|
+
declare global {
|
|
284
|
+
interface Window {
|
|
285
|
+
// Add web-specific properties
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Ensures code only runs on web.
|
|
291
|
+
*/
|
|
292
|
+
export const assertWeb = (): void => {
|
|
293
|
+
if (Platform.OS !== "web") {
|
|
294
|
+
throw new Error("This code should only run on web");
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
## Common Mistakes
|
|
300
|
+
|
|
301
|
+
### 1. Missing Base File in app/
|
|
302
|
+
|
|
303
|
+
```
|
|
304
|
+
❌ app/settings.web.tsx (without app/settings.tsx)
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
**Fix:** Always create base file first, then add platform overrides.
|
|
308
|
+
|
|
309
|
+
### 2. Expecting .native.tsx on Web
|
|
310
|
+
|
|
311
|
+
```tsx
|
|
312
|
+
// components/Button.native.tsx exists
|
|
313
|
+
// Expecting it to work on web - it won't!
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
**Fix:** Either create `.web.tsx` or ensure `.tsx` base exists.
|
|
317
|
+
|
|
318
|
+
### 3. Inconsistent Exports
|
|
319
|
+
|
|
320
|
+
```tsx
|
|
321
|
+
// Button.native.tsx
|
|
322
|
+
export const Button = () => {};
|
|
323
|
+
|
|
324
|
+
// Button.web.tsx
|
|
325
|
+
export default function Button() {} // Different export style!
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
**Fix:** Use consistent export patterns across all platform versions.
|
|
329
|
+
|
|
330
|
+
### 4. Different Props Between Platforms
|
|
331
|
+
|
|
332
|
+
```tsx
|
|
333
|
+
// Modal.native.tsx
|
|
334
|
+
const Modal = ({ visible, animationType }: Props) => {};
|
|
335
|
+
|
|
336
|
+
// Modal.web.tsx
|
|
337
|
+
const Modal = ({ isOpen }: Props) => {}; // Different prop names!
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
**Fix:** Use shared type definitions to ensure consistent interfaces.
|
package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/references/platform-api.md
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
# Platform Module API Reference
|
|
2
|
+
|
|
3
|
+
This reference documents the React Native Platform module for cross-platform development.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Platform.OS](#platformos)
|
|
8
|
+
- [Platform.select()](#platformselect)
|
|
9
|
+
- [Platform.Version](#platformversion)
|
|
10
|
+
- [Platform.isPad / Platform.isTV](#platformispad--platformistv)
|
|
11
|
+
- [Best Practices](#best-practices)
|
|
12
|
+
|
|
13
|
+
## Platform.OS
|
|
14
|
+
|
|
15
|
+
Returns the current platform as a string.
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { Platform } from "react-native";
|
|
19
|
+
|
|
20
|
+
// Values: "ios" | "android" | "web" | "windows" | "macos"
|
|
21
|
+
const currentPlatform = Platform.OS;
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Usage Patterns
|
|
25
|
+
|
|
26
|
+
**Simple Conditional:**
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
if (Platform.OS === "web") {
|
|
30
|
+
// Web-only code
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Ternary Expression:**
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
const buttonText = Platform.OS === "ios" ? "Done" : "OK";
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Switch Statement (for complex logic):**
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
switch (Platform.OS) {
|
|
44
|
+
case "ios":
|
|
45
|
+
return handleIOS();
|
|
46
|
+
case "android":
|
|
47
|
+
return handleAndroid();
|
|
48
|
+
case "web":
|
|
49
|
+
return handleWeb();
|
|
50
|
+
default:
|
|
51
|
+
return handleDefault();
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Platform.select()
|
|
56
|
+
|
|
57
|
+
Returns platform-specific values from an object. Preferred for style objects and configuration.
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
import { Platform, StyleSheet } from "react-native";
|
|
61
|
+
|
|
62
|
+
const styles = StyleSheet.create({
|
|
63
|
+
container: {
|
|
64
|
+
flex: 1,
|
|
65
|
+
...Platform.select({
|
|
66
|
+
ios: {
|
|
67
|
+
backgroundColor: "systemBackground",
|
|
68
|
+
},
|
|
69
|
+
android: {
|
|
70
|
+
backgroundColor: "#FFFFFF",
|
|
71
|
+
},
|
|
72
|
+
web: {
|
|
73
|
+
backgroundColor: "#FAFAFA",
|
|
74
|
+
},
|
|
75
|
+
default: {
|
|
76
|
+
backgroundColor: "#FFFFFF",
|
|
77
|
+
},
|
|
78
|
+
}),
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Key Features
|
|
84
|
+
|
|
85
|
+
- **default key**: Used when no platform-specific value matches
|
|
86
|
+
- **native key**: Shorthand for iOS + Android (not web)
|
|
87
|
+
- **Returns undefined**: If platform not found and no default
|
|
88
|
+
|
|
89
|
+
```tsx
|
|
90
|
+
// Using 'native' for iOS + Android
|
|
91
|
+
Platform.select({
|
|
92
|
+
native: { padding: 20 },
|
|
93
|
+
web: { padding: 16 },
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Using 'default' as fallback
|
|
97
|
+
Platform.select({
|
|
98
|
+
ios: "iOS specific",
|
|
99
|
+
default: "Everything else",
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Platform.Version
|
|
104
|
+
|
|
105
|
+
Returns the platform version.
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
// iOS: Returns iOS version number (e.g., 17.0)
|
|
109
|
+
// Android: Returns API level (e.g., 33)
|
|
110
|
+
// Web: Returns undefined
|
|
111
|
+
|
|
112
|
+
if (Platform.OS === "android" && Platform.Version >= 33) {
|
|
113
|
+
// Android 13+ specific code
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (Platform.OS === "ios" && parseInt(Platform.Version, 10) >= 17) {
|
|
117
|
+
// iOS 17+ specific code
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Platform.isPad / Platform.isTV
|
|
122
|
+
|
|
123
|
+
Boolean values for device type detection (iOS only).
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
if (Platform.OS === "ios" && Platform.isPad) {
|
|
127
|
+
// iPad-specific layout
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (Platform.OS === "ios" && Platform.isTV) {
|
|
131
|
+
// Apple TV specific code
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Best Practices
|
|
136
|
+
|
|
137
|
+
### 1. Always Handle All Platforms
|
|
138
|
+
|
|
139
|
+
```tsx
|
|
140
|
+
// BAD: Only checks for web, undefined behavior on other platforms
|
|
141
|
+
if (Platform.OS === "web") {
|
|
142
|
+
doWebThing();
|
|
143
|
+
}
|
|
144
|
+
// What happens on iOS/Android?
|
|
145
|
+
|
|
146
|
+
// GOOD: Explicit handling or default
|
|
147
|
+
if (Platform.OS === "web") {
|
|
148
|
+
doWebThing();
|
|
149
|
+
} else {
|
|
150
|
+
doNativeThing();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// BETTER: Use Platform.select with default
|
|
154
|
+
const behavior = Platform.select({
|
|
155
|
+
web: doWebThing,
|
|
156
|
+
default: doNativeThing,
|
|
157
|
+
});
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### 2. Prefer Platform.select for Objects
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
// BAD: Verbose conditionals
|
|
164
|
+
const shadowStyle =
|
|
165
|
+
Platform.OS === "ios"
|
|
166
|
+
? { shadowColor: "#000" }
|
|
167
|
+
: Platform.OS === "android"
|
|
168
|
+
? { elevation: 4 }
|
|
169
|
+
: { boxShadow: "0 2px 4px rgba(0,0,0,0.1)" };
|
|
170
|
+
|
|
171
|
+
// GOOD: Clean Platform.select
|
|
172
|
+
const shadowStyle = Platform.select({
|
|
173
|
+
ios: { shadowColor: "#000", shadowOpacity: 0.25 },
|
|
174
|
+
android: { elevation: 4 },
|
|
175
|
+
web: { boxShadow: "0 2px 4px rgba(0,0,0,0.1)" },
|
|
176
|
+
});
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### 3. Use Type Guards for TypeScript
|
|
180
|
+
|
|
181
|
+
```tsx
|
|
182
|
+
/**
|
|
183
|
+
* Type guard for web platform.
|
|
184
|
+
*/
|
|
185
|
+
const isWeb = Platform.OS === "web";
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Type guard for native platforms.
|
|
189
|
+
*/
|
|
190
|
+
const isNative = Platform.OS === "ios" || Platform.OS === "android";
|
|
191
|
+
|
|
192
|
+
// Usage
|
|
193
|
+
if (isWeb) {
|
|
194
|
+
// TypeScript knows this is web context
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### 4. Extract Platform Logic to Utilities
|
|
199
|
+
|
|
200
|
+
```tsx
|
|
201
|
+
// utils/platform.ts
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Checks if the current platform is web.
|
|
205
|
+
*/
|
|
206
|
+
export const isWeb = () => Platform.OS === "web";
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Checks if the current platform is native (iOS or Android).
|
|
210
|
+
*/
|
|
211
|
+
export const isNative = () => Platform.OS === "ios" || Platform.OS === "android";
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Checks if the current platform is iOS.
|
|
215
|
+
*/
|
|
216
|
+
export const isIOS = () => Platform.OS === "ios";
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Checks if the current platform is Android.
|
|
220
|
+
*/
|
|
221
|
+
export const isAndroid = () => Platform.OS === "android";
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Returns platform-specific value.
|
|
225
|
+
*/
|
|
226
|
+
export const platformValue = <T>(config: {
|
|
227
|
+
readonly ios?: T;
|
|
228
|
+
readonly android?: T;
|
|
229
|
+
readonly web?: T;
|
|
230
|
+
readonly native?: T;
|
|
231
|
+
readonly default?: T;
|
|
232
|
+
}): T | undefined => Platform.select(config);
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### 5. Avoid Nested Platform Checks
|
|
236
|
+
|
|
237
|
+
```tsx
|
|
238
|
+
// BAD: Nested and hard to read
|
|
239
|
+
if (Platform.OS === "ios") {
|
|
240
|
+
if (Platform.Version >= 17) {
|
|
241
|
+
// ...
|
|
242
|
+
}
|
|
243
|
+
} else if (Platform.OS === "android") {
|
|
244
|
+
if (Platform.Version >= 33) {
|
|
245
|
+
// ...
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// GOOD: Extract to named functions
|
|
250
|
+
const supportsNewFeature = () => {
|
|
251
|
+
if (Platform.OS === "ios") return parseInt(Platform.Version, 10) >= 17;
|
|
252
|
+
if (Platform.OS === "android") return Platform.Version >= 33;
|
|
253
|
+
return true; // Web always supports
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
if (supportsNewFeature()) {
|
|
257
|
+
// ...
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### 6. Document Platform-Specific Behavior
|
|
262
|
+
|
|
263
|
+
```tsx
|
|
264
|
+
/**
|
|
265
|
+
* Saves an image to the device.
|
|
266
|
+
*
|
|
267
|
+
* @remarks
|
|
268
|
+
* - iOS/Android: Saves to device media library
|
|
269
|
+
* - Web: Triggers browser download
|
|
270
|
+
*
|
|
271
|
+
* @param imageRef - Reference to the view to capture
|
|
272
|
+
*/
|
|
273
|
+
const saveImage = async (imageRef: React.RefObject<View>) => {
|
|
274
|
+
// Implementation
|
|
275
|
+
};
|
|
276
|
+
```
|