I'm trying to build my project using webpack. this is my webpack configuration file.
import * as path from 'path';
import * as webpack from 'webpack';
import { fileURLToPath } from 'url';
import nodeExternals from 'webpack-node-externals';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const input = path.resolve(__dirname, '../src/server');
const output = path.resolve(__dirname, '../server');
const config: webpack.Configuration = {
entry: {
server: path.resolve(input, './index.ts'),
},
experiments: {
outputModule: true,
},
externals: [
nodeExternals(),
],
module: {
rules: [
{
exclude: /node_modules/,
loader: 'ts-loader',
options: {
transpileOnly: true,
},
test: /\.ts(x?)$/,
},
],
},
name: 'server',
output: {
filename: '[name].js',
path: output,
publicPath: '/node/',
chunkFormat: 'module',
},
resolve: {
extensions: ['*', '.js', '.jsx', '.json', '.ts', '.tsx'],
},
target: 'node',
};
export default config;
when I run bundle, I get an error.
module.exports = require("express"); ^
ReferenceError: require is not defined in ES module scope, you can use
import instead This file is being treated as an ES module because it
has a '.js' file extension and
'/test/package.json' contains "type":
"module". To treat it as a CommonJS script, rename it to use the
'.cjs' file extension.
in the bundle I see
/***/ ((module) => {
module.exports = require("express");
/***/ }),
I expect to get something like this
/***/ ((module) => {
module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("http");
/***/ }),
I tried adding this code to config, but it didn't help
externals: [
nodeExternals(),
'express',
]
server.ts
import express from 'express';
const app = express();
app.get('/', (request, response) => {
response.send('Hi there');
});
app.listen(3000, () => {
console.log('Listen on the port 3000...');
});
Related
I have some react-app and trying to enable server-side rendering.
I make the server/index.js and describe the server logic (using express)
import path from 'path';
import fs from 'fs';
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import express from 'express';
import App from 'src/App';
const PORT = process.env.PORT || 3006;
const app = express();
app.get('/', (req, res) => {
const app = ReactDOMServer.renderToString(<App />);
const indexFile = path.resolve('./build/index.html'); <-- it's result of `npm run build`
fs.readFile(indexFile, 'utf8', (err, data) => {
if (err) {
console.error('Something went wrong:', err);
return res.status(500).send('Oops, better luck next time!');
}
return res.send(
data.replace('<div id="root"></div>', `<div id="root">${app}</div>`)
);
});
});
app.use(express.static('./build'));
app.listen(PORT, () => {
console.log(`Server is listening on port ${PORT}`);
});
Then I use webpack for convert jsx to js
const path = require('path');
const nodeExternals = require('webpack-node-externals');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
entry: './server/index.js',
target: 'node',
externals: [nodeExternals()],
devtool: false,
output: {
path: path.resolve('build'),
filename: 'server.js',
asyncChunks: true,
clean: true
},
module: {
rules: [
{
test: /\.tsx?$/,
use: [
{
loader: 'babel-loader',
options: { presets: ['solid'] },
},
{
loader: 'ts-loader',
options: {
compilerOptions: { noEmit: false },
}
}],
exclude: /node_modules/,
},
{
test: /\.s[ac]ss$/i,
use: [
MiniCssExtractPlugin.loader, // Creates `style` nodes from JS strings
"css-loader", // Translates CSS into CommonJS
"sass-loader", // Compiles Sass to CSS
],
},
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
{
test: /\.js$/,
use: 'babel-loader'
}
]
},
plugins: [
new MiniCssExtractPlugin()
],
resolve: {
roots: [
'node_modules'
],
alias: {
src: path.resolve(__dirname, 'src')
},
extensions: ['.tsx', '.ts', '.js'],
},
};
Then I try to start server node build/server.js but got some error
/* harmony import */ var solid_js_web__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! solid-js/web */ "./node_modules/solid-js/web/dist/server.js");
...
const _tmpl$ = /*#__PURE__*/(0,solid_js_web__WEBPACK_IMPORTED_MODULE_0__.template)(`<li></li>`, 2),
TypeError: (0 , solid_js_web__WEBPACK_IMPORTED_MODULE_0__.template) is not a function
I can't understand how it happened and what's wrong? How can I make it work?
It was my mistake in webpack config
{
loader: 'babel-loader',
options: {
presets: ['#babel/env', '#babel/preset-react'], <-- correct
// presets: ['solid'], <-- incorrect
},
},
I have a problem with webpack-hot-middleware and I'm not sure what I'm doing wrong.
In summary: Everytime I run the command node./ dev webpack runs and start to monitoring changes. This part is working great.
When I change my src / assets / js / index.js file, it refreshes the page after aply the changes. But with my src / assets / styles / index.scss file, it is only if that change is the first change I made after webpack start monitoring.
If I run node./ dev and change theindex.scss, the browser refresh after the changes are made in the output. On the second time, the browser does not refresh. Same happens if I change my index.js and tries to changeindex.scss.
In my Chrome console tab, it shows the following messages (when the page does not upload):
[HMR] bundle rebuilding client.js:242
[HMR] bundle rebuilt in 2407ms process-update.js:39
[HMR] Checking for updates on the server... process-update.js:110
[HMR] Nothing hot updated. process-update.js:119
[HMR] App is up to date.
Here is a Sample of my working code:
dev.js
const webpack = require('webpack');
const webpackMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const express = require('express');
const app = express();
const config = require('./webpack.dev.conf');
const DEFAULT_PORT = 3000;
const options = {
publicPath: config.output.publicPath
};
config.entry.main.push('webpack-hot-middleware/client?reload=true');
const compiler = webpack(config);
console.log('Starting the dev web server...');
app.use(webpackMiddleware(compiler, options));
app.use(webpackHotMiddleware(compiler));
app.listen(DEFAULT_PORT, (err) => {
if (err) {
console.log(err);
}
console.log('WebpackHotMiddleware is listening at http://localhost:3000/...');
});
webpack.dev.conf.js
const path = require('path');
const webpack = require('webpack');
const merge = require('webpack-merge');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const base = require('./webpack.base.conf');
const appHtmlTitle = 'Hello World';
process.env.NODE_ENV = 'development';
const dirSrc = path.join(__dirname, 'src');
process.noDeprecation = true;
module.exports = {
mode: 'development',
devtool: 'source-map',
output: {
path: path.join(__dirname, 'dev'),
publicPath: '/',
filename: 'assets/js/[name].js?[hash]'
},
optimization: {
splitChunks: {
chunks: 'all' // include all types of chunks
},
},
entry: {
main: [
path.join(dirSrc, 'assets', 'js', 'index'),
path.join(dirSrc, 'assets', 'styles', 'index.scss')
]
},
module: {
rules: [{
test: /\.html$/,
loader: 'html-loader',
options: { minimize: true }
},
{
enforce: 'pre',
test: /\.js$/,
exclude: [/node_modules/],
loader: 'eslint-loader'
},
{
test: /\.js?$/,
exclude: [/node_modules/],
loader: 'babel-loader'
},
// CSS / SASS
{
test: /\.(s)?css/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: { publicPath: '/' }
},
{ loader: 'css-loader' },
{ loader: 'sass-loader' }
]
},
// IMAGES
{
test: /\.(jpe?g|png|gif)$/i,
use: [
{
loader: 'file-loader',
options: {
publicPath: '/',
name: 'assets/images/[name].[ext]'
}
}
]
},
// FONTS
{
test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
loader: 'file-loader',
options: {
publicPath: '/',
name: 'assets/fonts/[name].[ext]'
}
}]
},
plugins: [
new CleanWebpackPlugin(['dev'], { verbose: false }),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
new HtmlWebpackPlugin({
filename: path.join(__dirname, 'dev', 'index.html'),
template: 'src/index.ejs',
title: appHtmlTitle,
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
}
}),
new MiniCssExtractPlugin({
publicPath: '/',
filename: 'assets/css/[name].css?[hash]'
}),
new webpack.DefinePlugin({
PRODUCTION: JSON.stringify(false)
})
]
};
I'm trying to setup webpack to bundle my backend code.
My webpack config looks like:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const nodeExternals = require('webpack-node-externals');
const outputDirectory = 'dist';
const client = {
mode: 'production',
entry: {
'app': [
'babel-polyfill',
'./client/index.js'
]
},
output: {
path: path.join(__dirname, outputDirectory),
publicPath: '/',
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.(js|jsx)$/, exclude: /node_modules/, loader: 'babel-loader'
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.(gif|svg|jpg|png)$/,
loader: "file-loader"
}
]
},
plugins: [
new CleanWebpackPlugin([outputDirectory]),
new HtmlWebpackPlugin({
template: './index.html',
})
]
}
const server = {
mode: 'production',
target: 'node',
entry: {
'app': [
'./server/server.js'
]
},
externals: [nodeExternals()],
output: {
path: path.join(__dirname, '/server'),
filename: 'server.bundle.js'
}
}
module.exports = [client, server]
If I run the non-webpack server.js, everything works fine. However if I run the webpack bundled server.bundle.js, express throws:
Error: ENOENT: no such file or directory, stat '/dist/index.html'
Both server files are in the same directory. Has anyone run into this issue before?
I figured it out, it's not explicitly stated in webpack's documentation but you need to configure a "node" property when using express
Ex. add this to your config
node: {
// Need this when working with express, otherwise the build fails
__dirname: false, // if you don't put this is, __dirname
__filename: false, // and __filename return blank or /
},
I'm having a hard time finding resources that explain how to connect webpack to a express server app. I'm wanting to use webpack for babel to use es6 when writing react and use its hot-module and cheap-module-source-map. But, webpack runs it's own express server and that currently conflicts with my express app. I want my express app to dictate the port and routes but still get the benefits of using webpack.
Any ideas?
The express app looks something like this:
var express = require('express'),
Sequelize = require('sequelize'),
/*
set up sequelize ...
app.route ...
*/
app.listen(port), function () {
console.log('Express server listening on port ' + port
});
You don't need the webpack-dev-server to use Webpack for Babel to use ES2015 when writing React and use its hot-module and cheap-module-source-map.
Webpack configuration for React app in development env:
module.exports = {
entry: {
app: [
'react-hot-loader/patch',
'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=20000',
'app/index.js,
],
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
],
})
.babelrc looks like this:
{
"presets": ["react", "es2015", "stage-0"],
"env": {
"development": {
"plugins": ["react-hot-loader/babel"]
}
}
}
app/index.js:
import { AppContainer} from 'react-hot-loader'
...
<AppContainer>
<App />
</AppContainer>
...
if (module.hot) {
module.hot.accept('./routes', () => {
// Hot reloading
})
}
server/index.js:
import webpack from 'webpack'
import webpackDevMiddleware from 'webpack-dev-middleware'
import webpackHotMiddleware from 'webpack-hot-middleware'
import webpackConfig from './webpack.dev.config'
const compiler = webpack(webpackConfig)
app.use(webpackDevMiddleware(compiler, {
noInfo: true,
publicPath: webpackConfig.output.publicPath,
}))
app.use(webpackHotMiddleware(compiler, {
path: '/__webpack_hmr',
heartbeat: 10000,
}))
I am not sure if it's allowed to refer to my own repo here, but please check my Github repo here to see how I have integrated React, Express, Webpack, HMR and Babel.
What I ended up doing was I used 2 different configurations, 1 for packing the server stuff together using webpack, and 1 for packing all the browser stuff together and also run webpack dev server for hot reloading.
Server webpack config aka webpack.node.config.js now looks like this:
var webpack = require('webpack');
var path = require('path');
var fs = require('fs');
var nodeModules = {};
// note the path.resolve(__dirname, ...) part
// without it, eslint-import-resolver-webpack fails
// since eslint might be invoked with different cwd
fs.readdirSync(path.resolve(__dirname, 'node_modules'))
.filter(x => ['.bin'].indexOf(x) === -1)
.forEach(mod => { nodeModules[mod] = `commonjs ${mod}`; });
// es5 style alternative
// fs.readdirSync(path.resolve(__dirname, 'node_modules'))
// .filter(function(x) {
// return ['.bin'].indexOf(x) === -1;
// })
// .forEach(function(mod) {
// nodeModules[mod] = 'commonjs ' + mod;
// });
module.exports =
{
// The configuration for the server-side rendering
name: 'server',
target: 'node',
entry: './app/server/serverEntryPrototype.js',
output: {
path: './bin/',
publicPath: 'bin/',
filename: 'serverEntryPoint.js'
},
externals: nodeModules,
module: {
loaders: [
{ test: /\.js$/,
loaders: [
// 'imports?document=this',
// 'react-hot',
'babel-loader'
//,'jsx-loader'
]
},
{ test: /\.json$/, loader: 'json-loader' },
]
},
plugins: [
// new webpack.NormalModuleReplacementPlugin("^(react-bootstrap-modal)$", "^(react)$")
// new webpack.IgnorePlugin(new RegExp("^(react-bootstrap-modal)$"))
// new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
]
};
Browser webpack config aka webpack.browser.config.js now looks like this:
var webpack = require('webpack');
var path = require('path');
var buildPath = path.resolve(__dirname, 'assets');
var fs = require('fs');
var commonLoaders = [
{ test: /\.js$/,
loaders: [
'react-hot',
'babel-loader'
//,'jsx-loader'
]
}
];
module.exports =
{
// Makes sure errors in console map to the correct file
// and line number
name: 'browser',
devtool: 'eval',
entry: [
//'./bin/www.js',
'./app/index.js',
'webpack/hot/dev-server',
'webpack-dev-server/client?http://localhost:8081' // WebpackDevServer host and port
],
output: {
path: buildPath,
filename: '[name].js',
// Everything related to Webpack should go through a build path,
// localhost:3000/build. That makes proxying easier to handle
publicPath: 'http://localhost:8081/assets/'
},
extensions: [
'',
'.jsx', '.js',
'.json',
'.html',
'.css', '.styl', '.scss', '.sass'
],
module: {
loaders: [
// Compile es6 to js.
{
test: /app\/.*\.jsx?$/,
loaders: [
'react-hot',
'babel-loader'
]
},
///app\/.*\.json$/
{ test: /\.json$/, loader: 'json-loader' },
// Styles
{ test: /\.css$/, loader: 'style-loader!css-loader' },
{ test: /\.s(a|c)ss$/, loader: 'style!css?localIdentName=[path][name]---[local]---[hash:base64:5]!postcss!sass' },
// Fonts
{ test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: 'url-loader?limit=10000&minetype=application/font-woff' },
{ test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: 'file-loader' }
//{ test: /\.png$/, loader: 'url-loader?limit=100000' },
//{ test: /\.jpg$/, loader: 'file-loader' }
],
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin()
]
},
postcss: [
require('autoprefixer-core')
],
devtool: 'source-map'
}
;
I have a project I'm doing with node in ES6 which was using babel-node to run. Now I'm trying to implement babel in a more production manner and have tried two attempts.
Webpack babel-loader with following configuration:
module.exports = {
entry: './src/cloud/main.js',
devtool: 'source-map',
output: {
path: './src/static/build',
filename: 'bundle.js'
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loaders: [
'babel-loader?presets[]=es2015',
],
},
{
test: /\.css$/,
loaders: [
'style-loader',
'css-loader',
],
},
{
test: /\.html$/,
loaders: [
'raw-loader',
],
},
],
},
}
It started complaining about the import statement in main.js and to silence it I used ?presets[]=es2015 which I found in a similar question. Then the problem arrived in which it filtered to the import statements that went to node_modules with the following message:
ERROR in ./~/socket.io/lib/index.js
Module not found: Error: Cannot resolve module 'fs' in
My other approach was with the register hook like this:
require('babel-core/register')({
ignore: /node_modules/,
});
require('./main.js');
but it threw this message:
import express from 'express';
^^^^^^
SyntaxError: Unexpected reserved word
//main.js - simplified
import express from 'express'
const app = express()
const server = app.listen(port, () => {
console.log(`Listening at http://${server.address().address === '::' ? 'localhost' : server.address().address}:${server.address().port}`)
})
I don't think you need to exclude the node_modules in your loader config. However, you might want to let webpack know what to resolve. Try adding something like this:
resolve: {
root: path.join(__dirname),
fallback: path.join(__dirname, 'node_modules'),
modulesDirectories: ['node_modules'],
extensions: ['', '.json', '.js', '.jsx', '.scss', '.png', '.jpg', '.jpeg', '.gif']
},
The modulesDirectories key should keep webpack from running down every single require / import in your working directory.
Also, adding target to the top of your config should resolve issues with builtins like fs
target: 'node'
Ok I figured it out thanks to other answers and 4m1r' answer. I post the example code.
var path = require('path');
var fs = require('fs');
var nodeModules = {};
fs.readdirSync('node_modules')
.filter(function (x) {
return ['.bin'].indexOf(x) === -1;
})
.forEach(function (mod) {
nodeModules[mod] = 'commonjs ' + mod;
});
module.exports = {
name: 'server',
target: 'node',
context: path.join(__dirname, 'src', 'cloud'),
entry: {
server: './main.js'
},
output: {
path: path.join(__dirname),
filename: '[name].js'
},
externals: nodeModules,
module: {
loaders: [
{test: /\.js$/, exclude: /node_modules/, loaders: ['babel-loader?presets[]=es2015']}
]
},
resolve: {
root: path.join(__dirname),
fallback: path.join(__dirname, 'node_modules'),
modulesDirectories: ['node_modules'],
}
};
What was really important too was the externals key which prevented it fro leaking to the node_modules through requires and specifying for some reason ?presets[]=2015 in the babel-loader. I'm accepting 4m1r because it was what finally fixed the code.