github.com/kubri/kubri@v0.5.1-0.20240317001612-bda2aaef967e/website/src/plugins/changelog/index.js (about)

     1  /**
     2   * Copyright (c) Facebook, Inc. and its affiliates.
     3   *
     4   * This source code is licensed under the MIT license found in the
     5   * LICENSE file in the root directory of this source tree.
     6   */
     7  
     8  import path from 'path'
     9  import fs from 'fs-extra'
    10  import pluginContentBlog from '@docusaurus/plugin-content-blog'
    11  import { aliasedSitePath, docuHash, normalizeUrl } from '@docusaurus/utils'
    12  
    13  /**
    14   * Multiple versions may be published on the same day, causing the order to be
    15   * the reverse. Therefore, our publish time has a "fake hour" to order them.
    16   */
    17  const publishTimes = new Set()
    18  /**
    19   * @type {Record<string, {name: string, url: string,alias: string, imageURL: string}>}
    20   */
    21  const authorsMap = {}
    22  
    23  /**
    24   * @param {string} section
    25   */
    26  function processSection(section) {
    27    const title = section
    28      .match(/\n## .*/)?.[0]
    29      .trim()
    30      .replace('## ', '')
    31      .replace(/\[(.*)\]\(.*\) (.*)/, '$1 $2')
    32    if (!title) {
    33      return null
    34    }
    35    const content = section
    36      .replace(/\n## .*/, '')
    37      .trim()
    38      .replace('running_woman', 'running')
    39  
    40    let authors = content.match(/## Committers: \d.*/s)
    41    if (authors) {
    42      authors = authors[0]
    43        .match(/- .*/g)
    44        .map((line) => line.match(/- (?:(?<name>.*?) \()?\[@(?<alias>.*)\]\((?<url>.*?)\)\)?/).groups)
    45        .map((author) => ({
    46          ...author,
    47          name: author.name ?? author.alias,
    48          imageURL: `https://github.com/${author.alias}.png`,
    49        }))
    50        .sort((a, b) => a.url.localeCompare(b.url))
    51  
    52      authors.forEach((author) => {
    53        authorsMap[author.alias] = author
    54      })
    55    }
    56  
    57    let hour = 20
    58    const date = title.match(/ \((?<date>.*)\)/)?.groups.date
    59    while (publishTimes.has(`${date}T${hour}:00`)) {
    60      hour -= 1
    61    }
    62    publishTimes.add(`${date}T${hour}:00`)
    63  
    64    return {
    65      title: title.replace(/ \(.*\)/, ''),
    66      content: `---
    67  mdx:
    68   format: md
    69  date: ${`${date}T${hour}:00`}
    70  ${authors ? `authors:\n${authors.map((author) => `  - '${author.alias}'`).join('\n')}` : ''}
    71  ---
    72  
    73  # ${title.replace(/ \(.*\)/, '')}
    74  
    75  <!-- truncate -->
    76  
    77  ${content.replace(/####/g, '##')}`,
    78    }
    79  }
    80  
    81  /**
    82   * @param {import('@docusaurus/types').LoadContext} context
    83   * @returns {import('@docusaurus/types').Plugin}
    84   */
    85  export default async function ChangelogPlugin(context, options) {
    86    const generateDir = path.join(context.generatedFilesDir, 'changelog-plugin/source')
    87    const blogPlugin = await pluginContentBlog.default(context, {
    88      ...options,
    89      path: generateDir,
    90      id: 'changelog',
    91      blogListComponent: '@theme/ChangelogList',
    92      blogPostComponent: '@theme/ChangelogPage',
    93    })
    94    const changelogPath = path.join(__dirname, '../../../../CHANGELOG.md')
    95    return {
    96      ...blogPlugin,
    97      name: 'changelog-plugin',
    98      async loadContent() {
    99        await fs.remove(generateDir)
   100        const fileContent = await fs.readFile(changelogPath, 'utf-8')
   101        const sections = fileContent
   102          .split(/(?=\n## )/)
   103          .map(processSection)
   104          .filter(Boolean)
   105        await Promise.all(
   106          sections.map((section) =>
   107            fs.outputFile(path.join(generateDir, `${section.title}.md`), section.content),
   108          ),
   109        )
   110        const authorsPath = path.join(generateDir, 'authors.json')
   111        await fs.outputFile(authorsPath, JSON.stringify(authorsMap, null, 2))
   112        const content = await blogPlugin.loadContent()
   113        content.blogPosts.forEach((post, index) => {
   114          const pageIndex = Math.floor(index / options.postsPerPage)
   115          const { metadata } = post
   116          metadata.listPageLink = normalizeUrl([
   117            context.baseUrl,
   118            options.routeBasePath,
   119            pageIndex === 0 ? '/' : `/page/${pageIndex + 1}`,
   120          ])
   121        })
   122        return content
   123      },
   124      configureWebpack(...args) {
   125        const config = blogPlugin.configureWebpack(...args)
   126        const pluginDataDirRoot = path.join(context.generatedFilesDir, 'changelog-plugin', 'default')
   127        // Redirect the metadata path to our folder
   128        const mdxLoader = config.module.rules[0].use[0]
   129        mdxLoader.options.metadataPath = (mdxPath) => {
   130          // Note that metadataPath must be the same/in-sync as
   131          // the path from createData for each MDX.
   132          const aliasedPath = aliasedSitePath(mdxPath, context.siteDir)
   133          return path.join(pluginDataDirRoot, `${docuHash(aliasedPath)}.json`)
   134        }
   135        return config
   136      },
   137      getThemePath() {
   138        return './theme'
   139      },
   140      getPathsToWatch() {
   141        // Don't watch the generated dir
   142        return [changelogPath]
   143      },
   144    }
   145  }
   146  
   147  export const { validateOptions } = pluginContentBlog