The expose-loader is a webpack loader that allows you to expose modules (in whole or in part) to the global object (self, window, and global). It enables developers to make npm packages available globally in browser environments, particularly useful for legacy code integration and shimming scenarios.
npm install expose-loader --save-devThe expose-loader is a webpack loader, so it's typically configured in webpack configuration files rather than imported directly:
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: require.resolve("jquery"),
loader: "expose-loader",
options: {
exposes: ["$", "jQuery"],
},
},
],
},
};For inline usage:
import $ from "expose-loader?exposes=$,jQuery!jquery";Exposing entire module:
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: require.resolve("jquery"),
loader: "expose-loader",
options: {
exposes: "$", // Exposes entire jQuery to window.$
},
},
],
},
};Exposing specific module exports:
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: require.resolve("underscore"),
loader: "expose-loader",
options: {
exposes: [
{
globalName: "_.map",
moduleLocalName: "map", // Only expose the map function
},
{
globalName: "_.filter",
moduleLocalName: "filter", // Only expose the filter function
},
],
},
},
],
},
};The main entry point for expose-loader configuration through webpack.
interface LoaderOptions {
exposes: string | ExposeConfig | Array<string | ExposeConfig>;
globalObject?: string;
}
interface ExposeConfig {
globalName: string | string[];
moduleLocalName?: string;
override?: boolean;
}Simple string format for basic exposing needs.
/**
* String syntax: "globalName [moduleLocalName] [override]"
* Separators: space or pipe (|)
*
* Examples:
* - "$" - Expose entire module as $
* - "$ jQuery" - Expose as both $ and jQuery
* - "_.map map" - Expose only 'map' export as _.map
* - "$ jQuery true" - Override existing global values
*/
type ExposeString = string;Detailed configuration object for complex exposing scenarios.
interface ExposeConfig {
/** The name(s) in the global object (required) */
globalName: string | string[];
/** The specific export to expose (optional - defaults to entire module) */
moduleLocalName?: string;
/** Allow overriding existing global values (optional - defaults to false) */
override?: boolean;
}Usage Examples:
// Single global name
{
globalName: "$",
moduleLocalName: "default"
}
// Multiple global names (nested object creation)
{
globalName: ["lodash", "_"],
moduleLocalName: "default"
}
// Override existing globals
{
globalName: "$",
override: true
}
// Expose specific export
{
globalName: "_.map",
moduleLocalName: "map"
}Configure multiple exposures in a single loader rule.
type ExposesList = Array<string | ExposeConfig>;Usage Example:
{
exposes: [
"$", // String format
"jQuery", // Another string format
{
globalName: "_.map",
moduleLocalName: "map"
},
{
globalName: ["utils", "helpers"],
moduleLocalName: "utilities"
}
]
}Specify a custom global object for exposing.
interface GlobalObjectOption {
/** Custom global object reference (optional - defaults to auto-detection) */
globalObject?: string;
}Usage Example:
{
exposes: ["myLibrary"],
globalObject: "this" // Use 'this' instead of window/global/self
}Use expose-loader directly in import statements.
/**
* Inline syntax: expose-loader?exposes=config!module
* Multiple exposes: expose-loader?exposes=config1,config2!module
*
* Examples:
* - "expose-loader?exposes=$!jquery"
* - "expose-loader?exposes=$,jQuery!jquery"
* - "expose-loader?exposes=_.map|map!lodash"
*/Usage Examples:
// Single expose
import $ from "expose-loader?exposes=$!jquery";
// Multiple exposes
import $ from "expose-loader?exposes=$,jQuery!jquery";
// With module local name
import { concat } from "expose-loader?exposes=_.concat!lodash/concat";
// Multiple with specific exports
import {
map,
reduce,
} from "expose-loader?exposes=_.map|map,_.reduce|reduce!underscore";interface LoaderContext {
/** Get validated options for the loader */
getOptions(schema?: object): LoaderOptions;
/** Async callback for loader completion */
async(): (error?: Error, result?: string) => void;
/** Current webpack mode (development/production) */
mode?: string;
/** Module being processed */
_module: {
userRequest: string;
factoryMeta?: {
sideEffectFree: boolean;
};
};
}
interface ExposeItem {
globalName: string[];
moduleLocalName?: string;
override?: boolean;
}/**
* Runtime global object detection function
* Returns the appropriate global object for the current environment
* Priority: globalThis > window > self > global > Function('return this')()
*/
declare function getGlobalThis(): typeof globalThis | typeof window | typeof self | typeof global;These functions are used internally by the loader but are part of the public API for advanced use cases.
/**
* Modifies module request to prevent caching conflicts
* @param request - webpack module request string
* @returns Modified request string with "-exposed" suffix
*/
declare function getNewUserRequest(request: string): string;
/**
* Parses and normalizes expose configuration into standard format
* @param items - expose configuration (string, object, or array)
* @returns Array of resolved expose objects
*/
declare function getExposes(items: string | ExposeConfig | Array<string | ExposeConfig>): ExposeItem[];
/**
* Converts absolute paths to relative paths for webpack
* @param loaderContext - webpack loader context
* @param context - base context path
* @param request - module request to contextualize
* @returns Contextualized request path
*/
declare function contextify(loaderContext: LoaderContext, context: string, request: string): string;
/**
* Safely stringifies module requests for code generation
* @param loaderContext - webpack loader context
* @param request - module request to stringify
* @returns JSON stringified request
*/
declare function stringifyRequest(loaderContext: LoaderContext, request: string): string;
/**
* Replaces [name] placeholder with actual module name
* @param loaderContext - webpack loader context
* @param filename - filename template with placeholders
* @returns Interpolated filename
*/
declare function interpolateName(loaderContext: LoaderContext, filename: string): string;// webpack.config.js
module.exports = {
module: {
rules: [
{
test: require.resolve("jquery"),
loader: "expose-loader",
options: {
exposes: ["$", "jQuery"],
},
},
],
},
};// webpack.config.js
module.exports = {
module: {
rules: [
{
test: require.resolve("lodash"),
loader: "expose-loader",
options: {
exposes: [
{
globalName: "_",
},
{
globalName: "_.map",
moduleLocalName: "map",
},
{
globalName: "_.filter",
moduleLocalName: "filter",
},
],
},
},
],
},
};// webpack.config.js - Development mode will throw errors
module.exports = {
mode: "development",
module: {
rules: [
{
test: require.resolve("jquery"),
loader: "expose-loader",
options: {
exposes: {
globalName: "$",
override: false, // Will throw error if $ already exists
},
},
},
],
},
};// webpack.config.js
module.exports = {
module: {
rules: [
{
test: require.resolve("my-library"),
loader: "expose-loader",
options: {
exposes: ["myLib"],
globalObject: "this", // Use 'this' context instead of window/global
},
},
],
},
};The expose-loader provides several error conditions:
override: trueDevelopment Mode Protection:
// In development mode, this will throw an error if window.$ already exists
{
exposes: {
globalName: "$",
override: false // Default value
}
}
// To allow overriding:
{
exposes: {
globalName: "$",
override: true
}
}The expose-loader generates runtime code that:
window.lodash._)Example Generated Code Pattern:
var ___EXPOSE_LOADER_IMPORT___ = require("./path/to/module");
var ___EXPOSE_LOADER_GET_GLOBAL_THIS___ = /* getGlobalThis function */;
var ___EXPOSE_LOADER_GLOBAL_THIS___ = ___EXPOSE_LOADER_GET_GLOBAL_THIS___;
// Create nested structure if needed
if (typeof ___EXPOSE_LOADER_GLOBAL_THIS___["$"] === 'undefined')
___EXPOSE_LOADER_GLOBAL_THIS___["$"] = ___EXPOSE_LOADER_IMPORT___;
module.exports = ___EXPOSE_LOADER_IMPORT___;