#How should I use content layer to render remote markdown

25 messages · Page 1 of 1 (latest)

shy idol
#

How should I use content layer to render remote markdown

plucky radishBOT
#

Hi @shy idol. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

To use the Content Layer API to render remote markdown in Astro, you need to follow these steps:

  1. Enable the Content Layer API by adding the contentLayer flag to the experimental object in your Astro config:
{  
  experimental: {    
    contentLayer: true,  
  }
}

Source (https://docs.astro.build/en/reference/configuration-reference/#experimentalcontentlayer)

  1. Create a loader that fetches collection entries from a remote API. The loader is defined in the collection’s schema and returns an array of entries. Here's an example of a loader fetching data from a remote API:
const countries = defineCollection({  
  loader: async () => {    
    const response = await fetch("<https://restcountries.com/v3.1/all">);    
    const data = await response.json();    
    return data.map((country) => ({      
      id: country.cca3,      
      ...country,    
    }));  
  },
});
export const collections = { countries };

Source (https://docs.astro.build/en/reference/configuration-reference/#creating-a-loader)

  1. Query the collection and render the entries. You can use the getCollection and getEntry functions to query the collection, and the render() function to render the entries:
import { getCollection, getEntry, render } from 'astro:content';
const allBlogPosts = await getCollection('blog');
const labradorData = await getEntry('dogs', 'labrador-retriever');
const { Content, headings } = await render(post);

Source (https://docs.astro.build/en/reference/configuration-reference/#querying-and-rendering-with-the-content-layer-api)

#

Please note that Astro does not include built-in support for remote Markdown. To fetch remote Markdown and render it to HTML, you will need to install and configure your own Markdown parser from npm. This will not inherit from any of Astro’s built-in Markdown and MDX settings that you have configured. Be sure that you understand these limitations before implementing this in your project. Source (https://docs.astro.build/en/guides/markdown-content/#fetching-remote-markdown)

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@shy idol kapa.ai is still learning and improving, please let us know how it did by reacting below

shy idol
#

我在 /conent/content.ts 中定义了一个:

import { defineCollection } from 'astro:content'
import { stringify } from 'qs'
import { handleFetch } from '@/util/api.ts'
import type { ArticleResponse } from '@/types'

const testArticle = defineCollection({
  loader: async () => {
    function getQuery() {
      return stringify(
        {
          filters: {
            category: {
              slug: {
                $eq: 'how-to',
              },
            },
            slug: {
              $eq: 'test000000001',
            },
          },

          populate: {
            category: {
              fields: ['name', 'slug', 'locale'],
              populate: ['seo'],
            },
            author: {
              fields: ['name', 'email', 'introduction', 'slug', 'feature'],
              populate: ['picture', 'social', 'social.logo'], // logo 是 media 填充时要这样进行声明
            },
            product: {
              fields: ['name'],
            },
            seo: {
              populate: '*',
            },
            cover_image: {
              populate: '*',
            },
            tags: {
              fields: ['value', 'color', 'locale'],
            },
            videos: '*',
            recommended_articles: '*',
            localizations: {
              fields: ['locale'],
            },
          },

          publicationState: 'live',
          locale: ['en'],
        },
        { encodeValuesOnly: true },
      )
    }

    const [res] = await handleFetch<ArticleResponse>(`/api/articles?${getQuery()}`)
    if (res) {
      return [{
        ...res.data[0],
        id: 'test000000001',
      }]
    }
    return []
  },
})

export { testArticle }

我应该如何在 astro 中使用它?

#

@plucky radish 我在 /conent/content.ts 中定义了一个:

import { defineCollection } from 'astro:content'
import { stringify } from 'qs'
import { handleFetch } from '@/util/api.ts'
import type { ArticleResponse } from '@/types'

const testArticle = defineCollection({
  loader: async () => {
    function getQuery() {
      return stringify(
        {
          filters: {
            category: {
              slug: {
                $eq: 'how-to',
              },
            },
            slug: {
              $eq: 'test000000001',
            },
          },

          populate: {
            category: {
              fields: ['name', 'slug', 'locale'],
              populate: ['seo'],
            },
            author: {
              fields: ['name', 'email', 'introduction', 'slug', 'feature'],
              populate: ['picture', 'social', 'social.logo'], // logo 是 media 填充时要这样进行声明
            },
            product: {
              fields: ['name'],
            },
            seo: {
              populate: '*',
            },
            cover_image: {
              populate: '*',
            },
            tags: {
              fields: ['value', 'color', 'locale'],
            },
            videos: '*',
            recommended_articles: '*',
            localizations: {
              fields: ['locale'],
            },
          },

          publicationState: 'live',
          locale: ['en'],
        },
        { encodeValuesOnly: true },
      )
    }

    const [res] = await handleFetch<ArticleResponse>(`/api/articles?${getQuery()}`)
    if (res) {
      return [{
        ...res.data[0],
        id: 'test000000001',
      }]
    }
    return []
  },
})

export { testArticle }

我应该如何在 astro 中使用它?

plucky radishBOT
#

Let me check the knowledge base for you. One moment! :books:

#

在 Astro 中使用你在 src/content/config.ts 文件中定义的 testArticle collection,你需要在你的 Astro 组件中导入 getCollectiongetEntry 函数,然后使用这些函数来查询你的 collection。

例如,如果你想在 src/pages/index.astro 中获取所有的文章,你可以这样做:

---
import { getCollection } from 'astro:content';
const articles = await getCollection('testArticle');
---

<ul>
  {articles.map(article => (
    <li>
      <h2>{article.data.title}</h2>
      <p>{article.data.description}</p>
    </li>
  ))}
</ul>

在上述代码中,getCollection('testArticle') 会返回你在 testArticle collection 中定义的所有文章。然后你可以在你的 Astro 组件模板中遍历这些文章并显示它们的标题和描述。

如果你想获取特定的文章,你可以使用 getEntry 函数。例如,如果你想获取 slug 为 'test000000001' 的文章,你可以在 src/pages/[slug].astro 文件中这样做:

---
import { getEntry } from 'astro:content';
const article = await getEntry('testArticle', Astro.params.slug);
---

<h1>{article.data.title}</h1>
<p>{article.data.description}</p>

在上述代码中,getEntry('testArticle', Astro.params.slug) 会返回 slug 为 'test000000001' 的文章。然后你可以在你的 Astro 组件模板中显示这篇文章的标题和描述。

参考资料 (https://docs.astro.build/en/guides/content-collections/#querying-collections)

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@shy idol kapa.ai is still learning and improving, please let us know how it did by reacting below

shy idol
#

@plucky radish 我的 collection 定义为

  schema: z.object({
    title: z.string(),
    description: z.string(),
    slug: z.string(),
    content: z.string(),
  })

当我在页面中进行渲染时,获取到的是空内容,这是为什么

const post = await getEntry('testArticle', 'test000000001')
const result = await render(post)
console.log(post, result);
plucky radishBOT
#

Let me check the knowledge base for you. One moment! :books:

#

在 Astro 中,slug 是一个保留字段,用于生成条目的 slug。因此,你不应该在你的 collection schema 中包含 slug 字段。如果你的 schema 中包含 slug 字段,你可能会遇到 ContentSchemaContainsSlugError 错误。你需要从你的 schema 中移除 slug 字段。你仍然可以在你的 frontmatter 中使用自定义的 slugs。

在你的例子中,你可以尝试将 slug 字段从你的 schema 中移除,然后再次尝试获取和渲染你的文章。

此外,content 字段通常不需要在 schema 中定义,因为 Astro 会自动处理 Markdown 或 MDX 文件的内容。你可以直接在你的 Astro 组件中使用 Content 组件来渲染你的文章内容。

例如:

---
import { getEntry, render } from 'astro:content';
const post = await getEntry('testArticle', 'test000000001');
const { Content } = await render(post);
---

<article>
  <Content />
</article>

在上述代码中,Content 组件会渲染你的文章内容。

参考资料 (https://docs.astro.build/en/reference/errors/content-schema-contains-slug-error/#_top)

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@shy idol kapa.ai is still learning and improving, please let us know how it did by reacting below

shy idol
#

@plucky radish 我的 cms 中存储的 article 数据类型为:

interface Article {
  title: string;
  description: string;
  content: string;
}

其中 content 字段是 markdown 格式的,我想使用 content-layer 将其渲染为 html 应该怎么做?

plucky radishBOT
#

Let me check the knowledge base for you. One moment! :books:

#

在 Astro 中,你可以使用 render() 函数来将 Markdown 或 MDX 内容渲染为 HTML。但是,你需要注意的是,Astro 并没有内置对远程 Markdown 的支持。要渲染远程 Markdown,你需要自己从 npm 安装并配置 Markdown 解析器。这将不会继承你在 Astro 中配置的任何内置 Markdown 和 MDX 设置。在实施之前,请确保你理解了这些限制。

对于你的情况,你可以在你的 collection loader 中使用一个 Markdown 解析器来将 content 字段的 Markdown 内容转换为 HTML。然后,你可以在你的 Astro 组件中直接使用这个 HTML 内容。

这是一个使用 marked(一个流行的 Markdown 解析器)将 Markdown 内容转换为 HTML 的例子:

import marked from 'marked';

const testArticle = defineCollection({
  loader: async () => {
    const [res] = await handleFetch<ArticleResponse>(`/api/articles?${getQuery()}`);
    if (res) {
      return [{
        ...res.data[0],
        id: 'test000000001',
        content: marked(res.data[0].content),  // Convert Markdown to HTML
      }];
    }
    return [];
  },
});

然后,你可以在你的 Astro 组件中使用 dangerouslySetInnerHTML 来显示这个 HTML 内容:

---
import { getEntry } from 'astro:content';
const post = await getEntry('testArticle', 'test000000001');
---

<article dangerouslySetInnerHTML={{ __html: post.content }}></article>

请注意,dangerouslySetInnerHTML 会直接插入 HTML 内容,因此你需要确保这个内容是安全的,不包含任何可能的 XSS 攻击。

参考资料 (https://docs.astro.build/en/guides/markdown-content/#fetching-remote-markdown)

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@shy idol kapa.ai is still learning and improving, please let us know how it did by reacting below

shy idol
#

@plucky radish 我的意思是 content-layer

plucky radishBOT
#

Let me check the knowledge base for you. One moment! :books:

#

对不起,我误解了你的问题。Astro 的 Content Layer API 目前不支持直接渲染远程 Markdown。Content Layer API 主要用于处理和加载内容,但它并不直接处理内容的渲染。

你可以在获取内容后,使用一个 Markdown 解析器(如 markedremark)将 Markdown 转换为 HTML。然后,你可以将这个 HTML 字符串作为一个属性传递给你的 Astro 组件,并在组件中使用 dangerouslySetInnerHTML 来显示这个 HTML 内容。

这是一个例子:

import marked from 'marked';

const testArticle = defineCollection({{
  loader: async () => {{
    const [res] = await handleFetch<ArticleResponse>(`/api/articles?${{getQuery()}}`);
    if (res) {{
      return [{{
        ...res.data[0],
        id: 'test000000001',
        htmlContent: marked(res.data[0].content),  // Convert Markdown to HTML
      }}];
    }}
    return [];
  }},
}});

然后,在你的 Astro 组件中:

---
import {{ getEntry }} from 'astro:content';
const post = await getEntry('testArticle', 'test000000001');
---

<article dangerouslySetInnerHTML={{ __html: post.htmlContent }}></article>

请注意,dangerouslySetInnerHTML 会直接插入 HTML 内容,因此你需要确保这个内容是安全的,不包含任何可能的 XSS 攻击。

参考资料 (https://docs.astro.build/en/guides/markdown-content/#fetching-remote-markdown)

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@shy idol kapa.ai is still learning and improving, please let us know how it did by reacting below

shy idol
#

@plucky radish 这是我的文件结构:

├── 404.astro
├── [category]
│   ├── [slug]
│   │   └── index.astro
│   ├── index.astro
│   └── page
│       └── [pagenum].astro
├── about.astro
├── author
│   └── [name].astro
├── black-friday-sale.astro
├── buhocleaner
│   ├── buy.astro
│   ├── download-mobile.astro
│   ├── download.astro
│   ├── faq.astro
│   ├── guide
│   │   ├── [slug].astro
│   │   └── index.astro
│   └── index.astro
├── buhounlocker
│   ├── buy.astro
│   ├── download-ios.astro
│   ├── download.astro
│   ├── faq.astro
│   ├── guide
│   │   └── index.astro
│   └── index.astro
├── index.astro
├── store.astro
├── support.astro
└── terms.astro

我希望使用 astro 的 localizations 功能开发一个多语言网站,下面是我的要求:

  1. 多语言有 en, zh-tw, jp 三种语言
  2. prefixDefaultLocale 为 false, 访问 / 应该渲染 /en 的内容;
  3. 尽量不使用额外的文件,我不想使用 en, zh-tw, jp 文件夹来包裹我的页面,这样的文件结构让我维护困难,我希望写一次代码可以应用于所有页面中;

我应该怎么做?

plucky radishBOT
#

Let me check the knowledge base for you. One moment! :books:

#

Astro 提供了一个强大的国际化(i18n)路由功能,可以帮助你创建多语言网站。你可以在 Astro 配置文件中设置 i18n 配置对象,包括 defaultLocalelocales 字段,以及 routing 对象来配置路由和回退行为。

首先,你需要在你的 astro.config.mjs 文件中设置你的 defaultLocalelocales

import { defineConfig } from "astro/config";
export default defineConfig({
  i18n: {
    defaultLocale: "en",
    locales: ["en", "zh-tw", "jp"],
    routing: {
      prefixDefaultLocale: false
    }
  }
});

在这个配置中,defaultLocale 设置为 "en",表示默认语言是英语。locales 包含了你支持的所有语言。prefixDefaultLocale: false 表示默认语言的 URL 不会包含语言前缀,即访问 / 将渲染 /en 的内容。

然后,你需要为每种语言创建对应的内容文件。你可以在 src/pages/ 中创建对应的 /[locale]/ 文件夹,Astro 的文件基路由将会在相应的 URL 路径下创建你的页面。你的文件夹名称必须与 locales 中的项完全匹配。只有在你设置 prefixDefaultLocale: true 时,才需要为你的 defaultLocale 创建本地化文件夹,以显示默认语言的本地化 URL 路径(例如 /en/about/)。

然而,你提到你不希望使用额外的文件,并且希望写一次代码就可以应用于所有页面。Astro 目前还不支持这种用例。你需要为每种语言创建对应的内容文件。这是因为 Astro 的 i18n 功能是基于文件系统的,它依赖于文件结构来确定路由和语言。

如果你希望避免重复的代码,你可以考虑使用部分(partials)或组件来重用一些通用的代码片段。你可以创建一个 Astro 组件,然后在你的各个页面中引用这个组件。

参考资料:


Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: