├── .gitignore ├── Gruntfile.js ├── LICENSE.txt ├── README.md ├── StringTemplate.md ├── bin ├── stc └── stst ├── compiler ├── group.js ├── groupGen.stg ├── groupGen_stg.js ├── stGrammar.js ├── stGrammar.pegjs └── stc.js ├── lib ├── Dictionary.js ├── Template.js ├── autoIndentWriter.js ├── errors.js ├── javaScriptAttributeRenderer.js ├── stGroup.js ├── stRuntime.js └── util.js ├── package.json ├── samples └── hello │ ├── hello.json │ ├── hello.st │ └── hello_empty.json └── test ├── compiler ├── group.js └── stGrammar.js ├── hello.st.js ├── helloTest.js ├── include.stg ├── includeData.json ├── includeTest.js ├── lib ├── Dictionary.js ├── Template.js ├── autoIndentWriter.js ├── javaScriptAttributeRenderer.js ├── stGroup.js └── stRuntime.js ├── testGroup.stg ├── testGroupData.json └── testGroupTest.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Include your project-specific ignores in this file 2 | # Read about how to use .gitignore: https://help.github.com/articles/ignoring-files 3 | /.idea 4 | /node_modules 5 | /test/misc/** 6 | /doc/** 7 | *_ast.json 8 | *_stg.js 9 | !groupGen_stg.js 10 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 3 | // Project configuration. 4 | grunt.initConfig({ 5 | pkg: grunt.file.readJSON("package.json"), 6 | 7 | peg: { 8 | st: { 9 | src: "compiler/stGrammar.pegjs", 10 | dest: "compiler/stGrammar.js", 11 | options: { 12 | allowedStartRules: ["groupFile", "templateFile", "templateFileRaw", "templateAndEOF"] 13 | } 14 | } 15 | }, 16 | 17 | jshint: { 18 | options: { 19 | bitwise: true, 20 | curly: true, 21 | eqeqeq: true, 22 | indent: 4, 23 | plusplus: false, 24 | undef: true, 25 | unused: true, 26 | node: true 27 | }, 28 | all: { 29 | options: { 30 | node: true 31 | }, 32 | src: ["lib/*.js", "compiler/*.js"] 33 | } 34 | }, 35 | 36 | mochaTest: { 37 | all: { 38 | options: { 39 | reporter: 'spec', 40 | quiet: false, 41 | clearRequireCache: true 42 | }, 43 | src: ['test/**/*.js'] 44 | } 45 | }, 46 | 47 | watch: { 48 | grammar: { 49 | files: ["compiler/stGrammar.pegjs"], 50 | tasks: ["peg"], 51 | options: { 52 | spawn: false 53 | } 54 | }, 55 | code: { 56 | files: ["lib/*.js", "compiler/*.js"], 57 | tasks: ["jshint"], 58 | options: { 59 | spawn: false 60 | } 61 | }, 62 | tests: { 63 | files: ["test/**/*.js", "lib/*.js", "compiler/*.js"], 64 | tasks: ["mochaTest"], 65 | options: { 66 | spawn: false 67 | } 68 | } 69 | } 70 | 71 | }); 72 | 73 | // 74 | // Load tasks 75 | // 76 | 77 | grunt.loadNpmTasks('grunt-peg'); 78 | grunt.loadNpmTasks('grunt-contrib-jshint'); 79 | grunt.loadNpmTasks('grunt-contrib-watch'); 80 | grunt.loadNpmTasks('grunt-mocha-test'); 81 | 82 | grunt.loadNpmTasks('grunt-contrib-copy'); // xxx needed? 83 | grunt.loadNpmTasks('grunt-contrib-uglify'); // xxx needed? 84 | 85 | // 86 | // Define tasks 87 | // 88 | 89 | // Default task(s). 90 | grunt.registerTask('default', ['peg', "jshint", "mochaTest"]); 91 | 92 | }; -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | StringTemplate-js License 2 | 3 | [The "BSD licence"] 4 | Copyright (c) 2015, John Snyders 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions 9 | are met: 10 | 1. Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | 2. Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | 3. The name of the author may not be used to endorse or promote products 16 | derived from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # StringTemplate v4 for JavaScript 2 | 3 | stringtemplate-js is a pure JavaScript implementation of [StringTemplate v4](http://www.stringtemplate.org/). 4 | Templates are compiled to JavaScript. The intent is to be fully compatible with StringTemplate 5 | syntax. This means that the same template processed with the same data should produce the same 6 | output when processed with the Java (reference implementation) and this JavaScript implementation. 7 | See below for known differences. The API will **not** be compatible with the Java implementation. 8 | 9 | # News 10 | 11 | ## 7-Nov 12 | Tested with Node 4.2.2. Unit tests pass and seems working fine. 13 | 14 | ## 9-Aug 15 | Version 0.1.1 ready for alpha testing. Feel free to use github Issues to report problems or make suggestions. 16 | Template regions not yet supported. 17 | 18 | ## 30-Jul 19 | Apologies to anyone who downloaded the 0.1.0 version I added to npm. It does not work as is. 20 | It may work if you compile the pegjs grammer. I plan to update the version on npm soon. 21 | 22 | ## 26-Jul 23 | 24 | Major milestone. StringTemplate-js now uses itself to generate compiled templates. 25 | It was able to pass all unit tests without using the Java version of StringTemplate for code generation! 26 | 27 | What works at least a little 28 | 29 | * compile and process directory of template files (.st) 30 | * compile and process directory of raw template files (.st) 31 | * compile and process group file (.stg) 32 | * configurable start, stop delimiters and escaping delimiters 33 | * comments 34 | * dictionaries 35 | * literals string, true, false, lists 36 | * escapes such as $\t$ and $\\\\$ 37 | * attribute expressions including implicit iteration over list/array attributes 38 | * property reference expressions 39 | * if, else if, else conditions including &&, ||, ! 40 | * imports 41 | * template include expressions and argument passing by position or name and pass through (...) 42 | * expression options 43 | * sub templates 44 | * map expressions 45 | * rotating through templates while mapping 46 | * function expressions 47 | * auto indent writer 48 | * indirect property reference 49 | * indirect template include 50 | * zipping multiple lists/arrays while mapping 51 | 52 | What doesn't work or isn't tested 53 | 54 | * regions 55 | 56 | Nest step is to stabilize, and release first version without support for regions so other can try it out. 57 | Then come back and implement regions. 58 | 59 | ## License 60 | BSD 61 | 62 | ## Install 63 | 64 | StringTemplate adds two command line utilities. The template compiler `stc` and standalone tool `stst` for testing 65 | templates with JSON input. For that reason it is best to install globally. 66 | 67 | npm install -g stringtemplate-js 68 | 69 | If you don't install globally links to the stc and stst commands are in the node_modules/.bin folder. 70 | 71 | ## Using 72 | 73 | The file samples/hello/hello.st contains (with comments removed): 74 | 75 | ``` 76 | hello(audience) ::= <> 79 | ``` 80 | 81 | * Step 1 compile the templates using the stc command. For example: 82 | 83 | ``` 84 | cd samples/hello 85 | stc hello.st 86 | ``` 87 | 88 | This produces `hello_stg.js` in the same folder. Type `stc -h` for help on stc command line options. 89 | 90 | Templates can also be compiled from your own application using the API in compiler/stc.js. Grunt task TBD. 91 | 92 | * Step 2 Execute the compiled template. This can be done with stst that is in the bin folder. 93 | 94 | ``` 95 | cd samples/hello 96 | stst hello hello.json 97 | ``` 98 | 99 | The first argument is the template name without the _stg.js suffix. The second argument is a JSON file that supplies 100 | the data for the template. Type `stst -h` for help on stst command line options. 101 | 102 | To execute the template from your application add code similar to the following: 103 | 104 | ``` 105 | cd samples/hello 106 | node 107 | var st = require("stringtemplate-js"); 108 | var g = st.loadGroup(require("./hello_stg")); 109 | console.log(g.render("hello", ["world"]); 110 | // or 111 | console.log(g.render("hello", {audience:"world"}); 112 | ``` 113 | 114 | ## API 115 | tbd 116 | 117 | 118 | ## Building 119 | 120 | Additional development dependencies: need grunt-cli, mocha and pegjs installed globally 121 | 122 | 123 | ## Known Differences 124 | This section lists differences between this implementation and the Java reference implementation 125 | 126 | * The Java AutoIndentWriter will strip lone cr (\r) from the output. This autoIndentWriter normalizes lone cr to the 127 | OS new line. A lone cr may be unlikely. One way to get one is with $"\r"$. 128 | 129 | * The Java implementation allows any start or stop delimiter even if they will later cause confusion. 130 | This implementation restricts the delimiters to "#$%^&*<>" 131 | 132 | * The JavaScript implementation doesn't support the old v3 style group file header 133 | 134 | * The order in which object properties (or map/dictionary keys) are iterated over may be different. 135 | For example $obj:T()$ when obj is { "a": "foo", "b": "bar"} could call template T 136 | with ["a", "b"] or ["b", "a"]. The JavaScript implementation will iterate over object 137 | properties in the order returned by Object.keys(obj); 138 | 139 | -------------------------------------------------------------------------------- /StringTemplate.md: -------------------------------------------------------------------------------- 1 | # StringTemplate Documentation 2 | 3 | StringTemplate is a language for describing how to combine structured data with literal text to produce output text. 4 | It is also a library that processes that language. 5 | 6 | A template system is a template processor or engine that processes the template language along with any application 7 | specific code needed to acquire, access or generate the data and possibly store or transmit the resulting text. 8 | The template processor is generally supplied as a library or program to be called by the application. 9 | 10 | The purpose of a template system is to produce output text where the output is given by the input template(s). 11 | The templates are made up of literal text that is copied to the output as is and embedded expressions that specify what 12 | data is to be inserted into the output. 13 | 14 | Official [documentation](https://theantlrguy.atlassian.net/wiki/display/ST4/StringTemplate+4+Documentation) for StringTemplate. 15 | 16 | ## Templates 17 | Templates are literal text with embedded template expressions. Expressions are delimited with a special start and stop 18 | character. All text outside of these characters is copied to the output nearly verbatim (see Escaping Expression 19 | Delimiters below). Text inside the delimiters are template expressions that determine how data is to be inserted into 20 | the output. The insertion happens at the point where the expression occurs within the literal text. 21 | 22 | StringTemplate templates enforce a separation between the template and the data (this is also known as model - view 23 | separation where the data is referred to as the model and the template is the view). Templates cannot implement 24 | any business logic *. The template cannot modify the data model. Processing the template produces no side effects. 25 | The data model is created prior to template processing, which means the order of data acquisition is independent of the 26 | order that the data is referenced by template processing. 27 | 28 | Templates *do* have a constructs for conditionals, looping, a small number of built-in functions, 29 | a few options to control the output rendering, and calling templates with arguments. These features are necessary 30 | for the efficient generation of output text. They are not sufficient to implement business logic. 31 | 32 | These claims of strict separation apply to the StringTemplate language. It is possible to abuse the API such that 33 | side effects or data modifications are possible. These are faults of the application implementation and not 34 | StringTemplate. 35 | 36 | \* One place where it could be argued that templates allow business logic is in conditional expressions because 37 | they allow boolean operators: and, or, and not. These operators are for convenience and provide very little opportunity 38 | for abuse. You don't need to use these operators if you don't want to. 39 | 40 | ### Template Expression Delimiters 41 | StringTemplate allows the start and stop delimiter characters to be configurable. This is useful for working with 42 | different text output formats. It allows you to choose characters that are uncommon in the output format. 43 | The default is determined by the implementation and can easily be changed through the processor API or with the 44 | group file syntax described below. 45 | 46 | The following characters can be used as start or stop delimiters. The start and stop delimiter need not be the same 47 | character. 48 | 49 | Possible start and stop delimiters: 50 | 51 | ``` 52 | #$%^&*<> 53 | ``` 54 | 55 | Common delimiter pairs are <> and $$. Most of the examples in this documentation use $$. 56 | 57 | Example: 58 | ``` 59 | Dear $name$, 60 | You may have already won $prize$. To collect your prize... 61 | ``` 62 | 63 | As will be explained in detail below all of the text is copied verbatim to the output except for what is in between the 64 | dollar signs ($): `$name$` will be replaced with the data associated with attribute `name` and `$prize$` will be 65 | replaced with the data associated with attribute `prize`. 66 | 67 | If the delimiters were instead configured to be < and > then the above example would be written: 68 | ``` 69 | Dear , 70 | You may have already won . To collect your prize... 71 | ``` 72 | 73 | ### Escaping Expression Delimiters 74 | To include the start character in the literal template text it will need to be escaped. There is no need to escape 75 | the stop character (assuming it is different from the start character) 76 | 77 | The start character is escaped using the backslash character as follows: `\$`. 78 | 79 | As we will learn later, sub templates are delimited with { } characters so inside a sub template the closing bracket 80 | needs to be escaped. Again a backslash is used: `\}`. 81 | 82 | Normally you don't need to worry about escaping the backslash character but in situations where there is any ambiguity 83 | you will need to escape it with another backslash like this: `\\`. 84 | 85 | This means that: 86 | 87 | To get this output | Enter this in the template 88 | -------------------|------------------------ 89 | `$` | `\$` 90 | `\` | `\` 91 | `\\` | `\\\\` 92 | `\$` | `\\\$` 93 | `\}` | `\\}` 94 | `\}` | `\\\}` 95 | `}` | `\}` 96 | 97 | The last two only apply in a sub template. 98 | 99 | ## The Data Model 100 | StringTemplate has very few data types. Most of how it deals with the data model is implementation specific. What it 101 | does know about is the shape of the data. Data can be one of the following shapes: 102 | 103 | * scalar - a single value. This includes obvious things like strings and numbers but also includes things like 104 | a Point or Complex object. 105 | StringTemplate doesn't deal with specific types but relies on the implementation to be able to turn any scalar into 106 | a string (for example via a toString method). 107 | 108 | * array - also known as a list or linear collection/enumeration. The items in the array can be of any shape or type. 109 | The values are ordered. StringTemplate will aways implicitly iterate over the array items. 110 | 111 | * object - also known as a map, dictionary, or hash. This is an unordered collection of named values. 112 | The named values are known as properties of the object. 113 | The values can be of any shape or type. StringTemplate allows accessing the values by their name. This is known as 114 | a property reference, access, or lookup. Depending on the context StringTemplate can also iterate over the 115 | property names of an object. 116 | 117 | The data types that StringTemplate deals with explicitly are: 118 | 119 | * Templates 120 | * Strings 121 | * Lists (arrays) 122 | * Booleans 123 | * Dictionaries (similar to objects but with a special feature for handling defaults) 124 | 125 | It is also aware of, but has no representation for, the special value null. 126 | 127 | The StringTemplate processor and its API is implemented in a specific programming language such as Java, C# or JavaScript and 128 | each implementation must specify how it maps the StringTemplate data model onto the native data structures of that language. 129 | 130 | The processor API provides a way to associate application data with templates. In addition data values can be passed from 131 | one template to another via argument passing as described below. Template expressions can also contain a few literal 132 | data represenetations as described next. See also dictionaries in Group Files section. 133 | 134 | xxx attribute names, scopes here or later 135 | 136 | ## Literals 137 | Template expressions may include the following literals: 138 | 139 | * The boolean values `true` and `false`. 140 | 141 | * Strings. Strings are sequences of characters enclosed in double quotes. Inside the double quotes these escape sequences 142 | are supported to include special characters: 143 | 144 | - `\t` tab 145 | - `\r` charage return 146 | - `\n` line feed 147 | - `\"` double quote 148 | - `\\` single back slash 149 | 150 | * Lists. Lists (also known as arrays) are comma separated expressions enclosed in square brackets. For example 151 | an empty list is `[ ]`. A list of strings would look like this: `[ "a", "b", "c" ]`. 152 | 153 | Note that there is no literal representation of numbers. 154 | 155 | It may seem unnecessary to have these literals. After all a template such as this: 156 | 157 | ``` 158 | My name is $"Sam"$. 159 | ``` 160 | 161 | could more easily be given as: 162 | 163 | ``` 164 | My name is Sam. 165 | ``` 166 | 167 | Where these literals come in handy is in contexts such as passing template parameters, dictionaries, options and 168 | conditions each of which will be discussed below. 169 | 170 | ## Character Escape Expressions 171 | The following expressions are used to insert special characters in the output: 172 | 173 | * `$\ $` insert a single space 174 | * `$\n$` insert a new line. This will be converted to the appropriate line ending character or character sequence 175 | by the implementation according to the Operating System conventions. 176 | * `$\t$` insert a tab 177 | * `$\uhhhh$` insert a Unicode character where *hhhh* is the hex value of the Unicode character 178 | 179 | The expression `$\\$` will suppress the following new line. This allows inserting a new line in the template for 180 | better readability without including that new line in the output. 181 | 182 | todo give an example 183 | 184 | ## Comments 185 | Inside templates you can include comments as follows: 186 | 187 | ``` 188 | $!this is a comment!$ 189 | ``` 190 | 191 | Comments are not copied to the output. 192 | 193 | See section Group Files below for comments you can have outside of templates. 194 | 195 | ## Expressions 196 | This section describes each of the possible expressions. 197 | 198 | ### Attribute Expressions 199 | The simplest expression is an attribute expression. An attribute is simply the name or identifier of a data value. 200 | An attribute identifier starts with an alphabetic character or underscore followed by zero or more alphabetic characters 201 | underscores or digits. 202 | 203 | An attribute identifier matches this regular expression: `[a-zA-Z_][a-zA-Z_0-9]*`. Attribute identifiers are case 204 | sensitive. 205 | 206 | For example: 207 | 208 | ``` 209 | $hobbit$ 210 | ``` 211 | 212 | If the value of the `hobbit` attribute is "Bilbo Baggins" then the above expression results in: 213 | 214 | ``` 215 | Bilbo Baggins 216 | ``` 217 | 218 | If the value of an attribute is null then the result is an empty string (i.e. nothing). If the value of an attribute 219 | is an array the result is a concatenation of all the values in the array. So using JSON syntax for arrays, suppose the 220 | attribute `hobbits` contains `[ "Samwise", "Frodo", "Bilbo" ]` then the expression 221 | 222 | ``` 223 | $hobbits$ 224 | ``` 225 | 226 | results in 227 | 228 | ``` 229 | SamwiseFrodoBilbo 230 | ``` 231 | 232 | This may not seem very useful but when we get to Map expressions and Options you will see how to make better 233 | use of arrays. 234 | 235 | ### Property Reference 236 | If the attribute value is an object then you can access the values of its properties with this property reference 237 | syntax: *attribute*.*property* 238 | 239 | For example: 240 | 241 | ``` 242 | Mr $hobbit.firstName$ $hobbit.lastName$, 243 | ``` 244 | 245 | If the value of the `hobbit` attribute is an object with properties firstName and lastName and those properties have 246 | values "Bilbo" and "Baggins" respectively then the above template results in: 247 | 248 | ``` 249 | Mr Bilbo Baggins, 250 | ``` 251 | 252 | If the value of a property is also an object then the same dot property name syntax can be used to access properties 253 | of that object. This nesting of property references can be continued to any number of levels. 254 | 255 | For example if the hobbit object had a property called `name` and its value was an object with properties `first` and 256 | `last`. 257 | 258 | (Using JSON syntax it would look like this 259 | 260 | ``` 261 | { 262 | "name": { 263 | "first": "Bilbo", 264 | "last": "Baggins" 265 | } 266 | } 267 | ``` 268 | ) 269 | 270 | Then the hobbit's first name could be accessed in a template expression like so: 271 | 272 | ``` 273 | $hobbit.name.first$ 274 | ``` 275 | 276 | xxx attribute indirect 277 | xxx property indirect 278 | 279 | 280 | ### Include 281 | Often the output text requires repeated patterns of similar text. For example the footer at the bottom of each page 282 | in a book. To accomplish this StringTemplate allows you to break up your overall template system into any number of 283 | modular templates and include the output of one template in another. Templates are given names so they can be referenced 284 | in expressions. Templates can also define names for data values they expect to be given. These names are called 285 | formal arguments or simply arguments or parameters. The syntax for defining templates with their name and arguments 286 | is given later in the Template Definition Files and Group Files sections. This section is about how to include templates 287 | from other templates. The syntax looks very much like a function call in various programming languages. Indeed, similar 288 | terminology is sometimes used. Rather than saying template A includes template B you could say template A calls or 289 | invokes template B. 290 | 291 | Here is an over simplified example using HTML. Suppose your HTLM page is going to have a few buttons and you don't 292 | want to repeat the button markup each time. Make a template called button that takes parameters id and label. 293 | 294 | ``` 295 | 296 | ``` 297 | 298 | Then from the page template you can include as many buttons as you need. 299 | 300 | ``` 301 | ... 302 | 303 | ... 304 |
305 | $button("okBtn", "OK")$ $button("cancelBtn", "Cancel")$ 306 |
307 | ... 308 | ``` 309 | 310 | In this simple example the values passed to the button template are string literals. In a more realistic example 311 | the argument values would be attribute or property reference expressions. For example: 312 | 313 | ``` 314 | $button(button1.id, button1.label)$ 315 | ``` 316 | 317 | 318 | #### Argument Passing 319 | 320 | xxx todo 321 | 322 | ### Map 323 | xxx todo 324 | 325 | xxx rot 326 | 327 | ### Zip 328 | xxx todo 329 | 330 | ### Expression Options 331 | 332 | todo 333 | 334 | separator 335 | format 336 | null 337 | wrap 338 | anchor 339 | 340 | 341 | ### Anonymous Sub Templates 342 | 343 | todo 344 | 345 | ### Functions 346 | StringTemplate defines a small fixed number of functions that operate on data values. 347 | The syntax for calling functions is the same as template includes. In practice this doesn't cause problems because 348 | the number of functions is very small. If you want to name a template the same as a function name then you will need 349 | to call it with absolute path syntax. For example `$/first(arg1)$`. Why this works will become clear later. 350 | 351 | The following functions expect to be passed an array. That means they make the most sense when given an array but 352 | they are forgiving enough if you pass a different type of data. 353 | 354 | * `first(a)` Returns the first element of the input array `a` or `a` if it is not an array. 355 | 356 | * `last(a)` Returns the last element of the input array `a` or `a` if it is not an array. 357 | 358 | * `rest(a)` Returns all but the first element of the input array `a` or null if it is not an array. 359 | 360 | * `trunc(a)` Returns all but the last element of the input array `a` or null if it is not an array. 361 | 362 | * `length(a)` Returns length of the input array `a` or 0 if input is null or undefined or 1 if input is a scalar value or object. 363 | 364 | * `reverse(a)` Returns an array that is the same as the input array `a` but with the elements reversed or `a` if it is not an array. 365 | 366 | * `strip(a)` Returns an array that is the same as the input array `a` but with all null elements removed or `a` if it is not an array. 367 | 368 | The following functions expect to be given a string. It is a runtime error if the input argument is not a string. 369 | 370 | * `trim(str)` Return copy of the input `str` with leading and trailing white space characters removed. 371 | 372 | * `strlen(str)` Return the length of a string. 373 | 374 | xxx discus the two return values that are numbers 375 | 376 | ### Conditionals 377 | todo: 378 | if 379 | elseif 380 | else 381 | endif 382 | 383 | todo explain evaluation of conditions 384 | !, &&, || 385 | 386 | 387 | ## Template Definition Files 388 | 389 | todo 390 | 391 | xxx >> and %> escapes 392 | 393 | ## Raw Template Files 394 | 395 | todo 396 | 397 | ## Group Files 398 | 399 | todo 400 | 401 | ### Dictionaries 402 | 403 | todo 404 | 405 | ### Group inheritance 406 | 407 | todo 408 | 409 | ## Regions 410 | 411 | todo 412 | -------------------------------------------------------------------------------- /bin/stc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* 3 | [The "BSD licence"] 4 | Copyright (c) 2015, John Snyders 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions 9 | are met: 10 | 1. Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | 2. Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | 3. The name of the author may not be used to endorse or promote products 16 | derived from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | /* 30 | * stc 31 | * Commandline interface to the StringTemplate compiler 32 | */ 33 | "use strict"; 34 | 35 | var fs = require("fs"), 36 | path = require("path"), 37 | stc = require("../compiler/stc"), 38 | group = require("../compiler/group"); 39 | 40 | // 41 | // Command line parsing 42 | // 43 | // Keep these defaults in sync with default options in stc.js 44 | var argv = require('yargs') 45 | .require(1, "Missing required input-path argument") 46 | .option("ast", { 47 | alias: "a", 48 | default: false, 49 | type: "boolean", 50 | describe: "Output AST." 51 | }) 52 | .option("encoding", { 53 | alias: "e", 54 | default: "utf8", 55 | type: "string", 56 | describe: "File encoding." 57 | }) 58 | .option("delimiters", { 59 | alias: "s", 60 | default: group.DEFAULT_START_DELIMITER + group.DEFAULT_STOP_DELIMITER, 61 | type: "string", 62 | describe: "Start and stop characters that delimit template expressions." 63 | }) 64 | .option("output", { 65 | alias: "o", 66 | type: "string", 67 | default: "", 68 | describe: "Output file name." 69 | }) 70 | .option("minify", { 71 | alias: "m", 72 | default: false, 73 | type: "boolean", 74 | describe: "Minify output TODO." 75 | }) 76 | .option("raw", { 77 | alias: "r", 78 | default: false, 79 | type: "boolean", 80 | describe: "Template files with no declarations (raw)." 81 | }) 82 | .option("v", { 83 | alias: "verbose", 84 | default: false, 85 | type: "boolean", 86 | describe: "Log output about what the compiler is doing" 87 | }) 88 | .usage("Usage: $0 [options] input-path") 89 | .wrap(78) 90 | .version(stc.version, "version") 91 | .strict() 92 | .help("help", "Display usage") 93 | .alias("help", "h") 94 | .check(function(args) { 95 | if (args.delimiters.length !== 2) { 96 | throw "Error: delimiters option must be exactly two characters."; 97 | } 98 | return true; 99 | }) 100 | .argv; 101 | 102 | // xxx option to include a copyright string in generated file 103 | // xxx option to generate source map 104 | // xxx option to control how to access group prototype node export, AMD, browser global etc. 105 | // xxx option to also compile imports? or take list of inputs? 106 | 107 | var ext, stat, options, startTime, 108 | inputPath = argv._[0]; 109 | 110 | if (argv.verbose) { 111 | console.log("StringTemplate compiler version " + stc.version); 112 | } 113 | 114 | try { 115 | stat = fs.statSync(inputPath); 116 | } catch (ex) { 117 | if (ex.code === "ENOENT") { 118 | console.log("Error: No such file or directory '" + inputPath + "'."); 119 | } else if (ex.code === "EACCES") { 120 | console.log("Error: Permission denied to access '" + inputPath + "'."); 121 | } else { 122 | console.log(ex.message); 123 | } 124 | process.exit(1); 125 | } 126 | 127 | options = { 128 | encoding: argv.encoding, 129 | verbose: argv.verbose, 130 | outputAST: argv.ast, 131 | output: argv.output, 132 | minify: argv.minify, 133 | delimiterStartChar: argv.delimiters.charAt(0), 134 | delimiterStopChar: argv.delimiters.charAt(1) 135 | }; 136 | 137 | function callback(err) { 138 | if (err) { 139 | console.log(err.message); 140 | process.exit(1); 141 | } 142 | console.log("Completed in " + (Math.round((Date.now() - startTime) / 10) / 100) + " seconds."); 143 | process.exit(0); 144 | } 145 | 146 | startTime = Date.now(); 147 | 148 | if (stat.isDirectory()) { 149 | if (argv.raw) { 150 | stc.compileRawGroupDir(inputPath, options, callback); 151 | } else { 152 | stc.compileGroupDir(inputPath, options, callback); 153 | } 154 | } else { 155 | if (argv.raw) { 156 | console.log("Warning: Raw option ignored when compiling a single file."); // xxx why would that be? 157 | } 158 | ext = path.extname(inputPath); 159 | if (ext === group.GROUP_FILE_EXTENSION) { 160 | stc.compileGroupFile(inputPath, options, callback); 161 | } else if (ext === group.TEMPLATE_FILE_EXTENSION) { 162 | // xxx 163 | stc.compileGroupFile(inputPath, options, callback); 164 | } else { 165 | console.log("Error: Unrecognized file extension '" + inputPath + "'."); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /bin/stst: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* 3 | [The "BSD licence"] 4 | Copyright (c) 2015, John Snyders 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions 9 | are met: 10 | 1. Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | 2. Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | 3. The name of the author may not be used to endorse or promote products 16 | derived from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | // xxx consider rename this to st 31 | 32 | "use strict"; 33 | 34 | var fs = require("fs"), 35 | path = require("path"), 36 | stc = require("../compiler/stc.js"), 37 | cGroup = require("../compiler/group.js"), 38 | st = require("../lib/stRuntime"), 39 | w = require("../lib/autoIndentWriter"); 40 | 41 | function fileExists(file) { 42 | var stat; 43 | try { 44 | stat = fs.statSync(file); 45 | } catch (ex) { 46 | return false; 47 | } 48 | return stat.isFile(); 49 | } 50 | 51 | function processTemplate(group, template, options, data) { 52 | var t, g, p, v, writer, compiledGroup; 53 | 54 | compiledGroup = require(group); 55 | g = st.loadGroup(compiledGroup); 56 | writer = w.makeWriter(); 57 | 58 | t = g.getTemplate(template); 59 | for (p in data) { 60 | if (data.hasOwnProperty(p)) { 61 | t.add(p, data[p]); 62 | } 63 | } 64 | 65 | t.write(writer); 66 | 67 | // xxx write to file or stdout 68 | if (options.verbose /*xxx and going to stdout*/) { 69 | console.log("Template output:"); 70 | } 71 | // don't use console.log because it adds an extra new line at the end. 72 | process.stdout.write(writer.toString()); 73 | } 74 | 75 | 76 | function compile(inputPath, xxx) { 77 | var stat, ext; 78 | try { 79 | stat = fs.statSync(inputPath); 80 | } catch (ex) { 81 | if (ex.code === "ENOENT") { 82 | console.log("Error: No such file or directory '" + inputPath + "'."); 83 | } else if (ex.code === "EACCES") { 84 | console.log("Error: Permission denied to access '" + inputPath + "'."); 85 | } else { 86 | console.log(ex.message); 87 | } 88 | process.exit(1); 89 | } 90 | 91 | // xxx pass in options object and callback or get a promise back? 92 | // xxx output time perhaps 93 | options = { 94 | encoding: argv.encoding, 95 | verbose: argv.verbose, 96 | outputAST: argv.ast, 97 | minify: argv.minify, 98 | delimiterStartChar: argv.delimiters.charAt(0), 99 | delimiterStopChar: argv.delimiters.charAt(1) 100 | }; 101 | 102 | function callback(err) { 103 | if (err) { 104 | console.log(err.message); 105 | process.exit(1); 106 | } 107 | console.log("Completed in " + (Math.round((Date.now() - startTime) / 10) / 100) + " seconds."); 108 | process.exit(0); 109 | } 110 | 111 | startTime = Date.now(); 112 | 113 | if (stat.isDirectory()) { 114 | if (argv.raw) { 115 | stc.compileRawGroupDir(inputPath, options, callback); 116 | } else { 117 | stc.compileGroupDir(inputPath, options, callback); 118 | } 119 | } else { 120 | if (argv.raw) { 121 | console.log("Warning: raw option ignored when compiling a single file."); // xxx why would that be? 122 | } 123 | ext = path.extname(inputPath); 124 | if (ext === cGroup.GROUP_FILE_EXTENSION) { 125 | stc.compileGroupFile(inputPath, options, callback); 126 | } else if (ext === cGroup.TEMPLATE_FILE_EXTENSION) { 127 | // xxx 128 | stc.compileGroupFile(inputPath, options, callback); 129 | } 130 | } 131 | } 132 | 133 | 134 | // 135 | // Command line parsing 136 | // 137 | var argv = require('yargs') 138 | .require(1, "Missing required [group.]template argument") 139 | .option("encoding", { 140 | alias: "e", 141 | default: "utf8", 142 | type: "string", 143 | describe: "File encoding." 144 | }) 145 | .option("templates", { 146 | alias: "t", 147 | type: "string", 148 | default: "", 149 | describe: "Template directory where .stg or .st files are found. Default is current working directory." 150 | }) 151 | .option("output", { 152 | alias: "o", 153 | type: "string", 154 | default: "", 155 | describe: "Output file. Default is to use stdout" 156 | }) 157 | .option("noindent", { 158 | alias: "n", 159 | default: false, 160 | type: "boolean", 161 | describe: "Don't auto indent the output" 162 | }) 163 | .option("delimiters", { 164 | alias: "s", 165 | default: cGroup.DEFAULT_START_DELIMITER + cGroup.DEFAULT_STOP_DELIMITER, 166 | type: "string", 167 | describe: "Start and stop characters that delimit template expressions. Only needed if compiling." 168 | }) 169 | .option("raw", { 170 | alias: "r", 171 | default: false, 172 | type: "boolean", 173 | describe: "Template files with no declarations (raw). Only needed if compiling." 174 | }) 175 | .option("v", { 176 | alias: "verbose", 177 | default: false, 178 | type: "boolean", 179 | describe: "Log output about runtime processing of the template" 180 | }) 181 | .usage("Usage: $0 [options] [.]