codeconsole opened a new pull request, #16144:
URL: https://github.com/apache/grails-core/pull/16144

   ### Summary
   
   A backslash immediately before a GSP expression now escapes it: `\${...}` 
renders a literal `${...}` with the backslash removed, instead of the 
expression being evaluated on the server.
   
   GSP previously had no escape syntax at all — `\${x}` rendered a stray `\` 
followed by the evaluated expression. The motivating case is JavaScript 
template literals, whose interpolation placeholders share the `${}` syntax and 
are today either evaluated against the page binding (an unbound name silently 
renders as empty) or blow up the whole page render:
   
   ```html
   <script>
       const name = 'Grails';
       console.log(`Hello \${name}`);
   </script>
   ```
   
   The browser now receives the template literal with its `${name}` placeholder 
intact, to be interpolated by JavaScript.
   
   ### Implementation
   
   Escaping is resolved entirely at parse time, in two small pieces:
   
   - `GroovyPageScanner` no longer starts an expression token when the `${` is 
immediately preceded by a backslash in template text — the sequence stays in 
the surrounding HTML token.
   - `GroovyPageParser.html()` rewrites `\${` to `${` when writing the HTML 
token out.
   
   Because this happens before code generation, behavior is identical in 
development, production, and `compileStatic` modes, and escaped text is 
invisible to static type checking — an escaped `${undeclaredName}` under `<%@ 
page compileStatic="true" %>` neither fails compilation nor evaluates (covered 
by a new test in `GspCompileStaticSpec`).
   
   ### Semantics
   
   - Only the backslash performing the escape is removed: `\\${x}` renders 
`\${x}`.
   - Each expression is escaped individually.
   - Expressions in the attributes of GSP tags are scanned in a different lexer 
state and cannot be escaped this way.
   
   ### Also in this PR
   
   `grails-gsp-core` declared `junit-jupiter-api` but never the Jupiter engine, 
so the JUnit Platform silently skipped every Jupiter-annotated test class in 
the module — all of `GroovyPagesTemplateEngineTests` (23 tests) never ran. 
Added the repo-standard `testRuntimeOnly 
'org.junit.jupiter:junit-jupiter-engine'`; all resurrected tests pass. The 
other engine-less modules with Jupiter tests (`grails-gsp/plugin`, 
`grails-gsp/grails-taglib`) receive the engine transitively and were already 
running them.
   
   ### Tests and docs
   
   - `ScanTests`: escaped `${` stays in the HTML token; unescaped expressions 
still tokenize; mixed escaped/unescaped side by side.
   - `GroovyPagesTemplateEngineTests`: literal render, escaped next to 
evaluated, JavaScript template literal end-to-end, double-backslash semantics.
   - `GspCompileStaticSpec`: escaped expressions are invisible to static 
compilation.
   - Guide: escaping documented in the GSP Basics "Expressions" section with 
the JavaScript template literal example.
   
   Full `:grails-gsp-core:test` and `:grails-gsp:test` suites plus downstream 
`:grails-taglib`, `:grails-sitemesh3`, `:grails-layout`, and `:grails-web-gsp` 
all pass; `codeStyle` is clean on the touched modules.
   
   https://claude.ai/code/session_01WtCxMe9pfmy7tiHCUr5THN
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to