Skip to content

Commit

Permalink
feat: add plugin for detached anchors
Browse files Browse the repository at this point in the history
  • Loading branch information
main-kun authored and moki committed Nov 30, 2023
1 parent c173ab4 commit de87eca
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 0 deletions.
31 changes: 31 additions & 0 deletions src/transform/plugins/detached-anchor/detached-anchor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import StateBlock from 'markdown-it/lib/rules_block/state_block';
import Token from 'markdown-it/lib/token';

const pattern = /^{#([a-zA-Z0-9\-_]+)}$/;
export const TOKEN_NAME = 'detached-anchor';

export function createTokens(state: StateBlock, startLine: number): boolean {
const pos = state.bMarks[startLine] + state.tShift[startLine];
const max = state.eMarks[startLine];
const str = state.src.slice(pos, max);
if (!str.startsWith('{')) {
return false;
}
const match = str.match(pattern);
if (!match) {
return false;
}
const anchorId = match[1];
state.line = startLine + 1;
const token = state.push(TOKEN_NAME, '', 0);
token.map = [startLine, state.line];
token.content = anchorId;
token.markup = match[0];
return true;
}

export function renderTokens(tokens: Token[], idx: number) {
const token = tokens[idx];
const id = token.content;
return `<a id=${id}></a>`;
}
11 changes: 11 additions & 0 deletions src/transform/plugins/detached-anchor/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import MarkdownIt from 'markdown-it';
import {createTokens, renderTokens, TOKEN_NAME} from './detached-anchor';

const freeAnchor = (md: MarkdownIt) => {
md.block.ruler.before('paragraph', TOKEN_NAME, createTokens);
md.renderer.rules[TOKEN_NAME] = renderTokens;

return md;
};

export default freeAnchor;
26 changes: 26 additions & 0 deletions test/detached-anchor.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import transform from '../src/transform';
import plugin from '../src/transform/plugins/detached-anchor';

const transformYfm = (text: string) => {
const {
result: {html},
} = transform(text, {
plugins: [plugin],
});
return html;
};

describe('detached-anchor', function () {
describe('simple rendering', () => {
it('should render an a tag', () => {
expect(transformYfm('{#my-anchor}')).toBe('<a id="my-anchor"></a>');
});
});
describe('with heading', () => {
it('does not conflict with heading anchors', () => {
expect(transformYfm('# Heading {#heading-anchor} \n {#my-anchor}')).toBe(
'<h1 id="heading-anchor">Heading</h1>\n<a id="my-anchor"></a>',
);
});
});
});

0 comments on commit de87eca

Please sign in to comment.