Skip to content
This repository has been archived by the owner on Jun 25, 2024. It is now read-only.

Support handlebars >=2 <=4 #5

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .babelrc

This file was deleted.

15 changes: 0 additions & 15 deletions .eslintrc

This file was deleted.

12 changes: 2 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
[![wercker status](https://app.wercker.com/status/0d5d25d4c12cbdfc395f363a2684a6a4/s/master "wercker status")](https://app.wercker.com/project/byKey/0d5d25d4c12cbdfc395f363a2684a6a4)

# communibase-template-data-factory

Enrich Communibase-data in any way possible for easy use in dynamic templates.
Expand Down Expand Up @@ -28,13 +30,3 @@ __stxt__: a Javascript-object containing optional translations, e.g.:

Extra (custom) serializers can be added to the factory using the `addSerializers`-method. See serializers in the
`entityType`-folder for examples and implementations

## Debugging?

See runTest.js line 44, attach a debugger from the IDE to mocha

### PLEASE NOTE ###

This library is compatible with Handlebars 2.0.0

Later versions of Handlebars have a different AST and do not function properly!
33 changes: 29 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "communibase-template-data-factory",
"version": "2.0.13",
"version": "3.0.0",
"description": "Helper methods for working with Templates and the Communibase service.",
"scripts": {
"test": "mocha $NODE_DEBUG_OPTION test/tests/**/*.js",
Expand All @@ -20,25 +20,50 @@
"dependencies": {
"bluebird": "^3.5.1",
"communibase-connector-js": "^0.5.16",
"handlebars": "^2.0.0",
"handlebars": ">=2.0.0 <= 4",
"lodash": "^4.17.4",
"moment": "^2.19.2"
},
"devDependencies": {
"Communibase": "git+ssh://[email protected]/communibase/api.git",
"babel-cli": "^6.26.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-env": "^1.7.0",
"bson-stream": "0.0.8",
"eslint": "^5.9.0",
"eslint-config-kingsquare": "^5.0.5",
"grunt": "^1.0.1",
"grunt-shell": "^2.1.0",
"handlebars-v2": "npm:[email protected]",
"handlebars-v3": "npm:[email protected]",
"handlebars-v4": "npm:[email protected]",
"kingsquare-handlebars-helpers": "^1.1.8",
"mocha": "^5.2.0",
"mongodb": "^2.2.24",
"prettier": "^1.15.2"
},
"eslintConfig": {
"extends": "kingsquare",
"env": {
"browser": true,
"node": true
},
"globals": {
"App": false,
"Ext": false,
"config": false
},
"rules": {
"prefer-rest-params": [
0
]
}
},
"babel": {
"presets": [
"env"
]
},
"engines": {
"node": ">=8.3.0"
"node": "^8.3.0"
}
}
255 changes: 255 additions & 0 deletions src/inc/HandlebarsAST.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
const uniq = require("lodash/uniq");

const debug = false;

const isHelper = function(node) {
// In v2 we had helper hints in the AST (`node.isHelper`)..
if (typeof node.isHelper !== "undefined") {
return node.isHelper;
}
// in v4 this information is not available in the AST so we need to
// check this our self... To do this we use the following code taken from
// https://github.com/wycats/handlebars.js/blob/95d84badcae89aa72a6f1433b851304700320920/lib/handlebars/compiler/ast.js#L8
return (
node.type === "SubExpression" ||
((node.type === "MustacheStatement" || node.type === "BlockStatement") &&
!!((node.params && node.params.length) || node.hash))
);
};

module.exports.getV2Paths = node => {
let result = [];

if (!node || !node.type) {
return result;
}

let blockKeys;
switch (node.type.toLowerCase()) {
// E.g. 'date' / 'debtor.debtorNumber'
case "id":
result.push(node.idName);
break;

case "program":
node.statements.forEach(statement => {
this.getV2Paths(statement).forEach(variable => {
result.push(variable);
});
});
break;

// E.g. #each / #if / #compare / '#invoiceItems' / '#ifIsCredit'
case "block":
blockKeys = this.getV2Paths(node.mustache);

if (
(!isHelper(node.mustache) || node.mustache.id.string === "each") &&
node.program
) {
this.getV2Paths(node.program).forEach(subValue => {
result.push(`${blockKeys[0]}.#.${subValue}`);
});
break;
}

if (node.mustache.id.string === "filter") {
// look for used properties in equation
[
node.mustache.params[1],
node.mustache.params[node.mustache.params.length === 4 ? 3 : 2]
].forEach(possiblePropertyNode => {
switch (possiblePropertyNode.type) {
case "STRING":
// e.g. ../session.personId
result.push(`${blockKeys[0]}.#.${possiblePropertyNode.string}`);
break;

case "ID":
// e.g. personId
result.push(possiblePropertyNode.string);
break;
}
});

this.getV2Paths(node.program).forEach(subValue => {
// indexes may change due to filtering: always request __all__ subValues
result.push(
subValue.replace(/^results\.(\d+|#)\./, `${blockKeys[0]}.#.`)
);
});
break;
}

result = blockKeys;

if (node.program) {
this.getV2Paths(node.program).forEach(variable => {
result.push(variable);
});
}
if (node.inverse) {
this.getV2Paths(node.inverse).forEach(variable => {
result.push(variable);
});
}
break;

// E.g. '{{#compare person.gender 'M'}}'
case "mustache":
if (!isHelper(node)) {
result.push(node.id.idName);
break;
}

node.params.forEach(param => {
this.getV2Paths(param).forEach(variable => {
result.push(variable);
});
});
break;
}

return result;
};

module.exports.getV4Paths = node => {
let result = [];
if (!node || !node.type) {
return result;
}
let blockKeys;
switch (node.type) {
case "Program":
node.body.forEach(item => {
this.getV4Paths(item).forEach(variable => {
result.push(variable);
});
});
break;
case "BlockStatement":
// const blockKeys = node.params.map(param => this.getV4Paths(param));
blockKeys = [];
node.params.forEach(param => {
this.getV4Paths(param).forEach(variable => {
blockKeys.push(variable);
});
});

if (
(!isHelper(node) || (node.path && node.path.original === "each")) &&
node.program
) {
this.getV4Paths(node.program).forEach(subValue => {
result.push(`${blockKeys[0]}.#.${subValue}`);
});
break;
}

if (node.path && node.path.original === "filter") {
// look for used properties in equation
[node.params[1], node.params[node.params.length === 4 ? 3 : 2]].forEach(
possiblePropertyNode => {
switch (possiblePropertyNode.type) {
case "StringLiteral":
// e.g. ../session.personId
result.push(
`${blockKeys[0]}.#.${possiblePropertyNode.original}`
);
break;

// @ TODO BooleanLiteral ?
// @ TODO the following
case "ID":
// e.g. personId
result.push(possiblePropertyNode.original);
break;
}
}
);

if (node.program) {
this.getV4Paths(node.program).forEach(subValue => {
// indexes may change due to filtering: always request __all__ subValues
result.push(
subValue.replace(/^results\.(\d+|#)\./, `${blockKeys[0]}.#.`)
);
});
}
break;
}

result = blockKeys;
// if (node.path) {
// result = this.getV4Paths(node.path);
// }

if (node.program) {
this.getV4Paths(node.program).forEach(variable => {
result.push(variable);
});
}

if (node.inverse) {
this.getV4Paths(node.inverse).forEach(variable => {
result.push(variable);
});
}
break;

case "MustacheStatement":
if (!isHelper(node)) {
result.push(node.path.original);
}

if (node.params) {
node.params.forEach(param => {
this.getV4Paths(param).forEach(variable => {
result.push(variable);
});
});
}
break;
case "PathExpression":
result.push(node.original);
break;
}
return result;
};

/**
* Gets all requested paths based on the given template. When inserting:
* {{invoiceNumber}} - {{#invoiceItems}} {{totalEx}} {{/invoiceItems}}
* It should return:
* ["invoiceNumber", "invoiceItems.#.totalEx"]
*
* This supports Handlebars >=2.0.0 <=4
*
* @param {object} node
*
* @returns {Array} result - The requested paths
*/
module.exports.getTemplatePaths = node => {
if (debug) {
// eslint-disable-next-line no-console
console.log(`${JSON.stringify(node)}\n\n`);
}

let paths = [];

// v2
if (node.statements) {
paths = this.getV2Paths(node);
}

// v3 / v4
if (node.body) {
paths = this.getV4Paths(node);
}

if (debug) {
// eslint-disable-next-line no-console
console.log(uniq(paths));
}
return uniq(paths);
};
Loading