Skip to content
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

feat: support css #206

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
16 changes: 16 additions & 0 deletions examples/react-component-bundle-false/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "@examples/react-component-bundle-false",
"private": true,
"scripts": {
"build": "rslib build"
},
"devDependencies": {
"@rsbuild/plugin-react": "1.0.1-rc.4",
"@rslib/core": "workspace:*",
"@types/react": "^18.3.5",
"react": "^18.3.1"
},
"peerDependencies": {
"react": "*"
}
}
53 changes: 53 additions & 0 deletions examples/react-component-bundle-false/rslib.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { pluginReact } from '@rsbuild/plugin-react';
import { type LibConfig, defineConfig } from '@rslib/core';

const shared: LibConfig = {
bundle: false,
};

export default defineConfig({
source: {
entry: {
index: ['./src/**', '!./src/env.d.ts'],
},
},
lib: [
{
...shared,
format: 'esm',
output: {
distPath: {
root: './dist/esm',
css: '.',
cssAsync: '.',
},
},
bundle: false,
},
// {
// ...shared,
// format: 'cjs',
// output: {
// distPath: {
// root: './dist/cjs',
// css: '.',
// cssAsync: '.',
// },
// },
// bundle: false,
// },
],
// tools: {
// bundlerChain(config, {CHAIN_ID}) {
// const cssExtract = CHAIN_ID.PLUGIN.MINI_CSS_EXTRACT
// config.plugins.delete(cssExtract)

// },
// cssExtract: {
// loaderOptions: {
// rootDir: './src',
// },
// },
// },
plugins: [pluginReact()],
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.button {
background: blue;
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import styles from './CounterButton.module.css';
interface CounterButtonProps {
onClick: () => void;
label: string;
Expand All @@ -7,7 +8,7 @@ export const CounterButton: React.FC<CounterButtonProps> = ({
onClick,
label,
}) => (
<button type="button" onClick={onClick}>
<button type="button" className={styles.button} onClick={onClick}>
{label}
</button>
);
4 changes: 4 additions & 0 deletions examples/react-component-bundle-false/src/env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
declare module '*.module.css' {
const classes: { [key: string]: string };
export default classes;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { CounterButton } from './CounterButton';
import { useCounter } from './useCounter';
import './reset.css';

export const Counter: React.FC = () => {
const { count, increment, decrement } = useCounter();
Expand Down
3 changes: 3 additions & 0 deletions examples/react-component-bundle-false/src/reset.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.body {
background: red;
}
7 changes: 7 additions & 0 deletions examples/react-component-bundle-false/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"compilerOptions": {
"jsx": "react-jsx",
"strict": true
},
"include": ["src/**/*"]
}
3 changes: 3 additions & 0 deletions examples/react-component-bundle/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# @examples/react-component

This example demonstrates how to use Rslib to build a simple React component.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@examples/react-component",
"name": "@examples/react-component-bundle",
"private": true,
"scripts": {
"build": "rslib build"
Expand Down
3 changes: 3 additions & 0 deletions examples/react-component-bundle/src/CounterButton.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.button {
background: blue;
}
14 changes: 14 additions & 0 deletions examples/react-component-bundle/src/CounterButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import styles from './CounterButton.module.css';
interface CounterButtonProps {
onClick: () => void;
label: string;
}

export const CounterButton: React.FC<CounterButtonProps> = ({
onClick,
label,
}) => (
<button type="button" className={styles.button} onClick={onClick}>
{label}
</button>
);
4 changes: 4 additions & 0 deletions examples/react-component-bundle/src/env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
declare module '*.module.css' {
const classes: { [key: string]: string };
export default classes;
}
15 changes: 15 additions & 0 deletions examples/react-component-bundle/src/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { CounterButton } from './CounterButton';
import { useCounter } from './useCounter';
import './reset.css';

export const Counter: React.FC = () => {
const { count, increment, decrement } = useCounter();

return (
<div>
<h2>Counter: {count}</h2>
<CounterButton onClick={decrement} label="-" />
<CounterButton onClick={increment} label="+" />
</div>
);
};
3 changes: 3 additions & 0 deletions examples/react-component-bundle/src/reset.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.body {
background: red;
}
10 changes: 10 additions & 0 deletions examples/react-component-bundle/src/useCounter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { useState } from 'react';

export const useCounter = (initialValue = 0) => {
const [count, setCount] = useState(initialValue);

const increment = () => setCount((prev) => prev + 1);
const decrement = () => setCount((prev) => prev - 1);

return { count, increment, decrement };
};
1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
},
"dependencies": {
"@rsbuild/core": "1.0.4",
"@rslib/lib-css-extract-loader": "workspace:*",
"rsbuild-plugin-dts": "workspace:*"
},
"devDependencies": {
Expand Down
52 changes: 50 additions & 2 deletions packages/core/src/config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import fs from 'node:fs';
import { createRequire } from 'node:module';
import path, { dirname, extname, isAbsolute, join } from 'node:path';
import {
type RsbuildConfig,
Expand All @@ -9,6 +10,7 @@ import {
mergeRsbuildConfig,
rspack,
} from '@rsbuild/core';
import type { RsbuildPlugin } from '@rsbuild/core';
import glob from 'fast-glob';
import {
DEFAULT_CONFIG_NAME,
Expand Down Expand Up @@ -47,6 +49,8 @@ import {
} from './utils/syntax';
import { loadTsconfig } from './utils/tsconfig';

const require = createRequire(import.meta.url);

/**
* This function helps you to autocomplete configuration types.
* It accepts a Rslib config object, or a function that returns a config.
Expand Down Expand Up @@ -605,6 +609,41 @@ const composeSyntaxConfig = (
};
};

const pluginName = 'rsbuild:lib-css';

const pluginLibCss = (rootDir: string): RsbuildPlugin => ({
name: pluginName,
setup(api) {
api.modifyBundlerChain((config, { CHAIN_ID }) => {
const cssExtract = CHAIN_ID.PLUGIN.MINI_CSS_EXTRACT;
config.plugins.delete(cssExtract);

for (const ruleId of [
CHAIN_ID.RULE.CSS,
CHAIN_ID.RULE.SASS,
CHAIN_ID.RULE.LESS,
CHAIN_ID.RULE.STYLUS,
]) {
const rule = config.module.rule(ruleId);
if (rule.uses.has(CHAIN_ID.USE.MINI_CSS_EXTRACT)) {
rule
.use(CHAIN_ID.USE.MINI_CSS_EXTRACT)
.loader(require.resolve('@rslib/lib-css-extract-loader'))
.options({
rootDir,
});
}
}
});
},
});

const composeCssConfig = (rootDir: string): RsbuildConfig => {
return {
plugins: [pluginLibCss(rootDir)],
};
};

const composeEntryConfig = async (
entries: NonNullable<RsbuildConfig['source']>['entry'],
bundle: LibConfig['bundle'],
Expand Down Expand Up @@ -665,11 +704,16 @@ const composeEntryConfig = async (
}
}

return {
const lcp = await calcLongestCommonPath(Object.values(resolvedEntries));
const cssConfig = composeCssConfig(lcp === null ? root : lcp);

const entryConfig: RsbuildConfig = {
source: {
entry: resolvedEntries,
},
};

return mergeRsbuildConfig(entryConfig, cssConfig);
};

const composeBundleConfig = (
Expand All @@ -691,8 +735,12 @@ const composeBundleConfig = (
// If data.request already have an extension, we replace it with new extension
// This may result in a change in semantics,
// user should use copy to keep origin file or use another separate entry to deal this
let request = data.request;
let request: string = data.request;
if (request[0] === '.') {
if (request.includes('compiled/css-loader')) {
return callback();
}

request = extname(request)
? request.replace(/\.[^.]+$/, jsExtension)
: `${request}${jsExtension}`;
Expand Down
8 changes: 8 additions & 0 deletions packages/core/src/types/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,19 @@ export type BannerAndFooter = {
dts?: string;
};

// export type Redirect = {
// alias?: boolean;
// style?: boolean;
// asset?: boolean;
// autoExtension?: boolean;
// };

export interface LibConfig extends RsbuildConfig {
bundle?: boolean;
format?: Format;
autoExtension?: boolean;
autoExternal?: AutoExternal;
// redirect?: Redirect;
/** Support esX and browserslist query */
syntax?: Syntax;
externalHelpers?: boolean;
Expand Down
21 changes: 21 additions & 0 deletions packages/lib-css-extract-loader/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2024-present Bytedance, Inc. and its affiliates.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
19 changes: 19 additions & 0 deletions packages/lib-css-extract-loader/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<picture>
<img alt="Rslib Banner" src="https://assets.rspack.dev/rslib/rslib-banner.png">
</picture>

# Rslib

Rslib is a library build tool powered by [Rsbuild](https://rsbuild.dev). It allows library developers to leverage the knowledge and ecosystem of webpack and Rspack.

<!-- ## Documentation
https://rslib.dev/ -->

## Contributing

Please read the [Contributing Guide](https://github.com/web-infra-dev/rslib/blob/main/CONTRIBUTING.md).

## License

Rslib is [MIT licensed](https://github.com/web-infra-dev/rslib/blob/main/LICENSE).
Loading
Loading