bem-xjst
Version:
Declarative Template Engine for the browser and server
350 lines (295 loc) • 9.5 kB
JavaScript
module.exports = function(match, block, elem, mod, elemMod, oninit, xjstOptions, wrap, replace, extend, mode, def, content, appendContent, prependContent, attrs, addAttrs, js, addJs, mix, addMix, mods, addMods, addElemMods, elemMods, tag, cls, bem, local, applyCtx, applyNext, apply) {
var stringify = function(v) {
var cache = [];
return JSON.stringify(v, function(key, value) {
if (typeof value === 'object' && value !== null) {
if (cache.indexOf(value) !== -1) {
// Circular reference found, discard key
return;
}
cache.push(value);
}
return value;
});
};
var collectMixes = function collectMixes(item, res, context) {
res = res || [];
if (!item)
return res;
context = context || {
block: item.block,
elem: item.elem,
mods: item.mods,
elemMods: item.elemMods
};
if (item.block || item.mods) {
res.push({
block: item.block || context.block,
mods: item.mods,
js: item.js
});
if (item.block)
context = { block: item.block, mods: item.mods };
}
if (item.elem || (item.elemMods && context.elem)) {
res.push({
block: item.block || context.block,
elem: item.elem || context.elem,
elemMods: item.elemMods,
js: item.js
});
if (item.elem)
context = {
block: item.block || context.block,
mods: item.mods || context.mods,
elem: item.elem,
elemMods: item.elemMods
};
}
if (typeof item === 'string')
res.push({ block: item });
if (item.mix) {
if (!Array.isArray(item.mix)) item.mix = [ item.mix ];
item.mix.map(function(mix) { return collectMixes(mix, res, context); });
}
return res;
};
var checkMixes = function checkMixes(mix, ctx, mixesFromTmpls) {
if (mix.length) {
var hash = {};
mix.forEach(function(mixItem) {
if (!mixItem.elem) {
if (!hash[mixItem.block])
hash[mixItem.block] = {};
if (mixItem.mods) {
Object.keys(mixItem.mods).forEach(function(modName) {
var b = hash[mixItem.block];
if (!b[modName]) {
b[modName] = true;
} else {
console.warn(
'\nBEM-XJST WARNING: you’re trying to mix block with mods to the same block with the same mods. ' +
'\nctx: ' + stringify(ctx) +
(mixesFromTmpls ? '\nmixes from templates: ' + stringify(mixesFromTmpls) : '')
);
}
});
}
} else {
var key = mixItem.block + '__' + mixItem.elem;
if (!hash[key])
hash[key] = {};
if (mixItem.elemMods) {
Object.keys(mixItem.elemMods).forEach(function(modName) {
var b = hash[key];
if (!b[modName]) {
b[modName] = true;
} else {
console.warn(
'\nBEM-XJST WARNING: you’re trying to mix block with mods to the same block with the same mods. ' +
'\nctx: ' + stringify(ctx) +
(mixesFromTmpls ? '\nmixes from templates: ' + stringify(mixesFromTmpls) : '')
);
}
});
}
}
});
}
};
// true/false in attributes
block('*')(
def()(function() {
var ret = applyNext();
var ctx = this.ctx;
var attrs = ctx.attrs;
if (attrs) {
Object.keys(attrs).forEach(function(key) {
if (typeof attrs[key] === 'boolean') {
console.warn(
'\nBEM-XJST WARNING: boolean attribute value: ' +
attrs[key] +
' in BEMJSON: ' +
stringify(ctx)
);
console.warn('Notice what bem-xjst behaviour changed: https://github.com/bem/bem-xjst/releases/tag/v4.3.3');
}
});
}
return ret;
}),
// mods with elem (instead of elemMods)
def()(function() {
var ret = applyNext();
var ctx = this.ctx;
if (ctx.mods && ctx.elem && !ctx.elemMods) {
console.warn(
'\nBEM-XJST WARNING: mods for elem in BEMJSON: ' +
stringify(ctx)
);
console.warn('Notice what bem-xjst behaviour changed: https://github.com/bem/bem-xjst/releases/tag/v5.0.0');
}
return ret;
}),
mode('check-attrs')(function() {
var ctx = this.ctx;
var attrs = this.check;
if (attrs.class) {
console.warn(
'\nBEM-XJST WARNING: looks like you’re trying to set HTML class from attrs field in BEMJSON. ' +
'Please use cls() mode for it. See documentation: https://github.com/bem/bem-xjst/blob/master/docs/en/5-templates-syntax.md#cls' +
'\nctx: ' + stringify(ctx) +
'\nattrs: ' + stringify(attrs)
);
}
if (attrs['data-bem']) {
console.warn(
'\nBEM-XJST WARNING: looks like you’re trying to set data-bem attribute from attrs field in BEMJSON. ' +
'Please use js() mode for it. See documentation: https://github.com/bem/bem-xjst/blob/master/docs/en/5-templates-syntax.md#js' +
'\nctx: ' + stringify(ctx) +
'\nattrs: ' + stringify(attrs)
);
}
}),
attrs()(function() {
var attrs = applyNext();
if (attrs)
apply('check-attrs', { check: attrs });
return attrs;
}),
def()(function() {
if (this.ctx.attrs)
apply('check-attrs', { check: this.ctx.attrs });
return applyNext();
}),
def()(function() {
var ctx = this.extend({}, this.ctx);
var mix = collectMixes(ctx, []);
if (ctx.mods || ctx.elemMods)
checkMixes(mix, ctx);
return applyNext();
}),
mix()(function() {
var ctx = this.extend({}, this.ctx);
var mixesFromTmpls = applyNext();
var mix;
if (!this.elem) {
mix = collectMixes({
block: this.block,
mods: this.mods,
mix: mixesFromTmpls
}, []);
} else {
mix = collectMixes({
block: this.block,
elem: this.elem,
elemMods: this.elemMods,
mix: mixesFromTmpls
}, []);
}
if (mixesFromTmpls && mixesFromTmpls.length)
checkMixes(mix, ctx, mixesFromTmpls);
return applyNext();
}),
// Check naming:
def()(function() {
var _this = this;
var cb = this._bemxjst.classBuilder;
var ctx = this.ctx;
var check = function check(str) {
if (!str)
return;
str = String(str);
return str.indexOf(cb.modDelim) !== -1 ||
str.indexOf(cb.elemDelim) !== -1;
};
if (check(this.block)) {
console.warn(
'\nBEM-XJST WARNING: wrong block name. ' +
'\nBlock name can not contain modifier delimeter nor elem delimeter. ' +
'\nblock: ' + this.block +
'\nctx: ' + stringify(ctx)
);
}
if (check(this.elem)) {
console.warn(
'\nBEM-XJST WARNING: wrong elem name. ' +
'\nElement name can not contain modifier delimeter nor elem delimeter. ' +
'\nelem: ' + this.elem +
'\nctx: ' + stringify(ctx)
);
}
Object.keys(_this.mods).forEach(function(modName) {
// modName
if (check(modName)) {
console.warn(
'\nBEM-XJST WARNING: wrong modifier name. ' +
'\nModifier name can not contain modifier delimeter nor elem delimeter. ' +
'\nmods: ' + stringify(_this.mods) +
'\nctx: ' + stringify(ctx)
);
}
// modVal
if (check(_this.mods[modName])) {
console.warn(
'\nBEM-XJST WARNING: wrong modifier value. ' +
'\nModifier value can not contain modifier delimeter nor elem delimeter. ' +
'\nmods: ' + stringify(_this.mods) +
'\nctx: ' + stringify(ctx)
);
}
});
// elemMods
Object.keys(_this.elemMods).forEach(function(modName) {
// modName
if (check(modName)) {
console.warn(
'\nBEM-XJST WARNING: wrong element modifier name. ' +
'\nModifier name can not contain modifier delimeter nor elem delimeter. ' +
'\nelemMods: ' + stringify(_this.elemMods) +
'\nctx: ' + stringify(ctx)
);
}
// modVal
if (check(_this.elemMods[modName])) {
console.warn(
'\nBEM-XJST WARNING: wrong element modifier value. ' +
'\nModifier value can not contain modifier delimeter nor elem delimeter. ' +
'\nelemMods: ' + stringify(_this.elemMods) +
'\nctx: ' + stringify(ctx)
);
}
});
return applyNext();
}),
def()(function(node, ctx) {
if (ctx.mods) {
var mods = ctx.mods;
Object.keys(mods).forEach(function(mod) {
var val = mods[mod];
if (Array.isArray(val)) {
console.warn(
'\nBEM-XJST WARNING: wrong modifier value. ' +
'\nModifier value can be undefined, null, String, Number or Boolean. ' +
'\nctx: ' + stringify(ctx)
);
}
});
}
if (ctx.elemMods) {
var elemMods = ctx.elemMods;
Object.keys(elemMods).forEach(function(mod) {
var val = elemMods[mod];
if (Array.isArray(val)) {
console.warn(
'\nBEM-XJST WARNING: wrong element modifier value. ' +
'\nModifier value can be undefined, null, String, Number or Boolean. ' +
'\nctx: ' + stringify(ctx)
);
}
});
}
return applyNext();
})
);
};