209 lines
5.0 KiB
JavaScript
209 lines
5.0 KiB
JavaScript
"use strict";
|
|
const { getScopeParserExecutionError } = require("./errors");
|
|
const { last } = require("./utils");
|
|
const { concatArrays } = require("./doc-utils");
|
|
|
|
function find(list, fn) {
|
|
const length = list.length >>> 0;
|
|
let value;
|
|
|
|
for (let i = 0; i < length; i++) {
|
|
value = list[i];
|
|
if (fn.call(this, value, i, list)) {
|
|
return value;
|
|
}
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
function getValue(tag, meta, num) {
|
|
const scope = this.scopeList[num];
|
|
if (this.resolved) {
|
|
let w = this.resolved;
|
|
this.scopePath.forEach((p, index) => {
|
|
const lIndex = this.scopeLindex[index];
|
|
w = find(w, function (r) {
|
|
return r.lIndex === lIndex;
|
|
});
|
|
w = w.value[this.scopePathItem[index]];
|
|
});
|
|
return [
|
|
this.scopePath.length - 1,
|
|
find(w, function (r) {
|
|
return meta.part.lIndex === r.lIndex;
|
|
}).value,
|
|
];
|
|
}
|
|
// search in the scopes (in reverse order) and keep the first defined value
|
|
let result;
|
|
|
|
let parser;
|
|
if (!this.cachedParsers || !meta.part) {
|
|
parser = this.parser(tag, {
|
|
scopePath: this.scopePath,
|
|
});
|
|
} else if (this.cachedParsers[meta.part.lIndex]) {
|
|
parser = this.cachedParsers[meta.part.lIndex];
|
|
} else {
|
|
parser = this.cachedParsers[meta.part.lIndex] = this.parser(tag, {
|
|
scopePath: this.scopePath,
|
|
});
|
|
}
|
|
try {
|
|
result = parser.get(scope, this.getContext(meta, num));
|
|
} catch (error) {
|
|
throw getScopeParserExecutionError({
|
|
tag,
|
|
scope,
|
|
error,
|
|
offset: meta.part.offset,
|
|
});
|
|
}
|
|
if (result == null && num > 0) {
|
|
return getValue.call(this, tag, meta, num - 1);
|
|
}
|
|
return [num, result];
|
|
}
|
|
|
|
function getValueAsync(tag, meta, num) {
|
|
const scope = this.scopeList[num];
|
|
// search in the scopes (in reverse order) and keep the first defined value
|
|
let parser;
|
|
if (!this.cachedParsers || !meta.part) {
|
|
parser = this.parser(tag, {
|
|
scopePath: this.scopePath,
|
|
});
|
|
} else if (this.cachedParsers[meta.part.lIndex]) {
|
|
parser = this.cachedParsers[meta.part.lIndex];
|
|
} else {
|
|
parser = this.cachedParsers[meta.part.lIndex] = this.parser(tag, {
|
|
scopePath: this.scopePath,
|
|
});
|
|
}
|
|
|
|
return Promise.resolve()
|
|
.then(() => {
|
|
return parser.get(scope, this.getContext(meta, num));
|
|
})
|
|
.catch(function (error) {
|
|
throw getScopeParserExecutionError({
|
|
tag,
|
|
scope,
|
|
error,
|
|
offset: meta.part.offset,
|
|
});
|
|
})
|
|
.then((result) => {
|
|
if (result == null && num > 0) {
|
|
return getValueAsync.call(this, tag, meta, num - 1);
|
|
}
|
|
return result;
|
|
});
|
|
}
|
|
|
|
// This class responsibility is to manage the scope
|
|
const ScopeManager = class ScopeManager {
|
|
constructor(options) {
|
|
this.scopePath = options.scopePath;
|
|
this.scopePathItem = options.scopePathItem;
|
|
this.scopePathLength = options.scopePathLength;
|
|
this.scopeList = options.scopeList;
|
|
this.scopeLindex = options.scopeLindex;
|
|
this.parser = options.parser;
|
|
this.resolved = options.resolved;
|
|
this.cachedParsers = options.cachedParsers;
|
|
}
|
|
loopOver(tag, functor, inverted, meta) {
|
|
return this.loopOverValue(this.getValue(tag, meta), functor, inverted);
|
|
}
|
|
functorIfInverted(inverted, functor, value, i, length) {
|
|
if (inverted) {
|
|
functor(value, i, length);
|
|
}
|
|
return inverted;
|
|
}
|
|
isValueFalsy(value, type) {
|
|
return (
|
|
value == null ||
|
|
!value ||
|
|
(type === "[object Array]" && value.length === 0)
|
|
);
|
|
}
|
|
loopOverValue(value, functor, inverted) {
|
|
if (this.resolved) {
|
|
inverted = false;
|
|
}
|
|
const type = Object.prototype.toString.call(value);
|
|
if (this.isValueFalsy(value, type)) {
|
|
return this.functorIfInverted(
|
|
inverted,
|
|
functor,
|
|
last(this.scopeList),
|
|
0,
|
|
1
|
|
);
|
|
}
|
|
if (type === "[object Array]") {
|
|
for (let i = 0; i < value.length; i++) {
|
|
this.functorIfInverted(!inverted, functor, value[i], i, value.length);
|
|
}
|
|
return true;
|
|
}
|
|
if (type === "[object Object]") {
|
|
return this.functorIfInverted(!inverted, functor, value, 0, 1);
|
|
}
|
|
return this.functorIfInverted(
|
|
!inverted,
|
|
functor,
|
|
last(this.scopeList),
|
|
0,
|
|
1
|
|
);
|
|
}
|
|
getValue(tag, meta) {
|
|
const [num, result] = getValue.call(
|
|
this,
|
|
tag,
|
|
meta,
|
|
this.scopeList.length - 1
|
|
);
|
|
this.num = num;
|
|
return result;
|
|
}
|
|
getValueAsync(tag, meta) {
|
|
return getValueAsync.call(this, tag, meta, this.scopeList.length - 1);
|
|
}
|
|
getContext(meta, num) {
|
|
return {
|
|
num,
|
|
meta,
|
|
scopeList: this.scopeList,
|
|
resolved: this.resolved,
|
|
scopePath: this.scopePath,
|
|
scopePathItem: this.scopePathItem,
|
|
scopePathLength: this.scopePathLength,
|
|
};
|
|
}
|
|
createSubScopeManager(scope, tag, i, part, length) {
|
|
return new ScopeManager({
|
|
resolved: this.resolved,
|
|
parser: this.parser,
|
|
cachedParsers: this.cachedParsers,
|
|
scopeList: concatArrays([this.scopeList, [scope]]),
|
|
scopePath: concatArrays([this.scopePath, [tag]]),
|
|
scopePathItem: concatArrays([this.scopePathItem, [i]]),
|
|
scopePathLength: concatArrays([this.scopePathLength, [length]]),
|
|
scopeLindex: concatArrays([this.scopeLindex, [part.lIndex]]),
|
|
});
|
|
}
|
|
};
|
|
|
|
module.exports = function (options) {
|
|
options.scopePath = [];
|
|
options.scopePathItem = [];
|
|
options.scopePathLength = [];
|
|
options.scopeLindex = [];
|
|
options.scopeList = [options.tags];
|
|
return new ScopeManager(options);
|
|
};
|