@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,416 @@
|
|
|
1
|
+
# Function Structure Reference
|
|
2
|
+
|
|
3
|
+
This document defines the required ordering of statements within functions, hooks, and components.
|
|
4
|
+
|
|
5
|
+
## The Three Sections
|
|
6
|
+
|
|
7
|
+
Every function body follows this order:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
1. VARIABLES & STATE
|
|
11
|
+
- Variable declarations (const)
|
|
12
|
+
- useState hooks
|
|
13
|
+
- useQuery/useMutation hooks
|
|
14
|
+
- useMemo computations
|
|
15
|
+
- useCallback handlers
|
|
16
|
+
- Regular function definitions
|
|
17
|
+
|
|
18
|
+
2. SIDE EFFECTS
|
|
19
|
+
- useEffect hooks
|
|
20
|
+
- Function calls with no return value
|
|
21
|
+
|
|
22
|
+
3. RETURN
|
|
23
|
+
- Return statement (always last)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## ESLint Enforcement
|
|
27
|
+
|
|
28
|
+
This ordering is enforced by the `code-organization/enforce-hook-order` ESLint rule.
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
Expected order: variables/state/functions → useEffects → return statement
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Hook Structure
|
|
35
|
+
|
|
36
|
+
### Complete Example
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
export const usePlayerData = (playerId: string): UsePlayerDataReturn => {
|
|
40
|
+
// ========================================
|
|
41
|
+
// 1. VARIABLES & STATE
|
|
42
|
+
// ========================================
|
|
43
|
+
|
|
44
|
+
// UI state
|
|
45
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
46
|
+
const [searchQuery, setSearchQuery] = useState("");
|
|
47
|
+
|
|
48
|
+
// External hooks
|
|
49
|
+
const { colors } = useTheme();
|
|
50
|
+
const router = useRouter();
|
|
51
|
+
|
|
52
|
+
// GraphQL queries
|
|
53
|
+
const { data, loading, refetch } = useQuery(GetPlayerDocument, {
|
|
54
|
+
variables: { playerId },
|
|
55
|
+
skip: !playerId,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Derived state with useMemo
|
|
59
|
+
const playerName = useMemo(
|
|
60
|
+
() => data?.player?.name ?? "Unknown",
|
|
61
|
+
[data?.player?.name]
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const filteredStats = useMemo(
|
|
65
|
+
() => data?.stats?.filter(s => s.name.includes(searchQuery)) ?? [],
|
|
66
|
+
[data?.stats, searchQuery]
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
// Handlers with useCallback
|
|
70
|
+
const handleRefresh = useCallback(() => {
|
|
71
|
+
refetch();
|
|
72
|
+
}, [refetch]);
|
|
73
|
+
|
|
74
|
+
const handleNavigate = useCallback(() => {
|
|
75
|
+
if (!playerId) {
|
|
76
|
+
console.error("Cannot navigate: player ID is missing");
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
router.push(`/players/${playerId}/details`);
|
|
80
|
+
}, [playerId, router]);
|
|
81
|
+
|
|
82
|
+
// Helper functions (defined as const arrow functions or regular functions)
|
|
83
|
+
const formatPlayerLabel = (name: string, position: string): string => {
|
|
84
|
+
return `${name} (${position})`;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// ========================================
|
|
88
|
+
// 2. SIDE EFFECTS
|
|
89
|
+
// ========================================
|
|
90
|
+
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
console.log("Player loaded:", playerName);
|
|
93
|
+
}, [playerName]);
|
|
94
|
+
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
if (data?.player) {
|
|
97
|
+
trackAnalytics("player_viewed", { playerId });
|
|
98
|
+
}
|
|
99
|
+
}, [data?.player, playerId]);
|
|
100
|
+
|
|
101
|
+
// ========================================
|
|
102
|
+
// 3. RETURN
|
|
103
|
+
// ========================================
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
playerName,
|
|
107
|
+
filteredStats,
|
|
108
|
+
isExpanded,
|
|
109
|
+
loading,
|
|
110
|
+
handleRefresh,
|
|
111
|
+
handleNavigate,
|
|
112
|
+
formatPlayerLabel,
|
|
113
|
+
setIsExpanded,
|
|
114
|
+
setSearchQuery,
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Container Component Structure
|
|
120
|
+
|
|
121
|
+
### Complete Example
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
const PlayerCardContainer: React.FC<PlayerCardContainerProps> = ({
|
|
125
|
+
playerId,
|
|
126
|
+
onPress,
|
|
127
|
+
showDetails = false,
|
|
128
|
+
}) => {
|
|
129
|
+
// ========================================
|
|
130
|
+
// 1. VARIABLES & STATE
|
|
131
|
+
// ========================================
|
|
132
|
+
|
|
133
|
+
// Theme and styling
|
|
134
|
+
const { colors } = useTheme();
|
|
135
|
+
const { width } = useWindowDimensions();
|
|
136
|
+
const isDark = useColorScheme() === "dark";
|
|
137
|
+
|
|
138
|
+
// Local state
|
|
139
|
+
const [isHovered, setIsHovered] = useState(false);
|
|
140
|
+
|
|
141
|
+
// Data fetching
|
|
142
|
+
const { data, loading, error } = useQuery(GetPlayerDocument, {
|
|
143
|
+
variables: { playerId },
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Derived values
|
|
147
|
+
const player = data?.player;
|
|
148
|
+
|
|
149
|
+
const cardStyle = useMemo(
|
|
150
|
+
() => ({
|
|
151
|
+
backgroundColor: isDark ? colors.cardDark : colors.cardLight,
|
|
152
|
+
maxWidth: width > 768 ? 400 : width - 32,
|
|
153
|
+
}),
|
|
154
|
+
[isDark, colors, width]
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
const formattedStats = useMemo(
|
|
158
|
+
() => player?.stats?.map(s => ({
|
|
159
|
+
...s,
|
|
160
|
+
displayValue: formatStatValue(s.value, s.type),
|
|
161
|
+
})) ?? [],
|
|
162
|
+
[player?.stats]
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
// Handlers
|
|
166
|
+
const handlePress = useCallback(() => {
|
|
167
|
+
if (!playerId) {
|
|
168
|
+
console.error("Cannot navigate: player ID is missing");
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
onPress?.(playerId);
|
|
172
|
+
}, [playerId, onPress]);
|
|
173
|
+
|
|
174
|
+
const handleHoverIn = useCallback(() => {
|
|
175
|
+
setIsHovered(true);
|
|
176
|
+
}, []);
|
|
177
|
+
|
|
178
|
+
const handleHoverOut = useCallback(() => {
|
|
179
|
+
setIsHovered(false);
|
|
180
|
+
}, []);
|
|
181
|
+
|
|
182
|
+
// ========================================
|
|
183
|
+
// 2. SIDE EFFECTS
|
|
184
|
+
// ========================================
|
|
185
|
+
|
|
186
|
+
useEffect(() => {
|
|
187
|
+
if (player) {
|
|
188
|
+
console.log("Player card rendered:", player.name);
|
|
189
|
+
}
|
|
190
|
+
}, [player]);
|
|
191
|
+
|
|
192
|
+
// ========================================
|
|
193
|
+
// 3. RETURN
|
|
194
|
+
// ========================================
|
|
195
|
+
|
|
196
|
+
// Early returns are acceptable after side effects
|
|
197
|
+
if (loading) {
|
|
198
|
+
return <LoadingSkeleton />;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (error) {
|
|
202
|
+
return <ErrorCard message={error.message} />;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return (
|
|
206
|
+
<PlayerCardView
|
|
207
|
+
player={player}
|
|
208
|
+
stats={formattedStats}
|
|
209
|
+
cardStyle={cardStyle}
|
|
210
|
+
isHovered={isHovered}
|
|
211
|
+
showDetails={showDetails}
|
|
212
|
+
onPress={handlePress}
|
|
213
|
+
onHoverIn={handleHoverIn}
|
|
214
|
+
onHoverOut={handleHoverOut}
|
|
215
|
+
/>
|
|
216
|
+
);
|
|
217
|
+
};
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Utility Function Structure
|
|
221
|
+
|
|
222
|
+
### Pure Function Example
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
export const calculateTeamRankings = (
|
|
226
|
+
players: readonly Player[],
|
|
227
|
+
teamId: string
|
|
228
|
+
): TeamRanking => {
|
|
229
|
+
// ========================================
|
|
230
|
+
// 1. VARIABLES & DERIVED VALUES
|
|
231
|
+
// ========================================
|
|
232
|
+
|
|
233
|
+
const validPlayers = players.filter(p => p.team && p.score != null);
|
|
234
|
+
|
|
235
|
+
const teamScores = validPlayers.reduce(
|
|
236
|
+
(acc, player) => {
|
|
237
|
+
const id = player.team.id;
|
|
238
|
+
const existing = acc[id];
|
|
239
|
+
return {
|
|
240
|
+
...acc,
|
|
241
|
+
[id]: {
|
|
242
|
+
teamId: id,
|
|
243
|
+
totalScore: (existing?.totalScore ?? 0) + player.score,
|
|
244
|
+
count: (existing?.count ?? 0) + 1,
|
|
245
|
+
},
|
|
246
|
+
};
|
|
247
|
+
},
|
|
248
|
+
{} as Record<string, { teamId: string; totalScore: number; count: number }>
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
const rankings = Object.values(teamScores).map(t => ({
|
|
252
|
+
teamId: t.teamId,
|
|
253
|
+
avgScore: t.totalScore / t.count,
|
|
254
|
+
}));
|
|
255
|
+
|
|
256
|
+
const sorted = [...rankings].sort((a, b) => b.avgScore - a.avgScore);
|
|
257
|
+
|
|
258
|
+
const targetRank = sorted.findIndex(r => r.teamId === teamId) + 1;
|
|
259
|
+
|
|
260
|
+
// ========================================
|
|
261
|
+
// 2. NO SIDE EFFECTS IN PURE FUNCTIONS
|
|
262
|
+
// ========================================
|
|
263
|
+
|
|
264
|
+
// ========================================
|
|
265
|
+
// 3. RETURN
|
|
266
|
+
// ========================================
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
rank: targetRank || null,
|
|
270
|
+
totalTeams: sorted.length,
|
|
271
|
+
avgScore: teamScores[teamId]?.totalScore / teamScores[teamId]?.count ?? 0,
|
|
272
|
+
};
|
|
273
|
+
};
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
## Helper Function Extraction
|
|
277
|
+
|
|
278
|
+
When reducing cognitive complexity, extract helper functions and place them between types and the main component.
|
|
279
|
+
|
|
280
|
+
### File Structure
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
// ========================================
|
|
284
|
+
// TYPES
|
|
285
|
+
// ========================================
|
|
286
|
+
|
|
287
|
+
interface Props {
|
|
288
|
+
readonly players: readonly Player[];
|
|
289
|
+
readonly isDark: boolean;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// ========================================
|
|
293
|
+
// HELPER FUNCTIONS
|
|
294
|
+
// ========================================
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Renders the loading skeleton state.
|
|
298
|
+
* @param props - Component properties
|
|
299
|
+
* @param props.isDark - Whether dark mode is active
|
|
300
|
+
* @returns Loading skeleton UI
|
|
301
|
+
*/
|
|
302
|
+
function renderLoadingState(props: { readonly isDark: boolean }) {
|
|
303
|
+
const { isDark } = props;
|
|
304
|
+
return <LoadingSkeleton isDark={isDark} />;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Renders the empty state when no players exist.
|
|
309
|
+
* @param props - Component properties
|
|
310
|
+
* @param props.message - Message to display
|
|
311
|
+
* @returns Empty state UI
|
|
312
|
+
*/
|
|
313
|
+
function renderEmptyState(props: { readonly message: string }) {
|
|
314
|
+
const { message } = props;
|
|
315
|
+
return <EmptyState message={message} />;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// ========================================
|
|
319
|
+
// MAIN COMPONENT
|
|
320
|
+
// ========================================
|
|
321
|
+
|
|
322
|
+
export const PlayerListView = memo(function PlayerListView({
|
|
323
|
+
players,
|
|
324
|
+
isDark,
|
|
325
|
+
}: Props) {
|
|
326
|
+
if (!players.length) {
|
|
327
|
+
return renderEmptyState({ message: "No players found" });
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return (
|
|
331
|
+
<Box>
|
|
332
|
+
{players.map(player => (
|
|
333
|
+
<PlayerCard key={player.id} player={player} />
|
|
334
|
+
))}
|
|
335
|
+
</Box>
|
|
336
|
+
);
|
|
337
|
+
});
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## Common Mistakes
|
|
341
|
+
|
|
342
|
+
### Wrong: useEffect Before useMemo
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
// WRONG
|
|
346
|
+
useEffect(() => {
|
|
347
|
+
console.log("Value:", computedValue);
|
|
348
|
+
}, [computedValue]);
|
|
349
|
+
|
|
350
|
+
const computedValue = useMemo(() => calculate(data), [data]);
|
|
351
|
+
|
|
352
|
+
// CORRECT
|
|
353
|
+
const computedValue = useMemo(() => calculate(data), [data]);
|
|
354
|
+
|
|
355
|
+
useEffect(() => {
|
|
356
|
+
console.log("Value:", computedValue);
|
|
357
|
+
}, [computedValue]);
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Wrong: Return Before useEffect
|
|
361
|
+
|
|
362
|
+
```typescript
|
|
363
|
+
// WRONG
|
|
364
|
+
const Component = () => {
|
|
365
|
+
const data = useData();
|
|
366
|
+
|
|
367
|
+
if (!data) {
|
|
368
|
+
return null; // Early return before useEffect!
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
useEffect(() => {
|
|
372
|
+
// This breaks the rules of hooks
|
|
373
|
+
}, []);
|
|
374
|
+
|
|
375
|
+
return <View />;
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
// CORRECT
|
|
379
|
+
const Component = () => {
|
|
380
|
+
const data = useData();
|
|
381
|
+
|
|
382
|
+
useEffect(() => {
|
|
383
|
+
if (data) {
|
|
384
|
+
// Handle data
|
|
385
|
+
}
|
|
386
|
+
}, [data]);
|
|
387
|
+
|
|
388
|
+
if (!data) {
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return <View />;
|
|
393
|
+
};
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
### Wrong: Handler Definition After useEffect
|
|
397
|
+
|
|
398
|
+
```typescript
|
|
399
|
+
// WRONG
|
|
400
|
+
useEffect(() => {
|
|
401
|
+
handleInit();
|
|
402
|
+
}, [handleInit]);
|
|
403
|
+
|
|
404
|
+
const handleInit = useCallback(() => {
|
|
405
|
+
// ...
|
|
406
|
+
}, []);
|
|
407
|
+
|
|
408
|
+
// CORRECT
|
|
409
|
+
const handleInit = useCallback(() => {
|
|
410
|
+
// ...
|
|
411
|
+
}, []);
|
|
412
|
+
|
|
413
|
+
useEffect(() => {
|
|
414
|
+
handleInit();
|
|
415
|
+
}, [handleInit]);
|
|
416
|
+
```
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
# Immutable Patterns Reference
|
|
2
|
+
|
|
3
|
+
This document provides comprehensive examples of immutable patterns used in this codebase.
|
|
4
|
+
|
|
5
|
+
## Building Lookup Objects with Reduce
|
|
6
|
+
|
|
7
|
+
### Basic Pattern
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
const colorMap =
|
|
11
|
+
edges?.reduce(
|
|
12
|
+
(acc, edge) => (edge.color ? { ...acc, [edge.tagId]: edge.color } : acc),
|
|
13
|
+
{} as Record<string, string>
|
|
14
|
+
) ?? {};
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### With Null Safety
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
const boardColorMap =
|
|
21
|
+
data?.edges?.reduce(
|
|
22
|
+
(acc, edge) => (edge.color ? { ...acc, [edge.tagId]: edge.color } : acc),
|
|
23
|
+
{} as Record<string, string>
|
|
24
|
+
) ?? {};
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Accumulating Multiple Properties
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
const teamGprAccumulator = validPlayers.reduce(
|
|
31
|
+
(acc, player) => {
|
|
32
|
+
const teamId = player.team?.id;
|
|
33
|
+
if (!teamId) return acc;
|
|
34
|
+
|
|
35
|
+
const existing = acc[teamId];
|
|
36
|
+
return {
|
|
37
|
+
...acc,
|
|
38
|
+
[teamId]: {
|
|
39
|
+
teamId,
|
|
40
|
+
teamName: player.team.name,
|
|
41
|
+
gprSum: (existing?.gprSum ?? 0) + player.gpr,
|
|
42
|
+
playerCount: (existing?.playerCount ?? 0) + 1,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
},
|
|
46
|
+
{} as Record<
|
|
47
|
+
string,
|
|
48
|
+
{ teamId: string; teamName: string; gprSum: number; playerCount: number }
|
|
49
|
+
>
|
|
50
|
+
);
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### With Parallel Array Lookups (using index)
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
const formattedMetricsMap = useMemo(
|
|
57
|
+
() =>
|
|
58
|
+
metrics.reduce(
|
|
59
|
+
(acc, metric, index) => ({
|
|
60
|
+
...acc,
|
|
61
|
+
[metric.displayName]: formattedMetrics[index],
|
|
62
|
+
}),
|
|
63
|
+
{} as Record<string, ICompositeMetric>
|
|
64
|
+
),
|
|
65
|
+
[metrics, formattedMetrics]
|
|
66
|
+
);
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Record vs Map
|
|
70
|
+
|
|
71
|
+
Always prefer `Record<string, T>` over `Map<string, T>` for simple lookups.
|
|
72
|
+
|
|
73
|
+
### Record Pattern (Preferred)
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
type UserNameMap = Record<string, string>;
|
|
77
|
+
const userNames: UserNameMap = { id1: "John Doe", id2: "Jane Smith" };
|
|
78
|
+
|
|
79
|
+
// Access
|
|
80
|
+
const name = userNames[userId];
|
|
81
|
+
const hasPlayer = playerId in playersOnKanban;
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Why Not Map
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
// Avoid this pattern
|
|
88
|
+
const lookup = new Map<string, User>();
|
|
89
|
+
users.forEach(u => lookup.set(u.id, u));
|
|
90
|
+
const user = lookup.get(userId);
|
|
91
|
+
|
|
92
|
+
// Use this instead
|
|
93
|
+
const lookup = users.reduce(
|
|
94
|
+
(acc, u) => ({ ...acc, [u.id]: u }),
|
|
95
|
+
{} as Record<string, User>
|
|
96
|
+
);
|
|
97
|
+
const user = lookup[userId];
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Array Transformations
|
|
101
|
+
|
|
102
|
+
### Spread Before Sort
|
|
103
|
+
|
|
104
|
+
Never sort in place. Always spread first.
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
// Correct
|
|
108
|
+
const sorted = [...Object.values(seasonData)].sort((a, b) =>
|
|
109
|
+
a.season > b.season ? 1 : -1
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
// Incorrect - mutates original
|
|
113
|
+
const sorted = seasonData.sort((a, b) => a.season - b.season);
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Filter with Boolean
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
const validPlayers = leaguePlayers.filter(
|
|
120
|
+
Boolean
|
|
121
|
+
) as readonly PlayerWithScores[];
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Map for Transformation
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
const teamGprData: readonly TeamGprData[] = Object.values(
|
|
128
|
+
teamGprAccumulator
|
|
129
|
+
).map(team => ({
|
|
130
|
+
teamId: team.teamId,
|
|
131
|
+
teamName: team.teamName,
|
|
132
|
+
averageGpr: team.playerCount > 0 ? team.gprSum / team.playerCount : 0,
|
|
133
|
+
}));
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Updating Array Item at Index
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
// Correct - creates new array
|
|
140
|
+
const updated = current.map((item, idx) => (idx === target ? newItem : item));
|
|
141
|
+
|
|
142
|
+
// Incorrect - mutation
|
|
143
|
+
current[target] = newItem;
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Adding to Array
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
// Correct
|
|
150
|
+
const withNew = [...existing, newItem];
|
|
151
|
+
|
|
152
|
+
// Incorrect
|
|
153
|
+
existing.push(newItem);
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Removing from Array
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
// Correct
|
|
160
|
+
const without = items.filter(item => item.id !== idToRemove);
|
|
161
|
+
|
|
162
|
+
// Incorrect
|
|
163
|
+
const idx = items.findIndex(item => item.id === idToRemove);
|
|
164
|
+
items.splice(idx, 1);
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Object Updates
|
|
168
|
+
|
|
169
|
+
### Spread for Updates
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
// Correct
|
|
173
|
+
const updated = { ...user, name: "New Name" };
|
|
174
|
+
|
|
175
|
+
// Incorrect
|
|
176
|
+
user.name = "New Name";
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Nested Object Updates
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
const updated = {
|
|
183
|
+
...state,
|
|
184
|
+
user: {
|
|
185
|
+
...state.user,
|
|
186
|
+
profile: {
|
|
187
|
+
...state.user.profile,
|
|
188
|
+
avatar: newAvatar,
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Conditional Property Addition
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
const result = {
|
|
198
|
+
...baseObj,
|
|
199
|
+
...(condition && { optionalProp: value }),
|
|
200
|
+
};
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Conditional Values Without Let
|
|
204
|
+
|
|
205
|
+
### Ternary for Simple Conditions
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
// Correct
|
|
209
|
+
const status = isComplete ? "done" : "pending";
|
|
210
|
+
|
|
211
|
+
// Incorrect
|
|
212
|
+
let status = "pending";
|
|
213
|
+
if (isComplete) {
|
|
214
|
+
status = "done";
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Ternary Chain for Multiple Conditions
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
const priority = score > 90 ? "high" : score > 70 ? "medium" : "low";
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Nullish Coalescing
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
const displayName = user.name ?? "Unknown User";
|
|
228
|
+
const count = existing?.count ?? 0;
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Optional Chaining with Nullish Coalescing
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
const firstName =
|
|
235
|
+
user.cognitoUser?.Attributes?.find(attr => attr.Name === "given_name")
|
|
236
|
+
?.Value ?? "";
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Apollo Reactive Variables
|
|
240
|
+
|
|
241
|
+
### Immutable Updates
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
// Direct update for simple values
|
|
245
|
+
isLoadingVar(true);
|
|
246
|
+
errorVar("Error message");
|
|
247
|
+
|
|
248
|
+
// Spread for array updates
|
|
249
|
+
messagesVar([...messagesVar(), newMessage]);
|
|
250
|
+
|
|
251
|
+
// Spread for complex updates
|
|
252
|
+
const current = messagesVar();
|
|
253
|
+
messagesVar([...current, newMessage]);
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Operation Hooks for Complex Updates
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
export const useMessageOperations = () => {
|
|
260
|
+
const addMessage = useCallback((message: IMessage) => {
|
|
261
|
+
const current = messagesVar();
|
|
262
|
+
messagesVar([...current, message]);
|
|
263
|
+
}, []);
|
|
264
|
+
|
|
265
|
+
const clearMessages = useCallback(() => {
|
|
266
|
+
messagesVar([]);
|
|
267
|
+
}, []);
|
|
268
|
+
|
|
269
|
+
return { addMessage, clearMessages };
|
|
270
|
+
};
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Readonly Types
|
|
274
|
+
|
|
275
|
+
### Function Parameters
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
export const calculateTeamGprRank = (
|
|
279
|
+
leaguePlayers: readonly (PlayerWithScores | null | undefined)[],
|
|
280
|
+
myTeamId: string | null | undefined
|
|
281
|
+
): number | null => {
|
|
282
|
+
// ...
|
|
283
|
+
};
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Apollo Cache Modifiers
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
modify({
|
|
290
|
+
fields: {
|
|
291
|
+
myField(existingData: readonly { __ref: string }[] = [], { readField }) {
|
|
292
|
+
return existingData.filter(ref => readField("id", ref) !== idToRemove);
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
});
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
## Testing Immutability
|
|
299
|
+
|
|
300
|
+
### Verify New Object Creation
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
test("should create new object on each reduce iteration", () => {
|
|
304
|
+
expect(result1).not.toBe(result2); // Different references
|
|
305
|
+
expect(result1).toEqual(result2); // Same values
|
|
306
|
+
});
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Verify Record vs Map
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
test("should return plain object, not Map", () => {
|
|
313
|
+
expect(result).toBeInstanceOf(Object);
|
|
314
|
+
expect(result).not.toBeInstanceOf(Map);
|
|
315
|
+
});
|
|
316
|
+
```
|