mirror of
https://github.com/benjaminion/upgrading-ethereum-book.git
synced 2026-01-06 21:24:02 -05:00
Build: more thorough processing of spec links
Deals with duplicate anchors in the one-page spec.
This commit is contained in:
@@ -12,6 +12,7 @@ import myAutoLinkHeadings from './integrations/my_autolink_headings';
|
||||
import mySvgInline from './integrations/my_svg_inline';
|
||||
import mySearchIndex from './integrations/my_search_index';
|
||||
import myAddTooltips from './integrations/my_add_tooltips';
|
||||
import mySpecLinks from './integrations/my_spec_links';
|
||||
import myFixupLinks from './integrations/my_fixup_links';
|
||||
import myCleanupHtml from './integrations/my_cleanup_html';
|
||||
import myHtaccess from './integrations/my_htaccess';
|
||||
@@ -27,6 +28,7 @@ export default defineConfig({
|
||||
mySvgInline({ filePath: 'src/', cachePath: './.svg_cache/' }),
|
||||
mySearchIndex(searchOptions),
|
||||
myAddTooltips({ constantsFile: 'src/include/constants.json' }),
|
||||
mySpecLinks(),
|
||||
myFixupLinks(),
|
||||
myCleanupHtml(),
|
||||
myHtaccess(),
|
||||
|
||||
108
integrations/my_spec_links.js
Normal file
108
integrations/my_spec_links.js
Normal file
@@ -0,0 +1,108 @@
|
||||
import { visit, CONTINUE, SKIP } from 'unist-util-visit';
|
||||
import GithubSlugger from 'github-slugger';
|
||||
|
||||
// Fix up internal links in the one-page annotated spec.
|
||||
// Must be configured to run after myAutoLinkHeadings, and before myFixupLinks and myCleanupHtml.
|
||||
// The one-page spec is excluded from search index processing, so no need to worry about that.
|
||||
|
||||
// Ignore SVGs and anything to do with footnotes (which should be fine without help)
|
||||
function isIgnoredElement(node) {
|
||||
return (
|
||||
node.tagName === 'svg' ||
|
||||
(node.tagName === 'a' && node.properties.dataFootnoteRef !== undefined) ||
|
||||
(node.tagName === 'section' && node.properties.dataFootnotes !== undefined)
|
||||
);
|
||||
}
|
||||
|
||||
// Only headings and <a id="..."> are of interest
|
||||
function isTargetElement(node) {
|
||||
return (
|
||||
node.properties?.id !== undefined &&
|
||||
['a', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(node.tagName)
|
||||
);
|
||||
}
|
||||
|
||||
// We look at all links, but '/part3/' should point to the main book, not the one-page spec
|
||||
function isLinkElement(node) {
|
||||
return (
|
||||
node.tagName === 'a' &&
|
||||
node.properties?.href !== undefined &&
|
||||
node.properties.href !== '/part3/'
|
||||
);
|
||||
}
|
||||
|
||||
// New pages are indicated by comments attached to certain headings in the Markdown
|
||||
function isNewPage(node) {
|
||||
return (
|
||||
['h1', 'h2', 'h3'].includes(node.tagName) &&
|
||||
node.children[node.children.length - 1].type === 'comment'
|
||||
);
|
||||
}
|
||||
|
||||
function specLinks({ logger }) {
|
||||
return function (tree, file) {
|
||||
if (file.data.astro.frontmatter.path !== '/annotated-spec/') return;
|
||||
|
||||
// We re-slug the slug to handle duplicates
|
||||
const slugger = new GithubSlugger();
|
||||
|
||||
// Pass 1: Build a map of pages and ids/slugs
|
||||
const map = {};
|
||||
let page = '';
|
||||
visit(tree, 'element', (node) => {
|
||||
if (isIgnoredElement(node)) return SKIP;
|
||||
|
||||
if (isTargetElement(node)) {
|
||||
const oldSlug = node.properties.id;
|
||||
const newSlug = slugger.slug(oldSlug);
|
||||
node.properties.id = newSlug;
|
||||
if (isNewPage(node)) {
|
||||
page = node.children[node.children.length - 1].value.trim();
|
||||
map[page] = newSlug;
|
||||
}
|
||||
page || logger.warn('Page is not set when processing ' + oldSlug);
|
||||
map[page + '#' + oldSlug] = newSlug;
|
||||
}
|
||||
});
|
||||
|
||||
// Pass 2: Adjust hrefs - we need two passes in case any references are forward-looking
|
||||
page = '';
|
||||
visit(tree, 'element', (node) => {
|
||||
if (isIgnoredElement(node)) return SKIP;
|
||||
|
||||
if (isNewPage(node)) {
|
||||
page = node.children[node.children.length - 1].value.trim();
|
||||
return CONTINUE;
|
||||
}
|
||||
|
||||
if (isLinkElement(node)) {
|
||||
const oldHref = node.properties.href;
|
||||
let newHref;
|
||||
if (oldHref.startsWith('#')) {
|
||||
newHref = map[page + oldHref];
|
||||
} else if (oldHref.startsWith('/part3/')) {
|
||||
newHref = map[oldHref];
|
||||
} else {
|
||||
return CONTINUE;
|
||||
}
|
||||
newHref || logger.warn('Failed to fix spec link: ' + oldHref);
|
||||
node.properties.href = '#' + newHref;
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export default function () {
|
||||
return {
|
||||
name: 'mySpecLinks',
|
||||
hooks: {
|
||||
'astro:config:setup': ({ updateConfig, logger }) => {
|
||||
updateConfig({
|
||||
markdown: {
|
||||
rehypePlugins: [[specLinks, { logger: logger }]],
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -8,7 +8,7 @@ const reEnd = /^# .*<!-- \/part4\/ -->$/dm;
|
||||
const preamble =
|
||||
'# One Page Annotated Spec\n\n' +
|
||||
'**Note:** This page is automatically generated from the chapters ' +
|
||||
'in [Part 3](/part3/). You may find that some internal links are broken.';
|
||||
'in [Part 3](/part3/).';
|
||||
|
||||
export function specLoader(fileName) {
|
||||
return {
|
||||
@@ -37,13 +37,10 @@ export function specLoader(fileName) {
|
||||
|
||||
let markdown = preamble;
|
||||
if (startMatch && endMatch) {
|
||||
// Remove the title - we will replace it
|
||||
// Extract the markdown, removing the title and replacing it with the preamble
|
||||
const start = startMatch.indices[0][1] + 1;
|
||||
const end = endMatch.indices[0][0];
|
||||
// Extract the spec, add the preamble, and rewrite internal links
|
||||
markdown += allMarkdown
|
||||
.substring(start, end)
|
||||
.replace(/]\(\/part3\/[^#)]*/g, '](');
|
||||
markdown += allMarkdown.substring(start, end);
|
||||
} else {
|
||||
logger.warn('Creating empty annotated spec');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user