jnordberg / wintersmith

A flexible static site generator
http://wintersmith.io/
MIT License
3.5k stars 335 forks source link

Browserifying article js embeds #273

Closed gamecubate closed 9 years ago

gamecubate commented 9 years ago

One of my articles needs to run some javascript code. I'd like to "require" some npm installed modules in this code. Am I correct in assuming that this is the "standard" way of doing it in Wintersmith?

  1. add a <script src="bundle.js"></script> element somewhere in the article.
  2. create main.js and store it in the article's directory. Example: var unique = require('uniq'); console.log(unique([1, 2, 2, 3, 4, 5, 5, 5, 6]));
  3. from inside the article directory (contents/articles/the-article/), do:
npm install uniq
browserify main.js -o bundle.js

I assume this will work (not tested yet) but it seems inefficient, repetitive, especially once I need to repeat this procedure for other articles.

Looking for a better way (and not even sure this is the right place to ask such questions).

Any hints appreciated. Thanks.

kennethjor commented 9 years ago

I'm not the maintainer of wintersmith, but personally I would recommend a build setup like the one you describe using browserify (or automated using something like grunt or gulp). However, rather than including it on every page, just add it to the layout for those pages. If it's a global script across your entire site, add it to the root of your directory structure.

gamecubate commented 9 years ago

Several -- i.e., not all -- pages will have their own, unique main.js file. Perhaps I can add a top-level browserify watcher that recursively checks the contents/ hierarchy for main.js files to bundle. Not sure how to do this, though. Will have to dig deeper. Thanks for the advice.

kennethjor commented 9 years ago

If your pages have their own unique JS file then I'd include it in the markdown rather than making a template for that page. Though I'd be interested in what the dev have to say about this.

Alternatively you could add a page config option which you can add at the top of your Jade template, allowing you to add arbitrary script sources. So for instance along with your title and other metadata:

---
title: Page title
script: main.js
---

and then in your template do something like (untested)

head
    if locals.script
        script(src=locals.script, type="text/javascript")
gamecubate commented 9 years ago

Interesting idea and a solid hint. Thanks for that.

Now, ideally, I should figure out a way for Wintersmith, at build time, to invoke browserify on those articles that have a script element defined in their metadata section.

kennethjor commented 9 years ago

Again, I'm not an official or anything, but I use gulp and run-wintersmith. You could probably set up gulp run the script compilation for you for each script found in the directory structure, although I'm not immediately able to tell you how to do that. I would recommend not doing that and instead have a single piece of JS for your entire site. If you absolutely need individual scripts for each page, you could have a master dependency script that your other scripts use. That would also prevent users from downloading the same dependencies more than once, since each individual script would have their dependencies baked into them.

gamecubate commented 9 years ago

I need per-page scripting as I am using D3.js to represent remote data accessed via Tabletop.js in various forms. The raison-d'être of my blog is to help visualize certain types of data (Settlers of Catan game results & stats, to be precise) for a group of friends.

Thanks to your help, I have enough to go on and to improve on my first successful-though-awkward attempts.

Still, I'm pretty sure that my goals are contrary to the concept of static page generation. The only thing that's static about them is that JS code and divs will be delivered by a server; the client will do the rest, executing said code and heavily modifying the article's DOM contents. Seems contrarian.

jnordberg commented 9 years ago

Have a look at https://github.com/jnordberg/wintersmith-browserify

With that plugin loaded all .js and .coffee files are automatically bundled

jnordberg commented 9 years ago

And then use wintersmith preview in combination with https://github.com/jnordberg/wintersmith-livereload for a nice workflow :)

jnordberg commented 9 years ago

Also i would recommend to add a plugin that sets up some static directories for heavy dependencies, d3.js tends to slow down the browserify pipeline a lot.

module.exports = (env, callback) ->
  env.registerContentPlugin 'scripts', 'scripts/static/*', env.plugins.StaticFile
  callback()
gamecubate commented 9 years ago

About the browserify plugin. You say:

With that plugin loaded all .js and .coffee files are automatically bundled

I'm not clear about this.

Say I wrote this article:

---
title: Game Stats
author: rousseau
date: 2015-01-21
template: article.jade
---

<div id="mydiv"></div>

Now say I have file "test.js" stored in the article folder:

var d3 = require('d3');
d3.select('#mydiv').append('p').text('Hello, world!');

I'm missing one piece of information. How would the browserify plugin help create the bundle and embed it in the article? Would this be via a <script src="bundle.js"></script> directive inside the Markdown or is there another way?

Also, am I correct in assuming that I have to invoke browserify manually in a shell to create the bundle, i.e., that Wintersmith will not perform this step automatically at build time?

gamecubate commented 9 years ago

To clarify further, I'd like it if I could do things such as this:

Create a new article:

---
title: Game Stats
author: rousseau
date: 2015-01-21
template: article.jade
---

<div id="mydiv"></div>

<script>
var d3 = require('d3');
d3.select('#mydiv').append('p').text('Hello world!');
</script>

and, upon saving, observing an automatic page reload with updated, browserified content.

Is this even possible with Wintersmith alone, or should I involve some third-party solution such as gulp?

jnordberg commented 9 years ago

I'm missing one piece of information. How would the browserify plugin help create the bundle and embed it in the article? Would this be via a <script src="bundle.js"></script> directive inside the Markdown or is there another way?

Yes, including it like that works. Another way would be to have a custom template for your articles and have a script: myscript.js variable in the pages metadata.

In the template you can then check if page.metadata.script exists and include it wherever you wish.

Also, am I correct in assuming that I have to invoke browserify manually in a shell to create the bundle, i.e., that Wintersmith will not perform this step automatically at build time?

You never have to bundle anything manually, wintersmith's rendering pipeline is the same whether you are building or previewing.

The only difference is the entry point, in preview it is the request your browser makes that is the entry point and the results are sent directly back to your browser. When building the entire contents directory is walked and the output is written to disk.


Regarding bundling inline scripts in the article, it would require you to extend the page plugin and hook in somewhere in the markdown parser to get all the script blocks and feed that into browserify. Pointless IMHO, but possible :smile:

gamecubate commented 9 years ago

Thanks. Very clear. About this last bit, though:

In the template you can then check if page.metadata.script exists and include it wherever you wish.

What is the syntax for this, i.e., checking for presence of, and possibly including a file?

I tried script(src=page.metadata.script) but that failed, miserably. :(

jnordberg commented 9 years ago

Have a look at the Jade documentation for that, closing this.