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

   ### Summary
   
   GSP has been able to compile pages statically for a long time, but the way 
to ask for it across an application was undocumented, untested, and failed on 
ordinary correct GSP. This makes it something an application can actually turn 
on.
   
   ### Compiling every page statically
   
   The `grails.views.gsp.compileStatic` setting already existed and was already 
read by both the build's page compiler and the application, but appeared 
nowhere in the guide and had no test covering it — every existing case drove 
static compilation from the per-page directive:
   
   ```yaml
   grails:
       views:
           gsp:
               compileStatic: true
   ```
   
   The same thing can now be asked for from the build, alongside the artefact 
opt-ins:
   
   ```groovy
   grails {
       compileStatic {
           gsp = true
       }
   }
   ```
   
   This states the setting that already exists rather than introducing a name 
of its own, as a system property, so it reaches both places a page is compiled 
— the forked compiler the build runs, and the JVM running the application, 
which compiles a page again when it changes. Reaching only the first would mean 
a page compiled one way while being developed and another when packaged.
   
   An individual page still decides for itself:
   
   ```html
   <%@ page compileStatic="false" %>
   ```
   
   ### Reading the names the framework supplies
   
   A statically compiled page resolved a name it had not declared through 
`getProperty`, typed `Object`, so this failed to compile:
   
   ```html
   ${params.id}
   ```
   ```
   [Static type checking] - No such property: id for class: java.lang.Object
   ```
   
   That is idiomatic GSP, and it applied equally to `flash`, `controllerName`, 
`actionName`, `namespace`, `grailsApplication` and `applicationContext` — names 
a page never declares because the framework binds them. They are now typed, so 
a page can read them without declaring anything:
   
   ```html
   <%@ page compileStatic="true" %>
   <g:if test="${params.id}">${flash.message} on 
${controllerName}/${actionName}</g:if>
   ```
   
   `params` and `flash` are typed `Map`, which is what lets the type checker 
turn `params.id` into a `get()`. Each delegates to the same binding lookup 
dynamic resolution uses, so a page renders identically and reads `null` where 
it is rendered outside a web request.
   
   ### Holding an artefact type back from `all`
   
   `compileStatic { all = true; services = false }` enabled services anyway: 
the shortcut was folded in as `all || services`, and every flag carried a 
convention of `false`, so a type set to `false` could not be told apart from 
one never set. An artefact type now falls back to `all` rather than being OR-ed 
with it, so a value stated for a type is the value used:
   
   ```groovy
   grails {
       compileStatic {
           all = true
           services = false   // every artefact type except services
       }
   }
   ```
   
   ### Limitations
   
   - **`gsp` is not included in `all`, deliberately.** The artefact opt-ins 
fail on code that is doubtful anyway. This one fails on any page reading a 
model variable it has not declared, which describes most pages in an 
application that has never declared one. Enabling it is a migration, not a 
switch, and it should not happen as a side effect of asking for everything.
   - **`request`, `response`, `session` and `application` are still not 
typed.** They are servlet types, and `grails-gsp-core` deliberately has no 
servlet dependency — pages render outside a servlet container too. A page can 
declare one in its `model` directive to read it, which the tests pin using a 
test-only servlet-api dependency. Typing them properly means a servlet-aware 
page base class in `grails-web-gsp` and selecting it by name rather than by 
class literal, which is more indirection than four names justify on their own.
   - **`<g:set var="x" .../>` followed by `${x}` does not compile.** `g:set` 
declares nothing the type checker can see; `<g:def type="..." var="..." 
value="${...}"/>` is the typed form. Unchanged here, and worth addressing 
separately.
   - **The build option outranks `application.yml`** rather than acting as a 
default under it, matching the order a system property takes over configuration 
in a running application. The alternative — the build and the running 
application disagreeing about precedence — is worse. It does mean a project 
setting `gsp = true` in the build cannot turn it off per-environment in 
configuration. Set it in one place or the other.
   - **Templates.** A `_foo.gsp` rendered via `<g:render template="foo" 
model="[...]"/>` takes its model from the caller, so it has to declare every 
attribute it may be passed, and nothing checks that the caller and the template 
agree.
   
   ### Relationship to #16134
   
   #16134 resolves tag calls at compile time, and overlaps with this in one 
place. Its change to `GroovyPageTypeCheckingExtension` seeds the allowed 
namespaces from the compiled tag library index:
   
   ```groovy
   currentScope.allowedTagLibs.addAll(tagLibraryIndex.namespaces)
   currentScope.allowedTagLibs.addAll(tagLibraryIndex.dynamicNamespaces)
   ```
   
   Once that merges:
   
   - **`grails.views.gsp.compileStaticConfig.taglibs` and the `taglibs` page 
directive stop being needed** for any tag library on the compile classpath — 
its namespaces are known rather than declared. The section documenting them 
here should gain a note saying so; they remain meaningful only for namespaces 
filled in while the application runs.
   - **A misspelled tag becomes a compile error** under that PR's `strictTags`, 
rather than a runtime failure — a strictly better guarantee than the allowlist 
this documents.
   - Nothing else here changes. #16134 touches tag dispatch only: its 
`GroovyPage` change is `getTagLibraryLookup()` and `methodMissing`, 
`resolveProperty` is untouched, and `<g:set>` is listed under its own "what is 
not rewritten".
   
   Two files are touched by both and will need merging whichever lands second — 
`GrailsCompileStaticOptions` (that PR adds `strictTags` and 
`dynamicTagNamespaces` to the same block this adds `gsp` to, and this changes 
the conventions the existing flags carry) and `GroovyPagePlugin`.
   


-- 
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