feat(json-templates): enhance date filter handling and support non-string types

This commit is contained in:
sheldon66 2025-02-18 14:36:29 +08:00
parent 144534f170
commit 8503080803
2 changed files with 75 additions and 29 deletions

View File

@ -8,7 +8,7 @@
*/ */
import { parse } from '../json-templates'; import { parse } from '../json-templates';
import { TokenKind } from 'liquidjs';
describe('json-templates', () => { describe('json-templates', () => {
it('parse json with string template', async () => { it('parse json with string template', async () => {
const template = { const template = {
@ -29,16 +29,18 @@ describe('json-templates', () => {
describe('json-templates filters', () => { describe('json-templates filters', () => {
it('date filters', async () => { it('date filters', async () => {
const template = { const template = {
now: '{{now}}',
today: '{{now | date_format: "YYYY-MM-DD"}}', today: '{{now | date_format: "YYYY-MM-DD"}}',
yesterday: '{{now | date_subtract: 1, "day" | date_format: "YYYY-MM-DD"}}', yesterday: '{{now | date_subtract: 1, "day" | date_format: "YYYY-MM-DD"}}',
}; };
const compiledFn = parse(template); const parsed = parse(template);
compiledFn.parameters.some((parameter) => parameter.key === 'now'); const now = new Date('2025-01-01: 12:00:00');
const result = compiledFn({ const result = parsed({
now: new Date('2025-01-01: 12:00:00'), now,
}); });
expect(result).toEqual({ expect(result).toEqual({
now,
today: '2025-01-01', today: '2025-01-01',
yesterday: '2024-12-31', yesterday: '2024-12-31',
}); });
@ -61,17 +63,31 @@ describe('json-templates filters', () => {
}); });
}); });
it('when non-string type', async () => {
class Form {}
const form = new Form();
const template = {
form: '{{form}}',
};
const result = parse(template)({
form,
});
expect(result).toEqual({
form,
});
});
it('when key is undefined, ignore it', async () => { it('when key is undefined, ignore it', async () => {
const template = { const template = {
key1: '{{current.key1}}', key1: '{{current.key1}}',
key2: '{{current.key2}}', key2: '{{current.key2}}',
}; };
const result = parse(template)({ const result = parse(template)({
current: { key1: 'value1', key2: undefined }, current: { key1: 'value1' },
}); });
expect(result).toEqual({ expect(result).toEqual({
key1: 'value1', key1: 'value1',
key2: '', key2: undefined,
}); });
}); });
}); });

View File

@ -12,9 +12,12 @@
// //
// Created by Curran Kelleher and Chrostophe Serafin. // Created by Curran Kelleher and Chrostophe Serafin.
// Contributions from Paul Brewer and Javier Blanco Martinez. // Contributions from Paul Brewer and Javier Blanco Martinez.
import { get } from 'lodash'; import { get, template } from 'lodash';
import liquidjsEngine from './liquidjs'; import liquidjsEngine from './liquidjs';
import { TokenKind } from 'liquidjs';
import engine from './liquidjs'; import engine from './liquidjs';
import raw from 'liquidjs/dist/tags/raw';
import b from 'packages/core/database/src/__tests__/fixtures/c1/b';
// An enhanced version of `typeof` that handles arrays and dates as well. // An enhanced version of `typeof` that handles arrays and dates as well.
function type(value) { function type(value) {
@ -30,25 +33,6 @@ function type(value) {
return valueType; return valueType;
} }
// Constructs a parameter object from a match result.
// e.g. "['{{foo}}']" --> { key: "foo" }
// e.g. "['{{foo:bar}}']" --> { key: "foo", defaultValue: "bar" }
function Parameter(match) {
let param;
const matchValue = match.substr(2, match.length - 4).trim();
const i = matchValue.indexOf(':');
if (i !== -1) {
param = {
key: matchValue.substr(0, i),
};
} else {
param = { key: matchValue };
}
return param;
}
// Constructs a template function with deduped `parameters` property. // Constructs a template function with deduped `parameters` property.
function Template(fn, parameters) { function Template(fn, parameters) {
fn.parameters = Array.from(new Map(parameters.map((parameter) => [parameter.key, parameter])).values()); fn.parameters = Array.from(new Map(parameters.map((parameter) => [parameter.key, parameter])).values());
@ -85,10 +69,56 @@ const parseString = (() => {
// template parameter syntax such as {{foo}} or {{foo:someDefault}}. // template parameter syntax such as {{foo}} or {{foo:someDefault}}.
return (str) => { return (str) => {
const rawTemplates = liquidjsEngine.parse(str);
// const ref = {};
// rawTemplates.forEach((template) => {
// if (template.token.kind === TokenKind.Output) {
// // @ts-ignore
// const variable = template.value?.initial?.postfix;
// // @ts-ignore
// ref.variableName = variable[0].props[0].content;
// // @ts-ignore
// ref.filters = template.value?.filters.map(({ name, handler, args }) => ({
// name,
// handler,
// args: args.map((arg) => arg.content),
// }));
// }
// });
const templates = rawTemplates
.filter((rawTemplate) => rawTemplate.token.kind === TokenKind.Output)
.map((rawTemplate) => {
const fullVariables = liquidjsEngine.fullVariablesSync(rawTemplate.token.input);
return {
// @ts-ignore
variableName: fullVariables[0],
tokenBegin: rawTemplate.token.begin,
tokenEnd: rawTemplate.token.end,
// @ts-ignore
filters: rawTemplate.value?.filters.map(({ name, handler, args }) => ({
name,
handler,
args: args.map((arg) => arg.content),
})),
};
});
const templateFn = (context) => { const templateFn = (context) => {
return engine.parseAndRenderSync(str, context); if (templates.length === 1 && templates[0].tokenBegin === 0 && templates[0].tokenEnd === str.length) {
let value = get(context, templates[0].variableName);
if (typeof value === 'function') {
value = value();
}
if (Array.isArray(templates[0].filters)) {
return templates[0].filters.reduce((acc, filter) => filter.handler(...[acc, ...filter.args]), value);
}
}
return engine.renderSync(rawTemplates, context);
}; };
const parameters = liquidjsEngine.fullVariablesSync(str).map((variable) => ({ key: variable }));
// Accommodate non-string as original values.
const parameters = templates.map((template) => ({ key: template.variableName }));
return Template(templateFn, parameters); return Template(templateFn, parameters);
}; };