Dynamic dependency injection in JS script

I’m working on a system that has two parts:

  • core
  • pluggable modules

The core consists of multiple components such as:

  • sale orders management
  • purchase orders management
  • payment management

Each component has such a structure:

component
|
| model
| route
| static
|| images
|  js
  templates

Core must not depend on modules at all. Modules can depend on core and other modules. In order to invoke modules functionality I use signals. Modules extending routes functionality and models of core components I managed without problem.

The problem is with JS. Majority of front end is built on DataTables and modules are supposed to extend tables as well. That implies extending of HTML template and table definition in JS. So far I’ve managed to do that by rendering JS from Jinja template this way:

order_products.html

{% extends "base.html" %}

{% block scripts %}
{{ super() }}
<script src="dynamic/order_products.js"></script>
{% endblock %}
    
{% block content %}
{{ super() }}
<div class="container-fluid">
    <h1>Order products</h1>
    <table id="order_products" class="table">
        <thead>
            <tr>
                <td></td>
                <td></td>
                <td><input /></td>
                <td><input /></td>
                <td><input /></td>
                <td><input type="date" /></td>
                <td><input /></td>
                <td><input /></td>
                <td></td>
                <td></td>
                <td><select></select></td>
                <td><select></select></td>
                <td></td>
                {% for column in ext_columns %}
                <td></td>
                {% endfor %}
            </tr>
            <tr>
                <th></th>
                <th scope="col" style="width: 1em;">ID</th>
                <th scope="col" style="width: 1em;">Order ID</th>
                <th scope="col">Customer</th>
                <th scope="col" style="width: 2em;">Subcustomer</th>
                <th scope="col" style="width: 1em;">Purchase date</th>
                <th scope="col" style="width: 1em;">Product ID</th>
                <th scope="col">Product name</th>
                <th scope="col">Price</th>
                <th scope="col" style="width: 1em;">Qty</th>
                <th scope="col">Order status</th>
                <th scope="col">OP Status</th>
                <th scope="col">When created</th>
                {% for column in ext_columns %}
                <th scope="col">{{ column.name }}</th>
                {% endfor %}
            </tr>
        </thead>
        <tbody></tbody>
    </table>
</div>
{% endblock %}

order_products.js

function init_order_products_table() {
    var editor = new $.fn.dataTable.Editor({
        ajax: (_method, _url, data, success_callback, error) => {
            for (var order_product_id in data.data) {
                $.ajax({
                    url: '/api/v1/admin/order/product/' + order_product_id,
                    method: 'post',
                    dataType: 'json',
                    contentType: 'application/json',
                    data: JSON.stringify(data.data[order_product_id]),
                    success: server_data => success_callback(({data: [server_data]})),
                    error: error
                });     
            }    
        },
        table: '#order_products',
        idSrc: 'id',
        fields: [
            {label: 'Subcustomer', name: 'subcustomer', type: 'readonly'},
            {label: 'Product ID', name: 'product_id'},
            {label: 'Price', name: 'price'},
            {label: 'Quantity', name: 'quantity'},
            {label: 'Status', name: 'status'},
            {label: 'Private Comment', name: 'private_comment'},
            {label: 'Public Comment', name: 'public_comment'},
            {% for field in extension.fields %}
                {{ field }} ,
            {% endfor %}
        ]
    });
    $('#order_products').on( 'click', 'tbody td.editable', function (e) {
        editor.inline( this);
    } );
    var table = $('#order_products').DataTable({
        dom: 'lrBtip',
        buttons: [
            'print',
            {extend: 'edit', editor: editor},
            { 
                extend: 'collection', 
                text: 'Set status',
                buttons: [ g_order_product_statuses.map(s => {
                    return {
                        extend: 'status',
                        text: s
                    }
                })]
            }
        ],        
        ajax: {
            url: '/api/v1/admin/order/product',
            dataSrc: 'data'
        },
        columns: [
            {
                "className":      'details-control',
                "orderable":      false,
                "data":           null,
                "defaultContent": ''
            },
            {data: 'id'},
            {data: 'order_id', name: 'order_id'},
            {data: 'customer'},
            {data: 'subcustomer', className: 'editable'},
            {data: 'buyout_date'},
            {data: 'product_id', className: 'editable'},
            {data: 'product'},
            {data: 'price', className: 'editable'},
            {data: 'quantity', className: 'editable'},
            {data: 'order_status'},
            {data: 'status'},
            {data: 'when_created'},
            {% for column in extension.columns %}
                {{ column }} ,
            {% endfor %}        ],
        order: [[12, 'desc']],
        keys: {
            columns: '.editable',
            keys: [ 9 ],
            editor: editor,
            editOnFocus: true
        }
});

But I have feeling this approach is not good enough and doesn’t utilize standard patterns, which I believe do exist. So my question is – is there a better way to inject dependency into front end components?