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