kiln / flourish-sdk

The SDK for developing Flourish templates
Other
81 stars 16 forks source link

Code splitting? #67

Closed aendra-rininsland closed 3 years ago

aendra-rininsland commented 3 years ago

I'm currently trying to do something like this:

const App = ({ state, chartData }) => {
  const [ExampleChart, setExampleChart] = useState(null);
  useEffect(() => {
    setExampleChart(
      lazy(() =>
        import(`../../charts/${state.chart}`).catch(() => import("./fallback"))
      )
    );
  }, [state.chart]);
  return (
    <main>
      {ExampleChart && (
        <Suspense fallback="loading example...">
          <ExampleChart />
        </Suspense>
      )}
    </main>
  );
};

./src/charts contains a bunch of chart examples and their respective data. I don't want to rollup all 40+ of those with the main app, so being able to dynamically import modules would be very helpful. Alas, when I try to do this, Rollup reports:

[!] Error: UMD and IIFE output formats are not supported for code-splitting builds.

I then tried to split the lazy-loading/React-y bits into its own ESM rollup bundle, then made the template.update() method call the following from index.html:

    <script type="module">
      import { load } from "/app/index.js";

      window.load_app = () => {
        const rootElement = document.querySelector(".container");
        if (!state.chart) {
          if (rootElement.children.length) {
            [...rootElement.children].forEach((d) => d.remove());
          }
        }
        load(rootElement);
      };
    </script>

However, I can't see to find any easy way of exposing the generated app/*.js files to the Flourish SDK server, it 404s regardless of whether the output directory is in the repo root or in static/ .

Any ideas how to do code splitting given the requirement by the Flourish SDK to expose a global named template as its main entrypoint, something seemingly incompatible with Rollup's code splitting mechanism?

Thanks!

robinhouston commented 3 years ago

This is a great question, @aendrew! Is there any way you can share a complete project so I can play around with it?

robinhouston commented 3 years ago

However, I can't see to find any easy way of exposing the generated app/*.js files to the Flourish SDK server, it 404s regardless of whether the output directory is in the repo root or in static/ .

Does this work if you put the files in the static directory, and then when you load them you prefix the filename with Flourish.static_prefix. I would have thought that would do it, though I haven’t tried.

aendra-rininsland commented 3 years ago

Hi! Will post a full example in a bit, having made some progress in the meantime:

In index.html I have:

    <script type="module">
      (async () => {
        const { load } = await import(
          "./" + Flourish.static_prefix + "/app/index.js"
        );

        window.load_app = () => {
          const rootElement = document.querySelector(".container");
          if (!window.template.state.chart) {
            if (rootElement.children.length) {
              [...rootElement.children].forEach((d) => d.remove());
            }
          }

          load(rootElement);
        };
      })();
    </script>

This works to some degree. It's now loading the app entry point (static/app/index.js) in the template. 🎉

However, it's now dying on this line in static/app/app.js (my main React app component):

import(`../../charts/${state.chart}`).catch(() => import("./fallback"))

Notably, it's still trying to load the relative path instead of pull in the correct codesplit bundle in static/app.

I'm getting the impression that Rollup just isn't very good at code splitting and this might be a bit of a fool's errand, I wonder if Webpack would work better...? 😅

robinhouston commented 3 years ago

In case it's useful, I've just learnt that @hughsk made a code-splitting example which is at https://github.com/kiln/template-example-code-splitting

hughsk commented 3 years ago

Hi! Yeah, code splitting is a bit tricky with Flourish at the moment, it took me a few attempts with different bundlers to get it working. I remember running into issues relying on dynamic paths with Webpack too. The rollup example hasn't been tested with different templates yet, but it's definitely a good starting point and I'd be interested to hear if it works for you @aendrew!

aendra-rininsland commented 3 years ago

Oh wicked, thanks both! I'll try that today. Was thinking SystemJS might be one solution, good to have a proper implementation!

aendra-rininsland commented 3 years ago

Hi again! @robinhouston, that example you gave with code-splitting was perfect, thank you very much!!! 🙌

One additional thing I had to do because the import paths were being dynamically generated at runtime was install @rollup/plugin-dynamic-import-vars and place it just before the terser plugin in the Rollup plugins array. Works brilliantly AFAICT now though. Thanks again!