├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── convert.md ├── documentation.md ├── index.js ├── lprc.js ├── package-lock.json ├── package.json ├── project.md ├── setup.md ├── test.js └── tests ├── blackwhitehats ├── .gitignore ├── blackwhitehats.md ├── canonical │ └── build │ │ └── hats.js └── original │ └── blackwhitehats.md ├── cheerio ├── .gitignore ├── canonical │ ├── build │ │ └── out.html │ └── out.test └── cheers.md ├── cinnamon ├── .gitignore ├── canonical │ └── build │ │ ├── cinnamon.css │ │ ├── cinnamon.html │ │ ├── cinnamon.js │ │ └── cinnamon_emb.js ├── cinnamon.md └── original │ └── cinnamon.md ├── csv ├── .gitignore ├── canonical │ └── build │ │ └── out.csv └── project.md ├── date ├── .gitignore ├── canonical │ └── build │ │ └── out.txt └── project.md ├── fence ├── .gitignore ├── canonical │ └── build │ │ └── fence.txt ├── fence.md └── original │ └── fence.md ├── first ├── .gitignore ├── canonical │ └── build │ │ └── first.txt ├── first.md ├── first.txt ├── second.md └── toread.txt ├── fizzbuzz ├── .gitignore ├── canonical │ └── build │ │ └── fizzbuzz.js ├── fizzbuzz.md └── original │ └── fizzbuzz.md ├── he ├── .gitignore ├── canonical │ └── build │ │ └── out.txt └── project.md ├── integrated ├── .gitignore ├── canonical │ └── build │ │ ├── dev │ │ └── index.html │ │ └── prod │ │ └── index.html ├── integrated.md ├── lprc.js └── lprc.md ├── jstidy ├── .gitignore ├── canonical │ └── build │ │ └── jstidytest.js ├── jstidy.md └── original │ └── jstidy.md ├── lodash ├── .gitignore ├── canonical │ └── build │ │ └── out.txt └── project.md ├── logs ├── .gitignore ├── canonical │ └── build │ │ ├── logs.htm │ │ └── logs.html ├── canonicalold │ └── build │ │ ├── logs.htm │ │ └── logs.html ├── logs.md ├── lprc.js └── original │ └── logs.md ├── lprc.js ├── matrix ├── .gitignore ├── canonical │ └── build │ │ └── out.txt └── matrix.md ├── primes ├── .gitignore ├── canonical │ └── out.test ├── original │ └── primes.md └── primes.md ├── sample ├── .gitignore ├── canonical │ └── build │ │ └── count.js ├── original │ └── sample.md └── sample.md ├── template ├── .gitignore ├── canonical │ └── build │ │ ├── first.html │ │ ├── out.txt │ │ └── snippet ├── original │ ├── simpletemp.md │ └── templating.md ├── simpletemp.md └── templating.md └── testpro ├── .gitignore ├── canonical └── build │ ├── just-talk │ ├── out.txt │ └── testpro.js ├── original ├── period.md └── testpro.md ├── period.md └── testpro.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /build 3 | /cache 4 | /.checksum 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | build 2 | .checksum 3 | cache 4 | tests 5 | test.js 6 | .travis.yml 7 | lprc.js 8 | *.md 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "node" 4 | - "4.0" 5 | sudo: false 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2020 James Taylor 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # literate-programming [](https://travis-ci.org/jostylr/literate-programming) 2 | 3 | 4 | This is the fat command-line client for 5 | [literate-programming-lib](https://github.com/jostylr/literate-programming-lib). 6 | It contains the full functionality for literate programming, including useful 7 | commands such as jshint included in it. For a thin client, 8 | check out 9 | [litpro](https://github.com/jostylr/litpro) 10 | 11 | 12 | Full documentation: [Literate Programming, MD: How to Treat and Prevent Software Project Mess](https://leanpub.com/literate-programming-md) 13 | 14 | This is not done being fully baked, hence v0.9. But this does represent a 15 | significant break from 0.8.4. You can take a look at convert.md for some 16 | observations of mine as I converted from the old version to the new. 17 | 18 | Install using `npm install literate-programming` 19 | 20 | Usage is `./node_modules/bin/litpro file` and it has some command flags. 21 | 22 | If you want a global install so that you just need to write 23 | `literate-programming` then use `npm install -g literate-programming`. 24 | 25 | The library has a full listing of the syntax, commands, and directives. Here 26 | we list the flags and new commands and directives. 27 | 28 | ## Example usage 29 | 30 | Save the following code to file `project.md` and run `literate-programming project.md`. 31 | 32 | # Welcome 33 | 34 | So you want to make a literate program? Let's have a program that outputs 35 | all numbers between 1 to 10. 36 | 37 | Let's save it in file count.js 38 | 39 | [count.js](#Structure "save: | jshint") 40 | 41 | ## Structure 42 | 43 | We have some intial setup. Then we will generate the array of numbers. We 44 | end with outputting the numbers. 45 | 46 | var numarr = [], start=1, end = 11, step = 1; 47 | 48 | _"Loop" 49 | 50 | _"Output" 51 | 52 | ## Output 53 | 54 | At this point, we have the array of numbers. Now we can join them with a 55 | comma and output that to the console. 56 | 57 | console.log("The numbers are: ", numarr.join(", ") ); 58 | 59 | ## Loop 60 | 61 | Set the loop up and push the numbers onto it. 62 | 63 | var i; 64 | for (i = start; i < end; i += step) { 65 | numarr.push(i); 66 | } 67 | 68 | ## Documentation 69 | 70 | For more information, see the 71 | [documentation book](https://leanpub.com/literate-programming-md) 72 | which is free to read online or available for purchase as a PDF. 73 | 74 | Some particularly useful syntax sections are: 75 | 76 | * [command-line flags](https://leanpub.com/literate-programming-md/read#leanpub-auto-command-line-1) 77 | * [directives](https://leanpub.com/literate-programming-md/read#leanpub-auto-directives-1) 78 | * [commands](https://leanpub.com/literate-programming-md/read#leanpub-auto-commands-1) 79 | * [subcommands](https://leanpub.com/literate-programming-md/read#leanpub-auto-subcommands-1) 80 | 81 | 82 | 83 | ## Use and Security 84 | 85 | It is inherently unsecure to compile literate 86 | program documents. No effort has been made to make it secure. Compiling a 87 | literate program using this program is equivalent to running arbitrary code on 88 | your computer. Only compile from trusted sources, i.e., use the same 89 | precautions as running a node module. 90 | 91 | 92 | ## LICENSE 93 | 94 | [MIT-LICENSE](https://github.com/jostylr/literate-programming/blob/master/LICENSE-MIT) 95 | -------------------------------------------------------------------------------- /convert.md: -------------------------------------------------------------------------------- 1 | Are you coming from the old version? There are syntax differences. Here we 2 | list a few tips to help the process. 3 | 4 | Old way: `| clean raw"` takes the raw text of the section and makes it 5 | cleaned up New way: New version does not see the commentary text. So we need 6 | to act on the document itself. We also don't want the blocks to get evaluated 7 | which automatically happens in this version. So we place directives to turn 8 | off that feature: `[off](# "block:")` turns off block reading, 9 | `[on](# "block:")` turns it back on. We then need to do the snippet cutting: 10 | `| raw ## README, !---- | sub \n\ #, \n# |trim` 11 | Raw says to take the raw document and 12 | cut between the two given pieces. We still shift the headings to not be read 13 | as heading and the sub puts it back. 14 | 15 | Moving towards a conventional setup of setup.md containing project files to 16 | be put in home directory (package.json and lprc.js for example) using `litpro 17 | -b . setup.md` then one can do `npm install` and then do `litpro` to process 18 | `project.md` which then calls the other files. 19 | 20 | Convert substitute(...) to sub. In VIM: `:%s/substitute(\([^)]\+\))/sub \1/g` 21 | 22 | Boilerplate. Old syntax, rather wonky, was to use * to indicate a template to 23 | fill in. New version has the compile command. So instead of `float tests*test 24 | template` we would have `_"test template | compile float tests"` All the minor 25 | sections in the template are required, but they can be empty. Instead of the * 26 | in front of the minor, escape the underscore. `_"*:code"` --> `\_":code"` 27 | While the asterisk notation is, in some sense, nicer, the async nature of the 28 | new version made it problematic. 29 | 30 | Beware of h5 and h6 headers; they fill a new role. Reduce the number of hashes. 31 | 32 | Minor blocks should now best be in the form `[name]()` They can appear 33 | anywhere; it is the form that matters. Alternatively, `[name](# ": | ...")` 34 | can be used if there are pipe transformations desired. The key is the leading 35 | colon and having a name. 36 | 37 | The old setup had minors that could have an extension between `: |` and that 38 | would become part of the name. That is not present in the new one. It was not 39 | really needed. Also minors can be referred to in the hash -- just use the 40 | colon as one would, e.g. `[logs.htm](#structure "Save: main")` becomes 41 | `[logs.htm](#structure:main "Save: ")` 42 | 43 | Blocks cannot refer to each other without problems as they will mutually wait 44 | for each other. 45 | 46 | Fencing of code blocks follows commonmark syntax. Be careful about lists too. 47 | 48 | To eval code directly, one can use `| eval _"some code"`. The incoming text is 49 | in the variable `text` and that variable is what the outgoing text is recorded 50 | as. 51 | 52 | We no longer can evaluate the blocks in terms of where we are in the command 53 | input stage. This was always confusing, anyway. Instead use backslashes and 54 | the compile command. 55 | 56 | To access the arguments called by the command line, one can do 57 | `doc.parent.stdin[whatever property]` and one can create whatever property by 58 | doing `- z prop:val` 59 | 60 | The two things above allow one to have the literate program directly doing 61 | computations instead of just creating a script that can be called. No real 62 | reason for this, I suppose, but hey, it works. 63 | 64 | No replacement for 65 | 66 | ``` 67 | \_"*:expected|heading" 68 | ## Heading 69 | 70 | function () { 71 | return this.hblock.heading.split("*")[0]; 72 | } 73 | 74 | [heading](#heading "define: command | | now") 75 | ``` 76 | 77 | 78 | ## Break with previous versions 79 | 80 | This is a complete rewrite. The syntax is simplified so that only the ``_`code 81 | block| function | functionn` `` syntax is needed. Leave off the code block to 82 | just start using functions (leave the pipe). The code block in that syntax 83 | need not be a code block, but could be a user-defined variable name, 84 | 85 | Another break in the syntax is the switch link. This was a link that was on a 86 | line by itself. It was a kind of heading, one that would make a quick separate 87 | code block under the same heading. I find it convenient. But in trying to 88 | match the general parsing of markdown programs, I am moving towards using a 89 | professional markdown parser that would make it difficult to recognize the 90 | positioning of a link, but trivial to parse one. So the switch link will be a 91 | link whose title quote starts with a colon. So an empty directive. It can 92 | still be positioned as always. Also can implement it so that if the 93 | parenthetical is completely empty, then that is a switch. I noticed that that 94 | is what I often do. 95 | 96 | For header purposes, a link's square bracket portion will be returned to the 97 | surrounding block. 98 | 99 | Also headers will have a way to be modified based on their levels. 100 | I have never used levels 5 and 6, for example. 101 | As an example, one could have level 5 headers for tests, docs, and examples, 102 | that could then be compiled and run bey selecting those headers. Not sure yet. 103 | 104 | Also, there is no tracking of the non-significant text. So for example, raw 105 | will not work in the same way. It was always a bit of a hack and now it will 106 | be more so. There can be easily a plugin that will search for the heading and 107 | cut the rest, etc. 108 | 109 | Multiple substitute cycles are no longer supported. I always found it hard to 110 | reason about it and it greatly simplifies the code. If you need that 111 | functionality, it probably is workable with substitutes and the variable 112 | storage introduced. 113 | 114 | The compiled blocks are stored as variables. We can store arbitrary variable 115 | names and so potentially can conflict with block names. You have been warned. 116 | It is all "global" scope though you can use syntax to kind of scope it. Well, 117 | actually we are scoped to the documents, that is `docname::..` gives another 118 | scope which the var setting respects. 119 | 120 | Another break is that block names need to match. There is the main block which 121 | has no minor associated with it and then there are the minors of the block. If 122 | you want the minor commands on the main, then start the code line with `[](# 123 | ":|...")` where the colon is there to indicate a minor directive, but with no 124 | name and no extension, this will signal the main block. Note that this will 125 | overwrite whatever was in the main code block, if anything. Once a block 126 | (minor or not) is switched from, it cannot be added to later. Trust me, this 127 | is a good thing. 128 | -------------------------------------------------------------------------------- /documentation.md: -------------------------------------------------------------------------------- 1 | * **jshint** This takes the input and runs it through JSHint. The command 2 | is of the form 3 | `js stuff | jshint options, globals, shortname, print clean`. 4 | 5 | * The options is an object that corresponds to the [options that JShint 6 | accepts](http://jshint.com/docs/options/); you can use a subcommand to 7 | create the options object if you like. Default is unused:true, else is 8 | their defaults. 9 | * Globals is an array of global 10 | names; if they can be written over, pass in `name:true` instead of 11 | `name`. 12 | * Shortname is the shortname to present in understanding what is being 13 | jshinted. Otherwise, it does its best to give you a cryptic but 14 | informative name. 15 | * If the fourth argument is a boolean, `t()` or `f()` will do it, then 16 | that toggles whether to print the message that it all went smoothly or 17 | not, respectively. The default is to not print it. 18 | * You can override the defaults repeatedly by modifying the 19 | `Folder.plugins.jshint` object with the names: `options`, `globals`, and 20 | `clean`. 21 | * **md** This takes the input as markdown and puts out html. The first 22 | argument is an optional string naming the renderer to use. The other 23 | arguments should be booleans, namely, `f()`, if one does not want 24 | preprocessing/post to occur. The default preprocessors, in order, are 25 | literate programming subs and math subs rendering to katex. 26 | 27 | To create a renderer, you can use Folder.plugins.md.req as the markdoan 28 | object and then render it per the instructions (an options object 29 | `req(options).use(...)`. This is all best done in the lprc.js file. 30 | Store the rendered under the preferred name in plugins.md. 31 | 32 | See the logs test directory and its lprc.js. 33 | * **cheerio** This gives access to the cheerio module, a lightweight node 34 | version of jQuery-esque without the overhead of jsdom. It can't do 35 | everything, but it does most things: 36 | [cheeriojs](https://github.com/cheeriojs/cheerio). To use, the incoming 37 | text is the html doc to modify, the first argument is the selector, the 38 | second the method, and then the arguments to the method, e.g., 39 | `somehtml | cheerio h2.title, .text, Hello there!` 40 | * **ch-replace** This is a convenience method for cheerio. This will use 41 | the first argument as a selector and the second argument as a 42 | html replacement. 43 | * **postcss** This takes incoming text and runs it through postcss. To do 44 | something useful, you need to have the arguments be the commands to use. 45 | At the moment, the only one shipping with this is autoprefixer, but 46 | others are likely to be added (minimizers and fixers, in particular). 47 | You can add them yourself by, in lprcs.js, saying (installing cssnano as 48 | example) 49 | `Folder.plugins.postcss[cssnano] = require('cssnano');` and ensuring 50 | that the cssnano module is installed in npm. 51 | * **tidy** This uses [js-beautify](https://www.npmjs.com/package/js-beautify) 52 | The first argument is the type: js, css, or html. The second argument are 53 | options that get merged with the defaults. The js has a default of 54 | `indent_size` of 4 and `jslint_happy` true. An unrecognized first argument 55 | (or none) will default to js. 56 | * **minify** The first argument says the type of minifier: js, css, and 57 | html. js is the default if the first argument is not realized. The 58 | second argument is an object of options that get passed in. This uses 59 | uglify-js, clean-css, and 60 | [html-minifier](https://www.npmjs.com/package/html-minifier), 61 | respectively. For css, the 62 | second argument can be a boolean indicating whether to pass on the 63 | results object (if true, `t()` ) or just the css output text (default). 64 | * **date** `... |date method||date, arg1, arg2, ..`. This uses the 65 | [date-fns](https://date-fns.org/) library. Any valid function in that 66 | should work fine. There are a few scenarios for getting a date going: 67 | 68 | * ` date object | date method, arg1, ...` will apply the method of 69 | `datefns` to the date as the leading argument and use the rest of the 70 | arguments to fill it in. Alias: `date object | -method arg1, ..` 71 | * `date string | date method, arg1, ...` will apply the method to the 72 | date parsed by `datefns.parse` Alias: `date string | -method arg1, ..` 73 | * `| date method, arg1, ...` will apply the method to today's date. 74 | Alias `| -method arg1, ...` 75 | * `| date` Just returns today's date. No alias 76 | * `| date string date, method, args1, ...` will parse the string date 77 | and apply the method. No alias. 78 | * Note that there is also a subcommand `date` that will generate today's 79 | date or a date object based on the input. 80 | 81 | Recommended form: `| date string | -method arg1, ...| ...` 82 | * **csv-parse/transform/stringify** 83 | This is an interface into the node-csv library. It does the three 84 | named methods. 85 | The first argument can be an object of options except for transform in 86 | which the options are second and the first argument is a function to 87 | execute on each row. 88 | See [node-csv](http://csv.adaltas.com/) for more details. 89 | 90 | If you need to use the streaming power, you should access the full power 91 | of it using `Folder.requres.csv` and take a look at, for example, [so](http://stackoverflow.com/questions/23080413/nodejs-reading-csv-file) 92 | * **lodash** The incoming data is the first 93 | argument into the function while the first argument is the method name. 94 | The other arguments are what they are. 95 | 96 | Example: ` abc | - pad 8, 0` 97 | * **html-encode/decode/qescape** This is an interface to the 98 | [he](https://github.com/mathiasbynens/he) library. It encodes and 99 | decodes all named html entities. There is also a simple escape function, 100 | that includes quotes which the lit-native html-escape does not. 101 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /*global process, require */ 4 | 5 | var mod = require('literate-programming-cli'); 6 | 7 | var args = mod.opts.parse(); 8 | 9 | args.build = args.build.map(function (el) { 10 | if (el.slice(-1) === "/") { 11 | return el.slice(0, -1); 12 | } else { 13 | return el; 14 | } 15 | }); 16 | 17 | var z = {}; 18 | args.other.forEach(function (arg) { 19 | var pair = arg.split(":"); 20 | if (pair.length === 1) { 21 | args[pair[0]] = true; 22 | } else if (pair.length === 2) { 23 | args[pair[0]] = pair[1]; 24 | } else { 25 | args[pair[0]] = pair.slice(1); 26 | } 27 | z[pair[0]] = args[pair[0]]; 28 | }); 29 | 30 | //console.warn("!!", args); 31 | 32 | var Folder = mod.Folder; 33 | 34 | Folder.inputs = args; 35 | Folder.z = z; 36 | 37 | var merge = Folder.requires.merge; 38 | if (! Folder.requires) { 39 | Folder.requires = {}; 40 | } 41 | var typeit = Folder.requires.typeit; 42 | 43 | var jshint = require('jshint').JSHINT; 44 | Folder.plugins.jshint = { 45 | options: {unused: true}, 46 | globals: [], 47 | clean : false 48 | } ; 49 | Folder.sync("jshint", function (input, args, name) { 50 | 51 | var doc = this; 52 | var options, globals; 53 | 54 | var log = [], err, i, lines, line, logcopy, 55 | globhash, ind, shortname; 56 | 57 | var plug = doc.plugins.jshint; 58 | 59 | options = merge(true, plug.options, args[0] || {}); 60 | 61 | globals = plug.globals.concat(args[1] || []); 62 | 63 | if (args[2]) { 64 | shortname = args[2]; 65 | } else { 66 | shortname = name.slice(name.lastIndexOf(":")+1, 67 | name.indexOf(doc.colon.v, ind) ); 68 | } 69 | 70 | globhash = {}; 71 | globals.forEach( function (el) { 72 | var bits; 73 | bits = el.trim().split(":"); 74 | bits[1] = bits[1] === "true"; 75 | globhash[bits[0].trim()] = bits[1]; 76 | }); 77 | 78 | jshint(input, options, globhash); 79 | 80 | 81 | var data = jshint.data(); 82 | 83 | 84 | lines = input.split("\n"); 85 | for (i = 0; i < jshint.errors.length; i += 1) { 86 | err = jshint.errors[i]; 87 | if (!err) {continue;} 88 | if (err.reason.indexOf("is defined but never used.") !== -1) { 89 | continue; //this is covered elsewhere. 90 | } 91 | line = lines[err.line-1]; 92 | if (line.trim().length < 4) { 93 | line = "\n---\n" + lines.slice(err.line-2, err.line+1).join("\n") + 94 | "\n---\n"; 95 | } 96 | log.push("E "+ err.line+","+err.character+": "+err.reason + 97 | " "+ line.trim()); 98 | } 99 | if (data.hasOwnProperty("implieds") ) { 100 | for (i = 0; i < data.implieds.length; i += 1) { 101 | err = data.implieds[i]; 102 | log.push("Implied Gobal "+ err.line+": "+err.name + 103 | " "+ lines[err.line[0]-1].trim()); 104 | } 105 | } 106 | if (data.hasOwnProperty("unused") ) { 107 | for (i = 0; i < data.unused.length; i += 1) { 108 | err = data.unused[i]; 109 | log.push("Unused "+ err.line+": "+err.name + 110 | " "+ lines[err.line-1].trim()); 111 | } 112 | } 113 | 114 | if (log.length > 0 ) { 115 | logcopy = log.slice(); 116 | logcopy.unshift(shortname, "jshint Report"); 117 | doc.log.apply(doc, logcopy); 118 | } else { 119 | if (args[3] || plug.clean) { 120 | doc.log(shortname, "jshint clean"); 121 | } 122 | } 123 | 124 | return input; 125 | }); 126 | 127 | var pug = require('pug'); 128 | Folder.plugins.pug = {pretty:true}; 129 | 130 | Folder.sync("pug" , function (code, args) { 131 | var options = merge(true, this.plugins.pug, args); 132 | return pug.render(code, options); 133 | }); 134 | 135 | var mdit = require('markdown-it'); 136 | 137 | var mddefault = { 138 | html:true, 139 | linkify:true 140 | }; 141 | 142 | 143 | Folder.plugins.md = { 144 | req : mdit, 145 | def: mdit(mddefault), 146 | options: mddefault, 147 | prepost : [function litsub (code) { 148 | var snips = []; 149 | var masklit = function (match) { 150 | snips.push(match); 151 | return ""; 152 | }; 153 | 154 | var rep = function (match, num) { 155 | return snips[parseInt(num, 10)]; 156 | }; 157 | 158 | var undo = function (html) { 159 | var reg = /<\!\-\-LITSNIP(\d+)\-\->/g; 160 | return html.replace(reg, rep); 161 | }; 162 | 163 | var lit = /(?:\\|\\\d*)?\_+(\"[^"]+\"|\`[^`]+\`|\'[^']\')/g; 164 | code = code.replace(lit, masklit); 165 | return [code, undo]; 166 | }, function mathsub (code) { 167 | var snips = []; 168 | var maskinline = function (match, ignore, math) { 169 | snips.push("\\(" + math + "\\)" ); 170 | return ""; 171 | }; 172 | 173 | var maskdisp = function (match, ignore, math) { 174 | snips.push("\\[" + math + "\\]" ); 175 | return ""; 176 | }; 177 | 178 | var rep = function (match, num) { 179 | return snips[parseInt(num, 10)]; 180 | }; 181 | 182 | var undo = function (html) { 183 | var reg = /<\!\-\-MATHSNIP(\d+)\-\->/g; 184 | return html.replace(reg, rep); 185 | }; 186 | 187 | var inline = /(\`+)([^`]+)\1\$/g; 188 | code = code.replace(inline, maskinline); 189 | var display = /(\`+)\$([^`]+)\1/g; 190 | code = code.replace(display, maskdisp); 191 | return [code, undo]; 192 | }] 193 | }; 194 | 195 | Folder.sync( "md", function (code, args) { 196 | var plug = this.plugins.md; 197 | var html, rend; 198 | rend = plug[args[0]] || plug.def; 199 | var post = []; 200 | plug.prepost.forEach(function (el, ind) { 201 | var temp; 202 | if (args[ind+1] !== false) { 203 | temp = el(code); 204 | post.push(temp[1]); 205 | code = temp[0]; 206 | } 207 | }); 208 | html = rend.render(code); 209 | post.forEach(function (el) { 210 | html = el(html); 211 | }); 212 | return html; 213 | }); 214 | 215 | var cheerio = require('cheerio'); 216 | Folder.plugins.cheerio = { 217 | req: cheerio 218 | }; 219 | 220 | Folder.sync( "cheerio" , function(code, args) { 221 | var selector = args.shift(); 222 | var method = args.shift(); 223 | var $ = cheerio.load(code); 224 | var el$ = $(selector); 225 | try { 226 | el$[method].apply(el$, args); 227 | return $.html(); 228 | } catch (e) { 229 | this.log("Problem with cheerio: " + selector + "," + 230 | method + "," + args.join(",")); 231 | return code; 232 | } 233 | }); 234 | Folder.sync( "ch-replace" , function(code, args) { 235 | var selector, replacement; 236 | var n = args.length; 237 | var $ = cheerio.load(code); 238 | var i; 239 | for (i = 0; i < n; i += 2) { 240 | selector = args[i]; 241 | replacement = args[i+1]; 242 | $(selector).replaceWith(replacement); 243 | } 244 | return $.html(); 245 | }); 246 | 247 | var postcss = require('postcss'); 248 | 249 | Folder.commands.postcss = function (input, args, name) { 250 | var doc = this; 251 | var pc = doc.plugins.postcss; 252 | var cmds = []; 253 | if ( (typeof input !== "string") || (input === '') ) { 254 | doc.gcd.emit("text ready:" + name, input); 255 | return; 256 | } 257 | args.forEach(function (el) { 258 | if (typeof pc[el] === "function" ) { 259 | cmds.push(pc[el]); 260 | } 261 | }); 262 | postcss(cmds).process(input).then(function (result) { 263 | result.warnings().forEach(function (warn) { 264 | doc.log(warn.toString()); 265 | }); 266 | doc.gcd.emit("text ready:" + name, result.css); 267 | }).catch(function (error) { 268 | doc.log(error.toString()); 269 | }); 270 | }; 271 | 272 | Folder.plugins.postcss = { 273 | req : postcss, 274 | autoprefixer : require('autoprefixer') 275 | }; 276 | 277 | var tidy = require("js-beautify"); 278 | 279 | Folder.plugins.tidy = { 280 | js : { 281 | "indent_size": 4, 282 | "jslint_happy": true 283 | }, 284 | css: {}, 285 | html :{} 286 | }; 287 | 288 | Folder.sync("tidy", function (code, args) { 289 | var type = args[0]; 290 | var options = args[1] || {}; 291 | var plug = this.plugins.tidy; 292 | 293 | if (! plug.hasOwnProperty(type) ) { 294 | type = "js"; 295 | } 296 | 297 | code = tidy[type](code, merge(true, plug[type], options )); 298 | 299 | return code; 300 | }); 301 | 302 | var jsmin = require("uglify-js").minify; 303 | var cssmin = require("clean-css"); 304 | var htmlmin = require("html-minifier").minify; 305 | 306 | Folder.plugins.minify = { 307 | js : {}, 308 | css : {}, 309 | html : {} 310 | }; 311 | 312 | Folder.sync("minify", function (code, args) { 313 | var type = args[0]; 314 | var options = args[1] || {}; 315 | var plug = this.plugins.minify; 316 | 317 | if (! plug.hasOwnProperty(type) ) { 318 | type = "js"; 319 | } 320 | 321 | options = merge(true, plug[type], options); 322 | 323 | switch (type) { 324 | case 'js' : 325 | options.fromString = true; 326 | code = jsmin(code, options); 327 | break; 328 | case 'css' : 329 | code = new cssmin(options).minify(code); 330 | if (args[2] !== true) { 331 | code = code.styles; 332 | } 333 | break; 334 | case 'html': 335 | code = htmlmin(code, options); 336 | break; 337 | } 338 | 339 | return code; 340 | } ); 341 | 342 | var datefns = require('date-fns'); 343 | Folder.requires.datefns = datefns; 344 | Folder.dash.date = [datefns, 0]; 345 | Folder.sync('date', function (date, args) { 346 | var fn = args[0]; 347 | if (date) { 348 | if (typeit(date) !== 'date') { 349 | date = datefns.parse(date); 350 | } 351 | } else { 352 | if (datefns.hasOwnProperty(fn) ) { 353 | // has method and no incoming so make date 354 | date = new Date(); 355 | } else if (! fn) { 356 | return new Date(); 357 | } else { 358 | // assuming date string in first argument 359 | date = datefns.parse(args.shift()); 360 | fn = args[0]; 361 | } 362 | } 363 | if (! (datefns.hasOwnProperty(fn) ) ) { 364 | // no method just get a date object 365 | return date; 366 | } else { 367 | args[0] = date; 368 | } 369 | return datefns[fn].apply(datefns, args); 370 | }); 371 | 372 | var csv = Folder.requires.csv = require("csv"); 373 | Folder.plugins.csv = { 374 | parse : {}, 375 | stringify : {}, 376 | transform : {} 377 | }; 378 | Folder.async("csv-parse", function (input, args, cb) { 379 | var options = merge(args[0], this.plugins.csv.parse); 380 | csv.parse(input, options, cb); 381 | }); 382 | Folder.async("csv-transform", function (input, args, cb) { 383 | var options = merge(args[1], this.plugins.csv.transform); 384 | var f = (typeit(args[0] === "function" ) ) ? 385 | args[0] : function (el) {return el;}; 386 | csv.transform( input, f, options, cb); 387 | }); 388 | Folder.async("csv-stringify", function (input, args, cb) { 389 | var options = merge(args[0], this.plugins.csv.stringify); 390 | csv.stringify(input, options, cb); 391 | }); 392 | 393 | var lodash = Folder.requires.lodash = require("lodash"); 394 | Folder.dash.lodash = [lodash, 1]; 395 | Folder.sync("lodash", function (input, args) { 396 | if (args.length) { 397 | var method = args[0]; 398 | if ( typeit(lodash[method], 'function') ) { 399 | args[0] = input; 400 | return lodash[method].apply(lodash, args); 401 | } else { 402 | // this is an error. need to come up with a warning. 403 | this.warn("lodash", "unrecognized method", method); 404 | return input; 405 | } 406 | } else { 407 | return input; 408 | } 409 | }); 410 | 411 | var he = require('he'); 412 | Folder.requires.he = he; 413 | Folder.plugins.he = { 414 | encode : {}, 415 | decode : {}, 416 | }; 417 | Folder.sync("html-encode", function (input, args) { 418 | var options = merge(this.plugins.he.encode, args[0]); 419 | return he.encode(input, options); 420 | }); 421 | Folder.sync("html-decode", function (input, args) { 422 | var options = merge(this.plugins.he.decode, args[0]); 423 | return he.decode(input, options); 424 | }); 425 | Folder.sync("html-qescape", function (input) { 426 | return he.escape(input); 427 | }); 428 | 429 | Folder.prototype.encoding = args.encoding; 430 | Folder.prototype.displayScopes = (args.scopes ? function () { 431 | var folder = this; 432 | var str = ''; 433 | Object.keys(folder.scopes).forEach(function (el) { 434 | str += el+ "::\n------\n"; 435 | var scope = folder.scopes[el]; 436 | Object.keys(scope).sort().forEach( 437 | function (v) { 438 | str+= v + ": '" + scope[v] + "'\n* * *\n"; 439 | }); 440 | str += "\n------\n"; 441 | }); 442 | str = str.replace(/\n\n/g, "\n"); 443 | console.log(str); 444 | } : 445 | function () {} ); 446 | 447 | 448 | Folder.lprc(args.lprc, args); 449 | 450 | Folder.process(args); 451 | 452 | process.on('exit', Folder.exit()); 453 | -------------------------------------------------------------------------------- /lprc.js: -------------------------------------------------------------------------------- 1 | /*global module, require */ 2 | module.exports = function(Folder, args) { 3 | 4 | if (args.file.length === 0) { 5 | args.file = ["project.md"]; 6 | } 7 | 8 | args.src = "."; 9 | 10 | //require('litpro-jshint')(Folder, args); 11 | 12 | }; 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "literate-programming", 3 | "description": "Fat command line for literate-programming", 4 | "version": "1.1.0", 5 | "homepage": "https://github.com/jostylr/literate-programming", 6 | "author": { 7 | "name": "James Taylor", 8 | "email": "jostylr@gmail.com" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git://github.com/jostylr/literate-programming.git" 13 | }, 14 | "bugs": { 15 | "url": "https://github.com/jostylr/literate-programming/issues" 16 | }, 17 | "license": "MIT", 18 | "main": "index.js", 19 | "engines": { 20 | "node": ">=4.0" 21 | }, 22 | "dependencies":{ 23 | "literate-programming-cli" : "^2.1.0", 24 | "jshint" : "^2.11.0", 25 | "postcss" : "^7.0.26", 26 | "autoprefixer" : "^9.7.4", 27 | "pug" : "^2.0.4", 28 | "markdown-it" : "^10.0.0", 29 | "cheerio" : "^0.22.0", 30 | "js-beautify" : "^1.10.3", 31 | "html-minifier" : "^4.0.0", 32 | "clean-css" : "^4.2.1", 33 | "uglify-js" : "^3.7.5", 34 | "csv" : "^5.3.1", 35 | "date-fns" : "^1.30.1", 36 | "lodash" : "^4.17.15", 37 | "he" : "^1.2.0" 38 | }, 39 | "devDependencies" : { 40 | "litpro-jshint" : "^0.4.0", 41 | "literate-programming-cli-test" : "^0.5.1", 42 | "markdown-it-anchor" : "^5.2.5" 43 | }, 44 | "scripts" : { 45 | "test" : "node ./test.js" 46 | }, 47 | "keywords": ["literate programming"], 48 | "bin": { 49 | "literate-programming" : "./index.js" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /project.md: -------------------------------------------------------------------------------- 1 | # Fat Client 2 | 3 | This is the fat literate programming client, full batteries included. The 4 | batteries largely pertain to web development and may grow in time. 5 | 6 | Currently, it includes markdown converter, pugs (formerly jade), postcss and 7 | autoprefixer, jshint, (minimizers, beautifiers). 8 | 9 | It avoids anything that involves compiling to install. Otherwise, this is 10 | quite a liberal package and issues are welcome to include new batteries. 11 | 12 | 13 | ## Files 14 | 15 | Most of this is adding in the plugins and running full tests. 16 | 17 | ### Load 18 | 19 | Reading file into cli 20 | 21 | * [cli](node_modules/literate-programming-cli/litpro.js "readfile:| 22 | sub ./index.js, literate-programming-cli | 23 | sub //plugin-to-folder, _'plugin to folder ' ") 24 | 25 | ### Save 26 | 27 | * [../](# "cd: save") It all goes in the main directory. 28 | * [index.js](#cli "save:|jshint ") 29 | * [README.md](# "save: | raw ## README, ---! | sub \n\ #, \n# |trim 30 | | sub COMDOC, _'comdoc | .join \n' ") The standard README. 31 | * [convert.md](# "save: | raw ## Converting, --! | sub \n\ #, # |trim") 32 | * [test.js](#test "save: |jshint ") The testing file. 33 | * [documentation.md](#comdoc "save: | .join \n ") 34 | * [](# "cd: save") 35 | 36 | 37 | ### h5 38 | 39 | The h5 headings can be used in a special way. Here we initialize 40 | 41 | * [comdoc](#cdoc "h5: ") 42 | 43 | 44 | ### Display scopes 45 | 46 | This is a function that displays the scopes. 47 | 48 | function () { 49 | var folder = this; 50 | var str = ''; 51 | Object.keys(folder.scopes).forEach(function (el) { 52 | str += el+ "::\n------\n"; 53 | var scope = folder.scopes[el]; 54 | Object.keys(scope).sort().forEach( 55 | function (v) { 56 | str+= v + ": '" + scope[v] + "'\n* * *\n"; 57 | }); 58 | str += "\n------\n"; 59 | 60 | }); 61 | str = str.replace(/\n\n/g, "\n"); 62 | console.log(str); 63 | } 64 | 65 | ## Plugin to Folder 66 | 67 | This is where the fat comes in. 68 | 69 | var merge = Folder.requires.merge; 70 | if (! Folder.requires) { 71 | Folder.requires = {}; 72 | } 73 | var typeit = Folder.requires.typeit; 74 | 75 | _"jshint" 76 | 77 | _"pug" 78 | 79 | _"md" 80 | 81 | _"cheerio" 82 | 83 | _"postcss" 84 | 85 | _"tidy" 86 | 87 | _"minify" 88 | 89 | _"date" 90 | 91 | _"csv" 92 | 93 | _"lodash" 94 | 95 | _"html encodings" 96 | 97 | `_"formatters"` 98 | 99 | 100 | ### Formatters 101 | 102 | This is some custom formatters for the command line client 103 | 104 | var oneArgOnly = _":one arg"; 105 | ["jshint", "jshint clean"]. 106 | forEach(function (el) { 107 | Folder.prototype.formatters[el] = oneArgOnly; 108 | }); 109 | 110 | 111 | [one arg]() 112 | 113 | This just lists the files that were saved. 114 | 115 | function (list) { 116 | var ret = ''; 117 | ret += list.map( 118 | function (args) { 119 | return args.shift(); 120 | }). 121 | join("\n"); 122 | return ret; 123 | } 124 | 125 | 126 | 127 | 128 | # JShint 129 | 130 | This checks for JS problems. 131 | 132 | This is the module entry point. It adds the commands jshint and the directive 133 | jshint which loads options and globals. 134 | 135 | var jshint = require('jshint').JSHINT; 136 | Folder.plugins.jshint = { 137 | options: {unused: true}, 138 | globals: [], 139 | clean : false 140 | } ; 141 | Folder.sync("jshint", _"jshint command"); 142 | 143 | 144 | 145 | ##### cdoc 146 | 147 | * **jshint** This takes the input and runs it through JSHint. The command 148 | is of the form 149 | `js stuff | jshint options, globals, shortname, print clean`. 150 | 151 | * The options is an object that corresponds to the [options that JShint 152 | accepts](http://jshint.com/docs/options/); you can use a subcommand to 153 | create the options object if you like. Default is unused:true, else is 154 | their defaults. 155 | * Globals is an array of global 156 | names; if they can be written over, pass in `name:true` instead of 157 | `name`. 158 | * Shortname is the shortname to present in understanding what is being 159 | jshinted. Otherwise, it does its best to give you a cryptic but 160 | informative name. 161 | * If the fourth argument is a boolean, `t()` or `f()` will do it, then 162 | that toggles whether to print the message that it all went smoothly or 163 | not, respectively. The default is to not print it. 164 | * You can override the defaults repeatedly by modifying the 165 | `Folder.plugins.jshint` object with the names: `options`, `globals`, and 166 | `clean`. 167 | 168 | ### jshint command 169 | 170 | The only thing we add is the jshint command. This takes an incoming text and 171 | hints it. The first argument, if present, is a JSON object of options. The 172 | 173 | function (input, args, name) { 174 | 175 | var doc = this; 176 | var options, globals; 177 | 178 | var log = [], err, i, lines, line, logcopy, 179 | globhash, ind, shortname; 180 | 181 | var plug = doc.plugins.jshint; 182 | 183 | _":options" 184 | 185 | jshint(input, options, globhash); 186 | 187 | 188 | var data = jshint.data(); 189 | 190 | 191 | _":generating the logs" 192 | 193 | _":report logs" 194 | 195 | return input; 196 | } 197 | 198 | 199 | 200 | [report logs]() 201 | 202 | if (log.length > 0 ) { 203 | logcopy = log.slice(); 204 | logcopy.unshift(shortname, "jshint Report"); 205 | doc.log.apply(doc, logcopy); 206 | } else { 207 | if (args[3] || plug.clean) { 208 | doc.log(shortname, "jshint clean"); 209 | } 210 | } 211 | 212 | [options]() 213 | 214 | We can get options from the plugins.jshint object as well as from the 215 | arguments. We are opinionated in setting unused to true. 216 | 217 | Globals are ultimately an object that has a bunch of true/false properties. If 218 | true, then they can be written too. There is also some blacklist property, but 219 | I am not sure where that gets put so ignoring it. 220 | 221 | 222 | options = merge(true, plug.options, args[0] || {}); 223 | 224 | globals = plug.globals.concat(args[1] || []); 225 | 226 | if (args[2]) { 227 | shortname = args[2]; 228 | } else { 229 | shortname = name.slice(name.lastIndexOf(":")+1, 230 | name.indexOf(doc.colon.v, ind) ); 231 | } 232 | 233 | globhash = {}; 234 | globals.forEach( function (el) { 235 | var bits; 236 | bits = el.trim().split(":"); 237 | bits[1] = bits[1] === "true"; 238 | globhash[bits[0].trim()] = bits[1]; 239 | }); 240 | 241 | 242 | 243 | 244 | 245 | [generating the logs]() 246 | 247 | 248 | lines = input.split("\n"); 249 | for (i = 0; i < jshint.errors.length; i += 1) { 250 | err = jshint.errors[i]; 251 | if (!err) {continue;} 252 | if (err.reason.indexOf("is defined but never used.") !== -1) { 253 | continue; //this is covered elsewhere. 254 | } 255 | line = lines[err.line-1]; 256 | if (line.trim().length < 4) { 257 | line = "\n---\n" + lines.slice(err.line-2, err.line+1).join("\n") + 258 | "\n---\n"; 259 | } 260 | log.push("E "+ err.line+","+err.character+": "+err.reason + 261 | " "+ line.trim()); 262 | } 263 | if (data.hasOwnProperty("implieds") ) { 264 | for (i = 0; i < data.implieds.length; i += 1) { 265 | err = data.implieds[i]; 266 | log.push("Implied Gobal "+ err.line+": "+err.name + 267 | " "+ lines[err.line[0]-1].trim()); 268 | } 269 | } 270 | if (data.hasOwnProperty("unused") ) { 271 | for (i = 0; i < data.unused.length; i += 1) { 272 | err = data.unused[i]; 273 | log.push("Unused "+ err.line+": "+err.name + 274 | " "+ lines[err.line-1].trim()); 275 | } 276 | } 277 | 278 | 279 | 280 | ## Pug 281 | 282 | pug converts the pug syntax into html. It is mainly used for structures as 283 | opposed to content. `pug text...|pug` 284 | 285 | var pug = require('pug'); 286 | Folder.plugins.pug = {pretty:true}; 287 | 288 | Folder.sync("pug" , function (code, args) { 289 | var options = merge(true, this.plugins.pug, args); 290 | return pug.render(code, options); 291 | }); 292 | 293 | ##### Doc 294 | 295 | * **pug** This transforms the incoming text by using the 296 | [pug, formerly jade, transformation engine](https://pugjs.org). 297 | Pass in an object as first argument for 298 | options. The defaults are currently used except `pretty:true`. 299 | 300 | 301 | ## Md 302 | 303 | This uses markdown-it. 304 | 305 | Because a new renderer is being made with the change of options and behavior 306 | is modified with uses, these should all be done not inline. The only choices 307 | inline is choosing the name (arg0), and toggling on, off the pre/post 308 | processors. 309 | 310 | The default is to process math and literate snippets. 311 | 312 | 313 | var mdit = require('markdown-it'); 314 | 315 | var mddefault = { 316 | html:true, 317 | linkify:true 318 | }; 319 | 320 | 321 | Folder.plugins.md = { 322 | req : mdit, 323 | def: mdit(mddefault), 324 | options: mddefault, 325 | prepost : [_":litpro subbing", _":math subbing"] 326 | }; 327 | 328 | Folder.sync( "md", function (code, args) { 329 | var plug = this.plugins.md; 330 | var html, rend; 331 | rend = plug[args[0]] || plug.def; 332 | var post = []; 333 | plug.prepost.forEach(function (el, ind) { 334 | var temp; 335 | if (args[ind+1] !== false) { 336 | temp = el(code); 337 | post.push(temp[1]); 338 | code = temp[0]; 339 | } 340 | }); 341 | html = rend.render(code); 342 | post.forEach(function (el) { 343 | html = el(html); 344 | }); 345 | return html; 346 | }); 347 | 348 | 349 | [litpro subbing]() 350 | 351 | We can create an option that does literate programming substituting before 352 | rendering and then replacing it. 353 | 354 | The comment html is to ensure that the markdown does not wrap something else 355 | around it. I hope. This requires `html:true` presumably. 356 | 357 | 358 | function litsub (code) { 359 | var snips = []; 360 | var masklit = function (match) { 361 | snips.push(match); 362 | return ""; 363 | }; 364 | 365 | var rep = function (match, num) { 366 | return snips[parseInt(num, 10)]; 367 | }; 368 | 369 | var undo = function (html) { 370 | var reg = /<\!\-\-LITSNIP(\d+)\-\->/g; 371 | return html.replace(reg, rep); 372 | }; 373 | 374 | var lit = /(?:\\|\\\d*)?\_+(\"[^"]+\"|\`[^`]+\`|\'[^']\')/g; 375 | code = code.replace(lit, masklit); 376 | return [code, undo]; 377 | } 378 | 379 | [math subbing]() 380 | 381 | We want to replace the math expressions to avoid markdown processing of random 382 | parts. Our syntax will be `` `x^2 + 3 < 5`$ `` and gets rendered by katex into 383 | html which is then subbed in after the markdown processing. We also use code 384 | fences for display math, the dollar sign comes after the first fence, where 385 | the language goes. Actually, we have if the dollar sign is inside the first 386 | set of backticks, then it is display, if it is outside after the second set, 387 | then it is inline. No spaces allowed in either case between the backtick and 388 | dollar sign. So this does allow display to happen for inline code and inline 389 | to happen for fenced code. This does mean that a dollar sign next to a 390 | backtick is will almost surely trigger this though a space will stop that. 391 | 392 | function mathsub (code) { 393 | var snips = []; 394 | var maskinline = function (match, ignore, math) { 395 | snips.push("\\(" + math + "\\)" ); 396 | return ""; 397 | }; 398 | 399 | var maskdisp = function (match, ignore, math) { 400 | snips.push("\\[" + math + "\\]" ); 401 | return ""; 402 | }; 403 | 404 | var rep = function (match, num) { 405 | return snips[parseInt(num, 10)]; 406 | }; 407 | 408 | var undo = function (html) { 409 | var reg = /<\!\-\-MATHSNIP(\d+)\-\->/g; 410 | return html.replace(reg, rep); 411 | }; 412 | 413 | var inline = /(\`+)([^`]+)\1\$/g; 414 | code = code.replace(inline, maskinline); 415 | var display = /(\`+)\$([^`]+)\1/g; 416 | code = code.replace(display, maskdisp); 417 | return [code, undo]; 418 | } 419 | 420 | [with katex]() 421 | 422 | Ignored for now as the rendered html is very verbose. 423 | 424 | function mathsub (code) { 425 | var snips = []; 426 | var options = { 427 | throwOnError: false, 428 | display : false 429 | }; 430 | var maskmath = function (match, ignore, math) { 431 | console.log("DEBUG0", match, ignore, math); 432 | snips.push(katex.renderToString(math, options)); 433 | return ""; 434 | }; 435 | 436 | var rep = function (match, num) { 437 | return snips[parseInt(num, 10)]; 438 | }; 439 | 440 | var undo = function (html) { 441 | var reg = /<\!\-\-MATHSNIP(\d+)\-\->/g; 442 | return html.replace(reg, rep); 443 | }; 444 | 445 | var inline = /(\`+)([^`]+)\1\$/g; 446 | code = code.replace(inline, maskmath); 447 | var display = /(\`+)\$([^`]+)\1/g; 448 | options.display = true; 449 | code = code.replace(display, maskmath); 450 | return [code, undo]; 451 | } 452 | 453 | 454 | ##### Cdoc 455 | 456 | * **md** This takes the input as markdown and puts out html. The first 457 | argument is an optional string naming the renderer to use. The other 458 | arguments should be booleans, namely, `f()`, if one does not want 459 | preprocessing/post to occur. The default preprocessors, in order, are 460 | literate programming subs and math subs rendering to katex. 461 | 462 | To create a renderer, you can use Folder.plugins.md.req as the markdoan 463 | object and then render it per the instructions (an options object 464 | `req(options).use(...)`. This is all best done in the lprc.js file. 465 | Store the rendered under the preferred name in plugins.md. 466 | 467 | See the logs test directory and its lprc.js. 468 | 469 | 470 | ## Cheerio 471 | 472 | Cheerio takes in html and can do replacements on it, like jQuery does. The 473 | syntax is `html... | cheerio selector, method, args to method...` 474 | 475 | var cheerio = require('cheerio'); 476 | Folder.plugins.cheerio = { 477 | req: cheerio 478 | }; 479 | 480 | Folder.sync( "cheerio" , function(code, args) { 481 | var selector = args.shift(); 482 | var method = args.shift(); 483 | var $ = cheerio.load(code); 484 | var el$ = $(selector); 485 | try { 486 | el$[method].apply(el$, args); 487 | return $.html(); 488 | } catch (e) { 489 | this.log("Problem with cheerio: " + selector + "," + 490 | method + "," + args.join(",")); 491 | return code; 492 | } 493 | }); 494 | 495 | There is a special function of replacement where the arguments are paired to 496 | be a selector and the html replacement. This is like the standard sub command. 497 | 498 | Folder.sync( "ch-replace" , function(code, args) { 499 | var selector, replacement; 500 | var n = args.length; 501 | var $ = cheerio.load(code); 502 | var i; 503 | for (i = 0; i < n; i += 2) { 504 | selector = args[i]; 505 | replacement = args[i+1]; 506 | $(selector).replaceWith(replacement); 507 | } 508 | return $.html(); 509 | }); 510 | 511 | 512 | ##### cdoc 513 | 514 | * **cheerio** This gives access to the cheerio module, a lightweight node 515 | version of jQuery-esque without the overhead of jsdom. It can't do 516 | everything, but it does most things: 517 | [cheeriojs](https://github.com/cheeriojs/cheerio). To use, the incoming 518 | text is the html doc to modify, the first argument is the selector, the 519 | second the method, and then the arguments to the method, e.g., 520 | `somehtml | cheerio h2.title, .text, Hello there!` 521 | * **ch-replace** This is a convenience method for cheerio. This will use 522 | the first argument as a selector and the second argument as a 523 | html replacement. 524 | 525 | 526 | ## Postcss 527 | 528 | This uses postcss to work its magic on the incoming text. The plugins should 529 | be loaded here; right now it is just autoprefixer. Then it can be used as 530 | `css...|postcss cmd1, cmd2, ...` 531 | 532 | var postcss = require('postcss'); 533 | 534 | Folder.commands.postcss = function (input, args, name) { 535 | var doc = this; 536 | var pc = doc.plugins.postcss; 537 | var cmds = []; 538 | if ( (typeof input !== "string") || (input === '') ) { 539 | doc.gcd.emit("text ready:" + name, input); 540 | return; 541 | } 542 | args.forEach(function (el) { 543 | if (typeof pc[el] === "function" ) { 544 | cmds.push(pc[el]); 545 | } 546 | }); 547 | postcss(cmds).process(input).then(function (result) { 548 | result.warnings().forEach(function (warn) { 549 | doc.log(warn.toString()); 550 | }); 551 | doc.gcd.emit("text ready:" + name, result.css); 552 | }).catch(function (error) { 553 | doc.log(error.toString()); 554 | }); 555 | }; 556 | 557 | Folder.plugins.postcss = { 558 | req : postcss, 559 | autoprefixer : require('autoprefixer') 560 | }; 561 | 562 | 563 | ##### cdoc 564 | 565 | * **postcss** This takes incoming text and runs it through postcss. To do 566 | something useful, you need to have the arguments be the commands to use. 567 | At the moment, the only one shipping with this is autoprefixer, but 568 | others are likely to be added (minimizers and fixers, in particular). 569 | You can add them yourself by, in lprcs.js, saying (installing cssnano as 570 | example) 571 | `Folder.plugins.postcss[cssnano] = require('cssnano');` and ensuring 572 | that the cssnano module is installed in npm. 573 | 574 | ## tidy 575 | 576 | This creates the web-tidy command using js-beautify. Why the name change? Cause 577 | its shorter. 578 | 579 | var tidy = require("js-beautify"); 580 | 581 | Folder.plugins.tidy = { 582 | js : { 583 | "indent_size": 4, 584 | "jslint_happy": true 585 | }, 586 | css: {}, 587 | html :{} 588 | }; 589 | 590 | Folder.sync("tidy", _":fun"); 591 | 592 | 593 | 594 | [fun]() 595 | 596 | function (code, args) { 597 | var type = args[0]; 598 | var options = args[1] || {}; 599 | var plug = this.plugins.tidy; 600 | 601 | if (! plug.hasOwnProperty(type) ) { 602 | type = "js"; 603 | } 604 | 605 | code = tidy[type](code, merge(true, plug[type], options )); 606 | 607 | return code; 608 | } 609 | 610 | ##### cdoc 611 | 612 | * **tidy** This uses [js-beautify](https://www.npmjs.com/package/js-beautify) 613 | The first argument is the type: js, css, or html. The second argument are 614 | options that get merged with the defaults. The js has a default of 615 | `indent_size` of 4 and `jslint_happy` true. An unrecognized first argument 616 | (or none) will default to js. 617 | 618 | 619 | ## Minify 620 | 621 | This combines three different minimizers into a single command. 622 | 623 | var jsmin = require("uglify-js").minify; 624 | var cssmin = require("clean-css"); 625 | var htmlmin = require("html-minifier").minify; 626 | 627 | Folder.plugins.minify = { 628 | js : {}, 629 | css : {}, 630 | html : {} 631 | }; 632 | 633 | Folder.sync("minify", _":fun"); 634 | 635 | [fun]() 636 | 637 | function (code, args) { 638 | var type = args[0]; 639 | var options = args[1] || {}; 640 | var plug = this.plugins.minify; 641 | 642 | if (! plug.hasOwnProperty(type) ) { 643 | type = "js"; 644 | } 645 | 646 | options = merge(true, plug[type], options); 647 | 648 | switch (type) { 649 | case 'js' : 650 | options.fromString = true; 651 | code = jsmin(code, options); 652 | break; 653 | case 'css' : 654 | code = new cssmin(options).minify(code); 655 | if (args[2] !== true) { 656 | code = code.styles; 657 | } 658 | break; 659 | case 'html': 660 | code = htmlmin(code, options); 661 | break; 662 | } 663 | 664 | return code; 665 | } 666 | 667 | 668 | ##### cdoc 669 | 670 | * **minify** The first argument says the type of minifier: js, css, and 671 | html. js is the default if the first argument is not realized. The 672 | second argument is an object of options that get passed in. This uses 673 | uglify-js, clean-css, and 674 | [html-minifier](https://www.npmjs.com/package/html-minifier), 675 | respectively. For css, the 676 | second argument can be a boolean indicating whether to pass on the 677 | results object (if true, `t()` ) or just the css output text (default). 678 | 679 | ## js-bench 680 | 681 | This is to benchmark javscript code. It should be a directive. 682 | 683 | There is the code of the block which could be transformed in a variety of 684 | ways. The linkname could be the benchmark name `bench:case` with the colon 685 | being the case name; the directive would then compare the different cases with 686 | the bench name. 687 | 688 | `[bench:case](#start "js-bench: off/log/varname | pipes for preprocessing")` 689 | 690 | function (input, args) { 691 | 692 | } 693 | 694 | 695 | ## js-test 696 | 697 | Not sure what to really do. This could be similar to the benchmark directive 698 | in form, but not sure if it to use tape, or role my own (with grabbing the 699 | deep-equal algorithm). 700 | 701 | 702 | ## date 703 | 704 | This exposes a nice api for getting dates done nicely. It uses [date-fns](https://date-fns.org/) 705 | 706 | So the incoming input is the date (or whatever) and the arguments are the 707 | function name and other arguments. We get the function name and then we will 708 | apply the args, putting in the date as the first one. If there are no 709 | arguments, a new Date() is returned. If the first argument is not a known 710 | method, then we assume it was a date to be parsed. 711 | 712 | var datefns = require('date-fns'); 713 | Folder.requires.datefns = datefns; 714 | Folder.dash.date = [datefns, 0]; 715 | Folder.sync('date', _":fun"); 716 | 717 | 718 | [fun]() 719 | 720 | function (date, args) { 721 | var fn = args[0]; 722 | _":get date" 723 | _":check for method" 724 | return datefns[fn].apply(datefns, args); 725 | } 726 | 727 | [get date]() 728 | 729 | The issue is that we might have dates coming in different ways. We could have 730 | an incoming date string, an incoming date object, no incoming, but rather have 731 | it as a first argument or we could have no date in either place and thus 732 | create a new date. Here we deal with that logic. 733 | 734 | if (date) { 735 | if (typeit(date) !== 'date') { 736 | date = datefns.parse(date); 737 | } 738 | } else { 739 | if (datefns.hasOwnProperty(fn) ) { 740 | // has method and no incoming so make date 741 | date = new Date(); 742 | } else if (! fn) { 743 | return new Date(); 744 | } else { 745 | // assuming date string in first argument 746 | date = datefns.parse(args.shift()); 747 | fn = args[0]; 748 | } 749 | } 750 | 751 | Could probably move check for method out of the whole thing. 752 | 753 | [check for method]() 754 | 755 | This checks for the method and returns date if not found. 756 | 757 | if (! (datefns.hasOwnProperty(fn) ) ) { 758 | // no method just get a date object 759 | return date; 760 | } else { 761 | args[0] = date; 762 | } 763 | 764 | ##### cdoc 765 | 766 | * **date** `... |date method||date, arg1, arg2, ..`. This uses the 767 | [date-fns](https://date-fns.org/) library. Any valid function in that 768 | should work fine. There are a few scenarios for getting a date going: 769 | 770 | * ` date object | date method, arg1, ...` will apply the method of 771 | `datefns` to the date as the leading argument and use the rest of the 772 | arguments to fill it in. Alias: `date object | -method arg1, ..` 773 | * `date string | date method, arg1, ...` will apply the method to the 774 | date parsed by `datefns.parse` Alias: `date string | -method arg1, ..` 775 | * `| date method, arg1, ...` will apply the method to today's date. 776 | Alias `| -method arg1, ...` 777 | * `| date` Just returns today's date. No alias 778 | * `| date string date, method, args1, ...` will parse the string date 779 | and apply the method. No alias. 780 | * Note that there is also a subcommand `date` that will generate today's 781 | date or a date object based on the input. 782 | 783 | Recommended form: `| date string | -method arg1, ...| ...` 784 | 785 | 786 | ## csv 787 | 788 | This exposes a csv parsing and stringifying library, [node-csv](https://github.com/wdavidw/node-csv) 789 | 790 | We provide a command that takes the incoming as the data, the first argument 791 | as the method, and the rest as options or function, depending. In calling the 792 | csv method, it uses callbacks that we push onto the args array. 793 | 794 | var csv = Folder.requires.csv = require("csv"); 795 | Folder.plugins.csv = { 796 | parse : {}, 797 | stringify : {}, 798 | transform : {} 799 | }; 800 | Folder.async("csv-parse", _":parse"); 801 | Folder.async("csv-transform", _":transform"); 802 | Folder.async("csv-stringify", _":parse | sub parse, stringify"); 803 | 804 | 805 | 806 | [parse]() 807 | 808 | function (input, args, cb) { 809 | var options = merge(args[0], this.plugins.csv.parse); 810 | csv.parse(input, options, cb); 811 | } 812 | 813 | 814 | [transform]() 815 | 816 | The first argument is needed and should be a function. 817 | 818 | function (input, args, cb) { 819 | var options = merge(args[1], this.plugins.csv.transform); 820 | var f = (typeit(args[0] === "function" ) ) ? 821 | args[0] : function (el) {return el;}; 822 | csv.transform( input, f, options, cb); 823 | } 824 | 825 | 826 | ##### cdoc 827 | 828 | * **csv-parse/transform/stringify** 829 | This is an interface into the node-csv library. It does the three 830 | named methods. 831 | The first argument can be an object of options except for transform in 832 | which the options are second and the first argument is a function to 833 | execute on each row. 834 | See [node-csv](http://csv.adaltas.com/) for more details. 835 | 836 | If you need to use the streaming power, you should access the full power 837 | of it using `Folder.requres.csv` and take a look at, for example, [so](http://stackoverflow.com/questions/23080413/nodejs-reading-csv-file) 838 | 839 | 840 | ## lodash 841 | 842 | This adds in the utility belt of lodash for very quick and easy manipulations 843 | of various objects. 844 | 845 | Since underscore is special in this syntax (though it probably could work), we 846 | use the command `dash`. 847 | 848 | var lodash = Folder.requires.lodash = require("lodash"); 849 | Folder.dash.lodash = [lodash, 1]; 850 | Folder.sync("lodash", _":fun"); 851 | 852 | [fun]() 853 | 854 | This takes the incoming as the first argument, making it appear as a method on 855 | the incoming thing. 856 | 857 | function (input, args) { 858 | if (args.length) { 859 | var method = args[0]; 860 | if ( typeit(lodash[method], 'function') ) { 861 | args[0] = input; 862 | return lodash[method].apply(lodash, args); 863 | } else { 864 | // this is an error. need to come up with a warning. 865 | this.warn("lodash", "unrecognized method", method); 866 | return input; 867 | } 868 | } else { 869 | return input; 870 | } 871 | } 872 | 873 | ##### cdoc 874 | 875 | * **lodash** The incoming data is the first 876 | argument into the function while the first argument is the method name. 877 | The other arguments are what they are. 878 | 879 | Example: ` abc | - pad 8, 0` 880 | 881 | ## html encodings 882 | 883 | This loads in the `he` npm module. 884 | 885 | var he = require('he'); 886 | Folder.requires.he = he; 887 | Folder.plugins.he = { 888 | encode : {}, 889 | decode : {}, 890 | }; 891 | Folder.sync("html-encode", _":encode"); 892 | Folder.sync("html-decode", _":encode| sub encode, decode"); 893 | Folder.sync("html-qescape", _":escape"); 894 | 895 | [encode]() 896 | 897 | function (input, args) { 898 | var options = merge(this.plugins.he.encode, args[0]); 899 | return he.encode(input, options); 900 | } 901 | 902 | [escape]() 903 | 904 | function (input) { 905 | return he.escape(input); 906 | } 907 | 908 | ##### cdoc 909 | 910 | * **html-encode/decode/qescape** This is an interface to the 911 | [he](https://github.com/mathiasbynens/he) library. It encodes and 912 | decodes all named html entities. There is also a simple escape function, 913 | that includes quotes which the lit-native html-escape does not. 914 | 915 | 916 | 917 | [off](# "block:") 918 | 919 | ## README 920 | 921 | 922 | # literate-programming [](https://travis-ci.org/jostylr/literate-programming) 923 | 924 | 925 | This is the fat command-line client for 926 | [literate-programming-lib](https://github.com/jostylr/literate-programming-lib). 927 | It contains the full functionality for literate programming, including useful 928 | commands such as jshint included in it. For a thin client, 929 | check out 930 | [litpro](https://github.com/jostylr/litpro) 931 | 932 | 933 | Full documentation: [Literate Programming, MD: How to Treat and Prevent Software Project Mess](https://leanpub.com/literate-programming-md) 934 | 935 | This is not done being fully baked, hence v0.9. But this does represent a 936 | significant break from 0.8.4. You can take a look at convert.md for some 937 | observations of mine as I converted from the old version to the new. 938 | 939 | Install using `npm install literate-programming` 940 | 941 | Usage is `./node_modules/bin/litpro file` and it has some command flags. 942 | 943 | If you want a global install so that you just need to write 944 | `literate-programming` then use `npm install -g literate-programming`. 945 | 946 | The library has a full listing of the syntax, commands, and directives. Here 947 | we list the flags and new commands and directives. 948 | 949 | ## Example usage 950 | 951 | Save the following code to file `project.md` and run `literate-programming project.md`. 952 | 953 | # Welcome 954 | 955 | So you want to make a literate program? Let's have a program that outputs 956 | all numbers between 1 to 10. 957 | 958 | Let's save it in file count.js 959 | 960 | [count.js](#Structure "save: | jshint") 961 | 962 | ## Structure 963 | 964 | We have some intial setup. Then we will generate the array of numbers. We 965 | end with outputting the numbers. 966 | 967 | var numarr = [], start=1, end = 11, step = 1; 968 | 969 | _"Loop" 970 | 971 | _"Output" 972 | 973 | ## Output 974 | 975 | At this point, we have the array of numbers. Now we can join them with a 976 | comma and output that to the console. 977 | 978 | console.log("The numbers are: ", numarr.join(", ") ); 979 | 980 | ## Loop 981 | 982 | Set the loop up and push the numbers onto it. 983 | 984 | var i; 985 | for (i = start; i < end; i += step) { 986 | numarr.push(i); 987 | } 988 | 989 | ## Documentation 990 | 991 | For more information, see the 992 | [documentation book](https://leanpub.com/literate-programming-md) 993 | which is free to read online or available for purchase as a PDF. 994 | 995 | Some particularly useful syntax sections are: 996 | 997 | * [command-line flags](https://leanpub.com/literate-programming-md/read#leanpub-auto-command-line-1) 998 | * [directives](https://leanpub.com/literate-programming-md/read#leanpub-auto-directives-1) 999 | * [commands](https://leanpub.com/literate-programming-md/read#leanpub-auto-commands-1) 1000 | * [subcommands](https://leanpub.com/literate-programming-md/read#leanpub-auto-subcommands-1) 1001 | 1002 | 1003 | 1004 | ## Use and Security 1005 | 1006 | It is inherently unsecure to compile literate 1007 | program documents. No effort has been made to make it secure. Compiling a 1008 | literate program using this program is equivalent to running arbitrary code on 1009 | your computer. Only compile from trusted sources, i.e., use the same 1010 | precautions as running a node module. 1011 | 1012 | 1013 | ## LICENSE 1014 | 1015 | [MIT-LICENSE](https://github.com/jostylr/literate-programming/blob/master/LICENSE-MIT) 1016 | 1017 | ---! 1018 | 1019 | 1020 | [on](# "block:") 1021 | 1022 | ## Test 1023 | 1024 | The first test determines that it is hooked up correctly. This should pass the 1025 | same tests as literate-programming-cli. Just need to make sure it is hooked 1026 | up correctly. 1027 | 1028 | Then we have tests to specifically test each of the command and directives. 1029 | 1030 | We also have integration tests, many of which come from the old version. In 1031 | addition to the usual directory items, it also has an "old" directory for each 1032 | one to allow comparison of the old version and new version. This may be of 1033 | help for those adapting to the new version. 1034 | 1035 | 1036 | 1037 | /* global require */ 1038 | var tests = require('literate-programming-cli-test')("node ../../index.js"); 1039 | 1040 | var files = [["first", "first.md second.md -s ."], 1041 | ["testpro", "period.md testpro.md -s ."], 1042 | ["primes", "primes.md -s . -z primes:20"], 1043 | ["sample", "sample.md -s ."], 1044 | ["template", "templating.md simpletemp.md -s ."], 1045 | ["blackwhitehats", "blackwhitehats.md -s ."], 1046 | ["cinnamon", "cinnamon.md -s ."], 1047 | ["fence", "fence.md -s ."], 1048 | ["jstidy", "jstidy.md -s ."], 1049 | ["fizzbuzz", "fizzbuzz.md -s ."], 1050 | ["matrix", "matrix.md -s ."], 1051 | ["logs", "logs.md -s ."], 1052 | ["cheerio", "cheers.md -s ."], 1053 | ["integrated", "integrated.md -s ."], 1054 | ["date"], 1055 | ["csv"], 1056 | ["lodash"], 1057 | ["he"] 1058 | ].slice(0); 1059 | tests.apply(null, files); 1060 | 1061 | ### test gitignore 1062 | 1063 | This needs to go into each directory in tests. 1064 | 1065 | node_modules 1066 | /build 1067 | /cache 1068 | /.checksum 1069 | /err.test 1070 | /out.test 1071 | 1072 | Need to think of a better way of propagating one file to multiple 1073 | destinations. 1074 | 1075 | * [../tests/](# "cd: save") 1076 | * [first/.gitignore](# "save:") 1077 | * [testpro/.gitignore](# "save:") 1078 | * [primes/.gitignore](# "save:") 1079 | * [sample/.gitignore](# "save:") 1080 | * [template/.gitignore](# "save:") 1081 | * [blackwhitehats/.gitignore](# "save:") 1082 | * [cinnamon/.gitignore](# "save:") 1083 | * [fence/.gitignore](# "save:") 1084 | * [jstidy/.gitignore](# "save:") 1085 | * [fizzbuzz/.gitignore](# "save:") 1086 | * [matrix/.gitignore](# "save:") 1087 | * [logs/.gitignore](# "save:") 1088 | * [cheerio/.gitignore](# "save:") 1089 | * [integrated/.gitignore](# "save:") 1090 | * [date/.gitignore](# "save:") 1091 | * [csv/.gitignore](# "save:") 1092 | * [lodash/.gitignore](# "save:") 1093 | * [he/.gitignore](# "save:") 1094 | * [](# "cd: save" 1095 | 1096 | 1097 | 1098 | [off](# "block:) 1099 | 1100 | ## Converting 1101 | 1102 | Are you coming from the old version? There are syntax differences. Here we 1103 | list a few tips to help the process. 1104 | 1105 | Old way: `| clean raw"` takes the raw text of the section and makes it 1106 | cleaned up New way: New version does not see the commentary text. So we need 1107 | to act on the document itself. We also don't want the blocks to get evaluated 1108 | which automatically happens in this version. So we place directives to turn 1109 | off that feature: `[off](# "block:")` turns off block reading, 1110 | `[on](# "block:")` turns it back on. We then need to do the snippet cutting: 1111 | `| raw ## README, !---- | sub \n\ #, \n# |trim` 1112 | Raw says to take the raw document and 1113 | cut between the two given pieces. We still shift the headings to not be read 1114 | as heading and the sub puts it back. 1115 | 1116 | Moving towards a conventional setup of setup.md containing project files to 1117 | be put in home directory (package.json and lprc.js for example) using `litpro 1118 | -b . setup.md` then one can do `npm install` and then do `litpro` to process 1119 | `project.md` which then calls the other files. 1120 | 1121 | Convert substitute(...) to sub. In VIM: `:%s/substitute(\([^)]\+\))/sub \1/g` 1122 | 1123 | Boilerplate. Old syntax, rather wonky, was to use * to indicate a template to 1124 | fill in. New version has the compile command. So instead of `float tests*test 1125 | template` we would have `_"test template | compile float tests"` All the minor 1126 | sections in the template are required, but they can be empty. Instead of the * 1127 | in front of the minor, escape the underscore. `_"*:code"` --> `\_":code"` 1128 | While the asterisk notation is, in some sense, nicer, the async nature of the 1129 | new version made it problematic. 1130 | 1131 | Beware of h5 and h6 headers; they fill a new role. Reduce the number of hashes. 1132 | 1133 | Minor blocks should now best be in the form `[name]()` They can appear 1134 | anywhere; it is the form that matters. Alternatively, `[name](# ": | ...")` 1135 | can be used if there are pipe transformations desired. The key is the leading 1136 | colon and having a name. 1137 | 1138 | The old setup had minors that could have an extension between `: |` and that 1139 | would become part of the name. That is not present in the new one. It was not 1140 | really needed. Also minors can be referred to in the hash -- just use the 1141 | colon as one would, e.g. `[logs.htm](#structure "Save: main")` becomes 1142 | `[logs.htm](#structure:main "Save: ")` 1143 | 1144 | Blocks cannot refer to each other without problems as they will mutually wait 1145 | for each other. 1146 | 1147 | Fencing of code blocks follows commonmark syntax. Be careful about lists too. 1148 | 1149 | To eval code directly, one can use `| eval _"some code"`. The incoming text is 1150 | in the variable `text` and that variable is what the outgoing text is recorded 1151 | as. 1152 | 1153 | We no longer can evaluate the blocks in terms of where we are in the command 1154 | input stage. This was always confusing, anyway. Instead use backslashes and 1155 | the compile command. 1156 | 1157 | To access the arguments called by the command line, one can do 1158 | `doc.parent.stdin[whatever property]` and one can create whatever property by 1159 | doing `- z prop:val` 1160 | 1161 | The two things above allow one to have the literate program directly doing 1162 | computations instead of just creating a script that can be called. No real 1163 | reason for this, I suppose, but hey, it works. 1164 | 1165 | No replacement for 1166 | 1167 | ``` 1168 | \_"*:expected|heading" 1169 | ## Heading 1170 | 1171 | function () { 1172 | return this.hblock.heading.split("*")[0]; 1173 | } 1174 | 1175 | [heading](#heading "define: command | | now") 1176 | ``` 1177 | 1178 | 1179 | ## Break with previous versions 1180 | 1181 | This is a complete rewrite. The syntax is simplified so that only the ``_`code 1182 | block| function | functionn` `` syntax is needed. Leave off the code block to 1183 | just start using functions (leave the pipe). The code block in that syntax 1184 | need not be a code block, but could be a user-defined variable name, 1185 | 1186 | Another break in the syntax is the switch link. This was a link that was on a 1187 | line by itself. It was a kind of heading, one that would make a quick separate 1188 | code block under the same heading. I find it convenient. But in trying to 1189 | match the general parsing of markdown programs, I am moving towards using a 1190 | professional markdown parser that would make it difficult to recognize the 1191 | positioning of a link, but trivial to parse one. So the switch link will be a 1192 | link whose title quote starts with a colon. So an empty directive. It can 1193 | still be positioned as always. Also can implement it so that if the 1194 | parenthetical is completely empty, then that is a switch. I noticed that that 1195 | is what I often do. 1196 | 1197 | For header purposes, a link's square bracket portion will be returned to the 1198 | surrounding block. 1199 | 1200 | Also headers will have a way to be modified based on their levels. 1201 | I have never used levels 5 and 6, for example. 1202 | As an example, one could have level 5 headers for tests, docs, and examples, 1203 | that could then be compiled and run bey selecting those headers. Not sure yet. 1204 | 1205 | Also, there is no tracking of the non-significant text. So for example, raw 1206 | will not work in the same way. It was always a bit of a hack and now it will 1207 | be more so. There can be easily a plugin that will search for the heading and 1208 | cut the rest, etc. 1209 | 1210 | Multiple substitute cycles are no longer supported. I always found it hard to 1211 | reason about it and it greatly simplifies the code. If you need that 1212 | functionality, it probably is workable with substitutes and the variable 1213 | storage introduced. 1214 | 1215 | The compiled blocks are stored as variables. We can store arbitrary variable 1216 | names and so potentially can conflict with block names. You have been warned. 1217 | It is all "global" scope though you can use syntax to kind of scope it. Well, 1218 | actually we are scoped to the documents, that is `docname::..` gives another 1219 | scope which the var setting respects. 1220 | 1221 | Another break is that block names need to match. There is the main block which 1222 | has no minor associated with it and then there are the minors of the block. If 1223 | you want the minor commands on the main, then start the code line with `[](# 1224 | ":|...")` where the colon is there to indicate a minor directive, but with no 1225 | name and no extension, this will signal the main block. Note that this will 1226 | overwrite whatever was in the main code block, if anything. Once a block 1227 | (minor or not) is switched from, it cannot be added to later. Trust me, this 1228 | is a good thing. 1229 | 1230 | 1231 | --! 1232 | 1233 | [on](# "block:") 1234 | 1235 | 1236 | -------------------------------------------------------------------------------- /setup.md: -------------------------------------------------------------------------------- 1 | # [literate-programming](# "version:1.1.0; Fat command line for literate-programming") 2 | 3 | This is the command line portion of literate-programming. It depends on 4 | literate-programming-lib. 5 | 6 | 7 | At the moment, at least, I am of the firm opinion that one should structure a 8 | litpro directory as cache, build, src, lprc.js as where you start. These 9 | locations can be changed in the command line, but the idea is that you are at 10 | the top, it all goes down. 11 | 12 | Any initially given filenames are read as is. This allows for shell 13 | completion. It is a little odd in that command line is non-prefixed while 14 | loading from within doc is prefixed. One can also specify starting files in 15 | lprc.js by modifying args.files. 16 | 17 | ## Directory structure 18 | 19 | * [../](# "cd: save") 20 | * [lprc.js](#lprc "save: |jshint") This will define the jshint command 21 | * [package.json](#npm-package "save: | jshint ") The requisite package file for a npm project. 22 | * [LICENSE](#license-mit "save: ") The MIT license as I think that is the standard in the node community. 23 | * [.npmignore](#npmignore "save: ") 24 | * [.gitignore](#gitignore "save: ") 25 | * [.travis.yml](#travis "save: ") 26 | * [](# "cd: save") 27 | 28 | 29 | ## lprc 30 | 31 | This imports the jshint command and starts that the build directory is the 32 | current one and the default file to process is this one. 33 | 34 | /*global module, require */ 35 | module.exports = function(Folder, args) { 36 | 37 | if (args.file.length === 0) { 38 | args.file = ["project.md"]; 39 | } 40 | 41 | args.src = "."; 42 | 43 | //require('litpro-jshint')(Folder, args); 44 | 45 | }; 46 | 47 | 48 | 49 | ## NPM package 50 | 51 | The requisite npm package file. 52 | 53 | 54 | { 55 | "name": "_`g::docname`", 56 | "description": "_`g::tagline`", 57 | "version": "_`g::docversion`", 58 | "homepage": "https://github.com/_`g::gituser`/_`g::docname`", 59 | "author": { 60 | "name": "_`g::authorname`", 61 | "email": "_`g::authoremail`" 62 | }, 63 | "repository": { 64 | "type": "git", 65 | "url": "git://github.com/_`g::gituser`/_`g::docname`.git" 66 | }, 67 | "bugs": { 68 | "url": "https://github.com/_`g::gituser`/_`g::docname`/issues" 69 | }, 70 | "license": "MIT", 71 | "main": "index.js", 72 | "engines": { 73 | "node": ">=4.0" 74 | }, 75 | "dependencies":{ 76 | _"g::npm dependencies" 77 | }, 78 | "devDependencies" : { 79 | _"g::npm dev dependencies" 80 | }, 81 | "scripts" : { 82 | "test" : "node ./test.js" 83 | }, 84 | "keywords": ["literate programming"], 85 | "bin": { 86 | "literate-programming" : "./index.js" 87 | } 88 | } 89 | 90 | 91 | ## gitignore 92 | 93 | node_modules 94 | /build 95 | /cache 96 | /.checksum 97 | 98 | 99 | ## npmignore 100 | 101 | 102 | build 103 | .checksum 104 | cache 105 | tests 106 | test.js 107 | .travis.yml 108 | lprc.js 109 | *.md 110 | 111 | 112 | ## Travis 113 | 114 | A travis.yml file for continuous test integration! 115 | 116 | language: node_js 117 | node_js: 118 | - "node" 119 | - "4.0" 120 | sudo: false 121 | 122 | 123 | 124 | ## LICENSE MIT 125 | 126 | 127 | The MIT License (MIT) 128 | Copyright (c) _"g::year" _"g::authorname" 129 | 130 | Permission is hereby granted, free of charge, to any person obtaining a copy 131 | of this software and associated documentation files (the "Software"), to deal 132 | in the Software without restriction, including without limitation the rights 133 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 134 | copies of the Software, and to permit persons to whom the Software is 135 | furnished to do so, subject to the following conditions: 136 | 137 | The above copyright notice and this permission notice shall be included in all 138 | copies or substantial portions of the Software. 139 | 140 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 141 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 142 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 143 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 144 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 145 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 146 | SOFTWARE. 147 | 148 | 149 | 150 | 151 | 152 | by [James Taylor](https://github.com/jostylr "npminfo: jostylr@gmail.com ; 153 | deps: literate-programming-cli 2.1.0, jshint 2.11.0, 154 | postcss 7.0.26, autoprefixer 9.7.4, pug 2.0.4, 155 | markdown-it 10.0.0, cheerio 0.22.0, js-beautify 1.10.3, 156 | html-minifier 4.0.0, clean-css 4.2.1, uglify-js 3.7.5, 157 | csv 5.3.1, date-fns 1.30.1, lodash 4.17.15, he 1.2.0 ; 158 | dev: litpro-jshint 0.4.0, 159 | literate-programming-cli-test 0.5.1, 160 | markdown-it-anchor 5.2.5") 161 | 162 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | /* global require */ 2 | var tests = require('literate-programming-cli-test')("node ../../index.js"); 3 | 4 | var files = [["first", "first.md second.md -s ."], 5 | ["testpro", "period.md testpro.md -s ."], 6 | ["primes", "primes.md -s . -z primes:20"], 7 | ["sample", "sample.md -s ."], 8 | ["template", "templating.md simpletemp.md -s ."], 9 | ["blackwhitehats", "blackwhitehats.md -s ."], 10 | ["cinnamon", "cinnamon.md -s ."], 11 | ["fence", "fence.md -s ."], 12 | ["jstidy", "jstidy.md -s ."], 13 | ["fizzbuzz", "fizzbuzz.md -s ."], 14 | ["matrix", "matrix.md -s ."], 15 | ["logs", "logs.md -s ."], 16 | ["cheerio", "cheers.md -s ."], 17 | ["integrated", "integrated.md -s ."], 18 | ["date"], 19 | ["csv"], 20 | ["lodash"], 21 | ["he"] 22 | ].slice(0); 23 | tests.apply(null, files); 24 | -------------------------------------------------------------------------------- /tests/blackwhitehats/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /build 3 | /cache 4 | /.checksum 5 | /err.test 6 | /out.test 7 | -------------------------------------------------------------------------------- /tests/blackwhitehats/blackwhitehats.md: -------------------------------------------------------------------------------- 1 | # Black and White Hats 2 | 3 | This is a literate program to implement strategies concerning the following 4 | problem: 5 | 6 | >You have 100 people in a line. Each is wearing a black or white hat, but they 7 | >don't know which. Each hat is equally likely to be on each person's head. 8 | >They are put in a line, and they can see all the people after them. Starting 9 | >with the first, they are each asked to make a guess as to which color hat 10 | >they are wearing. They can agree on a strategy and they can hear all previous 11 | >guesses. There is also one person in the line that may not follow the 12 | >strategy. What is the optimal strategy for maximizing the number of right 13 | >guesses? 14 | 15 | What follows below is the solution and can also be found on 16 | [github](https://github.com/jostylr/literate-programming/blob/master/examples/blackwhitehats.md). 17 | There is a live version on [JS 18 | Bin](http://jsbin.com/UpeveZe/1/edit?js,console). 19 | 20 | ## [hats.js](#hats.js "save: | jshint") 21 | 22 | We are going to simulate this with a nodejs program. We first generate the 23 | line. Then we have a starting function followed by a function that works for 24 | the later ones, using the guesses and the next line. 25 | 26 | Our strategy will be the first person indicates the parity of the number of 27 | black hats in the line (black if odd, white if not). Then the rest will simply 28 | say "black" if the parity changes and white otherwise. The one who does not 29 | follow this will mess up the person following them, but that's it. At least 30 | that's the assertion. 31 | 32 | 33 | var i, 34 | n = 100, 35 | line = [], 36 | msg = [], 37 | remainder = [], 38 | //traitor = n+1, 39 | traitor = Math.floor(Math.random()*n), 40 | success = 0, 41 | current, 42 | oldParity, 43 | parity; 44 | 45 | var parityFun = _"parity"; 46 | 47 | _"Make a line" 48 | 49 | remainder = line.slice(1); 50 | 51 | _"First guess" 52 | 53 | _"Rest of guesses" 54 | 55 | console.log(line.join('')); 56 | console.log(msg.join('')); 57 | 58 | console.log("traitor at "+ traitor + "\nNumber of Correct guesses: " + success); 59 | 60 | ## Make a line 61 | 62 | We can start with making the line. 63 | 64 | for (i = 0; i < n; i += 1) { 65 | if (Math.random() <= 0.5) { 66 | line.push("b"); 67 | } else { 68 | line.push("w"); 69 | } 70 | } 71 | 72 | ## Parity 73 | 74 | We need to determine the parity of the number of blacks. 75 | 76 | function (arr) { 77 | var i, n = arr.length, count = 0; 78 | 79 | for (i = 0; i < n; i += 1) { 80 | if (arr[i] === "b") { 81 | count +=1; 82 | } 83 | } 84 | 85 | return count %2; 86 | } 87 | 88 | ## First guess 89 | 90 | If the parity is 1, we say "b" otherwise "w". Then we check for success. 91 | 92 | parity = parityFun(remainder); 93 | i = 0; 94 | if (i === traitor) { 95 | msg.push("T"); 96 | parity = (parity +1)%2; 97 | } else { 98 | if (parity) { 99 | msg.push("b"); 100 | } else { 101 | msg.push("w"); 102 | } 103 | if (msg[i] === line[i]) { 104 | success += 1; 105 | } 106 | } 107 | 108 | 109 | ## Rest of guesses 110 | 111 | Here we go through all the remaining ones. Different parity, then black hat. 112 | Otherwise not. 113 | 114 | 115 | while (remainder.length !== 0) { 116 | i += 1; 117 | current = remainder.shift(); 118 | oldParity = parity; 119 | parity = parityFun(remainder); 120 | if (i === traitor) { 121 | msg.push("T"); 122 | parity = (parity +1)%2; 123 | } else { 124 | if (parity !== oldParity) { 125 | msg.push("b"); 126 | } else { 127 | msg.push("w"); 128 | } 129 | if (msg[i] === line[i]) { 130 | success += 1; 131 | } 132 | } 133 | } 134 | 135 | 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /tests/blackwhitehats/canonical/build/hats.js: -------------------------------------------------------------------------------- 1 | var i, 2 | n = 100, 3 | line = [], 4 | msg = [], 5 | remainder = [], 6 | //traitor = n+1, 7 | traitor = Math.floor(Math.random()*n), 8 | success = 0, 9 | current, 10 | oldParity, 11 | parity; 12 | 13 | var parityFun = function (arr) { 14 | var i, n = arr.length, count = 0; 15 | 16 | for (i = 0; i < n; i += 1) { 17 | if (arr[i] === "b") { 18 | count +=1; 19 | } 20 | } 21 | 22 | return count %2; 23 | }; 24 | 25 | for (i = 0; i < n; i += 1) { 26 | if (Math.random() <= 0.5) { 27 | line.push("b"); 28 | } else { 29 | line.push("w"); 30 | } 31 | } 32 | 33 | remainder = line.slice(1); 34 | 35 | parity = parityFun(remainder); 36 | i = 0; 37 | if (i === traitor) { 38 | msg.push("T"); 39 | parity = (parity +1)%2; 40 | } else { 41 | if (parity) { 42 | msg.push("b"); 43 | } else { 44 | msg.push("w"); 45 | } 46 | if (msg[i] === line[i]) { 47 | success += 1; 48 | } 49 | } 50 | 51 | while (remainder.length !== 0) { 52 | i += 1; 53 | current = remainder.shift(); 54 | oldParity = parity; 55 | parity = parityFun(remainder); 56 | if (i === traitor) { 57 | msg.push("T"); 58 | parity = (parity +1)%2; 59 | } else { 60 | if (parity !== oldParity) { 61 | msg.push("b"); 62 | } else { 63 | msg.push("w"); 64 | } 65 | if (msg[i] === line[i]) { 66 | success += 1; 67 | } 68 | } 69 | } 70 | 71 | console.log(line.join('')); 72 | console.log(msg.join('')); 73 | 74 | console.log("traitor at "+ traitor + "\nNumber of Correct guesses: " + success); 75 | -------------------------------------------------------------------------------- /tests/blackwhitehats/original/blackwhitehats.md: -------------------------------------------------------------------------------- 1 | # Black and White Hats 2 | 3 | This is a literate program to implement strategies concerning the following problem: 4 | 5 | >You have 100 people in a line. Each is wearing a black or white hat, but they don't know which. Each hat is equally likely to be on each person's head. They are put in a line, and they can see all the people after them. Starting with the first, they are each asked to make a guess as to which color hat they are wearing. They can agree on a strategy and they can hear all previous guesses. There is also one person in the line that may not follow the strategy. What is the optimal strategy for maximizing the number of right guesses? 6 | 7 | What follows below is the solution and can also be found on [github](https://github.com/jostylr/literate-programming/blob/master/examples/blackwhitehats.md). There is a live version on [JS Bin](http://jsbin.com/UpeveZe/1/edit?js,console). 8 | 9 | ## [hats.js](#hats.js "save: | jshint") 10 | 11 | We are going to simulate this with a nodejs program. We first generate the line. Then we have a starting function followed by a function that works for the later ones, using the guesses and the next line. 12 | 13 | Our strategy will be the first person indicates the parity of the number of black hats in the line (black if odd, white if not). Then the rest will simply say "black" if the parity changes and white otherwise. The one who does not follow this will mess up the person following them, but that's it. At least that's the assertion. 14 | 15 | 16 | var i, 17 | n = 100, 18 | line = [], 19 | msg = [], 20 | remainder = [], 21 | //traitor = n+1, 22 | traitor = Math.floor(Math.random()*n), 23 | success = 0, 24 | current, 25 | oldParity, 26 | parity; 27 | 28 | var parityFun = _"parity"; 29 | 30 | _"Make a line" 31 | 32 | remainder = line.slice(1); 33 | 34 | _"First guess" 35 | 36 | _"Rest of guesses" 37 | 38 | console.log(line.join('')); 39 | console.log(msg.join('')); 40 | 41 | console.log("traitor at "+ traitor + "\nNumber of Correct guesses: " + success); 42 | 43 | ## Make a line 44 | 45 | We can start with making the line. 46 | 47 | for (i = 0; i < n; i += 1) { 48 | if (Math.random() <= 0.5) { 49 | line.push("b"); 50 | } else { 51 | line.push("w"); 52 | } 53 | } 54 | 55 | ## Parity 56 | 57 | We need to determine the parity of the number of blacks. 58 | 59 | function (arr) { 60 | var i, n = arr.length, count = 0; 61 | 62 | for (i = 0; i < n; i += 1) { 63 | if (arr[i] === "b") { 64 | count +=1; 65 | } 66 | } 67 | 68 | return count %2; 69 | } 70 | 71 | ## First guess 72 | 73 | If the parity is 1, we say "b" otherwise "w". Then we check for success. 74 | 75 | parity = parityFun(remainder); 76 | i = 0; 77 | if (i === traitor) { 78 | msg.push("T"); 79 | parity = (parity +1)%2; 80 | } else { 81 | if (parity) { 82 | msg.push("b"); 83 | } else { 84 | msg.push("w"); 85 | } 86 | if (msg[i] === line[i]) { 87 | success += 1; 88 | } 89 | } 90 | 91 | 92 | ## Rest of guesses 93 | 94 | Here we go through all the remaining ones. Different parity, then black hat. Otherwise not. 95 | 96 | 97 | while (remainder.length !== 0) { 98 | i += 1; 99 | current = remainder.shift(); 100 | oldParity = parity; 101 | parity = parityFun(remainder); 102 | if (i === traitor) { 103 | msg.push("T"); 104 | parity = (parity +1)%2; 105 | } else { 106 | if (parity !== oldParity) { 107 | msg.push("b"); 108 | } else { 109 | msg.push("w"); 110 | } 111 | if (msg[i] === line[i]) { 112 | success += 1; 113 | } 114 | } 115 | } 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /tests/cheerio/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /build 3 | /cache 4 | /.checksum 5 | /err.test 6 | /out.test 7 | -------------------------------------------------------------------------------- /tests/cheerio/canonical/build/out.html: -------------------------------------------------------------------------------- 1 |
hi
4 | # FOLDER LOGS 5 | ## SAVED 6 | ./build/out.html 7 | ## DONE 8 | ./build 9 | -------------------------------------------------------------------------------- /tests/cheerio/cheers.md: -------------------------------------------------------------------------------- 1 | # Testing cheerio 2 | 3 |hi
" 18 | 19 | 20 | -------------------------------------------------------------------------------- /tests/cinnamon/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /build 3 | /cache 4 | /.checksum 5 | /err.test 6 | /out.test 7 | -------------------------------------------------------------------------------- /tests/cinnamon/canonical/build/cinnamon.css: -------------------------------------------------------------------------------- 1 | [data-cinnamon] { 2 | position: relative; 3 | display: inline-block; 4 | } 5 | .cinnamon { 6 | z-index: -1; 7 | position: absolute; 8 | top: 0; left: 0; 9 | display: inline-block; 10 | height: 100%; 11 | width: 100%; 12 | color: rgba(0,0,0,0); 13 | overflow: hidden; font-size: 999px; 14 | } 15 | @media all and (device-width: 768px) and (device-height: 1024px) { 16 | .cinnamon { 17 | z-index: 1; 18 | opacity: 0.25; } 19 | } 20 | -------------------------------------------------------------------------------- /tests/cinnamon/canonical/build/cinnamon.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |A visitor to your site wants to follow your Twitter account. You have a link to it in your footer, but their search for "Twitter" comes up empty and they move on — unfortunately, you happened to name the link "@username" instead.
31 | 32 |Cinnamon.js is a script that allows users to find elements by their synonyms, using the browser's built-in Find feature. To see it in action, search this page for "Twitter", "Spice", "Email" or "Contact".
33 | 34 |35 | 36 |43 | 44 |Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque cursus orci ut mi laoreet rhoncus. Pellentesque congue urna tincidunt tortor rhoncus dapibus. Duis faucibus dolor a sem ultrices at facilisis risus cursus. Cras vel euismod nisl. Ut vitae risus et libero sagittis ultrices et vitae ligula. Aliquam at turpis id diam placerat consequat at vitae est. Aenean tellus magna, lacinia vitae facilisis facilisis, egestas ut sapien. Follow me at @thomashpark. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec tincidunt dapibus dui luctus rhoncus. Nam sagittis egestas blandit. Nulla imperdiet tincidunt enim, a tempus sapien volutpat a. Maecenas sed elit ipsum, ut tristique odio. Suspendisse potenti.
37 | 38 |39 | 40 |
Photo by kobiz7Etiam et elit enim, quis semper metus. Nunc sodales posuere turpis, viverra vestibulum lectus vehicula sed. Nulla quis augue nec nibh varius pharetra adipiscing non felis. Etiam lobortis vestibulum luctus. Cras non felis enim, id gravida libero. Vivamus vulputate nisi at tellus pulvinar faucibus sodales mi feugiat. Reach me here. Proin id est ut quam dictum venenatis ac non risus. Ut porttitor mattis odio vel elementum. Aliquam molestie lorem nec diam pharetra et malesuada diam condimentum. Cras feugiat pulvinar sollicitudin. Etiam id diam fermentum quam varius laoreet. Quisque nibh nunc, ullamcorper et bibendum at, ultrices et lorem. Cras vitae euismod felis. Donec lectus libero, ornare eget luctus nec, dictum et sapien. Proin viverra justo ac augue pellentesque aliquet.
41 | 42 |
To add to your page, include cinnamon.js. Then wrap your element of choice (such as in span
tags) and give the data-cinnamon
attribute a comma-separated list of terms. For example, <span data-cinnamon="azure,cerulean,cobalt">blue</span>
. If you wrap an image, its alt text will also be used, as in the example above.
A visitor to your site wants to follow your Twitter account. You have a link to it in your footer, but their search for "Twitter" comes up empty and they move on — unfortunately, you happened to name the link "@username" instead.
197 | 198 |Cinnamon.js is a script that allows users to find elements by their synonyms, using the browser's built-in Find feature. To see it in action, search this page for "Twitter", "Spice", "Email" or "Contact".
199 | 200 |201 | 202 |209 | 210 |Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque cursus orci ut mi laoreet rhoncus. Pellentesque congue urna tincidunt tortor rhoncus dapibus. Duis faucibus dolor a sem ultrices at facilisis risus cursus. Cras vel euismod nisl. Ut vitae risus et libero sagittis ultrices et vitae ligula. Aliquam at turpis id diam placerat consequat at vitae est. Aenean tellus magna, lacinia vitae facilisis facilisis, egestas ut sapien. Follow me at @thomashpark. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec tincidunt dapibus dui luctus rhoncus. Nam sagittis egestas blandit. Nulla imperdiet tincidunt enim, a tempus sapien volutpat a. Maecenas sed elit ipsum, ut tristique odio. Suspendisse potenti.
203 | 204 |205 | 206 |
Photo by kobiz7Etiam et elit enim, quis semper metus. Nunc sodales posuere turpis, viverra vestibulum lectus vehicula sed. Nulla quis augue nec nibh varius pharetra adipiscing non felis. Etiam lobortis vestibulum luctus. Cras non felis enim, id gravida libero. Vivamus vulputate nisi at tellus pulvinar faucibus sodales mi feugiat. Reach me here. Proin id est ut quam dictum venenatis ac non risus. Ut porttitor mattis odio vel elementum. Aliquam molestie lorem nec diam pharetra et malesuada diam condimentum. Cras feugiat pulvinar sollicitudin. Etiam id diam fermentum quam varius laoreet. Quisque nibh nunc, ullamcorper et bibendum at, ultrices et lorem. Cras vitae euismod felis. Donec lectus libero, ornare eget luctus nec, dictum et sapien. Proin viverra justo ac augue pellentesque aliquet.
207 | 208 |
To add to your page, include cinnamon.js. Then wrap your element of choice (such as in span
tags) and give the data-cinnamon
attribute a comma-separated list of terms. For example, <span data-cinnamon="azure,cerulean,cobalt">blue</span>
. If you wrap an image, its alt text will also be used, as in the example above.
A visitor to your site wants to follow your Twitter account. You have a link to it in your footer, but their search for "Twitter" comes up empty and they move on — unfortunately, you happened to name the link "@username" instead.
195 | 196 |Cinnamon.js is a script that allows users to find elements by their synonyms, using the browser's built-in Find feature. To see it in action, search this page for "Twitter", "Spice", "Email" or "Contact".
197 | 198 |199 | 200 |207 | 208 |Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque cursus orci ut mi laoreet rhoncus. Pellentesque congue urna tincidunt tortor rhoncus dapibus. Duis faucibus dolor a sem ultrices at facilisis risus cursus. Cras vel euismod nisl. Ut vitae risus et libero sagittis ultrices et vitae ligula. Aliquam at turpis id diam placerat consequat at vitae est. Aenean tellus magna, lacinia vitae facilisis facilisis, egestas ut sapien. Follow me at @thomashpark. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec tincidunt dapibus dui luctus rhoncus. Nam sagittis egestas blandit. Nulla imperdiet tincidunt enim, a tempus sapien volutpat a. Maecenas sed elit ipsum, ut tristique odio. Suspendisse potenti.
201 | 202 |203 | 204 |
Photo by kobiz7Etiam et elit enim, quis semper metus. Nunc sodales posuere turpis, viverra vestibulum lectus vehicula sed. Nulla quis augue nec nibh varius pharetra adipiscing non felis. Etiam lobortis vestibulum luctus. Cras non felis enim, id gravida libero. Vivamus vulputate nisi at tellus pulvinar faucibus sodales mi feugiat. Reach me here. Proin id est ut quam dictum venenatis ac non risus. Ut porttitor mattis odio vel elementum. Aliquam molestie lorem nec diam pharetra et malesuada diam condimentum. Cras feugiat pulvinar sollicitudin. Etiam id diam fermentum quam varius laoreet. Quisque nibh nunc, ullamcorper et bibendum at, ultrices et lorem. Cras vitae euismod felis. Donec lectus libero, ornare eget luctus nec, dictum et sapien. Proin viverra justo ac augue pellentesque aliquet.
205 | 206 |
To add to your page, include cinnamon.js. Then wrap your element of choice (such as in span
tags) and give the data-cinnamon
attribute a comma-separated list of terms. For example, <span data-cinnamon="azure,cerulean,cobalt">blue</span>
. If you wrap an image, its alt text will also be used, as in the example above.
We got code.
3 |We got code.
40 |We got code.
40 |Hey ≠ there.
8 | 9 |Hey ≠ there.
10 | 11 | <p dude="right">Hey ≠ there.</p> 12 | 13 |Hey ≠ there.
14 | -------------------------------------------------------------------------------- /tests/he/project.md: -------------------------------------------------------------------------------- 1 | This does some html encoding and decoding. 2 | 3 | _"text | html-encode" 4 | 5 | _"encoded | html-decode" 6 | 7 | _"normal | html-qescape" 8 | 9 | _"normal | html-qescape | html-unescape" 10 | 11 | _"normal | html-qescape | html-decode" 12 | 13 | _"normal | html-escape" 14 | 15 | _"normal | html-escape | html-unescape" 16 | 17 | 18 | [out.txt](# "save:") 19 | 20 | ## Text 21 | 22 | foo © bar ≠ baz α qux 23 | 24 | ## encoded 25 | 26 | foo © bar ≠ baz 𝌆 qux 27 | 28 | ## normal 29 | 30 |Hey ≠ there.
31 | 32 | -------------------------------------------------------------------------------- /tests/integrated/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /build 3 | /cache 4 | /.checksum 5 | /err.test 6 | /out.test 7 | -------------------------------------------------------------------------------- /tests/integrated/canonical/build/dev/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |A bird in the hand is worth more than two in the bush.
13 |A bird in the hand is worth more than two in the bush.
Factorials get too big too fast. For example, \(69! \approx 1.7\times 4 | 10^{98}\) is the maximum factorial a calculator can handle. And Google 5 | maxes out around 170!
6 |The issue is that the numbers get too big for the computer to store in 7 | memory in its default way to store numbers.
8 |Logarithms cut numbers down to size, but we cannot use the logarithm of 10 | the answer without computing it. Or can we?
11 |The logarithm of a product is the sum of the logarithms...
12 |Using logarithms, I claim \(1000! \approx 4.02\times 10^{2567}\).
14 |To start with, computing \(\ln(1000!)\) does not help since \(1000!\) is 15 | computed first before the logarithm can act.
16 |But remember that logarithms convert products to sums:
17 | \[ 18 | \ln (1000* 999* 998* ... *3 *2 *1) = \ln(1000) + \ln(999) + \ln(998) + \cdots + \ln(3)+\ln(2) + \ln(1) 19 | \] 20 |For example, if you run the following command in http://geogebra.org:
21 | sum(sequence(ln(n), n, 1, 1000))
you will get \(5912.13\).
Now we cannot exponentiate that directly since it would generate too large 23 | a number. It is currently in base \(e\), i.e.,
24 | \(e^{5912} 25 | \approx 1000!\) 26 |How do we get it to base 10? Divide by \(\ln(10)\)
27 |Once in base 10, the integer part is the power of 10. The fractional part 28 | could then be exponentiated.
29 |sum(sequence(ln(n), n, 1, 1000))/ln(10) = 2567.6046
Therefore,
31 | \[ 32 | 1000! \approx 10^{0.6046} \times 10^{2567} \approx 4.02 \times 10^{2567} 33 | \] 34 |To see the growth of the factorial, let's compute a few of them.
36 |n | n! | \(\log_{10} n!\) |
---|---|---|
5 | 120 | 2.08 |
10 | 3.62880E6 | 6.56 |
50 | 3.04141E64 | 64.5 |
100 | 9.33262E157 | 158 |
500 | 1.22014E1134 | 1.13e+3 |
1000 | 4.02387E2567 | 2.57e+3 |
5000 | 4.22858E16325 | 1.63e+4 |
10000 | 2.84626E35659 | 3.57e+4 |
50000 | 3.34732E213236 | 2.13e+5 |
100000 | 2.82423E456573 | 4.57e+5 |
500000 | 1.02280E2632341 | 2.63e+6 |
1000000 | 8.26393E5565708 | 5.57e+6 |
To use, put in a positive integer.
54 | 55 | 56 | 57 | 58 | 59 |Would you like to play around with the code that generated this? Try 80 | editing the follow code block:
81 | 93 |The result of the above computation is .
94 |This is the implementation described above. We first compute the some of 95 | the logarithms, then we compute the logarithm relative to 10. The final 96 | line is just an attempt to present the result nicely.
97 |Feel free to modify the code to see how it works.
98 | 99 |Factorials are large. For example, suffling a deck of cards leads to about
101 | \(52! \approx 8.13 \times 10^{67}\) different possible outcomes. That is a 102 |number with 67 digits. To put that into perspective, the number of 103 | estimated molecules in the observed universe is about \(10^{80}\).
104 |While Google maxes out at \(170!\), one can use 105 | Wolfram Alpha 106 | to compute the factorial and check our work.
107 |Always keep in mind the power of logarithms.
108 | -------------------------------------------------------------------------------- /tests/logs/canonical/build/logs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |Factorials get too big too fast. For example, \(69! \approx 1.7\times 36 | 10^{98}\) is the maximum factorial a calculator can handle. And Google 37 | maxes out around 170!
38 |The issue is that the numbers get too big for the computer to store in 39 | memory in its default way to store numbers.
40 |Logarithms cut numbers down to size, but we cannot use the logarithm of 42 | the answer without computing it. Or can we?
43 |The logarithm of a product is the sum of the logarithms...
44 |Using logarithms, I claim \(1000! \approx 4.02\times 10^{2567}\).
46 |To start with, computing \(\ln(1000!)\) does not help since \(1000!\) is 47 | computed first before the logarithm can act.
48 |But remember that logarithms convert products to sums:
49 | \[ 50 | \ln (1000* 999* 998* ... *3 *2 *1) = \ln(1000) + \ln(999) + \ln(998) + \cdots + \ln(3)+\ln(2) + \ln(1) 51 | \] 52 |For example, if you run the following command in http://geogebra.org:
53 | sum(sequence(ln(n), n, 1, 1000))
you will get \(5912.13\).
Now we cannot exponentiate that directly since it would generate too large 55 | a number. It is currently in base \(e\), i.e.,
56 | \(e^{5912} 57 | \approx 1000!\) 58 |How do we get it to base 10? Divide by \(\ln(10)\)
59 |Once in base 10, the integer part is the power of 10. The fractional part 60 | could then be exponentiated.
61 |sum(sequence(ln(n), n, 1, 1000))/ln(10) = 2567.6046
Therefore,
63 | \[ 64 | 1000! \approx 10^{0.6046} \times 10^{2567} \approx 4.02 \times 10^{2567} 65 | \] 66 |To see the growth of the factorial, let's compute a few of them.
68 |n | n! | \(\log_{10} n!\) |
---|---|---|
5 | 120 | 2.08 |
10 | 3.62880E6 | 6.56 |
50 | 3.04141E64 | 64.5 |
100 | 9.33262E157 | 158 |
500 | 1.22014E1134 | 1.13e+3 |
1000 | 4.02387E2567 | 2.57e+3 |
5000 | 4.22858E16325 | 1.63e+4 |
10000 | 2.84626E35659 | 3.57e+4 |
50000 | 3.34732E213236 | 2.13e+5 |
100000 | 2.82423E456573 | 4.57e+5 |
500000 | 1.02280E2632341 | 2.63e+6 |
1000000 | 8.26393E5565708 | 5.57e+6 |
To use, put in a positive integer.
86 | 87 | 88 | 89 | 90 | 91 |Would you like to play around with the code that generated this? Try 112 | editing the follow code block:
113 | 125 |The result of the above computation is .
126 |This is the implementation described above. We first compute the some of 127 | the logarithms, then we compute the logarithm relative to 10. The final 128 | line is just an attempt to present the result nicely.
129 |Feel free to modify the code to see how it works.
130 | 131 |Factorials are large. For example, suffling a deck of cards leads to about
133 | \(52! \approx 8.13 \times 10^{67}\) different possible outcomes. That is a 134 |number with 67 digits. To put that into perspective, the number of 135 | estimated molecules in the observed universe is about \(10^{80}\).
136 |While Google maxes out at \(170!\), one can use 137 | Wolfram Alpha 138 | to compute the factorial and check our work.
139 |Always keep in mind the power of logarithms.
140 | 141 |Factorials get too big too fast. For example, $69! \approx 1.7\times 10^{98}$ is the maximum factorial a calculator can handle. And Google maxes out around 170!
4 |The issue is that the numbers get too big for the computer to store in memory in its default way to store numbers.
5 |Logarithms cut numbers down to size, but we cannot use the logarithm of the answer without computing it. Or can we?
7 |The logarithm of a product is the sum of the logarithms...
8 |Using logarithms, I claim $1000! \approx 4.02\times 10^{2567}$.
10 |To start with, computing $\ln(1000!)$ does not help since $1000!$ is computed first before the logarithm can act.
11 |But remember that logarithms convert products to sums: 12 | $$\ln (1000* 999* 998* ... *3 *2 *1) = \ln(1000) + \ln(999) + \ln(998) + \cdots + \ln(3)+\ln(2) + \ln(1) $$
13 |For example, if you run the following command in GeoGebra: sum(sequence(ln(n), n, 1, 1000))
you will get $5912.13$.
Now we cannot exponentiate that directly since it would generate too large a number. It is currently in base $e$, i.e., \(e^{5912} \approx 1000!\)
15 |How do we get it to base 10? Divide by $\ln(10)$
16 |Once in base 10, the integer part is the power of 10. The fractional part could then be exponentiated.
17 |sum(sequence(ln(n), n, 1, 1000))/ln(10) = 2567.6046
Therefore, 19 | $$1000! \approx 10^{0.6046} \times 10^{2567} \approx 4.02 \times 10^{2567}$$
20 |To see the growth of the factorial, let's compute a few of them.
22 |n | n! | \(\log_{10}(n!)\) |
---|---|---|
5 | 120 | 2.08 |
10 | 3.62880E6 | 6.56 |
50 | 3.04141E64 | 64.5 |
100 | 9.33262E157 | 158 |
500 | 1.22014E1134 | 1.13e+3 |
1000 | 4.02387E2567 | 2.57e+3 |
5000 | 4.22858E16325 | 1.63e+4 |
10000 | 2.84626E35659 | 3.57e+4 |
50000 | 3.34732E213236 | 2.13e+5 |
100000 | 2.82423E456573 | 4.57e+5 |
500000 | 1.02280E2632341 | 2.63e+6 |
1000000 | 8.26393E5565708 | 5.57e+6 |
To use, put in a positive integer.
26 | 27 | 28 | 29 | 30 |Would you like to play around with the code that generated this? Try editing the follow code block:
50 | 60 | 61 |The result of the above computation is .
62 | 63 |This is the implementation described above. We first compute the some of the logarithms, then we compute the logarithm relative to 10. The final line is just an attempt to present the result nicely.
64 |Feel free to modify the code to see how it works.
65 | 66 | 67 |Factorials are large. For example, suffling a deck of cards leads to about $52! \approx 8.13 \times 10^{67}$ different possible outcomes. That is a number with 67 digits. To put that into perspective, the number of estimated molecules in the observed universe is about $10^{80}$.
69 |While Google maxes out at $170!$, one can use Wolfram Alpha to compute the factorial and check our work.
70 |Always keep in mind the power of logarithms.
71 | -------------------------------------------------------------------------------- /tests/logs/canonicalold/build/logs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |Factorials get too big too fast. For example, $69! \approx 1.7\times 10^{98}$ is the maximum factorial a calculator can handle. And Google maxes out around 170!
57 |The issue is that the numbers get too big for the computer to store in memory in its default way to store numbers.
58 |Logarithms cut numbers down to size, but we cannot use the logarithm of the answer without computing it. Or can we?
60 |The logarithm of a product is the sum of the logarithms...
61 |Using logarithms, I claim $1000! \approx 4.02\times 10^{2567}$.
63 |To start with, computing $\ln(1000!)$ does not help since $1000!$ is computed first before the logarithm can act.
64 |But remember that logarithms convert products to sums: 65 | $$\ln (1000* 999* 998* ... *3 *2 *1) = \ln(1000) + \ln(999) + \ln(998) + \cdots + \ln(3)+\ln(2) + \ln(1) $$
66 |For example, if you run the following command in GeoGebra: sum(sequence(ln(n), n, 1, 1000))
you will get $5912.13$.
Now we cannot exponentiate that directly since it would generate too large a number. It is currently in base $e$, i.e., \(e^{5912} \approx 1000!\)
68 |How do we get it to base 10? Divide by $\ln(10)$
69 |Once in base 10, the integer part is the power of 10. The fractional part could then be exponentiated.
70 |sum(sequence(ln(n), n, 1, 1000))/ln(10) = 2567.6046
Therefore, 72 | $$1000! \approx 10^{0.6046} \times 10^{2567} \approx 4.02 \times 10^{2567}$$
73 |To see the growth of the factorial, let's compute a few of them.
75 |n | n! | \(\log_{10}(n!)\) |
---|---|---|
5 | 120 | 2.08 |
10 | 3.62880E6 | 6.56 |
50 | 3.04141E64 | 64.5 |
100 | 9.33262E157 | 158 |
500 | 1.22014E1134 | 1.13e+3 |
1000 | 4.02387E2567 | 2.57e+3 |
5000 | 4.22858E16325 | 1.63e+4 |
10000 | 2.84626E35659 | 3.57e+4 |
50000 | 3.34732E213236 | 2.13e+5 |
100000 | 2.82423E456573 | 4.57e+5 |
500000 | 1.02280E2632341 | 2.63e+6 |
1000000 | 8.26393E5565708 | 5.57e+6 |
To use, put in a positive integer.
79 | 80 | 81 | 82 | 83 |Would you like to play around with the code that generated this? Try editing the follow code block:
103 | 113 | 114 |The result of the above computation is .
115 | 116 |This is the implementation described above. We first compute the some of the logarithms, then we compute the logarithm relative to 10. The final line is just an attempt to present the result nicely.
117 |Feel free to modify the code to see how it works.
118 | 119 | 120 |Factorials are large. For example, suffling a deck of cards leads to about $52! \approx 8.13 \times 10^{67}$ different possible outcomes. That is a number with 67 digits. To put that into perspective, the number of estimated molecules in the observed universe is about $10^{80}$.
122 |While Google maxes out at $170!$, one can use Wolfram Alpha to compute the factorial and check our work.
123 |Always keep in mind the power of logarithms.
124 | 125 |The result of the above computation is .
196 | 197 | 198 | ## Common factorial 199 | 200 | Here we define the loop, get the result and format it appropriately. n is 201 | already defined as its definition differs. 202 | 203 | 204 | var lf = 0, nf; 205 | //sum ove the logs 206 | var i; 207 | for (i = 0; i < n; i += 1) { 208 | lf += Math.log(i+1); 209 | } 210 | var lf10 = lf/Math.LN10; 211 | nf = lf10 < 6 ? Math.round(Math.pow(10, lf10) ) : 212 | Math.pow(10, lf10-Math.floor(lf10)).toPrecision(6) + 213 | "E" + Math.floor(lf10); 214 | 215 | 216 | 217 | ## Table of Factorials 218 | 219 | It might be nice to see a variety of factorials. 220 | We'll do a crescendo of 1, 5, 10, 50, 100, 500, .... 221 | 222 | [main](# ":|md |compile ") 223 | 224 | ## A few factorials 225 | To see the growth of the factorial, let's compute a few of them. 226 | 227 | \_":Factorial Table| evil | html-table arr(n, n!, 228 | \_'|echo `\log_{10} n!`$ | md ') " 229 | 230 | [Factorial Table](# ":|jshint") 231 | 232 | var i = 1; 233 | var fact = function (n) { 234 | _"Common factorial" 235 | return [n, nf, lf10.toPrecision(3)]; 236 | }; 237 | var mat = []; 238 | while (i < 1e6) { 239 | i *= 5; 240 | mat.push(fact(i)); 241 | i *= 2; 242 | mat.push(fact(i)); 243 | } 244 | ret = new doc.Folder.Matrix(mat); 245 | 246 | 247 | ## Testing the factorial 248 | 249 | Let's make sure our factorial function is working. 250 | 251 | [](# ":|jshint | evil") 252 | 253 | (function () { 254 | var factorial = function (n) { 255 | _"common factorial" 256 | return nf; 257 | }; 258 | var factorials = {1 : 1, 2: 2, 3: 6, 4: 24, 5:120, 6:720}; 259 | var n, flag = false; 260 | for (n in factorials) { 261 | if (factorial(n) != factorials[n] ) { 262 | console.log(n + " does not work"); 263 | flag = true; 264 | } 265 | } 266 | if (flag) { 267 | console.log( "factorial function had errors"); 268 | } else { 269 | console.log("factorial function passed test"); 270 | } 271 | return ""; 272 | })(); 273 | 274 | 275 | Stand alone page 276 | ============= 277 | 278 | Boiler plate taken from the well-written page: [SitePoint](http://www.sitepoint.com/a-minimal-html-document-html5-edition/) 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 |The result of the above computation is .
180 | 181 | 182 | ## Common factorial 183 | 184 | Here we define the loop, get the result and format it appropriately. n is already defined as its definition differs. 185 | 186 | [](# "JS") 187 | 188 | var lf = 0, nf; 189 | //sum ove the logs 190 | var i; 191 | for (i = 0; i < n; i += 1) { 192 | lf += Math.log(i+1); 193 | } 194 | var lf10 = lf/Math.LN10; 195 | nf = lf10 < 6 ? Math.round(Math.pow(10, lf10) ) : Math.pow(10, lf10-Math.floor(lf10)).toPrecision(6) + "E" + Math.floor(lf10); 196 | 197 | 198 | 199 | ## Table of Factorials 200 | 201 | It might be nice to see a variety of factorials. We'll do a cresendo of 1, 5, 10, 50, 100, 500, .... 202 | 203 | [](# "MD |1 marked") 204 | 205 | ## A few factorials 206 | 207 | To see the growth of the factorial, let's compute a few of them. 208 | 209 | __":Factorial Table| eval | htmltable(rowswheader)" 210 | 211 | [Factorial Table](# "JS |jshint") 212 | 213 | var i = 1; 214 | var fact = function (n) { 215 | _"Common factorial|indent(4,4)" 216 | return [n, nf, lf10.toPrecision(3)]; 217 | }; 218 | var tarr = [["n", "n!", "\\(\\log_{10}(n!)\\)"]]; 219 | while (i < 1e6) { 220 | i *= 5; 221 | tarr.push(fact(i)); 222 | i *= 2; 223 | tarr.push(fact(i)); 224 | } 225 | 226 | return tarr; 227 | 228 | ## Jquery version 229 | 230 | Not active as we have a default jquery macro in the common standard plugins. But here as an example of how to define a macro until another example comes up. Remove leading space in front of DEFINE to get it to be active. 231 | 232 | function (v) { 233 | return ''; 235 | } 236 | 237 | [](# "define: jquery") 238 | 239 | ## Testing the factorial 240 | 241 | Let's make sure our factorial function is working. 242 | 243 | [](# "JS |jshint | eval") 244 | 245 | (function () { 246 | var factorial = function (n) { 247 | _"common factorial" 248 | return nf; 249 | }; 250 | var factorials = {1 : 1, 2: 2, 3: 6, 4: 24, 5:120, 6:720}; 251 | var n, flag = false; 252 | for (n in factorials) { 253 | if (factorial(n) != factorials[n] ) { 254 | console.log(n + " does not work"); 255 | flag = true; 256 | } 257 | } 258 | if (flag) { 259 | console.log( "factorial function had errors"); 260 | } else { 261 | console.log("factorial function passed test"); 262 | } 263 | return ""; 264 | })(); 265 | 266 | 267 | Stand alone page 268 | ============= 269 | 270 | Boiler plate taken from the well-written page: [SitePoint](http://www.sitepoint.com/a-minimal-html-document-html5-edition/) 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 |8 | 4 | 18 |
10 | 6 | 20 |
14 | 8 | 24 |
16 | 10 | 46 |
These are the great and magnificent mountains.
10 |The mountains rose from the ground over so many years...
11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/template/canonical/build/out.txt: -------------------------------------------------------------------------------- 1 | great: How are you? 2 | 3 | bye 4 | -------------------------------------------------------------------------------- /tests/template/canonical/build/snippet: -------------------------------------------------------------------------------- 1 |