Remove parts of the asynchronous logic

This commit is contained in:
Anton Liaposhchenko 2024-12-28 19:41:57 +02:00
parent f0b9c5396a
commit 94b2347867
4 changed files with 73 additions and 770 deletions
src/main
lib.ts
lib
environment.ts
extension
core.ts
core/functions

@ -22,9 +22,9 @@ export type {TwingArrayLoader, TwingSynchronousArrayLoader} from "./lib/loader/a
export type {TwingChainLoader, TwingSynchronousChainLoader} from "./lib/loader/chain";
export type {TwingLoader, TwingSynchronousLoader} from "./lib/loader";
export {createFilesystemLoader, createSynchronousFilesystemLoader} from "./lib/loader/filesystem";
export {createArrayLoader, createSynchronousArrayLoader} from "./lib/loader/array";
export {createChainLoader, createSynchronousChainLoader} from "./lib/loader/chain";
export {createSynchronousFilesystemLoader} from "./lib/loader/filesystem";
export {createSynchronousArrayLoader} from "./lib/loader/array";
export {createSynchronousChainLoader} from "./lib/loader/chain";
// markup
export type {TwingMarkup} from "./lib/markup";
@ -233,7 +233,7 @@ export {createEmbedNode} from "./lib/node/include/embed";
export {createIncludeNode} from "./lib/node/include/include";
// node executors
export {executeNode, executeNodeSynchronously, type TwingNodeExecutor, type TwingSynchronousNodeExecutor} from "./lib/node-executor";
export {executeNodeSynchronously, type TwingNodeExecutor, type TwingSynchronousNodeExecutor} from "./lib/node-executor";
// tag handlers
export type {TwingTagHandler, TwingTokenParser} from "./lib/tag-handler";
@ -298,16 +298,16 @@ export type {
export type {TwingTest, TwingSynchronousTest} from "./lib/test";
export type {TwingTokenStream} from "./lib/token-stream";
export {createEnvironment, createSynchronousEnvironment} from "./lib/environment";
export {createSynchronousEnvironment} from "./lib/environment";
export {createExtensionSet} from "./lib/extension-set";
export {createFilter, createSynchronousFilter} from "./lib/filter";
export {createFunction, createSynchronousFunction} from "./lib/function";
export {createSynchronousFilter} from "./lib/filter";
export {createSynchronousFunction} from "./lib/function";
export {createLexer} from "./lib/lexer";
export {createBaseNode, createNode, getChildren, getChildrenCount} from "./lib/node";
export {createOperator} from "./lib/operator";
export {createSandboxSecurityPolicy} from "./lib/sandbox/security-policy";
export {createSource} from "./lib/source";
export {createSourceMapRuntime} from "./lib/source-map-runtime";
export {type TwingTemplate, createTemplate, type TwingSynchronousTemplate, createSynchronousTemplate} from "./lib/template";
export {type TwingTemplateLoader, type TwingSynchronousTemplateLoader, createTemplateLoader, createSynchronousTemplateLoader} from "./lib/template-loader";
export {createTest, createSynchronousTest} from "./lib/test";
export {type TwingSynchronousTemplate, createSynchronousTemplate} from "./lib/template";
export {type TwingSynchronousTemplateLoader, createSynchronousTemplateLoader} from "./lib/template-loader";
export {createSynchronousTest} from "./lib/test";

@ -24,11 +24,9 @@ import {TwingSynchronousTemplate, TwingTemplate} from "./template";
import {Settings as DateTimeSettings} from "luxon";
import {createLexer, type TwingLexer} from "./lexer";
import {TwingCache, TwingSynchronousCache} from "./cache";
import {createCoreExtension, createSynchronousCoreExtension} from "./extension/core";
import {createSynchronousCoreExtension} from "./extension/core";
import {createAutoEscapeNode, createTemplateLoadingError, type TwingContext} from "../lib";
import {createSynchronousTemplateLoader, createTemplateLoader} from "./template-loader";
import {createContext} from "./context";
import {iterableToMap} from "./helpers/iterator-to-map";
import {createSynchronousTemplateLoader} from "./template-loader";
export type TwingNumberFormat = {
numberOfDecimals: number;
@ -243,193 +241,6 @@ export interface TwingSynchronousEnvironment {
tokenize(source: TwingSource): TwingTokenStream;
}
/**
* Creates an instance of {@link TwingEnvironment} backed by the passed loader.
*
* @param loader
* @param options
*/
export const createEnvironment = (
loader: TwingLoader | TwingSynchronousLoader,
options?: TwingEnvironmentOptions
): TwingEnvironment => {
const cssEscapingStrategy = createCssEscapingStrategyHandler();
const htmlEscapingStrategy = createHtmlEscapingStrategyHandler();
const htmlAttributeEscapingStrategy = createHtmlAttributeEscapingStrategyHandler();
const jsEscapingStrategy = createJsEscapingStrategyHandler();
const urlEscapingStrategy = createUrlEscapingStrategyHandler();
const escapingStrategyHandlers: Record<TwingEscapingStrategy, TwingEscapingStrategyHandler> = {
css: cssEscapingStrategy,
html: htmlEscapingStrategy,
html_attr: htmlAttributeEscapingStrategy,
js: jsEscapingStrategy,
url: urlEscapingStrategy
};
const extensionSet = createExtensionSet<TwingExtension>();
extensionSet.addExtension(createCoreExtension());
const cache: TwingCache | null = options?.cache || null;
const charset = options?.charset || 'UTF-8';
const dateFormat = options?.dateFormat || 'F j, Y H:i';
const dateIntervalFormat = options?.dateIntervalFormat || '%d days';
const numberFormat: TwingNumberFormat = options?.numberFormat || {
decimalPoint: '.',
numberOfDecimals: 0,
thousandSeparator: ','
};
const sandboxPolicy = options?.sandboxPolicy || createSandboxSecurityPolicy();
const globals = createContext(iterableToMap(options?.globals || {}));
let lexer: TwingLexer;
let parser: TwingParser;
const environment: TwingEnvironment = {
get cache() {
return cache;
},
get charset() {
return charset;
},
get dateFormat() {
return dateFormat;
},
get dateIntervalFormat() {
return dateIntervalFormat;
},
get escapingStrategyHandlers() {
return escapingStrategyHandlers;
},
get filters() {
return extensionSet.filters;
},
get functions() {
return extensionSet.functions;
},
get globals() {
return globals;
},
get loader() {
return loader;
},
get numberFormat() {
return numberFormat;
},
get sandboxPolicy() {
return sandboxPolicy;
},
get tests() {
return extensionSet.tests;
},
get timezone() {
return options?.timezone || DateTimeSettings.defaultZoneName
},
addExtension: extensionSet.addExtension,
addFilter: extensionSet.addFilter,
addFunction: extensionSet.addFunction,
addNodeVisitor: extensionSet.addNodeVisitor,
addOperator: extensionSet.addOperator,
addTagHandler: extensionSet.addTagHandler,
addTest: extensionSet.addTest,
loadTemplate: async (name, from = null) => {
const templateLoader = createTemplateLoader(environment);
return templateLoader(name, from)
.then((template) => {
if (template === null) {
throw createTemplateLoadingError([name]);
}
return template;
});
},
registerEscapingStrategy: (handler, name) => {
escapingStrategyHandlers[name] = handler;
},
parse: (stream, parserOptions) => {
if (!parser) {
const visitors = extensionSet.nodeVisitors;
if (options?.autoEscapingStrategy) {
const strategy = options.autoEscapingStrategy;
visitors.unshift({
enterNode: (node) => {
return node;
},
leaveNode: (node) => {
if (node.type === "template") {
node.children.body = createAutoEscapeNode(strategy, node.children.body, node.line, node.column);
}
return node;
}
})
}
parser = createParser(
extensionSet.unaryOperators,
extensionSet.binaryOperators,
extensionSet.tagHandlers,
extensionSet.nodeVisitors,
extensionSet.filters,
extensionSet.functions,
extensionSet.tests,
parserOptions || options?.parserOptions || {
strict: true,
level: 3
}
);
}
return parser.parse(stream);
},
render: (name, context, options) => {
return environment.loadTemplate(name)
.then((template) => {
return template.render(environment, context, options);
});
},
renderWithSourceMap: (name, context, options) => {
const sourceMapRuntime = createSourceMapRuntime();
return environment.loadTemplate(name)
.then((template) => {
return template.render(environment, context, {
...options,
sourceMapRuntime
});
})
.then((data) => {
const {sourceMap} = sourceMapRuntime;
return {
data,
sourceMap
};
});
},
tokenize: (source: TwingSource): TwingTokenStream => {
const level = options?.parserOptions?.level || 3;
if (!lexer) {
lexer = createLexer(
level,
extensionSet.binaryOperators,
extensionSet.unaryOperators
);
}
const stream = lexer.tokenizeSource(source);
return createTokenStream(stream.toAst(), stream.source);
}
};
return environment;
};
export const createSynchronousEnvironment = (
loader: TwingSynchronousLoader,
options?: TwingSynchronousEnvironmentOptions

@ -1,4 +1,4 @@
import type {TwingExtension, TwingSynchronousExtension} from "../extension";
import type {TwingSynchronousExtension} from "../extension";
import {createAndNode} from "../node/expression/binary/and";
import {createIsInNode} from "../node/expression/binary/is-in";
import {createIsGreaterThanNode} from "../node/expression/binary/is-greater-than";
@ -6,7 +6,7 @@ import {createIsLessThanNode} from "../node/expression/binary/is-less-than";
import {createNotNode} from "../node/expression/unary/not";
import {createNegativeNode} from "../node/expression/unary/negative";
import {createPositiveNode} from "../node/expression/unary/positive";
import {createFunction, createSynchronousFunction} from "../function";
import {createSynchronousFunction} from "../function";
import {createConcatenateNode} from "../node/expression/binary/concatenate";
import {createMultiplyNode} from "../node/expression/binary/multiply";
import {createDivideNode} from "../node/expression/binary/divide";
@ -27,68 +27,68 @@ import {createIsNotInNode} from "../node/expression/binary/is-not-in";
import {createNullishCoalescingNode} from "../node/expression/nullish-coalescing";
import {TwingBaseExpressionNode} from "../node/expression";
import {createPowerNode} from "../node/expression/binary/power";
import {createSynchronousTest, createTest} from "../test";
import {createSynchronousTest} from "../test";
import {createMatchesNode} from "../node/expression/binary/matches";
import {createStartsWithNode} from "../node/expression/binary/starts-with";
import {createEndsWithNode} from "../node/expression/binary/ends-with";
import {createFilter, createSynchronousFilter} from "../filter";
import {createSynchronousFilter} from "../filter";
import {createOperator, TwingOperator} from "../operator";
import {isEven, isEvenSynchronously} from "./core/tests/is-even";
import {isOdd, isOddSynchronously} from "./core/tests/is-odd";
import {isSameAs, isSameAsSynchronously} from "./core/tests/is-same-as";
import {isNull, isNullSynchronously} from "./core/tests/is-null";
import {isDivisibleBy, isDivisibleBySynchronously} from "./core/tests/is-divisible-by";
import {min, minSynchronously} from "./core/functions/min";
import {max, maxSynchronously} from "./core/functions/max";
import {date, dateFilterSynchronously} from "./core/filters/date";
import {dateModify, dateModifySynchronously} from "./core/filters/date-modify";
import {format, formatSynchronously} from "./core/filters/format";
import {replace, replaceSynchronously} from "./core/filters/replace";
import {numberFormat, numberFormatSynchronously} from "./core/filters/number_format";
import {abs, absSynchronously} from "./core/filters/abs";
import {url_encode, urlEncodeSynchronously} from "./core/filters/url_encode";
import {jsonEncode, jsonEncodeSynchronously} from "./core/filters/json-encode";
import {convertEncoding, convertEncodingSynchronously} from "./core/filters/convert-encoding";
import {title, titleSynchronously} from "./core/filters/title";
import {capitalize, capitalizeSynchronously} from "./core/filters/capitalize";
import {upper, upperSynchronously} from "./core/filters/upper";
import {lower, lowerSynchronously} from "./core/filters/lower";
import {striptags, striptagsSynchronously} from "./core/filters/striptags";
import {trim, trimSynchronously} from "./core/filters/trim";
import {nl2br, nl2brSynchronously} from "./core/filters/nl2br";
import {raw, rawSynchronously} from "./core/filters/raw";
import {join, joinSynchronously} from "./core/filters/join";
import {split, splitSynchronously} from "./core/filters/split";
import {sort, sortSynchronously} from "./core/filters/sort";
import {merge as mergeFilter, mergeSynchronously} from "./core/filters/merge";
import {batch, batchSynchronously} from "./core/filters/batch";
import {reverse as reverseFilter, reverseSynchronously} from "./core/filters/reverse";
import {length, lengthSynchronously} from "./core/filters/length";
import {slice as sliceFilter, sliceSynchronously} from "./core/filters/slice";
import {first as firstFilter, firstSynchronously} from "./core/filters/first";
import {last, lastSynchronously} from "./core/filters/last";
import {defaultFilter, defaultFilterSynchronously} from "./core/filters/default";
import {escape, escapeSynchronously} from "./core/filters/escape";
import {round, roundSynchronously} from "./core/filters/round";
import {include, includeSynchronously} from "./core/functions/include";
import {keys, keysSynchronously} from "./core/filters/keys";
import {spaceless, spacelessSynchronously} from "./core/filters/spaceless";
import {column, columnSynchronously} from "./core/filters/column";
import {filter, filterSynchronously} from "./core/filters/filter";
import {map, mapSynchronously} from "./core/filters/map";
import {reduce, reduceSynchronously} from "./core/filters/reduce";
import {range, rangeSynchronously} from "./core/functions/range";
import {constant, constantSynchronously} from "./core/functions/constant";
import {cycle, cycleSynchronously} from "./core/functions/cycle";
import {random, randomSynchronously} from "./core/functions/random";
import {source, sourceSynchronously} from "./core/functions/source";
import {templateFromString, templateFromStringSynchronously} from "./core/functions/template-from-string";
import {dump, dumpSynchronously} from "./core/functions/dump";
import {isEmpty, isEmptySynchronously} from "./core/tests/is-empty";
import {isIterable, isIterableSynchronously} from "./core/tests/is-iterable";
import {date as dateFunction, dateSynchronously} from "./core/functions/date";
import {isDefined, isDefinedSynchronously} from "./core/tests/is-defined";
import {isConstant, isConstantSynchronously} from "./core/tests/is-constant";
import {isEvenSynchronously} from "./core/tests/is-even";
import {isOddSynchronously} from "./core/tests/is-odd";
import {isSameAsSynchronously} from "./core/tests/is-same-as";
import {isNullSynchronously} from "./core/tests/is-null";
import {isDivisibleBySynchronously} from "./core/tests/is-divisible-by";
import {minSynchronously} from "./core/functions/min";
import {maxSynchronously} from "./core/functions/max";
import {dateFilterSynchronously} from "./core/filters/date";
import {dateModifySynchronously} from "./core/filters/date-modify";
import {formatSynchronously} from "./core/filters/format";
import {replaceSynchronously} from "./core/filters/replace";
import {numberFormatSynchronously} from "./core/filters/number_format";
import {absSynchronously} from "./core/filters/abs";
import {urlEncodeSynchronously} from "./core/filters/url_encode";
import {jsonEncodeSynchronously} from "./core/filters/json-encode";
import {convertEncodingSynchronously} from "./core/filters/convert-encoding";
import {titleSynchronously} from "./core/filters/title";
import {capitalizeSynchronously} from "./core/filters/capitalize";
import {upperSynchronously} from "./core/filters/upper";
import {lowerSynchronously} from "./core/filters/lower";
import {striptagsSynchronously} from "./core/filters/striptags";
import {trimSynchronously} from "./core/filters/trim";
import {nl2brSynchronously} from "./core/filters/nl2br";
import {rawSynchronously} from "./core/filters/raw";
import {joinSynchronously} from "./core/filters/join";
import {splitSynchronously} from "./core/filters/split";
import {sortSynchronously} from "./core/filters/sort";
import {mergeSynchronously} from "./core/filters/merge";
import {batchSynchronously} from "./core/filters/batch";
import {reverseSynchronously} from "./core/filters/reverse";
import {lengthSynchronously} from "./core/filters/length";
import {sliceSynchronously} from "./core/filters/slice";
import {firstSynchronously} from "./core/filters/first";
import {lastSynchronously} from "./core/filters/last";
import {defaultFilterSynchronously} from "./core/filters/default";
import {escapeSynchronously} from "./core/filters/escape";
import {roundSynchronously} from "./core/filters/round";
import {includeSynchronously} from "./core/functions/include";
import {keysSynchronously} from "./core/filters/keys";
import {spacelessSynchronously} from "./core/filters/spaceless";
import {columnSynchronously} from "./core/filters/column";
import {filterSynchronously} from "./core/filters/filter";
import {mapSynchronously} from "./core/filters/map";
import {reduceSynchronously} from "./core/filters/reduce";
import {rangeSynchronously} from "./core/functions/range";
import {constantSynchronously} from "./core/functions/constant";
import {cycleSynchronously} from "./core/functions/cycle";
import {randomSynchronously} from "./core/functions/random";
import {sourceSynchronously} from "./core/functions/source";
import {templateFromStringSynchronously} from "./core/functions/template-from-string";
import {dumpSynchronously} from "./core/functions/dump";
import {isEmptySynchronously} from "./core/tests/is-empty";
import {isIterableSynchronously} from "./core/tests/is-iterable";
import {dateSynchronously} from "./core/functions/date";
import {isDefinedSynchronously} from "./core/tests/is-defined";
import {isConstantSynchronously} from "./core/tests/is-constant";
import {createSpaceshipNode} from "../node/expression/binary/spaceship";
import {createHasEveryNode} from "../node/expression/binary/has-every";
import {createHasSomeNode} from "../node/expression/binary/has-some";
@ -194,445 +194,6 @@ const getOperators = (): Array<TwingOperator> => {
];
};
export const createCoreExtension = (): TwingExtension => {
return {
get filters() {
const escapeFilters = ['escape', 'e'].map((name) => {
return createFilter(name, (escape), [
{
name: 'strategy',
defaultValue: null
},
{
name: 'charset',
defaultValue: null
}
]);
});
return [
...escapeFilters,
createFilter('abs', abs, []),
createFilter('batch', batch, [
{
name: 'size'
},
{
name: 'fill',
defaultValue: null
},
{
name: 'preserve_keys',
defaultValue: true
}
]),
createFilter('capitalize', capitalize, []),
createFilter('column', column, [
{
name: 'name'
}
]),
createFilter('convert_encoding', (convertEncoding), [
{
name: 'to'
},
{
name: 'from'
}
]),
createFilter('date', date, [
{
name: 'format',
defaultValue: null
},
{
name: 'timezone',
defaultValue: null
}
]),
createFilter('date_modify', dateModify, [
{
name: 'modifier'
}
]),
createFilter('default', defaultFilter, [
{
name: 'default',
defaultValue: null
}
]),
createFilter('filter', filter, [
{
name: 'array'
},
{
name: 'arrow',
defaultValue: null
}
]),
createFilter('first', firstFilter, []),
createFilter('format', format, [], {
is_variadic: true
}),
createFilter('join', join, [
{
name: 'glue',
defaultValue: ''
},
{
name: 'and',
defaultValue: null
}
]),
createFilter('json_encode', jsonEncode, [
{
name: 'options',
defaultValue: null
}
]),
createFilter('keys', keys, []),
createFilter('last', last, []),
createFilter('length', length, []),
createFilter('lower', lower, []),
createFilter('map', map, [
{
name: 'arrow'
}
]),
createFilter('merge', mergeFilter, [
{
name: 'source'
}
]),
createFilter('nl2br', nl2br, []),
createFilter('number_format', numberFormat, [
{
name: 'decimal',
defaultValue: null
},
{
name: 'decimal_point',
defaultValue: null
},
{
name: 'thousand_sep',
defaultValue: null
}
]),
createFilter('raw', raw, []),
createFilter('reduce', reduce, [
{
name: 'arrow'
},
{
name: 'initial',
defaultValue: null
}
]),
createFilter('replace', replace, [
{
name: 'from'
}
]),
createFilter('reverse', reverseFilter, [
{
name: 'preserve_keys',
defaultValue: false
}
]),
createFilter('round', round, [
{
name: 'precision',
defaultValue: 0
},
{
name: 'method',
defaultValue: 'common'
}
]),
createFilter('slice', sliceFilter, [
{
name: 'start'
},
{
name: 'length',
defaultValue: null
},
{
name: 'preserve_keys',
defaultValue: false
}
]),
createFilter('sort', sort, [{
name: 'arrow',
defaultValue: null
}]),
createFilter('spaceless', spaceless, []),
createFilter('split', split, [
{
name: 'delimiter'
},
{
name: 'limit',
defaultValue: null
}
]),
createFilter('striptags', striptags, [
{
name: 'allowable_tags',
defaultValue: ''
}
]),
createFilter('title', title, []),
createFilter('trim', trim, [
{
name: 'character_mask',
defaultValue: null
},
{
name: 'side',
defaultValue: 'both'
}
]),
createFilter('upper', upper, []),
createFilter('url_encode', url_encode, []),
];
},
get functions() {
return [
createFunction('constant', constant, [
{name: 'name'},
{name: 'object', defaultValue: null}
]),
createFunction('cycle', cycle, [
{
name: 'values'
},
{
name: 'position'
}
]),
createFunction('date', dateFunction, [
{
name: 'date',
defaultValue: null
},
{
name: 'timezone',
defaultValue: null
}
]),
createFunction('dump', dump, [], {
is_variadic: true
}),
createFunction('include', include, [
{
name: 'template'
},
{
name: 'variables',
defaultValue: {}
},
{
name: 'with_context',
defaultValue: true
},
{
name: 'ignore_missing',
defaultValue: false
},
{
name: 'sandboxed',
defaultValue: false
}
]),
createFunction('max', max, [], {
is_variadic: true
}),
createFunction('min', min, [], {
is_variadic: true
}),
createFunction('random', random, [
{
name: 'values',
defaultValue: null
},
{
name: 'max',
defaultValue: null
}
]),
createFunction('range', range, [
{
name: 'low'
},
{
name: 'high'
},
{
name: 'step',
defaultValue: 1
}
]),
createFunction('source', source, [
{
name: 'name'
},
{
name: 'ignore_missing',
defaultValue: false
}
]),
createFunction('template_from_string', templateFromString, [
{
name: 'template'
},
{
name: 'name',
defaultValue: null
}
])
];
},
get nodeVisitors() {
return [];
},
get operators() {
return [
createOperator('not', "UNARY", 50, (operands: [TwingBaseExpressionNode, TwingBaseExpressionNode], line: number, column: number) => {
return createNotNode(operands[0], line, column);
}),
createOperator('-', "UNARY", 500, (operands: [TwingBaseExpressionNode, TwingBaseExpressionNode], line: number, column: number) => {
return createNegativeNode(operands[0], line, column);
}),
createOperator('+', "UNARY", 500, (operands: [TwingBaseExpressionNode, TwingBaseExpressionNode], line: number, column: number) => {
return createPositiveNode(operands[0], line, column);
}),
createOperator('or', "BINARY", 10, (operands: [TwingBaseExpressionNode, TwingBaseExpressionNode], line: number, column: number) => {
return createOrNode(operands, line, column);
}),
createOperator('and', "BINARY", 15, (operands: [TwingBaseExpressionNode, TwingBaseExpressionNode], line: number, column: number) => {
return createAndNode(operands, line, column);
}),
createOperator('b-or', "BINARY", 16, (operands: [TwingBaseExpressionNode, TwingBaseExpressionNode], line: number, column: number) => {
return createBitwiseOrNode(operands, line, column);
}),
createOperator('b-xor', "BINARY", 17, (operands: [TwingBaseExpressionNode, TwingBaseExpressionNode], line: number, column: number) => {
return createBitwiseXorNode(operands, line, column);
}),
createOperator('b-and', "BINARY", 18, (operands: [TwingBaseExpressionNode, TwingBaseExpressionNode], line: number, column: number) => {
return createBitwiseAndNode(operands, line, column);
}),
createOperator('==', "BINARY", 20, (operands: [TwingBaseExpressionNode, TwingBaseExpressionNode], line: number, column: number) => {
return createIsEqualNode(operands, line, column);
}),
createOperator('!=', "BINARY", 20, (operands: [TwingBaseExpressionNode, TwingBaseExpressionNode], line: number, column: number) => {
return createIsNotEqualToNode(operands, line, column);
}),
createOperator('<=>', "BINARY", 20, (operands: [TwingBaseExpressionNode, TwingBaseExpressionNode], line: number, column: number) => {
return createSpaceshipNode(operands, line, column);
}),
createOperator('<', "BINARY", 20, (operands: [TwingBaseExpressionNode, TwingBaseExpressionNode], line: number, column: number) => {
return createIsLessThanNode(operands, line, column);
}),
createOperator('<=', "BINARY", 20, (operands: [TwingBaseExpressionNode, TwingBaseExpressionNode], line: number, column: number) => {
return createIsLessThanOrEqualToNode(operands, line, column);
}),
createOperator('>', "BINARY", 20, (operands: [TwingBaseExpressionNode, TwingBaseExpressionNode], line: number, column: number) => {
return createIsGreaterThanNode(operands, line, column);
}),
createOperator('>=', "BINARY", 20, (operands: [TwingBaseExpressionNode, TwingBaseExpressionNode], line: number, column: number) => {
return createIsGreaterThanOrEqualToNode(operands, line, column);
}),
createOperator('not in', "BINARY", 20, (operands: [TwingBaseExpressionNode, TwingBaseExpressionNode], line: number, column: number) => {
return createIsNotInNode(operands, line, column);
}),
createOperator('in', "BINARY", 20, (operands: [TwingBaseExpressionNode, TwingBaseExpressionNode], line: number, column: number) => {
return createIsInNode(operands, line, column);
}),
createOperator('matches', "BINARY", 20, (operands: [TwingBaseExpressionNode, TwingBaseExpressionNode], line: number, column: number) => {
return createMatchesNode(operands, line, column);
}),
createOperator('starts with', "BINARY", 20, (operands: [TwingBaseExpressionNode, TwingBaseExpressionNode], line: number, column: number) => {
return createStartsWithNode(operands, line, column);
}),
createOperator('ends with', "BINARY", 20, (operands: [TwingBaseExpressionNode, TwingBaseExpressionNode], line: number, column: number) => {
return createEndsWithNode(operands, line, column);
}),
createOperator('has some', "BINARY", 20, (operands: [TwingBaseExpressionNode, TwingBaseExpressionNode], line: number, column: number) => {
return createHasSomeNode(operands, line, column);
}, "LEFT", 3),
createOperator('has every', "BINARY", 20, (operands: [TwingBaseExpressionNode, TwingBaseExpressionNode], line: number, column: number) => {
return createHasEveryNode(operands, line, column);
}, "LEFT", 3),
createOperator('..', "BINARY", 25, (operands: [TwingBaseExpressionNode, TwingBaseExpressionNode], line: number, column: number) => {
return createRangeNode(operands, line, column);
}),
createOperator('+', "BINARY", 30, (operands: [TwingBaseExpressionNode, TwingBaseExpressionNode], line: number, column: number) => {
return createAddNode(operands, line, column);
}),
createOperator('-', "BINARY", 30, (operands: [TwingBaseExpressionNode, TwingBaseExpressionNode], line: number, column: number) => {
return createSubtractNode(operands, line, column);
}),
createOperator('~', "BINARY", 40, (operands: [TwingBaseExpressionNode, TwingBaseExpressionNode], line: number, column: number) => {
return createConcatenateNode(operands, line, column);
}),
createOperator('*', "BINARY", 60, (operands: [TwingBaseExpressionNode, TwingBaseExpressionNode], line: number, column: number) => {
return createMultiplyNode(operands, line, column);
}),
createOperator('/', "BINARY", 60, (operands: [TwingBaseExpressionNode, TwingBaseExpressionNode], line: number, column: number) => {
return createDivideNode(operands, line, column);
}),
createOperator('//', "BINARY", 60, (operands: [TwingBaseExpressionNode, TwingBaseExpressionNode], line: number, column: number) => {
return createDivideAndFloorNode(operands, line, column);
}),
createOperator('%', "BINARY", 60, (operands: [TwingBaseExpressionNode, TwingBaseExpressionNode], line: number, column: number) => {
return createModuloNode(operands, line, column);
}),
createOperator('**', "BINARY", 200, (operands: [TwingBaseExpressionNode, TwingBaseExpressionNode], line: number, column: number) => {
return createPowerNode(operands, line, column);
}, "RIGHT"),
createOperator('??', "BINARY", 300, (operands: [TwingBaseExpressionNode, TwingBaseExpressionNode], line: number, column: number) => {
return createNullishCoalescingNode(operands, line, column);
}, "RIGHT")
];
},
get tagHandlers() {
return [];
},
get tests() {
return [
createTest('constant', isConstant, [
{
name: 'constant'
},
{
name: 'object',
defaultValue: null
}
]),
createTest('divisible by', isDivisibleBy, [
{
name: 'divisor'
}
]),
createTest('defined', isDefined, []),
createTest('empty', isEmpty, []),
createTest('even', isEven, []),
createTest('iterable', isIterable, []),
createTest('none', isNull, []),
createTest('null', isNull, []),
createTest('odd', isOdd, []),
createTest('same as', isSameAs, [
{
name: 'comparand'
}
]),
];
}
};
};
export const createSynchronousCoreExtension = (): TwingSynchronousExtension => {
return {
get filters() {

@ -22,76 +22,7 @@ export const createDateTime = (
input: Date | DateTime | number | string | null,
timezone: string | null | false
): Promise<DateTime> => {
const _do = (): DateTime => {
let result: DateTime;
if (input === null) {
result = DateTime.local();
}
else if (typeof input === 'number') {
result = DateTime.fromMillis(input * 1000);
}
else if (typeof input === 'string') {
if (input === 'now') {
result = DateTime.local();
}
else {
result = DateTime.fromISO(input, {
setZone: true
});
if (!result.isValid) {
result = DateTime.fromRFC2822(input, {
setZone: true
});
}
if (!result.isValid) {
result = DateTime.fromSQL(input, {
setZone: true
});
}
if (!result.isValid && /^-{0,1}\d+$/.test(input)) {
result = DateTime.fromMillis(Number.parseInt(input) * 1000, {
setZone: true
});
}
if (!result.isValid) {
result = modifyDate(input);
}
}
}
else if (input instanceof DateTime) {
result = input;
}
else {
result = DateTime.fromJSDate(input);
}
if (!result || !result.isValid) {
throw new Error(`Failed to parse date "${input}".`);
}
// now let's apply timezone
// determine the timezone
if (timezone !== false) {
if (timezone === null) {
timezone = defaultTimezone;
}
result = result.setZone(timezone);
}
return result;
};
try {
return Promise.resolve(_do());
} catch (e) {
return Promise.reject(e);
}
return Promise.resolve(createDateTimeSynchronously(defaultTimezone, input, timezone))
}
export const date: TwingCallable = (