diff --git a/.changeset/brown-suns-sin.md b/.changeset/brown-suns-sin.md new file mode 100644 index 00000000..8c09c686 --- /dev/null +++ b/.changeset/brown-suns-sin.md @@ -0,0 +1,5 @@ +--- +"svelte-exmarkdown": minor +--- + +Adds the ability for plugins to pass down options to remark2rehype diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b3bdc3d1..cbb5842f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2109,11 +2109,8 @@ packages: mdast-util-phrasing@4.0.0: resolution: {integrity: sha512-xadSsJayQIucJ9n053dfQwVu1kuXg7jCTdYsMK8rqzKZh52nLfSH/k0sAxE0u+pj/zKZX+o5wB+ML5mRayOxFA==} - mdast-util-to-hast@13.0.2: - resolution: {integrity: sha512-U5I+500EOOw9e3ZrclN3Is3fRpw8c19SMyNZlZ2IS+7vLsNzb2Om11VpIVOR+/0137GhZsFEF6YiKD5+0Hr2Og==} - - mdast-util-to-hast@13.1.0: - resolution: {integrity: sha512-/e2l/6+OdGp/FB+ctrJ9Avz71AN/GRH3oi/3KAx/kMnoUsD6q0woXlDT8lLEeViVKE7oZxE7RXzvO3T8kF2/sA==} + mdast-util-to-hast@13.2.0: + resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==} mdast-util-to-markdown@2.1.0: resolution: {integrity: sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==} @@ -5257,7 +5254,7 @@ snapshots: hast-util-from-parse5: 8.0.1 hast-util-to-parse5: 8.0.0 html-void-elements: 3.0.0 - mdast-util-to-hast: 13.0.2 + mdast-util-to-hast: 13.2.0 parse5: 7.1.2 unist-util-position: 5.0.0 unist-util-visit: 5.0.0 @@ -5782,18 +5779,7 @@ snapshots: '@types/mdast': 4.0.3 unist-util-is: 6.0.0 - mdast-util-to-hast@13.0.2: - dependencies: - '@types/hast': 3.0.1 - '@types/mdast': 4.0.3 - '@ungap/structured-clone': 1.2.0 - devlop: 1.1.0 - micromark-util-sanitize-uri: 2.0.0 - trim-lines: 3.0.1 - unist-util-position: 5.0.0 - unist-util-visit: 5.0.0 - - mdast-util-to-hast@13.1.0: + mdast-util-to-hast@13.2.0: dependencies: '@types/hast': 3.0.4 '@types/mdast': 4.0.3 @@ -6609,7 +6595,7 @@ snapshots: dependencies: '@types/hast': 3.0.4 '@types/mdast': 4.0.3 - mdast-util-to-hast: 13.1.0 + mdast-util-to-hast: 13.2.0 unified: 11.0.4 vfile: 6.0.1 diff --git a/src/lib/types.ts b/src/lib/types.ts index 5ce06795..9a697806 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -1,5 +1,6 @@ import type { ComponentType, SvelteComponent } from 'svelte'; import type { Pluggable, Processor } from 'unified'; +import type { Options } from 'remark-rehype'; export type Component = ComponentType; export type ComponentsMap = Record< @@ -10,6 +11,7 @@ export type Plugin = { remarkPlugin?: Pluggable; rehypePlugin?: Pluggable; renderer?: ComponentsMap; + remarkToRehypeOptions?: Options; }; export type UnistNode = ReturnType & { diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 829ee050..5cb3f061 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -60,11 +60,43 @@ const rehypeReactPropsToSvelteProps: UnifiedPlugin = () => { }; }; +/** + * Do a merge OF AN ITERABLE on the surface level, then delete the cur[k] + * Used for remark2rehype options reduction + */ +function fakeDeepMerge(agg: T, cur: T, k: K, def: T[K]) { + if (!cur[k]) return; + + agg[k] ??= def; + // Casting here helps avoid adding a billion lines of ts to determine if T[K] is truly an iterable + // This is used only internally so its ok to not truly be 'safe' + agg[k] = ( + Array.isArray(agg[k]) + ? [...agg[k], ...(cur[k] as unknown as Iterable)] + : { ...agg[k], ...cur[k] } + ) as T[K]; + delete cur[k]; +} + export const createParser = (plugins: Plugin[]): Parser => { const processor = unified() .use(remarkParse) .use(plugins.map((plugin) => plugin.remarkPlugin).filter(nonNullable)) - .use(remarkRehype, { allowDangerousHtml: true }) + .use( + remarkRehype, + plugins + .map((plugin) => plugin.remarkToRehypeOptions) + .filter(nonNullable) + .reduce( + (agg, cur) => { + fakeDeepMerge(agg, cur, 'handlers', {}); + fakeDeepMerge(agg, cur, 'passThrough', []); + + return { ...agg, ...cur }; + }, + { allowDangerousHtml: true } + ) + ) .use(plugins.map((plugin) => plugin.rehypePlugin).filter(nonNullable)) .use(rehypeReactPropsToSvelteProps); return (md: string) => processor.runSync(processor.parse(md), md);