Ajedi32 / metalsmith-metafiles

A Metalsmith plugin to read file metadata from separate files (as an alternative to frontmatter)
MIT License
6 stars 2 forks source link

Ability to name-space from multiple files? #17

Open dillonbheadley opened 8 years ago

dillonbheadley commented 8 years ago

Hi there! Is it possible to have multiple metafiles pointing to a single file? For example: index.html is the "host file". index.html.meta.json is generic metadata. index.html.books.meta.jsonwould be found under the books key for the index.html file. Does that make sense?

Ajedi32 commented 8 years ago

Right now there's no functionality for namespacing metadata under a single key like that. I guess I could build in a function similar to the namespace option in metalsmith-matters. Then you'd be able to accomplish that by using multiple copies of this plugin in succession, like so:

{
  "frontmatter": false,
  "plugins": [
    {"metalsmith-metafiles": {}},
    {"metalsmith-metafiles": {
      "postfix": ".books.meta",
      "namespace": "books"
    }},
    // Other plugins...
  ]
}
dillonbheadley commented 8 years ago

Hmmm interesting... I don't like how you would have to manually declare each namespace though. I had thrown together a simple plugin to do something similar with a markdown file:

var metaMarkdown = function(files, metalsmith, done) {
    // works like: target.metaname.meta.md
    Object.keys(files).forEach(function(file){
        var basename = path.basename(file);
        var dirname = path.dirname(file);
        if (basename.indexOf('.meta.') > -1) {
            var metaFile = files[file];
            var targetFile = basename.split('.meta.')[0];
            var metaName = targetFile.split('.')[1] || '';
            targetFile = targetFile.split('.')[0];

            dirname == "." ? dirname = "" : '';
            var hostFile = files[dirname+targetFile+".jade"];

            if (hostFile) {
                hostFile.page = hostFile.page || {};
                for (set in metaFile) {
                    if (metaName) {
                        hostFile.page[metaName] = hostFile.page[metaName] || {};
                        hostFile.page[metaName][set] = metaFile[set]
                    } else {
                        hostFile.page[set] = metaFile[set]
                    }
                }
                delete files[file];
                Object.assign(files[dirname+targetFile+".jade"], hostFile)
            }
        }
    });

    done();
};

this looks for a matching base name and adds the parsed file as a key. So in index.html, index.meta.md:

---
title: This is a test
---

# Some content

would be available as page.title and page.content. And index.hero.meta.md:

---
title: I'm the hero
---
## Hero content

Is available as page.hero.title and page.hero.content.

Kind of makes a Markdown CMS of sorts...

I've set it up just to work with .jade files via markdown-in-place. Could be fleshed out a bit. Thoughts?

Ajedi32 commented 8 years ago

Actually now that I think about it, you could do this with a custom parser:

var metafiles = require('metalsmith-metafiles');
var yaml = require('js-yaml');

Metalsmith(__dirname)
  .frontmatter(false)
  .use(metafiles({
    parsers: {
      ".json": false, // Disable using JSON metadata files
      ".yaml": function(content, options) { // Custom parser
        let metadata = yaml.safeLoad(content, {filename: options.path});

        // Find namespace based on `options.path`

        return {[namespace]: metadata}
      },
    }
  }))
  .use(/* Other plugin */)
  .build(function(err) {
    if (err) throw err;
  });
dillonbheadley commented 8 years ago

That's great! Could I also use Markdown files with a custom parser like that?

Ajedi32 commented 8 years ago

In theory, yes. Note though that metalsmith-metafiles parsers can't access metadata added to the meta files, only their contents, so if you want to include attributes from frontmatter you'd have to run metalsmith-metafiles before parsing the frontmatter (e.g. disable the built-in frontmatter parsing) and have your custom parser parse the frontmatter itself.

dillonbheadley commented 8 years ago

Ah I see. Ok good to know. I've started testing this out and I'm getting this error: Could not find main file index.jade.hero for metadata file index.jade.hero.meta.md

Seems like it won't take a file with another name segment in it.

Ajedi32 commented 8 years ago

Oh, darn. Yeah you're right; I should have realized that would happen. Not sure why I didn't. The names of the metadata files are fixed...

To be honest, your use case seems to be a bit outside the scope of what metalsmith-metafiles was originally intended to do. I can't really see myself adding functionality to support this natively, but I am open to adding options to expand the flexibility of metalsmith-metafiles to make it possible for you to use metalsmith-metafiles to do this yourself.

My current thought is to add support for some kind of custom matcher object for metadata files, so you could then implement this functionality yourself. Basically I'd be letting you specify your own MetafileMatcher class (with some changes to simplify the API). Does that sound reasonable to you?