번들에 node_modules 종속성이 있는 프로덕션용 NestJS 앱을 올바르게 구축하는 방법은 무엇입니까?
끝나고nest build
또는nest build --webpack
dist 폴더에 필요한 모든 모듈이 포함되어 있지 않습니다.Error: Cannot find module '@nestjs/core'
뛰려고 할 때node main.js
.
https://docs.nestjs.com/ 에서 운영을 위한 앱을 올바르게 구축하는 방법에 대한 명확한 지침을 찾을 수 없었습니다. 그래서 제가 무언가를 놓친 것이 아닐까요?
기본적으로, nest CLI는 다음을 포함하여 지원하지 않습니다.node_modules
에 대한 의존성dist
보따리를 묶다
그러나 번들의 종속성을 포함하는 사용자 지정 웹 팩 구성의 몇 가지 커뮤니티 예(예: 번들-네스트)가 있습니다.이번 호에서 설명한 바와 같이, 다음을 포함하는 것이 필요합니다.webpack.IgnorePlugin
사용하지 않는 동적 라이브러리를 화이트리스트에 추가합니다.
ncc는 완전한 NestJs 앱을 하나의 js 파일로 번들링하는 데 큰 역할을 합니다.
ncc build src/main.ts --out dist/main.js
더 이상의 구성은 필요하지 않습니다. 일부는 수정해야 할 수도 있습니다.import
경로. 트리 쉐이킹을 수행하고 바인딩을 감지하여 별도의 폴더에 복사하기도 합니다.
bundle-nest
보관/재보관 완료:
NestJS 또는 실제로는 일반적으로 NodeJS 웹 서버를 번들로 제공하지 않는 것이 좋습니다.이는 커뮤니티가 트리 셰이크 및 NestJS 앱 번들을 시도하는 동안 과거 참조용으로 보관됩니다.자세한 내용은 @kamilmysliwiec 주석을 참조하십시오.
사용 중인 라이브러리에 따라 Node.js 애플리케이션(NestJS 애플리케이션뿐만 아니라)을 모든 종속성(node_modules 폴더에 위치한 외부 패키지)과 함께 번들하지 않는 경우가 많습니다.이렇게 하면 (트리 쉐이킹으로 인해) 도커 이미지가 작아지고, 메모리 소비가 다소 감소하고, 부트스트랩 시간이 약간 증가할 수 있지만(특히 서버가 없는 환경에서 유용함), 에코시스템에서 일반적으로 사용되는 많은 일반적인 라이브러리와 함께 작동하지 않습니다.예를 들어 MongoDB로 NestJS(또는 그냥 express) 응용 프로그램을 구축하려고 하면 콘솔에 다음 오류가 표시됩니다.
오류: webpack EmptyContext에서 '//drivers/node-mongodb-native/connection' 모듈을 찾을 수 없습니다.
왜죠? 몽구스는 케르베로스(C++)와 노드집프에 의존하는 몽구스에 의존하기 때문입니다.
음에 대해서, 에서대해.mongo
일부 예외를 만들 수 있습니다(일부 모듈은 에 남깁니다).node_modules
), 할 수 있습니까?모든 것이 아니면 아무것도 아닌 것도 아닙니다.하지만 여전히, 저는 당신이 이 길을 따르고 있는지 확신할 수 없습니다.나는 방금 a를 묶는 것에 성공했습니다.nestjs
어플.개념 증명이었는데, 생산에 들어갈지는 잘 모르겠습니다.그리고 힘들었습니다. 제가 그 과정에서 무언가를 부러뜨렸을 수도 있지만, 언뜻 보기에는 효과가 있습니다.가장 복잡한 부분은.정말 그랬어요.rollup
그리고.babel
부양 가족으로서그리고 앱 코드에서는 어떤 이유로 무조건 호출합니다(운영 중인 UDP noop).어쨌든 이 경로를 따르려면 패키지의 코드를 디버깅/검사할 준비가 되어 있어야 합니다.또한 새 패키지가 프로젝트에 추가될 때 해결 방법을 추가해야 할 수도 있습니다.하지만 모든 것은 당신의 의존도에 달려 있습니다. 저의 경우보다 더 쉬울 수도 있습니다.새로 만든 + 앱의 경우 비교적 간단했습니다.
마지막으로 사용한 구성(기본값보다 우선함):
webpack.config.js
(webpack-5.58.2
,@nestjs/cli-8.1.4
):
const path = require('path');
const MakeOptionalPlugin = require('./make-optional-plugin');
module.exports = (defaultOptions, webpack) => {
return {
externals: {}, // make it not exclude `node_modules`
// https://github.com/nestjs/nest-cli/blob/v7.0.1/lib/compiler/defaults/webpack-defaults.ts#L24
resolve: {
...defaultOptions.resolve,
extensions: [...defaultOptions.resolve.extensions, '.json'], // some packages require json files
// https://unpkg.com/browse/babel-plugin-polyfill-corejs3@0.4.0/core-js-compat/data.js
// https://unpkg.com/browse/core-js-compat@3.19.1/data.json
alias: {
// an issue with rollup plugins
// https://github.com/webpack/enhanced-resolve/issues/319
'@rollup/plugin-json': '/app/node_modules/@rollup/plugin-json/dist/index.js',
'@rollup/plugin-replace': '/app/node_modules/@rollup/plugin-replace/dist/rollup-plugin-replace.cjs.js',
'@rollup/plugin-commonjs': '/app/node_modules/@rollup/plugin-commonjs/dist/index.js',
},
},
module: {
...defaultOptions.module,
rules: [
...defaultOptions.module.rules,
// a context dependency
// https://github.com/RobinBuschmann/sequelize-typescript/blob/v2.1.1/src/sequelize/sequelize/sequelize-service.ts#L51
{test: path.resolve('node_modules/sequelize-typescript/dist/sequelize/sequelize/sequelize-service.js'),
use: [
{loader: path.resolve('rewrite-require-loader.js'),
options: {
search: 'fullPath',
context: {
directory: path.resolve('src'),
useSubdirectories: true,
regExp: '/\\.entity\\.ts$/',
transform: ".replace('/app/src', '.').replace(/$/, '.ts')",
},
}},
]},
// adminjs resolves some files using stack (relative to the requiring module)
// and actually it needs them in the filesystem at runtime
// so you need to leave node_modules/@adminjs/upload
// I failed to find a workaround
// it bundles them to `$prj_root/.adminjs` using `rollup`, probably on production too
// https://github.com/SoftwareBrothers/adminjs-upload/blob/v2.0.1/src/features/upload-file/upload-file.feature.ts#L92-L100
{test: path.resolve('node_modules/@adminjs/upload/build/features/upload-file/upload-file.feature.js'),
use: [
{loader: path.resolve('rewrite-code-loader.js'),
options: {
replacements: [
{search: /adminjs_1\.default\.bundle\('\.\.\/\.\.\/\.\.\/src\/features\/upload-file\/components\/edit'\)/,
replace: "adminjs_1.default.bundle('/app/node_modules/@adminjs/upload/src/features/upload-file/components/edit')"},
{search: /adminjs_1\.default\.bundle\('\.\.\/\.\.\/\.\.\/src\/features\/upload-file\/components\/list'\)/,
replace: "adminjs_1.default.bundle('/app/node_modules/@adminjs/upload/src/features/upload-file/components/list')"},
{search: /adminjs_1\.default\.bundle\('\.\.\/\.\.\/\.\.\/src\/features\/upload-file\/components\/show'\)/,
replace: "adminjs_1.default.bundle('/app/node_modules/@adminjs/upload/src/features/upload-file/components/show')"},
],
}},
]},
// not sure what babel does here
// I made it return standardizedName
// https://github.com/babel/babel/blob/v7.16.4/packages/babel-core/src/config/files/plugins.ts#L100
{test: path.resolve('node_modules/@babel/core/lib/config/files/plugins.js'),
use: [
{loader: path.resolve('rewrite-code-loader.js'),
options: {
replacements: [
{search: /const standardizedName = [^;]+;/,
replace: match => `${match} return standardizedName;`},
],
}},
]},
// a context dependency
// https://github.com/babel/babel/blob/v7.16.4/packages/babel-core/src/config/files/module-types.ts#L51
{test: path.resolve('node_modules/@babel/core/lib/config/files/module-types.js'),
use: [
{loader: path.resolve('rewrite-require-loader.js'),
options: {
search: 'filepath',
context: {
directory: path.resolve('node_modules/@babel'),
useSubdirectories: true,
regExp: '/(preset-env\\/lib\\/index\\.js|preset-react\\/lib\\/index\\.js|preset-typescript\\/lib\\/index\\.js)$/',
transform: ".replace('./node_modules/@babel', '.')",
},
}},
]},
],
},
plugins: [
...defaultOptions.plugins,
// some optional dependencies, like this:
// https://github.com/nestjs/nest/blob/master/packages/core/nest-application.ts#L45-L52
// `webpack` detects optional dependencies when they are in try/catch
// https://github.com/webpack/webpack/blob/main/lib/dependencies/CommonJsImportsParserPlugin.js#L152
new MakeOptionalPlugin([
'@nestjs/websockets/socket-module',
'@nestjs/microservices/microservices-module',
'class-transformer/storage',
'fastify-swagger',
'pg-native',
]),
],
// to have have module names in the bundle, not some numbers
// although numbers are sometimes useful
// not really needed
optimization: {
moduleIds: 'named',
}
};
};
make-optional-plugin.js
:
class MakeOptionalPlugin {
constructor(deps) {
this.deps = deps;
}
apply(compiler) {
compiler.hooks.compilation.tap('HelloCompilationPlugin', compilation => {
compilation.hooks.succeedModule.tap(
'MakeOptionalPlugin', (module) => {
module.dependencies.forEach(d => {
this.deps.forEach(d2 => {
if (d.request == d2)
d.optional = true;
});
});
}
);
});
}
}
module.exports = MakeOptionalPlugin;
rewrite-require-loader.js
:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}
function processFile(source, search, replace) {
const re = `require\\(${escapeRegExp(search)}\\)`;
return source.replace(
new RegExp(re, 'g'),
`require(${replace})`);
}
function processFileContext(source, search, context) {
const re = `require\\(${escapeRegExp(search)}\\)`;
const _d = JSON.stringify(context.directory);
const _us = JSON.stringify(context.useSubdirectories);
const _re = context.regExp;
const _t = context.transform || '';
const r = source.replace(
new RegExp(re, 'g'),
match => `require.context(${_d}, ${_us}, ${_re})(${search}${_t})`);
return r;
}
module.exports = function(source) {
const options = this.getOptions();
return options.context
? processFileContext(source, options.search, options.context)
: processFile(source, options.search, options.replace);
};
rewrite-code-loader.js
:
function processFile(source, search, replace) {
return source.replace(search, replace);
}
module.exports = function(source) {
const options = this.getOptions();
return options.replacements.reduce(
(prv, cur) => {
return prv.replace(cur.search, cur.replace);
},
source);
};
앱을 구축하는 방법은 다음과 같습니다.
$ nest build --webpack
나는 소스 지도를 신경쓰지 않았다, 왜냐하면 그 목표는.nodejs
.
복사 붙여넣기만 하면 되는 구성이 아닙니다. 프로젝트에 필요한 내용을 직접 파악해야 합니다.
여기서 한 가지 더 요령이 있습니다만, 아마 필요 없을 것입니다.
업데이 adminjs
사전 구축된 번들과 함께 제공되므로 이 구성이 훨씬 단순할 수 있습니다.
언급URL : https://stackoverflow.com/questions/59635276/how-to-correctly-build-nestjs-app-for-production-with-node-modules-dependencies
'programing' 카테고리의 다른 글
개체가 유형이 아닌지 확인(!= "IS"에 해당) - C# (0) | 2023.06.08 |
---|---|
스트링 배열을 numpy로 플로트 배열로 변환하는 방법은 무엇입니까? (0) | 2023.06.08 |
Spring Batch ORA-08177: 단일 작업을 실행할 때 이 트랜잭션에 대한 액세스를 직렬화할 수 없습니다, 직렬화된 분리 수준 (0) | 2023.06.08 |
MariaDB 10.1.44 row_number()가 없는 행 번호 (0) | 2023.06.08 |
팬더 병합 - 열 중복 방지 방법 (0) | 2023.06.08 |