@docusaurus/plugin-content-blog 2.0.0-beta.ff31de0ff → 2.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/authors.d.ts +22 -0
- package/lib/authors.js +122 -0
- package/lib/blogUtils.d.ts +27 -7
- package/lib/blogUtils.js +214 -141
- package/lib/feed.d.ts +15 -0
- package/lib/feed.js +102 -0
- package/lib/frontMatter.d.ts +10 -0
- package/lib/frontMatter.js +62 -0
- package/lib/index.d.ts +4 -4
- package/lib/index.js +179 -205
- package/lib/markdownLoader.d.ts +3 -6
- package/lib/markdownLoader.js +6 -7
- package/lib/options.d.ts +10 -0
- package/lib/{pluginOptionSchema.js → options.js} +44 -14
- package/lib/remark/footnoteIDFixer.d.ts +14 -0
- package/lib/remark/footnoteIDFixer.js +29 -0
- package/lib/translations.d.ts +10 -0
- package/lib/translations.js +53 -0
- package/lib/types.d.ts +4 -109
- package/package.json +23 -18
- package/src/authors.ts +168 -0
- package/src/blogUtils.ts +316 -196
- package/src/feed.ts +171 -0
- package/src/frontMatter.ts +81 -0
- package/src/index.ts +246 -268
- package/src/markdownLoader.ts +11 -16
- package/src/{pluginOptionSchema.ts → options.ts} +57 -16
- package/src/plugin-content-blog.d.ts +587 -0
- package/src/remark/footnoteIDFixer.ts +29 -0
- package/src/translations.ts +69 -0
- package/src/types.ts +2 -128
- package/index.d.ts +0 -138
- package/lib/.tsbuildinfo +0 -4415
- package/lib/blogFrontMatter.d.ts +0 -28
- package/lib/blogFrontMatter.js +0 -50
- package/lib/pluginOptionSchema.d.ts +0 -33
- package/src/__tests__/__fixtures__/website/blog/2018-12-14-Happy-First-Birthday-Slash.md +0 -5
- package/src/__tests__/__fixtures__/website/blog/complex-slug.md +0 -7
- package/src/__tests__/__fixtures__/website/blog/date-matter.md +0 -5
- package/src/__tests__/__fixtures__/website/blog/draft.md +0 -6
- package/src/__tests__/__fixtures__/website/blog/heading-as-title.md +0 -5
- package/src/__tests__/__fixtures__/website/blog/simple-slug.md +0 -7
- package/src/__tests__/__fixtures__/website/blog-with-ref/2018-12-14-Happy-First-Birthday-Slash.md +0 -5
- package/src/__tests__/__fixtures__/website/blog-with-ref/post-with-broken-links.md +0 -11
- package/src/__tests__/__fixtures__/website/blog-with-ref/post.md +0 -5
- package/src/__tests__/__fixtures__/website/i18n/en/docusaurus-plugin-content-blog/2018-12-14-Happy-First-Birthday-Slash.md +0 -5
- package/src/__tests__/__fixtures__/website-blog-without-date/blog/no date.md +0 -1
- package/src/__tests__/__snapshots__/generateBlogFeed.test.ts.snap +0 -101
- package/src/__tests__/__snapshots__/linkify.test.ts.snap +0 -24
- package/src/__tests__/__snapshots__/pluginOptionSchema.test.ts.snap +0 -5
- package/src/__tests__/blogFrontMatter.test.ts +0 -265
- package/src/__tests__/generateBlogFeed.test.ts +0 -100
- package/src/__tests__/index.test.ts +0 -336
- package/src/__tests__/linkify.test.ts +0 -93
- package/src/__tests__/pluginOptionSchema.test.ts +0 -150
- package/src/blogFrontMatter.ts +0 -87
- package/tsconfig.json +0 -9
- package/types.d.ts +0 -13
package/src/blogUtils.ts
CHANGED
|
@@ -6,253 +6,378 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import fs from 'fs-extra';
|
|
9
|
-
import globby from 'globby';
|
|
10
|
-
import chalk from 'chalk';
|
|
11
9
|
import path from 'path';
|
|
10
|
+
import _ from 'lodash';
|
|
11
|
+
import logger from '@docusaurus/logger';
|
|
12
12
|
import readingTime from 'reading-time';
|
|
13
|
-
import {Feed} from 'feed';
|
|
14
|
-
import {keyBy, mapValues} from 'lodash';
|
|
15
13
|
import {
|
|
16
|
-
|
|
17
|
-
BlogPost,
|
|
18
|
-
DateLink,
|
|
19
|
-
BlogContentPaths,
|
|
20
|
-
BlogMarkdownLoaderOptions,
|
|
21
|
-
} from './types';
|
|
22
|
-
import {
|
|
23
|
-
parseMarkdownFile,
|
|
14
|
+
parseMarkdownString,
|
|
24
15
|
normalizeUrl,
|
|
25
16
|
aliasedSitePath,
|
|
26
17
|
getEditUrl,
|
|
27
18
|
getFolderContainingFile,
|
|
28
19
|
posixPath,
|
|
29
20
|
replaceMarkdownLinks,
|
|
21
|
+
Globby,
|
|
22
|
+
normalizeFrontMatterTags,
|
|
23
|
+
groupTaggedItems,
|
|
24
|
+
getFileCommitDate,
|
|
25
|
+
getContentPathList,
|
|
30
26
|
} from '@docusaurus/utils';
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
27
|
+
import {validateBlogPostFrontMatter} from './frontMatter';
|
|
28
|
+
import {type AuthorsMap, getAuthorsMap, getBlogPostAuthors} from './authors';
|
|
29
|
+
import type {LoadContext} from '@docusaurus/types';
|
|
30
|
+
import type {
|
|
31
|
+
PluginOptions,
|
|
32
|
+
ReadingTimeFunction,
|
|
33
|
+
BlogPost,
|
|
34
|
+
BlogTags,
|
|
35
|
+
BlogPaginated,
|
|
36
|
+
} from '@docusaurus/plugin-content-blog';
|
|
37
|
+
import type {BlogContentPaths, BlogMarkdownLoaderOptions} from './types';
|
|
33
38
|
|
|
34
39
|
export function truncate(fileString: string, truncateMarker: RegExp): string {
|
|
35
40
|
return fileString.split(truncateMarker, 1).shift()!;
|
|
36
41
|
}
|
|
37
42
|
|
|
38
|
-
export function getSourceToPermalink(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return
|
|
42
|
-
|
|
43
|
-
(v) => v.metadata.permalink,
|
|
43
|
+
export function getSourceToPermalink(blogPosts: BlogPost[]): {
|
|
44
|
+
[aliasedPath: string]: string;
|
|
45
|
+
} {
|
|
46
|
+
return Object.fromEntries(
|
|
47
|
+
blogPosts.map(({metadata: {source, permalink}}) => [source, permalink]),
|
|
44
48
|
);
|
|
45
49
|
}
|
|
46
50
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
51
|
+
export function paginateBlogPosts({
|
|
52
|
+
blogPosts,
|
|
53
|
+
basePageUrl,
|
|
54
|
+
blogTitle,
|
|
55
|
+
blogDescription,
|
|
56
|
+
postsPerPageOption,
|
|
57
|
+
}: {
|
|
58
|
+
blogPosts: BlogPost[];
|
|
59
|
+
basePageUrl: string;
|
|
60
|
+
blogTitle: string;
|
|
61
|
+
blogDescription: string;
|
|
62
|
+
postsPerPageOption: number | 'ALL';
|
|
63
|
+
}): BlogPaginated[] {
|
|
64
|
+
const totalCount = blogPosts.length;
|
|
65
|
+
const postsPerPage =
|
|
66
|
+
postsPerPageOption === 'ALL' ? totalCount : postsPerPageOption;
|
|
67
|
+
const numberOfPages = Math.ceil(totalCount / postsPerPage);
|
|
68
|
+
|
|
69
|
+
const pages: BlogPaginated[] = [];
|
|
70
|
+
|
|
71
|
+
function permalink(page: number) {
|
|
72
|
+
return page > 0
|
|
73
|
+
? normalizeUrl([basePageUrl, `page/${page + 1}`])
|
|
74
|
+
: basePageUrl;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
for (let page = 0; page < numberOfPages; page += 1) {
|
|
78
|
+
pages.push({
|
|
79
|
+
items: blogPosts
|
|
80
|
+
.slice(page * postsPerPage, (page + 1) * postsPerPage)
|
|
81
|
+
.map((item) => item.id),
|
|
82
|
+
metadata: {
|
|
83
|
+
permalink: permalink(page),
|
|
84
|
+
page: page + 1,
|
|
85
|
+
postsPerPage,
|
|
86
|
+
totalPages: numberOfPages,
|
|
87
|
+
totalCount,
|
|
88
|
+
previousPage: page !== 0 ? permalink(page - 1) : undefined,
|
|
89
|
+
nextPage: page < numberOfPages - 1 ? permalink(page + 1) : undefined,
|
|
90
|
+
blogDescription,
|
|
91
|
+
blogTitle,
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return pages;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function getBlogTags({
|
|
100
|
+
blogPosts,
|
|
101
|
+
...params
|
|
102
|
+
}: {
|
|
103
|
+
blogPosts: BlogPost[];
|
|
104
|
+
blogTitle: string;
|
|
105
|
+
blogDescription: string;
|
|
106
|
+
postsPerPageOption: number | 'ALL';
|
|
107
|
+
}): BlogTags {
|
|
108
|
+
const groups = groupTaggedItems(
|
|
109
|
+
blogPosts,
|
|
110
|
+
(blogPost) => blogPost.metadata.tags,
|
|
111
|
+
);
|
|
50
112
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
.
|
|
54
|
-
.
|
|
55
|
-
|
|
113
|
+
return _.mapValues(groups, ({tag, items: tagBlogPosts}) => ({
|
|
114
|
+
label: tag.label,
|
|
115
|
+
items: tagBlogPosts.map((item) => item.id),
|
|
116
|
+
permalink: tag.permalink,
|
|
117
|
+
pages: paginateBlogPosts({
|
|
118
|
+
blogPosts: tagBlogPosts,
|
|
119
|
+
basePageUrl: tag.permalink,
|
|
120
|
+
...params,
|
|
121
|
+
}),
|
|
122
|
+
}));
|
|
56
123
|
}
|
|
57
124
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
125
|
+
const DATE_FILENAME_REGEX =
|
|
126
|
+
/^(?<folder>.*)(?<date>\d{4}[-/]\d{1,2}[-/]\d{1,2})[-/]?(?<text>.*?)(?:\/index)?.mdx?$/;
|
|
127
|
+
|
|
128
|
+
type ParsedBlogFileName = {
|
|
129
|
+
date: Date | undefined;
|
|
130
|
+
text: string;
|
|
131
|
+
slug: string;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export function parseBlogFileName(
|
|
135
|
+
blogSourceRelative: string,
|
|
136
|
+
): ParsedBlogFileName {
|
|
137
|
+
const dateFilenameMatch = blogSourceRelative.match(DATE_FILENAME_REGEX);
|
|
138
|
+
if (dateFilenameMatch) {
|
|
139
|
+
const {folder, text, date: dateString} = dateFilenameMatch.groups!;
|
|
140
|
+
// Always treat dates as UTC by adding the `Z`
|
|
141
|
+
const date = new Date(`${dateString!}Z`);
|
|
142
|
+
const slugDate = dateString!.replace(/-/g, '/');
|
|
143
|
+
const slug = `/${slugDate}/${folder!}${text!}`;
|
|
144
|
+
return {date, text: text!, slug};
|
|
72
145
|
}
|
|
146
|
+
const text = blogSourceRelative.replace(/(?:\/index)?\.mdx?$/, '');
|
|
147
|
+
const slug = `/${text}`;
|
|
148
|
+
return {date: undefined, text, slug};
|
|
149
|
+
}
|
|
73
150
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
new
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
151
|
+
function formatBlogPostDate(
|
|
152
|
+
locale: string,
|
|
153
|
+
date: Date,
|
|
154
|
+
calendar: string,
|
|
155
|
+
): string {
|
|
156
|
+
try {
|
|
157
|
+
return new Intl.DateTimeFormat(locale, {
|
|
158
|
+
day: 'numeric',
|
|
159
|
+
month: 'long',
|
|
160
|
+
year: 'numeric',
|
|
161
|
+
timeZone: 'UTC',
|
|
162
|
+
calendar,
|
|
163
|
+
}).format(date);
|
|
164
|
+
} catch (err) {
|
|
165
|
+
logger.error`Can't format blog post date "${String(date)}"`;
|
|
166
|
+
throw err;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
92
169
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
feed.addItem({
|
|
99
|
-
title: metadataTitle,
|
|
100
|
-
id,
|
|
101
|
-
link: normalizeUrl([siteUrl, permalink]),
|
|
102
|
-
date,
|
|
103
|
-
description,
|
|
170
|
+
async function parseBlogPostMarkdownFile(blogSourceAbsolute: string) {
|
|
171
|
+
const markdownString = await fs.readFile(blogSourceAbsolute, 'utf-8');
|
|
172
|
+
try {
|
|
173
|
+
const result = parseMarkdownString(markdownString, {
|
|
174
|
+
removeContentTitle: true,
|
|
104
175
|
});
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
176
|
+
return {
|
|
177
|
+
...result,
|
|
178
|
+
frontMatter: validateBlogPostFrontMatter(result.frontMatter),
|
|
179
|
+
};
|
|
180
|
+
} catch (err) {
|
|
181
|
+
logger.error`Error while parsing blog post file path=${blogSourceAbsolute}.`;
|
|
182
|
+
throw err;
|
|
183
|
+
}
|
|
108
184
|
}
|
|
109
185
|
|
|
110
|
-
|
|
186
|
+
const defaultReadingTime: ReadingTimeFunction = ({content, options}) =>
|
|
187
|
+
readingTime(content, options).minutes;
|
|
188
|
+
|
|
189
|
+
async function processBlogSourceFile(
|
|
190
|
+
blogSourceRelative: string,
|
|
111
191
|
contentPaths: BlogContentPaths,
|
|
112
|
-
|
|
192
|
+
context: LoadContext,
|
|
113
193
|
options: PluginOptions,
|
|
114
|
-
|
|
194
|
+
authorsMap?: AuthorsMap,
|
|
195
|
+
): Promise<BlogPost | undefined> {
|
|
196
|
+
const {
|
|
197
|
+
siteConfig: {baseUrl},
|
|
198
|
+
siteDir,
|
|
199
|
+
i18n,
|
|
200
|
+
} = context;
|
|
115
201
|
const {
|
|
116
|
-
include,
|
|
117
202
|
routeBasePath,
|
|
203
|
+
tagsBasePath: tagsRouteBasePath,
|
|
118
204
|
truncateMarker,
|
|
119
205
|
showReadingTime,
|
|
120
206
|
editUrl,
|
|
121
207
|
} = options;
|
|
122
208
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const blogSourceFiles = await globby(include, {
|
|
129
|
-
cwd: contentPaths.contentPath,
|
|
130
|
-
});
|
|
209
|
+
// Lookup in localized folder in priority
|
|
210
|
+
const blogDirPath = await getFolderContainingFile(
|
|
211
|
+
getContentPathList(contentPaths),
|
|
212
|
+
blogSourceRelative,
|
|
213
|
+
);
|
|
131
214
|
|
|
132
|
-
const
|
|
215
|
+
const blogSourceAbsolute = path.join(blogDirPath, blogSourceRelative);
|
|
133
216
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
// Lookup in localized folder in priority
|
|
137
|
-
const blogDirPath = await getFolderContainingFile(
|
|
138
|
-
getContentPathList(contentPaths),
|
|
139
|
-
blogSourceFile,
|
|
140
|
-
);
|
|
217
|
+
const {frontMatter, content, contentTitle, excerpt} =
|
|
218
|
+
await parseBlogPostMarkdownFile(blogSourceAbsolute);
|
|
141
219
|
|
|
142
|
-
|
|
220
|
+
const aliasedSource = aliasedSitePath(blogSourceAbsolute, siteDir);
|
|
143
221
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
contentTitle,
|
|
148
|
-
excerpt,
|
|
149
|
-
} = await parseMarkdownFile(source);
|
|
150
|
-
const frontMatter = validateBlogPostFrontMatter(unsafeFrontMatter);
|
|
222
|
+
if (frontMatter.draft && process.env.NODE_ENV === 'production') {
|
|
223
|
+
return undefined;
|
|
224
|
+
}
|
|
151
225
|
|
|
152
|
-
|
|
226
|
+
if (frontMatter.id) {
|
|
227
|
+
logger.warn`name=${'id'} header option is deprecated in path=${blogSourceRelative} file. Please use name=${'slug'} option instead.`;
|
|
228
|
+
}
|
|
153
229
|
|
|
154
|
-
|
|
230
|
+
const parsedBlogFileName = parseBlogFileName(blogSourceRelative);
|
|
155
231
|
|
|
156
|
-
|
|
157
|
-
|
|
232
|
+
async function getDate(): Promise<Date> {
|
|
233
|
+
// Prefer user-defined date.
|
|
234
|
+
if (frontMatter.date) {
|
|
235
|
+
if (typeof frontMatter.date === 'string') {
|
|
236
|
+
// Always treat dates as UTC by adding the `Z`
|
|
237
|
+
return new Date(`${frontMatter.date}Z`);
|
|
158
238
|
}
|
|
239
|
+
// YAML only converts YYYY-MM-DD to dates and leaves others as strings.
|
|
240
|
+
return frontMatter.date;
|
|
241
|
+
} else if (parsedBlogFileName.date) {
|
|
242
|
+
return parsedBlogFileName.date;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
try {
|
|
246
|
+
const result = getFileCommitDate(blogSourceAbsolute, {
|
|
247
|
+
age: 'oldest',
|
|
248
|
+
includeAuthor: false,
|
|
249
|
+
});
|
|
250
|
+
return result.date;
|
|
251
|
+
} catch (err) {
|
|
252
|
+
logger.warn(err);
|
|
253
|
+
return (await fs.stat(blogSourceAbsolute)).birthtime;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
159
256
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
}
|
|
257
|
+
const date = await getDate();
|
|
258
|
+
const formattedDate = formatBlogPostDate(
|
|
259
|
+
i18n.currentLocale,
|
|
260
|
+
date,
|
|
261
|
+
i18n.localeConfigs[i18n.currentLocale]!.calendar,
|
|
262
|
+
);
|
|
167
263
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const dateFilenameMatch = blogFileName.match(DATE_FILENAME_PATTERN);
|
|
171
|
-
let linkName = blogFileName.replace(/\.mdx?$/, '');
|
|
264
|
+
const title = frontMatter.title ?? contentTitle ?? parsedBlogFileName.text;
|
|
265
|
+
const description = frontMatter.description ?? excerpt ?? '';
|
|
172
266
|
|
|
173
|
-
|
|
174
|
-
const [, dateString, name] = dateFilenameMatch;
|
|
175
|
-
date = new Date(dateString);
|
|
176
|
-
linkName = name;
|
|
177
|
-
}
|
|
267
|
+
const slug = frontMatter.slug ?? parsedBlogFileName.slug;
|
|
178
268
|
|
|
179
|
-
|
|
180
|
-
if (frontMatter.date) {
|
|
181
|
-
date = frontMatter.date;
|
|
182
|
-
}
|
|
269
|
+
const permalink = normalizeUrl([baseUrl, routeBasePath, slug]);
|
|
183
270
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
year: 'numeric',
|
|
190
|
-
}).format(date);
|
|
191
|
-
|
|
192
|
-
const title = frontMatter.title ?? contentTitle ?? linkName;
|
|
193
|
-
const description = frontMatter.description ?? excerpt ?? '';
|
|
194
|
-
|
|
195
|
-
const slug =
|
|
196
|
-
frontMatter.slug ||
|
|
197
|
-
(dateFilenameMatch ? toUrl({date, link: linkName}) : linkName);
|
|
198
|
-
|
|
199
|
-
const permalink = normalizeUrl([baseUrl, routeBasePath, slug]);
|
|
200
|
-
|
|
201
|
-
function getBlogEditUrl() {
|
|
202
|
-
const blogPathRelative = path.relative(
|
|
203
|
-
blogDirPath,
|
|
204
|
-
path.resolve(source),
|
|
205
|
-
);
|
|
206
|
-
|
|
207
|
-
if (typeof editUrl === 'function') {
|
|
208
|
-
return editUrl({
|
|
209
|
-
blogDirPath: posixPath(path.relative(siteDir, blogDirPath)),
|
|
210
|
-
blogPath: posixPath(blogPathRelative),
|
|
211
|
-
permalink,
|
|
212
|
-
locale: i18n.currentLocale,
|
|
213
|
-
});
|
|
214
|
-
} else if (typeof editUrl === 'string') {
|
|
215
|
-
const isLocalized = blogDirPath === contentPaths.contentPathLocalized;
|
|
216
|
-
const fileContentPath =
|
|
217
|
-
isLocalized && options.editLocalizedFiles
|
|
218
|
-
? contentPaths.contentPathLocalized
|
|
219
|
-
: contentPaths.contentPath;
|
|
220
|
-
|
|
221
|
-
const contentPathEditUrl = normalizeUrl([
|
|
222
|
-
editUrl,
|
|
223
|
-
posixPath(path.relative(siteDir, fileContentPath)),
|
|
224
|
-
]);
|
|
225
|
-
|
|
226
|
-
return getEditUrl(blogPathRelative, contentPathEditUrl);
|
|
227
|
-
} else {
|
|
228
|
-
return undefined;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
271
|
+
function getBlogEditUrl() {
|
|
272
|
+
const blogPathRelative = path.relative(
|
|
273
|
+
blogDirPath,
|
|
274
|
+
path.resolve(blogSourceAbsolute),
|
|
275
|
+
);
|
|
231
276
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
title,
|
|
239
|
-
description,
|
|
240
|
-
date,
|
|
241
|
-
formattedDate,
|
|
242
|
-
tags: frontMatter.tags ?? [],
|
|
243
|
-
readingTime: showReadingTime
|
|
244
|
-
? readingTime(content).minutes
|
|
245
|
-
: undefined,
|
|
246
|
-
truncated: truncateMarker?.test(content) || false,
|
|
247
|
-
},
|
|
277
|
+
if (typeof editUrl === 'function') {
|
|
278
|
+
return editUrl({
|
|
279
|
+
blogDirPath: posixPath(path.relative(siteDir, blogDirPath)),
|
|
280
|
+
blogPath: posixPath(blogPathRelative),
|
|
281
|
+
permalink,
|
|
282
|
+
locale: i18n.currentLocale,
|
|
248
283
|
});
|
|
249
|
-
})
|
|
250
|
-
|
|
284
|
+
} else if (typeof editUrl === 'string') {
|
|
285
|
+
const isLocalized = blogDirPath === contentPaths.contentPathLocalized;
|
|
286
|
+
const fileContentPath =
|
|
287
|
+
isLocalized && options.editLocalizedFiles
|
|
288
|
+
? contentPaths.contentPathLocalized
|
|
289
|
+
: contentPaths.contentPath;
|
|
290
|
+
|
|
291
|
+
const contentPathEditUrl = normalizeUrl([
|
|
292
|
+
editUrl,
|
|
293
|
+
posixPath(path.relative(siteDir, fileContentPath)),
|
|
294
|
+
]);
|
|
295
|
+
|
|
296
|
+
return getEditUrl(blogPathRelative, contentPathEditUrl);
|
|
297
|
+
}
|
|
298
|
+
return undefined;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const tagsBasePath = normalizeUrl([
|
|
302
|
+
baseUrl,
|
|
303
|
+
routeBasePath,
|
|
304
|
+
tagsRouteBasePath,
|
|
305
|
+
]);
|
|
306
|
+
const authors = getBlogPostAuthors({authorsMap, frontMatter});
|
|
307
|
+
|
|
308
|
+
return {
|
|
309
|
+
id: slug,
|
|
310
|
+
metadata: {
|
|
311
|
+
permalink,
|
|
312
|
+
editUrl: getBlogEditUrl(),
|
|
313
|
+
source: aliasedSource,
|
|
314
|
+
title,
|
|
315
|
+
description,
|
|
316
|
+
date,
|
|
317
|
+
formattedDate,
|
|
318
|
+
tags: normalizeFrontMatterTags(tagsBasePath, frontMatter.tags),
|
|
319
|
+
readingTime: showReadingTime
|
|
320
|
+
? options.readingTime({
|
|
321
|
+
content,
|
|
322
|
+
frontMatter,
|
|
323
|
+
defaultReadingTime,
|
|
324
|
+
})
|
|
325
|
+
: undefined,
|
|
326
|
+
hasTruncateMarker: truncateMarker.test(content),
|
|
327
|
+
authors,
|
|
328
|
+
frontMatter,
|
|
329
|
+
},
|
|
330
|
+
content,
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export async function generateBlogPosts(
|
|
335
|
+
contentPaths: BlogContentPaths,
|
|
336
|
+
context: LoadContext,
|
|
337
|
+
options: PluginOptions,
|
|
338
|
+
): Promise<BlogPost[]> {
|
|
339
|
+
const {include, exclude} = options;
|
|
340
|
+
|
|
341
|
+
if (!(await fs.pathExists(contentPaths.contentPath))) {
|
|
342
|
+
return [];
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const blogSourceFiles = await Globby(include, {
|
|
346
|
+
cwd: contentPaths.contentPath,
|
|
347
|
+
ignore: exclude,
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
const authorsMap = await getAuthorsMap({
|
|
351
|
+
contentPaths,
|
|
352
|
+
authorsMapPath: options.authorsMapPath,
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
const blogPosts = (
|
|
356
|
+
await Promise.all(
|
|
357
|
+
blogSourceFiles.map(async (blogSourceFile: string) => {
|
|
358
|
+
try {
|
|
359
|
+
return await processBlogSourceFile(
|
|
360
|
+
blogSourceFile,
|
|
361
|
+
contentPaths,
|
|
362
|
+
context,
|
|
363
|
+
options,
|
|
364
|
+
authorsMap,
|
|
365
|
+
);
|
|
366
|
+
} catch (err) {
|
|
367
|
+
logger.error`Processing of blog source file path=${blogSourceFile} failed.`;
|
|
368
|
+
throw err;
|
|
369
|
+
}
|
|
370
|
+
}),
|
|
371
|
+
)
|
|
372
|
+
).filter(Boolean) as BlogPost[];
|
|
251
373
|
|
|
252
374
|
blogPosts.sort(
|
|
253
375
|
(a, b) => b.metadata.date.getTime() - a.metadata.date.getTime(),
|
|
254
376
|
);
|
|
255
377
|
|
|
378
|
+
if (options.sortPosts === 'ascending') {
|
|
379
|
+
return blogPosts.reverse();
|
|
380
|
+
}
|
|
256
381
|
return blogPosts;
|
|
257
382
|
}
|
|
258
383
|
|
|
@@ -284,8 +409,3 @@ export function linkify({
|
|
|
284
409
|
|
|
285
410
|
return newContent;
|
|
286
411
|
}
|
|
287
|
-
|
|
288
|
-
// Order matters: we look in priority in localized folder
|
|
289
|
-
export function getContentPathList(contentPaths: BlogContentPaths): string[] {
|
|
290
|
-
return [contentPaths.contentPathLocalized, contentPaths.contentPath];
|
|
291
|
-
}
|