From 55db5c187a1501345d2b1f8d2192cc844735a223 Mon Sep 17 00:00:00 2001 From: pngwn Date: Fri, 27 May 2022 02:17:13 +0100 Subject: [PATCH] add cdn entrypoint (#1387) * add cdn entrypoint * cleanup --- ui/package.json | 1 + ui/packages/app/build_plugins.ts | 120 +++++++++++++++++++++++++++++++ ui/packages/app/src/main.ts | 1 - ui/packages/app/vite.config.js | 52 ++++---------- ui/pnpm-lock.yaml | 65 +++++++++++++++++ 5 files changed, 199 insertions(+), 40 deletions(-) create mode 100644 ui/packages/app/build_plugins.ts diff --git a/ui/package.json b/ui/package.json index 2e78875920..4d4f9e503a 100644 --- a/ui/package.json +++ b/ui/package.json @@ -34,6 +34,7 @@ "babylonjs": "^4.2.1", "babylonjs-loaders": "^4.2.1", "happy-dom": "^2.49.0", + "node-html-parser": "^5.3.3", "npm-run-all": "^4.1.5", "plotly.js-dist-min": "^2.10.1", "polka": "^1.0.0-next.22", diff --git a/ui/packages/app/build_plugins.ts b/ui/packages/app/build_plugins.ts new file mode 100644 index 0000000000..4490046818 --- /dev/null +++ b/ui/packages/app/build_plugins.ts @@ -0,0 +1,120 @@ +import type { Plugin } from "vite"; +import { parse } from "node-html-parser"; + +import path from "path"; +import fs from "fs"; + +export function inject_ejs(): Plugin { + return { + name: "inject-ejs", + enforce: "post", + transformIndexHtml: (html) => { + return html.replace( + /%gradio_config%/, + `` + ); + } + }; +} + +interface PatchDynamicImportOptionms { + mode: "cdn" | "local"; + gradio_version: string; + cdn_url: string; +} + +export function patch_dynamic_import({ + mode, + gradio_version, + cdn_url +}: PatchDynamicImportOptionms): Plugin { + return { + name: "patch-dynamic-import", + enforce: "post", + writeBundle(config, bundle) { + if (mode !== "cdn") return; + + const import_re = /import\(((?:'|")[\.\/a-zA-Z0-9]*(?:'|"))\)/g; + const import_meta = `${"import"}.${"meta"}.${"url"}`; + + for (const file in bundle) { + const chunk = bundle[file]; + if (chunk.type === "chunk") { + if (chunk.code.indexOf("import(") > -1) { + const fix_fn = `const VERSION_RE = new RegExp("${gradio_version}\/", "g");function import_fix(mod, base) {const url = new URL(mod, base); return import(\`${cdn_url}\${url.pathname?.startsWith('/') ? url.pathname.substring(1).replace(VERSION_RE, "") : url.pathname.replace(VERSION_RE, "")}\`);}`; + chunk.code = + fix_fn + + chunk.code.replace(import_re, `import_fix($1, ${import_meta})`); + + if (!config.dir) break; + const output_location = path.join(config.dir, chunk.fileName); + fs.writeFileSync(output_location, chunk.code); + } + } + } + } + }; +} + +export function generate_cdn_entry({ + enable, + cdn_url +}: { + enable: boolean; + cdn_url: string; +}): Plugin { + return { + name: "generate-cdn-entry", + enforce: "post", + writeBundle(config, bundle) { + if (!enable) return; + + if ( + !config.dir || + !bundle["index.html"] || + bundle["index.html"].type !== "asset" + ) + return; + + const tree = parse(bundle["index.html"].source as string); + const script = + Array.from(tree.querySelectorAll("script[type=module]")).find((node) => + node.attributes.src?.startsWith(cdn_url) + )?.attributes.src || ""; + const styles = + Array.from(tree.querySelectorAll("link[rel=stylesheet]")).find((node) => + node.attributes.href?.startsWith(cdn_url) + )?.attributes.href || ""; + + const output_location = path.join(config.dir, "gradio.js"); + + fs.writeFileSync(output_location, make_entry(script, styles)); + } + }; +} + +function make_entry(script: string, style: string) { + const make_style = ` +function make_style(href) { + const link = document.createElement('link'); + link.rel = 'stylesheet'; + link.href = href; + document.head.appendChild(link); +}`; + + const make_script = ` +function make_script(src) { + const script = document.createElement('script'); + script.type = 'module'; + script.setAttribute("crossorigin", ""); + script.src = src; + document.head.appendChild(script); +}`; + + return ` +${make_style} +${make_script} +make_script("${script}"); +make_style("${style}"); +`; +} diff --git a/ui/packages/app/src/main.ts b/ui/packages/app/src/main.ts index b73f0b312a..f0941cdd4e 100644 --- a/ui/packages/app/src/main.ts +++ b/ui/packages/app/src/main.ts @@ -14,7 +14,6 @@ interface CustomWindow extends Window { declare let window: CustomWindow; declare let BACKEND_URL: string; -declare let BACKEND_URL_TEST: string; declare let BUILD_MODE: string; interface Component { diff --git a/ui/packages/app/vite.config.js b/ui/packages/app/vite.config.js index 753eb60800..563eb3bf4b 100644 --- a/ui/packages/app/vite.config.js +++ b/ui/packages/app/vite.config.js @@ -2,14 +2,17 @@ import { defineConfig } from "vite"; import { svelte } from "@sveltejs/vite-plugin-svelte"; import sveltePreprocess from "svelte-preprocess"; -import path from "path"; -import fs from "fs"; +import { + inject_ejs, + patch_dynamic_import, + generate_cdn_entry +} from "./build_plugins"; // this is dupe config, gonna try fix this import tailwind from "tailwindcss"; import nested from "tailwindcss/nesting/index.js"; -const GRADIO_VERSION = process.env.GRADIO_VERSION; +const GRADIO_VERSION = process.env.GRADIO_VERSION || ""; //@ts-ignore export default defineConfig(({ mode }) => { @@ -55,42 +58,13 @@ export default defineConfig(({ mode }) => { postcss: { plugins: [tailwind, nested] } }) }), - { - name: "inject-ejs", - enforce: "post", - transformIndexHtml: (html) => { - return html.replace( - /%gradio_config%/, - `` - ); - }, - - writeBundle(config, bundle) { - if (!is_cdn) return; - - const import_re = /import\(((?:'|")[\.\/a-zA-Z0-9]*(?:'|"))\)/g; - const import_meta = `${"import"}.${"meta"}.${"url"}`; - - for (const file in bundle) { - const chunk = bundle[file]; - if (chunk.type === "chunk") { - if (chunk.code.indexOf("import(") > -1) { - const fix_fn = `const VERSION_RE = new RegExp("${GRADIO_VERSION}\/", "g");function import_fix(mod, base) {const url = new URL(mod, base); return import(\`${CDN_URL}\${url.pathname?.startsWith('/') ? url.pathname.substring(1).replace(VERSION_RE, "") : url.pathname.replace(VERSION_RE, "")}\`);}`; - chunk.code = - fix_fn + - chunk.code.replace( - import_re, - `import_fix($1, ${import_meta})` - ); - - if (!config.dir) break; - const output_location = path.join(config.dir, chunk.fileName); - fs.writeFileSync(output_location, chunk.code); - } - } - } - } - } + inject_ejs(), + patch_dynamic_import({ + mode: is_cdn ? "cdn" : "local", + gradio_version: GRADIO_VERSION, + cdn_url: CDN_URL + }), + generate_cdn_entry({ enable: is_cdn, cdn_url: CDN_URL }) ], test: { environment: "happy-dom", diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml index 6293fb679a..e2395aa459 100644 --- a/ui/pnpm-lock.yaml +++ b/ui/pnpm-lock.yaml @@ -16,6 +16,7 @@ importers: babylonjs: ^4.2.1 babylonjs-loaders: ^4.2.1 happy-dom: ^2.49.0 + node-html-parser: ^5.3.3 npm-run-all: ^4.1.5 plotly.js-dist-min: ^2.10.1 polka: ^1.0.0-next.22 @@ -45,6 +46,7 @@ importers: babylonjs: 4.2.2 babylonjs-loaders: 4.2.2 happy-dom: 2.49.0 + node-html-parser: 5.3.3 npm-run-all: 4.1.5 plotly.js-dist-min: 2.11.1 polka: 1.0.0-next.22 @@ -1447,6 +1449,10 @@ packages: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} + /boolbase/1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + dev: false + /brace-expansion/1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} dependencies: @@ -1660,6 +1666,21 @@ packages: which: 1.3.1 dev: false + /css-select/4.3.0: + resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 4.3.1 + domutils: 2.8.0 + nth-check: 2.1.1 + dev: false + + /css-what/6.1.0: + resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + engines: {node: '>= 6'} + dev: false + /css.escape/1.5.1: resolution: {integrity: sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=} dev: false @@ -1828,6 +1849,33 @@ packages: resolution: {integrity: sha512-R305kwb5CcMDIpSHUnLyIAp7SrSPBx6F0VfQFB3M75xVMHhXJJIdePYgbPPh1o57vCHNu5QztokWUPsLjWzFqw==} dev: false + /dom-serializer/1.4.1: + resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + entities: 2.2.0 + dev: false + + /domelementtype/2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + dev: false + + /domhandler/4.3.1: + resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} + engines: {node: '>= 4'} + dependencies: + domelementtype: 2.3.0 + dev: false + + /domutils/2.8.0: + resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} + dependencies: + dom-serializer: 1.4.1 + domelementtype: 2.3.0 + domhandler: 4.3.1 + dev: false + /electron-to-chromium/1.4.59: resolution: {integrity: sha512-AOJ3cAE0TWxz4fQ9zkND5hWrQg16nsZKVz9INOot1oV//u4wWu5xrj9CQMmPTYskkZRunSRc9sAnr4EkexXokg==} dev: true @@ -1842,6 +1890,10 @@ packages: once: 1.4.0 dev: false + /entities/2.2.0: + resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} + dev: false + /error-ex/1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} dependencies: @@ -2727,6 +2779,13 @@ packages: whatwg-url: 5.0.0 dev: false + /node-html-parser/5.3.3: + resolution: {integrity: sha512-ncg1033CaX9UexbyA7e1N0aAoAYRDiV8jkTvzEnfd1GDvzFdrsXLzR4p4ik8mwLgnaKP/jyUFWDy9q3jvRT2Jw==} + dependencies: + css-select: 4.3.0 + he: 1.2.0 + dev: false + /node-releases/2.0.1: resolution: {integrity: sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==} dev: true @@ -2768,6 +2827,12 @@ packages: string.prototype.padend: 3.1.3 dev: false + /nth-check/2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + dependencies: + boolbase: 1.0.0 + dev: false + /object-hash/2.2.0: resolution: {integrity: sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==} engines: {node: '>= 6'}