forked from github-mirror/twing
Remove the remaining async code
This commit is contained in:
parent
0296432bee
commit
e7aae35ef6
meta.json
src/main
lib.ts
lib
callable-wrapper.tscontext.tsenvironment.tsexecution-context.tsextension-set.tsextension.tsfilter.tsfunction.ts
extension/core
filters
abs.tsbatch.tscapitalize.tscolumn.tsconvert-encoding.tsdate-modify.tsdate.tsdefault.tsescape.tsfilter.tsfirst.tsformat.tsjoin.tsjson-encode.tskeys.tslast.tslength.tslower.tsmap.tsmerge.tsnl2br.tsnumber_format.tsraw.tsreduce.tsreplace.tsreverse.tsround.tsslice.tssort.tsspaceless.tssplit.tsstriptags.tstitle.tstrim.tsupper.tsurl_encode.ts
functions
constant.tscycle.tsdate.tsdump.tsinclude.tsmax.tsmin.tsrandom.tsrange.tssource.tstemplate-from-string.ts
tests
helpers
asort.tscompare.tsescape-value.tsget-attribute.tsget-constant.tsget-filter.tsget-function.tsget-test.tsmap-like.tsmerge-iterables.tssort.tstraceable-method.ts
node-executor.tsnode-executor
@ -233,7 +233,7 @@ export {createEmbedNode} from "./lib/node/include/embed";
|
||||
export {createIncludeNode} from "./lib/node/include/include";
|
||||
|
||||
// node executors
|
||||
export {executeNodeSynchronously, type TwingNodeExecutor, type TwingSynchronousNodeExecutor} from "./lib/node-executor";
|
||||
export {executeNodeSynchronously, type TwingSynchronousNodeExecutor} from "./lib/node-executor";
|
||||
|
||||
// tag handlers
|
||||
export type {TwingTagHandler, TwingTokenParser} from "./lib/tag-handler";
|
||||
@ -263,18 +263,17 @@ export {createWithTagHandler} from "./lib/tag-handler/with";
|
||||
|
||||
// core
|
||||
export type {
|
||||
TwingCallable, TwingCallableArgument, TwingCallableWrapperOptions, TwingCallableWrapper, TwingSynchronousCallable, TwingSynchronousCallableWrapper
|
||||
TwingCallableArgument, TwingCallableWrapperOptions, TwingSynchronousCallable, TwingSynchronousCallableWrapper
|
||||
} from "./lib/callable-wrapper";
|
||||
export {type TwingContext, createContext} from "./lib/context";
|
||||
export type {TwingEnvironment, TwingEnvironmentOptions, TwingNumberFormat, TwingSynchronousEnvironment, TwingSynchronousEnvironmentOptions} from "./lib/environment";
|
||||
export type {TwingEnvironmentOptions, TwingNumberFormat, TwingSynchronousEnvironment, TwingSynchronousEnvironmentOptions} from "./lib/environment";
|
||||
export type {
|
||||
TwingEscapingStrategy, TwingEscapingStrategyHandler, TwingEscapingStrategyResolver
|
||||
} from "./lib/escaping-strategy";
|
||||
export type {TwingExecutionContext, TwingSynchronousExecutionContext} from "./lib/execution-context";
|
||||
export type {TwingExtension, TwingSynchronousExtension} from "./lib/extension";
|
||||
export type {TwingSynchronousExecutionContext} from "./lib/execution-context";
|
||||
export type {TwingSynchronousExtension} from "./lib/extension";
|
||||
export type {TwingExtensionSet} from "./lib/extension-set";
|
||||
export type {TwingFilter, TwingSynchronousFilter} from "./lib/filter";
|
||||
export type {TwingFunction, TwingSynchronousFunction} from "./lib/function";
|
||||
export type {TwingSynchronousFilter} from "./lib/filter";
|
||||
export type {TwingSynchronousFunction} from "./lib/function";
|
||||
export type {TwingLexer} from "./lib/lexer";
|
||||
export type {TwingNodeVisitor} from "./lib/node-visitor";
|
||||
export type {
|
||||
@ -286,16 +285,12 @@ export type {TwingSandboxSecurityPolicy} from "./lib/sandbox/security-policy";
|
||||
export type {TwingSource} from "./lib/source";
|
||||
export type {TwingSourceMapRuntime} from "./lib/source-map-runtime";
|
||||
export type {
|
||||
TwingTemplateAliases,
|
||||
TwingTemplateBlockMap,
|
||||
TwingTemplateBlockHandler,
|
||||
TwingTemplateMacroHandler,
|
||||
TwingSynchronousTemplateAliases,
|
||||
TwingSynchronousTemplateBlockHandler,
|
||||
TwingSynchronousTemplateBlockMap,
|
||||
TwingSynchronousTemplateMacroHandler
|
||||
} from "./lib/template";
|
||||
export type {TwingTest, TwingSynchronousTest} from "./lib/test";
|
||||
export type {TwingSynchronousTest} from "./lib/test";
|
||||
export type {TwingTokenStream} from "./lib/token-stream";
|
||||
|
||||
export {createSynchronousEnvironment} from "./lib/environment";
|
||||
|
@ -1,6 +1,5 @@
|
||||
import {TwingExecutionContext, TwingSynchronousExecutionContext} from "./execution-context";
|
||||
import {TwingSynchronousExecutionContext} from "./execution-context";
|
||||
|
||||
export type TwingCallable<A extends Array<any> = any, R = any> = (executionContext: TwingExecutionContext, ...args: A) => Promise<R>;
|
||||
export type TwingSynchronousCallable<A extends Array<any> = any, R = any> = (executionContext: TwingSynchronousExecutionContext, ...args: A) => R;
|
||||
|
||||
export type TwingCallableArgument = {
|
||||
@ -14,10 +13,10 @@ export type TwingCallableWrapperOptions = {
|
||||
alternative?: string;
|
||||
}
|
||||
|
||||
export interface TwingCallableWrapper {
|
||||
export interface TwingSynchronousCallableWrapper {
|
||||
readonly acceptedArguments: Array<TwingCallableArgument>;
|
||||
readonly alternative: string | undefined;
|
||||
readonly callable: TwingCallable;
|
||||
readonly callable: TwingSynchronousCallable;
|
||||
readonly deprecatedVersion: string | boolean | undefined;
|
||||
readonly isDeprecated: boolean;
|
||||
readonly isVariadic: boolean;
|
||||
@ -30,51 +29,6 @@ export interface TwingCallableWrapper {
|
||||
nativeArguments: Array<string>;
|
||||
}
|
||||
|
||||
export interface TwingSynchronousCallableWrapper extends Omit<TwingCallableWrapper, "callable"> {
|
||||
readonly callable: TwingSynchronousCallable;
|
||||
}
|
||||
|
||||
export const createCallableWrapper = (
|
||||
name: string,
|
||||
callable: TwingCallable,
|
||||
acceptedArguments: Array<TwingCallableArgument>,
|
||||
options: TwingCallableWrapperOptions
|
||||
): TwingCallableWrapper => {
|
||||
let nativeArguments: Array<string> = [];
|
||||
|
||||
const callableWrapper = {
|
||||
get callable() {
|
||||
return callable;
|
||||
},
|
||||
get name() {
|
||||
return name;
|
||||
},
|
||||
get acceptedArguments() {
|
||||
return acceptedArguments;
|
||||
},
|
||||
get alternative() {
|
||||
return options.alternative;
|
||||
},
|
||||
get deprecatedVersion() {
|
||||
return options.deprecated;
|
||||
},
|
||||
get isDeprecated() {
|
||||
return options.deprecated ? true : false;
|
||||
},
|
||||
get isVariadic() {
|
||||
return options.is_variadic || false;
|
||||
},
|
||||
get nativeArguments() {
|
||||
return nativeArguments;
|
||||
},
|
||||
set nativeArguments(values) {
|
||||
nativeArguments = values;
|
||||
}
|
||||
};
|
||||
|
||||
return callableWrapper;
|
||||
};
|
||||
|
||||
export const createSynchronousCallableWrapper = (
|
||||
name: string,
|
||||
callable: TwingSynchronousCallable,
|
||||
|
@ -1,71 +0,0 @@
|
||||
export interface TwingContext<K extends string, V> {
|
||||
readonly size: number;
|
||||
|
||||
[Symbol.iterator](): IterableIterator<[string, V]>;
|
||||
|
||||
clone(): TwingContext<K, V>;
|
||||
|
||||
delete(key: K): boolean;
|
||||
|
||||
entries(): IterableIterator<[string, V]>;
|
||||
|
||||
get(key: K): V | undefined;
|
||||
|
||||
has(key: K): boolean;
|
||||
|
||||
set(key: K, value: V): TwingContext<K, V>;
|
||||
|
||||
values(): IterableIterator<V>;
|
||||
}
|
||||
|
||||
export const createContext = <K extends string, V>(
|
||||
container: Map<K, V> = new Map()
|
||||
): TwingContext<K, V> => {
|
||||
const context: TwingContext<K, V> = {
|
||||
get size() {
|
||||
return container.size;
|
||||
},
|
||||
[Symbol.iterator]: () => {
|
||||
return container[Symbol.iterator]();
|
||||
},
|
||||
clone: () => {
|
||||
const clonedContainer: Map<K, V> = new Map();
|
||||
|
||||
for (const [key, value] of container) {
|
||||
clonedContainer.set(key, value);
|
||||
}
|
||||
|
||||
return createContext(clonedContainer);
|
||||
},
|
||||
delete: (key) => {
|
||||
return container.delete(key);
|
||||
},
|
||||
entries: () => {
|
||||
return container.entries();
|
||||
},
|
||||
get: (key) => {
|
||||
return container.get(key);
|
||||
},
|
||||
has: (key) => {
|
||||
return container.has(key);
|
||||
},
|
||||
set: (key, value) => {
|
||||
container.set(key, value);
|
||||
|
||||
return context;
|
||||
},
|
||||
values: () => {
|
||||
return container.values();
|
||||
}
|
||||
};
|
||||
|
||||
return context;
|
||||
};
|
||||
|
||||
export const getEntries = <V>(context: Record<string, V>): IterableIterator<[string, V]> => {
|
||||
return Object.entries(context)[Symbol.iterator]();
|
||||
};
|
||||
|
||||
export const getValues = <V>(context: Record<string, V>): Array<V> => {
|
||||
return Object.values(context);
|
||||
};
|
@ -1,11 +1,11 @@
|
||||
import {TwingTagHandler} from "./tag-handler";
|
||||
import {TwingNodeVisitor} from "./node-visitor";
|
||||
import {createExtensionSet} from "./extension-set";
|
||||
import {TwingFilter, TwingSynchronousFilter} from "./filter";
|
||||
import {TwingSynchronousFilter} from "./filter";
|
||||
import {createParser, TwingParser, TwingParserOptions} from "./parser";
|
||||
import {TwingLoader, TwingSynchronousLoader} from "./loader";
|
||||
import {TwingSynchronousTest, TwingTest} from "./test";
|
||||
import {TwingFunction, TwingSynchronousFunction} from "./function";
|
||||
import {TwingSynchronousLoader} from "./loader";
|
||||
import {TwingSynchronousTest} from "./test";
|
||||
import {TwingSynchronousFunction} from "./function";
|
||||
import {TwingOperator} from "./operator";
|
||||
import {TwingEscapingStrategy, TwingEscapingStrategyHandler} from "./escaping-strategy";
|
||||
import {createHtmlEscapingStrategyHandler} from "./escaping-stragegy/html";
|
||||
@ -15,17 +15,17 @@ import {createUrlEscapingStrategyHandler} from "./escaping-stragegy/url";
|
||||
import {createHtmlAttributeEscapingStrategyHandler} from "./escaping-stragegy/html-attribute";
|
||||
import {TwingSource} from "./source";
|
||||
import {createTokenStream, TwingTokenStream} from "./token-stream";
|
||||
import {TwingExtension, TwingSynchronousExtension} from "./extension";
|
||||
import {TwingSynchronousExtension} from "./extension";
|
||||
import {TwingTemplateNode} from "./node/template";
|
||||
import {RawSourceMap} from "source-map";
|
||||
import {createSourceMapRuntime} from "./source-map-runtime";
|
||||
import {createSandboxSecurityPolicy, TwingSandboxSecurityPolicy} from "./sandbox/security-policy";
|
||||
import {TwingSynchronousTemplate, TwingTemplate} from "./template";
|
||||
import {TwingSynchronousTemplate} from "./template";
|
||||
import {Settings as DateTimeSettings} from "luxon";
|
||||
import {createLexer, type TwingLexer} from "./lexer";
|
||||
import {TwingCache, TwingSynchronousCache} from "./cache";
|
||||
import {createSynchronousCoreExtension} from "./extension/core";
|
||||
import {createAutoEscapeNode, createTemplateLoadingError, type TwingContext} from "../lib";
|
||||
import {createAutoEscapeNode, createTemplateLoadingError} from "../lib";
|
||||
import {createSynchronousTemplateLoader} from "./template-loader";
|
||||
|
||||
export type TwingNumberFormat = {
|
||||
@ -67,93 +67,6 @@ export type TwingSynchronousEnvironmentOptions = Omit<TwingEnvironmentOptions, "
|
||||
cache?: TwingSynchronousCache;
|
||||
};
|
||||
|
||||
export interface TwingEnvironment {
|
||||
readonly cache: TwingCache | null;
|
||||
readonly charset: string;
|
||||
readonly dateFormat: string;
|
||||
readonly dateIntervalFormat: string;
|
||||
readonly escapingStrategyHandlers: Record<TwingEscapingStrategy, TwingEscapingStrategyHandler>;
|
||||
readonly numberFormat: TwingNumberFormat;
|
||||
readonly filters: Map<string, TwingFilter>;
|
||||
readonly functions: Map<string, TwingFunction>;
|
||||
readonly globals: TwingContext<string, any>;
|
||||
readonly loader: TwingLoader | TwingSynchronousLoader;
|
||||
readonly sandboxPolicy: TwingSandboxSecurityPolicy;
|
||||
readonly tests: Map<string, TwingTest>;
|
||||
readonly timezone: string;
|
||||
|
||||
/**
|
||||
* Convenient method...
|
||||
*
|
||||
* @param extension
|
||||
*/
|
||||
addExtension(extension: TwingExtension): void;
|
||||
|
||||
addFilter(filter: TwingFilter): void;
|
||||
|
||||
addFunction(aFunction: TwingFunction): void;
|
||||
|
||||
addNodeVisitor(visitor: TwingNodeVisitor): void;
|
||||
|
||||
addOperator(operator: TwingOperator): void;
|
||||
|
||||
addTagHandler(parser: TwingTagHandler): void;
|
||||
|
||||
addTest(test: TwingTest): void;
|
||||
|
||||
/**
|
||||
* Loads a template by its name.
|
||||
*
|
||||
* @param name The name of the template to load
|
||||
* @param from The name of the template that requested the load
|
||||
*
|
||||
* @throws {Error} When the template cannot be found
|
||||
* @throws {TwingParsingError} When an error occurred during the parsing of the source
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
loadTemplate(name: string, from?: string | null): Promise<TwingTemplate>;
|
||||
|
||||
/**
|
||||
* Converts a token list to a template.
|
||||
*
|
||||
* @param {TwingTokenStream} stream
|
||||
* @param {TwingParserOptions} options
|
||||
* *
|
||||
* @throws {TwingParsingError} When the token stream is syntactically or semantically wrong
|
||||
*/
|
||||
parse(stream: TwingTokenStream, options?: TwingParserOptions): TwingTemplateNode;
|
||||
|
||||
/**
|
||||
* Convenient method that renders a template from its name.
|
||||
*/
|
||||
render(name: string, context: Record<string, any>, options?: {
|
||||
sandboxed?: boolean;
|
||||
strict?: boolean;
|
||||
}): Promise<string>;
|
||||
|
||||
/**
|
||||
* Convenient method that renders a template from its name and returns both the render result and its belonging source map.
|
||||
*/
|
||||
renderWithSourceMap(name: string, context: Record<string, any>, options?: {
|
||||
sandboxed?: boolean;
|
||||
strict?: boolean;
|
||||
}): Promise<{
|
||||
data: string;
|
||||
sourceMap: RawSourceMap;
|
||||
}>;
|
||||
|
||||
registerEscapingStrategy(handler: TwingEscapingStrategyHandler, name: string): void;
|
||||
|
||||
/**
|
||||
* Tokenizes a source code.
|
||||
*
|
||||
* @param {TwingSource} source The source to tokenize
|
||||
* @return {TwingTokenStream}
|
||||
*/
|
||||
tokenize(source: TwingSource): TwingTokenStream;
|
||||
}
|
||||
|
||||
export interface TwingSynchronousEnvironment {
|
||||
readonly cache: TwingSynchronousCache | null;
|
||||
readonly charset: string;
|
||||
|
@ -1,28 +1,10 @@
|
||||
import type {TwingTemplate, TwingTemplateAliases, TwingTemplateBlockMap} from "./template";
|
||||
import type {TwingContext} from "./context";
|
||||
import type {TwingOutputBuffer} from "./output-buffer";
|
||||
import type {TwingSourceMapRuntime} from "./source-map-runtime";
|
||||
import type {TwingEnvironment, TwingSynchronousEnvironment} from "./environment";
|
||||
import type {TwingNodeExecutor} from "./node-executor";
|
||||
import type {TwingTemplateLoader} from "./template-loader";
|
||||
import type {TwingSynchronousEnvironment} from "./environment";
|
||||
import {TwingSynchronousNodeExecutor} from "./node-executor";
|
||||
import {TwingSynchronousTemplate, TwingSynchronousTemplateAliases, TwingSynchronousTemplateBlockMap} from "./template";
|
||||
import {TwingSynchronousTemplateLoader} from "./template-loader";
|
||||
|
||||
export type TwingExecutionContext = {
|
||||
aliases: TwingTemplateAliases;
|
||||
blocks: TwingTemplateBlockMap;
|
||||
context: TwingContext<any, any>;
|
||||
environment: TwingEnvironment;
|
||||
nodeExecutor: TwingNodeExecutor;
|
||||
outputBuffer: TwingOutputBuffer;
|
||||
sandboxed: boolean;
|
||||
sourceMapRuntime?: TwingSourceMapRuntime;
|
||||
strict: boolean;
|
||||
template: TwingTemplate;
|
||||
templateLoader: TwingTemplateLoader;
|
||||
};
|
||||
|
||||
export type TwingSynchronousExecutionContext = {
|
||||
aliases: TwingSynchronousTemplateAliases;
|
||||
blocks: TwingSynchronousTemplateBlockMap;
|
||||
|
@ -1,9 +1,9 @@
|
||||
import {TwingTagHandler} from "./tag-handler";
|
||||
import {TwingNodeVisitor} from "./node-visitor";
|
||||
import {TwingOperator} from "./operator";
|
||||
import type {TwingExtension, TwingSynchronousExtension} from "./extension";
|
||||
import type {TwingSynchronousExtension} from "./extension";
|
||||
|
||||
export interface TwingExtensionSet<Extension extends TwingExtension | TwingSynchronousExtension> {
|
||||
export interface TwingExtensionSet<Extension extends TwingSynchronousExtension> {
|
||||
readonly binaryOperators: Array<TwingOperator>;
|
||||
readonly filters: Map<string, Extension["filters"][number]>;
|
||||
readonly functions: Map<string, Extension["functions"][number]>;
|
||||
@ -27,7 +27,7 @@ export interface TwingExtensionSet<Extension extends TwingExtension | TwingSynch
|
||||
addTest(test: Extension["tests"][number]): void;
|
||||
}
|
||||
|
||||
export const createExtensionSet = <Extension extends TwingExtension | TwingSynchronousExtension> (): TwingExtensionSet<Extension> => {
|
||||
export const createExtensionSet = <Extension extends TwingSynchronousExtension> (): TwingExtensionSet<Extension> => {
|
||||
const binaryOperators: Array<TwingOperator> = [];
|
||||
const filters: Map<string, Extension["filters"][number]> = new Map();
|
||||
const functions: Map<string, Extension["functions"][number]> = new Map();
|
||||
|
@ -1,54 +1,10 @@
|
||||
import {TwingTagHandler} from "./tag-handler";
|
||||
import {TwingNodeVisitor} from "./node-visitor";
|
||||
import {TwingFilter, TwingSynchronousFilter} from "./filter";
|
||||
import {TwingFunction, TwingSynchronousFunction} from "./function";
|
||||
import {TwingSynchronousTest, TwingTest} from "./test";
|
||||
import {TwingSynchronousFilter} from "./filter";
|
||||
import {TwingSynchronousFunction} from "./function";
|
||||
import {TwingSynchronousTest} from "./test";
|
||||
import {TwingOperator} from "./operator";
|
||||
|
||||
export interface TwingExtension {
|
||||
/**
|
||||
* Returns a list of filters to add to the existing list.
|
||||
*
|
||||
* @return Array<TwingFilter>
|
||||
*/
|
||||
readonly filters: Array<TwingFilter>;
|
||||
|
||||
/**
|
||||
* Returns a list of functions to add to the existing list.
|
||||
*
|
||||
* @return Array<TwingFunction>
|
||||
*/
|
||||
readonly functions: Array<TwingFunction>;
|
||||
|
||||
/**
|
||||
* Returns the node visitor instances to add to the existing list.
|
||||
*
|
||||
* @return Array<TwingNodeVisitor>
|
||||
*/
|
||||
readonly nodeVisitors: Array<TwingNodeVisitor>;
|
||||
|
||||
/**
|
||||
* Returns a list of operators to add to the existing list.
|
||||
*
|
||||
* @return TwingOperator[]
|
||||
*/
|
||||
readonly operators: Array<TwingOperator>;
|
||||
|
||||
/**
|
||||
* Returns the token parser instances to add to the existing list.
|
||||
*
|
||||
* @return Array<TwingTagHandler>
|
||||
*/
|
||||
readonly tagHandlers: Array<TwingTagHandler>;
|
||||
|
||||
/**
|
||||
* Returns a list of tests to add to the existing list.
|
||||
*
|
||||
* @returns Array<TwingTest>
|
||||
*/
|
||||
readonly tests: Array<TwingTest>;
|
||||
}
|
||||
|
||||
export interface TwingSynchronousExtension {
|
||||
/**
|
||||
* Returns a list of filters to add to the existing list.
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
/**
|
||||
* Return the absolute value of a number.
|
||||
@ -6,10 +6,6 @@ import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper
|
||||
* @param _executionContext
|
||||
* @param x
|
||||
*/
|
||||
export const abs: TwingCallable = (_executionContext, x: number): Promise<number> => {
|
||||
return Promise.resolve(Math.abs(x));
|
||||
};
|
||||
|
||||
export const absSynchronously: TwingSynchronousCallable = (_executionContext, x: number): number => {
|
||||
return Math.abs(x);
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {chunk, chunkSynchronously} from "../../../helpers/chunk";
|
||||
import {chunkSynchronously} from "../../../helpers/chunk";
|
||||
import {fillMap} from "../../../helpers/fill-map";
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
/**
|
||||
* Batches item.
|
||||
@ -13,29 +13,6 @@ import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper
|
||||
*
|
||||
* @returns Promise<Map<any, any>[]>
|
||||
*/
|
||||
export const batch: TwingCallable<[
|
||||
items: Array<any>,
|
||||
size: number,
|
||||
fill: any,
|
||||
preserveKeys: boolean
|
||||
], Array<Map<any, any>>> = (_executionContext, items, size, fill, preserveKeys) => {
|
||||
if ((items === null) || (items === undefined)) {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
return chunk(items, size, preserveKeys)
|
||||
.then((chunks) => {
|
||||
if (fill !== null && chunks.length) {
|
||||
const last = chunks.length - 1;
|
||||
const lastChunk: Map<any, any> = chunks[last];
|
||||
|
||||
fillMap(lastChunk, size, fill);
|
||||
}
|
||||
|
||||
return chunks;
|
||||
});
|
||||
};
|
||||
|
||||
export const batchSynchronously: TwingSynchronousCallable<[
|
||||
items: Array<any>,
|
||||
size: number,
|
||||
|
@ -1,5 +1,4 @@
|
||||
import type {TwingMarkup} from "../../../markup";
|
||||
import type {TwingCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
const words: (value: string) => string = require('capitalize');
|
||||
@ -11,16 +10,6 @@ const words: (value: string) => string = require('capitalize');
|
||||
*
|
||||
* @returns {Promise<string>} The capitalized string
|
||||
*/
|
||||
export const capitalize: TwingCallable<[
|
||||
string: string | TwingMarkup
|
||||
], string> = (_executionContext, string) => {
|
||||
if ((string === null) || (string === undefined) || string === '') {
|
||||
return Promise.resolve(string);
|
||||
}
|
||||
|
||||
return Promise.resolve(words(string.toString()));
|
||||
};
|
||||
|
||||
export const capitalizeSynchronously: TwingSynchronousCallable<[
|
||||
string: string | TwingMarkup
|
||||
], string> = (_executionContext, string) => {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {isTraversable} from "../../../helpers/is-traversable";
|
||||
import {iteratorToMap} from "../../../helpers/iterator-to-map";
|
||||
import {isPlainObject} from "../../../helpers/is-plain-object";
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
/**
|
||||
* Return the values from a single column in the input array.
|
||||
@ -11,30 +11,6 @@ import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper
|
||||
*
|
||||
* @return {Promise<Array<any>>} The array of values
|
||||
*/
|
||||
export const column: TwingCallable = (_executionContext, thing: any, columnKey: any): Promise<Array<any>> => {
|
||||
let map: Map<any, any>;
|
||||
|
||||
if (!isTraversable(thing) || isPlainObject(thing)) {
|
||||
return Promise.reject(new Error(`The column filter only works with arrays or "Traversable", got "${typeof thing}" as first argument.`));
|
||||
} else {
|
||||
map = iteratorToMap(thing);
|
||||
}
|
||||
|
||||
const result: Array<any> = [];
|
||||
|
||||
for (const value of map.values()) {
|
||||
const valueAsMap: Map<any, any> = iteratorToMap(value);
|
||||
|
||||
for (const [key, value] of valueAsMap) {
|
||||
if (key === columnKey) {
|
||||
result.push(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve(result);
|
||||
};
|
||||
|
||||
export const columnSynchronously: TwingSynchronousCallable = (_executionContext, thing: any, columnKey: any): Array<any> => {
|
||||
let map: Map<any, any>;
|
||||
|
||||
|
@ -1,13 +1,5 @@
|
||||
import {iconv} from "../../../helpers/iconv";
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
export const convertEncoding: TwingCallable<[
|
||||
value: string | Buffer,
|
||||
to: string,
|
||||
from: string
|
||||
], Buffer> = (_executionContext, value, to, from) => {
|
||||
return Promise.resolve(iconv(from, to, Buffer.from(value)));
|
||||
};
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
export const convertEncodingSynchronously: TwingSynchronousCallable<[
|
||||
value: string | Buffer,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {DateTime} from "luxon";
|
||||
import {createDateTime, createDateTimeSynchronously} from "../functions/date";
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {createDateTimeSynchronously} from "../functions/date";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
/**
|
||||
* Returns a new date object modified.
|
||||
@ -15,33 +15,6 @@ import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper
|
||||
*
|
||||
* @returns {Promise<DateTime>} A new date object
|
||||
*/
|
||||
export const dateModify: TwingCallable = (
|
||||
executionContext,
|
||||
date: Date | DateTime | string,
|
||||
modifier: string
|
||||
): Promise<DateTime> => {
|
||||
const {environment} = executionContext;
|
||||
const {timezone: defaultTimezone} = environment;
|
||||
|
||||
return createDateTime(defaultTimezone, date, null)
|
||||
.then((dateTime) => {
|
||||
let regExp = new RegExp(/(\+|-)([0-9])(.*)/);
|
||||
let parts = regExp.exec(modifier)!;
|
||||
|
||||
let operator: string = parts[1];
|
||||
let operand: number = Number.parseInt(parts[2]);
|
||||
let unit: string = parts[3].trim();
|
||||
|
||||
let duration: any = {};
|
||||
|
||||
duration[unit] = operator === '-' ? -operand : operand;
|
||||
|
||||
dateTime = dateTime.plus(duration);
|
||||
|
||||
return dateTime;
|
||||
});
|
||||
};
|
||||
|
||||
export const dateModifySynchronously: TwingSynchronousCallable = (
|
||||
executionContext,
|
||||
date: Date | DateTime | string,
|
||||
|
@ -1,8 +1,8 @@
|
||||
import {DateTime, Duration} from "luxon";
|
||||
import {formatDuration} from "../../../helpers/format-duration";
|
||||
import {formatDateTime} from "../../../helpers/format-date-time";
|
||||
import {date as createDate, dateSynchronously as createDateSynchronously} from "../functions/date";
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {dateSynchronously as createDateSynchronously} from "../functions/date";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
/**
|
||||
* Converts a date to the given format.
|
||||
@ -18,33 +18,6 @@ import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper
|
||||
*
|
||||
* @return {Promise<string>} The formatted date
|
||||
*/
|
||||
export const date: TwingCallable = (
|
||||
executionContext,
|
||||
date: DateTime | Duration | string,
|
||||
format: string | null,
|
||||
timezone: string | null | false
|
||||
): Promise<string> => {
|
||||
const {environment} = executionContext;
|
||||
const {dateFormat, dateIntervalFormat} = environment;
|
||||
|
||||
return createDate(executionContext, date, timezone)
|
||||
.then((date) => {
|
||||
if (date instanceof Duration) {
|
||||
if (format === null) {
|
||||
format = dateIntervalFormat;
|
||||
}
|
||||
|
||||
return Promise.resolve(formatDuration(date, format));
|
||||
}
|
||||
|
||||
if (format === null) {
|
||||
format = dateFormat;
|
||||
}
|
||||
|
||||
return Promise.resolve(formatDateTime(date, format));
|
||||
});
|
||||
};
|
||||
|
||||
export const dateFilterSynchronously: TwingSynchronousCallable = (
|
||||
executionContext,
|
||||
date: DateTime | Duration | string,
|
||||
|
@ -1,22 +1,6 @@
|
||||
import {isEmpty, isEmptySynchronously} from "../tests/is-empty";
|
||||
import type {TwingCallable} from "../../../callable-wrapper";
|
||||
import {isEmptySynchronously} from "../tests/is-empty";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
export const defaultFilter: TwingCallable<[
|
||||
value: any,
|
||||
defaultValue: any | null
|
||||
]> = (executionContext, value, defaultValue) => {
|
||||
return isEmpty(executionContext, value)
|
||||
.then((isEmpty) => {
|
||||
if (isEmpty) {
|
||||
return Promise.resolve(defaultValue);
|
||||
}
|
||||
else {
|
||||
return Promise.resolve(value);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const defaultFilterSynchronously: TwingSynchronousCallable<[
|
||||
value: any,
|
||||
defaultValue: any | null
|
||||
|
@ -1,33 +1,7 @@
|
||||
import {createMarkup, TwingMarkup} from "../../../markup";
|
||||
import type {TwingCallable} from "../../../callable-wrapper";
|
||||
import {escapeValue, escapeValueSynchronously} from "../../../helpers/escape-value";
|
||||
import {escapeValueSynchronously} from "../../../helpers/escape-value";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
export const escape: TwingCallable<[
|
||||
value: string | TwingMarkup | null,
|
||||
strategy: string | null
|
||||
], string | boolean | TwingMarkup | null> = (
|
||||
executionContext,
|
||||
value,
|
||||
strategy
|
||||
) => {
|
||||
if (strategy === null) {
|
||||
strategy = "html";
|
||||
}
|
||||
|
||||
const {template, environment} = executionContext;
|
||||
|
||||
// todo: probably we need to use traceable method
|
||||
return escapeValue(template, environment, value, strategy, environment.charset)
|
||||
.then((value) => {
|
||||
if (typeof value === "string") {
|
||||
return createMarkup(value, environment.charset);
|
||||
}
|
||||
|
||||
return value;
|
||||
});
|
||||
};
|
||||
|
||||
export const escapeSynchronously: TwingSynchronousCallable<[
|
||||
value: string | TwingMarkup | null,
|
||||
strategy: string | null
|
||||
|
@ -1,19 +1,5 @@
|
||||
import {iteratorToMap} from "../../../helpers/iterator-to-map";
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
export const filter: TwingCallable = async (_executionContext, map: any, callback: (...args: Array<any>) => Promise<boolean>): Promise<Map<any, any>> => {
|
||||
const result: Map<any, any> = new Map();
|
||||
|
||||
map = iteratorToMap(map);
|
||||
|
||||
for (const [key, value] of map) {
|
||||
if (await callback(value)) {
|
||||
result.set(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve(result);
|
||||
};
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
export const filterSynchronously: TwingSynchronousCallable = (_executionContext, map: any, callback: (...args: Array<any>) => boolean): Map<any, any> => {
|
||||
const result: Map<any, any> = new Map();
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {getFirstValue} from "../../../helpers/get-first-value";
|
||||
import {slice, sliceSynchronously} from "./slice";
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {sliceSynchronously} from "./slice";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
/**
|
||||
* Returns the first element of the item.
|
||||
@ -10,15 +10,6 @@ import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper
|
||||
*
|
||||
* @returns {Promise<any>} The first element of the item
|
||||
*/
|
||||
export const first: TwingCallable<[
|
||||
item: any
|
||||
]> = (executionContext, item) => {
|
||||
return slice(executionContext, item, 0, 1, false)
|
||||
.then((elements) => {
|
||||
return typeof elements === 'string' ? elements : getFirstValue(elements);
|
||||
});
|
||||
}
|
||||
|
||||
export const firstSynchronously: TwingSynchronousCallable<[
|
||||
item: any
|
||||
]> = (executionContext, item) => {
|
||||
|
@ -1,13 +1,7 @@
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
const sprintf = require('locutus/php/strings/sprintf');
|
||||
|
||||
export const format: TwingCallable = (_executionContext, ...args: any[]): Promise<string> => {
|
||||
return Promise.resolve(sprintf(...args.map((arg) => {
|
||||
return arg.toString();
|
||||
})));
|
||||
};
|
||||
|
||||
export const formatSynchronously: TwingSynchronousCallable = (_executionContext, ...args: any[]): string => {
|
||||
return sprintf(...args.map((arg) => {
|
||||
return arg.toString();
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {isTraversable} from "../../../helpers/is-traversable";
|
||||
import {iteratorToArray} from "../../../helpers/iterator-to-array";
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
/**
|
||||
* Joins the values to a string.
|
||||
@ -22,49 +22,6 @@ import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper
|
||||
*
|
||||
* @returns {Promise<string>} The concatenated string
|
||||
*/
|
||||
export const join: TwingCallable<[
|
||||
value: any,
|
||||
glue: string,
|
||||
and: string | null
|
||||
], string> = (_executionContext, value, glue, and) => {
|
||||
const _do = (): string => {
|
||||
if ((value == null) || (value === undefined)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (isTraversable(value)) {
|
||||
value = iteratorToArray(value);
|
||||
|
||||
// this is ugly, but we have to ensure that each element of the array is rendered as PHP would render it
|
||||
const safeValue = value.map((item: any) => {
|
||||
if (typeof item === 'boolean') {
|
||||
return (item === true) ? '1' : ''
|
||||
}
|
||||
|
||||
if (Array.isArray(item)) {
|
||||
return 'Array';
|
||||
}
|
||||
|
||||
return item;
|
||||
});
|
||||
|
||||
if (and === null || and === glue) {
|
||||
return safeValue.join(glue);
|
||||
}
|
||||
|
||||
if (safeValue.length === 1) {
|
||||
return safeValue[0];
|
||||
}
|
||||
|
||||
return safeValue.slice(0, -1).join(glue) + and + safeValue[safeValue.length - 1];
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
return Promise.resolve(_do());
|
||||
};
|
||||
|
||||
export const joinSynchronously: TwingSynchronousCallable<[
|
||||
value: any,
|
||||
glue: string,
|
||||
|
@ -3,7 +3,7 @@ import {iteratorToArray} from "../../../helpers/iterator-to-array";
|
||||
import {isPlainObject} from "../../../helpers/is-plain-object";
|
||||
import {iteratorToMap} from "../../../helpers/iterator-to-map";
|
||||
import {isTraversable} from "../../../helpers/is-traversable";
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
function isPureArray(map: Map<any, any>): boolean {
|
||||
let result: boolean = true;
|
||||
@ -22,44 +22,6 @@ function isPureArray(map: Map<any, any>): boolean {
|
||||
return result;
|
||||
}
|
||||
|
||||
export const jsonEncode: TwingCallable = (_executionContext, value: any): Promise<string> => {
|
||||
const _sanitize = (value: any): any => {
|
||||
if (isTraversable(value) || isPlainObject(value)) {
|
||||
value = iteratorToMap(value);
|
||||
}
|
||||
|
||||
if (value instanceof Map) {
|
||||
let sanitizedValue: any;
|
||||
|
||||
if (isPureArray(value)) {
|
||||
value = iteratorToArray(value);
|
||||
|
||||
sanitizedValue = [];
|
||||
|
||||
for (const key in value) {
|
||||
sanitizedValue.push(_sanitize(value[key]));
|
||||
}
|
||||
}
|
||||
else {
|
||||
value = iteratorToHash(value);
|
||||
|
||||
sanitizedValue = {};
|
||||
|
||||
for (let key in value) {
|
||||
|
||||
sanitizedValue[key] = _sanitize(value[key]);
|
||||
}
|
||||
}
|
||||
|
||||
value = sanitizedValue;
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
return Promise.resolve(JSON.stringify(_sanitize(value)));
|
||||
}
|
||||
|
||||
export const jsonEncodeSynchronously: TwingSynchronousCallable = (_executionContext, value: any): string => {
|
||||
const _sanitize = (value: any): any => {
|
||||
if (isTraversable(value) || isPlainObject(value)) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {iteratorToMap} from "../../../helpers/iterator-to-map";
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
/**
|
||||
* Returns the keys of the passed array.
|
||||
@ -9,23 +9,6 @@ import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper
|
||||
*
|
||||
* @returns {Promise<Array<any>>} The keys
|
||||
*/
|
||||
export const keys: TwingCallable<[
|
||||
values: Array<any>
|
||||
], Array<any>> = (
|
||||
_executionContext,
|
||||
values
|
||||
) => {
|
||||
let traversable;
|
||||
|
||||
if ((values === null) || (values === undefined)) {
|
||||
traversable = new Map();
|
||||
} else {
|
||||
traversable = iteratorToMap(values);
|
||||
}
|
||||
|
||||
return Promise.resolve([...traversable.keys()]);
|
||||
};
|
||||
|
||||
export const keysSynchronously: TwingSynchronousCallable<[
|
||||
values: Array<any>
|
||||
], Array<any>> = (
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {getFirstValue} from "../../../helpers/get-first-value";
|
||||
import {slice, sliceSynchronously} from "./slice";
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {sliceSynchronously} from "./slice";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
/**
|
||||
* Returns the last element of the item.
|
||||
@ -10,15 +10,6 @@ import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper
|
||||
*
|
||||
* @returns The last element of the item
|
||||
*/
|
||||
export const last: TwingCallable<[
|
||||
item: any
|
||||
]> = (executionContext, item) => {
|
||||
return slice(executionContext, item, -1, 1, false)
|
||||
.then((elements) => {
|
||||
return typeof elements === 'string' ? elements : getFirstValue(elements);
|
||||
});
|
||||
};
|
||||
|
||||
export const lastSynchronously: TwingSynchronousCallable<[
|
||||
item: any
|
||||
]> = (executionContext, item) => {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
/**
|
||||
* Returns the length of a thing.
|
||||
@ -7,24 +7,6 @@ import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper
|
||||
*
|
||||
* @returns {Promise<number>} The length of the thing
|
||||
*/
|
||||
export const length: TwingCallable = (_executionContext,thing: any): Promise<number> => {
|
||||
let length: number;
|
||||
|
||||
if ((thing === null) || (thing === undefined)) {
|
||||
length = 0;
|
||||
} else if (thing.length !== undefined) {
|
||||
length = thing.length;
|
||||
} else if (thing.size !== undefined) {
|
||||
length = thing.size;
|
||||
} else if (thing.toString && (typeof thing.toString === 'function')) {
|
||||
length = thing.toString().length;
|
||||
} else {
|
||||
length = 1;
|
||||
}
|
||||
|
||||
return Promise.resolve(length);
|
||||
};
|
||||
|
||||
export const lengthSynchronously: TwingSynchronousCallable = (_executionContext,thing: any): number => {
|
||||
let length: number;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type {TwingMarkup} from "../../../markup";
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
/**
|
||||
* Converts a string to lowercase.
|
||||
@ -8,10 +8,6 @@ import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper
|
||||
*
|
||||
* @returns The lowercased string
|
||||
*/
|
||||
export const lower: TwingCallable = (_executionContext,string: string | TwingMarkup): Promise<string> => {
|
||||
return Promise.resolve(string.toString().toLowerCase());
|
||||
};
|
||||
|
||||
export const lowerSynchronously: TwingSynchronousCallable = (_executionContext,string: string | TwingMarkup): string => {
|
||||
return string.toString().toLowerCase();
|
||||
};
|
||||
|
@ -1,20 +1,5 @@
|
||||
import {iteratorToMap} from "../../../helpers/iterator-to-map";
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
export const map: TwingCallable<[
|
||||
map: any,
|
||||
callback: (...args: Array<any>) => Promise<any>
|
||||
], Map<any, any>> = async (_executionContext, map, callback) => {
|
||||
const result: Map<any, any> = new Map();
|
||||
|
||||
map = iteratorToMap(map);
|
||||
|
||||
for (const [key, value] of map) {
|
||||
result.set(key, await callback(value, key));
|
||||
}
|
||||
|
||||
return Promise.resolve(result);
|
||||
};
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
export const mapSynchronously: TwingSynchronousCallable<[
|
||||
map: any,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {mergeIterables} from "../../../helpers/merge-iterables";
|
||||
import {isTraversable} from "../../../helpers/is-traversable";
|
||||
import {iteratorToMap} from "../../../helpers/iterator-to-map";
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
/**
|
||||
* Merges an array with another one.
|
||||
@ -19,22 +19,6 @@ import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper
|
||||
*
|
||||
* @return {Promise<Map<any, any>>} The merged map
|
||||
*/
|
||||
export const merge: TwingCallable = (_executionContext, iterable1: any, source: any): Promise<Map<any, any>> => {
|
||||
const isIterable1NullOrUndefined = (iterable1 === null) || (iterable1 === undefined);
|
||||
|
||||
if (isIterable1NullOrUndefined || (!isTraversable(iterable1) && (typeof iterable1 !== 'object'))) {
|
||||
return Promise.reject(new Error(`The merge filter only works on arrays or "Traversable", got "${!isIterable1NullOrUndefined ? typeof iterable1 : iterable1}".`));
|
||||
}
|
||||
|
||||
const isSourceNullOrUndefined = (source === null) || (source === undefined);
|
||||
|
||||
if (isSourceNullOrUndefined || (!isTraversable(source) && (typeof source !== 'object'))) {
|
||||
return Promise.reject(new Error(`The merge filter only accepts arrays or "Traversable" as source, got "${!isSourceNullOrUndefined ? typeof source : source}".`));
|
||||
}
|
||||
|
||||
return Promise.resolve(mergeIterables(iteratorToMap(iterable1), iteratorToMap(source)));
|
||||
};
|
||||
|
||||
export const mergeSynchronously: TwingSynchronousCallable = (_executionContext, iterable1: any, source: any): Map<any, any> => {
|
||||
const isIterable1NullOrUndefined = (iterable1 === null) || (iterable1 === undefined);
|
||||
|
||||
|
@ -1,13 +1,9 @@
|
||||
import type {TwingMarkup} from "../../../markup";
|
||||
import {createMarkup} from "../../../markup";
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
const phpNl2br = require('locutus/php/strings/nl2br');
|
||||
|
||||
export const nl2br: TwingCallable = (_executionContext, ...args: Array<any>): Promise<TwingMarkup> => {
|
||||
return Promise.resolve(createMarkup(phpNl2br(...args)));
|
||||
};
|
||||
|
||||
export const nl2brSynchronously: TwingSynchronousCallable = (_executionContext, ...args: Array<any>): TwingMarkup => {
|
||||
return createMarkup(phpNl2br(...args));
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
const phpNumberFormat = require('locutus/php/strings/number_format');
|
||||
|
||||
@ -16,31 +16,6 @@ const phpNumberFormat = require('locutus/php/strings/number_format');
|
||||
*
|
||||
* @returns {Promise<string>} The formatted number
|
||||
*/
|
||||
export const numberFormat: TwingCallable = (
|
||||
executionContext,
|
||||
number: any,
|
||||
numberOfDecimals: number | null,
|
||||
decimalPoint: string | null,
|
||||
thousandSeparator: string | null
|
||||
): Promise<string> => {
|
||||
const {environment} = executionContext;
|
||||
const {numberFormat} = environment;
|
||||
|
||||
if (numberOfDecimals === null) {
|
||||
numberOfDecimals = numberFormat.numberOfDecimals;
|
||||
}
|
||||
|
||||
if (decimalPoint === null) {
|
||||
decimalPoint = numberFormat.decimalPoint;
|
||||
}
|
||||
|
||||
if (thousandSeparator === null) {
|
||||
thousandSeparator = numberFormat.thousandSeparator;
|
||||
}
|
||||
|
||||
return Promise.resolve(phpNumberFormat(number, numberOfDecimals, decimalPoint, thousandSeparator));
|
||||
};
|
||||
|
||||
export const numberFormatSynchronously: TwingSynchronousCallable = (
|
||||
executionContext,
|
||||
number: any,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {createMarkup, TwingMarkup} from "../../../markup";
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
/**
|
||||
* Marks a variable as being safe.
|
||||
@ -8,10 +8,6 @@ import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper
|
||||
*
|
||||
* @return {Promise<string>}
|
||||
*/
|
||||
export const raw: TwingCallable = (_executionContext, value: string | TwingMarkup | null): Promise<TwingMarkup> => {
|
||||
return Promise.resolve(createMarkup(value !== null ? value.toString() : ''));
|
||||
};
|
||||
|
||||
export const rawSynchronously: TwingSynchronousCallable = (_executionContext, value: string | TwingMarkup | null): TwingMarkup => {
|
||||
return createMarkup(value !== null ? value.toString() : '');
|
||||
};
|
||||
|
@ -1,15 +1,5 @@
|
||||
import {iteratorToMap} from "../../../helpers/iterator-to-map";
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
export const reduce: TwingCallable = (_executionContext, map: any, callback: (accumulator: any, currentValue: any) => any, initial: any): Promise<string> => {
|
||||
map = iteratorToMap(map);
|
||||
|
||||
const values: any[] = [...map.values()];
|
||||
|
||||
return Promise.resolve(values.reduce((previousValue: any, currentValue: any): any => {
|
||||
return (async () => callback(await previousValue, currentValue))();
|
||||
}, initial));
|
||||
};
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
export const reduceSynchronously: TwingSynchronousCallable = (_executionContext, map: any, callback: (accumulator: any, currentValue: any) => any, initial: any): Promise<string> => {
|
||||
map = iteratorToMap(map);
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {isTraversable} from "../../../helpers/is-traversable";
|
||||
import {iteratorToHash} from "../../../helpers/iterator-to-hash";
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
const phpStrtr = require('locutus/php/strings/strtr');
|
||||
|
||||
@ -12,29 +12,6 @@ const phpStrtr = require('locutus/php/strings/strtr');
|
||||
*
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
export const replace: TwingCallable = (_executionContext,value: string | null, from: any): Promise<string> => {
|
||||
const _do = (): string => {
|
||||
if (isTraversable(from)) {
|
||||
from = iteratorToHash(from);
|
||||
}
|
||||
else if (typeof from !== 'object') {
|
||||
throw new Error(`The "replace" filter expects an hash or "Iterable" as replace values, got "${typeof from}".`);
|
||||
}
|
||||
|
||||
if (value === null) {
|
||||
value = '';
|
||||
}
|
||||
|
||||
return phpStrtr(value, from);
|
||||
};
|
||||
|
||||
try {
|
||||
return Promise.resolve(_do());
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
};
|
||||
|
||||
export const replaceSynchronously: TwingSynchronousCallable = (_executionContext, value: string | null, from: any): string => {
|
||||
if (isTraversable(from)) {
|
||||
from = iteratorToHash(from);
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {reverse as reverseHelper} from "../../../helpers/reverse";
|
||||
import {iteratorToMap} from "../../../helpers/iterator-to-map";
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
/**
|
||||
* Reverses a variable.
|
||||
@ -10,14 +10,6 @@ import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper
|
||||
*
|
||||
* @returns {Promise<string | Map<any, any>>} The reversed input
|
||||
*/
|
||||
export const reverse: TwingCallable = (_executionContext, item: any, preserveKeys: boolean): Promise<string | Map<any, any>> => {
|
||||
if (typeof item === 'string') {
|
||||
return Promise.resolve([...item].reverse().join(''));
|
||||
} else {
|
||||
return Promise.resolve(reverseHelper(iteratorToMap(item as Map<any, any>), preserveKeys));
|
||||
}
|
||||
};
|
||||
|
||||
export const reverseSynchronously: TwingSynchronousCallable = (_executionContext, item: any, preserveKeys: boolean): string | Map<any, any> => {
|
||||
if (typeof item === 'string') {
|
||||
return [...item].reverse().join('');
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
const phpRound = require('locutus/php/math/round');
|
||||
const phpCeil = require('locutus/php/math/ceil');
|
||||
@ -13,36 +13,6 @@ const phpFloor = require('locutus/php/math/floor');
|
||||
*
|
||||
* @returns {Promise<number>} The rounded number
|
||||
*/
|
||||
export const round: TwingCallable = (_executionContext, value: any, precision: number, method: string): Promise<number> => {
|
||||
const _do = (): number => {
|
||||
if (method === 'common') {
|
||||
return phpRound(value, precision);
|
||||
}
|
||||
|
||||
if (method !== 'ceil' && method !== 'floor') {
|
||||
throw new Error('The round filter only supports the "common", "ceil", and "floor" methods.');
|
||||
}
|
||||
|
||||
const intermediateValue = value * Math.pow(10, precision);
|
||||
const intermediateDivider = Math.pow(10, precision);
|
||||
|
||||
if (method === 'ceil') {
|
||||
return phpCeil(intermediateValue) / intermediateDivider;
|
||||
}
|
||||
else {
|
||||
return phpFloor(intermediateValue) / intermediateDivider;
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const result = _do();
|
||||
|
||||
return Promise.resolve(result);
|
||||
} catch (error: any) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
};
|
||||
|
||||
export const roundSynchronously: TwingSynchronousCallable = (_executionContext, value: any, precision: number, method: string): number => {
|
||||
if (method === 'common') {
|
||||
return phpRound(value, precision);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {isTraversable} from "../../../helpers/is-traversable";
|
||||
import {iteratorToMap} from "../../../helpers/iterator-to-map";
|
||||
import {sliceMap} from "../../../helpers/slice-map";
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
/**
|
||||
* Slices a variable.
|
||||
@ -14,31 +14,6 @@ import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper
|
||||
*
|
||||
* @returns {Promise<string | Map<any, any>>} The sliced variable
|
||||
*/
|
||||
export const slice: TwingCallable<[
|
||||
item: any,
|
||||
start: number,
|
||||
length: number | null,
|
||||
preserveKeys: boolean
|
||||
], string | Map<any, any>> = (_executionContext, item, start, length, preserveKeys) => {
|
||||
if (isTraversable(item)) {
|
||||
const iterableItem = iteratorToMap(item);
|
||||
|
||||
if (length === null) {
|
||||
length = iterableItem.size - start;
|
||||
}
|
||||
|
||||
return Promise.resolve(sliceMap(iterableItem, start, length, preserveKeys));
|
||||
}
|
||||
|
||||
item = '' + (item ? item : '');
|
||||
|
||||
if (length === null) {
|
||||
length = item.length - start;
|
||||
}
|
||||
|
||||
return Promise.resolve(item.substr(start, length));
|
||||
};
|
||||
|
||||
export const sliceSynchronously: TwingSynchronousCallable<[
|
||||
item: any,
|
||||
start: number,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {isTraversable} from "../../../helpers/is-traversable";
|
||||
import {iteratorToMap} from "../../../helpers/iterator-to-map";
|
||||
import {asort, asortSynchronously} from "../../../helpers/asort";
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {asortSynchronously} from "../../../helpers/asort";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
/**
|
||||
* Sorts an iterable.
|
||||
@ -12,21 +12,6 @@ import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper
|
||||
*
|
||||
* @returns {Promise<Map<any, any>>}
|
||||
*/
|
||||
export const sort: TwingCallable<[
|
||||
iterable: any,
|
||||
arrow: ((a: any, b: any) => Promise<-1 | 0 | 1>) | null
|
||||
], Map<any, any>> = async (_executionContext, iterable, arrow)=> {
|
||||
if (!isTraversable(iterable)) {
|
||||
return Promise.reject(new Error(`The sort filter only works with iterables, got "${typeof iterable}".`));
|
||||
}
|
||||
|
||||
const map = iteratorToMap(iterable);
|
||||
|
||||
await asort(map, arrow || undefined);
|
||||
|
||||
return map;
|
||||
};
|
||||
|
||||
export const sortSynchronously: TwingSynchronousCallable<[
|
||||
iterable: any,
|
||||
arrow: ((a: any, b: any) => -1 | 0 | 1) | null
|
||||
|
@ -1,15 +1,11 @@
|
||||
import {createMarkup, TwingMarkup} from "../../../markup";
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
/**
|
||||
* Removes whitespaces between HTML tags.
|
||||
*
|
||||
* @return {Promise<TwingMarkup>}
|
||||
*/
|
||||
export const spaceless: TwingCallable = (_executionContext, content: string | TwingMarkup): Promise<TwingMarkup> => {
|
||||
return Promise.resolve(createMarkup(content.toString().replace(/>\s+</g, '><').trim()));
|
||||
};
|
||||
|
||||
export const spacelessSynchronously: TwingSynchronousCallable = (_executionContext, content: string | TwingMarkup): TwingMarkup => {
|
||||
return createMarkup(content.toString().replace(/>\s+</g, '><').trim());
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
const explode = require('locutus/php/strings/explode');
|
||||
|
||||
@ -25,34 +25,6 @@ const explode = require('locutus/php/strings/explode');
|
||||
*
|
||||
* @returns {Promise<Array<string>>} The split string as an array
|
||||
*/
|
||||
export const split: TwingCallable = (_executionContext, value: string, delimiter: string, limit: number | null): Promise<Array<string>> => {
|
||||
let _do = (): Array<string> => {
|
||||
if (delimiter) {
|
||||
return !limit ? explode(delimiter, value) : explode(delimiter, value, limit);
|
||||
}
|
||||
|
||||
if (!limit || limit <= 1) {
|
||||
return value.match(/.{1,1}/ug)!;
|
||||
}
|
||||
|
||||
let length = value.length;
|
||||
|
||||
if (length < limit) {
|
||||
return [value];
|
||||
}
|
||||
|
||||
let r = [];
|
||||
|
||||
for (let i = 0; i < length; i += limit) {
|
||||
r.push(value.substr(i, limit));
|
||||
}
|
||||
|
||||
return r;
|
||||
};
|
||||
|
||||
return Promise.resolve(_do());
|
||||
};
|
||||
|
||||
export const splitSynchronously: TwingSynchronousCallable = (_executionContext, value: string, delimiter: string, limit: number | null): Array<string> => {
|
||||
if (delimiter) {
|
||||
return !limit ? explode(delimiter, value) : explode(delimiter, value, limit);
|
||||
|
@ -1,11 +1,7 @@
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
const phpStripTags = require('locutus/php/strings/strip_tags');
|
||||
|
||||
export const striptags: TwingCallable = (_executionContext, input: string, allowedTags: string): Promise<string> => {
|
||||
return Promise.resolve(phpStripTags(input, allowedTags));
|
||||
};
|
||||
|
||||
export const striptagsSynchronously: TwingSynchronousCallable = (_executionContext, input: string, allowedTags: string): string => {
|
||||
return phpStripTags(input, allowedTags);
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type {TwingMarkup} from "../../../markup";
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
const phpUcwords = require('locutus/php/strings/ucwords');
|
||||
|
||||
@ -11,14 +11,6 @@ const phpUcwords = require('locutus/php/strings/ucwords');
|
||||
*
|
||||
* @returns The title-cased string
|
||||
*/
|
||||
export const title: TwingCallable<[
|
||||
string: string | TwingMarkup
|
||||
], string> = (_executionContext, string) => {
|
||||
const result: string = phpUcwords(string.toString().toLowerCase());
|
||||
|
||||
return Promise.resolve(result);
|
||||
};
|
||||
|
||||
export const titleSynchronously: TwingSynchronousCallable<[
|
||||
string: string | TwingMarkup
|
||||
], string> = (_executionContext, string) => {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
const phpTrim = require('locutus/php/strings/trim');
|
||||
const phpLeftTrim = require('locutus/php/strings/ltrim');
|
||||
@ -11,31 +11,6 @@ const phpRightTrim = require('locutus/php/strings/rtrim');
|
||||
*
|
||||
* @throws TwingErrorRuntime When an invalid trimming side is used (not a string or not 'left', 'right', or 'both')
|
||||
*/
|
||||
export const trim: TwingCallable = (_executionContext, string: string, characterMask: string | null, side: string): Promise<string> => {
|
||||
const _do = (): string => {
|
||||
if (characterMask === null) {
|
||||
characterMask = " \t\n\r\0\x0B";
|
||||
}
|
||||
|
||||
switch (side) {
|
||||
case 'both':
|
||||
return phpTrim(string, characterMask);
|
||||
case 'left':
|
||||
return phpLeftTrim(string, characterMask);
|
||||
case 'right':
|
||||
return phpRightTrim(string, characterMask);
|
||||
default:
|
||||
throw new Error('Trimming side must be "left", "right" or "both".');
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
return Promise.resolve(_do());
|
||||
} catch (error: any) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
};
|
||||
|
||||
export const trimSynchronously: TwingSynchronousCallable = (_executionContext, string: string, characterMask: string | null, side: string): string => {
|
||||
if (characterMask === null) {
|
||||
characterMask = " \t\n\r\0\x0B";
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type {TwingMarkup} from "../../../markup";
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
/**
|
||||
* Converts a string to uppercase.
|
||||
@ -8,10 +8,6 @@ import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper
|
||||
*
|
||||
* @returns {Promise<string>} The uppercased string
|
||||
*/
|
||||
export const upper: TwingCallable = (_executionContext, string: string | TwingMarkup): Promise<string> => {
|
||||
return Promise.resolve(string.toString().toUpperCase());
|
||||
};
|
||||
|
||||
export const upperSynchronously: TwingSynchronousCallable = (_executionContext, string: string | TwingMarkup): string => {
|
||||
return string.toString().toUpperCase();
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {isTraversable} from "../../../helpers/is-traversable";
|
||||
import {iteratorToHash} from "../../../helpers/iterator-to-hash";
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
const phpHttpBuildQuery = require('locutus/php/url/http_build_query');
|
||||
|
||||
@ -11,20 +11,6 @@ const phpHttpBuildQuery = require('locutus/php/url/http_build_query');
|
||||
*
|
||||
* @returns {Promise<string>} The URL encoded value
|
||||
*/
|
||||
export const url_encode: TwingCallable = (_executionContext, url: string | {}): Promise<string> => {
|
||||
if (typeof url !== 'string') {
|
||||
if (isTraversable(url)) {
|
||||
url = iteratorToHash(url);
|
||||
}
|
||||
|
||||
const builtUrl: string = phpHttpBuildQuery(url, '', '&');
|
||||
|
||||
return Promise.resolve(builtUrl.replace(/\+/g, '%20'));
|
||||
}
|
||||
|
||||
return Promise.resolve(encodeURIComponent(url));
|
||||
}
|
||||
|
||||
export const urlEncodeSynchronously: TwingSynchronousCallable = (_executionContext, url: string | {}): string => {
|
||||
if (typeof url !== 'string') {
|
||||
if (isTraversable(url)) {
|
||||
|
@ -1,18 +1,6 @@
|
||||
import {getConstant as constantHelper} from "../../../helpers/get-constant";
|
||||
import type {TwingCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
export const constant: TwingCallable<[
|
||||
name: string,
|
||||
object: any | null
|
||||
]> = (
|
||||
executionContext,
|
||||
name,
|
||||
object
|
||||
): Promise<any> => {
|
||||
return Promise.resolve(constantHelper(executionContext.context, name, object));
|
||||
};
|
||||
|
||||
export const constantSynchronously: TwingSynchronousCallable<[
|
||||
name: string,
|
||||
object: any | null
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {isAMapLike} from "../../../helpers/map-like";
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
/**
|
||||
* Cycles over a value.
|
||||
@ -10,29 +10,6 @@ import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper
|
||||
*
|
||||
* @returns The value at position
|
||||
*/
|
||||
export const cycle: TwingCallable<[
|
||||
value: Map<any, any> | Array<any> | string | boolean | null,
|
||||
position: number
|
||||
]> = (_executionContext, value, position) => {
|
||||
if (!isAMapLike(value) && !Array.isArray(value)) {
|
||||
return Promise.resolve(value);
|
||||
}
|
||||
|
||||
let values: Array<any>;
|
||||
let size: number;
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
values = value;
|
||||
size = value.length;
|
||||
}
|
||||
else {
|
||||
values = [...value.values()];
|
||||
size = value.size;
|
||||
}
|
||||
|
||||
return Promise.resolve(values[position % size]);
|
||||
}
|
||||
|
||||
export const cycleSynchronously: TwingSynchronousCallable<[
|
||||
value: Map<any, any> | Array<any> | string | boolean | null,
|
||||
position: number
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {DateTime, Duration} from "luxon";
|
||||
import {modifyDate} from "../../../helpers/modify-date";
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
/**
|
||||
* Converts an input to a DateTime instance.
|
||||
@ -17,26 +17,6 @@ import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper
|
||||
*
|
||||
* @returns {Promise<DateTime | Duration>}
|
||||
*/
|
||||
export const createDateTime = (
|
||||
defaultTimezone: string,
|
||||
input: Date | DateTime | number | string | null,
|
||||
timezone: string | null | false
|
||||
): Promise<DateTime> => {
|
||||
return Promise.resolve(createDateTimeSynchronously(defaultTimezone, input, timezone))
|
||||
}
|
||||
|
||||
export const date: TwingCallable = (
|
||||
executionContext,
|
||||
date: Date | DateTime | Duration | number | string | null,
|
||||
timezone: string | null | false
|
||||
): Promise<DateTime | Duration> => {
|
||||
if (date instanceof Duration) {
|
||||
return Promise.resolve(date);
|
||||
}
|
||||
|
||||
return createDateTime(executionContext.environment.timezone, date, timezone);
|
||||
}
|
||||
|
||||
export const createDateTimeSynchronously = (
|
||||
defaultTimezone: string,
|
||||
input: Date | DateTime | number | string | null,
|
||||
|
@ -1,25 +1,7 @@
|
||||
import {iterate, iterateSynchronously} from "../../../helpers/iterate";
|
||||
import {iterateSynchronously} from "../../../helpers/iterate";
|
||||
import {createMarkup, TwingMarkup} from "../../../markup";
|
||||
import {varDump} from "../../../helpers/php";
|
||||
import type {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
export const dump: TwingCallable<[
|
||||
...vars: Array<any>
|
||||
], TwingMarkup> = (executionContext, ...vars) => {
|
||||
if (vars.length < 1) {
|
||||
const vars_ = new Map();
|
||||
|
||||
return iterate(executionContext.context, (key, value) => {
|
||||
vars_.set(key, value);
|
||||
|
||||
return Promise.resolve();
|
||||
}).then(() => {
|
||||
return createMarkup(varDump(vars_));
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.resolve(createMarkup(varDump(...vars)));
|
||||
};
|
||||
import type {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
export const dumpSynchronously: TwingSynchronousCallable<[
|
||||
...vars: Array<any>
|
||||
|
@ -1,11 +1,9 @@
|
||||
import {isTraversable} from "../../../helpers/is-traversable";
|
||||
import {isPlainObject} from "../../../helpers/is-plain-object";
|
||||
import {createContext} from "../../../context";
|
||||
import {createMarkup, TwingMarkup} from "../../../markup";
|
||||
import type {TwingSynchronousTemplate, TwingTemplate} from "../../../template";
|
||||
import type {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {iterableToMap, iteratorToMap} from "../../../helpers/iterator-to-map";
|
||||
import {mergeIterables} from "../../../helpers/merge-iterables";
|
||||
import type {TwingSynchronousTemplate} from "../../../template";
|
||||
import type {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {iteratorToMap} from "../../../helpers/iterator-to-map";
|
||||
|
||||
/**
|
||||
* Renders a template.
|
||||
@ -19,88 +17,6 @@ import {mergeIterables} from "../../../helpers/merge-iterables";
|
||||
*
|
||||
* @returns {Promise<TwingMarkup>} The rendered template
|
||||
*/
|
||||
export const include: TwingCallable<[
|
||||
templates: string | TwingTemplate | null | Array<string | TwingTemplate | null>,
|
||||
variables: Map<string, any>,
|
||||
withContext: boolean,
|
||||
ignoreMissing: boolean,
|
||||
sandboxed: boolean
|
||||
]> = (
|
||||
executionContext,
|
||||
templates,
|
||||
variables,
|
||||
withContext,
|
||||
ignoreMissing,
|
||||
sandboxed
|
||||
): Promise<TwingMarkup> => {
|
||||
const {
|
||||
template,
|
||||
environment,
|
||||
templateLoader,
|
||||
context,
|
||||
nodeExecutor,
|
||||
outputBuffer,
|
||||
sourceMapRuntime,
|
||||
strict
|
||||
} = executionContext;
|
||||
if (!isPlainObject(variables) && !isTraversable(variables)) {
|
||||
const isVariablesNullOrUndefined = variables === null || variables === undefined;
|
||||
|
||||
return Promise.reject(new Error(`Variables passed to the "include" function or tag must be iterable, got "${!isVariablesNullOrUndefined ? typeof variables : variables}".`));
|
||||
}
|
||||
|
||||
variables = iteratorToMap(variables);
|
||||
|
||||
if (withContext) {
|
||||
variables = mergeIterables(context, variables);
|
||||
}
|
||||
|
||||
if (!Array.isArray(templates)) {
|
||||
templates = [templates];
|
||||
}
|
||||
|
||||
const resolveTemplate = (templates: Array<string | TwingTemplate | null>): Promise<TwingTemplate | null> => {
|
||||
return template.loadTemplate(executionContext, templates)
|
||||
.catch((error) => {
|
||||
if (!ignoreMissing) {
|
||||
throw error;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return resolveTemplate(templates)
|
||||
.then((template) => {
|
||||
outputBuffer.start();
|
||||
|
||||
if (template) {
|
||||
return template.execute(
|
||||
environment,
|
||||
createContext(iterableToMap(variables)),
|
||||
new Map(),
|
||||
outputBuffer,
|
||||
{
|
||||
nodeExecutor,
|
||||
sandboxed,
|
||||
sourceMapRuntime: sourceMapRuntime || undefined,
|
||||
strict,
|
||||
templateLoader
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
const result = outputBuffer.getAndClean();
|
||||
|
||||
return createMarkup(result, environment.charset);
|
||||
});
|
||||
}
|
||||
|
||||
export const includeSynchronously: TwingSynchronousCallable<[
|
||||
templates: string | TwingSynchronousTemplate | null | Array<string | TwingSynchronousTemplate | null>,
|
||||
variables: Map<string, any>,
|
||||
|
@ -1,18 +1,7 @@
|
||||
import {iteratorToArray} from "../../../helpers/iterator-to-array";
|
||||
import {max as phpMax} from "locutus/php/math";
|
||||
import type {TwingCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
export const max: TwingCallable<[
|
||||
...values: Array<any>
|
||||
]> = (_executionContext, ...values) => {
|
||||
if (values.length === 1) {
|
||||
values = values[0];
|
||||
}
|
||||
|
||||
return Promise.resolve(phpMax(iteratorToArray(values)));
|
||||
};
|
||||
|
||||
export const maxSynchronously: TwingSynchronousCallable<[
|
||||
...values: Array<any>
|
||||
]> = (_executionContext, ...values) => {
|
||||
|
@ -1,18 +1,7 @@
|
||||
import {iteratorToArray} from "../../../helpers/iterator-to-array";
|
||||
import {min as phpMin} from "locutus/php/math";
|
||||
import type {TwingCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
export const min: TwingCallable<[
|
||||
...values: Array<any>
|
||||
]> = (_executionContext, ...values) => {
|
||||
if (values.length === 1) {
|
||||
values = values[0];
|
||||
}
|
||||
|
||||
return Promise.resolve(phpMin(iteratorToArray(values)));
|
||||
};
|
||||
|
||||
export const minSynchronously: TwingSynchronousCallable<[
|
||||
...values: Array<any>
|
||||
]> = (_executionContext, ...values) => {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {iconv} from "../../../helpers/iconv";
|
||||
import {isTraversable} from "../../../helpers/is-traversable";
|
||||
import {iteratorToArray} from "../../../helpers/iterator-to-array";
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
const runes = require('runes');
|
||||
const mt_rand = require('locutus/php/math/mt_rand');
|
||||
@ -21,72 +21,6 @@ const array_rand = require('locutus/php/array/array_rand');
|
||||
*
|
||||
* @returns {Promise<any>} A random value from the given sequence
|
||||
*/
|
||||
export const random: TwingCallable = (executionContext, values: any | null, max: number | null): any => {
|
||||
const {environment} = executionContext;
|
||||
const {charset} = environment;
|
||||
|
||||
let _do = (): any => {
|
||||
if (values === null) {
|
||||
return max === null ? mt_rand() : mt_rand(0, max);
|
||||
}
|
||||
|
||||
if (typeof values === 'number') {
|
||||
let min: number;
|
||||
|
||||
if (max === null) {
|
||||
if (values < 0) {
|
||||
max = 0;
|
||||
min = values;
|
||||
} else {
|
||||
max = values;
|
||||
min = 0;
|
||||
}
|
||||
} else {
|
||||
min = values;
|
||||
}
|
||||
|
||||
return mt_rand(min, max);
|
||||
}
|
||||
|
||||
if (typeof values === 'string') {
|
||||
values = Buffer.from(values);
|
||||
}
|
||||
|
||||
if (Buffer.isBuffer(values)) {
|
||||
if (values.toString() === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (charset !== 'UTF-8') {
|
||||
values = iconv(charset, 'UTF-8', values);
|
||||
}
|
||||
|
||||
// unicode split
|
||||
values = runes(values.toString());
|
||||
|
||||
if (charset !== 'UTF-8') {
|
||||
values = values.map((value: string) => {
|
||||
return iconv('UTF-8', charset, Buffer.from(value)).toString();
|
||||
});
|
||||
}
|
||||
} else if (isTraversable(values)) {
|
||||
values = iteratorToArray(values);
|
||||
}
|
||||
|
||||
if (!Array.isArray(values)) {
|
||||
return values;
|
||||
}
|
||||
|
||||
if (values.length < 1) {
|
||||
return Promise.reject(new Error('The random function cannot pick from an empty array.'));
|
||||
}
|
||||
|
||||
return values[array_rand(values, 1)];
|
||||
};
|
||||
|
||||
return Promise.resolve(_do());
|
||||
}
|
||||
|
||||
export const randomSynchronously: TwingSynchronousCallable = (executionContext, values: any | null, max: number | null): any => {
|
||||
const {environment} = executionContext;
|
||||
const {charset} = environment;
|
||||
|
@ -1,15 +1,5 @@
|
||||
import {createRange} from "../../../helpers/create-range";
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
type Range<V = any> = TwingCallable<[
|
||||
low: V,
|
||||
high: V,
|
||||
step: number
|
||||
], Map<number, V>>;
|
||||
|
||||
export const range: Range = (_executionContext, low, high, step) => {
|
||||
return Promise.resolve(createRange(low, high, step));
|
||||
}
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
type SynchronousRange<V = any> = TwingSynchronousCallable<[
|
||||
low: V,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {createTemplateLoadingError} from "../../../error/loader";
|
||||
import type {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import type {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousTemplate} from "../../../template";
|
||||
|
||||
/**
|
||||
@ -11,25 +11,6 @@ import {TwingSynchronousTemplate} from "../../../template";
|
||||
*
|
||||
* @return The template source
|
||||
*/
|
||||
export const source: TwingCallable<[
|
||||
name: string,
|
||||
ignoreMissing: boolean
|
||||
], string | null> = (executionContext, name, ignoreMissing) => {
|
||||
const {template} = executionContext;
|
||||
|
||||
return template.loadTemplate(executionContext, name)
|
||||
.catch(() => {
|
||||
return null;
|
||||
})
|
||||
.then((template) => {
|
||||
if (!ignoreMissing && (template === null)) {
|
||||
throw createTemplateLoadingError([name]);
|
||||
}
|
||||
|
||||
return template?.source.code || null;
|
||||
});
|
||||
};
|
||||
|
||||
export const sourceSynchronously: TwingSynchronousCallable<[
|
||||
name: string,
|
||||
ignoreMissing: boolean
|
||||
|
@ -2,9 +2,9 @@ import {createSynchronousTemplate, TwingSynchronousTemplate} from "../../../temp
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import * as createHash from "create-hash";
|
||||
import {createSource} from "../../../source";
|
||||
import {TwingExecutionContext, TwingSynchronousExecutionContext} from "../../../execution-context";
|
||||
import {TwingSynchronousExecutionContext} from "../../../execution-context";
|
||||
|
||||
const getAST = (executionContext: TwingExecutionContext | TwingSynchronousExecutionContext, code: string, name: string | null) => {
|
||||
const getAST = (executionContext: TwingSynchronousExecutionContext, code: string, name: string | null) => {
|
||||
const {environment} = executionContext;
|
||||
|
||||
const hash: string = createHash("sha256").update(code).digest("hex").toString();
|
||||
|
@ -1,20 +1,6 @@
|
||||
import {getConstant} from "../../../helpers/get-constant";
|
||||
import type {TwingCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
export const isConstant: TwingCallable<[
|
||||
comparand: any,
|
||||
constant: any,
|
||||
object: any | null
|
||||
], boolean> = (
|
||||
executionContext,
|
||||
comparand,
|
||||
constant,
|
||||
object
|
||||
) => {
|
||||
return Promise.resolve(comparand === getConstant(executionContext.context, constant, object));
|
||||
};
|
||||
|
||||
export const isConstantSynchronously: TwingSynchronousCallable<[
|
||||
comparand: any,
|
||||
constant: any,
|
||||
|
@ -1,13 +1,4 @@
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
export const isDefined: TwingCallable<[
|
||||
value: any
|
||||
], boolean> = (
|
||||
_executionContext,
|
||||
value
|
||||
) => {
|
||||
return Promise.resolve(!!value);
|
||||
};
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
export const isDefinedSynchronously: TwingSynchronousCallable<[
|
||||
value: any
|
||||
|
@ -1,10 +1,5 @@
|
||||
import type {TwingCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
export const isDivisibleBy: TwingCallable<[a: any, divisor: any], boolean> = (_executionContext, a, divisor) => {
|
||||
return Promise.resolve(a % divisor === 0);
|
||||
};
|
||||
|
||||
export const isDivisibleBySynchronously: TwingSynchronousCallable<[a: any, divisor: any], boolean> = (_executionContext, a, divisor) => {
|
||||
return a % divisor === 0;
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {iteratorToArray} from "../../../helpers/iterator-to-array";
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import { isPlainObject } from "../../../helpers/is-plain-object";
|
||||
|
||||
/**
|
||||
@ -17,35 +17,6 @@ import { isPlainObject } from "../../../helpers/is-plain-object";
|
||||
*
|
||||
* @returns {boolean} true if the value is empty, false otherwise
|
||||
*/
|
||||
export const isEmpty: TwingCallable<[value: any], boolean> = (executionContext, value) => {
|
||||
if (value === null || value === undefined) {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
if (typeof value === 'string') {
|
||||
return Promise.resolve(value.length < 1);
|
||||
}
|
||||
|
||||
if (typeof value[Symbol.iterator] === 'function') {
|
||||
return Promise.resolve(value[Symbol.iterator]().next().done === true);
|
||||
}
|
||||
|
||||
if (isPlainObject(value)) {
|
||||
if (value.hasOwnProperty('toString') && typeof value.toString === 'function') {
|
||||
return isEmpty(executionContext, value.toString());
|
||||
}
|
||||
else {
|
||||
return Promise.resolve(iteratorToArray(value).length < 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof value === 'object' && value.toString && typeof value.toString === 'function') {
|
||||
return isEmpty(executionContext, value.toString());
|
||||
}
|
||||
|
||||
return Promise.resolve(value === false);
|
||||
};
|
||||
|
||||
export const isEmptySynchronously: TwingSynchronousCallable<[value: any], boolean> = (executionContext, value) => {
|
||||
if (value === null || value === undefined) {
|
||||
return true;
|
||||
|
@ -1,8 +1,4 @@
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
export const isEven: TwingCallable<[value: any], boolean> = (_executionContext, value) => {
|
||||
return Promise.resolve(value % 2 === 0);
|
||||
};
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
export const isEvenSynchronously: TwingSynchronousCallable<[value: any], boolean> = (_executionContext, value) => {
|
||||
return value % 2 === 0;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
/**
|
||||
* Checks if a variable is traversable.
|
||||
@ -14,45 +14,6 @@ import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper
|
||||
*
|
||||
* @return {Promise<boolean>} true if the value is traversable
|
||||
*/
|
||||
export const isIterable: TwingCallable<[value: any], boolean> = (_executionContext, value) => {
|
||||
let _do = (): boolean => {
|
||||
/*
|
||||
Prevent `(null)[Symbol.iterator]`/`(undefined)[Symbol.iterator]` error,
|
||||
and return `false` instead.
|
||||
|
||||
Note that `value` should only be `undefined` if it's been explicitly
|
||||
set to that (e.g., in the JavaScript that provided the calling template
|
||||
with the context). Values that are simply "not defined" will either have
|
||||
been coerced to `null` or thrown a "does not exist" runtime error before
|
||||
this function is called (depending on whether `strict_variables` is enabled).
|
||||
|
||||
This *does* mean that an explicitly `undefined` value will return `false`
|
||||
instead of throwing an error if `strict_variables` is enabled, which is
|
||||
probably unexpected behavior, but short of some major refactoring to allow
|
||||
an environmental check here, the alternative is to have `undefined`
|
||||
throw an error even when `strict_variables` is disabled, and that unexpected
|
||||
behavior seems worse.
|
||||
*/
|
||||
if (value === null || value === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// for Twig, a string is not traversable
|
||||
if (typeof value === 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof value[Symbol.iterator] === 'function') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// in PHP objects are not iterable so we have to ensure that the test reflects that
|
||||
return false;
|
||||
};
|
||||
|
||||
return Promise.resolve(_do());
|
||||
};
|
||||
|
||||
export const isIterableSynchronously: TwingSynchronousCallable<[value: any], boolean> = (_executionContext, value) => {
|
||||
/*
|
||||
Prevent `(null)[Symbol.iterator]`/`(undefined)[Symbol.iterator]` error,
|
||||
|
@ -1,8 +1,4 @@
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
export const isNull: TwingCallable<[value: any], boolean> = (_executionContext, value) => {
|
||||
return Promise.resolve(value === null);
|
||||
};
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
export const isNullSynchronously: TwingSynchronousCallable<[value: any], boolean> = (_executionContext, value) => {
|
||||
return value === null;
|
||||
|
@ -1,8 +1,4 @@
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
export const isOdd: TwingCallable<[value: any], boolean> = (_executionContext, value) => {
|
||||
return Promise.resolve(value % 2 === 1);
|
||||
};
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
export const isOddSynchronously: TwingSynchronousCallable<[value: any], boolean> = (_executionContext, value) => {
|
||||
return value % 2 === 1;
|
||||
|
@ -1,8 +1,4 @@
|
||||
import {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
export const isSameAs: TwingCallable<[a: any, comparand: any], boolean> = (_executionContext, a, comparand) => {
|
||||
return Promise.resolve(a === comparand);
|
||||
};
|
||||
import {TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
|
||||
export const isSameAsSynchronously: TwingSynchronousCallable<[a: any, comparand: any], boolean> = (_executionContext, a, comparand) => {
|
||||
return a === comparand;
|
||||
|
@ -1,37 +1,16 @@
|
||||
import {
|
||||
TwingCallableWrapperOptions,
|
||||
TwingCallableArgument,
|
||||
TwingCallable,
|
||||
TwingCallableWrapper,
|
||||
createCallableWrapper,
|
||||
TwingSynchronousCallableWrapper,
|
||||
createSynchronousCallableWrapper, TwingSynchronousCallable
|
||||
} from "./callable-wrapper";
|
||||
|
||||
export type TwingFilterOptions = TwingCallableWrapperOptions;
|
||||
|
||||
export interface TwingFilter extends TwingCallableWrapper {
|
||||
|
||||
}
|
||||
|
||||
export interface TwingSynchronousFilter extends TwingSynchronousCallableWrapper {
|
||||
|
||||
}
|
||||
|
||||
export const createFilter = (
|
||||
name: string,
|
||||
callable: TwingCallable,
|
||||
acceptedArguments: TwingCallableArgument[],
|
||||
options: TwingFilterOptions = {}
|
||||
): TwingFilter => {
|
||||
const callableWrapper = createCallableWrapper(name, callable, acceptedArguments, options);
|
||||
|
||||
const filter: TwingFilter = {
|
||||
...callableWrapper
|
||||
};
|
||||
|
||||
return filter;
|
||||
};
|
||||
|
||||
export const createSynchronousFilter = (
|
||||
name: string,
|
||||
|
@ -1,32 +1,14 @@
|
||||
import {
|
||||
TwingCallableWrapperOptions,
|
||||
TwingCallableArgument,
|
||||
TwingCallable,
|
||||
TwingCallableWrapper,
|
||||
createCallableWrapper,
|
||||
TwingSynchronousCallableWrapper,
|
||||
TwingSynchronousCallable, createSynchronousCallableWrapper
|
||||
} from "./callable-wrapper";
|
||||
|
||||
export interface TwingFunction extends TwingCallableWrapper {
|
||||
|
||||
}
|
||||
|
||||
export interface TwingSynchronousFunction extends TwingSynchronousCallableWrapper {
|
||||
|
||||
}
|
||||
|
||||
export const createFunction = (
|
||||
name: string,
|
||||
callable: TwingCallable,
|
||||
acceptedArguments: TwingCallableArgument[],
|
||||
options: TwingCallableWrapperOptions = {}
|
||||
): TwingFunction => {
|
||||
const callableWrapper = createCallableWrapper(name, callable, acceptedArguments, options);
|
||||
|
||||
return callableWrapper;
|
||||
};
|
||||
|
||||
export const createSynchronousFunction = (
|
||||
name: string,
|
||||
callable: TwingSynchronousCallable,
|
||||
|
@ -1,5 +1,3 @@
|
||||
import {sortAsynchronously} from "./sort";
|
||||
|
||||
/**
|
||||
* Sort a map and maintain index association.
|
||||
*
|
||||
@ -7,37 +5,6 @@ import {sortAsynchronously} from "./sort";
|
||||
* @param compareFunction
|
||||
* @returns
|
||||
*/
|
||||
export const asort = async (map: Map<any, any>, compareFunction?: (a: any, b: any) => Promise<-1 | 0 | 1>) => {
|
||||
const sortedMap = new Map();
|
||||
const keys: Array<any> = ([] as Array<any>).fill(null, 0, map.size);
|
||||
const values = [...map.values()];
|
||||
|
||||
let sortedValues: Array<any>;
|
||||
|
||||
if (compareFunction) {
|
||||
sortedValues = await sortAsynchronously(values, compareFunction);
|
||||
}
|
||||
else {
|
||||
sortedValues = values.sort();
|
||||
}
|
||||
|
||||
for (const [key, value] of map) {
|
||||
const index = sortedValues.indexOf(value);
|
||||
|
||||
keys[index] = key;
|
||||
}
|
||||
|
||||
for (const key of keys) {
|
||||
sortedMap.set(key, map.get(key));
|
||||
}
|
||||
|
||||
map.clear();
|
||||
|
||||
for (const [key, value] of sortedMap) {
|
||||
map.set(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
export const asortSynchronously = (map: Map<any, any>, compareFunction?: (a: any, b: any) => -1 | 0 | 1) => {
|
||||
const sortedMap = new Map();
|
||||
const keys: Array<any> = ([] as Array<any>).fill(null, 0, map.size);
|
||||
|
@ -8,7 +8,6 @@
|
||||
import {DateTime} from "luxon";
|
||||
import {isAMapLike, MapLike} from "./map-like";
|
||||
import {isAMarkup, TwingMarkup} from "../markup";
|
||||
import {TwingContext} from "../context";
|
||||
import {iteratorToMap} from "./iterator-to-map";
|
||||
|
||||
type Operand = Buffer | TwingMarkup | DateTime | MapLike<any, any> | string | boolean | number | null | object | Array<any>;
|
||||
@ -100,8 +99,8 @@ export function compare(
|
||||
* └─────────┴───────┴───────┴───────┴───────┴───────┴───────┴───────┴───────┴───────┴───────┴─────────┴───────┴───────┘
|
||||
*/
|
||||
function compareToMap(
|
||||
firstOperand: Map<any, any> | TwingContext<any, any>,
|
||||
secondOperand: string | object | DateTime | Map<any, any> | TwingContext<any, any>
|
||||
firstOperand: Map<any, any>,
|
||||
secondOperand: string | object | DateTime | Map<any, any>
|
||||
): boolean {
|
||||
if (firstOperand.size === 0) {
|
||||
return isAMapLike(secondOperand) && (secondOperand.size === 0);
|
||||
|
@ -1,44 +1,11 @@
|
||||
import {isAMarkup, TwingMarkup} from "../markup";
|
||||
import {TwingEnvironment, TwingSynchronousEnvironment} from "../environment";
|
||||
import {TwingSynchronousEnvironment} from "../environment";
|
||||
import {TwingEscapingStrategy} from "../escaping-strategy";
|
||||
import {TwingSynchronousTemplate, TwingTemplate} from "../template";
|
||||
|
||||
export const escapeValue = (
|
||||
template: TwingTemplate,
|
||||
environment: TwingEnvironment,
|
||||
value: string | boolean | TwingMarkup | null | undefined,
|
||||
strategy: TwingEscapingStrategy | string,
|
||||
charset: string | null
|
||||
): Promise<string | boolean | TwingMarkup> => {
|
||||
if (typeof value === "boolean") {
|
||||
return Promise.resolve(value);
|
||||
}
|
||||
|
||||
if (isAMarkup(value)) {
|
||||
return Promise.resolve(value);
|
||||
}
|
||||
|
||||
let result: string;
|
||||
|
||||
if ((value === null) || (value === undefined)) {
|
||||
result = '';
|
||||
}
|
||||
else {
|
||||
const strategyHandler = environment.escapingStrategyHandlers[strategy];
|
||||
|
||||
if (strategyHandler === undefined) {
|
||||
return Promise.reject(new Error(`Invalid escaping strategy "${strategy}" (valid ones: ${Object.keys(environment.escapingStrategyHandlers).sort().join(', ')}).`));
|
||||
}
|
||||
|
||||
result = strategyHandler(value.toString(), charset || environment.charset, template.name);
|
||||
}
|
||||
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
import {TwingSynchronousTemplate} from "../template";
|
||||
|
||||
export const escapeValueSynchronously = (
|
||||
template: TwingTemplate | TwingSynchronousTemplate,
|
||||
environment: TwingEnvironment | TwingSynchronousEnvironment,
|
||||
template: TwingSynchronousTemplate,
|
||||
environment: TwingSynchronousEnvironment,
|
||||
value: string | boolean | TwingMarkup | null | undefined,
|
||||
strategy: TwingEscapingStrategy | string,
|
||||
charset: string | null
|
||||
|
@ -4,10 +4,7 @@ import { isPlainObject } from "./is-plain-object";
|
||||
import { get } from "./get";
|
||||
import type { TwingAttributeAccessorCallType } from "../node/expression/attribute-accessor";
|
||||
import { isBoolean, isFloat } from "./php";
|
||||
import type {
|
||||
TwingEnvironment,
|
||||
TwingSynchronousEnvironment,
|
||||
} from "../environment";
|
||||
import type {TwingSynchronousEnvironment} from "../environment";
|
||||
|
||||
const isObject = (value: unknown) =>
|
||||
typeof value === "object" && !!value && !Array.isArray(value);
|
||||
@ -29,244 +26,6 @@ const isObject = (value: unknown) =>
|
||||
*
|
||||
* @throw {TwingErrorRuntime} if the attribute does not exist and Twing is running in strict mode and isDefinedTest is false
|
||||
*/
|
||||
export const getAttribute = (
|
||||
environment: TwingEnvironment,
|
||||
object: any,
|
||||
attribute: any,
|
||||
methodArguments: Map<any, any>,
|
||||
type: TwingAttributeAccessorCallType,
|
||||
shouldTestExistence: boolean,
|
||||
shouldIgnoreStrictCheck: boolean | null,
|
||||
sandboxed: boolean,
|
||||
strict: boolean
|
||||
): Promise<any> => {
|
||||
const { sandboxPolicy } = environment;
|
||||
|
||||
shouldIgnoreStrictCheck =
|
||||
shouldIgnoreStrictCheck === null ? !strict : shouldIgnoreStrictCheck;
|
||||
|
||||
const _do = (): any => {
|
||||
let message: string;
|
||||
|
||||
// ANY_CALL or ARRAY_CALL
|
||||
if (type !== "method") {
|
||||
let arrayItem;
|
||||
|
||||
if (isBoolean(attribute)) {
|
||||
arrayItem = attribute ? 1 : 0;
|
||||
} else if (isFloat(attribute)) {
|
||||
arrayItem = parseInt(attribute);
|
||||
} else {
|
||||
arrayItem = attribute;
|
||||
}
|
||||
|
||||
if (object) {
|
||||
if (
|
||||
(isAMapLike(object) && object.has(arrayItem)) ||
|
||||
(Array.isArray(object) &&
|
||||
typeof arrayItem === "number" &&
|
||||
object.length > arrayItem) ||
|
||||
(isPlainObject(object) && Reflect.has(object, arrayItem))
|
||||
) {
|
||||
if (shouldTestExistence) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type !== "array" && sandboxed) {
|
||||
sandboxPolicy.checkPropertyAllowed(object, attribute);
|
||||
}
|
||||
|
||||
return get(object, arrayItem);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
type === "array" ||
|
||||
isAMapLike(object) ||
|
||||
Array.isArray(object) ||
|
||||
object === null ||
|
||||
typeof object !== "object"
|
||||
) {
|
||||
if (shouldTestExistence) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (shouldIgnoreStrictCheck) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (object === null) {
|
||||
// object is null
|
||||
if (type === "array") {
|
||||
message = `Impossible to access a key ("${attribute}") on a null variable.`;
|
||||
} else {
|
||||
message = `Impossible to access an attribute ("${attribute}") on a null variable.`;
|
||||
}
|
||||
} else if (isAMapLike(object)) {
|
||||
if (object.size < 1) {
|
||||
message = `Index "${arrayItem}" is out of bounds as the array is empty.`;
|
||||
} else {
|
||||
message = `Index "${arrayItem}" is out of bounds for array [${[
|
||||
...object.values(),
|
||||
]}].`;
|
||||
}
|
||||
} else if (Array.isArray(object)) {
|
||||
if (object.length < 1) {
|
||||
message = `Index "${arrayItem}" is out of bounds as the array is empty.`;
|
||||
} else {
|
||||
message = `Index "${arrayItem}" is out of bounds for array [${[
|
||||
...object,
|
||||
]}].`;
|
||||
}
|
||||
} else if (type === "array") {
|
||||
// object is another kind of object
|
||||
message = `Impossible to access a key ("${attribute}") on a ${typeof object} variable ("${object.toString()}").`;
|
||||
} else {
|
||||
// object is a primitive
|
||||
message = `Impossible to access an attribute ("${attribute}") on a ${typeof object} variable ("${object}").`;
|
||||
}
|
||||
|
||||
throw new Error(message);
|
||||
}
|
||||
}
|
||||
|
||||
// ANY_CALL or METHOD_CALL
|
||||
if (object === null || !isObject(object) || isAMapLike(object)) {
|
||||
if (shouldTestExistence) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (shouldIgnoreStrictCheck) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (object === null) {
|
||||
message = `Impossible to invoke a method ("${attribute}") on a null variable.`;
|
||||
} else if (isAMapLike(object) || Array.isArray(object)) {
|
||||
message = `Impossible to invoke a method ("${attribute}") on an array.`;
|
||||
} else {
|
||||
message = `Impossible to invoke a method ("${attribute}") on a ${typeof object} variable ("${object}").`;
|
||||
}
|
||||
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
// object property
|
||||
if (type !== "method") {
|
||||
if (
|
||||
Reflect.has(object, attribute) &&
|
||||
typeof object[attribute] !== "function"
|
||||
) {
|
||||
if (shouldTestExistence) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sandboxed) {
|
||||
sandboxPolicy.checkPropertyAllowed(object, attribute);
|
||||
}
|
||||
|
||||
return get(object, attribute);
|
||||
}
|
||||
}
|
||||
|
||||
// object method
|
||||
// precedence: getXxx() > isXxx() > hasXxx()
|
||||
let methods: Array<string> = [];
|
||||
|
||||
for (let property of examineObject(object)) {
|
||||
let candidate = object[property];
|
||||
|
||||
if (typeof candidate === "function") {
|
||||
methods.push(property);
|
||||
}
|
||||
}
|
||||
|
||||
methods.sort();
|
||||
|
||||
let lcMethods: Array<string> = methods.map((method) => {
|
||||
return method.toLowerCase();
|
||||
});
|
||||
|
||||
let candidates = new Map();
|
||||
|
||||
for (let i = 0; i < methods.length; i++) {
|
||||
let method: string = methods[i];
|
||||
let lcName: string = lcMethods[i];
|
||||
|
||||
candidates.set(method, method);
|
||||
candidates.set(lcName, method);
|
||||
|
||||
let name: string = "";
|
||||
|
||||
if (lcName[0] === "g" && lcName.indexOf("get") === 0) {
|
||||
name = method.substr(3);
|
||||
lcName = lcName.substr(3);
|
||||
} else if (lcName[0] === "i" && lcName.indexOf("is") === 0) {
|
||||
name = method.substr(2);
|
||||
lcName = lcName.substr(2);
|
||||
} else if (lcName[0] === "h" && lcName.indexOf("has") === 0) {
|
||||
name = method.substr(3);
|
||||
lcName = lcName.substr(3);
|
||||
|
||||
if (lcMethods.includes("is" + lcName)) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip get() and is() methods (in which case, name is empty)
|
||||
if (name.length > 0) {
|
||||
if (!candidates.has(name)) {
|
||||
candidates.set(name, method);
|
||||
}
|
||||
|
||||
if (!candidates.has(lcName)) {
|
||||
candidates.set(lcName, method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let itemAsString: string = attribute as string;
|
||||
let method: string;
|
||||
let lcItem: string;
|
||||
|
||||
if (candidates.has(attribute)) {
|
||||
method = candidates.get(attribute);
|
||||
} else if (candidates.has((lcItem = itemAsString.toLowerCase()))) {
|
||||
method = candidates.get(lcItem);
|
||||
} else {
|
||||
if (shouldTestExistence) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (shouldIgnoreStrictCheck) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Neither the property "${attribute}" nor one of the methods ${attribute}()" or "get${attribute}()"/"is${attribute}()"/"has${attribute}()" exist and have public access in class "${object.constructor.name}".`
|
||||
);
|
||||
}
|
||||
|
||||
if (shouldTestExistence) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sandboxed) {
|
||||
sandboxPolicy.checkMethodAllowed(object, method);
|
||||
}
|
||||
|
||||
return get(object, method).apply(object, [...methodArguments.values()]);
|
||||
};
|
||||
|
||||
try {
|
||||
return Promise.resolve(_do());
|
||||
} catch (e) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
};
|
||||
|
||||
export const getAttributeSynchronously = (
|
||||
environment: TwingSynchronousEnvironment,
|
||||
object: any,
|
||||
|
@ -8,9 +8,7 @@
|
||||
*
|
||||
* @returns {any}
|
||||
*/
|
||||
import {TwingContext} from "../context";
|
||||
|
||||
export function getConstant(context: TwingContext<any, any> | Map<string, any>, name: string, object: any | null): any {
|
||||
export function getConstant(context: Map<string, any>, name: string, object: any | null): any {
|
||||
if (object) {
|
||||
return object[name];
|
||||
} else {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type {TwingFilter, TwingSynchronousFilter} from "../filter";
|
||||
import type {TwingSynchronousFilter} from "../filter";
|
||||
|
||||
/**
|
||||
* Get a filter by name.
|
||||
@ -7,7 +7,7 @@ import type {TwingFilter, TwingSynchronousFilter} from "../filter";
|
||||
*
|
||||
* @return {TwingFilter|false} A TwingFilter instance or false if the filter does not exist
|
||||
*/
|
||||
export const getFilter = <Filter extends TwingFilter | TwingSynchronousFilter>(
|
||||
export const getFilter = <Filter extends TwingSynchronousFilter>(
|
||||
filters: Map<string, Filter>,
|
||||
name: string
|
||||
): Filter | null => {
|
||||
|
@ -1,4 +1,3 @@
|
||||
import type {TwingFunction} from "../function";
|
||||
import {TwingSynchronousFunction} from "../function";
|
||||
|
||||
/**
|
||||
@ -7,7 +6,7 @@ import {TwingSynchronousFunction} from "../function";
|
||||
* @param {string} name function name
|
||||
* @returns {TwingFunction} A TwingFunction instance or null if the function does not exist
|
||||
*/
|
||||
export const getFunction = <Function extends TwingFunction | TwingSynchronousFunction>(
|
||||
export const getFunction = <Function extends TwingSynchronousFunction>(
|
||||
functions: Map<string, Function>,
|
||||
name: string
|
||||
): Function | null => {
|
||||
|
@ -1,4 +1,3 @@
|
||||
import type {TwingTest} from "../test";
|
||||
import {TwingSynchronousTest} from "../test";
|
||||
|
||||
/**
|
||||
@ -7,7 +6,7 @@ import {TwingSynchronousTest} from "../test";
|
||||
* @param {string} name The test name
|
||||
* @returns {TwingTest} A MyTest instance or null if the test does not exist
|
||||
*/
|
||||
export const getTest = <Test extends TwingTest | TwingSynchronousTest>(
|
||||
export const getTest = <Test extends TwingSynchronousTest>(
|
||||
tests: Map<string, Test>,
|
||||
name: string
|
||||
): Test | null => {
|
||||
|
@ -1,7 +1,6 @@
|
||||
import {TwingContext} from "../context";
|
||||
import {iteratorToMap} from "./iterator-to-map";
|
||||
|
||||
export type MapLike<K extends string, V> = Map<K, V> | TwingContext<K, V>;
|
||||
export type MapLike<K extends string, V> = Map<K, V>;
|
||||
|
||||
export function isAMapLike(candidate: any): candidate is MapLike<any, any> {
|
||||
return candidate !== null &&
|
||||
|
@ -1,6 +1,4 @@
|
||||
import type {TwingContext} from "../context";
|
||||
|
||||
export function mergeIterables<V>(iterable1: TwingContext<any, V> | Map<any, V>, iterable2: TwingContext<any, V> | Map<any, V>): Map<any, V> {
|
||||
export function mergeIterables<V>(iterable1: Map<any, V>, iterable2: Map<any, V>): Map<any, V> {
|
||||
let result = new Map();
|
||||
|
||||
let index = 0;
|
||||
|
@ -1,77 +0,0 @@
|
||||
type Comparator = (a: any, b: any) => Promise<-1 | 0 | 1>;
|
||||
|
||||
export const sortAsynchronously = (array: Array<any>, comparator: Comparator) => {
|
||||
/**
|
||||
* return the median value among x, y, and z
|
||||
*/
|
||||
const getPivot = async (x: any, y: any, z: any, comparator: Comparator): Promise<any> => {
|
||||
if (await comparator(x, y) < 0) {
|
||||
if (await comparator(y, z) < 0) {
|
||||
return y;
|
||||
}
|
||||
else if (await comparator(z, x) < 0) {
|
||||
return x;
|
||||
}
|
||||
else {
|
||||
return z;
|
||||
}
|
||||
}
|
||||
else if (await comparator(y, z) > 0) {
|
||||
return y;
|
||||
}
|
||||
else if (await comparator(z, x) > 0) {
|
||||
return x;
|
||||
}
|
||||
else {
|
||||
return z;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Asynchronous quick sort.
|
||||
*
|
||||
* @see https://gist.github.com/kimamula/fa34190db624239111bbe0deba72a6ab
|
||||
*
|
||||
* @param array The array to sort
|
||||
* @param comparator The comparator function
|
||||
* @param left The index where the range of elements to be sorted starts
|
||||
* @param right The index where the range of elements to be sorted ends
|
||||
*/
|
||||
const quickSort = async (array: Array<any>, comparator: Comparator, left = 0, right = array.length - 1): Promise<Array<any>> => {
|
||||
if (left < right) {
|
||||
let i = left;
|
||||
let j = right;
|
||||
let tmp;
|
||||
|
||||
const pivot = await getPivot(array[i], array[i + Math.floor((j - i) / 2)], array[j], comparator);
|
||||
|
||||
while (true) {
|
||||
while (await comparator(array[i], pivot) < 0) {
|
||||
i++;
|
||||
}
|
||||
|
||||
while (await comparator(pivot, array[j]) < 0) {
|
||||
j--;
|
||||
}
|
||||
|
||||
if (i >= j) {
|
||||
break;
|
||||
}
|
||||
|
||||
tmp = array[i];
|
||||
array[i] = array[j];
|
||||
array[j] = tmp;
|
||||
|
||||
i++;
|
||||
j--;
|
||||
}
|
||||
|
||||
await quickSort(array, comparator, left, i - 1);
|
||||
await quickSort(array, comparator, j + 1, right);
|
||||
}
|
||||
|
||||
return array;
|
||||
};
|
||||
|
||||
return quickSort(array, comparator);
|
||||
};
|
@ -2,22 +2,6 @@ import {createRuntimeError} from "../error/runtime";
|
||||
import type {TwingSource} from "../source";
|
||||
import {isATwingError} from "../error";
|
||||
|
||||
export function getTraceableMethod<M extends (...args: Array<any>) => Promise<any>>(method: M, location: {
|
||||
line: number;
|
||||
column: number;
|
||||
}, templateSource: TwingSource): M {
|
||||
return ((...args: Array<any>) => {
|
||||
return method(...args)
|
||||
.catch((error) => {
|
||||
if (!isATwingError(error)) {
|
||||
throw createRuntimeError(error.message, location, templateSource, error);
|
||||
}
|
||||
|
||||
throw error;
|
||||
});
|
||||
}) as typeof method;
|
||||
}
|
||||
|
||||
export function getSynchronousTraceableMethod<M extends (...args: Array<any>) => any>(method: M, location: {
|
||||
line: number;
|
||||
column: number;
|
||||
|
@ -1,5 +1,4 @@
|
||||
import type {TwingBaseNode} from "./node";
|
||||
import type {TwingExecutionContext} from "./execution-context";
|
||||
import {executeBinaryNodeSynchronously} from "./node-executor/expression/binary";
|
||||
import {executeTemplateNodeSynchronously} from "./node-executor/template";
|
||||
import {executePrintNodeSynchronously} from "./node-executor/print";
|
||||
@ -46,11 +45,6 @@ import {executeCommentNodeSynchronously} from "./node-executor/comment";
|
||||
import {executeBaseNodeSynchronously} from "./node-executor/base";
|
||||
import {TwingSynchronousExecutionContext} from "./execution-context";
|
||||
|
||||
export type TwingNodeExecutor<Node extends TwingBaseNode = TwingBaseNode> = (
|
||||
node: Node,
|
||||
executionContext: TwingExecutionContext
|
||||
) => Promise<any>;
|
||||
|
||||
export type TwingSynchronousNodeExecutor<Node extends TwingBaseNode = TwingBaseNode> = (
|
||||
node: Node,
|
||||
executionContext: TwingSynchronousExecutionContext
|
||||
|
@ -1,35 +1,9 @@
|
||||
import {TwingNodeExecutor, TwingSynchronousNodeExecutor} from "../node-executor";
|
||||
import {TwingSynchronousNodeExecutor} from "../node-executor";
|
||||
import {TwingApplyNode} from "../node/apply";
|
||||
import {getKeyValuePairs} from "../helpers/get-key-value-pairs";
|
||||
import {createFilterNode} from "../node/expression/call/filter";
|
||||
import {createConstantNode} from "../node/expression/constant";
|
||||
|
||||
export const executeApplyNode: TwingNodeExecutor<TwingApplyNode> = (node, executionContext) => {
|
||||
const {outputBuffer, nodeExecutor: execute} = executionContext;
|
||||
const {body, filters} = node.children;
|
||||
const {line, column} = node;
|
||||
|
||||
outputBuffer.start();
|
||||
|
||||
return execute(body, executionContext)
|
||||
.then(async () => {
|
||||
let content = outputBuffer.getAndClean();
|
||||
|
||||
const keyValuePairs = getKeyValuePairs(filters);
|
||||
|
||||
while (keyValuePairs.length > 0) {
|
||||
const {key, value: filterArguments} = keyValuePairs.pop()!;
|
||||
|
||||
const filterName = key.attributes.value as string;
|
||||
const filterNode = createFilterNode(createConstantNode(content, line, column), filterName, filterArguments, line, column);
|
||||
|
||||
content = await execute(filterNode, executionContext);
|
||||
}
|
||||
|
||||
outputBuffer.echo(content);
|
||||
});
|
||||
};
|
||||
|
||||
export const executeApplyNodeSynchronously: TwingSynchronousNodeExecutor<TwingApplyNode> = (node, executionContext) => {
|
||||
const {outputBuffer, nodeExecutor: execute} = executionContext;
|
||||
const {body, filters} = node.children;
|
||||
|
@ -1,15 +1,4 @@
|
||||
import type {TwingNodeExecutor, TwingSynchronousNodeExecutor} from "../node-executor";
|
||||
|
||||
export const executeBaseNode: TwingNodeExecutor = async (node, executionContext) => {
|
||||
const output: Array<any> = [];
|
||||
const {nodeExecutor: execute} = executionContext;
|
||||
|
||||
for (const [, child] of Object.entries(node.children)) {
|
||||
output.push(await execute(child, executionContext));
|
||||
}
|
||||
|
||||
return output;
|
||||
};
|
||||
import type {TwingSynchronousNodeExecutor} from "../node-executor";
|
||||
|
||||
export const executeBaseNodeSynchronously: TwingSynchronousNodeExecutor = (node, executionContext) => {
|
||||
const output: Array<any> = [];
|
||||
|
@ -1,25 +1,6 @@
|
||||
import type {TwingNodeExecutor, TwingSynchronousNodeExecutor} from "../node-executor";
|
||||
import type {TwingSynchronousNodeExecutor} from "../node-executor";
|
||||
import type {TwingBlockReferenceNode} from "../node/block-reference";
|
||||
import {getSynchronousTraceableMethod, getTraceableMethod} from "../helpers/traceable-method";
|
||||
|
||||
export const executeBlockReferenceNode: TwingNodeExecutor<TwingBlockReferenceNode> = (node, executionContext) => {
|
||||
const {
|
||||
template,
|
||||
context
|
||||
} = executionContext;
|
||||
const {name} = node.attributes;
|
||||
|
||||
const displayBlock = getTraceableMethod(template.displayBlock, node, template.source);
|
||||
|
||||
return displayBlock(
|
||||
{
|
||||
...executionContext,
|
||||
context: context.clone()
|
||||
},
|
||||
name,
|
||||
true,
|
||||
);
|
||||
};
|
||||
import {getSynchronousTraceableMethod} from "../helpers/traceable-method";
|
||||
|
||||
export const executeBlockReferenceNodeSynchronously: TwingSynchronousNodeExecutor<TwingBlockReferenceNode> = (node, executionContext) => {
|
||||
const {
|
||||
|
@ -1,39 +1,8 @@
|
||||
import {TwingNodeExecutor, TwingSynchronousNodeExecutor} from "../node-executor";
|
||||
import {TwingSynchronousNodeExecutor} from "../node-executor";
|
||||
import {TwingCheckSecurityNode} from "../node/check-security";
|
||||
import type {TwingNode} from "../node";
|
||||
import {createRuntimeError} from "../error/runtime";
|
||||
|
||||
export const executeCheckSecurityNode: TwingNodeExecutor<TwingCheckSecurityNode> = (node, executionContext) => {
|
||||
const {template, environment, sandboxed} = executionContext;
|
||||
const {usedTags, usedFunctions, usedFilters} = node.attributes;
|
||||
|
||||
if (sandboxed) {
|
||||
const issue = environment.sandboxPolicy.checkSecurity(
|
||||
[...usedTags.keys()],
|
||||
[...usedFilters.keys()],
|
||||
[...usedFunctions.keys()]
|
||||
);
|
||||
|
||||
if (issue !== null) {
|
||||
const {type, token} = issue;
|
||||
|
||||
let node: TwingNode;
|
||||
|
||||
if (type === "tag") {
|
||||
node = usedTags.get(token)!;
|
||||
} else if (type === "filter") {
|
||||
node = usedFilters.get(token)!
|
||||
} else {
|
||||
node = usedFunctions.get(token)!;
|
||||
}
|
||||
|
||||
throw createRuntimeError(issue.message, node, template.source);
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
export const executeCheckSecurityNodeSynchronously: TwingSynchronousNodeExecutor<TwingCheckSecurityNode> = (node, executionContext) => {
|
||||
const {template, environment, sandboxed} = executionContext;
|
||||
const {usedTags, usedFunctions, usedFilters} = node.attributes;
|
||||
|
@ -1,33 +1,6 @@
|
||||
import {TwingNodeExecutor, TwingSynchronousNodeExecutor} from "../node-executor";
|
||||
import {TwingSynchronousNodeExecutor} from "../node-executor";
|
||||
import {TwingCheckToStringNode} from "../node/check-to-string";
|
||||
import {getSynchronousTraceableMethod, getTraceableMethod} from "../helpers/traceable-method";
|
||||
|
||||
export const executeCheckToStringNode: TwingNodeExecutor<TwingCheckToStringNode> = (node, executionContext) => {
|
||||
const {template, environment, nodeExecutor: execute, sandboxed} = executionContext;
|
||||
const {value: valueNode} = node.children;
|
||||
const {sandboxPolicy} = environment;
|
||||
|
||||
return execute(valueNode, executionContext)
|
||||
.then((value) => {
|
||||
if (sandboxed) {
|
||||
const assertToStringAllowed = getTraceableMethod((value: any) => {
|
||||
if ((value !== null) && (typeof value === 'object')) {
|
||||
try {
|
||||
sandboxPolicy.checkMethodAllowed(value, 'toString');
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve(value);
|
||||
}, valueNode, template.source)
|
||||
|
||||
return assertToStringAllowed(value);
|
||||
}
|
||||
|
||||
return value;
|
||||
});
|
||||
};
|
||||
import {getSynchronousTraceableMethod} from "../helpers/traceable-method";
|
||||
|
||||
export const executeCheckToStringNodeSynchronously: TwingSynchronousNodeExecutor<TwingCheckToStringNode> = (node, executionContext) => {
|
||||
const {template, environment, nodeExecutor: execute, sandboxed} = executionContext;
|
||||
|
@ -1,10 +1,6 @@
|
||||
import type {TwingNodeExecutor, TwingSynchronousNodeExecutor} from "../node-executor";
|
||||
import type {TwingSynchronousNodeExecutor} from "../node-executor";
|
||||
import type {TwingCommentNode} from "../node/comment";
|
||||
|
||||
export const executeCommentNode: TwingNodeExecutor<TwingCommentNode> = () => {
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
export const executeCommentNodeSynchronously: TwingSynchronousNodeExecutor<TwingCommentNode> = () => {
|
||||
return;
|
||||
};
|
||||
|
@ -1,10 +1,6 @@
|
||||
import type {TwingNodeExecutor, TwingSynchronousNodeExecutor} from "../node-executor";
|
||||
import type {TwingSynchronousNodeExecutor} from "../node-executor";
|
||||
import type {TwingConstantNode} from "../node/expression/constant";
|
||||
|
||||
export const executeConstantNode: TwingNodeExecutor<TwingConstantNode> = (node) => {
|
||||
return Promise.resolve(node.attributes.value);
|
||||
};
|
||||
|
||||
export const executeConstantNodeSynchronously: TwingSynchronousNodeExecutor<TwingConstantNode> = (node) => {
|
||||
return node.attributes.value;
|
||||
};
|
||||
|
@ -1,16 +1,6 @@
|
||||
import {TwingNodeExecutor, TwingSynchronousNodeExecutor} from "../node-executor";
|
||||
import {TwingSynchronousNodeExecutor} from "../node-executor";
|
||||
import {TwingDeprecatedNode} from "../node/deprecated";
|
||||
|
||||
export const executeDeprecatedNode: TwingNodeExecutor<TwingDeprecatedNode> = (node, executionContext) => {
|
||||
const {template, nodeExecutor: execute} = executionContext;
|
||||
const {message} = node.children;
|
||||
|
||||
return execute(message, executionContext)
|
||||
.then((message) => {
|
||||
console.warn(`${message} ("${template.name}" at line ${node.line}, column ${node.column})`);
|
||||
});
|
||||
};
|
||||
|
||||
export const executeDeprecatedNodeSynchronously: TwingSynchronousNodeExecutor<TwingDeprecatedNode> = (node, executionContext) => {
|
||||
const {template, nodeExecutor: execute} = executionContext;
|
||||
const {message: messageNode} = node.children;
|
||||
|
@ -1,10 +1,6 @@
|
||||
import {TwingNodeExecutor, TwingSynchronousNodeExecutor} from "../node-executor";
|
||||
import {TwingSynchronousNodeExecutor} from "../node-executor";
|
||||
import {TwingDoNode} from "../node/do";
|
||||
|
||||
export const executeDoNode: TwingNodeExecutor<TwingDoNode> = (node, executionContext) => {
|
||||
return executionContext.nodeExecutor(node.children.body, executionContext);
|
||||
};
|
||||
|
||||
export const executeDoNodeSynchronously: TwingSynchronousNodeExecutor<TwingDoNode> = (node, executionContext) => {
|
||||
return executionContext.nodeExecutor(node.children.body, executionContext);
|
||||
};
|
||||
|
@ -1,26 +1,7 @@
|
||||
import type {TwingNodeExecutor, TwingSynchronousNodeExecutor} from "../../node-executor";
|
||||
import type {TwingSynchronousNodeExecutor} from "../../node-executor";
|
||||
import {type TwingBaseArrayNode} from "../../node/expression/array";
|
||||
import type {TwingNode} from "../../node";
|
||||
import {getKeyValuePairs} from "../../helpers/get-key-value-pairs";
|
||||
import {getValues} from "../../context";
|
||||
|
||||
export const executeArrayNode: TwingNodeExecutor<TwingBaseArrayNode<any>> = async (baseNode, executionContext) => {
|
||||
const {nodeExecutor: execute} = executionContext;
|
||||
const keyValuePairs = getKeyValuePairs(baseNode);
|
||||
const array: Array<any> = [];
|
||||
|
||||
for (const {value: valueNode} of keyValuePairs) {
|
||||
const value = await execute(valueNode, executionContext);
|
||||
|
||||
if ((valueNode as TwingNode).type === "spread") {
|
||||
array.push(...value);
|
||||
} else {
|
||||
array.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
return array;
|
||||
};
|
||||
|
||||
export const executeArrayNodeSynchronously: TwingSynchronousNodeExecutor<TwingBaseArrayNode<any>> = (baseNode, executionContext) => {
|
||||
const {nodeExecutor: execute} = executionContext;
|
||||
@ -30,10 +11,8 @@ export const executeArrayNodeSynchronously: TwingSynchronousNodeExecutor<TwingBa
|
||||
for (const {value: valueNode} of keyValuePairs) {
|
||||
const value = execute(valueNode, executionContext);
|
||||
|
||||
if ((valueNode as TwingNode).type === "spread") {
|
||||
const values = getValues(value);
|
||||
|
||||
array.push(...values);
|
||||
if ((valueNode as TwingNode).type === "spread") {
|
||||
array.push(...Object.values(value));
|
||||
} else {
|
||||
array.push(value);
|
||||
}
|
||||
|
@ -1,26 +1,6 @@
|
||||
import {TwingNodeExecutor, TwingSynchronousNodeExecutor} from "../../node-executor";
|
||||
import {TwingSynchronousNodeExecutor} from "../../node-executor";
|
||||
import {TwingArrowFunctionNode} from "../../node/expression/arrow-function";
|
||||
|
||||
export const executeArrowFunctionNode: TwingNodeExecutor<TwingArrowFunctionNode> = (node, executionContext) => {
|
||||
const {context, nodeExecutor: execute} = executionContext;
|
||||
const {body, names} = node.children;
|
||||
const assignmentNodes = Object.values(names.children);
|
||||
|
||||
return Promise.resolve((...functionArgs: Array<any>): Promise<any> => {
|
||||
let index = 0;
|
||||
|
||||
for (const assignmentNode of assignmentNodes) {
|
||||
const {name} = assignmentNode.attributes;
|
||||
|
||||
context.set(name, functionArgs[index]);
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return execute(body, executionContext);
|
||||
});
|
||||
};
|
||||
|
||||
export const executeArrowFunctionNodeSynchronously: TwingSynchronousNodeExecutor<TwingArrowFunctionNode> = (node, executionContext) => {
|
||||
const {context, nodeExecutor: execute} = executionContext;
|
||||
const {body, names} = node.children;
|
||||
|
@ -1,10 +1,6 @@
|
||||
import type {TwingNodeExecutor, TwingSynchronousNodeExecutor} from "../../node-executor";
|
||||
import type {TwingSynchronousNodeExecutor} from "../../node-executor";
|
||||
import type {TwingAssignmentNode} from "../../node/expression/assignment";
|
||||
|
||||
export const executeAssignmentNode: TwingNodeExecutor<TwingAssignmentNode> = (node) => {
|
||||
return Promise.resolve(node.attributes.name);
|
||||
};
|
||||
|
||||
export const executeAssignmentNodeSynchronously: TwingSynchronousNodeExecutor<TwingAssignmentNode> = (node) => {
|
||||
return node.attributes.name;
|
||||
};
|
||||
|
@ -1,33 +1,7 @@
|
||||
import {TwingNodeExecutor, TwingSynchronousNodeExecutor} from "../../node-executor";
|
||||
import {TwingSynchronousNodeExecutor} from "../../node-executor";
|
||||
import {TwingAttributeAccessorNode} from "../../node/expression/attribute-accessor";
|
||||
import {getSynchronousTraceableMethod, getTraceableMethod} from "../../helpers/traceable-method";
|
||||
import {getAttribute, getAttributeSynchronously} from "../../helpers/get-attribute";
|
||||
|
||||
export const executeAttributeAccessorNode: TwingNodeExecutor<TwingAttributeAccessorNode> = (node, executionContext) => {
|
||||
const {template, sandboxed, environment, nodeExecutor: execute, strict} = executionContext;
|
||||
const {target, attribute, arguments: methodArguments} = node.children;
|
||||
const {type, shouldIgnoreStrictCheck, shouldTestExistence} = node.attributes;
|
||||
|
||||
return Promise.all([
|
||||
execute(target, executionContext),
|
||||
execute(attribute, executionContext),
|
||||
execute(methodArguments, executionContext)
|
||||
]).then(([target, attribute, methodArguments]) => {
|
||||
const traceableGetAttribute = getTraceableMethod(getAttribute, node, template.source);
|
||||
|
||||
return traceableGetAttribute(
|
||||
environment,
|
||||
target,
|
||||
attribute,
|
||||
methodArguments,
|
||||
type,
|
||||
shouldTestExistence,
|
||||
shouldIgnoreStrictCheck || null,
|
||||
sandboxed,
|
||||
strict
|
||||
)
|
||||
})
|
||||
};
|
||||
import {getSynchronousTraceableMethod} from "../../helpers/traceable-method";
|
||||
import {getAttributeSynchronously} from "../../helpers/get-attribute";
|
||||
|
||||
export const executeAttributeAccessorNodeSynchronously: TwingSynchronousNodeExecutor<TwingAttributeAccessorNode> = (node, executionContext) => {
|
||||
const {template, sandboxed, environment, nodeExecutor: execute, strict} = executionContext;
|
||||
|
@ -1,169 +1,13 @@
|
||||
import type {TwingNodeExecutor, TwingSynchronousNodeExecutor} from "../../node-executor";
|
||||
import type {TwingSynchronousNodeExecutor} from "../../node-executor";
|
||||
import type {TwingBaseBinaryNode} from "../../node/expression/binary";
|
||||
import {compare} from "../../helpers/compare";
|
||||
import {concatenate} from "../../helpers/concatenate";
|
||||
import {every, everySynchronously, isAMapLike, some, someSynchronously} from "../../helpers/map-like";
|
||||
import {everySynchronously, isAMapLike, someSynchronously} from "../../helpers/map-like";
|
||||
import {isIn} from "../../helpers/is-in";
|
||||
import {parseRegularExpression} from "../../helpers/parse-regular-expression";
|
||||
import {createRange} from "../../helpers/create-range";
|
||||
import {createRuntimeError} from "../../error/runtime";
|
||||
|
||||
export const executeBinaryNode: TwingNodeExecutor<TwingBaseBinaryNode<any>> = async (node, executionContext) => {
|
||||
const {left, right} = node.children;
|
||||
const {nodeExecutor: execute, template} = executionContext;
|
||||
|
||||
switch (node.type) {
|
||||
case "add": {
|
||||
return await execute(left, executionContext) + await execute(right, executionContext);
|
||||
}
|
||||
case "and": {
|
||||
return !!(await execute(left, executionContext) && await execute(right, executionContext));
|
||||
}
|
||||
case "bitwise_and": {
|
||||
return await execute(left, executionContext) & await execute(right, executionContext);
|
||||
}
|
||||
case "bitwise_or": {
|
||||
return await execute(left, executionContext) | await execute(right, executionContext);
|
||||
}
|
||||
case "bitwise_xor": {
|
||||
return await execute(left, executionContext) ^ await execute(right, executionContext);
|
||||
}
|
||||
case "concatenate": {
|
||||
const leftValue = await execute(left, executionContext);
|
||||
const rightValue = await execute(right, executionContext);
|
||||
|
||||
return concatenate(leftValue, rightValue);
|
||||
}
|
||||
case "divide": {
|
||||
return await execute(left, executionContext) / await execute(right, executionContext);
|
||||
}
|
||||
case "divide_and_floor": {
|
||||
return Math.floor(await execute(left, executionContext) / await execute(right, executionContext));
|
||||
}
|
||||
case "ends_with": {
|
||||
const leftValue = await execute(left, executionContext);
|
||||
|
||||
if (typeof leftValue !== "string") {
|
||||
return false;
|
||||
}
|
||||
|
||||
const rightValue = await execute(right, executionContext);
|
||||
|
||||
if (typeof rightValue !== "string") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return rightValue.length < 1 || leftValue.endsWith(rightValue);
|
||||
}
|
||||
case "has_every": {
|
||||
const leftValue = await execute(left, executionContext);
|
||||
const rightValue = await execute(right, executionContext);
|
||||
|
||||
if (typeof rightValue !== "function") {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
if (!isAMapLike(leftValue) && !Array.isArray(leftValue)) {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
return every(leftValue, rightValue);
|
||||
}
|
||||
case "has_some": {
|
||||
const leftValue = await execute(left, executionContext);
|
||||
const rightValue = await execute(right, executionContext);
|
||||
|
||||
if (typeof rightValue !== "function") {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
if (!isAMapLike(leftValue) && !Array.isArray(leftValue)) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
return some(leftValue, rightValue);
|
||||
}
|
||||
case "is_equal_to": {
|
||||
const leftValue = await execute(left, executionContext);
|
||||
const rightValue = await execute(right, executionContext);
|
||||
|
||||
return compare(leftValue, rightValue);
|
||||
}
|
||||
case "is_greater_than": {
|
||||
return await execute(left, executionContext) > await execute(right, executionContext);
|
||||
}
|
||||
case "is_greater_than_or_equal_to": {
|
||||
return await execute(left, executionContext) >= await execute(right, executionContext);
|
||||
}
|
||||
case "is_in": {
|
||||
return isIn(await execute(left, executionContext), await execute(right, executionContext));
|
||||
}
|
||||
case "is_less_than": {
|
||||
return await execute(left, executionContext) < await execute(right, executionContext);
|
||||
}
|
||||
case "is_less_than_or_equal_to": {
|
||||
return await execute(left, executionContext) <= await execute(right, executionContext);
|
||||
}
|
||||
case "is_not_equal_to": {
|
||||
return Promise.resolve(!compare(await execute(left, executionContext), await execute(right, executionContext)))
|
||||
}
|
||||
case "is_not_in": {
|
||||
return Promise.resolve(!isIn(await execute(left, executionContext), await execute(right, executionContext)))
|
||||
}
|
||||
case "matches": {
|
||||
return parseRegularExpression(
|
||||
await execute(right, executionContext)
|
||||
).test(
|
||||
await execute(left, executionContext)
|
||||
);
|
||||
}
|
||||
case "modulo": {
|
||||
return await execute(left, executionContext) % await execute(right, executionContext);
|
||||
}
|
||||
case "multiply": {
|
||||
return await execute(left, executionContext) * await execute(right, executionContext);
|
||||
}
|
||||
case "or": {
|
||||
return !!(await execute(left, executionContext) || await execute(right, executionContext));
|
||||
}
|
||||
case "power": {
|
||||
return Math.pow(await execute(left, executionContext), await execute(right, executionContext));
|
||||
}
|
||||
case "range": {
|
||||
const leftValue = await execute(left, executionContext);
|
||||
const rightValue = await execute(right, executionContext);
|
||||
|
||||
return createRange(leftValue, rightValue, 1);
|
||||
}
|
||||
case "spaceship": {
|
||||
const leftValue = await execute(left, executionContext);
|
||||
const rightValue = await execute(right, executionContext);
|
||||
|
||||
return compare(leftValue, rightValue) ? 0 : (leftValue < rightValue ? -1 : 1);
|
||||
}
|
||||
case "starts_with": {
|
||||
const leftValue = await execute(left, executionContext);
|
||||
|
||||
if (typeof leftValue !== "string") {
|
||||
return false;
|
||||
}
|
||||
|
||||
const rightValue = await execute(right, executionContext);
|
||||
|
||||
if (typeof rightValue !== "string") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return rightValue.length < 1 || leftValue.startsWith(rightValue);
|
||||
}
|
||||
case "subtract": {
|
||||
return await execute(left, executionContext) - await execute(right, executionContext);
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.reject(createRuntimeError(`Unrecognized binary node of type "${node.type}"`, node, template.source));
|
||||
};
|
||||
|
||||
export const executeBinaryNodeSynchronously: TwingSynchronousNodeExecutor<TwingBaseBinaryNode<any>> = (node, executionContext): boolean | number | string | Map<number, any> => {
|
||||
const {left, right} = node.children;
|
||||
const {nodeExecutor: execute, template} = executionContext;
|
||||
|
@ -1,63 +1,7 @@
|
||||
import type {TwingNodeExecutor, TwingSynchronousNodeExecutor} from "../../node-executor";
|
||||
import type {TwingSynchronousNodeExecutor} from "../../node-executor";
|
||||
import type {TwingBlockFunctionNode} from "../../node/expression/block-function";
|
||||
import {TwingSynchronousTemplate, TwingTemplate} from "../../template";
|
||||
import {getSynchronousTraceableMethod, getTraceableMethod} from "../../helpers/traceable-method";
|
||||
|
||||
export const executeBlockFunction: TwingNodeExecutor<TwingBlockFunctionNode> = async (node, executionContext) => {
|
||||
const {
|
||||
template,
|
||||
context,
|
||||
nodeExecutor: execute,
|
||||
blocks,
|
||||
outputBuffer
|
||||
} = executionContext;
|
||||
const {template: templateNode, name: blockNameNode} = node.children;
|
||||
|
||||
const blockName = await execute(blockNameNode, executionContext);
|
||||
|
||||
let resolveTemplate: Promise<TwingTemplate>;
|
||||
|
||||
if (templateNode) {
|
||||
const templateName = await execute(templateNode, executionContext);
|
||||
|
||||
const loadTemplate = getTraceableMethod(
|
||||
template.loadTemplate,
|
||||
templateNode,
|
||||
template.source
|
||||
);
|
||||
|
||||
resolveTemplate = loadTemplate(executionContext, templateName);
|
||||
}
|
||||
else {
|
||||
resolveTemplate = Promise.resolve(template)
|
||||
}
|
||||
|
||||
return resolveTemplate
|
||||
.then<Promise<boolean | string>>((templateOfTheBlock) => {
|
||||
if (node.attributes.shouldTestExistence) {
|
||||
const hasBlock = getTraceableMethod(templateOfTheBlock.hasBlock, node, template.source);
|
||||
|
||||
return hasBlock({
|
||||
...executionContext,
|
||||
context: context.clone()
|
||||
}, blockName, blocks);
|
||||
}
|
||||
else {
|
||||
const displayBlock = getTraceableMethod(templateOfTheBlock.displayBlock, node, template.source);
|
||||
|
||||
let useBlocks = templateNode === undefined;
|
||||
|
||||
outputBuffer.start();
|
||||
|
||||
return displayBlock({
|
||||
...executionContext,
|
||||
context: context.clone()
|
||||
}, blockName, useBlocks).then<string>(() => {
|
||||
return outputBuffer.getAndClean();
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
import {TwingSynchronousTemplate} from "../../template";
|
||||
import {getSynchronousTraceableMethod} from "../../helpers/traceable-method";
|
||||
|
||||
export const executeSynchronousBlockFunction: TwingSynchronousNodeExecutor<TwingBlockFunctionNode> = (node, executionContext) => {
|
||||
const {
|
||||
|
@ -1,8 +1,8 @@
|
||||
import type {TwingNodeExecutor, TwingSynchronousNodeExecutor} from "../../node-executor";
|
||||
import type {TwingSynchronousNodeExecutor} from "../../node-executor";
|
||||
import type {TwingBaseCallNode} from "../../node/expression/call";
|
||||
import {TwingCallableArgument, TwingCallableWrapper, TwingSynchronousCallableWrapper} from "../../callable-wrapper";
|
||||
import {TwingCallableArgument, TwingSynchronousCallableWrapper} from "../../callable-wrapper";
|
||||
import {createRuntimeError} from "../../error/runtime";
|
||||
import {getSynchronousTraceableMethod, getTraceableMethod} from "../../helpers/traceable-method";
|
||||
import {getSynchronousTraceableMethod} from "../../helpers/traceable-method";
|
||||
import {TwingArrayNode} from "../../node/expression/array";
|
||||
import {TwingBaseNode} from "../../node";
|
||||
import {createConstantNode, TwingConstantNode} from "../../node/expression/constant";
|
||||
@ -11,7 +11,7 @@ import {getKeyValuePairs} from "../../helpers/get-key-value-pairs";
|
||||
import {getTest} from "../../helpers/get-test";
|
||||
import {getFunction} from "../../helpers/get-function";
|
||||
import {getFilter} from "../../helpers/get-filter";
|
||||
import type {TwingSynchronousTemplate, TwingTemplate} from "../../template";
|
||||
import type {TwingSynchronousTemplate} from "../../template";
|
||||
|
||||
const array_merge = require('locutus/php/array/array_merge');
|
||||
const snakeCase = require('snake-case');
|
||||
@ -22,7 +22,7 @@ const normalizeName = (name: string) => {
|
||||
|
||||
const getArguments = (
|
||||
node: TwingBaseCallNode<any>,
|
||||
template: TwingTemplate | TwingSynchronousTemplate,
|
||||
template: TwingSynchronousTemplate,
|
||||
argumentsNode: TwingArrayNode,
|
||||
acceptedArguments: Array<TwingCallableArgument>,
|
||||
isVariadic: boolean
|
||||
@ -127,64 +127,6 @@ const getArguments = (
|
||||
return arguments_;
|
||||
}
|
||||
|
||||
export const executeCallNode: TwingNodeExecutor<TwingBaseCallNode<any>> = async (node, executionContext) => {
|
||||
const {type} = node;
|
||||
const {template, environment, nodeExecutor: execute} = executionContext
|
||||
const {operatorName} = node.attributes;
|
||||
|
||||
let callableWrapper: TwingCallableWrapper | null;
|
||||
|
||||
switch (type) {
|
||||
case "filter":
|
||||
callableWrapper = getFilter(environment.filters, operatorName);
|
||||
break;
|
||||
|
||||
case "function":
|
||||
callableWrapper = getFunction(environment.functions, operatorName);
|
||||
break;
|
||||
|
||||
// for some reason, using `case "test"` makes the compiler assume that callableWrapper is used
|
||||
// before it is assigned a value; this is probably a bug of the compiler
|
||||
default:
|
||||
callableWrapper = getTest(environment.tests, operatorName);
|
||||
break;
|
||||
}
|
||||
|
||||
if (callableWrapper === null) {
|
||||
throw createRuntimeError(`Unknown ${type} "${operatorName}".`, node, template.source);
|
||||
}
|
||||
|
||||
const {operand, arguments: callArguments} = node.children;
|
||||
|
||||
const argumentNodes = getArguments(
|
||||
node,
|
||||
template,
|
||||
callArguments,
|
||||
callableWrapper.acceptedArguments,
|
||||
callableWrapper.isVariadic
|
||||
);
|
||||
|
||||
const actualArguments: Array<any> = [];
|
||||
|
||||
actualArguments.push(...callableWrapper!.nativeArguments);
|
||||
|
||||
if (operand) {
|
||||
actualArguments.push(await execute(operand, executionContext));
|
||||
}
|
||||
|
||||
const providedArguments = await Promise.all([
|
||||
...argumentNodes.map((node) => execute(node, executionContext))
|
||||
]);
|
||||
|
||||
actualArguments.push(...providedArguments);
|
||||
|
||||
const traceableCallable = getTraceableMethod(callableWrapper.callable, node, template.source);
|
||||
|
||||
return traceableCallable(executionContext, ...actualArguments).then((value) => {
|
||||
return value;
|
||||
});
|
||||
};
|
||||
|
||||
export const executeCallNodeSynchronously: TwingSynchronousNodeExecutor<TwingBaseCallNode<any>> = (node, executionContext) => {
|
||||
const {type} = node;
|
||||
const {template, environment, nodeExecutor: execute} = executionContext
|
||||
|
@ -1,13 +1,6 @@
|
||||
import {TwingNodeExecutor, TwingSynchronousNodeExecutor} from "../../node-executor";
|
||||
import {TwingSynchronousNodeExecutor} from "../../node-executor";
|
||||
import {TwingBaseConditionalNode} from "../../node/expression/conditional";
|
||||
|
||||
export const executeConditionalNode: TwingNodeExecutor<TwingBaseConditionalNode<any>> = async (node, executionContext) => {
|
||||
const {expr1, expr2, expr3} = node.children;
|
||||
const {nodeExecutor: execute} = executionContext;
|
||||
|
||||
return (await execute(expr1, executionContext)) ? execute(expr2, executionContext) : execute(expr3, executionContext);
|
||||
};
|
||||
|
||||
export const executeConditionalNodeSynchronously: TwingSynchronousNodeExecutor<TwingBaseConditionalNode<any>> = (node, executionContext) => {
|
||||
const {expr1, expr2, expr3} = node.children;
|
||||
const {nodeExecutor: execute} = executionContext;
|
||||
|
@ -1,20 +1,7 @@
|
||||
import {TwingNodeExecutor, TwingSynchronousNodeExecutor} from "../../node-executor";
|
||||
import {TwingSynchronousNodeExecutor} from "../../node-executor";
|
||||
import {TwingEscapeNode} from "../../node/expression/escape";
|
||||
import {getSynchronousTraceableMethod, getTraceableMethod} from "../../helpers/traceable-method";
|
||||
import {escapeValue, escapeValueSynchronously} from "../../helpers/escape-value";
|
||||
|
||||
export const executeEscapeNode: TwingNodeExecutor<TwingEscapeNode> = (node, executionContext) => {
|
||||
const {template, environment, nodeExecutor: execute} = executionContext;
|
||||
const {strategy} = node.attributes;
|
||||
const {body} = node.children;
|
||||
|
||||
return execute(body, executionContext)
|
||||
.then((value) => {
|
||||
const traceableEscape = getTraceableMethod(escapeValue, node, template.source);
|
||||
|
||||
return traceableEscape(template, environment, value, strategy, null);
|
||||
});
|
||||
};
|
||||
import {getSynchronousTraceableMethod} from "../../helpers/traceable-method";
|
||||
import {escapeValueSynchronously} from "../../helpers/escape-value";
|
||||
|
||||
export const executeEscapeNodeSynchronously: TwingSynchronousNodeExecutor<TwingEscapeNode> = (node, executionContext) => {
|
||||
const {template, environment, nodeExecutor: execute} = executionContext;
|
||||
|
@ -1,31 +1,7 @@
|
||||
import type {TwingNodeExecutor, TwingSynchronousNodeExecutor} from "../../node-executor";
|
||||
import type {TwingSynchronousNodeExecutor} from "../../node-executor";
|
||||
import type {TwingHashNode} from "../../node/expression/hash";
|
||||
import type {TwingNode} from "../../node";
|
||||
import {getKeyValuePairs} from "../../helpers/get-key-value-pairs";
|
||||
import {getEntries} from "../../context";
|
||||
|
||||
export const executeHashNode: TwingNodeExecutor<TwingHashNode> = async (node, executionContext) => {
|
||||
const {nodeExecutor: execute} = executionContext;
|
||||
const keyValuePairs = getKeyValuePairs(node);
|
||||
const hash: Map<any, any> = new Map();
|
||||
|
||||
for (const {key: keyNode, value: valueNode} of keyValuePairs) {
|
||||
const [key, value] = await Promise.all([
|
||||
execute(keyNode, executionContext),
|
||||
execute(valueNode, executionContext)
|
||||
]);
|
||||
|
||||
if ((valueNode as TwingNode).type === "spread") {
|
||||
for (const [valueKey, valueValue] of value as Map<any, any>) {
|
||||
hash.set(valueKey, valueValue);
|
||||
}
|
||||
} else {
|
||||
hash.set(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
return hash;
|
||||
};
|
||||
|
||||
export const executeHashNodeSynchronously: TwingSynchronousNodeExecutor<TwingHashNode> = (node, executionContext) => {
|
||||
const {nodeExecutor: execute} = executionContext;
|
||||
@ -37,7 +13,7 @@ export const executeHashNodeSynchronously: TwingSynchronousNodeExecutor<TwingHas
|
||||
const value = execute(valueNode, executionContext);
|
||||
|
||||
if ((valueNode as TwingNode).type === "spread") {
|
||||
for (const [valueKey, valueValue] of getEntries(value as Record<any, any>)) {
|
||||
for (const [valueKey, valueValue] of Object.entries(value)) {
|
||||
hash.set(valueKey, valueValue);
|
||||
}
|
||||
} else {
|
||||
|
@ -1,63 +1,12 @@
|
||||
import type {TwingNodeExecutor, TwingSynchronousNodeExecutor} from "../../node-executor";
|
||||
import type {TwingSynchronousNodeExecutor} from "../../node-executor";
|
||||
import {
|
||||
TwingSynchronousTemplate,
|
||||
TwingSynchronousTemplateMacroHandler,
|
||||
TwingTemplate,
|
||||
TwingTemplateMacroHandler
|
||||
} from "../../template";
|
||||
import {createRuntimeError} from "../../error/runtime";
|
||||
import type {TwingMethodCallNode} from "../../node/expression/method-call";
|
||||
import {getKeyValuePairs} from "../../helpers/get-key-value-pairs";
|
||||
|
||||
export const executeMethodCall: TwingNodeExecutor<TwingMethodCallNode> = async (node, executionContext) => {
|
||||
const {template, aliases, nodeExecutor: execute} = executionContext;
|
||||
const {methodName, shouldTestExistence} = node.attributes;
|
||||
const {operand, arguments: methodArguments} = node.children;
|
||||
|
||||
if (shouldTestExistence) {
|
||||
return (aliases.get(operand.attributes.name) as TwingTemplate).hasMacro(methodName);
|
||||
} else {
|
||||
const keyValuePairs = getKeyValuePairs(methodArguments);
|
||||
|
||||
const macroArguments: Array<any> = [];
|
||||
|
||||
for (const {value: valueNode} of keyValuePairs) {
|
||||
const value = await execute(valueNode, executionContext);
|
||||
|
||||
macroArguments.push(value);
|
||||
}
|
||||
|
||||
// by nature, the alias exists - the parser only creates a method call node when the name _is_ an alias.
|
||||
const macroTemplate = aliases.get(operand.attributes.name)!;
|
||||
|
||||
const getHandler = (template: TwingTemplate): Promise<TwingTemplateMacroHandler | null> => {
|
||||
const macroHandler = template.macroHandlers.get(methodName);
|
||||
|
||||
if (macroHandler) {
|
||||
return Promise.resolve(macroHandler);
|
||||
} else {
|
||||
return template.getParent(executionContext)
|
||||
.then((parent) => {
|
||||
if (parent) {
|
||||
return getHandler(parent);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return getHandler(macroTemplate)
|
||||
.then((handler) => {
|
||||
if (handler) {
|
||||
return handler(executionContext, ...macroArguments);
|
||||
} else {
|
||||
throw createRuntimeError(`Macro "${methodName}" is not defined in template "${macroTemplate.name}".`, node, template.source);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const executeMethodCallSynchronously: TwingSynchronousNodeExecutor<TwingMethodCallNode> = (node, executionContext) => {
|
||||
const {template, aliases, nodeExecutor: execute} = executionContext;
|
||||
const {methodName, shouldTestExistence} = node.attributes;
|
||||
|
@ -1,16 +1,6 @@
|
||||
import type {TwingNodeExecutor, TwingSynchronousNodeExecutor} from "../../node-executor";
|
||||
import type {TwingSynchronousNodeExecutor} from "../../node-executor";
|
||||
import type {TwingParentFunctionNode} from "../../node/expression/parent-function";
|
||||
import {getSynchronousTraceableMethod, getTraceableMethod} from "../../helpers/traceable-method";
|
||||
|
||||
export const executeParentFunction: TwingNodeExecutor<TwingParentFunctionNode> = (node, executionContext) => {
|
||||
const {template, outputBuffer} = executionContext;
|
||||
const {name} = node.attributes;
|
||||
const displayParentBlock = getTraceableMethod(template.displayParentBlock, node, template.source);
|
||||
|
||||
outputBuffer.start();
|
||||
|
||||
return displayParentBlock(executionContext, name).then(() => outputBuffer.getAndClean());
|
||||
};
|
||||
import {getSynchronousTraceableMethod} from "../../helpers/traceable-method";
|
||||
|
||||
export const executeParentFunctionSynchronously: TwingSynchronousNodeExecutor<TwingParentFunctionNode> = (node, executionContext) => {
|
||||
const {template, outputBuffer} = executionContext;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user