JSRocksHQ / harmonic

The next static site generator

Home Page:http://harmonicjs.com/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Proposal for plugin architecture

soapdog opened this issue · comments

Hi Friends,

I have an idea on how to implement plugins for Harmonic that is very lightweight and requires minimal coding. If this idea is accepted then we'd be able to extend harmonic without touch harmonic itself.

All harmonic plugins would be normal node modules that would be installed with npm. These modules would follow a naming convention such as harmonic-plugin-*. The configuration for each plugin would reside in harmonic.json like:

{
    "name": "AndreGarzia.com",
    "title": "All We Do Is Code",
    "domain": "http://awesome.com",
    "bio": "I work for the web",
    "plugins": {
      "deploy-gihub": {
        "repo": "soapdog/site",
        "branch": "gh-pages"
      }
    }
}

In the case above there would be a node module named harmonic-plugin-deploy-github installed on the site. Harmonic would read harmonic.json, find the plugin key and load all necessary modules on start. We'd use a publisher/subscriber like pattern where upon loading each plugin could subscribe to a given message. As harmonic workflow cycled it would dispatch the messages like preBuild, prePageGeneration, postPageGeneration, prePostGeneration, postPostGeneration, preCopyResources, postCopyResources, postBuild and so on.

Modules could be built with ES6 or any other technology needed as long as they register their interest in some messages upon loading.

What do you folks think?

Awesome. 👍
We have to consider some details, such as whether the loading order of plugins is important (in that case, an array of plugins would make more sense, or maybe a "priority" config option for each plugin).

I think that array instead of object is the simplest thing. If we use a priority index then we'll soon write a process scheduler and I am really poor at such tasks...

Array sounds good to me. =]

What about:

"plugins": {
      "order": ["markdown", "deploy-github", "deploy-s3"],
      "config": {
        "deploy-gihub": {
          "repo": "soapdog/site",
          "branch": "gh-pages"
        },
        "markdown": {},
        "deploy-s3": {
          "bucket": "blablabla"
        }
      }
    }

In this case ``plugins` has two keys – config and order – which makes it easier to specify and fiddle with the order without the need of moving large quantities of object data chunks.

Seems good. What should we do if the config and order mismatch (e.g. order has a plugin which is not in the config, or the other way around)?

Let's assume some use cases:

  • Some plugins may be able to run with default configs (without the user specifying a config object).
  • The user may want to temporarily disable a plugin -- it would be a hassle if the user had to delete the entire config object for that.

So, I suggest renaming order and to something else (e.g. use, run, activate) and execute only the plugins that are listed in the array. This way, to disable a plugin you just have to remove/comment out its name from the array, and this also allows plugins to run with their default config (without requiring the user to pass a config object). So something like:

"plugins": {
  // temporary name
  "use": ["markdown", "deploy-github"/*, "deploy-s3"*/],
  "config": {
    "deploy-gihub": {
      "repo": "soapdog/site",
      "branch": "gh-pages"
    },
    "deploy-s3": {
      "bucket": "blablabla"
    }
  }
}

Next, we should look into error handling. I guess we can consider how to handle errors in plugins after we have an initial proof of concept of the plugins architecture.

Just wondering, i don't think let the config order in this layer is a good approach.
Perhaps we need programmatic way to deal with.
I mean, something like a Gruntfile.
Harmonicfile?

So we can do something like this (runtime plugin creation):

module.exports = (harmonic) => {
   harmonic.use(() => console.log('my tiny plugin'))
           .use('deploy-github', () => {
              // implementation
           });
}

Already defined plugins:

module.exports = (harmonic) => {
   harmonic.use(deploys3);
   harmonic.use(rss);
   ...
}

Too complicated?

@jaydson that would tie Harmonic usage to developers who know JS. We might want to opt for a simple config file and then it would be easier for a non-programmer to use.

I agree with @soapdog.
@jaydson I believe your suggestion would be nice as a skeleton to develop the plugins themselves and to integrate them with Harmonic (that is, can be used for both plugin development and Harmonic API consumption). We just need to somehow pass the config into the plugin when they're loaded via the plugins option.

Also this is an issue of declarative vs programmatic ways to approach plugin configuration. Declarative approach is easier to test and check for errors. The programmatic way is easier to shoot yourself in the foot.

My recommendation is to build a declarative way where plugins are configured using harmonic.json and once this is done and working then create a new programmatic way of controlling Harmonic that could be offered to power users trying to affect how the program works. This second offering could come as an API that a given user would import into his JS/ES6 code and thus drive Harmonic on his own.

My recommendation is to build a declarative way where plugins are configured using harmonic.json and once this is done and working then create a new programmatic way of controlling Harmonic that could be offered to power users trying to affect how the program works. This second offering could come as an API that a given user would import into his JS/ES6 code and thus drive Harmonic on his own.

Sounds good to me.

Ok, let's do it in a declarative way then.

@soapdog any progress on the plugin stuff?
If so, i want to help build it.

I was waiting for us to agree. I will build a tiny POC and share as a branch here.

some news about plugin support?

Nope. Harmonic still lacks on plugin support 😢

@soapdog By the way, did you manage to start a PoC?

I did but I think I could use some help. Will post it to a branch and get back here.