Nikaple / nest-typed-config

Intuitive, type-safe configuration module for Nest framework ✨
MIT License
207 stars 25 forks source link

Unable to bundle `nest-typed-config` with webpack #499

Open jcwillox opened 10 months ago

jcwillox commented 10 months ago

I'm submitting a...

Current behavior

The app fails to start when bundling with webpack, even if you exclude class-validator from bundling.

Webpack copies the https://github.com/Nikaple/nest-typed-config/blob/main/lib/utils/imports.util.ts directly into the bundle, except it tries to replace the require call but can't resolve the dependency, so instead replaces it with an empty context, which causes the requireFromRootNodeModules to throw an exception when starting.

/***/ }),
/* 1368 */
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {

"use strict";

Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.plainToClass = exports.validateSync = void 0;
const requireFromRootNodeModules = (moduleName) => {
    const modulePath = __webpack_require__(1369).resolve(moduleName, { paths: ['../..', '.'] });
    // eslint-disable-next-line @typescript-eslint/no-var-requires
    return __webpack_require__(1369)(modulePath);
};
exports.validateSync = requireFromRootNodeModules('class-validator').validateSync;
exports.plainToClass = requireFromRootNodeModules('class-transformer').plainToClass;
//# sourceMappingURL=imports.util.js.map

/***/ }),
/* 1369 */
/***/ ((module) => {

function webpackEmptyContext(req) {
    var e = new Error("Cannot find module '" + req + "'");
    e.code = 'MODULE_NOT_FOUND';
    throw e;
}
webpackEmptyContext.keys = () => ([]);
webpackEmptyContext.resolve = webpackEmptyContext;
webpackEmptyContext.id = 1369;
module.exports = webpackEmptyContext;

I created a basic patch that looks to fix the issue

diff --git a/dist/loader/dotenv-loader.js b/dist/loader/dotenv-loader.js
index 2bd4f53600790a2626f6469070c8c20f880ea6bf..142087986e787d65b4b1f5d269e435d0394c2398 100644
--- a/dist/loader/dotenv-loader.js
+++ b/dist/loader/dotenv-loader.js
@@ -40,7 +40,7 @@ const debug_util_1 = require("../utils/debug.util");
 let dotenv;
 let dotenvExpand;
 const loadEnvFile = (options) => {
-    dotenv = (0, load_package_util_1.loadPackage)('dotenv', 'dotenvLoader');
+    dotenv = require('dotenv');
     const envFilePaths = Array.isArray(options.envFilePath)
         ? options.envFilePath
         : [options.envFilePath || (0, path_1.resolve)(process.cwd(), '.env')];
diff --git a/dist/utils/imports.util.js b/dist/utils/imports.util.js
index 6ba2c6d4bca3e4ecc0003881494f2c21452c7a8b..cda6abecb57a8b2fb4e0e31222318a714a166ba9 100644
--- a/dist/utils/imports.util.js
+++ b/dist/utils/imports.util.js
@@ -6,6 +6,16 @@ const requireFromRootNodeModules = (moduleName) => {
     // eslint-disable-next-line @typescript-eslint/no-var-requires
     return require(modulePath);
 };
-exports.validateSync = requireFromRootNodeModules('class-validator').validateSync;
-exports.plainToClass = requireFromRootNodeModules('class-transformer').plainToClass;
+
+try {
+    exports.validateSync = requireFromRootNodeModules('class-validator').validateSync;
+} catch {
+    exports.validateSync = require('class-validator').validateSync;
+}
+
+try {
+    exports.plainToClass = requireFromRootNodeModules('class-transformer').plainToClass;
+} catch {
+    exports.plainToClass = require('class-transformer').plainToClass;
+}
 //# sourceMappingURL=imports.util.js.map

Besides the requireFromRootNodeModules, the loadPackage function also causes issues with webpacks resolution, while it's a convenient shorthand i'd suggest inlining it instead to avoid the issue.

try {
  dotenv = require('dotenv');
} catch (e) {
  console.error(MISSING_REQUIRED_DEPENDENCY('dotenv', 'dotenvLoader'));
  process.exit(1);
}

You could still have some sort of utility function if you want, like

dotenv = loadPackage('dotenv', 'dotenvLoader', () => require('dotenv'));

const MISSING_REQUIRED_DEPENDENCY = (name: string, reason: string) =>
  `The "${name}" package is missing. Please, make sure to install this library ($ npm install ${name}) to take advantage of ${reason}.`;

export function loadPackage(packageName: string, context: string, requireFn): any {
  try {
    return requireFn()
  } catch (e) {
    console.error(MISSING_REQUIRED_DEPENDENCY(packageName, context));
    process.exit(1);
  }
}

Expected behavior

Support being bundled

Minimal reproduction of the problem with instructions

Set externals: [] in your webpack config

What is the motivation / use case for changing the behavior?

Environment


Nest version: 10.3.1
Nest-Typed-Config version: 2.9.2


For Tooling issues:
- Node version: 20  
- Platform: Windows 

Others:

Nikaple commented 10 months ago

Thanks a bunch for pointing out this issue and suggesting a cool fix! I appreciate your help in making the library better. Your solution looks like a great way to tackle the problem, and I'd be stoked if you could submit a pull request for it 😃