Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Svelte errors from jsdom attempting to run code after tossr promise returns #14

Open
mizzao opened this issue Mar 2, 2021 · 9 comments · May be fixed by #16
Open

Svelte errors from jsdom attempting to run code after tossr promise returns #14

mizzao opened this issue Mar 2, 2021 · 9 comments · May be fixed by #16

Comments

@mizzao
Copy link
Contributor

mizzao commented Mar 2, 2021

Now that tossr is printing out errors properly (#11), it can still be hard to debug because they look like this:

Unhandled promise rejection:
TypeError: Cannot read property 'dispatchEvent' of undefined
    at Object.dispatch_dev10 (eval at <anonymous> (/home/mao/parsnip/cookwithme/node_modules/tossr/tossr.js:91:24), <anonymous>:10399:16)
    at create_if_block_1$12 (eval at <anonymous> (/home/mao/parsnip/cookwithme/node_modules/tossr/tossr.js:91:24), <anonymous>:5010:14)
    at create_fragment$22 (eval at <anonymous> (/home/mao/parsnip/cookwithme/node_modules/tossr/tossr.js:91:24), <anonymous>:5075:38)
    at Object.init10 (eval at <anonymous> (/home/mao/parsnip/cookwithme/node_modules/tossr/tossr.js:91:24), <anonymous>:10360:41)
    at new PhaseCard (eval at <anonymous> (/home/mao/parsnip/cookwithme/node_modules/tossr/tossr.js:91:24), <anonymous>:5225:16)
    at create_fragment$32 (eval at <anonymous> (/home/mao/parsnip/cookwithme/node_modules/tossr/tossr.js:91:24), <anonymous>:5301:19)
    at Object.init10 (eval at <anonymous> (/home/mao/parsnip/cookwithme/node_modules/tossr/tossr.js:91:24), <anonymous>:10360:41)
    at new U5BrecipeIdu5D (eval at <anonymous> (/home/mao/parsnip/cookwithme/node_modules/tossr/tossr.js:91:24), <anonymous>:5435:16)
    at Array.create_default_slot2 (eval at <anonymous> (/home/mao/parsnip/cookwithme/node_modules/tossr/tossr.js:91:24), <anonymous>:11562:27)
    at create_slot3 (eval at <anonymous> (/home/mao/parsnip/cookwithme/node_modules/tossr/tossr.js:91:24), <anonymous>:9618:29)

Is this because jsdom is running everything in an eval? If so, is there any way to bubble up the underlying code? Because it's really hard to see what line this error is referring to 😁. There is no dispatchEvent in my code so I think it might even be from a Svelte timing issue.

@jakobrosenberg
Copy link
Member

@mizzao I'm afraid your guess is as good as mine - probably even better 😳.

Did you get a reply in the jsdom repo? We can also try asking around in the React community. IIRC there are React frameworks using jsdom for SSR .

@mizzao
Copy link
Contributor Author

mizzao commented Mar 3, 2021

Good call, I will do some digging on how others deal with this and get back to you.

@mizzao
Copy link
Contributor Author

mizzao commented Mar 6, 2021

Possibly related: sveltejs/svelte#3327, though I can't figure out for the life of me what is possibly trying to dispatchEvent. Does Routify use dispatchEvent?

No leads on debugging jsdom's eval so far.

@mizzao
Copy link
Contributor Author

mizzao commented Mar 6, 2021

Interesting data point: after bumping Routify to 2.13.1 and tossr to 1.4.2 (implying the correspond bump in esbuild), the error changed:

[tossr] Unhandled promise rejection:
[tossr] TypeError: Cannot read property 'dispatchEvent' of undefined
    at dispatch_dev (eval at <anonymous> (/home/mao/parsnip/cookwithme/node_modules/tossr/tossr.js:96:24), <anonymous>:10388:14)
    at create_if_block_1$12 (eval at <anonymous> (/home/mao/parsnip/cookwithme/node_modules/tossr/tossr.js:96:24), <anonymous>:5071:7)
    at create_fragment$23 (eval at <anonymous> (/home/mao/parsnip/cookwithme/node_modules/tossr/tossr.js:96:24), <anonymous>:5136:38)
    at init (eval at <anonymous> (/home/mao/parsnip/cookwithme/node_modules/tossr/tossr.js:96:24), <anonymous>:10349:39)
    at new PhaseCard (eval at <anonymous> (/home/mao/parsnip/cookwithme/node_modules/tossr/tossr.js:96:24), <anonymous>:5286:9)
    at create_fragment$33 (eval at <anonymous> (/home/mao/parsnip/cookwithme/node_modules/tossr/tossr.js:96:24), <anonymous>:5362:19)
    at init (eval at <anonymous> (/home/mao/parsnip/cookwithme/node_modules/tossr/tossr.js:96:24), <anonymous>:10349:39)
    at new U5BrecipeIdu5D (eval at <anonymous> (/home/mao/parsnip/cookwithme/node_modules/tossr/tossr.js:96:24), <anonymous>:5496:9)
    at Array.create_default_slot2 (eval at <anonymous> (/home/mao/parsnip/cookwithme/node_modules/tossr/tossr.js:96:24), <anonymous>:11552:25)
    at create_slot (eval at <anonymous> (/home/mao/parsnip/cookwithme/node_modules/tossr/tossr.js:96:24), <anonymous>:9603:27)

The jsdom version didn't change, so what is generating this code that causes it to print out differently?

@mizzao
Copy link
Contributor Author

mizzao commented Mar 6, 2021

Okay, this seems to come from the https://github.com/sveltejs/svelte code. On the latest version, 3.35.0, Here are occurrences of dispatch_dev:

src/compiler/compile/render_dom/Block.ts:                                       @dispatch_dev("SvelteRegisterBlock", {
src/compiler/compile/render_dom/index.ts:                                       ${options.dev && b`@dispatch_dev("SvelteRegisterComponent", { component: this, tagName: "${name.name}", options, id: create_fragment.name });`}
src/runtime/internal/dev.ts:export function dispatch_dev<T=any>(type: string, detail?: T) {
src/runtime/internal/dev.ts:    dispatch_dev('SvelteDOMInsert', { target, node });
src/runtime/internal/dev.ts:    dispatch_dev('SvelteDOMInsert', { target, node, anchor });
src/runtime/internal/dev.ts:    dispatch_dev('SvelteDOMRemove', { node });
src/runtime/internal/dev.ts:    dispatch_dev('SvelteDOMAddEventListener', { node, event, handler, modifiers });
src/runtime/internal/dev.ts:            dispatch_dev('SvelteDOMRemoveEventListener', { node, event, handler, modifiers });
src/runtime/internal/dev.ts:    if (value == null) dispatch_dev('SvelteDOMRemoveAttribute', { node, attribute });
src/runtime/internal/dev.ts:    else dispatch_dev('SvelteDOMSetAttribute', { node, attribute, value });
src/runtime/internal/dev.ts:    dispatch_dev('SvelteDOMSetProperty', { node, property, value });
src/runtime/internal/dev.ts:    dispatch_dev('SvelteDOMSetDataset', { node, property, value });
src/runtime/internal/dev.ts:    dispatch_dev('SvelteDOMSetData', { node: text, data });

Do you find it strange that tossr is calling a function in compile/render_dom? Shouldn't it be using compile/render_ssr instead? https://github.com/sveltejs/svelte/tree/master/src/compiler/compile/render_ssr

@mizzao
Copy link
Contributor Author

mizzao commented Mar 6, 2021

This function seems to be the problem: https://github.com/sveltejs/svelte/blob/master/src/runtime/internal/dev.ts#L4

export function dispatch_dev<T=any>(type: string, detail?: T) {
	document.dispatchEvent(custom_event(type, { version: '__VERSION__', ...detail }));
}

So, document is undefined. Seems like it must be a jsdom issue, no?

@mizzao
Copy link
Contributor Author

mizzao commented Mar 6, 2021

Okay, this is weird. If I put console.log(document); in the <script> section of my Svelte components and run SSR, in some components it shows:

Document {
  location: [Getter/Setter],
  [Symbol(DOM SymbolTree)]: SymbolTreeNode {
    parent: null,
    previousSibling: null,
    nextSibling: null,
    firstChild: null,
    lastChild: null,
    childrenVersion: 0,
    childIndexCachedUpTo: null,
    cachedIndex: -1,
    cachedIndexVersion: NaN
  }
}

here dispatchEvent is on the prototype, but in other components document is undefined. Now I'm really confused...perhaps you have some ideas?

@mizzao mizzao changed the title Need better way to debug tossr errors Svelte errors from jsdom attempting to run code after tossr promise returns Mar 6, 2021
@mizzao
Copy link
Contributor Author

mizzao commented Mar 6, 2021

Ok, this is interesting...check out the where the undefined is getting printed:

[tossr] /recipe/65bf695fc2bd4106b1109ebcca4ee98f - 1435ms (rebuilt bundle)
undefined
[tossr] Error on url: /recipe/65bf695fc2bd4106b1109ebcca4ee98f
[tossr] Unhandled promise rejection:
[tossr] TypeError: Cannot read property 'dispatchEvent' of undefined
    at dispatch_dev (eval at <anonymous> (/home/mao/parsnip/tossr/tossr.js:96:24), <anonymous>:10393:14)
    at create_if_block_1$12 (eval at <anonymous> (/home/mao/parsnip/tossr/tossr.js:96:24), <anonymous>:5072:7)
    at create_fragment$23 (eval at <anonymous> (/home/mao/parsnip/tossr/tossr.js:96:24), <anonymous>:5137:38)
    at init (eval at <anonymous> (/home/mao/parsnip/tossr/tossr.js:96:24), <anonymous>:10354:39)
    at new PhaseCard (eval at <anonymous> (/home/mao/parsnip/tossr/tossr.js:96:24), <anonymous>:5288:9)
    at create_fragment$33 (eval at <anonymous> (/home/mao/parsnip/tossr/tossr.js:96:24), <anonymous>:5364:19)
    at init (eval at <anonymous> (/home/mao/parsnip/tossr/tossr.js:96:24), <anonymous>:10354:39)
    at new U5BrecipeIdu5D (eval at <anonymous> (/home/mao/parsnip/tossr/tossr.js:96:24), <anonymous>:5498:9)
    at Array.create_default_slot2 (eval at <anonymous> (/home/mao/parsnip/tossr/tossr.js:96:24), <anonymous>:11557:25)
    at create_slot (eval at <anonymous> (/home/mao/parsnip/tossr/tossr.js:96:24), <anonymous>:9605:27)

So, after the resolveHtml() function is all done, there is still DOM activity happening on the page, and that is why this error is thrown. Can't we just tell JSDOM to stop doing stuff instead of continuing to run in the background? Apparently dom.window.close() isn't cutting it.

@jakobrosenberg
Copy link
Member

Sorry for leaving you hanging @mizzao. Not sure how I missed all the comments, but you're way ahead of me 😅

Do you find it strange that tossr is calling a function in compile/render_dom? Shouldn't it be using compile/render_ssr instead?

If we use render_ssr, we won't need jsdom, but we will lose out on the simplicity and flexibility of being able to run async code near seamlessly. For render_ssr, all async code will have to be called inside a preload function. The preload function will have to be moved into a separate script block and all resolved values will have to be passed back to the original script tag.

render_ssr provides slightly faster rendering than jsdom, but with proper server side caching, there's no advantage in native SSR IMO.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants