# Adding a new client library

In the past some libraries have been copy-pasted (and adapted if needed) to the repository. However, new libraries must be obtained exclusively through npm.

The first step is to install the desired library. As an example we are going to install `i18next`:

    npm i i18next

### Step 1. Understanding the structure of the import

After installing the dependency, it's important to know how it's structured. You can do this by looking at the directory structure of the newly imported dependency:

    $ tree node_modules/i18next
    node_modules/i18next
    ├── dist
    │   ├── cjs
    │   │   └── i18next.js
    │   ├── esm
    │   │   ├── i18next.bundled.js
    │   │   ├── i18next.js
    │   │   └── package.json
    │   └── umd
    │       ├── i18next.js
    │       └── i18next.min.js
    ├── i18next.js
    ├── i18next.min.js
    ├── index.d.mts
    ├── index.d.ts
    ├── index.js
    ├── index.v4.d.ts
    ├── LICENSE
    ├── package.json
    ├── README.md
    └── typescript
        ├── helpers.d.ts
        ├── options.d.ts
        ├── t.d.ts
        └── t.v4.d.ts

Generally you should be looking for a `.min.js` file. Note that the `esm` and `cjs` variants generally don't work, we are looking for the classic, no module dependency.

### Step 2. Exposing the library from the server

The library must be delivered by the server and this is done via `src/routes/assets.ts`. In the `register` function, add a new entry near the bottom of the function:

    app.use(`/${assetPath}/node_modules/i18next/`, persistentCacheStatic(path.join(srcRoot, "..", 'node_modules/i18next/')));

### Step 3. Adding it to the library loader

The library loader is a client module which is in charge of downloading the library from the server and importing it. The loader is located in `src/public/app/services/library_loader.js`.

To add a new library, start by creating a constant for it, with the value pointing to the minified JS identified at the first step:

    const I18NEXT = {
        js: [
            "node_modules/i18next/i18next.min.js"
        ]
    };

Then add it to the `export default` section:

     export default {
         requireCss,
         requireLibrary,
         CKEDITOR,
         CODE_MIRROR,
         ESLINT,
         RELATION_MAP,
         PRINT_THIS,
         CALENDAR_WIDGET,
         KATEX,
         WHEEL_ZOOM,
         FORCE_GRAPH,
         MERMAID,
         EXCALIDRAW,
    -    MARKJS
    +    MARKJS,
    +    I18NEXT
     }

### Step 4. Using the library

To import the library, simply use the following mechanism:

    import library_loader from "./library_loader.js";

    await library_loader.requireLibrary(library_loader.I18NEXT);

Make sure to replace `I18NEXT` with the library that was created at the previous steps.

Note that because we are not using a module management mechanism such as ES Modules or Common.js modules, the `requireLibrary` method does not actually return anything.

To benefit from the library, it must export on its own an object in `window`.

In the case of `i18next`, it sets `window.i18next` and that can be used directly:

    i18next.init({});

### Step 5. Adding Electron support

For Electron, the `node_modules` are copied as a separate step by `bin/copy-dist.ts`.

Scroll all the way down to the `nodeModulesFolder` and append an entry for the newly added libraries.