Skip to content

Commit

Permalink
allow custom component authors to provide custom vite plugins and sve…
Browse files Browse the repository at this point in the history
…lte preprocessors (gradio-app#6787)
  • Loading branch information
pngwn authored Apr 25, 2024
1 parent 2e469a5 commit 15a7106
Show file tree
Hide file tree
Showing 12 changed files with 486 additions and 55 deletions.
7 changes: 7 additions & 0 deletions .changeset/eleven-nails-wait.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@gradio/preview": minor
"gradio": minor
"gradio_test": minor
---

feat:allow custom component authors to provide custom vite plugins and svelte preprocessors
4 changes: 4 additions & 0 deletions gradio/cli/commands/components/_create_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,10 @@ def ignore(_src, names):
source_package_json = _modify_js_deps(source_package_json, "dependencies", p)
source_package_json = _modify_js_deps(source_package_json, "devDependencies", p)
(frontend / "package.json").write_text(json.dumps(source_package_json, indent=2))
shutil.copy(
str(Path(__file__).parent / "files" / "gradio.config.js"),
frontend / "gradio.config.js",
)


def _replace_old_class_name(old_class_name: str, new_class_name: str, content: str):
Expand Down
6 changes: 6 additions & 0 deletions gradio/cli/commands/components/files/gradio.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default {
plugins: [],
svelte: {
preprocess: [],
},
};
27 changes: 27 additions & 0 deletions guides/05_custom-components/05_frontend.md
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,33 @@ For those interested in design customization, we provide the CSS variables consi

[Storybook Link](https://gradio.app/main/docs/js/storybook)

## Custom configuration

If you want to make use of the vast vite ecosystem, you can use the `gradio.config.js` file to configure your component's build process. This allows you to make use of tools like tailwindcss, mdsvex, and more.

Currently, it is possible to configure the following:

Vite options:
- `plugins`: A list of vite plugins to use.

Svelte options:
- `preprocess`: A list of svelte preprocessors to use.

The `gradio.config.js` file should be placed in the root of your component's `frontend` directory. A default config file is created for you when you create a new component. But you can also create your own config file and use it to customize your component's build process.

```typescript
import tailwindcss from "@tailwindcss/vite";
import { mdsvex } from "mdsvex";

export default {
plugins: [tailwindcss()],
svelte: {
preprocess: [
mdsvex()
]
}
};
```

## Conclusion

Expand Down
18 changes: 17 additions & 1 deletion js/preview/src/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,22 @@ export async function make_build({
const pkg = JSON.parse(
fs.readFileSync(join(source_dir, "package.json"), "utf-8")
);
let component_config = {
plugins: [],
svelte: {
preprocess: []
}
};

if (
comp.frontend_dir &&
fs.existsSync(join(comp.frontend_dir, "gradio.config.js"))
) {
const m = await import(join(comp.frontend_dir, "gradio.config.js"));

component_config.plugins = m.default.plugins || [];
component_config.svelte.preprocess = m.default.svelte?.preprocess || [];
}

const exports: string[][] = [
["component", pkg.exports["."] as string],
Expand All @@ -45,7 +61,7 @@ export async function make_build({
root: source_dir,
configFile: false,
plugins: [
...plugins,
...plugins(component_config),
make_gradio_plugin({ mode: "build", svelte_dir })
],
build: {
Expand Down
45 changes: 40 additions & 5 deletions js/preview/src/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,11 @@ export async function create_server({
python_path
}: ServerOptions): Promise<void> {
process.env.gradio_mode = "dev";
const imports = generate_imports(component_dir, root_dir, python_path);
const [imports, config] = await generate_imports(
component_dir,
root_dir,
python_path
);

const svelte_dir = join(root_dir, "assets", "svelte");

Expand All @@ -55,7 +59,7 @@ export async function create_server({
}
},
plugins: [
...plugins,
...plugins(config),
make_gradio_plugin({
mode: "dev",
backend_port,
Expand Down Expand Up @@ -107,11 +111,18 @@ function to_posix(_path: string): string {
return _path.replace(/\\/g, "/");
}

function generate_imports(
export interface ComponentConfig {
plugins: any[];
svelte: {
preprocess: unknown[];
};
}

async function generate_imports(
component_dir: string,
root: string,
python_path: string
): string {
): Promise<[string, ComponentConfig]> {
const components = find_frontend_folders(component_dir);

const component_entries = components.flatMap((component) => {
Expand All @@ -123,6 +134,30 @@ function generate_imports(
);
}

let component_config = {
plugins: [],
svelte: {
preprocess: []
}
};

await Promise.all(
component_entries.map(async (component) => {
if (
component.frontend_dir &&
fs.existsSync(join(component.frontend_dir, "gradio.config.js"))
) {
const m = await import(
join(component.frontend_dir, "gradio.config.js")
);

component_config.plugins = m.default.plugins || [];
component_config.svelte.preprocess = m.default.svelte?.preprocess || [];
} else {
}
})
);

const imports = component_entries.reduce((acc, component) => {
const pkg = JSON.parse(
fs.readFileSync(join(component.frontend_dir, "package.json"), "utf-8")
Expand Down Expand Up @@ -151,5 +186,5 @@ function generate_imports(
},\n`;
}, "");

return `{${imports}}`;
return [`{${imports}}`, component_config];
}
86 changes: 47 additions & 39 deletions js/preview/src/plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { viteCommonjs } from "@originjs/vite-plugin-commonjs";
import sucrase from "@rollup/plugin-sucrase";
import { createLogger } from "vite";
import { join } from "path";
import { type ComponentConfig } from "./dev";
import type { Preprocessor, PreprocessorGroup } from "svelte/compiler";

const svelte_codes_to_ignore: Record<string, string> = {
"reactive-component": "Icon"
Expand All @@ -13,46 +15,52 @@ const svelte_codes_to_ignore: Record<string, string> = {
const RE_SVELTE_IMPORT =
/import\s+([\w*{},\s]+)\s+from\s+['"](svelte|svelte\/internal)['"]/g;
const RE_BARE_SVELTE_IMPORT = /import ("|')svelte(\/\w+)*("|')(;)*/g;
export const plugins: PluginOption[] = [
viteCommonjs() as Plugin,
svelte({
onwarn(warning, handler) {
if (
svelte_codes_to_ignore.hasOwnProperty(warning.code) &&
svelte_codes_to_ignore[warning.code] &&
warning.message.includes(svelte_codes_to_ignore[warning.code])
) {
return;
}
handler!(warning);
},
prebundleSvelteLibraries: false,
hot: true,
compilerOptions: {
discloseVersion: false
},
preprocess: [
{
script: ({ attributes, filename, content }) => {
if (attributes.lang === "ts") {
const compiledCode = transform(content, {
transforms: ["typescript"],
keepUnusedImports: true
});
return {
code: compiledCode.code,
map: compiledCode.sourceMap
};
}
export function plugins(config: ComponentConfig): PluginOption[] {
const _additional_plugins = config.plugins || [];
const _additional_svelte_preprocess = config.svelte?.preprocess || [];
return [
viteCommonjs() as Plugin,
svelte({
onwarn(warning, handler) {
if (
svelte_codes_to_ignore.hasOwnProperty(warning.code) &&
svelte_codes_to_ignore[warning.code] &&
warning.message.includes(svelte_codes_to_ignore[warning.code])
) {
return;
}
}
]
}) as unknown as Plugin,
sucrase({
transforms: ["typescript"],
include: ["**/*.ts", "**/*.tsx"]
}) as unknown as Plugin
];
handler!(warning);
},
prebundleSvelteLibraries: false,
hot: true,
compilerOptions: {
discloseVersion: false
},
preprocess: [
{
script: ({ attributes, filename, content }) => {
if (attributes.lang === "ts") {
const compiledCode = transform(content, {
transforms: ["typescript"],
keepUnusedImports: true
});
return {
code: compiledCode.code,
map: compiledCode.sourceMap
};
}
}
},
...(_additional_svelte_preprocess as PreprocessorGroup[])
]
}) as unknown as Plugin,
sucrase({
transforms: ["typescript"],
include: ["**/*.ts", "**/*.tsx"]
}) as unknown as Plugin,
..._additional_plugins
];
}

interface GradioPluginOptions {
mode: "dev" | "build";
Expand Down
95 changes: 86 additions & 9 deletions js/preview/test/test/frontend/Index.svelte
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script lang="ts">
import "./main.css";
import { JsonView } from "@zerodevx/svelte-json-view";
import type { Gradio } from "@gradio/utils";
Expand All @@ -25,12 +26,88 @@
}>;
</script>

<Block {visible} {elem_id} {elem_classes} {container} {scale} {min_width}>
<StatusTracker
autoscroll={gradio.autoscroll}
i18n={gradio.i18n}
{...loading_status}
/>

<JsonView json={value} />
</Block>
<div class="relative flex min-h-screen flex-col justify-center overflow-hidden">
<div
class="relative bg-white px-6 pt-10 pb-8 shadow-xl ring-1 ring-gray-900/5 sm:mx-auto sm:max-w-lg sm:rounded-lg sm:px-10"
>
<div class="mx-auto max-w-md">
<h1 class="text-xl! font-bold! text-gray-900">
<span class="text-blue-500">Tailwind</span> in Gradio
</h1>
<h2><em>(i hope you're happy now)</em></h2>
<div class="divide-y divide-gray-300/50">
<div class="space-y-6 py-8 text-base leading-7 text-gray-600">
<p>
An advanced online playground for Tailwind CSS, including support
for things like:
</p>
<ul class="space-y-4 my-4!">
<li class="flex items-center">
<svg
class="h-6 w-6 flex-none fill-sky-100 stroke-sky-500 stroke-2 mr-4"
stroke-linecap="round"
stroke-linejoin="round"
>
<circle cx="12" cy="12" r="11" />
<path
d="m8 13 2.165 2.165a1 1 0 0 0 1.521-.126L16 9"
fill="none"
/>
</svg>
<p class="ml-4">
Customizing your
<code class="text-sm font-bold text-gray-900"
>tailwind.config.js</code
> file
</p>
</li>
<li class="flex items-center">
<svg
class="h-6 w-6 flex-none fill-sky-100 stroke-sky-500 stroke-2 mr-4"
stroke-linecap="round"
stroke-linejoin="round"
>
<circle cx="12" cy="12" r="11" />
<path
d="m8 13 2.165 2.165a1 1 0 0 0 1.521-.126L16 9"
fill="none"
/>
</svg>
<p class="ml-4">
Extracting classes with
<code class="text-sm font-bold text-gray-900">@apply</code>
</p>
</li>
<li class="flex items-center">
<svg
class="h-6 w-6 flex-none fill-sky-100 stroke-sky-500 stroke-2 mr-4"
stroke-linecap="round"
stroke-linejoin="round"
>
<circle cx="12" cy="12" r="11" />
<path
d="m8 13 2.165 2.165a1 1 0 0 0 1.521-.126L16 9"
fill="none"
/>
</svg>
<p class="ml-4">Code completion with instant preview</p>
</li>
</ul>
<p>
Perfect for learning how the framework works, prototyping a new
idea, or creating a demo to share online.
</p>
</div>
<div class="pt-8 text-base font-semibold leading-7">
<p class="text-gray-900">Want to dig deeper into Tailwind?</p>
<p>
<a
href="https://tailwindcss.com/docs"
class="text-sky-500 hover:text-sky-600">Read the docs &rarr;</a
>
</p>
</div>
</div>
</div>
</div>
</div>
8 changes: 8 additions & 0 deletions js/preview/test/test/frontend/gradio.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import tailwindcss from "@tailwindcss/vite";

export default {
plugins: [tailwindcss()],
svelte: {
preprocess: require("svelte-preprocess")()
}
};
1 change: 1 addition & 0 deletions js/preview/test/test/frontend/main.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import "tailwindcss";
4 changes: 3 additions & 1 deletion js/preview/test/test/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
"@zerodevx/svelte-json-view": "^1.0.7"
},
"devDependencies": {
"@gradio/preview": "workspace:^"
"@gradio/preview": "workspace:^",
"@tailwindcss/vite": "4.0.0-alpha.14",
"tailwindcss": "4.0.0-alpha.14"
}
}
Loading

0 comments on commit 15a7106

Please sign in to comment.