com-lihaoyi / cask Goto Github PK
View Code? Open in Web Editor NEWCask: a Scala HTTP micro-framework
Home Page: https://com-lihaoyi.github.io/cask/
License: Other
Cask: a Scala HTTP micro-framework
Home Page: https://com-lihaoyi.github.io/cask/
License: Other
To be clear - I'm unclear whether this is actually expected behaviour or not.
in "dev"
@cask.staticResource("assets")
def s() = "assets"
Works fine, where my html file has something like
<link rel = "stylesheet" href = "assets/css/todo.css"/>
However, once I "publish" (for me that looks like going through sbt stage, it's hosted somewhere I don't understand on AWS), and the environment changes to goes through https, some authentication wrapper etc...
the styles are not applied. I can see them being correctly loaded in the browser... but ... no style.
This appears to fix my problem... i.e. manually butcher some contents types into the resource routes
class myStaticResources(val path: String,
resourceRoot: ClassLoader = classOf[myStaticResources].getClassLoader,
headers: Seq[(String, String)] = Nil)
extends HttpEndpoint[String, Seq[String]]{
val methods = Seq("get")
type InputParser[T] = QueryParamReader[T]
override def subpath = true
def wrapFunction(ctx: Request, delegate: Delegate) = {
delegate(Map
()).map(t => {
println(ctx.remainingPathSegments.headOption)
val headersOut : Seq[(String, String)] = ctx.remainingPathSegments.headOption match {
case Some(s) if s.takeRight(2) == "js" => Seq(("Content-Type", "text/javascript"))
case Some(s) if s.takeRight(3) == "css" => Seq(("Content-Type", "text/css"))
case Some(s) if s.takeRight(4) == "html" => Seq(("Content-Type", "text/html"))
case _ => Nil
}
cask.model.StaticResource(StaticUtil.makePath(t, ctx), resourceRoot, headersOut)
}
)
}
def wrapPathSegment(s: String): Seq[String] = Seq(s)
}
@ myStaticResources("assets")
def s() = "assets"
But I have to say the behaviour was non-obvious to me... I didn't spot it in the docs - is there an existing decorator for this? It feels like something which should be "standard", but then perhaps there is complexity here I don0t understand. The underlying cause is the CSS being served as "text/plain" rather than "text/css"
Scala 2.11 release pleeease. Until Spark moves to 2.12, unfortunately necessary :(
SBT version is 1.8.0.
Here is build.sbt:
lazy val root = project
.in(file("."))
.settings(
scalaVersion := "3.2.1",
libraryDependencies ++= Seq(
"com.lihaoyi" %% "cask" % "0.8.3",
"com.lihaoyi" %% "os-lib" % "0.9.0",
)
)
The error:
[error] (update) found version conflict(s) in library dependencies; some are suspected to be binary incompatible:
[error]
[error] * com.lihaoyi:geny_3:1.0.0 (early-semver) is selected over {0.6.10, 0.7.1}
[error] +- com.lihaoyi:os-lib_3:0.9.0 (depends on 1.0.0)
[error] +- com.lihaoyi:upickle-core_3:1.6.0 (depends on 0.7.1)
[error] +- com.lihaoyi:cask-util_3:0.8.3 (depends on 0.6.10)
[error]
[error]
[error] this can be overridden using libraryDependencySchemes or evictionErrorLevel
There seems to be a typo in WebSocketEndpoint.scala. Sending a Pong message to a WsChannelActor, translates to a WebSockets.sendPingBlocking(...)
call on the underlying library. I think it should call sendPongBlocking instead.
This makes it impossible to implement a standard ping/pong keepalive.
cask.WsActor {
case cask.Ws.Ping(data) => channel.send(cask.Ws.Pong(data))
}
$ websocat -vv --ping-interval 3 ws://127.0.0.1:8080/connect
...
[INFO websocat::ws_client_peer] Connected to ws
[DEBUG websocat::ws_client_peer] Starting pinger
[INFO websocat::ws_peer] Sending WebSocket ping
[DEBUG websocat::ws_peer] incoming ping
[INFO websocat::ws_peer] Sending WebSocket ping
[DEBUG websocat::ws_peer] incoming ping
[INFO websocat::ws_peer] Sending WebSocket ping
[DEBUG websocat::ws_peer] incoming ping
[INFO websocat::ws_peer] Sending WebSocket ping
[DEBUG websocat::ws_peer] incoming ping
^C
The above looks wrong. The endpoint should respond to a ping with a pong, like this:
...
[INFO websocat::ws_client_peer] Connected to ws
[DEBUG websocat::ws_client_peer] Starting pinger
[INFO websocat::ws_peer] Sending WebSocket ping
[INFO websocat::ws_peer] Received a pong from websocket
[INFO websocat::ws_peer] Sending WebSocket ping
[INFO websocat::ws_peer] Received a pong from websocket
[INFO websocat::ws_peer] Sending WebSocket ping
[INFO websocat::ws_peer] Received a pong from websocket
[INFO websocat::ws_peer] Sending WebSocket ping
[INFO websocat::ws_peer] Received a pong from websocket
^C
PR incoming ;-)
The overloading based on query parameters is inconsistent. When checking routes for overlaps, the parameters are ignored, yet when serving the requests, the overloads are selected based on them.
Following routes are rejected as invalid.
@cask.get("/hello")
def hello() = {
"Hello World!"
}
@cask.get("/hello")
def helloName(name: String) = {
s"Hello ${name.reverse}"
}
The error is:
More than one endpoint has the same path: get /hello, get /hello
However when I remove the helloName
parameter, requests with the query parameter name
are not served.
I suggest query parameters are considered similar to path segments, as following similar routes are valid:
@cask.get("/hello")
def hello() = {
"Hello World!"
}
@cask.get("/hello/:name")
def helloName(name: String) = {
s"Hello ${name.reverse}"
}
As tested with Scala 3 and cask 0.7.11
Server definition
class ServerRoutes(using cask.Logger) extends cask.Routes:
@cask.get("/foo")
def foo() = "get foo!"
@cask.post("/bar")
def bar() = "post bar!"
initialize()
object server extends cask.Main:
val allRoutes = Seq(ServerRoutes())
Example queries with responses
> curl -D - 'localhost:8080/foo'
HTTP/1.1 200 OK
Connection: keep-alive
Content-Type: text/plain; charset=utf-8
Content-Length: 3
Date: Sun, 16 May 2021 19:57:13 GMT
get foo!
> curl -D - 'localhost:8080/bar'
HTTP/1.1 404 Not Found
Connection: keep-alive
Content-Type: text/plain; charset=utf-8
Content-Length: 20
Date: Sun, 16 May 2021 19:57:16 GMT
Error 404: Not Found
> curl -D - -XPUT 'localhost:8080/baz'
HTTP/1.1 405 Method Not Allowed
Connection: keep-alive
Content-Type: text/plain; charset=utf-8
Content-Length: 29
Date: Sun, 16 May 2021 19:57:24 GMT
Error 405: Method Not Allowed
Expected outcomes
Request -> response 1 is expected
Request -> response 2 should be 405 but is 404
Request -> response 3 should be 404 but is 405
First of all, thanks @lihaoyi for your work on this library. There have been discussions in my team whether we should drop Akka HTTP in favor of something more simple, and Cask looks very promising in this regard.
Before jumping into prototyping I have a question: The docs mention that cask intentionally does not support async as means to keep the library simple. I'm wondering, what does this mean exactly?
Currently, you cannot write an endpoint that takes a dynamic set of query parameters by manipulating req.queryParams
, as unknown query parameters are rejected during input validation. While that's a reasonable default, we should provide a flag to disable that validation so a developer can work with req.queryParams
fully dynamically if that's what they want
Can you apply the http
github tag to cask? It will help folks discover casks when searching through scala libraries on github.
It would be nice if cask had some built-in way of encrypting communications via TLS / HTTPS.
Sometimes it would be handy to allow any unknown query parameters.
Motivation 1:
One of my endpoints is used for GitHub OAuth loopback. While the behavior in case of success is well documented as receiving code
and state
parameters, in case of error the documentation is lacking. While I have experimentally determined the parameters in such case to be error
, error_description
and error_uri
, this is undocumented and perhaps could change anytime, causing my error handling not working as expected. Moreover. I am not really interested in some of them, but I cannot prevent them being included. At the moment I have to add them to the endpoint just to ignore them.
Motivation 2:
I am processing query parameters using request.queryParams
in my endpoint, yet I have to list all possible parameter names in my endpoint signature. This contrast with subPath
option available for paths. I can easily create an endpoint serving any subpath, but not any query parameters.
If i run the following file with ammonite (amm -w sdk_ui_server.sc
):
interp.load.ivy("com.lihaoyi" %% "cask" % "0.5.0")
@
object StaticFiles extends cask.MainRoutes{
@cask.staticFiles("/identity-ui.js")
def staticFileRoutes() = "/Users/guillaumebersac/projects/identity-web-ui-sdk/umd/identity-ui.js"
initialize()
}
I get the following error:
ompiling /Users/guillaumebersac/projects/sdk_ui_server.sc
java.lang.Exception: Failed to resolve ivy dependencies:Error downloading com.lihaoyi:cask_2.12:0.5.0
not found: /Users/guillaumebersac/.ivy2/local/com.lihaoyi/cask_2.12/0.5.0/ivys/ivy.xml
not found: https://repo1.maven.org/maven2/com/lihaoyi/cask_2.12/0.5.0/cask_2.12-0.5.0.pom
ammonite.interp.Interpreter$DefaultLoadJar.ivy(Interpreter.scala:663)
ammonite.$file.sdk_ui_server$.<init>(sdk_ui_server.sc:1)
ammonite.$file.sdk_ui_server$.<clinit>(sdk_ui_server.sc)
Note: if I replace the first line by interp.load.ivy("com.typesafe.akka" %% "akka-stream-kafka" % "0.22")
, it works as expected, so it looks like a cask related issue.
In https://github.com/siddhartha-gadgil/MillScalaServer (and also a larger project, which is what I wanted), serving from resources works fine with server.run
and server.runBackground
but fails on running the result of server.assembly
resources
folder behave the same way.Routes with optional query parameters return a 400 response when the query parameter is not provided. Below is a minimal repro example:
package app
import scala.annotation.nowarn
@nowarn
object MinimalApplication extends cask.MainRoutes {
@cask.get("/")
def hello(param: Option[String]) = {
"Hello World!"
}
initialize()
}
I ran this application and then ran the following in my terminal:
$ curl http://localhost:8080?param=asdf
Hello World!
$ curl http://localhost:8080
Missing argument: (param: Option[String])
Arguments provided did not match expected signature:
hello
param Option[String]
The expected behavior is that both curl
requests succeed.
Cask version 0.1.9 throws runtime exceptions when used with uPickle 0.7.1. Exception is:
ClassNotFoundException: uJson.Visitor
Hello,
just stumbled over that the build of the full Todo-Example in the docs is not working. Found the error: just a comma was missing in the build.sc
file.
Unfortunately I could not find where to change this and create a PR.
todo-0.5.6/build.sc
import mill._, scalalib._
object app extends ScalaModule{
def scalaVersion = "2.13.1"
def ivyDeps = Agg(
ivy"com.lihaoyi::cask:0.5.6" // <- here is a comma missing
ivy"org.xerial:sqlite-jdbc:3.18.0",
ivy"io.getquill::quill-jdbc:3.4.10",
ivy"com.lihaoyi::scalatags:0.8.4",
)
object test extends Tests{
def testFrameworks = Seq("utest.runner.Framework")
def ivyDeps = Agg(
ivy"com.lihaoyi::utest::0.7.3",
ivy"com.lihaoyi::requests::0.5.0",
)
}
}
Thanks.
Here's what I've found so far:
Main#defaultHandler
seems to be the way forward but this feels very "low-level" and introduces some concern-splittingI'm willing to do some legwork here but I'd like to know if you have any ideas on how this can best be solved.
This took me a while to find:
@cask.get("/get")
def hello(param: String = "Default") = {
s"Hello $param"
}
or:
@cask.get("/get")
def hello(param: Option[String] = None) = {
s"Hello $param"
}
This way the query parameter is optional. It may seem intuitive, but I have tried several different ways before trying this (e.g. just making param: Option[String]. or providing overloads of def hello).
I've been trying to set up distributed tracing for Cask, and found a lack of hooks I need to set things up.
A few requests (or help in docs):
If you're curious what we have, here's a simple library I threw together to give Cask the "best possible" OpenTelemetry HTTP experience: https://github.com/GoogleCloudPlatform/scala-o11y-cui-showcase/tree/main/utils/src/main/scala/com/google/example/o11y/cask
The meat of the implementation is all within the @traced decorator
The most recent version for scala 2.12 as of 2019-12-10 seems 0.2.9. Do you have any plan to update?
https://search.maven.org/artifact/com.lihaoyi/cask_2.12/0.2.9/jar
It would be very useful to expose undertow's setWorkerThreads() and setIoThreads() methods in cask.Main()
I've noticed that websocket endpoints always reject connections when the Upgrade
request header value is not exactly "websocket", all lowercase.
The WebSocket RFC specifies that the match should be case-insensitive:
https://www.rfc-editor.org/rfc/rfc6455#section-4.2.1
3. An |Upgrade| header field containing the value "websocket",
treated as an ASCII case-insensitive value.
So, i believe that the check in cask.main.Main.DefaultHandler is too strict
cask/cask/src/cask/main/Main.scala
Line 84 in d5fa6d4
This problem can be encountered in practice when the requests are going through Apache mod_proxy: even though the client specified the header as "Upgrade: websocket", for some reason mod_proxy decides that it would be cool to rewrite the header as WebSocket
in the downstream request, causing it to fail. Specifically, i noticed this behavior in Apache 2.4.6.
$ curl -v \
-H "Sec-WebSocket-Version: 13" \
-H "Sec-WebSocket-Key: atR+IRHDBUO+zMfMLfMKJA==" \
-H "Connection: keep-alive, Upgrade" \
-H "Upgrade: WebSocket" \
http://localhost:8080/connect/haoyi
(notice the capitalized "WebSocket" in the "Upgrade" request header)
> GET /connect/haoyi HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.85.0
> Accept: */*
> Sec-WebSocket-Version: 13
> Sec-WebSocket-Key: atR+IRHDBUO+zMfMLfMKJA==
> Connection: keep-alive, Upgrade
> Upgrade: WebSocket
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 405 Method Not Allowed
< Connection: keep-alive
< Content-Type: text/plain; charset=utf-8
< Content-Length: 29
< Date: Sun, 12 Mar 2023 20:44:09 GMT
<
Pull request follows shortly ;-)
cask currently depends on undertow
version "2.2.20.Final":
Line 61 in 53947e8
There is a GitHub security advisory on that version GHSA-pfcc-3g6r-8rg8. Please upgrade to version >= 2.2.24. Thanks!
macOS 10.13.6, unzipped minimalApplication-0.1.9 and cd-ed into folder, then,
$ ./cask -w app.runBackground
Error: Could not find or load main class Documents.Development.Scala.cask.minimalApplication-0.1.9.out.mill-cask
out/ folder contains mill-cask file and that's it, while
$ scala -version
Scala code runner version 2.12.2 -- Copyright 2002-2017, LAMP/EPFL and Lightbend, Inc.
thoughts?
Add TechEmpower benchmarks to see performance compared to undertow.
Hi all - is this project still maintained? I notice commits have slowed down and issues aren't being addressed.
Not trying to ruffle feathers(!) - I'm just trying to get the lay of the land.
Spawn a virtual thread for each incoming request, which would enable asynchronous behaviour and increase performance of the library. The new Java virtual threads are lightweight and can replace the traditional Java threads. Given that Cask does not have any strong async/concurrency model of its own, leveraging the new JVM feature would be really nice.
Haoyi: To incentivize contribution, I'm putting a 500USD bounty on resolving this ticket. This is payable via bank transfer, and at my discretion in case of ambiguity. The acceptance criteria is a PR implementing support for virtual threads, tests exercising them, and performance benchmarks (ad-hoc is OK) demonstrating a performance improvement (e.g. in high concurrency use cases) vs normal threads in Java 21+
Documentation in https://com-lihaoyi.github.io/cask/#variable-routes says:
you can set the flag subpath=true and ask for a : cask.Subpath
However no cask.Subpath
seems to exist, even the sample in the documentation uses request: cask.Request
.
I noticed GET, PUT, POST, and WebSocket requests are supported so far, any plans on supporting OPTIONS?
Latest version is 0.5.6, but the documentation references 0.5.2:
Cask is just a Scala library, and you can use Cask in any existing Scala project via the following coordinates:
// Mill
ivy"com.lihaoyi::cask:0.5.2"
// SBT
"com.lihaoyi" %% "cask" % "0.5.2"
It would be interesting to mention that Scala 2.13 is required.
Are these improvements being considered for implementation?
in "about" section
and in README (documentation)
we get a 404 page
Since #27 I believe, Main.DefaultHandler
throws a NoSuchElementException for any incoming request which uses a method that's not used in any route (this also oddly enough results in a 200 response).
The is the line that triggers the error: https://github.com/lihaoyi/cask/blob/4617b7e1f70eaa902b1aa492413a3a8ddc1491dd/cask/src/cask/main/Main.scala#L89
I think this could be resolved by adding a .withDefaultValue(DispatchTrie.empty)
to the map in Main.prepareRouteTries
, provided a static empty instance of DispatchTrie is possible to create. Another solution is to change L89 to a routeTries.get(effectiveMethod)
, but I believe that would add more overhead to each call and also make it easier for users to fall into the trap of missing this peculiarity if overriding the defaultHandler method on Main.
edit: A use-site workaround for now is to override the default routeTries
with
override def routeTries: Map[String,DispatchTrie[(Routes, EndpointMetadata[_])]] =
super.routeTries.withDefaultValue(DispatchTrie.construct(-1, Seq.empty))
Using cask 0.7.11 in a Scala 3 projects results in the following compilation error:
[error] Modules were resolved with conflicting cross-version suffixes in ProjectRef(uri("file:/home/vincenzo/Desktop/scala3-full-stack-example/"), "webserver"):
[error] com.lihaoyi:sourcecode _3, _2.13
The dependency tree shows that cask_3:0.7.11
depends on castor_2.13:0.1.7
which itself depends on sourcecode_2.13:0.2.1
:
[info] +-com.lihaoyi:cask_3:0.7.11
[info] | +-com.lihaoyi:cask-util_3:0.7.11
[info] | | +-com.lihaoyi:castor_2.13:0.1.7
[info] | | | +-com.lihaoyi:sourcecode_2.13:0.2.1
This could be fixed by upgrading cask-util
to depend on castor 1.8 which was published for Scala 3
add "com.lihaoyi" %% "cask" % "0.7.11" to libraryDependencies, sbt 1.5.5 failed when resolving:
error] Modules were resolved with conflicting cross-version suffixes in ProjectRef(uri("file:/D:/project/scala/exceltransformerdotty/"), "exceltransformerdotty"):
[error] com.lihaoyi:sourcecode _3, _2.13
[error] stack trace is suppressed; run 'last update' for the full output
[error] stack trace is suppressed; run 'last ssExtractDependencies' for the full output
[error] (update) Conflicting cross-version suffixes in: com.lihaoyi:sourcecode
[error] (ssExtractDependencies) Conflicting cross-version suffixes in: com.lihaoyi:sourcecode
this problem does not occur for
"com.lihaoyi" %% "pprint" % "0.6.6" ,
"com.lihaoyi" %% "requests" % "0.6.9",
@cask.get("/")
def getNonEnglishContent1() = "조선글"
@cask.get("/test1")
def getNonEnglishContent2() = "سىناك"
@cask.get("/test2")
def getNonEnglishContent3() = "שלום"
Browser content of non-English character changes to a garbled message.
Hi - I downloaded the example "TodoMVC Full Stack Web" example project following the link (https://github.com/lihaoyi/cask/releases/download/0.7.8/todo-0.7.8.zip) from https://com-lihaoyi.github.io/cask/#todomvc-api-server and then ran ./mill app.run
.
Visiting http://localhost:8080
gives:
I'm just reading the docs so I don't really know how to fix it, hence no PR.
Thanks!
Hi there - are there any plans to add authentication via oauth2?
Thanks!
Hi,
I'm trying to use case for a very simple website: a few web pages wrapping a simple REST API. My routes look like this:
@cask.getJson("/api/")
def api(exprType: ExprType, expr: String) = ???
@cask.staticFiles("/www/")
def sourcesStaticFileRoute() = STATIC_PATH
@cask.get("/")
def indexRedirect() = cask.Redirect("/www/index.html")
There are two problems with this approach:
/www/index.html
./www
instead of /
In Apache or nginx I would use a URL rewrite instead to rewrite /
to /www/
transparently except when the path starts with /api
. I have not found a way to do this in Cask. Hence, I have three questions:
/
route, call the staticFiles
route to get the contents of /www/index.html
?/
queries to /www
, except /api
?staticFiles
route that overlaps with other routes, to be used as a fallback when other routes don't match?The main blocker for me is the index.html
rewrite; I can live with the non-overlapping routes.
Thanks a lot!
I did not find session code in repo. Is there some better way? Like a community plugin?
Blocker : see Issue #11710 in GitHub project lampepfl/dotty
.
Hi there - thanks for this!
I notice that the decorators populate downstream with string based keys:
class withExtra extends cask.RawDecorator {
def wrapFunction(ctx: cask.Request, delegate: Delegate) = {
delegate(Map("extra" -> 31337))
}
}
@withExtra()
@cask.get("/hello/:world")
def hello(world: String)(extra: Int) = {
world + extra
}
Is there a way/a plan to make these type safe (enums for example)? My concern is that renaming the parameter name won't pick up the string key in the decorator's output, but this won't be picked up until runtime.
Thanks!
Hi, thank you for the great project. I ran into an issue, which prevents me from using twirl. Scalatags works without issues.
package app
object Twirl extends cask.MainRoutes{
@cask.get("/")
def hello() = {
"<!doctype html>" + html.hello("Hello World")
}
initialize()
}
This example from the docs does not work. It gets wrapped by additional HTML instead:
<html data-lt-installed="true"><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;"><!doctype html><html>
<body>
<h1>Hello World</h1>
<p>I am cow</p>
</body>
</html></pre></body></html>
I am not able to process even simple requests with query parameters.
Assume request http://localhost:8080/?param=hi
Following does not work:
object MinimalApplication extends cask.MainRoutes {
@cask.get("/")
def hello(request: cask.Request) = {
s"Hello"
}
initialize()
}
The error is:
Unknown argument: "param"
Arguments provided did not match expected signature:
hello
request cask.Request
Not knowing how to fix that, I have tried:
object MinimalApplication extends cask.MainRoutes {
@cask.get("/")
def hello(request: cask.Request)(param: String) = {
s"Hello"
}
initialize()
}
Now I get an error:
Unknown argument: "param"
Arguments provided did not match expected signature:
hello
param String
I did not find any documentation about query params. I do not see any annotation like @cask.Cookie
, maybe there is a way, but not documented?
Following code prints error when run:
object MinimalApplication extends cask.MainRoutes {
@cask.get("/root/x")
def hello() = {
"Hello World!"
}
@cask.get("/:path")
def doThing(path: String) = {
path.reverse
}
initialize()
}
The error is:
Routes overlap with wildcards: get /root/x, get /:path
When I replace the "/root/x" route with "/", the error disappears (this is how default MinimalApplication
looks like).
Is there a best practice to composite multiple "controllers" into one controller?
Hello,
tried to get the cask build repl running by issuing ./mill -i
on linux ubuntu 19.04 bash, scalac vesion 2.11.12
.
Any ideas what's wrong? (It's a plain git clone from this repo here).
Thx.
$ ./mill -i
Loading...
Compiling /home/hape/cask/build.sc
build.sc:133: object ci is not a member of package ammonite.$file
def publishVersion = T.input($file.ci.version.publishVersion)
^
build.sc:145: type mismatch;
found : Any
required: ujson.Value
"name" -> releaseTag
^
build.sc:153: object example is not a member of package ammonite.$file
$file.example.compress.build.millSourcePath,
^
build.sc:154: object example is not a member of package ammonite.$file
$file.example.compress2.build.millSourcePath,
^
build.sc:155: object example is not a member of package ammonite.$file
$file.example.compress3.build.millSourcePath,
^
build.sc:156: object example is not a member of package ammonite.$file
$file.example.cookies.build.millSourcePath,
^
build.sc:157: object example is not a member of package ammonite.$file
$file.example.decorated.build.millSourcePath,
^
build.sc:158: object example is not a member of package ammonite.$file
$file.example.decorated2.build.millSourcePath,
^
build.sc:159: object example is not a member of package ammonite.$file
$file.example.endpoints.build.millSourcePath,
^
build.sc:160: object example is not a member of package ammonite.$file
$file.example.formJsonPost.build.millSourcePath,
^
build.sc:161: object example is not a member of package ammonite.$file
$file.example.httpMethods.build.millSourcePath,
^
build.sc:162: object example is not a member of package ammonite.$file
$file.example.minimalApplication.build.millSourcePath,
^
build.sc:163: object example is not a member of package ammonite.$file
$file.example.minimalApplication2.build.millSourcePath,
^
build.sc:164: object example is not a member of package ammonite.$file
$file.example.redirectAbort.build.millSourcePath,
^
build.sc:165: object example is not a member of package ammonite.$file
$file.example.scalatags.build.millSourcePath,
^
build.sc:166: object example is not a member of package ammonite.$file
$file.example.staticFiles.build.millSourcePath,
^
build.sc:167: object example is not a member of package ammonite.$file
$file.example.staticFiles2.build.millSourcePath,
^
build.sc:168: object example is not a member of package ammonite.$file
$file.example.todo.build.millSourcePath,
^
build.sc:169: object example is not a member of package ammonite.$file
$file.example.todoApi.build.millSourcePath,
^
build.sc:170: object example is not a member of package ammonite.$file
$file.example.todoDb.build.millSourcePath,
^
build.sc:171: object example is not a member of package ammonite.$file
$file.example.twirl.build.millSourcePath,
^
build.sc:172: object example is not a member of package ammonite.$file
$file.example.variableRoutes.build.millSourcePath,
^
build.sc:173: object example is not a member of package ammonite.$file
$file.example.websockets.build.millSourcePath,
^
build.sc:174: object example is not a member of package ammonite.$file
$file.example.websockets2.build.millSourcePath,
^
build.sc:175: object example is not a member of package ammonite.$file
$file.example.websockets3.build.millSourcePath,
^
build.sc:176: object example is not a member of package ammonite.$file
$file.example.websockets4.build.millSourcePath,
^
build.sc:134: object ci is not a member of package ammonite.$file
def gitHead = T.input($file.ci.version.gitHead)
^
build.sc:130: object example is not a member of package ammonite.$file
object websockets4 extends $file.example.websockets4.build.AppModule with LocalModule
^
build.sc:129: object example is not a member of package ammonite.$file
object websockets3 extends $file.example.websockets3.build.AppModule with LocalModule
^
build.sc:128: object example is not a member of package ammonite.$file
object websockets2 extends $file.example.websockets2.build.AppModule with LocalModule
^
build.sc:127: object example is not a member of package ammonite.$file
object websockets extends $file.example.websockets.build.AppModule with LocalModule
^
build.sc:126: object example is not a member of package ammonite.$file
object variableRoutes extends $file.example.variableRoutes.build.AppModule with LocalModule
^
build.sc:125: object example is not a member of package ammonite.$file
object twirl extends $file.example.twirl.build.AppModule with LocalModule
^
build.sc:124: object example is not a member of package ammonite.$file
object todoDb extends $file.example.todoDb.build.AppModule with LocalModule
^
build.sc:123: object example is not a member of package ammonite.$file
object todoApi extends $file.example.todoApi.build.AppModule with LocalModule
^
build.sc:122: object example is not a member of package ammonite.$file
object todo extends $file.example.todo.build.AppModule with LocalModule
^
build.sc:121: object example is not a member of package ammonite.$file
object staticFiles2 extends $file.example.staticFiles2.build.AppModule with LocalModule
^
build.sc:120: object example is not a member of package ammonite.$file
object staticFiles extends $file.example.staticFiles.build.AppModule with LocalModule
^
build.sc:119: object example is not a member of package ammonite.$file
object scalatags extends $file.example.scalatags.build.AppModule with LocalModule
^
build.sc:118: object example is not a member of package ammonite.$file
object redirectAbort extends $file.example.redirectAbort.build.AppModule with LocalModule
^
build.sc:117: object example is not a member of package ammonite.$file
object minimalApplication2 extends $file.example.minimalApplication2.build.AppModule with LocalModule
^
build.sc:116: object example is not a member of package ammonite.$file
object minimalApplication extends $file.example.minimalApplication.build.AppModule with LocalModule
^
build.sc:115: object example is not a member of package ammonite.$file
object httpMethods extends $file.example.httpMethods.build.AppModule with LocalModule
^
build.sc:114: object example is not a member of package ammonite.$file
object formJsonPost extends $file.example.formJsonPost.build.AppModule with LocalModule
^
build.sc:113: object example is not a member of package ammonite.$file
object endpoints extends $file.example.endpoints.build.AppModule with LocalModule
^
build.sc:112: object example is not a member of package ammonite.$file
object decorated2 extends $file.example.decorated2.build.AppModule with LocalModule
^
build.sc:111: object example is not a member of package ammonite.$file
object decorated extends $file.example.decorated.build.AppModule with LocalModule
^
build.sc:110: object example is not a member of package ammonite.$file
object cookies extends $file.example.cookies.build.AppModule with LocalModule
^
build.sc:109: object example is not a member of package ammonite.$file
object compress3 extends $file.example.compress3.build.AppModule with LocalModule
^
build.sc:108: object example is not a member of package ammonite.$file
object compress2 extends $file.example.compress2.build.AppModule with LocalModule
^
build.sc:107: object example is not a member of package ammonite.$file
object compress extends $file.example.compress.build.AppModule with LocalModule
^
build.sc:32: `T.command` definitions must have 1 parameter list
def publishVersion = build.publishVersion()._2
^
Compilation Failed
I am on Scala 2.13 and I like to enable compiler warnings on non-exhaustive pattern matching but when I enable it, the following piece of code:
@cask.get(s"/app/:app")
def routeApp(app: String) =
s"Hello $app!"
@cask.staticFiles("/js/")
def scalaJs() =
"web/target/scala-2.13/web-fastopt/"
spits these warnings:
[info] compiling 1 Scala source to target/scala-2.13/classes ...
[error] Router.scala:25:7: match may not be exhaustive.
[error] def routeApi(req: cask.Request) =
[error] ^
[error] Router.scala:16:7: match may not be exhaustive.
[error] It would fail on the following input: (x: Seq[?] forSome x not in Nil)
[error] def scalaJs() =
[error] ^
I cannot myself add @unchecked
anywhere because the code is being generated by macro expansion. So perhaps in the macro expansion itself, can we add the @unchecked
annotation?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.