Skip to content

Fix CDN requests in server mode for loading.css and es-module-shims - #8408

Merged
philippjfr merged 4 commits into
holoviz:mainfrom
nojaf:cdn-fixes
Feb 5, 2026
Merged

philippjfr merged 4 commits into
holoviz:mainfrom
nojaf:cdn-fixes

Conversation

@nojaf

@nojaf nojaf commented Feb 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes external CDN requests when running Panel in server mode, ensuring all resources are served locally.

Problem

When deploying Panel applications in server mode with PANEL_NPM_CDN configured to an internal registry, the app still made external network requests to:

  1. https://cdn.holoviz.org/panel/<version>/dist/css/loading.css
  2. https://cdn.jsdelivr.net/npm/es-module-shims@^1.10.0/dist/es-module-shims.min.js

This is problematic for air-gapped environments or deployments with strict network policies that require all resources to be served locally.

Root Causes

1. loading.css (panel/reactive.py)

The loading stylesheet URL was hardcoded using CDN_DIST, which always points to cdn.holoviz.org regardless of the resource mode:

stylesheets = [..., f'{CDN_DIST}css/loading.css']

2. es-module-shims (panel/io/resources.py)

This one is more subtle. The ReactiveESM class defines __javascript_raw__ at class definition time (import time), capturing the default CDN URL before PANEL_NPM_CDN is read:

__javascript_raw__ = [f"{config.npm_cdn}/es-module-shims@^1.10.0/dist/es-module-shims.min.js"]

Later, bundled_files() checks if URLs start with config.npm_cdn to map them to local files. But since config.npm_cdn now reflects the custom registry URL while the captured URL still has cdn.jsdelivr.net, the check fails and the file isn't served locally.

Solution

1. loading.css

Use get_dist_path() instead of CDN_DIST. This function respects the current resource mode and returns the local path in server mode.

2. es-module-shims

Expand the prefix matching in bundled_files() to recognize all common NPM CDN prefixes:

npm_cdn_prefixes = [
    config.npm_cdn,
    'https://cdn.jsdelivr.net/npm',
    'https://unpkg.com',
]

This ensures URLs captured at import time with the default CDN are still correctly mapped to local bundled files, regardless of the runtime PANEL_NPM_CDN setting.

@nojaf

nojaf commented Feb 4, 2026

Copy link
Copy Markdown
Contributor Author

Related issue #6161

@philippjfr

Copy link
Copy Markdown
Member

Thanks @nojaf, this looks good.

@nojaf

nojaf commented Feb 4, 2026

Copy link
Copy Markdown
Contributor Author

@philippjfr thanks, sorry for all the formatting changes.
Is there some command I can run to format all the touched files?
My IDE does format on save 😅

@codecov

codecov Bot commented Feb 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.45%. Comparing base (ec05ecd) to head (ad9171b).
⚠️ Report is 4 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #8408      +/-   ##
==========================================
- Coverage   86.09%   85.45%   -0.64%     
==========================================
  Files         349      349              
  Lines       54821    54841      +20     
==========================================
- Hits        47199    46867     -332     
- Misses       7622     7974     +352     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@hoxbro

hoxbro commented Feb 5, 2026

Copy link
Copy Markdown
Member

We don't have a formatter. I know VS Code have an option to save without formatting in the task menu (ctrl+shift+p)

I think the easiest way is doing something like git checkout main -p -- panel/io/resources.py and remove all the format changes + some manual changes if needed.

- Use get_dist_path() instead of CDN_DIST for loading.css in reactive.py
- Handle multiple npm CDN prefixes in bundled_files()
- Add patch_stylesheet case for LOCAL_DIST to custom dist_url
- Update test assertions to accept both CDN and local loading.css paths
@nojaf

nojaf commented Feb 5, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the feedback on minimizing the diff @hoxbro ! I've force-pushed a cleaner version.

One thing that would help future contributions: would you consider adding a formatter (like ruff format) to the pre-commit hooks? It would prevent these formatting inconsistencies from creeping in and make reviews easier for everyone.

@philippjfr

Copy link
Copy Markdown
Member

Appreciate it! I do think we should adopt more formatting rules, i.e. consistent quoting would be nice. Will do that one of these days.

@nojaf

nojaf commented Feb 5, 2026

Copy link
Copy Markdown
Contributor Author

Sounds great! I do notice that some tests fail @philippjfr, I'm a bit confused if they are related or not? Any thoughts?

@philippjfr

Copy link
Copy Markdown
Member

Very confused too, just ran them locally without issue. I'll restart them but it's a bit worrying that some of the resource related tests are failing, seemingly randomly.

@philippjfr

Copy link
Copy Markdown
Member

Guess it's not totally random, ubuntu fails consistently.

@philippjfr

Copy link
Copy Markdown
Member

Well not that consistently apparently, one just passed and when I run it on my linux box and it works just fine.

@philippjfr

Copy link
Copy Markdown
Member

Okay, I think this was a result of bokeh document state leaking across tests. Should be resolved now.

@philippjfr
philippjfr merged commit 03925c2 into holoviz:main Feb 5, 2026
16 of 20 checks passed
@nojaf

nojaf commented Feb 5, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for merging this, @philippjfr! Much appreciated. This problem has been on our radar for quite some time, and I'm excited to see it fixed!

@github-actions

Copy link
Copy Markdown

This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions Bot locked as resolved and limited conversation to collaborators May 19, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants