├── .editorconfig ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── README.mdtex ├── docs └── images │ ├── flowchart.pdf │ ├── flowchart.png │ ├── flowchart.svg │ ├── fracdrhodt-0-0d95010981.svg │ ├── fracdrhodt-0-64ee686b6f.svg │ ├── fracdrhodt-0-6deddd4e7c.svg │ ├── fracdrhodt-0-d0893f9a61.svg │ ├── fracdrhodt-0-ebec560456.svg │ ├── fracdrhodt-0-f9ff87b518.svg │ ├── latex-35667f6f3d.svg │ ├── latex-41db537fd1.svg │ ├── latex-7d8af78a9d.svg │ ├── latex-8ae45909b6.svg │ ├── latex-ab99620889.svg │ ├── latex-d44b8c0ee5.svg │ ├── nabla-cdot-vecu-0-19e52303bc.svg │ ├── nabla-cdot-vecu-0-3ec1472ff7.svg │ ├── nabla-cdot-vecu-0-5ecc63754f.svg │ ├── nabla-cdot-vecu-0-c3529d4cca.svg │ ├── nabla-cdot-vecu-0-e420b67a9e.svg │ └── nabla-cdot-vecu-0-efbf661771.svg ├── example ├── gulpfile.js ├── images │ ├── y-frac1x-43d4730472.png │ └── y-frac1x-968f2fff1f.png ├── sample.md └── sample.mdtex ├── gulpfile.js ├── lib ├── compute-filename.js ├── equation-list.js ├── equation.js └── index.js ├── package.json └── test ├── compute-filename.js ├── fixtures ├── index.js └── sample.mdtex ├── gulp-markdown-equations.js └── test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | 3 | # Logs 4 | logs 5 | *.log 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | # Compiled binary addons (http://nodejs.org/api/addons.html) 22 | build/Release 23 | build 24 | 25 | # Dependency directory 26 | # Deployed apps should consider commenting this line out: 27 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git 28 | node_modules 29 | 30 | **/.DS_Store 31 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.12" 4 | - "0.11" 5 | - "0.10" 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Ricky Reusser 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gulp-markdown-equations 2 | [![Build Status](https://travis-ci.org/rreusser/gulp-markdown-equations.svg)](https://travis-ci.org/rreusser/gulp-markdown-equations) [![npm version](https://badge.fury.io/js/gulp-markdown-equations.svg)](http://badge.fury.io/js/gulp-markdown-equations) [![Dependency Status](https://david-dm.org/rreusser/gulp-markdown-equations.svg)](https://david-dm.org/rreusser/gulp-markdown-equations) 3 | 4 | A gulp plugin that makes it easy to replace markdown latex equations with rendered images 5 | 6 | 7 | ## Introduction 8 | 9 | This module exposes the tools necessary to to transform undefined equations in a markdown document into rendered raster or vector images. It uses the [transform-markdown-mathmode](https://www.npmjs.com/package/transform-markdown-mathmode) node module to locate and transform equations and reconnects with the gulp pipeline after the results have been rendered to complete the transformation using information from the result. 10 | 11 | This means you can just mix undefined into your markdown document. For example, 12 | 13 | ```markdown 14 | It handles inline equations like $\nabla \cdot \vec{u} = 0$ and display equations like $$\frac{D\rho}{Dt} = 0.$$ 15 | ``` 16 | 17 | gets transformed into: 18 | 19 | It handles inline equations like undefined and display equations like

undefined

20 | 21 | Of course it's gulp plugin though, so that means you can really do whatever you want with it! 22 | 23 | 24 | ## Installation 25 | 26 | To install, run: 27 | 28 | ```bash 29 | $ npm install gulp-markdown-equations 30 | ``` 31 | 32 | ## Example 33 | 34 | The following is a gulp task that locates equations in markdown, renders them, and lets you do whatever you want with the result! First things first, here's the data flow: 35 | 36 |

37 | 38 | ```javascript 39 | var gulp = require('gulp') 40 | , mdEqs = require('gulp-markdown-equations') 41 | , tap = require('gulp-tap') 42 | , filter = require('gulp-filter') 43 | , latex = require('gulp-latex') 44 | , pdftocairo = require('gulp-pdftocairo') 45 | 46 | 47 | gulp.task('mdtex',function() { 48 | 49 | var texFilter = filter('*.tex') 50 | var mdFilter = filter('*.md') 51 | 52 | // Instantiate the transform and set some defaults: 53 | var transform = mdEqs({ 54 | defaults: { 55 | display: { margin: '1pt' }, 56 | inline: {margin: '1pt'} 57 | } 58 | }) 59 | 60 | return gulp.src('*.mdtex') 61 | 62 | // Locate equations in the markdown stream and pass them as separate 63 | // *.tex file objects: 64 | .pipe(transform) 65 | 66 | // Filter to operate on *.tex documents: 67 | .pipe(texFilter) 68 | 69 | // Render the equations to pdf: 70 | .pipe(latex()) 71 | 72 | // Convert the pdf equations to png: 73 | .pipe(pdftocairo({format: 'png'})) 74 | 75 | // Send them to the images folder: 76 | .pipe(gulp.dest('images')) 77 | 78 | // Match the output images up with the closures that are still waiting 79 | // on their callbacks from the `.pipe(transform)` step above. That means 80 | // we can use metadata from the image output all the way back up in 81 | // the original transform. Sweet! 82 | .pipe(tap(function(file) { 83 | transform.completeSync(file,function() { 84 | var img = ''+this.alt+'' 86 | return this.display ? '

'+img+'

' : img 87 | }) 88 | })) 89 | 90 | // Restore and then change filters to operate on the *.md document: 91 | .pipe(texFilter.restore()).pipe(mdFilter) 92 | 93 | // Output in the current directory: 94 | .pipe(gulp.dest('./')) 95 | }) 96 | ``` 97 | 98 | The task is run with: 99 | 100 | ```bash 101 | $ gulp mdtex 102 | ``` 103 | 104 | 105 | ## API 106 | 107 | #### `require('gulp-markdown-mathmode')( [options] )` 108 | Create a gulp-compatible buffered file stream transform. Options are: 109 | 110 | - `defaults`: Parameters that get added to each equation object and passed to the undefined templator. A good example is setting default margins that can be overridden later on with config variables added to a specific equation. 111 | - `inline`: an associative array of key/value pairs for inline equations, into which preprocessed parameters are merged 112 | - `display`: an associative array of key/value pairs for displaystyle equations, into which preprocessed parameters are merged 113 | - `preprocessor`: a function that extracts equation-specific parameters. In other words, if your equation is `$[margin=10pt] y=x$`, the preprocessor extracts `{margin: '10pt'}`. Default preprocessor is [square-parameters](https://github.com/rreusser/square-parameters). If `null`, preprocessor step is skipped. See below for more details. 114 | - `templator`: a function of format `function( tex, params ) {}` that receives the preprocessed `\LaTeX` and parameters and returns a templated undefined document. If null, the original tex string will be passed through unmodified. Default templator is [equation-to-latex](https://github.com/rreusser/equation-to-latex). See below for more detail. 115 | 116 | 117 | ### Methods: 118 | 119 | #### `.completeSync( file, callback )` 120 | Once a file has been processed, you *must* tap into the stream and complete the markdown transformation. See above for an example. `callback` is executed with `this` set to an object containing equation metadata. The value you return is inserted into the markdown document. 121 | 122 | The metadata contains all information about the processed equation, including the fully processed file at this point in the pipeline. If image dimensions are available, they will be added. For example: 123 | 124 | ```javascript 125 | { display: false, 126 | inline: true, 127 | margin: '0 1pt 0pt 0pt', 128 | path: 'docs/images/latex-d3a0aa2938.png', 129 | height: 38, 130 | width: 104, 131 | equation: 132 | { tex: '\\LaTeX', 133 | alt: '\LaTeX', 134 | templated: '\\documentclass[10pt] ... \end{document}\n', 135 | file: > } } 136 | ``` 137 | 138 | #### `.complete( file, callback )` 139 | An asynchronous version of `.complete()`. The format of the callback is `function( resultCallback ) { … }`. Once you've computed the value, you may pass the result to `resultCallback` (e.g. `resultCallback('')`) and it will be inserted into the markdown document. 140 | 141 | 142 | 143 | ## Internals 144 | 145 | ### Preprocessor 146 | 147 | The preprocessor is just a function that operates on the content of an equation before it's inserted in the LaTeX template in order to extract equation-specific config. The extracted parameters are merged into this equation's parameters. The default preprocessor [square-parameters](https://github.com/rreusser/square-parameters). Sample usage: 148 | 149 | ```javascript 150 | var pre = require('square-parameters')` 151 | 152 | pre("[name=parabola][margin=10pt 0pt]y=x^2") 153 | // => { params: { name: "parabola", margin: "10pt 0pt" }, content: "y=x^2" } 154 | ``` 155 | 156 | ### Templator 157 | 158 | The templator's job is to insert undefined into a full undefined document. It receives configuration first from preprocessed parameters, then from the display/inline defaults passed as transform options. By default, the delimiters (`$$...$$` or `$...$`) add parameters `{display:true, inline: false}` or `{display:false, inline:true}`, respectively. The default templator is [equation-to-latex](https://github.com/rreusser/equation-to-latex), but in general it only must be a function of form: 159 | 160 | ```javscript 161 | function templator( content, parameters ) { 162 | // your logic 163 | return "\\documentclass... " 164 | } 165 | ``` 166 | 167 | 168 | ## Credits 169 | 170 | (c) 2015 Ricky Reusser. MIT License -------------------------------------------------------------------------------- /README.mdtex: -------------------------------------------------------------------------------- 1 | # gulp-markdown-equations 2 | [![Build Status](https://travis-ci.org/rreusser/gulp-markdown-equations.svg)](https://travis-ci.org/rreusser/gulp-markdown-equations) [![npm version](https://badge.fury.io/js/gulp-markdown-equations.svg)](http://badge.fury.io/js/gulp-markdown-equations) [![Dependency Status](https://david-dm.org/rreusser/gulp-markdown-equations.svg)](https://david-dm.org/rreusser/gulp-markdown-equations) 3 | 4 | A gulp plugin that makes it easy to replace markdown latex equations with rendered images 5 | 6 | 7 | ## Introduction 8 | 9 | This module exposes the tools necessary to to transform $\LaTeX$ equations in a markdown document into rendered raster or vector images. It uses the [transform-markdown-mathmode](https://www.npmjs.com/package/transform-markdown-mathmode) node module to locate and transform equations and reconnects with the gulp pipeline after the results have been rendered to complete the transformation using information from the result. 10 | 11 | This means you can just mix $\LaTeX$ into your markdown document. For example, 12 | 13 | ```markdown 14 | It handles inline equations like $\nabla \cdot \vec{u} = 0$ and display equations like $$\frac{D\rho}{Dt} = 0.$$ 15 | ``` 16 | 17 | gets transformed into: 18 | 19 | It handles inline equations like $\nabla \cdot \vec{u} = 0$ and display equations like $$\frac{D\rho}{Dt} = 0.$$ 20 | 21 | Of course it's gulp plugin though, so that means you can really do whatever you want with it! 22 | 23 | 24 | ## Installation 25 | 26 | To install, run: 27 | 28 | ```bash 29 | $ npm install gulp-markdown-equations 30 | ``` 31 | 32 | ## Example 33 | 34 | The following is a gulp task that locates equations in markdown, renders them, and lets you do whatever you want with the result! First things first, here's the data flow: 35 | 36 |

37 | 38 | ```javascript 39 | var gulp = require('gulp') 40 | , mdEqs = require('gulp-markdown-equations') 41 | , tap = require('gulp-tap') 42 | , filter = require('gulp-filter') 43 | , latex = require('gulp-latex') 44 | , pdftocairo = require('gulp-pdftocairo') 45 | 46 | 47 | gulp.task('mdtex',function() { 48 | 49 | var texFilter = filter('*.tex') 50 | var mdFilter = filter('*.md') 51 | 52 | // Instantiate the transform and set some defaults: 53 | var transform = mdEqs({ 54 | defaults: { 55 | display: { margin: '1pt' }, 56 | inline: {margin: '1pt'} 57 | } 58 | }) 59 | 60 | return gulp.src('*.mdtex') 61 | 62 | // Locate equations in the markdown stream and pass them as separate 63 | // *.tex file objects: 64 | .pipe(transform) 65 | 66 | // Filter to operate on *.tex documents: 67 | .pipe(texFilter) 68 | 69 | // Render the equations to pdf: 70 | .pipe(latex()) 71 | 72 | // Convert the pdf equations to png: 73 | .pipe(pdftocairo({format: 'png'})) 74 | 75 | // Send them to the images folder: 76 | .pipe(gulp.dest('images')) 77 | 78 | // Match the output images up with the closures that are still waiting 79 | // on their callbacks from the `.pipe(transform)` step above. That means 80 | // we can use metadata from the image output all the way back up in 81 | // the original transform. Sweet! 82 | .pipe(tap(function(file) { 83 | transform.completeSync(file,function() { 84 | var img = ''+this.alt+'' 86 | return this.display ? '

'+img+'

' : img 87 | }) 88 | })) 89 | 90 | // Restore and then change filters to operate on the *.md document: 91 | .pipe(texFilter.restore()).pipe(mdFilter) 92 | 93 | // Output in the current directory: 94 | .pipe(gulp.dest('./')) 95 | }) 96 | ``` 97 | 98 | The task is run with: 99 | 100 | ```bash 101 | $ gulp mdtex 102 | ``` 103 | 104 | 105 | ## API 106 | 107 | #### `require('gulp-markdown-mathmode')( [options] )` 108 | Create a gulp-compatible buffered file stream transform. Options are: 109 | 110 | - `defaults`: Parameters that get added to each equation object and passed to the $\LaTeX$ templator. A good example is setting default margins that can be overridden later on with config variables added to a specific equation. 111 | - `inline`: an associative array of key/value pairs for inline equations, into which preprocessed parameters are merged 112 | - `display`: an associative array of key/value pairs for displaystyle equations, into which preprocessed parameters are merged 113 | - `preprocessor`: a function that extracts equation-specific parameters. In other words, if your equation is `$[margin=10pt] y=x$`, the preprocessor extracts `{margin: '10pt'}`. Default preprocessor is [square-parameters](https://github.com/rreusser/square-parameters). If `null`, preprocessor step is skipped. See below for more details. 114 | - `templator`: a function of format `function( tex, params ) {}` that receives the preprocessed `\LaTeX` and parameters and returns a templated $\LaTeX$ document. If null, the original tex string will be passed through unmodified. Default templator is [equation-to-latex](https://github.com/rreusser/equation-to-latex). See below for more detail. 115 | 116 | 117 | ### Methods: 118 | 119 | #### `.completeSync( file, callback )` 120 | Once a file has been processed, you *must* tap into the stream and complete the markdown transformation. See above for an example. `callback` is executed with `this` set to an object containing equation metadata. The value you return is inserted into the markdown document. 121 | 122 | The metadata contains all information about the processed equation, including the fully processed file at this point in the pipeline. If image dimensions are available, they will be added. For example: 123 | 124 | ```javascript 125 | { display: false, 126 | inline: true, 127 | margin: '0 1pt 0pt 0pt', 128 | path: 'docs/images/latex-d3a0aa2938.png', 129 | height: 38, 130 | width: 104, 131 | equation: 132 | { tex: '\\LaTeX', 133 | alt: '\LaTeX', 134 | templated: '\\documentclass[10pt] ... \end{document}\n', 135 | file: > } } 136 | ``` 137 | 138 | #### `.complete( file, callback )` 139 | An asynchronous version of `.complete()`. The format of the callback is `function( resultCallback ) { … }`. Once you've computed the value, you may pass the result to `resultCallback` (e.g. `resultCallback('')`) and it will be inserted into the markdown document. 140 | 141 | 142 | 143 | ## Internals 144 | 145 | ### Preprocessor 146 | 147 | The preprocessor is just a function that operates on the content of an equation before it's inserted in the LaTeX template in order to extract equation-specific config. The extracted parameters are merged into this equation's parameters. The default preprocessor [square-parameters](https://github.com/rreusser/square-parameters). Sample usage: 148 | 149 | ```javascript 150 | var pre = require('square-parameters')` 151 | 152 | pre("[name=parabola][margin=10pt 0pt]y=x^2") 153 | // => { params: { name: "parabola", margin: "10pt 0pt" }, content: "y=x^2" } 154 | ``` 155 | 156 | ### Templator 157 | 158 | The templator's job is to insert $\LaTeX$ into a full $\LaTeX$ document. It receives configuration first from preprocessed parameters, then from the display/inline defaults passed as transform options. By default, the delimiters (`$$...$$` or `$...$`) add parameters `{display:true, inline: false}` or `{display:false, inline:true}`, respectively. The default templator is [equation-to-latex](https://github.com/rreusser/equation-to-latex), but in general it only must be a function of form: 159 | 160 | ```javscript 161 | function templator( content, parameters ) { 162 | // your logic 163 | return "\\documentclass... " 164 | } 165 | ``` 166 | 167 | 168 | ## Credits 169 | 170 | (c) 2015 Ricky Reusser. MIT License 171 | -------------------------------------------------------------------------------- /docs/images/flowchart.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rreusser/gulp-markdown-equations/eaf814633118f5bff3fb7e66eef453c377952ed7/docs/images/flowchart.pdf -------------------------------------------------------------------------------- /docs/images/flowchart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rreusser/gulp-markdown-equations/eaf814633118f5bff3fb7e66eef453c377952ed7/docs/images/flowchart.png -------------------------------------------------------------------------------- /docs/images/flowchart.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | -------------------------------------------------------------------------------- /docs/images/fracdrhodt-0-0d95010981.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /docs/images/fracdrhodt-0-64ee686b6f.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/images/fracdrhodt-0-6deddd4e7c.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /docs/images/fracdrhodt-0-d0893f9a61.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/images/fracdrhodt-0-ebec560456.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /docs/images/fracdrhodt-0-f9ff87b518.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/images/latex-35667f6f3d.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /docs/images/latex-41db537fd1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /docs/images/latex-7d8af78a9d.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/images/latex-8ae45909b6.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /docs/images/latex-ab99620889.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /docs/images/latex-d44b8c0ee5.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /docs/images/nabla-cdot-vecu-0-19e52303bc.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /docs/images/nabla-cdot-vecu-0-3ec1472ff7.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /docs/images/nabla-cdot-vecu-0-5ecc63754f.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /docs/images/nabla-cdot-vecu-0-c3529d4cca.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /docs/images/nabla-cdot-vecu-0-e420b67a9e.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /docs/images/nabla-cdot-vecu-0-efbf661771.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /example/gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var gulp = require('gulp') 4 | , eqSub = require('../lib') 5 | , tap = require('gulp-tap') 6 | , filter = require('gulp-filter') 7 | , latex = require('gulp-latex') 8 | , pdftocairo = require('gulp-pdftocairo') 9 | 10 | 11 | gulp.task('mdtex',function() { 12 | 13 | var texFilter = filter('*.tex') 14 | var mdFilter = filter('*.md') 15 | var sub = eqSub({ 16 | defaults: { 17 | display: { margin: '1pt' }, 18 | inline: {margin: '1pt 3pt 1pt 1pt'} 19 | } 20 | }) 21 | 22 | return gulp.src('*.mdtex') 23 | .pipe(sub) 24 | 25 | .pipe(texFilter) 26 | .pipe(latex()) 27 | .pipe(pdftocairo({format: 'png', resolution:288})) 28 | .pipe(gulp.dest('images')) 29 | .pipe(tap(function(file) { 30 | sub.complete(file,function(cb,meta) { 31 | var img = ''+meta.alt+'' 32 | meta.display ? cb('

'+img+'

') : cb(img) 33 | }) 34 | })) 35 | .pipe(texFilter.restore()) 36 | 37 | .pipe(mdFilter) 38 | .pipe(gulp.dest('./')) 39 | .pipe(mdFilter.restore()) 40 | 41 | }) 42 | 43 | -------------------------------------------------------------------------------- /example/images/y-frac1x-43d4730472.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rreusser/gulp-markdown-equations/eaf814633118f5bff3fb7e66eef453c377952ed7/example/images/y-frac1x-43d4730472.png -------------------------------------------------------------------------------- /example/images/y-frac1x-968f2fff1f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rreusser/gulp-markdown-equations/eaf814633118f5bff3fb7e66eef453c377952ed7/example/images/y-frac1x-968f2fff1f.png -------------------------------------------------------------------------------- /example/sample.md: -------------------------------------------------------------------------------- 1 | An inline hyperbola looks like y = \frac{1}{x}, and a display hyperbola looks like
y = \frac{1}{x}
-------------------------------------------------------------------------------- /example/sample.mdtex: -------------------------------------------------------------------------------- 1 | An inline hyperbola looks like $y = \frac{1}{x}$, and a display hyperbola looks like $$y = \frac{1}{x}$$ 2 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var gulp = require('gulp') 4 | , eqSub = require('./lib') 5 | , tap = require('gulp-tap') 6 | , filter = require('gulp-filter') 7 | , latex = require('gulp-latex') 8 | , pdftocairo = require('gulp-pdftocairo') 9 | , path = require('path') 10 | 11 | function giturl(rel) { 12 | return 'https://rawgit.com/rreusser/gulp-markdown-equations/master/' + rel 13 | //return 'https://cdn.rawgit.com/rreusser/gulp-markdown-equations/master/' + rel 14 | //return rel 15 | } 16 | 17 | gulp.task('mdtex',function() { 18 | 19 | var texFilter = filter('*.tex') 20 | var mdFilter = filter('*.md') 21 | var sub = eqSub({ 22 | defaults: { 23 | display: { margin: '1pt 5pt', fontSize: '12pt' }, 24 | inline: { margin: '0 1pt 0pt 0pt', fontSize: '12pt' } 25 | } 26 | }) 27 | 28 | return gulp.src('*.mdtex') 29 | .pipe(sub) 30 | 31 | .pipe(texFilter) 32 | .pipe(latex()) 33 | .pipe(pdftocairo({format: 'svg'})) 34 | .pipe(gulp.dest('docs/images')) 35 | .pipe(tap(function(file) { 36 | sub.completeSync(file,function(cb) { 37 | var img = ''+this.alt+'' 38 | return this.display ? '

'+img+'

' : img 39 | }) 40 | })) 41 | .pipe(texFilter.restore()) 42 | 43 | .pipe(mdFilter) 44 | .pipe(gulp.dest('./')) 45 | .pipe(mdFilter.restore()) 46 | 47 | }) 48 | 49 | -------------------------------------------------------------------------------- /lib/compute-filename.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var parameterize = require('parameterize') 4 | , md5 = require('md5') 5 | 6 | module.exports = computeFilename 7 | 8 | function computeFilename( content, params ) { 9 | 10 | var basename = params.name || parameterize(content).slice(0,45) 11 | var hash = md5(JSON.stringify(params)+content).slice(0,10) 12 | 13 | return ( basename + '-' + hash + '.tex') 14 | 15 | } 16 | -------------------------------------------------------------------------------- /lib/equation-list.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var sizeOf = require('image-size') 4 | , path = require('path') 5 | , gutil = require('gulp-util') 6 | , extend = require('util-extend') 7 | 8 | module.exports = EquationLookup 9 | 10 | function EquationLookup() { 11 | this.equations = {} 12 | } 13 | 14 | EquationLookup.prototype.store = function(equation) { 15 | var key = equation.file.basename 16 | var eqset = this.equations[key] = this.equations[key] || [] 17 | eqset.push( equation ) 18 | 19 | return equation 20 | } 21 | 22 | EquationLookup.prototype.completeSync = function(file, callback) { 23 | 24 | this.complete(file, function(result) { 25 | result( callback.bind(this)() ) 26 | }) 27 | } 28 | 29 | EquationLookup.prototype.complete = function(file, callback) { 30 | var key = path.basename(gutil.replaceExtension(file.path, '.tex')) 31 | var eqset = this.equations[key] = this.equations[key] || [] 32 | var equation = eqset.pop(equation) 33 | 34 | var meta = {} 35 | 36 | try { 37 | var dimensions = sizeOf(file.contents) 38 | meta.width = dimensions.width 39 | meta.height = dimensions.height 40 | } catch(e) { } 41 | 42 | meta.path = path.relative(file.cwd, file.path) 43 | 44 | meta = extend(equation.params,meta) 45 | meta.equation = equation 46 | meta.alt = equation.alt 47 | meta.templated = equation.templated 48 | 49 | callback.bind(meta)(function(result) { 50 | equation.callback(result) 51 | }) 52 | } 53 | -------------------------------------------------------------------------------- /lib/equation.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var extend = require('util-extend') 4 | , computeFilename = require('./compute-filename') 5 | , entities = require('entities') 6 | , gutil = require('gulp-util') 7 | , File = require('vinyl') 8 | , assert = require('assert') 9 | 10 | module.exports = Equation 11 | 12 | function Equation( input, params, callback, config ) { 13 | 14 | if( config.preprocessor === null ) { 15 | 16 | // Skip the preprocessor: 17 | this.tex = input 18 | this.params = params 19 | 20 | } else { 21 | var preprocessed = config.preprocessor(input) 22 | 23 | // Set the LaTeX content: 24 | this.tex = preprocessed.content 25 | 26 | // Extend the input params by params from the preprocessor: 27 | this.params = extend(params, preprocessed.params) 28 | } 29 | 30 | this.params.usepackages = (this.params.usepackages || []).concat( config.usepackages || [] ) 31 | 32 | // Set an alt tag unless provided: 33 | this.alt = this.params.alt || entities.encodeHTML( this.tex.trim() ) 34 | 35 | // Stick the raw eq inside a latex template: 36 | this.templated = config.templator === null ? this.tex : config.templator(this.tex, this.params) 37 | 38 | // Compute the filename if not already provided as this.params.name 39 | var filename = computeFilename( this.tex, this.params ) 40 | 41 | // Create a vinyl file. Extension (*.tex) comes from this.filename 42 | this.file = new File({ 43 | cwd: "./", 44 | base: "./", 45 | path: './' + filename, 46 | contents: new Buffer(this.templated) 47 | }) 48 | 49 | this.callback = function() { 50 | this.callback = function(){ 51 | throw new gutil.PluginError('gulp-markdown-equations', 'Equation transform callback executed more than once') 52 | } 53 | return callback.apply(this,arguments) 54 | }.bind(this) 55 | } 56 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var through = require('through2') 4 | , tmm = require('transform-markdown-mathmode') 5 | , gutil = require('gulp-util') 6 | , rawBody = require('raw-body') 7 | , extend = require('util-extend') 8 | , path = require('path') 9 | 10 | , defaultTemplator = require('equation-to-latex') 11 | , defaultPreprocessor = require('square-parameters') 12 | , Equation = require('./equation') 13 | , EquationLookup = require('./equation-list') 14 | 15 | module.exports = gulpMarkdownEquations 16 | 17 | function gulpMarkdownEquations( opts ) { 18 | 19 | 20 | // Generate the expected defaults if not provided: 21 | var config = opts || {} 22 | config.defaults = config.defaults || {} 23 | config.defaults.inline = config.defaults.inline || {} 24 | config.defaults.display = config.defaults.display || {} 25 | 26 | // Set default config: 27 | if( config.templator === undefined ) config.templator = defaultTemplator 28 | if( config.preprocessor === undefined ) config.preprocessor = defaultPreprocessor 29 | 30 | // Create a lookup table so we can match up files with their equations: 31 | var equations = new EquationLookup() 32 | 33 | 34 | // Return a through transform: 35 | var transform = through.obj(function(file,enc,cb) { 36 | 37 | // Pass null files through: 38 | if( file.isNull() ) { 39 | cb(null,file) 40 | return 41 | } 42 | 43 | // No reason we couldn't support streams, really, except LaTeX is pretty 44 | // fundamentally stream-unfriendly 45 | if( file.isStream() ) { 46 | throw new gutil.PluginError('gulp-markdown-equations','Streams not supported!') 47 | } 48 | 49 | 50 | // Locate equations and store the callback in a new equation for later use, 51 | // then push the vinyl file object for this equation down the file stream: 52 | var parser = tmm({ 53 | 54 | display: function(tex,cb) { 55 | var params = extend({display:true,inline:false},config.defaults.display) 56 | this.push( equations.store(new Equation( tex, params, cb, config )).file ) 57 | }.bind(this), 58 | 59 | 60 | inline: function(tex,cb) { 61 | var params = extend({display:false,inline:true},config.defaults.inline) 62 | this.push( equations.store(new Equation( tex, params, cb, config )).file ) 63 | }.bind(this) 64 | 65 | }) 66 | 67 | 68 | // Wait for the parser to complete, then rewrite the original file from 69 | // *.mdtex to *.md and push it down the file stream 70 | rawBody(parser,function(err,data) { 71 | file.contents = data 72 | file.path = gutil.replaceExtension( file.path, '.md' ) 73 | this.push(file) 74 | gutil.log("Pushed '" + gutil.colors.cyan(path.relative( file.base, file.path)) + "' to file stream") 75 | cb() 76 | }.bind(this)) 77 | 78 | 79 | // Oh yeah, actually need to send the file to the parser stream: 80 | file.pipe(parser) 81 | }) 82 | 83 | 84 | // Alias this function so that we can call it from the transform object itself: 85 | transform.complete = equations.complete.bind(equations) 86 | transform.completeSync = equations.completeSync.bind(equations) 87 | 88 | 89 | return transform 90 | } 91 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp-markdown-equations", 3 | "version": "1.2.3", 4 | "description": "A gulp plugin that makes it easy to replace markdown latex equations with rendered images", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "test": "node test/test.js | tap-spec" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git://github.com/rreusser/gulp-markdown-equations.git" 12 | }, 13 | "keywords": [ 14 | "markdown", 15 | "equations", 16 | "latex", 17 | "gulpplugin", 18 | "mathmode" 19 | ], 20 | "author": "Ricky Reusser", 21 | "license": "MIT", 22 | "devDependencies": { 23 | "glob": "^5.0.14", 24 | "gulp-filter": "^2.0.2", 25 | "gulp-latex": "^1.0.1", 26 | "gulp-pdftocairo": "^0.2.0", 27 | "gulp-tap": "^0.1.3", 28 | "tap-spec": "^4.0.2", 29 | "tape": "^4.0.1" 30 | }, 31 | "dependencies": { 32 | "entities": "^1.1.1", 33 | "equation-to-latex": "^2.0.1", 34 | "gulp-util": "^3.0.6", 35 | "handlebars": "^3.0.3", 36 | "image-size": "^0.3.5", 37 | "md5": "^2.0.0", 38 | "parameterize": "0.0.7", 39 | "raw-body": "^2.1.2", 40 | "square-parameters": "^1.0.2", 41 | "through2": "^2.0.0", 42 | "transform-markdown-mathmode": "^0.1.4", 43 | "util-extend": "^1.0.1", 44 | "vinyl": "^0.5.0" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/compute-filename.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var name = require('../lib/compute-filename') 4 | , test = require('tape') 5 | 6 | test('compute-filename: parameterizes the tex', function(t) { 7 | t.assert( name("y=x^2", {}).match(/yx2/) ) 8 | t.end() 9 | }) 10 | 11 | test('compute-filename: uses name over parameterized equation if name is provided', function(t) { 12 | t.assert( name("y=x^2", {name: 'equation'}).match(/equation/) ) 13 | t.assert( ! name("y=x^2", {name: 'equation'}).match(/yx2/) ) 14 | t.end() 15 | }) 16 | 17 | test('compute-filename: appends a ten-character hash', function(t) { 18 | t.assert( name("y=x^2", {}).match(/-[a-zA-Z0-9]{10}\./) ) 19 | t.end() 20 | }) 21 | 22 | test('compute-filename: distinct equations have distinct filenames', function(t) { 23 | var n1 = name("y=x^2", {}) 24 | var n2 = name("y=x^3", {}) 25 | t.assert( n1 !== n2 ) 26 | t.end() 27 | }) 28 | 29 | test('compute-filename: distinct parameters yield distinct filenames', function(t) { 30 | var n1 = name("y=x^2", {margin: '10pt'}) 31 | var n2 = name("y=x^2", {margin: '11pt'}) 32 | t.assert( n1 !== n2 ) 33 | t.end() 34 | }) 35 | 36 | test('compute-filename: appends ".tex"', function(t) { 37 | var n1 = name("y=x^2", {margin: '10pt'}) 38 | t.assert( n1.match(/\.tex$/) ) 39 | t.end() 40 | }) 41 | -------------------------------------------------------------------------------- /test/fixtures/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var glob = require('glob') 4 | , path = require('path') 5 | , fs = require('fs') 6 | 7 | var files = glob.sync(__dirname + "/**/*") 8 | var fixtures = {} 9 | 10 | files.forEach(function(f) { 11 | if( f === __filename ) return 12 | var rel = path.relative( __dirname, f ) 13 | 14 | fixtures[rel] = fs.readFileSync(f) 15 | }) 16 | 17 | module.exports = fixtures 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /test/fixtures/sample.mdtex: -------------------------------------------------------------------------------- 1 | An inline hyperbola looks like $[foo=bar]y = \frac{1}{x}$, and a display hyperbola looks like $$[foo=bar]y = \frac{1}{x}$$ 2 | -------------------------------------------------------------------------------- /test/gulp-markdown-equations.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var test = require('tape') 4 | , gulpMarkdownEquations = require('../lib') 5 | , tap = require('gulp-tap') 6 | , File = require('vinyl') 7 | , fixtures = require('./fixtures') 8 | , path = require('path') 9 | 10 | //test.createStream().pipe(require('tap-spec')()).pipe(process.stdout); 11 | 12 | function testBehavior(input, options, eqCallback, mdCallback) { 13 | 14 | var mdeq = gulpMarkdownEquations(options) 15 | 16 | mdeq.write(new File({ path: 'sample.mdtex', contents: input })) 17 | mdeq.end() 18 | 19 | mdeq.pipe(tap(function(file) { 20 | if( file.contents === null ) return 21 | if( file.basename.match(/\.md$/) ) { 22 | if( mdCallback ) mdCallback.bind(file)() 23 | } else { 24 | if( eqCallback ) { 25 | mdeq.complete(file,eqCallback) 26 | } else { 27 | mdeq.complete(file,function(cb) { cb() }) 28 | } 29 | } 30 | })) 31 | 32 | return mdeq 33 | } 34 | 35 | 36 | test('gulp-markdown-equations: a null templator passes tex through',function(t) { 37 | testBehavior(fixtures['sample.mdtex'], { templator: null }, function(cb) { 38 | t.equal( this.foo, 'bar', 'gets preprocessed config' ) 39 | t.equal( this.templated, 'y = \\frac{1}{x}', 'tex is not templated' ) 40 | cb('test') 41 | }).on('end',t.end) 42 | }) 43 | 44 | test('gulp-markdown-equations: tex gets escaped and added as alt tag',function(t) { 45 | testBehavior(fixtures['sample.mdtex'], { templator: null }, function(cb) { 46 | t.equal( this.alt, 'y = \frac{1}{x}', 'tex shows up in alt tag' ) 47 | cb('test') 48 | }).on('end',t.end) 49 | }) 50 | 51 | test('gulp-markdown-equations: a null preprocessor skips params',function(t) { 52 | testBehavior(fixtures['sample.mdtex'], { preprocessor: null }, function(cb) { 53 | t.equal( this.foo, undefined, 'params are not parsed' ) 54 | cb('test') 55 | }).on('end',t.end) 56 | }) 57 | 58 | test('gulp-markdown-equations: number of files is correct',function(t) { 59 | var numEq = 0, numMd = 0 60 | testBehavior( fixtures['sample.mdtex'], {}, function(cb) { 61 | numEq++ 62 | cb() 63 | }, function() { 64 | numMd++ 65 | }).on('end',function() { 66 | t.equal(numEq, 2, 'got two equations') 67 | t.equal(numMd, 1, 'got one markdown file') 68 | t.end() 69 | }) 70 | 71 | }) 72 | 73 | test('gulp-markdown-equations: default options get passed',function(t) { 74 | 75 | var options = { 76 | defaults: { 77 | inline: { beep: 'boop' }, 78 | display: { baz: 'bop' } 79 | } 80 | } 81 | 82 | testBehavior( fixtures['sample.mdtex'], options, function(cb) { 83 | if( this.display ) { 84 | t.equal(this.baz, 'bop', 'receives default display mode config') 85 | } else { 86 | t.equal(this.beep, 'boop', 'receives default display mode config') 87 | } 88 | cb() 89 | }).on('end',t.end) 90 | }) 91 | 92 | test('gulp-markdown-equations: correct result is inserted',function(t) { 93 | var numEq = 0, md 94 | testBehavior( fixtures['sample.mdtex'], {}, function(cb) { 95 | numEq++ 96 | cb('equation-number-' + numEq) 97 | },function() { 98 | md = this.contents.toString() 99 | t.assert( md.match(/equation-number-1/), 'markdown contains the result of the first equation') 100 | t.assert( md.match(/equation-number-2/), 'markdown contains the result of the second equation') 101 | }).on('end',t.end) 102 | }) 103 | 104 | test('gulp-markdown-equations: has the correct filename',function(t) { 105 | testBehavior( fixtures['sample.mdtex'], {}, function(cb) { 106 | t.assert( this.equation.file.basename.match(/^y-frac1x-[a-z0-9]{10}.tex/), 'has the correct filename') 107 | cb() 108 | }).on('end',t.end) 109 | }) 110 | 111 | test('gulp-markdown-equations: tex is extracted',function(t) { 112 | testBehavior( fixtures['sample.mdtex'], {}, function(cb) { 113 | t.equal( this.equation.tex, 'y = \\frac{1}{x}', 'has the tex extracted') 114 | cb() 115 | }).on('end',t.end) 116 | }) 117 | 118 | test('gulp-markdown-equations: preprocesses and inserts params',function(t) { 119 | testBehavior( fixtures['sample.mdtex'], {}, function(cb) { 120 | t.equal( this.foo, 'bar', 'parsed and received its params') 121 | cb() 122 | }).on('end',t.end) 123 | }) 124 | 125 | test('gulp-markdown-equations: latex templating',function(t) { 126 | testBehavior( fixtures['sample.mdtex'], {}, function(cb) { 127 | t.assert( this.equation.templated.match(/^\\documentclass/), 'got inserted into the latex template') 128 | cb() 129 | }).on('end',t.end) 130 | }) 131 | 132 | test('gulp-markdown-equations: fails if callback executed more than once',function(t) { 133 | testBehavior( fixtures['sample.mdtex'], {}, function(cb) { 134 | cb() 135 | t.throws(cb, Error, 'throws error on second execution') 136 | }).on('end',t.end) 137 | }) 138 | 139 | 140 | test('gulp-markdown-equations: completeSync',function(t) { 141 | t.plan(4) 142 | var eqNum = 0 143 | var mdeq = gulpMarkdownEquations(), md 144 | 145 | mdeq.write(new File({ path: 'sample.mdtex', contents: fixtures['sample.mdtex'] })) 146 | mdeq.end() 147 | mdeq.on('end',t.end) 148 | 149 | mdeq.pipe(tap(function(file) { 150 | if( file.extname !== '.md' ) { 151 | eqNum++ 152 | mdeq.completeSync(file,function() { 153 | t.equal( this.foo, 'bar', 'this is set to the equation' ) 154 | return 'inserted-equation-' + eqNum 155 | }) 156 | } else { 157 | md = file.contents.toString() 158 | t.assert( md.match(/inserted-equation-1/m), 'Markdown contains inserted equations') 159 | t.assert( md.match(/inserted-equation-2/m), 'Markdown contains inserted equations') 160 | } 161 | })) 162 | }) 163 | 164 | test('passes a null file',function(t) { 165 | var numEq = 0, numMd = 0, md 166 | 167 | testBehavior( null, {}, function(cb) { 168 | numEq++ 169 | }, function() { 170 | numMd++ 171 | }).on('end',function() { 172 | t.equal(numEq, 0, 'got no *.tex') 173 | t.equal(numMd, 0, 'got no *.md') 174 | t.end() 175 | }) 176 | }) 177 | 178 | test('gulp-markdown-equations: usepackages',function(t) { 179 | testBehavior( fixtures['sample.mdtex'], { 180 | usepackages: ['package1', 'package2'] 181 | }, function(cb) { 182 | t.assert( this.equation.templated.match( /usepackage\{\s*package1\s*\}/), 'contains \\usepackage{package1}' ) 183 | t.assert( this.equation.templated.match( /usepackage\{\s*package2\s*\}/), 'contains \\usepackage{package2}' ) 184 | cb() 185 | }).on('end',t.end) 186 | }) 187 | 188 | 189 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | require('./compute-filename') 4 | require('./gulp-markdown-equations') 5 | --------------------------------------------------------------------------------