Skip to content

ctco-dev: add CSS Modules, add css sourcemaps for dev, add sass support #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ If something doesn’t work, please [file an issue](https://github.com/ctco-dev/
* [TSLint Config Airbnb](https://github.com/progre/tslint-config-airbnb) - a tslint config for [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript)
* [12 Factor Application Configuration](https://12factor.net/config) approach to profile application in runtime. [Details](https://github.com/ctco-dev/create-react-app/blob/master/packages/react-scripts/template/README.md#12-factor-app-config)
* :whale: [Docker](https://www.docker.com/) support (plus 12 Factor configuration in runtime, see above)
* [CSS Modules](https://github.com/css-modules/css-modules) - optional
* [typings-for-css-modules-loader](https://github.com/Jimdo/typings-for-css-modules-loader) - lightweight `css-loader` replacement to enable type generation for css module files
* [Browserslist](https://github.com/browserslist/browserslist)
* [SASS/SCSS](https://sass-lang.com/) - optional
* Source maps for CSS enabled in dev mode
* [Classnames](https://github.com/JedWatson/classnames) - for conditionally joining classNames together

## Quick Overview

Expand Down
115 changes: 85 additions & 30 deletions packages/react-scripts/config/webpack.config.dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,60 @@ const publicUrl = '';
// Get environment variables to inject into our app.
const env = getClientEnvironment(publicUrl);

// style files regexes
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;
const localIdentName = '[name]__[local]___[hash:base64:8]';

// common function to get style loaders
const getStyleLoaders = (cssOptions, preProcessor, preProcessorOptions) => {
const loaders = [
{
loader: require.resolve('style-loader'),
options: { sourceMap: true },
},
// this one is used instead of css-loader to allow css modules inside typescript
{
loader: require.resolve('typings-for-css-modules-loader'),
options: Object.assign({},
{
sourceMap: true,
silent: true, // typings-for-css-modules-loader option
namedExport: true, // typings-for-css-modules-loader option
camelCase: true, // typings-for-css-modules-loader option
},
cssOptions)
},
{
// Options for PostCSS as we reference these options twice
// Adds vendor prefixing based on your specified browser support in
// package.json
loader: require.resolve('postcss-loader'),
options: {
// Necessary for external CSS imports to work
// https://github.com/facebook/create-react-app/issues/2677
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
autoprefixer({
flexbox: 'no-2009',
}),
],
sourceMap: true,
},
},
];
if (preProcessor) {
loaders.push({
loader: require.resolve(preProcessor),
options: Object.assign({}, { sourceMap: true }, preProcessorOptions),
});
}
return loaders;
};

// This is the development configuration.
// It is focused on developer experience and fast rebuilds.
// The production configuration is different and lives in a separate file.
Expand Down Expand Up @@ -210,37 +264,33 @@ module.exports = {
// "style" loader turns CSS into JS modules that inject <style> tags.
// In production, we use a plugin to extract that CSS to a file, but
// in development "style" loader enables hot editing of CSS.
// By default we support CSS Modules with the extension .module.css
{
test: /\.css$/,
use: [
require.resolve('style-loader'),
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
},
},
{
loader: require.resolve('postcss-loader'),
options: {
// Necessary for external CSS imports to work
// https://github.com/facebookincubator/create-react-app/issues/2677
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
autoprefixer({
browsers: [
'>1%',
'last 4 versions',
'Firefox ESR',
'not ie < 9', // React doesn't support IE8 anyway
],
flexbox: 'no-2009',
}),
],
},
},
],
test: cssRegex,
exclude: cssModuleRegex,
use: getStyleLoaders({ importLoaders: 1 }),
},
// Adds support for CSS Modules (https://github.com/css-modules/css-modules)
// using the extension .module.css
{
test: cssModuleRegex,
use: getStyleLoaders({ importLoaders: 1, modules: true, localIdentName }),
},
// Opt-in support for SASS (using .scss or .sass extensions).
// Chains the sass-loader with the css-loader and the style-loader
// to immediately apply all styles to the DOM.
// By default we support SASS Modules with the
// extensions .module.scss or .module.sass
{
test: sassRegex,
exclude: sassModuleRegex,
use: getStyleLoaders({ importLoaders: 2 }, 'sass-loader'),
},
// Adds support for CSS Modules, but using SASS
// using the extension .module.scss or .module.sass
{
test: sassModuleRegex,
use: getStyleLoaders({ importLoaders: 2, modules: true, localIdentName }, 'sass-loader'),
},
// "file" loader makes sure those assets get served by WebpackDevServer.
// When you `import` an asset, you get its (virtual) filename.
Expand Down Expand Up @@ -277,6 +327,11 @@ module.exports = {
}),
// inject env vars, filtered by mask WEB_APP_, into window.env
new HtmlWebpackInjectEnvPlugin(),
// ignore generated type files for CSS Modules,
// see https://github.com/Jimdo/typings-for-css-modules-loader#webpack-rebuilds--builds-slow
// new webpack.WatchIgnorePlugin([
// /\.(css|sass|scss)\.d\.ts$/
// ]),
// Add module names to factory functions so they appear in browser profiler.
new webpack.NamedModulesPlugin(),
// Makes some environment variables available to the JS code, for example:
Expand Down
141 changes: 103 additions & 38 deletions packages/react-scripts/config/webpack.config.prod.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,64 @@ const publicUrl = publicPath.slice(0, -1);
// Get environment variables to inject into our app.
const env = getClientEnvironment(publicUrl);

// style files regexes
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;
const localIdentName = '__[hash:base64:8]';

// common function to get style loaders
const getStyleLoaders = (cssOptions, preProcessor, preProcessorOptions) => {
const loaders = [
// this one is used instead of css-loader to allow css modules inside typescript
{
loader: require.resolve('typings-for-css-modules-loader'),
options: Object.assign({},
{
minimize: true,
sourceMap: shouldUseSourceMap,
silent: true, // typings-for-css-modules-loader option
namedExport: true, // typings-for-css-modules-loader option
camelCase: true, // typings-for-css-modules-loader option
},
cssOptions)
},
{
// Options for PostCSS as we reference these options twice
// Adds vendor prefixing based on your specified browser support in
// package.json
loader: require.resolve('postcss-loader'),
options: {
// Necessary for external CSS imports to work
// https://github.com/facebook/create-react-app/issues/2677
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
autoprefixer({
flexbox: 'no-2009',
}),
],
sourceMap: shouldUseSourceMap,
},
},
];
if (preProcessor) {
loaders.push({
loader: require.resolve(preProcessor),
options: Object.assign({}, { sourceMap: shouldUseSourceMap }, preProcessorOptions),
});
}
return loaders;
};

const fallbackStyleLoader = {
loader: require.resolve('style-loader'),
options: {
hmr: false,
},
};

// Assert this just to be safe.
// Development builds of React are slow and not intended for production.
if (env.stringified['process.env'].NODE_ENV !== '"production"') {
Expand Down Expand Up @@ -220,52 +278,59 @@ module.exports = {
// use the "style" loader inside the async code so CSS from them won't be
// in the main CSS file.
{
test: /\.css$/,
test: cssRegex,
exclude: cssModuleRegex,
loader: ExtractTextPlugin.extract(
Object.assign(
{
fallback: {
loader: require.resolve('style-loader'),
options: {
hmr: false,
},
},
use: [
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
minimize: true,
sourceMap: shouldUseSourceMap,
},
},
{
loader: require.resolve('postcss-loader'),
options: {
// Necessary for external CSS imports to work
// https://github.com/facebookincubator/create-react-app/issues/2677
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
autoprefixer({
browsers: [
'>1%',
'last 4 versions',
'Firefox ESR',
'not ie < 9', // React doesn't support IE8 anyway
],
flexbox: 'no-2009',
}),
],
},
},
],
{
fallback: fallbackStyleLoader,
use: getStyleLoaders({ importLoaders: 1 }),
},
extractTextPluginOptions
)
),
// Note: this won't work without `new ExtractTextPlugin()` in `plugins`.
},
{
// Adds support for CSS Modules (https://github.com/css-modules/css-modules)
// using the extension .module.css
test: cssModuleRegex,
loader: ExtractTextPlugin.extract(
Object.assign(
{ use: getStyleLoaders({ importLoaders: 1, modules: true, localIdentName }) },
extractTextPluginOptions
)
),
// Note: this won't work without `new ExtractTextPlugin()` in `plugins`.
},
{
// Opt-in support for SASS (using .scss or .sass extensions).
// Chains the sass-loader with the css-loader and the style-loader
// to immediately apply all styles to the DOM.
// By default we support SASS Modules with the
// extensions .module.scss or .module.sass
test: sassRegex,
exclude: sassModuleRegex,
loader: ExtractTextPlugin.extract(
Object.assign(
{ use: getStyleLoaders({ importLoaders: 2 }, 'sass-loader') },
extractTextPluginOptions
)
),
// Note: this won't work without `new ExtractTextPlugin()` in `plugins`.
},
{
// Adds support for CSS Modules, but using SASS
// using the extension .module.scss or .module.sass
test: sassModuleRegex,
loader: ExtractTextPlugin.extract(
Object.assign(
{ use: getStyleLoaders({ importLoaders: 2, modules: true, localIdentName }, 'sass-loader') },
extractTextPluginOptions
)
),
// Note: this won't work without `new ExtractTextPlugin()` in `plugins`.
},
// "file" loader makes sure assets end up in the `build` folder.
// When you `import` an asset, you get its filename.
// This loader doesn't use a "test" so it will catch all modules
Expand Down
3 changes: 3 additions & 0 deletions packages/react-scripts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"raf": "3.4.0",
"react-dev-utils": "^5.0.1",
"resolve": "1.6.0",
"sass-loader": "7.1.0",
"style-loader": "0.19.0",
"sw-precache-webpack-plugin": "0.11.4",
"ts-jest": "23.1.3",
Expand All @@ -62,13 +63,15 @@
"tslint-config-airbnb": "5.9.2",
"tslint-config-prettier": "1.14.0",
"tslint-react": "3.6.0",
"typings-for-css-modules-loader": "1.7.0",
"url-loader": "0.6.2",
"webpack": "3.8.1",
"webpack-dev-server": "2.9.4",
"webpack-manifest-plugin": "1.3.2",
"whatwg-fetch": "2.0.3"
},
"devDependencies": {
"classnames": "^2.2.6",
"react": "^16.0.0",
"react-dom": "^16.0.0",
"typescript": "^3.0.1"
Expand Down
2 changes: 1 addition & 1 deletion packages/react-scripts/scripts/utils/dependencies.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
'use strict';

module.exports = ['react', 'react-dom'];
module.exports = ['react', 'react-dom', 'classnames'];
10 changes: 10 additions & 0 deletions packages/react-scripts/template/.browserslistrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[production]
>1%
last 4 versions
Firefox ESR
not ie < 11

[development]
last 2 chrome versions
last 2 firefox versions
last 2 edge versions
20 changes: 19 additions & 1 deletion packages/react-scripts/template/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ You can find the official CRA docs [here](https://github.com/facebookincubator/c
* [How it differes from the official CRA](https://github.com/ctco-dev/create-react-app/blob/master/README.md#how-it-differs-from-the-official-cra)
* [Common tasks](#common-tasks)
* [Use docker](#use-docker)
* [12 Factor app config](#12-factor-app-config) - [12 Factor](https://12factor.net/config) approach to maintain configuration.
* [12 Factor app config](#12-factor-app-config) - [12 Factor](https://12factor.net/config) approach to maintain configuration
* [CSS Modules](#css-modules)
* [Browserslist](#browserslist) - autoprefixer configuration
* [SASS/SCSS](#sass/scss)

## Common tasks

Expand Down Expand Up @@ -64,3 +67,18 @@ To inject environment variables into an application on starting a docker contain
```sh
docker run --rm -it --name my-cra-container -e "WEB_APP_ONE=hello my app" -p 8080:80 my-cra-image
```

## CSS Modules

[CSS Modules](https://github.com/css-modules/css-modules) can be used to define CSS styles. The file for CSS Modules should be named with `.module.css` suffix. To make typescript understand typings for the CSS Module file, they are generated automatically, when `yarn start` is running. The typings file is named the same as `*.module.css` file plus `.d.ts` suffix.

## Browserslist

[Browserslist](https://github.com/browserslist/browserslist) is used to configure [autoprefixer](https://github.com/postcss/autoprefixer). See official [docs](https://github.com/browserslist/browserslist).

## SASS/SCSS

Just add it to the project. CSS Modules work with sass files as well.
```sh
yarn add sass
```
4 changes: 4 additions & 0 deletions packages/react-scripts/template/src/App.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.header-subtitle {
font-size: .9em;
text-shadow: 0 0 5px lightblue;
}
1 change: 1 addition & 0 deletions packages/react-scripts/template/src/App.module.css.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const headerSubtitle: string;
Loading