2

I'm trying to create a simple webpack 2 setup for experimenting with BlueprintJS and TypeScript. I think I'm close, but CSS is not loading properly.

Here is the directory structure for this project

  • src/
    • index.tsx
    • App.tsx
    • App.css
  • index.html
  • package.json
  • tsconfig.json
  • webpack.config.js

App.tsx currently contains a single Button component that should show a refresh icon. When I run npm run build, webpack indicates that the styles were loaded. When I run npm run start and view the app, however, the button has no icon and no other CSS styling.

I must be doing something wrong. Can anyone spot it? The source for all files is included below. Bonus points if you can figure out why the webpack-dev-server will not recompile when the Button text in App.tsx is changed. Other suggestions are welcome.

index.tsx

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import App from './App';

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

App.tsx

import * as React from "react";
import { Button } from "@blueprintjs/core";
import './App.css';

export class App extends React.Component<{}, {}> {
     public render(): JSX.Element {
        return (
                <Button iconName="refresh" text="Refresh"/>
        );
    }
}
export default App;

App.css

@import "@blueprintjs/core/dist/blueprint.css";

index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8" />
    <title>BlueprintJS Test</title>
</head>
<body>
    <div id="root"></div>
    <script src="./node_modules/react/dist/react.js"></script>
    <script src="./node_modules/react-dom/dist/react-dom.js"></script>
    <script type="text/javascript" src="./dist/bundle.js"></script>
</body>
</html>

package.json

{
  "name": "blueprintplay",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "build": "webpack",
    "start": "webpack-dev-server"
  },
  "dependencies": {
    "@blueprintjs/core": "^1.14.0",
    "classnames": "^2.2.5",
    "react": "^15.4.2",
    "react-addons-css-transition-group": "^15.4.2",
    "react-dom": "^15.4.2"
  },
  "devDependencies": {
    "@types/classnames": "^0.0.32",
    "@types/pure-render-decorator": "^0.2.27",
    "@types/react": "^15.0.18",
    "@types/react-addons-css-transition-group": "^15.0.1",
    "@types/react-dom": "^0.14.23",
    "case-sensitive-paths-webpack-plugin": "^2.0.0",
    "css-loader": "^0.28.0",
    "file-loader": "^0.11.1",
    "source-map-loader": "^0.2.0",
    "style-loader": "^0.16.1",
    "ts-loader": "^2.0.3",
    "typescript": "^2.2.2",
    "url-loader": "^0.5.8",
    "webpack": "^2.4.1",
    "webpack-dev-server": "^2.4.2"
  }
}

tsconfig.json

{
  "compilerOptions": {
    "outDir": "./dist/",
    "sourceMap": true,
    "module": "commonjs",
    "target": "es5",
    "jsx": "react"
  },
  "include": [
    "src/**/*"
  ],
  "exclude": [
    "node_modules",
    "**/*.spec.ts"      
  ]
}

webpack.config.js

const path = require('path');

module.exports = {
  entry: [
    './src/index.tsx'
  ],
  output: {
    filename: 'bundle.js',
    path: __dirname + "/dist"
  },
  module: {
    rules: [
      {
        enforce: 'pre',
        test: /\.js$/,
        loader: "source-map-loader"
      },
      {
        test: /\.tsx?$/,
        use: "ts-loader"
      },
     {
        test: /\.css$/,
        use: [
          {
            loader: 'style-loader',
          },
          {
            loader: 'css-loader',
            options: {
              modules: true,
            },
          },
        ],
      },
      {
        test: /\.(woff|woff2)$/,
        use: {
          loader: 'url-loader',
          options: {
            name: 'fonts/[hash].[ext]',
            limit: 5000,
            mimetype: 'application/font-woff'
          }
        }
      }, 
      {
        test: /\.(ttf|eot|svg)$/,
        use: {
          loader: 'file-loader',
          options: {
            name: 'fonts/[hash].[ext]'
          }
        }
      },
    ]
  },
  resolve: {
    extensions: [".tsx", ".ts", ".js"]
  },
  devtool: 'source-map',
  devServer: {
    host: process.env.HOST, // Defaults to `localhost`
    port: process.env.PORT, // Defaults to 8080
    hot: true,
  }, 
  externals: {
    "react": "React",
    "react-dom": "ReactDOM"
  },
  stats: "verbose",
};

1 Answers1

5

You're using CSS modules which changes every class to a local identifier, and you would need to reference the correct identifier. But since it's a library that sets the classes on the element, for example on the button you're using, it won't work with CSS modules, so you need to turn them off in the css-loader. You can just remove the option because by default they are turned off.

{
  test: /\.css$/,
  use: [
    {
      loader: 'style-loader',
    },
    {
      loader: 'css-loader',
    },
  ],
},

With CSS modules disabled you also need to change the import in you CSS because it is treated as a relative path. Webpack allows you to start the path with a ~ to indicate that it should be resolved as a module instead of a relative path.

@import "~@blueprintjs/core/dist/blueprint.css";
Michael Jungo
  • 31,583
  • 3
  • 91
  • 84