Webpack with babel and handlebars files - javascript

REVISED
So I am fairly new to this and I am struggling for some time to resolve this, so I would really appreciate help.
Goal: Create dynamic web-page that is using handlebars and D3 for dynamic text and visuals.
What I achieved until now: Use json file stored within to do some data manipulation and render data with hbs and express. Created simple bar chart that uses data from the previous file.
Issue: I am not sure how to completely set up webpack so I can actually see how my page looks like. If I just add script with D3 visuals to hbs I am getting require is not defined, which I get since it's not supported on client side.
folder structure
|main
|data
|data.json
|src
|index.js
|visuals.js
|templates
|views
|index.hbs
|node_modules
|package.json
|package-lock.json
|webpack.config.js
|babel.config.json
My code until now (there might be to many things here because I tried a lot of things plus I was anonymizing as I have sensitive items)
index.js:
const express = require("express");
const fs = require("fs");
const path = require("path");
const hbs = require("hbs");
const Data = JSON.parse(
fs.readFileSync(path.resolve(__dirname, "../data/data.json")).toString()
);
//Some data manipulation
module.exports = clusters; //array to be used in the other file
const app = express();
const publicDirectoryPath = path.join(__dirname, "..");
const viewPath = path.join(__dirname, "../templates/views");
app.set("view engine", "hbs");
app.set("views", viewPath);
app.use(express.static(publicDirectoryPath));
app.get("", (req, res) => {
res.render("index", {
data1: data1,
data2:data2,
});
});
Beginning of visuals.js
const d3 = require("d3");
var dt = require("./index.js");
const clusters = dt.clusters;
webpack.config.js
const path = require("path");
const HandlebarsPlugin = require("handlebars-webpack-plugin");
module.exports = {
entry: [
path.resolve(__dirname, "./src/visuals.js"),
path.resolve(__dirname, "./src/index.js"),
],
output: {
path: path.resolve(__dirname, "./public"),
filename: "bundle.js",
},
module: {
rules: [
{ test: /\.handlebars$/, loader: "handlebars-loader" },
{
test: /\.js$/,
exclude: /node_modules/,
use: ["babel-loader"],
},
],
},
resolve: {//I had errors and warnings with modules, below solved it
modules: [path.resolve(__dirname), "node_modules/"],
extensions: [".js", ".json"],
descriptionFiles: ["package.json"],
},
fallback: {
stream: false,
http: false,
buffer: false,
crypto: false,
zlib: false,
fs: false,
net: "empty",
},
},
plugins: [
new HandlebarsPlugin({//can't say I understood from documentation the setup for hbs
entry: path.resolve(__dirname, "./templates/views/index.hbs"),
output: path.resolve(__dirname, "./public/index.html"),
data: path.resolve(__dirname, "./data/data.json"),
onBeforeSetup: function (Handlebars) {},
onBeforeCompile: function (Handlebars, templateContent) {},
onBeforeRender: function (Handlebars, data, filename) {},
onBeforeSave: function (Handlebars, resultHtml, filename) {},
onDone: function (Handlebars, filename) {},
}),
],
};
package.json
{
"name": "triana_plus",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "webpack --mode development",
"start": "webpack serve --config ./webpack.config.js --open chrome"
},
"author": "",
"license": "ISC",
"devDependencies": {
"#babel/core": "^7.12.10",
"#babel/preset-env": "^7.12.10",
"babel-loader": "^8.2.2",
"handlebars-webpack-plugin": "^2.2.1",
"webpack": "^5.10.1",
"webpack-cli": "^4.2.0",
"webpack-dev-server": "^3.11.0",
"webpack-node-externals": "^2.5.2"
},
"dependencies": {
"d3": "^6.3.1",
"express": "^4.17.1",
"fs": "0.0.1-security",
"handlebars-loader": "^1.7.1",
"hbs": "^4.1.1",
"net": "^1.0.2",
"path": "^0.12.7",
"request": "^2.88.2"
}
}
babel.config.json
{
"presets": ["#babel/preset-env"]
}

What the error you posted means is that webpack is being asked to create some JS bundles that reference node packages like http. Referencing packages like that in client-side code bundles won't work since it cannot package node modules in the JS artifacts.
Make sure that no node-specific packages are required/imported for files you're bundling through webpack. Since it looks like you're creating an express app, keep the express-specific files outside of the folder visited by webpack.

Related

How do I fix "require is not defined" error with Webpack and Babel?

I keep getting error Uncaught ReferenceError: require is not defined in browser even with Webpack and Babel. For some reason I never had to worry about this before. I'm not sure if this error is caused by updated packages or what. I set up a very simple test application for simplicity.
package.json
{
"name": "require-test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"engines": {
"node": "16.16.0"
},
"scripts": {
"build": "webpack --config webpack.config.js"
},
"author": "",
"license": "ISC",
"devDependencies": {
"#babel/core": "^7.18.10",
"#babel/preset-env": "^7.18.10",
"babel-loader": "^8.2.5",
"webpack": "^5.74.0",
"webpack-cli": "^4.10.0"
}
}
webpack.config.js
const path = require('path');
module.exports = {
target: "node",
mode: "production",
output: {
path: path.resolve(__dirname, 'dist'),
clean: true,
filename: (pathData) => {
return 'index.js'
},
},
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
}
}
]
}
}
src/index.js (js file before build)
const path = require('path');
console.log(path);
dist/index.js (js file after build)
(()=>{var r,e={17:r=>{"use strict";r.exports=require("path")}},t={};r=function r(o){var s=t[o];if(void 0!==s)return s.exports;var p=t[o]={exports:{}};return e[o](p,p.exports,r),p.exports}(17),console.log(r)})();
There're a few things on your code needing changed. First of all, you can't target to build for node while you run that code on browser which is wrong. Secondly, path is designed and built to run on node server specifically, however, there's a fallback for path on browser though which is called path-browserify. You can check its document to see how it works.
Anyway to sum up, you need to switch target as web in the end:
module.exports = {
target: "web",
// ...
}

Webpack 5 React component library UMD bundle with SourceMaps

I am attempting to make a webpack 5 build process to create a react component library I just had a couple of things cannot seem to get working.
#1) The webpack build command works fine, and when using the inline-source-map option I can
SEE the data URL embeded in the outputted build file but when I ever I attempt to publish and test this library on NPM I always get obfuscated errors without original lines of code so I can't even tell where the errors are; what else am I missing to activate source-maps? I am using Chrome dev tools and it doesn't even tell me a source map is available for that code...
#2) Another issue I am having is after building this with webpack into the dist folder; I start another CRA test app and try to pull components out of the built library but all I get are these errors.
./src/dist/index.js
Line 1:1: Expected an assignment or function call and instead saw an expression no-unused-expressions
Line 1:112: 'define' is not defined no-undef
Line 1:123: 'define' is not defined no-undef
Line 1:190: Unexpected use of 'self' no-restricted-globals
Line 1:466: Expected an assignment or function call and instead saw an expression no-unused-expressions
Line 1:631: Expected an assignment or function call and instead saw an expression no-unused-expressions
I am aware webpack 5 stopped bundling polyfills for Node but shouldn't this code run
if I place it in the src directory of a CRA application? This is bundled code shouldn't it work in the browser/ in another React application? I targeted UMD so I thought it would work in this environment
here is all the necessary info
Webpack.config.js
const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const nodeExternals = require("webpack-node-externals");
module.exports = {
entry: "./src/index.js",
devtool: "inline-source-map",
externals: [nodeExternals()],
output: {
filename: "index.js",
path: path.resolve(__dirname, "dist"),
library: {
name: "test",
type: "umd",
},
},
plugins: [new CleanWebpackPlugin()],
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["#babel/preset-env", "#babel/preset-react"],
},
},
},
{
test: /\.scss$/,
use: ["style-loader", "css-loader", "sass-loader"],
include: path.resolve(__dirname, "./src"),
},
],
},
};
Package.json
{
"name": "test-lib",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "start-storybook",
"build": "webpack --mode production"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"prop-types": "^15.7.2",
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"devDependencies": {
"#babel/core": "^7.15.8",
"#babel/preset-env": "^7.15.8",
"#babel/preset-react": "^7.14.5",
"#storybook/addon-knobs": "^6.3.1",
"#storybook/react": "^6.3.10",
"babel-loader": "^8.2.2",
"clean-webpack-plugin": "^4.0.0",
"node-sass": "^6.0.1",
"path": "^0.12.7",
"sass-loader": "^12.1.0",
"webpack": "^5.58.0",
"webpack-cli": "^4.9.0",
"webpack-node-externals": "^3.0.0"
}
}
Button.js (sample component)
import React from 'react'
import PropTypes from 'prop-types';
const Button = ({message = 'Hello world'}) => (
<button>{message}</button>
)
Button.propTypes = {
message: PropTypes.string.isRequired
}
export default Button
Build entry point (index.js)
export { default as Button } from "./components/Button";

Why can't I call fetch on jest with vanilla javascript?

I'm kind of new to testing library, and recently started studying Jest. I'm currently trying to implement vanilla javascript project with jest.
I know 'mocking' an async API call in jest is best practice, but I wanted to actually call API using fetch to see if jest works correctly. But I got stuck on 'fetch' request. Need to remind you that my project is not node project, just plain vanilla javascript project.
Before moving on to actual test code, since I'm using babel and webpack on my project. Here's .bablerc.js and webpack.config.js implementation.
.babelrc.js
module.exports = {
presets: [
[
'#babel/env',
{
targets: {
browsers: ['> 0.25%, not dead'],
},
useBuiltIns: 'usage',
corejs: 3,
shippedProposals: true,
},
],
],
};
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
entry: './src/js/index.js',
output: {
path: path.resolve(__dirname, 'public'),
filename: 'static/js/main.js',
},
module: {
rules: [
{
loader: 'babel-loader',
test: /\.js$/,
exclude: /node_modules/,
},
{
test: /\.(sc|c)ss$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
},
],
},
devServer: {
contentBase: path.resolve(__dirname, 'public'),
inline: true,
hot: true,
watchOptions: {
poll: true,
ignored: '/node_modules/',
},
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'Typing Game',
minify: {
collapseWhitespace: true,
},
hash: true,
template: './src/index.html',
}),
new MiniCssExtractPlugin({ filename: 'static/css/styles.css' }),
],
};
With project configuration above, webpack and webpack-dev-server worked perfectlly and could retrieve completely built html, css and js files. After checking everything except Jest works perfectly, I implemented simple API call function that gets list of words in array,
export const getWordsAPI = () =>
fetch(
'https://server-api-address'
).then((res) => res.json());
And tried to call the api function on my jest test code.
import { getWordsAPI } from '../src/js/api';
describe('Integration with getWords API', () => {
test('Get Word List', async () => {
const result = await getWordsAPI();
expect(result).toEqual([
{
text: 'hello',
},
{
text: 'world',
}
]);
});
});
But, I continuously got this 'fetch is not defined' error.
ReferenceError: fetch is not defined
> 1 | export const getWordsAPI = () =>
| ^
2 | fetch(
3 | 'https://server-api-address'
4 | ).then((res) => res.json());
at getWordsAPI (src/js/api.js:1:28)
at _callee$ (__tests__/api.test.js:5:26)
at tryCatch (node_modules/regenerator-runtime/runtime.js:63:40)
at Generator.invoke [as _invoke] (node_modules/regenerator-runtime/runtime.js:293:22)
at Generator.next (node_modules/regenerator-runtime/runtime.js:118:21)
at asyncGeneratorStep (__tests__/api.test.js:11:103)
at _next (__tests__/api.test.js:13:194)
at __tests__/api.test.js:13:364
at Object.<anonymous> (__tests__/api.test.js:13:97)
I tried everything to solve this problem, including jest-fetch-mock but couldn't solve the problem. when I applied jest-fetch-mock to test code, I got new error which is this.
invalid json response body at reason unexpected end of json input
I eventually ended up using isomorphic-fetch library to make fetch work on my test code, which makes me uncomfortable.
import 'isomorphic-fetch';
import { getWordsAPI } from '../src/js/api';
describe('Integration with getWords API', () => {
...
}
I understand fetch is not defined error happening on nodejs environment, since there is not fetch provided by node, but this is vanilla javascript project. fetch is provided by default on browser, and should work without any additional work. But why is this error happening? I don't know what is causing this problem, either babel or webpack or even jest itself.
Please teach me what I'm missing. Probably I'm missing the main concept of Jest architecture, related to this problem. Waiting for anyone's response
ps. In case you need to know what version of libraries I'm using in my project, here's my package.json file. Other than libraries, I'm using WSL2 on Windows OS, and using npm instead of yarn.
{
"name": "typing_game",
"version": "1.0.0",
"description": "Typing Game by Vanilla Javascript",
"main": "index.js",
"author": "piecemakerz <piecemakerz#naver.com>",
"license": "MIT",
"scripts": {
"start": "webpack-dev-server --open",
"build": "webpack",
"test": "jest"
},
"devDependencies": {
"#babel/core": "^7.12.3",
"#babel/preset-env": "^7.12.1",
"#types/jest": "^26.0.15",
"babel-jest": "^26.6.3",
"babel-loader": "^8.2.1",
"clean-webpack-plugin": "^3.0.0",
"css-loader": "^5.0.1",
"html-webpack-plugin": "^4.5.0",
"jest": "^26.6.3",
"mini-css-extract-plugin": "^1.3.1",
"prettier": "^2.1.2",
"sass-loader": "^10.1.0",
"webpack": "^4.44.2",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.0"
},
"dependencies": {
"core-js": "^3.7.0",
"isomorphic-fetch": "^3.0.0",
"sass": "^1.29.0"
},
"jest": {
"transform": {
"^.+\\.js?$": "babel-jest"
}
}
}

webpack bundles files but index.js isn't running

I have an index.js file that imports my CSS and a few packages, but after bundling everything and starting the server I noticed that index.js wasn't running. I did a simple console.log in index.js and it isn't reached.
I copied the contents of my webpack.config file from a previous project which was working correctly, so I'm not sure if it's a file structure/path error or what not. Any thoughts?
Directory structure:
webpack.config.js:
const path = require('path')
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin')
var $ = require("jquery");
var HtmlWebpackPluginConfig = new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'RecruitmentTracking.txt',
inject: 'body'
});
module.exports = {
entry: "./src/index.js", // removing the . fails the build
output: {
filename: './SiteAssets/scripts/RecruitmentTracking.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [{
loader: 'babel-loader',
test: /\.js$/,
exclude: /node_modules/
}, {
test: /\.css$/,
use: ['style-loader', 'css-loader']
}, {
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
}
],
},
devServer: {
disableHostCheck: true
},
devtool: 'cheap-module-eval-source-map', // this helps the browser point to the exact file in the console, helps in debug
devServer: {
contentBase: path.join(__dirname, 'src'),
historyApiFallback: true // this prevents the default browser full page refresh on form submission and link change
},
plugins: [
HtmlWebpackPluginConfig,
new webpack.ProvidePlugin({
"$": "jquery",
"jQuery": "jquery",
"window.jQuery": "jquery"
})]
}
index.js:
import "./RecruitmentTracking.css";
import 'jquery';
import 'bootstrap/dist/js/bootstrap.bundle.min.js';
import 'jquery-ui-bundle/jquery-ui.min.js';
console.log('this is index.js');
package.json:
{
"name": "recruitmenttracking",
"version": "1.0.0",
"description": "Recruitment Initiatives Tracking",
"main": "index.js", // ----- should a more specific file path be here?
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack-dev-server --open --mode development",
"build": "webpack --config webpack.config.js",
"dev-server": "webpack-dev-server"
},
"author": "",
"license": "ISC",
"devDependencies": {
"#babel/cli": "^7.2.3",
"#babel/core": "^7.4.0",
"#babel/preset-env": "^7.4.2",
"babel-loader": "^8.0.5",
"css-loader": "^2.1.1",
"file-loader": "^3.0.1",
"html-webpack-plugin": "^3.2.0",
"style-loader": "^0.23.1",
"webpack": "^4.29.6",
"webpack-cli": "^3.3.0",
"webpack-dev-server": "^3.2.1"
},
"dependencies": {
"#babel/polyfill": "^7.4.0",
"axios": "^0.18.0",
"bootstrap": "^4.3.1",
"jquery": "^3.3.1",
"jquery-ui-bundle": "^1.12.1-migrate",
"pdfmake": "^0.1.54",
"popper": "^1.0.1"
}
}
What's happening here is:
1 - webpack compiles and outputs into: './SiteAssets/scripts/RecruitmentTracking.js'
2 - HtmlWebpackPlugin, will then read the template file './src/index.html', and inject RecruitmentTracking.js script inside the body.
3 - then, it outputs the result to dist/RecruitmentTracking.txt
I don't see any problem, apart from the file being a .txt and not .html. and would obviously not be interpreted by the browser.
Try outputting to an html file instead, it should work
1) For some reason you've configured the following plugin to output a .txt file. So don't expect the browser to intepret that as a html file
var HtmlWebpackPluginConfig = new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'RecruitmentTracking.txt',
inject: 'body'
});
2) Also I believe that file that you're opening in the browser is /dist/index.html and that file doesn't load your js file. Try adding the following line into /dist/index.html:
<script src"./SiteAssets/scripts/RecruitmentTracking.js"></script>
3) If the above works, please still consider taking a closer look at (1)
You have named the output file in HtmlWebpackPluginConfig as RecruitmentTracking.txt. change it to index.html and it should work
var HtmlWebpackPluginConfig = new HtmlWebpackPlugin({
template: './src/index.html', // webpack takes ./src/index.html as input file
filename: 'index.html', // webpack processes the above input template and should output to index.html
inject: 'body'
});

index.html using webpack-dev-server not reload

When I change my app.js and main.css while webpack-dev-server is running, the bundle is updated.
But when i change the index.html the content is not refreshed; if I add a line to the HTML, the webpack-dev-server does not refresh anything on the page.
Here are my webpack.config.js and package.json files.
I hope you can help me.
webpack.config.js:
var webpack = require('webpack');
var WebpackDevServer = require('webpack-dev-server');
var CleanWebpackPlugin = require('clean-webpack-plugin');
var chalk = require('chalk');
var env = process.env.WEBPACK_ENV;
var host = 'localhost';
var port = '8080';
var config = {
devtool: 'source-map',
entry: [
'webpack/hot/dev-server',
'webpack-dev-server/client?http://' + host + ':' + port +'/',
'./src/app.js'
],
output: {
path: __dirname + '/dist',
filename: 'bundle.js'
},
module : {
loaders: [
{ test: /\.css$/, loader: 'style-loader!css-loader' },
{ test: /\.html$/,loader: 'file?name=[name].[ext]' }
]
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin(),
new CleanWebpackPlugin(['dist'], {
root: __dirname,
verbose: true,
dry: false
})
]
};
if (env === 'dev') {
new WebpackDevServer(webpack(config), {
contentBase: './dist/',
stats: {colors: true},
hot: true,
debug: true
}).listen(port, host, function (err, result) {
if (err) {
console.log(err);
}
});
console.log('-------------------------');
console.log(chalk.bold.white('Local web server runs at ') + chalk.green('http://' + host + ':' + port));
console.log('-------------------------\n\n');
}
module.exports = config;
package.json:
{
"name": "webpack-skeleton",
"version": "1.0.0",
"description": "webpack skeleton",
"main": "bundle.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "WEBPACK_ENV=dev ./node_modules/.bin/webpack --watch --inline"
},
"author": "Jose Roa",
"license": "ISC",
"devDependencies": {
"chalk": "^1.1.3",
"clean-webpack-plugin": "^0.1.9",
"css-loader": "^0.23.1",
"file-loader": "^0.8.5",
"style-loader": "^0.13.1",
"webpack": "^1.13.0",
"webpack-dev-server": "^1.14.1"
}
}
My directory structure:
css
main.css
dist
bundle.js
bundle.js.map
index.html
node_modules
src
app.js
sum.js
package.json
index.html
node_modules
webpack.config.js
Every file inside the dist directory is generated by webpack.
add the HtmlWebpackPlugin
link: https://www.npmjs.com/package/html-webpack-plugin
add
// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: './index.html'
})
]
};
It can be due to your IDE -- see webpack dev server
Working with editors/IDEs supporting “safe write”... This behaviour causes file watcher to lose the track because the original file is removed. In order to prevent this issue, you have to disable “safe write” feature in your editor.
You can try this workaround for auto reloading while developing an app. Add to your entry point ('./src/app.js'):
require('../index.html'); //workround to reload page on index.html changes

Categories

Resources