diff --git a/.gitignore b/.gitignore index 8043b7a..914774c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ *.class *.log node_modules +*/node_modules # sbt specific .cache/ diff --git a/.travis.yml b/.travis.yml index adcc1a1..acca7f9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: scala scala: -- 2.11.8 +- 2.12.8 sudo: false jdk: - oraclejdk8 @@ -16,7 +16,6 @@ cache: - $HOME/.sbt/boot/ install: - . $HOME/.nvm/nvm.sh - - nvm install stable - - nvm use stable + - nvm install --latest-npm --lts=Dubnium + - nvm use --lts - npm install - - npm install jsdom diff --git a/build.sbt b/build.sbt index 6a6088a..aae6ead 100644 --- a/build.sbt +++ b/build.sbt @@ -1,11 +1,14 @@ import sbt.Keys._ import sbt.Project.projectToRef +// shadow sbt-scalajs' crossProject and CrossType from Scala.js 0.6.x +import sbtcrossproject.CrossPlugin.autoImport.{crossProject, CrossType} +import org.irundaia.sbt.sass._ // a special crossProject for configuring a JS/JVM/shared structure -lazy val shared = (crossProject.crossType(CrossType.Pure) in file("shared")) +lazy val shared = (crossProject(JSPlatform, JVMPlatform).crossType(CrossType.Pure) in file("shared")) .settings( - scalaVersion := Settings.versions.scala, - libraryDependencies ++= Settings.sharedDependencies.value + scalaVersion := Settings.v.scala, + libraryDependencies ++= Settings.sharedDependencies.value ) // set up settings specific to the JS project .jsConfigure(_ enablePlugins ScalaJSWeb) @@ -20,24 +23,25 @@ lazy val elideOptions = settingKey[Seq[String]]("Set limit for elidable function // instantiate the JS project for SBT with some additional settings lazy val client: Project = (project in file("client")) .settings( - name := "client", - version := Settings.version, - scalaVersion := Settings.versions.scala, - scalacOptions ++= Settings.scalacOptions, - libraryDependencies ++= Settings.scalajsDependencies.value, - // by default we do development build, no eliding - elideOptions := Seq(), - scalacOptions ++= elideOptions.value, - jsDependencies ++= Settings.jsDependencies.value, - // RuntimeDOM is needed for tests - jsDependencies += RuntimeDOM % "test", - // yes, we want to package JS dependencies - skip in packageJSDependencies := false, - // use Scala.js provided launcher code to start the client app - scalaJSUseMainModuleInitializer := true, - scalaJSUseMainModuleInitializer in Test := false, - // use uTest framework for tests - testFrameworks += new TestFramework("utest.runner.Framework") + name := "client", + version := Settings.v.app, + scalaVersion := Settings.v.scala, + scalacOptions ++= Settings.scalacOptions, + libraryDependencies ++= Settings.scalajsDependencies.value, + // by default we do development build, no eliding + elideOptions := Seq(), + scalacOptions ++= elideOptions.value, + jsDependencies ++= Settings.jsDependencies.value, + // RuntimeDOM is needed for tests + jsEnv in Test := new org.scalajs.jsenv.jsdomnodejs.JSDOMNodeJSEnv, + // yes, we want to package JS dependencies + skip in packageJSDependencies := false, + // use Scala.js provided launcher code to start the client app + scalaJSUseMainModuleInitializer := true, + scalaJSUseMainModuleInitializer in Test := false, + // use uTest framework for tests + testFrameworks += new TestFramework("utest.runner.Framework"), + dependencyOverrides ++= Settings.dependencyOverrides.value ) .enablePlugins(ScalaJSPlugin, ScalaJSWeb) .dependsOn(sharedJS) @@ -48,20 +52,22 @@ lazy val clients = Seq(client) // instantiate the JVM project for SBT with some additional settings lazy val server = (project in file("server")) .settings( - name := "server", - version := Settings.version, - scalaVersion := Settings.versions.scala, - scalacOptions ++= Settings.scalacOptions, - libraryDependencies ++= Settings.jvmDependencies.value, - commands += ReleaseCmd, - // triggers scalaJSPipeline when using compile or continuous compilation - compile in Compile := ((compile in Compile) dependsOn scalaJSPipeline).value, - // connect to the client project - scalaJSProjects := clients, - pipelineStages in Assets := Seq(scalaJSPipeline), - pipelineStages := Seq(digest, gzip), - // compress CSS - LessKeys.compress in Assets := true + name := "server", + version := Settings.v.app, + scalaVersion := Settings.v.scala, + scalacOptions ++= Settings.scalacOptions, + libraryDependencies ++= Settings.jvmDependencies.value, + libraryDependencies += guice, + commands += ReleaseCmd, + // triggers scalaJSPipeline when using compile or continuous compilation + compile in Compile := ((compile in Compile) dependsOn scalaJSPipeline).value, + // connect to the client project + scalaJSProjects := clients, + pipelineStages in Assets := Seq(scalaJSPipeline), + pipelineStages := Seq(digest, gzip), + // compress CSS + SassKeys.cssStyle in Assets := Minified, + dependencyOverrides ++= Settings.dependencyOverrides.value ) .enablePlugins(PlayScala) .disablePlugins(PlayLayoutPlugin) // use the standard directory layout instead of Play's custom @@ -70,14 +76,14 @@ lazy val server = (project in file("server")) // Command for building a release lazy val ReleaseCmd = Command.command("release") { - state => "set elideOptions in client := Seq(\"-Xelide-below\", \"WARNING\")" :: - "client/clean" :: - "client/test" :: - "server/clean" :: - "server/test" :: - "server/dist" :: - "set elideOptions in client := Seq()" :: - state + state => "set elideOptions in client := Seq(\"-Xelide-below\", \"WARNING\")" :: + "client/clean" :: + "client/test" :: + "server/clean" :: + "server/test" :: + "server/dist" :: + "set elideOptions in client := Seq()" :: + state } // lazy val root = (project in file(".")).aggregate(client, server) diff --git a/client/src/main/scala/spatutorial/client/SPAMain.scala b/client/src/main/scala/spatutorial/client/SPAMain.scala index 17b9c25..dd99b08 100644 --- a/client/src/main/scala/spatutorial/client/SPAMain.scala +++ b/client/src/main/scala/spatutorial/client/SPAMain.scala @@ -8,13 +8,14 @@ import spatutorial.client.logger._ import spatutorial.client.modules._ import spatutorial.client.services.SPACircuit -import scala.scalajs.js import scala.scalajs.js.annotation.{JSExport, JSExportTopLevel} import CssSettings._ import scalacss.ScalaCssReact._ -@JSExportTopLevel("SPAMain") -object SPAMain extends js.JSApp { +object SPAMain { + + @JSExportTopLevel("SPAMain") + protected def getInstance(): this.type = this // Define the locations (pages) used in this application sealed trait Loc @@ -39,9 +40,9 @@ object SPAMain extends js.JSApp { def layout(c: RouterCtl[Loc], r: Resolution[Loc]) = { <.div( // here we use plain Bootstrap class names as these are specific to the top level layout defined here - <.nav(^.className := "navbar navbar-inverse navbar-fixed-top", + <.nav(^.className := "navbar navbar-dark bg-dark fixed-top navbar-expand-md", <.div(^.className := "container", - <.div(^.className := "navbar-header", <.span(^.className := "navbar-brand", "SPA Tutorial")), + <.span(^.className := "navbar-brand", "SPA Tutorial"), <.div(^.className := "collapse navbar-collapse", // connect menu to model, because it needs to update when the number of open todos changes todoCountWrapper(proxy => MainMenu(c, r.page, proxy)) @@ -54,7 +55,7 @@ object SPAMain extends js.JSApp { } @JSExport - def main(): Unit = { + def main(args: Array[String]): Unit = { log.warn("Application starting") // send log messages also to the server log.enableServerLogging("/logging") diff --git a/client/src/main/scala/spatutorial/client/components/Bootstrap.scala b/client/src/main/scala/spatutorial/client/components/Bootstrap.scala index 86627ae..d7a28e3 100644 --- a/client/src/main/scala/spatutorial/client/components/Bootstrap.scala +++ b/client/src/main/scala/spatutorial/client/components/Bootstrap.scala @@ -42,15 +42,15 @@ object Bootstrap { def apply() = component } - object Panel { + object Card { case class Props(heading: String, style: CommonStyle.Value = CommonStyle.default) - val component = ScalaComponent.builder[Props]("Panel") + val component = ScalaComponent.builder[Props]("Card") .renderPC((_, p, c) => - <.div(bss.panelOpt(p.style), - <.div(bss.panelHeading, p.heading), - <.div(bss.panelBody, c) + <.div(bss.cardOpt(p.style), + <.div(bss.cardHeading, p.heading), + <.div(bss.cardBody, c) ) ).build @@ -67,7 +67,7 @@ object Bootstrap { class Backend(t: BackendScope[Props, Unit]) { def hide = // instruct Bootstrap to hide the modal - t.getDOMNode.map(jQuery(_).modal("hide")).void + t.getDOMNode.map(n => jQuery(n.toElement.get).modal("hide")).void // jQuery event handler to be fired when the modal has been hidden def hidden(e: JQueryEventObject): js.Any = { @@ -94,9 +94,9 @@ object Bootstrap { .componentDidMount(scope => Callback { val p = scope.props // instruct Bootstrap to show the modal - jQuery(scope.getDOMNode).modal(js.Dynamic.literal("backdrop" -> p.backdrop, "keyboard" -> p.keyboard, "show" -> true)) + jQuery(scope.getDOMNode.asElement).modal(js.Dynamic.literal("backdrop" -> p.backdrop, "keyboard" -> p.keyboard, "show" -> true)) // register event listener to be notified when the modal is closed - jQuery(scope.getDOMNode).on("hidden.bs.modal", null, null, scope.backend.hidden _) + jQuery(scope.getDOMNode.asElement).on("hidden.bs.modal", null, null, scope.backend.hidden _) }) .build diff --git a/client/src/main/scala/spatutorial/client/components/BootstrapStyles.scala b/client/src/main/scala/spatutorial/client/components/BootstrapStyles.scala index 8e6b178..59994f0 100644 --- a/client/src/main/scala/spatutorial/client/components/BootstrapStyles.scala +++ b/client/src/main/scala/spatutorial/client/components/BootstrapStyles.scala @@ -27,19 +27,19 @@ class BootstrapStyles(implicit r: mutable.Register) extends StyleSheet.Inline()( val button = buttonOpt(default) - val panelOpt = commonStyle(csDomain, "panel") + val cardOpt = commonStyle(csDomain, "card") - val panel = panelOpt(default) + val card = cardOpt(default) - val labelOpt = commonStyle(csDomain, "label") + val badgeOpt = commonStyle(csDomain, "badge") - val label = labelOpt(default) + val badge = badgeOpt(default) val alert = commonStyle(contextDomain, "alert") - val panelHeading = styleWrap("panel-heading") + val cardHeading = styleWrap("card-header") - val panelBody = styleWrap("panel-body") + val cardBody = styleWrap("card-body") // wrap styles in a namespace, assign to val to prevent lazy initialization object modal { @@ -61,14 +61,19 @@ class BootstrapStyles(implicit r: mutable.Register) extends StyleSheet.Inline()( } val _listGroup = listGroup - val pullRight = styleWrap("pull-right") - val buttonXS = styleWrap("btn-xs") + val floatRight = styleWrap("float-right") + val buttonSM = styleWrap("btn-sm") + val buttonSecondary = styleWrap("btn-secondary") val close = styleWrap("close") - val labelAsBadge = style(addClassName("label-as-badge"), borderRadius(1.em)) + val badgePill = style(addClassName("badge-pill"), borderRadius(1.em)) - val navbar = styleWrap("nav", "navbar-nav") + val navbar = styleWrap("navbar-nav", "mr-auto") val formGroup = styleWrap("form-group") val formControl = styleWrap("form-control") + + val spacingMR1 = styleWrap("mr-1") + val spacingMT3 = styleWrap("mt-3") + } diff --git a/client/src/main/scala/spatutorial/client/components/Chart.scala b/client/src/main/scala/spatutorial/client/components/Chart.scala index b4ebcd3..d21e581 100644 --- a/client/src/main/scala/spatutorial/client/components/Chart.scala +++ b/client/src/main/scala/spatutorial/client/components/Chart.scala @@ -95,7 +95,7 @@ object Chart { ) .componentDidMount(scope => Callback { // access context of the canvas - val ctx = scope.getDOMNode.asInstanceOf[HTMLCanvasElement].getContext("2d") + val ctx = scope.getDOMNode.asElement.asInstanceOf[HTMLCanvasElement].getContext("2d") // create the actual chart using the 3rd party component scope.props.style match { case LineChart => new JSChart(ctx, ChartConfiguration("line", scope.props.data)) diff --git a/client/src/main/scala/spatutorial/client/components/Motd.scala b/client/src/main/scala/spatutorial/client/components/Motd.scala index 4780e56..ffb7aeb 100644 --- a/client/src/main/scala/spatutorial/client/components/Motd.scala +++ b/client/src/main/scala/spatutorial/client/components/Motd.scala @@ -16,7 +16,7 @@ object Motd { // create the React component for holding the Message of the Day val Motd = ScalaComponent.builder[ModelProxy[Pot[String]]]("Motd") .render_P { proxy => - Panel(Panel.Props("Message of the day"), + Card(Card.Props("Message of the day"), // render messages depending on the state of the Pot proxy().renderPending(_ > 500, _ => <.p("Loading...")), proxy().renderFailed(ex => <.p("Failed to load")), diff --git a/client/src/main/scala/spatutorial/client/components/TodoList.scala b/client/src/main/scala/spatutorial/client/components/TodoList.scala index 425f33c..24863c7 100644 --- a/client/src/main/scala/spatutorial/client/components/TodoList.scala +++ b/client/src/main/scala/spatutorial/client/components/TodoList.scala @@ -31,8 +31,8 @@ object TodoList { <.input.checkbox(^.checked := item.completed, ^.onChange --> p.stateChange(item.copy(completed = !item.completed))), <.span(" "), if (item.completed) <.s(item.content) else <.span(item.content), - Button(Button.Props(p.editItem(item), addStyles = Seq(bss.pullRight, bss.buttonXS)), "Edit"), - Button(Button.Props(p.deleteItem(item), addStyles = Seq(bss.pullRight, bss.buttonXS)), "Delete") + Button(Button.Props(p.editItem(item), addStyles = Seq(bss.floatRight, bss.buttonSM, bss.buttonSecondary)), "Edit"), + Button(Button.Props(p.deleteItem(item), addStyles = Seq(bss.floatRight, bss.buttonSM, bss.buttonSecondary, bss.spacingMR1)), "Delete") ) } <.ul(style.listGroup)(p.items toTagMod renderItem) diff --git a/client/src/main/scala/spatutorial/client/modules/Dashboard.scala b/client/src/main/scala/spatutorial/client/modules/Dashboard.scala index 455e6ff..76eae0f 100644 --- a/client/src/main/scala/spatutorial/client/modules/Dashboard.scala +++ b/client/src/main/scala/spatutorial/client/modules/Dashboard.scala @@ -9,7 +9,6 @@ import spatutorial.client.SPAMain.{Loc, TodoLoc} import spatutorial.client.components._ import scala.util.Random -import scala.language.existentials object Dashboard { diff --git a/client/src/main/scala/spatutorial/client/modules/MainMenu.scala b/client/src/main/scala/spatutorial/client/modules/MainMenu.scala index 551e03e..228a242 100644 --- a/client/src/main/scala/spatutorial/client/modules/MainMenu.scala +++ b/client/src/main/scala/spatutorial/client/modules/MainMenu.scala @@ -18,19 +18,19 @@ object MainMenu { case class Props(router: RouterCtl[Loc], currentLoc: Loc, proxy: ModelProxy[Option[Int]]) - private case class MenuItem(idx: Int, label: (Props) => VdomNode, icon: Icon, location: Loc) + private case class MenuItem(idx: Int, badge: (Props) => VdomNode, icon: Icon, location: Loc) // build the Todo menu item, showing the number of open todos private def buildTodoMenu(props: Props): VdomElement = { val todoCount = props.proxy().getOrElse(0) <.span( - <.span("Todo "), - <.span(bss.labelOpt(CommonStyle.danger), bss.labelAsBadge, todoCount).when(todoCount > 0) + <.span(" Todo "), + <.span(bss.badgeOpt(CommonStyle.danger), bss.badgePill, todoCount).when(todoCount > 0) ) } private val menuItems = Seq( - MenuItem(1, _ => "Dashboard", Icon.dashboard, DashboardLoc), + MenuItem(1, _ => " Dashboard", Icon.dashboard, DashboardLoc), MenuItem(2, buildTodoMenu, Icon.check, TodoLoc) ) @@ -43,8 +43,8 @@ object MainMenu { <.ul(bss.navbar)( // build a list of menu items menuItems.toVdomArray(item => - <.li(^.key := item.idx, (^.className := "active").when(props.currentLoc == item.location), - props.router.link(item.location)(item.icon, " ", item.label(props)) + <.li(^.key := item.idx, if(props.currentLoc == item.location) (^.className := "nav-item active") else (^.className := "nav-item"), + props.router.link(item.location)(item.icon, "", item.badge(props))(^.`class` := "nav-link") )) ) } diff --git a/client/src/main/scala/spatutorial/client/modules/TODO.scala b/client/src/main/scala/spatutorial/client/modules/TODO.scala index 6636d4a..746cae1 100644 --- a/client/src/main/scala/spatutorial/client/modules/TODO.scala +++ b/client/src/main/scala/spatutorial/client/modules/TODO.scala @@ -14,6 +14,8 @@ import spatutorial.shared._ import scalacss.ScalaCssReact._ object Todo { + // shorthand for styles + @inline private def bss = GlobalStyles.bootstrapStyles case class Props(proxy: ModelProxy[Pot[Todos]]) @@ -41,13 +43,13 @@ object Todo { } def render(p: Props, s: State) = - Panel(Panel.Props("What needs to be done"), <.div( + Card(Card.Props("What needs to be done"), <.div( p.proxy().renderFailed(ex => "Error loading"), p.proxy().renderPending(_ > 500, _ => "Loading..."), p.proxy().render(todos => TodoList(todos.items, item => p.proxy.dispatchCB(UpdateTodo(item)), item => editTodo(Some(item)), item => p.proxy.dispatchCB(DeleteTodo(item)))), - Button(Button.Props(editTodo(None)), Icon.plusSquare, " New")), - // if the dialog is open, add it to the panel + Button(Button.Props(editTodo(None), addStyles = Seq(bss.buttonSecondary, bss.spacingMT3)), Icon.plusSquare, " New")), + // if the dialog is open, add it to the card if (s.showTodoForm) TodoForm(TodoForm.Props(s.selectedItem, todoEdited)) else // otherwise add an empty placeholder VdomArray.empty()) @@ -103,9 +105,9 @@ object TodoForm { val headerText = if (s.item.id == "") "Add new todo" else "Edit todo" Modal(Modal.Props( // header contains a cancel button (X) - header = hide => <.span(<.button(^.tpe := "button", bss.close, ^.onClick --> hide, Icon.close), <.h4(headerText)), + header = hide => React.Fragment(<.h4(headerText), <.button(^.tpe := "button", bss.close, ^.onClick --> hide, Icon.close)), // footer has the OK button that submits the form before hiding it - footer = hide => <.span(Button(Button.Props(submitForm() >> hide), "OK")), + footer = hide => <.span(Button(Button.Props(submitForm() >> hide, addStyles = Seq(bss.buttonSecondary)), "OK")), // this is called after the modal has been hidden (animation is completed) closed = formClosed(s, p)), <.div(bss.formGroup, diff --git a/doc/en/getting-started.md b/doc/en/getting-started.md index be095ed..ba2205a 100644 --- a/doc/en/getting-started.md +++ b/doc/en/getting-started.md @@ -18,13 +18,24 @@ changing for example the chart data in `Dashboard.scala` and reloading the web p ## Requirements -SPA Tutorial uses Play 2.5 which depends on Java 8, so make sure you are using JVM 8 or later. +SPA Tutorial uses Play 2.7 which depends on Java 8, so make sure you are using JVM 8 or later. Running client tests requires [Node.js](https://nodejs.org/) and `jsdom` to be installed. After installing `node` and its package manager `npm` you can -install `jsdom` into your project folder with: +install `jsdom` into your root folder with: ``` -npm install jsdom +npm install ``` +`npm install` will refer to your package.json to install necessary dependencies: + +`jsdom` for client tests + +`sassify` for sass compilation Make sure to add `node_modules` directory to your `.gitignore` file! + +If you have installed node via nvm and you have issues with running tests about node being missing, run this command to resolve the issue: + +``` +ln -s "$(which node)" ~/.local/bin/node +``` diff --git a/doc/en/integrating-javascript-components.md b/doc/en/integrating-javascript-components.md index 5589296..3430bf4 100644 --- a/doc/en/integrating-javascript-components.md +++ b/doc/en/integrating-javascript-components.md @@ -27,14 +27,15 @@ to define it through child component(s). object Button { case class Props(onClick: Callback, style: CommonStyle.Value = CommonStyle.default, addStyles: Seq[StyleA] = Seq()) - + val component = ScalaComponent.builder[Props]("Button") - .renderPC((_, props, children) => - <.button(bss.buttonOpt(props.style), props.addStyles.toTagMod, ^.tpe := "button", ^.onClick --> props.onClick, children) + .renderPC((_, p, c) => + <.button(bss.buttonOpt(p.style), p.addStyles.toTagMod, ^.tpe := "button", ^.onClick --> p.onClick, c) ).build - - def apply(props: Props, children: ReactNode*) = component(props)(children: _*) + + def apply(props: Props, children: VdomNode*) = component(props)(children: _*) def apply() = component + } ``` @@ -42,26 +43,28 @@ This time the render method gets two parameters: The properties and the children button using Bootstrap CSS and binds `onClick` to the handler we defined in the properties. Finally the children are rendered within the button tag. -Defining a Bootstrap Panel is about as simple. +Defining a Bootstrap Card is about as simple. ```scala -object Panel { - case class Props(heading: String, style: CommonStyle.Value = CommonStyle.default) +object Card { - val component = ScalaComponent.builder[Props]("Panel") + case class Props(heading: String, style: CommonStyle.Value = CommonStyle.default) + + val component = ScalaComponent.builder[Props]("Card") .renderPC((_, p, c) => - <.div(bss.panelOpt(p.style), - <.div(bss.panelHeading, p.heading), - <.div(bss.panelBody, c) + <.div(bss.cardOpt(p.style), + <.div(bss.cardHeading, p.heading), + <.div(bss.cardBody, c) ) ).build - - def apply(props: Props, children: ReactNode*) = component(props)(children: _*) + + def apply(props: Props, children: VdomNode*) = component(props)(children: _*) def apply() = component + } ``` -The panel provides no interactivity but this time we define a separate `heading` in addition to using the children property. +The card provides no interactivity but this time we define a separate `heading` in addition to using the children property. ## Icons diff --git a/doc/en/main-menu.md b/doc/en/main-menu.md index c36f357..a56bfee 100644 --- a/doc/en/main-menu.md +++ b/doc/en/main-menu.md @@ -5,21 +5,21 @@ statically within the class itself, because the referred locations are anyway al a dynamic system, but static is just fine here. ```scala -case class Props(ctl: RouterCtl[Loc], currentLoc: Loc, proxy: ModelProxy[Option[Int]]) +case class Props(router: RouterCtl[Loc], currentLoc: Loc, proxy: ModelProxy[Option[Int]]) -case class MenuItem(idx: Int, label: (Props) => VdomNode, icon: Icon, location: Loc) +private case class MenuItem(idx: Int, badge: (Props) => VdomNode, icon: Icon, location: Loc) // build the Todo menu item, showing the number of open todos private def buildTodoMenu(props: Props): VdomElement = { val todoCount = props.proxy().getOrElse(0) - Seq( - <.span("Todo "), - <.span(bss.labelOpt(CommonStyle.danger), bss.labelAsBadge, todoCount).when(todoCount > 0) + <.span( + <.span(" Todo "), + <.span(bss.badgeOpt(CommonStyle.danger), bss.badgePill, todoCount).when(todoCount > 0) ) } private val menuItems = Seq( - MenuItem(1, _ => "Dashboard", Icon.dashboard, DashboardLoc), + MenuItem(1, _ => " Dashboard", Icon.dashboard, DashboardLoc), MenuItem(2, buildTodoMenu, Icon.check, TodoLoc) ) ``` @@ -30,20 +30,18 @@ the label is simple text, but for Todo we also render the number of open todos, To render the menu we just loop over the items and create appropriate tags. For links we need to use the `RouterCtl` provided in the properties. ```scala -private class Backend(t: BackendScope[Props, _]) { - def mounted(props: Props) = { +private class Backend($: BackendScope[Props, Unit]) { + def mounted(props: Props) = // dispatch a message to refresh the todos - Callback.ifTrue(props.proxy.value.isEmpty, props.proxy.dispatchCB(RefreshTodos)) - } - + Callback.when(props.proxy.value.isEmpty)(props.proxy.dispatchCB(RefreshTodos)) + def render(props: Props) = { <.ul(bss.navbar)( // build a list of menu items - for (item <- menuItems) yield { - <.li(^.key := item.idx, (^.className := "active").when(props.currentLoc == item.location), - props.router.link(item.location)(item.icon, " ", item.label(props)) - ) - } + menuItems.toVdomArray(item => + <.li(^.key := item.idx, if(props.currentLoc == item.location) (^.className := "nav-item active") else (^.className := "nav-item"), + props.router.link(item.location)(item.icon, "", item.badge(props))(^.`class` := "nav-link") + )) ) } } diff --git a/doc/en/production-build.md b/doc/en/production-build.md index beb8408..032322d 100644 --- a/doc/en/production-build.md +++ b/doc/en/production-build.md @@ -16,12 +16,13 @@ We need to define a separate JS dependencies for the production build, using the ```scala /** Dependencies for external JS libs that are bundled into a single .js file according to dependency order */ val jsDependencies = Def.setting(Seq( - "org.webjars.bower" % "react" % versions.react / "react-with-addons.js" minified "react-with-addons.min.js" commonJSName "React", - "org.webjars.bower" % "react" % versions.react / "react-dom.js" minified "react-dom.min.js" dependsOn "react-with-addons.js" commonJSName "ReactDOM", - "org.webjars" % "jquery" % versions.jQuery / "jquery.js" minified "jquery.min.js", - "org.webjars" % "bootstrap" % versions.bootstrap / "bootstrap.js" minified "bootstrap.min.js" dependsOn "jquery.js", - "org.webjars" % "chartjs" % versions.chartjs / "Chart.js" minified "Chart.min.js", - "org.webjars" % "log4javascript" % versions.log4js / "js/log4javascript_uncompressed.js" minified "js/log4javascript.js" + "org.webjars.npm" % "react" % v.react / "umd/react.development.js" minified "umd/react.production.min.js" commonJSName "React", + "org.webjars.npm" % "react-dom" % v.react / "umd/react-dom.development.js" minified "umd/react-dom.production.min.js" dependsOn "umd/react.development.js" commonJSName "ReactDOM", + "org.webjars.npm" % "react-dom" % v.react / "umd/react-dom-server.browser.development.js" minified "umd/react-dom-server.browser.production.min.js" dependsOn "umd/react-dom.development.js" commonJSName "ReactDOMServer", + "org.webjars.npm" % "jquery" % v.jQuery / "dist/jquery.js" minified "jquery.min.js", + "org.webjars.npm" % "bootstrap" % v.bootstrap / "bootstrap.js" minified "bootstrap.min.js" dependsOn "dist/jquery.js", + "org.webjars.bower" % "chartjs" % v.chartjs / "Chart.js" minified "Chart.min.js", + "org.webjars" % "log4javascript" % v.log4js / "js/log4javascript_uncompressed.js" minified "js/log4javascript.js" )) ``` @@ -49,7 +50,6 @@ lazy val ReleaseCmd = Command.command("release") { "set elideOptions in client := Seq()" :: state } -} ``` and enable it in the `server` project diff --git a/doc/en/routing.md b/doc/en/routing.md index bcf8f3b..c005118 100644 --- a/doc/en/routing.md +++ b/doc/en/routing.md @@ -36,12 +36,13 @@ uses Bootstrap CSS to provide a nice looking layout, but you can use whatever CS ```scala import japgolly.scalajs.react.vdom.html_<^._ val todoCountWrapper = SPACircuit.connect(_.todos.map(_.items.count(!_.completed)).toOption) +// base layout for all pages def layout(c: RouterCtl[Loc], r: Resolution[Loc]) = { <.div( // here we use plain Bootstrap class names as these are specific to the top level layout defined here - <.nav(^.className := "navbar navbar-inverse navbar-fixed-top", + <.nav(^.className := "navbar navbar-dark bg-dark fixed-top navbar-expand-md", <.div(^.className := "container", - <.div(^.className := "navbar-header", <.span(^.className := "navbar-brand", "SPA Tutorial")), + <.span(^.className := "navbar-brand", "SPA Tutorial"), <.div(^.className := "collapse navbar-collapse", // connect menu to model, because it needs to update when the number of open todos changes todoCountWrapper(proxy => MainMenu(c, r.page, proxy)) diff --git a/doc/en/sbt-build-definition.md b/doc/en/sbt-build-definition.md index bf873ca..0736b63 100644 --- a/doc/en/sbt-build-definition.md +++ b/doc/en/sbt-build-definition.md @@ -19,21 +19,20 @@ and resources shared between the client and server. In the context of this tutor In a more realistic application you would have your data models etc. defined here. ```scala -lazy val shared = (crossProject.crossType(CrossType.Pure) in file("shared")) +lazy val shared = (crossProject(JSPlatform, JVMPlatform).crossType(CrossType.Pure) in file("shared")) .settings( - scalaVersion := Settings.versions.scala, - libraryDependencies ++= Settings.sharedDependencies.value + scalaVersion := Settings.v.scala, + libraryDependencies ++= Settings.sharedDependencies.value ) // set up settings specific to the JS project - .jsConfigure(_ enablePlugins ScalaJSPlay) - .jsSettings(sourceMapsBase := baseDirectory.value / "..") + .jsConfigure(_ enablePlugins ScalaJSWeb) ``` The shared dependencies include libraries used by both client and server such as `autowire` and `boopickle` for client/server communication. ```scala val sharedDependencies = Def.setting(Seq( - "com.lihaoyi" %%% "autowire" % versions.autowire, - "me.chrons" %%% "boopickle" % versions.booPickle + "com.lihaoyi" %%% "autowire" % v.autowire, + "io.suzaku" %%% "boopickle" % v.booPickle )) ``` @@ -45,8 +44,8 @@ Client is defined as a normal Scala.js project by enabling the `ScalaJSPlugin` o lazy val client: Project = (project in file("client")) .settings( name := "client", - version := Settings.version, - scalaVersion := Settings.versions.scala, + version := Settings.v.app, + scalaVersion := Settings.v.scala, scalacOptions ++= Settings.scalacOptions, libraryDependencies ++= Settings.scalajsDependencies.value, // by default we do development build, no eliding @@ -54,27 +53,25 @@ lazy val client: Project = (project in file("client")) scalacOptions ++= elideOptions.value, jsDependencies ++= Settings.jsDependencies.value, // RuntimeDOM is needed for tests - jsDependencies += RuntimeDOM % "test", + jsEnv in Test := new org.scalajs.jsenv.jsdomnodejs.JSDOMNodeJSEnv, // yes, we want to package JS dependencies skip in packageJSDependencies := false, // use Scala.js provided launcher code to start the client app scalaJSUseMainModuleInitializer := true, scalaJSUseMainModuleInitializer in Test := false, - // must specify source maps location because we use pure CrossProject - sourceMapsDirectories += sharedJS.base / "..", // use uTest framework for tests - testFrameworks += new TestFramework("utest.runner.Framework") + testFrameworks += new TestFramework("utest.runner.Framework"), + dependencyOverrides ++= Settings.dependencyOverrides.value ) - .enablePlugins(ScalaJSPlugin, ScalaJSPlay) + .enablePlugins(ScalaJSPlugin, ScalaJSWeb) .dependsOn(sharedJS) ``` First few settings are normal Scala settings, but let's go through the remaining settings to explain what they do. ```scala - // by default we do development build, no eliding - elideOptions := Seq(), - scalacOptions ++= elideOptions.value, + // use eliding to drop some debug code in the production build + lazy val elideOptions = settingKey[Seq[String]]("Set limit for elidable functions") ``` Eliding is used to remove code that is not needed in the production build, such as debug logging. This setting is empty by default, but is enabled in the `release` command. @@ -82,7 +79,7 @@ the `release` command. ```scala jsDependencies ++= Settings.jsDependencies.value, // RuntimeDOM is needed for tests - jsDependencies += RuntimeDOM % "test", + jsEnv in Test := new org.scalajs.jsenv.jsdomnodejs.JSDOMNodeJSEnv, // yes, we want to package JS dependencies skip in packageJSDependencies := false, ``` @@ -98,18 +95,17 @@ Make sure you have installed [PhantomJS](http://phantomjs.org/) before running t This setting informs Scala.js plugin to generate a special `launcher.js` file, which is loaded last and invokes your `main` method. Using a launcher keeps your HTML template clean, as you don't need to specify the `main` function there. -```scala - // must specify source maps location because we use pure CrossProject - sourceMapsDirectories += sharedJS.base / "..", -``` -Because we are using a pure `CrossProject`, the source map directories have to be manually adjusted to reflect where the source files can be found. - ```scala // use uTest framework for tests testFrameworks += new TestFramework("utest.runner.Framework") ``` Lets SBT know that we are using uTest framework for tests. +```scala + dependencyOverrides ++= Settings.dependencyOverrides.value +``` +Fixes unresolved deps issue: https://github.com/webjars/webjars/issues/1789 + ```scala .enablePlugins(ScalaJSPlugin, ScalaJSPlay) .dependsOn(sharedJS) @@ -126,16 +122,21 @@ plugin, which is automatically included to all projects using `PlayScala` plugin lazy val server = (project in file("server")) .settings( name := "server", - version := Settings.version, - scalaVersion := Settings.versions.scala, + version := Settings.v.app, + scalaVersion := Settings.v.scala, scalacOptions ++= Settings.scalacOptions, - libraryDependencies ++= Settings.jvmDependencies.value, + libraryDependencies ++= Settings.jvmDependencies.value, + libraryDependencies += guice, commands += ReleaseCmd, + // triggers scalaJSPipeline when using compile or continuous compilation + compile in Compile := ((compile in Compile) dependsOn scalaJSPipeline).value, // connect to the client project scalaJSProjects := clients, - pipelineStages := Seq(scalaJSProd), + pipelineStages in Assets := Seq(scalaJSPipeline), + pipelineStages := Seq(digest, gzip), // compress CSS - LessKeys.compress in Assets := true + SassKeys.cssStyle in Assets := Minified, + dependencyOverrides ++= Settings.dependencyOverrides.value ) .enablePlugins(PlayScala) .disablePlugins(PlayLayoutPlugin) // use the standard directory layout instead of Play's custom @@ -152,15 +153,21 @@ We define a new SBT command `release` to run a sequence of commands to produce a ```scala // connect to the client project scalaJSProjects := clients, - pipelineStages := Seq(scalaJSProd), + pipelineStages in Assets := Seq(scalaJSPipeline), + pipelineStages := Seq(digest, gzip), ``` Let the plugin know where our client project is and enable Scala.js processing in the pipeline. ```scala // compress CSS - LessKeys.compress in Assets := true, + SassKeys.cssStyle in Assets := Minified, +``` +This instructs the `sbt-sassify` plugin to minify the produced CSS. + +```scala + dependencyOverrides ++= Settings.dependencyOverrides.value ``` -This instructs the `sbt-less` plugin to minify the produced CSS. +Fixes unresolved deps issue: https://github.com/webjars/webjars/issues/1789 ```scala diff --git a/doc/en/the-client.md b/doc/en/the-client.md index b3a5c30..932910f 100644 --- a/doc/en/the-client.md +++ b/doc/en/the-client.md @@ -28,10 +28,18 @@ Once the browser has loaded all the resources, it will call the `SPAMain().main( entry point of the application. The class itself is very simple, ```scala -@JSExport("SPAMain") -object SPAMain extends JSApp { +object SPAMain { + + @JSExportTopLevel("SPAMain") + protected def getInstance(): this.type = this + @JSExport - def main(): Unit = { + def main(args: Array[String]): Unit = { + log.warn("Application starting") + // send log messages also to the server + log.enableServerLogging("/logging") + log.info("This message goes to server as well") + // create stylesheet GlobalStyles.addToDocument() // create the router diff --git a/doc/en/using-resources-from-webjars.md b/doc/en/using-resources-from-webjars.md index 91b342b..df267a3 100644 --- a/doc/en/using-resources-from-webjars.md +++ b/doc/en/using-resources-from-webjars.md @@ -13,12 +13,13 @@ configuration in the `build.scala` file: ```scala /** Dependencies for external JS libs that are bundled into a single .js file according to dependency order */ val jsDependencies = Def.setting(Seq( - "org.webjars.bower" % "react" % versions.react / "react-with-addons.js" minified "react-with-addons.min.js" commonJSName "React", - "org.webjars.bower" % "react" % versions.react / "react-dom.js" minified "react-dom.min.js" dependsOn "react-with-addons.js" commonJSName "ReactDOM", - "org.webjars" % "jquery" % versions.jQuery / "jquery.js" minified "jquery.min.js", - "org.webjars" % "bootstrap" % versions.bootstrap / "bootstrap.js" minified "bootstrap.min.js" dependsOn "jquery.js", - "org.webjars" % "chartjs" % versions.chartjs / "Chart.js" minified "Chart.min.js", - "org.webjars" % "log4javascript" % versions.log4js / "js/log4javascript_uncompressed.js" minified "js/log4javascript.js" + "org.webjars.npm" % "react" % v.react / "umd/react.development.js" minified "umd/react.production.min.js" commonJSName "React", + "org.webjars.npm" % "react-dom" % v.react / "umd/react-dom.development.js" minified "umd/react-dom.production.min.js" dependsOn "umd/react.development.js" commonJSName "ReactDOM", + "org.webjars.npm" % "react-dom" % v.react / "umd/react-dom-server.browser.development.js" minified "umd/react-dom-server.browser.production.min.js" dependsOn "umd/react-dom.development.js" commonJSName "ReactDOMServer", + "org.webjars.npm" % "jquery" % v.jQuery / "dist/jquery.js" minified "jquery.min.js", + "org.webjars.npm" % "bootstrap" % v.bootstrap / "bootstrap.js" minified "bootstrap.min.js" dependsOn "dist/jquery.js", + "org.webjars.bower" % "chartjs" % v.chartjs / "Chart.js" minified "Chart.min.js", + "org.webjars" % "log4javascript" % v.log4js / "js/log4javascript_uncompressed.js" minified "js/log4javascript.js" )) ``` @@ -27,36 +28,36 @@ JavaScript file is selected. ## WebJar CSS/LESS -For extracting CSS files from WebJars you could use the method described below, but there is bit more convenient method that gives you [LESS](http://lesscss.org/) -processing as a bonus. First we'll need to add the [sbt-less](https://github.com/sbt/sbt-less) plugin into our `plugins.sbt` +For extracting CSS files from WebJars you could use the method described below, but there is bit more convenient method that gives you [SASS](https://sass-lang.com/) +processing as a bonus. First we'll need to add the [sbt-sassify](https://github.com/irundaia/sbt-sassify) plugin into our `plugins.sbt` ```scala -addSbtPlugin("com.typesafe.sbt" % "sbt-less" % "1.0.6") +addSbtPlugin("org.irundaia.sbt" % "sbt-sassify" % "1.4.13") ``` -The server project automatically enables the `sbt-web` and `sbt-less` plugins because it uses the `PlayScala` plugin. +The server project automatically enables the `sbt-web` and `sbt-sassify` plugins because it uses the `PlayScala` plugin. -We'll be storing LESS files under `src/main/assets/stylesheets` to keep them separated from directly copied resources. +We'll be storing SASS files under `src/main/assets/stylesheets` to keep them separated from directly copied resources. ```scala -LessKeys.compress in Assets := true, +SassKeys.cssStyle in Assets := Minified, ``` This tells the LESS compiler to minify the produced CSS. Next step is to create a `main.less` (yes, it has to be named exactly that) with references to CSS/LESS files inside the WebJars. ```css -@import "lib/bootstrap/less/bootstrap.less"; -@import "lib/font-awesome/less/font-awesome.less"; +@import "lib/bootstrap/scss/bootstrap.scss"; +@import "lib/fontawesome/scss/font-awesome.scss"; ``` -In this case we just import Bootstrap and Font Awesome LESS files because all other CSS styles are defined using ScalaCSS. Depending on the WebJar, -it may or may not contain LESS files in addition to the CSS file. With the LESS files you can easily -[configure the library](http://getbootstrap.com/css/#less) to your liking by defining CSS variables in your `main.less` file. +In this case we just import Bootstrap and Font Awesome SASS files because all other CSS styles are defined using ScalaCSS. Depending on the WebJar, +it may or may not contain SASS files in addition to the CSS file. With the SASS files you can easily +[configure the library](https://getbootstrap.com/docs/4.0/getting-started/theming/) to your liking by defining CSS variables in your `main.scss` file. ```css -@import "lib/bootstrap/less/bootstrap.less"; -@import "lib/font-awesome/less/font-awesome.less"; +@import "lib/bootstrap/scss/bootstrap.scss"; +@import "lib/fontawesome/scss/font-awesome.scss"; @brand-danger: #00534f; ``` diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..1eb8abb --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1900 @@ +{ + "name": "scalajs-spa", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "abab": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz", + "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==" + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "acorn": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", + "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==" + }, + "acorn-globals": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.2.tgz", + "integrity": "sha512-BbzvZhVtZP+Bs1J1HcwrQe8ycfO0wStkSGxuul3He3GkHOIZ6eTqOkPuw9IP1X3+IkOo4wiJmwkobzXYz4wewQ==", + "requires": { + "acorn": "^6.0.1", + "acorn-walk": "^6.0.1" + } + }, + "acorn-walk": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz", + "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==" + }, + "ajv": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", + "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "array-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=" + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=" + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "async-foreach": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", + "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=" + }, + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "requires": { + "inherits": "~2.0.0" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "browser-process-hrtime": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz", + "integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==" + }, + "browserify-transform-tools": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/browserify-transform-tools/-/browserify-transform-tools-1.7.0.tgz", + "integrity": "sha1-g+J3Ih9jJZvtLn6yooOpcKUB9MQ=", + "requires": { + "falafel": "^2.0.0", + "through": "^2.3.7" + } + }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cross-spawn": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", + "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", + "requires": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + }, + "cssify": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/cssify/-/cssify-0.7.0.tgz", + "integrity": "sha1-iWEa2PGMSI5nr77Lra4WXvSykPQ=", + "requires": { + "through": "~2.3.4" + } + }, + "cssom": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.6.tgz", + "integrity": "sha512-DtUeseGk9/GBW0hl0vVPpU22iHL6YB5BUX7ml1hB+GMpo0NX5G4voX3kdWiMSEguFtcW3Vh3djqNF4aIe6ne0A==" + }, + "cssstyle": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.2.2.tgz", + "integrity": "sha512-43wY3kl1CVQSvL7wUY1qXkxVGkStjpkDmVjiIKX8R97uhajy8Bybay78uOtqvh7Q5GK75dNPfW0geWjE6qQQow==", + "requires": { + "cssom": "0.3.x" + } + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "requires": { + "array-find-index": "^1.0.1" + } + }, + "d": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/d/-/d-0.1.1.tgz", + "integrity": "sha1-2hhMU10Y2O57oqoim5FACfrhEwk=", + "requires": { + "es5-ext": "~0.10.2" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "data-urls": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", + "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", + "requires": { + "abab": "^2.0.0", + "whatwg-mimetype": "^2.2.0", + "whatwg-url": "^7.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "domexception": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", + "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", + "requires": { + "webidl-conversions": "^4.0.2" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es5-ext": { + "version": "0.10.50", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.50.tgz", + "integrity": "sha512-KMzZTPBkeQV/JcSQhI5/z6d9VWJ3EnQ194USTUwIYZ2ZbpN8+SGXQKt1h68EX44+qt+Fzr8DO17vnxrw7c3agw==", + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.1", + "next-tick": "^1.0.0" + }, + "dependencies": { + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + } + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + }, + "dependencies": { + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + } + } + }, + "es6-map": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.3.tgz", + "integrity": "sha1-/ljGZUxqzVTkOXzbcjedWbatWJQ=", + "requires": { + "d": "~0.1.1", + "es5-ext": "~0.10.8", + "es6-iterator": "2", + "es6-set": "~0.1.3", + "es6-symbol": "~3.0.1", + "event-emitter": "~0.3.4" + } + }, + "es6-set": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", + "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", + "requires": { + "d": "1", + "es5-ext": "~0.10.14", + "es6-iterator": "~2.0.1", + "es6-symbol": "3.1.1", + "event-emitter": "~0.3.5" + }, + "dependencies": { + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + } + } + }, + "es6-symbol": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.0.2.tgz", + "integrity": "sha1-HpKIeMb15jVBYltLtN9K8H0VQhk=", + "requires": { + "d": "~0.1.1", + "es5-ext": "~0.10.10" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "escodegen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.1.tgz", + "integrity": "sha512-JwiqFD9KdGVVpeuRa68yU3zZnBEOcPs0nKW7wZzXky8Z7tffdYUHbe11bPCV5jYlK6DVdKLWLm0f5I/QlL0Kmw==", + "requires": { + "esprima": "^3.1.3", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + } + }, + "esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=" + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" + }, + "event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + }, + "dependencies": { + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "falafel": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/falafel/-/falafel-2.1.0.tgz", + "integrity": "sha1-lrsXdh2rqU9G0AFzizzt86Z/4Gw=", + "requires": { + "acorn": "^5.0.0", + "foreach": "^2.0.5", + "isarray": "0.0.1", + "object-keys": "^1.0.6" + }, + "dependencies": { + "acorn": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==" + } + } + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + } + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "gaze": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", + "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", + "requires": { + "globule": "^1.0.0" + } + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=" + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "globule": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz", + "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==", + "requires": { + "glob": "~7.1.1", + "lodash": "~4.17.10", + "minimatch": "~3.0.2" + } + }, + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==" + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==" + }, + "html-encoding-sniffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", + "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", + "requires": { + "whatwg-encoding": "^1.0.1" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "in-publish": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz", + "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=" + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "requires": { + "repeating": "^2.0.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" + }, + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=" + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "js-base64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz", + "integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "jsdom": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-15.1.1.tgz", + "integrity": "sha512-cQZRBB33arrDAeCrAEWn1U3SvrvC8XysBua9Oqg1yWrsY/gYcusloJC3RZJXuY5eehSCmws8f2YeliCqGSkrtQ==", + "requires": { + "abab": "^2.0.0", + "acorn": "^6.1.1", + "acorn-globals": "^4.3.2", + "array-equal": "^1.0.0", + "cssom": "^0.3.6", + "cssstyle": "^1.2.2", + "data-urls": "^1.1.0", + "domexception": "^1.0.1", + "escodegen": "^1.11.1", + "html-encoding-sniffer": "^1.0.2", + "nwsapi": "^2.1.4", + "parse5": "5.1.0", + "pn": "^1.1.0", + "request": "^2.88.0", + "request-promise-native": "^1.0.7", + "saxes": "^3.1.9", + "symbol-tree": "^3.2.2", + "tough-cookie": "^3.0.1", + "w3c-hr-time": "^1.0.1", + "w3c-xmlserializer": "^1.1.2", + "webidl-conversions": "^4.0.2", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^7.0.0", + "ws": "^7.0.0", + "xml-name-validator": "^3.0.0" + } + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "requires": { + "invert-kv": "^1.0.0" + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + }, + "lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=" + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=" + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + } + }, + "mime-db": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" + }, + "mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "requires": { + "mime-db": "1.40.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + } + } + }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" + }, + "node-gyp": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", + "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", + "requires": { + "fstream": "^1.0.0", + "glob": "^7.0.3", + "graceful-fs": "^4.1.2", + "mkdirp": "^0.5.0", + "nopt": "2 || 3", + "npmlog": "0 || 1 || 2 || 3 || 4", + "osenv": "0", + "request": "^2.87.0", + "rimraf": "2", + "semver": "~5.3.0", + "tar": "^2.0.0", + "which": "1" + }, + "dependencies": { + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" + } + } + }, + "node-sass": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.12.0.tgz", + "integrity": "sha512-A1Iv4oN+Iel6EPv77/HddXErL2a+gZ4uBeZUy+a8O35CFYTXhgA8MgLCWBtwpGZdCvTvQ9d+bQxX/QC36GDPpQ==", + "requires": { + "async-foreach": "^0.1.3", + "chalk": "^1.1.1", + "cross-spawn": "^3.0.0", + "gaze": "^1.0.0", + "get-stdin": "^4.0.1", + "glob": "^7.0.3", + "in-publish": "^2.0.0", + "lodash": "^4.17.11", + "meow": "^3.7.0", + "mkdirp": "^0.5.1", + "nan": "^2.13.2", + "node-gyp": "^3.8.0", + "npmlog": "^4.0.0", + "request": "^2.88.0", + "sass-graph": "^2.2.4", + "stdout-stream": "^1.4.0", + "true-case-path": "^1.0.2" + } + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "requires": { + "abbrev": "1" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "nwsapi": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.1.4.tgz", + "integrity": "sha512-iGfd9Y6SFdTNldEy2L0GUhcarIutFmk+MPWIn9dmj8NMIup03G08uUF2KGbbmv/Ux4RT0VZJoP/sVbWA6d/VIw==" + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "requires": { + "lcid": "^1.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "^1.2.0" + } + }, + "parse5": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", + "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==" + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "^2.0.0" + } + }, + "pn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", + "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==" + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + }, + "psl": { + "version": "1.1.33", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.33.tgz", + "integrity": "sha512-LTDP2uSrsc7XCb5lO7A8BI1qYxRe/8EqlRvMeEl6rsnYAqDOl8xHR+8lSAIVfrNaSAlTPTNOCgNjWcoUL3AZsw==" + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + } + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "requires": { + "is-finite": "^1.0.0" + } + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + } + } + } + }, + "request-promise-core": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.2.tgz", + "integrity": "sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==", + "requires": { + "lodash": "^4.17.11" + } + }, + "request-promise-native": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.7.tgz", + "integrity": "sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w==", + "requires": { + "request-promise-core": "1.1.2", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + }, + "dependencies": { + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + } + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, + "resolve": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz", + "integrity": "sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==", + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-bower": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/resolve-bower/-/resolve-bower-0.0.1.tgz", + "integrity": "sha1-DOaeuxlgQwKxY6vZ2NjaFUVV0PI=" + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sass-graph": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz", + "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=", + "requires": { + "glob": "^7.0.0", + "lodash": "^4.0.0", + "scss-tokenizer": "^0.2.3", + "yargs": "^7.0.0" + } + }, + "sass-module-importer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/sass-module-importer/-/sass-module-importer-1.4.0.tgz", + "integrity": "sha1-7QQA+cGkDwF3kKF3LaC+tUMvkUk=", + "requires": { + "es6-map": "0.1.3", + "glob": "^7.1.1", + "object-assign": "4.0.1", + "resolve": "1.1.7", + "resolve-bower": "0.0.1" + }, + "dependencies": { + "object-assign": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.0.1.tgz", + "integrity": "sha1-mVBEVsNZi1ytT8WcJuipuxB/4L0=" + }, + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=" + } + } + }, + "sassify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/sassify/-/sassify-4.0.1.tgz", + "integrity": "sha512-fFLEYTYZyfwq1uN2WLE+nGgcuI1ouQ1Bve5LwUcGwnXz0d2/vL0ZSewsM7UicbXGkYLEFzN7KXvUK6JVg5F6Sg==", + "requires": { + "browserify-transform-tools": "^1.3.3", + "cssify": "^0.7.0", + "node-sass": "^4.5.3", + "sass-module-importer": "^1.2.1" + } + }, + "saxes": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-3.1.10.tgz", + "integrity": "sha512-G/mVZCCGhJqgS+I7wT5gBHyTNXLe2SQcu2qmhwl1OKcSHyJEXKQY2CLT+qWIxV+m6uiGMLfSOJGLQQHhklIeEQ==", + "requires": { + "xmlchars": "^1.3.1" + } + }, + "scss-tokenizer": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", + "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", + "requires": { + "js-base64": "^2.1.8", + "source-map": "^0.4.2" + }, + "dependencies": { + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==" + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz", + "integrity": "sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA==" + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "stdout-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", + "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", + "requires": { + "readable-stream": "^2.0.1" + } + }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "requires": { + "get-stdin": "^4.0.1" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + }, + "tar": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", + "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", + "requires": { + "block-stream": "*", + "fstream": "^1.0.12", + "inherits": "2" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "tough-cookie": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", + "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", + "requires": { + "ip-regex": "^2.1.0", + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", + "requires": { + "punycode": "^2.1.0" + } + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=" + }, + "true-case-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", + "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", + "requires": { + "glob": "^7.1.2" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "type": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/type/-/type-1.0.1.tgz", + "integrity": "sha512-MAM5dBMJCJNKs9E7JXo4CXRAansRfG0nlJxW7Wf6GZzSOvH31zClSaHdIMWLehe/EGMBkqeC55rrkaOr5Oo7Nw==" + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "requires": { + "punycode": "^2.1.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "w3c-hr-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", + "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", + "requires": { + "browser-process-hrtime": "^0.1.2" + } + }, + "w3c-xmlserializer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz", + "integrity": "sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==", + "requires": { + "domexception": "^1.0.1", + "webidl-conversions": "^4.0.2", + "xml-name-validator": "^3.0.0" + } + }, + "webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" + }, + "whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "requires": { + "iconv-lite": "0.4.24" + } + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" + }, + "whatwg-url": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz", + "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=" + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "ws": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.0.1.tgz", + "integrity": "sha512-ILHfMbuqLJvnSgYXLgy4kMntroJpe8hT41dOVWM8bxRuw6TK4mgMp9VJUNsZTEc5Bh+Mbs0DJT4M0N+wBG9l9A==", + "requires": { + "async-limiter": "^1.0.0" + } + }, + "xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" + }, + "xmlchars": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-1.3.1.tgz", + "integrity": "sha512-tGkGJkN8XqCod7OT+EvGYK5Z4SfDQGD30zAa58OcnAa0RRWgzUEK72tkXhsX1FZd+rgnhRxFtmO+ihkp8LHSkw==" + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + }, + "yargs": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", + "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", + "requires": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^5.0.0" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" + } + } + }, + "yargs-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", + "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", + "requires": { + "camelcase": "^3.0.0" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..5c48589 --- /dev/null +++ b/package.json @@ -0,0 +1,8 @@ +{ + "name": "scalajs-spa", + "version": "1.0.0", + "dependencies": { + "jsdom": "^15.1.1", + "sassify": "^4.0.1" + } +} diff --git a/project/Settings.scala b/project/Settings.scala index 783301c..6109e79 100644 --- a/project/Settings.scala +++ b/project/Settings.scala @@ -1,80 +1,98 @@ import sbt._ import org.scalajs.sbtplugin.ScalaJSPlugin.autoImport._ +import org.portablescala.sbtplatformdeps.PlatformDepsPlugin.autoImport._ /** - * Application settings. Configure the build for your application here. - * You normally don't have to touch the actual build definition after this. - */ + * Application settings. Configure the build for your application here. + * You normally don't have to touch the actual build definition after this. + */ object Settings { + + /** Declare global dependency versions here to avoid mismatches in multi part dependencies */ + object v { + /** The version of your application */ + val app = "1.1.5" + + val scala = "2.12.8" + val scalajsDom = "0.9.7" + val scalajsReact = "1.4.2" + val scalaCSS = "0.5.6" + val log4js = "1.4.13-1" + val autowire = "0.2.6" + val booPickle = "1.3.1" + val diode = "1.1.5" + val diodeReact = "1.1.5.142" + val uTest = "0.7.1" + + val react = "16.8.6" + val jQuery = "3.4.1" + val bootstrap = "4.3.1" + val chartjs = "2.6.0" + val fontawesome = "4.7.0" + + val scalajsScripts = "1.1.2" + val silencer = "1.4.1" + } + /** The name of your application */ val name = "scalajs-spa" - /** The version of your application */ - val version = "1.1.5" - /** Options for the scala compiler */ val scalacOptions = Seq( "-Xlint", "-unchecked", "-deprecation", - "-feature" + "-feature", + // silence all warnings on autogenerated files + "-P:silencer:pathFilters=target/.*" ) - /** Declare global dependency versions here to avoid mismatches in multi part dependencies */ - object versions { - val scala = "2.11.11" - val scalaDom = "0.9.3" - val scalajsReact = "1.1.0" - val scalaCSS = "0.5.3" - val log4js = "1.4.10" - val autowire = "0.2.6" - val booPickle = "1.2.6" - val diode = "1.1.2" - val uTest = "0.4.7" - - val react = "15.6.1" - val jQuery = "1.11.1" - val bootstrap = "3.3.6" - val chartjs = "2.1.3" - - val scalajsScripts = "1.0.0" - } - /** - * These dependencies are shared between JS and JVM projects - * the special %%% function selects the correct version for each project - */ + * These dependencies are shared between JS and JVM projects + * the special %%% function selects the correct version for each project + */ val sharedDependencies = Def.setting(Seq( - "com.lihaoyi" %%% "autowire" % versions.autowire, - "io.suzaku" %%% "boopickle" % versions.booPickle + "com.lihaoyi" %%% "autowire" % v.autowire, + "io.suzaku" %%% "boopickle" % v.booPickle )) /** Dependencies only used by the JVM project */ val jvmDependencies = Def.setting(Seq( - "com.vmunier" %% "scalajs-scripts" % versions.scalajsScripts, - "org.webjars" % "font-awesome" % "4.3.0-1" % Provided, - "org.webjars" % "bootstrap" % versions.bootstrap % Provided, - "com.lihaoyi" %% "utest" % versions.uTest % Test + "com.vmunier" %% "scalajs-scripts" % v.scalajsScripts, + "org.webjars.bower" % "fontawesome" % v.fontawesome % Provided, + "org.webjars.npm" % "bootstrap" % v.bootstrap % Provided, + "com.lihaoyi" %% "utest" % v.uTest % Test, + compilerPlugin("com.github.ghik" %% "silencer-plugin" % v.silencer), + "com.github.ghik" %% "silencer-lib" % v.silencer % Provided )) /** Dependencies only used by the JS project (note the use of %%% instead of %%) */ val scalajsDependencies = Def.setting(Seq( - "com.github.japgolly.scalajs-react" %%% "core" % versions.scalajsReact, - "com.github.japgolly.scalajs-react" %%% "extra" % versions.scalajsReact, - "com.github.japgolly.scalacss" %%% "ext-react" % versions.scalaCSS, - "io.suzaku" %%% "diode" % versions.diode, - "io.suzaku" %%% "diode-react" % versions.diode, - "org.scala-js" %%% "scalajs-dom" % versions.scalaDom, - "com.lihaoyi" %%% "utest" % versions.uTest % Test + "com.github.japgolly.scalajs-react" %%% "core" % v.scalajsReact, + "com.github.japgolly.scalajs-react" %%% "extra" % v.scalajsReact, + "com.github.japgolly.scalacss" %%% "ext-react" % v.scalaCSS, + "io.suzaku" %%% "diode" % v.diode, + "io.suzaku" %%% "diode-react" % v.diodeReact, + "org.scala-js" %%% "scalajs-dom" % v.scalajsDom, + "com.lihaoyi" %%% "utest" % v.uTest % Test, + compilerPlugin("com.github.ghik" %% "silencer-plugin" % v.silencer), + "com.github.ghik" %% "silencer-lib" % v.silencer % Provided )) /** Dependencies for external JS libs that are bundled into a single .js file according to dependency order */ val jsDependencies = Def.setting(Seq( - "org.webjars.bower" % "react" % versions.react / "react-with-addons.js" minified "react-with-addons.min.js" commonJSName "React", - "org.webjars.bower" % "react" % versions.react / "react-dom.js" minified "react-dom.min.js" dependsOn "react-with-addons.js" commonJSName "ReactDOM", - "org.webjars" % "jquery" % versions.jQuery / "jquery.js" minified "jquery.min.js", - "org.webjars" % "bootstrap" % versions.bootstrap / "bootstrap.js" minified "bootstrap.min.js" dependsOn "jquery.js", - "org.webjars" % "chartjs" % versions.chartjs / "Chart.js" minified "Chart.min.js", - "org.webjars" % "log4javascript" % versions.log4js / "js/log4javascript_uncompressed.js" minified "js/log4javascript.js" + "org.webjars.npm" % "react" % v.react / "umd/react.development.js" minified "umd/react.production.min.js" commonJSName "React", + "org.webjars.npm" % "react-dom" % v.react / "umd/react-dom.development.js" minified "umd/react-dom.production.min.js" dependsOn "umd/react.development.js" commonJSName "ReactDOM", + "org.webjars.npm" % "react-dom" % v.react / "umd/react-dom-server.browser.development.js" minified "umd/react-dom-server.browser.production.min.js" dependsOn "umd/react-dom.development.js" commonJSName "ReactDOMServer", + "org.webjars.npm" % "jquery" % v.jQuery / "dist/jquery.js" minified "jquery.min.js", + "org.webjars.npm" % "bootstrap" % v.bootstrap / "bootstrap.js" minified "bootstrap.min.js" dependsOn "dist/jquery.js", + "org.webjars.bower" % "chartjs" % v.chartjs / "Chart.js" minified "Chart.min.js", + "org.webjars" % "log4javascript" % v.log4js / "js/log4javascript_uncompressed.js" minified "js/log4javascript.js" + )) + + /** Fixes unresolved deps issue: https://github.com/webjars/webjars/issues/1789 **/ + val dependencyOverrides = Def.setting(Seq( + "org.webjars.npm" % "js-tokens" % "4.0.0", + "org.webjars.npm" % "scheduler" % "0.14.0" )) } diff --git a/project/build.properties b/project/build.properties index 512c836..c0bab04 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=0.13.15 +sbt.version=1.2.8 diff --git a/project/plugins.sbt b/project/plugins.sbt index 59dc4d7..d9393d3 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,16 +1,18 @@ // repository for Typesafe plugins -resolvers += "Typesafe Releases" at "http://repo.typesafe.com/typesafe/releases/" +resolvers += Resolver.typesafeRepo("releases") -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.18") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.28") -addSbtPlugin("com.typesafe.sbt" % "sbt-less" % "1.0.6") +addSbtPlugin("com.vmunier" % "sbt-web-scalajs" % "1.0.9-0.6") -addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.0.0") +addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "0.6.0") -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.5.15") +addSbtPlugin("org.irundaia.sbt" % "sbt-sassify" % "1.4.13") -addSbtPlugin("com.vmunier" % "sbt-web-scalajs" % "1.0.5") +addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.3.22") -addSbtPlugin("com.typesafe.sbt" % "sbt-digest" % "1.1.0") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.7.3") -addSbtPlugin("com.typesafe.sbt" % "sbt-gzip" % "1.0.0") +addSbtPlugin("com.typesafe.sbt" % "sbt-digest" % "1.1.4") + +addSbtPlugin("com.typesafe.sbt" % "sbt-gzip" % "1.0.2") diff --git a/server/src/main/assets/stylesheets/main.less b/server/src/main/assets/stylesheets/main.less deleted file mode 100644 index 4927716..0000000 --- a/server/src/main/assets/stylesheets/main.less +++ /dev/null @@ -1,2 +0,0 @@ -@import "lib/bootstrap/less/bootstrap.less"; -@import "lib/font-awesome/less/font-awesome.less"; diff --git a/server/src/main/assets/stylesheets/main.scss b/server/src/main/assets/stylesheets/main.scss new file mode 100644 index 0000000..b741b38 --- /dev/null +++ b/server/src/main/assets/stylesheets/main.scss @@ -0,0 +1,2 @@ +@import "lib/bootstrap/scss/bootstrap.scss"; +@import "lib/fontawesome/scss/font-awesome.scss"; diff --git a/server/src/main/resources/application.conf b/server/src/main/resources/application.conf index 9851fb0..977a1e1 100644 --- a/server/src/main/resources/application.conf +++ b/server/src/main/resources/application.conf @@ -6,3 +6,8 @@ application.cdn=${?APPLICATION_CDN} spatutorial { } + +play.filters.csp { + contentSecurityPolicy = "connect-src 'self'" +} +play.filters.disabled+=play.filters.csrf.CSRFFilter \ No newline at end of file diff --git a/server/src/main/resources/routes b/server/src/main/resources/routes index d714708..b9ee93d 100644 --- a/server/src/main/resources/routes +++ b/server/src/main/resources/routes @@ -6,7 +6,7 @@ GET / controllers.Application.index # Map static resources from the /public folder to the /assets URL path -GET /assets/fonts/*file controllers.Assets.at(path="/public/lib/font-awesome/fonts", file) +GET /assets/fonts/*file controllers.Assets.at(path="/public/lib/fontawesome/fonts", file) GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset) # Autowire calls diff --git a/server/src/main/scala/controllers/Application.scala b/server/src/main/scala/controllers/Application.scala index f17462d..7cf99dc 100644 --- a/server/src/main/scala/controllers/Application.scala +++ b/server/src/main/scala/controllers/Application.scala @@ -3,8 +3,8 @@ package controllers import java.nio.ByteBuffer import boopickle.Default._ -import com.google.inject.Inject -import play.api.{Configuration, Environment} +import javax.inject.Inject +import play.api.{Configuration} import play.api.mvc._ import services.ApiService import spatutorial.shared.Api @@ -16,7 +16,7 @@ object Router extends autowire.Server[ByteBuffer, Pickler, Pickler] { override def write[R: Pickler](r: R) = Pickle.intoBytes(r) } -class Application @Inject() (implicit val config: Configuration, env: Environment) extends Controller { +class Application @Inject() (implicit val config: Configuration) extends InjectedController { val apiService = new ApiService() def index = Action { diff --git a/server/src/main/twirl/views/index.scala.html b/server/src/main/twirl/views/index.scala.html index c0969dd..f41aa79 100644 --- a/server/src/main/twirl/views/index.scala.html +++ b/server/src/main/twirl/views/index.scala.html @@ -1,4 +1,4 @@ -@(title: String)(implicit config: play.api.Configuration, env: play.api.Environment) +@(title: String)(implicit config: play.api.Configuration) @import views.html.tags._ @@ -8,7 +8,7 @@ @title - + diff --git a/server/src/main/twirl/views/tags/_asset.scala.html b/server/src/main/twirl/views/tags/_asset.scala.html index 5eea1af..e569258 100644 --- a/server/src/main/twirl/views/tags/_asset.scala.html +++ b/server/src/main/twirl/views/tags/_asset.scala.html @@ -1 +1 @@ -@(path: String)(implicit config: play.api.Configuration)@{config.getString("application.cdn").getOrElse("") + routes.Assets.versioned(path)} \ No newline at end of file +@(path: String)(implicit config: play.api.Configuration)@{config.getOptional[String]("application.cdn").getOrElse("") + routes.Assets.versioned(path)} \ No newline at end of file