How to handle static assets with webpack server side? - javascript

I'm trying to create an universal react app (using webpack both on server and on the client) and struggle with images import. I want to write this :
import someImage from './someImage.png'
const SomeImage = () => <img src={someImage}/>
Here's my webpack config file:
var path = require('path');
var webpack = require('webpack');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: [
'webpack-dev-server/client?http://127.0.0.1:8080/',
'webpack/hot/only-dev-server',
'./client',
'babel-polyfill'
],
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js'
},
resolve: {
modulesDirectories: ['node_modules', 'shared'],
extensions: ['', '.js', '.jsx']
},
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loaders: ['babel']
},
{
test: /\.css/,
exclude: /node_modules/,
loader: ExtractTextPlugin.extract('style', 'css?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]')
},
{
test: /\.(jpe?g|png|gif|svg)$/i,
loaders: [
'file?emitFile=false',
]
}
]
},
plugins: [
new ExtractTextPlugin('styles.css', { allChunks: true }),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin()
],
devtool: 'inline-source-map',
devServer: {
hot: true,
proxy: {
'*': 'http://127.0.0.1:' + (process.env.PORT || 3000)
},
host: '127.0.0.1'
}
};
Obviously it's not working server side because node try to read the content of the ./someImage.png file, resulting in an error.
How can I handle this ? I know there are packages such as webpack-isomorphic-tools or universal-webpack or even the file-loader package that can emit or not the file, but I don't understand of to use it in my universal app.

I'm using file-loader with emitFile: false to exclude assets from bundling on server side. Works as expected.
const imageRules = (emit = true) => ({
test: /\.(png|svg|jpeg|jpg|gif|ico)$/,
type: "asset",
generator: {
emit: emit,
},
});
Then use it in webpack client config:
module: {
rules: [imageRules()],
},
And in server config
module: {
rules: [imageRules(false)],
},

Related

How do I access process.env.VAR in Webpack 5?

I've never used Webpack before and I'm working on a project that's just vanilla JS and HTML. I'm having an issue accessing the values I set in .env. Here's my config.
const path = require("path");
const dotenv = require('dotenv');
var webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const Dotenv = require('dotenv-webpack');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = () => {
env = dotenv.config().parsed;
const envKeys = Object.keys(env).reduce((prev, next) => {
prev[`process.env.${next}`] = JSON.stringify(env[next]);
return prev;
}, {});
return {
entry: {
main: './src/index.js'
},
output: {
path: path.join(__dirname, '../build'),
filename: '[name].bundle.js'
},
mode: 'development',
devServer: {
contentBase: "./src/",
publicPath: "./src/",
compress: true,
port: 9000,
overlay: true,
disableHostCheck: true
},
devtool: 'inline-source-map',
resolve: {
alias: {
process: "process/browser"
}},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
},
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
},
{
test: /\.(png|svg|jpe?g|gif)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'assets/'
}
}
]
},
{
test: /\.html$/,
use: {
loader: 'html-loader',
options: {
//attributes: ['img:src', ':data-src'],
minimize: true
}
}
}
]
},
plugins: [
new webpack.ProvidePlugin({
process: 'process/browser',
}),
new Dotenv(),
new webpack.DefinePlugin(envKeys),
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html'
}),
]
}
};
As you can see, I'm using dotEnv, definePlugin, and even the dotEnv-webpack plugin. Unfortunately, none of these solutions seem to allow me to access process.env.originURL.
originURL=https://localhost:3000
I'm not importing or requiring anything in my javascript file, index.js. I'm assuming this should work, but the console tells me that process is undefined.
console.log(process.env.originURL);
How can I access process.env.originURL in my index.js?
It should work. Maybe you could try this version:
new Dotenv({ systemvars: true })
If it still doesn't work, please show your package.json, as well as you .env file (randomize the values of course). Is .env at the root of you app?

Webpack HMR not reloading HTML file

I have a simple HMR setup for reloading typescript files and postcss files. And they work perfectly well and modules reload without a page refresh. But when I change my HTML files, the website doesn't reload on it's own and the HTML content is not being hot-reloaded in.
This is my webpack config file:
const { resolve } = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
entry: resolve(__dirname, 'src/main.ts'),
output: {
path: resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.ts$/,
loader: 'awesome-typescript-loader',
exclude: /node_modules/,
},
{
test: /\.css$/,
exclude: /node_modules/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader',
options: {
importLoaders: 1,
},
},
{
loader: 'postcss-loader',
}
]
}
]
},
devtool: 'source-map',
mode: 'development',
plugins: [
new HtmlWebpackPlugin({
template: resolve(__dirname, './src/index.html'),
}),
new webpack.HotModuleReplacementPlugin(),
new CleanWebpackPlugin(resolve(__dirname, 'dist'))
],
devServer: {
contentBase: resolve(__dirname, 'dist'),
port: 9000,
hot: true,
open: true,
progress: true,
}
}
With webpack 5,this setting is work for me:
devServer: {
hot:true,
open:true,
watchFiles: ['src/**/*']
},
webpack-dev-server github issue #3881
Problem is that html-webpack-plugin doesn't react to change and doesn't trigger the hmr.
In order to achieve that you could try something like this:
const { resolve } = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
let devServer; // set below in devserver part
function reloadHtml() {
this.plugin('compilation',
thing => thing.plugin('html-webpack-plugin-after-emit', trigger));
const cache = {};
function trigger(data, callback) {
const orig = cache[data.outputName];
const html = data.html.source();
if (orig && orig !== html)
devServer.sockWrite(devServer.sockets, 'content-changed');
cache[data.outputName] = html;
callback();
}
}
module.exports = {
entry: resolve(__dirname, 'src/main.ts'),
output: {
path: resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.ts$/,
loader: 'awesome-typescript-loader',
exclude: /node_modules/,
},
{
test: /\.css$/,
exclude: /node_modules/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader',
options: {
importLoaders: 1,
},
},
{
loader: 'postcss-loader',
}
]
}
]
},
devtool: 'source-map',
mode: 'development',
plugins: [
reloadHtml,
new HtmlWebpackPlugin({
template: resolve(__dirname, './src/index.html'),
}),
new webpack.HotModuleReplacementPlugin(),
new CleanWebpackPlugin(resolve(__dirname, 'dist'))
],
devServer: {
before(app, server) {
devServer = server;
},
contentBase: resolve(__dirname, 'dist'),
port: 9000,
hot: true,
open: true,
progress: true,
}
}
If you get :
DeprecationWarning: Tapable.plugin is deprecated. Use new API on `.hooks` instead
You can change reloadHtml to this:
function reloadHtml() {
const cache = {}
const plugin = {name: 'CustomHtmlReloadPlugin'}
this.hooks.compilation.tap(plugin, compilation => {
compilation.hooks.htmlWebpackPluginAfterEmit.tap(plugin, data => {
const orig = cache[data.outputName]
const html = data.html.source()
if (orig && orig !== html) {
devServer.sockWrite(devServer.sockets, 'content-changed')
}
cache[data.outputName] = html
})
})
}
Not sure if this is the case, but if you only want to extend your current HMR config to do a browser reload when your outer html/view files changed, then you can do it with a few extra lines using chokidar.
Maybe you already have it, because webpack-dev-server is using chokidar internally, but if not found then install it first with npm or yarn:
npm install chokidar --save
yarn add -D chokidar
Then require it in your webpack config:
const chokidar = require('chokidar');
Then in your devServer config:
devServer: {
before(app, server) {
chokidar.watch([
'./src/views/**/*.html'
]).on('all', function() {
server.sockWrite(server.sockets, 'content-changed');
})
},
Also check the API for more options.
I'm using it with Webpack4. Don't know if it works with earlier versions...
Hope it helps you or others looking for this situation.

Webpack doesn't compile on linux

I developed a program on windows
It successfully compiles and runs now
However, when I want to deploy it to my linux server, I get some errors:
This problem is very strange, can you help?
My webpack.config.js
let webpack = require('webpack'),
path = require('path'),
fs = require('fs'),
manifest = require('./manifest.json'),
{resolve, join} = path,
env = process.env.NODE_ENV;
let plugins = [
new webpack.BannerPlugin(
fs
.readFileSync('./LICENSE')
.toString('utf-8')),
new webpack.DllReferencePlugin({context: __dirname, manifest})
];
// if (env === 'pro') {
// } else {
// plugins.push()
// }
let config = {
// context: resolve(__dirname, 'src'),
entry: {
app: [
'./src/jsx/app.jsx',
'./src/style/app.less'
]
},
output: {
path: resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
devtool: false,
module: {
loaders: [{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loader: 'babel-loader',
query: {
presets: [
'stage-2',
'es2015',
'react'
]
}
},{
test: /\.less$/,
loader: 'style!css!autoprefixer!less'
}]
},
resolve: {
extensions: ['', '.js', '.jsx']
},
plugins
}
module.exports = config;

Webpack 2 beta + history API fallback not working

Today, after removing my node_modules and reinstalling them using npm install, my project doesn't seem to work.
Here's my webpack config
const webpack = require('webpack');
const path = require('path');
const srcPath = path.join(__dirname, './client');
const nodeEnv = process.env.NODE_ENV || 'development';
const isProd = nodeEnv === 'production';
module.exports = {
devtool: isProd ? 'hidden-source-map' : 'cheap-module-eval-source-map',
context: path.join(__dirname, './client'),
entry: {
js: './index.js',
vendor: ['react']
},
output: {
path: path.join(__dirname, './static'),
filename: 'bundle.js'
},
module: {
loaders: [
{
test: /\.html$/,
loader: 'file',
query: {
name: '[name].[ext]'
}
},
{
test: /\.scss$/,
loaders: ['style', 'css', 'sass']
},
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loaders: ['babel-loader']
},
{
test: /\.(png|jpg|gif|otf|eot|svg|ttf|woff|woff2)/,
loader: 'url-loader'
},
{
test: /\.(txt|json)/,
loader: 'raw-loader'
}
],
},
resolve: {
extensions: ['', '.js', '.jsx'],
modules: [
path.resolve('./client'),
'node_modules'
],
alias: {
stores: `${srcPath}/stores/`,
components: `${srcPath}/components/`,
services: `${srcPath}/services`,
models: `${srcPath}/models`,
constants: `${srcPath}/constants`,
sources: `${srcPath}/sources`,
images: `${srcPath}/assets/images`,
appConstants: isProd ? `${srcPath}/constants/_prod` : `${srcPath}/constants/_dev`
}
},
plugins: [
new webpack.IgnorePlugin(/regenerator|nodent|js\-beautify/, /ajv/),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: Infinity,
filename: 'vendor.bundle.js'
}),
new webpack.LoaderOptionsPlugin({
minimize: true,
debug: isProd
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: !isProd
},
output: {
comments: !isProd
},
sourceMap: !isProd
}),
new webpack.DefinePlugin({
'process.env': { NODE_ENV: JSON.stringify(nodeEnv) }
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.optimize.OccurrenceOrderPlugin()
],
devServer: {
contentBase: './client',
hot: true,
port: 3000,
historyApiFallback: true
}
};
My "client" folder with index.html and the rest of my code are in the same folder as webpack config.
Webpack does succesfully build, but going to localhost:3000, I get error message: "Cannot GET /"
Going to localhost:3000/client/index.html does serve my index.html, but my built files inserted using
<script src="./vendor.bundle.js"></script>
<script src="./bundle.js"></script>
doesn't load (GET to "http://localhost:3000/client/bundle.js" results in 404)
Anyone knows what's going on? I can't fix this issue, and I think I've tried everything, from changing path, publicPath to changing contentBase and moving my static files to different folder.
It's very strange, since this issue appeared only after reinstalling my project dependencies.
Every bit of help is much appreciated. Thanks.
There was an issue in webpack-dev-server#2.1.0-beta.3, which caused the contentBase option to be ignored. Could you try upgrading to 2.1.0-beta.4? It was just released.

Webpack doesn't catch the changes in Typescript files

I am working on an Angular project and use Webpack bundler. Everything was working fine until Webpack started to ignore my changes to Typescript files. When I change a Typescript file, Webpack first says:
[WDS] App updated. Recompiling...
followed by:
[WDS] Nothing changed.
Also, killing webpack-dev-server and rebuilding the sources doesn't make any difference. The changes I make to TS files are not applied in any way.
EDIT: Loader setup on request (which is exactly the same as in the Angular Developer Guide):
webpack.common.js:
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var helpers = require('./helpers');
module.exports = {
entry: {
'polyfills': './src/polyfills.ts',
'vendor': './src/vendor.ts',
'app': './src/main.ts'
},
resolve: {
extensions: ['', '.js', '.ts']
},
module: {
loaders: [
{
test: /\.ts$/,
loaders: ['awesome-typescript-loader', 'angular2-template-loader']
},
{
test: /\.html$/,
loader: 'html'
},
{
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
loader: 'file?name=assets/[name].[hash].[ext]'
},
{
test: /\.css$/,
exclude: helpers.root('src', 'app'),
loader: ExtractTextPlugin.extract('style', 'css?sourceMap')
},
{
test: /\.css$/,
include: helpers.root('src', 'app'),
loader: 'raw'
}
]
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: ['app', 'vendor', 'polyfills']
}),
new HtmlWebpackPlugin({
template: 'src/index.html'
})
]
};
webpack.dev.js:
var webpackMerge = require('webpack-merge');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var commonConfig = require('./webpack.common.js');
var helpers = require('./helpers');
module.exports = webpackMerge(commonConfig, {
devtool: 'cheap-module-eval-source-map',
output: {
path: helpers.root('dist'),
publicPath: 'http://localhost:8080/',
filename: '[name].js',
chunkFilename: '[id].chunk.js'
},
plugins: [
new ExtractTextPlugin('[name].css')
],
devServer: {
historyApiFallback: true,
stats: 'minimal'
}
});

Categories