What's the difference between plugins and extends in eslint? - javascript

I don't understand why we have plugins and extends. What is the difference between them and do I need one or the other?

extends uses a config file which applies set of rules when you add that to the extends options. A plugin on the other hand provides you with a set of rules that you can individually apply depending on your need. Just having a plugin does not enforce any rule. You have to choose which rules you need.
A plugin may provide you with zero, one, or more configuration files. If the plugin provides configuration file, then you can load that in your extends section after adding the plugin in the plugins section.
So essentially, plugins given you some rules that have been coded and you can choose which ones are relevant. It may also provide config files to apply rules that the authors think are logically grouped/relevant but providing a config file is not mandatory for a plugin. extends, on the other hand, provides you the ability to apply rules in bulk based on config file specifications.
Example Plugin - eslint-plugin-react
{
"plugins": [
"react"
],
"extends": [
"eslint:recommended",
"plugin:react/recommended"
]
}
Example Config - eslint-config-google
{
"extends": [
"google"
]
}

In addition to shmit's good answer:
extends
is about extending configurations in general, not only plugins. Potential values are:
"eslint:recommended"
"eslint:all"
Shareable configuration from npm package (eslint-config-xxx or scoped name)
Plugin configuration from npm package (eslint-plugin-xxx or scoped name)
Another configuration file, like "./my/path/.eslintrc.js"
Plugin notation: plugin:<package name>/<configuration name>, e.g. for eslint-plugin-react:
"extends": ["plugin:react/recommended"]
By extending from a plugin config, we can get recommended rules without adding them manually.
plugins
A plugin is a special eslint npm package, that provides additional rule definitions (rules), environments, processors and configs for different configurations of recommended / default rule values.
The plugins property in .eslintrc.js is merely a flag to enable a given plugin after installation with npm i. We now can refer to the plugin's rules, but have to set all rules values manually.
Think of plugins as a way to activate a plugin - to use its rules, you need to add the plugin once in the chain in every case.
plugins is not needed in your own config, if it is already defined in a configuration, that you extend from by extends.
Example:
eslint-plugin-react already contains plugins: [ 'react' ], hence this entry is not needed anymore in own config and plugin rules can be used directly.

So found out that plugins add extra capabilities and extends gives you a baseline on which to add your own custom rules. Thanks to my friend Oliver for helping me answer this question!

Related

Change the Default File Resolution in JavaScript

In a JavaScript project, a file titled index.js can be imported as such:
import SomeComponent from 'components/some-component'
Rather than having to specify index.js.
In my project, I prefer to use a different naming convention: some-component.component.js. This way I can tell what the file is from a glance (rather than having a million index.js).
What I'm trying to achieve is having this same import pattern happen for files with the pattern *.component.js. In other words:
import SomeComponent from 'components/some-component'
Rather than
import SomeComponent from 'components/some-component/some-component.component.js'
I have the following (abbreviated) jsconfig.json:
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"*": ["./*"]
}
}
}
Any ideas on how this could be achieved?
Thanks in advance!
Short answer: you can't customize the import resolution using a function (or RegExp). For a web project, you may use a tool like Webpack with a custom loader. But, that is a Webpack specific solution that will not work with other tools (like doing Ctrl-click in VSCode, or using TSC directly).
Long answer:
The resolution of the JavaScript files depends on the tools you use. The standards changed a little bit over the years, and while things are converging to ESM there are still a lot of inconsistencies.
The TypeScript module resolution is described here: https://www.typescriptlang.org/docs/handbook/module-resolution.html
You can have a baseUrl, merge multiple directories as one (rootDirs), or have a limited wildcard path mapping (https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping). You already used it in your question, so probably I'm not telling you anything new. But, that's where the TS configuration options end. (you can customize the compilation module resolution using the TSC API, but not from the CLI/tools config options)
NodeJS can hook a resolution mechanism for modules. For example, Yarn 2+ does that to support a feature called PNP (https://yarnpkg.com/features/pnp). However, TypeScript still needs to resolve types and it will not use your custom loading mechanism for that.
The situation with other tools like Webpack is similar.
The module resolution of Node and TypeScript are specific to those tools. The ESM standard is even more strict: the from part must be an absolute or relative URL, or a "bare identifier" that you need to map using an import map. In other words, you can't have a custom algorithm to resolve paths. The justification for that is described in the Import Maps standard repo: https://github.com/WICG/import-maps#a-programmable-resolution-hook
Conclusion: if you really-really want to do a custom module resolution, you may be able to create your own CLI using the TSC API. That may not work with other tools, and is probably easier to get used to another naming convention.

What "plugins" property in .eslintrc does?

module.exports = {
root: true,
parser: '#typescript-eslint/parser',
plugins: ['#typescript-eslint'],
extends: [
'eslint:recommended',
'plugin:#typescript-eslint/recommended',
],
};
Whenever I add or remove this line: plugins: ['#typescript-eslint']
eslint seems to behave the same. What exactly plugins property does and when using it is required?
This question is pretty straight forward if you think about what a plugin is.
The docs don't really do a good job of just out-right saying what an ESLint plugin is, though if you read through the docs (https://eslint.org/docs/user-guide/configuring), then it's pretty trivial to figure out:
ESLint supports the use of third-party plugins
When using rules, environments or configs defined by plugins. Before using the plugin, you have to install it using npm.
So a plugin is a 3rd party module that can define rules, environments, or configs.
So to answer your question:
What exactly plugins property does [sic]
the plugins property tells ESLint what plugins you want to use
and when using it is required? [sic]
when you use something from a plugin, you must first tell ESLint about it with the plugins property.
Plugins seem to work anyway when this field is omitted
If you use the extends option with the syntax plugin:<plugin>/<config>, then ESLint will load a specific file from the plugin ahead of time.
Why? Because this allows a plugin to provide a config and reduce the amount of config you need. A plugin's config can provide the plugins option for you, meaning you don't need to do it yourself.
I was also curious about what is parser and plugin
From documentation
"For example, once this parser successfully produces an AST for the TypeScript source code, it might well contain some information which simply does not exist in a standard JavaScript context, such as the data for a TypeScript-specific construct, like an interface.
The core rules built into ESLint, such as indent have no knowledge of such constructs, so it is impossible to expect them to work out of the box with them.
Instead, you also need to make use of one more plugins which will add or extend rules with TypeScript-specific features."
This helped me to understand it better.

babel-plugin-react-css-modules does not change class name in style tag

I want to use babel-plugin-react-css-modules to create unique class names, so I did a PoC: I ejected create-ract-app, then I add babel-plugin-react-css-modules to plugins in package.json:
"babel": {
"presets": [
"react-app"
],
"plugins": [
"babel-plugin-react-css-modules"
]
},
When I start application I see that class in HTML is set correctly, but inside <style> tag it's regular .App name:
Am I missing something? I think it should be pretty easy to set up and I make some stupid mistake I can't see. Thank you in advance for every answer.
Edit: App.js code:
import React from 'react';
import './App.css';
function App() {
return (
<div styleName="App">
Test app
</div>
);
}
export default App;
Rant: One of the many reasons I'm against the create-react-app (CRA) is because of how inflexible it can be when it comes to customization. If you're looking to include features, I'd recommend creating your own webpack configuration. Why? You'll become more familiar with Webpack, how to configure it, how to work within its limits, and how to add/change/adjust packages to your needs.
That said, the CRA comes preconfigured with both global and local (module) CSS imports and it conflicts with babel-plugin-react-css-modules (BPRCM). The CRA expects .css files without module.css to be normal global imports. Meanwhile, BPRCM expects .css files to be local imports. Not only is that a conflict, but the localIdentNames are mismatched. By default, CRA looks for App.module.css where module.css specifies it's a modulated import, like so: import { App } from "./App.module.css";. As such it assigns localIdentNames as [file/folder]_[local]_[hash] as mentioned here, here and within an ejected config/webpack.config.js (Lines 432-456 -- it utilizes the react-dev-utils/getCSSModuleLocalIdent package).
In order to resolve these conflicts, you'll need to establish a localIdentName (by default BPCRM uses this generateScopedName: [path]___[name]__[local]___[hash:base64:5] (4th option in the table), so I'll be using it for my example below), then you'll need to add BPRCM to the webpack.config.js under babel-loader because the CRA doesn't look for a babel configuration, then you'll have to remove the CSS module imports rule, and lastly, you'll need to add some options to their global CSS rule to make it local and utilize the localIdentName.
Here's a working example: https://github.com/mattcarlotta/cra-css-modules
To utilize the repo above:
open a terminal window on the ~/Desktop and clone the repo: git clone git#github.com:mattcarlotta/cra-css-modules.git
cd into the cra-css-modules folder and type yarn install or npm install
type yarn start or npm start to run the example
What I changed:
Declared a localIdentName to match BPRCM's default scoped class name (you can change this name to whatever you like and it'll still work with the example repo above).
Added BPRCM to the babel-loader rule.
Added a modules configuration to switch the global CSS rule to a local CSS rule
Removed the local modules CSS rule.
Changed all classNames to styleName in the App.js file.

Figuring out JavaScript libraries for Vim autocompletion with TernJS in .tern_project file

I love vim and want to keep using it to do web development although I am struggling setting up my .tern_project file with the correct libraries I need to do autocompletion. I am relatively new to JavaScript but what I have so far is making it a lot easier to learn.
There aren't many examples that I could find and I have tried to read the documentation but I do not know enough for it to be helpful. So far my .tern_project file looks like this:
{
"libs": [
"browser",
"ecma6"
],
"plugins": {
"requirejs": {
"baseURL": "./",
"paths": {}
}
}
}
I don't really know what the plugins do but I left them in for now, in libs the ecma6 really helped me with all the array methods (ie. forEach etc.). Now my question is how do I add stuff like console.table() to autocomplete?
Which library do I need to add to the .tern_project file?
Also, I am open to suggestions for better web development environments.
At this point all you've got is tern's default completion!!! Your .tern_project does not have any impact on completions that tern suggests because tern configuration file is .tern-project; Its Dash not underscore. so first rename it.
.tern-project is a json configuration file which tells tern what completions it should suggest through two property: libs and plugins.
plugins aren't much different from libs they tells tern to also suggest these completions you specify in addition to libs.
For example in your .tern-project file you've choose to use requirejs plugin. so if you use requirejs library which is a module loader and helps with writing client side modular code, then it completes variables, functions and methods from other modules.
console is a node's global. and to complete node stuff you should add node plugin. so your .tern-project file should be something like:
{
"libs": [
"browser",
"ecmascript"
],
"plugins": {
"node": {}
}
}
Note that i've used ecmascript in place of ecma6. in previous versions tern had ecma5 and ecma6 libs but in latest versions these two got combined in one lib named: ecmascript.
List of available tern libs:
browser
chai
ecmascript
jquery
react
underscore
You could always get an updated list of libs from tern js repository defs directory
List of available tern plugins :
angular
commonjs
complete_strings
doc_comment
es_modules
node
requirejs
webpack
You could always get an updated list of plugins from tern js repository plugin directory
As your javascript skills grow go add and play with libs and plugins and see what completions you get. Also note that you could have multiple .tern-project file. Tern will always search upward to root directory and uses the closest one. so you could configure completions on a project basis.

Using the whitelist option with Babel's external-helpers

I'm trying to use Rollup with Babel's external-helpers. It works, but it's dropping a bunch of babel helpers which I don't even need, for example asyncGenerator.
The docs show a whitelist option but I can't get it to work
rollup.rollup({
entry: 'src/buttonDropdown.es6',
plugins: [
babel({
presets: ['react', ['es2015', { modules: false }], 'stage-2'],
plugins: [['external-helpers', { whitelist: ['asyncGenerator'] }]]
})
]
})
The above has no effect: all Babel helpers are still dropped into my resulting bundle.
What is the correct way of using this feature, and is there a full list of which helpers' names the whitelist array takes?
Or is there some other Rollup plugin I should be using with Rollup to automatically "tree shake" the babel external helpers.
Problem
The babel-plugin-external-helpers plugin is not responsible for injecting those dependencies in the final bundle.
The only thing it controls is that how the generated code will access those functions. For example:
classCallCheck(this, Foo);
// or
babelHelpers.classCallCheck(this, Foo);
It is needed so all rollup-plugin-babel needs to do is to inject babelHelpers in every module.
The documentation is misleading, the whitelist options is not on the external-helpers plugin. It's on the completely separate module and command line tool called babel-external-helpers, which is actually responsible for generating babelHelpers.
It's rollup-plugin-babel what is injecting babelHelpers. And does it using a trick to modularize the final code. It calls babel-external-helpers to generate the helpers, and ignores the whitelist parameter. See my issue requesting to expose an option.
This approach is correct, because rollup will tree-shake the unused helper functions. However some of the helpers (like asyncGenerator) are written in a way that is hard to detect if the initialization has any side effects, thus preventing removal during tree-shaking.
Workaround
I forked rollup-plugin-babel and created a PR which exposes the whitelist option of building babelHelpers in the plugin's options. It can be used this way:
require("rollup").rollup({
entry: "./src/main.js",
plugins: [
require("rollup-plugin-babel")({
"presets": [["es2015", { "modules": false }]],
"plugins": ["external-helpers"],
"externalHelpersWhitelist": ['classCallCheck', 'inherits', 'possibleConstructorReturn']
})
]
}).then(bundle => {
var result = bundle.generate({
format: 'iife'
});
require("fs").writeFileSync("./dist/bundle.js", result.code);
}).then(null, err => console.error(err));
Note that I didn't publish distribution version on npm, you will have to clone the git repo and build it using rollup -c.
Solution
In my opinion the right solution would be to somehow detect or tell rollup that those exports are pure, so can be removed by tree shaking. I will start a discussion about it on github after doing some research.
As I have found in this particular issue in the GitHub page.
The Babel member Hzoo suggests that
Right now the intention of the preset is to allow people to use it without customization - if you want to modify it then you'll have to
just define plugins yourself or make your own preset.
But still if you want to exclude a specific plugin from the default preset then here are some steps.
As suggested by Krucher you can create a fork to the undesirable plugin in the following way
First one is by forking technique
"babel": {
"presets": [
"es2015"
],
"disablePlugins": [
"babel-plugin-transform-es2015-modules-commonjs"
]
}
But if two or more people want to include the es2015-with-commonjs then it would be a problem.For that you have to define your own preset or extend the preset of that module.
The second method would involve the tree-shaking as shown in this article done by Dr. Axel Rauschmayer.
According to the article webpack2 is used with the Babel6.
This helps in removal of the unwanted imports that might have been used anywhere in the project in two ways
First, all ES6 module files are combined into a single bundle file. In that file, exports that were not imported anywhere are not exported, anymore.
Second, the bundle is minified, while eliminating dead code. Therefore, entities that are neither exported nor used inside their modules do not appear in the minified bundle. Without the first step, dead code elimination would never remove exports (registering an export keeps it alive).
Other details can be found in the article.
Simple implemetation is referred as here.
The third method involves creating your own preset for the particular module.
Creating aplugin and greating your own preset can be implemented according to the documentation here
Also as an extra tip you should also use babel-plugin-transforn-runtime
If any of your modules have an external dependancy,the bundle as a whole will have the same external dependancy whether or not you actually used it which may have some side-effects.
There are also a lot of issues with tree shaking of rollup.js as seen in this article
Also as shown in the presets documentation
Enabled by default
These plugins have no effect anymore, as a newer babylon version enabled them by default
- async-functions (since babylon 6.9.1)
- exponentiation-operator (since babylon 6.9.1)
- trailing-function-commas (since babylon 6.9.1)**
Also the concept of whitelisting and blacklisting the plugins has benn brilliantly explained by loganfsmyth here in this thread.
you can pass a whitelist option to specify specific transformations to run, or a blacklist to specific transformations to disable.
You cannot blacklist specific plugins, but you may list only the plugins you want, excluding the ones you do not wish to run.
Update :
According to this article here is an important update -
"The --external-helpers option is now a plugin. To avoid repeated inclusion of Babel’s helper functions, you’ll now need to install and apply the babel-plugin-transform-runtime package, and then require the babel-runtime package within your code (yes, even if you’re using the polyfill)."
Hope this may solve your problem
Hope it may help you.

Categories

Resources