Repository: bokeh/bokeh

Issue:
Add a sample configuration for bundling with webpack
I don't think we will actually switch to webpack, but at least lets have a sample configuration available, as this was asked many times before.


Discussion:

Resolving patch:
diff --git a/bokeh/util/compiler.py b/bokeh/util/compiler.py
--- a/bokeh/util/compiler.py
+++ b/bokeh/util/compiler.py
@@ -19,25 +19,28 @@
 from ..settings import settings
 from .string import snakify
 
+_plugin_umd = \
+"""\
+(function(root, factory) {
+  if(typeof exports === 'object' && typeof module === 'object')
+    factory(require("bokeh"));
+  else if(typeof define === 'function' && define.amd)
+    define(["bokeh"], factory);
+  else if(typeof exports === 'object')
+    factory(require("Bokeh"));
+  else
+    factory(root["Bokeh"]);
+})(this, function(Bokeh, define /* void 0 */) {
+  return %(content)s;
+});
+"""
+
 # XXX: this is the same as bokehjs/src/js/plugin-prelude.js
 _plugin_prelude = \
-"""
-(function outer(modules, cache, entry) {
+"""\
+(function outer(modules, entry) {
   if (Bokeh != null) {
-    for (var name in modules) {
-      Bokeh.require.modules[name] = modules[name];
-    }
-
-    for (var i = 0; i < entry.length; i++) {
-      var plugin = Bokeh.require(entry[i]);
-      Bokeh.Models.register_models(plugin.models);
-
-      for (var name in plugin) {
-        if (name !== "models") {
-          Bokeh[name] = plugin[name];
-        }
-      }
-    }
+    return Bokeh.register_plugin(modules, {}, entry);
   } else {
     throw new Error("Cannot find Bokeh. You have to load it prior to loading plugins.");
   }
@@ -45,22 +48,22 @@
 """
 
 _plugin_template = \
-"""
-%(prelude)s
+"""\
+%(prelude)s\
 ({
-  "custom/main": [function(require, module, exports) {
-    module.exports = {
-      models: {
-        %(exports)s
-      }
+  "custom/main": function(require, module, exports) {
+    var models = {
+      %(exports)s
     };
-  }, {}],
+    require("base").register_models(models);
+    module.exports = models;
+  },
   %(modules)s
-}, {}, ["custom/main"]);
+}, "custom/main");
 """
 
 _style_template = \
-"""
+"""\
 (function() {
   var head = document.getElementsByTagName('head')[0];
   var style = document.createElement('style');
@@ -79,7 +82,7 @@
 """"%(name)s": require("%(module)s").%(name)s"""
 
 _module_template = \
-""""%(module)s": [function(require, module, exports) {\n%(code)s\n}, %(deps)s]"""
+""""%(module)s": function(require, module, exports) {\n%(source)s\n}"""
 
 class AttrDict(dict):
     ''' Provide a dict subclass that supports access by named attributes.
@@ -168,7 +171,7 @@ def npmjs_version():
     return _version(_run_npmjs)
 
 def nodejs_compile(code, lang="javascript", file=None):
-    compilejs_script = join(bokehjs_dir, "js", "compile.js")
+    compilejs_script = join(bokehjs_dir, "js", "compiler.js")
     output = _run_nodejs([compilejs_script], dict(code=code, lang=lang, file=file))
     return AttrDict(json.loads(output))
 
@@ -302,10 +305,13 @@ def bundle_models(models):
     exports = []
     modules = []
 
-    with io.open(join(bokehjs_dir, "js", "modules.json"), encoding="utf-8") as f:
-        known_modules = json.loads(f.read())
+    def read_json(name):
+        with io.open(join(bokehjs_dir, "js", name + ".json"), encoding="utf-8") as f:
+            return json.loads(f.read())
+
 
-    known_modules = set(known_modules["bokehjs"] + known_modules["widgets"])
+    bundles = ["bokeh", "bokeh-api", "bokeh-widgets", "bokeh-tables", "bokeh-gl"]
+    known_modules = set(sum([ read_json(name) for name in bundles ], []))
     custom_impls = {}
 
     dependencies = []
@@ -388,12 +394,19 @@ def resolve_deps(deps, root):
     exports = sorted(exports, key=lambda spec: spec[1])
     modules = sorted(modules, key=lambda spec: spec[0])
 
+    for i, (module, code, deps) in enumerate(modules):
+        for name, ref in deps.items():
+            code = code.replace("""require("%s")""" % name, """require("%s")""" % ref)
+            code = code.replace("""require('%s')""" % name, """require('%s')""" % ref)
+        modules[i] = (module, code)
+
     sep = ",\n"
 
     exports = sep.join([ _export_template % dict(name=name, module=module) for (name, module) in exports ])
-    modules = sep.join([ _module_template % dict(module=module, code=code, deps=json.dumps(deps)) for (module, code, deps) in modules ])
+    modules = sep.join([ _module_template % dict(module=module, source=code) for (module, code) in modules ])
 
-    return _plugin_template % dict(prelude=_plugin_prelude, exports=exports, modules=modules)
+    content = _plugin_template % dict(prelude=_plugin_prelude, exports=exports, modules=modules)
+    return _plugin_umd % dict(content=content)
 
 def bundle_all_models():
     return bundle_models(Model.model_class_reverse_map.values()) or ""


Test patch:
diff --git a/bokehjs/gulp/tasks/test.ts b/bokehjs/gulp/tasks/test.ts
--- a/bokehjs/gulp/tasks/test.ts
+++ b/bokehjs/gulp/tasks/test.ts
@@ -1,24 +1,21 @@
 import * as gulp from "gulp"
 import * as gutil from "gulp-util"
-import * as through from "through"
+import * as through from "through2"
 import * as cp from "child_process"
-import {Stream} from "stream"
 import {argv} from "yargs"
 
 import * as paths from "../paths"
 import {buildWatchTask} from "../utils"
 
 function mocha(options: {coverage?: boolean} = {}) {
-  type This = Stream & {_files: string[] | null}
+  const files: string[] = []
 
-  return through(
-    function(this: This, file: {path: string}) {
-      if (this._files == null)
-        this._files = [file.path]
-      else
-        this._files.push(file.path)
+  return through.obj(
+    function(file: {path: string}, _enc, next) {
+      files.push(file.path)
+      next()
     },
-    function(this: This) {
+    function(next) {
       const _mocha = "node_modules/mocha/bin/_mocha"
 
       let args: string[]
@@ -31,11 +28,11 @@ function mocha(options: {coverage?: boolean} = {}) {
         args.unshift("debug")
 
       args = args.concat(
-        ["--compilers", "coffee:coffee-script/register,ts:ts-node/register"],
+        ["--compilers", "coffee:coffee-script/register,ts:./ts-node/register"],
         ["--reporter", argv.reporter || "spec"],
         ["--slow", "5s"],
         ["./test/index.coffee"],
-        this._files!,
+        files,
       )
 
       const env = Object.assign({}, process.env, {
@@ -54,7 +51,7 @@ function mocha(options: {coverage?: boolean} = {}) {
           const comment = signal === "SIGINT" ? "interrupted" : "failed"
           this.emit("error", new gutil.PluginError("mocha", `tests ${comment}`))
         } else
-          this.emit("end")
+          next()
       })
 
       process.on('exit',    () => proc.kill())
diff --git a/bokehjs/test/defaults.ts b/bokehjs/test/defaults.ts
--- a/bokehjs/test/defaults.ts
+++ b/bokehjs/test/defaults.ts
@@ -11,11 +11,8 @@ import {isEqual} from "core/util/eq"
 import {Models} from "base"
 import {HasProps} from "core/has_props"
 
-import {models as widget_models} from "models/widgets/main"
-Models.register_models(widget_models as {[key: string]: any}, false, undefined)
-
-import {models as table_models} from "models/widgets/tables/main"
-Models.register_models(table_models as {[key: string]: any}, false, undefined)
+import {Widgets as widget_models} from "models/widgets/main"
+import {Tables as table_models} from "models/widgets/tables/main"
 
 function get_defaults(name: string) {
   const defaults = core_defaults.get_defaults(name) || widget_defaults.get_defaults(name)
diff --git a/bokehjs/test/index.coffee b/bokehjs/test/index.coffee
--- a/bokehjs/test/index.coffee
+++ b/bokehjs/test/index.coffee
@@ -1,20 +1,3 @@
-{TSError} = require "ts-node"
-chalk = require "chalk"
-
-prettyTSError = (error) ->
-  title = "#{chalk.red('⨯')} Unable to compile TypeScript:"
-  return "#{chalk.bold(title)}\n#{error.diagnostics.map((x) -> x.message).join('\n')}"
-
-module.constructor.prototype.require = (modulePath) ->
-  try
-    return this.constructor._load(modulePath, this)
-  catch err
-    if err instanceof TSError
-      console.error(prettyTSError(err))
-      process.exit(1)
-    else
-      throw err
-
 jsdom = require('jsdom').jsdom
 
 global.document = jsdom()
diff --git a/bokehjs/test/size.ts b/bokehjs/test/size.ts
--- a/bokehjs/test/size.ts
+++ b/bokehjs/test/size.ts
@@ -7,14 +7,14 @@ const build_dir = path.normalize(`${__dirname}/../build`)
 
 const LIMITS: {[key: string]: number} = {
   // js
-  "js/bokeh.min.js":           740,
+  "js/bokeh.min.js":           680,
   "js/bokeh-widgets.min.js":    90,
   "js/bokeh-tables.min.js":    200,
   "js/bokeh-api.min.js":        75,
   "js/bokeh-gl.min.js":         70,
   // css
   "css/bokeh.min.css":          60,
-  "css/bokeh-widgets.min.css":  70,
+  "css/bokeh-widgets.min.css":  50,
   "css/bokeh-tables.min.css":   30,
 }