Support code hot reloading for JavaScript projects (#7362)

* Support code hot reloading for JavaScript projects

* Add some missing JavaScript symbols and APIs

* fix the Travis build

* (review changes) remove the js type from the standard library as it doesn't follow NEP-1

* more additions to the DOM module

* Follow NEP-1 in jsffi; spell 'hot code reloading' correctly

* introduce a jscore module

* Document jscore module.

* readded js type

* Remove the '$' operator that doesn't behave
This commit is contained in:
zah
2018-04-13 20:08:43 +03:00
committed by Andreas Rumpf
parent 1d1d6f39a3
commit e3037a2f33
14 changed files with 285 additions and 68 deletions

91
lib/js/jscore.nim Normal file
View File

@@ -0,0 +1,91 @@
## This module wraps core JavaScript functions.
##
## Unless your application has very
## specific requirements and solely targets JavaScript, you should be using
## the relevant functions in the ``math``, ``json``, and ``times`` stdlib
## modules instead.
when not defined(js) and not defined(Nimdoc):
{.error: "This module only works on the JavaScript platform".}
type
MathLib* = ref object
JsonLib* = ref object
DateLib* = ref object
DateTime* = ref object
var
Math* {.importc, nodecl.}: MathLib
Date* {.importc, nodecl.}: DateLib
JSON* {.importc, nodecl.}: JsonLib
{.push importcpp.}
# Math library
proc abs*(m: MathLib, a: SomeNumber): SomeNumber
proc acos*(m: MathLib, a: SomeNumber): float
proc acosh*(m: MathLib, a: SomeNumber): float
proc asin*(m: MathLib, a: SomeNumber): float
proc asinh*(m: MathLib, a: SomeNumber): float
proc atan*(m: MathLib, a: SomeNumber): float
proc atan2*(m: MathLib, a: SomeNumber): float
proc atanh*(m: MathLib, a: SomeNumber): float
proc cbrt*(m: MathLib, f: SomeReal): SomeReal
proc ceil*(m: MathLib, f: SomeReal): SomeReal
proc clz32*(m: MathLib, f: SomeInteger): int
proc cos*(m: MathLib, a: SomeNumber): float
proc cosh*(m: MathLib, a: SomeNumber): float
proc exp*(m: MathLib, a: SomeNumber): float
proc expm1*(m: MathLib, a: SomeNumber): float
proc floor*(m: MathLib, f: SomeReal): int
proc fround*(m: MathLib, f: SomeReal): float32
proc hypot*(m: MathLib, args: varargs[distinct SomeNumber]): float
proc imul*(m: MathLib, a, b: int32): int32
proc log*(m: MathLib, a: SomeNumber): float
proc log10*(m: MathLib, a: SomeNumber): float
proc log1p*(m: MathLib, a: SomeNumber): float
proc log2*(m: MathLib, a: SomeNumber): float
proc max*(m: MathLib, a, b: SomeNumber): SomeNumber
proc min*[T: SomeNumber | JsRoot](m: MathLib, a, b: T): T
proc pow*(m: MathLib, a, b: distinct SomeNumber): float
proc random*(m: MathLib): float
proc round*(m: MathLib, f: SomeReal): int
proc sign*(m: MathLib, f: SomeNumber): int
proc sin*(m: MathLib, a: SomeNumber): float
proc sinh*(m: MathLib, a: SomeNumber): float
proc sqrt*(m: MathLib, f: SomeReal): SomeReal
proc tan*(m: MathLib, a: SomeNumber): float
proc tanh*(m: MathLib, a: SomeNumber): float
proc trunc*(m: MathLib, f: SomeReal): int
# Date library
proc now*(d: DateLib): int
proc UTC*(d: DateLib): int
proc parse*(d: DateLib, s: cstring): int
proc newDate*(): DateTime {.
importcpp: "new Date()".}
proc newDate*(date: int|string): DateTime {.
importcpp: "new Date(#)".}
proc newDate*(year, month, day, hours, minutes,
seconds, milliseconds: int): DateTime {.
importcpp: "new Date(#,#,#,#,#,#,#)".}
proc getDay*(d: DateTime): int
proc getFullYear*(d: DateTime): int
proc getHours*(d: DateTime): int
proc getMilliseconds*(d: DateTime): int
proc getMinutes*(d: DateTime): int
proc getMonth*(d: DateTime): int
proc getSeconds*(d: DateTime): int
proc getYear*(d: DateTime): int
proc getTime*(d: DateTime): int
proc toString*(d: DateTime): cstring
#JSON library
proc stringify*(l: JsonLib, s: JsRoot): cstring
proc parse*(l: JsonLib, s: cstring): JsRoot
{.pop.}

View File

@@ -70,22 +70,31 @@ template mangleJsName(name: cstring): cstring =
"mangledName" & $nameCounter
type
JsRoot* = ref object of RootObj
## Root type of both JsObject and JsAssoc
JsObject* = ref object of JsRoot
## Dynamically typed wrapper around a JavaScript object.
JsAssoc*[K, V] = ref object of JsRoot
## Statically typed wrapper around a JavaScript object.
NotString = concept c
c isnot string
js* = JsObject
var jsarguments* {.importc: "arguments", nodecl}: JsObject
## JavaScript's arguments pseudo-variable
var
jsArguments* {.importc: "arguments", nodecl}: JsObject
## JavaScript's arguments pseudo-variable
jsNull* {.importc: "null", nodecl.}: JsObject
## JavaScript's null literal
jsUndefined* {.importc: "undefined", nodecl.}: JsObject
## JavaScript's undefined literal
jsDirname* {.importc: "__dirname", nodecl.}: cstring
## JavaScript's __dirname pseudo-variable
jsFilename* {.importc: "__filename", nodecl.}: cstring
## JavaScript's __filename pseudo-variable
# New
proc newJsObject*: JsObject {. importcpp: "{@}" .}
## Creates a new empty JsObject
proc newJsAssoc*[K, V]: JsAssoc[K, V] {. importcpp: "{@}" .}
## Creates a new empty JsAssoc with key type `K` and value type `V`.
@@ -97,13 +106,16 @@ proc hasOwnProperty*(x: JsObject, prop: cstring): bool
proc jsTypeOf*(x: JsObject): cstring {. importcpp: "typeof(#)" .}
## Returns the name of the JsObject's JavaScript type as a cstring.
proc jsnew*(x: auto): JsObject {.importcpp: "(new #)".}
proc jsNew*(x: auto): JsObject {.importcpp: "(new #)".}
## Turns a regular function call into an invocation of the
## JavaScript's `new` operator
proc jsdelete*(x: auto): JsObject {.importcpp: "(delete #)".}
proc jsDelete*(x: auto): JsObject {.importcpp: "(delete #)".}
## JavaScript's `delete` operator
proc require*(module: cstring): JsObject {.importc.}
## JavaScript's `require` function
# Conversion to and from JsObject
proc to*(x: JsObject, T: typedesc): T {. importcpp: "(#)" .}
## Converts a JsObject `x` to type `T`.

View File

@@ -649,6 +649,11 @@ when defined(nimNewRuntime):
ESynch: Exception
].}
when defined(js) or defined(nimdoc):
type
JsRoot* = ref object of RootObj
## Root type of the JavaScript object hierarchy
proc unsafeNew*[T](a: var ref T, size: Natural) {.magic: "New", noSideEffect.}
## creates a new object of type ``T`` and returns a safe (traced)
## reference to it in ``a``. This is **unsafe** as it allocates an object
@@ -4080,6 +4085,25 @@ template closureScope*(body: untyped): untyped =
## myClosure() # outputs 3
(proc() = body)()
template once*(body: untyped): untyped =
## Executes a block of code only once (the first time the block is reached).
## When hot code reloading is enabled, protects top-level code from being
## re-executed on each module reload.
##
## .. code-block:: nim
## proc draw(t: Triangle) =
## once:
## graphicsInit()
##
## line(t.p1, t.p2)
## line(t.p2, t.p3)
## line(t.p3, t.p1)
##
var alreadyExecuted {.global.} = false
if not alreadyExecuted:
alreadyExecuted = true
body
{.pop.} #{.push warning[GcMem]: off, warning[Uninit]: off.}
when defined(nimconfig):