GithubHelp home page GithubHelp logo

react-snapshot's Introduction

๐Ÿ“ธ React Snapshot

โš ๏ธโš ๏ธโš ๏ธ DEPRECATED: USE https://github.com/stereobooster/react-snap INSTEAD โš ๏ธโš ๏ธ

A zero-configuration static pre-renderer for React apps. Starting by targeting Create React App (because it's great)

The Premise

Server-side rendering is a big feature of React, but for most apps it can be more trouble than its worth. Personally, I think the sweet spot is taking static site snapshots of all your publicly-accessible pages & leaving anything requiring authentication as a normal, JS-driven Single Page App.

This is a project to do that. Automatically, without any configuration, just smart defaults. Retrospective progressive enhancement.

The snapshots still have the normal JS bundle included, so once that downloads the site will function exactly as before (i.e. instantaneous page transitions), but you serve real, functional HTML & CSS as soon as possible. It's good for SEO (yes Google crawls SPAs now but they still reward perf and this perfs like a banshee), it's good if your JS is broken or something render-blocking has a network fail, it's good for accessibility, it's good for Slackbot or Facebook to read your opengraph tags, it's just good.

The How To

  • First, npm i -D react-snapshot
  • Second, open your package.json and change "scripts" from
- "build": "react-scripts build"
+ "build": "react-scripts build && react-snapshot"
  • Third, change your usage of react-dom:
- import ReactDOM from 'react-dom';
+ import { render } from 'react-snapshot';

- ReactDOM.render(
+ render(
    <App/>,
    document.getElementById('root')
  );

This calls ReactDOM.render in development and ReactDOMServer.renderToString when prerendering. If I can make this invisible I will but I can't think how at the moment.

Options

You can specify additional paths as entry points for crawling that would otherwise not be found. It's also possible to exclude particular paths from crawling. Simply add a section called "reactSnapshot" to your package.json.

  "reactSnapshot": {
    "include": [
      "/other-path",
      "/another/nested-path"
    ],
    "exclude": [
      "/signup",
      "/other-path/exclude-me/**"
    ],
    "snapshotDelay": 300
  }

Note that exclude can be passed a glob, but include cannot.

The default snapshot delay is 50ms, and this can be changed to suit your app's requirements.

The Demo

Check out create-react-app-snapshot.surge.sh for a live version or geelen/create-react-app-snapshot for how it was built, starting from create-react-app's awesome baseline. No ejecting necessary, either.

The diff from the original create-react-app code might be enlightening to you as well.

The Implementation

It's pretty simple in principle:

  • Fire up the home page in a fake browser and snapshot the HTML once the page is rendered
  • Follow every relative URL to crawl the whole site
  • Repeat.

There's a few more steps to it, but not much.

React-snapshot will crawl all links that it finds. You can create "site map" page, which will contain links to all pages.

  • We move build/index.html to build/200.html at the beginning, because it's a nice convention. Hosts like surge.sh understand this, serving 200.html if no snapshot exists for a URL. If you use a different host I'm sure you can make it do the same.
  • pushstate-server is used to serve the build directory & serving 200.html by default
  • The fake browser is JSDOM, set to execute any local scripts (same origin) in order to actually run your React code, but it'll ignore any third-party scripts (analytics or social widgets)
  • We start a new JSDOM session for each URL to ensure that each page gets the absolute minimum HTML to render it.

The Caveats

This is a hacky experiment at the moment. I would really like to see how far we can take this approach so things "just work" without ever adding config. Off the top of my head:

  • Waiting on pushstate-server#29. Right now pushstate-server serves 200.html even if a HTML snapshot is present. So once you've run react-snapshot, you have to switch to http-server or superstatic to test if it worked. Or you could just push to surge.sh each time, which isn't too bad.
  • Is starting at / and crawling sufficient? Might there be unreachable sections of your site?
  • Should we exclude certain URLs? Maybe parse the robots.txt file?
  • What if you don't want the 200.html pushstate fallback? What if you want to remove the bundle (effectively making this a static site generator)?
  • This doesn't pass down any state except what's contained in the markup. That feels ok for simple use-cases (you can always roll your own) but if you have a use-case where you need it and want zero-config raise an issue.
  • #2 I'm using a regexp to parse URLs out of the HTML because I wrote this on a flight with no wifi and couldn't NPM install anything. We should use a real parser. You should submit a PR to use a real parser. That would be real swell.
  • Should we clone the build directory to something like snapshot or dist instead of modifying it in-place?
  • There's virtually no error checking things so will just explode in interesting ways. So yeah that should be fixed.
  • Is JSDOM gonna hold us back at some point?
  • If the React code is changing what it renders based on size of viewport then things may "pop in" once the JS loads. Anything driven by media queries should just work though. So stick to Media Queries, I guess?
  • Does someone else want to take this idea and run with it? I would be 100% happy to not be the maintainer of this project :)

The Alternatives

This should work for simple cases. For less simple cases, go with:

  • Webpack Static Site Generator Plugin
  • Gatsby or Phenomic if you're doing something bigger or more structured. Phenomic has service worker support & minimal bundles and all kinds of things, Gatsby is getting that stuff too.
  • Actually run a server-side React node server because you have more complex stuff to do, like pre-rendering stuff behind a login.

License

MIT

react-snapshot's People

Contributors

aaronshaf avatar alexeyraspopov avatar arve0 avatar bguiz avatar codyray avatar evenchange4 avatar geelen avatar giuseppeg avatar kantenkugel avatar kevin940726 avatar martinandert avatar munter avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-snapshot's Issues

react 16 fragments cause error

I'm getting an error when I use react 16's new fragment syntax https://facebook.github.io/react/blog/2017/09/26/react-v16.0.html#new-render-return-types-fragments-and-strings

Here is the output:

Error: Uncaught [Invariant Violation: Minified React error #105; visit http://facebook.github.io/react/docs/error-decoder.html?invariant=105&args[]=u for the full message or use the non-minified dev environment for full errors and additional helpful warnings.]
    at reportException (/home/eric/Documents/react-flow/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:58:24)
    at processJavaScript (/home/eric/Documents/react-flow/node_modules/jsdom/lib/jsdom/living/nodes/HTMLScriptElement-impl.js:130:7)
    at HTMLScriptElementImpl._eval (/home/eric/Documents/react-flow/node_modules/jsdom/lib/jsdom/living/nodes/HTMLScriptElement-impl.js:65:7)
    at /home/eric/Documents/react-flow/node_modules/jsdom/lib/jsdom/browser/resource-loader.js:31:22
    at Object.check (/home/eric/Documents/react-flow/node_modules/jsdom/lib/jsdom/living/nodes/Document-impl.js:89:11)
    at /home/eric/Documents/react-flow/node_modules/jsdom/lib/jsdom/living/nodes/Document-impl.js:108:12
    at wrappedEnqueued (/home/eric/Documents/react-flow/node_modules/jsdom/lib/jsdom/browser/resource-loader.js:255:16)
    at Request.request [as _callback] (/home/eric/Documents/react-flow/node_modules/jsdom/lib/jsdom/browser/resource-loader.js:203:9)
    at Request.self.callback (/home/eric/Documents/react-flow/node_modules/request/request.js:186:22)
    at emitTwo (events.js:125:13) { Invariant Violation: Minified React error #105; visit http://facebook.github.io/react/docs/error-decoder.html?invariant=105&args[]=u for the full message or use the non-minified dev environment for full errors and additional helpful warnings.
    at r (http://localhost:34491/static/js/main.ff387819.js:1:1230)
    at p.mountComponent (http://localhost:34491/static/js/main.ff387819.js:1:143767)
    at Object.mountComponent (http://localhost:34491/static/js/main.ff387819.js:1:9686)
    at performInitialMount (http://localhost:34491/static/js/main.ff387819.js:1:145370)
    at p.mountComponent (http://localhost:34491/static/js/main.ff387819.js:1:144256)
    at Object.mountComponent (http://localhost:34491/static/js/main.ff387819.js:1:9686)
    at mountChildren (http://localhost:34491/static/js/main.ff387819.js:1:140079)
    at m._createContentMarkup (http://localhost:34491/static/js/main.ff387819.js:1:112229)
    at mountComponent (http://localhost:34491/static/js/main.ff387819.js:1:110893)
    at Object.mountComponent (http://localhost:34491/static/js/main.ff387819.js:1:9686) name: 'Invariant Violation', framesToPop: 1 }
๐Ÿ”ฅ 'render' from react-snapshot was never called. Did you replace the call to ReactDOM.render()?

When I swapped out the new syntax for a wrapping div, it ran with no errors.

Cheers

I followed the guide, it created a 200.html but is it working? (question)

After following the instructions in read me section: "The How To" , I get a an extra 200.html file in build directory however I am unclear as to if its working. Is there something extra i need to do? Additionally, If i use something like react-router would I just add the paths that I want react-snapshot to create static files for in the "include" but if I don't add anything in "exclude" would react-snapshot ignore all others paths? or do i need to specify which paths to ignore(exclude) exactly?

static markup for a protected secure path

I have a protected route that is allowed once user is logged in. i.e : path='/user/:id'
Is it possible to generate static markup for this path at runtime using this lib? i tired using

 "reactSnapshot": {   
     "include": [
        "/",
       "/user/**"]
   }

that leads to error:  `No such filr or directory`

Errors with external snippets

We use external snippets, like facebook pixel, linkedin. They seem to give errors on load.

We also use a snippet from Segment that tries to include the resource like this:
("https:"===document.location.protocol?"https://":"http://") which falls back to http and that shows warnings in browsers when the site is running https.

Any ideas where to look?

Create React App serves index.html(markup from "/" root route) initially on all routes

I'm using create react app with react-snapshot to pre-render static markup for my routes. i.e "/", "/signIn", "/signUp" generates index.html, signIn.html, signUp.html respectively.

However what I'm seeing is that if I go to any route my app serves index.html (the static markup for the root route "/") momentarily and then renders the actual static markup(from react-snapshot) for the route (see the gif). This behevior makes sense if I was serving the app entirely from a main.js bundle , but since I want to use the static pre-generated html files, how do I disable the the service worker from serving index.html on certain routes for which I have static html file already.

posted this on stackoverflow as well:

Update: If I remove service worker from the create react app, the app loads static file for the path fine. However, I want to keep the functionality of service worker for PWA features.

Update 2: On chrome browser the quick flicker of root route static markup happens only once for each route. After the 1st flicker it seems the chrome browser cache fixes it, additionally if I disable cache from chrome dev tools and try to go to new route the flicker of root route returns.
On Firefox browser the problem exists no matter what, on every route change or refresh the momentary flick of root route static markup occurs.

GiF shows me trying to access "/signIn" Route, and notice the word home (static markup for "/" route) come up for a moment before the actual form for signIn renders.

ezgif com-video-to-gif

How to add polyfill?

I get this error:

Pushstate server started on port 2999

๐Ÿ•ท   Starting crawling http://localhost:2999
Error: Uncaught [TypeError: o.URL.createObjectURL is not a function]
    at reportException (/map-react/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:58:24)
    at processJavaScript (/map-react/node_modules/jsdom/lib/jsdom/living/nodes/HTMLScriptElement-impl.js:130:7)
    at HTMLScriptElementImpl._eval (/map-react/node_modules/jsdom/lib/jsdom/living/nodes/HTMLScriptElement-impl.js:65:7)
    at /map-react/node_modules/jsdom/lib/jsdom/browser/resource-loader.js:31:22
    at Object.check (/map-react/node_modules/jsdom/lib/jsdom/living/nodes/Document-impl.js:89:11)
    at /map-react/node_modules/jsdom/lib/jsdom/living/nodes/Document-impl.js:108:12
    at wrappedEnqueued (/map-react/node_modules/jsdom/lib/jsdom/browser/resource-loader.js:255:16)
    at Request.request [as _callback] (/map-react/node_modules/jsdom/lib/jsdom/browser/resource-loader.js:203:9)
    at Request.self.callback (/map-react/node_modules/request/request.js:188:22)
    at emitTwo (events.js:106:13) TypeError: o.URL.createObjectURL is not a function
    at Object.o.196.../../source/worker (http://localhost:2999/static/js/main.e1936e75.js:1:488554)
    at i (http://localhost:2999/static/js/main.e1936e75.js:1:49924)
    at http://localhost:2999/static/js/main.e1936e75.js:1:49975
    at Object.o.217.../ (http://localhost:2999/static/js/main.e1936e75.js:1:515610)
    at i (http://localhost:2999/static/js/main.e1936e75.js:1:49924)
    at http://localhost:2999/static/js/main.e1936e75.js:1:49975
    at Object.o.205../worker_pool (http://localhost:2999/static/js/main.e1936e75.js:1:494710)
    at i (http://localhost:2999/static/js/main.e1936e75.js:1:49924)
    at http://localhost:2999/static/js/main.e1936e75.js:1:49975
    at Object.o.149.../render/line_atlas (http://localhost:2999/static/js/main.e1936e75.js:1:369730)
โœ๏ธ   Saving / as /index.html
๐Ÿ•ธ   Finished crawling.

I can implement polyfil, but not sure how to pass it:

Those are not working:

if (!window.URL) {
  window.URL = { createObjectURL: () => {} }
}

if (!window.URL.createObjectURL) {
  window.URL.createObjectURL = () => {}
}

if (!global.URL) {
  global.URL = { createObjectURL: () => {} }
}

if (!global.URL.createObjectURL) {
  global.URL.createObjectURL = () => {}
}

I suppose I need to patch jsdom?..

Related: alex3165/react-mapbox-gl#240

UPD

Found that this is a bug in jsdom jsdom/jsdom#1721.

Though I do not care that much about actual implementation, even simple fake noop function will do - I do not need to render map on server side, I only need render placeholder for the map, actual map will be rendered on client. Map is for users (with js), server side rendered html is for search bot.

Crawl failing with syntax error

I seem to have run into a error which I can't pinpoint the source of. I'm thinking that its some dependency issue.

My setup:
Using create-react-app (react-scripts 0.9.5)
Which uses jsdom 9.12.0, react and react-dom 15.4.2

Having this very basic example:

import React from 'react';
import {render} from 'react-snapshot';

const TestComponent = () => (
    <div><h1>Test</h1></div>
);

render(<TestComponent/>, document.querySelector('main'));

Which throws following error when running react-scripts build && react-snapshot:
(paths shortened to start at project root)

Starting crawling http://localhost:2999
Error: Uncaught [SyntaxError: Unexpected token <]
at reportException (...\node_modules\jsdom\lib\jsdom\living\helpers\runtime-script-errors.js:58:24)
at processJavaScript (...\node_modules\jsdom\lib\jsdom\living\nodes\HTMLScriptElement-impl.js:130:7)
at HTMLScriptElementImpl._eval (...\node_modules\jsdom\lib\jsdom\living\nodes\HTMLScriptElement-impl.js:65:7)
at e (...\node_modules\jsdom\lib\jsdom\browser\resource-loader.js:31:22)
at Object.check (...\node_modules\jsdom\lib\jsdom\living\nodes\Document-impl.js:89:11)
at ...\node_modules\jsdom\lib\jsdom\living\nodes\Document-impl.js:108:12
at wrappedEnqueued (...\node_modules\jsdom\lib\jsdom\browser\resource-loader.js:255:16)
at Request.request [as _callback] (...\node_modules\jsdom\lib\jsdom\browser\resource-loader.js:203:9)
at Request.self.callback (...\node_modules\request\request.js:188:22)
at emitTwo (events.js:106:13) SyntaxError: Unexpected token <
at Object.exports.runInContext (vm.js:66:16)
at processJavaScript (...\node_modules\jsdom\lib\jsdom\living\nodes\HTMLScriptElement-impl.js:128:10)
at HTMLScriptElementImpl._eval (...\node_modules\jsdom\lib\jsdom\living\nodes\HTMLScriptElement-impl.js:65:7)
at e (...\node_modules\jsdom\lib\jsdom\browser\resource-loader.js:31:22)
at Object.check (...\node_modules\jsdom\lib\jsdom\living\nodes\Document-impl.js:89:11)
at ...\node_modules\jsdom\lib\jsdom\living\nodes\Document-impl.js:108:12
at wrappedEnqueued (...\node_modules\jsdom\lib\jsdom\browser\resource-loader.js:255:16)
at Request.request [as _callback] (...\node_modules\jsdom\lib\jsdom\browser\resource-loader.js:203:9)
at Request.self.callback (...\node_modules\request\request.js:188:22)
at emitTwo (events.js:106:13)
โœ๏ธ Saving / as /index.html
๏ฟฝ Finished crawling.

Error in npm run build does not return error code

I've recently had an error where my check to find if it is running in JSDOM failed, and the build failed, but my CI server didnt know as this was not returned as an error. I'm not sure if the issue is here or in CRA?

2017-07-06T10:55:20.1494827Z Error: Uncaught [ReferenceError: sessionStorage is not defined]
2017-07-06T10:55:20.1494827Z     at reportException (C:\VSO Agent Geoff\_work\119\s\node_modules\jsdom\lib\jsdom\living\helpers\runtime-script-errors.js:58:24)
2017-07-06T10:55:20.1494827Z     at processJavaScript (C:\VSO Agent Geoff\_work\119\s\node_modules\jsdom\lib\jsdom\living\nodes\HTMLScriptElement-impl.js:130:7)
2017-07-06T10:55:20.1494827Z     at HTMLScriptElementImpl._eval (C:\VSO Agent Geoff\_work\119\s\node_modules\jsdom\lib\jsdom\living\nodes\HTMLScriptElement-impl.js:65:7)
2017-07-06T10:55:20.1494827Z     at C:\VSO Agent Geoff\_work\119\s\node_modules\jsdom\lib\jsdom\browser\resource-loader.js:31:22
2017-07-06T10:55:20.1494827Z     at Object.check (C:\VSO Agent Geoff\_work\119\s\node_modules\jsdom\lib\jsdom\living\nodes\Document-impl.js:89:11)
2017-07-06T10:55:20.1494827Z     at C:\VSO Agent Geoff\_work\119\s\node_modules\jsdom\lib\jsdom\living\nodes\Document-impl.js:108:12
2017-07-06T10:55:20.1494827Z     at wrappedEnqueued (C:\VSO Agent Geoff\_work\119\s\node_modules\jsdom\lib\jsdom\browser\resource-loader.js:255:16)
2017-07-06T10:55:20.1651068Z     at Request.request [as _callback] (C:\VSO Agent Geoff\_work\119\s\node_modules\jsdom\lib\jsdom\browser\resource-loader.js:203:9)
2017-07-06T10:55:20.1651068Z     at Request.self.callback (C:\VSO Agent Geoff\_work\119\s\node_modules\request\request.js:188:22)
2017-07-06T10:55:20.1651068Z     at emitTwo (events.js:106:13) ReferenceError: sessionStorage is not defined
2017-07-06T10:55:20.1651068Z     at Function.get (http://localhost:50813/static/js/main.8f3473ef.js:1:674766)
2017-07-06T10:55:20.1651068Z     at new t (http://localhost:50813/static/js/main.8f3473ef.js:1:985225)
2017-07-06T10:55:20.1651068Z     at new t (http://localhost:50813/static/js/main.8f3473ef.js:1:973460)
2017-07-06T10:55:20.1651068Z     at Object.<anonymous> (http://localhost:50813/static/js/main.8f3473ef.js:1:117584)
2017-07-06T10:55:20.1651068Z     at t (http://localhost:50813/static/js/main.8f3473ef.js:1:101)
2017-07-06T10:55:20.1651068Z     at Object.<anonymous> (http://localhost:50813/static/js/main.8f3473ef.js:1:463196)
2017-07-06T10:55:20.1651068Z     at t (http://localhost:50813/static/js/main.8f3473ef.js:1:101)
2017-07-06T10:55:20.1651068Z     at Object.<anonymous> (http://localhost:50813/static/js/main.8f3473ef.js:1:464023)
2017-07-06T10:55:20.1651068Z     at t (http://localhost:50813/static/js/main.8f3473ef.js:1:101)
2017-07-06T10:55:20.1651068Z     at Object.<anonymous> (http://localhost:50813/static/js/main.8f3473ef.js:1:6590)
2017-07-06T10:55:20.1651068Z ยญฦ’รถร‘ 'render' from react-snapshot was never called. Did you replace the call to ReactDOM.render()?
2017-07-06T10:55:20.1651068Z ยญฦ’รฒยฉ   Finished crawling.
2017-07-06T10:55:20.1807357Z ##[debug]rc:0
2017-07-06T10:55:20.1807357Z ##[debug]success:true
2017-07-06T10:55:20.1807357Z ##[debug]Npm command succeeded with code: 0
2017-07-06T10:55:20.1807357Z ##[debug]cleaning up...
2017-07-06T10:55:20.1807357Z ##[debug]task result: Succeeded

It would be better if the build folder could be customized.

Hi, Geelen, first of all thanks for your great job. I'm using react-snapshot in my project now, but I encountered a problem. Our website is an international site so I need to globalize it when generating static pages. My idea is to generate several build folders for different areas, such as build for English speaking area, build-cn for Chinese speaking area, etc.

When I look into the code, I found that the build folder is fixed into the code. I think we can add parameter in the cli command to determine the destination to build. The command looks like:

react-snapshot --dist build-cn

If possible I will make a PR.

Saving sub routes

Thanks for a very nice library, @geelen . I am learning how I can use it with my brand new create-react-app test site and noticing behavior that I do not understand. I would appreciate your help explaining.

I created the following site structure (urls):

  • / - home
  • /shop -- list of shopping categories
  • /shop/pizza -- list of products in shopping category
  • /shop/pizza/margherita -- product page

snapshot output:
? Starting crawling http://localhost:2999
?? Saving / as /index.html
?? Saving /shop as /shop.html
?? Saving /shop/pizza as /shop/pizza.html
?? Saving /shop/pizza/margherita as /shop/pizza/margherita.html

I run "serve build" and navigate to different urls using Chrome. Here is observed behavior:

  • / -- works as expected and home page is loaded
  • /shop -- loads folder view of the /shop/ url -- wrong
  • /shop/pizza -- loads folder view of the /shop/pizza/url -- wrong
  • /shop/pizza/margherita -- returns 404 -- wrong

What is the best approach to handle sub routes? May be I am missing http server config that should handle file layout produced by react-snapshot (other than always serving 200.html for all requests)?

One solution is for every url always produce folder and index.html inside (assuming http server always serves index.html as default doc for folders). My structure will look like:

/index.html
/shop/index.html
/shop/pizza/index.html
/shop/pizza/margherita/index.html

Thanks,
Andrey

only get 200.html , but no other pages. I have no idea what to do @@

Search for the keywords to learn more about each warning.
To ignore, add // eslint-disable-next-line to the line before.

File sizes after gzip:

147.55 KB build/static/js/main.6cbd44b6.js
15.12 KB build/static/css/main.d0134822.css

The project was built assuming it is hosted at the server root.
To override this, specify the homepage in your package.json.
For example, add this to build it for GitHub Pages:

"homepage" : "http://myname.github.io/myapp",

The build folder is ready to be deployed.
You may serve it with a static server:

npm install -g serve
serve -s build

๐Ÿ•ท Starting crawling http://localhost:64801/
Error: Uncaught [SyntaxError: Unexpected token <]
at reportException (/Users/binny/Desktop/Big-Project/projs/batchfield-react/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:58:24)
at processJavaScript (/Users/binny/Desktop/Big-Project/projs/batchfield-react/node_modules/jsdom/lib/jsdom/living/nodes/HTMLScriptElement-impl.js:130:7)
at HTMLScriptElementImpl._eval (/Users/binny/Desktop/Big-Project/projs/batchfield-react/node_modules/jsdom/lib/jsdom/living/nodes/HTMLScriptElement-impl.js:65:7)
at e (/Users/binny/Desktop/Big-Project/projs/batchfield-react/node_modules/jsdom/lib/jsdom/browser/resource-loader.js:31:22)
at Object.check (/Users/binny/Desktop/Big-Project/projs/batchfield-react/node_modules/jsdom/lib/jsdom/living/nodes/Document-impl.js:89:11)
at /Users/binny/Desktop/Big-Project/projs/batchfield-react/node_modules/jsdom/lib/jsdom/living/nodes/Document-impl.js:108:12
at wrappedEnqueued (/Users/binny/Desktop/Big-Project/projs/batchfield-react/node_modules/jsdom/lib/jsdom/browser/resource-loader.js:255:16)
at Request.request [as _callback] (/Users/binny/Desktop/Big-Project/projs/batchfield-react/node_modules/jsdom/lib/jsdom/browser/resource-loader.js:203:9)
at Request.self.callback (/Users/binny/Desktop/Big-Project/projs/batchfield-react/node_modules/request/request.js:188:22)
at emitTwo (events.js:106:13) SyntaxError: Unexpected token <
at createScript (vm.js:56:10)
at Object.runInContext (vm.js:88:10)
at processJavaScript (/Users/binny/Desktop/Big-Project/projs/batchfield-react/node_modules/jsdom/lib/jsdom/living/nodes/HTMLScriptElement-impl.js:128:10)
at HTMLScriptElementImpl._eval (/Users/binny/Desktop/Big-Project/projs/batchfield-react/node_modules/jsdom/lib/jsdom/living/nodes/HTMLScriptElement-impl.js:65:7)
at e (/Users/binny/Desktop/Big-Project/projs/batchfield-react/node_modules/jsdom/lib/jsdom/browser/resource-loader.js:31:22)
at Object.check (/Users/binny/Desktop/Big-Project/projs/batchfield-react/node_modules/jsdom/lib/jsdom/living/nodes/Document-impl.js:89:11)
at /Users/binny/Desktop/Big-Project/projs/batchfield-react/node_modules/jsdom/lib/jsdom/living/nodes/Document-impl.js:108:12
at wrappedEnqueued (/Users/binny/Desktop/Big-Project/projs/batchfield-react/node_modules/jsdom/lib/jsdom/browser/resource-loader.js:255:16)
at Request.request [as _callback] (/Users/binny/Desktop/Big-Project/projs/batchfield-react/node_modules/jsdom/lib/jsdom/browser/resource-loader.js:203:9)
at Request.self.callback (/Users/binny/Desktop/Big-Project/projs/batchfield-react/node_modules/request/request.js:188:22)
Error: Uncaught [TypeError: Cannot read property 'fn' of undefined]
at reportException (/Users/binny/Desktop/Big-Project/projs/batchfield-react/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:58:24)
at processJavaScript (/Users/binny/Desktop/Big-Project/projs/batchfield-react/node_modules/jsdom/lib/jsdom/living/nodes/HTMLScriptElement-impl.js:130:7)
at HTMLScriptElementImpl._eval (/Users/binny/Desktop/Big-Project/projs/batchfield-react/node_modules/jsdom/lib/jsdom/living/nodes/HTMLScriptElement-impl.js:65:7)
at e (/Users/binny/Desktop/Big-Project/projs/batchfield-react/node_modules/jsdom/lib/jsdom/browser/resource-loader.js:31:22)
at Object.check (/Users/binny/Desktop/Big-Project/projs/batchfield-react/node_modules/jsdom/lib/jsdom/living/nodes/Document-impl.js:89:11)
at /Users/binny/Desktop/Big-Project/projs/batchfield-react/node_modules/jsdom/lib/jsdom/living/nodes/Document-impl.js:108:12
at wrappedEnqueued (/Users/binny/Desktop/Big-Project/projs/batchfield-react/node_modules/jsdom/lib/jsdom/browser/resource-loader.js:255:16)
at Request.request [as _callback] (/Users/binny/Desktop/Big-Project/projs/batchfield-react/node_modules/jsdom/lib/jsdom/browser/resource-loader.js:203:9)
at Request.self.callback (/Users/binny/Desktop/Big-Project/projs/batchfield-react/node_modules/request/request.js:188:22)
at emitTwo (events.js:106:13) TypeError: Cannot read property 'fn' of undefined
at eval (webpack:///./src/owl.carousel.js?:1667:3)
at eval (webpack:///./src/owl.carousel.js?:1701:3)
at Object. (http://localhost:64801/static/js/main.6cbd44b6.js:1:432398)
at e (http://localhost:64801/static/js/main.6cbd44b6.js:1:420602)
at eval (webpack:///./components/OwlCarousel.jsx?:23:1)
at Object. (http://localhost:64801/static/js/main.6cbd44b6.js:1:420741)
at e (http://localhost:64801/static/js/main.6cbd44b6.js:1:420602)
at http://localhost:64801/static/js/main.6cbd44b6.js:1:420689
at http://localhost:64801/static/js/main.6cbd44b6.js:1:420694
at n.(anonymous function).exports (http://localhost:64801/static/js/main.6cbd44b6.js:1:420314)
๐Ÿ”ฅ 'render' from react-snapshot was never called. Did you replace the call to ReactDOM.render()?
๐Ÿ•ธ Finished crawling.

Homepage of "." fails in latest version

Using a homepage of "." in the package.json file results in the error:

'render' from react-snapshot was never called. Did you replace the call to ReactDOM.render()?

in react-snapshot 1.1.0, but not 1.0.4.

Perhaps a pkg.homepage value of "." should default to a basename of "/" in cli.js?

Discussing caveat: "Is starting at / and crawling sufficient?"

"Is starting at / and crawling sufficient? Might there be unreachable sections of your site?"

I think for most well-structured sites this should not be an issue since you can hopefully get to every page that ever existed by just clicking through the web of links...

However, for my use case, I want react-snapshot to render a static page that is not linked from anywhere. The page is a variation of one of my form pages, but it only contains the form, not the site layout (header, footer, etc.). (Unfortunately) I use this by embedding this page in another website in an iframe. This is why it doesn't need to be linked anywhere in the main site (it should not be crawled, but it does need to exist statically).

I know the premise of react-snapshot is to be zero-configuration so I'm not sure that my use case will be supported, but do you have any ideas how this might look?

Will it be good to give user the options to choose where to store the output html?

Right now the output is fixed to be ${urlPath}.html, I think it will be helpful that user can specify file name and location to store the output for a route.

For example,

"reactSnapshot": {
    "include": [
      "/other-path",
      "/another/nested-path"
    ],
    "exclude": [
      "/signup",
      "/other-path/exclude-me/**"
    ],
    output: {
       "/other-path": "./otherFolder/otherfiles.html"
    },
    "snapshotDelay": 300
  }

What do you guys think? I will create a PR if you guys think it is good.

Add tests

Obviously, we need tests, as described in #2

Cannot read 'map' of undefined

When I try to build my project, I am getting an error. I am definitely calling the react-snapshot render function (instead of ReactDOM.render()

I am trying to hunt down the code where it occurs, but am having trouble interpreting since its the bundled code. I tried building with my dev config, but that cause even more errors.

Any ideas / suggestions on how to proceed?

๐Ÿ•ท   Starting crawling http://localhost:64362/
โœ๏ธ   Saving / as /index.html
โœ๏ธ   Saving /en/ as /en/index.html
Error: Uncaught [TypeError: Cannot read property 'map' of undefined]
    at reportException (/Users/sara/Repositories/tablet-frontend/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:58:24)
    at processJavaScript (/Users/sara/Repositories/tablet-frontend/node_modules/jsdom/lib/jsdom/living/nodes/HTMLScriptElement-impl.js:130:7)
    at HTMLScriptElementImpl._eval (/Users/sara/Repositories/tablet-frontend/node_modules/jsdom/lib/jsdom/living/nodes/HTMLScriptElement-impl.js:65:7)
    at e (/Users/sara/Repositories/tablet-frontend/node_modules/jsdom/lib/jsdom/browser/resource-loader.js:31:22)
    at Object.check (/Users/sara/Repositories/tablet-frontend/node_modules/jsdom/lib/jsdom/living/nodes/Document-impl.js:89:11)
    at /Users/sara/Repositories/tablet-frontend/node_modules/jsdom/lib/jsdom/living/nodes/Document-impl.js:108:12
    at wrappedEnqueued (/Users/sara/Repositories/tablet-frontend/node_modules/jsdom/lib/jsdom/browser/resource-loader.js:255:16)
    at Request.request [as _callback] (/Users/sara/Repositories/tablet-frontend/node_modules/jsdom/lib/jsdom/browser/resource-loader.js:203:9)
    at Request.self.callback (/Users/sara/Repositories/tablet-frontend/node_modules/request/request.js:188:22)
    at emitTwo (events.js:106:13) TypeError: Cannot read property 'map' of undefined
    at a (http://localhost:64362/static/js/main-react.08b020e2.js:1:625919)
    at T (http://localhost:64362/static/js/main-react.08b020e2.js:1:641974)
    at _constructComponentWithoutOwner (http://localhost:64362/static/js/main-react.08b020e2.js:1:1447254)
    at _constructComponent (http://localhost:64362/static/js/main-react.08b020e2.js:1:1447108)
    at mountComponent (http://localhost:64362/static/js/main-react.08b020e2.js:1:1446289)
    at Object.mountComponent (http://localhost:64362/static/js/main-react.08b020e2.js:1:94471)
    at performInitialMount (http://localhost:64362/static/js/main-react.08b020e2.js:1:1448063)
    at mountComponent (http://localhost:64362/static/js/main-react.08b020e2.js:1:1446949)
    at Object.mountComponent (http://localhost:64362/static/js/main-react.08b020e2.js:1:94471)
    at performInitialMount (http://localhost:64362/static/js/main-react.08b020e2.js:1:1448063)
๐Ÿ”ฅ 'render' from react-snapshot was never called. Did you replace the call to ReactDOM.render()?
๐Ÿ•ธ   Finished crawling.

window.reactSnapshotRender is undefined

node_modules/react-snapshot/lib/snapshot.js:35
        window.reactSnapshotRender = function () {
                                   ^

TypeError: Cannot set property 'reactSnapshotRender' of undefined

Dynamic routes

Looked at some examples and pre defined routes like /about seems to work fine, but does this work for dynamic routes aswell like /:pageid. How should i set it up then?

chinese characters in url break snapshot

One of the routes I have to render has a few chinese chars in it: .../some-route-ๅŽๅฎ‰ๆฐ/

When snapshot's going thru my routes, it throws on this one with the error below. removing the characters from the path fixes it right up.

/home/bmp/code/admitbrain/node_modules/react-snapshot/lib/snapshot.js:41
        window.react_snapshot_render = function (element, state, rootComponent) {
                                     ^

TypeError: Cannot set property 'react_snapshot_render' of undefined
    at Object.created (/home/bmp/code/admitbrain/node_modules/react-snapshot/lib/snapshot.js:41:38)
    at reportInitError (/home/bmp/code/admitbrain/node_modules/jsdom/lib/jsdom.js:439:12)
    at resourceLoader.download (/home/bmp/code/admitbrain/node_modules/jsdom/lib/jsdom.js:259:9)
    at Request.request [as _callback] (/home/bmp/code/admitbrain/node_modules/jsdom/lib/jsdom/browser/resource-loader.js:208:7)
    at self.callback (/home/bmp/code/admitbrain/node_modules/request/request.js:188:22)
    at emitOne (events.js:115:13)
    at Request.emit (events.js:210:7)
    at Request.onRequestError (/home/bmp/code/admitbrain/node_modules/request/request.js:884:8)
    at emitOne (events.js:115:13)
    at ClientRequest.emit (events.js:210:7)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] build: `cross-env NODE_PATH=src react-scripts build && react-snapshot`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] build script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/bmp/.npm/_logs/

Support to save crawled page as `/path/index.html`

Some server, for example SimpleHTTPServer for Python, don't redirect /path to path.html. It failed when I visited /path directly.After moving all path.html to /path/index.html, everything works fine.

Could you please add a feature that would allow us to control the location of saving? This won't be hard, I think, just add a few lines in Writer.js & cli.js.

localStorage is not defined.

I guess this is due to lack of localStorage support in jsdom.

Starting` crawling http://localhost:59817/ Failed to retrieve initialize state from localStorage: ReferenceError: localStorage is not defined at http://localhost:59817/static/js/main.923fa5f4.js:24:24448 at http://localhost:59817/static/js/main.923fa5f4.js:24:26074 at o (http://localhost:59817/static/js/main.923fa5f4.js:13:23313) at Object.<anonymous> (http://localhost:59817/static/js/main.923fa5f4.js:14:24180) at t (http://localhost:59817/static/js/main.923fa5f4.js:1:107) at Object.<anonymous> (http://localhost:59817/static/js/main.923fa5f4.js:1:505) at t (http://localhost:59817/static/js/main.923fa5f4.js:1:107) at http://localhost:59817/static/js/main.923fa5f4.js:1:195 at http://localhost:59817/static/js/main.923fa5f4.js:1:200 at ContextifyScript.Script.runInContext (vm.js:32:29) Unable to persist state to localStorage: ReferenceError: localStorage is not defined at Array.<anonymous> (http://localhost:59817/static/js/main.923fa5f4.js:24:24646) at d (http://localhost:59817/static/js/main.923fa5f4.js:13:22744) at http://localhost:59817/static/js/main.923fa5f4.js:24:306 at Object.dispatch (http://localhost:59817/static/js/main.923fa5f4.js:24:25796) at t.r.handleLocationChange (http://localhost:59817/static/js/main.923fa5f4.js:23:31542) at t.componentWillMount (http://localhost:59817/static/js/main.923fa5f4.js:23:31796) at c.performInitialMount (http://localhost:59817/static/js/main.923fa5f4.js:21:17205) at c.mountComponent (http://localhost:59817/static/js/main.923fa5f4.js:21:16373) at Object.mountComponent (http://localhost:59817/static/js/main.923fa5f4.js:3:7565) at c.performInitialMount (http://localhost:59817/static/js/main.923fa5f4.js:21:17487) โœ๏ธ Saving / as /index.html

State Caveat

"This doesn't pass down any state except what's contained in the markup. That feels ok for simple use-cases (you can always roll your own) but if you have a use-case where you need it and want zero-config raise an issue."

I think I've run into this^

My site has some checkboxes and a react-select that are used to filter a schedule that's rendered on the page. It works great locally, doesn't work at all when deployed.

Am I right in thinking this might be due to some missing state? Any tips on how to workaround?

Best,
Craig

How can I avoid Google Analytics to be inserted into `header`

This package is great, however I'm facing some challenges with Google Analytics; I'm using React Google Analytics react-ga

React-ga is smart and will load the Google script lazily, react-snapshot will unfortunately capture the final rendered page and will include the final <script> object in the header of the document

So the question is how can I list objects that are lazy and should not be included in the final build?

Q: Crawl speed and limits

Is there any info on how long approximately it would take to render 1000 pages? How about 10,000+?

Zero-config sounds amazing but I'm wondering if this tool would be viable for a medium-size content app.

Thank you!
-d.

Delay snapshot until redux store is re-hydrated

I am delaying my main app component render until my store is re-hydrated using redux-persist like so:

  render() {
    const {authed, emailVerified, authedId, location, rehydrationComplete} = this.props
    return (
      <div>
      { rehydrationComplete
       ? <MainContainer>
          <Switch key={location.key} location={location}>
            <Route exact={true} path='/' component={HomeContainer} />
            <Route render={() => <h2> Oops. Page not found. </h2>} />
          </Switch>
      </MainContainer>
      : <div>...Loading </div> }
      </div>
    )
  }

However , even with snapshotDelay of 8200 all the static generated html files from react-snapshot contain only ...Loading . Am I misunderstanding what snapshotDelay option does?

Can i use the Async Branch somehow?

Pre-rendered static markup using create-react-app not being served correctly

I successfully can generate static files at build for the routes: "/", "/signIn","/signUp"respectively: index.html, signIn.html, signUp.html.

When I launch my local server to run the app, the app succesfully loads the static index.html for the root route "/" , however the issue is If I go to any subroute such as "/signIn" or "/signUp" , the app renders the same index.html static file from rootpath and then immediately serves the correct components using main.js.

Checking the View page source for the sub routes, I have confirmed the index.html markup for the homepage is being served. Additionally I checked network traffic, and it seems the static document for the subroutes is being served from Service Worker. Not sure if that is the problem.
Also posted the issue on Stackoverflow:

code in package.json:

  "reactSnapshot": {
    "include": [
      "/",
      "/signIn",
      "/signUp"
    ],
    "exclude": [],
    "snapshotDelay": 300
  }

react-snapshot crawl fails

console error below.

Pushstate server started on port 2999

๐Ÿ•ท   Starting crawling http://localhost:2999
TypeError: Parameter "url" must be a string, not object
    at Url.parse (url.js:81:11)
    at urlParse (url.js:75:5)
    at Url.resolve (url.js:646:29)
    at Object.urlResolve [as resolve] (url.js:642:40)
    at .../node_modules/react-snapshot/lib/Crawler.js:96:44
    at Array.forEach (native)
    at .../node_modules/react-snapshot/lib/Crawler.js:88:83
    at Array.forEach (native)
    at Crawler.extractNewLinks (.../node_modules/react-snapshot/lib/Crawler.js:86:36)
    at .../node_modules/react-snapshot/lib/Crawler.js:68:15

on now.sh i get this error:

at Url.parse (url.js:95:11)
>     at urlParse (url.js:89:5)
>     at Url.resolve (url.js:657:29)
>     at Object.urlResolve [as resolve] (url.js:653:40)
>     at /home/nowuser/src/node_modules/react-snapshot/lib/Crawler.js:96:44
>     at Array.forEach (native)
>     at /home/nowuser/src/node_modules/react-snapshot/lib/Crawler.js:88:83
>     at Array.forEach (native)
>     at Crawler.extractNewLinks (/home/nowuser/src/node_modules/react-snapshot/lib/Crawler.js:86:36)
>     at /home/nowuser/src/node_modules/react-snapshot/lib/Crawler.js:68:15

anyone got into this?
not sure what to try next

thanks

`react-helmet` v5 no longer works with `react-snapshot`

Setup: Follow this tutorial to setup an app with react-helmet version 5 or greater.

Expected: The snapshot contains a title (or other metadata) specified in a <Helmet>

Actual: The snapshot contains the head from the index.html template without the proper title, etc.

nfl/react-helmet@0ad7908 is the breaking commit in react-helmet. It looks like they started batching updates to the dom for better performance. My guess is that the snapshot is taken before the callback is triggered to update the title now.

If this seems like this is the correct place to fix the issue I would be happy to investigate a fix/pr.

Error during build: Network request failed

I am trying to build a static site out from a create-react-app project, and I get the following error when I run 'npm run build'

[HPM] Proxy created: / -> http://localhost:3001
๐Ÿ•ท Starting crawling http://localhost:60815/
โœ๏ธ Saving / as /index.html
๐Ÿ•ธ Finished crawling.
(node:25895) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): TypeError: Network request failed
(node:25895) DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

No available storage method found Error

I am using this with redux-persist and redux.
It seemed to be linked to localForage
UPDATE: I tried using delay in package.json of 800 and yet error persists and no static html files are generated for the routes.

(node:10052) UnhandledPromiseRejectionWarning: Unhandled promise rejection (reje
ction id: 2): Error: No available storage method found.
??   Saving / as /index.html
?   Finished crawling.```

Feature Request: Skip proxied routes to avoid error

This project is awesome! Thanks for your work!

This code is causing an error (Error: Only string proxies are implemented currently.) when attempting to run react-snapshot on an app with proxies defined in package.json.

if (proxy) {
if (typeof proxy !== "string") throw new Error("Only string proxies are implemented currently.")
app.use(httpProxyMiddleware({
target: proxy,
onProxyReq: proxyReq => {
if (proxyReq.getHeader('origin')) proxyReq.setHeader('origin', proxy)
},
changeOrigin: true,
xfwd: true,
}))

I'm using proxies defined in package.json of a CRA app for development purposes (as laid out in the CRA docs). The paths are forwarded to node services.

Maybe it would be beneficial to add the option to skip over proxied routes.

Use env PUBLIC_URL as an alternative to pkg.homepage

Would it be possible to use the env PUBLIC_URL as an alternative to pkg.homepage?

Consider the use case of deploying to different environments which each have a different path. Currently we have to use something like sed to change the package.json before building. If we had an env var, things would be simpler

I would create a PR if this is something you are willing to include?

Getting "addComponentAsRefTo(...): Only a ReactOwner can have refs" when using render() with React 16 + Redux + Redux-Form.

Hi there,

For some reason react-snapshot was the problem when using redux + redux-form.

Once I dropped back to ReactDOM.render() all is well. This is weird!

Has anyone else had this, and if so is there a workaround - I'd like to still use this module.

Note: I did try this with the latest 15.x React version and still seems to happen.

Error

addComponentAsRefTo(...): Only a ReactOwner can have refs. You might be adding a ref to a component that was not created inside a component's `render` method, or you have multiple copies of React loaded (details: https://fb.me/react-refs-must-have-owner).

Stack frames:

invariant
node_modules/fbjs/lib/invariant.js:42
addComponentAsRefTo
node_modules/react-snapshot/node_modules/react-dom/lib/ReactOwner.js:66
attachRef
node_modules/react-snapshot/node_modules/react-dom/lib/ReactRef.js:21
./node_modules/react-snapshot/node_modules/react-dom/lib/ReactRef.js.ReactRef.attachRefs
node_modules/react-snapshot/node_modules/react-dom/lib/ReactRef.js:40
ReactCompositeComponentWrapper.attachRefs
node_modules/react-snapshot/node_modules/react-dom/lib/ReactReconciler.js:21
CallbackQueue.notifyAll
node_modules/react-snapshot/node_modules/react-dom/lib/CallbackQueue.js:74
ReactReconcileTransaction.close
node_modules/react-snapshot/node_modules/react-dom/lib/ReactReconcileTransaction.js:78
ReactReconcileTransaction.closeAll
node_modules/react-snapshot/node_modules/react-dom/lib/Transaction.js:207
ReactReconcileTransaction.perform
node_modules/react-snapshot/node_modules/react-dom/lib/Transaction.js:154
batchedMountComponentIntoNode
node_modules/react-snapshot/node_modules/react-dom/lib/ReactMount.js:124
ReactDefaultBatchingStrategyTransaction.perform
node_modules/react-snapshot/node_modules/react-dom/lib/Transaction.js:141
batchedUpdates
node_modules/react-snapshot/node_modules/react-dom/lib/ReactDefaultBatchingStrategy.js:60
batchedUpdates
node_modules/react-snapshot/node_modules/react-dom/lib/ReactUpdates.js:95
_renderNewRootComponent
node_modules/react-snapshot/node_modules/react-dom/lib/ReactMount.js:317
_renderSubtreeIntoContainer
node_modules/react-snapshot/node_modules/react-dom/lib/ReactMount.js:399
render
node_modules/react-snapshot/node_modules/react-dom/lib/ReactMount.js:420
render
node_modules/react-snapshot/lib/index.js:23

Feature: convention or config option to specify error page

Thank you for your work on this @geelen - I've been banging my head against the wall for a week trying to pre-render my very simple React site for static hosting. After numerous fruitless attempts with server-rendering, server-side webpack bundles and various static site generators, this lib is an absolute breath of fresh air. I'm not using create-react-app, and react-snapshot worked for me pretty much "out of the box" - I only needed to send my build output to build/ rather than dist/.

The only gotcha I ran into was trying to snapshot an error page to serve for 404s. I use react-router, and have a catch-all route configured. This works fine once client-side routing kicks in, but I still need to pre-render an error page to serve to users with JS disabled. The problem is the route is unreachable by crawling relative links from the entry point. As a workaround, I'm including a link to /error in my root route and hiding it with CSS.

Any of the following would be welcome features:

  • A convention for crawling error pages (crawler hits /error or some other standard location)
  • A configuration option for specifying an error page or other locations unreachable by crawling
  • For react-router users, an option to tell the crawler to traverse the route tree

I'll try to hack something together and will open a PR if I get anywhere with any of the above, but documenting this in the meantime in case someone else gets to it first.

Build not starting properly

My build is not starting properly. I keep getting:

[TypeError: Parameter 'url' must be a string, not object]

when running npm run build, but no further information is shown.

I am using Create React App

This is my package.json file:

{
  "name": "landing-react",
  "version": "0.0.1",
  "private": true,
  "devDependencies": {
    "react-scripts": "0.9.5",
    "react-snapshot": "^1.0.4"
  },
  "dependencies": {
    "classnames": "^2.2.5",
    "jquery": "^3.2.1",
    "react": "^15.5.4",
    "react-dom": "^15.5.4",
    "react-scroll": "^1.5.2"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build && react-snapshot",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  }
}

And here is my index.js, located in /src/index.js

import React from 'react';
import { render } from 'react-snapshot';

import App from './containers/App';
import './css/index.css';

render(
  <App />,
  document.getElementById('root')
);

Folder Indexes

A slight issue we've run into is that subfolders don't have an index.html file.

For example our blog has a fairly typical url structure, like this:

`/blog` displays the main blog index.
`/blog/${post_name}` displays an individual blog post.
`/blog/${category}/${page}` displays a category index.

The files generated by react-snapshot are structured like this:

/blog.html
/blog/post-name.html
/blog/category/1.html

The problem this causes is that when you visit /blog directly (i.e. not through the in-app navigation), the server returns the file index for the /blog/ folder instead of the /blog.html file.

We're hosting this on s3, so I don't think we can change any server config to fix that. Instead, we'll have to manually copy the blog.html file into the /blog/ folder and rename it index.html for it to work as expected. I'm pretty sure most servers would do the same thing as ours, so regardless of server type it'd take a bit of configuration for it to display the correct file.

Does it make sense for the current setup to be the default behaviour? Or perhaps have we missed some setting somewhere which changes how it works?

Picture tag incorrectly populating src

When using the picture tag with a "source" and "src", react-snapshot will populate the src with the "source". For example, this is the correct code and what we see in the development build:

<picture data-object-fit="contain">
  <source media="(min-width: 640px)" sizes="(min-width: 1000px) 100vw, 1900px" srcset="hero_D.jpg">
  <img alt="alt text" src="hero_M.jpg">
</picture>

but instead it is rendering this after the snapshot, and note the correct image in data-pfsrc. src is displaying what was set in srcset:

<picture data-reactid="14">
  <source media="(min-width: 640px)" sizes="(min-width: 1000px) 100vw, 1900px" srcset="hero_D.jpg" data-reactid="15">
  <img alt="alt text" src="hero_D.jpg" data-reactid="16" data-pfsrc="hero_M.jpg">
</picture>

Thanks!

How does the snapshotDelay actually work? (question)

I'm trying to understand what actually happens with the snapshotDelay value. What is actually getting delayed?

Recently, i ran into an issue which I thought could be solved by increasing the snapshotDelay see #68 .

But Now I don't know If I even understand how this prop effects my app.

question about the extracting stylesheet import to static markup

This is a Amazing Lib since it has improved my page load tremendously. I'm hoping there is something I'm just not doing to get the following done. I'm importing external css file into my component however those styles never get extracted to the static markup. only the inline styles are extracted. This leads to layout jump on initial page load and on page refresh, because the styles are missing when the react-snapshot generated snapshot renders.

create react app homepage

Hey,

I am not sure if this falls under the category of Is starting at / and crawling sufficient? Might there be unreachable sections of your site? but when I tried using the homepage option in package json from create react app build I am getting an error:

Error: Uncaught [SyntaxError: Unexpected token <]
    at reportException (/opt/app/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:58:24)
    at processJavaScript (/opt/app/node_modules/jsdom/lib/jsdom/living/nodes/HTMLScriptElement-impl.js:130:7)
    at HTMLScriptElementImpl._eval (/opt/app/node_modules/jsdom/lib/jsdom/living/nodes/HTMLScriptElement-impl.js:65:7)
    at e (/opt/app/node_modules/jsdom/lib/jsdom/browser/resource-loader.js:31:22)
    at Object.check (/opt/app/node_modules/jsdom/lib/jsdom/living/nodes/Document-impl.js:88:11)
    at /opt/app/node_modules/jsdom/lib/jsdom/living/nodes/Document-impl.js:107:12
    at wrappedEnqueued (/opt/app/node_modules/jsdom/lib/jsdom/browser/resource-loader.js:255:16)
    at Request.request [as _callback] (/opt/app/node_modules/jsdom/lib/jsdom/browser/resource-loader.js:203:9)
    at Request.self.callback (/opt/app/node_modules/request/request.js:187:22)
    at emitTwo (events.js:106:13) SyntaxError: Unexpected token <
    at Object.exports.runInContext (vm.js:66:16)
    at processJavaScript (/opt/app/node_modules/jsdom/lib/jsdom/living/nodes/HTMLScriptElement-impl.js:128:10)
    at HTMLScriptElementImpl._eval (/opt/app/node_modules/jsdom/lib/jsdom/living/nodes/HTMLScriptElement-impl.js:65:7)
    at e (/opt/app/node_modules/jsdom/lib/jsdom/browser/resource-loader.js:31:22)
    at Object.check (/opt/app/node_modules/jsdom/lib/jsdom/living/nodes/Document-impl.js:88:11)
    at /opt/app/node_modules/jsdom/lib/jsdom/living/nodes/Document-impl.js:107:12
    at wrappedEnqueued (/opt/app/node_modules/jsdom/lib/jsdom/browser/resource-loader.js:255:16)
    at Request.request [as _callback] (/opt/app/node_modules/jsdom/lib/jsdom/browser/resource-loader.js:203:9)
    at Request.self.callback (/opt/app/node_modules/request/request.js:187:22)
    at emitTwo (events.js:106:13)

Everything seems to work without the homepage flag.

My app isn't so big so I could probably go in and redo the differences performed by the build script.

Once again this might fall under the aforementioned caveat, though it seems like a caveat that can be easily handled by parsing out the package json and maybe making a few edits to the server function. (i.e. still no configs of your own!)

Let me know what you think!

Build not working when opening the page manually.

When opening the index.html file by doing right click -> open with .....
I receive the following error:

main.0ea4972e.css Failed to load resource: net::ERR_FILE_NOT_FOUND
main.202c8226.js Failed to load resource: net::ERR_FILE_NOT_FOUND

I went to the builded index.html file where I see that index.html is trying to import the files from /static/css/ instead of static/css/, see here:

<link href="/static/css/main.0ea4972e.css" rel="stylesheet">
<script type="text/javascript" src="/static/js/main.202c8226.js"></script>

If I replace this to:

<link href="static/css/main.0ea4972e.css" rel="stylesheet">
<script type="text/javascript" src="static/js/main.202c8226.js"></script>

It works!

Error when combining with react-loadable.

Hi, I followed guide in create-react-app and done code splitting with react-loadable. After deploying my app to server, I found an error in console said Can't find variable: webpackJsonp . I then found the problem is that react-snap will overwrite the html but inject chunks inside <head>, the chunk's content is something like webpackJsonp(...). Since the webpackJsonp was defined inside main.js and main.js is at the bottom of <body>, that cause the problem.

Here's the sample code produced by npm run build && npm run react-snap

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <meta name="viewport"
        content="width=device-width,initial-scale=1,shrink-to-fit=no">
  <meta name="theme-color"
        content="#000000">
  <link rel="manifest"
        href="/manifest.json">
  <link rel="shortcut icon"
        href="/favicon.ico">
  <title>React App</title>
  <link href="/static/css/main.cacbacc7.css"
        rel="stylesheet">
  <style type="text/css"
         data-styled-components="kCrotB hzkseL"
         data-styled-components-is-local="true">
    /* sc-component-id: sc-bdVaJa */

    .sc-bdVaJa {}

    .kCrotB {
      display: -webkit-box;
      display: -webkit-flex;
      display: -ms-flexbox;
      display: flex;
      height: 100vh;
      width: 100vw;
    }
    /* sc-component-id: sc-bwzfXH */

    .sc-bwzfXH {}

    .hzkseL {
      font-size: 48px;
      text-align: center;
      margin: auto;
    }

  </style>
  <script type="text/javascript"
          charset="utf-8"
          src="/static/js/0.7749783b.chunk.js"></script>
</head>

<body><noscript>You need to enable JavaScript to run this app.</noscript>
  <div id="root">
    <div class="App"
         data-reactroot=""
         data-reactid="1"
         data-react-checksum="1111271051">
      <div class="App-header"
           data-reactid="2">
        <h2 data-reactid="3">Welcome to React</h2></div>
      <ul data-reactid="4">
        <li data-reactid="5"><a class="active"
             style="text-decoration:none;"
             aria-current="true"
             href="/"
             data-reactid="6">Home</a></li>
        <li data-reactid="7"><a aria-current="false"
             href="/about"
             data-reactid="8">About</a></li>
      </ul>
      <div class="sc-bdVaJa kCrotB"
           data-reactid="9">
        <p class="sc-bwzfXH hzkseL"
           data-reactid="10">Loading...</p>
      </div>
    </div>
  </div>
  <script type="text/javascript"
          src="/static/js/main.9652c51a.js"></script>
</body>

</html>

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.