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]