Sunday, February 25, 2018

No loading/serving of stylesheets - react

Leave a Comment

I am rendering pages server-side 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="./dist/client/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`,         filename: "app.min.js",         publicPath: "http://0.0.0.0:8000/"     },     resolve: {         extensions: [             ".js",             ".jsx"         ],         modules: [             "node_modules"         ],     },     externals: [         nodeExternals()     ],     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 = {     entry: `${__dirname}/server/server.js`,     output: {         path: `${__dirname}/dist/`,         filename: "server.bundle.js",     },     target: "node",     node: {         __filename: true,         __dirname: true,     },     resolve: {         extensions: [             ".js",             ".jsx",         ],         modules: [             "client",             "node_modules",         ],     },     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. I am also getting an error thrown from Babel whenever I try to start my project:

SyntaxError: E:/Documents/Projects/website/client/styles/Main.scss: Unexpected token (1:0) > 1 | .header {     | ^   2 |   background-color: #002933;   3 | }   4 |     at Parser.pp$5.raise (E:\Documents\Projects\website\node_modules\babel-core\node_modules\babylon\lib\index.js:4454:13)     at Parser.pp.unexpected (E:\Documents\Projects\website\node_modules\babel-core\node_modules\babylon\lib\index.js:1761:8)     at Parser.pp$3.parseExprAtom (E:\Documents\Projects\website\node_modules\babel-core\node_modules\babylon\lib\index.js:3750:12)     at Parser.parseExprAtom (E:\Documents\Projects\website\node_modules\babel-core\node_modules\babylon\lib\index.js:7238:22)     at Parser.pp$3.parseExprSubscripts (E:\Documents\Projects\website\node_modules\babel-core\node_modules\babylon\lib\index.js:3494:19)     at Parser.pp$3.parseMaybeUnary (E:\Documents\Projects\website\node_modules\babel-core\node_modules\babylon\lib\index.js:3474:19)     at Parser.pp$3.parseExprOps (E:\Documents\Projects\website\node_modules\babel-core\node_modules\babylon\lib\index.js:3404:19)     at Parser.pp$3.parseMaybeConditional (E:\Documents\Projects\website\node_modules\babel-core\node_modules\babylon\lib\index.js:3381:19)     at Parser.pp$3.parseMaybeAssign (E:\Documents\Projects\website\node_modules\babel-core\node_modules\babylon\lib\index.js:3344:19)     at Parser.parseMaybeAssign (E:\Documents\Projects\website\node_modules\babel-core\node_modules\babylon\lib\index.js:6474:20)     at Parser.pp$3.parseExpression (E:\Documents\Projects\website\node_modules\babel-core\node_modules\babylon\lib\index.js:3306:19)     at Parser.pp$1.parseStatement (E:\Documents\Projects\website\node_modules\babel-core\node_modules\babylon\lib\index.js:1906:19)     at Parser.parseStatement (E:\Documents\Projects\website\node_modules\babel-core\node_modules\babylon\lib\index.js:5910:22)     at Parser.pp$1.parseBlockBody (E:\Documents\Projects\website\node_modules\babel-core\node_modules\babylon\lib\index.js:2268:21)     at Parser.pp$1.parseTopLevel (E:\Documents\Projects\website\node_modules\babel-core\node_modules\babylon\lib\index.js:1778:8)     at Parser.parse (E:\Documents\Projects\website\node_modules\babel-core\node_modules\babylon\lib\index.js:1673:17) 

Whilst playing around with this issue, I have not yet encountered that error. 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"             ]         }     } } 

2 Answers

Answers 1

I think you have an error in your webpack.config json hierarchy for you css/sass loaders. Replace your "loaders" array under module with this "rules" array:

module: {     rules: [         {             test: /\.scss$/,             use: ExtractTextPlugin.extract({                 fallback: "style-loader",                 use: ['css-loader', 'sass-loader']             }),         },         {             test: /\.css$/,             use: ExtractTextPlugin.extract({                 fallback: "style-loader",                 use: "css-loader"             })         },     ], }, 

You can see more examples of ExtractTextPlugin usage here: https://github.com/webpack-contrib/extract-text-webpack-plugin#usage

Answers 2

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.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment