mirror of
https://gitlab.com/nightlycommit/twing.git
synced 2025-01-18 08:46:50 +02:00
Resolve issue #618
This commit is contained in:
parent
7e02a05e4d
commit
2fd56ff4f8
@ -1,7 +1,7 @@
|
|||||||
/* istanbul ignore file */
|
/* istanbul ignore file */
|
||||||
|
|
||||||
// cache
|
// cache
|
||||||
export type {TwingCache} from "./lib/cache";
|
export type {TwingCache, TwingSynchronousCache} from "./lib/cache";
|
||||||
|
|
||||||
// error
|
// error
|
||||||
export type {TwingError} from "./lib/error";
|
export type {TwingError} from "./lib/error";
|
||||||
@ -16,15 +16,15 @@ export {createTemplateLoadingError} from "./lib/error/loader";
|
|||||||
|
|
||||||
// loader
|
// loader
|
||||||
export type {
|
export type {
|
||||||
TwingFilesystemLoader, TwingFilesystemLoaderFilesystem, TwingFilesystemLoaderFilesystemStats
|
TwingFilesystemLoader, TwingFilesystemLoaderFilesystem, TwingFilesystemLoaderFilesystemStats, TwingSynchronousFilesystemLoader, TwingSynchronousFilesystemLoaderFilesystem
|
||||||
} from "./lib/loader/filesystem";
|
} from "./lib/loader/filesystem";
|
||||||
export type {TwingArrayLoader, TwingSynchronousArrayLoader} from "./lib/loader/array";
|
export type {TwingArrayLoader, TwingSynchronousArrayLoader} from "./lib/loader/array";
|
||||||
export type {TwingChainLoader} from "./lib/loader/chain";
|
export type {TwingChainLoader, TwingSynchronousChainLoader} from "./lib/loader/chain";
|
||||||
export type {TwingLoader, TwingSynchronousLoader} from "./lib/loader";
|
export type {TwingLoader, TwingSynchronousLoader} from "./lib/loader";
|
||||||
|
|
||||||
export {createFilesystemLoader, createSynchronousFilesystemLoader} from "./lib/loader/filesystem";
|
export {createFilesystemLoader, createSynchronousFilesystemLoader} from "./lib/loader/filesystem";
|
||||||
export {createArrayLoader, createSynchronousArrayLoader} from "./lib/loader/array";
|
export {createArrayLoader, createSynchronousArrayLoader} from "./lib/loader/array";
|
||||||
export {createChainLoader} from "./lib/loader/chain";
|
export {createChainLoader, createSynchronousChainLoader} from "./lib/loader/chain";
|
||||||
|
|
||||||
// markup
|
// markup
|
||||||
export type {TwingMarkup} from "./lib/markup";
|
export type {TwingMarkup} from "./lib/markup";
|
||||||
@ -263,18 +263,18 @@ export {createWithTagHandler} from "./lib/tag-handler/with";
|
|||||||
|
|
||||||
// core
|
// core
|
||||||
export type {
|
export type {
|
||||||
TwingCallable, TwingCallableArgument, TwingCallableWrapperOptions, TwingCallableWrapper
|
TwingCallable, TwingCallableArgument, TwingCallableWrapperOptions, TwingCallableWrapper, TwingSynchronousCallable, TwingSynchronousCallableWrapper
|
||||||
} from "./lib/callable-wrapper";
|
} from "./lib/callable-wrapper";
|
||||||
export {type TwingContext, createContext} from "./lib/context";
|
export {type TwingContext, createContext} from "./lib/context";
|
||||||
export type {TwingEnvironment, TwingEnvironmentOptions, TwingNumberFormat} from "./lib/environment";
|
export type {TwingEnvironment, TwingEnvironmentOptions, TwingNumberFormat, TwingSynchronousEnvironment, TwingSynchronousEnvironmentOptions} from "./lib/environment";
|
||||||
export type {
|
export type {
|
||||||
TwingEscapingStrategy, TwingEscapingStrategyHandler, TwingEscapingStrategyResolver
|
TwingEscapingStrategy, TwingEscapingStrategyHandler, TwingEscapingStrategyResolver
|
||||||
} from "./lib/escaping-strategy";
|
} from "./lib/escaping-strategy";
|
||||||
export type {TwingExecutionContext} from "./lib/execution-context";
|
export type {TwingExecutionContext, TwingSynchronousExecutionContext} from "./lib/execution-context";
|
||||||
export type {TwingExtension} from "./lib/extension";
|
export type {TwingExtension, TwingSynchronousExtension} from "./lib/extension";
|
||||||
export type {TwingExtensionSet} from "./lib/extension-set";
|
export type {TwingExtensionSet} from "./lib/extension-set";
|
||||||
export type {TwingFilter} from "./lib/filter";
|
export type {TwingFilter, TwingSynchronousFilter} from "./lib/filter";
|
||||||
export type {TwingFunction} from "./lib/function";
|
export type {TwingFunction, TwingSynchronousFunction} from "./lib/function";
|
||||||
export type {TwingLexer} from "./lib/lexer";
|
export type {TwingLexer} from "./lib/lexer";
|
||||||
export type {TwingNodeVisitor} from "./lib/node-visitor";
|
export type {TwingNodeVisitor} from "./lib/node-visitor";
|
||||||
export type {
|
export type {
|
||||||
@ -289,26 +289,25 @@ export type {
|
|||||||
TwingTemplateAliases,
|
TwingTemplateAliases,
|
||||||
TwingTemplateBlockMap,
|
TwingTemplateBlockMap,
|
||||||
TwingTemplateBlockHandler,
|
TwingTemplateBlockHandler,
|
||||||
TwingTemplateMacroHandler
|
TwingTemplateMacroHandler,
|
||||||
|
TwingSynchronousTemplateAliases,
|
||||||
|
TwingSynchronousTemplateBlockHandler,
|
||||||
|
TwingSynchronousTemplateBlockMap,
|
||||||
|
TwingSynchronousTemplateMacroHandler
|
||||||
} from "./lib/template";
|
} from "./lib/template";
|
||||||
export type {TwingTest} from "./lib/test";
|
export type {TwingTest, TwingSynchronousTest} from "./lib/test";
|
||||||
export type {TwingTokenStream} from "./lib/token-stream";
|
export type {TwingTokenStream} from "./lib/token-stream";
|
||||||
|
|
||||||
export interface TwingTemplate {
|
|
||||||
execute: import("./lib/template").TwingTemplate["execute"];
|
|
||||||
render: import("./lib/template").TwingTemplate["render"];
|
|
||||||
}
|
|
||||||
|
|
||||||
export {createEnvironment, createSynchronousEnvironment} from "./lib/environment";
|
export {createEnvironment, createSynchronousEnvironment} from "./lib/environment";
|
||||||
export {createExtensionSet} from "./lib/extension-set";
|
export {createExtensionSet} from "./lib/extension-set";
|
||||||
export {createFilter} from "./lib/filter";
|
export {createFilter, createSynchronousFilter} from "./lib/filter";
|
||||||
export {createFunction} from "./lib/function";
|
export {createFunction, createSynchronousFunction} from "./lib/function";
|
||||||
export {createLexer} from "./lib/lexer";
|
export {createLexer} from "./lib/lexer";
|
||||||
export {createBaseNode, createNode, getChildren, getChildrenCount} from "./lib/node";
|
export {createBaseNode, createNode, getChildren, getChildrenCount} from "./lib/node";
|
||||||
export {createOperator} from "./lib/operator";
|
export {createOperator} from "./lib/operator";
|
||||||
export {createSandboxSecurityPolicy} from "./lib/sandbox/security-policy";
|
export {createSandboxSecurityPolicy} from "./lib/sandbox/security-policy";
|
||||||
export {createSource} from "./lib/source";
|
export {createSource} from "./lib/source";
|
||||||
export {createSourceMapRuntime} from "./lib/source-map-runtime";
|
export {createSourceMapRuntime} from "./lib/source-map-runtime";
|
||||||
export {createTemplate} from "./lib/template";
|
export {type TwingTemplate, createTemplate, type TwingSynchronousTemplate, createSynchronousTemplate} from "./lib/template";
|
||||||
export {type TwingTemplateLoader, createTemplateLoader} from "./lib/template-loader";
|
export {type TwingTemplateLoader, type TwingSynchronousTemplateLoader, createTemplateLoader, createSynchronousTemplateLoader} from "./lib/template-loader";
|
||||||
export {createTest} from "./lib/test";
|
export {createTest, createSynchronousTest} from "./lib/test";
|
||||||
|
@ -69,5 +69,3 @@ export const getEntries = <V>(context: Record<string, V>): IterableIterator<[str
|
|||||||
export const getValues = <V>(context: Record<string, V>): Array<V> => {
|
export const getValues = <V>(context: Record<string, V>): Array<V> => {
|
||||||
return Object.values(context);
|
return Object.values(context);
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TwingContext2 = Map<string, any>;
|
|
||||||
|
@ -27,7 +27,7 @@ import {TwingCache, TwingSynchronousCache} from "./cache";
|
|||||||
import {createCoreExtension, createSynchronousCoreExtension} from "./extension/core";
|
import {createCoreExtension, createSynchronousCoreExtension} from "./extension/core";
|
||||||
import {createAutoEscapeNode, createTemplateLoadingError, type TwingContext} from "../lib";
|
import {createAutoEscapeNode, createTemplateLoadingError, type TwingContext} from "../lib";
|
||||||
import {createSynchronousTemplateLoader, createTemplateLoader} from "./template-loader";
|
import {createSynchronousTemplateLoader, createTemplateLoader} from "./template-loader";
|
||||||
import {createContext, TwingContext2} from "./context";
|
import {createContext} from "./context";
|
||||||
import {iterableToMap} from "./helpers/iterator-to-map";
|
import {iterableToMap} from "./helpers/iterator-to-map";
|
||||||
|
|
||||||
export type TwingNumberFormat = {
|
export type TwingNumberFormat = {
|
||||||
@ -165,7 +165,7 @@ export interface TwingSynchronousEnvironment {
|
|||||||
readonly numberFormat: TwingNumberFormat;
|
readonly numberFormat: TwingNumberFormat;
|
||||||
readonly filters: Map<string, TwingSynchronousFilter>;
|
readonly filters: Map<string, TwingSynchronousFilter>;
|
||||||
readonly functions: Map<string, TwingSynchronousFunction>;
|
readonly functions: Map<string, TwingSynchronousFunction>;
|
||||||
readonly globals: TwingContext2;
|
readonly globals: Map<string, any>;
|
||||||
readonly loader: TwingSynchronousLoader;
|
readonly loader: TwingSynchronousLoader;
|
||||||
readonly sandboxPolicy: TwingSandboxSecurityPolicy;
|
readonly sandboxPolicy: TwingSandboxSecurityPolicy;
|
||||||
readonly tests: Map<string, TwingSynchronousTest>;
|
readonly tests: Map<string, TwingSynchronousTest>;
|
||||||
@ -567,14 +567,14 @@ export const createSynchronousEnvironment = (
|
|||||||
},
|
},
|
||||||
render: (name, data, options) => {
|
render: (name, data, options) => {
|
||||||
const template = environment.loadTemplate(name);
|
const template = environment.loadTemplate(name);
|
||||||
const context: TwingContext2 = new Map(Object.entries(data));
|
const context: Map<string, any> = new Map(Object.entries(data));
|
||||||
|
|
||||||
return template.render(environment, context, options);
|
return template.render(environment, context, options);
|
||||||
},
|
},
|
||||||
renderWithSourceMap: (name, data, options) => {
|
renderWithSourceMap: (name, data, options) => {
|
||||||
const sourceMapRuntime = createSourceMapRuntime();
|
const sourceMapRuntime = createSourceMapRuntime();
|
||||||
|
|
||||||
const context: TwingContext2 = new Map(Object.entries(data));
|
const context: Map<string, any> = new Map(Object.entries(data));
|
||||||
const template = environment.loadTemplate(name);
|
const template = environment.loadTemplate(name);
|
||||||
const output = template.render(environment, context, {
|
const output = template.render(environment, context, {
|
||||||
...options,
|
...options,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import type {TwingTemplate, TwingTemplateAliases, TwingTemplateBlockMap} from "./template";
|
import type {TwingTemplate, TwingTemplateAliases, TwingTemplateBlockMap} from "./template";
|
||||||
import type {TwingContext, TwingContext2} from "./context";
|
import type {TwingContext} from "./context";
|
||||||
import type {TwingOutputBuffer} from "./output-buffer";
|
import type {TwingOutputBuffer} from "./output-buffer";
|
||||||
import type {TwingSourceMapRuntime} from "./source-map-runtime";
|
import type {TwingSourceMapRuntime} from "./source-map-runtime";
|
||||||
import type {TwingEnvironment, TwingSynchronousEnvironment} from "./environment";
|
import type {TwingEnvironment, TwingSynchronousEnvironment} from "./environment";
|
||||||
@ -26,7 +26,7 @@ export type TwingExecutionContext = {
|
|||||||
export type TwingSynchronousExecutionContext = {
|
export type TwingSynchronousExecutionContext = {
|
||||||
aliases: TwingSynchronousTemplateAliases;
|
aliases: TwingSynchronousTemplateAliases;
|
||||||
blocks: TwingSynchronousTemplateBlockMap;
|
blocks: TwingSynchronousTemplateBlockMap;
|
||||||
context: TwingContext2;
|
context: Map<string, any>;
|
||||||
environment: TwingSynchronousEnvironment;
|
environment: TwingSynchronousEnvironment;
|
||||||
nodeExecutor: TwingSynchronousNodeExecutor;
|
nodeExecutor: TwingSynchronousNodeExecutor;
|
||||||
outputBuffer: TwingOutputBuffer;
|
outputBuffer: TwingOutputBuffer;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import {isTraversable} from "../../../helpers/is-traversable";
|
import {isTraversable} from "../../../helpers/is-traversable";
|
||||||
import {isPlainObject} from "../../../helpers/is-plain-object";
|
import {isPlainObject} from "../../../helpers/is-plain-object";
|
||||||
import {createContext, TwingContext2} from "../../../context";
|
import {createContext} from "../../../context";
|
||||||
import {createMarkup, TwingMarkup} from "../../../markup";
|
import {createMarkup, TwingMarkup} from "../../../markup";
|
||||||
import type {TwingSynchronousTemplate, TwingTemplate} from "../../../template";
|
import type {TwingSynchronousTemplate, TwingTemplate} from "../../../template";
|
||||||
import type {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
import type {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||||
@ -103,7 +103,7 @@ export const include: TwingCallable<[
|
|||||||
|
|
||||||
export const includeSynchronously: TwingSynchronousCallable<[
|
export const includeSynchronously: TwingSynchronousCallable<[
|
||||||
templates: string | TwingSynchronousTemplate | null | Array<string | TwingSynchronousTemplate | null>,
|
templates: string | TwingSynchronousTemplate | null | Array<string | TwingSynchronousTemplate | null>,
|
||||||
variables: TwingContext2,
|
variables: Map<string, any>,
|
||||||
withContext: boolean,
|
withContext: boolean,
|
||||||
ignoreMissing: boolean,
|
ignoreMissing: boolean,
|
||||||
sandboxed: boolean
|
sandboxed: boolean
|
||||||
|
@ -8,9 +8,9 @@
|
|||||||
*
|
*
|
||||||
* @returns {any}
|
* @returns {any}
|
||||||
*/
|
*/
|
||||||
import {TwingContext, TwingContext2} from "../context";
|
import {TwingContext} from "../context";
|
||||||
|
|
||||||
export function getConstant(context: TwingContext<any, any> | TwingContext2, name: string, object: any | null): any {
|
export function getConstant(context: TwingContext<any, any> | Map<string, any>, name: string, object: any | null): any {
|
||||||
if (object) {
|
if (object) {
|
||||||
return object[name];
|
return object[name];
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type {TwingContext, TwingContext2} from "../context";
|
import type {TwingContext} from "../context";
|
||||||
|
|
||||||
export const getContextValue = (
|
export const getContextValue = (
|
||||||
charset: string,
|
charset: string,
|
||||||
@ -51,8 +51,8 @@ export const getContextValueSynchronously = (
|
|||||||
charset: string,
|
charset: string,
|
||||||
templateName: string,
|
templateName: string,
|
||||||
isStrictVariables: boolean,
|
isStrictVariables: boolean,
|
||||||
context: TwingContext2,
|
context: Map<string, any>,
|
||||||
globals: TwingContext2,
|
globals: Map<string, any>,
|
||||||
name: string,
|
name: string,
|
||||||
isAlwaysDefined: boolean,
|
isAlwaysDefined: boolean,
|
||||||
shouldIgnoreStrictCheck: boolean,
|
shouldIgnoreStrictCheck: boolean,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type {TwingLoader} from "../loader";
|
import type {TwingLoader, TwingSynchronousLoader} from "../loader";
|
||||||
import type {TwingSource} from "../source";
|
import type {TwingSource} from "../source";
|
||||||
|
|
||||||
export interface TwingChainLoader extends TwingLoader {
|
export interface TwingChainLoader extends TwingLoader {
|
||||||
@ -7,6 +7,12 @@ export interface TwingChainLoader extends TwingLoader {
|
|||||||
addLoader(loader: TwingLoader): void;
|
addLoader(loader: TwingLoader): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TwingSynchronousChainLoader extends TwingSynchronousLoader {
|
||||||
|
readonly loaders: Array<TwingSynchronousLoader>;
|
||||||
|
|
||||||
|
addLoader(loader: TwingSynchronousLoader): void;
|
||||||
|
}
|
||||||
|
|
||||||
export const createChainLoader = (
|
export const createChainLoader = (
|
||||||
loaders: Array<TwingLoader>
|
loaders: Array<TwingLoader>
|
||||||
): TwingChainLoader => {
|
): TwingChainLoader => {
|
||||||
@ -139,3 +145,125 @@ export const createChainLoader = (
|
|||||||
|
|
||||||
return loader;
|
return loader;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const createSynchronousChainLoader = (
|
||||||
|
loaders: Array<TwingSynchronousLoader>
|
||||||
|
): TwingSynchronousChainLoader => {
|
||||||
|
let existsCache: Map<string, boolean> = new Map();
|
||||||
|
|
||||||
|
const addLoader: TwingSynchronousChainLoader["addLoader"] = (loader) => {
|
||||||
|
loaders.push(loader);
|
||||||
|
existsCache = new Map();
|
||||||
|
};
|
||||||
|
|
||||||
|
const loader: TwingSynchronousChainLoader = {
|
||||||
|
get loaders() {
|
||||||
|
return loaders
|
||||||
|
},
|
||||||
|
addLoader,
|
||||||
|
exists: (name, from) => {
|
||||||
|
const cachedResult = existsCache.get(name);
|
||||||
|
|
||||||
|
if (cachedResult) {
|
||||||
|
return cachedResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
const existsAtIndex = (index: number): boolean => {
|
||||||
|
if (index < loaders.length) {
|
||||||
|
const loader = loaders[index];
|
||||||
|
|
||||||
|
const exists = loader.exists(name, from);
|
||||||
|
|
||||||
|
existsCache.set(name, exists);
|
||||||
|
|
||||||
|
if (!exists) {
|
||||||
|
return existsAtIndex(index + 1);
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const exists = existsAtIndex(0);
|
||||||
|
|
||||||
|
existsCache.set(name, exists);
|
||||||
|
|
||||||
|
return exists;
|
||||||
|
},
|
||||||
|
resolve: (name, from) => {
|
||||||
|
const resolveAtIndex = (index: number): string | null => {
|
||||||
|
if (index < loaders.length) {
|
||||||
|
const loader = loaders[index];
|
||||||
|
|
||||||
|
const exists = loader.exists(name, from);
|
||||||
|
|
||||||
|
const key = exists ? loader.resolve(name, from) : resolveAtIndex(index + 1);
|
||||||
|
|
||||||
|
if (key === null) {
|
||||||
|
return resolveAtIndex(index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return key;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const key = resolveAtIndex(0);
|
||||||
|
|
||||||
|
if (key) {
|
||||||
|
return key;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getSource: (name, from) => {
|
||||||
|
const getSourceContextAtIndex = (index: number): TwingSource | null => {
|
||||||
|
if (index < loaders.length) {
|
||||||
|
let loader = loaders[index];
|
||||||
|
|
||||||
|
const source = loader.getSource(name, from);
|
||||||
|
|
||||||
|
if (source === null) {
|
||||||
|
return getSourceContextAtIndex(index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return source;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const source = getSourceContextAtIndex(0);
|
||||||
|
|
||||||
|
if (source) {
|
||||||
|
return source;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isFresh: (name, time, from) => {
|
||||||
|
const isFreshAtIndex = (index: number): boolean | null => {
|
||||||
|
if (index < loaders.length) {
|
||||||
|
const loader = loaders[index];
|
||||||
|
|
||||||
|
const isFresh = loader.isFresh(name, time, from);
|
||||||
|
|
||||||
|
if (isFresh === null) {
|
||||||
|
return isFreshAtIndex(index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return isFresh;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return isFreshAtIndex(0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return loader;
|
||||||
|
};
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import {TwingNodeExecutor, TwingSynchronousNodeExecutor} from "../node-executor";
|
import {TwingNodeExecutor, TwingSynchronousNodeExecutor} from "../node-executor";
|
||||||
import {TwingForNode} from "../node/for";
|
import {TwingForNode} from "../node/for";
|
||||||
import {TwingContext, TwingContext2} from "../context";
|
import {TwingContext} from "../context";
|
||||||
import {ensureTraversable} from "../helpers/ensure-traversable";
|
import {ensureTraversable} from "../helpers/ensure-traversable";
|
||||||
import {count} from "../helpers/count";
|
import {count} from "../helpers/count";
|
||||||
import {iterate, iterateSynchronously} from "../helpers/iterate";
|
import {iterate, iterateSynchronously} from "../helpers/iterate";
|
||||||
@ -140,7 +140,7 @@ export const executeForNodeSynchronously: TwingSynchronousNodeExecutor<TwingForN
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const parent: TwingContext2 = context.get('_parent');
|
const parent: Map<string, any> = context.get('_parent');
|
||||||
|
|
||||||
context.delete('_seq');
|
context.delete('_seq');
|
||||||
context.delete('_iterated');
|
context.delete('_iterated');
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import {TwingNodeExecutor, TwingSynchronousNodeExecutor} from "../node-executor";
|
import {TwingNodeExecutor, TwingSynchronousNodeExecutor} from "../node-executor";
|
||||||
import {TwingWithNode} from "../node/with";
|
import {TwingWithNode} from "../node/with";
|
||||||
import {createContext, TwingContext, TwingContext2} from "../context";
|
import {createContext, TwingContext} from "../context";
|
||||||
import {createRuntimeError} from "../error/runtime";
|
import {createRuntimeError} from "../error/runtime";
|
||||||
import {mergeIterables} from "../helpers/merge-iterables";
|
import {mergeIterables} from "../helpers/merge-iterables";
|
||||||
import {iteratorToMap} from "../helpers/iterator-to-map";
|
import {iteratorToMap} from "../helpers/iterator-to-map";
|
||||||
@ -47,7 +47,7 @@ export const executeWithNodeSynchronously: TwingSynchronousNodeExecutor<TwingWit
|
|||||||
const {variables: variablesNode, body} = node.children;
|
const {variables: variablesNode, body} = node.children;
|
||||||
const {only} = node.attributes;
|
const {only} = node.attributes;
|
||||||
|
|
||||||
let scopedContext: TwingContext2;
|
let scopedContext: Map<string, any>;
|
||||||
|
|
||||||
if (variablesNode) {
|
if (variablesNode) {
|
||||||
let variables = execute(variablesNode, executionContext);
|
let variables = execute(variablesNode, executionContext);
|
||||||
|
@ -5,7 +5,7 @@ export interface TwingSandboxSecurityPolicy {
|
|||||||
* @param {any | TwingMarkup} candidate
|
* @param {any | TwingMarkup} candidate
|
||||||
* @param {string} method
|
* @param {string} method
|
||||||
*
|
*
|
||||||
* @throws {@link TwingSandboxSecurityNotAllowedMethodError} When the method is not allowed on the passed object
|
* @throws When the method is not allowed on the passed object
|
||||||
*/
|
*/
|
||||||
checkMethodAllowed(candidate: any | TwingMarkup, method: string): void;
|
checkMethodAllowed(candidate: any | TwingMarkup, method: string): void;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {createContext, TwingContext, TwingContext2} from "./context";
|
import {createContext, TwingContext} from "./context";
|
||||||
import {TwingEnvironment, TwingSynchronousEnvironment} from "./environment";
|
import {TwingEnvironment, TwingSynchronousEnvironment} from "./environment";
|
||||||
import {createOutputBuffer, TwingOutputBuffer} from "./output-buffer";
|
import {createOutputBuffer, TwingOutputBuffer} from "./output-buffer";
|
||||||
import {TwingSourceMapRuntime} from "./source-map-runtime";
|
import {TwingSourceMapRuntime} from "./source-map-runtime";
|
||||||
@ -184,7 +184,7 @@ export interface TwingSynchronousTemplate {
|
|||||||
*/
|
*/
|
||||||
execute(
|
execute(
|
||||||
environment: TwingSynchronousEnvironment,
|
environment: TwingSynchronousEnvironment,
|
||||||
context: TwingContext2,
|
context: Map<string, any>,
|
||||||
blocks: TwingSynchronousTemplateBlockMap,
|
blocks: TwingSynchronousTemplateBlockMap,
|
||||||
outputBuffer: TwingOutputBuffer,
|
outputBuffer: TwingOutputBuffer,
|
||||||
options?: {
|
options?: {
|
||||||
@ -225,7 +225,7 @@ export interface TwingSynchronousTemplate {
|
|||||||
|
|
||||||
render(
|
render(
|
||||||
environment: TwingSynchronousEnvironment,
|
environment: TwingSynchronousEnvironment,
|
||||||
context: TwingContext2,
|
context: Map<string, any>,
|
||||||
options?: {
|
options?: {
|
||||||
nodeExecutor?: TwingSynchronousNodeExecutor;
|
nodeExecutor?: TwingSynchronousNodeExecutor;
|
||||||
outputBuffer?: TwingOutputBuffer;
|
outputBuffer?: TwingOutputBuffer;
|
||||||
@ -749,7 +749,7 @@ export const createSynchronousTemplate = (
|
|||||||
|
|
||||||
const aliases = {...template.aliases};
|
const aliases = {...template.aliases};
|
||||||
|
|
||||||
const localVariables: TwingContext2 = new Map();
|
const localVariables: Map<string, any> = new Map();
|
||||||
|
|
||||||
for (const {key: keyNode, value: defaultValueNode} of keyValuePairs) {
|
for (const {key: keyNode, value: defaultValueNode} of keyValuePairs) {
|
||||||
const key = keyNode.attributes.value as string;
|
const key = keyNode.attributes.value as string;
|
||||||
|
@ -9,7 +9,7 @@ tape('library index', ({same, end}) => {
|
|||||||
'createParsingError',
|
'createParsingError',
|
||||||
'createFilesystemLoader', 'createSynchronousFilesystemLoader',
|
'createFilesystemLoader', 'createSynchronousFilesystemLoader',
|
||||||
'createArrayLoader', 'createSynchronousArrayLoader',
|
'createArrayLoader', 'createSynchronousArrayLoader',
|
||||||
'createChainLoader',
|
'createChainLoader', 'createSynchronousChainLoader',
|
||||||
'createMarkup', 'isAMarkup',
|
'createMarkup', 'isAMarkup',
|
||||||
'createApplyNode',
|
'createApplyNode',
|
||||||
'createAutoEscapeNode',
|
'createAutoEscapeNode',
|
||||||
@ -117,17 +117,17 @@ tape('library index', ({same, end}) => {
|
|||||||
'getChildrenCount',
|
'getChildrenCount',
|
||||||
'createEnvironment', 'createSynchronousEnvironment',
|
'createEnvironment', 'createSynchronousEnvironment',
|
||||||
'createExtensionSet',
|
'createExtensionSet',
|
||||||
'createFilter',
|
'createFilter', 'createSynchronousFilter',
|
||||||
'createFunction',
|
'createFunction', 'createSynchronousFunction',
|
||||||
'createLexer',
|
'createLexer',
|
||||||
'createOperator',
|
'createOperator',
|
||||||
'createSandboxSecurityPolicy',
|
'createSandboxSecurityPolicy',
|
||||||
'createSource',
|
'createSource',
|
||||||
'createSourceMapRuntime',
|
'createSourceMapRuntime',
|
||||||
'createTemplate',
|
'createTemplate', 'createSynchronousTemplate',
|
||||||
'createTest',
|
'createTest', 'createSynchronousTest',
|
||||||
'executeNode', 'executeNodeSynchronously',
|
'executeNode', 'executeNodeSynchronously',
|
||||||
'createTemplateLoader',
|
'createTemplateLoader', 'createSynchronousTemplateLoader',
|
||||||
'createContext',
|
'createContext',
|
||||||
'createOutputBuffer'
|
'createOutputBuffer'
|
||||||
];
|
];
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import * as tape from 'tape';
|
import * as tape from 'tape';
|
||||||
import {createChainLoader} from "../../../../../../main/lib/loader/chain";
|
import {createChainLoader, createSynchronousChainLoader} from "../../../../../../main/lib/loader/chain";
|
||||||
import {createArrayLoader} from "../../../../../../main/lib/loader/array";
|
import {createArrayLoader, createSynchronousArrayLoader} from "../../../../../../main/lib/loader/array";
|
||||||
import {spy, stub} from "sinon";
|
import {spy, stub} from "sinon";
|
||||||
|
|
||||||
tape('createChainLoader', ({test}) => {
|
tape('createChainLoader', ({test}) => {
|
||||||
@ -267,3 +267,266 @@ tape('createChainLoader', ({test}) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
tape('createSynchronousChainLoader', ({test}) => {
|
||||||
|
test('getSourceContext', ({test}) => {
|
||||||
|
let loader = createSynchronousChainLoader([
|
||||||
|
createSynchronousArrayLoader({'foo': 'bar'}),
|
||||||
|
createSynchronousArrayLoader({'errors/index.html': 'baz'})
|
||||||
|
]);
|
||||||
|
|
||||||
|
test('return the source context of the first loader that returns a source context', ({test}) => {
|
||||||
|
test('foo', ({same, end}) => {
|
||||||
|
const source = loader.getSource('foo', null);
|
||||||
|
|
||||||
|
same(source?.name, 'foo');
|
||||||
|
same(source?.code, 'bar');
|
||||||
|
|
||||||
|
end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('errors/index.html', ({same, end}) => {
|
||||||
|
const source = loader.getSource('errors/index.html', null);
|
||||||
|
|
||||||
|
same(source?.name, 'errors/index.html');
|
||||||
|
same(source?.code, 'baz');
|
||||||
|
|
||||||
|
end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns null when the template does not exist', ({same, end}) => {
|
||||||
|
const loader = createSynchronousChainLoader([]);
|
||||||
|
|
||||||
|
same(loader.getSource('foo', null), null);
|
||||||
|
|
||||||
|
end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('resolve', ({test}) => {
|
||||||
|
test('returns the template FQN when the template exists', ({same, end}) => {
|
||||||
|
let loader = createSynchronousChainLoader([
|
||||||
|
createSynchronousArrayLoader({'foo': 'bar'}),
|
||||||
|
createSynchronousArrayLoader({'foo': 'foobar', 'bar': 'foo'}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
same(loader.resolve('foo', null), 'foo');
|
||||||
|
same(loader.resolve('bar', null), 'bar');
|
||||||
|
|
||||||
|
let resolveStub = stub(loader, 'resolve').returns(null);
|
||||||
|
|
||||||
|
loader = createSynchronousChainLoader([
|
||||||
|
loader
|
||||||
|
]);
|
||||||
|
|
||||||
|
same(loader.resolve('foo', null), null);
|
||||||
|
|
||||||
|
resolveStub.restore();
|
||||||
|
|
||||||
|
let loader2 = createSynchronousArrayLoader({'foo': 'bar'});
|
||||||
|
|
||||||
|
resolveStub = stub(loader2, 'resolve').returns(null);
|
||||||
|
|
||||||
|
loader = createSynchronousChainLoader([
|
||||||
|
loader2
|
||||||
|
]);
|
||||||
|
|
||||||
|
same(loader.resolve('foo', null), null);
|
||||||
|
|
||||||
|
resolveStub.restore();
|
||||||
|
|
||||||
|
end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns null when the template does not exist', ({same, end}) => {
|
||||||
|
const loader = createSynchronousChainLoader([]);
|
||||||
|
|
||||||
|
same(loader.resolve('foo', null), null);
|
||||||
|
|
||||||
|
end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('addLoader', ({same, end}) => {
|
||||||
|
const loader = createSynchronousChainLoader([]);
|
||||||
|
|
||||||
|
loader.addLoader(createSynchronousArrayLoader({'foo': 'bar'}));
|
||||||
|
|
||||||
|
const source = loader.getSource('foo', null);
|
||||||
|
|
||||||
|
same(source?.code, 'bar');
|
||||||
|
|
||||||
|
end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getLoaders', (test) => {
|
||||||
|
let loaders = [
|
||||||
|
createArrayLoader({'foo': 'bar'})
|
||||||
|
];
|
||||||
|
|
||||||
|
let loader = createChainLoader(loaders);
|
||||||
|
|
||||||
|
test.same(loader.loaders, loaders);
|
||||||
|
|
||||||
|
test.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('exists', ({test}) => {
|
||||||
|
let loader1 = createSynchronousArrayLoader({});
|
||||||
|
let loader1ExistsStub = stub(loader1, 'exists').returns(false);
|
||||||
|
let loader1GetSourceSpy = spy(loader1, 'getSource');
|
||||||
|
|
||||||
|
let loader2 = createSynchronousArrayLoader({});
|
||||||
|
let loader2ExistsStub = stub(loader2, 'exists').returns(true);
|
||||||
|
let loader2GetSourceSpy = spy(loader2, 'getSource');
|
||||||
|
|
||||||
|
let loader3 = createSynchronousArrayLoader({});
|
||||||
|
let loader3ExistsStub = stub(loader3, 'exists').returns(true);
|
||||||
|
let loader3GetSourceSpy = spy(loader3, 'getSource');
|
||||||
|
|
||||||
|
test('resolves to true as soon as a loader resolves to true', ({same, end}) => {
|
||||||
|
let loader = createSynchronousChainLoader([
|
||||||
|
loader1,
|
||||||
|
loader2,
|
||||||
|
loader3
|
||||||
|
]);
|
||||||
|
|
||||||
|
same(loader.exists('foo', null), true);
|
||||||
|
same(loader1ExistsStub.callCount, 1, 'loader 1 exists is called once');
|
||||||
|
same(loader2ExistsStub.callCount, 1, 'loader 2 exists is called once');
|
||||||
|
same(loader3ExistsStub.callCount, 0, 'loader 3 exists is not called');
|
||||||
|
same(loader1GetSourceSpy.callCount, 0, 'loader 1 getSourceContext is not called');
|
||||||
|
same(loader2GetSourceSpy.callCount, 0, 'loader 2 getSourceContext is not called');
|
||||||
|
same(loader3GetSourceSpy.callCount, 0, 'loader 3 getSourceContext is not called');
|
||||||
|
|
||||||
|
loader1ExistsStub.restore();
|
||||||
|
loader2ExistsStub.restore();
|
||||||
|
loader3ExistsStub.restore();
|
||||||
|
|
||||||
|
end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('resolves to false is all loaders resolve to false', ({same, end}) => {
|
||||||
|
let loader = createSynchronousChainLoader([
|
||||||
|
loader1,
|
||||||
|
loader2
|
||||||
|
]);
|
||||||
|
|
||||||
|
loader1ExistsStub = stub(loader1, 'exists').returns(false);
|
||||||
|
loader2ExistsStub = stub(loader2, 'exists').returns(false);
|
||||||
|
|
||||||
|
same(loader.exists('foo', null), false);
|
||||||
|
|
||||||
|
loader1ExistsStub.restore();
|
||||||
|
loader2ExistsStub.restore();
|
||||||
|
|
||||||
|
end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('hits cache on subsequent calls', async ({same, end}) => {
|
||||||
|
let loader = createSynchronousChainLoader([
|
||||||
|
createSynchronousArrayLoader({
|
||||||
|
foo: 'foo'
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
|
||||||
|
const existsSpy = spy(loader.loaders[0], 'exists');
|
||||||
|
|
||||||
|
await loader.exists('foo', null);
|
||||||
|
await loader.exists('foo', null);
|
||||||
|
|
||||||
|
same(existsSpy.callCount, 1);
|
||||||
|
|
||||||
|
existsSpy.restore();
|
||||||
|
|
||||||
|
end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('isFresh', ({same, end}) => {
|
||||||
|
let loader = createSynchronousChainLoader([
|
||||||
|
createSynchronousArrayLoader({'foo': 'bar'}),
|
||||||
|
createSynchronousArrayLoader({'foo': 'foobar', 'bar': 'foo'}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
same(loader.isFresh('foo', 0, null), true);
|
||||||
|
same(loader.isFresh('bar', 0, null), true);
|
||||||
|
|
||||||
|
let isFreshStub = stub(loader, 'isFresh').returns(null);
|
||||||
|
|
||||||
|
loader = createSynchronousChainLoader([
|
||||||
|
loader
|
||||||
|
]);
|
||||||
|
|
||||||
|
same(loader.isFresh('foo', 0, null), null);
|
||||||
|
|
||||||
|
isFreshStub.restore();
|
||||||
|
|
||||||
|
const loader2 = createSynchronousArrayLoader({'foo': 'bar'});
|
||||||
|
|
||||||
|
isFreshStub = stub(loader2, 'isFresh').returns(null);
|
||||||
|
|
||||||
|
loader = createSynchronousChainLoader([
|
||||||
|
loader2
|
||||||
|
]);
|
||||||
|
|
||||||
|
same(loader.isFresh('foo', 0, null), null);
|
||||||
|
|
||||||
|
isFreshStub.restore();
|
||||||
|
|
||||||
|
end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('resolve', ({test}) => {
|
||||||
|
test('returns whatever the first loader that handles the passed name returns', ({same, end}) => {
|
||||||
|
const loader = createSynchronousChainLoader([
|
||||||
|
createSynchronousArrayLoader({'foo': 'bar'}),
|
||||||
|
createSynchronousArrayLoader({'bar': 'foo'}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
same(loader.resolve('bar', null), 'bar');
|
||||||
|
|
||||||
|
end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('when some loaders return null', ({same, end}) => {
|
||||||
|
const loader1 = createSynchronousArrayLoader({});
|
||||||
|
|
||||||
|
stub(loader1, 'exists').returns(true);
|
||||||
|
stub(loader1, 'resolve').returns(null);
|
||||||
|
|
||||||
|
const loader2 = createSynchronousArrayLoader({'bar': 'foo'});
|
||||||
|
|
||||||
|
const loader = createSynchronousChainLoader([
|
||||||
|
loader1,
|
||||||
|
loader2
|
||||||
|
]);
|
||||||
|
|
||||||
|
same(loader.resolve('bar', null), 'bar');
|
||||||
|
|
||||||
|
end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('when all loaders return null', ({same, end}) => {
|
||||||
|
const loader1 = createSynchronousArrayLoader({});
|
||||||
|
|
||||||
|
stub(loader1, 'exists').returns(true);
|
||||||
|
stub(loader1, 'resolve').returns(null);
|
||||||
|
|
||||||
|
const loader2 = createSynchronousArrayLoader({});
|
||||||
|
|
||||||
|
stub(loader2, 'exists').returns(true);
|
||||||
|
stub(loader2, 'resolve').returns(null);
|
||||||
|
|
||||||
|
const loader = createSynchronousChainLoader([
|
||||||
|
loader1,
|
||||||
|
loader2
|
||||||
|
]);
|
||||||
|
|
||||||
|
same(loader.resolve('foo', null), null);
|
||||||
|
|
||||||
|
end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user