Skip to content

Commit

Permalink
Fix custom component CLI on main/5.0 (#9482)
Browse files Browse the repository at this point in the history
* Add code

* add changeset

* WIP

* add changeset

* Working SSR

* WIP

* Proper ssr build

* fix paths

* fix

* revert .vscode change

* format

* lint

* uncomment

* fix

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
  • Loading branch information
freddyaboulton and gradio-pr-bot authored Oct 8, 2024
1 parent c3d93be commit bd6c5f2
Show file tree
Hide file tree
Showing 21 changed files with 241 additions and 55 deletions.
9 changes: 9 additions & 0 deletions .changeset/great-ghosts-find.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@gradio/preview": minor
"@self/app": minor
"@self/build": minor
"@self/spa": minor
"gradio": minor
---

feat:Fix custom component CLI on main/5.0
2 changes: 1 addition & 1 deletion gradio/cli/commands/components/files/pyproject_.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ keywords = [
"<<template>>"
]
# Add dependencies here
dependencies = ["gradio>=4.0,<5.0"]
dependencies = ["gradio>=4.0,<6.0"]
classifiers = [
'Development Status :: 3 - Alpha',
'Operating System :: OS Independent',
Expand Down
4 changes: 3 additions & 1 deletion gradio/node_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,10 @@ def start_node_process(
if GRADIO_LOCAL_DEV_MODE:
env["GRADIO_LOCAL_DEV_MODE"] = "1"

register_file = Path(__file__).parent.joinpath("templates", "register.mjs")

node_process = subprocess.Popen(
[node_path, SSR_APP_PATH],
[node_path, "--import", str(register_file), SSR_APP_PATH],
stdout=subprocess.DEVNULL,
env=env,
)
Expand Down
26 changes: 24 additions & 2 deletions gradio/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@ async def conditional_routing_middleware(
and not path.startswith("/gradio_api")
and path not in ["/config", "/login", "/favicon.ico"]
and not path.startswith("/theme")
and not path.startswith("/svelte")
and not path.startswith("/static")
):
if App.app_port is None:
Expand Down Expand Up @@ -508,6 +509,15 @@ def logout(user: str = Depends(get_current_user)):
# Main Routes
###############

@app.get("/svelte/{path:path}")
def _(path: str):
svelte_path = Path(BUILD_PATH_LIB) / "svelte"
return FileResponse(
routes_safe_join(
DeveloperPath(str(svelte_path)), UserProvidedPath(path)
)
)

@app.head("/", response_class=HTMLResponse)
@app.get("/", response_class=HTMLResponse)
def main(request: fastapi.Request, user: str = Depends(get_current_user)):
Expand Down Expand Up @@ -587,10 +597,18 @@ def static_resource(path: str):
static_file = routes_safe_join(STATIC_PATH_LIB, UserProvidedPath(path))
return FileResponse(static_file)

@router.get("/custom_component/{id}/{type}/{file_name}")
@router.get("/custom_component/{id}/{environment}/{type}/{file_name}")
def custom_component_path(
id: str, type: str, file_name: str, req: fastapi.Request
id: str,
environment: Literal["client", "server"],
type: str,
file_name: str,
req: fastapi.Request,
):
if environment not in ["client", "server"]:
raise HTTPException(
status_code=404, detail="Environment not supported."
)
config = app.get_blocks().config
components = config["components"]
location = next(
Expand Down Expand Up @@ -622,6 +640,10 @@ def custom_component_path(
UserProvidedPath(requested_path),
)

# Uncomment when we support custom component SSR
# if environment == "server":
# return PlainTextResponse(path)

key = f"{id}-{type}-{file_name}"

if key not in app.custom_component_hashes:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ This guide will cover how to get started making custom components.

You will need to have:

* Python 3.8+ (<a href="https://www.python.org/downloads/" target="_blank">install here</a>)
* Python 3.10+ (<a href="https://www.python.org/downloads/" target="_blank">install here</a>)
* pip 21.3+ (`python -m pip install --upgrade pip`)
* Node.js v16.14+ (<a href="https://nodejs.dev/en/download/package-manager/" target="_blank">install here</a>)
* npm 9+ (<a href="https://docs.npmjs.com/downloading-and-installing-node-js-and-npm/" target="_blank">install here</a>)
Expand Down
9 changes: 8 additions & 1 deletion guides/08_custom-components/06_frequently-asked-questions.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
# Frequently Asked Questions

## What do I need to install before using Custom Components?
Before using Custom Components, make sure you have Python 3.8+, Node.js v16.14+, npm 9+, and Gradio 4.0+ installed.
Before using Custom Components, make sure you have Python 3.10+, Node.js v18+, npm 9+, and Gradio 4.0+ installed.

## Are custom components compatible between Gradio 4.0 and 5.0?

Custom components built with Gradio 5.0 should be compatible with Gradio 4.0. If you built your custom component in Gradio 4.0 you will have to rebuild your component to be compatible with Gradio 5.0. Simply follow these steps:
1. Update the `@gradio/preview` package. `cd` into the `frontend` directory and run `npm update`.
2. Modify the `dependencies` key in `pyproject.toml` to pin the maximum allowed Gradio version at version 5, e.g. `dependencies = ["gradio>=4.0,<6.0"]`.
3. Run the build and publish commands

## What templates can I use to create my custom component?
Run `gradio cc show` to see the list of built-in templates.
Expand Down
1 change: 1 addition & 0 deletions js/app/src/routes/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import "@gradio/theme/global.css";
import "@gradio/theme/pollen.css";
import "@gradio/theme/typography.css";
import "./svelte_init";
</script>

<slot></slot>
18 changes: 9 additions & 9 deletions js/app/src/routes/[...catchall]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@
let active_theme_mode: ThemeMode;
let intersecting: ReturnType<typeof create_intersection_store> = {
register: () => {},
subscribe: writable({}).subscribe,
subscribe: writable({}).subscribe
};
$: if (config?.app_id) {
Expand All @@ -126,7 +126,7 @@
message: "",
load_status: "pending",
status: "sleeping",
detail: "SLEEPING",
detail: "SLEEPING"
};
let app: ClientType = data.app;
Expand Down Expand Up @@ -158,7 +158,7 @@
message: "",
load_status: "complete",
status: "running",
detail: "RUNNING",
detail: "RUNNING"
};
css_ready = true;
Expand All @@ -181,7 +181,7 @@
app = await Client.connect(data.api_url, {
status_callback: handle_status,
with_null_state: true,
events: ["data", "log", "status", "render"],
events: ["data", "log", "status", "render"]
});
if (!app.config) {
Expand All @@ -207,8 +207,8 @@
new CustomEvent("render", {
bubbles: true,
cancelable: false,
composed: true,
}),
composed: true
})
);
}
Expand All @@ -219,7 +219,7 @@
async function mount_space_header(
space_id: string | null | undefined,
is_embed: boolean,
is_embed: boolean
): Promise<void> {
if (space_id && !is_embed && window.self === window.top) {
if (spaceheader) {
Expand All @@ -240,7 +240,7 @@
const url = new URL(window.location.toString());
const url_color_mode: ThemeMode | null = url.searchParams.get(
"__theme",
"__theme"
) as ThemeMode | null;
new_theme_mode = theme_mode || url_color_mode || "system";
Expand All @@ -260,7 +260,7 @@
function update_scheme(): "light" | "dark" {
let _theme: "light" | "dark" = window?.matchMedia?.(
"(prefers-color-scheme: dark)",
"(prefers-color-scheme: dark)"
).matches
? "dark"
: "light";
Expand Down
20 changes: 20 additions & 0 deletions js/app/src/routes/svelte_init.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as svelte from "svelte";
const is_browser = typeof window !== "undefined";
if (is_browser) {
const o = {
SvelteComponent: svelte.SvelteComponent
};
for (const key in svelte) {
if (key === "SvelteComponent") continue;
if (key === "SvelteComponentDev") {
//@ts-ignore
o[key] = o["SvelteComponent"];
} else {
//@ts-ignore
o[key] = svelte[key];
}
}
window.__gradio__svelte__internal = o;
window.__gradio__svelte__internal["globals"] = {};
window.globals = window;
}
43 changes: 38 additions & 5 deletions js/app/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,17 @@ export default defineConfig(({ mode }) => {
resolve: {
conditions: ["gradio"]
},
build: {
rollupOptions: {
external: [
"/svelte/svelte.js",
"/svelte/svelte-submodules.js",
"./svelte/svelte-submodules.js",
"./svelte/svelte.js"
]
},
minify: false
},
define: {
BUILD_MODE: production ? JSON.stringify("prod") : JSON.stringify("dev"),
BACKEND_URL: production
Expand Down Expand Up @@ -73,23 +84,45 @@ export default defineConfig(({ mode }) => {
}
},
ssr: {
noExternal: ["@gradio/*", "@huggingface/space-header"]
noExternal: ["@gradio/*", "@huggingface/space-header"],
external: ["svelte", "svelte/*"]
},

optimizeDeps: {
exclude: ["@gradio/*"]
exclude: [
"@gradio/*",
"svelte",
"svelte/*",
"./svelte/svelte-submodules.js",
"./svelte/svelte.js"
]
},
plugins: [
sveltekit(),
// resolve_svelte(development),

// generate_dev_entry({
// enable: !development && mode !== "test"
// }),
// inject_ejs(),
// generate_cdn_entry({ version: GRADIO_VERSION, cdn_base: CDN_BASE }),
// handle_ce_css(),
inject_component_loader({ mode })
inject_component_loader({ mode }),
{
name: "resolve_svelte",
enforce: "pre",
resolveId(id, importer, options) {
if (!options?.ssr) {
if (id === "svelte" || id === "svelte/internal") {
return { id: "../../../svelte/svelte.js", external: true };
}
if (id.startsWith("svelte/")) {
return {
id: "../../../svelte/svelte-submodules.js",
external: true
};
}
}
}
}
]
};
});
38 changes: 30 additions & 8 deletions js/build/out/component_loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@

const request_map = {};

const is_browser= typeof window !== "undefined";
const is_browser = typeof window !== "undefined";

export function load_component({ api_url, name, id, variant }) {

const comps = is_browser ? window.__GRADIO__CC__ : {};
const comps = is_browser && window.__GRADIO__CC__;

const _component_map = {
// eslint-disable-next-line no-undef
Expand Down Expand Up @@ -62,6 +61,9 @@ export function load_component({ api_url, name, id, variant }) {
}

function load_css(url) {
if(!is_browser) {
return Promise.resolve();
}
return new Promise((resolve, reject) => {
const link = document.createElement("link");
link.rel = "stylesheet";
Expand All @@ -73,12 +75,32 @@ function load_css(url) {
}

function get_component_with_css(api_url, id, variant) {
return Promise.all([
const environment = is_browser ? "client": "server";
let path;
if (environment === "server") {
// uncomment when we make gradio cc build support ssr
//path = await (await fetch(`${api_url}/custom_component/${id}/${variant}/index.js/server`)).text();
return Promise.all([
load_css(`${api_url}/custom_component/${id}/${variant}/style.css`),
import(
/* @vite-ignore */ `${api_url}/custom_component/${id}/${variant}/index.js`
/* @vite-ignore */
"@gradio/fallback"
)
]).then(([_, module]) => {
]).then(([_, module]) => {
return module;
});
}
});
}

path = `${api_url}/custom_component/${id}/${environment}/${variant}/index.js`;

return Promise.all([
load_css(`${api_url}/custom_component/${id}/${environment}/${variant}/style.css`),
import(
/* @vite-ignore */
path
)
]).then(([_, module]) => {
return module;
});

}
2 changes: 1 addition & 1 deletion js/build/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"author": "",
"license": "ISC",
"scripts": {
"build": "esbuild src/index.ts --platform=node --format=esm --target=node18 --bundle --packages=external --outfile=out/index.js"
"build": "esbuild src/index.ts --platform=node --format=esm --target=node18 --bundle --packages=external --outfile=out/index.js && cp src/component_loader.js out/"
},
"dependencies": {
"@gradio/theme": "workspace:^",
Expand Down
Loading

0 comments on commit bd6c5f2

Please sign in to comment.