Structured text to markdown
Converting DatoCMS structured text to markdown
I'd been using DatoCMS structured text for my blogs, but I wanted to convert them back to Markdown to have better control over what is included. This facility isn't included in DatoCMS, so I decided to build it myself.
The first step for this was using the inquirer library, which allows you to provide prompts to the user to get the information required, like their API token and required model. The only complexity with this bit was having a conditional question for the folder name depending on if the user wants the files in a folder, but this has quite a nice included solution.
inquirer
.prompt([
{
type: "input",
name: "token",
message: "What's your API Token?",
},
{
type: "input",
name: "model",
message: "Which model do you want to query?",
},
{
type: "input",
name: "title",
message: "What's the name of the field you want to use as file names?",
},
{
type: "input",
name: "structuredtext",
message: "Which field contains the structured text?",
},
{
type: "confirm",
name: "folder",
message: "Do you want the files to be put in a folder?",
},
{
type: "input",
name: "folder_name",
message: "Okay! What name do you want the folder to have?",
when(answers) {
return answers.folder;
},
},
])
These answers then formed a GraphQL query which I made to DatoCMS to get all the information I needed. This required the use of both the capitalize
and pluralize
libraries, as DatoCMS converts an individual blog
into allBlogs
when making a request for the whole collection.
async function getAllBlogs(token, model, title, structuredtext) {
const data = await fetchAPI(
token,
`
{
all${capitalize(pluralize(model))} {
${title}
${structuredtext}{
value
}
}
}
`
).catch((e) => console.log(e));
return data[`all${capitalize(pluralize(model))}`];
}
Once the data is fetched, it then comes to converting the Structured Text to Markdown. The starting point for this is datocms-structured-text-to-html-string
, an official library by DatoCMS to convert the structured text to HTML. And from here I can use the unifiedjs ecosystem to convert it to Markdown. One catch here is how the Structured Text to HTML library handles code blocks, as it places the language class on the pre
tag, whereas unifiedjs expects it on the code
tag. Fortunately, the library allows custom render rules, which allow me to put the class on both pre
and code
const content = render(structuredtext, {
customRules: [
renderRule(isCode, ({ adapter: { renderNode, renderText }, key, node }) =>
renderNode(
"pre",
{ key, class: `language-${node.language}` },
renderNode(
"code",
{ key, class: `language-${node.language}` },
renderText(node.code)
)
)
),
],
});
Then it's just a case of passing this to remark and running it through rehype-remark
const markdown = await rehype()
.use(rehype2remark)
.use(stringify)
.process(content);
These markdown files can then be written to the disk using fs
in a loop, and I wrapped all this in an ora
progress indicator, so the user can see how the conversion is going as it could take some time for many files