#Extract <p> tag from component jsx node

18 messages · Page 1 of 1 (latest)

warped stirrup
#

I want to extract the first <p> tag's content of a component on server side.

#

I want to do this, because I need a short description about blog posts, therefore I extract the first <p> tag from these posts.

hollow isle
warped stirrup
#

@hollow isle I need the first <p> tag’s content from a component. For example: <p> The following blog post is about … </p> I have to do this automatically. I want to create a snippet card for every blog post on my homepage. I store my blog posts under /routes/posts. So I’ll load all route under posts, then I need to make a short description about the posts. Thats why I need the first <p> tag.

hollow isle
warped stirrup
#

As I said It would be great, if I can do this automatically, without touch the blog page components.

hollow isle
#

Do you need the description to populate the page metadata?

warped stirrup
#

I just want to create card like this automatically, without touch the blog posts.

#

So I have to load all blog post components, then I have to “extract” description info about the post.

#

The first paragraph is enough to me to display in the cards

#

Do you understand the problem now?

hollow isle
#

I see. do you have a public repo to play with it?

warped stirrup
#

Currently I’m on phone, but I’ll make a repo! @hollow isle thanks the help, I’ll write if I’m at home.

hollow isle
#

If you create a repo we will find a smart solution

warped stirrup
#
// Find the first <p> tag, throw error if contains anything else than plaintext
function extractSnippetFromJsxNode(jsxNode: JSXNode): string | undefined {
  for (const node of jsxNode.children ?? []) {
    if (node.type == "p") {
      if (typeof node.children != "string") {
        throw Error(
          "INVALID FIRST PARAGRAPH OF BLOG POST," +
            " BLOG POST FIRST <p> should only contain plain text"
        );
      }
      return node.children;
    }
  }
}

export const usePostSummaryLoader = routeLoader$(async (requestEvent) => {
  const modules = import.meta.glob(`/src/routes/posts/**/**/index.tsx`);

  const postSummaries: BlogPostSummary[] = [];

  for (const moduleName of Object.keys(modules)) {
    const importedRoute = await modules[moduleName]();

    const head: BlogPostHead = (importedRoute as any).head;
    if (head == undefined) {
      return requestEvent.fail(500, { errorMessage: "Missing post's head" });
    }

    const relativeUrl = moduleName
      .replace("/src/routes", "")
      .replace("/index.tsx", "");

    const jsxNodeQRL: any = (importedRoute as any).default({}, null, 0);
    const component = await jsxNodeQRL.props["q:renderFn"]();

    const summary: BlogPostSummary = {
      title: head.title!,
      headerImageUrl: head.headerImageUrl,
      introductionTextHTML: extractSnippetFromJsxNode(component) ?? "",
      relativePostUrl: relativeUrl,
      releaseTimestamp: head.releaseTimestamp,
      tags: head.tags,
    };

    if(summary.introductionTextHTML == ""){
      return requestEvent.fail(500, { errorMessage: `Missing introduction text of ${summary.title}` });
    }

    postSummaries.push(summary);
  }

  return {
    errorMessage: undefined,
    postSummaries: postSummaries,
  };
});
#

Btw, this is my current solution. Because the components are lazy-loaded (QRL), we have to resolve these first.

#

It looks dirty:

const jsxNodeQRL: any = (importedRoute as any).default({}, null, 0);
const component = await jsxNodeQRL.props["q:renderFn"]();