@carlosv2/glue
Version:
Dependency injection library that stays out of the way
218 lines (217 loc) • 10.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Processor = void 0;
const index_1 = require("./context/index");
const error_1 = require("./error");
const service_1 = require("./error/service");
const utils_1 = require("./utils");
class Processor {
fromPaths(pattern) {
let fullPattern = pattern;
if (!fullPattern.startsWith('/')) {
const base = (0, utils_1.getCallerFile)();
if (!base) {
throw new error_1.DiError('The caller file could not be determined');
}
fullPattern = this.getFullPath(base, pattern);
}
this.glob(fullPattern).forEach(this.fromPath.bind(this));
}
fromPath(path) {
this.fromContent(path, this.readContents(path));
}
fromContent(path, content) {
this.fromData(path, this.deserialise(path, content));
}
fromData(path, content) {
if (!(0, utils_1.isDictionary)(content)) {
throw new error_1.DiError(`The contents of the path \`${path}\` must represent an object`);
}
if ((0, utils_1.has)('extends', content)) {
if (!(0, utils_1.isArray)(content['extends'])) {
throw new error_1.DiError(`The extends section in the path \`${path}\` must represent an array`);
}
const context = new index_1.DefinitionContext(path, 'ignore', 'ignore');
content['extends'].forEach(extension => {
if (!(0, utils_1.isString)(extension)) {
throw new error_1.DiError(`Each extended file must add as an string in the path \`${path}\``);
}
this.addExtend(this.getFullPath(context.getPath(), extension));
});
}
if ((0, utils_1.has)('parameters', content)) {
if (!(0, utils_1.isDictionary)(content['parameters'])) {
throw new error_1.DiError(`The parameters section in the path \`${path}\` must represent an object`);
}
Object.entries(content['parameters']).forEach(([id, value]) => {
const definition = this.serialise(path, value);
const context = new index_1.DefinitionContext(path, id, definition);
this.addParameter(id, this.parseValue(context, value));
});
}
if ((0, utils_1.has)('services', content)) {
if (!(0, utils_1.isDictionary)(content['services'])) {
throw new error_1.DiError(`The services section in the path \`${path}\` must represent an object`);
}
Object.entries(content['services']).forEach(([id, value]) => {
const definition = this.serialise(path, value);
const context = new index_1.DefinitionContext(path, id, definition);
if ((0, utils_1.isString)(value)) {
this.addAlias(id, this.parseAlias(context, value));
}
else {
this.addService(id, this.parseService(context, value));
}
});
}
}
parseValue(context, value) {
if ((0, utils_1.isArray)(value)) {
return this.processLiteralValue(context, value.map(item => this.parseValue(context, item)));
}
if ((0, utils_1.isDictionary)(value)) {
if ((0, utils_1.has)('symbol', value)) {
return this.parseService(context, value);
}
if ((0, utils_1.has)('_symbol', value)) {
value['symbol'] = value['_symbol'];
}
return this.processLiteralValue(context, Object.fromEntries(Object.entries(value).map(([key, item]) => [
key,
this.parseValue(context, item),
])));
}
if ((0, utils_1.isString)(value)) {
if ((0, utils_1.isFirstChar)(value, '<')) {
return this.processServiceValue(context, value.substring(1));
}
else if ((0, utils_1.isFirstChar)(value, '$')) {
return this.processParameterValue(context, value.substring(1));
}
else if ((0, utils_1.isFirstChar)(value, '>')) {
return this.processTagListValue(context, value.substring(1));
}
else if ((0, utils_1.isFirstChar)(value, '}')) {
return this.processTagObjectValue(context, value.substring(1));
}
else if ((0, utils_1.isFirstChar)(value, '%')) {
const [name, declaredType, ...values] = value.substring(1).split('/');
const type = declaredType !== null && declaredType !== void 0 ? declaredType : 's';
const fallback = values.length > 0 ? values.join('/') : undefined;
if (type === 's') {
return this.processEnvStrValue(context, name, fallback);
}
else if (type === 'b') {
return this.processEnvBoolValue(context, name, fallback);
}
else if (type === 'n') {
return this.processEnvNumValue(context, name, fallback);
}
else {
throw new error_1.DiError('Unknown environment variable type while parsing', context);
}
}
else if ((0, utils_1.isFirstChar)(value, '!')) {
return this.processEvalValue(context, value);
}
else if ((0, utils_1.isFirstChar)(value, '(')) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_, path, name] = value.match(/^\(([^\)]+)(?:\)(.*))?$/);
return this.processSymbolValue(context, path, name);
}
else if ((0, utils_1.isFirstChar)(value, '[')) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_, pattern, name] = value.match(/^\[([^\]]+)(?:\](.*))?$/);
const fullPattern = this.getFullPath(context.getPath(), pattern, false);
return this.processLiteralValue(context, this.glob(fullPattern).map(path => {
return this.processSymbolValue(context, path, name);
}));
}
else {
return this.processLiteralValue(context, value);
}
}
return this.processLiteralValue(context, value);
}
parseAlias(context, value) {
if (!(0, utils_1.isString)(value)) {
throw new error_1.DiError('A string definition was expected', context);
}
return this.processAlias(context, value);
}
parseTag(context, value) {
if ((0, utils_1.isString)(value)) {
return { name: value };
}
if ((0, utils_1.isDictionary)(value)) {
if (!(0, utils_1.has)('name', value)) {
throw new error_1.DiError('POJO tags must have the `name` key.', context);
}
return value;
}
throw new error_1.DiError('Tags must be defined as strings or POJOs.', context);
}
parseCall(context, value) {
var _a;
if ((0, utils_1.isString)(value)) {
return { method: this.parseValue(context, value), params: [] };
}
if ((0, utils_1.isDictionary)(value)) {
if (!(0, utils_1.has)('method', value)) {
throw new error_1.DiError('POJO calls must have the `method` key.', context);
}
const method = this.parseValue(context, value['method']);
const params = (_a = value['params']) !== null && _a !== void 0 ? _a : [];
if (!(0, utils_1.isArray)(params)) {
throw new error_1.DiError('Service call parameters must be an array of values.', context);
}
return {
method,
params: params.map(param => this.parseValue(context, param)),
};
}
throw new error_1.DiError('Calls must be defined as strings or POJOs.', context);
}
parseService(context, value) {
var _a, _b;
if (!(0, utils_1.isDictionary)(value)) {
throw new error_1.DiError('A service can only be created from a POJO', context);
}
if (!(0, utils_1.has)('symbol', value)) {
throw new error_1.DiError('A service must declare the `symbol` property.', context);
}
const symbol = this.parseValue(context, value['symbol']);
const scope = value['scope'];
let tags = [];
if ((0, utils_1.has)('tags', value)) {
if (!(0, utils_1.isArray)(value['tags'])) {
throw new error_1.DiError('Service tags must be an array of tags.', context);
}
tags = value['tags'].map(tag => this.parseTag(context, tag));
}
let calls = [];
if ((0, utils_1.has)('calls', value)) {
if (!(0, utils_1.isArray)(value['calls'])) {
throw new error_1.DiError('Service calls must be an array of calls.', context);
}
calls = value['calls'].map(call => this.parseCall(context, call));
}
if (!(0, utils_1.has)('factory', value) && !(0, utils_1.has)('property', value)) {
const args = ((_a = value['args']) !== null && _a !== void 0 ? _a : []).map(arg => this.parseValue(context, arg));
return this.processConstructorService(context, symbol, args, scope, tags, calls);
}
else if ((0, utils_1.has)('factory', value) && !(0, utils_1.has)('property', value)) {
const factory = this.parseValue(context, value['factory']);
const args = ((_b = value['args']) !== null && _b !== void 0 ? _b : []).map(arg => this.parseValue(context, arg));
return this.processFactoryService(context, symbol, factory, args, scope, tags, calls);
}
else if (!(0, utils_1.has)('factory', value) && (0, utils_1.has)('property', value)) {
const property = this.parseValue(context, value['property']);
return this.processPropertyService(context, symbol, property, scope, tags, calls);
}
else {
throw new service_1.UnknownServiceTypeError(context);
}
}
}
exports.Processor = Processor;