├── docs
├── en
│ ├── README.md
│ ├── book.json
│ ├── SUMMARY.md
│ ├── 2-quick-start.md
│ └── 1-about.md
├── ru
│ ├── README.md
│ ├── book.json
│ ├── SUMMARY.md
│ ├── 2-quick-start.md
│ └── 1-about.md
└── LANGS.md
├── bench
├── .gitignore
├── templates
│ ├── basic.bemhtml
│ └── basic.bemjson
├── prepare.sh
├── package.json
├── README.md
├── test.js
└── lib
│ └── compare.py
├── examples
├── source-maps
│ ├── demo1
│ │ ├── b1.bemhtml.js
│ │ ├── demo1.html
│ │ └── demo1.js
│ ├── demo3
│ │ ├── b3.bemhtml.js
│ │ ├── demo3.html
│ │ └── demo3.js
│ ├── demo2
│ │ ├── demo2.html
│ │ ├── demo2.js
│ │ └── tmpls-with-sourcemap.bemhtml.js
│ └── demo-in-nodejs
│ │ ├── b1.bemhtml.js
│ │ └── test-in-node.js
└── simple-page
│ ├── package.json
│ ├── index.js
│ ├── README.md
│ ├── data.js
│ ├── bemtree-templates.js
│ └── bemhtml-templates.js
├── scripts
└── update-authors.sh
├── migration
├── lib
│ ├── transformers
│ │ ├── __testfixtures__
│ │ │ ├── 0-func-to-simple-1.output.js
│ │ │ ├── 0-func-to-simple-2.input.js
│ │ │ ├── 0-func-to-simple-2.output.js
│ │ │ ├── 2-def-must-return-something-2.input.js
│ │ │ ├── 2-def-must-return-something-2.output.js
│ │ │ ├── 8-chain-mix-to-chain-addmix-1.input.js
│ │ │ ├── 8-chain-attrs-to-chain-addattrs-1.input.js
│ │ │ ├── 8-chain-attrs-to-chain-addattrs-1.output.js
│ │ │ ├── 8-chain-mix-to-chain-addmix-1.output.js
│ │ │ ├── 7-once-to-def-1.input.js
│ │ │ ├── 7-once-to-def-1.output.js
│ │ │ ├── 2-no-more-this-underscore-1.output.js
│ │ │ ├── 2-no-more-this-underscore-2.input.js
│ │ │ ├── 2-no-more-this-underscore-2.output.js
│ │ │ ├── 2-no-more-this-underscore-1.input.js
│ │ │ ├── 8-mix-to-addmix-1.input.js
│ │ │ ├── 5-api-changed-1.input.js
│ │ │ ├── 8-attrs-to-addattrs-1.input.js
│ │ │ ├── 8-attrs-to-addattrs-1.output.js
│ │ │ ├── 8-mix-to-addmix-1.output.js
│ │ │ ├── 5-api-changed-1.output.js
│ │ │ ├── 0-func-to-simple-1.input.js
│ │ │ ├── 0-func-to-simple-3.output.js
│ │ │ ├── 5-elemmatch-to-elem-match-1.input.js
│ │ │ ├── 7-thisisarray-to-arrayisarray-1.input.js
│ │ │ ├── 7-thisisarray-to-arrayisarray-1.output.js
│ │ │ ├── 5-elemmatch-to-elem-match-1.output.js
│ │ │ ├── 5-elemmatch-to-elem-match-2.input.js
│ │ │ ├── 5-elemmatch-to-elem-match-2.output.js
│ │ │ ├── 7-xhtml-true-1.input.js
│ │ │ ├── 2-no-empty-mode-1.input.js
│ │ │ ├── 2-def-must-return-something-3.input.js
│ │ │ ├── 2-def-must-return-something-1.input.js
│ │ │ ├── 2-def-must-return-something-3.output.js
│ │ │ ├── 8-chain-attrs-to-chain-addattrs-2.input.js
│ │ │ ├── 2-no-empty-mode-1.output.js
│ │ │ ├── 2-no-empty-mode-2.input.js
│ │ │ ├── 2-no-empty-mode-2.output.js
│ │ │ ├── 7-xhtml-true-1.output.js
│ │ │ ├── 8-chain-attrs-to-chain-addattrs-2.output.js
│ │ │ ├── 8-chain-mix-to-chain-addmix-2.input.js
│ │ │ ├── 0-func-to-simple-3.input.js
│ │ │ ├── 2-no-empty-mode-call-1.input.js
│ │ │ ├── 7-xhtml-true-2.input.js
│ │ │ ├── 8-chain-mix-to-chain-addmix-2.output.js
│ │ │ ├── 7-xhtml-true-3.input.js
│ │ │ ├── 7-xhtml-true-3.output.js
│ │ │ ├── 2-no-empty-mode-call-1.output.js
│ │ │ ├── 7-xhtml-true-2.output.js
│ │ │ ├── 8-chain-js-to-chain-addjs-1.input.js
│ │ │ ├── 8-js-to-addjs-1.input.js
│ │ │ ├── 8-chain-js-to-chain-addjs-1.output.js
│ │ │ ├── 8-js-to-addjs-1.output.js
│ │ │ ├── 8-js-to-addjs-2.input.js
│ │ │ ├── 8-js-to-addjs-2.output.js
│ │ │ ├── 7-thisisarray-to-arrayisarray-2.input.js
│ │ │ ├── 2-def-must-return-something-1.output.js
│ │ │ ├── 7-thisisarray-to-arrayisarray-2.output.js
│ │ │ ├── 8-attrs-to-addattrs-2.input.js
│ │ │ ├── 0-dont-check-this-mods-1.output.js
│ │ │ ├── 8-attrs-to-addattrs-2.output.js
│ │ │ ├── 8-mix-to-addmix-2.input.js
│ │ │ ├── 8-mix-to-addmix-2.output.js
│ │ │ ├── 0-dont-check-this-elemmods-1.output.js
│ │ │ ├── 3-apply-call-to-apply.output.js
│ │ │ ├── 3-apply-call-to-apply.input.js
│ │ │ ├── 0-dont-check-this-mods-1.input.js
│ │ │ ├── 0-dont-check-this-elemmods-1.input.js
│ │ │ ├── 0-arr-to-func-generator-1.input.js
│ │ │ ├── 0-obj-to-func-generator.input.js
│ │ │ ├── 7-mods-value-1.input.js
│ │ │ ├── 7-mods-value-1.output.js
│ │ │ ├── 2-def-must-return-something-5.input.js
│ │ │ ├── 0-arr-to-func-generator-1.output.js
│ │ │ ├── 2-def-must-return-something-5.output.js
│ │ │ ├── 0-obj-to-func-generator.output.js
│ │ │ ├── 2-def-must-return-something-4.input.js
│ │ │ └── 2-def-must-return-something-4.output.js
│ │ ├── 8-js-to-addjs.js
│ │ ├── 8-mix-to-addmix.js
│ │ ├── 0-html-entities.js
│ │ ├── 8-attrs-to-addattrs.js
│ │ ├── 7-thisisarray-to-arrayisarray.js
│ │ ├── 7-once-to-def.js
│ │ ├── 5-elemmatch-to-elem-match.js
│ │ ├── 8-chain-js-to-chain-addjs.js
│ │ ├── 8-chain-mix-to-chain-addmix.js
│ │ ├── 0-dont-check-this-mods.js
│ │ ├── 8-chain-attrs-to-chain-addattrs.js
│ │ ├── 0-dont-check-this-elemmods.js
│ │ ├── 0-func-to-simple.js
│ │ ├── 5-api-changed.js
│ │ ├── 2-no-empty-mode.js
│ │ ├── 2-no-empty-mode-call.js
│ │ ├── 2-no-more-this-underscore.js
│ │ ├── 7-mods-value.js
│ │ ├── 7-xhtml-true.js
│ │ ├── 0-arr-to-func-generator.js
│ │ ├── 0-obj-to-func-generator.js
│ │ ├── 2-def-must-return-something.js
│ │ └── 3-apply-call-to-apply.js
│ ├── logger.js
│ └── transformer.js
├── sample-config.json
├── package.json
└── README.md
├── .gitignore
├── .editorconfig
├── book.json
├── PULL_REQUEST_TEMPLATE.md
├── test
├── api-apply-test.js
├── bemjson-content-test.js
├── modes-def-test.js
├── runtime-local-test.js
├── runtime-reapply-test.js
├── bemcontext-isshorttag-test.js
├── bemcontext-block-test.js
├── bemcontext-elem-test.js
├── bemcontext-extend-test.js
├── modes-custom-test.js
├── bemjson-tag-test.js
├── bemcontext-custom-error-logger-test.js
├── bemcontext-custom-fields-test.js
├── runtime-applyctx-test.js
├── runtime-bemcontext-test.js
├── bemcontext-elemmods-test.js
├── custom-naming-test.js
├── bemjson-elem-test.js
├── bemcontext-islast-test.js
├── modes-cls-test.js
├── modes-bem-test.js
├── bemcontext-generateid-test.js
├── bemjson-elemmods-test.js
├── bemcontext-identify-test.js
├── utils-isunquotedattr-test.js
├── bemcontext-issimple-test.js
├── modes-tag-test.js
├── modes-prependcontent-test.js
├── bemjson-values-test.js
├── modes-addmods-test.js
├── modes-block-test.js
├── bemjson-block-test.js
├── modes-attrs-test.js
├── modes-addmix-test.js
├── fixtures.js
├── modes-appendcontent-test.js
├── modes-content-test.js
├── modes-addelemmods-test.js
├── modes-wrap-test.js
├── modes-elem-test.js
├── modes-match-test.js
├── modes-js-test.js
├── modes-addattrs-test.js
├── modes-replace-test.js
├── bemjson-mods-test.js
├── bemjson-cls-test.js
├── modes-addjs-test.js
├── bemjson-bem-test.js
├── runtime-escaping-test.js
├── modes-mods-test.js
├── bemcontext-xmlescape-test.js
├── runtime-match-test.js
├── bemcontext-attrescape-test.js
├── bemcontext-jsattrescape-test.js
├── init-test.js
├── modes-extend-test.js
├── modes-elemmods-test.js
├── bemcontext-isfirst-test.js
└── runtime-apply-test.js
├── .travis.yml
├── index.js
├── lib
├── bemxjst
│ ├── error.js
│ ├── class-builder.js
│ └── context.js
├── bemtree
│ ├── entity.js
│ └── index.js
└── bemhtml
│ └── entity.js
├── runtime-lint
├── README.md
└── stand.js
├── ISSUE_TEMPLATE.md
├── .github
└── workflows
│ └── node.js.yml
├── CONTRIBUTING.md
├── AUTHORS
├── .jscsrc
└── bin
└── bem-xjst
/docs/en/README.md:
--------------------------------------------------------------------------------
1 | ../../README.md
--------------------------------------------------------------------------------
/docs/ru/README.md:
--------------------------------------------------------------------------------
1 | ../../README.ru.md
--------------------------------------------------------------------------------
/bench/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | npm-debug.log
3 |
--------------------------------------------------------------------------------
/docs/en/book.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": "./"
3 | }
4 |
--------------------------------------------------------------------------------
/docs/ru/book.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": "./"
3 | }
4 |
--------------------------------------------------------------------------------
/docs/LANGS.md:
--------------------------------------------------------------------------------
1 | # Languages
2 |
3 | * [English](en/)
4 | * [Russian](ru/)
--------------------------------------------------------------------------------
/examples/source-maps/demo1/b1.bemhtml.js:
--------------------------------------------------------------------------------
1 | block('b1').tag()('blah');
2 |
--------------------------------------------------------------------------------
/examples/source-maps/demo3/b3.bemhtml.js:
--------------------------------------------------------------------------------
1 | block('b3').tag()('blah');
2 |
--------------------------------------------------------------------------------
/examples/source-maps/demo1/demo1.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/examples/source-maps/demo2/demo2.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/examples/source-maps/demo3/demo3.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/bench/templates/basic.bemhtml:
--------------------------------------------------------------------------------
1 | block('b1').content()(function() {
2 | return '#' + applyNext() + '#';
3 | });
4 |
--------------------------------------------------------------------------------
/scripts/update-authors.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | git log --reverse --format='%aN <%aE>' | sort | uniq -c | sort -bgr > AUTHORS
4 |
5 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/0-func-to-simple-1.output.js:
--------------------------------------------------------------------------------
1 | block('b').elem('e').tag()('span');
2 | block('b').tag()('span');
3 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/0-func-to-simple-2.input.js:
--------------------------------------------------------------------------------
1 | block('b').content()(function() {
2 | return [ 'span' ];
3 | });
4 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/0-func-to-simple-2.output.js:
--------------------------------------------------------------------------------
1 | block('b').content()(function() {
2 | return [ 'span' ];
3 | });
4 |
--------------------------------------------------------------------------------
/bench/templates/basic.bemjson:
--------------------------------------------------------------------------------
1 | module.exports = ({
2 | block: 'b1',
3 | content: [
4 | {
5 | block: 'b2'
6 | }
7 | ]
8 | });
9 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/2-def-must-return-something-2.input.js:
--------------------------------------------------------------------------------
1 | block('test').def()(function() {
2 | return 'text';
3 | });
4 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/2-def-must-return-something-2.output.js:
--------------------------------------------------------------------------------
1 | block('test').def()(function() {
2 | return 'text';
3 | });
4 |
--------------------------------------------------------------------------------
/examples/source-maps/demo-in-nodejs/b1.bemhtml.js:
--------------------------------------------------------------------------------
1 | block('b1').tag()('blah');
2 | block('b1').content()(function() {
3 | return this.ctx.a.b.c;
4 | });
5 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/8-chain-mix-to-chain-addmix-1.input.js:
--------------------------------------------------------------------------------
1 | block('b').mix()(function() {
2 | return { block: 'mixed' };
3 | });
4 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/8-chain-attrs-to-chain-addattrs-1.input.js:
--------------------------------------------------------------------------------
1 | block('b').attrs()(function() {
2 | return { id: 'test' };
3 | });
4 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/8-chain-attrs-to-chain-addattrs-1.output.js:
--------------------------------------------------------------------------------
1 | block('b').addAttrs()(function() {
2 | return { id: 'test' };
3 | });
4 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/8-chain-mix-to-chain-addmix-1.output.js:
--------------------------------------------------------------------------------
1 | block('b').addMix()(function() {
2 | return { block: 'mixed' };
3 | });
4 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/7-once-to-def-1.input.js:
--------------------------------------------------------------------------------
1 | block('test').once()(function() {
2 | this.mods.once = 1;
3 |
4 | return applyNext();
5 | });
6 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/7-once-to-def-1.output.js:
--------------------------------------------------------------------------------
1 | block('test').def()(function() {
2 | this.mods.once = 1;
3 |
4 | return applyNext();
5 | });
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | lib/bem*/bundle.js
2 | /node_modules/
3 | _book
4 | npm-debug.log
5 | /.idea/
6 | /coverage/
7 | /bench/bem-xjst-*/
8 | /bench/dat-*/
9 | /bench/web-data/
10 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/2-no-more-this-underscore-1.output.js:
--------------------------------------------------------------------------------
1 | block('test').attrs()(function() {
2 | return this.extend(this.ctx.attrs, { test: 1 });
3 | });
4 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/2-no-more-this-underscore-2.input.js:
--------------------------------------------------------------------------------
1 | block('test').content()(function() {
2 | return this._.isSimpleTag(this.ctx.tag) || [];
3 | });
4 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/2-no-more-this-underscore-2.output.js:
--------------------------------------------------------------------------------
1 | block('test').content()(function() {
2 | return this.isSimpleTag(this.ctx.tag) || [];
3 | });
4 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/2-no-more-this-underscore-1.input.js:
--------------------------------------------------------------------------------
1 | block('test').attrs()(function() {
2 | return this._.extend(this.ctx.attrs, { test: 1 });
3 | });
4 |
5 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/8-mix-to-addmix-1.input.js:
--------------------------------------------------------------------------------
1 | block('b')(
2 | mix()(function() {
3 | return { block: 'mixed' };
4 | }),
5 |
6 | content()('test')
7 | );
8 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/5-api-changed-1.input.js:
--------------------------------------------------------------------------------
1 | var bemhtml = require('bem-xjst');
2 | var templates = bemhtml.compile(function() {})
3 | var html = templates.apply({});
4 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/8-attrs-to-addattrs-1.input.js:
--------------------------------------------------------------------------------
1 | block('b')(
2 | attrs()(function() {
3 | return { id: 'test' };
4 | }),
5 |
6 | content()('test')
7 | );
8 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/8-attrs-to-addattrs-1.output.js:
--------------------------------------------------------------------------------
1 | block('b')(
2 | addAttrs()(function() {
3 | return { id: 'test' };
4 | }),
5 |
6 | content()('test')
7 | );
8 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/8-mix-to-addmix-1.output.js:
--------------------------------------------------------------------------------
1 | block('b')(
2 | addMix()(function() {
3 | return { block: 'mixed' };
4 | }),
5 |
6 | content()('test')
7 | );
8 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/5-api-changed-1.output.js:
--------------------------------------------------------------------------------
1 | var bemhtml = require('bem-xjst').bemhtml;
2 | var templates = bemhtml.compile(function() {})
3 | var html = templates.apply({});
4 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/0-func-to-simple-1.input.js:
--------------------------------------------------------------------------------
1 | block('b').elem('e').tag()(function() {
2 | return 'span';
3 | });
4 | block('b').tag()(function() {
5 | return 'span';
6 | });
7 |
--------------------------------------------------------------------------------
/migration/sample-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 4,
3 | "usetabs": true,
4 | "reuseWhitespace": true,
5 | "wrapColumn": 120,
6 | "quote": "single",
7 | "trailingComma": false
8 | }
9 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/0-func-to-simple-3.output.js:
--------------------------------------------------------------------------------
1 | block('link')
2 | .tag()('span');
3 |
4 | block('link')
5 | .match(function() { return this.mods.disabled; })
6 | .tag()('span');
7 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/5-elemmatch-to-elem-match-1.input.js:
--------------------------------------------------------------------------------
1 | block('b')
2 | .elemMatch(function() {
3 | return this.elem !== 'name' && this.elem !== 'url';
4 | })
5 | .tag()('li');
6 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/7-thisisarray-to-arrayisarray-1.input.js:
--------------------------------------------------------------------------------
1 | block('test').content()(function() {
2 | var ret = applyNext();
3 |
4 | return this.isArray(ret) ? ret : [ ret ];
5 | });
6 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/7-thisisarray-to-arrayisarray-1.output.js:
--------------------------------------------------------------------------------
1 | block('test').content()(function() {
2 | var ret = applyNext();
3 |
4 | return Array.isArray(ret) ? ret : [ ret ];
5 | });
6 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: http://EditorConfig.org
2 | root = true
3 |
4 | [*]
5 | end_of_line = lf
6 | insert_final_newline = true
7 |
8 | [*.js]
9 | indent_style = space
10 | indent_size = 2
11 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/5-elemmatch-to-elem-match-1.output.js:
--------------------------------------------------------------------------------
1 | block('b')
2 | .elem('*').match(function() {
3 | return this.elem !== 'name' && this.elem !== 'url';
4 | })
5 | .tag()('li');
6 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/5-elemmatch-to-elem-match-2.input.js:
--------------------------------------------------------------------------------
1 | block('b')(
2 | elemMatch(function() {
3 | return this.elem !== 'name' && this.elem !== 'url';
4 | })
5 | .tag()('li')
6 | );
7 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/5-elemmatch-to-elem-match-2.output.js:
--------------------------------------------------------------------------------
1 | block('b')(
2 | elem('*').match(function() {
3 | return this.elem !== 'name' && this.elem !== 'url';
4 | })
5 | .tag()('li')
6 | );
7 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/7-xhtml-true-1.input.js:
--------------------------------------------------------------------------------
1 | var bemhtml = require('bem-xjst').bemhtml;
2 |
3 | var templates = bemhtml.compile(function() {});
4 |
5 | var html = bemhtml.apply({ block: 'page' });
6 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/2-no-empty-mode-1.input.js:
--------------------------------------------------------------------------------
1 | block('test')(
2 | mode('')(function() {
3 | return { test: 1 };
4 | }),
5 |
6 | def()(function() {
7 | return apply('');
8 | })
9 | );
10 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/2-def-must-return-something-3.input.js:
--------------------------------------------------------------------------------
1 | block('image').mod('name', 'maestro').def()(function() {
2 | var newCtx = this.ctx;
3 | newCtx.alt = 'maestro';
4 | applyCtx(newCtx);
5 | });
6 |
--------------------------------------------------------------------------------
/book.json:
--------------------------------------------------------------------------------
1 | {
2 | "description":"bem-xjst",
3 | "root": "./docs",
4 | "plugins":[
5 | "comment",
6 | "theme-api",
7 | "-sharing",
8 | "search",
9 | "collapsible-menu"
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/2-def-must-return-something-1.input.js:
--------------------------------------------------------------------------------
1 | block('test').elem('e').def()(function() {
2 | this.mods = { test: 1 };
3 | });
4 | block('test').def()(function() {
5 | this.mods = { test: 1 };
6 | });
7 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/2-def-must-return-something-3.output.js:
--------------------------------------------------------------------------------
1 | block('image').mod('name', 'maestro').def()(function() {
2 | var newCtx = this.ctx;
3 | newCtx.alt = 'maestro';
4 | return applyCtx(newCtx);
5 | });
6 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/8-chain-attrs-to-chain-addattrs-2.input.js:
--------------------------------------------------------------------------------
1 | block('test').attrs()(function() {
2 | var attrs = this.ctx.attrs;
3 |
4 | attrs = this.extend(attrs, { id: 'test' });
5 |
6 | return attrs;
7 | });
8 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/2-no-empty-mode-1.output.js:
--------------------------------------------------------------------------------
1 | block('test')(
2 | mode('custom-mode')(function() {
3 | return { test: 1 };
4 | }),
5 |
6 | def()(function() {
7 | return apply('');
8 | })
9 | );
10 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/2-no-empty-mode-2.input.js:
--------------------------------------------------------------------------------
1 | block('test')(
2 | mode('custom')(function() {
3 | return { test: 1 };
4 | }),
5 |
6 | def()(function() {
7 | return apply('custom');
8 | })
9 | );
10 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/2-no-empty-mode-2.output.js:
--------------------------------------------------------------------------------
1 | block('test')(
2 | mode('custom')(function() {
3 | return { test: 1 };
4 | }),
5 |
6 | def()(function() {
7 | return apply('custom');
8 | })
9 | );
10 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/7-xhtml-true-1.output.js:
--------------------------------------------------------------------------------
1 | var bemhtml = require('bem-xjst').bemhtml;
2 |
3 | var templates = bemhtml.compile(function() {}, {
4 | xhtml: true
5 | });
6 |
7 | var html = bemhtml.apply({ block: 'page' });
8 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/8-chain-attrs-to-chain-addattrs-2.output.js:
--------------------------------------------------------------------------------
1 | block('test').addAttrs()(function() {
2 | var attrs = this.ctx.attrs;
3 |
4 | attrs = this.extend(attrs, { id: 'test' });
5 |
6 | return attrs;
7 | });
8 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/8-chain-mix-to-chain-addmix-2.input.js:
--------------------------------------------------------------------------------
1 | block('test').mix()(function() {
2 | // TODO: rewrite push()
3 | var mix = this.ctx.mix;
4 |
5 | mix.push({ block: 'mixed' });
6 |
7 | return mix;
8 | });
9 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/0-func-to-simple-3.input.js:
--------------------------------------------------------------------------------
1 | block('link')
2 | .tag()(function() { return 'span'; });
3 |
4 | block('link')
5 | .match(function() { return this.mods.disabled; })
6 | .tag()(function() { return 'span'; });
7 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/2-no-empty-mode-call-1.input.js:
--------------------------------------------------------------------------------
1 | block('test')(
2 | mode('')(function() {
3 | return { test: 1 };
4 | }),
5 |
6 | def()(function() {
7 | return apply('', { test: 1 });
8 | })
9 | );
10 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/7-xhtml-true-2.input.js:
--------------------------------------------------------------------------------
1 | var bemhtml = require('bem-xjst').bemhtml;
2 |
3 | var templates = bemhtml.compile(function() {}, {
4 | someOption: 1
5 | });
6 |
7 | var html = bemhtml.apply({ block: 'page' });
8 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/8-chain-mix-to-chain-addmix-2.output.js:
--------------------------------------------------------------------------------
1 | block('test').addMix()(function() {
2 | // TODO: rewrite push()
3 | var mix = this.ctx.mix;
4 |
5 | mix.push({ block: 'mixed' });
6 |
7 | return mix;
8 | });
9 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/7-xhtml-true-3.input.js:
--------------------------------------------------------------------------------
1 | var bemhtml = require('bem-xjst').bemhtml;
2 |
3 | var templates = bemhtml.compile(function() {}, {
4 | test: 1,
5 | xhtml: false
6 | });
7 |
8 | var html = bemhtml.apply({ block: 'page' });
9 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/7-xhtml-true-3.output.js:
--------------------------------------------------------------------------------
1 | var bemhtml = require('bem-xjst').bemhtml;
2 |
3 | var templates = bemhtml.compile(function() {}, {
4 | test: 1,
5 | xhtml: true
6 | });
7 |
8 | var html = bemhtml.apply({ block: 'page' });
9 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/2-no-empty-mode-call-1.output.js:
--------------------------------------------------------------------------------
1 | block('test')(
2 | mode('')(function() {
3 | return { test: 1 };
4 | }),
5 |
6 | def()(function() {
7 | return apply('custom-mode', { test: 1 });
8 | })
9 | );
10 |
11 |
--------------------------------------------------------------------------------
/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | Fixes #
2 |
3 | ### Changes proposed in this pull request
4 |
5 | -
6 | -
7 | -
8 |
9 | ### Checklist
10 |
11 | - [ ] Documentation changed
12 | - [ ] Tests added
13 | - [ ] Benchmark checked
14 |
15 |
16 | ### Benchmark result
17 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/7-xhtml-true-2.output.js:
--------------------------------------------------------------------------------
1 | var bemhtml = require('bem-xjst').bemhtml;
2 |
3 | var templates = bemhtml.compile(function() {}, {
4 | someOption: 1,
5 | xhtml: true
6 | });
7 |
8 | var html = bemhtml.apply({ block: 'page' });
9 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/8-chain-js-to-chain-addjs-1.input.js:
--------------------------------------------------------------------------------
1 | block('test').js()(function() {
2 | // TODO: ololo
3 | var js = this.ctx.js;
4 |
5 | js = { data: 'lol' };
6 |
7 | return js;
8 | });
9 |
10 | block('test').js()(true);
11 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/8-js-to-addjs-1.input.js:
--------------------------------------------------------------------------------
1 | block('b')(
2 | js()(function() {
3 | return { data: 'lol' };
4 | }),
5 |
6 | content()('test')
7 | );
8 |
9 | block('b')(
10 | js()(true),
11 |
12 | content()('test')
13 | );
14 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/8-chain-js-to-chain-addjs-1.output.js:
--------------------------------------------------------------------------------
1 | block('test').addJs()(function() {
2 | // TODO: ololo
3 | var js = this.ctx.js;
4 |
5 | js = { data: 'lol' };
6 |
7 | return js;
8 | });
9 |
10 | block('test').addJs()(true);
11 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/8-js-to-addjs-1.output.js:
--------------------------------------------------------------------------------
1 | block('b')(
2 | addJs()(function() {
3 | return { data: 'lol' };
4 | }),
5 |
6 | content()('test')
7 | );
8 |
9 | block('b')(
10 | addJs()(true),
11 |
12 | content()('test')
13 | );
14 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/8-js-to-addjs-2.input.js:
--------------------------------------------------------------------------------
1 | block('test')(
2 | js()(function() {
3 | // TODO: ololo
4 | var js = this.ctx.js;
5 |
6 | js = { data: 'lol' };
7 |
8 | return js;
9 | }),
10 |
11 | content()('test')
12 | );
13 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/8-js-to-addjs-2.output.js:
--------------------------------------------------------------------------------
1 | block('test')(
2 | addJs()(function() {
3 | // TODO: ololo
4 | var js = this.ctx.js;
5 |
6 | js = { data: 'lol' };
7 |
8 | return js;
9 | }),
10 |
11 | content()('test')
12 | );
13 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/7-thisisarray-to-arrayisarray-2.input.js:
--------------------------------------------------------------------------------
1 | block('test').mix()(function() {
2 | var mix = this.mix;
3 |
4 | if (!this.isArray(mix))
5 | mix = [ this.mix ];
6 |
7 | mix.push({ block: 'mixed' });
8 |
9 | return mix;
10 | });
11 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/2-def-must-return-something-1.output.js:
--------------------------------------------------------------------------------
1 | block('test').elem('e').def()(function() {
2 | this.mods = { test: 1 };
3 | return applyNext();
4 | });
5 | block('test').def()(function() {
6 | this.mods = { test: 1 };
7 | return applyNext();
8 | });
9 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/7-thisisarray-to-arrayisarray-2.output.js:
--------------------------------------------------------------------------------
1 | block('test').mix()(function() {
2 | var mix = this.mix;
3 |
4 | if (!Array.isArray(mix))
5 | mix = [ this.mix ];
6 |
7 | mix.push({ block: 'mixed' });
8 |
9 | return mix;
10 | });
11 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/8-attrs-to-addattrs-2.input.js:
--------------------------------------------------------------------------------
1 | block('test')(
2 | attrs()(function() {
3 | var attrs = this.ctx.attrs;
4 |
5 | attrs = this.extend(attrs, { id: 'test' });
6 |
7 | return attrs;
8 | }),
9 |
10 | content()('test')
11 | );
12 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/0-dont-check-this-mods-1.output.js:
--------------------------------------------------------------------------------
1 | block('b1').elem('e').def()(function() {
2 | var some = this.mods.some;
3 |
4 | return some;
5 | });
6 | block('b1').def()(function() {
7 | var some = this.mods.some;
8 |
9 | return some;
10 | });
11 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/8-attrs-to-addattrs-2.output.js:
--------------------------------------------------------------------------------
1 | block('test')(
2 | addAttrs()(function() {
3 | var attrs = this.ctx.attrs;
4 |
5 | attrs = this.extend(attrs, { id: 'test' });
6 |
7 | return attrs;
8 | }),
9 |
10 | content()('test')
11 | );
12 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/8-mix-to-addmix-2.input.js:
--------------------------------------------------------------------------------
1 | block('test')(
2 | mix()(function() {
3 | // TODO: rewrite push
4 | var mix = this.ctx.mix;
5 |
6 | mix.push({ block: 'mixed' });
7 |
8 | return mix;
9 | }),
10 |
11 | content()('test')
12 | );
13 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/8-mix-to-addmix-2.output.js:
--------------------------------------------------------------------------------
1 | block('test')(
2 | addMix()(function() {
3 | // TODO: rewrite push
4 | var mix = this.ctx.mix;
5 |
6 | mix.push({ block: 'mixed' });
7 |
8 | return mix;
9 | }),
10 |
11 | content()('test')
12 | );
13 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/0-dont-check-this-elemmods-1.output.js:
--------------------------------------------------------------------------------
1 | block('b1').def()(function() {
2 | var some = this.elemMods.some;
3 |
4 | return some;
5 | });
6 | block('b1').elem('e').def()(function() {
7 | var some = this.elemMods.some;
8 |
9 | return some;
10 | });
11 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/3-apply-call-to-apply.output.js:
--------------------------------------------------------------------------------
1 | var bemhtml = require('bem-xjst').bemhtml;
2 |
3 | var templates = bemhtml.compile(function() {});
4 |
5 | var html = bemhtml.apply({ block: 'page' });
6 |
7 | var apply = bemhtml.apply;
8 |
9 | apply({ block: 'page' });
10 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/3-apply-call-to-apply.input.js:
--------------------------------------------------------------------------------
1 | var bemhtml = require('bem-xjst').bemhtml;
2 |
3 | var templates = bemhtml.compile(function() {});
4 |
5 | var html = bemhtml.apply.call({ block: 'page' });
6 |
7 | var apply = bemhtml.apply;
8 |
9 | apply.call({ block: 'page' });
10 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/0-dont-check-this-mods-1.input.js:
--------------------------------------------------------------------------------
1 | block('b1').elem('e').def()(function() {
2 | var some = this.mods && this.mods.some;
3 |
4 | return some;
5 | });
6 | block('b1').def()(function() {
7 | var some = this.mods && this.mods.some;
8 |
9 | return some;
10 | });
11 |
--------------------------------------------------------------------------------
/docs/ru/SUMMARY.md:
--------------------------------------------------------------------------------
1 | # Summary
2 |
3 | * [Введение](README.md)
4 | * [О bem-xjst](1-about.md)
5 | * [Быстрый старт](2-quick-start.md)
6 | * [API](3-api.md)
7 | * [Входные данные — BEMJSON](4-data.md)
8 | * [Синтаксис шаблонов](5-templates-syntax.md)
9 | * [Что доступно в теле шаблона?](6-templates-context.md)
10 | * [Runtime](7-runtime.md)
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/0-dont-check-this-elemmods-1.input.js:
--------------------------------------------------------------------------------
1 | block('b1').def()(function() {
2 | var some = this.elemMods && this.elemMods.some;
3 |
4 | return some;
5 | });
6 | block('b1').elem('e').def()(function() {
7 | var some = this.elemMods && this.elemMods.some;
8 |
9 | return some;
10 | });
11 |
--------------------------------------------------------------------------------
/docs/en/SUMMARY.md:
--------------------------------------------------------------------------------
1 | # Summary
2 |
3 | * [Introduction](README.md)
4 | * [About bem-xjst](1-about.md)
5 | * [Quick start](2-quick-start.md)
6 | * [API](3-api.md)
7 | * [Input data — BEMJSON](4-data.md)
8 | * [Template syntax](5-templates-syntax.md)
9 | * [What is available in the template body?](6-templates-context.md)
10 | * [Runtime](7-runtime.md)
11 |
--------------------------------------------------------------------------------
/test/api-apply-test.js:
--------------------------------------------------------------------------------
1 | var assert = require('assert');
2 | var bemhtml = require('../').bemhtml;
3 |
4 | describe('API apply', function() {
5 | it('should support apply', function() {
6 | var templates = bemhtml.compile();
7 | var html = templates.apply({ block: 'b' });
8 | assert.equal(html, '
');
9 | });
10 | });
11 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 0.10
4 | - 0.12
5 | - 4
6 | - 5
7 | - 6
8 | - 7
9 | - 8
10 |
11 | env:
12 | global:
13 | - ISTANBUL_COVERAGE: yes
14 |
15 | after_success:
16 | - npm i coveralls
17 | - cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && echo "Coverage data was sent to coveralls!"
18 |
--------------------------------------------------------------------------------
/examples/source-maps/demo1/demo1.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs'),
2 | bemxjst = require('../../../').bemhtml,
3 | tmpl = 'b1.bemhtml.js',
4 | bundle = 'bundle.bemhtml.js';
5 |
6 | var result = bemxjst.generate(fs.readFileSync(tmpl, 'utf8'), {
7 | to: bundle,
8 | sourceMap: { from: tmpl }
9 | });
10 |
11 | fs.writeFileSync(bundle, result);
12 |
--------------------------------------------------------------------------------
/test/bemjson-content-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 |
4 | describe('BEMJSON content', function() {
5 | it('should render block by default as div', function () {
6 | test(function() {},
7 | [ { content: 'Hello, bemhtml!' } ],
8 | 'Hello, bemhtml!
');
9 | });
10 | });
11 |
--------------------------------------------------------------------------------
/bench/prepare.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | rev1=$1;
4 | rev2=$2;
5 |
6 | get_revision(){
7 | if [ ! -d "bem-xjst-$1" ]; then
8 | curl https://codeload.github.com/bem/bem-xjst/zip/$1 > $1.zip && unzip $1.zip && rm $1.zip
9 | cd bem-xjst-$1 && npm i && npm run make && cd ../
10 | fi
11 | }
12 |
13 | get_revision "$rev1"
14 | get_revision "$rev2"
15 |
--------------------------------------------------------------------------------
/examples/source-maps/demo2/demo2.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs'),
2 | bemxjst = require('../../../').bemhtml,
3 | tmpl = 'tmpls-with-sourcemap.bemhtml.js',
4 | bundle = 'bundle.bemhtml.js';
5 |
6 | var result = bemxjst.generate(fs.readFileSync(tmpl, 'utf8'), {
7 | to: bundle,
8 | sourceMap: { from: tmpl }
9 | });
10 |
11 | fs.writeFileSync(bundle, result);
12 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | var Compiler = require('./lib/compiler');
2 | var _cache = {};
3 |
4 | function getEngine(engineName) {
5 | var engine = _cache[engineName];
6 |
7 | return engine || (engine = new Compiler(engineName));
8 | };
9 |
10 |
11 | module.exports = {
12 | get bemtree() { return getEngine('bemtree'); },
13 | get bemhtml() { return getEngine('bemhtml'); }
14 | };
15 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/0-arr-to-func-generator-1.input.js:
--------------------------------------------------------------------------------
1 | block('b').mix()(['abs', 'abc']);
2 | block('b').elem('e').mix()(['test']);
3 | block('b').mod('mn', 'mv').elem('e').mix()(['test']);
4 | block('b')(
5 | mix()([0,1,2])
6 | );
7 |
8 | block('b')(
9 | mix()(function() { return []; })
10 | );
11 |
12 | block('b')(
13 | content()([])
14 | );
15 |
--------------------------------------------------------------------------------
/test/modes-def-test.js:
--------------------------------------------------------------------------------
1 | var assert = require('assert');
2 | var bemhtml = require('./fixtures')('bemhtml');
3 |
4 | describe('Modes def', function() {
5 | it('should throw error when args passed to def mode', function() {
6 | assert.throws(function() {
7 | bemhtml.compile(function() {
8 | block('b1').def('blah');
9 | });
10 | });
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/0-obj-to-func-generator.input.js:
--------------------------------------------------------------------------------
1 | block('b').attrs()({ id: 'attrs' });
2 | block('b').elem('e').js()({ id: 'js-test' });
3 | block('b')(
4 | js()({ id: 'js-test2' })
5 | );
6 | block('b')(
7 | attrs()({ id: 'attrs-test' })
8 | );
9 | block('b')(
10 | mix()({ block: 'a' })
11 | );
12 | block('b')(
13 | elem('e').mix()({ block: 'a' })
14 | );
15 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/7-mods-value-1.input.js:
--------------------------------------------------------------------------------
1 | block('a').mod('m', 1).content()('number');
2 | block('a')(
3 | mod('m', 1).content()('number')
4 | );
5 |
6 | block('a').elem('e').elemMod('m', 1).content()('number');
7 | block('a').elem('e')(
8 | elemMod('m', 1).content()('number')
9 | );
10 | block('a')(
11 | elem('e').elemMod('m', 1).content()('number')
12 | );
13 |
--------------------------------------------------------------------------------
/migration/lib/logger.js:
--------------------------------------------------------------------------------
1 | module.exports = function logger(opts) {
2 | 'use strict';
3 | var file = opts.file;
4 | var path = opts.path;
5 | var start = path.loc.start;
6 |
7 | console.warn([
8 | 'BEM-XJST WARNING:',
9 | opts.descr,
10 | [ file.path, start.line, start.column ].join(':'),
11 | file.source.slice(path.start - 5, path.end + 15),
12 | '\n'
13 | ].join('\n>>>> '));
14 | };
15 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/7-mods-value-1.output.js:
--------------------------------------------------------------------------------
1 | block('a').mod('m', '1').content()('number');
2 | block('a')(
3 | mod('m', '1').content()('number')
4 | );
5 |
6 | block('a').elem('e').elemMod('m', '1').content()('number');
7 | block('a').elem('e')(
8 | elemMod('m', '1').content()('number')
9 | );
10 | block('a')(
11 | elem('e').elemMod('m', '1').content()('number')
12 | );
13 |
--------------------------------------------------------------------------------
/examples/source-maps/demo3/demo3.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs'),
2 | bemxjst = require('../../../').bemhtml,
3 | tmpl = 'b3.bemhtml.js',
4 | bundle = 'bundle.bemhtml.js';
5 |
6 | var result = bemxjst.generate(fs.readFileSync(tmpl, 'utf8'), {
7 | to: bundle,
8 | sourceMap: {
9 | prev: 'block(\'a\').tag()(\'a\');',
10 | from: tmpl
11 | },
12 | });
13 |
14 | fs.writeFileSync(bundle, result);
15 |
--------------------------------------------------------------------------------
/lib/bemxjst/error.js:
--------------------------------------------------------------------------------
1 | function BEMXJSTError(msg, func) {
2 | this.name = 'BEMXJSTError';
3 | this.message = msg;
4 |
5 | if (Error.captureStackTrace)
6 | Error.captureStackTrace(this, func || this.constructor);
7 | else
8 | this.stack = (new Error()).stack;
9 | }
10 |
11 | BEMXJSTError.prototype = Object.create(Error.prototype);
12 | BEMXJSTError.prototype.constructor = BEMXJSTError;
13 |
14 | exports.BEMXJSTError = BEMXJSTError;
15 |
--------------------------------------------------------------------------------
/test/runtime-local-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 |
4 | describe('Runtime local()', function() {
5 | it('should support local()', function() {
6 | test(function() {
7 | block('b1').content()(function() {
8 | return local({ tmp: 'b2' })(function() {
9 | return this.tmp;
10 | });
11 | });
12 | }, { block: 'b1' }, 'b2
');
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/examples/simple-page/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "simple-page",
3 | "version": "1.0.0",
4 | "description": "bem-xjst example: simple page",
5 | "main": "index.js",
6 | "keywords": [
7 | "BEMTREE",
8 | "BEMHTML",
9 | "BEMJSON",
10 | "template engine",
11 | "bem-xjst example"
12 | ],
13 | "author": "Slava Oliyanchuk (http://miripiruni.org/)",
14 | "license": "MIT",
15 | "dependencies": {
16 | "bem-xjst": "^6.4.0"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/2-def-must-return-something-5.input.js:
--------------------------------------------------------------------------------
1 | block('username').def()(function() {
2 | var size = this.ctx.size || 3;
3 |
4 | applyCtx([
5 | {
6 | block: 'font',
7 | mods: { color: 'accent' },
8 | size: size,
9 | content: this.ctx.content[0]
10 | },
11 | {
12 | block: 'font',
13 | color: '#000000',
14 | size: size,
15 | content: this.ctx.content.substr(1)
16 | }
17 | ]);
18 | });
19 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/0-arr-to-func-generator-1.output.js:
--------------------------------------------------------------------------------
1 | block('b').mix()(function () {
2 | return ['abs', 'abc'];
3 | });
4 | block('b').elem('e').mix()(function () {
5 | return ['test'];
6 | });
7 | block('b').mod('mn', 'mv').elem('e').mix()(function () {
8 | return ['test'];
9 | });
10 | block('b')(
11 | mix()(function () {
12 | return [0,1,2];
13 | })
14 | );
15 |
16 | block('b')(
17 | mix()(function() { return []; })
18 | );
19 |
20 | block('b')(
21 | content()(function () {
22 | return [];
23 | })
24 | );
25 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/2-def-must-return-something-5.output.js:
--------------------------------------------------------------------------------
1 | block('username').def()(function() {
2 | var size = this.ctx.size || 3;
3 |
4 | return applyCtx([
5 | {
6 | block: 'font',
7 | mods: { color: 'accent' },
8 | size: size,
9 | content: this.ctx.content[0]
10 | },
11 | {
12 | block: 'font',
13 | color: '#000000',
14 | size: size,
15 | content: this.ctx.content.substr(1)
16 | }
17 | ]);
18 | });
19 |
--------------------------------------------------------------------------------
/examples/simple-page/index.js:
--------------------------------------------------------------------------------
1 | var bemxjst = require('bem-xjst');
2 | var data = require('./data');
3 |
4 | // In this section we convert JSON from backend to BEMJSON
5 | var bemtreeRuntime = bemxjst
6 | .bemtree
7 | .compile(require('./bemtree-templates'));
8 | var bemjson = bemtreeRuntime.apply({ block: 'root', data: data });
9 |
10 | // In this section we convert BEMJSON to HTML
11 | var bemhtmlRuntime = bemxjst
12 | .bemhtml
13 | .compile(require('./bemhtml-templates'), { xhtml: false });
14 | var html = bemhtmlRuntime.apply(bemjson);
15 |
16 | console.log(html);
17 |
--------------------------------------------------------------------------------
/migration/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bem-xjst-static-analyzer",
3 | "description": "bem-xjst templates analyzer. Can be used as static linter or migration tool.",
4 | "version": "1.0.0",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "./node_modules/.bin/jest --bail"
8 | },
9 | "author": "Slava Oliyanchuk (http://miripiruni.org/)",
10 | "license": "ISC",
11 | "dependencies": {
12 | "jest": "^15.1.1",
13 | "jscodeshift": "^0.3.29",
14 | "lodash.get": "^4.4.2",
15 | "yargs": "^6.3.0"
16 | },
17 | "devDependencies": {}
18 | }
19 |
--------------------------------------------------------------------------------
/test/runtime-reapply-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 |
4 | describe('Runtime this.reapply()', function() {
5 | it('should support this.reapply()', function() {
6 | test(function() {
7 | block('b1').content()(function() {
8 | this.wtf = 'fail';
9 | return this.reapply({ block: 'b2' });
10 | });
11 |
12 | block('b2').content()(function() {
13 | return this.wtf || 'ok';
14 | });
15 | },
16 | { block: 'b1' },
17 | '<div class="b2">ok</div>
');
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/0-obj-to-func-generator.output.js:
--------------------------------------------------------------------------------
1 | block('b').attrs()(function () {
2 | return { id: 'attrs' };
3 | });
4 | block('b').elem('e').js()(function () {
5 | return { id: 'js-test' };
6 | });
7 | block('b')(
8 | js()(function () {
9 | return { id: 'js-test2' };
10 | })
11 | );
12 | block('b')(
13 | attrs()(function () {
14 | return { id: 'attrs-test' };
15 | })
16 | );
17 | block('b')(
18 | mix()(function () {
19 | return { block: 'a' };
20 | })
21 | );
22 | block('b')(
23 | elem('e').mix()(function () {
24 | return { block: 'a' };
25 | })
26 | );
27 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/2-def-must-return-something-4.input.js:
--------------------------------------------------------------------------------
1 | block('table').mod('theme', 'grey')(
2 | elem('cell').elemMod('type', 'head').def()(function() {
3 | var defaultStyles = {
4 | width: 'width: 180px;',
5 | color: 'color: #605d5d;'
6 | };
7 |
8 | var defaultStylesConcated = '';
9 | Object.keys(defaultStyles).map(function() {
10 | defaultStylesConcated = defaultStylesConcated + defaultStyles[style];
11 | });
12 |
13 | var style = defaultStylesConcated + (this.ctx.style || '');
14 |
15 | return applyNext({
16 | 'ctx.style': style
17 | });
18 | })
19 | );
20 |
--------------------------------------------------------------------------------
/migration/lib/transformers/__testfixtures__/2-def-must-return-something-4.output.js:
--------------------------------------------------------------------------------
1 | block('table').mod('theme', 'grey')(
2 | elem('cell').elemMod('type', 'head').def()(function() {
3 | var defaultStyles = {
4 | width: 'width: 180px;',
5 | color: 'color: #605d5d;'
6 | };
7 |
8 | var defaultStylesConcated = '';
9 | Object.keys(defaultStyles).map(function() {
10 | defaultStylesConcated = defaultStylesConcated + defaultStyles[style];
11 | });
12 |
13 | var style = defaultStylesConcated + (this.ctx.style || '');
14 |
15 | return applyNext({
16 | 'ctx.style': style
17 | });
18 | })
19 | );
20 |
--------------------------------------------------------------------------------
/bench/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bem-xjst-bench",
3 | "version": "1.0.0",
4 | "private": true,
5 | "description": "Benchmarks for bem-xjst",
6 | "main": "run.js",
7 | "scripts": {
8 | "postinstall": "cd ./node_modules/bem-xjst && npm install && npm run make",
9 | "test": "echo \"Error: no test specified\" && exit 1"
10 | },
11 | "author": "",
12 | "license": "MIT",
13 | "dependencies": {
14 | "bem-xjst": "https://github.com/bem/bem-xjst.git#4d90404",
15 | "benchmark": "https://github.com/miripiruni/benchmark.js.git",
16 | "browserify": "^11.0.0",
17 | "microtime": "^2.0.0",
18 | "yargs": "^3.15.0"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/migration/lib/transformers/8-js-to-addjs.js:
--------------------------------------------------------------------------------
1 | var log = require('../logger');
2 | var Transformer = require('../transformer');
3 | var t = new Transformer();
4 |
5 | module.exports = function(file, api, opts) {
6 | t.description = 'From v8.x you should use addJs() instead of js()';
7 |
8 | t.find = function(file, j) {
9 | return j(file.source)
10 | .find(j.CallExpression, { callee: { type: 'Identifier', name: 'js' } });
11 | };
12 |
13 | t.replace = function(ret, j) {
14 | return ret.replaceWith(function(p) {
15 | return j.callExpression(j.identifier('addJs'), []);
16 | });
17 | };
18 |
19 | return t.run(file, api, opts);
20 | };
21 |
--------------------------------------------------------------------------------
/migration/lib/transformers/8-mix-to-addmix.js:
--------------------------------------------------------------------------------
1 | var log = require('../logger');
2 | var Transformer = require('../transformer');
3 | var t = new Transformer();
4 |
5 | module.exports = function(file, api, opts) {
6 | t.description = 'From v8.x you should use addMix() instead of mix()';
7 |
8 | t.find = function(file, j) {
9 | return j(file.source)
10 | .find(j.CallExpression, { callee: { type: 'Identifier', name: 'mix' } });
11 | };
12 |
13 | t.replace = function(ret, j) {
14 | return ret.replaceWith(function(p) {
15 | return j.callExpression(j.identifier('addMix'), []);
16 | });
17 | };
18 |
19 | return t.run(file, api, opts);
20 | };
21 |
--------------------------------------------------------------------------------
/migration/lib/transformers/0-html-entities.js:
--------------------------------------------------------------------------------
1 | var log = require('../logger');
2 | var Transformer = require('../transformer');
3 | var t = new Transformer();
4 |
5 | module.exports = function(file, api, opts) {
6 | t.description = 'Use UTF-8 symbols instead of HTML entities. If you turn on escaping this code will be broken. With non visible UTF-8 symbols like non breaking space you can leave verbose comment in code.';
7 |
8 | t.find = function(file, j) {
9 | return j(file.source)
10 | .find(j.Literal)
11 | .filter(function(p) {
12 | return /\&[\w\D]+;/.test(p.value.value);
13 | });
14 | };
15 |
16 | return t.run(file, api, opts);
17 | };
18 |
--------------------------------------------------------------------------------
/test/bemcontext-isshorttag-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var compile = fixtures.compile;
3 |
4 | describe('BEMContext this.isShortTag(str)', function() {
5 | var bemhtml;
6 |
7 | before(function() {
8 | bemhtml = compile(function() {
9 | block('b').def()(function() {
10 | return this.isShortTag(this.ctx.tag);
11 | });
12 | });
13 | });
14 |
15 | it('should return true for br', function() {
16 | bemhtml.apply({ block: 'b', tag: 'br' }).should.equal(true);
17 | });
18 |
19 | it('should return false for form', function() {
20 | bemhtml.apply({ block: 'b', tag: 'form' }).should.equal(false);
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/migration/lib/transformers/8-attrs-to-addattrs.js:
--------------------------------------------------------------------------------
1 | var log = require('../logger');
2 | var Transformer = require('../transformer');
3 | var t = new Transformer();
4 |
5 | module.exports = function(file, api, opts) {
6 | t.description = 'From v8.x you should use addAttrs() instead of attrs()';
7 |
8 | t.find = function(file, j) {
9 | return j(file.source)
10 | .find(j.CallExpression, { callee: { type: 'Identifier', name: 'attrs' } });
11 | };
12 |
13 | t.replace = function(ret, j) {
14 | return ret
15 | .replaceWith(function(p) {
16 | return j.callExpression(j.identifier('addAttrs'), []);
17 | });
18 | };
19 |
20 | return t.run(file, api, opts);
21 | };
22 |
--------------------------------------------------------------------------------
/examples/source-maps/demo-in-nodejs/test-in-node.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs');
2 | var bemxjst = require('../../../');
3 | const BUNDLE = 'bundle.bemhtml.js';
4 | const TMPL = 'b1.bemhtml.js';
5 |
6 | var bundle = bemxjst.bemhtml.generate(fs.readFileSync(TMPL, 'utf8'), {
7 | to: BUNDLE,
8 | sourceMap: { from: TMPL, include: false }
9 | });
10 |
11 | // Create bundle with bem-xjst enginge and templates
12 | fs.writeFileSync(BUNDLE,
13 | 'require(\'source-map-support\').install();' +
14 | bundle.content +
15 | 'bemhtml.apply({block:"b1"});\n//# sourceMappingURL=' + BUNDLE + '.map');
16 |
17 | // Create source map file
18 | fs.writeFileSync(BUNDLE + '.map', JSON.stringify(bundle.sourceMap, null, '\t'));
19 |
--------------------------------------------------------------------------------
/test/bemcontext-block-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 |
4 | describe('BEMContent this.block', function() {
5 | it('should support this.block', function() {
6 | test(function() {
7 | block('b').def()(function() {
8 | return this.block;
9 | });
10 | },
11 | { block: 'b' },
12 | 'b');
13 | });
14 |
15 | it('should properly set this.block', function() {
16 | test(function() {
17 | block('b').elem('e').def()(function() {
18 | return this.block;
19 | });
20 | },
21 | {
22 | block: 'b',
23 | content: { elem: 'e' }
24 | },
25 | 'b
');
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/runtime-lint/README.md:
--------------------------------------------------------------------------------
1 | # Runtime linter
2 |
3 | Runtime linter can be used for runtime check templates and BEMJSON. To use runtime linter you need to turn on [`runtimeLint` option](https://github.com/bem/bem-xjst/blob/master/docs/en/3-api.md#runtime-linting).
4 |
5 | List of checks:
6 |
7 | * boolean value in attributes (recomend to use string value)
8 | * mods for elem
9 | * changes or additons in `this.ctx.mods`
10 | * class in `attrs` (recomend use `cls`)
11 | * `data-bem` attribute in `attrs` (recomend use `js`)
12 | * mix the same mod of elemMod (warning about useless operation)
13 | * wrong naming (elem delimeter in block names or mod delimeter in mod name and etc)
14 | * `mods` and `elemMods` values can’t be an Array type
15 |
--------------------------------------------------------------------------------
/examples/simple-page/README.md:
--------------------------------------------------------------------------------
1 | # bem-xjst example: simple page
2 |
3 | Simplest usage example of bem-xjst.
4 |
5 | Install dependencies (bem-xjst):
6 | ```bash
7 | npm install
8 | ```
9 |
10 | Run example:
11 | ```bash
12 | node index.js > index.html
13 | ```
14 |
15 | The result is `index.html` file with markup of simple page on top of `data.js`.
16 |
17 | Project structure:
18 |
19 | * [index.js](index.js) — entry point.
20 | * [data.js](data.js) — typical data from any API or your backend.
21 | * [bemtree-templates.js](bemtree-templates.js) — templates to convert JSON to [BEMJSON](https://github.com/bem/bem-xjst/blob/master/docs/en/4-data.md).
22 | * [bemhtml-templates.js](bemhtml-templates.js) — templates to convert BEMJSON to HTML.
23 |
--------------------------------------------------------------------------------
/migration/lib/transformers/7-thisisarray-to-arrayisarray.js:
--------------------------------------------------------------------------------
1 | var log = require('../logger');
2 | var Transformer = require('../transformer');
3 | var t = new Transformer();
4 |
5 | module.exports = function(file, api, opts) {
6 | t.description = 'this.isArray() is deprecated. Please use Array.isArray().';
7 |
8 | t.find = function(file, j) {
9 | return j(file.source)
10 | .find(j.MemberExpression, {
11 | property: { type: 'Identifier', name: 'isArray' },
12 | object: { type: 'ThisExpression' }
13 | });
14 | };
15 |
16 | t.replace = function(ret, j) {
17 | return ret.replaceWith(function(p) {
18 | return 'Array.isArray';
19 | });
20 | };
21 |
22 | return t.run(file, api, opts);
23 | };
24 |
--------------------------------------------------------------------------------
/migration/lib/transformers/7-once-to-def.js:
--------------------------------------------------------------------------------
1 | var log = require('../logger');
2 | var Transformer = require('../transformer');
3 | var t = new Transformer();
4 |
5 | module.exports = function(file, api, opts) {
6 | t.description = 'once() is deprecated. Please use def().';
7 |
8 | t.find = function(file, j) {
9 | return j(file.source)
10 | .find(j.MemberExpression, {
11 | property: { type: 'Identifier' },
12 | object: { callee: { type: 'Identifier', name: 'block' }}
13 | })
14 | .find(j.Identifier, { name: 'once' });
15 | };
16 |
17 | t.replace = function(ret, j) {
18 | return ret.replaceWith(function(p) { return j.identifier('def'); });
19 | };
20 |
21 | return t.run(file, api, opts);
22 | };
23 |
--------------------------------------------------------------------------------
/migration/lib/transformers/5-elemmatch-to-elem-match.js:
--------------------------------------------------------------------------------
1 | var log = require('../logger');
2 | var Transformer = require('../transformer');
3 | var t = new Transformer();
4 |
5 | module.exports = function(file, api, opts) {
6 | t.description = 'elemMatch is deprecated. Please use elem(\'*\').match(function() { … })';
7 |
8 | t.find = function(file, j) {
9 | return j(file.source)
10 | .find(j.Identifier, { name: 'elemMatch' });
11 | };
12 |
13 | t.replace = function(ret, j) {
14 | return ret
15 | .replaceWith(
16 | j.memberExpression(
17 | j.callExpression(j.identifier('elem'), [ j.literal('*') ]),
18 | j.identifier('match'))
19 | );
20 | };
21 |
22 | return t.run(file, api, opts);
23 | };
24 |
--------------------------------------------------------------------------------
/test/bemcontext-elem-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 |
4 | describe('BEMContent this.elem', function() {
5 | it('should support this.elem', function() {
6 | test(function() {
7 | block('b').elem('e').def()(function() {
8 | return this.elem;
9 | });
10 | },
11 | { block: 'b', elem: 'e' },
12 | 'e');
13 | });
14 |
15 | it('should properly set this.block', function() {
16 | test(function() {
17 | block('b2').def()(function() {
18 | return this.elem;
19 | });
20 | },
21 | {
22 | block: 'b1',
23 | elem: 'e',
24 | content: { block: 'b2' }
25 | },
26 | 'undefined
');
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/test/bemcontext-extend-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 |
4 | describe('BEMContext this.extend()', function() {
5 | it('should extend empty target', function() {
6 | test(function() {
7 | block('button').def()(function() {
8 | return this.extend(null, { foo: 'bar' }).foo;
9 | });
10 | },
11 | { block: 'button' },
12 | 'bar');
13 | });
14 |
15 | it('should extend object', function() {
16 | test(function() {
17 | block('button').def()(function() {
18 | return this.extend(
19 | { foo: 'bar' },
20 | { foo: 'foo' }
21 | ).foo;
22 | });
23 | },
24 | { block: 'button' },
25 | 'foo');
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/migration/lib/transformers/8-chain-js-to-chain-addjs.js:
--------------------------------------------------------------------------------
1 | var log = require('../logger');
2 | var Transformer = require('../transformer');
3 | var t = new Transformer();
4 |
5 | module.exports = function(file, api, opts) {
6 | t.description = 'From v8.x you should use addJs() instead of js()';
7 |
8 | t.find = function(file, j) {
9 | return j(file.source)
10 | .find(j.MemberExpression, {
11 | property: { type: 'Identifier' },
12 | object: { callee: { type: 'Identifier', name: 'block' }}
13 | })
14 | .find(j.Identifier, { name: 'js' });
15 | };
16 |
17 | t.replace = function(ret, j) {
18 | return ret.replaceWith(function(p) {
19 | return j.identifier('addJs');
20 | });
21 | };
22 |
23 | return t.run(file, api, opts);
24 | };
25 |
--------------------------------------------------------------------------------
/migration/lib/transformers/8-chain-mix-to-chain-addmix.js:
--------------------------------------------------------------------------------
1 | var log = require('../logger');
2 | var Transformer = require('../transformer');
3 | var t = new Transformer();
4 |
5 | module.exports = function(file, api, opts) {
6 | t.description = 'From v8.x you should use addMix() instead of mix()';
7 |
8 | t.find = function(file, j) {
9 | return j(file.source)
10 | .find(j.MemberExpression, {
11 | property: { type: 'Identifier' },
12 | object: { callee: { type: 'Identifier', name: 'block' }}
13 | })
14 | .find(j.Identifier, { name: 'mix' });
15 | };
16 |
17 | t.replace = function(ret, j) {
18 | return ret.replaceWith(function(p) {
19 | return j.identifier('addMix');
20 | });
21 | };
22 |
23 | return t.run(file, api, opts);
24 | };
25 |
--------------------------------------------------------------------------------
/migration/lib/transformers/0-dont-check-this-mods.js:
--------------------------------------------------------------------------------
1 | var log = require('../logger');
2 | var Transformer = require('../transformer');
3 | var t = new Transformer();
4 |
5 | module.exports = function(file, api, opts) {
6 | t.description = 'this.mods always exists. You don’t need to check it.';
7 |
8 | t.find = function(file, j) {
9 | return j(file.source)
10 | .find(j.LogicalExpression, {
11 | left: {
12 | type: 'MemberExpression',
13 | object: { type: 'ThisExpression' },
14 | property: { name: 'mods' }
15 | }
16 | });
17 | };
18 |
19 | t.replace = function(ret, j) {
20 | return ret.map(function(item) {
21 | return item.replace(item.value.right);
22 | })
23 | };
24 |
25 | return t.run(file, api, opts);
26 | };
27 |
--------------------------------------------------------------------------------
/migration/lib/transformers/8-chain-attrs-to-chain-addattrs.js:
--------------------------------------------------------------------------------
1 | var log = require('../logger');
2 | var Transformer = require('../transformer');
3 | var t = new Transformer();
4 |
5 | module.exports = function(file, api, opts) {
6 | t.description = 'From v8.x you should use addAttrs() instead of attrs()';
7 |
8 | t.find = function(file, j) {
9 | return j(file.source)
10 | .find(j.MemberExpression, {
11 | property: { type: 'Identifier' },
12 | object: { callee: { type: 'Identifier', name: 'block' }}
13 | })
14 | .find(j.Identifier, { name: 'attrs' });
15 | };
16 |
17 | t.replace = function(ret, j) {
18 | return ret.replaceWith(function(p) {
19 | return j.identifier('addAttrs');
20 | });
21 | };
22 |
23 | return t.run(file, api, opts);
24 | };
25 |
--------------------------------------------------------------------------------
/migration/lib/transformers/0-dont-check-this-elemmods.js:
--------------------------------------------------------------------------------
1 | var log = require('../logger');
2 | var Transformer = require('../transformer');
3 | var t = new Transformer();
4 |
5 | module.exports = function(file, api, opts) {
6 | t.description = 'this.elemMods always exists. You don’t need to check it.';
7 |
8 | t.find = function(file, j) {
9 | return j(file.source)
10 | .find(j.LogicalExpression, {
11 | left: {
12 | type: 'MemberExpression',
13 | object: { type: 'ThisExpression' },
14 | property: { name: 'elemMods' }
15 | }
16 | });
17 | };
18 |
19 | t.replace = function(ret, j) {
20 | return ret.map(function(item) {
21 | return item.replace(item.value.right);
22 | });
23 | };
24 |
25 | return t.run(file, api, opts);
26 | };
27 |
--------------------------------------------------------------------------------
/lib/bemtree/entity.js:
--------------------------------------------------------------------------------
1 | var inherits = require('inherits');
2 | var BemxjstEntity = require('../bemxjst/entity').Entity;
3 |
4 | function Entity() {
5 | BemxjstEntity.apply(this, arguments);
6 | }
7 |
8 | inherits(Entity, BemxjstEntity);
9 | exports.Entity = Entity;
10 |
11 | Entity.prototype.defaultBody = function(context) {
12 | context.mods = this.mods.exec(context);
13 | if (context.ctx.elem) context.elemMods = this.elemMods.exec(context);
14 |
15 | return this.bemxjst.render(context,
16 | this,
17 | this.content.exec(context),
18 | this.js.exec(context),
19 | this.mix.exec(context),
20 | context.mods,
21 | context.elemMods);
22 | };
23 |
--------------------------------------------------------------------------------
/test/modes-custom-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 |
4 | describe('Modes custom', function() {
5 | it('should support custom modes', function () {
6 | test(function() {
7 | block('b1').mode('custom')('ok');
8 | block('b1').content()(function() {
9 | return apply('custom');
10 | });
11 | }, {
12 | block: 'b1'
13 | }, 'ok
');
14 | });
15 |
16 | it('should support custom modes with changes', function () {
17 | test(function() {
18 | block('b1').mode('custom')(function() {
19 | return this.yikes;
20 | });
21 | block('b1').content()(function() {
22 | return apply('custom', { yikes: 'ok' });
23 | });
24 | }, {
25 | block: 'b1'
26 | }, 'ok
');
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/migration/lib/transformers/0-func-to-simple.js:
--------------------------------------------------------------------------------
1 | var log = require('../logger');
2 | var Transformer = require('../transformer');
3 | var t = new Transformer();
4 |
5 | module.exports = function(file, api, opts) {
6 | t.description = 'Function that returns a literal can be replaced with literal itself.';
7 |
8 | t.find = function(file, j) {
9 | return j(file.source)
10 | .find(j.FunctionExpression)
11 | .filter(function(p) {
12 | var body = p.node.body.body;
13 | return body.length === 1 &&
14 | body[0].type === 'ReturnStatement' &&
15 | body[0].argument.type === 'Literal';
16 | });
17 | };
18 |
19 | t.replace = function(ret, j) {
20 | return ret.replaceWith(function(p) {
21 | return j.literal(p.node.body.body[0].argument.value);
22 | });
23 | };
24 |
25 | return t.run(file, api, opts);
26 | };
27 |
--------------------------------------------------------------------------------
/test/bemjson-tag-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 |
4 | describe('BEMJSON tag', function() {
5 | it('should render default tag as `div`', function() {
6 | test(function() {},
7 | { block: 'b' },
8 | '');
9 | });
10 |
11 | it('should return html tag', function() {
12 | test(function() {
13 | block('btn').def()(function() {
14 | return this.ctx.tag;
15 | });
16 | },
17 | { block: 'btn', tag: 'button' },
18 | 'button');
19 | });
20 |
21 | it('should render without tag', function() {
22 | test(function() {
23 | }, { tag: false, content: 'ok' }, 'ok');
24 | });
25 |
26 | it('should render empty string ' +
27 | 'if block with no content and no tag', function() {
28 | test(function() {
29 | }, { block: 'test', tag: false }, '');
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/test/bemcontext-custom-error-logger-test.js:
--------------------------------------------------------------------------------
1 | var assert = require('assert');
2 | var bemhtml = require('../').bemhtml;
3 | var sinon = require('sinon');
4 |
5 | describe('BEMContext: custom error logger', function() {
6 | it('should use custom function', function() {
7 | var templates = bemhtml.compile(function() {
8 | block('b1').attrs()(function() {
9 | var attrs = applyNext();
10 | attrs.foo = 'bar';
11 | return attrs;
12 | });
13 | }, { production: true });
14 |
15 | var p = templates.BEMContext.prototype;
16 | p.onError = function(context, e) {
17 | console.info('>>> Error occurred', context.ctx, e);
18 | };
19 | var onError = sinon.spy(p, 'onError');
20 |
21 | assert.doesNotThrow(function() {
22 | assert.equal(templates.apply({ block: 'b1' }), '');
23 | });
24 |
25 | sinon.assert.calledOnce(onError);
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ### BEFORE YOU SUBMIT please do the following:
2 | * Search if [the issue already open or closed](https://github.com/bem/bem-xjst/issues?utf8=✓&q=is%3Aissue%20)?
3 | * Is there is answer in [documentation](https://github.com/bem/bem-xjst/tree/master/docs)?
4 | * Is there is answer in Readme? [README](https://github.com/bem/bem-xjst/blob/master/README.md)
5 | * Is there is answer in release notes? [CHANGELOG](https://github.com/bem/bem-xjst/blob/master/CHANGELOG.md)
6 | * Is there is answer in migration guides? [1.x to 2.x](https://github.com/bem/bem-xjst/wiki/Notable-changes-between-bem-xjst@1.x-and-bem-xjst@2.x) or [4.x to 5.x](https://github.com/bem/bem-xjst/wiki/Migration-guide-from-4.x-to-5.x)
7 |
8 | ### Input code or something about issue background
9 |
10 | ### Expected Behavior
11 |
12 | ### Actual Behavior
13 |
14 | ### Possible Solution
15 |
16 | ### Your Environment
17 |
18 | bem-xjst version and all relevant details
19 |
--------------------------------------------------------------------------------
/migration/lib/transformers/5-api-changed.js:
--------------------------------------------------------------------------------
1 | var log = require('../logger');
2 |
3 | module.exports = function(file, api, opts) {
4 | var j = api.jscodeshift;
5 |
6 | var ret = j(file.source)
7 | .find(j.CallExpression, {
8 | callee: { type: 'Identifier', name: 'require' },
9 | arguments: [ { type: 'Literal', value: 'bem-xjst' } ]
10 | });
11 |
12 |
13 | if (opts.lint) {
14 | if (ret.length === 0)
15 | return;
16 |
17 | ret.forEach(function(p) {
18 | log({
19 | descr: 'bem-xjst API changed: require(\'bem-xjst\') now returns two engines bemhtml and bemtree.',
20 | path: p.value,
21 | ret: ret,
22 | file: file
23 | });
24 | });
25 |
26 | } else {
27 | return ret
28 | .replaceWith(function(p) {
29 | var val = p.value;
30 |
31 | return j.memberExpression(val, j.identifier('bemhtml'))
32 | })
33 | .toSource({ quote: 'single' });
34 | }
35 | };
36 |
--------------------------------------------------------------------------------
/bench/README.md:
--------------------------------------------------------------------------------
1 | # Benchmarks
2 |
3 | ## New benchmark test
4 |
5 | 1. Install dependencies: `npm i`
6 | 2. Install python and gnuplot (e.g. `brew install gnuplot`) if you want to see charts
7 | 3. Prepare json data files and template (you can request large production bemjson files from @miripiruni)
8 | 4. Run test
9 |
10 | `node runner.js --rev1 d53646f2c340b5496fbd75a5313e3284b58e238d --rev2 d53646f2c340b5496fbd75a5313e3284b58e238d --bemjson 1000 --dataPath ~/web-data/data --templatePath ~/web-data/templates.js`
11 |
12 | `--dataPath` and `--templatePath` should point to your data files and template
13 |
14 | The results of test would be directory `dat-d5364-and-d5364` with svg histogram and STDOUT with some numbers.
15 |
16 | ## Old test
17 |
18 | Or you can run old test:
19 |
20 | 1. Make sure the `bench/package.json` has proper git hash of `bem-xjst` before running!
21 | 2. Run `node run.js --compile --compare --min-samples 1000`
22 |
23 | Benchmark help:
24 |
25 | ```bash
26 | node run.js --help
27 | ```
28 |
--------------------------------------------------------------------------------
/.github/workflows/node.js.yml:
--------------------------------------------------------------------------------
1 | # This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3 |
4 | name: Node.js CI
5 |
6 | on:
7 | push:
8 | branches: [ master ]
9 | pull_request:
10 | branches: [ master ]
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: ubuntu-latest
16 |
17 | strategy:
18 | matrix:
19 | node-version: [10.x, 12.x, 14.x, 16.x]
20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
21 |
22 | steps:
23 | - uses: actions/checkout@v2
24 | - name: Use Node.js ${{ matrix.node-version }}
25 | uses: actions/setup-node@v2
26 | with:
27 | node-version: ${{ matrix.node-version }}
28 | cache: 'npm'
29 | - run: npm ci
30 | - run: npm run build --if-present
31 | - run: npm test
32 |
--------------------------------------------------------------------------------
/test/bemcontext-custom-fields-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 |
4 | describe('BEMContext: custom BEMJSON fields', function() {
5 | it('should preserve users fields', function() {
6 | test(function() {
7 | block('select').elem('control').def()(function() {
8 | this.lol = 333;
9 | return applyNext();
10 | });
11 | block('select').def()(function() {
12 | this.foo = 222;
13 | return applyNext();
14 | });
15 | block('select').mod('disabled', true).def()(function() {
16 | this.bar = 111;
17 | return applyNext();
18 | });
19 | block('select').elem('control').def()(function() {
20 | applyNext();
21 | return this.foo + this.bar + this.lol;
22 | });
23 | },
24 | {
25 | block: 'select',
26 | mods: { disabled: true },
27 | content: { elem: 'control' }
28 | },
29 | '666
');
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/test/runtime-applyctx-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 |
4 | describe('Runtime applyCtx()', function() {
5 | it('should work with just context', function() {
6 | test(function() {
7 | block('b1').content()(function() {
8 | return applyCtx([
9 | { block: 'b2', content: 'omg' },
10 | { block: 'b3', tag: 'br' }
11 | ]);
12 | });
13 | },
14 | { block: 'b1' },
15 | '' +
16 | '<div class="b2">omg</div><br class="b3">
');
17 | });
18 |
19 | it('should work with both context and changes', function() {
20 | test(function() {
21 | block('b2').content()(function() {
22 | return this.wtf;
23 | });
24 |
25 | block('b1').content()(function() {
26 | return applyCtx([ { block: 'b2' } ], { wtf: 'ohai' });
27 | });
28 | },
29 | { block: 'b1' },
30 | '<div class="b2">ohai</div>
');
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/migration/lib/transformers/2-no-empty-mode.js:
--------------------------------------------------------------------------------
1 | var log = require('../logger');
2 | var Transformer = require('../transformer');
3 | var t = new Transformer();
4 |
5 | module.exports = function(file, api, opts) {
6 | t.description = 'We find definition of empty mode. Empty mode(\'\') is no longer supported. Please read: https://github.com/bem/bem-xjst/wiki/Notable-changes-between-bem-xjst@1.x-and-bem-xjst@2.x#user-content-mode-no-more';
7 |
8 | t.find = function(file, j) {
9 | return j(file.source)
10 | .find(j.CallExpression,
11 | { callee: {
12 | type: 'CallExpression',
13 | arguments: [ { type: 'Literal', value: '' } ]
14 | } });
15 | };
16 |
17 | t.replace = function(ret, j) {
18 | return ret
19 | .replaceWith(function(p) {
20 | return j.callExpression(
21 | j.callExpression(j.identifier('mode'),
22 | [ j.literal('custom-mode') ]),
23 | p.node.arguments);
24 | });
25 | };
26 |
27 | return t.run(file, api, opts);
28 | };
29 |
--------------------------------------------------------------------------------
/test/runtime-bemcontext-test.js:
--------------------------------------------------------------------------------
1 | var assert = require('assert');
2 | var bemhtml = require('./fixtures')('bemhtml');
3 |
4 | describe('Runtime BEMContext', function() {
5 | it('should support extending of templates.BEMContext prototype', function() {
6 | var templates = bemhtml.compile();
7 | templates.BEMContext.prototype.myField = 'opa';
8 | templates.compile(function() {
9 | block('b').content()(function() { return this.myField; });
10 | });
11 | assert.equal(templates.apply({ block: 'b' }), 'opa
');
12 | });
13 |
14 | it('should redefine templates.BEMContext prototype later', function() {
15 | var templates = bemhtml.compile();
16 | var bemjson = { block: 'b', tag: false };
17 | templates.BEMContext.prototype.what = 'hip';
18 | templates.compile(function() {
19 | block('b').content()(function() { return this.what; });
20 | });
21 | assert.equal(templates.apply(bemjson), 'hip');
22 | templates.BEMContext.prototype.what = 'hipier';
23 | assert.equal(templates.apply(bemjson), 'hipier');
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/docs/en/2-quick-start.md:
--------------------------------------------------------------------------------
1 | # Quick start
2 |
3 | ## Installation
4 |
5 | To use bem-xjst, you need [Node.js](https://nodejs.org/) v0.10 or later, and [npm](https://www.npmjs.com/).
6 |
7 | Install:
8 |
9 | ```bash
10 | npm install bem-xjst
11 | ```
12 |
13 | ## Basic example
14 |
15 | ```js
16 | var bemxjst = require('bem-xjst');
17 |
18 | // bem-xjst contains two engines, BEMHTML and BEMTREE (starting from v5.0.0)
19 | // Choose the BEMHTML engine
20 | var bemhtml = bemxjst.bemhtml;
21 |
22 | // Add templates using the `compile` method
23 | var templates = bemhtml.compile(() => {
24 | block('text')({ tag: 'span' });
25 | });
26 |
27 | // Add data in BEMJSON format
28 | var bemjson = [
29 | { block: 'text', content: 'First' },
30 | { block: 'text', content: 'Second' }
31 | ];
32 |
33 | // Apply templates
34 | var html = templates.apply(bemjson);
35 | ```
36 |
37 | The resulting `html` contains this string:
38 |
39 | ```html
40 | FirstSecond
41 | ```
42 |
43 | [Online demo](https://bem.github.io/bem-xjst/).
44 |
45 | Read next: [API](3-api.md)
46 |
--------------------------------------------------------------------------------
/migration/lib/transformer.js:
--------------------------------------------------------------------------------
1 | var log = require('./logger');
2 |
3 | function Transformer() {
4 | this.init();
5 | };
6 |
7 | Transformer.prototype.init = function() {};
8 |
9 | Transformer.prototype.description = '';
10 |
11 | Transformer.prototype.replace = function(ret) {
12 | return ret;
13 | };
14 |
15 | Transformer.prototype.find = function(file) {
16 | return file;
17 | };
18 |
19 | Transformer.prototype.run = function(file, api, opts) {
20 | var j = api.jscodeshift;
21 | var ret = this.find(file, j);
22 | var config = opts.config ? require(opts.config) : {};
23 |
24 | if (!config.quote) config.quote = 'single';
25 |
26 | if (opts.lint) {
27 | if (ret.length === 0) return;
28 | this.log(ret, file);
29 | return;
30 | }
31 |
32 | return this.replace(ret, j).toSource(config);
33 | };
34 |
35 | Transformer.prototype.log = function(ret, file) {
36 | var t = this;
37 |
38 | ret.forEach(function(p) {
39 | log({
40 | descr: t.description,
41 | path: p.value,
42 | ret: ret,
43 | file: file
44 | });
45 | });
46 | };
47 |
48 | module.exports = Transformer;
49 |
--------------------------------------------------------------------------------
/migration/lib/transformers/2-no-empty-mode-call.js:
--------------------------------------------------------------------------------
1 | var log = require('../logger');
2 | var Transformer = require('../transformer');
3 | var t = new Transformer();
4 |
5 | module.exports = function(file, api, opts) {
6 | t.description = 'We find a call of empty mode. Empty mode mode(\'\') is no longer supported. Please read: https://github.com/bem/bem-xjst/wiki/Notable-changes-between-bem-xjst@1.x-and-bem-xjst@2.x#user-content-mode-no-more';
7 |
8 | t.find = function(file, j) {
9 | return j(file.source)
10 | .find(j.CallExpression, { callee: { type: 'Identifier', name: 'apply' } })
11 | .filter(function(p) {
12 | return p.value.arguments.filter(function(arg) {
13 | return arg.value === '';
14 | }).length !== 0;
15 | });
16 | };
17 |
18 | t.replace = function(ret, j) {
19 | return ret
20 | .replaceWith(function(p) {
21 | var args = p.node.arguments;
22 | args[0] = j.literal('custom-mode');
23 |
24 | return j.callExpression(j.identifier('apply'), args);
25 | });
26 | };
27 |
28 | return t.run(file, api, opts);
29 | };
30 |
--------------------------------------------------------------------------------
/docs/ru/2-quick-start.md:
--------------------------------------------------------------------------------
1 | # Быстрый старт
2 |
3 | ## Установка
4 |
5 | Для использования bem-xjst вам понадобится [Node.js](https://nodejs.org/) v0.10 и выше и [npm](https://www.npmjs.com/).
6 |
7 | Установка:
8 |
9 | ```bash
10 | npm install bem-xjst
11 | ```
12 |
13 | ## Простой пример
14 |
15 | ```js
16 | var bemxjst = require('bem-xjst');
17 |
18 | // bem-xjst содержит два движка: BEMHTML и BEMTREE (начиная с v5.0.0)
19 | // Выбираем движок BEMHTML
20 | var bemhtml = bemxjst.bemhtml;
21 |
22 | // Добавляем шаблоны с помощью метода compile
23 | var templates = bemhtml.compile(() => {
24 | block('text')({ tag: 'span' });
25 | });
26 |
27 | // Добавляем данные в формате BEMJSON
28 | var bemjson = [
29 | { block: 'text', content: 'Первый' },
30 | { block: 'text', content: 'Второй' }
31 | ];
32 |
33 | // Применяем шаблоны
34 | var html = templates.apply(bemjson);
35 | ```
36 |
37 | В результате `html` будет содержать строку:
38 |
39 | ```html
40 | ПервыйВторой
41 | ```
42 |
43 | [Online демо](https://bem.github.io/bem-xjst/).
44 |
45 | Читать далее: [API](3-api.md)
46 |
--------------------------------------------------------------------------------
/migration/lib/transformers/2-no-more-this-underscore.js:
--------------------------------------------------------------------------------
1 | var log = require('../logger');
2 | var Transformer = require('../transformer');
3 | var t = new Transformer();
4 |
5 | module.exports = function(file, api, opts) {
6 | t.description = 'All helpers have finally migrated to this from this._. From now on this._ will be undefined. Please read: https://github.com/bem/bem-xjst/wiki/Notable-changes-between-bem-xjst@1.x-and-bem-xjst@2.x#this_-no-more';
7 |
8 | t.find = function(file, j) {
9 | return j(file.source)
10 | .find(j.MemberExpression)
11 | .filter(function(p) {
12 | var node = p.node;
13 | var property = node.object && node.object.property;
14 |
15 | return property &&
16 | property.name === '_' &&
17 | node.object.object &&
18 | node.object.object.type === 'ThisExpression';
19 | });
20 | };
21 |
22 | t.replace = function(ret, j) {
23 | return ret.replaceWith(function(p) {
24 | return j.memberExpression(
25 | j.thisExpression(),
26 | j.identifier(p.node.property.name)
27 | );
28 | });
29 | };
30 |
31 | return t.run(file, api, opts);
32 | };
33 |
--------------------------------------------------------------------------------
/examples/simple-page/data.js:
--------------------------------------------------------------------------------
1 | // This is typical data from any API or your backend
2 | module.exports = {
3 | name: {
4 | first: 'Slava',
5 | last: 'Oliyanchuk',
6 | },
7 | username: 'miripiruni',
8 | position: 'Front-End Web Developer',
9 | avatar: {
10 | small: {
11 | url: 'http://miripiruni.org/i/miripiruni_moscow_27-03-2011-250x375.jpg',
12 | author: 'Patrick H. Lauke',
13 | authorUrl: 'http://www.splintered.co.uk/about/'
14 | },
15 | big: {
16 | url: 'http://miripiruni.org/i/miripiruni_moscow_27-03-2011.jpg',
17 | author: 'Patrick H. Lauke',
18 | authorUrl: 'http://www.splintered.co.uk/about/'
19 | },
20 | },
21 | email: 'mail@miripiruni.org',
22 | profiles: [
23 | {
24 | service: 'Soundcloud',
25 | url: 'https://soundcloud.com/miripiruni'
26 | },
27 | {
28 | service: 'Bandcamp',
29 | url: 'https://miripiruni.bandcamp.com'
30 | },
31 | {
32 | service: 'Github',
33 | url: 'https://github.com/miripiruni'
34 | },
35 | {
36 | url: 'https://twitter.com/miripiruni'
37 | },
38 | {
39 | url: 'http://wantr.ru/miripiruni'
40 | },
41 | ]
42 | };
43 |
--------------------------------------------------------------------------------
/test/bemcontext-elemmods-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 |
4 | describe('BEMContext this.elemMods', function() {
5 | it('should support elemMods', function() {
6 | test(function() {
7 | block('b').elem('e').def()(function() {
8 | return JSON.stringify(this.elemMods);
9 | });
10 | },
11 | {
12 | block: 'b',
13 | elem: 'e',
14 | elemMods: { type: 'button' }
15 | },
16 | '{"type":"button"}');
17 | });
18 |
19 | it('should lazily define elemMods', function() {
20 | test(function() {
21 | block('b1').elem('e1').content()(function() {
22 | return this.elemMods.a || 'yes';
23 | });
24 | }, { block: 'b1', content: { elem: 'e1' } },
25 | '');
26 | });
27 |
28 | it('should support changing elemMods in runtime', function() {
29 | test(function() {
30 | block('b1').elem('e1').def()(function() {
31 | this.elemMods.a = 'b';
32 | return applyNext();
33 | });
34 | }, {
35 | block: 'b1',
36 | elem: 'e1'
37 | }, '');
38 | });
39 | });
40 |
--------------------------------------------------------------------------------
/test/custom-naming-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 |
4 | describe('Custom delimeters for BEM naming', function() {
5 | it('should support custom naming', function() {
6 | test(function() {
7 | }, [
8 | {
9 | block: 'b1',
10 | elem: 'e1',
11 | elemMods: {
12 | a: 'b'
13 | }
14 | },
15 | {
16 | block: 'b1',
17 | elem: 'e1'
18 | },
19 | {
20 | block: 'b1',
21 | mix: { elem: 'e2' }
22 | }
23 | ], '' +
24 | '' +
25 | '', {
26 | naming: {
27 | elem: '$$',
28 | mod: '@@'
29 | }
30 | });
31 | });
32 |
33 | it('should support custom naming ' +
34 | 'for modName and modVal delimeters', function() {
35 | test(function() {
36 | }, [
37 | {
38 | block: 'b1',
39 | elem: 'e1',
40 | elemMods: { a: 'b' }
41 | }
42 | ], '', {
43 | naming: {
44 | elem: '__',
45 | mod: { name: '--', val: '_' }
46 | }
47 | });
48 | });
49 | });
50 |
--------------------------------------------------------------------------------
/test/bemjson-elem-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 |
4 | describe('BEMJSON elem', function() {
5 | it('should assume elem=\'\' is a falsey value', function() {
6 | test(function() {
7 | block('b1').elem('e1').def()(function() {
8 | return applyCtx(this.extend(this.ctx, {
9 | block: 'b2',
10 | elem: ''
11 | }));
12 | });
13 | }, { block: 'b1' }, '');
14 | });
15 |
16 | it('wildcard elem should be called before the matched templates',
17 | function() {
18 | test(function() {
19 | block('b1').content()(function() {
20 | return 'block';
21 | });
22 | block('b1').elem('a').content()(function() {
23 | return 'block-a';
24 | });
25 | block('b1').elem('*').content()(function() {
26 | return '%' + applyNext() + '%';
27 | });
28 | }, [ {
29 | block: 'b1'
30 | }, {
31 | block: 'b1',
32 | elem: 'a'
33 | }, {
34 | block: 'b3',
35 | elem: 'b',
36 | content: 'ok'
37 | } ], 'block
' +
38 | '%block-a%
' +
39 | '%ok%
');
40 | });
41 | });
42 |
--------------------------------------------------------------------------------
/migration/lib/transformers/7-mods-value.js:
--------------------------------------------------------------------------------
1 | var log = require('../logger');
2 | var get = require('lodash.get');
3 | var Transformer = require('../transformer');
4 | var t = new Transformer();
5 |
6 | module.exports = function(file, api, opts) {
7 | t.description = 'Modifier value must be a string type';
8 |
9 | t.find = function(file, j) {
10 | return j(file.source)
11 | .find(j.Literal)
12 | .filter(function(p) {
13 | var arg = p.value.arguments;
14 |
15 | if (typeof p.value.rawValue === 'number') {
16 | var callee = get(p, 'parentPath.parentPath.value.callee');
17 |
18 | if (!callee)
19 | return false;
20 |
21 | if (callee.property && callee.property.type === 'Identifier' &&
22 | (callee.property.name === 'mod' || callee.property.name === 'elemMod')) {
23 | callee = callee.property;
24 | }
25 |
26 | return callee.name === 'mod' || callee.name === 'elemMod';
27 | }
28 |
29 | return false;
30 | });
31 | };
32 |
33 | t.replace = function(ret, j) {
34 | return ret.replaceWith(function(p) {
35 | return j.literal(p.value.rawValue.toString());
36 | });
37 | };
38 |
39 | return t.run(file, api, opts);
40 | };
41 |
--------------------------------------------------------------------------------
/examples/simple-page/bemtree-templates.js:
--------------------------------------------------------------------------------
1 | // This is BEMTREE template to convert regular data in JSON to
2 | // BEMJSON (see https://github.com/bem/bem-xjst/blob/master/docs/en/4-data.md)
3 | module.exports = function() {
4 | block('root').replace()(function() {
5 | // Save `data` in `this.data` for usage in other templates.
6 | var data = this.data = this.ctx.data;
7 |
8 | return {
9 | block: 'page',
10 | title: [ data.name.first, data.name.last, 'profile' ].join(' '),
11 | data: data
12 | };
13 | });
14 |
15 | block('page').content()(function() {
16 | var data = this.data;
17 |
18 | return [
19 | {
20 | block: 'user',
21 | content: [ data.name.first, data.name.last ].join(' '),
22 | username: data.username,
23 | position: data.position,
24 | },
25 | {
26 | block: 'avatar',
27 | avatar: data.avatar
28 | },
29 | {
30 | block: 'links',
31 | profiles: data.profiles
32 | }
33 | ];
34 | });
35 |
36 | block('links').content()(function() {
37 | return this.ctx.profiles.map(function(link) {
38 | return {
39 | elem: 'item',
40 | content: link.service,
41 | url: link.url
42 | };
43 | });
44 | });
45 | };
46 |
--------------------------------------------------------------------------------
/bench/test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const fs = require('fs');
3 |
4 | const argv = require('yargs')
5 | .describe('rev', 'bem-xjst revision (commit hash)')
6 | .describe('bemjson', 'amount of bemjson files to render (default 2000)')
7 | .describe('dataPath', 'path to bemjson files')
8 | .describe('templatePath', 'path to template file')
9 | .describe('verbose', 'verbose results')
10 | .help('h')
11 | .alias('help', 'h')
12 | .argv;
13 |
14 | const dataPath = argv.dataPath || './node_modules/web-data/data';
15 | const templatePath = argv.templatePath || './node_modules/web-data/templates.js';
16 | const files = fs.readdirSync(dataPath);
17 | const times = argv.bemjson || 2000;
18 | const secInNs = 1e9;
19 | const res = [];
20 |
21 | const bemhtml = require('./bem-xjst-' + argv.rev).bemhtml;
22 | const templates = bemhtml.compile(fs.readFileSync(templatePath, 'utf8'), { escapeContent: true });
23 |
24 | for (var i = 0; i < times; i++) {
25 | var path = dataPath + '/' + files[i % files.length];
26 | // argv.verbose && console.warn(path);
27 | var data = JSON.parse(fs.readFileSync(path, 'utf8'));
28 |
29 | var time = process.hrtime();
30 | templates.apply(data);
31 | var diff = process.hrtime(time);
32 |
33 | res.push((diff[0] * secInNs + diff[1]) / 1000000);
34 | }
35 |
36 | console.log(JSON.stringify(res));
37 |
--------------------------------------------------------------------------------
/lib/bemxjst/class-builder.js:
--------------------------------------------------------------------------------
1 | function ClassBuilder(options) {
2 | this.elemDelim = options.elem || '__';
3 |
4 | this.modDelim = typeof options.mod === 'string' ?
5 | {
6 | name: options.mod || '_',
7 | val: options.mod || '_'
8 | } :
9 | {
10 | name: options.mod && options.mod.name || '_',
11 | val: options.mod && options.mod.val || '_'
12 | };
13 | }
14 |
15 | exports.ClassBuilder = ClassBuilder;
16 |
17 | ClassBuilder.prototype.build = function(block, elem) {
18 | if (!elem)
19 | return block;
20 | else
21 | return block + this.elemDelim + elem;
22 | };
23 |
24 | ClassBuilder.prototype.buildModPostfix = function(modName, modVal) {
25 | var res = this.modDelim.name + modName;
26 | if (modVal !== true) res += this.modDelim.val + modVal;
27 | return res;
28 | };
29 |
30 | ClassBuilder.prototype.buildBlockClass = function(name, modName, modVal) {
31 | var res = name;
32 | if (modVal) res += this.buildModPostfix(modName, modVal);
33 | return res;
34 | };
35 |
36 | ClassBuilder.prototype.buildElemClass = function(block, name, modName, modVal) {
37 | return this.buildBlockClass(block) +
38 | this.elemDelim +
39 | name +
40 | this.buildModPostfix(modName, modVal);
41 | };
42 |
43 | ClassBuilder.prototype.split = function(key) {
44 | return key.split(this.elemDelim, 2);
45 | };
46 |
--------------------------------------------------------------------------------
/migration/lib/transformers/7-xhtml-true.js:
--------------------------------------------------------------------------------
1 | var log = require('../logger');
2 | var Transformer = require('../transformer');
3 | var t = new Transformer();
4 |
5 | module.exports = function(file, api, opts) {
6 | t.description = 'For backward capability use option `xhtml:true`'
7 |
8 | t.find = function(file, j) {
9 | return j(file.source)
10 | .find(j.CallExpression, {
11 | callee: { property: { type: 'Identifier', name: 'compile' }
12 | }});
13 | };
14 |
15 | t.replace = function(ret, j) {
16 | return ret
17 | .replaceWith(function(p) {
18 | var val = p.value;
19 | var args = val.arguments;
20 |
21 | if (args.length === 1) {
22 | args.push(
23 | j.objectExpression(
24 | [ j.property('init', j.identifier('xhtml'), j.literal(true)) ]
25 | )
26 | );
27 | } else if (args.length === 2) {
28 | args[1].properties = args[1].properties.filter(function(p) {
29 | return p.key.name !== 'xhtml';
30 | });
31 | args[1].properties.push(j.property('init', j.identifier('xhtml'), j.literal(true)))
32 | }
33 |
34 | return j.callExpression(
35 | j.memberExpression(j.identifier(val.callee.object.name), j.identifier('compile')),
36 | args)
37 | });
38 | };
39 |
40 | return t.run(file, api, opts);
41 | };
42 |
--------------------------------------------------------------------------------
/test/bemcontext-islast-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 |
4 | describe('BEMContext this.isLast()', function() {
5 | it('should support this.isLast()', function() {
6 | test(function() {
7 | block('b1')(
8 | match(function() { return this.isLast(); })
9 | .mix()({ mods: { position: 'last' } })
10 | );
11 | }, [
12 | {
13 | tag: 'table',
14 | content: {
15 | block: 'b1',
16 | tag: 'tr',
17 | content: [
18 | { content: '', tag: 'td' },
19 | { content: '', tag: 'td' }
20 | ]
21 | }
22 | },
23 | {
24 | block: 'b1',
25 | content: 'first'
26 | },
27 | {
28 | block: 'b1',
29 | content: 'last'
30 | }
31 | ], '' +
32 | 'first
' +
33 | 'last
');
34 | });
35 |
36 | it('should preserve position', function() {
37 | test(function() {
38 | block('button')
39 | .match(function() { return this.isLast(); })
40 | .addMods()({ last: 'yes' });
41 | },
42 | [
43 | { block: 'button' },
44 | { block: 'button' }
45 | ],
46 | '' +
47 | '');
48 | });
49 | });
50 |
--------------------------------------------------------------------------------
/test/modes-cls-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 | var assert = require('assert');
4 | var bemxjst = require('../').bemhtml;
5 | var compile = fixtures.compile;
6 |
7 | describe('Modes cls', function() {
8 | it('should throw error when args passed to cls mode', function() {
9 | assert.throws(function() {
10 | bemxjst.compile(function() {
11 | block('b1').cls('blah');
12 | });
13 | });
14 | });
15 |
16 | it('should set cls', function() {
17 | test(function() {
18 | block('button').cls()('btn');
19 | },
20 | { block: 'button' },
21 | '');
22 | });
23 |
24 | it('should not override later declarations', function() {
25 | test(function() {
26 | block('button').cls()('control');
27 | block('button').cls()('btn');
28 | },
29 | { block: 'button' },
30 | '');
31 | });
32 |
33 | it('should trim cls', function() {
34 | compile(function() {
35 | block('button').cls()(' one two ');
36 | })
37 | .apply({ block: 'button' })
38 | .should.equal('');
39 | });
40 |
41 | it('should escape cls', function() {
42 | compile(function() {
43 | block('button').cls()('">');
44 | })
45 | .apply({ block: 'button' })
46 | .should.equal('');
47 | });
48 | });
49 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # bem-xjst contributing notes
2 |
3 | If you want help you can fix issues than labeled [help wanted](https://github.com/bem/bem-xjst/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22).
4 |
5 | ## Development dependencies
6 |
7 | All `devDependencies` must fix only major version. Use `^` in package.json.
8 |
9 | ## Unit Tests
10 |
11 | To run tests cast `npm run test`.
12 |
13 | ## Benchmarks
14 |
15 | To run benchmarks see [bench readme](https://github.com/bem/bem-xjst/blob/master/bench/README.md).
16 |
17 | ## Release version
18 |
19 | ### Before
20 |
21 | 1. Functional: unit tests.
22 | 2. Performance: benchmarks.
23 | 3. Integration: check Islands and web4 (skip if you not from Yandex :).
24 | 4. Write changelog (use [changelog-maker](https://github.com/rvagg/changelog-maker)) и release notes.
25 | 5. Don’t forget about `git tag`.
26 | 6. If you build package from support branch (4.x, 5.x etc) don’t forget about [`-- tag`](https://docs.npmjs.com/cli/publish) parameter of `npm publish`.
27 |
28 | ### After
29 | 1. Merge PR about update `bem-xjst` dependency to [`enb-bemxjst`](https://github.com/enb/enb-bemxjst/). If you build package from support branch update `enb-bemxjst-6x`.
30 | 2. Write changelog `enb-bemxjst`.
31 | 3. Publish `enb-bemxjst` npm release.
32 | 4. Update bem-xjst online demo.
33 |
34 |
35 | # Online demo
36 |
37 | To update or change online demo read [manual](https://github.com/bem/bem-xjst/blob/gh-pages/README.md).
38 |
--------------------------------------------------------------------------------
/migration/lib/transformers/0-arr-to-func-generator.js:
--------------------------------------------------------------------------------
1 | var log = require('../logger');
2 | var get = require('lodash.get');
3 | var Transformer = require('../transformer');
4 | var t = new Transformer();
5 |
6 | module.exports = function(file, api, opts) {
7 | t.description = 'Use function generator instead (Example: `function() { return []; }`. ' +
8 | 'See docs: https://github.com/bem/bem-xjst/wiki/Notable-changes-' +
9 | 'between-bem-xjst@1.x-and-bem-xjst@2.x#static-objects-shortcuts-in-mix-content-etc'
10 |
11 | t.find = function(file, j) {
12 | return j(file.source)
13 | .find(j.ArrayExpression)
14 | .filter(function(p) {
15 | var arg = p.value.arguments;
16 | var callee = get(p, 'parentPath.parentPath.value.callee.callee');
17 |
18 | if (! callee)
19 | return false;
20 |
21 | callee = callee.name || (callee.property && callee.property.name);
22 |
23 | return [
24 | 'js',
25 | 'mix',
26 | 'content',
27 | 'def',
28 | 'addMix',
29 | 'appendContent',
30 | 'prependContent'
31 | ].indexOf(callee) !== -1;
32 | });
33 | };
34 |
35 | t.replace = function(ret, j) {
36 | return ret.replaceWith(function(p) {
37 | return j.functionExpression(
38 | j.identifier(''),
39 | [],
40 | j.blockStatement([j.returnStatement(p.value)])
41 | );
42 | });
43 | };
44 |
45 | return t.run(file, api, opts);
46 | };
47 |
--------------------------------------------------------------------------------
/test/modes-bem-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 | var assert = require('assert');
4 | var bemxjst = require('../').bemhtml;
5 |
6 | describe('Modes bem', function() {
7 | it('should throw error when args passed to bem mode', function() {
8 | assert.throws(function() {
9 | bemxjst.compile(function() {
10 | block('b1').bem('blah');
11 | });
12 | });
13 | });
14 |
15 | it('should return bem by default', function() {
16 | test(function() {
17 | block('button').def()(function() {
18 | return typeof this.ctx.bem;
19 | });
20 | },
21 | { block: 'button' },
22 | 'undefined');
23 | });
24 |
25 | it('should set bem to false', function() {
26 | test(function() {
27 | block('button').bem()(false);
28 | },
29 | { block: 'button' },
30 | '');
31 | });
32 |
33 | it('should not override later declarations', function() {
34 | test(function() {
35 | block('button').bem()(false);
36 | block('button').bem()(true);
37 | },
38 | { block: 'button' },
39 | '');
40 | });
41 |
42 | it('should output cls value if bem:false', function() {
43 | test(function() {
44 | block('b')(
45 | bem()(false),
46 | js()(true)
47 | );
48 | },
49 | [
50 | { block: 'b', cls: 'anything' },
51 | { block: 'b' }
52 | ],
53 | '');
54 | });
55 | });
56 |
--------------------------------------------------------------------------------
/docs/en/1-about.md:
--------------------------------------------------------------------------------
1 | # About bem-xjst
2 |
3 | ## What is bem-xjst?
4 |
5 | bem-xjst is a template engine for web development using the BEM methodology.
6 |
7 | It contains two engines:
8 |
9 | 1. **BEMHTML** — for transforming BEMJSON to HTML.
10 | 2. **BEMTREE** — for transforming BEMJSON with data to BEMJSON with a BEM tree for further transformation using BEMHTML.
11 |
12 | The template engine is based on the declarative principles of [XSLT](https://www.w3.org/TR/xslt) (eXtensible Stylesheet Language Transformations). The name XJST (eXtensible JavaScript Transformations) was also created as an analogy to XSLT.
13 |
14 | Before using the template engine, you should review:
15 |
16 | 1. [BEMJSON format for input data](4-data.md)
17 | 2. [How to write templates](5-templates-syntax.md)
18 | 3. [Processes for selecting and applying templates](7-runtime.md)
19 |
20 | ## Features
21 |
22 | 1. Templates are extensible: they can be redefined or extended.
23 | 2. Templates are written using [pattern matching](7-runtime.md#how-templates-are-selected-and-applied) for the values and structure of input data.
24 | 3. Traverses input data by default.
25 | 4. Built-in rendering behavior is used by default, even if the user didn’t add templates.
26 | 5. Written in JavaScript, so the entire JavaScript infrastructure is available for checking code quality and conforming to best practices.
27 | 6. Doesn’t require compiling templates.
28 | 7. API provided for adding templates in runtime.
29 | 8. Runs on a server and client.
30 |
31 | Read next: [Quick Start](2-quick-start.md)
32 |
--------------------------------------------------------------------------------
/test/bemcontext-generateid-test.js:
--------------------------------------------------------------------------------
1 | var assert = require('assert');
2 | var bemxjst = require('./fixtures')('bemhtml');
3 |
4 | describe('BEMContext this.generateId()', function() {
5 | var template;
6 |
7 | beforeEach(function() {
8 | template = bemxjst.compile(function() {
9 | block('b1')(
10 | tag()(''),
11 | content()(function() {
12 | return this.generateId();
13 | })
14 | );
15 | });
16 | });
17 |
18 | it('starts with uniq', function() {
19 | var str = template.apply({ block: 'b1' });
20 | assert(str.indexOf('uniq') === 0);
21 | });
22 |
23 | it('should be unique in different applies', function() {
24 | assert.notEqual(
25 | template.apply({ block: 'b1' }),
26 | template.apply({ block: 'b1' })
27 | );
28 | });
29 |
30 | it('should be unique in one apply', function() {
31 | var sep = '❄';
32 | var str = template.apply([ { block: 'b1' }, sep, { block: 'b1' } ]);
33 | var arr = str.split(sep);
34 |
35 | assert.notEqual(arr[0], arr[1]);
36 | });
37 |
38 | it('should be equal for same ctx', function() {
39 | var sep = '❄';
40 | var template = bemxjst.compile(function() {
41 | var sep = '❄';
42 | block('b2')(
43 | tag()(''),
44 | content()(function() {
45 | return [ this.generateId(), sep, this.generateId() ];
46 | })
47 | );
48 | });
49 | var str = template.apply({ block: 'b2' });
50 | var arr = str.split(sep);
51 |
52 | assert.equal(arr[0], arr[1]);
53 | });
54 | });
55 |
--------------------------------------------------------------------------------
/docs/ru/1-about.md:
--------------------------------------------------------------------------------
1 | # О bem-xjst
2 |
3 | ## Что такое bem-xjst?
4 |
5 | bem-xjst — шаблонизатор для тех, кто ведёт веб-разработку в рамках БЭМ-методологии.
6 |
7 | Шаблонизатор содержит два движка:
8 |
9 | 1. **BEMHTML** — для преобразования BEMJSON в HTML.
10 | 2. **BEMTREE** — для преобразования BEMJSON с данными в BEMJSON с БЭМ-деревом для последующего преобразования с помощью BEMHTML.
11 |
12 | В основе шаблонизатора лежат декларативные принципы из [XSLT](https://www.w3.org/TR/xslt) (eXtensible Stylesheet Language Transformations). По аналогии было придумано название XJST — eXtensible JavaScript Transformations.
13 |
14 | Для работы с шаблонизатором вам стоит изучить:
15 |
16 | 1. [Формат входных данных — BEMJSON](4-data.md)
17 | 2. [Как писать шаблоны](5-templates-syntax.md)
18 | 3. [Процессы выбора и применения шаблонов](7-runtime.md)
19 |
20 | ## Отличительные черты
21 |
22 | 1. Шаблоны расширяемы: их можно переопределить или доопределить.
23 | 2. Для написания шаблонов используется [сопоставление с образцом](7-runtime.md#Как-выбираются-и-применяются-шаблоны) (pattern matching) по значениям и структуре входных данных.
24 | 3. Обходит входные данные по умолчанию.
25 | 4. Есть встроенное поведение рендеринга по умолчанию, даже если пользователь не добавил шаблонов.
26 | 5. Написан на JavaScript — можно проводить проверки качества и корректности кода, пользоваться всей инфраструктурой JS.
27 | 6. Не требует компиляции шаблонов.
28 | 7. Предоставляет API для добавления шаблонов в рантайме.
29 | 8. Работает на сервере и клиенте.
30 |
31 | Читать далее: [быстрый старт](2-quick-start.md)
32 |
--------------------------------------------------------------------------------
/migration/lib/transformers/0-obj-to-func-generator.js:
--------------------------------------------------------------------------------
1 | var log = require('../logger');
2 | var get = require('lodash.get');
3 | var Transformer = require('../transformer');
4 | var t = new Transformer();
5 |
6 | module.exports = function(file, api, opts) {
7 | t.description = 'Use function generator instead (Example: `function() { return { … }; }`. ' +
8 | 'See docs: https://github.com/bem/bem-xjst/wiki/Notable-changes-' +
9 | 'between-bem-xjst@1.x-and-bem-xjst@2.x#static-objects-shortcuts-in-mix-content-etc';
10 |
11 | t.find = function(file, j) {
12 | return j(file.source)
13 | .find(j.ObjectExpression)
14 | .filter(function(p) {
15 | var arg = p.value.arguments;
16 | var callee = get(p, 'parentPath.parentPath.value.callee.callee');
17 |
18 | if (! callee)
19 | return false;
20 |
21 | callee = callee.name || (callee.property && callee.property.name);
22 |
23 | return [
24 | 'attrs',
25 | 'addAttrs',
26 | 'js',
27 | 'addJs',
28 | 'mix',
29 | 'addMix',
30 | 'mods',
31 | 'addMods',
32 | 'elemMods',
33 | 'addElemMods'
34 | ].indexOf(callee) !== -1;
35 | });
36 | };
37 |
38 | t.replace = function(ret, j) {
39 | return ret.replaceWith(function(p) {
40 | return j.functionExpression(
41 | j.identifier(''),
42 | [],
43 | j.blockStatement([j.returnStatement(p.value)])
44 | );
45 | });
46 | };
47 |
48 | return t.run(file, api, opts);
49 | };
50 |
--------------------------------------------------------------------------------
/migration/lib/transformers/2-def-must-return-something.js:
--------------------------------------------------------------------------------
1 | var log = require('../logger');
2 | var Transformer = require('../transformer');
3 | var t = new Transformer();
4 | var get = require('lodash.get');
5 |
6 | module.exports = function(file, api, opts) {
7 | t.description = 'def() mode must return something.';
8 |
9 | t.find = function(file, j) {
10 | return j(file.source)
11 | .find(j.CallExpression, { callee: { callee: { property: { name: 'def', type: 'Identifier' } } } })
12 | .find(j.FunctionExpression)
13 | .filter(function(p) {
14 | //if (get(p, 'parentPath.parentPath.value.callee.callee.property.name') === 'def')
15 | //return false;
16 |
17 | var functionBody = p.node.body.body;
18 | var noReturn = function(item) {
19 | return item.type === 'ReturnStatement';
20 | };
21 |
22 | return functionBody.filter(noReturn).length === 0;
23 | });
24 | };
25 |
26 | t.replace = function(ret, j) {
27 | return ret.replaceWith(function(p) {
28 | var body = p.node.body.body;
29 | var len = body.length;
30 | var b = body[len - 1];
31 |
32 | if (b.type === 'ExpressionStatement' &&
33 | get(b, 'expression.callee.name') === 'applyCtx') {
34 | body[len - 1] = (j.returnStatement(b.expression));
35 | } else {
36 | body.push(j.returnStatement(j.callExpression(j.identifier('applyNext'), [])));
37 | }
38 |
39 | return j.functionExpression(p.node.id, [], p.node.body)
40 | });
41 | };
42 |
43 | return t.run(file, api, opts);
44 | };
45 |
--------------------------------------------------------------------------------
/lib/bemxjst/context.js:
--------------------------------------------------------------------------------
1 | var utils = require('./utils');
2 |
3 | function Context(bemxjst) {
4 | this._bemxjst = bemxjst;
5 |
6 | this.ctx = null;
7 | this.block = '';
8 |
9 | // Save current block until the next BEM entity
10 | this._currBlock = '';
11 |
12 | this.elem = null;
13 | this.mods = {};
14 | this.elemMods = {};
15 |
16 | this.position = 0;
17 | this._listLength = 0;
18 | this._notNewList = false;
19 |
20 | this.escapeContent = bemxjst.options.escapeContent !== false;
21 | }
22 | exports.Context = Context;
23 |
24 | Context.prototype._flush = null;
25 |
26 | Context.prototype.isSimple = utils.isSimple;
27 |
28 | Context.prototype.isShortTag = utils.isShortTag;
29 | Context.prototype.extend = utils.extend;
30 | Context.prototype.identify = utils.identify;
31 |
32 | Context.prototype.xmlEscape = utils.xmlEscape;
33 | Context.prototype.attrEscape = utils.attrEscape;
34 | Context.prototype.jsAttrEscape = utils.jsAttrEscape;
35 |
36 | Context.prototype.onError = function(context, e) {
37 | console.error('bem-xjst rendering error:', {
38 | block: context.ctx.block,
39 | elem: context.ctx.elem,
40 | mods: context.ctx.mods,
41 | elemMods: context.ctx.elemMods
42 | }, e);
43 | };
44 |
45 | Context.prototype.isFirst = function() {
46 | return this.position === 1;
47 | };
48 |
49 | Context.prototype.isLast = function() {
50 | return this.position === this._listLength;
51 | };
52 |
53 | Context.prototype.generateId = function() {
54 | return utils.identify(this.ctx);
55 | };
56 |
57 | Context.prototype.reapply = function(ctx) {
58 | return this._bemxjst.run(ctx);
59 | };
60 |
--------------------------------------------------------------------------------
/test/bemjson-elemmods-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 |
4 | describe('BEMJSON elemMods', function() {
5 | it('should support elemMods', function() {
6 | test(function() {},
7 | {
8 | block: 'b',
9 | elem: 'e',
10 | elemMods: { type: 'button' }
11 | },
12 | '');
13 | });
14 |
15 | it('should take elemMods from BEMJSON', function() {
16 | test(function() {
17 | block('b1').elem('e1').content()(function() {
18 | return this.elemMods.a || 'no';
19 | });
20 | }, {
21 | block: 'b1',
22 | content: {
23 | elem: 'e1',
24 | elemMods: { a: 'yes' }
25 | }
26 | }, '');
27 | });
28 |
29 | it('should restore elemMods', function() {
30 | test(function() {
31 | block('b2').elem('e1').content()(function() {
32 | return this.elemMods.a || 'yes';
33 | });
34 | }, {
35 | block: 'b1',
36 | content: {
37 | elem: 'e1',
38 | elemMods: {
39 | a: 'no'
40 | },
41 | content: {
42 | block: 'b2',
43 | elem: 'e1'
44 | }
45 | }
46 |
47 | }, '');
49 | });
50 |
51 | it('should not treat elemMods as mods', function() {
52 | test(function() {}, {
53 | block: 'b1',
54 | elemMods: { m1: 'v1' }
55 | }, '');
56 | });
57 | });
58 |
--------------------------------------------------------------------------------
/test/bemcontext-identify-test.js:
--------------------------------------------------------------------------------
1 | var assert = require('assert');
2 | var bemxjst = require('./fixtures')('bemhtml');
3 |
4 | describe('BEMContext this.identify()', function() {
5 | it('should work without arguments', function() {
6 | var template = bemxjst.compile(function() {
7 | block('b').def()(function() {
8 | return this.identify();
9 | });
10 | });
11 |
12 | // Compare first 10 symbols
13 | // to avoid bounce tests results because of new Date()
14 | assert.equal(template.apply({ block: 'b' }).substr(0, 10),
15 | ('uniq' + (+new Date()) + 1).substr(0, 10));
16 | });
17 |
18 | it('should work with one argument', function() {
19 | var template = bemxjst.compile(function() {
20 | block('b').def()(function() {
21 | var elem = { elem: 'e' };
22 | return [ this.identify(elem), this.identify(elem) ];
23 | });
24 | });
25 |
26 | var res = template.apply({ block: 'b' });
27 |
28 | assert.equal(res[0], res[1]);
29 | });
30 |
31 | it('should work with two arguments', function() {
32 | var template = bemxjst.compile(function() {
33 | block('b').def()(function() {
34 | var elem = { elem: 'e' };
35 | var i1 = this.identify(elem, true);
36 | this.identify(elem); // set private field in elem object
37 | var i2 = this.identify(elem, true);
38 | return [
39 | i1,
40 | i2
41 | ];
42 | });
43 | });
44 |
45 | var res = template.apply({ block: 'b' });
46 |
47 | assert.equal(res[0], undefined);
48 | assert.equal(res[1].substr(0, 11),
49 | ('uniq' + (+new Date()) + 1).substr(0, 11));
50 | });
51 | });
52 |
--------------------------------------------------------------------------------
/migration/lib/transformers/3-apply-call-to-apply.js:
--------------------------------------------------------------------------------
1 | var log = require('../logger');
2 | var Transformer = require('../transformer');
3 |
4 | module.exports = function(file, api, opts) {
5 | Transformer.prototype.init = function() {
6 | this.member = [];
7 | };
8 |
9 | var t = new Transformer();
10 | t.description = 'Since v3.x apply.call(bemjson) must be apply(bemjson)';
11 |
12 | t.find = function(file, j) {
13 | var transformer = this;
14 |
15 | return j(file.source)
16 | .find(j.MemberExpression, {
17 | property: { type: 'Identifier', name: 'call' }
18 | })
19 | .filter(function(p) {
20 | var isApply = function(o) {
21 | return o.type === 'Identifier' && o.name === 'apply'
22 | };
23 | var isMember = function(o) {
24 | return o.type === 'MemberExpression' && isApply(o.property);
25 | }
26 |
27 | var o = p.value.object;
28 |
29 | if (isMember(o)) {
30 | transformer.member.push(o.object.name);
31 | } else if (isApply(o)){
32 | transformer.member.push(false);
33 | }
34 |
35 | return isApply(o) || isMember(o);
36 | });
37 | };
38 |
39 | t.replace = function(ret, j) {
40 | var transformer = this;
41 |
42 | return ret.map(function(item, i) {
43 | return item.replace(transformer.member[i] ?
44 | j.memberExpression(
45 | j.identifier(transformer.member[i]),
46 | j.identifier('apply')
47 | )
48 | : j.identifier('apply'));
49 | });
50 | };
51 |
52 | return t.run(file, api, opts);
53 | };
54 |
--------------------------------------------------------------------------------
/AUTHORS:
--------------------------------------------------------------------------------
1 | 372 miripiruni
2 | 153 Slava Oliyanchuk
3 | 127 Fedor Indutny
4 | 71 Fedor Indutny
5 | 58 Vladimir Grinenko
6 | 7 v-homyakov
7 | 6 greenkeeperio-bot
8 | 6 Vitaly Harisov
9 | 5 sbmaxx
10 | 4 Vitaly Harisov
11 | 4 Vasiliy Loginevskiy
12 | 4 Vasiliy
13 | 4 Sergey Berezhnoy
14 | 4 Alexey Yaroshevich
15 | 3 Vassily Krasnov
16 | 3 Sergey Berezhnoy
17 | 3 Alexandr Shleyko
18 | 2 dustyo-O
19 | 2 blond
20 | 2 Sergei Bocharov
21 | 2 Mikhail Troshev
22 | 2 Alexey Khlebaev
23 | 1 zhdanov
24 | 1 vkz
25 | 1 baymer
26 | 1 Sergey Berezhnoy
27 | 1 Sergey Belov
28 | 1 Ruslan Posevkin
29 | 1 Rozaev Viktor
30 | 1 Inna Belaya
31 | 1 DoctorDee <33066299+DoctorDee@users.noreply.github.com>
32 | 1 Dmitry Starostin
33 | 1 Dima Belitsky
34 | 1 Andrey <30811474+Akvy@users.noreply.github.com>
35 | 1 Alexey Gurianov
36 | 1 Alexander Savin
37 | 1 Alexander Gulnyashkin
38 |
--------------------------------------------------------------------------------
/.jscsrc:
--------------------------------------------------------------------------------
1 | {
2 | "disallowKeywordsOnNewLine": [ "else" ],
3 | "disallowMixedSpacesAndTabs": true,
4 | "disallowMultipleLineStrings": true,
5 | "disallowMultipleVarDecl": true,
6 | "disallowNewlineBeforeBlockStatements": true,
7 | "disallowQuotedKeysInObjects": true,
8 | "disallowSpaceAfterObjectKeys": true,
9 | "disallowSpaceAfterPrefixUnaryOperators": true,
10 | "disallowSpaceBeforePostfixUnaryOperators": true,
11 | "disallowSpacesInCallExpression": true,
12 | "disallowTrailingComma": true,
13 | "disallowTrailingWhitespace": true,
14 | "disallowYodaConditions": true,
15 |
16 | "requireCommaBeforeLineBreak": true,
17 | "requireOperatorBeforeLineBreak": true,
18 | "requireSpaceAfterBinaryOperators": true,
19 | "requireSpaceAfterKeywords": [ "if", "for", "while", "else", "try", "catch" ],
20 | "requireSpaceAfterLineComment": true,
21 | "requireSpaceBeforeBinaryOperators": true,
22 | "requireSpaceBeforeBlockStatements": true,
23 | "requireSpaceBeforeKeywords": [ "else", "catch" ],
24 | "requireSpaceBeforeObjectValues": true,
25 | "requireSpaceBetweenArguments": true,
26 | "requireSpacesInAnonymousFunctionExpression": {
27 | "beforeOpeningCurlyBrace": true
28 | },
29 | "requireSpacesInFunctionDeclaration": {
30 | "beforeOpeningCurlyBrace": true
31 | },
32 | "requireSpacesInFunctionExpression": {
33 | "beforeOpeningCurlyBrace": true
34 | },
35 | "requireSpacesInConditionalExpression": true,
36 | "requireSpacesInForStatement": true,
37 | "requireSpacesInsideArrayBrackets": "all",
38 | "requireSpacesInsideObjectBrackets": "all",
39 | "requireDotNotation": true,
40 |
41 | "maximumLineLength": 80,
42 | "validateIndentation": 2,
43 | "validateLineBreaks": "LF",
44 | "validateParameterSeparator": ", ",
45 | "validateQuoteMarks": "'"
46 | }
47 |
--------------------------------------------------------------------------------
/test/utils-isunquotedattr-test.js:
--------------------------------------------------------------------------------
1 | var utils = require('../lib/bemxjst/utils');
2 |
3 | describe('Utils', function() {
4 |
5 | var attrCheck = function attrCheck(str) {
6 | return !!utils.isUnquotedAttr(str);
7 | };
8 | describe('isUnquotedAttr()', function() {
9 | it('should return true with simple class', function() {
10 | attrCheck('b').should.equal(true);
11 | });
12 |
13 | it('should return false with class with space', function() {
14 | attrCheck('block mixed').should.equal(false);
15 | });
16 |
17 | it('should return true with class with hyphens', function() {
18 | attrCheck('b-page').should.equal(true);
19 | });
20 |
21 | it('should return true with class with uppercase', function() {
22 | attrCheck('bPage').should.equal(true);
23 | });
24 |
25 | it('should return true with class with period', function() {
26 | attrCheck('b.page').should.equal(true);
27 | });
28 |
29 | it('should return true with class with underscores', function() {
30 | attrCheck('page__content').should.equal(true);
31 | });
32 |
33 | it('should return true with class with colons', function() {
34 | attrCheck('test:test').should.equal(true);
35 | });
36 |
37 | it('should return false with double quote', function() {
38 | attrCheck('"test').should.equal(false);
39 | });
40 |
41 | it('should return true with class with digits', function() {
42 | attrCheck('color333').should.equal(true);
43 | });
44 |
45 | it('should return true with class with combination of above', function() {
46 | attrCheck('b-page__content_test_100').should.equal(true);
47 | });
48 |
49 | it('should return false with empty string', function() {
50 | attrCheck('').should.equal(false);
51 | });
52 | });
53 | });
54 |
--------------------------------------------------------------------------------
/test/bemcontext-issimple-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var compile = fixtures.compile;
3 |
4 | describe('BEMContext this.isSimple(arg)', function() {
5 | var bemhtml;
6 |
7 | before(function() {
8 | bemhtml = compile(function() {
9 | block('b').def()(function() {
10 | return this.isSimple(this.ctx.val);
11 | });
12 | });
13 | });
14 |
15 | it('should return true for undefined', function() {
16 | bemhtml.apply({ block: 'b', val: undefined }).should.equal(true);
17 | });
18 |
19 | it('should return true for null', function() {
20 | bemhtml.apply({ block: 'b', val: null }).should.equal(true);
21 | });
22 |
23 | it('should return true for Number', function() {
24 | bemhtml.apply({ block: 'b', val: 0 }).should.equal(true);
25 | });
26 |
27 | it('should return false for NaN', function() {
28 | bemhtml.apply({ block: 'b', val: NaN }).should.equal(true);
29 | });
30 |
31 | it('should return true for String', function() {
32 | bemhtml.apply({ block: 'b', val: '' }).should.equal(true);
33 | });
34 |
35 | it('should return true for escaped String', function() {
36 | bemhtml.apply({ block: 'b', val: { html: '' } }).should.equal(true);
37 | });
38 |
39 | it('should return true for Boolean', function() {
40 | bemhtml.apply({ block: 'b', val: false }).should.equal(true);
41 | });
42 |
43 | it('should return false for Array', function() {
44 | bemhtml.apply({ block: 'b', val: [] }).should.equal(false);
45 | });
46 |
47 | it('should return false for Object', function() {
48 | bemhtml.apply({ block: 'b', val: {} }).should.equal(false);
49 | });
50 |
51 | it('should return false for Function', function() {
52 | bemhtml.apply({ block: 'b', val: function() {} }).should.equal(false);
53 | });
54 | });
55 |
--------------------------------------------------------------------------------
/test/modes-tag-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 | var assert = require('assert');
4 |
5 | describe('Modes tag', function() {
6 | it('should throw error when args passed to tag mode', function() {
7 | assert.throws(function() {
8 | fixtures.compile(function() {
9 | block('b1').tag('span');
10 | });
11 | });
12 | });
13 |
14 | it('should set empty tag', function() {
15 | test(function() {
16 | block('link').tag()('');
17 | block('button').tag()(false);
18 | },
19 | {
20 | block: 'button',
21 | content: {
22 | block: 'link',
23 | content: 'link'
24 | }
25 | },
26 | 'link');
27 | });
28 |
29 | it('should set html tag', function() {
30 | test(function() {
31 | block('button').tag()('button');
32 | },
33 | { block: 'button' },
34 | '');
35 | });
36 |
37 | it('should override user tag', function() {
38 | test(function() {
39 | block('button').tag()('button');
40 | },
41 | { block: 'button', tag: 'a' },
42 | '');
43 | });
44 |
45 | it('user can choose between tag in bemjson ' +
46 | 'and custom value in templates', function() {
47 | test(function() {
48 | block('b').tag()(function() {
49 | return this.ctx.tag || 'strong';
50 | });
51 | },
52 | [ { block: 'b', tag: 'em' }, { block: 'b' } ],
53 | '');
54 | });
55 |
56 | it('should not override later declarations', function() {
57 | test(function() {
58 | block('button').tag()('input');
59 | block('button').tag()('button');
60 | },
61 | { block: 'button' },
62 | '');
63 | });
64 | });
65 |
--------------------------------------------------------------------------------
/test/modes-prependcontent-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 |
4 | describe('Modes prependContent', function() {
5 | it('should support prependContent', function() {
6 | test(function() {
7 | block('b').prependContent()(function() {
8 | return 'before';
9 | });
10 | },
11 | { block: 'b', content: { block: 'test' } },
12 | '');
13 | });
14 |
15 | it('should support prependContent with literal', function() {
16 | test(function() {
17 | block('b').prependContent()('before ');
18 | },
19 | { block: 'b', content: 'text' },
20 | 'before text
');
21 | });
22 |
23 | it('should accumulate result', function() {
24 | test(function() {
25 | block('b')(
26 | prependContent()('2'),
27 | prependContent()('4')
28 | );
29 | },
30 | { block: 'b', content: ' is the answer' },
31 | '42 is the answer
');
32 | });
33 |
34 | it('should prepend things to content', function() {
35 | test(function() {
36 | block('b').content()('2');
37 | block('b').prependContent()('4');
38 | },
39 | { block: 'b', content: 'test' },
40 | '42
');
41 | });
42 |
43 | it('should prepend non simple values to content', function() {
44 | test(function() {
45 | block('foo').prependContent()({ elem: 'test' });
46 | },
47 | { block: 'foo' },
48 | '');
49 | });
50 |
51 | it('should prepend function to content', function() {
52 | test(function() {
53 | block('foo').prependContent()(function() { return { elem: 'test' }; });
54 | },
55 | { block: 'foo' },
56 | '');
57 | });
58 | });
59 |
--------------------------------------------------------------------------------
/test/bemjson-values-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 |
4 | describe('BEMJSON values', function() {
5 | it('should work with empty input', function() {
6 | test(function() {
7 | }, '', '');
8 | });
9 |
10 | it('should work with false input', function() {
11 | test(function() {
12 | }, false, '');
13 | });
14 |
15 | it('should work with null input', function() {
16 | test(function() {
17 | }, null, '');
18 | });
19 |
20 | it('should work with 0 input', function() {
21 | test(function() {
22 | }, 0, '0');
23 | });
24 |
25 | it('should not render `undefined`', function () {
26 | test(function() {
27 | }, [
28 | undefined,
29 | undefined,
30 | { block: 'b1' },
31 | undefined
32 | ], '');
33 | });
34 |
35 | it('should properly save context while render plain html items', function() {
36 | test(function() {
37 | }, {
38 | block: 'aaa',
39 | content: [
40 | {
41 | elem: 'xxx1',
42 | content: {
43 | block: 'bbb',
44 | elem: 'yyy1',
45 | content: { tag: 'h1', content: 'h 1' }
46 | }
47 | },
48 | {
49 | elem: 'xxx2'
50 | }
51 | ]
52 | }, '' +
53 | '
' +
54 | '
' +
55 | '
h 1
' +
56 | '' +
57 | '
' +
58 | '
' +
59 | '
');
60 | });
61 |
62 | it('should return undefined on failed match', function() {
63 | test(function() {
64 | block('b1').content()(function() {
65 | return { elem: 'e1' };
66 | });
67 |
68 | block('b1').elem('e1').mod('a', 'b').tag()('span');
69 | }, { block: 'b1' }, '');
70 | });
71 | });
72 |
--------------------------------------------------------------------------------
/test/modes-addmods-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 |
4 | describe('Modes addMods', function() {
5 | it('should support addMods', function() {
6 | test(function() {
7 | block('button')(
8 | addMods()(function() {
9 | return { size: 'l' };
10 | })
11 | );
12 | },
13 | { block: 'button' },
14 | '');
15 | });
16 |
17 | it('should support addMods with object literal', function() {
18 | test(function() {
19 | block('button').addMods()({ size: 'l' });
20 | },
21 | { block: 'button' },
22 | '');
23 | });
24 |
25 | it('should rewrite mods from bemjson if no other mods templates',
26 | function() {
27 | test(function() {
28 | block('button').addMods()(function() {
29 | return { size: 'l' };
30 | });
31 | },
32 | { block: 'button', mods: { size: 's' } },
33 | '');
34 | });
35 |
36 | it('should apply templates for mods after mods changes', function() {
37 | test(function() {
38 | block('a')(
39 | mod('withTag', 'span').tag()('span'),
40 | mod('withMix', 'test').mix()('test'),
41 | addMods()({ withTag: 'span' }),
42 | addMods()({ withMix: 'test' })
43 | );
44 | },
45 | { block: 'a' },
46 | '');
47 | });
48 |
49 | it('should accumulate result', function() {
50 | test(function() {
51 | block('button')(
52 | addMods()(function() {
53 | return { theme: 'dark' };
54 | }),
55 | addMods()(function() {
56 | return { size: 'xl' };
57 | })
58 | );
59 | },
60 | { block: 'button' },
61 | '');
62 | });
63 | });
64 |
--------------------------------------------------------------------------------
/lib/bemhtml/entity.js:
--------------------------------------------------------------------------------
1 | var inherits = require('inherits');
2 | var Match = require('../bemxjst/match').Match;
3 | var BemxjstEntity = require('../bemxjst/entity').Entity;
4 |
5 | /**
6 | * @class Entity
7 | * @param {BEMXJST} bemxjst
8 | * @param {String} block
9 | * @param {String} elem
10 | * @param {Array} templates
11 | */
12 | function Entity(bemxjst) {
13 | this.bemxjst = bemxjst;
14 |
15 | this.jsClass = null;
16 |
17 | // "Fast modes" about HTML
18 | this.tag = new Match(this, 'tag');
19 | this.attrs = new Match(this, 'attrs');
20 | this.bem = new Match(this, 'bem');
21 | this.cls = new Match(this, 'cls');
22 |
23 | BemxjstEntity.apply(this, arguments);
24 | }
25 |
26 | inherits(Entity, BemxjstEntity);
27 | exports.Entity = Entity;
28 |
29 | Entity.prototype.init = function(block, elem) {
30 | this.block = block;
31 | this.elem = elem;
32 |
33 | // Class for jsParams
34 | this.jsClass = this.bemxjst.classBuilder.build(this.block, this.elem);
35 | };
36 |
37 | Entity.prototype._keys = {
38 | tag: 1,
39 | content: 1,
40 | attrs: 1,
41 | mix: 1,
42 | js: 1,
43 | mods: 1,
44 | elemMods: 1,
45 | cls: 1,
46 | bem: 1
47 | };
48 |
49 | Entity.prototype.defaultBody = function(context) {
50 | context.mods = this.mods.exec(context);
51 | if (context.ctx.elem) context.elemMods = this.elemMods.exec(context);
52 |
53 | return this.bemxjst.render(context,
54 | this,
55 | this.tag.exec(context),
56 | this.js.exec(context),
57 | this.bem.exec(context),
58 | this.cls.exec(context),
59 | this.mix.exec(context),
60 | this.attrs.exec(context),
61 | this.content.exec(context),
62 | context.mods,
63 | context.elemMods);
64 | };
65 |
--------------------------------------------------------------------------------
/test/modes-block-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 |
4 | describe('Modes block(blockName)', function() {
5 | it('should support block() match', function () {
6 | test(function() {
7 | block('b').content()('ok');
8 | },
9 | { block: 'b' },
10 | 'ok
');
11 | });
12 |
13 | it('should match block(*) to empty BEMJSON-object', function() {
14 | test(function() {
15 | block('*').def()(function() {
16 | return 'lol';
17 | });
18 | },
19 | {},
20 | 'lol');
21 | });
22 |
23 | it('should apply block(*) template', function() {
24 | test(function() {
25 | block('*').tag()('b');
26 | },
27 | [
28 | { content: 'foo' },
29 | { block: 'b' },
30 | { block: 'input', elem: 'control' }
31 | ],
32 | 'foo');
33 | });
34 |
35 | it('block(*) should be called before the matched templates', function() {
36 | test(function() {
37 | block('b1').content()('ok');
38 | block('b2').content()('yes');
39 | block('*').content()(function() {
40 | return '#' + applyNext() + '#';
41 | });
42 | }, [ { block: 'b1' },
43 | { block: 'b2' },
44 | { block: 'b3', content: 'ya' } ],
45 | '#ok#
' +
46 | '#yes#
' +
47 | '#ya#
');
48 | });
49 |
50 | it('should apply several block(*) templates in proper order', function() {
51 | test(function() {
52 | block('*').cls()(function() {
53 | return this.ctx.cls;
54 | });
55 | block('*').cls()(function() {
56 | return applyNext() + '1';
57 | });
58 | block('*').cls()(function() {
59 | return applyNext() + '2';
60 | });
61 | },
62 | { block: 'button', cls: 'foo' },
63 | '');
64 | });
65 | });
66 |
--------------------------------------------------------------------------------
/test/bemjson-block-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 |
4 | describe('BEMJSON block', function() {
5 | it('should render block by default as div', function () {
6 | test(function() {}, [
7 | { block: 'b' }
8 | ], '');
9 | });
10 |
11 | it('should not preserve block on tag', function () {
12 | test(function() {
13 | }, [
14 | {
15 | block: 'b1',
16 | content: {
17 | tag: 'span',
18 | content: {
19 | block: 'b2'
20 | }
21 | }
22 | }
23 | ], '');
24 | });
25 |
26 | it('should inherit block from the parent, and reset it back', function() {
27 | test(function() {
28 | }, {
29 | block: 'b2',
30 | content: [
31 | { block: 'b1', content: { elem: 'e1' } },
32 | { elem: 'e1' }
33 | ]
34 | }, '');
36 | });
37 |
38 | it('should preserve block on next BEM entity', function() {
39 | test(function() {
40 | }, [
41 | {
42 | block: 'b1',
43 | content: {
44 | tag: 'span',
45 | content: {
46 | elem: 'e1'
47 | }
48 | }
49 | }
50 | ], '');
51 | });
52 |
53 | it('should not preserve block/elem on tag', function() {
54 | test(function() {
55 | }, [
56 | {
57 | block: 'b1',
58 | content: {
59 | elem: 'e1',
60 | content: {
61 | tag: 'span',
62 | content: {
63 | block: 'b2'
64 | }
65 | }
66 | }
67 | }
68 | ], '');
70 | });
71 | });
72 |
--------------------------------------------------------------------------------
/test/modes-attrs-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 | var assert = require('assert');
4 | var bemxjst = require('../').bemhtml;
5 |
6 | describe('Modes attrs', function() {
7 | it('should throw error when args passed to attrs mode', function() {
8 | assert.throws(function() {
9 | bemxjst.compile(function() {
10 | block('b1').attrs('blah');
11 | });
12 | });
13 | });
14 |
15 | it('should set attrs', function() {
16 | test(function() {
17 | block('checkbox').attrs()({
18 | name: undefined,
19 | type: 'button',
20 | disabled: false,
21 | hidden: true,
22 | value: null
23 | });
24 | },
25 | { block: 'checkbox' },
26 | '');
27 | });
28 |
29 | it('should override bemjson attrs', function() {
30 | test(function() {
31 | block('button').attrs()({
32 | type: 'button',
33 | disabled: true
34 | });
35 | },
36 | {
37 | block: 'button',
38 | attrs: {
39 | type: 'link',
40 | disabled: undefined,
41 | name: 'button'
42 | }
43 | },
44 | '');
45 | });
46 |
47 | it('should not modify state of bemxjst if attrs non simple', function() {
48 | test(function() {
49 | block('*')
50 | .match(function() { return this.block; })
51 | .def()(function(n) {
52 | return n.block === 'ERROR' ? ('Good: ' + n.block) : applyNext();
53 | });
54 |
55 | block('image').attrs()({ alt: [ 'Река Ока' ] });
56 | },
57 | [
58 | {
59 | block: 'a',
60 | content: { block: 'image' }
61 | },
62 | {
63 | block: 'b',
64 | content: { block: 'error' }
65 | }
66 | ],
67 | '' +
68 | '');
69 | });
70 | });
71 |
--------------------------------------------------------------------------------
/test/modes-addmix-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 |
4 | describe('Modes addMix', function() {
5 | it('should support addMix', function() {
6 | test(function() {
7 | block('button').addMix()(function() {
8 | return 'tmpls';
9 | });
10 | },
11 | { block: 'button', mix: { block: 'bemjson' } },
12 | '');
13 | });
14 |
15 | it('should support addMix with literal', function() {
16 | test(function() {
17 | block('button').addMix()('tmpls');
18 | },
19 | { block: 'button', mix: { block: 'bemjson' } },
20 | '');
21 | });
22 |
23 | it('should render mix with just mods', function() {
24 | test(function() {
25 | block('b').addMix()(function() {
26 | return { mods: { type: 'test' } };
27 | });
28 | },
29 | { block: 'b' },
30 | '');
31 | });
32 |
33 | it('should accumulate result', function() {
34 | test(function() {
35 | block('button')(
36 | addMix()(function() {
37 | return 'tmpls';
38 | }),
39 | addMix()(function() {
40 | return 'tmpls1';
41 | })
42 | );
43 | },
44 | { block: 'button', mix: { block: 'bemjson' } },
45 | '');
46 | });
47 |
48 | it('should concat with mix from BEMJSON', function() {
49 | test(function() {
50 | block('button').addMix()({ block: 'templ' });
51 | },
52 | { block: 'button', mix: { block: 'bemjson' } },
53 | '');
54 | });
55 |
56 | it('should extend mix', function() {
57 | test(function() {
58 | block('button').mix()({ block: 'templ_1' });
59 | block('button').addMix()({ block: 'templ_2' });
60 | },
61 | { block: 'button', mix: { block: 'bemjson' } },
62 | '');
63 | });
64 | });
65 |
--------------------------------------------------------------------------------
/bench/lib/compare.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | import sys
4 | import json
5 | import codecs
6 | import os
7 | import collections
8 | import subprocess
9 |
10 |
11 | DATAFILE_NAME = 'data.dat'
12 | PLOTFILE_NAME = 'plot'
13 |
14 |
15 | def get_hist(filename):
16 | with open(filename) as uni_data:
17 | return dict(json.load(uni_data));
18 |
19 |
20 | def get_name(filename):
21 | return os.path.splitext(filename)[0]
22 |
23 |
24 | def get_sorted_buckets(storage):
25 | buckets = set()
26 |
27 | for value in storage.itervalues():
28 | buckets.update(value.keys())
29 |
30 | return sorted(buckets)
31 |
32 |
33 | storage = {}
34 |
35 | for jsonfile in sys.argv[2:]:
36 | storage[get_name(jsonfile)] = get_hist(jsonfile)
37 |
38 | shootings_names = sorted(storage.keys())
39 | buckets = get_sorted_buckets(storage)
40 | normalized_data = collections.OrderedDict()
41 |
42 | for bucket in buckets:
43 | requests = []
44 | for name in shootings_names:
45 | requests.append(storage[name].get(bucket, 0))
46 | normalized_data[bucket] = requests
47 |
48 | datafile = codecs.open(DATAFILE_NAME, 'w', 'utf-8')
49 | plotfile = codecs.open(PLOTFILE_NAME, 'w', 'utf-8')
50 |
51 | for bucket, requests in normalized_data.iteritems():
52 | datafile.write('%d %s\n' % (bucket, ' '.join(str(x) for x in requests)))
53 |
54 | plotfile.write("""set terminal svg size 1024, 768
55 | set ylabel 'requests'
56 | set xlabel 'ms'
57 | plot """)
58 |
59 | for i, name in enumerate(shootings_names):
60 | plotfile.write("'{datafile}' using 1:{pos} with lines title '{name}'{delim}".format(
61 | datafile=DATAFILE_NAME,
62 | pos=i + 2,
63 | name=name,
64 | delim=(', ', '\n')[i == len(shootings_names) - 1]
65 | ))
66 |
67 | subprocess.call('gnuplot {name} > {dir}/out.svg'.format(
68 | name=PLOTFILE_NAME,
69 | dir=sys.argv[1]
70 | ) , shell=True)
71 |
72 | # os.remove(DATAFILE_NAME)
73 | # os.remove(PLOTFILE_NAME)
74 |
75 |
--------------------------------------------------------------------------------
/test/fixtures.js:
--------------------------------------------------------------------------------
1 | var assert = require('assert');
2 |
3 | require('chai').should();
4 |
5 | module.exports = function(engine) {
6 | function compile(fn, options) {
7 | if (typeof fn !== 'function') {
8 | options = fn;
9 | fn = function() {};
10 | }
11 |
12 | if (!options) options = {};
13 |
14 | var engineName = options.engine || 'BEMHTML';
15 | var Engine = require('../lib/' + engineName.toLowerCase());
16 | var api = new Engine(options);
17 | var template = {};
18 |
19 | api.compile(fn);
20 | api.exportApply(template);
21 |
22 | return template;
23 | }
24 |
25 | function fail(fn, regexp) {
26 | assert.throws(function() {
27 | compile(fn, { engine: engine });
28 | }, regexp);
29 | }
30 |
31 | /**
32 | * test helper
33 | *
34 | * @param {?Function} [fn] - matchers
35 | * @param {Object} data - incoming bemjson
36 | * @param {String} expected - expected resulting html
37 | * @param {?Object} [options] - compiler options
38 | */
39 | function test(fn, data, expected, options) {
40 | if (typeof fn !== 'function') {
41 | options = expected;
42 | expected = data;
43 | data = fn;
44 | fn = function() {};
45 | }
46 | if (!options) options = {};
47 |
48 | var template = compile(fn, options);
49 |
50 | if (options.flush) {
51 | template._buf = [];
52 | template.BEMContext.prototype._flush = function flush(str) {
53 | if (str !== '')
54 | template._buf.push(str);
55 | return '';
56 | };
57 | }
58 |
59 | // Invoke multiple times
60 | var count = options.count || 1;
61 | for (var i = 0; i < count; i++) {
62 | try {
63 | assert.deepEqual(template.apply(data), expected, i);
64 | } catch (e) {
65 | console.error(e.stack);
66 | throw e;
67 | }
68 | }
69 |
70 | if (options.after)
71 | options.after(template);
72 | }
73 |
74 | return {
75 | test: test,
76 | fail: fail,
77 | compile: compile
78 | };
79 | };
80 |
--------------------------------------------------------------------------------
/test/modes-appendcontent-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 |
4 | describe('Modes appendContent', function() {
5 | it('should support appendContent', function() {
6 | test(function() {
7 | block('button').appendContent()(function() {
8 | return 'some text';
9 | });
10 | },
11 | { block: 'button', content: { block: 'test' } },
12 | '');
13 | });
14 |
15 | it('should support appendContent with literal', function() {
16 | test(function() {
17 | block('button').appendContent()('more text');
18 | },
19 | { block: 'button', content: 'text' },
20 | 'textmore text
');
21 | });
22 |
23 | it('should accumulate result', function() {
24 | test(function() {
25 | block('button')(
26 | appendContent()('tmpls_1'),
27 | appendContent()('tmpls_2')
28 | );
29 | },
30 | { block: 'button', content: { block: 'test' } },
31 | '');
32 | });
33 |
34 | it('should append things to content', function() {
35 | test(function() {
36 | block('button').content()({ block: 'tmpl_1' });
37 | block('button').appendContent()('tmpl_2');
38 | block('tmpl_1').tag()('span');
39 | },
40 | { block: 'button', content: 'test' },
41 | 'tmpl_2
');
42 | });
43 |
44 | it('should append non simple values to content', function() {
45 | test(function() {
46 | block('foo').appendContent()({ elem: 'test' });
47 | },
48 | { block: 'foo' },
49 | '');
50 | });
51 |
52 | it('should append function to content', function() {
53 | test(function() {
54 | block('foo').appendContent()(function() { return { elem: 'test' }; });
55 | },
56 | { block: 'foo' },
57 | '');
58 | });
59 | });
60 |
--------------------------------------------------------------------------------
/test/modes-content-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 | var assert = require('assert');
4 | var bemxjst = require('../').bemhtml;
5 |
6 | describe('Modes content', function() {
7 | it('should throw error when args passed to content mode', function() {
8 | assert.throws(function() {
9 | bemxjst.compile(function() {
10 | block('b1').content('blah');
11 | });
12 | });
13 | });
14 |
15 | it('should set bemjson content', function() {
16 | test(function() {
17 | block('button').content()({ elem: 'text' });
18 | },
19 | { block: 'button' },
20 | '');
21 | });
22 |
23 | it('should set bemjson array content', function() {
24 | test(function() {
25 | block('button').content()([ { elem: 'text1' }, { elem: 'text2' } ]);
26 | },
27 | { block: 'button' },
28 | '');
31 | });
32 |
33 | it('should set bemjson string content', function() {
34 | test(function() {
35 | block('button').content()('Hello World');
36 | },
37 | { block: 'button' },
38 | 'Hello World
');
39 | });
40 |
41 | it('should set bemjson numeric content', function() {
42 | test(function() {
43 | block('button').content()(123);
44 | },
45 | { block: 'button' },
46 | '123
');
47 | });
48 |
49 | it('should set bemjson zero-numeric content', function() {
50 | test(function() {
51 | block('button').content()(0);
52 | },
53 | { block: 'button' },
54 | '0
');
55 | });
56 |
57 | it('should not override later declarations', function() {
58 | test(function() {
59 | block('button').content()({ elem: 'text2' });
60 | block('button').content()({ elem: 'text1' });
61 | },
62 | { block: 'button' },
63 | '');
64 | });
65 | });
66 |
--------------------------------------------------------------------------------
/test/modes-addelemmods-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 |
4 | describe('Modes addElemMods', function() {
5 | it('should support addElemMods', function() {
6 | test(function() {
7 | block('button').elem('e')(
8 | addElemMods()(function() {
9 | return { size: 'l' };
10 | })
11 | );
12 | },
13 | { block: 'button', elem: 'e' },
14 | '');
15 | });
16 |
17 | it('should support addElemMods with object literal', function() {
18 | test(function() {
19 | block('button').elem('e').addElemMods()({ size: 'l' });
20 | },
21 | { block: 'button', elem: 'e' },
22 | '');
23 | });
24 |
25 | it('should rewrite mods from bemjson if no other mods templates',
26 | function() {
27 | test(function() {
28 | block('button').elem('e').addElemMods()(function() {
29 | return { size: 'l' };
30 | });
31 | },
32 | { block: 'button', elem: 'e', mods: { size: 's' } },
33 | '');
34 | });
35 |
36 | it('should apply templates for mods after mods changes', function() {
37 | test(function() {
38 | block('a').elem('e')(
39 | elemMod('withTag', 'span').tag()('span'),
40 | elemMod('withMix', 'test').mix()('test'),
41 | addElemMods()({ withTag: 'span' }),
42 | addElemMods()({ withMix: 'test' })
43 | );
44 | },
45 | { block: 'a', elem: 'e' },
46 | '');
47 | });
48 |
49 | it('should accumulate result', function() {
50 | test(function() {
51 | block('button').elem('e')(
52 | addElemMods()(function() {
53 | return { theme: 'dark' };
54 | }),
55 | addElemMods()(function() {
56 | return { size: 'xl' };
57 | })
58 | );
59 | },
60 | { block: 'button', elem: 'e' },
61 | '');
62 | });
63 | });
64 |
--------------------------------------------------------------------------------
/test/modes-wrap-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 | var assert = require('assert');
4 |
5 | describe('Modes wrap', function() {
6 | it('should throw error when args passed to wrap mode', function() {
7 | assert.throws(function() {
8 | fixtures.compile(function() {
9 | block('b1').wrap('blah');
10 | });
11 | });
12 | });
13 |
14 | it('should support `.wrap()`', function() {
15 | test(function() {
16 | block('b1').wrap()(function() {
17 | return {
18 | block: 'wrap',
19 | content: this.ctx
20 | };
21 | });
22 | }, [ {
23 | block: 'b1',
24 | tag: 'a',
25 | content: {
26 | block: 'b1',
27 | tag: 'a'
28 | }
29 | } ], '');
31 | });
32 |
33 | it('should support predicates after `wrap()`', function() {
34 | test(function() {
35 | block('b1')(
36 | wrap().match(function() {
37 | return this.ctx.key;
38 | })(function() {
39 | return {
40 | block: 'wrap',
41 | content: this.ctx
42 | };
43 | })
44 | );
45 | }, [ {
46 | block: 'b1',
47 | tag: 'a',
48 | key: 'val'
49 | } ], '');
50 | });
51 |
52 | it('should protected from infinite loop', function() {
53 | test(function() {
54 | block('b1').wrap()(function() {
55 | return { block: 'b2' };
56 | });
57 | block('b2').wrap()({ block: 'b1' });
58 | }, { block: 'b1' }, '');
59 | });
60 |
61 |
62 | it('should use current context (with simple value)', function() {
63 | test(function() {
64 | block('page').wrap()([ { elem: 'head' } ]);
65 | }, { block: 'page' }, '');
66 | });
67 |
68 | it('should use current context (with function)', function() {
69 | test(function() {
70 | block('page').wrap()(function() { return [ { elem: 'head' } ]; });
71 | }, { block: 'page' }, '');
72 | });
73 | });
74 |
--------------------------------------------------------------------------------
/test/modes-elem-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 |
4 | describe('Modes elem(elemName)', function() {
5 | it('should support elem() match', function () {
6 | test(function() {
7 | block('b').elem('e').content()('ok');
8 | },
9 | { block: 'b', elem: 'e' },
10 | 'ok
');
11 | });
12 |
13 | it('should apply elem(*) template', function() {
14 | test(function() {
15 | block('b').elem('*').tag()('b');
16 | },
17 | [
18 | { block: 'b' },
19 | { block: 'b', elem: 'foo' },
20 | {
21 | block: 'b',
22 | elem: 'bar',
23 | content: {
24 | block: 'b',
25 | elem: 'inner',
26 | content: 'test'
27 | }
28 | }
29 | ],
30 | '' +
31 | 'test');
32 | });
33 |
34 | it('elem(*) should be called before the matched templates',
35 | function() {
36 | test(function() {
37 | block('b1').content()(function() {
38 | return 'block';
39 | });
40 | block('b1').elem('a').content()(function() {
41 | return 'block-a';
42 | });
43 | block('b1').elem('*').content()(function() {
44 | return '%' + applyNext() + '%';
45 | });
46 | }, [
47 | { block: 'b1' },
48 | {
49 | block: 'b1',
50 | elem: 'a'
51 | },
52 | {
53 | block: 'b3',
54 | elem: 'b',
55 | content: 'ok'
56 | }
57 | ], 'block
%block-a%
' +
58 | '%ok%
');
59 | });
60 |
61 | it('should apply several elem(*) templates in proper order', function() {
62 | test(function() {
63 | block('b').elem('*').cls()(function() {
64 | return this.ctx.cls;
65 | });
66 | block('b').elem('*').cls()(function() {
67 | return applyNext() + '1';
68 | });
69 | block('b').elem('*').cls()(function() {
70 | return applyNext() + '2';
71 | });
72 | },
73 | { block: 'b', elem: 'e', cls: 'foo' },
74 | '');
75 | });
76 | });
77 |
--------------------------------------------------------------------------------
/test/modes-match-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 | var fail = fixtures.fail;
4 |
5 | describe('Modes match', function() {
6 | it('should support custom matches', function () {
7 | test(function() {
8 | block('b1').content()('!');
9 | block('b1').match(function() { return this.ctx.test2; }).content()('ok');
10 | block('b1').match(function() { return this.ctx.test1; }).content()('!');
11 | }, { block: 'b1', test2: true }, 'ok
');
12 | });
13 |
14 | it('should verify match() argument', function() {
15 | fail(function() {
16 | block('b1').match('123')('123');
17 | }, /Wrong.*match.*argument/);
18 | });
19 |
20 | it('should’t work without match() argument', function() {
21 | fail(function() {
22 | block('b1').match()('123');
23 | }, /.match.*must.*have.*argument/);
24 | });
25 |
26 | it('should execute matches in right order', function() {
27 | test(function() {
28 | block('bla')(
29 | tag()('span'),
30 | // this.ctx.d is undefined
31 | match(function() { return this.ctx.d; })(
32 | tag()('a'),
33 | attrs()(match(function() { return this.ctx.d.a; })(function() {
34 | // this will throw error
35 | return { f: 1 };
36 | }))
37 | )
38 | );
39 | }, {
40 | block: 'bla'
41 | }, '');
42 | });
43 |
44 | it('should work with apply() inside match()',
45 | function() {
46 | test(function() {
47 | block('b')(
48 | mode('test')(function() { return true; }),
49 |
50 | match(function() { return apply('test'); })
51 | .content()(function() { return 'OK'; })
52 | );
53 | }, { block: 'b' }, 'OK
');
54 | });
55 |
56 | it('should work with apply() with changes inside match()',
57 | function() {
58 | test(function() {
59 | block('b')(
60 | mode('test')(function() { return this.changes; }),
61 |
62 | match(function() { return apply('test', { changes: true }); })
63 | .content()(function() { return 'OK'; })
64 | );
65 | }, { block: 'b' }, 'OK
');
66 | });
67 | });
68 |
--------------------------------------------------------------------------------
/lib/bemtree/index.js:
--------------------------------------------------------------------------------
1 | var inherits = require('inherits');
2 | var BEMXJST = require('../bemxjst');
3 | var Entity = require('./entity').Entity;
4 | var utils = require('../bemxjst/utils');
5 |
6 | function BEMTREE() {
7 | BEMXJST.apply(this, arguments);
8 | }
9 |
10 | inherits(BEMTREE, BEMXJST);
11 | module.exports = BEMTREE;
12 |
13 | BEMTREE.prototype.Entity = Entity;
14 |
15 | BEMTREE.prototype.runMany = function(arr) {
16 | var out = [];
17 | var context = this.context;
18 | var prevPos = context.position;
19 | var prevNotNewList = context._notNewList;
20 |
21 | if (prevNotNewList) {
22 | context._listLength += arr.length - 1;
23 | } else {
24 | context.position = 0;
25 | context._listLength = arr.length;
26 | }
27 | context._notNewList = true;
28 |
29 | if (this.canFlush) {
30 | for (var i = 0; i < arr.length; i++)
31 | out += context._flush(this._run(arr[i])); // TODO: fixme!
32 | } else {
33 | for (var i = 0; i < arr.length; i++)
34 | out.push(this._run(arr[i]));
35 | }
36 |
37 | if (!prevNotNewList)
38 | context.position = prevPos;
39 |
40 | return out;
41 | };
42 |
43 | BEMTREE.prototype.render = function(context, entity, content, js, mix, mods,
44 | elemMods) {
45 | var ctx = utils.extend({}, context.ctx);
46 | var isBEM = !!(ctx.block || ctx.elem || ctx.bem);
47 |
48 | if (typeof js !== 'undefined')
49 | ctx.js = js;
50 |
51 | if (typeof mix !== 'undefined')
52 | ctx.mix = mix;
53 |
54 | if (!entity.elem && mods && Object.keys(mods).length > 0)
55 | ctx.mods = utils.extend(ctx.mods || {}, mods);
56 |
57 | if (entity.elem && elemMods && Object.keys(elemMods).length > 0)
58 | ctx.elemMods = utils.extend(ctx.elemMods || {}, elemMods);
59 |
60 | if (typeof content === 'undefined')
61 | return ctx;
62 |
63 | ctx.content = this.renderContent(content, isBEM);
64 |
65 | return ctx;
66 | };
67 |
68 | BEMTREE.prototype._run = function(context) {
69 | if (!context || context === true) return context;
70 | return BEMXJST.prototype._run.call(this, context);
71 | };
72 |
73 | BEMTREE.prototype.runUnescaped = function(context) {
74 | this.context._listLength--;
75 | return context;
76 | };
77 |
--------------------------------------------------------------------------------
/test/modes-js-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 | var assert = require('assert');
4 |
5 | describe('Modes js', function() {
6 | it('should throw error when args passed to js mode', function() {
7 | assert.throws(function() {
8 | fixtures.compile(function() {
9 | block('b1').js('blah');
10 | });
11 | });
12 | });
13 |
14 | it('should set js', function() {
15 | test(function() {
16 | block('button').js()(true);
17 | },
18 | { block: 'button' },
19 | '');
20 | });
21 |
22 | it('should not set js', function() {
23 | test(function() {
24 | block('button').js()(false);
25 | },
26 | { block: 'button' },
27 | '');
28 | });
29 |
30 | it('should override user declarations', function() {
31 | test(function() {
32 | block('button').js()(true);
33 | },
34 | { block: 'button', js: false },
35 | '');
36 | });
37 |
38 | it('should not override later declarations #1', function() {
39 | test(function() {
40 | block('button').js()(false);
41 | block('button').js()(true);
42 | },
43 | { block: 'button' },
44 | '');
45 | });
46 |
47 | it('should not override later declarations #2', function() {
48 | test(function() {
49 | block('button').js()(true);
50 | block('button').js()(false);
51 | },
52 | { block: 'button' },
53 | '');
54 | });
55 |
56 | it('should render i-bem for elems with elemJsInstances option', function() {
57 | test(function() {
58 | block('b').elem('e').js()(true);
59 | },
60 | { block: 'b', elem: 'e' },
61 | '',
62 | { elemJsInstances: true });
63 | });
64 |
65 | it('should override js from bemjson via js from templates', function() {
66 | test(function() {
67 | block('b').js()({ templ: '1' });
68 | },
69 | { block: 'b', js: { bemjson: '2' } },
70 | '');
71 | });
72 | });
73 |
--------------------------------------------------------------------------------
/test/modes-addattrs-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 |
4 | describe('Modes addAttrs', function() {
5 | it('should support addAttrs', function() {
6 | test(function() {
7 | block('button')(
8 | addAttrs()(function() {
9 | return { id: 'test' };
10 | })
11 | );
12 | },
13 | { block: 'button' },
14 | '');
15 | });
16 |
17 | it('should support addAttrs with object literal', function() {
18 | test(function() {
19 | block('button').addAttrs()({ id: 'test' });
20 | },
21 | { block: 'button' },
22 | '');
23 | });
24 |
25 | it('should extend attrs from BEMJSON', function() {
26 | test(function() {
27 | block('button')(
28 | addAttrs()(function() {
29 | return { c: 'd' };
30 | })
31 | );
32 | },
33 | { block: 'button', attrs: { a: 'b' } },
34 | '');
35 | });
36 |
37 | it('should rewrite attrs from bemjson if no other attrs templates',
38 | function() {
39 | test(function() {
40 | block('button')(
41 | addAttrs()(function() {
42 | return { id: 'from-tmpls' };
43 | })
44 | );
45 | },
46 | { block: 'button', attrs: { id: 'from-data' } },
47 | '');
48 | });
49 |
50 | it('should accumulate result', function() {
51 | test(function() {
52 | block('button')(
53 | addAttrs()(function() {
54 | return { one: 'true' };
55 | }),
56 | addAttrs()(function() {
57 | return { two: 'false' };
58 | })
59 | );
60 | },
61 | { block: 'button' },
62 | '');
63 | });
64 |
65 | it('should extend attrs from attrs mode', function() {
66 | test(function() {
67 | block('button')(
68 | attrs()(function() {
69 | return { id: 'action' };
70 | }),
71 | addAttrs()(function() {
72 | return { name: 'test' };
73 | })
74 | );
75 | },
76 | { block: 'button' },
77 | '');
78 | });
79 | });
80 |
--------------------------------------------------------------------------------
/test/modes-replace-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 | var assert = require('assert');
4 |
5 | describe('Modes replace', function() {
6 | it('should throw error when args passed to replace mode', function() {
7 | assert.throws(function() {
8 | fixtures.compile(function() {
9 | block('b1').replace('blah');
10 | });
11 | });
12 | });
13 |
14 | it('should support basic mode of operation', function () {
15 | test(function() {
16 | block('b1').content()('ok');
17 | block('b2').content()('replaced');
18 | block('b1').replace()(function () { return { block: 'b2' }; });
19 | }, { block: 'b1' }, 'replaced
');
20 | });
21 |
22 | it('should have proper `this`', function () {
23 | test(function() {
24 | block('b1').content()('ok');
25 | block('b2').content()('replaced');
26 | block('b1').replace()(function () { return { block: this.ctx.wtf }; });
27 | }, { block: 'b1', wtf: 'b2' }, 'replaced
');
28 | });
29 |
30 | it('should work as a singular function', function () {
31 | test(function() {
32 | block('b1').content()('ok');
33 | block('b2').content()('replaced');
34 | block('b1')(replace()(function () { return { block: 'b2' }; }));
35 | }, { block: 'b1' }, 'replaced
');
36 | });
37 |
38 | it('should support inline argument', function () {
39 | test(function() {
40 | block('b1').content()('ok');
41 | block('b2').content()('replaced');
42 | block('b1').replace()({ block: 'b2' });
43 | }, { block: 'b1' }, 'replaced
');
44 | });
45 |
46 | it('should not protected from infinite loop', function() {
47 | assert.throws(function() {
48 | block('b1').replace()({ block: 'b2' });
49 | block('b2').replace()({ block: 'b1' });
50 | });
51 | });
52 |
53 | it('should not match on removed mods', function () {
54 | test(function() {
55 | block('b1').mod('a', 'b').replace()(function() {
56 | return {
57 | block: 'b1',
58 | content: 'content'
59 | };
60 | });
61 | }, {
62 | block: 'b1',
63 | mods: { a: 'b' }
64 | }, 'content
');
65 | });
66 | });
67 |
--------------------------------------------------------------------------------
/test/bemjson-mods-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var compile = fixtures.compile;
3 | var test = fixtures.test;
4 |
5 | describe('BEMJSON mods', function() {
6 | var tmpls;
7 | beforeEach(function() {
8 | tmpls = compile(function() {
9 | block('button').def()(function() {
10 | return JSON.stringify(this.mods);
11 | });
12 | block('button').elem('*').def()(function() {
13 | return JSON.stringify(this.mods);
14 | });
15 | });
16 | });
17 |
18 | it('should return empty mods', function() {
19 | tmpls.apply({ block: 'button' }).should.equal('{}');
20 | });
21 |
22 | it('should return mods', function() {
23 | tmpls.apply({ block: 'button', mods: { type: 'button' } })
24 | .should.equal('{"type":"button"}');
25 | });
26 |
27 | it('should return boolean mods', function() {
28 | tmpls.apply({ block: 'button', mods: { disabled: true } })
29 | .should.equal('{"disabled":true}');
30 | });
31 |
32 | it('should not treat mods as elemMods', function() {
33 | test(function() {}, {
34 | block: 'b1',
35 | content: {
36 | elem: 'e1',
37 | mods: { m1: 'v1' }
38 | }
39 | }, '');
40 | });
41 |
42 | it('should not treat mods as elemMods even if block exist', function() {
43 | test(function() {}, {
44 | block: 'b1',
45 | content: {
46 | block: 'b1',
47 | elem: 'e1',
48 | mods: { m1: 'v1' }
49 | }
50 | }, '');
51 | });
52 |
53 | it('should not treat mods as elemMods in mixes', function() {
54 | test(function() {}, {
55 | block: 'b1',
56 | mix: {
57 | elem: 'e1',
58 | mods: { m1: 'v1' }
59 | }
60 | }, '');
61 | });
62 |
63 | it('should not inherit mods from namesake parent block', function() {
64 | test(function() {}, {
65 | block: 'b1',
66 | mods: { a: 1 },
67 | content: { block: 'b1' }
68 | }, '');
69 | });
70 |
71 | it('should not render epmty modName mods', function() {
72 | test(function() {}, {
73 | block: 'b',
74 | mods: { a: 1, '': 2 }
75 | }, '');
76 | });
77 | });
78 |
--------------------------------------------------------------------------------
/bin/bem-xjst:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | var fs = require('fs');
3 | var bem_xjst = require('../');
4 |
5 | require('coa').Cmd()
6 | .name(process.argv[1])
7 | .helpful()
8 | .opt()
9 | .name('version')
10 | .title('Version')
11 | .short('v')
12 | .long('version')
13 | .only()
14 | .flag()
15 | .act(function(opts) {
16 | // return message as result of action
17 | var package = fs.readFileSync(__dirname +
18 | '/../package.json');
19 | return JSON.parse(package).version;
20 | })
21 | .end()
22 | .opt()
23 | .name('input').title('File with user templates (default: stdin)')
24 | .short('i').long('input')
25 | .input()
26 | .end()
27 | .opt()
28 | .name('output').title('Output bundle file (default: stdout)')
29 | .short('o').long('output')
30 | .output()
31 | .end()
32 | .opt()
33 | .name('engine').title('Engine name (default: bemhtml, supported: bemhtml | bemtree)')
34 | .short('e').long('engine')
35 | .def('bemhtml')
36 | .end()
37 | .opt()
38 | .name('sourcemap').title('Generate source map (default: false)')
39 | .short('s').long('sourcemap')
40 | .def(false)
41 | .flag()
42 | .end()
43 | .act(function(options) {
44 | return new Promise(function(resolve) {
45 | var input = [];
46 |
47 | options.input.on('data', function(chunk) {
48 | input.push(chunk);
49 | });
50 |
51 | options.input.once('end', function() {
52 | finish(input.join(''));
53 | });
54 |
55 | options.input.resume();
56 |
57 | function finish(source) {
58 | var out = bem_xjst[options.engine].generate(source, {
59 | 'no-opt': options['no-opt'],
60 | from: options.input.path,
61 | to: options.output.path,
62 | sourceMap: options.sourceMap
63 | });
64 |
65 | options.output.write(out);
66 |
67 | if (options.output === process.stdout) {
68 | options.output.write('\n');
69 | } else {
70 | options.output.end();
71 | }
72 |
73 | resolve();
74 | }
75 | });
76 | })
77 | .run();
78 |
--------------------------------------------------------------------------------
/test/bemjson-cls-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var compile = fixtures.compile;
3 |
4 | describe('BEMJSON cls', function() {
5 | it('should not return undefined as cls value', function() {
6 | compile('')
7 | .apply({ cls: undefined })
8 | .should.equal('');
9 | });
10 |
11 | it('should not return null as cls value', function() {
12 | compile('')
13 | .apply({ cls: null })
14 | .should.equal('');
15 | });
16 |
17 | it('should return Number as cls value', function() {
18 | compile('')
19 | .apply({ cls: -100 })
20 | .should.equal('');
21 | });
22 |
23 | it('should not return zero as cls value', function() {
24 | compile('')
25 | .apply({ cls: 0 })
26 | .should.equal('');
27 | });
28 |
29 | it('should not return NaN as cls value', function() {
30 | compile('')
31 | .apply({ cls: NaN })
32 | .should.equal('');
33 | });
34 |
35 | it('should return String as cls value', function() {
36 | compile('')
37 | .apply({ cls: 'name' })
38 | .should.equal('');
39 | });
40 |
41 | it('should not return empty string as cls value', function() {
42 | compile('')
43 | .apply({ cls: '' })
44 | .should.equal('');
45 | });
46 |
47 | it('should return true as cls value', function() {
48 | compile('')
49 | .apply({ cls: true })
50 | .should.equal('');
51 | });
52 |
53 | it('should not return false as cls value', function() {
54 | compile('')
55 | .apply({ cls: false })
56 | .should.equal('');
57 | });
58 |
59 | it('should return Array as cls value', function() {
60 | compile('')
61 | .apply({ cls: [] })
62 | .should.equal('');
63 | });
64 |
65 | it('should not return Object as cls value', function() {
66 | compile('')
67 | .apply({ cls: { a: 1, b: 2 } })
68 | .should.equal('');
69 | });
70 |
71 | it('should trim cls', function() {
72 | compile('')
73 | .apply({ cls: ' hello ' })
74 | .should.equal('');
75 | });
76 |
77 | it('should escape cls', function() {
78 | compile('')
79 | .apply({ block: 'b', cls: '">' })
80 | .should.equal('');
81 | });
82 | });
83 |
--------------------------------------------------------------------------------
/test/modes-addjs-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 |
4 | describe('Modes addJs', function() {
5 | it('should support addJs', function() {
6 | test(function() {
7 | block('button')(
8 | addJs()(function() {
9 | return { test: 1 };
10 | })
11 | );
12 | },
13 | { block: 'button' },
14 | '');
15 | });
16 |
17 | it('should support addJs with object literal', function() {
18 | test(function() {
19 | block('button').addJs()({ test: 1 });
20 | },
21 | { block: 'button' },
22 | '');
23 | });
24 |
25 | it('should rewrite js from bemjson if no other js templates',
26 | function() {
27 | test(function() {
28 | block('button')(
29 | addJs()(function() {
30 | return { test: 2 };
31 | })
32 | );
33 | },
34 | { block: 'button', js: { test: 1 } },
35 | '');
36 | });
37 |
38 | it('should accumulate result', function() {
39 | test(function() {
40 | block('button')(
41 | addJs()(function() {
42 | return { one: 1 };
43 | }),
44 | addJs()(function() {
45 | return { two: 42 };
46 | })
47 | );
48 | },
49 | { block: 'button' },
50 | '');
52 | });
53 |
54 | it('should extend js from BEMJSON', function() {
55 | test(function() {
56 | block('button')(
57 | addJs()(function() {
58 | return { type: 'serial' };
59 | })
60 | );
61 | },
62 | { block: 'button', js: { film: 'lost' } },
63 | '');
65 | });
66 |
67 | it('should extend js from js mode', function() {
68 | test(function() {
69 | block('button')(
70 | js()(function() {
71 | return { film: 'lost' };
72 | }),
73 | addJs()(function() {
74 | return { type: 'serial' };
75 | })
76 | );
77 | },
78 | { block: 'button' },
79 | '');
81 | });
82 | });
83 |
--------------------------------------------------------------------------------
/test/bemjson-bem-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 |
4 | describe('BEMJSON bem', function() {
5 | it('should not render class if bem equals false', function () {
6 | test(function() {},
7 | { block: 'b', bem: false },
8 | '');
9 | });
10 |
11 | it('should render class if bem equals false and cls field is set',
12 | function () {
13 | test(function() {},
14 | { cls: 'test', bem: false },
15 | '');
16 | });
17 |
18 | it('should render class if bem equals true', function () {
19 | test(function() {},
20 | { block: 'b', bem: true },
21 | '');
22 | });
23 |
24 | it('should render class if bem equals undefined', function() {
25 | test(function() {},
26 | { block: 'b', bem: undefined },
27 | '');
28 | });
29 |
30 | it('should render class if bem equals null', function() {
31 | test(function() {},
32 | { block: 'b', bem: null },
33 | '');
34 | });
35 |
36 | it('should render class if bem equals Number', function() {
37 | test(function() {},
38 | { block: 'b', bem: 100 },
39 | '');
40 | });
41 |
42 | it('should render class if bem equals zero', function() {
43 | test(function() {},
44 | { block: 'b', bem: 0 },
45 | '');
46 | });
47 |
48 | it('should render class if bem equals NaN', function() {
49 | test(function() {},
50 | { block: 'b', bem: NaN },
51 | '');
52 | });
53 |
54 | it('should render class if bem equals String', function() {
55 | test(function() {},
56 | { block: 'b', bem: 'skipme' },
57 | '');
58 | });
59 |
60 | it('should render class if bem equals empty string', function() {
61 | test(function() {},
62 | { block: 'b', bem: '' },
63 | '');
64 | });
65 |
66 | it('should not return Array as attrs value', function() {
67 | test(function() {},
68 | { block: 'b', bem: [ 1, 2 ] },
69 | '');
70 | });
71 |
72 | it('should output cls value if bem:false', function() {
73 | test(function() {
74 | block('b').js()(true);
75 | },
76 | [
77 | { block: 'b', bem: false, cls: 'anything' },
78 | { block: 'b', bem: false }
79 | ],
80 | '');
81 | });
82 | });
83 |
--------------------------------------------------------------------------------
/test/runtime-escaping-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 |
4 | describe('Content escaping', function() {
5 | it('should escape content if escapeContent option flag is set', function() {
6 | test(function() {},
7 | { block: 'b', content: ''
72 | },
73 | '');
74 | });
75 | });
76 |
--------------------------------------------------------------------------------
/test/modes-mods-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 |
4 | describe('Modes mods', function() {
5 | it('should support mods', function() {
6 | test(function() {
7 | block('button').mods()(function() {
8 | return { size: 'l' };
9 | });
10 | },
11 | { block: 'button' },
12 | '');
13 | });
14 |
15 | it('should support mods with object literal', function() {
16 | test(function() {
17 | block('button').mods()({ size: 'l' });
18 | },
19 | { block: 'button' },
20 | '');
21 | });
22 |
23 | it('should rewrite mods from bemjson if no other mods templates',
24 | function() {
25 | test(function() {
26 | block('button').mods()(function() {
27 | return { size: 'l' };
28 | });
29 | },
30 | { block: 'button', mods: { size: 's' } },
31 | '');
32 | });
33 |
34 | it('should rewrite previous mods', function() {
35 | test(function() {
36 | block('button')(
37 | mods()(function() {
38 | return { theme: 'dark' };
39 | }),
40 | mods()(function() {
41 | return { size: 'xl' };
42 | })
43 | );
44 | },
45 | { block: 'button', mods: { type: 'promo' } },
46 | '');
47 | });
48 |
49 | it('should return this.mods by default', function() {
50 | test(function() {
51 | block('a').def()(function() { return JSON.stringify(apply('mods')); });
52 | block('b').def()(function() { return JSON.stringify(apply('mods')); });
53 | },
54 | [ { block: 'a' }, { block: 'b', mods: { type: 'promo' } } ],
55 | '{}{"type":"promo"}');
56 | });
57 |
58 | it('should apply templates for mods after mods changes', function() {
59 | test(function() {
60 | block('a')(
61 | mod('test', 'ok').tag()('span'),
62 | mods()({ test: 'ok' })
63 | );
64 | },
65 | { block: 'a' },
66 | '');
67 | });
68 |
69 | it('should support applyNext()', function() {
70 | test(function() {
71 | block('b')(
72 | mods()(function() {
73 | return this.extend(applyNext(), { theme: 'dark' });
74 | }),
75 | mods()(function() {
76 | return this.extend(applyNext(), { size: 'xl' });
77 | })
78 | );
79 | },
80 | { block: 'b', mods: { type: 'promo' } },
81 | '');
82 | });
83 | });
84 |
--------------------------------------------------------------------------------
/test/bemcontext-xmlescape-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var compile = fixtures.compile;
3 | var test = fixtures.test;
4 |
5 | describe('BEMContext this.xmlEscape()', function() {
6 | it('should escape lower than, greater than and ampersands', function() {
7 | test(function() {
8 | block('button').def()(function() {
9 | return this.xmlEscape('&');
10 | });
11 | },
12 | { block: 'button' },
13 | '<b>&</b>');
14 | });
15 |
16 | describe('Type of argument', function() {
17 | var bemhtml;
18 |
19 | before(function() {
20 | bemhtml = compile(function() {
21 | block('b').def()(function(n, ctx) {
22 | return n.xmlEscape(ctx.val);
23 | });
24 | });
25 | });
26 |
27 | it('should return \'\' for undefined', function() {
28 | bemhtml.apply({ block: 'b', val: undefined }).should.equal('');
29 | });
30 |
31 | it('should return \'\' for null', function() {
32 | bemhtml.apply({ block: 'b', val: null }).should.equal('');
33 | });
34 |
35 | it('should return String for zero', function() {
36 | bemhtml.apply({ block: 'b', val: 0 }).should.equal('0');
37 | });
38 |
39 | it('should return String for Number', function() {
40 | bemhtml.apply({ block: 'b', val: 42 }).should.equal('42');
41 | });
42 |
43 | it('should return \'\' for NaN', function() {
44 | bemhtml.apply({ block: 'b', val: NaN }).should.equal('');
45 | });
46 |
47 | it('should return String for String', function() {
48 | bemhtml.apply({ block: 'b', val: '' }).should.equal('');
49 | });
50 |
51 | it('should return String for String', function() {
52 | bemhtml.apply({ block: 'b', val: 'test' }).should.equal('test');
53 | });
54 |
55 | it('should return String for Boolean', function() {
56 | bemhtml.apply({ block: 'b', val: false }).should.equal('false');
57 | });
58 |
59 | it('should return String for Array', function() {
60 | bemhtml.apply({ block: 'b', val: [] }).should.equal('');
61 | });
62 |
63 | it('should return String for Array', function() {
64 | bemhtml.apply({ block: 'b', val: [ 'a', 'b' ] }).should.equal('a,b');
65 | });
66 |
67 | it('should return \'\' for Object', function() {
68 | bemhtml.apply({ block: 'b', val: {
69 | toString: function() { return ''; }
70 | } }).should.equal('');
71 | });
72 |
73 | it('should return \'\' for Function', function() {
74 | bemhtml.apply({ block: 'b', val: function() {} })
75 | .should.equal('function() {}');
76 | });
77 | });
78 | });
79 |
--------------------------------------------------------------------------------
/runtime-lint/stand.js:
--------------------------------------------------------------------------------
1 | var bemxjst = require('../');
2 | var bemhtml = bemxjst.bemhtml;
3 |
4 | var templates = bemhtml.compile(function() {
5 | block('b').content()('yay');
6 |
7 | block('class-attr-tmpl').attrs()(function() {
8 | return { class: 'wrong' };
9 | });
10 |
11 | block('databem-attr-tmpl').attrs()(function() {
12 | return { 'data-bem': 'wrong' };
13 | });
14 |
15 | block('mix-mod-tmpl').mix()(function() {
16 | return [ applyNext(), { mods: { color: 'green' } } ];
17 | });
18 |
19 | block('mix-elemmod-tmpl').elem('e').mix()(function() {
20 | return [ applyNext(), { elemMods: { color: 'green' } } ];
21 | });
22 | }, { runtimeLint: true });
23 |
24 | var html = templates.apply([
25 | { block: 'b' },
26 |
27 | // boolean attributes
28 | { block: 'b', attrs: { one: true, two: 'true' } },
29 |
30 | // mods for elem
31 | { block: 'c', elem: 'e', mods: { test: 'opa' } },
32 |
33 | // class in attrs
34 | { block: 'class-attr-bemjson', attrs: { id: 'test', class: 'jquery' } },
35 | { block: 'class-attr-tmpl' },
36 |
37 | // 'data-bem' in attrs
38 | { block: 'databem-attr-bemjson', attrs: { 'data-bem': { block: 'a', js: true } } },
39 | { block: 'databem-attr-tmpl' },
40 |
41 | // mix the same mod
42 | { block: 'mix-mod', mods: { m: 1 }, mix: { mods: { m: 2 } } },
43 | { block: 'mix-mod', mix: [ { mods: { type: 'test' } }, { mods: { type: 'shmest' } } ] },
44 | // mix the same mod from templates
45 | { block: 'mix-mod-tmpl', mods: { color: 'red' } },
46 |
47 | { block: 'mix-elemmod', elem: 'e', elemMods: { m: 1 }, mix: { elemMods: { m: 2 } } },
48 | { block: 'mix-elemmod', elem: 'e', mix: [ { elemMods: { type: 'test' } }, { elemMods: { type: 'shmest' } } ] },
49 | // mix the same mod from templates
50 | { block: 'mix-elemmod-tmpl', elem: 'e', elemMods: { color: 'red' } },
51 |
52 | // Wrong names
53 | { block: 'bad__name' },
54 | { block: 'bad_name' },
55 | { block: 'b', elem: 'e_e' },
56 | { block: 'b', elem: 'e__e' },
57 | { block: 'b', mods: { mod_name: 'bad' } },
58 | { block: 'b', mods: { mod__name: 'bad' } },
59 | { block: 'b', mods: { modName: 'very_bad' } },
60 | { block: 'b', mods: { modName: 'very__bad' } },
61 |
62 | { block: 'b', elem: 'e', elemMods: { mod_name: 'bad' } },
63 | { block: 'b', elem: 'e', elemMods: { mod__name: 'bad' } },
64 | { block: 'b', elem: 'e', elemMods: { modName: 'very_bad' } },
65 | { block: 'b', elem: 'e', elemMods: { modName: 'very__bad' } },
66 |
67 | // Wrong mods/elemMods types
68 | { block: 'b', mods: { test: [ 'a', 'b' ] } },
69 | { block: 'b', elem: 'e', elemMods: { test: [ 'a', 'b' ] } }
70 | ]);
71 |
72 | console.log(html);
73 |
--------------------------------------------------------------------------------
/test/runtime-match-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 |
4 | describe('Runtime Match', function() {
5 | it('should call body function in BEMContext context', function() {
6 | test(function() {
7 | block('b').def()(function() {
8 | return this.constructor.name;
9 | });
10 | }, { block: 'b' }, 'ContextChild');
11 | });
12 |
13 | it('should pass BEMContext as the first argument', function() {
14 | test(function() {
15 | block('b').def()(function(ctx) {
16 | return ctx.constructor.name;
17 | });
18 | }, { block: 'b' }, 'ContextChild');
19 | });
20 |
21 | it('should pass BEMContext instance to custom mode', function() {
22 | test(function() {
23 | block('b').mode('custom')(function(ctx) {
24 | return ctx.constructor.name;
25 | });
26 | block('b').def()(function() {
27 | return apply('custom');
28 | });
29 | }, { block: 'b' }, 'ContextChild');
30 | });
31 |
32 | it('should pass bemjson node as the second argument', function() {
33 | test(function() {
34 | block('b').def()(function(_, json) {
35 | return json.foo;
36 | });
37 | }, { block: 'b', foo: 'bar' }, 'bar');
38 | });
39 |
40 | it('should pass json to custom mode', function() {
41 | test(function() {
42 | block('b').mode('custom')(function(_, json) {
43 | return json.foo;
44 | });
45 | block('b').def()(function() {
46 | return apply('custom');
47 | });
48 | }, { block: 'b', foo: 'bar' }, 'bar');
49 | });
50 |
51 | it('should pass BEMContext instance and json to match method body',
52 | function() {
53 | test(function() {
54 | block('b').match(function(ctx, json) {
55 | this._what = json.foo + ' ' + ctx.constructor.name;
56 | return true;
57 | }).def()(function() {
58 | return this._what;
59 | });
60 | }, { block: 'b', foo: 'This is' }, 'This is ContextChild');
61 | });
62 |
63 | it('should pass BEMContext instance and json to replace body. #226',
64 | function() {
65 | test(function() {
66 | block('b').replace()(function(ctx, json) {
67 | return json.foo + ' ' + ctx.constructor.name;
68 | });
69 | }, { block: 'b', foo: 'This is' }, 'This is ContextChild');
70 | });
71 |
72 | it('should pass BEMContext instance and json to wrap body',
73 | function() {
74 | test(function() {
75 | block('b').wrap()(function(ctx, json) {
76 | return json.foo + ' ' + ctx.constructor.name;
77 | });
78 | }, { block: 'b', foo: 'This is' }, 'This is ContextChild');
79 | });
80 | });
81 |
--------------------------------------------------------------------------------
/test/bemcontext-attrescape-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var compile = fixtures.compile;
3 | var test = fixtures.test;
4 |
5 | describe('BEMContext this.attrEscape(str)', function() {
6 | it('should escape xml attr string', function() {
7 | test(function() {
8 | block('button').def()(function() {
9 | return this.attrEscape('a&b&c');
10 | });
11 | },
12 | { block: 'button' },
13 | '<b id="a" class="bem">a&b&c</b>');
14 | });
15 |
16 | describe('Type of argument', function() {
17 | var bemhtml;
18 |
19 | before(function() {
20 | bemhtml = compile(function() {
21 | block('b').def()(function(n, ctx) {
22 | return n.attrEscape(ctx.val);
23 | });
24 | });
25 | });
26 |
27 | it('should return \'\' for undefined', function() {
28 | bemhtml.apply({ block: 'b', val: undefined }).should.equal('');
29 | });
30 |
31 | it('should return \'\' for null', function() {
32 | bemhtml.apply({ block: 'b', val: null }).should.equal('');
33 | });
34 |
35 | it('should return String for zero', function() {
36 | bemhtml.apply({ block: 'b', val: 0 }).should.equal('0');
37 | });
38 |
39 | it('should return String for Number', function() {
40 | bemhtml.apply({ block: 'b', val: 42 }).should.equal('42');
41 | });
42 |
43 | it('should return \'\' for NaN', function() {
44 | bemhtml.apply({ block: 'b', val: NaN }).should.equal('');
45 | });
46 |
47 | it('should return String for String', function() {
48 | bemhtml.apply({ block: 'b', val: '' }).should.equal('');
49 | });
50 |
51 | it('should return String for String', function() {
52 | bemhtml.apply({ block: 'b', val: 'test' }).should.equal('test');
53 | });
54 |
55 | it('should return String for Boolean', function() {
56 | bemhtml.apply({ block: 'b', val: false }).should.equal('false');
57 | });
58 |
59 | it('should return String for Array', function() {
60 | bemhtml.apply({ block: 'b', val: [] }).should.equal('');
61 | });
62 |
63 | it('should return String for Array', function() {
64 | bemhtml.apply({ block: 'b', val: [ 'a', 'b' ] }).should.equal('a,b');
65 | });
66 |
67 | it('should return \'\' for Object', function() {
68 | bemhtml.apply({ block: 'b', val: {
69 | toString: function() { return ''; }
70 | } }).should.equal('');
71 | });
72 |
73 | it('should return \'\' for Function', function() {
74 | bemhtml.apply({ block: 'b', val: function() {} })
75 | .should.equal('function() {}');
76 | });
77 | });
78 | });
79 |
--------------------------------------------------------------------------------
/test/bemcontext-jsattrescape-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var compile = fixtures.compile;
3 |
4 | describe('BEMContext this.jsAttrEscape(str)', function() {
5 | it('should escape quotes and ampersands', function() {
6 | compile(function() {
7 | block('button').def()(function() {
8 | return this.jsAttrEscape('\'&\'\'&\'');
9 | });
10 | })
11 | .apply({ block: 'button' })
12 | .should.equal(''&''&' +
13 | ''');
14 | });
15 |
16 | describe('Type of argument', function() {
17 | var bemhtml;
18 |
19 | before(function() {
20 | bemhtml = compile(function() {
21 | block('b').def()(function(n, ctx) {
22 | return n.jsAttrEscape(ctx.val);
23 | });
24 | });
25 | });
26 |
27 | it('should return \'\' for undefined', function() {
28 | bemhtml.apply({ block: 'b', val: undefined }).should.equal('');
29 | });
30 |
31 | it('should return \'\' for null', function() {
32 | bemhtml.apply({ block: 'b', val: null }).should.equal('');
33 | });
34 |
35 | it('should return String for zero', function() {
36 | bemhtml.apply({ block: 'b', val: 0 }).should.equal('0');
37 | });
38 |
39 | it('should return String for Number', function() {
40 | bemhtml.apply({ block: 'b', val: 42 }).should.equal('42');
41 | });
42 |
43 | it('should return \'\' for NaN', function() {
44 | bemhtml.apply({ block: 'b', val: NaN }).should.equal('');
45 | });
46 |
47 | it('should return String for String', function() {
48 | bemhtml.apply({ block: 'b', val: '' }).should.equal('');
49 | });
50 |
51 | it('should return String for String', function() {
52 | bemhtml.apply({ block: 'b', val: 'test' }).should.equal('test');
53 | });
54 |
55 | it('should return String for Boolean', function() {
56 | bemhtml.apply({ block: 'b', val: false }).should.equal('false');
57 | });
58 |
59 | it('should return String for Array', function() {
60 | bemhtml.apply({ block: 'b', val: [] }).should.equal('');
61 | });
62 |
63 | it('should return String for Array', function() {
64 | bemhtml.apply({ block: 'b', val: [ 'a', 'b' ] }).should.equal('a,b');
65 | });
66 |
67 | it('should return \'\' for Object', function() {
68 | bemhtml.apply({ block: 'b', val: {
69 | toString: function() { return ''; }
70 | } }).should.equal('');
71 | });
72 |
73 | it('should return \'\' for Function', function() {
74 | bemhtml.apply({ block: 'b', val: function() {} })
75 | .should.equal('function() {}');
76 | });
77 | });
78 | });
79 |
--------------------------------------------------------------------------------
/test/init-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 | var bemhtml = require('../').bemhtml;
4 | var assert = require('assert');
5 |
6 | describe('oninit', function() {
7 | it('should support oninit', function() {
8 | test(function() {
9 | oninit(function(exports) {
10 | exports.apply = function() {
11 | return 'ok';
12 | };
13 | });
14 | }, {}, 'ok');
15 | });
16 |
17 | it('should support changing prototype of BEMContext', function() {
18 | test(function() {
19 | oninit(function(exports) {
20 | exports.BEMContext.prototype.yes = 'hah';
21 | });
22 |
23 | block('b1').content()(function() {
24 | return this.yes;
25 | });
26 | }, {
27 | block: 'b1'
28 | }, 'hah
');
29 | });
30 |
31 | it('should put BEMContext to sharedContext too', function() {
32 | test(function() {
33 | oninit(function(exports, shared) {
34 | shared.BEMContext.prototype.yes = 'hah';
35 | });
36 |
37 | block('b1').content()(function() {
38 | return this.yes;
39 | });
40 | }, {
41 | block: 'b1'
42 | }, 'hah
');
43 | });
44 |
45 | it('should support flushing', function() {
46 | test(function() {
47 | oninit(function(exports) {
48 | exports.BEMContext.prototype._flushIndex = 0;
49 | exports.BEMContext.prototype._flush = function flush(str) {
50 | return '[' + (this._flushIndex++) + '.' + str + ']';
51 | };
52 | });
53 | }, {
54 | block: 'b1',
55 | content: {
56 | block: 'b2'
57 | }
58 | }, '[4.[3.[0.]]');
59 | });
60 |
61 | it('should use global object as `this`', function() {
62 | test(function() {
63 | oninit(function() {
64 | this._something = { blah: 42 };
65 | });
66 | block('b1').replace()(function() {
67 | /* global _something */
68 | return _something.blah;
69 | });
70 | }, { block: 'b1' }, '42');
71 | });
72 |
73 | it('should exec new oninit functions on every compile', function() {
74 | var template = bemhtml.compile(function() {
75 | oninit(function(exports) { exports.BEMContext.prototype._one = '1'; });
76 | block('b').content()(function() { return this._one + this._two; });
77 | });
78 | var bemjson = { block: 'b' };
79 | assert.equal(template.apply(bemjson), '1undefined
');
80 |
81 | template.compile(function() {
82 | oninit(function(exports) {
83 | exports.BEMContext.prototype._two = 1; });
84 | });
85 | assert.equal(template.apply(bemjson), '11
');
86 | });
87 | });
88 |
--------------------------------------------------------------------------------
/test/modes-extend-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 | var assert = require('assert');
4 |
5 | describe('Modes extend', function() {
6 | it('should throw error when args passed to extend mode', function() {
7 | assert.throws(function() {
8 | fixtures.compile(function() {
9 | block('b1').extend('blah');
10 | });
11 | });
12 | });
13 |
14 | it('should support basic mode of operation', function () {
15 | test(function() {
16 | block('b')(
17 | extend()(function() { return { test: 'it work’s' }; }),
18 | content()(function() { return this.test; })
19 | );
20 | }, { block: 'b' }, 'it work’s
');
21 | });
22 |
23 | it('should support inline argument', function () {
24 | test(function() {
25 | block('b')(
26 | extend()({ test: 'it work’s' }),
27 | content()(function() { return this.test; })
28 | );
29 | }, { block: 'b' }, 'it work’s
');
30 | });
31 |
32 | it('should have proper `this`', function () {
33 | test(function() {
34 | block('b')(
35 | extend()(function() { return { test: this.ctx.wtf }; }),
36 | content()(function() { return this.test; })
37 | );
38 | }, { block: 'b', wtf: 'it work’s' }, 'it work’s
');
39 | });
40 |
41 | it('should proxy data', function () {
42 | test(function() {
43 | block('b').extend()(function() { return { test: 42 }; });
44 | block('*').attrs()(function() { return { life: this.test }; });
45 | },
46 | {
47 | block: 'b',
48 | content: { block: 'a', content: { block: 'c' } }
49 | },
50 | '');
52 | });
53 |
54 | it('should extend ctx', function () {
55 | test(function() {
56 | block('b').extend()(function() { return { 'ctx.content': 42 }; });
57 | },
58 | { block: 'b' },
59 | '42
');
60 | });
61 |
62 | it('should support applyNext', function () {
63 | test(function() {
64 | block('b').extend()(function() { return { 'ctx.content': 1 }; });
65 | block('b').extend()(function() {
66 | return { 'ctx.attrs': { id: 'test' } }; });
67 | },
68 | { block: 'b' },
69 | '1
');
70 | });
71 |
72 | it('should pass BEMContext instance and json to extend body',
73 | function() {
74 | test(function() {
75 | block('b')(
76 | extend()(function(ctx, json) {
77 | return { bar: json.foo + ' ' + ctx.constructor.name };
78 | }),
79 | content()(function() { return this.bar; })
80 | );
81 | }, { block: 'b', foo: 'This is' },
82 | 'This is ContextChild
');
83 | });
84 | });
85 |
--------------------------------------------------------------------------------
/migration/README.md:
--------------------------------------------------------------------------------
1 | # Static linter for templates
2 |
3 | Linting allows you see some warnings about template code in STDERR. List of checks you can find in [migration guides](https://github.com/bem/bem-xjst/wiki/Migration-guides).
4 |
5 | `cd migration/ && npm i`
6 |
7 | `cd ../`
8 |
9 | `./migration/lib/index.js --lint --input ./path-to-templates/ --from 7 --to 8`
10 |
11 | where
12 | * `lint` lint option (if not present script will rewrite your templates)
13 | * `input` path to templates (relative to current directory or absolute)
14 | * `7` is major version from. If you specify `0` common check will be included.
15 | * `8` is major to lint.
16 |
17 | Result of linting is console warning like this:
18 |
19 | ```
20 | BEM-XJST WARNING:
21 | >>>> Function that returns a literal can be replaced with literal itself.
22 | >>>> migration/tmpls/template.js:8:1
23 | >>>> function() { return 42; }
24 | ```
25 |
26 | # Migration tool for templates
27 |
28 | Migration tool helps you migrate your project to next major version of bem-xjst.
29 |
30 | `cd migration/ && npm i`
31 |
32 | `cd ../`
33 |
34 | `./migration/lib/index.js --input ./path-to-templates/ --from 7 --to 8`
35 |
36 | where
37 | * `7` is current bem-xjst version on your project
38 | * `8` is major version to migrate.
39 |
40 | Notice that templates in `./path-to-templates/` will be overwritten.
41 |
42 | ## Codestyle config
43 |
44 | You can create json file with several
45 | [options](https://github.com/benjamn/recast/blob/52a7ec3eaaa37e78436841ed8afc948033a86252/lib/options.js#L1) that have recast.
46 |
47 | Using `config` option you can pass path to json config:
48 |
49 | `./migration/lib/index.js --input ./path-to-templates-dir --from 4 --to 8 --config ~/my-prj/config/codestyle-config.json`
50 |
51 | Notice: path to json config must be absolute.
52 |
53 | See example of codestyle config `sample-config.json` in this directory.
54 |
55 | # List of transformers (checks)
56 |
57 | * Array to function generator
58 | * Object to function generator
59 | * Don’t check `this.elemMods`
60 | * Don’t check `this.mods`
61 | * If function generator return simple type rewrite this function to it’s return value
62 | * Check for HTML entities. Warning about using UTF-8 symbols.
63 | * def() must return something
64 | * No empty mode call
65 | * No empty mode
66 | * No more `this.`.
67 | * Apply call to apply
68 | * API changed (require('bem-xjst').bemhtml)
69 | * `elemMatch()` to `elem()` with `match()`
70 | * `mods` value
71 | * `once()` to `def()`
72 | * `this.isArray()` to `Array.isArray()`
73 | * `xhtml: true` option for backwards capability (from 6 to 7)
74 | * `attrs()` to `addAttrs()`
75 | * `js()` to `addJs()`
76 | * `mix()` to `addMix()`
77 |
78 |
79 | # Tests
80 |
81 | `npm test`
82 |
83 |
84 | # Migrate from old DSL BEMHTML to JS syntax
85 |
86 | Take a look at https://github.com/bem/bem-templates-converter
87 |
--------------------------------------------------------------------------------
/test/modes-elemmods-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 |
4 | describe('Modes elemMods', function() {
5 | it('should support elemMods', function() {
6 | test(function() {
7 | block('button').elem('e').elemMods()(function() {
8 | return { size: 'l' };
9 | });
10 | },
11 | { block: 'button', elem: 'e' },
12 | '');
13 | });
14 |
15 | it('should support elemMods with object literal', function() {
16 | test(function() {
17 | block('button').elem('e').elemMods()({ size: 'l' });
18 | },
19 | { block: 'button', elem: 'e' },
20 | '');
21 | });
22 |
23 | it('should rewrite elemMods from bemjson if no other mods templates',
24 | function() {
25 | test(function() {
26 | block('button').elem('e').elemMods()(function() {
27 | return { size: 'l' };
28 | });
29 | },
30 | { block: 'button', elem: 'e', elemMods: { size: 's' } },
31 | '');
32 | });
33 |
34 | it('should rewrite previous elemMods', function() {
35 | test(function() {
36 | block('button').elem('e')(
37 | elemMods()(function() {
38 | return { theme: 'dark' };
39 | }),
40 | elemMods()(function() {
41 | return { size: 'xl' };
42 | })
43 | );
44 | },
45 | { block: 'button', elem: 'e', elemMods: { type: 'promo' } },
46 | '');
47 | });
48 |
49 | it('should return this.elemMods by default', function() {
50 | test(function() {
51 | block('a').elem('e')
52 | .def()(function() { return JSON.stringify(apply('elemMods')); });
53 | block('b').elem('e')
54 | .def()(function() { return JSON.stringify(apply('elemMods')); });
55 | },
56 | [
57 | { block: 'a', elem: 'e' },
58 | { block: 'b', elem: 'e', elemMods: { type: 'promo' } }
59 | ],
60 | '{}{"type":"promo"}');
61 | });
62 |
63 | it('should apply templates for mods after mods changes', function() {
64 | test(function() {
65 | block('a').elem('e')(
66 | elemMod('test', 'ok').tag()('span'),
67 | elemMods()({ test: 'ok' })
68 | );
69 | },
70 | { block: 'a', elem: 'e' },
71 | '');
72 | });
73 |
74 | it('should support applyNext()', function() {
75 | test(function() {
76 | block('b').elem('e')(
77 | elemMods()(function() {
78 | return this.extend(applyNext(), { theme: 'dark' });
79 | }),
80 | elemMods()(function() {
81 | return this.extend(applyNext(), { size: 'xl' });
82 | })
83 | );
84 | },
85 | { block: 'b', elem: 'e', elemMods: { type: 'promo' } },
86 | '');
87 | });
88 | });
89 |
--------------------------------------------------------------------------------
/examples/simple-page/bemhtml-templates.js:
--------------------------------------------------------------------------------
1 | module.exports = function() {
2 | // 1. Common HTML elements — doctype, html, head, meta, title, body
3 | block('page').wrap()(function() {
4 | var ctx = this.ctx;
5 | var data = ctx.data;
6 |
7 | return [
8 | '',
9 | {
10 | tag: 'html',
11 | attrs: { lang: ctx.lang || 'en-US' },
12 | content: [
13 | {
14 | tag: 'head',
15 | content: [
16 | { tag: 'meta', attrs: { charset: 'utf-8' } },
17 | { tag: 'title', content: ctx.title }
18 | ]
19 | },
20 | {
21 | tag: 'body',
22 | content: ctx
23 | }
24 | ]
25 | }
26 | ];
27 | });
28 |
29 | // 2. User block — name, username, position
30 | block('user')(
31 | tag()('h1'),
32 |
33 | // Add to content username only if present
34 | match(function() { return this.ctx.username; })
35 | .content()(function() {
36 | return [ applyNext(), 'aka', this.ctx.username ].join(' ');
37 | }),
38 |
39 | // Add to content position only if present
40 | match(function() { return this.ctx.position; })
41 | .content()(function() {
42 | return [ applyNext(), this.ctx.position ].join(', ');
43 | })
44 | );
45 |
46 | // 3. User avatar
47 | block('avatar').replace()(function() {
48 | var avatar = this.ctx.avatar;
49 | return [
50 | {
51 | block: 'link',
52 | url: avatar.big.url,
53 | content: {
54 | block: 'image',
55 | url: this.ctx.avatar.small.url
56 | }
57 | },
58 | {
59 | block: 'para',
60 | content: 'Photo by ' + avatar.small.author
61 | }
62 | ];
63 | });
64 |
65 | block('image')(
66 | tag()('img'),
67 | attrs()(function() {
68 | return { src: this.ctx.url };
69 | })
70 | );
71 |
72 | // 4. List of links — service name, link
73 | block('links')(
74 | tag()('ul'),
75 | elem('item')(
76 | tag()('li'),
77 | content()(function() {
78 | return {
79 | block: 'link',
80 | url: this.ctx.url,
81 | content: applyNext()
82 | };
83 | })
84 | )
85 | );
86 |
87 | block('link')(
88 | tag()('span'),
89 | // Check if there is url field otherwise it’s a span element
90 | match(function() { return this.ctx.url; })(
91 | tag()('a'),
92 | attrs()(function() {
93 | return {
94 | href: this.ctx.url,
95 | target: '_blank'
96 | };
97 | }),
98 |
99 | // Check if there is no service name field in data
100 | // and try to render it from link
101 | match(function() { return !this.ctx.content; })
102 | .content()(function() {
103 | return this.ctx.url.replace(/^https?:\/\//, '');
104 | })
105 | )
106 | );
107 | };
108 |
--------------------------------------------------------------------------------
/test/bemcontext-isfirst-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 |
4 | describe('BEMContext this.isFirst()', function() {
5 | it('should preserve position', function() {
6 | test(function() {
7 | block('button')
8 | .match(function() { return this.isFirst(); })
9 | .addMods()({ first: 'yes' });
10 |
11 | block('button').def()(function() {
12 | return applyNext();
13 | });
14 | },
15 | [
16 | { block: 'button' },
17 | { block: 'button' }
18 | ],
19 | '' +
20 | '');
21 | });
22 |
23 | it('should calc isFirst/isLast with array mess', function() {
24 | test(function() {
25 | block('button').elem('inner')(
26 | match(function() { return this.isFirst(); })
27 | .addElemMods()(function() { return { first: 'yes' }; }),
28 |
29 | match(function() { return this.isLast(); })
30 | .addElemMods()(function() { return { last: 'yes' }; })
31 | );
32 | },
33 | {
34 | block: 'button',
35 | content: [
36 | [ { elem: 'inner' } ],
37 | [ { elem: 'inner' }, [ { elem: 'inner' } ] ]
38 | ]
39 | },
40 | '');
45 | });
46 |
47 | it('should calc isFirst/isLast for single element', function() {
48 | test(function() {
49 | block('button').elem('inner')(
50 | match(function() { return this.isFirst(); })
51 | .addElemMods()({ first: 'yes' }),
52 | match(function() { return this.isLast(); })
53 | .addElemMods()({ last: 'yes' })
54 | );
55 | },
56 | { block: 'button', content: { elem: 'inner' } },
57 | '');
61 | });
62 |
63 | // TODO: https://github.com/bem/bem-xjst/issues/174
64 | it.skip('should ignore empty array items', function() {
65 | test(function() {
66 | block('button')(
67 | match(function() { return this.isFirst(); })
68 | .addMods()({ first: 'yes' }),
69 | match(function() { return this.isLast(); })
70 | .addMods()({ last: 'yes' })
71 | );
72 | },
73 | [
74 | false,
75 | { block: 'button' },
76 | {
77 | content: [
78 | false,
79 | { block: 'button' },
80 | { block: 'button' },
81 | { block: 'button' },
82 | [ null ]
83 | ]
84 | },
85 | null
86 | ],
87 | '' +
88 | '' +
89 | '
' +
90 | '
' +
91 | '
' +
92 | '
');
93 | });
94 | });
95 |
--------------------------------------------------------------------------------
/examples/source-maps/demo2/tmpls-with-sourcemap.bemhtml.js:
--------------------------------------------------------------------------------
1 | block('button')(
2 | def()(function() {
3 | var tag = apply('tag'),
4 | isRealButton = (tag === 'button') && (!this.mods.type || this.mods.type === 'submit');
5 |
6 | return applyNext({ _isRealButton : isRealButton });
7 | }),
8 |
9 | tag()(function() {
10 | return this.ctx.tag || 'button';
11 | }),
12 |
13 | js()(true),
14 |
15 | // NOTE: mix below is to satisfy interface of `control`
16 | mix()({ elem : 'control' }),
17 |
18 | attrs()(
19 | // Common attributes
20 | function() {
21 | var ctx = this.ctx,
22 | attrs = {
23 | role : 'button',
24 | tabindex : ctx.tabIndex,
25 | id : ctx.id,
26 | title : ctx.title
27 | };
28 |
29 | this.mods.disabled &&
30 | !this._isRealButton && (attrs['aria-disabled'] = true);
31 |
32 | return attrs;
33 | },
34 |
35 | // Attributes for button variant
36 | match(function() { return this._isRealButton; })(function() {
37 | var ctx = this.ctx,
38 | attrs = {
39 | type : this.mods.type || 'button',
40 | name : ctx.name,
41 | value : ctx.val
42 | };
43 |
44 | this.mods.disabled && (attrs.disabled = 'disabled');
45 |
46 | return this.extend(applyNext(), attrs);
47 | })
48 | ),
49 |
50 | content()(
51 | function() {
52 | var ctx = this.ctx,
53 | content = [ctx.icon];
54 | // NOTE: wasn't moved to separate template for optimization
55 | 'text' in ctx && content.push({ elem : 'text', content : ctx.text });
56 | return content;
57 | },
58 | match(function() { return typeof this.ctx.content !== 'undefined'; })(function() {
59 | return this.ctx.content;
60 | })
61 | )
62 | );
63 |
64 | block('input')(
65 | tag()('span'),
66 | js()(true),
67 | def()(function() {
68 | return applyNext({ _input : this.ctx });
69 | }),
70 | content()({ elem : 'box', content : { elem : 'control' } })
71 | );
72 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImxpYnMvYmVtLWNvbXBvbmVudHMvY29tbW9uLmJsb2Nrcy9idXR0b24vYnV0dG9uLmJlbWh0bWwiLCJsaWJzL2JlbS1jb21wb25lbnRzL2NvbW1vbi5ibG9ja3MvaW5wdXQvaW5wdXQuYmVtaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUM5REE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwiZmlsZSI6InRtcGwtd2l0aC1zb3VyY2VtYXAuanMifQ==
--------------------------------------------------------------------------------
/test/runtime-apply-test.js:
--------------------------------------------------------------------------------
1 | var fixtures = require('./fixtures')('bemhtml');
2 | var test = fixtures.test;
3 |
4 | describe('Runtime apply()', function() {
5 | it('apply(\'content\') from def()', function() {
6 | test(function() {
7 | block('b').def()(function() {
8 | return apply('content');
9 | });
10 | }, { block: 'b', content: 'test' },
11 | 'test');
12 | });
13 |
14 | it('apply(\'mix\') from def()', function() {
15 | test(function() {
16 | block('b').def()(function() {
17 | return apply('mix');
18 | });
19 | }, { block: 'b', mix: 'test' },
20 | 'test');
21 | });
22 |
23 | it('apply(\'tag\') from tag()', function() {
24 | test(function() {
25 | block('b').tag()(function() {
26 | return 'span';
27 | });
28 | block('b').tag()(function() {
29 | return apply('tag');
30 | });
31 | }, { block: 'b', tag: 'a' },
32 | '');
33 | });
34 |
35 | it('apply(\'tag\') from def()', function() {
36 | test(function() {
37 | block('b').def()(function() {
38 | return apply('tag');
39 | });
40 | }, { block: 'b', tag: 'a' },
41 | 'a');
42 | });
43 |
44 | it('apply(\'bem\') from def()', function() {
45 | test(function() {
46 | block('b').def()(function() {
47 | return apply('bem');
48 | });
49 | }, { block: 'b', bem: false },
50 | false);
51 | });
52 |
53 | it('apply(\'cls\') from def()', function() {
54 | test(function() {
55 | block('b').def()(function() {
56 | return apply('cls');
57 | });
58 | }, { block: 'b', cls: 'test' },
59 | 'test');
60 | });
61 |
62 | it('apply(\'attrs\') from def()', function() {
63 | test(function() {
64 | block('b').def()(function() {
65 | return apply('attrs');
66 | });
67 | }, { block: 'b', attrs: { target: '_blank' } },
68 | { target: '_blank' });
69 | });
70 |
71 | it('apply(\'js\') from def()', function() {
72 | test(function() {
73 | block('b').def()(function() {
74 | return apply('js');
75 | });
76 | }, { block: 'b', js: { data: 'test' } },
77 | { data: 'test' });
78 | });
79 |
80 | it('apply(\'usermode\') from def()', function() {
81 | test(function() {
82 | block('b').def()(function() {
83 | return apply('usermode');
84 | });
85 | }, { block: 'b', usermode: 'test' },
86 | 'test');
87 | });
88 |
89 | it('apply(\'undefusermode\') from def()', function() {
90 | test(function() {
91 | block('b').def()(function() {
92 | return apply('undefusermode');
93 | });
94 | }, { block: 'b' },
95 | undefined);
96 | });
97 |
98 | it('apply(\'\') should lookup field from bemjson by default', function() {
99 | test(function() {
100 | block('b').def()(function() {
101 | return apply('test');
102 | });
103 | }, { block: 'b', test: 'bemjson' },
104 | 'bemjson');
105 | });
106 | });
107 |
--------------------------------------------------------------------------------