golgi

Golgi Server-Side Build and Optimisation Tools

Index

Background

The Golgi repository includes a number of useful optional server-side tools that server two specific purposes:

The tools have been written using JavaScript/Node.js and can be executed in one of two ways:

NOTES:

Installing

Copy the server-side-tools Folder

To make use of the server-side tools, you should create a directory on the Linux machine on which you want to build and maintain your Golgi Assemblies and Components. This does not have to be your web-server machine. For example:

    mkdir ~/golgi-sst

Then copy the contents of the Golgi repository /server-side-tools folder into your directory.

Install Node.js or Docker

Building Components

Create Source and Destination Folders

Create a folder for your source components, and another for the generated versions that will be created by the Golgi server-side tools, eg:

    mkdir ~/components_src
    mkdir ~/components

The Golgi Component Source Template

Within the source components directory, you create each of your Golgi Components using the following template:

    let def = {
      name: '-',
      html: `
        
      `,
        methods: `
          
      `
    };
    export {def};

Note the use of the back-tick character to surround your Component’s HTML and methods

Example Using the Template

For example, let’s take one of the simple Components from the Conduit example in this repository components (ie from the sbadmin namespace) that is used in the examples within this repository. This is used to display a tag:

    let def = {
      name: 'conduit-tag',
      html: `
    <a class="tag-pill tag-default" href="#">golgi:bind</a>
      `,
      methods: `
        setState(state) {
          if (state.name) {
            this.name = state.name;
          }
        }
      `
    };
    export {def};

Save this as conduit-tag.mjs.

Note that the file extension for your source Component files must be .mjs rather than .js.

Compile/Build Your Golgi Components

Once you’ve created your source Component definitions using the template above, you can compile/build the actual WebComponents that they represent.

Native Node.js

If you’re using Node.js natively:

Using the Node-Runner Docker Container

If you’re using Docker:

Running the Build Script

Whether you run the scripts natively in Node.js or using Docker, you’ll see the same dialogue:

You’ll be asked for the directory path for your source component definitions and the directory path into which the compiled/generated WebComponents are to be saved. It will suggest folders within the current tools directory, but you supply your own directory names. Note:

For example:

    $ npm run build_components
    
    > golgi-sst@0.1.1 build_components
    > node buildComponents.js
    
    
    This will create Golgi WebComponent files from all source files in a folder

    Source folder containing Golgi Component Definition Files (./components_src)
    
    Destination folder for optimised WebComponent files (./components)

Examine the Generated WebComponents

Look in the destination components directory and you should now see a corresponding file for each of your source definition files, eg for our example above, you’ll find:

    ~/components/conduit-tag.js

Note that the file extension for the generated files is .js.

Its contents will look like this:

    function load(){let t="conduit-tag",e=-1;customElements.define(
    t,class extends HTMLElement{constructor(){super(),e++;this.html
    ='<a class="tag-pill tag-default" href="#">golgi:bind</a>',this.
    name=t+"-"+e}setState(t){t.name&&(this.name=t.name)}})}export{load};

You’ll see that the builder has minified the generated WebComponent code.

Take a further look in the directory and you’ll find an additional file named golgi-components.js. This is a single JavaScript module file that contains the minimised generated WebComponent code for all your Components. It will look something like this:

    let golgi_components = [];
      ... etc
    golgi_components.push(function load(){let t="conduit-tag",e=-1;
    customElements.define(t,class extends HTMLElement{constructor()
    {super(),e++;this.html='<a class="tag-pill tag-default" href="#
    ">golgi:bind</a>',this.name=t+"-"+e}setState(t){t.name&&(this.n
    ame=t.name)}})});
    export {golgi_components}

Optimising Assemblies

Create Source and Destination Folders

Create a folder for your source Assembly files, and another for the generated versions that will be created by the Golgi server-side tools, eg:

    mkdir ~/assemblies_src
    mkdir ~/assemblies

If you’ve already created one or more Golgi Assembly files, copy them into the source folder.

It’s usually a good idea to have previously tested and debugged your Assembly files using their full source code, as the optimised versions will be minified and much more difficult to debug.

You must change the file extension of all your Assembly files to .mjs.

You’ll find that the Golgi tools directory includes a handy bash script file called rename. You can use this to automate the process. Copy it into your source assembly folder and change its permissions to make it executable.

Example Source Assembly

Here’s an example of a simple Golgi Assembly file (taken from the /examples directory in this repository):

    export function load(ctx) {
      const gx=`
        <conduit-content-page name="profile">
          <conduit-profile />
        </conduit-content-page>
      `;
      return {gx};
    };

This would be saved in the source Assembly folder as ~/assemblies/profile.mjs.

Optimise Your Golgi Assemblies

You can now generate optimised versions of your Assemblies

Native Node.js

If you’re using Node.js natively:

Using the Node-Runner Docker Container

If you’re using Docker:

Running the Optimisation Script

Whether you run the scripts natively in Node.js or using Docker, you’ll see the same dialogue:

You’ll be asked for the directory path for your source Assembly files and the directory path into which the optimised versions are to be saved. It will suggest folders within the current tools directory, but you can supply your own directory names. Note:

For example:

    $ npm run optimise_assemblies
    
    > golgi-ssr@0.1.1 optimise_assemblies
    > node optimiseAssemblies.js
    
    
    This will create optimised Assembly files from all standard Golgi Assembly source files in a folder
    
    Source folder containing Golgi Assembly Files (./assemblies_src)
    
    Destination folder for optimised output files (./assemblies)

Examine the Optimised Assemblies

Look in the destination Assemblies directory and you should now see a corresponding file for each of your source Assembly files, eg for our example above, you’ll find:

    ~/assemblies/profile.js

It would now look like this in the destination folder:

    function load(e){return{gjson:{componentName:"conduit-content-page",
    state:{name:"profile"},children:[{componentName:"conduit-profile",
    state:{},assemblyName:"profile"}],assemblyName:"profile"}}}export{load};

You’ll notice that two things have happened:

Take a further look in the directory and you’ll find an additional file named golgi-assemblies.js. This is a single JavaScript module file that contains the minimised and optimised code for all your Assemblies. It will look something like this:

    let golgi_assemblies = [];
    golgi_assemblies.push({name:'profile',code:function load(e){
    return{gjson:{componentName:"conduit-content-page",state:{
    name:"profile"},children:[{componentName:"conduit-profile",
    state:{},assemblyName:"profile"}],assemblyName:"profile"}}}});
    export {golgi_assemblies}

Using the Optimised Components and Assemblies

Copy the Generated Files to your Web Server

You can now copy the generated Component and Assembly files to their corresponding directories on your Web Server.

Make sure you also copy the golgi-components.js and golgi-assemblies.js files to your Web Server.

If you now try loading the application in a browser, you’ll see that the optimised versions of the Assemblies and Components will be loaded. This will save some download time and reducing processing overheads within the browser, so you should see a bit of an improvement in initial load times.

Significantly Increasing Initial Load Times

You can make a significant improvement in Golgi application start-up/initial load times by making use of those composite generated files: golgi-components.js and golgi-assemblies.js.

To make use of them, modify the app.js file for your Golgi application as follows.

Let’s say your app.js file looked like this (taken from the /examples):

    (async () => {
      let context = {
        componentPaths: {
          conduit: '../examples/conduit-opt/components/conduit/',
        },
        assemblyPath: '../examples/conduit-opt/assemblies/'
      };
      const {golgi} = await import('../../src/golgi.min.js');
      golgi.renderAssembly('root_assembly', 'body', context);
    })();

Simply change it to this:

    (async () => {
      let context = {
        componentPaths: {
          conduit: '../examples/conduit-opt/components/conduit/',
        },
        assemblyPath: '../examples/conduit-opt/assemblies/'
      };
      const {golgi} = await import('../../src/golgi.min.js');

      golgi.fetch_optimised_components('conduit', context);
      golgi.fetch_optimised_assemblies(context);

      golgi.on('assembliesLoaded', async function() {
        golgi.renderAssembly('root_assembly', 'body', context);
      });

    })();

This will now pre-fetch the composite optimised Components and Assembly files containing all your application’s assemblies and Components in just two minified files. This will significantly reduce the file download overhead, and it also means that by the time your Golgi application needs to use a WebComponent, it will already be loaded and ready to use.

It still takes a finite amount of time for Golgi to download and process your Assemblies, particularly if you have a lot of them, so you’ll notice in the example above that the Root Assembly isn’t loaded until the fetch_optimised_assemblies method has triggered an assembliesLoaded event:

      golgi.on('assembliesLoaded', async function() {
        golgi.renderAssembly('root_assembly', 'body', context);
      });

Once this event is emitted, Golgi can safely render the Root Assembly, but at that stage, all the pre-optimised Assemblies and minified WebComponents needed for your application are ready for use, so you won’t experience any further delays due to downloads or processing.


Further Potential Optimisations

You may find that you can make further optimisations to enhance the initial load time for your Golgi application by modifying the way you load other resources such as script files and CSS files.

Take a look at this example (actually from our own web site):

You’ll notice that we’re also using the index.html file to pre-load the various modules that are used in the app.js file, eg:

        <script type="module" src="/js/golgi.min.js"></script>
        <script type="module" src="/js/assemblies/golgi-assemblies.js"></script>
        <script type="module" src="/js/components/sbadmin/golgi-components.js"></script>

This ensures that by the time the app.js is loaded and begins executing, it doesn’t have to wait any further to download these other resources: they should already be available.

By performing these additional, simple optimisation steps, you should see very significant initial load times. We’ve been able to achieve full 100% Lighthouse performance values for Golgi applications, demonstrating just how performant a fully WebComponent-based application can be.