@carlosv2/glue
Version:
Dependency injection library that stays out of the way
222 lines (221 loc) • 8.84 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { Alias } from '../alias.js';
import { Processor } from '../processor.js';
import { Constructor, Factory, Property } from '../service/index.js';
import { has, isFunction, isString } from '../utils.js';
import { EnvStr, EnvBool, EnvNum, Eval, Literal, Service as ServiceValue, Parameter, Symbol as SymbolValue, TagList, TagObject, } from '../value/index.js';
import { DiError } from '../error.js';
import { ParameterNotFoundError } from '../error/parameter.js';
import { AliasNotFoundError } from '../error/alias.js';
import { ServiceNotFoundError } from '../error/service.js';
import { AssemblingContainer, FallbackContainer } from '../container/index.js';
export class Loader extends Processor {
constructor(defaultScope) {
super();
this.defaultScope = defaultScope !== null && defaultScope !== void 0 ? defaultScope : Symbol();
this.parameters = {};
this.aliases = {};
this.services = {};
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
glob(_) {
throw new DiError("Unfortunately, Loaders don't have this functionality. Please, use a Compiler instead.");
}
getFullPath(baseFile, relative) {
return `${baseFile.replace(/\/[^/]*$/, '')}/${relative}`;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
readContents(_) {
throw new DiError("Unfortunately, Loaders don't have this functionality. Please, use a Compiler instead.");
}
importPath(baseFile, relative) {
return __awaiter(this, void 0, void 0, function* () {
let path = this.getFullPath(baseFile, relative);
if (path.endsWith('.ts') || path.endsWith('js')) {
path = path.substring(0, path.length - 3);
}
return import(/* @vite-ignore */ path);
});
}
addExtend(path) {
this.fromPath(path);
}
addParameter(id, definition) {
this.parameters[id] = definition;
}
hasParameter(id) {
return has(id, this.parameters);
}
getParameter(id) {
if (has(id, this.parameters)) {
return this.parameters[id];
}
throw new ParameterNotFoundError(id);
}
getAllParameters() {
return Object.keys(this.parameters);
}
removeParameter(id) {
delete this.parameters[id];
}
addAlias(id, definition) {
this.aliases[id] = definition;
}
hasAlias(id) {
return has(id, this.aliases);
}
getAlias(id) {
if (has(id, this.aliases)) {
return this.aliases[id];
}
throw new AliasNotFoundError(id);
}
getAllAliases() {
return Object.keys(this.aliases);
}
removeAlias(id) {
delete this.aliases[id];
}
addService(id, definition) {
var _a;
this.removeService(id);
const scope = (_a = definition.getScope()) !== null && _a !== void 0 ? _a : this.defaultScope;
if (!has(scope, this.services)) {
this.services[scope] = {};
}
this.services[scope][id] = definition;
}
hasService(id) {
return Object.values(this.services).some(services => has(id, services));
}
getService(id) {
for (const scope of Object.keys(this.services)) {
if (has(id, this.services[scope])) {
return this.services[scope][id];
}
}
throw new ServiceNotFoundError(id);
}
getAllServices() {
return Object.values(this.services)
.map(services => Object.keys(services))
.flat();
}
removeService(id) {
for (const scope of Object.keys(this.services)) {
delete this.services[scope][id];
}
}
processEnvStrValue(context, name, fallback) {
return new EnvStr(context, name, fallback);
}
processEnvBoolValue(context, name, fallback) {
return new EnvBool(context, name, fallback);
}
processEnvNumValue(context, name, fallback) {
return new EnvNum(context, name, fallback);
}
processEvalValue(context, code) {
return new Eval(context, code);
}
processLiteralValue(context, value) {
return new Literal(context, value);
}
processParameterValue(context, id) {
return new Parameter(context, id);
}
processServiceValue(context, id) {
return new ServiceValue(context, id);
}
isClass(value) {
if (!isFunction(value)) {
return false;
}
const representation = value.toString();
return (representation.startsWith('class ') ||
representation.startsWith('class{') ||
[String(Object), String(Date), String(Error)].includes(representation));
}
isFunc(value) {
return isFunction(value) && !this.isClass(value);
}
getTheOnlyOne(context, haystack, filter) {
let token = undefined;
for (const value of Object.values(haystack)) {
if (filter(value)) {
if (token) {
throw new DiError('Multiple exported symbols where found', context);
}
else {
token = value;
}
}
}
if (token) {
return token;
}
throw new DiError('A suitable exported symbol was not found', context);
}
processSymbolValue(context, path, name) {
// Importing module. For example: (my/path
if (!isString(name)) {
return new SymbolValue(context, this.importPath(context.getPath(), path));
}
// Importing the default symbol. For example: (my/path)
if (name.length === 0) {
return new SymbolValue(context, this.importPath(context.getPath(), path).then(module => module['default']));
}
// Importing the only exported symbol. For example: (my/path)~
if (name === '~') {
return new SymbolValue(context, this.importPath(context.getPath(), path).then(module => this.getTheOnlyOne(context, module, () => true)));
}
// Importing the only exported class. For example: (my/path)~class
if (name === '~class') {
return new SymbolValue(context, this.importPath(context.getPath(), path).then(module => this.getTheOnlyOne(context, module, this.isClass.bind(this))));
}
// Importing the only exported function. For example: (my/path)~func
if (name === '~func') {
return new SymbolValue(context, this.importPath(context.getPath(), path).then(module => this.getTheOnlyOne(context, module, this.isFunc.bind(this))));
}
// Importing the symbol found before or a known one. For example: (my/path)MyObj
return new SymbolValue(context, this.importPath(context.getPath(), path).then(module => module[name]));
}
processTagListValue(context, name) {
return new TagList(context, name);
}
processTagObjectValue(context, name) {
return new TagObject(context, name);
}
processAlias(context, aliased) {
return new Alias(context, aliased);
}
processConstructorService(context, symbol, args, scope, tags, calls) {
return new Constructor(symbol, args, context, scope, tags, calls);
}
processFactoryService(context, symbol, factory, args, scope, tags, calls) {
return new Factory(symbol, factory, args, context, scope, tags, calls);
}
processPropertyService(context, symbol, property, scope, tags, calls) {
return new Property(symbol, property, context, scope, tags, calls);
}
getContainer(scope, parent) {
var _a;
const containerScope = scope !== null && scope !== void 0 ? scope : this.defaultScope;
const params = this.parameters;
const aliases = this.aliases;
const services = (_a = this.services[containerScope]) !== null && _a !== void 0 ? _a : {};
const container = new AssemblingContainer(params, aliases, services);
if (parent) {
return new FallbackContainer(container, parent);
}
return container;
}
}