0
\$\begingroup\$

Background

I'm writing a server-side-rendering (i.e. "traditional") web-application that uses a dependency-free client-side components library (specifically, Syncfusion Essential JS 2). Ostensibly the library is designed to be modular and to work well with SystemJS in particular - this is good because normally a Webpack-assembled and minified index.js containing only their scripts is over 7MB in size (!!).

Their documentation for their Grid component (an interactive HTML table, i.e. a data-grid) provides an example system.config.js file for System.JS and another page lists optional injectable modules.

I built a web-page to test how it all works and it looks like this:

index.html

<htmll>
<head>
    <!-- title, meta, stylesheets, etc -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/3.0.0/system.min.js" type="text/javascript"></script>
    <script src="system.config.js" type="text/javascript"></script>
</head>
<body>
    <div id="Grid"></div>
</body>
</html>

system.config.js

System.config({
    paths: {
        'npm:'       : './node_modules/',
        'syncfusion:': 'npm:@syncfusion/'
    },
    map: {
        app: 'app',

        //Syncfusion packages mapping
        "@syncfusion/ej2-base"        : "syncfusion:ej2-base/dist/ej2-base.umd.min.js",
        "@syncfusion/ej2-data"        : "syncfusion:ej2-data/dist/ej2-data.umd.min.js",
        "@syncfusion/ej2-buttons"     : "syncfusion:ej2-buttons/dist/ej2-buttons.umd.min.js",
        "@syncfusion/ej2-splitbuttons": "syncfusion:ej2-splitbuttons/dist/ej2-splitbuttons.umd.min.js",
        "@syncfusion/ej2-popups"      : "syncfusion:ej2-popups/dist/ej2-popups.umd.min.js",
        "@syncfusion/ej2-navigations" : "syncfusion:ej2-navigations/dist/ej2-navigations.umd.min.js",
        "@syncfusion/ej2-inputs"      : "syncfusion:ej2-inputs/dist/ej2-inputs.umd.min.js",
        "@syncfusion/ej2-dropdowns"   : "syncfusion:ej2-dropdowns/dist/ej2-dropdowns.umd.min.js",
        "@syncfusion/ej2-calendars"   : "syncfusion:ej2-calendars/dist/ej2-calendars.umd.min.js",
        "@syncfusion/ej2-lists"       : "syncfusion:ej2-lists/dist/ej2-lists.umd.min.js",
        "@syncfusion/ej2-grids"       : "syncfusion:ej2-grids/dist/ej2-grids.umd.min.js",
        "@syncfusion/ej2-excel-export": "syncfusion:ej2-excel-export/dist/ej2-excel-export.umd.min.js",
        "@syncfusion/ej2-pdf-export"  : "syncfusion:ej2-pdf-export/dist/ej2-pdf-export.umd.min.js",
        "@syncfusion/ej2-file-utils"  : "syncfusion:ej2-file-utils/dist/ej2-file-utils.umd.min.js",
        "@syncfusion/ej2-compression" : "syncfusion:ej2-compression/dist/ej2-compression.umd.min.js"
    },
    packages: {
        'app': { main: 'app', defaultExtension: 'js' }
    }
});

System.import('app');

app.ts

(Right from Syncfusion's own sample code)

import { Grid } from '@syncfusion/ej2-grids';
import { data } from './datasource';

let grid: Grid = new Grid({
    dataSource: data,
    columns: [
                { field: 'OrderID', headerText: 'Order ID', textAlign: 'Right', width: 120, type: 'number' },
                { field: 'CustomerID', width: 140, headerText: 'Customer ID', type: 'string' },
                { field: 'Freight', headerText: 'Freight', textAlign: 'Right', width: 120, format: 'C' },
                { field: 'OrderDate', headerText: 'Order Date', width: 140, format: 'yMd' }
    ],
    height: 315
});

grid.appendTo('#Grid');

Note that my app.ts (and transpiled app.js file) do not include any of the optional Grid modules like PdfExport and ExcelExport.

However, when I used this configuration I checked the network tab in my browser's Dev Tools window and I saw that my browser was requesting the optional module files for PdfExport and ExcelExport and others - in all, almost 2MB of additional script downloads. Not good.

I found a workaround by blocking certain dependencies in the system.config.js file, so now my system.config.js file looks like this:

system.config.js - version 2:

System.config({
    paths: {
        'npm:'       : './node_modules/',
        'syncfusion:': 'npm:@syncfusion/'
    },
    map: {
        app: 'app',

        //Syncfusion packages mapping
        "@syncfusion/ej2-base"        : "syncfusion:ej2-base/dist/ej2-base.umd.min.js",
        "@syncfusion/ej2-data"        : "syncfusion:ej2-data/dist/ej2-data.umd.min.js",
        "@syncfusion/ej2-popups"      : "syncfusion:ej2-popups/dist/ej2-popups.umd.min.js",
        "@syncfusion/ej2-grids"       : "syncfusion:ej2-grids/dist/ej2-grids.umd.min.js",

        // Disable packages we don't want to use: https://stackoverflow.com/questions/37403990/how-to-tell-systemjs-to-ignore-an-import
        "@syncfusion/ej2-buttons"     : "@empty",
        "@syncfusion/ej2-splitbuttons": "@empty",
        "@syncfusion/ej2-navigations" : "@empty",
        "@syncfusion/ej2-inputs"      : "@empty",
        "@syncfusion/ej2-dropdowns"   : "@empty",
        "@syncfusion/ej2-calendars"   : "@empty",
        "@syncfusion/ej2-lists"       : "@empty",
        "@syncfusion/ej2-excel-export": "@empty",
        "@syncfusion/ej2-pdf-export"  : "@empty",
        "@syncfusion/ej2-file-utils"  : "@empty"
    },
    packages: {
        'app': { main: 'app', defaultExtension: 'js' }
    }
});

System.import('app');

And this works! I don't get any errors about any necessary dependencies missing and the browser doesn't download script files that aren't being used.

Unfortunately this affects all pages that use this system.config.js file - if I have a webpage that should use PdfExport then that page will need to use a different system.config.js file that does not use "@empty" to block it.

...and this got me thinking, if each page has a potentially different SystemJS configuration, why not have a per-page system.config.js? In fact, including the SystemJS configuration directly in the HTML would eliminate another HTTP request.

My solution

So now my index.html looks like this:

<htmll>
<head>
    <!-- title, meta, stylesheets, etc -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/3.0.0/system.min.js" type="text/javascript" onload="onSystemJSLoaded"></script>
    <script type="text/javascript">
// onSystemJSLoaded invoked via onload="" attribute above.
function onSystemJSLoaded() {
    System.config({
        paths: {
            'npm:'       : './node_modules/',
            'syncfusion:': 'npm:@syncfusion/'
        },
        map: {
            app: 'app',

            //Syncfusion packages mapping
            "@syncfusion/ej2-base"        : "syncfusion:ej2-base/dist/ej2-base.umd.min.js",
            "@syncfusion/ej2-data"        : "syncfusion:ej2-data/dist/ej2-data.umd.min.js",
            "@syncfusion/ej2-popups"      : "syncfusion:ej2-popups/dist/ej2-popups.umd.min.js",
            "@syncfusion/ej2-grids"       : "syncfusion:ej2-grids/dist/ej2-grids.umd.min.js",

            // Disable packages we don't want to use: https://stackoverflow.com/questions/37403990/how-to-tell-systemjs-to-ignore-an-import
            "@syncfusion/ej2-buttons"     : "@empty",
            "@syncfusion/ej2-splitbuttons": "@empty",
            "@syncfusion/ej2-navigations" : "@empty",
            "@syncfusion/ej2-inputs"      : "@empty",
            "@syncfusion/ej2-dropdowns"   : "@empty",
            "@syncfusion/ej2-calendars"   : "@empty",
            "@syncfusion/ej2-lists"       : "@empty",
            "@syncfusion/ej2-excel-export": "@empty",
            "@syncfusion/ej2-pdf-export"  : "@empty",
            "@syncfusion/ej2-file-utils"  : "@empty"
        },
        packages: {
            'app': { main: 'app', defaultExtension: 'js' }
        }
    });

    System.import('app');
}
    </script>
</head>
<body>
    <div id="Grid"></div>
</body>
</html>

So if a page does need to use PdfExport or some other optional module I can provide the path to the file instead of using @empty.

Or am I doing it wrong?

I haven't seen anyone else using this technique elsewhere, which makes me worried I'm doing something wrong - especially because it feels like I'm fighting with SystemJS and Syncfusion's library instead of falling into the pit-of-success. Granted, if Syncfusion's library didn't pull-in modules that weren't being used then I wouldn't need to do anything, but I'm unable to find any guidance online regarding optional vs required dependencies in libraries, and I only found out about "@empty" by finding this SO question after a lucky Google search: https://stackoverflow.com/questions/37403990/how-to-tell-systemjs-to-ignore-an-import

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.