/
pipeline.ts
133 lines (116 loc) · 4.17 KB
/
pipeline.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import { joinURL } from 'ufo'
import type { ContentQueryFindResponse, ContentQueryResponse } from '../../types/api'
import type { ContentQueryBuilder, ContentQueryBuilderParams } from '../../types/query'
import { apply, ensureArray, sortList, withoutKeys, withKeys, omit } from './utils'
import { createMatch } from '.'
export function createPipelineFetcher<T> (getContentsList: () => Promise<T[]>) {
// Create Matcher
const match = createMatch()
/**
* Exctract surrounded items of specific condition
*/
const surround = (data: any[], { query, before, after }: Exclude<ContentQueryBuilderParams['surround'], undefined>) => {
const matchQuery = typeof query === 'string' ? { _path: query } : query
// Find matched item index
const index = data.findIndex(item => match(item, matchQuery))
before = before ?? 1
after = after ?? 1
const slice = new Array(before + after).fill(null, 0)
return index === -1 ? slice : slice.map((_, i) => data[index - before! + i + Number(i >= before!)] || null)
}
type ContentQueryPipe<T, InputState = ContentQueryFindResponse<T>, OutputState = ContentQueryFindResponse<T>> = (state: InputState, params: ContentQueryBuilderParams, db: Array<T>) => OutputState | void
const matchingPipelines: Array<ContentQueryPipe<T>> = [
// Conditions
(state, params) => {
const filtered = state.result.filter(item => ensureArray(params.where!).every(matchQuery => match(item, matchQuery)))
return {
...state,
result: filtered,
total: filtered.length
}
},
// Sort data
(state, params) => ensureArray(params.sort).forEach(options => sortList(state.result, options!)),
function fetchSurround (state, params, db) {
if (params.surround) {
let _surround = surround(state.result?.length === 1 ? db : state.result, params.surround)
_surround = apply(withoutKeys(params.without))(_surround)
_surround = apply(withKeys(params.only))(_surround)
// @ts-ignore
state.surround = _surround
}
return state
}
]
const transformingPiples: Array<ContentQueryPipe<T>> = [
// Skip first items
(state, params) => {
if (params.skip) {
return {
...state,
result: state.result.slice(params.skip),
skip: params.skip
}
}
},
// Pick first items
(state, params) => {
if (params.limit) {
return {
...state,
result: state.result.slice(0, params.limit),
limit: params.limit
}
}
},
function fetchDirConfig (state, params, db) {
if (params.dirConfig) {
const path = (state.result[0] as any)?._path || params.where?.find(w => w._path)?._path as (string | RegExp)
if (typeof path === 'string') {
const dirConfig = db.find((item: any) => item._path === joinURL(path, '_dir'))
if (dirConfig) {
// @ts-ignore
state.dirConfig = { _path: dirConfig._path, ...withoutKeys(['_'])(dirConfig) }
}
}
}
return state
},
// Remove unwanted fields
(state, params) => ({
...state,
result: apply(withoutKeys(params.without))(state.result)
}),
// Select only wanted fields
(state, params) => ({
...state,
result: apply(withKeys(params.only))(state.result)
})
]
return async (query: ContentQueryBuilder<T>): Promise<ContentQueryResponse<T>> => {
const db = await getContentsList()
const params = query.params()
const result1: ContentQueryFindResponse<T> = {
result: db,
limit: 0,
skip: 0,
total: db.length
}
const matchedData = matchingPipelines.reduce(($data, pipe) => pipe($data, params, db) || $data, result1)
// return count if query is for count
if (params.count) {
return {
result: matchedData.result.length
}
}
const result = transformingPiples.reduce(($data, pipe) => pipe($data, params, db) || $data, matchedData)
// return first item if query is for single item
if (params.first) {
return {
...omit(['skip', 'limit', 'total'])(result),
result: result.result[0]
}
}
return result
}
}