discourse/app/assets/javascripts/discourse-common/addon/lib/loader-shim.js

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

59 lines
3.1 KiB
JavaScript
Raw Normal View History

// Webpack has bugs, using globalThis is the safest
// https://github.com/embroider-build/embroider/issues/1545
let { define: __define__, require: __require__ } = globalThis;
// Traditionally, Ember compiled ES modules into AMD modules, which are then
// made usable in the browser at runtime via loader.js. In a classic build, all
// the modules, including any external ember-auto-imported dependencies, are
// added to the loader.js registry and therefore require()-able at runtime.
//
// Overtime, the AMD-ness of the modules, the ability to define arbitrarily
// named modules and the ability to require any modules and even enumerate the
// known modules at runtime (require.entries/_eak_seen) became heavily relied
// upon, which is problematic. For one thing, these features don't align well
// with ES modules semantics, and it is also impossible to perform tree-shaking
// as the presence of a particular module could end up being important even if
// it appears to be unused in the static analysis.
//
// For Discourse, the AMD/loader.js mechanism is an important glue. It is what
// allows Discourse core/admin/wizard/plugins to all be separate .js bundlers
// and be "glued back together" as full module graph in the browser.
//
// For instance, a plugin module can `import Post from "discourse/models/post";
// because the babel plugin compiled discourse/models/post.js into an AMD
// module into app.js (`define("discourse/models/post", ...)`), which makes
// it available in the runtime loader.js registry, and the plugin module itself
// is also compiled into AMD with a dependency on the core module.
//
// This has similar drawbacks as the general problem in the ecosystem, but in
// addition, it has a particular bad side-effect that any external dependencies
// (NPM packages) we use in core will automatically become a defacto public API
// for plugins to use as well, making it difficult for core to upgrade/remove
// dependencies (and thus so as introducing them in the first place).
//
// Ember is aggressively moving away from AMD modules and there are active RFCs
// to explore the path to deprecating AMD/loader.js. While it would still be
// fine (in the medium term at least) for us to use AMD/loader.js as an interop
// mechanism between our bundles, we will have to be more conscious about what
// to make available to plugins via this mechanism.
//
// In the meantime Embroider no longer automatically add AMD shims for external
// dependencies. In order to preserve compatibility for plugins, this utility
// allows us to manually force a particular module to be included in loader.js
// and available to plugins. Overtime we should review this list and start
// deprecating any accidental leakages.
//
// The general way to use it is:
//
// import { importSync } from "@embroider/macros";
//
// loaderShim("some-npm-pkg", () => importSync("some-npm-pkg"));
//
// Note that `importSync` is a macro which must be passed a string
// literal, therefore cannot be abstracted away.
export default function loaderShim(pkg, callback) {
if (!__require__.has(pkg)) {
__define__(pkg, callback);
}
}