tindzk / seed

Build tool for Scala projects
https://tindzk.github.io/seed/
Apache License 2.0
236 stars 13 forks source link

How would one compile Twirl templates? #48

Open nafg opened 5 years ago

nafg commented 5 years ago

I think that what needs to happen is for something to pull in https://mvnrepository.com/artifact/com.typesafe.play/twirl-compiler_2.12/1.4.2 and call https://github.com/playframework/twirl/blob/master/compiler/src/main/scala/play/twirl/compiler/TwirlCompiler.scala#L177 ?

tindzk commented 5 years ago

You could write a small wrapper class that calls TwirlCompiler.compile(). Then, you can reference it in your build as follows:

[module.twirl.jvm]
root = "twirl"
sources = "twirl/src/"
scalaDeps = [["com.typesafe.play", "twirl-compiler", "1.4.2"]]

[module.templates.target.html]
root  = "templates"
class = { module = "twirl:jvm", main = "twirl.TwirlWrapper" }
await = true
nafg commented 5 years ago

I suppose to use it for multiple modules I would have to make its behavior dependent on $BUILD_PATH -- there isn't any other way to parameterize it is there? Although in my case that might suffice, have to see.

On Tue, Aug 20, 2019, 5:35 AM Tim Nieradzik notifications@github.com wrote:

You could write a small wrapper class that calls TwirlCompiler.compile(). Then, you can reference it in your build as follows:

[module.twirl.jvm]root = "twirl"sources = "twirl/src/"scalaDeps = [["com.typesafe.play", "twirl-compiler", "1.4.2"]]

[module.templates.target.html]root = "templates"class = { module = "twirl:jvm", main = "twirl.TwirlWrapper" }

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/tindzk/seed/issues/48?email_source=notifications&email_token=AAAYAUH4DLJWC4OCWQLCLLTQFO3G3A5CNFSM4INQ5FA2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD4VV6GI#issuecomment-522936089, or mute the thread https://github.com/notifications/unsubscribe-auth/AAAYAUBEPWRWBAO4K3NYTIDQFO3G3ANCNFSM4INQ5FAQ .

nafg commented 5 years ago

Also, where do I want the generated sources to be placed and how do I depend on them?

On Tue, Aug 20, 2019 at 1:15 PM Naftoli Gugenheim naftoligug@gmail.com wrote:

I suppose to use it for multiple modules I would have to make its behavior dependent on $BUILD_PATH -- there isn't any other way to parameterize it is there? Although in my case that might suffice, have to see.

On Tue, Aug 20, 2019, 5:35 AM Tim Nieradzik notifications@github.com wrote:

You could write a small wrapper class that calls TwirlCompiler.compile(). Then, you can reference it in your build as follows:

[module.twirl.jvm]root = "twirl"sources = "twirl/src/"scalaDeps = [["com.typesafe.play", "twirl-compiler", "1.4.2"]]

[module.templates.target.html]root = "templates"class = { module = "twirl:jvm", main = "twirl.TwirlWrapper" }

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/tindzk/seed/issues/48?email_source=notifications&email_token=AAAYAUH4DLJWC4OCWQLCLLTQFO3G3A5CNFSM4INQ5FA2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD4VV6GI#issuecomment-522936089, or mute the thread https://github.com/notifications/unsubscribe-auth/AAAYAUBEPWRWBAO4K3NYTIDQFO3G3ANCNFSM4INQ5FAQ .

nafg commented 5 years ago

I see there's also MODULE_PATH but it's also based on the root project. I don't see any way to get which module it's being invoked for (and its path).

tindzk commented 5 years ago

$MODULE_PATH is used for accessing or creating source files, whereas $BUILD_PATH is for generated assets (JavaScript, CSS) that your application needs during runtime.

$MODULE_PATH is based on the project the build target is part of, which is not necessarily the root project. For example, your build file might import another build from a different folder which contains a custom target.

I am not sure we can introduce an additional environment variable that points to the module path the target is being invoked for. The reason is that the source generator can be run in isolation:

seed build templates:html

You can have a look at this example which is similar to what you are trying to do. The generator script is part of utils. It creates a Scala file in the source directory of the demo module. You could do the same and exclude the generated files in your .gitignore.

nafg commented 5 years ago

I saw that, but this is not a realistic approach when I have multiple modules with twirl templates.

tindzk commented 5 years ago

I see. We could change the semantics of the $MODULE_PATH environment variable such that if we have the following build target:

[module.templates.target.html]
root  = "templates"
class = { module = "twirl:jvm", main = "twirl.TwirlWrapper" }
await = true

$MODULE_PATH would point to the module's root path. If the root path is not set, the $MODULE_PATH environment variable would not be defined either.

Additionally, we can introduce $PROJECT_PATH that will inherit the current behaviour of $MODULE_PATH.

nafg commented 5 years ago

Okay let's say we do that...

Now TwirlWrapper has to find twirl templates in some subdirectory and produce the generated Scala sources in some other subdirectory. Are those going to be hardcoded in TwirlWrapper? That would really be a maintenance issue.

Also I really don't want to see a bunch of .template.scala files alongside my own sources, even if that suffix is .gitignore'd. Is there any chance of adding a concept of generated sources directory?

On Thu, Aug 22, 2019, 6:22 AM Tim Nieradzik notifications@github.com wrote:

I see. We could change the semantics of the $MODULE_PATH environment variable such that if we have the following build target:

[module.templates.target.html]root = "templates"class = { module = "twirl:jvm", main = "twirl.TwirlWrapper" }await = true

$MODULE_PATH would point to the module's root path. If the root path is not set, the $MODULE_PATH environment variable would not be defined either.

Additionally, we can introduce $PROJECT_PATH that will inherit the current behaviour of $MODULE_PATH.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/tindzk/seed/issues/48?email_source=notifications&email_token=AAAYAUFAPWZG7K5LUGEXTH3QFZSHVA5CNFSM4INQ5FA2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD44TU6Q#issuecomment-523844218, or mute the thread https://github.com/notifications/unsubscribe-auth/AAAYAUAEIL2TDNDPZ4WXKYDQFZSHVANCNFSM4INQ5FAQ .

tindzk commented 5 years ago

No, you do not necessarily have to hard-code any paths in TwirlWrapper. It could compile all Twirl files in $MODULE_PATH/ to $MODULE_PATH/generated.

I am not sure how useful the notion of generated sources would be since you can already pass in multiple paths to the sources setting. Here is a full example of how I envision it to work:

[module.shop.jvm]
sources = ["shop/src/", "shop/twirl/generated/"]

[module.shop.target.html]
root = "shop/twirl/"
class = ["twirl:jvm", "twirl.TwirlWrapper"]
await = true

[module.dashboard.jvm]
sources = ["dashboard/src/", "dashboard/twirl/generated/"]

[module.dashboard.target.html]
root = "dashboard/twirl/"
class = ["twirl:jvm", "twirl.TwirlWrapper"]
await = true
nafg commented 5 years ago

Interesting. But I need to keep the sbt layout. So the root is not the twirl source directory. For instance views_common/src/main/twirl and output it under e.g. views_common/target/somewhere. I could hardcode "src/main/twirl" and "target/somewhere" into TwirlWrapper, or I could use the first as the target root but then not sure how to know where to put it without duplication something.

Maybe I just should expect duplication as the price of the build description not being Turing-complete?

On Thu, Aug 22, 2019, 9:27 AM Tim Nieradzik notifications@github.com wrote:

No, you do not necessarily have to hard-code any paths in TwirlWrapper. It could compile all Twirl files in $MODULE_PATH/twirl to $MODULE_PATH/twirl/generated.

I am not sure how useful the notion of generated sources would be since you can already pass in multiple paths to the sources setting. Here is a full example of how I envision it to work:

[module.shop.jvm]sources = ["shop/src/", "shop/twirl/generated/"]

[module.shop.target.html]root = "shop/twirl/"class = ["twirl:jvm", "twirl.TwirlWrapper"]await = true

[module.dashboard.jvm]sources = ["dashboard/src/", "dashboard/twirl/generated/"]

[module.dashboard.target.html]root = "dashboard/twirl/"class = ["twirl:jvm", "twirl.TwirlWrapper"]await = true

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/tindzk/seed/issues/48?email_source=notifications&email_token=AAAYAUERWWPR2J2YGQA6ATTQF2H4DA5CNFSM4INQ5FA2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD45CXGY#issuecomment-523905947, or mute the thread https://github.com/notifications/unsubscribe-auth/AAAYAUHW65YHS6C7AJX5HPLQF2H4DANCNFSM4INQ5FAQ .

tindzk commented 5 years ago

Note that in my example from above I only set the root path on the custom build target. You can additionally set it on the JVM module. Here is how your particular example could look like:

[module.viewsCommon.jvm]
root = "views_common/"
sources = ["views_common/src/main/scala/", "views_common/target/"]

[module.viewsCommon.target.html]
root = "views_common/src/main/twirl/"
class = ["twirl:jvm", "twirl.TwirlWrapper"]
await = true

If the paths of your modules follow the same convention, your wrapper class could compile $MODULE_PATH/ to $MODULE_PATH/../../target/.

nafg commented 4 years ago

This isn't working. I'm still not getting the path of the module that's invoking the class in MODULE_PATH.

nafg commented 4 years ago

I see that if I move the viewsCommon stuff into views_common/build.toml and include it from the main build.toml, then $MODULE_PATH gets set to views_common and I can proceed. In that way I got some twirl to compile. However it is annoying to have to do that.

nafg commented 4 years ago

How can I get --watch to include changes to templates without seed going and passing them to scalac?

tindzk commented 4 years ago

I see that if I move the viewsCommon stuff into views_common/build.toml and include it from the main build.toml, then $MODULE_PATH gets set to views_common and I can proceed. In that way I got some twirl to compile. However it is annoying to have to do that.

Yes, correct. I will introduce another environment variable that will refer to the path of the invoked module (if available).

How can I get --watch to include changes to templates without seed going and passing them to scalac?

I am not sure I understood you. Do you want to run your script to convert Twirl templates to Scala, but not compile the files afterwards?

nafg commented 4 years ago

Yes, correct. I will introduce another environment variable that will refer to the path of the invoked module (if available).

More broadly, it would be great if I could get the entire config somehow (e.g. see https://github.com/tindzk/seed/issues/72 bullet 4).

I am not sure I understood you. Do you want to run your script to convert Twirl templates to Scala, but not compile the files afterwards?

I mean if I make a change to a .scala.html file it should run the .target.twirl target, even though src/main/twirl isn't in the .jvm module's sources.

tindzk commented 4 years ago

I mean if I make a change to a .scala.html file it should run the .target.twirl target, even though src/main/twirl isn't in the .jvm module's sources.

I see. As you noticed, the --watch parameter is ignored on class-based build targets. We could set up a file watcher for all files in root or introduce a separate setting, e.g. watchPaths. Whenever a file is changed, it would re-run your Twirl code generator. The reason I was hesitant about implementing this was that the JVM incurs a startup penalty which would be undesirable in your use case. It would be more efficient to do the file watching from within your Twirl wrapper, e.g. using the directory-watcher library.

nafg commented 4 years ago

It would be more efficient to do the file watching from within your Twirl wrapper, e.g. using the directory-watcher library.

But then it would have to be async, and wouldn't that make Seed not know when to compile the resulting .template.scala files?

tindzk commented 4 years ago

Yes, it would have to be running in the background. Seed already sets up file watchers for all source paths. Since the viewsCommon module lists the target path for the generated Twirl templates (views_common/target/), Seed should detect any new or changed files, compile them and restart the application.