├── .gitignore ├── README.md ├── examples └── viewmodel │ ├── .meteor │ ├── .finished-upgraders │ ├── .gitignore │ ├── .id │ ├── packages │ ├── platforms │ ├── release │ └── versions │ └── client │ ├── index.jade │ ├── lib │ ├── helpers.js │ └── register-bind.js │ └── views │ ├── paren.tpl.jade │ └── space.tpl.jade └── packages ├── jade-compiler ├── .gitignore ├── .npm │ ├── package │ │ ├── .gitignore │ │ ├── README │ │ └── npm-shrinkwrap.json │ └── plugin │ │ └── compileJade │ │ ├── .gitignore │ │ ├── README │ │ └── npm-shrinkwrap.json ├── .versions ├── lib │ ├── exports.js │ ├── lexer.js │ ├── parser.js │ └── transpilers.js ├── package.js ├── regex │ ├── attribute-args.regex │ └── component-args.regex ├── tests │ └── tests.js └── versions.json └── jade ├── .gitignore ├── .versions ├── package.js ├── plugin └── handler.js ├── tests ├── body.tpl.jade ├── img_tag_here.tpl.jade ├── match.html ├── match.jade ├── match.js ├── runtime.jade └── runtime.js └── versions.json /.gitignore: -------------------------------------------------------------------------------- 1 | *.sublime-workspace 2 | *.sublime-project 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | dalgard:jade 0.5.4 2 | ================== 3 | 4 | This package is a fork of [`mquandalle:jade`](https://github.com/mquandalle/meteor-jade) and will be kept in sync with the original. 5 | 6 | Hopefully, the two packages can be merged at some point. 7 | 8 | #### Install 9 | 10 | `meteor add dalgard:jade` 11 | 12 | #### Rationale 13 | 14 | The latest development on the `mquandalle:jade` package was on 29 April 2015. Judging from the lack of responsiveness in the issues forum, it looks like the project have been set on stand-by. 15 | 16 | Until development is resumed, this package may improve things a bit in some areas (especially in relation to the [`dalgard:viewmodel`](https://github.com/dalgard/meteor-viewmodel/) package). 17 | 18 | A live version of `/examples/viewmodel` can be found [here](http://dalgard-jade.meteor.com/). 19 | 20 | 21 | ## Changes 22 | 23 | Although only a few lines of code have been added to the package, they enable some useful features. 24 | 25 | Like the existing syntax, the new syntax comes in two variants – space separated or with parentheses. I recommend using the parenthesized version, since this is the only available style for attribute helpers. 26 | 27 | **Note:** The parenthesis format for helper arguments may also be used after includes, components, and built-ins (`if helper(args)`), as long as they don't span multiple lines. 28 | 29 | #### Arguments in interpolation 30 | 31 | Positional and keyword arguments have been missing from Jade's interpolation syntax, but may now be used in one of the two mentioned forms, alleviating the need for Blaze syntax: 32 | 33 | ```jade 34 | body 35 | // Space separated version – similar to Blaze 36 | | Hello #{person name prefix='Lord'} 37 | 38 | // Parenthesis version – similar to attribute helpers 39 | | Hello #{person(name prefix='Lord')} 40 | ``` 41 | 42 | #### Arguments in attributes 43 | 44 | Positional and keyword arguments can be passed to helpers that are used in attributes: 45 | 46 | ```jade 47 | input(type='text' placeholder=person(name prefix='Lord')) 48 | ``` 49 | 50 | #### Dollar sign attributes 51 | 52 | Dynamic attributes were added to the Meteor version of Jade using the `$dyn` syntax: 53 | 54 | ```jade 55 | input(type='text' $dyn=bind('value: value')) 56 | ``` 57 | 58 | This package introduces the concept of using a dollar sign (`$`) in front of an attribute name as a shorthand for designating a dynamic attribute helper. These two examples are equivalent: 59 | 60 | ```jade 61 | div($attr) 62 | ``` 63 | 64 | ```html 65 |
66 | ``` 67 | 68 | If a value is set on the attribute, it becomes the first positional argument of the helper. Consequently, the `$dyn` example may be rewritten in two ways: 69 | 70 | ```jade 71 | input(type='text' $bind='value: value') 72 | ``` 73 | 74 | Or, when more arguments are needed: 75 | 76 | ```jade 77 | input(type='text' $bind('value: value' throttle=500)) 78 | ``` 79 | 80 | #### @index 81 | 82 | The special `@index` variable inside `each` loops may now be used in interpolation or as an argument to a helper. 83 | 84 | The only limitation is direct attribute assignment, which should be written as `attr='#{@index}'`, not as `attr=@index`. 85 | 86 | 87 | ## Compatibility 88 | 89 | So far, I these improvements are fully backwards compatible, since the added syntax previously resulted in errors (with the exception of `$dyn`, which this package leaves untouched). 90 | 91 | 92 | ## History 93 | 94 | - 0.5.4 – Fixed missing @index variable and `Uncaught TypeError: Cannot read property 'nodeType' of undefined` 95 | - 0.5.3 – Added tests and fixed corner case with parenthesis syntax 96 | - 0.5.2 – Bug fix: Broken multiline component arguments. 97 | - 0.5.1 – Extended parenthesis syntax to includes, components, and built-ins. 98 | - 0.5.0 – Arguments for attributes and interpolation. -------------------------------------------------------------------------------- /examples/viewmodel/.meteor/.finished-upgraders: -------------------------------------------------------------------------------- 1 | # This file contains information which helps Meteor properly upgrade your 2 | # app when you run 'meteor update'. You should check it into version control 3 | # with your project. 4 | 5 | notices-for-0.9.0 6 | notices-for-0.9.1 7 | 0.9.4-platform-file 8 | notices-for-facebook-graph-api-2 9 | 1.2.0-standard-minifiers-package 10 | 1.2.0-meteor-platform-split 11 | 1.2.0-cordova-changes 12 | 1.2.0-breaking-changes 13 | -------------------------------------------------------------------------------- /examples/viewmodel/.meteor/.gitignore: -------------------------------------------------------------------------------- 1 | local 2 | -------------------------------------------------------------------------------- /examples/viewmodel/.meteor/.id: -------------------------------------------------------------------------------- 1 | # This file contains a token that is unique to your project. 2 | # Check it into your repository along with the rest of this directory. 3 | # It can be used for purposes such as: 4 | # - ensuring you don't accidentally deploy one app on top of another 5 | # - providing package authors with aggregated statistics 6 | 7 | 21o36snkarto1wxddzm 8 | -------------------------------------------------------------------------------- /examples/viewmodel/.meteor/packages: -------------------------------------------------------------------------------- 1 | # Meteor packages used by this project, one per line. 2 | # Check this file (and the other files in this directory) into your repository. 3 | # 4 | # 'meteor add' and 'meteor remove' will edit this file for you, 5 | # but you can also edit it by hand. 6 | 7 | standard-minifiers 8 | ecmascript 9 | meteor-base 10 | mobile-experience 11 | blaze-html-templates 12 | reload 13 | spacebars 14 | jquery 15 | underscore 16 | 17 | dalgard:jade 18 | dalgard:viewmodel 19 | -------------------------------------------------------------------------------- /examples/viewmodel/.meteor/platforms: -------------------------------------------------------------------------------- 1 | server 2 | browser 3 | -------------------------------------------------------------------------------- /examples/viewmodel/.meteor/release: -------------------------------------------------------------------------------- 1 | METEOR@1.2.0.2 2 | -------------------------------------------------------------------------------- /examples/viewmodel/.meteor/versions: -------------------------------------------------------------------------------- 1 | autoupdate@1.2.3 2 | babel-compiler@5.8.24_1 3 | babel-runtime@0.1.4 4 | base64@1.0.4 5 | binary-heap@1.0.4 6 | blaze@2.1.3 7 | blaze-html-templates@1.0.1 8 | blaze-tools@1.0.4 9 | boilerplate-generator@1.0.4 10 | caching-compiler@1.0.0 11 | caching-html-compiler@1.0.2 12 | callback-hook@1.0.4 13 | check@1.0.6 14 | dalgard:jade@0.5.4_1 15 | dalgard:jade-compiler@0.5.4_1 16 | dalgard:reactive-map@0.1.0 17 | dalgard:viewmodel@0.9.1 18 | ddp@1.2.2 19 | ddp-client@1.2.1 20 | ddp-common@1.2.1 21 | ddp-server@1.2.1 22 | deps@1.0.9 23 | diff-sequence@1.0.1 24 | ecmascript@0.1.5 25 | ecmascript-collections@0.1.6 26 | ejson@1.0.7 27 | fastclick@1.0.7 28 | geojson-utils@1.0.4 29 | hot-code-push@1.0.0 30 | html-tools@1.0.5 31 | htmljs@1.0.5 32 | http@1.1.1 33 | id-map@1.0.4 34 | jquery@1.11.4 35 | launch-screen@1.0.4 36 | livedata@1.0.15 37 | logging@1.0.8 38 | meteor@1.1.9 39 | meteor-base@1.0.1 40 | minifiers@1.1.7 41 | minimongo@1.0.10 42 | mobile-experience@1.0.1 43 | mobile-status-bar@1.0.6 44 | mongo@1.1.2 45 | mongo-id@1.0.1 46 | npm-mongo@1.4.39_1 47 | observe-sequence@1.0.7 48 | ordered-dict@1.0.4 49 | promise@0.5.0 50 | random@1.0.4 51 | reactive-dict@1.1.2 52 | reactive-var@1.0.6 53 | reload@1.1.4 54 | retry@1.0.4 55 | routepolicy@1.0.6 56 | sha@1.0.4 57 | spacebars@1.0.7 58 | spacebars-compiler@1.0.7 59 | standard-minifiers@1.0.1 60 | stevezhu:lodash@3.10.1 61 | templating@1.1.4 62 | templating-tools@1.0.0 63 | tracker@1.0.9 64 | ui@1.0.8 65 | underscore@1.0.4 66 | url@1.0.5 67 | webapp@1.2.2 68 | webapp-hashing@1.0.5 69 | -------------------------------------------------------------------------------- /examples/viewmodel/client/index.jade: -------------------------------------------------------------------------------- 1 | body 2 | +space color='green' 3 | +paren(color='blue') 4 | -------------------------------------------------------------------------------- /examples/viewmodel/client/lib/helpers.js: -------------------------------------------------------------------------------- 1 | Template.registerHelper("$name", numeral => "Name " + numeral); 2 | 3 | Template.registerHelper("person", (name, kw) => { 4 | let intro = "Hi, my name is " + name; 5 | 6 | if (kw && kw.hash) 7 | intro += " and my favorite color is " + kw.hash.color; 8 | 9 | return intro; 10 | }); 11 | 12 | Template.registerHelper("isNonEmpty", function (arg) { 13 | return _.isString(arg) && !!arg; 14 | }); 15 | -------------------------------------------------------------------------------- /examples/viewmodel/client/lib/register-bind.js: -------------------------------------------------------------------------------- 1 | ViewModel.registerHelper("bind"); 2 | -------------------------------------------------------------------------------- /examples/viewmodel/client/views/paren.tpl.jade: -------------------------------------------------------------------------------- 1 | // Parenthesis style 2 | div 3 | input(type='text' placeholder=$name('two (throttled)') $bind('value: name2' throttle=1500)) 4 | 5 | if isNonEmpty(name2) 6 | | #{person(name2 color=color)} 7 | -------------------------------------------------------------------------------- /examples/viewmodel/client/views/space.tpl.jade: -------------------------------------------------------------------------------- 1 | // Space separated style 2 | div 3 | input(type='text' placeholder=$name('one') $bind='value: name1') 4 | 5 | if isNonEmpty name1 6 | | #{person name1 color=color} 7 | -------------------------------------------------------------------------------- /packages/jade-compiler/.gitignore: -------------------------------------------------------------------------------- 1 | .build* 2 | -------------------------------------------------------------------------------- /packages/jade-compiler/.npm/package/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /packages/jade-compiler/.npm/package/README: -------------------------------------------------------------------------------- 1 | This directory and the files immediately inside it are automatically generated 2 | when you change this package's NPM dependencies. Commit the files in this 3 | directory (npm-shrinkwrap.json, .gitignore, and this README) to source control 4 | so that others run the same versions of sub-dependencies. 5 | 6 | You should NOT check in the node_modules directory that Meteor automatically 7 | creates; if you are using git, the .gitignore file tells git to ignore it. 8 | -------------------------------------------------------------------------------- /packages/jade-compiler/.npm/package/npm-shrinkwrap.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "jade": { 4 | "version": "https://github.com/mquandalle/jade/tarball/f3f956fa1031e05f85be7bc7b67f12e9ec80ba37", 5 | "dependencies": { 6 | "commander": { 7 | "version": "2.1.0" 8 | }, 9 | "mkdirp": { 10 | "version": "0.3.5" 11 | }, 12 | "transformers": { 13 | "version": "2.1.0", 14 | "dependencies": { 15 | "promise": { 16 | "version": "2.0.0", 17 | "dependencies": { 18 | "is-promise": { 19 | "version": "1.0.1" 20 | } 21 | } 22 | }, 23 | "css": { 24 | "version": "1.0.8", 25 | "dependencies": { 26 | "css-parse": { 27 | "version": "1.0.4" 28 | }, 29 | "css-stringify": { 30 | "version": "1.0.5" 31 | } 32 | } 33 | }, 34 | "uglify-js": { 35 | "version": "2.2.5", 36 | "dependencies": { 37 | "source-map": { 38 | "version": "0.1.40", 39 | "dependencies": { 40 | "amdefine": { 41 | "version": "0.1.0" 42 | } 43 | } 44 | }, 45 | "optimist": { 46 | "version": "0.3.7", 47 | "dependencies": { 48 | "wordwrap": { 49 | "version": "0.0.2" 50 | } 51 | } 52 | } 53 | } 54 | } 55 | } 56 | }, 57 | "character-parser": { 58 | "version": "1.2.0" 59 | }, 60 | "monocle": { 61 | "version": "1.1.51", 62 | "dependencies": { 63 | "readdirp": { 64 | "version": "0.2.5", 65 | "dependencies": { 66 | "minimatch": { 67 | "version": "2.0.1", 68 | "dependencies": { 69 | "brace-expansion": { 70 | "version": "1.0.0", 71 | "dependencies": { 72 | "balanced-match": { 73 | "version": "0.2.0" 74 | }, 75 | "concat-map": { 76 | "version": "0.0.0" 77 | } 78 | } 79 | } 80 | } 81 | } 82 | } 83 | } 84 | } 85 | }, 86 | "with": { 87 | "version": "3.0.1", 88 | "dependencies": { 89 | "uglify-js": { 90 | "version": "2.4.15", 91 | "dependencies": { 92 | "async": { 93 | "version": "0.2.10" 94 | }, 95 | "source-map": { 96 | "version": "0.1.34", 97 | "dependencies": { 98 | "amdefine": { 99 | "version": "0.1.0" 100 | } 101 | } 102 | }, 103 | "optimist": { 104 | "version": "0.3.7", 105 | "dependencies": { 106 | "wordwrap": { 107 | "version": "0.0.2" 108 | } 109 | } 110 | }, 111 | "uglify-to-browserify": { 112 | "version": "1.0.2" 113 | } 114 | } 115 | } 116 | } 117 | }, 118 | "constantinople": { 119 | "version": "2.0.1", 120 | "dependencies": { 121 | "uglify-js": { 122 | "version": "2.4.15", 123 | "dependencies": { 124 | "async": { 125 | "version": "0.2.10" 126 | }, 127 | "source-map": { 128 | "version": "0.1.34", 129 | "dependencies": { 130 | "amdefine": { 131 | "version": "0.1.0" 132 | } 133 | } 134 | }, 135 | "optimist": { 136 | "version": "0.3.7", 137 | "dependencies": { 138 | "wordwrap": { 139 | "version": "0.0.2" 140 | } 141 | } 142 | }, 143 | "uglify-to-browserify": { 144 | "version": "1.0.2" 145 | } 146 | } 147 | } 148 | } 149 | } 150 | } 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /packages/jade-compiler/.npm/plugin/compileJade/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /packages/jade-compiler/.npm/plugin/compileJade/README: -------------------------------------------------------------------------------- 1 | This directory and the files immediately inside it are automatically generated 2 | when you change this package's NPM dependencies. Commit the files in this 3 | directory (npm-shrinkwrap.json, .gitignore, and this README) to source control 4 | so that others run the same versions of sub-dependencies. 5 | 6 | You should NOT check in the node_modules directory that Meteor automatically 7 | creates; if you are using git, the .gitignore file tells git to ignore it. 8 | -------------------------------------------------------------------------------- /packages/jade-compiler/.npm/plugin/compileJade/npm-shrinkwrap.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "jade": { 4 | "version": "https://github.com/mquandalle/jade/tarball/f3f956fa1031e05f85be7bc7b67f12e9ec80ba37", 5 | "dependencies": { 6 | "commander": { 7 | "version": "2.1.0" 8 | }, 9 | "mkdirp": { 10 | "version": "0.3.5" 11 | }, 12 | "transformers": { 13 | "version": "2.1.0", 14 | "dependencies": { 15 | "promise": { 16 | "version": "2.0.0", 17 | "dependencies": { 18 | "is-promise": { 19 | "version": "1.0.1" 20 | } 21 | } 22 | }, 23 | "css": { 24 | "version": "1.0.8", 25 | "dependencies": { 26 | "css-parse": { 27 | "version": "1.0.4" 28 | }, 29 | "css-stringify": { 30 | "version": "1.0.5" 31 | } 32 | } 33 | }, 34 | "uglify-js": { 35 | "version": "2.2.5", 36 | "dependencies": { 37 | "source-map": { 38 | "version": "0.1.40", 39 | "dependencies": { 40 | "amdefine": { 41 | "version": "0.1.0" 42 | } 43 | } 44 | }, 45 | "optimist": { 46 | "version": "0.3.7", 47 | "dependencies": { 48 | "wordwrap": { 49 | "version": "0.0.2" 50 | } 51 | } 52 | } 53 | } 54 | } 55 | } 56 | }, 57 | "character-parser": { 58 | "version": "1.2.0" 59 | }, 60 | "monocle": { 61 | "version": "1.1.51", 62 | "dependencies": { 63 | "readdirp": { 64 | "version": "0.2.5", 65 | "dependencies": { 66 | "minimatch": { 67 | "version": "2.0.0", 68 | "dependencies": { 69 | "brace-expansion": { 70 | "version": "1.0.0", 71 | "dependencies": { 72 | "balanced-match": { 73 | "version": "0.2.0" 74 | }, 75 | "concat-map": { 76 | "version": "0.0.0" 77 | } 78 | } 79 | } 80 | } 81 | } 82 | } 83 | } 84 | } 85 | }, 86 | "with": { 87 | "version": "3.0.1", 88 | "dependencies": { 89 | "uglify-js": { 90 | "version": "2.4.15", 91 | "dependencies": { 92 | "async": { 93 | "version": "0.2.10" 94 | }, 95 | "source-map": { 96 | "version": "0.1.34", 97 | "dependencies": { 98 | "amdefine": { 99 | "version": "0.1.0" 100 | } 101 | } 102 | }, 103 | "optimist": { 104 | "version": "0.3.7", 105 | "dependencies": { 106 | "wordwrap": { 107 | "version": "0.0.2" 108 | } 109 | } 110 | }, 111 | "uglify-to-browserify": { 112 | "version": "1.0.2" 113 | } 114 | } 115 | } 116 | } 117 | }, 118 | "constantinople": { 119 | "version": "2.0.1", 120 | "dependencies": { 121 | "uglify-js": { 122 | "version": "2.4.15", 123 | "dependencies": { 124 | "async": { 125 | "version": "0.2.10" 126 | }, 127 | "source-map": { 128 | "version": "0.1.34", 129 | "dependencies": { 130 | "amdefine": { 131 | "version": "0.1.0" 132 | } 133 | } 134 | }, 135 | "optimist": { 136 | "version": "0.3.7", 137 | "dependencies": { 138 | "wordwrap": { 139 | "version": "0.0.2" 140 | } 141 | } 142 | }, 143 | "uglify-to-browserify": { 144 | "version": "1.0.2" 145 | } 146 | } 147 | } 148 | } 149 | } 150 | } 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /packages/jade-compiler/.versions: -------------------------------------------------------------------------------- 1 | babel-compiler@5.8.24_1 2 | babel-runtime@0.1.4 3 | base64@1.0.4 4 | binary-heap@1.0.4 5 | blaze@2.1.3 6 | blaze-tools@1.0.4 7 | boilerplate-generator@1.0.4 8 | callback-hook@1.0.4 9 | check@1.0.6 10 | dalgard:jade-compiler@0.5.4_1 11 | ddp@1.2.2 12 | ddp-client@1.2.1 13 | ddp-common@1.2.1 14 | ddp-server@1.2.1 15 | deps@1.0.9 16 | diff-sequence@1.0.1 17 | ecmascript@0.1.5 18 | ecmascript-collections@0.1.6 19 | ejson@1.0.7 20 | geojson-utils@1.0.4 21 | html-tools@1.0.5 22 | htmljs@1.0.5 23 | id-map@1.0.4 24 | jquery@1.11.4 25 | local-test:dalgard:jade-compiler@0.5.4_1 26 | logging@1.0.8 27 | meteor@1.1.9 28 | minifiers@1.1.7 29 | minimongo@1.0.10 30 | mongo@1.1.2 31 | mongo-id@1.0.1 32 | npm-mongo@1.4.39_1 33 | observe-sequence@1.0.7 34 | ordered-dict@1.0.4 35 | promise@0.5.0 36 | random@1.0.4 37 | reactive-var@1.0.6 38 | retry@1.0.4 39 | routepolicy@1.0.6 40 | spacebars@1.0.7 41 | spacebars-compiler@1.0.7 42 | tinytest@1.0.6 43 | tracker@1.0.9 44 | ui@1.0.8 45 | underscore@1.0.4 46 | webapp@1.2.2 47 | webapp-hashing@1.0.5 48 | -------------------------------------------------------------------------------- /packages/jade-compiler/lib/exports.js: -------------------------------------------------------------------------------- 1 | var codeGen = SpacebarsCompiler.codeGen; 2 | 3 | JadeCompiler = { 4 | parse: function(source, options) { 5 | options = options || {}; 6 | var parser, Compiler; 7 | 8 | try { 9 | parser = new Parser(source, options.filename || "", { lexer: Lexer }); 10 | Compiler = (options.fileMode) ? FileCompiler : TemplateCompiler; 11 | return new Compiler(parser.parse(), options).compile(); 12 | 13 | } catch (err) { 14 | throw err; 15 | } 16 | }, 17 | 18 | compile: function(source) { 19 | var ast = JadeCompiler.parse(source, { fileMode: false }); 20 | return codeGen(ast); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /packages/jade-compiler/lib/lexer.js: -------------------------------------------------------------------------------- 1 | // We modify a bit the Jade grammar in order to define both user defined and 2 | // build-in components. 3 | // 4 | // We save components as "Mixins" nodes. That's a good host because like 5 | // "Mixins", components have: 6 | // 1. a name 7 | // 2. some arguments (optionnal) 8 | 9 | Lexer = Npm.require("jade").Lexer; 10 | 11 | 12 | var component_re = /^(\+[\.\w-]+|if|unless|else if|else|with|each)\b/, 13 | plus_re = /^\+/, 14 | args_re = /^\((.*)\)\s*$/m, 15 | singleline_re = /([^\n]*)/, 16 | parens_re = /(?:(['"])\1|(['"]).*?[^\\]\2)|((?:\.{1,2}\/)?[\w\.-]+)\(((?:(['"])\5|(['"]).*?[^\\]\6|[^)]*?(['"])\7|[^)]*?(['"]).*?[^\\]\8)*[^)]*)\)/g, 17 | multiline_re = /^\(((?:(['"])\2|(['"]).*?[^\\]\3|[^)]*[^\\](['"])\4|[^)]*?[^\\](['"]).*[^\\]\5)*[^)]*?)\)/, 18 | newline_re = /\n/g, 19 | attrs_re = /(?:(['"])\1|(['"]).*?[^\\]\2)|(!?=?[$=])((?:\.{1,2}\/)?[\w\.-]+)\(((?:(['"])\6|(['"]).*?[^\\]\7|[^)]*?(['"])\8|[^)]*?(['"]).*?[^\\]\9)*[^)]*)\)/g, 20 | quote_re = /(^|[^\\])'/g; 21 | 22 | function parens_replace(match, $1, $2, helper, args) { 23 | if (_.isString($1) || _.isString($2)) 24 | return match; 25 | 26 | return helper + " " + args; 27 | } 28 | 29 | function attrs_replace(match, $1, $2, prefix, helper, args) { 30 | if (_.isString($1) || _.isString($2)) 31 | return match; 32 | 33 | var helper_pre = ""; 34 | 35 | if (prefix.slice(-2) == "=$") { 36 | prefix = prefix.slice(0, -1); 37 | helper_pre = "$"; 38 | } 39 | 40 | var begin = (prefix === "$" ? "$dyn='" : (prefix === "!=" ? "!='{" : "='")) + "{{", 41 | end = "}}" + (prefix === "!=" ? "}'" : "'"); 42 | 43 | return begin + helper_pre + helper + " " + args.replace(quote_re, "$1\\'") + end; 44 | } 45 | 46 | 47 | // Blaze helper used as a component 48 | Lexer.prototype.blazeComponent = function () { 49 | var key = this.input.match(component_re); 50 | 51 | if (key) { 52 | this.consume(key[0].length); 53 | 54 | var tok = this.tok("mixin", key[1].replace(plus_re, "")), 55 | is_paren = this.input.charAt(0) === "(", 56 | args = this.input.match(args_re), 57 | is_singleline = !is_paren || args; 58 | 59 | if (is_singleline) { 60 | if (!args) 61 | args = this.input.match(singleline_re); 62 | 63 | tok.args = args[1].replace(parens_re, parens_replace); 64 | } 65 | else { 66 | args = this.input.match(multiline_re); 67 | 68 | if (args) 69 | tok.args = args[1].replace(newline_re, ""); 70 | } 71 | 72 | this.consume(args[0].length); 73 | 74 | return tok; 75 | } 76 | }; 77 | 78 | 79 | // Super method 80 | var attrs = Lexer.prototype.attrs; 81 | 82 | // Make attributes accept Blaze helper expressions inside parentheses 83 | Lexer.prototype.attrs = function (push) { 84 | if (this.input.charAt(0) === "(") { 85 | var range = this.bracketExpression(), 86 | attrs_str = this.input.slice(range.start, range.end); 87 | 88 | attrs_str = attrs_str.replace(attrs_re, attrs_replace); 89 | 90 | this.input = "(" + attrs_str + this.input.slice(range.end); 91 | } 92 | 93 | return attrs.call(this, push); 94 | }; 95 | 96 | 97 | // Super 98 | var next = Lexer.prototype.next; 99 | 100 | Lexer.prototype.next = function () { 101 | return this.blazeComponent() || next.call(this); 102 | }; 103 | -------------------------------------------------------------------------------- /packages/jade-compiler/lib/parser.js: -------------------------------------------------------------------------------- 1 | // XXX Remove this file when PR jade#1392 is merged and released 2 | // https://github.com/visionmedia/jade/pull/1392 3 | // 4 | // The goal of the PR is to add a `lexer` option to the parser so that the user 5 | // could provide his own customized Lexer object. 6 | 7 | Parser = function Parser(str, filename, options){ 8 | //Strip any UTF-8 BOM off of the start of `str`, if it exists. 9 | this.input = str.replace(/^\uFEFF/, ''); 10 | this.filename = filename; 11 | this.blocks = {}; 12 | this.mixins = {}; 13 | this.options = options || {}; 14 | //Hello 47 | world
48 | 49 |Hello.
50 | 51 | 52 | 53 | 54 | 55 | 56 | {{helper}} 57 | 58 |Hello {{world}}
59 | 60 |Hello {{{world}}}
61 | 62 |{{func arg1 arg2}}
63 | 64 |Hello {{foo}} {{bar}} {{baz}}
65 | 66 | 67 | 68 | 69 | 70 | 71 | {{#if weAreTheWorld}}hello world{{/if}} 72 | 73 | {{#if helper arg1 arg2}}hello world{{/if}} 74 | 75 | {{#if weAreTheWorld}}hello world{{else}}we are the children{{/if}} 76 | 77 | {{#if Template.subscriptionsReady}}ready{{else}}loading{{/if}} 78 | 79 | {{#with user}}{{name}}{{/with}} 80 | 81 | {{#each users}}{{name}}{{/each}} 82 | 83 | {{#each u in users}}{{u.name}}{{/each}} 84 | 85 | {{#each users}}{{@index}}{{/each}} 86 | 87 | {{#each u in users}}{{@index}}{{/each}} 88 | 89 | {{#each users}}{{helper @index}}{{/each}} 90 | 91 | {{#each users}}{{> myTemplate index=@index}}{{/each}} 92 | 93 | {{#each users}}{{#if helper @index}}{{@index}}{{/if}}{{/each}} 94 | 95 | {{#each users}}{{/each}} 96 | 97 | 98 | {{#if condA}}This is a block 121 | of text on multiple 122 | lines. 123 |
124 | 125 | 128 | 129 | {{#markdown}}This is my email: 130 | {{/markdown}} 131 | 132 | {{#markdown}}This is my email: {{email}}{{/markdown}} 133 | 134 | 135 | 136 | 137 | {{> myTemplate helper arg1}} 138 | 139 | {{> myTemplate helper arg1 key1=arg2}} 140 | 141 | {{> myTemplate helper '()'}} 142 | 143 | {{> myTemplate helper arg1 key1=')'}} 144 | 145 | {{helper arg1 key1=arg2}} 146 | 147 | {{helper '}' key1=arg1}} 148 | 149 | {{helper arg1 key1=arg2}} 150 | 151 | {{helper '})' key1=arg1}} 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | -------------------------------------------------------------------------------- /packages/jade/tests/match.jade: -------------------------------------------------------------------------------- 1 | //- HTML tags 2 | 3 | template(name='match-jade-singleTagAttribute') 4 | input(required) 5 | 6 | template(name='match-jade-nonStandardTag') 7 | qr(text="jade rocks") 8 | 9 | template(name='match-jade-tagAttributeWithTextValue-1') 10 | input(type='text') 11 | 12 | template(name='match-jade-tagAttributeWithTextValue-2') 13 | input(type="text") 14 | 15 | template(name='match-jade-emptyTagAttribute') 16 | input(placehover="") 17 | 18 | template(name='match-jade-parentContextInTagAttribute') 19 | a(href="/users/{{../userId}}/") 20 | 21 | template(name='match-jade-helperInTagAttribute') 22 | input(required=isRequired) 23 | 24 | template(name='match-jade-dynamicTagAttributes') 25 | input($dyn=attrs) 26 | 27 | template(name='match-jade-dynamicTagAttributesWithArgs') 28 | input($dyn="{{attrs arg1 arg2}}") 29 | 30 | template(name='match-jade-multipleTagAttributes-1') 31 | input(type="password" required=isRequired autofocus $dyn=attrs) 32 | 33 | template(name='match-jade-multipleTagAttributes-2') 34 | input(type="password", required=isRequired, autofocus, $dyn=attrs) 35 | 36 | template(name='match-jade-charRef') 37 | script(src="//maps.googleapis.com/maps/api/js?language=ru®ion=ru") 38 | 39 | template(name='match-jade-classNames') 40 | div.class1.class2(class="class3") 41 | 42 | template(name='match-jade-classNamesMerging') 43 | .sixteen.wide.mobile.twelve.wide.computer.column 44 | 45 | template(name='match-jade-multipleIdentifiers') 46 | #id1.class1(class="class2" class="class3 class4") 47 | 48 | template(name='match-jade-inlineColonTags') 49 | form: input 50 | 51 | template(name='match-jade-inlineBracketsTags') 52 | div styling #[span #[span tags]] 53 | 54 | template(name='match-jade-iframe') 55 | iframe(width="13" height="37") 56 | 57 | template(name='match-jade-styleTag') 58 | style. 59 | body { 60 | background-color: {{themeColor}}; 61 | } 62 | 63 | template(name='match-jade-empty-1') 64 | 65 | template(name='match-jade-empty-2') 66 | = "" 67 | 68 | template(name='match-jade-justText-1') 69 | | Hello world 70 | 71 | template(name='match-jade-justText-2') 72 | = "Hello world" 73 | 74 | template(name='match-jade-justText-3') 75 | = 'Hello world' 76 | 77 | template(name='match-jade-justText-4') 78 | = 'Hello ' 79 | = 'world' 80 | 81 | template(name='match-jade-textWithEscapedQuote') 82 | = 'Je t\'aime' 83 | 84 | template(name='match-jade-multipleChildren') 85 | p 86 | | Hello 87 | | world 88 | 89 | template(name='match-jade-noSpaceBetweenChildren') 90 | p 91 | | Hello 92 | = "." 93 | 94 | 95 | //- Template helpers 96 | 97 | template(name='match-jade-tagHelper') 98 | textarea= textareaValue 99 | 100 | template(name='match-jade-justHelper') 101 | = helper 102 | 103 | template(name='match-jade-inlineHelper-1') 104 | p Hello #{world} 105 | 106 | template(name='match-jade-inlineHelper-2') 107 | p Hello {{world}} 108 | 109 | template(name='match-jade-unescapedInlineHelper-1') 110 | p Hello !{world} 111 | 112 | template(name='match-jade-unescapedInlineHelper-2') 113 | p Hello {{{world}}} 114 | 115 | template(name='match-jade-mustacheCall') 116 | p {{func arg1 arg2}} 117 | 118 | template(name='match-jade-multipleInlineHelpers') 119 | p Hello #{foo} #{bar} #{baz} 120 | 121 | template(name='match-jade-tagAttributeInterpolation') 122 | button(class="btn #{btnKind}") 123 | 124 | 125 | //- Built-in components 126 | 127 | template(name='match-jade-if-1') 128 | if weAreTheWorld 129 | | hello world 130 | 131 | template(name='match-jade-if-2') 132 | if(weAreTheWorld) 133 | | hello world 134 | 135 | template(name='match-jade-if-3') 136 | +if(weAreTheWorld) 137 | | hello world 138 | 139 | template(name='match-jade-if-4') 140 | +if weAreTheWorld 141 | | hello world 142 | 143 | template(name='match-jade-ifWithHelper-1') 144 | if helper arg1 arg2 145 | | hello world 146 | 147 | template(name='match-jade-ifWithHelper-2') 148 | if(helper arg1 arg2) 149 | | hello world 150 | 151 | template(name='match-jade-ifAndElse') 152 | if weAreTheWorld 153 | | hello world 154 | else 155 | | we are the children 156 | 157 | template(name='match-jade-ifSubscriptionReady') 158 | if Template.subscriptionsReady 159 | | ready 160 | else 161 | | loading 162 | 163 | template(name='match-jade-with') 164 | with user 165 | = name 166 | 167 | template(name='match-jade-each') 168 | each users 169 | = name 170 | 171 | template(name='match-jade-eachIn-1') 172 | each u in users 173 | = u.name 174 | 175 | template(name='match-jade-eachIn-2') 176 | +each(u in users) 177 | = u.name 178 | 179 | template(name='match-jade-eachIndex') 180 | each users 181 | | #{@index} 182 | 183 | template(name='match-jade-eachInIndex') 184 | each u in users 185 | | #{@index} 186 | 187 | template(name='match-jade-eachHelperIndex') 188 | each users 189 | | #{helper @index} 190 | 191 | template(name='match-jade-eachIncludeIndex') 192 | each users 193 | +myTemplate(index=@index) 194 | 195 | template(name='match-jade-eachIfIndex') 196 | each users 197 | if helper(@index) 198 | | #{@index} 199 | 200 | template(name='match-jade-eachAttributeIndex') 201 | each users 202 | input(index=helper(@index)) 203 | 204 | //- else if syntaxic sugar 205 | template(name='match-jade-elseif') 206 | if condA 207 | h1 hello A 208 | else if condB 209 | h2 hello B 210 | else if condC 211 | h3 hello C 212 | else 213 | h4 goodbye 214 | 215 | 216 | //- Components - templates inclusions 217 | 218 | template(name='match-jade-inclusion') 219 | +myTemplate 220 | 221 | template(name='match-jade-inclusionWithContent') 222 | +myTemplate 223 | | this is some {{content}} 224 | 225 | template(name='match-jade-contentsInclusion') 226 | +UI.content 227 | +UI.elseContent 228 | 229 | template(name='match-jade-inclusionWithPositionalArgs-1') 230 | +myTemplate(arg1 arg2) 231 | 232 | template(name='match-jade-inclusionWithPositionalArgs-2') 233 | +myTemplate(arg1 234 | arg2) 235 | 236 | template(name='match-jade-inclusionWithPositionalArgs-3') 237 | +myTemplate( 238 | arg1 239 | arg2 240 | ) 241 | 242 | template(name='match-jade-inclusionWithPositionalArgs-4') 243 | +myTemplate arg1 arg2 244 | 245 | template(name='match-jade-inclusionWithKeywordArgs-1') 246 | +myTemplate(key1=arg1 key2=arg2) 247 | 248 | template(name='match-jade-inclusionWithKeywordArgs-2') 249 | +myTemplate key1=arg1 key2=arg2 250 | 251 | template(name='match-jade-dynamicTemplate') 252 | +Template.dynamic(template=template) 253 | 254 | template(name='match-jade-callableInComponentArg-1') 255 | +Component test="moo()" 256 | 257 | template(name='match-jade-callableInComponentArg-2') 258 | +Component(test="moo()") 259 | 260 | 261 | //- Blocks of text 262 | 263 | template(name='match-jade-blockOfText') 264 | p. 265 | This is a block 266 | of text on multiple 267 | lines. 268 | 269 | template(name='match-jade-textareaContent') 270 | textarea. 271 | Inside a textarea 272 | tags shouldn't be interpreted 273 | 274 | template(name='match-jade-specialMarkdownComponent') 275 | +markdown 276 | This is my email: 277 | 278 | template(name='match-jade-specialMarkdownComponentWithHelper') 279 | +markdown 280 | This is my email: {{email}} 281 | //- end of template 282 | 283 | 284 | //- Added syntax in dalgard:jade 285 | 286 | template(name='match-jade-inclusionWithParenthesisPositionalArg') 287 | +myTemplate(helper(arg1)) 288 | 289 | template(name='match-jade-inclusionWithParenthesisPositionalAndKeywordArgs') 290 | +myTemplate helper(arg1 key1=arg2) 291 | 292 | template(name='match-jade-inclusionWithParenthesisPositionalArgsWithStringParen') 293 | +myTemplate(helper('()')) 294 | 295 | template(name='match-jade-inclusionWithParenthesisPositionalArgsWithStringParenAndKeyword') 296 | +myTemplate helper(arg1 key1=')') 297 | 298 | template(name='match-jade-extrapolationWithSpacesArgs') 299 | | #{helper arg1 key1=arg2} 300 | 301 | template(name='match-jade-extrapolationWithSpacesArgsWithStringBraces') 302 | | #{helper '}' key1=arg1} 303 | 304 | template(name='match-jade-extrapolationWithParenthesisArgs') 305 | | #{helper(arg1 key1=arg2)} 306 | 307 | template(name='match-jade-extrapolationWithParenthesisArgsWithStringBraces') 308 | | #{helper('})' key1=arg1)} 309 | 310 | template(name='match-jade-attributeWithHelperAndParenthesisArgs') 311 | input(type=helper(arg1 key1=arg2)) 312 | 313 | template(name='match-jade-attributeWithHelperAndParenthesisArgsEmptyString') 314 | input(type=helper("")) 315 | 316 | template(name='match-jade-attributeWithDollarHelperAndParenthesisArgs') 317 | input(type=$helper(arg1 key1=arg2)) 318 | 319 | template(name='match-jade-attributeWithParenthesisArgsWithStringParen') 320 | input(type=helper(')' key1="()")) 321 | 322 | template(name='match-jade-attributesWithParenthesisArgsWithStrings') 323 | input(type=helper1(key1='string') placeholder=helper2('string')) 324 | 325 | template(name='match-jade-attributeExtremeString') 326 | input(type='type=helper1(key1="string") placeholder=helper2("string")') 327 | 328 | template(name='match-jade-attributeExtremeStringDouble') 329 | input(type="type=helper1(key1='string') placeholder=helper2('string')") 330 | 331 | template(name='match-jade-attributeExtremeStringEscaped') 332 | input(type='type=helper1(key1=\'string\') placeholder=helper2(\'string\')') 333 | 334 | template(name='match-jade-dynamicAttributeWithDollarSign') 335 | input($attr) 336 | 337 | template(name='match-jade-dynamicAttributeWithDollarSignAndSingleArg') 338 | input($attr=arg) 339 | 340 | template(name='match-jade-dynamicAttributeWithDollarSignAndSingleDollarArg') 341 | input($attr=$arg) 342 | 343 | template(name='match-jade-dynamicAttributeWithDollarSignAndParenthesisArgs') 344 | input($attr(arg key1=')')) 345 | -------------------------------------------------------------------------------- /packages/jade/tests/match.js: -------------------------------------------------------------------------------- 1 | var removeLineComment = function (code) { 2 | var lineBreak = "\n"; 3 | return _.map(code.split(lineBreak), function (line) { 4 | return line.replace(/(.+?)\s*\/\/ [0-9]+/, "$1"); 5 | }).join(lineBreak); 6 | }; 7 | 8 | var tpl2txt = function(tplName) { 9 | var tpl = Template[tplName]; 10 | if (! tpl.renderFunction) 11 | throw Error("The template object doesn't have a render function"); 12 | return removeLineComment(tpl.renderFunction.toString()); 13 | }; 14 | 15 | Tinytest.add('Jade - Compiled template match Spacebars', function (test) { 16 | for (var jadeTplName in Template) { 17 | if (! jadeTplName.match(/^match-jade/)) 18 | continue; 19 | 20 | var parts = jadeTplName.split('-'); 21 | var testName = parts[2]; 22 | var testIndex = parts[3]; 23 | var description = testName + (testIndex ? " (" + testIndex + ")" : ""); 24 | var htmlTplName = ["match", "html", testName].join("-"); 25 | 26 | if (_.has(Template, htmlTplName)) 27 | test.equal(tpl2txt(jadeTplName), tpl2txt(htmlTplName), description); 28 | else 29 | test.fail("Missing template: " + htmlTplName); 30 | } 31 | }); 32 | -------------------------------------------------------------------------------- /packages/jade/tests/runtime.jade: -------------------------------------------------------------------------------- 1 | head 2 | meta(name="jadetest" content="1337") 3 | 4 | body(data-test="value") 5 | #myUniqueJadeIdentifier 6 | +insert_a_PRE_tag_here 7 | 8 | template(name="insert_a_PRE_tag_here") 9 | pre 10 | -------------------------------------------------------------------------------- /packages/jade/tests/runtime.js: -------------------------------------------------------------------------------- 1 | Tinytest.add('Jade - Head inclusion', function (test) { 2 | test.equal($('meta[name=jadetest]').attr('content'), '1337'); 3 | }); 4 | 5 | Tinytest.add('Jade - Runtime template insertion', function (test) { 6 | test.isNotNull(document.querySelector('#myUniqueJadeIdentifier pre', 7 | "it should insert a pre tag inside the body")); 8 | }); 9 | 10 | Tinytest.add('Jade - Runtime unwrapped template insertion', function (test) { 11 | test.isNotNull(document.querySelector('#UnwrappedTemplateUniqueIdentifier img', 12 | "it should insert an img tag inside the body")); 13 | }); 14 | 15 | Tinytest.add('Jade - Body attributes', function (test) { 16 | test.equal(document.body.dataset.test, 'value'); 17 | }); 18 | -------------------------------------------------------------------------------- /packages/jade/versions.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": [ 3 | [ 4 | "meteor", 5 | "1.1.3" 6 | ], 7 | [ 8 | "underscore", 9 | "1.0.1" 10 | ] 11 | ], 12 | "pluginDependencies": [ 13 | [ 14 | "compileJade", 15 | { 16 | "spacebars-compiler": "1.0.3", 17 | "mquandalle:jade-compiler": "0.4.1", 18 | "deps": "1.0.5", 19 | "tracker": "1.0.3", 20 | "html-tools": "1.0.2", 21 | "underscore": "1.0.1", 22 | "meteor": "1.1.3", 23 | "blaze-tools": "1.0.1", 24 | "htmljs": "1.0.2", 25 | "minifiers": "1.1.2" 26 | } 27 | ] 28 | ], 29 | "toolVersion": "meteor-tool@1.0.35", 30 | "format": "1.0" 31 | } --------------------------------------------------------------------------------