From 79f74100a86246db9bf1cdf6a7bcd29033f956e8 Mon Sep 17 00:00:00 2001 From: Robert Montesano Date: Sat, 1 Oct 2016 13:29:30 -0400 Subject: [PATCH 1/3] added example blog --- examples/blog/Makefile | 8 + examples/blog/Readme.md | 289 ++++++++++++++++++++++ examples/blog/index.js | 42 ++++ examples/blog/layouts/layout.html | 19 ++ examples/blog/layouts/partials/footer.hbs | 8 + examples/blog/layouts/partials/header.hbs | 13 + examples/blog/layouts/post.html | 7 + examples/blog/package.json | 14 ++ examples/blog/src/index.md | 5 + examples/blog/src/posts/fifth-post.md | 10 + examples/blog/src/posts/first-post.md | 10 + examples/blog/src/posts/fourth-post.md | 10 + examples/blog/src/posts/second-post.md | 11 + examples/blog/src/posts/third-post.md | 11 + 14 files changed, 457 insertions(+) create mode 100644 examples/blog/Makefile create mode 100644 examples/blog/Readme.md create mode 100644 examples/blog/index.js create mode 100644 examples/blog/layouts/layout.html create mode 100644 examples/blog/layouts/partials/footer.hbs create mode 100644 examples/blog/layouts/partials/header.hbs create mode 100644 examples/blog/layouts/post.html create mode 100644 examples/blog/package.json create mode 100644 examples/blog/src/index.md create mode 100644 examples/blog/src/posts/fifth-post.md create mode 100644 examples/blog/src/posts/first-post.md create mode 100644 examples/blog/src/posts/fourth-post.md create mode 100644 examples/blog/src/posts/second-post.md create mode 100644 examples/blog/src/posts/third-post.md diff --git a/examples/blog/Makefile b/examples/blog/Makefile new file mode 100644 index 00000000..8d025523 --- /dev/null +++ b/examples/blog/Makefile @@ -0,0 +1,8 @@ + +build: node_modules + node index.js + +node_modules: package.json + npm install + +.PHONY: build diff --git a/examples/blog/Readme.md b/examples/blog/Readme.md new file mode 100644 index 00000000..8687bb10 --- /dev/null +++ b/examples/blog/Readme.md @@ -0,0 +1,289 @@ + +# BLOG + +This example uses Metalsmith to make a simple blog. To test it out yourself just run: + + $ make build + +If that spits out an error saying you there is no such make command. Then try this: + + $ npm install + +followed by: + + $ node index.js + +If your new to node, npm, and metalsmith in general then you are probably a little confused. Lets take a closer look at what we just did. Open up your Makefile located in this examples root directory. It should look like this: + + build: node_modules + node index.js + + node_modules: package.json + npm install + + .PHONY: build + +When you run `$ make build` it is telling the machine to run the build command located in the directories makefile. The build command is located under the `.PHONY` field which acts as an alias to run all the commands found in the make file. When you run `$ make build` it's telling the machine to run all the command found in the make file, which in our case will run `$ npm install` and `$ node index.js`. If you encounter an error and the make command is not recognized on your machine then you probably need to install it someway. I was working on a Windows machine and found it much easier to just stop using the `$ make build` command. Now let's see what these other commands do. + +The `$ npm install` command will take a look at your package.json file and install any dependencies that are listed here. This gives you all the plugins you need to build your blog. Let's open it up and take a look at the plugins we will be using for this site, the package.json file should look like this: + + { + "name": "blog-example", + "private": true, + "dependencies": { + "handlebars": "^4.0.5", + "metalsmith": "^2.1.0", + "metalsmith-layouts": "^1.4.1", + "metalsmith-markdown": "^0.2.1", + "metalsmith-permalinks": "^0.5.0", + "metalsmith-drafts": "^0.0.1", + "metalsmith-collections": "^0.7.0", + "metalsmith-discover-partials": "^0.1.0" + } + } + +Line-by-line here are the plugins we are using and why: + +1. handlebars - Installs handlebars as our templating engine for the build. If you are not familiar with handlebars, check out the docs [here](http://handlebarsjs.com/). +2. metalsmith - Our favorite static site generator. +3. metalsmith-layouts - Interperets out templating engine. +4. metalsmith-markdown - Lets us use markdown to write our posts. +5. metalsmith-permalinks - Gives us pretty urls for seo and usability purposes. Changes the first-post.html to first-post/index.html during the compiling phase. +6. metalsmith-drafts - Lets us mark posts as drafts, which will not be compiled into the production folder +7. metalsmith-collections - Gives us the ability to group posts and iterate through them if we want to list all of our posts specific to a certain category. +8. metalsmith-discover-partials - Lets us use handlebars partials in our project. + +Run `$ npm install` and a folder named "node_modules" will be created in the projects root directory containing all your plugins. + +Now lets take a look at our `index.js` file. It should look something like this: + + var Metalsmith = require('metalsmith'); + var markdown = require('metalsmith-markdown'); + var layouts = require('metalsmith-layouts'); + var permalinks = require('metalsmith-permalinks'); + var drafts = require('metalsmith-drafts'); + var collections = require('metalsmith-collections'); + var discoverPartials = require('metalsmith-discover-partials'); + + Metalsmith(__dirname) + .metadata({ + title: "My Blog", + description: "It's about saying »Hello« to the World.", + generator: "Metalsmith", + url: "http://www.metalsmith.io/", + twitter: "https://twitter.com/", + facebook: "https://www.facebook.com/", + github: "https://github.com/" + }) + .source('./src') + .destination('./build') + .clean(false) + .use(drafts()) + .use(discoverPartials({ + directory: 'layouts/partials', + pattern: /\.hbs$/ + })) + .use(collections({ + posts: { + pattern: 'posts/*.md', + sortBy: 'date', + reverse: true + } + })) + .use(markdown()) + .use(permalinks()) + .use(layouts({ + engine: 'handlebars' + })) + .build(function(err, files) { + if (err) { throw err; } + }); + +Line-by-line lets take a look at whats happening here: + + var Metalsmith = require('metalsmith'); + var markdown = require('metalsmith-markdown'); + var layouts = require('metalsmith-layouts'); + var permalinks = require('metalsmith-permalinks'); + var drafts = require('metalsmith-drafts'); + var collections = require('metalsmith-collections'); + var discoverPartials = require('metalsmith-discover-partials'); + +At the top of the file we instantiate our variables and link them with their corresponding plugins. We will use these variables in our metalsmith function below. + +Next you will see the Metalsmith function being called. Here we will chain the plugins together which will tell Metalsmith which functions to run during our compiling phase. +Order does matter here and sometime when adding a plugin you will notice that it doesn't have the expected result. If your not seeing the result, but Metalsmith is not notifying you of any error then you might just have to change the order the plugin was enter in. I usually try adding it right after the `.clean()` function. + + .metadata({ + title: "My Blog", + description: "It's about saying »Hello« to the World.", + generator: "Metalsmith", + url: "http://www.metalsmith.io/", + twitter: "https://twitter.com/", + facebook: "https://www.facebook.com/", + github: "https://github.com/" + }) + +The metadata function states general data about your project and is a good place to enter global variables that will be used in your project. Here I have included twitter, facebook, and github links in order to use them in the footer of our pages. + + .source('./src') + .destination('./build') + +These two functions I'll describe together. Pretty self explanitory. The `./src` directory is where you will edit your files pre-compiling and the `./build` directory is where your files will end up post-compiling. + + .clean(false) + +If the clean function is set to true it will delete all the files within the `./build` directory before each compiling phase making sure it perfectly mirrors the `./src` folder, while setting the function to false will not delete files in the `./build` directory before each compiling phase. + + .use(drafts()) + +This just simply runs the draft function and enables the drafts plugin and functionality on the project. + + .use(discoverPartials({ + directory: 'layouts/partials', + pattern: /\.hbs$/ + })) + +This will discovery any partials in the `./layouts/partials` folder with the `.hbs` file extension. We will be creating header and footer partials for our blog. + + .use(collections({ + posts: { + pattern: 'posts/*.md', + sortBy: 'date', + reverse: true + } + })) + +This block groups all our posts from the `./build/posts` folder with the `.md` extension. We can now iterate through this object in our template files if we want to list all our posts or posts of a certain category. You can checkout this plugins docs [here](https://github.com/segmentio/metalsmith-collections). + + .use(markdown()) + +Simply enables the markdown syntax for editing pre-compilation. + + .use(permalinks()) + +Makes our urls pretty post compilation, as described before when we were going over our package.json file. + + .use(layouts({ + engine: 'handlebars' + })) + +Lets us use handlebars as the templating engine. + + .build(function(err, files) { + if (err) { throw err; } + }); + +This will finally build the project using the plugins and options we've provided and spit out any errors if they occur. You can also add code to this block if maybe you want it to notify you that the build was successful. + +Now before we run `$ node index.json` lets take a look at our `./src` and `./layouts` folders. In the `./src/posts` folder open up the file `first-post.md`. It should look like this. + + --- + title: My First Post + date: 09-30-2016 + layout: post.html + collection: testing + author: John Doe + draft: false + --- + + Bacon ipsum dolor amet chicken pork meatball cow swine. Meatloaf chicken meatball tail bacon andouille sausage prosciutto rump. Short loin biltong tri-tip, swine venison beef corned beef shank pork chop bacon. Turducken alcatra sirloin short ribs cow short loin. Short loin beef spare ribs meatloaf tri-tip bresaola. Beef ribs bacon meatball beef. + + +Line-by-line here is what its doing. + +1. All the data in the comment block is considered variables to be used in your template files before compilation. You can enter whatever variables you want for each individual post. Remember that some variables work with your plugins and need to be added to each post file. +2. Title, date, and author are self explanitory and general data for this post. +3. layout signifies which template to use for this post. +4. collections works with our collections plugin to group posts in a specific category. +5. drafts works with our drafts plugin to signify if this post is a draft or not and if it should be left out of the compilation phase. +6. Anything under the comment block is part of the contents field. + +Now open up your `./layouts/post file`, it should look something like this: + + {{> header }} + +

{{ title }}

+ + {{{ contents }}} + + {{> footer }} + +At a glance you should see how these variables correlate in your templates folder. Take notice that the content of the post will be displayed in the `{{{ contents }}}` line. Another thing you should notice is the `{{> header }}` and `{{> footer }}` lines. If at this point your confused, again take a look at the handlebars docs [here](http://handlebarsjs.com/). This is handlebars way of including partials in the file. Open up your `./layouts/partials/footer.hbs` file. The file should look like this: + + + + + + +Notice that the twitter, facebook, and github variables are not taken from the `first-post.md` file, but are included in the metadata function of the `index.js` file. This is important because that data can be used globally throughout your metalsmith project. If your having trouble remembering that line of code, here it is below: + + .metadata({ + title: "My Blog", + description: "It's about saying »Hello« to the World.", + generator: "Metalsmith", + url: "http://www.metalsmith.io/", + twitter: "https://twitter.com/", + facebook: "https://www.facebook.com/", + github: "https://github.com/" + }) + +Now lets take a look at our template for our landing page at `./layouts/layout.html`: + + {{> header }} + +

{{ title }}

+

{{ description }}

+ + {{{ contents }}} + + +
+ + {{#each collections.posts }} + +

{{this.title}}

+
+ {{/each}} + +
+ + {{> footer }} + +This file should look pretty familiar to you with the exception this: + + {{#each collections.posts }} + +

{{this.title}}

+
+ {{/each}} + +This is taking advantage of our collections plugin. In our `index.js` file we created a collection called posts that grabs all the `.md` files in our `./src/posts/` folder and creates and obj out of them. Here we are iterating through that object in order to list our recent posts. The collections plugin also give you a way to link to this file with the `{{baseUrl}}` and `{{this.path}}` variables. Here is the block of code from our `index.js` file for your reference: + + .use(collections({ + posts: { + pattern: 'posts/*.md', + sortBy: 'date', + reverse: true + } + })) + +Now we should have a pretty good understanding of the content of our `./build` folder and how it communicates with out `./layouts` folder and how all of that works with out plugins and `index.js` file. So with that lets run our `index.js` file with: + +`$ node index.js` + +This should take a second and if there were no errors found ,it should show you a new line. Now go take a look at your `./build` folder. You should see that it is now populated with an index.html as well as a posts folder with the corresponding posts. Open the `index.html` file up with your browser and start exploring. + + + + + + + + + + diff --git a/examples/blog/index.js b/examples/blog/index.js new file mode 100644 index 00000000..4b550117 --- /dev/null +++ b/examples/blog/index.js @@ -0,0 +1,42 @@ +var Metalsmith = require('metalsmith'); +var markdown = require('metalsmith-markdown'); +var layouts = require('metalsmith-layouts'); +var permalinks = require('metalsmith-permalinks'); +var drafts = require('metalsmith-drafts'); +var collections = require('metalsmith-collections'); +var discoverPartials = require('metalsmith-discover-partials'); + + +Metalsmith(__dirname) + .metadata({ + title: "My Blog", + description: "It's about saying »Hello« to the World.", + generator: "Metalsmith", + url: "http://www.metalsmith.io/", + twitter: "https://twitter.com/", + facebook: "https://www.facebook.com/", + github: "https://github.com/" + }) + .source('./src') + .destination('./build') + .clean(false) + .use(drafts()) + .use(discoverPartials({ + directory: 'layouts/partials', + pattern: /\.hbs$/ + })) + .use(collections({ + posts: { + pattern: 'posts/*.md', + sortBy: 'date', + reverse: true + } + })) + .use(markdown()) + .use(permalinks()) + .use(layouts({ + engine: 'handlebars' + })) + .build(function(err, files) { + if (err) { throw err; } + }); diff --git a/examples/blog/layouts/layout.html b/examples/blog/layouts/layout.html new file mode 100644 index 00000000..97ae04df --- /dev/null +++ b/examples/blog/layouts/layout.html @@ -0,0 +1,19 @@ +{{> header }} + +

{{ title }}

+

{{ description }}

+ + {{{ contents }}} + + +
+ + {{#each collections.posts }} + +

{{this.title}}

+
+ {{/each}} + +
+ +{{> footer }} diff --git a/examples/blog/layouts/partials/footer.hbs b/examples/blog/layouts/partials/footer.hbs new file mode 100644 index 00000000..603ec06a --- /dev/null +++ b/examples/blog/layouts/partials/footer.hbs @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/examples/blog/layouts/partials/header.hbs b/examples/blog/layouts/partials/header.hbs new file mode 100644 index 00000000..a1859e01 --- /dev/null +++ b/examples/blog/layouts/partials/header.hbs @@ -0,0 +1,13 @@ + + + + + {{ title }} + + + +
+

+ Home +

+
\ No newline at end of file diff --git a/examples/blog/layouts/post.html b/examples/blog/layouts/post.html new file mode 100644 index 00000000..2e50e34c --- /dev/null +++ b/examples/blog/layouts/post.html @@ -0,0 +1,7 @@ +{{> header }} + +

{{ title }}

+ + {{{ contents }}} + +{{> footer }} diff --git a/examples/blog/package.json b/examples/blog/package.json new file mode 100644 index 00000000..39b6f982 --- /dev/null +++ b/examples/blog/package.json @@ -0,0 +1,14 @@ +{ + "name": "blog-example", + "private": true, + "dependencies": { + "handlebars": "^4.0.5", + "metalsmith": "^2.1.0", + "metalsmith-layouts": "^1.4.1", + "metalsmith-markdown": "^0.2.1", + "metalsmith-permalinks": "^0.5.0", + "metalsmith-drafts": "^0.0.1", + "metalsmith-collections": "^0.7.0", + "metalsmith-discover-partials": "^0.1.0" + } +} diff --git a/examples/blog/src/index.md b/examples/blog/src/index.md new file mode 100644 index 00000000..be26314f --- /dev/null +++ b/examples/blog/src/index.md @@ -0,0 +1,5 @@ +--- +layout: layout.html +--- + +

Read what I have to say

diff --git a/examples/blog/src/posts/fifth-post.md b/examples/blog/src/posts/fifth-post.md new file mode 100644 index 00000000..fe4f507e --- /dev/null +++ b/examples/blog/src/posts/fifth-post.md @@ -0,0 +1,10 @@ +--- +title: My Fifth Post +date: 10-04-2016 +layout: post.html +collection: vows +author: John Doe +draft: true +--- + +Pig pork short loin turducken tail spare ribs pork belly ham shank burgdoggen corned beef tongue frankfurter ribeye. T-bone pork belly turducken short loin filet mignon chuck tail cupim pastrami hamburger spare ribs. Tenderloin meatball tail t-bone landjaeger, brisket fatback tongue salami kielbasa. Boudin biltong andouille tongue picanha salami beef ribs shankle t-bone. T-bone prosciutto pork belly tail tongue tenderloin doner. diff --git a/examples/blog/src/posts/first-post.md b/examples/blog/src/posts/first-post.md new file mode 100644 index 00000000..51c3e7a6 --- /dev/null +++ b/examples/blog/src/posts/first-post.md @@ -0,0 +1,10 @@ +--- +title: My First Post +date: 09-30-2016 +layout: post.html +collection: vows +author: John Doe +draft: false +--- + +Bacon ipsum dolor amet chicken pork meatball cow swine. Meatloaf chicken meatball tail bacon andouille sausage prosciutto rump. Short loin biltong tri-tip, swine venison beef corned beef shank pork chop bacon. Turducken alcatra sirloin short ribs cow short loin. Short loin beef spare ribs meatloaf tri-tip bresaola. Beef ribs bacon meatball beef. diff --git a/examples/blog/src/posts/fourth-post.md b/examples/blog/src/posts/fourth-post.md new file mode 100644 index 00000000..02da48ce --- /dev/null +++ b/examples/blog/src/posts/fourth-post.md @@ -0,0 +1,10 @@ +--- +title: My Fourth Post +date: 10-03-2016 +layout: post.html +collection: vows +author: John Doe +draft: false +--- + +Pig pork short loin turducken tail spare ribs pork belly ham shank burgdoggen corned beef tongue frankfurter ribeye. T-bone pork belly turducken short loin filet mignon chuck tail cupim pastrami hamburger spare ribs. Tenderloin meatball tail t-bone landjaeger, brisket fatback tongue salami kielbasa. Boudin biltong andouille tongue picanha salami beef ribs shankle t-bone. T-bone prosciutto pork belly tail tongue tenderloin doner. diff --git a/examples/blog/src/posts/second-post.md b/examples/blog/src/posts/second-post.md new file mode 100644 index 00000000..94517d88 --- /dev/null +++ b/examples/blog/src/posts/second-post.md @@ -0,0 +1,11 @@ +--- +title: My Second Post +date: 10-01-2016 +layout: post.html +collection: vows +author: John Doe +draft: false +--- + +Tongue doner meatball, tail pancetta jerky frankfurter ground round jowl bacon. Pork chop pork loin corned beef, ham hock t-bone pig chicken burgdoggen. Andouille tri-tip turkey, beef ribs chuck landjaeger porchetta. Leberkas short ribs ham hock, burgdoggen doner venison t-bone ground round. Swine venison jowl biltong prosciutto. Pork belly fatback prosciutto short loin, bacon boudin biltong ground round cow swine chicken. Turducken shoulder swine rump chuck tenderloin andouille cow, picanha jowl sausage ribeye kevin. + diff --git a/examples/blog/src/posts/third-post.md b/examples/blog/src/posts/third-post.md new file mode 100644 index 00000000..27e01079 --- /dev/null +++ b/examples/blog/src/posts/third-post.md @@ -0,0 +1,11 @@ +--- +title: My Third Post +date: 10-02-2016 +layout: post.html +collection: vows +author: John Doe +draft: false +--- + +Hamburger kevin pastrami shankle bacon chicken burgdoggen short loin shoulder tri-tip cupim venison. Filet mignon corned beef ham ground round, meatball porchetta strip steak jerky pancetta t-bone meatloaf. Capicola fatback shoulder shank doner. Shank tongue pork loin kevin ball tip andouille kielbasa ham hock fatback beef ribs prosciutto turducken sirloin alcatra sausage. + From d126e7f56f9b2fd40fbcdf47f7fd2b7efa7ba9a0 Mon Sep 17 00:00:00 2001 From: Robert Montesano Date: Mon, 10 Oct 2016 13:10:34 -0400 Subject: [PATCH 2/3] added ebook example --- examples/blog/Makefile | 8 - examples/blog/Readme.md | 289 ------------- examples/blog/index.js | 42 -- examples/blog/layouts/layout.html | 19 - examples/blog/layouts/partials/footer.hbs | 8 - examples/blog/layouts/post.html | 7 - examples/blog/package.json | 14 - examples/blog/src/index.md | 5 - examples/blog/src/posts/fifth-post.md | 10 - examples/blog/src/posts/first-post.md | 10 - examples/blog/src/posts/fourth-post.md | 10 - examples/blog/src/posts/second-post.md | 11 - examples/blog/src/posts/third-post.md | 11 - examples/ebook/Readme.md | 409 ++++++++++++++++++ .../custom-modules/metalsmith-ebook/index.js | 192 ++++++++ .../metalsmith-ebook/package.json | 10 + examples/ebook/index.js | 43 ++ .../header.hbs => ebook/layouts/layout.html} | 17 +- examples/ebook/package.json | 13 + examples/ebook/src/chapters/chapter001.md | 10 + examples/ebook/src/chapters/chapter002.md | 11 + examples/ebook/src/index.md | 3 + 22 files changed, 703 insertions(+), 449 deletions(-) delete mode 100644 examples/blog/Makefile delete mode 100644 examples/blog/Readme.md delete mode 100644 examples/blog/index.js delete mode 100644 examples/blog/layouts/layout.html delete mode 100644 examples/blog/layouts/partials/footer.hbs delete mode 100644 examples/blog/layouts/post.html delete mode 100644 examples/blog/package.json delete mode 100644 examples/blog/src/index.md delete mode 100644 examples/blog/src/posts/fifth-post.md delete mode 100644 examples/blog/src/posts/first-post.md delete mode 100644 examples/blog/src/posts/fourth-post.md delete mode 100644 examples/blog/src/posts/second-post.md delete mode 100644 examples/blog/src/posts/third-post.md create mode 100644 examples/ebook/Readme.md create mode 100644 examples/ebook/custom-modules/metalsmith-ebook/index.js create mode 100644 examples/ebook/custom-modules/metalsmith-ebook/package.json create mode 100644 examples/ebook/index.js rename examples/{blog/layouts/partials/header.hbs => ebook/layouts/layout.html} (50%) create mode 100644 examples/ebook/package.json create mode 100644 examples/ebook/src/chapters/chapter001.md create mode 100644 examples/ebook/src/chapters/chapter002.md create mode 100644 examples/ebook/src/index.md diff --git a/examples/blog/Makefile b/examples/blog/Makefile deleted file mode 100644 index 8d025523..00000000 --- a/examples/blog/Makefile +++ /dev/null @@ -1,8 +0,0 @@ - -build: node_modules - node index.js - -node_modules: package.json - npm install - -.PHONY: build diff --git a/examples/blog/Readme.md b/examples/blog/Readme.md deleted file mode 100644 index 8687bb10..00000000 --- a/examples/blog/Readme.md +++ /dev/null @@ -1,289 +0,0 @@ - -# BLOG - -This example uses Metalsmith to make a simple blog. To test it out yourself just run: - - $ make build - -If that spits out an error saying you there is no such make command. Then try this: - - $ npm install - -followed by: - - $ node index.js - -If your new to node, npm, and metalsmith in general then you are probably a little confused. Lets take a closer look at what we just did. Open up your Makefile located in this examples root directory. It should look like this: - - build: node_modules - node index.js - - node_modules: package.json - npm install - - .PHONY: build - -When you run `$ make build` it is telling the machine to run the build command located in the directories makefile. The build command is located under the `.PHONY` field which acts as an alias to run all the commands found in the make file. When you run `$ make build` it's telling the machine to run all the command found in the make file, which in our case will run `$ npm install` and `$ node index.js`. If you encounter an error and the make command is not recognized on your machine then you probably need to install it someway. I was working on a Windows machine and found it much easier to just stop using the `$ make build` command. Now let's see what these other commands do. - -The `$ npm install` command will take a look at your package.json file and install any dependencies that are listed here. This gives you all the plugins you need to build your blog. Let's open it up and take a look at the plugins we will be using for this site, the package.json file should look like this: - - { - "name": "blog-example", - "private": true, - "dependencies": { - "handlebars": "^4.0.5", - "metalsmith": "^2.1.0", - "metalsmith-layouts": "^1.4.1", - "metalsmith-markdown": "^0.2.1", - "metalsmith-permalinks": "^0.5.0", - "metalsmith-drafts": "^0.0.1", - "metalsmith-collections": "^0.7.0", - "metalsmith-discover-partials": "^0.1.0" - } - } - -Line-by-line here are the plugins we are using and why: - -1. handlebars - Installs handlebars as our templating engine for the build. If you are not familiar with handlebars, check out the docs [here](http://handlebarsjs.com/). -2. metalsmith - Our favorite static site generator. -3. metalsmith-layouts - Interperets out templating engine. -4. metalsmith-markdown - Lets us use markdown to write our posts. -5. metalsmith-permalinks - Gives us pretty urls for seo and usability purposes. Changes the first-post.html to first-post/index.html during the compiling phase. -6. metalsmith-drafts - Lets us mark posts as drafts, which will not be compiled into the production folder -7. metalsmith-collections - Gives us the ability to group posts and iterate through them if we want to list all of our posts specific to a certain category. -8. metalsmith-discover-partials - Lets us use handlebars partials in our project. - -Run `$ npm install` and a folder named "node_modules" will be created in the projects root directory containing all your plugins. - -Now lets take a look at our `index.js` file. It should look something like this: - - var Metalsmith = require('metalsmith'); - var markdown = require('metalsmith-markdown'); - var layouts = require('metalsmith-layouts'); - var permalinks = require('metalsmith-permalinks'); - var drafts = require('metalsmith-drafts'); - var collections = require('metalsmith-collections'); - var discoverPartials = require('metalsmith-discover-partials'); - - Metalsmith(__dirname) - .metadata({ - title: "My Blog", - description: "It's about saying »Hello« to the World.", - generator: "Metalsmith", - url: "http://www.metalsmith.io/", - twitter: "https://twitter.com/", - facebook: "https://www.facebook.com/", - github: "https://github.com/" - }) - .source('./src') - .destination('./build') - .clean(false) - .use(drafts()) - .use(discoverPartials({ - directory: 'layouts/partials', - pattern: /\.hbs$/ - })) - .use(collections({ - posts: { - pattern: 'posts/*.md', - sortBy: 'date', - reverse: true - } - })) - .use(markdown()) - .use(permalinks()) - .use(layouts({ - engine: 'handlebars' - })) - .build(function(err, files) { - if (err) { throw err; } - }); - -Line-by-line lets take a look at whats happening here: - - var Metalsmith = require('metalsmith'); - var markdown = require('metalsmith-markdown'); - var layouts = require('metalsmith-layouts'); - var permalinks = require('metalsmith-permalinks'); - var drafts = require('metalsmith-drafts'); - var collections = require('metalsmith-collections'); - var discoverPartials = require('metalsmith-discover-partials'); - -At the top of the file we instantiate our variables and link them with their corresponding plugins. We will use these variables in our metalsmith function below. - -Next you will see the Metalsmith function being called. Here we will chain the plugins together which will tell Metalsmith which functions to run during our compiling phase. -Order does matter here and sometime when adding a plugin you will notice that it doesn't have the expected result. If your not seeing the result, but Metalsmith is not notifying you of any error then you might just have to change the order the plugin was enter in. I usually try adding it right after the `.clean()` function. - - .metadata({ - title: "My Blog", - description: "It's about saying »Hello« to the World.", - generator: "Metalsmith", - url: "http://www.metalsmith.io/", - twitter: "https://twitter.com/", - facebook: "https://www.facebook.com/", - github: "https://github.com/" - }) - -The metadata function states general data about your project and is a good place to enter global variables that will be used in your project. Here I have included twitter, facebook, and github links in order to use them in the footer of our pages. - - .source('./src') - .destination('./build') - -These two functions I'll describe together. Pretty self explanitory. The `./src` directory is where you will edit your files pre-compiling and the `./build` directory is where your files will end up post-compiling. - - .clean(false) - -If the clean function is set to true it will delete all the files within the `./build` directory before each compiling phase making sure it perfectly mirrors the `./src` folder, while setting the function to false will not delete files in the `./build` directory before each compiling phase. - - .use(drafts()) - -This just simply runs the draft function and enables the drafts plugin and functionality on the project. - - .use(discoverPartials({ - directory: 'layouts/partials', - pattern: /\.hbs$/ - })) - -This will discovery any partials in the `./layouts/partials` folder with the `.hbs` file extension. We will be creating header and footer partials for our blog. - - .use(collections({ - posts: { - pattern: 'posts/*.md', - sortBy: 'date', - reverse: true - } - })) - -This block groups all our posts from the `./build/posts` folder with the `.md` extension. We can now iterate through this object in our template files if we want to list all our posts or posts of a certain category. You can checkout this plugins docs [here](https://github.com/segmentio/metalsmith-collections). - - .use(markdown()) - -Simply enables the markdown syntax for editing pre-compilation. - - .use(permalinks()) - -Makes our urls pretty post compilation, as described before when we were going over our package.json file. - - .use(layouts({ - engine: 'handlebars' - })) - -Lets us use handlebars as the templating engine. - - .build(function(err, files) { - if (err) { throw err; } - }); - -This will finally build the project using the plugins and options we've provided and spit out any errors if they occur. You can also add code to this block if maybe you want it to notify you that the build was successful. - -Now before we run `$ node index.json` lets take a look at our `./src` and `./layouts` folders. In the `./src/posts` folder open up the file `first-post.md`. It should look like this. - - --- - title: My First Post - date: 09-30-2016 - layout: post.html - collection: testing - author: John Doe - draft: false - --- - - Bacon ipsum dolor amet chicken pork meatball cow swine. Meatloaf chicken meatball tail bacon andouille sausage prosciutto rump. Short loin biltong tri-tip, swine venison beef corned beef shank pork chop bacon. Turducken alcatra sirloin short ribs cow short loin. Short loin beef spare ribs meatloaf tri-tip bresaola. Beef ribs bacon meatball beef. - - -Line-by-line here is what its doing. - -1. All the data in the comment block is considered variables to be used in your template files before compilation. You can enter whatever variables you want for each individual post. Remember that some variables work with your plugins and need to be added to each post file. -2. Title, date, and author are self explanitory and general data for this post. -3. layout signifies which template to use for this post. -4. collections works with our collections plugin to group posts in a specific category. -5. drafts works with our drafts plugin to signify if this post is a draft or not and if it should be left out of the compilation phase. -6. Anything under the comment block is part of the contents field. - -Now open up your `./layouts/post file`, it should look something like this: - - {{> header }} - -

{{ title }}

- - {{{ contents }}} - - {{> footer }} - -At a glance you should see how these variables correlate in your templates folder. Take notice that the content of the post will be displayed in the `{{{ contents }}}` line. Another thing you should notice is the `{{> header }}` and `{{> footer }}` lines. If at this point your confused, again take a look at the handlebars docs [here](http://handlebarsjs.com/). This is handlebars way of including partials in the file. Open up your `./layouts/partials/footer.hbs` file. The file should look like this: - - - - - - -Notice that the twitter, facebook, and github variables are not taken from the `first-post.md` file, but are included in the metadata function of the `index.js` file. This is important because that data can be used globally throughout your metalsmith project. If your having trouble remembering that line of code, here it is below: - - .metadata({ - title: "My Blog", - description: "It's about saying »Hello« to the World.", - generator: "Metalsmith", - url: "http://www.metalsmith.io/", - twitter: "https://twitter.com/", - facebook: "https://www.facebook.com/", - github: "https://github.com/" - }) - -Now lets take a look at our template for our landing page at `./layouts/layout.html`: - - {{> header }} - -

{{ title }}

-

{{ description }}

- - {{{ contents }}} - - -
- - {{#each collections.posts }} - -

{{this.title}}

-
- {{/each}} - -
- - {{> footer }} - -This file should look pretty familiar to you with the exception this: - - {{#each collections.posts }} - -

{{this.title}}

-
- {{/each}} - -This is taking advantage of our collections plugin. In our `index.js` file we created a collection called posts that grabs all the `.md` files in our `./src/posts/` folder and creates and obj out of them. Here we are iterating through that object in order to list our recent posts. The collections plugin also give you a way to link to this file with the `{{baseUrl}}` and `{{this.path}}` variables. Here is the block of code from our `index.js` file for your reference: - - .use(collections({ - posts: { - pattern: 'posts/*.md', - sortBy: 'date', - reverse: true - } - })) - -Now we should have a pretty good understanding of the content of our `./build` folder and how it communicates with out `./layouts` folder and how all of that works with out plugins and `index.js` file. So with that lets run our `index.js` file with: - -`$ node index.js` - -This should take a second and if there were no errors found ,it should show you a new line. Now go take a look at your `./build` folder. You should see that it is now populated with an index.html as well as a posts folder with the corresponding posts. Open the `index.html` file up with your browser and start exploring. - - - - - - - - - - diff --git a/examples/blog/index.js b/examples/blog/index.js deleted file mode 100644 index 4b550117..00000000 --- a/examples/blog/index.js +++ /dev/null @@ -1,42 +0,0 @@ -var Metalsmith = require('metalsmith'); -var markdown = require('metalsmith-markdown'); -var layouts = require('metalsmith-layouts'); -var permalinks = require('metalsmith-permalinks'); -var drafts = require('metalsmith-drafts'); -var collections = require('metalsmith-collections'); -var discoverPartials = require('metalsmith-discover-partials'); - - -Metalsmith(__dirname) - .metadata({ - title: "My Blog", - description: "It's about saying »Hello« to the World.", - generator: "Metalsmith", - url: "http://www.metalsmith.io/", - twitter: "https://twitter.com/", - facebook: "https://www.facebook.com/", - github: "https://github.com/" - }) - .source('./src') - .destination('./build') - .clean(false) - .use(drafts()) - .use(discoverPartials({ - directory: 'layouts/partials', - pattern: /\.hbs$/ - })) - .use(collections({ - posts: { - pattern: 'posts/*.md', - sortBy: 'date', - reverse: true - } - })) - .use(markdown()) - .use(permalinks()) - .use(layouts({ - engine: 'handlebars' - })) - .build(function(err, files) { - if (err) { throw err; } - }); diff --git a/examples/blog/layouts/layout.html b/examples/blog/layouts/layout.html deleted file mode 100644 index 97ae04df..00000000 --- a/examples/blog/layouts/layout.html +++ /dev/null @@ -1,19 +0,0 @@ -{{> header }} - -

{{ title }}

-

{{ description }}

- - {{{ contents }}} - - -
- - {{#each collections.posts }} - -

{{this.title}}

-
- {{/each}} - -
- -{{> footer }} diff --git a/examples/blog/layouts/partials/footer.hbs b/examples/blog/layouts/partials/footer.hbs deleted file mode 100644 index 603ec06a..00000000 --- a/examples/blog/layouts/partials/footer.hbs +++ /dev/null @@ -1,8 +0,0 @@ - - - - \ No newline at end of file diff --git a/examples/blog/layouts/post.html b/examples/blog/layouts/post.html deleted file mode 100644 index 2e50e34c..00000000 --- a/examples/blog/layouts/post.html +++ /dev/null @@ -1,7 +0,0 @@ -{{> header }} - -

{{ title }}

- - {{{ contents }}} - -{{> footer }} diff --git a/examples/blog/package.json b/examples/blog/package.json deleted file mode 100644 index 39b6f982..00000000 --- a/examples/blog/package.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "blog-example", - "private": true, - "dependencies": { - "handlebars": "^4.0.5", - "metalsmith": "^2.1.0", - "metalsmith-layouts": "^1.4.1", - "metalsmith-markdown": "^0.2.1", - "metalsmith-permalinks": "^0.5.0", - "metalsmith-drafts": "^0.0.1", - "metalsmith-collections": "^0.7.0", - "metalsmith-discover-partials": "^0.1.0" - } -} diff --git a/examples/blog/src/index.md b/examples/blog/src/index.md deleted file mode 100644 index be26314f..00000000 --- a/examples/blog/src/index.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -layout: layout.html ---- - -

Read what I have to say

diff --git a/examples/blog/src/posts/fifth-post.md b/examples/blog/src/posts/fifth-post.md deleted file mode 100644 index fe4f507e..00000000 --- a/examples/blog/src/posts/fifth-post.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: My Fifth Post -date: 10-04-2016 -layout: post.html -collection: vows -author: John Doe -draft: true ---- - -Pig pork short loin turducken tail spare ribs pork belly ham shank burgdoggen corned beef tongue frankfurter ribeye. T-bone pork belly turducken short loin filet mignon chuck tail cupim pastrami hamburger spare ribs. Tenderloin meatball tail t-bone landjaeger, brisket fatback tongue salami kielbasa. Boudin biltong andouille tongue picanha salami beef ribs shankle t-bone. T-bone prosciutto pork belly tail tongue tenderloin doner. diff --git a/examples/blog/src/posts/first-post.md b/examples/blog/src/posts/first-post.md deleted file mode 100644 index 51c3e7a6..00000000 --- a/examples/blog/src/posts/first-post.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: My First Post -date: 09-30-2016 -layout: post.html -collection: vows -author: John Doe -draft: false ---- - -Bacon ipsum dolor amet chicken pork meatball cow swine. Meatloaf chicken meatball tail bacon andouille sausage prosciutto rump. Short loin biltong tri-tip, swine venison beef corned beef shank pork chop bacon. Turducken alcatra sirloin short ribs cow short loin. Short loin beef spare ribs meatloaf tri-tip bresaola. Beef ribs bacon meatball beef. diff --git a/examples/blog/src/posts/fourth-post.md b/examples/blog/src/posts/fourth-post.md deleted file mode 100644 index 02da48ce..00000000 --- a/examples/blog/src/posts/fourth-post.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: My Fourth Post -date: 10-03-2016 -layout: post.html -collection: vows -author: John Doe -draft: false ---- - -Pig pork short loin turducken tail spare ribs pork belly ham shank burgdoggen corned beef tongue frankfurter ribeye. T-bone pork belly turducken short loin filet mignon chuck tail cupim pastrami hamburger spare ribs. Tenderloin meatball tail t-bone landjaeger, brisket fatback tongue salami kielbasa. Boudin biltong andouille tongue picanha salami beef ribs shankle t-bone. T-bone prosciutto pork belly tail tongue tenderloin doner. diff --git a/examples/blog/src/posts/second-post.md b/examples/blog/src/posts/second-post.md deleted file mode 100644 index 94517d88..00000000 --- a/examples/blog/src/posts/second-post.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: My Second Post -date: 10-01-2016 -layout: post.html -collection: vows -author: John Doe -draft: false ---- - -Tongue doner meatball, tail pancetta jerky frankfurter ground round jowl bacon. Pork chop pork loin corned beef, ham hock t-bone pig chicken burgdoggen. Andouille tri-tip turkey, beef ribs chuck landjaeger porchetta. Leberkas short ribs ham hock, burgdoggen doner venison t-bone ground round. Swine venison jowl biltong prosciutto. Pork belly fatback prosciutto short loin, bacon boudin biltong ground round cow swine chicken. Turducken shoulder swine rump chuck tenderloin andouille cow, picanha jowl sausage ribeye kevin. - diff --git a/examples/blog/src/posts/third-post.md b/examples/blog/src/posts/third-post.md deleted file mode 100644 index 27e01079..00000000 --- a/examples/blog/src/posts/third-post.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: My Third Post -date: 10-02-2016 -layout: post.html -collection: vows -author: John Doe -draft: false ---- - -Hamburger kevin pastrami shankle bacon chicken burgdoggen short loin shoulder tri-tip cupim venison. Filet mignon corned beef ham ground round, meatball porchetta strip steak jerky pancetta t-bone meatloaf. Capicola fatback shoulder shank doner. Shank tongue pork loin kevin ball tip andouille kielbasa ham hock fatback beef ribs prosciutto turducken sirloin alcatra sausage. - diff --git a/examples/ebook/Readme.md b/examples/ebook/Readme.md new file mode 100644 index 00000000..c44bdd27 --- /dev/null +++ b/examples/ebook/Readme.md @@ -0,0 +1,409 @@ +# Creating an Ebook Generator with Metalsmith + +In this example we will learn how to create a simple Metalsmith project and custom plugin that will generate an html, pdf, epub, and mobi file for and entire ebook as well as each individual chapter. First Metalsmith will generate html files based on our markdown files, then those files will be used to create an ebook in pdf, epub, and mobi format based on the html files. Our custom plugin will use three different node plugins to generate the three seperate ebook formats. I will try to assume that the reader has no experience with node, npm, nor metalsmith. + +## Install Node and Update NPM + +You should have npm and node installed. If you do not have these installed you can download and install Node.js via the installer from [here](https://nodejs.org/en/download/). Once the installer is completed you must update npm. Update npm by opening up your bash client and running the line below: + + $ npm install npm@latest -g + +##Setting Up Your Project + +Let's create a directory for this project and cd into it. Navigate to the folder where you usually place all your projects. In your favorite bash client make the projects directory, I'm going to name mine `html-minify-plugin-example`. + + $ mkdir ebook-example + +Now that the directory is created, I'm going to open it up with + + $ cd ebook-example + +##Creating and Running your package.json file + +Once your in the projects directory, create a file called `package.json`. The `package.json` file will have information relative to your project and is necessary for npm to install different tools and modules that will help you while building your project. The contents of your `package.json` should look like this. + + { + "name": "ebook-example", + "version": "1.0.0", + "dependencies": { + "handlebars": "^4.0.5", + "metalsmith": "^2.1.0", + "metalsmith-layouts": "^1.4.1", + "metalsmith-markdown": "^0.2.1", + "metalsmith-permalinks": "^0.5.0", + "metalsmith-collections": "^0.7.0" + } + } + +Let's go through the fields in this file. + +1. The name field is the name of your project and is required. +2. The version number is the version number of your project and is required. +3. Dependencies are the dependencies the project will use. +4. handlebars will enable handlebars templating to be used in our project. +5. metalsmith allows metalsmith to be used in our project +6. metalsmith-layouts helps us generate templates in conjunction with handlebars. +7. metalsmith-markdown enables markdown files to be compiled into html files. +8. metalsmith-permalinks will prettify our urls and nest our subdirectories to make them more managable. +9. metalsmith-collection helps us group different files and iterate over these files in other places in our project. + +Now that we have created our package.json, file let's run it. If npm completes the installation with no errors you should see a new folder in the directory named `node-modules`, which will have all the dependencies we listed in the `package.json` file. + + $ npm install + +##Creating Our index.js file + +Lets create a simple index.js file in our projects root directory. The file should look like this: + + var Metalsmith = require('metalsmith'); + var markdown = require('metalsmith-markdown'); + var layouts = require('metalsmith-layouts'); + var permalinks = require('metalsmith-permalinks'); + var collections = require('metalsmith-collections'); + var ebook = require('./custom-modules/metalsmith-ebook'); + + Metalsmith(__dirname) + .metadata({ + title: "Ebook Generator Example", + description: "Generate ebooks easily with Metalsmith!", + generator: "Metalsmith", + url: "http://www.metalsmith.io/" + }) + .source('./src') + .destination('./build') + .clean(true) + .use(collections({ + chapters: { + pattern: 'chapters/*.md' + } + })) + .use(markdown()) + .use(permalinks()) + .use(layouts({ + engine: 'handlebars' + })) + .use(ebook({ + title: "Metalsmith Ebook Example", + author: "Team Metalsmith", + pdf: { + "format": "Letter", // allowed units: A3, A4, A5, Legal, Letter, Tabloid + "orientation": "portrait", // portrait or landscape + } + })) + .build(function(err, files) { + if (err) { + throw err; + } + }); + +Now lets dissect this file, line-by-line. + + var Metalsmith = require('metalsmith'); + var markdown = require('metalsmith-markdown'); + var layouts = require('metalsmith-layouts'); + var permalinks = require('metalsmith-permalinks'); + var collections = require('metalsmith-collections'); + var ebook = require('./custom-modules/metalsmith-ebook'); + +At the top of our `index.js` we declare our variables for metalsmith as well as all the plugins we will be using in this file. You should notice that all these variables are plugins that we declared in our package.json, one of the variable ebook, is initializing our custom plugin that we will build later on in this tutorial. + + Metalsmith(__dirname) + .metadata({ + title: "Ebook Example", + description: "Generate ebooks easily with Metalsmith!", + generator: "Metalsmith", + url: "http://www.metalsmith.io/" + }) + .source('./src') + .destination('./build') + .clean(true) + .use(collections({ + chapters: { + pattern: 'chapters/*.md' + } + })) + .use(markdown()) + .use(permalinks()) + .use(layouts({ + engine: 'handlebars' + })) + .use(ebook({ + title: "Metalsmith Ebook Example", + author: "Team Metalsmith", + pdf: { + "format": "Letter", // allowed units: A3, A4, A5, Legal, Letter, Tabloid + "orientation": "portrait", // portrait or landscape + } + })) + .build(function(err, files) { + if (err) { + throw err; + } + }); + +Here is our metalsmith instance, where metalsmith declares its methods sequentially from metalsmiths native api as well as any methods from plugins we might be using. So line-by-line lets see what going on here. + +1. The `.metadata()` method is providing variables that can be used throughout your project. This is a good place to put general variables that will be used throughout the project. In this particular instance we use the title field to declare our ebook title which is a required variable in our custom ebook plugin we will be building later on in this tutorial. Make sure to add the title now. +2. The `source()` method declares the directory where the files will be pre-manipulation. +3. The `destination()` method declares the destination directory the files will end up post-manipulation. +4. The `clean()` method will delete the contents of the destination directory everytime metalsmith is run and the files are manipulated. In this project we will set this value to true. +5. The `use()` method wraps instances of plugins we will use in our project. +6. The `collections()` method groups all our files from the `./chapters` folder with the `.md` extension. We can now iterate through this object in our template files if we want to list all our chapters or chapters of a certain category. You can checkout this plugins docs [here](https://github.com/segmentio/metalsmith-collections). +7. The `markdown()` enables the markdown syntax for editing pre-compilation. +8. The `permalinks()` method nests our files in subdirectories. +9. The `layouts()` enables handlebars as the templating engine. +10. The `ebook()` plugin generates pdf, epub, and mobi files based on the html files in our project. +11. The `build()` method will finally build the project using the plugins and options we've provided and spit out any errors if they occur. You can also add code to this block if maybe you want it to notify you that the build was successful. + +##Creating Your Source and Destination Folders + +1. Create a folder named `src` in your project's base directory. +2. Navigate to your projects `src` directory and create a file called `index.md`. +3. Open up your `index.md`. +4. Copy the YAML block below into your file, then save and close it. + + --- + layout: layout.html + --- + +5. The layout field will tell our layouts plugin which template to use when compiling this file into html. +6. Now create a subdirectory in your src folder called `chapters`. +7. Navigate into your `chapters` subdirectory and create a file called `chapter001.md`. +8. In your `chapter001.md` copy and paste the contents below and save and close it. + + --- + chapter: 1 + title: The First Example Chapter + --- + ## Chapter 1: The First Example Chapter + The more she shat robb nymeria jaehaerys euron night's king. Mya stone pate lyanna white walkers. Qhorin halfhand ashara yoren gendry the hound, robb mya stone varys aemon. Wights thoros areo hotah shaggydog, olenna night's king tormund balon edmure asha ghost summer bran. Gendry areo margaery craster aegon daeron. + Stannis brandon stark areo hotah, greywind varamyr meera reed dunk theon euron baelor night's king sandor. Catelyn myrcella gerold hightower, rhaegar tommen podrick grey worm mance rayder donal noye robb dunk old nan areo hotah craster. Arthur dayne meera reed dagmer cleftjaw areo hotah white walkers the hound. Olenna Wun Weg Wun Dar Wun aemon edmure rickon khal drogo. + +5. Let's take a look at this file more closely. In comments at the top you will see a block of YAML declaring two variables chapters and title. In order to use our plugin properly we need to declare the chapters variable and the title variable for each of our individual chapters. The chapters field will let us order our ebook and ascending order and the title field will act as our individual chapters file name when we save the chapter to file. Let rest of the file is written in normal markdown syntax and will act as the content for our chapter. +6. Create another file in the `chapters` subdirectory called `chapter002.md`. +7. In your `chapter002.md` copy and paste the contents below and save and close it. + + --- + chapter: 2 + title: The Second Example Chapter + --- + ## Chapter 2: The Second Example Chapter + The more she shat robb nymeria jaehaerys euron night's king. Mya stone pate lyanna white walkers. Qhorin halfhand ashara yoren gendry the hound, robb mya stone varys aemon. Wights thoros areo hotah shaggydog, olenna night's king tormund balon edmure asha ghost summer bran. Gendry areo margaery craster aegon daeron. + Stannis brandon stark areo hotah, greywind varamyr meera reed dunk theon euron baelor night's king sandor. Catelyn myrcella gerold hightower, rhaegar tommen podrick grey worm mance rayder donal noye robb dunk old nan areo hotah craster. Arthur dayne meera reed dagmer cleftjaw areo hotah white walkers the hound. Olenna Wun Weg Wun Dar Wun aemon edmure rickon khal drogo. + +7. Now navigate out of your `chapters` and `src` folder and create a `build` folder in your projects root directory. This will act as the destination folder for your project after it's compiled. + +##Creating our layout.html File for the Full Book. + +1. Navigate back to your projects root directory. +2. Create a directory called `layouts`. +3. Navigate into your `layouts` directory and create a file called `layout.html`. +4. Your `layout.html` file should look like this: + + + + + + {{ title }} + + + + + {{#each collections.chapters }} + {{{this.contents}}} +
+ {{/each}} + + + +5. In this file we take advantage of our `collections` plugin to iterate over all the chapters in our book. This template will display the full contents of our ebook. The `
` is used to seperate chapters for our ebook plugin. + +##Creating our Custom Ebook Plugin + +1. In your projects base directory, create a folder called `custom_modules` +2. In the custom_modules directory, create a folder called `metalsmith-ebook`. This folder will house the code that will make up your `metalsmith-ebook` plugin. +3. In your `metalsmith-ebook` folder create a `package.json` file. It should look like this: + + { + "name": "metalsmith-ebook", + "version": "1.0.0", + "dependencies": { + "html-pdf": "^2.1.0", + "epub-gen": "^0.0.16", + "kindlegen": "1.0.1", + "mkdirp": "^0.5.1" + } + } + +4. Now remember that name and version are required fields in a package.json. In our dependencies field we add a couple of different plugins we will be using in order to generate the pdf, epub, and mobi files. Let's go through these and describe what they do. +5. `html-pdf` takes the html files generated by Metalsmith and converts them into pdf files. +6. `epub-gen` generates epub files based on html files. +7. `kindlegen` generates mobi files based on epub files. +8. `mkdirp` easily creates directories that don't exist. +9. now that we understand what each of the plugins do lets open up our favorite bash client and install via: + + $ npm install + +10. This will create and install the plugins in the `node_modules` folder. + +##Creating our Plugins index.js File + +1. In the `metalsmith-ebook` directory create an `index.js` file. It should look like this: + + var pdf = require('html-pdf'); + var Epub = require("epub-gen"); + var kindlegen = require('kindlegen'); + var fs = require('fs'); + var mkdirp = require('mkdirp'); + var my_plugin = function (options) { + return function (files, metalsmith, done) { + var destPath = metalsmith.destination()+'/'; + var metadata = metalsmith.metadata(); + // THROW ERROR IF TITLE ATTRIBUTE IS NOT SET + if (!metadata.title) { + throw("YOU MUST SET TITLE ATTRIBUTE OF METADATA"); + }else{ + var bookTitle = metadata.title; + } + Object.keys(files).forEach(function(file){ + var contents = files[file].contents.toString(); + var path = files[file].path + '/'; + var title = files[file].title; + var p0 = new Promise( + function(resolve, reject){ + if (files[file].path) { + var filePath = destPath + path + title + '.pdf'; + createPDF(contents, options.pdf, filePath); + }else{ + var filePath = destPath + bookTitle + '.pdf'; + createPDF(contents, options.pdf, filePath); + } + function createPDF(contents, options, filePath){ + pdf.create(contents, options).toFile(filePath, function(err, res) { + if (err) { + reject(); + } else { + resolve(contents, options); + } + }); + } + } + ); + p0.then(function(){ + console.log("PDF CREATED"); + var p1 = new Promise( + function(resolve, reject){ + var multiChapterCheck = contents.includes(''); + if (multiChapterCheck) { + var res = contents.split('
'); + var chapterArray = []; + res.forEach(function(entry){ + chapterArray.push({data: entry}); + }); + } + // Generate EPUB + if (multiChapterCheck) { + chapterArray.pop(); + options.epub = { + title: options.title, // *Required, title of the book. + author: options.author, // *Required, name of the author. + content: chapterArray + }; + }else{ + options.epub = { + title: options.title, // *Required, title of the book. + author: options.author, // *Required, name of the author. + content: [ + { + data: contents + } + ] + }; + } + if (files[file].path) { + var epubpath = destPath+path+title+'.epub'; + var mobipath = destPath+path+title+'.mobi'; + var dir = destPath+path; + }else{ + var epubpath = destPath + bookTitle + '.epub'; + var mobipath = destPath + bookTitle + '.mobi'; + var dir = destPath + } + if (!fs.existsSync(dir)){ + mkdirp(dir, function (err) { + if (err){ + reject(err); + } + else { + var paths = { + epubpath: epubpath, + mobipath: mobipath + } + resolve(paths); + } + }); + }else{ + var paths = { + epubpath: epubpath, + mobipath: mobipath + } + resolve(paths); + } + } + ); + p1.then(function(paths) { + new Epub(options.epub, paths.epubpath).promise.then(function(){ + console.log('EPUB CREATED'); + kindlegen(fs.readFileSync(paths.epubpath), (error, mobi) => { + if (error) { + console.log(error); + }else{ + fs.writeFile(paths.mobipath, mobi, function(err){ + if (err) { + console.log(err); + }else{ + console.log("MOBI CREATED"); + }; + }); + }; + }); + }, function(err){ + console.error("Failed to generate Ebook because of ", err) + }); + }).catch(function(err){ + console.log(err); + }); + }).catch( function() { + console.log('ERROR GENERATING PDF'); + }); + }); + done(); + }; + }; + // Expose the plugin + module.exports = my_plugin; + +2. Essentially what's happening here is we use promises to generate pdf files first. Once the pdf files are done we then create epub files using the content passed by the metalsmith function. Once the epub are finished we then generate the mobi files based on the epubs. +3. Now that we understand whats going on in our ebook plugin lets navigate back to our projects root directory and build our project. + +##Building our Project + +1. Once we are back in our projects root directory open up your favorite bash client and run: + + $ node index.js + +2. You should see a bunch of output. If there are no errors the book should have been compiled. +3. Open up your projects `./build` folder. Html, pdf, epub, and mobi files should be generated for the full book as well as chapter-by-chapter in their relative subdirectories. + + + + + + + + + + + + + diff --git a/examples/ebook/custom-modules/metalsmith-ebook/index.js b/examples/ebook/custom-modules/metalsmith-ebook/index.js new file mode 100644 index 00000000..da79db7b --- /dev/null +++ b/examples/ebook/custom-modules/metalsmith-ebook/index.js @@ -0,0 +1,192 @@ +var pdf = require('html-pdf'); +var Epub = require("epub-gen"); +var kindlegen = require('kindlegen'); +var fs = require('fs'); +var mkdirp = require('mkdirp'); + +var my_plugin = function (options) { + return function (files, metalsmith, done) { + + var destPath = metalsmith.destination()+'/'; + var metadata = metalsmith.metadata(); + + // THROW ERROR IF TITLE ATTRIBUTE IS NOT SET + if (!metadata.title) { + throw("YOU MUST SET TITLE ATTRIBUTE OF METADATA"); + }else{ + var bookTitle = metadata.title; + } + + Object.keys(files).forEach(function(file){ + + var contents = files[file].contents.toString(); + + var path = files[file].path + '/'; + + var title = files[file].title; + + var p0 = new Promise( + + function(resolve, reject){ + + if (files[file].path) { + + var filePath = destPath + path + title + '.pdf'; + + createPDF(contents, options.pdf, filePath); + + }else{ + + var filePath = destPath + bookTitle + '.pdf'; + + createPDF(contents, options.pdf, filePath); + + } + + function createPDF(contents, options, filePath){ + + pdf.create(contents, options).toFile(filePath, function(err, res) { + + if (err) { + reject(); + } else { + resolve(contents, options); + } + + }); + + } + + } + ); + + p0.then(function(){ + + console.log("PDF CREATED"); + + var p1 = new Promise( + + function(resolve, reject){ + + var multiChapterCheck = contents.includes(''); + + if (multiChapterCheck) { + var res = contents.split('
'); + + var chapterArray = []; + + res.forEach(function(entry){ + + chapterArray.push({data: entry}); + + }); + } + + // Generate EPUB + + if (multiChapterCheck) { + + chapterArray.pop(); + + options.epub = { + title: options.title, // *Required, title of the book. + author: options.author, // *Required, name of the author. + content: chapterArray + }; + + }else{ + + options.epub = { + title: options.title, // *Required, title of the book. + author: options.author, // *Required, name of the author. + content: [ + { + data: contents + } + ] + }; + + } + + if (files[file].path) { + var epubpath = destPath+path+title+'.epub'; + var mobipath = destPath+path+title+'.mobi'; + var dir = destPath+path; + }else{ + var epubpath = destPath + bookTitle + '.epub'; + var mobipath = destPath + bookTitle + '.mobi'; + var dir = destPath + } + + if (!fs.existsSync(dir)){ + mkdirp(dir, function (err) { + if (err){ + reject(err); + } + else { + var paths = { + epubpath: epubpath, + mobipath: mobipath + } + resolve(paths); + } + }); + }else{ + var paths = { + epubpath: epubpath, + mobipath: mobipath + } + resolve(paths); + } + + + } + + ); + + p1.then(function(paths) { + + new Epub(options.epub, paths.epubpath).promise.then(function(){ + + console.log('EPUB CREATED'); + + kindlegen(fs.readFileSync(paths.epubpath), (error, mobi) => { + if (error) { + console.log(error); + }else{ + fs.writeFile(paths.mobipath, mobi, function(err){ + if (err) { + console.log(err); + }else{ + console.log("MOBI CREATED"); + }; + }); + }; + }); + + }, function(err){ + + console.error("Failed to generate Ebook because of ", err) + + }); + + }).catch(function(err){ + + console.log(err); + + }); + + }).catch( function() { + + console.log('ERROR GENERATING PDF'); + + }); + + }); + + done(); + }; +}; + +// Expose the plugin +module.exports = my_plugin; diff --git a/examples/ebook/custom-modules/metalsmith-ebook/package.json b/examples/ebook/custom-modules/metalsmith-ebook/package.json new file mode 100644 index 00000000..d03c122a --- /dev/null +++ b/examples/ebook/custom-modules/metalsmith-ebook/package.json @@ -0,0 +1,10 @@ +{ + "name": "metalsmith-ebook", + "version": "1.0.0", + "dependencies": { + "html-pdf": "^2.1.0", + "epub-gen": "^0.0.16", + "kindlegen": "1.0.1", + "mkdirp": "^0.5.1" + } +} diff --git a/examples/ebook/index.js b/examples/ebook/index.js new file mode 100644 index 00000000..8df88cc4 --- /dev/null +++ b/examples/ebook/index.js @@ -0,0 +1,43 @@ +var Metalsmith = require('metalsmith'); +var markdown = require('metalsmith-markdown'); +var layouts = require('metalsmith-layouts'); +var permalinks = require('metalsmith-permalinks'); +var collections = require('metalsmith-collections'); +var ebook = require('./custom-modules/metalsmith-ebook'); + +Metalsmith(__dirname) + .metadata({ + title: "Ebook Example", + description: "Generate ebooks easily with Metalsmith!", + generator: "Metalsmith", + url: "http://www.metalsmith.io/" + }) + .source('./src') + .destination('./build') + .clean(true) + .use(collections({ + chapters: { + pattern: 'chapters/*.md' + } + })) + .use(markdown()) + .use(permalinks()) + .use(layouts({ + engine: 'handlebars' + })) + .use(ebook({ + title: "Metalsmith Ebook Example", + author: "Team Metalsmith", + pdf: { + "format": "Letter", // allowed units: A3, A4, A5, Legal, Letter, Tabloid + "orientation": "portrait", // portrait or landscape + } + })) + .build(function(err, files) { + if (err) { + throw err; + } + }); + + + diff --git a/examples/blog/layouts/partials/header.hbs b/examples/ebook/layouts/layout.html similarity index 50% rename from examples/blog/layouts/partials/header.hbs rename to examples/ebook/layouts/layout.html index a1859e01..be01ae30 100644 --- a/examples/blog/layouts/partials/header.hbs +++ b/examples/ebook/layouts/layout.html @@ -4,10 +4,17 @@ {{ title }} + -
-

- Home -

-
\ No newline at end of file + +{{#each collections.chapters }} + + {{{this.contents}}} + +
+ +{{/each}} + + + \ No newline at end of file diff --git a/examples/ebook/package.json b/examples/ebook/package.json new file mode 100644 index 00000000..8ced6fde --- /dev/null +++ b/examples/ebook/package.json @@ -0,0 +1,13 @@ +{ + "name": "ebook-example", + "version": "1.0.0", + "dependencies": { + "handlebars": "^4.0.5", + "metalsmith": "^2.1.0", + "metalsmith-layouts": "^1.4.1", + "metalsmith-markdown": "^0.2.1", + "metalsmith-permalinks": "^0.5.0", + "metalsmith-collections": "^0.7.0", + "mkdirp": "^0.5.1" + } +} diff --git a/examples/ebook/src/chapters/chapter001.md b/examples/ebook/src/chapters/chapter001.md new file mode 100644 index 00000000..02fe876d --- /dev/null +++ b/examples/ebook/src/chapters/chapter001.md @@ -0,0 +1,10 @@ +--- +chapter: 1 +title: The First Example Chapter +--- + +## Chapter 1: The First Example Chapter + +The more she shat robb nymeria jaehaerys euron night's king. Mya stone pate lyanna white walkers. Qhorin halfhand ashara yoren gendry the hound, robb mya stone varys aemon. Wights thoros areo hotah shaggydog, olenna night's king tormund balon edmure asha ghost summer bran. Gendry areo margaery craster aegon daeron. + +Stannis brandon stark areo hotah, greywind varamyr meera reed dunk theon euron baelor night's king sandor. Catelyn myrcella gerold hightower, rhaegar tommen podrick grey worm mance rayder donal noye robb dunk old nan areo hotah craster. Arthur dayne meera reed dagmer cleftjaw areo hotah white walkers the hound. Olenna Wun Weg Wun Dar Wun aemon edmure rickon khal drogo. diff --git a/examples/ebook/src/chapters/chapter002.md b/examples/ebook/src/chapters/chapter002.md new file mode 100644 index 00000000..51a34ad1 --- /dev/null +++ b/examples/ebook/src/chapters/chapter002.md @@ -0,0 +1,11 @@ +--- +chapter: 2 +title: The Second Example Chapter +--- + +## Chapter 2: The Second Example Chapter + +The more she shat robb nymeria jaehaerys euron night's king. Mya stone pate lyanna white walkers. Qhorin halfhand ashara yoren gendry the hound, robb mya stone varys aemon. Wights thoros areo hotah shaggydog, olenna night's king tormund balon edmure asha ghost summer bran. Gendry areo margaery craster aegon daeron. + +Stannis brandon stark areo hotah, greywind varamyr meera reed dunk theon euron baelor night's king sandor. Catelyn myrcella gerold hightower, rhaegar tommen podrick grey worm mance rayder donal noye robb dunk old nan areo hotah craster. Arthur dayne meera reed dagmer cleftjaw areo hotah white walkers the hound. Olenna Wun Weg Wun Dar Wun aemon edmure rickon khal drogo. + diff --git a/examples/ebook/src/index.md b/examples/ebook/src/index.md new file mode 100644 index 00000000..fe121df9 --- /dev/null +++ b/examples/ebook/src/index.md @@ -0,0 +1,3 @@ +--- +layout: layout.html +--- From 39e5c1390980d9c153689d29ea3c051638305049 Mon Sep 17 00:00:00 2001 From: Robert Montesano Date: Tue, 11 Oct 2016 13:30:47 -0400 Subject: [PATCH 3/3] corrected a typo --- examples/ebook/Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ebook/Readme.md b/examples/ebook/Readme.md index c44bdd27..76385298 100644 --- a/examples/ebook/Readme.md +++ b/examples/ebook/Readme.md @@ -10,7 +10,7 @@ You should have npm and node installed. If you do not have these installed you c ##Setting Up Your Project -Let's create a directory for this project and cd into it. Navigate to the folder where you usually place all your projects. In your favorite bash client make the projects directory, I'm going to name mine `html-minify-plugin-example`. +Let's create a directory for this project and cd into it. Navigate to the folder where you usually place all your projects. In your favorite bash client make the projects directory, I'm going to name mine `ebook-example`. $ mkdir ebook-example