-
Notifications
You must be signed in to change notification settings - Fork 13
/
indexer.mjs
130 lines (118 loc) · 3.23 KB
/
indexer.mjs
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
import glob from 'fast-glob';
import { vfileToAst } from '../mdx-plugins/index.mjs';
import { read } from 'to-vfile';
import slugify from 'slugify';
import fm from 'gray-matter';
import { selectAll } from 'unist-util-select';
import { getTextValue } from './tree-tools.mjs';
import isEqual from 'lodash.isequal';
export function createIndexer(metadata) {
function _createIndexer(filesGlobs = [], nodeMapper = {}) {
return {
addGlob(glob) {
return _createIndexer(filesGlobs.concat(glob), nodeMapper);
},
addNodeMap(node, mapping) {
return _createIndexer(filesGlobs, {
...nodeMapper,
[node]: mapping,
});
},
addNodeData(node) {
return _createIndexer(filesGlobs, {
...nodeMapper,
[node]: dataIdentity,
});
},
generateIndexes: async function generateIndexes() {
const files = await glob(filesGlobs);
const indexer = createFileIndexer(metadata, nodeMapper);
const result = await Promise.all(files.map(indexer));
return result.flat();
},
};
}
return _createIndexer();
}
function dataIdentity(data) {
return data;
}
function createFileIndexer(metadata, nodeMapper) {
return async function toFileIndexes(file) {
const { ast, matter } = await parseFile(file);
const urlPath = fileToUrl(file);
return getMappedDataWithHeadings(
nodeMapper,
metadata,
matter.data,
urlPath,
ast,
);
};
}
export async function parseFile(file) {
const vfile = await read(file, 'utf8');
return { ast: await vfileToAst(vfile), matter: fm(vfile.toString()) };
}
function fileToUrl(file) {
const path = file.replace(/^.*?pages/, '').replace(/\.mdx?$/, '');
if (path === '/index') {
return '/';
}
return path;
}
function getMappedDataWithHeadings(
nodeMapper,
metadata,
matter,
urlPath,
tree,
) {
let currentHeading = null;
let contentList = [];
for (let node of tree?.children ?? []) {
if (node.type === 'heading') {
currentHeading = getTextValue(node);
}
for (let [nodeSelector, mapping] of Object.entries(nodeMapper)) {
const slug = currentHeading ? slugify(currentHeading) : null;
const result = doMapping(nodeSelector, mapping, node, tree, {
slug,
title: currentHeading,
urlPath,
matter,
systemId: metadata.systemId,
url: `${metadata.baseUrl}${urlPath}#${slug}`,
});
const uniqueContent = result.reduce(function (acc, mappedData) {
if (isTextFromBefore(mappedData, contentList)) {
return acc;
}
return acc.concat(mappedData);
}, []);
contentList = contentList.concat(uniqueContent);
}
}
return contentList;
}
function isTextFromBefore(current, previous) {
return previous.some(function (visited) {
return isEqual(visited.content, current.content);
});
}
function doMapping(nodeSelector, mapping, node, tree, data) {
return selectAll(nodeSelector, node)
.map(function (innerNode) {
const output = mapping(
{
...data,
content: getTextValue(innerNode),
},
innerNode,
nodeSelector,
tree,
);
return output;
})
.filter(Boolean);
}