| @@ -1,74 +1,74 | |||
| 
             | 
        1 | 1 | plugins { | 
| 
             | 
        2 | 2 | id "org.implab.gradle-typescript" version "1.3.3" | 
| 
             | 
        3 | 3 | id "ivy-publish" | 
| 
             | 
        4 | 4 | } | 
| 
             | 
        5 | 5 | |
| 
             | 
        6 | 6 | typescript { | 
| 
             | 
        7 | 7 | compilerOptions { | 
| 
             | 
        8 | 8 | lib = ["es5", "dom", "scripthost", "es2015.promise", "es2015.symbol", "es2015.iterable"] | 
| 
             | 
        9 | 9 | //listFiles = true | 
| 
             | 
        10 | 10 | declaration = true | 
| 
             | 
        11 | 11 | strict = true | 
| 
             | 
        12 | 12 | types = [] | 
| 
             | 
        13 | 13 | module = "amd" | 
| 
             | 
        14 | 14 | it.target = "es5" | 
| 
             | 
        15 | 15 | experimentalDecorators = true | 
| 
             | 
        16 | 16 | jsx = "react" | 
| 
             | 
        17 | 17 | jsxFactory = "createElement" | 
| 
             | 
        18 | 18 | moduleResolution = "node" | 
| 
             | 
        19 | 19 | // dojo-typings are sick | 
| 
             | 
        20 | 20 | skipLibCheck = true | 
| 
             | 
        21 | 21 | // traceResolution = true | 
| 
             | 
        22 | 22 | // baseUrl = "./" | 
| 
             | 
        23 | 23 | // paths = [ "*": [ "$projectDir/src/typings/*" ] ] | 
| 
             | 
        24 | 24 | // baseUrl = "$projectDir/src/typings" | 
| 
             | 
        25 | 25 | // typeRoots = ["$projectDir/src/typings"] | 
| 
             | 
        26 | 26 | } | 
| 
             | 
        27 | 27 | |
| 
             | 
        28 | 28 | tscCmd = "$projectDir/node_modules/.bin/tsc" | 
| 
             | 
        29 | 29 | tsLintCmd = "$projectDir/node_modules/.bin/tslint" | 
| 
             | 
        30 | 30 | esLintCmd = "$projectDir/node_modules/.bin/eslint" | 
| 
             | 
        31 | 31 | } | 
| 
             | 
        32 | 32 | |
| 
             | 
        33 | 33 | configureTsMain { | 
| 
             | 
        34 | 34 | compilerOptions { | 
| 
             | 
        35 | 35 | /*baseUrl = "$projectDir/src" | 
| 
             | 
        36 | 36 | paths = [ | 
| 
             | 
        37 | 37 | "dojo/*" : [ "typings/dojo/*" ], | 
| 
             | 
        38 | 38 | "dijit/*" : [ "typings/dijit/*" ] | 
| 
             | 
        39 | 39 | ]*/ | 
| 
             | 
        40 | 40 | types = ["requirejs", "dojo-typings"] | 
| 
             | 
        41 | 41 | } | 
| 
             | 
        42 | 42 | } | 
| 
             | 
        43 | 43 | |
| 
             | 
        44 | 44 | configureTsTest { | 
| 
             | 
        45 | 45 | compilerOptions { | 
| 
             | 
        46 | 46 | typeRoots = [] | 
| 
             | 
        47 | 
            
                     types = ["requirejs", sources.main.output.typingsDir.get().toString() | 
    |
| 
             | 
        47 | types = ["requirejs", sources.main.output.typingsDir.get().toString() ] | |
| 
             | 
        48 | 48 | } | 
| 
             | 
        49 | 49 | } | 
| 
             | 
        50 | 50 | |
| 
             | 
        51 | 51 | npmPackMeta { | 
| 
             | 
        52 | 52 | meta { | 
| 
             | 
        53 | 53 | name = "@$npmScope/$project.name" | 
| 
             | 
        54 | 54 | } | 
| 
             | 
        55 | 55 | } | 
| 
             | 
        56 | 56 | |
| 
             | 
        57 | 57 | task npmPackTypings(type: Copy) { | 
| 
             | 
        58 | 58 | dependsOn typings | 
| 
             | 
        59 | 59 | |
| 
             | 
        60 | 60 | npmPackContents.dependsOn it | 
| 
             | 
        61 | 61 | |
| 
             | 
        62 | 62 | from typescript.typingsDir | 
| 
             | 
        63 | 63 | into npm.packageDir | 
| 
             | 
        64 | 64 | } | 
| 
             | 
        65 | 65 | |
| 
             | 
        66 | 66 | task printVersion { | 
| 
             | 
        67 | 67 | doLast { | 
| 
             | 
        68 | 68 | println "packageName: ${npmPackMeta.metadata.get().name}"; | 
| 
             | 
        69 | 69 | println "version: $version"; | 
| 
             | 
        70 | 70 | println "target: $typescript.compilerOptions.target"; | 
| 
             | 
        71 | 71 | println "module: $typescript.compilerOptions.module"; | 
| 
             | 
        72 | 72 | println "symbols: $symbols"; | 
| 
             | 
        73 | 73 | } | 
| 
             | 
        74 | 74 | } No newline at end of file | 
| @@ -1,3 +1,198 | |||
| 
             | 
        1 | 1 | # @implab/djx | 
| 
             | 
        2 | 2 | |
| 
             | 
        3 | TODO No newline at end of file | |
| 
             | 
        3 | ## SYNOPSIS | |
| 
             | 
        4 | ||
| 
             | 
        5 | ```tsx | |
| 
             | 
        6 | import { djbase, djclass, bind, prototype, AbstractConstructor } from "@implab/djx/declare"; | |
| 
             | 
        7 | ||
| 
             | 
        8 | import { DjxWidgetBase } from "@implab/djx/tsx/DjxWidgetBase"; | |
| 
             | 
        9 | import { createElement } from "@implab/djx/tsx"; | |
| 
             | 
        10 | ||
| 
             | 
        11 | interface MyWidgetAttrs { | |
| 
             | 
        12 | title: string; | |
| 
             | 
        13 | ||
| 
             | 
        14 | counter: number; | |
| 
             | 
        15 | } | |
| 
             | 
        16 | ||
| 
             | 
        17 | interface MyWidgetEvents { | |
| 
             | 
        18 | "count-inc": Event; | |
| 
             | 
        19 | ||
| 
             | 
        20 | "count-dec": Event; | |
| 
             | 
        21 | } | |
| 
             | 
        22 | ||
| 
             | 
        23 | ||
| 
             | 
        24 | @djclass | |
| 
             | 
        25 | export class MyWidget extends djbase( | |
| 
             | 
        26 | DjxWidgetBase as AbstractConstructor<DjxWidgetBase<MyWidgetAttrs, MyWidgetEvents>> | |
| 
             | 
        27 | ) { | |
| 
             | 
        28 | ||
| 
             | 
        29 | @bind({ node: "titleNode", type: "innerHTML" }) | |
| 
             | 
        30 | title = ""; | |
| 
             | 
        31 | ||
| 
             | 
        32 | @prototype() | |
| 
             | 
        33 | counter = 0; | |
| 
             | 
        34 | ||
| 
             | 
        35 | render() { | |
| 
             | 
        36 | const Frame = (props: any) => <div>{props.children}</div>; | |
| 
             | 
        37 | return <div | |
| 
             | 
        38 | className="myWidget" | |
| 
             | 
        39 | tabIndex={3} | |
| 
             | 
        40 | style={ alignContent: "center", border: "1px solid" } | |
| 
             | 
        41 | > | |
| 
             | 
        42 | <h1 data-dojo-attach-point="titleNode"></h1> | |
| 
             | 
        43 | <Frame> | |
| 
             | 
        44 | <span class="up-button" onclick={e => this._onIncClick(e)}>[+]</span> | |
| 
             | 
        45 | <span class="down-button" onclick={() => this._onDecClick()}>[-]</span> | |
| 
             | 
        46 | </Frame> | |
| 
             | 
        47 | </div>; | |
| 
             | 
        48 | } | |
| 
             | 
        49 | ||
| 
             | 
        50 | _onIncClick(e: MouseEvent) { | |
| 
             | 
        51 | this.emit("count-inc", { bubbles: false }); | |
| 
             | 
        52 | } | |
| 
             | 
        53 | ||
| 
             | 
        54 | _onDecClick() { | |
| 
             | 
        55 | this.emit("count-dec", { bubbles: false }); | |
| 
             | 
        56 | } | |
| 
             | 
        57 | } | |
| 
             | 
        58 | ||
| 
             | 
        59 | ``` | |
| 
             | 
        60 | ||
| 
             | 
        61 | ## DESCRIPTION | |
| 
             | 
        62 | ||
| 
             | 
        63 | This package provides you with tools to glue your good-fellow dojo with modern | |
| 
             | 
        64 | techniques of building the webapp. The core concept is built around widgets and | |
| 
             | 
        65 | using .tsx to write it. Here some features: | |
| 
             | 
        66 | ||
| 
             | 
        67 | * `djbase()`, `@djaclass` - traits to declare your classes with `dojo/_base/declare` | |
| 
             | 
        68 | * `@implab/djx/tsx` - traits to build the rendering of your widgets with tsx | |
| 
             | 
        69 | * `DjxWidgetBase` - abstract class which supports tsx markup and | |
| 
             | 
        70 | `data-dojo-attach-*` attributes. | |
| 
             | 
        71 | * `@bind(...)` - annotations provides an easy way of using standard dojo widget | |
| 
             | 
        72 | attribute bindings. | |
| 
             | 
        73 | ||
| 
             | 
        74 | ### djbase, @djclass | |
| 
             | 
        75 | ||
| 
             | 
        76 | These two traits provides convenient way of using `dojo/_base/declare` in Typescript | |
| 
             | 
        77 | for declaring your classes. | |
| 
             | 
        78 | ||
| 
             | 
        79 | `djbase(...constructors)` - this method accepts a list of constructors in its | |
| 
             | 
        80 | parameters and returns the **fake** base type which then can be used to derive | |
| 
             | 
        81 | your own class. This allows you to provide the Typescript with the correct | |
| 
             | 
        82 | information about the base type and even use `super`!. The only one caveat of | |
| 
             | 
        83 | this approach is that you **MUST** decorate your class with `@djclass` annotation. | |
| 
             | 
        84 | ||
| 
             | 
        85 | Consider the following example: | |
| 
             | 
        86 | ||
| 
             | 
        87 | ```ts | |
| 
             | 
        88 | import { djbase, djclass } from "@implab/djx/declare"; | |
| 
             | 
        89 | import { FooMixin } from "./FooMixin"; | |
| 
             | 
        90 | import { BarMixin } from "./BarMixin"; | |
| 
             | 
        91 | import { BoxMixin } from "./BoxMixin"; | |
| 
             | 
        92 | ||
| 
             | 
        93 | @djclass | |
| 
             | 
        94 | export class Baz extends djbase(FooMixin, BarMixin, BoxMixin) { | |
| 
             | 
        95 | writeHello(out: string[]) { | |
| 
             | 
        96 | out.push("-> Baz"); | |
| 
             | 
        97 | ||
| 
             | 
        98 | super.writeHello(out); | |
| 
             | 
        99 | ||
| 
             | 
        100 | out.push("<- Baz"); | |
| 
             | 
        101 | } | |
| 
             | 
        102 | } | |
| 
             | 
        103 | ||
| 
             | 
        104 | ``` | |
| 
             | 
        105 | ||
| 
             | 
        106 | All mixins are declared like the one below: | |
| 
             | 
        107 | ||
| 
             | 
        108 | ```ts | |
| 
             | 
        109 | import { djclass, djbase } from "@implab/djx/declare"; | |
| 
             | 
        110 | ||
| 
             | 
        111 | interface Super { | |
| 
             | 
        112 | writeHello(out: string[]): void; | |
| 
             | 
        113 | ||
| 
             | 
        114 | } | |
| 
             | 
        115 | ||
| 
             | 
        116 | @djclass | |
| 
             | 
        117 | export class BarMixin extends djbase<Super>() { | |
| 
             | 
        118 | writeHello(out: string[]) { | |
| 
             | 
        119 | out.push("-> Bar"); | |
| 
             | 
        120 | ||
| 
             | 
        121 | super.writeHello(out); | |
| 
             | 
        122 | ||
| 
             | 
        123 | out.push("<- Bar"); | |
| 
             | 
        124 | } | |
| 
             | 
        125 | } | |
| 
             | 
        126 | ``` | |
| 
             | 
        127 | ||
| 
             | 
        128 | finally create an instance and call the `writeHello` method | |
| 
             | 
        129 | ||
| 
             | 
        130 | ```ts | |
| 
             | 
        131 | const baz = new Baz(); | |
| 
             | 
        132 | ||
| 
             | 
        133 | const data: string[] = []; | |
| 
             | 
        134 | baz.writeHello(data); | |
| 
             | 
        135 | ||
| 
             | 
        136 | console.log(data.join("\n")); | |
| 
             | 
        137 | ||
| 
             | 
        138 | ``` | |
| 
             | 
        139 | ||
| 
             | 
        140 | you will get the following output: | |
| 
             | 
        141 | ||
| 
             | 
        142 | ```text | |
| 
             | 
        143 | -> Baz | |
| 
             | 
        144 | -> Box | |
| 
             | 
        145 | -> Bar | |
| 
             | 
        146 | -> Foo | |
| 
             | 
        147 | <- Foo | |
| 
             | 
        148 | <- Bar | |
| 
             | 
        149 | <- Box | |
| 
             | 
        150 | <- Baz | |
| 
             | 
        151 | ``` | |
| 
             | 
        152 | ||
| 
             | 
        153 | Let's take a closer look to the `Baz` declaration it uses `djbase` to derive | |
| 
             | 
        154 | from three mixins and the class is decorated with `@djclass` to accomplish the | |
| 
             | 
        155 | declaration and make a real constructor. | |
| 
             | 
        156 | ||
| 
             | 
        157 | To allow an access to the next sibling method (in terms of multiple inheritance) | |
| 
             | 
        158 | Dojo provides `this.inherited(arguments)` method but this approach leads to the | |
| 
             | 
        159 | problem with 'strict' mode of ES5 and eliminates the type information about a | |
| 
             | 
        160 | calling method. This library solves the problem calling inherited/next method by | |
| 
             | 
        161 | utilizing `super` keyword. Under the hood there are proxy methods generated in | |
| 
             | 
        162 | the prototype of the declared class which make calls to `this.inherited(...)` | |
| 
             | 
        163 | method. This technique is compatible with 'strict' mode. | |
| 
             | 
        164 | ||
| 
             | 
        165 | Mixins are declared the similar, they are also may have the base types although | |
| 
             | 
        166 | the most common case is declaring the mixin without any base classes. To allow | |
| 
             | 
        167 | the mixin to access the next method you should declare the interface with | |
| 
             | 
        168 | desired methods and use the special form of `djbase<Super>()` without arguments. | |
| 
             | 
        169 | ||
| 
             | 
        170 | ### DjxWidgetBase<Attrs, Events> | |
| 
             | 
        171 | ||
| 
             | 
        172 | TODO | |
| 
             | 
        173 | ||
| 
             | 
        174 | ### Markup (.tsx) | |
| 
             | 
        175 | ||
| 
             | 
        176 | Add to your `tsconfig.json` the following options | |
| 
             | 
        177 | ||
| 
             | 
        178 | ```json | |
| 
             | 
        179 | { | |
| 
             | 
        180 | "compilerOptions": { | |
| 
             | 
        181 | "types": ["@implab/djx"], | |
| 
             | 
        182 | "experimentalDecorators": true, | |
| 
             | 
        183 | "jsxFactory": "createElement", | |
| 
             | 
        184 | "jsx": "react", | |
| 
             | 
        185 | } | |
| 
             | 
        186 | } | |
| 
             | 
        187 | ||
| 
             | 
        188 | ``` | |
| 
             | 
        189 | ||
| 
             | 
        190 | Import `createElement` into your `.tsx` file | |
| 
             | 
        191 | ||
| 
             | 
        192 | ```ts | |
| 
             | 
        193 | import { createElement } from "@implab/djx/tsx"; | |
| 
             | 
        194 | ``` | |
| 
             | 
        195 | ||
| 
             | 
        196 | You are ready to go! | |
| 
             | 
        197 | ||
| 
             | 
        198 | TODO | |
| @@ -1,34 +1,52 | |||
| 
             | 
        1 | 1 | /// <reference path="./css.d.ts"/> | 
| 
             | 
        2 | 2 | /// <reference path="./dijit.d.ts"/> | 
| 
             | 
        3 | 3 | |
| 
             | 
        4 | 4 | declare namespace JSX { | 
| 
             | 
        5 | 5 | |
| 
             | 
        6 | 6 | interface DjxIntrinsicAttributes { | 
| 
             | 
        7 | /** alias for className */ | |
| 
             | 
        7 | 8 | class: string; | 
| 
             | 
        9 | ||
| 
             | 
        10 | /** specifies the name of the property in the widget where the the | |
| 
             | 
        11 | * reference to the current object will be stored | |
| 
             | 
        12 | */ | |
| 
             | 
        8 | 13 | "data-dojo-attach-point": string; | 
| 
             | 
        14 | ||
| 
             | 
        15 | /** specifies handlers map for the events */ | |
| 
             | 
        9 | 16 | "data-dojo-attach-event": string; | 
| 
             | 
        17 | ||
| 
             | 
        18 | [attr: string]: any; | |
| 
             | 
        19 | } | |
| 
             | 
        20 | ||
| 
             | 
        21 | interface DjxIntrinsicElements { | |
| 
             | 
        10 | 22 | } | 
| 
             | 
        11 | 23 | |
| 
             | 
        12 | 24 | type RecursivePartial<T> = T extends string | number | boolean | null | undefined | Function ? | 
| 
             | 
        13 | 25 | T : | 
| 
             | 
        14 | 26 | { [k in keyof T]?: RecursivePartial<T[k]> }; | 
| 
             | 
        15 | 27 | |
| 
             | 
        16 | 28 | type MatchingMemberKeys<T, U> = { | 
| 
             | 
        17 | 29 | [K in keyof T]: T[K] extends U ? K : never; | 
| 
             | 
        18 | 30 | }[keyof T]; | 
| 
             | 
        19 | 31 | type NotMatchingMemberKeys<T, U> = { | 
| 
             | 
        20 | 32 | [K in keyof T]: T[K] extends U ? never : K; | 
| 
             | 
        21 | 33 | }[keyof T]; | 
| 
             | 
        34 | ||
| 
             | 
        35 | type ExtractMembers<T, U> = Pick<T, MatchingMemberKeys<T, U>>; | |
| 
             | 
        36 | ||
| 
             | 
        37 | type ExcludeMembers<T, U> = Pick<T, NotMatchingMemberKeys<T, U>>; | |
| 
             | 
        38 | ||
| 
             | 
        22 | 39 | type ElementAttrNames<E> = NotMatchingMemberKeys<E, (...args: any[]) => any>; | 
| 
             | 
        23 | 40 | |
| 
             | 
        24 | 41 | type ElementAttrType<E, K extends keyof any> = K extends keyof E ? RecursivePartial<E[K]> : string; | 
| 
             | 
        25 | 42 | |
| 
             | 
        26 | type LaxElement<E extends object> = E & { } | |
| 
             | 
        43 | type LaxElement<E extends object> = ExcludeMembers<Omit<E, "children">, (...args: any[]) => any> & DjxIntrinsicAttributes; | |
| 
             | 
        27 | 44 | |
| 
             | 
        28 | 
            
                 type L | 
    |
| 
             | 
        29 | [attr in ElementAttrNames<E>]?: ElementAttrType<E, attr>; | |
| 
             | 
        30 | 
            
                 }  | 
    |
| 
             | 
        31 | interface IntrinsicElements { | |
| 
             | 
        32 | [tag: keyof HTMLElementTagNameMap]: LegacyElementAttributes<HTMLElementTagNameMap[tag]>; | |
| 
             | 
        45 | type LaxIntrinsicElementsMap = { | |
| 
             | 
        46 | [tag in keyof HTMLElementTagNameMap]: LaxElement<HTMLElementTagNameMap[tag]> | |
| 
             | 
        47 | } & DjxIntrinsicElements; | |
| 
             | 
        48 | ||
| 
             | 
        49 | type IntrinsicElements = { | |
| 
             | 
        50 | [tag in keyof LaxIntrinsicElementsMap]: RecursivePartial<LaxIntrinsicElementsMap[tag]>; | |
| 
             | 
        33 | 51 | } | 
| 
             | 
        34 | 52 | } | 
| @@ -1,51 +1,46 | |||
| 
             | 
        1 | 1 | import { djbase, djclass, bind, prototype, AbstractConstructor } from "../declare"; | 
| 
             | 
        2 | 2 | |
| 
             | 
        3 | 3 | import { DjxWidgetBase } from "../tsx/DjxWidgetBase"; | 
| 
             | 
        4 | 4 | import { createElement } from "../tsx"; | 
| 
             | 
        5 | 5 | |
| 
             | 
        6 | 6 | interface MyWidgetAttrs { | 
| 
             | 
        7 | 7 | title: string; | 
| 
             | 
        8 | 8 | |
| 
             | 
        9 | 9 | counter: number; | 
| 
             | 
        10 | 10 | } | 
| 
             | 
        11 | 11 | |
| 
             | 
        12 | 12 | interface MyWidgetEvents { | 
| 
             | 
        13 | 13 | "count-inc": Event; | 
| 
             | 
        14 | 14 | |
| 
             | 
        15 | 15 | "count-dec": Event; | 
| 
             | 
        16 | 16 | } | 
| 
             | 
        17 | 17 | |
| 
             | 
        18 | 18 | |
| 
             | 
        19 | 19 | @djclass | 
| 
             | 
        20 | 20 | export class MyWidget extends djbase(DjxWidgetBase as AbstractConstructor<DjxWidgetBase<MyWidgetAttrs, MyWidgetEvents>>) { | 
| 
             | 
        21 | 21 | |
| 
             | 
        22 | 22 | @bind({ node: "titleNode", type: "innerHTML" }) | 
| 
             | 
        23 | 23 | title = ""; | 
| 
             | 
        24 | 24 | |
| 
             | 
        25 | 25 | @prototype() | 
| 
             | 
        26 | 26 | counter = 0; | 
| 
             | 
        27 | 27 | |
| 
             | 
        28 | 28 | render() { | 
| 
             | 
        29 | 29 | const Frame = (props: any) => <div>{props.children}</div>; | 
| 
             | 
        30 | 
            
                     return <div  | 
    |
| 
             | 
        30 | return <div className="myWidget" tabIndex={3} style={{ alignContent: "center", border: "1px solid" }} > | |
| 
             | 
        31 | 31 | <h1 data-dojo-attach-point="titleNode"></h1> | 
| 
             | 
        32 | 32 | <Frame> | 
| 
             | 
        33 | 33 | <span class="up-button" onclick={e => this._onIncClick(e)}>[+]</span> | 
| 
             | 
        34 | 
            
                             <span class="down-button"  | 
    |
| 
             | 
        34 | <span class="down-button" onclick={() => this._onDecClick()}>[-]</span> | |
| 
             | 
        35 | 35 | </Frame> | 
| 
             | 
        36 | <table style="pretty"> | |
| 
             | 
        37 | <tr> | |
| 
             | 
        38 | <td colSpan={2} colspan="3" onclick=""></td> | |
| 
             | 
        39 | </tr> | |
| 
             | 
        40 | </table> | |
| 
             | 
        41 | 36 | </div>; | 
| 
             | 
        42 | 37 | } | 
| 
             | 
        43 | 38 | |
| 
             | 
        44 | 39 | _onIncClick(e: MouseEvent) { | 
| 
             | 
        45 | 40 | this.emit("count-inc", { bubbles: false }); | 
| 
             | 
        46 | 41 | } | 
| 
             | 
        47 | 42 | |
| 
             | 
        48 | 43 | _onDecClick() { | 
| 
             | 
        49 | 44 | this.emit("count-dec", { bubbles: false }); | 
| 
             | 
        50 | 45 | } | 
| 
             | 
        51 | 46 | } | 
| @@ -1,13 +1,13 | |||
| 
             | 
        1 | 1 | { | 
| 
             | 
        2 | 2 | "extends": "../tsconfig", | 
| 
             | 
        3 | 3 | "compilerOptions": { | 
| 
             | 
        4 | 4 | //"rootDir": "ts", | 
| 
             | 
        5 | 5 | "rootDirs": [ | 
| 
             | 
        6 | 6 | "ts", | 
| 
             | 
        7 | 7 | "typings", | 
| 
             | 
        8 | 8 | "../main/ts", | 
| 
             | 
        9 | 9 | "../main/typings" | 
| 
             | 
        10 | 10 | ], | 
| 
             | 
        11 | 
            
                     "types": ["requirejs", "../main/typings | 
    |
| 
             | 
        11 | "types": ["requirejs", "../main/typings", "dojo-typings"] | |
| 
             | 
        12 | 12 | } | 
| 
             | 
        13 | 13 | } No newline at end of file | 
        
        General Comments 0
    
    
  
  
                      You need to be logged in to leave comments.
                      Login now
                    
                