stealjs / steal

Gets JavaScript
https://stealjs.com
MIT License
1.36k stars 522 forks source link

strategy:incremental in dev with 404ing files treats 404 HTML as JS #1478

Closed leoj3n closed 5 years ago

leoj3n commented 5 years ago

strategy: 'seo'

The "don't panic" message (expected):

image

tasks.js:251 GET http://localhost:3030/node_modules/@feathersjs/feathers/lib/hooks.js 404 (Not Found)
dev.js:131 steal.js WARNING: Some 404s were encountered while loading. Don't panic! These will only happen in dev and are harmless.

strategy: 'incremental'

The "failed promise" error (unexpected):

image

can-log.js:98 Failed promise: Error: http://localhost:3030/node_modules/@feathersjs/feathers/lib/hooks.js: Unexpected token (1:1)
> 1 | <!doctype html>
    |  ^
  2 | <html data-can-automount="false" data-incrementally-rendered><head><script>window.closeSsrIframe=function(){var d=document;var f=d.getElementById("donessr-iframe");f.parentNode.removeChild(f);d.body.style.visibility = ''}</script><!--autorender-keep preload placeholder--><iframe id="donessr-iframe" data-keep srcdoc="<html data-can-automount=&quot;false&quot; data-streamurl=&quot;/_donessr_instructions/1543013045417&quot;><head><script type=&quot;module&quot;>const Instructions={Zero:0,Insert:1,Remove:2,Move:3,Text:4,SetAttr:5,RemoveAttr:6,Prop:7};var doneMutation_2_0_0_tags=Instructions,decodeNode_1=decodeNode,decodeString_1=decodeString,decodeType_1=decodeType,toUint16_1=toUint16;function toUint16(a){let b=a.next().value,c=a.next().value;return(255&amp;b)<<8|255&amp;c}function decodeString(a){for(let b=&quot;&quot;;;){let{value:c}=a.next();switch(c){case doneMutation_2_0_0_tags.Zero:return b;default:b+=String.fromCharCode(c);}}}function decodeType(a){let b=a.next().value;switch(b){case 1:return!!a.next().value;case 2:return+a.next().value;case 3:return decodeString(a);default:throw new Error(`The type ${b} is not recognized.`);}}function decodeNode(a,b,c){switch(b){case 3:return c.createTextNode(decodeString(a));case 1:return decodeElement(a,c);default:throw new Error(`Unable to decode nodeType ${b}`);}}function decodeElement(a,b){let c=b.createElement(decodeString(a)),d=decodeString(a);for(;d;){let b=decodeString(a);c.setAttribute(d,b),d=decodeString(a)}let e=c,f=a.next().value;for(;f!==doneMutation_2_0_0_tags.Zero;){let c=decodeNode(a,f,b);e.appendChild(c),f=a.next().value}return c}var doneMutation_2_0_0_decode={decodeNode:decodeNode_1,decodeString:decodeString_1,decodeType:decodeType_1,toUint16:toUint16_1};const{decodeNode:decodeNode$1,decodeString:decodeString$1,decodeType:decodeType$1,toUint16:toUint16$1}=doneMutation_2_0_0_decode;function sepNode(a){return 8===a.nodeType&amp;&amp;&quot;__DONEJS-SEP__&quot;===a.nodeValue}function*walk(a,b){const c=getDocument(a),d=c.createTreeWalker(a,-1);let e=0,f=d.nextNode();for(;;)if(e===b)b=yield f;else if(!(e<b))e--,f=d.previousNode();else if(e++,f=d.nextNode(),sepNode(f)){var g=f;f=d.nextNode(),g.parentNode.removeChild(g)}}function getDocument(a){return 9===a.nodeType?a:a.ownerDocument}class MutationPatcher{constructor(a){this.root=a,this._startWalker()}_startWalker(){this.walker=walk(this.root,0),this.walker.next()}patch(a){const b=a[Symbol.iterator](),c=this.root,d=getDocument(c);for(let c of b){let a,e,f,g;switch(c){case doneMutation_2_0_0_tags.Zero:break;case doneMutation_2_0_0_tags.Insert:a=toUint16$1(b),e=toUint16$1(b);let h=b.next().value;g=decodeNode$1(b,h,d);let i=this.walker.next(a).value,j=getChild(i,e);i.insertBefore(g,j);break;case doneMutation_2_0_0_tags.Remove:a=toUint16$1(b);let k=toUint16$1(b),l=this.walker.next(a).value;g=getChild(l,k),l.removeChild(g),this._startWalker();break;case doneMutation_2_0_0_tags.Text:a=toUint16$1(b);let m=decodeString$1(b);f=this.walker.next(a).value,f.nodeValue=m;break;case doneMutation_2_0_0_tags.SetAttr:a=toUint16$1(b),f=this.walker.next(a).value;let n=decodeString$1(b),o=decodeString$1(b);f.setAttribute(n,o);break;case doneMutation_2_0_0_tags.RemoveAttr:a=toUint16$1(b),f=this.walker.next(a).value,f.removeAttribute(decodeString$1(b));break;case doneMutation_2_0_0_tags.Prop:a=toUint16$1(b),f=this.walker.next(a).value,f[decodeString$1(b)]=decodeType$1(b);break;default:throw new Error(`The instruction ${c} is not supported.`);}}}}function getChild(a,b){let c=0,d=a.firstChild;for(;c<b;)if(c++,d=d.nextSibling,d&amp;&amp;sepNode(d)){var e=d;d=d.nextSibling,e.parentNode.removeChild(e)}return d}var doneMutation_2_0_0_patch=MutationPatcher,isAttached=function(){return document.documentElement.hasAttribute(&quot;data-attached&quot;)},common={isAttached:isAttached};const isAttached$1=common.isAttached;async function read(a,b){let{done:c,value:d}=await a.read();return!(c||isAttached$1())&amp;&amp;(b.patch(d),!0)}async function incrementallyRender({fetch:a,url:b,onStart:c}){let d=await a(b,{crendentials:&quot;same-origin&quot;}),e=d.body.getReader(),f=new doneMutation_2_0_0_patch(document);for(await read(e,f),c();await read(e,f););}var render=incrementallyRender;const isAttached$2=common.isAttached;function depth(a){let b=0,c=document.createTreeWalker(a,4294967295,{acceptNode:function(a){let b=a.nodeType;return 1===b||3===b}});for(;c.nextNode();)b++;return b}function reattach(a,b){let c=new MutationObserver(function(){let d=depth(document.documentElement),e=depth(a.documentElement),f=isAttached$2();f||(f=d<=e),f&amp;&amp;(c.disconnect(),!isAttached$2()&amp;&amp;(document.documentElement.setAttribute(&quot;data-attached&quot;,&quot;&quot;),b()))});c.observe(a,{childList:!0,subtree:!0})}var reattach_1=reattach;const url=document.documentElement.dataset.streamurl;function removeSelf(){let a=window.parent;a.closeSsrIframe&amp;&amp;a.closeSsrIframe()}render({fetch,url,onStart:()=>{reattach_1(window.parent.document,removeSelf)}});var irReattach={};export default irReattach;</script><link rel=&quot;preload&quot; href=&quot;/_donessr_instructions/1543013045417&quot;><!--iframe placeholder-->
  3 |     <title>Bitcentivez</title>
  4 |     <meta name=&quot;Description&quot; content=&quot;A StealJS and CanJS demo application.&quot;>
    at (node_modules/@feathersjs/feathers/lib/hooks.js:1)

How can we stop it trying to parse the 404 HTML as JavaScript?

My app is very similar to bitballs that uses incremental without such errors:

EDIT:

I figured what the heck and tried deploying this same code to Heroku and magically it works in production without this 404 business. Any idea why that is? Development still 404ing on HTML.

matthewp commented 5 years ago

I'm going to move this to done-serve as the problem is likely there. I can't imagine why this would be different based on the strategy though.

matthewp commented 5 years ago

Hm, looks like I can't move the issue to another org. Would you might reposting this in done-serve?

leoj3n commented 5 years ago

Maybe don't move it though because I don't use done-serve. I use done-ssr-middleware with feathers@3 express if that makes sense. I can share that code if necessary. Does done-serve wire something up special to make incremental work? Maybe I need to emulate that or use done-serve?

EDIT:

I can confirm the site that worked after being pushed to production is falling back to http/1.1

I'm new to h2 and haven't done anything with keys or certs so I assume I need to change something about how the server part of my app works.

leoj3n commented 5 years ago

I'll try and replicate the relevant parts of done-serve and open an issue there if I can't work it out.

matthewp commented 5 years ago

So you should be configuring your express server not to let node_modules/ requests hit the SSR middleware. I'm not an express expert but I believe you can do app.use('/node_modules', static('path/to/node_modules')) and it shouldn't hit the other middleware. done-serve should be doing something similar (but likely isn't).

leoj3n commented 5 years ago

I was able to correct the /node_modules 404 status with res.sendStatus(404) after express.static:

  .use(express.static(app.get('client')))
  .use('/node_modules', (req, res) => {
    res.sendStatus(404);
  })
  .configure(middleware);

Although, it's not entirely clear to me why that is needed when using incremental but not for seo.