1

In my web application I developed several (vanilla+jQuery) javascript modules. Unfortunately I don't know how to manage their versioning.

The argument of script versioning is very discussed and has different solution as described here for example. Instead in case of modules I didn't found any valid mechanism to version my files.

An example of my code is:

myJsFile.js

import A from './js/A.js';

const a = new A();
a.doStuff();

A.js

export default class A {

    constructor() {}

    doStuff() {
        console.log("I hope to have a version, one day");
    }
}

How can I prevent browsers from using old cached versions of module A?

Timmy
  • 693
  • 2
  • 8
  • 26

2 Answers2

2

You can add a query to the end of the import file:

import A from './js/A.js?version=1.1';

const a = new A();
a.doStuff();

Note: You should then make sure all your imports of the same file across your platform should use the same version number otherwise the module can get imported multiple times. To manage this, you can either do a global find/replace "?version=X" to update all version numbers at once, or create an import map on a global HTML file you include at the head to manage them easier. For example, if you are using PHP, you can add a section to your HTML heads:

<script type="importmap">
    {
        "imports": {
            "moduleA": "./js/A.js?version=<?=VERSION?>",
            "moduleB": "./js/B.js?version=<?=VERSION?>",
            ...
        }
    }
</script>

Then you can import the module as:

import A from 'moduleA';

const a = new A();
a.doStuff();
msawired
  • 91
  • 1
  • 4
1

Since you can only use the import declaration only at the top-level of your script, I would perhaps use instead dynamic import. You can use it just as any other function, only you should keep in mind that it returns a promise.

My solution would be to first create a manifest.json file with all the scripts you want to load like this:

{
  "test2": {
    "path": "./test2.js",
    "version": "1.0.0"
  },
  "test3": {
    "path": "./test3.js",
    "version": "1.0.0"
  }
}

Then, we would create a function that fetches this manifest.json file, always with a new timestamp:

async function fetchManifest() {
  const manifestReq = await fetch('manifest.json?t=' + Date.now());
  if( !manifestReq.ok ) return {};

  const manifest = await manifestReq.json();

  return manifest;
}

After that, we would create a function that simulates the import() syntax, but it would import the file we want with the version number that we specify in the manifest:

async function importWithVersion( moduleName, importDefault = false ) {
  window.manifest = window.manifest || await fetchManifest();
  if( !(moduleName in window.manifest) ) return {};

  const { path, version } = window.manifest[moduleName];

  const moduleRequest = await import( path + '?t=' + version );

  if( importDefault ) {
    return moduleRequest.default;
  }

  return moduleRequest;
}

I added a importDefault parameter so that you could import either the default export or the whole thing. You can learn more about the import operator here

The usage of the importWithVersion function would be like this:

const AModule = await importWithVersion('test2', true);
const A = new AModule();
A.doStuff();

Or if you want to import all the functions of a file:

const BModule = await importWithVersion('test3');
BModule.sayHi();
BModule.helloPerson('Peter');

test3.js

export function sayHi() {
  console.log('Hello!')
}

export function helloPerson( person ) {
  console.log(`Hello ${person}! How are you doing today?`);
}
L.Raudel
  • 52
  • 3