How to build a multi package node module with some optional modules

1.7k Views Asked by At

I am trying since several days to build a node module which will be used by a react application.

Today my module is packaged as a big javascript file packaged with webpack. Some part of this node module is optional and is only needed if we want to use certain features (activated through config). In order to avoid loading unecessary part of the code, I have tried first to use chunking and dynamic import as explained by webpack but this has not worked: chunks are created and packaged in the dist/ folder but i never succeed to run the dynamic import in the node module when called from the react app. The application complains that the chunk cannot be loaded from the app. This make sense for me as this webpack feature was more built to load dynamically part of the application and not as an internal mechanism for a node module dependency to load code on demand (but i could be wrong).

I looked at other projects like babel or react-router where the module is split in several package using lerna and yarn package. So i have tried to build the library with lerna with different packages:

my-library
  core/
    src/
      index.js
  another-package/
    src/
      index.js

I want the index.js file from core to call a method from index.jsfrom another-package only if needed and only if the node-module @my-library/another-package was installed. But i never found the solution to do it. Is it possible to achieve this with ES5/ES6 with webpack/lerna or did i took the wrong approach?

UPDATE October 27TH

So after several tests, I was able to use Aram solution with a plain HTML/JS (https://github.com/PixelDuck/lerna-webpack/blob/main/a-react-app/src/client/test.html) but the solution is not working with a react app package with webpack https://github.com/PixelDuck/lerna-webpack/blob/main/a-react-app/src/client/App.js.

The code is available there: https://github.com/PixelDuck/lerna-webpack.

Open a terminal to my-lerna-library and run yarn install yarn link:all, this will create symbolic link for each packages `yarn dev', this will create bundle and listen to changes

then open a new terminal to folder a-react-app: yarn install yarn link "my-lerna-library" yarn link "@my-lerna-library/another-package" `yarn dev``

a page will be open on http://0.0.0.0:3000 and you will see that module @my-lerna-library/another-package is not found.

If you open http://0.0.0.0:5000/test.html the plainJS test, everything is looking fine.

It seems that the issue is on the webpack side for the react app because when debugging the application i can see that the core package is looking to a library name my_lerna_library__WEBPACK_IMPORTED_MODULE_3__ which is not the one used by webpack when loading the other package _my_lerna_library_another_package__WEBPACK_IMPORTED_MODULE_2___default

__webpack_require__.r(__webpack_exports__);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "./node_modules/react/index.js");
/* harmony import */ var _App_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./App.css */ "./src/client/App.css");
/* harmony import */ var _App_css__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_App_css__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var _my_lerna_library_another_package__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @my-lerna-library/another-package */ "../my-lerna-library/packages/another-package/dist/index.bundle.js");
/* harmony import */ var _my_lerna_library_another_package__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_my_lerna_library_another_package__WEBPACK_IMPORTED_MODULE_2__);
/* harmony import */ var my_lerna_library__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! my-lerna-library */ "../my-lerna-library/packages/core/dist/index.bundle.js");
/* harmony import */ var my_lerna_library__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(my_lerna_library__WEBPACK_IMPORTED_MODULE_3__);

3

There are 3 best solutions below

0
On BEST ANSWER

Finally i was able to achieve what i want. Code here: https://github.com/PixelDuck/lerna-webpack

So the idea was to set another-package webpack config to output library as 'global' and on core package, looking at this global variable with

function isMyLernaLibraryAnotherPackageDefined() {
  return typeof myLernaLibraryAnotherPackage !== 'undefined';
}
testFromAnotherPackage() {
  if (isMyLernaLibraryAnotherPackageDefined())
    return new myLernaLibraryAnotherPackage.AnotherClass().test();
}

Now on the react app, if i am importing import '@my-lerna-library/another-package';then message and svg are displayed. If i am commenting this line, module is not found and nothing is displayed

3
On

If you need a package with optional dependencies you can create a core package together with several feature packages (which are not even listed in peerDependencies). You may explicitly list those feature packages in optionalDependencies.

Also, you core package should be designed so that it does not necessary require feature packages but can correctly work with them if they're installed. For example this way https://stackoverflow.com/a/50841764/14451484.

2
On

In your my-lerna-library > package:-

(1) another-package package.json file replace with this code

{
  "name": "@my-lerna-library/another-package",
  "private": true,
  "version": "1.0.0",
  "scripts": {
    "dev": "webpack --watch --devtool inline-source-map --mode development",
    "build": "webpack --mode production"
  },
  "dependencies": {
    "@my-lerna-library/core": "1.0.0"
  }
}

and

(2) core package.json file replace with this code

{
  "name": "@my-lerna-library/core",
  "private": true,
  "version": "1.0.0",
  "scripts": {
    "dev": "webpack --watch --devtool inline-source-map --mode development",
    "build": "webpack --mode production"
  }
}

and my-lerna-library package.json file replace workspaces like this,

"workspaces": {
    "packages": [
      "packages/**"
    ]
},

Then run command "yarn run dev". I thing this is work for you!!