Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Webpack + Express + EJS: Error: Cannot find module "."

I'm writing an express web app with webpack, typescript and ejs. When hitting one of the routes that's supposed to serve a .ejs file I get the following error:

Error: Cannot find module "."
   at webpackMissingModule (/Users/max/Development/test/express-webpack/dist/server.js:20669:74)
   at new View (/Users/max/Development/test/express-webpack/dist/server.js:20669:152)
   at EventEmitter.render (/Users/max/Development/test/express-webpack/dist/server.js:18776:12)
   at ServerResponse.render (/Users/max/Development/test/express-webpack/dist/server.js:20479:7)
   at /Users/max/Development/test/express-webpack/dist/server.js:25508:7
   at Layer.handle [as handle_request] (/Users/max/Development/test/express-webpack/dist/server.js:4524:5)
   at next (/Users/max/Development/test/express-webpack/dist/server.js:4743:13)
   at Route.dispatch (/Users/max/Development/test/express-webpack/dist/server.js:4724:3)
   at Layer.handle [as handle_request] (/Users/max/Development/test/express-webpack/dist/server.js:4524:5)
   at /Users/max/Development/test/express-webpack/dist/server.js:4054:22

Here's the code:

package.json:

{
  "name": "express-webpack",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "awesome-typescript-loader": "^3.0.0-beta.18",
    "copy-webpack-plugin": "^4.0.1",
    "debug": "^2.6.0",
    "ejs": "^2.5.5",
    "express": "^4.14.0",
    "json-loader": "^0.5.4",
    "source-map-loader": "^0.1.6",
    "webpack": "^2.2.0-rc.3"
  }
}

webpack.config.js

var CopyWebpackPlugin = require('copy-webpack-plugin');

module.exports = {
    entry: __dirname + "/src/index.js",
    target: "node",
    output: {
        filename: "server.js",
        path: __dirname + "/dist"
    },

    // Enable sourcemaps for debugging webpack's output.
    devtool: "source-map",

    resolve: {
        // Add '.ts' and '.tsx' as resolvable extensions.
        extensions: [".webpack.js", ".web.js", ".ts", ".tsx", ".js"]
    },

    module: {
        rules: [
            // All files with a '.ts' or '.tsx' extension will be handled by 'awesome-typescript-loader'.
            { test: /\.tsx?$/, loader: "awesome-typescript-loader" },
            { test: /\.json$/, loader: 'json-loader' },

            // All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
            { enforce: "pre", test: /\.js$/, loader: "source-map-loader" }
        ]
    },

    plugins: [
        new CopyWebpackPlugin([
            { from: 'src/views', to: 'views' }
        ])
    ],

    node: {
      fs: "empty"
    }
};

src/index.js

var express = require('express');

var app = express();
app.set("view engine", "ejs");
app.set("views", "./views");
app.get('/', function(req, res, next){
  res.render('index'); // <-- error originates here AFAIK
});
app.listen(8000);

views/index.ejs

hello world

Reproduction steps:

  • Run webpack to build the project
  • Start the server with node dist/server.js
  • Visit http://localhost:8000
  • Expect to see the contents of index.ejs, instead get above stack trace

Any idea what's going wrong?

like image 211
Max Mumford Avatar asked Jan 17 '17 21:01

Max Mumford


People also ask

How do I download EJS?

Go to the latest release, download ./ejs.js or ./ejs.min.js . Alternately, you can compile it yourself by cloning the repository and running jake build (or $(npm bin)/jake build if jake is not installed globally).

How do I know if EJS is installed?

See npm ls | grep ejs at root level of your project to check if you have already added ejs dependency to your project. If not, add it as dependencies to your project. (I prefer adding dependency to package. json instead of npm install ing the module.)

Can not find module HTML?

To solve the error "Cannot find module 'html-webpack-plugin'", make sure to install the html-webpack-plugin package by opening your terminal in your project's root directory and running the following command: npm i -D html-webpack-plugin and restart your IDE and dev server.


3 Answers

I digged into webpack generated code and I found it seems there is a bug with context parse evaluation of webpack. There is a simple expression that express uses to require selected view engine, but it is failing to resolve that.

To overcome this issue, in your index you can write

const ejs = require("ejs").__express;
const app = express();
app.set("view engine", "ejs");   
app.engine('ejs', ejs);// <-- this does the trick

But if you want a definitive solution, open an issue on webpack repo.

like image 56
Cleiton Avatar answered Oct 16 '22 07:10

Cleiton


Adding on Cleiton's answer, you now have to have a callback function in while calling app.engine(string, callback)

app.set('views', 'src/views');
app.set('view engine', 'ejs');
app.engine('ejs', require('ejs').__express); //<-- this
like image 27
Riadhoq Avatar answered Oct 16 '22 06:10

Riadhoq


I belive your issue it has to do with the fact that your bundled file (dist/server.js) does not have access to your dependencies, express, template engine etc. In order to sort that out you can configure webpack to avoid bundling the dependencies (due to the fact your app is running from server/node side):

var CopyWebpackPlugin = require('copy-webpack-plugin');

module.exports = {
    entry: __dirname + "/src/index.js",
    target: "node",
    output: {
        filename: "server.js",
        path: __dirname + "/dist"a
    },

    // Enable sourcemaps for debugging webpack's output.
    devtool: "source-map",

    resolve: {
        // Add '.ts' and '.tsx' as resolvable extensions.
        extensions: [".webpack.js", ".web.js", ".ts", ".tsx", ".js"]
    },

    module: {
        rules: [
            // All files with a '.ts' or '.tsx' extension will be handled by 'awesome-typescript-loader'.
            { test: /\.tsx?$/, loader: "awesome-typescript-loader" },
            { test: /\.json$/, loader: 'json-loader' },

            // All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
            { enforce: "pre", test: /\.js$/, loader: "source-map-loader" }
        ]
    },

    plugins: [
        new CopyWebpackPlugin([
            { from: 'src/views', to: 'views' }
        ])
    ],

    node: {
      fs: "empty"
    }
};

// Node module dependencies should not be bundled
config.externals = fs.readdirSync("node_modules")
.reduce(function(acc, mod) {
   if (mod === ".bin") {
   return acc
 }

   acc[mod] = "commonjs " + mod
   return acc
}

You might also need to override node global config, using the node option config to make the views folder accessible from your bundled file, more details: https://webpack.js.org/configuration/node/#components/sidebar/sidebar.jsx

That config could also help you https://gist.github.com/madx/53853c3d7b527744917f

like image 22
mgonyan Avatar answered Oct 16 '22 07:10

mgonyan