I have an application that's structured like this:
- root
- app/
- config/
- css/
- js/
- public/
- package.json
- webpack.config.js
The React app is in the app folder and I'm publishing to the public folder. Here is my webpack configuration:
var debug = process.env.NODE_ENV !== "production";
var webpack = require('webpack');
var path = require('path');
var ExtractTextPlugin = require("extract-text-webpack-plugin");
var extractSass = new ExtractTextPlugin(
"../css/style.css", {
allChunks: true
});
module.exports = {
context: path.join(__dirname, "public"),
devtool: debug ? "inline-sourcemap" : null,
entry: {
withify: '../app/main.jsx',
fbauth: '../app/fbauth.jsx',
fbuser: '../app/fbuser.jsx',
fontawesome: 'font-awesome-webpack!../node_modules/font-awesome-webpack/font-awesome.config.js',
styles: './css/style.scss'
},
output: {
path: './js',
filename: '[name].js'
},
resolve: {
alias: {
config: path.join(__dirname, 'config', process.env.NODE_ENV || 'development')
}
},
module: {
loaders: [
{
test: /\.json$/,
loader: 'json'
},
{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel-loader',
query: {
presets: ['react', 'es2015', 'stage-0'],
plugins: ['react-html-attrs', 'transform-class-properties', 'transform-decorators-legacy'],
}
},
{
test: /\.scss$/,
loader: extractSass.extract(["css", "sass"])
},
{
test: /\.(eot|woff|woff2|ttf|svg|png|jpg)?(\?v=[0-9]\.[0-9]\.[0-9])?$/i,
loader: 'url'
},
{
test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: "file-loader"
}]
},
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery'
}),
extractSass
],
externals: {
fs: '{}',
tls: '{}',
net: '{}',
console: '{}'
}
};
Here is how I'm trying to start the app with npm run dev:
"scripts": {
"dev": "./node_modules/.bin/webpack-dev-server --content-base public --inline --hot"
},
However, when my index.html tries to access the withify.js file that should be available in memory in the development server I'm getting a 404; what did I do wrong here?
<html>
<head>
<title>Withify Application</title>
<!-- Mobile Specific Metas ================================================== -->
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=AIzaSyAlPNWOZmv60OUaD3_idHMP15-Ghwm7RDE&libraries=places"></script>
<link type="text/css" rel="stylesheet" href="/css/style.css">
</head>
<body>
<div id="app"></div>
<script src="/js/jquery.min.js"></script>
<script src="/js/withify.js"></script>
<script defer src="https://code.getmdl.io/1.1.3/material.min.js"></script>
</body>
</html>
Ok well a couple things:
Context is the folder where you tell webpack where to look for your entries. So currently you are telling webpack to look into the public folder, but all of your entries live in "/app"
So your context would be something like:
context: path.join(_dirname, 'app')
This tells webpack look into root/app and find the entries which should now look like:
entry: [
withify: 'main.jsx',
fbauth: 'fbauth.jsx',
fbuser: 'fbuser.jsx',
]
And your output will be something like this:
output:[
path: path.join(__dirname, 'public', 'js')
filename: '[name].js'
]
I think that should fix your issues as long as you are pulling everything from your public folder!
EDIT:
Try setting this as well in your output:
output:[
path: path.join(__dirname, 'public', 'js'),
filename: '[name].js',
publicPath: '/js/'
]
Related
My main problem is that I'm trying to build a React Application but the assets folder is missing and I don't know how to import it and configure it in webpack.conf. Another problem is the relative route of index.html: I don't know if it will be affected in the application build.
Thanks in advance.
Application Tree
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>App</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="shortcut icon" href="src/assets/favicon.ico"/>
</head>
<body>
<div id="app"></div>
</body>
</html>
Webpack Config:
const path = require('path');
const HTMLWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry: './src/index.tsx',
output: {
path: path.resolve(__dirname, 'build'),
filename: 'bundle.js'
},
resolve: {
// Add '.ts' and '.tsx' as resolvable extensions.
extensions: [".ts", ".tsx", ".js", ".jsx", ".json"]
},
module: {
rules: [
{
test: /\.tsx?$/,
loader: "awesome-typescript-loader"
},
// All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
{ enforce: "pre", test: /\.js$/, loader: "source-map-loader" },
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
"sass-loader"
]
}
]
},
plugins: [
new HTMLWebpackPlugin({
template: 'public/index.html'
}),
new MiniCssExtractPlugin("style.css")
],
// Enable sourcemaps for debugging webpack's output.
devtool: "source-map",
devServer: {
historyApiFallback: true,
port: 3000
}
};
You can use webpack-copy-plugin to copy additional folders / files as part of the build process
new CopyPlugin([{
from: path.resolve(__dirname, 'src', 'assets'),
to: path.resolve(__dirname, 'build', 'assets')
}])
As #James mentioned: "You can use webpack-copy-plugin to copy additional folders / files as part of the build process"
However, there is a small error while using you example #James, it should be like this:
webpack.config.js
plugins: [
//...
new CopyPlugin({
patterns: [
{ from: path.resolve(__dirname, 'src', 'assets'), to: path.resolve(__dirname, 'build', 'assets') },
],
})
],
Hope it helps (upvote james for his help if it helps you too)
I'm new to webpack and I'm facing an issue while trying to build a simple webpage.
While I don't have problems when using webpack-dev-server, when I build my application with webpack I don't see the CSS inside the index.html file.
Here is the webpack.conf.js:
var path = require( "path" );
const HtmlWebpackPlugin = require( 'html-webpack-plugin' );
module.exports = {
entry: {
index: path.join( __dirname, "src", "index.js" )
},
output: {
path: path.join( __dirname, "dist" ),
filename: "bundle.js"
},
devServer: {
host: '0.0.0.0',
port: 9000,
writeToDisk: true
},
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/,
exclude: /(node_modules)/,
use: ['file-loader'],
},
{
test: /\.svg$/,
exclude: /(node_modules)/,
loader: 'svg-inline-loader'
},
{
test: /\.js$/,
exclude: /(node_modules)/,
use: {
loader: "babel-loader",
options: { presets: ["#babel/preset-env"] }
}
},
{
test: /\.styl$/,
exclude: /(node_modules)/,
use: [
"style-loader", // creates style nodes from JS strings
"css-loader", // translates CSS into CommonJS
"stylus-loader" // compiles Stylus to CSS
]
}
]
},
plugins: [
new HtmlWebpackPlugin( {
filename: 'index.html',
template: path.join( __dirname, "src", "index.html" ),
inject: true
} ),
]
};
This is the index.js:
import "./css/index.styl";
import "./css/graphics.styl";
import "./js/polyfills.js"
import "./js/manager.js"
console.log( "TEEEEEEEEEEEEEEEEST" );
Here the index.html:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport"
content="user-scalable=no, width=device-width,initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" />
</head>
<body>
<div id="graphicsContainer" class="graphics-container">
<%=require('./assets/images/barca_onde.svg')%>
</div>
</body>
</html>
I run the dev mode with this command:
./node_modules/webpack-dev-server/bin/webpack-dev-server.js --config /app/webpack.config.js --mode development --color --watch
While the build command I use is this:
./node_modules/webpack/bin/webpack.js --config /app/webpack.config.js --mode production --color
Both commands don't show any error, but the first generates the bundle.js, the inline SVG, the CSS code and injects them in the index.html while the second only generates bundle.js and the SVG and injects them in index.html leaving the CSS out.
What am I missing? Thank you in advance!
This is happening because your configuration isn't set up to write your compiled CSS to a file. The injection of style only happens in development mode, which is why you aren't seeing it when you build in production mode.
If you're using the latest major release of webpack (which is 4 as of this answer), you'll want to use MiniCssExtractPlugin. It replaces style-loader in your configuration.
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const devMode = process.env.NODE_ENV === 'development';
// ...
{
plugins: [
new MiniCssExtractPlugin({
filename: devMode ? '[name].css' : '[name].[hash].css',
chunkFilename: devMode ? '[id].css' : '[id].[hash].css',
}),
],
rules: [
{
test: /\.styl$/,
exclude: /(node_modules)/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: { hmr: devMode },
},
"css-loader",
"stylus-loader"
]
}
]
}
For prior major releases of webpack, see ExtractTextWebpackPlugin.
I'm trying to load a favicon using the index.html that is the template for the HtmlWebpackPlugin but it's not loading.
That is how my Webpack config looks like:
'use strict'
const webpack = require('webpack')
const { join, resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
devtool: 'cheap-module-eval-source-map',
entry: join(__dirname, 'src', 'index'),
output: {
filename: 'bundle.js',
path: resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
},
{
test: /\.s?css$/,
exclude: /node_modules/,
use: ['style-loader', 'css-loader', 'sass-loader']
}
]
},
resolve: {
extensions: ['.js']
},
devServer: {
contentBase: resolve(__dirname, 'build')
},
plugins: [
new HtmlWebpackPlugin({
template: join(__dirname, 'public', 'index.html')
})
]
}
And that is my index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="shortcut icon" href="favicon.ico">
<title>React App</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
</body>
</html>
HTMLWebpackPlugin will not parse the HTML to find your resources. You'll need to include it like this in your template:
index.html
<link rel="shortcut icon" href="${require('./favicon.ico')}">
You'll also need to include file-loader for your .ico file:
webpack.config.js
{
test: /\.ico$/,
loader: 'file-loader'
}
HtmlWebpackPlugin has an option called favicon which lets you inject an icon link into the header in development or production.
new HtmlWebpackPlugin({
title: "Title Here",
template: "./src/index.html",
favicon: "./src/favicon.ico",
inject: "body",
})
You should also have a rule to grab the icon, and also import it in your bundler file.
// # Target: favicon
{
test: /\.ico$/i,
type: "asset/resource",
// Use 'generator' to output unique name (based on webpack pattern e.g. [name], [ext], etc.)
generator: {
filename: "[name][ext][query]"
}
},
NB: I'm writing for Webpack 5
I'm not sure if Webpack 4 has the type: "asset/resource" feature, but I assume you can achieve the same thing with file-loader and its options.
{
test: /\.ico$/i,
use: {
loader: "file-loader",
options: {
name: "[name].[ext]"
}
}
},
*Not guaranteed for Webpack 4.
How can i live reload the page when css is changed? Right now when js file is changed, live reload works. The project structure of my project is
output - css(assets/css/main.css) is inside this folder and so index.html
src - all react files
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Kolaboo</title>
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="./assets/css/main.css">
</head>
<body>
<div class="app">
</div>
<script type="text/javascript" src="https://code.jquery.com/jquery-2.2.0.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" type="text/javascript"></script>
<script src="./app.js"></script>
</body>
</html>
webpack.config.js
const path = require('path');
const webpack = require('webpack');
// webpack sets the NODE_ENV when calling it with -p or -d
if (!process.env.NODE_ENV) {
process.env.NODE_ENV = 'development';
}
const webpackConfig = {
cache: true,
entry: './src/index.js',
output: {
path: path.join(__dirname, 'output', 'build'),
filename: 'app.js'
},
module: {
loaders: [
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/,
query: {
presets: ['es2015', 'react', 'stage-2']
}
}, // also adds ES6 support
{
test: /\.(png|svg|jpg|jpeg|jpeg|ttf|woff|eot|gif)$/,
loader: 'url-loader?limit=100000'
},
{
test: /\.css$/,
loader: 'style-loader!css-loader'
},
{
test: /masonry|imagesloaded|fizzy\-ui\-utils|desandro\-|outlayer|get\-size|doc\-ready|eventie|eventemitter/,
loader: 'imports?define=>false&this=>window'
}
]
},
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
})
],
resolve: {
root: path.join(__dirname, 'src'),
modulesDirectories: ['node_modules']
},
devServer: {
contentBase: './public',
hot: true,
historyApiFallback: true
},
};
if (process.env.NODE_ENV === 'production') {
webpackConfig.plugins.push(
new webpack.optimize.UglifyJsPlugin()
);
}
module.exports = webpackConfig;
Note: if i place the css file on the same place where js file is and import it using import './main.css', it works but not if i want to include the css file in index.html.
I have an app.bundle.js (basically the main app bundle) and two cordova bundles: iosCordova.bundle.js and androidCordova.bundle.js. I only script src one of them depending on whether the user is logging in on an ios or android. I have all the bundles, css's, png's and everything except index.html in a generated folder (named _dist). I can't put index.html using htmlWebpackPlugin in this folder because otherwise there would be an automatic script src to both the cordova bundles (which is something I want to avoid). My problem is building the project: when I run build it is looking for index.html in _dist which results in 404. FYI When I run "npm run dev" however it knows that it should look for index.html in the main folder while app.bundle.css and app.css should be in _dist.
my webpack.config:
config.entry = {
app: './app.js',
iosCordova: ['./static/wrapper/js/ios/cordova_all.min.js'],
androidCordova: ['./static/wrapper/js/android/cordova_all.min.js']
};
config.output = {
path: __dirname + '/_dist',
publicPath: 'localhost:8080/',
filename: '[name].bundle.js',
chunkFilename: '[name].bundle.js'
};
config.module = {
noParse: /node_modules\/html2canvas/,
preLoaders: [],
loaders: [
{
test: /\.js$/,
loader: 'babel?optional[]=runtime',
presets: ['es2015'],
exclude: [
/node_modules/,
/cordova_all/
]
},
{
test: /\.(png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot)$/,
loader: 'file?name=[name].[ext]'
}, {
test: /\.html$/,
loader: "ngtemplate?relativeTo=" + __dirname + "!raw"
}]
};
var cssLoader = {
test: /\.css$/,
loader: ExtractTextPlugin.extract('style', 'css?sourceMap!postcss', 'scss', 'sass')
};
config.module.loaders.push(cssLoader);
config.devServer = {
stats: {
modules: false,
cached: false,
colors: true,
chunk: false
}
};
//config.resolve = {
// alias: {
// moment: "node_modules/moment/min/moment.min.js"
// //"jquery-ui": "vendor/plugins/jquery-ui.min.js"
// }
//};
return config;
};
index.html:
<!DOCTYPE html>
...
<html>
<head>
<script type="text/javascript">
(function(){
if (window.location.search.indexOf('cordova=') > -1) {
var platform = /i(phone|pod|pad)/gi.test(navigator.appVersion) ? 'iosCordova.bundle.js' : 'androidCordova.bundle.js';
var cordovaScript = document.createElement('script');
cordovaScript.setAttribute('src',platform);
document.head.appendChild(cordovaScript);
}
}());
</script>
<link href="app.css" rel="stylesheet">
</head>
...
<script src="app.bundle.js"></script>
</body>
</html>
So the answer is:
config.plugins.push(
new HtmlWebpackPlugin({
template: './index.html',
inject: true,
excludeChunks: ['app','iosCordova','androidCordova']
})
);