-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.ts
109 lines (94 loc) · 3.91 KB
/
index.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
import collections, { CollectionConfig } from '@metalsmith/collections';
import deepmerge from 'deepmerge';
import Metalsmith from 'metalsmith';
type MetalsmithMetadata = { [key: string]: unknown };
type MetalsmithMetadataWithCollections = MetalsmithMetadata & {
collections?: { [key: string]: Metalsmith.File };
};
// https://github.com/metalsmith/collections/blob/362c20c6814b626850a41f8d245cde81f5af66df/lib/index.d.ts#L48-L50
type MetalsmithCollectionsOptions = {
[x: string]: CollectionConfig | string;
};
export interface Options {
pattern: string;
key?: string;
collection: string;
settings: CollectionConfig;
}
interface DefaultedOptions extends Options {
key: string;
}
export default (options: Options): Metalsmith.Plugin =>
async (files, metalsmith, done) => {
const defaultedOptions = deepmerge(
{
key: '{val}',
},
options || {},
) as DefaultedOptions;
const debug = metalsmith.debug('metalsmith-multi-collections');
debug('running with options: %O', defaultedOptions);
// Generate the @metalsmith/collections config file
const collectionsConfig: MetalsmithCollectionsOptions = {};
// For every file that has `key` present as a metadata filed...
metalsmith
.match(defaultedOptions.pattern, Object.keys(files))
.filter((filename) => files[filename][defaultedOptions.key])
.forEach((filename) => {
const file = files[filename];
// Coerce the metadata value into an array
const fileValue = file[defaultedOptions.key];
(Array.isArray(fileValue) ? fileValue : [fileValue]).forEach((value) => {
// Generate the collection name
const collection = defaultedOptions.collection.replace('{val}', value);
// Add the generated collection name in the file's metadata, which will cause the
// @metalsmith/collections execution below to create this collection
const fileCollection = files[filename].collection as string[] | undefined;
files[filename].collection = [...(fileCollection || []), collection];
// Build the settings for @metalsmith/collections
collectionsConfig[collection] = defaultedOptions.settings;
});
});
debug('generated collections: %O', Object.keys(collectionsConfig));
// Clear side effect data from previous @metalsmith/collections,
// otherwise we could end up with duplicate pages in collections
const metadata = metalsmith.metadata() as MetalsmithMetadataWithCollections;
Object.keys(metadata.collections || {}).forEach((collection) => {
if (metadata[collection]) {
delete metadata[collection];
}
});
// Snapshot any collections we didn't intend to change
// @metalsmith/collections will end up overwriting them and forgetting previous settings
const collectionsSnapshot = Object.keys(metadata.collections || [])
.filter((collection) => !collectionsConfig[collection])
.reduce(
(acc, collection) => ({
...acc,
[collection]: (metadata.collections || {})[collection],
}),
{} as { [key: string]: Metalsmith.File },
);
// Run @metalsmith/collections
collections(collectionsConfig)(files, metalsmith, (...args) => {
// Restore collections we didn't intend to change
Object.keys(collectionsSnapshot).forEach((collection) => {
if (!metadata.collections) {
metadata.collections = {};
}
metadata.collections[collection] = collectionsSnapshot[collection];
});
// Sort the collections
const metadataCollections = metadata.collections || {};
metadata.collections = Object.keys(metadataCollections)
.sort()
.reduce(
(dict, collection) => {
dict[collection] = metadataCollections[collection];
return dict;
},
{} as { [key: string]: Metalsmith.File },
);
done(...args);
});
};