From a02ffbf3a61316822f8e1bc1d0fcbc600679f5a3 Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Tue, 7 Jan 2025 12:51:49 +0700 Subject: [PATCH 1/6] setup services in OnAfterRenderAsync which plays better with server pre rendering --- backend/FwLite/FwLiteShared/Pages/CrdtProject.razor | 3 ++- backend/FwLite/FwLiteShared/Pages/FwdataProject.razor | 3 ++- backend/FwLite/FwLiteShared/Pages/Home.razor | 5 +++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/backend/FwLite/FwLiteShared/Pages/CrdtProject.razor b/backend/FwLite/FwLiteShared/Pages/CrdtProject.razor index 2a775469e..bb320bd0c 100644 --- a/backend/FwLite/FwLiteShared/Pages/CrdtProject.razor +++ b/backend/FwLite/FwLiteShared/Pages/CrdtProject.razor @@ -13,8 +13,9 @@ private IAsyncDisposable? _disposable; - protected override async Task OnInitializedAsync() + protected override async Task OnAfterRenderAsync(bool firstRender) { + if (!firstRender) return; var fwLiteProvider = ScopedServices.GetRequiredService(); _disposable = await fwLiteProvider.InjectCrdtProject(JS, ScopedServices, ProjectName); } diff --git a/backend/FwLite/FwLiteShared/Pages/FwdataProject.razor b/backend/FwLite/FwLiteShared/Pages/FwdataProject.razor index 20110c079..7dfbe564c 100644 --- a/backend/FwLite/FwLiteShared/Pages/FwdataProject.razor +++ b/backend/FwLite/FwLiteShared/Pages/FwdataProject.razor @@ -13,8 +13,9 @@ private IAsyncDisposable? _disposable; - protected override async Task OnInitializedAsync() + protected override async Task OnAfterRenderAsync(bool firstRender) { + if (!firstRender) return; var fwLiteProvider = ScopedServices.GetRequiredService(); _disposable = await fwLiteProvider.InjectFwDataProject(JS, ScopedServices, ProjectName); } diff --git a/backend/FwLite/FwLiteShared/Pages/Home.razor b/backend/FwLite/FwLiteShared/Pages/Home.razor index d1c1ecff4..6602abc52 100644 --- a/backend/FwLite/FwLiteShared/Pages/Home.razor +++ b/backend/FwLite/FwLiteShared/Pages/Home.razor @@ -8,8 +8,9 @@ @*this looks empty because it is, but it's required to declare the route which is then used by the svelte router*@ @code { - protected override async Task OnInitializedAsync() + protected override async Task OnAfterRenderAsync(bool firstRender) { - await FwLiteProvider.SetService(JS, DotnetService.MiniLcmApi, null); + if (firstRender) + await FwLiteProvider.SetService(JS, DotnetService.MiniLcmApi, null); } } From 4757c2c762a0b459e23301d3b477490a3f5f41d5 Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Tue, 7 Jan 2025 12:52:23 +0700 Subject: [PATCH 2/6] when an override service is null, remove it from the services object instead of setting it to null --- backend/FwLite/FwLiteShared/Layout/SvelteLayout.razor | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/backend/FwLite/FwLiteShared/Layout/SvelteLayout.razor b/backend/FwLite/FwLiteShared/Layout/SvelteLayout.razor index bbc2bc5eb..2ad42a837 100644 --- a/backend/FwLite/FwLiteShared/Layout/SvelteLayout.razor +++ b/backend/FwLite/FwLiteShared/Layout/SvelteLayout.razor @@ -16,7 +16,11 @@ window.lexbox.DotNetServiceProvider.setOverrideServices(window.lexbox.FwLiteProvider); } const services = window.lexbox.FwLiteProvider; - services[key] = service; + if (service) { + services[key] = service; + } else { + delete services[key]; + } }; //called from FwLiteProvider.InjectCrdtProject window['notifyEntryUpdated'] = (projectName, entry) => { From 12bff6bf2b2f4c49cc5aaf93c368a65d9753b4b8 Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Tue, 7 Jan 2025 12:53:15 +0700 Subject: [PATCH 3/6] move invokeOnWindow to new Root component so it's shared between web and Maui, use that instead of Routes in each project --- backend/FwLite/FwLiteMaui/MainPage.xaml | 2 +- backend/FwLite/FwLiteMaui/wwwroot/index.html | 10 ---------- backend/FwLite/FwLiteShared/Root.razor | 14 ++++++++++++++ backend/FwLite/FwLiteShared/Routes.razor | 9 ++++++++- backend/FwLite/FwLiteWeb/Components/App.razor | 15 ++------------- 5 files changed, 25 insertions(+), 25 deletions(-) create mode 100644 backend/FwLite/FwLiteShared/Root.razor diff --git a/backend/FwLite/FwLiteMaui/MainPage.xaml b/backend/FwLite/FwLiteMaui/MainPage.xaml index ec18ea3c0..b0ad4a4c0 100644 --- a/backend/FwLite/FwLiteMaui/MainPage.xaml +++ b/backend/FwLite/FwLiteMaui/MainPage.xaml @@ -6,7 +6,7 @@ x:Class="FwLiteMaui.MainPage"> - + diff --git a/backend/FwLite/FwLiteMaui/wwwroot/index.html b/backend/FwLite/FwLiteMaui/wwwroot/index.html index 16b644ed4..4748ee3b8 100644 --- a/backend/FwLite/FwLiteMaui/wwwroot/index.html +++ b/backend/FwLite/FwLiteMaui/wwwroot/index.html @@ -6,16 +6,6 @@ FwLiteMaui - diff --git a/backend/FwLite/FwLiteShared/Root.razor b/backend/FwLite/FwLiteShared/Root.razor new file mode 100644 index 000000000..62a616d80 --- /dev/null +++ b/backend/FwLite/FwLiteShared/Root.razor @@ -0,0 +1,14 @@ + + +@code { + +} diff --git a/backend/FwLite/FwLiteShared/Routes.razor b/backend/FwLite/FwLiteShared/Routes.razor index 00e759ac5..16ee987a7 100644 --- a/backend/FwLite/FwLiteShared/Routes.razor +++ b/backend/FwLite/FwLiteShared/Routes.razor @@ -1,8 +1,15 @@ @inject IJSRuntime jsRuntime @code { + bool firstNavigation = true; private async Task OnNavigateAsync(NavigationContext context) { - await jsRuntime.DurableInvokeVoidAsync("svelteNavigate", context.Path); + if (firstNavigation) + { + firstNavigation = false; + return; + } + if (RendererInfo.IsInteractive) + await jsRuntime.DurableInvokeVoidAsync("svelteNavigate", context.Path); } } diff --git a/backend/FwLite/FwLiteWeb/Components/App.razor b/backend/FwLite/FwLiteWeb/Components/App.razor index d99a9ce51..56cf7d752 100644 --- a/backend/FwLite/FwLiteWeb/Components/App.razor +++ b/backend/FwLite/FwLiteWeb/Components/App.razor @@ -5,22 +5,11 @@ - - - + - + From 16e8c9e4e9ca29e98e7fa0ef0c38b514158e9225 Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Tue, 7 Jan 2025 16:18:51 +0700 Subject: [PATCH 4/6] use vite dev for serving js --- .../FwLiteShared/Layout/SvelteLayout.razor | 26 +++++++++++++++---- backend/FwLite/FwLiteWeb/Components/App.razor | 2 +- frontend/viewer/src/lib/append-head-hack.ts | 15 +++++++++++ frontend/viewer/src/main.ts | 9 ++----- frontend/viewer/vite.config.ts | 22 ++++++---------- 5 files changed, 47 insertions(+), 27 deletions(-) create mode 100644 frontend/viewer/src/lib/append-head-hack.ts diff --git a/backend/FwLite/FwLiteShared/Layout/SvelteLayout.razor b/backend/FwLite/FwLiteShared/Layout/SvelteLayout.razor index 2ad42a837..6b35a9ab6 100644 --- a/backend/FwLite/FwLiteShared/Layout/SvelteLayout.razor +++ b/backend/FwLite/FwLiteShared/Layout/SvelteLayout.razor @@ -1,13 +1,21 @@ @inherits LayoutComponentBase @using FwLiteShared.Services +@using Microsoft.Extensions.Hosting @using Microsoft.Extensions.Logging @inject IJSRuntime JS @inject ILogger Logger @inject FwLiteProvider FwLiteProvider +@inject IHostEnvironment Environment; @implements IAsyncDisposable - - - +@if (useDevAssets) +{ + +} +else +{ + + +} diff --git a/frontend/viewer/src/lib/append-head-hack.ts b/frontend/viewer/src/lib/append-head-hack.ts new file mode 100644 index 000000000..2ebed2346 --- /dev/null +++ b/frontend/viewer/src/lib/append-head-hack.ts @@ -0,0 +1,15 @@ +if (import.meta.env.DEV) { + //when in dev mode, svelte and vite want to put style sheets in the head + //however blazor will remove them, so we need to put them in the body instead + const headerAppend = document.head.appendChild; + document.head.appendChild = function newAppend(node: T) { + //this is used for both svelte and vite imports + if (node.tagName === 'STYLE') { + document.getElementById('svelte-app')?.appendChild(node); + } else { + headerAppend.call(document.head, node); + } + return node; + }; +} + diff --git a/frontend/viewer/src/main.ts b/frontend/viewer/src/main.ts index 055068464..19ce5504b 100644 --- a/frontend/viewer/src/main.ts +++ b/frontend/viewer/src/main.ts @@ -1,5 +1,6 @@ //*// v1 Run with normal Svelte App (advantages: tailwind classes stay up to date) - +import 'vite/modulepreload-polyfill'; +import './lib/append-head-hack'; import './app.postcss'; import App from './App.svelte'; @@ -9,9 +10,3 @@ setupDotnetServiceProvider(); new App({ target: document.getElementById('svelte-app')!, }); - -/*/// v2 Run with web-component in shadow dom - -import './web-component'; - -//*/ diff --git a/frontend/viewer/vite.config.ts b/frontend/viewer/vite.config.ts index f5756d8e9..591375b77 100644 --- a/frontend/viewer/vite.config.ts +++ b/frontend/viewer/vite.config.ts @@ -3,10 +3,11 @@ import {svelte} from '@sveltejs/vite-plugin-svelte'; import {svelteTesting} from '@testing-library/svelte/vite'; // https://vitejs.dev/config/ -export default defineConfig(({ mode }) => { +export default defineConfig(({ mode, command }) => { const webComponent = mode === 'web-component'; return { - base: '/_content/FwLiteShared/viewer', + base: !webComponent && command == "build" ? '/_content/FwLiteShared/viewer' : '/', + build: { ...(webComponent ? { lib: { @@ -16,11 +17,13 @@ export default defineConfig(({ mode }) => { outDir: 'dist-web-component', } : { outDir: '../../backend/FwLite/FwLiteShared/wwwroot/viewer', + manifest: true, }), minify: false, sourcemap: true, chunkSizeWarningLimit: 1000, rollupOptions: { + input: ['src/main.ts', 'src/app.postcss'], output: { entryFileNames: '[name].js', chunkFileNames: '[name].js', @@ -46,18 +49,9 @@ export default defineConfig(({ mode }) => { handler(warning); }, }), svelteTesting()], - ...(!webComponent ? { - server: { - open: 'http://localhost:5173/testing/project-view', - proxy: { - '/api': { - target: 'http://localhost:5137', - secure: false, - ws: true - } - } - } - } : {}), + server: { + origin: 'http://localhost:5173', + }, test: { environment: 'happy-dom', setupFiles: ['./vitest-setup.js'], From 77c03498d8a38efcfb775fd6fe49414b99a55f86 Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Wed, 8 Jan 2025 08:52:28 +0700 Subject: [PATCH 5/6] fix web component build --- frontend/viewer/vite.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/viewer/vite.config.ts b/frontend/viewer/vite.config.ts index 591375b77..94a797f8a 100644 --- a/frontend/viewer/vite.config.ts +++ b/frontend/viewer/vite.config.ts @@ -23,7 +23,7 @@ export default defineConfig(({ mode, command }) => { sourcemap: true, chunkSizeWarningLimit: 1000, rollupOptions: { - input: ['src/main.ts', 'src/app.postcss'], + input: webComponent ? undefined : ['src/main.ts'], output: { entryFileNames: '[name].js', chunkFileNames: '[name].js', From b9471bfc6a87824d45d577f05b5bf1995507925f Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Wed, 8 Jan 2025 08:58:20 +0700 Subject: [PATCH 6/6] use property that will always be defined in my append header hack --- frontend/viewer/src/lib/append-head-hack.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/viewer/src/lib/append-head-hack.ts b/frontend/viewer/src/lib/append-head-hack.ts index 2ebed2346..32658cf71 100644 --- a/frontend/viewer/src/lib/append-head-hack.ts +++ b/frontend/viewer/src/lib/append-head-hack.ts @@ -1,10 +1,11 @@ if (import.meta.env.DEV) { //when in dev mode, svelte and vite want to put style sheets in the head //however blazor will remove them, so we need to put them in the body instead + // eslint-disable-next-line @typescript-eslint/unbound-method const headerAppend = document.head.appendChild; document.head.appendChild = function newAppend(node: T) { //this is used for both svelte and vite imports - if (node.tagName === 'STYLE') { + if (node.nodeName === 'STYLE') { document.getElementById('svelte-app')?.appendChild(node); } else { headerAppend.call(document.head, node);