How to use LESS in React 16.7 with webpack 4.29? - javascript

I am just start to learn React, and I want to use less in React. I did some research about it , and they said I should write the config in webpack.config.dev.js but , after I run npm run eject I only got one file :webpack.config.js .
I think maybe that's because the update , the new version maybe combined webpack.config.dev.js and webpack.config.prod.js to `webpack.config.js .
and I checked the homepage of webpack , it says:
// webpack.config.js
module.exports = {
...
module: {
rules: [{
test: /\.js$/,
issuer: /\.less$/,
use: [{
loader: 'js-to-less-loader'
}]
}]
}
};
but I don't know where to insert these code. I did not find any code like that.so I want a simple example for how to support less in React.
Thanks.

You need to do the following steps after ejecting to use less in react
1. Go to webpack.config.js file and search for cssRegex change it from /\.css$/ to /\.(?:le|c)ss$/
2. install less and less loader using npm install -S less less-loader
3. search for getStyleLoaders method in webpack config file and add the following object to array of loaders specified over there
{
loader: require.resolve('less-loader'),
options: {
importLoaders: 1,
},
}
Then you will be good to go, feel free to ask for clarifications if any

I believe you are using the create-react-app starter kit.
Well its good but if you want to add more features to your setup, Starter kits come with their own complications.
You can learn to setup react instead of letting the starter kit handling it for you.
Anyways you can try this,
You will need babel depedencies for handling transpilation:
npm install babel-core babel-loader babel-preset-env babel-preset-react --save-dev
Setup your react dependencies like so:
npm i react react-dom --save-dev
Once you are done with this setup, include your .bablerc file with the following code:
{
"presets" : [
"env",
"react"
]
}
Setup dependencies to load your css/less files.
This will fetch all the loaders you require for css and less.
npm install less less-loader style-loader css-loader --save--dev
Now you will need an HTML plugin and a template .html file for webpack to serve your react.
npm i html-webpack-plugin html-loader --save-dev
The template file (save this as src/index.html):
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<style>
* {
box-sizing: border-box;
}
</style>
<body>
<div class='content' id='app'></div>
</body>
</html>
You will now need a webpack devServer for serving the generated index.html
npm install webpack-dev-server --save-dev
Add the following to your package.json file
"scripts": {
"start": "webpack --mode development",
"build": "webpack --mode production"
}
By now you should be having a webpack.config.js file, which fairly looks like this.
const path = require('path');
const webpack = require('webpack');
const HtmlWebPackPlugin = require("html-webpack-plugin");
module.exports = (env, argv) => {
const {
mode
} = argv;
return {
module: {
entry: path.join(__dirname, 'src', 'app.js'),
watch: mode !== 'production',
output: {
path: path.join(__dirname, 'dist'),
filename: mode === 'production' ? 'bundle.min.js' : 'bundle.js'
},
rules: [{
test: /\.js$/,
exclude: /node_modules/,
use: ['babel-loader']
},
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
}
],
plugins: [
new HtmlWebPackPlugin({
title: 'App',
template: "./src/index.html",
filename: "./index.html"
})
]
},
devServer: {
contentBase: path.join(__dirname, 'dist')
}
};
}
And a app.js file in your src folder like so,
import React from 'react';
import ReactDOM from 'react-dom';
import './styles/style.less'
ReactDOM.render( <div> Hello from react! </div>, document.getElementById("app"));
Use npm start to run the dev version.
Hope this helps. Let me know if you face any issues.
I agree this is tedious work for the first time. But it does help in the long run to scale with features.

Related

Styles not inserted into HEAD using Webpack MiniCssExtractPlugin with Create React App

I'm using create-react-app to create a component library using Storybook JS. The aim is to publish a NPM package where these components can be used in other projects. SASS is being used for this library, with global variables defined and imported into the src/index.js file.
I'm at the point of testing my NPM package, and trying to bundle it with Webpack V4. This has been semi-successful using npm link on another local project. However, I'm facing an issue with MiniCssExtractPlugin where the styles are not being inserted at all into the HEAD of this project.
The SASS stylesheets are converted into CSS and added to the dist/ folder of my component library project with no issues. But these don't get applied anywhere when importing components via the NPM package to my other project, e.g. import { Button } from '#my-account/components';
There are no issues on my dev environment when using style-loader, and the styles are inserted straight into the DOM with a <style> tag. I'm sure I must be missing something, but I feel like I've exhausted everything to try diagnose this. Is create-react-app not compatible with this plugin? Or are styles not automatically injected to the HEAD of projects via NPM packages with this plugin?
The styling does work if I import the file from the NPM package into my local project, e.g. import '#my-account/components/dist/main.cd2be00655e79c5077cb.css'; - but this doesn't seem maintainable if styles are regularly updated and the file uses a hash in its name?
I attempted to add HtmlWebpackPlugin to create an index.html file, but this didn't resolve the issue.
Any help would be greatly appreciated!
webpack.config.prod.js
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: 'production',
devtool: 'source-map',
entry: './src/index.js',
output: {
path: path.resolve('dist'),
filename: 'index.js',
libraryTarget: 'commonjs2'
},
module: {
rules: [
{
test: /\.js?$/,
exclude: /(node_modules)/,
use: 'babel-loader',
}
],
rules: [
{
test: /\.scss$/,
sideEffects: true,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
sourceMap: true
}
},
{
loader: 'sass-loader',
options: {
sourceMap: true
}
},
{
loader: 'sass-resources-loader',
options: {
resources: require(path.join(process.cwd(), 'src/assets/sass/WebpackStyles.js')),
hoistUseStatements: true
}
}
],
},
]
},
resolve: {
extensions: ['.js'],
},
plugins: [
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: '[name].[hash].css'
})
]
};
Component libraries regularly require css to be imported alongside the js in order to work properly. If you update your plugins as:
new MiniCssExtractPlugin({
filename: '[name].css'
})
Then you can instruct consumers of your library to add import '#my-account/components/dist/main.css' which is a little easier to swallow. I'm not sure there's a magical way to have the styles just show up without extensive custom webpack loaders in your consumers.

Failed to resolve module specifier "babel-runtime/regenerator"

I'm following the instructions for this link. https://reactjs.org/docs/add-react-to-a-website.html
I have the following code and it works:
const domContainers = document.querySelectorAll('[name="uiattr"]');
domContainers.forEach((element) => {
const id = element.id.split("-")[1];
ReactDOM.render(e(LikeButton), element);
});
If i change it to this code and add async to it:
const domContainers = document.querySelectorAll('[name="uiattr"]');
domContainers.forEach(async (element) => {
const id = element.id.split("-")[1];
const attr = await getAttr(id);
ReactDOM.render(e(LikeButton), element);
});
i get the following error in the console with nothing else: Failed to resolve module specifier "babel-runtime/regenerator"
I installed bable like this: npm install babel-cli#6 babel-preset-react-app#3
and i deploy with this command: js-dev$ npx babel --watch src --out-dir ../prj/static/prj/js/ --presets react-app/prod
I am new to the babel world, i'm assuming i need something else, but have no idea. I'm assuming the syntax is correct, only because its compiling without error. Something i've seen babel fail on when i have it wrong.
The React tutorial is indeed very confusing and doesn't cover all aspects.
I would suggest to just install react & react-dom: npm install --save react react-dom
You will need webpack to bundle your code and babel + a couple of plugins to compile your JSX, use async functions, ...: npm install --save-dev #babel/core #babel/preset-env #babel/preset-react #babel/plugin-transform-runtime babel-loader webpack webpack-cli.
Create a webpack.config.js file in your root folder:
const path = require('path');
module.exports = {
mode: "development", // or production
entry: {
app1: './react/app1/index.js'
},
watch: true,
output: {
filename: "[name].js",
path: path.join(__dirname, "public/react")
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
}
}
]
}
};
Create a .babelrc file in your root folder:
{
"presets": ["#babel/preset-env", "#babel/preset-react"],
"plugins": ["#babel/plugin-transform-runtime"]
}
Example react/app1/index.js file:
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
ReactDOM.render(<App />, document.getElementById("myContainer"));
Write your react code in ./react/app1/index.js (you can import other React files, modules, ...)
Then in your html just put a script tag: <script src="public/react/app1.js"></script>

html-webpack-plugin not inject js file into index.html when using webpack-dev-server

Here is my webpack config :
var path = require('path');
var webpack = require('webpack')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var fs = require('fs'),buildPath='./dist/';
var folder_exists = fs.existsSync(buildPath);
if(folder_exists == true)
{
require('shelljs/global')
rm('-rf', 'dist')
};
module.exports = {
entry: './src/main',
output: {
path: path.join(__dirname, './dist'),
filename: '[name].js',
publicPath: '/dist/'
},
devServer: {
historyApiFallback: true,
hot: false,
inline: true,
grogress: true,
},
// "vue-hot-reload-api": "^1.2.2",
module: {
loaders: [
{ test: /\.vue$/, loader: 'vue' },
{ test: /\.js$/, loader: 'babel', exclude: /node_modules/ },
{ test: /\.css$/, loader: 'style-loader!css-loader'},
//install css-loader style-loader sass-loader node-sass --save-dev
{ test: /\.scss$/, loader: 'style!css!sass?sourceMap'},
{ test: /\.(png|jpg|gif)$/, loader: 'url-loader?limit=8192&name=images/[name].[ext]'},
{ test: /\.(html|tpl)$/, loader: 'html-loader' },
]
},
vue: {
loaders: {
js:'babel',
css: 'style-loader!css-loader',
sass:'style!css!sass?sourceMap'
}
},
babel: {
presets: ['es2015'],
plugins: ['transform-runtime']
},
plugins:[
new HtmlWebpackPlugin({
template: 'index.html',
filename: './index.html',
inject:true
}),
],
resolve: {
extensions: ['', '.js', '.vue'],
alias: {
filter: path.join(__dirname, './src/filters'),
components: path.join(__dirname, './src/components')
}
},
devtool: 'eval-source-map'
};
And in package.json:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack-dev-server --inline",
"build": "webpack --config webpack.config.prod.js"
},
When I run npm start, in localhost the js file is not injected in index.html.
If I run webpack or npm run build, the js file is injected successfully.
Can html-webpack-plugin also inject js file into index.html when I'm in localhost?
This issue is related specifically to the fact that the webpack-development-server does not have the ability to write files to your local file system and instead writes its files to MEMORY Only.
This is why you were able to properly generate files wth Html-Webpack-Plugin when you run the default webpack build command (NOT the WDS / Webpack-Development-Server) with:
webpack
Alternately since you were using vue.js Webpack-simple project (https://github.com/vuejs-templates/webpack-simple/tree/master/template) you were also able to use the npm scripts that come with the Vue.js sample (located inside of your package.json) via:
npm run build
In either case the files ARE written to the directory as you would have thought they should be since Building with webpack CAN write the file system, where as no files were written when using Webpack-Development-Server (again this does not work because WDS writes to memory and not the local file system).
I stumbled onto this answer when working on the same problem thanks to your comment:
"If I run webpack or npm run build, the js file is injected successfully. Can html-webpack-plugin also inject js file into index.html when I'm in localhost?"
To sum up:
Html-Webpack-Plugin WILL NOT write files to the local file system when it is used as a part of the Webpack-Development-Server (WDS) even though Html-Webpack-Plugin WILL write files (with an identical webpack configuration) when using the normal webpack build process (no WDS).
I have same problem and below config work for me.
I used to have absolute path and when change it to relative, it work.
new HtmlWebpackPlugin({
inject: true,
template:'public/index.html', // relative to project root
filename:'index.html' // relative to build folder
})
You can try config like this..
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: true
})
and Index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>My App</title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
You will need install npm i -D html-webpack-plugin vue-loader vue-html-loader
but I recommend you create a project from template, vue init webpack
This template use the html-webpack-plugin

npm link with webpack - cannot find module

I'm trying to npm link a module to a project using webpack as its bundler. Of course, after trying many things, I keep getting this error:
ERROR in ./src/components/store/TableView.jsx
Module not found: Error: Cannot resolve module 'react-bootstrap-table'
Here are the exact steps I take when doing this:
1.) cd ../forks/react-bootstrap-table
2.) npm link
(success, checked ~/.nvm/.../node_modules/react-bootstrap-table for symlink and it's there)
3.) cd ../../projRoot/
4.) npm link react-bootstrap-table
(no errors thrown?, says successful link)
5.) node ./node_modules/webpack-dev-server/bin/webpack-dev-server.js
Solutions I've tried:
- https://webpack.github.io/docs/troubleshooting.html
- How to make a linked component peerDepdencies use the equivalent node_modules of the script being linked to?
- And many purple links on google serps
webpack.config.js
const webpack = require('webpack')
const path = require('path')
const ROOT_PATH = path.resolve(__dirname)
module.exports = {
devtool: process.env.NODE_ENV === 'production' ? '' : 'source-map',
entry: [
'webpack/hot/only-dev-server',
'./src/index.js'
],
module: {
loaders: [{
test: /\.jsx?$/,
exclude: /node_modules/,
loaders: ['react-hot','babel']
},
{
test: /\.scss$/,
loaders: ['style','css','sass'],
exclude: /node_modules/
},
{
test: /\.css$/,
loaders: ['style','css']
},
{
test: /\.(ttf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,
loader: 'file-loader'
}
]
},
resolve: {
extensions: ['', '.js', '.jsx'],
fallback: path.resolve(__dirname, './node_modules')
},
resolveLoader: {
fallback: path.resolve(__dirname, './node_modules')
},
output: {
path: process.env.NODE_ENV === 'production' ? path.resolve(ROOT_PATH, 'app/dist') : path.resolve(ROOT_PATH, 'app/build'),
publicPath: '/',
filename: 'bundle.js'
},
devServer: {
contentBase: path.resolve(ROOT_PATH),
historyApiFallback: true,
hot: true,
inline: true,
progress: true,
stats: 'errors-only',
host: '192.168.1.115'
},
plugins: [
new webpack.HotModuleReplacementPlugin()
]
}
Notes:
1. this is the only symlink in the project
2. I run npm install inside forked version (also tried without, doesn't work)
3. I use NVM, but I have used symlinks before without webpack successfully.
I've been at this for a few days now, any help will be much appreciated.
I was facing a similar issue with webpack and ended up by adding this my webpack.config.js:
module.exports = {
resolve: {
symlinks: false
}
};
Here is the link to webpack docs. Since your question there happened a lot to webpack and their api, so I do not know how much relevance my answer still has according to your question. But for people facing this or a similar issue today this could be a solution. As to be seen, there are still people complaining about:
Webpack GitHub Issue 1643
Webpack GitHub Issue 1866
Also make sure you have bundle and yarn installed and executed in the linked package
Okay guys, this is specific to my use case, but make sure to follow all the instructions to completely build the library you are symlinking. Initially, I a npm install and gulp build, but that wasn't enough. I had to run a few extra commands to get the library to fully build.
Now it works! If you are still having issues, go through the documentation for each library you are symlinking, and use my webpack config as a template for resolving external libraries.
Just in case it's useful for others, the solution of adding the resolve.symlinks configuration to false suggested by #Beat was not enough in my case, I had to perform the following steps to solve it:
In the library:
Setup the libraries that are generating issues as peerDependencies in the package.json instead of dependencies or devDependencies, e.g. in my case react:
"peerDependencies": {
"react": "^16.8.6",
...
}
run npm install
build the library (in my case, with a rollup -c npm script
In my main app:
change the version of my library to point to my local project with a relative path in package.json, e.g.
"dependencies": {
"my-library": "file:../../libraries/my-library",
...
}
Add resolve.symlinks = false to my main app's webpack configuration
Add --preserve-symlinks-main and --preserve-symlinks to my package.json start script, e.g:
"scripts": {
"build": "set WEBPACK_CONFIG_FILE=all&& webpack",
"start": "set WEBPACK_CONFIG_FILE=all&& webpack && node --preserve-symlinks-main --preserve-symlinks dist/server.js",
}
run npm install
run npm run start

ES6 import from root

I'm currently playing around with React Native. I'm trying to structure my app, however it's starting to get messy with imports.
--app/
-- /components
-- Loading.js
-- index.ios.js
Now, within my index.ios.js i'm able to simply do:
import Loading from './components/Loading';
However, when I start to create more components, with a deeper directory struture, it starts to get messy:
import Loading from '.../../../../components/Loading';
I understand the preferred solution would be to make private npm modules for things, but that's overkill for a small project.
You could do a global.requireRoot type solution on the browser, but how do I implement this with import?
Had the same issue with React.
So i wrote some plugin for babel which make it possible to import the modules from the root perspective - the paths are not shorter - but it's clear what you import.
So instead of:
import 'foo' from '../../../components/foo.js';
You can use:
import 'foo' from '~/components/foo.js';
Here is the Plugin (tested and with a clear README)
The react documentation explain how to do that:
https://create-react-app.dev/docs/importing-a-component/#absolute-imports
just add a jsconfig.json in your project root:
{
"compilerOptions": {
"baseUrl": "src"
},
"include": ["src"]
}
If you are using Webpack you can configure it via the resolve property to resolve a your import path.
Webpack 1
resolve: {
root: [
path.resolve(__dirname + '/src')
]
}......
Webpack 2
resolve: {
modules: [
path.resolve(__dirname + '/src'),
path.resolve(__dirname + '/node_modules')
]
}.....
After that you can use
import configureStore from "store/configureStore";
instead of the:
import configureStore from "../../store/configureStore";
Webpack will configure your import path from the passed resolve param.
The same stuff you can do with System.js loader but with it's own config param (it's can be map or path. Check it in the System.js documentation) (if you would like to use it. It's mostly for a Angular 2 case. But I suggest: don't use standard System.js even if you are working with ng2. Webpack is much better).
I just checked out a React project which is more than 6 months old and for some reasons my imports no longer worked. I tried the first answer:
import 'foo' from '~/components/foo.js';
Unfortunately this did not work.
I added an .env file in the root of my project at the same level as my package.json. I added the following line to that file and this fixed my imports in my project.
NODE_PATH=src/
If you're using Create-React-App, you just need to change the environmental variable NODE_PATH to contain the root of your project.
In your config.json do the following change to set this variable before running the react-scripts commands:
"scripts": {
"start": "cross-env NODE_PATH=. react-scripts start",
"build": "cross-env NODE_PATH=. react-scripts build",
"test": "cross-env NODE_PATH=. react-scripts test",
"eject": "react-scripts eject"
},
We're using the npm library cross-env because it works on unix and windows. The syntax is cross-env var=value command.
Now instead of import module from '../../my/module' we can do import module from 'src/my/module'
Extra details on implementation
Its important to note that cross-env's scope is limited to the command it executes, so cross-env var=val command1 && command2 will only have var set during command1. Fix this if needed by doing cross-env var=val command1 && cross-env var=val command2
create-react-app gives precedence to folders in node_modules/ over whats in NODE_PATH, which is why we're setting NODE_PATH to "." instead of "./src". Using "." requires all absolute imports to start with "src/" which means there should never be a name conflict, unless you're using some node_module called src.
(Note of caution: The solution described above replaces the variable NODE_PATH. Ideally we would append to it if it already exists. NODE_PATH is a ":" or ";" separated list of paths, depending on if its unix or windows. If anyone finds a cross-platform solution to do this, I can edit my answer.)
With webpack you can also make paths starting with for example ~ resolve to the root, so you can use import Loading from '~/components/Loading';:
resolve: {
extensions: ['.js'],
modules: [
'node_modules',
path.resolve(__dirname + '/app')
],
alias: {
['~']: path.resolve(__dirname + '/app')
}
}
The trick is using the javascript bracket syntax to assign the property.
In Webpack 3 the config is slightly diffrent:
import webpack from 'webpack';
import {resolve} from 'path';
...
module: {
loaders: [
{
test: /\.js$/,
use: ["babel-loader"]
},
{
test: /\.scss$|\.css$/,
use: ["style-loader", "css-loader", "sass-loader"]
}
]
},
resolve: {
extensions: [".js"],
alias: {
["~"]: resolve(__dirname, "src")
}
},
If you're using Create React App you can add paths.appSrc to resolve.modules in config/webpack.config.dev.js and config/webpack.config.prod.js.
From:
resolve: {
modules: ['node_modules', paths.appNodeModules].concat(...
To:
resolve: {
modules: [paths.appSrc, 'node_modules', paths.appNodeModules].concat(...
Your code would then work:
import Loading from 'components/Loading';

Categories

Resources