JSRocksHQ / harmonic

The next static site generator
http://harmonicjs.com/
MIT License
282 stars 26 forks source link

Proposal for plugin architecture #140

Open soapdog opened 9 years ago

soapdog commented 9 years ago

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?

UltCombo commented 9 years ago

Awesome. :+1: 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).

soapdog commented 9 years ago

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...

UltCombo commented 9 years ago

Array sounds good to me. =]

soapdog commented 9 years ago

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.

UltCombo commented 9 years ago

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:

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.

jaydson commented 9 years ago

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?

soapdog commented 9 years ago

@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.

UltCombo commented 9 years ago

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.

soapdog commented 9 years ago

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.

UltCombo commented 9 years ago

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.

jaydson commented 9 years ago

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

jaydson commented 9 years ago

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

soapdog commented 9 years ago

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

jaydson commented 9 years ago

:+1:

viniciusdacal commented 9 years ago

some news about plugin support?

jaydson commented 9 years ago

Nope. Harmonic still lacks on plugin support :cry:

UltCombo commented 9 years ago

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

soapdog commented 9 years ago

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