├── .npmignore ├── tests ├── components │ ├── K.jsx │ ├── L.ts │ ├── M.tsx │ ├── N.tsx │ ├── D.js │ ├── E.js │ ├── G.js │ ├── H.js │ ├── F.js │ ├── A.js │ ├── I.js │ ├── B.js │ ├── C.js │ └── J.js ├── ignored_types │ ├── file.min.js │ ├── file.chunk.js │ └── file.bundle.js ├── ignored_paths │ ├── bin │ │ └── index.js │ ├── build │ │ └── index.js │ ├── dist │ │ └── index.js │ ├── log │ │ └── index.js │ ├── logs │ │ └── index.js │ ├── out │ │ └── index.js │ ├── output │ │ └── index.js │ ├── target │ │ └── index.js │ ├── test │ │ └── index.js │ ├── tests │ │ └── index.js │ └── node_modules │ │ └── index.js ├── imports │ └── index.js ├── imports_commented │ └── index.js └── index.bats ├── .gitignore ├── scripts ├── deploy_package.sh ├── deploy_site.sh └── install_bats.sh ├── .github ├── release-drafter.yml └── workflows │ ├── release-drafter.yml │ └── test.yml ├── docs ├── .vuepress │ └── config.js └── README.md ├── package.json ├── LICENSE └── findead.sh /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | tests 3 | scripts 4 | docs 5 | .github -------------------------------------------------------------------------------- /tests/components/K.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export const TestComponentK = () => { } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | !tests/ignored_paths/dist 4 | !tests/ignored_paths/node_modules -------------------------------------------------------------------------------- /tests/components/L.ts: -------------------------------------------------------------------------------- 1 | //@ts-ignore 2 | import React from "react"; 3 | 4 | export const TestComponentL = () => { } -------------------------------------------------------------------------------- /tests/components/M.tsx: -------------------------------------------------------------------------------- 1 | //@ts-ignore 2 | import React from "react"; 3 | 4 | export const TestComponentM = () => { } -------------------------------------------------------------------------------- /tests/components/N.tsx: -------------------------------------------------------------------------------- 1 | //@ts-ignore 2 | import React from "react"; 3 | 4 | export const TestComponentN = () => {}; 5 | -------------------------------------------------------------------------------- /tests/components/D.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | function TestComponentD() { } 4 | 5 | export default TestComponentD; -------------------------------------------------------------------------------- /tests/components/E.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | function TestComponentE(props) { } 4 | 5 | export default TestComponentE; -------------------------------------------------------------------------------- /tests/components/G.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const TestComponentG = () => { } 4 | 5 | export default TestComponentG; -------------------------------------------------------------------------------- /scripts/deploy_package.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -e 4 | 5 | cp docs/README.md README.md 6 | 7 | npm publish 8 | 9 | rm README.md -------------------------------------------------------------------------------- /tests/components/H.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const TestComponentH = (props) => { } 4 | 5 | export default TestComponentH; -------------------------------------------------------------------------------- /tests/components/F.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | function TestComponentF({prop1, prop2}) { } 4 | 5 | export default TestComponentF; -------------------------------------------------------------------------------- /tests/components/A.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | class TestComponentA extends React.Component { } 4 | 5 | export default TestComponentA; -------------------------------------------------------------------------------- /tests/components/I.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const TestComponentI = ({ prop1, prop2 }) => { } 4 | 5 | export default TestComponentI; -------------------------------------------------------------------------------- /tests/components/B.js: -------------------------------------------------------------------------------- 1 | import { Component } from "react"; 2 | 3 | class TestComponentB extends Component { } 4 | 5 | export default TestComponentB; 6 | -------------------------------------------------------------------------------- /tests/components/C.js: -------------------------------------------------------------------------------- 1 | import { PureComponent } from "react"; 2 | 3 | class TestComponentC extends PureComponent { } 4 | 5 | export default TestComponentC; -------------------------------------------------------------------------------- /tests/ignored_types/file.min.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | class TestComponentMinExt extends React.Component { } 4 | 5 | export default TestComponentMinExt; -------------------------------------------------------------------------------- /tests/ignored_types/file.chunk.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | class TestComponentChunkExt extends React.Component { } 4 | 5 | export default TestComponentChunkExt; -------------------------------------------------------------------------------- /tests/ignored_types/file.bundle.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | class TestComponentBundleExt extends React.Component { } 4 | 5 | export default TestComponentBundleExt; -------------------------------------------------------------------------------- /tests/ignored_paths/bin/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | class TestComponentInIgnoredPath extends React.Component { } 4 | 5 | export default TestComponentInIgnoredPath; -------------------------------------------------------------------------------- /tests/ignored_paths/build/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | class TestComponentInIgnoredPath extends React.Component { } 4 | 5 | export default TestComponentInIgnoredPath; -------------------------------------------------------------------------------- /tests/ignored_paths/dist/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | class TestComponentInIgnoredPath extends React.Component { } 4 | 5 | export default TestComponentInIgnoredPath; -------------------------------------------------------------------------------- /tests/ignored_paths/log/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | class TestComponentInIgnoredPath extends React.Component { } 4 | 5 | export default TestComponentInIgnoredPath; -------------------------------------------------------------------------------- /tests/ignored_paths/logs/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | class TestComponentInIgnoredPath extends React.Component { } 4 | 5 | export default TestComponentInIgnoredPath; -------------------------------------------------------------------------------- /tests/ignored_paths/out/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | class TestComponentInIgnoredPath extends React.Component { } 4 | 5 | export default TestComponentInIgnoredPath; -------------------------------------------------------------------------------- /tests/ignored_paths/output/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | class TestComponentInIgnoredPath extends React.Component { } 4 | 5 | export default TestComponentInIgnoredPath; -------------------------------------------------------------------------------- /tests/ignored_paths/target/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | class TestComponentInIgnoredPath extends React.Component { } 4 | 5 | export default TestComponentInIgnoredPath; -------------------------------------------------------------------------------- /tests/ignored_paths/test/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | class TestComponentInIgnoredPath extends React.Component { } 4 | 5 | export default TestComponentInIgnoredPath; -------------------------------------------------------------------------------- /tests/ignored_paths/tests/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | class TestComponentInIgnoredPath extends React.Component { } 4 | 5 | export default TestComponentInIgnoredPath; -------------------------------------------------------------------------------- /tests/ignored_paths/node_modules/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | class TestComponentInIgnoredPath extends React.Component { } 4 | 5 | export default TestComponentInIgnoredPath; -------------------------------------------------------------------------------- /tests/components/J.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const TestComponentJ1 = ({ prop1, prop2 }) => { } 4 | 5 | const TestComponentJ2 = ({ prop1, prop2 }) => { 6 | return 7 | } 8 | 9 | export default TestComponentJ2; -------------------------------------------------------------------------------- /scripts/deploy_site.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -e 4 | 5 | npm run site:build 6 | 7 | cd docs/.vuepress/dist 8 | 9 | git init 10 | git add -A 11 | git commit -m 'deploy' 12 | 13 | git push -f git@github.com:narcello/findead.git master:gh-pages 14 | 15 | cd - 16 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | categories: 2 | - title: "🚀 Features" 3 | labels: 4 | - "feature" 5 | - "enhancement" 6 | - title: "🐛 Bug Fixes" 7 | labels: 8 | - "fix" 9 | - "bugfix" 10 | - "bug" 11 | change-template: "- $TITLE @$AUTHOR (#$NUMBER)" 12 | exclude-labels: 13 | - "skip-changelog" 14 | template: | 15 | ## Changes 16 | 17 | $CHANGES 18 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Update Changelog 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | update_release_draft: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: release-drafter/release-drafter@v5 15 | with: 16 | config-name: release-drafter.yml 17 | env: 18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 19 | -------------------------------------------------------------------------------- /scripts/install_bats.sh: -------------------------------------------------------------------------------- 1 | install_bats() { 2 | cd bats 3 | sudo ./install.sh /usr/local 4 | } 5 | 6 | clone_bats_repo() { 7 | cd ~ 8 | EXISTS_BATS_FOLDER=$(ls | grep 'bats') 9 | [[ -z "$EXISTS_BATS_FOLDER" ]] && git clone https://github.com/sstephenson/bats.git 10 | install_bats 11 | } 12 | 13 | check_bats_on_environment() { 14 | EXISTS_BATS_ON_ENVIRONMENT=$(whereis bats | grep 'bats: .*') 15 | [[ -z "$EXISTS_BATS_ON_ENVIRONMENT" ]] && clone_bats_repo 16 | } 17 | 18 | check_bats_on_environment 19 | -------------------------------------------------------------------------------- /docs/.vuepress/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | title: 'Findead', 3 | description: 'Dead react components finder', 4 | base: '/findead/', 5 | themeConfig: { 6 | nav: [ 7 | { text: 'Home', link: '/' }, 8 | { text: 'Motivation', link: '/#motivation-dead-components' }, 9 | { text: 'Tech', link: '/#tech' }, 10 | { text: 'Install', link: '/#install' }, 11 | { text: 'Usage', link: '/#usage' }, 12 | { text: 'Examples', link: '/#examples' }, 13 | { text: 'Github', link: 'https://github.com/narcello/findead', target:'_blank', rel:'' } 14 | ] 15 | } 16 | } -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Run Tests 2 | 3 | on: pull_request 4 | 5 | jobs: 6 | test-on-ubuntu: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v1 11 | - name: Install Bats 12 | run: ./scripts/install_bats.sh 13 | - name: Install dependencies 14 | run: npm i 15 | - name: Run tests 16 | run: npm t 17 | 18 | test-on-mac: 19 | runs-on: macos-latest 20 | 21 | steps: 22 | - uses: actions/checkout@v1 23 | - name: Install Bats 24 | run: ./scripts/install_bats.sh 25 | - name: Install dependencies 26 | run: npm i 27 | - name: Run tests 28 | run: npm t 29 | -------------------------------------------------------------------------------- /tests/imports/index.js: -------------------------------------------------------------------------------- 1 | import TestComponentA from "../components/A"; 2 | import TestComponentB from "../components/B"; 3 | import TestComponentC from "../components/C"; 4 | import TestComponentD from "../components/D"; 5 | import TestComponentE from "../components/E"; 6 | import TestComponentF from "../components/F"; 7 | import TestComponentG from "../components/G"; 8 | import TestComponentH from "../components/H"; 9 | import TestComponentI from "../components/I"; 10 | import TestComponentJ2 from "../components/J"; 11 | import TestComponentK from "../components/K"; 12 | import TestComponentL from "../components/L"; 13 | import TestComponentM from "../components/M"; 14 | 15 | const routes = [ 16 | { 17 | path: '/path/to/component', 18 | exact: true, 19 | component: lazy(() => import('../components/N')) 20 | }, 21 | ] -------------------------------------------------------------------------------- /tests/imports_commented/index.js: -------------------------------------------------------------------------------- 1 | // import TestComponentA from "../components/A"; 2 | // import TestComponentB from "../components/B"; 3 | // import TestComponentC from "../components/C"; 4 | // import TestComponentD from "../components/D"; 5 | // import TestComponentE from "../components/E"; 6 | // import TestComponentF from "../components/F"; 7 | // import TestComponentG from "../components/G"; 8 | // import TestComponentH from "../components/H"; 9 | // import TestComponentI from "../components/I"; 10 | // import TestComponentJ2 from "../components/J"; 11 | // import TestComponentK from "../components/K"; 12 | // import TestComponentL from "../components/L"; 13 | // import TestComponentM from "../components/M"; 14 | 15 | const routes = [ 16 | { 17 | path: '/path/to/component', 18 | exact: true, 19 | // component: lazy(() => import('../components/N')) 20 | }, 21 | ] -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "findead", 3 | "version": "1.2.3", 4 | "description": "Dead react components finder", 5 | "preferGlobal": true, 6 | "bin": { 7 | "findead": "findead.sh" 8 | }, 9 | "author": { 10 | "name": "Marcello Silva", 11 | "email": "marcellovcs@gmail.com", 12 | "url": "https://github.com/narcello" 13 | }, 14 | "scripts": { 15 | "test": "bats ./tests/index.bats", 16 | "site:dev": "vuepress dev docs", 17 | "site:build": "vuepress build docs", 18 | "site:deploy": "./scripts/deploy_site.sh", 19 | "npm:deploy": "./scripts/deploy_package.sh" 20 | }, 21 | "repository": "https://github.com/narcello/findead", 22 | "homepage": "https://narcello.github.io/findead", 23 | "keywords": [ 24 | "dead", 25 | "react", 26 | "components", 27 | "bash", 28 | "sh", 29 | "unused" 30 | ], 31 | "license": "ISC", 32 | "devDependencies": { 33 | "bats-assert": "git+https://github.com/ztombol/bats-assert.git", 34 | "bats-support": "git+https://github.com/ztombol/bats-support.git", 35 | "vuepress": "1.5.4" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Marcello Victor 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # :mag: findead 2 | Dead react components finder 3 | 4 | 5 | [![NPM Version](https://img.shields.io/npm/v/findead?logo=npm)]() 6 | [![NPM Downloads](https://img.shields.io/npm/dw/findead?logo=npm)]() 7 | [![Run Tests](https://github.com/narcello/findead/actions/workflows/test.yml/badge.svg)](https://github.com/narcello/findead/actions/workflows/test.yml) 8 | 9 | ## :rocket: *Dead Components* is the Motivation 10 | Many times in large or even small projects, we forgot some components in code that we'll never use and we never take time to search one by one and remove. 11 | 12 | ## :camera: Demonstration 13 | ![Demonstration](https://user-images.githubusercontent.com/6786382/73863397-c3d5aa00-481e-11ea-9360-0a530a93cd4a.png) 14 | When findead finish, you'll can see: 15 | * Components name 16 | * Path of each one component 17 | * Size of each one file 18 | * How many dead components 19 | * How many browsed files 20 | * How much time spent to execution 21 | ## :computer: Tech 22 | Just bash :) 23 | 24 | ## :inbox_tray: Install 25 | * Npm 26 | ```sh 27 | npm i -g findead 28 | ``` 29 | * Yarn 30 | ```sh 31 | yarn add findead 32 | ``` 33 | 34 | ## :hammer: Usage 35 | ```bash 36 | findead 37 | ``` 38 | Pass folder to get all of your components in js, jsx, ts and tsx files. 39 | 40 | ___obs: By default, all `node_modules` folder is ignored.___ 41 | 42 | ## :zap: Examples 43 | #### Just one argument 44 | * If you pass just one argument, it will be used for `get components` and `search usages` 45 | ```bash 46 | findead ~/path/to/search 47 | ``` 48 | #### Raw result 49 | * Pass `-r` flag for raw output. Better for atribute output into a file. 50 | ```bash 51 | findead -r ~/path/to/search 52 | ``` 53 | #### Multiple and specific folders 54 | ```bash 55 | findead -m ~/path/to/search/{folder1,folder2,...,folderN} 56 | ``` 57 | -------------------------------------------------------------------------------- /tests/index.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load '../node_modules/bats-support/load' 4 | load '../node_modules/bats-assert/load' 5 | 6 | @test 'Test used component' { 7 | run echo "$(./findead.sh -m tests/{imports,components} | grep -o 'No unused components found')" 8 | assert_output "No unused components found" 9 | } 10 | 11 | @test 'Test unused component(without imports)' { 12 | run echo "$(./findead.sh tests/components | grep -o '14 possible dead components :/')" 13 | assert_output "14 possible dead components :/" 14 | } 15 | 16 | @test 'Test unused component(commented imports)' { 17 | run echo "$(./findead.sh -m tests/{imports_commented,components} | grep -o '14 possible dead components :/')" 18 | assert_output "14 possible dead components :/" 19 | } 20 | 21 | @test 'Test error: paths must precede expression' { 22 | run bash -c "cd tests/components/ && ../../findead.sh | grep -o 'paths must precede expression'" 23 | assert_failure 24 | } 25 | 26 | @test 'Test --version predicate' { 27 | PACKAGE_VERSION=$(cat ./package.json | grep '"version": .*,' | awk '{ print $2 }' | cut -d '"' -f 2) 28 | run ./findead.sh --version 29 | assert_output "findead@$PACKAGE_VERSION" 30 | } 31 | 32 | @test 'Test -v predicate' { 33 | PACKAGE_VERSION=$(cat ./package.json | grep '"version": .*,' | awk '{ print $2 }' | cut -d '"' -f 2) 34 | run ./findead.sh -v 35 | assert_output "findead@$PACKAGE_VERSION" 36 | } 37 | 38 | @test 'Test --help predicate' { 39 | run ./findead.sh --help 40 | assert_line --partial 'findead -h | --help' 41 | } 42 | 43 | @test 'Test -h predicate' { 44 | run ./findead.sh -h 45 | assert_line --partial 'findead -h | --help' 46 | } 47 | 48 | @test 'Test -r predicate' { 49 | run echo "$(./findead.sh -r tests | grep -o 'No unused components found')" 50 | assert_output 'No unused components found' 51 | } 52 | 53 | @test 'Test multiples predicates' { 54 | run echo "$(./findead.sh -mr tests/{imports_commented,components} | grep -o '14 possible dead components :/')" 55 | assert_output '14 possible dead components :/' 56 | } 57 | 58 | @test 'Test specific comands' { 59 | run wc -c < ./tests/components/A.js 60 | assert_line --partial '108' 61 | } 62 | 63 | @test 'Test ignored type files' { 64 | run bash -c "cd tests/ignored_types/ && ../../findead.sh | grep -o '1 browsed files in'" 65 | assert_output '1 browsed files in' 66 | } 67 | 68 | @test 'Test ignored paths' { 69 | run bash -c "cd tests/ignored_paths/ && ../../findead.sh | grep -o '1 browsed files in'" 70 | assert_output '1 browsed files in' 71 | } -------------------------------------------------------------------------------- /findead.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # set -e 4 | 5 | CLASS_COMPONENT='' 6 | FIRST_LETTER_COMPONENT='' 7 | declare -a COMPONENTS 8 | AUX_ARRAY_COMPONENTS='' 9 | COUNTER_UNUSED_COMPONENTS=0 10 | AUX_COUNTER=0 11 | FIND_RETURN='' 12 | FIRST_ARGUMENT=$1 13 | PATH_TO_FIND='' 14 | FINDEAD_TIME='' 15 | TIMEFORMAT="%R" 16 | FILE_PATH='' 17 | CURRENT_FUNCTIONS='' 18 | if [ $# -gt 1 ]; then 19 | MULTIPLE_PATHS=$(echo $FIRST_ARGUMENT | grep '\-.*m') 20 | RAW=$(echo $FIRST_ARGUMENT | grep '\-.*r') 21 | fi 22 | 23 | fileSizeKB() { 24 | FILE_SIZE_B=$(wc -c <$1) 25 | [[ ${FILE_SIZE_B} -lt 1024 ]] && echo "${FILE_SIZE_B} Bytes" 26 | [[ ${FILE_SIZE_B} -gt 1023 ]] && echo "$(echo ${FILE_SIZE_B}/1024 | bc) KB" 27 | } 28 | 29 | center() { 30 | termwidth="$(tput -T xterm cols)" 31 | padding="$(printf '%0.1s' ={1..500})" 32 | printf '%*.*s %s %*.*s\n' 0 "$(((termwidth - 2 - ${#1}) / 2))" "$padding" "$1" 0 "$(((termwidth - 1 - ${#1}) / 2))" "$padding" 33 | } 34 | 35 | centerResult() { 36 | termwidth="$(tput -T xterm cols)" 37 | padding="$(printf '%0.1s' ={1..500})" 38 | printf "\e[0m%*.*s \e[36m%s\e[0m %*.*s\n" 0 "$(((termwidth - 2 - ${#1}) / 2))" "$padding" "$1" 0 "$(((termwidth - 1 - ${#1}) / 2))" "$padding" 39 | } 40 | 41 | searchFiles() { 42 | FIND_RETURN=$( 43 | eval $"find $PATH_TO_FIND -type f \( ! -name '*.chunk.*' ! -name '*.min.*' ! -name '*.bundle.*' -name '*.js' -o -name '*.jsx' -o -name '*.ts' -o -name '*.tsx' \) \ 44 | -not -path '*/node_modules/*' \ 45 | -not -path '*/dist/*' \ 46 | -not -path '*/build/*' \ 47 | -not -path '*/bin/*' \ 48 | -not -path '*/out/*' \ 49 | -not -path '*/output/*' \ 50 | -not -path '*/target/*' \ 51 | -not -path '*/log/*' \ 52 | -not -path '*/logs/*' \ 53 | -not -path '*/test/*' \ 54 | -not -path '*/tests/*' \ 55 | -print" 56 | ) 57 | } 58 | 59 | getClassComponents() { 60 | CLASS_COMPONENT=$(cat ${FILE_PATH} | grep -o "class.*Component" | awk '{ print $2 }') 61 | USED_IN_SAME_FILE=$(grep -o "<$CLASS_COMPONENT" ${FILE_PATH}) 62 | if [ ! -z "$CLASS_COMPONENT" ]; then 63 | [[ -z "$USED_IN_SAME_FILE" ]] && COMPONENTS+=("$CLASS_COMPONENT;$FILE_PATH") 64 | fi 65 | AUX_ARRAY_COMPONENTS=${COMPONENTS[@]} 66 | } 67 | 68 | checkFunctions() { 69 | for FUNCTION in $CURRENT_FUNCTIONS; do 70 | if [[ ! $FUNCTION =~ ^(\[|\]|\{|\})$ ]]; then 71 | FIRST_LETTER_FUNCTION_COMPONENT="$(echo "$FUNCTION" | head -c 1)" 72 | USED_IN_SAME_FILE=$(grep -o "<$FUNCTION" ${FILE_PATH}) 73 | [[ "$FIRST_LETTER_FUNCTION_COMPONENT" =~ [A-Z] ]] && 74 | [[ -z "$USED_IN_SAME_FILE" ]] && COMPONENTS+=("$FUNCTION;$FILE_PATH") 75 | fi 76 | AUX_ARRAY_COMPONENTS=${COMPONENTS[@]} 77 | done 78 | } 79 | 80 | getES5FunctionComponents() { 81 | CURRENT_FUNCTIONS=$(cat ${FILE_PATH} | grep -o "function.*(" | awk '{ print $2 }' | cut -d "(" -f 1) 82 | checkFunctions 83 | } 84 | 85 | getES6FunctionComponents() { 86 | CURRENT_FUNCTIONS=$(cat ${FILE_PATH} | grep -o "const.*= (.*) =>" | awk '{ print $2 }') 87 | checkFunctions 88 | } 89 | 90 | getFunctionComponents() { 91 | IS_REACT_FILE=$(cat ${FILE_PATH} | grep 'import.*React') 92 | if [ ! -z "$IS_REACT_FILE" ]; then 93 | getES5FunctionComponents 94 | getES6FunctionComponents 95 | fi 96 | } 97 | 98 | getComponents() { 99 | for ITEM in $FIND_RETURN; do 100 | FILE_PATH=$ITEM 101 | getClassComponents 102 | getFunctionComponents 103 | done 104 | } 105 | 106 | searchImports() { 107 | for COMPONENT in ${COMPONENTS[@]}; do 108 | COMPONENT_NAME=$(echo $COMPONENT | cut -d ";" -f 1) 109 | COMPONENT_FILE_PATH=$(echo $COMPONENT | cut -d ";" -f 2) 110 | ABSOLUTE_COMPONENT_FILE_PATH="/$(basename "$COMPONENT_FILE_PATH" | cut -d . -f 1)" 111 | 112 | GREP_RECURSIVE_RESULT=$(echo $FIND_RETURN | xargs grep "import.*$COMPONENT_NAME.*from") 113 | GREP_RECURSIVE_RESULT_FOR_LAZY_IMPORT=$(echo $FIND_RETURN | xargs grep "lazy(() => import(.*$ABSOLUTE_COMPONENT_FILE_PATH.*)") 114 | 115 | COMPONENTS_OCURRENCES=$([[ -z "$GREP_RECURSIVE_RESULT" && -z "$GREP_RECURSIVE_RESULT_FOR_LAZY_IMPORT" ]] && echo 0 || (printf "$GREP_RECURSIVE_RESULT\n$GREP_RECURSIVE_RESULT_FOR_LAZY_IMPORT" | wc -l)) 116 | COMMENTED_IMPORT_OCCURRENCES=$(printf "$GREP_RECURSIVE_RESULT\n$GREP_RECURSIVE_RESULT_FOR_LAZY_IMPORT" | grep // | wc -l) 117 | 118 | if [ "$COMPONENTS_OCURRENCES" = 0 ] || [ "$COMPONENTS_OCURRENCES" = "$COMMENTED_IMPORT_OCCURRENCES" ]; then 119 | ((COUNTER_UNUSED_COMPONENTS++)) 120 | FILE_SIZE=$(fileSizeKB $COMPONENT_FILE_PATH) 121 | [[ -z $RAW ]] && printf "\e[39m%40s | \e[35m%s | \e[33m%s %s\n" $COMPONENT_NAME $COMPONENT_FILE_PATH $FILE_SIZE || echo "$COMPONENT_NAME | $COMPONENT_FILE_PATH | $FILE_SIZE" 122 | fi 123 | AUX_COUNTER=$COUNTER_UNUSED_COMPONENTS 124 | done 125 | } 126 | 127 | showResult() { 128 | handleResult "Results" 129 | if [ $COUNTER_UNUSED_COMPONENTS -eq 0 ]; then 130 | handleResult "No unused components found" 131 | else 132 | handleResult "$COUNTER_UNUSED_COMPONENTS possible dead components :/" '\e[0m' 133 | fi 134 | BROWSED_FILES=$(echo "${FIND_RETURN/ /\n}" | wc -l) 135 | handleResult "$BROWSED_FILES browsed files in $FINDEAD_TIME seconds" 136 | } 137 | 138 | handleResult() { 139 | [[ -z $RAW ]] && centerResult "$1" || echo "$1" 140 | } 141 | 142 | main() { 143 | searchFiles && getComponents && searchImports 144 | } 145 | 146 | initStyle() { 147 | TERM=xterm-256color 148 | tput -T xterm clear 149 | tput -T xterm cup 1 0 150 | tput -T xterm rev 151 | tput -T xterm bold 152 | center 'Findead is looking for components...' 153 | tput -T xterm sgr0 154 | tput -T xterm cup 3 0 155 | } 156 | 157 | start() { 158 | [[ -z $RAW ]] && initStyle 159 | PATH_TO_FIND=$1 160 | { time main; } 2>findead_execution_time.txt 161 | FINDEAD_TIME=$(cat findead_execution_time.txt) 162 | showResult 163 | rm -rf findead_execution_time.txt 164 | [[ -z $RAW ]] && unset TIMEFORMAT 165 | } 166 | 167 | if [[ $FIRST_ARGUMENT == "--version" || $FIRST_ARGUMENT == "-v" ]]; then 168 | echo "findead@1.2.3" 169 | elif [[ $FIRST_ARGUMENT == "--help" || $FIRST_ARGUMENT == "-h" ]]; then 170 | cat <