Sat Apr 12 2025
Tucker McKnight
Previously, all pages were inside of a repository. Now, there is a repository that can contain many branches. The branches are what actually have a darcs repo in them. The URL for all pages should now be: /repos/reponame/branches/branchname/page.html Flattened data (like flatPatches and flatFiles) now has the branch name included in it -- there were not major changes needed there. The paginated patches object needs to sort into pages by matching on the repo name and the branch name. The main repos object now has the repository name nested under it, and the branch name(s) underneath that, and then the files and patches nested underneath the branch name. Anything that was previously getting information out of that repo object will now need to look under the branch name, too. (E.g. getFileContents will need to know the branch name in order to get the file contents.) addfile ./_data/branches.js
eae6f68caf35f1258e33fe13e09b32104b229dc5
import repos from './repos.js'
export default async () => {
const result = await repos()
return Object.keys(result).flatMap((repoName) => {
return Object.keys(result[repoName]).map((branchName) => {
return {
branchName,
repoName,
}
})
})
}
export default () => {
return {
baseUrl: darcsConfig.baseUrl,
}
}
export default darcsConfig
return result[repoName].files.map((file) => {
return {
file,
repoName,
}
return Object.keys(result[repoName]).flatMap((branchName) => {
return result[repoName][branchName].files.map((file) => {
return {
file,
branchName,
repoName,
}
})
return result[repoName].patches.map((patch) => {
return {
patch,
repoName,
}
return Object.keys(result[repoName]).flatMap((branchName) => {
return result[repoName][branchName].patches.map((patch) => {
return {
patch,
branchName,
repoName,
}
})
return page.repoName === patch.repoName
&& page.patches.length <= patchesPerPage
return (
page.repoName === patch.repoName
&& page.branchName == patch.branchName
&& page.patches.length <= patchesPerPage
)
const pageNumber = paginatedPatches.filter(page => page.repoName === patch.repoName ).length + 1
const pageNumber = paginatedPatches.filter(page => (page.repoName === patch.repoName && page.branchName === patch.branchName)).length + 1
branchName: patch.branchName,
const repoLocation = darcsConfig.repos[repoName].location
const filesRes = await exec(`(cd ${repoLocation}; darcs show files)`)
const files = filesRes.stdout.split("\n").filter(item => item.length > 0 && item != ".")
const patches = new Map()
const totalPatchesCountRes = await exec(`(cd ${repoLocation}; darcs log --count)`)
const totalPatchesCount = parseInt(totalPatchesCountRes.stdout)
let hunkRegex = RegExp(/^ *hunk /)
// Get 100 patches at a time and parse those
for (let i = 1; i <= totalPatchesCount; i = i + 100) {
let patchesSubset = await exec(`(cd ${repoLocation}; darcs log --index=${i}-${i+100} -v)`)
patchesSubset = patchesSubset.stdout.split("\n")
do {
const nextPatchStart = patchesSubset.findIndex((line, index) => {
return index > 0 && line.startsWith("patch ")
})
const currentPatch = patchesSubset.slice(0, nextPatchStart - 1)
patchesSubset = patchesSubset.slice(nextPatchStart)
const hash = currentPatch[0].replace("patch ", "").trim()
const author = currentPatch[1].replace("Author: ", "").trim()
const date = currentPatch[2].replace("Date: ", "").trim()
const name = currentPatch[3].replace(" * ", "").trim()
const diffStart = currentPatch.findIndex((line) => {
return line.match(hunkRegex)
})
const description = currentPatch.slice(5, diffStart).map(str => str.replace(" ", "")).join("\n").trim()
const diffs = getDiffsFromPatchText(currentPatch.slice(diffStart).map(str => str.trimStart()).join("\n"))
patches.set(hash, {
name,
description,
author,
date,
hash,
diffs,
})
} while (patchesSubset.length > 1)
}
const info = {
files,
patches: [...patches.values()],
}
return [repoName, info]
const branchNames = Object.keys(darcsConfig.repos[repoName].branches)
const branches = await Promise.all(branchNames.map(async (branchName) => {
const repoLocation = darcsConfig.repos[repoName].branches[branchName].location
const filesRes = await exec(`(cd ${repoLocation}; darcs show files)`)
const files = filesRes.stdout.split("\n").filter(item => item.length > 0 && item != ".")
const patches = new Map()
const totalPatchesCountRes = await exec(`(cd ${repoLocation}; darcs log --count)`)
const totalPatchesCount = parseInt(totalPatchesCountRes.stdout)
let hunkRegex = RegExp(/^ *hunk /)
// Get 100 patches at a time and parse those
for (let i = 1; i <= totalPatchesCount; i = i + 100) {
let patchesSubset = await exec(`(cd ${repoLocation}; darcs log --index=${i}-${i+100} -v)`)
patchesSubset = patchesSubset.stdout.split("\n")
do {
const nextPatchStart = patchesSubset.findIndex((line, index) => {
return index > 0 && line.startsWith("patch ")
})
const currentPatch = patchesSubset.slice(0, nextPatchStart - 1)
patchesSubset = patchesSubset.slice(nextPatchStart)
const hash = currentPatch[0].replace("patch ", "").trim()
const author = currentPatch[1].replace("Author: ", "").trim()
const date = currentPatch[2].replace("Date: ", "").trim()
const name = currentPatch[3].replace(" * ", "").trim()
const diffStart = currentPatch.findIndex((line) => {
return line.match(hunkRegex)
})
const description = currentPatch.slice(5, diffStart).map(str => str.replace(" ", "")).join("\n").trim()
const diffs = getDiffsFromPatchText(currentPatch.slice(diffStart).map(str => str.trimStart()).join("\n"))
patches.set(hash, {
name,
description,
author,
date,
hash,
diffs,
})
} while (patchesSubset.length > 1)
}
const info = {
files,
patches: [...patches.values()],
}
return [branchName, info]
}))
const branchObject = {}
branches.forEach((branch) => {
branchObject[branch[0]] = branch[1]
})
return [repoName, branchObject]
const reposObject = {}
repos.forEach((repo) => {
reposObject[repo[0]] = repo[1]
})
repos = reposObject
const returnMe = {}
repos.forEach((repo) => {
returnMe[repo[0]] = repo[1]
})
return returnMe
return repos
<html>
<a href="/" class="text-decoration-none">Darcs Repositories</a>{% if nav.repoName %}<span class="text-secondary mx-2">></span><a href="/repos/{{nav.repoName }}" class="text-decoration-none">eleventy-darcs</a >{% endif %}
<a href="/" class="text-decoration-none">Darcs Repositories</a>{% if nav.repoName %}<span class="text-secondary mx-2">></span><a href="/repos/{{nav.repoName | slugify}}/branches/{{darcsConfig.repos[nav.repoName].defaultBranch | slugify}}" class="text-decoration-none">{{nav.repoName}}</a>{% endif %}{% if nav.branchName %}<span class="text-secondary mx-2">></span><a class="text-decoration-none" href="/repos/{{nav.repoName | slugify}}/branches/{{nav.branchName | slugify}}">{{nav.branchName}}</a>{% endif %}
<a class="nav-link {% if navTab == "landing" %}active{% endif %}" href="/repos/{{nav.repoName | slugify}}">Landing Page</a>
<a class="nav-link {% if navTab == "landing" %}active{% endif %}" href="/repos/{{nav.repoName | slugify}}/branches/{{nav.branchName}}">Landing Page</a>
<a class="nav-link {% if navTab == "files" %}active{% endif %}" href="/repos/{{nav.repoName | slugify}}/files">Files</a>
<a class="nav-link {% if navTab == "files" %}active{% endif %}" href="/repos/{{nav.repoName | slugify}}/branches/{{nav.branchName}}/files">Files</a>
<a class="nav-link {% if navTab == "patches" %}active{% endif %}" href="/repos/{{nav.repoName | slugify}}/patches/page1">Patches</a>
<a class="nav-link {% if navTab == "patches" %}active{% endif %}" href="/repos/{{nav.repoName | slugify}}/branches/{{nav.branchName}}/patches/page1">Patches</a>
"eleventy-darcs": { // <-- Edit this to be the name of your local repository
location: "./", // <-- Make this the relative path to that repository.
// You might want a "../" at the beginning to go up // one directory.
// Change this "eleventy-darcs" key to be the name of your project
"Eleventy Darcs": {
defaultBranch: 'eleventy-darcs-main',
branches: {
'eleventy-darcs-main': {
// Set the 'location' to be the relative path to that repository.
// You might want a "../" at the beginning to go up one level.
location: "./",
},
},
// Add things to "languageExtensions" to tell the syntax highlighter what to do
// with file extensions that it might not know by default. "njk": "html" means
// to highlight a .njk file like a .html file.
// "another-repository": {
// location: "./another/relative/path"
// },
baseUrl : "https://repos.tuckerm.us", // <-- Change this to your deployed domain
// if this site is public.
// Change the baseUrl to be your real domain name when when deploying this publicly.
baseUrl: "https://repos.tuckerm.us",
passthroughs[`${darcsConfig.repos[repoName].location}/_darcs`] = `repos/${repoName}/_darcs`
Object.keys(darcsConfig.repos[repoName].branches).forEach((branchName) => {
passthroughs[`${darcsConfig.repos[repoName].branches[branchName].location}/_darcs`] = `repos/${eleventyConfig.getFilter("slugify")(repoName)}/branches/${branchName}/_darcs`
})
eleventyConfig.addAsyncFilter("getReadMe", async (repoName) => {
const res = await exec(`(cd ${darcsConfig.repos[repoName].location}; darcs show contents README.md)`)
eleventyConfig.addAsyncFilter("getReadMe", async (repoName, branchName) => {
const res = await exec(`(cd ${darcsConfig.repos[repoName].branches[branchName].location}; darcs show contents README.md)`)
eleventyConfig.addAsyncFilter("isDirectory", async(repo, filename) => {
eleventyConfig.addAsyncFilter("isDirectory", async(repo, branch, filename) => {
const fileInfo = await fs.stat(`${darcsConfig.repos[repo].location}/${filename}`)
const fileInfo = await fs.stat(`${darcsConfig.repos[repo].branches[branch].location}/${filename}`)
eleventyConfig.addFilter("getDirectoryContents", (repo, dirPath) => {
return repos[repo].files.filter(file => file.startsWith(dirPath) && file !== dirPath)
eleventyConfig.addFilter("getDirectoryContents", (repo, branch, dirPath) => {
return repos[repo][branch].files.filter(file => file.startsWith(dirPath) && file !== dirPath)
eleventyConfig.addAsyncFilter("getFileContents", async (repo, filename) => {
const res = await exec(`(cd ${darcsConfig.repos[repo].location}; darcs show contents ${filename})`)
eleventyConfig.addAsyncFilter("getFileContents", async (repo, branch, filename) => {
const res = await exec(`(cd ${darcsConfig.repos[repo].branches[branch].location}; darcs show contents ${filename})`)
data: repos
data: branches
alias: repo
permalink: "repos/{{repo | slugify }}/patches.xml"
alias: branch
permalink: "repos/{{branch.repoName | slugify }}/branches/{{branch.branchName}}/patches.xml"
<title>Latest patches in {{repo}}</title>
<link href="{{ ('/repos/' + (repo | slugify) + '/patches.xml')| htmlBaseUrl(darcsConfig.baseUrl) }}" rel="self" />
<link href="{{ ('/repos/' + (repo | slugify) )| htmlBaseUrl(darcsConfig.baseUrl) }}" />
{% set lastPatch = repos[repo].patches | last %}
<title>Latest patches in {{branch.branchName}}</title>
<link href="{{ ('/repos/' + (branch.repoName | slugify) + '/branches/' + (branch.branchName | slugify) + '/patches.xml')| htmlBaseUrl(darcsConfig.baseUrl) }}" rel="self" />
<link href="{{ ('/repos/' + (branch.repoName | slugify) + '/branches/' + (branch.branchName | slugify)) | htmlBaseUrl(darcsConfig.baseUrl) }}" />
{% set lastPatch = repos[branch.repoName][branch.branchName].patches | last %}
<name>{{repo}} contributors</name>
<name>{{branch.repoName}} contributors</name>
{%- for patch in repos[repo].patches | reverse %}
{%- set absolutePostUrl %}{{ ('/repos/' + repo + '/patches/' + patch.hash) | htmlBaseUrl(darcsConfig.baseUrl) }}{% endset %}
{%- for patch in repos[branch.repoName][branch.branchName].patches | reverse %}
{%- set absolutePostUrl %}{{ ('/repos/' + (branch.repoName | slugify) + '/branches/' + (branch.branchName | slugify) + '/patches/' + patch.hash) | htmlBaseUrl(darcsConfig.baseUrl) }}{% endset %}
},
branchName: (data) => {
return data.fileInfo.branchName
permalink: "repos/{{fileInfo.repoName | slugify}}/files/{{fileInfo.file}}.html"
permalink: "repos/{{fileInfo.repoName | slugify}}/branches/{{fileInfo.branchName}}/files/{{fileInfo.file}}.html"
{% if fileInfo.repoName | isDirectory(fileInfo.file) %}
{% if fileInfo.repoName | isDirectory(fileInfo.branchName, fileInfo.file) %}
{% set dirs = fileInfo.repoName | getDirectoryContents(fileInfo.file) %}
{% set dirs = fileInfo.repoName | getDirectoryContents(fileInfo.branchName, fileInfo.file) %}
<li><a href="/repos/{{fileInfo.repoName | slugify}}/files/{{dir}}.html">{{fileInfo.file | getRelativePath(dir)}}</a></li>
<li><a href="/repos/{{fileInfo.repoName | slugify}}/branches/{{fileInfo.branchName | slugify}}/files/{{dir}}.html">{{fileInfo.file | getRelativePath(dir)}}</a></li>
<pre class="code"><code class="language-{{fileInfo.file | languageExtension(fileInfo.repoName)}}">{{ fileInfo.repoName | getFileContents(fileInfo.file) }}</code></pre>
<pre><code class="line-numbers language-{{fileInfo.file | languageExtension(fileInfo.repoName)}}">{{ fileInfo.repoName | getFileContents(fileInfo.branchName, fileInfo.file) }}</code></pre>
return data.repo
return data.branchInfo.repoName
},
branchName: (data) => {
return data.branchInfo.branchName
data: repos
data: branches
alias: repo
permalink: "repos/{{repo | slugify}}/files/"
alias: branchInfo
permalink: "repos/{{branchInfo.repoName | slugify}}/branches/{{branchInfo.branchName}}/files/"
{% set files = repos[repo].files | topLevelFilesOnly %}
{% set files = repos[branchInfo.repoName][branchInfo.branchName].files | topLevelFilesOnly %}
<li><a href="/repos/{{repo | slugify}}/files/{{file}}.html">{{file}}</a></li>
<li><a href="/repos/{{branchInfo.repoName | slugify}}/branches/{{branchInfo.branchName}}/files/{{file}}.html">{{file}}</a></li>
<li><a href="/repos/{{repoName | slugify}}">{{repoName}}</a></li>
<li><a href="/repos/{{repoName | slugify}}/branches/{{darcsConfig.repos[repoName].defaultBranch}}">{{repoName}}</a></li>
},
branchName: (data) => {
return data.patchInfo.branchName
permalink: "repos/{{patchInfo.repoName | slugify}}/patches/{{patchInfo.patch.hash}}/"
permalink: "repos/{{patchInfo.repoName | slugify}}/branches/{{patchInfo.branchName | slugify}}/patches/{{patchInfo.patch.hash}}/"
{% set url = [darcsConfig.baseUrl, "/repos/", patchInfo.repoName | slugify] | join | url %}
{% set url = [darcsConfig.baseUrl, "/repos/", patchInfo.repoName | slugify, "/branches/", patchInfo.branchName | slugify] | join | url %}
<span class="font-monospace fw-bold"><a href="/repos/{{patchInfo.repoName }}/files/{{ hunk.file }}.html">{{ hunk.file }}:{{ hunk.lineNumber }}</a></span>
<span class="font-monospace fw-bold"><a href="/repos/{{patchInfo.repoName | slugify}}/branches/{{patchInfo.branchName | slugify}}/files/{{ hunk.file }}.html">{{ hunk.file }}:{{ hunk.lineNumber }}</a></span>
<pre class='line-numbers'data-start="{{hunk.lineNumber}}"><code data-type="before" class="language-{{hunk.file | languageExtension(patchInfo.repoName)}}">{{hunk.previousText | safe}}</code></pre>
<pre><code data-start="{{hunk.lineNumber}}" data-type="before" class="line-numbers language-{{hunk.file | languageExtension(patchInfo.repoName)}}">{{hunk.previousText | safe}}</code></pre>
<pre class='line-numbers'data-start="{{hunk.lineNumber}}"><code data-type="after" class="language-{{hunk.file | languageExtension(patchInfo.repoName)}}">{{ hunk.afterText | safe}}</code></pre>
<pre><code data-type="after" data-start="{{hunk.lineNumber}}" class="line-numbers language-{{hunk.file | languageExtension(patchInfo.repoName)}}">{{ hunk.afterText | safe}}</code></pre>
},
branchName: (data) => {
return data.patchPage.branchName
permalink: "repos/{{patchPage.repoName | slugify}}/patches/page{{patchPage.pageNumber}}/"
permalink: "repos/{{patchPage.repoName | slugify}}/branches/{{patchPage.branchName}}/patches/page{{patchPage.pageNumber}}/"
<a class="page-link {% if pageObj.pageNumber == patchPage.pageNumber %}active{% endif %}" href="/repos/{{patchPage.repoName | slugify}}/patches/page{{pageObj.pageNumber}}">{{ pageObj.pageNumber }}</a>
<a class="page-link {% if pageObj.pageNumber == patchPage.pageNumber %}active{% endif %}" href="/repos/{{patchPage.repoName | slugify}}/branches/{{patchPage.branchName}}/patches/page{{pageObj.pageNumber}}">{{ pageObj.pageNumber }}</a>
<span class="patch-name"><a href="/repos/{{patchPage.repoName | slugify}}/patches/{{patch.hash}}">{{patch.name}}</a></span>
<span class="patch-name"><a href="/repos/{{patchPage.repoName | slugify}}/branches/{{patchPage.branchName | slugify}}/patches/{{patch.hash}}">{{patch.name}}</a></span>
return data.repo
return data.branch.repoName
},
branchName: (data) => {
return data.branch.branchName
data: repos
data: branches
alias: repo
permalink: "repos/{{repo | slugify}}/"
alias: branch
permalink: "repos/{{branch.repoName | slugify}}/branches/{{branch.branchName}}/"
{{ repo | getReadMe| renderContent("md") | safe }}
{{ branch.repoName | getReadMe(branch.branchName) | renderContent("md") | safe }}
{% set url = [darcsConfig.baseUrl, "/repos/", repo | slugify] | join | url %}
{% set url = [darcsConfig.baseUrl, "/repos/", branch.repoName | slugify, "/branches/", branch.branchName | slugify] | join | url %}
<a href="/repos/{{ repo }}/patches.xml" class="initialism">RSS<i class="bi bi-rss-fill ms-2" style="color: orange;"></i></a>
<a href="/repos/{{ branch.repoName | slugify }}/branches/{{ branch.branchName | slugify }}/patches.xml" class="initialism">RSS<i class="bi bi-rss-fill ms-2" style="color: orange;"></i></a>
{% for patch in repos[repo].patches | batch(3) | first %}
{% for patch in repos[branch.repoName][branch.branchName].patches | batch(3) | first %}