How to use webpack for pack node program - node.js

Can you provide me an example how to use webpack for pack node js project to single file with dependencies?
I wrote webpack config, but its not working, because is packed only first (index.js) file. Whats is wrong with my script?
var webpack = require('webpack');
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 = {
entry: './src/index.js',
target: 'node',
output: {
path: path.join(__dirname, 'build'),
filename: 'backend.js'
},
externals: nodeModules,
plugins: [
],
devtool: 'sourcemap'
}
src/index.js
src/library.js
src/library2.js
How to pack into single file with dependency from node_modules?

You should install webpack.
This is install command:
npm install --save-dev webpack#2.3.3 --cache /tmp/empty-cache --global
2.3.3 is version of your project.
You can specify version and then use this command:
webpack -w
It will work.

Related

Node/Webpack: Load package.json with ES6

I want to load my package.json in an ES6 environment and access it like pkg.name.
My solution so far is making a simple module for reading the file with fs, parsing it and import this module in every place I need to access it.
The problem is, I want to bundle my script with webpack and the path to the package.json is resolved to my current path from my pc I bundle everything. Like: D:/dev/app-name/package.json
This is wrong, I need a way to run the bundled script afterwards on e.g. a separate pc, or linux and still find the package.json. Is there a way to tell webpack to not resolve this path, or is there another (simpler) way to use the "root" path in my script?
My bundled app is an express server and I'm using webpack 5.
src/api/package-json.js:
import path from 'path'
import { fileURLToPath } from 'url'
import fs from 'fs-extra'
// Path CONST
// we need to change up how __dirname is used for ES6 purposes
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const PROJECT_ROOT = path.join(__dirname, '..', '..')
const PKG_FILE = path.join(PROJECT_ROOT, 'package.json')
// Package.json
const pkg = JSON.parse(fs.readFileSync(PKG_FILE)) // import pkg from '~root/package.json'
export default pkg
src/api/app.js:
import pkg from './package-json.js'
console.log(pkg.name)
webpack.config.js:
import path from 'path'
import { fileURLToPath } from 'url'
// import nodeExternals from 'webpack-node-externals'
// we need to change up how __dirname is used for ES6 purposes
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const { NODE_ENV = 'production' } = process.env
export default {
entry: './src/api/app.js',
mode: NODE_ENV,
target: 'node',
node: {
global: false,
__filename: false,
__dirname: false
},
output: {
path: path.resolve(__dirname, 'dist', 'server'),
filename: 'app.cjs'
},
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx']
},
externals: {
'node-pty': 'commonjs2 node-pty'
}
}
I found a workaround with process.cwd(), but I don't think this is a good solution for my problem. The problem is, process.cwd() can be changed in runtime as far as I know. :(
src/api/package-json.js:
import path from 'path'
import fs from 'fs-extra'
// Path CONST
const PROJECT_ROOT = process.cwd()
const PKG_FILE = path.join(PROJECT_ROOT, 'package.json')
// Package.json
const pkg = JSON.parse(fs.readFileSync(PKG_FILE)) // import pkg from '~root/package.json'
export default pkg

How to include readline in webpack

I have created nodejs console app and trying to minify it with webpack.
webpack.config.js I got:
const path = require('path');
module.exports = {
externals: {
fs: "require('fs')",
readline: "require('readline')",
},
entry: './index.js',
output: {
path: path.resolve(__dirname, 'bin'),
filename: 'bin.js'
},
mode: 'development'
};
Everything goes ok before I run builded app:
readline.js:189
input.on('data', ondata);
^
TypeError: input.on is not a function
at new Interface (readline.js:189:11)
at Object.createInterface (readline.js:69:10)
What should I do to prevent this error?
Somewhere I found the solution. In the webpack configuration file I must set target: node to point the collector what to do with standard external packages (e.g. readline, fs, path, etc.).

How to run and pack external executable using Electron?

For example, I have a compiled binary cudaDeviceQuery which returns a list of devices as JSON. Here's a piece of code:
export default function cudaDeviceQuery(): Promise<CollectorResponse> {
const throwError = () => {
throw new Error("Unfortunately your platform isn't yet unsupported");
};
const file = __DARWIN__
? path.join(__dirname, 'darwin', 'cudaDeviceQuery')
: __WIN32__
? path.join(__dirname, 'win', 'cudaDeviceQuery.exe')
: throwError();
const descriptor = spawn(file);
return new Promise((resolve, reject) => {
let outerData = '';
descriptor.stdout.on('data', data => {
outerData += data;
});
descriptor.on('close', () => {
try {
resolve(JSON.parse(outerData));
} catch (e) {
reject(e);
}
});
});
}
But when I use this function from renderer process __dirname is /, so I get spawn /darwin/cudaDeviceQuery ENOENT error. What's proper way to spawn it in dev envivroment and pack it in production?
A webpack config:
webpack.config.base.js:
/**
* Base webpack config used across other specific configs
*/
const webpack = require('webpack');
const path = require('path');
const getReplacements = require('./app/app-info').getReplacements;
const { dependencies: externals } = require('./app/renderer/package.json');
module.exports = {
module: {
noParse: [path.join(__dirname, 'node_modules/ws')],
rules: [
{
test: /\.tsx?$/,
use: [
{
loader: 'babel-loader',
},
{
loader: 'ts-loader',
},
],
exclude: /node_modules/,
},
],
},
output: {
path: path.join(__dirname, 'app', 'renderer'),
filename: 'bundle.js',
libraryTarget: 'commonjs2',
},
// https://webpack.github.io/docs/configuration.html#resolve
resolve: {
extensions: ['.js', '.ts', '.tsx', 'json'],
modules: [path.join(__dirname, 'app', 'renderer'), 'node_modules'],
},
plugins: [new webpack.DefinePlugin(getReplacements())],
externals: [...Object.keys(externals || {}), 'ws'],
};
webpack.config.development.js:
/**
* Build config for development process that uses Hot-Module-Replacement
* https://webpack.github.io/docs/hot-module-replacement-with-webpack.html
*/
const webpack = require('webpack');
const merge = require('webpack-merge');
const baseConfig = require('./webpack.config.base');
const getReplacements = require('./app/app-info').getReplacements;
const port = process.env.PORT || 3000;
module.exports = merge(baseConfig, {
devtool: 'inline-source-map',
entry: [
'react-hot-loader/patch',
`webpack-hot-middleware/client?path=http://localhost:${port}/__webpack_hmr&reload=true`,
'./app/renderer/index',
],
output: {
publicPath: `http://localhost:${port}/dist/`,
},
module: {
rules: [
// Css, SCSS, woff loaders are here
],
},
plugins: [
// https://webpack.github.io/docs/hot-module-replacement-with-webpack.html
new webpack.HotModuleReplacementPlugin(),
new webpack.LoaderOptionsPlugin({
debug: true,
}),
],
// https://github.com/chentsulin/webpack-target-electron-renderer#how-this-module-works
target: 'electron-renderer',
});
webpack.config.electron.js:
/**
* Build config for electron 'Main Process' file
*/
const webpack = require('webpack');
const merge = require('webpack-merge');
const baseConfig = require('./webpack.config.base');
const getReplacements = require('./app/app-info').getReplacements;
module.exports = merge(baseConfig, {
devtool: 'source-map',
entry: ['./app/main/index.ts'],
// 'main.js' in root
output: {
path: __dirname,
filename: './app/main/main.js',
},
plugins: [
// Add source map support for stack traces in node
// https://github.com/evanw/node-source-map-support
// new webpack.BannerPlugin(
// 'require("source-map-support").install();',
// { raw: true, entryOnly: false }
// ),
],
/**
* Set target to Electron specific node.js env.
* https://github.com/chentsulin/webpack-target-electron-renderer#how-this-module-works
*/
target: 'electron-main',
/**
* Disables webpack processing of __dirname and __filename.
* If you run the bundle in node.js it falls back to these values of node.js.
* https://github.com/webpack/webpack/issues/2010
*/
node: {
__dirname: false,
__filename: false
},
});
As you see, I'm using dev server for hot module replacement, so maybe that is reason of this... I have server.js which create server with scripts and then I use it from main process. Here's server.js:
/**
* Setup and run the development server for Hot-Module-Replacement
* https://webpack.github.io/docs/hot-module-replacement-with-webpack.html
*/
const argv = require('minimist')(process.argv.slice(2));
const { spawn } = require('child_process');
async function createMiddleware(port, configPath) {
const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const config = require(configPath);
const app = express();
const compiler = webpack(config);
const PORT = process.env.PORT || port;
const wdm = webpackDevMiddleware(compiler, {
publicPath: config.output.publicPath,
stats: {
colors: true,
},
});
app.use(wdm);
app.use(webpackHotMiddleware(compiler));
const server = app.listen(PORT, serverError => {
if (serverError) {
return console.error(serverError);
}
console.log(`Listening at http://localhost:${PORT}`);
});
process.on('SIGTERM', () => {
console.log('Stopping dev server');
wdm.close();
server.close(() => {
process.exit(0);
});
});
}
createMiddleware(3000, './webpack.config.development'); // A main renderer process
createMiddleware(3010, './webpack.config.server'); // A backend for communicating between renderer and remote server
if (argv['start-hot']) {
spawn('npm', ['run', 'start-hot'], {
shell: true,
env: process.env,
stdio: 'inherit',
})
.on('close', code => process.exit(code))
.on('error', spawnError => console.error(spawnError));
}
Another words, I need to call cudaDeviceQuery library from electron renderer process. I'm using a electron-builder but it doesn't matter, I can switch to another builder.
There are two things. If you set __dirname: true in your web app config you will get the relative path of the file from your context directory
If you set __dirname: false then __dirname will have the full path.
Development Mode
You have two options
Set __dirname: true and concatenate it with os.cwd()
Set __dirname: false and use __dirname directly
Production Mode
Set __dirname: true and use os.cwd().
Set __dirname: true and use process.resourcePath
I will prefer 2 as the preferred approach in production
Add this in package.json:
"scripts": {
"start": "electron .", "install": "electron-rebuild",
"package-osx": "electron-packager . Node-RED --platform=darwin --arch=x64 -- out=build --overwrite",
"package-mac": "electron-packager . --overwrite --platform=darwin --arch=x64 --prune=true --out=release-builds",
"package-win": "electron-packager . electron-serialport --overwrite --asar=true --platform=win32 --arch=x64 --prune=true --out=release-builds --version-string.CompanyName=CE --version-string.FileDescription=CE --version-string.ProductName=\"CryptoApp\"",
"package-linux": "electron-packager . electron-serialport --overwrite --asar=true --platform=linux --arch=x64 --prune=true --out=release-builds"
},
"dependencies": {
"electron-packager": "^12.1.0",
"electron-prebuilt": "^1.4.13",
}
In case of not working for windows use the following:
"package-win": "electron-packager . electron-serialport --overwrite --asar=true --platform=win32 --arch=ia32 --prune=true --out=release-builds --version-string.CompanyName=CE --version-string.FileDescription=CE --version-string.ProductName=\"CryptoApp\"",
Thanks...

How to load the js generated from webpack-dev-middleware and start that js

I am new to Node JS. I am working on creating a Backend API server with hot reloading by using webpack-hot-middleware. I try to use webpack-dev-middleware to load the server script (index.js) to the memory and execute that javascrip t file.
Here is my code: link
I find that my code can do the hot reloading successfully, but I fail to load the server script (index.js) and boot my API server. For some reasons, I want to use webpack-dev-middleware rather than webpack dev server.
It is my file strucuture:
├── package.json.
├── build
| ├── devserver.js
| └── webpack.devserver.config.js
├── server
| └── index.js (code of the API server)
└── dist
I found tons of tutorials about how to do that on the front end or the backend server that render the HTML, but I need to start my index.js.
So, my question is how to load the js file generated from webpack-dev-middleware and start that js file.
The below is what I try:
const path = require('path');
const express = require('express');
const webpack = require('webpack');
const webpackMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const webpackConfig = require('./webpack.devserver.config');
var StreamCache = require('stream-cache');
var fs = require('fs');
const compiler = webpack(webpackConfig);
const app = express();
const hotMiddleware = webpackHotMiddleware(compiler);
const middleware = webpackMiddleware(compiler, {
publicPath: "",
contentBase: 'server',
stats: {
colors: true,
hash: false,
timings: true,
chunks: false,
chunkModules: false,
modules: false
}
});
app.use(middleware);
app.use(hotMiddleware);
middleware.waitUntilValid(() => {
//Then, what should I do??????
console.log(middleware.fileSystem.readFileSync(path.join(__dirname, '../dist/backend.js')));
})
In webpack.devserver.config.js:
var fs = require('fs');
var path = require('path');
var webpack = require('webpack');
var nodeModules = {};
fs.readdirSync('node_modules')
.filter(function (x) {
return ['.bin'].indexOf(x) === -1;
})
.forEach(function (mod) {
nodeModules[mod] = 'commonjs ' + mod;
});
module.exports = {
devtool: 'eval-source-map',
entry: [
'webpack/hot/signal.js',
'./server/index.js'
],
target: 'node',
output: {
path: path.resolve(__dirname, '../dist'),
filename: 'backend.js',
publicPath: ''
},
plugins: [
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
new webpack.DefinePlugin({
})
],
recordsPath: path.resolve(__dirname, '../dist/a.json'),
resolveLoader: {
moduleExtensions: ['-loader']
},
externals: nodeModules,
watch: true,
module: {
rules: [{
test: /\.js$/,
loader: 'babel-loader'
},
{
test: /\.es6$/,
loaders: ['babel-loader']
},
{
test: /\.json?$/,
loader: 'json'
}
]
}
};
Result: code seems to be reloaded successfully after I change something on index.js, but I fail to see the change when I access /api/hi API.
webpack building...
webpack built 4f5191c370239429fae8 in 378ms
Version: webpack 3.10.0
Time: 378ms
Asset Size Chunks Chunk Names
backend.js 47 kB 0 [emitted] main
webpack: Compiled successfully.
webpack: Compiling...
webpack building...
webpack built 59489ea86a2ccf081fa6 in 30ms
Version: webpack 3.10.0
Time: 30ms
Asset Size Chunks Chunk Names
backend.js 47.1 kB 0 [emitted] main
0.4f5191c370239429fae8.hot-update.js 1.84 kB 0 [emitted] main
4f5191c370239429fae8.hot-update.json 43 bytes [emitted]
webpack: Compiled successfully.

how to use ngnix to serve webpack build files in production

I wrote a vue + webpack project and it works fine in webpack-dev-middleware. Now I want to deploy it with nginx. What I do is write a webpack.build.config.js and bundle all files into a dist folder. Then I just copy the dist folder into nginx html folder and assign the index in nginx.conf. However, it has an error said:
[Vue warn]: Failed to mount component: template or render function not
defined. (found in root instance)
I am a newbie for devops/backend and quite confused with the overall build or deploy process. Is webpack-dev-server or nodejs still need in the production environment? My production environment backend is nginx/PHP and IIS/.Net, now it do not have node installed at all.
My nginx.conf is
location / {
root html/dist;
index index.html index.htm;
}
And the webpack.build.config.js is
var path = require('path');
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var public_dir = "components";
//var ModernizrWebpackPlugin = require('modernizr-webpack-plugin');
module.exports = {
entry: [
path.join(__dirname,'./index.js')
],
output: {
path: path.join(__dirname, '/dist/'),
filename: '[name].js',
publicPath: '/'
},
devtool: 'eval-source-map',
plugins: [
new webpack.optimize.CommonsChunkPlugin('common.js'),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.UglifyJsPlugin(),
new webpack.optimize.AggressiveMergingPlugin(),
new HtmlWebpackPlugin({
filename: 'index.html',
template: path.resolve(__dirname, 'index.html'),
inject: true
})
],
resolve: {
root: [path.resolve('./components')],
extensions: ['', '.js', '.css']
},
module: {
loaders: [
]
}
};
When build I run
webpack -p --config ./webpack.build.config.js
I'm using vue-cli to init vuejs webpack project. And the project already has build script, you can refer it:
require('./check-versions')()
process.env.NODE_ENV = 'production'
var ora = require('ora')
var rm = require('rimraf')
var path = require('path')
var chalk = require('chalk')
var webpack = require('webpack')
var config = require('../config')
var webpackConfig = require('./webpack.prod.conf')
var spinner = ora('building for production...')
spinner.start()
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
if (err) throw err
webpack(webpackConfig, function (err, stats) {
spinner.stop()
if (err) throw err
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false,
chunks: false,
chunkModules: false
}) + '\n\n')
console.log(chalk.cyan(' Build complete.\n'))
console.log(chalk.yellow(
' Tip: built files are meant to be served over an HTTP server.\n' +
' Opening index.html over file:// won\'t work.\n'
))
})
})
After built, we will have a dist folder. Upload all files inside to html folder of Nginx (default)
Config root path to use full path like this:
listen 80;
server_name mydomain www.mydomain;
root /var/www/html;

Resources