-
-
Notifications
You must be signed in to change notification settings - Fork 75
/
relations.ts
154 lines (126 loc) Β· 3.71 KB
/
relations.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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import { merge } from "../core/utils.ts";
import type { Data, Page, Site } from "../core.ts";
export interface Options {
/** The list of extensions this plugin applies to */
extensions: string[];
/** The field name used to save the page id */
idKey: string;
/** The field name used to save the page type */
typeKey: string;
/** The foreign keys per type (type => foreign_key) */
foreignKeys: Record<string, string | [string, string]>;
}
// Default options
export const defaults: Options = {
extensions: [".html"],
idKey: "id",
typeKey: "type",
foreignKeys: {},
};
export default function (userOptions?: Partial<Options>) {
const options = merge(defaults, userOptions);
return (site: Site) => {
site.preprocess(options.extensions, index);
function index(page1: Page, pages: Page[]) {
const data1 = page1.data;
const [type1, foreignKey1, id1] = getForeignKey(data1);
// Index the current page with the other pages
pages.forEach(indexPage);
// Index the current page with previously generated pages (if any)
site.pages.forEach(indexPage);
function indexPage(page2: Page) {
if (page1 === page2) {
return;
}
const data2 = page2.data;
const [type2, foreignKey2, id2] = getForeignKey(data2);
// Page2 has a foreign key to page1
const directRelation = relate(
data1,
data2,
foreignKey1,
id1,
type1,
);
// If it was related, do the opposite relation
if (directRelation && type2) {
saveMultipleRelation(
data2,
data1,
type2,
);
return;
}
// Page1 has a foreign key to page2
const reverseRelation = relate(
data2,
data1,
foreignKey2,
id2,
type2,
);
// If it was related, do the opposite relation
if (reverseRelation && type1) {
saveMultipleRelation(
data1,
data2,
type1,
);
}
}
function relate(
rel: Data,
data: Data,
foreignKey?: string,
id?: string,
type?: string,
): boolean {
if (foreignKey && type && id && data[foreignKey]) {
const relId = data[foreignKey] as string | string[];
// The foreign key contain an array
if (Array.isArray(relId)) {
if (relId.includes(id)) {
saveMultipleRelation(rel, data, type);
return true;
}
return false;
}
// The foreign key is a single value
if (relId == id) {
data[type] = rel;
return true;
}
}
return false;
}
function saveMultipleRelation(rel: Data, data: Data, type: string) {
const relData = (data[type] || []) as Data[];
if (!relData.includes(rel)) {
relData.push(rel);
data[type] = relData;
// Sort by id
relData.sort((a, b) => {
const idA = a[options.idKey] as string;
const idB = b[options.idKey] as string;
return idA < idB ? -1 : 1;
});
}
}
}
};
function getForeignKey(data: Data): [string?, string?, string?] {
const type = data[options.typeKey];
if (!type) {
return [undefined, undefined, undefined];
}
const foreignKey = options.foreignKeys[type];
if (!foreignKey) {
return [type, undefined, undefined];
}
if (Array.isArray(foreignKey)) {
const [fk, id] = foreignKey;
return [type, fk, data[id]];
}
return [type, foreignKey, data[options.idKey]];
}
}