saibotsivad / blockdown

A Markdown-like syntax that supports defining blocks of text.
https://blockdown.md/
Other
5 stars 0 forks source link

Optional options for convenient metadata parsing #3

Open Renddslow opened 4 years ago

Renddslow commented 4 years ago

Per the readme:

Note: The metadata is enclosed in square brackets, but the exact content of the metadata is not an opinion enforced by Blockdown. You can use simple ini style [key1=value1,key2=value], or use plain JSON [{"foo":"bar"}] or use JSON5 [{foo:'bar'}]. Blockdown syntax does not care–it leaves metadata interpretation up to you.

Wondering if you would be open to offering an options object that could set one of these two and parse it for you, just as a convenience:

parse(markdownString, { metadataFormat: 'ini' })

That way if I passed in ---!button[class="btn btn--primary",href=/cart] I could get:

{
    name: 'button',
    metadata: {
        class: 'btn btn--primary',
        href: '/cart'
    },
    ...
}
saibotsivad commented 4 years ago

(These are notes I collated from a private conversation. I'm copying them here for public discussion/record.)


My personal motivation is this:

After working with a friend to rescue 20+ years of documents written in a half dozen different proprietary (and a few non-proprietary) document editors, I came to the conclusion that plain text files are where it's at.

I have a big folder filled with markdown files, organized into folders and sub-folders as a collection of "notebooks". All these notes are written in Markdown, with Front Matter metadata at the top.

Markdown is amazing, of course, but I need a programmatic way to add "blocks" of data in the middle of the file, as discussed in the README.md, and thus was born Blockdown.

I wanted to write Blockdown as specifications only, with very clear specs, so that it could be implemented in any other language.

I wanted the plain text files that you write in Blockdown to be "human readable" enough that you could go read them 50 years from now--I'm willing to bet a significant amount of money that plaintext files will exist in the far future.

But it's not very useful to only have specs, you need some sort of implementation to go along with it. That's what this module is for.

Because of that, I'm very opposed to baking in a specific metadata parser (e.g. ini style or JSON parsing) to the implementation. I wanted the implementation to be as un-opinionated as possible

If one desires a specific metadata parser in their project, they could write it like so:

import { readFileSync } from 'fs'
import { parse } from '@saibotsivad/blockdown'
import parseMetadata from '...your specific metadata parser...'

const blockdown = parse(readFileSync('path/to/file.md'))
blockdown.blocks.forEach(block => {
    block.metadata = block.metadata && parseMetadata(block.metadata)
})

However...

As I've started to make tools that use this Blockdown implementation, I have desired a way to more easily set a metadata parser, and I think something like this would be very reasonable:

import { readFileSync } from 'fs'
import { parse } from '@saibotsivad/blockdown'
import parseMetadata from '...your specific metadata parser...'

const blockdown = parse(readFileSync('path/to/file.md'), {
    metadata: string => {
        return parseMetadata(string)
    }
})

Some sort of constructor approach could also be handy, e.g.:

import { readFileSync } from 'fs'
import { parse } from '@saibotsivad/blockdown'
import parseMetadata from '...your specific metadata parser...'

const parseString = parse({
    metadataParser: string => {
        return parseMetadata(string)
    }
})

const blockdown1 = parseString(readFileSync('path/to/file1.md'))
const blockdown2 = parseString(readFileSync('path/to/file2.md'))

I suppose that we could have the best of both worlds:

export const parse = (stringOrOptions, optionsOrNothing) => {
    if (typeof stringOrOptions === 'string') {
        // parse, possibly using optionsOrNothing as options
        return blockdown
    } else {
        return (string) => {
            // parse, using stringOrOptions as options
            return blockdown
        }
    }
}

I don't have strong opinions on the right way to do it.

At the moment I'm wrapping up a few projects, at which point I'll turn my dedicated attention to Blockdown Binder, which will use this library as the foundation. When I get around to that, I'll very likely add the discussed functionality to this library, in one form or another.

I would accept a well-tested pull request that implemented one of the above solutions.