@huggingface/jinja
Advanced tools
Comparing version 0.3.2 to 0.3.3
@@ -23,5 +23,5 @@ /** | ||
constructor(template: string); | ||
render(items: Record<string, unknown>): string; | ||
render(items?: Record<string, unknown>): string; | ||
} | ||
export { Environment, Interpreter, tokenize, parse }; | ||
//# sourceMappingURL=index.d.ts.map |
{ | ||
"name": "@huggingface/jinja", | ||
"packageManager": "pnpm@8.10.5", | ||
"version": "0.3.2", | ||
"version": "0.3.3", | ||
"description": "A minimalistic JavaScript implementation of the Jinja templating engine, specifically designed for parsing and rendering ML chat templates.", | ||
@@ -43,3 +43,3 @@ "repository": "https://github.com/huggingface/huggingface.js.git", | ||
"@huggingface/transformers": "^3.0.0", | ||
"@huggingface/hub": "^0.18.2" | ||
"@huggingface/hub": "^1.0.0" | ||
}, | ||
@@ -46,0 +46,0 @@ "scripts": { |
@@ -34,3 +34,3 @@ /** | ||
render(items: Record<string, unknown>): string { | ||
render(items?: Record<string, unknown>): string { | ||
// Create a new environment for this template | ||
@@ -48,4 +48,6 @@ const env = new Environment(); | ||
// Add user-defined variables | ||
for (const [key, value] of Object.entries(items)) { | ||
env.set(key, value); | ||
if (items) { | ||
for (const [key, value] of Object.entries(items)) { | ||
env.set(key, value); | ||
} | ||
} | ||
@@ -52,0 +54,0 @@ |
@@ -346,3 +346,3 @@ import type { Token, TokenType } from "./lexer"; | ||
const member = parseMemberExpression(); // foo.x | ||
const member = parseMemberExpression(parsePrimaryExpression()); // foo.x | ||
@@ -356,11 +356,13 @@ if (is(TOKEN_TYPES.OpenParen)) { | ||
function parseCallExpression(callee: Statement): CallExpression { | ||
let callExpression = new CallExpression(callee, parseArgs()); | ||
function parseCallExpression(callee: Statement): Statement { | ||
let expression: Statement = new CallExpression(callee, parseArgs()); | ||
expression = parseMemberExpression(expression); // foo.x().y | ||
if (is(TOKEN_TYPES.OpenParen)) { | ||
// foo.x()() | ||
callExpression = parseCallExpression(callExpression); | ||
expression = parseCallExpression(expression); | ||
} | ||
return callExpression; | ||
return expression; | ||
} | ||
@@ -438,5 +440,3 @@ | ||
function parseMemberExpression(): Statement { | ||
let object = parsePrimaryExpression(); | ||
function parseMemberExpression(object: Statement): Statement { | ||
while (is(TOKEN_TYPES.Dot) || is(TOKEN_TYPES.OpenSquareBracket)) { | ||
@@ -443,0 +443,0 @@ const operator = tokens[current]; // . or [ |
@@ -120,2 +120,44 @@ import type { | ||
], | ||
[ | ||
"split", | ||
// follows Python's `str.split(sep=None, maxsplit=-1)` function behavior | ||
// https://docs.python.org/3.13/library/stdtypes.html#str.split | ||
new FunctionValue((args) => { | ||
const sep = args[0] ?? new NullValue(); | ||
if (!(sep instanceof StringValue || sep instanceof NullValue)) { | ||
throw new Error("sep argument must be a string or null"); | ||
} | ||
const maxsplit = args[1] ?? new NumericValue(-1); | ||
if (!(maxsplit instanceof NumericValue)) { | ||
throw new Error("maxsplit argument must be a number"); | ||
} | ||
let result = []; | ||
if (sep instanceof NullValue) { | ||
// If sep is not specified or is None, runs of consecutive whitespace are regarded as a single separator, and the | ||
// result will contain no empty strings at the start or end if the string has leading or trailing whitespace. | ||
// Trailing whitespace may be present when maxsplit is specified and there aren't sufficient matches in the string. | ||
const text = this.value.trimStart(); | ||
for (const { 0: match, index } of text.matchAll(/\S+/g)) { | ||
if (maxsplit.value !== -1 && result.length >= maxsplit.value && index !== undefined) { | ||
result.push(match + text.slice(index + match.length)); | ||
break; | ||
} | ||
result.push(match); | ||
} | ||
} else { | ||
// If sep is specified, consecutive delimiters are not grouped together and are deemed to delimit empty strings. | ||
if (sep.value === "") { | ||
throw new Error("empty separator"); | ||
} | ||
result = this.value.split(sep.value); | ||
if (maxsplit.value !== -1 && result.length > maxsplit.value) { | ||
// Follow Python's behavior: If maxsplit is given, at most maxsplit splits are done, | ||
// with any remaining text returned as the final element of the list. | ||
result.push(result.splice(maxsplit.value).join(sep.value)); | ||
} | ||
} | ||
return new ArrayValue(result.map((part) => new StringValue(part))); | ||
}), | ||
], | ||
]); | ||
@@ -547,2 +589,4 @@ } | ||
); | ||
case "join": | ||
return new StringValue(operand.value.map((x) => x.value).join("")); | ||
default: | ||
@@ -575,2 +619,3 @@ throw new Error(`Unknown ArrayValue filter: ${filter.value}`); | ||
); | ||
case "join": | ||
case "string": | ||
@@ -616,2 +661,20 @@ return operand; // no-op | ||
return new StringValue(toJSON(operand, indent.value)); | ||
} else if (filterName === "join") { | ||
let value; | ||
if (operand instanceof StringValue) { | ||
// NOTE: string.split('') breaks for unicode characters | ||
value = Array.from(operand.value); | ||
} else if (operand instanceof ArrayValue) { | ||
value = operand.value.map((x) => x.value); | ||
} else { | ||
throw new Error(`Cannot apply filter "${filterName}" to type: ${operand.type}`); | ||
} | ||
const [args, kwargs] = this.evaluateArguments(filter.args, environment); | ||
const separator = args.at(0) ?? kwargs.get("separator") ?? new StringValue(""); | ||
if (!(separator instanceof StringValue)) { | ||
throw new Error("separator must be a string"); | ||
} | ||
return new StringValue(value.join(separator.value)); | ||
} | ||
@@ -618,0 +681,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
237852
6604