@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,318 @@
|
|
|
1
|
+
# Container/View Pattern - Common Patterns and Anti-Patterns
|
|
2
|
+
|
|
3
|
+
## Correct Patterns
|
|
4
|
+
|
|
5
|
+
### 1. State in Container, Props in View
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
// Container - owns state
|
|
9
|
+
const PlayerCardContainer = ({ playerId }: Props) => {
|
|
10
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
11
|
+
const { data } = usePlayerQuery({ variables: { id: playerId } });
|
|
12
|
+
|
|
13
|
+
const handleToggle = useCallback(() => {
|
|
14
|
+
setIsExpanded(prev => !prev);
|
|
15
|
+
}, []);
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<PlayerCardView
|
|
19
|
+
player={data?.player}
|
|
20
|
+
isExpanded={isExpanded}
|
|
21
|
+
onToggle={handleToggle}
|
|
22
|
+
/>
|
|
23
|
+
);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// View - receives props only
|
|
27
|
+
const PlayerCardView = ({ player, isExpanded, onToggle }: ViewProps) => (
|
|
28
|
+
<Box>
|
|
29
|
+
<Pressable onPress={onToggle}>
|
|
30
|
+
<Text>{player?.name}</Text>
|
|
31
|
+
</Pressable>
|
|
32
|
+
{isExpanded && <PlayerDetails player={player} />}
|
|
33
|
+
</Box>
|
|
34
|
+
);
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### 2. Memoized Computed Values
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
// Container - memoize all derived data
|
|
41
|
+
const ListContainer = ({ items }: Props) => {
|
|
42
|
+
const sortedItems = useMemo(
|
|
43
|
+
() => [...items].sort((a, b) => a.name.localeCompare(b.name)),
|
|
44
|
+
[items]
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const itemCount = useMemo(() => items.length, [items]);
|
|
48
|
+
|
|
49
|
+
return <ListView items={sortedItems} count={itemCount} />;
|
|
50
|
+
};
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 3. Memoized Callbacks
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
// Container - wrap all handlers
|
|
57
|
+
const FormContainer = ({ onSubmit }: Props) => {
|
|
58
|
+
const [value, setValue] = useState("");
|
|
59
|
+
|
|
60
|
+
const handleChange = useCallback((text: string) => {
|
|
61
|
+
setValue(text);
|
|
62
|
+
}, []);
|
|
63
|
+
|
|
64
|
+
const handleSubmit = useCallback(() => {
|
|
65
|
+
onSubmit(value);
|
|
66
|
+
}, [onSubmit, value]);
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<FormView value={value} onChange={handleChange} onSubmit={handleSubmit} />
|
|
70
|
+
);
|
|
71
|
+
};
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### 4. Loading/Error/Empty States
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
// Container - determines which state to show
|
|
78
|
+
const DataListContainer = () => {
|
|
79
|
+
const { data, loading, error } = useDataQuery();
|
|
80
|
+
|
|
81
|
+
const isEmpty = useMemo(
|
|
82
|
+
() => !loading && !error && (!data?.items || data.items.length === 0),
|
|
83
|
+
[loading, error, data?.items]
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<DataListView
|
|
88
|
+
items={data?.items ?? []}
|
|
89
|
+
isLoading={loading}
|
|
90
|
+
hasError={!!error}
|
|
91
|
+
isEmpty={isEmpty}
|
|
92
|
+
/>
|
|
93
|
+
);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// View - renders based on state props
|
|
97
|
+
const DataListView = ({ items, isLoading, hasError, isEmpty }: Props) => (
|
|
98
|
+
<Box>
|
|
99
|
+
{isLoading && <LoadingSpinner />}
|
|
100
|
+
{hasError && <ErrorMessage />}
|
|
101
|
+
{isEmpty && <EmptyState />}
|
|
102
|
+
{!isLoading && !hasError && !isEmpty && (
|
|
103
|
+
<FlashList data={items} renderItem={renderItem} />
|
|
104
|
+
)}
|
|
105
|
+
</Box>
|
|
106
|
+
);
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### 5. Safe Area Insets
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
// Container - gets insets from hook
|
|
113
|
+
const ScreenContainer = () => {
|
|
114
|
+
const { bottom: bottomInset } = useSafeAreaInsets();
|
|
115
|
+
|
|
116
|
+
return <ScreenView bottomInset={bottomInset} />;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// View - applies inset as style
|
|
120
|
+
const ScreenView = ({ bottomInset }: { readonly bottomInset: number }) => (
|
|
121
|
+
<Box style={{ paddingBottom: bottomInset }}>
|
|
122
|
+
<Content />
|
|
123
|
+
</Box>
|
|
124
|
+
);
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 6. Platform-Specific Logic
|
|
128
|
+
|
|
129
|
+
```tsx
|
|
130
|
+
// Container - handles platform logic
|
|
131
|
+
const ActionContainer = ({ onDelete }: Props) => {
|
|
132
|
+
const handleDelete = useCallback(() => {
|
|
133
|
+
if (Platform.OS === "web") {
|
|
134
|
+
if (window.confirm("Delete this item?")) {
|
|
135
|
+
onDelete();
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
Alert.alert("Confirm", "Delete this item?", [
|
|
139
|
+
{ text: "Cancel", style: "cancel" },
|
|
140
|
+
{ text: "Delete", onPress: onDelete, style: "destructive" },
|
|
141
|
+
]);
|
|
142
|
+
}
|
|
143
|
+
}, [onDelete]);
|
|
144
|
+
|
|
145
|
+
return <ActionView onDelete={handleDelete} />;
|
|
146
|
+
};
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Anti-Patterns to Avoid
|
|
150
|
+
|
|
151
|
+
### 1. Hooks in View
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
// WRONG - hooks in View
|
|
155
|
+
const BadView = ({ playerId }: Props) => {
|
|
156
|
+
const { data } = usePlayerQuery({ variables: { id: playerId } }); // WRONG
|
|
157
|
+
const [isOpen, setIsOpen] = useState(false); // WRONG
|
|
158
|
+
|
|
159
|
+
return <Box>{data?.name}</Box>;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
// CORRECT - hooks in Container
|
|
163
|
+
const GoodContainer = ({ playerId }: Props) => {
|
|
164
|
+
const { data } = usePlayerQuery({ variables: { id: playerId } });
|
|
165
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
166
|
+
|
|
167
|
+
return <GoodView player={data} isOpen={isOpen} />;
|
|
168
|
+
};
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### 2. Block Body in View
|
|
172
|
+
|
|
173
|
+
```tsx
|
|
174
|
+
// WRONG - block body with return
|
|
175
|
+
const BadView = ({ items }: Props) => {
|
|
176
|
+
return (
|
|
177
|
+
<Box>
|
|
178
|
+
{items.map(item => (
|
|
179
|
+
<Item key={item.id} />
|
|
180
|
+
))}
|
|
181
|
+
</Box>
|
|
182
|
+
);
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
// CORRECT - arrow shorthand
|
|
186
|
+
const GoodView = ({ items }: Props) => (
|
|
187
|
+
<Box>
|
|
188
|
+
{items.map(item => (
|
|
189
|
+
<Item key={item.id} />
|
|
190
|
+
))}
|
|
191
|
+
</Box>
|
|
192
|
+
);
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### 3. Inline Functions
|
|
196
|
+
|
|
197
|
+
```tsx
|
|
198
|
+
// WRONG - inline function creates new reference
|
|
199
|
+
const BadContainer = () => (
|
|
200
|
+
<View onClick={() => console.log("clicked")} /> // WRONG
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
// CORRECT - memoized callback
|
|
204
|
+
const GoodContainer = () => {
|
|
205
|
+
const handleClick = useCallback(() => {
|
|
206
|
+
console.log("clicked");
|
|
207
|
+
}, []);
|
|
208
|
+
|
|
209
|
+
return <View onClick={handleClick} />;
|
|
210
|
+
};
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### 4. Inline Objects
|
|
214
|
+
|
|
215
|
+
```tsx
|
|
216
|
+
// WRONG - inline object creates new reference
|
|
217
|
+
const BadContainer = ({ user }: Props) => (
|
|
218
|
+
<View style={{ padding: 10 }} user={{ name: user.name }} /> // WRONG
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
// CORRECT - memoized values
|
|
222
|
+
const GoodContainer = ({ user }: Props) => {
|
|
223
|
+
const style = useMemo(() => ({ padding: 10 }), []);
|
|
224
|
+
const userData = useMemo(() => ({ name: user.name }), [user.name]);
|
|
225
|
+
|
|
226
|
+
return <View style={style} user={userData} />;
|
|
227
|
+
};
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### 5. Logic in View
|
|
231
|
+
|
|
232
|
+
```tsx
|
|
233
|
+
// WRONG - logic in View
|
|
234
|
+
const BadView = ({ items, filter }: Props) => {
|
|
235
|
+
const filtered = items.filter(i => i.type === filter); // WRONG
|
|
236
|
+
|
|
237
|
+
return <List items={filtered} />;
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
// CORRECT - pre-filtered in Container
|
|
241
|
+
const GoodContainer = ({ items, filter }: Props) => {
|
|
242
|
+
const filtered = useMemo(
|
|
243
|
+
() => items.filter(i => i.type === filter),
|
|
244
|
+
[items, filter]
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
return <GoodView items={filtered} />;
|
|
248
|
+
};
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### 6. Missing memo Wrapper
|
|
252
|
+
|
|
253
|
+
```tsx
|
|
254
|
+
// WRONG - no memo
|
|
255
|
+
const BadView = ({ data }: Props) => <Box>{data}</Box>;
|
|
256
|
+
export default BadView;
|
|
257
|
+
|
|
258
|
+
// CORRECT - wrapped with memo
|
|
259
|
+
const GoodView = ({ data }: Props) => <Box>{data}</Box>;
|
|
260
|
+
GoodView.displayName = "GoodView";
|
|
261
|
+
export default memo(GoodView);
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### 7. Early Returns in View
|
|
265
|
+
|
|
266
|
+
```tsx
|
|
267
|
+
// WRONG - early return in View
|
|
268
|
+
const BadView = ({ data, isLoading }: Props) => {
|
|
269
|
+
if (isLoading) return <Spinner />;
|
|
270
|
+
if (!data) return <Empty />;
|
|
271
|
+
return <Content data={data} />;
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
// CORRECT - ternary in View (or handle in Container)
|
|
275
|
+
const GoodView = ({ data, isLoading, isEmpty }: Props) => (
|
|
276
|
+
<Box>
|
|
277
|
+
{isLoading ? <Spinner /> : isEmpty ? <Empty /> : <Content data={data} />}
|
|
278
|
+
</Box>
|
|
279
|
+
);
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
## Complex View Extraction
|
|
283
|
+
|
|
284
|
+
When Views become too complex, extract helper functions:
|
|
285
|
+
|
|
286
|
+
```tsx
|
|
287
|
+
/**
|
|
288
|
+
* Renders loading state skeleton.
|
|
289
|
+
* @param props - Helper props
|
|
290
|
+
* @param props.isDark - Dark mode flag
|
|
291
|
+
*/
|
|
292
|
+
function renderLoading(props: { readonly isDark: boolean }) {
|
|
293
|
+
return <Skeleton className={props.isDark ? "bg-gray-800" : "bg-gray-200"} />;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Renders empty state message.
|
|
298
|
+
* @param props - Helper props
|
|
299
|
+
* @param props.message - Message to display
|
|
300
|
+
*/
|
|
301
|
+
function renderEmpty(props: { readonly message: string }) {
|
|
302
|
+
return (
|
|
303
|
+
<Box className="items-center justify-center p-8">
|
|
304
|
+
<Text>{props.message}</Text>
|
|
305
|
+
</Box>
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const ComplexView = ({ isLoading, isEmpty, isDark, items }: Props) => (
|
|
310
|
+
<Box>
|
|
311
|
+
{isLoading && renderLoading({ isDark })}
|
|
312
|
+
{!isLoading && isEmpty && renderEmpty({ message: "No items found" })}
|
|
313
|
+
{!isLoading && !isEmpty && (
|
|
314
|
+
<FlashList data={items} renderItem={renderItem} />
|
|
315
|
+
)}
|
|
316
|
+
</Box>
|
|
317
|
+
);
|
|
318
|
+
```
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Create a new component following the Container/View pattern.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
python3 create_component.py <type> <name> [feature]
|
|
7
|
+
|
|
8
|
+
Types:
|
|
9
|
+
global-component - Creates in components/<name>/
|
|
10
|
+
feature-component - Creates in features/<feature>/components/<name>/
|
|
11
|
+
global-screen - Creates in screens/<name>/
|
|
12
|
+
feature-screen - Creates in features/<feature>/screens/<name>/
|
|
13
|
+
|
|
14
|
+
Examples:
|
|
15
|
+
python3 create_component.py global-component PlayerCard
|
|
16
|
+
python3 create_component.py feature-component PlayerCard player-kanban
|
|
17
|
+
python3 create_component.py global-screen Settings
|
|
18
|
+
python3 create_component.py feature-screen Main dashboard
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import os
|
|
22
|
+
import sys
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
CONTAINER_TEMPLATE = '''import {{ useCallback, useMemo, useState }} from "react";
|
|
27
|
+
|
|
28
|
+
import {name}View from "./{name}View";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Props for the {name} component.
|
|
32
|
+
*/
|
|
33
|
+
interface {name}Props {{
|
|
34
|
+
// Define props here
|
|
35
|
+
}}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Container component that manages state and logic for {name}.
|
|
39
|
+
* @param props - Component properties
|
|
40
|
+
*/
|
|
41
|
+
const {name}Container = (props: {name}Props) => {{
|
|
42
|
+
// 1. Variables, state, useMemo, useCallback
|
|
43
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
44
|
+
|
|
45
|
+
const computedValue = useMemo(() => {{
|
|
46
|
+
return null;
|
|
47
|
+
}}, []);
|
|
48
|
+
|
|
49
|
+
const handleAction = useCallback(() => {{
|
|
50
|
+
// Handle action
|
|
51
|
+
}}, []);
|
|
52
|
+
|
|
53
|
+
// 2. useEffect hooks
|
|
54
|
+
// useEffect(() => {{}}, []);
|
|
55
|
+
|
|
56
|
+
// 3. Return View
|
|
57
|
+
return (
|
|
58
|
+
<{name}View
|
|
59
|
+
isLoading={{isLoading}}
|
|
60
|
+
onAction={{handleAction}}
|
|
61
|
+
/>
|
|
62
|
+
);
|
|
63
|
+
}};
|
|
64
|
+
|
|
65
|
+
export default {name}Container;
|
|
66
|
+
'''
|
|
67
|
+
|
|
68
|
+
VIEW_TEMPLATE = '''import {{ memo }} from "react";
|
|
69
|
+
|
|
70
|
+
import {{ Box }} from "@/components/ui/box";
|
|
71
|
+
import {{ Text }} from "@/components/ui/text";
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Props for the {name}View component.
|
|
75
|
+
*/
|
|
76
|
+
interface {name}ViewProps {{
|
|
77
|
+
readonly isLoading: boolean;
|
|
78
|
+
readonly onAction: () => void;
|
|
79
|
+
}}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* View component that renders the {name} UI.
|
|
83
|
+
* @param props - Component properties
|
|
84
|
+
* @param props.isLoading - Loading state indicator
|
|
85
|
+
* @param props.onAction - Action handler callback
|
|
86
|
+
*/
|
|
87
|
+
const {name}View = ({{
|
|
88
|
+
isLoading,
|
|
89
|
+
onAction,
|
|
90
|
+
}}: {name}ViewProps) => (
|
|
91
|
+
<Box testID="{test_id}.CONTAINER">
|
|
92
|
+
{{isLoading ? (
|
|
93
|
+
<Text>Loading...</Text>
|
|
94
|
+
) : (
|
|
95
|
+
<Text>{name} Component</Text>
|
|
96
|
+
)}}
|
|
97
|
+
</Box>
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
{name}View.displayName = "{name}View";
|
|
101
|
+
|
|
102
|
+
export default memo({name}View);
|
|
103
|
+
'''
|
|
104
|
+
|
|
105
|
+
INDEX_TEMPLATE = '''export {{ default }} from "./{name}Container";
|
|
106
|
+
'''
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def create_component(component_type: str, name: str, feature: str = None) -> bool:
|
|
110
|
+
"""
|
|
111
|
+
Create a new component with Container/View pattern.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
component_type: Type of component (global-component, feature-component, etc.)
|
|
115
|
+
name: Component name in PascalCase
|
|
116
|
+
feature: Feature name (required for feature-component and feature-screen)
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
True if successful, False otherwise
|
|
120
|
+
"""
|
|
121
|
+
# Validate component name is PascalCase
|
|
122
|
+
if not name[0].isupper():
|
|
123
|
+
print(f"Error: Component name '{name}' must be PascalCase (start with uppercase)")
|
|
124
|
+
return False
|
|
125
|
+
|
|
126
|
+
# Determine target directory
|
|
127
|
+
if component_type == "global-component":
|
|
128
|
+
target_dir = Path("components") / name
|
|
129
|
+
test_id = name.upper()
|
|
130
|
+
elif component_type == "feature-component":
|
|
131
|
+
if not feature:
|
|
132
|
+
print("Error: feature-component requires a feature name")
|
|
133
|
+
return False
|
|
134
|
+
feature_dir = Path("features") / feature
|
|
135
|
+
if not feature_dir.exists():
|
|
136
|
+
print(f"Error: Feature '{feature}' does not exist at {feature_dir}")
|
|
137
|
+
return False
|
|
138
|
+
target_dir = feature_dir / "components" / name
|
|
139
|
+
test_id = f"{feature.upper().replace('-', '_')}.{name.upper()}"
|
|
140
|
+
elif component_type == "global-screen":
|
|
141
|
+
target_dir = Path("screens") / name
|
|
142
|
+
test_id = f"SCREEN.{name.upper()}"
|
|
143
|
+
elif component_type == "feature-screen":
|
|
144
|
+
if not feature:
|
|
145
|
+
print("Error: feature-screen requires a feature name")
|
|
146
|
+
return False
|
|
147
|
+
feature_dir = Path("features") / feature
|
|
148
|
+
if not feature_dir.exists():
|
|
149
|
+
print(f"Error: Feature '{feature}' does not exist at {feature_dir}")
|
|
150
|
+
return False
|
|
151
|
+
target_dir = feature_dir / "screens" / name
|
|
152
|
+
test_id = f"{feature.upper().replace('-', '_')}.SCREEN.{name.upper()}"
|
|
153
|
+
else:
|
|
154
|
+
print(f"Error: Unknown component type '{component_type}'")
|
|
155
|
+
print("Valid types: global-component, feature-component, global-screen, feature-screen")
|
|
156
|
+
return False
|
|
157
|
+
|
|
158
|
+
# Check if component already exists
|
|
159
|
+
if target_dir.exists():
|
|
160
|
+
print(f"Error: Component already exists at {target_dir}")
|
|
161
|
+
return False
|
|
162
|
+
|
|
163
|
+
# Create directory
|
|
164
|
+
target_dir.mkdir(parents=True, exist_ok=True)
|
|
165
|
+
|
|
166
|
+
# Create files
|
|
167
|
+
container_content = CONTAINER_TEMPLATE.format(name=name)
|
|
168
|
+
view_content = VIEW_TEMPLATE.format(name=name, test_id=test_id)
|
|
169
|
+
index_content = INDEX_TEMPLATE.format(name=name)
|
|
170
|
+
|
|
171
|
+
(target_dir / f"{name}Container.tsx").write_text(container_content)
|
|
172
|
+
(target_dir / f"{name}View.tsx").write_text(view_content)
|
|
173
|
+
(target_dir / "index.tsx").write_text(index_content)
|
|
174
|
+
|
|
175
|
+
print(f"Created component at {target_dir}/")
|
|
176
|
+
print(f" - {name}Container.tsx")
|
|
177
|
+
print(f" - {name}View.tsx")
|
|
178
|
+
print(f" - index.tsx")
|
|
179
|
+
|
|
180
|
+
return True
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def main():
|
|
184
|
+
"""Main entry point."""
|
|
185
|
+
if len(sys.argv) < 3:
|
|
186
|
+
print(__doc__)
|
|
187
|
+
sys.exit(1)
|
|
188
|
+
|
|
189
|
+
component_type = sys.argv[1]
|
|
190
|
+
name = sys.argv[2]
|
|
191
|
+
feature = sys.argv[3] if len(sys.argv) > 3 else None
|
|
192
|
+
|
|
193
|
+
success = create_component(component_type, name, feature)
|
|
194
|
+
sys.exit(0 if success else 1)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
if __name__ == "__main__":
|
|
198
|
+
main()
|