sveltesappersveltekit

Call component from one SvelteKit App to another SvelteKit App


Note: I have migrated my Sapper app to SvelteKit(Update 3 below), so looking for solution for SvelteKit now. I have multiple MFEs(Micro-Frontends) built using Sapper and they are under different servers. There is a svelte component(that renders HTML content) in one MFE that I want to call/render in another MFE. How can I do that? I have tried running/serving both MFEs in my local at the same time and did a fetch(route-that-loads-component), but it returns a POJO in the response and I have no idea what to do with it:

Response {
  size: 0,
  timeout: 0,
  [Symbol(Body internals)]: {
    body: Gunzip {
      _writeState: [Uint32Array],
      _readableState: [ReadableState],
      _events: [Object: null prototype],
      _eventsCount: 5,
      _maxListeners: undefined,
      _writableState: [WritableState],
      allowHalfOpen: true,
      bytesWritten: 0,
      _handle: [Zlib],
      _outBuffer: <Buffer 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 f7 7f 00 00 41 ff e2 55 48 89 e5 56 57 48 81 ec 10 01 00 00 48 89 75 e8 49 3b 65 e0 0f 86 fb 11 00 00 ... 16334 more bytes>,
      _outOffset: 0,
      _chunkSize: 16384,
      _defaultFlushFlag: 2,
      _finishFlushFlag: 2,
      _defaultFullFlushFlag: 3,
      _info: undefined,
      _maxOutputLength: 4294967295,
      _level: -1,
      _strategy: 0,
      [Symbol(kCapture)]: false,
      [Symbol(kTransformState)]: [Object],
      [Symbol(kError)]: null
    },
    disturbed: false,
    error: null
  },
  [Symbol(Response internals)]: {
    url: 'http://localhost:3000/mfe/content-mfe/content/testrouteasset1',
    status: 200,
    statusText: 'OK',
    headers: Headers { [Symbol(map)]: [Object: null prototype] },
    counter: 0
  }
}

UPDATE 1:

I was able to get the response from MFE by doing response.text(), and this is what it looks like:

<!doctype html>
<html lang="en">
<head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width,initial-scale=1.0">
        <meta name="theme-color" content="#333333">

        <base href="/mfe/content-mfe/">

        <link rel="stylesheet" href="global.css">
        <link rel="manifest" href="manifest.json" crossorigin="use-credentials">
        <link rel="icon" type="image/png" href="favicon.png">

        <!-- Sapper creates a <script> tag containing `src/client.js`
             and anything else it needs to hydrate the app and
             initialise the router -->
        <script>__SAPPER__={baseUrl:"/mfe/content-mfe",preloaded:[void 0,null,{}]};if('serviceWorker' in navigator)navigator.serviceWorker.register('/mfe/content-mfe/service-worker.js');var s=document.createElement("script");try{new Function("if(0)import('')")();s.src="/mfe/content-mfe/client/client.1dc273d9.js";s.type="module";s.crossOrigin="use-credentials";}catch(e){s.src="/mfe/content-mfe/client/shimport@2.0.4.js";s.setAttribute("data-main","/mfe/content-mfe/client/client.1dc273d9.js")}document.head.appendChild(s)</script>   

        <!-- Sapper generates a <style> tag containing critical CSS
             for the current page. CSS for the rest of the app is
             lazily loaded when it precaches secondary pages -->
        <link rel="stylesheet" href="client/client-a7fe6d9e.css"><link rel="stylesheet" href="client/FieldErrorMessage-bce587e1.css">

        <!-- This contains the contents of the <svelte:head> component, if
             the current page has one -->

</head>
<body>
        <!-- The application will be rendered inside this element,
             because `src/client.js` references it -->
        <div id="mfe-content">


<main><slot></slot></main></div>
</body>
</html>

However, Sapper is replacing baseURL of the MFE that is sending response to the calling MFE's baseURL, making it not-hydratable since client.js and client.css not reachable(404s) enter image description here

Even if I do end up resolving those base path URLs issue, will it still work for 2 Sapper apps to be initiated on one page?

UPDATE 2:

I somehow managed to add static baseURL for the MFE from where the content is to be requested, now I get hydration issue because Sapper can't hydrate 2 Sapper Apps in one page.

UPDATE 3: I have migrated my Sapper apps to SvelteKit. So looking for suggestions on how to achieve this using SvelteKit now!

Any other solution to call/import a component from one MFE to another MFE or would be really appreciated!


Solution

  • After migrating to SvelteKit(Update 3 in the question), I was able to solve this issue by creating an endpoint (.js file inside route folder), importing required component and exporting it as shown below:

    export async function get({ query, page }) {
        const ASSET = (await import("../../assets/myasset.svelte")).default;
    
        if (ASSET) {
            let renderedAsset = Asset.render();
            return {
                body: { asset: renderedAsset }
            };
        } else {
            return {
                status: data.status,
                error: new Error(`Could not load ${response}`)
            }
        }
    }
    This returns an object that contains HTML and CSS code:

    {
        "asset": {
            "html": "<h1 class=\"s-ZmOKpGJa32Cj\">Hello!    \n</h1>",
            "css": {
                "code": "h1.s-ZmOKpGJa32Cj{color:blue}.s-ZmOKpGJa32Cj{}",
                "map": null
            },
            "head": ""
        }
    }
    

    Which at the end can be used to render to DOM like this:

    <script context="module">
        export async function load({ fetch }) {
            let status, Final;
            const Asset = await fetch('path-to-the-endpoint');
            if (Asset && Asset.status === 404) {
                return (status = 'Asset not found');
            }
            if (Asset && Asset.ok) {
                Final = await Asset.json();
                return {
                    props: {
                        Final: Final,
                        status: 'Asset is OK',
                    },
                };
            } else {
                return {
                    props: {
                        status: 'Okay, no prop.',
                    },
                };
            }
        }
    </script>
    
    <script>
        export let Final, status;
    </script>
    {status}
    
    {@html `<${''}style>${Final.asset.css.code }</${''}style>` }
    
    {@html Final.asset.html}
    This works perfectly fine in SvelteKit. However, I have not tested if this will work in Sapper or not(Since I've moved away from Sapper), but hopefully it should work!