how to ready react app with webpack - javascript

I'm new to React and Webpack and all this stuff. I've created a React app with Webpack and I used webpack-dev-server to create and debug my app.
So in my webpack.config.js file I have this code:
var debug = process.env.NODE_ENV !== "production";
var webpack = require('webpack');
var path = require('path');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
context: path.join(__dirname, "src"),
devtool: debug ? "inline-sourcemap" : false,
entry: "./js/client.js",
module: {
loaders: [
{
test: /\.js?$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel-loader',
query: {
presets: ['react', 'es2015', 'stage-0'],
plugins: ['react-html-attrs', 'transform-decorators-legacy', 'transform-class-properties'],
}
},
{
test: /\.scss$/,
use: debug ? [{
loader: "style-loader" // creates style nodes from JS strings
}, {
loader: "css-loader" // translates CSS into CommonJS
}, {
loader: "sass-loader" // compiles Sass to CSS
}] : ExtractTextPlugin.extract({ fallback: "style-loader", use: "css-loader" })
}
]
},
output: {
path: __dirname + "/src/",
filename: "client.min.js"
},
plugins: debug ? [] : [
new ExtractTextPlugin('style.min.css'),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin({ mangle: false, sourcemap: false }),
],
externals: {
"jquery": "jQuery",
"react": "React",
"react-dom": "ReactDOM",
"animejs": "anime"
}
};
When I use webpack-dev-server --content-base src --inline --hot, I see my app working in localhost:8080 but now I want to make the app ready for production. so I ran these codes in my terminal:
$: NODE_ENV=production
$: webpack
It doesn't change anything! So first question: what is wrong with NODE_ENV=production? When I change the first line of my webpack to var debug = process.env.NODE_ENV === "production"; //false it works.
There are other problems!
I'm using sass and When debug === false and I open my index.html file in browser, my styles aren't compiled! Just all of my sass code is copied to style.min.css file The problem should be with this part of code:
{
test: /\.scss$/,
use: debug ? [{
loader: "style-loader" // creates style nodes from JS strings
}, {
loader: "css-loader" // translates CSS into CommonJS
}, {
loader: "sass-loader" // compiles Sass to CSS
}] : ExtractTextPlugin.extract({ fallback: "style-loader", use: "css-loader" })
}
And the last problem is with absolute paths! I have this code in my app:
<img src="/images/avatar.jpg">
It works when I use webpack-dev-server but when I use webpack, the image is not found as it tries to open it from the root of my linux.
So these are my questions:
why NODE_ENV=production doesn't work?
How should I compile sass and put the css in style.min.css?
How can I use absolute paths in my app?
thanks in advance

How should I compile sass and put the css in style.min.css?
Try following config
{
test: /\.scss$/,
use: debug ? [{
loader: "style-loader" // creates style nodes from JS strings
}, {
loader: "css-loader" // translates CSS into CommonJS
}, {
loader: "sass-loader" // compiles Sass to CSS
}] : ExtractTextPlugin.extract({
fallback: "style-loader",
use: [
{
loader: "css-loader",
options: {
modules: true,
},
},
{
loader: "sass-loader",
options: {
modules: true,
},
},
],
}),
},
How can I use absolute paths in my app?
A better way to import images in react app is using file loader in webpack and then directly importing images in application like you import other modules. First add file-loader in webpack like this
{
test: /\.(jpg|png)$/,
loader: "file-loader",
},
and then the public path of your server in webpack
output: {
path: __dirname + "/src/",
publicPath: "http://localhost:3000/static", // Your server public path
filename: "client.min.js"
},
Then in directly import image and put in src like this
import avatar from "../images/avatar.jpg";
<img src={avatar} />

Related

Add a specific node module in webpack bundle

I have added one of Bulma extensions in my react project
https://wikiki.github.io/components/quickview/
To use it I have installed it using npm & extension requires to add below line when DOM is completely loaded, so I added it in componentDidMount
var quickviews = bulmaQuickview.attach();
and below line in my index.html
<script type="text/javascript" src="/node_modules/bulma-extensions/bulma-quickview/dist/js/bulma-quickview.min.js"></script>
I am using webpack to bundle & have excluded all node modules from bundle.
Problem : In development environment for webpack it works fine but after bundling for production it gives me error that
bulmaQuickview is not defined
How can I bundle a specific node module in my webpack bundle? and what will be the correct reference to add.
Webpack config
module.exports = {
target: 'web',
entry: "./index.js",
output: {
filename: "bundle.js"
},
devtool: 'eval-source-map',
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader"
})
},
{
test: /\.(png|jpg)$/,
use: [{
loader: 'url-loader',
options: {
limit: 8000, // Convert images < 8kb to base64 strings
name: 'images/[hash]-[name].[ext]'
}
}]
},
{
test: /\.(png|svg|jpg|gif)$/,
include: path.join(__dirname, 'dist/image'),
loader: 'file-loader'
}
]
},
plugins: [new WebpackNotifierPlugin(), new BrowserSyncPlugin(), new ExtractTextPlugin("styles.css"),
new webpack.DefinePlugin({
'API_URL': API_URL[environment]
})
]
};
You're adding it to the index.html so webpack isn't bundling it. Have you tried importing it into the same js file that it is used in? Either that or add it to your entry property after index.html (make it an array).

Moving from webpack v1 to v2

I'm trying to migrate my code from webpack v1 to v2 and add in the sass-loader, however I get the error
throw new WebpackOptionsValidationError(webpackOptionsValidationErrors);
I'm very confused as to what the final file is supposed to look like:
let webpack = require('webpack');
let path = require('path');
module.exports = {
devtool: 'eval-source-map',
entry: [
'./src/index'
],
module: {
rules: [
{
test: /\.scss$/,
use: [
"style-loader", // creates style nodes from JS strings
"css-loader", // translates CSS into CommonJS
"sass-loader" // compiles Sass to CSS
]
}],
test: /\.js?$/,
loader: 'babel-loader',
exclude: /node_modules/
},
resolve: {
extensions: ['.js'],
options: {
enforceExtension: false
}
},
output: {
path: path.join(__dirname, '/dist'),
publicPath: '/',
filename: 'bundle.js'
},
devServer: {
contentBase: './dist',
hot: true,
historyApiFallback: true
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin(),
new webpack.LoaderOptionsPlugin({
debug: true,
options: {
context: __dirname
}
})
]
};
At the moment the code is a mix of the two versions. I am using webpack version 2.2.1. Thanks.
There are several things you need to change:
Your test: /\.js?$/ and the corresponding loader and exclude should be another object inside the rules array:
module: {
rules: [
{
test: /\.scss$/,
use: [
"style-loader", // creates style nodes from JS strings
"css-loader", // translates CSS into CommonJS
"sass-loader" // compiles Sass to CSS
]
},
{
test: /\.js?$/,
loader: 'babel-loader',
exclude: /node_modules/
}
]
},
resolve.options does not exist, it is just resolve.enforceExtension directly:
resolve: {
extensions: ['.js'],
enforceExtension: false
},
And finally, although it's not an error but just a warning, new webpack.NoErrorsPlugin() is deprecated and has been replaced with:
new webpack.NoEmitOnErrorsPlugin()
Also if you haven't yet, you should have a look at the migration guide from the official docs https://webpack.js.org/guides/migrating/.

Webpack Sass - cannot resolve images

I am trying to compile my Sass via webpack. Compiling normal sass is fine but I get an error.
Module not found: Error: Can't resolve '../img/twitter.svg' in '/Users/Steve/mywebsite/scss'
# ./~/css-loader!./~/sass-loader/lib/loader.js!./scss/main.scss 6:94501-94530
Is there a way to resolve this? Alternatively is there a way to set the level of the sass compiler to be less strict to just ignore certain errors
Below is my current config.
const path = require("path");
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
resolve: {
alias: {
masonry: "masonry-layout",
isotope: "isotope-layout",
},
},
entry: "./main.js",
output: {
path: path.resolve(__dirname, "./dist/dist2"),
filename: "bundle.js",
},
module: {
rules: [
{
test: /\.(png|jpg|svg)$/,
include: path.join(__dirname, "/dist/img"),
loader: "url-loader?limit=30000&name=images/[name].[ext]",
},
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader?presets[]=es2015",
},
{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
use: ["css-loader", "sass-loader"],
}),
},
{
test: /\.vue$/,
loader: "vue-loader",
options: {
loaders: {},
// other vue-loader options go here
},
},
],
},
plugins: [
// new webpack.optimize.UglifyJsPlugin(),
new ExtractTextPlugin("ross.css"),
],
};
I know this is late, but for anyone looking for a workaround this error;
In my case the image was loading perfectly in the template, however, Webpack was returning an error: Module not found: Error: Can't resolve './path/to/assets.png'
Fix/Workaround:
Add ?url=false to your css-loader, that will disable url handling by the css-loader :
...
{
loader: "css-loader?url=false"
},
...
I didn't have any luck with url-loader and file-loaderas suggested in the other answer. I was able to solve it using resolve-url-loader
module: {
rules: [
{ // sass / scss loader for webpack
test: /\.(sass|scss|svg|png|jpe?g)$/, //Make sure to allow all necessary file types here
use: ExtractTextPlugin.extract({
use: [
{
loader: 'css-loader',
options: {
importLoaders: 1,
minimize: true,
sourceMap: true
}
},
{
loader: 'postcss-loader',
options: {
sourceMap: true
}
},
{
loader: "resolve-url-loader", //resolve-url-loader needs to come *BEFORE* sass-loader
options: {
sourceMap: true
}
},
{
loader: "sass-loader",
options: {
sourceMap: true
}
}
]
})
}
],
},
This is a breaking change in css-loader 4.x (according to css-loader issue 1136).
You have not specified any loaders for images in your webpack file.
Install url-loader and file-loader to your package.json
via
npm install --save url-loader file-loader
Inside your webpack config file add following -
{
test : /\.(png|jpg|svg)$/,
include : path.join(__dirname, 'img'),
loader : 'url-loader?limit=30000&name=images/[name].[ext]'
}, // inline base64 URLs for <=30k images, direct URLs for the rest
I use this "Disable url resolving using the /* webpackIgnore: true */ comment"
https://webpack.js.org/loaders/css-loader/#disable-url-resolving-using-the--webpackignore-true--comment

Webpack2 + babel all but one js file are converted to es5

I'm using webpack and babel to convert my js and vue2 files to es5 and compile into a single build.js
This is my .babelrc
{
"presets": ["latest", 'stage-1'],
"plugins": [
"transform-runtime",
"transform-es2015-arrow-functions"
],
"comments": false
}
and webpack.config.js
var path = require('path'),
webpack = require('webpack');
require("babel-polyfill");
module.exports = {
entry: ['babel-polyfill', './src'],
output: {
path: './dist/',
filename: 'build.js'
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
scss: 'style-loader!css-loader!sass-loader',
styl: 'style-loader!css-loader!stylus-loader'
}
}
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.styl$/,
use: [
'style-loader',
'css-loader',
{
loader: 'stylus-loader',
},
],
}
]
},
resolve: {
modules: [
path.resolve(__dirname, 'src'),
path.resolve(__dirname, 'node_modules')
],
alias: {
vue: 'vue/dist/vue.common.js'
},
extensions: ['.js', '.vue']
},
plugins: [
// new webpack.DefinePlugin({
// 'process.env': {
// 'NODE_ENV': JSON.stringify('production')
// }
// })
]
}
Running the following command, successfuly compiles all js and vue files including the imported ones into build.js
./node_modules/webpack/bin/webpack.js --progress --watch --display-error-details
All es6 compatible codes are converted to es5 except for BaseModal.js in https://github.com/vue-bulma/modal/
BaseModal.js resides in node_modules/vue-bulma-modal/src/ and is imported by Modal.vue in the same package
I import Modal over index.js of the same package by
import {Modal} from 'vue-bulma-modal'
and BaseModal.js ends up in build.js as it is, without being converted to es5.
But if i copy Modal.vue and BaseModal.js directly into my code and update the import paths accordingly , it is converted to es5 just fine without any problems.
What is it that I'm doing wrong in my webpack.config.js?

Webpack: Loading images from html templates

I'm trying to set up an angular project using Webpack but I can't figure out how to reference images from within html templates and have them included in the build.
My project tree is as follows:
package.json
app/
- images/
- foo.png
- scripts/
- styles/
- templates/
I'm trying to use html-loader along with url-loader and file-loader but it's just not happening.
This is an example template: app/templates/foo.html
<img src="../images/foo.png" />
Problem #1: I would like to be able to reference images relative to app/. Right now, the paths need to be relative to the template file and this will get ugly very quickly (../../../images/foo.png).
Problem #2: Even if I specify the relative path, as I have done above, the project builds successfully but nothing really happens. The paths are left as-is and no images appear in dist/.
Here is my webpack config:
var path = require('path');
var webpack = require('webpack');
var ngminPlugin = require('ngmin-webpack-plugin');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var ngAnnotatePlugin = require('ng-annotate-webpack-plugin');
module.exports = function(config, env) {
var appRoot = path.join(__dirname, 'app/')
if(!env) env = 'development';
var webpackConfig = {
cache: true,
debug: true,
contentBase: appRoot,
entry: {
app: path.join(appRoot, '/scripts/app.coffee')
},
output: {
path: path.join(__dirname, 'dist/),
publicPath: '/',
libraryTarget: 'var',
filename: 'scripts/[name].[hash].js',
chunkFilename: '[name].[chunkhash].js'
},
module: {
loaders: [
{
test: /\.css$/,
loader: ExtractTextPlugin.extract("style-loader", "css-loader")
},
{
test: /\.scss$/,
loader: ExtractTextPlugin.extract('style-loader', 'css-loader!sass-loader?outputStyle=expanded&includePaths[]=./node_modules/foundation/scss/')
},
{
test: /\.coffee$/,
loader: 'coffee-loader'
},
{
loader: 'ngtemplate?relativeTo=' + (path.resolve(__dirname, './app')) + '/!html'
},
{
test: /\.png$/, loader: "url-loader?limit=100000&mimetype=image/png&name=[path][name].[hash].[ext]"
},
{
test: /\.jpg$/, loader: "file-loader?name=[path][name].[hash].[ext]"
},
{
test: /\.(woff|woff2)(\?(.*))?$/,
loader: 'url?prefix=factorynts/&limit=5000&mimetype=application/font-woff'
},
{
test: /\.ttf(\?(.*))?$/,
loader: 'file?prefix=fonts/'
},
{
test: /\.eot(\?(.*))?$/,
loader: 'file?prefix=fonts/'
},
{
test: /\.svg(\?(.*))?$/,
loader: 'file?prefix=fonts/'
},
{
test: /\.json$/,
loader: 'json'
}
]
},
resolve: {
extensions: [
'',
'.js',
'.coffee',
'.scss',
'.css'
],
root: [appRoot],
},
singleRun: true,
plugins: [
new webpack.ContextReplacementPlugin(/.*$/, /a^/),
new webpack.ProvidePlugin({
'_': 'lodash'
}),
new ExtractTextPlugin("styles/[name].[chunkhash].css", {allChunks: true}),
new HtmlWebpackPlugin({
template: appRoot + '/app.html',
filename: 'app.html',
inject: 'body',
chunks: ['app']
})
],
devtool: 'eval'
}
if(env === 'production') {
webpackConfig.plugins = webpackConfig.plugins.concat(
new ngAnnotatePlugin(),
new webpack.optimize.UglifyJsPlugin(),
new webpack.DefinePlugin({
'process-env': {
'NODE_ENV': JSON.stringify('production')
}
}),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.UglifyJsPlugin()
);
webpackConfig.devtool = false;
webpackConfig.debug = false;
}
return webpackConfig;
}
If you are using HTML templates in Webpack 2, in addition to use the file-loader you need to change in your HTML:
<img src="../images/foo.png" />
to this
<img src=<%=require("../images/foo.png")%> />
Yes, you will have to do so for loading images from different path.
I had similar issue and I resolved this using file loader:
.
loaders: [{
// JS LOADER
test: /\.js$/,
loader: 'babel-loader?optional[]=runtime',
exclude: /node_modules/
}, {
// ASSET LOADER
test: /\.(woff|woff2|ttf|eot)$/,
loader: 'file-loader'
},
{
//IMAGE LOADER
test: /\.(jpe?g|png|gif|svg)$/i,
loader:'file-loader'
},
{
// HTML LOADER
test: /\.html$/,
loader: 'html-loader'
},
{
//SCSS LOADER
test: /\.scss$/,
loaders: ["style-loader", "css-loader", "sass-loader?indentedSyntax"]
}
]
Good Luck
For Webpack 5 you have to write this way:
module: {
rules: [
// other stuff above.....
{
test: /\.html$/,
use: [
{
loader: 'html-loader'
}
]
}
]
}
With Webpack 4, I was able to solve this problem by updating by html-loader to specify the root for my files. So given #syko's original structure; in webpack.config.js...
module: {
rules: [
// other stuff above.....
{
test: /\.html$/,
use: [
// ...The other file-loader and extract-loader go here.
{
loader: 'html-loader'
options: {
// THIS will resolve relative URLs to reference from the app/ directory
root: path.resolve(__dirname, 'app')
}
}
]
}
]
}
This tells the html-loader to interpret all absolute urls from the /app folder. So in our app/templates/foo.html, you can then use the following...
<img src="/images/foo.png" />
This then tells html-loader to see the above as path.resolve(__dirname, 'app', 'images', 'foo.png'). Then if you have extract-loader and/or file-loader, all the paths should be correct.
Managed to get this solution after hammering away at it for a while. The biggest confusion is where about in the loader stack the /images/foo.png is interpreted, in this case it starts at the html-loader plugin. Everything else after that, is more about how the image files are to be published.
Hope that helps.
You can use file-loader to extract images. Then using html-loader you can specify which tag-attribute combination should be processed by this loader via the query parameter attrs.
I could make it work with this configuration:
{
test: /\.(jpe?g|png|gif|svg|ico)$/i,
use: [{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/'
}
}]
},
{
test: /\.(html)$/,
use: {
loader: 'html-loader',
options: {
attrs: ['img:src', 'link:href']
}
}
}
Or in angular something like this:
attrs: [':ng-src', ':ng-href']

Categories

Resources