@bedrockio/templates 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ## 0.1.0
2
+
3
+ - Initial commit
package/README.md ADDED
@@ -0,0 +1,167 @@
1
+ # @bedrockio/templates
2
+
3
+ This package provides a wrapper for Handlebars with additional features for
4
+ custom templating. It standardizes template rendering with support for front
5
+ matter, sections, and custom helpers.
6
+
7
+ - [Install](#install)
8
+ - [Usage](#usage)
9
+ - [Templates](#templates)
10
+ - [Sections](#sections)
11
+ - [Helpers](#helpers)
12
+ - [Default Helpers](#default-helpers)
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ yarn install @bedrockio/templates
18
+ ```
19
+
20
+ ## Usage
21
+
22
+ ```js
23
+ import { TemplateRenderer } from '@bedrockio/templates';
24
+
25
+ const renderer = new TemplateRenderer({
26
+ // Templates directory
27
+ dir: 'templates',
28
+
29
+ // Custom helpers (optional)
30
+ helpers: {
31
+ uppercase: (str) => str.toUpperCase(),
32
+ },
33
+
34
+ // Default params (optional)
35
+ params: {
36
+ foo: 'bar',
37
+ },
38
+ });
39
+
40
+ // Render a template
41
+ const result = renderer.run({
42
+ // The template string or path
43
+ template: 'Hello {{name}}!',
44
+
45
+ // Parameters to interpolate
46
+ params: {
47
+ name: 'World',
48
+ },
49
+ });
50
+
51
+ console.log(result.body); // "Hello World!"
52
+ ```
53
+
54
+ ## Templates
55
+
56
+ Templates use Handlebars syntax and support front matter for metadata:
57
+
58
+ ```
59
+ ---
60
+ title: My Title
61
+ ---
62
+
63
+ Hello, {{user.name}}!
64
+
65
+ {{#if user.isPremium}}
66
+ Welcome, premium user!
67
+ {{/if}}
68
+ ```
69
+
70
+ ## Sections
71
+
72
+ Templates can be divided into named sections using three equals signs: `===` as
73
+ delimiters:
74
+
75
+ ```
76
+ === SYSTEM ===
77
+
78
+ You are a helpful assistant.
79
+
80
+ === USER ===
81
+
82
+ I am the user!
83
+ ```
84
+
85
+ Sections are accessed in the result:
86
+
87
+ ```js
88
+ const { sections } = renderer.run({
89
+ template: 'my-template,
90
+ });
91
+
92
+ console.log(sections);
93
+
94
+ /*
95
+ [
96
+ {
97
+ name:'SYSTEM',
98
+ content: 'You are a helpful assistant.'
99
+ },
100
+ {
101
+ name:'USER',
102
+ content: 'I am the user!'
103
+ },
104
+ ]
105
+ */
106
+
107
+ ```
108
+
109
+ ## Helpers
110
+
111
+ The renderer includes default helpers and supports custom helpers. Custom
112
+ helpers can be passed during instantiation or per render call.
113
+
114
+ ```js
115
+ const renderer = new TemplateRenderer({
116
+ helpers: {
117
+ foo() {
118
+ return 'foo';
119
+ },
120
+ },
121
+ });
122
+ ```
123
+
124
+ ### Default Helpers
125
+
126
+ | Helper | Params | Example |
127
+ | -------------------- | ---------------------- | ----------------------------------------------------------- |
128
+ | **`date`** | | `2025-01-01` |
129
+ | **`dateLong`** | | `January 1, 2025` |
130
+ | **`dateMedium`** | | `Jan 1, 2025` |
131
+ | **`dateShort`** | | `1/1/2025` |
132
+ | **`time`** | | `7:00am` |
133
+ | **`time`** | `meridiem="caps"` | `2:00PM` |
134
+ | **`time`** | `meridiem="space"` | `2:00 pm` |
135
+ | **`time`** | `meridiem="period"` | `2:00 p.m.` |
136
+ | **`time`** | `meridiem="short"` | `2:00p` |
137
+ | **`timeLong`** | | `7:00:00am` |
138
+ | **`timeMedium`** | | `7:00am` |
139
+ | **`timeShort`** | | `7am` |
140
+ | **`timeZone`** | | `7:00am EST` |
141
+ | **`timeZone`** | `style="long"` | `7:00am Eastern Standard Time` |
142
+ | **`timeZone`** | `style="shortGeneric"` | `7:00am ET` |
143
+ | **`timeZone`** | `style="longGeneric"` | `7:00am Eastern Time` |
144
+ | **`dateTime`** | | `January 1, 2025 at 7:00am` |
145
+ | **`dateTimeLong`** | | `January 1, 2025 at 7:00am` |
146
+ | **`dateTimeMedium`** | | `Jan 1, 2025, 7:00am` |
147
+ | **`dateTimeShort`** | | `1/1/2025, 7:00am` |
148
+ | **`dateTimeZone`** | | `January 1, 2025 at 7:00am EST` |
149
+ | **`dateTimeZone`** | `style="long"` | `January 1, 2025 at 7:00am Eastern Standard Time` |
150
+ | **`dateTimeZone`** | `style="shortGeneric"` | `January 1, 2025 at 7:00am ET` |
151
+ | **`dateTimeZone`** | `style="longGeneric"` | `January 1, 2025 at 7:00am Eastern Time` |
152
+ | **`relTime`** | `date` | `6 months ago` |
153
+ | **`relTime`** | `date min=cutoff` | `January 1, 2025` |
154
+ | **`number`** | | `1. Frank` |
155
+ | **`link`** | `url text` | `[Hello](http://example.com)` |
156
+ | **`button`** | `url text` | `<a href="..." class="button" target="_blank">Click me</a>` |
157
+ | **`list`** | `arr` | `- one`<br>`- two`<br>`- three` |
158
+
159
+ **`relTime`** - Formats a date as relative time (e.g., "6 months ago"). When a
160
+ `min` or `max` cutoff date is provided, dates beyond that threshold will be
161
+ formatted as absolute dates instead of relative time.
162
+
163
+ **`number`** - Provides a 1-based index when used inside an `{{#each}}` loop.
164
+ Useful for creating numbered lists.
165
+
166
+ **`list`** - Converts an array into a markdown-formatted bullet list with each
167
+ item prefixed by `- `.
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const handlebars_1 = __importDefault(require("handlebars"));
7
+ const lodash_es_1 = require("lodash-es");
8
+ const helpers_1 = require("./helpers");
9
+ const utils_1 = require("./utils");
10
+ class TemplateRenderer {
11
+ constructor(options = {}) {
12
+ /** @type {Object} */
13
+ this.options = {
14
+ ...options,
15
+ helpers: {
16
+ ...helpers_1.DEFAULT_HELPERS,
17
+ ...options.helpers,
18
+ },
19
+ };
20
+ }
21
+ run(options) {
22
+ const { template, params, helpers, ...rest } = this.resolveOptions(options);
23
+ if (!template) {
24
+ return {
25
+ body: '',
26
+ sections: [],
27
+ };
28
+ }
29
+ const compiled = this.loadTemplate(template, rest);
30
+ return compiled(params, {
31
+ helpers: (0, helpers_1.resolveHelpers)(helpers, rest),
32
+ allowProtoPropertiesByDefault: true,
33
+ });
34
+ }
35
+ // Private
36
+ /** @returns {Object} */
37
+ resolveOptions(options = {}) {
38
+ return {
39
+ ...this.options,
40
+ ...options,
41
+ params: {
42
+ ...this.options.params,
43
+ ...options.params,
44
+ },
45
+ };
46
+ }
47
+ loadTemplate = (0, lodash_es_1.memoize)((input, options) => {
48
+ const source = (0, utils_1.resolveTemplateSource)(input, options);
49
+ const template = handlebars_1.default.compile(source.trim());
50
+ return (params, options) => {
51
+ const output = template(params, options);
52
+ const { body, meta } = (0, utils_1.runFrontMatter)(output);
53
+ const sections = (0, utils_1.getSections)(body);
54
+ return {
55
+ body,
56
+ meta,
57
+ sections,
58
+ };
59
+ };
60
+ });
61
+ }
62
+ exports.default = TemplateRenderer;
@@ -0,0 +1,189 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.DEFAULT_HELPERS = void 0;
7
+ exports.resolveHelpers = resolveHelpers;
8
+ const chrono_1 = require("@bedrockio/chrono");
9
+ const handlebars_1 = __importDefault(require("handlebars"));
10
+ exports.DEFAULT_HELPERS = {
11
+ // date
12
+ date(arg) {
13
+ return new chrono_1.DateTime(arg).toDate();
14
+ },
15
+ dateLong(arg) {
16
+ return new chrono_1.DateTime(arg).toDateLong();
17
+ },
18
+ dateMedium(arg) {
19
+ return new chrono_1.DateTime(arg).toDateMedium();
20
+ },
21
+ dateShort(arg) {
22
+ return new chrono_1.DateTime(arg).toDateShort();
23
+ },
24
+ // time
25
+ time(arg, meridiem) {
26
+ return new chrono_1.DateTime(arg).toTimeMedium({
27
+ meridiem,
28
+ });
29
+ },
30
+ /**
31
+ * @param {Intl.DateTimeFormatOptions['timeZoneName']} [style='short']
32
+ */
33
+ timeZone(arg, meridiem, style = 'short') {
34
+ return new chrono_1.DateTime(arg).toTimeWithZone({
35
+ meridiem,
36
+ timeZoneName: style,
37
+ });
38
+ },
39
+ timeLong(arg, meridiem) {
40
+ return new chrono_1.DateTime(arg).toTimeLong({
41
+ meridiem,
42
+ });
43
+ },
44
+ timeMedium(arg, meridiem) {
45
+ return new chrono_1.DateTime(arg).toTimeMedium({
46
+ meridiem,
47
+ });
48
+ },
49
+ timeShort(arg, meridiem) {
50
+ return new chrono_1.DateTime(arg).toTimeShort({
51
+ meridiem,
52
+ });
53
+ },
54
+ // datetime
55
+ dateTime(arg, meridiem) {
56
+ return new chrono_1.DateTime(arg).formatLong({
57
+ meridiem,
58
+ });
59
+ },
60
+ /**
61
+ * @param {Intl.DateTimeFormatOptions['timeZoneName']} [style='short']
62
+ */
63
+ dateTimeZone(arg, meridiem, style = 'short') {
64
+ return new chrono_1.DateTime(arg).formatWithZone({
65
+ meridiem,
66
+ timeZoneName: style,
67
+ });
68
+ },
69
+ dateTimeLong(arg, meridiem) {
70
+ return new chrono_1.DateTime(arg).formatLong({
71
+ meridiem,
72
+ });
73
+ },
74
+ dateTimeMedium(arg, meridiem) {
75
+ return new chrono_1.DateTime(arg).formatMedium({
76
+ meridiem,
77
+ });
78
+ },
79
+ dateTimeShort(arg, meridiem) {
80
+ return new chrono_1.DateTime(arg).formatShort({
81
+ meridiem,
82
+ });
83
+ },
84
+ // relative time
85
+ relTime(arg, min, max) {
86
+ return new chrono_1.DateTime(arg).relative({
87
+ min,
88
+ max,
89
+ });
90
+ },
91
+ number(options) {
92
+ const { index } = options.data;
93
+ if (index == null) {
94
+ return '';
95
+ }
96
+ return index + 1;
97
+ },
98
+ link(url, text) {
99
+ return new handlebars_1.default.SafeString(`[${text}](${url})`);
100
+ },
101
+ button(url, text) {
102
+ return generateHtml('a', {
103
+ text,
104
+ href: url,
105
+ class: 'button',
106
+ target: '_blank',
107
+ });
108
+ },
109
+ list(arr) {
110
+ return arr
111
+ .map((el) => {
112
+ return `- ${el}`;
113
+ })
114
+ .join('\n');
115
+ },
116
+ };
117
+ function resolveHelpers(helpers, options) {
118
+ const result = {};
119
+ for (let [key, value] of Object.entries(helpers)) {
120
+ result[key] = resolveHelper(value, options);
121
+ }
122
+ return result;
123
+ }
124
+ function resolveHelper(arg, options) {
125
+ const { names, handler } = resolveArgumentNames(arg);
126
+ return (...args) => {
127
+ return handler(...resolveHelperArgs(args, names, options));
128
+ };
129
+ }
130
+ // Arguments
131
+ const ARGUMENT_NAMES_REG = /\w+\((.+)\) {/;
132
+ function resolveArgumentNames(arg) {
133
+ if (typeof arg === 'function') {
134
+ const handler = arg;
135
+ const match = handler.toString().match(ARGUMENT_NAMES_REG);
136
+ let names = [];
137
+ if (match) {
138
+ names = match[1]
139
+ .split(', ')
140
+ .map((arg) => {
141
+ return arg.split(' ')[0];
142
+ })
143
+ .filter((arg) => {
144
+ return arg !== 'options';
145
+ });
146
+ }
147
+ return { handler, names };
148
+ }
149
+ else {
150
+ return {
151
+ names: arg.params,
152
+ handler: arg.handler,
153
+ };
154
+ }
155
+ }
156
+ function resolveHelperArgs(args, names, options) {
157
+ const ordered = args.slice(0, -1);
158
+ const [meta] = args.slice(-1);
159
+ let params = { ...meta.hash };
160
+ ordered.forEach((value, i) => {
161
+ const name = names[i];
162
+ params[name] = value;
163
+ });
164
+ const resolved = names.map((name) => {
165
+ return params[name];
166
+ });
167
+ options = {
168
+ options,
169
+ data: meta.data,
170
+ };
171
+ return [...resolved, options];
172
+ }
173
+ function generateHtml(tag, props) {
174
+ const { text, ...rest } = props;
175
+ const attr = Object.entries(rest)
176
+ .map((entry) => {
177
+ const [key, value] = entry;
178
+ if (value) {
179
+ return [key, `"${value}"`].join('=');
180
+ }
181
+ })
182
+ .filter((a) => a)
183
+ .join(' ');
184
+ let html = `<${tag} ${attr}>`;
185
+ if (text) {
186
+ html += `${text}</${tag}>`;
187
+ }
188
+ return new handlebars_1.default.SafeString(html);
189
+ }
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.TemplateRenderer = void 0;
7
+ var TemplateRenderer_1 = require("./TemplateRenderer");
8
+ Object.defineProperty(exports, "TemplateRenderer", { enumerable: true, get: function () { return __importDefault(TemplateRenderer_1).default; } });
@@ -0,0 +1 @@
1
+ { "type": "commonjs" }
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.resolveTemplateSource = resolveTemplateSource;
7
+ exports.runFrontMatter = runFrontMatter;
8
+ exports.getSections = getSections;
9
+ const fs_1 = require("fs");
10
+ const path_1 = __importDefault(require("path"));
11
+ const front_matter_1 = __importDefault(require("front-matter"));
12
+ function resolveTemplateSource(arg, options) {
13
+ const { dir } = options;
14
+ if (dir) {
15
+ const filepath = path_1.default.resolve(dir, arg);
16
+ return readSource(filepath) || arg;
17
+ }
18
+ else {
19
+ return arg;
20
+ }
21
+ }
22
+ function runFrontMatter(str) {
23
+ let { body, attributes: meta } = (0, front_matter_1.default)(str);
24
+ return {
25
+ meta,
26
+ body: body.trim(),
27
+ };
28
+ }
29
+ function tryReadFile(filepath, ext) {
30
+ try {
31
+ return (0, fs_1.readFileSync)(filepath + ext, 'utf-8');
32
+ }
33
+ catch (error) {
34
+ if (error.code !== 'ENOENT') {
35
+ throw error;
36
+ }
37
+ }
38
+ }
39
+ function readSource(filepath) {
40
+ return tryReadFile(filepath, '.md') || tryReadFile(filepath, '.txt');
41
+ }
42
+ // Sections
43
+ const SECTIONS_REG = /^=== (\w+) ===\n\n/gm;
44
+ function getSections(str) {
45
+ str = str.trim();
46
+ const arr = str.split(SECTIONS_REG).slice(1);
47
+ if (!arr.length) {
48
+ return [
49
+ {
50
+ content: str,
51
+ },
52
+ ];
53
+ }
54
+ const sections = [];
55
+ for (let i = 0; i < arr.length; i += 2) {
56
+ sections.push({
57
+ title: arr[i],
58
+ content: arr[i + 1].trim(),
59
+ });
60
+ }
61
+ return sections;
62
+ }
@@ -0,0 +1,56 @@
1
+ import Handlebars from 'handlebars';
2
+ import { memoize } from 'lodash-es';
3
+ import { DEFAULT_HELPERS, resolveHelpers } from './helpers.js';
4
+ import { getSections, resolveTemplateSource, runFrontMatter } from './utils.js';
5
+ export default class TemplateRenderer {
6
+ constructor(options = {}) {
7
+ /** @type {Object} */
8
+ this.options = {
9
+ ...options,
10
+ helpers: {
11
+ ...DEFAULT_HELPERS,
12
+ ...options.helpers,
13
+ },
14
+ };
15
+ }
16
+ run(options) {
17
+ const { template, params, helpers, ...rest } = this.resolveOptions(options);
18
+ if (!template) {
19
+ return {
20
+ body: '',
21
+ sections: [],
22
+ };
23
+ }
24
+ const compiled = this.loadTemplate(template, rest);
25
+ return compiled(params, {
26
+ helpers: resolveHelpers(helpers, rest),
27
+ allowProtoPropertiesByDefault: true,
28
+ });
29
+ }
30
+ // Private
31
+ /** @returns {Object} */
32
+ resolveOptions(options = {}) {
33
+ return {
34
+ ...this.options,
35
+ ...options,
36
+ params: {
37
+ ...this.options.params,
38
+ ...options.params,
39
+ },
40
+ };
41
+ }
42
+ loadTemplate = memoize((input, options) => {
43
+ const source = resolveTemplateSource(input, options);
44
+ const template = Handlebars.compile(source.trim());
45
+ return (params, options) => {
46
+ const output = template(params, options);
47
+ const { body, meta } = runFrontMatter(output);
48
+ const sections = getSections(body);
49
+ return {
50
+ body,
51
+ meta,
52
+ sections,
53
+ };
54
+ };
55
+ });
56
+ }
@@ -0,0 +1,182 @@
1
+ import { DateTime } from '@bedrockio/chrono';
2
+ import Handlebars from 'handlebars';
3
+ export const DEFAULT_HELPERS = {
4
+ // date
5
+ date(arg) {
6
+ return new DateTime(arg).toDate();
7
+ },
8
+ dateLong(arg) {
9
+ return new DateTime(arg).toDateLong();
10
+ },
11
+ dateMedium(arg) {
12
+ return new DateTime(arg).toDateMedium();
13
+ },
14
+ dateShort(arg) {
15
+ return new DateTime(arg).toDateShort();
16
+ },
17
+ // time
18
+ time(arg, meridiem) {
19
+ return new DateTime(arg).toTimeMedium({
20
+ meridiem,
21
+ });
22
+ },
23
+ /**
24
+ * @param {Intl.DateTimeFormatOptions['timeZoneName']} [style='short']
25
+ */
26
+ timeZone(arg, meridiem, style = 'short') {
27
+ return new DateTime(arg).toTimeWithZone({
28
+ meridiem,
29
+ timeZoneName: style,
30
+ });
31
+ },
32
+ timeLong(arg, meridiem) {
33
+ return new DateTime(arg).toTimeLong({
34
+ meridiem,
35
+ });
36
+ },
37
+ timeMedium(arg, meridiem) {
38
+ return new DateTime(arg).toTimeMedium({
39
+ meridiem,
40
+ });
41
+ },
42
+ timeShort(arg, meridiem) {
43
+ return new DateTime(arg).toTimeShort({
44
+ meridiem,
45
+ });
46
+ },
47
+ // datetime
48
+ dateTime(arg, meridiem) {
49
+ return new DateTime(arg).formatLong({
50
+ meridiem,
51
+ });
52
+ },
53
+ /**
54
+ * @param {Intl.DateTimeFormatOptions['timeZoneName']} [style='short']
55
+ */
56
+ dateTimeZone(arg, meridiem, style = 'short') {
57
+ return new DateTime(arg).formatWithZone({
58
+ meridiem,
59
+ timeZoneName: style,
60
+ });
61
+ },
62
+ dateTimeLong(arg, meridiem) {
63
+ return new DateTime(arg).formatLong({
64
+ meridiem,
65
+ });
66
+ },
67
+ dateTimeMedium(arg, meridiem) {
68
+ return new DateTime(arg).formatMedium({
69
+ meridiem,
70
+ });
71
+ },
72
+ dateTimeShort(arg, meridiem) {
73
+ return new DateTime(arg).formatShort({
74
+ meridiem,
75
+ });
76
+ },
77
+ // relative time
78
+ relTime(arg, min, max) {
79
+ return new DateTime(arg).relative({
80
+ min,
81
+ max,
82
+ });
83
+ },
84
+ number(options) {
85
+ const { index } = options.data;
86
+ if (index == null) {
87
+ return '';
88
+ }
89
+ return index + 1;
90
+ },
91
+ link(url, text) {
92
+ return new Handlebars.SafeString(`[${text}](${url})`);
93
+ },
94
+ button(url, text) {
95
+ return generateHtml('a', {
96
+ text,
97
+ href: url,
98
+ class: 'button',
99
+ target: '_blank',
100
+ });
101
+ },
102
+ list(arr) {
103
+ return arr
104
+ .map((el) => {
105
+ return `- ${el}`;
106
+ })
107
+ .join('\n');
108
+ },
109
+ };
110
+ export function resolveHelpers(helpers, options) {
111
+ const result = {};
112
+ for (let [key, value] of Object.entries(helpers)) {
113
+ result[key] = resolveHelper(value, options);
114
+ }
115
+ return result;
116
+ }
117
+ function resolveHelper(arg, options) {
118
+ const { names, handler } = resolveArgumentNames(arg);
119
+ return (...args) => {
120
+ return handler(...resolveHelperArgs(args, names, options));
121
+ };
122
+ }
123
+ // Arguments
124
+ const ARGUMENT_NAMES_REG = /\w+\((.+)\) {/;
125
+ function resolveArgumentNames(arg) {
126
+ if (typeof arg === 'function') {
127
+ const handler = arg;
128
+ const match = handler.toString().match(ARGUMENT_NAMES_REG);
129
+ let names = [];
130
+ if (match) {
131
+ names = match[1]
132
+ .split(', ')
133
+ .map((arg) => {
134
+ return arg.split(' ')[0];
135
+ })
136
+ .filter((arg) => {
137
+ return arg !== 'options';
138
+ });
139
+ }
140
+ return { handler, names };
141
+ }
142
+ else {
143
+ return {
144
+ names: arg.params,
145
+ handler: arg.handler,
146
+ };
147
+ }
148
+ }
149
+ function resolveHelperArgs(args, names, options) {
150
+ const ordered = args.slice(0, -1);
151
+ const [meta] = args.slice(-1);
152
+ let params = { ...meta.hash };
153
+ ordered.forEach((value, i) => {
154
+ const name = names[i];
155
+ params[name] = value;
156
+ });
157
+ const resolved = names.map((name) => {
158
+ return params[name];
159
+ });
160
+ options = {
161
+ options,
162
+ data: meta.data,
163
+ };
164
+ return [...resolved, options];
165
+ }
166
+ function generateHtml(tag, props) {
167
+ const { text, ...rest } = props;
168
+ const attr = Object.entries(rest)
169
+ .map((entry) => {
170
+ const [key, value] = entry;
171
+ if (value) {
172
+ return [key, `"${value}"`].join('=');
173
+ }
174
+ })
175
+ .filter((a) => a)
176
+ .join(' ');
177
+ let html = `<${tag} ${attr}>`;
178
+ if (text) {
179
+ html += `${text}</${tag}>`;
180
+ }
181
+ return new Handlebars.SafeString(html);
182
+ }
@@ -0,0 +1 @@
1
+ export { default as TemplateRenderer } from './TemplateRenderer.js';
@@ -0,0 +1,54 @@
1
+ import { readFileSync } from 'fs';
2
+ import path from 'path';
3
+ import frontmatter from 'front-matter';
4
+ export function resolveTemplateSource(arg, options) {
5
+ const { dir } = options;
6
+ if (dir) {
7
+ const filepath = path.resolve(dir, arg);
8
+ return readSource(filepath) || arg;
9
+ }
10
+ else {
11
+ return arg;
12
+ }
13
+ }
14
+ export function runFrontMatter(str) {
15
+ let { body, attributes: meta } = frontmatter(str);
16
+ return {
17
+ meta,
18
+ body: body.trim(),
19
+ };
20
+ }
21
+ function tryReadFile(filepath, ext) {
22
+ try {
23
+ return readFileSync(filepath + ext, 'utf-8');
24
+ }
25
+ catch (error) {
26
+ if (error.code !== 'ENOENT') {
27
+ throw error;
28
+ }
29
+ }
30
+ }
31
+ function readSource(filepath) {
32
+ return tryReadFile(filepath, '.md') || tryReadFile(filepath, '.txt');
33
+ }
34
+ // Sections
35
+ const SECTIONS_REG = /^=== (\w+) ===\n\n/gm;
36
+ export function getSections(str) {
37
+ str = str.trim();
38
+ const arr = str.split(SECTIONS_REG).slice(1);
39
+ if (!arr.length) {
40
+ return [
41
+ {
42
+ content: str,
43
+ },
44
+ ];
45
+ }
46
+ const sections = [];
47
+ for (let i = 0; i < arr.length; i += 2) {
48
+ sections.push({
49
+ title: arr[i],
50
+ content: arr[i + 1].trim(),
51
+ });
52
+ }
53
+ return sections;
54
+ }
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@bedrockio/templates",
3
+ "version": "0.1.0",
4
+ "description": "Bedrock utility for custom templates.",
5
+ "type": "module",
6
+ "scripts": {
7
+ "test": "vitest run",
8
+ "lint": "eslint",
9
+ "build": "scripts/build",
10
+ "eject": "scripts/eject",
11
+ "build:cjs": "tsc -p tsconfig.cjs.json",
12
+ "build:esm": "tsc -p tsconfig.esm.json && tsc-alias -f -p tsconfig.esm.json",
13
+ "build:types": "tsc -p tsconfig.types.json",
14
+ "prepublish": "yarn build"
15
+ },
16
+ "types": "types/index.d.ts",
17
+ "main": "./dist/cjs/index.js",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./types/index.d.ts",
21
+ "import": "./dist/esm/index.js",
22
+ "require": "./dist/cjs/index.js"
23
+ }
24
+ },
25
+ "contributors": [
26
+ {
27
+ "name": "Andrew Plummer",
28
+ "email": "andrew@rekall.ai"
29
+ }
30
+ ],
31
+ "license": "MIT",
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "https://github.com/bedrockio/templates"
35
+ },
36
+ "dependencies": {
37
+ "@bedrockio/chrono": "^0.7.0",
38
+ "front-matter": "^4.0.2",
39
+ "handlebars": "^4.7.8",
40
+ "lodash-es": "^4.17.21"
41
+ },
42
+ "devDependencies": {
43
+ "@bedrockio/eslint-plugin": "^1.3.0",
44
+ "@bedrockio/prettier-config": "^1.1.1",
45
+ "@sinonjs/fake-timers": "^15.0.0",
46
+ "eslint": "^9.37.0",
47
+ "tsc-alias": "^1.8.16",
48
+ "typescript": "^5.9.3",
49
+ "vitest": "^3.2.4"
50
+ },
51
+ "prettier": "@bedrockio/prettier-config",
52
+ "files": [
53
+ "dist/**",
54
+ "types/**",
55
+ "README.md",
56
+ "CHANGELOG.md"
57
+ ],
58
+ "volta": {
59
+ "node": "22.20.0",
60
+ "yarn": "1.22.22"
61
+ }
62
+ }
@@ -0,0 +1,9 @@
1
+ export default class Interpolator {
2
+ constructor(options: any);
3
+ options: any;
4
+ loadTemplates(): void;
5
+ loadTemplate: any;
6
+ run(options: any): any;
7
+ resolveOptions(options: any): any;
8
+ }
9
+ //# sourceMappingURL=Interpolator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Interpolator.d.ts","sourceRoot":"","sources":["../src/Interpolator.js"],"names":[],"mappings":"AA8HA;IACE,0BAGC;IAFC,aAAsB;IAIxB,sBAAkB;IAElB,kBAKG;IAuBH,uBAOC;IAED,kCAMC;CACF"}
@@ -0,0 +1,10 @@
1
+ export default class TemplateRenderer {
2
+ constructor(options?: {});
3
+ /** @type {Object} */
4
+ options: any;
5
+ run(options: any): any;
6
+ /** @returns {Object} */
7
+ resolveOptions(options?: {}): any;
8
+ loadTemplate: any;
9
+ }
10
+ //# sourceMappingURL=TemplateRenderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TemplateRenderer.d.ts","sourceRoot":"","sources":["../src/TemplateRenderer.js"],"names":[],"mappings":"AAMA;IACE,0BASC;IARC,qBAAqB;IACrB,aAMC;IAGH,uBAgBC;IAID,wBAAwB;IACxB,kCASC;IAED,kBAgBG;CACJ"}
@@ -0,0 +1,30 @@
1
+ export function resolveHelpers(helpers: any, options: any): {};
2
+ export namespace DEFAULT_HELPERS {
3
+ function date(arg: any): string;
4
+ function dateLong(arg: any): any;
5
+ function dateMedium(arg: any): any;
6
+ function dateShort(arg: any): any;
7
+ function time(arg: any, meridiem: any): any;
8
+ /**
9
+ * @param {Intl.DateTimeFormatOptions['timeZoneName']} [style='short']
10
+ */
11
+ function timeZone(arg: any, meridiem: any, style?: Intl.DateTimeFormatOptions["timeZoneName"]): any;
12
+ function timeLong(arg: any, meridiem: any): any;
13
+ function timeMedium(arg: any, meridiem: any): any;
14
+ function timeShort(arg: any, meridiem: any): any;
15
+ function dateTime(arg: any, meridiem: any): any;
16
+ /**
17
+ * @param {Intl.DateTimeFormatOptions['timeZoneName']} [style='short']
18
+ */
19
+ function dateTimeZone(arg: any, meridiem: any, style?: Intl.DateTimeFormatOptions["timeZoneName"]): any;
20
+ function dateTimeLong(arg: any, meridiem: any): any;
21
+ function dateTimeMedium(arg: any, meridiem: any): any;
22
+ function dateTimeShort(arg: any, meridiem: any): any;
23
+ function relTime(arg: any, min: any, max: any): any;
24
+ function number(options: any): any;
25
+ function link(url: any, text: any): Handlebars.SafeString;
26
+ function button(url: any, text: any): Handlebars.SafeString;
27
+ function list(arr: any): any;
28
+ }
29
+ import Handlebars from 'handlebars';
30
+ //# sourceMappingURL=helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.js"],"names":[],"mappings":"AAyHA,+DAMC;;IA1HC,gCAEC;IACD,iCAEC;IACD,mCAEC;IACD,kCAEC;IAGD,4CAIC;IAED;;OAEG;IACH,mDAFW,IAAI,CAAC,qBAAqB,CAAC,cAAc,CAAC,OAOpD;IACD,gDAIC;IACD,kDAIC;IACD,iDAIC;IAGD,gDAIC;IAED;;OAEG;IACH,uDAFW,IAAI,CAAC,qBAAqB,CAAC,cAAc,CAAC,OAOpD;IACD,oDAIC;IACD,sDAIC;IACD,qDAIC;IAID,oDAKC;IAED,mCAMC;IAED,0DAEC;IAED,4DAOC;IAED,6BAMC;;uBArHoB,YAAY"}
@@ -0,0 +1,2 @@
1
+ export { default as TemplateRenderer } from "./TemplateRenderer";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export function getInterpolator(channel: any): (params: any) => Promise<{}>;
2
+ //# sourceMappingURL=something.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"something.d.ts","sourceRoot":"","sources":["../src/something.js"],"names":[],"mappings":"AAgCA,gDAEgB,WAAM,iBAerB"}
@@ -0,0 +1,12 @@
1
+ export function resolveTemplateSource(arg: any, options: any): any;
2
+ export function runFrontMatter(str: any): {
3
+ meta: any;
4
+ body: string;
5
+ };
6
+ export function getSections(str: any): {
7
+ content: any;
8
+ }[] | {
9
+ title: any;
10
+ content: any;
11
+ }[];
12
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.js"],"names":[],"mappings":"AAKA,mEASC;AAED;;;EAOC;AAoBD;;;;;IAsBC"}