├── .gitattributes ├── .gitignore ├── .npmignore ├── JSXTyper.njsproj ├── JSXTyper.sln ├── LICENSE ├── NuGet.config ├── README.md ├── SampleProject ├── .gitignore ├── Assets │ ├── caretdown.png │ └── style.less ├── BankApp.ts ├── BankDemo.csproj ├── BankDemo.sln ├── Controllers │ ├── AccountController.ts │ ├── ControllerBase.ts │ ├── HomeController.ts │ ├── SupportController.ts │ └── TransactionController.ts ├── Controls │ ├── DropdownMenu.ts │ └── KeyCodes.ts ├── Dialogs │ ├── ChangePasswordDialog.ts │ ├── DialogBase.ts │ └── MessageBox.ts ├── Gruntfile.js ├── Libs │ ├── simplemvc.d.ts │ └── simplemvc.js ├── Models │ ├── Account.ts │ └── Bank.ts ├── NuGet.config ├── README.md ├── Views │ ├── .gitignore │ ├── AccountPage.jsx │ ├── ChangePassword.jsx │ ├── DropdownMenu.jsx │ ├── HomePage.jsx │ ├── MasterPage.jsx │ ├── MessageBox.jsx │ ├── SupportPage.jsx │ └── TransactionPage.jsx ├── index.html ├── package.json ├── packages.config ├── tsd.json ├── web.Debug.config ├── web.Release.config └── web.config ├── Scripts └── typings │ ├── estree │ └── jsxtree.d.ts │ └── node │ └── node.d.ts ├── debug.jsx ├── jsxtyper.ts ├── jsxtypercore.ts ├── package.json ├── packages.config ├── tasks └── grunt-jsxtyper.js └── tests ├── test1.jsx ├── test10.jsx ├── test11.jsx ├── test12.jsx ├── test2.jsx ├── test3.jsx ├── test4.jsx ├── test5.jsx ├── test6.jsx ├── test7.jsx ├── test8.jsx └── test9.jsx /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | 11 | [Dd]ebug/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | [Bb]in/ 16 | [Oo]bj/ 17 | 18 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets 19 | !packages/*/build/ 20 | 21 | # MSTest test Results 22 | [Tt]est[Rr]esult*/ 23 | [Bb]uild[Ll]og.* 24 | 25 | *_i.c 26 | *_p.c 27 | *.ilk 28 | *.meta 29 | *.obj 30 | *.pch 31 | *.pdb 32 | *.pgc 33 | *.pgd 34 | *.rsp 35 | *.sbr 36 | *.tlb 37 | *.tli 38 | *.tlh 39 | *.tmp 40 | *.tmp_proj 41 | *.log 42 | *.vspscc 43 | *.vssscc 44 | .builds 45 | *.pidb 46 | *.log 47 | *.scc 48 | 49 | # Visual C++ cache files 50 | ipch/ 51 | *.aps 52 | *.ncb 53 | *.opensdf 54 | *.sdf 55 | *.cachefile 56 | 57 | # Visual Studio profiler 58 | *.psess 59 | *.vsp 60 | *.vspx 61 | 62 | # Guidance Automation Toolkit 63 | *.gpState 64 | 65 | # ReSharper is a .NET coding add-in 66 | _ReSharper*/ 67 | *.[Rr]e[Ss]harper 68 | 69 | # TeamCity is a build add-in 70 | _TeamCity* 71 | 72 | # DotCover is a Code Coverage Tool 73 | *.dotCover 74 | 75 | # NCrunch 76 | *.ncrunch* 77 | .*crunch*.local.xml 78 | 79 | # Installshield output folder 80 | [Ee]xpress/ 81 | 82 | # DocProject is a documentation generator add-in 83 | DocProject/buildhelp/ 84 | DocProject/Help/*.HxT 85 | DocProject/Help/*.HxC 86 | DocProject/Help/*.hhc 87 | DocProject/Help/*.hhk 88 | DocProject/Help/*.hhp 89 | DocProject/Help/Html2 90 | DocProject/Help/html 91 | 92 | # Click-Once directory 93 | publish/ 94 | 95 | # Publish Web Output 96 | *.Publish.xml 97 | 98 | # NuGet Packages Directory 99 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 100 | packages/ 101 | 102 | # Windows Azure Build Output 103 | csx 104 | *.build.csdef 105 | 106 | # Windows Store app package directory 107 | AppPackages/ 108 | 109 | # Others 110 | sql/ 111 | *.Cache 112 | ClientBin/ 113 | [Ss]tyle[Cc]op.* 114 | ~$* 115 | *~ 116 | *.dbmdl 117 | *.[Pp]ublish.xml 118 | *.pfx 119 | *.publishsettings 120 | 121 | # RIA/Silverlight projects 122 | Generated_Code/ 123 | 124 | # Backup & report files from converting an old project file to a newer 125 | # Visual Studio version. Backup files are not needed, because we have git ;-) 126 | _UpgradeReport_Files/ 127 | Backup*/ 128 | UpgradeLog*.XML 129 | UpgradeLog*.htm 130 | 131 | # SQL Server files 132 | App_Data/*.mdf 133 | App_Data/*.ldf 134 | 135 | 136 | #LightSwitch generated files 137 | GeneratedArtifacts/ 138 | _Pvt_Extensions/ 139 | ModelManifest.xml 140 | 141 | # ========================= 142 | # Windows detritus 143 | # ========================= 144 | 145 | # Windows image file caches 146 | Thumbs.db 147 | ehthumbs.db 148 | 149 | # Folder config file 150 | Desktop.ini 151 | 152 | # Recycle Bin used on file shares 153 | $RECYCLE.BIN/ 154 | 155 | # Mac desktop service store files 156 | .DS_Store 157 | 158 | # ========================= 159 | node_modules/ 160 | .ntvs_analysis.dat 161 | *.map 162 | jsxtyper.js 163 | jsxtypercore.js 164 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Visual Studio files 2 | *.njsproj 3 | *.sln 4 | *.suo 5 | *.dat 6 | 7 | # Git files 8 | .gitattributes 9 | 10 | # Unwanted build output 11 | *.js.map 12 | bin/ 13 | obj/ 14 | 15 | # Nuget files 16 | NuGet.config 17 | packages.config 18 | packages/ 19 | 20 | # Source code 21 | *.ts 22 | 23 | # Specific to this project 24 | Scripts/ 25 | tests/ 26 | *.jsx 27 | -------------------------------------------------------------------------------- /JSXTyper.njsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 11.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | JSXTyper 7 | JSXTyper 8 | debug.jsx 9 | 10 | 11 | 12 | Debug 13 | 2.0 14 | f458ec20-3a1d-4aba-99bc-2f7d807a1db3 15 | . 16 | jsxtyper.ts 17 | False 18 | 19 | 20 | . 21 | . 22 | v4.0 23 | {3AF33F2E-1136-4D97-BBB7-1795711AC8B8};{9092AA53-FB77-4645-B42D-1CCCA6BD08BD} 24 | ProjectFiles 25 | true 26 | CommonJS 27 | true 28 | False 29 | 30 | 31 | true 32 | 33 | 34 | true 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | False 62 | True 63 | 0 64 | / 65 | http://localhost:48022/ 66 | False 67 | True 68 | http://localhost:1337 69 | False 70 | 71 | 72 | 73 | 74 | 75 | 76 | CurrentPage 77 | True 78 | False 79 | False 80 | False 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | False 90 | False 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /JSXTyper.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.31101.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9092AA53-FB77-4645-B42D-1CCCA6BD08BD}") = "JSXTyper", "JSXTyper.njsproj", "{F458EC20-3A1D-4ABA-99BC-2F7D807A1DB3}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {F458EC20-3A1D-4ABA-99BC-2F7D807A1DB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {F458EC20-3A1D-4ABA-99BC-2F7D807A1DB3}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {F458EC20-3A1D-4ABA-99BC-2F7D807A1DB3}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {F458EC20-3A1D-4ABA-99BC-2F7D807A1DB3}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Microsoft 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /NuGet.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | packages 4 | 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JSXtyper 2 | 3 | JSXtyper generates TypeScript interfaces from your .jsx files. By referencing the generated .ts file and using the generated *props* and *state* interfaces you can make sure all data expected by the .jsx is supplied, and catch any typos at build-time. 4 | 5 | ## How to build JSXtyper 6 | 7 | Install Node if you haven't already. Then open Node.js command prompt and run: 8 | 9 | npm install 10 | 11 | This will install esprima-fb, estraverse and estraverse-fb. 12 | 13 | We also depend on estree.d.ts. This file will be automatically downloaded when you build the solution in Visual Studio, unless you have turned off automatic downloading of dependencies. To manually download this dependency open Package Manager Console and type: 14 | 15 | Install-Package estree.TypeScript.DefinitelyTyped 16 | 17 | You are now ready to build. Open the .sln file in Visual Studio and select Build > Build Solution from the menu. Note that you must have [Node.js Tools for Visual Studio](https://www.visualstudio.com/features/node-js-vs) installed in order to open .njsproj projects. 18 | 19 | ## Example 20 | 21 | Here's an example .jsx file and the corresponding generated .ts file: 22 | 23 | var OrderPage = React.createClass({ 24 | getInitialState: function () { 25 | return { orderStatus: this.props.initialOrderStatus }; 26 | }, 27 | render: function () { 28 | var header = 29 |
30 |
Customer: {this.props.customerName}
31 |
Order Date: {this.props.orderDate}
32 |
; 33 | var details = []; 34 | for (var i = 0; i < this.props.items.length; i++) { 35 | var item = this.props.items[i]; 36 | details.push( 37 | 38 | {item.name} 39 | {item.price} 40 | 41 | ); 42 | }; 43 | return ( 44 |
45 | {header} 46 | 47 | 48 | {details} 49 | 50 | 51 | 52 | 53 | 54 |
Total{this.props.total}
55 |
Status: {this.state.orderStatus}
56 |
57 | ); 58 | } 59 | }); 60 | 61 | The following TypeScript interfaces were automatically generated from the JSX file above. 62 | 63 | // This file was automatically generated by jsxtyper. Do not modify by hand! 64 | 65 | interface OrderPageProps { 66 | initialOrderStatus: any; 67 | customerName: any; 68 | orderDate: any; 69 | items: { 70 | name: any; 71 | price: any; 72 | }[]; 73 | total: any; 74 | } 75 | 76 | interface OrderPageState { 77 | orderStatus: any; 78 | } 79 | 80 | declare var OrderPage: React.ComponentClass; 81 | 82 | The generated interfaces are compatible with [react-global.d.ts](https://github.com/borisyankov/DefinitelyTyped/blob/master/react/react-global.d.ts). 83 | 84 | The `SampleProject` folder contains a complete sample project. 85 | 86 | ## How to run JSXtyper 87 | 88 | Open Node.js command prompt and run 89 | 90 | node jsxtyper.js example.jsx 91 | 92 | ## Grunt task 93 | 94 | JSXtyper can also be invoked from Grunt. 95 | 96 | To install type 97 | 98 | npm install grunt-jsxtyper --save-dev 99 | 100 | Here's a sample Gruntfile.js: 101 | 102 | module.exports = function(grunt) { 103 | 104 | grunt.initConfig({ 105 | pkg: grunt.file.readJSON('package.json'), 106 | jsxtyper: { 107 | 'Generated/Views.ts': ['Views/*.jsx'] 108 | } 109 | }); 110 | 111 | grunt.loadNpmTasks('grunt-jsxtyper'); 112 | 113 | grunt.registerTask('default', ['jsxtyper']); 114 | }; 115 | -------------------------------------------------------------------------------- /SampleProject/.gitignore: -------------------------------------------------------------------------------- 1 | typings/ 2 | packages/ 3 | node_modules/ 4 | Generated/ 5 | *.user 6 | *.css 7 | -------------------------------------------------------------------------------- /SampleProject/Assets/caretdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fuselabs/jsxtyper/b56654594db88054980ed1958db219bcfba29f5a/SampleProject/Assets/caretdown.png -------------------------------------------------------------------------------- /SampleProject/Assets/style.less: -------------------------------------------------------------------------------- 1 | // sizes 2 | @left-panel-width: 220px; 3 | 4 | // fonts 5 | @base-font: 'Segoe UI', Arial, Helvetica, sans-serif; 6 | 7 | // colors 8 | @brand-color: #0072c6; 9 | @button-background: #cacaca; 10 | 11 | // z-indices 12 | @z-dropdown: 100; 13 | @z-dialog-mask: 999; 14 | 15 | body { 16 | margin: 0; 17 | font-family: @base-font; 18 | } 19 | 20 | button { 21 | font-family: @base-font; 22 | height: 30px; 23 | min-width: 90px; 24 | border: 0; 25 | background-color: @button-background; 26 | &:hover { 27 | background-color: lighten(@button-background, 10%); 28 | } 29 | 30 | // Style for default buttons 31 | &.default-button { 32 | background-color: @brand-color; 33 | color: white; 34 | &:hover { 35 | background-color: lighten(@brand-color, 10%); 36 | } 37 | } 38 | } 39 | 40 | .accounts-table { 41 | th { 42 | text-align: left; 43 | background-color: #fafafa; 44 | } 45 | td:nth-child(1) { 46 | min-width: 225px; 47 | } 48 | td:nth-child(2) { 49 | text-align: right; 50 | } 51 | tr:nth-child(even) { 52 | background-color: #fafafa; 53 | } 54 | th, td { 55 | padding: 0 15px 0 15px; 56 | } 57 | } 58 | 59 | .header { 60 | background-color: @brand-color; 61 | color: white; 62 | .company-name { 63 | display: inline-block; 64 | font-size: 36px; 65 | font-style: italic; 66 | font-weight: bold; 67 | padding: 5px 20px 5px 20px; 68 | } 69 | .rhs { 70 | float: right; 71 | margin: 20px 15px 0 0; 72 | } 73 | .sign-out { 74 | display: inline-block; 75 | cursor: pointer; 76 | } 77 | .goto { 78 | display: inline-block; 79 | cursor: pointer; 80 | margin-right: 70px; 81 | &::after { 82 | content: ''; 83 | display: inline-block; 84 | position: relative; 85 | left: 7px; 86 | top: 0px; 87 | background-image: url(/Assets/caretdown.png); 88 | width: 8px; 89 | height: 8px; 90 | } 91 | } 92 | } 93 | 94 | .leftnav { 95 | position: fixed; 96 | display: inline-block; 97 | width: @left-panel-width; 98 | height: 100%; 99 | padding-top: 30px; 100 | background-color: #f3f3f3; 101 | div { 102 | margin: 10px 0 10px 20px; 103 | } 104 | a { 105 | text-decoration: none; 106 | &:hover { 107 | text-decoration: underline; 108 | } 109 | } 110 | } 111 | 112 | .leftnav-underlay { 113 | display: inline-block; 114 | width: @left-panel-width; 115 | } 116 | 117 | .page-content { 118 | margin-left: 15px; 119 | display: inline-block; 120 | } 121 | 122 | .dropdown-menu { 123 | position: absolute; 124 | background-color: white; 125 | cursor: default; 126 | -webkit-box-shadow: 0px 1px 3px #666; 127 | box-shadow: 0px 1px 3px #666; 128 | outline: 0; 129 | z-index: @z-dropdown; 130 | 131 | .menu-item { 132 | padding: 2px 14px 3px 14px; 133 | 134 | &.highlight { 135 | color: white; 136 | background-color: lighten(@brand-color, 10%); 137 | } 138 | } 139 | } 140 | 141 | .dialog-mask { 142 | z-index: @z-dialog-mask; 143 | background-color: rgba(0, 0, 0, 0.3); 144 | position: fixed; 145 | left: 0; 146 | top: 0; 147 | width: 100%; 148 | height: 100%; 149 | } 150 | 151 | .dialog { 152 | position: absolute; 153 | min-width: 270px; 154 | border: 1px solid #999; 155 | padding: 10px; 156 | background-color: white; 157 | 158 | .button-panel { 159 | margin-top: 25px; 160 | text-align: right; 161 | } 162 | } 163 | 164 | .message-box { 165 | width: 350px; 166 | } 167 | 168 | .change-password { 169 | input[type="password"] { 170 | border: 1px solid #999; 171 | margin-bottom: 5px; 172 | padding: 3px; 173 | min-width: 275px; 174 | } 175 | } -------------------------------------------------------------------------------- /SampleProject/BankApp.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// 5 | /// 6 | /// 7 | 8 | module BankDemo { 9 | export class BankApp extends SimpleMvc.App { 10 | private bank = new Model.Bank(); 11 | 12 | constructor() { 13 | super(); 14 | 15 | var router = this.getRouter(); 16 | router.addRoute("/", Controllers.HomeController); 17 | router.addRoute("/account", Controllers.AccountController); 18 | router.addRoute("/account/transaction", Controllers.TransactionController); 19 | router.addRoute("/support", Controllers.SupportController); 20 | 21 | this.load(); 22 | } 23 | 24 | public getBank(): Model.Bank { 25 | return this.bank; 26 | } 27 | } 28 | } 29 | 30 | $(() => { 31 | new BankDemo.BankApp(); 32 | }); 33 | -------------------------------------------------------------------------------- /SampleProject/BankDemo.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | {040723E0-1A77-48F2-9F8B-523BFFB9B974} 7 | {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} 8 | Library 9 | bin 10 | v4.0 11 | full 12 | true 13 | 1.4 14 | true 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | style.less 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | web.config 45 | 46 | 47 | web.config 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 12.0 70 | 71 | 72 | BankDemo 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | True 81 | True 82 | 54270 83 | / 84 | http://localhost:54270/ 85 | False 86 | False 87 | 88 | 89 | False 90 | 91 | 92 | 93 | 94 | 95 | false 96 | true 97 | Generated\demo.js 98 | none 99 | 100 | 101 | true 102 | false 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /SampleProject/BankDemo.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.31101.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BankDemo", "BankDemo.csproj", "{040723E0-1A77-48F2-9F8B-523BFFB9B974}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {040723E0-1A77-48F2-9F8B-523BFFB9B974}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {040723E0-1A77-48F2-9F8B-523BFFB9B974}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {040723E0-1A77-48F2-9F8B-523BFFB9B974}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {040723E0-1A77-48F2-9F8B-523BFFB9B974}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /SampleProject/Controllers/AccountController.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// 5 | 6 | module BankDemo.Controllers { 7 | export class AccountController extends ControllerBase { 8 | private accountType: number; 9 | private account: Model.Account; 10 | 11 | constructor(app: BankApp) { 12 | super(app); 13 | } 14 | 15 | public load(params: SimpleMvc.QueryParams): void { 16 | super.load(params); 17 | 18 | // Get data based on query parameters. 19 | this.accountType = parseInt(params["type"]); 20 | this.account = this.app.getBank().getAccount(this.accountType); 21 | 22 | // Render page. 23 | var props: AccountPageProps = { 24 | account: this.account 25 | }; 26 | var element = React.createElement(AccountPage, props); 27 | React.render(element, this.pageContent, () => { 28 | this.attachEventHandlers(); 29 | }); 30 | } 31 | 32 | private attachEventHandlers(): void { 33 | $(AccountPageSelectors.widthdrawButton).on('click', () => this.onWithdrawClicked()); 34 | $(AccountPageSelectors.depositButton).on('click', () => this.onDepositClicked()); 35 | } 36 | 37 | private onDepositClicked(): void { 38 | this.app.navigate("/account/transaction", { "type": this.accountType.toString(), mode: "deposit" }); 39 | } 40 | 41 | private onWithdrawClicked(): void { 42 | this.app.navigate("/account/transaction", { "type": this.accountType.toString(), mode: "withdrawal" }); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /SampleProject/Controllers/ControllerBase.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// 5 | /// 6 | /// 7 | 8 | module BankDemo.Controllers { 9 | /** 10 | * This is the base class for all controllers in this app. 11 | * Here we render parts common to all pages, and handle events in those parts. 12 | */ 13 | export class ControllerBase extends SimpleMvc.Controller { 14 | protected pageContent: Element; 15 | 16 | constructor(public app: BankApp) { 17 | super(); 18 | } 19 | 20 | public load(params: SimpleMvc.QueryParams): void { 21 | super.load(params); 22 | 23 | // Render page 24 | var $content = $('
').appendTo($(document.body)); 25 | var element = React.createElement(MasterPage, {}); 26 | React.render(element, $content[0], () => { 27 | this.pageContent = $(MasterPageSelectors.pageContent)[0]; 28 | this.attachHandlers(); 29 | }); 30 | } 31 | 32 | private attachHandlers(): void { 33 | $(MasterPageSelectors.goto).on('click', ev => this.onGotoClicked(ev)); 34 | $(MasterPageSelectors.signOut).on('click', () => this.onSignOutClicked()); 35 | } 36 | 37 | public onGotoClicked(ev: JQueryEventObject): void { 38 | // Add menu to DOM and position it 39 | var $menu = $("
").appendTo($(document.body)); 40 | var gotoOffset = $(ev.target).offset(); 41 | $menu.css({ left: gotoOffset.left + 'px', top: (gotoOffset.top + 25) + 'px' }); 42 | 43 | // Display commands in the menu 44 | var commands = this.getCommands(); 45 | var menu = new Controls.DropdownMenu($menu, { items: commands }); 46 | menu.itemSelectedEvent.addListener((sender, ev) => { 47 | // Execute the selected command 48 | commands[ev.selectedItemIndex].action(); 49 | }); 50 | } 51 | 52 | public onSignOutClicked(): void { 53 | Dialogs.MessageBox.show("Thank you for clicking the sign out button."); 54 | } 55 | 56 | public onChangePassword(): void { 57 | var dialog = new Dialogs.ChangePasswordDialog(); 58 | dialog.showDialog().done(() => { 59 | Dialogs.MessageBox.show("Your password has been updated."); 60 | }); 61 | } 62 | 63 | /** Gets commands to be displayed in a menu. */ 64 | public getCommands() { 65 | return [ 66 | new Command("Pay bills"), 67 | new Command("Reorder checks"), 68 | new Command("Change password", () => this.onChangePassword()), 69 | new Command("Open a new account"), 70 | new Command("Investments"), 71 | new Command("Wire transfer"), 72 | new Command("Statements") 73 | ]; 74 | } 75 | } 76 | 77 | /** Represents a command to be displayed in a menu. Each command has a label and an action method. */ 78 | export class Command { 79 | constructor(public label: string, public action?: () => void) { 80 | if (!this.action) 81 | this.action = () => Dialogs.MessageBox.show("You selected: " + this.label); 82 | } 83 | 84 | public toString(): string { 85 | return this.label; 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /SampleProject/Controllers/HomeController.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// 5 | /// 6 | 7 | module BankDemo.Controllers { 8 | export class HomeController extends ControllerBase { 9 | constructor(app: BankApp) { 10 | super(app); 11 | } 12 | 13 | public load(params: SimpleMvc.QueryParams): void { 14 | super.load(params); 15 | 16 | var props: HomePageProps = { 17 | accounts: this.app.getBank().getAccounts() 18 | }; 19 | var element = React.createElement(HomePage, props); 20 | React.render(element, this.pageContent); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /SampleProject/Controllers/SupportController.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// 5 | 6 | module BankDemo.Controllers { 7 | export class SupportController extends ControllerBase { 8 | constructor(app: BankApp) { 9 | super(app); 10 | } 11 | 12 | public load(params: SimpleMvc.QueryParams): void { 13 | super.load(params); 14 | 15 | var element = React.createElement(SupportPage, {}); 16 | React.render(element, this.pageContent); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /SampleProject/Controllers/TransactionController.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// 5 | /// 6 | 7 | module BankDemo.Controllers { 8 | export class TransactionController extends ControllerBase { 9 | private accountType: number; 10 | private depositing: boolean; 11 | private account: Model.Account; 12 | private component: React.Component; 13 | private state: TransactionPageState; 14 | 15 | constructor(app: BankApp) { 16 | super(app); 17 | } 18 | 19 | public load(params: SimpleMvc.QueryParams): void { 20 | super.load(params); 21 | 22 | // Get data based on query parameters 23 | this.accountType = parseInt(params["type"]); 24 | this.account = this.app.getBank().getAccount(this.accountType); 25 | this.depositing = (params["mode"] == "deposit"); 26 | 27 | // Render page 28 | var props: TransactionPageProps = { 29 | labels: this.depositing ? this.depositLabels : this.withdrawalLabels, 30 | accountName: this.account.getName() 31 | }; 32 | var element = React.createElement(TransactionPage, props); 33 | this.component = React.render(element, this.pageContent, () => { 34 | this.attachEventHandlers(); 35 | }); 36 | this.state = { balance: this.account.getBalance() }; 37 | this.component.setState(this.state); 38 | } 39 | 40 | private attachEventHandlers(): void { 41 | $(TransactionPageSelectors.okButton).on('click', () => this.onOkClicked()); 42 | $(TransactionPageSelectors.cancelButton).on('click', () => this.onCancelClicked()); 43 | } 44 | 45 | private onOkClicked(): void { 46 | if (this.depositing) 47 | this.performDeposit(); 48 | else 49 | this.performWithdrawal(); 50 | } 51 | 52 | private performDeposit(): void { 53 | var amountStr = $(TransactionPageSelectors.amountInput).val(); 54 | var amount = parseInt(amountStr); 55 | try { 56 | this.account.depositMoney(amount); 57 | this.state.balance = this.account.getBalance(); 58 | this.component.setState(this.state); 59 | Dialogs.MessageBox.show("Amount deposited.").done(() => { 60 | this.app.navigate("/"); 61 | }); 62 | } 63 | catch (ex) { 64 | Dialogs.MessageBox.show((ex).message); 65 | } 66 | } 67 | 68 | private performWithdrawal(): void { 69 | var amountStr = $(TransactionPageSelectors.amountInput).val(); 70 | var amount = parseInt(amountStr); 71 | try { 72 | this.account.withdrawMoney(amount); 73 | this.state.balance = this.account.getBalance(); 74 | this.component.setState(this.state); 75 | Dialogs.MessageBox.show("Amount withdrawn.").done(() => { 76 | this.app.navigate("/"); 77 | }); 78 | } 79 | catch (ex) { 80 | Dialogs.MessageBox.show((ex).message); 81 | } 82 | } 83 | 84 | private onCancelClicked(): void { 85 | this.app.navigate("/account", { type: this.accountType.toString() }); 86 | } 87 | 88 | private depositLabels: TransactionPageStrings = { 89 | transactionType: "Deposit", 90 | prompt: "How much do you want to deposit?" 91 | }; 92 | 93 | private withdrawalLabels: TransactionPageStrings = { 94 | transactionType: "Withdrawal", 95 | prompt: "How much do you want to withdraw?" 96 | }; 97 | } 98 | 99 | export interface TransactionPageStrings { 100 | transactionType: string; 101 | prompt: string; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /SampleProject/Controls/DropdownMenu.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | module BankDemo.Controls { 5 | export class DropdownMenu { 6 | // Events 7 | public itemSelectedEvent = new SimpleMvc.Event(); 8 | 9 | // Css classes 10 | private menuCssClass = "dropdown-menu"; 11 | private highlightCssClass = "highlight"; 12 | 13 | // Private members 14 | private ops: DropdownMenuOptions; 15 | 16 | constructor(private $el: JQuery, options?: DropdownMenuOptions) { 17 | this.ops = $.extend({}, DefaultDropdownMenuOptions, options); 18 | this.render(); 19 | this.$el.focus(); 20 | this.handleEvents(); 21 | } 22 | 23 | private render(): void { 24 | this.$el.addClass(this.menuCssClass).attr("tabindex", "0"); 25 | if (this.ops.items && this.ops.items.length) { 26 | var labels = this.ops.items.map(value => value.toString()); 27 | var element = React.createElement(DropdownMenuPanel, { items: labels }); 28 | React.render(element, this.$el[0]); 29 | } 30 | } 31 | 32 | private handleEvents(): void { 33 | this.$el.on('blur', () => this.close()); 34 | this.$el.on('mouseover', DropdownMenuPanelSelectors.menuItem, ev => this.onMouseOverItem(ev)); 35 | this.$el.on('mouseout', ev => this.$el.find('.' + this.highlightCssClass).removeClass(this.highlightCssClass)); 36 | this.$el.on('mousedown', ev => this.onItemSelected(this.$el.find('.' + this.highlightCssClass))); 37 | this.$el.on('keydown', ev => this.onKeyDown(ev)); 38 | } 39 | 40 | private onMouseOverItem(ev: JQueryEventObject): void { 41 | this.$el.find('.' + this.highlightCssClass).removeClass(this.highlightCssClass); 42 | $(ev.currentTarget).addClass(this.highlightCssClass); 43 | } 44 | 45 | private onKeyDown(ev: JQueryEventObject): void { 46 | switch (ev.which) { 47 | case KeyCodes.Escape: 48 | this.close(); 49 | break; 50 | case KeyCodes.Enter: 51 | this.onItemSelected(this.$el.find('.' + this.highlightCssClass)); 52 | break; 53 | case KeyCodes.UpArrow: 54 | case KeyCodes.DownArrow: 55 | var $items = this.$el.find(DropdownMenuPanelSelectors.menuItem); 56 | var $highlighted = this.$el.find('.' + this.highlightCssClass); 57 | var index = $items.index($highlighted); 58 | var newIndex; 59 | if (ev.which == KeyCodes.UpArrow) 60 | newIndex = (index == -1 || index == 0) ? $items.length - 1 : index - 1; 61 | else 62 | newIndex = (index == -1 || index == $items.length - 1) ? 0 : index + 1; 63 | $highlighted.removeClass(this.highlightCssClass); 64 | $items.eq(newIndex).addClass(this.highlightCssClass); 65 | break; 66 | } 67 | ev.preventDefault(); 68 | ev.stopPropagation(); 69 | } 70 | 71 | private onItemSelected($item: JQuery): void { 72 | var index = this.$el.find(DropdownMenuPanelSelectors.menuItem).index($item); 73 | if (index != -1) { 74 | this.itemSelectedEvent.trigger(this, { selectedItemIndex: index }); 75 | this.close(); 76 | } 77 | } 78 | 79 | private close(): void { 80 | this.$el.remove(); 81 | } 82 | } 83 | 84 | export interface DropdownMenuOptions { 85 | /** Items to display in the menu. Set to null if menu items are already rendered. */ 86 | items?: any[]; 87 | } 88 | 89 | export var DefaultDropdownMenuOptions: DropdownMenuOptions = { 90 | items: null 91 | }; 92 | 93 | export class MenuItemSelectedEventArgs extends SimpleMvc.EventArgs { 94 | public selectedItemIndex: number; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /SampleProject/Controls/KeyCodes.ts: -------------------------------------------------------------------------------- 1 | module BankDemo.Controls { 2 | export enum KeyCodes { 3 | Backspace = 8, 4 | Tab = 9, 5 | Enter = 13, 6 | Escape = 27, 7 | PageUp = 33, 8 | PageDown = 34, 9 | End = 35, 10 | Home = 36, 11 | LeftArrow = 37, 12 | UpArrow = 38, 13 | RightArrow = 39, 14 | DownArrow = 40, 15 | Delete = 46 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /SampleProject/Dialogs/ChangePasswordDialog.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | module BankDemo.Dialogs { 5 | 6 | // How to use dialogs: 7 | // var dialog = SomeDialog(); 8 | // dialog.field1 = "foo"; 9 | // dialog.field2 = "bar"; 10 | // dialog.showDialog().done(() => { 11 | // // User pressed OK. 12 | // // Here dialog.field1 and dialog.field2 will have new values entered by the user. 13 | // }); 14 | 15 | export class ChangePasswordDialog extends DialogBase { 16 | // public members 17 | public oldPassword = ""; 18 | public newPassword = ""; 19 | 20 | constructor() { 21 | super(); 22 | } 23 | 24 | protected render(): void { 25 | var element = React.createElement(ChangePasswordPanel, {}); 26 | React.render(element, this.$el[0], () => { 27 | this.init(); 28 | }); 29 | } 30 | 31 | protected init(): void { 32 | // Call base class method to complete initialization. 33 | super.init(); 34 | } 35 | 36 | public onOK(): void { 37 | // Get new values from dialog controls. 38 | this.oldPassword = this.$el.find(ChangePasswordPanelSelectors.oldPasswordInput).val(); 39 | this.newPassword = this.$el.find(ChangePasswordPanelSelectors.newPasswordInput).val(); 40 | 41 | super.onOK(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /SampleProject/Dialogs/DialogBase.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | module BankDemo.Dialogs { 5 | /** Base class for modal dialogs. */ 6 | export class DialogBase { 7 | protected $el: JQuery; 8 | private $mask: JQuery; 9 | private dfd = $.Deferred(); 10 | private $okButton: JQuery; 11 | private $cancelButton: JQuery; 12 | 13 | constructor() { 14 | this.$mask = $('
').appendTo($(document.body)); 15 | this.$el = $('
').appendTo(this.$mask); 16 | } 17 | 18 | protected render(): void { 19 | } 20 | 21 | protected init(): void { 22 | this.$okButton = this.$el.find(".ok-button"); 23 | this.$cancelButton = this.$el.find(".cancel-button"); 24 | this.centerDialog(); 25 | this.handleEvents(); 26 | this.$okButton.focus(); 27 | } 28 | 29 | protected handleEvents(): void { 30 | this.$okButton.click(() => this.onOK()); 31 | this.$cancelButton.click(() => this.onCancel()); 32 | this.$el.keydown(ev => this.onKeyDown(ev)); 33 | } 34 | 35 | protected onKeyDown(ev: JQueryKeyEventObject): void { 36 | switch (ev.which) { 37 | case Controls.KeyCodes.Enter: 38 | this.onEnterKeyPressed(); 39 | ev.preventDefault(); 40 | break; 41 | case Controls.KeyCodes.Escape: 42 | if (this.$cancelButton.is(":visible")) { 43 | this.onEscapeKeyPressed(); 44 | } 45 | ev.preventDefault(); 46 | break; 47 | } 48 | } 49 | 50 | /** Handles enter key. May be overridden to change default behavior. */ 51 | protected onEnterKeyPressed(): void { 52 | this.onOK(); 53 | } 54 | 55 | /** Handles escape key. May be overridden to change default behavior. */ 56 | protected onEscapeKeyPressed(): void { 57 | this.onCancel(); 58 | } 59 | 60 | private centerDialog(): void { 61 | var t = ($(window).height() - this.$el.height()) / 3; 62 | var l = ($(window).width() - this.$el.width()) / 2; 63 | t = Math.max(0, t); 64 | l = Math.max(0, l); 65 | this.$el.css({ top: t + 'px', left: l + 'px' }); 66 | } 67 | 68 | public showDialog(): JQueryPromise { 69 | this.render(); 70 | return this.dfd.promise(); 71 | } 72 | 73 | private closeDialog(): void { 74 | this.$el.remove(); 75 | this.$mask.remove(); 76 | } 77 | 78 | protected onCancel(): void { 79 | this.closeDialog(); 80 | this.dfd.reject(); 81 | } 82 | 83 | /** Handles OK button. Override this method and get user input, then call this base class method. */ 84 | protected onOK(): void { 85 | this.closeDialog(); 86 | this.dfd.resolve(); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /SampleProject/Dialogs/MessageBox.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | 5 | module BankDemo.Dialogs { 6 | export class MessageBox extends DialogBase { 7 | public message: string; 8 | private options: MessageBoxOptions; 9 | 10 | constructor(options: MessageBoxOptions) { 11 | this.options = $.extend({}, DefaultMessageBoxOptions, options); 12 | super(); 13 | } 14 | 15 | protected render(): void { 16 | var props: MessageBoxPanelProps = { 17 | okButtonLabel: this.options.okButtonLabel, 18 | cancelButtonLabel: this.options.cancelButtonLabel, 19 | hideCancelButton: this.options.hideCancelButton, 20 | message: this.message 21 | }; 22 | var element = React.createElement(MessageBoxPanel, props); 23 | React.render(element, this.$el[0], () => { 24 | this.init(); 25 | }); 26 | } 27 | 28 | protected init(): void { 29 | // Call base class method to complete initialization. 30 | super.init(); 31 | } 32 | 33 | /** Static method for displaying a simple message. */ 34 | public static show(message: string): JQueryPromise { 35 | var mb = new MessageBox({ hideCancelButton: true }); 36 | mb.message = message; 37 | return mb.showDialog(); 38 | } 39 | 40 | /** Static method for asking the user a yes/no question. OK/Cancel buttons are relabeled to Yes/No. */ 41 | public static ask(title: string, message: string): JQueryPromise { 42 | var mb = new MessageBox({ okButtonLabel: "Yes", cancelButtonLabel: "No", dialogTitle: title }); 43 | mb.message = message; 44 | return mb.showDialog(); 45 | } 46 | } 47 | 48 | export interface MessageBoxOptions { 49 | hideCancelButton?: boolean; 50 | okButtonLabel?: string; 51 | cancelButtonLabel?: string; 52 | } 53 | 54 | var DefaultMessageBoxOptions: MessageBoxOptions = { 55 | hideCancelButton: false, 56 | okButtonLabel: 'OK', 57 | cancelButtonLabel: 'Cancel' 58 | }; 59 | } 60 | -------------------------------------------------------------------------------- /SampleProject/Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | 3 | // Project configuration 4 | grunt.initConfig({ 5 | pkg: grunt.file.readJSON('package.json'), 6 | tsd: { 7 | refresh: { 8 | options: { 9 | command: 'reinstall', 10 | config: 'tsd.json', 11 | } 12 | } 13 | }, 14 | less: { 15 | development: { 16 | compress: false, 17 | files: { 18 | 'Assets/style.css' : 'Assets/style.less' 19 | } 20 | } 21 | }, 22 | react: { 23 | dynamic_mappings: { 24 | files: [ 25 | { 26 | expand: true, 27 | cwd: 'Views', 28 | src: ['**/*.jsx'], 29 | dest: 'Views', 30 | ext: '.js' 31 | } 32 | ] 33 | } 34 | }, 35 | concat: { 36 | dist: { 37 | src: ['Views/*.js'], 38 | dest: 'Generated/views.js', 39 | }, 40 | }, 41 | jsxtyper: { 42 | 'Generated/ViewDefs.ts': ['Views/*.jsx'] 43 | } 44 | }); 45 | 46 | // Load plugins 47 | grunt.loadNpmTasks('grunt-tsd'); 48 | grunt.loadNpmTasks('grunt-jsxtyper'); 49 | grunt.loadNpmTasks('grunt-react'); 50 | grunt.loadNpmTasks('grunt-contrib-less'); 51 | grunt.loadNpmTasks('grunt-contrib-concat'); 52 | 53 | // Default tasks 54 | grunt.registerTask('default', ['tsd', 'react', 'less', 'concat', 'jsxtyper']); 55 | }; 56 | -------------------------------------------------------------------------------- /SampleProject/Libs/simplemvc.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | declare module SimpleMvc { 3 | class Exception { 4 | message: string; 5 | name: string; 6 | constructor(message: string, name?: string); 7 | } 8 | } 9 | declare module SimpleMvc { 10 | /** 11 | * You can reduce network traffic by storing frequently needed objects in the cache. 12 | * By specifying time-to-live you can control how stale data is allowed to get. 13 | */ 14 | class Cache { 15 | private objects; 16 | constructor(); 17 | setValue(key: string, value: any, secondsToLive: number): void; 18 | getValue(key: string): any; 19 | removeValue(key: string): void; 20 | clear(): void; 21 | clean(): void; 22 | } 23 | } 24 | declare module SimpleMvc { 25 | /** 26 | * Your model objects and your custom controls can implement events to let other parts of the 27 | * application know that something has happened. 28 | */ 29 | class Event { 30 | private listeners; 31 | trigger(sender: T1, eventArgs: T2): any; 32 | addListener(listener: (sender: T1, eventArgs: T2) => void): void; 33 | removeListener(listener: (sender: T1, eventArgs: T2) => void): void; 34 | } 35 | class EventArgs { 36 | static Empty: EventArgs; 37 | } 38 | class PropertyChangedEventArgs extends EventArgs { 39 | propertyName: string; 40 | } 41 | } 42 | declare module SimpleMvc { 43 | /** 44 | * Each URL path in your app will have a corresponding controller, which should be a subclass of this class. 45 | * The controller is responsible for rendering the entire page and handling user input as well as UI events. 46 | * A new instance of the controller is created each time the user navigates to the path. 47 | * Data that must live longer than the page should be stored in your subclass of App, or in the cache. 48 | * 49 | * You can have a hierarchy of controllers, with parts that are common to all pages rendered by the base controller. 50 | */ 51 | class Controller { 52 | private loaded; 53 | /** 54 | * Renders the page, and sets up event handlers. 55 | * Note that since the user can press the refresh button of the browser, the controller is 56 | * responsible for rendering the entire page, not just the portions that have changed. 57 | */ 58 | load(params: QueryParams): void; 59 | /** 60 | * Check if this controller is still loaded. 61 | * When an ajax call returns, update the UI only if this method returns true. 62 | */ 63 | isLoaded(): boolean; 64 | /** 65 | * Whether the controller is unloadable is returned through a callback. 66 | * This gives the controller the opportunity to display a "Do you want to save?" dialog. 67 | */ 68 | isUnloadable(callback: (unloadable: boolean) => void): void; 69 | /** 70 | * Performs any cleanup or finalization that must be performed before the page is torn down. 71 | */ 72 | unload(): void; 73 | } 74 | interface ControllerClass { 75 | new (app: App): Controller; 76 | } 77 | class PageNotFoundController extends Controller { 78 | load(params: QueryParams): void; 79 | } 80 | } 81 | declare module SimpleMvc { 82 | /** 83 | * The router is responsible for unloading and loading controllers when a page navigation 84 | * happens inside your app. You must associate paths with controllers when your application starts. 85 | * Each time the user navigates to a specific path/page a new instance of the controller will be created. 86 | */ 87 | class Router { 88 | private routes; 89 | private resolver; 90 | private app; 91 | /** The currently active controller. */ 92 | private currentController; 93 | /** The hash corresponding to the current controller. */ 94 | private currentHash; 95 | constructor(app: App); 96 | /** Associates a path with a controller class. */ 97 | addRoute(path: string, controllerClass: ControllerClass): void; 98 | /** 99 | * Sets a custom resolver that examines the path and the query parameters and returns 100 | * a controller class and extra parameters to pass to the controller. 101 | */ 102 | setCustomResolver(resolver: Resolver): void; 103 | /** Loads the controller corresponding to the browser's current location. */ 104 | loadController(): void; 105 | /** Loads the supplied controller and sets it as the current controller. */ 106 | private loadNewController(controllerClass, query); 107 | /** Restores hash to its previous value. */ 108 | private restoreHash(); 109 | /** Removes the hash change handler. */ 110 | private detachHashChangeHandler(); 111 | /** Attaches the hash change handler. */ 112 | private attachHashChangeHandler(); 113 | /** Updates the hash without reloading the page. */ 114 | updateHash(hash: string): void; 115 | /** Replaces a query parameter in the browser's location without reloading the page. */ 116 | replaceQueryParameter(parameter: string, newValue: string): void; 117 | /** Constructs the query part of the hash from the supplied name value pairs. */ 118 | private constructQueryString(query); 119 | /** Parses the query part of the hash. */ 120 | private parseQuery(q); 121 | /** Parses the hash from the browser's current location. */ 122 | parseHash(): { 123 | path: string; 124 | query: QueryParams; 125 | }; 126 | /** Navigates the app to the new path. */ 127 | navigate(path: string, query?: QueryParams): void; 128 | /** This enables your App subclass to talk to the currently active controller. */ 129 | getCurrentController(): Controller; 130 | } 131 | interface QueryParams { 132 | [index: string]: string; 133 | } 134 | /** Custom resolver function. */ 135 | type Resolver = (path: string, params: QueryParams) => ResolverResult; 136 | /** The return type of custom resolver. */ 137 | interface ResolverResult { 138 | /** The controller class corresponding to the hash */ 139 | controllerClass: ControllerClass; 140 | /** Extra query parameters to be passed to the controller */ 141 | extraParams?: QueryParams; 142 | } 143 | } 144 | declare module SimpleMvc { 145 | /** 146 | * This is the root object of the application. 147 | * Do not use this class directly, make your own subclass instead. 148 | */ 149 | class App { 150 | static cacheCleaningIntervalMinutes: number; 151 | private static instance; 152 | private router; 153 | private cache; 154 | constructor(); 155 | /** This method must be called after your App subclass is fully initialized. */ 156 | load(): void; 157 | getCache(): Cache; 158 | getRouter(): Router; 159 | static getInstance(): App; 160 | /** 161 | * Navigates the app to the page corresponding to the supplied URL. 162 | * @path path to the page for example "/bank/deposits" will set the location to http://localhost/#/bank/deposits 163 | */ 164 | navigate(path: string, queryParams?: QueryParams): void; 165 | private static setupCacheCleaner(); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /SampleProject/Libs/simplemvc.js: -------------------------------------------------------------------------------- 1 | var SimpleMvc; 2 | (function (SimpleMvc) { 3 | var Exception = (function () { 4 | function Exception(message, name) { 5 | this.message = message; 6 | this.name = name; 7 | if (!this.name) 8 | this.name = "ApplicationException"; 9 | } 10 | return Exception; 11 | })(); 12 | SimpleMvc.Exception = Exception; 13 | })(SimpleMvc || (SimpleMvc = {})); 14 | var SimpleMvc; 15 | (function (SimpleMvc) { 16 | /** 17 | * You can reduce network traffic by storing frequently needed objects in the cache. 18 | * By specifying time-to-live you can control how stale data is allowed to get. 19 | */ 20 | var Cache = (function () { 21 | function Cache() { 22 | this.objects = {}; 23 | } 24 | Cache.prototype.setValue = function (key, value, secondsToLive) { 25 | var expirationTime = new Date().getTime() + secondsToLive * 1000; 26 | this.objects[key] = { value: value, expirationTime: expirationTime }; 27 | }; 28 | Cache.prototype.getValue = function (key) { 29 | var val = this.objects[key]; 30 | if (!val) 31 | return null; 32 | if (val.expirationTime < new Date().getTime()) { 33 | delete this.objects[key]; 34 | return null; 35 | } 36 | return val.value; 37 | }; 38 | Cache.prototype.removeValue = function (key) { 39 | delete this.objects[key]; 40 | }; 41 | Cache.prototype.clear = function () { 42 | this.objects = {}; 43 | }; 44 | Cache.prototype.clean = function () { 45 | var expired = []; 46 | var now = new Date().getTime(); 47 | for (var key in this.objects) { 48 | var val = this.objects[key]; 49 | if (val.expirationTime < now) 50 | expired.push(key); 51 | } 52 | for (var i = 0; i < expired.length; i++) 53 | delete this.objects[expired[i]]; 54 | }; 55 | return Cache; 56 | })(); 57 | SimpleMvc.Cache = Cache; 58 | var CachedValue = (function () { 59 | function CachedValue() { 60 | } 61 | return CachedValue; 62 | })(); 63 | })(SimpleMvc || (SimpleMvc = {})); 64 | var __extends = this.__extends || function (d, b) { 65 | for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; 66 | function __() { this.constructor = d; } 67 | __.prototype = b.prototype; 68 | d.prototype = new __(); 69 | }; 70 | var SimpleMvc; 71 | (function (SimpleMvc) { 72 | /** 73 | * Your model objects and your custom controls can implement events to let other parts of the 74 | * application know that something has happened. 75 | */ 76 | var Event = (function () { 77 | function Event() { 78 | this.listeners = []; 79 | } 80 | Event.prototype.trigger = function (sender, eventArgs) { 81 | for (var i = 0; i < this.listeners.length; i++) { 82 | this.listeners[i].call(sender, sender, eventArgs); 83 | } 84 | }; 85 | Event.prototype.addListener = function (listener) { 86 | for (var i = 0; i < this.listeners.length; i++) { 87 | if (this.listeners[i] == listener) { 88 | throw new SimpleMvc.Exception("Listener already added."); 89 | } 90 | } 91 | this.listeners.push(listener); 92 | }; 93 | Event.prototype.removeListener = function (listener) { 94 | for (var i = 0; i < this.listeners.length; i++) { 95 | if (this.listeners[i] == listener) { 96 | this.listeners.splice(i, 1); 97 | return; 98 | } 99 | } 100 | }; 101 | return Event; 102 | })(); 103 | SimpleMvc.Event = Event; 104 | var EventArgs = (function () { 105 | function EventArgs() { 106 | } 107 | EventArgs.Empty = new EventArgs(); 108 | return EventArgs; 109 | })(); 110 | SimpleMvc.EventArgs = EventArgs; 111 | var PropertyChangedEventArgs = (function (_super) { 112 | __extends(PropertyChangedEventArgs, _super); 113 | function PropertyChangedEventArgs() { 114 | _super.apply(this, arguments); 115 | } 116 | return PropertyChangedEventArgs; 117 | })(EventArgs); 118 | SimpleMvc.PropertyChangedEventArgs = PropertyChangedEventArgs; 119 | })(SimpleMvc || (SimpleMvc = {})); 120 | /// 121 | var SimpleMvc; 122 | (function (SimpleMvc) { 123 | /** 124 | * Each URL path in your app will have a corresponding controller, which should be a subclass of this class. 125 | * The controller is responsible for rendering the entire page and handling user input as well as UI events. 126 | * A new instance of the controller is created each time the user navigates to the path. 127 | * Data that must live longer than the page should be stored in your subclass of App, or in the cache. 128 | * 129 | * You can have a hierarchy of controllers, with parts that are common to all pages rendered by the base controller. 130 | */ 131 | var Controller = (function () { 132 | function Controller() { 133 | this.loaded = false; 134 | } 135 | /** 136 | * Renders the page, and sets up event handlers. 137 | * Note that since the user can press the refresh button of the browser, the controller is 138 | * responsible for rendering the entire page, not just the portions that have changed. 139 | */ 140 | Controller.prototype.load = function (params) { 141 | $(document.body).empty().off(); 142 | this.loaded = true; 143 | }; 144 | /** 145 | * Check if this controller is still loaded. 146 | * When an ajax call returns, update the UI only if this method returns true. 147 | */ 148 | Controller.prototype.isLoaded = function () { 149 | return this.loaded; 150 | }; 151 | /** 152 | * Whether the controller is unloadable is returned through a callback. 153 | * This gives the controller the opportunity to display a "Do you want to save?" dialog. 154 | */ 155 | Controller.prototype.isUnloadable = function (callback) { 156 | // This method is meant to be overridden. 157 | callback(true); 158 | }; 159 | /** 160 | * Performs any cleanup or finalization that must be performed before the page is torn down. 161 | */ 162 | Controller.prototype.unload = function () { 163 | this.loaded = false; 164 | }; 165 | return Controller; 166 | })(); 167 | SimpleMvc.Controller = Controller; 168 | var PageNotFoundController = (function (_super) { 169 | __extends(PageNotFoundController, _super); 170 | function PageNotFoundController() { 171 | _super.apply(this, arguments); 172 | } 173 | PageNotFoundController.prototype.load = function (params) { 174 | $(document.body).empty().append('
Page not found
'); 175 | }; 176 | return PageNotFoundController; 177 | })(Controller); 178 | SimpleMvc.PageNotFoundController = PageNotFoundController; 179 | })(SimpleMvc || (SimpleMvc = {})); 180 | /// 181 | var SimpleMvc; 182 | (function (SimpleMvc) { 183 | /** 184 | * The router is responsible for unloading and loading controllers when a page navigation 185 | * happens inside your app. You must associate paths with controllers when your application starts. 186 | * Each time the user navigates to a specific path/page a new instance of the controller will be created. 187 | */ 188 | var Router = (function () { 189 | function Router(app) { 190 | this.routes = {}; 191 | this.app = app; 192 | this.attachHashChangeHandler(); 193 | } 194 | /** Associates a path with a controller class. */ 195 | Router.prototype.addRoute = function (path, controllerClass) { 196 | this.routes[path] = controllerClass; 197 | }; 198 | /** 199 | * Sets a custom resolver that examines the path and the query parameters and returns 200 | * a controller class and extra parameters to pass to the controller. 201 | */ 202 | Router.prototype.setCustomResolver = function (resolver) { 203 | this.resolver = resolver; 204 | }; 205 | /** Loads the controller corresponding to the browser's current location. */ 206 | Router.prototype.loadController = function () { 207 | var _this = this; 208 | var result = this.parseHash(); 209 | var controllerClass = this.routes[result.path.trim().toLowerCase()]; 210 | if (!controllerClass && this.resolver) { 211 | var resolverResult = this.resolver(result.path, result.query); 212 | if (resolverResult) { 213 | controllerClass = resolverResult.controllerClass; 214 | $.extend(result.query, resolverResult.extraParams); 215 | } 216 | } 217 | if (!controllerClass) 218 | controllerClass = SimpleMvc.PageNotFoundController; 219 | if (this.currentController) { 220 | this.currentController.isUnloadable(function (unloadable) { 221 | if (unloadable) 222 | _this.loadNewController(controllerClass, result.query); 223 | else 224 | _this.restoreHash(); 225 | }); 226 | } 227 | else { 228 | this.loadNewController(controllerClass, result.query); 229 | } 230 | }; 231 | /** Loads the supplied controller and sets it as the current controller. */ 232 | Router.prototype.loadNewController = function (controllerClass, query) { 233 | if (this.currentController) 234 | this.currentController.unload(); 235 | this.currentHash = window.location.hash; 236 | this.currentController = new controllerClass(this.app); 237 | this.currentController.load(query); 238 | }; 239 | /** Restores hash to its previous value. */ 240 | Router.prototype.restoreHash = function () { 241 | if (window.location.hash != this.currentHash) 242 | this.updateHash(this.currentHash); 243 | }; 244 | /** Removes the hash change handler. */ 245 | Router.prototype.detachHashChangeHandler = function () { 246 | window.onhashchange = null; 247 | }; 248 | /** Attaches the hash change handler. */ 249 | Router.prototype.attachHashChangeHandler = function () { 250 | var _this = this; 251 | window.onhashchange = function () { 252 | _this.loadController(); 253 | }; 254 | }; 255 | /** Updates the hash without reloading the page. */ 256 | Router.prototype.updateHash = function (hash) { 257 | var _this = this; 258 | this.detachHashChangeHandler(); 259 | window.location.hash = hash; 260 | window.setTimeout(function () { 261 | _this.attachHashChangeHandler(); 262 | }, 0); 263 | }; 264 | /** Replaces a query parameter in the browser's location without reloading the page. */ 265 | Router.prototype.replaceQueryParameter = function (parameter, newValue) { 266 | var result = this.parseHash(); 267 | result.query[parameter] = newValue; 268 | var hash = result.path + '?' + this.constructQueryString(result.query); 269 | this.updateHash(hash); 270 | }; 271 | /** Constructs the query part of the hash from the supplied name value pairs. */ 272 | Router.prototype.constructQueryString = function (query) { 273 | var q = []; 274 | for (var key in query) { 275 | var value = query[key]; 276 | q.push(key + '=' + encodeURIComponent(value)); 277 | } 278 | return q.join('&'); 279 | }; 280 | /** Parses the query part of the hash. */ 281 | Router.prototype.parseQuery = function (q) { 282 | var query = {}; 283 | var pairs = q.split('&'); 284 | for (var i = 0; i < pairs.length; i++) { 285 | var v = pairs[i]; 286 | var pair = v.split('='); 287 | if (pair.length > 0) { 288 | var name, value; 289 | name = pair[0]; 290 | if (pair.length > 1) 291 | value = pair[1]; 292 | else 293 | value = ''; 294 | query[name] = decodeURIComponent(value); 295 | } 296 | } 297 | return query; 298 | }; 299 | /** Parses the hash from the browser's current location. */ 300 | Router.prototype.parseHash = function () { 301 | var path = "/"; 302 | var query = {}; 303 | if (window.location.hash && window.location.hash.length > 1) { 304 | var t = window.location.hash.substring(1); 305 | var i = t.indexOf('?'); 306 | if (i == -1) { 307 | path = t; 308 | } 309 | else { 310 | path = t.substring(0, i); 311 | var q = t.substring(i + 1); 312 | query = this.parseQuery(q); 313 | } 314 | } 315 | return { path: path, query: query }; 316 | }; 317 | /** Navigates the app to the new path. */ 318 | Router.prototype.navigate = function (path, query) { 319 | var q = query ? ('?' + this.constructQueryString(query)) : ''; 320 | window.location.hash = '#' + path + q; 321 | }; 322 | /** This enables your App subclass to talk to the currently active controller. */ 323 | Router.prototype.getCurrentController = function () { 324 | return this.currentController; 325 | }; 326 | return Router; 327 | })(); 328 | SimpleMvc.Router = Router; 329 | })(SimpleMvc || (SimpleMvc = {})); 330 | /// 331 | /// 332 | /// 333 | /// 334 | /// 335 | var SimpleMvc; 336 | (function (SimpleMvc) { 337 | /** 338 | * This is the root object of the application. 339 | * Do not use this class directly, make your own subclass instead. 340 | */ 341 | var App = (function () { 342 | function App() { 343 | this.router = new SimpleMvc.Router(this); 344 | this.cache = new SimpleMvc.Cache(); 345 | if (App.instance) 346 | throw new SimpleMvc.Exception("App instance exists; use getInstance() instead."); 347 | App.instance = this; 348 | App.setupCacheCleaner(); 349 | } 350 | /** This method must be called after your App subclass is fully initialized. */ 351 | App.prototype.load = function () { 352 | this.router.loadController(); 353 | }; 354 | App.prototype.getCache = function () { 355 | return this.cache; 356 | }; 357 | App.prototype.getRouter = function () { 358 | return this.router; 359 | }; 360 | App.getInstance = function () { 361 | return this.instance; 362 | }; 363 | /** 364 | * Navigates the app to the page corresponding to the supplied URL. 365 | * @path path to the page for example "/bank/deposits" will set the location to http://localhost/#/bank/deposits 366 | */ 367 | App.prototype.navigate = function (path, queryParams) { 368 | if (!path || path[0] != '/' || path.indexOf('#') != -1) 369 | throw new SimpleMvc.Exception("Invalid path"); 370 | this.router.navigate(path, queryParams); 371 | }; 372 | App.setupCacheCleaner = function () { 373 | if (App.cacheCleaningIntervalMinutes > 0) { 374 | window.setInterval(function () { 375 | App.getInstance().getCache().clean(); 376 | }, App.cacheCleaningIntervalMinutes * 60 * 1000); 377 | } 378 | }; 379 | App.cacheCleaningIntervalMinutes = 5; 380 | return App; 381 | })(); 382 | SimpleMvc.App = App; 383 | })(SimpleMvc || (SimpleMvc = {})); 384 | -------------------------------------------------------------------------------- /SampleProject/Models/Account.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | module BankDemo.Model { 4 | export enum AccountType { Checking, Savings }; 5 | 6 | /** Represents a bank account */ 7 | export class Account { 8 | private name: string; 9 | private balance: number; 10 | 11 | constructor(private accountType: AccountType, balance: number) { 12 | this.name = (accountType == AccountType.Checking) ? "Fee Maximizer Checking" : "Interest Free Savings"; 13 | this.balance = balance; 14 | } 15 | 16 | public getAccountType(): AccountType { 17 | return this.accountType; 18 | } 19 | 20 | public getName(): string { 21 | return this.name; 22 | } 23 | 24 | public getBalance(): number { 25 | return this.balance; 26 | } 27 | 28 | public depositMoney(amount: number): void { 29 | if (!amount) 30 | throw new SimpleMvc.Exception("Invalid amount"); 31 | this.balance = this.balance + amount; 32 | } 33 | 34 | public withdrawMoney(amount: number): void { 35 | if (!amount) 36 | throw new SimpleMvc.Exception("Invalid amount"); 37 | if (amount > this.balance) 38 | throw new SimpleMvc.Exception("Amount exceeds balance"); 39 | this.balance = this.balance - amount; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /SampleProject/Models/Bank.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | module BankDemo.Model { 4 | export class Bank { 5 | private checkingAccount = new Account(AccountType.Checking, 1000); 6 | private savingsAccount = new Account(AccountType.Savings, 5000); 7 | 8 | public getAccounts(): Account[] { 9 | var accounts = []; 10 | accounts.push(this.checkingAccount); 11 | accounts.push(this.savingsAccount); 12 | return accounts; 13 | } 14 | 15 | public getAccount(accountType: number): Account { 16 | if (accountType == AccountType.Checking) 17 | return this.checkingAccount; 18 | else 19 | return this.savingsAccount; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /SampleProject/NuGet.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | packages 4 | 5 | -------------------------------------------------------------------------------- /SampleProject/README.md: -------------------------------------------------------------------------------- 1 | # Sample Project 2 | 3 | ## How to build 4 | Install [Node](https://nodejs.org/) if you haven't already. 5 | 6 | If you have not installed Grunt then open Node.js command prompt and run: 7 | 8 | npm install -g grunt-cli 9 | 10 | Then at the Node.js command prompt type: 11 | 12 | cd SampleProject 13 | npm install 14 | 15 | This will install several Grunt plugins. Now you are ready to run Grunt. At the Node.js command prompt type: 16 | 17 | grunt 18 | 19 | Now start Visual Studio, open the .sln file, build and run. 20 | 21 | If you add or modify a .jsx file run Grunt to regenerate TypeScript interfaces. 22 | -------------------------------------------------------------------------------- /SampleProject/Views/.gitignore: -------------------------------------------------------------------------------- 1 | *.js 2 | -------------------------------------------------------------------------------- /SampleProject/Views/AccountPage.jsx: -------------------------------------------------------------------------------- 1 | var AccountPage = React.createClass({ 2 | render: function () { 3 | return ( 4 |
5 |

{this.props.account.getName()}

6 |
Your balance is {this.props.account.getBalance()}
7 |
8 |
9 |   10 | 11 |
12 |
13 | ); 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /SampleProject/Views/ChangePassword.jsx: -------------------------------------------------------------------------------- 1 | var ChangePasswordPanel = React.createClass({ 2 | render: function () { 3 | return ( 4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |   12 | 13 |
14 |
15 | ); 16 | } 17 | }); 18 | -------------------------------------------------------------------------------- /SampleProject/Views/DropdownMenu.jsx: -------------------------------------------------------------------------------- 1 | var DropdownMenuPanel = React.createClass({ 2 | render: function () { 3 | var items = []; 4 | for (var i = 0; i < this.props.items.length; i++) { 5 | items.push(
{this.props.items[i]}
); 6 | } 7 | return ( 8 |
{items}
9 | ); 10 | } 11 | }); 12 | -------------------------------------------------------------------------------- /SampleProject/Views/HomePage.jsx: -------------------------------------------------------------------------------- 1 | var HomePage = React.createClass({ 2 | render: function () { 3 | var rows = []; 4 | for (var i = 0; i < this.props.accounts.length; i++) { 5 | rows.push( 6 | 7 | {this.props.accounts[i].getName()} 8 | {this.props.accounts[i].getBalance()} 9 | 10 | ); 11 | } 12 | return ( 13 |
14 |

Accounts

15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | {rows} 23 |
Account NameBalance
24 |
25 | ); 26 | } 27 | }); 28 | -------------------------------------------------------------------------------- /SampleProject/Views/MasterPage.jsx: -------------------------------------------------------------------------------- 1 | var MasterPage = React.createClass({ 2 | render: function () { 3 | return ( 4 |
5 |
6 |
Mega Bank
7 |
8 |
Go to
9 |
Sign out
10 |
11 |
12 |
13 | 14 | 15 |
16 |
17 |
18 |
19 | ); 20 | } 21 | }); 22 | -------------------------------------------------------------------------------- /SampleProject/Views/MessageBox.jsx: -------------------------------------------------------------------------------- 1 | var MessageBoxPanel = React.createClass({ 2 | render: function () { 3 | var cancelButton = this.props.hideCancelButton ? 4 | : 5 | return ( 6 |
7 |
{this.props.message}
8 |
9 |   10 | {cancelButton} 11 |
12 |
13 | ); 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /SampleProject/Views/SupportPage.jsx: -------------------------------------------------------------------------------- 1 | var SupportPage = React.createClass({ 2 | render: function () { 3 | return ( 4 |
5 |

Support

6 |
Call 1-800-MEGA-BANK for support.
7 |
8 | ); 9 | } 10 | }); 11 | -------------------------------------------------------------------------------- /SampleProject/Views/TransactionPage.jsx: -------------------------------------------------------------------------------- 1 | var TransactionPage = React.createClass({ 2 | getInitialState: function () { 3 | return { balance: 0 }; 4 | }, 5 | render: function () { 6 | return ( 7 |
8 |

{this.props.accountName} - {this.props.labels.transactionType}

9 |
Your balance is {this.state.balance}
10 |
11 |
12 |
{this.props.labels.prompt}
13 |
$
14 |
15 |
16 |
17 |   18 | 19 |
20 |
21 | ); 22 | } 23 | }); 24 | -------------------------------------------------------------------------------- /SampleProject/index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Mega Bank 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /SampleProject/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bankdemo", 3 | "version": "0.1.0", 4 | "description": "JSXtyper sample project", 5 | "main": "index.html", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Rajeev-K", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "grunt": "^0.4.5", 13 | "grunt-tsd": "^0.2.0-beta.1", 14 | "grunt-contrib-concat": "^0.5.1", 15 | "grunt-contrib-less": "^1.0.0", 16 | "grunt-jsxtyper": "^0.1.0", 17 | "grunt-react": "^0.12.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /SampleProject/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /SampleProject/tsd.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "v4", 3 | "repo": "borisyankov/DefinitelyTyped", 4 | "ref": "master", 5 | "path": "typings", 6 | "bundle": "typings/tsd.d.ts", 7 | "installed": { 8 | "jquery/jquery.d.ts": { 9 | "commit": "8b7cc13f6dbabd0a49de7ccba75c342160e600ad" 10 | }, 11 | "react/react-global.d.ts": { 12 | "commit": "1cc5ea01d8986ba5793072de8c63822947298ce2" 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /SampleProject/web.Debug.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | -------------------------------------------------------------------------------- /SampleProject/web.Release.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 30 | 31 | -------------------------------------------------------------------------------- /SampleProject/web.config: -------------------------------------------------------------------------------- 1 |  2 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Scripts/typings/estree/jsxtree.d.ts: -------------------------------------------------------------------------------- 1 | // To get estree.d.ts open Package Manager Console and type: 2 | // Install-Package estree.TypeScript.DefinitelyTyped 3 | /// 4 | 5 | declare module ESTree { 6 | interface JSXIdentifier extends ESTree.Node { 7 | name: string; 8 | } 9 | 10 | interface JSXAttribute extends ESTree.Node { 11 | name: JSXIdentifier; 12 | value?: ESTree.Literal | ESTree.JSXExpressionContainer; 13 | } 14 | 15 | interface JSXOpeningElement extends ESTree.Node { 16 | name: JSXIdentifier; 17 | selfClosing: boolean; 18 | attributes: JSXAttribute[]; 19 | } 20 | 21 | interface JSXClosingElement extends ESTree.Node { 22 | name: JSXIdentifier; 23 | } 24 | 25 | interface JSXExpressionContainer extends ESTree.Node { 26 | expression: ESTree.Expression; 27 | } 28 | 29 | interface JSXElement extends ESTree.Node { 30 | openingElement: JSXOpeningElement; 31 | closingElement: JSXClosingElement; 32 | children: (JSXExpressionContainer | ESTree.Expression)[]; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Scripts/typings/node/node.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for Node.js v0.10.1 2 | // Project: http://nodejs.org/ 3 | // Definitions by: Microsoft TypeScript , DefinitelyTyped 4 | // Definitions: https://github.com/borisyankov/DefinitelyTyped 5 | 6 | /************************************************ 7 | * * 8 | * Node.js v0.10.1 API * 9 | * * 10 | ************************************************/ 11 | 12 | /************************************************ 13 | * * 14 | * GLOBAL * 15 | * * 16 | ************************************************/ 17 | declare var process: NodeJS.Process; 18 | declare var global: any; 19 | 20 | declare var __filename: string; 21 | declare var __dirname: string; 22 | 23 | declare function setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): NodeJS.Timer; 24 | declare function clearTimeout(timeoutId: NodeJS.Timer): void; 25 | declare function setInterval(callback: (...args: any[]) => void, ms: number, ...args: any[]): NodeJS.Timer; 26 | declare function clearInterval(intervalId: NodeJS.Timer): void; 27 | declare function setImmediate(callback: (...args: any[]) => void, ...args: any[]): any; 28 | declare function clearImmediate(immediateId: any): void; 29 | 30 | declare var require: { 31 | (id: string): any; 32 | resolve(id: string): string; 33 | cache: any; 34 | extensions: any; 35 | main: any; 36 | }; 37 | 38 | declare var module: { 39 | exports: any; 40 | require(id: string): any; 41 | id: string; 42 | filename: string; 43 | loaded: boolean; 44 | parent: any; 45 | children: any[]; 46 | }; 47 | 48 | // Same as module.exports 49 | declare var exports: any; 50 | declare var SlowBuffer: { 51 | new (str: string, encoding?: string): Buffer; 52 | new (size: number): Buffer; 53 | new (size: Uint8Array): Buffer; 54 | new (array: any[]): Buffer; 55 | prototype: Buffer; 56 | isBuffer(obj: any): boolean; 57 | byteLength(string: string, encoding?: string): number; 58 | concat(list: Buffer[], totalLength?: number): Buffer; 59 | }; 60 | 61 | 62 | // Buffer class 63 | interface Buffer extends NodeBuffer { } 64 | declare var Buffer: { 65 | new (str: string, encoding?: string): Buffer; 66 | new (size: number): Buffer; 67 | new (size: Uint8Array): Buffer; 68 | new (array: any[]): Buffer; 69 | prototype: Buffer; 70 | isBuffer(obj: any): boolean; 71 | byteLength(string: string, encoding?: string): number; 72 | concat(list: Buffer[], totalLength?: number): Buffer; 73 | }; 74 | 75 | /************************************************ 76 | * * 77 | * GLOBAL INTERFACES * 78 | * * 79 | ************************************************/ 80 | declare module NodeJS { 81 | export interface ErrnoException extends Error { 82 | errno?: any; 83 | code?: string; 84 | path?: string; 85 | syscall?: string; 86 | } 87 | 88 | export interface EventEmitter { 89 | addListener(event: string, listener: Function): EventEmitter; 90 | on(event: string, listener: Function): EventEmitter; 91 | once(event: string, listener: Function): EventEmitter; 92 | removeListener(event: string, listener: Function): EventEmitter; 93 | removeAllListeners(event?: string): EventEmitter; 94 | setMaxListeners(n: number): void; 95 | listeners(event: string): Function[]; 96 | emit(event: string, ...args: any[]): boolean; 97 | } 98 | 99 | export interface ReadableStream extends EventEmitter { 100 | readable: boolean; 101 | read(size?: number): any; 102 | setEncoding(encoding: string): void; 103 | pause(): void; 104 | resume(): void; 105 | pipe(destination: T, options?: { end?: boolean; }): T; 106 | unpipe(destination?: T): void; 107 | unshift(chunk: string): void; 108 | unshift(chunk: Buffer): void; 109 | wrap(oldStream: ReadableStream): ReadableStream; 110 | } 111 | 112 | export interface WritableStream extends EventEmitter { 113 | writable: boolean; 114 | write(buffer: Buffer, cb?: Function): boolean; 115 | write(str: string, cb?: Function): boolean; 116 | write(str: string, encoding?: string, cb?: Function): boolean; 117 | end(): void; 118 | end(buffer: Buffer, cb?: Function): void; 119 | end(str: string, cb?: Function): void; 120 | end(str: string, encoding?: string, cb?: Function): void; 121 | } 122 | 123 | export interface ReadWriteStream extends ReadableStream, WritableStream { } 124 | 125 | export interface Process extends EventEmitter { 126 | stdout: WritableStream; 127 | stderr: WritableStream; 128 | stdin: ReadableStream; 129 | argv: string[]; 130 | execPath: string; 131 | abort(): void; 132 | chdir(directory: string): void; 133 | cwd(): string; 134 | env: any; 135 | exit(code?: number): void; 136 | getgid(): number; 137 | setgid(id: number): void; 138 | setgid(id: string): void; 139 | getuid(): number; 140 | setuid(id: number): void; 141 | setuid(id: string): void; 142 | version: string; 143 | versions: { 144 | http_parser: string; 145 | node: string; 146 | v8: string; 147 | ares: string; 148 | uv: string; 149 | zlib: string; 150 | openssl: string; 151 | }; 152 | config: { 153 | target_defaults: { 154 | cflags: any[]; 155 | default_configuration: string; 156 | defines: string[]; 157 | include_dirs: string[]; 158 | libraries: string[]; 159 | }; 160 | variables: { 161 | clang: number; 162 | host_arch: string; 163 | node_install_npm: boolean; 164 | node_install_waf: boolean; 165 | node_prefix: string; 166 | node_shared_openssl: boolean; 167 | node_shared_v8: boolean; 168 | node_shared_zlib: boolean; 169 | node_use_dtrace: boolean; 170 | node_use_etw: boolean; 171 | node_use_openssl: boolean; 172 | target_arch: string; 173 | v8_no_strict_aliasing: number; 174 | v8_use_snapshot: boolean; 175 | visibility: string; 176 | }; 177 | }; 178 | kill(pid: number, signal?: string): void; 179 | pid: number; 180 | title: string; 181 | arch: string; 182 | platform: string; 183 | memoryUsage(): { rss: number; heapTotal: number; heapUsed: number; }; 184 | nextTick(callback: Function): void; 185 | umask(mask?: number): number; 186 | uptime(): number; 187 | hrtime(time?: number[]): number[]; 188 | 189 | // Worker 190 | send? (message: any, sendHandle?: any): void; 191 | } 192 | 193 | export interface Timer { 194 | ref(): void; 195 | unref(): void; 196 | } 197 | } 198 | 199 | /** 200 | * @deprecated 201 | */ 202 | interface NodeBuffer { 203 | [index: number]: number; 204 | write(string: string, offset?: number, length?: number, encoding?: string): number; 205 | toString(encoding?: string, start?: number, end?: number): string; 206 | toJSON(): any; 207 | length: number; 208 | copy(targetBuffer: Buffer, targetStart?: number, sourceStart?: number, sourceEnd?: number): number; 209 | slice(start?: number, end?: number): Buffer; 210 | readUInt8(offset: number, noAsset?: boolean): number; 211 | readUInt16LE(offset: number, noAssert?: boolean): number; 212 | readUInt16BE(offset: number, noAssert?: boolean): number; 213 | readUInt32LE(offset: number, noAssert?: boolean): number; 214 | readUInt32BE(offset: number, noAssert?: boolean): number; 215 | readInt8(offset: number, noAssert?: boolean): number; 216 | readInt16LE(offset: number, noAssert?: boolean): number; 217 | readInt16BE(offset: number, noAssert?: boolean): number; 218 | readInt32LE(offset: number, noAssert?: boolean): number; 219 | readInt32BE(offset: number, noAssert?: boolean): number; 220 | readFloatLE(offset: number, noAssert?: boolean): number; 221 | readFloatBE(offset: number, noAssert?: boolean): number; 222 | readDoubleLE(offset: number, noAssert?: boolean): number; 223 | readDoubleBE(offset: number, noAssert?: boolean): number; 224 | writeUInt8(value: number, offset: number, noAssert?: boolean): void; 225 | writeUInt16LE(value: number, offset: number, noAssert?: boolean): void; 226 | writeUInt16BE(value: number, offset: number, noAssert?: boolean): void; 227 | writeUInt32LE(value: number, offset: number, noAssert?: boolean): void; 228 | writeUInt32BE(value: number, offset: number, noAssert?: boolean): void; 229 | writeInt8(value: number, offset: number, noAssert?: boolean): void; 230 | writeInt16LE(value: number, offset: number, noAssert?: boolean): void; 231 | writeInt16BE(value: number, offset: number, noAssert?: boolean): void; 232 | writeInt32LE(value: number, offset: number, noAssert?: boolean): void; 233 | writeInt32BE(value: number, offset: number, noAssert?: boolean): void; 234 | writeFloatLE(value: number, offset: number, noAssert?: boolean): void; 235 | writeFloatBE(value: number, offset: number, noAssert?: boolean): void; 236 | writeDoubleLE(value: number, offset: number, noAssert?: boolean): void; 237 | writeDoubleBE(value: number, offset: number, noAssert?: boolean): void; 238 | fill(value: any, offset?: number, end?: number): void; 239 | } 240 | 241 | /************************************************ 242 | * * 243 | * MODULES * 244 | * * 245 | ************************************************/ 246 | declare module "buffer" { 247 | export var INSPECT_MAX_BYTES: number; 248 | } 249 | 250 | declare module "querystring" { 251 | export function stringify(obj: any, sep?: string, eq?: string): string; 252 | export function parse(str: string, sep?: string, eq?: string, options?: { maxKeys?: number; }): any; 253 | export function escape(): any; 254 | export function unescape(): any; 255 | } 256 | 257 | declare module "events" { 258 | export class EventEmitter implements NodeJS.EventEmitter { 259 | static listenerCount(emitter: EventEmitter, event: string): number; 260 | 261 | addListener(event: string, listener: Function): EventEmitter; 262 | on(event: string, listener: Function): EventEmitter; 263 | once(event: string, listener: Function): EventEmitter; 264 | removeListener(event: string, listener: Function): EventEmitter; 265 | removeAllListeners(event?: string): EventEmitter; 266 | setMaxListeners(n: number): void; 267 | listeners(event: string): Function[]; 268 | emit(event: string, ...args: any[]): boolean; 269 | } 270 | } 271 | 272 | declare module "http" { 273 | import events = require("events"); 274 | import net = require("net"); 275 | import stream = require("stream"); 276 | 277 | export interface Server extends events.EventEmitter { 278 | listen(port: number, hostname?: string, backlog?: number, callback?: Function): Server; 279 | listen(path: string, callback?: Function): Server; 280 | listen(handle: any, listeningListener?: Function): Server; 281 | close(cb?: any): Server; 282 | address(): { port: number; family: string; address: string; }; 283 | maxHeadersCount: number; 284 | } 285 | export interface ServerRequest extends events.EventEmitter, stream.Readable { 286 | method: string; 287 | url: string; 288 | headers: any; 289 | trailers: string; 290 | httpVersion: string; 291 | setEncoding(encoding?: string): void; 292 | pause(): void; 293 | resume(): void; 294 | connection: net.Socket; 295 | } 296 | export interface ServerResponse extends events.EventEmitter, stream.Writable { 297 | // Extended base methods 298 | write(buffer: Buffer): boolean; 299 | write(buffer: Buffer, cb?: Function): boolean; 300 | write(str: string, cb?: Function): boolean; 301 | write(str: string, encoding?: string, cb?: Function): boolean; 302 | write(str: string, encoding?: string, fd?: string): boolean; 303 | 304 | writeContinue(): void; 305 | writeHead(statusCode: number, reasonPhrase?: string, headers?: any): void; 306 | writeHead(statusCode: number, headers?: any): void; 307 | statusCode: number; 308 | setHeader(name: string, value: string): void; 309 | sendDate: boolean; 310 | getHeader(name: string): string; 311 | removeHeader(name: string): void; 312 | write(chunk: any, encoding?: string): any; 313 | addTrailers(headers: any): void; 314 | 315 | // Extended base methods 316 | end(): void; 317 | end(buffer: Buffer, cb?: Function): void; 318 | end(str: string, cb?: Function): void; 319 | end(str: string, encoding?: string, cb?: Function): void; 320 | end(data?: any, encoding?: string): void; 321 | } 322 | export interface ClientRequest extends events.EventEmitter, stream.Writable { 323 | // Extended base methods 324 | write(buffer: Buffer): boolean; 325 | write(buffer: Buffer, cb?: Function): boolean; 326 | write(str: string, cb?: Function): boolean; 327 | write(str: string, encoding?: string, cb?: Function): boolean; 328 | write(str: string, encoding?: string, fd?: string): boolean; 329 | 330 | write(chunk: any, encoding?: string): void; 331 | abort(): void; 332 | setTimeout(timeout: number, callback?: Function): void; 333 | setNoDelay(noDelay?: boolean): void; 334 | setSocketKeepAlive(enable?: boolean, initialDelay?: number): void; 335 | 336 | // Extended base methods 337 | end(): void; 338 | end(buffer: Buffer, cb?: Function): void; 339 | end(str: string, cb?: Function): void; 340 | end(str: string, encoding?: string, cb?: Function): void; 341 | end(data?: any, encoding?: string): void; 342 | } 343 | export interface ClientResponse extends events.EventEmitter, stream.Readable { 344 | statusCode: number; 345 | httpVersion: string; 346 | headers: any; 347 | trailers: any; 348 | setEncoding(encoding?: string): void; 349 | pause(): void; 350 | resume(): void; 351 | } 352 | export interface Agent { maxSockets: number; sockets: any; requests: any; } 353 | 354 | export var STATUS_CODES: { 355 | [errorCode: number]: string; 356 | [errorCode: string]: string; 357 | }; 358 | export function createServer(requestListener?: (request: ServerRequest, response: ServerResponse) => void): Server; 359 | export function createClient(port?: number, host?: string): any; 360 | export function request(options: any, callback?: Function): ClientRequest; 361 | export function get(options: any, callback?: Function): ClientRequest; 362 | export var globalAgent: Agent; 363 | } 364 | 365 | declare module "cluster" { 366 | import child = require("child_process"); 367 | import events = require("events"); 368 | 369 | export interface ClusterSettings { 370 | exec?: string; 371 | args?: string[]; 372 | silent?: boolean; 373 | } 374 | 375 | export class Worker extends events.EventEmitter { 376 | id: string; 377 | process: child.ChildProcess; 378 | suicide: boolean; 379 | send(message: any, sendHandle?: any): void; 380 | kill(signal?: string): void; 381 | destroy(signal?: string): void; 382 | disconnect(): void; 383 | } 384 | 385 | export var settings: ClusterSettings; 386 | export var isMaster: boolean; 387 | export var isWorker: boolean; 388 | export function setupMaster(settings?: ClusterSettings): void; 389 | export function fork(env?: any): Worker; 390 | export function disconnect(callback?: Function): void; 391 | export var worker: Worker; 392 | export var workers: Worker[]; 393 | 394 | // Event emitter 395 | export function addListener(event: string, listener: Function): void; 396 | export function on(event: string, listener: Function): any; 397 | export function once(event: string, listener: Function): void; 398 | export function removeListener(event: string, listener: Function): void; 399 | export function removeAllListeners(event?: string): void; 400 | export function setMaxListeners(n: number): void; 401 | export function listeners(event: string): Function[]; 402 | export function emit(event: string, ...args: any[]): boolean; 403 | } 404 | 405 | declare module "zlib" { 406 | import stream = require("stream"); 407 | export interface ZlibOptions { chunkSize?: number; windowBits?: number; level?: number; memLevel?: number; strategy?: number; dictionary?: any; } 408 | 409 | export interface Gzip extends stream.Transform { } 410 | export interface Gunzip extends stream.Transform { } 411 | export interface Deflate extends stream.Transform { } 412 | export interface Inflate extends stream.Transform { } 413 | export interface DeflateRaw extends stream.Transform { } 414 | export interface InflateRaw extends stream.Transform { } 415 | export interface Unzip extends stream.Transform { } 416 | 417 | export function createGzip(options?: ZlibOptions): Gzip; 418 | export function createGunzip(options?: ZlibOptions): Gunzip; 419 | export function createDeflate(options?: ZlibOptions): Deflate; 420 | export function createInflate(options?: ZlibOptions): Inflate; 421 | export function createDeflateRaw(options?: ZlibOptions): DeflateRaw; 422 | export function createInflateRaw(options?: ZlibOptions): InflateRaw; 423 | export function createUnzip(options?: ZlibOptions): Unzip; 424 | 425 | export function deflate(buf: Buffer, callback: (error: Error, result: any) => void): void; 426 | export function deflateRaw(buf: Buffer, callback: (error: Error, result: any) => void): void; 427 | export function gzip(buf: Buffer, callback: (error: Error, result: any) => void): void; 428 | export function gunzip(buf: Buffer, callback: (error: Error, result: any) => void): void; 429 | export function inflate(buf: Buffer, callback: (error: Error, result: any) => void): void; 430 | export function inflateRaw(buf: Buffer, callback: (error: Error, result: any) => void): void; 431 | export function unzip(buf: Buffer, callback: (error: Error, result: any) => void): void; 432 | 433 | // Constants 434 | export var Z_NO_FLUSH: number; 435 | export var Z_PARTIAL_FLUSH: number; 436 | export var Z_SYNC_FLUSH: number; 437 | export var Z_FULL_FLUSH: number; 438 | export var Z_FINISH: number; 439 | export var Z_BLOCK: number; 440 | export var Z_TREES: number; 441 | export var Z_OK: number; 442 | export var Z_STREAM_END: number; 443 | export var Z_NEED_DICT: number; 444 | export var Z_ERRNO: number; 445 | export var Z_STREAM_ERROR: number; 446 | export var Z_DATA_ERROR: number; 447 | export var Z_MEM_ERROR: number; 448 | export var Z_BUF_ERROR: number; 449 | export var Z_VERSION_ERROR: number; 450 | export var Z_NO_COMPRESSION: number; 451 | export var Z_BEST_SPEED: number; 452 | export var Z_BEST_COMPRESSION: number; 453 | export var Z_DEFAULT_COMPRESSION: number; 454 | export var Z_FILTERED: number; 455 | export var Z_HUFFMAN_ONLY: number; 456 | export var Z_RLE: number; 457 | export var Z_FIXED: number; 458 | export var Z_DEFAULT_STRATEGY: number; 459 | export var Z_BINARY: number; 460 | export var Z_TEXT: number; 461 | export var Z_ASCII: number; 462 | export var Z_UNKNOWN: number; 463 | export var Z_DEFLATED: number; 464 | export var Z_NULL: number; 465 | } 466 | 467 | declare module "os" { 468 | export function tmpdir(): string; 469 | export function hostname(): string; 470 | export function type(): string; 471 | export function platform(): string; 472 | export function arch(): string; 473 | export function release(): string; 474 | export function uptime(): number; 475 | export function loadavg(): number[]; 476 | export function totalmem(): number; 477 | export function freemem(): number; 478 | export function cpus(): { model: string; speed: number; times: { user: number; nice: number; sys: number; idle: number; irq: number; }; }[]; 479 | export function networkInterfaces(): any; 480 | export var EOL: string; 481 | } 482 | 483 | declare module "https" { 484 | import tls = require("tls"); 485 | import events = require("events"); 486 | import http = require("http"); 487 | 488 | export interface ServerOptions { 489 | pfx?: any; 490 | key?: any; 491 | passphrase?: string; 492 | cert?: any; 493 | ca?: any; 494 | crl?: any; 495 | ciphers?: string; 496 | honorCipherOrder?: boolean; 497 | requestCert?: boolean; 498 | rejectUnauthorized?: boolean; 499 | NPNProtocols?: any; 500 | SNICallback?: (servername: string) => any; 501 | } 502 | 503 | export interface RequestOptions { 504 | host?: string; 505 | hostname?: string; 506 | port?: number; 507 | path?: string; 508 | method?: string; 509 | headers?: any; 510 | auth?: string; 511 | agent?: any; 512 | pfx?: any; 513 | key?: any; 514 | passphrase?: string; 515 | cert?: any; 516 | ca?: any; 517 | ciphers?: string; 518 | rejectUnauthorized?: boolean; 519 | } 520 | 521 | export interface Agent { 522 | maxSockets: number; 523 | sockets: any; 524 | requests: any; 525 | } 526 | export var Agent: { 527 | new (options?: RequestOptions): Agent; 528 | }; 529 | export interface Server extends tls.Server { } 530 | export function createServer(options: ServerOptions, requestListener?: Function): Server; 531 | export function request(options: RequestOptions, callback?: (res: http.ClientResponse) => void): http.ClientRequest; 532 | export function get(options: RequestOptions, callback?: (res: http.ClientResponse) => void): http.ClientRequest; 533 | export var globalAgent: Agent; 534 | } 535 | 536 | declare module "punycode" { 537 | export function decode(string: string): string; 538 | export function encode(string: string): string; 539 | export function toUnicode(domain: string): string; 540 | export function toASCII(domain: string): string; 541 | export var ucs2: ucs2; 542 | interface ucs2 { 543 | decode(string: string): string; 544 | encode(codePoints: number[]): string; 545 | } 546 | export var version: any; 547 | } 548 | 549 | declare module "repl" { 550 | import stream = require("stream"); 551 | import events = require("events"); 552 | 553 | export interface ReplOptions { 554 | prompt?: string; 555 | input?: NodeJS.ReadableStream; 556 | output?: NodeJS.WritableStream; 557 | terminal?: boolean; 558 | eval?: Function; 559 | useColors?: boolean; 560 | useGlobal?: boolean; 561 | ignoreUndefined?: boolean; 562 | writer?: Function; 563 | } 564 | export function start(options: ReplOptions): events.EventEmitter; 565 | } 566 | 567 | declare module "readline" { 568 | import events = require("events"); 569 | import stream = require("stream"); 570 | 571 | export interface ReadLine extends events.EventEmitter { 572 | setPrompt(prompt: string, length: number): void; 573 | prompt(preserveCursor?: boolean): void; 574 | question(query: string, callback: Function): void; 575 | pause(): void; 576 | resume(): void; 577 | close(): void; 578 | write(data: any, key?: any): void; 579 | } 580 | export interface ReadLineOptions { 581 | input: NodeJS.ReadableStream; 582 | output: NodeJS.WritableStream; 583 | completer?: Function; 584 | terminal?: boolean; 585 | } 586 | export function createInterface(options: ReadLineOptions): ReadLine; 587 | } 588 | 589 | declare module "vm" { 590 | export interface Context { } 591 | export interface Script { 592 | runInThisContext(): void; 593 | runInNewContext(sandbox?: Context): void; 594 | } 595 | export function runInThisContext(code: string, filename?: string): void; 596 | export function runInNewContext(code: string, sandbox?: Context, filename?: string): void; 597 | export function runInContext(code: string, context: Context, filename?: string): void; 598 | export function createContext(initSandbox?: Context): Context; 599 | export function createScript(code: string, filename?: string): Script; 600 | } 601 | 602 | declare module "child_process" { 603 | import events = require("events"); 604 | import stream = require("stream"); 605 | 606 | export interface ChildProcess extends events.EventEmitter { 607 | stdin: stream.Writable; 608 | stdout: stream.Readable; 609 | stderr: stream.Readable; 610 | pid: number; 611 | kill(signal?: string): void; 612 | send(message: any, sendHandle: any): void; 613 | disconnect(): void; 614 | } 615 | 616 | export function spawn(command: string, args?: string[], options?: { 617 | cwd?: string; 618 | stdio?: any; 619 | custom?: any; 620 | env?: any; 621 | detached?: boolean; 622 | }): ChildProcess; 623 | export function exec(command: string, options: { 624 | cwd?: string; 625 | stdio?: any; 626 | customFds?: any; 627 | env?: any; 628 | encoding?: string; 629 | timeout?: number; 630 | maxBuffer?: number; 631 | killSignal?: string; 632 | }, callback: (error: Error, stdout: Buffer, stderr: Buffer) => void): ChildProcess; 633 | export function exec(command: string, callback: (error: Error, stdout: Buffer, stderr: Buffer) => void): ChildProcess; 634 | export function execFile(file: string, 635 | callback?: (error: Error, stdout: Buffer, stderr: Buffer) => void): ChildProcess; 636 | export function execFile(file: string, args?: string[], 637 | callback?: (error: Error, stdout: Buffer, stderr: Buffer) => void): ChildProcess; 638 | export function execFile(file: string, args?: string[], options?: { 639 | cwd?: string; 640 | stdio?: any; 641 | customFds?: any; 642 | env?: any; 643 | encoding?: string; 644 | timeout?: number; 645 | maxBuffer?: string; 646 | killSignal?: string; 647 | }, callback?: (error: Error, stdout: Buffer, stderr: Buffer) => void): ChildProcess; 648 | export function fork(modulePath: string, args?: string[], options?: { 649 | cwd?: string; 650 | env?: any; 651 | encoding?: string; 652 | }): ChildProcess; 653 | } 654 | 655 | declare module "url" { 656 | export interface Url { 657 | href: string; 658 | protocol: string; 659 | auth: string; 660 | hostname: string; 661 | port: string; 662 | host: string; 663 | pathname: string; 664 | search: string; 665 | query: any; // string | Object 666 | slashes: boolean; 667 | hash?: string; 668 | path?: string; 669 | } 670 | 671 | export interface UrlOptions { 672 | protocol?: string; 673 | auth?: string; 674 | hostname?: string; 675 | port?: string; 676 | host?: string; 677 | pathname?: string; 678 | search?: string; 679 | query?: any; 680 | hash?: string; 681 | path?: string; 682 | } 683 | 684 | export function parse(urlStr: string, parseQueryString?: boolean, slashesDenoteHost?: boolean): Url; 685 | export function format(url: UrlOptions): string; 686 | export function resolve(from: string, to: string): string; 687 | } 688 | 689 | declare module "dns" { 690 | export function lookup(domain: string, family: number, callback: (err: Error, address: string, family: number) => void): string; 691 | export function lookup(domain: string, callback: (err: Error, address: string, family: number) => void): string; 692 | export function resolve(domain: string, rrtype: string, callback: (err: Error, addresses: string[]) => void): string[]; 693 | export function resolve(domain: string, callback: (err: Error, addresses: string[]) => void): string[]; 694 | export function resolve4(domain: string, callback: (err: Error, addresses: string[]) => void): string[]; 695 | export function resolve6(domain: string, callback: (err: Error, addresses: string[]) => void): string[]; 696 | export function resolveMx(domain: string, callback: (err: Error, addresses: string[]) => void): string[]; 697 | export function resolveTxt(domain: string, callback: (err: Error, addresses: string[]) => void): string[]; 698 | export function resolveSrv(domain: string, callback: (err: Error, addresses: string[]) => void): string[]; 699 | export function resolveNs(domain: string, callback: (err: Error, addresses: string[]) => void): string[]; 700 | export function resolveCname(domain: string, callback: (err: Error, addresses: string[]) => void): string[]; 701 | export function reverse(ip: string, callback: (err: Error, domains: string[]) => void): string[]; 702 | } 703 | 704 | declare module "net" { 705 | import stream = require("stream"); 706 | 707 | export interface Socket extends stream.Duplex { 708 | // Extended base methods 709 | write(buffer: Buffer): boolean; 710 | write(buffer: Buffer, cb?: Function): boolean; 711 | write(str: string, cb?: Function): boolean; 712 | write(str: string, encoding?: string, cb?: Function): boolean; 713 | write(str: string, encoding?: string, fd?: string): boolean; 714 | 715 | connect(port: number, host?: string, connectionListener?: Function): void; 716 | connect(path: string, connectionListener?: Function): void; 717 | bufferSize: number; 718 | setEncoding(encoding?: string): void; 719 | write(data: any, encoding?: string, callback?: Function): void; 720 | destroy(): void; 721 | pause(): void; 722 | resume(): void; 723 | setTimeout(timeout: number, callback?: Function): void; 724 | setNoDelay(noDelay?: boolean): void; 725 | setKeepAlive(enable?: boolean, initialDelay?: number): void; 726 | address(): { port: number; family: string; address: string; }; 727 | unref(): void; 728 | ref(): void; 729 | 730 | remoteAddress: string; 731 | remotePort: number; 732 | bytesRead: number; 733 | bytesWritten: number; 734 | 735 | // Extended base methods 736 | end(): void; 737 | end(buffer: Buffer, cb?: Function): void; 738 | end(str: string, cb?: Function): void; 739 | end(str: string, encoding?: string, cb?: Function): void; 740 | end(data?: any, encoding?: string): void; 741 | } 742 | 743 | export var Socket: { 744 | new (options?: { fd?: string; type?: string; allowHalfOpen?: boolean; }): Socket; 745 | }; 746 | 747 | export interface Server extends Socket { 748 | listen(port: number, host?: string, backlog?: number, listeningListener?: Function): Server; 749 | listen(path: string, listeningListener?: Function): Server; 750 | listen(handle: any, listeningListener?: Function): Server; 751 | close(callback?: Function): Server; 752 | address(): { port: number; family: string; address: string; }; 753 | maxConnections: number; 754 | connections: number; 755 | } 756 | export function createServer(connectionListener?: (socket: Socket) => void): Server; 757 | export function createServer(options?: { allowHalfOpen?: boolean; }, connectionListener?: (socket: Socket) => void): Server; 758 | export function connect(options: { allowHalfOpen?: boolean; }, connectionListener?: Function): Socket; 759 | export function connect(port: number, host?: string, connectionListener?: Function): Socket; 760 | export function connect(path: string, connectionListener?: Function): Socket; 761 | export function createConnection(options: { allowHalfOpen?: boolean; }, connectionListener?: Function): Socket; 762 | export function createConnection(port: number, host?: string, connectionListener?: Function): Socket; 763 | export function createConnection(path: string, connectionListener?: Function): Socket; 764 | export function isIP(input: string): number; 765 | export function isIPv4(input: string): boolean; 766 | export function isIPv6(input: string): boolean; 767 | } 768 | 769 | declare module "dgram" { 770 | import events = require("events"); 771 | 772 | interface RemoteInfo { 773 | address: string; 774 | port: number; 775 | size: number; 776 | } 777 | 778 | interface AddressInfo { 779 | address: string; 780 | family: string; 781 | port: number; 782 | } 783 | 784 | export function createSocket(type: string, callback?: (msg: Buffer, rinfo: RemoteInfo) => void): Socket; 785 | 786 | interface Socket extends events.EventEmitter { 787 | send(buf: Buffer, offset: number, length: number, port: number, address: string, callback?: (error: Error, bytes: number) => void): void; 788 | bind(port: number, address?: string, callback?: () => void): void; 789 | close(): void; 790 | address(): AddressInfo; 791 | setBroadcast(flag: boolean): void; 792 | setMulticastTTL(ttl: number): void; 793 | setMulticastLoopback(flag: boolean): void; 794 | addMembership(multicastAddress: string, multicastInterface?: string): void; 795 | dropMembership(multicastAddress: string, multicastInterface?: string): void; 796 | } 797 | } 798 | 799 | declare module "fs" { 800 | import stream = require("stream"); 801 | import events = require("events"); 802 | 803 | interface Stats { 804 | isFile(): boolean; 805 | isDirectory(): boolean; 806 | isBlockDevice(): boolean; 807 | isCharacterDevice(): boolean; 808 | isSymbolicLink(): boolean; 809 | isFIFO(): boolean; 810 | isSocket(): boolean; 811 | dev: number; 812 | ino: number; 813 | mode: number; 814 | nlink: number; 815 | uid: number; 816 | gid: number; 817 | rdev: number; 818 | size: number; 819 | blksize: number; 820 | blocks: number; 821 | atime: Date; 822 | mtime: Date; 823 | ctime: Date; 824 | } 825 | 826 | interface FSWatcher extends events.EventEmitter { 827 | close(): void; 828 | } 829 | 830 | export interface ReadStream extends stream.Readable { 831 | close(): void; 832 | } 833 | export interface WriteStream extends stream.Writable { 834 | close(): void; 835 | } 836 | 837 | export function rename(oldPath: string, newPath: string, callback?: (err?: NodeJS.ErrnoException) => void): void; 838 | export function renameSync(oldPath: string, newPath: string): void; 839 | export function truncate(path: string, callback?: (err?: NodeJS.ErrnoException) => void): void; 840 | export function truncate(path: string, len: number, callback?: (err?: NodeJS.ErrnoException) => void): void; 841 | export function truncateSync(path: string, len?: number): void; 842 | export function ftruncate(fd: number, callback?: (err?: NodeJS.ErrnoException) => void): void; 843 | export function ftruncate(fd: number, len: number, callback?: (err?: NodeJS.ErrnoException) => void): void; 844 | export function ftruncateSync(fd: number, len?: number): void; 845 | export function chown(path: string, uid: number, gid: number, callback?: (err?: NodeJS.ErrnoException) => void): void; 846 | export function chownSync(path: string, uid: number, gid: number): void; 847 | export function fchown(fd: number, uid: number, gid: number, callback?: (err?: NodeJS.ErrnoException) => void): void; 848 | export function fchownSync(fd: number, uid: number, gid: number): void; 849 | export function lchown(path: string, uid: number, gid: number, callback?: (err?: NodeJS.ErrnoException) => void): void; 850 | export function lchownSync(path: string, uid: number, gid: number): void; 851 | export function chmod(path: string, mode: number, callback?: (err?: NodeJS.ErrnoException) => void): void; 852 | export function chmod(path: string, mode: string, callback?: (err?: NodeJS.ErrnoException) => void): void; 853 | export function chmodSync(path: string, mode: number): void; 854 | export function chmodSync(path: string, mode: string): void; 855 | export function fchmod(fd: number, mode: number, callback?: (err?: NodeJS.ErrnoException) => void): void; 856 | export function fchmod(fd: number, mode: string, callback?: (err?: NodeJS.ErrnoException) => void): void; 857 | export function fchmodSync(fd: number, mode: number): void; 858 | export function fchmodSync(fd: number, mode: string): void; 859 | export function lchmod(path: string, mode: number, callback?: (err?: NodeJS.ErrnoException) => void): void; 860 | export function lchmod(path: string, mode: string, callback?: (err?: NodeJS.ErrnoException) => void): void; 861 | export function lchmodSync(path: string, mode: number): void; 862 | export function lchmodSync(path: string, mode: string): void; 863 | export function stat(path: string, callback?: (err: NodeJS.ErrnoException, stats: Stats) => any): void; 864 | export function lstat(path: string, callback?: (err: NodeJS.ErrnoException, stats: Stats) => any): void; 865 | export function fstat(fd: number, callback?: (err: NodeJS.ErrnoException, stats: Stats) => any): void; 866 | export function statSync(path: string): Stats; 867 | export function lstatSync(path: string): Stats; 868 | export function fstatSync(fd: number): Stats; 869 | export function link(srcpath: string, dstpath: string, callback?: (err?: NodeJS.ErrnoException) => void): void; 870 | export function linkSync(srcpath: string, dstpath: string): void; 871 | export function symlink(srcpath: string, dstpath: string, type?: string, callback?: (err?: NodeJS.ErrnoException) => void): void; 872 | export function symlinkSync(srcpath: string, dstpath: string, type?: string): void; 873 | export function readlink(path: string, callback?: (err: NodeJS.ErrnoException, linkString: string) => any): void; 874 | export function readlinkSync(path: string): string; 875 | export function realpath(path: string, callback?: (err: NodeJS.ErrnoException, resolvedPath: string) => any): void; 876 | export function realpath(path: string, cache: { [path: string]: string }, callback: (err: NodeJS.ErrnoException, resolvedPath: string) => any): void; 877 | export function realpathSync(path: string, cache?: { [path: string]: string }): string; 878 | export function unlink(path: string, callback?: (err?: NodeJS.ErrnoException) => void): void; 879 | export function unlinkSync(path: string): void; 880 | export function rmdir(path: string, callback?: (err?: NodeJS.ErrnoException) => void): void; 881 | export function rmdirSync(path: string): void; 882 | export function mkdir(path: string, callback?: (err?: NodeJS.ErrnoException) => void): void; 883 | export function mkdir(path: string, mode: number, callback?: (err?: NodeJS.ErrnoException) => void): void; 884 | export function mkdir(path: string, mode: string, callback?: (err?: NodeJS.ErrnoException) => void): void; 885 | export function mkdirSync(path: string, mode?: number): void; 886 | export function mkdirSync(path: string, mode?: string): void; 887 | export function readdir(path: string, callback?: (err: NodeJS.ErrnoException, files: string[]) => void): void; 888 | export function readdirSync(path: string): string[]; 889 | export function close(fd: number, callback?: (err?: NodeJS.ErrnoException) => void): void; 890 | export function closeSync(fd: number): void; 891 | export function open(path: string, flags: string, callback?: (err: NodeJS.ErrnoException, fd: number) => any): void; 892 | export function open(path: string, flags: string, mode: number, callback?: (err: NodeJS.ErrnoException, fd: number) => any): void; 893 | export function open(path: string, flags: string, mode: string, callback?: (err: NodeJS.ErrnoException, fd: number) => any): void; 894 | export function openSync(path: string, flags: string, mode?: number): number; 895 | export function openSync(path: string, flags: string, mode?: string): number; 896 | export function utimes(path: string, atime: number, mtime: number, callback?: (err?: NodeJS.ErrnoException) => void): void; 897 | export function utimes(path: string, atime: Date, mtime: Date, callback?: (err?: NodeJS.ErrnoException) => void): void; 898 | export function utimesSync(path: string, atime: number, mtime: number): void; 899 | export function utimesSync(path: string, atime: Date, mtime: Date): void; 900 | export function futimes(fd: number, atime: number, mtime: number, callback?: (err?: NodeJS.ErrnoException) => void): void; 901 | export function futimes(fd: number, atime: Date, mtime: Date, callback?: (err?: NodeJS.ErrnoException) => void): void; 902 | export function futimesSync(fd: number, atime: number, mtime: number): void; 903 | export function futimesSync(fd: number, atime: Date, mtime: Date): void; 904 | export function fsync(fd: number, callback?: (err?: NodeJS.ErrnoException) => void): void; 905 | export function fsyncSync(fd: number): void; 906 | export function write(fd: number, buffer: Buffer, offset: number, length: number, position: number, callback?: (err: NodeJS.ErrnoException, written: number, buffer: Buffer) => void): void; 907 | export function writeSync(fd: number, buffer: Buffer, offset: number, length: number, position: number): number; 908 | export function read(fd: number, buffer: Buffer, offset: number, length: number, position: number, callback?: (err: NodeJS.ErrnoException, bytesRead: number, buffer: Buffer) => void): void; 909 | export function readSync(fd: number, buffer: Buffer, offset: number, length: number, position: number): number; 910 | export function readFile(filename: string, encoding: string, callback: (err: NodeJS.ErrnoException, data: string) => void): void; 911 | export function readFile(filename: string, options: { encoding: string; flag?: string; }, callback: (err: NodeJS.ErrnoException, data: string) => void): void; 912 | export function readFile(filename: string, options: { flag?: string; }, callback: (err: NodeJS.ErrnoException, data: Buffer) => void): void; 913 | export function readFile(filename: string, callback: (err: NodeJS.ErrnoException, data: Buffer) => void): void; 914 | export function readFileSync(filename: string, encoding: string): string; 915 | export function readFileSync(filename: string, options: { encoding: string; flag?: string; }): string; 916 | export function readFileSync(filename: string, options?: { flag?: string; }): Buffer; 917 | export function writeFile(filename: string, data: any, callback?: (err: NodeJS.ErrnoException) => void): void; 918 | export function writeFile(filename: string, data: any, options: { encoding?: string; mode?: number; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void; 919 | export function writeFile(filename: string, data: any, options: { encoding?: string; mode?: string; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void; 920 | export function writeFileSync(filename: string, data: any, options?: { encoding?: string; mode?: number; flag?: string; }): void; 921 | export function writeFileSync(filename: string, data: any, options?: { encoding?: string; mode?: string; flag?: string; }): void; 922 | export function appendFile(filename: string, data: any, options: { encoding?: string; mode?: number; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void; 923 | export function appendFile(filename: string, data: any, options: { encoding?: string; mode?: string; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void; 924 | export function appendFile(filename: string, data: any, callback?: (err: NodeJS.ErrnoException) => void): void; 925 | export function appendFileSync(filename: string, data: any, options?: { encoding?: string; mode?: number; flag?: string; }): void; 926 | export function appendFileSync(filename: string, data: any, options?: { encoding?: string; mode?: string; flag?: string; }): void; 927 | export function watchFile(filename: string, listener: (curr: Stats, prev: Stats) => void): void; 928 | export function watchFile(filename: string, options: { persistent?: boolean; interval?: number; }, listener: (curr: Stats, prev: Stats) => void): void; 929 | export function unwatchFile(filename: string, listener?: (curr: Stats, prev: Stats) => void): void; 930 | export function watch(filename: string, listener?: (event: string, filename: string) => any): FSWatcher; 931 | export function watch(filename: string, options: { persistent?: boolean; }, listener?: (event: string, filename: string) => any): FSWatcher; 932 | export function exists(path: string, callback?: (exists: boolean) => void): void; 933 | export function existsSync(path: string): boolean; 934 | export function createReadStream(path: string, options?: { 935 | flags?: string; 936 | encoding?: string; 937 | fd?: string; 938 | mode?: number; 939 | bufferSize?: number; 940 | }): ReadStream; 941 | export function createReadStream(path: string, options?: { 942 | flags?: string; 943 | encoding?: string; 944 | fd?: string; 945 | mode?: string; 946 | bufferSize?: number; 947 | }): ReadStream; 948 | export function createWriteStream(path: string, options?: { 949 | flags?: string; 950 | encoding?: string; 951 | string?: string; 952 | }): WriteStream; 953 | } 954 | 955 | declare module "path" { 956 | export function normalize(p: string): string; 957 | export function join(...paths: any[]): string; 958 | export function resolve(...pathSegments: any[]): string; 959 | export function relative(from: string, to: string): string; 960 | export function dirname(p: string): string; 961 | export function basename(p: string, ext?: string): string; 962 | export function extname(p: string): string; 963 | export var sep: string; 964 | } 965 | 966 | declare module "string_decoder" { 967 | export interface NodeStringDecoder { 968 | write(buffer: Buffer): string; 969 | detectIncompleteChar(buffer: Buffer): number; 970 | } 971 | export var StringDecoder: { 972 | new (encoding: string): NodeStringDecoder; 973 | }; 974 | } 975 | 976 | declare module "tls" { 977 | import crypto = require("crypto"); 978 | import net = require("net"); 979 | import stream = require("stream"); 980 | 981 | var CLIENT_RENEG_LIMIT: number; 982 | var CLIENT_RENEG_WINDOW: number; 983 | 984 | export interface TlsOptions { 985 | pfx?: any; //string or buffer 986 | key?: any; //string or buffer 987 | passphrase?: string; 988 | cert?: any; 989 | ca?: any; //string or buffer 990 | crl?: any; //string or string array 991 | ciphers?: string; 992 | honorCipherOrder?: any; 993 | requestCert?: boolean; 994 | rejectUnauthorized?: boolean; 995 | NPNProtocols?: any; //array or Buffer; 996 | SNICallback?: (servername: string) => any; 997 | } 998 | 999 | export interface ConnectionOptions { 1000 | host?: string; 1001 | port?: number; 1002 | socket?: net.Socket; 1003 | pfx?: any; //string | Buffer 1004 | key?: any; //string | Buffer 1005 | passphrase?: string; 1006 | cert?: any; //string | Buffer 1007 | ca?: any; //Array of string | Buffer 1008 | rejectUnauthorized?: boolean; 1009 | NPNProtocols?: any; //Array of string | Buffer 1010 | servername?: string; 1011 | } 1012 | 1013 | export interface Server extends net.Server { 1014 | // Extended base methods 1015 | listen(port: number, host?: string, backlog?: number, listeningListener?: Function): Server; 1016 | listen(path: string, listeningListener?: Function): Server; 1017 | listen(handle: any, listeningListener?: Function): Server; 1018 | 1019 | listen(port: number, host?: string, callback?: Function): Server; 1020 | close(): Server; 1021 | address(): { port: number; family: string; address: string; }; 1022 | addContext(hostName: string, credentials: { 1023 | key: string; 1024 | cert: string; 1025 | ca: string; 1026 | }): void; 1027 | maxConnections: number; 1028 | connections: number; 1029 | } 1030 | 1031 | export interface ClearTextStream extends stream.Duplex { 1032 | authorized: boolean; 1033 | authorizationError: Error; 1034 | getPeerCertificate(): any; 1035 | getCipher: { 1036 | name: string; 1037 | version: string; 1038 | }; 1039 | address: { 1040 | port: number; 1041 | family: string; 1042 | address: string; 1043 | }; 1044 | remoteAddress: string; 1045 | remotePort: number; 1046 | } 1047 | 1048 | export interface SecurePair { 1049 | encrypted: any; 1050 | cleartext: any; 1051 | } 1052 | 1053 | export function createServer(options: TlsOptions, secureConnectionListener?: (cleartextStream: ClearTextStream) => void): Server; 1054 | export function connect(options: TlsOptions, secureConnectionListener?: () => void): ClearTextStream; 1055 | export function connect(port: number, host?: string, options?: ConnectionOptions, secureConnectListener?: () => void): ClearTextStream; 1056 | export function connect(port: number, options?: ConnectionOptions, secureConnectListener?: () => void): ClearTextStream; 1057 | export function createSecurePair(credentials?: crypto.Credentials, isServer?: boolean, requestCert?: boolean, rejectUnauthorized?: boolean): SecurePair; 1058 | } 1059 | 1060 | declare module "crypto" { 1061 | export interface CredentialDetails { 1062 | pfx: string; 1063 | key: string; 1064 | passphrase: string; 1065 | cert: string; 1066 | ca: any; //string | string array 1067 | crl: any; //string | string array 1068 | ciphers: string; 1069 | } 1070 | export interface Credentials { context?: any; } 1071 | export function createCredentials(details: CredentialDetails): Credentials; 1072 | export function createHash(algorithm: string): Hash; 1073 | export function createHmac(algorithm: string, key: string): Hmac; 1074 | export function createHmac(algorithm: string, key: Buffer): Hmac; 1075 | interface Hash { 1076 | update(data: any, input_encoding?: string): Hash; 1077 | digest(encoding: 'buffer'): Buffer; 1078 | digest(encoding: string): any; 1079 | digest(): Buffer; 1080 | } 1081 | interface Hmac { 1082 | update(data: any, input_encoding?: string): Hmac; 1083 | digest(encoding: 'buffer'): Buffer; 1084 | digest(encoding: string): any; 1085 | digest(): Buffer; 1086 | } 1087 | export function createCipher(algorithm: string, password: any): Cipher; 1088 | export function createCipheriv(algorithm: string, key: any, iv: any): Cipher; 1089 | interface Cipher { 1090 | update(data: Buffer): Buffer; 1091 | update(data: string, input_encoding?: string, output_encoding?: string): string; 1092 | final(): Buffer; 1093 | final(output_encoding: string): string; 1094 | setAutoPadding(auto_padding: boolean): void; 1095 | } 1096 | export function createDecipher(algorithm: string, password: any): Decipher; 1097 | export function createDecipheriv(algorithm: string, key: any, iv: any): Decipher; 1098 | interface Decipher { 1099 | update(data: Buffer): Buffer; 1100 | update(data: string, input_encoding?: string, output_encoding?: string): string; 1101 | final(): Buffer; 1102 | final(output_encoding: string): string; 1103 | setAutoPadding(auto_padding: boolean): void; 1104 | } 1105 | export function createSign(algorithm: string): Signer; 1106 | interface Signer { 1107 | update(data: any): void; 1108 | sign(private_key: string, output_format: string): string; 1109 | } 1110 | export function createVerify(algorith: string): Verify; 1111 | interface Verify { 1112 | update(data: any): void; 1113 | verify(object: string, signature: string, signature_format?: string): boolean; 1114 | } 1115 | export function createDiffieHellman(prime_length: number): DiffieHellman; 1116 | export function createDiffieHellman(prime: number, encoding?: string): DiffieHellman; 1117 | interface DiffieHellman { 1118 | generateKeys(encoding?: string): string; 1119 | computeSecret(other_public_key: string, input_encoding?: string, output_encoding?: string): string; 1120 | getPrime(encoding?: string): string; 1121 | getGenerator(encoding: string): string; 1122 | getPublicKey(encoding?: string): string; 1123 | getPrivateKey(encoding?: string): string; 1124 | setPublicKey(public_key: string, encoding?: string): void; 1125 | setPrivateKey(public_key: string, encoding?: string): void; 1126 | } 1127 | export function getDiffieHellman(group_name: string): DiffieHellman; 1128 | export function pbkdf2(password: string, salt: string, iterations: number, keylen: number, callback: (err: Error, derivedKey: Buffer) => any): void; 1129 | export function pbkdf2Sync(password: string, salt: string, iterations: number, keylen: number): Buffer; 1130 | export function randomBytes(size: number): Buffer; 1131 | export function randomBytes(size: number, callback: (err: Error, buf: Buffer) => void): void; 1132 | export function pseudoRandomBytes(size: number): Buffer; 1133 | export function pseudoRandomBytes(size: number, callback: (err: Error, buf: Buffer) => void): void; 1134 | } 1135 | 1136 | declare module "stream" { 1137 | import events = require("events"); 1138 | 1139 | export interface Stream extends events.EventEmitter { 1140 | pipe(destination: T, options?: { end?: boolean; }): T; 1141 | } 1142 | 1143 | export interface ReadableOptions { 1144 | highWaterMark?: number; 1145 | encoding?: string; 1146 | objectMode?: boolean; 1147 | } 1148 | 1149 | export class Readable extends events.EventEmitter implements NodeJS.ReadableStream { 1150 | readable: boolean; 1151 | constructor(opts?: ReadableOptions); 1152 | _read(size: number): void; 1153 | read(size?: number): any; 1154 | setEncoding(encoding: string): void; 1155 | pause(): void; 1156 | resume(): void; 1157 | pipe(destination: T, options?: { end?: boolean; }): T; 1158 | unpipe(destination?: T): void; 1159 | unshift(chunk: string): void; 1160 | unshift(chunk: Buffer): void; 1161 | wrap(oldStream: NodeJS.ReadableStream): NodeJS.ReadableStream; 1162 | push(chunk: any, encoding?: string): boolean; 1163 | } 1164 | 1165 | export interface WritableOptions { 1166 | highWaterMark?: number; 1167 | decodeStrings?: boolean; 1168 | } 1169 | 1170 | export class Writable extends events.EventEmitter implements NodeJS.WritableStream { 1171 | writable: boolean; 1172 | constructor(opts?: WritableOptions); 1173 | _write(data: Buffer, encoding: string, callback: Function): void; 1174 | _write(data: string, encoding: string, callback: Function): void; 1175 | write(buffer: Buffer, cb?: Function): boolean; 1176 | write(str: string, cb?: Function): boolean; 1177 | write(str: string, encoding?: string, cb?: Function): boolean; 1178 | end(): void; 1179 | end(buffer: Buffer, cb?: Function): void; 1180 | end(str: string, cb?: Function): void; 1181 | end(str: string, encoding?: string, cb?: Function): void; 1182 | } 1183 | 1184 | export interface DuplexOptions extends ReadableOptions, WritableOptions { 1185 | allowHalfOpen?: boolean; 1186 | } 1187 | 1188 | // Note: Duplex extends both Readable and Writable. 1189 | export class Duplex extends Readable implements NodeJS.ReadWriteStream { 1190 | writable: boolean; 1191 | constructor(opts?: DuplexOptions); 1192 | _write(data: Buffer, encoding: string, callback: Function): void; 1193 | _write(data: string, encoding: string, callback: Function): void; 1194 | write(buffer: Buffer, cb?: Function): boolean; 1195 | write(str: string, cb?: Function): boolean; 1196 | write(str: string, encoding?: string, cb?: Function): boolean; 1197 | end(): void; 1198 | end(buffer: Buffer, cb?: Function): void; 1199 | end(str: string, cb?: Function): void; 1200 | end(str: string, encoding?: string, cb?: Function): void; 1201 | } 1202 | 1203 | export interface TransformOptions extends ReadableOptions, WritableOptions { } 1204 | 1205 | // Note: Transform lacks the _read and _write methods of Readable/Writable. 1206 | export class Transform extends events.EventEmitter implements NodeJS.ReadWriteStream { 1207 | readable: boolean; 1208 | writable: boolean; 1209 | constructor(opts?: TransformOptions); 1210 | _transform(chunk: Buffer, encoding: string, callback: Function): void; 1211 | _transform(chunk: string, encoding: string, callback: Function): void; 1212 | _flush(callback: Function): void; 1213 | read(size?: number): any; 1214 | setEncoding(encoding: string): void; 1215 | pause(): void; 1216 | resume(): void; 1217 | pipe(destination: T, options?: { end?: boolean; }): T; 1218 | unpipe(destination?: T): void; 1219 | unshift(chunk: string): void; 1220 | unshift(chunk: Buffer): void; 1221 | wrap(oldStream: NodeJS.ReadableStream): NodeJS.ReadableStream; 1222 | push(chunk: any, encoding?: string): boolean; 1223 | write(buffer: Buffer, cb?: Function): boolean; 1224 | write(str: string, cb?: Function): boolean; 1225 | write(str: string, encoding?: string, cb?: Function): boolean; 1226 | end(): void; 1227 | end(buffer: Buffer, cb?: Function): void; 1228 | end(str: string, cb?: Function): void; 1229 | end(str: string, encoding?: string, cb?: Function): void; 1230 | } 1231 | 1232 | export class PassThrough extends Transform { } 1233 | } 1234 | 1235 | declare module "util" { 1236 | export interface InspectOptions { 1237 | showHidden?: boolean; 1238 | depth?: number; 1239 | colors?: boolean; 1240 | customInspect?: boolean; 1241 | } 1242 | 1243 | export function format(format: any, ...param: any[]): string; 1244 | export function debug(string: string): void; 1245 | export function error(...param: any[]): void; 1246 | export function puts(...param: any[]): void; 1247 | export function print(...param: any[]): void; 1248 | export function log(string: string): void; 1249 | export function inspect(object: any, showHidden?: boolean, depth?: number, color?: boolean): string; 1250 | export function inspect(object: any, options: InspectOptions): string; 1251 | export function isArray(object: any): boolean; 1252 | export function isRegExp(object: any): boolean; 1253 | export function isDate(object: any): boolean; 1254 | export function isError(object: any): boolean; 1255 | export function inherits(constructor: any, superConstructor: any): void; 1256 | } 1257 | 1258 | declare module "assert" { 1259 | function internal(value: any, message?: string): void; 1260 | module internal { 1261 | export class AssertionError implements Error { 1262 | name: string; 1263 | message: string; 1264 | actual: any; 1265 | expected: any; 1266 | operator: string; 1267 | generatedMessage: boolean; 1268 | 1269 | constructor(options?: { 1270 | message?: string; actual?: any; expected?: any; 1271 | operator?: string; stackStartFunction?: Function 1272 | }); 1273 | } 1274 | 1275 | export function fail(actual?: any, expected?: any, message?: string, operator?: string): void; 1276 | export function ok(value: any, message?: string): void; 1277 | export function equal(actual: any, expected: any, message?: string): void; 1278 | export function notEqual(actual: any, expected: any, message?: string): void; 1279 | export function deepEqual(actual: any, expected: any, message?: string): void; 1280 | export function notDeepEqual(acutal: any, expected: any, message?: string): void; 1281 | export function strictEqual(actual: any, expected: any, message?: string): void; 1282 | export function notStrictEqual(actual: any, expected: any, message?: string): void; 1283 | export var throws: { 1284 | (block: Function, message?: string): void; 1285 | (block: Function, error: Function, message?: string): void; 1286 | (block: Function, error: RegExp, message?: string): void; 1287 | (block: Function, error: (err: any) => boolean, message?: string): void; 1288 | }; 1289 | 1290 | export var doesNotThrow: { 1291 | (block: Function, message?: string): void; 1292 | (block: Function, error: Function, message?: string): void; 1293 | (block: Function, error: RegExp, message?: string): void; 1294 | (block: Function, error: (err: any) => boolean, message?: string): void; 1295 | }; 1296 | 1297 | export function ifError(value: any): void; 1298 | } 1299 | 1300 | export = internal; 1301 | } 1302 | 1303 | declare module "tty" { 1304 | import net = require("net"); 1305 | 1306 | export function isatty(fd: number): boolean; 1307 | export interface ReadStream extends net.Socket { 1308 | isRaw: boolean; 1309 | setRawMode(mode: boolean): void; 1310 | } 1311 | export interface WriteStream extends net.Socket { 1312 | columns: number; 1313 | rows: number; 1314 | } 1315 | } 1316 | 1317 | declare module "domain" { 1318 | import events = require("events"); 1319 | 1320 | export class Domain extends events.EventEmitter { 1321 | run(fn: Function): void; 1322 | add(emitter: events.EventEmitter): void; 1323 | remove(emitter: events.EventEmitter): void; 1324 | bind(cb: (err: Error, data: any) => any): any; 1325 | intercept(cb: (data: any) => any): any; 1326 | dispose(): void; 1327 | 1328 | addListener(event: string, listener: Function): Domain; 1329 | on(event: string, listener: Function): Domain; 1330 | once(event: string, listener: Function): Domain; 1331 | removeListener(event: string, listener: Function): Domain; 1332 | removeAllListeners(event?: string): Domain; 1333 | } 1334 | 1335 | export function create(): Domain; 1336 | } -------------------------------------------------------------------------------- /debug.jsx: -------------------------------------------------------------------------------- 1 | var AccountPage = React.createClass({ 2 | render: function () { 3 | return ( 4 |
5 |

{this.props.account.getName()}

6 |
Your balance is {this.props.account.getBalance()}
7 |
8 |
9 |   10 | 11 |
12 |
13 | ); 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /jsxtyper.ts: -------------------------------------------------------------------------------- 1 | (function () { 2 | var jsxtyper = require('./jsxtypercore.js'), 3 | fs = require('fs'); 4 | 5 | if (process.argv.length < 3) { 6 | var scriptName = process.argv[1].replace(/.*[\/\\]/, ''); 7 | console.error(`${scriptName} generates TypeScript interfaces from React .jsx files`); 8 | console.error(`Usage: node ${scriptName} `); 9 | process.exit(1); 10 | } 11 | 12 | fs.readFile(process.argv[2], (err, data) => { 13 | if (err) { 14 | console.error(`file could not be read: ${err.message}`); 15 | process.exit(1); 16 | } 17 | 18 | jsxtyper.generateTypeScript(data, (err: Error, tsText: string) => { 19 | if (err) { 20 | console.error(err.message); 21 | } 22 | else { 23 | console.log(tsText); 24 | } 25 | }); 26 | }); 27 | })(); 28 | -------------------------------------------------------------------------------- /jsxtypercore.ts: -------------------------------------------------------------------------------- 1 | // Uses esprima-fb to generate syntax tree of supplied JSX file, then traverses 2 | // the syntax tree to generate TypeScript interfaces for props and state. 3 | 4 | /// 5 | 6 | // To get estree.d.ts open Package Manager Console and type: 7 | // Install-Package estree.TypeScript.DefinitelyTyped 8 | /// 9 | 10 | /// 11 | 12 | var fs = require('fs'), 13 | esprima = require('esprima-fb'), 14 | estraverse = require('estraverse-fb'); 15 | 16 | // ------------------------------------------------------------------------------------------------ 17 | // Find React.createClass() statements. 18 | // ------------------------------------------------------------------------------------------------ 19 | 20 | function traverseProgram(program: ESTree.Program): void { 21 | for (var i = 0; i < program.body.length; i++) { 22 | // Look for statements of the form: var MyClass = React.createClass({ ... }); 23 | if (program.body[i].type === "VariableDeclaration") { 24 | var statement = program.body[i]; 25 | var declarator = statement.declarations[0]; 26 | if (declarator.init.type == "CallExpression") { 27 | var callExpression = declarator.init; 28 | if (calleeIsReactCreateClass(callExpression.callee)) { 29 | var id = declarator.id; 30 | addComponentClass(id.name); 31 | traverseCreateClassCallExpression(callExpression); 32 | } 33 | } 34 | } 35 | } 36 | } 37 | 38 | /** Checks whether callee is React.createClass */ 39 | function calleeIsReactCreateClass(callee: ESTree.Expression): boolean { 40 | if (callee.type != "MemberExpression") { 41 | return false; 42 | } 43 | var memberExpression = callee; 44 | if (memberExpression.object.type != "Identifier" || memberExpression.property.type != "Identifier") { 45 | return false; 46 | } 47 | var objIdentifier = memberExpression.object; 48 | var propIdentifier = memberExpression.property; 49 | if (objIdentifier.name != "React" || propIdentifier.name != "createClass") { 50 | return false; 51 | } 52 | return true; 53 | } 54 | 55 | function traverseCreateClassCallExpression(callExpression: ESTree.CallExpression): void { 56 | if (callExpression.arguments.length < 1 || callExpression.arguments[0].type != "ObjectExpression") { 57 | throw constructError(302, "React.createClass() must be passed an object expression.", callExpression.loc); 58 | } 59 | var objectExpression = callExpression.arguments[0]; 60 | traverse(objectExpression); 61 | extractCssNames(objectExpression); 62 | } 63 | 64 | function extractCssNames(node: ESTree.Node): void { 65 | estraverse.traverse(node, { 66 | enter: node => { 67 | if (node.type === 'JSXElement') { 68 | var jsxElement = node; 69 | var attributes = jsxElement.openingElement.attributes; 70 | for (var i = 0; i < attributes.length; i++) { 71 | if (!attributes[i].name) { 72 | continue; 73 | } 74 | var attrName = attributes[i].name.name; 75 | if (attrName === 'class') { 76 | throw constructError(303, "Use 'className' to specify css class, not 'class'.", attributes[i].name.loc); 77 | } 78 | else if (attrName === 'className' && attributes[i].value.type === "Literal") { 79 | var literal = attributes[i].value; 80 | addCssClassNames(literal.value); 81 | } 82 | } 83 | } 84 | } 85 | }); 86 | } 87 | 88 | // ------------------------------------------------------------------------------------------------ 89 | // Traverse various types of nodes. 90 | // The actual traverse happens in estraverse library. We only eavesdrop on that traverse here. 91 | // ------------------------------------------------------------------------------------------------ 92 | 93 | function traverse(ast: ESTree.Node): void { 94 | estraverse.traverse(ast, { 95 | enter: node => { 96 | switch (node.type) { 97 | case 'MemberExpression': 98 | case 'CallExpression': 99 | var memberPath = traverseMemberExpression(node); 100 | addMemberPath(memberPath); 101 | return estraverse.VisitorOption.Skip; 102 | 103 | case 'VariableDeclaration': 104 | traverseVariableDeclaration(node); 105 | return estraverse.VisitorOption.Skip; 106 | 107 | case 'FunctionDeclaration': 108 | case 'FunctionExpression': 109 | pushScope(); 110 | break; 111 | } 112 | }, 113 | leave: node => { 114 | switch (node.type) { 115 | case 'FunctionDeclaration': 116 | case 'FunctionExpression': 117 | popScope(); 118 | break; 119 | } 120 | } 121 | }); 122 | } 123 | 124 | function traverseVariableDeclaration(declaration: ESTree.VariableDeclaration): void { 125 | for (var i = 0; i < declaration.declarations.length; i++) { 126 | var declarator = declaration.declarations[i]; 127 | if (declarator.id.type === 'Identifier' && declarator.init && 128 | (declarator.init.type === 'MemberExpression' || 129 | declarator.init.type === 'CallExpression')) { 130 | var identifer = declarator.id; 131 | var memberExpression = declarator.init; 132 | var memberPath = traverseMemberExpression(memberExpression); 133 | processVariableDeclaration(identifer, memberPath); 134 | } 135 | else if (declarator.init) { 136 | traverse(declarator.init); 137 | } 138 | } 139 | } 140 | 141 | function traverseMemberExpression(expression: ESTree.MemberExpression): MemberPath { 142 | var members: Member[] = []; 143 | for (var node = expression; ;) { 144 | if (node.type === 'MemberExpression') { 145 | var memberExpression = node; 146 | if (memberExpression.computed) { 147 | traverse(memberExpression.property); 148 | } 149 | members.unshift(new Member(memberExpression.property, memberExpression.computed)); 150 | node = memberExpression.object; 151 | } 152 | else if (node.type === 'CallExpression') { 153 | members.unshift(new Member(node)); 154 | var callExpression = node; 155 | 156 | if (callExpression.callee.type === 'FunctionExpression') { 157 | var functionExpression = callExpression.callee; 158 | pushScope(); 159 | traverse(functionExpression.body); 160 | popScope(); 161 | } 162 | 163 | for (var i = 0; i < callExpression.arguments.length; i++) { 164 | traverse(callExpression.arguments[i]); 165 | } 166 | node = callExpression.callee; 167 | } 168 | else { 169 | members.unshift(new Member(node)); 170 | break; 171 | } 172 | } 173 | return new MemberPath(members); 174 | } 175 | 176 | // ------------------------------------------------------------------------------------------------ 177 | 178 | /** Represents a node in a member expression path */ 179 | class Member { 180 | constructor(public node: ESTree.Expression, public isComputed = false) { 181 | } 182 | 183 | public isArrayIndexer(): boolean { 184 | if (this.isComputed) { 185 | return !isLiteralString(this.node); 186 | } 187 | else { 188 | return false; 189 | } 190 | } 191 | } 192 | 193 | /** Represents a member expression path */ 194 | class MemberPath { 195 | constructor(public path: Member[]) { 196 | } 197 | 198 | /** This is very limited, but works for the common case of copying an array element to a temp variable. */ 199 | public resolvePath(): Member[] { 200 | if (this.path.length && this.path[0].node.type == 'Identifier') { 201 | var identifier = this.path[0].node; 202 | var memberPath = resolve(identifier.name); 203 | if (memberPath) { 204 | var p = this.path.slice(1); 205 | return memberPath.path.concat(p); 206 | } 207 | } 208 | return this.path; 209 | } 210 | 211 | /** (For debugging) Reconstructs source code for the member path. */ 212 | public debug_getCode(): string { 213 | var path = this.resolvePath(); 214 | 215 | var output: string[] = []; 216 | for (var i = 0; i < path.length; i++) { 217 | var node = path[i].node; 218 | if (path[i].isComputed) { 219 | if (path[i].isArrayIndexer()) { 220 | output.push('[]'); 221 | } 222 | else { 223 | break; 224 | } 225 | } 226 | else { 227 | switch (node.type) { 228 | case 'Identifier': 229 | var identifier = node; 230 | output.push(identifier.name); 231 | break; 232 | case 'ThisExpression': 233 | output.push('this'); 234 | break; 235 | case 'CallExpression': 236 | output.push('()'); 237 | break; 238 | default: 239 | output.push('~' + node.type + '~'); 240 | break; 241 | } 242 | } 243 | if (i < path.length - 1) { 244 | var nextMember = path[i + 1]; 245 | if (nextMember.node.type !== 'CallExpression' && !nextMember.isComputed) { 246 | output.push('.'); 247 | } 248 | } 249 | } 250 | return output.join(''); 251 | } 252 | 253 | public addToFieldDict(propsDict: Field, stateDict: Field): void { 254 | var path = this.resolvePath(); 255 | 256 | if (path.length >= 3 && 257 | path[0].node.type === 'ThisExpression' && 258 | path[1].node.type === 'Identifier') { 259 | 260 | var identifier = path[1].node; 261 | var d: Field; 262 | if (identifier.name === 'props') 263 | d = propsDict; 264 | else if (identifier.name === 'state') 265 | d = stateDict; 266 | else 267 | return; 268 | 269 | var key: string; 270 | var field: Field; 271 | for (var i = 2; i < path.length; i++) { 272 | var node = path[i].node; 273 | if (path[i].isComputed) { 274 | if (path[i].isArrayIndexer()) { 275 | key = 'Indexer'; 276 | field = { "nodeType": nodeTypes.Indexer }; 277 | } 278 | else { 279 | break; 280 | } 281 | } 282 | else { 283 | switch (node.type) { 284 | case 'Identifier': 285 | var identifier = node; 286 | key = '.' + identifier.name; 287 | field = { "nodeType": nodeTypes.Identifier }; 288 | break; 289 | case 'CallExpression': 290 | key = 'CallExpression'; 291 | field = { "nodeType": nodeTypes.CallExpression }; 292 | break; 293 | } 294 | } 295 | d = d[key] || (d[key] = field); 296 | } 297 | } 298 | } 299 | } 300 | 301 | function isLiteralString(node: ESTree.Expression): boolean { 302 | if (node.type === 'Literal') { 303 | var literal = node; 304 | return (typeof literal.value === 'string'); 305 | } 306 | return false; 307 | } 308 | 309 | var nodeTypes = { 310 | Identifier: 'Identifier', 311 | CallExpression: 'CallExpression', 312 | Indexer: 'Indexer' 313 | }; 314 | 315 | interface Field { 316 | nodeType?: string; 317 | identifier?: string; 318 | } 319 | 320 | interface FieldDict { [fieldName: string]: FieldDict; }; 321 | 322 | function constructError(errorCode: number, errorMessage: string, loc?: ESTree.SourceLocation): Error { 323 | if (loc) { 324 | return new Error(`Error ${errorCode} near line ${loc.start.line}, column ${loc.start.column}: ${errorMessage}`); 325 | } 326 | else { 327 | return new Error(`Error ${errorCode}: ${errorMessage}`); 328 | } 329 | } 330 | 331 | // ------------------------------------------------------------------------------------------------ 332 | // Simple scope implementation 333 | // Intended to handle the common case of copying an array element to a temp variable. 334 | // ------------------------------------------------------------------------------------------------ 335 | 336 | interface Scope { [symbol: string]: MemberPath }; 337 | 338 | var scopeChain: Scope[] = [{}]; 339 | 340 | function pushScope(): void { 341 | scopeChain.push({}); 342 | } 343 | 344 | function popScope(): void { 345 | scopeChain.pop(); 346 | } 347 | 348 | function addToCurrentScope(name: string, value: MemberPath): void { 349 | var scope = scopeChain[scopeChain.length - 1]; 350 | scope[name] = value; 351 | } 352 | 353 | function resolve(name: string): MemberPath { 354 | for (var i = scopeChain.length - 1; i >= 0; i--) { 355 | if (name in scopeChain[i]) { 356 | return scopeChain[i][name]; 357 | } 358 | } 359 | return null; 360 | } 361 | 362 | function debug_printScopeChain(): void { 363 | console.log(); 364 | for (var i = 0; i < scopeChain.length; i++) { 365 | var scope = scopeChain[i]; 366 | console.log("scope"); 367 | console.log("====="); 368 | for (var symbol in scope) { 369 | console.log(symbol + " = " + scope[symbol].debug_getCode()); 370 | } 371 | console.log(); 372 | } 373 | } 374 | 375 | // ------------------------------------------------------------------------------------------------ 376 | // TypeScript generation 377 | // ------------------------------------------------------------------------------------------------ 378 | 379 | enum FieldType { Object, Scalar, FunctionCall }; 380 | 381 | interface ComponentClass { 382 | className: string; 383 | propsDict: FieldDict; 384 | stateDict: FieldDict; 385 | cssClassNames: { [cssClass: string]: string }; 386 | } 387 | 388 | var componentClasses: ComponentClass[] = []; 389 | 390 | function addComponentClass(className: string): void { 391 | componentClasses.push({ 392 | className: className, 393 | propsDict: {}, 394 | stateDict: {}, 395 | cssClassNames: {} 396 | }); 397 | } 398 | 399 | function addMemberPath(memberPath: MemberPath): void { 400 | var c = componentClasses[componentClasses.length - 1]; 401 | memberPath.addToFieldDict(c.propsDict, c.stateDict); 402 | } 403 | 404 | function processVariableDeclaration(identifier: ESTree.Identifier, memberPath: MemberPath) { 405 | addToCurrentScope(identifier.name, memberPath); 406 | var c = componentClasses[componentClasses.length - 1]; 407 | memberPath.addToFieldDict(c.propsDict, c.stateDict); 408 | } 409 | 410 | function addCssClassNames(cssClassNames: string): void { 411 | var c = componentClasses[componentClasses.length - 1]; 412 | var names = cssClassNames.split(' '); 413 | for (var i = 0; i < names.length; i++) { 414 | c.cssClassNames[names[i]] = ''; 415 | } 416 | } 417 | 418 | function getFieldType(dict: any): FieldType { 419 | if (dict.CallExpression) { 420 | return FieldType.FunctionCall; 421 | } 422 | var fieldCount = 0; 423 | for (var key in dict) { 424 | if (key[0] === '.') { 425 | fieldCount++; 426 | } 427 | } 428 | return fieldCount > 0 ? FieldType.Object : FieldType.Scalar; 429 | } 430 | 431 | function findArrayIndexerKey(dict: any): string { 432 | for (var key in dict) { 433 | var field = dict[key]; 434 | if (field.nodeType === nodeTypes.Indexer) { 435 | return key; 436 | } 437 | } 438 | return null; 439 | } 440 | 441 | function constructCssNameForTS(cssClassName: string): string { 442 | var parts = cssClassName.split('-'); 443 | for (var i = 1; i < parts.length; i++) { 444 | parts[i] = parts[i].substr(0, 1).toUpperCase() + parts[i].substr(1); 445 | } 446 | return parts.join(''); 447 | } 448 | 449 | function outputInterfaces(output: string[], component: ComponentClass): void { 450 | // Uncomment next two lines for debugging. 451 | // console.log('props = ' + JSON.stringify(component.propsDict)); 452 | // console.log('state = ' + JSON.stringify(component.stateDict)); 453 | output.push(`interface ${component.className}Props {`); 454 | outputFields(output, component.propsDict, 1); 455 | output.push(`}`); 456 | output.push(''); 457 | output.push(`interface ${component.className}State {`); 458 | outputFields(output, component.stateDict, 1); 459 | output.push(`}`); 460 | output.push(''); 461 | output.push(`declare var ${component.className}: React.ComponentClass<${component.className}Props>;`); 462 | output.push(''); 463 | 464 | output.push(`var ${component.className}Selectors = {`); 465 | var cssClassNames = Object.keys(component.cssClassNames); 466 | for (var i = 0; i < cssClassNames.length; i++) { 467 | var cssName = cssClassNames[i]; 468 | var tsName = constructCssNameForTS(cssName); 469 | var sep = (i < cssClassNames.length - 1) ? ',' : ''; 470 | output.push(` ${tsName}: '.${cssName}'${sep}`); 471 | } 472 | output.push('};'); 473 | 474 | output.push(''); 475 | } 476 | 477 | function outputFields(output: string[], dict: FieldDict, indentationLevel: number): void { 478 | for (var key in dict) { 479 | if (key[0] !== '.') { 480 | continue; 481 | } 482 | var indent = ''; 483 | for (var i = 0; i < indentationLevel; i++) { 484 | indent = indent + ' '; 485 | } 486 | var field = dict[key]; 487 | var name = key.substr(1); 488 | var arrayIndexerKey = findArrayIndexerKey(dict[key]); 489 | var arrayNotation = arrayIndexerKey ? '[]' : ''; 490 | var fieldsDict = dict[key]; 491 | if (arrayIndexerKey) { 492 | fieldsDict = fieldsDict[arrayIndexerKey]; 493 | } 494 | var fieldType = getFieldType(fieldsDict); 495 | if (fieldType === FieldType.Object) { 496 | output.push(`${indent}${name}: {`); 497 | outputFields(output, fieldsDict, indentationLevel + 1); 498 | output.push(`${indent}}${arrayNotation};`); 499 | } 500 | else if (fieldType === FieldType.FunctionCall) { 501 | var returnType = getFunctionReturnType(fieldsDict['CallExpression']); 502 | output.push(`${indent}${name}: { (...args: any[]): ${returnType} }${arrayNotation};`); 503 | } 504 | else if (fieldType === FieldType.Scalar) { 505 | output.push(`${indent}${name}: any${arrayNotation};`); 506 | } 507 | } 508 | } 509 | 510 | function getFunctionReturnType(dict: FieldDict): string { 511 | var arrayNotation = ''; 512 | var arrayIndexerKey = findArrayIndexerKey(dict); 513 | if (arrayIndexerKey) { 514 | dict = dict[arrayIndexerKey]; 515 | arrayNotation = '[]'; 516 | } 517 | 518 | var returnType = getFieldType(dict); 519 | if (returnType === FieldType.Scalar) { 520 | return `any${arrayNotation}`; 521 | } 522 | else if (returnType === FieldType.FunctionCall) { 523 | return `{ (...args: any[]): any }${arrayNotation}`; 524 | } 525 | else { 526 | var output: string[] = []; 527 | output.push('{'); 528 | outputFields(output, dict, -1000); 529 | output.push(`}${arrayNotation}`); 530 | return output.join(' '); 531 | } 532 | } 533 | 534 | function generateTypeScript(jsxText: string, callback: (err: Error, tsText: string) => void): void { 535 | try { 536 | var program = esprima.parse(jsxText, { loc: true }); 537 | } 538 | catch (ex) { 539 | callback(new Error(`parser error: ${ex.message}`), null); 540 | return; 541 | } 542 | try { 543 | traverseProgram(program); 544 | } 545 | catch (ex) { 546 | callback(ex, null); 547 | return; 548 | } 549 | 550 | var output: string[] = []; 551 | output.push('// This file was automatically generated by jsxtyper. Do not modify by hand!'); 552 | output.push(''); 553 | var count = 0; 554 | for (var i = 0; i < componentClasses.length; i++) { 555 | if (componentClasses[i].className) { 556 | outputInterfaces(output, componentClasses[i]); 557 | count++; 558 | } 559 | } 560 | if (!count) { 561 | var messages = []; 562 | messages.push("Did not find any valid React components."); 563 | messages.push("Only class definitions in the following format are recognized: "); 564 | messages.push(" var MyClass = React.createClass({ ... });"); 565 | messages.push(""); 566 | callback(new Error(messages.join('\n')), null); 567 | } 568 | else { 569 | callback(null, output.join('\n')); 570 | } 571 | } 572 | 573 | module.exports.generateTypeScript = generateTypeScript; 574 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grunt-jsxtyper", 3 | "version": "0.1.0", 4 | "description": "Generates TypeScript interfaces from React's JSX files", 5 | "main": "jsxtyper.js", 6 | "author": { 7 | "name": "Rajeev" 8 | }, 9 | "dependencies": { 10 | "esprima-fb": "^15001.1.0-dev-harmony-fb", 11 | "estraverse": "^4.1.0", 12 | "estraverse-fb": "^1.3.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /tasks/grunt-jsxtyper.js: -------------------------------------------------------------------------------- 1 | var jsxtypercore = require('../jsxtypercore'), 2 | path = require('path'); 3 | 4 | module.exports = function (grunt) { 5 | grunt.registerMultiTask('jsxtyper', 'Generate TypeScript interfaces from jsx files.', function () { 6 | this.files.forEach(function (file) { 7 | 8 | // Read and concat all source jsx files. 9 | var jsxFileNames = []; 10 | var jsxText = file.src.map(function (filepath) { 11 | jsxFileNames.push(path.basename(filepath)); 12 | return grunt.file.read(filepath); 13 | }).join('\n'); 14 | grunt.log.writeln("Read " + jsxFileNames.join(", ")); 15 | 16 | // Generate TypeScript. 17 | jsxtypercore.generateTypeScript(jsxText, function (err, tsText) { 18 | if (err) { 19 | grunt.fail.warn(err); 20 | } 21 | else { 22 | grunt.log.writeln("Writing " + file.dest); 23 | grunt.file.write(file.dest, tsText); 24 | } 25 | }); 26 | }); 27 | }); 28 | }; 29 | -------------------------------------------------------------------------------- /tests/test1.jsx: -------------------------------------------------------------------------------- 1 | var AccountPage = React.createClass({ 2 | render: function () { 3 | return ( 4 |
5 |

{this.props.account.getName()}

6 |
Your balance is {this.props.account.getBalance()}
7 |
8 |
9 |   10 | 11 |
12 |
13 | ); 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /tests/test10.jsx: -------------------------------------------------------------------------------- 1 | var MembersPage = React.createClass({ 2 | getInitialState: function() { 3 | return { 4 | members: this.props.initialMembers 5 | }; 6 | }, 7 | render: function () { 8 | var memberRows = []; 9 | if (this.state.members != null) { 10 | for (var i = 0; i < this.state.members.length; i++) { 11 | var member = this.state.members[i]; 12 | memberRows.push( 13 |
14 | 15 | 16 |
17 | ); 18 | } 19 | } 20 | return ( 21 |
22 |

Member Maintenance

23 |
24 | 25 | 26 | 27 |
28 |
29 | {memberRows} 30 |
31 |
32 | ); 33 | } 34 | }); 35 | -------------------------------------------------------------------------------- /tests/test11.jsx: -------------------------------------------------------------------------------- 1 | var MessageBoxPanel = React.createClass({ 2 | render: function () { 3 | var cancelButton = this.props.hideCancelButton ? 4 | : 5 | return ( 6 |
7 |
{this.props.message}
8 |
9 |   10 | {cancelButton} 11 |
12 |
13 | ); 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /tests/test12.jsx: -------------------------------------------------------------------------------- 1 | var TransactionPage = React.createClass({ 2 | getInitialState: function () { 3 | return { balance: 0 }; 4 | }, 5 | render: function () { 6 | return ( 7 |
8 |

{this.props.accountName} - {this.props.labels.transactionType}

9 |
Your balance is {this.state.balance}
10 |
11 |
12 |
{this.props.labels.prompt}
13 |
$
14 |
15 |
16 |
17 |   18 | 19 |
20 |
21 | ); 22 | } 23 | }); 24 | -------------------------------------------------------------------------------- /tests/test2.jsx: -------------------------------------------------------------------------------- 1 | var BookPage = React.createClass({ 2 | getInitialState: function() { 3 | return { 4 | checkedOutTo: this.props.initialBookDetails.CheckedOutTo, 5 | checkedOutToMember: null 6 | }; 7 | }, 8 | render: function () { 9 | var checkedOutToMemberRow = null; 10 | if (this.state.checkedOutToMember) { 11 | var memberUrl = "#/member?id=" + this.state.checkedOutToMember.MemberId; 12 | checkedOutToMemberRow = 13 | ; 17 | } 18 | var status; 19 | if (this.state.checkedOutTo) { 20 | if (this.state.checkedOutToMember) { 21 | status = "Checked out"; 22 | } 23 | else { 24 | status = Checked out 25 | } 26 | } 27 | else { 28 | status = "Available"; 29 | } 30 | var lendingPanel = null; 31 | if (this.state.checkedOutTo) { 32 | lendingPanel =
; 33 | } 34 | else { 35 | if (this.state.lendingInitiated) { 36 | var borrowingMemberName = '(Press Find to find a member)'; 37 | if (this.state.borrowingMember) { 38 | borrowingMemberName = this.state.borrowingMember.FirstName + ' ' + this.state.borrowingMember.LastName; 39 | } 40 | lendingPanel = 41 |
42 |

Lending

43 |
44 | 45 |
{borrowingMemberName}
46 | 47 |
48 |
49 |   50 | 51 |
52 |
; 53 | } 54 | else { 55 | lendingPanel =
; 56 | } 57 | } 58 | return ( 59 |
60 |

Book Details

61 |
62 | Title: 63 | {this.props.initialBookDetails.Title} 64 |
65 |
66 | Author: 67 | {this.props.initialBookDetails.AuthorName} 68 |
69 |
70 | Status: 71 | {status} 72 |
73 | {checkedOutToMemberRow} 74 | {lendingPanel} 75 |
76 | ); 77 | } 78 | }); 79 | -------------------------------------------------------------------------------- /tests/test3.jsx: -------------------------------------------------------------------------------- 1 | var ChangePasswordPanel = React.createClass({ 2 | render: function () { 3 | return ( 4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |   12 | 13 |
14 |
15 | ); 16 | } 17 | }); 18 | -------------------------------------------------------------------------------- /tests/test4.jsx: -------------------------------------------------------------------------------- 1 | var DropdownMenuPanel = React.createClass({ 2 | render: function () { 3 | var items = []; 4 | for (var i = 0; i < this.props.items.length; i++) { 5 | items.push(
{this.props.items[i]}
); 6 | } 7 | return ( 8 |
{items}
9 | ); 10 | } 11 | }); 12 | -------------------------------------------------------------------------------- /tests/test5.jsx: -------------------------------------------------------------------------------- 1 | var EditMemberPanel = React.createClass({ 2 | render: function () { 3 | return ( 4 |
5 |
6 | 7 | 8 |
9 |
10 | 11 | 12 |
13 |
14 |   15 |   16 |
17 |
18 | ); 19 | } 20 | }); 21 | -------------------------------------------------------------------------------- /tests/test6.jsx: -------------------------------------------------------------------------------- 1 | var FindMemberPanel = React.createClass({ 2 | getInitialState: function() { 3 | return { memberResults: null }; 4 | }, 5 | render: function () { 6 | var memberResults = []; 7 | if (this.state.memberResults) { 8 | for (var i = 0; i < this.state.memberResults.length; i++) { 9 | var member = this.state.memberResults[i]; 10 | memberResults.push( 11 |
12 | 13 | 14 |
15 | ); 16 | } 17 | } 18 | return ( 19 |
20 |
21 |
Type a few characters of the member's name:
22 |
23 | 24 | 25 |
26 |
27 |
28 | {memberResults} 29 |
30 |
31 |   32 |   33 |
34 |
35 | ); 36 | } 37 | }); 38 | -------------------------------------------------------------------------------- /tests/test7.jsx: -------------------------------------------------------------------------------- 1 | var HomePage = React.createClass({ 2 | render: function () { 3 | var rows = []; 4 | for (var i = 0; i < this.props.books.length; i++) { 5 | rows.push( 6 | {this.props.books[i].Title} 7 | {this.props.books[i].CheckedOutTo ? "Checked out" : "Available"} 8 | ); 9 | } 10 | return ( 11 |
12 |

All Books

13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | {rows} 21 |
TitleStatus
22 |
23 | ); 24 | } 25 | }); 26 | -------------------------------------------------------------------------------- /tests/test8.jsx: -------------------------------------------------------------------------------- 1 | var MasterPage = React.createClass({ 2 | render: function () { 3 | return ( 4 |
5 |
6 |
Lending Library
7 |
8 |
9 |
Sign out
10 |
11 |
12 |
13 | 14 | 15 |
16 |
17 |
18 |
19 | ); 20 | } 21 | }); 22 | -------------------------------------------------------------------------------- /tests/test9.jsx: -------------------------------------------------------------------------------- 1 | var MemberPage = React.createClass({ 2 | getInitialState: function() { 3 | return {}; 4 | }, 5 | render: function () { 6 | var borrowedBooksDisplay = null; 7 | if (this.state.borrowedBooks != null) { 8 | var bookList = []; 9 | for (var i = 0; i < this.state.borrowedBooks.length; i++) { 10 | var link = '#/book?id=' + this.state.borrowedBooks[i].BookId; 11 | bookList.push(); 12 | } 13 | if (!bookList.length) { 14 | bookList =
No books borrowed
15 | } 16 | borrowedBooksDisplay = 17 |
18 |

Books borrowed

19 | {bookList} 20 |
; 21 | } 22 | else { 23 | borrowedBooksDisplay = ; 24 | } 25 | return ( 26 |
27 |

Member Details

28 |
29 | First name: 30 | {this.props.member.FirstName} 31 |
32 |
33 | Last name: 34 | {this.props.member.LastName} 35 |
36 | {borrowedBooksDisplay} 37 |
38 | ); 39 | } 40 | }); 41 | 42 | --------------------------------------------------------------------------------