davidjbradshaw / iframe-resizer

Keep iFrames sized to their content.
https://iframe-resizer.com
Other
6.65k stars 981 forks source link

Module version is not treeshakable and has side-effects on import #1155

Closed maranomynet closed 3 months ago

maranomynet commented 1 year ago

if you import either contentWindow or iframeResize from the 'iframe-resizer' module, for example like so

import { iframeResize } from 'iframe-resizer';

…you'd expect the reuslting browser bundle to only contain the iframeResize function.

What happens, however, is that both the host script and the contentWindow script get bundled (and executed!).

This result is completely un-obvious, until you open and carefully inspect the output bundle (which most people never do), or if you inspect the window of the webpage running the bundled code and notice that it has both the client and host scripts running at the same time.

This happens beacuse both modules (src/iframeResizer.js, and src/iframeResizer.contentWindow.js) contain an IIFE with immediate side-effects (creating global variables, adding window event hanlers, etc.) and thus the code can't be safely excluded from the output bundle.

...

There exist a few ways to resolve this.

I guess the simplest would be to stop exporting both modules from index.js, and instead tell people they must import from 'iframe-resizer/js/iframeResizer.js' and from 'iframe-resizer/js/iframeResizer.contentWindow.js'.

An even better solution would be to refactor the source files to export an init function, and then create separate output files for browsers and node.js import usage.

The browser files would then immediately invoke their respective init() function, whereas the node.js module would simply export the init functions and have developers decide when and if to run them.

maranomynet commented 1 year ago

BTW, my current workaround is to import the host function like this

import iframeResizer from 'iframe-resizer/js/iframeResizer.js';

which solves the problem, I guess, but only as long as the script path doesn't change.

A formal documentation of this would be nice.

SamMousa commented 1 year ago

I ran into the same issue in my svelte application (which needs to be the client / contentWindow). Solved it like this:


export default async () => {
    console.info("Starting async import");
    const orgPostMessage = window.parent.postMessage;

    let loaded = false;
    window.parent.postMessage = () =>  loaded = true;
    window.dispatchEvent(new Event('readystatechange'));

    if (loaded) {
        console.error("Already loaded, not loading it dynamically");
    } else {
        console.log("Not loaded yet, doing a dynamic import now");
        const contentWindow = await import('iframe-resizer/js/iframeResizer.contentWindow.js');
        window.dispatchEvent(new Event('readystatechange'));
        if (!loaded) {
            console.log("Failed to load iframeResizer contentWindow");
        } else {
            console.log("Successfully loaded iframeResizer contentWindow");
        }

    }

    window.parent.postMessage = orgPostMessage;

}

This allows me to use it in a more regular way:

<script lang="ts">
    import { onMount } from "svelte";
    import iframeResizer from '$lib/helpers/iframeResizer';
    onMount(async () => {
            setTimeout(async () => {
                const test = await iframeResizerClient();
            }, 5000);

        });

The delay is mostly to allow me to clearly follow the events in the browser.

Of course it is still a side effect and will break things like HMR.

alexcumplido commented 1 year ago

Hi @SamMousa trying to implement your approach but I have a few questions.

-Do you still placing a script in the external page containing the frame as specified in the docs ?

-How are you using the <iframe/> element on the svelte component ?

SamMousa commented 1 year ago

Yes, external page is a classic php monolith that renders html in which we include the script.

The svelte app is deployed separately so it has a URL

alexcumplido commented 1 year ago

Understood, thank you for clarifying it.

davidjbradshaw commented 4 months ago

For version 5 I am breaking the library up into separate modules. If your interested the draft docs are now online at https://iframe-resizer.com and their is an early alpha release on NPM. Would be interested in any feedback you have.

davidjbradshaw commented 3 months ago

Fixed in V5.0.0

Please see https://iframe-resizer.com