I am trying to use webpack to bundle my application through teamcity and deploy it on a remote server. I was able to achieve it as well, but on the final step of running the application using 'node ./dist/main' I am getting the below error.
PS C:\engage-analytics> node .\dist\main.js
node:internal/modules/cjs/loader:928
throw err;
^
Error: Cannot find module '@nestjs/core'
Require stack:
- C:\engage-analytics\dist\main.js
at Function.Module._resolveFilename (node:internal/modules/cjs/loader:925:15)
at Function.Module._load (node:internal/modules/cjs/loader:769:27)
at Module.require (node:internal/modules/cjs/loader:997:19)
at require (node:internal/modules/cjs/helpers:92:18)
at Object.@nestjs/core (C:\engage-analytics\dist\main.js:181:18)
at __webpack_require__ (C:\engage-analytics\dist\main.js:229:32)
at fn (C:\engage-analytics\dist\main.js:331:21)
at eval (webpack://nestjs-intro/./src/main.ts?:3:16)
at Object../src/main.ts (C:\engage-analytics\dist\main.js:52:1)
at __webpack_require__ (C:\engage-analytics\dist\main.js:229:32) {
code: 'MODULE_NOT_FOUND',
requireStack: [ 'C:\\engage-analytics\\dist\\main.js' ]
}
PS C:\engage-analytics> webpack -v
webpack 5.18.0
webpack-cli 4.4.0
PS C:\engage-analytics> node -v
v15.5.1
When I run the same command on the teamcity work directory I am able to start the application. After build I have simply copied the contents of dist to remote server. Here is the package.json, I am executing the highlighted scripts through npm on teamcity server.
package.json
{
name: "nestjs-intro",
version: "0.0.1",
description: "<p align="center"> <a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo_text.svg" width="320" alt="Nest Logo" /></a> </p>",
author: "",
private: true,
license: "UNLICENSED",
scripts: {
prebuild: "rimraf dist",
**build**: "npm install && npx webpack",
format: "prettier --write "src/**/*.ts" "test/**/*.ts"",
start: "nest start",
start:dev: "nest start --watch",
start:debug: "nest start --debug --watch",
**start:prod**: "node dist/main",
lint: "eslint "{src,apps,libs,test}/**/*.ts" --fix",
test: "jest",
test:watch: "jest --watch",
test:cov: "jest --coverage",
test:debug: "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
test:e2e: "jest --config ./test/jest-e2e.json"
},
dependencies: {
@nestjs/common: "^7.6.5",
@nestjs/core: "^7.5.1",
@nestjs/mongoose: "^7.2.2",
mongoose: "^5.11.3",
reflect-metadata: "^0.1.13",
@nestjs/platform-express: "^7.6.5",
rxjs: "^6.6.3"
},
devDependencies: {
@nestjs/cli: "^7.5.1",
@nestjs/schematics: "^7.1.3",
@nestjs/testing: "^7.5.1",
@types/express: "^4.17.8",
@types/jest: "^26.0.15",
@types/node: "^14.14.6",
@types/supertest: "^2.0.10",
@typescript-eslint/eslint-plugin: "^4.6.1",
@typescript-eslint/parser: "^4.6.1",
eslint: "^7.12.1",
eslint-config-prettier: "7.1.0",
eslint-plugin-prettier: "^3.1.4",
jest: "^26.6.3",
prettier: "^2.2.1",
rimraf: "^3.0.2",
ts-jest: "^26.4.4",
ts-loader: "^8.0.14",
ts-node: "^9.1.1",
tsconfig-paths: "^3.9.0",
tsconfig-paths-webpack-plugin: "^3.3.0",
typescript: "^4.0.5",
webpack: "^5.15.0",
webpack-cli: "^4.4.0"
},
jest: {
moduleFileExtensions: [
"js",
"json",
"ts"
],
rootDir: "src",
testRegex: ".*\.spec\.ts$",
transform: {
^.+\.(t|j)s$: "ts-jest"
},
collectCoverageFrom: [
"**/*.(t|j)s"
],
coverageDirectory: "../coverage",
testEnvironment: "node"
},
main: "webpack.config.js",
}
webpack-config
const webpack = require('webpack');
const path = require('path');
const nodeExternals = require('webpack-node-externals');
// const config = require('config');
const isProduction = typeof process.env.NODE_ENV !== 'undefined' && process.env.NODE_ENV === 'production';
const mode = isProduction ? 'production' : 'development';
module.exports = {
entry: [
'webpack/hot/poll?100',
'./src/main.ts',
],
optimization: {
minimize: false,
},
target: 'node',
mode,
externals: [
nodeExternals({
allowlist: ['webpack/hot/poll?100'],
}),
],
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'ts-loader',
options: {
transpileOnly: true
},
exclude: /node_modules/,
}
]
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
// new webpack.WatchIgnorePlugin([/\.js$/, /\.d\.ts$/]),
new webpack.DefinePlugin(
{
// CONFIG: JSON.stringify(config)
}
),
],
output: {
path: path.join(__dirname, 'dist'),
filename: 'main.js',
},
};
When using nodeExternals
webpack skips bundling the node_modules
into the final bundle, so you till need to run your production dependency install command on your production server.
This is done due to low level dependencies of some node packages, like pg
needing C++ bindings or bcrypt
using node-pre-gyp
and python. Bundling these dependencies into your code isn't compatible, which is why they need to be left external. There's a lot of discussion that goes more into depth on this here