├── .gitattributes ├── .gitignore ├── .jshintrc ├── .no-sublime-package ├── .tern-project ├── .travis.yml ├── AUTHORS ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── bin ├── condense ├── tern ├── test └── update_authors.sh ├── defs ├── browser.json ├── chai.json ├── ecmascript.json ├── jquery.json ├── react.json └── underscore.json ├── doc ├── BACKERS.txt ├── Makefile ├── asciidoc.conf ├── demo │ ├── demo.css │ ├── demo.js │ ├── index.html │ ├── polyfill.js │ └── underscore.js ├── docs.css ├── header.svg ├── logo.png ├── manual.html └── manual.txt ├── emacs ├── tern-auto-complete.el └── tern.el ├── index.html ├── lib ├── bootstrap.js ├── comment.js ├── condense.js ├── def.js ├── infer.js ├── signal.js └── tern.js ├── package.json ├── plugin ├── angular.js ├── commonjs.js ├── complete_strings.js ├── doc_comment.js ├── es_modules.js ├── modules.js ├── node.js ├── node_resolve.js ├── requirejs.js └── webpack.js └── test ├── cases ├── angular │ ├── config.js │ ├── filters.js │ └── main.js ├── angular_service.js ├── arguments.js ├── arithmetic_ops.js ├── array_holes.js ├── arrow.js ├── async-arrow.js ├── async.js ├── async_for_of.js ├── async_generator.js ├── autothis.js ├── bind.js ├── block_scope.js ├── blowup.js ├── browser.js ├── builtins.js ├── catch_error.js ├── cautiouspropagation.js ├── class.js ├── complete_strings.js ├── computedprop.js ├── contextcomplete.js ├── copyprops.js ├── ctorpattern.js ├── def_type_string.js ├── defineProperty.js ├── defs │ ├── span.json │ └── string.json ├── destructure.js ├── docstrings.js ├── effects.js ├── empty_overridden_prop.js ├── es6-features.js ├── es_modules │ ├── blah.js │ ├── class.js │ ├── foo.js │ ├── func.js │ ├── main.js │ ├── obj.js │ └── reexp.js ├── extends.js ├── fetch.js ├── finddef.js ├── findref.js ├── fn_arguments.js ├── for_of.js ├── formdata.js ├── generator.js ├── generic_each.js ├── getter.js ├── global_this.js ├── hint-objlit.js ├── hint_browser.js ├── hint_ecma5.js ├── hint_simple.js ├── in_comment.js ├── indirect_class.js ├── infinite-expansion.js ├── jquery_event.js ├── jsdoc.js ├── map.js ├── merge.js ├── mixin.js ├── navigator.js ├── new_array.js ├── new_to_prototype.js ├── node │ ├── binary.node │ ├── dir │ │ ├── index.js │ │ └── lib │ │ │ ├── other.js │ │ │ └── relative.js │ ├── exportfunc.js │ ├── localfile.js │ └── main.js ├── node_exports.js ├── node_modules │ ├── mod1 │ │ ├── a.js │ │ ├── dir1 │ │ │ └── index.js │ │ ├── doc.js │ │ ├── mainfile.js │ │ ├── node_modules │ │ │ └── subdep1 │ │ │ │ ├── package.json │ │ │ │ └── subdep1.js │ │ ├── package.json │ │ ├── reassign_exports.js │ │ ├── reassign_exports_to_required.js │ │ └── secondfile.js │ └── mymod.json ├── node_origin_paths.js ├── object-string-prop.js ├── object_create.js ├── objectlit.js ├── objnames.js ├── or_empty_array.js ├── order_of_definition.js ├── phantom_object.js ├── plus.js ├── promise.js ├── proto.js ├── protoname.js ├── replace_bogus_prop.js ├── requirejs │ ├── bar.js │ ├── baz.js │ ├── foo.js │ ├── func.js │ ├── main.js │ ├── module_exports.js │ ├── named.js │ ├── requireme.js │ ├── simplifiedcommon.js │ ├── subdir │ │ └── zap.js │ └── useexports.js ├── requirejs_config │ └── main.js ├── set.js ├── simple.js ├── simple_generic.js ├── span_from_def.js ├── super.js ├── symbol.js ├── template.js ├── underscore.js └── webpack │ ├── component │ └── foo.js │ ├── main.js │ ├── node_modules │ ├── esnext │ │ ├── index.js │ │ └── package.json │ ├── foo │ │ ├── browser.js │ │ ├── index.js │ │ └── package.json │ ├── modu │ │ ├── index.js │ │ └── package.json │ └── xyz │ │ ├── index.js │ │ └── package.json │ └── webpack.config.js ├── condense.js ├── condense ├── add_to_old.js ├── add_to_old.json ├── angular_simple.js ├── angular_simple.json ├── array.js ├── array.json ├── basic.js ├── basic.json ├── double_ref.js ├── double_ref.json ├── extend_foo.js ├── fn.js ├── fn.json ├── function_prop.js ├── function_prop.json ├── generic.js ├── generic.json ├── ignore_newer.js ├── ignore_newer.json ├── node_export_function_a.js ├── node_fn_export.js ├── node_fn_export.json ├── node_other_module_type_ref.js ├── node_other_module_type_ref.json ├── node_require_private.js ├── node_require_private.json ├── node_simple.js ├── node_simple.json ├── proto.js ├── proto.json ├── recursive.js ├── recursive.json ├── ref_in_type.js ├── ref_in_type.json ├── ref_to_old.js ├── ref_to_old.json ├── requirejs_const.js ├── requirejs_const.json ├── requirejs_dep.js ├── requirejs_dep.json ├── requirejs_empty_deps.js ├── requirejs_empty_deps.json ├── requirejs_primitive.js ├── requirejs_primitive.json ├── requirejs_setup.js ├── requirejs_setup.json ├── uniontype.js └── uniontype.json ├── data └── large.js ├── fragments.js ├── reload.js ├── runcases.js ├── timeout.js └── util.js /.gitattributes: -------------------------------------------------------------------------------- 1 | test/**/* text eol=lf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .tern-port 2 | /node_modules 3 | /misc 4 | *.elc 5 | *.pyc 6 | 7 | .DS_Store -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "sub" : true, 3 | "boss" : true, 4 | "expr" : true, 5 | "node" : true, 6 | "devel" : true, 7 | "curly" : false, 8 | "immed" : true, 9 | "noarg" : true, 10 | "undef" : true, 11 | "newcap" : true, 12 | "shadow" : true, 13 | "newcap" : false, 14 | "eqnull" : true, 15 | "jquery" : true, 16 | "eqeqeq" : false, 17 | "browser" : true, 18 | "latedef" : false, 19 | "supernew" : true, 20 | "laxcomma" : false, 21 | "laxbreak" : true, 22 | "lastsemic": true, 23 | "globals" : {"tern": true, "acorn": true, "define": true}, 24 | "loopfunc" : true 25 | } -------------------------------------------------------------------------------- /.no-sublime-package: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.tern-project: -------------------------------------------------------------------------------- 1 | { 2 | "libs": [ 3 | ], 4 | "plugins": { 5 | "node": {}, 6 | "complete_strings": {} 7 | } 8 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: node_js 4 | 5 | node_js: 6 | - 9 7 | - 8 8 | - 7 9 | - 6 10 | 11 | cache: 12 | directories: 13 | - node_modules 14 | 15 | install: 16 | - npm i -g npm@latest 17 | - npm install 18 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | List of Tern contributors. Updated before every release. 2 | 3 | A2ZH 4 | Adam Faulkner 5 | Adam Niederer 6 | Adrian Toncean 7 | AGhost-7 8 | Albertina Durante 9 | Alex Pinder 10 | Alex Saveliev 11 | Amila Welihinda 12 | Anders Thøgersen 13 | angelozerr 14 | Antonina Cherednichenko 15 | Ben Joldersma 16 | Ben McCormick 17 | berkin 18 | Boris Jonica 19 | Brian Frichette 20 | byronigoe 21 | CentricStorm 22 | chemzqm 23 | Claus Reinke 24 | Connor Osborn 25 | Curtis Windatt 26 | Damien Diederen 27 | Dani Hodovic 28 | Danny Su 29 | Dewdrops 30 | dloverin 31 | Dmitry Gutov 32 | eidheim 33 | Eloy Toro 34 | Erik Tierney 35 | Fabian Zeindl 36 | Forbes Lindesay 37 | Francisc Romano 38 | Francis Murillo 39 | Hans Huebner 40 | Hiroaki Otsu 41 | Iku Iwasa 42 | impinball 43 | i-p 44 | itai 45 | Jackson Ray Hamilton 46 | J David Smith 47 | Jeffrey Fisher 48 | Jeff Stern 49 | Jonathan Persson 50 | Josh Giles 51 | jzhang 52 | katspaugh 53 | Kim Se-won 54 | Kyle P Davis 55 | liuyujun 56 | Marcel Gerber 57 | Marcello Bastea-Forte 58 | Marijn Haverbeke 59 | Matt 60 | Matteo Landi 61 | Matthias Dahl 62 | Matthis LEMAITRE--COSQUER 63 | Max Schaefer 64 | MetaMemoryT 65 | Michael Russell 66 | Michał Grzejszczak 67 | Miguel Castillo 68 | Mihai Bazon 69 | Mike Rennie 70 | Mikko Rantanen 71 | Miroslav Bajtoš 72 | Mounir Lamouri 73 | Nallamalli Venkata Sai Krishna 74 | Nate Eagleson 75 | Neha Modi 76 | Nick Malaguti 77 | Nicolas Petton 78 | Nico Weber 79 | Nikolaj Kappler 80 | Olivier Ligot 81 | Ossi Herrala 82 | othree 83 | Paris Kasidiaris 84 | partizan 85 | Paul Bakker 86 | Paul Verest 87 | Peter Elmers 88 | Peter Farland 89 | Piotr Tomiak 90 | Pok-Pok 91 | Quinn Slack 92 | Ralf Kistner 93 | Randy Edmunds 94 | Renato F 95 | Renato Ferreira 96 | Reuben Thomas 97 | Ryan Stewart 98 | SAKURAI Masashi 99 | Sean Usick 100 | Sergey Chikuyonok 101 | Sérgio Ramos 102 | sevin7676 103 | Se-Won Kim 104 | sharils 105 | Sindre Sorhus 106 | Slava Kim 107 | Stephan Seidt 108 | Steve Purcell 109 | stoskov 110 | tijsmallaerts 111 | Tim M. Madsen 112 | Timothy Gu 113 | Tommy Troy Lin 114 | Trey Thomas 115 | vheon 116 | Vincent Woo 117 | xd1le 118 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Tern 2 | 3 | ## Submitting bug reports 4 | 5 | The preferred way to report bugs is to use the [GitHub issue 6 | tracker](http://github.com/ternjs/tern/issues). Please put some 7 | thought and energy into your report, to make it easy for us to help 8 | you. 9 | 10 | - Describe very precisely what went wrong. "X is broken" is not a good bug 11 | report. What did you expect to happen? What happened instead? 12 | 13 | - Provide a way for the maintainer to reproduce the problem you are 14 | seeing. That sometimes means waiting for a problem to occur a second 15 | time, and experimenting to see exactly which circumstances trigger 16 | the problem. We can rarely fix bugs that we can not reproduce. 17 | 18 | - Reduce your instructions for reproducing the bug as much as 19 | possible. The more irrelevant pieces (code, steps) are included, the 20 | more time we'll have to waste going through them. 21 | 22 | - Mention which exact version (release number or, if you got the code 23 | from git, revision hash) of Tern you are using. Include your project 24 | configuration if there is any chance of it being relevant. 25 | 26 | - Be civil. The project is maintained by volunteers who do not owe you 27 | anything. Reports with an indignant or belligerent tone tend to be 28 | moved to the bottom of the pile. 29 | 30 | ## Contributing code 31 | 32 | The preferred way to contribute code is through GitHub pull requests. 33 | 34 | If you plan to do something major, please discuss it on the [mailing 35 | list](https://groups.google.com/forum/?fromgroups#!forum/tern-dev) 36 | first, to avoid wasting time on something that will not be merged. 37 | 38 | Follow the project's general coding style. Patches that randomly 39 | change code to your preferred coding style or reorganize code for 40 | subjective reasons will not be accepted. 41 | 42 | By contributing code to Tern you 43 | 44 | - agree to license the contributed code under Tern's [MIT 45 | license](http://ternjs.net/LICENSE). 46 | 47 | - confirm that you have the right to contribute and license the code 48 | in question. (Either you hold all rights on the code, or the rights 49 | holder has explicitly granted the right to use it like this, 50 | through a compatible open source license or through a direct 51 | agreement with you.) 52 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2013 by Marijn Haverbeke and others 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tern 2 | 3 | [![Build Status](https://secure.travis-ci.org/ternjs/tern.svg)](http://travis-ci.org/ternjs/tern) 4 | [![NPM version](https://img.shields.io/npm/v/tern.svg)](https://www.npmjs.org/package/tern) 5 | 6 | 7 | This is [Tern][1]. Tern is a stand-alone, editor-independent 8 | JavaScript analyzer that can be used to improve the JavaScript 9 | integration of existing editors. 10 | 11 | Thanks to a group of generous [crowd funders][2], Tern is open-source 12 | software, under an MIT license. 13 | 14 | There are currently plugins available for [Emacs][emacs] (and Emacs 15 | [company-mode][cmode]), [Vim][vim], [Sublime Text][st], [Eclipse (and general Java API)][ec], 16 | [Light Table][lt], [Atom][atom], [TextMate][tm] and [gedit][gedit], and built-in support in 17 | [Brackets][brackets], [Edge Code][edge_code], [CodeLite](http://codelite.org/), 18 | [vy](https://github.com/iogf/vy), and [SourceLair][sourcelair]. 19 | 20 | For further documentation, see the [project page][1] and the 21 | [manual][3]. To report issues, use the 22 | [issue tracker](https://github.com/ternjs/tern/issues). 23 | 24 | [1]: http://ternjs.net 25 | [2]: http://www.indiegogo.com/projects/tern-intelligent-javascript-editing 26 | [3]: http://ternjs.net/doc/manual.html 27 | 28 | [emacs]: http://ternjs.net/doc/manual.html#emacs 29 | [ec]: https://github.com/angelozerr/tern.java 30 | [vim]: https://github.com/ternjs/tern_for_vim 31 | [st]: https://github.com/ternjs/tern_for_sublime 32 | [lt]: https://github.com/mortalapeman/LT-TernJS 33 | [atom]: https://atom.io/packages/atom-ternjs 34 | [gedit]: https://github.com/Swatinem/tern_for_gedit 35 | [brackets]: http://brackets.io 36 | [edge_code]: http://html.adobe.com/edge/code 37 | [cmode]: https://github.com/proofit404/company-tern 38 | [tm]: https://github.com/fab1an/JavaScript-Tern-Completion.tmbundle 39 | [sourcelair]: https://www.sourcelair.com 40 | -------------------------------------------------------------------------------- /bin/condense: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var tern = require("../lib/tern"), condense = require("../lib/condense"); 4 | var path = require("path"), fs = require("fs"); 5 | require("../plugin/doc_comment"); 6 | 7 | var localDir = process.cwd(), ourDir = path.resolve(__dirname, ".."); 8 | var spans = true; 9 | 10 | function usage(exit) { 11 | console.error("usage: " + process.argv[1] + " [--name name] [--plugin name]* [--def name]* [+extrafile.js]* [file.js]+"); 12 | if (exit != null) process.exit(exit); 13 | } 14 | 15 | function findFile(file, where, ext) { 16 | if (file.slice(file.length - ext.length) != ext) file += ext; 17 | var local = path.resolve(localDir, file); 18 | if (fs.existsSync(local)) return local; 19 | var our = path.resolve(path.resolve(ourDir, where), file); 20 | if (fs.existsSync(our)) return our; 21 | } 22 | 23 | var defs = [], plugins = {doc_comment: {}}, files = [], name; 24 | 25 | function loadDef(name) { 26 | var found = findFile(name, "defs", ".json"); 27 | if (!found) { 28 | console.error("Could not find def file " + name); 29 | process.exit(1); 30 | } 31 | defs.push(JSON.parse(fs.readFileSync(found, "utf8"))); 32 | } 33 | 34 | loadDef("ecmascript"); 35 | 36 | function loadPlugin(name, val) { 37 | var found = findFile(name, "plugin", ".js"); 38 | if (!found) { 39 | try { 40 | found = require.resolve("tern-" + name); 41 | } catch (e) { 42 | console.error("Could not find plugin " + name); 43 | process.exit(1); 44 | } 45 | } 46 | var mod = require(found); 47 | if (mod.hasOwnProperty("initialize")) mod.initialize(ourDir); 48 | plugins[path.basename(name, ".js")] = val; 49 | } 50 | 51 | for (var i = 2, len = process.argv.length; i < len; ++i) { 52 | var cur = process.argv[i]; 53 | if (cur == "--plugin" && i < len - 1) { 54 | var plugin = process.argv[++i], eq = plugin.indexOf("="); 55 | if (eq > -1) 56 | loadPlugin(plugin.slice(0, eq), JSON.parse(plugin.slice(eq + 1))); 57 | else 58 | loadPlugin(plugin, {}); 59 | } else if (cur == "--def" && i < len - 1) { 60 | loadDef(process.argv[++i]); 61 | } else if (cur == "--name" && i < len - 1) { 62 | name = process.argv[++i]; 63 | } else if (cur == "--no-spans") { 64 | spans = false; 65 | } else if (cur.charAt(0) == "-") { 66 | usage(1); 67 | } else { 68 | files.push(cur); 69 | } 70 | } 71 | 72 | var server = new tern.Server({ 73 | getFile: function(file) { return fs.readFileSync(path.resolve(localDir, file), "utf8"); }, 74 | defs: defs, 75 | plugins: plugins, 76 | debug: true, 77 | projectDir: localDir 78 | }); 79 | 80 | var origins = []; 81 | for (var i = 0; i < files.length; ++i) { 82 | var file = files[i]; 83 | if (file.charAt(0) == "+") { 84 | file = file.slice(1); 85 | } else { 86 | origins.push(file); 87 | if (!name) name = file; 88 | } 89 | if (!fs.existsSync(file)) { 90 | console.error("File " + file + " does not exist"); 91 | process.exit(1); 92 | } 93 | server.addFile(file, fs.readFileSync(file, "utf8")); 94 | } 95 | 96 | if (!origins.length) usage(1); 97 | 98 | server.flush(function(err) { 99 | if (err) throw err; 100 | console.log(JSON.stringify(condense.condense(origins, name, {spans: spans}), null, 2)); 101 | }); 102 | -------------------------------------------------------------------------------- /bin/tern: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // HTTP server for desktop editors 4 | 5 | // Starts a Tern server and wraps it in an HTTP wrapper 6 | // so that editor plug-ins can talk to it. 7 | 8 | var bootstrapServer = require('../lib/bootstrap'); 9 | var fs = require("fs"), path = require("path"), url = require("url"); 10 | 11 | var portFileName = ".tern-port"; 12 | var maxIdleTime = 6e4 * 5; // Shut down after five minutes of inactivity 13 | 14 | var persistent = process.argv.indexOf("--persistent") > -1; 15 | var stripCRs = process.argv.indexOf("--strip-crs") > -1; 16 | var disableLoadingLocal = process.argv.indexOf("--disable-loading-local") > -1; 17 | var verbose = process.argv.indexOf("--verbose") > -1; 18 | var debug = verbose || process.argv.indexOf("--debug") > -1; 19 | var noPortFile = process.argv.indexOf("--no-port-file") > -1; 20 | var host = "127.0.0.1"; 21 | var hostArg = process.argv.indexOf("--host"); 22 | if (hostArg > -1) { 23 | host = process.argv[hostArg + 1]; 24 | if (host == "null" || host == "any") host = null; 25 | } 26 | var port = 0; 27 | var portArg = process.argv.indexOf("--port"); 28 | if (portArg > -1) { 29 | port = Number(process.argv[portArg + 1]); 30 | if (isNaN(port)) port = 0; 31 | } 32 | var ignoreStdin = process.argv.indexOf("--ignore-stdin") > -1; 33 | 34 | var httpServer = require("http").createServer(function(req, resp) { 35 | clearTimeout(shutdown); 36 | shutdown = setTimeout(doShutdown, maxIdleTime); 37 | 38 | var target = url.parse(req.url, true); 39 | if (target.pathname == "/ping") return respondSimple(resp, 200, "pong"); 40 | if (target.pathname != "/") return respondSimple(resp, 404, "No service at " + target.pathname); 41 | 42 | if (req.method == "POST") { 43 | var body = ""; 44 | req.on("data", function (data) { body += data; }); 45 | req.on("end", function() { respond(resp, body); }); 46 | } else if (req.method == "GET") { 47 | if (target.query.doc) respond(resp, target.query.doc); 48 | else respondSimple(resp, 400, "Missing query document"); 49 | } 50 | }); 51 | 52 | var serverConfig = { 53 | disableLoadingLocal: disableLoadingLocal, 54 | tern: { 55 | stripCRs: stripCRs, 56 | debug: debug, 57 | parent: { httpServer: httpServer } 58 | } 59 | }; 60 | var server = bootstrapServer(serverConfig); 61 | 62 | function doShutdown() { 63 | if (persistent) return; 64 | console.log("Was idle for " + Math.floor(maxIdleTime / 6e4) + " minutes. Shutting down."); 65 | process.exit(); 66 | } 67 | 68 | var shutdown = setTimeout(doShutdown, maxIdleTime); 69 | 70 | if (!ignoreStdin) { 71 | process.stdin.on("end", function() { 72 | console.log("stdin pipe closed, killing tern"); 73 | process.exit(); 74 | }); 75 | process.stdin.resume(); 76 | } 77 | 78 | httpServer.listen(port, host, function() { 79 | var port = httpServer.address().port; 80 | if (!noPortFile) { 81 | var portFile = path.resolve(server.projectDir, portFileName); 82 | fs.writeFileSync(portFile, String(port), "utf8"); 83 | process.on("exit", function() { 84 | try { 85 | var cur = Number(fs.readFileSync(portFile, "utf8")); 86 | if (cur == port) fs.unlinkSync(portFile); 87 | } catch(e) {} 88 | }); 89 | } 90 | process.on("SIGINT", function() { 91 | console.log("SIGINT trapped, killing tern"); 92 | process.exit(); 93 | }); 94 | process.on("SIGTERM", function() { 95 | console.log("SIGTERM trapped, killing tern"); 96 | process.exit(); 97 | }); 98 | console.log("Listening on port " + port); 99 | }); 100 | 101 | function respondSimple(resp, status, text) { 102 | resp.writeHead(status, {"content-type": "text/plain; charset=utf-8"}); 103 | resp.end(text); 104 | if (verbose) console.log("Response: " + status + " " + text); 105 | } 106 | 107 | function respond(resp, doc) { 108 | try { var doc = JSON.parse(doc); } 109 | catch(e) { return respondSimple(resp, 400, "JSON parse error: " + e.message); } 110 | if (verbose) console.log("Request: " + JSON.stringify(doc, null, 2)); 111 | 112 | server.request(doc, function(err, data) { 113 | if (err) return respondSimple(resp, 400, String(err)); 114 | resp.writeHead(200, {"content-type": "application/json; charset=utf-8"}); 115 | if (verbose) console.log("Response: " + JSON.stringify(data, null, 2) + "\n"); 116 | resp.end(JSON.stringify(data)); 117 | }); 118 | } 119 | -------------------------------------------------------------------------------- /bin/test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var filter = process.argv[2]; 4 | 5 | require("../test/runcases").runTests(filter); 6 | require("../test/fragments").runTests(filter); 7 | require("../test/condense").runTests(filter); 8 | require("../test/timeout").runTests(filter); 9 | require("../test/reload").runTests(filter); 10 | 11 | process.exit(require("../test/util").hasFailed()); 12 | -------------------------------------------------------------------------------- /bin/update_authors.sh: -------------------------------------------------------------------------------- 1 | # Combine existing list of authors with everyone known in git, sort, add header. 2 | tail --lines=+3 AUTHORS > AUTHORS.tmp 3 | git log --format='%aN' | grep -v 'Sérgio Ramos\|othree_kao\|Angelo$' >> AUTHORS.tmp 4 | echo -e "List of Tern contributors. Updated before every release.\n" > AUTHORS 5 | sort -u AUTHORS.tmp >> AUTHORS 6 | rm -f AUTHORS.tmp 7 | -------------------------------------------------------------------------------- /defs/react.json: -------------------------------------------------------------------------------- 1 | { 2 | "!name": "react", 3 | "!define": { 4 | "Component": { 5 | "!doc": "Components let you split the UI into independent, reusable pieces, and think about each piece in isolation.", 6 | "!url": "https://reactjs.org/docs/components-and-props.html", 7 | "defaultProps": { 8 | "!type": "?", 9 | "!doc": "defaultProps can be defined as a property on the component class itself, to set the default props for the class.", 10 | "!url": "https://facebook.github.io/react/docs/react-component.html#defaultprops" 11 | }, 12 | "displayName": { 13 | "!type": "string", 14 | "!doc": "The displayName string is used in debugging messages.", 15 | "!url": "https://facebook.github.io/react/docs/react-component.html#displayname" 16 | }, 17 | "propTypes": { 18 | "!type": "PropTypes", 19 | "!doc": "To run typechecking on the props for a component, you can assign the special propTypes property.", 20 | "!url": "https://facebook.github.io/react/docs/typechecking-with-proptypes.html" 21 | }, 22 | "prototype": { 23 | "props": "?", 24 | "state": "?", 25 | "setState": { 26 | "!type": "fn(updater: ?, callback?: fn())", 27 | "!doc": "setState() enqueues changes to the component state and tells React that this component and its children need to be re-rendered with the updated state.", 28 | "!url": "https://facebook.github.io/react/docs/react-component.html#setstate" 29 | }, 30 | "forceUpdate": { 31 | "!type": "fn(callback: fn())", 32 | "!doc": "By default, when your component's state or props change, your component will re-render. If your render() method depends on some other data, you can tell React that the component needs re-rendering by calling forceUpdate().", 33 | "!url": "https://facebook.github.io/react/docs/react-component.html#forceupdate" 34 | } 35 | } 36 | }, 37 | "ReactElement": { 38 | "!doc": "Elements are the smallest building blocks of React apps.", 39 | "!url": "https://reactjs.org/docs/rendering-elements.html" 40 | }, 41 | "ref": { 42 | "!doc": "Refs provide a way to access DOM nodes or React elements created in the render method.", 43 | "!url": "https://reactjs.org/docs/refs-and-the-dom.html", 44 | "current": "node" 45 | }, 46 | "PropTypes": { 47 | "!doc": "Runtime type checking for React props and similar objects.", 48 | "!url": "https://github.com/facebook/prop-types/", 49 | "array": { 50 | "!type": "PropType", 51 | "!doc": "You can declare that a prop is a specific JS primitive." 52 | }, 53 | "bool": { 54 | "!type": "PropType", 55 | "!doc": "You can declare that a prop is a specific JS primitive." 56 | }, 57 | "func": { 58 | "!type": "PropType", 59 | "!doc": "You can declare that a prop is a specific JS primitive." 60 | }, 61 | "number": { 62 | "!type": "PropType", 63 | "!doc": "You can declare that a prop is a specific JS primitive." 64 | }, 65 | "object": { 66 | "!type": "PropType", 67 | "!doc": "You can declare that a prop is a specific JS primitive." 68 | }, 69 | "string": { 70 | "!type": "PropType", 71 | "!doc": "You can declare that a prop is a specific JS primitive." 72 | }, 73 | "symbol": { 74 | "!type": "PropType", 75 | "!doc": "You can declare that a prop is a specific JS primitive." 76 | }, 77 | "node": { 78 | "!type": "PropType", 79 | "!doc": "Anything that can be rendered: numbers, strings, elements or an array (or fragment) containing these types." 80 | }, 81 | "element": { 82 | "!type": "PropType", 83 | "!doc": "A React element." 84 | }, 85 | "instanceOf": { 86 | "!type": "fn(class: fn()) -> PropType", 87 | "!doc": "You can also declare that a prop is an instance of a class. This uses JS's instanceof operator." 88 | }, 89 | "oneOf": { 90 | "!type": "fn([?]) -> PropType", 91 | "!doc": "You can ensure that your prop is limited to specific values by treating it as an enum." 92 | }, 93 | "oneOfType": { 94 | "!type": "fn([PropType]) -> PropType", 95 | "!doc": "An object that could be one of many types" 96 | }, 97 | "arrayOf": { 98 | "!type": "fn(propType: PropType) -> PropType", 99 | "!doc": "An array of a certain type" 100 | }, 101 | "objectOf": { 102 | "!type": "fn(propType: PropType) -> PropType", 103 | "!doc": "An object with property values of a certain type" 104 | }, 105 | "shape": { 106 | "!type": "fn(propTypes: PropTypes) -> PropType", 107 | "!doc": "An object taking on a particular shape" 108 | }, 109 | "any": { 110 | "!type": "PropType", 111 | "!doc": "A value of any data type" 112 | } 113 | }, 114 | "PropType": { 115 | "isRequired": { 116 | "!doc": "You can chain any of the above with `isRequired` to make sure a warning is shown if the prop isn't provided" 117 | } 118 | } 119 | }, 120 | "PropTypes": "PropTypes", 121 | "React": { 122 | "!doc": "React is the entry point to the React library.", 123 | "!url": "https://reactjs.org/docs/react-api.html", 124 | "Component": { 125 | "!type": "fn() -> Component", 126 | "!doc": "React.Component is the base class for React components when they are defined using ES6 classes", 127 | "!url": "https://reactjs.org/docs/react-api.html#reactcomponent", 128 | "prototype": "Component" 129 | }, 130 | "PureComponent": { 131 | "!type": "fn() -> Component", 132 | "!doc": "React.PureComponent is similar to React.Component. The difference between them is that React.Component doesn’t implement shouldComponentUpdate(), but React.PureComponent implements it with a shallow prop and state comparison.", 133 | "!url": "https://reactjs.org/docs/react-api.html#reactpurecomponent", 134 | "prototype": "Component" 135 | }, 136 | "memo": { 137 | "!type": "Component", 138 | "!doc": "React.memo is a higher order component. It’s similar to React.PureComponent but for function components instead of classes.", 139 | "!url": "https://reactjs.org/docs/react-api.html#reactmemo" 140 | }, 141 | "createElement": { 142 | "!type": "fn(element: ReactElement, props: [?], children: [?]) -> ReactElement", 143 | "!doc": "Create and return a new React element of the given type.", 144 | "!url": "https://reactjs.org/docs/react-api.html#createelement" 145 | }, 146 | "cloneElement": { 147 | "!type": "fn(element: ReactElement, props: [?], children: [?]) -> ReactElement", 148 | "!doc": "Clone and return a new React element using element as the starting point.", 149 | "!url": "https://reactjs.org/docs/react-api.html#cloneelement" 150 | }, 151 | "createFactory": { 152 | "!type": "fn(type: string|Component|React.Fragment) -> fn() -> ReactElement", 153 | "!doc": "", 154 | "!url": "https://reactjs.org/docs/react-api.html#createfactory" 155 | }, 156 | "isValidElement": { 157 | "!type": "fn(object: ?) -> bool", 158 | "!doc": "Verifies the object is a React element. Returns true or false.", 159 | "!url": "https://reactjs.org/docs/react-api.html#isvalidelement" 160 | }, 161 | "Children": { 162 | "map": { 163 | "!type": "fn(children: ?, fn()) -> [?]", 164 | "!doc": "Invokes a function on every immediate child contained within children with this set to thisArg.", 165 | "!url": "https://reactjs.org/docs/react-api.html#reactchildrenmap" 166 | }, 167 | "forEach": { 168 | "!type": "fn(children: ?, fn())", 169 | "!doc": "Like React.Children.map() but does not return an array.", 170 | "!url": "https://reactjs.org/docs/react-api.html#reactchildrenforeach" 171 | }, 172 | "count": { 173 | "!type": "fn(?) -> number", 174 | "!doc": "Returns the total number of components in children, equal to the number of times that a callback passed to map or forEach would be invoked.", 175 | "!url": "https://reactjs.org/docs/react-api.html#reactchildrencount" 176 | }, 177 | "only": { 178 | "!type": "fn(?) -> Component", 179 | "!doc": "Verifies that children has only one child (a React element) and returns it. Otherwise this method throws an error.", 180 | "!url": "https://reactjs.org/docs/react-api.html#reactchildrenonly" 181 | }, 182 | "toArray": { 183 | "!type": "fn(?) -> [Component]", 184 | "!doc": "Returns the children opaque data structure as a flat array with keys assigned to each child.", 185 | "!url": "https://reactjs.org/docs/react-api.html#reactchildrentoarray" 186 | } 187 | }, 188 | "Fragment": { 189 | "!type": "Component", 190 | "!doc": "The React.Fragment component lets you return multiple elements in a render() method without creating an additional DOM element", 191 | "!url": "https://reactjs.org/docs/react-api.html#reactfragment" 192 | }, 193 | "createRef": { 194 | "!type": "fn() -> ref", 195 | "!doc": "Refs are created using React.createRef() and attached to React elements via the ref attribute.", 196 | "!url": "https://reactjs.org/docs/refs-and-the-dom.html#creating-refs" 197 | }, 198 | "forwardRef": { 199 | "!type": "fn(render: fn()) -> Component", 200 | "!doc": "Ref forwarding is a technique for automatically passing a ref through a component to one of its children.", 201 | "!url": "https://reactjs.org/docs/forwarding-refs.html" 202 | } 203 | }, 204 | "ReactDOM": { 205 | "!doc": "The react-dom package provides DOM-specific methods that can be used at the top level of your app and as an escape hatch to get outside of the React model if you need to.", 206 | "!url": "https://reactjs.org/docs/react-dom.html", 207 | "render": { 208 | "!type": "fn(element: ReactElement, container: Element, callback?: fn()) -> !0", 209 | "!doc": "Render a React element into the DOM in the supplied container and return a reference to the component (or returns null for stateless components).", 210 | "!url": "https://facebook.github.io/react/docs/react-dom.html#render" 211 | }, 212 | "hydrate": { 213 | "!type": "fn(element: ReactElement, container: Element, callback?: fn()) -> !0", 214 | "!doc": "Same as render(), but is used to hydrate a container whose HTML contents were rendered by ReactDOMServer.", 215 | "!url": "https://reactjs.org/docs/react-dom.html#hydrate" 216 | }, 217 | "unmountComponentAtNode": { 218 | "!type": "fn(container: Element) -> bool", 219 | "!doc": "Remove a mounted React component from the DOM and clean up its event handlers and state.", 220 | "!url": "https://facebook.github.io/react/docs/react-dom.html#unmountcomponentatnode" 221 | }, 222 | "findDOMNode": { 223 | "!type": "fn(component: ReactElement) -> Element", 224 | "!doc": "If this component has been mounted into the DOM, this returns the corresponding native browser DOM element.", 225 | "!url": "https://facebook.github.io/react/docs/react-dom.html#finddomnode" 226 | }, 227 | "createPortal": { 228 | "!type": "fn(component: ReactElement, container: Element) -> ReactElement", 229 | "!doc": "Creates a portal. Portals provide a way to render children into a DOM node that exists outside the hierarchy of the DOM component.", 230 | "!url": "https://reactjs.org/docs/portals.html" 231 | } 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /doc/BACKERS.txt: -------------------------------------------------------------------------------- 1 | Backers of the donation drive that made Tern open source 2 | See http://www.indiegogo.com/projects/tern-intelligent-javascript-editing 3 | 4 | Super backers: 5 | 6 | Telerik 7 | Karl Guertin 8 | Peter Lubbers 9 | Remy Sharp 10 | Anton Kovalyov 11 | Meryn Stol 12 | Rahul Choudhury 13 | Jake Verbaten 14 | Sébastien Gruhier 15 | Tim Branyen 16 | 17 | Further backers: 18 | 19 | Christophe Porteneuve 20 | James Morrin 21 | Marko Mikulicic 22 | Morgan Roderick 23 | Paul Irish 24 | wesen 25 | Aron Woost 26 | Brandon Frohs 27 | Dan Rogers 28 | Jan Krems 29 | Jörn Zaefferer 30 | Karl Forshaw 31 | Matt Claypotch 32 | Miguel Castillo 33 | Mihai Bazon 34 | patrick.wolf 35 | Rebecca Murphey 36 | Scott Sauyet 37 | Todd Wolfson 38 | Erik Söhnel 39 | Matt Pass 40 | Paul Grock 41 | Damian Soto 42 | Fayolle 43 | Pierre Spring 44 | Douglas Soares de Andrade 45 | egonelbre 46 | Gavin Suntop 47 | ivan4th 48 | Matt Field 49 | Tiffany Conroy 50 | Markus Messner-Chaney 51 | fronx 52 | A/C Luis Cardoso FeedZai S.A. 53 | aaron bieber 54 | Aaron Toth 55 | Adrian Christopher Schaedle 56 | Aijaz Mohammad 57 | Alberto Zaccagni 58 | Alex Sexton 59 | Alexandre Gaudencio 60 | Amrit Sundar Tuladhar 61 | Andreas Köberle 62 | Andrey Okonechnikov 63 | Andrius Kazdailevicius 64 | Antons Jakimovs 65 | Antranig Basman 66 | aowerb 67 | Arnout Kazemier 68 | Artur Carvalho 69 | Asbjørn Andersen 70 | Balázs Galambosi 71 | Baptiste Millou 72 | Bastiaan Rasing 73 | Ben Marvell 74 | Benoît Zugmeyer 75 | Björn Günzel 76 | Brad Dougherty 77 | Brandon Satrom 78 | Brian Brennan 79 | Brian Frichette 80 | bruce.mitchener 81 | Byron Darlison 82 | Chris Engebretson 83 | Christoffer Björkskog 84 | Christoph Schwienheer, bei Zierock 85 | Christopher Wyllie 86 | Codevise Solutions / Vangelis Tsoumenis 87 | Cristian Carlesso 88 | Cuong Vo 89 | Damien Klinnert 90 | Dan Reeves 91 | Dane Summers 92 | Daniel Kloosterman 93 | Daniel Wippermann 94 | Darren Waddell 95 | Daryl Koopersmith 96 | David Håsäther 97 | David JENNI 98 | David Sargeant 99 | David White 100 | Dethe Elza 101 | Dirk Ginader 102 | Dmitri Molchanenko 103 | Doc No 104 | Dominique Poulain 105 | Drew Miller 106 | Drew Neil 107 | Edd Hannay 108 | edouard duplessis 109 | Eike Send 110 | Felix Raab 111 | Ferdinand Salis-Samaden 112 | Filip Noetzel 113 | Frederick Ostrander 114 | Fronx Wurmus 115 | Gleb Mazovetskiy 116 | Gregers Rygg 117 | Hans Christian Reinl 118 | Hans Hübner 119 | Henrik vendelbo 120 | Henry Allen-Tilford 121 | Herculano Campos 122 | Hieu Nguyen 123 | holmlundlists 124 | Jack Franklin 125 | Jan Lehnardt 126 | Jan Löfberg 127 | Jans Aasman 128 | Jason LeMoine 129 | Jendrik Johannes 130 | jeroen14 131 | Jim Tittsler 132 | Joel M Hooks 133 | John Pallister 134 | Jonas Coch 135 | Jonathan Smart 136 | Joshua Kalis 137 | José María Balsas 138 | Julian Wachholz 139 | Justin Lowery 140 | Jyri Tuulos 141 | Karl J Smith 142 | Karl Vestin 143 | Kasper Fabæch Brandt 144 | Kevin Vlahos 145 | Marcell Mars 146 | Konstantin Käfer 147 | Kurt MacDonald 148 | Kyle Barrow 149 | Kyle Powers 150 | Lajos Koszti 151 | Laurie Kemmerer 152 | Loïc Moriamé 153 | Lukas F. Hartmann 154 | Luke Arduini 155 | Luke Karrys 156 | Luís Almeida 157 | Maciej Małecki 158 | Maksim Lin 159 | Marcin Wosinek 160 | Marco Rogers 161 | Marius Gundersen 162 | Martin Hansen 163 | Martin Moellenbeck 164 | Mat Brennan 165 | Matt Smillie 166 | Matthew Buscemi 167 | Matthew L Daniel 168 | Maurizio Mangione 169 | Max F. Albrecht 170 | Michael Goulbourn 171 | Michael Kessler 172 | Michael Lehenbauer 173 | Michael Russell 174 | Michael Zehrer 175 | Mikael Abrahamsson 176 | Mikko Ohtamaa 177 | Jonathan Puckey 178 | Niall O'Higgins 179 | Nicholas Niemeir 180 | Nicky B Hajal 181 | Niclas Hoyer 182 | Niklas Lindgren 183 | Olav Cleemann 184 | Oskar Schöldström 185 | Paolo Negri 186 | Patrick Arminio 187 | Patrik Henningsson 188 | Paul Conroy 189 | Paul Dijou 190 | Paul Young 191 | Peeter Tomberg 192 | Per Ejeklint 193 | Per Thulin 194 | Peter Wong 195 | Phillip Strong 196 | pjschaffner 197 | Raphael LANG 198 | Ray Cohen 199 | Richard Powell 200 | Rick Richardson 201 | Robert Oroszi 202 | Robin Brandt 203 | Ryan Morlok 204 | Ryan Wachtl 205 | Sveinung Røsaker 206 | Salvatore Formisano 207 | Scott White 208 | Seth House 209 | Simon Cossar 210 | Simon Goumaz 211 | Simon Jockers 212 | Stefan Gojan 213 | Stefan Hayden 214 | Stefan Kienzle 215 | Stephan Hoyer 216 | steve heron 217 | tape.io GmbH / Sascha Reuter 218 | Thanasis Polychronakis 219 | Thomas Bassetto 220 | Thomas Roger 221 | Tiago Rodrigues 222 | Tobias Kaatz 223 | Tom MacWright 224 | Tomasz Stryjewski 225 | Tommi Laine 226 | Tony Atkins 227 | Trey Griffith 228 | Vernon Kesner 229 | Victor Nguyen 230 | Vitaliy Meshchaninov 231 | Wolfram Twelker 232 | Zachary King 233 | Zaki Manian 234 | Ziga Ham 235 | Øystein Kjærnet 236 | 范致宣 237 | aritter 238 | David Flanagan 239 | Klemen Slavič 240 | Leichnig Loyce 241 | assaf3 242 | hebrox 243 | Kasra Kyanzadeh 244 | Pauld Aoust 245 | Roland Warmerdam 246 | Thomas Gorny 247 | Thomas Greiner 248 | Anders Madsen 249 | ceejceej 250 | Curist Chang 251 | Danny Coates 252 | David Owens 253 | dinalamdany 254 | dsmlover 255 | ebraun1 256 | Eugen Tudorancea 257 | Frode Nilsen 258 | glen15 259 | Irakli Nadareishvili 260 | Jack Ellis 261 | Jon Gjengset 262 | Michiel van Oosten 263 | mwsherman 264 | ncoder2000 265 | oogatta 266 | owz534 267 | rekurzija 268 | Rémi Gérard-Marchant 269 | rvagg 270 | shikakaa 271 | sressler1 272 | Stephan Seidt 273 | Tristen Brown 274 | Volodymyr Prokopyuk 275 | wbinnssmith 276 | Austin Montoya 277 | andr1 278 | Andrew Jones 279 | Bogdan Durla 280 | botequilha 281 | boyerchen 282 | brauer1 283 | Dmitry Semenov 284 | Dominik Rask 285 | eliasgs 286 | Hayden Chambers 287 | hugomallet06 288 | ithkuil 289 | jacek3 290 | jfromaniello 291 | Jiri Zuna 292 | johannes20 293 | Kyle Hardgrave 294 | larry8 295 | Lorenz Anmey 296 | Martin Hečko 297 | Max Shishkin 298 | Michi-Mueller 299 | Oliver Jash 300 | Gobin Goos 301 | Bob McBride 302 | roriz87 303 | RRTimmerman 304 | Sascha Greiff 305 | Sbastien Lebel 306 | Serkan Yersen 307 | Sindre Sorhus 308 | steve228uk 309 | Thomas Bartels 310 | Thomas Ruoff 311 | twilightfeel 312 | Wil Moore 313 | Xavier Seignard 314 | Cyrielvant End 315 | Tom Greuter 316 | Andrew Blackshaw 317 | Ben 318 | bitomule 319 | Luiz Felipe 320 | Nagy Istvn 321 | pembeci 322 | Peter Josling 323 | spacehelmetboy 324 | Bryan Poetz 325 | I. Zakurenyi 326 | Kuno Woudt 327 | Michael Ficarra 328 | michal17 329 | Alex Koloskov 330 | Antonio Murdaca 331 | bestander 332 | Bjarne Heden 333 | Bob Foster 334 | Brandon Williams 335 | bzmwillemsen 336 | Carwin Young 337 | dainstore 338 | Dan Bravender 339 | Daniel Hjelm 340 | Daniel Lager 341 | Dariusz Mlodnicki 342 | Devin Clark 343 | dragos 344 | dskreppsigg 345 | dyuri 346 | eelke 347 | Ekrem Bykkaya 348 | Erik Dasque 349 | Erik Kronberg 350 | erwinmombay 351 | excelenter 352 | fishlevel 353 | Florent Galland 354 | Francesco di Palma 355 | Francisc Romano 356 | Henning Schrder 357 | Hugo Agbonon 358 | Hwulex 359 | iley1 360 | Isaac Z. Schlueter 361 | Ivan Nikoli 362 | James Westgate 363 | James Campos 364 | Januardo Kusuma 365 | jdduarte 366 | jdharding 367 | jose48 368 | Justin Yost 369 | karl58 370 | Kasper Souren 371 | Koryl Prince 372 | kris80 373 | Kristofor Carle 374 | matjazl 375 | Matthew Dierker 376 | mcw0933 377 | mihaipaun01 378 | mr.pohoda 379 | naoyuki.kanezawa 380 | Nikita Vasilyev 381 | Nikki Stevens 382 | Radovan Lozej 383 | Rob Alarcon 384 | roger56 385 | sngsheftel 386 | snorremd 387 | steele 388 | Stefan Huber (Signalwerk) 389 | tanel 390 | Tom Parker 391 | Travis Heeter 392 | UniqueVN 393 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | all: manual.html 2 | 3 | manual.html: manual.txt asciidoc.conf 4 | PATH=../node_modules/codemirror/bin:$(PATH) asciidoc --backend=html5 -o manual.html manual.txt 5 | -------------------------------------------------------------------------------- /doc/asciidoc.conf: -------------------------------------------------------------------------------- 1 | [miscellaneous] 2 | newline=\n 3 | 4 | [attributes] 5 | disable-javascript=True 6 | warnings=False 7 | #pygments=True 8 | highlight=True 9 | 10 | [literal-inlinemacro] 11 | {passtext} 12 | 13 | [sect0] 14 |

{id? }{title}

15 | | 16 | 17 | [sect1] 18 |

{id? }{title}

19 | | 20 | 21 | [sect2] 22 |

{id? }{title}

23 | | 24 | 25 | [sect3] 26 |

{id? }{title}

27 | | 28 | 29 | [paragraph] 30 |

{id? }{title?

{title}
} 31 | | 32 |

33 | 34 | [listtags-labeled] 35 | list=
|
36 | term=
|
37 | item=
|
38 | text=

|

39 | 40 | [header] 41 | 42 | 43 | 44 | 45 | {doctitle} 46 | 47 | 48 |
49 | 55 |
56 |

{doctitle}

57 | 58 | [footer] 59 | 60 | 61 | [source-highlight-block] 62 | 63 | | 64 | 65 | 66 | [blockdef-listing] 67 | filter=../node_modules/codemirror/bin/source-highlight -s {language} 68 | delimiter=^-{4,}$ 69 | template=source-highlight-block 70 | subs=verbatim 71 | -------------------------------------------------------------------------------- /doc/demo/demo.css: -------------------------------------------------------------------------------- 1 | .CodeMirror { 2 | border: 1px solid silver; 3 | height: 400px; 4 | line-height: 1.3; 5 | font-size: 13pt; 6 | font-family: "Ubuntu Mono", monospace; 7 | z-index: 2; 8 | } 9 | .CodeMirror pre { 10 | padding: 0 9px; 11 | } 12 | .CodeMirror-linenumber { 13 | font-size: 80%; 14 | padding-top: 2px; 15 | } 16 | 17 | ul.tabs { 18 | list-style: none; 19 | margin: 0 0 -1px 0; 20 | padding: 0 0 0 37px; 21 | font-family: "Ubuntu Mono", monospace; 22 | overflow: hidden; 23 | } 24 | ul.tabs li { 25 | font-size: 11pt; 26 | color: #777; 27 | cursor: pointer; 28 | margin: 0 2px 0 0; 29 | padding: 2px 10px 5px; 30 | border: 1px solid silver; 31 | border-bottom-width: 0px; 32 | background: #f3f3f3; 33 | display: inline-block; 34 | z-index: 1; 35 | border-top-left-radius: 6px; 36 | border-top-right-radius: 6px; 37 | position: relative; 38 | } 39 | ul.tabs li:hover { 40 | color: #444; 41 | } 42 | ul.tabs li.selected { 43 | background: white; 44 | color: #222; 45 | z-index: 3; 46 | cursor: default; 47 | } 48 | ul.tabs li:before, 49 | ul.tabs li:after { 50 | position: absolute; 51 | bottom: 0; 52 | width: 6px; 53 | height: 6px; 54 | content: " "; 55 | border: 1px solid silver; 56 | } 57 | ul.tabs li:before { 58 | left: -7px; 59 | border-bottom-right-radius: 6px; 60 | border-width: 0 1px 1px 0; 61 | box-shadow: 2px 2px 0 #f3f3f3; 62 | } 63 | ul.tabs li:after { 64 | right: -7px; 65 | border-bottom-left-radius: 6px; 66 | border-width: 0 0 1px 1px; 67 | box-shadow: -2px 2px 0 #f3f3f3; 68 | } 69 | ul.tabs li.selected:before { 70 | box-shadow: 2px 2px 0 white; 71 | } 72 | ul.tabs li.selected:after { 73 | box-shadow: -2px 2px 0 white; 74 | } 75 | 76 | #menus { 77 | position: absolute; 78 | right: 0; 79 | top: 0; 80 | } 81 | #menus select { 82 | font-family: "Ubuntu Mono", monospace; 83 | font-size: 11pt; 84 | width: 8em; 85 | } 86 | 87 | .CodeMirror div.CodeMirror-cursor { 88 | border-left: 2px solid #444; 89 | } 90 | 91 | .CodeMirror-hint { font-size: 12pt; } 92 | -------------------------------------------------------------------------------- /doc/demo/demo.js: -------------------------------------------------------------------------------- 1 | var project 2 | 3 | function Project(name, place, config, docs) { 4 | this.name = name 5 | this.docs = Object.create(null) 6 | this.tabs = place.appendChild(document.createElement("ul")) 7 | this.tabs.className = "tabs" 8 | var self = this 9 | CodeMirror.on(this.tabs, "click", function(e) { 10 | var target = e.target || e.srcElement 11 | if (target.nodeName.toLowerCase() != "li") return 12 | var doc = self.findDoc(target.textContent) 13 | if (doc) self.selectDoc(doc) 14 | }) 15 | 16 | var myConf = { 17 | switchToDoc: function(name) { self.selectDoc(self.findDoc(name)) }, 18 | useWorker: false 19 | } 20 | for (var prop in config) myConf[prop] = config[prop] 21 | var server = this.server = new CodeMirror.TernServer(myConf) 22 | 23 | var firstDoc 24 | for (var name in docs) { 25 | var data = this.registerDoc(name, new CodeMirror.Doc(docs[name], "javascript")) 26 | if (!firstDoc) firstDoc = data 27 | } 28 | this.curDoc = firstDoc 29 | this.setSelectedTab(firstDoc) 30 | 31 | var keyMap = { 32 | "Ctrl-I": function(cm) { server.showType(cm); }, 33 | "Ctrl-O": function(cm) { server.showDocs(cm); }, 34 | "Ctrl-Space": function(cm) { server.complete(cm); }, 35 | "Alt-.": function(cm) { server.jumpToDef(cm); }, 36 | "Alt-,": function(cm) { server.jumpBack(cm); }, 37 | "Ctrl-Q": function(cm) { server.rename(cm); } 38 | } 39 | this.editor = new CodeMirror(place, { 40 | lineNumbers: true, 41 | extraKeys: keyMap, 42 | matchBrackets: true, 43 | value: firstDoc.doc 44 | }) 45 | this.editor.on("cursorActivity", function(cm) { server.updateArgHints(cm); }) 46 | } 47 | 48 | Project.prototype = { 49 | findDoc: function(name) { return this.docs[name] }, 50 | 51 | registerDoc: function(name, doc) { 52 | this.server.addDoc(name, doc) 53 | var data = this.docs[name] = { 54 | name: name, 55 | doc: doc, 56 | tab: this.tabs.appendChild(document.createElement("li")) 57 | } 58 | data.tab.textContent = name 59 | return data 60 | }, 61 | 62 | unregisterDoc: function(doc) { 63 | this.server.delDoc(doc.name) 64 | delete this.docs[doc.name] 65 | this.tabs.removeChild(doc.tab) 66 | if (this.curDoc == doc) for (var n in this.docs) return this.selectDoc(this.docs[n]) 67 | }, 68 | 69 | setSelectedTab: function(doc) { 70 | for (var n = this.tabs.firstChild; n; n = n.nextSibling) 71 | n.className = n.textContent == doc.name ? "selected" : "" 72 | }, 73 | 74 | selectDoc: function(doc) { 75 | if (doc == this.curDoc) return 76 | this.server.hideDoc(this.curDoc) 77 | this.setSelectedTab(doc) 78 | this.curDoc = doc 79 | this.editor.swapDoc(doc.doc) 80 | } 81 | } 82 | 83 | // Initialization 84 | 85 | function load(file, c) { 86 | var xhr = new XMLHttpRequest(); 87 | xhr.open("get", file, true); 88 | xhr.send(); 89 | xhr.onreadystatechange = function() { 90 | if (xhr.readyState == 4) c(xhr.responseText, xhr.status); 91 | }; 92 | } 93 | 94 | CodeMirror.on(window, "load", function() { 95 | var cmds = document.getElementById("commands") 96 | CodeMirror.on(cmds, "change", function() { 97 | if (!project || cmds.selectedIndex == 0) return 98 | var found = commands[cmds.value] 99 | cmds.selectedIndex = 0 100 | project.editor.focus() 101 | if (found) found() 102 | }) 103 | 104 | var projects = document.getElementById("projects") 105 | var projectNames = [] 106 | var projectTags = document.querySelectorAll("project") 107 | for (var i = 0; i < projectTags.length; i++) { 108 | var opt = projects.appendChild(document.createElement("option")) 109 | projectNames.push(opt.textContent = projectTags[i].id) 110 | } 111 | CodeMirror.on(projects, "change", function() { 112 | if (projects.selectedIndex != 0) 113 | initProject(projects.value, function() { 114 | projects.selectedIndex = 0 115 | project.editor.focus() 116 | }) 117 | }) 118 | function updateFromHash() { 119 | var name = location.hash.slice(1) 120 | if (projectNames.indexOf(name) > -1 && 121 | (!project || project.name != name)) { 122 | initProject(name) 123 | return true 124 | } 125 | } 126 | CodeMirror.on(window, "hashchange", updateFromHash) 127 | 128 | updateFromHash() || initProject("simple") 129 | }) 130 | 131 | function loadFiles(project, c) { 132 | var found = {} 133 | function inner(node) { 134 | while (node && node.nodeName != "PRE") node = node.nextSibling 135 | if (!node) return c(found) 136 | if (node.hasAttribute("file")) { 137 | load(node.getAttribute("file"), function(data) { 138 | found[node.id] = data 139 | inner(node.nextSibling) 140 | }) 141 | } else { 142 | found[node.id] = node.textContent 143 | inner(node.nextSibling) 144 | } 145 | } 146 | inner(project.firstChild) 147 | } 148 | 149 | var defsLoaded = Object.create(null) 150 | function loadDefs(defs, c) { 151 | var result = [], loaded = 0 152 | for (var i = 0; i < defs.length; ++i) (function(i) { 153 | var name = defs[i] 154 | if (defsLoaded[name]) { 155 | result[i] = defsLoaded[name] 156 | if (++loaded == defs.length) c(result) 157 | } else { 158 | load("../../defs/" + name + ".json", function(json) { 159 | defsLoaded[name] = result[i] = JSON.parse(json) 160 | if (++loaded == defs.length) c(result) 161 | }) 162 | } 163 | })(i) 164 | } 165 | 166 | function words(str) { 167 | return str ? str.split(" ") : [] 168 | } 169 | 170 | function initProject(name, c) { 171 | var node = document.getElementById(name) 172 | loadDefs(words(node.getAttribute("data-libs")), function(defs) { 173 | var plugins = {} 174 | words(node.getAttribute("data-plugins")).forEach(function(name) { plugins[name] = true }) 175 | 176 | if (plugins.requirejs) plugins.requirejs = {override: {"jquery": "=$"}} 177 | 178 | loadFiles(node, function(files) { 179 | var place = document.getElementById("place") 180 | place.textContent = "" 181 | 182 | if (project) project.server.destroy() 183 | 184 | project = new Project(name, place, { 185 | defs: defs, 186 | plugins: plugins 187 | }, files) 188 | location.hash = "#" + name 189 | c && c() 190 | }) 191 | }) 192 | } 193 | 194 | var commands = { 195 | complete: function() { project.server.complete(project.editor) }, 196 | jumptodef: function() { project.server.jumpToDef(project.editor) }, 197 | finddocs: function() { project.server.showDocs(project.editor) }, 198 | findtype: function() { project.server.showType(project.editor) }, 199 | rename: function() { project.server.rename(project.editor) }, 200 | addfile: function() { 201 | var name = prompt("Name of the new buffer", "") 202 | if (name == null) return 203 | if (!name) name = "test" 204 | var i = 0 205 | while (project.findDoc(name + (i || ""))) ++i 206 | project.selectDoc(project.registerDoc(name + (i || ""), new CodeMirror.Doc("", "javascript"))) 207 | }, 208 | delfile: function() { 209 | var hasDoc = false 210 | for (var _ in project.docs) { hasDoc = true; break } 211 | if (hasDoc) project.unregisterDoc(project.curDoc) 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /doc/demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Tern Demo 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 46 | 47 |

Tern demo

48 | 49 |

This editor is hooked up to Tern. The drop-down in the top right 50 | corner lists the commands and keyboard shortcuts available. Output and 51 | function argument hints will appear in the bar below the editor.

52 | 53 |
54 | 69 |
70 |
71 | 72 | 226 | 227 | 228 | -------------------------------------------------------------------------------- /doc/demo/polyfill.js: -------------------------------------------------------------------------------- 1 | // Shims to fill in enough of ECMAScript 5 to make Tern run. Does not 2 | // supply standard-compliant methods, in that some functionality is 3 | // left out (such as second argument to Object.create, self args in 4 | // array methods, etc). WILL clash with other ECMA5 polyfills in a 5 | // probably disruptive way. 6 | 7 | (function() { 8 | Object.create = Object.create || (function() { 9 | if (!({__proto__: null} instanceof Object)) 10 | return function(base) { return {__proto__: base}; }; 11 | function ctor() {} 12 | var frame = document.body.appendChild(document.createElement("iframe")); 13 | frame.src = "javascript:"; 14 | var empty = frame.contentWindow.Object.prototype; 15 | delete empty.hasOwnProperty; 16 | delete empty.isPrototypeOf; 17 | delete empty.propertyIsEnumerable; 18 | delete empty.valueOf; 19 | delete empty.toString; 20 | delete empty.toLocaleString; 21 | delete empty.constructor; 22 | return function(base) { ctor.prototype = base || empty; return new ctor(); }; 23 | })(); 24 | 25 | // Array methods 26 | 27 | var AP = Array.prototype; 28 | 29 | AP.some = AP.some || function(pred) { 30 | for (var i = 0; i < this.length; ++i) if (pred(this[i], i)) return true; 31 | }; 32 | 33 | AP.forEach = AP.forEach || function(f) { 34 | for (var i = 0; i < this.length; ++i) f(this[i], i); 35 | }; 36 | 37 | AP.indexOf = AP.indexOf || function(x, start) { 38 | for (var i = start || 0; i < this.length; ++i) if (this[i] === x) return i; 39 | return -1; 40 | }; 41 | 42 | AP.lastIndexOf = AP.lastIndexOf || function(x, start) { 43 | for (var i = start == null ? this.length - 1 : start; i >= 0; ++i) if (this[i] === x) return i; 44 | return -1; 45 | }; 46 | 47 | AP.map = AP.map || function(f) { 48 | for (var r = [], i = 0; i < this.length; ++i) r.push(f(this[i], i)); 49 | return r; 50 | }; 51 | 52 | Array.isArray = Array.isArray || function(v) { 53 | return Object.prototype.toString.call(v) == "[object Array]"; 54 | }; 55 | 56 | String.prototype.trim = String.prototype.trim || function() { 57 | var from = 0, to = this.length; 58 | while (/\s/.test(this.charAt(from))) ++from; 59 | while (/\s/.test(this.charAt(to - 1))) --to; 60 | return this.slice(from, to); 61 | }; 62 | 63 | /*! JSON v3.2.4 | http://bestiejs.github.com/json3 | Copyright 2012, Kit Cambridge | http://kit.mit-license.org */ 64 | if (!window.JSON) (function(){var e=void 0,i=!0,k=null,l={}.toString,m,n,p="function"===typeof define&&define.c,q=!p&&"object"==typeof exports&&exports;q||p?"object"==typeof JSON&&JSON?p?q=JSON:(q.stringify=JSON.stringify,q.parse=JSON.parse):p&&(q=this.JSON={}):q=this.JSON||(this.JSON={});var r,t,u,x,z,B,C,D,E,F,G,H,I,J=new Date(-3509827334573292),K,O,P;try{J=-109252==J.getUTCFullYear()&&0===J.getUTCMonth()&&1==J.getUTCDate()&&10==J.getUTCHours()&&37==J.getUTCMinutes()&&6==J.getUTCSeconds()&&708==J.getUTCMilliseconds()}catch(Q){} 65 | function R(b){var c,a,d,j=b=="json";if(j||b=="json-stringify"||b=="json-parse"){if(b=="json-stringify"||j){if(c=typeof q.stringify=="function"&&J){(d=function(){return 1}).toJSON=d;try{c=q.stringify(0)==="0"&&q.stringify(new Number)==="0"&&q.stringify(new String)=='""'&&q.stringify(l)===e&&q.stringify(e)===e&&q.stringify()===e&&q.stringify(d)==="1"&&q.stringify([d])=="[1]"&&q.stringify([e])=="[null]"&&q.stringify(k)=="null"&&q.stringify([e,l,k])=="[null,null,null]"&&q.stringify({A:[d,i,false,k,"\x00\u0008\n\u000c\r\t"]})== 66 | '{"A":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}'&&q.stringify(k,d)==="1"&&q.stringify([1,2],k,1)=="[\n 1,\n 2\n]"&&q.stringify(new Date(-864E13))=='"-271821-04-20T00:00:00.000Z"'&&q.stringify(new Date(864E13))=='"+275760-09-13T00:00:00.000Z"'&&q.stringify(new Date(-621987552E5))=='"-000001-01-01T00:00:00.000Z"'&&q.stringify(new Date(-1))=='"1969-12-31T23:59:59.999Z"'}catch(f){c=false}}if(!j)return c}if(b=="json-parse"||j){if(typeof q.parse=="function")try{if(q.parse("0")===0&&!q.parse(false)){d= 67 | q.parse('{"A":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}');if(a=d.a.length==5&&d.a[0]==1){try{a=!q.parse('"\t"')}catch(o){}if(a)try{a=q.parse("01")!=1}catch(g){}}}}catch(h){a=false}if(!j)return a}return c&&a}} 68 | if(!R("json")){J||(K=Math.floor,O=[0,31,59,90,120,151,181,212,243,273,304,334],P=function(b,c){return O[c]+365*(b-1970)+K((b-1969+(c=+(c>1)))/4)-K((b-1901+c)/100)+K((b-1601+c)/400)});if(!(m={}.hasOwnProperty))m=function(b){var c={},a;if((c.__proto__=k,c.__proto__={toString:1},c).toString!=l)m=function(a){var b=this.__proto__,a=a in(this.__proto__=k,this);this.__proto__=b;return a};else{a=c.constructor;m=function(b){var c=(this.constructor||a).prototype;return b in this&&!(b in c&&this[b]===c[b])}}c= 69 | k;return m.call(this,b)};n=function(b,c){var a=0,d,j,f;(d=function(){this.valueOf=0}).prototype.valueOf=0;j=new d;for(f in j)m.call(j,f)&&a++;d=j=k;if(a)a=a==2?function(a,b){var c={},d=l.call(a)=="[object Function]",f;for(f in a)!(d&&f=="prototype")&&!m.call(c,f)&&(c[f]=1)&&m.call(a,f)&&b(f)}:function(a,b){var c=l.call(a)=="[object Function]",d,f;for(d in a)!(c&&d=="prototype")&&m.call(a,d)&&!(f=d==="constructor")&&b(d);(f||m.call(a,d="constructor"))&&b(d)};else{j=["valueOf","toString","toLocaleString", 70 | "propertyIsEnumerable","isPrototypeOf","hasOwnProperty","constructor"];a=function(a,b){var c=l.call(a)=="[object Function]",d;for(d in a)!(c&&d=="prototype")&&m.call(a,d)&&b(d);for(c=j.length;d=j[--c];m.call(a,d)&&b(d));}}a(b,c)};R("json-stringify")||(r={"\\":"\\\\",'"':'\\"',"\u0008":"\\b","\u000c":"\\f","\n":"\\n","\r":"\\r","\t":"\\t"},t=function(b,c){return("000000"+(c||0)).slice(-b)},u=function(b){for(var c='"',a=0,d;d=b.charAt(a);a++)c=c+('\\"\u0008\u000c\n\r\t'.indexOf(d)>-1?r[d]:r[d]=d<" "? 71 | "\\u00"+t(2,d.charCodeAt(0).toString(16)):d);return c+'"'},x=function(b,c,a,d,j,f,o){var g=c[b],h,s,v,w,L,M,N,y,A;if(typeof g=="object"&&g){h=l.call(g);if(h=="[object Date]"&&!m.call(g,"toJSON"))if(g>-1/0&&g<1/0){if(P){v=K(g/864E5);for(h=K(v/365.2425)+1970-1;P(h+1,0)<=v;h++);for(s=K((v-P(h,0))/30.42);P(h,s+1)<=v;s++);v=1+v-P(h,s);w=(g%864E5+864E5)%864E5;L=K(w/36E5)%24;M=K(w/6E4)%60;N=K(w/1E3)%60;w=w%1E3}else{h=g.getUTCFullYear();s=g.getUTCMonth();v=g.getUTCDate();L=g.getUTCHours();M=g.getUTCMinutes(); 72 | N=g.getUTCSeconds();w=g.getUTCMilliseconds()}g=(h<=0||h>=1E4?(h<0?"-":"+")+t(6,h<0?-h:h):t(4,h))+"-"+t(2,s+1)+"-"+t(2,v)+"T"+t(2,L)+":"+t(2,M)+":"+t(2,N)+"."+t(3,w)+"Z"}else g=k;else if(typeof g.toJSON=="function"&&(h!="[object Number]"&&h!="[object String]"&&h!="[object Array]"||m.call(g,"toJSON")))g=g.toJSON(b)}a&&(g=a.call(c,b,g));if(g===k)return"null";h=l.call(g);if(h=="[object Boolean]")return""+g;if(h=="[object Number]")return g>-1/0&&g<1/0?""+g:"null";if(h=="[object String]")return u(g);if(typeof g== 73 | "object"){for(b=o.length;b--;)if(o[b]===g)throw TypeError();o.push(g);y=[];c=f;f=f+j;if(h=="[object Array]"){s=0;for(b=g.length;s0){d="";for(a>10&&(a=10);d.length-1)H++;else{if("{}[]:,".indexOf(a)>-1){H++;return a}if(a=='"'){d="@";for(H++;H-1){d=d+B[a];H++}else if(a=="u"){j=++H;for(f=H+4;H="0"&&a<="9"||a>="a"&&a<="f"||a>="A"&&a<="F"||C()}d=d+z("0x"+b.slice(j,H))}else C()}else{if(a=='"')break; 76 | d=d+a;H++}}if(b.charAt(H)=='"'){H++;return d}}else{j=H;if(a=="-"){o=i;a=b.charAt(++H)}if(a>="0"&&a<="9"){for(a=="0"&&(a=b.charAt(H+1),a>="0"&&a<="9")&&C();H="0"&&a<="9");H++);if(b.charAt(H)=="."){for(f=++H;f="0"&&a<="9");f++);f==H&&C();H=f}a=b.charAt(H);if(a=="e"||a=="E"){a=b.charAt(++H);(a=="+"||a=="-")&&H++;for(f=H;f="0"&&a<="9");f++);f==H&&C();H=f}return+b.slice(j,H)}o&&C();if(b.slice(H,H+4)=="true"){H=H+4;return i}if(b.slice(H,H+5)== 77 | "false"){H=H+5;return false}if(b.slice(H,H+4)=="null"){H=H+4;return k}}C()}}return"$"},E=function(b){var c,a;b=="$"&&C();if(typeof b=="string"){if(b.charAt(0)=="@")return b.slice(1);if(b=="["){for(c=[];;a||(a=i)){b=D();if(b=="]")break;if(a)if(b==","){b=D();b=="]"&&C()}else C();b==","&&C();c.push(E(b))}return c}if(b=="{"){for(c={};;a||(a=i)){b=D();if(b=="}")break;if(a)if(b==","){b=D();b=="}"&&C()}else C();(b==","||typeof b!="string"||b.charAt(0)!="@"||D()!=":")&&C();c[b.slice(1)]=E(D())}return c}C()}return b}, 78 | G=function(b,c,a){a=F(b,c,a);a===e?delete b[c]:b[c]=a},F=function(b,c,a){var d=b[c],j;if(typeof d=="object"&&d)if(l.call(d)=="[object Array]")for(j=d.length;j--;)G(d,j,a);else n(d,function(b){G(d,b,a)});return a.call(b,c,d)},q.parse=function(b,c){var a,d;H=0;I=b;a=E(D());D()!="$"&&C();H=I=k;return c&&l.call(c)=="[object Function]"?F((d={},d[""]=a,d),"",c):a})}p&&define(function(){return q}); 79 | }()); 80 | })(); 81 | -------------------------------------------------------------------------------- /doc/docs.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Signika'; 3 | font-style: normal; 4 | font-weight: 700; 5 | src: local('Signika-Bold'), url(http://themes.googleusercontent.com/static/fonts/signika/v3/7M5kxD4eGxuhgFaIk95pBRsxEYwM7FgeyaSgU71cLG0.woff) format('woff'); 6 | } 7 | 8 | @font-face { 9 | font-family: 'Ubuntu Mono'; 10 | font-style: normal; 11 | font-weight: 500; 12 | src: local('Ubuntu Mono'), local('UbuntuMono-Regular'), url('http://themes.googleusercontent.com/static/fonts/ubuntumono/v3/ViZhet7Ak-LRXZMXzuAfkYbN6UDyHWBl620a-IRfuBk.woff') format('woff'); 13 | } 14 | 15 | body { 16 | font-family: "Helvetica Neue", "Lucida Grande", "Lucida Sans Unicode", Helvetica, Arial, sans-serif; 17 | color: #222; 18 | font-size: 11pt; 19 | max-width: 700px; 20 | margin: 0 0 0 60px; 21 | padding: 60px 0 4em; 22 | } 23 | 24 | #top { 25 | position: fixed; 26 | top: 0; 27 | background: white; 28 | padding-top: 15px; 29 | left: 20px; right: 20px; 30 | z-index: 5; 31 | } 32 | 33 | #head { 34 | border-radius: 5px; 35 | background: #1c5b64; 36 | box-shadow: 0px 3px 3px rgba(0, 0, 0, .5); 37 | position: relative; 38 | font-family: "Signika", sans-serif; 39 | font-weight: 700; 40 | } 41 | 42 | #head > a { 43 | border-right: 1px solid #276c81; 44 | display: inline-block; 45 | height: 25px; 46 | padding: 5px 20px; 47 | color: white; 48 | text-decoration: none; 49 | line-height: 25px; 50 | font-size: 17px; 51 | } 52 | 53 | #head > a.title { 54 | font-size: 20px; 55 | line-height: 24px; 56 | padding-left: 80px; 57 | background: url(logo.png); 58 | background-position: 40px center; 59 | background-size: auto 80%; 60 | background-repeat: no-repeat; 61 | border-bottom-left-radius: 5px; 62 | border-top-left-radius: 5px; 63 | } 64 | 65 | #head > a:hover { 66 | background-color: #276c81 !important; 67 | } 68 | 69 | .title span { 70 | font-size: 13px; 71 | } 72 | 73 | code, pre { font-family: "Ubuntu Mono", monospace; } 74 | code { color: #124f55; font-size: 110%; } 75 | pre { 76 | border-left: 3px solid #def; 77 | padding: 2px 0 2px 10px; 78 | } 79 | 80 | h1, h2, h3, h4 { 81 | font-family: "Signika", sans-serif; 82 | font-weight: 700; 83 | color: #124f55; 84 | } 85 | h1 { font-size: 20pt; } 86 | h2 { font-size: 17pt; } 87 | h3 { font-size: 13pt; } 88 | h4 { font-size: 11pt; } 89 | 90 | a, a:visited, a code { color: #df4c11; text-decoration: none; } 91 | a:hover { text-decoration: underline; } 92 | 93 | ul, ol { 94 | margin: 0; 95 | padding: 0; 96 | } 97 | li { 98 | margin-left: 1.5em; 99 | padding: 0; 100 | } 101 | 102 | li p { margin: 0; } 103 | dd p:first-child { margin-top: 1px; } 104 | dd { margin-left: 20px; } 105 | dt { text-indent: -1em; padding-left: 1em; } 106 | 107 | a[id] { 108 | position: relative; 109 | display: inline-block; 110 | vertical-align: top; 111 | top: -60px; 112 | } 113 | 114 | .release-note { 115 | margin-left: 1em; 116 | } 117 | 118 | /* CodeMirror default theme for highlighting */ 119 | 120 | .cm-s-default .cm-keyword {color: #708;} 121 | .cm-s-default .cm-atom {color: #219;} 122 | .cm-s-default .cm-number {color: #164;} 123 | .cm-s-default .cm-def {color: #00f;} 124 | .cm-s-default .cm-variable {color: black;} 125 | .cm-s-default .cm-variable-2 {color: #05a;} 126 | .cm-s-default .cm-variable-3 {color: #085;} 127 | .cm-s-default .cm-property {color: black;} 128 | .cm-s-default .cm-operator {color: black;} 129 | .cm-s-default .cm-comment {color: #a50;} 130 | .cm-s-default .cm-string {color: #a11;} 131 | .cm-s-default .cm-string-2 {color: #f50;} 132 | .cm-s-default .cm-meta {color: #555;} 133 | .cm-s-default .cm-error {color: #f00;} 134 | .cm-s-default .cm-qualifier {color: #555;} 135 | .cm-s-default .cm-builtin {color: #30a;} 136 | .cm-s-default .cm-bracket {color: #997;} 137 | .cm-s-default .cm-tag {color: #170;} 138 | .cm-s-default .cm-attribute {color: #00c;} 139 | .cm-s-default .cm-header {color: blue;} 140 | .cm-s-default .cm-quote {color: #090;} 141 | .cm-s-default .cm-hr {color: #999;} 142 | .cm-s-default .cm-link {color: #00c;} 143 | -------------------------------------------------------------------------------- /doc/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ternjs/tern/33316af5be6cee834114c6dd4628f7eec1da4138/doc/logo.png -------------------------------------------------------------------------------- /emacs/tern-auto-complete.el: -------------------------------------------------------------------------------- 1 | ;;; tern-auto-complete.el --- Tern Completion by auto-complete.el -*- lexical-binding: t -*- 2 | 3 | ;; Author: 4 | ;; Version: 0.0.1 5 | ;; Package-Requires: ((tern "0.0.1") (auto-complete "1.4") (cl-lib "0.5") (emacs "24")) 6 | 7 | ;;; Commentary: 8 | 9 | ;; Display completion items with its type and document. 10 | 11 | ;; If `tern-ac-on-dot' is non-nil (default), typing '.(dot)' invokes auto-complete with tern. 12 | ;; Calling the command `tern-ac-complete', you can invoke auto-complete manually. 13 | ;; This program does not provide an ac-source for arbitrary timing yet. 14 | 15 | ;;; Installation: 16 | 17 | ;; Add following lines below the tern setup code. 18 | 19 | ;; (eval-after-load 'tern 20 | ;; '(progn 21 | ;; (require 'tern-auto-complete) 22 | ;; (tern-ac-setup))) 23 | 24 | ;;; Code: 25 | 26 | (require 'cl-lib) 27 | (require 'tern) 28 | (require 'auto-complete) 29 | 30 | 31 | 32 | ;;; Completion 33 | 34 | (defcustom tern-ac-on-dot t 35 | "[AC] If t, tern enable completion by auto-completion." 36 | :type 'boolean 37 | :group 'auto-complete) 38 | 39 | (defcustom tern-ac-sync t 40 | "[AC] If t, auto-complete will wait for tern canditates before starting. 41 | This enables tern canditates to integrate automatically in auto-complete without 42 | the need for a separate keybinding. 43 | 44 | Remember to add ac-source-tern-completion to ac-sources." 45 | :type 'boolean 46 | :group 'auto-complete) 47 | 48 | 49 | (defvar tern-ac-complete-reply nil "[internal] tern-ac-complete-reply.") 50 | 51 | (defvar tern-ac-complete-request-point 0 52 | "[internal] The point where `tern-ac-complete-request' is called.") 53 | 54 | (defun tern-ac-complete-request (cc) 55 | (setq tern-last-point-pos (point)) 56 | (setq tern-ac-complete-reply nil) 57 | (setq tern-ac-complete-request-point (point)) 58 | (tern-run-query 59 | (lambda (data) 60 | (tern-ac-complete-response data) 61 | (funcall cc)) 62 | `((type . "completions") (types . t) (docs . t) (caseInsensitive . t)) 63 | (point))) 64 | 65 | (defun tern-ac-complete-response (data) 66 | (let ((cs (cl-loop for elt across (cdr (assq 'completions data)) collect elt)) 67 | (start (+ 1 (cdr (assq 'start data)))) 68 | (end (+ 1 (cdr (assq 'end data))))) 69 | (setq tern-last-completions (list (buffer-substring-no-properties start end) start end cs)) 70 | (setq tern-ac-complete-reply cs))) 71 | 72 | (defun tern-ac-complete () 73 | "Complete code at point by tern." 74 | (interactive) 75 | (tern-ac-complete-request 76 | (lambda () 77 | (let ((ac-sources (cons 'ac-source-tern-completion ac-sources))) 78 | (ac-start))))) 79 | 80 | (defun tern-ac-dot-complete () 81 | "Insert dot and complete code at point by tern." 82 | (interactive) 83 | (insert ".") 84 | (unless (nth 4 (syntax-ppss)) 85 | (tern-ac-complete))) 86 | 87 | (defvar tern-ac-completion-truncate-length 22 88 | "[AC] truncation length for type summary.") 89 | 90 | (defun tern-ac-completion-matches () 91 | (mapcar 92 | (lambda (item) 93 | (let ((doc (cdr (assq 'doc item))) 94 | (type (cdr (assq 'type item))) 95 | (name (cdr (assq 'name item)))) 96 | (popup-make-item 97 | name 98 | :symbol (if (null type) "?" (if (string-match "fn" type) "f" "v")) 99 | :summary (truncate-string-to-width 100 | (or type "?") tern-ac-completion-truncate-length 0 nil "...") 101 | :document (concat type "\n\n" doc)))) 102 | tern-ac-complete-reply)) 103 | 104 | (defun tern-ac-completion-prefix () 105 | (or (ac-prefix-default) 106 | (when (= tern-ac-complete-request-point (point)) 107 | tern-ac-complete-request-point))) 108 | 109 | ;; (makunbound 'ac-source-tern-completion) 110 | (ac-define-source tern-completion 111 | '((candidates . tern-ac-completion-matches) 112 | (prefix . tern-ac-completion-prefix) 113 | (requires . -1))) 114 | 115 | ;;;###autoload 116 | (defun tern-ac-setup () 117 | "Setup auto-complete for tern-mode." 118 | (interactive) 119 | (if tern-ac-on-dot 120 | (define-key tern-mode-keymap "." 'tern-ac-dot-complete) 121 | (define-key tern-mode-keymap "." nil))) 122 | 123 | (defvar tern-ac-js-major-modes '(rjsx-mode js2-mode js2-jsx-mode js-mode js-jsx-mode javascript-mode)) 124 | 125 | (defadvice auto-complete (around add-tern-ac-candidates first activate) 126 | "Load tern-js canditates before ac-start." 127 | (if (and tern-ac-sync 128 | (memq major-mode tern-ac-js-major-modes) 129 | (not (or (ac-menu-live-p) (ac-inline-live-p)))) 130 | (tern-ac-complete-request 131 | 'auto-complete-1) 132 | ad-do-it)) 133 | 134 | 135 | (provide 'tern-auto-complete) 136 | ;;; tern-auto-complete.el ends here 137 | -------------------------------------------------------------------------------- /lib/bootstrap.js: -------------------------------------------------------------------------------- 1 | var tern = require("./tern"); 2 | var fs = require("fs"), path = require("path"), url = require("url"); 3 | var glob = require("glob"), minimatch = require("minimatch"); 4 | var resolveFrom = require("resolve-from"); 5 | 6 | var projectFileName = ".tern-project", portFileName = ".tern-port"; 7 | 8 | function findProjectDir() { 9 | var dir = process.cwd(); 10 | for (;;) { 11 | try { 12 | if (fs.statSync(path.resolve(dir, projectFileName)).isFile()) return dir; 13 | } catch(e) {} 14 | var shorter = path.dirname(dir); 15 | if (shorter == dir) return null; 16 | dir = shorter; 17 | } 18 | } 19 | 20 | var defaultConfig = { 21 | libs: [], 22 | loadEagerly: false, 23 | plugins: {doc_comment: true}, 24 | ecmaScript: true, 25 | ecmaVersion: 9, 26 | dependencyBudget: tern.defaultOptions.dependencyBudget 27 | }; 28 | var homeDir = process.env.HOME || process.env.USERPROFILE; 29 | if (homeDir && fs.existsSync(path.resolve(homeDir, ".tern-config"))) 30 | defaultConfig = readProjectFile(path.resolve(homeDir, ".tern-config")); 31 | 32 | function readJSON(fileName) { 33 | var file = fs.readFileSync(fileName, "utf8"); 34 | try { 35 | return JSON.parse(file); 36 | } catch (e) { 37 | console.error("Bad JSON in " + fileName + ": " + e.message); 38 | process.exit(1); 39 | } 40 | } 41 | 42 | function readProjectFile(fileName) { 43 | var data = readJSON(fileName), name; 44 | for (var option in defaultConfig) { 45 | if (!data.hasOwnProperty(option)) 46 | data[option] = defaultConfig[option]; 47 | else if (option == "plugins") 48 | for (name in defaultConfig.plugins) 49 | if (!Object.prototype.hasOwnProperty.call(data.plugins, name)) 50 | data.plugins[name] = defaultConfig.plugins[name]; 51 | } 52 | return data; 53 | } 54 | 55 | function findFile(file, projectDir, fallbackDir, options) { 56 | var local = path.resolve(projectDir, file); 57 | if (!options.disableLoadingLocal && fs.existsSync(local)) return local; 58 | var shared = path.resolve(fallbackDir, file); 59 | if (fs.existsSync(shared)) return shared; 60 | } 61 | 62 | var distDir = path.resolve(__dirname, ".."); 63 | 64 | function findDefs(projectDir, config, options) { 65 | var defs = [], src = config.libs.slice(); 66 | if (config.ecmaScript && src.indexOf("ecmascript") == -1) 67 | src.unshift("ecmascript"); 68 | for (var i = 0; i < src.length; ++i) { 69 | var file = src[i]; 70 | if (!/\.json$/.test(file)) file = file + ".json"; 71 | var found = findFile(file, projectDir, path.resolve(distDir, "defs"), options) 72 | || resolveFrom(projectDir, "tern-" + src[i]); 73 | if (!found) { 74 | try { 75 | found = require.resolve("tern-" + src[i]); 76 | } catch (e) { 77 | process.stderr.write("Failed to find library " + src[i] + ".\n"); 78 | continue; 79 | } 80 | } 81 | if (found) defs.push(readJSON(found)); 82 | } 83 | return defs; 84 | } 85 | 86 | function loadPlugins(projectDir, config, options) { 87 | var plugins = config.plugins, options = {}; 88 | for (var plugin in plugins) { 89 | var val = plugins[plugin]; 90 | if (!val) continue; 91 | var found = findFile(plugin + ".js", projectDir, path.resolve(distDir, "plugin"), options) 92 | || resolveFrom(projectDir, "tern-" + plugin); 93 | if (!found) { 94 | try { 95 | found = require.resolve("tern-" + plugin); 96 | } catch (e) { 97 | process.stderr.write("Failed to find plugin " + plugin + ".\n"); 98 | continue; 99 | } 100 | } 101 | var mod = require(found); 102 | if (mod.hasOwnProperty("initialize")) mod.initialize(distDir); 103 | options[path.basename(plugin)] = val; 104 | } 105 | 106 | return options; 107 | } 108 | 109 | function startServer(dir, config, options) { 110 | var defs = findDefs(dir, config, options); 111 | var plugins = loadPlugins(dir, config, options); 112 | var ternConfig = { 113 | getFile: function(name, c) { 114 | if (config.dontLoad && config.dontLoad.some(function(pat) {return minimatch(name, pat)})) 115 | c(null, ""); 116 | else 117 | fs.readFile(path.resolve(dir, name), "utf8", c); 118 | }, 119 | normalizeFilename: function(name) { 120 | var pt = path.resolve(dir, name); 121 | try { pt = fs.realPathSync(path.resolve(dir, name), true) } 122 | catch(e) {} 123 | return path.relative(dir, pt); 124 | }, 125 | async: true, 126 | defs: defs, 127 | plugins: plugins, 128 | projectDir: dir, 129 | ecmaVersion: config.ecmaVersion, 130 | dependencyBudget: config.dependencyBudget, 131 | }; 132 | if (options.tern) { 133 | for (var option in options.tern) { 134 | if (options.tern.hasOwnProperty(option)) { 135 | ternConfig[option] = options.tern[option]; 136 | } 137 | } 138 | } 139 | var server = new tern.Server(ternConfig); 140 | 141 | if (config.loadEagerly) config.loadEagerly.forEach(function(pat) { 142 | glob.sync(pat, { cwd: dir }).forEach(function(file) { 143 | server.addFile(file); 144 | }); 145 | }); 146 | server.flush(function(){}); 147 | return server; 148 | } 149 | 150 | module.exports = function bootstrapServer(options) { 151 | var projectDir = options.projectDir; 152 | if (!projectDir) { 153 | projectDir = findProjectDir(); 154 | } 155 | 156 | if (projectDir) { 157 | var config = readProjectFile(path.resolve(projectDir, projectFileName)); 158 | } else { 159 | projectDir = process.cwd(); 160 | var config = defaultConfig; 161 | } 162 | return startServer(projectDir, config, options); 163 | } 164 | -------------------------------------------------------------------------------- /lib/comment.js: -------------------------------------------------------------------------------- 1 | (function(mod) { 2 | if (typeof exports == "object" && typeof module == "object") // CommonJS 3 | return mod(exports); 4 | if (typeof define == "function" && define.amd) // AMD 5 | return define(["exports"], mod); 6 | mod(tern.comment || (tern.comment = {})); 7 | })(function(exports) { 8 | function isSpace(ch) { 9 | return (ch < 14 && ch > 8) || ch === 32 || ch === 160; 10 | } 11 | 12 | function onOwnLine(text, pos) { 13 | for (; pos > 0; --pos) { 14 | var ch = text.charCodeAt(pos - 1); 15 | if (ch == 10) break; 16 | if (!isSpace(ch)) return false; 17 | } 18 | return true; 19 | } 20 | 21 | // Gather comments directly before a function 22 | exports.commentsBefore = function(text, pos) { 23 | var found = null, emptyLines = 0, topIsLineComment; 24 | out: while (pos > 0) { 25 | var prev = text.charCodeAt(pos - 1); 26 | if (prev == 10) { 27 | for (var scan = --pos, sawNonWS = false; scan > 0; --scan) { 28 | prev = text.charCodeAt(scan - 1); 29 | if (prev == 47 && text.charCodeAt(scan - 2) == 47) { 30 | if (!onOwnLine(text, scan - 2)) break out; 31 | var content = text.slice(scan, pos); 32 | if (!emptyLines && topIsLineComment) found[0] = content + "\n" + found[0]; 33 | else (found || (found = [])).unshift(content); 34 | topIsLineComment = true; 35 | emptyLines = 0; 36 | pos = scan - 2; 37 | break; 38 | } else if (prev == 10) { 39 | if (!sawNonWS && ++emptyLines > 1) break out; 40 | break; 41 | } else if (!sawNonWS && !isSpace(prev)) { 42 | sawNonWS = true; 43 | } 44 | } 45 | } else if (prev == 47 && text.charCodeAt(pos - 2) == 42) { 46 | for (var scan = pos - 2; scan > 1; --scan) { 47 | if (text.charCodeAt(scan - 1) == 42 && text.charCodeAt(scan - 2) == 47) { 48 | if (!onOwnLine(text, scan - 2)) break out; 49 | (found || (found = [])).unshift(text.slice(scan, pos - 2)); 50 | topIsLineComment = false; 51 | emptyLines = 0; 52 | break; 53 | } 54 | } 55 | pos = scan - 2; 56 | } else if (isSpace(prev)) { 57 | --pos; 58 | } else { 59 | break; 60 | } 61 | } 62 | return found; 63 | }; 64 | 65 | exports.commentAfter = function(text, pos) { 66 | while (pos < text.length) { 67 | var next = text.charCodeAt(pos); 68 | if (next == 47) { 69 | var after = text.charCodeAt(pos + 1), end; 70 | if (after == 47) // line comment 71 | end = text.indexOf("\n", pos + 2); 72 | else if (after == 42) // block comment 73 | end = text.indexOf("*/", pos + 2); 74 | else 75 | return; 76 | return text.slice(pos + 2, end < 0 ? text.length : end); 77 | } else if (isSpace(next)) { 78 | ++pos; 79 | } 80 | } 81 | }; 82 | 83 | exports.ensureCommentsBefore = function(text, node) { 84 | if (node.hasOwnProperty("commentsBefore")) return node.commentsBefore; 85 | return node.commentsBefore = exports.commentsBefore(text, node.start); 86 | }; 87 | }); 88 | -------------------------------------------------------------------------------- /lib/signal.js: -------------------------------------------------------------------------------- 1 | (function(root, mod) { 2 | if (typeof exports == "object" && typeof module == "object") // CommonJS 3 | return mod(exports); 4 | if (typeof define == "function" && define.amd) // AMD 5 | return define(["exports"], mod); 6 | mod((root.tern || (root.tern = {})).signal = {}); // Plain browser env 7 | })(this, function(exports) { 8 | 9 | function on(type, f) { 10 | var handlers = this._handlers || (this._handlers = Object.create(null)); 11 | (handlers[type] || (handlers[type] = [])).push(f); 12 | } 13 | 14 | function off(type, f) { 15 | var arr = this._handlers && this._handlers[type]; 16 | if (arr) for (var i = 0; i < arr.length; ++i) 17 | if (arr[i] == f) { arr.splice(i, 1); break; } 18 | } 19 | 20 | var noHandlers = []; 21 | function getHandlers(emitter, type) { 22 | var arr = emitter._handlers && emitter._handlers[type]; 23 | return arr && arr.length ? arr.slice() : noHandlers; 24 | } 25 | 26 | function signal(type, a1, a2, a3, a4) { 27 | var arr = getHandlers(this, type); 28 | for (var i = 0; i < arr.length; ++i) arr[i].call(this, a1, a2, a3, a4); 29 | } 30 | 31 | function signalReturnFirst(type, a1, a2, a3, a4) { 32 | var arr = getHandlers(this, type); 33 | for (var i = 0; i < arr.length; ++i) { 34 | var result = arr[i].call(this, a1, a2, a3, a4); 35 | if (result) return result; 36 | } 37 | } 38 | 39 | function hasHandler(type) { 40 | var arr = this._handlers && this._handlers[type]; 41 | return arr && arr.length > 0 && arr; 42 | } 43 | 44 | exports.mixin = function(obj) { 45 | obj.on = on; obj.off = off; 46 | obj.signal = signal; 47 | obj.signalReturnFirst = signalReturnFirst; 48 | obj.hasHandler = hasHandler; 49 | return obj; 50 | }; 51 | }); 52 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tern", 3 | "license": "MIT", 4 | "version": "0.24.3", 5 | "author": "Marijn Haverbeke ", 6 | "description": "A JavaScript code analyzer for deep, cross-editor language support", 7 | "main": "lib/tern.js", 8 | "bin": "./bin/tern", 9 | "repository": { 10 | "type": "git", 11 | "url": "git://github.com/ternjs/tern.git" 12 | }, 13 | "scripts": { 14 | "test": "node ./bin/test" 15 | }, 16 | "dependencies": { 17 | "acorn": "^6.0.0", 18 | "acorn-walk": "^6.0.0", 19 | "acorn-loose": "^6.0.0", 20 | "enhanced-resolve": "^2.2.2", 21 | "glob": "^7.1.1", 22 | "minimatch": "^3.0.3", 23 | "resolve-from": "2.0.0" 24 | }, 25 | "devDependencies": { 26 | "codemirror": "git://github.com/codemirror/CodeMirror.git" 27 | }, 28 | "blint": { 29 | "allowedGlobals": [ 30 | "tern", 31 | "acorn", 32 | "define", 33 | "clearTimeout", 34 | "setTimeout", 35 | "__dirname", 36 | "global", 37 | "process" 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /plugin/commonjs.js: -------------------------------------------------------------------------------- 1 | (function(mod) { 2 | if (typeof exports == "object" && typeof module == "object") // CommonJS 3 | return mod(require("../lib/infer"), require("../lib/tern"), require("./modules")); 4 | if (typeof define == "function" && define.amd) // AMD 5 | return define(["../lib/infer", "../lib/tern", "./modules"], mod); 6 | mod(tern, tern); 7 | })(function(infer, tern) { 8 | "use strict"; 9 | 10 | var WG_DEFAULT_EXPORT = 95; 11 | 12 | function initScope(scope) { 13 | var defs = infer.cx().definitions.commonjs; 14 | defs.require.propagate(scope.defProp("require")); 15 | var module = new infer.Obj(defs.Module.getProp("prototype").getType()); 16 | module.propagate(scope.defProp("module")); 17 | var exports = new infer.Obj(true); 18 | module.origin = exports.origin = scope.origin; 19 | module.originNode = exports.originNode = scope.originNode; 20 | exports.propagate(scope.defProp("exports")); 21 | var moduleExports = scope.exports = module.defProp("exports"); 22 | exports.propagate(moduleExports, WG_DEFAULT_EXPORT); 23 | } 24 | 25 | infer.registerFunction("require", function(_self, _args, argNodes) { 26 | if (!argNodes || !argNodes.length || argNodes[0].type != "Literal" || typeof argNodes[0].value != "string") 27 | return infer.ANull; 28 | var cx = infer.cx(), server = cx.parent; 29 | var currentFile = argNodes[0].sourceFile.name; 30 | 31 | var name = argNodes[0].value; 32 | var resolved = server.mod.modules.resolveModule(name, currentFile); 33 | return resolved; 34 | }); 35 | 36 | function isStaticRequire(node) { 37 | if (node.type != "CallExpression" || node.callee.type != "Identifier" || node.callee.name != "require") return; 38 | var arg = node.arguments[0]; 39 | if (arg && arg.type == "Literal" && typeof arg.value == "string") return arg.value; 40 | } 41 | 42 | function isModuleName(node) { 43 | if (node.type != "Literal" || typeof node.value != "string") return; 44 | 45 | var call = infer.findExpressionAround(node.sourceFile.ast, null, node.end, null, 46 | function(_, n) { return isStaticRequire(n) != null }); 47 | if (call && call.node.arguments[0] == node) return node.value; 48 | } 49 | 50 | function isImport(node) { 51 | if (node.type != "Identifier") return; 52 | var decl = infer.findExpressionAround(node.sourceFile.ast, null, node.end, null, "VariableDeclarator"), name; 53 | if (!decl || decl.node.id != node) return; 54 | var init = decl.node.init; 55 | if (init && (name = isStaticRequire(init)) != null) 56 | return {name: name, prop: null}; 57 | if (init && init.type == "MemberExpression" && !init.computed && (name = isStaticRequire(init.object)) != null) 58 | return {name: name, prop: init.property.name}; 59 | } 60 | 61 | function hasProps(obj) { 62 | if (obj) for (var _prop in obj) return true; 63 | } 64 | 65 | tern.registerPlugin("commonjs", function(server) { 66 | server.loadPlugin("modules"); 67 | server.mod.modules.on("wrapScope", initScope); 68 | server.mod.modules.on("getExports", function(file, mod) { 69 | var exports = file.scope.exports; 70 | if (exports.types.length > 1 || hasProps(exports.getObjType())) 71 | exports.propagate(mod); 72 | }); 73 | 74 | server.mod.modules.modNameTests.push(isModuleName); 75 | server.mod.modules.importTests.push(isImport); 76 | server.mod.modules.completableTypes.Identifier = true; 77 | server.mod.modules.completableTypes.Literal = true; 78 | 79 | server.addDefs(defs); 80 | }); 81 | 82 | var defs = { 83 | "!name": "commonjs", 84 | "!define": { 85 | require: { 86 | "!type": "fn(id: string) -> !custom:require", 87 | resolve: { 88 | "!type": "fn() -> string", 89 | "!url": "https://nodejs.org/api/globals.html#globals_require_resolve", 90 | "!doc": "Use the internal require() machinery to look up the location of a module, but rather than loading the module, just return the resolved filename." 91 | }, 92 | cache: { 93 | "!url": "https://nodejs.org/api/globals.html#globals_require_cache", 94 | "!doc": "Modules are cached in this object when they are required. By deleting a key value from this object, the next require will reload the module." 95 | }, 96 | extensions: { 97 | "!url": "https://nodejs.org/api/globals.html#globals_require_extensions", 98 | "!doc": "Instruct require on how to handle certain file extensions." 99 | }, 100 | "!url": "https://nodejs.org/api/globals.html#globals_require", 101 | "!doc": "To require modules." 102 | }, 103 | Module: { 104 | "!type": "fn()", 105 | "!url": "https://nodejs.org/api/modules.html", 106 | "!doc": "Node has a simple module loading system. In Node, files and modules are in one-to-one correspondence.", 107 | prototype: { 108 | exports: { 109 | "!type": "?", 110 | "!url": "https://nodejs.org/api/modules.html#modules_module_exports", 111 | "!doc": "The exports object is created by the Module system. Sometimes this is not acceptable, many want their module to be an instance of some class. To do this assign the desired export object to module.exports. For example suppose we were making a module called a.js" 112 | }, 113 | require: { 114 | "!type": "require", 115 | "!url": "https://nodejs.org/api/modules.html#modules_module_require_id", 116 | "!doc": "The module.require method provides a way to load a module as if require() was called from the original module." 117 | }, 118 | id: { 119 | "!type": "string", 120 | "!url": "https://nodejs.org/api/modules.html#modules_module_id", 121 | "!doc": "The identifier for the module. Typically this is the fully resolved filename." 122 | }, 123 | filename: { 124 | "!type": "string", 125 | "!url": "https://nodejs.org/api/modules.html#modules_module_filename", 126 | "!doc": "The fully resolved filename to the module." 127 | }, 128 | loaded: { 129 | "!type": "bool", 130 | "!url": "https://nodejs.org/api/modules.html#modules_module_loaded", 131 | "!doc": "Whether or not the module is done loading, or is in the process of loading." 132 | }, 133 | parent: { 134 | "!type": "+Module", 135 | "!url": "https://nodejs.org/api/modules.html#modules_module_parent", 136 | "!doc": "The module that required this one." 137 | }, 138 | children: { 139 | "!type": "[+Module]", 140 | "!url": "https://nodejs.org/api/modules.html#modules_module_children", 141 | "!doc": "The module objects required by this one." 142 | } 143 | } 144 | }, 145 | module: {} 146 | }, 147 | module: { 148 | "!type": "+Module", 149 | "!url": "https://nodejs.org/api/globals.html#globals_module", 150 | "!doc": "A reference to the current module. In particular module.exports is the same as the exports object. module isn't actually a global but rather local to each module." 151 | } 152 | }; 153 | }); 154 | -------------------------------------------------------------------------------- /plugin/complete_strings.js: -------------------------------------------------------------------------------- 1 | // When enabled, this plugin will gather (short) strings in your code, 2 | // and completing when inside a string will try to complete to 3 | // previously seen strings. Takes a single option, maxLength, which 4 | // controls the maximum length of string values to gather, and 5 | // defaults to 15. 6 | 7 | (function(mod) { 8 | if (typeof exports == "object" && typeof module == "object") // CommonJS 9 | return mod(require("../lib/infer"), require("../lib/tern"), require("acorn-walk")); 10 | if (typeof define == "function" && define.amd) // AMD 11 | return define(["../lib/infer", "../lib/tern", "acorn-walk/dist/walk"], mod); 12 | mod(tern, tern, acorn.walk); 13 | })(function(infer, tern, walk) { 14 | "use strict"; 15 | 16 | tern.registerPlugin("complete_strings", function(server, options) { 17 | server.mod.completeStrings = { maxLen: options && options.maxLength || 15, 18 | seen: Object.create(null) }; 19 | server.on("reset", function() { 20 | server.mod.completeStrings.seen = Object.create(null); 21 | }); 22 | server.on("postParse", postParse); 23 | server.on("completion", complete); 24 | }); 25 | 26 | function postParse(ast) { 27 | var data = infer.cx().parent.mod.completeStrings; 28 | walk.simple(ast, { 29 | Literal: function(node) { 30 | if (typeof node.value == "string" && node.value && node.value.length < data.maxLen) 31 | data.seen[node.value] = ast.sourceFile.name; 32 | } 33 | }); 34 | } 35 | 36 | function complete(file, query) { 37 | var pos = tern.resolvePos(file, query.end); 38 | var lit = infer.findExpressionAround(file.ast, null, pos, file.scope, "Literal"); 39 | if (!lit || typeof lit.node.value != "string") return; 40 | var before = lit.node.value.slice(0, pos - lit.node.start - 1); 41 | var matches = [], seen = infer.cx().parent.mod.completeStrings.seen; 42 | for (var str in seen) if (str.length > before.length && str.indexOf(before) == 0) { 43 | if (query.types || query.docs || query.urls || query.origins) { 44 | var rec = {name: JSON.stringify(str), displayName: str}; 45 | matches.push(rec); 46 | if (query.types) rec.type = "string"; 47 | if (query.origins) rec.origin = seen[str]; 48 | } else { 49 | matches.push(JSON.stringify(str)); 50 | } 51 | } 52 | if (matches.length) return { 53 | start: tern.outputPos(query, file, lit.node.start), 54 | end: tern.outputPos(query, file, pos + (file.text.charAt(pos) == file.text.charAt(lit.node.start) ? 1 : 0)), 55 | isProperty: false, 56 | completions: matches 57 | }; 58 | } 59 | }); 60 | -------------------------------------------------------------------------------- /plugin/es_modules.js: -------------------------------------------------------------------------------- 1 | (function(mod) { 2 | if (typeof exports == "object" && typeof module == "object") // CommonJS 3 | return mod(require("../lib/infer"), require("../lib/tern"), require("acorn-walk"), require("./modules")); 4 | if (typeof define == "function" && define.amd) // AMD 5 | return define(["../lib/infer", "../lib/tern", "acorn-walk/dist/walk", "./modules"], mod); 6 | mod(tern, tern, acorn.walk); 7 | })(function(infer, tern, walk) { 8 | "use strict"; 9 | 10 | var WG_IMPORT_DEFAULT_FALLBACK = 80; 11 | 12 | function connectModule(file, out) { 13 | var modules = infer.cx().parent.mod.modules; 14 | var outObj = null; 15 | function exp(prop, type, originNode) { 16 | if (!outObj) { 17 | outObj = new infer.Obj(true); 18 | outObj.origin = file.name; 19 | outObj.originNode = file.ast; 20 | out.addType(outObj); 21 | } 22 | var propObj = outObj.defProp(prop, originNode); 23 | propObj.origin = file.name; 24 | type.propagate(propObj); 25 | } 26 | 27 | walk.simple(file.ast, { 28 | ImportDeclaration: function(node) { 29 | var input = modules.resolveModule(node.source.value, file.name); 30 | for (var i = 0; i < node.specifiers.length; i++) { 31 | var spec = node.specifiers[i]; 32 | var aval = file.scope.getProp(spec.local.name); 33 | if (spec.type == "ImportNamespaceSpecifier") { 34 | input.propagate(aval); 35 | } else if (spec.type == "ImportDefaultSpecifier") { 36 | input.getProp("default").propagate(aval); 37 | input.propagate(aval, WG_IMPORT_DEFAULT_FALLBACK); 38 | } else { 39 | input.getProp(spec.imported.name).propagate(aval); 40 | } 41 | } 42 | }, 43 | ExportAllDeclaration: function(node) { 44 | var input = modules.resolveModule(node.source.value, file.name); 45 | input.forAllProps(function(prop, val, local) { 46 | if (local) exp(prop, val, val.originNode); 47 | }); 48 | }, 49 | ExportDefaultDeclaration: function(node) { 50 | var decl = node.declaration.id || node.declaration; 51 | exp("default", infer.expressionType({node: decl, state: file.scope}), decl); 52 | }, 53 | ExportNamedDeclaration: function(node) { 54 | var decl = node.declaration; 55 | if (decl) { 56 | if (decl.type == "VariableDeclaration") { 57 | for (var i = 0; i < decl.declarations.length; ++i) { 58 | var cur = decl.declarations[i]; 59 | if (cur.id.type == "Identifier") 60 | exp(cur.id.name, file.scope.getProp(cur.id.name), cur.id); 61 | } 62 | } else if (decl.id){ 63 | exp(decl.id.name, file.scope.getProp(decl.id.name), decl.id); 64 | } 65 | } 66 | if (node.specifiers.length) { 67 | var src = node.source ? modules.resolveModule(node.source.value, file.name) : file.scope; 68 | for (var i = 0; i < node.specifiers.length; i++) { 69 | var spec = node.specifiers[i]; 70 | exp(spec.exported.name, src.getProp(spec.local.name), spec.local); 71 | } 72 | } 73 | } 74 | }); 75 | } 76 | 77 | function isModuleName(node) { 78 | if (node.type != "Literal" || typeof node.value != "string") return; 79 | 80 | var decl = infer.findExpressionAround(node.sourceFile.ast, null, node.end, null, function(_, node) { 81 | return node.type == "ImportDeclaration" || /Export(All|Named)Declaration/.test(node.type); 82 | }); 83 | if (!decl || decl.node.source != node) return; 84 | return node.value; 85 | } 86 | 87 | function isImport(node, pos) { 88 | if (node.type == "Identifier") { 89 | var imp = infer.findExpressionAround(node.sourceFile.ast, null, node.end, null, "ImportDeclaration"); 90 | if (!imp) return; 91 | var specs = imp.node.specifiers; 92 | for (var i = 0; i < specs.length; i++) { 93 | var spec = specs[i]; 94 | if (spec.local != node) continue; 95 | var result = {name: imp.node.source.value, prop: null}; 96 | if (spec.type == "ImportDefaultSpecifier") result.prop = "default"; 97 | else if (spec.type == "ImportSpecifier") result.prop = spec.imported.name; 98 | return result; 99 | } 100 | } else if (node.type == "ImportDeclaration" && 101 | /^import\s+\{\s*([\w$]+\s*,\s*)*$/.test(node.sourceFile.text.slice(node.start, pos))) { 102 | return {name: node.source.value, prop: ""}; 103 | } 104 | } 105 | 106 | tern.registerPlugin("es_modules", function(server) { 107 | server.loadPlugin("modules"); 108 | server.mod.modules.on("getExports", connectModule); 109 | server.mod.modules.modNameTests.push(isModuleName); 110 | server.mod.modules.importTests.push(isImport); 111 | server.mod.modules.completableTypes.Identifier = true; 112 | server.mod.modules.completableTypes.Literal = true; 113 | server.mod.modules.completableTypes.ImportDeclaration = true; 114 | }); 115 | }); 116 | -------------------------------------------------------------------------------- /plugin/node_resolve.js: -------------------------------------------------------------------------------- 1 | (function(mod) { 2 | if (typeof exports == "object" && typeof module == "object") // CommonJS 3 | return mod(require("../lib/infer"), require("../lib/tern"), require("./commonjs"), require); 4 | if (typeof define == "function" && define.amd) // AMD 5 | return define(["../lib/infer", "../lib/tern", "./commonjs"], mod); 6 | mod(tern, tern); 7 | })(function(infer, tern, _, require) { 8 | "use strict"; 9 | 10 | function resolve(name, parentFile) { 11 | var resolved = resolveToFile(name, parentFile); 12 | return resolved && infer.cx().parent.normalizeFilename(resolved); 13 | } 14 | 15 | var findDeclaredDeps = function () {}; 16 | 17 | var resolveToFile; 18 | if (require) (function() { 19 | var module_ = require("module"), path = require("path"), fs = require("fs"); 20 | 21 | resolveToFile = function(name, parentFile) { 22 | var projectDir = infer.cx().parent.projectDir; 23 | var fullParent = path.resolve(projectDir, parentFile); 24 | var parentDir = path.dirname(fullParent); 25 | if (/^\.\.?\//.test(name)) 26 | name = path.resolve(projectDir, parentDir, name); 27 | 28 | var parentModule = { 29 | id: fullParent, 30 | paths: module_._nodeModulePaths(parentDir).concat(module_.globalPaths) 31 | }; 32 | try { 33 | return module_._resolveFilename(name, parentModule); 34 | } catch(e) { 35 | return null; 36 | } 37 | }; 38 | 39 | function findPackageFile(dir) { 40 | for (;;) { 41 | try { 42 | return JSON.parse(fs.readFileSync(path.resolve(dir, "package.json"))); 43 | } catch(e) {} 44 | var shorter = path.dirname(dir); 45 | if (shorter == dir) return null; 46 | dir = shorter; 47 | } 48 | } 49 | 50 | findDeclaredDeps = function(path, knownModules) { 51 | var packageFile = findPackageFile(path); 52 | if (!packageFile) return null; 53 | 54 | function add(obj) { 55 | for (var name in obj) if (!(name in knownModules)) knownModules[name] = null; 56 | } 57 | add(packageFile.dependencies); 58 | add(packageFile.devDependencies); 59 | add(packageFile.peerDependencies); 60 | }; 61 | 62 | })(); else (function() { 63 | function resolvePath(base, path) { 64 | if (path[0] == "/") return path; 65 | var slash = base.lastIndexOf("/"), m; 66 | if (slash >= 0) path = base.slice(0, slash + 1) + path; 67 | while (m = /[^\/]*[^\/\.][^\/]*\/\.\.\//.exec(path)) 68 | path = path.slice(0, m.index) + path.slice(m.index + m[0].length); 69 | return path.replace(/(^|[^\.])\.\//g, "$1"); 70 | } 71 | 72 | resolveToFile = function(name, parentFile) { 73 | return /^\.\.?\//.test(name) ? resolvePath(parentFile, name) : name; 74 | }; 75 | })(); 76 | 77 | tern.registerPlugin("node_resolve", function(server) { 78 | server.loadPlugin("commonjs"); 79 | server.mod.modules.resolvers.push(resolve); 80 | findDeclaredDeps(server.projectDir, server.mod.modules.knownModules); 81 | }); 82 | }); 83 | -------------------------------------------------------------------------------- /plugin/webpack.js: -------------------------------------------------------------------------------- 1 | if (typeof exports != "object" || typeof module != "object") 2 | throw new Error("This plugin works only in a CommonJS environment"); 3 | 4 | var infer = require("../lib/infer"); 5 | var tern = require("../lib/tern"); 6 | require("./commonjs"); 7 | require("./es_modules"); 8 | 9 | function isArray(v) { 10 | return Object.prototype.toString.call(v) == "[object Array]"; 11 | } 12 | 13 | var fs = require('fs'); 14 | var path = require("path"); 15 | var ResolverFactory = require("enhanced-resolve").ResolverFactory; 16 | var SyncNodeJsInputFileSystem = require("enhanced-resolve/lib/SyncNodeJsInputFileSystem"); 17 | 18 | function getResolver(modules, configPath) { 19 | var config = { 20 | unsafeCache: true, 21 | modules: modules || ["node_modules"], 22 | extensions: [".js", ".jsx", ".json"], 23 | aliasFields: ["browser"], 24 | mainFields: ["browser", "web", "browserify", "main"], 25 | fileSystem: new SyncNodeJsInputFileSystem() 26 | }; 27 | var webpackConfig = (configPath && fs.existsSync(configPath)) ? require(configPath) : null; 28 | if (typeof webpackConfig === 'function') { 29 | webpackConfig = webpackConfig(); 30 | } 31 | var resolveConfig = webpackConfig && webpackConfig.resolve; 32 | if (resolveConfig) { 33 | Object.keys(resolveConfig).forEach(function (key) { 34 | if (key === 'packageMains') { 35 | config.mainFields = resolveConfig[key]; 36 | } else if (key === 'root') { 37 | var roots = resolveConfig[key]; 38 | if (isArray(roots)) { 39 | config.modules = roots.concat(config.modules); 40 | } else { 41 | config.modules.unshift(roots); 42 | } 43 | } else if (key === 'fallback') { 44 | var fallback = resolveConfig[key]; 45 | if (isArray(fallback)) { 46 | config.modules = config.modules.concat(fallback); 47 | } else { 48 | config.modules.push(fallback); 49 | } 50 | } else if (key === 'modules') { 51 | config.modules = config.modules.concat(resolveConfig[key]); 52 | } else { 53 | config[key] = resolveConfig[key]; 54 | } 55 | }); 56 | } 57 | 58 | return ResolverFactory.createResolver(config); 59 | } 60 | 61 | function resolveToFile(resolver, name, parentFile) { 62 | var projectDir = infer.cx().parent.projectDir; 63 | var fullParent = path.resolve(projectDir, parentFile); 64 | try { 65 | return resolver.resolveSync({}, path.dirname(fullParent), name); 66 | } catch(e) { 67 | console.log(e.stack); 68 | return ''; 69 | } 70 | } 71 | 72 | tern.registerPlugin("webpack", function(server, options) { 73 | var configPath = options.configPath || './webpack.config.js'; 74 | var modules = options.modules || ['node_modules']; 75 | configPath = path.resolve(server.options.projectDir, configPath); 76 | var resolver = getResolver(modules, configPath); 77 | server.loadPlugin("commonjs"); 78 | server.loadPlugin("es_modules"); 79 | server.mod.modules.resolvers.push(function (name, parentFile) { 80 | var resolved = resolveToFile(resolver, name, parentFile); 81 | return resolved && infer.cx().parent.normalizeFilename(resolved); 82 | }); 83 | }); 84 | -------------------------------------------------------------------------------- /test/cases/angular/config.js: -------------------------------------------------------------------------------- 1 | angular.module('sample.config', []) 2 | 3 | .value('myConfig', { 4 | myColor: 'red', 5 | }) 6 | 7 | ; 8 | -------------------------------------------------------------------------------- /test/cases/angular/filters.js: -------------------------------------------------------------------------------- 1 | angular.module('sample.filters', []) 2 | 3 | // greet greets the named user. 4 | .filter('greet', function() { 5 | return function(s) { 6 | return 'Hello, ' + s + '!'; 7 | }; 8 | }) 9 | 10 | .constant("someNumber", 44) 11 | 12 | ; 13 | -------------------------------------------------------------------------------- /test/cases/angular/main.js: -------------------------------------------------------------------------------- 1 | // plugin=angular 2 | // environment=browser 3 | // environment=jquery 4 | // loadfiles=config.js, filters.js 5 | 6 | angular.module('sample', ['ngResource', 'sample.config', 'sample.filters']) 7 | 8 | .run(function($rootScope, someNumber) { 9 | someNumber; //: number 10 | $rootScope.rootName; //: string 11 | }) 12 | 13 | .controller('GreetingCtrl', ['$rootScope', '$scope', 'User', 'myConfig', 'version', function($rootScope, $scope, User, myConfig, version) { 14 | $rootScope.rootName = $scope.myName = 'John'; 15 | $scope.myName; //: string 16 | 17 | $scope.myConfig = myConfig; //: {myColor} 18 | $scope.myConfig; //: {myColor} 19 | 20 | $scope.version = version; //: string 21 | $scope.version; //: string 22 | 23 | User; //doc: doc for User 24 | 25 | $scope.user = User.get({login: 'sqs'}); 26 | $scope.user.$promise.finally; //: fn(callback: fn()) -> Promise 27 | }]) 28 | 29 | .controller('OtherCtrl', ['$scope', function($scope) { 30 | // Test that controllers' $scope objects are distinct. 31 | $scope.myName; //: ? 32 | }]) 33 | 34 | .constant('version', 'v1.2.3') 35 | 36 | // doc for User 37 | .factory('User', ['$resource', function($resource) { 38 | return $resource('https://api.github.com/users/:login'); 39 | }]) 40 | 41 | ; 42 | 43 | angular.module('docsScopeProblemExample', []) 44 | .directive('myCustomer', function() { 45 | return { 46 | //+ bindToController, compile, controller, controllerAs, template, templateUrl, ... 47 | }; 48 | }).directive('myCustomer', function() { 49 | return { 50 | t //+ template, templateNamespace, templateUrl, ... 51 | }; 52 | }); 53 | 54 | // Create a new object and extend it with angular.extend 55 | var extendObject = { aNumber: 2 }; 56 | angular.extend(extendObject, { aString: 'baz' }); 57 | 58 | extendObject.aNumber; //: number 59 | extendObject.aString; //: string 60 | -------------------------------------------------------------------------------- /test/cases/angular_service.js: -------------------------------------------------------------------------------- 1 | // Issue #267 2 | 3 | // plugin=angular 4 | // environment=browser 5 | 6 | var foodMeApp = angular.module('foodMeApp'); 7 | 8 | foodMeApp.service('cart', function Cart(localStorage, customer, $rootScope, $http, alert) { 9 | this.length = "4"; 10 | }); 11 | 12 | foodMeApp.controller('CheckoutController', function CheckoutController($scope, cart) { 13 | cart; //: Cart 14 | cart.length; //: string 15 | }); 16 | -------------------------------------------------------------------------------- /test/cases/arguments.js: -------------------------------------------------------------------------------- 1 | var arguments = 1; //: number 2 | 3 | (function() { return arguments.length; })(); //: number 4 | -------------------------------------------------------------------------------- /test/cases/arithmetic_ops.js: -------------------------------------------------------------------------------- 1 | 2 | var a = '1' + 1; 3 | a; //: string 4 | 5 | var b = '1' - 1; 6 | b; //: number 7 | 8 | var c = '1' * 1; 9 | c; //: number 10 | 11 | var d = '1' / 1; 12 | d; //: number 13 | 14 | var e = '1' % 1; 15 | e; //: number 16 | 17 | var f = '1' ** 1; 18 | f; //: number 19 | -------------------------------------------------------------------------------- /test/cases/array_holes.js: -------------------------------------------------------------------------------- 1 | var x = [,, "foo", "bar"] 2 | 3 | var [,, y, z] = x 4 | 5 | y //: string 6 | z //: string 7 | -------------------------------------------------------------------------------- /test/cases/arrow.js: -------------------------------------------------------------------------------- 1 | let f = (a, [b]) => ({a, b}) 2 | 3 | f(1, [true]) //:: {a: number, b: bool} 4 | 5 | function wrap() { 6 | return () => (this //:: {a: number} 7 | ) 8 | } 9 | wrap.call({a: 10}).call({b: true}) //:: {a: number} 10 | 11 | function Obj() { 12 | this.x = true 13 | } 14 | Obj.prototype.map = function() { 15 | return [1, 2, 3].map(e => this.x) 16 | } 17 | 18 | ;(new Obj).map() //: [bool] 19 | -------------------------------------------------------------------------------- /test/cases/async-arrow.js: -------------------------------------------------------------------------------- 1 | // environment=browser 2 | 3 | var aFetch = async () => { 4 | var res = await fetch('htts://www.google.com'); 5 | var data = await res.arrayBuffer(); 6 | data; //: ArrayBuffer 7 | return data; 8 | } 9 | 10 | var aParse = async () => { 11 | var tmp = await 100; 12 | tmp; //: number 13 | var arr = await aFetch(); 14 | arr; //: ArrayBuffer 15 | } 16 | 17 | 18 | var bFetch = async () => { 19 | var res = await fetch('htts://www.google.com'); 20 | return res.arrayBuffer(); 21 | } 22 | 23 | var bParse = async () => { 24 | var arr = await bFetch(); 25 | arr; //: ArrayBuffer 26 | } 27 | 28 | 29 | var cFetch = async () => { 30 | var res = await fetch('htts://www.google.com'); 31 | return res.arrayBuffer(); 32 | } 33 | 34 | var cParse = async () => { 35 | var pr = cFetch(); 36 | pr; //: Promise 37 | pr.then(function (arr) { 38 | arr; //: ArrayBuffer 39 | }); 40 | } 41 | -------------------------------------------------------------------------------- /test/cases/async.js: -------------------------------------------------------------------------------- 1 | // environment=browser 2 | 3 | async function aFetch () { 4 | var res = await fetch('htts://www.google.com'); 5 | var data = await res.arrayBuffer(); 6 | data; //: ArrayBuffer 7 | return data; 8 | } 9 | 10 | async function aParse () { 11 | var tmp = await 100; 12 | tmp; //: number 13 | var arr = await aFetch(); 14 | arr; //: ArrayBuffer 15 | } 16 | 17 | 18 | async function bFetch () { 19 | var res = await fetch('htts://www.google.com'); 20 | return res.arrayBuffer(); 21 | } 22 | 23 | async function bParse () { 24 | var arr = await bFetch(); 25 | arr; //: ArrayBuffer 26 | } 27 | 28 | 29 | async function cFetch () { 30 | var res = await fetch('htts://www.google.com'); 31 | return res.arrayBuffer(); 32 | } 33 | 34 | async function cParse () { 35 | var pr = cFetch(); 36 | pr; //: Promise 37 | pr.then(function (arr) { 38 | arr; //: ArrayBuffer 39 | }); 40 | } 41 | -------------------------------------------------------------------------------- /test/cases/async_for_of.js: -------------------------------------------------------------------------------- 1 | var myIter = { 2 | [Symbol.asyncIterator]() { 3 | return { 4 | next() { 5 | return Promise.resolve({value: {a: 1, b: true}, done: false}) 6 | } 7 | } 8 | } 9 | } 10 | 11 | async function run () { 12 | 13 | for await (var hello of myIter) { 14 | hello //:: {a: number, b: bool} 15 | } 16 | 17 | for await (var {a, b} of myIter) { 18 | a //: number 19 | b //: bool 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /test/cases/async_generator.js: -------------------------------------------------------------------------------- 1 | async function scope () { 2 | async function * myGen () { 3 | yield {c: 1}; 4 | return {c: 2}; 5 | } 6 | 7 | var iter = myGen(); 8 | 9 | for await (const item of iter) { 10 | item; //:: {c: number} 11 | } 12 | } -------------------------------------------------------------------------------- /test/cases/autothis.js: -------------------------------------------------------------------------------- 1 | function Bar() { this.prop = 10; } 2 | Bar.prototype.hallo = function() { 3 | this; //: Bar 4 | this.prop; //: number 5 | }; 6 | 7 | Bar.prototype.fn2 = function() { 8 | this; //: Date 9 | }; 10 | 11 | Date.prototype.fn2 = Bar.prototype.fn2; 12 | new Date().fn2(); 13 | -------------------------------------------------------------------------------- /test/cases/bind.js: -------------------------------------------------------------------------------- 1 | function f(a, b, c, d) { return a + b + c + d; } 2 | var g = f.bind(null, 1, 2); 3 | 4 | g(2, 3); //: number 5 | g; //: fn(c: number, d: number) -> number 6 | 7 | function h(a) { return {a: a, th: this}; } 8 | var i = h.bind({str: "foo"}, 2); 9 | 10 | i.call({x: 1}); //:: {a: number, th: {str: string}} 11 | 12 | var o = {i: i}; 13 | o.i(); //:: {a: number, th: {str: string}} 14 | 15 | function j() { return this; } 16 | var k = j.bind({a: true}); 17 | 18 | k.call({b: false}); 19 | k(); //:: {a: bool} 20 | -------------------------------------------------------------------------------- /test/cases/block_scope.js: -------------------------------------------------------------------------------- 1 | function x(a) { 2 | { 3 | let a = 4 4 | let b = 10 5 | var c = true 6 | a; //: number 7 | b; //: number 8 | c; //: bool 9 | } 10 | a; //: string 11 | b; //: ? 12 | c; //: bool 13 | } 14 | 15 | x("hello") 16 | -------------------------------------------------------------------------------- /test/cases/blowup.js: -------------------------------------------------------------------------------- 1 | var Ext = Ext || {}; 2 | Ext.extend = function(subclass, superclass) { 3 | superclass = subclass; 4 | subclass = function() { superclass.apply(this, arguments); }; 5 | 6 | var F = function() {}; 7 | F.prototype = superclass.prototype; 8 | subclass.prototype = new F(); 9 | 10 | subclass.extend = function(o) { return Ext.extend(subclass, o); }; 11 | }; 12 | 13 | var Ext = Ext || {}; 14 | Ext.extend = function(subclass, superclass) { 15 | superclass = subclass; 16 | subclass = function() { superclass.apply(this, arguments); }; 17 | 18 | var F = function() {}; 19 | F.prototype = superclass.prototype; 20 | subclass.prototype = new F(); 21 | 22 | subclass.extend = function(o) { return Ext.extend(subclass, o); }; 23 | }; 24 | -------------------------------------------------------------------------------- /test/cases/browser.js: -------------------------------------------------------------------------------- 1 | // environment=browser 2 | 3 | window.document.body; //: Element 4 | 5 | var newElt = document.createElement("div"); //: Element 6 | 7 | newElt.style.border; //: string 8 | 9 | var e_which; 10 | window.addEventListener("mousemove", function(e) { e_which = e.which; }); 11 | e_which; //: number 12 | 13 | console.; //+ assert, clear, count, debug, dir, error, group, groupCollapsed, groupEnd, info, log, table, time, timeEnd, trace, warn 14 | 15 | var u = new URL(); 16 | u.; //+ hash, host, hostname, href, origin, password, pathname, port, protocol, search, searchParams, username 17 | u.origin; //: string 18 | 19 | var sq = u.searchParams; 20 | sq; //: URLSearchParams 21 | sq.; //+ append, delete, entries, get, getAll, has, keys, set, sort, toString, values 22 | sq.keys().next().value; //: string -------------------------------------------------------------------------------- /test/cases/builtins.js: -------------------------------------------------------------------------------- 1 | var x = Math.PI; //: number 2 | Math.cos(x); //: number 3 | 4 | var a = [1, 2, 3]; //: [number] 5 | a.slice(2); //: [number] 6 | a.pop(); //: number 7 | 8 | ["x"].concat(["hi"]); //: [string] 9 | 10 | [true, false, true].filter(function(x){return x;}); //: [bool] 11 | 12 | [].map(function() {return "x";}); //: [string] 13 | 14 | [].reduce(function(a, b) { return a - 2; }, 0); //: number 15 | 16 | Math.cos.call(null, 10); //: number 17 | 18 | (10).toFixed; //: fn(digits: number) -> string 19 | 20 | "foo bar baz".split(" "); //: [string] 21 | 22 | toString; //: fn() -> string 23 | 24 | new Date; //: Date 25 | 26 | var num = new Number(1); 27 | num; //: Number 28 | 29 | "foo".toString(); //: string 30 | 31 | Array.prototype.slice.call([1, 2, 3], 1); //: [number] 32 | 33 | Array.prototype.slice.apply([1, 2, 3], [1]); //: [number] 34 | 35 | String.prototype.indexOf.bind("abcde", "a"); //: fn(from?: number) -> number 36 | -------------------------------------------------------------------------------- /test/cases/catch_error.js: -------------------------------------------------------------------------------- 1 | try { 2 | foo(); 3 | } catch(e) { 4 | e.message; //doc: A human-readable description of the error. 5 | } 6 | e //: ? 7 | -------------------------------------------------------------------------------- /test/cases/cautiouspropagation.js: -------------------------------------------------------------------------------- 1 | var grabbag = {}; 2 | grabbag[foo()] = "hi"; 3 | grabbag[bar()] = {abc: 10}; 4 | grabbag[baz()] = [1, 2, 3]; 5 | var inner = 55 || grabbag[quux()]; 6 | inner; //: number 7 | 8 | var simple = {}; 9 | simple[foo()] = "a"; 10 | simple[bar()] = "b"; 11 | simple[baz()] = "c"; 12 | simple[quux()]; //: string 13 | -------------------------------------------------------------------------------- /test/cases/class.js: -------------------------------------------------------------------------------- 1 | class Point2 { 2 | constructor(x, y) { this.x = x; this.y = y } 3 | plus(pt) { 4 | this //: Point2 5 | pt //: Point2 6 | return new Point2(this.x + pt.x, this.y + pt.y) 7 | } 8 | get xx() { return this.x } 9 | quux() { return 1 } 10 | static origin() { return new Point2(0, 0) } 11 | } 12 | 13 | class Point4 extends Point3 { 14 | constructor(x, y, z, u) { super(x, y, z); this.u = u } 15 | argh() { return 2 } 16 | } 17 | 18 | var Point3 = class extends Point2 { 19 | constructor(x, y, z) { super(x, y); this.z = z } 20 | foobar() { return true } 21 | } 22 | 23 | var p1 = new Point2(1, 2) 24 | p1.x //: number 25 | p1 //: Point2 26 | var p2 = Point2.origin() 27 | p2 //: Point2 28 | p1.plus(p2) //: Point2 29 | p1.foobar() //: ? 30 | p1.xx //: number 31 | 32 | var p3 = new Point3(0, 0, 5) 33 | p3 //: Point3 34 | p3.quux() //: number 35 | p3.foobar() //: bool 36 | 37 | var p4 = new Point4(1, 2, 3, 4) 38 | p4 //: Point4 39 | p4.argh() //: number 40 | p4.foobar() //: bool 41 | p4.quux() //: number 42 | -------------------------------------------------------------------------------- /test/cases/complete_strings.js: -------------------------------------------------------------------------------- 1 | // plugin=complete_strings 2 | // blank-comments=true 3 | 4 | if (x == "foobar") { 5 | apparently(x == "f 6 | //<+ "foobar" 7 | } 8 | -------------------------------------------------------------------------------- /test/cases/computedprop.js: -------------------------------------------------------------------------------- 1 | var x = {}; 2 | x[foo()] = {a: 10, b: 20}; 3 | x.bar.a; //: number 4 | x.bar. //+? a, b 5 | 6 | var obj = {a: "bar", b: "baz"}; 7 | obj[foo()]; //:? string 8 | -------------------------------------------------------------------------------- /test/cases/contextcomplete.js: -------------------------------------------------------------------------------- 1 | function whoAmI(a, i) { 2 | a.splice(i, 1); 3 | a.c //+? concat, copyWithin 4 | } 5 | -------------------------------------------------------------------------------- /test/cases/copyprops.js: -------------------------------------------------------------------------------- 1 | function buildCopy(o) { 2 | var oo = {}; 3 | for (var prop in o) oo[prop] = o[prop]; 4 | return oo; 5 | } 6 | 7 | buildCopy({xx: 10, yy: 20}); //:: {xx: number, yy: number} 8 | -------------------------------------------------------------------------------- /test/cases/ctorpattern.js: -------------------------------------------------------------------------------- 1 | // Verify that the binding of `this` to the top scope is overridden by 2 | // the binding to an instance. 3 | 4 | function Ctor() { 5 | if (!(this instanceof Ctor)) return new Ctor(); 6 | this.foo = 20; 7 | } 8 | 9 | Ctor().foo; //: number 10 | foo; //: ? 11 | -------------------------------------------------------------------------------- /test/cases/def_type_string.js: -------------------------------------------------------------------------------- 1 | // environment=string 2 | 3 | foo; //: string 4 | -------------------------------------------------------------------------------- /test/cases/defineProperty.js: -------------------------------------------------------------------------------- 1 | var o = {}; 2 | 3 | // Docstring for prop1 4 | Object.defineProperty(o, "prop1", { 5 | get: function() { return "hi"; } 6 | }); 7 | 8 | Object.defineProperty(o, "prop2", { 9 | value: 100 10 | }); 11 | 12 | o.prop1; //: string 13 | o.prop2; //: number 14 | 15 | o.prop1; //doc: Docstring for prop1 16 | 17 | var o2 = {}; 18 | 19 | Object.defineProperties(o2, { 20 | prop1: {get: function() { return 2; }}, 21 | prop2: {value: true} 22 | }); 23 | 24 | o2.prop1; //: number 25 | o2.prop2; //: bool 26 | -------------------------------------------------------------------------------- /test/cases/defs/span.json: -------------------------------------------------------------------------------- 1 | { 2 | "!name": "a/b.js", 3 | "iHaveASpan": { 4 | "!type": "fn()", 5 | "!span": "40[3:2]-60[3:22]" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test/cases/defs/string.json: -------------------------------------------------------------------------------- 1 | { 2 | "!name": "a/b.js", 3 | "foo": { 4 | "!type": "string", 5 | "!span": "1[1:1]-2[2:2]" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test/cases/destructure.js: -------------------------------------------------------------------------------- 1 | let [a] = [1, 2] 2 | a //: number 3 | 4 | let {c, d} = {c: "hello", d: true} 5 | c //: string 6 | d //: bool 7 | 8 | function foo([e], {f=1}, {g}, ...h) { 9 | e //: bool 10 | f //: number 11 | g //: string 12 | h //: [number] 13 | } 14 | // Check for argument leakage 15 | e //: ? 16 | g //: ? 17 | 18 | foo([false], blah(), {g: "hello"}, 20) 19 | 20 | let i, j 21 | ;({i, j, k: [l, ...m]} = {i: 1, j: false, k: ["a", "b"]}) 22 | 23 | i //: number 24 | j //: bool 25 | l //: string 26 | m //: [string] 27 | 28 | var out = {} 29 | ;[out.prop, out.prop2] = [55, true] 30 | out //:: {prop2: bool, prop: number} 31 | 32 | var [n, o] = ["a", false] 33 | n //: string 34 | o //: bool 35 | -------------------------------------------------------------------------------- /test/cases/docstrings.js: -------------------------------------------------------------------------------- 1 | Date; //doc: Creates JavaScript Date instances which let you work with dates and times. 2 | new Date; //doc: Creates JavaScript Date instances which let you work with dates and times. 3 | 4 | var myalias = Date; 5 | 6 | myalias; //doc: Creates JavaScript Date instances which let you work with dates and times. 7 | 8 | // This is variable foo. 9 | var foo = 10; 10 | 11 | foo; //doc: This is variable foo. 12 | 13 | // This function returns a monkey. 14 | function makeMonkey() { return "monkey"; } 15 | 16 | makeMonkey; //doc: This function returns a monkey. 17 | 18 | var monkeyAlias = makeMonkey; 19 | 20 | monkeyAlias; //doc: This function returns a monkey. 21 | 22 | // This is an irrelevant comment. 23 | 24 | 25 | // This describes abc. 26 | var abc = 20; 27 | 28 | abc; //doc: This describes abc. 29 | 30 | // Quux is a thing. 31 | // Two lines. 32 | function Quux() {} 33 | 34 | Quux; //doc+: Quux is a thing.\nTwo lines. 35 | 36 | /* 37 | * Extra bogus 38 | * whitespace is also stripped. 39 | */ 40 | var baz = "hi"; 41 | 42 | baz; //doc: Extra bogus whitespace is also stripped. 43 | 44 | /* starry format 45 | * with first line text 46 | */ 47 | var oy = 1; 48 | 49 | oy; //doc: starry format with first line text 50 | 51 | // Block of text 52 | // With some 53 | // * indented 54 | // * pieces 55 | // 56 | // And a blank line 57 | var arr = 6; 58 | 59 | arr; //doc+: Block of text\nWith some\n * indented\n * pieces\n\nAnd a blank line 60 | 61 | // Split off sentences after the first 100 characters. If our pattern 62 | // happens to match. Here we are at about 90 so this one goes over and 63 | // would be removed. 64 | var aha = ""; 65 | 66 | aha; //doc: Split off sentences after the first 100 characters. If our pattern happens to match. 67 | 68 | // Also ignore JSDoc-y stuff 69 | // @type {zoink} 70 | var xyzzy = false; 71 | 72 | xyzzy; //doc: Also ignore JSDoc-y stuff 73 | 74 | var o = { 75 | // Get the name. 76 | getName: function() { return this.name; }, 77 | // The name 78 | name: "Harold", 79 | // A computed property 80 | [1 + 1]: "OK", 81 | // A string property 82 | 'bar': 4 83 | }; 84 | 85 | // The string "foo". 86 | o.foo = "foo"; 87 | 88 | o.getName; //doc: Get the name. 89 | o.name; //doc: The name 90 | o.foo; //doc: The string "foo". 91 | o.bar //doc: A string property 92 | 93 | class C { 94 | // The method 95 | method() { return 10 } 96 | // The something 97 | get something() { return 20 } 98 | } 99 | 100 | var c = new C 101 | 102 | c.method //doc: The method 103 | c.something //doc: The something 104 | -------------------------------------------------------------------------------- /test/cases/effects.js: -------------------------------------------------------------------------------- 1 | ["foo", "bar"].map(function(s) { return s.charCodeAt(0); }); //: [number] 2 | 3 | var b = []; 4 | b.push(true); 5 | b; //: [bool] 6 | 7 | var c = []; 8 | c.push("hi"); 9 | c.push(10); 10 | c; //: [string|number] 11 | 12 | var d; 13 | function setD(a) { d = a; } 14 | setD.call(null, 55); 15 | d; //: number 16 | -------------------------------------------------------------------------------- /test/cases/empty_overridden_prop.js: -------------------------------------------------------------------------------- 1 | function Class() {} 2 | Class.prototype.hello = "string"; 3 | 4 | var c = new Class; 5 | c.hello = nobodyKnows(); 6 | 7 | c.hello; //: string 8 | -------------------------------------------------------------------------------- /test/cases/es6-features.js: -------------------------------------------------------------------------------- 1 | class Foo { 2 | constructor(a = 10, ...b) { 3 | this.a = a 4 | this.b = b 5 | } 6 | 7 | method() { return this.a + this.b } 8 | 9 | get x() { return `template${this.a}` } 10 | } 11 | 12 | let x = 1 13 | 14 | const y = { 15 | func() { return 10 }, 16 | get b() { return false }, 17 | ["foo" + "bar"]: 800 18 | } 19 | 20 | let it = function*(n) { 21 | for (let i = 0; i < n; i++) yield n 22 | } 23 | 24 | console.log([for (a of [1, 2, ...it(10)]) a * 2]) 25 | 26 | let [a, b] = [1, 2]; 27 | 28 | let x = ([a], {b}) => a + b 29 | 30 | let [e1,,e3] = ["5", false, 6] 31 | 32 | const obj = { 33 | hi: 'hello' 34 | }; 35 | 36 | const newObj = { 37 | hi, 38 | ...o //+ 39 | } 40 | const { 41 | hi, 42 | ...o //+ 43 | } = obj //+ obj 44 | -------------------------------------------------------------------------------- /test/cases/es_modules/blah.js: -------------------------------------------------------------------------------- 1 | export var a = 10 2 | export let b = "ten", c = true 3 | export function d() { return 10 } 4 | -------------------------------------------------------------------------------- /test/cases/es_modules/class.js: -------------------------------------------------------------------------------- 1 | export default class { 2 | methodA() { 3 | } 4 | }; -------------------------------------------------------------------------------- /test/cases/es_modules/foo.js: -------------------------------------------------------------------------------- 1 | export default 22 2 | 3 | export function hello() { return true } 4 | 5 | export var heythere = 100 6 | -------------------------------------------------------------------------------- /test/cases/es_modules/func.js: -------------------------------------------------------------------------------- 1 | export default function () { return true; } -------------------------------------------------------------------------------- /test/cases/es_modules/main.js: -------------------------------------------------------------------------------- 1 | // plugin=node 2 | // plugin=es_modules 3 | 4 | import foo from "./foo" 5 | foo //: number 6 | import {hello as holle // bool 10 | 11 | import {//+ isatty 12 | } from "tty" 13 | 14 | import * as blah // number} 17 | 18 | import * as reexp from "./reexp" 19 | reexp //:: {a: number, b: bool} 20 | 21 | import "./b" //+ "./blah" 22 | 23 | import C from "./class" 24 | 25 | (new C()). //+ methodA 26 | 27 | import f from "./func" 28 | 29 | f //: fn() -> bool 30 | 31 | import o from "./obj" 32 | 33 | o.propA //: number 34 | o.propB //: string 35 | -------------------------------------------------------------------------------- /test/cases/es_modules/obj.js: -------------------------------------------------------------------------------- 1 | export default {propA: 1, propB: "str"} -------------------------------------------------------------------------------- /test/cases/es_modules/reexp.js: -------------------------------------------------------------------------------- 1 | export {a, c as b} from "./blah" 2 | -------------------------------------------------------------------------------- /test/cases/extends.js: -------------------------------------------------------------------------------- 1 | // Follows the pattern CoffeeScript uses to define classes. 2 | 3 | var __extends = function(child, parent) { 4 | for (var key in parent) { child[key] = parent[key]; } 5 | function ctor() { this.constructor = child; } 6 | ctor.prototype = parent.prototype; 7 | child.prototype = new ctor(); 8 | }; 9 | 10 | var Top = (function() { 11 | function Top() {} 12 | Top.prototype.topMethod = function() {return "hey";}; 13 | Top.topStatic = 20; 14 | return Top; 15 | })(); 16 | 17 | var SubOne = (function(_super) { 18 | function SubOne(arg) { this.argOne = arg; } 19 | __extends(SubOne, _super); 20 | SubOne.prototype.methodOne = function() {return 11;}; 21 | return SubOne; 22 | })(Top); 23 | 24 | var SubTwo = (function(_super) { 25 | function SubTwo(arg) { this.argTwo = arg; } 26 | __extends(SubTwo, _super); 27 | SubTwo.prototype.methodTwo = function() {return null;}; 28 | return SubTwo; 29 | })(Top); 30 | 31 | var SubEleven = (function(_super) { 32 | function SubEleven(arg) { SubOne.call(this, arg); } 33 | __extends(SubEleven, SubOne); 34 | SubEleven.prototype.methodEleven = function() {return "blah";}; 35 | return SubEleven; 36 | })(SubOne); 37 | 38 | var top = new Top, one = new SubOne(true), two = new SubTwo(false), elf = new SubEleven(true); 39 | 40 | one.topMethod; //: fn() -> string 41 | 42 | one.methodOne; //: fn() -> number 43 | 44 | one.argOne; //: bool 45 | 46 | one.argTwo; //: ? 47 | 48 | two.argTwo; //: bool 49 | 50 | one.methodTwo; //: ? 51 | 52 | one.methodEleven; //: ? 53 | 54 | SubEleven.topStatic; //: number 55 | 56 | elf.methodOne; //: fn() -> number 57 | 58 | elf.methodEleven(); //: string 59 | 60 | two.methodEleven; //: ? 61 | -------------------------------------------------------------------------------- /test/cases/fetch.js: -------------------------------------------------------------------------------- 1 | // environment=browser 2 | 3 | var f = fetch('htts://www.google.com'); 4 | 5 | f.then(function (resp) { 6 | resp; //: Response 7 | 8 | resp.; //+ arrayBuffer, blob, bodyUsed, clone, error, formData, headers, json, ok, redirect, status, statusText, text, type, url 9 | 10 | resp.arrayBuffer().then(function (ab) { 11 | ab; //: ArrayBuffer 12 | }); 13 | resp.blob().then(function (b) { 14 | b; //: Blob 15 | }); 16 | resp.json().then(function (j) { 17 | j; //: ? 18 | }); 19 | resp.text().then(function (t) { 20 | t; //: string 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/cases/finddef.js: -------------------------------------------------------------------------------- 1 | function blah() {} 2 | 3 | var jaja = 40; 4 | 5 | var obj = { 6 | prop1: 10, 7 | prop2: function(foo) {} 8 | }; 9 | 10 | obj.prop3 = "hi"; 11 | 12 | blah; //loc: 1, 9 13 | jaja; //loc: 3, 4 14 | obj; //loc: 5, 4 15 | obj.prop1; //loc: 6, 2 16 | obj.prop2; //loc: 7, 2 17 | obj.prop3; //loc: 10, 4 18 | 19 | function hide() { return obj.prop2; } 20 | 21 | hide(); //loc: 7, 9 22 | 23 | function another(arg) { 24 | var local = 1; 25 | arg; // 23, 17 26 | local; //loc: 24, 6 27 | } 28 | 29 | class foo { 30 | bar // string 15 | this.foo; //: fn() -> number 16 | } 17 | }); 18 | -------------------------------------------------------------------------------- /test/cases/infinite-expansion.js: -------------------------------------------------------------------------------- 1 | // Issue #16 2 | 3 | var f = function(n) { 4 | n.prototype = {}; // Make this count as a type construction function 5 | f(n()); // Push an IsCallee constraint from the body 6 | }; 7 | f(f); 8 | 9 | // Create a self-referential type 10 | var x = [x]; 11 | 12 | // Force analysis 13 | x[0]; //: [?] 14 | 15 | function goop(n) { 16 | n.prototype = {}; 17 | return function(f){f(1);}; 18 | }; 19 | goop(1)(goop); 20 | -------------------------------------------------------------------------------- /test/cases/jquery_event.js: -------------------------------------------------------------------------------- 1 | // environment=browser 2 | // environment=jquery 3 | 4 | $("#foo").blur().click(function(e) { 5 | e; //: jQuery.Event 6 | e.pageX; //: number 7 | }); 8 | -------------------------------------------------------------------------------- /test/cases/jsdoc.js: -------------------------------------------------------------------------------- 1 | /** @type {Date} */ 2 | var a = getSomething(); 3 | a; //: Date 4 | 5 | a.getTime; //: fn() -> number 6 | 7 | /** @type {{x: Integer, y: [String]}} */ 8 | var c = somethingElse(); 9 | c; //:: {x: number, y: [string]} 10 | 11 | /** 12 | * This is a function 13 | * @return {[Number]} 14 | * @param {Number} a 15 | * @param {String} b 16 | */ 17 | function foo(a, b) { return hohoho(); } 18 | foo; //: fn(a: number, b: string) -> [number] 19 | 20 | /** @this Date */ 21 | var abc = function() { 22 | this; //: Date 23 | }; 24 | 25 | /** @this Abc */ 26 | var Abc = function() { 27 | this; //: Abc 28 | }; 29 | 30 | /** @class */ 31 | var AbcCls = function() { 32 | this; //: AbcCls 33 | }; 34 | 35 | /** @constructor */ 36 | var AbcCtor = function() { 37 | this; //: AbcCtor 38 | }; 39 | 40 | /** 41 | * This is also a function 42 | * @returns {string} 43 | * @arg {Number} a 44 | */ 45 | var bar = function(a, b) { return goop(); }; 46 | bar(gulp(), 10); 47 | bar; //: fn(a: number, b: number) -> string 48 | 49 | var o = { 50 | /** @type {String} */ 51 | prop1: mystery(), 52 | 53 | /** @returns {Number} */ 54 | prop2: function() { return anything(); } 55 | }; 56 | 57 | /** @returns {String} */ 58 | o.prop3 = function() { return something(); }; 59 | 60 | o.prop1; //: string 61 | o.prop2; //: fn() -> number 62 | o.prop3; //: fn() -> string 63 | 64 | /** @type {Array.} */ 65 | var closureArray = anotherThing(); 66 | closureArray[1]; //: string 67 | 68 | /** @type {Object.} */ 69 | var closureMap = yetAnotherThing(); 70 | closureMap[1]; //: bool 71 | 72 | /** @param {Number=} a */ 73 | function takesOpt(a) { console.log(a || 20); } 74 | 75 | takesOpt; //: fn(a?: number) 76 | 77 | /** @typedef {Array.} Bitset */ 78 | 79 | /** 80 | * @typedef {Object} MyType 81 | * @property {boolean} one - Property one 82 | * @property {integer} two - And two 83 | */ 84 | 85 | someNonDeclarationStatement(); 86 | 87 | /** @type {Bitset} */ 88 | var myBitset = getABitset(); 89 | 90 | myBitset; //: [bool] 91 | 92 | /** @type {MyType} */ 93 | var myObj; 94 | 95 | myObj.one //: bool 96 | myObj.two //: number 97 | ({}).one //: ? 98 | 99 | function NonAscïį() { this.length = "hi"; } 100 | 101 | /** @type {NonAscïį} */ 102 | var inst; 103 | 104 | inst.length; //: string 105 | 106 | /** @type {bogus.Type} */ 107 | var bogus = abcdef(); 108 | 109 | bogus; //: bogus.Type 110 | 111 | /** @type {bogus.Overridden} */ 112 | var again = 10; 113 | 114 | again; //: number 115 | 116 | /** 117 | * @return {bogus.Retval} 118 | * @param {bogus.Arg} a 119 | */ 120 | function functionBogus(a) { return hohoho(); } 121 | 122 | functionBogus; //: fn(a: bogus.Arg) -> bogus.Retval 123 | 124 | /** @type {string|number} */ 125 | var stringOrNumber; 126 | 127 | stringOrNumber; //: string|number 128 | 129 | /** 130 | * @param {string|null} a 131 | * @return {Array.} 132 | */ 133 | function unionFunction(a) { return argh(); } 134 | 135 | unionFunction; //: fn(a: string) -> [Foo|number] 136 | 137 | /** 138 | * @returns {string} 139 | */ 140 | function ui() {} 141 | 142 | ui(); //: string 143 | 144 | /** 145 | * @param {?string} [somebody=John Doe] - Somebody's name. 146 | */ 147 | function sayHello(somebody) { 148 | somebody; //: string 149 | } 150 | 151 | /** 152 | * Testing jsdoc with properties for an object 153 | * @param {!Object} employee - The employee who is responsible for the project. 154 | * @param {string} employee.name - The name of the employee. 155 | * @param {string} employee.department - The employee's department. 156 | */ 157 | function paramProperties(employee) { 158 | employee; //:: {department: string, name: string} 159 | } 160 | 161 | /** 162 | * Testing jsdoc with properties for objects in an array 163 | * @param {Object[]} employees - The employees who are responsible for the project. 164 | * @param {string} employees[].name - The name of an employee. 165 | * @param {string} employees[].department - The employee's department. 166 | */ 167 | function arrayParamProperties(employees) { 168 | employees; //:: [{department: string, name: string}] 169 | } 170 | -------------------------------------------------------------------------------- /test/cases/map.js: -------------------------------------------------------------------------------- 1 | let map = new Map 2 | 3 | map.set(55, "hello") 4 | map.get(55) //: string 5 | 6 | for (let val of map.values()) 7 | val //: string 8 | 9 | for (let key of map.keys()) 10 | key //: number 11 | 12 | for (let [key, value] of map) { 13 | key //: number 14 | value //: string 15 | } 16 | for (let pair of map) { 17 | pair //: [number, string] 18 | ;[key, value] = pair 19 | key //: number 20 | value //: string 21 | } 22 | 23 | map.forEach(function(val, key) { 24 | val //: string 25 | key //: number 26 | }) 27 | -------------------------------------------------------------------------------- /test/cases/merge.js: -------------------------------------------------------------------------------- 1 | function sum(a) { 2 | return a.x + 20; 3 | } 4 | 5 | sum({x: 10, y: 20}); 6 | sum({x: 10, y: 20}); 7 | 8 | sum; //:: fn(a: {x: number, y: number}) -> number 9 | -------------------------------------------------------------------------------- /test/cases/mixin.js: -------------------------------------------------------------------------------- 1 | function Class() {} 2 | Class.prototype = mixin({ 3 | // M3 4 | m3: function() {} 5 | }) 6 | 7 | function mixin(obj) { 8 | obj.m1 = m1 9 | obj.m2 = m2 10 | return obj 11 | } 12 | 13 | // M1 14 | function m1() {} 15 | // M2 16 | function m2() {} 17 | 18 | function OtherClass() {} 19 | OtherClass.prototype = mixin({ 20 | // M4 21 | m4: function() {} 22 | }) 23 | 24 | let c = new Class 25 | 26 | c.m1 //doc: M1 27 | c.m2 //doc: M2 28 | c.m3 //doc: M3 29 | c.m4 //: ? 30 | 31 | let oc = new OtherClass 32 | 33 | oc.m1 //doc: M1 34 | oc.m3 //: ? 35 | oc.m4 //doc: M4 36 | 37 | mixin(new Date) //: Date 38 | mixin("foo") //: string 39 | -------------------------------------------------------------------------------- /test/cases/navigator.js: -------------------------------------------------------------------------------- 1 | // environment=browser 2 | 3 | navigator; //: navigator 4 | 5 | navigator.onLine; //: bool 6 | 7 | navigator.permissions.query({name: 'geolocation'}).then(function (status) { 8 | status; //: PermissionStatus 9 | }) 10 | -------------------------------------------------------------------------------- /test/cases/new_array.js: -------------------------------------------------------------------------------- 1 | var a = new Array(); 2 | a.push("hi"); 3 | a[0]; //: string 4 | 5 | var b = new Array(true, false, true); 6 | b[0]; //: bool 7 | 8 | var c = new Array(1); 9 | c[0]; //: ? 10 | 11 | var d = new Array("one"); 12 | d[0]; //: string 13 | -------------------------------------------------------------------------------- /test/cases/new_to_prototype.js: -------------------------------------------------------------------------------- 1 | function A() {} 2 | A.prototype.prop_A = 1; 3 | function B() {} 4 | B.prototype = new A; 5 | B.prototype.prop_B = 2; 6 | function C() {} 7 | C.prototype = new A; 8 | C.prototype.prop_C = 3; 9 | 10 | (new A).prop_ //+ prop_A 11 | ; 12 | (new B).prop_ //+ prop_A, prop_B 13 | ; 14 | (new C).prop_ //+ prop_A, prop_C 15 | -------------------------------------------------------------------------------- /test/cases/node/binary.node: -------------------------------------------------------------------------------- 1 | exports.binary = 1; 2 | -------------------------------------------------------------------------------- /test/cases/node/dir/index.js: -------------------------------------------------------------------------------- 1 | exports.foo = "hey"; 2 | exports.rel = require("./lib/relative"); 3 | -------------------------------------------------------------------------------- /test/cases/node/dir/lib/other.js: -------------------------------------------------------------------------------- 1 | exports.xyz = "foo"; 2 | -------------------------------------------------------------------------------- /test/cases/node/dir/lib/relative.js: -------------------------------------------------------------------------------- 1 | exports.abc = 1; 2 | exports.def = require("./other.js"); 3 | -------------------------------------------------------------------------------- /test/cases/node/exportfunc.js: -------------------------------------------------------------------------------- 1 | var f = module.exports = function(a, b) { 2 | return a + b; 3 | }; 4 | 5 | f(1, 2); 6 | -------------------------------------------------------------------------------- /test/cases/node/localfile.js: -------------------------------------------------------------------------------- 1 | exports.hello = function() { return 10; }; 2 | -------------------------------------------------------------------------------- /test/cases/node/main.js: -------------------------------------------------------------------------------- 1 | // plugin=node 2 | 3 | var fs = require("fs"), crypto = require("crypto"), tls = require("tls"), util = require("util"); 4 | 5 | util.error; //: fn(msg: string, ...string: string) 6 | util.inspect; //: fn(object: ?, options: {colors: bool, customInspect: bool, depth: number, maxArrayLength: number, showHidden: bool, showProxy: bool, ...}) -> string 7 | 8 | fs.createReadStream; //: fn(path: string|Buffer, options?: ?) -> fs.ReadStream 9 | 10 | fs.stat("foobar", function(err, stats) { 11 | err; //: Error 12 | stats.isFile(); //: bool 13 | }); 14 | 15 | var module = {}; 16 | 17 | crypto.getCiphers()[3]; //: string 18 | crypto.createHash("sha1").digest().readUInt16BE(0); //: number 19 | 20 | tls.createServer({}, function(stream) { 21 | // Has event emitter props 22 | stream.once; //: fn(event: string, listener: fn()) 23 | // Writable stream props 24 | stream.write; //: fn(chunk: Buffer, encoding?: string, callback?: fn()) -> bool 25 | // Readable stream 26 | stream.read; //: fn(size?: number) -> Buffer 27 | // ClearTextStream 28 | stream.authorized; //: bool 29 | }); 30 | 31 | require("timers").setInterval; //: fn(callback: fn(), delay: number, args?: ?) -> timers.Timer 32 | setInterval; //: fn(callback: fn(), delay: number, args?: ?) -> timers.Timer 33 | setTimeout(function(){}, 10).ref; //: fn() -> timers.Timer 34 | 35 | 36 | require("module"); 37 | 38 | // don't attempt to handle .node binary modules 39 | require("./binary.node").binary; //: ? 40 | 41 | var mymod = require("mymod"); 42 | 43 | require("_stream_readable"); 44 | 45 | mymod.foo; //: number 46 | mymod.bar; //: string 47 | 48 | require("./localfile").hello; //: fn() -> number 49 | 50 | require("./foo/../exportfunc.js"); //: fn(a: number, b: number) -> number 51 | 52 | require("./dir"); //:: {foo: string, rel: {abc: number, def: {xyz: string}}} 53 | 54 | var mod1 = require("mod1"); 55 | var mod2 = require("mod1/mainfile.js"); 56 | mod1.mainExport.x; //: number 57 | mod2.mainExport.x; //: number 58 | mod1.fromSubdep; //: fn() 59 | 60 | require("mod1/secondfile").secondExport.u; //: number 61 | require("mod1/dir1").foo.a; //: number 62 | 63 | require("mod1/reassign_exports").funcPropExport; //loc: 2, 15 64 | 65 | require("mod1/reassign_exports_to_required"); //:: {A: number} 66 | 67 | // inference should continue even if a module is not found 68 | require("mod_not_found"); //: ? 69 | 70 | var doc = require("mod1/doc"); 71 | doc.f1; //doc: doc for f1 72 | doc.f2; //doc: doc for f2 73 | 74 | module.exports. //+ 75 | 76 | // completion on known require module 77 | require('f' //+ 'fs' 78 | // completion on custom require module 79 | require("my" //+ "mymod" 80 | 81 | // go to definition for modulest 82 | require('./localfile'//loc: 1, 0, localfile.js 83 | 84 | require("./dir/" //+ "./dir/index", "./dir/lib" 85 | require("mod1/d" //+ "mod1/dir1", "mod1/doc" 86 | 87 | require("" //+ "fs", "path", "mod1", ... 88 | -------------------------------------------------------------------------------- /test/cases/node_exports.js: -------------------------------------------------------------------------------- 1 | // plugin=node 2 | 3 | exports.a = 10; 4 | 5 | exports.b = function() {}; 6 | 7 | //exports: {a: number, b: fn()} 8 | -------------------------------------------------------------------------------- /test/cases/node_modules/mod1/a.js: -------------------------------------------------------------------------------- 1 | exports.A = 1; 2 | -------------------------------------------------------------------------------- /test/cases/node_modules/mod1/dir1/index.js: -------------------------------------------------------------------------------- 1 | exports.foo = {a: 10}; 2 | -------------------------------------------------------------------------------- /test/cases/node_modules/mod1/doc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // doc for f1 3 | f1: function() { return 7; } 4 | }; 5 | 6 | // doc for f2 7 | module.exports.f2 = function() { return 7; }; 8 | -------------------------------------------------------------------------------- /test/cases/node_modules/mod1/mainfile.js: -------------------------------------------------------------------------------- 1 | exports.mainExport = {x: 10, y: 20}; 2 | 3 | exports.fromSubdep = require("subdep1").subdepFunc; 4 | -------------------------------------------------------------------------------- /test/cases/node_modules/mod1/node_modules/subdep1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "subdep1", 3 | "version": "0.0.1", 4 | "main": "subdep1" 5 | } 6 | -------------------------------------------------------------------------------- /test/cases/node_modules/mod1/node_modules/subdep1/subdep1.js: -------------------------------------------------------------------------------- 1 | exports.subdepFunc = function(){}; 2 | -------------------------------------------------------------------------------- /test/cases/node_modules/mod1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "mainfile.js", 3 | "dependencies": { 4 | "subdep1": "0.0.1" 5 | }, 6 | "bundledDependencies": ["subdep1"] 7 | } 8 | -------------------------------------------------------------------------------- /test/cases/node_modules/mod1/reassign_exports.js: -------------------------------------------------------------------------------- 1 | module.exports = function() {}; 2 | module.exports.funcPropExport = 7; 3 | -------------------------------------------------------------------------------- /test/cases/node_modules/mod1/reassign_exports_to_required.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./a'); 2 | -------------------------------------------------------------------------------- /test/cases/node_modules/mod1/secondfile.js: -------------------------------------------------------------------------------- 1 | exports.secondExport = {u: 10, v: 20}; 2 | -------------------------------------------------------------------------------- /test/cases/node_modules/mymod.json: -------------------------------------------------------------------------------- 1 | { 2 | "!name": "mymod", 3 | "exports": { 4 | "foo": "number", 5 | "bar": "string" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test/cases/node_origin_paths.js: -------------------------------------------------------------------------------- 1 | // plugin=node 2 | 3 | require('./node/localfile').hello; //origin: node/localfile.js 4 | -------------------------------------------------------------------------------- /test/cases/object-string-prop.js: -------------------------------------------------------------------------------- 1 | 2 | var obj = { 3 | Test: "Hello" 4 | }; 5 | 6 | obj = { 7 | "T //+ Test 8 | }; -------------------------------------------------------------------------------- /test/cases/object_create.js: -------------------------------------------------------------------------------- 1 | var base = {foo: 10, bar: 20}; 2 | var gen1 = Object.create(base); 3 | var gen2 = Object.create(gen1); 4 | 5 | base.baz = 30; 6 | gen1.quux = 50; 7 | gen2.kaka = 10; 8 | 9 | gen1.foo; //: number 10 | 11 | gen2.foo; //: number 12 | 13 | gen1.baz; //: number 14 | 15 | gen2.baz; //: number 16 | 17 | gen1.quux; //: number 18 | 19 | gen2.quux; //: number 20 | 21 | gen1.kaka; //: ? 22 | 23 | var extend = Object.create(base, {prop1: {value: "hi"}, prop2: {}}); 24 | 25 | extend.prop1; //: string 26 | 27 | extend.bar; //: number 28 | 29 | var empty = Object.create(null); 30 | empty.prop1 = "hi"; 31 | 32 | empty.hasOwnProperty; //: ? 33 | empty.prop1; //: string 34 | 35 | function create() { 36 | // Implementation 37 | } 38 | 39 | module.exports = { 40 | create //loc: 35,9 41 | }; 42 | 43 | module.exports = { 44 | create: create //loc: 35,9 45 | }; -------------------------------------------------------------------------------- /test/cases/objectlit.js: -------------------------------------------------------------------------------- 1 | var bar 2 | 3 | var foo = { 4 | [bar = 100]: 44, 5 | __proto__: baz, 6 | a: true, 7 | method() { return "ok" } 8 | } 9 | 10 | var baz = { 11 | __proto__: null, 12 | b: "hi" 13 | } 14 | 15 | foo.a //: bool 16 | foo.method() //: string 17 | foo.b //: string 18 | foo.toString //: ? 19 | -------------------------------------------------------------------------------- /test/cases/objnames.js: -------------------------------------------------------------------------------- 1 | function Ctor1() { this.x = 10; } 2 | Ctor1.prototype = {a: 1}; 3 | 4 | function Ctor2() {} 5 | 6 | var singleton = {a: 10, b: 20}; //: singleton 7 | 8 | new Ctor1(); //: Ctor1 9 | new Ctor2(); //: Ctor2 10 | -------------------------------------------------------------------------------- /test/cases/or_empty_array.js: -------------------------------------------------------------------------------- 1 | var x = "foo".match(/o/) || [] 2 | 3 | x //: [string] 4 | -------------------------------------------------------------------------------- /test/cases/order_of_definition.js: -------------------------------------------------------------------------------- 1 | foo.bar.baz.bug({ 2 | prop: function() { 3 | this.a; //: bool 4 | } 5 | }); 6 | 7 | var foo = {}; 8 | 9 | var baz = {bug: function(o) { 10 | function x() { this.a = true; } 11 | x.prototype = o; 12 | return new x; 13 | }}; 14 | 15 | foo.bar = {baz: baz}; 16 | -------------------------------------------------------------------------------- /test/cases/phantom_object.js: -------------------------------------------------------------------------------- 1 | var mod = window.mymodule 2 | 3 | mod.func = function() { return 10 } 4 | 5 | mod.func //: fn() -> number 6 | 7 | var otherMod = window.yourmodule.theirmodule 8 | 9 | otherMod.c = 10 10 | 11 | otherMod.c //: number 12 | -------------------------------------------------------------------------------- /test/cases/plus.js: -------------------------------------------------------------------------------- 1 | var x = 10; 2 | var y = "foo"; 3 | 4 | x + 20; //: number 5 | x + y; //: string 6 | "foo" + y; //: string 7 | "foo" + x; //: string 8 | -------------------------------------------------------------------------------- /test/cases/promise.js: -------------------------------------------------------------------------------- 1 | var p = new Promise(function(accept, reject) { 2 | reject; //: fn(reason: ?) 3 | accept({x: 20}); 4 | }); 5 | 6 | p.; //+ then, catch, finally 7 | 8 | p.then(function(value) { 9 | value; //:: {x: number} 10 | }).then(function(value) { 11 | value; //:: {x: number} 12 | }); 13 | 14 | var p2 = new Promise(function(acc) { acc("hi"); }); 15 | 16 | Promise.all([p2]).then(function(value) { 17 | value; //: [string] 18 | return Promise.resolve(33); 19 | }).then(function(value) { 20 | value; //: number 21 | }); 22 | 23 | var p3 = Promise.resolve(10); 24 | 25 | p3.then(function(value) { 26 | value; //: number 27 | return true; 28 | }).then(function(value) { 29 | value; //: bool 30 | }); 31 | 32 | var p4 = Promise.resolve(Promise.resolve(10)); 33 | p4.then(function(value) { 34 | value; //: number 35 | }); 36 | 37 | var arg5 = 1 < 2 ? Promise.resolve(10) : 20; 38 | var p5 = Promise.resolve(arg5); 39 | p5.then(function(value) { 40 | value; //: number 41 | }); 42 | 43 | var p6 = Promise.resolve('t').then(function() { 44 | return 1 < 2 ? Promise.resolve(10) : 20; 45 | }).then(function(value) { 46 | value; //: number 47 | }); 48 | 49 | var p7 = Promise.resolve().then(function() { 50 | return 20; 51 | }).then(function(value) { 52 | value; //: number 53 | }); 54 | 55 | var p8 = Promise.resolve(); 56 | p8 //: Promise 57 | 58 | function myResolve1(arg) { 59 | return Promise.resolve(arg); 60 | } 61 | 62 | myResolve1('s') //:: {:t: string} 63 | 64 | function myResolve2(arg) { 65 | return Promise.resolve(arg); 66 | } 67 | 68 | myResolve2('s') //:: {:t: string|number} 69 | myResolve2(4) //:: {:t: string|number} 70 | 71 | myResolve2('s').then(function(value) { 72 | value; //: string|number 73 | }) 74 | 75 | function myResolve3(arg7) { 76 | return Promise.resolve(arg7); 77 | } 78 | 79 | myResolve3(Promise.resolve(4)).then(function(value) { 80 | value; //: number 81 | }); 82 | 83 | myResolve3(Promise.resolve(4)) //:: {:t: number} 84 | 85 | myResolve3(4).then(function(value) { 86 | value; //: number 87 | }); 88 | -------------------------------------------------------------------------------- /test/cases/proto.js: -------------------------------------------------------------------------------- 1 | function Foo(x) { 2 | this.x = x; 3 | this.y = [1]; 4 | } 5 | Foo; //: fn(x: bool) 6 | 7 | Foo.prototype = { 8 | makeString: function() { return "hi"; }, 9 | bar: 13 10 | }; 11 | 12 | var z = new Foo(true); //:: {x: bool, y: [number]} 13 | 14 | z.toString; //: fn() -> string 15 | 16 | z.bar; //: number 17 | -------------------------------------------------------------------------------- /test/cases/protoname.js: -------------------------------------------------------------------------------- 1 | function Base() {} 2 | Base.prototype = {}; 3 | 4 | Base.prototype; //: Base.prototype 5 | new Base; //: Base 6 | 7 | function Sub1() {} 8 | Sub1.prototype = new Base(); 9 | new Sub1(); //: Sub1 10 | 11 | function Sub2() {} 12 | Sub2.prototype = Object.create(Base.prototype); 13 | new Sub2(); //: Sub2 14 | 15 | function Base2() {} 16 | 17 | function Sub3() {} 18 | Sub3.prototype = new Base2(); 19 | 20 | new Sub3(); //: Sub3 21 | -------------------------------------------------------------------------------- /test/cases/replace_bogus_prop.js: -------------------------------------------------------------------------------- 1 | var x = new Type(); 2 | 3 | x.foo; //: string 4 | 5 | function Type() {} 6 | Type.prototype.foo = "hi"; 7 | -------------------------------------------------------------------------------- /test/cases/requirejs/bar.js: -------------------------------------------------------------------------------- 1 | define(["baz"], function(baz) { 2 | return {aNumber: 10, baz: baz}; 3 | }); 4 | -------------------------------------------------------------------------------- /test/cases/requirejs/baz.js: -------------------------------------------------------------------------------- 1 | define("foo", function(foo) { 2 | return {bazProp: new Date, bazFooProp: foo.aString}; 3 | }); 4 | -------------------------------------------------------------------------------- /test/cases/requirejs/foo.js: -------------------------------------------------------------------------------- 1 | define({aString: "hello"}); 2 | -------------------------------------------------------------------------------- /test/cases/requirejs/func.js: -------------------------------------------------------------------------------- 1 | define(function(require, exports, module){ 2 | function aFunction() { return false; } 3 | return aFunction; 4 | }); 5 | -------------------------------------------------------------------------------- /test/cases/requirejs/main.js: -------------------------------------------------------------------------------- 1 | // environment=browser 2 | // environment=jquery 3 | // plugin=requirejs {"override": {"jquery": "=$"}} 4 | 5 | requirejs(["foo", "bar!abc", "useexports", "simplifiedcommon", "subdir/zap", "module_exports"], 6 | function(foo, bar, useexports, simplified, zap, module_exports) { 7 | foo.aString; //: string 8 | bar.aNumber; //: number 9 | bar.baz.bazProp; //: Date 10 | bar.baz.bazFooProp; //: string 11 | useexports.hello; //: bool 12 | simplified.hello; //: string 13 | simplified.func; //: fn() -> bool 14 | zap; //: string 15 | module_exports; //:: {one: number, two: number} 16 | 17 | foo; //origin: foo.js 18 | bar; //origin: bar.js 19 | bar.baz; //origin: baz.js 20 | }); 21 | 22 | requirejs(["jquery"], function($) { 23 | $.fx.off; //: bool 24 | }); 25 | 26 | requirejs(["require"], function(require) { 27 | require("jquery").fx.off; //: bool 28 | require("requireme").someprop; //: string 29 | }); 30 | 31 | requirejs(["named"], function(named) { 32 | named.foo; //:: {a: number} 33 | }); 34 | 35 | requirejs.config({ 36 | p //+ packages, paths, ... 37 | }); 38 | 39 | requirejs.config({ 40 | //+ baseUrl, config, context, map, nodeIdCompat, packages, paths, shim, ... 41 | }); 42 | 43 | requirejs.config({ 44 | baseUrl: '', 45 | //+ config, context, map, nodeIdCompat, packages, paths, shim, ... 46 | }); 47 | 48 | var c1 = { 49 | pa //?+ packages, paths, ... 50 | } 51 | requirejs.config(c1); 52 | 53 | var c2 = { 54 | //?+ baseUrl, config, context, map, nodeIdCompat, packages, paths, shim, ... 55 | } 56 | requirejs.config(c2); 57 | 58 | var c3 = { 59 | baseUrl: '', 60 | //?+ config, context, map, nodeIdCompat, packages, paths, shim, ... 61 | } 62 | requirejs.config(c3); 63 | -------------------------------------------------------------------------------- /test/cases/requirejs/module_exports.js: -------------------------------------------------------------------------------- 1 | define(["module"], function(module) { 2 | module.exports = {one: 1, two: 2}; 3 | }); 4 | -------------------------------------------------------------------------------- /test/cases/requirejs/named.js: -------------------------------------------------------------------------------- 1 | define("myname", ["exports"], function(exports) { 2 | exports.foo = {a: 10}; 3 | }); 4 | -------------------------------------------------------------------------------- /test/cases/requirejs/requireme.js: -------------------------------------------------------------------------------- 1 | define(["exports"], function(exports) { 2 | exports.someprop = "a string"; 3 | }); 4 | -------------------------------------------------------------------------------- /test/cases/requirejs/simplifiedcommon.js: -------------------------------------------------------------------------------- 1 | define(function(require, exports) { 2 | exports.hello = require("foo").aString; 3 | exports.func = require("func"); 4 | }); 5 | -------------------------------------------------------------------------------- /test/cases/requirejs/subdir/zap.js: -------------------------------------------------------------------------------- 1 | define(["../foo"], function(foo) { 2 | return foo.aString; 3 | }); 4 | -------------------------------------------------------------------------------- /test/cases/requirejs/useexports.js: -------------------------------------------------------------------------------- 1 | define(["exports"], function(exports) { 2 | exports.hello = true; 3 | }); 4 | -------------------------------------------------------------------------------- /test/cases/requirejs_config/main.js: -------------------------------------------------------------------------------- 1 | // plugin=requirejs 2 | 3 | requirejs.config({ 4 | paths: { 5 | fooAlias: "../requirejs/foo" 6 | } 7 | }); 8 | 9 | require(["fooAlias"], function(foo) { 10 | foo.aString; //: string 11 | }); 12 | -------------------------------------------------------------------------------- /test/cases/set.js: -------------------------------------------------------------------------------- 1 | let set = new Set 2 | 3 | set.add(true) 4 | set.size //: number 5 | set.has(true) //: bool 6 | 7 | for (var elt of set.values()) 8 | elt //: bool 9 | 10 | set.forEach(function(val) { 11 | val //: bool 12 | }) 13 | -------------------------------------------------------------------------------- /test/cases/simple.js: -------------------------------------------------------------------------------- 1 | var foo = (function() { 2 | return 42; 3 | })(); 4 | foo; //: number 5 | 6 | var x = {}; 7 | 8 | function init(v) { 9 | v.foo = 10; 10 | v.bar = 1 + 1; 11 | } 12 | init; //:: fn(v: {bar: number, foo: number}) 13 | 14 | init(x); 15 | x; //:: {bar: number, foo: number} 16 | -------------------------------------------------------------------------------- /test/cases/simple_generic.js: -------------------------------------------------------------------------------- 1 | function last(arr) { return arr[arr.length - 1]; } 2 | 3 | last([1, 2, 3]); //: number 4 | last(["a", "b", "c"]); //: string 5 | 6 | function map(arr, f) { 7 | var res = []; 8 | for (var i = 0; i < arr.length; ++i) res.push(f(arr[i])); 9 | return res; 10 | } 11 | 12 | map([1, 2, 3], function() { return "X"; }); //: [string] 13 | map([1, 2, 3], function() { return true; }); //: [bool] 14 | -------------------------------------------------------------------------------- /test/cases/span_from_def.js: -------------------------------------------------------------------------------- 1 | // environment=span 2 | 3 | iHaveASpan; //loc: 4, 2 4 | -------------------------------------------------------------------------------- /test/cases/super.js: -------------------------------------------------------------------------------- 1 | class Point3 extends Point2 { 2 | constructor(x, y, z) { super(x, y); this.z = z } 3 | foobar() { return this.x } 4 | callSuper() { return super.hello() } 5 | } 6 | 7 | class Point2 { 8 | constructor(x, y) { this.x = x; this.y = y } 9 | hello() { return "hello" } 10 | } 11 | 12 | var p = new Point3(1, 2, 3) 13 | p.x //: number 14 | p.hello() //: string 15 | p.callSuper() //: string 16 | p.foobar() //: number 17 | 18 | var pro = {x: 10} 19 | 20 | var obj = { 21 | __proto__: pro, 22 | x: "string", 23 | getSuperX() { return super.x } 24 | } 25 | 26 | obj.x //: string 27 | obj.getSuperX() //: number 28 | -------------------------------------------------------------------------------- /test/cases/symbol.js: -------------------------------------------------------------------------------- 1 | var mySym = Symbol("my sym") 2 | 3 | var obj = { 4 | [mySym]: 22 5 | } 6 | obj[mySym] //: number 7 | 8 | obj[Symbol.iterator] = "hello" 9 | obj[Symbol.iterator] //: string 10 | -------------------------------------------------------------------------------- /test/cases/template.js: -------------------------------------------------------------------------------- 1 | `foo` //: string 2 | var x 3 | `foo${x = 10}bar` //: string 4 | x //: number 5 | 6 | function build(strs, a, b) { 7 | strs //: [string] 8 | a //: number 9 | b //: string 10 | return true 11 | } 12 | 13 | build`foo${10}bar${'hi'}` //: bool 14 | -------------------------------------------------------------------------------- /test/cases/underscore.js: -------------------------------------------------------------------------------- 1 | // environment=underscore 2 | 3 | var cloneObjOrig = {a: 1}, cloneObjCopy = _.clone(cloneObjOrig); 4 | cloneObjOrig.a; //: number 5 | cloneObjCopy.a; //: number 6 | 7 | var extendObj = {a: 1, b: true}; 8 | _.extend(extendObj, {c: ''}, {d: 2}); 9 | extendObj; //:: {a: number, b: bool, c: string, d: number} 10 | 11 | var defaulted = {x: 10}; 12 | _.defaults(defaulted, {y: ""}); 13 | defaulted.x; //: number 14 | defaulted.y; //: string 15 | 16 | _.has({}, "foo"); //: bool 17 | _.isNaN(NaN); //: bool 18 | 19 | var tapped; 20 | _.tap(11, function(a) { tapped = a; }); 21 | tapped; //: number 22 | 23 | _.reduce(["foo", "bar"], function(sum, s) { return sum + s.length; }, 0); //: number 24 | _.map([1, 2, 3], toString); //: [string] 25 | _.each([true, false], function(val, i) { 26 | val; //: bool 27 | i; //: number 28 | this.aa; //: number 29 | }, {aa: 4}); 30 | -------------------------------------------------------------------------------- /test/cases/webpack/component/foo.js: -------------------------------------------------------------------------------- 1 | module.exports = {index: true} 2 | -------------------------------------------------------------------------------- /test/cases/webpack/main.js: -------------------------------------------------------------------------------- 1 | // plugin=webpack {"configPath": "./webpack.config.js"} 2 | 3 | require("foo") //:: {browser: bool} 4 | 5 | require("foo/index") //:: {index: bool} 6 | 7 | require("./component/foo") //:: {index: bool} 8 | 9 | require("component/foo") //:: {index: bool} 10 | 11 | require("xyz") //:: {index: bool} 12 | 13 | require("esnext") //:: {default: {index: bool}} 14 | -------------------------------------------------------------------------------- /test/cases/webpack/node_modules/esnext/index.js: -------------------------------------------------------------------------------- 1 | export default {index: true} 2 | -------------------------------------------------------------------------------- /test/cases/webpack/node_modules/esnext/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "jsnext:main": "index.js" 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/webpack/node_modules/foo/browser.js: -------------------------------------------------------------------------------- 1 | module.exports = {browser: true} 2 | -------------------------------------------------------------------------------- /test/cases/webpack/node_modules/foo/index.js: -------------------------------------------------------------------------------- 1 | module.exports = {index: true} 2 | -------------------------------------------------------------------------------- /test/cases/webpack/node_modules/foo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "browser": "browser.js" 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/webpack/node_modules/modu/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { index: true} 2 | -------------------------------------------------------------------------------- /test/cases/webpack/node_modules/modu/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "index.js" 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/webpack/node_modules/xyz/index.js: -------------------------------------------------------------------------------- 1 | module.exports = {index: false} 2 | -------------------------------------------------------------------------------- /test/cases/webpack/node_modules/xyz/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "index.js" 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/webpack/webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | 3 | module.exports = { 4 | resolve: { 5 | packageMains: ['jsnext:main', 'browser', 'browserify', 'main'], 6 | root: __dirname, 7 | alias: { 8 | xyz: "modu" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/condense.js: -------------------------------------------------------------------------------- 1 | var util = require("./util"); 2 | var tern = require("../lib/tern"), condense = require("../lib/condense"); 3 | var fs = require("fs"), path = require("path"); 4 | require("../plugin/angular"); 5 | require("../plugin/node"); 6 | 7 | var condenseDir = "test/condense"; 8 | function jsonFile(name) { return util.resolve(condenseDir + "/" + name.replace(/\.js$/, ".json")); } 9 | 10 | function runTest(options) { 11 | var server = new tern.Server({ 12 | defs: [util.ecmascript, util.browser], 13 | plugins: options.plugins, 14 | projectDir: util.resolve(condenseDir), 15 | getFile: function(name) { 16 | return fs.readFileSync(path.resolve(condenseDir, name), "utf8"); 17 | } 18 | }); 19 | options.load.forEach(function(file) { 20 | server.addFile(file); 21 | }); 22 | server.flush(function() { 23 | var origins = options.include || options.load; 24 | var condensed = condense.condense(origins, null, {sortOutput: true}); 25 | var out = JSON.stringify(condensed, null, 2); 26 | var expect = fs.readFileSync(jsonFile(origins[0]), "utf8").trim(); 27 | if (out != expect) 28 | return util.failure("condense/" + origins[0] + ": Mismatch in condense output. Got " + 29 | out + "\nExpected " + expect); 30 | 31 | // Test loading the condensed defs. 32 | var server2 = new tern.Server({ 33 | defs: [util.ecmascript, util.browser, condensed], 34 | plugins: options.plugins 35 | }); 36 | server2.flush(function() { 37 | var condensed = condense.condense(origins, null, {sortOutput: true}); 38 | var out = JSON.stringify(condensed, null, 2); 39 | if (out != expect) 40 | util.failure("condense/" + origins[0] + ": Mismatch in condense output after loading defs. Got " + 41 | out + "\nExpected " + expect); 42 | }); 43 | }); 44 | } 45 | 46 | exports.runTests = function(filter) { 47 | function jsFile(f) { 48 | return f + ".js"; 49 | } 50 | function test(options) { 51 | if (typeof options == "string") options = {load: [options]}; 52 | options.load = options.load.map(jsFile); 53 | if (options.include) options.include = options.include.map(jsFile); 54 | if (filter && options.load[0].indexOf(filter) == -1) return; 55 | util.addTest(); 56 | util.addFile(); 57 | runTest(options); 58 | } 59 | 60 | test("basic"); 61 | test("fn"); 62 | test("add_to_old"); 63 | test({load: ["ignore_newer", "extend_foo"], 64 | include: ["ignore_newer"]}); 65 | test("ref_to_old"); 66 | test("ref_in_type"); 67 | test("double_ref"); 68 | test("proto"); 69 | test("generic"); 70 | test("array"); 71 | test("function_prop"); 72 | test("uniontype"); 73 | 74 | test({load: ["node_simple"], plugins: {node: true}}); 75 | test({load: ["node_require_private"], plugins: {node: true}}); 76 | test({load: ["node_fn_export"], plugins: {node: true}}); 77 | test({load: ["node_other_module_type_ref"], include: ["node_other_module_type_ref", "node_export_function_a"], plugins: {node: true}}); 78 | 79 | test({load: ["angular_simple"], plugins: {angular: true}}); 80 | 81 | test({load: ["requirejs_const"], plugins: {requirejs: true}}); 82 | test({load: ["requirejs_primitive"], plugins: {requirejs: true}}); 83 | test({load: ["requirejs_setup"], plugins: {requirejs: true}}); 84 | test({load: ["requirejs_empty_deps"], plugins: {requirejs: true}}); 85 | // TODO(sqs): if load order is reversed, then 86 | // !define.!requirejs.requirejs_dep.a duplicates the definition instead of 87 | // referring to !requirejs.requirejs_const. 88 | test({load: ["requirejs_const", "requirejs_dep"], include: ["requirejs_dep", "requirejs_const"], plugins: {requirejs: true}}); 89 | 90 | test("recursive"); 91 | }; 92 | -------------------------------------------------------------------------------- /test/condense/add_to_old.js: -------------------------------------------------------------------------------- 1 | function Thing() {} 2 | 3 | Element.prototype.foo = new Thing; 4 | -------------------------------------------------------------------------------- /test/condense/add_to_old.json: -------------------------------------------------------------------------------- 1 | { 2 | "!name": "add_to_old.js", 3 | "Element": { 4 | "prototype": { 5 | "foo": { 6 | "!span": "39[2:18]-42[2:21]", 7 | "!type": "+Thing" 8 | } 9 | } 10 | }, 11 | "Thing": { 12 | "!span": "9[0:9]-14[0:14]", 13 | "!type": "fn()" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test/condense/angular_simple.js: -------------------------------------------------------------------------------- 1 | angular.module("foo", []).value("fooVal", {info: "info"}}); 2 | 3 | angular.module("test", ["foo"]) 4 | // Doc for testVal 5 | .factory("testVal", function() { 6 | return "hi"; 7 | }); 8 | -------------------------------------------------------------------------------- /test/condense/angular_simple.json: -------------------------------------------------------------------------------- 1 | { 2 | "!define": { 3 | "!ng": { 4 | "foo": { 5 | "!data": { 6 | "includes": [] 7 | }, 8 | "!proto": "angular.Module.prototype", 9 | "_inject_fooVal": { 10 | "!span": "32[0:32]-40[0:40]", 11 | "info": { 12 | "!span": "43[0:43]-47[0:47]", 13 | "!type": "string" 14 | } 15 | } 16 | }, 17 | "test": { 18 | "!data": { 19 | "includes": [ 20 | "foo" 21 | ] 22 | }, 23 | "!proto": "angular.Module.prototype", 24 | "_inject_testVal": { 25 | "!doc": "Doc for testVal", 26 | "!span": "121[4:9]-130[4:18]", 27 | "!type": "string" 28 | } 29 | } 30 | } 31 | }, 32 | "!name": "angular_simple.js" 33 | } 34 | -------------------------------------------------------------------------------- /test/condense/array.js: -------------------------------------------------------------------------------- 1 | var x = [{a: 10, b: true}]; 2 | -------------------------------------------------------------------------------- /test/condense/array.json: -------------------------------------------------------------------------------- 1 | { 2 | "!define": { 3 | "x.": { 4 | "!span": "9[0:9]-25[0:25]", 5 | "a": { 6 | "!span": "10[0:10]-11[0:11]", 7 | "!type": "number" 8 | }, 9 | "b": { 10 | "!span": "17[0:17]-18[0:18]", 11 | "!type": "bool" 12 | } 13 | } 14 | }, 15 | "!name": "array.js", 16 | "x": { 17 | "!span": "4[0:4]-5[0:5]", 18 | "!type": "[x.]" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/condense/basic.js: -------------------------------------------------------------------------------- 1 | var x = 10; 2 | var y = { 3 | foo: 20, 4 | bar: "hi"; 5 | }; 6 | -------------------------------------------------------------------------------- /test/condense/basic.json: -------------------------------------------------------------------------------- 1 | { 2 | "!name": "basic.js", 3 | "x": { 4 | "!span": "4[0:4]-5[0:5]", 5 | "!type": "number" 6 | }, 7 | "y": { 8 | "!span": "16[1:4]-17[1:5]", 9 | "bar": { 10 | "!span": "35[3:2]-38[3:5]", 11 | "!type": "string" 12 | }, 13 | "foo": { 14 | "!span": "24[2:2]-27[2:5]", 15 | "!type": "number" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/condense/double_ref.js: -------------------------------------------------------------------------------- 1 | var a = {an: "object"}; 2 | var b = a; 3 | -------------------------------------------------------------------------------- /test/condense/double_ref.json: -------------------------------------------------------------------------------- 1 | { 2 | "!name": "double_ref.js", 3 | "a": { 4 | "!span": "4[0:4]-5[0:5]", 5 | "an": { 6 | "!span": "9[0:9]-11[0:11]", 7 | "!type": "string" 8 | } 9 | }, 10 | "b": "a" 11 | } 12 | -------------------------------------------------------------------------------- /test/condense/extend_foo.js: -------------------------------------------------------------------------------- 1 | foo.z = {something: "else"}; 2 | -------------------------------------------------------------------------------- /test/condense/fn.js: -------------------------------------------------------------------------------- 1 | function a(){} 2 | var b = a; 3 | -------------------------------------------------------------------------------- /test/condense/fn.json: -------------------------------------------------------------------------------- 1 | { 2 | "!name": "fn.js", 3 | "a": { 4 | "!span": "9[0:9]-10[0:10]", 5 | "!type": "fn()" 6 | }, 7 | "b": "a" 8 | } 9 | -------------------------------------------------------------------------------- /test/condense/function_prop.js: -------------------------------------------------------------------------------- 1 | foo = function() {}; 2 | foo.s = ''; 3 | -------------------------------------------------------------------------------- /test/condense/function_prop.json: -------------------------------------------------------------------------------- 1 | { 2 | "!name": "function_prop.js", 3 | "foo": { 4 | "!span": "0[0:0]-3[0:3]", 5 | "!type": "fn()", 6 | "s": { 7 | "!span": "25[1:4]-26[1:5]", 8 | "!type": "string" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/condense/generic.js: -------------------------------------------------------------------------------- 1 | function x(y) { return y.bar; } 2 | -------------------------------------------------------------------------------- /test/condense/generic.json: -------------------------------------------------------------------------------- 1 | { 2 | "!name": "generic.js", 3 | "x": { 4 | "!span": "9[0:9]-10[0:10]", 5 | "!type": "fn(y: ?) -> !0.bar" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test/condense/ignore_newer.js: -------------------------------------------------------------------------------- 1 | var foo = { 2 | x: 10, 3 | y: 20 4 | }; 5 | -------------------------------------------------------------------------------- /test/condense/ignore_newer.json: -------------------------------------------------------------------------------- 1 | { 2 | "!name": "ignore_newer.js", 3 | "foo": { 4 | "!span": "4[0:4]-7[0:7]", 5 | "x": { 6 | "!span": "14[1:2]-15[1:3]", 7 | "!type": "number" 8 | }, 9 | "y": { 10 | "!span": "23[2:2]-24[2:3]", 11 | "!type": "number" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/condense/node_export_function_a.js: -------------------------------------------------------------------------------- 1 | exports.a = function(){}; 2 | -------------------------------------------------------------------------------- /test/condense/node_fn_export.js: -------------------------------------------------------------------------------- 1 | module.exports = function(a) { return a; }; 2 | -------------------------------------------------------------------------------- /test/condense/node_fn_export.json: -------------------------------------------------------------------------------- 1 | { 2 | "!define": { 3 | "!modules": { 4 | "node_fn_export`js": { 5 | "!span": "17[0:17]-42[0:42]", 6 | "!type": "fn(a: ?) -> !0" 7 | } 8 | } 9 | }, 10 | "!name": "node_fn_export.js" 11 | } 12 | -------------------------------------------------------------------------------- /test/condense/node_other_module_type_ref.js: -------------------------------------------------------------------------------- 1 | exports.b = require('./node_export_function_a').a; 2 | -------------------------------------------------------------------------------- /test/condense/node_other_module_type_ref.json: -------------------------------------------------------------------------------- 1 | { 2 | "!define": { 3 | "!modules": { 4 | "node_export_function_a`js": { 5 | "!span": "0[0:0]-26[1:0]", 6 | "a": { 7 | "!span": "8[0:8]-9[0:9]", 8 | "!type": "fn()" 9 | } 10 | }, 11 | "node_other_module_type_ref`js": { 12 | "!span": "0[0:0]-51[1:0]", 13 | "b": "!modules.node_export_function_a`js.a" 14 | } 15 | } 16 | }, 17 | "!name": "node_other_module_type_ref.js" 18 | } 19 | -------------------------------------------------------------------------------- /test/condense/node_require_private.js: -------------------------------------------------------------------------------- 1 | // "freelist" could be any other undocumented node.js API module that isn't 2 | // included in the tern node plugin definitions. 3 | require('freelist'); 4 | -------------------------------------------------------------------------------- /test/condense/node_require_private.json: -------------------------------------------------------------------------------- 1 | { 2 | "!name": "node_require_private.js" 3 | } 4 | -------------------------------------------------------------------------------- /test/condense/node_simple.js: -------------------------------------------------------------------------------- 1 | exports.a = 10; 2 | exports.b = function() { return exports.a; }; 3 | -------------------------------------------------------------------------------- /test/condense/node_simple.json: -------------------------------------------------------------------------------- 1 | { 2 | "!define": { 3 | "!modules": { 4 | "node_simple`js": { 5 | "!span": "0[0:0]-62[2:0]", 6 | "a": { 7 | "!span": "8[0:8]-9[0:9]", 8 | "!type": "number" 9 | }, 10 | "b": { 11 | "!span": "24[1:8]-25[1:9]", 12 | "!type": "fn() -> number" 13 | } 14 | } 15 | } 16 | }, 17 | "!name": "node_simple.js" 18 | } 19 | -------------------------------------------------------------------------------- /test/condense/proto.js: -------------------------------------------------------------------------------- 1 | var x = {my: "object"}; 2 | var y = Object.create(x); 3 | y.bar = "extended"; 4 | -------------------------------------------------------------------------------- /test/condense/proto.json: -------------------------------------------------------------------------------- 1 | { 2 | "!name": "proto.js", 3 | "x": { 4 | "!span": "4[0:4]-5[0:5]", 5 | "my": { 6 | "!span": "9[0:9]-11[0:11]", 7 | "!type": "string" 8 | } 9 | }, 10 | "y": { 11 | "!proto": "x", 12 | "!span": "28[1:4]-29[1:5]", 13 | "bar": { 14 | "!span": "52[2:2]-55[2:5]", 15 | "!type": "string" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/condense/recursive.js: -------------------------------------------------------------------------------- 1 | var b = {}; 2 | b[3] = [b[3]]; 3 | -------------------------------------------------------------------------------- /test/condense/recursive.json: -------------------------------------------------------------------------------- 1 | { 2 | "!name": "recursive.js", 3 | "b": { 4 | "!span": "4[0:4]-5[0:5]", 5 | "": { 6 | "!span": "14[1:2]-15[1:3]", 7 | "!type": "[b.]" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/condense/ref_in_type.js: -------------------------------------------------------------------------------- 1 | function out(a) { return a.bar + 10; } 2 | 3 | (function() { 4 | function Foo() { this.bar = 10; } 5 | out(new Foo); 6 | })(); 7 | -------------------------------------------------------------------------------- /test/condense/ref_in_type.json: -------------------------------------------------------------------------------- 1 | { 2 | "!define": { 3 | "out.!0": { 4 | "bar": { 5 | "!span": "78[3:24]-81[3:27]", 6 | "!type": "number" 7 | } 8 | } 9 | }, 10 | "!name": "ref_in_type.js", 11 | "out": { 12 | "!span": "9[0:9]-12[0:12]", 13 | "!type": "fn(a: out.!0) -> number" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test/condense/ref_to_old.js: -------------------------------------------------------------------------------- 1 | var myElement = new Date(); 2 | -------------------------------------------------------------------------------- /test/condense/ref_to_old.json: -------------------------------------------------------------------------------- 1 | { 2 | "!name": "ref_to_old.js", 3 | "myElement": { 4 | "!span": "4[0:4]-13[0:13]", 5 | "!type": "+Date" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test/condense/requirejs_const.js: -------------------------------------------------------------------------------- 1 | define({a: "a"}); 2 | -------------------------------------------------------------------------------- /test/condense/requirejs_const.json: -------------------------------------------------------------------------------- 1 | { 2 | "!define": { 3 | "!requirejs": { 4 | "requirejs_const": { 5 | "!span": "7[0:7]-15[0:15]", 6 | "a": { 7 | "!span": "8[0:8]-9[0:9]", 8 | "!type": "string" 9 | } 10 | } 11 | } 12 | }, 13 | "!name": "requirejs_const.js" 14 | } 15 | -------------------------------------------------------------------------------- /test/condense/requirejs_dep.js: -------------------------------------------------------------------------------- 1 | define(['./requirejs_const'], function(requirejs_const) { 2 | return {a: requirejs_const}; 3 | }); 4 | -------------------------------------------------------------------------------- /test/condense/requirejs_dep.json: -------------------------------------------------------------------------------- 1 | { 2 | "!define": { 3 | "!requirejs": { 4 | "requirejs_const": { 5 | "!span": "7[0:7]-15[0:15]", 6 | "a": { 7 | "!span": "8[0:8]-9[0:9]", 8 | "!type": "string" 9 | } 10 | }, 11 | "requirejs_dep": { 12 | "!span": "67[1:9]-87[1:29]", 13 | "a": "!requirejs.requirejs_const" 14 | } 15 | } 16 | }, 17 | "!name": "requirejs_dep.js" 18 | } 19 | -------------------------------------------------------------------------------- /test/condense/requirejs_empty_deps.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | return {c: "c"}; 3 | }); 4 | -------------------------------------------------------------------------------- /test/condense/requirejs_empty_deps.json: -------------------------------------------------------------------------------- 1 | { 2 | "!define": { 3 | "!requirejs": { 4 | "requirejs_empty_deps": { 5 | "!span": "33[1:9]-41[1:17]", 6 | "c": { 7 | "!span": "34[1:10]-35[1:11]", 8 | "!type": "string" 9 | } 10 | } 11 | } 12 | }, 13 | "!name": "requirejs_empty_deps.js" 14 | } 15 | -------------------------------------------------------------------------------- /test/condense/requirejs_primitive.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | return 1; 3 | }); 4 | -------------------------------------------------------------------------------- /test/condense/requirejs_primitive.json: -------------------------------------------------------------------------------- 1 | { 2 | "!define": { 3 | "!requirejs": { 4 | "requirejs_primitive": "number" 5 | } 6 | }, 7 | "!name": "requirejs_primitive.js" 8 | } 9 | -------------------------------------------------------------------------------- /test/condense/requirejs_setup.js: -------------------------------------------------------------------------------- 1 | define(function() { 2 | return {b: "b"}; 3 | }); 4 | -------------------------------------------------------------------------------- /test/condense/requirejs_setup.json: -------------------------------------------------------------------------------- 1 | { 2 | "!define": { 3 | "!requirejs": { 4 | "requirejs_setup": { 5 | "!span": "29[1:9]-37[1:17]", 6 | "b": { 7 | "!span": "30[1:10]-31[1:11]", 8 | "!type": "string" 9 | } 10 | } 11 | } 12 | }, 13 | "!name": "requirejs_setup.js" 14 | } 15 | -------------------------------------------------------------------------------- /test/condense/uniontype.js: -------------------------------------------------------------------------------- 1 | function x(a) {} 2 | function y() { if (something) return true; else return 100; } 3 | 4 | x("hi"); 5 | x([10]); 6 | y(); 7 | -------------------------------------------------------------------------------- /test/condense/uniontype.json: -------------------------------------------------------------------------------- 1 | { 2 | "!name": "uniontype.js", 3 | "x": { 4 | "!span": "9[0:9]-10[0:10]", 5 | "!type": "fn(a: string|[number])" 6 | }, 7 | "y": { 8 | "!span": "26[1:9]-27[1:10]", 9 | "!type": "fn() -> bool|number" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/data/large.js: -------------------------------------------------------------------------------- 1 | var fs = require("fs"); 2 | var rmrf = require("rimraf"); 3 | var yaml = require("js-yaml"); 4 | var marked = require("marked"); 5 | var Mold = require("mold-template"); 6 | var util = require("./util"); 7 | CodeMirror = require("codemirror/addon/runmode/runmode.node.js"); 8 | 9 | marked.setOptions({highlight: highlightCode, gfm: true}); 10 | 11 | function highlightCode(code, lang) { 12 | if (!lang) return code; 13 | if (!CodeMirror.modes.hasOwnProperty(lang)) { 14 | try { require("codemirror/mode/" + lang + "/" + lang); } 15 | catch(e) { console.log(e.toString());CodeMirror.modes[lang] = false; } 16 | } 17 | if (CodeMirror.modes[lang]) { 18 | var html = ""; 19 | CodeMirror.runMode(code, lang, function(token, style) { 20 | if (style) html += "" + Mold.escapeHTML(token) + ""; 21 | else html += Mold.escapeHTML(token); 22 | }); 23 | return html; 24 | } else return code; 25 | } 26 | 27 | function hasFrontMatter(file) { 28 | var fd = fs.openSync(file, "r"); 29 | var b = new Buffer(4); 30 | var ret = fs.readSync(fd, b, 0, 4, 0) == 4 && b.toString() == "---\n"; 31 | fs.closeSync(fd); 32 | return ret; 33 | } 34 | 35 | function readFrontMatter(file) { 36 | if (/^---\n/.test(file)) { 37 | var end = file.search(/\n---\n/); 38 | if (end != -1) return {front: yaml.load(file.slice(4, end + 1)) || {}, main: file.slice(end + 5)}; 39 | } 40 | return {front: {}, main: file}; 41 | } 42 | 43 | function readPosts(config) { 44 | var posts = []; 45 | fs.readdirSync("_posts/").forEach(function(file) { 46 | var d = file.match(/^(\d{4})-(\d\d?)-(\d\d?)-(.+)\.(md|link)$/); 47 | var split = readFrontMatter(fs.readFileSync("_posts/" + file, "utf8")); 48 | var post = split.front; 49 | post.date = new Date(d[1], d[2] - 1, d[3]); 50 | post.name = d[4]; 51 | if (!post.tags) post.tags = []; 52 | if (!post.tags.forEach && post.tags.split) post.tags = post.tags.split(/\s+/); 53 | if (d[5] == "md") { 54 | post.content = marked(split.main); 55 | post.url = getURL(config, post); 56 | } else if (d[5] == "link") { 57 | var escd = Mold.escapeHTML(post.url); 58 | post.content = "

Read this post at " + escd + ".

"; 59 | post.isLink = true; 60 | } 61 | posts.push(post); 62 | }); 63 | posts.sort(function(a, b){return b.date - a.date;}); 64 | return posts; 65 | } 66 | 67 | function gatherTags(posts) { 68 | var tags = {}; 69 | posts.forEach(function(post) { 70 | if (post.tags) post.tags.forEach(function(tag) { 71 | (tags.hasOwnProperty(tag) ? tags[tag] : (tags[tag] = [])).push(post); 72 | }); 73 | else post.tags = []; 74 | }); 75 | return tags; 76 | } 77 | 78 | var defaults = { 79 | postLink: "${name}.html" 80 | }; 81 | 82 | function readConfig() { 83 | var config = (util.exists("_config.yml") && yaml.load(fs.readFileSync("_config.yml", "utf8"))) || {}; 84 | for (var opt in defaults) if (defaults.hasOwnProperty(opt) && !config.hasOwnProperty(opt)) 85 | config[opt] = defaults[opt]; 86 | return config; 87 | } 88 | 89 | function getURL(config, post) { 90 | var link = config.postLink; 91 | for (var prop in post) link = link.replace("${" + prop + "}", post[prop]); 92 | return link; 93 | } 94 | 95 | function ensureDirectories(path) { 96 | var parts = path.split("/"), cur = ""; 97 | for (var i = 0; i < parts.length - 1; ++i) { 98 | cur += parts[i] + "/"; 99 | if (!util.exists(cur, true)) fs.mkdirSync(cur); 100 | } 101 | } 102 | 103 | function prepareIncludes(ctx) { 104 | if (!util.exists("_includes/", true)) return; 105 | fs.readdirSync("_includes/").forEach(function(file) { 106 | Mold.define(file.match(/^(.*?)\.[^\.]+$/)[1], 107 | Mold.bake(fs.readFileSync("_includes/" + file, "utf8"), ctx)); 108 | }); 109 | } 110 | 111 | var layouts = {}; 112 | function getLayout(name, ctx) { 113 | if (name.indexOf(".") == -1) name = name + ".html"; 114 | if (layouts.hasOwnProperty(name)) return layouts[name]; 115 | var tmpl = Mold.bake(fs.readFileSync("_layouts/" + name, "utf8"), ctx); 116 | tmpl.filename = name; 117 | layouts[name] = tmpl; 118 | return tmpl; 119 | } 120 | 121 | function generate() { 122 | var config = readConfig(), posts = readPosts(config); 123 | var ctx = {site: {posts: posts, tags: gatherTags(posts), config: config}, 124 | dateFormat: require("dateformat")}; 125 | prepareIncludes(ctx); 126 | if (util.exists("_site", true)) rmrf.sync("_site"); 127 | posts.forEach(function(post) { 128 | if (post.isLink) return; 129 | var path = "_site/" + post.url; 130 | ensureDirectories(path); 131 | fs.writeFileSync(path, getLayout(post.layout || "post.html", ctx)(post), "utf8"); 132 | }); 133 | function walkDir(dir) { 134 | fs.readdirSync(dir).forEach(function(fname) { 135 | if (/^[_\.]/.test(fname)) return; 136 | var file = dir + fname; 137 | if (fs.statSync(file).isDirectory()) { 138 | walkDir(file + "/"); 139 | } else { 140 | var out = "_site/" + file; 141 | ensureDirectories(out); 142 | if (/\.md$/.test(fname) && hasFrontMatter(file)) { 143 | var split = readFrontMatter(fs.readFileSync(file, "utf8")); 144 | var doc = split.front; 145 | var layout = getLayout(doc.layout || "default.html", ctx); 146 | doc.content = marked(split.main); 147 | doc.name = fname.match(/^(.*?)\.[^\.]+$/)[1]; 148 | doc.url = file; 149 | out = out.replace(/\.md$/, layout.filename.match(/(\.\w+|)$/)[1]); 150 | fs.writeFileSync(out, layout(doc), "utf8"); 151 | } else { 152 | util.copyFileSync(file, out); 153 | } 154 | } 155 | }); 156 | } 157 | walkDir("./"); 158 | } 159 | 160 | generate(); 161 | 162 | // Long line 163 | function highlightCode2(code, lang) { if (!lang) return code; if (!CodeMirror.modes.hasOwnProperty(lang)) { try { require("codemirror/mode/" + lang + "/" + lang); } catch(e) { console.log(e.toString());CodeMirror.modes[lang] = false; } } if (CodeMirror.modes[lang]) { var html = ""; CodeMirror.runMode(code, lang, function(token, style) { if (style) html += "" + Mold.escapeHTML(token) + ""; else html += Mold.escapeHTML(token); }); return html; } else return code; } function hasFrontMatter2(file) { var fd = fs.openSync(file, "r"); var b = new Buffer(4); var ret = fs.readSync(fd, b, 0, 4, 0) == 4 && b.toString() == "---\n"; fs.closeSync(fd); return ret; } function readFrontMatter2(file) { if (/^---\n/.test(file)) { var end = file.search(/\n---\n/); if (end != -1) return {front: yaml.load(file.slice(4, end + 1)) || {}, main: file.slice(end + 5)}; } return {front: {}, main: file}; } function readPosts2(config) { var posts = []; fs.readdirSync("_posts/").forEach(function(file) { var d = file.match(/^(\d{4})-(\d\d?)-(\d\d?)-(.+)\.(md|link)$/); var split = readFrontMatter(fs.readFileSync("_posts/" + file, "utf8")); var post = split.front; post.date = new Date(d[1], d[2] - 1, d[3]); post.name = d[4]; if (!post.tags) post.tags = []; if (!post.tags.forEach && post.tags.split) post.tags = post.tags.split(/\s+/); if (d[5] == "md") { post.content = marked(split.main); post.url = getURL(config, post); } else if (d[5] == "link") { var escd = Mold.escapeHTML(post.url); post.content = "

Read this post at " + escd + ".

"; post.isLink = true; } posts.push(post); }); posts.sort(function(a, b){return b.date - a.date;}); return posts; } 164 | -------------------------------------------------------------------------------- /test/fragments.js: -------------------------------------------------------------------------------- 1 | var util = require("./util"); 2 | var tern = require("../lib/tern"); 3 | var fs = require("fs"); 4 | 5 | var file = fs.readFileSync(util.resolve("test/data/large.js"), "utf8"); 6 | 7 | var server = new tern.Server({defs: [util.ecmascript]}); 8 | 9 | var curTest; 10 | function fail(msg) { throw curTest + ": " + msg; } 11 | function eq(a, b, msg) { if (a != b) fail(msg || a + " != " + b); } 12 | 13 | exports.runTests = function(filter) { 14 | var added = false; 15 | function test(name, start, text, query, check) { 16 | name = "fragments/" + name; 17 | if (filter && name.indexOf(filter) == -1) return; 18 | if (!added) { 19 | server.request({files: [{type: "full", name: "file", text: file}]}, function(){}); 20 | util.addFile(); 21 | added = true; 22 | } 23 | util.addTest(); 24 | curTest = name; 25 | if (typeof text == "number") text = file.slice(start, text); 26 | query.file = "#0"; 27 | var fdata = {type: "part", name: "file", text: text, offset: start}; 28 | server.request({files: [fdata], query: query}, function(err, data) { 29 | try { 30 | if (err) fail(err); 31 | else check(data); 32 | } catch (e) { 33 | util.failure(String(e)); 34 | } 35 | }); 36 | } 37 | 38 | test("simple", 864, 1065, {type: "definition", end: 59}, 39 | function(data) { eq(data.start, 888); }); 40 | test("only_body", 895, 1064, {type: "definition", end: 28}, 41 | function(data) { eq(data.start, 888); }); 42 | test("replace_body", 895, "\n file", {type: "definition", end: 7}, 43 | function(data) { eq(data.start, 888); }); 44 | 45 | test("long_line", 6141, 6774, {type: "type", end: 603}, 46 | function(data) { eq(data.type, "bool"); }); 47 | test("long_line_replace", 6141, "var x = 'hi'; x;", {type: "type", end: 15}, 48 | function(data) { eq(data.type, "string"); }); 49 | 50 | test("redefine", 2444, "var defaults = 900; // and a long comment to cover the whole exprssion\n", 51 | {type: "type", end: 0, variable: "defaults"}, 52 | function(data) { eq(data.type, "number"); }); 53 | 54 | test("find_def_in_fragment", 3420, file.slice(3424, 5103), 55 | {type: "definition", end: 1280}, 56 | function(data) { eq(data.start, 3429); }); 57 | }; 58 | -------------------------------------------------------------------------------- /test/reload.js: -------------------------------------------------------------------------------- 1 | var tern = require("../lib/tern"); 2 | var util = require("./util"); 3 | 4 | var tests = [], added = false; 5 | function test(name, f) { 6 | tests.push(function(filter) { 7 | if (filter && name.indexOf(filter) == -1) return; 8 | if (!added) { util.addFile(); added = true; } 9 | util.addTest(); 10 | 11 | f(new tern.Server({defs: [util.ecmascript], debug: true})); 12 | }); 13 | } 14 | 15 | exports.runTests = function(filter) { 16 | tests.forEach(function(test) { test(filter); }); 17 | }; 18 | 19 | test("reloadCall", function(server) { 20 | server.addFile("call.js", "myfun('2');"); 21 | server.addFile("fun.js", "function myfun(x) {\n x;\n}"); 22 | server.flush(function() { 23 | var query = {files: [{name: "fun.js", type: "full", text: "function myfun(xy) {\n xy;\n}"}], 24 | query: {type: "type", end: {line: 1, ch: 4}, file: "fun.js"}}; 25 | server.request(query, function(err, data) { 26 | if (err) return util.failure(err); 27 | if (data.type != "string") util.failure("reloadCall: Reloading function cleared argument type"); 28 | }); 29 | }); 30 | }); 31 | 32 | test("reloadProps", function(server) { 33 | server.addFile("obj.js", "var o = {};\no.id = 1234;\no.name = 'test';"); 34 | server.flush(function() { 35 | var query = {files: [{name: "obj.js", type: "full", text: "var o = {};\no.kapow = 1234;\no.name = 'test';\no."}], 36 | query: {type: "completions", end: {line: 3, ch: 2}, file: "obj.js"}}; 37 | server.request(query, function(err, data) { 38 | if (err) return util.failure(err); 39 | var compls = data.completions; 40 | compls.sort(); 41 | if (compls.join() != "kapow,name") util.failure("reloadProps: Reloading properties failed (" + compls + ")"); 42 | }); 43 | }); 44 | }); 45 | 46 | test("reloadVar", function(server) { 47 | server.addFile("a.js", "var x = 1;"); 48 | server.flush(function() { 49 | server.request({files: [{name: "a.js", type: "full", text: "var x = 'hi';"}], 50 | query: {type: "type", end: {line: 0, ch: 5}, file: "a.js"}}, function(err, data) { 51 | 52 | if (err) return util.failure(err); 53 | if (data.type != "string") 54 | util.failure("reloadVar: var did not get new string type (" + data.type + ")"); 55 | }); 56 | }); 57 | }); 58 | 59 | test("reloadForeignProp", function(server) { 60 | server.addFile("a.js", "Date.foo = 100;"); 61 | server.flush(function() { 62 | server.request({files: [{name: "a.js", type: "full", text: "Date.foo"}], 63 | query: {type: "type", end: {line: 0, ch: 8}, file: "a.js"}}, function(err, data) { 64 | if (err) return util.failure(err); 65 | if (data.type != "?") 66 | util.failure("reloadForeignProp: reload did not clear Date.foo (" + data.type + ")"); 67 | }); 68 | }); 69 | }); 70 | 71 | test("reloadCalledForeignProp", function(server) { 72 | server.addFile("a.js", "Date.foo = function(a) { return a; };"); 73 | server.addFile("b.js", "Date.foo('hi');"); 74 | server.flush(function() { 75 | server.request({files: [{name: "a.js", type: "full", text: "Date.foo = function(a) { return a + 1; };"}], 76 | query: {type: "type", end: {line: 0, ch: 33}, file: "a.js"}}, function(err, data) { 77 | if (err) return util.failure(err); 78 | if (data.type != "string") 79 | util.failure("reloadCalledForeignProp: reload cleared argument type for Date.foo (" + data.type + ")"); 80 | }); 81 | }); 82 | }); 83 | 84 | test("reloadDelFile", function(server) { 85 | server.addFile("a.js", "function foobar() { return 10; }"); 86 | server.flush(function() { 87 | server.delFile("a.js"); 88 | server.request({files: [{name: "b.js", type: "full", text: "foobar"}], 89 | query: {type: "type", end: {line: 0, ch: 6}, file: "b.js"}}, function(err, data) { 90 | if (err) return util.failure(err); 91 | if (data.type != "?") 92 | util.failure("reloadDelFile: deleting a file did not clear the variable it created (" + data.type + ")"); 93 | }); 94 | }); 95 | }); 96 | -------------------------------------------------------------------------------- /test/runcases.js: -------------------------------------------------------------------------------- 1 | var fs = require("fs"), path = require("path"); 2 | var infer = require("../lib/infer"); 3 | var tern = require("../lib/tern"); 4 | var acorn = require("acorn"); 5 | var walk = require("acorn-walk"); 6 | require("../plugin/requirejs.js"); 7 | require("../plugin/node.js"); 8 | require("../plugin/es_modules.js"); 9 | require("../plugin/doc_comment.js"); 10 | require("../plugin/angular.js"); 11 | require("../plugin/complete_strings.js"); 12 | require("../plugin/webpack.js"); 13 | var util = require("./util"); 14 | 15 | var defData = { 16 | browser: util.browser, 17 | jquery: util.jquery, 18 | underscore: util.underscore 19 | }; 20 | 21 | function getDefs(text) { 22 | var spec = /\/\/ environment=(\w+)\n/g, m, defs = [util.ecmascript]; 23 | while (m = spec.exec(text)) { 24 | var data = defData[m[1]]; 25 | if (!data) throw new Error("Unknown environment: " + m[1]); 26 | defs.push(data); 27 | } 28 | return defs; 29 | } 30 | 31 | function getPlugins(text) { 32 | var spec = /\/\/ plugin=(\w+)(?: (.*))?\n/g, m, plugins = {doc_comment: true}; 33 | while (m = spec.exec(text)) { 34 | if (m[2]) { 35 | var options = JSON.parse(m[2]); 36 | if (options) 37 | plugins[m[1]] = options; 38 | else 39 | delete plugins[m[1]]; 40 | } else { 41 | plugins[m[1]] = {}; 42 | if (m[1] == "node") plugins.modules = {modules: nodeModules}; 43 | } 44 | } 45 | return plugins; 46 | } 47 | 48 | fs.readdirSync(util.resolve("test/cases/defs")).forEach(function(name) { 49 | if (/\.json$/.test(name)) 50 | defData[path.basename(name, ".json")] = 51 | JSON.parse(fs.readFileSync(util.resolve("test/cases/defs/" + name), "utf8")); 52 | }); 53 | 54 | var nodeModules = {}; 55 | fs.readdirSync(util.resolve("test/cases/node_modules")).forEach(function(name) { 56 | if (/\.json$/.test(name)) 57 | nodeModules[path.basename(name, ".json")] = 58 | JSON.parse(fs.readFileSync(util.resolve("test/cases/node_modules/" + name), "utf8")); 59 | }); 60 | 61 | function serverOptions(context, text) { 62 | return { 63 | defs: getDefs(text), 64 | getFile: function(name) { return fs.readFileSync(path.resolve(context, name), "utf8"); }, 65 | debug: true, 66 | projectDir: context, 67 | plugins: getPlugins(text) 68 | }; 69 | } 70 | 71 | exports.runTests = function(filter, caseDir) { 72 | caseDir = caseDir || util.resolve("test/cases"); 73 | fs.readdirSync(caseDir).forEach(function(name) { 74 | if (filter && name.indexOf(filter) == -1) return; 75 | 76 | var fname = name, context = caseDir; 77 | if (fs.statSync(path.resolve(context, name)).isDirectory()) { 78 | if (name == "node_modules" || name == "defs") return; 79 | context = path.join(context, name); 80 | fname = "main.js"; 81 | } 82 | if (!/\.js$/.test(fname)) return; 83 | util.addFile(); 84 | 85 | var text = fs.readFileSync(path.join(context, fname), "utf8"), m; 86 | var server = new tern.Server(serverOptions(context, text)); 87 | server.addFile(fname); 88 | var ast = server.files[0].ast; 89 | 90 | if (m = text.match(/\/\/ loadfiles=\s*(.*)\s*\n/)) 91 | m[1].split(/,\s*/g).forEach(function(f) {server.addFile(f);}); 92 | 93 | var typedef = /\/\/(<)?(\+\??|:\?|::?|doc\+?:|loc:|refs:|origin:|exports:) *([^\r\n]*)/g; 94 | function fail(m, str) { 95 | util.failure(name + ", line " + acorn.getLineInfo(text, m.index).line + ": " + str); 96 | } 97 | 98 | while (m = typedef.exec(text)) (function(m) { 99 | var args = m[3], kind = m[2], directlyHere = m[1]; 100 | util.addTest(); 101 | if (kind == "+" || kind == "+?") { // Completion test 102 | var columnInfo = /\s*@(\d+)$/.exec(args), pos = m.index; 103 | if (columnInfo) { 104 | var line = acorn.getLineInfo(text, m.index).line; 105 | pos = {line: line - 1, ch: Number(columnInfo[1]) - 1}; 106 | args = args.slice(0, columnInfo.index); 107 | } else { 108 | while (/[\s;]/.test(text.charAt(pos - 1))) --pos; 109 | if (/['"]/.test(text.charAt(pos - 1))) --pos; 110 | } 111 | var query = {type: "completions", end: pos, file: fname, guess: kind == "+?"}; 112 | var andOthers = /,\s*\.\.\.$/.test(args); 113 | if (andOthers) args = args.slice(0, args.lastIndexOf(",")); 114 | var parts = args ? args.split(/\s*,\s*/) : []; 115 | server.request({query: query}, function(err, resp) { 116 | if (err) throw err; 117 | var match = andOthers || parts.length == resp.completions.length; 118 | for (var i = 0; i < parts.length && match; ++i) 119 | if (resp.completions.indexOf(parts[i]) < 0) match = false; 120 | if (!match) 121 | fail(m, "Completion set failed at hint: " + parts[i - 1] + 122 | "\n got: " + resp.completions.join(", ") + "\n wanted: " + args); 123 | }); 124 | } else if (kind == "exports:") { 125 | server.request({query: {type: "exports", file: fname}}, function(err, resp) { 126 | if (err) throw err; 127 | if (resp.type != args) fail(m, "Export type failed. Got:\n " + resp.type + "\nwanted:\n " + args); 128 | }); 129 | } else { 130 | var start, end; 131 | if (directlyHere) { 132 | for (end = m.index; end && /[\s:,;]/.test(text.charAt(end - 1)); --end) {} 133 | start = null; 134 | } else { 135 | var expr = walk.findNodeBefore(ast, m.index, "Expression"); 136 | if (!expr) { 137 | fail(m, "No expresion found"); 138 | return; 139 | } 140 | start = expr.node.start; end = expr.node.end; 141 | } 142 | var query = {type: /doc/.test(kind) ? "documentation" : kind == "loc:" ? "definition" : kind == "refs:" ? "refs" : "type", 143 | start: start, end: end, 144 | docFormat: kind == "doc+:" ? "full" : null, 145 | file: fname, 146 | depth: kind == "::" ? 5 : null, 147 | lineCharPositions: true}; 148 | server.request({query: query}, function(err, resp) { 149 | if (err) throw err; 150 | 151 | if (/doc/.test(kind)) { // Docstring test 152 | var expected = args.replace(/\\n/g, "\n"); 153 | if (resp.doc != expected) { 154 | fail(m, "Found " + (resp.doc ? "docstring\n " + resp.doc + "\n" : "no docstring ") + 155 | "instead of expected docstring\n " + expected); 156 | } 157 | } else if (kind == "loc:") { // Definition finding test 158 | var pos = args.match(/^\s*(\d+),\s*(\d+)(?:,\s*([^,]+))?/), line = Number(pos[1]), col = Number(pos[2]), file = pos[3]; 159 | var actualLoc = (file ? resp.origin + ":" : "") + (resp.start.line + 1) + ":" + resp.start.ch; 160 | var expectedLoc = (file ? file + ":" : "") + line + ":" + col; 161 | if (!resp.start) 162 | fail(m, "Did not find a definition (expected " + expectedLoc + ")."); 163 | else if (resp.start.line + 1 != line || resp.start.ch != col || (file && file !== resp.origin)) 164 | fail(m, "Found definition at " + actualLoc + 165 | " instead of expected " + expectedLoc); 166 | } else if (kind == "origin:") { // Origin finding test 167 | if (resp.origin != args) 168 | fail(m, "Found origin\n " + resp.origin + "\ninstead of expected origin\n " + args); 169 | } else if (kind == "refs:") { // Reference finding test 170 | var pos = /\s*(\d+),\s*(\d+)/g, mm; 171 | while (mm = pos.exec(args)) { 172 | var line = Number(mm[1]), col = Number(mm[2]), found = false; 173 | for (var i = 0; i < resp.refs.length; ++i) { 174 | var ref = resp.refs[i]; 175 | if (ref.start.line + 1 == line && ref.start.ch == col) { found = true; break; } 176 | } 177 | if (!found) fail(m, "Did not find reference at line " + line + ", column " + col + "."); 178 | } 179 | } else { // Type test 180 | var type = resp.guess && kind != ":?" ? "?" : resp.type || "?"; 181 | if (type != args) 182 | fail(m, "Expression has type\n " + type + "\ninstead of expected type\n " + args); 183 | } 184 | }); 185 | } 186 | })(m); 187 | }); 188 | }; 189 | -------------------------------------------------------------------------------- /test/timeout.js: -------------------------------------------------------------------------------- 1 | var tern = require("../lib/tern"); 2 | var infer = require("../lib/infer"); 3 | var util = require("./util"); 4 | var fs = require("fs"); 5 | 6 | exports.runTests = function(filter) { 7 | if (filter && "timeout".indexOf(filter) == -1) return; 8 | 9 | util.addFile(); 10 | util.addTest(); 11 | var server = new tern.Server({}); 12 | var file = fs.readFileSync(util.resolve("lib/infer.js"), "utf8"); 13 | try { 14 | server.request({timeout: 10, 15 | files: [{type: "full", name: "infer.js", text: file}], 16 | query: {type: "type", end: 0, file: "infer.js"}}, function(err, result) { 17 | if (!err || !(err instanceof infer.TimedOut)) 18 | util.failure("timeout: failed to time out"); 19 | }); 20 | } catch(e) { 21 | util.failure("timeout: exception thrown: " + e.stack); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /test/util.js: -------------------------------------------------------------------------------- 1 | var fs = require("fs"), path = require("path"); 2 | 3 | var projectDir = path.resolve(__dirname, ".."); 4 | exports.resolve = function(pth) { return path.resolve(projectDir, pth); }; 5 | 6 | exports.ecmascript = JSON.parse(fs.readFileSync(exports.resolve("defs/ecmascript.json")), "utf8"); 7 | exports.browser = JSON.parse(fs.readFileSync(exports.resolve("defs/browser.json")), "utf8"); 8 | exports.jquery = JSON.parse(fs.readFileSync(exports.resolve("defs/jquery.json")), "utf8"); 9 | exports.underscore = JSON.parse(fs.readFileSync(exports.resolve("defs/underscore.json")), "utf8"); 10 | 11 | var files = 0, tests = 0, failed = 0; 12 | 13 | exports.addFile = function() { ++files; }; 14 | exports.addTest = function() { ++tests; }; 15 | 16 | exports.failure = function(message) { 17 | console.log(message); 18 | ++failed; 19 | }; 20 | 21 | exports.hasFailed = function() { return failed > 0; }; 22 | 23 | process.on("exit", function() { 24 | console.log("Ran " + tests + " tests from " + files + " files."); 25 | console.log(failed ? failed + " failures!" : "All passed."); 26 | }); 27 | --------------------------------------------------------------------------------