Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to successfully build NodeJS project with pug templating engine using webpack 4

This is going to be a little long and I was thinking I should maybe split it into two questions but I feel it all ties together and gives better context, so here goes...My build attempts have been failing for almost two days now and I can't seem to find any solutions. My project is basically a REST API with the index page accessible via localhost:5000. The project structure is as follows:

controllers
init
  - db.js
models
public
  - css
routes
views
  - layout.pug
  - index.pug
.babelrc
.env
app.js
start.js
package.json
webpack.server.config.js
webpack.prod.config.js

The entry point of the project is start.js which imports a function from the db.js file and a constant app from the app.js file. The app.js file imports and sets up express const app = express() adds all relevant middleware and exports the constant app. The db.js file in the init folder exports a function, (export default function { ... }), which initializes the database. app.js and db.js come together inside start.js which looks like so...

import InitializeDB from "./init/db";
import app from "./app";

InitializeDB();
app.set("port", process.env.PORT || 3000);
const server = app.listen(app.get("port"), () => {
  console.log(`Express running → PORT ${server.address().port}`);
});

And because I wanted to use ES6 imports, I installed babel npm install --save-dev @babel/core @babel/cli @babel/preset-env @babel/node and added this to .babelrc

{   
  "presets": [
    "@babel/preset-env"   
  ] 
}

My package.json scripts:

  "scripts": {
    "build": "rm -rf dist && webpack --mode production --config webpack.server.config.js && webpack --mode production --config webpack.prod.config.js",
    "start": "node ./dist/server.bundle.js",
    "watch": "nodemon --exec babel-node start.js --ignore public/",
    "start:dev": "concurrently \"npm run test\" \"npm run watch\"",
    "test": "echo \"Testing...\""
  },

Locally, npm run start:dev gets everything running fine. However, that's where my success ends.

My first problem is that when I try to run node ./start.js I get hit with the error (node:149228) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension. So then I add "type": "module" in my package.json and run npm run start:dev again but then I get the Error...

internal/modules/run_main.js:54
    internalBinding('errors').triggerUncaughtException(
                              ^

Error [ERR_MODULE_NOT_FOUND]: Cannot find module 'G:\apps\myproject\init\db' imported from G:\apps\myproject\start.js
Did you mean to import ../init/db.js?

That's my first problem. Now for the webpack part...

My webpack.server.config.js...

const path = require("path");
const webpack = require("webpack");
const nodeExternals = require("webpack-node-externals");

module.exports = {
  entry: {
    server: "./start.js",
  },
  output: {
    path: path.join(__dirname, "dist"),
    publicPath: "/",
    filename: "[name].bundle.js",
  },
  target: "node",
  node: {
    __dirname: false,
    __filename: false,
  },
  externals: [nodeExternals()],
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
        },
      },
    ],
  },
};

My webpack.prod.config.js file...

const path = require("path");
const webpack = require("webpack");
const HtmlWebPackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");

module.exports = {
  entry: {
    app: "./start.js",
  },
  output: {
    path: path.join(__dirname, "dist"),
    publicPath: "/",
    filename: "[name].bundle.js",
  },
  target: "node",
  devtool: "source-map",
  optimization: {
    minimizer: [
      new UglifyJsPlugin({
        cache: true,
        parallel: true,
        sourceMap: true,
      }),
      new OptimizeCSSAssetsPlugin({}),
    ],
  },
  module: {
    rules: [
      {
        test: /\.pug$/,
        use: ["pug-loader"],
      },
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, "css-loader"],
      },
      {
        test: /\.(png|svg|jpg|gif)$/,
        use: ["file-loader"],
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader",
      },
    ],
  },
  plugins: [
    new HtmlWebPackPlugin({
      template: "./views/index.pug",
    }),
    new MiniCssExtractPlugin({
      filename: "[name].css",
      chunkFilename: "[id].css",
    }),
  ],
};

For the webpack.prod.config.js file, some suggest changing target to web instead of node but that hasn't worked for me.

When I run npm run build with "type": "module" set in package.json, webpack throws the error

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: G:\apps\myproject\webpack.server.config.js
require() of ES modules is not supported.
require() of G:\apps\myproject\webpack.server.config.js from G:\apps\myproject\node_modules\webpack-cli\bin\utils\convert-argv.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename webpack.server.config.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from G:\apps\myproject\package.json.

So then I remove "type": "module" from package.json and run npm run build again and this time I get a bunch of... Critical dependency: the request of a dependency is an expressionand Module not found: Error: Can't resolve 'net' in 'G:\apps\myproject\node_modules\... for multiple packages.

These issues have been gnawing at me quite a bit. Your help would be greatly appreciated.

like image 914
Nelson King Avatar asked Dec 31 '25 03:12

Nelson King


1 Answers

That's my first problem. Now for the webpack part...

Your first problem needs to update your codebase as ESM requires:

  • all your *.js files should be renamed to *.mjs. Adding {type: 'module'} in your package.json avoid this tedious operation since it will enable/override this check
  • if you are using node>=8 <13 you must run your server with node --experimental-modules start.js
  • ESM doesn't support require, exports, module.exports, __filename, __dirname so you need to change your __dirname to:
import { dirname } from 'path';
import { fileURLToPath } from 'url';

const __dirname = dirname(fileURLToPath(import.meta.url));

...
app.use(express.static(path.join(__dirname, 'public')))
...
  • all the ESM relative import must have the extension, so you need to change all your imports to:
import Routes from './routes/index.js' // added .js
// etc.. for all the files

At the end, node --experimental-modules start.js will start:

screen


For the webpack part, instead, you need to:

npm i @open-wc/webpack-import-meta-loader -D

And add it to your webpack.prod.config.js file (I just remove UglifyJsPlugin since it wasn't working):

{
  test: /\.js$/,
  loader: require.resolve('@open-wc/webpack-import-meta-loader')
}
like image 143
Manuel Spigolon Avatar answered Jan 02 '26 03:01

Manuel Spigolon



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!