![](/img/trans.png)
[英]Single file bundle with NestJS + Typescript + Webpack + node_modules
[英]Bundle nestjs app into 1 file with all dependencies using webpack
我正在尝试创建 1 个可以为运行我的 nestjs 应用程序提供的文件。 我设法从构建过程中创建了 1 个文件,但是当在不同位置的不同机器/我的机器上运行它时,当我的项目是我得到以下 output:
node main.js
node:internal/modules/cjs/loader:936
throw err;
^
Error: Cannot find module '@nestjs/core'
Require stack:
- C:\Users\xxx\Desktop\Temp\main.js
[90m at Function.Module._resolveFilename (node:internal/modules/cjs/loader:933:15)[39m
[90m at Function.Module._load (node:internal/modules/cjs/loader:778:27)[39m
[90m at Module.require (node:internal/modules/cjs/loader:1005:19)[39m
[90m at require (node:internal/modules/cjs/helpers:102:18)[39m
at Object.__decorate (C:\Users\xxx\Desktop\Temp\main.js:8:18)
at __webpack_require__ (C:\Users\xxx\Desktop\Temp\main.js:3635:42)
at C:\Users\xxx\Desktop\Temp\main.js:3648:16
at C:\Users\xxx\Desktop\Temp\main.js:3656:3
at Object.<anonymous> (C:\Users\xxx\Desktop\Temp\main.js:3658:12)
[90m at Module._compile (node:internal/modules/cjs/loader:1105:14)[39m {
code: [32m'MODULE_NOT_FOUND'[39m,
requireStack: [ [32m'C:\\Users\\xxx\\Desktop\\Temp\\main.js'[39m ]
-------
\Desktop\Temp\main.js:3658:12)
[90m at Module._compile (node:internal/modules/cjs/loader:1105:14)[39m {
code: [32m'MODULE_NOT_FOUND'[39m,
requireStack: [ [32m'C:\\Users\\xxx\\Desktop\\Temp\\main.js'[39m ]
webpack.config.js( 取自这篇文章):
const path = require('path');
const webpack = require('webpack');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const WebPackIgnorePlugin = {
checkResource: function (resource) {
const lazyImports = [
'@nestjs/microservices',
'@nestjs/microservices/microservices-module',
'cache-manager',
'class-transformer',
'class-validator',
'fastify-static',
];
if (!lazyImports.includes(resource)) return false;
try {
require.resolve(resource);
} catch (err) {
return true;
}
return false;
},
};
module.exports = {
mode: 'production',
target: 'node',
entry: {
server: './src/main.ts',
},
devtool: 'source-map',
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
],
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
node: {
__dirname: false,
},
plugins: [
new CleanWebpackPlugin(),
new webpack.IgnorePlugin(WebPackIgnorePlugin),
],
optimization: {
minimize: false,
},
performance: {
maxEntrypointSize: 1000000000,
maxAssetSize: 1000000000,
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'prod'),
},
};
Package.json:
"scripts": {
"prebuild": "rimraf dist",
"build": "nest build --webpack",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start --entryFile ./my-app/src/main.js",
"start:dev": "nest start --entryFile ./my-app/src/main.js --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": "^8.0.0",
"@nestjs/core": "^8.0.0",
"@nestjs/platform-express": "^8.0.0",
"@stablelib/aes-kw": "^1.0.1",
"bcryptjs": "^2.4.3",
"core-js": "^3.18.1",
"csv-parser": "^3.0.0",
"ec-key": "^0.0.4",
"express": "^4.17.3",
"fast-xml-parser": "^3.20.3",
"futoin-hkdf": "^1.4.2",
"net-snmp": "^3.5.5",
"object.pick": "^1.3.0",
"path": "^0.12.7",
"pkcs11js": "^1.3.0",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.2.0",
"serialport": "^10.4.0",
"sqlite3": "^5.0.2",
"ssh2": "^1.11.0",
"sshpk": "^1.17.0",
"strftime": "^0.10.1",
"tftp": "^0.1.2",
"tsconfig-paths-webpack-plugin": "^4.0.0",
"webpack-cli": "^4.10.0",
"winston": "^3.3.3",
"winston-transport": "^4.4.0"
},
"devDependencies": {
"@nestjs/cli": "^8.0.0",
"@nestjs/schematics": "^8.0.0",
"@nestjs/testing": "^8.0.0",
"@types/express": "^4.17.13",
"@types/jest": "27.5.0",
"@types/node": "^16.0.0",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "28.0.3",
"prettier": "^2.3.2",
"source-map-support": "^0.5.20",
"supertest": "^6.1.3",
"ts-jest": "28.0.1",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.0.0",
"typescript": "^4.3.5",
"webpack": "^5.74.0",
"webpack-merge": "^5.8.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"
}
构建 output:
> npm run build
> my-app@0.0.1 prebuild
> rimraf dist
> my-app@0.0.1 build
> nest build --webpack
webpack 5.73.0 compiled successfully in 10253 ms
output 产生1个文件: main.js
在 webpack 配置文件中,您有所有 node_modules 的exclude
规则:使用webpack-node-externals
尝试这个简单的配置并确保清理 node_modules 并重新安装所有
const webpack = require('webpack');
const nodeExternals = require('webpack-node-externals');
const { TsconfigPathsPlugin } = require('tsconfig-paths-webpack-plugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const { RunScriptWebpackPlugin } = require('run-script-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const path = require('path');
const tsConfigFile = 'tsconfig.build.json';
const lazyImports = [
'@nestjs/microservices',
'cache-manager',
'class-validator',
'class-transformer',
];
const config = {
entry: {
server: `./src/main.ts`,
'app-init': './scripts/app-init.ts',
'prisma-migrations': './scripts/prisma-migrations.ts',
},
output: {
filename: `[name].js`,
path: path.join(__dirname, 'dist'),
devtoolModuleFilenameTemplate: `${path.sep}[absolute-resource-path][loaders]`,
},
cache: {
type: 'filesystem',
cacheDirectory: path.resolve(__dirname, '.build_cache'),
},
devtool: false,
target: 'node',
mode: 'none',
optimization: {
nodeEnv: false,
},
node: {
__filename: false,
__dirname: false,
},
externals: [nodeExternals()],
module: {
rules: [
{
test: /.ts?$/,
include: [
path.resolve(__dirname, 'src'),
],
use: [
{
loader: 'ts-loader',
options: {
transpileOnly: true,
configFile: tsConfigFile,
},
},
],
exclude: /node_modules/,
},
],
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
plugins: [
new TsconfigPathsPlugin({
configFile: tsConfigFile,
}),
],
},
plugins: [
new webpack.ProgressPlugin(),
new webpack.IgnorePlugin({
checkResource(resource) {
if (!lazyImports.includes(resource)) {
return false;
}
try {
require.resolve(resource, {
paths: [process.cwd()],
});
} catch (err) {
return true;
}
return false;
},
}),
new CleanWebpackPlugin(),
new ForkTsCheckerWebpackPlugin({
typescript: {
configFile: tsConfigFile,
},
}),
],
};
module.exports = (
env = { debug: false },
argv = { mode: 'none', watch: false },
) => {
config.mode = argv.mode;
if (argv.mode === 'development') {
config.watch = argv.watch;
config.cache.name = env.debug ? 'development_debug' : 'development';
config.devtool = env.debug ? 'eval-source-map' : undefined;
// If running in watch mode
if (config.watch) {
config.cache.name = env.debug
? 'development_debug_hmr'
: 'development_hmr';
config.entry = {
...config.entry,
server: [
'webpack/hot/poll?100',
'webpack/hot/signal',
config.entry.server,
],
};
config.externals = [
nodeExternals({
allowlist: ['webpack/hot/poll?100', 'webpack/hot/signal'],
}),
];
config.plugins = [
...config.plugins,
new webpack.WatchIgnorePlugin({ paths: [/\.js$/, /\.d\.ts$/] }),
new webpack.HotModuleReplacementPlugin(),
new RunScriptWebpackPlugin({
name: 'server.js',
nodeArgs: env.debug ? ['--inspect'] : undefined, // Allow debugging
signal: true, // Signal to send for HMR (defaults to `false`, uses 'SIGUSR2' if `true`)
keyboard: true, // Allow typing 'rs' to restart the server. default: only if NODE_ENV is 'development'
// args: ['scriptArgument1', 'scriptArgument2'], // pass args to script
}),
];
}
}
if (argv.mode === 'production') {
config.devtool = 'source-map';
config.cache.name = 'production';
config.optimization.minimize = true;
config.optimization.minimizer = [
new TerserPlugin({
terserOptions: {
keep_classnames: true,
keep_fnames: true,
},
}),
];
}
return config;
};
加上一些与 nextjs 相关的 tsconfig 编辑:
{
"compilerOptions": {
"module": "esnext",
"moduleResolution": "node",
...
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.