theKashey / used-styles

📝All the critical styles you've used to render a page.
MIT License
138 stars 10 forks source link

TypeError: Cannot read property 'isReady' of undefined #18

Open sayjeyhi opened 4 years ago

sayjeyhi commented 4 years ago

I did same steps as explained in example but got this error :

/Users/user/projcect/node_modules/used-styles/dist/es5/utils.js:39
    if (!def.isReady) {
             ^

TypeError: Cannot read property 'isReady' of undefined
    at Object.exports.assertIsReady (/Users/user/projcect/node_modules/used-styles/dist/es5/utils.js:39:14)
    at Transform.transform [as _transform] (/Users/user/projcect/node_modules/used-styles/dist/es5/reporters/used.js:37:21)

my code looks like :

import React from 'react';
import through from 'through';
import { renderToNodeStream } from 'react-dom/server';
import { StaticRouter, matchPath } from 'react-router-dom';
import { HelmetProvider } from 'react-helmet-async';
import { Provider as ReduxProvider } from 'react-redux';
import { ServerStyleSheet } from 'styled-components';
import { ChunkExtractor } from '@loadable/server';
import {
  createLink,
  createStyleStream,
  discoverProjectStyles,
} from 'used-styles';
import MultiStream from 'multistream';
import Hoc from 'components/Common/Hoc';

import { readableString } from './utils/readable';
import createStore from '../../app/config/configureStoreSSR';
import { indexHtml } from './indexHtml';

export async function renderServerSideApp(req, res) {
  const context = {};
  const helmetContext = {};

  /**
   * Styled component instance
   * @type {ServerStyleSheet}
   */
  const sheet = new ServerStyleSheet();

  try {
    const { store, history } = createStore(req.url);

    /**
     * A box of components which will load Server app
     * @returns {*}
     * @constructor
     */
    const ServerApp = () => (
      <HelmetProvider context={helmetContext}>
        <ReduxProvider store={store}>
          <StaticRouter history={history} location={req.url} context={context}>
            <Hoc routerProvider="server" />
          </StaticRouter>
        </ReduxProvider>
      </HelmetProvider>
    );

    const stats = require(`${process.cwd()}/build/${process.env.PUBLIC_URL}/loadable-stats.json`);
    const chunkExtractor = new ChunkExtractor({ stats });

    const stylesLookup = discoverProjectStyles(`${process.cwd()}/build`); // __dirname usually

    /**
     * Handle 404 and 301
     */
    if (context.url) {
      return res.redirect(301, context.url);
    }
    if (context.status === 404) {
      res.status(404);
    }

    const [header, footer] = indexHtml({
      helmet: helmetContext.helmet,
      state: store.getState(),
    });

    /**
     * Get styledComponents global styles
     */
    const jsx = chunkExtractor.collectChunks(
      sheet.collectStyles(<ServerApp />),
    );

    const endStream = readableString(footer);

    res.set({ 'content-type': 'text/html; charset=utf-8' });
    res.write([header, '<div id="root">'].join(''));

    // Pipe that HTML to the response
    const HtmlStream = sheet.interleaveWithNodeStream(renderToNodeStream(jsx));

    const lookup = await stylesLookup;
    // create a style steam
    const styledStream = createStyleStream(
      lookup,
      style =>
        // _return_ link tag, and it will be appended to the stream output
        createLink(`dist/${style}`), // <link href="dist/mystyle.css />
    );
    new MultiStream([styledStream, endStream]).pipe(res);

    HtmlStream.pipe(
      .pipe(styledStream, { end: false }).pipe(
      through(
        function write(data) {
          this.queue(styledStream);
          this.queue(data);
        },
        async function end() {
          this.queue('</div>');
          this.queue(chunkExtractor.getScriptTags());
          this.queue(chunkExtractor.getStyleTags());
          this.queue(endStream);
          this.queue(null);

          res.end();
        },
      ),
    );
  } catch (error) {
    res.status(500).send('Oops, better luck next time!');
  }
}
theKashey commented 4 years ago

That means that const lookup = await stylesLookup; resolves to undefined. And it does. And it's easy to fix it.

However this is intended way to use it:

await stylesLookup;
 const styledStream = createStyleStream(
      stylesLookup, // use the same variable

Plus please scan styles only once, and better outside of the render function, so they would be ready a bit earlier. Plus the scan operation is more or less expensive(they all got parsed with PostCSS and analysed), and it's better not to run it frequently.