mirror of
https://gitlab.com/nightlycommit/twing.git
synced 2025-01-18 08:46:50 +02:00
Merge branch 'issue-618' into 'main'
Resolve issue #618 Closes #618 See merge request nightlycommit/twing!612
This commit is contained in:
commit
00a01c7127
@ -1,7 +1,7 @@
|
||||
/* istanbul ignore file */
|
||||
|
||||
// cache
|
||||
export type {TwingCache} from "./lib/cache";
|
||||
export type {TwingCache, TwingSynchronousCache} from "./lib/cache";
|
||||
|
||||
// error
|
||||
export type {TwingError} from "./lib/error";
|
||||
@ -16,15 +16,15 @@ export {createTemplateLoadingError} from "./lib/error/loader";
|
||||
|
||||
// loader
|
||||
export type {
|
||||
TwingFilesystemLoader, TwingFilesystemLoaderFilesystem, TwingFilesystemLoaderFilesystemStats
|
||||
TwingFilesystemLoader, TwingFilesystemLoaderFilesystem, TwingFilesystemLoaderFilesystemStats, TwingSynchronousFilesystemLoader, TwingSynchronousFilesystemLoaderFilesystem
|
||||
} from "./lib/loader/filesystem";
|
||||
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 {createFilesystemLoader, createSynchronousFilesystemLoader} from "./lib/loader/filesystem";
|
||||
export {createArrayLoader, createSynchronousArrayLoader} from "./lib/loader/array";
|
||||
export {createChainLoader} from "./lib/loader/chain";
|
||||
export {createChainLoader, createSynchronousChainLoader} from "./lib/loader/chain";
|
||||
|
||||
// markup
|
||||
export type {TwingMarkup} from "./lib/markup";
|
||||
@ -263,18 +263,18 @@ export {createWithTagHandler} from "./lib/tag-handler/with";
|
||||
|
||||
// core
|
||||
export type {
|
||||
TwingCallable, TwingCallableArgument, TwingCallableWrapperOptions, TwingCallableWrapper
|
||||
TwingCallable, TwingCallableArgument, TwingCallableWrapperOptions, TwingCallableWrapper, TwingSynchronousCallable, TwingSynchronousCallableWrapper
|
||||
} from "./lib/callable-wrapper";
|
||||
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 {
|
||||
TwingEscapingStrategy, TwingEscapingStrategyHandler, TwingEscapingStrategyResolver
|
||||
} from "./lib/escaping-strategy";
|
||||
export type {TwingExecutionContext} from "./lib/execution-context";
|
||||
export type {TwingExtension} from "./lib/extension";
|
||||
export type {TwingExecutionContext, TwingSynchronousExecutionContext} from "./lib/execution-context";
|
||||
export type {TwingExtension, TwingSynchronousExtension} from "./lib/extension";
|
||||
export type {TwingExtensionSet} from "./lib/extension-set";
|
||||
export type {TwingFilter} from "./lib/filter";
|
||||
export type {TwingFunction} from "./lib/function";
|
||||
export type {TwingFilter, TwingSynchronousFilter} from "./lib/filter";
|
||||
export type {TwingFunction, TwingSynchronousFunction} from "./lib/function";
|
||||
export type {TwingLexer} from "./lib/lexer";
|
||||
export type {TwingNodeVisitor} from "./lib/node-visitor";
|
||||
export type {
|
||||
@ -289,26 +289,25 @@ export type {
|
||||
TwingTemplateAliases,
|
||||
TwingTemplateBlockMap,
|
||||
TwingTemplateBlockHandler,
|
||||
TwingTemplateMacroHandler
|
||||
TwingTemplateMacroHandler,
|
||||
TwingSynchronousTemplateAliases,
|
||||
TwingSynchronousTemplateBlockHandler,
|
||||
TwingSynchronousTemplateBlockMap,
|
||||
TwingSynchronousTemplateMacroHandler
|
||||
} from "./lib/template";
|
||||
export type {TwingTest} from "./lib/test";
|
||||
export type {TwingTest, TwingSynchronousTest} from "./lib/test";
|
||||
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 {createExtensionSet} from "./lib/extension-set";
|
||||
export {createFilter} from "./lib/filter";
|
||||
export {createFunction} from "./lib/function";
|
||||
export {createFilter, createSynchronousFilter} from "./lib/filter";
|
||||
export {createFunction, createSynchronousFunction} from "./lib/function";
|
||||
export {createLexer} from "./lib/lexer";
|
||||
export {createBaseNode, createNode, getChildren, getChildrenCount} from "./lib/node";
|
||||
export {createOperator} from "./lib/operator";
|
||||
export {createSandboxSecurityPolicy} from "./lib/sandbox/security-policy";
|
||||
export {createSource} from "./lib/source";
|
||||
export {createSourceMapRuntime} from "./lib/source-map-runtime";
|
||||
export {createTemplate} from "./lib/template";
|
||||
export {type TwingTemplateLoader, createTemplateLoader} from "./lib/template-loader";
|
||||
export {createTest} from "./lib/test";
|
||||
export {type TwingTemplate, createTemplate, type TwingSynchronousTemplate, createSynchronousTemplate} from "./lib/template";
|
||||
export {type TwingTemplateLoader, type TwingSynchronousTemplateLoader, createTemplateLoader, createSynchronousTemplateLoader} from "./lib/template-loader";
|
||||
export {createTest, createSynchronousTest} from "./lib/test";
|
||||
|
@ -69,5 +69,3 @@ export const getEntries = <V>(context: Record<string, V>): IterableIterator<[str
|
||||
export const getValues = <V>(context: Record<string, V>): Array<V> => {
|
||||
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 {createAutoEscapeNode, createTemplateLoadingError, type TwingContext} from "../lib";
|
||||
import {createSynchronousTemplateLoader, createTemplateLoader} from "./template-loader";
|
||||
import {createContext, TwingContext2} from "./context";
|
||||
import {createContext} from "./context";
|
||||
import {iterableToMap} from "./helpers/iterator-to-map";
|
||||
|
||||
export type TwingNumberFormat = {
|
||||
@ -165,7 +165,7 @@ export interface TwingSynchronousEnvironment {
|
||||
readonly numberFormat: TwingNumberFormat;
|
||||
readonly filters: Map<string, TwingSynchronousFilter>;
|
||||
readonly functions: Map<string, TwingSynchronousFunction>;
|
||||
readonly globals: TwingContext2;
|
||||
readonly globals: Map<string, any>;
|
||||
readonly loader: TwingSynchronousLoader;
|
||||
readonly sandboxPolicy: TwingSandboxSecurityPolicy;
|
||||
readonly tests: Map<string, TwingSynchronousTest>;
|
||||
@ -567,14 +567,14 @@ export const createSynchronousEnvironment = (
|
||||
},
|
||||
render: (name, data, options) => {
|
||||
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);
|
||||
},
|
||||
renderWithSourceMap: (name, data, options) => {
|
||||
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 output = template.render(environment, context, {
|
||||
...options,
|
||||
|
@ -1,5 +1,5 @@
|
||||
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 {TwingSourceMapRuntime} from "./source-map-runtime";
|
||||
import type {TwingEnvironment, TwingSynchronousEnvironment} from "./environment";
|
||||
@ -26,7 +26,7 @@ export type TwingExecutionContext = {
|
||||
export type TwingSynchronousExecutionContext = {
|
||||
aliases: TwingSynchronousTemplateAliases;
|
||||
blocks: TwingSynchronousTemplateBlockMap;
|
||||
context: TwingContext2;
|
||||
context: Map<string, any>;
|
||||
environment: TwingSynchronousEnvironment;
|
||||
nodeExecutor: TwingSynchronousNodeExecutor;
|
||||
outputBuffer: TwingOutputBuffer;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {isTraversable} from "../../../helpers/is-traversable";
|
||||
import {isPlainObject} from "../../../helpers/is-plain-object";
|
||||
import {createContext, TwingContext2} from "../../../context";
|
||||
import {createContext} from "../../../context";
|
||||
import {createMarkup, TwingMarkup} from "../../../markup";
|
||||
import type {TwingSynchronousTemplate, TwingTemplate} from "../../../template";
|
||||
import type {TwingCallable, TwingSynchronousCallable} from "../../../callable-wrapper";
|
||||
@ -103,7 +103,7 @@ export const include: TwingCallable<[
|
||||
|
||||
export const includeSynchronously: TwingSynchronousCallable<[
|
||||
templates: string | TwingSynchronousTemplate | null | Array<string | TwingSynchronousTemplate | null>,
|
||||
variables: TwingContext2,
|
||||
variables: Map<string, any>,
|
||||
withContext: boolean,
|
||||
ignoreMissing: boolean,
|
||||
sandboxed: boolean
|
||||
|
@ -8,9 +8,9 @@
|
||||
*
|
||||
* @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) {
|
||||
return object[name];
|
||||
} else {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type {TwingContext, TwingContext2} from "../context";
|
||||
import type {TwingContext} from "../context";
|
||||
|
||||
export const getContextValue = (
|
||||
charset: string,
|
||||
@ -51,8 +51,8 @@ export const getContextValueSynchronously = (
|
||||
charset: string,
|
||||
templateName: string,
|
||||
isStrictVariables: boolean,
|
||||
context: TwingContext2,
|
||||
globals: TwingContext2,
|
||||
context: Map<string, any>,
|
||||
globals: Map<string, any>,
|
||||
name: string,
|
||||
isAlwaysDefined: boolean,
|
||||
shouldIgnoreStrictCheck: boolean,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type {TwingLoader} from "../loader";
|
||||
import type {TwingLoader, TwingSynchronousLoader} from "../loader";
|
||||
import type {TwingSource} from "../source";
|
||||
|
||||
export interface TwingChainLoader extends TwingLoader {
|
||||
@ -7,6 +7,12 @@ export interface TwingChainLoader extends TwingLoader {
|
||||
addLoader(loader: TwingLoader): void;
|
||||
}
|
||||
|
||||
export interface TwingSynchronousChainLoader extends TwingSynchronousLoader {
|
||||
readonly loaders: Array<TwingSynchronousLoader>;
|
||||
|
||||
addLoader(loader: TwingSynchronousLoader): void;
|
||||
}
|
||||
|
||||
export const createChainLoader = (
|
||||
loaders: Array<TwingLoader>
|
||||
): TwingChainLoader => {
|
||||
@ -139,3 +145,125 @@ export const createChainLoader = (
|
||||
|
||||
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 {TwingForNode} from "../node/for";
|
||||
import {TwingContext, TwingContext2} from "../context";
|
||||
import {TwingContext} from "../context";
|
||||
import {ensureTraversable} from "../helpers/ensure-traversable";
|
||||
import {count} from "../helpers/count";
|
||||
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('_iterated');
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {TwingNodeExecutor, TwingSynchronousNodeExecutor} from "../node-executor";
|
||||
import {TwingWithNode} from "../node/with";
|
||||
import {createContext, TwingContext, TwingContext2} from "../context";
|
||||
import {createContext, TwingContext} from "../context";
|
||||
import {createRuntimeError} from "../error/runtime";
|
||||
import {mergeIterables} from "../helpers/merge-iterables";
|
||||
import {iteratorToMap} from "../helpers/iterator-to-map";
|
||||
@ -47,7 +47,7 @@ export const executeWithNodeSynchronously: TwingSynchronousNodeExecutor<TwingWit
|
||||
const {variables: variablesNode, body} = node.children;
|
||||
const {only} = node.attributes;
|
||||
|
||||
let scopedContext: TwingContext2;
|
||||
let scopedContext: Map<string, any>;
|
||||
|
||||
if (variablesNode) {
|
||||
let variables = execute(variablesNode, executionContext);
|
||||
|
@ -5,7 +5,7 @@ export interface TwingSandboxSecurityPolicy {
|
||||
* @param {any | TwingMarkup} candidate
|
||||
* @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;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {createContext, TwingContext, TwingContext2} from "./context";
|
||||
import {createContext, TwingContext} from "./context";
|
||||
import {TwingEnvironment, TwingSynchronousEnvironment} from "./environment";
|
||||
import {createOutputBuffer, TwingOutputBuffer} from "./output-buffer";
|
||||
import {TwingSourceMapRuntime} from "./source-map-runtime";
|
||||
@ -184,7 +184,7 @@ export interface TwingSynchronousTemplate {
|
||||
*/
|
||||
execute(
|
||||
environment: TwingSynchronousEnvironment,
|
||||
context: TwingContext2,
|
||||
context: Map<string, any>,
|
||||
blocks: TwingSynchronousTemplateBlockMap,
|
||||
outputBuffer: TwingOutputBuffer,
|
||||
options?: {
|
||||
@ -225,7 +225,7 @@ export interface TwingSynchronousTemplate {
|
||||
|
||||
render(
|
||||
environment: TwingSynchronousEnvironment,
|
||||
context: TwingContext2,
|
||||
context: Map<string, any>,
|
||||
options?: {
|
||||
nodeExecutor?: TwingSynchronousNodeExecutor;
|
||||
outputBuffer?: TwingOutputBuffer;
|
||||
@ -749,7 +749,7 @@ export const createSynchronousTemplate = (
|
||||
|
||||
const aliases = {...template.aliases};
|
||||
|
||||
const localVariables: TwingContext2 = new Map();
|
||||
const localVariables: Map<string, any> = new Map();
|
||||
|
||||
for (const {key: keyNode, value: defaultValueNode} of keyValuePairs) {
|
||||
const key = keyNode.attributes.value as string;
|
||||
|
@ -9,7 +9,7 @@ tape('library index', ({same, end}) => {
|
||||
'createParsingError',
|
||||
'createFilesystemLoader', 'createSynchronousFilesystemLoader',
|
||||
'createArrayLoader', 'createSynchronousArrayLoader',
|
||||
'createChainLoader',
|
||||
'createChainLoader', 'createSynchronousChainLoader',
|
||||
'createMarkup', 'isAMarkup',
|
||||
'createApplyNode',
|
||||
'createAutoEscapeNode',
|
||||
@ -117,17 +117,17 @@ tape('library index', ({same, end}) => {
|
||||
'getChildrenCount',
|
||||
'createEnvironment', 'createSynchronousEnvironment',
|
||||
'createExtensionSet',
|
||||
'createFilter',
|
||||
'createFunction',
|
||||
'createFilter', 'createSynchronousFilter',
|
||||
'createFunction', 'createSynchronousFunction',
|
||||
'createLexer',
|
||||
'createOperator',
|
||||
'createSandboxSecurityPolicy',
|
||||
'createSource',
|
||||
'createSourceMapRuntime',
|
||||
'createTemplate',
|
||||
'createTest',
|
||||
'createTemplate', 'createSynchronousTemplate',
|
||||
'createTest', 'createSynchronousTest',
|
||||
'executeNode', 'executeNodeSynchronously',
|
||||
'createTemplateLoader',
|
||||
'createTemplateLoader', 'createSynchronousTemplateLoader',
|
||||
'createContext',
|
||||
'createOutputBuffer'
|
||||
];
|
||||
|
@ -1,6 +1,6 @@
|
||||
import * as tape from 'tape';
|
||||
import {createChainLoader} from "../../../../../../main/lib/loader/chain";
|
||||
import {createArrayLoader} from "../../../../../../main/lib/loader/array";
|
||||
import {createChainLoader, createSynchronousChainLoader} from "../../../../../../main/lib/loader/chain";
|
||||
import {createArrayLoader, createSynchronousArrayLoader} from "../../../../../../main/lib/loader/array";
|
||||
import {spy, stub} from "sinon";
|
||||
|
||||
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