Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Oskari 3.0 bundle lazy-loader #2792

Merged
merged 3 commits into from
Feb 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,32 @@ Removed bundles:
- `catalogue/metadatacatalogue` replaced by `catalogue/metadatasearch`
- `catalogue/metadataflyout` replaced by `catalogue/metadata`

### Changes for bundle registrations

In preparation of removing the packages-folder from oskari-frontend and migrating any bundle.js files under it to bundles-folder as index.js files.


New loaders for application main.js usage:
- `oskari-bundle` replaces oskari-loader and supports more streamlined bundle registrations
- `oskari-lazy-bundle` replaces oskari-lazy-loader for adding support to lazy-load bundles with the streamlined bundle registration

This allows linking bundles like this:
```
import 'oskari-bundle!oskari-frontend/bundles/admin/admin';
```
instead of:
```
import 'oskari-loader!oskari-frontend/packages/admin/bundle/admin/bundle.js';
```
and removes the unnecessary complication that comes with the packages-folder.

These changes shouldn't really affect your app, but things that have changed inside the "engine":

- New core component `src/BundleRegister` for managing bundles and exposes Oskari.bundle() (previously part of src/loader.js) and Oskari.lazyBundle() functions.
- Oskari.bundle_manager functions removed:
- registerDynamic() - replaced by BundleRegister.lazyBundle() that is exposed as Oskari.lazyBundle()
- loadDynamic() - moved to src/loader.js as private function as it doesn't need to be exposed

## 2.14.2

For a full list of changes see:
Expand Down
34 changes: 34 additions & 0 deletions src/BundleRegister.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const _bundleRegistry = {};
const _availableLazyBundles = {};

export const BundleRegister = {
/**
* @method bundle
* Register bundle that is loaded and available for starting
*
* @param {string} bundlename Bundle name
* @param {Function} factory function returns a bundle instance
*/
bundle: (bundleId, value) => {
if (value) {
_bundleRegistry[bundleId] = value;
}
return _bundleRegistry[bundleId];
},
/**
* @method lazyBundle
* Register bundle for lazy-loading/run-time loading with ES import()
*
* @param {string} bundlename Bundle name
* @param {Function} loader function that returns an promise that resolve to the module to be loaded
*/
lazyBundle: (bundleId, loader) => {
if (loader) {
if (!_availableLazyBundles[bundleId]) {
_availableLazyBundles[bundleId] = [];
}
_availableLazyBundles[bundleId].push(loader);
}
return _availableLazyBundles[bundleId];
}
};
68 changes: 19 additions & 49 deletions src/bundle_manager.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,27 @@

(function (o) {
if (!o || !o.clazz) {
// can't add loader if no Oskari ref
return;
}
var log = Oskari.log('Oskari.BundleManager');
const log = Oskari.log('Oskari.BundleManager');
/**
* singleton instance of the class system
*/
var cs = o.clazz;
const cs = o.clazz;

/* legacy Bundle_manager */

/**
* @singleton @class Oskari.Bundle_manager
*/
var BundleManager = function () {
const BundleManager = function () {
this.clazz = o.clazz;
var me = this;
const me = this;
me.serial = 0;
me.bundleDefinitions = {};
me.sources = {};
me.bundleInstances = {};
me.bundles = {};
me.dynamicLoaders = {};

/*
* CACHE for lookups state management
Expand Down Expand Up @@ -55,8 +53,8 @@
* @private @method _purge
*/
_purge: function () {
var p;
var me = this;
let p;
const me = this;

for (p in me.sources) {
if (me.sources.hasOwnProperty(p)) {
Expand Down Expand Up @@ -88,8 +86,8 @@
*
*/
_install: function (biid, bundleDefinition, srcFiles, bundleMetadata) {
var me = this;
var defState = me.bundleDefinitionStates[biid];
const me = this;
let defState = me.bundleDefinitionStates[biid];

if (defState) {
defState.state = 1;
Expand Down Expand Up @@ -120,11 +118,11 @@
*
*/
installBundleClass: function (biid, className) {
var clazz = Oskari.clazz.create(className);
const clazz = Oskari.clazz.create(className);
if (clazz) {
// Oskari.bundle is the new registry for requirejs loader
Oskari.bundle(biid, {
clazz: clazz,
clazz,
metadata: cs.getMetadata(className)
});
}
Expand All @@ -141,10 +139,10 @@
* @return {Object} Bundle
*/
createBundle: function (biid, bid) {
var bundle;
var bundleDefinition;
var me = this;
var bundleDefinitionState;
const me = this;
let bundle;
let bundleDefinition;
let bundleDefinitionState;

if (biid === null || biid === undefined) {
throw new TypeError('createBundle(): Missing biid');
Expand Down Expand Up @@ -193,10 +191,10 @@
// creates a bundle_instance
// any configuration and setup IS BUNDLE / BUNDLE INSTANCE specific
// create / config / start / process / stop / destroy ...
var me = this;
var bundle;
var bundleInstance;
var bundleInstanceId;
const me = this;
let bundle;
let bundleInstance;
let bundleInstanceId;

if (bid === null || bid === undefined) {
throw new TypeError('createInstance(): Missing bid');
Expand Down Expand Up @@ -244,7 +242,7 @@
* @return
*/
_destroyInstance: function (biid) {
var bundleInstance;
let bundleInstance;

if (biid === null || biid === undefined) {
throw new TypeError('_destroyInstance(): Missing biid');
Expand All @@ -255,34 +253,6 @@
bundleInstance = null;

return bundleInstance;
},
/**
* @method registerDynamic
* Register bundle for run-time loading with ES import()
*
* @param {string} bundlename Bundle name
* @param {Function} loader function that returns an promise that resolve to the module to be loaded
*/
registerDynamic: function(bundlename, loader) {
if (!this.dynamicLoaders[bundlename]) {
this.dynamicLoaders[bundlename] = [];
}
this.dynamicLoaders[bundlename].push(loader);
},
/**
* @method loadDynamic
* Called to start dynamic loading of a bundle
*
* @param {string} bundlename Bundle name
*
* @return Promise that resolves when all modules have loaded
*/
loadDynamic: function(bundlename) {
const loaders = this.dynamicLoaders[bundlename];
if (loaders) {
return Promise.all(loaders.map((l) => l.call()));
}
return null;
}
};

Expand Down
6 changes: 3 additions & 3 deletions src/global.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ import './sandbox/sandbox-map-methods.js';
import './sandbox/sandbox-abstraction-methods.js';

// Oskari application helpers
import '../src/loader.js';
import '../src/oskari.app.js';
import '../src/BasicBundle.js';
import './loader.js';
import './oskari.app.js';
import './BasicBundle.js';

// deprecated functions
import './deprecated/deprecated.core.js';
Expand Down
68 changes: 27 additions & 41 deletions src/loader.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,8 @@
/**
* Bundle register.
* Usage:
*
* // get
* var bundledetails = Oskari.bundle('mybundle');
* // set
* Oskari.bundle('mybundle', bundledetails);
*
* Details contain an object with:
* {
* clazz : instance of class defined in packages/.../bundle.js,
* metadata : metadata for above bundle.js including scripts to load for the bundle
* }
*
*/
(function (o) {
if (!o) {
// can't add bundle if no Oskari ref
return;
}
var _bundleRegistry = {};
// Add the bundle method to Oskari
o.bundle = function (bundleId, value) {
if (value) {
_bundleRegistry[bundleId] = value;
}
return _bundleRegistry[bundleId];
};
}(Oskari));
/**
* Startupsequence processor for Oskari. Bundles have ether been bundled/minfied
* into the application JS file or separate chunks. In both cases bundles must be
* declared in miniferAppSetup.json
*
*
* Usage:
*
* var startupSequence = [...bundles to load/start... ];
Expand All @@ -52,16 +22,32 @@
if (o.loader) {
// loader already present, but we might want another?
}
var log = Oskari.log('Loader');
var linkFile = function (href, rel, type) {
var importParentElement = document.head || document.body;
var linkElement = document.createElement('link');
const log = Oskari.log('Loader');
const linkFile = function (href, rel, type) {
const importParentElement = document.head || document.body;
const linkElement = document.createElement('link');
linkElement.rel = rel || 'stylesheet';
linkElement.type = type || 'text/css';
linkElement.href = href;
importParentElement.appendChild(linkElement);
};

/**
* @method loadDynamic
* Called to start dynamic loading of a bundle
*
* @param {string} bundlename Bundle name
*
* @return Promise that resolves when all modules have loaded
*/
const loadDynamic = function (bundlename) {
const loaders = Oskari.lazyBundle(bundlename);
if (loaders) {
return Promise.all(loaders.map((l) => l.call()));
}
return null;
};

/**
* Loader
* @param {Object[]} startupSequence sequence of bundles to load/start
Expand Down Expand Up @@ -97,7 +83,7 @@
* @param {Function} done callback
*/
processSequence: function (done, suppressStartEvent = false) {
var me = this;
const me = this;
if (sequence.length === 0) {
// everything has been loaded
if (typeof done === 'function') {
Expand All @@ -108,7 +94,7 @@
}
return;
}
var seqToLoad = sequence.shift();
const seqToLoad = sequence.shift();
if (typeof seqToLoad !== 'object') {
// log warning: block not object
log.warn('StartupSequence item is a ' + typeof seqToLoad + ' instead of object. Skipping');
Expand All @@ -117,24 +103,24 @@
return;
}

var bundleToStart = seqToLoad.bundlename;
const bundleToStart = seqToLoad.bundlename;
if (!bundleToStart) {
log.warn('StartupSequence item doesn\'t contain bundlename. Skipping ', seqToLoad);
// iterate to next
this.processSequence(done, suppressStartEvent);
return;
}
// if bundleinstancename is missing, use bundlename for config key.
var configId = seqToLoad.bundleinstancename || bundleToStart;
var config = appConfig[configId] || {};
const configId = seqToLoad.bundleinstancename || bundleToStart;
const config = appConfig[configId] || {};

if (Oskari.bundle(bundleToStart)) {
log.debug('Bundle preloaded ' + bundleToStart);
me.startBundle(bundleToStart, config, configId);
this.processSequence(done, suppressStartEvent);
return;
}
let bundlePromise = Oskari.bundle_manager.loadDynamic(bundleToStart);
const bundlePromise = loadDynamic(bundleToStart);
if (!bundlePromise) {
log.warn('Bundle wasn\'t preloaded nor registered as dynamic. Skipping ', bundleToStart);
this.processSequence(done, suppressStartEvent);
Expand Down
12 changes: 8 additions & 4 deletions src/oskari.es6.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
*
* A set of methods to support loosely coupled classes and instances for the mapframework
*/
import Sequence from './counter.es6.js';
import Logger from './logger.es6.js';
import Sequence from './counter.es6';
import Logger from './logger.es6';
import pkg from '../package.json';
import { DOMHelper } from './oskari.dom.js';
import { Customization } from './oskari.customization.js';
import { DOMHelper } from './oskari.dom';
import { Customization } from './oskari.customization';
import { BundleRegister } from './BundleRegister';

const defaultSequence = new Sequence();
const sequences = {};
Expand Down Expand Up @@ -59,6 +60,9 @@ const Oskari = {
Oskari.log('Oskari').deprecated('getDefaultMarker', 'Use Oskari.custom.getMarker() instead');
return Customization.getMarker();
},
// from BundleRegister.js
bundle: BundleRegister.bundle,
lazyBundle: BundleRegister.lazyBundle,
// from oskari.customization.js
custom: Customization,
// from oskari.dom
Expand Down
1 change: 1 addition & 0 deletions webpack/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ const RESOLVE_LOADER = {
alias: {
'oskari-loader': path.resolve(__dirname, './oskariLoader.js'),
'oskari-bundle': path.resolve(__dirname, './oskariBundleLoader.js'),
'oskari-lazy-bundle': path.resolve(__dirname, './oskariBundleLazyLoader.js'),
'oskari-lazy-loader': path.resolve(__dirname, './oskariLazyLoader.js')
}
};
Expand Down
Loading