I'm using the dot-env NPM package in order to pass simple variables to my webpack/express application.
When I run in PRODUCTION mode for webpack, all my variables from .env become undefined.
I'm building up a development and production webpack config files and currently have the following setup.
Any advise on the mistakes I'm making and why my .env variables are being dropped would be greatly appreciated.
package.json (Scripts)
"buildDev": "rm -rf dist && webpack --mode development --config webpack.server.config.js && webpack --mode development --config webpack.dev.config.js",
"buildProd": "rm -rf dist && webpack --mode production --config webpack.server.config.js && webpack --mode production --config webpack.prod.config.js",
webpack.dev
const path = require("path");
const webpack = require("webpack");
const HtmlWebPackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const paths = require("./config/paths");
const isDevelopment = false;
const Dotenv = require('dotenv-webpack');
require('dotenv').config()
module.exports = {
entry: {
main: [
"webpack-hot-middleware/client?path=/__webpack_hmr&timeout=20000",
paths.appIndexJs,
],
},
output: {
path: path.join(__dirname, "dist"),
publicPath: "/",
filename: "[name].js",
},
devServer: {
historyApiFallback: true,
},
mode: "production",
target: "web",
devtool: "#source-map",
module: {
rules: [
{
enforce: "pre",
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loader: "eslint-loader",
options: {
emitWarning: true,
failOnError: false,
failOnWarning: false,
},
},
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
include: path.resolve(paths.appSrc),
loader: "babel-loader",
},
{
// Loads the javacript into html template provided.
// Entry point is set below in HtmlWebPackPlugin in Plugins
test: /\.html$/,
include: path.resolve(paths.appSrc),
use: [
{
loader: "html-loader",
},
],
},
{
test: /\.css$/,
include: path.resolve(paths.appSrc),
use: ["style-loader", "css-loader"],
},
{
test: /\.css$/,
include: /node_modules/,
use: ["style-loader", "css-loader"],
},
{
test: /\.scss$/,
use: ["style-loader", "css-loader", "sass-loader"],
},
{
test: /\.(png|svg|jpg|gif|eot|woff|woff2|ttf)$/,
use: ["file-loader"],
},
],
},
resolve: {
extensions: [".js", ".jsx"],
},
plugins: [
new HtmlWebPackPlugin({
favicon: "./src/assets/img/favicons/favicon.ico",
template: "./src/html/index.html",
filename: "./index.html",
excludeChunks: ["server"],
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
new Dotenv()
],
};
webpack.prod
const path = require("path");
const webpack = require("webpack");
const HtmlWebPackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const paths = require("./config/paths");
const isDevelopment = false;
const Dotenv = require('dotenv-webpack');
require('dotenv').config()
module.exports = {
entry: {
main: [
"webpack-hot-middleware/client?path=/__webpack_hmr&timeout=20000",
paths.appIndexJs,
],
},
output: {
path: path.join(__dirname, "dist"),
publicPath: "/",
filename: "[name].js",
},
devServer: {
historyApiFallback: true,
},
mode: "production",
target: "web",
devtool: "#source-map",
module: {
rules: [
{
enforce: "pre",
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loader: "eslint-loader",
options: {
emitWarning: true,
failOnError: false,
failOnWarning: false,
},
},
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
include: path.resolve(paths.appSrc),
loader: "babel-loader",
},
{
// Loads the javacript into html template provided.
// Entry point is set below in HtmlWebPackPlugin in Plugins
test: /\.html$/,
include: path.resolve(paths.appSrc),
use: [
{
loader: "html-loader",
},
],
},
{
test: /\.css$/,
include: path.resolve(paths.appSrc),
use: ["style-loader", "css-loader"],
},
{
test: /\.css$/,
include: /node_modules/,
use: ["style-loader", "css-loader"],
},
{
test: /\.scss$/,
use: ["style-loader", "css-loader", "sass-loader"],
},
/*{
test: /\.scss$/,
use: [
{ loader: 'css-loader', options: { url: false, sourceMap: true } },
{ loader: 'sass-loader', options: { sourceMap: true }
},
]
},*/
/*{
test: /\.css$/,
include: path.resolve(paths.appSrc),
use: ["style-loader", "css-loader"],
},
{
test: /\.sass$/,
use: [
MiniCssExtractPlugin.loader,
{ loader: 'css-loader', options: { url: false, sourceMap: true } },
{ loader: 'sass-loader', options: { sourceMap: true } },
]
},
{
test: /\.css$/,
include: /node_modules/,
loader: [
isDevelopment ? "style-loader" : MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
modules: true,
sourceMap: isDevelopment,
},
},
],
},
{
test: /\.(scss)$/,
use: [
MiniCssExtractPlugin.loader, { loader: 'css-loader',
options: { url: false, sourceMap:
true } }, { loader: 'sass-loader', options: { sourceMap: true } },
]
},*/
{
test: /\.(png|svg|jpg|gif|eot|woff|woff2|ttf)$/,
//include: path.resolve(paths.appSrc),
use: ["file-loader"],
},
],
},
resolve: {
extensions: [".js", ".jsx"],
},
plugins: [
new webpack.DefinePlugin({
'process.env': { 'NODE_ENV': JSON.stringify('production') }
}),
new HtmlWebPackPlugin({
favicon: "./src/assets/img/favicons/favicon.ico",
template: "./src/html/index.html",
filename: "./index.html",
excludeChunks: ["server"],
}),
new webpack.NoEmitOnErrorsPlugin(),
new Dotenv()
],
};
webpack.server
const path = require("path");
const webpack = require("webpack");
const nodeExternals = require("webpack-node-externals");
const HtmlWebPackPlugin = require("html-webpack-plugin");
module.exports = (env, argv) => {
const SERVER_PATH =
argv.mode === "production" ? "./src/server/server-prod.js" : "./src/server/server-dev.js";
return {
entry: {
server: SERVER_PATH,
},
output: {
path: path.join(__dirname, "dist"),
publicPath: "/",
filename: "[name].js",
},
mode: argv.mode,
target: "node",
node: {
// Need this when working with express, otherwise the build fails
__dirname: false, // if you don't put this is, __dirname
__filename: false, // and __filename return blank or /
},
externals: [nodeExternals()], // Need this to avoid error when working with Express
devServer: {
historyApiFallback: true,
},
module: {
rules: [
{
// Transpiles ES6-8 into ES5
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
},
}
],
},
};
};
If you look at yow webpack.prod in its plugins section you have it like.
new webpack.DefinePlugin({
'process.env': { 'NODE_ENV': JSON.stringify('production') }
So basically you is setting process.env to be equals to What Eva is there
solution
first you need to install two modules
dotenv-expand
dotenv
$ npm i -D dotenv-expand dotenv
Next
create a new file called env.js, I mean the name is up to you. Place the file same place where yow webpacks are
env.js
'use strict';
const fs = require('fs');
const path = require('path');
const {NODE_ENV} = process.env.NODE_ENV;
const dotenvFile = `.env.${NODE_ENV}`;
require('dotenv-expand')(
require('dotenv').config({
path: dotenvFile,
}));
const appDirectory = fs.realpathSync(process.cwd());
process.env.NODE_PATH = (process.env.NODE_PATH || '')
.split(path.delimiter)
.filter(folder => folder && !path.isAbsolute(folder))
.map(folder => path.resolve(appDirectory, folder))
.join(path.delimiter);
// set your own prefix
const PREFIX = /^ENETO_/i;
function getEnvironment() {
const raw = Object.keys(process.env)
.filter(key => PREFIX.test(key))
.reduce(
(env, key) => {
env[key] = process.env[key];
return env;
},
{
NODE_ENV: process.env.NODE_ENV || 'development',
PORT: process.env.PORT||3000,
}
);
const stringified = {
'process.env': Object.keys(raw).reduce((env, key) => {
env[key] = JSON.stringify(raw[key]);
return env;
}, {}),
};
console.log("stringified", stringified);
return { raw, stringified };
}
module.exports = getEnvironment();
NOTE
Make sure to add a prefix so all of them shuld start with a PREFIX
you can set your own prefix
now on each one of yow webpacks add it;
webpack.server.js
/**
webpack server
*/
"use strict";
const path = require("path");
const nodeExternals = require("webpack-node-externals");
const webpack = require("webpack");
/**
* HERE IS IMPORTED ALL THE WAY TO THE BOTTOM YOU'LL SEE IT
*/
const vars = require("./env");
const { NODE_ENV } = process.env;
const entry = [path.join(__dirname, NODE_ENV === "production" ? `../src/prod.ts` : `../src/dev.ts`)];
module.exports = {
target: "node",
name: "server",
externals: [nodeExternals()],
entry: [
require.resolve("#babel/register"),
require.resolve("core-js/proposals"),
require.resolve("core-js"),
require.resolve("#babel/runtime-corejs3/regenerator"),
require.resolve("es6-promise/auto"),
...entry,
],
devtool: false,
mode: process.env.NODE_ENV,
output: {
filename: "[name]-bundle.js",
chunkFilename: "[name].chunk.js",
path: path.resolve(__dirname, "../bundle"),
publicPath: "/",
libraryTarget: "commonjs2",
},
resolve: {
extensions: [".ts", ".tsx", ".js"],
},
module: {
rules: [
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: [
{
loader: "babel-loader",
},
],
},
{
test: /\.(js|mjs)$/,
exclude: /#babel(?:\/|\\{1,2})runtime/,
loader: require.resolve("babel-loader"),
options: {
babelrc: false,
configFile: false,
compact: false,
presets: [[require.resolve("#babel/preset-env")]],
cacheDirectory: true,
cacheCompression: false,
sourceMaps: false,
inputSourceMap: false,
},
},
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: "file-loader",
},
],
},
],
},
plugins: [new webpack.DefinePlugin(vars.stringified)],
};
Do the same with yow client and thats it :3
do not need third part deps.
just use env like this:
--node-env=
// in package.json
"build": "webpack --node-env=production --config build/webpack.config.js"
or
module.exports = (env, argv) => {
const isDevelopment = argv.mode !== 'production';
return {
// Config...
}
}
see:
--node-env
argv
Im new to webpack. Im using flask-cookie-cutter which in turn uses Flask-Webpack
Im having issues in that App wont find my images in development.
Images are in assets/img folder
Ive tried using {{ asset_url_for('img/img_name.png') }} and various variations of it - and it just returns none.
Ive tried the usual flask way of {{ url_for('static', filename='img/img_name.png') }}
But I cant seem to access them.
const path = require('path');
const webpack = require('webpack');
/*
* Webpack Plugins
*/
const ManifestRevisionPlugin = require('manifest-revision-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// take debug mode from the environment
const debug = (process.env.NODE_ENV !== 'production');
// Development asset host (webpack dev server)
const publicHost = debug ? 'http://0.0.0.0:2992' : '';
const rootAssetPath = path.join(__dirname, 'assets');
module.exports = {
// configuration
context: __dirname,
entry: {
main_js: './assets/js/main',
main_css: [
path.join(__dirname, 'node_modules', 'materialize-css', 'dist', 'css', 'materialize.css'),
path.join(__dirname, 'assets', 'css', 'style.css'),
],
},
output: {
path: path.join(__dirname, 'dsi_website', 'static', 'build'),
publicPath: `${publicHost}/static/build/`,
filename: '[name].[hash].js',
chunkFilename: '[id].[hash].js',
},
resolve: {
extensions: ['.js', '.jsx', '.css'],
},
devtool: 'source-map',
devServer: {
headers: { 'Access-Control-Allow-Origin': '*' },
},
module: {
rules: [
{
test: /\.less$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
hmr: debug,
},
},
'css-loader!less-loader',
],
},
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
hmr: debug,
},
},
'css-loader',
],
},
{
test: /\.html$/,
loader: 'raw-loader'
},
{
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: 'url-loader',
options: {
limit: 10000,
mimetype: 'application/font-woff'
}
},
{
test: /\.(ttf|eot|svg|png|jpe?g|gif|ico)(\?.*)?$/i,
loader: `file-loader?context=${rootAssetPath}&name=[path][name].[hash].[ext]`
},
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
query: {
presets: ['env'],
cacheDirectory: true
}
},
],
},
plugins: [
new MiniCssExtractPlugin({ filename: '[name].[hash].css', }),
new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery' }),
new ManifestRevisionPlugin(path.join(__dirname, 'dsi_website', 'webpack', 'manifest.json'), {
rootAssetPath,
ignorePaths: ['/js', '/css'],
}),
].concat(debug ? [] : [
// production webpack plugins go here
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production'),
}
}),
]),
};
I was the same problem with flask-cookie-cutter and solve it by update/upgrade npm and add extensionsRegex: /\.(jpe?g|png|gif|svg)$/i at down file: webpack.config.js like this:
ManifestRevisionPlugin(path.join(__dirname, '<your app>', 'webpack', 'manifest.json'), {
rootAssetPath,
ignorePaths: ['/js', '/css'],
extensionsRegex: /\.(jpe?g|png|gif|svg)$/i
}),
source solution. Proper call to show image in temaplate:
{{ asset_url_for('img/img_name.png') }}
after that i check startup screen and i see that all my images load properly
[WEBPACK] _/assets/img/image_name.20aed528d50316c91ffedb5ba47b8c74.jpg 18.9 KiB [emitted]
Screenshots from the devtools and vs code after running the application with webpack.
This is my config file:
var webpack = require('webpack');
var globby = require('globby');
var path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var CleanWebpackPlugin = require('clean-webpack-plugin');
var AssetsPlugin = require('assets-webpack-plugin');
var CopyWebpackPlugin = require('copy-webpack-plugin');
const ConcatPlugin = require('webpack-concat-plugin');
const extractLESS = new ExtractTextPlugin('[name].css');
module.exports = {
entry: {
app: globby.sync(['./app/app.js','./app/app.run.js', './app/app.config.js', './app/**/*.js']),
lessStyles: globby.sync(['./content/styles/less/*.less']),
styles: globby.sync(['./content/styles/*.css']),
images: globby.sync(['./content/images/**/*.*']),
vendor: [
//removed to save space
],
},
output: {
filename: './scripts/[name].bundle.js',
path: path.join(__dirname, "public")
},
devServer: {
port: 1384,
contentBase: './public/'
},
// Enable sourcemaps for debugging webpack's output.
devtool: 'source-map',
module: {
rules: [
{
test: /\.html$/,
loader: 'raw-loader',
exclude: [/node_modules/]
},
{
test: /\.css$/,
loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader' }),
},
{ test: /\.less$/,
use: extractLESS.extract([ 'css-loader', 'less-loader' ])
},
{
test: /\.(ico)$/,
loader: "url-loader?name=./[name].[ext]",
include: path.resolve(__dirname, "content", "images")
},
{
test: /\.svg$/,
loader: 'svg-loader'
},
{
test: /\.(jpg|jpeg|gif|png|PNG|tiff|svg)$/,
loader: 'file-loader?name=/[path]/[name].[ext]',
include: path.resolve(__dirname, "content", "images"),
},
{
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: 'url-loader?minetype=application/font-woff&name=./fonts/[name].[ext]'
},
{
test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
loader: 'file-loader?name=./fonts/[name].[ext]'
},
{
test: require.resolve('adal-angular/lib/adal'),
loader: 'expose-loader?AuthenticationContext'
},
{
test: /\.js$/,
enforce: "pre",
loader: 'source-map-loader'
}
],
},
plugins: [
new webpack.DefinePlugin({
ADMIN_API_URL: JSON.stringify('http://localhost:41118/api/'),
API_URL: JSON.stringify('http://epdapi.tradesolution.no/api/'),
GLOBAL_ADMIN_URL: JSON.stringify('https://adminapi.tradesolution.no/')
}),
new HtmlWebpackPlugin({
template: './app/layout.html',
filename: 'index.html'
}),
new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', filename: './scripts/vendor.bundle.js' }),
new ExtractTextPlugin({ filename: './[name].bundle.css' }),
extractLESS,
/*
new CleanWebpackPlugin(['./public'], {
verbose: false
}),
*/
new AssetsPlugin({
filename: 'webpack.assets.json',
path: './public/scripts',
prettyPrint: true
}),
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery',
'window.$': 'jquery',
"window.AuthenticationContext": "AuthenticationContext",
_: 'underscore'
}),
new CopyWebpackPlugin([
{from: './app/**/*.html', to: './'}
]),
/*
new ConcatPlugin({
uglify: true,
sourceMap: true,
name: 'all',
fileName: '[name].bundle.css',
filesToConcat: [
'./public/styles.css',
'./public/vendor.css',
'./public/lessStyles.css',
]
})
*/
],
externals: [
{ xmlhttprequest: '{XMLHttpRequest:XMLHttpRequest}' }
]
}
I am very confused as to why the two folder are so different. For example: the fonts folder isn't even there in the browser, and there is a styles folder within content, that I have no idea where it came from.
Other files appear to be there but aren't. The ts-logo.svg for example says 0 B when i click it in devtools, but running wget form the terminal gives another result:
wget http://localhost:1384/content/images/ts-logo.svg
--2017-11-07 10:23:58-- http://localhost:1384/content/images/ts-logo.svg
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:1384... connected.
HTTP request sent, awaiting response... 200 OK
Length: 65 [image/svg+xml]
Saving to: ‘ts-logo.svg’
ts-logo.svg 100%[======================>] 65 --.-KB/s in 0s
2017-11-07 10:23:58 (4,65 MB/s) - ‘ts-logo.svg’ saved [65/65]
Server.js
var express = require('express');
app.use(express.static('public'));
app.listen(PORT, function () {
console.log('Express server is up on port ' + PORT);
});
Webpack.config.js
var path = require('path');
var webpack = require('webpack');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
const extractSass = new ExtractTextPlugin({
});
// const autoprefixer = require('autoprefixer');
module.exports = {
entry: path.resolve(__dirname, 'src', 'Main.js'),
output: {
path: __dirname,
publicPath: '/',
filename: 'bundle.js'
},
module: {
loaders: [{
exclude: /node_modules/,
test: /\.js$/,
loader: 'babel-loader',
query: {
presets: ['react', 'es2015', 'stage-1'],
plugin
s: ["react-html-attrs"]
}
}
, {
test: /\.(png|jpg|jpeg|gif|svg|woff|woff2)$/,
loader: 'url-loader?limit=10000',
},
{
test: /masonry|imagesloaded|fizzy\-ui\-utils|desandro\-|outlayer|get\-size|doc\-ready|eventie|eventemitter/,
loader: 'imports-loader?define=>false&this=>window'
},
{
test: /\.css$/,
loader: "style-loader!css-loader"
},
{
test: /\.scss$/,
loaders: ["style-loader", "css-loader", "sass-loader"]
}]
},
plugins: [
extractSass
],
resolve: {
extensions: ['.js', '.jsx', '.css', '.scss'],
},
devServer: {
historyApiFallback: true,
contentBase: './',
}
};
I am trying to deploy on Heroku But before that when I run on cmd through npm start it starts on localhost but say Cannot GET / and nothing else runs but when I pass on cmd webpack-dev-server --config webpack.config.js it runs on localhost 8080 and works fine! I need to add serve.js before deploying on heroku what I am missing?
Make sure you have a file named Procfile that includes web: <command to run your project>. That command might be node server.js or something else dependent on Webpack, which has a Production Guide here (possibly webpack -p).