Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

No loading/serving of stylesheets - react

I am attempting SSR with React router's StaticRouter.

express.js (server)

const html = ReactDOMServer.renderToString(
    <StaticRouter location={req.url} context={context}>
        <App />
    </StaticRouter>
);

res.status(200).send(`
    <!DOCTYPE html>
    <html>
        <head>
            <link rel="stylesheet" href="/app.css" type="text/css"/>
        </head>
        <body>
            <div id="app">${html}</div>
        </body>
    </html>
`);

Serving of static files:

app.use(express.static(path.resolve(__dirname, "../dist/client")));

App.js (shared)

import React from "react";
import { Switch, Route } from "react-router";

export default () => {
    return (
        <Switch>
            ...
        </Switch>
    );
};

index.jsx (client)

import React from "react";
import { BrowserRouter } from "react-router-dom";
import ReactDOM from "react-dom";
import App from "./App";

ReactDOM.render(
    <BrowserRouter>
        <App/>
    </BrowserRouter>,
    document.getElementById("app")
);

./styles/Main.scss

.header {
    background-color: #002933;
}

I have 2 webpack configurations, 1 for the client & 1 for the server:

webpack.config.dev.js

const nodeExternals = require("webpack-node-externals");
const webpack = require("webpack");
const ExtractTextPlugin = require("extract-text-webpack-plugin");

module.exports = {
    devtool: "cheap-module-eval-source-map",
    entry: {
        app: [
            "eventsource-polyfill",
            "webpack-hot-middleware/client",
            "webpack/hot/only-dev-server",
            "react-hot-loader/patch",
            "./client/index.jsx",
        ],
        vendor: [
            "react",
            "react-dom",
        ],
    },
    output: {
        path: `${__dirname}/dist/client`,
        ...
    },
    ...
    module: {
        loaders: [
            ...
            }, {
                test: /\.scss$/,
                exclude: /node_modules/,
                loader: ExtractTextPlugin.extract({
                    fallback: "style-loader",
                    use: [
                        {
                            loader: "css-loader",
                            query: {
                                localIdentName: "[hash:8]",
                                modules: true
                            }
                        }, {
                            loader: "postcss-loader"
                        }, {
                            loader: "sass-loader"
                        }
                    ]
                }),
            },
        ],
    },
    plugins: [
        new ExtractTextPlugin({
            filename: "[name].css",
            allChunks: true
        }),
    ]
};

webpack.config.server.js

const ExternalsPlugin = require("webpack-externals-plugin");

module.exports = {
    ...
    output: {
        path: `${__dirname}/dist/`,
        filename: "server.bundle.js",
    },
    ...
    resolve: {
        ...
        modules: [
            "client",
        ],
    },
    module: {
        loaders: [
            {
                test: /\.(js|jsx)$/,
                exclude: /node_modules/,
                loader: "babel-loader",
            }, {
               test: /\.scss$/,
               loader: 'style-loader!css-loader/locals?module&localIdentName=[name]__[local]___[hash:base64:5]!sass-loader',
            },
        ],
    },
    plugins: [
        new ExternalsPlugin({
            type: "commonjs",
            include: `${__dirname}/node_modules/`,
        }),
    ],
};

I have a JSX file where the .header should be applied to:

import React from "react";

import Links from "./Links.jsx";
import profilePic from "../../img/brand/profilePic.jpg";

import styles from "../../styles/Main.scss";

export default class Header extends React.Component {
    constructor() {
        super();
    }

    render() {
        return (
            <header className={styles.header}>
                <img src={profilePic} alt="Professional Picture"/>
                <h5>{this.props.pageName}</h5>
                <Links/>
            </header>
        );
    }
}

This throws the error:

TypeError: Cannot read property 'header' of undefined
    at Header.render (E:/Documents/Projects/website/client/js/components/Header.jsx:22:30)
    at resolve (E:\Documents\Projects\website\node_modules\react-dom\cjs\react-dom-server.node.development.js:2149:18)
    at ReactDOMServerRenderer.render (E:\Documents\Projects\website\node_modules\react-dom\cjs\react-dom-server.node.development.js:2260:22)
    at ReactDOMServerRenderer.read (E:\Documents\Projects\website\node_modules\react-dom\cjs\react-dom-server.node.development.js:2234:19)
    at Object.renderToString (E:\Documents\Projects\website\node_modules\react-dom\cjs\react-dom-server.node.development.js:2501:25)
    at E:/Documents/Projects/website/server/config/lib/express.js:204:31
    at Layer.handle [as handle_request] (E:\Documents\Projects\website\node_modules\express\lib\router\layer.js:95:5)
    at trim_prefix (E:\Documents\Projects\website\node_modules\express\lib\router\index.js:317:13)
    at E:\Documents\Projects\website\node_modules\express\lib\router\index.js:284:7
    at Function.process_params (E:\Documents\Projects\website\node_modules\express\lib\router\index.js:335:12)
    at next (E:\Documents\Projects\website\node_modules\express\lib\router\index.js:275:10)
    at p3p (E:\Documents\Projects\website\node_modules\lusca\lib\p3p.js:15:9)
    at E:\Documents\Projects\website\node_modules\lusca\index.js:59:28
    at xframe (E:\Documents\Projects\website\node_modules\lusca\lib\xframes.js:12:9)
    at E:\Documents\Projects\website\node_modules\lusca\index.js:59:28
    at xssProtection (E:\Documents\Projects\website\node_modules\lusca\lib\xssprotection.js:16:9)

When running the application, webpack reports that the stylesheet has been loaded:

enter image description here

EDIT

Other than an ES6 import, I have attempted to use CommonJS' require() as in MERN but still no look...

When I build my server webpack config, I am now getting the error:

ERROR in (webpack)-dev-middleware/node_modules/mime/index.js
Module not found: Error: Can't resolve './types/standard' in 'E:\Documents\Projects\website\node_modules\webpack-dev middleware\node_modules\mime'
 @ (webpack)-dev-middleware/node_modules/mime/index.js 4:26-53
 @ (webpack)-dev-middleware/index.js
 @ ./server/config/lib/express.js
 @ ./server/config/lib/app.js
 @ ./server/server.js

I am not sure if this a red-herring or not in this situation or not but thought it worth mentioning here as I am quite lost. Feel as though I am clutching at straws at this point.

This is my .babelrc:

{
    "presets": [
        "react",
        "es2015",
        "stage-0"
    ],
    "plugins": [
        "react-hot-loader/babel",
        "transform-decorators-legacy"
    ],
    "env": {
        "server": {
            "plugins": [
                [
                    "css-modules-transform", {
                        "preprocessCss": "./loaders/sass-loader.js",
                        "generateScopedName": "[hash:8]",
                        "extensions": [".scss"]
                    }
                ]
            ]
        },
        "production": {
            "presets": [
                "es2015",
                "react",
                "react-optimize",
                "es2015-native-modules",
                "stage-0"
            ]
        }
    }
}

I was attempting to go back to basics and have my babel handle server-side bundling instead of webpack. This was built from a tutorial for SSR with CSS modules I was kindly linked to by @mootrichard

EDIT 2

A few observations which might help...when using an es6 import for stylesheets:

import styles from "../../styles/Main.scss";

and log styles into the console, it returns undefined (evidence that it cannot find the file for some reason).

When putting the <link> tag in the head for the initial page, the <link> tag is present in the markup but not in the network:

enter image description here

enter image description here

However, when navigating to localhost:8000/app.css, a positive response with the styling is sent back:

enter image description here

If the browser can find the bundled version standalone, then why is it not being loaded in my initial page? (The path is correct)

like image 642
wmash Avatar asked Feb 15 '18 22:02

wmash


1 Answers

You're having issues because you're using css-loader/locals but not using ExtractTextPlugin (at least in Development). https://github.com/webpack-contrib/css-loader/issues/59

Note: For prerendering with extract-text-webpack-plugin you should use css-loader/locals instead of style-loader!css-loader in the prerendering bundle. It doesn't embed CSS but only exports the identifier mappings.

This also explains why you're not able to access the style variable .theHeader.

Also, the error Resource interpreted as Stylesheet but transferred with MIME type text/html: "http://localhost:3000/app.css". is a red herring. That is simply the error message you receive if you try to load a stylesheet that doesn't even exist. Which in this case, doesn't appear to be in the directory that you think it is, or isn't actually being generated into a file there.

Since ExtractTextPlugin is disabled in development, its likely that your CSS is only being processed by css-loader/locals. This might not be a problem in production, since it pairs with ExtractTextPlugin but could explain your problems of running this in development.


Update:

In looking into this over a little more, I came across a blog post that I think might help you figure out how to configure your CSS to work how you want. https://medium.com/@mattvagni/server-side-rendering-with-css-modules-6b02f1238eb1

I think the main reason for the complication here is that you're sending over the HTML as a rendered string via ReactDOMServer. So there is no where for webpack to inject a <link> tag into. You might want to consider just having a <link> tag in your header to reference your desired CSS file, since webpack is going to create a single CSS file anyways.

Finally, I highly recommend studying a bit more on webpack, especially since SSR is a newer process and requires doing things a bit differently than many have initially anticipated when webpack was first created.

like image 127
mootrichard Avatar answered Oct 03 '22 04:10

mootrichard