diff --git a/packages/create-rslib/fragments/tools/rstest-node-js/package.json b/packages/create-rslib/fragments/tools/rstest-node-js/package.json
new file mode 100644
index 000000000..f72d6b688
--- /dev/null
+++ b/packages/create-rslib/fragments/tools/rstest-node-js/package.json
@@ -0,0 +1,8 @@
+{
+ "scripts": {
+ "test": "rstest"
+ },
+ "devDependencies": {
+ "@rstest/core": "^0.1.3"
+ }
+}
diff --git a/packages/create-rslib/fragments/tools/rstest-node-js/rstest.config.js b/packages/create-rslib/fragments/tools/rstest-node-js/rstest.config.js
new file mode 100644
index 000000000..9ee3cbabc
--- /dev/null
+++ b/packages/create-rslib/fragments/tools/rstest-node-js/rstest.config.js
@@ -0,0 +1,3 @@
+import { defineConfig } from '@rstest/core';
+
+export default defineConfig({});
diff --git a/packages/create-rslib/fragments/tools/rstest-node-js/tests/index.test.js b/packages/create-rslib/fragments/tools/rstest-node-js/tests/index.test.js
new file mode 100644
index 000000000..d87ea6400
--- /dev/null
+++ b/packages/create-rslib/fragments/tools/rstest-node-js/tests/index.test.js
@@ -0,0 +1,7 @@
+import { expect, test } from '@rstest/core';
+import { squared } from '../src/index.js';
+
+test('squared', () => {
+ expect(squared(2)).toBe(4);
+ expect(squared(12)).toBe(144);
+});
diff --git a/packages/create-rslib/fragments/tools/rstest-node-ts/package.json b/packages/create-rslib/fragments/tools/rstest-node-ts/package.json
new file mode 100644
index 000000000..f72d6b688
--- /dev/null
+++ b/packages/create-rslib/fragments/tools/rstest-node-ts/package.json
@@ -0,0 +1,8 @@
+{
+ "scripts": {
+ "test": "rstest"
+ },
+ "devDependencies": {
+ "@rstest/core": "^0.1.3"
+ }
+}
diff --git a/packages/create-rslib/fragments/tools/rstest-node-ts/rstest.config.ts b/packages/create-rslib/fragments/tools/rstest-node-ts/rstest.config.ts
new file mode 100644
index 000000000..9ee3cbabc
--- /dev/null
+++ b/packages/create-rslib/fragments/tools/rstest-node-ts/rstest.config.ts
@@ -0,0 +1,3 @@
+import { defineConfig } from '@rstest/core';
+
+export default defineConfig({});
diff --git a/packages/create-rslib/fragments/tools/rstest-node-ts/tests/index.test.ts b/packages/create-rslib/fragments/tools/rstest-node-ts/tests/index.test.ts
new file mode 100644
index 000000000..f8081f198
--- /dev/null
+++ b/packages/create-rslib/fragments/tools/rstest-node-ts/tests/index.test.ts
@@ -0,0 +1,7 @@
+import { expect, test } from '@rstest/core';
+import { squared } from '../src/index';
+
+test('squared', () => {
+ expect(squared(2)).toBe(4);
+ expect(squared(12)).toBe(144);
+});
diff --git a/packages/create-rslib/fragments/tools/rstest-react-js/package.json b/packages/create-rslib/fragments/tools/rstest-react-js/package.json
new file mode 100644
index 000000000..aa7d61441
--- /dev/null
+++ b/packages/create-rslib/fragments/tools/rstest-react-js/package.json
@@ -0,0 +1,11 @@
+{
+ "scripts": {
+ "test": "rstest"
+ },
+ "devDependencies": {
+ "@rstest/core": "^0.1.3",
+ "@testing-library/jest-dom": "^6.6.4",
+ "@testing-library/react": "^16.3.0",
+ "jsdom": "^26.1.0"
+ }
+}
diff --git a/packages/create-rslib/fragments/tools/rstest-react-js/rstest.config.js b/packages/create-rslib/fragments/tools/rstest-react-js/rstest.config.js
new file mode 100644
index 000000000..1394dc0dd
--- /dev/null
+++ b/packages/create-rslib/fragments/tools/rstest-react-js/rstest.config.js
@@ -0,0 +1,8 @@
+import { pluginReact } from '@rsbuild/plugin-react';
+import { defineConfig } from '@rstest/core';
+
+export default defineConfig({
+ testEnvironment: 'jsdom',
+ setupFiles: ['./rstest.setup.js'],
+ plugins: [pluginReact()],
+});
diff --git a/packages/create-rslib/fragments/tools/rstest-react-js/rstest.setup.js b/packages/create-rslib/fragments/tools/rstest-react-js/rstest.setup.js
new file mode 100644
index 000000000..32b38ae28
--- /dev/null
+++ b/packages/create-rslib/fragments/tools/rstest-react-js/rstest.setup.js
@@ -0,0 +1,4 @@
+import { expect } from '@rstest/core';
+import * as jestDomMatchers from '@testing-library/jest-dom/matchers';
+
+expect.extend(jestDomMatchers);
diff --git a/packages/create-rslib/fragments/tools/rstest-react-js/tests/index.test.jsx b/packages/create-rslib/fragments/tools/rstest-react-js/tests/index.test.jsx
new file mode 100644
index 000000000..91e7bde86
--- /dev/null
+++ b/packages/create-rslib/fragments/tools/rstest-react-js/tests/index.test.jsx
@@ -0,0 +1,11 @@
+import { expect, test } from '@rstest/core';
+import { render, screen } from '@testing-library/react';
+import { Button } from '../src/Button';
+
+test('The button should have correct background color', async () => {
+ render();
+ const button = screen.getByText('Demo Button');
+ expect(button).toHaveStyle({
+ backgroundColor: '#ccc',
+ });
+});
diff --git a/packages/create-rslib/fragments/tools/rstest-react-ts/package.json b/packages/create-rslib/fragments/tools/rstest-react-ts/package.json
new file mode 100644
index 000000000..aa7d61441
--- /dev/null
+++ b/packages/create-rslib/fragments/tools/rstest-react-ts/package.json
@@ -0,0 +1,11 @@
+{
+ "scripts": {
+ "test": "rstest"
+ },
+ "devDependencies": {
+ "@rstest/core": "^0.1.3",
+ "@testing-library/jest-dom": "^6.6.4",
+ "@testing-library/react": "^16.3.0",
+ "jsdom": "^26.1.0"
+ }
+}
diff --git a/packages/create-rslib/fragments/tools/rstest-react-ts/rstest.config.ts b/packages/create-rslib/fragments/tools/rstest-react-ts/rstest.config.ts
new file mode 100644
index 000000000..222962418
--- /dev/null
+++ b/packages/create-rslib/fragments/tools/rstest-react-ts/rstest.config.ts
@@ -0,0 +1,8 @@
+import { pluginReact } from '@rsbuild/plugin-react';
+import { defineConfig } from '@rstest/core';
+
+export default defineConfig({
+ testEnvironment: 'jsdom',
+ setupFiles: ['./rstest.setup.ts'],
+ plugins: [pluginReact()],
+});
diff --git a/packages/create-rslib/fragments/tools/rstest-react-ts/rstest.setup.ts b/packages/create-rslib/fragments/tools/rstest-react-ts/rstest.setup.ts
new file mode 100644
index 000000000..32b38ae28
--- /dev/null
+++ b/packages/create-rslib/fragments/tools/rstest-react-ts/rstest.setup.ts
@@ -0,0 +1,4 @@
+import { expect } from '@rstest/core';
+import * as jestDomMatchers from '@testing-library/jest-dom/matchers';
+
+expect.extend(jestDomMatchers);
diff --git a/packages/create-rslib/fragments/tools/rstest-react-ts/tests/index.test.tsx b/packages/create-rslib/fragments/tools/rstest-react-ts/tests/index.test.tsx
new file mode 100644
index 000000000..91e7bde86
--- /dev/null
+++ b/packages/create-rslib/fragments/tools/rstest-react-ts/tests/index.test.tsx
@@ -0,0 +1,11 @@
+import { expect, test } from '@rstest/core';
+import { render, screen } from '@testing-library/react';
+import { Button } from '../src/Button';
+
+test('The button should have correct background color', async () => {
+ render();
+ const button = screen.getByText('Demo Button');
+ expect(button).toHaveStyle({
+ backgroundColor: '#ccc',
+ });
+});
diff --git a/packages/create-rslib/fragments/tools/rstest-react-ts/tests/test.d.ts b/packages/create-rslib/fragments/tools/rstest-react-ts/tests/test.d.ts
new file mode 100755
index 000000000..e48dee965
--- /dev/null
+++ b/packages/create-rslib/fragments/tools/rstest-react-ts/tests/test.d.ts
@@ -0,0 +1 @@
+///
diff --git a/packages/create-rslib/fragments/tools/rstest-react-ts/tests/tsconfig.json b/packages/create-rslib/fragments/tools/rstest-react-ts/tests/tsconfig.json
new file mode 100644
index 000000000..8935f17ed
--- /dev/null
+++ b/packages/create-rslib/fragments/tools/rstest-react-ts/tests/tsconfig.json
@@ -0,0 +1,4 @@
+{
+ "extends": "../tsconfig.json",
+ "include": [".", "../rstest.setup.ts"]
+}
diff --git a/packages/create-rslib/fragments/tools/rstest-vue-js/package.json b/packages/create-rslib/fragments/tools/rstest-vue-js/package.json
new file mode 100644
index 000000000..bbc8d0834
--- /dev/null
+++ b/packages/create-rslib/fragments/tools/rstest-vue-js/package.json
@@ -0,0 +1,12 @@
+{
+ "scripts": {
+ "test": "rstest"
+ },
+ "devDependencies": {
+ "@rstest/core": "^0.1.3",
+ "@testing-library/jest-dom": "^6.6.4",
+ "@testing-library/vue": "^8.0.0",
+ "@vue/test-utils": "^2.4.0",
+ "jsdom": "^26.1.0"
+ }
+}
diff --git a/packages/create-rslib/fragments/tools/rstest-vue-js/rstest.config.js b/packages/create-rslib/fragments/tools/rstest-vue-js/rstest.config.js
new file mode 100644
index 000000000..5dfd87ba2
--- /dev/null
+++ b/packages/create-rslib/fragments/tools/rstest-vue-js/rstest.config.js
@@ -0,0 +1,8 @@
+import { defineConfig } from '@rstest/core';
+import { pluginUnpluginVue } from 'rsbuild-plugin-unplugin-vue';
+
+export default defineConfig({
+ testEnvironment: 'jsdom',
+ setupFiles: ['./rstest.setup.js'],
+ plugins: [pluginUnpluginVue()],
+});
diff --git a/packages/create-rslib/fragments/tools/rstest-vue-js/rstest.setup.js b/packages/create-rslib/fragments/tools/rstest-vue-js/rstest.setup.js
new file mode 100644
index 000000000..32b38ae28
--- /dev/null
+++ b/packages/create-rslib/fragments/tools/rstest-vue-js/rstest.setup.js
@@ -0,0 +1,4 @@
+import { expect } from '@rstest/core';
+import * as jestDomMatchers from '@testing-library/jest-dom/matchers';
+
+expect.extend(jestDomMatchers);
diff --git a/packages/create-rslib/fragments/tools/rstest-vue-js/tests/index.test.js b/packages/create-rslib/fragments/tools/rstest-vue-js/tests/index.test.js
new file mode 100644
index 000000000..096747aa8
--- /dev/null
+++ b/packages/create-rslib/fragments/tools/rstest-vue-js/tests/index.test.js
@@ -0,0 +1,16 @@
+import { expect, test } from '@rstest/core';
+import { render, screen } from '@testing-library/vue';
+import Button from '../src/Button.vue';
+
+test('The button should have correct background color', async () => {
+ render(Button, {
+ props: {
+ backgroundColor: '#ccc',
+ label: 'Demo Button',
+ },
+ });
+ const button = screen.getByText('Demo Button');
+ expect(button).toHaveStyle({
+ backgroundColor: '#ccc',
+ });
+});
diff --git a/packages/create-rslib/fragments/tools/rstest-vue-ts/package.json b/packages/create-rslib/fragments/tools/rstest-vue-ts/package.json
new file mode 100644
index 000000000..bbc8d0834
--- /dev/null
+++ b/packages/create-rslib/fragments/tools/rstest-vue-ts/package.json
@@ -0,0 +1,12 @@
+{
+ "scripts": {
+ "test": "rstest"
+ },
+ "devDependencies": {
+ "@rstest/core": "^0.1.3",
+ "@testing-library/jest-dom": "^6.6.4",
+ "@testing-library/vue": "^8.0.0",
+ "@vue/test-utils": "^2.4.0",
+ "jsdom": "^26.1.0"
+ }
+}
diff --git a/packages/create-rslib/fragments/tools/rstest-vue-ts/rstest.config.ts b/packages/create-rslib/fragments/tools/rstest-vue-ts/rstest.config.ts
new file mode 100644
index 000000000..c0c74d0c8
--- /dev/null
+++ b/packages/create-rslib/fragments/tools/rstest-vue-ts/rstest.config.ts
@@ -0,0 +1,8 @@
+import { defineConfig } from '@rstest/core';
+import { pluginUnpluginVue } from 'rsbuild-plugin-unplugin-vue';
+
+export default defineConfig({
+ testEnvironment: 'jsdom',
+ setupFiles: ['./rstest.setup.ts'],
+ plugins: [pluginUnpluginVue()],
+});
diff --git a/packages/create-rslib/fragments/tools/rstest-vue-ts/rstest.setup.ts b/packages/create-rslib/fragments/tools/rstest-vue-ts/rstest.setup.ts
new file mode 100644
index 000000000..32b38ae28
--- /dev/null
+++ b/packages/create-rslib/fragments/tools/rstest-vue-ts/rstest.setup.ts
@@ -0,0 +1,4 @@
+import { expect } from '@rstest/core';
+import * as jestDomMatchers from '@testing-library/jest-dom/matchers';
+
+expect.extend(jestDomMatchers);
diff --git a/packages/create-rslib/fragments/tools/rstest-vue-ts/tests/index.test.ts b/packages/create-rslib/fragments/tools/rstest-vue-ts/tests/index.test.ts
new file mode 100644
index 000000000..096747aa8
--- /dev/null
+++ b/packages/create-rslib/fragments/tools/rstest-vue-ts/tests/index.test.ts
@@ -0,0 +1,16 @@
+import { expect, test } from '@rstest/core';
+import { render, screen } from '@testing-library/vue';
+import Button from '../src/Button.vue';
+
+test('The button should have correct background color', async () => {
+ render(Button, {
+ props: {
+ backgroundColor: '#ccc',
+ label: 'Demo Button',
+ },
+ });
+ const button = screen.getByText('Demo Button');
+ expect(button).toHaveStyle({
+ backgroundColor: '#ccc',
+ });
+});
diff --git a/packages/create-rslib/fragments/tools/rstest-vue-ts/tests/test.d.ts b/packages/create-rslib/fragments/tools/rstest-vue-ts/tests/test.d.ts
new file mode 100755
index 000000000..e48dee965
--- /dev/null
+++ b/packages/create-rslib/fragments/tools/rstest-vue-ts/tests/test.d.ts
@@ -0,0 +1 @@
+///
diff --git a/packages/create-rslib/fragments/tools/rstest-vue-ts/tests/tsconfig.json b/packages/create-rslib/fragments/tools/rstest-vue-ts/tests/tsconfig.json
new file mode 100644
index 000000000..8935f17ed
--- /dev/null
+++ b/packages/create-rslib/fragments/tools/rstest-vue-ts/tests/tsconfig.json
@@ -0,0 +1,4 @@
+{
+ "extends": "../tsconfig.json",
+ "include": [".", "../rstest.setup.ts"]
+}
diff --git a/packages/create-rslib/src/helpers.ts b/packages/create-rslib/src/helpers.ts
index b3ffa3443..b59b57f85 100644
--- a/packages/create-rslib/src/helpers.ts
+++ b/packages/create-rslib/src/helpers.ts
@@ -25,10 +25,10 @@ export const composeTemplateName = ({
lang,
}: {
template: string;
- tools?: Record;
+ tools?: string[];
lang: Lang;
}) => {
- return `[${template}]-[${(tools ? Object.keys(tools) : []).sort()}]-${lang}`;
+ return `[${template}]-[${(tools ? tools.sort() : []).join(',')}]-${lang}`;
};
export function genTemplate({
@@ -43,7 +43,7 @@ export function genTemplate({
const toolKeys = tools ? Object.keys(tools) : [];
const target = path.resolve(
__dirname,
- `../template-${composeTemplateName({ template, tools, lang })}`,
+ `../template-${composeTemplateName({ template, tools: toolKeys, lang })}`,
);
fs.rmSync(target, { recursive: true, force: true });
const srcBase = getBase({ template, lang });
@@ -77,11 +77,15 @@ export const TEMPLATES: Template[] = [
{ template: 'node-dual', lang: 'ts' },
{ template: 'node-dual', lang: 'js', tools: { vitest: 'vitest-node-js' } },
{ template: 'node-dual', lang: 'ts', tools: { vitest: 'vitest-node-ts' } },
+ { template: 'node-dual', lang: 'js', tools: { rstest: 'rstest-node-js' } },
+ { template: 'node-dual', lang: 'ts', tools: { rstest: 'rstest-node-ts' } },
// node-esm
{ template: 'node-esm', lang: 'js' },
{ template: 'node-esm', lang: 'ts' },
{ template: 'node-esm', lang: 'js', tools: { vitest: 'vitest-node-js' } },
{ template: 'node-esm', lang: 'ts', tools: { vitest: 'vitest-node-ts' } },
+ { template: 'node-esm', lang: 'js', tools: { rstest: 'rstest-node-js' } },
+ { template: 'node-esm', lang: 'ts', tools: { rstest: 'rstest-node-ts' } },
// react-js
{
template: 'react',
@@ -97,11 +101,21 @@ export const TEMPLATES: Template[] = [
lang: 'js',
tools: { vitest: 'vitest-react-js' },
},
+ {
+ template: 'react',
+ lang: 'js',
+ tools: { rstest: 'rstest-react-js' },
+ },
{
template: 'react',
lang: 'js',
tools: { storybook: 'storybook-react-js', vitest: 'vitest-react-js' },
},
+ {
+ template: 'react',
+ lang: 'js',
+ tools: { storybook: 'storybook-react-js', rstest: 'rstest-react-js' },
+ },
// react-ts
{
template: 'react',
@@ -117,11 +131,21 @@ export const TEMPLATES: Template[] = [
lang: 'ts',
tools: { vitest: 'vitest-react-ts' },
},
+ {
+ template: 'react',
+ lang: 'ts',
+ tools: { rstest: 'rstest-react-ts' },
+ },
{
template: 'react',
lang: 'ts',
tools: { storybook: 'storybook-react-ts', vitest: 'vitest-react-ts' },
},
+ {
+ template: 'react',
+ lang: 'ts',
+ tools: { storybook: 'storybook-react-ts', rstest: 'rstest-react-ts' },
+ },
// vue-js
{
template: 'vue',
@@ -137,11 +161,21 @@ export const TEMPLATES: Template[] = [
lang: 'js',
tools: { vitest: 'vitest-vue-js' },
},
+ {
+ template: 'vue',
+ lang: 'js',
+ tools: { rstest: 'rstest-vue-js' },
+ },
{
template: 'vue',
lang: 'js',
tools: { storybook: 'storybook-vue-js', vitest: 'vitest-vue-js' },
},
+ {
+ template: 'vue',
+ lang: 'js',
+ tools: { storybook: 'storybook-vue-js', rstest: 'rstest-vue-js' },
+ },
// vue-ts
{
template: 'vue',
@@ -157,9 +191,19 @@ export const TEMPLATES: Template[] = [
lang: 'ts',
tools: { vitest: 'vitest-vue-ts' },
},
+ {
+ template: 'vue',
+ lang: 'ts',
+ tools: { rstest: 'rstest-vue-ts' },
+ },
{
template: 'vue',
lang: 'ts',
tools: { storybook: 'storybook-vue-ts', vitest: 'vitest-vue-ts' },
},
+ {
+ template: 'vue',
+ lang: 'ts',
+ tools: { storybook: 'storybook-vue-ts', rstest: 'rstest-vue-ts' },
+ },
] as const;
diff --git a/packages/create-rslib/src/index.ts b/packages/create-rslib/src/index.ts
index 491ff0568..cb38f4ce3 100644
--- a/packages/create-rslib/src/index.ts
+++ b/packages/create-rslib/src/index.ts
@@ -53,28 +53,40 @@ async function getTemplateName({ template }: Argv) {
const supportStorybook = ['react', 'vue'].includes(templateName);
type ExcludesFalse = (x: T | false) => x is T;
- const tools = checkCancel(
- await multiselect({
- message:
- 'Select development tools (Use to select, to continue)',
- required: false,
- options: [
- supportStorybook && {
- value: 'storybook',
- label: 'Storybook',
- },
- { value: 'vitest', label: 'Vitest' },
- // TODO: support Rspress Module doc in the future
- ].filter(Boolean as any as ExcludesFalse),
- }),
- );
+
+ async function selectTools() {
+ const tools = checkCancel(
+ await multiselect({
+ message:
+ 'Select development tools (Use to select, to continue)',
+ required: false,
+ options: [
+ supportStorybook && {
+ value: 'storybook',
+ label: 'Storybook',
+ },
+ { value: 'rstest', label: 'Rstest' },
+ { value: 'vitest', label: 'Vitest' },
+ // TODO: support Rspress Module doc in the future
+ ].filter(Boolean as any as ExcludesFalse),
+ }),
+ );
+
+ if (tools.includes('rstest') && tools.includes('vitest')) {
+ console.error(
+ 'You selected both Rstest and Vitest for testing, you should only select one of them.',
+ );
+ return selectTools();
+ }
+ return tools;
+ }
+
+ const tools = await selectTools();
return composeTemplateName({
template: templateName,
lang: language as Lang,
- tools: Object.fromEntries(
- tools.map((tool) => [tool, `vitest-${tool}-${language}`]),
- ),
+ tools,
});
}
@@ -87,7 +99,7 @@ create({
root: path.resolve(__dirname, '..'),
name: 'rslib',
templates: TEMPLATES.map(({ template, tools, lang }) =>
- composeTemplateName({ template, lang, tools }),
+ composeTemplateName({ template, lang, tools: Object.keys(tools || {}) }),
),
getTemplateName,
mapESLintTemplate,
diff --git a/packages/create-rslib/template-[node-dual]-[rstest]-js/package.json b/packages/create-rslib/template-[node-dual]-[rstest]-js/package.json
new file mode 100644
index 000000000..2cde3df84
--- /dev/null
+++ b/packages/create-rslib/template-[node-dual]-[rstest]-js/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "rslib-node-dual-js",
+ "version": "0.0.0",
+ "type": "module",
+ "exports": {
+ ".": {
+ "import": "./dist/index.js",
+ "require": "./dist/index.cjs"
+ }
+ },
+ "main": "./dist/index.cjs",
+ "files": [
+ "dist"
+ ],
+ "scripts": {
+ "build": "rslib build",
+ "dev": "rslib build --watch",
+ "test": "rstest"
+ },
+ "devDependencies": {
+ "@rslib/core": "workspace:*",
+ "@rstest/core": "^0.1.3"
+ }
+}
diff --git a/packages/create-rslib/template-[node-dual]-[rstest]-js/rslib.config.mjs b/packages/create-rslib/template-[node-dual]-[rstest]-js/rslib.config.mjs
new file mode 100644
index 000000000..1ce38121a
--- /dev/null
+++ b/packages/create-rslib/template-[node-dual]-[rstest]-js/rslib.config.mjs
@@ -0,0 +1,14 @@
+import { defineConfig } from '@rslib/core';
+
+export default defineConfig({
+ lib: [
+ {
+ format: 'esm',
+ syntax: ['node 18'],
+ },
+ {
+ format: 'cjs',
+ syntax: ['node 18'],
+ },
+ ],
+});
diff --git a/packages/create-rslib/template-[node-dual]-[rstest]-js/rstest.config.js b/packages/create-rslib/template-[node-dual]-[rstest]-js/rstest.config.js
new file mode 100644
index 000000000..9ee3cbabc
--- /dev/null
+++ b/packages/create-rslib/template-[node-dual]-[rstest]-js/rstest.config.js
@@ -0,0 +1,3 @@
+import { defineConfig } from '@rstest/core';
+
+export default defineConfig({});
diff --git a/packages/create-rslib/template-[node-dual]-[rstest]-js/src/index.js b/packages/create-rslib/template-[node-dual]-[rstest]-js/src/index.js
new file mode 100644
index 000000000..f8beddf58
--- /dev/null
+++ b/packages/create-rslib/template-[node-dual]-[rstest]-js/src/index.js
@@ -0,0 +1 @@
+export const squared = (n) => n * n;
diff --git a/packages/create-rslib/template-[node-dual]-[rstest]-js/tests/index.test.js b/packages/create-rslib/template-[node-dual]-[rstest]-js/tests/index.test.js
new file mode 100644
index 000000000..d87ea6400
--- /dev/null
+++ b/packages/create-rslib/template-[node-dual]-[rstest]-js/tests/index.test.js
@@ -0,0 +1,7 @@
+import { expect, test } from '@rstest/core';
+import { squared } from '../src/index.js';
+
+test('squared', () => {
+ expect(squared(2)).toBe(4);
+ expect(squared(12)).toBe(144);
+});
diff --git a/packages/create-rslib/template-[node-dual]-[rstest]-ts/package.json b/packages/create-rslib/template-[node-dual]-[rstest]-ts/package.json
new file mode 100644
index 000000000..0ab4840bc
--- /dev/null
+++ b/packages/create-rslib/template-[node-dual]-[rstest]-ts/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "rslib-node-dual-ts",
+ "version": "0.0.0",
+ "type": "module",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.js",
+ "require": "./dist/index.cjs"
+ }
+ },
+ "main": "./dist/index.cjs",
+ "types": "./dist/index.d.ts",
+ "files": [
+ "dist"
+ ],
+ "scripts": {
+ "build": "rslib build",
+ "dev": "rslib build --watch",
+ "test": "rstest"
+ },
+ "devDependencies": {
+ "@rslib/core": "workspace:*",
+ "@rstest/core": "^0.1.3",
+ "@types/node": "^22.17.1",
+ "typescript": "^5.9.2"
+ }
+}
diff --git a/packages/create-rslib/template-[node-dual]-[rstest]-ts/rslib.config.ts b/packages/create-rslib/template-[node-dual]-[rstest]-ts/rslib.config.ts
new file mode 100644
index 000000000..922f44d97
--- /dev/null
+++ b/packages/create-rslib/template-[node-dual]-[rstest]-ts/rslib.config.ts
@@ -0,0 +1,15 @@
+import { defineConfig } from '@rslib/core';
+
+export default defineConfig({
+ lib: [
+ {
+ format: 'esm',
+ syntax: ['node 18'],
+ dts: true,
+ },
+ {
+ format: 'cjs',
+ syntax: ['node 18'],
+ },
+ ],
+});
diff --git a/packages/create-rslib/template-[node-dual]-[rstest]-ts/rstest.config.ts b/packages/create-rslib/template-[node-dual]-[rstest]-ts/rstest.config.ts
new file mode 100644
index 000000000..9ee3cbabc
--- /dev/null
+++ b/packages/create-rslib/template-[node-dual]-[rstest]-ts/rstest.config.ts
@@ -0,0 +1,3 @@
+import { defineConfig } from '@rstest/core';
+
+export default defineConfig({});
diff --git a/packages/create-rslib/template-[node-dual]-[rstest]-ts/src/index.ts b/packages/create-rslib/template-[node-dual]-[rstest]-ts/src/index.ts
new file mode 100644
index 000000000..fdf0e1164
--- /dev/null
+++ b/packages/create-rslib/template-[node-dual]-[rstest]-ts/src/index.ts
@@ -0,0 +1 @@
+export const squared = (n: number): number => n * n;
diff --git a/packages/create-rslib/template-[node-dual]-[rstest]-ts/tests/index.test.ts b/packages/create-rslib/template-[node-dual]-[rstest]-ts/tests/index.test.ts
new file mode 100644
index 000000000..f8081f198
--- /dev/null
+++ b/packages/create-rslib/template-[node-dual]-[rstest]-ts/tests/index.test.ts
@@ -0,0 +1,7 @@
+import { expect, test } from '@rstest/core';
+import { squared } from '../src/index';
+
+test('squared', () => {
+ expect(squared(2)).toBe(4);
+ expect(squared(12)).toBe(144);
+});
diff --git a/packages/create-rslib/template-[node-dual]-[rstest]-ts/tsconfig.json b/packages/create-rslib/template-[node-dual]-[rstest]-ts/tsconfig.json
new file mode 100644
index 000000000..64b77e489
--- /dev/null
+++ b/packages/create-rslib/template-[node-dual]-[rstest]-ts/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "compilerOptions": {
+ "lib": ["ES2022"],
+ "module": "ESNext",
+ "noEmit": true,
+ "strict": true,
+ "skipLibCheck": true,
+ "isolatedModules": true,
+ "resolveJsonModule": true,
+ "moduleResolution": "bundler",
+ "useDefineForClassFields": true,
+ "allowImportingTsExtensions": true
+ },
+ "include": ["src"]
+}
diff --git a/packages/create-rslib/template-[node-esm]-[rstest]-js/package.json b/packages/create-rslib/template-[node-esm]-[rstest]-js/package.json
new file mode 100644
index 000000000..6a7b5a65b
--- /dev/null
+++ b/packages/create-rslib/template-[node-esm]-[rstest]-js/package.json
@@ -0,0 +1,22 @@
+{
+ "name": "rslib-node-esm-js",
+ "version": "0.0.0",
+ "type": "module",
+ "exports": {
+ ".": {
+ "import": "./dist/index.js"
+ }
+ },
+ "files": [
+ "dist"
+ ],
+ "scripts": {
+ "build": "rslib build",
+ "dev": "rslib build --watch",
+ "test": "rstest"
+ },
+ "devDependencies": {
+ "@rslib/core": "workspace:*",
+ "@rstest/core": "^0.1.3"
+ }
+}
diff --git a/packages/create-rslib/template-[node-esm]-[rstest]-js/rslib.config.mjs b/packages/create-rslib/template-[node-esm]-[rstest]-js/rslib.config.mjs
new file mode 100644
index 000000000..a981d59f2
--- /dev/null
+++ b/packages/create-rslib/template-[node-esm]-[rstest]-js/rslib.config.mjs
@@ -0,0 +1,10 @@
+import { defineConfig } from '@rslib/core';
+
+export default defineConfig({
+ lib: [
+ {
+ format: 'esm',
+ syntax: ['node 18'],
+ },
+ ],
+});
diff --git a/packages/create-rslib/template-[node-esm]-[rstest]-js/rstest.config.js b/packages/create-rslib/template-[node-esm]-[rstest]-js/rstest.config.js
new file mode 100644
index 000000000..9ee3cbabc
--- /dev/null
+++ b/packages/create-rslib/template-[node-esm]-[rstest]-js/rstest.config.js
@@ -0,0 +1,3 @@
+import { defineConfig } from '@rstest/core';
+
+export default defineConfig({});
diff --git a/packages/create-rslib/template-[node-esm]-[rstest]-js/src/index.js b/packages/create-rslib/template-[node-esm]-[rstest]-js/src/index.js
new file mode 100644
index 000000000..f8beddf58
--- /dev/null
+++ b/packages/create-rslib/template-[node-esm]-[rstest]-js/src/index.js
@@ -0,0 +1 @@
+export const squared = (n) => n * n;
diff --git a/packages/create-rslib/template-[node-esm]-[rstest]-js/tests/index.test.js b/packages/create-rslib/template-[node-esm]-[rstest]-js/tests/index.test.js
new file mode 100644
index 000000000..d87ea6400
--- /dev/null
+++ b/packages/create-rslib/template-[node-esm]-[rstest]-js/tests/index.test.js
@@ -0,0 +1,7 @@
+import { expect, test } from '@rstest/core';
+import { squared } from '../src/index.js';
+
+test('squared', () => {
+ expect(squared(2)).toBe(4);
+ expect(squared(12)).toBe(144);
+});
diff --git a/packages/create-rslib/template-[node-esm]-[rstest]-ts/package.json b/packages/create-rslib/template-[node-esm]-[rstest]-ts/package.json
new file mode 100644
index 000000000..dcaa8c44d
--- /dev/null
+++ b/packages/create-rslib/template-[node-esm]-[rstest]-ts/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "rslib-node-esm-ts",
+ "version": "0.0.0",
+ "type": "module",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.js"
+ }
+ },
+ "types": "./dist/index.d.ts",
+ "files": [
+ "dist"
+ ],
+ "scripts": {
+ "build": "rslib build",
+ "dev": "rslib build --watch",
+ "test": "rstest"
+ },
+ "devDependencies": {
+ "@rslib/core": "workspace:*",
+ "@rstest/core": "^0.1.3",
+ "@types/node": "^22.17.1",
+ "typescript": "^5.9.2"
+ }
+}
diff --git a/packages/create-rslib/template-[node-esm]-[rstest]-ts/rslib.config.ts b/packages/create-rslib/template-[node-esm]-[rstest]-ts/rslib.config.ts
new file mode 100644
index 000000000..75a1f2011
--- /dev/null
+++ b/packages/create-rslib/template-[node-esm]-[rstest]-ts/rslib.config.ts
@@ -0,0 +1,11 @@
+import { defineConfig } from '@rslib/core';
+
+export default defineConfig({
+ lib: [
+ {
+ format: 'esm',
+ syntax: ['node 18'],
+ dts: true,
+ },
+ ],
+});
diff --git a/packages/create-rslib/template-[node-esm]-[rstest]-ts/rstest.config.ts b/packages/create-rslib/template-[node-esm]-[rstest]-ts/rstest.config.ts
new file mode 100644
index 000000000..9ee3cbabc
--- /dev/null
+++ b/packages/create-rslib/template-[node-esm]-[rstest]-ts/rstest.config.ts
@@ -0,0 +1,3 @@
+import { defineConfig } from '@rstest/core';
+
+export default defineConfig({});
diff --git a/packages/create-rslib/template-[node-esm]-[rstest]-ts/src/index.ts b/packages/create-rslib/template-[node-esm]-[rstest]-ts/src/index.ts
new file mode 100644
index 000000000..fdf0e1164
--- /dev/null
+++ b/packages/create-rslib/template-[node-esm]-[rstest]-ts/src/index.ts
@@ -0,0 +1 @@
+export const squared = (n: number): number => n * n;
diff --git a/packages/create-rslib/template-[node-esm]-[rstest]-ts/tests/index.test.ts b/packages/create-rslib/template-[node-esm]-[rstest]-ts/tests/index.test.ts
new file mode 100644
index 000000000..f8081f198
--- /dev/null
+++ b/packages/create-rslib/template-[node-esm]-[rstest]-ts/tests/index.test.ts
@@ -0,0 +1,7 @@
+import { expect, test } from '@rstest/core';
+import { squared } from '../src/index';
+
+test('squared', () => {
+ expect(squared(2)).toBe(4);
+ expect(squared(12)).toBe(144);
+});
diff --git a/packages/create-rslib/template-[node-esm]-[rstest]-ts/tsconfig.json b/packages/create-rslib/template-[node-esm]-[rstest]-ts/tsconfig.json
new file mode 100644
index 000000000..64b77e489
--- /dev/null
+++ b/packages/create-rslib/template-[node-esm]-[rstest]-ts/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "compilerOptions": {
+ "lib": ["ES2022"],
+ "module": "ESNext",
+ "noEmit": true,
+ "strict": true,
+ "skipLibCheck": true,
+ "isolatedModules": true,
+ "resolveJsonModule": true,
+ "moduleResolution": "bundler",
+ "useDefineForClassFields": true,
+ "allowImportingTsExtensions": true
+ },
+ "include": ["src"]
+}
diff --git a/packages/create-rslib/template-[react]-[rstest,storybook]-js/.storybook/main.js b/packages/create-rslib/template-[react]-[rstest,storybook]-js/.storybook/main.js
new file mode 100644
index 000000000..ca1d928e3
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest,storybook]-js/.storybook/main.js
@@ -0,0 +1,36 @@
+import { dirname, join } from 'node:path';
+
+/**
+ * This function is used to resolve the absolute path of a package.
+ * It is needed in projects that use Yarn PnP or are set up within a monorepo.
+ */
+function getAbsolutePath(value) {
+ return dirname(require.resolve(join(value, 'package.json')));
+}
+
+const config = {
+ stories: [
+ '../stories/**/*.mdx',
+ '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)',
+ ],
+ addons: [
+ '@storybook/addon-docs',
+ '@storybook/addon-onboarding',
+ '@storybook/addon-links',
+ '@storybook/addon-essentials',
+ '@storybook/addon-interactions',
+ {
+ name: getAbsolutePath('storybook-addon-rslib'),
+ },
+ ],
+ framework: {
+ name: getAbsolutePath('storybook-react-rsbuild'),
+ options: {},
+ },
+ typescript: {
+ reactDocgen: 'react-docgen-typescript',
+ check: true,
+ },
+};
+
+export default config;
diff --git a/packages/create-rslib/template-[react]-[rstest,storybook]-js/.storybook/preview.js b/packages/create-rslib/template-[react]-[rstest,storybook]-js/.storybook/preview.js
new file mode 100644
index 000000000..0853b53bd
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest,storybook]-js/.storybook/preview.js
@@ -0,0 +1,12 @@
+const preview = {
+ parameters: {
+ controls: {
+ matchers: {
+ color: /(background|color)$/i,
+ date: /Date$/i,
+ },
+ },
+ },
+};
+
+export default preview;
diff --git a/packages/create-rslib/template-[react]-[rstest,storybook]-js/package.json b/packages/create-rslib/template-[react]-[rstest,storybook]-js/package.json
new file mode 100644
index 000000000..495ad38b7
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest,storybook]-js/package.json
@@ -0,0 +1,45 @@
+{
+ "name": "rslib-react-js",
+ "version": "0.0.0",
+ "type": "module",
+ "exports": {
+ ".": {
+ "import": "./dist/index.js"
+ }
+ },
+ "files": [
+ "dist"
+ ],
+ "scripts": {
+ "build": "rslib build",
+ "build:storybook": "storybook build",
+ "dev": "rslib build --watch",
+ "storybook": "storybook dev",
+ "test": "rstest"
+ },
+ "devDependencies": {
+ "@rsbuild/core": "1.5.0-beta.0",
+ "@rsbuild/plugin-react": "^1.3.5",
+ "@rslib/core": "workspace:*",
+ "@rstest/core": "^0.1.3",
+ "@storybook/addon-docs": "^9.1.1",
+ "@storybook/addon-essentials": "^9.0.0-alpha.12",
+ "@storybook/addon-interactions": "^9.0.0-alpha.10",
+ "@storybook/addon-links": "^9.1.1",
+ "@storybook/addon-onboarding": "^9.1.1",
+ "@storybook/blocks": "^9.0.0-alpha.17",
+ "@storybook/react": "^9.1.1",
+ "@storybook/test": "^9.0.0-alpha.2",
+ "@testing-library/jest-dom": "^6.6.4",
+ "@testing-library/react": "^16.3.0",
+ "jsdom": "^26.1.0",
+ "react": "^19.1.1",
+ "storybook": "^9.1.1",
+ "storybook-addon-rslib": "^2.0.3",
+ "storybook-react-rsbuild": "^2.0.3"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+}
diff --git a/packages/create-rslib/template-[react]-[rstest,storybook]-js/rslib.config.mjs b/packages/create-rslib/template-[react]-[rstest,storybook]-js/rslib.config.mjs
new file mode 100644
index 000000000..1cc7880e2
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest,storybook]-js/rslib.config.mjs
@@ -0,0 +1,20 @@
+import { pluginReact } from '@rsbuild/plugin-react';
+import { defineConfig } from '@rslib/core';
+
+export default defineConfig({
+ source: {
+ entry: {
+ index: ['./src/**'],
+ },
+ },
+ lib: [
+ {
+ bundle: false,
+ format: 'esm',
+ },
+ ],
+ output: {
+ target: 'web',
+ },
+ plugins: [pluginReact()],
+});
diff --git a/packages/create-rslib/template-[react]-[rstest,storybook]-js/rstest.config.js b/packages/create-rslib/template-[react]-[rstest,storybook]-js/rstest.config.js
new file mode 100644
index 000000000..1394dc0dd
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest,storybook]-js/rstest.config.js
@@ -0,0 +1,8 @@
+import { pluginReact } from '@rsbuild/plugin-react';
+import { defineConfig } from '@rstest/core';
+
+export default defineConfig({
+ testEnvironment: 'jsdom',
+ setupFiles: ['./rstest.setup.js'],
+ plugins: [pluginReact()],
+});
diff --git a/packages/create-rslib/template-[react]-[rstest,storybook]-js/rstest.setup.js b/packages/create-rslib/template-[react]-[rstest,storybook]-js/rstest.setup.js
new file mode 100644
index 000000000..32b38ae28
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest,storybook]-js/rstest.setup.js
@@ -0,0 +1,4 @@
+import { expect } from '@rstest/core';
+import * as jestDomMatchers from '@testing-library/jest-dom/matchers';
+
+expect.extend(jestDomMatchers);
diff --git a/packages/create-rslib/template-[react]-[rstest,storybook]-js/src/Button.jsx b/packages/create-rslib/template-[react]-[rstest,storybook]-js/src/Button.jsx
new file mode 100644
index 000000000..1e0570fb3
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest,storybook]-js/src/Button.jsx
@@ -0,0 +1,21 @@
+import './button.css';
+
+export const Button = ({
+ primary = false,
+ size = 'medium',
+ backgroundColor,
+ label,
+ ...props
+}) => {
+ const mode = primary ? 'demo-button--primary' : 'demo-button--secondary';
+ return (
+
+ );
+};
diff --git a/packages/create-rslib/template-[react]-[rstest,storybook]-js/src/button.css b/packages/create-rslib/template-[react]-[rstest,storybook]-js/src/button.css
new file mode 100644
index 000000000..b7a36cbde
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest,storybook]-js/src/button.css
@@ -0,0 +1,34 @@
+.demo-button {
+ font-weight: 700;
+ border: 0;
+ border-radius: 3em;
+ cursor: pointer;
+ display: inline-block;
+ line-height: 1;
+}
+
+.demo-button--primary {
+ color: white;
+ background-color: #1ea7fd;
+}
+
+.demo-button--secondary {
+ color: #333;
+ background-color: transparent;
+ box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset;
+}
+
+.demo-button--small {
+ font-size: 12px;
+ padding: 10px 16px;
+}
+
+.demo-button--medium {
+ font-size: 14px;
+ padding: 11px 20px;
+}
+
+.demo-button--large {
+ font-size: 16px;
+ padding: 12px 24px;
+}
diff --git a/packages/create-rslib/template-[react]-[rstest,storybook]-js/src/index.jsx b/packages/create-rslib/template-[react]-[rstest,storybook]-js/src/index.jsx
new file mode 100644
index 000000000..fe9c53c51
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest,storybook]-js/src/index.jsx
@@ -0,0 +1 @@
+export { Button } from './Button';
diff --git a/packages/create-rslib/template-[react]-[rstest,storybook]-js/stories/Button.stories.jsx b/packages/create-rslib/template-[react]-[rstest,storybook]-js/stories/Button.stories.jsx
new file mode 100644
index 000000000..52df85343
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest,storybook]-js/stories/Button.stories.jsx
@@ -0,0 +1,50 @@
+import { fn } from '@storybook/test';
+import { Button } from '../src/Button';
+
+// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
+const meta = {
+ title: 'Example/Button',
+ component: Button,
+ parameters: {
+ // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
+ layout: 'centered',
+ },
+ // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
+ tags: ['autodocs'],
+ // More on argTypes: https://storybook.js.org/docs/api/argtypes
+ argTypes: {
+ backgroundColor: { control: 'color' },
+ },
+ // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
+ args: { onClick: fn() },
+};
+
+export default meta;
+
+// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
+export const Primary = {
+ args: {
+ primary: true,
+ label: 'Button',
+ },
+};
+
+export const Secondary = {
+ args: {
+ label: 'Button',
+ },
+};
+
+export const Large = {
+ args: {
+ size: 'large',
+ label: 'Button',
+ },
+};
+
+export const Small = {
+ args: {
+ size: 'small',
+ label: 'Button',
+ },
+};
diff --git a/packages/create-rslib/template-[react]-[rstest,storybook]-js/tests/index.test.jsx b/packages/create-rslib/template-[react]-[rstest,storybook]-js/tests/index.test.jsx
new file mode 100644
index 000000000..91e7bde86
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest,storybook]-js/tests/index.test.jsx
@@ -0,0 +1,11 @@
+import { expect, test } from '@rstest/core';
+import { render, screen } from '@testing-library/react';
+import { Button } from '../src/Button';
+
+test('The button should have correct background color', async () => {
+ render();
+ const button = screen.getByText('Demo Button');
+ expect(button).toHaveStyle({
+ backgroundColor: '#ccc',
+ });
+});
diff --git a/packages/create-rslib/template-[react]-[rstest,storybook]-ts/.storybook/main.ts b/packages/create-rslib/template-[react]-[rstest,storybook]-ts/.storybook/main.ts
new file mode 100644
index 000000000..87da115ab
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest,storybook]-ts/.storybook/main.ts
@@ -0,0 +1,37 @@
+import { dirname, join } from 'node:path';
+import type { StorybookConfig } from 'storybook-react-rsbuild';
+
+/**
+ * This function is used to resolve the absolute path of a package.
+ * It is needed in projects that use Yarn PnP or are set up within a monorepo.
+ */
+function getAbsolutePath(value: string): any {
+ return dirname(require.resolve(join(value, 'package.json')));
+}
+
+const config: StorybookConfig = {
+ stories: [
+ '../stories/**/*.mdx',
+ '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)',
+ ],
+ addons: [
+ '@storybook/addon-docs',
+ '@storybook/addon-onboarding',
+ '@storybook/addon-links',
+ '@storybook/addon-essentials',
+ '@storybook/addon-interactions',
+ {
+ name: getAbsolutePath('storybook-addon-rslib'),
+ },
+ ],
+ framework: {
+ name: getAbsolutePath('storybook-react-rsbuild'),
+ options: {},
+ },
+ typescript: {
+ reactDocgen: 'react-docgen-typescript',
+ check: true,
+ },
+};
+
+export default config;
diff --git a/packages/create-rslib/template-[react]-[rstest,storybook]-ts/.storybook/preview.ts b/packages/create-rslib/template-[react]-[rstest,storybook]-ts/.storybook/preview.ts
new file mode 100644
index 000000000..adcda96bd
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest,storybook]-ts/.storybook/preview.ts
@@ -0,0 +1,14 @@
+import type { Preview } from '@storybook/react';
+
+const preview: Preview = {
+ parameters: {
+ controls: {
+ matchers: {
+ color: /(background|color)$/i,
+ date: /Date$/i,
+ },
+ },
+ },
+};
+
+export default preview;
diff --git a/packages/create-rslib/template-[react]-[rstest,storybook]-ts/package.json b/packages/create-rslib/template-[react]-[rstest,storybook]-ts/package.json
new file mode 100644
index 000000000..e058e9a2e
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest,storybook]-ts/package.json
@@ -0,0 +1,49 @@
+{
+ "name": "rslib-react-ts",
+ "version": "0.0.0",
+ "type": "module",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.js"
+ }
+ },
+ "types": "./dist/index.d.ts",
+ "files": [
+ "dist"
+ ],
+ "scripts": {
+ "build": "rslib build",
+ "build:storybook": "storybook build",
+ "dev": "rslib build --watch",
+ "storybook": "storybook dev",
+ "test": "rstest"
+ },
+ "devDependencies": {
+ "@rsbuild/core": "1.5.0-beta.0",
+ "@rsbuild/plugin-react": "^1.3.5",
+ "@rslib/core": "workspace:*",
+ "@rstest/core": "^0.1.3",
+ "@storybook/addon-docs": "^9.1.1",
+ "@storybook/addon-essentials": "^9.0.0-alpha.12",
+ "@storybook/addon-interactions": "^9.0.0-alpha.10",
+ "@storybook/addon-links": "^9.1.1",
+ "@storybook/addon-onboarding": "^9.1.1",
+ "@storybook/blocks": "^9.0.0-alpha.17",
+ "@storybook/react": "^9.1.1",
+ "@storybook/test": "^9.0.0-alpha.2",
+ "@testing-library/jest-dom": "^6.6.4",
+ "@testing-library/react": "^16.3.0",
+ "@types/react": "^19.1.9",
+ "jsdom": "^26.1.0",
+ "react": "^19.1.1",
+ "storybook": "^9.1.1",
+ "storybook-addon-rslib": "^2.0.3",
+ "storybook-react-rsbuild": "^2.0.3",
+ "typescript": "^5.9.2"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+}
diff --git a/packages/create-rslib/template-[react]-[rstest,storybook]-ts/rslib.config.ts b/packages/create-rslib/template-[react]-[rstest,storybook]-ts/rslib.config.ts
new file mode 100644
index 000000000..0180c8c57
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest,storybook]-ts/rslib.config.ts
@@ -0,0 +1,21 @@
+import { pluginReact } from '@rsbuild/plugin-react';
+import { defineConfig } from '@rslib/core';
+
+export default defineConfig({
+ source: {
+ entry: {
+ index: ['./src/**'],
+ },
+ },
+ lib: [
+ {
+ bundle: false,
+ dts: true,
+ format: 'esm',
+ },
+ ],
+ output: {
+ target: 'web',
+ },
+ plugins: [pluginReact()],
+});
diff --git a/packages/create-rslib/template-[react]-[rstest,storybook]-ts/rstest.config.ts b/packages/create-rslib/template-[react]-[rstest,storybook]-ts/rstest.config.ts
new file mode 100644
index 000000000..222962418
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest,storybook]-ts/rstest.config.ts
@@ -0,0 +1,8 @@
+import { pluginReact } from '@rsbuild/plugin-react';
+import { defineConfig } from '@rstest/core';
+
+export default defineConfig({
+ testEnvironment: 'jsdom',
+ setupFiles: ['./rstest.setup.ts'],
+ plugins: [pluginReact()],
+});
diff --git a/packages/create-rslib/template-[react]-[rstest,storybook]-ts/rstest.setup.ts b/packages/create-rslib/template-[react]-[rstest,storybook]-ts/rstest.setup.ts
new file mode 100644
index 000000000..32b38ae28
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest,storybook]-ts/rstest.setup.ts
@@ -0,0 +1,4 @@
+import { expect } from '@rstest/core';
+import * as jestDomMatchers from '@testing-library/jest-dom/matchers';
+
+expect.extend(jestDomMatchers);
diff --git a/packages/create-rslib/template-[react]-[rstest,storybook]-ts/src/Button.tsx b/packages/create-rslib/template-[react]-[rstest,storybook]-ts/src/Button.tsx
new file mode 100644
index 000000000..18018d316
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest,storybook]-ts/src/Button.tsx
@@ -0,0 +1,29 @@
+import './button.css';
+
+interface ButtonProps {
+ primary?: boolean;
+ backgroundColor?: string;
+ size?: 'small' | 'medium' | 'large';
+ label: string;
+ onClick?: () => void;
+}
+
+export const Button = ({
+ primary = false,
+ size = 'medium',
+ backgroundColor,
+ label,
+ ...props
+}: ButtonProps) => {
+ const mode = primary ? 'demo-button--primary' : 'demo-button--secondary';
+ return (
+
+ );
+};
diff --git a/packages/create-rslib/template-[react]-[rstest,storybook]-ts/src/button.css b/packages/create-rslib/template-[react]-[rstest,storybook]-ts/src/button.css
new file mode 100644
index 000000000..b7a36cbde
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest,storybook]-ts/src/button.css
@@ -0,0 +1,34 @@
+.demo-button {
+ font-weight: 700;
+ border: 0;
+ border-radius: 3em;
+ cursor: pointer;
+ display: inline-block;
+ line-height: 1;
+}
+
+.demo-button--primary {
+ color: white;
+ background-color: #1ea7fd;
+}
+
+.demo-button--secondary {
+ color: #333;
+ background-color: transparent;
+ box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset;
+}
+
+.demo-button--small {
+ font-size: 12px;
+ padding: 10px 16px;
+}
+
+.demo-button--medium {
+ font-size: 14px;
+ padding: 11px 20px;
+}
+
+.demo-button--large {
+ font-size: 16px;
+ padding: 12px 24px;
+}
diff --git a/packages/create-rslib/template-[react]-[rstest,storybook]-ts/src/index.tsx b/packages/create-rslib/template-[react]-[rstest,storybook]-ts/src/index.tsx
new file mode 100644
index 000000000..fe9c53c51
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest,storybook]-ts/src/index.tsx
@@ -0,0 +1 @@
+export { Button } from './Button';
diff --git a/packages/create-rslib/template-[react]-[rstest,storybook]-ts/stories/Button.stories.ts b/packages/create-rslib/template-[react]-[rstest,storybook]-ts/stories/Button.stories.ts
new file mode 100644
index 000000000..b35a99786
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest,storybook]-ts/stories/Button.stories.ts
@@ -0,0 +1,52 @@
+import type { Meta, StoryObj } from '@storybook/react';
+import { fn } from '@storybook/test';
+import { Button } from '../src/Button';
+
+// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
+const meta = {
+ title: 'Example/Button',
+ component: Button,
+ parameters: {
+ // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
+ layout: 'centered',
+ },
+ // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
+ tags: ['autodocs'],
+ // More on argTypes: https://storybook.js.org/docs/api/argtypes
+ argTypes: {
+ backgroundColor: { control: 'color' },
+ },
+ // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
+ args: { onClick: fn() },
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
+export const Primary: Story = {
+ args: {
+ primary: true,
+ label: 'Button',
+ },
+};
+
+export const Secondary: Story = {
+ args: {
+ label: 'Button',
+ },
+};
+
+export const Large: Story = {
+ args: {
+ size: 'large',
+ label: 'Button',
+ },
+};
+
+export const Small: Story = {
+ args: {
+ size: 'small',
+ label: 'Button',
+ },
+};
diff --git a/packages/create-rslib/template-[react]-[rstest,storybook]-ts/tests/index.test.tsx b/packages/create-rslib/template-[react]-[rstest,storybook]-ts/tests/index.test.tsx
new file mode 100644
index 000000000..91e7bde86
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest,storybook]-ts/tests/index.test.tsx
@@ -0,0 +1,11 @@
+import { expect, test } from '@rstest/core';
+import { render, screen } from '@testing-library/react';
+import { Button } from '../src/Button';
+
+test('The button should have correct background color', async () => {
+ render();
+ const button = screen.getByText('Demo Button');
+ expect(button).toHaveStyle({
+ backgroundColor: '#ccc',
+ });
+});
diff --git a/packages/create-rslib/template-[react]-[rstest,storybook]-ts/tests/test.d.ts b/packages/create-rslib/template-[react]-[rstest,storybook]-ts/tests/test.d.ts
new file mode 100755
index 000000000..e48dee965
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest,storybook]-ts/tests/test.d.ts
@@ -0,0 +1 @@
+///
diff --git a/packages/create-rslib/template-[react]-[rstest,storybook]-ts/tests/tsconfig.json b/packages/create-rslib/template-[react]-[rstest,storybook]-ts/tests/tsconfig.json
new file mode 100644
index 000000000..8935f17ed
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest,storybook]-ts/tests/tsconfig.json
@@ -0,0 +1,4 @@
+{
+ "extends": "../tsconfig.json",
+ "include": [".", "../rstest.setup.ts"]
+}
diff --git a/packages/create-rslib/template-[react]-[rstest,storybook]-ts/tsconfig.json b/packages/create-rslib/template-[react]-[rstest,storybook]-ts/tsconfig.json
new file mode 100644
index 000000000..510ab68df
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest,storybook]-ts/tsconfig.json
@@ -0,0 +1,14 @@
+{
+ "compilerOptions": {
+ "lib": ["DOM", "ES2022"],
+ "module": "ESNext",
+ "jsx": "react-jsx",
+ "strict": true,
+ "skipLibCheck": true,
+ "isolatedModules": true,
+ "resolveJsonModule": true,
+ "moduleResolution": "bundler",
+ "useDefineForClassFields": true
+ },
+ "include": ["src"]
+}
diff --git a/packages/create-rslib/template-[react]-[rstest]-js/package.json b/packages/create-rslib/template-[react]-[rstest]-js/package.json
new file mode 100644
index 000000000..8516d845f
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest]-js/package.json
@@ -0,0 +1,31 @@
+{
+ "name": "rslib-react-js",
+ "version": "0.0.0",
+ "type": "module",
+ "exports": {
+ ".": {
+ "import": "./dist/index.js"
+ }
+ },
+ "files": [
+ "dist"
+ ],
+ "scripts": {
+ "build": "rslib build",
+ "dev": "rslib build --watch",
+ "test": "rstest"
+ },
+ "devDependencies": {
+ "@rsbuild/plugin-react": "^1.3.5",
+ "@rslib/core": "workspace:*",
+ "@rstest/core": "^0.1.3",
+ "@testing-library/jest-dom": "^6.6.4",
+ "@testing-library/react": "^16.3.0",
+ "jsdom": "^26.1.0",
+ "react": "^19.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+}
diff --git a/packages/create-rslib/template-[react]-[rstest]-js/rslib.config.mjs b/packages/create-rslib/template-[react]-[rstest]-js/rslib.config.mjs
new file mode 100644
index 000000000..1cc7880e2
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest]-js/rslib.config.mjs
@@ -0,0 +1,20 @@
+import { pluginReact } from '@rsbuild/plugin-react';
+import { defineConfig } from '@rslib/core';
+
+export default defineConfig({
+ source: {
+ entry: {
+ index: ['./src/**'],
+ },
+ },
+ lib: [
+ {
+ bundle: false,
+ format: 'esm',
+ },
+ ],
+ output: {
+ target: 'web',
+ },
+ plugins: [pluginReact()],
+});
diff --git a/packages/create-rslib/template-[react]-[rstest]-js/rstest.config.js b/packages/create-rslib/template-[react]-[rstest]-js/rstest.config.js
new file mode 100644
index 000000000..1394dc0dd
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest]-js/rstest.config.js
@@ -0,0 +1,8 @@
+import { pluginReact } from '@rsbuild/plugin-react';
+import { defineConfig } from '@rstest/core';
+
+export default defineConfig({
+ testEnvironment: 'jsdom',
+ setupFiles: ['./rstest.setup.js'],
+ plugins: [pluginReact()],
+});
diff --git a/packages/create-rslib/template-[react]-[rstest]-js/rstest.setup.js b/packages/create-rslib/template-[react]-[rstest]-js/rstest.setup.js
new file mode 100644
index 000000000..32b38ae28
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest]-js/rstest.setup.js
@@ -0,0 +1,4 @@
+import { expect } from '@rstest/core';
+import * as jestDomMatchers from '@testing-library/jest-dom/matchers';
+
+expect.extend(jestDomMatchers);
diff --git a/packages/create-rslib/template-[react]-[rstest]-js/src/Button.jsx b/packages/create-rslib/template-[react]-[rstest]-js/src/Button.jsx
new file mode 100644
index 000000000..1e0570fb3
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest]-js/src/Button.jsx
@@ -0,0 +1,21 @@
+import './button.css';
+
+export const Button = ({
+ primary = false,
+ size = 'medium',
+ backgroundColor,
+ label,
+ ...props
+}) => {
+ const mode = primary ? 'demo-button--primary' : 'demo-button--secondary';
+ return (
+
+ );
+};
diff --git a/packages/create-rslib/template-[react]-[rstest]-js/src/button.css b/packages/create-rslib/template-[react]-[rstest]-js/src/button.css
new file mode 100644
index 000000000..b7a36cbde
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest]-js/src/button.css
@@ -0,0 +1,34 @@
+.demo-button {
+ font-weight: 700;
+ border: 0;
+ border-radius: 3em;
+ cursor: pointer;
+ display: inline-block;
+ line-height: 1;
+}
+
+.demo-button--primary {
+ color: white;
+ background-color: #1ea7fd;
+}
+
+.demo-button--secondary {
+ color: #333;
+ background-color: transparent;
+ box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset;
+}
+
+.demo-button--small {
+ font-size: 12px;
+ padding: 10px 16px;
+}
+
+.demo-button--medium {
+ font-size: 14px;
+ padding: 11px 20px;
+}
+
+.demo-button--large {
+ font-size: 16px;
+ padding: 12px 24px;
+}
diff --git a/packages/create-rslib/template-[react]-[rstest]-js/src/index.jsx b/packages/create-rslib/template-[react]-[rstest]-js/src/index.jsx
new file mode 100644
index 000000000..fe9c53c51
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest]-js/src/index.jsx
@@ -0,0 +1 @@
+export { Button } from './Button';
diff --git a/packages/create-rslib/template-[react]-[rstest]-js/tests/index.test.jsx b/packages/create-rslib/template-[react]-[rstest]-js/tests/index.test.jsx
new file mode 100644
index 000000000..91e7bde86
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest]-js/tests/index.test.jsx
@@ -0,0 +1,11 @@
+import { expect, test } from '@rstest/core';
+import { render, screen } from '@testing-library/react';
+import { Button } from '../src/Button';
+
+test('The button should have correct background color', async () => {
+ render();
+ const button = screen.getByText('Demo Button');
+ expect(button).toHaveStyle({
+ backgroundColor: '#ccc',
+ });
+});
diff --git a/packages/create-rslib/template-[react]-[rstest]-ts/package.json b/packages/create-rslib/template-[react]-[rstest]-ts/package.json
new file mode 100644
index 000000000..94e3749f5
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest]-ts/package.json
@@ -0,0 +1,35 @@
+{
+ "name": "rslib-react-ts",
+ "version": "0.0.0",
+ "type": "module",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.js"
+ }
+ },
+ "types": "./dist/index.d.ts",
+ "files": [
+ "dist"
+ ],
+ "scripts": {
+ "build": "rslib build",
+ "dev": "rslib build --watch",
+ "test": "rstest"
+ },
+ "devDependencies": {
+ "@rsbuild/plugin-react": "^1.3.5",
+ "@rslib/core": "workspace:*",
+ "@rstest/core": "^0.1.3",
+ "@testing-library/jest-dom": "^6.6.4",
+ "@testing-library/react": "^16.3.0",
+ "@types/react": "^19.1.9",
+ "jsdom": "^26.1.0",
+ "react": "^19.1.1",
+ "typescript": "^5.9.2"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+}
diff --git a/packages/create-rslib/template-[react]-[rstest]-ts/rslib.config.ts b/packages/create-rslib/template-[react]-[rstest]-ts/rslib.config.ts
new file mode 100644
index 000000000..0180c8c57
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest]-ts/rslib.config.ts
@@ -0,0 +1,21 @@
+import { pluginReact } from '@rsbuild/plugin-react';
+import { defineConfig } from '@rslib/core';
+
+export default defineConfig({
+ source: {
+ entry: {
+ index: ['./src/**'],
+ },
+ },
+ lib: [
+ {
+ bundle: false,
+ dts: true,
+ format: 'esm',
+ },
+ ],
+ output: {
+ target: 'web',
+ },
+ plugins: [pluginReact()],
+});
diff --git a/packages/create-rslib/template-[react]-[rstest]-ts/rstest.config.ts b/packages/create-rslib/template-[react]-[rstest]-ts/rstest.config.ts
new file mode 100644
index 000000000..222962418
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest]-ts/rstest.config.ts
@@ -0,0 +1,8 @@
+import { pluginReact } from '@rsbuild/plugin-react';
+import { defineConfig } from '@rstest/core';
+
+export default defineConfig({
+ testEnvironment: 'jsdom',
+ setupFiles: ['./rstest.setup.ts'],
+ plugins: [pluginReact()],
+});
diff --git a/packages/create-rslib/template-[react]-[rstest]-ts/rstest.setup.ts b/packages/create-rslib/template-[react]-[rstest]-ts/rstest.setup.ts
new file mode 100644
index 000000000..32b38ae28
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest]-ts/rstest.setup.ts
@@ -0,0 +1,4 @@
+import { expect } from '@rstest/core';
+import * as jestDomMatchers from '@testing-library/jest-dom/matchers';
+
+expect.extend(jestDomMatchers);
diff --git a/packages/create-rslib/template-[react]-[rstest]-ts/src/Button.tsx b/packages/create-rslib/template-[react]-[rstest]-ts/src/Button.tsx
new file mode 100644
index 000000000..18018d316
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest]-ts/src/Button.tsx
@@ -0,0 +1,29 @@
+import './button.css';
+
+interface ButtonProps {
+ primary?: boolean;
+ backgroundColor?: string;
+ size?: 'small' | 'medium' | 'large';
+ label: string;
+ onClick?: () => void;
+}
+
+export const Button = ({
+ primary = false,
+ size = 'medium',
+ backgroundColor,
+ label,
+ ...props
+}: ButtonProps) => {
+ const mode = primary ? 'demo-button--primary' : 'demo-button--secondary';
+ return (
+
+ );
+};
diff --git a/packages/create-rslib/template-[react]-[rstest]-ts/src/button.css b/packages/create-rslib/template-[react]-[rstest]-ts/src/button.css
new file mode 100644
index 000000000..b7a36cbde
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest]-ts/src/button.css
@@ -0,0 +1,34 @@
+.demo-button {
+ font-weight: 700;
+ border: 0;
+ border-radius: 3em;
+ cursor: pointer;
+ display: inline-block;
+ line-height: 1;
+}
+
+.demo-button--primary {
+ color: white;
+ background-color: #1ea7fd;
+}
+
+.demo-button--secondary {
+ color: #333;
+ background-color: transparent;
+ box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset;
+}
+
+.demo-button--small {
+ font-size: 12px;
+ padding: 10px 16px;
+}
+
+.demo-button--medium {
+ font-size: 14px;
+ padding: 11px 20px;
+}
+
+.demo-button--large {
+ font-size: 16px;
+ padding: 12px 24px;
+}
diff --git a/packages/create-rslib/template-[react]-[rstest]-ts/src/index.tsx b/packages/create-rslib/template-[react]-[rstest]-ts/src/index.tsx
new file mode 100644
index 000000000..fe9c53c51
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest]-ts/src/index.tsx
@@ -0,0 +1 @@
+export { Button } from './Button';
diff --git a/packages/create-rslib/template-[react]-[rstest]-ts/tests/index.test.tsx b/packages/create-rslib/template-[react]-[rstest]-ts/tests/index.test.tsx
new file mode 100644
index 000000000..91e7bde86
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest]-ts/tests/index.test.tsx
@@ -0,0 +1,11 @@
+import { expect, test } from '@rstest/core';
+import { render, screen } from '@testing-library/react';
+import { Button } from '../src/Button';
+
+test('The button should have correct background color', async () => {
+ render();
+ const button = screen.getByText('Demo Button');
+ expect(button).toHaveStyle({
+ backgroundColor: '#ccc',
+ });
+});
diff --git a/packages/create-rslib/template-[react]-[rstest]-ts/tests/test.d.ts b/packages/create-rslib/template-[react]-[rstest]-ts/tests/test.d.ts
new file mode 100755
index 000000000..e48dee965
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest]-ts/tests/test.d.ts
@@ -0,0 +1 @@
+///
diff --git a/packages/create-rslib/template-[react]-[rstest]-ts/tests/tsconfig.json b/packages/create-rslib/template-[react]-[rstest]-ts/tests/tsconfig.json
new file mode 100644
index 000000000..8935f17ed
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest]-ts/tests/tsconfig.json
@@ -0,0 +1,4 @@
+{
+ "extends": "../tsconfig.json",
+ "include": [".", "../rstest.setup.ts"]
+}
diff --git a/packages/create-rslib/template-[react]-[rstest]-ts/tsconfig.json b/packages/create-rslib/template-[react]-[rstest]-ts/tsconfig.json
new file mode 100644
index 000000000..510ab68df
--- /dev/null
+++ b/packages/create-rslib/template-[react]-[rstest]-ts/tsconfig.json
@@ -0,0 +1,14 @@
+{
+ "compilerOptions": {
+ "lib": ["DOM", "ES2022"],
+ "module": "ESNext",
+ "jsx": "react-jsx",
+ "strict": true,
+ "skipLibCheck": true,
+ "isolatedModules": true,
+ "resolveJsonModule": true,
+ "moduleResolution": "bundler",
+ "useDefineForClassFields": true
+ },
+ "include": ["src"]
+}
diff --git a/packages/create-rslib/template-[vue]-[rstest,storybook]-js/.storybook/main.js b/packages/create-rslib/template-[vue]-[rstest,storybook]-js/.storybook/main.js
new file mode 100644
index 000000000..73b7e2e96
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest,storybook]-js/.storybook/main.js
@@ -0,0 +1,35 @@
+import { dirname, join } from 'node:path';
+
+/**
+ * This function is used to resolve the absolute path of a package.
+ * It is needed in projects that use Yarn PnP or are set up within a monorepo.
+ */
+function getAbsolutePath(value) {
+ return dirname(require.resolve(join(value, 'package.json')));
+}
+
+const config = {
+ stories: [
+ '../stories/**/*.mdx',
+ '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)',
+ ],
+ addons: [
+ '@storybook/addon-docs',
+ '@storybook/addon-onboarding',
+ '@storybook/addon-links',
+ '@storybook/addon-essentials',
+ '@storybook/addon-interactions',
+ {
+ name: getAbsolutePath('storybook-addon-rslib'),
+ },
+ ],
+ framework: {
+ name: getAbsolutePath('storybook-vue3-rsbuild'),
+ options: {},
+ },
+ typescript: {
+ check: true,
+ },
+};
+
+export default config;
diff --git a/packages/create-rslib/template-[vue]-[rstest,storybook]-js/.storybook/preview.js b/packages/create-rslib/template-[vue]-[rstest,storybook]-js/.storybook/preview.js
new file mode 100644
index 000000000..0853b53bd
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest,storybook]-js/.storybook/preview.js
@@ -0,0 +1,12 @@
+const preview = {
+ parameters: {
+ controls: {
+ matchers: {
+ color: /(background|color)$/i,
+ date: /Date$/i,
+ },
+ },
+ },
+};
+
+export default preview;
diff --git a/packages/create-rslib/template-[vue]-[rstest,storybook]-js/package.json b/packages/create-rslib/template-[vue]-[rstest,storybook]-js/package.json
new file mode 100644
index 000000000..045f1b52d
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest,storybook]-js/package.json
@@ -0,0 +1,47 @@
+{
+ "name": "rslib-vue-js",
+ "version": "0.0.0",
+ "type": "module",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.js"
+ }
+ },
+ "types": "./dist/index.d.ts",
+ "files": [
+ "dist"
+ ],
+ "scripts": {
+ "build": "rslib build",
+ "build:storybook": "storybook build",
+ "dev": "rslib build --watch",
+ "storybook": "storybook dev",
+ "test": "rstest"
+ },
+ "devDependencies": {
+ "@rsbuild/core": "1.5.0-beta.0",
+ "@rslib/core": "workspace:*",
+ "@rstest/core": "^0.1.3",
+ "@storybook/addon-docs": "^9.1.1",
+ "@storybook/addon-essentials": "^9.0.0-alpha.12",
+ "@storybook/addon-interactions": "^9.0.0-alpha.10",
+ "@storybook/addon-links": "^9.1.1",
+ "@storybook/addon-onboarding": "^9.1.1",
+ "@storybook/blocks": "^9.0.0-alpha.17",
+ "@storybook/test": "^9.0.0-alpha.2",
+ "@storybook/vue3": "^9.1.1",
+ "@testing-library/jest-dom": "^6.6.4",
+ "@testing-library/vue": "^8.0.0",
+ "@vue/test-utils": "^2.4.0",
+ "jsdom": "^26.1.0",
+ "rsbuild-plugin-unplugin-vue": "^0.1.0",
+ "storybook": "^9.1.1",
+ "storybook-addon-rslib": "^2.0.3",
+ "storybook-vue3-rsbuild": "^2.0.3",
+ "vue": "^3.5.18"
+ },
+ "peerDependencies": {
+ "vue": "^3.2.0"
+ }
+}
diff --git a/packages/create-rslib/template-[vue]-[rstest,storybook]-js/rslib.config.ts b/packages/create-rslib/template-[vue]-[rstest,storybook]-js/rslib.config.ts
new file mode 100644
index 000000000..a5290d235
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest,storybook]-js/rslib.config.ts
@@ -0,0 +1,15 @@
+import { defineConfig } from '@rslib/core';
+import { pluginUnpluginVue } from 'rsbuild-plugin-unplugin-vue';
+
+export default defineConfig({
+ lib: [
+ {
+ bundle: false,
+ format: 'esm',
+ },
+ ],
+ output: {
+ target: 'web',
+ },
+ plugins: [pluginUnpluginVue()],
+});
diff --git a/packages/create-rslib/template-[vue]-[rstest,storybook]-js/rstest.config.js b/packages/create-rslib/template-[vue]-[rstest,storybook]-js/rstest.config.js
new file mode 100644
index 000000000..5dfd87ba2
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest,storybook]-js/rstest.config.js
@@ -0,0 +1,8 @@
+import { defineConfig } from '@rstest/core';
+import { pluginUnpluginVue } from 'rsbuild-plugin-unplugin-vue';
+
+export default defineConfig({
+ testEnvironment: 'jsdom',
+ setupFiles: ['./rstest.setup.js'],
+ plugins: [pluginUnpluginVue()],
+});
diff --git a/packages/create-rslib/template-[vue]-[rstest,storybook]-js/rstest.setup.js b/packages/create-rslib/template-[vue]-[rstest,storybook]-js/rstest.setup.js
new file mode 100644
index 000000000..32b38ae28
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest,storybook]-js/rstest.setup.js
@@ -0,0 +1,4 @@
+import { expect } from '@rstest/core';
+import * as jestDomMatchers from '@testing-library/jest-dom/matchers';
+
+expect.extend(jestDomMatchers);
diff --git a/packages/create-rslib/template-[vue]-[rstest,storybook]-js/src/Button.vue b/packages/create-rslib/template-[vue]-[rstest,storybook]-js/src/Button.vue
new file mode 100644
index 000000000..49ec53881
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest,storybook]-js/src/Button.vue
@@ -0,0 +1,43 @@
+
+
+
+
+
diff --git a/packages/create-rslib/template-[vue]-[rstest,storybook]-js/src/index.js b/packages/create-rslib/template-[vue]-[rstest,storybook]-js/src/index.js
new file mode 100644
index 000000000..652e7950f
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest,storybook]-js/src/index.js
@@ -0,0 +1 @@
+export { default as Button } from './Button.vue';
diff --git a/packages/create-rslib/template-[vue]-[rstest,storybook]-js/src/style.css b/packages/create-rslib/template-[vue]-[rstest,storybook]-js/src/style.css
new file mode 100644
index 000000000..b7a36cbde
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest,storybook]-js/src/style.css
@@ -0,0 +1,34 @@
+.demo-button {
+ font-weight: 700;
+ border: 0;
+ border-radius: 3em;
+ cursor: pointer;
+ display: inline-block;
+ line-height: 1;
+}
+
+.demo-button--primary {
+ color: white;
+ background-color: #1ea7fd;
+}
+
+.demo-button--secondary {
+ color: #333;
+ background-color: transparent;
+ box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset;
+}
+
+.demo-button--small {
+ font-size: 12px;
+ padding: 10px 16px;
+}
+
+.demo-button--medium {
+ font-size: 14px;
+ padding: 11px 20px;
+}
+
+.demo-button--large {
+ font-size: 16px;
+ padding: 12px 24px;
+}
diff --git a/packages/create-rslib/template-[vue]-[rstest,storybook]-js/stories/Button.stories.js b/packages/create-rslib/template-[vue]-[rstest,storybook]-js/stories/Button.stories.js
new file mode 100644
index 000000000..fc5c64f18
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest,storybook]-js/stories/Button.stories.js
@@ -0,0 +1,50 @@
+import { fn } from '@storybook/test';
+import Button from '../src/Button';
+
+// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
+const meta = {
+ title: 'Example/Button',
+ component: Button,
+ parameters: {
+ // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
+ layout: 'centered',
+ },
+ // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
+ tags: ['autodocs'],
+ // More on argTypes: https://storybook.js.org/docs/api/argtypes
+ argTypes: {
+ backgroundColor: { control: 'color' },
+ },
+ // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
+ args: { onClick: fn() },
+};
+
+export default meta;
+
+// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
+export const Primary = {
+ args: {
+ primary: true,
+ label: 'Button',
+ },
+};
+
+export const Secondary = {
+ args: {
+ label: 'Button',
+ },
+};
+
+export const Large = {
+ args: {
+ size: 'large',
+ label: 'Button',
+ },
+};
+
+export const Small = {
+ args: {
+ size: 'small',
+ label: 'Button',
+ },
+};
diff --git a/packages/create-rslib/template-[vue]-[rstest,storybook]-js/tests/index.test.js b/packages/create-rslib/template-[vue]-[rstest,storybook]-js/tests/index.test.js
new file mode 100644
index 000000000..096747aa8
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest,storybook]-js/tests/index.test.js
@@ -0,0 +1,16 @@
+import { expect, test } from '@rstest/core';
+import { render, screen } from '@testing-library/vue';
+import Button from '../src/Button.vue';
+
+test('The button should have correct background color', async () => {
+ render(Button, {
+ props: {
+ backgroundColor: '#ccc',
+ label: 'Demo Button',
+ },
+ });
+ const button = screen.getByText('Demo Button');
+ expect(button).toHaveStyle({
+ backgroundColor: '#ccc',
+ });
+});
diff --git a/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/.storybook/main.ts b/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/.storybook/main.ts
new file mode 100644
index 000000000..6066b129b
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/.storybook/main.ts
@@ -0,0 +1,36 @@
+import { dirname, join } from 'node:path';
+import type { StorybookConfig } from 'storybook-vue3-rsbuild';
+
+/**
+ * This function is used to resolve the absolute path of a package.
+ * It is needed in projects that use Yarn PnP or are set up within a monorepo.
+ */
+function getAbsolutePath(value) {
+ return dirname(require.resolve(join(value, 'package.json')));
+}
+
+const config: StorybookConfig = {
+ stories: [
+ '../stories/**/*.mdx',
+ '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)',
+ ],
+ addons: [
+ '@storybook/addon-docs',
+ '@storybook/addon-onboarding',
+ '@storybook/addon-links',
+ '@storybook/addon-essentials',
+ '@storybook/addon-interactions',
+ {
+ name: getAbsolutePath('storybook-addon-rslib'),
+ },
+ ],
+ framework: {
+ name: getAbsolutePath('storybook-vue3-rsbuild'),
+ options: {},
+ },
+ typescript: {
+ check: true,
+ },
+};
+
+export default config;
diff --git a/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/.storybook/preview.ts b/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/.storybook/preview.ts
new file mode 100644
index 000000000..0853b53bd
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/.storybook/preview.ts
@@ -0,0 +1,12 @@
+const preview = {
+ parameters: {
+ controls: {
+ matchers: {
+ color: /(background|color)$/i,
+ date: /Date$/i,
+ },
+ },
+ },
+};
+
+export default preview;
diff --git a/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/package.json b/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/package.json
new file mode 100644
index 000000000..002e92394
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/package.json
@@ -0,0 +1,49 @@
+{
+ "name": "rslib-vue-ts",
+ "version": "0.0.0",
+ "type": "module",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.js"
+ }
+ },
+ "types": "./dist/index.d.ts",
+ "files": [
+ "dist"
+ ],
+ "scripts": {
+ "build": "rslib build && vue-tsc",
+ "build:storybook": "storybook build",
+ "dev": "rslib build --watch",
+ "storybook": "storybook dev",
+ "test": "rstest"
+ },
+ "devDependencies": {
+ "@rsbuild/core": "1.5.0-beta.0",
+ "@rslib/core": "workspace:*",
+ "@rstest/core": "^0.1.3",
+ "@storybook/addon-docs": "^9.1.1",
+ "@storybook/addon-essentials": "^9.0.0-alpha.12",
+ "@storybook/addon-interactions": "^9.0.0-alpha.10",
+ "@storybook/addon-links": "^9.1.1",
+ "@storybook/addon-onboarding": "^9.1.1",
+ "@storybook/blocks": "^9.0.0-alpha.17",
+ "@storybook/test": "^9.0.0-alpha.2",
+ "@storybook/vue3": "^9.1.1",
+ "@testing-library/jest-dom": "^6.6.4",
+ "@testing-library/vue": "^8.0.0",
+ "@vue/test-utils": "^2.4.0",
+ "jsdom": "^26.1.0",
+ "rsbuild-plugin-unplugin-vue": "^0.1.0",
+ "storybook": "^9.1.1",
+ "storybook-addon-rslib": "^2.0.3",
+ "storybook-vue3-rsbuild": "^2.0.3",
+ "typescript": "^5.9.2",
+ "vue": "^3.5.18",
+ "vue-tsc": "^3.0.5"
+ },
+ "peerDependencies": {
+ "vue": "^3.2.0"
+ }
+}
diff --git a/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/rslib.config.ts b/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/rslib.config.ts
new file mode 100644
index 000000000..a5290d235
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/rslib.config.ts
@@ -0,0 +1,15 @@
+import { defineConfig } from '@rslib/core';
+import { pluginUnpluginVue } from 'rsbuild-plugin-unplugin-vue';
+
+export default defineConfig({
+ lib: [
+ {
+ bundle: false,
+ format: 'esm',
+ },
+ ],
+ output: {
+ target: 'web',
+ },
+ plugins: [pluginUnpluginVue()],
+});
diff --git a/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/rstest.config.ts b/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/rstest.config.ts
new file mode 100644
index 000000000..c0c74d0c8
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/rstest.config.ts
@@ -0,0 +1,8 @@
+import { defineConfig } from '@rstest/core';
+import { pluginUnpluginVue } from 'rsbuild-plugin-unplugin-vue';
+
+export default defineConfig({
+ testEnvironment: 'jsdom',
+ setupFiles: ['./rstest.setup.ts'],
+ plugins: [pluginUnpluginVue()],
+});
diff --git a/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/rstest.setup.ts b/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/rstest.setup.ts
new file mode 100644
index 000000000..32b38ae28
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/rstest.setup.ts
@@ -0,0 +1,4 @@
+import { expect } from '@rstest/core';
+import * as jestDomMatchers from '@testing-library/jest-dom/matchers';
+
+expect.extend(jestDomMatchers);
diff --git a/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/src/Button.vue b/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/src/Button.vue
new file mode 100644
index 000000000..9d84eec2c
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/src/Button.vue
@@ -0,0 +1,35 @@
+
+
+
+
+
diff --git a/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/src/index.ts b/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/src/index.ts
new file mode 100644
index 000000000..652e7950f
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/src/index.ts
@@ -0,0 +1 @@
+export { default as Button } from './Button.vue';
diff --git a/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/src/style.css b/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/src/style.css
new file mode 100644
index 000000000..b7a36cbde
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/src/style.css
@@ -0,0 +1,34 @@
+.demo-button {
+ font-weight: 700;
+ border: 0;
+ border-radius: 3em;
+ cursor: pointer;
+ display: inline-block;
+ line-height: 1;
+}
+
+.demo-button--primary {
+ color: white;
+ background-color: #1ea7fd;
+}
+
+.demo-button--secondary {
+ color: #333;
+ background-color: transparent;
+ box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset;
+}
+
+.demo-button--small {
+ font-size: 12px;
+ padding: 10px 16px;
+}
+
+.demo-button--medium {
+ font-size: 14px;
+ padding: 11px 20px;
+}
+
+.demo-button--large {
+ font-size: 16px;
+ padding: 12px 24px;
+}
diff --git a/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/stories/Button.stories.js b/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/stories/Button.stories.js
new file mode 100644
index 000000000..fc5c64f18
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/stories/Button.stories.js
@@ -0,0 +1,50 @@
+import { fn } from '@storybook/test';
+import Button from '../src/Button';
+
+// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
+const meta = {
+ title: 'Example/Button',
+ component: Button,
+ parameters: {
+ // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
+ layout: 'centered',
+ },
+ // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
+ tags: ['autodocs'],
+ // More on argTypes: https://storybook.js.org/docs/api/argtypes
+ argTypes: {
+ backgroundColor: { control: 'color' },
+ },
+ // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
+ args: { onClick: fn() },
+};
+
+export default meta;
+
+// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
+export const Primary = {
+ args: {
+ primary: true,
+ label: 'Button',
+ },
+};
+
+export const Secondary = {
+ args: {
+ label: 'Button',
+ },
+};
+
+export const Large = {
+ args: {
+ size: 'large',
+ label: 'Button',
+ },
+};
+
+export const Small = {
+ args: {
+ size: 'small',
+ label: 'Button',
+ },
+};
diff --git a/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/tests/index.test.ts b/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/tests/index.test.ts
new file mode 100644
index 000000000..096747aa8
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/tests/index.test.ts
@@ -0,0 +1,16 @@
+import { expect, test } from '@rstest/core';
+import { render, screen } from '@testing-library/vue';
+import Button from '../src/Button.vue';
+
+test('The button should have correct background color', async () => {
+ render(Button, {
+ props: {
+ backgroundColor: '#ccc',
+ label: 'Demo Button',
+ },
+ });
+ const button = screen.getByText('Demo Button');
+ expect(button).toHaveStyle({
+ backgroundColor: '#ccc',
+ });
+});
diff --git a/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/tests/test.d.ts b/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/tests/test.d.ts
new file mode 100755
index 000000000..e48dee965
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/tests/test.d.ts
@@ -0,0 +1 @@
+///
diff --git a/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/tests/tsconfig.json b/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/tests/tsconfig.json
new file mode 100644
index 000000000..8935f17ed
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/tests/tsconfig.json
@@ -0,0 +1,4 @@
+{
+ "extends": "../tsconfig.json",
+ "include": [".", "../rstest.setup.ts"]
+}
diff --git a/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/tsconfig.json b/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/tsconfig.json
new file mode 100644
index 000000000..86a389fe0
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest,storybook]-ts/tsconfig.json
@@ -0,0 +1,24 @@
+{
+ "compilerOptions": {
+ "lib": ["DOM", "ES2022"],
+ "jsx": "preserve",
+ "skipLibCheck": true,
+ "jsxImportSource": "vue",
+ "useDefineForClassFields": true,
+ "declaration": true,
+ "outDir": "dist",
+ "emitDeclarationOnly": true,
+
+ /* modules */
+ "module": "ESNext",
+ "isolatedModules": true,
+ "resolveJsonModule": true,
+ "moduleResolution": "bundler",
+
+ /* type checking */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true
+ },
+ "include": ["src"]
+}
diff --git a/packages/create-rslib/template-[vue]-[rstest]-js/package.json b/packages/create-rslib/template-[vue]-[rstest]-js/package.json
new file mode 100644
index 000000000..830985a9a
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest]-js/package.json
@@ -0,0 +1,33 @@
+{
+ "name": "rslib-vue-js",
+ "version": "0.0.0",
+ "type": "module",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.js"
+ }
+ },
+ "types": "./dist/index.d.ts",
+ "files": [
+ "dist"
+ ],
+ "scripts": {
+ "build": "rslib build",
+ "dev": "rslib build --watch",
+ "test": "rstest"
+ },
+ "devDependencies": {
+ "@rslib/core": "workspace:*",
+ "@rstest/core": "^0.1.3",
+ "@testing-library/jest-dom": "^6.6.4",
+ "@testing-library/vue": "^8.0.0",
+ "@vue/test-utils": "^2.4.0",
+ "jsdom": "^26.1.0",
+ "rsbuild-plugin-unplugin-vue": "^0.1.0",
+ "vue": "^3.5.18"
+ },
+ "peerDependencies": {
+ "vue": "^3.2.0"
+ }
+}
diff --git a/packages/create-rslib/template-[vue]-[rstest]-js/rslib.config.ts b/packages/create-rslib/template-[vue]-[rstest]-js/rslib.config.ts
new file mode 100644
index 000000000..a5290d235
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest]-js/rslib.config.ts
@@ -0,0 +1,15 @@
+import { defineConfig } from '@rslib/core';
+import { pluginUnpluginVue } from 'rsbuild-plugin-unplugin-vue';
+
+export default defineConfig({
+ lib: [
+ {
+ bundle: false,
+ format: 'esm',
+ },
+ ],
+ output: {
+ target: 'web',
+ },
+ plugins: [pluginUnpluginVue()],
+});
diff --git a/packages/create-rslib/template-[vue]-[rstest]-js/rstest.config.js b/packages/create-rslib/template-[vue]-[rstest]-js/rstest.config.js
new file mode 100644
index 000000000..5dfd87ba2
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest]-js/rstest.config.js
@@ -0,0 +1,8 @@
+import { defineConfig } from '@rstest/core';
+import { pluginUnpluginVue } from 'rsbuild-plugin-unplugin-vue';
+
+export default defineConfig({
+ testEnvironment: 'jsdom',
+ setupFiles: ['./rstest.setup.js'],
+ plugins: [pluginUnpluginVue()],
+});
diff --git a/packages/create-rslib/template-[vue]-[rstest]-js/rstest.setup.js b/packages/create-rslib/template-[vue]-[rstest]-js/rstest.setup.js
new file mode 100644
index 000000000..32b38ae28
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest]-js/rstest.setup.js
@@ -0,0 +1,4 @@
+import { expect } from '@rstest/core';
+import * as jestDomMatchers from '@testing-library/jest-dom/matchers';
+
+expect.extend(jestDomMatchers);
diff --git a/packages/create-rslib/template-[vue]-[rstest]-js/src/Button.vue b/packages/create-rslib/template-[vue]-[rstest]-js/src/Button.vue
new file mode 100644
index 000000000..49ec53881
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest]-js/src/Button.vue
@@ -0,0 +1,43 @@
+
+
+
+
+
diff --git a/packages/create-rslib/template-[vue]-[rstest]-js/src/index.js b/packages/create-rslib/template-[vue]-[rstest]-js/src/index.js
new file mode 100644
index 000000000..652e7950f
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest]-js/src/index.js
@@ -0,0 +1 @@
+export { default as Button } from './Button.vue';
diff --git a/packages/create-rslib/template-[vue]-[rstest]-js/src/style.css b/packages/create-rslib/template-[vue]-[rstest]-js/src/style.css
new file mode 100644
index 000000000..b7a36cbde
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest]-js/src/style.css
@@ -0,0 +1,34 @@
+.demo-button {
+ font-weight: 700;
+ border: 0;
+ border-radius: 3em;
+ cursor: pointer;
+ display: inline-block;
+ line-height: 1;
+}
+
+.demo-button--primary {
+ color: white;
+ background-color: #1ea7fd;
+}
+
+.demo-button--secondary {
+ color: #333;
+ background-color: transparent;
+ box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset;
+}
+
+.demo-button--small {
+ font-size: 12px;
+ padding: 10px 16px;
+}
+
+.demo-button--medium {
+ font-size: 14px;
+ padding: 11px 20px;
+}
+
+.demo-button--large {
+ font-size: 16px;
+ padding: 12px 24px;
+}
diff --git a/packages/create-rslib/template-[vue]-[rstest]-js/tests/index.test.js b/packages/create-rslib/template-[vue]-[rstest]-js/tests/index.test.js
new file mode 100644
index 000000000..096747aa8
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest]-js/tests/index.test.js
@@ -0,0 +1,16 @@
+import { expect, test } from '@rstest/core';
+import { render, screen } from '@testing-library/vue';
+import Button from '../src/Button.vue';
+
+test('The button should have correct background color', async () => {
+ render(Button, {
+ props: {
+ backgroundColor: '#ccc',
+ label: 'Demo Button',
+ },
+ });
+ const button = screen.getByText('Demo Button');
+ expect(button).toHaveStyle({
+ backgroundColor: '#ccc',
+ });
+});
diff --git a/packages/create-rslib/template-[vue]-[rstest]-ts/package.json b/packages/create-rslib/template-[vue]-[rstest]-ts/package.json
new file mode 100644
index 000000000..5211817b1
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest]-ts/package.json
@@ -0,0 +1,35 @@
+{
+ "name": "rslib-vue-ts",
+ "version": "0.0.0",
+ "type": "module",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.js"
+ }
+ },
+ "types": "./dist/index.d.ts",
+ "files": [
+ "dist"
+ ],
+ "scripts": {
+ "build": "rslib build && vue-tsc",
+ "dev": "rslib build --watch",
+ "test": "rstest"
+ },
+ "devDependencies": {
+ "@rslib/core": "workspace:*",
+ "@rstest/core": "^0.1.3",
+ "@testing-library/jest-dom": "^6.6.4",
+ "@testing-library/vue": "^8.0.0",
+ "@vue/test-utils": "^2.4.0",
+ "jsdom": "^26.1.0",
+ "rsbuild-plugin-unplugin-vue": "^0.1.0",
+ "typescript": "^5.9.2",
+ "vue": "^3.5.18",
+ "vue-tsc": "^3.0.5"
+ },
+ "peerDependencies": {
+ "vue": "^3.2.0"
+ }
+}
diff --git a/packages/create-rslib/template-[vue]-[rstest]-ts/rslib.config.ts b/packages/create-rslib/template-[vue]-[rstest]-ts/rslib.config.ts
new file mode 100644
index 000000000..a5290d235
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest]-ts/rslib.config.ts
@@ -0,0 +1,15 @@
+import { defineConfig } from '@rslib/core';
+import { pluginUnpluginVue } from 'rsbuild-plugin-unplugin-vue';
+
+export default defineConfig({
+ lib: [
+ {
+ bundle: false,
+ format: 'esm',
+ },
+ ],
+ output: {
+ target: 'web',
+ },
+ plugins: [pluginUnpluginVue()],
+});
diff --git a/packages/create-rslib/template-[vue]-[rstest]-ts/rstest.config.ts b/packages/create-rslib/template-[vue]-[rstest]-ts/rstest.config.ts
new file mode 100644
index 000000000..c0c74d0c8
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest]-ts/rstest.config.ts
@@ -0,0 +1,8 @@
+import { defineConfig } from '@rstest/core';
+import { pluginUnpluginVue } from 'rsbuild-plugin-unplugin-vue';
+
+export default defineConfig({
+ testEnvironment: 'jsdom',
+ setupFiles: ['./rstest.setup.ts'],
+ plugins: [pluginUnpluginVue()],
+});
diff --git a/packages/create-rslib/template-[vue]-[rstest]-ts/rstest.setup.ts b/packages/create-rslib/template-[vue]-[rstest]-ts/rstest.setup.ts
new file mode 100644
index 000000000..32b38ae28
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest]-ts/rstest.setup.ts
@@ -0,0 +1,4 @@
+import { expect } from '@rstest/core';
+import * as jestDomMatchers from '@testing-library/jest-dom/matchers';
+
+expect.extend(jestDomMatchers);
diff --git a/packages/create-rslib/template-[vue]-[rstest]-ts/src/Button.vue b/packages/create-rslib/template-[vue]-[rstest]-ts/src/Button.vue
new file mode 100644
index 000000000..9d84eec2c
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest]-ts/src/Button.vue
@@ -0,0 +1,35 @@
+
+
+
+
+
diff --git a/packages/create-rslib/template-[vue]-[rstest]-ts/src/index.ts b/packages/create-rslib/template-[vue]-[rstest]-ts/src/index.ts
new file mode 100644
index 000000000..652e7950f
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest]-ts/src/index.ts
@@ -0,0 +1 @@
+export { default as Button } from './Button.vue';
diff --git a/packages/create-rslib/template-[vue]-[rstest]-ts/src/style.css b/packages/create-rslib/template-[vue]-[rstest]-ts/src/style.css
new file mode 100644
index 000000000..b7a36cbde
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest]-ts/src/style.css
@@ -0,0 +1,34 @@
+.demo-button {
+ font-weight: 700;
+ border: 0;
+ border-radius: 3em;
+ cursor: pointer;
+ display: inline-block;
+ line-height: 1;
+}
+
+.demo-button--primary {
+ color: white;
+ background-color: #1ea7fd;
+}
+
+.demo-button--secondary {
+ color: #333;
+ background-color: transparent;
+ box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset;
+}
+
+.demo-button--small {
+ font-size: 12px;
+ padding: 10px 16px;
+}
+
+.demo-button--medium {
+ font-size: 14px;
+ padding: 11px 20px;
+}
+
+.demo-button--large {
+ font-size: 16px;
+ padding: 12px 24px;
+}
diff --git a/packages/create-rslib/template-[vue]-[rstest]-ts/tests/index.test.ts b/packages/create-rslib/template-[vue]-[rstest]-ts/tests/index.test.ts
new file mode 100644
index 000000000..096747aa8
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest]-ts/tests/index.test.ts
@@ -0,0 +1,16 @@
+import { expect, test } from '@rstest/core';
+import { render, screen } from '@testing-library/vue';
+import Button from '../src/Button.vue';
+
+test('The button should have correct background color', async () => {
+ render(Button, {
+ props: {
+ backgroundColor: '#ccc',
+ label: 'Demo Button',
+ },
+ });
+ const button = screen.getByText('Demo Button');
+ expect(button).toHaveStyle({
+ backgroundColor: '#ccc',
+ });
+});
diff --git a/packages/create-rslib/template-[vue]-[rstest]-ts/tests/test.d.ts b/packages/create-rslib/template-[vue]-[rstest]-ts/tests/test.d.ts
new file mode 100755
index 000000000..e48dee965
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest]-ts/tests/test.d.ts
@@ -0,0 +1 @@
+///
diff --git a/packages/create-rslib/template-[vue]-[rstest]-ts/tests/tsconfig.json b/packages/create-rslib/template-[vue]-[rstest]-ts/tests/tsconfig.json
new file mode 100644
index 000000000..8935f17ed
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest]-ts/tests/tsconfig.json
@@ -0,0 +1,4 @@
+{
+ "extends": "../tsconfig.json",
+ "include": [".", "../rstest.setup.ts"]
+}
diff --git a/packages/create-rslib/template-[vue]-[rstest]-ts/tsconfig.json b/packages/create-rslib/template-[vue]-[rstest]-ts/tsconfig.json
new file mode 100644
index 000000000..86a389fe0
--- /dev/null
+++ b/packages/create-rslib/template-[vue]-[rstest]-ts/tsconfig.json
@@ -0,0 +1,24 @@
+{
+ "compilerOptions": {
+ "lib": ["DOM", "ES2022"],
+ "jsx": "preserve",
+ "skipLibCheck": true,
+ "jsxImportSource": "vue",
+ "useDefineForClassFields": true,
+ "declaration": true,
+ "outDir": "dist",
+ "emitDeclarationOnly": true,
+
+ /* modules */
+ "module": "ESNext",
+ "isolatedModules": true,
+ "resolveJsonModule": true,
+ "moduleResolution": "bundler",
+
+ /* type checking */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true
+ },
+ "include": ["src"]
+}
diff --git a/packages/create-rslib/test/index.test.ts b/packages/create-rslib/test/index.test.ts
index a6388b630..4e4005d66 100644
--- a/packages/create-rslib/test/index.test.ts
+++ b/packages/create-rslib/test/index.test.ts
@@ -7,6 +7,8 @@ import { createAndValidate } from './helper';
const CASES_NODE_DUAL = [
'[node-dual]-[]-js',
'[node-dual]-[]-ts',
+ '[node-dual]-[rstest]-js',
+ '[node-dual]-[rstest]-ts',
'[node-dual]-[vitest]-js',
'[node-dual]-[vitest]-ts',
];
@@ -14,6 +16,8 @@ const CASES_NODE_DUAL = [
const CASES_NODE_ESM = [
'[node-esm]-[]-js',
'[node-esm]-[]-ts',
+ '[node-esm]-[rstest]-js',
+ '[node-esm]-[rstest]-ts',
'[node-esm]-[vitest]-js',
'[node-esm]-[vitest]-ts',
];
@@ -21,27 +25,43 @@ const CASES_NODE_ESM = [
const CASES_REACT = [
'[react]-[]-js',
'[react]-[]-ts',
+ '[react]-[rstest,storybook]-js',
+ '[react]-[rstest,storybook]-ts',
'[react]-[storybook,vitest]-js',
'[react]-[storybook,vitest]-ts',
'[react]-[storybook]-js',
+ '[react]-[rstest]-js',
'[react]-[vitest]-js',
'[react]-[storybook]-ts',
+ '[react]-[rstest]-ts',
'[react]-[vitest]-ts',
];
const CASES_VUE = [
'[vue]-[]-js',
'[vue]-[]-ts',
+ '[vue]-[rstest,storybook]-js',
+ '[vue]-[rstest,storybook]-ts',
'[vue]-[storybook,vitest]-js',
'[vue]-[storybook,vitest]-ts',
'[vue]-[storybook]-js',
'[vue]-[storybook]-ts',
+ '[vue]-[rstest]-js',
+ '[vue]-[rstest]-ts',
'[vue]-[vitest]-js',
'[vue]-[vitest]-ts',
];
test('exhaust all cases', () => {
- expect(TEMPLATES.map(composeTemplateName).sort()).toEqual(
+ expect(
+ TEMPLATES.map((t) =>
+ composeTemplateName({
+ template: t.template,
+ lang: t.lang,
+ tools: Object.keys(t.tools || {}),
+ }),
+ ).sort(),
+ ).toEqual(
[
...CASES_NODE_DUAL,
...CASES_NODE_ESM,
diff --git a/website/docs/en/guide/start/quick-start.mdx b/website/docs/en/guide/start/quick-start.mdx
index 4a5edd48e..95adce6f8 100644
--- a/website/docs/en/guide/start/quick-start.mdx
+++ b/website/docs/en/guide/start/quick-start.mdx
@@ -53,14 +53,15 @@ Each template supports both JavaScript and TypeScript, along with optional devel
### Development tools
-`create-rslib` can help you set up some commonly used development tools, including [Vitest](https://vitest.dev/), [Storybook](https://storybook.js.org/). You can use the arrow keys and the space bar to make your selections. If you don't need these tools, you can simply press Enter to skip.
+`create-rslib` can help you set up some commonly used development tools, including [Rstest](https://rstest.rs/), [Vitest](https://vitest.dev/), [Storybook](https://storybook.js.org/). You can use the arrow keys and the space bar to make your selections. If you don't need these tools, you can simply press Enter to skip.
-- Vitest is available for all templates, it will be adapted based on the template's selection.
+- Rstest and Vitest are available for all templates, it will be adapted based on the template's selection.
- Storybook is available for web targeted templates (React, Vue), it will be adapted based on the template's selection.
```text
◆ Select development tools (Use to select, to continue)
│ ◻ Storybook
+│ ◻ Rstest
│ ◻ Vitest
└
```
diff --git a/website/docs/zh/guide/start/quick-start.mdx b/website/docs/zh/guide/start/quick-start.mdx
index 8dbe366c2..ac57223fb 100644
--- a/website/docs/zh/guide/start/quick-start.mdx
+++ b/website/docs/zh/guide/start/quick-start.mdx
@@ -53,14 +53,15 @@ import { PackageManagerTabs } from '@theme';
### 开发工具
-`create-rslib` 可以帮助你设置一些常用的开发工具,包括 [Vitest](https://vitest.dev/)、[Storybook](https://storybook.js.org/)。你可以使用方向键和空格键进行选择。如果你不需要这些工具,只需按 Enter 跳过即可。
+`create-rslib` 可以帮助你设置一些常用的开发工具,包括 [Rstest](https://rstest.rs/)、[Vitest](https://vitest.dev/)、[Storybook](https://storybook.js.org/)。你可以使用方向键和空格键进行选择。如果你不需要这些工具,只需按 Enter 跳过即可。
-- Vitest 适用于所有模板,它将根据模板的选择进行调整。
+- Rstest / Vitest 适用于所有模板,它将根据模板的选择进行调整。
- Storybook 可用于 Web 目标模板 (React、Vue),它将根据模板的选择进行调整。
```text
◆ Select development tools (Use to select, to continue)
│ ◻ Storybook
+│ ◻ Rstest
│ ◻ Vitest
└
```