From 8ddf2bcdc18806fc7bd58d94922842aacf587b4c Mon Sep 17 00:00:00 2001 From: Zak Patterson Date: Fri, 1 Feb 2019 11:21:07 -0500 Subject: [PATCH] Add initial url parsing and pattern matching Credit to http4s everywhere. --- .gitignore | 142 +++++++++++ .scalafmt.conf | 23 ++ .travis.yml | 33 +++ README.md | 8 + build.sbt | 77 ++++++ docs/Intro.md | 43 ++++ .../src/main/scala/outwatch/router/Path.scala | 220 ++++++++++++++++++ .../main/scala/outwatch/router/State.scala | 9 + project/Dependencies.scala | 84 +++++++ project/Options.scala | 58 +++++ project/Version.scala | 4 + project/build.properties | 1 + project/plugins.sbt | 4 + 13 files changed, 706 insertions(+) create mode 100644 .gitignore create mode 100644 .scalafmt.conf create mode 100644 .travis.yml create mode 100644 README.md create mode 100644 build.sbt create mode 100644 docs/Intro.md create mode 100644 outwatch-router/src/main/scala/outwatch/router/Path.scala create mode 100644 outwatch-router/src/main/scala/outwatch/router/State.scala create mode 100644 project/Dependencies.scala create mode 100644 project/Options.scala create mode 100644 project/Version.scala create mode 100644 project/build.properties create mode 100644 project/plugins.sbt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a5311a0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,142 @@ + +# Created by https://www.gitignore.io/api/sbt,scala,bloop,metals,intellij,sublimetext +# Edit at https://www.gitignore.io/?templates=sbt,scala,bloop,metals,intellij,sublimetext + +### Bloop ### +.bloop/ + +### Intellij ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/modules.xml +# .idea/*.iml +# .idea/modules + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Intellij Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +.idea/sonarlint + +### Metals ### +.metals/ + +### SBT ### +# Simple Build Tool +# http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control + +dist/* +target/ +lib_managed/ +src_managed/ +project/boot/ +project/plugins/project/ +.history +.cache +.lib/ + +### Scala ### +*.class +*.log + +### SublimeText ### +# Cache files for Sublime Text +*.tmlanguage.cache +*.tmPreferences.cache +*.stTheme.cache + +# Workspace files are user-specific +*.sublime-workspace + +# Project files should be checked into the repository, unless a significant +# proportion of contributors will probably not be using Sublime Text +# *.sublime-project + +# SFTP configuration file +sftp-config.json + +# Package control specific files +Package Control.last-run +Package Control.ca-list +Package Control.ca-bundle +Package Control.system-ca-bundle +Package Control.cache/ +Package Control.ca-certs/ +Package Control.merged-ca-bundle +Package Control.user-ca-bundle +oscrypto-ca-bundle.crt +bh_unicode_properties.cache + +# Sublime-github package stores a github token in this file +# https://packagecontrol.io/packages/sublime-github +GitHub.sublime-settings + +# End of https://www.gitignore.io/api/sbt,scala,bloop,metals,intellij,sublimetext + +/.idea/ diff --git a/.scalafmt.conf b/.scalafmt.conf new file mode 100644 index 0000000..3557379 --- /dev/null +++ b/.scalafmt.conf @@ -0,0 +1,23 @@ +style = default + +maxColumn = 100 + +// Vertical alignment is pretty, but leads to bigger diffs +align = none + +// Insist on trailing commas for better difs in element construction +trailingCommas = always +danglingParentheses = true + +rewrite.rules = [ + AvoidInfix + RedundantBraces + RedundantParens + AsciiSortImports + PreferCurlyFors +] + +project.excludeFilters = [ + "scalafix-inputs", + "scalafix-outputs" +] diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..97b3540 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,33 @@ +language: scala + +install: + - rvm use 2.3.0 --install --fuzzy + - gem update --system + - gem install sass + - gem install jekyll -v 3.2.1 + +scala: + - 2.12.8 + +jobs: + include: + - stage: verify + + script: + - sbt ++$TRAVIS_SCALA_VERSION todomvc/fullOptJS::webpack + - sbt docs/mdoc + - sbt docs/makeMicrosite + - mkdir -p router-docs/site/todomvc + - cp -R router-docs/target/site/* ./router-docs/site/ + - cp ./router-docs/site/Readme.html ./router-docs/site/index.html + +deploy: + provider: pages + skip-cleanup: true + github-token: $GITHUB_TOKEN # Set in the settings page of your repository, as a secure variable + keep-history: true + local-dir: router-docs/site + target-branch: gh-pages + on: + branch: master + diff --git a/README.md b/README.md new file mode 100644 index 0000000..86a32a0 --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +outwatch-router +=== + +Easy routing for [outwatch](https://outwatch.github.io) on scala.js + +Most of this code is adapted from [http4s](http4s.org)'s route parsing and path pattern matching. + +See [documentation](https://clovellytech.github.io/outwatch-router/index.html) diff --git a/build.sbt b/build.sbt new file mode 100644 index 0000000..8a4dfbd --- /dev/null +++ b/build.sbt @@ -0,0 +1,77 @@ +import dependencies._ + +cancelable in Global := true + +val commonSettings = Seq( + organization := "com.clovellytech", + version := Version.version, + scalaVersion := Version.scalaVersion, + resolvers ++= addResolvers, + scalacOptions ++= options.scalac, + scalacOptions in (Compile, console) := options.scalacConsole, + updateOptions := updateOptions.value.withLatestSnapshots(false) +) ++ compilerPlugins + +val withTests : String = "compile->compile;test->test" +val testOnly : String = "test->test" + +lazy val docs = (project in file("./router-docs")) + .settings(name := "outwatch-router-docs") + .enablePlugins(MdocPlugin) + .settings(commonSettings) + .dependsOn(router) + +lazy val copyFastOptJS = TaskKey[Unit]("copyFastOptJS", "Copy javascript files to target directory") + +lazy val router = (project in file("./outwatch-router")) + .settings(name := "outwatch-router") + .enablePlugins(ScalaJSPlugin) + .enablePlugins(ScalaJSBundlerPlugin) + .settings(commonSettings) + .settings( + scalaJSModuleKind := ModuleKind.CommonJSModule, + scalacOptions += "-P:scalajs:sjsDefinedByDefault", + useYarn := true, // makes scalajs-bundler use yarn instead of npm + jsEnv in Test := new org.scalajs.jsenv.jsdomnodejs.JSDOMNodeJSEnv, + scalaJSUseMainModuleInitializer := true, + scalaJSModuleKind := ModuleKind.CommonJSModule, // configure Scala.js to emit a JavaScript module instead of a top-level script + version in webpack := "4.16.1", + version in startWebpackDevServer := "3.1.4", + webpackDevServerExtraArgs := Seq("--progress", "--color"), + webpackConfigFile in fastOptJS := Some(baseDirectory.value / "webpack.config.dev.js"), + // https://scalacenter.github.io/scalajs-bundler/cookbook.html#performance + webpackBundlingMode in fastOptJS := BundlingMode.LibraryOnly(), + resolvers += "jitpack" at "https://jitpack.io", + libraryDependencies ++= Seq( + "io.github.outwatch" % "outwatch" % "ea240c6d04", + "org.http4s" %% "parboiled" % "1.0.0", + "org.scalatest" %%% "scalatest" % "3.0.5" % Test + ), + copyFastOptJS := { + val inDir = (crossTarget in (Compile, fastOptJS)).value + val outDir = (crossTarget in (Compile, fastOptJS)).value / "dev" + val files = Seq("outwatch-router-fastopt-loader.js", "outwatch-router-frontend-fastopt.js", "outwatch-router-frontend-fastopt.js.map") map { p => (inDir / p, outDir / p) } + IO.copy(files, overwrite = true, preserveLastModified = true, preserveExecutable = true) + }, + // hot reloading configuration: + // https://github.com/scalacenter/scalajs-bundler/issues/180 + addCommandAlias("dev", "; compile; fastOptJS::startWebpackDevServer; devwatch; fastOptJS::stopWebpackDevServer"), + addCommandAlias("devwatch", "~; fastOptJS; copyFastOptJS") + ) + +lazy val exampleApp = (project in file("router-example")) + .settings(name := "outwatch-example") + .settings(commonSettings) + .dependsOn(router) + +lazy val root = (project in file(".")) + .settings(name := "outwatch-router-root") + .settings(commonSettings) + .settings( + skip in publish := true, + aggregate in reStart := false, + ) + .dependsOn(router) + .aggregate(router) + + diff --git a/docs/Intro.md b/docs/Intro.md new file mode 100644 index 0000000..721b159 --- /dev/null +++ b/docs/Intro.md @@ -0,0 +1,43 @@ +Outwatch Router +=== + +```scala mdoc +import cats._ +import cats.implicits._ +import cats.Applicative +import cats.data.Kleisli +import outwatch.router._, Router._ + +sealed abstract class Page +case class RootPage() extends Page +case class Login() extends Page +case class Register() extends Page +case class Profile(userId: String) extends Page +case class NotFound() extends Page + +object Page{ + def root: Page = RootPage() + def login: Page = Login() + def register: Page = Register() + def profile(userId: String): Page = Profile(userId) + def notFound: Page = NotFound() +} + +def routes[F[_]: Applicative]: AppRouter[F, Page] = Kleisli[F, Path, Page] { + case Root => Page.root.pure[F] + case Root / "login" => Page.login.pure[F] + case Root / "register" => Page.register.pure[F] + case Root / "profile" / userId => Page.profile(userId).pure[F] + case _ => Page.notFound.pure[F] +} + +val router = routes[Id] + +router.run(Root) +router.run(Root / "login") +router.run(Root / "profile" / "saopa98f") + +router.run(Path("/profile/asd")) +router.run(Path("/apsinoasn")) +``` + diff --git a/outwatch-router/src/main/scala/outwatch/router/Path.scala b/outwatch-router/src/main/scala/outwatch/router/Path.scala new file mode 100644 index 0000000..8143656 --- /dev/null +++ b/outwatch-router/src/main/scala/outwatch/router/Path.scala @@ -0,0 +1,220 @@ +// Completely copied from org.http4s.dsl.impl + +package outwatch.router + +import cats.implicits._ +import java.nio.{ByteBuffer, CharBuffer} +import java.nio.charset.Charset +import java.nio.charset.StandardCharsets.UTF_8 + +/** Base class for path extractors. */ +trait Path { + def /(child: String) = new /(this, child) + def toList: List[String] + def parent: Path + def lastOption: Option[String] + def startsWith(other: Path): Boolean +} + +object Path { + + /** Constructs a path from a single string by splitting on the `'/'` + * character. + * + * Leading slashes do not create an empty path segment. This is to + * reflect that there is no distinction between a request to + * `http://www.example.com` from `http://www.example.com/`. + * + * Trailing slashes result in a path with an empty final segment, + * unless the path is `"/"`, which is `Root`. + * + * Segments are URL decoded. + * + * {{{ + * scala> Path("").toList + * res0: List[String] = List() + * scala> Path("/").toList + * res1: List[String] = List() + * scala> Path("a").toList + * res2: List[String] = List(a) + * scala> Path("/a").toList + * res3: List[String] = List(a) + * scala> Path("/a/").toList + * res4: List[String] = List(a, "") + * scala> Path("//a").toList + * res5: List[String] = List("", a) + * scala> Path("/%2F").toList + * res0: List[String] = List(/) + * }}} + */ + def apply(str: String): Path = + if (str == "" || str == "/") + Root + else { + val segments = str.split("/", -1) + // .head is safe because split always returns non-empty array + val segments0 = if (segments.head == "") segments.drop(1) else segments + segments0.foldLeft(Root: Path)((path, seg) => path / UrlCodingUtils.urlDecode(seg)) + } + + def apply(first: String, rest: String*): Path = + rest.foldLeft(Root / first)(_ / _) + + def apply(list: List[String]): Path = + list.foldLeft(Root: Path)(_ / _) + + def unapplySeq(path: Path): Some[List[String]] = + Some(path.toList) +// +// def unapplySeq[F[_]](request: Request[F]): Some[List[String]] = +// Some(Path(request.pathInfo).toList) +} + + +final case class /(parent: Path, child: String) extends Path { + lazy val toList: List[String] = parent.toList ++ List(child) + + def lastOption: Some[String] = Some(child) + + lazy val asString: String = s"$parent/${UrlCodingUtils.pathEncode(child)}" + override def toString: String = asString + def startsWith(other: Path): Boolean = { + val components = other.toList + toList.take(components.length) === components + } +} + +/** + * Root extractor: + * {{{ + * Path("/") match { + * case Root => ... + * } + * }}} + */ +case object Root extends Path { + def toList: List[String] = Nil + def parent: Path = this + def lastOption: None.type = None + override def toString = "" + def startsWith(other: Path): Boolean = other == Root +} + + +private[router] object UrlCodingUtils { + + private val lower = ('a' to 'z').toSet + private val upper = ('A' to 'Z').toSet + private val num = ('0' to '9').toSet + val Unreserved: Set[Char] = lower ++ upper ++ num ++ "-_.~" + + private val toSkip : Set[Char] = Unreserved ++ "!$&'()*+,;=:/?@" + + private val HexUpperCaseChars: Array[Char] = ('A' to 'F').toArray + /** + * Percent-encodes a string. Depending on the parameters, this method is + * appropriate for URI or URL form encoding. Any resulting percent-encodings + * are normalized to uppercase. + * + * @param toEncode the string to encode + * @param charset the charset to use for characters that are percent encoded + * @param spaceIsPlus if space is not skipped, determines whether it will be + * rendreed as a `"+"` or a percent-encoding according to `charset`. + * @param toSkip a predicate of characters exempt from encoding. In typical + * use, this is composed of all Unreserved URI characters and sometimes a + * subset of Reserved URI characters. + */ + def urlEncode( + toEncode: String, + charset: Charset = UTF_8, + spaceIsPlus: Boolean = false, + toSkip: Char => Boolean = toSkip): String = { + val in = charset.encode(toEncode) + val out = CharBuffer.allocate((in.remaining() * 3).toInt) + while (in.hasRemaining) { + val c = in.get().toChar + if (toSkip(c)) { + out.put(c) + } else if (c == ' ' && spaceIsPlus) { + out.put('+') + } else { + out.put('%') + out.put(HexUpperCaseChars((c >> 4) & 0xF)) + out.put(HexUpperCaseChars(c & 0xF)) + } + } + out.flip() + out.toString + } + + private val SkipEncodeInPath = + Unreserved ++ ":@!$&'()*+,;=" + + def pathEncode(s: String, charset: Charset = UTF_8): String = + UrlCodingUtils.urlEncode(s, charset, false, SkipEncodeInPath) + + /** + * Percent-decodes a string. + * + * @param toDecode the string to decode + * @param charset the charset of percent-encoded characters + * @param plusIsSpace true if `'+'` is to be interpreted as a `' '` + * @param toSkip a predicate of characters whose percent-encoded form + * is left percent-encoded. Almost certainly should be left empty. + */ + def urlDecode( + toDecode: String, + charset: Charset = UTF_8, + plusIsSpace: Boolean = false, + toSkip: Char => Boolean = Function.const(false)): String = { + val in = CharBuffer.wrap(toDecode) + // reserve enough space for 3-byte UTF-8 characters. 4-byte characters are represented + // as surrogate pairs of characters, and will get a luxurious 6 bytes of space. + val out = ByteBuffer.allocate(in.remaining() * 3) + while (in.hasRemaining) { + val mark = in.position() + val c = in.get() + if (c == '%') { + if (in.remaining() >= 2) { + val xc = in.get() + val yc = in.get() + // scalastyle:off magic.number + val x = Character.digit(xc, 0x10) + val y = Character.digit(yc, 0x10) + // scalastyle:on magic.number + if (x != -1 && y != -1) { + val oo = (x << 4) + y + if (!toSkip(oo.toChar)) { + out.put(oo.toByte) + } else { + out.put('%'.toByte) + out.put(xc.toByte) + out.put(yc.toByte) + } + } else { + out.put('%'.toByte) + in.position(mark + 1) + } + } else { + // This is an invalid encoding. Fail gracefully by treating the '%' as + // a literal. + out.put(c.toByte) + while (in.hasRemaining) out.put(in.get().toByte) + } + } else if (c == '+' && plusIsSpace) { + out.put(' '.toByte) + } else { + // normally `out.put(c.toByte)` would be enough since the url is %-encoded, + // however there are cases where a string can be partially decoded + // so we have to make sure the non us-ascii chars get preserved properly. + if (this.toSkip(c)) { + out.put(c.toByte) + } else { + out.put(charset.encode(String.valueOf(c))) + } + } + } + out.flip() + charset.decode(out).toString + } +} diff --git a/outwatch-router/src/main/scala/outwatch/router/State.scala b/outwatch-router/src/main/scala/outwatch/router/State.scala new file mode 100644 index 0000000..6056b44 --- /dev/null +++ b/outwatch-router/src/main/scala/outwatch/router/State.scala @@ -0,0 +1,9 @@ +package outwatch.router + +import cats.data.Kleisli + +object Router { + type AppRouter[F[_], A] = Kleisli[F, Path, A] + val AppRouter = Kleisli + +} diff --git a/project/Dependencies.scala b/project/Dependencies.scala new file mode 100644 index 0000000..ce83b31 --- /dev/null +++ b/project/Dependencies.scala @@ -0,0 +1,84 @@ +import sbt._ +import sbt.librarymanagement.DependencyBuilders +import org.portablescala.sbtplatformdeps._ + +object dependencies { + val addResolvers = Seq( + "52north for postgis" at "http://52north.org/maven/repo/releases/", + Resolver.sonatypeRepo("releases"), + Resolver.sonatypeRepo("snapshots") + ) + + val compilerPlugins = Seq( + addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.9.9"), + addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full) + ) + + val bcrypt = "3.1" + val cats = "1.4.0" + val catsMtl = "0.4.0" + val catsEffect = "1.2.0" + val circe = "0.11.0" + val circeConfig = "0.6.1" + val doobie = "0.6.0" + val flyway = "5.2.4" + val fs2 = "1.0.2" + val h4sm = "0.0.17" + val http4s = "0.20.0-M5" + val logback = "1.2.3" + val monocle = "1.5.0" + val postgis = "1.3.3" + val postgres = "42.2.5" + val scalaCheck = "1.14.0" + val scalaTest = "3.0.5" + val simulacrum = "0.14.0" + + val httpDeps = Seq( + "http4s-blaze-server", + "http4s-blaze-client", + "http4s-circe", + "http4s-dsl" + ).map("org.http4s" %% _ % http4s) ++ Seq( + "circe-core", + "circe-generic", + "circe-parser", + "circe-java8" + ).map("io.circe" %% _ % circe) + + val testDeps = Seq( + "org.scalatest" %% "scalatest" % scalaTest, + "org.tpolecat" %% "doobie-scalatest" % doobie, + "org.scalacheck" %% "scalacheck" % scalaCheck, + "com.clovellytech" %% "h4sm-dbtesting" % h4sm + ) + + val testDepsInTestOnly = testDeps.map(_ % "test") + + val dbDeps = Seq( + "org.flywaydb" % "flyway-core" % flyway, + "org.postgresql" % "postgresql" % postgres, + "org.postgis" % "postgis-jdbc" % postgis + ) ++ Seq( + "doobie-core", + "doobie-postgres", + "doobie-hikari" + ).map("org.tpolecat" %% _ % doobie) + + val commonDeps = Seq( + "io.circe" %% "circe-config" % circeConfig, + "ch.qos.logback" % "logback-classic" % logback, + "com.github.mpilquist" %% "simulacrum" % simulacrum + ) ++ Seq( + "h4sm-auth", + "h4sm-files", + "h4sm-permissions" + ).map("com.clovellytech" %% _ % h4sm) ++ Seq( + "monocle-core", + "monocle-generic", + "monocle-macro", + "monocle-state", + "monocle-refined" + ).map("com.github.julien-truffaut" %% _ % monocle) + + val allDeps = httpDeps ++ dbDeps ++ commonDeps ++ testDepsInTestOnly +} diff --git a/project/Options.scala b/project/Options.scala new file mode 100644 index 0000000..46016de --- /dev/null +++ b/project/Options.scala @@ -0,0 +1,58 @@ + +object options { + + val scalac = Seq( + // format: off + "-deprecation", // Emit warning and location for usages of deprecated APIs. + "-encoding", "utf-8", // Specify character encoding used by source files. + "-explaintypes", // Explain type errors in more detail. + "-feature", // Emit warning and location for usages of features that should be imported explicitly. + "-language:existentials", // Existential types (besides wildcard types) can be written and inferred + "-language:experimental.macros", // Allow macro definition (besides implementation and application) + "-language:higherKinds", // Allow higher-kinded types + "-language:implicitConversions", // Allow definition of implicit functions called views + "-unchecked", // Enable additional warnings where generated code depends on assumptions. + "-Xcheckinit", // Wrap field accessors to throw an exception on uninitialized access. + "-Xfatal-warnings", // Fail the compilation if there are any warnings. + "-Xfuture", // Turn on future language features. + "-Xlint:adapted-args", // Warn if an argument list is modified to match the receiver. + "-Xlint:by-name-right-associative", // By-name parameter of right associative operator. + "-Xlint:constant", // Evaluation of a constant arithmetic expression results in an error. + "-Xlint:delayedinit-select", // Selecting member of DelayedInit. + "-Xlint:doc-detached", // A Scaladoc comment appears to be detached from its element. + "-Xlint:inaccessible", // Warn about inaccessible types in method signatures. + "-Xlint:infer-any", // Warn when a type argument is inferred to be `Any`. + "-Xlint:missing-interpolator", // A string literal appears to be missing an interpolator id. + "-Xlint:nullary-override", // Warn when non-nullary `def f()' overrides nullary `def f'. + "-Xlint:nullary-unit", // Warn when nullary methods return Unit. + "-Xlint:option-implicit", // Option.apply used implicit view. + "-Xlint:package-object-classes", // Class or object defined in package object. + "-Xlint:poly-implicit-overload", // Parameterized overloaded implicit methods are not visible as view bounds. + "-Xlint:private-shadow", // A private field (or class parameter) shadows a superclass field. + "-Xlint:stars-align", // Pattern sequence wildcard must align with sequence component. + "-Xlint:type-parameter-shadow", // A local type parameter shadows a type already in scope. + "-Xlint:unsound-match", // Pattern match may not be typesafe. + "-Yno-adapted-args", // Do not adapt an argument list (either by inserting () or creating a tuple) to match the receiver. + "-Ypartial-unification", // Enable partial unification in type constructor inference + "-Ywarn-dead-code", // Warn when dead code is identified. + "-Ywarn-extra-implicit", // Warn when more than one implicit parameter section is defined. + "-Ywarn-inaccessible", // Warn about inaccessible types in method signatures. + "-Ywarn-infer-any", // Warn when a type argument is inferred to be `Any`. + "-Ywarn-nullary-unit", // Warn when nullary methods return Unit. + "-Ywarn-nullary-override", // Warn when non-nullary `def f()' overrides nullary `def f'. + "-Ywarn-numeric-widen", // Warn when numerics are widened. + "-Ywarn-unused:implicits", // Warn if an implicit parameter is unused. + "-Ywarn-unused:imports", // Warn if an import selector is not referenced. + "-Ywarn-unused:locals", // Warn if a local definition is unused. + "-Ywarn-unused:params", // Warn if a value parameter is unused. + "-Ywarn-unused:patvars", // Warn if a variable bound in a pattern is unused. + "-Ywarn-unused:privates", // Warn if a private member is unused. + "-Ywarn-value-discard", // Warn when non-Unit expression results are unused. + "-Yrangepos" + // format: on + ) + + val badScalacConsoleFlags = Seq("-Xfatal-warnings", "-Ywarn-unused:imports") + + val scalacConsole = scalac.filterNot(badScalacConsoleFlags.contains(_)) +} diff --git a/project/Version.scala b/project/Version.scala new file mode 100644 index 0000000..7836cb4 --- /dev/null +++ b/project/Version.scala @@ -0,0 +1,4 @@ +object Version{ + val version = "0.0.1" + val scalaVersion = "2.12.8" +} diff --git a/project/build.properties b/project/build.properties new file mode 100644 index 0000000..0cd8b07 --- /dev/null +++ b/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.2.3 diff --git a/project/plugins.sbt b/project/plugins.sbt new file mode 100644 index 0000000..b2cc145 --- /dev/null +++ b/project/plugins.sbt @@ -0,0 +1,4 @@ +addSbtPlugin("ch.epfl.scala" % "sbt-scalajs-bundler" % "0.13.1") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.26") +addSbtPlugin("org.scalameta" % "sbt-mdoc" % "1.2.8" ) +addSbtPlugin("com.47deg" % "sbt-microsites" % "0.8.0")