eslint / eslint

Find and fix problems in your JavaScript code.
https://eslint.org
MIT License
24.33k stars 4.39k forks source link

Support having plugins as dependencies in shareable config #3458

Closed sindresorhus closed 1 year ago

sindresorhus commented 8 years ago

My shareable config uses rules from an external plugin and I would like to make it a dependency so the user doesn't have to manually install the plugin manually. I couldn't find any docs on this, but it doesn't seem to work, so I'll assume it's not currently supported.

module.js:338
    throw err;
          ^
Error: Cannot find module 'eslint-plugin-no-use-extend-native'
    at Function.Module._resolveFilename (module.js:336:15)
    at Function.Module._load (module.js:278:25)
    at Module.require (module.js:365:17)
    at require (module.js:384:17)
    at /usr/local/lib/node_modules/eslint/lib/cli-engine.js:106:26
    at Array.forEach (native)
    at loadPlugins (/usr/local/lib/node_modules/eslint/lib/cli-engine.js:97:21)
    at processText (/usr/local/lib/node_modules/eslint/lib/cli-engine.js:182:5)
    at processFile (/usr/local/lib/node_modules/eslint/lib/cli-engine.js:224:12)
    at /usr/local/lib/node_modules/eslint/lib/cli-engine.js:391:26

I assume it's because you only try to load the plugin when the config is finished merging.

Other shareable configs that depend on a plugin instructs the users to manually install the plugin too and they have it in peerDependencies. I find this sub-optimal though and I don't want the users to have to care what plugins my config uses internally.

The whole point of shareable configs is to minimize boilerplate and overhead, so this would be a welcome improvement.

Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

eslintbot commented 8 years ago

Thanks for the issue! We get a lot of issues, so this message is automatically posted to each one to help you check that you've included all of the information we need to help you.

Reporting a bug? Please be sure to include:

  1. The version of ESLint you are using (run eslint -v)
  2. The source code that caused the problem
  3. The configuration you're using (for the rule or your entire config file)
  4. The actual ESLint output complete with line numbers

Requesting a new rule? Please be sure to include:

  1. The use case for the rule - what is it trying to prevent or flag?
  2. Whether the rule is trying to prevent an error or is purely stylistic
  3. Why you believe this rule is generic enough to be included

Requesting a feature? Please be sure to include:

  1. The problem you want to solve (don't mention the solution)
  2. Your take on the correct solution to problem

Including this information in your issue helps us to triage it and get you a response as quickly as possible.

Thanks!

lo1tuma commented 8 years ago

We already discussed this in #2518 with the conclusion that a peerDependency is the way to go.

sindresorhus commented 8 years ago

Ugh, that's such a leaky abstraction. I guess I won't use plugins then...

The user shouldn't have to care what plugins I use for the rules. This is like requiring to manual install of Lodash when you want ESLint. A shareable config is a node module and should act like it.

lo1tuma commented 8 years ago

A shareable config is a node module and should act like it.

We use require to load shareable configs or plugins, what would make it act more like a node module than that?

This issue should be also solved when using npm version 3 which installs all subdependencies in the top-level node_modules folder.

sindresorhus commented 8 years ago

We use require to load shareable configs or plugins, what would make it act more like a node module than that?

Let the shareable config provide the plugin as an object:

module.exports = {
    plugins: [
        require('eslint-plugin-no-use-extend-native')
    ],
    env: {
        node: true
    },
    rules: {
        'comma-dangle': [2, 'never'],
        'no-cond-assign': 2
};

This issue should be also solved when using npm version 3 which installs all sub-dependencies in the top-level node_modules folder.

That's an implementation detail and not always guaranteed. Nobody should ever depend on that. npm@3 promises flatter dependency tree, not flat. If there are conflicts, there will be nesting.

feross commented 8 years ago

The option to require the plugin in the config itself would allow users to use my shareable config without needing to manually install two other plugins.

I like this proposal.

lo1tuma commented 8 years ago

@sindresorhus good point about npm 3, let's forget about this.

I kind of like your proposal, but it has a few problems:

  1. Eslint needs to know the name of the plugin. This could be solved easily by providing an object with the name e.g plugins: [ { 'eslint-plugin-foo' : require('eslint-plugin-foo')} ]
  2. Eslint caches plugins once they are loaded. This could be a problem if you use different shareable configs in different .eslintrc files where the shareable configs require the same plugin, but in a different version. Possible solution would be to avoid caching in this case.
BYK commented 8 years ago

Possible solution would be to avoid caching in this case.

Or we can prefix plugins provided by shareable configs with the name of the config?

lo1tuma commented 8 years ago

@BYK how would you reference the rules then? configname/pluginname/rulename? But I guess we would have the same problem if we avoid caching. We can't determine to which version of the plugin the rule belongs to.

That said, I think we should first decide if we want this feature in ESLint.

BYK commented 8 years ago

@BYK how would you reference the rules then? configname/pluginname/rulename?

Yep.

That said, I think we should first decide if we want this feature in ESLint.

Agreed. Might be worth piggy backing on npm 3 instead of introducing this complexity.

nzakas commented 8 years ago

A few things:

  1. npm 3 doesn't solve this problem, the relationship between a config and a plugin remains a peer relationship. The fact that npm 3 flattens dependencies doesn't fundamentally change that relationship, is just an implementation quirk that allows dependencies to be treated as peers in certain situations. That's not a solution, is a gamble.
  2. Configs should not contain executable code, that's very far outside of the responsibilities of configs.
  3. To keep this in context: we are talking about a one-time setup cost rather than ongoing pain.

While I can understand the desire to have one install that works, I don't see a path towards that without introducing a new type of shareable thing that could encapsulate this functionality.

sindresorhus commented 8 years ago

Then maybe introduce a universal sharing thing that can contain multiple plugins/configs/whatever. It could even in the future allow extending ESLint in some ways, with hooks, but I don't want to start that discussion. Just showing the possibilities with something like this.

JSCS supports it like this: https://github.com/wealthfront/javascript/blob/f1f976e9c75a8d141ec77a5493d9d965d951d4a6/jscs/index.js

I just want the user to be able to npm install one module and have the needed config and plugins without having to care about how anything works internally. That's the beauty of normal npm packages.

IanVS commented 8 years ago

I agree that the current method becomes unwieldy when you begin sharing configs which use other shared configs and/or plugins. For example, the installation instructions for my own personal config (which extends from Standard) is:

npm install --save-dev eslint-plugin-standard eslint-config-standard eslint-config-ianvs 

It would be much nicer UX to only need:

npm install --save-dev eslint-config-ianvs 

That said, I have no idea how that could be accomplished, and in the end it's a pain I can live with until/unless a better solution is found.

nzakas commented 8 years ago

We could extend plugins to allow the inclusion of configs, as plugins were always intended to be a dumping ground of stuff. Thoughts:

  1. How do we specify them in extends? eslint-plugin-foo.configs.whatever? Something else?
  2. We could, in theory, just expose the file system so you could do eslint.plugin-foo/configs/whatever.
  3. This is a bit lousy because we lose the nice eslint-config-* convention for configurations, so it ends up blurring what is a configuration and what is not.
  4. What if a config wants to depend on a plugin that it doesn't own? What does that look like?
  5. What is the expected behavior when a plugin is install directly and the same plugin is installed indirectly via another plugin?
feross commented 8 years ago

@nzakas

Configs should not contain executable code, that's very far outside of the responsibilities of configs.

Could you elaborate on this? It seems like this is a philosophical rather than practical objection. From a user's perspective, an eslint config is just an npm package that they need to install and extend in their .eslintrc. They don't care if there's executable code in there or not. Why complicate things for users?

nzakas commented 8 years ago

@feross Allowing executable objects arbitrarily in configs would complicate things for users. What I'm saying is let's not complicate it by ensuring that configs remain static regardless of their form.

joakimbeng commented 8 years ago

Let the shareable config provide the plugin as an object

:+1: would love to have this functionality!

We use require to load shareable configs or plugins, what would make it act more like a node module than that?

The problem is that the plugin name is not left as is, but instead parsed and prepended with eslint-plugin-. If ESLint didn't do this one could have solved the problem by adding plugins by their full paths, e.g. plugins: [path.join(__dirname, 'node_modules', 'eslint-plugin-babel')], not fancy but it would probably work.

nzakas commented 8 years ago

We don't have a good answer for this now. We'll revisit once we've finished some 2.0.0 tasks.

ilyavolodin commented 8 years ago

Related to #3659

davidmason commented 8 years ago

It seems as though this is the case for configs as well, unless I am mistaken. For configs at least, is it possible to change how extends are loaded so that nested extends are processed in the module context that they come from?

This could at least solve the issue for configs, which do not have the issue of executable code.

e.g.

// eslint-config-myteam/index.js
module.exports = {
  extends: 'goodstyles'
}
# myproject/.eslintrc
extends: myteam

So when eslint is processing myproject/.eslintrc and finds extends: myteam it will locate node_modules/eslint-config-myteam.

At the moment I think it blindly reads that in, then fails when it hits the nested extends: goodstyles because that is not available at the top level. Could it instead keep track of which module it found the myteam config in, and if it finds an extends in there, search in that module for the config it extends. There are a few options for how to search:

  1. look only in the specific module that the extending config is from
  2. look first in the specific module that the extending config is from, then look at the higher level if it is not found there
  3. look first in the module where eslint was run, then in the specific module if the config is missing form there

The question is whether people should be able to override configs by name (on purpose or otherwise) in their extending config. Overriding configs by accident would be possible with 3, so I would rule that out. 1 would not allow peer-dependency configs, so I think 2 is the best option - if someone wants to make other configs peer dependencies they can just not include them in their package.json, but there is the option to include them and make life easier for consumers of their shared config.

nzakas commented 8 years ago

This issue isn't really about extending configs, it's about bundling configs with plugins.

IanVS commented 8 years ago

Or is it about bundling plugins with configs? From the first post in this issue by @sindresorhus:

My shareable config uses rules from an external plugin and I would like to make it a dependency so the user doesn't have to manually install the plugin manually.

It is currently painful to extend a config which uses rules from a plugin. Or am I missing a larger picture?

nzakas commented 8 years ago

Semantics. To me "configs with plugins" means "config packages contain dependencies on plugin packages".

davidmason commented 8 years ago

@nzakas should I copy the "configs with configs" thing to a separate issue?

nzakas commented 8 years ago

@davidmason no need. That's a level of complexity that I don't want to build. If you want to extend an existing config in your own, just require it, make whatever modifications you want, then export it. It will work.

davidmason commented 8 years ago

@nzakas makes sense, I'll do that. I can check the eslint code to make sure I merge my changes in the same way so there are no surprises.

michaelcontento commented 8 years ago

Any news on this topic? I do understand that, from some point of view, peerDependencies might be the "theoretical right" spot to declare it.

But if we take a step back and think about the user, a more flexible / babeljs@6-like solution would be wunderful! Custom bundles of eslint rules/plugins are currently cumbersome to both

The issues above are the things I, as the creator of the bundle, like to avoid for all my users. And this is currently, AFAIK, not possible. So from a user point of view a good solution to create, share and update "eslint presets" would totally be a benefit. Or to put it differently: Those are the core requirements of a good "eslint presets" feature, they are currently missing and so is the whole feature.

A personal short side-note: The require.resolve-hack used by groupon and proposed to airbnb is nothing eslint should encourage you to! Also it only works for extends and parser - plugins does not allow this "trick".

nzakas commented 8 years ago

@michaelcontento no updates. This is quite a bit more difficult than Babel plugins because you can directly reference plugins in config files. If a shareable config depends on a plugin foo, and a user has manually installed that plugin as well, then when the end user references foo, what is the expected behavior? Which instance of the plugin should it refer to?

We will take another look after 2.0.0, by this is the main problem that needs resolving.

michaelcontento commented 8 years ago

Thank you for the honest response!

Is there a target release date for 2.0.0?

nzakas commented 8 years ago

Targeting January.

mysticatea commented 8 years ago

FYI: Shareable configs can include some plugins in those dependencies field.

For example, my config is depending on eslint-plugin-mysticatea, eslint-plugin-node, and eslint-plugin-react. But user (me!) does not need to install the plugins manually (e.g. package.json). Because npm does flatten dependencies, so user's project can require() the plugins.

For npm 2.x, we can use peerDependencies.

nzakas commented 8 years ago

@mysticatea that only works when the user hasnt also installed the plugins (maybe a different version). The risk is still there if you ever do that in the future.

Also keep in mind that npm 3 only flattens dependencies when there's no conflict amongst dependency versions for all dependents. If there is a conflict, the npm 2 approach is used.

boneskull commented 8 years ago

npm 3 doesn't solve this problem, the relationship between a config and a plugin remains a peer relationship. The fact that npm 3 flattens dependencies doesn't fundamentally change that relationship, is just an implementation quirk that allows dependencies to be treated as peers in certain situations. That's not a solution, is a gamble.

:+1: to this. ask anyone on npm team; if you need a package, you need to specify it as a dependency, flattened node_modules or no, or you're just asking for trouble

boneskull commented 8 years ago

if anyone wants to mess around with this, here's a quick-and-dirty monkeypatch to avoid having to npm install n packages to get one config and the plugins it uses.

callumlocke commented 8 years ago

Sorry to ping everyone, but has there been any progress on this?

I want to make a shareable config that extends xo and then adds a few custom in-house rules. Is the situation still that this is not possible?

IanVS commented 8 years ago

You can, but you need to treat xo as a peerDependency. Take a look at semistandard for an example of another config doing something similar.

nzakas commented 8 years ago

@callumlocke our current priority is making autofix better. When there are updates for this issue, we will comment.

markelog commented 7 years ago

Since this was possible in JSCS and it is needed for good amount of plugin writers (including me) i think we can treat it as priority.

If no one minds i'd like to talk about this on the next TSC meeting

nzakas commented 7 years ago

Per TSC meeting (21-Jul-16), it would be good to get an overview of the JSCS plugin use case plus a description of how JSCS currently solves this problem. Then, we need a proposal for how to overcome the problems discussed earlier in this thread (primarily, what happens when two different versions of a plugin are required by different configs used in the same project?).

markelog commented 7 years ago

Oh, there is bunch of issues then that, will try to document a proposal and tackle them when I finish with couple other issues.

Will put a JSCS label here, not sure if it needed or not though, so feel free to remove it if you feel it is not appropriate here

gaearon commented 7 years ago

Just to add another use case example for this. We recently released Create React App—a tool that creates React apps with no build configuration. Internally we use ESLint but we are intentionally keeping it non-configurable. (There is an escape hatch for people who want configuration: they can “eject” and then all config files get copied into their projects.)

This project also follows the “single dev dependency” approach because many people (especially those getting started) are tired of installing dependencies. I know it sounds funny but it’s true. That create-react-app gained 4k stars in 4 days speaks to the desire for zero configuration and having few dependencies. Of course this doesn’t work for everyone. But our initial users seem fairly happy with this setup.

The problem for us is that while command line linting works fine (since we’re able to specify a custom hidden config), editor integrations are not as smooth.

Having to do this in the generated project is already a tiny bit frustrating:

{
  "name": "my-app",
  "version": "0.0.1",
  "private": true,
  "devDependencies": {
    "react-scripts": "0.1.0"
  },
  "dependencies": {
    "react": "^15.2.1",
    "react-dom": "^15.2.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "./node_modules/react-scripts/config/eslint.js" // meh!
  }
}

The only reason eslintConfig is needed here is because editors don’t “know” about our custom config, and we have to make it discoverable.

But of course even that doesn’t solve the problem completely because of this issue. If the user is on npm 2, they’re out of luck completely because plugins are not getting discovered. Even if the user is on npm 3, it working feels like a fragile coincidence.

I think the bigger theme here is that as a community, we solved many problems of customizing configuration (and ESLint has been terrific at it), but maybe it’s time for the pendulum to swing back, and for us to start solving the problem of sane zeroconf defaults via opinionated packages.

gaearon commented 7 years ago

On a related note, I posted a proposal for a configurator field in package.json. It is intended for zero-conf tools like standard and create-react-app, and I think a convention like that could solve this issue. Please let me know what you think: https://github.com/facebookincubator/create-react-app/issues/215

stefanpenner commented 7 years ago

cross-posted as per: https://github.com/eslint/eslint/pull/6865#issuecomment-238923390

eslintrc at odds with npm link (and other node resolution friendly usages)

Some examples: (this may address)

Given 2 modules:

Working scenario:

  1. A depends on B
  2. B is a descendent of A on the file system
  3. when B "resolves" A's configuration it will walk up the file system and eventually find it
~/projects/A/
~/projects/A/node_modules/B

Failing scenario 1:

  1. A depends on B
  2. B is in an ancestor directories node_module of A on the file system
  3. when B "resolves" A's configuration it will walk up the file system and not find A, rather it may find something else
~/projects/A/
~/projects/node_modules/B

Failing scenario 2 (npm link):

  1. A depends on B
  2. B npm link'd to A on the file system
  3. when B "resolves" A's configuration it will walk up the file system and not find A, rather it may find something else
~/projects/A/
~/projects/node_modules/B -> ~/src/B

In node references to other code, and modules is always related to the including modules position on disk. Not relative to the invoker, or cwd. But fs/system calls are relative to cwd().

// foo/bar/baz.js
require('some-module'); // is a resolve relative to the current file
Cellule commented 7 years ago

For those interested, I found a workaround to have your plugins as dependencies in your shareable config. See https://github.com/Cellule/eslint-myrules/commit/83b3ac20a3b202f952726f57a5d5f680a83d64a9

Essentially this monkey patches eslint plugin resolution by resolving the plugin yourself therefore using your module's resolve paths. This works with npm link as well. It heavily depends on the current eslint file structure (works at least between versions 2.0.0 and 3.3.0) thus can be broken at any time.

Until this is officially supported I hope this might help someone else who like me wanted to alleviate the burden of settings up and maintaining eslint plugin versions.

jdalton commented 7 years ago

Just pinging in that I too was bit by this.

iamstarkov commented 7 years ago

I think community needs shareable configs to behave as usual npm packages—encapsulate deps with itself

nzakas commented 7 years ago

Thanks everyone for the feedback. We're aware of everyone's desire for this feature and plan on addressing it once we've finished up JSCS compatibility work. As such, I'm going to lock this issue because we understand the request and the use cases, and at this point we're just getting a bunch of "me too" comments.

nzakas commented 7 years ago

Summary

I know this is a popular issue, so I'm going to try to summarize where we are and why this is hard. The team is aware that this is a popular request, but unfortunately, popularity does not make the work any simpler or create the extra time needed to investigate how to solve this issue.

History

ESLint supported plugins first, and sometime later, shareable configs were created. At that time, we encouraged people to use peer dependencies through npm in order to define the relationships between shareable configs and plugins. This worked well because npm would install peer dependencies for you and it solved the deduping problem if, for example, a shareable config relied on a different version of a peer dependency than the one you already had installed.

Unfortunately, npm decided they would stop installing peer dependencies by default, instead relying on developers to manually install those dependencies. That was really the beginning of the request for shareable configs to be able to include plugins because developers didn't want to tell users of their shareable configs to manually install a bunch of other stuff. That's fair, but unfortunately, the relationship between shareable configs and plugins was designed, from the start, to be one of peer dependencies. With the npm changes, that relationship was no longer optimal.

Differences with npm Packages

There are a lot of comments that say something along the lines of, "well, I think shareable configs should just act like any other npm package in the way it defines its dependencies." Yes, it would be great if that were possible, however, shareable configs don't work the same way that other npm modules do (that's a feature, not a bug).

One of our early assumptions was that there could only be one instance of a plugin loaded per run. This is important because of the way we refer to plugin assets like rules. For instance, suppose you had eslint-config-myplugin, your config looks like this:

plugins:
  - myplugin
rules:
  myplugin/rule: "error"

First, we load the plugin based on myplugin in the plugins array, and then we reference a rule within the plugin as myplugin/rule. In order to use that convention, we must know with 100% certainty what myplugin refers to, and it must refer to the same thing across all configs in the project in order to ensure each config may override settings that other configs set. That means each config cannot search up the directory structure for plugins like npm package lookup does. We keep an internal cache of the plugins and use that for lookup.

The Hard Case

All that said, the reason this issue hasn't moved forward is because of one very serious edge case: what if two different versions of the same plugin are required by two different configs in a project? For instance, suppose:

In this case, we have several problems:

  1. How do you determine which eslint-plugin-foo version to load, given that the config file will just say foo?
  2. If we choose to load, say, the highest-up package by default (eslint-plugin-foo@1.2.0) and eslint-config-foo references a rule that doesn't exist until version 2.0.0, ESLint will throw an error because the rule is missing.
  3. If we instead choose to load the package closest to the config that first specified it, we have the same problem in the other direction: a rule the user's config references might have been removed or the options format might have changed, causing ESLint to error.
  4. If we choose to load both packages, then we'll risk naming collisions for rules and environments.

At this point, you'll likely arrive at a number of different solutions:

Throw an error if the same package is attempted to be loaded twiceT

This is deceptively difficult to do. First, we don't have a good way of knowing that we're attempting to load a different version of the plugin rather than just the same one we saw before. Second, it might be beyond the end user's capabilities to fix this problem because they may be using several shareable configs, each of which depends on a different version of the plugin. Penalizing the end user for including configs with contradictory plugin package versions seems like the wrong choice.

Figure out a way to load two different versions of the same package

First, that would mean figuring out a way to load two different versions of the same package, which Node.js isn't designed to do by default, so we'd have to go crawling around the filesystem to do this. Crawling around the filesystem is made much more difficult due to npm node_modules flattening, so we couldn't really know the correct places to look, which would mean a lot of searching around for packages. We'd also need to come up with some way to ensure the end users knows which version of plugin they are configuring at any given time, which likely means we'd need a different naming scheme for configuring plugins that were included via shareable config vs. installed by the user. That's also difficult because shareable configs are treated like any other config inside of ESLint, so that would also be a lot of work.

Create a new shareable thing that works the way we want it to

Of course, when what you created isn't working the way you wanted, it's logical to stop and consider if you want to create a new type of thing that can work the way you want. The big problem here is that we have a big ecosystem of already-existing shareable configs and plugins, and would we really want to impose the burden of creating something new to fulfill that need?

Where We Are

I hope that helps explain why this issue hasn't moved much. Despite the desire of many people to figure out how to make this work, at the end of the day, there are enough complexities that it will take a significant amount of time from someone (on the team or off) to really dig in and try to solve this. As the ESLint team is a group of unpaid volunteers, no one has had enough time or interest to dig into this problem.

Next Steps

If you are interested in pitching in to solve this problem, we are open to hearing design proposals. The only constraint is that any design must address the hard problem of having two different parts of the project include different versions of the same plugin. All of the proposals to this point, both in this thread and through various pull requests, have not addressed it and so cannot be considered.

I'm unlocking this thread to allow for designs to be posted but if I see the thread fill up with +1s and other useless comments, I'll lock it again.

What we need is someone or a group of someones who are willing to dig and do the research and prototyping necessary to solve this problem. If that's you, please let us know.

feross commented 7 years ago

@nzakas

Figure out a way to load two different versions of the same package

First, that would mean figuring out a way to load two different versions of the same package, which Node.js isn't designed to do by default, so we'd have to go crawling around the filesystem to do this.

This issue could potentially be addressed by allowing configs to include code, like this:

module.exports = {
  plugins: {
    foo: require('eslint-plugin-foo')
  },
  rules: {
    'foo/rule': 'error'
  }
}

This way, each config can use the exact plugin version that they list in their package.json dependencies. No need to crawl around the filesystem.

We'd also need to come up with some way to ensure the end users knows which version of plugin they are configuring at any given time.

We could solve this by saying: you can only configure plugin rules if you require that plugin in your config. That way it's clear what version of the plugin you're configuring.

If you re-use a plugin rule that was already defined in an earlier config, then the later rule takes precedence (like how it works today). Note: Both the rule's setting and the rule code from the later config/plugin will take precedence.

Optional convenience feature: We could consider relaxing the rule about depending on the plugin, if the config is just disabling rules from that plugin.

One downside of this approach is that it only works with JS configs, not JSON or YAML.

stoikerty commented 7 years ago

@feross Essentially you're specifying a path, right? I don't see why that would have to be restricted to javascript. Babel does something similar where you can specify presets & plugin paths.

// how babel reads it
{
  "presets": [
    "/my_project/node_modules/babel-preset-es2015/lib/index.js",
    "/my_project/node_modules/babel-preset-react/lib/index.js"
  ],
  "plugins": [
    "/my_project/node_modules/babel-plugin-transform-flow-strip-types/lib/index.js"
  ]
}

// can get compiled into a babel-readable config from any other language, example js:
  presets: [
    require.resolve('babel-preset-es2015'),
    require.resolve('babel-preset-react')
  ],
  plugins: [
    require.resolve('babel-plugin-transform-flow-strip-types')
  ]