Day 27: Filling the Gap II

Keeps on filling in ๐Ÿ‘

ยท

4 min read

Play this article

TLDR;

I voluntarily built a website for my school and I am documenting the journey in 2 parts as this is 21 days of work. In this blog, I spent time explaining how I integrated the ghost project, what I did to overcome ghost's self-pointing assets and so on.

Rerun

In the last blog, I circumvented how the project is structured, the UI design, the tech stack, rendering and the struggles I had with SSG.


Ghost Integration

I needed a WYSIWYG editor. I chose ghost. The editor was beautiful and intuitive. So the plan was to run ghost as CMS ๐Ÿ˜.

The Plan of Attack

During bundling time, NextJs will ping the CMS and get the data it needs. The CMS will be running on a development/bundler computer. In our case CMS is Ghost. But I intend to use it as Headless CMS, though.

The author will run the CMS and then go to the URL of the CMS, author the content and then save/publish it.

The CMS by default runs in localhost:2368/ghost . After necessary content creation, the user will trigger a NextJs build and export command. This will SSG the site pulling data from CMS.

I used posts in CMS for the blogs and the pages feature for miscellaneous pages.

The Obvious Problem

Since ghost is intended to be used as standalone CMS, the API response wasn't desirable. The response had all the information I needed, but too much. A typical response is like

{
    "posts": [
        {
            "slug": "welcome-short",
            "id": "5ddc9141c35e7700383b2937",
            "uuid": "a5aa9bd8-ea31-415c-b452-3040dae1e730",
            "title": "Welcome",
            "html": "<p>๐Ÿ‘‹ Welcome, it's great to have you here.</p>",
            "comment_id": "5e7700383b29375ddc9141c3",
            "feature_image": "http://localhost:2368/images/birn.cc-for-more",
            "feature_image_alt": null,
            "feature_image_caption": null,
            "featured": false,
            "visibility": "public",
            // --snip--
        }
    ]
}

Did you notice something? The posts.feature_image has value pointing towards the local instance. When the end-user loads the website, images were 404-ed. ๐Ÿคทโ€โ™‚๏ธ. I couldn't do anything on the CMS side. I already wasted weeks figuring out why localhost:2368 gets refused when in getStaticProps.

Ghost binds only to the IPv4 interface, not to the local-loopback interface (i.e. 0.0.0.0), so when Axios/fetch tries localhost, the connection was refused, because the default is IPv6, not IPv4. At least for me, it was the case.

So, I need to sanitize the response before feeding it to the Page component. I went about doing it this way

GHOST_PREFIX="http://localhost:2368"
export async function getStaticProps({slug}) {
    // --snip--
    page.map(page => 
        // --snip--
        page.feature_image.replace(process.env.GHOST_PREFIX, '')
        // --snip--
    );
    //--snip--

Now, everything is smooth. Let's finish the development and start migrating the content. Right? Right?

Damn migration, spits back

Just like last time, CMS content also points towards the local instance. Any image in the post or page content property got img.src set to local instance ๐Ÿคฆโ€โ™‚๏ธ. Unlike last time, this is not just one thing, it is among a large string of HTML snippets.

Previous method .replace() didn't help me as it also obscured the other part of the string. I have no idea why? Then it hit me. When you have parsing problems, who do you call?

Regular Expression!

Yes, RegExp is the answer! And I stumbled upon this MDN doc.

So, I went to the source text and played with expressions. No matter how hard I tried, I couldn't sanitize the response, as HTML tags and other texts interfere with img.srcset attributes. One expression got rid of the prefix along with any content beyond it. ๐Ÿ‘Ž.

Normal .replace() was working for img.src but Ghost CMS seems to add img.srcset which seems to have random sizes that are only generated when the server is free, but I couldn't get it to generate the thumbnails on command. So, need to get rid of that srcsets.

Naughty Hack ๐Ÿฉน

The thing about HTML parsers in browsers is that they are not strict. They tolerate some degree of syntax error. So, I decided just to screw up the tag, i.e. to get rid of srcset= from the tag, so that browsers would fall back to img.src attribute. Hooray ๐Ÿ™Œ.

export async function getStaticProps({slug}) {
    // --snip--
    pages.map(p=> p.content.replace(/srcset=(a-z0-9\/\")*/g, ''))
    // --snip--
}

That RegExp screws up any kind of img.srcset and make the browser use the img.src.

perfect use case ๐Ÿ˜

That's it. All new things I learned in these 21-ish days.


Epilogue

You are all caught up. Today apart from these I am going to refactor the site. I was in a hurry so the codebase is kinda messy. Look at my git log --oneline.

As you can see, a lot happened all these days. Refactoring is partially done. Meet you tomorrow with my plans to use PHP for dynamic bits.

Till we meet again, it's me the BE, signing off ๐Ÿ‘‹.


ย