My project directory Structure is :
mcdist <build output folder>
- assets
index.html
- css
main.css
- js
app.js
lib.js
- ng-app
<all HTML Files>
- images
src
- images
1.png
2.png
- javascripts
- ng-app
- components
1.js
- directives
2.js
- filters
3.js
- stylesheets
1.scss
2.scss
index.html
app.js
node_modules
webpack.config.js
package.json
My webpack.config looks like this :
var path = require('path');
var webpack = require('webpack');
var config = {
entry: {
app: ['./app.js'],
lib: ['./lib.js']
},
output: {
path: path.join(__dirname, '/mcdist/assets/js/'),
filename: 'app.js',
publicPath: "/public/js/"
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.optimize.CommonsChunkPlugin({
name: 'lib',
filename: 'lib.js',
minChunks: Infinity
}),
new webpack.NamedModulesPlugin()
],
devServer: {
proxy: [{
context: [
'/mcdist/api',
'/mcdist/appConfig',
'/mcdist/j_spring_security_logout'
],
target: 'http://localhost:8080',
secure: false
}],
port: 9000
}
};
module.exports = config
I run:
webpack-dev-server --inline
I create build folder 'mcdist' using npm command as :
"build:js": "uglifyjs mcdist/assets/js/app.js -m -o mcdist/assets/js/app.js",
My src/app.js looks like this :
require('./src/javascripts/ng-app/config/init-config.js');
require('./src/javascripts/ng-app/config/theme-config.js');
require('./src/javascripts/ng-app/components/dialog-message/dialog-message.service.js');
require('./src/javascripts/ng-app/components/footer/footer.controller.js');
require('./src/javascripts/ng-app/components/topbar/topbar.controller.js');
and many more files as list in require format in app.js
Now every time I make changes in HTML, JS files I need to create new build so changes get pushed to 'mcdist' folder (JS files gets concatenated and uglified in single app.js and all HTML files pushed to ng-app folder inside 'mcdist' folder.)
AND I've to RESTART WEBPACK dev server to see changes reflected.
This is very painful in local/development environment as changing small code needs build and restart, which is not the case with Grails application.
is there any way I can avoid restarting a server and just make changes to them hot-reloaded ? I've already tried running with --hot and --inline approaches but nothing had luck.
Help is most appreciated.
Related
I have found numerous posts about the Webpack error:
Uncaught ReferenceError: process is not defined
most of which suggest adding a plugin to the webpack.config.js:
plugins: [
// ...
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV || 'development')
}
}),
// ...
]
however this does not seem to do the trick in my case.
To make things easy, I have created a repo with the bare minimum to setup SemanticUI-React with Webpack, which should be straightforward to navigate. My config in webpack.config.js is heavily inspired from this recent tutorial which seems to have a lot of positive comments.
To reproduce the error, just clone the repo on your machine (I use yarn, but this should work with npm too):
git clone https://github.com/sheljohn/minimal-semantic-react
cd minimal-semantic-react/
yarn install
yarn run serve
which opens at localhost:3000, and the error can be seen in the developer console.
As far as I understand, it seems that when React loads, it is looking to determine whether production or development mode is set, using the variable process.env.NODE_ENV, which is undefined in the browser.
This might be related to the target field in the Webpack config (set to web by default); but since React is loaded from CDN, prior to the bundle, I guess it doesn't know about what WebPack is doing, which makes me perplex as to why adding a plugin in the config would change anything...
Hence my question: is it possible to use semantic-ui-react by declaring the big libs (React, ReactDOM, semantic) as externals? Everything works fine if I bundle them, but the bundle ends up around 4MB, which is quite big.
Additional Details
Error as seen in Chrome (OSX High Sierra, v66.0.3359.181, dev console):
react.development.js:14 Uncaught ReferenceError: process is not defined
at react.development.js:14
(anonymous) # react.development.js:14
and code excerpt at line 14:
if (process.env.NODE_ENV !== "production") {
File webpack.config.js
const path = require("path");
const webpack = require("webpack");
const publicFolder = path.resolve(__dirname, "public");
module.exports = {
entry: "./src/index.jsx",
target: "web",
output: {
path: publicFolder,
filename: "bundle.js"
},
devServer: {
contentBase: publicFolder,
port: 3000
},
externals: {
'jquery': 'jQuery',
'lodash': '_',
'react': 'React',
'react-dom': 'ReactDOM',
'semantic-ui-react': 'semantic-ui-react'
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader'
}
}
]
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV || 'development')
}
}),
new webpack.HotModuleReplacementPlugin()
]
};
File .babelrc
{
"presets": ["env", "react"]
}
I think I finally solved this:
Mistake #1: I was using cjs versions of the React libs from cdnjs, when I should have been using umd instead. Although UMD style is ugly, it seems to work fine within browsers, whereas CommonJS uses require for example. See this post for a comparison of AMD / CommonJS / UMD.
Mistake #2: in webpack.config.js, the "name" for the external semantic-ui-react should be semanticUIReact (case sensitive). This is what is defined in the window global when the script is loaded from the CDN (e.g. like jQuery or React).
I updated the repository with these fixes, and you should be able to reproduce that working example on your machine. This repository contains the bare minimum needed to get SemanticUI, React and Webpack working together. This would have saved me a lot of time, so hopefully other people get to benefit from that!
Everything works fine if I bundle them, but the bundle ends up around 4MB, which is quite big.
It's because you bundle them in "development" mode. Try using "production" in your script instead, it will be much smaller.
"build": "webpack --mode production"
If you bundle everything in production, without specifying external, it will be better for a standalone app.
Whenever I perform a git pull from my master branch onto my server, all my React files seem to just disappear and the screen turns white.
The temporary workarounds I had found were:
Delete browser cookies, cache & site history, and then close the browser and try again.
Delete node_modules, npm install all react dependencies again
After a while, the site reappears and everything works as normal until the next time after a few pull requests, the problem appears again.
Any console I use on any browser shows no error messages at all.
After 2+ weeks of googling around, I can't seem to find anything that relates to this issue.
Here are my specs:
Ubuntu 16.04 server
Framework: React 16.2.0
webpack 1.12
nginx version: nginx/1.10.3 (Ubuntu)
git version 2.7.4
My webpack settings (for clarity, I compile all my react files with the command):
node_modules/.bin/webpack --config webpack.local.config.js
(local)
var path = require("path")
var webpack = require('webpack')
var BundleTracker = require('webpack-bundle-tracker')
var config = require('./webpack.base.config.js')
config.devtool = "#eval-source-map"
config.plugins = config.plugins.concat([
new BundleTracker({
filename: './webpack-stats-local.json'
}),
])
config.module.loaders.push({
test: /\.js[x]?$/,
exclude: /node_modules/,
loaders: ['react-hot-loader/webpack', 'babel'],
})
module.exports = config
(base)
var path = require("path")
var webpack = require('webpack')
module.exports = {
context: __dirname,
entry: {
App1: './path/to/App1/',
App2: './path/to/App2/',
// ...
App10: './path/to/App10/',
vendors: ['react'],
},
output: {
path: path.resolve('./backend/static/bundles/local/'),
filename: "[name]-[hash].js"
},
externals: {
"gettext":"gettext",
"django":"django",
}, // add all vendor libs
plugins: [
new webpack.optimize.CommonsChunkPlugin('vendors', 'vendors.js'),
],
module: {
loaders: []
},
resolve: {
modulesDirectories: ['node_modules', 'bower_components'],
extensions: ['', '.js', '.jsx']
},
}
Any help will be greatly appreciated
I solved this problem by updating Webpack to version 4 + updating the dependencies i used while getting rid of the ones i don't use.
Have a large AngularJS 1.6 project (~120 JS files), which is currently built through gulp/bower.
Looking to migrate over to using yarn & webpack, and we would prefer not to have to modify our project files just to implement webpack. Each of our AngularJS component JS files are wrapped in a IIFE, so they are already out of the global scope.
One of our gulp steps uses the gulp-angular-templatecache module, which collects all of our HTML files and neatly compacts them into a single $tmplateCache loader module.
angular.module("ourApp").run(["$templateCache", function ($templateCache) {
$templateCache.put("/app/components/ourComponent1.html", "<div id=\"component1-html\"> ... </div>\r\n");
$templateCache.put("/app/components/ourComponent2.html", "<div id=\"component2-html\"> ... </div>\r\n");
// etc...
});
I have managed to figure out how to resolve most of our other build processes using webpack, but this one is giving me a problem.
I have looked at the ngtemplate-loader package, but do not think this would work for our needs because of the following:
Would require us to update all of our existing HTML templates to use 'require("./template.html")'
This would create a separate webpack module for each HTML template, which seems very inefficient.
Probably most important, I haven't been able to get it to work.
The current webpack configuration I have setup is based on a simple demo, and splits the project files out from the vendor files. Our project files are bundled up into a 'app.[hashcode].js' file into the /dist folder.
Ultimately, I would like to be able to inject the compiled $templateCache module, into a specific point in our final 'app.[hashcode].js' bundle file. However, right now I would be satisfied with the $templateCache definition file being created into a separate bundle file.
Is there an existing webpack plugin, or loader, or combination of plugin(s)/loader(s), that I could use to accomplish this build step? Is this even possible with webpack?
This is the base directory structure for the demo project:
/dashboard
/app
/assets
/global
global.less
app.less
/components
/controllers
dashboard.controller.js
/directives
yep-nope.directive.js
/messenger
messenger.directive.js
messenger.html
/services
github-status.service.js
dashbboard.config.js
dashboard.module.js
app.js
/dist
app.4f12bb49f144e559bd9b.js
assets.css
index.html
vendor.b0a30c79aa77e0126a5c.js
index.html
package.json
webpack.config.js
This is the current working webpack.config.js file:
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
context: __dirname,
entry: {
app: path.resolve(__dirname, 'app/app.js'),
vendor: ['angular','angular-sanitize']
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[chunkhash].js'
},
module: {
rules: [
{
test: /\.less$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: ['css-loader', 'less-loader']
})
},
{
test: /\.js$/,
loader: 'ng-annotate-loader'
},
{
test: /\.html$/,
exclude: path.resolve(__dirname, 'app/index.html'),
use: [
{ loader: 'html-loader', options: { minimize: false } }
]
}]
},
plugins: [
new CleanWebpackPlugin(['dist','assets/**/*.css']),
new HtmlWebpackPlugin({
title: 'GitUp',
template: 'index.html',
inject: 'body'
}),
new webpack.optimize.CommonsChunkPlugin({
name:"vendor", filename:"[name].[chunkhash].js"
}),
new ExtractTextPlugin('assets.css')
]
};
This is the webpack 'entry' file app/app.js:
require('./assets/app.less');
require('../node_modules/angular/angular.js');
require('../node_modules/angular-sanitize/angular-sanitize.js');
require('./components/dashboard.module.js'); // Define module
require('./components/dashboard.config.js'); // Setup config
// Load in all components files, except for files already loaded above
var reqCtx = require.context('./components', true, /^(?!.*(?:dashboard\.config|dashboard\.module)).*\.js$/);
reqCtx.keys().forEach(reqCtx);
If needed, I can provide the entire sample project...
This may or may not be the answer you're looking for but in the past I've used html-loader to allow me to require my component template URLs, and it worked brilliantly:
https://www.npmjs.com/package/html-loader
It just inlines the template in your bundled script.
I'm new to WebPack and React, and am having an issue getting my settings correct.
My directory structure is:
/project/
/src/
index.html
app.js
/static/
/images
My "output" WebPack config section is:
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'dist')
},
My React app runs fine, but I'm not getting any images served with urls as such:
<img src="/static/images/logo.jpg" />
If I change the image src values to:
<img src="/src/static/images/logo.jpg" />
they work, but of course, that's not going to be the case in production, since the /src/ directory wouldn't be deployed. It seems I have the root set incorrectly.
Do I need to tell WebPack to copy the "static" directory into "dist"? or? Advice appreciated.
After some digging, what I actually wanted was: https://webpack.github.io/docs/webpack-dev-server.html#content-base
Then I modified my npm start script to:
"./node_modules/.bin/webpack-dev-server --hot --inline --progress --content-base src/"
and now relative image paths are happy again.
You can import (or require) images with webpack.
Add the file-loader to the webpack config (make sure file-loader is installed):
{
test: /\.(png|gif|jpg)$/,
include: [
path.join(__dirname, 'static')
],
loader: 'file',
}
You can then import an image:
import Image1 from '../static/images/image1.png';
and use it like this:
<img src={Image1} width="64" height="64" />
Webpack will place all imported images in the "dist" directory.
React router allows react apps to handle /arbitrary/route. In order this to work, I need my server to send the React app on any matched route.
But webpack dev server doesn't handle arbitrary end points.
There is a solution here using additional express server.
How to allow for webpack-dev-server to allow entry points from react-router
But I don't want to fire up another express server to allow route matching. I just want to tell webpack dev server to match any url and send me my react app. please.
I found the easiest solution to include a small config:
devServer: {
port: 3000,
historyApiFallback: {
index: 'index.html'
}
}
I found this by visiting: PUSHSTATE WITH WEBPACK-DEV-SERVER.
historyApiFallback option on official documentation for webpack-dev-server explains clearly how you can achieve either by using
historyApiFallback: true
which simply falls back to index.html when the route is not found
or
// output.publicPath: '/foo-app/'
historyApiFallback: {
index: '/foo-app/'
}
Adding public path to config helps webpack to understand real root (/) even when you are on subroutes, eg. /article/uuid
So modify your webpack config and add following:
output: {
publicPath: "/"
}
devServer: {
historyApiFallback: true
}
Without publicPath resources might not be loaded properly, only index.html.
Tested on Webpack 4.6
Larger part of config (just to have better picture):
entry: "./main.js",
output: {
publicPath: "/",
path: path.join(__dirname, "public"),
filename: "bundle-[hash].js"
},
devServer: {
host: "domain.local",
https: true,
port: 123,
hot: true,
contentBase: "./public",
inline: true,
disableHostCheck: true,
historyApiFallback: true
}
Works for me like this
devServer: {
contentBase: "./src",
hot: true,
port: 3000,
historyApiFallback: true
},
Working on riot app
My situation was a little different, since I am using the angular CLI with webpack and the 'eject' option after running the ng eject command. I modified the ejected npm script for 'npm start' in the package.json to pass in the --history-api-fallback flag
"start": "webpack-dev-server --port=4200 --history-api-fallback"
"scripts": {
"ng": "ng",
"start": "webpack-dev-server --port=4200 --history-api-fallback",
"build": "webpack",
"test": "karma start ./karma.conf.js",
"lint": "ng lint",
"e2e": "protractor ./protractor.conf.js",
"prepree2e": "npm start",
"pree2e": "webdriver-manager update --standalone false --gecko false --quiet",
"startold": "webpack-dev-server --inline --progress --port 8080",
"testold": "karma start",
"buildold": "rimraf dist && webpack --config config/webpack.prod.js --progress --profile --bail"},
I agree with the majority of existing answers.
One key thing I wanted to mention is if you hit issues when manually reloading pages on deeper paths where it keeps the all but the last section of the path and tacks on the name of your js bundle file you probably need an extra setting (specifically the publicPath setting).
For example, if I have a path /foo/bar and my bundler file is called bundle.js. When I try to manually refresh the page I get a 404 saying /foo/bundle.js cannot be found. Interestingly if you try reloading from the path /foo you see no issues (this is because the fallback handles it).
Try using the below in conjunction with your existing webpack config to fix the issue. output.publicPath is the key piece!
output: {
filename: 'bundle.js',
publicPath: '/',
path: path.resolve(__dirname, 'public')
},
...
devServer: {
historyApiFallback: true
}
If you choose to use webpack-dev-server, you should not use it to serve your entire React app. You should use it to serve your bundle.js file as well as the static dependencies. In this case, you would have to start 2 servers, one for the Node.js entry points, that are actually going to process routes and serve the HTML, and another one for the bundle and static resources.
If you really want a single server, you have to stop using the webpack-dev-server and start using the webpack-dev-middleware within your app-server. It will process bundles "on the fly" (I think it supports caching and hot module replacements) and make sure your calls to bundle.js are always up to date.
For me I had dots "." in my path e.g. /orgs.csv so I had to put this in my webpack confg.
devServer: {
historyApiFallback: {
disableDotRule: true,
},
},
You can enable historyApiFallback to serve the index.html instead of an 404 error when no other resource has been found at this location.
let devServer = new WebpackDevServer(compiler, {
historyApiFallback: true,
});
If you want to serve different files for different URIs, you can add basic rewriting rules to this option. The index.html will still be served for other paths.
let devServer = new WebpackDevServer(compiler, {
historyApiFallback: {
rewrites: [
{ from: /^\/page1/, to: '/page1.html' },
{ from: /^\/page2/, to: '/page2.html' },
{ from: /^\/page3/, to: '/page3.html' },
]
},
});
I know this question is for webpack-dev-server, but for anyone who uses webpack-serve 2.0. with webpack 4.16.5; webpack-serve allows add-ons.You'll need to create serve.config.js:
const serve = require('webpack-serve');
const argv = {};
const config = require('./webpack.config.js');
const history = require('connect-history-api-fallback');
const convert = require('koa-connect');
serve(argv, { config }).then((result) => {
server.on('listening', ({ server, options }) => {
options.add: (app, middleware, options) => {
// HistoryApiFallback
const historyOptions = {
// ... configure options
};
app.use(convert(history(historyOptions)));
}
});
});
Reference
You will need to change the dev script from webpack-serve to node serve.config.js.