mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-09 23:49:27 +08:00
feat: refactor JSON template parser and add date filter tests
This commit is contained in:
parent
98e659b115
commit
b35bc82639
@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the NocoBase (R) project.
|
||||||
|
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||||
|
* Authors: NocoBase Team.
|
||||||
|
*
|
||||||
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||||
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
|
*/
|
||||||
|
import { escape, revertEscape } from '../escape';
|
||||||
|
import { createJSONTemplateParser } from '../parser';
|
||||||
|
|
||||||
|
const parser = createJSONTemplateParser();
|
||||||
|
|
||||||
|
describe('ctx function', () => {
|
||||||
|
it('should escape array', () => {
|
||||||
|
const template = {
|
||||||
|
user: '{{$user.id}}-{{$user.name}}',
|
||||||
|
state: {
|
||||||
|
userId: 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const data = {
|
||||||
|
$user({ fields, context }) {
|
||||||
|
if (context.state.userId) {
|
||||||
|
return (field) => 1;
|
||||||
|
} else return (field) => 2;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const parsed = parser.parse(template);
|
||||||
|
const result = parsed(data);
|
||||||
|
});
|
||||||
|
});
|
@ -7,5 +7,4 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export { parse } from './parse';
|
|
||||||
export { createJSONTemplateParser, JSONTemplateParser } from './json-template-parser';
|
export { createJSONTemplateParser, JSONTemplateParser } from './json-template-parser';
|
||||||
|
@ -7,9 +7,10 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Liquid } from 'liquidjs';
|
import { Liquid, TokenKind } from 'liquidjs';
|
||||||
|
import { get } from 'lodash';
|
||||||
import { variableFilters, filterGroups } from '../filters';
|
import { variableFilters, filterGroups } from '../filters';
|
||||||
import { escape } from '../escape';
|
import { escape, revertEscape } from '../escape';
|
||||||
type FilterGroup = {
|
type FilterGroup = {
|
||||||
name: string;
|
name: string;
|
||||||
title: string;
|
title: string;
|
||||||
@ -26,12 +27,12 @@ type Filter = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export class JSONTemplateParser {
|
export class JSONTemplateParser {
|
||||||
engine: Liquid;
|
private _engine: Liquid;
|
||||||
private _filterGroups: Array<FilterGroup>;
|
private _filterGroups: Array<FilterGroup>;
|
||||||
private _filters: Array<Filter>;
|
private _filters: Array<Filter>;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.engine = new Liquid();
|
this._engine = new Liquid();
|
||||||
this._filterGroups = [];
|
this._filterGroups = [];
|
||||||
this._filters = [];
|
this._filters = [];
|
||||||
}
|
}
|
||||||
@ -40,6 +41,10 @@ export class JSONTemplateParser {
|
|||||||
return this._filters;
|
return this._filters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get engine(): Liquid {
|
||||||
|
return this._engine;
|
||||||
|
}
|
||||||
|
|
||||||
get filterGroups(): Array<
|
get filterGroups(): Array<
|
||||||
FilterGroup & {
|
FilterGroup & {
|
||||||
filters: Array<Filter>;
|
filters: Array<Filter>;
|
||||||
@ -56,12 +61,167 @@ export class JSONTemplateParser {
|
|||||||
}
|
}
|
||||||
registerFilter(filter: Filter): void {
|
registerFilter(filter: Filter): void {
|
||||||
this._filters.push(filter);
|
this._filters.push(filter);
|
||||||
this.engine.registerFilter(filter.name, filter.handler);
|
this._engine.registerFilter(filter.name, filter.handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
render(template: string, data: any = {}): any {
|
render(template: string, data: any = {}): any {
|
||||||
return this.engine.parseAndRenderSync(escape(template), escape(data));
|
const scopeFieldsMap = new Map<string, string[]>();
|
||||||
|
Object.keys(data).forEach((key) => {
|
||||||
|
if (key.startsWith('$') || typeof data[key] === 'function') {
|
||||||
|
scopeFieldsMap.set(key, []);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const parsed = this.parse(template, { scopeFieldsMap });
|
||||||
|
return parsed(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parse = (value: any, opts?: { scopeFieldsMap: Map<string, string[]> }) => {
|
||||||
|
const engine = this.engine;
|
||||||
|
function type(value) {
|
||||||
|
let valueType: string = typeof value;
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
valueType = 'array';
|
||||||
|
} else if (value instanceof Date) {
|
||||||
|
valueType = 'date';
|
||||||
|
} else if (value === null) {
|
||||||
|
valueType = 'null';
|
||||||
|
}
|
||||||
|
return valueType;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Template(fn, parameters) {
|
||||||
|
fn.parameters = Array.from(new Map(parameters.map((parameter) => [parameter.key, parameter])).values());
|
||||||
|
return fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This regular expression detects instances of the
|
||||||
|
// template parameter syntax such as {{foo}} or {{foo:someDefault}}.
|
||||||
|
const parseString = (() => {
|
||||||
|
// This regular expression detects instances of the
|
||||||
|
// template parameter syntax such as {{foo}} or {{foo:someDefault}}.
|
||||||
|
|
||||||
|
return (str) => {
|
||||||
|
const escapeStr = escape(str);
|
||||||
|
const rawTemplates = engine.parse(escapeStr);
|
||||||
|
const templates = rawTemplates
|
||||||
|
.filter((rawTemplate) => rawTemplate.token.kind === TokenKind.Output)
|
||||||
|
.map((rawTemplate) => {
|
||||||
|
if (rawTemplate.token.kind === TokenKind.Output) {
|
||||||
|
// @ts-ignore
|
||||||
|
const fullVariables = engine.fullVariablesSync(rawTemplate.token.content);
|
||||||
|
const variableName = fullVariables[0];
|
||||||
|
// @ts-ignore
|
||||||
|
const variableSegments = (engine.variableSegmentsSync(rawTemplate.token.content)[0] ?? []) as string[];
|
||||||
|
/* collect scope fields to map
|
||||||
|
eg: '{{ $user.name }} - {{$user.id}}'
|
||||||
|
fieldsMap = {'$user': ['name', 'id']}
|
||||||
|
*/
|
||||||
|
if (
|
||||||
|
opts?.scopeFieldsMap &&
|
||||||
|
variableName.startsWith('$') &&
|
||||||
|
variableSegments.length > 1 &&
|
||||||
|
opts.scopeFieldsMap.has(variableSegments[0])
|
||||||
|
) {
|
||||||
|
opts.scopeFieldsMap.set(variableName, variableSegments.slice(1));
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
// @ts-ignore
|
||||||
|
variableName: fullVariables[0],
|
||||||
|
variableSegments,
|
||||||
|
tokenKind: rawTemplate.token.kind,
|
||||||
|
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),
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
tokenKind: rawTemplate.token.kind,
|
||||||
|
tokenBegin: rawTemplate.token.begin,
|
||||||
|
tokenEnd: rawTemplate.token.end,
|
||||||
|
// @ts-ignore
|
||||||
|
content: rawTemplate.token.content,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const templateFn = (context) => {
|
||||||
|
const escapedContext = escape(context);
|
||||||
|
if (templates.length === 1 && templates[0].tokenBegin === 0 && templates[0].tokenEnd === escapeStr.length) {
|
||||||
|
let value = get(escapedContext, 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);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return revertEscape(engine.renderSync(rawTemplates, escapedContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
templates.map((template) => {
|
||||||
|
if (template.tokenKind === TokenKind.Output) {
|
||||||
|
const value = get(escapedContext, template.variableName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Accommodate non-string as original values.
|
||||||
|
|
||||||
|
const parameters = templates.map((template) => ({ key: revertEscape(template.variableName) }));
|
||||||
|
|
||||||
|
return Template(templateFn, parameters);
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
function parseObject(object) {
|
||||||
|
const children = Object.keys(object).map((key) => ({
|
||||||
|
keyTemplate: parseString(key),
|
||||||
|
valueTemplate: _parse(object[key]),
|
||||||
|
}));
|
||||||
|
const templateParameters = children.reduce(
|
||||||
|
(parameters, child) => parameters.concat(child.valueTemplate.parameters, child.keyTemplate.parameters),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
const templateFn = (context) => {
|
||||||
|
return children.reduce((newObject, child) => {
|
||||||
|
newObject[child.keyTemplate(context)] = child.valueTemplate(context);
|
||||||
|
return newObject;
|
||||||
|
}, {});
|
||||||
|
};
|
||||||
|
|
||||||
|
return Template(templateFn, templateParameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parses non-leaf-nodes in the template object that are arrays.
|
||||||
|
function parseArray(array) {
|
||||||
|
const templates = array.map(_parse);
|
||||||
|
const templateParameters = templates.reduce((parameters, template) => parameters.concat(template.parameters), []);
|
||||||
|
const templateFn = (context) => templates.map((template) => template(context));
|
||||||
|
|
||||||
|
return Template(templateFn, templateParameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _parse(value) {
|
||||||
|
switch (type(value)) {
|
||||||
|
case 'string':
|
||||||
|
return parseString(value);
|
||||||
|
case 'object':
|
||||||
|
return parseObject(value);
|
||||||
|
case 'array':
|
||||||
|
return parseArray(value);
|
||||||
|
default:
|
||||||
|
return Template(function () {
|
||||||
|
return value;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _parse(value);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const parser = new JSONTemplateParser();
|
const parser = new JSONTemplateParser();
|
||||||
|
@ -1,140 +0,0 @@
|
|||||||
/**
|
|
||||||
* This file is part of the NocoBase (R) project.
|
|
||||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
||||||
* Authors: NocoBase Team.
|
|
||||||
*
|
|
||||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// json-templates
|
|
||||||
// Simple templating within JSON structures.
|
|
||||||
//
|
|
||||||
// Created by Curran Kelleher and Chrostophe Serafin.
|
|
||||||
// Contributions from Paul Brewer and Javier Blanco Martinez.
|
|
||||||
import { get } from 'lodash';
|
|
||||||
import { TokenKind } from 'liquidjs';
|
|
||||||
import { escape, revertEscape } from '../escape';
|
|
||||||
import { createJSONTemplateParser } from './json-template-parser';
|
|
||||||
|
|
||||||
// An enhanced version of `typeof` that handles arrays and dates as well.
|
|
||||||
const parser = createJSONTemplateParser();
|
|
||||||
const liquidjsEngine = parser.engine;
|
|
||||||
function type(value) {
|
|
||||||
let valueType: string = typeof value;
|
|
||||||
if (Array.isArray(value)) {
|
|
||||||
valueType = 'array';
|
|
||||||
} else if (value instanceof Date) {
|
|
||||||
valueType = 'date';
|
|
||||||
} else if (value === null) {
|
|
||||||
valueType = 'null';
|
|
||||||
}
|
|
||||||
|
|
||||||
return valueType;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constructs a template function with deduped `parameters` property.
|
|
||||||
function Template(fn, parameters) {
|
|
||||||
fn.parameters = Array.from(new Map(parameters.map((parameter) => [parameter.key, parameter])).values());
|
|
||||||
return fn;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parses the given template object.
|
|
||||||
//
|
|
||||||
// Returns a function `template(context)` that will "fill in" the template
|
|
||||||
// with the context object passed to it.
|
|
||||||
//
|
|
||||||
// The returned function has a `parameters` property,
|
|
||||||
// which is an array of parameter descriptor objects,
|
|
||||||
// each of which has a `key` property and possibly a `defaultValue` property.
|
|
||||||
export function parse(value) {
|
|
||||||
switch (type(value)) {
|
|
||||||
case 'string':
|
|
||||||
return parseString(value);
|
|
||||||
case 'object':
|
|
||||||
return parseObject(value);
|
|
||||||
case 'array':
|
|
||||||
return parseArray(value);
|
|
||||||
default:
|
|
||||||
return Template(function () {
|
|
||||||
return value;
|
|
||||||
}, []);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parses leaf nodes of the template object that are strings.
|
|
||||||
// Also used for parsing keys that contain templates.
|
|
||||||
const parseString = (() => {
|
|
||||||
// This regular expression detects instances of the
|
|
||||||
// template parameter syntax such as {{foo}} or {{foo:someDefault}}.
|
|
||||||
|
|
||||||
return (str) => {
|
|
||||||
const escapeStr = escape(str);
|
|
||||||
const rawTemplates = liquidjsEngine.parse(escapeStr);
|
|
||||||
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 escapedContext = escape(context);
|
|
||||||
if (templates.length === 1 && templates[0].tokenBegin === 0 && templates[0].tokenEnd === escapeStr.length) {
|
|
||||||
let value = get(escapedContext, 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 revertEscape(liquidjsEngine.renderSync(rawTemplates, escapedContext));
|
|
||||||
};
|
|
||||||
|
|
||||||
// Accommodate non-string as original values.
|
|
||||||
|
|
||||||
const parameters = templates.map((template) => ({ key: revertEscape(template.variableName) }));
|
|
||||||
|
|
||||||
return Template(templateFn, parameters);
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|
||||||
// Parses non-leaf-nodes in the template object that are objects.
|
|
||||||
function parseObject(object) {
|
|
||||||
const children = Object.keys(object).map((key) => ({
|
|
||||||
keyTemplate: parseString(key),
|
|
||||||
valueTemplate: parse(object[key]),
|
|
||||||
}));
|
|
||||||
const templateParameters = children.reduce(
|
|
||||||
(parameters, child) => parameters.concat(child.valueTemplate.parameters, child.keyTemplate.parameters),
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
const templateFn = (context) => {
|
|
||||||
return children.reduce((newObject, child) => {
|
|
||||||
newObject[child.keyTemplate(context)] = child.valueTemplate(context);
|
|
||||||
return newObject;
|
|
||||||
}, {});
|
|
||||||
};
|
|
||||||
|
|
||||||
return Template(templateFn, templateParameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parses non-leaf-nodes in the template object that are arrays.
|
|
||||||
function parseArray(array) {
|
|
||||||
const templates = array.map(parse);
|
|
||||||
const templateParameters = templates.reduce((parameters, template) => parameters.concat(template.parameters), []);
|
|
||||||
const templateFn = (context) => templates.map((template) => template(context));
|
|
||||||
|
|
||||||
return Template(templateFn, templateParameters);
|
|
||||||
}
|
|
@ -9,6 +9,10 @@
|
|||||||
|
|
||||||
import lodash from 'lodash';
|
import lodash from 'lodash';
|
||||||
import { dayjs } from './dayjs';
|
import { dayjs } from './dayjs';
|
||||||
|
import { createJSONTemplateParser } from '@nocobase/json-template-parser';
|
||||||
|
const parser = createJSONTemplateParser();
|
||||||
|
const parse = parser.parse;
|
||||||
|
export { parse };
|
||||||
|
|
||||||
export * from './assign';
|
export * from './assign';
|
||||||
export * from './collections-graph';
|
export * from './collections-graph';
|
||||||
@ -19,7 +23,6 @@ export * from './forEach';
|
|||||||
export * from './fs-exists';
|
export * from './fs-exists';
|
||||||
export * from './handlebars';
|
export * from './handlebars';
|
||||||
export * from './isValidFilter';
|
export * from './isValidFilter';
|
||||||
export { parse } from '@nocobase/json-template-parser';
|
|
||||||
export * from './koa-multer';
|
export * from './koa-multer';
|
||||||
export * from './measure-execution-time';
|
export * from './measure-execution-time';
|
||||||
export * from './merge';
|
export * from './merge';
|
||||||
|
@ -1,31 +1,22 @@
|
|||||||
/**
|
import Database, { Repository } from '@nocobase/database';
|
||||||
* This file is part of the NocoBase (R) project.
|
import { createMockServer, MockServer } from '@nocobase/test';
|
||||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
||||||
* Authors: NocoBase Team.
|
|
||||||
*
|
|
||||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { parse } from '../parser';
|
describe('date filters', async () => {
|
||||||
describe('json-templates', () => {
|
let app: MockServer;
|
||||||
it('parse json with string template', async () => {
|
let db: Database;
|
||||||
const template = {
|
let repo: Repository;
|
||||||
name: '{{id}}-{{name}}.',
|
let agent;
|
||||||
age: 18,
|
let parse;
|
||||||
};
|
|
||||||
const result = parse(template)({
|
beforeAll(async () => {
|
||||||
name: 'test',
|
app = await createMockServer({
|
||||||
id: 1,
|
plugins: ['field-sort', 'auth', 'variable-filters'],
|
||||||
});
|
|
||||||
expect(result).toEqual({
|
|
||||||
name: '1-test.',
|
|
||||||
age: 18,
|
|
||||||
});
|
});
|
||||||
|
db = app.db;
|
||||||
|
repo = db.getRepository('authenticators');
|
||||||
|
agent = app.agent();
|
||||||
|
parse = app.jsonTemplateParser.parse;
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
describe('json-templates filters', () => {
|
|
||||||
it('date filters', async () => {
|
it('date filters', async () => {
|
||||||
const template = {
|
const template = {
|
||||||
now: '{{now}}',
|
now: '{{now}}',
|
||||||
@ -33,8 +24,8 @@ describe('json-templates filters', () => {
|
|||||||
yesterday: '{{now | date_subtract: 1, "day" | date_format: "YYYY-MM-DD"}}',
|
yesterday: '{{now | date_subtract: 1, "day" | date_format: "YYYY-MM-DD"}}',
|
||||||
};
|
};
|
||||||
|
|
||||||
const parsed = parse(template);
|
const parsed = app.jsonTemplateParser.parse(template);
|
||||||
const now = new Date('2025-01-01: 12:00:00');
|
const now = new Date('2025-01-01 12:00:00');
|
||||||
const result = parsed({
|
const result = parsed({
|
||||||
now,
|
now,
|
||||||
});
|
});
|
||||||
@ -51,7 +42,7 @@ describe('json-templates filters', () => {
|
|||||||
firstOfArray1: '{{array.0}}',
|
firstOfArray1: '{{array.0}}',
|
||||||
firstOfArray2: '{{array[0]}}',
|
firstOfArray2: '{{array[0]}}',
|
||||||
};
|
};
|
||||||
const result = parse(template)({
|
const result = app.jsonTemplateParser.parse(template)({
|
||||||
user: { name: 'john' },
|
user: { name: 'john' },
|
||||||
array: ['first', 'second'],
|
array: ['first', 'second'],
|
||||||
});
|
});
|
||||||
@ -69,7 +60,7 @@ describe('json-templates filters', () => {
|
|||||||
form: '{{form}}',
|
form: '{{form}}',
|
||||||
$form: '{{$form}}',
|
$form: '{{$form}}',
|
||||||
};
|
};
|
||||||
const result = parse(template)({
|
const result = app.jsonTemplateParser.parse(template)({
|
||||||
form,
|
form,
|
||||||
$form: form,
|
$form: form,
|
||||||
});
|
});
|
||||||
@ -84,7 +75,7 @@ describe('json-templates filters', () => {
|
|||||||
key1: '{{current.key1}}',
|
key1: '{{current.key1}}',
|
||||||
key2: '{{current.key2}}',
|
key2: '{{current.key2}}',
|
||||||
};
|
};
|
||||||
const result = parse(template)({
|
const result = app.jsonTemplateParser.parse(template)({
|
||||||
current: { key1: 'value1' },
|
current: { key1: 'value1' },
|
||||||
});
|
});
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
@ -100,7 +91,7 @@ describe('json-templates filters', () => {
|
|||||||
$yesterday: '{{ $now | date_subtract: 1, "day" | date_format: "YYYY-MM-DD" }}',
|
$yesterday: '{{ $now | date_subtract: 1, "day" | date_format: "YYYY-MM-DD" }}',
|
||||||
};
|
};
|
||||||
|
|
||||||
const parsed = parse(template);
|
const parsed = app.jsonTemplateParser.parse(template);
|
||||||
const $now = new Date('2025-01-01: 12:00:00');
|
const $now = new Date('2025-01-01: 12:00:00');
|
||||||
const result = parsed({
|
const result = parsed({
|
||||||
$now,
|
$now,
|
Loading…
x
Reference in New Issue
Block a user