Skip to content

Commit

Permalink
Closes #89: provide annotation for native 1.5 components
Browse files Browse the repository at this point in the history
  • Loading branch information
jokade committed Mar 30, 2016
1 parent 0e1ca70 commit d89f884
Show file tree
Hide file tree
Showing 11 changed files with 140 additions and 173 deletions.
9 changes: 6 additions & 3 deletions build.sbt
@@ -1,7 +1,7 @@

lazy val commonSettings = Seq(
organization := "biz.enef",
version := "0.2.3-SNAPSHOT",
version := "0.3-SNAPSHOT",
scalaVersion := "2.11.6",
scalacOptions ++= Seq("-deprecation","-unchecked","-feature","-language:implicitConversions","-Xlint"),
autoCompilerPlugins := true,
Expand All @@ -11,7 +11,8 @@ lazy val commonSettings = Seq(
val a = baseDirectory.value.toURI.toString.replaceFirst("[^/]+/?$", "")
val g = "https://raw.githubusercontent.com/jokade/scalajs-angulate"
s"-P:scalajs:mapSourceURI:$a->$g/v${version.value}/"
}))
})),
resolvers += Resolver.sonatypeRepo("snapshots")
)


Expand All @@ -24,9 +25,11 @@ lazy val root = project.in(file(".")).
libraryDependencies ++= Seq(
"org.scala-lang" % "scala-reflect" % scalaVersion.value % "provided",
"org.scala-js" %%% "scalajs-dom" % "0.8.0",
"de.surfice" %%% "smacrotools-sjs" % "0.1-SNAPSHOT",
"be.doeraene" %%% "scalajs-jquery" % "0.8.0" % "provided"
),
resolvers += Resolver.sonatypeRepo("releases")
resolvers += Resolver.sonatypeRepo("releases"),
addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full)
)


Expand Down
87 changes: 87 additions & 0 deletions src/main/scala/biz/enef/angulate/Component.scala
@@ -0,0 +1,87 @@
// - Project: scalajs-angulate (https://github.com/jokade/scalajs-angulate)
// Description: Annotation for defining Angular 1.5 components
//
// Distributed under the MIT License (see included file LICENSE)

// Project: angulate2 (https://github.com/jokade/angulate2)
// Description:
package biz.enef.angulate

import biz.enef.angulate.impl.{ControllerMacroUtils, MacroBase}
import de.surfice.smacrotools.JsWhiteboxMacroTools

import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
import scala.scalajs.js

@compileTimeOnly("enable macro paradise to expand macro annotations")
class Component(selector: String,
template: String = "",
templateUrl: String = "",
controllerAs: String = "$ctrl",
bindings: js.Dictionary[String] = null) extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro Component.Macro.impl
}

object Component {


private [angulate] class Macro(val c: whitebox.Context) extends JsWhiteboxMacroTools {
import c.universe._

val annotationParamNames = Seq(
"selector",
"template",
"templateUrl",
"controllerAs",
"bindings"
)

def componentOf[T: c.WeakTypeTag] = {
val ct = weakTypeOf[T]
val module = Select(c.prefix.tree, TermName("self"))

val comp = TermName(ct.typeSymbol.fullName)

val tree = q"""$module.component($comp.selector,$comp.options)"""
tree
}

def impl(annottees: c.Expr[Any]*) : c.Expr[Any] = annottees.map(_.tree).toList match {
case (classDecl: ClassDef) :: Nil => modifiedDeclaration(classDecl)
case _ => c.abort(c.enclosingPosition, "Invalid annottee for @Component")
}

def modifiedDeclaration(classDecl: ClassDef) = {
val parts = extractClassParts(classDecl)
import parts._

val module = Select(c.prefix.tree, TermName("self"))

val objName = fullName + "_"

val annots = extractAnnotationParameters(c.prefix.tree,annotationParamNames) collect {
case (p,Some(v)) if p != "selector" => q""" ${p.toString} -> $v"""
}

val base = getJSBaseClass(parents)

val tree =
q"""{
@scalajs.js.annotation.JSExport($fullName)
@scalajs.js.annotation.ScalaJSDefined
class $name ( ..$params ) extends ..$base { ..$body }
@scalajs.js.annotation.JSExport($objName)
@scalajs.js.annotation.ScalaJSDefined
object ${name.toTermName} extends scalajs.js.Object {
def selector = "test"
def controller = js.Array((() => new $name(..$params)):js.Function)
def options = scalajs.js.Dictionary( "controller" -> controller, ..$annots )
}
}
"""
c.Expr[Any](tree)
}
}
}
40 changes: 9 additions & 31 deletions src/main/scala/biz/enef/angulate/Module.scala
Expand Up @@ -6,6 +6,7 @@ package biz.enef.angulate

import acyclic.file
import scala.scalajs.js
import scala.scalajs.js.|

/**
* Defines the bindings to the angular.Module API and enhancements provided by scalajs-angulate.
Expand All @@ -24,21 +25,20 @@ trait Module extends js.Object {
* Defines an animation hook that can be later used with the \$animate service and directives that use this service.
*
* @note animations take effect only if the ngAnimate module is loaded
*
* @param name animation name
* @param animationFactory Array with the names of the dependencies to be injected.
* The last element in this array must be the factory function
*
* @see [[https://docs.angularjs.org/api/ng/type/angular.Module#animate]]
*/
def animation(name: String, animationFactory: js.Array[Any]): Module = js.native

def component(name: String, options: js.Any): Module = js.native

/**
* Use this method to register work which needs to be performed on module loading.
*
* @param configFn Array with the names of the dependencies to be injected.
* The last element in this array must be the function to be called on module load
*
* @see [[https://docs.angularjs.org/api/ng/type/angular.Module#config]]
*/
def config(configFn: js.Array[Any]): Module = js.native
Expand All @@ -48,7 +48,6 @@ trait Module extends js.Object {
*
* @param name The name of the constant
* @param value The constant value
*
* @see [[https://docs.angularjs.org/api/ng/type/angular.Module#constant]]
*/
def constant(name: String, value: js.Any): Module = js.native
Expand All @@ -59,7 +58,6 @@ trait Module extends js.Object {
* @param name The name of the controller
* @param constructor Array containing the names of the dependencies to be injected and
* the constructor function as last element
*
* @see [[https://docs.angularjs.org/api/ng/type/angular.Module#controller]]
*/
def controller(name: String, constructor: js.Array[Any]): Module = js.native
Expand All @@ -70,7 +68,6 @@ trait Module extends js.Object {
* @param name Name of the directive in camel-case (ie `ngBind`)
* @param directiveFactory Array containing the names of the dependencies to be injected and
* the constructor function as last element
*
* @see [[https://docs.angularjs.org/api/ng/type/angular.Module#directive]]
*/
def directive(name: String, directiveFactory: js.Array[Any]): Module = js.native
Expand All @@ -80,7 +77,6 @@ trait Module extends js.Object {
*
* @param name Name of the directive in camel-case (ie `ngBind`)
* @param directiveFactory Function that returns the directive definition object (DDO) when called
*
* @see [[https://docs.angularjs.org/api/ng/type/angular.Module#directive]]
*/
def directive(name: String, directiveFactory: js.Function): Module = js.native
Expand All @@ -91,7 +87,6 @@ trait Module extends js.Object {
* @param name The name of the service
* @param constructor Array containing the names of the dependencies to be injected and
* the constructor function as last element
*
* @see [[https://docs.angularjs.org/api/ng/type/angular.Module#factory]]
*/
def factory(name: String, constructor: js.Array[Any]): Module = js.native
Expand All @@ -102,7 +97,6 @@ trait Module extends js.Object {
* @param name The name of the filter
* @param filterFactory Array containing the names of the dependencies to be injected and
* the constructor function as last element
*
* @see [[https://docs.angularjs.org/api/ng/type/angular.Module#filter]]
*/
def filter(name: String, filterFactory: js.Array[Any]): Module = js.native
Expand All @@ -113,7 +107,6 @@ trait Module extends js.Object {
* @param name The name of the instance. NOTE: the provider will be available under name + 'Provider' key.
* @param constructor Array containing the names of the dependencies to be injected and
* the constructor function as last element
*
* @see [[https://docs.angularjs.org/api/ng/type/angular.Module#provider]]
*/
def provider(name: String, constructor: js.Array[Any]): Module = js.native
Expand All @@ -123,7 +116,6 @@ trait Module extends js.Object {
*
* @param initializationFn Array containing the names of the dependencies to be injected and
* the initialization function as last element
*
* @see [[https://docs.angularjs.org/api/ng/type/angular.Module#run]]
*/
def run(initializationFn: js.Array[Any]): Module = js.native
Expand All @@ -133,7 +125,6 @@ trait Module extends js.Object {
*
* @param name The name of the service
* @param constructor A class constructor function
*
* @see [[https://docs.angularjs.org/api/ng/type/angular.Module#service]]
*/
def service(name: String, constructor: js.Array[Any]): Module = js.native
Expand All @@ -156,10 +147,8 @@ object Module {
* Defines an animation hook that can be later used with the \$animate service and directives that use this service.
*
* @note animations take effect only if the ngAnimate module is loaded
*
* @param name animation name
* @param animationFactory Factory function
*
* @see [[https://docs.angularjs.org/api/ng/type/angular.Module#animate]]
*/
@inline def animation(name: String, animationFactory: AnnotatedFunction): Module = self.animation(name, animationFactory.inlineArrayAnnotatedFn)
Expand All @@ -168,7 +157,6 @@ object Module {
* Use this method to register work which needs to be performed on module loading.
*
* @param configFn This function is executed on module load
*
* @see [[https://docs.angularjs.org/api/ng/type/angular.Module#config]]
*/
@inline def config(configFn: AnnotatedFunction): Module = self.config(configFn.inlineArrayAnnotatedFn)
Expand All @@ -178,7 +166,6 @@ object Module {
*
* @param name The name of the controller
* @param constructor Controller construction function
*
* @see [[https://docs.angularjs.org/api/ng/type/angular.Module#controller]]
*/
@inline def controller(name: String, constructor: AnnotatedFunction): Module = self.controller(name, constructor.inlineArrayAnnotatedFn)
Expand All @@ -188,7 +175,6 @@ object Module {
*
* @param name Name of the directive in camel-case (ie `ngBind`)
* @param directiveFactory Directive constructor function
*
* @see [[https://docs.angularjs.org/api/ng/type/angular.Module#directive]]
*/
@inline def directive(name: String, directiveFactory: AnnotatedFunction): Module = self.directive(name, directiveFactory.inlineArrayAnnotatedFn)
Expand All @@ -198,7 +184,6 @@ object Module {
*
* @param name The name of the service
* @param constructor Service constructor function
*
* @see [[https://docs.angularjs.org/api/ng/type/angular.Module#factory]]
*/
@inline def factory(name: String, constructor: AnnotatedFunction): Module = self.factory(name, constructor.inlineArrayAnnotatedFn)
Expand All @@ -208,7 +193,6 @@ object Module {
*
* @param name The name of the filter
* @param filterFactory Filter constructor function
*
* @see [[https://docs.angularjs.org/api/ng/type/angular.Module#filter]]
*/
@inline def filter(name: String, filterFactory: AnnotatedFunction): Module = self.filter(name, filterFactory.inlineArrayAnnotatedFn)
Expand All @@ -218,7 +202,6 @@ object Module {
*
* @param name The name of the instance. NOTE: the provider will be available under name + 'Provider' key.
* @param constructor Provider constructor function
*
* @see [[https://docs.angularjs.org/api/ng/type/angular.Module#provider]]
*/
@inline def provider(name: String, constructor: AnnotatedFunction): Module = self.provider(name, constructor.inlineArrayAnnotatedFn)
Expand All @@ -227,7 +210,6 @@ object Module {
* Use this method to register work which should be performed when the injector is done loading all modules.
*
* @param initializationFn This function is executed on module fully loaded
*
* @see [[https://docs.angularjs.org/api/ng/type/angular.Module#run]]
*/
@inline def run(initializationFn: AnnotatedFunction): Module = self.run(initializationFn.inlineArrayAnnotatedFn)
Expand All @@ -237,19 +219,16 @@ object Module {
*
* @param name The name of the service
* @param constructor A class constructor function
*
* @see [[https://docs.angularjs.org/api/ng/type/angular.Module#service]]
*/
@inline def service(name: String, constructor: AnnotatedFunction): Module = self.service(name, constructor.inlineArrayAnnotatedFn)

@inline implicit def autoUnwrapRichModule(m: RichModule): Module = m.self

//------------------------------ ENHANCEMENTS ------------------------------
/**
* Registers the specified controller using the fully qualified class as the name of the controller.
*
* @note This is a scalajs-angulate enhancement
*
* @tparam T Controller class
*/
@inline def controllerOf[T <: NGController]: Module = macro impl.ControllerMacros.controllerOf[T]
Expand All @@ -258,7 +237,6 @@ object Module {
* Registers the specified controller using an explicitly given controller name.
*
* @note This is a scalajs-angulate enhancement
*
* @param name The controller name
* @tparam T Controller class
*/
Expand All @@ -269,7 +247,6 @@ object Module {
* The class name is used as the name of the service, with the __first letter in lower case__.
*
* @note This is a scalajs-angulate enhancement
*
* @tparam T Service class
*/
@inline def serviceOf[T <: Service]: Module = macro impl.ServiceMacros.serviceOf[T]
Expand All @@ -278,7 +255,6 @@ object Module {
* Registers the specified class as Angular service using the explicitly given service name.
*
* @note This is a scalajs-angulate enhancement
*
* @param name The service name
* @tparam T Service class
*/
Expand All @@ -290,7 +266,6 @@ object Module {
* without the suffix 'Directive'.
*
* @note This is a scalajs-angulate enhancement
*
* @tparam T Class defining the directive
*/
@inline def directiveOf[T <: Directive]: Module = macro impl.DirectiveMacros.directiveOf[T]
Expand All @@ -299,19 +274,22 @@ object Module {
* Registers the specified class as Angular directive under the given name.
*
* @note This is a scalajs-angulate enhancement
*
* @param name The name of the directive
* @tparam T Class defining the directive
*/
@inline def directiveOf[T <: Directive](name: String): Module = macro impl.DirectiveMacros.directiveOfWithName[T]

/**
* Registers a Angular2-style Component.
* Registers an Angular2-style Component.
*
* @tparam T
*/
@inline def componentOf[T] : Module = macro impl.ComponentMacros.componentOf[T]
@inline def componentOf[T] : Module = macro Component.Macro.componentOf[T]

}

object RichModule {
@inline implicit def autoUnwrapRichModule(m: RichModule): Module = m.self
}

}
20 changes: 10 additions & 10 deletions src/main/scala/biz/enef/angulate/annotations.scala
Expand Up @@ -24,15 +24,15 @@ case class named(name: String) extends StaticAnnotation
* @param templateUrl
* @param bind
*/
case class ComponentDef(selector: String,
template: String = null,
templateUrl: String = null,
bind: js.Dictionary[String] = null)

/**
*
* @param cd
*/
case class Component(cd: ComponentDef) extends StaticAnnotation
//case class ComponentDef(selector: String,
// template: String = null,
// templateUrl: String = null,
// bind: js.Dictionary[String] = null)
//
///**
// *
// * @param cd
// */
//case class Component(cd: ComponentDef) extends StaticAnnotation


0 comments on commit d89f884

Please sign in to comment.