README
Require-es
A microfrontend module loader
Installation
Migration from RequireJs
Register a package
Require a package
Define a package
RequireEs options
RequireEs events
WebPack / Rollup bundling for RequireEs
Custom elements support
A microfrontend module loader
Require-es will:
- allow micro-frontends to share dependencies
- help finding compatible package-versions
The project will be structured in 3 parts:
provide a module loader which:
- loads amd modules
- loads multiple filetypes: js / json / xml / txt / html / css / wasm
- registers multiple versions of a single package
- can set default-versions
- easily migrates from RequireJs
- emits events to allow usage monitoring
create a require-es-server, where:
- packages can be registered
- usage of package-version can be tracked
- module loading can become predictable
- predictive http2-pushes are possible
create a webpack/rollup loader
Installation
Add the package to your project
npm install requirees --save
Serve the file in node_modules/requirees/build/requirees.js in the header of your webpage (9Kb Gzipped)
<!DOCTYPE html>
<html>
<head>
<title>Pagetitle</title>
<script src="scripts/requirees.js"></script>
</head>
<body></body>
</html>
Migration from RequireJs
RequireEs allows a soft transition from RequireJs. This means that most syntax of RequireJs, is also supported in RequireEs:
- define(packageName, [dependencies], factory);
- define([dependencies], factory);
- require(['packageName'], packageInstance => {});
- requirejs(['packageName'], packageInstance => {});
- require('packageName');
- requirejs.config({paths});
In a later version, require.config({shim}) will be supported as well.
More info on RequireJs: https://requirejs.org/
Register a package
Usage
require.register({
packageName1(@)(version)(.filetype)(-default): [
url(.filetype),
url(.filetype),
url(.filetype),
...
],
packageName2(@)(version)(.filetype)(-default): {
versions,
url,
urls
}
});
Key | Description |
---|---|
PackageName | the name of the package |
Version | the version-number in string format: major.minor(.patch)(.build)(-releaseCandidate) |
Filetype | the file-extension - js/css/txt/xml/json/html/wasm/tag |
Default | indicates the default version |
Versions | array of versions to fill out in the URL (use placeholder ${version} in the url-string) |
Url | single url string |
Urls | multiple urls for 1 package |
Note - Determining the version number, happens in this order:
- Is a 'versions' attribute present in the package-value
- Is '@version' present in the package-name
- Is a version present in the url (cdnjs.com/17.0.2/package.js)
Samples
//register react, version '17.0.2'
require.register({
react: 'https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js'
});
//register react, versions '0.14.9', '15.6.2', '15.7.0', '16.14.0' (set as default), '17.0.2'
require.register({
react: {
versions: ['0.14.9', '15.6.2', '15.7.0'],
url: 'https://cdnjs.cloudflare.com/ajax/libs/react/${version}/react.min.js'
},
'react@16.14.0-default': 'https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js',
'react@17.0.2': 'https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js'
});
//register react, versions '15.7.0', '16.14.0', '17.0.2'
require.register({
react: [
'https://cdnjs.cloudflare.com/ajax/libs/react/15.7.0/react.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js',
]
});
//register bootstrap, both css and js
require.register({
bootstrap: [
"https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js",
"https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.css",
"https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.0/js/bootstrap.bundle.min.js",
"https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.0/css/bootstrap.min.css"
]
});
Require a package
Usage
//async await
const packageInstance = await require('packageName(@)(^)(~)(*)(version)(.filetype)');
//mulitple packages
const [pckg1, packg2] = await require([
'pckg1(@)(^)(~)(*)(version)(.filetype)',
'pckg1(@)(^)(~)(*)(version)(.filetype)'
]);
//cjs style (only works when a package is already loaded once, or in a defined factory)
const packageInstance = require('packageName(@)(^)(~)(*)(version)(.filetype)');
//using promises
requirees('packageName(@)(^)(~)(*)(version)(.filetype)').then(packageInstance);
//multiple packages
requirees([
'pckg1(@)(^)(~)(*)(version)(.filetype)',
'pckg1(@)(^)(~)(*)(version)(.filetype)'
]).then(
([pckg1, pckg2]) => {}
);
//using callbacks
require(['packageName(@)(^)(~)(*)(version)(.filetype)'], package => {});
//multiple packages
require([
'pckg1(@)(^)(~)(*)(version)(.filetype)',
'pckg1(@)(^)(~)(*)(version)(.filetype)'
], (pckg1, pckg2) => {});
//without registration
await require(url);
Key | Description |
---|---|
PackageName | the name of the package |
Version | the version-number in string format: major.minor(.patch)(.build)(-releaseCandidate) |
Filetype | the file-extension - js/css/txt/xml/json/html/wasm/tag; if no filetype is specified ALL filetypes will be loaded |
- | load highest version ^ | load highest minor-version ~ | load highest patch-version
Note - Determining the version number happens in this order
- Find best version match, if any versionnumber is specified
- Find the default, if no versionnumber is specified
- Take the highest version number if no default, nor versionnumber are specified
Note - If no filetype is specified all registered filetypes will be loaded
Samples
//register bootstrap
require.register({
bootstrap: [
"https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js",
"https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.css",
"https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.0/js/bootstrap.bundle.min.js",
"https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.0/css/bootstrap.min.css"
]
})
//load both css & js from the highest available version (4.6.0)
//4.6.0 will be the default (no default is set)
const bootstrap = await require('bootstrap');
//4.6.0 is the highest registered version
const bootstrap = await require('bootstrap@*');
//load only the css tag from version 3.4.1
const bootstrapCssTag = await require('bootstrap.css@3.4.1'); //fixed version number
//load only the js tag from version 3.x
const bootstrapJs = await require('bootstrap.js@^3.0.0'); //find highest minor
//try to load bootstrap 3.4.2
const bootstrap = await require('bootstrap@3.4.2');
//will result in a console.warn("package not found");
//version 3.4.2 was not registered using define(), require.register, nor require.config({paths})
//download without registration
const bootstrap = await require('https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.0/js/bootstrap.bundle.min.js')
Define a package
Usage (follows AMD-pattern)
//named define
define(packageName(@)(version)(.filetype)(-default), [dependencies], factory);
//anonymous define
define([dependencies], factory);
Key | Description |
---|---|
PackageName | the name of the package |
Version | the version-number in string format: major.minor(.patch)(.build)(-releaseCandidate) |
Filetype | the file extension - js/css/txt/xml/json/html/wasm/tag |
Default | indicates the default version |
Dependencies | package names (or urls) on which this package is dependent |
Factory | function / json / text / HTMLElement / xml / ... |
Note - The dependency naming rules are equal to the require naming rules:
packageName(@)(^)(~)(*)(version)(.filetype)
Note - The datatype of the factory can be different, depending on the filetype:
- js: function
- json: json-text (gets converted to JSON) / json-object
- css: css-text (gets converted to a script-tag)
- txt: text
- xml: text
- html: html-text (gets converted to an HTMLElement) / HTMLElement-object
Samples
//define an amd module
define('hello.js', ['react@^16.0.0'], (react) => {
function fn1(){}
return {fn1}
});
//define a CJS module
define('hello.js', [], () => {
const react = require('react');
function fn1(){}
module.exports = {fn1};
});
//define a json object
define('hello.json', [], '{"foo": "bar"}');
//define css
define('hello.css', [], 'body{background-color: red}');
//define a specific version of hello.css
define('hello.css@1.0.0', [], 'body{background-color: red}');
//define an html fragment
//gets converted to an HTMLElement when required + will automatically add the CSS to the page
define('hello.html', ['hello.css@^1.0.0'], '<div>bla</div>');
//alternative
define('hello.html', [], document.createElement('div'));
//define r.js style
define('react', reactFactoryFn);
//using version number
define('react@17.0.2', react1702FactoryFn);
RequireEs options (coming soon)
require.config({
allowRedefine: false,
invokeNonMatchedDefines: true
});
Key | Description |
---|---|
allowRedefine | default: false; false: you cannot change the factory, for a given package (after it gets required for the first time) true: the factory can be changed at any time, next require will use the new factory |
invokeNonMatchedDefines | default: false; automatically invoke anonymous defines which could not be matched to any package in the RequireEs register. |
RequireEs events
Usage
//subscribe to an event
require.on(evtName, callback);
//require.subscribe is a synonym for require.on
require.subscribe(evtName, callback);
//publish an event
require.publish(evtName, payload);
//spy on all events
require.addWireTap(callback);
Predefined events
Event | Trigger |
---|---|
requirees.pre-define | when define gets called, but the factory is not stored into the registry yet |
requirees.define | when a package factory was added to the registry |
requirees.pre-register | when require.register or require.config({paths}) are called, but the package it not added to the registry yet |
requirees.register | when a package was added to the registry |
requirees.pre-file-load | before an actual file/factory load is happening |
requirees.file-load | when a file/factory load has completed |
requirees.wiretaps | on all events |
Samples
//listen to all files being loaded
requirees.subscribe('requirees.pre-file-load', ({package}) => {
console.log(`start loading package: ${package.name}`);
})
//listen to all events being triggered
requirees.addWireTap(console.log);
//alternative:
requirees.subscribe('requirees.wiretaps', console.log);
//create custom events
requirees.subscribe('hendrik.sayHello', data => {
console.log('hello!!', data)
});
requirees.publish('hendrik.sayHello', {foo: 'bar'});
WebPack / Rollup bundling for RequireEs
Intro
In the examples below project-dependencies 'jquery', 'react' and 'lodash' will be removed from your bundle and loaded using RequireEs.
To make this happen:
- Add RequireEs to the top of your HTML-document
- Register your dependencies on the document (This will happen automatically using RequireEs-server, coming soon)
- Load your AMD (/UMD) bundle through RequireEs (recommended), or using a script-tag
Samples for Webpack and Rollup bundling can be found in the sections below.
//require the AMD module
require.register({
jquery: 'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js',
react: [
'https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/react/16.10.1/umd/react.production.min.js'
],
lodash: [
'https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.min.js'
]
});
//require the application package
await require('/scripts/myLib.amd.js');
If your package is added through a script-tag, call require.config({invokeNonMatchedDefines: true}) to invoke the factory immediately:
<script>
requirees.config({
invokeNonMatchedDefines: true
})
</script>
<script src="/scripts/myLib.amd.js"></script>
Build your bundle AMD style
Webpack
module.exports = {
//...
output: {
libraryTarget: 'amd',
filename: 'scripts/myLib.amd.js'
},
//...
externals: {
jquery: 'jquery@*',
react: 'react@^17.0.0',
lodash: 'lodash@^4.17.0'
},
};
Rollup
export default {
//...
output: {
format: 'amd',
file: 'scripts/myLib.amd.js',
external: ['react', 'jquery', 'lodash'],
paths: {
react: 'react@^17.0.0',
jquery: 'jquery@*',
lodash: 'lodash@^4.17.0'
}
},
//...
};
Build your bundle UMD style
Webpack
module.exports = {
//...
output: {
libraryTarget: 'umd',
filename: 'scripts/myLib.amd.js',
library: 'myLib'
},
//...
externals: {
jquery: {
root: '