I've installed eslint-config-airbnb that is supposed to pre configure ESLINT for React:
Our default export contains all of our ESLint rules, including
ECMAScript 6+ and React. It requires eslint, eslint-plugin-import,
eslint-plugin-react, and eslint-plugin-jsx-a11y.
My .eslintrc extending its configuration:
{ "extends": "eslint-config-airbnb",
"env": {
"browser": true,
"node": true,
"mocha": true
},
"rules": {
"new-cap": [2, { "capIsNewExceptions": ["List", "Map", "Set"] }],
"react/no-multi-comp": 0,
"import/default": 0,
"import/no-duplicates": 0,
"import/named": 0,
"import/namespace": 0,
"import/no-unresolved": 0,
"import/no-named-as-default": 2,
"comma-dangle": 0, // not sure why airbnb turned this on. gross!
"indent": [2, 2, {"SwitchCase": 1}],
"no-console": 0,
"no-alert": 0,
"linebreak-style": 0
},
"plugins": [
"react", "import"
],
"settings": {
"import/parser": "babel-eslint",
"import/resolve": {
"moduleDirectory": ["node_modules", "src"]
}
},
"globals": {
"__DEVELOPMENT__": true,
"__CLIENT__": true,
"__SERVER__": true,
"__DISABLE_SSR__": true,
"__DEVTOOLS__": true,
"socket": true,
"webpackIsomorphicTools": true
}
}
Unfortunatelly I'm getting the following error when linting a .js file with React JSX code inside it:
error JSX not allowed in files with extension '.js' react/jsx-filename-extension
Wasn't eslint-config-airbnb configured react to support JSX already, as stated ?
What should be done to remove that error ?
Either change your file extensions to .jsx as mentioned or disable the jsx-filename-extension rule. You can add the following to your config to allow .js extensions for JSX.
"rules": {
"react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }],
}
It's work for me. hope to help you.
disable jsx-filename-extension in .eslintrc:
"rules": {
"react/jsx-filename-extension": [0]
}
in webpack.config.js:
resolve: {
extensions: ['.js', '.jsx']
},
Call me a dummy if it does not work for you
"rules": {
"react/jsx-filename-extension": [0],
"import/extensions": "off"
}
If you don't want to change your file extension, you can export a function that returns jsx, and then import and call that function in your js file.
// greeter.jsx
import React from 'react';
export default name => (
<div>
{`Hello, ${name}!`}
</div>
);
and then
// index.js
import ReactDOM from 'react-dom';
import greeter from './components/greeter';
const main = document.getElementsByTagName('main')[0];
ReactDOM.render(greeter('World'), main);
Following React documentation:
Each JSX element is just syntactic sugar for calling React.createElement(component, props, ...children).
Following Airbnb's style guide:
Do not use React.createElement unless you’re initializing the app from a file that is not JSX.
You could do this:
//index.js
const app = React.createElement(App);
ReactDOM.render(app, document.getElementById('root'));
To expound on Martin's answer, it seems that it is not possible, currently, to use JSX in React Native. A PR was created but reverted due to concerns about fragmentation and unknown consequences of having things like index.ios.jsx. I'm not sure how AirBnb works around this or if they do, but I have used basically the same rules block as Martin.
For future readers who want to write jsx in .js files:
Install npm i react react-dom even if you think you're not writing react.
Install npm i -D #babel/preset-react
In babel config: plugins: ['#babel/plugin-transform-react-jsx']
you should setup module.rules for /\.jsx?$/ files with babel-loader (so you will need to install npm i -D babel-loader too)
Related
TL;DR
How can you tell SWC to compile CSS files imported in React components?
How can you tell SWC to compile absolute imports in tests and in React components?
Here is a minimal reproducible example.
Context
We're migrating from Babel to SWC. (I asked a question a little while ago. I'm improving on that question's answer.)
We're migrated the command from:
"test": "NODE_ENV=test riteway -r #babel/register 'src/**/*.test.js' | tap-nirvana",
to
"test": "SWC_NODE_PROJECT=./jsconfig.json riteway -r #swc-node/register src/**/*.test.js | tap-nirvana",
where the jsconfig.json looks like this:
{
"compilerOptions": {
"allowJs": true,
"baseUrl": "./src",
"jsx": "react-jsx"
}
}
If we write try to compile a test for a self-contained component (no absolute imports, no CSS) it works:
import { describe } from 'riteway';
import render from 'riteway/render-component';
function HomePageComponent({ user: { email } }) {
return <p>{email}</p>;
}
describe('home page component', async assert => {
const user = { email: 'foo' };
const $ = render(<HomePageComponent user={user} />);
assert({
given: 'a user',
should: 'render its email',
actual: $('p').text(),
expected: user.email,
});
});
The test compiles fine.
With Babel we had a .babelrc like this:
{
"env": {
"test": {
"plugins": [
[
"module-resolver",
{
"root": [
"."
],
"alias": {
"components": "./src/components",
"config": "./src/config",
"features": "./src/features",
"hocs": "./src/hocs",
"hooks": "./src/hooks",
"pages": "./src/pages",
"redux": "./src/redux",
"styles": "./src/styles",
"tests": "./src/tests",
"utils": "./src/utils"
}
}
]
]
}
},
"presets": [
[
"next/babel",
{
"ramda": {}
}
]
],
"plugins": [
["styled-components", { "ssr": true }]
]
}
Where the styles where taken care of by styled-components and the absolute imports where defined via the module-resolver plugin. (We switched away from styled-components to CSS modules, which is why we import from .module.css CSS files. Anyways ...)
If we write the test how we wanted to write it with their actual imports like this:
import { describe } from 'riteway';
import render from 'riteway/render-component';
import { createPopulatedUserProfile } from 'user-profile/user-profile-factories';
import HomePageComponent from './home-page-component';
describe('home page component', async assert => {
const user = createPopulatedUserProfile();
const $ = render(<HomePageComponent user={user} />);
assert({
given: 'a user',
should: 'render its email',
actual: $('p').text(),
expected: user.email,
});
});
It fails with:
$ SWC_NODE_PROJECT=./jsconfig.json riteway -r #swc-node/register src/features/home/home-page-component.test.js | tap-nirvana
/Users/janhesters/dev/my-project/src/features/home/home.module.css:1
(function (exports, require, module, __filename, __dirname) { .container {
^
SyntaxError: Unexpected token '.'
when we leave in the CSS import in home-page-component.js, or with:
$ SWC_NODE_PROJECT=./jsconfig.json riteway -r #swc-node/register src/features/home/home-page-component.test.js | tap-nirvana
node:internal/modules/cjs/loader:936
throw err;
^
Error: Cannot find module 'user-profile/user-profile-factories'
Require stack:
- /Users/janhesters/dev/my-project/src/features/home/home-page-component.test.js
- /Users/janhesters/dev/my-project/node_modules/riteway/bin/riteway
respectively, when we get rid of the CSS import.
How can we help SWC understand CSS (or mock CSS modules) and how can we help it understand absolute imports?
We already set the baseUrl in jsconfig.json ...
About absolute path
You already add baseUrl in the jsconfig.json file but didn't add the paths, you should modify your config file like mine:
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"#screens": ["./screens"],
"#shared": ["./shared"],
"#shared/*": ["./shared/*"]
},
The paths are the alias of module-resolver, and I guess your root shouldn't be ".", it should be exactly like your jsconfig.json file, I mean the baseUrl value.
"plugins": [
[
"module-resolver",
{
"root": ["./src"],
"extensions": [".ts", ".tsx", ".js", ".jsx", ".json"],
"alias": {
"#screens": "./src/screens",
"#shared": "./src/shared"
}
}
]
],
If you have Webpack, you should have alias config in your resolve key off Webpack config object, like mine:
const resolve = {
extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
alias: {
'#screens': path.join(__dirname, 'src/screens/'),
'#shared': path.join(__dirname, 'src/shared/'),
},
modules: ['src', 'node_modules'],
descriptionFiles: ['package.json'],
};
About CSS
Actually, you are using CSS file as CSS-Modules not like recommended NexJS doc, in docs developer should import CSS like import './styles.css' and then use it as string in JSX like <div className="main"
But
You are importing it like a module (CSS-Module):
// you did it
import styles from './styles.css';
<div className={styles.main}
As you know it is built-in support by this doc, it is supported by NextJS, but SWC cannot understand it. I put in hours to find a way for it, but it seems SWC doesn't support CSS-Module yet. you should create your own plugin for SWC to support CSS-Module.
How can we help SWC understand CSS (or mock CSS modules)? - SWC doesn't understand css natively, and neither did Babel. As you noted, when you were using Babel, the plugin styled-components took care of this. You'll need to do the same with SWC. I can't find an existing SWC plugin that does this, but you can roll your own. Obviously this is a pain, but such is the cost of using new tooling.
How can we help SWC understand absolute imports? - The .swrc options for baseUrl and paths should do what you want, but that, too, seems to have some issues.
You may have better luck creating issues directly in the #swc-node GitHub repo, but given the comments there it feels like you might be SOL for a while. Might be faster/easier to rewrite your tests using one of the libraries that Next supports out of the box.
I was able to solve all issues without writing any plugins. I pushed the solution to my example repo from the question.
Firstly, use the official SWC project's register. This means, you have to compile your tests like this:
"test": "riteway -r #swc/register 'src/**/*.test.js' | tap-nirvana",
You can install it by running:
yarn add --dev #swc/core #swc/register
We need this version of register, because it takes into account an .swcrc file, which the other one did not.
Secondly, you need both "path" and "baseUrl" to fix the absolute imports because for some reason SWC doesn't infer all paths with only a "baseUrl" provided. And you need to adapt your .swcrc to handle React.
{
"jsc": {
"baseUrl": "./src",
"paths": {
"*.css": ["utils/identity-object-proxy.js"],
"utils/*": ["utils/*"]
},
"parser": {
"jsx": true,
"syntax": "ecmascript"
},
"transform": {
"react": {
"runtime": "automatic"
}
}
},
"module": {
"type": "commonjs"
}
}
Thirdly, to solve .css you need to remap all imports to .css files to an identity object proxy, which you can see in the .swcrc example above. An identity object proxy is an object that, when you reference any property, returns the stringified key that you're trying to reference. You can create one yourself like this:
const identityObjectProxy = new Proxy(
{},
{
get: function getter(target, key) {
if (key === '__esModule') {
return false;
}
return key;
},
},
);
export default identityObjectProxy;
For the .css remap to go into effect you need to make all your imports to .css files absolute imports. (import styles from './styles.module.css won't work!)
I am new in svelte. I trying to add ESLint to my svelte project.
My eslintrc.json:
{
"env": {
"es6": true,
"browser": true,
"node": true
},
"extends": [
"standard", "airbnb-base/legacy"
],
"plugins": [
"svelte3"
],
"overrides": [
{
"files": ["**/*.svelte"],
"processor": "svelte3/svelte3"
}
],
"parser": "babel-eslint",
"parserOptions": {
"ecmaVersion": 2019,
"sourceType": "module"
}
}
It works, but linter rules does not support sass syntax. I have this error.
How can I fix it?
If you're using some sort of preprocessor on the component styles, then it's likely that when this plugin calls the Svelte compiler on your component, it will throw an exception. In a perfect world, this plugin would be able to apply the preprocessor to the component and then use source maps to translate any warnings back to the original source. In the current reality, however, you can instead simply disregard styles written in anything other than standard CSS. You won't get warnings about the styles from the linter, but your application will still use them (of course) and compiler warnings will still appear in your build logs.
This setting can be given a function that accepts an object of attributes on a tag (like that passed to a Svelte preprocessor) and returns whether to ignore the style block for the purposes of linting.
The default is to not ignore any styles.
settings: {
'svelte3/ignore-styles': () => true
}
I faced the same issue and there is no documentation on how to use the svelte3/ignore-styles in settings to ignore the styles in eslint. Here is how i fixed the issue -
I Changed .eslintrc to .eslintrc.js in order to use the ignore rule as a function -
module.exports = {
...
"rules" : {
},
"settings": {
"svelte3/ignore-styles": () => true
}
}
I have a lerna monorepo containing lots of packages.
I'm trying to achieve the following:
Ensure that VSCode provides the correct import suggestions (based on package names, not on relative paths) from one package to another.
Ensure that I can 'Open Definition' of one of these imports and be taken to the src of that file.
For 1. I mean that if I am navigating code within package-a and I start to type a function exported by package-b, I get a suggestion that will trigger the adding of an import: `import { example } from 'package-b'.
For 2. I mean that if I alt/click on the name of a function exported by 'package-b' while navigating the file from a different package that has imported it, I am taken to '/packages/namespace/package/b/src/file-that-contains-function.js',
My (lerna) monorepo is structured as standard, for example here is a 'components' package that is published as #namespace/components.
- packages
- components
- package.json
- node_modules
- src
- index.js
- components
- Button
- index.js
- Button.js
- es
- index.js
- components
- Button
- index.js
- Button.js
Note that each component is represented by a directory so that it can contain other components if necessary. In this example, packages/components/index exports Button as a named export. Files are transpiled to the package's /es/ directory.
By default, VSCode provides autosuggestions for imports, but it is confused by this structure and, for if a different package in the monorepo needs to use Button for example, will autosuggest all of the following import paths:
packages/components/src/index.js
packages/components/src/Button/index.js
packages/components/src/Button/Button.js
packages/components/es/index.js
packages/components/es/Button/index.js
packages/components/es/Button/Button.js
However none of these are the appropriate, because they will be rendered as relative paths from the importing file to the imported file. In this case, the following import is the correct import:
import { Button } from '#namespace/components'
Adding excludes to the project's jsconfig.json has no effect on the suggested paths, and doesn't even remove the suggestions at /es/*:
{
"compilerOptions": {
"target": "es6",
},
"exclude": [
"**/dist/*",
"**/coverage/*",
"**/lib/*",
"**/public/*",
"**/es/*"
]
}
Explicitly adding paths using the "compilerOptions" also fails to set up the correct relationship between the files:
{
"compilerOptions": {
"target": "es6",
"baseUrl": ".",
"paths": {
"#namespace/components/*": [
"./packages/namespace-components/src/*.js"
]
}
},
}
At present Cmd/Clicking on an import from a different package fails to open anything (no definition is found).
How should I configure VSCode so that:
VSCode autosuggests imports from other packages in the monorepo using the namespaced package as the import value.
Using 'Open Definition' takes me to the src of that file.
As requested, I have a single babel config in the root:
const { extendBabelConfig } = require(`./packages/example/src`)
const config = extendBabelConfig({
// Allow local .babelrc.js files to be loaded first as overrides
babelrcRoots: [`packages/*`],
})
module.exports = config
Which extends:
const presets = [
[
`#babel/preset-env`,
{
loose: true,
modules: false,
useBuiltIns: `entry`,
shippedProposals: true,
targets: {
browsers: [`>0.25%`, `not dead`],
},
},
],
[
`#babel/preset-react`,
{
useBuiltIns: true,
modules: false,
pragma: `React.createElement`,
},
],
]
const plugins = [
`#babel/plugin-transform-object-assign`,
[
`babel-plugin-styled-components`,
{
displayName: true,
},
],
[
`#babel/plugin-proposal-class-properties`,
{
loose: true,
},
],
`#babel/plugin-syntax-dynamic-import`,
[
`#babel/plugin-transform-runtime`,
{
helpers: true,
regenerator: true,
},
],
]
// By default we build without transpiling modules so that Webpack can perform
// tree shaking. However Jest cannot handle ES6 imports becuase it runs on
// babel, so we need to transpile imports when running with jest.
if (process.env.UNDER_TEST === `1`) {
// eslint-disable-next-line no-console
console.log(`Running under test, so transpiling imports`)
plugins.push(`#babel/plugin-transform-modules-commonjs`)
}
const config = {
presets,
plugins,
}
module.exports = config
In your case, I would make use of lerna in combination with yarn workspaces.
When running yarn install, all your packages are linked under your #namespace in a global node_modules folder. With that, you get IntelliSense.
I've set up an example repository here: https://github.com/flolude/stackoverflow-lerna-monorepo-vscode-intellisense
You just need to add "useWorkspaces": "true" to your lerna.json
lerna.json
{
"packages": ["packages/*"],
"version": "0.0.0",
"useWorkspaces": "true"
}
And the rest is just propper naming:
global package.json
{
"name": "namespace",
// ...
}
package.json of your component package
{
"name": "#namespace/components",
"main": "src/index.js",
// ...
}
package.json of the package that imports the components
{
"name": "#namespace/components",
"main": "src/index.js",
"dependencies": {
"#namespace/components":"0.0.0"
}
// ...
}
Then you can do the following:
import { Component1 } from '#namespace/components';
// your logic
Automatically Importing from #namespace
Unfortunately, I couldn't find a way to make this work in VSCode with a Javascript Monorepo. But there are some things you can do:
Use Typescript (tutorial, other tutorial)
Use module-alias
Add import {} from '#namespace/components' to the top of your file
Use Auto Import Extension
Edit: This is broken with the latest version of VSCode.
I finally managed to get this working reliably. You need to create a separate jsconfig.js for every package in your monorepo, for example:
{monorepo root}/packages/some-package/jsconfig.json:
{
"compilerOptions": {
"target": "es6",
"jsx": "preserve",
"module": "commonjs"
},
"include": ["src/**/*.js"],
"exclude": ["src/index.js"]
}
Note that I've excluded the src/index.js file so it doesn't get offered as an import suggestion from within that package.
This setup appears to achieve:
Intellisense import suggestions from packages instead of using relative paths.
Go to definition to source of other packages in the monorepo.
VSCode has been pretty flaky of late, but it seems to be working.
Note this is working for a JavaScript-only monorepo (not Typescript).
I'm quite new to react and webpack but have used eslint and javascript for years but this is really driving me insane.
I've created my project and have a actions.js file in my redux folder and in that file all I currently have is
export default UPDATE_IS_LOGGED_IN = 'UPDATE_IS_LOGGED_IN';
I've installed airbnb eslinter and setup the webpack task like so
{
test: /\.js$/,
exclude: ['/node_modules/'],
use: ['eslint-loader', 'babel-loader']
}
and in my .eslintrc file I have
{
"parser": "babel-eslint",
"rules": {
"comma-dangle" : 0,
"no-undef": "off",
"max-len": [1, 120, 2, {"ignoreComments": true}],
"no-compare-neg-zero": "error",
"no-unused-vars": "off",
"class-methods-use-this": ["error", {
"exceptMethods": [
"render",
"getInitialState",
"getDefaultProps",
"componentWillMount",
"componentDidMount",
"componentWillReceiveProps",
"shouldComponentUpdate",
"componentWillUpdate",
"componentDidUpdate",
"componentWillUnmount"
]
}]
},
"extends": ["airbnb-base"]
}
But when I run webpack I get the following eslinting errors for the action.js file.
1:1 error 'use strict' is unnecessary inside of modules strict
3:32 error Strings must use singlequote quotes
6:19 error Unexpected chained assignment no-multi-assign
6:63 error Newline required at end of file but not found eol-last
This isn't the only file its happening on its nearly every js file in my project.
I'm guessing it is linting the files that webpack has already built. Try adding your build file to excludes.
I'm trying to use decorators in my JS project, however ESLint is throwing an error stating that the # symbol is a unexpected character.
My code:
#observable items = [];
My .eslintrc:
{
"parserOptions": {
"ecmaVersion": 6,
"ecmaFeatures": {
"jsx": true
},
"sourceType": "module"
},
"env": {
"browser": true,
"node": true,
"es6": false
},
"ecmaFeatures": {
"modules": true
},
"rules": {
"strict": [
2,
"global"
],
"quotes": [
2,
"single"
],
"indent": [
2,
4
],
"eqeqeq": [
2,
"smart"
],
"semi": [
2,
"always"
],
"max-depth": [
2,
4
],
"max-statements": [
2,
15
],
"complexity": [
2,
5
]
}
}
You probably want to use babel-eslint which uses Babel to parse things that ESLint hasn't implemented yet (usually experimental features like this). From their README:
At the moment, you'll need it if you use stuff like class properties, decorators, types.
It is used with your current eslint setup, you just have to update some configuration in your .eslintrc
A quick answer:
Install a lib
npm i -D babel-eslint
Add to your .eslintrc
"parser": "babel-eslint"
If you using Visual Code it will not always work. You need to add into User Settings (or Workspace Settings) following parameter:
{
...
"eslint.options": {
"experimentalDecorators": true
}
...
}
Somehow this option wins anything you put into .eslintrc .
If you using #babel/core, you need to install #babel/eslint-parser.
Add to your .eslintrc
parser: "#babel/eslint-parser"
Using babel-parser might have fixed the '#'s but caused a bunch of other warnings and errors. What I did was put all the files that used the decorator into a store folder, create an .eslintignore file and pointed to that directory.
To fix this you need to choose google as your style guide as follow:
extends: ["google"],