Comfy-Org / Comfy-Org/ComfyUI_frontend

RFC: Standardize how custom node extensions declare and load JS library dependencies

Open
#10,701 0 comments 0 reactions 0 assignees View on GitHub
Custom Node enhancement Public API
Dominant language
TypeScript
Stars
2k
Forks
705
Avg merge
1d 8h
Merged PRs (30d)
512

Description

## Problem

Custom node extensions that need third-party JS libraries (markdown parsers, visualization libraries, code editors) cannot place them in `WEB_DIRECTORY` because the frontend unconditionally `import()`s every `.js` file found there. This forces node packs to use workarounds like registering custom aiohttp static routes (`kjweb_async`, `mtb_async`), which break on Cloud.

### Root Cause

1. Backend (`server.py`): The `/extensions` endpoint uses `glob("**/*.js")` to find every JS file in all `WEB_DIRECTORY` paths
2. Frontend (`extensionService.ts`): `loadExtensions()` calls `import()` on every URL returned — no filtering, no manifest, no opt-in
3. Vendored libraries (e.g., `marked.min.js`) get executed as ES modules on every page load, causing errors and global pollution

### Current Workarounds

| Pack | Workaround | How |
|------|-----------|-----|
| KJNodes | `kjweb_async/` folder | Custom `web.static("/kjweb_async", ...)` aiohttp route |
| comfy_mtb | `web_async/` folder | Custom `web.static("/mtb_async", ...)` aiohttp route |

Both use a `loadScript()` helper to inject `` tags on demand. Both break on Cloud.

## Proposed Layered Solution

### Short-term (S, in progress)

**Expose commonly-needed libraries from the frontend bundle.**

`marked` and `DOMPurify` are already bundled as direct dependencies. PR #10700 exposes `renderMarkdownToHtml()` on `ExtensionManager`, eliminating the need for custom nodes to bundle their own copies.

```js
// Custom node extension can now do:
const html = app.extensionManager.renderMarkdownToHtml(nodeData.description)
```

### Medium-term (S-M)

**Static subdirectory convention**: Backend treats a subdirectory (e.g., `web/libs/` or `web/static/`) as served-but-not-imported.

```python
# Backend change (~5 lines):
# When building the /extensions response, exclude files under a `libs/` subdirectory
# Files are still served via the existing static route, just not included in the glob
```

This formalizes what `kjweb_async` does manually, without requiring custom `add_routes()` calls. Cloud-compatible because files are served from the standard extension static path.

### Long-term (M)

**Extension manifest in `pyproject.toml`**: Custom nodes already declare metadata in `[tool.comfy]`. Extend this to declare which files are extensions vs. static assets:

```toml
[tool.comfy]
PublisherId = "kijai"
DisplayName = "KJNodes"

[tool.comfy.web]
extensions = ["web/js/*.js"] # Files to import() as extensions
static = ["web/libs/*"] # Files to serve but not auto-import
```

This gives the backend and frontend a proper manifest for filtering, without breaking any existing packs (missing manifest = current behavior).

## Design Considerations

- **Backward compatibility**: All solutions should be additive — existing `WEB_DIRECTORY` behavior unchanged
- **Cloud compatibility**: Solutions must work without custom aiohttp routes
- **Deduplication**: If multiple packs bundle the same library, consider shared asset serving
- **Security**: Exposed utilities should be safe by default (e.g., `renderMarkdownToHtml` includes DOMPurify sanitization)

## References

- KJNodes `kjweb_async` pattern: [__init__.py](https://github.com/kijai/ComfyUI-KJNodes) — `web.static("/kjweb_async", ...)`
- comfy_mtb `web_async` pattern: [comfy_mtb](https://github.com/melmass/comfy_mtb)
- Frontend extension loading: `src/services/extensionService.ts` L31-53
- Backend extension serving: `server.py` `/extensions` endpoint

┆Issue is synchronized with this [Notion page](https://www.notion.so/Issue-10701-RFC-Standardize-how-custom-node-extensions-declare-and-load-JS-library-dependencies-3326d73d365081f9a266f24419f54584) by [Unito](https://www.unito.io)

Contributor guide

Open the contributing guide

Research direction

Start by reading src/services/extensionService.ts L31-53 and the /extensions endpoint in server.py to trace how JavaScript files are discovered, served, and imported. Compare the short-, medium-, and long-term proposals with the backward-compatibility and Cloud requirements; done requires an agreed standard and an implementation path, but this RFC does not select one.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, python, typescript
Domain
backend, cloud, frontend
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.