stereobooster / react-snap

👻 Zero-configuration framework-agnostic static prerendering for SPAs
MIT License
5.07k stars 392 forks source link

TypeError: Cannot read property 'ok' of null after adding inline css #405

Open ShivamJoker opened 5 years ago

ShivamJoker commented 5 years ago
🔥  error at / TypeError: Cannot read property 'ok' of null
    at isOk (/Users/shivam/Documents/web/ylight-music/client/node_modules/minimalcss/src/run.js:13:35)
    at Promise (/Users/shivam/Documents/web/ylight-music/client/node_modules/minimalcss/src/run.js:290:14)
    at process._tickCallback (internal/process/next_tick.js:68:7)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! client@0.1.0 postbuild: `react-snap`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the client@0.1.0 postbuild 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!     /Users/shivam/.npm/_logs/2019-08-16T20_15_19_708Z-debug.log

if i am not adding the inlineCss option it works fine but the problem happens when i reload the player page and minimize the player my bottom navigation bar gets messed up image

I have no idea why this is happening please help I am using progressive web app and also react lazy with react router

AdamBCo commented 4 years ago

I'm having the same issue. @ShivamJoker Were you able to resolve this issue?

aptlin commented 4 years ago

Same here, any updates?

PatNeedham commented 4 years ago

I was getting the same issue and was able to resolve it by running NODE_ENV=production REACT_APP_ENV=production npm run build. For background context I'm also using mock-service-worker and have this code in my src/index.tsx:

if (process.env.REACT_APP_ENV === "development") {
  const { worker } = require("./mocks/browser");
  worker.start();
}

I thought merely including NODE_ENV=production would be sufficient enough but was wrong about that. Hope this helps anyone else using MSW. (Just tried with only setting REACT_APP_ENV=production and that worked perfectly fine also)

The minimalcss library's issue with this same title also has a comments from people saying that disabling their service worker got the error to go away.

superjose commented 4 years ago

@PatNeedham Thanks for the post. But I'm still having the same issue. It's so unpredictable to make it work. It's a shame because this library is exactly what I was looking for. Maybe the solution is going to be to migrate to Gatsby altogether, as it has better support for the App Shell architecture that I'm looking for.

superjose commented 4 years ago

@PatNeedham @superjose I've found out a solution. It's hacky, and I will make a post that will explain this solution further on.

What I'm doing is disabling the service worker when react-snap tries to pre-render. For this, I insert a random piece of code that I can replace to true after Webpack minifies the code (and oh boy! it is aggressive!). You can't use environment variables because they are entirely replaced at build time! And there's no way to reference them:

First, wrap the register and unregiser service worker functions in a function:

// in serviceWorker.ts
// To Prevent TreeShaking from getting the register and unregister out!
export function useSw(useSw?: boolean) {
  console.log('entered useSw');
  useSw ? register() : unregister();
}

Then in your index.ts file (where you're hydrating)

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './index.css';
import { useSw } from './serviceWorker';

const rootElement = document.getElementById('root');

if (rootElement?.hasChildNodes()) {
  ReactDOM.hydrate(<App />, rootElement);
} else {
  ReactDOM.render(<App />, rootElement);
}

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: http://bit.ly/CRA-PWA

// Disables the service worker if react snap is set.
const enableSw = null !== document.getElementById('snapmode');
console.log('using SW is  ', enableSw);
useSw(enableSw);

We need to do it this way to prevent Tree Shaking from Webpack. See what I do here, I place a null !== document.getElementById('snapmode');, which will be a replaced by a true after react-snap finishes.

The reason why we place that like that is because strings get evaluated at build time and are completely replaced. That code does not get more minified than that (and we can easily replace it with gulp).

Then we will need Gulp. Create a gulpfile.js in the root of your directory:

const gulp = require('gulp');
const replace = require('gulp-replace');

gulp.task('snap-off', function () {
  return gulp
    .src('./build/static/js/**.js')
    .pipe(replace('null!==document.getElementById("snapmode")', true))
    .pipe(
      gulp.dest(function (file) {
        return file.base;
      }),
    );
});

Note some quirks: Webpack adds the quotes for getElementById('snapmode') even if you specify single ones.

Then, in your package.json:

"scripts": {
    "analyze": "source-map-explorer 'build/static/js/*.js'",
    "start": "react-scripts start --host 0.0.0.0",
    "build": "react-scripts build && yarn run build-snap && yarn run enable-sw",
    "build-snap": "react-snap && npm run generate-sw",
    "enable-sw": "gulp snap-off",
    "test": "react-scripts test --env=jest-environment-jsdom-sixteen --maxWorkers=4",
    "eject": "react-scripts eject",
    "lint": "eslint --ext .ts --ext .tsx src",
    "lint-fix": "eslint --fix --ext .ts --ext .tsx src",
    "generate-sw": "sw-precache --root=build --config scripts/sw-precache-config.js && uglifyjs build/service-worker.js -o build/service-worker.js"
  },

Run yarn build.

Note that After build-snap is ran, we run the enable-sw to execute our gulp file.

superjose commented 4 years ago

I'm going to do that same approach to render the app-shell.

almahdi404 commented 3 years ago

Same here, any solution?

almahdi404 commented 3 years ago

Disabling the serviceworker solved the issue for me.

if ("serviceWorker" in navigator && navigator.userAgent !== "ReactSnap") {
  navigator.serviceWorker.register("/serviceworker.js")
}

disable it by checking the ReactSnap userAgent

natansevero commented 2 years ago

The @almahdi404 solution works for me. Thank you so much