solid-services
Version:
Solid.js library adding a services layer for global shared state.
160 lines (152 loc) • 4.46 kB
JavaScript
;
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
const solidJs = require('solid-js');
const ServiceRegistryContext = solidJs.createContext();
const ServiceRegistry = (props) => {
let registry;
return solidJs.createComponent(ServiceRegistryContext.Provider, {
get value() {
return registry ??= createRegistry({
get expose() {
return props.expose;
}
});
},
get children() {
return props.children;
}
});
};
function useRegistry() {
const registry = solidJs.useContext(ServiceRegistryContext);
if (!registry) {
throw new Error(
"Your app needs to be wrapped with <ServiceRegistry> context in order to use services."
);
}
return registry;
}
function createSubRoot(fn, owner = solidJs.getOwner()) {
return solidJs.createRoot((dispose) => {
owner && solidJs.runWithOwner(owner, solidJs.onCleanup.bind(void 0, dispose));
return fn(dispose);
}, owner);
}
function runInSubRoot(fn, owner) {
let error;
let hasError = false;
const result = createSubRoot((dispose) => {
try {
return fn(dispose);
} catch (e) {
hasError = true;
error = e;
dispose();
throw e;
}
}, owner);
if (hasError) {
throw error;
}
return result;
}
class Service {
}
class Registry {
#owner;
#config;
#cache;
constructor(config = {}) {
this.#config = config;
this.#owner = solidJs.getOwner();
this.#cache = /* @__PURE__ */ new Map();
}
/**
* Checks weather the registry has a service registered for the given initializer.
*
* First it will check in the parent registry and if no service is registered there,
* it will check in the current registry.
*/
has(initializer) {
const parentRegistry = this.getParentRegistry();
if (parentRegistry?.isExposing(initializer)) {
return parentRegistry.has(initializer);
}
return this.#cache.has(initializer);
}
/**
* Returns the service registered for the given initializer.
*
* First it will try to find it in the parent registry and if no service is registered there,
* it will check in the current registry.
*/
get(initializer) {
const parentRegistry = this.getParentRegistry();
if (parentRegistry?.isExposing(initializer)) {
return parentRegistry.get(initializer);
}
return this.#cache.get(initializer);
}
/**
* Clears the registry.
*/
clear() {
this.#cache.clear();
}
/**
* Deletes registered service for the given initializer.
*/
delete(initializer) {
this.#cache.delete(initializer);
}
/**
* Registers a service for the given initializer.
*
* If the registry has a parent registry, it will check if the service is exposed there.
*
* If it is exposed, it will register the service in the parent registry.
*
* If it is not exposed, it will register the service in the current registry.
*
* If the registry does not have a parent registry, it will register the service in the current registry.
*/
register(initializer) {
const parentRegistry = this.getParentRegistry();
if (parentRegistry?.isExposing(initializer)) {
return parentRegistry.register(initializer);
}
const registration = runInSubRoot(
() => this.initializeService(initializer),
this.#owner
);
this.#cache.set(initializer, registration);
return registration;
}
isExposing(initializer) {
return this.#config.expose === true || Array.isArray(this.#config.expose) && this.#config.expose?.includes(initializer);
}
getParentRegistry() {
return this.#owner?.owner ? runInSubRoot((dispose) => {
const context = solidJs.useContext(ServiceRegistryContext);
dispose();
return context;
}, this.#owner.owner) : void 0;
}
initializeService(initializer) {
return initializer.prototype?.constructor ? Reflect.construct(initializer, []) : initializer();
}
}
function createRegistry(config) {
return new Registry(config);
}
function useService(initializer) {
const registry = useRegistry();
return () => registry.get(initializer) || registry.register(initializer);
}
exports.Registry = Registry;
exports.Service = Service;
exports.ServiceRegistry = ServiceRegistry;
exports.ServiceRegistryContext = ServiceRegistryContext;
exports.createRegistry = createRegistry;
exports.useRegistry = useRegistry;
exports.useService = useService;