@@ -437,16 +437,45 @@ export function processExpressions(template: string, context: Record<string, any
437437
438438 // Replace {{ expr }} with escaped expressions
439439 output = output . replace ( / \{ \{ ( [ \s \S ] * ?) \} \} / g, ( match , expr , offset ) => {
440+ const trimmedExpr = expr . trim ( )
441+
442+ // Skip common JS built-ins that should be evaluated server-side
443+ const jsBuiltins = [ 'parseInt' , 'parseFloat' , 'String' , 'Number' , 'Boolean' , 'Array' , 'Object' , 'JSON' , 'Math' , 'Date' , 'encodeURIComponent' , 'decodeURIComponent' , 'encodeURI' , 'decodeURI' , 'true' , 'false' , 'null' , 'undefined' ]
444+
445+ // First, check if the expression starts with a known variable in context
446+ // This handles cases like `items.filter(...)` where `items` is in context
447+ const identifierPattern = / ^ ( [ a - z A - Z _ $ ] [ a - z A - Z 0 - 9 _ $ ] * ) /
448+ const identifierMatch = trimmedExpr . match ( identifierPattern )
449+ const firstVarName = identifierMatch ?. [ 1 ]
450+ const firstVarInContext = firstVarName && ( firstVarName in context || jsBuiltins . includes ( firstVarName ) )
451+
452+ // Detect if this looks like a client-side signal expression
453+ // Signals are typically function calls like loading(), sessions().length, etc.
454+ // Only preserve for client-side if it's a direct function call that's not in context
455+ const directFunctionCall = / ^ ( [ a - z A - Z _ $ ] [ a - z A - Z 0 - 9 _ $ ] * ) \s * \( /
456+ const directFuncMatch = trimmedExpr . match ( directFunctionCall )
457+ const isLikelySignal = directFuncMatch && ! jsBuiltins . includes ( directFuncMatch [ 1 ] ) && ! ( directFuncMatch [ 1 ] in context )
458+
459+ // Preserve signal-like expressions for client-side processing
460+ if ( isLikelySignal ) {
461+ return match
462+ }
463+
440464 try {
441465 const value = evaluateExpression ( expr , context )
442466 // Escape HTML for security
443467 return value !== undefined && value !== null ? escapeHtml ( String ( value ) ) : ''
444468 }
445469 catch ( error : unknown ) {
470+ // If evaluation fails and it looks like a client-side signal,
471+ // preserve the expression for client-side processing
472+ if ( isLikelySignal ) {
473+ return match
474+ }
446475 const msg = error instanceof Error ? error . message : String ( error )
447476 return createDetailedErrorMessage (
448477 'Expression' ,
449- `Error evaluating: {{ ${ expr . trim ( ) } }}: ${ msg } ` ,
478+ `Error evaluating: {{ ${ trimmedExpr } }}: ${ msg } ` ,
450479 filePath ,
451480 template ,
452481 offset ,
0 commit comments