Repository navigation
Migrate from Jekyll + Just the Docs to VitePress #423
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
bbd3403
Initial plan
Copilot d9ddaf9
Initial VitePress setup and content migration
Copilot 810930b
Complete VitePress configuration and workflow setup
Copilot 6465793
Add comprehensive migration documentation
Copilot 77b2c90
Fix .gitignore to exclude VitePress build artifacts
Copilot eba9852
Merge main into copilot/migrate-to-vitepress and complete the VitePre…
simonebortolin a4cb4cf
Use wrangler-action for the PR preview deploy
simonebortolin 5492d9f
Run the PR preview on pull_request
simonebortolin f794d5f
Show the hardware specifications in the aside, edit link next to the …
simonebortolin ba45aaf
Sidebar: expand arrow on the left of the items
simonebortolin 454e522
Edit link on the left of the page title, like the heading anchors
simonebortolin e03449f
Merge main into copilot/migrate-to-vitepress
simonebortolin 09b8244
Merge main into copilot/migrate-to-vitepress
simonebortolin ebfded4
Run the PR preview on pull_request_target again, with the authorize job
simonebortolin 95796df
Fix the review findings before the merge
simonebortolin d10a025
Add Dependabot for npm, fix the tables of the EEPROM decoder
simonebortolin File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| # To get started with Dependabot version updates, you'll need to specify which | ||
| # package ecosystems to update and where the package manifests are located. | ||
| # Please see the documentation for all configuration options: | ||
| # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file | ||
|
|
||
| version: 2 | ||
| updates: | ||
| - package-ecosystem: "npm" # See documentation for possible values | ||
| directory: "/" # Location of package manifests | ||
| schedule: | ||
| interval: "weekly" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,7 @@ | ||
| ### Jekyll ### | ||
| _site/ | ||
| .sass-cache/ | ||
| .jekyll-cache/ | ||
| .jekyll-metadata | ||
| Gemfile.lock | ||
| assets/js/zzzz-search-data.json | ||
| ### VitePress ### | ||
| .vitepress/dist/ | ||
| .vitepress/cache/ | ||
| .vitepress/.temp/ | ||
|
|
||
| ### Node ### | ||
| node_modules/ | ||
| assets/js/generated |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| import fs from 'node:fs' | ||
| import path from 'node:path' | ||
| import { createMarkdownRenderer, defineConfig, type MarkdownRenderer } from 'vitepress' | ||
| import { configureMarkdown, extractHardwareSpecs } from './markdown' | ||
| import { isExternal, pages, resolveLink, rewrites, sidebar, srcExclude } from './pages' | ||
|
|
||
| const hostname = 'https://hack-gpon.org' | ||
| const repository = 'https://github.com/hack-gpon/hack-gpon.github.io' | ||
|
|
||
| let specsRenderer: Promise<MarkdownRenderer> | undefined | ||
|
|
||
| const telegramIcon = | ||
| '<svg role="img" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><title>Telegram</title><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM8.287 5.906c-.778.324-2.334.994-4.666 2.01-.378.15-.577.298-.595.442-.03.243.275.339.69.47l.175.055c.408.133.958.288 1.243.294.26.006.549-.1.868-.32 2.179-1.471 3.304-2.214 3.374-2.23.05-.012.12-.026.166.016.047.041.042.12.037.141-.03.129-1.227 1.241-1.846 1.817-.193.18-.33.307-.358.336a8.154 8.154 0 0 1-.188.186c-.38.366-.664.64.015 1.088.327.216.589.393.85.571.284.194.568.387.936.629.093.06.183.125.27.187.331.236.63.448.997.414.214-.02.435-.22.547-.82.265-1.417.786-4.486.906-5.751a1.426 1.426 0 0 0-.013-.315.337.337 0 0 0-.114-.217.526.526 0 0 0-.31-.093c-.3.005-.763.166-2.984 1.09z"/></svg>' | ||
|
|
||
| export default defineConfig({ | ||
| lang: 'en-US', | ||
| title: 'Hack GPON', | ||
| description: 'Worldwide wiki on how to access, change and edit ONTs', | ||
|
|
||
| srcExclude, | ||
| rewrites, | ||
| cleanUrls: true, | ||
| lastUpdated: true, | ||
| sitemap: { | ||
| hostname, | ||
| // the redirect pages are not real pages (as with jekyll-redirect-from) | ||
| transformItems: (items) => { | ||
| const redirects = new Set(pages.filter((p) => p.frontmatter.redirect_to).map((p) => p.url.slice(1))) | ||
| return items.filter((item) => !redirects.has(decodeURI(item.url))) | ||
| } | ||
| }, | ||
|
|
||
| head: [ | ||
| ['link', { rel: 'icon', href: '/favicon.ico', sizes: '48x48' }], | ||
| ['link', { rel: 'icon', type: 'image/png', sizes: '32x32', href: '/favicon-32x32.png' }], | ||
| ['link', { rel: 'icon', type: 'image/png', sizes: '16x16', href: '/favicon-16x16.png' }], | ||
| ['link', { rel: 'apple-touch-icon', sizes: '180x180', href: '/apple-touch-icon.png' }], | ||
| ['link', { rel: 'manifest', href: '/site.webmanifest' }], | ||
| ['link', { rel: 'mask-icon', href: '/safari-pinned-tab.svg', color: '#27262b' }], | ||
| ['meta', { name: 'msapplication-TileColor', content: '#27262b' }], | ||
| ['meta', { name: 'theme-color', content: '#27262b' }] | ||
| ], | ||
|
|
||
| transformHead({ pageData }) { | ||
| const head: [string, Record<string, string>][] = [] | ||
| const page = pages.find((p) => p.file === pageData.filePath) | ||
| const redirect = pageData.frontmatter.redirect_to | ||
| if (redirect) { | ||
| const target = (page && resolveLink(page.url)) ?? redirect | ||
| head.push(['meta', { 'http-equiv': 'refresh', content: `0; url=${target}` }]) | ||
| head.push(['link', { rel: 'canonical', href: isExternal(target) ? target : hostname + target }]) | ||
| } else if (page) { | ||
| head.push(['link', { rel: 'canonical', href: hostname + page.url }]) | ||
| } | ||
| return head | ||
| }, | ||
|
|
||
| markdown: { | ||
| config: configureMarkdown, | ||
| // in dev the cache of the rendered pages is not cleared for the rewritten pages when a | ||
| // partial changes, so the page would not be updated | ||
| cache: process.argv[2] !== 'dev' | ||
| }, | ||
|
|
||
| // the "Hardware Specifications" table is also rendered in the aside, above the outline | ||
| async transformPageData(pageData, { siteConfig }) { | ||
| const file = path.join(siteConfig.srcDir, pageData.filePath) | ||
| if (!fs.existsSync(file)) return | ||
| const table = extractHardwareSpecs(fs.readFileSync(file, 'utf8')) | ||
| if (!table) return | ||
| specsRenderer ??= createMarkdownRenderer(siteConfig.srcDir, siteConfig.markdown, siteConfig.site.base, siteConfig.logger) | ||
| pageData.hardwareSpecs = (await specsRenderer).render(table, { path: file, relativePath: pageData.relativePath }) | ||
| }, | ||
|
|
||
| vite: { | ||
| build: { | ||
| // the big chunks (search index, mermaid, pages with serial dumps) are loaded on demand | ||
| chunkSizeWarningLimit: 1000 | ||
| } | ||
| }, | ||
|
|
||
| themeConfig: { | ||
| logo: { src: '/favicon-32x32.png', alt: '' }, | ||
|
|
||
| nav: [ | ||
| { text: 'Quick Start', link: '/quick-start/' }, | ||
| { text: 'FAQ', link: '/faq/' } | ||
| ], | ||
|
|
||
| sidebar: sidebar(), | ||
|
|
||
| socialLinks: [ | ||
| { icon: 'github', link: repository, ariaLabel: 'GitHub' }, | ||
| { icon: { svg: telegramIcon }, link: 'https://t.me/HackGPON', ariaLabel: 'Telegram' } | ||
| ], | ||
|
|
||
| editLink: { | ||
| pattern: `${repository}/edit/main/:path`, | ||
| text: 'Edit this page on GitHub' | ||
| }, | ||
|
|
||
| lastUpdated: { | ||
| text: 'Last Modified', | ||
| formatOptions: { dateStyle: 'medium' } | ||
| }, | ||
|
|
||
| search: { | ||
| provider: 'local', | ||
| options: { | ||
| _render(src, env, md) { | ||
| const html = md.render(src, env) | ||
| if (env.frontmatter?.search === false || env.frontmatter?.redirect_to) return '' | ||
| // footnote references in the headings would be taken as section anchors | ||
| return html.replace(/<sup class="footnote-ref">.*?<\/sup>/g, '') | ||
| } | ||
| } | ||
| }, | ||
|
|
||
| outline: { | ||
| level: [1, 3] | ||
| }, | ||
|
|
||
| docFooter: { | ||
| prev: false, | ||
| next: false | ||
| } | ||
| } | ||
| }) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| import fs from 'node:fs' | ||
| import path from 'node:path' | ||
| import { Liquid } from 'liquidjs' | ||
| import type MarkdownIt from 'markdown-it' | ||
| import footnote from 'markdown-it-footnote' | ||
| import { resolveLink } from './pages' | ||
|
|
||
| const liquid = new Liquid() | ||
|
|
||
| /** | ||
| * Parametrized partials. | ||
| * | ||
| * <!--@partial: ./_partials/ont-nokia-use.md | ||
| * username: "ONTUSER" | ||
| * --> | ||
| * | ||
| * The partial is a Liquid template, the parameters are available as `include.<name>`. | ||
| * Every parameter is on its own line: `name: value`, the value can be a JSON string. | ||
| */ | ||
| export function expandPartials(src: string, file: string, includes?: string[]): string { | ||
| return src.replace(/<!--\s*@partial:\s*(\S+?)\s*(?:\n([\s\S]*?))?-->/g, (_, target: string, rawParams = '') => { | ||
| const params: Record<string, unknown> = {} | ||
| for (const line of rawParams.split(/\r?\n/)) { | ||
| const kv = /^\s*([A-Za-z_][\w-]*):\s*(.*?)\s*$/.exec(line) | ||
| if (!kv) continue | ||
| try { | ||
| params[kv[1]] = JSON.parse(kv[2]) | ||
| } catch { | ||
| params[kv[1]] = kv[2] | ||
| } | ||
| } | ||
| const partialFile = path.resolve(path.dirname(file), target) | ||
| // registered as dependency of the page, like the VitePress includes: the dev server reloads it | ||
| includes?.push(partialFile.replace(/\\/g, '/')) | ||
| const template = fs.readFileSync(partialFile, 'utf8') | ||
| return liquid.parseAndRenderSync(template, { include: params }) | ||
| }) | ||
| } | ||
|
|
||
| /** | ||
| * Internal links point to the canonical URL of the page, with the trailing slash (as Jekyll did), | ||
| * and links to the redirect pages point directly to their destination. | ||
| */ | ||
| function canonicalLinks(md: MarkdownIt) { | ||
| md.core.ruler.push('canonical_links', (state) => { | ||
| for (const block of state.tokens) { | ||
| for (const token of block.children ?? []) { | ||
| if (token.type !== 'link_open') continue | ||
| const href = token.attrGet('href') | ||
| if (!href || !href.startsWith('/') || href.startsWith('//')) continue | ||
| const url = resolveLink(href) | ||
| if (url) token.attrSet('href', url) | ||
| } | ||
| } | ||
| }) | ||
| } | ||
|
|
||
| const hardwareSpecsHeading = /^#{1,6}[ \t]+Hardware Specifications[ \t]*$/m | ||
|
|
||
| /** Markdown of the table under the "Hardware Specifications" heading, shown in the aside. */ | ||
| export function extractHardwareSpecs(src: string): string | undefined { | ||
| const heading = hardwareSpecsHeading.exec(src) | ||
| if (!heading) return | ||
| const lines = src.slice(heading.index + heading[0].length).split(/\r?\n/) | ||
| let i = 0 | ||
| while (i < lines.length && lines[i].trim() === '') i++ | ||
| const table: string[] = [] | ||
| while (i < lines.length && lines[i].trim().startsWith('|')) table.push(lines[i++]) | ||
| return table.length > 2 ? table.join('\n') : undefined | ||
| } | ||
|
|
||
| /** Marks the "Hardware Specifications" table, hidden in the page when it is shown in the aside. */ | ||
| function hardwareSpecs(md: MarkdownIt) { | ||
| md.core.ruler.push('hardware_specs', (state) => { | ||
| const tokens = state.tokens | ||
| const heading = tokens.findIndex( | ||
| (t, i) => t.type === 'heading_open' && tokens[i + 1]?.content.trim() === 'Hardware Specifications' | ||
| ) | ||
| if (heading === -1) return | ||
| // the table must follow the heading directly | ||
| const table = tokens[heading + 3] | ||
| if (table?.type === 'table_open') table.attrJoin('class', 'hardware-specs') | ||
| }) | ||
| // the VitePress renderer of the tables drops the attributes | ||
| const tableOpen = md.renderer.rules.table_open! | ||
| md.renderer.rules.table_open = (tokens, idx, options, env, self) => { | ||
| const html = tableOpen(tokens, idx, options, env, self) | ||
| const cls = tokens[idx].attrGet('class') | ||
| return cls ? html.replace('<table', `<table class="${cls}"`) : html | ||
| } | ||
| } | ||
|
|
||
| /** ```mermaid code blocks are rendered client side by the <Mermaid> component. */ | ||
| function mermaid(md: MarkdownIt) { | ||
| const fence = md.renderer.rules.fence! | ||
| md.renderer.rules.fence = (tokens, idx, options, env, self) => { | ||
| const token = tokens[idx] | ||
| if (token.info.trim() === 'mermaid') { | ||
| return `<Mermaid code="${encodeURIComponent(token.content)}" />` | ||
| } | ||
| return fence(tokens, idx, options, env, self) | ||
| } | ||
| } | ||
|
|
||
| export function configureMarkdown(md: MarkdownIt) { | ||
| const parse = md.parse.bind(md) | ||
| md.parse = (src, env) => { | ||
| const file = env?.realPath ?? env?.path | ||
| return parse(file ? expandPartials(src, file, env.includes) : src, env) | ||
| } | ||
| md.use(footnote) | ||
| md.use(canonicalLinks) | ||
| md.use(hardwareSpecs) | ||
| md.use(mermaid) | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.