content icon indicating copy to clipboard operation
content copied to clipboard

Manipulating mdast / file.body during afterParse hook?

Open N0K0 opened this issue 2 years ago • 5 comments

Discussed in https://github.com/nuxt/content/discussions/2411

Originally posted by N0K0 November 5, 2023 Hi! Hitting a wall when it comes to using the afterParse hook. I'm able to change the value of text nodes, but I'm unable to change the node type, like for example from text to inlineCode, which is defined as just swapping type from text to inlineCode

Getting the same error when I try to do the manipulation during a visit run, which i assumed was related to manipulation during iteration, and when using findAndReplace from mdast-util-find-and-replace

So I figured it might still work during direct reference without any iterators and the likes, but still hitting the wall.

Any help at all would be greatly appreciated!

My goal is to make a plugin to support the Foam format, which means supporting Wiki links like [[ this ]] and [[ this|alias]], as well as inline #tags that links to a common page for example :)

index.md

# Index

## Tags

#tag1 #tag2 #tag3  
#tag_newline  
#tag1  
#tag1  

## Wiki links

[[ 1 |Alias for 1]] (Broken)  
[[2 |Alias for 2]] (Broken)  
[[3|Alias for 3]]  (Valid)  
[[ 4|Alias for 4]] (Broken)  

:heavy_check_mark: Can change value of a text node

Code

nitroApp.hooks.hook("content:file:afterParse", (file: MarkdownParsedContent ) => {
    if ( !useRuntimeConfig().parse_after ) return
    if (!file._id.endsWith(".md")) return

    if (file.body.children[2]?.children[2]) {
      const link_node = {type: "inlineCode", value: "test"}
      // Targeting the #tag_newline node
      file.body.children[2].children[2].value = "MANIPULATED"
    }
  });

Output

image

:negative_squared_cross_mark: Unable to change node type to anything else:

Code

nitroApp.hooks.hook("content:file:afterParse", (file: MarkdownParsedContent ) => {
    if ( !useRuntimeConfig().parse_after ) return
    if (!file._id.endsWith(".md")) return

    if (file.body.children[2]?.children[2]) {
      const link_node = {type: "inlineCode", value: "test"}
      console.log(file.body.children[2].children[2])
      file.body.children[2].children[2].type = "inlineCode"
    }
  });

Output

{ type: 'text', value: '\n#tag3\n#tag_newline\n#tag1' }

Stacktrace

[nuxt] [request error] [unhandled] [500] src.replace is not a function
  at Object.escapeHtmlComment (./node_modules/@vue/shared/dist/shared.cjs.js:334:14)  
  at renderVNode (./node_modules/@vue/server-renderer/dist/server-renderer.cjs.js:720:34)  
  at renderVNodeChildren (./node_modules/@vue/server-renderer/dist/server-renderer.cjs.js:759:5)  
  at ssrRenderSlotInner (./node_modules/@vue/server-renderer/dist/server-renderer.cjs.js:120:7)  
  at Module.ssrRenderSlot (./node_modules/@vue/server-renderer/dist/server-renderer.cjs.js:95:3)  
  at _sfc_ssrRender (./node_modules/@nuxtjs/mdc/dist/runtime/components/prose/ProseP.vue:11:25)  
  at renderComponentSubTree (./node_modules/@vue/server-renderer/dist/server-renderer.cjs.js:683:9)  
  at renderComponentVNode (./node_modules/@vue/server-renderer/dist/server-renderer.cjs.js:631:12)  
  at renderVNode (./node_modules/@vue/server-renderer/dist/server-renderer.cjs.js:743:14)  
  at renderComponentSubTree (./node_modules/@vue/server-renderer/dist/server-renderer.cjs.js:698:7)
[nuxt] [request error] [unhandled] [500] src.replace is not a function
  at Object.escapeHtmlComment (./node_modules/@vue/shared/dist/shared.cjs.js:334:14)  
  at renderVNode (./node_modules/@vue/server-renderer/dist/server-renderer.cjs.js:720:34)  
  at renderVNodeChildren (./node_modules/@vue/server-renderer/dist/server-renderer.cjs.js:759:5)  
  at ssrRenderSlotInner (./node_modules/@vue/server-renderer/dist/server-renderer.cjs.js:120:7)  
  at Module.ssrRenderSlot (./node_modules/@vue/server-renderer/dist/server-renderer.cjs.js:95:3)  
  at _sfc_ssrRender (./node_modules/@nuxtjs/mdc/dist/runtime/components/prose/ProseP.vue:11:25)  
  at renderComponentSubTree (./node_modules/@vue/server-renderer/dist/server-renderer.cjs.js:683:9)  
  at renderComponentVNode (./node_modules/@vue/server-renderer/dist/server-renderer.cjs.js:631:12)  
  at renderVNode (./node_modules/@vue/server-renderer/dist/server-renderer.cjs.js:743:14)  
  at renderComponentSubTree (./node_modules/@vue/server-renderer/dist/server-renderer.cjs.js:698:7)

Tsconfig:

export default defineNuxtConfig({
  modules: [
    '@nuxt/content'
  ],
  runtimeConfig: {
    stripBrackets: true,
    parse_before: false,
    parse_after: true
  },
  content: {
    documentDriven: true,
    markdown: {
    //  anchorLinks: false
    }
  }
})

```</div>

N0K0 avatar Jan 14 '24 20:01 N0K0

Thanks for reporting the issue? Do you mind providing a reproduction? You can use Nuxt Starter if you want to

farnabaz avatar Jan 17 '24 11:01 farnabaz

Hi @farnabaz, the following is the smallest example I managed to make :) Only touched the index.md file to fill with some alternative content and the plugin https://stackblitz.com/edit/github-njqpgs?file=server%2Fplugins%2Fafter.ts

The log is different from when I last tried in November, but the final error is the same src.replace is not a function

Log:

{
  _path: '/about',
  _dir: '',
  _draft: false,
  _partial: false,
  _locale: '',
  title: 'About Content v2',
  description: 'Back home',
  excerpt: undefined,
  body: {
    type: 'root',
    children: [ [Object], [Object] ],
    toc: { title: '', searchDepth: 2, depth: 2, links: [] }
  },
  _type: 'markdown',
  _id: 'content:about.md',
  _source: 'content',
  _file: 'about.md',
  _extension: 'md'
}
[Vue warn]: Failed to resolve component: 
If this is a native custom element, make sure to exclude it from component resolution via compilerOptions.isCustomElement.
{
  _path: '/',
  _dir: '',
  _draft: false,
  _partial: false,
  _locale: '',
  title: 'Index',
  description: '',
  excerpt: undefined,
  body: {
    type: 'root',
    children: [ [Object], [Object], [Object], [Object] ],
    toc: { title: '', searchDepth: 2, depth: 2, links: [Array] }
  },
  _type: 'markdown',
  _id: 'content:index.md',
  _source: 'content',
  _file: 'index.md',
  _extension: 'md'
}
[Vue warn]: Failed to resolve component: 
If this is a native custom element, make sure to exclude it from component resolution via compilerOptions.isCustomElement.
[Vue warn]: Invalid vnode type when creating vnode: undefined.
[nuxt] [request error] [unhandled] [500] src.replace is not a function

[Vue warn]: Failed to resolve component: 
If this is a native custom element, make sure to exclude it from component resolution via compilerOptions.isCustomElement.
[Vue warn]: Failed to resolve component: 
If this is a native custom element, make sure to exclude it from component resolution via compilerOptions.isCustomElement.
[Vue warn]: Invalid vnode type when creating vnode: undefined.
[nuxt] [request error] [unhandled] [500] src.replace is not a function

N0K0 avatar Jan 17 '24 20:01 N0K0

I see, This happens because the modified tree is invalid. If you want to change a text into a code, you can do this:

if (file.body.children[2]?.children[2]) {
      const link_node = {
        type: 'element',
        tag: 'code',
        children: [
          {
            type: 'text',
            value: 'test updated',
          },
        ],
      };

      file.body.children[2].children[2] = link_node;
    }

I have updated your reproduction: https://stackblitz.com/edit/github-njqpgs-yxn88u?file=server%2Fplugins%2Fafter.ts

farnabaz avatar Jan 25 '24 16:01 farnabaz

Ah, interesting! Sorry for the slow response. I've been referring to the mdast spec https://github.com/syntax-tree/mdast?tab=readme-ov-file#code

Any place I can read about the structure Content expects? :)

N0K0 avatar Feb 01 '24 12:02 N0K0

The structure of MDC tree is very similar to HAST structure, except that MDC use tag instead of tagName and props instead of properties. Checkout Hast docs here: https://github.com/syntax-tree/hast

farnabaz avatar Mar 08 '24 09:03 farnabaz

This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 30 days.

github-actions[bot] avatar Jul 20 '24 01:07 github-actions[bot]

This issue was closed because it has been stalled for 30 days with no activity.

github-actions[bot] avatar Aug 19 '24 01:08 github-actions[bot]