#Manipulate Markdown Content

21 messages · Page 1 of 1 (latest)

lean otter
#

Hi, I am trying to manipulate markdown content before rendering it.

const post = await getEntryBySlug('blog', Astro.params.slug);
post.body = post.body.replaceAll('/public', '');
const { Content } = await post.render();

This does change post.body, but the output from Content is still the original. How can I change that?

oak kernel
#

What is the purpose of replacing post.body? 👀

tulip wedge
#

youll probably want a remark / rehype plugin

lean otter
lean otter
latent sundial
#

Markdoc allows you to define custom tags that can use Astro components to render complex content

#

MDX can do the same, but MDX is more of a "programming language in Markdown", depending on the use-case of whom is supposed to edit the pages. MDX is more powerful, but Markdoc is more user friendly.

lean otter
lean otter
# tulip wedge youll probably want a remark / rehype plugin

Hi Oliver, I have switched to use mdx with components, but still need to change all urls in the conent. So I was looking into using rehype-url-inspector, but I can not get it to work. Do you have an example of how to define the js to work on every mdx file? I have the following:

  // in astro.config.mjs
  import { fixPublicAssetUrls } from './src/js/fixPublicAssetUrls';
  ...
  markdown: {
    rehypePlugins: [fixPublicAssetUrls],
  },

but now I am lost.

And in fixPublicAssetUrls.js I tried something like:

import unified from 'unified';
import inspectUrls from '@jsdevtools/rehype-url-inspector';
import parse from 'rehype-parse';
import stringify from 'rehype-stringify';
import toVFile from 'to-vfile';

// now use rehype url inspector to replace the urls given in the markdown files
export default function fixPublicAssetUrls() {
  return (tree, file) => {
    unified()
      .use(parse)
      .use(inspectUrls, {
        inspectEach(url) {
          console.log(url);
          // replace urls here
          if (url.href.startsWith('/public/assets/')) {
            url.href = url.href.replaceAll('/public/assets/', '/assets/');
          }
        }, // This function is called for each URL as it's found
        selectors: [
          'a[href]', // Only search for links, not other types of URLs
          'img[src]', // CSS selectors for custom URL attributes
        ],
      })
      .use(stringify)
      .process(tree, (err, file) => {
        if (err) {
          console.error(err);
        }
        toVFile.writeSync(file);
      });
  };
}

But I get markdown.rehypePlugins.0 Invalid input.

I really only want to replace all occurrances of /public/assets with /assets for all urls in the content.

tulip wedge
#

can i ask why you need to replaceall like this? is it old content migrated from a framework that required you to type /public?

but bsaically you dont need to do the unified . use etc astro has all that hooked up already you just need the actual logic

put this in /src/js/fixpublicasseturls then import rehypeRemovePublicFromUrls from ./src/js/fixpublicasseturls

import { visit } from 'unist-util-visit';

/**
 * Rehype plugin to remove `/public/` from URLs in href and src attributes.
 * 
 * This function will traverse the HAST tree, find anchor tags (`<a>`) and image tags (`<img>`),
 * and update the `href` or `src` attribute if they start with `/public/`, removing the `/public/` prefix.
 * 
 * @returns {Function} - A transformer function that manipulates the HAST tree in place.
 */
export default function rehypeRemovePublicFromUrls() {
  return (tree) => {
    visit(tree, 'element', (node) => {
      // Check if the node is an anchor element with an href attribute or an img element with a src attribute
      if (node.tagName === 'a' && node.properties && node.properties.href) {
        let href = node.properties.href;
        
        // If the href starts with '/public/', replace '/public/' with '/'
        if (href.startsWith('/public/')) {
          node.properties.href = href.replace('/public/', '/');
        }
      } else if (node.tagName === 'img' && node.properties && node.properties.src) {
        let src = node.properties.src;

        // If the src starts with '/public/', replace '/public/' with '/'
        if (src.startsWith('/public/')) {
          node.properties.src = src.replace('/public/', '/');
        }
      }
    });
  };
}
#

I cant promise this exact logic works but it should get you going at least @oak kernel

oak kernel
#

@lean otter

tulip wedge
#

oh sorry

lean otter
#

@tulip wedge Thanks so much Oliver! I need to do this because of a flaw in the CloudCannon CMS editor, where I needed to omit a setting for the public folder as I wanted to allow uploads into the /src/assets folder as well. This issue may not be the case anymore, but at the moment it is still much easier for me to rewrite those urls instead of rewriting the whole logic of file uploads and image processing, which was really time consuming in the first place. Their Astro integration has (or had) some open issues.

#

Your code works great for a tags generated through markdown. But any html that is within the markdown code, is omitted. Do you know why?

#

The nodes that are visited seem not to include plain html code nodes.

tulip wedge
#

i dont know exactly off the top of my head probably due to tagName check I'd guess

#

I think its raw if you use actual html

#

not sure though what you can do is console log node at the top of te visit function and see everything

lean otter
#

Great, thanks so much @tulip wedge . I got it working like this: