diff --git a/lib/index.cjs b/lib/index.cjs index 64fcf0c..e6a6f46 100644 --- a/lib/index.cjs +++ b/lib/index.cjs @@ -82,22 +82,37 @@ function slug (value, maintainCase) { let slugger; +let headings = []; + function gfmHeadingId({ prefix = '' } = {}) { return { headerIds: false, // prevent deprecation warning; remove this once headerIds option is removed hooks: { preprocess(src) { + headings = []; slugger = new BananaSlug(); return src; } }, renderer: { heading(text, level, raw) { - raw = raw.toLowerCase().trim().replace(/<[!\/a-z].*?>/ig, ''); - return `${text}\n`; + raw = raw + .toLowerCase() + .trim() + .replace(/<[!\/a-z].*?>/gi, ''); + const id = `${prefix}${slugger.slug(raw)}`; + const heading = { level, text, id }; + headings.push(heading); + + return `${text}\n`; } } }; } +function getHeadingList() { + return headings; +} + +exports.getHeadingList = getHeadingList; exports.gfmHeadingId = gfmHeadingId; diff --git a/lib/index.umd.js b/lib/index.umd.js index b09f4cb..af1f244 100644 --- a/lib/index.umd.js +++ b/lib/index.umd.js @@ -86,24 +86,39 @@ let slugger; + let headings = []; + function gfmHeadingId({ prefix = '' } = {}) { return { headerIds: false, // prevent deprecation warning; remove this once headerIds option is removed hooks: { preprocess(src) { + headings = []; slugger = new BananaSlug(); return src; } }, renderer: { heading(text, level, raw) { - raw = raw.toLowerCase().trim().replace(/<[!\/a-z].*?>/ig, ''); - return `${text}\n`; + raw = raw + .toLowerCase() + .trim() + .replace(/<[!\/a-z].*?>/gi, ''); + const id = `${prefix}${slugger.slug(raw)}`; + const heading = { level, text, id }; + headings.push(heading); + + return `${text}\n`; } } }; } + function getHeadingList() { + return headings; + } + + exports.getHeadingList = getHeadingList; exports.gfmHeadingId = gfmHeadingId; })); diff --git a/spec/index.test.js b/spec/index.test.js index 7312789..c469a37 100644 --- a/spec/index.test.js +++ b/spec/index.test.js @@ -1,5 +1,5 @@ import { marked } from 'marked'; -import { gfmHeadingId } from '../src/index.js'; +import { getHeadingList, gfmHeadingId } from '../src/index.js'; describe('marked-gfm-heading-id', () => { beforeEach(() => { @@ -57,7 +57,16 @@ describe('marked-gfm-heading-id', () => { }); test('weird headings', () => { - marked.use(gfmHeadingId()); + let headings = []; + marked.use(gfmHeadingId(), { + hooks: { + postprocess(html) { + headings = getHeadingList(); + return html; + } + } + }); + const markdown = ` # foo 1 @@ -117,5 +126,27 @@ describe('marked-gfm-heading-id', () => {

Hello world!

" `); + + expect(headings.length).toBe(18); + expect(headings[0].id).toBe('foo-1'); + expect(headings[1].id).toBe('foo'); + expect(headings[2].id).toBe('foo-2'); + expect(headings[3].id).toBe('html-in-header'); + expect(headings[4].id).toBe('just-test'); + expect(headings[5].id).toBe('just-test-2'); + + expect(headings[6].id).toBe('just--test-2-spaces-'); + expect(headings[7].id).toBe('just-test-3'); + expect(headings[8].id).toBe('just-test-4'); + expect(headings[9].id).toBe('just-non-tags'); + expect(headings[10].id).toBe('just-spaces'); + + expect(headings[11].id).toBe('just--weird-chars'); + expect(headings[12].id).toBe('followed-by-weird-chars'); + expect(headings[13].id).toBe('followed--space-then-weird-chars'); + expect(headings[14].id).toBe(''); + expect(headings[15].id).toBe('comment-'); + expect(headings[16].id).toBe('hello-world'); + expect(headings[17].id).toBe('hello-world-1'); }); }); diff --git a/src/index.d.ts b/src/index.d.ts index 0feaa6b..4ea422a 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -1,4 +1,4 @@ -import type { MarkedExtension, marked } from "marked" +import type {MarkedExtension, marked} from 'marked'; /** Options for configuring marked-gfm-heading-id extension */ interface GfmHeadingIdOptions { @@ -10,6 +10,25 @@ interface GfmHeadingIdOptions { * Add `id` attribute to headings (h1, h2, h3, etc) like GitHub. * * @param options Options for the extension - * @returns A {@link marked.MarkedExtension | MarkedExtension} to be passed to {@link marked.use | `marked.use()`} + * @returns A {@link marked.MarkedExtension | MarkedExtension} to be passed + * to {@link marked.use | `marked.use()`} */ export function gfmHeadingId(options?: GfmHeadingIdOptions): MarkedExtension; + +/** + * Headings information, can be used to create table of content + */ +export interface HeadingData { + level: number; + text: string; + id: string; +} + +/** + * Returns a list of headings with the ids as computed by gfmHeadingId + * + * @param tokens a lexer output + * @param options Options for the extension + * @returns A string formatted the same as what would {@link gfmHeadingId} do. + */ +export function getHeadingList(): HeadingData[]; diff --git a/src/index.js b/src/index.js index bce6dd2..aa6f12b 100644 --- a/src/index.js +++ b/src/index.js @@ -1,20 +1,34 @@ import GithubSlugger from 'github-slugger'; let slugger; +let headings = []; + export function gfmHeadingId({ prefix = '' } = {}) { return { headerIds: false, // prevent deprecation warning; remove this once headerIds option is removed hooks: { preprocess(src) { + headings = []; slugger = new GithubSlugger(); return src; } }, renderer: { heading(text, level, raw) { - raw = raw.toLowerCase().trim().replace(/<[!\/a-z].*?>/ig, ''); - return `${text}\n`; + raw = raw + .toLowerCase() + .trim() + .replace(/<[!\/a-z].*?>/gi, ''); + const id = `${prefix}${slugger.slug(raw)}`; + const heading = { level, text, id }; + headings.push(heading); + + return `${text}\n`; } } }; } + +export function getHeadingList() { + return headings; +}