Sticky Module Singleton Utility
sticky-module is a specialized JavaScript utility designed to guarantee that a given module is instantiated and evaluated only once, even across multiple imports or different bundling contexts within an application. It achieves this by storing and retrieving module instances using a unique, Symbol-based key, effectively creating an application-wide singleton for that module. This mechanism is particularly useful for libraries or components that require strict single-instance behavior to maintain consistent state or prevent redundant setup in complex module graphs, micro-frontends, or scenarios with multiple bundler outputs. The current stable version is 0.1.1. Its release cadence is likely slow, focusing on stability given its niche purpose. A key differentiator is its Symbol-based internal storage, which offers a robust and collision-resistant approach to global module caching compared to string-based keys.
Common errors
-
TypeError: stickyModule is not a function
cause This typically occurs when attempting to import 'sticky-module' using CommonJS `require()` syntax in a project that expects ESM, or if transpilation is misconfigured, leading to the default export not being correctly resolved.fixEnsure you are using `import stickyModule from 'sticky-module';` in an ESM context. If using CommonJS, check your build tools for proper ESM-to-CJS interop or consider using dynamic import `import('sticky-module').then(m => m.default)`. -
Module 'sticky-module' has no default export.
cause This error can occur if you're trying to destructure a named export when `sticky-module` only provides a default export, or if your TypeScript configuration (e.g., `esModuleInterop`) is not correctly set up for handling default ESM imports.fixUse the correct default import syntax: `import stickyModule from 'sticky-module';`. If in TypeScript, ensure `"esModuleInterop": true` and `"allowSyntheticDefaultImports": true` are set in your `tsconfig.json`.
Warnings
- gotcha The package description explicitly calls itself a 'leaky utility'. This implies that once a module is stored, it will persist in memory for the lifetime of the JavaScript realm and is not automatically garbage collected unless the application explicitly manages its removal (which sticky-module itself does not provide an API for). This is intentional for its singleton purpose but can be a concern for long-running applications or if modules store large objects.
- gotcha The module uses a Symbol-based key for internal storage, which makes it effective for singleton enforcement within a single JavaScript realm (e.g., a single browser window, a Node.js process). However, this mechanism does not extend across different realms like iframes, Web Workers (without SharedWorker), or separate Node.js child processes, where each would have its own independent module storage.
- breaking Given the current version is 0.1.1, any future major release (e.g., 1.0.0) is very likely to introduce breaking changes without a deprecation cycle. APIs might change significantly, or internal mechanisms could be altered.
Install
-
npm install sticky-module -
yarn add sticky-module -
pnpm add sticky-module
Imports
- stickyModule
const stickyModule = require('sticky-module');import stickyModule from 'sticky-module';
- Module interface
let { config, known } = stickyModule('my-config', { config: {} });let [{ config }, known] = stickyModule('my-config', { config: {} });
Quickstart
import stickyModule from 'sticky-module';
console.log('--- First attempt to get module ---');
let [{a, b}, known] = stickyModule('@custom/my-singleton', {
a: Math.random(),
b: 'initial value'
});
console.log(`Was module known? ${known}`); // Expected: false
console.log(`Module values: a=${a}, b='${b}'`); // Shows initial random 'a' and 'b'
// Simulate a delay or another part of the application importing it later
setTimeout(() => {
console.log('\n--- Second attempt to get module (after delay) ---');
let [{a: a2, b: b2}, known2] = stickyModule('@custom/my-singleton', {
a: Math.random(), // This value will be ignored as module is already stored
b: 'new value, ignored' // This value will also be ignored
});
console.log(`Was module known? ${known2}`); // Expected: true
console.log(`Module values: a=${a2}, b='${b2}'`); // Shows the *same* initial random 'a' and 'b'
// Verify they are the same instance/values
console.log(`Are 'a' and 'a2' the same? ${a === a2}`); // Expected: true
console.log(`Are 'b' and 'b2' the same? ${b === b2}`); // Expected: true
}, 100);