japgolly.scalajs.react.util.ConsoleHijack.scala Maven / Gradle / Ivy
The newest version!
package japgolly.scalajs.react.util
import scala.scalajs.js
final class ConsoleHijack(val config: ConsoleHijack.Config) {
import ConsoleHijack._
import ConsoleHijack.Internals._
def ++(b: ConsoleHijack): ConsoleHijack =
new ConsoleHijack(config ++ b.config)
/** Install these hijacks into `console`, execute the given procedure,
* then restore `console` to the state that it was before this is called.
*/
def apply[A](a: => A): A = {
val undo = new js.Array[() => Unit]
try {
_install(undo)
a
} finally
undo.foreach(_())
}
/** Install these hijacks into `console`.
*
* @return A procedure to restore `console` to the state that it was before this is called.
*/
def install(): () => Unit = {
val undo = new js.Array[() => Unit]
_install(undo)
() => undo.foreach(_())
}
private def _install(undo: js.Array[() => Unit]): Unit = {
val console = js.Dynamic.global.console
def setHandler(m: Method, h: JsVarargsFn): () => Unit = () => {
console.updateDynamic(m.name)(h)
}
config.foreach { case (method, handler) =>
val orig = console.selectDynamic(method.name).asInstanceOf[JsVarargsFn]
undo.push(setHandler(method, orig))
setHandler(method, mkHandler(handler, orig))()
}
}
}
object ConsoleHijack {
lazy val fatalReactWarnings: ConsoleHijack =
Error.handleWith { i =>
if (i.msg.startsWith("Warning: "))
i.throwException()
else
i.fallthrough()
}
def apply(cfg: (Method, Handler)*): ConsoleHijack =
apply(cfg.toMap)
def apply(cfg: Config): ConsoleHijack =
new ConsoleHijack(cfg)
type Config = Map[Method, Handler]
sealed abstract class Method(final val name: String) {
final def handleWith(h: Handler): ConsoleHijack =
ConsoleHijack(this -> h)
final def throwException =
handleWith(Handler.throwException)
}
case object Debug extends Method("debug")
case object Error extends Method("error")
case object Info extends Method("info")
case object Log extends Method("log")
case object Warn extends Method("warn")
case object Trace extends Method("trace")
final case class Intercept(args: Seq[Any], fallthrough: () => Unit) {
override def toString = msg
lazy val msg: String =
if (args.isEmpty)
""
else {
@inline def default = args.mkString
args.head match {
case fmt: String =>
try
Internals.vsprintf(fmt, js.Array(args.tail: _*))
catch {
case _: Throwable => default
}
case _ => default
}
}
def throwException(): Nothing =
throw new js.JavaScriptException(msg)
}
type Handler = Intercept => Unit
object Handler {
@inline def apply(h: Handler): Handler = h
def throwException: Handler =
apply(_.throwException())
}
private object Internals {
type VsprintfFn = js.Function2[String, js.Array[Any], String]
lazy val vsprintf: VsprintfFn = {
// https://github.com/alexei/sprintf.js/blob/master/dist/sprintf.min.js
val code = """
!function(){"use strict";var g={not_string:/[^s]/,not_bool:/[^t]/,not_type:/[^T]/,not_primitive:/[^v]/,number:/[diefg]/,numeric_arg:/[bcdiefguxX]/,json:/[j]/,not_json:/[^j]/,text:/^[^\x25]+/,modulo:/^\x25{2}/,placeholder:/^\x25(?:([1-9]\d*)\$|\(([^)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-gijostTuvxX])/,key:/^([a-z_][a-z_\d]*)/i,key_access:/^\.([a-z_][a-z_\d]*)/i,index_access:/^\[(\d+)\]/,sign:/^[+-]/};function y(e){return function(e,t){var r,n,i,s,a,o,p,c,l,u=1,f=e.length,d="";for(n=0;n>>0).toString(8);break;case"s":r=String(r),r=s.precision?r.substring(0,s.precision):r;break;case"t":r=String(!!r),r=s.precision?r.substring(0,s.precision):r;break;case"T":r=Object.prototype.toString.call(r).slice(8,-1).toLowerCase(),r=s.precision?r.substring(0,s.precision):r;break;case"u":r=parseInt(r,10)>>>0;break;case"v":r=r.valueOf(),r=s.precision?r.substring(0,s.precision):r;break;case"x":r=(parseInt(r,10)>>>0).toString(16);break;case"X":r=(parseInt(r,10)>>>0).toString(16).toUpperCase()}g.json.test(s.type)?d+=r:(!g.number.test(s.type)||c&&!s.sign?l="":(l=c?"+":"-",r=r.toString().replace(g.sign,"")),o=s.pad_char?"0"===s.pad_char?"0":s.pad_char.charAt(1):" ",p=s.width-(l+r).length,a=s.width&&0
val i = Intercept(args, () => orig(args: _*))
f(i)
}
}
}