Skip to content

Commit

Permalink
feat: add cssModules.mode option (#2459)
Browse files Browse the repository at this point in the history
  • Loading branch information
chenjiahan authored May 28, 2024
1 parent a5ba0f4 commit 18f603e
Show file tree
Hide file tree
Showing 7 changed files with 220 additions and 99 deletions.
23 changes: 23 additions & 0 deletions e2e/cases/css/css-modules-mode/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { build } from '@e2e/helper';
import { expect, test } from '@playwright/test';

test('should compile CSS Modules global mode correctly', async () => {
const rsbuild = await build({
cwd: __dirname,
rsbuildConfig: {
output: {
cssModules: {
mode: 'global',
},
},
},
});
const files = await rsbuild.unwrapOutputJSON();

const content =
files[Object.keys(files).find((file) => file.endsWith('.css'))!];

expect(content).toEqual(
'.foo{position:relative}.foo .bar,.foo .baz{height:100%;overflow:hidden}.foo .lol{width:80%}',
);
});
13 changes: 13 additions & 0 deletions e2e/cases/css/css-modules-mode/src/a.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.foo {
position: relative;
}

.foo .bar,
.foo .baz {
height: 100%;
overflow: hidden;
}

.foo .lol {
width: 80%;
}
1 change: 1 addition & 0 deletions e2e/cases/css/css-modules-mode/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import './a.module.css';
6 changes: 2 additions & 4 deletions packages/core/src/plugins/css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,11 +219,9 @@ const getCSSLoaderOptions = ({
const defaultOptions: CSSLoaderOptions = {
importLoaders,
modules: {
auto: cssModules.auto,
namedExport: false,
exportGlobals: cssModules.exportGlobals,
exportLocalsConvention: cssModules.exportLocalsConvention,
...cssModules,
localIdentName,
namedExport: false,
},
sourceMap: config.output.sourceMap.css,
};
Expand Down
22 changes: 10 additions & 12 deletions packages/shared/src/types/config/output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type {
Externals,
SwcJsMinimizerRspackPluginOptions,
} from '@rspack/core';
import type { HTMLPluginOptions } from '../../types';
import type { CSSLoaderModulesOptions, HTMLPluginOptions } from '../../types';
import type { RsbuildTarget } from '../rsbuild';
import type { RspackConfig } from '../rspack';

Expand Down Expand Up @@ -147,26 +147,23 @@ export type CSSModules = {
/**
* Allows CSS Modules to be automatically enabled based on their filenames.
*/
auto?:
| boolean
| RegExp
| ((
resourcePath: string,
resourceQuery: string,
resourceFragment: string,
) => boolean);
auto?: CSSLoaderModulesOptions['auto'];
/**
* Allows exporting names from global class names, so you can use them via import.
*/
exportGlobals?: boolean;
/**
* Style of exported class names.
*/
exportLocalsConvention?: CSSModulesLocalsConvention;
/**
* Set the local ident name of CSS Modules.
*/
localIdentName?: string;
/**
* Style of exported class names.
* Controls the level of compilation applied to the input styles.
*/
exportLocalsConvention?: CSSModulesLocalsConvention;
mode?: CSSLoaderModulesOptions['mode'];
};

export type Minify =
Expand Down Expand Up @@ -329,9 +326,10 @@ export interface NormalizedOutputConfig extends OutputConfig {
injectStyles: boolean;
cssModules: {
auto: CSSModules['auto'];
localIdentName?: string;
exportGlobals: boolean;
exportLocalsConvention: CSSModulesLocalsConvention;
localIdentName?: string;
mode?: CSSModules['mode'];
};
emitAssets: EmitAssets;
}
120 changes: 81 additions & 39 deletions website/docs/en/config/output/css-modules.mdx
Original file line number Diff line number Diff line change
@@ -1,21 +1,6 @@
# output.cssModules

- **Type:**

```ts
type CSSModules = {
auto?:
| boolean
| RegExp
| ((
resourcePath: string,
resourceQuery: string,
resourceFragment: string,
) => boolean);
localIdentName?: string;
exportLocalsConvention?: CSSModulesLocalsConvention;
};
```
- **Type:** `CSSModules`

For custom CSS Modules configuration.

Expand Down Expand Up @@ -94,6 +79,48 @@ export default {
};
```

## cssModules.exportGlobals

- **Type:** `boolean`
- **Default:** `false`

Allows exporting names from global class names, so you can use them via import.

### Example

Set the `exportGlobals` to `true`:

```ts
export default {
output: {
cssModules: {
exportGlobals: true,
},
},
};
```

Use `:global()` in CSS Modules:

```css title="style.module.css"
:global(.blue) {
color: blue;
}

.red {
color: red;
}
```

Then you can import the class name wrapped with `:global()`:

```tsx title="Button.tsx"
import styles from './style.module.css';

console.log(styles.blue); // 'blue'
console.log(styles.red); // 'red-[hash]'
```

## cssModules.localIdentName

- **Type:** `string`
Expand Down Expand Up @@ -149,44 +176,59 @@ export default {
};
```

## cssModules.exportGlobals
## cssModules.mode

- **Type:** `boolean`
- **Default:** `false`
- **Type:**

Allows exporting names from global class names, so you can use them via import.
```ts
type Mode =
| 'local'
| 'global'
| 'pure'
| 'icss'
| ((resourcePath: string) => 'local' | 'global' | 'pure' | 'icss');
```

### Example
- **Default:** `'local'`

Set the `exportGlobals` to `true`:
Controls the mode of compilation applied to the CSS Modules.

### Optional values

`cssModules.mode` can take one of the following values:

1. `'local'` (default): This enables the CSS Modules specification for scoping CSS locally. Class and ID selectors are rewritten to be module-scoped, and `@value` bindings are injected.
2. `'global'`: This opts-out of the CSS Modules behavior, disabling both local scoping and injecting `@value` bindings. Global selectors are preserved as-is.
3. `'pure'`: This enables dead-code elimination by removing any unused local classnames and values from the final CSS. It still performs local scoping and `@value` injection.
4. `'icss'`: This compiles to the low-level Interoperable CSS format, which provides a syntax for declaring `:import` and `:export` dependencies between CSS and other languages. It does not perform any scoping or `@value` injection.

The `'local'` mode is the most common use case for CSS Modules, enabling modular and locally-scoped styles within components. The other modes may be used in specific scenarios.

For example:

```ts
export default {
output: {
cssModules: {
exportGlobals: true,
mode: 'global',
},
},
};
```

Use `:global()` in CSS Modules:
### Function

```css title="style.module.css"
:global(.blue) {
color: blue;
}
You can also pass a function to `modules.mode` that determines the mode based on the resource path, query, or fragment. This allows you to use different modes for different files.

.red {
color: red;
}
```

Then you can import the class name wrapped with `:global()`:
For example, to use local scoping for `.module.css` files and global styles for other files:

```tsx title="Button.tsx"
import styles from './style.module.css';

console.log(styles.blue); // 'blue'
console.log(styles.red); // 'red-[hash]'
```js
modules: {
mode: (resourcePath) => {
if (/\.module\.\css$/.test(resourcePath)) {
return 'local';
}
return 'global';
};
}
```
Loading

0 comments on commit 18f603e

Please sign in to comment.