How to handle ejs views using webpack - node.js

I'm trying to configure webpack with my website using node js, I'm using also ejs as a view. I have tried with many ways to handle the ejs in my webpack, but till now I didn't get success.
const path = require('path')
const nodeExternals = require('webpack-node-externals')
module.exports = (env, argv) => {
return ({
entry: {
server: './src/app.js',
},
output: {
path: path.join(__dirname, 'dist'),
publicPath: '/',
filename: 'main.js'
},
mode: argv.mode,
target: 'node',
node: {
// Need this when working with express, otherwise the build fails
__dirname: true,
__filename: true,
},
externals: [nodeExternals()], // Need this to avoid error when working with Express
module: {
rules: [
{
// Transpiles ES6-8 into ES5
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: /\.ejs$/,
loader: 'ejs-loader',
options: {
esModule: false
}
}
]
}
})
}
when I use HtmlWebPackPlugin I get some errors because of data inside <%- %> it's like he didn't know this data where comes from. like for example, <%- include('partials/head.ejs') %>.
is there a way to handle my views as ejs using webpack?

I know this has been asked a few months ago. But for those who come across this issue like I have, this is how I got this to work. Assuming your using webpack 4.
If you have not already install html-webpack-plugin
Most importantly to help solve the issue install raw-loader
add the following to your webpack config
new HtmlWebpackPlugin({
template: '!!raw-loader!./src/views/pages/<file-name-here>.ejs',
filename: 'index.ejs',
chunks: ['main', 'owl_carousel']
})
This is where the magic is. when including the template path make sure to include !!raw-loader! followed by the relative path.
raw-loader makes it so when html plugin creates the file it ignores the special syntax ejs uses. It is basically like "hey plugin ignore whatever I put here and just get me my file".

As #JRichardsz explained, You won't need Webpack explicitly to use EJS templates in your NodeJS project.
Also, It simply bundles up the EJS template (code) implicitly.
Try to bundle up your files with latest Webpack.js using below command to install:
npm install --save-dev webpack
Also, try this code with a little fix:
module.exports = (env, argv) => {
return ({
output: {
path: path.resolve(__dirname, 'dist'),
publicPath: '/',
filename: 'main.js'
}
...
// in case, all of this doesn't work. Then, explicitly whitelist EJS using regex
...
nodeExternals({
whitelist: [/\.(?|ejs)$)],
}),
...
})
}

If you want to use ejs for nodejs projects, you don't need webpack. Webpack is commonly used for client side rendering like angular, vue, react, etc
Check this: Which command should I use to minify and optimize nodejs express application? to view some ways to optimize your static js files used in ejs or another nodejs server rendering framework.
Basic structure for ejs projects is:
|____
|____server.js
|____views
| |____hello.ejs
|____package.json
hello.ejs a simple and plain template in which you can use any of ejs code like your
<%- include('partials/head.ejs') %>
As you can see, you don't need webpack to run ejs apps.
Check these samples:
minimal ejs sample
partials sample

You would need to make bundle of the EJS.
Try below commands:
module.exports = (env, argv) => {
return ({
output: {
path: './dist',
publicPath: '/',
filename: 'main.js'
}
})
}

copy-webpack-plugin worked perfectly
plugins: [
new webpack.ProgressPlugin(),
new CopyWebpackPlugin({
patterns: [
{from: "src/views", to: "views"}
]
})

Related

Bundling files in "src" folder into "public" folder using webpack

How to Bundle all the files inside “src” folder and replace the existing bundled files inside “Public” folder in any Node.js web app?
My project structure is similar to this: https://github.com/googlearchive/friendlypix-web
For javascript files, you should have at least one entry-point for your application in order to bundle it.
Example of webpack.config.json with one entrypoint src/index.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'public'),
},
};
More information about bundling one or many entry-points : https://webpack.js.org/concepts/entry-points/
Furthermore, if you have imported static assets (images, css, saas, fonts...) in your javascript files, you need to add some configuration and eventually install webpack loaders to bundle them.
For example, for CSS assets :
Install webpack loaders via npm install --save-dev style-loader css-loader
Update webpack configuration
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'public'),
},
module: {
rules: [
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
],
},
};
More information about asset management : https://webpack.js.org/guides/asset-management/
Finally, you may need to bundle static files or directories that are not imported in your javascript files and you want to copy them as they are.
In this case, you could use the copy-webpack-plugin :
Install plugin via npm install copy-webpack-plugin --save-dev
Update webpack configuration
const CopyPlugin = require("copy-webpack-plugin");
module.exports = {
...
plugins: [
new CopyPlugin({
patterns: [
{ from: "source", to: "dest" },
{ from: "other", to: "public" },
],
}),
],
...
}
More info about copy-webpack-plugin : https://webpack.js.org/plugins/copy-webpack-plugin/
I really recommend you to take a look at the official webpack documentation which covers topics including creating production bundles, code splitting, caching and other tips and hints.

Creating an npm package from CRA build

We are trying to create a microfront ends app, we would like to take each micro app (created with CRA) run
npm run build
over this app, take the /build folder created, and make a npm package out of it and publish it to our npm repo.
We don't want to ejact the app projects to be able to edit the webpack.config.
We prefer to take the /build and pass it over webpack again (even in a different project) in order to get an output that can be used as npm package and can be published and imported correctly to a new project.
Im trying doing it by taking the /build folder and running it over webpack with this configuration:
const path = require("path");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const glob = require("glob");
module.exports = {
entry: {
"bundle.js": glob
.sync("build/static/?(js|css)/main.*.?(js|css)")
.map(f => path.resolve(__dirname, f))
},
output: {
filename: "build/static/js/bundle.min.js",
libraryTarget: "commonjs2"
},
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
}
]
},
plugins: [new UglifyJsPlugin()],
resolve: {
alias: {
src: path.join(__dirname, "./src")
}
},
externals: {
react: "commonjs react"
}
};
The result is a single js. after publishing it to npm, im trying to import it and getting many errors like:
I think there is something missing in my webpack.config or maybe there is a different way to take the all /build folder and combine it to something that can be published as npm package and imported correctly?
Any help will be very much appreciated!
Thank you!

Module not found: Error: Can't resolve 'fs' Using Webpack

Hi im using socket io in my application. Which is requiring fs. when i try to bundle my javascript using below webpack config. im getting error can not resolve 'fs'.
Module not found: Error: Can't resolve 'fs' in 'my application path/node_modules/socket.io/lib'
i found by adding target:'node' and node:{fs:'empty'}. this issue got resolved.
But there is an issue in sass-loader. Getting below error.
ERROR in javascript/bundle.js from UglifyJs
Unexpected token: name (zlibLimiter) [javascript/bundle.js:60019,4]
Child extract-text-webpack-plugin ../../../node_modules/extract-text-webpack-plugin/dist ../../../node_modules/css-loader/index.js??ref--2-2!../../../node_modules/sass-loader/lib/loader.js!s
running the application ignoring above error. getting below error.
external "crypto":1 Uncaught ReferenceError: require is not defined
at Object.__decorate (external "crypto":1)
at __webpack_require__ (bootstrap 93620a17882f7a2aa1d3:19)
at Object.byteToHex (rng.js:4)
at __webpack_require__ (bootstrap 93620a17882f7a2aa1d3:19)
Below is my webpack config and versions. Can some one please help me to resolve this issue.
"webpack": "~3.6.0",
npm -v 5.8.0
node -v v8.4.0
const webpack = require('webpack');
const env = process.env.NODE_ENV;
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const path = require('path');
const extractSass = new ExtractTextPlugin({
filename: 'css/[name].css',
allChunks: false
});
let output = {
path: __dirname + '/src/main/resources/static/',
filename: 'javascript/[name].js'
};
if (env === 'debug' || env === 'nondev') {
output = {
path: __dirname + '/target/classes/static/',
filename: 'javascript/[name].js'
};
}
let config = {
context: __dirname + '/app/js/src',
entry: {
bundle: './index.jsx',
application: './static/scss/application.scss',
'application-pdap': './static/scss/application-pdap.scss'
},
output: output,
devtool: 'cheap-module-source-map',
module: {
rules: [
{
test: /.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/,
query: {presets: ['es2015', 'react']}
},
{
test: /\.(woff|woff2|eot|ttf|svg|png|jpg|gif)$/,
loader: 'file-loader?limit=1024&name=images/[name].[ext]'
},
{
test: /\.(scss|css)$/,
include: [path.resolve(__dirname, 'app/js/src/static/scss')],
use: ExtractTextPlugin.extract({
publicPath: '../',
use: [
{
loader: 'css-loader',
options: {
minimize: true,
sourceMap: false
}
},
{loader: 'sass-loader'}
],
fallback: 'style-loader'
})
}
]
},
plugins: [extractSass],
};
if (env === 'production' || env === 'nondev') {
config.devtool = 'nosources-source-map';
config.plugins.push(
new webpack.DefinePlugin({
'process.env': {NODE_ENV: '"production"'}
})
);
config.plugins.push(new webpack.optimize.UglifyJsPlugin({
compress: {warnings: false},
comments: false,
sourceMap: false,
minimize: false
}));
}
module.exports = config;
The solution depends on the type of application you want to build. Usually front-end and back-end JavaScript code are bundled separately, effectively creating two output bundles.
Front-end
For a frontend/web project, add the socket.io client libary to your app bundle. There is no need to include any node dependencies (fs) or mock entries like node: { fs:'empty' }. You can choose target:'web' or leave it out, as it is the default.
Back-end
Pick target:'node' and install socket.io server library. You do not need to specify externals: ["fs"] as shown in the other answer, because target: 'node' will take care of not bundling path, fs and other built-in modules.
Better avoid npm i fs - this is a needless escape hatch and a security risk. There have already happened cases of malicious npm packages with common package names.
You could even deliberate about, wether a node backend bundle is needed at all. An alternative is to install webpack-node-externals, which treats either all or specific npm packages as "externals" and excludes them from the bundle:
var nodeExternals = require('webpack-node-externals');
module.exports = {
target: 'node', // ignore built-in modules like path, fs, etc.
externals: [nodeExternals()], // ignore all modules in node_modules folder
// ...
};
This makes sense for the back-end, as all dependencies are installed in node_modules at server start time and don't need to be included in the bundle.
Exclude files from loaders
To exclude files from certain loaders and their transformations, you can use exclude module rule. An example is to omit node_modules from babel-loader transformations:
{ test: /\.(jsx|js)$/, exclude: /node_modules/, loader: "babel-loader" }
Further reading
Just install fs (npm i fs), and add at your webpack config
externals: ["fs"],
If someone is still facing this issue, you can try the following workaround. Update your webpack file to including the following configuration:
node: {
fs: 'empty',
}
We had a similar issue within our project and adding this specific configuration resolved the 'fs' related error in webpack.
It's also worth checking the version of Webpack version. we had to revert back the webpack and webpack cli versions to 4.0.0 and 4.2.0 respectively.

Node, Webpack and React Routing using FS filesystem

I want to read and write a file using NodeJS default fs module.
I've almost tried everything I can find on the internet. ES6 and CommonJS examples, editing the Webpack.config file, added packages that should add Promises and so on.. But nothing seems to work out.
Currently this is my webpack.config.js
var webpack = require('webpack');
module.exports = {
entry: './index.js',
output: {
path: 'public',
filename: 'bundle.js',
publicPath: ''
},
node: {
fs: "empty"
},
"browser": { "fs": false },
resolve: {
modulesDirectories: ['web_modules', 'bower_components', 'node_modules']
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader?presets[]=es2015&presets[]=react'
}
]
}
}
As I've mentioned some lines suggested in this topic where added.
I want to use the fs.readFile in the component shown below:
var React = require('react');
var fs = require('fs');
var QuestionList = React.createClass({
handleVote: function(id, state) {
var file = '../public/api/questions';
fs.readSyncFile(file, function(err, data) {
console.log( err );
console.log( data );
});
},
render() {
var rows = []
this.state.questions.forEach( function(question) {
rows.push(
<QuestionItem
onVoteUpdate={this.handleVote}
key={question.id}
up={question.upvotes} down={question.downvotes}
/>
);
}.bind(this));
return (
<section>
<ul className="question-list">{rows}</ul>
</section>
)
}
});
module.exports = QuestionList;
I've removed some functions, like loading the question with jQuery and set the InitialState, for this example.
I'll can imagine webpack can't build any back-end tasks like FS in an front-end js file or something like that, but how is it possible to read and write files using Node, Webpack and React? Is this even possible?
You can't use fs in a front-end JS file, as far as I know, as you don't have access to the filesystem from the browser (it looks like you might be expecting the readFileSync call to get run during Webpack's compilation - this isn't how it works!). You either need a Webpack loader of some kind to make it so you can require() the file, or you'll have to load it via AJAX.

Cannot get HotModuleReplace plugin with react-hot to be enabled in the browser, though file watching is working

I'm trying to use webpack's Hot Module Replacement plugin. I've managed to randomly get it working, but it still isn't doing quite what I would hope it to.
Basically, I get no messages in my console that it's even active, though it's building without issue and file watching is working, as I get the messages webpack: bundle is now VALID and webpack: bundle is now INVALID when I update.
webpack, webpack-dev-server, and react-hot are all installed locally.
But in the browser's console, the only thing I see is:
Download the React DevTools for a better development experience: https://fb.me/react-devtools
I'm using Laravel to update my index file based on an environment variable and it is working just fine.
Here is the index.php file:
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<div id="content"></div>
#if(env("APP_HOTRELOAD"))
<script src="http://localhost:8080/js/vendor.js"></script>
<script src="http://localhost:8080/js/app.js"></script>
#else
<script src="js/vendor.js"></script>
<script src="js/app.js"></script>
#endif
</body>
</html>
Here is the webpack config file (webpack.hot-reload.config.js):
var path = require("path");
var webpack = require("webpack");
var node_modules = path.resolve(__dirname, "node_modules");
var public_dir = path.resolve(__dirname, "public");
module.exports = {
debug: (process.env.NODE_ENV === "production"),
entry: {
vendor: [
"es5-shim",
"es5-shim/es5-sham",
"babel-core/polyfill",
"babel-core/external-helpers",
"react",
"react-router-component"
],
app: [
"webpack-dev-server/client?http://localhost:8080",
"webpack/hot/only-dev-server",
path.resolve(__dirname, "resources/assets/js/index.js")
]
},
contentBase: public_dir,
output: {
path: path.resolve(public_dir, "js"),
filename: "app.js",
publicPath: "/"
},
plugins: [
new webpack.optimize.CommonsChunkPlugin("vendor", "vendor.js"),
//This is necessary for React to know whether it's supposed to strip out
//addons and extra stuff when being minified. Essentially, it becomes dead
//code and webpack will take it out.
new webpack.DefinePlugin({
"process.env": {"NODE_ENV": JSON.stringify(process.env.NODE_ENV)}
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin()
],
module: {
loaders: [
{
test: /\.(sa|c)ss$/,
loader: "css!style!sass"
},
{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
loaders: [
"react-hot",
"strip-loader?strip[]=debug,strip[]=console.log,strip[]=console.error",
"babel-loader"
]
}
]
},
resolve: {
root: path.resolve(__dirname, "resources/assets/js"),
extensions: ["", ".js", ".json"]
}
};
In order to start the webpack-dev-server, I use a separate server.js file, executed by using node server.js:
var webpack = require('webpack');
var WebpackDevServer = require('webpack-dev-server');
var config = require('./webpack.hot-reload.config');
new WebpackDevServer(webpack(config), {
publicPath: config.output.publicPath,
contentBase: config.contentBase,
hot: true,
historyApiFallback: true,
quiet: false,
noInfo: false,
stats: {
colors: true
}
}).listen(8080, 'localhost', function (err, result) {
if (err) {
console.log(err);
}
console.log('Listening at localhost:8080');
});
It seems to work randomly after waiting some time, but if I change a file or refresh the page manually, it seems to just break. I've tried using both Firefox and Chrome and it doesn't make a difference, so I'm thinking it's in the build.
What could be wrong?
I figured it out. There was a comment about it on the page that notes how to use webpack-dev-server, but I managed to read over it.
If you look in my config you'll see:
...
output: {
path: path.resolve(public_dir, "js"),
filename: "app.js",
**publicPath: "/"**
},
...
I misinterpreted the publicPath key and its path.
However, the example given in the docs shows:
module.exports = {
entry: {
app: ["./app/main.js"]
},
output: {
path: "./build",
publicPath: "/assets/",
filename: "bundle.js"
}
};
And states:
This modified bundle is served from memory at the relative path specified in publicPath (see API). It will not be written to your configured output directory. Where a bundle already exists at the same url path the bundle in memory will take precedence.
However, for this example, this bundle will be served from /, not /assets/ because further down, the content base is given as build/. There's nothing that notes that the directory where the scripts lie is possibly aliased to /assets/ at all, so that's why I placed the / path as the publicPath instead of the subdirectory my JS was actually being served from..
The docs note that:
To teach webpack to make requests (for chunk loading or HMR) to the webpack-dev-server you need to provide a full URL in the output.publicPath option.
So I changed:
publicPath: "/"
to:
publicPath: "http://localhost:8080/js/"
And now my files are being served up correctly. I added the /js/ because that's where I my JavaScript is served from on the actual server.

Resources