├── .eslintrc.yml ├── .gitignore ├── .npmignore ├── .travis.yml ├── AUTHORS ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── bin ├── jp-coffeescript-console.js ├── jp-coffeescript-install.js ├── jp-coffeescript-notebook.js ├── jp-coffeescript.js └── rc.js ├── docs ├── bin_jp-coffeescript.js.html ├── bin_rc.js.html ├── fonts │ ├── OpenSans-Bold-webfont.eot │ ├── OpenSans-Bold-webfont.svg │ ├── OpenSans-Bold-webfont.woff │ ├── OpenSans-BoldItalic-webfont.eot │ ├── OpenSans-BoldItalic-webfont.svg │ ├── OpenSans-BoldItalic-webfont.woff │ ├── OpenSans-Italic-webfont.eot │ ├── OpenSans-Italic-webfont.svg │ ├── OpenSans-Italic-webfont.woff │ ├── OpenSans-Light-webfont.eot │ ├── OpenSans-Light-webfont.svg │ ├── OpenSans-Light-webfont.woff │ ├── OpenSans-LightItalic-webfont.eot │ ├── OpenSans-LightItalic-webfont.svg │ ├── OpenSans-LightItalic-webfont.woff │ ├── OpenSans-Regular-webfont.eot │ ├── OpenSans-Regular-webfont.svg │ └── OpenSans-Regular-webfont.woff ├── global.html ├── global.html#Context ├── index.html ├── jp-coffeescript.js.html ├── lib_kernel.js.html ├── lib_kernel.spec.js.html ├── scripts │ ├── linenumber.js │ └── prettify │ │ ├── Apache-License-2.0.txt │ │ ├── lang-css.js │ │ └── prettify.js └── styles │ ├── jsdoc-default.css │ ├── prettify-jsdoc.css │ └── prettify-tomorrow.css ├── images ├── logo-32x32.png ├── logo-64x64.png └── nodejs │ ├── LICENSE │ ├── README.md │ ├── js-green-32x32.png │ ├── js-green-64x64.png │ └── js-green.svg ├── lib ├── kernel.js └── kernel.spec.js └── package.json /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | root: true 2 | 3 | extends: 4 | - eslint:recommended 5 | 6 | parserOptions: 7 | ecmaVersion: 5 8 | 9 | env: 10 | node: true 11 | mocha: true 12 | 13 | rules: 14 | # indent and spacing 15 | indent: [error, 4] 16 | 17 | array-bracket-spacing: [error, never] 18 | arrow-spacing: 19 | - error 20 | - before: true 21 | after: true 22 | block-spacing: [error, always] 23 | comma-spacing: 24 | - error 25 | - after: true 26 | computed-property-spacing: [error, never] 27 | generator-star-spacing: 28 | - error 29 | - before: true 30 | after: false 31 | keyword-spacing: 32 | - error 33 | - before: true 34 | after: true 35 | no-irregular-whitespace: error 36 | no-mixed-spaces-and-tabs: error 37 | no-multi-spaces: 38 | - error 39 | - exceptions: 40 | Property: true 41 | VariableDeclarator: true 42 | ImportDeclaration: true 43 | no-regex-spaces: error 44 | no-trailing-spaces: error 45 | no-whitespace-before-property: error 46 | object-curly-spacing: [error, never] 47 | rest-spread-spacing: [error, never] 48 | semi-spacing: 49 | - error 50 | - before: false 51 | after: true 52 | space-before-blocks: [error, always] 53 | space-before-function-paren: [error, never] 54 | space-in-parens: [error, never] 55 | space-infix-ops: 56 | - error 57 | - int32Hint: false 58 | space-unary-ops: 59 | - error 60 | - words: true 61 | nonwords: false 62 | spaced-comment: [error, always] 63 | template-curly-spacing: [error, never] 64 | yield-star-spacing: 65 | - error 66 | - before: false 67 | after: true 68 | 69 | # other rules 70 | comma-dangle: off 71 | 72 | complexity: error 73 | 74 | max-len: 75 | - error 76 | - 80 77 | 78 | no-console: off 79 | 80 | no-empty: off 81 | 82 | no-unused-vars: 83 | - error 84 | - args: none 85 | 86 | no-var: off 87 | 88 | one-var: off 89 | 90 | quotes: [error, double] 91 | 92 | semi: error 93 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | misc/ 3 | node_modules/ 4 | npm-debug.log 5 | .ipynb_checkpoints/ 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | docs/ 2 | test/ 3 | 4 | *.swp 5 | misc/ 6 | node_modules/ 7 | npm-debug.log 8 | .ipynb_checkpoints/ 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | os: linux 3 | language: node_js 4 | node_js: 5 | - "6" 6 | - "8" 7 | - "10" 8 | - "12" 9 | - "14" 10 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of authors for copyright purposes. 2 | 3 | # Names should be added to this file as 4 | # Name or Organization [ ...] 5 | # The email address is not required for organizations. 6 | 7 | # Please keep the list sorted. 8 | 9 | Benjamin Abel 10 | Kevin Kwok 11 | Min RK 12 | Nicolas Riesco 13 | Will Whitney 14 | Slava Ganzin 15 | Kelvin Ng 16 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | First of all, thank you for taking the time to contribute. 4 | 5 | Here, you will find relevant information for contributing to this project. 6 | 7 | ## Issue tracker 8 | 9 | Please, feel free to use the [issue 10 | tracker](https://github.com/n-riesco/jp-coffeescript/issues) to report any 11 | problems you encounter or any enhancements you would like to see implemented. To 12 | facilitate the process of fixing a problem, please, include the following 13 | information in your report: 14 | 15 | - jp-CoffeeScript version. Please, run the command: 16 | 17 | ```sh 18 | jp-coffee --version 19 | ``` 20 | 21 | - npm version: 22 | 23 | ```sh 24 | npm version 25 | ``` 26 | 27 | - IPython version: 28 | 29 | ```sh 30 | ipython --version 31 | ``` 32 | 33 | - Operating system. In most modern linux distributions, it is enough to run: 34 | 35 | ```sh 36 | lsb_release -sd 37 | ``` 38 | 39 | ## Code contributions 40 | 41 | - Please, open an issue in the [issue 42 | tracker](https://github.com/n-riesco/jp-coffeescript/issues). 43 | 44 | - Pull requests will be distributed under the terms in the LICENSE file. Hence, 45 | before accepting any pull requests, it is important that the copyright holder 46 | of a pull request acknowledges their consent. To express this consent, please, 47 | ensure the AUTHORS file has been updated accordingly. 48 | 49 | ## Coding guidelines 50 | 51 | - For the sake of readability, please, ensure the coding style of your pull 52 | requests is consistent with this project: lowerCamelCaseNaming, 53 | CONSTANTS_NAMING, 4-space indent, collapsed brackets... 54 | 55 | - The IPython protocol uses underscores (`_`) in their the naming convention (as 56 | recommended in [PEP8](https://www.python.org/dev/peps/pep-0008/)). For these 57 | names, I find more readable to keep the original naming (although, if possible 58 | limited to a local scope). 59 | 60 | - The source code in IJavascript is annotated using 61 | [JSDoc](https://github.com/jsdoc3/jsdoc). The generated documentation can be 62 | found [here](http://n-riesco.github.io/ijavascript/jsdoc). 63 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Unless otherwise indicated, the software in this project is made available under 2 | the BSD 3-Clause License. 3 | 4 | Copyright (c) 2015, Nicolas Riesco and others as credited in the AUTHORS file 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without modification, 8 | are permitted provided that the following conditions are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 13 | 2. Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation and/or 15 | other materials provided with the distribution. 16 | 17 | 3. Neither the name of the copyright holder nor the names of its contributors 18 | may be used to endorse or promote products derived from this software without 19 | specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 25 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 28 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jp-CoffeeScript: A CoffeeScript Kernel for the Jupyter Notebook 2 | 3 | jp-CoffeeScript is an [`npm` package](https://www.npmjs.com/) that implements a 4 | CoffeeScript kernel for the [Jupyter notebook](http://jupyter.org/)). A Jupyter 5 | notebook combines the creation of rich-text documents (including equations, 6 | plots and videos) with the execution of code in a number of programming 7 | languages. 8 | 9 | The execution of code is carried out by means of a kernel that implements the 10 | [Jupyter messaging 11 | protocol](http://ipython.org/ipython-doc/stable/development/messaging.html). 12 | There are kernels available for [Python](http://ipython.org/notebook.html), 13 | [Julia](https://github.com/JuliaLang/IJulia.jl), 14 | [Ruby](https://github.com/minad/iruby), 15 | [Haskell](https://github.com/gibiansky/IHaskell) and [many other 16 | languages](https://github.com/ipython/ipython/wiki/IPython-kernels-for-other-languages). 17 | 18 | 19 | ## Proof-of-Concept and Goals 20 | 21 | jp-CoffeeScript came to existence prompted by a number of requests from 22 | [IJavascript](http://n-riesco.github.io/ijavascript) users. See [this 23 | issue](https://github.com/n-riesco/nel/issues/1) for further details. 24 | 25 | By publishing jp-CoffeeScript I'm seeking to: 26 | 27 | - provide users with a "usable" CoffeeScript kernel that with some support may 28 | become a fully featured kernel, 29 | 30 | - reuse the IJavascript code and develop a Node.js library to implement Jupyter 31 | kernels for other languages. 32 | 33 | 34 | ## Installation 35 | 36 | Please, refer to the [installation notes for 37 | IJavascript](http://n-riesco.github.io/ijavascript/doc/install.md.html). 38 | 39 | For example, in Ubuntu 16.04, you can run: 40 | 41 | ```sh 42 | sudo apt-get install nodejs-legacy npm ipython ipython-notebook 43 | sudo npm install -g jp-coffeescript 44 | ``` 45 | 46 | ## Usage 47 | 48 | jp-CoffeeScript provides 5 executables: `jp-coffee-install`, 49 | `jp-coffee-notebook`, `jp-coffee-console`, `jp-coffee-kernel` and `jp-coffee`. 50 | Their purpose and basic use is described in the sections below. Please, refer to 51 | the [usage notes](http://n-riesco.github.io/ijavascript/doc/usage.md.html) for 52 | further details. 53 | 54 | 55 | ### `jp-coffee-install`: jp-CoffeeScript kernel spec installer 56 | 57 | `jp-coffee-install` registers the jp-CoffeeScript kernel with Jupyter, so that 58 | other tools (e.g. the Jupyter notebook) can invoke it. The following command 59 | flags are recognised: 60 | 61 | ``` 62 | --debug enable debug messages 63 | --help show this help 64 | --hide-undefined do not show undefined results 65 | --install=[local|global] install kernel for current user or globally 66 | --protocol=version set messaging protocol version, e.g. 5.0 67 | --show-undefined show undefined results 68 | --spec-path=[none|full] set whether kernel spec uses full paths 69 | --startup-script=path run script on kernel startup 70 | (path can be a file or a folder) 71 | --version show kernel version 72 | --versions show kernel and library versions 73 | --working-dir=path set kernel working directory 74 | (default = current working directory) 75 | ``` 76 | 77 | 78 | ### `jp-coffee-notebook`: jp-CoffeeScript notebook 79 | 80 | After running `jp-coffee-install`, Jupyter notebook users can invoke the Jupyter 81 | notebook as usual. `jp-coffee-notebook` is provided for convenience to users of 82 | the IPython notebook prior to version 3. `jp-coffee-notebook` is a wrapper 83 | around `ipython notebook`. It extends the command flags accepted by `ipython 84 | notebook` with the following: 85 | 86 | ``` 87 | --help show jp-CoffeeScript and notebook help 88 | --jp-debug enable debug messages 89 | --jp-help show this help 90 | --jp-hide-undefined do not show undefined results 91 | --jp-install=[local|global] install kernel for current user or globally 92 | --jp-protocol=version set protocol version, e.g. 5.0 93 | --jp-show-undefined show undefined results 94 | --jp-spec-path=[none|full] set whether kernel spec uses full paths 95 | --jp-startup-script=path run script on startup 96 | (path can be a file or a folder) 97 | --jp-working-dir=path set kernel working directory 98 | (default = current working directory) 99 | --version show kernel version 100 | --versions show kernel and library versions 101 | ``` 102 | 103 | 104 | ### `jp-coffee-console`: jp-CoffeeScript console 105 | 106 | `jp-coffee-console` is provided for convenience to users as a wrapper around 107 | `jupyter console`. The following command flags are recognised: 108 | 109 | ``` 110 | --help show jp-CoffeeScript and notebook help 111 | --jp-debug enable debug messages 112 | --jp-help show this help 113 | --jp-hide-undefined do not show undefined results 114 | --jp-install=[local|global] install kernel for current user or globally 115 | --jp-protocol=version set protocol version, e.g. 5.0 116 | --jp-show-undefined show undefined results 117 | --jp-spec-path=[none|full] set whether kernel spec uses full paths 118 | --jp-startup-script=path run script on startup 119 | (path can be a file or a folder) 120 | --jp-working-dir=path set kernel working directory 121 | (default = current working directory) 122 | --version show kernel version 123 | --versions show kernel and library versions 124 | ``` 125 | 126 | 127 | ### `jp-coffee-kernel`: jp-CoffeeScript kernel 128 | 129 | `jp-coffee-kernel` is the executable invoked by Jupyter tools (e.g. the 130 | notebook) and that appears in the kernel spec that `jp-coffee-install` creates 131 | for jp-CoffeeScript. You won't need this command, unless you want to create a 132 | custom kernel spec. 133 | 134 | ``` 135 | Usage: 136 | jp-coffee-kernel [options] connection_file 137 | 138 | Options: 139 | --debug enable debug messages 140 | --hide-undefined do not show undefined results 141 | --protocol=Major[.minor[.patch]] set protocol version, e.g. 5.0 142 | --session-working-dir=path set session working directory 143 | --show-undefined show undefined results 144 | --startup-script=path run script on startup 145 | (path can be a file or a folder) 146 | ``` 147 | 148 | 149 | ### `jp-coffee`: Deprecated CLI 150 | 151 | `jp-coffee` is provided for backwards-compatibility. It will be removed in the 152 | next major-version update. Please, use `jp-coffee-install` or 153 | `jp-coffee-notebook` instead. 154 | 155 | 156 | # Contributions 157 | 158 | First of all, thank you for taking the time to contribute. The maintenance of 159 | IJavascript is currently my priority. I would really appreciate some help. 160 | Please, read [CONTRIBUTING](CONTRIBUTING.md) and use the [issue 161 | tracker](https://github.com/n-riesco/jp-coffeescript/issues) for any 162 | contributions: support requests, bug reports, enhancement requests, pull 163 | requests, submission of tutorials, ... 164 | 165 | 166 | # TO DO 167 | 168 | - Add tests 169 | -------------------------------------------------------------------------------- /bin/jp-coffeescript-console.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* 4 | * BSD 3-Clause License 5 | * 6 | * Copyright (c) 2017, Nicolas Riesco and others as credited in the AUTHORS file 7 | * All rights reserved. 8 | * 9 | * Redistribution and use in source and binary forms, with or without 10 | * modification, are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, 13 | * this list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * 19 | * 3. Neither the name of the copyright holder nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 24 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 27 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 31 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 | * POSSIBILITY OF SUCH DAMAGE. 34 | * 35 | */ 36 | 37 | var console = require("console"); 38 | 39 | var rc = require("./rc.js"); 40 | var context = rc.context; 41 | var installKernelAsync = rc.installKernelAsync; 42 | var log = rc.log; 43 | var readPackageJson = rc.readPackageJson; 44 | var parseCommandArgs = rc.parseCommandArgs; 45 | var setJupyterInfoAsync = rc.setJupyterInfoAsync; 46 | var setPaths = rc.setPaths; 47 | var setProtocol = rc.setProtocol; 48 | var spawnFrontend = rc.spawnFrontend; 49 | 50 | setPaths(context); 51 | 52 | readPackageJson(context); 53 | 54 | parseCommandArgs(context, { 55 | flagPrefix: "jp", 56 | 57 | usageHeader: [ 58 | "jp-CoffeeScript Console", 59 | "", 60 | "Usage:", 61 | "", 62 | " jp-coffee-console ", 63 | ].join("\n"), 64 | 65 | usageFooter: [ 66 | "and any other options recognised by the Jupyter notebook; run:", 67 | "", 68 | " jupyter console --help", 69 | "", 70 | "for a full list.", 71 | ].join("\n"), 72 | }); 73 | 74 | setJupyterInfoAsync(context, function() { 75 | setProtocol(context); 76 | 77 | installKernelAsync(context, function() { 78 | log("CONTEXT:", context); 79 | 80 | if (!context.flag.install) { 81 | console.error("To quit press ctrl-d and confirm.\n"); 82 | spawnFrontend(context); 83 | } 84 | }); 85 | }); 86 | -------------------------------------------------------------------------------- /bin/jp-coffeescript-install.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* 4 | * BSD 3-Clause License 5 | * 6 | * Copyright (c) 2017, Nicolas Riesco and others as credited in the AUTHORS file 7 | * All rights reserved. 8 | * 9 | * Redistribution and use in source and binary forms, with or without 10 | * modification, are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, 13 | * this list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * 19 | * 3. Neither the name of the copyright holder nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 24 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 27 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 31 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 | * POSSIBILITY OF SUCH DAMAGE. 34 | * 35 | */ 36 | 37 | var rc = require("./rc.js"); 38 | var context = rc.context; 39 | var installKernelAsync = rc.installKernelAsync; 40 | var log = rc.log; 41 | var readPackageJson = rc.readPackageJson; 42 | var parseCommandArgs = rc.parseCommandArgs; 43 | var setJupyterInfoAsync = rc.setJupyterInfoAsync; 44 | var setPaths = rc.setPaths; 45 | var setProtocol = rc.setProtocol; 46 | 47 | setPaths(context); 48 | 49 | readPackageJson(context); 50 | 51 | parseCommandArgs(context, { 52 | installer: true, 53 | 54 | usageHeader: [ 55 | "jp-CoffeeScript Kernel Installer", 56 | "", 57 | "Usage:", 58 | "", 59 | " jp-coffe-install ", 60 | ].join("\n"), 61 | }); 62 | 63 | setJupyterInfoAsync(context, function() { 64 | setProtocol(context); 65 | 66 | installKernelAsync(context, function() { 67 | log("CONTEXT:", context); 68 | }); 69 | }); 70 | -------------------------------------------------------------------------------- /bin/jp-coffeescript-notebook.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* 4 | * BSD 3-Clause License 5 | * 6 | * Copyright (c) 2017, Nicolas Riesco and others as credited in the AUTHORS file 7 | * All rights reserved. 8 | * 9 | * Redistribution and use in source and binary forms, with or without 10 | * modification, are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, 13 | * this list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * 19 | * 3. Neither the name of the copyright holder nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 24 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 27 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 31 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 | * POSSIBILITY OF SUCH DAMAGE. 34 | * 35 | */ 36 | 37 | var rc = require("./rc.js"); 38 | var context = rc.context; 39 | var installKernelAsync = rc.installKernelAsync; 40 | var log = rc.log; 41 | var readPackageJson = rc.readPackageJson; 42 | var parseCommandArgs = rc.parseCommandArgs; 43 | var setJupyterInfoAsync = rc.setJupyterInfoAsync; 44 | var setPaths = rc.setPaths; 45 | var setProtocol = rc.setProtocol; 46 | var spawnFrontend = rc.spawnFrontend; 47 | 48 | setPaths(context); 49 | 50 | readPackageJson(context); 51 | 52 | parseCommandArgs(context, { 53 | flagPrefix: "jp", 54 | 55 | usageHeader: [ 56 | "jp-CoffeeScript Notebook", 57 | "", 58 | "Usage:", 59 | "", 60 | " jp-coffee-notebook ", 61 | ].join("\n"), 62 | 63 | usageFooter: [ 64 | "and any other options recognised by the Jupyter notebook; run:", 65 | "", 66 | " jupyter notebook --help", 67 | "", 68 | "for a full list.", 69 | ].join("\n"), 70 | }); 71 | 72 | setJupyterInfoAsync(context, function() { 73 | setProtocol(context); 74 | 75 | installKernelAsync(context, function() { 76 | log("CONTEXT:", context); 77 | 78 | if (!context.flag.install) { 79 | spawnFrontend(context); 80 | } 81 | }); 82 | }); 83 | -------------------------------------------------------------------------------- /bin/jp-coffeescript.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* 4 | * BSD 3-Clause License 5 | * 6 | * Copyright (c) 2015, Nicolas Riesco and others as credited in the AUTHORS file 7 | * All rights reserved. 8 | * 9 | * Redistribution and use in source and binary forms, with or without 10 | * modification, are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, 13 | * this list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * 19 | * 3. Neither the name of the copyright holder nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 24 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 27 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 31 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 | * POSSIBILITY OF SUCH DAMAGE. 34 | * 35 | */ 36 | 37 | var rc = require("./rc.js"); 38 | var context = rc.context; 39 | var installKernelAsync = rc.installKernelAsync; 40 | var log = rc.log; 41 | var readPackageJson = rc.readPackageJson; 42 | var parseCommandArgs = rc.parseCommandArgs; 43 | var setJupyterInfoAsync = rc.setJupyterInfoAsync; 44 | var setPaths = rc.setPaths; 45 | var setProtocol = rc.setProtocol; 46 | var spawnFrontend = rc.spawnFrontend; 47 | 48 | setPaths(context); 49 | 50 | readPackageJson(context); 51 | 52 | parseCommandArgs(context, { 53 | showUndefined: true, 54 | includeDeprecated: true, 55 | 56 | flagPrefix: "jp", 57 | 58 | usageHeader: [ 59 | "jp-CoffeeScript Notebook", 60 | "", 61 | "Usage:", 62 | "", 63 | " jp-coffee ", 64 | ].join("\n"), 65 | 66 | usageFooter: [ 67 | "and any other options recognised by the Jupyter notebook; run:", 68 | "", 69 | " jupyter notebook --help", 70 | "", 71 | "for a full list.", 72 | ].join("\n"), 73 | }); 74 | 75 | setJupyterInfoAsync(context, function() { 76 | setProtocol(context); 77 | 78 | installKernelAsync(context, function() { 79 | log("CONTEXT:", context); 80 | 81 | if (!context.flag.install) { 82 | spawnFrontend(context); 83 | } 84 | }); 85 | }); 86 | -------------------------------------------------------------------------------- /bin/rc.js: -------------------------------------------------------------------------------- 1 | /* 2 | * BSD 3-Clause License 3 | * 4 | * Copyright (c) 2017, Nicolas Riesco and others as credited in the AUTHORS file 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * 3. Neither the name of the copyright holder nor the names of its contributors 18 | * may be used to endorse or promote products derived from this software without 19 | * specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | * 33 | */ 34 | 35 | var console = require("console"); 36 | var crypto = require("crypto"); 37 | var exec = require("child_process").exec; 38 | var fs = require("fs"); 39 | var os = require("os"); 40 | var path = require("path"); 41 | var spawn = require("child_process").spawn; 42 | var util = require("util"); 43 | 44 | 45 | // Setup logging helpers 46 | var DEBUG; 47 | 48 | var log; 49 | var dontLog = function dontLog() {}; 50 | var doLog = function doLog() { 51 | process.stderr.write("JP-COFFEE: "); 52 | console.error.apply(this, arguments); 53 | }; 54 | 55 | if (process.env.DEBUG) { 56 | DEBUG = true; 57 | 58 | try { 59 | doLog = require("debug")("JP-COFFEE:"); 60 | } catch (err) {} 61 | } 62 | 63 | log = DEBUG ? doLog : dontLog; 64 | 65 | 66 | /** 67 | * @typedef Context 68 | * 69 | * @property context 70 | * @property context.path 71 | * @property {String} context.path.node Path to Node.js shell 72 | * @property {String} context.path.root Path to CoffeeScript root folder 73 | * @property {String} context.path.kernel Path to CoffeeScript kernel 74 | * @property {String} context.path.images Path to CoffeeScript images folder 75 | * @property {Object} context.packageJSON Contents of npm package.json 76 | * @property context.flag 77 | * @property {Boolean} context.flag.debug --debug 78 | * @property {Boolean} context.flag.hideUndefined --[hide|show]-undefined 79 | * @property {String} context.flag.install --install=[local|global] 80 | * @property {String} context.flag.specPath --spec-path=[none|full] 81 | * @property {String} context.flag.startup --startup-script=path 82 | * @property {String} context.flag.cwd --working-dir=path 83 | * @property context.args 84 | * @property {String[]} context.args.kernel Command arguments to run kernel 85 | * @property {String[]} context.args.frontend Command arguments to run frontend 86 | * @property context.protocol 87 | * @property {String} context.protocol.version Protocol version 88 | * @property {Integer} context.protocol.majorVersion Protocol major version 89 | * @property context.frontend 90 | * @property {Error} context.frontend.error Frontend error 91 | * @property {String} context.frontend.version Frontend version 92 | * @property {Integer} context.frontend.majorVersion Frontend major version 93 | */ 94 | 95 | /** 96 | * Script context 97 | * @type Context 98 | */ 99 | var context = { 100 | path: {}, 101 | packageJSON: undefined, 102 | flag: {}, 103 | args: {}, 104 | protocol: {}, 105 | frontend: {}, 106 | }; 107 | 108 | function setPaths(context) { 109 | context.path.node = process.argv[0]; 110 | context.path.root = path.dirname(path.dirname( 111 | fs.realpathSync(process.argv[1]) 112 | )); 113 | context.path.kernel = path.join(context.path.root, "lib", "kernel.js"); 114 | context.path.images = path.join(context.path.root, "images"); 115 | } 116 | 117 | function readPackageJson(context) { 118 | context.packageJSON = JSON.parse( 119 | fs.readFileSync(path.join(context.path.root, "package.json")) 120 | ); 121 | } 122 | 123 | function getPackageVersion(packageName) { 124 | var packagePath = path.dirname(require.resolve(packageName)); 125 | var packageJSON = JSON.parse( 126 | fs.readFileSync(path.join(packagePath, "package.json")) 127 | ); 128 | return packageJSON.version; 129 | } 130 | 131 | var FLAGS = [{ 132 | excludeIfInstaller: true, 133 | flag: "help", 134 | description: "show jp-CoffeeScript and Jupyter/IPython help", 135 | parse: function(context, arg) { 136 | context.args.frontend.push(arg); 137 | }, 138 | showUsage: true, 139 | }, { 140 | flag: "version", 141 | description: "show jp-CoffeeScript version", 142 | parse: function(context, arg) { 143 | console.log(context.packageJSON.version); 144 | }, 145 | exit: true, 146 | }, { 147 | flag: "versions", 148 | description: "show jp-CoffeeScript and library versions", 149 | parse: function(context, arg) { 150 | console.log("jp-coffeescript", context.packageJSON.version); 151 | console.log("jmp", getPackageVersion("jmp")); 152 | console.log("jp-kernel", getPackageVersion("jp-kernel")); 153 | console.log("nel", getPackageVersion("nel")); 154 | console.log("uuid", getPackageVersion("uuid")); 155 | console.log("zeromq", getPackageVersion("zeromq")); 156 | }, 157 | exit: true, 158 | }, { 159 | prefixedFlag: "debug", 160 | description: "enable debug log level", 161 | parse: function(context, arg) { 162 | DEBUG = true; 163 | log = doLog; 164 | 165 | context.flag.debug = true; 166 | context.args.kernel.push(arg); 167 | }, 168 | }, { 169 | prefixedFlag: "help", 170 | description: "show jp-CoffeeScript help", 171 | parse: function(context, arg) { 172 | }, 173 | showUsage: true, 174 | exit: true, 175 | }, { 176 | prefixedFlag: "hide-execution-result", 177 | description: "do not show execution results", 178 | parse: function(context, arg) { 179 | context.flag.hideExecutionResult = true; 180 | context.args.kernel.push("--hide-execution-result"); 181 | }, 182 | }, { 183 | prefixedFlag: "hide-undefined", 184 | description: "do not show undefined results", 185 | parse: function(context, arg) { 186 | context.flag.hideUndefined = true; 187 | context.args.kernel.push("--hide-undefined"); 188 | }, 189 | }, { 190 | prefixedFlag: "install=[local|global]", 191 | description: "install jp-CoffeeScript kernel", 192 | parse: function(context, arg) { 193 | context.flag.install = getValue(arg); 194 | if (context.flag.install !== "local" && 195 | context.flag.install !== "global") { 196 | throw new Error( 197 | util.format("Unknown flag option '%s'\n", arg) 198 | ); 199 | } 200 | }, 201 | }, { 202 | deprecated: true, 203 | prefixedFlag: "install-kernel", 204 | description: 205 | "same as --PREFIX-install=local (for backwards-compatibility)", 206 | parse: function(context, arg) { 207 | context.flag.install = "local"; 208 | }, 209 | }, { 210 | prefixedFlag: "protocol=version", 211 | description: "set protocol version, e.g. 4.1", 212 | parse: function(context, arg) { 213 | context.protocol.version = getValue(arg); 214 | context.protocol.majorVersion = parseInt( 215 | context.protocol.version.split(".", 1)[0] 216 | ); 217 | }, 218 | }, { 219 | prefixedFlag: "show-undefined", 220 | description: "show undefined results", 221 | parse: function(context, arg) { 222 | context.flag.hideUndefined = false; 223 | context.args.kernel.push("--show-undefined"); 224 | }, 225 | }, { 226 | prefixedFlag: "spec-path=[none|full]", 227 | description: "set whether kernel spec uses full paths", 228 | parse: function(context, arg) { 229 | context.flag.specPath = getValue(arg); 230 | if (context.flag.specPath !== "none" && 231 | context.flag.specPath !== "full") { 232 | throw new Error( 233 | util.format("Unknown flag option '%s'\n", arg) 234 | ); 235 | } 236 | }, 237 | }, { 238 | prefixedFlag: "startup-script=path", 239 | description: "run script on startup (path can be a file or a folder)", 240 | parse: function(context, arg) { 241 | context.flag.startup = fs.realpathSync(getValue(arg)); 242 | context.args.kernel.push( 243 | "--startup-script=" + context.flag.startup 244 | ); 245 | }, 246 | }, { 247 | prefixedFlag: "working-dir=path", 248 | description: 249 | "set session working directory (default = current working directory)", 250 | parse: function(context, arg) { 251 | context.flag.cwd = fs.realpathSync(getValue(arg)); 252 | context.args.kernel.push( 253 | "--session-working-dir=" + context.flag.cwd 254 | ); 255 | }, 256 | }]; 257 | 258 | function parseCommandArgs(context, options) { 259 | var flagPrefix = options.flagPrefix || ""; 260 | 261 | context.args.kernel = []; 262 | context.args.frontend = [ 263 | "jupyter", 264 | "notebook", 265 | ]; 266 | 267 | /* eslint-disable complexity */ 268 | process.argv.slice(2).forEach(function(arg) { 269 | var matched = false; 270 | 271 | for (var i = 0; i < FLAGS.length; i++) { 272 | var flagDefinition = FLAGS[i]; 273 | 274 | if (flagDefinition.deprecated && !options.includeDeprecated) { 275 | continue; 276 | } 277 | 278 | if (flagDefinition.excludeIfInstaller && options.installer) { 279 | continue; 280 | } 281 | 282 | var fullFlag = getFullFlag(flagDefinition, options); 283 | var flag = getFlag(fullFlag); 284 | var value = getValue(fullFlag); 285 | 286 | // if arg doesn't match flag definition, continue iterating 287 | if (value) { 288 | if (arg.lastIndexOf(flag, 0) !== 0) continue; 289 | } else { 290 | if (arg !== flag) continue; 291 | } 292 | 293 | matched = true; 294 | flagDefinition.parse(context, arg); 295 | if (flagDefinition.showUsage) showUsage(options); 296 | if (flagDefinition.exit) process.exit(0); 297 | break; 298 | } 299 | 300 | if (matched) { 301 | return; 302 | 303 | } else if (options.installer) { 304 | throw new Error(util.format("Unknown flag '%s'\n", arg)); 305 | 306 | } else if (arg.lastIndexOf("--" + flagPrefix + "-", 0) === 0) { 307 | throw new Error(util.format("Unknown flag '%s'\n", arg)); 308 | 309 | } else if (arg.lastIndexOf("--KernelManager.kernel_cmd=", 0) === 0) { 310 | console.warn(util.format("Warning: Flag '%s' skipped", arg)); 311 | 312 | } else { 313 | context.args.frontend.push(arg); 314 | } 315 | }); 316 | /* eslint-enable complexity */ 317 | 318 | if (context.flag.specPath === "full") { 319 | context.args.kernel = [ 320 | context.path.node, 321 | context.path.kernel, 322 | ].concat(context.args.kernel); 323 | } else { 324 | context.args.kernel = [ 325 | (process.platform === "win32") ? 326 | "jp-coffee-kernel.cmd" : 327 | "jp-coffee-kernel", 328 | ].concat(context.args.kernel); 329 | } 330 | 331 | if (!context.flag.hasOwnProperty("hideUndefined")) { 332 | if (options.showUndefined) { 333 | context.args.kernel.push("--show-undefined"); 334 | } else { 335 | context.args.kernel.push("--hide-undefined"); 336 | } 337 | } 338 | 339 | context.args.kernel.push("{connection_file}"); 340 | } 341 | 342 | function getFullFlag(flagDefinition, config) { 343 | return (flagDefinition.flag) ? 344 | "--" + flagDefinition.flag : 345 | (config.flagPrefix) ? 346 | "--" + config.flagPrefix + "-" + flagDefinition.prefixedFlag : 347 | "--" + flagDefinition.prefixedFlag; 348 | } 349 | 350 | function getFlag(arg) { 351 | var index = arg.indexOf("="); 352 | return (index === -1) ? arg : arg.slice(0, index + 1); 353 | } 354 | 355 | function getValue(arg) { 356 | var index = arg.indexOf("="); 357 | return (index === -1) ? "" : arg.slice(index + 1); 358 | } 359 | 360 | var PREFIX_RE = /PREFIX/g; 361 | 362 | function showUsage(options) { 363 | var flagPrefix = options.flagPrefix || ""; 364 | var usageHeader = options.usageHeader; 365 | var usageFooter = options.usageFooter; 366 | 367 | var usage = ""; 368 | 369 | if (usageHeader) usage += usageHeader + "\n\n"; 370 | 371 | usage += "The recognised options are:\n\n"; 372 | 373 | var maxFlagLength = 0; 374 | var flags = []; 375 | for (var i = 0; i < FLAGS.length; i++) { 376 | var flagDefinition = FLAGS[i]; 377 | 378 | if (flagDefinition.deprecated && !options.includeDeprecated) { 379 | continue; 380 | } 381 | 382 | if (flagDefinition.excludeIfInstaller && options.installer) { 383 | continue; 384 | } 385 | 386 | var fullFlag = getFullFlag(flagDefinition, options); 387 | 388 | if (fullFlag.length > maxFlagLength) { 389 | maxFlagLength = fullFlag.length; 390 | } 391 | 392 | var description = flagDefinition.description; 393 | description = description.replace(PREFIX_RE, flagPrefix); 394 | 395 | flags.push({ 396 | fullFlag: fullFlag, 397 | description: description, 398 | }); 399 | } 400 | 401 | flags.forEach(function(flag) { 402 | var fullFlag = flag.fullFlag; 403 | var description = flag.description; 404 | 405 | var indent = " "; 406 | var paddingLength = maxFlagLength - fullFlag.length + 2; 407 | var padding = (new Array(paddingLength + 1)).join(" "); 408 | var line = indent + fullFlag + padding + description + "\n"; 409 | 410 | usage += line; 411 | }); 412 | 413 | if (usageFooter) usage += "\n" + usageFooter; 414 | 415 | console.log(usage); 416 | } 417 | 418 | function setJupyterInfoAsync(context, callback) { 419 | exec("jupyter --version", function(error, stdout, stderr) { 420 | if (error) { 421 | context.frontend.error = error; 422 | setIPythonInfoAsync(context, callback); 423 | return; 424 | } 425 | 426 | context.args.frontend[0] = "jupyter"; 427 | 428 | var version; 429 | var majorVersion; 430 | 431 | // Parse version number before Jupyter 4.5.0 432 | version = stdout.toString().trim(); 433 | majorVersion = parseInt(version.split(".")[0]); 434 | 435 | if (isNaN(majorVersion)) { 436 | // Parse version number after Jupyter 4.5.0 437 | var match = stdout.match(/^jupyter core\s+: (\d+\.\d+\.\d+)/m); 438 | if (match) { 439 | version = match[1]; 440 | majorVersion = parseInt(version.split(".")[0]); 441 | } else { 442 | // Failed to parse the output of "jupyter --version" 443 | console.warn( 444 | "Warning: Unable to parse Jupyter version:", 445 | stdout 446 | ); 447 | version = "unknown"; 448 | majorVersion = Infinity; 449 | } 450 | } 451 | 452 | context.frontend.version = version; 453 | context.frontend.majorVersion = majorVersion; 454 | 455 | if (callback) { 456 | callback(); 457 | } 458 | }); 459 | } 460 | 461 | function setIPythonInfoAsync(context, callback) { 462 | exec("ipython --version", function(error, stdout, stderr) { 463 | if (error) { 464 | if (context.frontend.error) { 465 | console.error("Error running `jupyter --version`"); 466 | console.error(context.frontend.error.toString()); 467 | } 468 | console.error("Error running `ipython --version`"); 469 | console.error(error.toString()); 470 | log("CONTEXT:", context); 471 | process.exit(1); 472 | } 473 | 474 | context.args.frontend[0] = "ipython"; 475 | context.frontend.version = stdout.toString().trim(); 476 | context.frontend.majorVersion = parseInt( 477 | context.frontend.version.split(".")[0] 478 | ); 479 | if (isNaN(context.frontend.majorVersion)) { 480 | console.error( 481 | "Error parsing IPython version:", 482 | context.frontend.version 483 | ); 484 | log("CONTEXT:", context); 485 | process.exit(1); 486 | } 487 | 488 | if (callback) { 489 | callback(); 490 | } 491 | }); 492 | } 493 | 494 | function setProtocol(context) { 495 | if (!context.protocol.version) { 496 | if (context.frontend.majorVersion < 3) { 497 | context.protocol.version = "4.1"; 498 | context.protocol.majorVersion = 4; 499 | } else { 500 | context.protocol.version = "5.1"; 501 | context.protocol.majorVersion = 5; 502 | } 503 | } 504 | 505 | context.args.kernel.push("--protocol=" + context.protocol.version); 506 | 507 | if (context.frontend.majorVersion < 3) { 508 | context.args.frontend.push(util.format( 509 | "--KernelManager.kernel_cmd=['%s']", 510 | context.args.kernel.join("', '") 511 | )); 512 | } else if (context.args.frontend[1] === "console") { 513 | context.args.frontend.push("--kernel=coffeescript"); 514 | } 515 | 516 | if (context.frontend.majorVersion < 3 && 517 | context.protocol.majorVersion >= 5) { 518 | console.warn("Warning: Protocol v5+ requires Jupyter v3+"); 519 | } 520 | } 521 | 522 | function installKernelAsync(context, callback) { 523 | if (context.frontend.majorVersion < 3) { 524 | if (context.flag.install) { 525 | console.error( 526 | "Error: Installation of kernel specs requires Jupyter v3+" 527 | ); 528 | } 529 | 530 | if (callback) { 531 | callback(); 532 | } 533 | 534 | return; 535 | } 536 | 537 | // Create temporary spec folder 538 | var tmpdir = makeTmpdir(); 539 | var specDir = path.join(tmpdir, "coffeescript"); 540 | fs.mkdirSync(specDir); 541 | 542 | // Create spec file 543 | var specFile = path.join(specDir, "kernel.json"); 544 | var spec = { 545 | argv: context.args.kernel, 546 | display_name: "CoffeeScript (Node.js)", 547 | language: "coffeescript", 548 | }; 549 | fs.writeFileSync(specFile, JSON.stringify(spec)); 550 | 551 | // Copy logo files 552 | var logoDir = path.join(context.path.images, "nodejs"); 553 | var logo32Src = path.join(logoDir, "js-green-32x32.png"); 554 | var logo32Dst = path.join(specDir, "logo-32x32.png"); 555 | var logo64Src = path.join(logoDir, "js-green-64x64.png"); 556 | var logo64Dst = path.join(specDir, "logo-64x64.png"); 557 | copyAsync(logo32Src, logo32Dst, function() { 558 | copyAsync(logo64Src, logo64Dst, function() { 559 | 560 | // Install kernel spec 561 | var args = [ 562 | context.args.frontend[0], 563 | "kernelspec install --replace", 564 | specDir, 565 | ]; 566 | if (context.flag.install !== "global") { 567 | args.push("--user"); 568 | } 569 | var cmd = args.join(" "); 570 | exec(cmd, function(error, stdout, stderr) { 571 | 572 | // Remove temporary spec folder 573 | fs.unlinkSync(specFile); 574 | fs.unlinkSync(logo32Dst); 575 | fs.unlinkSync(logo64Dst); 576 | fs.rmdirSync(specDir); 577 | fs.rmdirSync(tmpdir); 578 | 579 | if (error) { 580 | console.error(util.format("Error running `%s`", cmd)); 581 | console.error(error.toString()); 582 | if (stderr) console.error(stderr.toString()); 583 | log("CONTEXT:", context); 584 | process.exit(1); 585 | } 586 | 587 | if (callback) { 588 | callback(); 589 | } 590 | }); 591 | }); 592 | }); 593 | } 594 | 595 | function spawnFrontend(context) { 596 | var cmd = context.args.frontend[0]; 597 | var args = context.args.frontend.slice(1); 598 | var frontend = spawn(cmd, args, { 599 | stdio: "inherit" 600 | }); 601 | 602 | // Relay SIGINT onto the frontend 603 | var signal = "SIGINT"; 604 | process.on(signal, function() { 605 | frontend.emit(signal); 606 | }); 607 | } 608 | 609 | function makeTmpdir(maxAttempts) { 610 | maxAttempts = maxAttempts ? maxAttempts : 10; 611 | var attempts = 0; 612 | 613 | var tmpdir; 614 | while (!tmpdir) { 615 | attempts++; 616 | try { 617 | tmpdir = path.join( 618 | os.tmpdir(), 619 | crypto.randomBytes(16).toString("hex") 620 | ); 621 | fs.mkdirSync(tmpdir); 622 | } catch (e) { 623 | if (attempts >= maxAttempts) 624 | throw e; 625 | tmpdir = null; 626 | } 627 | } 628 | 629 | return tmpdir; 630 | } 631 | 632 | function copyAsync(src, dst, callback) { 633 | var readStream = fs.createReadStream(src); 634 | var writeStream = fs.createWriteStream(dst); 635 | if (callback) { 636 | readStream.on("end", callback); 637 | } 638 | readStream.pipe(writeStream); 639 | } 640 | 641 | module.exports = { 642 | context: context, 643 | copyAsync: copyAsync, 644 | doLog: doLog, 645 | dontLog: dontLog, 646 | installKernelAsync: installKernelAsync, 647 | log: log, 648 | makeTmpdir: makeTmpdir, 649 | parseCommandArgs: parseCommandArgs, 650 | readPackageJson: readPackageJson, 651 | setIPythonInfoAsync: setIPythonInfoAsync, 652 | setJupyterInfoAsync: setJupyterInfoAsync, 653 | setPaths: setPaths, 654 | setProtocol: setProtocol, 655 | spawnFrontend: spawnFrontend, 656 | }; 657 | -------------------------------------------------------------------------------- /docs/bin_jp-coffeescript.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Source: bin/jp-coffeescript.js 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Source: bin/jp-coffeescript.js

21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |
29 |
#!/usr/bin/env node
 30 | 
 31 | /*
 32 |  * BSD 3-Clause License
 33 |  *
 34 |  * Copyright (c) 2015, Nicolas Riesco and others as credited in the AUTHORS file
 35 |  * All rights reserved.
 36 |  *
 37 |  * Redistribution and use in source and binary forms, with or without
 38 |  * modification, are permitted provided that the following conditions are met:
 39 |  *
 40 |  * 1. Redistributions of source code must retain the above copyright notice,
 41 |  * this list of conditions and the following disclaimer.
 42 |  *
 43 |  * 2. Redistributions in binary form must reproduce the above copyright notice,
 44 |  * this list of conditions and the following disclaimer in the documentation
 45 |  * and/or other materials provided with the distribution.
 46 |  *
 47 |  * 3. Neither the name of the copyright holder nor the names of its contributors
 48 |  * may be used to endorse or promote products derived from this software without
 49 |  * specific prior written permission.
 50 |  *
 51 |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 52 |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 53 |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 54 |  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 55 |  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 56 |  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 57 |  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 58 |  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 59 |  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 60 |  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 61 |  * POSSIBILITY OF SUCH DAMAGE.
 62 |  *
 63 |  */
 64 | var DEBUG = false;
 65 | 
 66 | var console = require("console");
 67 | var exec = require("child_process").exec;
 68 | var fs = require("fs");
 69 | var os = require("os");
 70 | var path = require("path");
 71 | var spawn = require("child_process").spawn;
 72 | var util = require("util");
 73 | 
 74 | var uuid = require("node-uuid");
 75 | 
 76 | var usage = (
 77 |     "CoffeeScript Notebook\n" +
 78 |     "\n" +
 79 |     "Usage:\n" +
 80 |     "\n" +
 81 |     "    jp-coffee <options>\n" +
 82 |     "\n" +
 83 |     "The recognised options are:\n" +
 84 |     "\n" +
 85 |     "    --jp-debug                   enable debug log level\n" +
 86 |     "    --jp-help                    show this help\n" +
 87 |     "    --jp-hide-undefined          do not show undefined results\n" +
 88 |     "    --jp-install=[local|global]  install CoffeeScript kernel\n" +
 89 |     "    --jp-install-kernel          same as --jp-install=local\n" +
 90 |     "                                 (for backwards-compatibility)\n" +
 91 |     "    --jp-protocol=version  set protocol version, e.g. 4.1\n" +
 92 |     "    --jp-working-dir=path  set CoffeeScript working directory\n" +
 93 |     "                           (default = current working directory)\n" +
 94 |     "    --version              show CoffeeScript version\n" +
 95 |     "\n" +
 96 |     "and any other options recognised by the Jupyter notebook; run:\n" +
 97 |     "\n" +
 98 |     "    jupyter notebook --help\n" +
 99 |     "\n" +
100 |     "for a full list.\n"
101 | );
102 | 
103 | /**
104 |  * @typedef Context
105 |  *
106 |  * @property            context
107 |  * @property            context.path
108 |  * @property {String}   context.path.node     Path to Node.js shell
109 |  * @property {String}   context.path.root     Path to CoffeeScript root folder
110 |  * @property {String}   context.path.kernel   Path to CoffeeScript kernel
111 |  * @property {String}   context.path.images   Path to CoffeeScript images folder
112 |  * @property {Object}   context.packageJSON   Contents of npm package.json
113 |  * @property            context.flag
114 |  * @property {Boolean}  context.flag.debug    --jp-debug
115 |  * @property {String}   context.flag.install  --jp-install=[local|global]
116 |  * @property {String}   context.flag.cwd      --jp-working-dir=path
117 |  * @property            context.args
118 |  * @property {String[]} context.args.kernel   Command arguments to run kernel
119 |  * @property {String[]} context.args.frontend Command arguments to run frontend
120 |  * @property            context.protocol
121 |  * @property {String}   context.protocol.version      Protocol version
122 |  * @property {Integer}  context.protocol.majorVersion Protocol major version
123 |  * @property            context.frontend
124 |  * @property {String}   context.frontend.version      Frontend version
125 |  * @property {Integer}  context.frontend.majorVersion Frontend major version
126 |  */
127 | 
128 | /**
129 |  * Script context
130 |  * @type Context
131 |  */
132 | var context = {
133 |     path: {},
134 |     packageJSON: undefined,
135 |     flag: {},
136 |     args: {},
137 |     protocol: {},
138 |     frontend: {},
139 | };
140 | 
141 | setPaths(context);
142 | readPackageJson(context);
143 | parseCommandArgs(context);
144 | setFrontendInfoAsync(context, function() {
145 |     setProtocol(context);
146 | 
147 |     if (DEBUG) console.log("CONTEXT:", context);
148 | 
149 |     installKernelAsync(context, function() {
150 |         if (!context.flag.install) {
151 |             spawnFrontend(context);
152 |         }
153 |     });
154 | });
155 | 
156 | function setPaths(context) {
157 |     context.path.node = process.argv[0];
158 |     context.path.root = path.dirname(path.dirname(
159 |         fs.realpathSync(process.argv[1])
160 |     ));
161 |     context.path.kernel = path.join(context.path.root, "lib", "kernel.js");
162 |     context.path.images = path.join(context.path.root, "images");
163 | }
164 | 
165 | function readPackageJson(context) {
166 |     context.packageJSON = JSON.parse(
167 |         fs.readFileSync(path.join(context.path.root, "package.json"))
168 |     );
169 | }
170 | 
171 | function parseCommandArgs(context) {
172 |     context.args.kernel = [
173 |         context.path.node,
174 |         context.path.kernel,
175 |     ];
176 |     context.args.frontend = [
177 |         "ipython",
178 |         "notebook",
179 |     ];
180 | 
181 |     process.argv.slice(2).forEach(function(e) {
182 |         var FLAG_JP_DEBUG = "--jp-debug";
183 |         var FLAG_JP_HELP = "--jp-help";
184 |         var FLAG_JP_HIDE_UNDEFINED = "--jp-hide-undefined";
185 |         var FLAG_JP_INSTALL = "--jp-install=";
186 |         var FLAG_JP_INSTALL_KERNEL = "--jp-install-kernel";
187 |         var FLAG_JP_PROTOCOL = "--jp-protocol=";
188 |         var FLAG_JP_WORKING_DIR = "--jp-working-dir=";
189 | 
190 |         if (e === FLAG_JP_DEBUG) {
191 |             context.flag.debug = DEBUG = true;
192 |             context.args.kernel.push("--debug");
193 | 
194 |         } else if (e === FLAG_JP_HELP) {
195 |             console.log(usage);
196 |             process.exit(0);
197 | 
198 |         } else if (e === FLAG_JP_HIDE_UNDEFINED) {
199 |             context.args.kernel.push("--hide-undefined");
200 | 
201 |         } else if (e.lastIndexOf(FLAG_JP_INSTALL, 0) === 0) {
202 |             context.flag.install = e.slice(FLAG_JP_INSTALL.length);
203 |             if (context.flag.install !== "local" &&
204 |                 context.flag.install !== "global") {
205 |                 console.error(
206 |                     util.format("Error: Unknown flag option '%s'\n", e)
207 |                 );
208 |                 console.error(usage);
209 |                 process.exit(1);
210 |             }
211 | 
212 |         } else if (e === FLAG_JP_INSTALL_KERNEL) {
213 |             context.flag.install = "local";
214 | 
215 |         } else if (e.lastIndexOf(FLAG_JP_PROTOCOL, 0) === 0) {
216 |             context.protocol.version = e.slice(FLAG_JP_PROTOCOL.length);
217 |             context.protocol.majorVersion = parseInt(
218 |                 context.protocol.version.split(".", 1)[0]
219 |             );
220 | 
221 |         } else if (e.lastIndexOf(FLAG_JP_WORKING_DIR, 0) === 0) {
222 |             context.flag.cwd = fs.realpathSync(
223 |                 e.slice(FLAG_JP_WORKING_DIR.length)
224 |             );
225 | 
226 |         } else if (e.lastIndexOf("--jp-", 0) === 0) {
227 |             console.error(util.format("Error: Unknown flag '%s'\n", e));
228 |             console.error(usage);
229 |             process.exit(1);
230 | 
231 |         } else if (e.lastIndexOf("--KernelManager.kernel_cmd=", 0) === 0) {
232 |             console.warn(util.format("Warning: Flag '%s' skipped", e));
233 | 
234 |         } else if (e === "--version") {
235 |             console.log(context.packageJSON.version);
236 |             process.exit(0);
237 | 
238 |         } else {
239 |             context.args.frontend.push(e);
240 |         }
241 |     });
242 | 
243 |     if (context.flag.cwd) {
244 |         context.args.kernel.push("--session-working-dir=" + context.flag.cwd);
245 |     }
246 | 
247 |     context.args.kernel.push("{connection_file}");
248 | }
249 | 
250 | function setFrontendInfoAsync(context, callback) {
251 |     exec("ipython --version", function(error, stdout, stderr) {
252 |         if (error) {
253 |             console.error("Error running `ipython --version`");
254 |             console.error(error.toString());
255 |             if (stderr) console.error(stderr.toString());
256 |             if (DEBUG) console.log("CONTEXT:", context);
257 |             process.exit(1);
258 |         }
259 | 
260 |         context.frontend.version = stdout.toString().trim();
261 |         context.frontend.majorVersion = parseInt(
262 |             context.frontend.version.split(".")[0]
263 |         );
264 |         if (isNaN(context.frontend.majorVersion)) {
265 |             console.error(
266 |                 "Error parsing IPython version:",
267 |                 context.version.frontend
268 |             );
269 |             if (DEBUG) console.log("CONTEXT:", context);
270 |             process.exit(1);
271 |         }
272 | 
273 |         if (callback) {
274 |             callback();
275 |         }
276 |     });
277 | }
278 | 
279 | function setProtocol(context) {
280 |     if (!context.protocol.version) {
281 |         if (context.frontend.majorVersion < 3) {
282 |             context.protocol.version = "4.1";
283 |             context.protocol.majorVersion = 4;
284 |         } else {
285 |             context.protocol.version = "5.0";
286 |             context.protocol.majorVersion = 5;
287 |         }
288 |     }
289 | 
290 |     context.args.kernel.push("--protocol=" + context.protocol.version);
291 | 
292 |     if (context.frontend.majorVersion < 3) {
293 |         context.args.frontend.push(util.format(
294 |             "--KernelManager.kernel_cmd=['%s']",
295 |             context.args.kernel.join("', '")
296 |         ));
297 |     }
298 | 
299 |     if (context.frontend.majorVersion < 3 &&
300 |         context.protocol.majorVersion >= 5) {
301 |         console.warn("Warning: Protocol v5+ requires IPython v3+");
302 |     }
303 | }
304 | 
305 | function installKernelAsync(context, callback) {
306 |     if (context.frontend.majorVersion < 3) {
307 |         if (context.flag.install) {
308 |             console.error(
309 |                 "Error: Installation of kernel specs requires IPython v3+"
310 |             );
311 |         }
312 | 
313 |         if (callback) {
314 |             callback();
315 |         }
316 | 
317 |         return;
318 |     }
319 | 
320 |     // Create temporary spec folder
321 |     var tmpdir = makeTmpdir();
322 |     var specDir = path.join(tmpdir, "coffeescript");
323 |     fs.mkdirSync(specDir);
324 | 
325 |     // Create spec file
326 |     var specFile = path.join(specDir, "kernel.json");
327 |     var spec = {
328 |         argv: context.args.kernel,
329 |         display_name: "CoffeeScript (Node.js)",
330 |         language: "coffeescript",
331 |     };
332 |     fs.writeFileSync(specFile, JSON.stringify(spec));
333 | 
334 |     // Copy logo files
335 |     var logo32Src = path.join(context.path.images, "logo-32x32.png");
336 |     var logo32Dst = path.join(specDir, "logo-32x32.png");
337 |     var logo64Src = path.join(context.path.images, "logo-64x64.png");
338 |     var logo64Dst = path.join(specDir, "logo-64x64.png");
339 |     copyAsync(logo32Src, logo32Dst, function() {
340 |         copyAsync(logo64Src, logo64Dst, function() {
341 | 
342 |             // Install kernel spec
343 |             var cmd = "ipython kernelspec install --replace " + specDir;
344 |             if (context.flag.install !== "global") {
345 |                 cmd += "  --user";
346 |             }
347 |             exec(cmd, function(error, stdout, stderr) {
348 | 
349 |                 // Remove temporary spec folder
350 |                 fs.unlinkSync(specFile);
351 |                 fs.unlinkSync(logo32Dst);
352 |                 fs.unlinkSync(logo64Dst);
353 |                 fs.rmdirSync(specDir);
354 |                 fs.rmdirSync(tmpdir);
355 | 
356 |                 if (error) {
357 |                     console.error(util.format("Error running `%s`", cmd));
358 |                     console.error(error.toString());
359 |                     if (stderr) console.error(stderr.toString());
360 |                     if (DEBUG) console.log("CONTEXT:", context);
361 |                     process.exit(1);
362 |                 }
363 | 
364 |                 if (callback) {
365 |                     callback();
366 |                 }
367 |             });
368 |         });
369 |     });
370 | }
371 | 
372 | function spawnFrontend(context) {
373 |     var cmd = context.args.frontend[0];
374 |     var args = context.args.frontend.slice(1);
375 |     var frontend = spawn(cmd, args, {
376 |         stdio: "inherit"
377 |     });
378 | 
379 |     // Relay SIGINT onto the frontend
380 |     var signal = "SIGINT";
381 |     process.on(signal, function() {
382 |         frontend.emit(signal);
383 |     });
384 | }
385 | 
386 | function makeTmpdir(maxAttempts) {
387 |     maxAttempts = maxAttempts ? maxAttempts : 10;
388 |     var attempts = 0;
389 | 
390 |     var tmpdir;
391 |     while (!tmpdir) {
392 |         attempts++;
393 |         try {
394 |             tmpdir = path.join(os.tmpdir(), uuid.v4());
395 |             fs.mkdirSync(tmpdir);
396 |         } catch (e) {
397 |             if (attempts >= maxAttempts)
398 |                 throw e;
399 |             tmpdir = null;
400 |         }
401 |     }
402 | 
403 |     return tmpdir;
404 | }
405 | 
406 | function copyAsync(src, dst, callback) {
407 |     var readStream = fs.createReadStream(src);
408 |     var writeStream = fs.createWriteStream(dst);
409 |     if (callback) {
410 |         readStream.on("end", callback);
411 |     }
412 |     readStream.pipe(writeStream);
413 | }
414 | 
415 |
416 |
417 | 418 | 419 | 420 | 421 |
422 | 423 | 426 | 427 |
428 | 429 |
430 | Documentation generated by JSDoc 3.4.0 on Thu Sep 01 2016 10:21:33 GMT+0100 (BST) 431 |
432 | 433 | 434 | 435 | 436 | 437 | -------------------------------------------------------------------------------- /docs/bin_rc.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Source: bin/rc.js 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Source: bin/rc.js

21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |
29 |
/*
 30 |  * BSD 3-Clause License
 31 |  *
 32 |  * Copyright (c) 2017, Nicolas Riesco and others as credited in the AUTHORS file
 33 |  * All rights reserved.
 34 |  *
 35 |  * Redistribution and use in source and binary forms, with or without
 36 |  * modification, are permitted provided that the following conditions are met:
 37 |  *
 38 |  * 1. Redistributions of source code must retain the above copyright notice,
 39 |  * this list of conditions and the following disclaimer.
 40 |  *
 41 |  * 2. Redistributions in binary form must reproduce the above copyright notice,
 42 |  * this list of conditions and the following disclaimer in the documentation
 43 |  * and/or other materials provided with the distribution.
 44 |  *
 45 |  * 3. Neither the name of the copyright holder nor the names of its contributors
 46 |  * may be used to endorse or promote products derived from this software without
 47 |  * specific prior written permission.
 48 |  *
 49 |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 50 |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 51 |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 52 |  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 53 |  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 54 |  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 55 |  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 56 |  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 57 |  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 58 |  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 59 |  * POSSIBILITY OF SUCH DAMAGE.
 60 |  *
 61 |  */
 62 | 
 63 | var console = require("console");
 64 | var crypto = require("crypto");
 65 | var exec = require("child_process").exec;
 66 | var fs = require("fs");
 67 | var os = require("os");
 68 | var path = require("path");
 69 | var spawn = require("child_process").spawn;
 70 | var util = require("util");
 71 | 
 72 | 
 73 | // Setup logging helpers
 74 | var DEBUG;
 75 | 
 76 | var log;
 77 | var dontLog = function dontLog() {};
 78 | var doLog = function doLog() {
 79 |     process.stderr.write("JP-COFFEE: ");
 80 |     console.error.apply(this, arguments);
 81 | };
 82 | 
 83 | if (process.env.DEBUG) {
 84 |     DEBUG = true;
 85 | 
 86 |     try {
 87 |         doLog = require("debug")("JP-COFFEE:");
 88 |     } catch (err) {}
 89 | }
 90 | 
 91 | log = DEBUG ? doLog : dontLog;
 92 | 
 93 | 
 94 | /**
 95 |  * @typedef Context
 96 |  *
 97 |  * @property            context
 98 |  * @property            context.path
 99 |  * @property {String}   context.path.node     Path to Node.js shell
100 |  * @property {String}   context.path.root     Path to CoffeeScript root folder
101 |  * @property {String}   context.path.kernel   Path to CoffeeScript kernel
102 |  * @property {String}   context.path.images   Path to CoffeeScript images folder
103 |  * @property {Object}   context.packageJSON   Contents of npm package.json
104 |  * @property            context.flag
105 |  * @property {Boolean}  context.flag.debug          --debug
106 |  * @property {Boolean}  context.flag.hideUndefined  --[hide|show]-undefined
107 |  * @property {String}   context.flag.install        --install=[local|global]
108 |  * @property {String}   context.flag.specPath       --spec-path=[none|full]
109 |  * @property {String}   context.flag.startup        --startup-script=path
110 |  * @property {String}   context.flag.cwd            --working-dir=path
111 |  * @property            context.args
112 |  * @property {String[]} context.args.kernel   Command arguments to run kernel
113 |  * @property {String[]} context.args.frontend Command arguments to run frontend
114 |  * @property            context.protocol
115 |  * @property {String}   context.protocol.version      Protocol version
116 |  * @property {Integer}  context.protocol.majorVersion Protocol major version
117 |  * @property            context.frontend
118 |  * @property {Error}    context.frontend.error        Frontend error
119 |  * @property {String}   context.frontend.version      Frontend version
120 |  * @property {Integer}  context.frontend.majorVersion Frontend major version
121 |  */
122 | 
123 | /**
124 |  * Script context
125 |  * @type Context
126 |  */
127 | var context = {
128 |     path: {},
129 |     packageJSON: undefined,
130 |     flag: {},
131 |     args: {},
132 |     protocol: {},
133 |     frontend: {},
134 | };
135 | 
136 | function setPaths(context) {
137 |     context.path.node = process.argv[0];
138 |     context.path.root = path.dirname(path.dirname(
139 |         fs.realpathSync(process.argv[1])
140 |     ));
141 |     context.path.kernel = path.join(context.path.root, "lib", "kernel.js");
142 |     context.path.images = path.join(context.path.root, "images");
143 | }
144 | 
145 | function readPackageJson(context) {
146 |     context.packageJSON = JSON.parse(
147 |         fs.readFileSync(path.join(context.path.root, "package.json"))
148 |     );
149 | }
150 | 
151 | function getPackageVersion(packageName) {
152 |     var packagePath = path.dirname(require.resolve(packageName));
153 |     var packageJSON = JSON.parse(
154 |         fs.readFileSync(path.join(packagePath, "package.json"))
155 |     );
156 |     return packageJSON.version;
157 | }
158 | 
159 | var FLAGS = [{
160 |     excludeIfInstaller: true,
161 |     flag: "help",
162 |     description: "show jp-CoffeeScript and Jupyter/IPython help",
163 |     parse: function(context, arg) {
164 |         context.args.frontend.push(arg);
165 |     },
166 |     showUsage: true,
167 | }, {
168 |     flag: "version",
169 |     description: "show jp-CoffeeScript version",
170 |     parse: function(context, arg) {
171 |         console.log(context.packageJSON.version);
172 |     },
173 |     exit: true,
174 | }, {
175 |     flag: "versions",
176 |     description: "show jp-CoffeeScript and library versions",
177 |     parse: function(context, arg) {
178 |         console.log("jp-coffeescript", context.packageJSON.version);
179 |         console.log("jmp", getPackageVersion("jmp"));
180 |         console.log("jp-kernel", getPackageVersion("jp-kernel"));
181 |         console.log("nel", getPackageVersion("nel"));
182 |         console.log("uuid", getPackageVersion("uuid"));
183 |         console.log("zeromq", getPackageVersion("zeromq"));
184 |     },
185 |     exit: true,
186 | }, {
187 |     prefixedFlag: "debug",
188 |     description: "enable debug log level",
189 |     parse: function(context, arg) {
190 |         DEBUG = true;
191 |         log = doLog;
192 | 
193 |         context.flag.debug = true;
194 |         context.args.kernel.push(arg);
195 |     },
196 | }, {
197 |     prefixedFlag: "help",
198 |     description: "show jp-CoffeeScript help",
199 |     parse: function(context, arg) {
200 |     },
201 |     showUsage: true,
202 |     exit: true,
203 | }, {
204 |     prefixedFlag: "hide-execution-result",
205 |     description: "do not show execution results",
206 |     parse: function(context, arg) {
207 |         context.flag.hideExecutionResult = true;
208 |         context.args.kernel.push("--hide-execution-result");
209 |     },
210 | }, {
211 |     prefixedFlag: "hide-undefined",
212 |     description: "do not show undefined results",
213 |     parse: function(context, arg) {
214 |         context.flag.hideUndefined = true;
215 |         context.args.kernel.push("--hide-undefined");
216 |     },
217 | }, {
218 |     prefixedFlag: "install=[local|global]",
219 |     description: "install jp-CoffeeScript kernel",
220 |     parse: function(context, arg) {
221 |         context.flag.install = getValue(arg);
222 |         if (context.flag.install !== "local" &&
223 |             context.flag.install !== "global") {
224 |             throw new Error(
225 |                 util.format("Unknown flag option '%s'\n", arg)
226 |             );
227 |         }
228 |     },
229 | }, {
230 |     deprecated: true,
231 |     prefixedFlag: "install-kernel",
232 |     description:
233 |     "same as --PREFIX-install=local (for backwards-compatibility)",
234 |     parse: function(context, arg) {
235 |         context.flag.install = "local";
236 |     },
237 | }, {
238 |     prefixedFlag: "protocol=version",
239 |     description: "set protocol version, e.g. 4.1",
240 |     parse: function(context, arg) {
241 |         context.protocol.version = getValue(arg);
242 |         context.protocol.majorVersion = parseInt(
243 |             context.protocol.version.split(".", 1)[0]
244 |         );
245 |     },
246 | }, {
247 |     prefixedFlag: "show-undefined",
248 |     description: "show undefined results",
249 |     parse: function(context, arg) {
250 |         context.flag.hideUndefined = false;
251 |         context.args.kernel.push("--show-undefined");
252 |     },
253 | }, {
254 |     prefixedFlag: "spec-path=[none|full]",
255 |     description: "set whether kernel spec uses full paths",
256 |     parse: function(context, arg) {
257 |         context.flag.specPath = getValue(arg);
258 |         if (context.flag.specPath !== "none" &&
259 |             context.flag.specPath !== "full") {
260 |             throw new Error(
261 |                 util.format("Unknown flag option '%s'\n", arg)
262 |             );
263 |         }
264 |     },
265 | }, {
266 |     prefixedFlag: "startup-script=path",
267 |     description: "run script on startup (path can be a file or a folder)",
268 |     parse: function(context, arg) {
269 |         context.flag.startup = fs.realpathSync(getValue(arg));
270 |         context.args.kernel.push(
271 |             "--startup-script=" + context.flag.startup
272 |         );
273 |     },
274 | }, {
275 |     prefixedFlag: "working-dir=path",
276 |     description:
277 |     "set session working directory (default = current working directory)",
278 |     parse: function(context, arg) {
279 |         context.flag.cwd = fs.realpathSync(getValue(arg));
280 |         context.args.kernel.push(
281 |             "--session-working-dir=" + context.flag.cwd
282 |         );
283 |     },
284 | }];
285 | 
286 | function parseCommandArgs(context, options) {
287 |     var flagPrefix = options.flagPrefix || "";
288 | 
289 |     context.args.kernel = [];
290 |     context.args.frontend = [
291 |         "jupyter",
292 |         "notebook",
293 |     ];
294 | 
295 |     /* eslint-disable complexity */
296 |     process.argv.slice(2).forEach(function(arg) {
297 |         var matched = false;
298 | 
299 |         for (var i = 0; i < FLAGS.length; i++) {
300 |             var flagDefinition =  FLAGS[i];
301 | 
302 |             if (flagDefinition.deprecated && !options.includeDeprecated) {
303 |                 continue;
304 |             }
305 | 
306 |             if (flagDefinition.excludeIfInstaller && options.installer) {
307 |                 continue;
308 |             }
309 | 
310 |             var fullFlag = getFullFlag(flagDefinition, options);
311 |             var flag = getFlag(fullFlag);
312 |             var value = getValue(fullFlag);
313 | 
314 |             // if arg doesn't match flag definition, continue iterating
315 |             if (value) {
316 |                 if (arg.lastIndexOf(flag, 0) !== 0) continue;
317 |             } else {
318 |                 if (arg !== flag) continue;
319 |             }
320 | 
321 |             matched = true;
322 |             flagDefinition.parse(context, arg);
323 |             if (flagDefinition.showUsage) showUsage(options);
324 |             if (flagDefinition.exit) process.exit(0);
325 |             break;
326 |         }
327 | 
328 |         if (matched) {
329 |             return;
330 | 
331 |         } else if (options.installer) {
332 |             throw new Error(util.format("Unknown flag '%s'\n", arg));
333 | 
334 |         } else if (arg.lastIndexOf("--" + flagPrefix + "-", 0) === 0) {
335 |             throw new Error(util.format("Unknown flag '%s'\n", arg));
336 | 
337 |         } else if (arg.lastIndexOf("--KernelManager.kernel_cmd=", 0) === 0) {
338 |             console.warn(util.format("Warning: Flag '%s' skipped", arg));
339 | 
340 |         } else {
341 |             context.args.frontend.push(arg);
342 |         }
343 |     });
344 |     /* eslint-enable complexity */
345 | 
346 |     if (context.flag.specPath === "full") {
347 |         context.args.kernel = [
348 |             context.path.node,
349 |             context.path.kernel,
350 |         ].concat(context.args.kernel);
351 |     } else {
352 |         context.args.kernel = [
353 |             (process.platform === "win32") ?
354 |                 "jp-coffee-kernel.cmd" :
355 |                 "jp-coffee-kernel",
356 |         ].concat(context.args.kernel);
357 |     }
358 | 
359 |     if (!context.flag.hasOwnProperty("hideUndefined")) {
360 |         if (options.showUndefined) {
361 |             context.args.kernel.push("--show-undefined");
362 |         } else {
363 |             context.args.kernel.push("--hide-undefined");
364 |         }
365 |     }
366 | 
367 |     context.args.kernel.push("{connection_file}");
368 | }
369 | 
370 | function getFullFlag(flagDefinition, config) {
371 |     return (flagDefinition.flag) ?
372 |         "--" + flagDefinition.flag :
373 |         (config.flagPrefix) ?
374 |             "--" + config.flagPrefix + "-" + flagDefinition.prefixedFlag :
375 |             "--" + flagDefinition.prefixedFlag;
376 | }
377 | 
378 | function getFlag(arg) {
379 |     var index = arg.indexOf("=");
380 |     return (index === -1) ? arg : arg.slice(0, index + 1);
381 | }
382 | 
383 | function getValue(arg) {
384 |     var index = arg.indexOf("=");
385 |     return (index === -1) ? "" : arg.slice(index + 1);
386 | }
387 | 
388 | var PREFIX_RE = /PREFIX/g;
389 | 
390 | function showUsage(options) {
391 |     var flagPrefix = options.flagPrefix || "";
392 |     var usageHeader = options.usageHeader;
393 |     var usageFooter = options.usageFooter;
394 | 
395 |     var usage = "";
396 | 
397 |     if (usageHeader) usage += usageHeader + "\n\n";
398 | 
399 |     usage += "The recognised options are:\n\n";
400 | 
401 |     var maxFlagLength = 0;
402 |     var flags = [];
403 |     for (var i = 0; i < FLAGS.length; i++) {
404 |         var flagDefinition = FLAGS[i];
405 | 
406 |         if (flagDefinition.deprecated && !options.includeDeprecated) {
407 |             continue;
408 |         }
409 | 
410 |         if (flagDefinition.excludeIfInstaller && options.installer) {
411 |             continue;
412 |         }
413 | 
414 |         var fullFlag = getFullFlag(flagDefinition, options);
415 | 
416 |         if (fullFlag.length > maxFlagLength) {
417 |             maxFlagLength = fullFlag.length;
418 |         }
419 | 
420 |         var description = flagDefinition.description;
421 |         description = description.replace(PREFIX_RE, flagPrefix);
422 | 
423 |         flags.push({
424 |             fullFlag: fullFlag,
425 |             description: description,
426 |         });
427 |     }
428 | 
429 |     flags.forEach(function(flag) {
430 |         var fullFlag = flag.fullFlag;
431 |         var description = flag.description;
432 | 
433 |         var indent = "    ";
434 |         var paddingLength = maxFlagLength - fullFlag.length + 2;
435 |         var padding = (new Array(paddingLength + 1)).join(" ");
436 |         var line = indent + fullFlag + padding + description + "\n";
437 | 
438 |         usage += line;
439 |     });
440 | 
441 |     if (usageFooter) usage += "\n" + usageFooter;
442 | 
443 |     console.log(usage);
444 | }
445 | 
446 | function setJupyterInfoAsync(context, callback) {
447 |     exec("jupyter --version", function(error, stdout, stderr) {
448 |         if (error) {
449 |             context.frontend.error = error;
450 |             setIPythonInfoAsync(context, callback);
451 |             return;
452 |         }
453 | 
454 |         context.args.frontend[0] = "jupyter";
455 | 
456 |         var version;
457 |         var majorVersion;
458 | 
459 |         // Parse version number before Jupyter 4.5.0
460 |         version = stdout.toString().trim();
461 |         majorVersion = parseInt(version.split(".")[0]);
462 | 
463 |         if (isNaN(majorVersion)) {
464 |             // Parse version number after Jupyter 4.5.0
465 |             var match = stdout.match(/^jupyter core\s+: (\d+\.\d+\.\d+)/m);
466 |             if (match) {
467 |                 version = match[1];
468 |                 majorVersion = parseInt(version.split(".")[0]);
469 |             } else {
470 |                 // Failed to parse the output of "jupyter --version"
471 |                 console.warn(
472 |                     "Warning: Unable to parse Jupyter version:",
473 |                     stdout
474 |                 );
475 |                 version = "unknown";
476 |                 majorVersion = Infinity;
477 |             }
478 |         }
479 | 
480 |         context.frontend.version = version;
481 |         context.frontend.majorVersion = majorVersion;
482 | 
483 |         if (callback) {
484 |             callback();
485 |         }
486 |     });
487 | }
488 | 
489 | function setIPythonInfoAsync(context, callback) {
490 |     exec("ipython --version", function(error, stdout, stderr) {
491 |         if (error) {
492 |             if (context.frontend.error) {
493 |                 console.error("Error running `jupyter --version`");
494 |                 console.error(context.frontend.error.toString());
495 |             }
496 |             console.error("Error running `ipython --version`");
497 |             console.error(error.toString());
498 |             log("CONTEXT:", context);
499 |             process.exit(1);
500 |         }
501 | 
502 |         context.args.frontend[0] = "ipython";
503 |         context.frontend.version = stdout.toString().trim();
504 |         context.frontend.majorVersion = parseInt(
505 |             context.frontend.version.split(".")[0]
506 |         );
507 |         if (isNaN(context.frontend.majorVersion)) {
508 |             console.error(
509 |                 "Error parsing IPython version:",
510 |                 context.frontend.version
511 |             );
512 |             log("CONTEXT:", context);
513 |             process.exit(1);
514 |         }
515 | 
516 |         if (callback) {
517 |             callback();
518 |         }
519 |     });
520 | }
521 | 
522 | function setProtocol(context) {
523 |     if (!context.protocol.version) {
524 |         if (context.frontend.majorVersion < 3) {
525 |             context.protocol.version = "4.1";
526 |             context.protocol.majorVersion = 4;
527 |         } else {
528 |             context.protocol.version = "5.1";
529 |             context.protocol.majorVersion = 5;
530 |         }
531 |     }
532 | 
533 |     context.args.kernel.push("--protocol=" + context.protocol.version);
534 | 
535 |     if (context.frontend.majorVersion < 3) {
536 |         context.args.frontend.push(util.format(
537 |             "--KernelManager.kernel_cmd=['%s']",
538 |             context.args.kernel.join("', '")
539 |         ));
540 |     } else if (context.args.frontend[1] === "console") {
541 |         context.args.frontend.push("--kernel=coffeescript");
542 |     }
543 | 
544 |     if (context.frontend.majorVersion < 3 &&
545 |         context.protocol.majorVersion >= 5) {
546 |         console.warn("Warning: Protocol v5+ requires Jupyter v3+");
547 |     }
548 | }
549 | 
550 | function installKernelAsync(context, callback) {
551 |     if (context.frontend.majorVersion < 3) {
552 |         if (context.flag.install) {
553 |             console.error(
554 |                 "Error: Installation of kernel specs requires Jupyter v3+"
555 |             );
556 |         }
557 | 
558 |         if (callback) {
559 |             callback();
560 |         }
561 | 
562 |         return;
563 |     }
564 | 
565 |     // Create temporary spec folder
566 |     var tmpdir = makeTmpdir();
567 |     var specDir = path.join(tmpdir, "coffeescript");
568 |     fs.mkdirSync(specDir);
569 | 
570 |     // Create spec file
571 |     var specFile = path.join(specDir, "kernel.json");
572 |     var spec = {
573 |         argv: context.args.kernel,
574 |         display_name: "CoffeeScript (Node.js)",
575 |         language: "coffeescript",
576 |     };
577 |     fs.writeFileSync(specFile, JSON.stringify(spec));
578 | 
579 |     // Copy logo files
580 |     var logoDir = path.join(context.path.images, "nodejs");
581 |     var logo32Src = path.join(logoDir, "js-green-32x32.png");
582 |     var logo32Dst = path.join(specDir, "logo-32x32.png");
583 |     var logo64Src = path.join(logoDir, "js-green-64x64.png");
584 |     var logo64Dst = path.join(specDir, "logo-64x64.png");
585 |     copyAsync(logo32Src, logo32Dst, function() {
586 |         copyAsync(logo64Src, logo64Dst, function() {
587 | 
588 |             // Install kernel spec
589 |             var args = [
590 |                 context.args.frontend[0],
591 |                 "kernelspec install --replace",
592 |                 specDir,
593 |             ];
594 |             if (context.flag.install !== "global") {
595 |                 args.push("--user");
596 |             }
597 |             var cmd = args.join(" ");
598 |             exec(cmd, function(error, stdout, stderr) {
599 | 
600 |                 // Remove temporary spec folder
601 |                 fs.unlinkSync(specFile);
602 |                 fs.unlinkSync(logo32Dst);
603 |                 fs.unlinkSync(logo64Dst);
604 |                 fs.rmdirSync(specDir);
605 |                 fs.rmdirSync(tmpdir);
606 | 
607 |                 if (error) {
608 |                     console.error(util.format("Error running `%s`", cmd));
609 |                     console.error(error.toString());
610 |                     if (stderr) console.error(stderr.toString());
611 |                     log("CONTEXT:", context);
612 |                     process.exit(1);
613 |                 }
614 | 
615 |                 if (callback) {
616 |                     callback();
617 |                 }
618 |             });
619 |         });
620 |     });
621 | }
622 | 
623 | function spawnFrontend(context) {
624 |     var cmd = context.args.frontend[0];
625 |     var args = context.args.frontend.slice(1);
626 |     var frontend = spawn(cmd, args, {
627 |         stdio: "inherit"
628 |     });
629 | 
630 |     // Relay SIGINT onto the frontend
631 |     var signal = "SIGINT";
632 |     process.on(signal, function() {
633 |         frontend.emit(signal);
634 |     });
635 | }
636 | 
637 | function makeTmpdir(maxAttempts) {
638 |     maxAttempts = maxAttempts ? maxAttempts : 10;
639 |     var attempts = 0;
640 | 
641 |     var tmpdir;
642 |     while (!tmpdir) {
643 |         attempts++;
644 |         try {
645 |             tmpdir = path.join(
646 |                 os.tmpdir(),
647 |                 crypto.randomBytes(16).toString("hex")
648 |             );
649 |             fs.mkdirSync(tmpdir);
650 |         } catch (e) {
651 |             if (attempts >= maxAttempts)
652 |                 throw e;
653 |             tmpdir = null;
654 |         }
655 |     }
656 | 
657 |     return tmpdir;
658 | }
659 | 
660 | function copyAsync(src, dst, callback) {
661 |     var readStream = fs.createReadStream(src);
662 |     var writeStream = fs.createWriteStream(dst);
663 |     if (callback) {
664 |         readStream.on("end", callback);
665 |     }
666 |     readStream.pipe(writeStream);
667 | }
668 | 
669 | module.exports = {
670 |     context: context,
671 |     copyAsync: copyAsync,
672 |     doLog: doLog,
673 |     dontLog: dontLog,
674 |     installKernelAsync: installKernelAsync,
675 |     log: log,
676 |     makeTmpdir: makeTmpdir,
677 |     parseCommandArgs: parseCommandArgs,
678 |     readPackageJson: readPackageJson,
679 |     setIPythonInfoAsync: setIPythonInfoAsync,
680 |     setJupyterInfoAsync: setJupyterInfoAsync,
681 |     setPaths: setPaths,
682 |     setProtocol: setProtocol,
683 |     spawnFrontend: spawnFrontend,
684 | };
685 | 
686 |
687 |
688 | 689 | 690 | 691 | 692 |
693 | 694 | 697 | 698 |
699 | 700 |
701 | Documentation generated by JSDoc 3.6.3 on Thu Sep 26 2019 19:06:01 GMT+0100 (BST) 702 |
703 | 704 | 705 | 706 | 707 | 708 | -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Bold-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n-riesco/jp-coffeescript/ff02c75082ddb162d1266eb61d7dba9ab383b451/docs/fonts/OpenSans-Bold-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Bold-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n-riesco/jp-coffeescript/ff02c75082ddb162d1266eb61d7dba9ab383b451/docs/fonts/OpenSans-Bold-webfont.woff -------------------------------------------------------------------------------- /docs/fonts/OpenSans-BoldItalic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n-riesco/jp-coffeescript/ff02c75082ddb162d1266eb61d7dba9ab383b451/docs/fonts/OpenSans-BoldItalic-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/OpenSans-BoldItalic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n-riesco/jp-coffeescript/ff02c75082ddb162d1266eb61d7dba9ab383b451/docs/fonts/OpenSans-BoldItalic-webfont.woff -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Italic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n-riesco/jp-coffeescript/ff02c75082ddb162d1266eb61d7dba9ab383b451/docs/fonts/OpenSans-Italic-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Italic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n-riesco/jp-coffeescript/ff02c75082ddb162d1266eb61d7dba9ab383b451/docs/fonts/OpenSans-Italic-webfont.woff -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Light-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n-riesco/jp-coffeescript/ff02c75082ddb162d1266eb61d7dba9ab383b451/docs/fonts/OpenSans-Light-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Light-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n-riesco/jp-coffeescript/ff02c75082ddb162d1266eb61d7dba9ab383b451/docs/fonts/OpenSans-Light-webfont.woff -------------------------------------------------------------------------------- /docs/fonts/OpenSans-LightItalic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n-riesco/jp-coffeescript/ff02c75082ddb162d1266eb61d7dba9ab383b451/docs/fonts/OpenSans-LightItalic-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/OpenSans-LightItalic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n-riesco/jp-coffeescript/ff02c75082ddb162d1266eb61d7dba9ab383b451/docs/fonts/OpenSans-LightItalic-webfont.woff -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n-riesco/jp-coffeescript/ff02c75082ddb162d1266eb61d7dba9ab383b451/docs/fonts/OpenSans-Regular-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n-riesco/jp-coffeescript/ff02c75082ddb162d1266eb61d7dba9ab383b451/docs/fonts/OpenSans-Regular-webfont.woff -------------------------------------------------------------------------------- /docs/global.html#Context: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Class: Context 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Class: Context

21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 |
30 | 31 |

Context(protocolVersion)

32 | 33 |
Context shared by tests 34 | (adapted from MessagingTestEngine in IJavascript)
35 | 36 | 37 |
38 | 39 |
40 |
41 | 42 | 43 | 44 | 45 |

Constructor

46 | 47 | 48 | 49 |

new Context(protocolVersion)

50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 |
Parameters:
65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 |
NameTypeDescription
protocolVersion 93 | 94 | 95 | string 96 | 97 | 98 | 99 | Messaging protocol version
111 | 112 | 113 | 114 | 115 | 116 | 117 |
118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 |
Source:
145 |
148 | 149 | 150 | 151 | 152 | 153 |
See:
154 |
155 | 158 |
159 | 160 | 161 | 162 |
163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 |
185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 |
206 | 207 |
208 | 209 | 210 | 211 | 212 |
213 | 214 | 217 | 218 |
219 | 220 |
221 | Documentation generated by JSDoc 3.6.3 on Thu Sep 26 2019 19:06:01 GMT+0100 (BST) 222 |
223 | 224 | 225 | 226 | 227 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Home 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Home

21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |

30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 |

jp-CoffeeScript: A CoffeeScript Kernel for the Jupyter Notebook

47 |

jp-CoffeeScript is an npm package that implements a 48 | CoffeeScript kernel for the Jupyter notebook). A Jupyter 49 | notebook combines the creation of rich-text documents (including equations, 50 | plots and videos) with the execution of code in a number of programming 51 | languages.

52 |

The execution of code is carried out by means of a kernel that implements the 53 | Jupyter messaging 54 | protocol. 55 | There are kernels available for Python, 56 | Julia, 57 | Ruby, 58 | Haskell and many other 59 | languages.

60 |

Proof-of-Concept and Goals

61 |

jp-CoffeeScript came to existence prompted by a number of requests from 62 | IJavascript users. See this 63 | issue for further details.

64 |

By publishing jp-CoffeeScript I'm seeking to:

65 |
    66 |
  • 67 |

    provide users with a "usable" CoffeeScript kernel that with some support may 68 | become a fully featured kernel,

    69 |
  • 70 |
  • 71 |

    reuse the IJavascript code and develop a Node.js library to implement Jupyter 72 | kernels for other languages.

    73 |
  • 74 |
75 |

Installation

76 |

Please, refer to the installation notes for 77 | IJavascript.

78 |

For example, in Ubuntu 16.04, you can run:

79 |
sudo apt-get install nodejs-legacy npm ipython ipython-notebook
 80 | sudo npm install -g jp-coffeescript
 81 | 
82 |

Usage

83 |

jp-CoffeeScript provides 5 executables: jp-coffee-install, 84 | jp-coffee-notebook, jp-coffee-console, jp-coffee-kernel and jp-coffee. 85 | Their purpose and basic use is described in the sections below. Please, refer to 86 | the usage notes for 87 | further details.

88 |

jp-coffee-install: jp-CoffeeScript kernel spec installer

89 |

jp-coffee-install registers the jp-CoffeeScript kernel with Jupyter, so that 90 | other tools (e.g. the Jupyter notebook) can invoke it. The following command 91 | flags are recognised:

92 |
--debug                   enable debug messages
 93 | --help                    show this help
 94 | --hide-undefined          do not show undefined results
 95 | --install=[local|global]  install kernel for current user or globally
 96 | --protocol=version        set messaging protocol version, e.g. 5.0
 97 | --show-undefined          show undefined results
 98 | --spec-path=[none|full]   set whether kernel spec uses full paths
 99 | --startup-script=path     run script on kernel startup
100 |                           (path can be a file or a folder)
101 | --version                 show kernel version
102 | --versions                show kernel and library versions
103 | --working-dir=path        set kernel working directory
104 |                           (default = current working directory)
105 | 
106 |

jp-coffee-notebook: jp-CoffeeScript notebook

107 |

After running jp-coffee-install, Jupyter notebook users can invoke the Jupyter 108 | notebook as usual. jp-coffee-notebook is provided for convenience to users of 109 | the IPython notebook prior to version 3. jp-coffee-notebook is a wrapper 110 | around ipython notebook. It extends the command flags accepted by ipython notebook with the following:

111 |
--help                       show jp-CoffeeScript and notebook help
112 | --jp-debug                   enable debug messages
113 | --jp-help                    show this help
114 | --jp-hide-undefined          do not show undefined results
115 | --jp-install=[local|global]  install kernel for current user or globally
116 | --jp-protocol=version        set protocol version, e.g. 5.0
117 | --jp-show-undefined          show undefined results
118 | --jp-spec-path=[none|full]   set whether kernel spec uses full paths
119 | --jp-startup-script=path     run script on startup
120 |                              (path can be a file or a folder)
121 | --jp-working-dir=path        set kernel working directory
122 |                              (default = current working directory)
123 | --version                    show kernel version
124 | --versions                   show kernel and library versions
125 | 
126 |

jp-coffee-console: jp-CoffeeScript console

127 |

jp-coffee-console is provided for convenience to users as a wrapper around 128 | jupyter console. The following command flags are recognised:

129 |
--help                       show jp-CoffeeScript and notebook help
130 | --jp-debug                   enable debug messages
131 | --jp-help                    show this help
132 | --jp-hide-undefined          do not show undefined results
133 | --jp-install=[local|global]  install kernel for current user or globally
134 | --jp-protocol=version        set protocol version, e.g. 5.0
135 | --jp-show-undefined          show undefined results
136 | --jp-spec-path=[none|full]   set whether kernel spec uses full paths
137 | --jp-startup-script=path     run script on startup
138 |                              (path can be a file or a folder)
139 | --jp-working-dir=path        set kernel working directory
140 |                              (default = current working directory)
141 | --version                    show kernel version
142 | --versions                   show kernel and library versions
143 | 
144 |

jp-coffee-kernel: jp-CoffeeScript kernel

145 |

jp-coffee-kernel is the executable invoked by Jupyter tools (e.g. the 146 | notebook) and that appears in the kernel spec that jp-coffee-install creates 147 | for jp-CoffeeScript. You won't need this command, unless you want to create a 148 | custom kernel spec.

149 |
Usage:
150 |     jp-coffee-kernel [options] connection_file
151 | 
152 | Options:
153 |     --debug                           enable debug messages
154 |     --hide-undefined                  do not show undefined results
155 |     --protocol=Major[.minor[.patch]]  set protocol version, e.g. 5.0
156 |     --session-working-dir=path        set session working directory
157 |     --show-undefined                  show undefined results
158 |     --startup-script=path             run script on startup
159 |                                       (path can be a file or a folder)
160 | 
161 |

jp-coffee: Deprecated CLI

162 |

jp-coffee is provided for backwards-compatibility. It will be removed in the 163 | next major-version update. Please, use jp-coffee-install or 164 | jp-coffee-notebook instead.

165 |

Contributions

166 |

First of all, thank you for taking the time to contribute. The maintenance of 167 | IJavascript is currently my priority. I would really appreciate some help. 168 | Please, read CONTRIBUTING and use the issue 169 | tracker for any 170 | contributions: support requests, bug reports, enhancement requests, pull 171 | requests, submission of tutorials, ...

172 |

TO DO

173 |
    174 |
  • Add tests
  • 175 |
176 |
177 | 178 | 179 | 180 | 181 | 182 | 183 |
184 | 185 | 188 | 189 |
190 | 191 |
192 | Documentation generated by JSDoc 3.6.3 on Thu Sep 26 2019 19:06:01 GMT+0100 (BST) 193 |
194 | 195 | 196 | 197 | 198 | -------------------------------------------------------------------------------- /docs/jp-coffeescript.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Source: jp-coffeescript.js 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Source: jp-coffeescript.js

21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |
29 |
#!/usr/bin/env node
 30 | 
 31 | /*
 32 |  * BSD 3-Clause License
 33 |  *
 34 |  * Copyright (c) 2015, Nicolas Riesco and others as credited in the AUTHORS file
 35 |  * All rights reserved.
 36 |  *
 37 |  * Redistribution and use in source and binary forms, with or without
 38 |  * modification, are permitted provided that the following conditions are met:
 39 |  *
 40 |  * 1. Redistributions of source code must retain the above copyright notice,
 41 |  * this list of conditions and the following disclaimer.
 42 |  *
 43 |  * 2. Redistributions in binary form must reproduce the above copyright notice,
 44 |  * this list of conditions and the following disclaimer in the documentation
 45 |  * and/or other materials provided with the distribution.
 46 |  *
 47 |  * 3. Neither the name of the copyright holder nor the names of its contributors
 48 |  * may be used to endorse or promote products derived from this software without
 49 |  * specific prior written permission.
 50 |  *
 51 |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 52 |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 53 |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 54 |  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 55 |  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 56 |  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 57 |  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 58 |  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 59 |  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 60 |  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 61 |  * POSSIBILITY OF SUCH DAMAGE.
 62 |  *
 63 |  */
 64 | var DEBUG = false;
 65 | 
 66 | var console = require("console");
 67 | var exec = require("child_process").exec;
 68 | var fs = require("fs");
 69 | var os = require("os");
 70 | var path = require("path");
 71 | var spawn = require("child_process").spawn;
 72 | var util = require("util");
 73 | 
 74 | var uuid = require("node-uuid");
 75 | 
 76 | var usage = (
 77 |     "CoffeeScript Notebook\n" +
 78 |     "\n" +
 79 |     "Usage:\n" +
 80 |     "\n" +
 81 |     "    jp-coffee <options>\n" +
 82 |     "\n" +
 83 |     "The recognised options are:\n" +
 84 |     "\n" +
 85 |     "    --jp-debug                   enable debug log level\n" +
 86 |     "    --jp-help                    show this help\n" +
 87 |     "    --jp-hide-undefined          do not show undefined results\n" +
 88 |     "    --jp-install=[local|global]  install CoffeeScript kernel\n" +
 89 |     "    --jp-install-kernel          same as --jp-install=local\n" +
 90 |     "                                 (for backwards-compatibility)\n" +
 91 |     "    --jp-protocol=version  set protocol version, e.g. 4.1\n" +
 92 |     "    --jp-working-dir=path  set CoffeeScript working directory\n" +
 93 |     "                           (default = current working directory)\n" +
 94 |     "    --version              show CoffeeScript version\n" +
 95 |     "\n" +
 96 |     "and any other options recognised by the Jupyter notebook; run:\n" +
 97 |     "\n" +
 98 |     "    jupyter notebook --help\n" +
 99 |     "\n" +
100 |     "for a full list.\n"
101 | );
102 | 
103 | /**
104 |  * @typedef Context
105 |  *
106 |  * @property            context
107 |  * @property            context.path
108 |  * @property {String}   context.path.node     Path to Node.js shell
109 |  * @property {String}   context.path.root     Path to CoffeeScript root folder
110 |  * @property {String}   context.path.kernel   Path to CoffeeScript kernel
111 |  * @property {String}   context.path.images   Path to CoffeeScript images folder
112 |  * @property {Object}   context.packageJSON   Contents of npm package.json
113 |  * @property            context.flag
114 |  * @property {Boolean}  context.flag.debug    --jp-debug
115 |  * @property {String}   context.flag.install  --jp-install=[local|global]
116 |  * @property {String}   context.flag.cwd      --jp-working-dir=path
117 |  * @property            context.args
118 |  * @property {String[]} context.args.kernel   Command arguments to run kernel
119 |  * @property {String[]} context.args.frontend Command arguments to run frontend
120 |  * @property            context.protocol
121 |  * @property {String}   context.protocol.version      Protocol version
122 |  * @property {Integer}  context.protocol.majorVersion Protocol major version
123 |  * @property            context.frontend
124 |  * @property {String}   context.frontend.version      Frontend version
125 |  * @property {Integer}  context.frontend.majorVersion Frontend major version
126 |  */
127 | 
128 | /**
129 |  * Script context
130 |  * @type Context
131 |  */
132 | var context = {
133 |     path: {},
134 |     packageJSON: undefined,
135 |     flag: {},
136 |     args: {},
137 |     protocol: {},
138 |     frontend: {},
139 | };
140 | 
141 | setPaths(context);
142 | readPackageJson(context);
143 | parseCommandArgs(context);
144 | setFrontendInfoAsync(context, function() {
145 |     setProtocol(context);
146 | 
147 |     if (DEBUG) console.log("CONTEXT:", context);
148 | 
149 |     installKernelAsync(context, function() {
150 |         if (!context.flag.install) {
151 |             spawnFrontend(context);
152 |         }
153 |     });
154 | });
155 | 
156 | function setPaths(context) {
157 |     context.path.node = process.argv[0];
158 |     context.path.root = path.dirname(path.dirname(
159 |         fs.realpathSync(process.argv[1])
160 |     ));
161 |     context.path.kernel = path.join(context.path.root, "lib", "kernel.js");
162 |     context.path.images = path.join(context.path.root, "images");
163 | }
164 | 
165 | function readPackageJson(context) {
166 |     context.packageJSON = JSON.parse(
167 |         fs.readFileSync(path.join(context.path.root, "package.json"))
168 |     );
169 | }
170 | 
171 | function parseCommandArgs(context) {
172 |     context.args.kernel = [
173 |         context.path.node,
174 |         context.path.kernel,
175 |     ];
176 |     context.args.frontend = [
177 |         "ipython",
178 |         "notebook",
179 |     ];
180 | 
181 |     process.argv.slice(2).forEach(function(e) {
182 |         var FLAG_JP_DEBUG = "--jp-debug";
183 |         var FLAG_JP_HELP = "--jp-help";
184 |         var FLAG_JP_HIDE_UNDEFINED = "--jp-hide-undefined";
185 |         var FLAG_JP_INSTALL = "--jp-install=";
186 |         var FLAG_JP_INSTALL_KERNEL = "--jp-install-kernel";
187 |         var FLAG_JP_PROTOCOL = "--jp-protocol=";
188 |         var FLAG_JP_WORKING_DIR = "--jp-working-dir=";
189 | 
190 |         if (e === FLAG_JP_DEBUG) {
191 |             context.flag.debug = DEBUG = true;
192 |             context.args.kernel.push("--debug");
193 | 
194 |         } else if (e === FLAG_JP_HELP) {
195 |             console.log(usage);
196 |             process.exit(0);
197 | 
198 |         } else if (e === FLAG_JP_HIDE_UNDEFINED) {
199 |             context.args.kernel.push("--hide-undefined");
200 | 
201 |         } else if (e.lastIndexOf(FLAG_JP_INSTALL, 0) === 0) {
202 |             context.flag.install = e.slice(FLAG_JP_INSTALL.length);
203 |             if (context.flag.install !== "local" &&
204 |                 context.flag.install !== "global") {
205 |                 console.error(
206 |                     util.format("Error: Unknown flag option '%s'\n", e)
207 |                 );
208 |                 console.error(usage);
209 |                 process.exit(1);
210 |             }
211 | 
212 |         } else if (e === FLAG_JP_INSTALL_KERNEL) {
213 |             context.flag.install = "local";
214 | 
215 |         } else if (e.lastIndexOf(FLAG_JP_PROTOCOL, 0) === 0) {
216 |             context.protocol.version = e.slice(FLAG_JP_PROTOCOL.length);
217 |             context.protocol.majorVersion = parseInt(
218 |                 context.protocol.version.split(".", 1)[0]
219 |             );
220 | 
221 |         } else if (e.lastIndexOf(FLAG_JP_WORKING_DIR, 0) === 0) {
222 |             context.flag.cwd = fs.realpathSync(
223 |                 e.slice(FLAG_JP_WORKING_DIR.length)
224 |             );
225 | 
226 |         } else if (e.lastIndexOf("--jp-", 0) === 0) {
227 |             console.error(util.format("Error: Unknown flag '%s'\n", e));
228 |             console.error(usage);
229 |             process.exit(1);
230 | 
231 |         } else if (e.lastIndexOf("--KernelManager.kernel_cmd=", 0) === 0) {
232 |             console.warn(util.format("Warning: Flag '%s' skipped", e));
233 | 
234 |         } else if (e === "--version") {
235 |             console.log(context.packageJSON.version);
236 |             process.exit(0);
237 | 
238 |         } else {
239 |             context.args.frontend.push(e);
240 |         }
241 |     });
242 | 
243 |     if (context.flag.cwd) {
244 |         context.args.kernel.push("--session-working-dir=" + context.flag.cwd);
245 |     }
246 | 
247 |     context.args.kernel.push("{connection_file}");
248 | }
249 | 
250 | function setFrontendInfoAsync(context, callback) {
251 |     exec("ipython --version", function(error, stdout, stderr) {
252 |         if (error) {
253 |             console.error("Error running `ipython --version`");
254 |             console.error(error.toString());
255 |             if (stderr) console.error(stderr.toString());
256 |             if (DEBUG) console.log("CONTEXT:", context);
257 |             process.exit(1);
258 |         }
259 | 
260 |         context.frontend.version = stdout.toString().trim();
261 |         context.frontend.majorVersion = parseInt(
262 |             context.frontend.version.split(".")[0]
263 |         );
264 |         if (isNaN(context.frontend.majorVersion)) {
265 |             console.error(
266 |                 "Error parsing IPython version:",
267 |                 context.version.frontend
268 |             );
269 |             if (DEBUG) console.log("CONTEXT:", context);
270 |             process.exit(1);
271 |         }
272 | 
273 |         if (callback) {
274 |             callback();
275 |         }
276 |     });
277 | }
278 | 
279 | function setProtocol(context) {
280 |     if (!context.protocol.version) {
281 |         if (context.frontend.majorVersion < 3) {
282 |             context.protocol.version = "4.1";
283 |             context.protocol.majorVersion = 4;
284 |         } else {
285 |             context.protocol.version = "5.0";
286 |             context.protocol.majorVersion = 5;
287 |         }
288 |     }
289 | 
290 |     context.args.kernel.push("--protocol=" + context.protocol.version);
291 | 
292 |     if (context.frontend.majorVersion < 3) {
293 |         context.args.frontend.push(util.format(
294 |             "--KernelManager.kernel_cmd=['%s']",
295 |             context.args.kernel.join("', '")
296 |         ));
297 |     }
298 | 
299 |     if (context.frontend.majorVersion < 3 &&
300 |         context.protocol.majorVersion >= 5) {
301 |         console.warn("Warning: Protocol v5+ requires IPython v3+");
302 |     }
303 | }
304 | 
305 | function installKernelAsync(context, callback) {
306 |     if (context.frontend.majorVersion < 3) {
307 |         if (context.flag.install) {
308 |             console.error(
309 |                 "Error: Installation of kernel specs requires IPython v3+"
310 |             );
311 |         }
312 | 
313 |         if (callback) {
314 |             callback();
315 |         }
316 | 
317 |         return;
318 |     }
319 | 
320 |     // Create temporary spec folder
321 |     var tmpdir = makeTmpdir();
322 |     var specDir = path.join(tmpdir, "coffeescript");
323 |     fs.mkdirSync(specDir);
324 | 
325 |     // Create spec file
326 |     var specFile = path.join(specDir, "kernel.json");
327 |     var spec = {
328 |         argv: context.args.kernel,
329 |         display_name: "CoffeeScript (Node.js)",
330 |         language: "coffeescript",
331 |     };
332 |     fs.writeFileSync(specFile, JSON.stringify(spec));
333 | 
334 |     // Copy logo files
335 |     var logo32Src = path.join(context.path.images, "logo-32x32.png");
336 |     var logo32Dst = path.join(specDir, "logo-32x32.png");
337 |     var logo64Src = path.join(context.path.images, "logo-64x64.png");
338 |     var logo64Dst = path.join(specDir, "logo-64x64.png");
339 |     copyAsync(logo32Src, logo32Dst, function() {
340 |         copyAsync(logo64Src, logo64Dst, function() {
341 | 
342 |             // Install kernel spec
343 |             var cmd = "ipython kernelspec install --replace " + specDir;
344 |             if (context.flag.install !== "global") {
345 |                 cmd += "  --user";
346 |             }
347 |             exec(cmd, function(error, stdout, stderr) {
348 | 
349 |                 // Remove temporary spec folder
350 |                 fs.unlinkSync(specFile);
351 |                 fs.unlinkSync(logo32Dst);
352 |                 fs.unlinkSync(logo64Dst);
353 |                 fs.rmdirSync(specDir);
354 |                 fs.rmdirSync(tmpdir);
355 | 
356 |                 if (error) {
357 |                     console.error(util.format("Error running `%s`", cmd));
358 |                     console.error(error.toString());
359 |                     if (stderr) console.error(stderr.toString());
360 |                     if (DEBUG) console.log("CONTEXT:", context);
361 |                     process.exit(1);
362 |                 }
363 | 
364 |                 if (callback) {
365 |                     callback();
366 |                 }
367 |             });
368 |         });
369 |     });
370 | }
371 | 
372 | function spawnFrontend(context) {
373 |     var cmd = context.args.frontend[0];
374 |     var args = context.args.frontend.slice(1);
375 |     var frontend = spawn(cmd, args, {
376 |         stdio: "inherit"
377 |     });
378 | 
379 |     // Relay SIGINT onto the frontend
380 |     var signal = "SIGINT";
381 |     process.on(signal, function() {
382 |         frontend.emit(signal);
383 |     });
384 | }
385 | 
386 | function makeTmpdir(maxAttempts) {
387 |     maxAttempts = maxAttempts ? maxAttempts : 10;
388 |     var attempts = 0;
389 | 
390 |     var tmpdir;
391 |     while (!tmpdir) {
392 |         attempts++;
393 |         try {
394 |             tmpdir = path.join(os.tmpdir(), uuid.v4());
395 |             fs.mkdirSync(tmpdir);
396 |         } catch (e) {
397 |             if (attempts >= maxAttempts)
398 |                 throw e;
399 |             tmpdir = null;
400 |         }
401 |     }
402 | 
403 |     return tmpdir;
404 | }
405 | 
406 | function copyAsync(src, dst, callback) {
407 |     var readStream = fs.createReadStream(src);
408 |     var writeStream = fs.createWriteStream(dst);
409 |     if (callback) {
410 |         readStream.on("end", callback);
411 |     }
412 |     readStream.pipe(writeStream);
413 | }
414 | 
415 |
416 |
417 | 418 | 419 | 420 | 421 |
422 | 423 | 426 | 427 |
428 | 429 |
430 | Documentation generated by JSDoc 3.4.0 on Thu Aug 18 2016 22:55:20 GMT+0100 (BST) 431 |
432 | 433 | 434 | 435 | 436 | 437 | -------------------------------------------------------------------------------- /docs/lib_kernel.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Source: lib/kernel.js 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Source: lib/kernel.js

21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |
29 |
#!/usr/bin/env node
 30 | 
 31 | /*
 32 |  * BSD 3-Clause License
 33 |  *
 34 |  * Copyright (c) 2015, Nicolas Riesco and others as credited in the AUTHORS file
 35 |  * All rights reserved.
 36 |  *
 37 |  * Redistribution and use in source and binary forms, with or without
 38 |  * modification, are permitted provided that the following conditions are met:
 39 |  *
 40 |  * 1. Redistributions of source code must retain the above copyright notice,
 41 |  * this list of conditions and the following disclaimer.
 42 |  *
 43 |  * 2. Redistributions in binary form must reproduce the above copyright notice,
 44 |  * this list of conditions and the following disclaimer in the documentation
 45 |  * and/or other materials provided with the distribution.
 46 |  *
 47 |  * 3. Neither the name of the copyright holder nor the names of its contributors
 48 |  * may be used to endorse or promote products derived from this software without
 49 |  * specific prior written permission.
 50 |  *
 51 |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 52 |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 53 |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 54 |  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 55 |  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 56 |  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 57 |  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 58 |  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 59 |  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 60 |  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 61 |  * POSSIBILITY OF SUCH DAMAGE.
 62 |  *
 63 |  */
 64 | 
 65 | var console = require("console");
 66 | var fs = require("fs");
 67 | var path = require("path");
 68 | 
 69 | var cs = require("coffeescript");
 70 | var csOptions = {bare: true};
 71 | 
 72 | var Kernel = require("jp-kernel");
 73 | 
 74 | 
 75 | // Parse command arguments
 76 | var config = parseCommandArguments();
 77 | 
 78 | 
 79 | // Setup logging helpers
 80 | var log;
 81 | var dontLog = function dontLog() {};
 82 | var doLog = function doLog() {
 83 |     process.stderr.write("KERNEL: ");
 84 |     console.error.apply(this, arguments);
 85 | };
 86 | 
 87 | if (process.env.DEBUG) {
 88 |     global.DEBUG = true;
 89 | 
 90 |     try {
 91 |         doLog = require("debug")("KERNEL:");
 92 |     } catch (err) {}
 93 | }
 94 | 
 95 | log = global.DEBUG ? doLog : dontLog;
 96 | 
 97 | 
 98 | // Setup session initialisation
 99 | config.startupCallback = function startupCallback() {
100 |     var requirePath = require.resolve("coffeescript/register");
101 |     var code = "require('" + requirePath + "');";
102 |     this.session.execute(code, {
103 |         onSuccess: function() {
104 |             log("startupCallback: '" + code + "' run successfuly");
105 |         },
106 |         onError: function() {
107 |             log("startupCallback: '" + code + "' failed to run");
108 |         },
109 |     });
110 | };
111 | 
112 | 
113 | // Setup coffeescript transpiler
114 | config.transpile = function(code) {
115 |     return cs.compile(code, csOptions);
116 | };
117 | 
118 | // Start kernel
119 | var kernel = new Kernel(config);
120 | 
121 | kernel.handlers.is_complete_request = function is_complete_request(request) {
122 |     request.respond(this.iopubSocket, "status", {
123 |         execution_state: "busy"
124 |     });
125 | 
126 |     var content;
127 |     try {
128 |         config.transpile(request.content.code);
129 |         content = {
130 |             status: "complete",
131 |         };
132 |     } catch (err) {
133 |         content = {
134 |             status: "incomplete",
135 |             indent: "",
136 |         };
137 |     }
138 | 
139 |     request.respond(
140 |         this.shellSocket,
141 |         "is_complete_reply",
142 |         content,
143 |         {},
144 |         this.protocolVersion
145 |     );
146 | 
147 |     request.respond(this.iopubSocket, "status", {
148 |         execution_state: "idle"
149 |     });
150 | };
151 | 
152 | // Interpret a SIGINT signal as a request to interrupt the kernel
153 | process.on("SIGINT", function() {
154 |     log("Interrupting kernel");
155 |     kernel.restart(); // TODO(NR) Implement kernel interruption
156 | });
157 | 
158 | 
159 | /**
160 |  * Parse command arguments
161 |  *
162 |  * @returns {module:jp-kernel~Config} Kernel config
163 |  */
164 | function parseCommandArguments() {
165 |     var config = {
166 |         cwd: process.cwd(),
167 |         hideUndefined: true,
168 |         protocolVersion: "5.1",
169 |     };
170 | 
171 |     var usage = (
172 |         "Usage: node kernel.js " +
173 |         "[--debug] " +
174 |         "[--hide-undefined] " +
175 |         "[--protocol=Major[.minor[.patch]]] " +
176 |         "[--session-working-dir=path] " +
177 |         "[--show-undefined] " +
178 |         "[--startup-script=path] " +
179 |         "connection_file"
180 |     );
181 | 
182 |     var FLAGS = [
183 |         ["--debug", function() {
184 |             config.debug = true;
185 |         }],
186 |         ["--hide-undefined", function() {
187 |             config.hideUndefined = true;
188 |         }],
189 |         ["--protocol=", function(setting) {
190 |             config.protocolVersion = setting;
191 |         }],
192 |         ["--session-working-dir=", function(setting) {
193 |             config.cwd = setting;
194 |         }],
195 |         ["--show-undefined", function() {
196 |             config.hideUndefined = false;
197 |         }],
198 |         ["--startup-script=", function(setting) {
199 |             config.startupScript = setting;
200 |         }],
201 |     ];
202 | 
203 |     try {
204 |         var connectionFile;
205 | 
206 |         process.argv.slice(2).forEach(function(arg) {
207 |             for (var i = 0; i < FLAGS.length; i++) {
208 |                 var flag = FLAGS[i];
209 |                 var label = flag[0];
210 |                 var action = flag[1];
211 | 
212 |                 var matchesFlag = (arg.indexOf(label) === 0);
213 |                 if (matchesFlag) {
214 |                     var setting = arg.slice(label.length);
215 |                     action(setting);
216 |                     return;
217 |                 }
218 |             }
219 | 
220 |             if (connectionFile) {
221 |                 throw new Error("Error: too many arguments");
222 |             }
223 | 
224 |             connectionFile = arg;
225 |         });
226 | 
227 |         if (!connectionFile) {
228 |             throw new Error("Error: missing connection_file");
229 |         }
230 | 
231 |         config.connection = JSON.parse(fs.readFileSync(connectionFile));
232 | 
233 |     } catch (e) {
234 |         console.error("KERNEL: ARGV:", process.argv);
235 |         console.error(usage);
236 |         throw e;
237 |     }
238 | 
239 |     var nodeVersion;
240 |     var protocolVersion;
241 |     var jpVersion;
242 |     var majorVersion = parseInt(config.protocolVersion.split(".")[0]);
243 |     if (majorVersion <= 4) {
244 |         nodeVersion = process.versions.node.split(".")
245 |             .map(function(v) {
246 |                 return parseInt(v, 10);
247 |             });
248 |         protocolVersion = config.protocolVersion.split(".")
249 |             .map(function(v) {
250 |                 return parseInt(v, 10);
251 |             });
252 |         config.kernelInfoReply = {
253 |             "language": "coffeescript",
254 |             "language_version": nodeVersion,
255 |             "protocol_version": protocolVersion,
256 |         };
257 |     } else {
258 |         nodeVersion = process.versions.node;
259 |         protocolVersion = config.protocolVersion;
260 |         var packageJsonPath = path.join(__dirname, "..", "package.json");
261 |         jpVersion = JSON.parse(fs.readFileSync(packageJsonPath)).version;
262 |         config.kernelInfoReply = {
263 |             "protocol_version": protocolVersion,
264 |             "implementation": "jp-coffeescript",
265 |             "implementation_version": jpVersion,
266 |             "language_info": {
267 |                 "name": "coffeescript",
268 |                 "version": nodeVersion,
269 |                 "mimetype": "application/coffeescript",
270 |                 "file_extension": ".coffee",
271 |             },
272 |             "banner": (
273 |                 "jp-coffeescript v" + jpVersion + "\n" +
274 |                 "https://github.com/n-riesco/jp-coffeescript\n"
275 |             ),
276 |             "help_links": [{
277 |                 "text": "jp-coffeescript Homepage",
278 |                 "url": "https://github.com/n-riesco/jp-coffeescript",
279 |             }],
280 |         };
281 |     }
282 | 
283 |     return config;
284 | }
285 | 
286 |
287 |
288 | 289 | 290 | 291 | 292 |
293 | 294 | 297 | 298 |
299 | 300 |
301 | Documentation generated by JSDoc 3.6.3 on Thu Sep 26 2019 19:06:01 GMT+0100 (BST) 302 |
303 | 304 | 305 | 306 | 307 | 308 | -------------------------------------------------------------------------------- /docs/lib_kernel.spec.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Source: lib/kernel.spec.js 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Source: lib/kernel.spec.js

21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |
29 |
/*
 30 |  * BSD 3-Clause License
 31 |  *
 32 |  * Copyright (c) 2017, Nicolas Riesco and others as credited in the AUTHORS file
 33 |  * All rights reserved.
 34 |  *
 35 |  * Redistribution and use in source and binary forms, with or without
 36 |  * modification, are permitted provided that the following conditions are met:
 37 |  *
 38 |  * 1. Redistributions of source code must retain the above copyright notice,
 39 |  * this list of conditions and the following disclaimer.
 40 |  *
 41 |  * 2. Redistributions in binary form must reproduce the above copyright notice,
 42 |  * this list of conditions and the following disclaimer in the documentation
 43 |  * and/or other materials provided with the distribution.
 44 |  *
 45 |  * 3. Neither the name of the copyright holder nor the names of its contributors
 46 |  * may be used to endorse or promote products derived from this software without
 47 |  * specific prior written permission.
 48 |  *
 49 |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 50 |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 51 |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 52 |  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 53 |  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 54 |  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 55 |  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 56 |  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 57 |  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 58 |  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 59 |  * POSSIBILITY OF SUCH DAMAGE.
 60 |  *
 61 |  */
 62 | 
 63 | var assert = require("assert");
 64 | var console = require("console");
 65 | var crypto = require("crypto");
 66 | var fs = require("fs");
 67 | var os = require("os");
 68 | var path = require("path");
 69 | var spawn = require("child_process").spawn;
 70 | var uuid = require("uuid");
 71 | 
 72 | var jmp = require("jmp");
 73 | var zmq = jmp.zmq;
 74 | 
 75 | // Setup logging helpers
 76 | var LABEL = "JP-COFFEE-KERNEL: TEST";
 77 | var log;
 78 | var dontLog = function dontLog() {};
 79 | var doLog = function doLog() {
 80 |     process.stderr.write(LABEL);
 81 |     console.error.apply(this, arguments);
 82 | };
 83 | 
 84 | if (process.env.DEBUG) {
 85 |     global.DEBUG = true;
 86 | 
 87 |     try {
 88 |         doLog = require("debug")(LABEL);
 89 |     } catch (err) {}
 90 | }
 91 | 
 92 | log = global.DEBUG ? doLog : dontLog;
 93 | 
 94 | /**
 95 |  * @typedef     {Object} Message
 96 |  * @description IPython/Jupyter message
 97 |  *
 98 |  * @property    {Array}   [message.idents]        Identities
 99 |  * @property    {Object}  [message.header]        Header
100 |  * @property    {Object}  [message.parent_header] Parent header
101 |  * @property    {Object}  [message.metadata]      Message metadata
102 |  * @property    {Object}  [message.content]       Message content
103 |  */
104 | 
105 | /**
106 |  * Callback to handle kernel messages
107 |  *
108 |  * @callback  onMessageCallback
109 |  * @param    {string}  socketName Socket name
110 |  * @param    {Message} message    IPython/Jupyter message
111 |  */
112 | 
113 | /* eslint-disable max-len */
114 | /**
115 |  * @class     Context
116 |  * @classdesc Context shared by tests
117 |  *            (adapted from MessagingTestEngine in IJavascript)
118 |  *            @see {@link https://github.com/n-riesco/ijavascript/tree/9c2cf6a94575b5016b4a6f3cbe380ab1fcfbcf76}
119 |  * @param {string} protocolVersion Messaging protocol version
120 |  */
121 | function Context(protocolVersion) {
122 | /* eslint-disable max-len */
123 | 
124 |     /**
125 |      * @member          version
126 |      * @member {String} version.protocol Messaging protocol version
127 |      */
128 |     this.version = {
129 |         protocol: protocolVersion || "5.1",
130 |     };
131 | 
132 |     /**
133 |      * @member          path
134 |      * @member {String} path.node           Path to node
135 |      * @member {String} path.root           Path to package folder
136 |      * @member {String} path.kernel         Path to kernel
137 |      * @member {String} path.connectionFile Path to kernel connection file
138 |      */
139 |     this.path = {};
140 | 
141 |     /**
142 |      * @member           connection
143 |      * @member {String}  connection.transport    Transport protocol (e.g. "tcp")
144 |      * @member {String}  connection.ip           IP address (e.g. "127.0.0.1")
145 |      * @member {String}  connection.signature_scheme
146 |      *                                           Signature scheme
147 |      *                                           (e.g. "hmac-sha256")
148 |      * @member {String}  connection.key          Hashing key (e.g. uuid.v4())
149 |      * @member {Integer} connection.hb_port      HeartBeat port
150 |      * @member {Integer} connection.shell_port   Shell port
151 |      * @member {Integer} connection.stdin_port   Stdin port
152 |      * @member {Integer} connection.iopub_port   IOPub port
153 |      * @member {Integer} connection.control_port Control port
154 |      */
155 |     this.connection = {};
156 | 
157 |     /**
158 |      * @member                     socket
159 |      * @member {module:jmp~Socket} socket.control Control socket
160 |      * @member {module:jmp~Socket} socket.hb      HearBeat socket
161 |      * @member {module:jmp~Socket} socket.iopub   IOPub socket
162 |      * @member {module:jmp~Socket} socket.shell   Shell socket
163 |      * @member {module:jmp~Socket} socket.stdin   Stdin socket
164 |      */
165 |     this.socket = {};
166 | 
167 |     /**
168 |      * @member      {module:child_process~ChildProcess} kernel
169 |      * @description Kernel instance
170 |      */
171 |     this.kernel = null;
172 | 
173 |     /**
174 |      * @member      {onMessageCallback} [onMessage]
175 |      * @description Callback to handle kernel messages
176 |      */
177 |     this.onMessage = null;
178 | }
179 | 
180 | /**
181 |  * @method      init
182 |  * @param       {function} done
183 |  * @description Initialise the messaging test engine
184 |  */
185 | Context.prototype.init = function(done) {
186 |     this._setPaths();
187 |     this._initSockets();
188 |     this._createConnectionFile();
189 |     this._initKernel(done);
190 | };
191 | 
192 | /**
193 |  * @method      dispose
194 |  * @description Dispose the messaging test engine
195 |  */
196 | Context.prototype.dispose = function() {
197 |     this._disposeKernel();
198 |     this._removeConnectionFile();
199 |     this._disposeSockets();
200 | };
201 | 
202 | /**
203 |  * @method      _setPaths
204 |  * @description Set up paths
205 |  * @private
206 |  */
207 | Context.prototype._setPaths = function() {
208 |     log("Setting paths");
209 | 
210 |     this.path.node = process.argv[0];
211 |     this.path.root = path.dirname(__dirname);
212 |     this.path.kernel = path.join(this.path.root, "lib", "kernel.js");
213 | 
214 |     this.path.connectionFile = path.join(
215 |         os.tmpdir(),
216 |         "conn-" + uuid.v4() + ".json"
217 |     );
218 | };
219 | 
220 | /**
221 |  * @method      _createConnectionFile
222 |  * @description Create connection file
223 |  * @private
224 |  */
225 | Context.prototype._createConnectionFile = function() {
226 |     log("Creating connection file");
227 | 
228 |     fs.writeFileSync(
229 |         this.path.connectionFile,
230 |         JSON.stringify(this.connection)
231 |     );
232 | };
233 | 
234 | /**
235 |  * @method      _removeConnectionFile
236 |  * @description Remove connection file
237 |  * @private
238 |  */
239 | Context.prototype._removeConnectionFile = function() {
240 |     log("Removing connection file");
241 | 
242 |     try {
243 |         fs.unlinkSync(this.path.connectionFile);
244 |     } catch (e) {
245 |         console.error(e.message);
246 |     }
247 | };
248 | 
249 | /**
250 |  * @method      _initSockets
251 |  * @description Setup ZMQ sockets and message listeners
252 |  * @private
253 |  */
254 | Context.prototype._initSockets = function() {
255 |     log("Setting up ZMQ sockets");
256 | 
257 |     var transport = "tcp";
258 |     var ip = "127.0.0.1";
259 |     var address = transport + "://" + ip + ":";
260 |     var scheme = "sha256";
261 |     var key = crypto.randomBytes(256).toString("base64");
262 | 
263 |     this.connection = {
264 |         transport: transport,
265 |         ip: ip,
266 |         signature_scheme: "hmac-" + scheme,
267 |         key: key,
268 |     };
269 | 
270 |     var socketNames = ["hb", "shell", "stdin", "iopub", "control"];
271 |     var socketTypes = ["req", "dealer", "dealer", "sub", "dealer"];
272 |     for (var i = 0, attempts = 0; i < socketNames.length; attempts++) {
273 |         var socketName = socketNames[i];
274 |         var socketType = socketTypes[i];
275 |         var socket = (socketName === "hb") ?
276 |             new zmq.Socket(socketType) :
277 |             new jmp.Socket(socketType, scheme, key);
278 |         var port = Math.floor(1024 + Math.random() * (65536 - 1024));
279 | 
280 |         try {
281 |             socket.connect(address + port);
282 |             this.connection[socketName + "_port"] = port;
283 |             this.socket[socketName] = socket;
284 |             i++;
285 |         } catch (e) {
286 |             log(e.stack);
287 |         }
288 | 
289 |         if (attempts >= 100) {
290 |             throw new Error("can't bind to any local ports");
291 |         }
292 |     }
293 | 
294 |     this.socket.iopub.subscribe("");
295 | 
296 |     log("Setting up message listeners");
297 | 
298 |     Object.getOwnPropertyNames(this.socket).forEach((function(socketName) {
299 |         this.socket[socketName]
300 |             .on("message", this._onMessage.bind(this, socketName));
301 |     }).bind(this));
302 | };
303 | 
304 | /**
305 |  * @method      _disposeSockets
306 |  * @description Dispose ZMQ sockets
307 |  * @private
308 |  */
309 | Context.prototype._disposeSockets = function() {
310 |     log("Disposing ZMQ sockets");
311 | 
312 |     this.socket.control.close();
313 |     this.socket.hb.close();
314 |     this.socket.iopub.close();
315 |     this.socket.shell.close();
316 |     this.socket.stdin.close();
317 | };
318 | 
319 | /**
320 |  * @method      _initKernel
321 |  * @description Setup IJavascript kernel
322 |  * @param       {function} done
323 |  * @private
324 |  */
325 | Context.prototype._initKernel = function(done) {
326 |     log("Initialising a kernel");
327 | 
328 |     var cmd = this.path.node;
329 |     var args = [
330 |         this.path.kernel,
331 |         "--protocol=" + this.version.protocol,
332 |         this.path.connectionFile,
333 |     ];
334 |     if (global.DEBUG) args.push("--debug");
335 |     var config = {
336 |         stdio: "inherit"
337 |     };
338 |     log("spawn", cmd, args, config);
339 |     this.kernel = spawn(cmd, args, config);
340 | 
341 |     if (done) {
342 |         this._waitUntilConnect(function() {
343 |             this._waitUntilKernelIsIdle(done);
344 |         }.bind(this));
345 |     }
346 | };
347 | 
348 | /**
349 |  * @method      _disposeKernel
350 |  * @description Dispose IJavascript kernel
351 |  * @private
352 |  */
353 | Context.prototype._disposeKernel = function() {
354 |     log("Disposing IJavascript kernel");
355 | 
356 |     if (this.kernel) {
357 |         this.kernel.kill("SIGTERM");
358 |     }
359 | };
360 | 
361 | /**
362 |  * @method      _waitUntilConnect
363 |  * @description Wait for ZMQ sockets to connect
364 |  * @param       {function} done
365 |  * @private
366 |  */
367 | Context.prototype._waitUntilConnect = function(done) {
368 |     log("Waiting for ZMQ sockets to connect");
369 | 
370 |     var socketNames = ["hb", "shell", "iopub", "control"];
371 | 
372 |     var waitGroup = socketNames.length;
373 |     function onConnect() {
374 |         waitGroup--;
375 |         if (waitGroup === 0) {
376 |             for (var i = 0; i < socketNames.length; i++) {
377 |                 this.socket[socketNames[i]].unmonitor();
378 |             }
379 |             if (done) done();
380 |         }
381 |     }
382 | 
383 |     for (var j = 0; j < socketNames.length; j++) {
384 |         this.socket[socketNames[j]].on("connect", onConnect.bind(this));
385 |         this.socket[socketNames[j]].monitor();
386 |     }
387 | };
388 | 
389 | /**
390 |  * @method      _waitUntilKernelIsIdle
391 |  * @description Wait until kernel is idle
392 |  * @param       {function} done
393 |  * @private
394 |  */
395 | Context.prototype._waitUntilKernelIsIdle = function(done) {
396 |     log("Waiting until kernel is idle");
397 | 
398 |     var onMessage = this.onMessage;
399 | 
400 |     var request = this.run("", function(socketName, response) {
401 |         if (response.parent_header.msg_id !== request.header.msg_id) {
402 |             return;
403 |         }
404 | 
405 |         if (socketName === "iopub") {
406 |             if (response.header.msg_type === "status") {
407 |                 if (response.content.execution_state === "idle") {
408 |                     this.onMessage = onMessage;
409 |                     if (done) done();
410 |                 }
411 |             }
412 |         }
413 |     });
414 | };
415 | 
416 | /**
417 |  * @method      _onMessage
418 |  * @description Handle kernel message
419 |  * @param       {string}  socketName Socket name
420 |  * @param       {Message} message    IPython/Jupyter message
421 |  * @private
422 |  */
423 | Context.prototype._onMessage = function(socketName, message) {
424 |     log("Received on " + socketName, message);
425 | 
426 |     if (this.onMessage) this.onMessage(socketName, message);
427 | };
428 | 
429 | /**
430 |  * @method      run
431 |  * @description Send kernel an execution request
432 |  *
433 |  * @param       {string}            code        Code to be run by kernel
434 |  * @param       {onMessageCallback} [onMessage] Kernel message handler
435 |  *
436 |  * @returns     {Message}           message     IPython/Jupyter message
437 |  */
438 | Context.prototype.run = function(code, onMessage) {
439 |     log("Running:", code);
440 | 
441 |     var message = {
442 |         "header": {
443 |             "msg_type": "execute_request"
444 |         },
445 |         "content": {
446 |             "code": code
447 |         }
448 |     };
449 | 
450 |     if (!(message instanceof jmp.Message)) {
451 |         message = new jmp.Message(message);
452 |     }
453 | 
454 |     if (!message.header.msg_id) {
455 |         message.header.msg_id = uuid.v4();
456 |     }
457 | 
458 |     if (!message.header.username) {
459 |         message.header.username = "user";
460 |     }
461 | 
462 |     if (!message.header.session) {
463 |         message.header.session = uuid.v4();
464 |     }
465 | 
466 |     if (!message.header.version) {
467 |         message.header.version = this.version.protocol;
468 |     }
469 | 
470 |     this.onMessage = onMessage;
471 |     this.socket.shell.send(message);
472 | 
473 |     return message;
474 | };
475 | 
476 | describe("jp-coffee-kernel", function() {
477 |     var ctx = new Context();
478 | 
479 |     before(function(done) {
480 |         this.timeout(5000);
481 |         ctx.init(done);
482 |     });
483 | 
484 |     after(function() {
485 |         ctx.dispose();
486 |     });
487 | 
488 |     it("can run: console.log 'Hello, World!'", function(done) {
489 |         test("console.log 'Hello, World!'", "Hello, World!\n", done);
490 |     });
491 | 
492 |     it("uses coffeescript@2", function(done) {
493 |         var code = [
494 |             "outer = ->",
495 |             "  inner = => Array.prototype.slice.call arguments",
496 |             "  inner()",
497 |             "",
498 |             "console.log outer(1, 2)",
499 |         ].join("\n");
500 | 
501 |         var stdout = "[ 1, 2 ]\n";
502 | 
503 |         test(code, stdout, done);
504 |     });
505 | 
506 |     function test(code, stdout, done) {
507 |         var receivedExecuteReply = false;
508 |         var receivedStdout = false;
509 | 
510 |         var requestMessage = ctx.run(code, function(socketName, message) {
511 |             if (message.parent_header.msg_id !== requestMessage.header.msg_id) {
512 |                 return;
513 |             }
514 | 
515 |             var msg_type = message && message.header && message.header.msg_type;
516 | 
517 |             if (socketName === "shell") {
518 |                 if (msg_type === "execute_reply") {
519 |                     receivedExecuteReply = true;
520 |                 }
521 |             } else if (socketName === "iopub") {
522 |                 if (msg_type === "stream") {
523 |                     var name = message.content.name;
524 |                     if (name === "stdout") {
525 |                         var text = (message.content.hasOwnProperty("text")) ?
526 |                             message.content.text :
527 |                             message.content.data;
528 |                         assert.equal(text, stdout, "Unexpected stdout");
529 |                         receivedStdout = true;
530 |                     }
531 |                 }
532 |             }
533 | 
534 |             if (receivedExecuteReply && receivedStdout) {
535 |                 ctx.onMessage = null;
536 |                 done();
537 |             }
538 |         });
539 |     }
540 | });
541 | 
542 |
543 |
544 | 545 | 546 | 547 | 548 |
549 | 550 | 553 | 554 |
555 | 556 |
557 | Documentation generated by JSDoc 3.6.3 on Thu Sep 26 2019 19:06:01 GMT+0100 (BST) 558 |
559 | 560 | 561 | 562 | 563 | 564 | -------------------------------------------------------------------------------- /docs/scripts/linenumber.js: -------------------------------------------------------------------------------- 1 | /*global document */ 2 | (() => { 3 | const source = document.getElementsByClassName('prettyprint source linenums'); 4 | let i = 0; 5 | let lineNumber = 0; 6 | let lineId; 7 | let lines; 8 | let totalLines; 9 | let anchorHash; 10 | 11 | if (source && source[0]) { 12 | anchorHash = document.location.hash.substring(1); 13 | lines = source[0].getElementsByTagName('li'); 14 | totalLines = lines.length; 15 | 16 | for (; i < totalLines; i++) { 17 | lineNumber++; 18 | lineId = `line${lineNumber}`; 19 | lines[i].id = lineId; 20 | if (lineId === anchorHash) { 21 | lines[i].className += ' selected'; 22 | } 23 | } 24 | } 25 | })(); 26 | -------------------------------------------------------------------------------- /docs/scripts/prettify/Apache-License-2.0.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /docs/scripts/prettify/lang-css.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", 2 | /^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); 3 | -------------------------------------------------------------------------------- /docs/scripts/prettify/prettify.js: -------------------------------------------------------------------------------- 1 | var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; 2 | (function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= 3 | [],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), 9 | l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, 10 | q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, 11 | q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, 12 | "");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), 13 | a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} 14 | for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], 18 | "catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], 19 | H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], 20 | J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ 21 | I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), 22 | ["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", 23 | /^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), 24 | ["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", 25 | hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= 26 | !k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p th:last-child { border-right: 1px solid #ddd; } 224 | 225 | .ancestors, .attribs { color: #999; } 226 | .ancestors a, .attribs a 227 | { 228 | color: #999 !important; 229 | text-decoration: none; 230 | } 231 | 232 | .clear 233 | { 234 | clear: both; 235 | } 236 | 237 | .important 238 | { 239 | font-weight: bold; 240 | color: #950B02; 241 | } 242 | 243 | .yes-def { 244 | text-indent: -1000px; 245 | } 246 | 247 | .type-signature { 248 | color: #aaa; 249 | } 250 | 251 | .name, .signature { 252 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 253 | } 254 | 255 | .details { margin-top: 14px; border-left: 2px solid #DDD; } 256 | .details dt { width: 120px; float: left; padding-left: 10px; padding-top: 6px; } 257 | .details dd { margin-left: 70px; } 258 | .details ul { margin: 0; } 259 | .details ul { list-style-type: none; } 260 | .details li { margin-left: 30px; padding-top: 6px; } 261 | .details pre.prettyprint { margin: 0 } 262 | .details .object-value { padding-top: 0; } 263 | 264 | .description { 265 | margin-bottom: 1em; 266 | margin-top: 1em; 267 | } 268 | 269 | .code-caption 270 | { 271 | font-style: italic; 272 | font-size: 107%; 273 | margin: 0; 274 | } 275 | 276 | .source 277 | { 278 | border: 1px solid #ddd; 279 | width: 80%; 280 | overflow: auto; 281 | } 282 | 283 | .prettyprint.source { 284 | width: inherit; 285 | } 286 | 287 | .source code 288 | { 289 | font-size: 100%; 290 | line-height: 18px; 291 | display: block; 292 | padding: 4px 12px; 293 | margin: 0; 294 | background-color: #fff; 295 | color: #4D4E53; 296 | } 297 | 298 | .prettyprint code span.line 299 | { 300 | display: inline-block; 301 | } 302 | 303 | .prettyprint.linenums 304 | { 305 | padding-left: 70px; 306 | -webkit-user-select: none; 307 | -moz-user-select: none; 308 | -ms-user-select: none; 309 | user-select: none; 310 | } 311 | 312 | .prettyprint.linenums ol 313 | { 314 | padding-left: 0; 315 | } 316 | 317 | .prettyprint.linenums li 318 | { 319 | border-left: 3px #ddd solid; 320 | } 321 | 322 | .prettyprint.linenums li.selected, 323 | .prettyprint.linenums li.selected * 324 | { 325 | background-color: lightyellow; 326 | } 327 | 328 | .prettyprint.linenums li * 329 | { 330 | -webkit-user-select: text; 331 | -moz-user-select: text; 332 | -ms-user-select: text; 333 | user-select: text; 334 | } 335 | 336 | .params .name, .props .name, .name code { 337 | color: #4D4E53; 338 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 339 | font-size: 100%; 340 | } 341 | 342 | .params td.description > p:first-child, 343 | .props td.description > p:first-child 344 | { 345 | margin-top: 0; 346 | padding-top: 0; 347 | } 348 | 349 | .params td.description > p:last-child, 350 | .props td.description > p:last-child 351 | { 352 | margin-bottom: 0; 353 | padding-bottom: 0; 354 | } 355 | 356 | .disabled { 357 | color: #454545; 358 | } 359 | -------------------------------------------------------------------------------- /docs/styles/prettify-jsdoc.css: -------------------------------------------------------------------------------- 1 | /* JSDoc prettify.js theme */ 2 | 3 | /* plain text */ 4 | .pln { 5 | color: #000000; 6 | font-weight: normal; 7 | font-style: normal; 8 | } 9 | 10 | /* string content */ 11 | .str { 12 | color: #006400; 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | 17 | /* a keyword */ 18 | .kwd { 19 | color: #000000; 20 | font-weight: bold; 21 | font-style: normal; 22 | } 23 | 24 | /* a comment */ 25 | .com { 26 | font-weight: normal; 27 | font-style: italic; 28 | } 29 | 30 | /* a type name */ 31 | .typ { 32 | color: #000000; 33 | font-weight: normal; 34 | font-style: normal; 35 | } 36 | 37 | /* a literal value */ 38 | .lit { 39 | color: #006400; 40 | font-weight: normal; 41 | font-style: normal; 42 | } 43 | 44 | /* punctuation */ 45 | .pun { 46 | color: #000000; 47 | font-weight: bold; 48 | font-style: normal; 49 | } 50 | 51 | /* lisp open bracket */ 52 | .opn { 53 | color: #000000; 54 | font-weight: bold; 55 | font-style: normal; 56 | } 57 | 58 | /* lisp close bracket */ 59 | .clo { 60 | color: #000000; 61 | font-weight: bold; 62 | font-style: normal; 63 | } 64 | 65 | /* a markup tag name */ 66 | .tag { 67 | color: #006400; 68 | font-weight: normal; 69 | font-style: normal; 70 | } 71 | 72 | /* a markup attribute name */ 73 | .atn { 74 | color: #006400; 75 | font-weight: normal; 76 | font-style: normal; 77 | } 78 | 79 | /* a markup attribute value */ 80 | .atv { 81 | color: #006400; 82 | font-weight: normal; 83 | font-style: normal; 84 | } 85 | 86 | /* a declaration */ 87 | .dec { 88 | color: #000000; 89 | font-weight: bold; 90 | font-style: normal; 91 | } 92 | 93 | /* a variable name */ 94 | .var { 95 | color: #000000; 96 | font-weight: normal; 97 | font-style: normal; 98 | } 99 | 100 | /* a function name */ 101 | .fun { 102 | color: #000000; 103 | font-weight: bold; 104 | font-style: normal; 105 | } 106 | 107 | /* Specify class=linenums on a pre to get line numbering */ 108 | ol.linenums { 109 | margin-top: 0; 110 | margin-bottom: 0; 111 | } 112 | -------------------------------------------------------------------------------- /docs/styles/prettify-tomorrow.css: -------------------------------------------------------------------------------- 1 | /* Tomorrow Theme */ 2 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */ 3 | /* Pretty printing styles. Used with prettify.js. */ 4 | /* SPAN elements with the classes below are added by prettyprint. */ 5 | /* plain text */ 6 | .pln { 7 | color: #4d4d4c; } 8 | 9 | @media screen { 10 | /* string content */ 11 | .str { 12 | color: #718c00; } 13 | 14 | /* a keyword */ 15 | .kwd { 16 | color: #8959a8; } 17 | 18 | /* a comment */ 19 | .com { 20 | color: #8e908c; } 21 | 22 | /* a type name */ 23 | .typ { 24 | color: #4271ae; } 25 | 26 | /* a literal value */ 27 | .lit { 28 | color: #f5871f; } 29 | 30 | /* punctuation */ 31 | .pun { 32 | color: #4d4d4c; } 33 | 34 | /* lisp open bracket */ 35 | .opn { 36 | color: #4d4d4c; } 37 | 38 | /* lisp close bracket */ 39 | .clo { 40 | color: #4d4d4c; } 41 | 42 | /* a markup tag name */ 43 | .tag { 44 | color: #c82829; } 45 | 46 | /* a markup attribute name */ 47 | .atn { 48 | color: #f5871f; } 49 | 50 | /* a markup attribute value */ 51 | .atv { 52 | color: #3e999f; } 53 | 54 | /* a declaration */ 55 | .dec { 56 | color: #f5871f; } 57 | 58 | /* a variable name */ 59 | .var { 60 | color: #c82829; } 61 | 62 | /* a function name */ 63 | .fun { 64 | color: #4271ae; } } 65 | /* Use higher contrast and text-weight for printable form. */ 66 | @media print, projection { 67 | .str { 68 | color: #060; } 69 | 70 | .kwd { 71 | color: #006; 72 | font-weight: bold; } 73 | 74 | .com { 75 | color: #600; 76 | font-style: italic; } 77 | 78 | .typ { 79 | color: #404; 80 | font-weight: bold; } 81 | 82 | .lit { 83 | color: #044; } 84 | 85 | .pun, .opn, .clo { 86 | color: #440; } 87 | 88 | .tag { 89 | color: #006; 90 | font-weight: bold; } 91 | 92 | .atn { 93 | color: #404; } 94 | 95 | .atv { 96 | color: #060; } } 97 | /* Style */ 98 | /* 99 | pre.prettyprint { 100 | background: white; 101 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 102 | font-size: 12px; 103 | line-height: 1.5; 104 | border: 1px solid #ccc; 105 | padding: 10px; } 106 | */ 107 | 108 | /* Specify class=linenums on a pre to get line numbering */ 109 | ol.linenums { 110 | margin-top: 0; 111 | margin-bottom: 0; } 112 | 113 | /* IE indents via margin-left */ 114 | li.L0, 115 | li.L1, 116 | li.L2, 117 | li.L3, 118 | li.L4, 119 | li.L5, 120 | li.L6, 121 | li.L7, 122 | li.L8, 123 | li.L9 { 124 | /* */ } 125 | 126 | /* Alternate shading for lines */ 127 | li.L1, 128 | li.L3, 129 | li.L5, 130 | li.L7, 131 | li.L9 { 132 | /* */ } 133 | -------------------------------------------------------------------------------- /images/logo-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n-riesco/jp-coffeescript/ff02c75082ddb162d1266eb61d7dba9ab383b451/images/logo-32x32.png -------------------------------------------------------------------------------- /images/logo-64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n-riesco/jp-coffeescript/ff02c75082ddb162d1266eb61d7dba9ab383b451/images/logo-64x64.png -------------------------------------------------------------------------------- /images/nodejs/LICENSE: -------------------------------------------------------------------------------- 1 | The original contents of the nodejs.org repo are licensed for use as follows: 2 | 3 | """ 4 | Copyright node.js Website WG contributors. All rights reserved. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to 8 | deal in the Software without restriction, including without limitation the 9 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | sell copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | IN THE SOFTWARE. 23 | """ 24 | -------------------------------------------------------------------------------- /images/nodejs/README.md: -------------------------------------------------------------------------------- 1 | # Trademark notice 2 | 3 | Node.js is a trademark of Joyent, Inc. and is used with its permission. We are 4 | not endorsed by or affiliated with Joyent. 5 | 6 | # Files 7 | 8 | ## From the repository for the Node.js website 9 | - [LICENSE](https://github.com/nodejs/nodejs.org/blob/master/LICENSE) 10 | - [js-green.svg](https://github.com/nodejs/nodejs.org/blob/master/static/images/logos/js-green.svg) 11 | 12 | ## Generated using [rsvg-convert](http://live.gnome.org/LibRsvg) 13 | - [js-green-32x32.png](js-green-32x32.png) 14 | - [js-green-64x64.png](js-green-64x64.png) 15 | -------------------------------------------------------------------------------- /images/nodejs/js-green-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n-riesco/jp-coffeescript/ff02c75082ddb162d1266eb61d7dba9ab383b451/images/nodejs/js-green-32x32.png -------------------------------------------------------------------------------- /images/nodejs/js-green-64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n-riesco/jp-coffeescript/ff02c75082ddb162d1266eb61d7dba9ab383b451/images/nodejs/js-green-64x64.png -------------------------------------------------------------------------------- /images/nodejs/js-green.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 17 | 18 | -------------------------------------------------------------------------------- /lib/kernel.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* 4 | * BSD 3-Clause License 5 | * 6 | * Copyright (c) 2015, Nicolas Riesco and others as credited in the AUTHORS file 7 | * All rights reserved. 8 | * 9 | * Redistribution and use in source and binary forms, with or without 10 | * modification, are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, 13 | * this list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, 16 | * this list of conditions and the following disclaimer in the documentation 17 | * and/or other materials provided with the distribution. 18 | * 19 | * 3. Neither the name of the copyright holder nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software without 21 | * specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 24 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 27 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 31 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 | * POSSIBILITY OF SUCH DAMAGE. 34 | * 35 | */ 36 | 37 | var console = require("console"); 38 | var fs = require("fs"); 39 | var path = require("path"); 40 | 41 | var cs = require("coffeescript"); 42 | var csOptions = {bare: true}; 43 | 44 | var Kernel = require("jp-kernel"); 45 | 46 | 47 | // Parse command arguments 48 | var config = parseCommandArguments(); 49 | 50 | 51 | // Setup logging helpers 52 | var log; 53 | var dontLog = function dontLog() {}; 54 | var doLog = function doLog() { 55 | process.stderr.write("KERNEL: "); 56 | console.error.apply(this, arguments); 57 | }; 58 | 59 | if (process.env.DEBUG) { 60 | global.DEBUG = true; 61 | 62 | try { 63 | doLog = require("debug")("KERNEL:"); 64 | } catch (err) {} 65 | } 66 | 67 | log = global.DEBUG ? doLog : dontLog; 68 | 69 | 70 | // Setup session initialisation 71 | config.startupCallback = function startupCallback() { 72 | var requirePath = require.resolve("coffeescript/register"); 73 | var code = "require('" + requirePath + "');"; 74 | this.session.execute(code, { 75 | onSuccess: function() { 76 | log("startupCallback: '" + code + "' run successfuly"); 77 | }, 78 | onError: function() { 79 | log("startupCallback: '" + code + "' failed to run"); 80 | }, 81 | }); 82 | }; 83 | 84 | 85 | // Setup coffeescript transpiler 86 | config.transpile = function(code) { 87 | return cs.compile(code, csOptions); 88 | }; 89 | 90 | // Start kernel 91 | var kernel = new Kernel(config); 92 | 93 | kernel.handlers.is_complete_request = function is_complete_request(request) { 94 | request.respond(this.iopubSocket, "status", { 95 | execution_state: "busy" 96 | }); 97 | 98 | var content; 99 | try { 100 | config.transpile(request.content.code); 101 | content = { 102 | status: "complete", 103 | }; 104 | } catch (err) { 105 | content = { 106 | status: "incomplete", 107 | indent: "", 108 | }; 109 | } 110 | 111 | request.respond( 112 | this.shellSocket, 113 | "is_complete_reply", 114 | content, 115 | {}, 116 | this.protocolVersion 117 | ); 118 | 119 | request.respond(this.iopubSocket, "status", { 120 | execution_state: "idle" 121 | }); 122 | }; 123 | 124 | // Interpret a SIGINT signal as a request to interrupt the kernel 125 | process.on("SIGINT", function() { 126 | log("Interrupting kernel"); 127 | kernel.restart(); // TODO(NR) Implement kernel interruption 128 | }); 129 | 130 | 131 | /** 132 | * Parse command arguments 133 | * 134 | * @returns {module:jp-kernel~Config} Kernel config 135 | */ 136 | function parseCommandArguments() { 137 | var config = { 138 | cwd: process.cwd(), 139 | hideUndefined: true, 140 | protocolVersion: "5.1", 141 | }; 142 | 143 | var usage = ( 144 | "Usage: node kernel.js " + 145 | "[--debug] " + 146 | "[--hide-undefined] " + 147 | "[--protocol=Major[.minor[.patch]]] " + 148 | "[--session-working-dir=path] " + 149 | "[--show-undefined] " + 150 | "[--startup-script=path] " + 151 | "connection_file" 152 | ); 153 | 154 | var FLAGS = [ 155 | ["--debug", function() { 156 | config.debug = true; 157 | }], 158 | ["--hide-undefined", function() { 159 | config.hideUndefined = true; 160 | }], 161 | ["--protocol=", function(setting) { 162 | config.protocolVersion = setting; 163 | }], 164 | ["--session-working-dir=", function(setting) { 165 | config.cwd = setting; 166 | }], 167 | ["--show-undefined", function() { 168 | config.hideUndefined = false; 169 | }], 170 | ["--startup-script=", function(setting) { 171 | config.startupScript = setting; 172 | }], 173 | ]; 174 | 175 | try { 176 | var connectionFile; 177 | 178 | process.argv.slice(2).forEach(function(arg) { 179 | for (var i = 0; i < FLAGS.length; i++) { 180 | var flag = FLAGS[i]; 181 | var label = flag[0]; 182 | var action = flag[1]; 183 | 184 | var matchesFlag = (arg.indexOf(label) === 0); 185 | if (matchesFlag) { 186 | var setting = arg.slice(label.length); 187 | action(setting); 188 | return; 189 | } 190 | } 191 | 192 | if (connectionFile) { 193 | throw new Error("Error: too many arguments"); 194 | } 195 | 196 | connectionFile = arg; 197 | }); 198 | 199 | if (!connectionFile) { 200 | throw new Error("Error: missing connection_file"); 201 | } 202 | 203 | config.connection = JSON.parse(fs.readFileSync(connectionFile)); 204 | 205 | } catch (e) { 206 | console.error("KERNEL: ARGV:", process.argv); 207 | console.error(usage); 208 | throw e; 209 | } 210 | 211 | var nodeVersion; 212 | var protocolVersion; 213 | var jpVersion; 214 | var majorVersion = parseInt(config.protocolVersion.split(".")[0]); 215 | if (majorVersion <= 4) { 216 | nodeVersion = process.versions.node.split(".") 217 | .map(function(v) { 218 | return parseInt(v, 10); 219 | }); 220 | protocolVersion = config.protocolVersion.split(".") 221 | .map(function(v) { 222 | return parseInt(v, 10); 223 | }); 224 | config.kernelInfoReply = { 225 | "language": "coffeescript", 226 | "language_version": nodeVersion, 227 | "protocol_version": protocolVersion, 228 | }; 229 | } else { 230 | nodeVersion = process.versions.node; 231 | protocolVersion = config.protocolVersion; 232 | var packageJsonPath = path.join(__dirname, "..", "package.json"); 233 | jpVersion = JSON.parse(fs.readFileSync(packageJsonPath)).version; 234 | config.kernelInfoReply = { 235 | "protocol_version": protocolVersion, 236 | "implementation": "jp-coffeescript", 237 | "implementation_version": jpVersion, 238 | "language_info": { 239 | "name": "coffeescript", 240 | "version": nodeVersion, 241 | "mimetype": "application/coffeescript", 242 | "file_extension": ".coffee", 243 | }, 244 | "banner": ( 245 | "jp-coffeescript v" + jpVersion + "\n" + 246 | "https://github.com/n-riesco/jp-coffeescript\n" 247 | ), 248 | "help_links": [{ 249 | "text": "jp-coffeescript Homepage", 250 | "url": "https://github.com/n-riesco/jp-coffeescript", 251 | }], 252 | }; 253 | } 254 | 255 | return config; 256 | } 257 | -------------------------------------------------------------------------------- /lib/kernel.spec.js: -------------------------------------------------------------------------------- 1 | /* 2 | * BSD 3-Clause License 3 | * 4 | * Copyright (c) 2017, Nicolas Riesco and others as credited in the AUTHORS file 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright notice, 14 | * this list of conditions and the following disclaimer in the documentation 15 | * and/or other materials provided with the distribution. 16 | * 17 | * 3. Neither the name of the copyright holder nor the names of its contributors 18 | * may be used to endorse or promote products derived from this software without 19 | * specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | * 33 | */ 34 | 35 | var assert = require("assert"); 36 | var console = require("console"); 37 | var crypto = require("crypto"); 38 | var fs = require("fs"); 39 | var os = require("os"); 40 | var path = require("path"); 41 | var spawn = require("child_process").spawn; 42 | var uuid = require("uuid"); 43 | 44 | var jmp = require("jmp"); 45 | var zmq = jmp.zmq; 46 | 47 | // Setup logging helpers 48 | var LABEL = "JP-COFFEE-KERNEL: TEST"; 49 | var log; 50 | var dontLog = function dontLog() {}; 51 | var doLog = function doLog() { 52 | process.stderr.write(LABEL); 53 | console.error.apply(this, arguments); 54 | }; 55 | 56 | if (process.env.DEBUG) { 57 | global.DEBUG = true; 58 | 59 | try { 60 | doLog = require("debug")(LABEL); 61 | } catch (err) {} 62 | } 63 | 64 | log = global.DEBUG ? doLog : dontLog; 65 | 66 | /** 67 | * @typedef {Object} Message 68 | * @description IPython/Jupyter message 69 | * 70 | * @property {Array} [message.idents] Identities 71 | * @property {Object} [message.header] Header 72 | * @property {Object} [message.parent_header] Parent header 73 | * @property {Object} [message.metadata] Message metadata 74 | * @property {Object} [message.content] Message content 75 | */ 76 | 77 | /** 78 | * Callback to handle kernel messages 79 | * 80 | * @callback onMessageCallback 81 | * @param {string} socketName Socket name 82 | * @param {Message} message IPython/Jupyter message 83 | */ 84 | 85 | /* eslint-disable max-len */ 86 | /** 87 | * @class Context 88 | * @classdesc Context shared by tests 89 | * (adapted from MessagingTestEngine in IJavascript) 90 | * @see {@link https://github.com/n-riesco/ijavascript/tree/9c2cf6a94575b5016b4a6f3cbe380ab1fcfbcf76} 91 | * @param {string} protocolVersion Messaging protocol version 92 | */ 93 | function Context(protocolVersion) { 94 | /* eslint-disable max-len */ 95 | 96 | /** 97 | * @member version 98 | * @member {String} version.protocol Messaging protocol version 99 | */ 100 | this.version = { 101 | protocol: protocolVersion || "5.1", 102 | }; 103 | 104 | /** 105 | * @member path 106 | * @member {String} path.node Path to node 107 | * @member {String} path.root Path to package folder 108 | * @member {String} path.kernel Path to kernel 109 | * @member {String} path.connectionFile Path to kernel connection file 110 | */ 111 | this.path = {}; 112 | 113 | /** 114 | * @member connection 115 | * @member {String} connection.transport Transport protocol (e.g. "tcp") 116 | * @member {String} connection.ip IP address (e.g. "127.0.0.1") 117 | * @member {String} connection.signature_scheme 118 | * Signature scheme 119 | * (e.g. "hmac-sha256") 120 | * @member {String} connection.key Hashing key (e.g. uuid.v4()) 121 | * @member {Integer} connection.hb_port HeartBeat port 122 | * @member {Integer} connection.shell_port Shell port 123 | * @member {Integer} connection.stdin_port Stdin port 124 | * @member {Integer} connection.iopub_port IOPub port 125 | * @member {Integer} connection.control_port Control port 126 | */ 127 | this.connection = {}; 128 | 129 | /** 130 | * @member socket 131 | * @member {module:jmp~Socket} socket.control Control socket 132 | * @member {module:jmp~Socket} socket.hb HearBeat socket 133 | * @member {module:jmp~Socket} socket.iopub IOPub socket 134 | * @member {module:jmp~Socket} socket.shell Shell socket 135 | * @member {module:jmp~Socket} socket.stdin Stdin socket 136 | */ 137 | this.socket = {}; 138 | 139 | /** 140 | * @member {module:child_process~ChildProcess} kernel 141 | * @description Kernel instance 142 | */ 143 | this.kernel = null; 144 | 145 | /** 146 | * @member {onMessageCallback} [onMessage] 147 | * @description Callback to handle kernel messages 148 | */ 149 | this.onMessage = null; 150 | } 151 | 152 | /** 153 | * @method init 154 | * @param {function} done 155 | * @description Initialise the messaging test engine 156 | */ 157 | Context.prototype.init = function(done) { 158 | this._setPaths(); 159 | this._initSockets(); 160 | this._createConnectionFile(); 161 | this._initKernel(done); 162 | }; 163 | 164 | /** 165 | * @method dispose 166 | * @description Dispose the messaging test engine 167 | */ 168 | Context.prototype.dispose = function() { 169 | this._disposeKernel(); 170 | this._removeConnectionFile(); 171 | this._disposeSockets(); 172 | }; 173 | 174 | /** 175 | * @method _setPaths 176 | * @description Set up paths 177 | * @private 178 | */ 179 | Context.prototype._setPaths = function() { 180 | log("Setting paths"); 181 | 182 | this.path.node = process.argv[0]; 183 | this.path.root = path.dirname(__dirname); 184 | this.path.kernel = path.join(this.path.root, "lib", "kernel.js"); 185 | 186 | this.path.connectionFile = path.join( 187 | os.tmpdir(), 188 | "conn-" + uuid.v4() + ".json" 189 | ); 190 | }; 191 | 192 | /** 193 | * @method _createConnectionFile 194 | * @description Create connection file 195 | * @private 196 | */ 197 | Context.prototype._createConnectionFile = function() { 198 | log("Creating connection file"); 199 | 200 | fs.writeFileSync( 201 | this.path.connectionFile, 202 | JSON.stringify(this.connection) 203 | ); 204 | }; 205 | 206 | /** 207 | * @method _removeConnectionFile 208 | * @description Remove connection file 209 | * @private 210 | */ 211 | Context.prototype._removeConnectionFile = function() { 212 | log("Removing connection file"); 213 | 214 | try { 215 | fs.unlinkSync(this.path.connectionFile); 216 | } catch (e) { 217 | console.error(e.message); 218 | } 219 | }; 220 | 221 | /** 222 | * @method _initSockets 223 | * @description Setup ZMQ sockets and message listeners 224 | * @private 225 | */ 226 | Context.prototype._initSockets = function() { 227 | log("Setting up ZMQ sockets"); 228 | 229 | var transport = "tcp"; 230 | var ip = "127.0.0.1"; 231 | var address = transport + "://" + ip + ":"; 232 | var scheme = "sha256"; 233 | var key = crypto.randomBytes(256).toString("base64"); 234 | 235 | this.connection = { 236 | transport: transport, 237 | ip: ip, 238 | signature_scheme: "hmac-" + scheme, 239 | key: key, 240 | }; 241 | 242 | var socketNames = ["hb", "shell", "stdin", "iopub", "control"]; 243 | var socketTypes = ["req", "dealer", "dealer", "sub", "dealer"]; 244 | for (var i = 0, attempts = 0; i < socketNames.length; attempts++) { 245 | var socketName = socketNames[i]; 246 | var socketType = socketTypes[i]; 247 | var socket = (socketName === "hb") ? 248 | new zmq.Socket(socketType) : 249 | new jmp.Socket(socketType, scheme, key); 250 | var port = Math.floor(1024 + Math.random() * (65536 - 1024)); 251 | 252 | try { 253 | socket.connect(address + port); 254 | this.connection[socketName + "_port"] = port; 255 | this.socket[socketName] = socket; 256 | i++; 257 | } catch (e) { 258 | log(e.stack); 259 | } 260 | 261 | if (attempts >= 100) { 262 | throw new Error("can't bind to any local ports"); 263 | } 264 | } 265 | 266 | this.socket.iopub.subscribe(""); 267 | 268 | log("Setting up message listeners"); 269 | 270 | Object.getOwnPropertyNames(this.socket).forEach((function(socketName) { 271 | this.socket[socketName] 272 | .on("message", this._onMessage.bind(this, socketName)); 273 | }).bind(this)); 274 | }; 275 | 276 | /** 277 | * @method _disposeSockets 278 | * @description Dispose ZMQ sockets 279 | * @private 280 | */ 281 | Context.prototype._disposeSockets = function() { 282 | log("Disposing ZMQ sockets"); 283 | 284 | this.socket.control.close(); 285 | this.socket.hb.close(); 286 | this.socket.iopub.close(); 287 | this.socket.shell.close(); 288 | this.socket.stdin.close(); 289 | }; 290 | 291 | /** 292 | * @method _initKernel 293 | * @description Setup IJavascript kernel 294 | * @param {function} done 295 | * @private 296 | */ 297 | Context.prototype._initKernel = function(done) { 298 | log("Initialising a kernel"); 299 | 300 | var cmd = this.path.node; 301 | var args = [ 302 | this.path.kernel, 303 | "--protocol=" + this.version.protocol, 304 | this.path.connectionFile, 305 | ]; 306 | if (global.DEBUG) args.push("--debug"); 307 | var config = { 308 | stdio: "inherit" 309 | }; 310 | log("spawn", cmd, args, config); 311 | this.kernel = spawn(cmd, args, config); 312 | 313 | if (done) { 314 | this._waitUntilConnect(function() { 315 | this._waitUntilKernelIsIdle(done); 316 | }.bind(this)); 317 | } 318 | }; 319 | 320 | /** 321 | * @method _disposeKernel 322 | * @description Dispose IJavascript kernel 323 | * @private 324 | */ 325 | Context.prototype._disposeKernel = function() { 326 | log("Disposing IJavascript kernel"); 327 | 328 | if (this.kernel) { 329 | this.kernel.kill("SIGTERM"); 330 | } 331 | }; 332 | 333 | /** 334 | * @method _waitUntilConnect 335 | * @description Wait for ZMQ sockets to connect 336 | * @param {function} done 337 | * @private 338 | */ 339 | Context.prototype._waitUntilConnect = function(done) { 340 | log("Waiting for ZMQ sockets to connect"); 341 | 342 | var socketNames = ["hb", "shell", "iopub", "control"]; 343 | 344 | var waitGroup = socketNames.length; 345 | function onConnect() { 346 | waitGroup--; 347 | if (waitGroup === 0) { 348 | for (var i = 0; i < socketNames.length; i++) { 349 | this.socket[socketNames[i]].unmonitor(); 350 | } 351 | if (done) done(); 352 | } 353 | } 354 | 355 | for (var j = 0; j < socketNames.length; j++) { 356 | this.socket[socketNames[j]].on("connect", onConnect.bind(this)); 357 | this.socket[socketNames[j]].monitor(); 358 | } 359 | }; 360 | 361 | /** 362 | * @method _waitUntilKernelIsIdle 363 | * @description Wait until kernel is idle 364 | * @param {function} done 365 | * @private 366 | */ 367 | Context.prototype._waitUntilKernelIsIdle = function(done) { 368 | log("Waiting until kernel is idle"); 369 | 370 | var onMessage = this.onMessage; 371 | 372 | var request = this.run("", function(socketName, response) { 373 | if (response.parent_header.msg_id !== request.header.msg_id) { 374 | return; 375 | } 376 | 377 | if (socketName === "iopub") { 378 | if (response.header.msg_type === "status") { 379 | if (response.content.execution_state === "idle") { 380 | this.onMessage = onMessage; 381 | if (done) done(); 382 | } 383 | } 384 | } 385 | }); 386 | }; 387 | 388 | /** 389 | * @method _onMessage 390 | * @description Handle kernel message 391 | * @param {string} socketName Socket name 392 | * @param {Message} message IPython/Jupyter message 393 | * @private 394 | */ 395 | Context.prototype._onMessage = function(socketName, message) { 396 | log("Received on " + socketName, message); 397 | 398 | if (this.onMessage) this.onMessage(socketName, message); 399 | }; 400 | 401 | /** 402 | * @method run 403 | * @description Send kernel an execution request 404 | * 405 | * @param {string} code Code to be run by kernel 406 | * @param {onMessageCallback} [onMessage] Kernel message handler 407 | * 408 | * @returns {Message} message IPython/Jupyter message 409 | */ 410 | Context.prototype.run = function(code, onMessage) { 411 | log("Running:", code); 412 | 413 | var message = { 414 | "header": { 415 | "msg_type": "execute_request" 416 | }, 417 | "content": { 418 | "code": code 419 | } 420 | }; 421 | 422 | if (!(message instanceof jmp.Message)) { 423 | message = new jmp.Message(message); 424 | } 425 | 426 | if (!message.header.msg_id) { 427 | message.header.msg_id = uuid.v4(); 428 | } 429 | 430 | if (!message.header.username) { 431 | message.header.username = "user"; 432 | } 433 | 434 | if (!message.header.session) { 435 | message.header.session = uuid.v4(); 436 | } 437 | 438 | if (!message.header.version) { 439 | message.header.version = this.version.protocol; 440 | } 441 | 442 | this.onMessage = onMessage; 443 | this.socket.shell.send(message); 444 | 445 | return message; 446 | }; 447 | 448 | describe("jp-coffee-kernel", function() { 449 | var ctx = new Context(); 450 | 451 | before(function(done) { 452 | this.timeout(5000); 453 | ctx.init(done); 454 | }); 455 | 456 | after(function() { 457 | ctx.dispose(); 458 | }); 459 | 460 | it("can run: console.log 'Hello, World!'", function(done) { 461 | test("console.log 'Hello, World!'", "Hello, World!\n", done); 462 | }); 463 | 464 | it("uses coffeescript@2", function(done) { 465 | var code = [ 466 | "outer = ->", 467 | " inner = => Array.prototype.slice.call arguments", 468 | " inner()", 469 | "", 470 | "console.log outer(1, 2)", 471 | ].join("\n"); 472 | 473 | var stdout = "[ 1, 2 ]\n"; 474 | 475 | test(code, stdout, done); 476 | }); 477 | 478 | function test(code, stdout, done) { 479 | var receivedExecuteReply = false; 480 | var receivedStdout = false; 481 | 482 | var requestMessage = ctx.run(code, function(socketName, message) { 483 | if (message.parent_header.msg_id !== requestMessage.header.msg_id) { 484 | return; 485 | } 486 | 487 | var msg_type = message && message.header && message.header.msg_type; 488 | 489 | if (socketName === "shell") { 490 | if (msg_type === "execute_reply") { 491 | receivedExecuteReply = true; 492 | } 493 | } else if (socketName === "iopub") { 494 | if (msg_type === "stream") { 495 | var name = message.content.name; 496 | if (name === "stdout") { 497 | var text = (message.content.hasOwnProperty("text")) ? 498 | message.content.text : 499 | message.content.data; 500 | assert.equal(text, stdout, "Unexpected stdout"); 501 | receivedStdout = true; 502 | } 503 | } 504 | } 505 | 506 | if (receivedExecuteReply && receivedStdout) { 507 | ctx.onMessage = null; 508 | done(); 509 | } 510 | }); 511 | } 512 | }); 513 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jp-coffeescript", 3 | "version": "2.1.0", 4 | "description": "jp-CoffeeScript is a CoffeeScript kernel for the Jupyter notebook", 5 | "keywords": [ 6 | "coffeescript", 7 | "kernel", 8 | "jupyter" 9 | ], 10 | "homepage": "https://github.com/n-riesco/jp-coffeescript", 11 | "bugs": { 12 | "url": "https://github.com/n-riesco/jp-coffeescript/issues" 13 | }, 14 | "license": "BSD-3-Clause", 15 | "author": { 16 | "name": "Nicolas Riesco", 17 | "email": "enquiries@nicolasriesco.net", 18 | "url": "http://www.nicolasriesco.net/" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "https://github.com/n-riesco/jp-coffeescript.git" 23 | }, 24 | "bin": { 25 | "jp-coffee": "bin/jp-coffeescript.js", 26 | "jp-coffee-console": "bin/jp-coffeescript-console.js", 27 | "jp-coffee-install": "bin/jp-coffeescript-install.js", 28 | "jp-coffee-notebook": "bin/jp-coffeescript-notebook.js", 29 | "jp-coffee-kernel": "lib/kernel.js" 30 | }, 31 | "dependencies": { 32 | "coffeescript": "2", 33 | "jp-kernel": "2" 34 | }, 35 | "devDependencies": { 36 | "debug": "2", 37 | "eslint": "2", 38 | "jmp": "2", 39 | "jsdoc": "3", 40 | "mocha": "3", 41 | "uuid": "3" 42 | }, 43 | "engines": { 44 | "node": ">=6" 45 | }, 46 | "scripts": { 47 | "doc": "jsdoc -R README.md -d docs bin lib", 48 | "lint": "eslint bin lib test", 49 | "test:mocha": "mocha lib/**/*.spec.js", 50 | "test": "npm run lint && npm run test:mocha" 51 | } 52 | } 53 | --------------------------------------------------------------------------------