├── .editorconfig ├── .gitignore ├── .nycrc ├── .travis.yml ├── LICENSE ├── README.md ├── docs ├── 2a487566599ed2dab22e.worker.js ├── 2a487566599ed2dab22e.worker.js.map ├── README.md ├── app.js ├── app.js.map ├── assets │ ├── favicon.ico │ ├── fonts │ │ ├── icons.svg │ │ ├── icons.ttf │ │ └── icons.woff │ └── ghlogo.png ├── d4e043022c81c2a9a55c.vendors.tslibs.js ├── d4e043022c81c2a9a55c.vendors.tslibs.js.map ├── index.html ├── proxy.js ├── proxy.js.map ├── run.js ├── run.js.map ├── style.css ├── style.css.map ├── ts-runtime.lib.js └── ts-runtime.lib.js.map ├── package.json ├── src ├── bin │ ├── ambient.d.ts │ ├── index.ts │ ├── process.ts │ ├── program.ts │ └── util.ts ├── bus.ts ├── context.ts ├── errors.ts ├── factory.ts ├── host.ts ├── index.ts ├── lib │ ├── ambient.d.ts │ ├── build │ │ └── webpack.config.js │ ├── index.ts │ └── tsconfig.json ├── mutators.ts ├── mutators │ ├── ArrowFunctionMutator.ts │ ├── AsExpressionMutator.ts │ ├── BinaryExpressionMutator.ts │ ├── BlockLikeMutator.ts │ ├── ClassDeclarationMutator.ts │ ├── FunctionDeclarationMutator.ts │ ├── FunctionExpressionMutator.ts │ ├── InterfaceDeclararionMutator.ts │ ├── Mutator.ts │ ├── SourceFileMutator.ts │ ├── TypeAliasDeclararionMutator.ts │ └── VariableDeclarationListMutator.ts ├── options.ts ├── playground │ ├── assets │ │ ├── favicon.ico │ │ ├── fonts │ │ │ ├── icons.svg │ │ │ ├── icons.ttf │ │ │ └── icons.woff │ │ └── ghlogo.png │ ├── build │ │ ├── config.js │ │ ├── gulpfile.js │ │ └── webpack.config.js │ ├── index.html │ ├── index.ts │ ├── proxy.js │ ├── run-console.html │ ├── run-plain.html │ ├── run.js │ ├── style.less │ ├── styles │ │ ├── grid.less │ │ ├── icons.less │ │ ├── index.less │ │ ├── loaders.less │ │ ├── main.less │ │ └── run.less │ ├── tsconfig.json │ ├── tsconfig.lib.json │ ├── types │ │ ├── globals.d.ts │ │ ├── html-module │ │ │ └── index.d.ts │ │ ├── monaco.d.ts │ │ └── worker-loader │ │ │ └── index.d.ts │ └── worker.ts ├── scanner.ts ├── transform.ts ├── types │ ├── commondir │ │ └── index.d.ts │ ├── path-browserify │ │ └── index.d.ts │ └── pretty-time │ │ └── index.d.ts └── util.ts ├── test ├── components │ ├── context.ts │ ├── factory.ts │ ├── index.test.ts │ ├── scanner.ts │ ├── transform.ts │ └── util.ts ├── known_issues │ ├── constructor_type_node.ts │ └── index.test.ts ├── mocha.opts ├── setup.ts ├── transformation │ ├── arrow_function.ts │ ├── as_expression.ts │ ├── binary_expression.ts │ ├── block_like.ts │ ├── class_declaration.ts │ ├── declaration_merging.ts │ ├── function_declaration.ts │ ├── function_expression.ts │ ├── index.test.ts │ ├── interface_declaration.ts │ ├── program_error.ts │ ├── source_file.ts │ ├── type_alias_declaration.ts │ └── variable_declaration_list.ts ├── tsconfig.json ├── types │ ├── commondir │ │ └── index.d.ts │ └── globals.d.ts └── util.ts ├── tsconfig-lkg.json ├── tsconfig.json ├── tslint.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | indent_style = space 9 | 10 | [*.{js,jsx,ts,tsx}] 11 | indent_size = 2 12 | 13 | [*.ts] 14 | max_line_length = 125 15 | 16 | [*.{json,yml}] 17 | indent_size = 2 18 | 19 | [*.md] 20 | trim_trailing_whitespace = false 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | .nyc_output 4 | .idea 5 | .DS_Store 6 | logs 7 | *.log 8 | pids 9 | *.pid 10 | *.seed 11 | .tsr 12 | coverage 13 | dist 14 | dist-tsr 15 | lkg 16 | src/play 17 | -------------------------------------------------------------------------------- /.nycrc: -------------------------------------------------------------------------------- 1 | { 2 | "reporter": [ 3 | "text", 4 | "text-summary", 5 | "html" 6 | ], 7 | "extension": [ 8 | ".ts" 9 | ], 10 | "include": [ 11 | "src/**" 12 | ], 13 | "exclude": [ 14 | "**/*.d.ts" 15 | ], 16 | "require": [ 17 | "ts-node/register" 18 | ], 19 | "sourceMap": true, 20 | "instrument": true 21 | } 22 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "10" 4 | - "12" 5 | - "14" 6 | cache: 7 | yarn: true 8 | install: 9 | - yarn 10 | script: 11 | - yarn build 12 | - yarn build:lkg 13 | - yarn cover 14 | - node lkg/bin/index 15 | after_success: 16 | - yarn coveralls 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2017 Fabian Pirklbauer 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | Playground for ts-runtime: https://fabiandev.github.io/ts-runtime/ 2 | -------------------------------------------------------------------------------- /docs/app.js: -------------------------------------------------------------------------------- 1 | !function(n){function t(t){for(var e,i,r=t[0],a=t[1],s=0,l=[];s'+t+""}(n,i),x.innerHTML+=i+"\n"}.apply(void 0,o([n.data.type,i],r))}};var u,d,p,f,m,v=V(a),y=V(s),g=document.getElementById("editor-js"),w=document.getElementById("editor-ts"),h=document.getElementById("run-code"),b=document.getElementById("run-text"),_=document.getElementById("loading"),O=document.getElementById("processing"),E=document.getElementById("options-toggle"),x=document.getElementById("console-content"),j=document.getElementById("options"),S=Array.prototype.slice.call(j.getElementsByClassName("option")).map(function(n){return n.firstElementChild});function T(){var n,t=(n=window.location.hash.substr(1))?JSON.parse(decodeURIComponent(atob(n))):{};m={tsrOptions:{force:!0,noAnnotate:!1,declarationPrefix:"_"},compilerOptions:{noImplicitAny:!1,strictNullChecks:!0,noImplicitReturns:!1,noImplicitThis:!1,removeComments:!1,experimentalDecorators:!0,emitDecoratorMetadata:!1,allowNonTsExtensions:!0,module:window.monaco.languages.typescript.ModuleKind.ES2015,target:window.monaco.languages.typescript.ScriptTarget.ES2015},windowOptions:{console:!0}},window.tsp.options=m,window.tsp.compile=k,window.tsp.emit=k,window.tsp.run=function(){return R()},window.tsp.sync=function(){I(),P()},window.tsp.setCompilerOption=function(n,t){window.tsp.options.compilerOptions[n]=t,I(),P(),k()};var e=t&&t.editor?t.editor:["interface Foo {"," prop: T;","}","","let a: Foo = {"," prop: 'bar' as any","};",""].join("\n");t&&t.options&&function n(t,e){void 0===e&&(e=window.tsp.options);for(var o in t)null!==t[o]&&"object"==typeof t[o]?n(t[o],e[o]):e[o]=t[o]}(t.options),P(),u=window.monaco.editor.create(w,{value:e,language:"typescript",automaticLayout:!0,minimap:{enabled:!1},selectionClipboard:!1}),d=window.monaco.editor.create(g,{value:[""].join("\n"),language:"javascript",readOnly:!0,automaticLayout:!0,minimap:{enabled:!1},quickSuggestions:!1,parameterHints:{enabled:!1},autoClosingBrackets:"never",suggestOnTriggerCharacters:!1,snippetSuggestions:"none",wordBasedSuggestions:!1,selectionClipboard:!1}),u.onDidChangeModelContent(i(k,400)),E.onclick=$,h.onclick=R,I(),window.onkeydown=M,k(),function(n,t,e){void 0===t&&(t=5),void 0===e&&(e=.01),n.style.opacity="1";var o=setInterval(function(){parseFloat(n.style.opacity)<.05?(clearInterval(o),n.style.opacity="0",n.style.display="none"):n.style.opacity=""+(parseFloat(n.style.opacity)-e)},t)}(_)}function I(){for(var n=S,t=0;t=t||e<0||p&&n-u>=a}function _(){var n=y();if(b(n))return O(n);c=setTimeout(_,function(n){var e=t-(n-l);return p?v(e,a-(n-u)):e}(n))}function O(n){return c=void 0,f&&i?h(n):(i=r=void 0,s)}function E(){var n=y(),e=b(n);if(i=arguments,r=this,l=n,e){if(void 0===c)return function(n){return u=n,c=setTimeout(_,t),d?h(n):s}(l);if(p)return c=setTimeout(_,t),h(l)}return void 0===c&&(c=setTimeout(_,t)),s}return t=w(t)||0,g(o)&&(d=!!o.leading,a=(p="maxWait"in o)?m(w(o.maxWait)||0,t):a,f="trailing"in o?!!o.trailing:f),E.cancel=function(){void 0!==c&&clearTimeout(c),u=0,i=l=r=c=void 0},E.flush=function(){return void 0===c?s:O(y())},E}}).call(this,e(3))},function(n,t){var e;e=function(){return this}();try{e=e||new Function("return this")()}catch(n){"object"==typeof window&&(e=window)}n.exports=e},function(n,t,e){n.exports=function(){return new Worker(e.p+"2a487566599ed2dab22e.worker.js")}},function(n,t){n.exports='\n\n\n\n \n \n ts-runtime Playground (run code)\n \n \n 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /docs/proxy.js: -------------------------------------------------------------------------------- 1 | "use strict";self.MonacoEnvironment={baseUrl:"https://unpkg.com/monaco-editor@^0.21.2/min/"},importScripts("https://unpkg.com/monaco-editor@^0.21.2/min/vs/base/worker/workerMain.js"); 2 | //# sourceMappingURL=proxy.js.map 3 | -------------------------------------------------------------------------------- /docs/proxy.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["proxy.js"],"names":["self","MonacoEnvironment","baseUrl","importScripts"],"mappings":"aAAAA,KAAKC,kBAAoB,CACvBC,QAAS,gDADcC,cAAzB","file":"proxy.js","sourcesContent":["self.MonacoEnvironment = {\n baseUrl: 'https://unpkg.com/monaco-editor@^0.21.2/min/'\n};\n\nimportScripts('https://unpkg.com/monaco-editor@^0.21.2/min/vs/base/worker/workerMain.js');\n"]} -------------------------------------------------------------------------------- /docs/run.js: -------------------------------------------------------------------------------- 1 | "use strict";function _typeof(obj){return(_typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(obj){return typeof obj}:function(obj){return obj&&"function"==typeof Symbol&&obj.constructor===Symbol&&obj!==Symbol.prototype?"symbol":typeof obj})(obj)}tsp.initialized||(tsp.getElement=function(){return document.getElementById("console-content")},tsp.clearConsole=function(){tsp.getElement().innerHTML=""},tsp.wrapConsoleText=function(type,text){return'').concat(text,"")},tsp.updateConsole=function(type,message){for(var text=tsp.logToText(message),_len=arguments.length,optionalParams=new Array(2<_len?_len-2:0),_key=2;_key<_len;_key++)optionalParams[_key-2]=arguments[_key];for(var _i=0,_optionalParams=optionalParams;_i<_optionalParams.length;_i++){var param=_optionalParams[_i];text+="\n".concat(tsp.logToText(param))}text=tsp.wrapConsoleText(type,text),tsp.getElement().innerHTML+="".concat(text,"\n")},tsp.logToText=function(message){return"object"===_typeof(message)&&null!==message?JSON.stringify(message):tsp.escape(message)},tsp.escape=function(text){var div=document.createElement("div");return div.innerText=text,div.innerHTML},tsp.log=function(data){var message=data.data.message,type=data.type,params=data.data.optionalParams;params.unshift(message),data.log&&data.log.apply(this,params),params.unshift(type),tsp.updateConsole.apply(this,params)},tsp.fadeOut=function(target){target.style.opacity="1";var fadeEffect=setInterval(function(){parseFloat(target.style.opacity)<.05?(clearInterval(fadeEffect),target.style.opacity="0",target.style.display="none"):target.style.opacity=parseFloat(target.style.opacity)-.02+""},5)},tsp.originalLog=console.log,console.log=function(message){for(var _len2=arguments.length,optionalParams=new Array(1<_len2?_len2-1:0),_key2=1;_key2<_len2;_key2++)optionalParams[_key2-1]=arguments[_key2];tsp.log({name:"log",type:"log",log:tsp.originalLog,data:{message:message,optionalParams:optionalParams}})},tsp.originalInfo=console.info,console.info=function(message){for(var _len3=arguments.length,optionalParams=new Array(1<_len3?_len3-1:0),_key3=1;_key3<_len3;_key3++)optionalParams[_key3-1]=arguments[_key3];tsp.log({name:"log",type:"info",log:tsp.originalInfo,data:{message:message,optionalParams:optionalParams}})},tsp.originalWarn=console.warn,console.warn=function(message){for(var _len4=arguments.length,optionalParams=new Array(1<_len4?_len4-1:0),_key4=1;_key4<_len4;_key4++)optionalParams[_key4-1]=arguments[_key4];tsp.log({name:"log",type:"warn",log:tsp.originalWarn,data:{message:message,optionalParams:optionalParams}})},tsp.originalError=console.error,console.error=function(message){for(var _len5=arguments.length,optionalParams=new Array(1<_len5?_len5-1:0),_key5=1;_key5<_len5;_key5++)optionalParams[_key5-1]=arguments[_key5];tsp.log({name:"log",type:"error",log:tsp.originalError,data:{message:message,optionalParams:optionalParams}})},tsp.initialized=!0),window.onerror=function(message,url,lineNumber){tsp.log({name:"error",type:"error",data:{message:message,optionalParams:[]}})}; 2 | //# sourceMappingURL=run.js.map 3 | -------------------------------------------------------------------------------- /docs/run.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["run.js"],"names":["tsp","initialized","getElement","document","getElementById","clearConsole","type","text","wrapConsoleText","logToText","message","_len","arguments","length","optionalParams","Array","_key","_i","_optionalParams","param","updateConsole","innerHTML","concat","JSON","stringify","div","createElement","innerText","log","data","escape","params","apply","this","unshift","target","style","opacity","fadeEffect","setInterval","parseFloat","clearInterval","display","_len2","_key2","name","originalLog","console","info","_len3","_key3","originalInfo","originalWarn","warn","_len4","_key4","error","originalError","url","lineNumber"],"mappings":"gRAAKA,IAAIC,cACPD,IAAIE,WAAa,WACf,OAAOC,SAASC,eAAe,oBADjCJ,IAAIE,aAAa,WACfF,IAAAE,aAAgBE,UAAAA,IAGlBJ,IAAIK,gBAAe,SAAwBC,KAAAC,MACzCP,MAAAA,oBAAAA,OAAAM,KAAAN,wBAAAA,OAAAM,KAAAN,aAAAA,OAAAO,KAAAP,YAGFA,IAAIQ,cAAJ,SAA+BA,KAAgBF,SAAY,IACzD,IAAAC,KAAAP,IAAAS,UAAAC,SADyDC,KAAAC,UAAAC,OAAAC,eAAA,IAAAC,MAAA,EAAAJ,KAAAA,KAAA,EAAA,GAAAK,KAAA,EAAAA,KAAAL,KAAAK,OAAAF,eAAAE,KAAA,GAAAJ,UAAAI,MAOzD,IAAA,IAAAC,GAAA,EAAAC,gBAAkBJ,eAAlBG,GAAAC,gBAAAL,OAAAI,KAAkC,CAA7B,IAAIE,MAAKD,gBAAAD,IAHZG,MAAAA,KAAAA,OAAJpB,IAAoBS,UAASW,QAAgDb,KAAAP,IAAAQ,gBAAAF,KAAAC,MAAAP,IAAAE,aAAAmB,WAAA,GAAAC,OAAAf,KAAA,OAGtEP,IAAAS,UAAS,SAATC,SACHH,MAAaP,WAAbO,QAAIG,UAAJ,OAAaA,QACda,KAAAC,UAAAd,SAIGR,IAAAA,OAAamB,UAGnBrB,IAAIS,OAAJ,SAAyBA,MACvB,IAAIgB,IAAOf,SAAPgB,cAAA,OAEH,OADCD,IAAAE,UAAYH,KACbC,IAAAJ,WAGFrB,IAND4B,IAAA,SAAAC,MAeE,IAAMnB,QAAUmB,KAAKA,KAAKnB,QAPxBoB,KAASD,KAAAvB,KAEPqB,OADiBD,KAAAA,KAAcZ,eAEnCiB,OAAON,QAAIJ,SAHbQ,KAAAD,KAaIC,KAAKD,IAAII,MAAMC,KAAMF,QANvBA,OAAMrB,QAAUmB,MAChB7B,IAAMM,cAAYA,MAAlB2B,KAAAF,SAGAA,IAAAA,QAAOG,SAAPC,QASAA,OAAOC,MAAMC,QAAU,IAPrBR,IAAIS,WAAWC,YAAf,WACDC,WAAAL,OAAAC,MAAAC,SAAA,KAUGI,cAAcH,YATlBP,OAAOG,MAAQ5B,QAAf,IACIc,OAAAA,MAAJsB,QAAwB,QAYpBP,OAAOC,MAAMC,QAAWG,WAAWL,OAAOC,MAAMC,SAAW,IAAQ,IARvEF,IAIIM,IAAAA,YAAAA,QAAcH,IACdH,QAAAA,IAAOC,SAAP1B,SAAA,IAAA,IAAAiC,MAAA/B,UAAAC,OAAAC,eAAA,IAAAC,MAAA,EAAA4B,MAAAA,MAAA,EAAA,GAAAC,MAAA,EAAAA,MAAAD,MAAAC,QAAA9B,eAAA8B,MAAA,GAAAhC,UAAAgC,OACAT,IAAAA,IAAAA,CACDU,KAJD,MAKEV,KAAAA,MACDP,IAAA5B,IAAA8C,YAP2BjB,KAA9B,CAHFnB,QAAAA,QAsBMI,eAAAA,mBAPiCA,IAAAA,aAAgBiC,QAAAC,KAAAD,QAAAC,KAAA,SAAAtC,SAAA,IAAA,IAAAuC,MAAArC,UAAAC,OAAAC,eAAA,IAAAC,MAAA,EAAAkC,MAAAA,MAAA,EAAA,GAAAC,MAAA,EAAAA,MAAAD,MAAAC,QAAApC,eAAAoC,MAAA,GAAAtC,UAAAsC,OAcrDlD,IAAI4B,IAAI,CAbR5B,KAAQ,MACN6C,KAAM,OACNvC,IAAIN,IAAEmD,aACNvB,KAAK5B,CACL6B,QAAAA,QACEnB,eAAAA,mBAmBNV,IAAIoD,aAAeL,QAAQM,KAb3BrD,QAAImD,KAAAA,SAAuBH,SAA3B,IAAA,IAAAM,MAAA1C,UAAAC,OAAAC,eAAA,IAAAC,MAAA,EAAAuC,MAAAA,MAAA,EAAA,GAAAC,MAAA,EAAAA,MAAAD,MAAAC,QAAAzC,eAAAyC,MAAA,GAAA3C,UAAA2C,OAeEvD,IAAI4B,IAAI,CAdVmB,KAAQC,MAAiD1C,KAAA,OAAhBQ,IAAAA,IAAAA,aAAgBe,KAAA,CAmBnDnB,QAAAA,QAlBAkB,eAAAA,mBAKAlB,IAAAA,cAAAA,QADI8C,MAEJ1C,QAAAA,MAAAA,SAAAA,SAAAA,IAAAA,IAAAA,MAAAA,UAAAA,OAAAA,eAAAA,IAAAA,MAAAA,EAAAA,MAAAA,MAAAA,EAAAA,GAAAA,MAAAA,EAAAA,MAAAA,MAAAA,QAAAA,eAAAA,MAAAA,GAAAA,UAAAA,OAFId,IAAA4B,IAAA,CAJRiB,KAAA,MADFvC,KAAA,QA8BIsB,IAAK5B,IAAIyD,cAlBTL,KAAAA,CAoBE1C,QAAAA,QAnBNqC,eAAAA,mBACE/C,IAAAA,aAAQ,GAGN4B,OAAAA,QAASwB,SAAAA,QAHHM,IAAAC,YAIN9B,IAAAA,IAAI,CACFnB,KAAAA,QACAI,KAAAA,QANIe,KAAR,CADFnB,QAAAA,QAmCII,eAAgB","file":"run.js","sourcesContent":["if (!tsp.initialized) {\n tsp.getElement = function getElement() {\n return document.getElementById('console-content');\n }\n\n tsp.clearConsole = function clearConsole() {\n tsp.getElement().innerHTML = '';\n }\n\n tsp.wrapConsoleText = function wrapConsoleText(type, text) {\n return `${text}`;\n }\n\n tsp.updateConsole = function updateConsole(type, message, ...optionalParams) {\n let text = tsp.logToText(message);\n\n for (let param of optionalParams) {\n text += `\\n${tsp.logToText(param)}`;\n }\n\n text = tsp.wrapConsoleText(type, text);\n\n tsp.getElement().innerHTML += `${text}\\n`;\n }\n\n tsp.logToText = function logToText(message) {\n if (typeof message === 'object' && message !== null) {\n return JSON.stringify(message);\n }\n\n return tsp.escape(message);\n }\n\n tsp.escape = function escape(text) {\n const div = document.createElement('div');\n div.innerText = text;\n return div.innerHTML;\n }\n\n tsp.log = function log(data) {\n const message = data.data.message;\n const type = data.type;\n const optionalParams = data.data.optionalParams;\n const params = optionalParams;\n params.unshift(message);\n if (data.log) {\n data.log.apply(this, params);\n }\n params.unshift(type);\n tsp.updateConsole.apply(this, params);\n }\n\n tsp.fadeOut = function fadeOut(target) {\n target.style.opacity = '1';\n\n const fadeEffect = setInterval(() => {\n if (parseFloat(target.style.opacity) < 0.05) {\n clearInterval(fadeEffect);\n target.style.opacity = '0';\n target.style.display = 'none';\n } else {\n target.style.opacity = (parseFloat(target.style.opacity) - 0.02) + '';\n }\n }, 5);\n }\n\n tsp.originalLog = console.log;\n console.log = function log(message, ...optionalParams) {\n tsp.log({\n name: 'log',\n type: 'log',\n log: tsp.originalLog,\n data: {\n message,\n optionalParams\n }\n });\n }\n\n tsp.originalInfo = console.info;\n console.info = function info(message, ...optionalParams) {\n tsp.log({\n name: 'log',\n type: 'info',\n log: tsp.originalInfo,\n data: {\n message,\n optionalParams\n }\n });\n }\n\n tsp.originalWarn = console.warn;\n console.warn = function warn(message, ...optionalParams) {\n tsp.log({\n name: 'log',\n type: 'warn',\n log: tsp.originalWarn,\n data: {\n message,\n optionalParams\n }\n });\n }\n\n tsp.originalError = console.error;\n console.error = function error(message, ...optionalParams) {\n tsp.log({\n name: 'log',\n type: 'error',\n log: tsp.originalError,\n data: {\n message,\n optionalParams\n }\n });\n }\n\n tsp.initialized = true;\n}\n\nwindow.onerror = function(message, url, lineNumber) {\n tsp.log({\n name: 'error',\n type: 'error',\n // log: __originalError,\n data: {\n message,\n optionalParams: []\n }\n });\n}\n"]} -------------------------------------------------------------------------------- /docs/style.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}.grid{margin:0}.grid [class*=col-]{position:relative;float:left;padding-right:20px}.grid [class*=col-]:last-of-type{padding-right:0}.grid .col-1-1{width:100%}.grid .col-1-2{width:50%}.grid .col-1-3{width:33.33%}.grid .col-2-3{width:66.66%}.grid .col-1-4{width:25%}.grid .col-1-8{width:12.5%}.grid:after{content:"";display:table;clear:both}.grid-pad{padding:20px 0 20px 20px}.grid-pad>[class*=col-]:last-of-type{padding-right:20px}@font-face{font-family:icons;src:url(assets/fonts/icons.ttf?ywd0ns) format('truetype'),url(assets/fonts/icons.woff?ywd0ns) format('woff'),url(assets/fonts/icons.svg?ywd0ns#icons) format('svg');font-weight:400;font-style:normal}[class*=" icon-"],[class^=icon-]{font-family:icons!important;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.icon-info:before{content:"\e900"}.icon-warn:before{content:"\e901"}.icon-log:before{content:"\e902"}.icon-error:before{content:"\e903"}.icon-cog:before{content:"\e904"}.loader{position:absolute;top:50%;left:50%;display:inline-block;width:30px;height:30px;border:4px solid #fff;animation:loader 2s infinite ease}.loader .loader-inner{vertical-align:top;display:inline-block;width:100%;background-color:#fff;animation:loader-inner 2s infinite ease-in}@keyframes loader{0%{transform:rotate(0)}25%{transform:rotate(180deg)}50%{transform:rotate(180deg)}75%{transform:rotate(360deg)}100%{transform:rotate(360deg)}}@keyframes loader-inner{0%{height:0}25%{height:0}50%{height:100%}75%{height:100%}100%{height:0}}.spinner{width:30px;height:30px;position:absolute;top:50%;left:50%;margin-left:-15px;margin-top:-15px}.spinner .double-bounce1,.spinner .double-bounce2{width:100%;height:100%;border-radius:50%;background-color:#efefef;opacity:.6;position:absolute;top:0;left:0;-webkit-animation:spinner 2s infinite ease-in-out;animation:spinner 2s infinite ease-in-out}.spinner .double-bounce2{-webkit-animation-delay:-1s;animation-delay:-1s}@-webkit-keyframes spinner{0%,100%{-webkit-transform:scale(0)}50%{-webkit-transform:scale(1)}}@keyframes spinner{0%,100%{transform:scale(0);-webkit-transform:scale(0)}50%{transform:scale(1);-webkit-transform:scale(1)}}*,:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-family:sans-serif}body,html{height:100%}body{padding:20px}a{color:#333;text-decoration:none}#loading{z-index:1000;position:absolute;top:0;right:0;bottom:0;left:0;background-color:rgba(0,0,0,.8)}#header{padding-top:0;padding-bottom:0}#header #title{text-align:left;font-size:15px}#header #title a{color:#000}#header #title .icon-cog{cursor:pointer;vertical-align:middle;display:inline-block;margin-left:5px;padding-bottom:3px;color:#333}#header #title .icon-cog.active{color:#000}#header #options{position:absolute;display:none;margin-left:-18px;top:25px;padding:10px 20px;z-index:1000;background-color:#fff;border:#c3c3c3 1px solid}#header #options .info{font-size:12px}#header #options .info,#header #options .option{padding:5px 0}#header #options.visible{display:inline-block}#header #options h6{padding:0;border-bottom:1px solid;padding-bottom:2px;margin-top:10px;margin-bottom:10px}#header #options input[type=text]{outline:0;width:50px;height:19px}#header #github{text-align:right;font-size:13px}#header #ghlogo{vertical-align:bottom;display:inline-block;padding-bottom:1px;height:16px}#playground{height:70%;padding-bottom:0}#playground .editor-container{height:100%}#playground .editor-container .editor{height:100%;border:1px solid #eee}#playground .editor-container .editor-footer{display:inline-block;position:absolute;z-index:10;left:1px;bottom:1px;text-align:center;right:21px;height:25px;line-height:1.7;font-size:15px}#playground #run-code{cursor:pointer;background-color:#f1f1f1;color:#333}#playground #processing{display:none;position:absolute;z-index:500;top:1px;right:15px;bottom:1px;left:1px}#playground #processing .spinner{width:15px;height:15px;position:absolute;top:22px;left:22px}#console{padding-bottom:0}#console #console-heading{border:1px solid #eee;padding-top:10px;padding-bottom:10px;padding-left:20px;border-bottom:none;font-size:15px}#console #console-content{border:1px solid #eee;padding-bottom:20px}#console #console-content .log-log{color:#333}#console #console-content .log-info{color:#2153a4}#console #console-content .log-warn{color:#f0ad4e}#console #console-content .log-error{color:#d9534f}#console #console-content .log-error{color:#d9534f}#console .console{min-height:100px;max-height:200px;overflow-y:scroll;overflow-x:auto;white-space:pre;font-family:Menlo,Monaco,Consolas,'Liberation Mono','Courier New',Courier,monospace;line-height:1.7;font-size:13px;padding-left:40px;padding-top:20px}#console .console [class*=" icon-"],#console .console [class^=icon-]{display:inline-block;margin-right:10px;vertical-align:middle;padding-bottom:1px;width:1em}#run-window #loading-window{position:absolute;z-index:500;top:0;right:0;bottom:0;left:0}#run-window #loading-window .spinner{width:30px;height:30px;position:absolute;top:50%;left:50%;margin-left:-15px;margin-top:-15px}#run-window #loading-window .spinner .double-bounce1,#run-window #loading-window .spinner .double-bounce2{background-color:#e4e2e2}#run-window #console{height:100%}#run-window #console [class*=col-]:last-of-type{height:85%}#run-window #console .console{min-height:300px;max-height:none;height:100%} 2 | /*# sourceMappingURL=style.css.map */ 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ts-runtime", 3 | "version": "0.3.0", 4 | "description": "Runtime Type Checks for TypeScript", 5 | "main": "index.js", 6 | "bin": { 7 | "tsr": "bin/index.js" 8 | }, 9 | "repository": "https://github.com/fabiandev/ts-runtime.git", 10 | "author": "Fabian Leutgeb ", 11 | "license": "MIT", 12 | "engines": { 13 | "node": ">=10" 14 | }, 15 | "scripts": { 16 | "test": "NODE_ENV=test TS_NODE_CACHE=false ./node_modules/mocha/bin/_mocha", 17 | "cover": "NODE_ENV=test TS_NODE_CACHE=false ./node_modules/.bin/nyc npm test", 18 | "ci": "npm run build && npm run build:lkg && node lkg/bin/index && node lkg/bin/index && npm run cover", 19 | "coveralls": "./node_modules/.bin/nyc report --reporter=text-lcov | ./node_modules/.bin/coveralls", 20 | "build": "npm run build:tsr && npm run build:lib", 21 | "build:dist": "npm run build:tsr && npm run build:lib && npm run build:playground", 22 | "build:tsr": "./node_modules/.bin/tsc --rootDir src --outDir dist --module CommonJS --target es5 --skipLibCheck && cp package.json dist/package.json && cp README.md dist/README.md && cp LICENSE dist/LICENSE", 23 | "build:lib": "node_modules/.bin/webpack --config src/lib/build/webpack.config.js", 24 | "build:playground": "node_modules/.bin/gulp --gulpfile src/playground/build/gulpfile.js build", 25 | "build:lkg": "ts-node --no-cache ./src/bin/index src/index src/lib/index src/bin/index src/bin/process --libDeclarations --moduleAlias -c tsconfig-lkg.json --stackTraceOutput 50" 26 | }, 27 | "_moduleAliases": { 28 | "ts-runtime": "dist" 29 | }, 30 | "dependencies": { 31 | "chalk": "^4.1.0", 32 | "commander": "^6.2.0", 33 | "commondir": "^1.0.1", 34 | "flow-runtime": "^0.17.0", 35 | "ora": "^5.1.0", 36 | "pretty-time": "^1.1.0", 37 | "regenerator-runtime": "^0.13.7", 38 | "rimraf": "^3.0.2", 39 | "typescript": "^4.0.3" 40 | }, 41 | "devDependencies": { 42 | "@babel/core": "^7.12.3", 43 | "@babel/preset-env": "^7.12.1", 44 | "@types/expect.js": "^0.3.29", 45 | "@types/lodash.debounce": "^4.0.2", 46 | "@types/mocha": "^5.2.5", 47 | "@types/node": "^10.9.4", 48 | "@types/ora": "^1.3.4", 49 | "@types/rimraf": "^2.0.2", 50 | "@types/webpack-env": "^1.13.6", 51 | "coveralls": "^3.0.0", 52 | "css-loader": "^1.0.0", 53 | "del": "^3.0.0", 54 | "expect.js": "^0.3.1", 55 | "gulp": "^4.0.0", 56 | "gulp-babel": "^8.0.0", 57 | "gulp-clean-css": "^4.3.0", 58 | "gulp-less": "^4.0.1", 59 | "gulp-preprocess": "^3.0.3", 60 | "gulp-rename": "^2.0.0", 61 | "gulp-sourcemaps": "^2.6.0", 62 | "gulp-uglify": "^3.0.0", 63 | "lodash.debounce": "^4.0.8", 64 | "mocha": "^5.0.0", 65 | "module-alias": "^2.0.0", 66 | "monaco-editor": "^0.21.2", 67 | "monaco-typescript": "Microsoft/monaco-typescript", 68 | "normalize.css": "^8.0.0", 69 | "null-loader": "^0.1.1", 70 | "nyc": "^13.0.1", 71 | "raw-loader": "^0.5.1", 72 | "source-map-loader": "^0.2.1", 73 | "source-map-support": "^0.5.3", 74 | "ts-loader": "^5.0.0", 75 | "ts-node": "^6.0.0", 76 | "uglify-js": "^3.0.28", 77 | "uglifyjs-webpack-plugin": "^2.1.2", 78 | "webpack": "^4.17.2", 79 | "webpack-cli": "^3.1.0", 80 | "worker-loader": "^2.0.0" 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/bin/ambient.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'find-up'; 2 | declare module 'log-symbols'; 3 | declare module 'ora'; 4 | -------------------------------------------------------------------------------- /src/bin/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import * as path from 'path'; 4 | import * as ts from 'typescript'; 5 | import * as commander from 'commander'; 6 | import * as util from './util'; 7 | import * as program from './program'; 8 | import { transform } from '../transform'; 9 | import { ProgramError } from '../errors'; 10 | import { Options, defaultOptions } from '../options'; 11 | 12 | const pkgDir = ts.sys.fileExists(path.join(__dirname, '../package.json')) ? 13 | path.join(__dirname, '../package.json') : path.join(__dirname, '../../package.json'); 14 | 15 | const pkg = require(pkgDir); 16 | const options: Options = Object.assign({}, defaultOptions); 17 | 18 | let compilerOptions: string = '{}'; 19 | let parsedCompilerOptions: any; 20 | let tsConfigPath: string; 21 | let fastMode = false; 22 | 23 | function defaultAction() { 24 | if (fastMode) { 25 | options.log = true; 26 | } else { 27 | program.start(options, pkg.version); 28 | options.log = false; 29 | } 30 | 31 | const files: string[] = commander.args 32 | .filter(arg => typeof arg === 'string'); 33 | 34 | if (files.length === 0) { 35 | throw new ProgramError('No entry file(s) passed to transform.'); 36 | } 37 | 38 | try { 39 | parsedCompilerOptions = JSON.parse(compilerOptions); 40 | } catch (e) { 41 | throw new ProgramError(`Could not parse compiler configuration.`); 42 | } 43 | 44 | if (tsConfigPath) { 45 | if (!ts.sys.fileExists(tsConfigPath)) { 46 | throw new ProgramError(`Could not load configuration from ${tsConfigPath}.`); 47 | } 48 | 49 | const resolvedTsConfigPath = path.resolve(tsConfigPath); 50 | const tsConfig = require(resolvedTsConfigPath); 51 | 52 | if (tsConfig.hasOwnProperty('compilerOptions')) { 53 | parsedCompilerOptions = tsConfig.compilerOptions; 54 | } else { 55 | if (options.log) { 56 | console.warn(`No compiler options found in ${tsConfigPath}, used defaults.`); 57 | } else { 58 | program.status.warn(`No compiler options found in ${tsConfigPath}, used defaults.`); 59 | } 60 | parsedCompilerOptions = {}; 61 | } 62 | } 63 | 64 | const opts = ts.convertCompilerOptionsFromJson(parsedCompilerOptions, '.'); 65 | 66 | options.compilerOptions = opts.options; 67 | 68 | if (opts.errors.length > 0) { 69 | const formattedDiagnostics = util.formatDiagnostics(opts.errors); 70 | 71 | if (options.log) { 72 | for (let diagnostic of formattedDiagnostics) { 73 | console.error(diagnostic); 74 | } 75 | 76 | process.exit(1); 77 | } 78 | 79 | program.status.diagnostics(util.formatDiagnostics(opts.errors)); 80 | program.status.error(); 81 | return; 82 | } 83 | 84 | if (fastMode) { 85 | transform(files, options); 86 | } else { 87 | program.transform(files); 88 | } 89 | } 90 | 91 | function useTsConfig(path: string) { 92 | tsConfigPath = path; 93 | } 94 | 95 | function setNoAnnotate() { 96 | options.noAnnotate = true; 97 | } 98 | 99 | function setCompilerOptions(opts: string) { 100 | compilerOptions = opts; 101 | } 102 | 103 | function setDeclarationFileName(fileName: string) { 104 | const ext = path.extname(fileName); 105 | 106 | if (ext) { 107 | fileName = fileName.slice(0, ext.length * -1); 108 | } 109 | 110 | if (path.basename(fileName) !== fileName) { 111 | throw new ProgramError('Declaration file name must not be a path.'); 112 | } 113 | 114 | options.declarationFileName = fileName; 115 | } 116 | 117 | function setExcludeDeclarationFile() { 118 | options.excludeDeclarationFile = true; 119 | } 120 | 121 | function setExcludeLib() { 122 | options.excludeLib = true; 123 | } 124 | 125 | function setForce() { 126 | options.force = true; 127 | } 128 | 129 | function setFast() { 130 | fastMode = true; 131 | } 132 | 133 | // function setKeepTemp() { 134 | // options.keepTemp = true; 135 | // } 136 | 137 | function setLibIdentifier(identifier: string) { 138 | options.libIdentifier = identifier; 139 | } 140 | 141 | function setLibDeclarations() { 142 | options.libDeclarations = true; 143 | } 144 | 145 | function setLibNamespace(namespace: string) { 146 | options.libNamespace = namespace; 147 | } 148 | 149 | function setModuleAlias() { 150 | options.moduleAlias = true; 151 | } 152 | 153 | function setDeclarationPrefix(prefix: string) { 154 | options.declarationPrefix = prefix; 155 | } 156 | 157 | function setStackTraceOutput(limit: string) { 158 | options.stackTraceOutput = parseInt(limit); 159 | } 160 | 161 | // function setTempFolderName(name: string) { 162 | // options.tempFolderName = name; 163 | // } 164 | 165 | commander 166 | .version(pkg.version, '-v, --version') 167 | .description(`--------- ts-runtime --------- 168 | Turn TypeScript type annotations 169 | --> into runtime type checks <-- 170 | --------------------------------`) 171 | .usage(' [options]') 172 | .option('-a, --noAnnotate', 'do not annotate classes and functions', setNoAnnotate) 173 | .option('-c, --tsConfig ', 'use the compiler options of the given tsconfig.json', useTsConfig) 174 | .option('-C, --compilerOptions ', 'set TypeScript compiler options. defaults to "{}"', setCompilerOptions) 175 | .option('-d, --declarationFileName ', 'set file name for global declarations. defaults to "tsr-declarations"', setDeclarationFileName) 176 | .option('-e, --excludeDeclarationFile', 'do not import the ambient declarations file. defaults to false', setExcludeDeclarationFile) 177 | .option('-E, --excludeLib', 'do not automatically import the runtime library. defaults to false', setExcludeLib) 178 | .option('-f, --force', 'try to finish on TypeScript compiler error. defaults to false', setForce) 179 | .option('-F, --fast', 'no status for the command line, but faster processing. defaults to false', setFast) 180 | // .option('-k, --keepTemp', 'keep temporary files. defaults to false', setKeepTemp) 181 | .option('-l, --libIdentifier ', 'lib import name. defaults to "t"', setLibIdentifier) 182 | .option('-L, --libDeclarations', 'reflect declarations from global libs (e.g. DOM). defaults to false', setLibDeclarations) 183 | .option('-m, --moduleAlias', 'import package module-alias on top of every file.', setModuleAlias) 184 | .option('-n, --libNamespace ', 'prefix for lib and code additions. defaults to ""', setLibNamespace) 185 | .option('-p, --declarationPrefix ', 'prefix for added variables. defaults to "_"', setDeclarationPrefix) 186 | .option('-s, --stackTraceOutput ', 'output a specified number of lines of the stack trace. defaults to 3', setStackTraceOutput) 187 | // .option('-t, --tempFolderName ', 'set folder name for temporary files. defaults to ".tsr"', setTempFolderName) 188 | .on('--help', () => { 189 | console.log(); 190 | console.log(' Examples:'); 191 | console.log(); 192 | console.log(' $ tsr entry.ts --force'); 193 | console.log(' $ tsr src/entry1 bin/entry2 lib/entry3'); 194 | console.log(' $ tsr entry.ts -c tsconfig.json'); 195 | console.log(); 196 | }); 197 | 198 | commander.parse(process.argv); 199 | 200 | if (!process.argv.slice(2).length) { 201 | commander.outputHelp(); 202 | process.exit(); 203 | } else { 204 | defaultAction(); 205 | } 206 | -------------------------------------------------------------------------------- /src/bin/process.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as bus from '../bus'; 3 | import * as util from './util'; 4 | import { Options } from '../options'; 5 | import { transform } from '../transform'; 6 | 7 | let options: Options; 8 | let started = false; 9 | 10 | function handleError(error: string | Error) { 11 | process.send({ message: 'error', payload: util.getError(error, options) }); 12 | } 13 | 14 | process.on('message', (data: { message: string, payload: any[] }) => { 15 | options = data.payload[1]; 16 | 17 | if (data.message === 'startTransformation' && !started) { 18 | started = true; 19 | transform(data.payload[0], data.payload[1]); 20 | } 21 | }); 22 | 23 | process.on('uncaughtException', handleError); 24 | process.on('unhandledRejection', handleError); 25 | 26 | bus.on(bus.events.ERROR, handleError); 27 | 28 | bus.on(bus.events.WARN, (args: any[]) => { 29 | process.send({ message: 'warn', payload: args }); 30 | }); 31 | 32 | bus.on(bus.events.START, (args: any[]) => { 33 | process.send({ message: 'start', payload: args }); 34 | }); 35 | 36 | bus.on(bus.events.SCAN, (args: any[]) => { 37 | process.send({ message: 'scan', payload: args }); 38 | }); 39 | 40 | bus.on(bus.events.TRANSFORM, (args: any[]) => { 41 | const sourceFiles = args[0] as ts.SourceFile[]; 42 | const time = args[1]; 43 | const fileNames = sourceFiles.map(sf => sf.fileName); 44 | 45 | process.send({ message: 'transform', payload: [fileNames, time] }); 46 | }); 47 | 48 | bus.on(bus.events.EMIT, (args: any[]) => { 49 | process.send({ message: 'emit', payload: args }); 50 | }); 51 | 52 | bus.on(bus.events.DIAGNOSTICS, (args: any[]) => { 53 | const diagnostics = args[0] as ts.Diagnostic[]; 54 | let formatted = util.formatDiagnostics(diagnostics); 55 | 56 | let i, j, temp, chunk = 10; 57 | for (i = 0, j = formatted.length; i < j; i += chunk) { 58 | temp = formatted.slice(i, i + chunk); 59 | process.send({ message: 'diagnostics', payload: [temp] }); 60 | } 61 | }); 62 | 63 | bus.on(bus.events.CLEAN, (args: any[]) => { 64 | process.send({ message: 'cleanup', payload: args }); 65 | }); 66 | 67 | bus.on(bus.events.STOP, (args: any[]) => { 68 | process.send({ message: 'stop', payload: args }); 69 | }); 70 | 71 | bus.on(bus.events.END, (args: any[]) => { 72 | process.send({ message: 'end', payload: args }); 73 | }); 74 | -------------------------------------------------------------------------------- /src/bin/program.ts: -------------------------------------------------------------------------------- 1 | import * as cp from 'child_process'; 2 | import * as path from 'path'; 3 | import * as ora from 'ora'; 4 | import * as chalk from 'chalk'; 5 | import * as util from './util'; 6 | import { Options } from '../options'; 7 | 8 | export interface ProgramStatus { 9 | [index: string]: any; 10 | init: () => void; 11 | start: () => void; 12 | transform: (fileNames: string[], time: string) => void; 13 | scan: (time: string) => void; 14 | cleanup: (time: string) => void; 15 | diagnostics: (diags: string[], total?: number) => void; 16 | end: (time: string, totalTime: string) => void; 17 | warn: (warning: string, defer?: boolean) => void; 18 | stop: () => void; 19 | term: () => void; 20 | error: (error?: string | Error) => void; 21 | } 22 | 23 | let child: cp.ChildProcess; 24 | let started = false; 25 | let pkgVersion: string; 26 | let spinner: any = ora(); 27 | let current = 'Processing'; 28 | let currentPast = 'Processed'; 29 | let hasErrors = false; 30 | let numDiagnostics = 0; 31 | let warnings: string[] = []; 32 | let options: Options; 33 | 34 | export function start(opts: Options, version: string) { 35 | pkgVersion = version; 36 | current = 'Processing'; 37 | currentPast = 'Processed'; 38 | hasErrors = false; 39 | numDiagnostics = 0; 40 | warnings = []; 41 | options = opts; 42 | status.init(); 43 | status.start(); 44 | } 45 | 46 | export function transform(entryFiles: string[]) { 47 | if (child) { 48 | return; 49 | } 50 | 51 | child = cp.fork(path.join(__dirname, './process')); 52 | started = true; 53 | 54 | child.on('exit', code => { 55 | status.term(); 56 | child.stdin.end(); 57 | child.kill(); 58 | process.exit(code); 59 | }); 60 | 61 | child.on('message', (data: { message: string, payload: any }) => { 62 | if (typeof status[data.message] === 'function') { 63 | if (Array.isArray(data.payload)) { 64 | status[data.message](...data.payload); 65 | } else { 66 | status[data.message](data.payload); 67 | } 68 | } 69 | }); 70 | 71 | child.send({ message: 'startTransformation', payload: [entryFiles, options] }); 72 | } 73 | 74 | export const status: ProgramStatus = { 75 | init: () => { 76 | spinner = null; 77 | spinner = ora(); 78 | }, 79 | 80 | start: () => { 81 | current = 'Processing'; 82 | currentPast = 'Processed'; 83 | if (!started) spinner.info(chalk.bold(`ts-runtime v${pkgVersion}`)); 84 | spinner.text = current; 85 | spinner.start(); 86 | }, 87 | 88 | transform: (fileNames: string[], time: string) => { 89 | spinner.succeed(`${current} (${time})`); 90 | current = 'Transforming'; 91 | currentPast = 'Transformed'; 92 | spinner.text = chalk.bold(current); 93 | spinner.start(); 94 | }, 95 | 96 | emit: (time: string) => { 97 | spinner.succeed(`${current} (${time})`); 98 | current = 'Emitting'; 99 | currentPast = 'Emitted'; 100 | spinner.text = chalk.bold(current); 101 | spinner.start(); 102 | }, 103 | 104 | scan: (time: string) => { 105 | spinner.succeed(`${current} (${time})`); 106 | current = 'Scanning'; 107 | currentPast = 'Scanned'; 108 | spinner.text = chalk.bold(current); 109 | spinner.start(); 110 | }, 111 | 112 | cleanup: (time: string) => { 113 | spinner.succeed(`${current} (${time})`); 114 | current = 'Cleaning'; 115 | currentPast = 'Cleaned'; 116 | spinner.text = chalk.bold(current); 117 | spinner.start(); 118 | }, 119 | 120 | diagnostics: (diags: string[], total?: number) => { 121 | total = total || diags.length; 122 | numDiagnostics += diags.length; 123 | 124 | for (let diag of diags) { 125 | spinner.fail(diag); 126 | } 127 | 128 | if (total > diags.length) { 129 | spinner.fail(chalk.bold(` -> ${total - diags.length} diagnostics have been hidden.`)); 130 | } 131 | 132 | spinner.start(); 133 | }, 134 | 135 | end: (time: string, totalTime: string) => { 136 | spinner.succeed(`${current} (${time})`); 137 | 138 | for (let warning of warnings) { 139 | spinner.warn(chalk.yellow(warning)); 140 | } 141 | 142 | if (hasErrors) { 143 | spinner.fail(chalk.red.bold(`Done in ${totalTime}, but there were errors.`)); 144 | } else if (numDiagnostics > 0) { 145 | let wasWere = numDiagnostics === 1 ? 'was' : 'were'; 146 | let diagPlural = numDiagnostics === 1 ? 'diagnostic' : 'diagnostics'; 147 | let text = `Done in ${totalTime}, but there ${wasWere} ${numDiagnostics} compiler ${diagPlural}`; 148 | if (warnings.length > 0) { 149 | let warningPlural = warnings.length === 1 ? 'warning' : 'warnings'; 150 | text += ` and ${warnings.length} ${warningPlural}` 151 | } 152 | spinner.succeed(chalk.yellow.bold(`${text}.`)); 153 | } else if (warnings.length > 0) { 154 | let wasWere = warnings.length === 1 ? 'was' : 'were'; 155 | let warningPlural = warnings.length === 1 ? 'warning' : 'warnings'; 156 | spinner.succeed(chalk.yellow.bold(`Done in ${totalTime}, but there ${wasWere} ${warnings.length} ${warningPlural}.`)); 157 | } else { 158 | spinner.succeed(chalk.green.bold(`Done in ${totalTime}.`)); 159 | } 160 | 161 | process.exit(0); 162 | }, 163 | 164 | warn: (warning: string, defer = true) => { 165 | if (warnings.indexOf(warning) === -1) { 166 | warnings.push(warning); 167 | } else 168 | 169 | if(!defer) { 170 | spinner.warn(warning); 171 | } 172 | }, 173 | 174 | stop: () => { 175 | hasErrors = true; 176 | status.error(); 177 | }, 178 | 179 | term: () => { 180 | hasErrors = true; 181 | 182 | if (started) { 183 | spinner.fail(chalk.red.bold(`${current} was interrupted.`)); 184 | } 185 | }, 186 | 187 | error: (error?: string | Error) => { 188 | hasErrors = true; 189 | 190 | let err = util.getError(error); 191 | 192 | if (err) { 193 | spinner.fail(err); 194 | } 195 | 196 | status.term(); 197 | process.exit(1); 198 | }, 199 | } 200 | 201 | process.on('uncaughtException', (error: Error) => { 202 | status.error(error); 203 | }); 204 | 205 | process.on('unhandledRejection', (reason: any, p: any) => { 206 | status.error(new Error(reason)); 207 | }); 208 | 209 | process.on('SIGINT', () => { 210 | status.term(); 211 | process.exit(0); 212 | }); 213 | 214 | process.on('SIGTERM', () => { 215 | status.term(); 216 | process.exit(0); 217 | }); 218 | -------------------------------------------------------------------------------- /src/bin/util.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as chalk from 'chalk'; 3 | import { Options } from '../options'; 4 | import { ProgramError } from '../errors'; 5 | 6 | export function formatDiagnostics(diagnostics: ts.Diagnostic[]): string[] { 7 | let formatted: string[] = []; 8 | 9 | for (let diag of diagnostics) { 10 | formatted.push(ts.formatDiagnostics([diag], { 11 | getCurrentDirectory: () => ts.sys.getCurrentDirectory(), 12 | getNewLine: () => ts.sys.newLine, 13 | getCanonicalFileName: (f: string) => f 14 | }).trim()); 15 | } 16 | 17 | return formatted.filter(str => str.trim().length > 0); 18 | } 19 | 20 | export function getError(error: string | Error, options?: Options): string { 21 | let err = typeof error === 'string' ? error : ''; 22 | let isProgramError = false; 23 | 24 | if (error instanceof Error) { 25 | if (error.stack) { 26 | err += error.stack; 27 | } 28 | 29 | if (error.message && err.indexOf(error.message) === -1) { 30 | err = err ? `${error.message} ${err}` : error.message; 31 | } 32 | 33 | if (error.name === ProgramError.id) { 34 | isProgramError = true; 35 | err = err.replace(`${ProgramError.id}: `, ''); 36 | } 37 | } 38 | 39 | if (isProgramError) { 40 | const split = err.split('\n'); 41 | const result = split.slice(0, 1); 42 | 43 | return result[0]; 44 | } else if (options) { 45 | let limit = options.stackTraceOutput; 46 | limit = typeof limit === 'string' ? parseInt(limit) : limit; 47 | limit = limit < 0 ? 0 : limit; 48 | limit = limit === undefined ? 3 : limit; 49 | 50 | const lines = limit === 0 ? 1 : limit + 1; 51 | const split = err.split('\n'); 52 | const result = split.slice(0, lines); 53 | 54 | if (limit > 0 && split.length > result.length) { 55 | result.push(chalk.bold(` -> There are ${split.length - lines} more items on the stack trace. Use option -s ${split.length - 1} to show the full stack trace.`)); 56 | } 57 | 58 | return result.join('\n');; 59 | } 60 | 61 | return err; 62 | } 63 | -------------------------------------------------------------------------------- /src/bus.ts: -------------------------------------------------------------------------------- 1 | import * as EventEmitter from 'events'; 2 | 3 | export const events = { 4 | START: Symbol('start'), 5 | END: Symbol('end'), 6 | STOP: Symbol('stop'), 7 | ERROR: Symbol('error'), 8 | WARN: Symbol('warn'), 9 | DIAGNOSTICS: Symbol('diagnostics'), 10 | TRANSFORM: Symbol('transform'), 11 | SCAN: Symbol('transform'), 12 | EMIT: Symbol('emit'), 13 | CLEAN: Symbol('cleanup'), 14 | }; 15 | 16 | export const emitter = new EventEmitter(); 17 | export const emit: typeof emitter.emit = emitter.emit.bind(emitter); 18 | export const on: typeof emitter.on = emitter.on.bind(emitter); 19 | -------------------------------------------------------------------------------- /src/errors.ts: -------------------------------------------------------------------------------- 1 | export class ProgramError extends Error { 2 | 3 | public static id = 'ProgramError'; 4 | 5 | constructor(public message: string) { 6 | super(message); 7 | this.name = ProgramError.id; 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/host.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as ts from 'typescript'; 3 | 4 | export interface FileReflection { 5 | name: string; 6 | text: string; 7 | } 8 | 9 | export class Host implements ts.CompilerHost { 10 | 11 | private fileMap: Map = new Map(); 12 | private outputs: Map = new Map(); 13 | 14 | private defaultLibFileName = 'node_modules/typescript/lib/lib.d.ts'; 15 | private defaultLibLocation = 'node_modules/typescript/lib/'; 16 | private currentDirectory = ''; 17 | private caseSensitiveFileNames = false; 18 | private newLine = '\n'; 19 | 20 | constructor(files: FileReflection[], private compilerOptions: ts.CompilerOptions, setParentNodes = true) { 21 | for (let file of files) { 22 | this.fileMap.set( 23 | file.name, 24 | ts.createSourceFile( 25 | file.name, file.text, compilerOptions.target, setParentNodes 26 | ) 27 | ); 28 | } 29 | } 30 | 31 | public getResult(): FileReflection[] { 32 | const result: FileReflection[] = []; 33 | const cwd = process.cwd(); 34 | 35 | this.outputs.forEach((text, name) => { 36 | const sepRegExp = new RegExp(`^\\${path.sep}+`); 37 | name = name.split(`${cwd}${path.sep}`).join(''); 38 | name = name.replace(sepRegExp, ''); 39 | result.push({ name, text }); 40 | }); 41 | 42 | return result; 43 | } 44 | 45 | public setDefaultLibFileName(defaultLibFileName: string) { 46 | this.defaultLibFileName = defaultLibFileName; 47 | } 48 | 49 | public setDefaultLibLocation(defaultLibLocation: string) { 50 | this.defaultLibLocation = defaultLibLocation; 51 | } 52 | 53 | public setCurrentDirectory(currentDirectory: string) { 54 | this.currentDirectory = currentDirectory; 55 | } 56 | 57 | public setUseCaseSensitiveFileNames(useCaseSensitiveFileNames: boolean) { 58 | this.caseSensitiveFileNames = useCaseSensitiveFileNames; 59 | } 60 | 61 | public setNewLine(newLine: string) { 62 | this.newLine = newLine; 63 | } 64 | 65 | public getSourceFile(fileName: string) { 66 | return this.fileMap.get(fileName); 67 | } 68 | 69 | public getDefaultLibFileName(options: ts.CompilerOptions): string { 70 | return path.join(path.resolve(path.dirname(this.defaultLibFileName)), path.basename(this.defaultLibFileName)); 71 | } 72 | 73 | public getDefaultLibLocation(): string { 74 | return path.resolve(this.defaultLibLocation); 75 | } 76 | 77 | public getCurrentDirectory(): string { 78 | return path.resolve(this.currentDirectory); 79 | } 80 | 81 | public getDirectories(path: string): string[] { 82 | return []; 83 | } 84 | 85 | public getCanonicalFileName(fileName: string): string { 86 | return fileName; 87 | } 88 | 89 | public useCaseSensitiveFileNames(): boolean { 90 | return this.caseSensitiveFileNames; 91 | } 92 | 93 | public getNewLine(): string { 94 | return this.newLine; 95 | } 96 | 97 | public fileExists(fileName: string): boolean { 98 | return this.fileMap.has(fileName); 99 | } 100 | 101 | public readFile(fileName: string): string { 102 | return this.fileMap.has(fileName) ? this.fileMap.get(fileName).text : undefined; 103 | } 104 | 105 | public writeFile(fileName: string, data: string, writeByteOrderMark?: boolean, onError?: (message: string) => void, sourceFiles?: ts.SourceFile[]): void { 106 | this.outputs.set(fileName, data); 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import * as bus from './bus'; 2 | 3 | export * from './options'; 4 | export * from './transform'; 5 | 6 | export { bus }; 7 | -------------------------------------------------------------------------------- /src/lib/ambient.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'flow-runtime'; 2 | -------------------------------------------------------------------------------- /src/lib/build/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | const UglifyJsWebpackPlugin = require('uglifyjs-webpack-plugin'); 4 | 5 | const config = {}; 6 | 7 | config.paths = { 8 | src: '../', 9 | dest: '../../../dist/lib' 10 | }; 11 | 12 | config.typescript = { 13 | configFile: './tsconfig.json' 14 | }; 15 | 16 | config.webpack = [ 17 | { 18 | mode: 'production', 19 | entry: { 20 | 'ts-runtime.lib': path.normalize(path.join(__dirname, config.paths.src, 'index.ts')), 21 | 'ts-runtime.lib.min': path.normalize(path.join(__dirname, config.paths.src, 'index.ts')) 22 | }, 23 | output: { 24 | filename: '[name].js', 25 | path: path.normalize(path.join(__dirname, config.paths.dest)), 26 | library: { 27 | root: 'tsr', 28 | amd: 'ts-runtime/lib', 29 | commonjs: 'ts-runtime/lib' 30 | }, 31 | libraryTarget: 'umd' 32 | }, 33 | devtool: 'source-map', 34 | resolve: { 35 | extensions: ['.ts', '.tsx', '.js'] 36 | }, 37 | module: { 38 | rules: [ 39 | { 40 | test: /\.js$/, 41 | use: ["source-map-loader"], 42 | enforce: "pre" 43 | }, { 44 | test: /\.tsx?$/, 45 | use: ["source-map-loader"], 46 | enforce: "pre" 47 | }, { 48 | test: /\.tsx?$/, 49 | exclude: /\.d\.ts$/, 50 | loader: 'ts-loader', 51 | options: config.typescript 52 | } 53 | ] 54 | }, 55 | plugins: [new UglifyJsWebpackPlugin({include: /\.min\.js$/, sourceMap: true})] 56 | } 57 | ]; 58 | 59 | module.exports = config.webpack; 60 | -------------------------------------------------------------------------------- /src/lib/index.ts: -------------------------------------------------------------------------------- 1 | import * as t from 'flow-runtime'; 2 | 3 | const voidType = t.void; 4 | const typeOf = t.typeOf; 5 | 6 | t.undef = () => { 7 | return voidType.bind(t)(); 8 | } 9 | 10 | t.nostrict = (...args: any[]) => { 11 | return t.union(...args, t.null(), t.undef()); 12 | } 13 | 14 | t.void = () => { 15 | return t.union(t.null(), t.undef()); 16 | } 17 | 18 | t.n = (...args: any[]) => { 19 | return t.nullable(...args); 20 | } 21 | 22 | t.enum = (...args: any[]) => { 23 | return t.union(...args); 24 | } 25 | 26 | t.enumMember = (arg: any) => { 27 | return t.literal(arg); 28 | } 29 | 30 | t.enumRef = (...args: any[]) => { 31 | return t.typeOf(...args); 32 | } 33 | 34 | t.typeOf = (input: any, declaration = false) => { 35 | if (declaration && typeof input === 'string') { 36 | input = t.get(input); 37 | 38 | if (input) { 39 | if (input.typeName === 'ClassDeclaration') { 40 | return t.Class(input); 41 | } 42 | 43 | return input; 44 | } 45 | } 46 | 47 | return typeOf.bind(t)(input); 48 | } 49 | 50 | export const lib = t; 51 | export default t; 52 | 53 | // const map: Map = new Map(); 54 | // const intersect = t.intersect; 55 | // const declare = t.declare; 56 | // const ref = t.ref; 57 | // const decorate = t.decorate; 58 | // t.decorate = (type: any, shouldAssert?: boolean) => { 59 | // return (input: any, propertyName: any, descriptor: any) => { 60 | // const decorator = decorate.bind(t)(type, shouldAssert)(input, propertyName, descriptor); 61 | // if (descriptor) descriptor.writable = true; 62 | // input.writable = true; 63 | // Object.defineProperty(input, propertyName, decorator); 64 | // }; 65 | // } 66 | // 67 | // t.declare = (name: string, type: any) => { 68 | // map.set(name, type); 69 | // declare.bind(t)(name, type); 70 | // } 71 | // 72 | // t.ref = (type: any, ...args: any[]) => { 73 | // if (typeof type === 'string') { 74 | // if (map.has(type)) { 75 | // type = map.get(type); 76 | // } 77 | // } 78 | // 79 | // return ref.bind(t)(type, ...args); 80 | // } 81 | // t.intersect = (...args: any[]) => { 82 | // return intersect.bind(t)(...args).unwrap(); 83 | // } 84 | // 85 | // t.intersection = (...args: any[]) => { 86 | // return t.intersect(...args); 87 | // } 88 | -------------------------------------------------------------------------------- /src/lib/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "skipLibCheck": true, 5 | "experimentalDecorators": true, 6 | "sourceMap": true, 7 | "rootDir": "./", 8 | "declaration": false, 9 | "module": "commonjs", 10 | "target": "ES5", 11 | "moduleResolution": "node", 12 | "lib": [ 13 | "es2015", 14 | "dom", 15 | "scripthost", 16 | "webworker" 17 | ] 18 | }, 19 | "include": [ 20 | "**/*" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /src/mutators.ts: -------------------------------------------------------------------------------- 1 | import { Mutator } from './mutators/Mutator'; 2 | import { ArrowFunctionMutator } from './mutators/ArrowFunctionMutator'; 3 | import { AsExpressionMutator } from './mutators/AsExpressionMutator'; 4 | import { BinaryExpressionMutator } from './mutators/BinaryExpressionMutator'; 5 | import { BlockLikeMutator } from './mutators/BlockLikeMutator'; 6 | import { ClassDeclarationMutator } from './mutators/ClassDeclarationMutator' 7 | import { FunctionDeclarationMutator } from './mutators/FunctionDeclarationMutator'; 8 | import { FunctionExpressionMutator } from './mutators/FunctionExpressionMutator'; 9 | import { InterfaceDeclarationMutator } from './mutators/InterfaceDeclararionMutator'; 10 | import { SourceFileMutator } from './mutators/SourceFileMutator'; 11 | import { TypeAliasDeclarationMutator } from './mutators/TypeAliasDeclararionMutator'; 12 | import { VariableDeclarationListMutator } from './mutators/VariableDeclarationListMutator'; 13 | 14 | export { 15 | Mutator, 16 | ArrowFunctionMutator, 17 | AsExpressionMutator, 18 | BinaryExpressionMutator, 19 | BlockLikeMutator, 20 | ClassDeclarationMutator, 21 | FunctionDeclarationMutator, 22 | FunctionExpressionMutator, 23 | InterfaceDeclarationMutator, 24 | SourceFileMutator, 25 | TypeAliasDeclarationMutator, 26 | VariableDeclarationListMutator, 27 | }; 28 | 29 | export const mutators: (typeof Mutator)[] = [ 30 | ArrowFunctionMutator, 31 | AsExpressionMutator, 32 | BinaryExpressionMutator, 33 | BlockLikeMutator, 34 | ClassDeclarationMutator, 35 | FunctionDeclarationMutator, 36 | FunctionExpressionMutator, 37 | InterfaceDeclarationMutator, 38 | SourceFileMutator, 39 | TypeAliasDeclarationMutator, 40 | VariableDeclarationListMutator, 41 | ]; 42 | 43 | export function getMutators(): Mutator[] { 44 | const instances: Mutator[] = []; 45 | 46 | for (let mutator of mutators) { 47 | instances.push(new (mutator as any)()); 48 | } 49 | 50 | return instances; 51 | } 52 | -------------------------------------------------------------------------------- /src/mutators/ArrowFunctionMutator.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Mutator } from './Mutator'; 3 | 4 | export class ArrowFunctionMutator extends Mutator { 5 | 6 | protected kind = ts.SyntaxKind.ArrowFunction; 7 | 8 | protected mutate(node: ts.ArrowFunction): ts.Expression { 9 | let substitution: ts.Expression = this.factory.mutateFunctionBody(node) as ts.ArrowFunction; 10 | 11 | if (!this.options.noAnnotate) { 12 | substitution = this.factory.annotate([ 13 | substitution, 14 | this.factory.functionReflection(node) 15 | ]); 16 | } 17 | 18 | return substitution; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/mutators/AsExpressionMutator.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as util from '../util'; 3 | import { Mutator } from './Mutator'; 4 | 5 | export class AsExpressionMutator extends Mutator { 6 | 7 | protected kind = ts.SyntaxKind.AsExpression; 8 | 9 | protected mutate(node: ts.AsExpression): ts.Node { 10 | if (util.isAnyKeyword(node.type)) { 11 | return node; 12 | } 13 | 14 | // if (this.context.isSafeAssignment(node.type, node.expression)) { 15 | // return node; 16 | // } 17 | 18 | return this.factory.typeReflectionAndAssertion(node.type, node.expression); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/mutators/BinaryExpressionMutator.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as util from '../util'; 3 | import { Mutator } from './Mutator'; 4 | 5 | export class BinaryExpressionMutator extends Mutator { 6 | 7 | protected kind = ts.SyntaxKind.BinaryExpression; 8 | 9 | private assignmentOperators: ts.AssignmentOperator[] = [ 10 | ts.SyntaxKind.EqualsToken, 11 | ts.SyntaxKind.PlusEqualsToken, 12 | ts.SyntaxKind.MinusEqualsToken, 13 | ts.SyntaxKind.AsteriskAsteriskEqualsToken, 14 | ts.SyntaxKind.AsteriskEqualsToken, 15 | ts.SyntaxKind.SlashEqualsToken, 16 | ts.SyntaxKind.PercentEqualsToken, 17 | ts.SyntaxKind.AmpersandEqualsToken, 18 | ts.SyntaxKind.BarEqualsToken, 19 | ts.SyntaxKind.CaretEqualsToken, 20 | ts.SyntaxKind.LessThanLessThanEqualsToken, 21 | ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken, 22 | ts.SyntaxKind.GreaterThanGreaterThanEqualsToken 23 | ]; 24 | 25 | protected mutate(node: ts.BinaryExpression): ts.Node { 26 | if (node.left.kind !== ts.SyntaxKind.Identifier) { 27 | return node; 28 | } 29 | 30 | if (!this.isAssignmentOperator(node)) { 31 | return node; 32 | } 33 | 34 | if (!ts.isIdentifier(node.left)) { 35 | return node; 36 | } 37 | 38 | if (!this.options.assertAny && this.context.isAny(node.left)) { 39 | return node; 40 | } 41 | 42 | // if (this.context.isSafeAssignment(node.left, node.right)) { 43 | // return node; 44 | // } 45 | 46 | const name = this.context.getTypeDeclarationName(node.left); 47 | const right = this.factory.typeAssertion(name, node.right); 48 | 49 | return ts.updateBinary(node, node.left, right); 50 | } 51 | 52 | private isAssignmentOperator(node: ts.BinaryExpression): boolean { 53 | return this.assignmentOperators.indexOf(node.operatorToken.kind as any) !== -1; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/mutators/BlockLikeMutator.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as util from '../util'; 3 | import { Mutator } from './Mutator'; 4 | 5 | export class BlockLikeMutator extends Mutator { 6 | 7 | protected kind = [ 8 | ts.SyntaxKind.SourceFile, 9 | ts.SyntaxKind.Block, 10 | ts.SyntaxKind.ModuleBlock, 11 | ts.SyntaxKind.CaseClause, 12 | ts.SyntaxKind.DefaultClause 13 | ]; 14 | 15 | protected mutate(node: ts.BlockLike): ts.BlockLike { 16 | let needsUpdate = false; 17 | const statements: ts.Statement[] = []; 18 | 19 | for (let statement of node.statements) { 20 | if (util.isAmbientDeclaration(statement)) { 21 | continue; 22 | } 23 | 24 | let statementSubstitution = util.asArray(statement); 25 | 26 | switch (statement.kind) { 27 | case ts.SyntaxKind.FunctionDeclaration: 28 | statementSubstitution = this.annotateFunctionDeclaration(statement as ts.FunctionDeclaration); 29 | needsUpdate = true; 30 | break; 31 | case ts.SyntaxKind.ClassDeclaration: 32 | if (util.hasTypeParameters(statement)) { 33 | statementSubstitution = this.annotateClassDeclaration(statement as ts.ClassDeclaration); 34 | needsUpdate = true; 35 | } 36 | break; 37 | case ts.SyntaxKind.EnumDeclaration: 38 | statementSubstitution = this.annotateEnumDeclaration(statement as ts.EnumDeclaration); 39 | needsUpdate = true; 40 | break; 41 | } 42 | 43 | statements.push(...statementSubstitution); 44 | } 45 | 46 | return needsUpdate ? this.updateBlock(node, statements) : node; 47 | } 48 | 49 | private updateBlock(node: ts.BlockLike, statements: ts.Statement[]): ts.BlockLike { 50 | switch (node.kind) { 51 | case this.kind[0]: 52 | return this.map(ts.updateSourceFileNode(node as ts.SourceFile, statements), node); 53 | case this.kind[1]: 54 | return this.map(ts.updateBlock(node as ts.Block, statements), node); 55 | case this.kind[2]: 56 | return this.map(ts.updateModuleBlock(node as ts.ModuleBlock, statements), node); 57 | case this.kind[3]: 58 | return this.map(ts.updateCaseClause(node as ts.CaseClause, (node as ts.CaseClause).expression, statements), node); 59 | case this.kind[4]: 60 | return this.map(ts.updateDefaultClause(node as ts.DefaultClause, statements), node); 61 | default: 62 | return node; 63 | } 64 | } 65 | 66 | private annotateFunctionDeclaration(node: ts.FunctionDeclaration): ts.Statement[] { 67 | if (this.options.noAnnotate) { 68 | return [node]; 69 | } 70 | 71 | const annotation = ts.createStatement( 72 | this.factory.annotate([ 73 | node.name, 74 | this.factory.functionReflection(node) 75 | ]) 76 | ); 77 | 78 | return [node, annotation]; 79 | } 80 | 81 | private annotateClassDeclaration(node: ts.ClassDeclaration): ts.Statement[] { 82 | const annotation = ts.createVariableStatement( 83 | undefined, 84 | this.factory.classTypeParameterSymbolDeclaration(node.name) 85 | ); 86 | 87 | return [annotation, node]; 88 | } 89 | 90 | private annotateEnumDeclaration(node: ts.EnumDeclaration): ts.Statement[] { 91 | const annotation = ts.createStatement( 92 | this.factory.annotate([ 93 | node.name, 94 | this.factory.enumReflection(node) 95 | ]) 96 | ); 97 | 98 | return [node, annotation]; 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /src/mutators/ClassDeclarationMutator.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as util from '../util'; 3 | import { Mutator } from './Mutator'; 4 | 5 | type MethodLikeProperty = ts.ConstructorDeclaration | ts.MethodDeclaration | 6 | ts.SetAccessorDeclaration | ts.GetAccessorDeclaration; 7 | 8 | type PropertySubstitution = ts.PropertyDeclaration | ts.GetAccessorDeclaration | 9 | ts.SetAccessorDeclaration; 10 | 11 | export class ClassDeclarationMutator extends Mutator { 12 | 13 | protected kind = ts.SyntaxKind.ClassDeclaration; 14 | protected initializers: ts.Statement[] = []; 15 | 16 | protected mutate(node: ts.ClassDeclaration): ts.Node { 17 | const members: ts.ClassElement[] = []; 18 | 19 | const decorators = this.options.noAnnotate ? 20 | node.decorators : this.reflectClass(node); 21 | 22 | for (let member of node.members) { 23 | switch (member.kind) { 24 | case ts.SyntaxKind.Constructor: 25 | case ts.SyntaxKind.MethodDeclaration: 26 | case ts.SyntaxKind.GetAccessor: 27 | case ts.SyntaxKind.SetAccessor: 28 | members.push(this.mutateMethodDeclaration(member as MethodLikeProperty)); 29 | break; 30 | case ts.SyntaxKind.PropertyDeclaration: 31 | members.push(...util.asArray(this.mutatePropertyDeclaration(member as ts.PropertyDeclaration))); 32 | break; 33 | case ts.SyntaxKind.IndexSignature: 34 | default: 35 | members.push(member); 36 | } 37 | } 38 | 39 | this.addInitializers(node, members); 40 | this.declareTypeParameters(node, members); 41 | this.assertImplementing(node, members); 42 | this.setMerged(node); 43 | 44 | return ts.updateClassDeclaration( 45 | node, decorators, node.modifiers, node.name, 46 | node.typeParameters, node.heritageClauses, members 47 | ); 48 | } 49 | 50 | private setMerged(node: ts.ClassDeclaration) { 51 | const nodeSymbol = this.scanner.getNodeSymbol(node.name); 52 | this.context.setMerged(nodeSymbol); 53 | } 54 | 55 | private reflectClass(node: ts.ClassDeclaration): ts.Decorator[] { 56 | const classReflection = this.factory.classReflection(node); 57 | const decorators = util.arrayFromNodeArray(node.decorators); 58 | const decorator = ts.createDecorator(this.factory.annotate(classReflection)); 59 | 60 | decorators.unshift(decorator); 61 | 62 | return decorators; 63 | } 64 | 65 | private addInitializers(node: ts.ClassDeclaration, members: ts.ClassElement[]): void { 66 | if (this.initializers.length === 0) { 67 | return; 68 | } 69 | 70 | const constructor = this.getConstructor(members); 71 | let statements = util.arrayFromNodeArray(constructor.body.statements); 72 | 73 | for (let initializer of this.initializers) { 74 | statements = util.insertAfterSuper(statements, initializer); 75 | } 76 | 77 | this.updateConstructor(members, constructor, statements); 78 | } 79 | 80 | private assertImplementing(node: ts.ClassDeclaration, members: ts.ClassElement[]): ts.ClassElement[] { 81 | const implementsClause = util.getImplementsClause(node); 82 | 83 | if (!implementsClause) { 84 | return members; 85 | } 86 | 87 | let constructor = this.getConstructor(members); 88 | let statements = util.arrayFromNodeArray(constructor.body.statements); 89 | 90 | for (let impl of implementsClause.types || []) { 91 | const assertion = ts.createStatement( 92 | this.factory.typeAssertion( 93 | this.factory.expressionWithTypeArgumentsReflection(impl), 94 | ts.createThis() 95 | ) 96 | ); 97 | 98 | statements.push(assertion); 99 | } 100 | 101 | this.updateConstructor(members, constructor, statements); 102 | 103 | return members; 104 | } 105 | 106 | private declareTypeParameters(node: ts.ClassDeclaration, members: ts.ClassElement[]): ts.ClassElement[] { 107 | const extendsClause = util.getExtendsClause(node); 108 | const hasTypeParameters = util.hasTypeParameters(node); 109 | const extendsClauseHasTypeArguments = util.extendsClauseHasTypeArguments(extendsClause); 110 | 111 | if (!hasTypeParameters && !extendsClauseHasTypeArguments) { 112 | return members; 113 | } 114 | 115 | let constructor = this.getConstructor(members); 116 | let statements: ts.Statement[] = util.arrayFromNodeArray(constructor.body.statements); 117 | 118 | let typeParametersStatement: ts.Statement; 119 | let thisStatement: ts.Statement; 120 | let bindStatement: ts.Statement; 121 | 122 | const insert: ts.Statement[] = []; 123 | 124 | if (hasTypeParameters) { 125 | typeParametersStatement = this.factory.typeParametersLiteralDeclaration(util.arrayFromNodeArray(node.typeParameters)); 126 | thisStatement = this.factory.classTypeParameterSymbolConstructorDeclaration(node.name); 127 | insert.push(typeParametersStatement); 128 | } 129 | 130 | if (extendsClauseHasTypeArguments) { 131 | bindStatement = this.factory.typeParameterBindingDeclaration( 132 | util.arrayFromNodeArray(extendsClause.types[0].typeArguments) 133 | ); 134 | } 135 | 136 | insert.push(...[thisStatement, bindStatement].filter(statement => !!statement)); 137 | util.insertAfterSuper(statements, insert); 138 | this.updateConstructor(members, constructor, statements); 139 | 140 | if (hasTypeParameters) { 141 | members.unshift(this.factory.classTypeParameterSymbolPropertyDeclaration(node.name)); 142 | } 143 | 144 | return members; 145 | } 146 | 147 | private mutatePropertyDeclaration(node: ts.PropertyDeclaration): PropertySubstitution | PropertySubstitution[] { 148 | if (!this.options.assertAny && this.context.isAny(node.type)) { 149 | return node; 150 | } 151 | 152 | if (ts.isComputedPropertyName(node.name)) { 153 | return node; 154 | } 155 | 156 | if (util.hasModifier(node, ts.SyntaxKind.AbstractKeyword)) { 157 | return node; 158 | } 159 | 160 | const isStatic = util.isStatic(node); 161 | const className = (this.node as ts.ClassDeclaration).name.text; 162 | 163 | // Use decorators for static properties temporarily 164 | // if (util.isStatic(node)) { 165 | // const decorators = util.asNewArray(node.decorators); 166 | // 167 | // if (node.initializer) { 168 | // const typeReflection = this.factory.typeReflection(node.type); 169 | // 170 | // let decorator: ts.Decorator; 171 | // 172 | // if (util.hasKind(typeReflection, ts.SyntaxKind.ThisKeyword)) { 173 | // decorator = ts.createDecorator(this.factory.decorate( 174 | // ts.createFunctionExpression(undefined, undefined, undefined, undefined, undefined, undefined, 175 | // ts.createBlock([ts.createReturn(typeReflection)], true) 176 | // ) 177 | // )); 178 | // } else { 179 | // decorator = ts.createDecorator(this.factory.decorate(typeReflection)); 180 | // } 181 | // 182 | // decorators.unshift(decorator); 183 | // } 184 | // 185 | // return ts.updateProperty(node, decorators, node.modifiers, node.name, node.type, node.initializer); 186 | // } 187 | 188 | let name = node.name.text; 189 | if (!ts.isComputedPropertyName(node.name)) { 190 | name = this.context.getPropertyName(this.node as ts.ClassDeclaration, `_${node.name.text}`) 191 | } 192 | 193 | let initializer: ts.Expression = undefined; 194 | 195 | if (node.initializer) { 196 | initializer = this.factory.typeReflectionAndAssertion(node.type, node.initializer); 197 | } 198 | 199 | if (!isStatic && initializer) { 200 | this.initializers.push(ts.createStatement( 201 | ts.createBinary( 202 | ts.createPropertyAccess(isStatic ? ts.createIdentifier(className) : ts.createThis(), name), 203 | ts.SyntaxKind.FirstAssignment, 204 | initializer 205 | ) 206 | )); 207 | } 208 | 209 | const property = this.map( 210 | ts.updateProperty(node, node.decorators, node.modifiers, ts.createIdentifier(name), node.questionToken, node.type, isStatic ? initializer : undefined), 211 | node); 212 | 213 | let setAccessor: ts.SetAccessorDeclaration; 214 | if (!util.hasModifier(node, ts.SyntaxKind.ReadonlyKeyword)) { 215 | setAccessor = this.factory.mutateFunctionBody(ts.createSetAccessor(undefined, node.modifiers, node.name, [ 216 | ts.createParameter(undefined, undefined, undefined, node.name.text, undefined, node.type) 217 | ], ts.createBlock([ts.createStatement( 218 | ts.createBinary(ts.createPropertyAccess(isStatic ? ts.createIdentifier(className) : ts.createThis(), name), ts.SyntaxKind.FirstAssignment, node.name as ts.Expression) 219 | )], true 220 | ))) as ts.SetAccessorDeclaration; 221 | } 222 | 223 | const getAccessor = ts.createGetAccessor(undefined, node.modifiers, node.name, undefined, node.type, ts.createBlock( 224 | [ts.createReturn( 225 | ts.createPropertyAccess( 226 | isStatic ? ts.createIdentifier(className) : ts.createThis(), 227 | name 228 | ) 229 | )], true 230 | )); 231 | 232 | // TODO: fix any cast 233 | (node.name as any) = ts.createIdentifier(name); 234 | 235 | return [property, getAccessor, setAccessor].filter(val => !!val); 236 | } 237 | 238 | // TODO: assert parameters and return type is isImplementationOfOverload 239 | private mutateMethodDeclaration(node: MethodLikeProperty): MethodLikeProperty { 240 | return this.factory.mutateFunctionBody(node) as MethodLikeProperty; 241 | } 242 | 243 | private getConstructor(members: ts.ClassElement[], create = true): ts.ConstructorDeclaration { 244 | const index = members.findIndex(member => member.kind === ts.SyntaxKind.Constructor); 245 | const exists = index !== -1; 246 | 247 | if (exists) { 248 | return members[index] as ts.ConstructorDeclaration; 249 | } 250 | 251 | if (!create) { 252 | return null; 253 | } 254 | 255 | const extendsClause = util.getExtendsClause(this.node as ts.ClassDeclaration); 256 | const isExtending = !!extendsClause; 257 | 258 | const constructor = ts.createConstructor( 259 | undefined, undefined, 260 | isExtending 261 | ? [ts.createParameter(undefined, undefined, ts.createToken(ts.SyntaxKind.DotDotDotToken), 'args')] 262 | : undefined, 263 | ts.createBlock( 264 | isExtending 265 | ? [ts.createStatement( 266 | ts.createCall(ts.createSuper(), undefined, [ts.createSpread(ts.createIdentifier('args'))]) 267 | )] : [], 268 | true 269 | ) 270 | ); 271 | 272 | return constructor; 273 | } 274 | 275 | private updateConstructor(members: ts.ClassElement[], constructor: ts.ConstructorDeclaration, statements: ts.Statement[]): ts.ClassElement[] { 276 | const index = members.findIndex(member => member.kind === ts.SyntaxKind.Constructor); 277 | const exists = index !== -1; 278 | 279 | constructor = this.map(ts.updateConstructor( 280 | constructor, 281 | constructor.decorators, 282 | constructor.modifiers, 283 | constructor.parameters, 284 | this.map(ts.updateBlock(constructor.body, statements), constructor.body) 285 | ), constructor); 286 | 287 | if (exists) { 288 | members[index] = constructor; 289 | } else { 290 | members.unshift(constructor); 291 | } 292 | 293 | return members; 294 | } 295 | 296 | } 297 | -------------------------------------------------------------------------------- /src/mutators/FunctionDeclarationMutator.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Mutator } from './Mutator'; 3 | 4 | export class FunctionDeclarationMutator extends Mutator { 5 | 6 | protected kind = ts.SyntaxKind.FunctionDeclaration; 7 | 8 | protected mutate(node: ts.FunctionDeclaration): ts.FunctionDeclaration { 9 | return this.factory.mutateFunctionBody(node); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/mutators/FunctionExpressionMutator.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Mutator } from './Mutator'; 3 | 4 | export class FunctionExpressionMutator extends Mutator { 5 | 6 | protected kind = ts.SyntaxKind.FunctionExpression; 7 | 8 | protected mutate(node: ts.FunctionExpression): ts.Expression { 9 | let substitution: ts.Expression = this.factory.mutateFunctionBody(node) as ts.FunctionExpression; 10 | 11 | if (!this.options.noAnnotate) { 12 | substitution = this.factory.annotate([ 13 | substitution, 14 | this.factory.functionReflection(node) 15 | ]); 16 | } 17 | 18 | return substitution; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/mutators/InterfaceDeclararionMutator.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Mutator } from './Mutator'; 3 | 4 | export class InterfaceDeclarationMutator extends Mutator { 5 | 6 | protected kind = ts.SyntaxKind.InterfaceDeclaration; 7 | 8 | protected mutate(node: ts.InterfaceDeclaration): ts.Node { 9 | if (this.removeInterface(node)) { 10 | return null; 11 | } 12 | 13 | return this.factory.interfaceSubstitution(node); 14 | } 15 | 16 | private removeInterface(node: ts.InterfaceDeclaration): boolean { 17 | const nodeSymbol = this.scanner.getNodeSymbol(node.name); 18 | const willBeDeclaredInClass = this.willBeDeclaredInClass(nodeSymbol.getDeclarations()); 19 | const wasMerged = this.context.wasMerged(nodeSymbol); 20 | 21 | if (!willBeDeclaredInClass) { 22 | this.context.setMerged(nodeSymbol); 23 | } 24 | 25 | return willBeDeclaredInClass || wasMerged; 26 | } 27 | 28 | private willBeDeclaredInClass(declarations: ts.Declaration[]) { 29 | for(let declaration of declarations) { 30 | if (ts.isClassDeclaration(declaration)) { 31 | return true; 32 | } 33 | } 34 | 35 | return false; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/mutators/Mutator.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as util from '../util'; 3 | import { Options } from '../options'; 4 | import { Factory } from '../factory'; 5 | import { Scanner } from '../scanner'; 6 | import { MutationContext } from '../context'; 7 | 8 | export abstract class Mutator { 9 | 10 | protected abstract kind: ts.SyntaxKind | ts.SyntaxKind[]; 11 | protected abstract mutate(node: ts.Node): ts.Node; 12 | 13 | protected context: MutationContext; 14 | protected node: ts.Node; 15 | 16 | public mutateNode(node: ts.Node, context: MutationContext): ts.Node { 17 | this.context = context; 18 | this.node = node; 19 | 20 | if (!this.shouldMutate(node)) { 21 | return node; 22 | } 23 | 24 | if (this.shouldSkip(node)) { 25 | return node; 26 | } 27 | 28 | if (util.isAmbientDeclaration(node)) { 29 | return node; 30 | } 31 | 32 | return this.mutate(node); 33 | } 34 | 35 | 36 | public shouldMutate(node: ts.Node): boolean { 37 | return node && util.asArray(this.kind).indexOf(node.kind) !== -1; 38 | } 39 | 40 | get options(): Options { 41 | return this.context.options; 42 | } 43 | 44 | get factory(): Factory { 45 | return this.context.factory; 46 | } 47 | 48 | get scanner(): Scanner { 49 | return this.context.scanner; 50 | } 51 | 52 | get skip(): (node: T, recursive: boolean, ...exclude: ts.Node[]) => T { 53 | return this.context.skip.bind(this.context); 54 | } 55 | 56 | get shouldSkip(): (node: ts.Node) => boolean { 57 | return this.context.shouldSkip.bind(this.context); 58 | } 59 | 60 | get map(): (alias: T, original: ts.Node) => T { 61 | return this.scanner.mapNode.bind(this.scanner); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/mutators/SourceFileMutator.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as ts from 'typescript'; 3 | import * as util from '../util'; 4 | import { Mutator } from './Mutator'; 5 | 6 | export class SourceFileMutator extends Mutator { 7 | 8 | protected kind = ts.SyntaxKind.SourceFile; 9 | 10 | protected mutate(node: ts.SourceFile): ts.SourceFile { 11 | const statements = util.arrayFromNodeArray(node.statements); 12 | const declarations: ts.Statement[] = []; 13 | 14 | if (this.options.moduleAlias) { 15 | declarations.push(ts.createImportDeclaration( 16 | undefined, undefined, undefined, 17 | ts.createLiteral('module-alias/register') 18 | )); 19 | } 20 | 21 | if (!this.options.excludeDeclarationFile && this.context.scanner.getDeclarations().length > 0 && this.context.isEntryFile(node.fileName)) { 22 | const relativePath = path.relative(path.dirname(node.fileName), this.context.commonDir); 23 | const filePath = path.join(relativePath, this.context.options.declarationFileName); 24 | const prefix = !relativePath ? './' : ''; 25 | 26 | declarations.push(ts.createImportDeclaration( 27 | undefined, undefined, undefined, 28 | ts.createLiteral(`${prefix}${filePath}`) 29 | )); 30 | } 31 | 32 | if (!this.options.excludeLib) { 33 | declarations.push(this.factory.importLibStatement()); 34 | } 35 | 36 | statements.unshift(...declarations); 37 | 38 | return declarations.length > 0 ? ts.updateSourceFileNode(node, statements) : node; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/mutators/TypeAliasDeclararionMutator.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Mutator } from './Mutator'; 3 | 4 | export class TypeAliasDeclarationMutator extends Mutator { 5 | 6 | protected kind = ts.SyntaxKind.TypeAliasDeclaration; 7 | 8 | protected mutate(node: ts.TypeAliasDeclaration): ts.Node { 9 | return this.factory.typeAliasSubstitution(node); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/mutators/VariableDeclarationListMutator.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as util from '../util'; 3 | import { Mutator } from './Mutator'; 4 | 5 | export class VariableDeclarationListMutator extends Mutator { 6 | 7 | protected kind = ts.SyntaxKind.VariableDeclarationList; 8 | 9 | private incompatibleParents = [ 10 | ts.SyntaxKind.ForOfStatement, 11 | ts.SyntaxKind.ForInStatement, 12 | ts.SyntaxKind.CatchClause, 13 | ts.SyntaxKind.ImportClause 14 | ]; 15 | 16 | protected mutate(node: ts.VariableDeclarationList): ts.Node { 17 | if (!this.shouldTransform(node)) { 18 | return node; 19 | } 20 | 21 | const declarations: ts.VariableDeclaration[] = []; 22 | 23 | for (const declaration of node.declarations) { 24 | declarations.push(...this.transform(declaration)); 25 | } 26 | 27 | return ts.updateVariableDeclarationList(node, declarations); 28 | } 29 | 30 | private transform(node: ts.VariableDeclaration): ts.VariableDeclaration[] { 31 | if (!ts.isIdentifier(node.name)) { 32 | return [node]; 33 | } 34 | 35 | const isConstDeclaration = util.hasFlag(node.parent, ts.NodeFlags.Const); 36 | 37 | if (isConstDeclaration) { 38 | return this.transformConstDeclaration(node); 39 | } 40 | 41 | return this.transformDeclaration(node); 42 | } 43 | 44 | private transformDeclaration(node: ts.VariableDeclaration): ts.VariableDeclaration[] { 45 | if (!this.options.assertAny && this.context.isAny(node.type)) { 46 | return [node]; 47 | } 48 | 49 | const nodeName = this.context.getTypeDeclarationName((node.name as ts.Identifier).text); 50 | const typeDefinition = this.factory.typeDeclaration(nodeName, node.type); 51 | 52 | if (!node.initializer) { 53 | return [typeDefinition, node]; 54 | } 55 | 56 | // if (this.context.isSafeAssignment(node.type, node.initializer)) { 57 | // return [typeDefinition, node]; 58 | // } 59 | 60 | const initializer = this.factory.typeAssertion(nodeName, node.initializer); 61 | const assignment = ts.updateVariableDeclaration(node, node.name, node.type, initializer); 62 | 63 | return [typeDefinition, assignment]; 64 | } 65 | 66 | private transformConstDeclaration(node: ts.VariableDeclaration): ts.VariableDeclaration[] { 67 | if (!this.options.assertAny && this.context.isAny(node.type)) { 68 | return [node]; 69 | } 70 | 71 | if (!node.initializer || !node.type || (!this.options.assertAny && this.context.isAny(node.type))) { 72 | return [node]; 73 | } 74 | 75 | // if (this.context.isSafeAssignment(node.type, node.initializer)) { 76 | // return [node]; 77 | // } 78 | 79 | const nodeName = this.context.getTypeDeclarationName((node.name as ts.Identifier).text); 80 | const initializer = this.factory.typeReflectionAndAssertion(node.type, node.initializer); 81 | const assignment = ts.updateVariableDeclaration(node, node.name, node.type, initializer); 82 | 83 | return [assignment]; 84 | } 85 | 86 | private shouldTransform(node: ts.VariableDeclarationList): boolean { 87 | if (!node.declarations) { 88 | return false; 89 | } 90 | 91 | if (node.parent && this.incompatibleParents.indexOf(node.parent.kind) !== -1) { 92 | return false; 93 | } 94 | 95 | return true; 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/options.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | 3 | export interface Options { 4 | [index: string]: any; 5 | libDeclarations?: boolean; 6 | noAnnotate?: boolean; 7 | compilerOptions?: ts.CompilerOptions; 8 | declarationFileName?: string; 9 | force?: boolean; 10 | excludeDeclarationFile?: boolean; 11 | excludeLib?: boolean; 12 | keepTemp?: boolean; 13 | libIdentifier?: string; 14 | libNamespace?: string; 15 | declarationPrefix?: string; 16 | moduleAlias?: boolean; 17 | stackTraceOutput?: number; 18 | tempFolderName?: string; 19 | log?: boolean; 20 | assertSafe?: true; 21 | assertAny?: false; 22 | } 23 | 24 | export const defaultOptions: Options = { 25 | noAnnotate: false, 26 | compilerOptions: { 27 | preserveConstEnums: true, 28 | experimentalDecorators: true 29 | }, 30 | libDeclarations: false, 31 | declarationFileName: 'tsr-declarations', 32 | force: false, 33 | excludeDeclarationFile: false, 34 | excludeLib: false, 35 | keepTemp: false, 36 | libIdentifier: 't', 37 | libNamespace: '', 38 | declarationPrefix: '_', 39 | moduleAlias: false, 40 | stackTraceOutput: 3, 41 | tempFolderName: '.tsr', 42 | log: true, 43 | assertSafe: true, 44 | assertAny: false, 45 | }; 46 | -------------------------------------------------------------------------------- /src/playground/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabiandev/ts-runtime/bba9059bc9829d7cb7d14b97818c2fe2c891fa60/src/playground/assets/favicon.ico -------------------------------------------------------------------------------- /src/playground/assets/fonts/icons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/playground/assets/fonts/icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabiandev/ts-runtime/bba9059bc9829d7cb7d14b97818c2fe2c891fa60/src/playground/assets/fonts/icons.ttf -------------------------------------------------------------------------------- /src/playground/assets/fonts/icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabiandev/ts-runtime/bba9059bc9829d7cb7d14b97818c2fe2c891fa60/src/playground/assets/fonts/icons.woff -------------------------------------------------------------------------------- /src/playground/assets/ghlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabiandev/ts-runtime/bba9059bc9829d7cb7d14b97818c2fe2c891fa60/src/playground/assets/ghlogo.png -------------------------------------------------------------------------------- /src/playground/build/config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | const UglifyJsWebpackPlugin = require('uglifyjs-webpack-plugin'); 4 | const pkg = require('../../../package.json'); 5 | const config = {}; 6 | 7 | config.version = pkg.version; 8 | 9 | config.paths = { 10 | src: '../', 11 | dest: '../../../docs', 12 | root: '../../../', 13 | }; 14 | 15 | config.monaco = { 16 | version: pkg.devDependencies['monaco-editor'], 17 | entry: 'vs/editor/editor.main', 18 | get base() { 19 | return `https://unpkg.com/monaco-editor@${this.version}/min`; 20 | }, 21 | get location() { 22 | return `https://unpkg.com/monaco-editor@${this.version}/min/vs` 23 | }, 24 | get loader() { 25 | return `${this.location}/loader.js` 26 | } 27 | }; 28 | 29 | config.bundleName = 'app.js'; 30 | 31 | config.typescript = { 32 | app: { 33 | allowTsInNodeModules: true, 34 | configFile: 'src/playground/tsconfig.json' 35 | }, 36 | lib: { 37 | configFile: 'src/playground/tsconfig.lib.json' 38 | } 39 | }; 40 | 41 | config.replace = { 42 | VERSION: config.version, 43 | BUNDLE_NAME: config.bundleName, 44 | MONACO_VERSION: config.monaco.version, 45 | MONACO_ENTRY: config.monaco.entry, 46 | MONACO_BASE: config.monaco.base, 47 | MONACO_LOCATION: config.monaco.location, 48 | MONACO_LOADER: config.monaco.loader 49 | }; 50 | 51 | config.webpack = [{ 52 | mode: 'production', 53 | entry: { 54 | bundle: path.normalize(path.join(__dirname, config.paths.src, 'index.ts')) 55 | }, 56 | output: { 57 | filename: config.bundleName, 58 | chunkFilename: '[chunkhash].[name].js', 59 | path: path.join(__dirname, config.paths.dest) 60 | }, 61 | performance: { 62 | maxEntrypointSize: 250000, 63 | maxAssetSize: 2560000 64 | }, 65 | devtool: 'source-map', 66 | resolve: { 67 | extensions: ['.ts', '.js', '.tsx'] 68 | }, 69 | module: { 70 | rules: [ 71 | { 72 | test: /\.tsx?$/, 73 | exclude: /\.d\.ts$/, 74 | loader: 'ts-loader', 75 | options: config.typescript.app 76 | }, 77 | { 78 | test: /\.(js|d\.ts)$/, 79 | use: ['source-map-loader'], 80 | enforce: 'pre', 81 | exclude: /node_modules/ 82 | }, 83 | { 84 | test: /\.html/, 85 | use: 'raw-loader' 86 | }, 87 | { 88 | test: /^rimraf$/, 89 | use: 'null-loader' 90 | }, 91 | { 92 | test: /^pretty-time$/, 93 | use: 'null-loader' 94 | }, 95 | ] 96 | }, 97 | node: { 98 | path: true, 99 | fs: 'empty', 100 | module: 'empty' 101 | }, 102 | plugins: [ 103 | new webpack.DefinePlugin(Object.keys(config.replace).reduce(function(previous, current) { 104 | previous[current] = JSON.stringify(config.replace[current]); 105 | return previous; 106 | }, {})), 107 | new webpack.ContextReplacementPlugin( 108 | /node_modules(\\|\/)typescript(\\|\/)lib/, 109 | path.join(__dirname, config.paths.src), 110 | {} 111 | ) 112 | ], 113 | optimization: { 114 | splitChunks: { 115 | automaticNameDelimiter: '.' 116 | } 117 | } 118 | }, { 119 | mode: 'production', 120 | entry: path.normalize(path.join(__dirname, config.paths.root, 'src/lib/index.ts')), 121 | output: { 122 | filename: 'ts-runtime.lib.js', 123 | path: path.normalize(path.join(__dirname, config.paths.root, 'docs')), 124 | library: 't', 125 | libraryTarget: 'window' 126 | }, 127 | devtool: 'source-map', 128 | resolve: { 129 | extensions: ['.ts', '.tsx', '.js'] 130 | }, 131 | module: { 132 | rules: [ 133 | { 134 | test: /\.tsx?$/, 135 | exclude: /\.d\.ts$/, 136 | loader: 'ts-loader', 137 | options: config.typescript.lib 138 | }, 139 | { 140 | test: /\.(js|tsc)$/, 141 | use: ['source-map-loader'], 142 | enforce: 'pre', 143 | exclude: /node_modules/ 144 | } 145 | ] 146 | }, 147 | optimization: { 148 | minimizer: [ 149 | new UglifyJsWebpackPlugin({sourceMap: true}) 150 | ] 151 | } 152 | }]; 153 | 154 | module.exports = config; 155 | -------------------------------------------------------------------------------- /src/playground/build/gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const babel = require('gulp-babel'); 3 | const css = require('gulp-clean-css'); 4 | const del = require('del'); 5 | const less = require('gulp-less'); 6 | const preprocess = require('gulp-preprocess'); 7 | const rename = require('gulp-rename'); 8 | const sourcemaps = require('gulp-sourcemaps'); 9 | const uglify = require('gulp-uglify'); 10 | const webpack = require('webpack'); 11 | 12 | const config = require('./config'); 13 | 14 | function assets() { 15 | return gulp.src([`${config.paths.src}/assets/**/*`]) 16 | .pipe(gulp.dest(`${config.paths.dest}/assets`)) 17 | } 18 | 19 | function copy() { 20 | return gulp.src([ 21 | `${config.paths.src}/proxy.js`, 22 | `${config.paths.src}/run.js` 23 | ]) 24 | .pipe(sourcemaps.init()) 25 | .pipe(preprocess({ context: config.replace })) 26 | .pipe(babel({ presets: ['@babel/preset-env'] })) 27 | .pipe(uglify({ mangle: false })) 28 | .pipe(sourcemaps.write('.')) 29 | .pipe(preprocess({ context: config.replace })) 30 | .pipe(gulp.dest(config.paths.dest)); 31 | } 32 | 33 | function clean(done) { 34 | del([ 35 | `${config.paths.dest}/**/*`, 36 | `!${config.paths.dest}/README.md` 37 | ], { force: true }) 38 | .then(paths => done()); 39 | } 40 | 41 | function html() { 42 | return gulp.src([`${config.paths.src}/index.html`]) 43 | .pipe(preprocess({ context: config.replace })) 44 | .pipe(gulp.dest(config.paths.dest)); 45 | } 46 | 47 | function scripts(done) { 48 | webpack(require('./webpack.config'), (err, stats) => { 49 | if (err) { 50 | console.error(err.stack || err); 51 | if (err.details) { 52 | console.error(err.details); 53 | } 54 | return done(); 55 | } 56 | 57 | const info = stats.toJson(); 58 | 59 | if (stats.hasErrors()) { 60 | console.error(info.errors); 61 | } 62 | 63 | if (stats.hasWarnings()) { 64 | console.warn(info.warnings) 65 | } 66 | 67 | console.log(stats.toString({ 68 | chunks: false, 69 | colors: true 70 | })); 71 | 72 | done(); 73 | }); 74 | } 75 | 76 | function styles() { 77 | return gulp.src([`${config.paths.src}/style.less`]) 78 | .pipe(sourcemaps.init()) 79 | .pipe(less()) 80 | .pipe(css()) 81 | .pipe(rename({ basename: 'style' })) 82 | .pipe(preprocess({ context: config.replace })) 83 | .pipe(sourcemaps.write('.')) 84 | .pipe(gulp.dest(config.paths.dest)); 85 | } 86 | 87 | function watch() { 88 | gulp.watch(`${config.paths.src}/**/*.js`, gulp.parallel(copy)); 89 | gulp.watch([`${config.paths.src}/**/*.ts`, `${config.paths.src}/run.html`], gulp.parallel(scripts)); 90 | gulp.watch(`${config.paths.src}/index.html`, gulp.parallel(html)); 91 | gulp.watch(`${config.paths.src}/**/*.less`, gulp.parallel(styles)); 92 | gulp.watch(`${config.paths.src}/assets/**/*`, gulp.parallel(assets)); 93 | } 94 | 95 | const build = gulp.series( 96 | clean, 97 | gulp.parallel( 98 | assets, copy, html, scripts, styles 99 | ) 100 | ); 101 | 102 | gulp.task('build', build); 103 | gulp.task('watch', gulp.series(build, watch)); 104 | gulp.task('default', build); 105 | -------------------------------------------------------------------------------- /src/playground/build/webpack.config.js: -------------------------------------------------------------------------------- 1 | const config = require('./config'); 2 | module.exports = config.webpack; 3 | -------------------------------------------------------------------------------- /src/playground/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ts-runtime Playground 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 |
18 | 89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 | 101 |
102 |
103 |
104 |
105 |
106 |
107 | Console Output: 108 |
109 |
110 |
111 |
112 |
113 |
114 | 115 | 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /src/playground/proxy.js: -------------------------------------------------------------------------------- 1 | self.MonacoEnvironment = { 2 | baseUrl: '/* @echo MONACO_BASE *//' 3 | }; 4 | 5 | importScripts('/* @echo MONACO_LOCATION *//base/worker/workerMain.js'); 6 | -------------------------------------------------------------------------------- /src/playground/run-console.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ts-runtime Playground (run code) 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |
17 | Console Output: 18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | 31 | 32 | 35 | 38 | 43 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/playground/run-plain.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ts-runtime Playground (run code) 8 | 9 | 10 | 11 | 12 | 13 | 16 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/playground/run.js: -------------------------------------------------------------------------------- 1 | if (!tsp.initialized) { 2 | tsp.getElement = function getElement() { 3 | return document.getElementById('console-content'); 4 | } 5 | 6 | tsp.clearConsole = function clearConsole() { 7 | tsp.getElement().innerHTML = ''; 8 | } 9 | 10 | tsp.wrapConsoleText = function wrapConsoleText(type, text) { 11 | return `${text}`; 12 | } 13 | 14 | tsp.updateConsole = function updateConsole(type, message, ...optionalParams) { 15 | let text = tsp.logToText(message); 16 | 17 | for (let param of optionalParams) { 18 | text += `\n${tsp.logToText(param)}`; 19 | } 20 | 21 | text = tsp.wrapConsoleText(type, text); 22 | 23 | tsp.getElement().innerHTML += `${text}\n`; 24 | } 25 | 26 | tsp.logToText = function logToText(message) { 27 | if (typeof message === 'object' && message !== null) { 28 | return JSON.stringify(message); 29 | } 30 | 31 | return tsp.escape(message); 32 | } 33 | 34 | tsp.escape = function escape(text) { 35 | const div = document.createElement('div'); 36 | div.innerText = text; 37 | return div.innerHTML; 38 | } 39 | 40 | tsp.log = function log(data) { 41 | const message = data.data.message; 42 | const type = data.type; 43 | const optionalParams = data.data.optionalParams; 44 | const params = optionalParams; 45 | params.unshift(message); 46 | if (data.log) { 47 | data.log.apply(this, params); 48 | } 49 | params.unshift(type); 50 | tsp.updateConsole.apply(this, params); 51 | } 52 | 53 | tsp.fadeOut = function fadeOut(target) { 54 | target.style.opacity = '1'; 55 | 56 | const fadeEffect = setInterval(() => { 57 | if (parseFloat(target.style.opacity) < 0.05) { 58 | clearInterval(fadeEffect); 59 | target.style.opacity = '0'; 60 | target.style.display = 'none'; 61 | } else { 62 | target.style.opacity = (parseFloat(target.style.opacity) - 0.02) + ''; 63 | } 64 | }, 5); 65 | } 66 | 67 | tsp.originalLog = console.log; 68 | console.log = function log(message, ...optionalParams) { 69 | tsp.log({ 70 | name: 'log', 71 | type: 'log', 72 | log: tsp.originalLog, 73 | data: { 74 | message, 75 | optionalParams 76 | } 77 | }); 78 | } 79 | 80 | tsp.originalInfo = console.info; 81 | console.info = function info(message, ...optionalParams) { 82 | tsp.log({ 83 | name: 'log', 84 | type: 'info', 85 | log: tsp.originalInfo, 86 | data: { 87 | message, 88 | optionalParams 89 | } 90 | }); 91 | } 92 | 93 | tsp.originalWarn = console.warn; 94 | console.warn = function warn(message, ...optionalParams) { 95 | tsp.log({ 96 | name: 'log', 97 | type: 'warn', 98 | log: tsp.originalWarn, 99 | data: { 100 | message, 101 | optionalParams 102 | } 103 | }); 104 | } 105 | 106 | tsp.originalError = console.error; 107 | console.error = function error(message, ...optionalParams) { 108 | tsp.log({ 109 | name: 'log', 110 | type: 'error', 111 | log: tsp.originalError, 112 | data: { 113 | message, 114 | optionalParams 115 | } 116 | }); 117 | } 118 | 119 | tsp.initialized = true; 120 | } 121 | 122 | window.onerror = function(message, url, lineNumber) { 123 | tsp.log({ 124 | name: 'error', 125 | type: 'error', 126 | // log: __originalError, 127 | data: { 128 | message, 129 | optionalParams: [] 130 | } 131 | }); 132 | } 133 | -------------------------------------------------------------------------------- /src/playground/style.less: -------------------------------------------------------------------------------- 1 | @import './styles/index'; 2 | -------------------------------------------------------------------------------- /src/playground/styles/grid.less: -------------------------------------------------------------------------------- 1 | .grid { 2 | margin: 0; 3 | 4 | [class*='col-'] { 5 | position: relative; 6 | float: left; 7 | padding-right: 20px; 8 | } 9 | 10 | [class*='col-']:last-of-type { 11 | padding-right: 0; 12 | } 13 | 14 | .col-1-1 { 15 | width: 100%; 16 | } 17 | 18 | .col-1-2 { 19 | width: 50%; 20 | } 21 | 22 | .col-1-3 { 23 | width: 33.33%; 24 | } 25 | 26 | .col-2-3 { 27 | width: 66.66%; 28 | } 29 | 30 | .col-1-4 { 31 | width: 25%; 32 | } 33 | 34 | .col-1-8 { 35 | width: 12.5%; 36 | } 37 | 38 | &:after { 39 | content: ""; 40 | display: table; 41 | clear: both; 42 | } 43 | } 44 | 45 | .grid-pad { 46 | padding: 20px 0 20px 20px; 47 | } 48 | 49 | .grid-pad > [class*='col-']:last-of-type { 50 | padding-right: 20px; 51 | } 52 | -------------------------------------------------------------------------------- /src/playground/styles/icons.less: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'icons'; 3 | src: url('assets/fonts/icons.ttf?ywd0ns') format('truetype'), url('assets/fonts/icons.woff?ywd0ns') format('woff'), url('assets/fonts/icons.svg?ywd0ns#icons') format('svg'); 4 | font-weight: normal; 5 | font-style: normal; 6 | } 7 | 8 | [class*=" icon-"], 9 | [class^="icon-"] { 10 | font-family: 'icons' !important; 11 | speak: none; 12 | font-style: normal; 13 | font-weight: normal; 14 | font-variant: normal; 15 | text-transform: none; 16 | line-height: 1; 17 | -webkit-font-smoothing: antialiased; 18 | -moz-osx-font-smoothing: grayscale; 19 | } 20 | 21 | .icon-info:before { 22 | content: "\e900"; 23 | } 24 | 25 | .icon-warn:before { 26 | content: "\e901"; 27 | } 28 | 29 | .icon-log:before { 30 | content: "\e902"; 31 | } 32 | 33 | .icon-error:before { 34 | content: "\e903"; 35 | } 36 | 37 | .icon-cog:before { 38 | content: "\e904"; 39 | } 40 | -------------------------------------------------------------------------------- /src/playground/styles/index.less: -------------------------------------------------------------------------------- 1 | @import (inline) '../../../node_modules/normalize.css/normalize.css'; 2 | @import './grid'; 3 | @import './icons'; 4 | @import './loaders'; 5 | @import './main'; 6 | @import './run'; 7 | -------------------------------------------------------------------------------- /src/playground/styles/loaders.less: -------------------------------------------------------------------------------- 1 | .loader { 2 | position: absolute; 3 | top: 50%; 4 | left: 50%; 5 | display: inline-block; 6 | width: 30px; 7 | height: 30px; 8 | border: 4px solid #Fff; 9 | animation: loader 2s infinite ease; 10 | 11 | .loader-inner { 12 | vertical-align: top; 13 | display: inline-block; 14 | width: 100%; 15 | background-color: #fff; 16 | animation: loader-inner 2s infinite ease-in; 17 | } 18 | } 19 | @keyframes loader { 20 | 0% { 21 | transform: rotate(0deg); 22 | } 23 | 24 | 25% { 25 | transform: rotate(180deg); 26 | } 27 | 28 | 50% { 29 | transform: rotate(180deg); 30 | } 31 | 32 | 75% { 33 | transform: rotate(360deg); 34 | } 35 | 36 | 100% { 37 | transform: rotate(360deg); 38 | } 39 | } 40 | @keyframes loader-inner { 41 | 0% { 42 | height: 0; 43 | } 44 | 45 | 25% { 46 | height: 0; 47 | } 48 | 49 | 50% { 50 | height: 100%; 51 | } 52 | 53 | 75% { 54 | height: 100%; 55 | } 56 | 57 | 100% { 58 | height: 0; 59 | } 60 | } 61 | 62 | .spinner { 63 | width: 30px; 64 | height: 30px; 65 | position: absolute; 66 | top: 50%; 67 | left: 50%; 68 | margin-left: -15px; 69 | margin-top: -15px; 70 | 71 | .double-bounce1, 72 | .double-bounce2 { 73 | width: 100%; 74 | height: 100%; 75 | border-radius: 50%; 76 | background-color: #efefef; 77 | opacity: 0.6; 78 | position: absolute; 79 | top: 0; 80 | left: 0; 81 | -webkit-animation: spinner 2.0s infinite ease-in-out; 82 | animation: spinner 2.0s infinite ease-in-out; 83 | } 84 | 85 | .double-bounce2 { 86 | -webkit-animation-delay: -1.0s; 87 | animation-delay: -1.0s; 88 | } 89 | } 90 | @-webkit-keyframes spinner { 91 | 0%, 92 | 100% { 93 | -webkit-transform: scale(0.0); 94 | } 95 | 96 | 50% { 97 | -webkit-transform: scale(1.0); 98 | } 99 | } 100 | @keyframes spinner { 101 | 0%, 102 | 100% { 103 | transform: scale(0.0); 104 | -webkit-transform: scale(0.0); 105 | } 106 | 107 | 50% { 108 | transform: scale(1.0); 109 | -webkit-transform: scale(1.0); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/playground/styles/main.less: -------------------------------------------------------------------------------- 1 | *, 2 | *:after, 3 | *:before { 4 | -webkit-box-sizing: border-box; 5 | -moz-box-sizing: border-box; 6 | box-sizing: border-box; 7 | } 8 | 9 | html { 10 | font-family: sans-serif; 11 | } 12 | 13 | body, 14 | html { 15 | height: 100%; 16 | } 17 | 18 | body { 19 | padding: 20px; 20 | } 21 | 22 | a { 23 | color: #333; 24 | text-decoration: none; 25 | } 26 | 27 | #loading { 28 | z-index: 1000; 29 | position: absolute; 30 | top: 0; 31 | right: 0; 32 | bottom: 0; 33 | left: 0; 34 | background-color: rgba(0, 0, 0, 0.8); 35 | } 36 | 37 | #header { 38 | padding-top: 0; 39 | padding-bottom: 0; 40 | 41 | #title { 42 | text-align: left; 43 | font-size: 15px; 44 | 45 | a { 46 | color: #000; 47 | } 48 | 49 | .icon-cog { 50 | cursor: pointer; 51 | vertical-align: middle; 52 | display: inline-block; 53 | margin-left: 5px; 54 | padding-bottom: 3px; 55 | color: #333; 56 | 57 | &.active { 58 | color: #000; 59 | } 60 | } 61 | } 62 | 63 | #options { 64 | position: absolute; 65 | display: none; 66 | margin-left: -18px; 67 | top: 25px; 68 | padding: 10px 20px; 69 | z-index: 1000; 70 | background-color: rgba(255, 255, 255, 1); 71 | border: #c3c3c3 1px solid; 72 | 73 | .info { 74 | font-size: 12px; 75 | } 76 | 77 | .option, .info { 78 | padding: 5px 0; 79 | } 80 | 81 | &.visible { 82 | display: inline-block; 83 | } 84 | 85 | h6 { 86 | padding: 0; 87 | border-bottom: 1px solid; 88 | padding-bottom: 2px; 89 | margin-top: 10px; 90 | margin-bottom: 10px; 91 | } 92 | 93 | input[type="text"] { 94 | outline: none; 95 | width: 50px; 96 | height: 19px; 97 | } 98 | } 99 | 100 | #github { 101 | text-align: right; 102 | font-size: 13px; 103 | } 104 | 105 | #ghlogo { 106 | vertical-align: bottom; 107 | display: inline-block; 108 | padding-bottom: 1px; 109 | height: 16px; 110 | } 111 | } 112 | 113 | #playground { 114 | height: 70%; 115 | padding-bottom: 0; 116 | 117 | .editor-container { 118 | height: 100%; 119 | 120 | .editor { 121 | height: 100%; 122 | border: 1px solid #eee; 123 | } 124 | 125 | .editor-footer { 126 | display: inline-block; 127 | position: absolute; 128 | z-index: 10; 129 | left: 1px; 130 | bottom: 1px; 131 | text-align: center; 132 | right: 21px; 133 | height: 25px; 134 | line-height: 1.7; 135 | font-size: 15px; 136 | } 137 | } 138 | 139 | #run-code { 140 | cursor: pointer; 141 | background-color: rgba(241, 241, 241, 1); 142 | color: #333; 143 | } 144 | 145 | #processing { 146 | display: none; 147 | position: absolute; 148 | z-index: 500; 149 | top: 1px; 150 | right: 15px; 151 | bottom: 1px; 152 | left: 1px; 153 | 154 | .spinner { 155 | width: 15px; 156 | height: 15px; 157 | position: absolute; 158 | top: 22px; 159 | left: 22px; 160 | } 161 | } 162 | } 163 | 164 | #console { 165 | padding-bottom: 0; 166 | 167 | #console-heading { 168 | border: 1px solid #eee; 169 | padding-top: 10px; 170 | padding-bottom: 10px; 171 | padding-left: 20px; 172 | border-bottom: none; 173 | font-size: 15px; 174 | } 175 | 176 | #console-content { 177 | border: 1px solid #eee; 178 | padding-bottom: 20px; 179 | 180 | .log-log { 181 | color: rgb(51, 51, 51); 182 | } 183 | 184 | .log-info { 185 | color: rgb(33, 83, 164); 186 | } 187 | 188 | .log-warn { 189 | color: rgb(240, 173, 78); 190 | } 191 | 192 | .log-error { 193 | color: rgb(217, 83, 79); 194 | } 195 | 196 | .log-error { 197 | color: rgb(217, 83, 79); 198 | } 199 | } 200 | 201 | .console { 202 | min-height: 100px; 203 | max-height: 200px; 204 | overflow-y: scroll; 205 | overflow-x: auto; 206 | white-space: pre; 207 | font-family: Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', Courier, monospace; 208 | line-height: 1.7; 209 | font-size: 13px; 210 | padding-left: 40px; 211 | padding-top: 20px; 212 | 213 | [class*=" icon-"], 214 | [class^="icon-"] { 215 | display: inline-block; 216 | margin-right: 10px; 217 | vertical-align: middle; 218 | padding-bottom: 1px; 219 | width: 1em; 220 | } 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /src/playground/styles/run.less: -------------------------------------------------------------------------------- 1 | #run-window { 2 | #loading-window { 3 | position: absolute; 4 | z-index: 500; 5 | top: 0; 6 | right: 0; 7 | bottom: 0; 8 | left: 0; 9 | 10 | .spinner { 11 | width: 30px; 12 | height: 30px; 13 | position: absolute; 14 | top: 50%; 15 | left: 50%; 16 | margin-left: -15px; 17 | margin-top: -15px; 18 | 19 | .double-bounce1, 20 | .double-bounce2 { 21 | background-color: #e4e2e2; 22 | } 23 | } 24 | } 25 | 26 | #console { 27 | height: 100%; 28 | 29 | [class*=col-]:last-of-type { 30 | height: 85%; 31 | } 32 | 33 | .console { 34 | min-height: 300px; 35 | max-height: none; 36 | height: 100%; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/playground/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "skipLibCheck": true, 5 | "experimentalDecorators": true, 6 | "sourceMap": true, 7 | "rootDir": "../", 8 | "declaration": false, 9 | "module": "commonjs", 10 | "target": "es5", 11 | "moduleResolution": "node", 12 | "lib": [ 13 | "es2015", 14 | "dom", 15 | "scripthost", 16 | "webworker" 17 | ] 18 | }, 19 | "include": [ 20 | "**/*" 21 | ], 22 | "files": [ 23 | "../../node_modules/monaco-typescript/src/lib/lib.ts" 24 | ], 25 | "typeRoots": [ 26 | "../../node_modules/@types", 27 | "./types" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /src/playground/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "skipLibCheck": true, 5 | "experimentalDecorators": true, 6 | "sourceMap": true, 7 | "rootDir": "../", 8 | "declaration": false, 9 | "module": "commonjs", 10 | "target": "es5", 11 | "moduleResolution": "node", 12 | "lib": [ 13 | "es2015", 14 | "dom", 15 | "scripthost", 16 | "webworker" 17 | ] 18 | }, 19 | "include": [ 20 | "**/*" 21 | ], 22 | "typeRoots": [ 23 | "../../node_modules/@types", 24 | "./types" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /src/playground/types/globals.d.ts: -------------------------------------------------------------------------------- 1 | declare var VERSION: string; 2 | declare var BUNDLE_NAME: string; 3 | declare var MONACO_VERSION: string; 4 | declare var MONACO_ENTRY: string; 5 | declare var MONACO_BASE: string; 6 | declare var MONACO_LOCATION: string; 7 | declare var MONACO_LOADER: string; 8 | -------------------------------------------------------------------------------- /src/playground/types/html-module/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.html" { 2 | const content: any; 3 | export = content; 4 | } 5 | -------------------------------------------------------------------------------- /src/playground/types/monaco.d.ts: -------------------------------------------------------------------------------- 1 | import * as monaco from 'monaco-editor'; 2 | 3 | declare global { 4 | interface Window { 5 | MonacoEnvironment?: monaco.Environment; 6 | require: MonacoLoader; 7 | monaco: typeof monaco; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/playground/types/worker-loader/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module "worker-loader*" { 2 | const content: any; 3 | export = content; 4 | } 5 | -------------------------------------------------------------------------------- /src/playground/worker.ts: -------------------------------------------------------------------------------- 1 | import { Options } from '../options'; 2 | import { FileReflection } from '../host'; 3 | import { transformReflection } from '../transform'; 4 | 5 | function sendMessage(message: any) { 6 | postMessage(JSON.parse(JSON.stringify(message)), void 0); 7 | } 8 | 9 | function transform(entryFiles: string[], reflections: FileReflection[], options: Options) { 10 | let transformed: FileReflection[]; 11 | let error: any; 12 | 13 | try { 14 | transformed = transformReflection( 15 | entryFiles, 16 | reflections, 17 | options 18 | ); 19 | } catch (e) { 20 | transformed = undefined; 21 | error = e; 22 | } 23 | 24 | sendMessage({ 25 | name: 'transformed', 26 | data: transformed, 27 | error: error 28 | }); 29 | 30 | if (error) { 31 | throw error; 32 | } 33 | } 34 | 35 | self.addEventListener('message', (message: any) => { 36 | if (message.data.name === 'transform') { 37 | transform(message.data.entryFiles, message.data.reflections, message.data.options); 38 | } 39 | }); 40 | 41 | console.log = function log(message: any, ...optionalParams: any[]) { 42 | sendMessage({ 43 | name: 'log', 44 | type: 'log', 45 | data: { 46 | message, 47 | optionalParams 48 | } 49 | }); 50 | }; 51 | 52 | console.info = function info(message: any, ...optionalParams: any[]) { 53 | sendMessage({ 54 | name: 'log', 55 | type: 'info', 56 | data: { 57 | message, 58 | optionalParams 59 | } 60 | }); 61 | }; 62 | 63 | console.warn = function warn(message: any, ...optionalParams: any[]) { 64 | sendMessage({ 65 | name: 'log', 66 | type: 'warn', 67 | data: { 68 | message, 69 | optionalParams 70 | } 71 | }); 72 | }; 73 | 74 | console.error = function error(message: any, ...optionalParams: any[]) { 75 | sendMessage({ 76 | name: 'log', 77 | type: 'error', 78 | data: { 79 | message, 80 | optionalParams 81 | } 82 | }); 83 | }; 84 | 85 | self.onerror = function onerror(this: WorkerGlobalScope, ev: ErrorEvent) { 86 | console.error(ev.toString().replace(/^(Uncaught )/, '')); 87 | } 88 | -------------------------------------------------------------------------------- /src/types/commondir/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'commondir'; 2 | -------------------------------------------------------------------------------- /src/types/path-browserify/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'path-browserify'; 2 | -------------------------------------------------------------------------------- /src/types/pretty-time/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'pretty-time'; 2 | -------------------------------------------------------------------------------- /src/util.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { ProgramError } from './errors'; 3 | 4 | export const LITERAL_KINDS = [ 5 | ts.SyntaxKind.LiteralType, 6 | ts.SyntaxKind.NumericLiteral, 7 | ts.SyntaxKind.StringLiteral, 8 | ts.SyntaxKind.TrueKeyword, 9 | ts.SyntaxKind.FalseKeyword 10 | ]; 11 | 12 | export const AMBIENT_KINDS = [ 13 | ts.SyntaxKind.InterfaceDeclaration, 14 | ts.SyntaxKind.TypeAliasDeclaration, 15 | ]; 16 | 17 | export function asArray(value: T | T[]): T[] { 18 | return Array.isArray(value) ? value : !value ? [] : [value]; 19 | } 20 | 21 | export function asNewArray(value: T | T[]): T[] { 22 | return Array.isArray(value) ? value.length > 0 ? [...value] : [] : !value ? [] : [value]; 23 | } 24 | 25 | export function arrayFromNodeArray(value: ts.NodeArray) { 26 | return !value ? [] : [...value]; 27 | } 28 | 29 | export function hasTypeParameters(node: ts.Node): boolean { 30 | return Array.isArray((node as any).typeParameters) && (node as any).typeParameters.length > 0; 31 | } 32 | 33 | export function hasProperty(obj: any, prop: string): boolean { 34 | return obj && obj.hasOwnProperty(prop); 35 | } 36 | 37 | export function hasArrayProperty(obj: any, prop: string): boolean { 38 | return hasProperty(obj, prop) && Array.isArray(obj[prop]); 39 | } 40 | 41 | export function hasNonEmptyArrayProperty(obj: any, prop: string): boolean { 42 | return hasArrayProperty(obj, prop) && obj[prop].length > 0; 43 | } 44 | 45 | export function hasKind(node: ts.Node, kind: ts.SyntaxKind): boolean { 46 | if (!node) { 47 | return false; 48 | } 49 | 50 | if (node.kind === kind) { 51 | return true; 52 | } 53 | 54 | return ts.forEachChild(node, n => hasKind(n, kind)) || false; 55 | } 56 | 57 | export function hasModifier(node: ts.Node, modifier: ts.SyntaxKind): boolean { 58 | for (let flag of node.modifiers || []) { 59 | if (flag.kind === modifier) { 60 | return true; 61 | } 62 | } 63 | 64 | return false; 65 | } 66 | 67 | export function hasFlag(node: ts.Node | ts.Symbol, flag: ts.NodeFlags | ts.SymbolFlags) { 68 | return !!(node.flags & flag); 69 | } 70 | 71 | export function setParent(node: ts.Node): ts.Node { 72 | if (!node) return node; 73 | 74 | ts.forEachChild(node, n => { 75 | // TODO: fix any cast 76 | (n.parent as any) = node; 77 | setParent(n); 78 | }); 79 | 80 | return node; 81 | } 82 | 83 | export function isAnyKeyword(node: ts.Node): boolean { 84 | if (!node) { 85 | return false; 86 | } 87 | 88 | if (node.kind === ts.SyntaxKind.AnyKeyword) { 89 | return true; 90 | } 91 | 92 | return false; 93 | } 94 | 95 | export function isSynthesized(node: ts.Node): boolean { 96 | return (node.flags & ts.NodeFlags.Synthesized) === ts.NodeFlags.Synthesized; 97 | } 98 | 99 | export function isAmbient(node: ts.Node): boolean { 100 | do { 101 | if (isKind(node, ...AMBIENT_KINDS)) { 102 | return true; 103 | } 104 | } while (node = node.parent); 105 | 106 | return false 107 | } 108 | 109 | export function isAmbientDeclaration(node: ts.Node): boolean { 110 | do { 111 | if (hasModifier(node, ts.SyntaxKind.DeclareKeyword)) { 112 | return true; 113 | } 114 | } while (node = node.parent); 115 | 116 | return false; 117 | } 118 | 119 | export function isPartOfTypeNode(node: ts.Node) { 120 | while (node && node.parent) { 121 | if (ts.isTypeNode(node)) { 122 | return true; 123 | } 124 | 125 | node = node.parent; 126 | } 127 | 128 | return false; 129 | } 130 | 131 | export function isBindingPattern(node: ts.Node): node is ts.BindingPattern { 132 | return ts.isArrayBindingPattern(node) || ts.isObjectBindingPattern(node); 133 | } 134 | 135 | export function isStatic(node: ts.Node): boolean { 136 | return !Array.isArray(node.modifiers) ? false : node.modifiers.findIndex((el: any) => el.kind === ts.SyntaxKind.StaticKeyword) !== -1; 137 | } 138 | 139 | export function isTypeParameter(node: ts.TypeReferenceNode): boolean { 140 | const nodeName = getEntityNameText(node.typeName); 141 | 142 | while (node = node.parent as any) { 143 | if ((node as any).typeParameters && (node as any).typeParameters.length > 0) { 144 | if ((node as any).typeParameters.find((param: ts.TypeParameterDeclaration) => { 145 | return param.name.text === nodeName; 146 | })) { 147 | return true; 148 | } 149 | } 150 | } 151 | 152 | return false; 153 | } 154 | 155 | export function isTypeParameterOf(node: ts.TypeNode, typeParameters: ts.TypeParameterDeclaration[]) { 156 | if (!ts.isTypeReferenceNode(node)) { 157 | return false; 158 | } 159 | 160 | const nodeName = getEntityNameText(node.typeName); 161 | 162 | for (let typeParameter of typeParameters) { 163 | 164 | if (typeParameter.name.text === nodeName) { 165 | return true; 166 | } 167 | } 168 | 169 | return false; 170 | } 171 | 172 | export function isTypeParameterOfClass(node: ts.TypeReferenceNode): ts.ClassDeclaration { 173 | let current = node as ts.Node; 174 | while (current = current.parent) { 175 | if (ts.isClassDeclaration(current)) { 176 | if (isTypeParameterOf(node, arrayFromNodeArray(current.typeParameters))) { 177 | return current; 178 | } 179 | 180 | return null; 181 | } 182 | } 183 | 184 | return null; 185 | } 186 | 187 | export function isInStaticContext(node: ts.TypeReferenceNode): boolean { 188 | let current = node as ts.Node; 189 | while (current = current.parent) { 190 | if (isStatic(current)) { 191 | return true; 192 | } 193 | } 194 | 195 | return false; 196 | } 197 | 198 | export function isSuperStatement(node: ts.Node): boolean { 199 | return ts.isExpressionStatement(node) && ts.isCallExpression(node.expression) && 200 | node.expression.expression.kind === ts.SyntaxKind.SuperKeyword; 201 | } 202 | 203 | export function isKind(node: ts.Node, ...kind: ts.SyntaxKind[]): boolean { 204 | return kind.indexOf(node.kind) !== -1; 205 | } 206 | 207 | export function isBindingName(node: ts.Node): node is ts.Identifier | ts.BindingPattern | ts.ArrayBindingPattern { 208 | return ts.isIdentifier(node) || this.isBindingPattern(node); 209 | } 210 | 211 | export function isLiteral(node: ts.Node): node is ts.LiteralTypeNode | ts.NumericLiteral | ts.BooleanLiteral | ts.StringLiteral { 212 | return LITERAL_KINDS.indexOf(node.kind) !== -1; 213 | } 214 | 215 | export function getHash(str: string): number { 216 | var hash = 5381, i = str.length; 217 | 218 | while (i) { 219 | hash = (hash * 33) ^ str.charCodeAt(--i); 220 | } 221 | 222 | return hash >>> 0; 223 | } 224 | 225 | export function getHashedDeclarationName(name: string, fileName: string) { 226 | return `${name}.${getHash(fileName)}`; 227 | } 228 | 229 | export function getIdentifierOfEntityName(node: ts.EntityName): ts.Identifier { 230 | if (ts.isIdentifier(node)) { 231 | return node; 232 | } 233 | 234 | return node.right; 235 | } 236 | 237 | export function getBaseIdentifierOfEntityName(node: ts.EntityName): ts.Identifier { 238 | while (ts.isQualifiedName(node)) { 239 | node = node.left; 240 | } 241 | 242 | return node; 243 | } 244 | 245 | export function getExtendsClause(node: ts.InterfaceDeclaration | ts.ClassDeclaration): ts.HeritageClause { 246 | return node.heritageClauses && node.heritageClauses.find(clause => { 247 | if (clause.token === ts.SyntaxKind.ExtendsKeyword) { 248 | return true; 249 | } 250 | }); 251 | } 252 | 253 | export function getImplementsClause(node: ts.InterfaceDeclaration | ts.ClassDeclaration): ts.HeritageClause { 254 | return node.heritageClauses && node.heritageClauses.find(clause => { 255 | if (clause.token === ts.SyntaxKind.ImplementsKeyword) { 256 | return true; 257 | } 258 | }); 259 | } 260 | 261 | export function getExpression(node: ts.Node): ts.Expression { 262 | while ((node as any).expression) { 263 | node = (node as any).expression; 264 | } 265 | 266 | return node as ts.Expression; 267 | } 268 | 269 | export function getPropertyAccessExpressionTextOrFail(node: ts.PropertyAccessExpression): string { 270 | let text = ''; 271 | 272 | while (ts.isPropertyAccessExpression(node)) { 273 | text += `.${node.name.text}`; 274 | node = node.expression as ts.PropertyAccessExpression; 275 | } 276 | 277 | if ((node as ts.Node).kind !== ts.SyntaxKind.Identifier) { 278 | throw new ProgramError('Can\'t get text of property access expression that contains other expressions than property access expression.'); 279 | } 280 | 281 | text = `${(node as ts.Identifier).text}${text}`; 282 | 283 | return text; 284 | } 285 | 286 | export function getIdentifierOfPropertyAccessExpressionOrFail(node: ts.PropertyAccessExpression): ts.Identifier { 287 | let expression = node.expression; 288 | 289 | while (expression.kind === ts.SyntaxKind.PropertyAccessExpression) { 290 | expression = (expression as ts.PropertyAccessExpression).expression; 291 | } 292 | 293 | if (expression.kind !== ts.SyntaxKind.Identifier) { 294 | throw new ProgramError('Can\'t get identifier of property access expression that contains other expressions than property access expression.'); 295 | } 296 | 297 | return expression as ts.Identifier; 298 | } 299 | 300 | export function getEntityNameText(node: ts.EntityName): string { 301 | if (ts.isIdentifier(node)) { 302 | return node.text; 303 | } 304 | 305 | const left = getEntityNameText(node.left) 306 | const right = node.right.text; 307 | 308 | return `${left}.${right}`; 309 | } 310 | 311 | export function insertBeforeSuper(statements: ts.Statement[], insert: ts.Statement | ts.Statement[], offset = 0): ts.Statement[] { 312 | const index = statements.findIndex(statement => isSuperStatement(statement)); 313 | 314 | insert = asArray(insert); 315 | 316 | if (index !== -1) { 317 | statements.splice(index + offset, 0, ...insert) 318 | } else { 319 | statements.unshift(...insert); 320 | // statements.splice(statements.length, 0, ...insert); 321 | } 322 | 323 | return statements; 324 | } 325 | 326 | export function insertAfterSuper(statements: ts.Statement[], insert: ts.Statement | ts.Statement[], offset = 0): ts.Statement[] { 327 | return this.insertBeforeSuper(statements, insert, 1); 328 | } 329 | 330 | export function declarationCanHaveTypeAnnotation(node: ts.Node) { 331 | let current: ts.Node = node; 332 | 333 | if (ts.isVariableDeclaration(current) && current.parent) { 334 | current = current.parent; 335 | } 336 | 337 | if (ts.isVariableDeclarationList(current) && current.parent) { 338 | current = current.parent; 339 | } 340 | 341 | switch (current.kind) { 342 | case ts.SyntaxKind.ForOfStatement: 343 | case ts.SyntaxKind.ForInStatement: 344 | case ts.SyntaxKind.CatchClause: 345 | case ts.SyntaxKind.ImportClause: 346 | return false; 347 | } 348 | 349 | return true; 350 | } 351 | 352 | export function extendsClauseHasTypeArguments(node: ts.HeritageClause): boolean { 353 | return node && node.types && node.types[0] && node.types[0].typeArguments && node.types[0].typeArguments.length > 0; 354 | } 355 | 356 | export function canHaveType(node: any): node is { type: ts.TypeNode } { 357 | if (!node) { 358 | return false; 359 | } 360 | 361 | switch (node.kind) { 362 | case ts.SyntaxKind.VariableDeclaration: 363 | case ts.SyntaxKind.ObjectBindingPattern: 364 | case ts.SyntaxKind.ArrayBindingPattern: 365 | case ts.SyntaxKind.Parameter: 366 | case ts.SyntaxKind.PropertySignature: 367 | case ts.SyntaxKind.PropertyDeclaration: 368 | case ts.SyntaxKind.MethodSignature: 369 | case ts.SyntaxKind.CallSignature: 370 | case ts.SyntaxKind.ConstructSignature: 371 | case ts.SyntaxKind.IndexSignature: 372 | case ts.SyntaxKind.MethodDeclaration: 373 | case ts.SyntaxKind.GetAccessor: 374 | case ts.SyntaxKind.FunctionExpression: 375 | case ts.SyntaxKind.ArrowFunction: 376 | case ts.SyntaxKind.FunctionDeclaration: 377 | return true; 378 | } 379 | 380 | return false; 381 | } 382 | 383 | export function annotateWithAny(node: ts.Node): boolean { 384 | switch (node.kind) { 385 | case ts.SyntaxKind.VariableDeclaration: 386 | if (!declarationCanHaveTypeAnnotation(node)) { 387 | return false; 388 | } 389 | case ts.SyntaxKind.ObjectBindingPattern: 390 | case ts.SyntaxKind.ArrayBindingPattern: 391 | case ts.SyntaxKind.Parameter: 392 | case ts.SyntaxKind.PropertySignature: 393 | case ts.SyntaxKind.PropertyDeclaration: 394 | case ts.SyntaxKind.MethodSignature: 395 | case ts.SyntaxKind.CallSignature: 396 | case ts.SyntaxKind.ConstructSignature: 397 | case ts.SyntaxKind.IndexSignature: 398 | case ts.SyntaxKind.MethodDeclaration: 399 | case ts.SyntaxKind.GetAccessor: 400 | case ts.SyntaxKind.FunctionExpression: 401 | case ts.SyntaxKind.ArrowFunction: 402 | case ts.SyntaxKind.FunctionDeclaration: 403 | (node as any).type = ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword); 404 | (node as any).type.parent = node; 405 | return true; 406 | } 407 | 408 | return false; 409 | } 410 | -------------------------------------------------------------------------------- /test/components/index.test.ts: -------------------------------------------------------------------------------- 1 | import context from './context'; 2 | import factory from './factory'; 3 | import scanner from './scanner'; 4 | import transform from './transform'; 5 | import util from './util'; 6 | 7 | describe('Components', () => { 8 | context(); 9 | factory(); 10 | scanner(); 11 | transform(); 12 | util(); 13 | }); 14 | -------------------------------------------------------------------------------- /test/components/scanner.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Scanner, TypeInfo } from '../../src/scanner'; 3 | import { MutationContext } from '../../src/context'; 4 | 5 | export default () => { 6 | describe('Scanner', () => { 7 | let input = `let foo: string = "bar";`; 8 | let context: MutationContext; 9 | let scanner: Scanner; 10 | let sf: ts.SourceFile; 11 | let id: ts.Identifier; 12 | 13 | beforeEach(() => { 14 | input = ` 15 | let foo: string = "bar"; 16 | let bar: string = "bar"; 17 | `; 18 | const host = util.host(input); 19 | context = util.context(util.program(host), host); 20 | sf = context.sourceFile; 21 | scanner = context.scanner; 22 | id = (sf.statements[0] as ts.VariableStatement).declarationList.declarations[0].name as ts.Identifier; 23 | }); 24 | 25 | describe('#scan', () => { 26 | it('should scan all source files', () => { 27 | scanner.scan(); 28 | }); 29 | }); 30 | 31 | describe('#mapNode', () => { 32 | it('should map a node to another node', () => { 33 | const result = scanner.mapNode(sf.statements[0], sf.statements[1]); 34 | expect(result).ok(); 35 | }); 36 | 37 | it('should be possible to retrieve a mapped node', () => { 38 | const result = scanner.mapNode(sf.statements[0], sf.statements[1]); 39 | const node1 = scanner.getNode(sf.statements[0]); 40 | const node2 = scanner.getNode(sf.statements[0]); 41 | expect(node1).to.equal(node2); 42 | expect(node1).to.equal(sf.statements[1]); 43 | expect(node2).to.equal(sf.statements[1]); 44 | }); 45 | }); 46 | 47 | describe('#getTypeInfo', () => { 48 | it('should retrieve the type info for a node', () => { 49 | const result = scanner.getTypeInfo(id); 50 | expect(result).ok(); 51 | }); 52 | }); 53 | 54 | describe('#getNodeSymbol', () => { 55 | it('should retrieve the symbol od a node', () => { 56 | const result = scanner.getNodeSymbol(id); 57 | expect(result).ok(); 58 | }); 59 | }); 60 | 61 | describe('#hasNodeSymbol', () => { 62 | it('should check of a symbol is available for a node', () => { 63 | const result = scanner.getNodeSymbol(id); 64 | expect(result).ok(); 65 | }); 66 | }); 67 | 68 | describe('#hasTypeInfo', () => { 69 | it('should check if a type info is available for a node', () => { 70 | const result = scanner.hasTypeInfo(id); 71 | expect(result).to.be(true); 72 | }); 73 | }); 74 | 75 | describe('#getNode', () => { 76 | it('should get a node', () => { 77 | const result = scanner.getNode(id); 78 | expect(result).ok(); 79 | }); 80 | }); 81 | 82 | describe('#getAliasedNode', () => { 83 | it('should get an aliased node', () => { 84 | const result = scanner.getAliasedNode(id); 85 | expect(result).ok(); 86 | }); 87 | }); 88 | 89 | describe('#getSymbol', () => { 90 | it('should get the symbol by a node\'s type and the node', () => { 91 | const result = scanner.getSymbol(scanner.getType(id), id); 92 | expect(result).ok(); 93 | }); 94 | }); 95 | 96 | describe('#getDeclarations', () => { 97 | it('should get the ambient and external declarations', () => { 98 | const result = scanner.getDeclarations() 99 | expect(result).to.be.an('array'); 100 | }); 101 | }); 102 | 103 | describe('#getIdentifiers', () => { 104 | it('should get all identifiers of a source file', () => { 105 | const result = scanner.getIdentifiers(sf) 106 | expect(result.has('foo')).to.be(true); 107 | expect(result.has('bar')).to.be(true); 108 | expect(result.has('foobar')).to.be(false); 109 | }); 110 | }); 111 | 112 | describe('#isAllowedDeclarationSymbol', () => { 113 | it('should check if a symbol is an allowed ambient or external declaration', () => { 114 | const result = scanner.isAllowedDeclarationSymbol(scanner.getNodeSymbol(id)) 115 | expect(result).to.be(true); 116 | }); 117 | }); 118 | 119 | describe('#hasDeclarations', () => { 120 | it('should check if a symbol has declarations', () => { 121 | const result = scanner.hasDeclarations(scanner.getNodeSymbol(id)) 122 | expect(result).to.be(true); 123 | }); 124 | }); 125 | 126 | describe('#pathIsExternal', () => { 127 | it('should check if a path is external', () => { 128 | const result = scanner.pathIsExternal(sf.fileName) 129 | expect(result).to.be(false); 130 | }); 131 | }); 132 | 133 | describe('#getType', () => { 134 | it('should retrieve the type of a node', () => { 135 | const result = scanner.getType(id) 136 | expect(result).ok(); 137 | }); 138 | }); 139 | 140 | describe('#getTypeNode', () => { 141 | it('should retrieve the (synthesized) type node of a node', () => { 142 | const result = scanner.getTypeNode(id, scanner.getType(id)); 143 | expect(result).ok(); 144 | }); 145 | }); 146 | 147 | describe('#getBaseType', () => { 148 | it('should retrieve the base type of a type', () => { 149 | const result = scanner.getBaseType(scanner.getType(id)); 150 | expect(result).ok(); 151 | }); 152 | }); 153 | 154 | describe('Type Info', () => { 155 | let info: TypeInfo; 156 | 157 | beforeEach(() => { 158 | info = scanner.getTypeInfo(id); 159 | }); 160 | 161 | describe('#isTsrDeclaration', () => { 162 | it('should check if it is a TSR declaration', () => { 163 | const result = info.isTsrDeclaration(); 164 | expect(result).to.be(false); 165 | }); 166 | }); 167 | 168 | describe('#hasDeclarations', () => { 169 | it('should check if it has declarations', () => { 170 | const result = info.hasDeclarations(); 171 | expect(result).to.be(true); 172 | }); 173 | }); 174 | 175 | describe('#symbol', () => { 176 | it('should retrieve the symbol', () => { 177 | const result = info.symbol; 178 | expect(result).ok(); 179 | }); 180 | }); 181 | 182 | describe('#enclosing', () => { 183 | it('should retrieve the enclosing node', () => { 184 | const result = info.enclosing; 185 | expect(result).ok(); 186 | }); 187 | }); 188 | 189 | describe('#sourceFiles', () => { 190 | it('should retrieve the source files of its declarations', () => { 191 | const result = info.sourceFiles; 192 | expect(result).to.be.an('array'); 193 | }); 194 | }); 195 | 196 | describe('#fileName', () => { 197 | it('should retrieve the source file name', () => { 198 | const result = info.fileNames; 199 | expect(result).to.be.an('array'); 200 | }); 201 | }); 202 | 203 | describe('#declarations', () => { 204 | it('should retrieve all declarations', () => { 205 | const result = info.declarations; 206 | expect(result).to.be.an('array'); 207 | }); 208 | }); 209 | 210 | describe('#type', () => { 211 | it('should retrieve the type', () => { 212 | const result = info.type; 213 | expect(result).ok(); 214 | }); 215 | }); 216 | 217 | describe('#typeText', () => { 218 | it('should retrieve the type text', () => { 219 | const result = info.typeText; 220 | expect(result).to.be.a('string'); 221 | }); 222 | }); 223 | 224 | describe('#typeNode', () => { 225 | it('should retrieve the type node', () => { 226 | const result = info.typeNode; 227 | expect(result).ok(); 228 | }); 229 | }); 230 | 231 | describe('#baseType', () => { 232 | it('should retrieve the base tyoe', () => { 233 | const result = info.baseType; 234 | expect(result).to.be(undefined); 235 | }); 236 | }); 237 | 238 | describe('#baseTypeNode', () => { 239 | it('should retrieve the base type node', () => { 240 | const result = info.baseTypeNode; 241 | expect(result).to.be(undefined); 242 | }); 243 | }); 244 | 245 | describe('#baseTypeText', () => { 246 | it('should retrieve the base type text', () => { 247 | const result = info.baseTypeText; 248 | expect(result).to.be(undefined); 249 | }); 250 | }); 251 | 252 | describe('#isSynthesized', () => { 253 | it('should check if it is synthesized', () => { 254 | const result = info.isSynthesized; 255 | expect(result).to.be(true); 256 | }); 257 | }); 258 | 259 | describe('#isReference', () => { 260 | it('should check if it is a reference', () => { 261 | const result = info.isReference; 262 | expect(result).to.be(false); 263 | }); 264 | }); 265 | 266 | describe('#isLiteral', () => { 267 | it('should check if it is a literal', () => { 268 | const result = info.isLiteral; 269 | expect(result).to.be(false); 270 | }); 271 | }); 272 | 273 | describe('#isAmbient', () => { 274 | it('should check if it is ambient', () => { 275 | const result = info.isAmbient; 276 | expect(result).to.be(false); 277 | }); 278 | }); 279 | 280 | describe('#isDeclaration', () => { 281 | it('should check if it is an ambient declaration', () => { 282 | const result = info.isDeclaration; 283 | expect(result).to.be(false); 284 | }); 285 | }); 286 | 287 | describe('#isInDeclarationFile', () => { 288 | it('should check if it is in a declaration file', () => { 289 | const result = info.isInDeclarationFile; 290 | expect(result).to.be(false); 291 | }); 292 | }); 293 | 294 | describe('#isExternal', () => { 295 | it('should check if it is external', () => { 296 | const result = info.isExternal; 297 | expect(result).to.be(false); 298 | }); 299 | }); 300 | 301 | describe('#isExternalModule', () => { 302 | it('should check if it is an external module', () => { 303 | const result = info.isExternalModule; 304 | expect(result).to.be(false); 305 | }); 306 | }); 307 | }); 308 | }); 309 | }; 310 | -------------------------------------------------------------------------------- /test/components/transform.ts: -------------------------------------------------------------------------------- 1 | export default () => { 2 | describe('Transform', () => { 3 | it('should transform a file reflection', () => { 4 | const input = `let foo: string = 'bar'`; 5 | const result = util.transform(input); 6 | expect(result).ok(); 7 | }); 8 | 9 | it('should enable the preserveConstEnums option', () => { 10 | const input = `const enum Foo { }`; 11 | 12 | const result = util.transform(input, { compilerOptions: { preserveConstEnums: false } }); 13 | 14 | const expected = ` 15 | import t from "ts-runtime/lib"; 16 | 17 | var Foo; 18 | (function (Foo) { 19 | })(Foo || (Foo = {})); 20 | t.annotate(Foo, t.enum());`; 21 | 22 | util.compare(result, expected); 23 | }); 24 | 25 | it('should enable the experimentalDecorators option', () => { 26 | const input = ``; 27 | const result = util.transform(input, { compilerOptions: { experimentalDecorators: false } }); 28 | }); 29 | }); 30 | }; 31 | -------------------------------------------------------------------------------- /test/known_issues/constructor_type_node.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Factory } from '../../src/factory'; 3 | 4 | export default () => { 5 | // function foo(): new () => any; 6 | describe('ConstructorTypeNode', () => { 7 | let input = `let foo: string = "bar";`; 8 | let factory: Factory; 9 | 10 | beforeEach(() => { 11 | const host = util.host(input); 12 | const context = util.context(util.program(host), host); 13 | factory = context.factory; 14 | }); 15 | 16 | it('should reflect ConstructorTypeNode', () => { 17 | const type = ts.createConstructorTypeNode( 18 | [], 19 | [], 20 | ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword) 21 | ); 22 | 23 | util.expectError(() => factory.typeReflection(type)); 24 | }); 25 | }); 26 | }; 27 | -------------------------------------------------------------------------------- /test/known_issues/index.test.ts: -------------------------------------------------------------------------------- 1 | import constructor_type_node from './constructor_type_node'; 2 | 3 | describe('Known Issues', () => { 4 | constructor_type_node(); 5 | }); 6 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --compilers ts-node/register 2 | --require source-map-support/register 3 | --require ./test/setup.ts 4 | --ui bdd 5 | --timeout 3000 6 | --slow 1000 7 | --inline-diffs 8 | --recursive 9 | ./test/**/*.test.ts 10 | -------------------------------------------------------------------------------- /test/setup.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import expect = require('expect.js'); 4 | import * as assert from 'assert'; 5 | import * as tsr from '../src/index'; 6 | import * as util from './util'; 7 | 8 | global.expect = expect; 9 | global.assert = assert; 10 | global.tsr = tsr; 11 | global.util = util; 12 | -------------------------------------------------------------------------------- /test/transformation/arrow_function.ts: -------------------------------------------------------------------------------- 1 | export default () => { 2 | describe('Arrow Function', () => { 3 | it('should be annotated', () => { 4 | const input = `() => void 0`; 5 | 6 | const expected = ` 7 | import t from "ts-runtime/lib"; 8 | 9 | t.annotate(() => { 10 | return void 0; 11 | }, t.function( 12 | t.return(t.any())) 13 | );`; 14 | 15 | const result = util.transform(input); 16 | 17 | util.compare(expected, result); 18 | }); 19 | 20 | it('should not be annotated with noAnnotate', () => { 21 | const input = `() => void 0`; 22 | 23 | const expected = ` 24 | () => { 25 | return void 0; 26 | }; 27 | 28 | export {};`; 29 | 30 | const result = util.transform(input, { noAnnotate: true }); 31 | 32 | util.compare(expected, result); 33 | }); 34 | 35 | it('should assert the return type', () => { 36 | const input = `(): string => "Foo"`; 37 | 38 | const expected = ` 39 | import t from "ts-runtime/lib"; 40 | 41 | () => { 42 | const _returnType = t.return(t.string()); 43 | return _returnType.assert("Foo"); 44 | };`; 45 | 46 | const result = util.transform(input, { noAnnotate: true }); 47 | 48 | util.compare(expected, result); 49 | }); 50 | 51 | it('should assert multiple returns', () => { 52 | const input = ` 53 | (): string => { 54 | if (true === true) { 55 | return "Bar"; 56 | } 57 | 58 | [1, 2, 3].map(val => { 59 | return 1; 60 | }); 61 | 62 | return "Foo"; 63 | }`; 64 | 65 | const expected = ` 66 | import t from "ts-runtime/lib"; 67 | 68 | () => { 69 | const _returnType = t.return(t.string()); 70 | 71 | if (true === true) { 72 | return _returnType.assert("Bar"); 73 | } 74 | 75 | [1, 2, 3].map((val) => { 76 | return 1; 77 | }); 78 | 79 | return _returnType.assert("Foo"); 80 | };`; 81 | 82 | const result = util.transform(input, { noAnnotate: true }); 83 | 84 | util.compare(expected, result); 85 | }); 86 | 87 | it('should assert parameters', () => { 88 | const input = `(param1: boolean, param2: number): string => "Foo"`; 89 | 90 | const expected = ` 91 | import t from "ts-runtime/lib"; 92 | 93 | (param1, param2) => { 94 | let _param1Type = t.boolean(); 95 | let _param2Type = t.number(); 96 | const _returnType = t.return(t.string()); 97 | t.param("param1", _param1Type).assert(param1); 98 | t.param("param2", _param2Type).assert(param2); 99 | return _returnType.assert("Foo"); 100 | };`; 101 | 102 | const result = util.transform(input, { noAnnotate: true }); 103 | 104 | util.compare(expected, result); 105 | }); 106 | 107 | it('should assert optional parameters', () => { 108 | const input = `(param?: string) => void 0`; 109 | 110 | const expected = ` 111 | import t from "ts-runtime/lib"; 112 | 113 | (param) => { 114 | let _paramType = t.string(); 115 | t.param("param", _paramType, true).assert(param); 116 | return void 0; 117 | };`; 118 | 119 | const result = util.transform(input, { noAnnotate: true }); 120 | 121 | util.compare(expected, result); 122 | }); 123 | 124 | it('should assert rest parameters', () => { 125 | const input = `(...params: string[]) => void 0`; 126 | 127 | const expected = ` 128 | import t from "ts-runtime/lib"; 129 | 130 | (...params) => { 131 | let _paramsType = t.array(t.string()); 132 | t.rest("params", _paramsType).assert(params); 133 | return void 0; 134 | };`; 135 | 136 | const result = util.transform(input, { noAnnotate: true }); 137 | 138 | util.compare(expected, result); 139 | }); 140 | 141 | it('should assert type parameters', () => { 142 | const input = ` 143 | (param: T): T => { 144 | return param; 145 | }`; 146 | 147 | const expected = ` 148 | import t from "ts-runtime/lib"; 149 | 150 | (param) => { 151 | const T = t.typeParameter("T"); 152 | let _paramType = t.flowInto(T); 153 | const _returnType = t.return(T); 154 | t.param("param", _paramType).assert(param); 155 | return _returnType.assert(param); 156 | };`; 157 | 158 | const result = util.transform(input, { noAnnotate: true }); 159 | 160 | util.compare(expected, result); 161 | }); 162 | }); 163 | } 164 | -------------------------------------------------------------------------------- /test/transformation/as_expression.ts: -------------------------------------------------------------------------------- 1 | export default () => { 2 | describe('As Expression', () => { 3 | it('should be asserted', () => { 4 | const input = `10 as any as string`; 5 | 6 | const expected = ` 7 | import t from "ts-runtime/lib"; 8 | 9 | t.string().assert(10);`; 10 | 11 | const result = util.transform(input); 12 | 13 | util.compare(expected, result); 14 | }); 15 | 16 | it('should not be asserted if any', () => { 17 | const input = `10 as any`; 18 | 19 | const expected = `10; 20 | 21 | export {};`; 22 | 23 | const result = util.transform(input); 24 | 25 | util.compare(expected, result); 26 | }); 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /test/transformation/binary_expression.ts: -------------------------------------------------------------------------------- 1 | export default () => { 2 | describe('Binary Expression', () => { 3 | it('should not assert non-assignment operators', () => { 4 | const input = ` 5 | let a: boolean = true; 6 | let b: boolean = false; 7 | a && b; 8 | `; 9 | 10 | const expected = ` 11 | import t from "ts-runtime/lib"; 12 | let _aType = t.boolean(), a = _aType.assert(true); 13 | let _bType = t.boolean(), b = _bType.assert(false); 14 | a && b;`; 15 | 16 | const result = util.transform(input); 17 | 18 | util.compare(expected, result); 19 | }); 20 | 21 | it('should assert assignments', () => { 22 | const input = ` 23 | let foo: string; 24 | foo = "bar"; 25 | `; 26 | 27 | const expected = ` 28 | import t from "ts-runtime/lib"; 29 | 30 | let _fooType = t.string(), foo; 31 | foo = _fooType.assert("bar");`; 32 | 33 | const result = util.transform(input); 34 | 35 | util.compare(expected, result); 36 | }); 37 | 38 | it('should assert addition assignments', () => { 39 | const input = ` 40 | let foo: number = 1; 41 | foo += 1; 42 | `; 43 | 44 | const expected = ` 45 | import t from "ts-runtime/lib"; 46 | 47 | let _fooType = t.number(), foo = _fooType.assert(1); 48 | foo += _fooType.assert(1);`; 49 | 50 | const result = util.transform(input); 51 | 52 | util.compare(expected, result); 53 | }); 54 | 55 | it('should assert substraction assignments', () => { 56 | const input = ` 57 | let foo: number = 1; 58 | foo -= 1; 59 | `; 60 | 61 | const expected = ` 62 | import t from "ts-runtime/lib"; 63 | 64 | let _fooType = t.number(), foo = _fooType.assert(1); 65 | foo -= _fooType.assert(1);`; 66 | 67 | const result = util.transform(input); 68 | 69 | util.compare(expected, result); 70 | }); 71 | 72 | it('should assert multiplication assignments', () => { 73 | const input = ` 74 | let foo: number = 1; 75 | foo *= 1; 76 | `; 77 | 78 | const expected = ` 79 | import t from "ts-runtime/lib"; 80 | 81 | let _fooType = t.number(), foo = _fooType.assert(1); 82 | foo *= _fooType.assert(1);`; 83 | 84 | const result = util.transform(input); 85 | 86 | util.compare(expected, result); 87 | }); 88 | 89 | it('should assert division assignments', () => { 90 | const input = ` 91 | let foo: number = 1; 92 | foo /= 1; 93 | `; 94 | 95 | const expected = ` 96 | import t from "ts-runtime/lib"; 97 | 98 | let _fooType = t.number(), foo = _fooType.assert(1); 99 | foo /= _fooType.assert(1);`; 100 | 101 | const result = util.transform(input); 102 | 103 | util.compare(expected, result); 104 | }); 105 | 106 | it('should assert remainder assignments', () => { 107 | const input = ` 108 | let foo: number = 1; 109 | foo %= 1; 110 | `; 111 | 112 | const expected = ` 113 | import t from "ts-runtime/lib"; 114 | 115 | let _fooType = t.number(), foo = _fooType.assert(1); 116 | foo %= _fooType.assert(1);`; 117 | 118 | const result = util.transform(input); 119 | 120 | util.compare(expected, result); 121 | }); 122 | 123 | it('should assert exponentiation assignments', () => { 124 | const input = ` 125 | let foo: number = 1; 126 | foo **= 1; 127 | `; 128 | 129 | const expected = ` 130 | import t from "ts-runtime/lib"; 131 | 132 | let _fooType = t.number(), foo = _fooType.assert(1); 133 | foo = Math.pow(foo, _fooType.assert(1));`; 134 | 135 | const result = util.transform(input); 136 | 137 | util.compare(expected, result); 138 | }); 139 | 140 | it('should assert left shift assignments', () => { 141 | const input = ` 142 | let foo: number = 1; 143 | foo <<= 1; 144 | `; 145 | 146 | const expected = ` 147 | import t from "ts-runtime/lib"; 148 | 149 | let _fooType = t.number(), foo = _fooType.assert(1); 150 | foo <<= _fooType.assert(1);`; 151 | 152 | const result = util.transform(input); 153 | 154 | util.compare(expected, result); 155 | }); 156 | 157 | it('should assert right shift assignments', () => { 158 | const input = ` 159 | let foo: number = 1; 160 | foo >>= 1; 161 | `; 162 | 163 | const expected = ` 164 | import t from "ts-runtime/lib"; 165 | 166 | let _fooType = t.number(), foo = _fooType.assert(1); 167 | foo >>= _fooType.assert(1);`; 168 | 169 | const result = util.transform(input); 170 | 171 | util.compare(expected, result); 172 | }); 173 | 174 | it('should assert unsigned right shift assignments', () => { 175 | const input = ` 176 | let foo: number = 1; 177 | foo >>>= 1; 178 | `; 179 | 180 | const expected = ` 181 | import t from "ts-runtime/lib"; 182 | 183 | let _fooType = t.number(), foo = _fooType.assert(1); 184 | foo >>>= _fooType.assert(1);`; 185 | 186 | const result = util.transform(input); 187 | 188 | util.compare(expected, result); 189 | }); 190 | 191 | it('should assert bitwise AND assignments', () => { 192 | const input = ` 193 | let foo: number = 1; 194 | foo &= 1; 195 | `; 196 | 197 | const expected = ` 198 | import t from "ts-runtime/lib"; 199 | 200 | let _fooType = t.number(), foo = _fooType.assert(1); 201 | foo &= _fooType.assert(1);`; 202 | 203 | const result = util.transform(input); 204 | 205 | util.compare(expected, result); 206 | }); 207 | 208 | it('should assert bitwise XOR assignments', () => { 209 | const input = ` 210 | let foo: number = 1; 211 | foo ^= 1; 212 | `; 213 | 214 | const expected = ` 215 | import t from "ts-runtime/lib"; 216 | 217 | let _fooType = t.number(), foo = _fooType.assert(1); 218 | foo ^= _fooType.assert(1);`; 219 | 220 | const result = util.transform(input); 221 | 222 | util.compare(expected, result); 223 | }); 224 | 225 | it('should assert bitwise OR assignments', () => { 226 | const input = ` 227 | let foo: number = 1; 228 | foo |= 1; 229 | `; 230 | 231 | const expected = ` 232 | import t from "ts-runtime/lib"; 233 | 234 | let _fooType = t.number(), foo = _fooType.assert(1); 235 | foo |= _fooType.assert(1);`; 236 | 237 | const result = util.transform(input); 238 | 239 | util.compare(expected, result); 240 | }); 241 | 242 | it('should not assert any', () => { 243 | const input = ` 244 | let foo: any; 245 | foo = 1;`; 246 | 247 | const expected = ` 248 | let foo; 249 | foo = 1; 250 | 251 | export {};`; 252 | 253 | const result = util.transform(input); 254 | 255 | util.compare(expected, result); 256 | }); 257 | }); 258 | } 259 | -------------------------------------------------------------------------------- /test/transformation/block_like.ts: -------------------------------------------------------------------------------- 1 | export default () => { 2 | describe('Block Like', () => { 3 | it('should annotate function declarations', () => { 4 | const input = `function foo() { }`; 5 | 6 | const expected = ` 7 | import t from "ts-runtime/lib"; 8 | 9 | function foo() { } 10 | t.annotate(foo, t.function(t.return(t.any())));`; 11 | 12 | const result = util.transform(input); 13 | 14 | util.compare(expected, result); 15 | }); 16 | 17 | it('should annotate enum declarations', () => { 18 | const input = `enum Foo { }`; 19 | 20 | const expected = ` 21 | import t from "ts-runtime/lib"; 22 | 23 | var Foo; 24 | (function (Foo) { 25 | })(Foo || (Foo = {})); 26 | t.annotate(Foo, t.enum()); 27 | `; 28 | 29 | const result = util.transform(input); 30 | 31 | util.compare(expected, result); 32 | }); 33 | 34 | it('should annotate const enum declarations', () => { 35 | const input = `const enum Foo { }`; 36 | 37 | const expected = ` 38 | import t from "ts-runtime/lib"; 39 | 40 | var Foo; 41 | (function (Foo) { 42 | })(Foo || (Foo = {})); 43 | t.annotate(Foo, t.enum()); 44 | `; 45 | 46 | const result = util.transform(input); 47 | 48 | util.compare(expected, result); 49 | }); 50 | 51 | it('should define type parameters symbol for class declarations with type parameters', () => { 52 | const input = `class A { }`; 53 | 54 | const expected = `const _ATypeParametersSymbol = Symbol("ATypeParameters");`; 55 | 56 | const result = util.transform(input); 57 | 58 | util.contains(expected, result); 59 | }); 60 | 61 | it('should transform SourceFile', () => { 62 | const input = `function foo() { }`; 63 | 64 | const expected = ` 65 | import t from "ts-runtime/lib"; 66 | 67 | function foo() { } 68 | t.annotate(foo, t.function(t.return(t.any())));`; 69 | 70 | const result = util.transform(input); 71 | 72 | util.compare(expected, result); 73 | }); 74 | 75 | it('should transform Block', () => { 76 | const input = ` 77 | { 78 | function foo() { } 79 | }`; 80 | 81 | const expected = ` 82 | import t from "ts-runtime/lib"; 83 | 84 | { 85 | function foo() { } 86 | t.annotate(foo, t.function(t.return(t.any()))); 87 | }`; 88 | 89 | const result = util.transform(input); 90 | 91 | util.compare(expected, result); 92 | }); 93 | 94 | it('should transform ModuleBlock', () => { 95 | const input = ` 96 | namespace Foo { 97 | function foo() { } 98 | }`; 99 | 100 | const expected = ` 101 | import t from "ts-runtime/lib"; 102 | 103 | var Foo; 104 | (function (Foo) { 105 | function foo() { } 106 | t.annotate(foo, t.function(t.return(t.any()))); 107 | })(Foo || (Foo = {}));`; 108 | 109 | const result = util.transform(input); 110 | 111 | util.compare(expected, result); 112 | }); 113 | 114 | it('should transform CaseClause', () => { 115 | const input = ` 116 | switch(true) { 117 | case true: 118 | function foo() { } 119 | break; 120 | }`; 121 | 122 | const expected = ` 123 | import t from "ts-runtime/lib"; 124 | 125 | switch (true) { 126 | case true: 127 | function foo() { } 128 | t.annotate(foo, t.function(t.return(t.any()))); 129 | break; 130 | }`; 131 | 132 | const result = util.transform(input); 133 | 134 | util.compare(expected, result); 135 | }); 136 | 137 | it('should transform DefaultClause', () => { 138 | const input = ` 139 | switch(true) { 140 | default: 141 | function foo() { } 142 | break; 143 | }`; 144 | 145 | const expected = ` 146 | import t from "ts-runtime/lib"; 147 | 148 | switch (true) { 149 | default: 150 | function foo() { } 151 | t.annotate(foo, t.function(t.return(t.any()))); 152 | break; 153 | }`; 154 | 155 | const result = util.transform(input); 156 | 157 | util.compare(expected, result); 158 | }); 159 | }); 160 | } 161 | -------------------------------------------------------------------------------- /test/transformation/declaration_merging.ts: -------------------------------------------------------------------------------- 1 | export default () => { 2 | describe('Declaration Merging', () => { 3 | it('should merge interfaces', () => { 4 | const input = ` 5 | interface Foo { 6 | prop: string; 7 | } 8 | 9 | interface Foo { 10 | prop2: number; 11 | }`; 12 | 13 | const expected = ` 14 | import t from "ts-runtime/lib"; 15 | 16 | const Foo = t.type("Foo", t.object(t.property("prop", t.string()), t.property("prop2", t.number())));`; 17 | 18 | const result = util.transform(input); 19 | 20 | util.compare(expected, result); 21 | }); 22 | 23 | it('should merge classes and interfaces', () => { 24 | const input = ` 25 | class Foo { 26 | prop: string; 27 | } 28 | 29 | interface Foo { 30 | prop2: number; 31 | }`; 32 | 33 | const expected = ` 34 | t.annotate(t.class("Foo", t.property("prop", t.string()), t.property("prop2", t.number())))`; 35 | 36 | const result = util.transform(input); 37 | 38 | util.contains(expected, result); 39 | }); 40 | 41 | it('should merge interfaces and classes', () => { 42 | const input = ` 43 | interface Foo { 44 | prop2: number; 45 | } 46 | 47 | class Foo { 48 | prop: string; 49 | }`; 50 | 51 | const expected = ` 52 | t.annotate(t.class("Foo", t.property("prop2", t.number()), t.property("prop", t.string())))`; 53 | 54 | const result = util.transform(input); 55 | 56 | util.contains(expected, result); 57 | }); 58 | 59 | it('should merge interfaces and classes with overloads', () => { 60 | const input = ` 61 | interface Foo { 62 | prop2: number; 63 | method(a: string): number; 64 | } 65 | 66 | class Foo { 67 | prop: string; 68 | method(a: boolean): symbol; 69 | method(a: any): any { } 70 | }`; 71 | 72 | const expected = ` 73 | t.annotate(t.class("Foo", t.property("prop2", t.number()), t.property("prop", t.string()), t.property("method", t.function(t.param("a", t.union(t.string(), t.boolean())), t.return(t.union(t.number(), t.symbol()))))))`; 74 | 75 | const result = util.transform(input); 76 | 77 | util.contains(expected, result); 78 | }); 79 | }); 80 | } 81 | -------------------------------------------------------------------------------- /test/transformation/function_declaration.ts: -------------------------------------------------------------------------------- 1 | export default () => { 2 | // Function body transformations are already covered by other tests 3 | describe('Function Declaration', () => { 4 | it('should be annotated', () => { 5 | const input = `function foo() { }`; 6 | 7 | const expected = ` 8 | import t from "ts-runtime/lib"; 9 | 10 | function foo() { 11 | } 12 | t.annotate(foo, t.function(t.return(t.any())));`; 13 | 14 | const result = util.transform(input); 15 | 16 | util.compare(expected, result); 17 | }); 18 | 19 | it('should not be annotated with noAnnotate', () => { 20 | const input = `function foo() { }`; 21 | 22 | const expected = `function foo() { } 23 | 24 | export {};`; 25 | 26 | const result = util.transform(input, { noAnnotate: true }); 27 | 28 | util.compare(expected, result); 29 | }); 30 | }); 31 | } 32 | -------------------------------------------------------------------------------- /test/transformation/function_expression.ts: -------------------------------------------------------------------------------- 1 | export default () => { 2 | // Function body transformations are already covered by other tests 3 | describe('Function Expression', () => { 4 | it('should be annotated', () => { 5 | const input = `let foo = function() { };`; 6 | 7 | const expected = ` 8 | import t from "ts-runtime/lib"; 9 | 10 | let foo = t.annotate(function () { 11 | }, t.function(t.return(t.any())));`; 12 | 13 | const result = util.transform(input); 14 | 15 | util.compare(expected, result); 16 | }); 17 | 18 | it('should not be annotated with noAnnotate', () => { 19 | const input = `let foo = function() { };`; 20 | 21 | const expected = `let foo = function () { }; 22 | 23 | export {};`; 24 | 25 | const result = util.transform(input, { noAnnotate: true }); 26 | 27 | util.compare(expected, result); 28 | }); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /test/transformation/index.test.ts: -------------------------------------------------------------------------------- 1 | import arrow_function from './arrow_function'; 2 | import as_expression from './as_expression'; 3 | import binary_expression from './binary_expression'; 4 | import block_like from './block_like'; 5 | import class_declaration from './class_declaration'; 6 | import declaration_merging from './declaration_merging'; 7 | import function_declaration from './function_declaration'; 8 | import function_expression from './function_expression'; 9 | import interface_declaration from './interface_declaration'; 10 | import program_error from './program_error'; 11 | import source_file from './source_file'; 12 | import type_alias_declaration from './type_alias_declaration'; 13 | import variable_declaration_list from './variable_declaration_list'; 14 | 15 | describe('Transformation', () => { 16 | arrow_function(); 17 | as_expression(); 18 | binary_expression(); 19 | block_like(); 20 | class_declaration(); 21 | declaration_merging(); 22 | function_declaration(); 23 | function_expression(); 24 | interface_declaration(); 25 | program_error(); 26 | source_file(); 27 | type_alias_declaration(); 28 | variable_declaration_list(); 29 | }); 30 | -------------------------------------------------------------------------------- /test/transformation/interface_declaration.ts: -------------------------------------------------------------------------------- 1 | export default () => { 2 | describe('Interface Declaration', () => { 3 | it('should be reflected', () => { 4 | const input = `interface Foo { }`; 5 | 6 | const expected = ` 7 | import t from "ts-runtime/lib"; 8 | 9 | const Foo = t.type("Foo", t.object());`; 10 | 11 | const result = util.transform(input); 12 | 13 | util.compare(expected, result); 14 | }); 15 | 16 | it('should transform extending other types', () => { 17 | const input = ` 18 | interface Bar { } 19 | interface Foo extends Bar { }`; 20 | 21 | const expected = ` 22 | import t from "ts-runtime/lib"; 23 | 24 | const Bar = t.type("Bar", t.object()); 25 | const Foo = t.type("Foo", t.intersect(t.ref(Bar), t.object()));`; 26 | 27 | const result = util.transform(input); 28 | 29 | util.compare(expected, result); 30 | }); 31 | 32 | it('should support type parameters', () => { 33 | const input = ` 34 | interface Foo { 35 | prop: T; 36 | }`; 37 | 38 | const expected = ` 39 | import t from "ts-runtime/lib"; 40 | 41 | const Foo = t.type("Foo", Foo => { 42 | const T = Foo.typeParameter("T"); 43 | return t.object(t.property("prop", T)); 44 | });`; 45 | 46 | const result = util.transform(input); 47 | 48 | util.compare(expected, result); 49 | }); 50 | }); 51 | } 52 | -------------------------------------------------------------------------------- /test/transformation/program_error.ts: -------------------------------------------------------------------------------- 1 | export default () => { 2 | describe('Program Error', () => { 3 | it('should throw an error for classes with unsupported heritage clause', () => { 4 | const input = `class Foo extends class { } { }`; 5 | 6 | util.expectProgramError(() => util.transform(input)); 7 | }); 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /test/transformation/source_file.ts: -------------------------------------------------------------------------------- 1 | export default () => { 2 | describe('Source File', () => { 3 | it('should remain empty if containing whitespaces only', () => { 4 | const input = ` `; 5 | 6 | const expected = `export {};`; 7 | 8 | const result = util.transform(input); 9 | 10 | util.compare(expected, result); 11 | }); 12 | 13 | it('should not import lib if not required', () => { 14 | const input = `let foo = "bar";`; 15 | 16 | const expected = `let foo = "bar"; 17 | 18 | export {};`; 19 | 20 | const result = util.transform(input); 21 | 22 | util.compare(expected, result); 23 | }); 24 | 25 | it('should prefix the lib identifier if it is already in use', () => { 26 | const input = `let t: string = "foo";`; 27 | 28 | const expected = ` 29 | import _t from "ts-runtime/lib"; 30 | let _tType = _t.string(), t = _tType.assert("foo");`; 31 | 32 | const result = util.transform(input); 33 | 34 | util.compare(expected, result); 35 | }); 36 | 37 | it('should recursively prefix the lib identifier if it is already in use', () => { 38 | const input = ` 39 | let t: string = "foo"; 40 | let _t: string = "bar";`; 41 | 42 | const expected = ` 43 | import __t from "ts-runtime/lib"; 44 | let _tType = __t.string(), t = _tType.assert("foo"); 45 | let __tType = __t.string(), _t = __tType.assert("bar");`; 46 | 47 | const result = util.transform(input); 48 | 49 | util.compare(expected, result); 50 | }); 51 | 52 | it('should not reflect built ins by default', () => { 53 | const input = `let foo: Set;`; 54 | 55 | const expected = ` 56 | import t from "ts-runtime/lib"; 57 | let _fooType = t.ref(Set, t.any()), foo;`; 58 | 59 | const result = util.transform(input); 60 | 61 | util.compare(expected, result); 62 | }); 63 | 64 | it('should reflect ambient and external types in a separate file and import it', () => { 65 | const input = ` 66 | declare type Foo = string; 67 | let foo: Foo;`; 68 | 69 | const expected = ` 70 | import "./tsr-declarations"; 71 | import t from "ts-runtime/lib"; 72 | let _fooType = t.ref("Foo.__UID__"), foo;`; 73 | 74 | const result = util.transform(input); 75 | 76 | util.compare(expected, result); 77 | }); 78 | 79 | it('should import module-alias if desired', () => { 80 | const input = ``; 81 | 82 | const expected = `import "module-alias/register";`; 83 | 84 | const result = util.transform(input, { moduleAlias: true }); 85 | 86 | util.compare(expected, result); 87 | }); 88 | }); 89 | } 90 | -------------------------------------------------------------------------------- /test/transformation/type_alias_declaration.ts: -------------------------------------------------------------------------------- 1 | export default () => { 2 | describe('Type Alias Declaration', () => { 3 | it('should be reflected', () => { 4 | const input = `type Foo = any;`; 5 | 6 | const expected = ` 7 | import t from "ts-runtime/lib"; 8 | 9 | const Foo = t.type("Foo", t.any()); 10 | `; 11 | 12 | const result = util.transform(input); 13 | 14 | util.compare(expected, result); 15 | }); 16 | 17 | it('should support type parameters', () => { 18 | const input = `type Foo = { 19 | prop: T; 20 | };`; 21 | 22 | const expected = ` 23 | import t from "ts-runtime/lib"; 24 | 25 | const Foo = t.type("Foo", Foo => { 26 | const T = Foo.typeParameter("T"); 27 | return t.object(t.property("prop", T)); 28 | });`; 29 | 30 | const result = util.transform(input); 31 | 32 | util.compare(expected, result); 33 | }); 34 | 35 | it('should support self references', () => { 36 | const input = `type Foo = { 37 | prop: Foo; 38 | };`; 39 | 40 | const expected = ` 41 | import t from "ts-runtime/lib"; 42 | 43 | const Foo = Foo => t.object(t.property("prop", t.ref(Foo)));`; 44 | 45 | const result = util.transform(input); 46 | 47 | util.compare(expected, result); 48 | }); 49 | 50 | it('should support optional parameters', () => { 51 | const input = `type Foo = { 52 | prop?: string; 53 | };`; 54 | 55 | const expected = ` 56 | import t from "ts-runtime/lib"; 57 | 58 | const Foo = t.type("Foo", t.object(t.property("prop", t.string(), true)));`; 59 | 60 | const result = util.transform(input); 61 | 62 | util.compare(expected, result); 63 | }); 64 | 65 | it('should support indexers', () => { 66 | const input = `type Foo = { 67 | [index: string]: any; 68 | };`; 69 | 70 | const expected = ` 71 | import t from "ts-runtime/lib"; 72 | 73 | const Foo = t.type("Foo", t.object(t.indexer("index", t.string(), t.any())));`; 74 | 75 | const result = util.transform(input); 76 | 77 | util.compare(expected, result); 78 | }); 79 | }); 80 | } 81 | -------------------------------------------------------------------------------- /test/transformation/variable_declaration_list.ts: -------------------------------------------------------------------------------- 1 | export default () => { 2 | describe('Variable Declaration List', () => { 3 | it('should not assert in for-of statements', () => { 4 | const input = ` 5 | for (let foo of []) { 6 | 7 | }`; 8 | 9 | const expected = ` 10 | for (let foo of []) { 11 | 12 | } 13 | 14 | export {};`; 15 | 16 | const result = util.transform(input); 17 | 18 | util.compare(expected, result); 19 | }); 20 | 21 | it('should not assert in for-in statements', () => { 22 | const input = ` 23 | for (let foo in {}) { 24 | 25 | }`; 26 | 27 | const expected = ` 28 | for (let foo in {}) { 29 | 30 | } 31 | 32 | export {};`; 33 | 34 | const result = util.transform(input); 35 | 36 | util.compare(expected, result); 37 | }); 38 | 39 | it('should not assert in catch clauses', () => { 40 | const input = ` 41 | try { 42 | } catch (e) { 43 | }`; 44 | 45 | const expected = ` 46 | try { 47 | 48 | } catch (e) { 49 | 50 | } 51 | 52 | export {};`; 53 | 54 | const result = util.transform(input); 55 | 56 | util.compare(expected, result); 57 | }); 58 | 59 | it('should reflect let declarations', () => { 60 | const input = `let foo: string;`; 61 | 62 | const expected = ` 63 | import t from "ts-runtime/lib"; 64 | let _fooType = t.string(), foo;`; 65 | 66 | const result = util.transform(input); 67 | 68 | util.compare(expected, result); 69 | }); 70 | 71 | it('should reflect and assert let declarations', () => { 72 | const input = `let foo: string = "bar";`; 73 | 74 | const expected = ` 75 | import t from "ts-runtime/lib"; 76 | let _fooType = t.string(), foo = _fooType.assert("bar");`; 77 | 78 | const result = util.transform(input); 79 | 80 | util.compare(expected, result); 81 | }); 82 | 83 | it('should reflect and assert let reassignments', () => { 84 | const input = ` 85 | let foo: string = "bar"; 86 | foo = "bar";`; 87 | 88 | const expected = ` 89 | import t from "ts-runtime/lib"; 90 | let _fooType = t.string(), foo = _fooType.assert("bar"); 91 | foo = _fooType.assert("bar");`; 92 | 93 | const result = util.transform(input); 94 | 95 | util.compare(expected, result); 96 | }); 97 | 98 | it('should reflect var declarations', () => { 99 | const input = `var foo: string;`; 100 | 101 | const expected = ` 102 | import t from "ts-runtime/lib"; 103 | var _fooType = t.string(), foo;`; 104 | 105 | const result = util.transform(input); 106 | 107 | util.compare(expected, result); 108 | }); 109 | 110 | it('should reflect and assert var declarations', () => { 111 | const input = `var foo: string = "bar";`; 112 | 113 | const expected = ` 114 | import t from "ts-runtime/lib"; 115 | var _fooType = t.string(), foo = _fooType.assert("bar");`; 116 | 117 | const result = util.transform(input); 118 | 119 | util.compare(expected, result); 120 | }); 121 | 122 | it('should reflect and assert var reassignments', () => { 123 | const input = ` 124 | var foo: string = "bar"; 125 | foo = "bar";`; 126 | 127 | const expected = ` 128 | import t from "ts-runtime/lib"; 129 | var _fooType = t.string(), foo = _fooType.assert("bar"); 130 | foo = _fooType.assert("bar");`; 131 | 132 | const result = util.transform(input); 133 | 134 | util.compare(expected, result); 135 | }); 136 | 137 | it('should reflect and assert const declarations', () => { 138 | const input = `const foo: string = "bar";`; 139 | 140 | const expected = ` 141 | import t from "ts-runtime/lib"; 142 | const foo = t.string().assert("bar");`; 143 | 144 | const result = util.transform(input); 145 | 146 | util.compare(expected, result); 147 | }); 148 | 149 | it('should reflect type queries', () => { 150 | const input = ` 151 | let foo = "foo"; 152 | let bar: typeof foo;`; 153 | 154 | const expected = ` 155 | import t from "ts-runtime/lib"; 156 | let foo = "foo"; 157 | let _barType = t.typeOf(foo), bar;`; 158 | 159 | const result = util.transform(input); 160 | 161 | util.compare(expected, result); 162 | }); 163 | 164 | it('should reflect and assert type queries', () => { 165 | const input = ` 166 | let foo = "foo"; 167 | let bar: typeof foo = "bar";`; 168 | 169 | const expected = ` 170 | import t from "ts-runtime/lib"; 171 | let foo = "foo"; 172 | let _barType = t.typeOf(foo), bar = _barType.assert("bar");`; 173 | 174 | const result = util.transform(input); 175 | 176 | util.compare(expected, result); 177 | }); 178 | }); 179 | } 180 | -------------------------------------------------------------------------------- /test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "lib": ["es2015"], 5 | "moduleResolution": "node", 6 | "target": "es5", 7 | "noImplicitAny": true, 8 | "preserveConstEnums": true, 9 | "removeComments": false, 10 | "sourceMap": true 11 | }, 12 | "include": [ 13 | "**/*" 14 | ], 15 | "typeRoots": [ 16 | "../node_modules/@types", 17 | "./types" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /test/types/commondir/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'commondir'; 2 | -------------------------------------------------------------------------------- /test/types/globals.d.ts: -------------------------------------------------------------------------------- 1 | import expectLib = require('expect.js'); 2 | import assertLib = require('assert'); 3 | import tsrLib = require('../../src/index'); 4 | import utilLib = require('../util'); 5 | 6 | declare global { 7 | const assert: typeof assertLib; 8 | const tsr: typeof tsrLib; 9 | const util: typeof utilLib; 10 | 11 | namespace NodeJS { 12 | interface Global { 13 | expect: typeof expect; 14 | assert: typeof assert; 15 | tsr: typeof tsr; 16 | util: typeof util; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/util.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as fs from 'fs'; 3 | import * as ts from 'typescript'; 4 | import * as commondir from 'commondir'; 5 | import { getOptions } from '../src/transform'; 6 | import { ProgramError } from '../src/errors'; 7 | import { Scanner } from '../src/scanner'; 8 | import { Options } from '../src/options'; 9 | import { MutationContext } from '../src/context'; 10 | import { Host, FileReflection } from '../src/host'; 11 | 12 | const DEFAULT_LIBS = libs(['es2015']); 13 | 14 | export function program(host: Host, compilerOptions = options().compilerOptions): ts.Program { 15 | return ts.createProgram(['src/module.ts'], compilerOptions, host); 16 | } 17 | 18 | export function host(input: string, compilerOptions = options().compilerOptions): Host { 19 | return new Host(reflect(input)[1], compilerOptions); 20 | } 21 | 22 | export function scanner(program: ts.Program, opts = options()): Scanner { 23 | return new Scanner(program, opts); 24 | } 25 | 26 | export function resolveEntryFiles(entryFiles: string[]): string[] { 27 | return entryFiles.map(f => path.join(`${path.resolve(path.dirname(f))}`, path.basename(f)));; 28 | } 29 | 30 | export function commonDir(entryFiles: string[]): string { 31 | return commondir(resolveEntryFiles(entryFiles).map(f => path.dirname(f))); 32 | } 33 | 34 | export function context(program: ts.Program, host: Host, opts = options(), entryFiles = ['src/module.ts']): MutationContext { 35 | const resolvedEntryFiles = resolveEntryFiles(entryFiles); 36 | 37 | return new MutationContext( 38 | program.getSourceFile(resolvedEntryFiles[0]), 39 | opts, 40 | program, 41 | host, 42 | scanner(program, options), 43 | resolveEntryFiles(entryFiles), 44 | commonDir(resolvedEntryFiles), 45 | // TODO: fix any cast 46 | (ts as any).nullTransformationContext 47 | ); 48 | } 49 | 50 | export function transform(input: string, options?: Options): string { 51 | const reflection = util.reflect(input); 52 | const rootName = reflection[0]; 53 | const fileReflections = reflection[1]; 54 | const opts = mergeOptions(reflection[2], options || {}); 55 | return tsr.transformReflection(rootName, fileReflections, opts)[0].text; 56 | } 57 | 58 | export function contains(expected: string, result: string): Expect.Assertion { 59 | return expect(normalize(result)).to.contain(normalize(expected)); 60 | } 61 | 62 | export function compare(expected: string, result: string): Expect.Assertion { 63 | return expect(normalize(expected)).to.eql(normalize(result)); 64 | } 65 | 66 | export function expectProgramError(fn: () => any): void { 67 | return expect(fn).to.throwException(e => { 68 | expect(e).to.be.a(Error); 69 | expect(e.name).to.equal(ProgramError.id); 70 | }); 71 | } 72 | 73 | export function expectError(fn: () => any): void { 74 | return expect(fn).to.throwException(e => { 75 | expect(e).to.be.a(Error); 76 | }); 77 | } 78 | 79 | export function libs(libs?: string[]): {name: string, text: string}[] { 80 | if (!libs) { 81 | return DEFAULT_LIBS; 82 | } 83 | 84 | const rootNames = libs.map(lib => require.resolve(`typescript/lib/lib.${lib}.d.ts`)); 85 | 86 | const options = ts.getDefaultCompilerOptions(); 87 | const host = ts.createCompilerHost(options, true); 88 | const program = ts.createProgram(rootNames, options, host); 89 | 90 | const reflections = program.getSourceFiles().map(sf => ({ 91 | name: sf.fileName, 92 | text: sf.getFullText() 93 | })); 94 | 95 | reflections.push({ 96 | name: require.resolve(`typescript/lib/lib.d.ts`), 97 | text: [ 98 | `/// `, 99 | ...libs.map(lib => `/// `) 100 | ].join(ts.sys.newLine) 101 | }); 102 | 103 | return reflections; 104 | } 105 | 106 | export function options(): Options { 107 | return getOptions({ 108 | force: true, 109 | compilerOptions: { 110 | skipLibCheck: true, 111 | strictNullChecks: true, 112 | target: ts.ScriptTarget.ES2015, 113 | module: ts.ModuleKind.ES2015, 114 | noEmitHelpers: true 115 | } 116 | }); 117 | } 118 | 119 | export function reflect(input: string, name?: string): [string, FileReflection[], Options] { 120 | const fileReflections = [ 121 | { 122 | name: name || 'src/module.ts', 123 | text: input 124 | }, 125 | ...libs() 126 | ]; 127 | 128 | return [name || 'src/module.ts', fileReflections, options()]; 129 | } 130 | 131 | export function normalize(input: string): string { 132 | return input 133 | .trim() 134 | .replace(/\s+/g, ' ') 135 | .replace(/\(\s+/g, '(') 136 | .replace(/\s+\)/g, ')') 137 | .replace(/\{\s+/g, '{\n') 138 | .replace(/\s+\}/g, '\n}') 139 | .replace(/\[\s+/g, '[') 140 | .replace(/\s+]/g, ']') 141 | .replace(/\}\s+([A-Za-z])/g, '\n}\n$1') 142 | .replace(/([a-zA-Z]+)\.(\d+)/, '$1.__UID__') 143 | .split(';') 144 | .join(';\n') 145 | .trim() 146 | ; 147 | } 148 | 149 | export function mergeOptions(options: Options, other: Options): Options { 150 | const opts = Object.assign({}, options, other); 151 | const compilerOptions = Object.assign(ts.getDefaultCompilerOptions(), options.compilerOptions, other && other.compilerOptions || {}); 152 | opts.compilerOptions = compilerOptions; 153 | return opts; 154 | } 155 | -------------------------------------------------------------------------------- /tsconfig-lkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "lkg", 4 | "strictNullChecks": false, 5 | "moduleResolution": "node", 6 | "module": "commonjs", 7 | "target": "es6", 8 | "lib": ["es6"], 9 | "typeRoots": [ 10 | "node_modules/@types", 11 | "src/types" 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "declaration": true, 5 | "experimentalDecorators": true, 6 | "emitDecoratorMetadata": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "lib": ["es6"], 9 | "moduleResolution": "node", 10 | "target": "es5", 11 | "noImplicitAny": true, 12 | "preserveConstEnums": true, 13 | "removeComments": false 14 | }, 15 | "include": [ 16 | "src/**/*" 17 | ], 18 | "exclude": [ 19 | "src/playground", 20 | "src/play" 21 | ], 22 | "typeRoots": [ 23 | "node_modules/@types", 24 | "src/types" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:latest", 3 | "rules": { 4 | "quotemark": [true, "single"], 5 | "no-console": false, 6 | "arrow-parens": false, 7 | "interface-name": false, 8 | "ordered-imports": false, 9 | "no-empty": false, 10 | "curly": false, 11 | "max-line-length": 125, 12 | "object-literal-sort-keys": false, 13 | "object-literal-shorthand": false, 14 | "no-empty-interface": false, 15 | "member-ordering": false, 16 | "no-trailing-whitespace": false, 17 | "no-consecutive-blank-lines": false, 18 | "interface-over-type-literal": false, 19 | "prefer-const": false, 20 | "no-var-requires": false, 21 | "array-type": false, 22 | "trailing-comma": false, 23 | "variable-name": false, 24 | "no-conditional-assignment": false, 25 | "switch-default": false 26 | }, 27 | "jsRules": { 28 | 29 | }, 30 | "rulesDirectory": [] 31 | } 32 | --------------------------------------------------------------------------------