animation-lib-check.sh 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. #!/usr/bin/env bash
  2. # ============================================================
  3. # lib/animation-lib-check.sh — Animation library detection
  4. # Sourced by: skills/init-project, skills/onboard, agents/plugin-advisor.
  5. #
  6. # Recommends `motion` (ex-`framer-motion`, rebranded Nov 2024) for
  7. # React-family / Svelte stacks, and `motion-v` for Vue 3 / Nuxt.
  8. #
  9. # Override the project root with: ANIM_PROJECT_ROOT=/path before sourcing.
  10. # ============================================================
  11. ANIM_PROJECT_ROOT="${ANIM_PROJECT_ROOT:-$PWD}"
  12. # Match a literal dep key in package.json (deps + devDeps + peerDeps).
  13. # Args: $1 = exact dep name. Returns 0 if found, 1 otherwise. No output.
  14. _anim_has_dep() {
  15. local pkg_json="$ANIM_PROJECT_ROOT/package.json"
  16. [ -f "$pkg_json" ] || return 1
  17. # The closing escaped quote anchors the match so "react" does not
  18. # collide with "react-native" or "react-dom".
  19. grep -Eq "\"$1\"[[:space:]]*:" "$pkg_json"
  20. }
  21. # Decide whether the project can consume the motion library.
  22. # Outputs on stdout: '<status>|<package>|<reason>'
  23. # status : eligible | no
  24. # package : motion | motion-v | -
  25. # reason : short human-readable note
  26. # Returns 0 if eligible, 1 if not.
  27. detect_anim_eligibility() {
  28. local pkg_json="$ANIM_PROJECT_ROOT/package.json"
  29. if [ ! -f "$pkg_json" ]; then
  30. echo "no|-|no package.json (not a Node project)"
  31. return 1
  32. fi
  33. # React Native / Expo are excluded: motion targets the DOM, RN apps
  34. # should use react-native-reanimated instead.
  35. if _anim_has_dep "react-native" || _anim_has_dep "expo"; then
  36. echo "no|-|React Native stack — use react-native-reanimated"
  37. return 1
  38. fi
  39. if _anim_has_dep "react" || _anim_has_dep "next" \
  40. || _anim_has_dep "@remix-run/react" || _anim_has_dep "@astrojs/react"; then
  41. echo "eligible|motion|React-family stack"
  42. return 0
  43. fi
  44. if _anim_has_dep "vue" || _anim_has_dep "nuxt"; then
  45. echo "eligible|motion-v|Vue 3 / Nuxt stack"
  46. return 0
  47. fi
  48. if _anim_has_dep "svelte" || _anim_has_dep "@sveltejs/kit"; then
  49. echo "eligible|motion|Svelte stack"
  50. return 0
  51. fi
  52. if _anim_has_dep "astro"; then
  53. echo "no|-|Astro without a React/Vue/Svelte integration"
  54. return 1
  55. fi
  56. echo "no|-|no supported UI framework detected"
  57. return 1
  58. }
  59. # Look for any installed animation library.
  60. # Outputs the detected package name on stdout (empty if none).
  61. # Returns 0 if found, 1 otherwise.
  62. is_anim_lib_installed() {
  63. local pkg_json="$ANIM_PROJECT_ROOT/package.json"
  64. [ -f "$pkg_json" ] || return 1
  65. local libs=(
  66. motion
  67. motion-v
  68. framer-motion
  69. gsap
  70. "@gsap/react"
  71. lottie-react
  72. react-spring
  73. "@react-spring/web"
  74. popmotion
  75. "@formkit/auto-animate"
  76. )
  77. local lib
  78. for lib in "${libs[@]}"; do
  79. if _anim_has_dep "$lib"; then
  80. echo "$lib"
  81. return 0
  82. fi
  83. done
  84. return 1
  85. }
  86. # Build the install command for the recommended package.
  87. # Args: $1 = package name (motion | motion-v).
  88. # Outputs the command on stdout. Returns 1 on missing arg.
  89. recommend_anim_install_cmd() {
  90. local pkg="$1"
  91. [ -z "$pkg" ] && return 1
  92. local root="$ANIM_PROJECT_ROOT"
  93. if [ -f "$root/pnpm-lock.yaml" ]; then
  94. echo "pnpm add $pkg"
  95. elif [ -f "$root/yarn.lock" ]; then
  96. echo "yarn add $pkg"
  97. elif [ -f "$root/bun.lockb" ] || [ -f "$root/bun.lock" ]; then
  98. echo "bun add $pkg"
  99. else
  100. echo "npm install $pkg"
  101. fi
  102. }