From 86ce79eeac87391ffe92866782dfca9a5c4d882e Mon Sep 17 00:00:00 2001 From: Karsten Schmidt Date: Sun, 5 Mar 2023 10:43:50 +0100 Subject: [PATCH] feat(hiccup-markdown): add anchor ID syntax support for headings - update parse grammar & hd parser - support for `{#id}` suffixes for defining heading anchor IDs - only auto-generate anchor IDs if not explicitly specified - update tests --- packages/hiccup-markdown/src/api.ts | 4 ++-- packages/hiccup-markdown/src/parse.ts | 25 +++++++++++-------------- packages/hiccup-markdown/test/parse.ts | 16 +++++++++++++--- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/packages/hiccup-markdown/src/api.ts b/packages/hiccup-markdown/src/api.ts index c7a0f9084f..f01d8b466b 100644 --- a/packages/hiccup-markdown/src/api.ts +++ b/packages/hiccup-markdown/src/api.ts @@ -123,14 +123,14 @@ export interface TagTransforms { * * @param ctx * @param level - * @param id + * @param anchorID * @param body * @param meta */ heading( ctx: TransformCtx, level: number, - id: string, + anchorID: string, body: any[], meta?: any ): any; diff --git a/packages/hiccup-markdown/src/parse.ts b/packages/hiccup-markdown/src/parse.ts index c9a805e10a..8c75816669 100644 --- a/packages/hiccup-markdown/src/parse.ts +++ b/packages/hiccup-markdown/src/parse.ts @@ -1,5 +1,5 @@ -// Downloaded @ 2023-02-26T13:59:47.034Z -// Source: https://demo.thi.ng/umbrella/parse-playground/#ldoNR0ROTDE6IDxETkw-KyA9PiBkaXNjYXJkIDsKRE5MMjogPE5MPnsyLH0gOwpsYnI6ICdcXCchIDxOTD4hIDsKZXNjOiAnXFwnISAoICc8JyB8ICdbJyB8ICdfJyB8ICcqJyB8ICdgJyB8ICd-JyB8ICc6JyApIDsKaW5saW5lZGVsaW06ICggIiFbIiB8ICdbJyB8ICIqKiIgfCAnXycgfCAnYCcgfCAnPCcgfCAnXFwnIHwgIn5-IiB8ICIgOiIgfCA8bGJyPiApIDsKZGVsaW06ICggPGlubGluZWRlbGltPiB8IDxETkwyPiApIDsKZGVsaW0xOiAoIDxpbmxpbmVkZWxpbT4gfCA8Tkw-ICkgOwpib2R5OiAuKD8tPGRlbGltPiEpID0-IGpvaW4gOwpib2R5MTogLig_LTxkZWxpbTE-ISkgPT4gam9pbiA7Cgp3aWtpcmVmOiAiW1siISAuKD8rIl1dIiEpID0-IGpvaW4gOwpmbnJlZjogIlteIiEgPGxhYmVsPiA7CmZub3RlOiA8TFNUQVJUPiAiW14iISA8Zm5sYWJlbD4gPFdTMT4gPHBhcmE-IDsKZm5sYWJlbDogLig_KyJdOiIhKSA9PiBqb2luIDsKbGFiZWw6IC4oPysnXSchKSA9PiBqb2luIDsKdGFyZ2V0OiAuKD8rJyknISkgPT4gam9pbiA7Cmxpbms6ICdbJyEgPGxpbmtsYWJlbD4gJygnISA8dGFyZ2V0PiA7CmxpbmtyZWY6ICdbJyEgPGxpbmtsYWJlbD4gJ1snISA8bGFiZWw-IDsKbGlua2RlZjogPExTVEFSVD4gJ1snISA8bGFiZWw-ICc6JyEgPFdTMT4gPGxkdGFyZ2V0PiA7CmxkdGFyZ2V0OiAuKD8rPEROTDE-KSA9PiBqb2luIDsKbGlua2xhYmVsOiAoPGltZz4gfCA8Ym9sZD4gfCA8aXRhbGljPiB8IDxzdHJpa2U-IHwgPGNvZGU-IHwgPGVtb2ppPiB8IDxsaW5rYm9keT4pKiAnXSchIDsKbGlua2RlbGltOiAoICIhWyIgfCAnWycgfCAiKioiIHwgJ18nIHwgIn5-IiB8ICdgJyB8ICddJykgOwpsaW5rYm9keTogLig_LTxsaW5rZGVsaW0-ISkgPT4gam9pbiA7CmltZzogIiFbIiEgPGxhYmVsPiAnKCchIDx0YXJnZXQ-IDsKYm9sZDogIioqIiEgKDx3aWtpcmVmPiB8IDxpbWc-IHwgPGZucmVmPiB8IDxsaW5rcmVmPiB8IDxsaW5rPiB8IDxpdGFsaWM-IHwgPHN0cmlrZT4gfCA8Y29kZT4gfCA8ZW1vamk-IHwgPGJvZHkxPikqICIqKiIhIDsKaXRhbGljOiAnXychICg8d2lraXJlZj4gfCA8aW1nPiB8IDxmbnJlZj4gfCA8bGlua3JlZj4gfCA8bGluaz4gfCA8Ym9sZD4gfCA8c3RyaWtlPiB8IDxjb2RlPiB8IDxlbW9qaT4gfCA8Ym9keTE-KSogJ18nISA7CnN0cmlrZTogIn5-IiEgKDx3aWtpcmVmPiB8IDxpbWc-IHwgPGZucmVmPiB8IDxsaW5rcmVmPiB8IDxsaW5rPiB8IDxib2xkPiB8IDxpdGFsaWM-IHwgPGNvZGU-IHwgPGVtb2ppPiB8IDxib2R5MT4pKiAifn4iISA7CmNvZGU6ICdgJyEgLig_KydgJyEpID0-IGpvaW4gOwprYmQ6ICI8a2JkPiIhIC4oPysiPC9rYmQ-IiEpID0-IGpvaW4gOwplbW9qaTogJyAnPyAnOichICg8QUxQSEFfTlVNPiB8ICcrJyB8ICctJykoPysnOichKSA9PiBqb2luIDsKcGFyYTogKDx3aWtpcmVmPiB8IDxpbWc-IHwgPGZucmVmPiB8IDxsaW5rcmVmPiB8IDxsaW5rPiB8IDxib2xkPiB8IDxpdGFsaWM-IHwgPHN0cmlrZT4gfCA8Y29kZT4gfCA8a2JkPiB8IDxlbW9qaT4gfCA8bGJyPiB8IDxlc2M-IHwgPGJvZHk-KSogPEROTDI-ISA7CgpoZGxldmVsOiAnIycrID0-IGNvdW50IDsKaGQ6IDxMU1RBUlQ-IDxoZGxldmVsPiA8V1MwPgogICAgKDx3aWtpcmVmPiB8IDxpbWc-IHwgPGZucmVmPiB8IDxsaW5rPiB8IDxib2xkPiB8IDxpdGFsaWM-IHwgPHN0cmlrZT4gfCA8Y29kZT4gfCA8ZW1vamk-IHwgPGJvZHkxPiApKiA8RE5MMT4gOwoKbGlsZXZlbDogJyAnKiA9PiBjb3VudCA7CnVpbnQ6IDxESUdJVD4rID0-IGludCA7CnVsaWQ6IDxETkw-IDxXUzA-ICctJyEgOwpvbGlkOiA8RE5MPiA8V1MwPiA8RElHSVQ-KyEgJy4nISA7CmxpZGVsaW06ICggPGRlbGltPiB8IDx1bGlkPiB8IDxvbGlkPiApIDsKbGlib2R5OiAuKD8tPGxpZGVsaW0-ISkgPT4gam9pbiA7CnRvZG86ICdbJyEgWyB4WF0gJ10nISA8V1MxPiA9PiBob2lzdFIgOwp1bGl0ZW06IDxMU1RBUlQ-IDxsaWxldmVsPiAiLSAiISA8dG9kbz4_CiAgICAgICAgKDx3aWtpcmVmPiB8IDxpbWc-IHwgPGZucmVmPiB8IDxsaW5rPiB8IDxib2xkPiB8IDxpdGFsaWM-IHwgPHN0cmlrZT4gfCA8Y29kZT4gfCA8ZW1vamk-IHwgPGxpYm9keT4gKSogPEROTD4gOwpvbGl0ZW06IDxMU1RBUlQ-IDxsaWxldmVsPiA8dWludD4gIi4gIiEgPHRvZG8-PwogICAgICAgICg8d2lraXJlZj4gfCA8aW1nPiB8IDxmbnJlZj4gfCA8bGluaz4gfCA8Ym9sZD4gfCA8aXRhbGljPiB8IDxzdHJpa2U-IHwgPGNvZGU-IHwgPGVtb2ppPiB8IDxsaWJvZHk-ICkqIDxETkw-IDsKbGlzdDogKDx1bGl0ZW0-IHwgPG9saXRlbT4pKyA8RE5MMT4gOwoKY2JkZWxpbTogPExTVEFSVD4gImBgYCIhIDsKY29kZWJsb2NrOiA8Y2JkZWxpbT4hIDxjb2RlbWV0YT4gPGNvZGVib2R5PiA8RE5MMT4gOwpjb2RlbWV0YTogLig_KzxOTD4hKSA9PiBqb2luIDsKY29kZWJvZHk6IC4oPys8Y2JkZWxpbT4pID0-IGpvaW4gOwoKY3VzdG9tZGVsaW06IDxMU1RBUlQ-ICI6OjoiISA7CmN1c3RvbWJsb2NrOiA8Y3VzdG9tZGVsaW0-ISA8Y3VzdG9tbWV0YT4gPGN1c3RvbWJvZHk-IDxETkwxPiA7CmN1c3RvbW1ldGE6IC4oPys8Tkw-ISkgPT4gam9pbiA7CmN1c3RvbWJvZHk6IC4oPys8Y3VzdG9tZGVsaW0-KSA9PiBqb2luIDsKCm1ldGFibG9jazogPExTVEFSVD4gInt7eyIhIDxtZXRhYm9keT4gPEROTDE-IDsKbWV0YWJvZHk6IC4oPys8bWV0YWVuZD4hKSA9PiBqb2luIDsKbWV0YWVuZDogIn19fSIgPExFTkQ-IDsKCmJxbGV2ZWw6ICc-JysgPT4gY291bnQgOwpicWxpbmU6IDxMU1RBUlQ-IDxicWxldmVsPiAnICc_IQogICAgICAgICg8d2lraXJlZj4gfCA8aW1nPiB8IDxmbnJlZj4gfCA8bGluaz4gfCA8Ym9sZD4gfCA8aXRhbGljPiB8IDxzdHJpa2U-IHwgPGNvZGU-IHwgPGVtb2ppPiB8IDxicWxicj4gfCA8Ym9keTE-KSogPEROTD4gOwpicWxicjogJ1xcJyEoPy08RE5MPikgOwpicXVvdGU6IDxicWxpbmU-KyA8RE5MMT4gOwoKdGRlbGltOiAoPGlubGluZWRlbGltPiB8ICd8JyApIDsKdGJvZHk6IC4oPy08dGRlbGltPiEpID0-IGpvaW4gOwp0Y2VsbDogPFdTMD4gKDx3aWtpcmVmPiB8IDxpbWc-IHwgPGZucmVmPiB8IDxsaW5rPiB8IDxib2xkPiB8IDxpdGFsaWM-IHwgPHN0cmlrZT4gfCA8Y29kZT4gfCA8ZW1vamk-IHwgPHRib2R5PiApKiAnfCchIDsKdHJvdzogPExTVEFSVD4gJ3wnISA8dGNlbGw-KD8rPEROTD4pIDsKdGFibGU6IDx0cm93PisgPEROTDE-IDsKCmhyOiAiLS0iICctJysgPEROTDE-ID0-IGpvaW4gOwoKbWFpbjogPFdTMD4gKDxoZD4gfCA8bGlzdD4gfCA8YnF1b3RlPiB8IDxjb2RlYmxvY2s-IHwgPGN1c3RvbWJsb2NrPiB8IDxtZXRhYmxvY2s-IHwgPHRhYmxlPiB8IDxocj4gfCA8Zm5vdGU-IHwgPGxpbmtkZWY-IHwgPHBhcmE-KSogO6RtYWlu2gKLIyBIZWxsbyA6d29ybGQ6OnNtaWxlOlteYWJjXQoKYSBcPCBiCgpMZXQgYGV4cDIoeCkgPSAyKip4YAoKUHJlc3MgPGtiZD5Db250cm9sPC9rYmQ-ICsgPGtiZD5SPC9rYmQ-IHRvIHJlbG9hZC4KCjo6OmFsZXJ0ClRlc3QgW2xhYmVsXVtyZWZyZWZdCjo6OgoKLS0tCgp7e3sgeyJpZCI6IDEyMywgInByb3RlY3RlZCI6IHRydWUgfSB9fX0KU28gW1tzbWlsZV9mb28xMl1dIPCfmI0gd2hhdCdzIFt0aGlzXSguL3RoaXMubWQpW14yXSB0aGVuOgoKQSBbbGluayBfd2l0aCAqKm5lc3RlZCB-fmZvcm1hdH5-KiogYW5kIGByZWZlcmVuY2VgX11bcmVmcmVmXS4uLgoKYGBgdHMgdGFuZ2xlOm5vIGxuOnllcwpjb25zdCBmb28gPSA0MjsKLy8gdGhlIGVuZApgYGAKCjEuIGZvbwogIDQuIGJhcgogICAgIGJheiBhbmQgc28gb24KICAgICBtdWx0aWxpbmUKMi4gWyBdIHRhc2sKMy4gW3hdICoqZG9uZSoqCgo-IGJsb2NrcXVvdGUKPiBtdWx0aWxpbmUgdG9vXAo-PiBuZXN0ZWQKPj4-IGxldmVsIDMKPiAtLSBzb3VyY2UKCnwgZm9vIHwgYmFyIHwKfDotfC06fAp8IFtbcmVmIzIyXV0gfCBgb25gICoqb2ZmKiogfAoKW15hYmNdOiBGb28gX2Jhcl8gYmF6CgpbXjJdOiBUT0RPCgpbcmVmcmVmXTogaHR0cDovL3RoaS5uZy8KW2Zvb106IC4vZm9vLm1kI2JhcgoK2TUxMS4gYWJjCiAgLSB4eXoKMjEuIHh4eAogIDEuIGZvbwogICAgMS4gYmFyCjMxLiB5eXkKCtlWPiAqKmxldmVsIDFhKioKPj4gbGV2ZWwgMmEKPj4gbGV2ZWwgMmIKPgo-IGxldmVsIDFiCgpbKipmb28qKl0oYmFyKQoKKiohW2Zvb10oYmFyKSoqCgo +// Downloaded @ 2023-03-05T09:08:51.082Z +// Source: https://demo.thi.ng/umbrella/parse-playground/#ldoNwUROTDE6IDxETkw-KyA9PiBkaXNjYXJkIDsKRE5MMjogPE5MPnsyLH0gOwpsYnI6ICdcXCchIDxOTD4hIDsKZXNjOiAnXFwnISAoICc8JyB8ICdbJyB8ICdfJyB8ICcqJyB8ICdgJyB8ICd-JyB8ICc6JyApIDsKaW5saW5lZGVsaW06ICggIiFbIiB8ICdbJyB8ICIqKiIgfCAnXycgfCAnYCcgfCAnPCcgfCAnXFwnIHwgIn5-IiB8ICIgOiIgfCA8bGJyPiApIDsKZGVsaW06ICggPGlubGluZWRlbGltPiB8IDxETkwyPiApIDsKZGVsaW0xOiAoIDxpbmxpbmVkZWxpbT4gfCA8Tkw-ICkgOwpib2R5OiAuKD8tPGRlbGltPiEpID0-IGpvaW4gOwpib2R5MTogLig_LTxkZWxpbTE-ISkgPT4gam9pbiA7Cgp3aWtpcmVmOiAiW1siISAuKD8rIl1dIiEpID0-IGpvaW4gOwpmbnJlZjogIlteIiEgPGxhYmVsPiA7CmZub3RlOiA8TFNUQVJUPiAiW14iISA8Zm5sYWJlbD4gPFdTMT4gPHBhcmE-IDsKZm5sYWJlbDogLig_KyJdOiIhKSA9PiBqb2luIDsKbGFiZWw6IC4oPysnXSchKSA9PiBqb2luIDsKdGFyZ2V0OiAuKD8rJyknISkgPT4gam9pbiA7Cmxpbms6ICdbJyEgPGxpbmtsYWJlbD4gJygnISA8dGFyZ2V0PiA7CmxpbmtyZWY6ICdbJyEgPGxpbmtsYWJlbD4gJ1snISA8bGFiZWw-IDsKbGlua2RlZjogPExTVEFSVD4gJ1snISA8bGFiZWw-ICc6JyEgPFdTMT4gPGxkdGFyZ2V0PiA7CmxkdGFyZ2V0OiAuKD8rPEROTDE-KSA9PiBqb2luIDsKbGlua2xhYmVsOiAoPGltZz4gfCA8Ym9sZD4gfCA8aXRhbGljPiB8IDxzdHJpa2U-IHwgPGNvZGU-IHwgPGVtb2ppPiB8IDxsaW5rYm9keT4pKiAnXSchIDsKbGlua2RlbGltOiAoICIhWyIgfCAnWycgfCAiKioiIHwgJ18nIHwgIn5-IiB8ICdgJyB8ICddJykgOwpsaW5rYm9keTogLig_LTxsaW5rZGVsaW0-ISkgPT4gam9pbiA7CmltZzogIiFbIiEgPGxhYmVsPiAnKCchIDx0YXJnZXQ-IDsKYm9sZDogIioqIiEgKDx3aWtpcmVmPiB8IDxpbWc-IHwgPGZucmVmPiB8IDxsaW5rcmVmPiB8IDxsaW5rPiB8IDxpdGFsaWM-IHwgPHN0cmlrZT4gfCA8Y29kZT4gfCA8ZW1vamk-IHwgPGJvZHkxPikqICIqKiIhIDsKaXRhbGljOiAnXychICg8d2lraXJlZj4gfCA8aW1nPiB8IDxmbnJlZj4gfCA8bGlua3JlZj4gfCA8bGluaz4gfCA8Ym9sZD4gfCA8c3RyaWtlPiB8IDxjb2RlPiB8IDxlbW9qaT4gfCA8Ym9keTE-KSogJ18nISA7CnN0cmlrZTogIn5-IiEgKDx3aWtpcmVmPiB8IDxpbWc-IHwgPGZucmVmPiB8IDxsaW5rcmVmPiB8IDxsaW5rPiB8IDxib2xkPiB8IDxpdGFsaWM-IHwgPGNvZGU-IHwgPGVtb2ppPiB8IDxib2R5MT4pKiAifn4iISA7CmNvZGU6ICdgJyEgLig_KydgJyEpID0-IGpvaW4gOwprYmQ6ICI8a2JkPiIhIC4oPysiPC9rYmQ-IiEpID0-IGpvaW4gOwplbW9qaTogJyAnPyAnOichICg8QUxQSEFfTlVNPiB8ICcrJyB8ICctJykoPysnOichKSA9PiBqb2luIDsKcGFyYTogKDx3aWtpcmVmPiB8IDxpbWc-IHwgPGZucmVmPiB8IDxsaW5rcmVmPiB8IDxsaW5rPiB8IDxib2xkPiB8IDxpdGFsaWM-IHwgPHN0cmlrZT4gfCA8Y29kZT4gfCA8a2JkPiB8IDxlbW9qaT4gfCA8bGJyPiB8IDxlc2M-IHwgPGJvZHk-KSogPEROTDI-ISA7CgpoZGxldmVsOiAnIycrID0-IGNvdW50IDsKaGRpZDogInsjIiEgKDxBTFBIQV9OVU0-IHwgJy0nKSsgJ30nISA9PiBqb2luIDsKaGRkZWxpbTogKCA8ZGVsaW0xPiB8ICJ7IyIgKSA7CmhkYm9keTogLig_LTxoZGRlbGltPiEpID0-IGpvaW4gOwpoZDogPExTVEFSVD4gPGhkbGV2ZWw-IDxXUzA-CiAgICAoPHdpa2lyZWY-IHwgPGltZz4gfCA8Zm5yZWY-IHwgPGxpbms-IHwgPGJvbGQ-IHwgPGl0YWxpYz4gfCA8c3RyaWtlPiB8IDxjb2RlPiB8IDxlbW9qaT4gfCA8aGRib2R5PiApKiA8aGRpZD4_IDxETkwxPiA7CgpsaWxldmVsOiAnICcqID0-IGNvdW50IDsKdWludDogPERJR0lUPisgPT4gaW50IDsKdWxpZDogPEROTD4gPFdTMD4gJy0nISA7Cm9saWQ6IDxETkw-IDxXUzA-IDxESUdJVD4rISAnLichIDsKbGlkZWxpbTogKCA8ZGVsaW0-IHwgPHVsaWQ-IHwgPG9saWQ-ICkgOwpsaWJvZHk6IC4oPy08bGlkZWxpbT4hKSA9PiBqb2luIDsKdG9kbzogJ1snISBbIHhYXSAnXSchIDxXUzE-ID0-IGhvaXN0UiA7CnVsaXRlbTogPExTVEFSVD4gPGxpbGV2ZWw-ICItICIhIDx0b2RvPj8KICAgICAgICAoPHdpa2lyZWY-IHwgPGltZz4gfCA8Zm5yZWY-IHwgPGxpbms-IHwgPGJvbGQ-IHwgPGl0YWxpYz4gfCA8c3RyaWtlPiB8IDxjb2RlPiB8IDxlbW9qaT4gfCA8bGlib2R5PiApKiA8RE5MPiA7Cm9saXRlbTogPExTVEFSVD4gPGxpbGV2ZWw-IDx1aW50PiAiLiAiISA8dG9kbz4_CiAgICAgICAgKDx3aWtpcmVmPiB8IDxpbWc-IHwgPGZucmVmPiB8IDxsaW5rPiB8IDxib2xkPiB8IDxpdGFsaWM-IHwgPHN0cmlrZT4gfCA8Y29kZT4gfCA8ZW1vamk-IHwgPGxpYm9keT4gKSogPEROTD4gOwpsaXN0OiAoPHVsaXRlbT4gfCA8b2xpdGVtPikrIDxETkwxPiA7CgpjYmRlbGltOiA8TFNUQVJUPiAiYGBgIiEgOwpjb2RlYmxvY2s6IDxjYmRlbGltPiEgPGNvZGVtZXRhPiA8Y29kZWJvZHk-IDxETkwxPiA7CmNvZGVtZXRhOiAuKD8rPE5MPiEpID0-IGpvaW4gOwpjb2RlYm9keTogLig_KzxjYmRlbGltPikgPT4gam9pbiA7CgpjdXN0b21kZWxpbTogPExTVEFSVD4gIjo6OiIhIDsKY3VzdG9tYmxvY2s6IDxjdXN0b21kZWxpbT4hIDxjdXN0b21tZXRhPiA8Y3VzdG9tYm9keT4gPEROTDE-IDsKY3VzdG9tbWV0YTogLig_KzxOTD4hKSA9PiBqb2luIDsKY3VzdG9tYm9keTogLig_KzxjdXN0b21kZWxpbT4pID0-IGpvaW4gOwoKbWV0YWJsb2NrOiA8TFNUQVJUPiAie3t7IiEgPG1ldGFib2R5PiA8RE5MMT4gOwptZXRhYm9keTogLig_KzxtZXRhZW5kPiEpID0-IGpvaW4gOwptZXRhZW5kOiAifX19IiA8TEVORD4gOwoKYnFsZXZlbDogJz4nKyA9PiBjb3VudCA7CmJxbGluZTogPExTVEFSVD4gPGJxbGV2ZWw-ICcgJz8hCiAgICAgICAgKDx3aWtpcmVmPiB8IDxpbWc-IHwgPGZucmVmPiB8IDxsaW5rPiB8IDxib2xkPiB8IDxpdGFsaWM-IHwgPHN0cmlrZT4gfCA8Y29kZT4gfCA8ZW1vamk-IHwgPGJxbGJyPiB8IDxib2R5MT4pKiA8RE5MPiA7CmJxbGJyOiAnXFwnISg_LTxETkw-KSA7CmJxdW90ZTogPGJxbGluZT4rIDxETkwxPiA7Cgp0ZGVsaW06ICg8aW5saW5lZGVsaW0-IHwgJ3wnICkgOwp0Ym9keTogLig_LTx0ZGVsaW0-ISkgPT4gam9pbiA7CnRjZWxsOiA8V1MwPiAoPHdpa2lyZWY-IHwgPGltZz4gfCA8Zm5yZWY-IHwgPGxpbms-IHwgPGJvbGQ-IHwgPGl0YWxpYz4gfCA8c3RyaWtlPiB8IDxjb2RlPiB8IDxlbW9qaT4gfCA8dGJvZHk-ICkqICd8JyEgOwp0cm93OiA8TFNUQVJUPiAnfCchIDx0Y2VsbD4oPys8RE5MPikgOwp0YWJsZTogPHRyb3c-KyA8RE5MMT4gOwoKaHI6ICItLSIgJy0nKyA8RE5MMT4gPT4gam9pbiA7CgptYWluOiA8V1MwPiAoPGhkPiB8IDxsaXN0PiB8IDxicXVvdGU-IHwgPGNvZGVibG9jaz4gfCA8Y3VzdG9tYmxvY2s-IHwgPG1ldGFibG9jaz4gfCA8dGFibGU-IHwgPGhyPiB8IDxmbm90ZT4gfCA8bGlua2RlZj4gfCA8cGFyYT4pKiA7pG1haW7aAqgjIEhlbGxvIDp3b3JsZDo6c21pbGU6W15hYmNdIHsjaGQxfQoKIyMgYWJjIHsjaGQyfQojIyBkZWYKCmEgXDwgYgoKTGV0IGBleHAyKHgpID0gMioqeGAKClByZXNzIDxrYmQ-Q29udHJvbDwva2JkPiArIDxrYmQ-Ujwva2JkPiB0byByZWxvYWQuCgo6OjphbGVydApUZXN0IFtsYWJlbF1bcmVmcmVmXQo6OjoKCi0tLQoKe3t7IHsiaWQiOiAxMjMsICJwcm90ZWN0ZWQiOiB0cnVlIH0gfX19ClNvIFtbc21pbGVfZm9vMTJdXSDwn5iNIHdoYXQncyBbdGhpc10oLi90aGlzLm1kKVteMl0gdGhlbjoKCkEgW2xpbmsgX3dpdGggKipuZXN0ZWQgfn5mb3JtYXR-fioqIGFuZCBgcmVmZXJlbmNlYF9dW3JlZnJlZl0uLi4KCmBgYHRzIHRhbmdsZTpubyBsbjp5ZXMKY29uc3QgZm9vID0gNDI7Ci8vIHRoZSBlbmQKYGBgCgoxLiBmb28KICA0LiBiYXIKICAgICBiYXogYW5kIHNvIG9uCiAgICAgbXVsdGlsaW5lCjIuIFsgXSB0YXNrCjMuIFt4XSAqKmRvbmUqKgoKPiBibG9ja3F1b3RlCj4gbXVsdGlsaW5lIHRvb1wKPj4gbmVzdGVkCj4-PiBsZXZlbCAzCj4gLS0gc291cmNlCgp8IGZvbyB8IGJhciB8Cnw6LXwtOnwKfCBbW3JlZiMyMl1dIHwgYG9uYCAqKm9mZioqIHwKClteYWJjXTogRm9vIF9iYXJfIGJhegoKW14yXTogVE9ETwoKW3JlZnJlZl06IGh0dHA6Ly90aGkubmcvCltmb29dOiAuL2Zvby5tZCNiYXIKCtk1MTEuIGFiYwogIC0geHl6CjIxLiB4eHgKICAxLiBmb28KICAgIDEuIGJhcgozMS4geXl5CgrZVj4gKipsZXZlbCAxYSoqCj4-IGxldmVsIDJhCj4-IGxldmVsIDJiCj4KPiBsZXZlbCAxYgoKWyoqZm9vKipdKGJhcikKCioqIVtmb29dKGJhcikqKgoK import type { Fn3, IObjectOf, Primitive } from "@thi.ng/api"; import { peek } from "@thi.ng/arrays/peek"; @@ -58,8 +58,11 @@ emoji: ' '? ':'! ( | '+' | '-')(?+':'!) => join ; para: ( | | | | | | | | | | | | | )* ! ; hdlevel: '#'+ => count ; +hdid: "{#"! ( | '-')+ '}'! => join ; +hddelim: ( | "{#" ) ; +hdbody: .(?-!) => join ; hd: - ( | | | | | | | | | )* ; + ( | | | | | | | | | )* ? ; lilevel: ' '* => count ; uint: + => int ; @@ -303,6 +306,7 @@ export const transformScope: Fn3< body1: "body", bqlbr: "lbr", bqline: "repeat0", + hdbody: "body", label: "body", libody: "body", linkbody: "body", @@ -448,18 +452,11 @@ export const transformScope: Fn3< const body: any[] = []; const level = children![0].result; transformScope(children![1], ctx, body); - ctx.headings.push({ level, body }); __trimBody(body); - __collect( - acc, - ctx.tags.heading( - ctx, - level, - slugifyGH(extractBody(body).join("")), - body, - ctx.meta - ) - ); + const slug = + children![2]?.result || slugifyGH(extractBody(body).join("")); + ctx.headings.push({ level, body }); + __collect(acc, ctx.tags.heading(ctx, level, slug, body, ctx.meta)); ctx.meta = null; }, diff --git a/packages/hiccup-markdown/test/parse.ts b/packages/hiccup-markdown/test/parse.ts index bcf335a439..a550ec18bb 100644 --- a/packages/hiccup-markdown/test/parse.ts +++ b/packages/hiccup-markdown/test/parse.ts @@ -2,9 +2,10 @@ import { group } from "@thi.ng/testament"; import * as assert from "assert"; import { parse } from "../src/index.js"; -const check = (src: string, expected: any[]) => { - const { result, complete } = parse(src); - assert.ok(complete); +const check = (src: string, expected: any[], expectFailure = false) => { + const { result, complete, state } = parse(src); + if (expectFailure) assert.ok(!complete, "didn't fail"); + else assert.ok(complete, `failed to parse at line ${state.l}`); assert.deepStrictEqual(result, expected, JSON.stringify(result, null, 4)); }; @@ -202,6 +203,15 @@ with linebreaks ]); }, + "hd optional id": () => { + check(`# Heading {#custom-id-123}`, [ + ["h1", { id: "custom-id-123" }, "Heading"], + ]); + check(`# _Head_ing{#custom_id}`, [ + ["h1", { id: "custom_id" }, ["em", {}, "Head"], "ing"], + ]); + }, + hr: () => { check(`---`, [["hr", { __length: 3 }]]); check(`----`, [["hr", { __length: 4 }]]);