123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292 |
- "use strict";
- const { HookMap, SyncBailHook, SyncWaterfallHook } = require("tapable");
- const { concatComparators, keepOriginalOrder } = require("../util/comparators");
- const smartGrouping = require("../util/smartGrouping");
- class StatsFactory {
- constructor() {
- this.hooks = Object.freeze({
-
- extract: new HookMap(
- () => new SyncBailHook(["object", "data", "context"])
- ),
-
- filter: new HookMap(
- () => new SyncBailHook(["item", "context", "index", "unfilteredIndex"])
- ),
-
- sort: new HookMap(() => new SyncBailHook(["comparators", "context"])),
-
- filterSorted: new HookMap(
- () => new SyncBailHook(["item", "context", "index", "unfilteredIndex"])
- ),
-
- groupResults: new HookMap(
- () => new SyncBailHook(["groupConfigs", "context"])
- ),
-
- sortResults: new HookMap(
- () => new SyncBailHook(["comparators", "context"])
- ),
-
- filterResults: new HookMap(
- () => new SyncBailHook(["item", "context", "index", "unfilteredIndex"])
- ),
-
- merge: new HookMap(() => new SyncBailHook(["items", "context"])),
-
- result: new HookMap(() => new SyncWaterfallHook(["result", "context"])),
-
- getItemName: new HookMap(() => new SyncBailHook(["item", "context"])),
-
- getItemFactory: new HookMap(() => new SyncBailHook(["item", "context"]))
- });
- const hooks = this.hooks;
- this._caches =
- ({});
- for (const key of Object.keys(hooks)) {
- this._caches[key] = new Map();
- }
- this._inCreate = false;
- }
- _getAllLevelHooks(hookMap, cache, type) {
- const cacheEntry = cache.get(type);
- if (cacheEntry !== undefined) {
- return cacheEntry;
- }
- const hooks = [];
- const typeParts = type.split(".");
- for (let i = 0; i < typeParts.length; i++) {
- const hook = hookMap.get(typeParts.slice(i).join("."));
- if (hook) {
- hooks.push(hook);
- }
- }
- cache.set(type, hooks);
- return hooks;
- }
- _forEachLevel(hookMap, cache, type, fn) {
- for (const hook of this._getAllLevelHooks(hookMap, cache, type)) {
- const result = fn(hook);
- if (result !== undefined) return result;
- }
- }
- _forEachLevelWaterfall(hookMap, cache, type, data, fn) {
- for (const hook of this._getAllLevelHooks(hookMap, cache, type)) {
- data = fn(hook, data);
- }
- return data;
- }
- _forEachLevelFilter(hookMap, cache, type, items, fn, forceClone) {
- const hooks = this._getAllLevelHooks(hookMap, cache, type);
- if (hooks.length === 0) return forceClone ? items.slice() : items;
- let i = 0;
- return items.filter((item, idx) => {
- for (const hook of hooks) {
- const r = fn(hook, item, idx, i);
- if (r !== undefined) {
- if (r) i++;
- return r;
- }
- }
- i++;
- return true;
- });
- }
-
- create(type, data, baseContext) {
- if (this._inCreate) {
- return this._create(type, data, baseContext);
- } else {
- try {
- this._inCreate = true;
- return this._create(type, data, baseContext);
- } finally {
- for (const key of Object.keys(this._caches)) this._caches[key].clear();
- this._inCreate = false;
- }
- }
- }
- _create(type, data, baseContext) {
- const context = {
- ...baseContext,
- type,
- [type]: data
- };
- if (Array.isArray(data)) {
-
- const items = this._forEachLevelFilter(
- this.hooks.filter,
- this._caches.filter,
- type,
- data,
- (h, r, idx, i) => h.call(r, context, idx, i),
- true
- );
-
- const comparators = [];
- this._forEachLevel(this.hooks.sort, this._caches.sort, type, h =>
- h.call(comparators, context)
- );
- if (comparators.length > 0) {
- items.sort(
-
- concatComparators(...comparators, keepOriginalOrder(items))
- );
- }
-
- const items2 = this._forEachLevelFilter(
- this.hooks.filterSorted,
- this._caches.filterSorted,
- type,
- items,
- (h, r, idx, i) => h.call(r, context, idx, i),
- false
- );
-
- let resultItems = items2.map((item, i) => {
- const itemContext = {
- ...context,
- _index: i
- };
-
- const itemName = this._forEachLevel(
- this.hooks.getItemName,
- this._caches.getItemName,
- `${type}[]`,
- h => h.call(item, itemContext)
- );
- if (itemName) itemContext[itemName] = item;
- const innerType = itemName ? `${type}[].${itemName}` : `${type}[]`;
-
- const itemFactory =
- this._forEachLevel(
- this.hooks.getItemFactory,
- this._caches.getItemFactory,
- innerType,
- h => h.call(item, itemContext)
- ) || this;
-
- return itemFactory.create(innerType, item, itemContext);
- });
-
- const comparators2 = [];
- this._forEachLevel(
- this.hooks.sortResults,
- this._caches.sortResults,
- type,
- h => h.call(comparators2, context)
- );
- if (comparators2.length > 0) {
- resultItems.sort(
-
- concatComparators(...comparators2, keepOriginalOrder(resultItems))
- );
- }
-
- const groupConfigs = [];
- this._forEachLevel(
- this.hooks.groupResults,
- this._caches.groupResults,
- type,
- h => h.call(groupConfigs, context)
- );
- if (groupConfigs.length > 0) {
- resultItems = smartGrouping(resultItems, groupConfigs);
- }
-
- const finalResultItems = this._forEachLevelFilter(
- this.hooks.filterResults,
- this._caches.filterResults,
- type,
- resultItems,
- (h, r, idx, i) => h.call(r, context, idx, i),
- false
- );
-
- let result = this._forEachLevel(
- this.hooks.merge,
- this._caches.merge,
- type,
- h => h.call(finalResultItems, context)
- );
- if (result === undefined) result = finalResultItems;
-
- return this._forEachLevelWaterfall(
- this.hooks.result,
- this._caches.result,
- type,
- result,
- (h, r) => h.call(r, context)
- );
- } else {
- const object = {};
-
- this._forEachLevel(this.hooks.extract, this._caches.extract, type, h =>
- h.call(object, data, context)
- );
-
- return this._forEachLevelWaterfall(
- this.hooks.result,
- this._caches.result,
- type,
- object,
- (h, r) => h.call(r, context)
- );
- }
- }
- }
- module.exports = StatsFactory;
|