├── .gitignore ├── .jshintrc ├── Gruntfile.js ├── README.md ├── package.json ├── tasks ├── data │ ├── highlight.docco.css │ ├── highlight.github.css │ ├── highlight.monokai.css │ ├── highlight.solarized-dark.css │ ├── highlight.solarized-light.css │ ├── highlight.xcode.css │ ├── scripts.js │ ├── template.hbs │ └── theme.css ├── libs │ └── sassdown.js └── sassdown.js └── test ├── custom └── assets │ ├── css │ └── application.css │ └── sass │ ├── application.sass │ ├── base │ ├── _base.sass │ └── _variables.sass │ └── modules │ └── _media.sass ├── example └── assets │ ├── css │ ├── screen.css │ ├── theme.css │ └── webfonts.css │ ├── js │ └── script.js │ └── sass │ ├── partials │ ├── base │ │ ├── _headings.scss │ │ ├── _preformatted.scss │ │ └── _typography.scss │ ├── comments │ │ ├── _comments-sass.sass │ │ └── _comments-scss.scss │ ├── modules │ │ └── _navigation.scss │ └── objects │ │ ├── common │ │ ├── _circular.scss │ │ ├── _clearfix.scss │ │ ├── _vertical-middle.scss │ │ └── _visual-hide.scss │ │ ├── excluded │ │ └── _balerts.scss │ │ └── user │ │ ├── _alerts.scss │ │ └── _buttons.scss │ ├── readme.md │ └── screen.scss └── helpers ├── handlebars.lowercase.js └── nested-helpers └── handlebars.uppercase.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | Thumbs.db 3 | test/custom/styleguide 4 | test/example/styleguide 5 | node_modules 6 | npm-debug.log 7 | tmp 8 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "bitwise": true, 3 | "camelcase": true, 4 | "curly": true, 5 | "eqeqeq": true, 6 | "immed": true, 7 | "indent": 4, 8 | "latedef": true, 9 | "newcap": true, 10 | "noempty": true, 11 | "quotmark": "single", 12 | "undef": true, 13 | "unused": true, 14 | "trailing": true, 15 | "eqnull": true, 16 | "browser": true, 17 | "jquery": true, 18 | "node": true, 19 | "devel": true, 20 | "globals": { 21 | "grunt": true 22 | } 23 | } -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | /* 2 | * sassdown 3 | * github.com/nopr/sassdown 4 | * 5 | * Copyright (c) 2013 Jesper Hills, contributors 6 | * Some rights reserved 7 | */ 8 | 9 | 'use strict'; 10 | 11 | module.exports = function(grunt) { 12 | 13 | // Project configuration. 14 | grunt.initConfig({ 15 | 16 | jshint: { 17 | all: [ 18 | 'Gruntfile.js', 19 | 'tasks/libs/sassdown.js', 20 | 'tasks/sassdown.js', 21 | ], 22 | options: { 23 | jshintrc: '.jshintrc', 24 | }, 25 | }, 26 | 27 | test: 'xxx', 28 | 29 | // Before generating any new files, remove any previously-created files. 30 | clean: { 31 | example: ['test/*/styleguide/'], 32 | }, 33 | 34 | // Configuration to be run (and then tested). 35 | sassdown: { 36 | defaultStyleguide: { 37 | options: { 38 | assets: [ 39 | 'test/example/assets/css/*.css', 40 | 'test/example/assets/js/*.js', 41 | ], 42 | readme: 'test/example/assets/sass/readme.md', 43 | //handlebarsHelpers: ['test/helpers/**/*.js'], 44 | //theme: 'test/theme.css', 45 | //template: 'test/template.hbs' 46 | //highlight: 'github' 47 | }, 48 | files: [{ 49 | expand: true, 50 | cwd: 'test/example/assets/sass/partials', 51 | src: ['**/*.{scss,sass}'], 52 | dest: 'test/example/styleguide/' 53 | }] 54 | }, 55 | customStyleguide: { 56 | options: { 57 | assets: [ 58 | 'test/custom/assets/css/*.css' 59 | ], 60 | excludeMissing: true, 61 | readme: false, 62 | commentStart: /\/\* (?:[=]{4,}\n[ ]+|(?!\n))/, 63 | commentEnd: /[ ]+[=]{4,} \*\// 64 | }, 65 | files: [{ 66 | expand: true, 67 | cwd: 'test/custom/assets/sass', 68 | src: ['**/*.sass'], 69 | dest: 'test/custom/styleguide/' 70 | }] 71 | } 72 | 73 | }, 74 | 75 | }); 76 | 77 | // Actually load this plugin's task(s). 78 | grunt.loadTasks('tasks'); 79 | 80 | // These plugins provide necessary tasks. 81 | grunt.loadNpmTasks('grunt-contrib-jshint'); 82 | grunt.loadNpmTasks('grunt-contrib-clean'); 83 | grunt.loadNpmTasks('grunt-contrib-nodeunit'); 84 | 85 | // By default, lint and run all tests. 86 | grunt.registerTask('default', ['clean', 'jshint', 'sassdown']); 87 | 88 | }; 89 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sassdown 2 | 3 | > Grunt plugin for building living styleguides with Handlebars from Markdown comments in CSS, Sass and LESS files. 4 | 5 | **Note: *This plugin is in semi-active development!* So expect it to be a little rough around the edges. If you have any questions, issues or suggestions get in touch. Currently on version `0.2.7`.** 6 | 7 | 1. [Getting started](#getting-started) 8 | 2. [Sassdown task](#sassdown-task) 9 | - [Overview](#overview) 10 | - [Options](#options) 11 | - [Usage](#usage) 12 | 3. [Markdown](#markdown) 13 | 4. [Handlebars](#handlebars) 14 | 5. [Highlight.js](#highlightjs) 15 | 6. [Data Objects](#data-objects) 16 | - [Page](#page) 17 | - [Pages](#pages) 18 | 7. [Template](#template) 19 | 8. [Sass](#sass) 20 | 21 | ### What's new in version 0.2.7? 22 | 23 | - Path resolving is relative; no more issues serving from localhost or using file:// protocols 24 | - Whitespace and preformatting is preserved in markup results 25 | - Source styles shown in conjunction with markup and result 26 | - Pages are served form an array-literal node tree; meaning clearer and nested navigation 27 | - Comment block matching is modifiable via regular expressions 28 | - Choice of syntax highlighting styles from various popular Highlight.js themes 29 | - Syntax highlighting is done with Node before templates compile; faster page loads 30 | 31 | ## Getting started 32 | 33 | Install this plugin with this command: 34 | 35 | ```bash 36 | npm install sassdown --save-dev 37 | ``` 38 | 39 | Enabled inside your Gruntfile with this line of JavaScript: 40 | 41 | ```js 42 | grunt.loadNpmTasks('sassdown'); 43 | ``` 44 | 45 | ## Sassdown Task 46 | 47 | Run the task using `grunt sassdown`. Task targets, files and options may be specified according to the grunt [configuring tasks](http://gruntjs.com/configuring-tasks) guide. 48 | 49 | ### Overview 50 | In your project's Gruntfile, add a section named `sassdown` to the data object passed into `grunt.initConfig()`. 51 | 52 | ```js 53 | sassdown: { 54 | options: { 55 | // Task-specific options go here. 56 | }, 57 | target: { 58 | // Target-specific file lists and/or options go here. 59 | }, 60 | }, 61 | ``` 62 | 63 | ### Options 64 | 65 | #### options.assets 66 | Type: `Array`
67 | Default: `null` 68 | 69 | *Optional*. Array of file paths. Will be included into the styleguide output. Supports [globbing](http://gruntjs.com/configuring-tasks#globbing-patterns). Supports relative and absolute file paths (eg. `http://`, `https://`, `//` or even `file://`). 70 | 71 | #### options.template 72 | Type: `String`
73 | Default: `null` 74 | 75 | *Optional*. A path to a Handlebars template file. Will use default Sassdown template if left blank. 76 | 77 | #### options.handlebarsHelpers 78 | Type: `Array`
79 | Default: `null` 80 | 81 | *Optional*. Array of file paths. The [Handlebars helpers](http://handlebarsjs.com/#helpers) will be available to use in the template. Supports [globbing](http://gruntjs.com/configuring-tasks#globbing-patterns). Supports relative and absolute file paths (eg. `http://`, `https://` or even `file://`). 82 | 83 | #### options.theme 84 | Type: `String`
85 | Default: `null` 86 | 87 | *Optional*. A path to a theme stylesheet. Will use default Sassdown theme if left blank. 88 | 89 | #### options.readme 90 | Type: `String`
91 | Default: `null` 92 | 93 | *Optional*. Path to a README file. When set, this file will be parsed with Markdown and used as the index page for the styleguide. 94 | 95 | #### options.highlight 96 | Type: `String`
97 | Default: `github` 98 | 99 | *Optional*. Choice of syntax highlighting style. Defaults to `github`, but other available options are: `docco`, `monokai`, `solarized-light`, `solarized-dark` or `xcode`. 100 | 101 | #### options.scripts 102 | Type: `Array`
103 | Default: `null` 104 | 105 | *Optional*. Array of file paths. The scripts will be linked with script tags with src attributes. Supports [globbing](http://gruntjs.com/configuring-tasks#globbing-patterns). Supports relative and absolute file paths (eg. `http://`, `https://`, `//` or even `file://`). 106 | 107 | If this option is set the default scripts won't be included, but you can include them again by adding `node_modules/sassdown/tasks/data/scripts.js` to the file list, or by copying and modifying that file. 108 | 109 | #### options.commentStart 110 | Type: `RegExp`
111 | Default: `/\/\*/` 112 | 113 | *Optional*. A regular expression to match beginning part of a comment block. Defaults to regular block comment (`/*`). 114 | 115 | #### options.commentEnd 116 | Type: `RegExp`
117 | Default: `/\*\//` 118 | 119 | *Optional*. A regular expression to match ending part of a comment block. Defaults to regular block comment (`*/`). 120 | 121 | #### options.excludeMissing 122 | Type: `Boolean`
123 | Default: `false` 124 | 125 | *Optional*. When set to true, Sassdown will ignore any files that do not contain matching or valid comment blocks. 126 | 127 | #### options.dryRun 128 | Type: `Boolean`
129 | Default: `false` 130 | 131 | *Optional*. When set to true, Sassdown will not generate any files, and will exit with status `1` if any files do not contain matching or valid comment blocks. 132 | 133 | ### Usage 134 | 135 | You will need to use an [expanded files object](http://gruntjs.com/configuring-tasks#building-the-files-object-dynamically), but here is roughly the minimum configuration required. 136 | ```js 137 | sassdown: { 138 | styleguide: { 139 | options: { 140 | assets: ['public/css/*.css'] 141 | }, 142 | files: [{ 143 | expand: true, 144 | cwd: 'src/sass', 145 | src: ['*.scss'], 146 | dest: 'public/styleguide/' 147 | }] 148 | } 149 | }, 150 | ``` 151 | 152 | On larger projects you may need to include additional assets and customise the output with a user theme, template and scripts. 153 | ```js 154 | sassdown: { 155 | styleguide: { 156 | options: { 157 | assets: [ 158 | 'public/css/**/*.min.css', 159 | 'public/js/*.min.js', 160 | 'http://use.typekit.net/sea5yvm.js', 161 | ], 162 | theme: 'src/styleguide/theme.css', 163 | template: 'src/styleguide/template.hbs', 164 | scripts: ['src/styleguide/*.js'], 165 | readme: 'src/assets/sass/readme.md', 166 | highlight: 'monokai', 167 | excludeMissing: true 168 | }, 169 | files: [{ 170 | expand: true, 171 | cwd: 'src/assets/sass', 172 | src: [ 173 | 'partials/**/*.{scss,sass}', 174 | 'modules/**/*.{scss,sass}' 175 | ], 176 | dest: 'public/styleguide/' 177 | }] 178 | } 179 | }, 180 | ``` 181 | 182 | # Markdown 183 | 184 | Sassdown uses [Markdown](https://github.com/chjj/marked) to parse any block comments in your Sass files. From these, it generates the text content in the styleguide. Any recognised code blocks will be rendered as HTML/SCSS source-result pairs. 185 | 186 | ## Structure 187 | 188 | You may use any Markdown-compatible [heading syntax](https://github.com/nopr/sassdown/issues/7) you like. You may use any common style of [block-comment syntax](https://github.com/nopr/sassdown/issues/12#issuecomment-28219982) you like. Code blocks may be fenced or indented (four spaces or one tab character). Below are several examples; each will be correctly parsed by Sassdown into identical output. 189 | 190 | ### Example .scss file 191 | 192 | ```scss 193 | /* 194 | 195 | Alerts 196 | ====== 197 | 198 | Creates an alert box notification using the `.alert-` prefix. The following options are available: 199 | 200 |
Success
201 |
Warning
202 |
Error
203 | 204 | */ 205 | @mixin alert($colour){ 206 | color: darken($colour, 50%); 207 | background: $colour; 208 | border-radius: 5px; 209 | margin-bottom: 1em; 210 | padding: 1em; 211 | } 212 | .alert-success { @include alert(#e2f3c1) } 213 | .alert-warning { @include alert(#fceabe) } 214 | .alert-error { @include alert(#ffdcdc) } 215 | ``` 216 | 217 | # Handlebars 218 | 219 | [Handlebars](http://handlebarsjs.com/) is a semantic templating syntax. Put simply, it allows you to output dynamic properties in HTML using `{{var}}` from a variety of data sources such as JSON. 220 | 221 | Sassdown uses Handlebars to output data from the [data objects](#data-objects) it creates. Your `.hbs` file specified in the `template` option may contain code that looks like this for example: 222 | 223 | ```html 224 | {{#each page.sections}} 225 |
226 | {{#if comment}} 227 |
{{{comment}}}
228 | {{/if}} 229 | {{#if result}} 230 |
{{{result}}}
231 | {{/if}} 232 | {{#if markup}} 233 |
{{{markup}}}
234 | {{/if}} 235 | {{#if styles}} 236 |
{{{styles}}}
237 | {{/if}} 238 |
239 | {{/each}} 240 | ``` 241 | 242 | ### Common partials 243 | 244 | Sassdown also provides a series of Handlebars **partials**, which can be used to output specific information on each page. These are: 245 | 246 | * `{{> root}}`
Outputs a path to the root directory of the styleguide, relative to whatever page you are on. 247 | 248 | * `{{> assets}}`
Outputs a set of `` or ` 63 | 64 | {{/if}} 65 | {{#if markup}} 66 |
{{{markup}}}
67 | {{/if}} 68 | {{#if styles}} 69 |
{{{styles}}}
70 | {{/if}} 71 | 72 | {{/each}} 73 | 74 | 75 | 76 | 77 | 84 | 85 | {{> theme}} 86 | {{> highlight}} 87 | {{> scripts}} 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /tasks/data/theme.css: -------------------------------------------------------------------------------- 1 | /* Resetting */ 2 | * { margin: 0; padding: 0; border: 0; font-size: inherit; } 3 | html { background: #e2e5dc } 4 | html, body { font-size: 100%; } 5 | 6 | /* Typography */ 7 | body { 8 | font: 14px/25px 'Calibri', Arial, sans-serif; 9 | color: black; 10 | padding: 2em; 11 | } 12 | a { text-decoration: none; color: black; } 13 | h1, h2, h3, h4, ul, ol, dl, p, blockquote { 14 | margin-bottom: 15px; 15 | } 16 | h1, h2, h3, h4, dt { 17 | font-weight: bold; 18 | } 19 | h1 { font-size: 25px; } 20 | h2 { font-size: 21px; } 21 | h3 { font-size: 17px; } 22 | h4 { font-size: 14px; } 23 | ul, ol { margin-left: 1em; list-style-position: outside; } 24 | 25 | /* Layout */ 26 | .gutter { 27 | width: 100%; 28 | max-width: 960px; 29 | overflow: hidden; 30 | margin: 0 auto; 31 | } 32 | #nav { 33 | float: left; 34 | width: 20%; 35 | min-width: 150px; 36 | margin-right: 25px; 37 | } 38 | #content { 39 | overflow: hidden; 40 | min-width: 400px; 41 | } 42 | 43 | /* Navigation */ 44 | #nav { 45 | margin-bottom: 25px; 46 | } 47 | #nav ul, 48 | #nav li, 49 | #nav a { 50 | display: block; 51 | } 52 | #nav ul { 53 | list-style: none; 54 | margin: 0 0 1em; 55 | } 56 | #nav h1 { 57 | border-bottom: 1px solid #d0d5c5; 58 | font-size: 14px; 59 | padding-bottom: 10px; 60 | margin: 20px 0 10px; 61 | } 62 | #nav .heading { 63 | margin-top: 1em; 64 | text-transform: uppercase; 65 | font-size: 12px; 66 | margin: 0; 67 | padding: 2px 5px; 68 | cursor: pointer; 69 | position: relative; 70 | border-radius: 4px; 71 | } 72 | #nav .heading:hover { 73 | background: #d0d5c5; 74 | } 75 | #nav .heading:after { 76 | content: "+"; 77 | position: absolute; 78 | top: 2px; 79 | right: 5px; 80 | } 81 | #nav .heading.is-open:after { 82 | content: "-"; 83 | } 84 | #nav ul ul, 85 | #nav ul .heading { 86 | margin-left: 20px; 87 | } 88 | #nav a { 89 | padding: 2px 5px; 90 | border-radius: 4px; 91 | } 92 | #nav a:hover, 93 | #nav a:focus { 94 | background: #d0d5c5; 95 | } 96 | #nav ul { display: none; } 97 | #nav ul.is-open { display: block; } 98 | 99 | /* Sections */ 100 | .section { 101 | background: #fff; 102 | box-shadow: 0 2px 3px rgba(0,0,0,0.2); 103 | margin-bottom: 20px; 104 | } 105 | .section .comment { 106 | padding: 20px 20px 5px; 107 | } 108 | .section .result { 109 | padding: 0 20px 20px; 110 | } 111 | .section .result iframe { 112 | display: block; 113 | overflow: hidden; 114 | position: relative; 115 | width: 100%; 116 | } 117 | .section .markup, 118 | .section .styles { 119 | border-top: 1px solid #ddd; 120 | background: #f7f7f7; 121 | position: relative; 122 | overflow-y: auto; 123 | max-height: 500px; 124 | } 125 | .section .markup pre, 126 | .section .styles pre { 127 | padding: 20px; 128 | } 129 | .section .markup:before, 130 | .section .styles:before { 131 | display: block; 132 | background: #eee; 133 | color: #ccc; 134 | padding: 0 20px; 135 | position: absolute; 136 | top: 0px; 137 | right: 0px; 138 | border-radius: 0 0 0 4px; 139 | } 140 | .section .markup:before { content: "Markup"; } 141 | .section .styles:before { content: "Styles"; } 142 | 143 | /* Code */ 144 | .section .comment code { 145 | color: #3fbead; 146 | } 147 | /* avoids conflict with highlight.js styles */ 148 | .section .comment pre code{ 149 | color: inherit; 150 | } 151 | code, 152 | pre { 153 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 154 | font-size: 13px; 155 | line-height: 20px; 156 | direction: ltr; 157 | text-align: left; 158 | white-space: pre-wrap; 159 | word-spacing: normal; 160 | margin: 0; 161 | -moz-tab-size: 4; 162 | -o-tab-size: 4; 163 | tab-size: 4; 164 | -webkit-hyphens: none; 165 | -moz-hyphens: none; 166 | -ms-hyphens: none; 167 | hyphens: none; 168 | } 169 | 170 | /** Small viewport adjustments **/ 171 | @media only screen and (max-width : 630px) { 172 | body { 173 | padding: 0; 174 | } 175 | 176 | #nav { 177 | float: none; 178 | margin-bottom: 0; 179 | width: 100%; 180 | } 181 | 182 | #nav a { 183 | border-radius: 0; 184 | } 185 | 186 | #nav h1 { 187 | font-size: 24px; 188 | line-height: 36px; 189 | margin: 0; 190 | padding-bottom: 0; 191 | } 192 | 193 | #nav .heading { 194 | border-radius: 0; 195 | font-size: 18px; 196 | padding: 5px; 197 | } 198 | 199 | #nav ul.is-open { 200 | background: #f7f7f7; 201 | margin-bottom: 0; 202 | padding-bottom: 10px; 203 | } 204 | 205 | #nav > ul { 206 | box-shadow: inset 0 2px 3px 0 rgba(0,0,0,0.2); 207 | border-bottom: 1px solid #d0d5c5; 208 | } 209 | 210 | #nav ul ul, #nav ul .heading { 211 | margin-left: 0px; 212 | } 213 | 214 | #nav .is-open li { 215 | font-size: 16px; 216 | padding-top: 4px; 217 | } 218 | 219 | #content { 220 | min-width: 0; 221 | } 222 | 223 | .section { 224 | box-shadow: inset 0 2px 3px 0 rgba(0,0,0,0.2), 0 2px 3px rgba(0,0,0,0.2) 225 | } 226 | 227 | .section .comment, 228 | .section .result { 229 | padding-left: 5px; 230 | padding-right: 5px; 231 | } 232 | } -------------------------------------------------------------------------------- /tasks/libs/sassdown.js: -------------------------------------------------------------------------------- 1 | /* 2 | sassdown 3 | github.com/nopr/sassdown 4 | ------------------------ 5 | Copyright (c) 2013 Jesper Hills, contributors 6 | Some rights reserved 7 | */ 8 | 'use strict'; 9 | 10 | // Exposed global objects 11 | // ====================== 12 | var Sassdown, grunt; 13 | 14 | // Required Node modules 15 | // ===================== 16 | var fs = require('fs'); 17 | var junk = require('junk'); 18 | var path = require('path'); 19 | var hljs = require('highlight.js'); 20 | var cssmin = require('cssmin'); 21 | var markdown = require('marked'); 22 | var Handlebars = require('handlebars'); 23 | var AllHtmlEntities = require('html-entities').AllHtmlEntities; 24 | var entities = new AllHtmlEntities(); 25 | 26 | // Quick utility functions 27 | // ======================= 28 | function warning (message) { return grunt.verbose.warn(message); } 29 | function unspace (string) { return string.replace(/\r\n|\n| /g,''); } 30 | function datapath (filename) { return path.resolve(__dirname, '..', 'data', filename); } 31 | function sourcify (section, file) { 32 | return file.data.split(section)[1].split(Sassdown.config.option.commentStart)[0]; 33 | } 34 | function normalize (comment) { 35 | comment = comment.replace(Sassdown.config.option.commentStart, ''); 36 | comment = comment.replace(Sassdown.config.option.commentEnd, ''); 37 | comment = comment.trim().replace(/^\*/, '').replace(/\n \* |\n \*|\n /g, '\n').replace(/\n /g, '\n '); 38 | if (!comment.match('```') && comment.match(' ')) { 39 | comment = comment.replace(/ |```\n /, '```\n '); 40 | comment = comment.replace(/\n /g, '\n').replace(/\n /g, '\n'); 41 | comment += '\n```'; 42 | } 43 | return comment; 44 | } 45 | 46 | // Constants 47 | // ========================= 48 | 49 | var REGEXP_PATH_HAS_SCHEME = /^((https?|file):)?\/\//; 50 | 51 | // Exported Sassdown methods 52 | // ========================= 53 | module.exports.init = function (_grunt) { 54 | grunt = _grunt; 55 | Sassdown = this; 56 | }; 57 | 58 | module.exports.registerHandlebarsHelpers = function () { 59 | var helpers = Sassdown.getFileList(Sassdown.config.option.handlebarsHelpers); 60 | helpers.forEach(function (helper) { 61 | helper = path.resolve(__dirname, path.relative(__dirname, process.cwd()), helper); 62 | if (fs.existsSync(helper)) { 63 | helper = require(helper); 64 | if (typeof helper.register === 'function') { helper.register(Handlebars); } 65 | else if (typeof helper === 'function') { helper(Handlebars); } 66 | } 67 | }); 68 | }; 69 | 70 | module.exports.template = function () { 71 | // Check for existence of user defined template 72 | Sassdown.checkfor('template', datapath('template.hbs')); 73 | // Return Sassdown.config.template object 74 | Sassdown.config.template = { 75 | html: Handlebars.compile(grunt.file.read(Sassdown.config.option.template)), 76 | assets: null 77 | }; 78 | }; 79 | 80 | module.exports.theme = function () { 81 | // Check for existence of user defined theme 82 | Sassdown.checkfor('theme', datapath('theme.css')); 83 | // Read file using grunt 84 | var minify = grunt.file.read(Sassdown.config.option.theme); 85 | // Assign theme to Handlebars partial; minify this 86 | Handlebars.registerPartial('theme', ''); 87 | }; 88 | 89 | module.exports.highlight = function () { 90 | // Check for existence of user defined highlight style 91 | Sassdown.checkfor('highlight', 'github'); 92 | // Read file using grunt 93 | var minify = grunt.file.read(datapath('highlight.'+Sassdown.config.option.highlight+'.css')); 94 | // Assign highlight style to Handlebars partial; minify this 95 | Handlebars.registerPartial('highlight', ''); 96 | }; 97 | 98 | module.exports.scripts = function () { 99 | // Check for existence of user defined scripts 100 | Sassdown.checkfor('scripts', [datapath('scripts.js')]); 101 | var fileList = Sassdown.getFileList(Sassdown.config.option.scripts); 102 | // Process each script to get the partial to use 103 | var partial = fileList.map(function (file) { 104 | var scriptSrc; 105 | if (file.match(REGEXP_PATH_HAS_SCHEME)) { 106 | // The file is hosted elsewhere so just link to it 107 | scriptSrc = file; 108 | } else { 109 | // Get the filename and paths 110 | var fileName = file.split('/').pop(); 111 | var src = path.resolve(process.cwd(), file); 112 | var dest = path.resolve(Sassdown.config.root, fileName); 113 | if (grunt.file.exists(src)) { 114 | // Copy the script file to the styleguide root dir 115 | grunt.file.write(dest, grunt.file.read(src)); 116 | // Link to the copied script relative to the styleguide root 117 | scriptSrc = '{{> root}}/' + fileName; 118 | } 119 | } 120 | return scriptSrc ? '\n' : ''; 121 | }).join(''); 122 | // Register the partial 123 | Handlebars.registerPartial('scripts', partial); 124 | }; 125 | 126 | module.exports.checkfor = function (requirement, defaults) { 127 | // If the requirement isn't met 128 | if (!Sassdown.config.option[requirement]) { 129 | warning('User ' + requirement + ' not specified. Using default.'); 130 | Sassdown.config.option[requirement] = defaults; 131 | } 132 | }; 133 | 134 | module.exports.getFileList = function (assets) { 135 | var fileList = []; 136 | // Expand through matches in options and include both 137 | // internal and external files into the array 138 | if (assets && typeof assets.forEach === 'function') { 139 | assets.forEach( function (asset) { 140 | grunt.file.expand(asset).forEach( function (file) { 141 | fileList.push(file); 142 | grunt.verbose.write(file+'...').ok(); 143 | }); 144 | if (asset.match(REGEXP_PATH_HAS_SCHEME)) { 145 | fileList.push(asset); 146 | grunt.verbose.write(asset+'...').ok(); 147 | } 148 | }); 149 | } 150 | return fileList; 151 | }; 152 | 153 | module.exports.assets = function () { 154 | // Check if we added includes option 155 | if (!Sassdown.config.option.assets) { 156 | warning('User assets not specified. Styleguide will be unstyled!'); 157 | } else { 158 | Sassdown.config.assets = Sassdown.getFileList(Sassdown.config.option.assets); 159 | } 160 | }; 161 | 162 | module.exports.include = function (file, dest) { 163 | // Output 164 | var output; 165 | // If this file is not external, build a local relative path 166 | if (!file.match(REGEXP_PATH_HAS_SCHEME)) { file = path.relative(dest, file); } 167 | // Preserve correct path escaping for