Dynamic require inside a webpacked express server can not find module - node.js

I have a really small express application. Basically a poor man's server less function provider. However this is nothing I use in production and it works in an unpackaged mode. But after I webpack this application the dynamic require always fails with an Error: Cannot find module exception. But the module is there and the path is correct.
devserver.js:
const express = require('express')
const fs = require('fs')
const resourceMapper = fs.existsSync(__dirname + '/../webpack.config.js') ? require('../webpack.config') : null
const app = express()
// enable express json
app.use(express.json())
// add dynamic lambda functions
app.post('/api/:lambda', function(request, response) {
const resource = resourceMapper
? `${__dirname}/.${resourceMapper[1].entry[request.params.lambda.replace(/^common-/, "").replace(/\.js$/, "")]}`
: `${__dirname}/${request.params.lambda}`
const lambda = require(resource) // <--------- problematic require
const result = lambda.main(request.body)
response.status(200).json(result)
});
app.listen(3000)
webpack.conf.js:
module.exports = [
{
name: "development-server",
target: 'node',
externals: [(require('webpack-node-externals'))()],
node: {
__dirname: false,
__filename: false,
},
entry: {
devserver: "./devserver/server.js",
},
output: {
filename: "./server.js",
path: __dirname + '/build/node/'
}
},
{
name: "lambdas",
target: 'node',
externals: [(require('webpack-node-externals'))()],
entry: {
returns: "./lambdas/returns/src/main/index.js",
echo: "./lambdas/echo/src/main/echo.js"
},
output: {
library: "[name]",
libraryTarget: "commonjs2",
filename: "./common-[name].js",
path: __dirname + '/build/node/'
}
}
]

I have really tried lots of things and various modules and plugins. In the end they only way I got it working is using a very nasty eval trick.
const require2 = eval('require')
//[snip]
const lambda = resourceMapper
? require(`${__dirname}/.${resourceMapper[1].entry[request.params.lambda.replace(/^common-/, "").replace(/\.js$/, "")]}`)
: require2(`./${request.params.lambda}`)

Related

Was able to resolve modules with Polymer 3, but not with Lit Element

I have always been able to use https://github.com/nodecg/express-transform-bare-module-specifiers with Polymer 3 and browser sync. But now I am getting errors with the set up and lit element
Uncaught TypeError: Failed to resolve module specifier "lit". Relative references must start with either "/", "./", or "../".
It is for the following files being requested:
/node_modules/#webcomponents/webcomponentsjs/webcomponents-loader.js
/node_modules/lit/polyfill-support.js
/app/main-app.js
/node_modules/lit/polyfill-support.js.map
What has changed? A module is a module....this should work with lit element as it did with P3
Please do not recommend wds, while it is a great tool it does not fit my use case
My browser-sync config that works with Polymer 3 modules but not Lit:
const app = express();
const bs = require('browser-sync').create();
app.use('*', transformMiddleware());
bs.init({
server: true,
notify: false,
"files": [
],
middleware: [
proxyMiddleware,
historyApiFallback(),
],
port: 4000,
ui: false,
});
You can try in browser-sync config:
app.use('*', transformMiddleware({
transform: (code, id) => {
if (id.endsWith('.js')) {
return code.replace(/from 'lit'/g, 'from \'lit/index.js\'');
//return code.replace(/from 'lit'/g, `from 'lit/index.js'`);
}
return code;
}
}));
Another option:
const app = express();
const bs = require('browser-sync').create();
app.use('*', transformMiddleware());
bs.init({
server: true,
notify: false,
"files": [
],
middleware: [
proxyMiddleware,
historyApiFallback(),
expressTransformBareModuleSpecifiers
],
port: 4000,
ui: false,
});
I realized I did not have app placed in the middleWare array....
Placing in app in the array fixed the issue and now browser sync works great with lit element and express-transform-bare-module-specifiers.
const historyApiFallback = require('connect-history-api-fallback');
const httpProxy = require('http-proxy');
const path = require('path');
const express = require('express');
const transformMiddleware =
require('express-transform-bare-module-specifiers').default;
const app = express();
const bs = require('browser-sync').create();
app.use(transformMiddleware());
bs.init({
server: true,
notify: false,
"files": [
'app/**/**/**/*',
'app/**/**/*',
'app/**/*',
'app/*',
],
middleware: [
historyApiFallback(),
app,
],
port: 4000,
ui: false,
});

Can't resolve 'fs' in nextjs with custom express server

I add fs to my nextjs project and received the following error:
Module not found: Can't resolve 'fs' in '/Users/neven/Development/next-js/node_modules/dotenv/lib'
I found that to resolve this issue, I should add config.node = { fs: 'empty' } to my next.config.js file.
The problem is that when I add that config param, dotenv plugin stops working, that is env variables are not loaded on client side.
This is my next.config.js file, which works without any issues.
const withCSS = require('#zeit/next-css')
const dotenv = require('dotenv')
const path = require('path')
const Dotenv = require('dotenv-webpack')
dotenv.config()
module.exports = withCSS({
webpack: config => {
config.plugins = config.plugins || []
config.plugins = [
...config.plugins,
// Read the .env file
new Dotenv({
path: path.join(__dirname, '.env'),
systemvars: true,
}),
]
return config
},
})
And then when I add fs: 'empty', it looks like this:
const withCSS = require('#zeit/next-css')
const dotenv = require('dotenv')
const path = require('path')
const Dotenv = require('dotenv-webpack')
dotenv.config()
module.exports = withCSS({
webpack: config => {
config.plugins = config.plugins || []
config.node = {
fs: 'empty'
}
config.plugins = [
...config.plugins,
// Read the .env file
new Dotenv({
path: path.join(__dirname, '.env'),
systemvars: true,
}),
]
return config
},
})
Do you have any suggestions on how I could work this thing out?
Let me know in case additional details are needed.
I found out what the issue was; dotenv plugin is working correctly, but I was trying to get the variables on client side, and that is not possible in this way.
The solution to use env variables on client side is to add env: { EXAMPLE: 'helloWorld' } to next.config.js file.
const withCSS = require('#zeit/next-css')
const dotenv = require('dotenv')
const path = require('path')
const Dotenv = require('dotenv-webpack')
dotenv.config()
module.exports = withCSS({
env: { EXAMPLE: 'helloWorld' },
webpack: config => {
config.plugins = config.plugins || []
config.node = {
fs: 'empty'
}
config.plugins = [
...config.plugins,
// Read the .env file
new Dotenv({
path: path.join(__dirname, '.env'),
systemvars: true,
}),
]
return config
},
})
The issue here is that your client-side can't access the environment variables.
Started NextJS 9.4, you can use .env* files to add your environment variables.
For your client-side to get access access to the environment variables, you just need to prefix them with NEXT_PUBLIC_
NEXT_PUBLIC_YOUR_KEY="keykeykey"
These can be accessible with:
process.env.NEXT_PUBLIC_YOUR_KEY

NextJS Webworker `window is not defined`

Getting a window not defined error when using NextJS and next-web-worker package.
require('dotenv').config();
const withPlugins = require('next-compose-plugins');
const withCSS = require('#zeit/next-css');
const withWorkers = require('#zeit/next-workers');
const Dotenv = require('dotenv-webpack');
const webpackConfig = (config) => {
config.plugins = config.plugins || [];
config.target = 'universal';
config.output = {
...config.output,
filename: 'bundle.js',
path: path.join(__dirname, 'dist'),
globalObject: '(this || global)',
publicPath: 'http://localhost:3000'
};
config.plugins = [
...config.plugins,
// Read the .env file
new Dotenv({
path: path.join(__dirname, '.env'),
systemvars: true
})
];
return config;
};
module.exports = withPlugins([[withCSS], [withWorkers]], webpackConfig);
The recommended work around appears to be setting the globalObject to this or global But that isn't working. Are there any other work arounds for this?
I was just experiencing this same error and it was solved by following the advice here:
yarn add #zeit/next-workers#canary

Webpack-dev-server doesn't find files to serve

I just copy-pasted webpack-dev-server + express settings from webpack docs, but it doesn't work because the server can't find files to serve. What is wrong with this setup?
server.js
const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const app = express();
const config = require('../webpack.config.js');
const compiler = webpack(config);
app.use(webpackDevMiddleware(compiler, {
publicPath: config.output.publicPath
}));
/*app.use('/',express.static('public')) it works when used insted of webpack dev middleware */
// Serve the files on port 3000.
app.listen(3000, function () {
console.log('Example app listening on port 3000!\n');
});
webpack.config.js
const path = require('path');
const webpack = require('webpack');
const scssRules = {
test:/\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader']
};
const jsRules = {
test: /\.js$/,
use: [
{loader: 'babel-loader'}
]
};
module.exports = {
entry: './js/App.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'public'),
publicPath: '/'
},
module: {
rules: [
jsRules,
scssRules
]
},
devServer: {
contentBase: './public'
},
plugins:[
new webpack.HotModuleReplacementPlugin()
],
devtool: 'inline-source-map'
}
File structure:
What i see:
I also came across this problem.
Turns out webpack-dev-middleware doesn't output any files, instead serves from memory. While the express server is expecting physical files.
Here, this plugin might help: https://github.com/gajus/write-file-webpack-plugin

Cannot find entry module in webpack using nodejs API

I am trying to run webpack from within nodejs. My directory structure looks like this:
build
|- dev.js
dist
|- bundle.js
src
|- layout
|- App.js
|- server
|- app.js
dev.js:
const webpack = require('webpack');
const path = require('path');
// returns a Compiler instance
const compiler = webpack(
{
context: path.resolve(__dirname, '../src'),
entry: [
'./server/app.js'
],
output: {
path: path.join(__dirname, '../dist'),
filename: 'bundle.js'
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader?presets[]=es2015&presets[]=react'
}
]
}
}
);
compiler.run(function(err, stats) {
if(err) {
console.log('Err');
}
});
app.js:
import express from 'express';
import React from 'react';
import { renderToString } from 'react-dom/server';
import App from '../layout/App';
const app = express();
app.get('*', (req, res) => {
res.send(renderToString(<App />));
});
app.listen(3000, () => {
});
If I now run node build/dev.js it generates my bundle.js, but it does not find my entry module.
(function webpackMissingModule() { throw new Error("Cannot find module \"./server/app.js\""); }());
Even though I think this is how the entry point should be defined, I have tried many combinations, even the absolute path. But always the same result, it cannot find the entry module. What am I missing here?
It looks like something is going wrong with bundling the assets - since webpack looks in your node_modules to find its assets it, would be best if you could post your package.json alongside the other files.
You're using webpack programatically instead of the default webpack command, which normally gives a lot of debug output (along with information about the build-steps, errors, etc.) - I would suggest using that to debug it, and then switch to the programmatical approach when you know that everything is working.
It looks like you want to do server-side rendering with react. Some special configuration is needed for your webpack configuration then. This is already handled here: http://jlongster.com/Backend-Apps-with-Webpack--Part-I
Without having the package.json it is really hard to debug exactly what goes wrong here.
Assuming your package.json is correct, here's a dev.js file that would work with the above code:
const webpack = require('webpack');
const path = require('path');
const fs = require('fs');
var nodeModules = {};
fs.readdirSync('node_modules')
.filter(function(x) {
return ['.bin'].indexOf(x) === -1;
})
.forEach(function(mod) {
nodeModules[mod] = 'commonjs ' + mod;
});
// returns a Compiler instance
const compiler = webpack(
{
context: path.resolve(__dirname, "../src"),
target: 'node',
entry: [
'./server/app.js'
],
output: {
path: path.join(__dirname, '../dist'),
filename: 'bundle.js'
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader?presets[]=es2015&presets[]=react'
}
]
},
externals: nodeModules
}
);
compiler.run(function(err, stats) {
console.log(err, stats);
});

Resources