Repository: bokeh/bokeh

Issue:
custom latex examples do not run
Cannot get either example under `custom` to function with 2.1.0rc3 with a simple python call to the script. Both seem to compile things fine, but then report 
> Unhandled Promise Rejection: Error: Model 'latex_label.LatexLabel' does not exist. This could be due to a widget or a custom model not being registered before first usage.

in the browser.  Using this node on OSX:
```
nodejs                    14.4.0               h2c41780_0    conda-forge
```

If there is something beyond a simple `python foo.py` that is necessary to run these, then this issue should be re-tasked to add a README to each. Otherwise it can be changed to `bug`. If there is some unsupported range of build tools we need to find a way to warn better. 

To note: the `examples/models/file/latex_extension.py` example **does*** work.

cc @mattpap 


Discussion:
@mattpap tangential to this if you could do a cross section of manual testing on linux in the next 24hrs it would appreciated (and if you cannot, please let me know so that I can try to squeeze at lease a small amount in myself)

Resolving patch:
diff --git a/bokeh/embed/bundle.py b/bokeh/embed/bundle.py
--- a/bokeh/embed/bundle.py
+++ b/bokeh/embed/bundle.py
@@ -183,7 +183,11 @@ def bundle_for_objs_and_resources(objs, resources):
         elif mode == "server":
             js_files.extend([ bundle.server_url for bundle in extensions ])
         elif mode == "cdn":
-            js_files.extend([ bundle.cdn_url for bundle in extensions if bundle.cdn_url is not None ])
+            for bundle in extensions:
+                if bundle.cdn_url is not None:
+                    js_files.append(bundle.cdn_url)
+                else:
+                    js_raw.append(Resources._inline(bundle.artifact_path))
         else:
             js_files.extend([ bundle.artifact_path for bundle in extensions ])
 


Test patch:
diff --git a/tests/unit/bokeh/embed/ext_package_no_main/.gitignore b/tests/unit/bokeh/embed/ext_package_no_main/.gitignore
new file mode 100644
--- /dev/null
+++ b/tests/unit/bokeh/embed/ext_package_no_main/.gitignore
@@ -0,0 +1,3 @@
+/.bokeh
+/dist/
+/node_modules/
diff --git a/tests/unit/bokeh/embed/ext_package_no_main/__init__.py b/tests/unit/bokeh/embed/ext_package_no_main/__init__.py
new file mode 100644
--- /dev/null
+++ b/tests/unit/bokeh/embed/ext_package_no_main/__init__.py
@@ -0,0 +1,6 @@
+# Bokeh imports
+from bokeh.model import Model
+
+
+class AModel(Model):
+    pass
diff --git a/tests/unit/bokeh/embed/ext_package_no_main/bokeh.ext.json b/tests/unit/bokeh/embed/ext_package_no_main/bokeh.ext.json
new file mode 100644
--- /dev/null
+++ b/tests/unit/bokeh/embed/ext_package_no_main/bokeh.ext.json
@@ -0,0 +1 @@
+{}
diff --git a/tests/unit/bokeh/embed/ext_package_no_main/index.ts b/tests/unit/bokeh/embed/ext_package_no_main/index.ts
new file mode 100644
--- /dev/null
+++ b/tests/unit/bokeh/embed/ext_package_no_main/index.ts
@@ -0,0 +1,8 @@
+import {Model} from "@bokehjs/model"
+
+export class AModel extends Model {
+  static __module__ = "ext_package_no_main"
+}
+
+import {register_models} from "@bokehjs/base"
+register_models({AModel})
diff --git a/tests/unit/bokeh/embed/ext_package_no_main/package-lock.json b/tests/unit/bokeh/embed/ext_package_no_main/package-lock.json
new file mode 100644
--- /dev/null
+++ b/tests/unit/bokeh/embed/ext_package_no_main/package-lock.json
@@ -0,0 +1,323 @@
+{
+  "name": "ext_package_no_main",
+  "version": "0.0.1",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "@bokeh/bokehjs": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/@bokeh/bokehjs/-/bokehjs-2.1.1.tgz",
+      "integrity": "sha512-1VoFV9+5KW1zG0pXprSGvvWff11v5+HklVyQtzqNbxEKg6Hs6FTtrp97I/OcYT48XflhUQbI7QwuE0nRq2EBWQ==",
+      "requires": {
+        "@bokeh/gloo2": "^0.0.1",
+        "@bokeh/numbro": "^1.6.2",
+        "@bokeh/slickgrid": "^2.3.23",
+        "choices.js": "^9.0.1",
+        "es5-ext": "^0.10.53",
+        "es6-map": "^0.1.5",
+        "es6-promise": "4.2.8",
+        "es6-set": "^0.1.5",
+        "es6-weak-map": "^2.0.2",
+        "flatbush": "^3.2.1",
+        "flatpickr": "^4.6.3",
+        "hammerjs": "^2.0.4",
+        "nouislider": "^14.5.0",
+        "proj4": "^2.6.0",
+        "sprintf-js": "^1.1.2",
+        "timezone": "^1.0.23",
+        "tslib": "^1.11.0",
+        "underscore.template": "^0.1.7"
+      }
+    },
+    "@bokeh/gloo2": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/@bokeh/gloo2/-/gloo2-0.0.1.tgz",
+      "integrity": "sha512-N1RMIcxRLnUcycn/vXPwCgw24GAfGfdTiGIPW/DgPULpsn25Q7+iXKmieP3/BB/ZsRQvS3cSek6qaCv5pUbVPw=="
+    },
+    "@bokeh/numbro": {
+      "version": "1.6.2",
+      "resolved": "https://registry.npmjs.org/@bokeh/numbro/-/numbro-1.6.2.tgz",
+      "integrity": "sha512-owIECPc3T3QXHCb2v5Ez+/uE9SIxI7N4nd9iFlWnfBrOelr0/omvFn09VisRn37AAFAY39sJiCVgECwryHWUPA=="
+    },
+    "@bokeh/slickgrid": {
+      "version": "2.3.23",
+      "resolved": "https://registry.npmjs.org/@bokeh/slickgrid/-/slickgrid-2.3.23.tgz",
+      "integrity": "sha512-gm01jGqIn+mSqK4OgfC85QqWoRE3tYJcP6PHfDI6BCK9NcS7PuPaok7cGEQPafcfzscQAm1BP7tGpfMsnj/CjQ==",
+      "requires": {
+        "@types/slickgrid": "^2.1.27",
+        "jquery": ">=3.4.0",
+        "jquery-ui": ">=1.8.0",
+        "tslib": "^1.10.0"
+      }
+    },
+    "@types/jquery": {
+      "version": "3.5.1",
+      "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.1.tgz",
+      "integrity": "sha512-Tyctjh56U7eX2b9udu3wG853ASYP0uagChJcQJXLUXEU6C/JiW5qt5dl8ao01VRj1i5pgXPAf8f1mq4+FDLRQg==",
+      "requires": {
+        "@types/sizzle": "*"
+      }
+    },
+    "@types/sizzle": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz",
+      "integrity": "sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg=="
+    },
+    "@types/slickgrid": {
+      "version": "2.1.30",
+      "resolved": "https://registry.npmjs.org/@types/slickgrid/-/slickgrid-2.1.30.tgz",
+      "integrity": "sha512-9nTqNWD3BtEVK0CP+G+mBtvSrKTfQy3Dg5/al+GdTSVMHFm37UxsHJ1eURwPg7rYu6vc7xU95fGTCKMZbxsD5w==",
+      "requires": {
+        "@types/jquery": "*"
+      }
+    },
+    "choices.js": {
+      "version": "9.0.1",
+      "resolved": "https://registry.npmjs.org/choices.js/-/choices.js-9.0.1.tgz",
+      "integrity": "sha512-JgpeDY0Tmg7tqY6jaW/druSklJSt7W68tXFJIw0GSGWmO37SDAL8o60eICNGbzIODjj02VNNtf5h6TgoHDtCsA==",
+      "requires": {
+        "deepmerge": "^4.2.0",
+        "fuse.js": "^3.4.5",
+        "redux": "^4.0.4"
+      }
+    },
+    "d": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+      "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+      "requires": {
+        "es5-ext": "^0.10.50",
+        "type": "^1.0.1"
+      }
+    },
+    "deepmerge": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
+      "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg=="
+    },
+    "es5-ext": {
+      "version": "0.10.53",
+      "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz",
+      "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==",
+      "requires": {
+        "es6-iterator": "~2.0.3",
+        "es6-symbol": "~3.1.3",
+        "next-tick": "~1.0.0"
+      }
+    },
+    "es6-iterator": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
+      "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
+      "requires": {
+        "d": "1",
+        "es5-ext": "^0.10.35",
+        "es6-symbol": "^3.1.1"
+      }
+    },
+    "es6-map": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz",
+      "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=",
+      "requires": {
+        "d": "1",
+        "es5-ext": "~0.10.14",
+        "es6-iterator": "~2.0.1",
+        "es6-set": "~0.1.5",
+        "es6-symbol": "~3.1.1",
+        "event-emitter": "~0.3.5"
+      }
+    },
+    "es6-promise": {
+      "version": "4.2.8",
+      "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
+      "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
+    },
+    "es6-set": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz",
+      "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=",
+      "requires": {
+        "d": "1",
+        "es5-ext": "~0.10.14",
+        "es6-iterator": "~2.0.1",
+        "es6-symbol": "3.1.1",
+        "event-emitter": "~0.3.5"
+      },
+      "dependencies": {
+        "es6-symbol": {
+          "version": "3.1.1",
+          "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz",
+          "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=",
+          "requires": {
+            "d": "1",
+            "es5-ext": "~0.10.14"
+          }
+        }
+      }
+    },
+    "es6-symbol": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
+      "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
+      "requires": {
+        "d": "^1.0.1",
+        "ext": "^1.1.2"
+      }
+    },
+    "es6-weak-map": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz",
+      "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==",
+      "requires": {
+        "d": "1",
+        "es5-ext": "^0.10.46",
+        "es6-iterator": "^2.0.3",
+        "es6-symbol": "^3.1.1"
+      }
+    },
+    "event-emitter": {
+      "version": "0.3.5",
+      "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
+      "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=",
+      "requires": {
+        "d": "1",
+        "es5-ext": "~0.10.14"
+      }
+    },
+    "ext": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz",
+      "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==",
+      "requires": {
+        "type": "^2.0.0"
+      },
+      "dependencies": {
+        "type": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/type/-/type-2.0.0.tgz",
+          "integrity": "sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow=="
+        }
+      }
+    },
+    "flatbush": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/flatbush/-/flatbush-3.3.0.tgz",
+      "integrity": "sha512-F3EzQvKpdmXUbFwWxLKBpytOFEGYQMCTBLuqZ4GEajFOEAvnOIBiyxW3OFSZXIOtpCS8teN6bFEpNZtnVXuDQA==",
+      "requires": {
+        "flatqueue": "^1.2.0"
+      }
+    },
+    "flatpickr": {
+      "version": "4.6.3",
+      "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.3.tgz",
+      "integrity": "sha512-007VucCkqNOMMb9ggRLNuJowwaJcyOh4sKAFcdGfahfGc7JQbf94zSzjdBq/wVyHWUEs5o3+idhFZ0wbZMRmVQ=="
+    },
+    "flatqueue": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/flatqueue/-/flatqueue-1.2.1.tgz",
+      "integrity": "sha512-X86TpWS1rGuY7m382HuA9vngLeDuWA9lJvhEG+GfgKMV5onSvx5a71cl7GMbXzhWtlN9dGfqOBrpfqeOtUfGYQ=="
+    },
+    "fuse.js": {
+      "version": "3.6.1",
+      "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-3.6.1.tgz",
+      "integrity": "sha512-hT9yh/tiinkmirKrlv4KWOjztdoZo1mx9Qh4KvWqC7isoXwdUY3PNWUxceF4/qO9R6riA2C29jdTOeQOIROjgw=="
+    },
+    "hammerjs": {
+      "version": "2.0.8",
+      "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz",
+      "integrity": "sha1-BO93hiz/K7edMPdpIJWTAiK/YPE="
+    },
+    "jquery": {
+      "version": "3.5.1",
+      "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.1.tgz",
+      "integrity": "sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg=="
+    },
+    "jquery-ui": {
+      "version": "1.12.1",
+      "resolved": "https://registry.npmjs.org/jquery-ui/-/jquery-ui-1.12.1.tgz",
+      "integrity": "sha1-vLQEXI3QU5wTS8FIjN0+dop6nlE="
+    },
+    "js-tokens": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
+    },
+    "loose-envify": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+      "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+      "requires": {
+        "js-tokens": "^3.0.0 || ^4.0.0"
+      }
+    },
+    "mgrs": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/mgrs/-/mgrs-1.0.0.tgz",
+      "integrity": "sha1-+5FYjnjJACVnI5XLQLJffNatGCk="
+    },
+    "next-tick": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
+      "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw="
+    },
+    "nouislider": {
+      "version": "14.6.0",
+      "resolved": "https://registry.npmjs.org/nouislider/-/nouislider-14.6.0.tgz",
+      "integrity": "sha512-KY0jH2pU4G/55wpoS5Ynyrc5xpOMZ10/Xr51sMYG/JxmYoPJGy3fG8mOMio0MJXerKp5Go3elwcODk3lX6mFMQ=="
+    },
+    "proj4": {
+      "version": "2.6.2",
+      "resolved": "https://registry.npmjs.org/proj4/-/proj4-2.6.2.tgz",
+      "integrity": "sha512-Pn0+HZtXb4JzuN8RR0VM7yyseegiYHbXkF+2FOdGpzRojcZ1BTjWxOh7qfp2vH0EyLu8pvcrhLxidwzgyUy/Gw==",
+      "requires": {
+        "mgrs": "1.0.0",
+        "wkt-parser": "^1.2.4"
+      }
+    },
+    "redux": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.5.tgz",
+      "integrity": "sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==",
+      "requires": {
+        "loose-envify": "^1.4.0",
+        "symbol-observable": "^1.2.0"
+      }
+    },
+    "sprintf-js": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
+      "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug=="
+    },
+    "symbol-observable": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
+      "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ=="
+    },
+    "timezone": {
+      "version": "1.0.23",
+      "resolved": "https://registry.npmjs.org/timezone/-/timezone-1.0.23.tgz",
+      "integrity": "sha512-yhQgk6qmSLB+TF8HGmApZAVI5bfzR1CoKUGr+WMZWmx75ED1uDewAZA8QMGCQ70TEv4GmM8pDB9jrHuxdaQ1PA=="
+    },
+    "tslib": {
+      "version": "1.13.0",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
+      "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q=="
+    },
+    "type": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
+      "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg=="
+    },
+    "underscore.template": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/underscore.template/-/underscore.template-0.1.7.tgz",
+      "integrity": "sha1-MBPg6hgXVjBvFgnpWcr7xyKts+k="
+    },
+    "wkt-parser": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/wkt-parser/-/wkt-parser-1.2.4.tgz",
+      "integrity": "sha512-ZzKnc7ml/91fOPh5bANBL4vUlWPIYYv11waCtWTkl2TRN+LEmBg60Q1MA8gqV4hEp4MGfSj9JiHz91zw/gTDXg=="
+    }
+  }
+}
diff --git a/tests/unit/bokeh/embed/ext_package_no_main/package.json b/tests/unit/bokeh/embed/ext_package_no_main/package.json
new file mode 100644
--- /dev/null
+++ b/tests/unit/bokeh/embed/ext_package_no_main/package.json
@@ -0,0 +1,11 @@
+{
+  "name": "ext_package_no_main",
+  "version": "0.0.1",
+  "description": "",
+  "license": "BSD-3-Clause",
+  "keywords": [],
+  "repository": {},
+  "dependencies": {
+    "@bokeh/bokehjs": "^2.1.1"
+  }
+}
diff --git a/tests/unit/bokeh/embed/ext_package_no_main/tsconfig.json b/tests/unit/bokeh/embed/ext_package_no_main/tsconfig.json
new file mode 100644
--- /dev/null
+++ b/tests/unit/bokeh/embed/ext_package_no_main/tsconfig.json
@@ -0,0 +1,38 @@
+{
+  "compilerOptions": {
+    "noImplicitAny": true,
+    "noImplicitThis": true,
+    "noImplicitReturns": true,
+    "noUnusedLocals": true,
+    "noUnusedParameters": true,
+    "strictNullChecks": true,
+    "strictBindCallApply": false,
+    "strictFunctionTypes": false,
+    "strictPropertyInitialization": false,
+    "alwaysStrict": true,
+    "noErrorTruncation": true,
+    "noEmitOnError": false,
+    "allowJs": false,
+    "declaration": true,
+    "sourceMap": true,
+    "importHelpers": false,
+    "experimentalDecorators": true,
+    "module": "ES2020",
+    "moduleResolution": "node",
+    "resolveJsonModule": true,
+    "esModuleInterop": true,
+    "target": "ES2017",
+    "lib": ["es2017", "dom", "dom.iterable"],
+    "baseUrl": ".",
+    "outDir": "./dist/lib",
+    "paths": {
+      "@bokehjs/*": [
+        "./node_modules/@bokeh/bokehjs/build/js/lib/*",
+        "./node_modules/@bokeh/bokehjs/build/js/types/*"
+      ]
+    }
+  },
+  "include": [
+    "./**/*.ts"
+  ]
+}
diff --git a/tests/unit/bokeh/embed/test_bundle.py b/tests/unit/bokeh/embed/test_bundle.py
--- a/tests/unit/bokeh/embed/test_bundle.py
+++ b/tests/unit/bokeh/embed/test_bundle.py
@@ -128,6 +128,39 @@ def test_with_Server_resources(self) -> None:
         assert len(bundle.js_files) == 2
         assert bundle.js_files[1] == "http://localhost:5006/static/extensions/latex_label/latex_label.js"
 
+class Test_bundle_ext_package_no_main:
+
+    @classmethod
+    def setup_class(cls):
+        base_dir = dirname(__file__)
+        build(join(base_dir, "ext_package_no_main"), rebuild=True)
+
+    @classmethod
+    def teardown_class(cls):
+        del Model.model_class_reverse_map["ext_package_no_main.AModel"]
+        extension_dirs.clear()
+
+    def test_with_INLINE_resources(self) -> None:
+        from ext_package_no_main import AModel
+        model = AModel()
+        bundle = beb.bundle_for_objs_and_resources([model], "inline")
+        assert len(bundle.js_files) == 0
+        assert len(bundle.js_raw) == 3
+
+    def test_with_CDN_resources(self) -> None:
+        from ext_package_no_main import AModel
+        model = AModel()
+        bundle = beb.bundle_for_objs_and_resources([model], "cdn")
+        assert len(bundle.js_files) == 1
+        assert len(bundle.js_raw) == 2
+
+    def test_with_Server_resources(self) -> None:
+        from ext_package_no_main import AModel
+        model = AModel()
+        bundle = beb.bundle_for_objs_and_resources([model], "server")
+        assert len(bundle.js_files) == 2
+        assert len(bundle.js_raw) == 1
+
 #-----------------------------------------------------------------------------
 # Private API
 #-----------------------------------------------------------------------------