Code Monkey home page Code Monkey logo

ember-unused-components's Introduction

npm version Build Status

ember-unused-components

This script searches for unused components in your Ember project. It supports:

  • classic structure,
  • POD structure,
  • Module Unification structure,
  • {{curly-braces}} syntax,
  • <AngleBrackets> syntax (also nested ones like <Angle::MyBrackets>),
  • ember-light-table's way of defining cellComponent: 'component-name' and component: 'component-name'
  • (component "component-name") helper used in templates
  • ignoring files,
  • whitelisting components unused temporary,
  • addons,
  • and components being used in addons with --includeAddons option.

It also has a very interesting statistics module.

Installation

$ yarn add -D ember-unused-components

or

$ npm install ember-unused-components --save-dev

Usage

Run in your app root directory:

$ ./node_modules/.bin/ember-unused-components

or

$ npx ember-unused-components

Expected output (simplified):

 No. of components: 277
 No. of unused components: 2 (7.22%)

 Unused components:
  - app/app-tab-panel
  - user/user-birthday

If you feel like there are too many components listed then check Configuration Section.

Stats

Make sure to try optional parameter --stats so you'll see some interesting stuff:

$ npx ember-unused-components --stats

 No. of components: 304
 No. of unused components: 8 (2.63%)

 Unused components:
  - report-header
  - report-row-title
  - reports-settings-dropdown
  - ui-checkbox-list
  - ui-manage-screen-title
  - ui-phone/-body/-message
  - ui-table/-cell-currency
  - ui-table/-cell-date

 The most used component: ui-form-button (101 occurrences)
 The number of components used just once: 171 (56.25%)
 Usage of {{curly-braces}} vs <AngleBrackets> syntax: 509 (56.81%) vs 387 (43.19%)
 Usage of (component "component-name") helper in templates: 70
 Usage in JS files (e.g. through `import` or ELT): 63

Occurrences

You can also print all occurrences of components that were found. Use --occurrences or --o:

$ npx ember-unused-components --occurrences

// simplified

user-avatar:

   > ./app/templates/components/user-card.hbs
    - <UserAvatar @src={{_imageSource}} />

welcome-page:

   > ./app/templates/application.hbs
    - {{welcome-page}}

[EXPERIMENTAL] Searching components contained in other packages

You can also print all occurrences of components that were found in included addons. Use --includeAddons to include all found addons, or includeAddons=company-* to only include addons that match company-*

$ npx ember-unused-components --occurrences --includeAddons=company-*

// simplified

[company-buttons] button-a:

   > ./app/templates/components/user-card.hbs
    - <ButtonA>Button Text</ButtonA>

welcome-page:

   > ./app/templates/application.hbs
    - {{welcome-page}}

Advanced usage

Typically the script should realize if you are using POD structure or not and find its way to components directory.

If you have problems with that, consider:

Forcing POD

To force using POD use --pods argument (alias: -p). Like this:

$ npx ember-unused-components --pods

The script will use the default directory of POD components: app/components. Please let me know if you had to force using POD. I made a simple guessing algorithm that should handle PODs out-of-the-box.

Forcing POD with the custom directory

Your app should be configured to have podModulePrefix property in config/environment.js if you are using POD but if it somehow doesn't work you can specify it through --pods-dir (alias: -pd). Like this:

$ npx ember-unused-components --pods --pods-dir="modules/components-repository"

Configuration

In typical use cases, it should work out of the box and you don't have to configure anything but you can consider the following options. First, you need to create .eucrc.js file in the root directory:

module.exports = {
  whitelist: [
    'app/app-tab-panel' // we will use it again soon
  ],
  ignore: [
    'app/templates/freestyle.hbs' // this is our template with style guides
  ],
  failOnUnused: false // optional, default is false, should we throw errors when we find unused components (useful in CI)
};

Whitelist

You can specify which components should not be treated as unused even if the script couldn't find their usage occurrences. This happens when:

  • you know that the component will be used in the future and you don't want to remove it and being reminded of that
  • you use some kind of "dynamic name resolution" for your components

For the "dynamic name resolution" scenario consider following example. Typical dynamic component look like this:

Template

{{component name car=car}}

JavaScript

name: computed('car.type', function () {
  return `car-card-${this.get('car.type')}`;
});

Result

Which may result in having following components in use:

  • car.type = 'suv' => {{car-card-suv car=car}}
  • car.type = 'sport' => {{car-card-sport car=car}}
  • car.type = 'sedan' => {{car-card-sedan car=car}}

Unfortunately, this static analysis tool doesn't understand it yet and doesn't know that your component car-card-suv has been used anywhere. You can whitelist these components from being marked as unused by referencing to them directly:

module.exports = {
  whitelist: [
    'car-card-suv',
    'car-card-sport',
    'car-card-sedan'
  ]
};

or by using wildcard:

module.exports = {
  whitelist: ['car-card-*']
};

Ignored files

A component might be used in some files like guidelines template (see ember-freestyle) that in fact does not indicate that it is in use. The best practice is to ignore that file:

module.exports = {
  ignore: [
    'app/templates/freestyle.hbs' // this is our template with style guides
  ]
};

Fail on unused

You might want to throw errors when unused components are found. This is especially useful when running in CI. This behavior is turned off by default.

Turn this behavior on in 2 ways:

Setting the failOnUnused property of your .eucrc.js file to true.

module.exports = {
  failOnUnused: true
};

// In practice, it might look something like this:

module.exports = {
  failOnUnused: process.env.CI // many CI services like Travis-ci and Circle-ci inject a CI env variable by default.
};

Passing the --fail-on-unused flag to the cli:

./node_modules/.bin/ember-unused-components --fail-on-unused

The .eucrc.js configuration file takes precedence over the cli argument.

Removing components

Auto removing components might be useful but it's not yet supported. Please consider that simple removal of:

  • template.hbs or templates/component-name.hbs
  • component.js or components/component-name.js
  • style.css or styles/components/style.css

might not remove everything you would like. Examples:

  • global CSS classes that are no longer used
  • 3rd party JS libraries used only in that component
  • translation keys that are no longer needed
  • assets (images) that are no longer used
  • local overwrites/adjustments for that component made by parent's CSS

So you'll still have some dead code because of unused components.

I encourage you to remove components from the list manually.

Best Practices

Once you delete unused components run the script once again :) You might find that now some other components are no longer used. This happens when the component is used in the unused component.

Example:

{{!-- users-list component --}}
{{#each users as |user|}}
  {{user-card user=user}}
{{/each}}

So user-card is being used but in unused component users-list. Once you will delete users-list component then user-card will not be longer used.

Contributing

If you feel like you need some functionality please raise an issue or event better Contribute!

Testing

It's very important to prepare test cases for fixes and new features.

We have a directory test-apps with applications that have different configs and different Ember versions which support or does not support certain "features" like angle brackets components or module unification project structure.

Running tests

  • yarn run test

Linting

  • yarn run lint

Debugging

  • npx ember-unused-components --debug

License

This project is licensed under the MIT License.

ember-unused-components's People

Contributors

ctjhoa avatar dependabot-preview[bot] avatar ember-tomster avatar evoactivity avatar gideonswaak avatar jkeen avatar tniezurawski avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

ember-unused-components's Issues

Handle nested folders

Right now, without pods enabled, if I have a tree folder like that:

/components
├── first-component.js
└── /custom-folder
    ├── second-component.js
    └── third-component.js

Ember resolve second-component & third-component without the custom-folder prefix. which means that in template file you can call them like that:

{{!-- some random .hbs template --}}
{{second-component}}

In the _mapComponents function it is assumed that it's a flat components hierarchy.

So even if second-component is used, the addon will look for a component named custom-folder/second-component

Does not find template-only components

The function mapComponents in lib/analyzer.js is called only with app/components which means that components that only have a template won't be indexed. So the same mapComponents function should also be called with app/templates/components I guess.

Does not work for .coffee and .ts files

Reading through the code, one can see in the lib/analyzer.js file in the mapComponents function:

let recognizer = config.usePods || config.useModuleUnification ? '/component.js' : '.js';

if (filename.includes(recognizer)) {

In the case when neither pods, nor MU is used, a file extension of js is expected. This won't work correctly for .coffee and .ts files.

Find component calls from addon dependencies?

I'm working on a project with many dependencies—some of which are internal addons containing shared components used within the company. I'm in the process of trying to determine which of these addons can be cut out from a project (as in: "does this parent project ever call a component within this addon?")

ember-unused-components --occurrences is wonderful, and I'd love to know if there's a way to get it to list components that come from addons, too?

No output for pods components

Hi,

Just trying out this neat addon and so far all I can get out of it are zeros!

The app has ~100 components under ./app/components, which use the pods structure. The rest of the app uses the classic layout for routes, controllers, and so on, i.e. it only use the pod structure for components.

config/environment.js sets podModulePrefix to an empty string.

Running yarn ember-unused-components --pods gives:

 No. of components: 0
 No. of unused components: 0 

 Congratulations! No unused components found in your project.

I'll try to find some time to troubleshoot the problem next week. My understanding is that this is expected tot be supported already, is that correct?

Many thanks.

Feature request for more stats

It's great to know the total components and total unused components. My team found this addon while looking for a solution to count the components in our app in an effort to identify components that are reused (vs unused).

Could the --stats output include the count of components that are reused?

 No. of components: 100
 No. of unused components: 5 (5.00%)
 No. of reused components: 25 (25.00%)

I noticed the option for --occurrences lists where the components are found, perhaps components that are found more than once could be considered "reused" and a statistic for reused could be incremented.

What I had in mind is an option for --counts that leverages --occurrences behavior but only lists the components and the number of time that the component is used.

Better component matching

Hi I just discover your repository while trying to write similar addon and it's a great discovery!

You can find my experimental addon here
I took a different approach I've written all the main part in a bash script (I don't care about OS compatibility for the moment).

I've discover that your addon miss some cases like inheritance, for example.

// components/concrete.js
import AbstractComponent from 'my-app/components/abstract';

export default AbstractComponent.extend({
   // do stuff
})

or some addons like ember-light-table declare stuff like:

// components/my-table.js
import { computed } from '@ember/object';
import Table from 'ember-light-table';

export default Ember.Component.extend({
  model: null,

  columns: computed(function() {
    return [{
      label: 'Avatar',
      valuePath: 'avatar',
      width: '60px',
      sortable: false,
      cellComponent: 'user-avatar' // <----- here is the issue
    }];
  }),

  table: computed('model', function() {
   return new Table(this.get('columns'), this.get('model'));
  })
});

So I made a PR to fix that -> #2

Cleanup option

I've also notice that you don't provide any cleanup option to directly remove components from the addon.
Something like ember unused:components --cleanup.

I have achieved this in my project with dirty exec
Do you know a way to call directly another ember command without spawning another process?

A few false positives

Hi, thank you for building and share this script!

I wanted to note a few false positives I found running this on my app. These cases require advanced analysis and perhaps @lifeart's ember-meta-explorer better suits finding those occurrences. Dyfactor could also apply here.

  1. we invoke a component name as an argument to an action
{{action "someAction" "my-component-form" bubbles=false}} 
  1. we have a component only used in tests (very lame, I know 😄).
  2. We have multiple {{component (concat "thing-" someVariable)}}

There are a couple others, mostly around more complex {{component helper usage.
To me, a good solution would just be indicating possible false positives around any {{component usage. Perhaps even more naive checks for the string of a component name used by helpers or passed as properties to a component.

Handle other ember objects

Right now the addon only handle components but we can imagine other algorithm to find unused routes, views, partials...

The addon can be renamed ember-unused and can provides multiple commands like ember unused:views.

Is it something you have in mind for futur releases?

Unstable-Ember-Language server integration?

VSCode addon: https://marketplace.visualstudio.com/items?itemName=lifeart.vscode-ember-unstable

Usecase: https://code.visualstudio.com/api/language-extensions/programmatic-language-features#find-all-references-to-a-symbol

context: NullVoxPopuli/coc-ember#9

Fild all references to a symbol

proposed api:

"ember-language-server": {
    "entry": "./lib/langserver",
    "referencesProvider": true
}
module.exports.onReference = async function(projectRoot, { itemType: 'COMPONENT', itemName: 'normalized/component/name' }) {
        return [
         { path: "app/templates/route-name/route.hbs", position: { line: 0, character: 12 } },
         { path: "app/templates/route-name/route.hbs", position: { line: 10, character: 12 } }
        ];
    }
}

//cc @NullVoxPopuli

Module Unification Support?

I don't have an app directory :(

$ ember unused:components
Searching for unused components:
ENOENT: no such file or directory, scandir './app/components'

Sometimes doesn't throw an error when `--path` is wrong

Consider these:

➜  ember-unused-components git:(master) ./index.js --path="test-apps/ember_3_10_mu/" --stats
Can't find Ember config. Are you sure you are running this in root directory?

☝️ That's ok. The path was wrong. The error was thrown.
👇 That's not ok. A small change in path with / at the beginning but not at the end and it crashes:

➜  ember-unused-components git:(master) ./index.js --path="/test-apps/ember_3_10_mu" --stats
[1/3] 🗺️  Mapping the project...
fs.js:114
    throw err;
    ^

Error: ENOENT: no such file or directory, scandir './/test-apps/ember_3_10_musrc/ui/components'
    at Object.readdirSync (fs.js:790:3)
    at Object.mapComponents (/Users/tomek/Projects/my/ember-unused-components/lib/analyser.js:231:20)
    at main (/Users/tomek/Projects/my/ember-unused-components/index.js:36:12)
    at Object.<anonymous> (/Users/tomek/Projects/my/ember-unused-components/index.js:46:1)
    at Module._compile (internal/modules/cjs/loader.js:776:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:787:10)
    at Module.load (internal/modules/cjs/loader.js:653:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
    at Function.Module._load (internal/modules/cjs/loader.js:585:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:829:12)

Make the package size smaller

Whitelist files that are needed for this library to operate. The current size of the package is too big to what it does. Mostly because of including tests files and test applications (a few Ember apps).

➜  ember-unused-components git:(master) npm pack --dry-run

npm notice === Tarball Details ===
npm notice name:          ember-unused-components
npm notice version:       1.0.4
npm notice filename:      ember-unused-components-1.0.4.tgz
npm notice package size:  789.0 kB
npm notice unpacked size: 2.2 MB
npm notice total files:   422

Nested component structure support

Hi! It would be nice if ember-unused-components also supports the nested component structure.

Atm, even though used, this component is flagged as unused:

- app/components/foo-bar/index.js
- app/components/foo-bar/index.hbs

It seems like we can achieve this by updating the objectName function to also strip index.js and index.hbs.

If you can also tell me what else is needed testing wise, I could probably make a PR for this.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.