Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions scripts/data/banners.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Builds the banner config doc-kit fetches at runtime, pointing it at the
// latest webpack release .

import { readdir, readFile } from 'node:fs/promises';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import matter from 'gray-matter';
import { major, minor } from 'semver';

const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..');
const POSTS_DIR = join(ROOT, 'pages', 'blog', 'posts');

// How long the banner stays up after a release.
const BANNER_DURATION_DAYS = 7;

const titleFromBody = body => body.match(/^#\s+(.+)$/m)?.[1].trim() ?? null;

const isRelease = category =>
typeof category === 'string' && category.toLowerCase() === 'release';

// Reads `category: Release` blog posts, newest first.
const readReleasePosts = async () => {
const files = (await readdir(POSTS_DIR)).filter(name => name.endsWith('.md'));

const posts = await Promise.all(
files.map(async file => {
const { data, content } = matter(
await readFile(join(POSTS_DIR, file), 'utf8')
);

return isRelease(data.category)
? {
slug: file.replace(/\.md$/, ''),
title: titleFromBody(content),
date: new Date(data.date),
}
: null;
})
);

return posts.filter(Boolean).sort((a, b) => b.date - a.date);
};

// Builds the banner config for `version` (e.g. `v5.107.2`): links to the
// matching release post, or the newest one if none matches.
export const buildWebsiteBanners = async version => {
const releases = await readReleasePosts();

if (!releases.length) {
return { websiteBanners: {} };
}

const series = new RegExp(`\\b${major(version)}\\.${minor(version)}\\b`);
const release =
releases.find(post => post.title && series.test(post.title)) ?? releases[0];

const endDate = new Date(release.date);
endDate.setUTCDate(endDate.getUTCDate() + BANNER_DURATION_DAYS);

return {
websiteBanners: {
index: {
text: `webpack ${version} is now available - read the release notes`,
link: `/blog/posts/${release.slug}`,
type: 'default',
startDate: release.date.toISOString(),
endDate: endDate.toISOString(),
},
},
};
};
2 changes: 1 addition & 1 deletion scripts/html/doc-kit.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export default {
web: {
project: 'webpack',
useAbsoluteURLs: true,
remoteConfigUrl: null,
remoteConfigUrl: '/banners.json',
title: VERSION ? `Webpack ${MAJOR_VERSION} Documentation` : 'Webpack',
editURL:
'https://github.com/webpack/webpack-doc-kit/blob/main/pages/{path}.md',
Expand Down
9 changes: 8 additions & 1 deletion scripts/html/index.mjs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { execFile } from 'node:child_process';
import { readFile, cp } from 'node:fs/promises';
import { readFile, writeFile, cp } from 'node:fs/promises';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { promisify } from 'node:util';
import { buildWebsiteBanners } from '../data/banners.mjs';

const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..');

const ASSETS_SOURCE = join(ROOT, 'assets');
const ASSETS_DESTINATION = join(ROOT, 'out/assets');
const BANNERS_DESTINATION = join(ROOT, 'out/banners.json');

const execFileAsync = promisify(execFile);

Expand Down Expand Up @@ -48,3 +50,8 @@ await runDocKit();
// copy assets folder to the out directory

await cp(ASSETS_SOURCE, ASSETS_DESTINATION, { recursive: true });

// the announcement banner config advertising the latest webpack release

const banners = await buildWebsiteBanners(versions[0]);
await writeFile(BANNERS_DESTINATION, `${JSON.stringify(banners, null, 2)}\n`);
Loading