2021-02-01 13:55:37 +01:00
|
|
|
const HELPER_LIBRARY = "@budibase/handlebars-helpers"
|
|
|
|
const helpers = require(HELPER_LIBRARY)
|
2021-01-27 19:09:04 +01:00
|
|
|
const { HelperFunctionBuiltin } = require("../src/helpers/constants")
|
|
|
|
const fs = require("fs")
|
|
|
|
const doctrine = require("doctrine")
|
2021-02-01 13:55:37 +01:00
|
|
|
const marked = require("marked")
|
2021-01-27 19:09:04 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* full list of supported helpers can be found here:
|
2021-02-04 11:17:10 +01:00
|
|
|
* https://github.com/budibase/handlebars-helpers
|
2021-01-27 19:09:04 +01:00
|
|
|
*/
|
2023-07-03 13:07:45 +02:00
|
|
|
const { join } = require("path")
|
2024-01-30 15:12:09 +01:00
|
|
|
const path = require("path")
|
2023-11-20 21:52:29 +01:00
|
|
|
|
2021-09-15 20:02:44 +02:00
|
|
|
const COLLECTIONS = [
|
|
|
|
"math",
|
|
|
|
"array",
|
|
|
|
"number",
|
|
|
|
"url",
|
|
|
|
"string",
|
|
|
|
"comparison",
|
|
|
|
"object",
|
2023-12-13 18:56:16 +01:00
|
|
|
"uuid",
|
2021-09-15 20:02:44 +02:00
|
|
|
]
|
2023-07-03 13:07:45 +02:00
|
|
|
const FILENAME = join(__dirname, "..", "manifest.json")
|
2021-01-27 19:09:04 +01:00
|
|
|
const outputJSON = {}
|
2021-02-04 11:17:10 +01:00
|
|
|
const ADDED_HELPERS = {
|
|
|
|
date: {
|
|
|
|
date: {
|
|
|
|
args: ["datetime", "format"],
|
|
|
|
numArgs: 2,
|
2021-06-09 12:50:00 +02:00
|
|
|
example: '{{date now "DD-MM-YYYY" "America/New_York" }} -> 21-01-2021',
|
2021-06-09 12:52:26 +02:00
|
|
|
description:
|
|
|
|
"Format a date using moment.js date formatting - the timezone is optional and uses the tz database.",
|
2021-02-04 11:17:10 +01:00
|
|
|
},
|
|
|
|
duration: {
|
|
|
|
args: ["time", "durationType"],
|
|
|
|
numArgs: 2,
|
2024-01-22 12:16:59 +01:00
|
|
|
example: '{{duration 8 "seconds"}} -> a few seconds',
|
2021-02-04 11:25:04 +01:00
|
|
|
description:
|
|
|
|
"Produce a humanized duration left/until given an amount of time and the type of time measurement.",
|
|
|
|
},
|
|
|
|
},
|
2021-02-04 11:17:10 +01:00
|
|
|
}
|
2021-01-27 19:09:04 +01:00
|
|
|
|
|
|
|
function fixSpecialCases(name, obj) {
|
|
|
|
const args = obj.args
|
|
|
|
if (name === "ifNth") {
|
|
|
|
args[0] = "a"
|
|
|
|
args[1] = "b"
|
|
|
|
}
|
|
|
|
if (name === "eachIndex") {
|
|
|
|
obj.description = "Iterates the array, listing an item and the index of it."
|
|
|
|
}
|
|
|
|
if (name === "toFloat") {
|
|
|
|
obj.description = "Convert input to a float."
|
|
|
|
}
|
|
|
|
if (name === "toInt") {
|
|
|
|
obj.description = "Convert input to an integer."
|
|
|
|
}
|
|
|
|
return obj
|
|
|
|
}
|
|
|
|
|
|
|
|
function lookForward(lines, funcLines, idx) {
|
|
|
|
const funcLen = funcLines.length
|
|
|
|
for (let i = idx, j = 0; i < idx + funcLen; ++i, j++) {
|
|
|
|
if (!lines[i].includes(funcLines[j])) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
function getCommentInfo(file, func) {
|
|
|
|
const lines = file.split("\n")
|
|
|
|
const funcLines = func.split("\n")
|
|
|
|
let comment = null
|
|
|
|
for (let idx = 0; idx < lines.length; ++idx) {
|
|
|
|
// from here work back until we have the comment
|
|
|
|
if (lookForward(lines, funcLines, idx)) {
|
|
|
|
let fromIdx = idx
|
2021-01-27 19:09:32 +01:00
|
|
|
let start = 0,
|
|
|
|
end = 0
|
2021-01-27 19:09:04 +01:00
|
|
|
do {
|
|
|
|
if (lines[fromIdx].includes("*/")) {
|
|
|
|
end = fromIdx
|
|
|
|
} else if (lines[fromIdx].includes("/*")) {
|
|
|
|
start = fromIdx
|
|
|
|
}
|
|
|
|
if (start && end) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
fromIdx--
|
|
|
|
} while (fromIdx > 0)
|
|
|
|
comment = lines.slice(start, end + 1).join("\n")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (comment == null) {
|
|
|
|
return { description: "" }
|
|
|
|
}
|
|
|
|
const docs = doctrine.parse(comment, { unwrap: true })
|
|
|
|
// some hacky fixes
|
|
|
|
docs.description = docs.description.replace(/\n/g, " ")
|
|
|
|
docs.description = docs.description.replace(/[ ]{2,}/g, " ")
|
|
|
|
docs.description = docs.description.replace(/is is/g, "is")
|
2021-02-01 18:12:18 +01:00
|
|
|
const examples = docs.tags
|
2021-05-04 12:32:22 +02:00
|
|
|
.filter(el => el.title === "example")
|
|
|
|
.map(el => el.description)
|
2021-02-01 18:12:18 +01:00
|
|
|
const blocks = docs.description.split("```")
|
|
|
|
if (examples.length > 0) {
|
|
|
|
docs.example = examples.join(" ")
|
2021-01-27 19:09:04 +01:00
|
|
|
}
|
2022-07-29 15:12:04 +02:00
|
|
|
// hacky example fix
|
|
|
|
if (docs.example && docs.example.includes("product")) {
|
|
|
|
docs.example = docs.example.replace("product", "multiply")
|
|
|
|
}
|
2021-02-01 18:12:18 +01:00
|
|
|
docs.description = blocks[0].trim()
|
2024-01-30 15:49:18 +01:00
|
|
|
docs.requiresBlock = docs.tags.some(el => el.title === "block")
|
2021-01-27 19:09:04 +01:00
|
|
|
return docs
|
|
|
|
}
|
|
|
|
|
2024-01-23 16:12:53 +01:00
|
|
|
const excludeFunctions = { string: ["raw"] }
|
|
|
|
|
2021-01-27 19:09:04 +01:00
|
|
|
/**
|
|
|
|
* This script is very specific to purpose, parsing the handlebars-helpers files to attempt to get information about them.
|
|
|
|
*/
|
|
|
|
function run() {
|
|
|
|
const foundNames = []
|
|
|
|
for (let collection of COLLECTIONS) {
|
2021-01-27 19:09:32 +01:00
|
|
|
const collectionFile = fs.readFileSync(
|
2024-01-30 15:12:09 +01:00
|
|
|
`${path.dirname(require.resolve(HELPER_LIBRARY))}/lib/${collection}.js`,
|
2021-01-27 19:09:32 +01:00
|
|
|
"utf8"
|
|
|
|
)
|
2021-01-27 19:09:04 +01:00
|
|
|
const collectionInfo = {}
|
|
|
|
// collect information about helper
|
|
|
|
let hbsHelperInfo = helpers[collection]()
|
|
|
|
for (let entry of Object.entries(hbsHelperInfo)) {
|
|
|
|
const name = entry[0]
|
|
|
|
// skip built in functions and ones seen already
|
|
|
|
if (
|
|
|
|
HelperFunctionBuiltin.indexOf(name) !== -1 ||
|
2024-01-23 16:12:53 +01:00
|
|
|
foundNames.indexOf(name) !== -1 ||
|
|
|
|
excludeFunctions[collection]?.includes(name)
|
2021-01-27 19:09:04 +01:00
|
|
|
) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
foundNames.push(name)
|
|
|
|
// this is ridiculous, but it parse the function header
|
|
|
|
const fnc = entry[1].toString()
|
|
|
|
const jsDocInfo = getCommentInfo(collectionFile, fnc)
|
|
|
|
let args = jsDocInfo.tags
|
2021-05-04 12:32:22 +02:00
|
|
|
.filter(tag => tag.title === "param")
|
2021-01-27 19:09:32 +01:00
|
|
|
.map(
|
2021-05-04 12:32:22 +02:00
|
|
|
tag =>
|
2021-01-27 19:09:32 +01:00
|
|
|
tag.description &&
|
2021-05-03 09:31:09 +02:00
|
|
|
tag.description.replace(/`/g, "").split(" ")[0].trim()
|
2021-01-27 19:09:32 +01:00
|
|
|
)
|
2021-01-27 19:09:04 +01:00
|
|
|
collectionInfo[name] = fixSpecialCases(name, {
|
|
|
|
args,
|
|
|
|
numArgs: args.length,
|
|
|
|
example: jsDocInfo.example || undefined,
|
|
|
|
description: jsDocInfo.description,
|
2024-01-30 15:49:18 +01:00
|
|
|
requiresBlock: jsDocInfo.requiresBlock,
|
2021-01-27 19:09:04 +01:00
|
|
|
})
|
|
|
|
}
|
|
|
|
outputJSON[collection] = collectionInfo
|
|
|
|
}
|
2021-02-04 11:17:10 +01:00
|
|
|
// add extra helpers
|
|
|
|
for (let [collectionName, collection] of Object.entries(ADDED_HELPERS)) {
|
|
|
|
let input = collection
|
|
|
|
if (outputJSON[collectionName]) {
|
|
|
|
input = Object.assign(outputJSON[collectionName], collection)
|
|
|
|
}
|
|
|
|
outputJSON[collectionName] = input
|
2021-01-27 19:09:04 +01:00
|
|
|
}
|
2021-02-04 11:17:10 +01:00
|
|
|
|
2021-02-01 13:55:37 +01:00
|
|
|
// convert all markdown to HTML
|
|
|
|
for (let collection of Object.values(outputJSON)) {
|
|
|
|
for (let helper of Object.values(collection)) {
|
2022-07-29 15:12:04 +02:00
|
|
|
helper.description = marked.parse(helper.description)
|
2021-02-01 13:55:37 +01:00
|
|
|
}
|
|
|
|
}
|
2021-01-27 19:09:04 +01:00
|
|
|
fs.writeFileSync(FILENAME, JSON.stringify(outputJSON, null, 2))
|
|
|
|
}
|
|
|
|
|
2021-01-27 19:09:32 +01:00
|
|
|
run()
|