mirror of
https://gitlab.com/nightlycommit/twing.git
synced 2025-01-18 08:46:50 +02:00
Merge branch 'issue-611' into 'milestone/7.0.0'
Resolve issue #611 - Reduce the surface of TwingTemplate API See merge request nightlycommit/twing!603
This commit is contained in:
commit
78da934a48
@ -63,7 +63,7 @@ export const include: TwingCallable<[
|
||||
}
|
||||
|
||||
const resolveTemplate = (templates: Array<string | TwingTemplate | null>): Promise<TwingTemplate | null> => {
|
||||
return template.resolveTemplate(executionContext, templates)
|
||||
return template.loadTemplate(executionContext, templates)
|
||||
.catch((error) => {
|
||||
if (!ignoreMissing) {
|
||||
throw error;
|
||||
@ -82,6 +82,7 @@ export const include: TwingCallable<[
|
||||
return template.execute(
|
||||
environment,
|
||||
createContext(variables),
|
||||
new Map(),
|
||||
outputBuffer,
|
||||
{
|
||||
nodeExecutor,
|
||||
|
@ -11,8 +11,6 @@ export function getTraceableMethod<M extends (...args: Array<any>) => Promise<an
|
||||
error.source = templateName;
|
||||
}
|
||||
} else {
|
||||
console.log(error);
|
||||
|
||||
throw createRuntimeError(`An exception has been thrown during the rendering of a template ("${error.message}").`, {
|
||||
line,
|
||||
column
|
||||
|
@ -5,19 +5,18 @@ import {getTraceableMethod} from "../helpers/traceable-method";
|
||||
export const executeBlockReferenceNode: TwingNodeExecutor<TwingBlockReferenceNode> = (node, executionContext) => {
|
||||
const {
|
||||
template,
|
||||
context,
|
||||
outputBuffer
|
||||
context
|
||||
} = executionContext;
|
||||
const {name} = node.attributes;
|
||||
|
||||
const renderBlock = getTraceableMethod(template.renderBlock, node.line, node.column, template.name);
|
||||
const displayBlock = getTraceableMethod(template.displayBlock, node.line, node.column, template.name);
|
||||
|
||||
return renderBlock(
|
||||
return displayBlock(
|
||||
{
|
||||
...executionContext,
|
||||
context: context.clone()
|
||||
},
|
||||
name,
|
||||
true,
|
||||
).then(outputBuffer.echo);
|
||||
);
|
||||
};
|
||||
|
@ -8,7 +8,8 @@ export const executeBlockFunction: TwingNodeExecutor<TwingBlockFunctionNode> = a
|
||||
template,
|
||||
context,
|
||||
nodeExecutor: execute,
|
||||
blocks
|
||||
blocks,
|
||||
outputBuffer
|
||||
} = executionContext;
|
||||
const {template: templateNode, name: blockNameNode} = node.children;
|
||||
|
||||
@ -41,19 +42,18 @@ export const executeBlockFunction: TwingNodeExecutor<TwingBlockFunctionNode> = a
|
||||
context: context.clone()
|
||||
}, blockName, blocks);
|
||||
} else {
|
||||
const renderBlock = getTraceableMethod(templateOfTheBlock.renderBlock, node.line, node.column, template.name);
|
||||
const displayBlock = getTraceableMethod(templateOfTheBlock.displayBlock, node.line, node.column, template.name);
|
||||
|
||||
if (templateNode) {
|
||||
return renderBlock({
|
||||
...executionContext,
|
||||
context: context.clone()
|
||||
}, blockName, false);
|
||||
} else {
|
||||
return renderBlock({
|
||||
...executionContext,
|
||||
context: context.clone()
|
||||
}, blockName, true);
|
||||
}
|
||||
let useBlocks = templateNode === undefined;
|
||||
|
||||
outputBuffer.start();
|
||||
|
||||
return displayBlock({
|
||||
...executionContext,
|
||||
context: context.clone()
|
||||
}, blockName, useBlocks).then<string>(() => {
|
||||
return outputBuffer.getAndClean();
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -24,8 +24,6 @@ export const executeMethodCall: TwingNodeExecutor<TwingMethodCallNode> = async (
|
||||
|
||||
// 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)!;
|
||||
|
||||
console.log('executeMethodCall', template.name, aliases.has('macros'));
|
||||
|
||||
const getHandler = (template: TwingTemplate): Promise<TwingTemplateMacroHandler | null> => {
|
||||
const macroHandler = template.macroHandlers.get(methodName);
|
||||
|
@ -3,9 +3,11 @@ import type {TwingParentFunctionNode} from "../../node/expression/parent-functio
|
||||
import {getTraceableMethod} from "../../helpers/traceable-method";
|
||||
|
||||
export const executeParentFunction: TwingNodeExecutor<TwingParentFunctionNode> = (node, executionContext) => {
|
||||
const {template} = executionContext;
|
||||
const {template, outputBuffer} = executionContext;
|
||||
const {name} = node.attributes;
|
||||
const renderParentBlock = getTraceableMethod(template.renderParentBlock, node.line, node.column, template.name);
|
||||
|
||||
return renderParentBlock(executionContext, name);
|
||||
const displayParentBlock = getTraceableMethod(template.displayParentBlock, node.line, node.column, template.name);
|
||||
|
||||
outputBuffer.start();
|
||||
|
||||
return displayParentBlock(executionContext, name).then(() => outputBuffer.getAndClean());
|
||||
};
|
||||
|
@ -25,10 +25,6 @@ export const executeImportNode: TwingNodeExecutor<TwingImportNode> = async (node
|
||||
aliases.set(aliasNode.attributes.name, aliasValue);
|
||||
|
||||
if (global) {
|
||||
console.log('executeImportNode', template.name, template.aliases.has('macros'));
|
||||
|
||||
template.aliases.set(aliasNode.attributes.name, aliasValue);
|
||||
|
||||
console.log('>>> executeImportNode', template.name, template.aliases.has('macros'));
|
||||
}
|
||||
};
|
||||
|
@ -14,7 +14,7 @@ export const executeBaseIncludeNode = async (
|
||||
const {only, ignoreMissing} = node.attributes;
|
||||
|
||||
const templatesToInclude = await getTemplate(executionContext);
|
||||
|
||||
|
||||
const traceableInclude = getTraceableMethod(include, node.line, node.column, template.name);
|
||||
|
||||
const output = await traceableInclude(
|
||||
|
@ -7,8 +7,15 @@ export const executeEmbedNode: TwingNodeExecutor<TwingEmbedNode> = (node, execut
|
||||
return executeBaseIncludeNode(node, executionContext, ({template}) => {
|
||||
const {index} = node.attributes;
|
||||
|
||||
const loadTemplate = getTraceableMethod(template.loadEmbeddedTemplate, node.line, node.column, template.name);
|
||||
const loadEmbeddedTemplate = getTraceableMethod(() => {
|
||||
const {embeddedTemplates} = template;
|
||||
|
||||
return loadTemplate(index);
|
||||
// by design, it is guaranteed that an embed node is always executed with an index that corresponds to an existing embedded template
|
||||
const embeddedTemplate = embeddedTemplates.get(index)!;
|
||||
|
||||
return Promise.resolve(embeddedTemplate);
|
||||
}, node.line, node.column, template.name);
|
||||
|
||||
return loadEmbeddedTemplate();
|
||||
})
|
||||
};
|
||||
|
@ -19,7 +19,7 @@ import {getKeyValuePairs} from "./helpers/get-key-value-pairs";
|
||||
import {createTemplateLoader, type TwingTemplateLoader} from "./template-loader";
|
||||
import type {TwingExecutionContext} from "./execution-context";
|
||||
|
||||
export type TwingTemplateBlockMap = Map<string, [TwingTemplate, string]>;
|
||||
export type TwingTemplateBlockMap = Map<string, [template: TwingTemplate, name: string]>;
|
||||
export type TwingTemplateBlockHandler = (executionContent: TwingExecutionContext) => Promise<void>;
|
||||
export type TwingTemplateMacroHandler = (
|
||||
executionContent: TwingExecutionContext,
|
||||
@ -36,6 +36,7 @@ export interface TwingTemplate {
|
||||
readonly ast: TwingTemplateNode;
|
||||
readonly blockHandlers: Map<string, TwingTemplateBlockHandler>;
|
||||
readonly canBeUsedAsATrait: boolean;
|
||||
readonly embeddedTemplates: Map<number, TwingTemplate>;
|
||||
readonly source: TwingSource;
|
||||
readonly macroHandlers: Map<string, TwingTemplateMacroHandler>;
|
||||
readonly name: string;
|
||||
@ -46,22 +47,32 @@ export interface TwingTemplate {
|
||||
useBlocks: boolean
|
||||
): Promise<void>;
|
||||
|
||||
displayParentBlock(
|
||||
executionContext: TwingExecutionContext,
|
||||
name: string
|
||||
): Promise<void>;
|
||||
|
||||
/**
|
||||
* Execute the template against an environment,
|
||||
*
|
||||
* Theoretically speaking,
|
||||
*
|
||||
* Semantically speaking, provide a closure to the template:
|
||||
* * an environment providing the filters, functions, globals and tests;
|
||||
* * a context providing the variables;
|
||||
* * a block map providing the available blocks;
|
||||
* * an output buffer providing the mean to output the result of an expression.
|
||||
*
|
||||
* Technically speaking, execute the template and stream the result to the output buffer.
|
||||
*
|
||||
* @param environment
|
||||
* @param context
|
||||
* @param blocks
|
||||
* @param outputBuffer
|
||||
* @param options
|
||||
*/
|
||||
execute(
|
||||
environment: TwingEnvironment,
|
||||
context: TwingContext<any, any>,
|
||||
blocks: TwingTemplateBlockMap,
|
||||
outputBuffer: TwingOutputBuffer,
|
||||
options?: {
|
||||
blocks?: TwingTemplateBlockMap;
|
||||
nodeExecutor?: TwingNodeExecutor;
|
||||
sandboxed?: boolean;
|
||||
sourceMapRuntime?: TwingSourceMapRuntime;
|
||||
@ -89,21 +100,12 @@ export interface TwingTemplate {
|
||||
|
||||
hasMacro(name: string): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* @param index
|
||||
*
|
||||
* @throws {TwingTemplateLoadingError} When no embedded template exists for the passed index.
|
||||
*/
|
||||
loadEmbeddedTemplate(
|
||||
index: number
|
||||
): Promise<TwingTemplate>;
|
||||
|
||||
/**
|
||||
* @throws {TwingTemplateLoadingError} When no embedded template exists for the passed identifier.
|
||||
*/
|
||||
loadTemplate(
|
||||
executionContext: TwingExecutionContext,
|
||||
identifier: TwingTemplate | string | Array<TwingTemplate | null>,
|
||||
identifier: TwingTemplate | string | Array<string | TwingTemplate | null>,
|
||||
): Promise<TwingTemplate>;
|
||||
|
||||
render(
|
||||
@ -122,30 +124,6 @@ export interface TwingTemplate {
|
||||
strict?: boolean;
|
||||
}
|
||||
): Promise<string>;
|
||||
|
||||
renderBlock(
|
||||
executionContext: TwingExecutionContext,
|
||||
name: string,
|
||||
useBlocks: boolean
|
||||
): Promise<string>;
|
||||
|
||||
renderParentBlock(
|
||||
executionContext: TwingExecutionContext,
|
||||
name: string
|
||||
): Promise<string>;
|
||||
|
||||
/**
|
||||
* Tries to load templates consecutively from an array.
|
||||
*
|
||||
* Similar to loadTemplate() but it also accepts instances of TwingTemplate and an array of templates where each is tried to be loaded.
|
||||
*
|
||||
* @param executionContext
|
||||
* @param names A template or an array of templates to try consecutively
|
||||
*/
|
||||
resolveTemplate(
|
||||
executionContext: TwingExecutionContext,
|
||||
names: Array<string | TwingTemplate | null>
|
||||
): Promise<TwingTemplate>;
|
||||
}
|
||||
|
||||
export const createTemplate = (
|
||||
@ -236,38 +214,15 @@ export const createTemplate = (
|
||||
let traits: TwingTemplateBlockMap | null = null;
|
||||
|
||||
// embedded templates
|
||||
const embeddedTemplates: Map<number, TwingTemplateNode> = new Map();
|
||||
const embeddedTemplates: Map<number, TwingTemplate> = new Map();
|
||||
|
||||
for (const embeddedTemplate of ast.embeddedTemplates) {
|
||||
embeddedTemplates.set(embeddedTemplate.attributes.index, embeddedTemplate);
|
||||
embeddedTemplates.set(embeddedTemplate.attributes.index, createTemplate(embeddedTemplate));
|
||||
}
|
||||
|
||||
// parent
|
||||
let parent: TwingTemplate | null = null;
|
||||
|
||||
const displayParentBlock = (executionContext: TwingExecutionContext, name: string): Promise<void> => {
|
||||
return template.getTraits(executionContext)
|
||||
.then((traits) => {
|
||||
const trait = traits.get(name);
|
||||
|
||||
if (trait) {
|
||||
const [blockTemplate, blockName] = trait;
|
||||
|
||||
return blockTemplate.displayBlock(executionContext, blockName, false);
|
||||
} else {
|
||||
return template.getParent(executionContext)
|
||||
.then((parent) => {
|
||||
if (parent !== null) {
|
||||
return parent.displayBlock(executionContext, name, false);
|
||||
} else {
|
||||
throw createRuntimeError(`The template has no parent and no traits defining the "${name}" block.`, undefined, template.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
// A template can be used as a trait if:
|
||||
// * it has no parent
|
||||
// * it has no macros
|
||||
@ -298,6 +253,49 @@ export const createTemplate = (
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to load templates consecutively from an array.
|
||||
*
|
||||
* Similar to loadTemplate() but it also accepts instances of TwingTemplate and an array of templates where each is tried to be loaded.
|
||||
*
|
||||
* @param executionContext
|
||||
* @param names A template or an array of templates to try consecutively
|
||||
*/
|
||||
const resolveTemplate = (
|
||||
executionContext: TwingExecutionContext,
|
||||
names: Array<string | TwingTemplate | null>
|
||||
) => {
|
||||
const loadTemplateAtIndex = (index: number): Promise<TwingTemplate> => {
|
||||
if (index < names.length) {
|
||||
const name = names[index];
|
||||
|
||||
if (name === null) {
|
||||
return loadTemplateAtIndex(index + 1);
|
||||
}
|
||||
else if (typeof name !== "string") {
|
||||
return Promise.resolve(name);
|
||||
}
|
||||
else {
|
||||
return template.loadTemplate(executionContext, name)
|
||||
.catch(() => {
|
||||
return loadTemplateAtIndex(index + 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
return Promise.reject(createTemplateLoadingError((names as Array<string | null>).map((name) => {
|
||||
if (name === null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return name;
|
||||
}), undefined, template.name));
|
||||
}
|
||||
};
|
||||
|
||||
return loadTemplateAtIndex(0);
|
||||
};
|
||||
|
||||
const template: TwingTemplate = {
|
||||
get aliases() {
|
||||
return aliases;
|
||||
@ -311,6 +309,9 @@ export const createTemplate = (
|
||||
get canBeUsedAsATrait() {
|
||||
return canBeUsedAsATrait;
|
||||
},
|
||||
get embeddedTemplates() {
|
||||
return embeddedTemplates;
|
||||
},
|
||||
get macroHandlers() {
|
||||
return macroHandlers;
|
||||
},
|
||||
@ -332,7 +333,8 @@ export const createTemplate = (
|
||||
const [blockTemplate, blockName] = block;
|
||||
|
||||
blockHandler = blockTemplate.blockHandlers.get(blockName);
|
||||
} else if ((block = ownBlocks.get(name)) !== undefined) {
|
||||
}
|
||||
else if ((block = ownBlocks.get(name)) !== undefined) {
|
||||
const [blockTemplate, blockName] = block;
|
||||
|
||||
blockHandler = blockTemplate.blockHandlers.get(blockName);
|
||||
@ -340,18 +342,21 @@ export const createTemplate = (
|
||||
|
||||
if (blockHandler) {
|
||||
return blockHandler(executionContext);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return template.getParent(executionContext).then((parent) => {
|
||||
if (parent) {
|
||||
return parent.displayBlock(executionContext, name, false);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
const block = blocks.get(name);
|
||||
|
||||
if (block) {
|
||||
const [blockTemplate] = block!;
|
||||
|
||||
throw createRuntimeError(`Block "${name}" should not call parent() in "${blockTemplate.name}" as the block does not exist in the parent template "${template.name}".`, undefined, blockTemplate.name);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
throw createRuntimeError(`Block "${name}" on template "${template.name}" does not exist.`, undefined, template.name);
|
||||
}
|
||||
}
|
||||
@ -360,9 +365,32 @@ export const createTemplate = (
|
||||
}
|
||||
});
|
||||
},
|
||||
execute: async (environment, context, outputBuffer, options) => {
|
||||
displayParentBlock: (executionContext: TwingExecutionContext, name: string): Promise<void> => {
|
||||
return template.getTraits(executionContext)
|
||||
.then((traits) => {
|
||||
const trait = traits.get(name);
|
||||
|
||||
if (trait) {
|
||||
const [blockTemplate, blockName] = trait;
|
||||
|
||||
return blockTemplate.displayBlock(executionContext, blockName, false);
|
||||
}
|
||||
else {
|
||||
return template.getParent(executionContext)
|
||||
.then((parent) => {
|
||||
if (parent !== null) {
|
||||
return parent.displayBlock(executionContext, name, false);
|
||||
}
|
||||
else {
|
||||
throw createRuntimeError(`The template has no parent and no traits defining the "${name}" block.`, undefined, template.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
execute: async (environment, context, blocks, outputBuffer, options) => {
|
||||
const aliases = template.aliases.clone();
|
||||
const childBlocks = options?.blocks || new Map();
|
||||
const nodeExecutor = options?.nodeExecutor || executeNode;
|
||||
const sandboxed = options?.sandboxed || false;
|
||||
const sourceMapRuntime = options?.sourceMapRuntime;
|
||||
@ -386,17 +414,14 @@ export const createTemplate = (
|
||||
template.getParent(executionContext),
|
||||
template.getBlocks(executionContext)
|
||||
]).then(([parent, ownBlocks]) => {
|
||||
const blocks = mergeIterables(ownBlocks, childBlocks);
|
||||
blocks = mergeIterables(ownBlocks, blocks);
|
||||
|
||||
return nodeExecutor(ast, {
|
||||
...executionContext,
|
||||
blocks
|
||||
}).then(() => {
|
||||
if (parent) {
|
||||
return parent.execute(environment, context, outputBuffer, {
|
||||
...options,
|
||||
blocks
|
||||
});
|
||||
return parent.execute(environment, context, blocks, outputBuffer, options);
|
||||
}
|
||||
});
|
||||
}).catch((error: TwingError) => {
|
||||
@ -414,7 +439,8 @@ export const createTemplate = (
|
||||
getBlocks: (executionContext) => {
|
||||
if (blocks) {
|
||||
return Promise.resolve(blocks);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return template.getTraits(executionContext)
|
||||
.then((traits) => {
|
||||
blocks = mergeIterables(traits, new Map([...blockHandlers.keys()].map((key) => {
|
||||
@ -458,7 +484,8 @@ export const createTemplate = (
|
||||
|
||||
return loadedParent;
|
||||
});
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
},
|
||||
@ -509,17 +536,20 @@ export const createTemplate = (
|
||||
hasBlock: (executionContext, name, blocks): Promise<boolean> => {
|
||||
if (blocks.has(name)) {
|
||||
return Promise.resolve(true);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return template.getBlocks(executionContext)
|
||||
.then((blocks) => {
|
||||
if (blocks.has(name)) {
|
||||
return Promise.resolve(true);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return template.getParent(executionContext)
|
||||
.then((parent) => {
|
||||
if (parent) {
|
||||
return parent.hasBlock(executionContext, name, blocks);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
@ -531,15 +561,6 @@ export const createTemplate = (
|
||||
// @see https://github.com/twigphp/Twig/issues/3174 as to why we don't check macro existence in parents
|
||||
return Promise.resolve(template.macroHandlers.has(name));
|
||||
},
|
||||
loadEmbeddedTemplate: (index) => {
|
||||
const ast = embeddedTemplates.get(index);
|
||||
|
||||
if (ast === undefined) {
|
||||
return Promise.reject(createTemplateLoadingError([`embedded#${index}`]));
|
||||
}
|
||||
|
||||
return Promise.resolve(createTemplate(ast));
|
||||
},
|
||||
loadTemplate: (executionContext, identifier) => {
|
||||
let promise: Promise<TwingTemplate>;
|
||||
|
||||
@ -549,12 +570,14 @@ export const createTemplate = (
|
||||
if (template === null) {
|
||||
throw createTemplateLoadingError([identifier]);
|
||||
}
|
||||
|
||||
|
||||
return template;
|
||||
});
|
||||
} else if (Array.isArray(identifier)) {
|
||||
promise = template.resolveTemplate(executionContext, identifier);
|
||||
} else {
|
||||
}
|
||||
else if (Array.isArray(identifier)) {
|
||||
promise = resolveTemplate(executionContext, identifier);
|
||||
}
|
||||
else {
|
||||
promise = Promise.resolve(identifier);
|
||||
}
|
||||
|
||||
@ -568,63 +591,12 @@ export const createTemplate = (
|
||||
return template.execute(
|
||||
environment,
|
||||
createContext(iteratorToMap(context)),
|
||||
new Map(),
|
||||
outputBuffer,
|
||||
options
|
||||
).then(() => {
|
||||
return outputBuffer.getAndFlush();
|
||||
});
|
||||
},
|
||||
renderBlock: (executionContext, name, useBlocks) => {
|
||||
const {outputBuffer} = executionContext;
|
||||
|
||||
outputBuffer.start();
|
||||
|
||||
return template.displayBlock(executionContext, name, useBlocks).then(() => {
|
||||
return outputBuffer.getAndClean();
|
||||
});
|
||||
},
|
||||
renderParentBlock: (executionContext, name) => {
|
||||
const {outputBuffer} = executionContext;
|
||||
|
||||
outputBuffer.start();
|
||||
|
||||
return template.getBlocks(executionContext)
|
||||
.then((blocks) => {
|
||||
return displayParentBlock({
|
||||
...executionContext,
|
||||
blocks
|
||||
}, name).then(() => {
|
||||
return outputBuffer.getAndClean();
|
||||
})
|
||||
});
|
||||
},
|
||||
resolveTemplate: (executionContext, names) => {
|
||||
const loadTemplateAtIndex = (index: number): Promise<TwingTemplate> => {
|
||||
if (index < names.length) {
|
||||
const name = names[index];
|
||||
|
||||
if (name === null) {
|
||||
return loadTemplateAtIndex(index + 1);
|
||||
} else if (typeof name !== "string") {
|
||||
return Promise.resolve(name);
|
||||
} else {
|
||||
return template.loadTemplate(executionContext, name)
|
||||
.catch(() => {
|
||||
return loadTemplateAtIndex(index + 1);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return Promise.reject(createTemplateLoadingError((names as Array<string | null>).map((name) => {
|
||||
if (name === null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return name;
|
||||
}), undefined, template.name));
|
||||
}
|
||||
};
|
||||
|
||||
return loadTemplateAtIndex(0);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -2,6 +2,7 @@ import "./basic";
|
||||
import "./complex_dynamic_parent";
|
||||
import "./dynamic_parent";
|
||||
import "./error_line";
|
||||
import "./missing";
|
||||
import "./multiple";
|
||||
import "./nested";
|
||||
import "./with_extends";
|
||||
|
11
test/tests/integration/tags/embed/missing.ts
Normal file
11
test/tests/integration/tags/embed/missing.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import {runTest} from "../../TestBase";
|
||||
|
||||
runTest({
|
||||
description: '"embed" tag with missing template',
|
||||
templates: {
|
||||
'index.twig': `{% embed "missing" %}{% endembed %}`
|
||||
},
|
||||
expectedErrorMessage: `TwingRuntimeError: Unable to find template "missing" in "index.twig" at line 1, column 4.`
|
||||
});
|
||||
|
||||
|
36
test/tests/unit/lib/template/embedded-templates.ts
Normal file
36
test/tests/unit/lib/template/embedded-templates.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import * as tape from "tape";
|
||||
import {createTemplate} from "../../../../../src/lib/template";
|
||||
import {createTemplateNode} from "../../../../../src/lib/node/template";
|
||||
import {createBaseNode} from "../../../../../src/lib/node";
|
||||
import {createSource} from "../../../../../src/lib/source";
|
||||
|
||||
tape('createTemplate => ::embeddedTemplates', ({test}) => {
|
||||
test('throws an error on invalid index', ({same, end}) => {
|
||||
const embeddedTemplateNode = createTemplateNode(createBaseNode(null),
|
||||
null,
|
||||
createBaseNode(null),
|
||||
createBaseNode(null),
|
||||
createBaseNode(null),
|
||||
[],
|
||||
createSource('', ''),
|
||||
1, 1
|
||||
);
|
||||
|
||||
const template = createTemplate(createTemplateNode(
|
||||
createBaseNode(null),
|
||||
null,
|
||||
createBaseNode(null),
|
||||
createBaseNode(null),
|
||||
createBaseNode(null),
|
||||
[
|
||||
embeddedTemplateNode
|
||||
],
|
||||
createSource('', ''),
|
||||
1, 1
|
||||
));
|
||||
|
||||
same(template.embeddedTemplates.get(0)?.ast, embeddedTemplateNode);
|
||||
|
||||
end();
|
||||
});
|
||||
});
|
@ -38,9 +38,9 @@ tape('createTemplate => ::execute', ({test}) => {
|
||||
return template.execute(
|
||||
environment,
|
||||
createContext(),
|
||||
new Map(),
|
||||
createOutputBuffer(),
|
||||
{
|
||||
blocks: new Map(),
|
||||
nodeExecutor: executeNodeSpy
|
||||
}
|
||||
).then(() => {
|
||||
@ -73,9 +73,9 @@ tape('createTemplate => ::execute', ({test}) => {
|
||||
return template.execute(
|
||||
environment,
|
||||
createContext(),
|
||||
new Map(),
|
||||
createOutputBuffer(),
|
||||
{
|
||||
blocks: new Map(),
|
||||
nodeExecutor: executeNodeSpy,
|
||||
sandboxed: true,
|
||||
sourceMapRuntime
|
||||
@ -121,7 +121,7 @@ tape('createTemplate => ::execute', ({test}) => {
|
||||
|
||||
outputBuffer.start();
|
||||
|
||||
return template.execute(environment, createContext(), outputBuffer, {
|
||||
return template.execute(environment, createContext(), new Map(), outputBuffer, {
|
||||
nodeExecutor
|
||||
}).then(() => {
|
||||
same(outputBuffer.getContents(), 'foo5');
|
||||
@ -152,6 +152,7 @@ tape('createTemplate => ::execute', ({test}) => {
|
||||
return template.execute(
|
||||
environment,
|
||||
createContext(),
|
||||
new Map(),
|
||||
outputBuffer,
|
||||
{
|
||||
templateLoader
|
||||
@ -163,4 +164,30 @@ tape('createTemplate => ::execute', ({test}) => {
|
||||
]);
|
||||
}).finally(end);
|
||||
});
|
||||
|
||||
test('with some blocks', async ({same, end}) => {
|
||||
const environment = createEnvironment(createArrayLoader({
|
||||
index: '{{ block("foo") }}, {{ block("aliased-bar") }}',
|
||||
blocks: `{% block foo %}foo block content{% endblock %}{% block bar %}bar block content{% endblock %}`
|
||||
}));
|
||||
|
||||
const template = await environment.loadTemplate('index');
|
||||
const outputBuffer = createOutputBuffer();
|
||||
|
||||
outputBuffer.start();
|
||||
|
||||
const blockTemplate = await environment.loadTemplate('blocks');
|
||||
|
||||
return template.execute(
|
||||
environment,
|
||||
createContext(),
|
||||
new Map([
|
||||
['foo', [blockTemplate, 'foo']],
|
||||
['aliased-bar', [blockTemplate, 'bar']]
|
||||
]),
|
||||
outputBuffer
|
||||
).then(() => {
|
||||
same(outputBuffer.getContents(), 'foo block content, bar block content');
|
||||
}).finally(end);
|
||||
});
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
import "./ast";
|
||||
import "./embedded-templates";
|
||||
import "./execute";
|
||||
import "./load-embedded-template";
|
||||
import "./render";
|
||||
|
@ -1,29 +0,0 @@
|
||||
import * as tape from "tape";
|
||||
import {createTemplate} from "../../../../../src/lib/template";
|
||||
import {createTemplateNode} from "../../../../../src/lib/node/template";
|
||||
import {createBaseNode} from "../../../../../src/lib/node";
|
||||
import {createSource} from "../../../../../src/lib/source";
|
||||
|
||||
tape('createTemplate => ::loadEmbeddedTemplate', ({test}) => {
|
||||
test('throws an error on invalid index', ({fail, same, end}) => {
|
||||
const template = createTemplate(createTemplateNode(
|
||||
createBaseNode(null, {}, {
|
||||
content: createBaseNode(null)
|
||||
}, 1, 1),
|
||||
null,
|
||||
createBaseNode(null),
|
||||
createBaseNode(null),
|
||||
createBaseNode(null),
|
||||
[],
|
||||
createSource('', ''),
|
||||
1, 1
|
||||
));
|
||||
|
||||
return template.loadEmbeddedTemplate(0)
|
||||
.then(() => fail())
|
||||
.catch((error) => {
|
||||
same((error as Error).message, 'Unable to find template "embedded#0".');
|
||||
})
|
||||
.finally(end);
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user