Rename Markdown util getHeaders()
to getHeadings()
(#4031)
* Renamed getHeaders() to getHeadings(), according to RFC #208. * chore: update changeset * fix: expose MarkdownHeading type from `astro` Co-authored-by: Félix Sanz <me@felixsanz.com> Co-authored-by: Nate Moore <nate@astro.build>
This commit is contained in:
parent
1215e731b8
commit
6e27a5fdc2
12 changed files with 64 additions and 42 deletions
6
.changeset/young-radios-call.md
Normal file
6
.changeset/young-radios-call.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
'astro': patch
|
||||||
|
'@astrojs/markdown-remark': minor
|
||||||
|
---
|
||||||
|
|
||||||
|
**BREAKING** Renamed Markdown utility function `getHeaders()` to `getHeadings()`.
|
|
@ -4,14 +4,14 @@ import TableOfContents from "../RightSidebar/TableOfContents.tsx";
|
||||||
|
|
||||||
const { content, githubEditUrl } = Astro.props;
|
const { content, githubEditUrl } = Astro.props;
|
||||||
const title = content.title;
|
const title = content.title;
|
||||||
const headers = content.astro.headers;
|
const headings = content.astro.headings;
|
||||||
---
|
---
|
||||||
|
|
||||||
<article id="article" class="content">
|
<article id="article" class="content">
|
||||||
<section class="main-section">
|
<section class="main-section">
|
||||||
<h1 class="content-title" id="overview">{title}</h1>
|
<h1 class="content-title" id="overview">{title}</h1>
|
||||||
<nav class="block sm:hidden">
|
<nav class="block sm:hidden">
|
||||||
<TableOfContents client:media="(max-width: 50em)" {headers} />
|
<TableOfContents client:media="(max-width: 50em)" {headings} />
|
||||||
</nav>
|
</nav>
|
||||||
<slot />
|
<slot />
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -8,7 +8,7 @@ const showMoreSection = CONFIG.COMMUNITY_INVITE_URL || editHref;
|
||||||
{showMoreSection && <h2 class="heading">More</h2>}
|
{showMoreSection && <h2 class="heading">More</h2>}
|
||||||
<ul>
|
<ul>
|
||||||
{editHref && (
|
{editHref && (
|
||||||
<li class={`header-link depth-2`}>
|
<li class={`heading-link depth-2`}>
|
||||||
<a class="edit-on-github" href={editHref} target="_blank">
|
<a class="edit-on-github" href={editHref} target="_blank">
|
||||||
<svg
|
<svg
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
|
@ -32,7 +32,7 @@ const showMoreSection = CONFIG.COMMUNITY_INVITE_URL || editHref;
|
||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
{CONFIG.COMMUNITY_INVITE_URL && (
|
{CONFIG.COMMUNITY_INVITE_URL && (
|
||||||
<li class={`header-link depth-2`}>
|
<li class={`heading-link depth-2`}>
|
||||||
<a href={CONFIG.COMMUNITY_INVITE_URL} target="_blank">
|
<a href={CONFIG.COMMUNITY_INVITE_URL} target="_blank">
|
||||||
<svg
|
<svg
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
import TableOfContents from "./TableOfContents.tsx";
|
import TableOfContents from "./TableOfContents.tsx";
|
||||||
import MoreMenu from "./MoreMenu.astro";
|
import MoreMenu from "./MoreMenu.astro";
|
||||||
const { content, githubEditUrl } = Astro.props;
|
const { content, githubEditUrl } = Astro.props;
|
||||||
const headers = content.astro.headers;
|
const headings = content.astro.headings;
|
||||||
---
|
---
|
||||||
|
|
||||||
<nav class="sidebar-nav" aria-labelledby="grid-right">
|
<nav class="sidebar-nav" aria-labelledby="grid-right">
|
||||||
<div class="sidebar-nav-inner">
|
<div class="sidebar-nav-inner">
|
||||||
<TableOfContents client:media="(min-width: 50em)" {headers} />
|
<TableOfContents client:media="(min-width: 50em)" {headings} />
|
||||||
<MoreMenu editHref={githubEditUrl} />
|
<MoreMenu editHref={githubEditUrl} />
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import type { FunctionalComponent } from 'preact';
|
import type { FunctionalComponent } from 'preact';
|
||||||
import { h, Fragment } from 'preact';
|
import { h, Fragment } from 'preact';
|
||||||
import { useState, useEffect, useRef } from 'preact/hooks';
|
import { useState, useEffect, useRef } from 'preact/hooks';
|
||||||
|
import { MarkdownHeading } from 'astro';
|
||||||
|
|
||||||
const TableOfContents: FunctionalComponent<{ headers: any[] }> = ({ headers = [] }) => {
|
const TableOfContents: FunctionalComponent<{ headings: MarkdownHeading[] }> = ({ headings = [] }) => {
|
||||||
const itemOffsets = useRef([]);
|
const itemOffsets = useRef([]);
|
||||||
const [activeId, setActiveId] = useState<string>(undefined);
|
const [activeId, setActiveId] = useState<string>(undefined);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const getItemOffsets = () => {
|
const getItemOffsets = () => {
|
||||||
const titles = document.querySelectorAll('article :is(h1, h2, h3, h4)');
|
const titles = document.querySelectorAll('article :is(h1, h2, h3, h4)');
|
||||||
|
@ -27,18 +27,18 @@ const TableOfContents: FunctionalComponent<{ headers: any[] }> = ({ headers = []
|
||||||
<>
|
<>
|
||||||
<h2 class="heading">On this page</h2>
|
<h2 class="heading">On this page</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li class={`header-link depth-2 ${activeId === 'overview' ? 'active' : ''}`.trim()}>
|
<li class={`heading-link depth-2 ${activeId === 'overview' ? 'active' : ''}`.trim()}>
|
||||||
<a href="#overview">Overview</a>
|
<a href="#overview">Overview</a>
|
||||||
</li>
|
</li>
|
||||||
{headers
|
{headings
|
||||||
.filter(({ depth }) => depth > 1 && depth < 4)
|
.filter(({ depth }) => depth > 1 && depth < 4)
|
||||||
.map((header) => (
|
.map((heading) => (
|
||||||
<li
|
<li
|
||||||
class={`header-link depth-${header.depth} ${
|
class={`heading-link depth-${heading.depth} ${
|
||||||
activeId === header.slug ? 'active' : ''
|
activeId === heading.slug ? 'active' : ''
|
||||||
}`.trim()}
|
}`.trim()}
|
||||||
>
|
>
|
||||||
<a href={`#${header.slug}`}>{header.text}</a>
|
<a href={`#${heading.slug}`}>{heading.text}</a>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -311,42 +311,42 @@ h2.heading {
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-link {
|
.heading-link {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
padding: 0.1rem 0 0.1rem 1rem;
|
padding: 0.1rem 0 0.1rem 1rem;
|
||||||
border-left: 4px solid var(--theme-divider);
|
border-left: 4px solid var(--theme-divider);
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-link:hover,
|
.heading-link:hover,
|
||||||
.header-link:focus {
|
.heading-link:focus {
|
||||||
border-left-color: var(--theme-accent);
|
border-left-color: var(--theme-accent);
|
||||||
color: var(--theme-accent);
|
color: var(--theme-accent);
|
||||||
}
|
}
|
||||||
.header-link:focus-within {
|
.heading-link:focus-within {
|
||||||
color: var(--theme-text-light);
|
color: var(--theme-text-light);
|
||||||
border-left-color: hsla(var(--color-gray-40), 1);
|
border-left-color: hsla(var(--color-gray-40), 1);
|
||||||
}
|
}
|
||||||
.header-link svg {
|
.heading-link svg {
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
.header-link:hover svg {
|
.heading-link:hover svg {
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
.header-link a {
|
.heading-link a {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0.15em 0 0.15em 0;
|
padding: 0.15em 0 0.15em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-link.depth-3 {
|
.heading-link.depth-3 {
|
||||||
padding-left: 2rem;
|
padding-left: 2rem;
|
||||||
}
|
}
|
||||||
.header-link.depth-4 {
|
.heading-link.depth-4 {
|
||||||
padding-left: 3rem;
|
padding-left: 3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-link a {
|
.heading-link a {
|
||||||
font: inherit;
|
font: inherit;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
4
packages/astro/env.d.ts
vendored
4
packages/astro/env.d.ts
vendored
|
@ -18,7 +18,9 @@ declare module '*.md' {
|
||||||
export const frontmatter: MD['frontmatter'];
|
export const frontmatter: MD['frontmatter'];
|
||||||
export const file: MD['file'];
|
export const file: MD['file'];
|
||||||
export const url: MD['url'];
|
export const url: MD['url'];
|
||||||
export const getHeaders: MD['getHeaders'];
|
export const getHeadings: MD['getHeadings'];
|
||||||
|
/** @deprecated Renamed to `getHeadings()` */
|
||||||
|
export const getHeaders: () => void;
|
||||||
export const Content: MD['Content'];
|
export const Content: MD['Content'];
|
||||||
export const rawContent: MD['rawContent'];
|
export const rawContent: MD['rawContent'];
|
||||||
export const compiledContent: MD['compiledContent'];
|
export const compiledContent: MD['compiledContent'];
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import type {
|
import type {
|
||||||
MarkdownHeader,
|
MarkdownHeading,
|
||||||
MarkdownMetadata,
|
MarkdownMetadata,
|
||||||
MarkdownRenderingResult,
|
MarkdownRenderingResult,
|
||||||
RehypePlugins,
|
RehypePlugins,
|
||||||
|
@ -16,6 +16,14 @@ import type { AstroConfigSchema } from '../core/config';
|
||||||
import type { ViteConfigWithSSR } from '../core/create-vite';
|
import type { ViteConfigWithSSR } from '../core/create-vite';
|
||||||
import type { AstroComponentFactory, Metadata } from '../runtime/server';
|
import type { AstroComponentFactory, Metadata } from '../runtime/server';
|
||||||
export type { SSRManifest } from '../core/app/types';
|
export type { SSRManifest } from '../core/app/types';
|
||||||
|
export type {
|
||||||
|
MarkdownHeading,
|
||||||
|
MarkdownMetadata,
|
||||||
|
MarkdownRenderingResult,
|
||||||
|
RehypePlugins,
|
||||||
|
RemarkPlugins,
|
||||||
|
ShikiConfig,
|
||||||
|
} from '@astrojs/markdown-remark';
|
||||||
|
|
||||||
export interface AstroBuiltinProps {
|
export interface AstroBuiltinProps {
|
||||||
'client:load'?: boolean;
|
'client:load'?: boolean;
|
||||||
|
@ -783,7 +791,9 @@ export interface MarkdownInstance<T extends Record<string, any>> {
|
||||||
rawContent(): string;
|
rawContent(): string;
|
||||||
/** Markdown file compiled to valid Astro syntax. Queryable with most HTML parsing libraries */
|
/** Markdown file compiled to valid Astro syntax. Queryable with most HTML parsing libraries */
|
||||||
compiledContent(): Promise<string>;
|
compiledContent(): Promise<string>;
|
||||||
getHeaders(): Promise<MarkdownHeader[]>;
|
getHeadings(): Promise<MarkdownHeading[]>;
|
||||||
|
/** @deprecated Renamed to `getHeadings()` */
|
||||||
|
getHeaders(): void;
|
||||||
default: () => Promise<{
|
default: () => Promise<{
|
||||||
metadata: MarkdownMetadata;
|
metadata: MarkdownMetadata;
|
||||||
frontmatter: MarkdownContent<T>;
|
frontmatter: MarkdownContent<T>;
|
||||||
|
|
|
@ -123,8 +123,12 @@ export default function markdown({ config }: AstroPluginOptions): Plugin {
|
||||||
return load().then((m) => m.default(...args));
|
return load().then((m) => m.default(...args));
|
||||||
}
|
}
|
||||||
Content.isAstroComponentFactory = true;
|
Content.isAstroComponentFactory = true;
|
||||||
|
export function getHeadings() {
|
||||||
|
return load().then((m) => m.metadata.headings);
|
||||||
|
}
|
||||||
export function getHeaders() {
|
export function getHeaders() {
|
||||||
return load().then((m) => m.metadata.headers);
|
console.warn('getHeaders() have been deprecated. Use getHeadings() function instead.');
|
||||||
|
return load().then((m) => m.metadata.headings);
|
||||||
};`,
|
};`,
|
||||||
map: null,
|
map: null,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import type { MarkdownRenderingOptions, MarkdownRenderingResult } from './types';
|
import type { MarkdownRenderingOptions, MarkdownRenderingResult } from './types';
|
||||||
|
|
||||||
import { loadPlugins } from './load-plugins.js';
|
import { loadPlugins } from './load-plugins.js';
|
||||||
import createCollectHeaders from './rehype-collect-headers.js';
|
import createCollectHeadings from './rehype-collect-headings.js';
|
||||||
import rehypeEscape from './rehype-escape.js';
|
import rehypeEscape from './rehype-escape.js';
|
||||||
import rehypeExpressions from './rehype-expressions.js';
|
import rehypeExpressions from './rehype-expressions.js';
|
||||||
import rehypeIslands from './rehype-islands.js';
|
import rehypeIslands from './rehype-islands.js';
|
||||||
|
@ -41,7 +41,7 @@ export async function renderMarkdown(
|
||||||
} = opts;
|
} = opts;
|
||||||
const input = new VFile({ value: content, path: fileURL });
|
const input = new VFile({ value: content, path: fileURL });
|
||||||
const scopedClassName = opts.$?.scopedClassName;
|
const scopedClassName = opts.$?.scopedClassName;
|
||||||
const { headers, rehypeCollectHeaders } = createCollectHeaders();
|
const { headings, rehypeCollectHeadings } = createCollectHeadings();
|
||||||
|
|
||||||
let parser = unified()
|
let parser = unified()
|
||||||
.use(markdown)
|
.use(markdown)
|
||||||
|
@ -94,8 +94,8 @@ export async function renderMarkdown(
|
||||||
parser
|
parser
|
||||||
.use(
|
.use(
|
||||||
isAstroFlavoredMd
|
isAstroFlavoredMd
|
||||||
? [rehypeJsx, rehypeExpressions, rehypeEscape, rehypeIslands, rehypeCollectHeaders]
|
? [rehypeJsx, rehypeExpressions, rehypeEscape, rehypeIslands, rehypeCollectHeadings]
|
||||||
: [rehypeCollectHeaders, rehypeRaw]
|
: [rehypeCollectHeadings, rehypeRaw]
|
||||||
)
|
)
|
||||||
.use(rehypeStringify, { allowDangerousHtml: true });
|
.use(rehypeStringify, { allowDangerousHtml: true });
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ export async function renderMarkdown(
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
metadata: { headers, source: content, html: result.toString() },
|
metadata: { headings, source: content, html: result.toString() },
|
||||||
code: result.toString(),
|
code: result.toString(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,13 @@ import Slugger from 'github-slugger';
|
||||||
import { toHtml } from 'hast-util-to-html';
|
import { toHtml } from 'hast-util-to-html';
|
||||||
import { visit } from 'unist-util-visit';
|
import { visit } from 'unist-util-visit';
|
||||||
|
|
||||||
import type { MarkdownHeader, RehypePlugin } from './types.js';
|
import type { MarkdownHeading, RehypePlugin } from './types.js';
|
||||||
|
|
||||||
export default function createCollectHeaders() {
|
export default function createCollectHeadings() {
|
||||||
const headers: MarkdownHeader[] = [];
|
const headings: MarkdownHeading[] = [];
|
||||||
const slugger = new Slugger();
|
const slugger = new Slugger();
|
||||||
|
|
||||||
function rehypeCollectHeaders(): ReturnType<RehypePlugin> {
|
function rehypeCollectHeadings(): ReturnType<RehypePlugin> {
|
||||||
return function (tree) {
|
return function (tree) {
|
||||||
visit(tree, (node) => {
|
visit(tree, (node) => {
|
||||||
if (node.type !== 'element') return;
|
if (node.type !== 'element') return;
|
||||||
|
@ -61,13 +61,13 @@ export default function createCollectHeaders() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
headers.push({ depth, slug: node.properties.id, text });
|
headings.push({ depth, slug: node.properties.id, text });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
headers,
|
headings,
|
||||||
rehypeCollectHeaders,
|
rehypeCollectHeadings,
|
||||||
};
|
};
|
||||||
}
|
}
|
|
@ -44,14 +44,14 @@ export interface MarkdownRenderingOptions extends AstroMarkdownOptions {
|
||||||
isAstroFlavoredMd?: boolean;
|
isAstroFlavoredMd?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MarkdownHeader {
|
export interface MarkdownHeading {
|
||||||
depth: number;
|
depth: number;
|
||||||
slug: string;
|
slug: string;
|
||||||
text: string;
|
text: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MarkdownMetadata {
|
export interface MarkdownMetadata {
|
||||||
headers: MarkdownHeader[];
|
headings: MarkdownHeading[];
|
||||||
source: string;
|
source: string;
|
||||||
html: string;
|
html: string;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue