Repository: bokeh/bokeh

Issue:
Check nodejs version number and update documentation
Example [Specialized axis ticking](http://bokeh.pydata.org/en/latest/docs/user_guide/extensions_gallery/ticking.html#userguide-extensions-examples-ticking) is not working in bokeh 0.12.7. When I try to run the file I get following exception:

```
$ python specialized_axis_ticking.py
Traceback (most recent call last):
  File "specialized_axis_ticking.py", line 33, in <module>
    show(p)
  File "/home/artimi/.virtualenvs/bokeh/lib/python3.5/site-packages/bokeh/io.py", line 391, in show
    return _show_with_state(obj, _state, browser, new, notebook_handle=notebook_handle)
  File "/home/artimi/.virtualenvs/bokeh/lib/python3.5/site-packages/bokeh/io.py", line 425, in _show_with_state
    _show_file_with_state(obj, state, new, controller)
  File "/home/artimi/.virtualenvs/bokeh/lib/python3.5/site-packages/bokeh/io.py", line 434, in _show_file_with_state
    filename = save(obj, state=state)
  File "/home/artimi/.virtualenvs/bokeh/lib/python3.5/site-packages/bokeh/io.py", line 489, in save
    _save_helper(obj, filename, resources, title)
  File "/home/artimi/.virtualenvs/bokeh/lib/python3.5/site-packages/bokeh/io.py", line 544, in _save_helper
    html = file_html(obj, resources, title=title)
  File "/home/artimi/.virtualenvs/bokeh/lib/python3.5/site-packages/bokeh/embed.py", line 428, in file_html
    template=template, template_variables=template_variables)
  File "/usr/lib/python3.5/contextlib.py", line 77, in __exit__
    self.gen.throw(type, value, traceback)
  File "/home/artimi/.virtualenvs/bokeh/lib/python3.5/site-packages/bokeh/embed.py", line 77, in _ModelInDocument
    yield models
  File "/home/artimi/.virtualenvs/bokeh/lib/python3.5/site-packages/bokeh/embed.py", line 428, in file_html
    template=template, template_variables=template_variables)
  File "/home/artimi/.virtualenvs/bokeh/lib/python3.5/site-packages/bokeh/embed.py", line 706, in _html_page_for_render_items
    script  = bundle_all_models()
  File "/home/artimi/.virtualenvs/bokeh/lib/python3.5/site-packages/bokeh/util/compiler.py", line 417, in bundle_all_models
    return bundle_models(Model.model_class_reverse_map.values()) or ""
  File "/home/artimi/.virtualenvs/bokeh/lib/python3.5/site-packages/bokeh/util/compiler.py", line 332, in bundle_models
    compiled = nodejs_compile(impl.code, lang=impl.lang, file=impl.file)
  File "/home/artimi/.virtualenvs/bokeh/lib/python3.5/site-packages/bokeh/util/compiler.py", line 177, in nodejs_compile
    output = _run_nodejs([compilejs_script], dict(code=code, lang=lang, file=file))
  File "/home/artimi/.virtualenvs/bokeh/lib/python3.5/site-packages/bokeh/util/compiler.py", line 156, in _run_nodejs
    return _run(_nodejs_path(), argv, input)
  File "/home/artimi/.virtualenvs/bokeh/lib/python3.5/site-packages/bokeh/util/compiler.py", line 151, in _run
    raise RuntimeError(errout)
RuntimeError: b'/home/artimi/.virtualenvs/bokeh/lib/python3.5/site-packages/bokeh/server/static/js/compiler.js:44\n    let { line, character: column } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);\n        ^\n\nSyntaxError: Unexpected token {\n    at exports.runInThisContext (vm.js:53:16)\n    at Module._compile (module.js:374:25)\n    at Object.Module._extensions..js (module.js:417:10)\n    at Module.load (module.js:344:32)\n    at Function.Module._load (module.js:301:12)\n    at Function.Module.runMain (module.js:442:10)\n    at startup (node.js:136:18)\n    at node.js:966:3\n'

```
I'm running this in following environment:
```
bokeh 0.12.7
python 3.5.2
Ubuntu 16.04
```
When I run it on bokeh 0.12.6 the graph is correctly shown in browser.
```
$ python specialized_axis_ticking.py
$
```

I'm just guessing but maybe there is some problem with javascript/coffeescript/typescript complilation like here https://github.com/bokeh/bokeh/issues/5345. 



Discussion:
What version of nodejs do you have installed (`node --version`)? Minimum required version is 6.10.x. If node is installed via the operating system then it's  likely outdated. You can install most recent version with conda (`conda install nodejs`).
@mattpap you are right my nodejs was outdated:
```
$ nodejs -v                        
v4.2.6
```
After I upgraded there is no more problem with custom javascript formatter. Thank you for fast tip. I was using just `pip` until now because we are deploying all our services via `pip` but I might reconsider. Are requirements like `nodejs` listed anywhere or is it just in conda?
This isn't well documented and only conda package specification ([meta.yaml](https://github.com/bokeh/bokeh/blob/master/conda.recipe/meta.yaml)) contains this information. In fact, a version check should be added to bokeh's compiler module, along with check whether nodejs is installed at all.

Resolving patch:
diff --git a/bokeh/util/compiler.py b/bokeh/util/compiler.py
--- a/bokeh/util/compiler.py
+++ b/bokeh/util/compiler.py
@@ -7,6 +7,7 @@
 logger = logging.getLogger(__name__)
 
 import io
+import re
 import os
 import sys
 import six
@@ -109,6 +110,7 @@ def __str__(self):
         return self.text
 
 bokehjs_dir = settings.bokehjsdir()
+nodejs_min_version = (6, 10, 0)
 
 def _detect_nodejs():
     if settings.nodejs_path() is not None:
@@ -119,14 +121,25 @@ def _detect_nodejs():
     for nodejs_path in nodejs_paths:
         try:
             proc = Popen([nodejs_path, "--version"], stdout=PIPE, stderr=PIPE)
-            proc.wait()
+            (stdout, _) = proc.communicate()
         except OSError:
-            pass
-        else:
-            return nodejs_path
-    else:
-        raise RuntimeError('node.js is needed to allow compilation of custom models ' +
-                           '("conda install -c bokeh nodejs" or follow https://nodejs.org/en/download/)')
+            continue
+
+        if proc.returncode != 0:
+            continue
+
+        match = re.match(r"^v(\d+)\.(\d+)\.(\d+).*$", stdout.decode("utf-8"))
+
+        if match is not None:
+            version = tuple(int(v) for v in match.groups())
+
+            if version >= nodejs_min_version:
+                return nodejs_path
+
+    # if we've reached here, no valid version was found
+    version = ".".join(map(str, nodejs_min_version))
+    raise RuntimeError('node.js v%s or higher is needed to allow compilation of custom models ' % version +
+                       '("conda install -c bokeh nodejs" or follow https://nodejs.org/en/download/)')
 
 _nodejs = None
 _npmjs = None


Test patch:
diff --git a/bokeh/util/tests/test_compiler.py b/bokeh/util/tests/test_compiler.py
--- a/bokeh/util/tests/test_compiler.py
+++ b/bokeh/util/tests/test_compiler.py
@@ -1,7 +1,5 @@
 from __future__ import absolute_import
 
-from mock import Mock
-
 import bokeh.util.compiler as buc
 
 def test_nodejs_compile_coffeescript():
@@ -77,11 +75,3 @@ def test_nodejs_compile_less():
             text="some.less:1:21:Unrecognised input",
             extract=".bk-some-style color: green; }",
             annotated="some.less:1:21:Unrecognised input\n  .bk-some-style color: green; }"))
-
-def test__detect_nodejs_calls_wait():
-    m = Mock()
-    old_Popen = buc.Popen
-    buc.Popen = lambda *args, **kw: m
-    buc._detect_nodejs()
-    assert m.wait.called
-    buc.Popen = old_Popen