Clean TwingNode API

This commit is contained in:
Eric MORAND 2023-12-05 10:47:33 +00:00
parent c94bd15a47
commit 2aaf14c6da
17 changed files with 62 additions and 98 deletions

View File

@ -46,6 +46,7 @@ export type {
export type {TwingApplyNode, TwingApplyNodeAttributes, TwingApplyNodeChildren} from "./lib/node/apply";
export type {TwingAutoEscapeNode, TwingAutoEscapeNodeAttributes} from "./lib/node/auto-escape";
export type {TwingBlockNode, TwingBlockNodeAttributes} from "./lib/node/block";
export type {TwingBlockReferenceNode, TwingBlockReferenceNodeAttributes} from "./lib/node/block-reference";
export type {TwingBodyNode} from "./lib/node/body";
export type {TwingCheckSecurityNode, TwingCheckSecurityNodeAttributes} from "./lib/node/check-security";
export type {TwingCheckToStringNode} from "./lib/node/check-to-string";
@ -65,17 +66,21 @@ export type {
} from "./lib/node/include";
export type {TwingLineNode, TwingLineNodeAttributes} from "./lib/node/line";
export type {TwingMacroNode, TwingMacroNodeAttributes} from "./lib/node/macro";
export type {TwingBaseOutputNode} from "./lib/node/output";
export type {TwingTemplateNode, TwingTemplateNodeAttributes, TwingTemplateNodeChildren} from "./lib/node/template";
export type {TwingPrintNode} from "./lib/node/print";
export type {TwingSandboxNode} from "./lib/node/sandbox";
export type {TwingSpacelessNode} from "./lib/node/spaceless";
export type {TwingTemplateNode, TwingTemplateNodeAttributes, TwingTemplateNodeChildren} from "./lib/node/template";
export type {TwingSetNode, TwingSetNodeAttributes} from "./lib/node/set";
export type {TwingTextNode, TwingBaseTextNode, TwingBaseTextNodeAttributes} from "./lib/node/text";
export type {TwingTraitNode} from "./lib/node/trait";
export type {TwingVerbatimNode} from "./lib/node/verbatim";
export type {TwingWithNode, TwingWithNodeAttributes, TwingWithNodeChildren} from "./lib/node/with";
export type {TwingWrapperNode, TwingWrapperNodeChildren} from "./lib/node/wrapper";
export {createApplyNode} from "./lib/node/apply";
export {createAutoEscapeNode} from "./lib/node/auto-escape";
export {createBlockNode} from "./lib/node/block";
export {createBlockReferenceNode} from "./lib/node/block-reference";
export {createBodyNode} from "./lib/node/body";
export {createCheckSecurityNode} from "./lib/node/check-security";
export {createCheckToStringNode} from "./lib/node/check-to-string";
@ -90,10 +95,14 @@ export {createImportNode} from "./lib/node/import";
export {createBaseIncludeNode} from "./lib/node/include";
export {createLineNode} from "./lib/node/line";
export {createMacroNode} from "./lib/node/macro";
export {createTemplateNode} from "./lib/node/template";
export {createPrintNode} from "./lib/node/print";
export {createSandboxNode} from "./lib/node/sandbox";
export {createSetNode} from "./lib/node/set";
export {createSpacelessNode} from "./lib/node/spaceless";
export {createTemplateNode} from "./lib/node/template";
export {createTextNode} from "./lib/node/text";
export {createTraitNode} from "./lib/node/trait";
export {createVerbatimNode} from "./lib/node/verbatim";
export {createWithNode} from "./lib/node/with";
export {createWrapperNode} from "./lib/node/wrapper";
@ -228,19 +237,6 @@ export type {TwingIncludeNode, TwingIncludeNodeChildren} from "./lib/node/includ
export {createEmbedNode} from "./lib/node/include/embed";
export {createIncludeNode} from "./lib/node/include/include";
// node/output
export type {TwingBlockReferenceNode, TwingBlockReferenceNodeAttributes} from "./lib/node/output/block-reference";
export type {TwingPrintNode} from "./lib/node/output/print";
export type {TwingSpacelessNode} from "./lib/node/output/spaceless";
export type {TwingTextNode, TwingBaseTextNode, TwingBaseTextNodeAttributes} from "./lib/node/output/text";
export type {TwingVerbatimNode} from "./lib/node/output/verbatim";
export {createBlockReferenceNode} from "./lib/node/output/block-reference";
export {createPrintNode} from "./lib/node/output/print";
export {createSpacelessNode} from "./lib/node/output/spaceless";
export {createTextNode} from "./lib/node/output/text";
export {createVerbatimNode} from "./lib/node/output/verbatim";
// tag handlers
export type {TwingTagHandler, TwingTokenParser} from "./lib/tag-handler";

View File

@ -1,5 +1,5 @@
import {TwingBaseNode, TwingNode} from "../node";
import {createPrintNode, TwingPrintNode} from "../node/output/print";
import {createPrintNode, TwingPrintNode} from "../node/print";
import {createDoNode} from "../node/do";
import {createConditionalNode, TwingConditionalNode} from "../node/expression/conditional";
import {createEscapeNode, TwingEscapeNode} from "../node/expression/escape";

View File

@ -1,7 +1,7 @@
import type {TwingExpressionNode} from "./node/expression";
import type {TwingPrintNode} from "./node/output/print";
import type {TwingBlockReferenceNode} from "./node/output/block-reference";
import type {TwingTextNode} from "./node/output/text";
import type {TwingPrintNode} from "./node/print";
import type {TwingBlockReferenceNode} from "./node/block-reference";
import type {TwingTextNode} from "./node/text";
import type {TwingAutoEscapeNode} from "./node/auto-escape";
import type {TwingBodyNode} from "./node/body";
import type {TwingCheckSecurityNode} from "./node/check-security";
@ -21,9 +21,9 @@ import type {TwingTemplateNode} from "./node/template";
import type {TwingBlockNode} from "./node/block";
import type {TwingTraitNode} from "./node/trait";
import type {TwingSetNode} from "./node/set";
import type {TwingVerbatimNode} from "./node/output/verbatim";
import type {TwingVerbatimNode} from "./node/verbatim";
import type {TwingSandboxNode} from "./node/sandbox";
import type {TwingSpacelessNode} from "./node/output/spaceless";
import type {TwingSpacelessNode} from "./node/spaceless";
import type {TwingWithNode} from "./node/with";
import type {TwingIfNode} from "./node/if";
import type {TwingMethodCallNode} from "./node/expression/method-call";
@ -82,8 +82,6 @@ export interface TwingBaseNode<
readonly attributes: Attributes;
readonly children: Children;
readonly column: number;
readonly isACaptureNode: boolean;
readonly isAnOutputNode: boolean;
readonly line: number;
readonly tag: string | null;
readonly type: Type;
@ -123,8 +121,7 @@ export const createBaseNode = <
column,
line,
tag,
isACaptureNode: false,
isAnOutputNode: false,
type,
execute: async (executionContext) => {
const output: Array<any> = [];
@ -133,7 +130,6 @@ export const createBaseNode = <
}
return output;
},
type
}
};
};

View File

@ -1,12 +1,11 @@
import {TwingBaseNodeAttributes} from "../../node";
import {TwingBaseOutputNode, createBaseOutputNode} from "../output";
import {getTraceableMethod} from "../../helpers/traceable-method";
import {createBaseNode, TwingBaseNode, TwingBaseNodeAttributes} from "../node";
import {getTraceableMethod} from "../helpers/traceable-method";
export type TwingBlockReferenceNodeAttributes = TwingBaseNodeAttributes & {
name: string;
};
export interface TwingBlockReferenceNode extends TwingBaseOutputNode<"block_reference", TwingBlockReferenceNodeAttributes, {}> {
export interface TwingBlockReferenceNode extends TwingBaseNode<"block_reference", TwingBlockReferenceNodeAttributes, {}> {
}
export const createBlockReferenceNode = (
@ -15,7 +14,7 @@ export const createBlockReferenceNode = (
column: number,
tag: string
): TwingBlockReferenceNode => {
const outputNode = createBaseOutputNode("block_reference", {
const outputNode = createBaseNode("block_reference", {
name
}, {}, line, column, tag);

View File

@ -1,6 +1,5 @@
import {TwingBaseNodeAttributes} from "../../node";
import {TwingBaseNodeAttributes, TwingBaseNode} from "../../node";
import {TwingBaseExpressionNode, createBaseExpressionNode} from "../expression";
import {TwingBaseOutputNode} from "../output";
import {getTraceableMethod} from "../../helpers/traceable-method";
export interface TwingEscapeNodeAttributes extends TwingBaseNodeAttributes {
@ -8,12 +7,12 @@ export interface TwingEscapeNodeAttributes extends TwingBaseNodeAttributes {
}
export interface TwingEscapeNode extends TwingBaseExpressionNode<"escape", TwingEscapeNodeAttributes, {
body: TwingBaseOutputNode<any>;
body: TwingBaseNode;
}> {
}
export const createEscapeNode = (
body: TwingBaseOutputNode<any>,
body: TwingBaseNode,
strategy: string
): TwingEscapeNode => {
const baseNode = createBaseExpressionNode("escape", {

View File

@ -1,25 +0,0 @@
import {TwingBaseNode, TwingBaseNodeAttributes, TwingBaseNodeChildren, createBaseNode} from "../node";
export interface TwingBaseOutputNode<
Type extends string,
Attributes extends TwingBaseNodeAttributes = TwingBaseNodeAttributes,
Children extends TwingBaseNodeChildren = TwingBaseNodeChildren
> extends TwingBaseNode<Type, Attributes, Children> {
}
export const createBaseOutputNode = <Type extends string, Attributes extends TwingBaseNodeAttributes, Children extends TwingBaseNodeChildren>(
type: Type,
attributes: Attributes,
children: Children,
line: number,
column: number,
tag: string | null
): TwingBaseOutputNode<Type, Attributes, Children> => {
const baseNode = createBaseNode(type, attributes, children, line, column, tag);
return {
...baseNode,
isAnOutputNode: true
};
};

View File

@ -1,7 +1,7 @@
import {TwingBaseOutputNode, createBaseOutputNode} from "../output";
import {TwingBaseExpressionNode} from "../expression";
import {TwingBaseExpressionNode} from "./expression";
import {createBaseNode, TwingBaseNode} from "../node";
export interface TwingPrintNode extends TwingBaseOutputNode<"print", {}, {
export interface TwingPrintNode extends TwingBaseNode<"print", {}, {
expression: TwingBaseExpressionNode;
}> {
}
@ -11,7 +11,7 @@ export const createPrintNode = (
line: number,
column: number
): TwingPrintNode => {
const outputNode: TwingPrintNode = createBaseOutputNode("print", {}, {
const outputNode: TwingPrintNode = createBaseNode("print", {}, {
expression: expression
}, line, column, null);

View File

@ -76,8 +76,7 @@ export const createSetNode = (
index++;
}
}
},
isACaptureNode: true
}
};
return setNode;

View File

@ -1,7 +1,6 @@
import {TwingBaseNode, TwingBaseNodeAttributes} from "../../node";
import {TwingBaseOutputNode, createBaseOutputNode} from "../output";
import {TwingBaseNode, TwingBaseNodeAttributes, createBaseNode} from "../node";
export interface TwingSpacelessNode extends TwingBaseOutputNode<"spaceless", TwingBaseNodeAttributes, {
export interface TwingSpacelessNode extends TwingBaseNode<"spaceless", TwingBaseNodeAttributes, {
body: TwingBaseNode;
}> {
}
@ -12,7 +11,7 @@ export const createSpacelessNode = (
column: number,
tag: string
): TwingSpacelessNode => {
const baseNode = createBaseOutputNode("spaceless", {}, {
const baseNode = createBaseNode("spaceless", {}, {
body
}, line, column, tag);

View File

@ -1,11 +1,10 @@
import {TwingBaseNodeAttributes} from "../../node";
import {TwingBaseOutputNode, createBaseOutputNode} from "../output";
import {createBaseNode, TwingBaseNodeAttributes, TwingBaseNode} from "../node";
export type TwingBaseTextNodeAttributes = TwingBaseNodeAttributes & {
data: string;
};
export interface TwingBaseTextNode<Type extends string> extends TwingBaseOutputNode<Type, TwingBaseTextNodeAttributes> {
export interface TwingBaseTextNode<Type extends string> extends TwingBaseNode<Type, TwingBaseTextNodeAttributes> {
}
export interface TwingTextNode extends TwingBaseTextNode<"text"> {
@ -18,7 +17,7 @@ export const createBaseTextNode = <Type extends string>(
column: number,
tag: string | null = null
): TwingBaseTextNode<Type> => {
const outputNode = createBaseOutputNode(type, {
const outputNode = createBaseNode(type, {
data
}, {}, line, column, tag);

View File

@ -3,8 +3,8 @@ import {TwingTagHandler, TwingTokenParser} from "./tag-handler";
import {TwingNodeVisitor} from "./node-visitor";
import {createParsingError, TwingParsingError} from "./error/parsing";
import {TwingBaseNode, createBaseNode, getChildren, TwingNode} from "./node";
import {createTextNode} from "./node/output/text";
import {createPrintNode} from "./node/output/print";
import {createTextNode} from "./node/text";
import {createPrintNode} from "./node/print";
import {TwingBaseExpressionNode, TwingExpressionNode} from "./node/expression";
import {createBodyNode} from "./node/body";
import {createTemplateNode, TwingTemplateNode} from "./node/template";
@ -265,10 +265,10 @@ export const createParser = (
// checks that the node only contains "constant" elements
const checkConstantExpression = (stackEntry: TwingTokenStream, node: TwingBaseNode): TwingBaseNode | null => {
if (!(
(node as TwingNode).type === "constant"
|| (node as TwingNode).type === "array"
|| (node as TwingNode).type === "hash"
|| (node as TwingNode).type === "negative"
(node as TwingNode).type === "constant"
|| (node as TwingNode).type === "array"
|| (node as TwingNode).type === "hash"
|| (node as TwingNode).type === "negative"
|| (node as TwingNode).type === "positive"
)) {
return node;
@ -292,7 +292,7 @@ export const createParser = (
const filterChildBodyNode = (stream: TwingTokenStream, node: TwingBaseNode, nested: boolean = false): TwingBaseNode | null => {
// non-empty text nodes are not allowed as direct child of a
const testedNode = node as TwingNode;
if (testedNode.type === "text" && !isMadeOfWhitespaceOnly(testedNode.attributes.data)) {
const {data} = testedNode.attributes;
@ -312,21 +312,22 @@ export const createParser = (
);
}
const {type} = (node as TwingNode);
// bypass nodes that "capture" the output
if (node.isACaptureNode) {
// a "block" tag in such a node will serve as a block definition AND be displayed in place as well
if (type === "set") {
return node;
}
// to be removed completely in Twig 3.0
if (!nested && (node.type === "spaceless")) {
if (!nested && (type === "spaceless")) {
console.warn(`Using the spaceless tag at the root level of a child template in "${stream.source.name}" at line ${node.line} is deprecated since Twig 2.5.0 and will become a syntax error in Twig 3.0.`);
}
// "block" tags that are not capturing (see above) are only used for defining
// the content of the block. In such a case, nesting it does not work as
// expected as the definition is not part of the default template code flow.
if (nested && (node.type === "block_reference")) {
if (nested && (type === "block_reference")) {
if (level >= 3) {
throw createParsingError(`A block definition cannot be nested under non-capturing nodes.`, node, stream.source.name);
}
@ -336,17 +337,18 @@ export const createParser = (
return null;
}
}
if (node.isAnOutputNode && (node.type !== "spaceless")) {
if (type === "block_reference" || type === "print" || type === "text") {
return null;
}
// here, nested means "being at the root level of a child template"
// we need to discard the wrapping node for the "body" node
nested = nested || (node.type !== "wrapper");
nested = nested || (type !== "wrapper");
for (const [key, child] of getChildren(node)) {
if (child !== null && (filterChildBodyNode(stream, child, nested) === null)) {
delete (node.children as any)[key];
}
}
@ -895,7 +897,7 @@ export const createParser = (
break;
}
}
return createWrapperNode(targets, line, column);
};

View File

@ -1,8 +1,8 @@
import {createBaseNode} from "../node";
import {createParsingError} from "../error/parsing";
import {createBlockNode} from "../node/block";
import {createPrintNode} from "../node/output/print";
import {createBlockReferenceNode} from "../node/output/block-reference";
import {createPrintNode} from "../node/print";
import {createBlockReferenceNode} from "../node/block-reference";
import {Token} from "twig-lexer";
import {TwingTagHandler} from "../tag-handler";

View File

@ -1,7 +1,7 @@
import {createBlockFunctionNode} from "../node/expression/block-function";
import {createConstantNode} from "../node/expression/constant";
import {createBlockNode} from "../node/block";
import {createPrintNode} from "../node/output/print";
import {createPrintNode} from "../node/print";
import {Token} from "twig-lexer";
import {TwingTagHandler} from "../tag-handler";

View File

@ -1,4 +1,4 @@
import {createSpacelessNode} from "../node/output/spaceless";
import {createSpacelessNode} from "../node/spaceless";
import {Token} from "twig-lexer";
import {TwingTagHandler} from "../tag-handler";

View File

@ -1,4 +1,4 @@
import {createVerbatimNode} from "../node/output/verbatim";
import {createVerbatimNode} from "../node/verbatim";
import {Token} from "twig-lexer";
import {TwingTagHandler} from "../tag-handler";
import {TwingNode} from "../node";

View File

@ -1,7 +1,7 @@
import * as tape from 'tape';
import {SinonStub, stub} from 'sinon';
import {createEnvironment, TwingEnvironmentOptions} from "../../../src/lib/environment";
import {createPrintNode} from "../../../src/lib/node/output/print";
import {createPrintNode} from "../../../src/lib/node/print";
import {createConstantNode} from "../../../src/lib/node/expression/constant";
import {TwingExtension} from "../../../src/lib/extension";
import {createFilter} from "../../../src/lib/filter";