Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can the frontmatter be dynamic? #287

Open
poornimasolanki opened this issue Jul 31, 2021 · 10 comments
Open

Can the frontmatter be dynamic? #287

poornimasolanki opened this issue Jul 31, 2021 · 10 comments
Labels
🕵️ I'm looking into it enhancement New feature or request
Milestone

Comments

@poornimasolanki
Copy link

poornimasolanki commented Jul 31, 2021

Hi, I have a blog created with MDsveX written in Sveltekit and deployed on Netlify. It works amazingly.

But whenever I want to use images I have to use links in the blog post. I want to be able to import photos directly into the .svx files. Here's an example of what I want to do:

image

Is something like this possible?

E: Can those variables, coverimg and thumbnailimg be exported and then imported along with frontmatter, because that would solve my problem as well?

@pngwn
Copy link
Owner

pngwn commented Aug 4, 2021

This isn't currently possible, front matter is expected to be static.

You could just export them, they will still be available in the document if needed:

<script context='module'>
  import coverimg from '$assets/demopost/cover.png';
  import thumbnailimg from '$assets/demopost/thumbnail.png';

  export { coverimg, thumbnailimg };
</script>

@michaeloliverx
Copy link

michaeloliverx commented Aug 10, 2021

I have hit a need for something like this a few times also, it would be useful if there was hooks to manipulate the metadata export during the transformation so we could so something like this:

---
title: My lovely article
author: Dr. Fabuloso the Fabulous
coverimage: some-image.jpg
coverimageAlt: Cool coverphoto
---
<script context="module">
  import coverimg from "$assets/demopost/some-image.jpg"

  export let metadata = {
    title: "My lovely article",
    author: "Dr. Fabuloso the Fabulous",
    coverimage: coverimg,
    coverimageAlt: "Cool coverphoto"
  };
</script>

Would allow us to use this data inside layout files

@pngwn
Copy link
Owner

pngwn commented Aug 10, 2021

I guess we could merge all exports from the context=module script into metadata which would allow overrides:

---
title: My lovely article
author: Dr. Fabuloso the Fabulous
coverimage: some-image.jpg
coverimageAlt: Cool coverphoto
---
<script context="module">
  import coverimage from "$assets/demopost/some-image.jpg"

  export { coverimage };
</script>

Another alternative is just to pass all exports from context module down as props.

I'll have to think about this one.

@babichjacob
Copy link
Contributor

This already works:

---
title: My lovely article
author: Dr. Fabuloso the Fabulous
coverimageAlt: Cool coverphoto
---

<script context="module">
  import coverimage from "$assets/demopost/some-image.jpg"

  metadata.coverimage = coverimage;
</script>

Blog post content

@michaeloliverx
Copy link

@babichjacob Just tested yes it does!! Thanks

@pngwn
Copy link
Owner

pngwn commented Sep 10, 2021

What is the thinking on this, is there a reasonable way to make it easier, or is the current situation okay?

@michaeloliverx
Copy link

I want to automatically convert this:

---
title: My lovely article
coverimage: ./testing.jpg
coverimageAlt: Cool coverphoto
---

into this:

<script context="module">
  import ___ASSET___0 from "./testing.jpg";
  export const metadata = {
    title: "My lovely article",
    coverimage: ___ASSET___0,
    coverimageAlt: "Cool coverphoto",
  };
  const { title, coverimage, coverimageAlt } = metadata;
</script>

I managed to figure this out with a simple svelte preprocessor:

// svelte.config.js
import preprocess from "svelte-preprocess";
import { mdsvex } from "mdsvex";
import adapterStatic from "@sveltejs/adapter-static";

import { parse, walk } from "svelte/compiler";
import MagicString from "magic-string";

/** @returns {import("svelte/types/compiler/preprocess").PreprocessorGroup} */
function plugin() {
  return {
    async markup({ content, filename }) {
      if (!filename.endsWith(".svelte.md")) return { code: content };

      const s = new MagicString(content);
      const ast = parse(content, { filename });

      walk(ast.module, {
        enter(node) {
          if (!(node.type === "VariableDeclarator" && node.id.name === "metadata")) return;
          for (const property of node.init.properties) {
            if (property.key.value === "coverimage") {
              const importText = `import ___ASSET___0 from "${property.value.value}";`;
              s.appendLeft(ast.module.content.start, importText);
              s.overwrite(property.value.start, property.value.end, "___ASSET___0");
              console.log(s.toString());
            }
          }
        },
      });

      return {
        code: s.toString(),
        map: s.generateMap(),
      };
    },
  };
}

/** @type {import('@sveltejs/kit').Config} */
const config = {
  extensions: [".svelte", ".svelte.md"],
  preprocess: [mdsvex({ extensions: [".svelte.md"] }), plugin(), preprocess()],
  kit: {
    target: "#svelte",
    adapter: adapterStatic(),
  },
};

export default config;

This works but is pretty fragile, I wonder would an API like the following be better:

/** @type {MdsvexOptions} */
export const mdsvexConfig = {
  extensions: [".svelte.md"],
  frontmatter: {
    transform: {
      coverimage(value) {
        return {
          newValue: "coverimage",
          import: `import ___ASSET___0 from "${value}"`,
        };
      },
    },
  },
};

@pngwn
Copy link
Owner

pngwn commented Sep 18, 2021

Interesting. I'll have a think about this. We could even do something simpler for the common case and have some default handling for certain things:

/** @type {MdsvexOptions} */
export const mdsvexConfig = {
  extensions: [".svelte.md"],
  frontmatter: {
    types: {
      coverimage: 'image-import'
    },
  },
};

That said we could also just detect this automatically (for images) and enable/ disable based on an boolean options, or do it based on extension instead:

/** @type {MdsvexOptions} */
export const mdsvexConfig = {
  extensions: [".svelte.md"],
  frontmatter: {
    types: [
     /\.jpg$|\.png$/, 'image-import' // or a function to handle it
    ],
  },
};

In line with other parts of the design for v1, I'm likely to handle this in an 'official' plugin:

import { transformFrontmatter } from 'mdsvex/plugins';

/** @type {MdsvexOptions} */
export const mdsvexConfig = {
  extensions: [".svelte.md"],
  plugins: [
    transformFrontmatter({
       transform: { 
         // not sure if a matcher on the value or a handler for the prop name is most appropriate, probably name based
         match: /\.jpg$|\.png$/,          
         handler: 'import' // builtin or a function to handle it
       } 
    })
  ],
};

@pngwn pngwn added 🕵️ I'm looking into it enhancement New feature or request mdsvex labels Sep 18, 2021
@pngwn pngwn added this to the 1.0 milestone Oct 16, 2021
@Madd0g
Copy link

Madd0g commented Nov 3, 2021

I thought a lot about communication between the markdown file and the layout file.

I went about this a little differently, I went into the mdsvex code and added let:bridge={bridge}.

<Layout_MDSVEX_DEFAULT let:bridge={bridge} {...$$props} .....

then in my blog.svelte, I create the slot with:

<slot {bridge} />

// this works and gets updated from the frontmatter
export let title = 'default title'; 

// this works for dynamically setting the title
let bridge = {
  setTitle(value) {
    title = value;
  }
}

this way I can call the setTitle from the markdown file and dynamically update the title based on information I only have in the markdown.

I only did this to see if it would work. I don't know if it's a good idea, I want to get thoughts from people more experienced with svelte than I am. Should this be a feature in mdsvex (allowing 2-way communication through the "markdown slot", instead of just passing things one way)

EDIT: actually, I see the solution babichjacob suggested works for setting the title too.

@thitemple
Copy link

Hey there folks, I'm sorry for reviving an older issue but I did not understand the solution here (I should also mention that I'm learning SvelteKit at the same time).
What I am trying to do is what was mentioned above:

---
cover: ./cover.png
---

when I consume this I'm getting an error:
Error: Not found: /posts/post-slug/cover.png

My posts folder is side by side with the lib folder and I understand that normally that URL path would be under static, but I was under the assumption that by adding the image path in the frontmatter it would be bundled with vite.

I guess I'm wrong on that, so what is the solution for the above? Thanks

@pngwn pngwn removed v1 labels Feb 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🕵️ I'm looking into it enhancement New feature or request
Projects
No open projects
Status: Refine
Development

No branches or pull requests

6 participants