@lbu/stdlib
Advanced tools
Comparing version 0.0.9 to 0.0.10
@@ -16,5 +16,6 @@ export { uuid } from "./src/datatypes.js"; | ||
export { | ||
addToTemplateContext, | ||
newTemplateContext, | ||
compileTemplate, | ||
compileTemplateDirectory, | ||
compileTemplateDirectorySync, | ||
executeTemplate, | ||
@@ -21,0 +22,0 @@ } from "./src/template.js"; |
{ | ||
"name": "@lbu/stdlib", | ||
"version": "0.0.9", | ||
"version": "0.0.10", | ||
"description": "All kinds of utility functions", | ||
"main": "index.js", | ||
"main": "./index.js", | ||
"exports": "./index.js", | ||
"type": "module", | ||
@@ -15,6 +16,6 @@ "keywords": [ | ||
"dependencies": { | ||
"@lbu/insight": "^0.0.9", | ||
"@lbu/insight": "^0.0.10", | ||
"dotenv": "8.2.0", | ||
"lodash.merge": "4.6.2", | ||
"uuid": "7.0.2" | ||
"uuid": "7.0.3" | ||
}, | ||
@@ -33,5 +34,5 @@ "author": { | ||
"engines": { | ||
"node": ">=12" | ||
"node": ">=14" | ||
}, | ||
"gitHead": "d847630e049071c7c2385eef8377ba976ddd0e2a" | ||
"gitHead": "35e227dd5217c7eda76fcc69adaabb19207e3008" | ||
} |
@@ -21,3 +21,3 @@ # @lbu/stdlib | ||
- Various utilities like loading .env files, executing other processes and a | ||
basic template system | ||
basic string templating system | ||
@@ -24,0 +24,0 @@ ## Docs |
import uuidPkg from "uuid"; | ||
/** | ||
* Return a new uuid v4 | ||
* @function | ||
@@ -5,0 +6,0 @@ * @return {string} |
@@ -36,2 +36,9 @@ import { exec as cpExec, spawn as cpSpawn } from "child_process"; | ||
/** | ||
* @typedef {object} ProcessDirectoryOptions | ||
* @property {boolean} [skipNodeModules] Skip node_modules directory, true by default | ||
* @property {boolean} [skipDotFiles] Skip files and directories starting with a '.', true | ||
* by default | ||
*/ | ||
/** | ||
* Recursively walks directory async and calls cb on all files. | ||
@@ -41,5 +48,3 @@ * By default skips node_modules and files starting with a dot | ||
* @param {Function} cb | ||
* @param {Object} [opts={}] | ||
* @param {boolean} [opts.skipNodeModules=true] | ||
* @param {boolean} [opts.skipDotFiles=true] | ||
* @param {ProcessDirectoryOptions} [opts] | ||
*/ | ||
@@ -73,3 +78,3 @@ export async function processDirectoryRecursive( | ||
/** | ||
* | ||
* Sync version of processDirectoryRecursive | ||
* @param {string} dir | ||
@@ -76,0 +81,0 @@ * @param {Function} cb |
@@ -1,5 +0,8 @@ | ||
import { promises } from "fs"; | ||
import { promises, readFileSync } from "fs"; | ||
import path from "path"; | ||
import { isNil } from "./lodash.js"; | ||
import { processDirectoryRecursive } from "./node.js"; | ||
import { | ||
processDirectoryRecursive, | ||
processDirectoryRecursiveSync, | ||
} from "./node.js"; | ||
@@ -9,29 +12,41 @@ const { readFile } = promises; | ||
/** | ||
* Global context for template execution | ||
* @typedef {object} TemplateContext | ||
* @property {object<string, function>} globals | ||
* @property {Map<string, function>} templates | ||
*/ | ||
const templateContext = { | ||
isNil, | ||
quote: (it) => `"${it}"`, | ||
singleQuote: (it) => `'${it}'`, | ||
}; | ||
/** | ||
* Global store for all templates | ||
* @return {TemplateContext} | ||
*/ | ||
const templateStore = new Map(); | ||
export function newTemplateContext() { | ||
return { | ||
globals: { | ||
isNil, | ||
}, | ||
templates: new Map(), | ||
}; | ||
} | ||
/** | ||
* Simple template support | ||
* Unsafe for not trusted inputs | ||
* Compile templates add to TemplateContext | ||
* Unsafe for untrusted inputs | ||
* Fields need to be explicitly set to undefined or access them via `it.field` | ||
* Inspired by: | ||
* https://johnresig.com/blog/javascript-micro-templating/ | ||
* Other known templates and globals will be available when executing | ||
* Inspired by: https://johnresig.com/blog/javascript-micro-templating/ | ||
* | ||
* @param {TemplateContext} tc | ||
* @param {string} name Name that is exposed in the template it self and to be used with | ||
* the executeTemplate function | ||
* @param {string} str Template string | ||
* @param {Object} [opts={}] | ||
* @param {boolean} opts.debug Set to true to print context keys and input object before | ||
* @param {object} [opts={}] | ||
* @param {boolean} [opts.debug] Set to true to print context keys and input object before | ||
* executing the template | ||
*/ | ||
export function compileTemplate(name, str, opts = {}) { | ||
export function compileTemplate(tc, name, str, opts = {}) { | ||
if (isNil(tc)) { | ||
throw new TypeError( | ||
"TemplateContext is required, please create a new one with `newTemplateContext()`", | ||
); | ||
} | ||
if (isNil(name) || isNil(str)) { | ||
@@ -41,2 +56,6 @@ throw new TypeError("Both name and string are required"); | ||
if (tc.templates.has(name)) { | ||
throw new TypeError(`Template with name ${name} already registered`); | ||
} | ||
const compiled = str | ||
@@ -67,3 +86,3 @@ .split("\n") | ||
try { | ||
templateStore.set( | ||
tc.templates.set( | ||
name, | ||
@@ -104,24 +123,84 @@ new Function( | ||
export function compileTemplateDirectory(dir, extension, opts) { | ||
/** | ||
* Compile all templates found in the provided directory with the provided extension | ||
* | ||
* @param {TemplateContext} tc | ||
* @param {string} dir | ||
* @param {string} extension | ||
* @param {ProcessDirectoryOptions} [opts] | ||
* @returns {Promise<void>} | ||
*/ | ||
export function compileTemplateDirectory(tc, dir, extension, opts) { | ||
if (isNil(tc)) { | ||
throw new TypeError( | ||
"TemplateContext is required, please create a new one with `newTemplateContext()`", | ||
); | ||
} | ||
const ext = extension[0] !== "." ? `.${extension}` : extension; | ||
return processDirectoryRecursive(dir, async (file) => { | ||
if (!file.endsWith(ext)) { | ||
return; | ||
} | ||
return processDirectoryRecursive( | ||
dir, | ||
async (file) => { | ||
if (!file.endsWith(ext)) { | ||
return; | ||
} | ||
const content = await readFile(file, { encoding: "utf-8" }); | ||
const name = path.parse(file).name; | ||
const content = await readFile(file, { encoding: "utf-8" }); | ||
const name = path.parse(file).name; | ||
compileTemplate(name, content, opts); | ||
}); | ||
compileTemplate(tc, name, content); | ||
}, | ||
opts, | ||
); | ||
} | ||
/** | ||
* Sync version of compileTemplateDirectory | ||
* | ||
* @param {TemplateContext} tc | ||
* @param {string} dir | ||
* @param {string} extension | ||
* @param {ProcessDirectoryOptions} [opts] | ||
* @returns {void} | ||
*/ | ||
export function compileTemplateDirectorySync(tc, dir, extension, opts) { | ||
if (isNil(tc)) { | ||
throw new TypeError( | ||
"TemplateContext is required, please create a new one with `newTemplateContext()`", | ||
); | ||
} | ||
const ext = extension[0] !== "." ? `.${extension}` : extension; | ||
return processDirectoryRecursiveSync( | ||
dir, | ||
async (file) => { | ||
if (!file.endsWith(ext)) { | ||
return; | ||
} | ||
const content = readFileSync(file, { encoding: "utf-8" }); | ||
const name = path.parse(file).name; | ||
compileTemplate(tc, name, content); | ||
}, | ||
opts, | ||
); | ||
} | ||
/** | ||
* Execute a template, template should be compiled using compileTemplate | ||
* @param name | ||
* @param data | ||
* | ||
* @param {TemplateContext} tc | ||
* @param {string} name | ||
* @param {*} data | ||
* @returns {string} The resulting string for executing the template | ||
*/ | ||
export function executeTemplate(name, data) { | ||
if (!templateStore.has(name)) { | ||
export function executeTemplate(tc, name, data) { | ||
if (isNil(tc)) { | ||
throw new TypeError( | ||
"TemplateContext is required, please create a new one with `newTemplateContext()`", | ||
); | ||
} | ||
if (!tc.templates.has(name)) { | ||
throw new Error(`Unknown template: ${name}`); | ||
@@ -131,3 +210,3 @@ } | ||
try { | ||
return templateStore.get(name)(getExecutionContext(), data).trim(); | ||
return tc.templates.get(name)(getExecutionContext(tc), data).trim(); | ||
} catch (e) { | ||
@@ -141,16 +220,11 @@ const err = new Error(`Error while executing ${name} template`); | ||
/** | ||
* Simply add an item to the Context dictionary, note name can overwrite or be | ||
* overwritten by a template or other context value | ||
* @param name | ||
* @param value | ||
* Combine globals and registered templates into a single object | ||
* | ||
* @param {TemplateContext} tc | ||
*/ | ||
export function addToTemplateContext(name, value) { | ||
templateContext[name] = value; | ||
} | ||
function getExecutionContext() { | ||
function getExecutionContext(tc) { | ||
const result = { | ||
...templateContext, | ||
...tc.globals, | ||
}; | ||
for (const [key, item] of templateStore.entries()) { | ||
for (const [key, item] of tc.templates.entries()) { | ||
result[key] = item.bind(undefined, result); | ||
@@ -157,0 +231,0 @@ } |
@@ -9,2 +9,6 @@ import dotenv from "dotenv"; | ||
/** | ||
* Return seconds since unix epoch | ||
* @returns {number} | ||
*/ | ||
export function getSecondsSinceEpoch() { | ||
@@ -22,2 +26,3 @@ return Math.floor(Date.now() / 1000); | ||
/** | ||
* HACKY | ||
* Let V8 know to please run the garbage collector. | ||
@@ -24,0 +29,0 @@ */ |
16353
509
+ Added@lbu/insight@0.0.10(transitive)
+ Addeduuid@7.0.3(transitive)
- Removed@lbu/insight@0.0.9(transitive)
- Removeduuid@7.0.2(transitive)
Updated@lbu/insight@^0.0.10
Updateduuid@7.0.3