Skip to content

Instantly share code, notes, and snippets.

@PatrickJS
Forked from devonChurch/environment.config.json
Created March 31, 2022 01:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save PatrickJS/78a24417093b99377186b124d45a930d to your computer and use it in GitHub Desktop.
Save PatrickJS/78a24417093b99377186b124d45a930d to your computer and use it in GitHub Desktop.
{
"environment": "staging"
}
<!DOCTYPE html>
<html lang="en">
<head>
<script>
(async () => {
// The chunks required to get this application up and running in an
// isolated context.
const isolationChunks = [
"main", // The bundle that initialises the application.
"remoteEntry" // This applications remoteEntry file.
];
// The chunks that point to the `remoteEntry,js` files for the other
// federated modules associated with the applicationthat will create
// the graph in conjunction with the `isolatedChunks`.
const remoteChunks = [
"appOne",
"appTwo",
"utilities"
];
// References to the endpoints that we need to get the configuration files
// from to initialise the application again the current session.
const configChunks = [
"http://example.com/location.config.json", // Global location config.
"../environment.config.json" // Per environment config.
];
// Grab our configuration files in parallel.
const [location, { environment }] = await Promise.all(
configChunks.map(chunk => fetch(chunk).then(response => response.json()))
);
// Reference our configuration files against the global window so that
// our dynamic `publicPath` function can grab them when initialising our
// bundles.
window.__ENVIRONMENT__ = environment;
window.__LOCATION__ = location;
// Now that we have our configuration, lets get the javascript we need to
// initialise the application. We parallelise the requests to keep things
// speedy.
await Promise.all([
// Fetch the `remoteEntry.js` files for our federated dependancies.
...remoteChunks
.map(chunk => {
const { href } = location[chunk][environment];
return fetch(`${href}/remoteEntry.js`).then(response => response.text());
}),
// Get files dedicated to running this micro front-end in isolation
// mode.
...isolationChunks
.map(chunk => fetch(`./${chunk}.js`)
.then(response => response.text())
)
])
.then(scripts => scripts.forEach(script => {
// Now that we have our scripts, let's inject them onto the page
// so that we can run out application initialisation sequence.
const element = document.createElement("script");
element.text = script;
document.querySelector("body").appendChild(element);
}));
})();
</script>
</head>
<body></body>
</html>
{
"shell": {
"development": {
"href": "http://localhost:8000/"
},
"staging": {
"href": "http://staging.shell.example.com/"
},
"production": {
"href": "http://prod.shell.example.com/"
}
},
"appOne": {
"development": {
"href": "http://localhost:8001/"
},
"staging": {
"href": "http://staging.app-one.example.com/"
},
"production": {
"href": "http://prod.app-one.example.com/"
}
},
"appTwo": {
"development": {
"href": "http://localhost:8002/"
},
"staging": {
"href": "http://staging.app-two.example.com/"
},
"production": {
"href": "http://prod.app-two.example.com/"
}
},
"utilities": {
"development": {
"href": "http://localhost:8003/"
},
"staging": {
"href": "http://staging.utilities.example.com/"
},
"production": {
"href": "http://prod.utilities.example.com/"
}
}
}
...
/**
* Allows static builds to change their `publicName` dynamically at run time rather
* than build time. This allows builds to be environment agnostic, where we can
* change from Staging, UAT, to Production with the same artefact and have the
* `publicName` update accordingly via some run time configuration.
* @class
*/
class DynamicPublicPathPlugin {
/**
* The Webpack hook to apply our plugin.
* @parem {Object} compiler - The Webpack compiler.
*/
apply(compiler) {
compiler.hooks.make.tap("MutateRuntime", (compilation) => {
compilation.hooks.runtimeModule.tap("MutateRuntime", (module, chunk) => {
const isPublicPathRuntimeModule = module.constructor.name === "PublicPathRuntimeModule";
if (!isPublicPathRuntimeModule) { return; }
console.log(`* Update "${chunk.name}" dynamic public path`);
// We extract the variable "key" (the namespace to left of the equals sign)
// and leave the rest behind as we are going to build our own new value
// and assign it back to the variable.
const [key] = module.getGeneratedCode().split("=");
// Swap out the old static string value for a function that will run in
// the browser session and create a public path based on the current
// configuration.
//
// We create the `publicPath` from the "environment.config.json" and
// "location.config.json" configuration files that are saved as global
// variables in the browser `__ENVIRONEMENT__` and `__LOCATION__`.
module._cachedGeneratedCode = `${key}=(function() {
const { __LOCATION__, __ENVIRONEMENT__ } = window;
const { href } = __LOCATION__.shell[__ENVIRONEMENT__];
const publicPath = href + "/";
return publicPath;
})();`
return module;
});
});
}
}
...
module.exports = async (_, args) => {
...
return {
...
plugins: [
new ModuleFederationPlugin({
...
}),
new DynamicPublicPathPlugin(),
...
]
...
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment