Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/create-rstack/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ npx create-rstack --dir my-project --template app-vanilla-ts --no-git
- `lib-solid-ts` - TypeScript Solid library
- `doc` - Basic documentation site
- `doc-i18n` - Multilingual documentation site
- `pnpm` - pnpm workspace
- `turborepo` - Turborepo
- `nx` - Nx

Expand Down
6 changes: 4 additions & 2 deletions packages/create-rstack/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const templateNames = [
'lib-solid-ts',
'doc',
'doc-i18n',
'pnpm',
'turborepo',
'nx',
];
Expand All @@ -57,7 +58,7 @@ const resolveTemplateName = (template: string): string => {

const getTemplateName = async ({ template }: Argv): Promise<string> => {
if (typeof template === 'string') {
if (/^(?:app|lib|doc|turborepo|nx)(?:-|$)/u.test(template)) {
if (/^(?:app|lib|doc|pnpm|turborepo|nx)(?:-|$)/u.test(template)) {
return resolveTemplateName(template);
}

Expand All @@ -81,6 +82,7 @@ const getTemplateName = async ({ template }: Argv): Promise<string> => {
await select({
message: 'Select monorepo tool',
options: [
{ value: 'pnpm', label: 'pnpm workspace' },
{ value: 'turborepo', label: 'Turborepo', hint: 'pnpm' },
{ value: 'nx', label: 'Nx', hint: 'pnpm' },
],
Expand Down Expand Up @@ -239,6 +241,6 @@ await create({
builtinTools: [],
getTemplateName,
getPackageManager: ({ templateName }) =>
['turborepo', 'nx'].includes(templateName) ? 'pnpm' : undefined,
['pnpm', 'turborepo', 'nx'].includes(templateName) ? 'pnpm' : undefined,
onGitResolved: injectStagedSetup,
});
33 changes: 33 additions & 0 deletions packages/create-rstack/template-pnpm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# pnpm workspace + Rstack

## Setup

```bash
pnpm install
```

## Structure

- `apps/web`: React application.
- `packages/ui`: Shared UI components.
- `tsconfig.base.json`: Shared TypeScript configuration.

## Scripts

Run from the repository root:

- `pnpm run build`: Build all packages in dependency order.
- `pnpm run dev`: Build shared packages, then start the web app and watch the shared UI package.
- `pnpm run test`: Build shared packages, then run tests.
- `pnpm run check`: Run lint, formatting, and TypeScript checks.
- `pnpm run lint`: Lint the workspace.
- `pnpm run format`: Format the workspace.

Run `pnpm run build` before `pnpm run check`.

Use `pnpm --filter <package-name> <script>` to run a script in one package.

## Learn more

- [Rstack CLI monorepo guide](https://rstack.rs/guide/monorepo)
- [pnpm workspace documentation](https://pnpm.io/workspaces)
26 changes: 26 additions & 0 deletions packages/create-rstack/template-pnpm/apps/web/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "web",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"build": "rs build",
"dev": "rs dev",
"preview": "rs preview",
"test": "rs test"
},
"dependencies": {
"@repo/ui": "workspace:^",
"react": "^19.3.0",
"react-dom": "^19.3.0"
},
"devDependencies": {
"@rsbuild/plugin-react": "^2.1.1",
"@testing-library/dom": "^10.4.2",
"@testing-library/jest-dom": "^7.0.1",
"@testing-library/react": "^16.3.3",
"@types/react": "^19.3.0",
"@types/react-dom": "^19.3.0",
"happy-dom": "^21.0.1"
}
}
13 changes: 13 additions & 0 deletions packages/create-rstack/template-pnpm/apps/web/rstack.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Configuration guide: https://rstack.rs/config
import { define } from 'rstack';

define.app(async () => {
const { pluginReact } = await import('@rsbuild/plugin-react');
return {
plugins: [pluginReact()],
};
});

define.test({
setupFiles: ['./tests/rstest.setup.ts'],
});
17 changes: 17 additions & 0 deletions packages/create-rstack/template-pnpm/apps/web/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Button } from '@repo/ui/button';

export function App() {
return (
<main>
<h1>Web</h1>
<p>pnpm workspace + Rstack CLI</p>
<p>Edit apps/web/src/App.tsx to get started.</p>
<Button onClick={() => window.alert('Hello from @repo/ui!')}>
Shared button
</Button>
<nav>
<a href="https://rstack.rs">Rstack documentation</a>
</nav>
</main>
);
}
27 changes: 27 additions & 0 deletions packages/create-rstack/template-pnpm/apps/web/src/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
body {
margin: 0;
font-family: system-ui, sans-serif;
color: #202124;
background: #f8f9fa;
}

main {
max-width: 640px;
margin: 15vh auto;
padding: 24px;
}

button {
padding: 10px 16px;
border: 0;
border-radius: 8px;
color: white;
background: #5755d9;
cursor: pointer;
}

nav {
display: flex;
gap: 20px;
margin-top: 24px;
}
13 changes: 13 additions & 0 deletions packages/create-rstack/template-pnpm/apps/web/src/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { App } from './App';
import './index.css';

const root = document.getElementById('root');
if (root) {
createRoot(root).render(
<StrictMode>
<App />
</StrictMode>,
);
}
11 changes: 11 additions & 0 deletions packages/create-rstack/template-pnpm/apps/web/tests/app.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { expect, test } from 'rstack/test';
import { render, screen } from '@testing-library/react';
import { App } from '../src/App';

test('renders the web app with the shared UI package', () => {
render(<App />);
expect(screen.getByRole('heading', { name: 'Web' })).toBeInTheDocument();
expect(
screen.getByRole('button', { name: 'Shared button' }),
).toBeInTheDocument();
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { expect } from 'rstack/test';
import * as jestDomMatchers from '@testing-library/jest-dom/matchers';

expect.extend(jestDomMatchers);
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"types": ["rstack/types", "node", "@testing-library/jest-dom"]
},
"include": ["./"]
}
4 changes: 4 additions & 0 deletions packages/create-rstack/template-pnpm/apps/web/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.base.json",
"include": ["src", "rstack.config.ts"]
}
13 changes: 13 additions & 0 deletions packages/create-rstack/template-pnpm/gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Local
.DS_Store
*.local
*.log*

# Dist
node_modules
dist/

# IDE
.vscode/*
!.vscode/extensions.json
.idea
19 changes: 19 additions & 0 deletions packages/create-rstack/template-pnpm/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "rstack-pnpm",
"private": true,
"type": "module",
"scripts": {
"build": "pnpm -r build",
"check": "rs check --type-check",
"dev": "pnpm --filter \"./packages/**\" build && pnpm -r --parallel dev",
"format": "rs fmt",
"lint": "rs lint",
"test": "pnpm --filter \"./packages/**\" build && pnpm -r test"
},
"devDependencies": {
"@types/node": "^26.6.5",
"rstack": "^0.8.4",
"typescript": "^7.0.2"
},
"packageManager": "pnpm@12.11.2"
}
30 changes: 30 additions & 0 deletions packages/create-rstack/template-pnpm/packages/ui/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "@repo/ui",
"version": "0.0.0",
"private": true,
"type": "module",
"exports": {
"./button": {
"types": "./dist/button.d.ts",
"default": "./dist/button.js"
}
},
"scripts": {
"build": "rs lib",
"dev": "rs lib --watch --no-clean",
"test": "rs test"
},
"dependencies": {
"react": "^19.3.0"
},
"devDependencies": {
"@rsbuild/plugin-react": "^2.1.1",
"@testing-library/dom": "^10.4.2",
"@testing-library/jest-dom": "^7.0.1",
"@testing-library/react": "^16.3.3",
"@types/react": "^19.3.0",
"@types/react-dom": "^19.3.0",
"happy-dom": "^21.0.1",
"react-dom": "^19.3.0"
}
}
20 changes: 20 additions & 0 deletions packages/create-rstack/template-pnpm/packages/ui/rstack.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Configuration guide: https://rstack.rs/config
import { define } from 'rstack';

define.lib(async () => {
const { pluginReact } = await import('@rsbuild/plugin-react');
return {
dts: true,
source: {
entry: { button: './src/button.tsx' },
},
output: {
target: 'web',
},
plugins: [pluginReact()],
};
});

define.test({
setupFiles: ['./tests/rstest.setup.ts'],
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { ComponentProps } from 'react';

export function Button({
type = 'button',
...props
}: ComponentProps<'button'>) {
return <button type={type} {...props} />;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { expect, test } from 'rstack/test';
import { render, screen } from '@testing-library/react';
import { Button } from '../src/button';

test('defaults to a non-submitting button and forwards props', () => {
render(<Button disabled>Save</Button>);
const button = screen.getByRole('button', { name: 'Save' });
expect(button).toHaveAttribute('type', 'button');
expect(button).toBeDisabled();
});

test('allows a submit button', () => {
render(<Button type="submit">Submit</Button>);
expect(screen.getByRole('button', { name: 'Submit' })).toHaveAttribute(
'type',
'submit',
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { expect } from 'rstack/test';
import * as jestDomMatchers from '@testing-library/jest-dom/matchers';

expect.extend(jestDomMatchers);
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"rootDir": "..",
"types": ["rstack/types", "node", "@testing-library/jest-dom"]
},
"include": ["./", "../rstack.config.ts"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"rootDir": "src"
},
"include": ["src"]
}
3 changes: 3 additions & 0 deletions packages/create-rstack/template-pnpm/pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
packages:
- apps/*
- packages/*
29 changes: 29 additions & 0 deletions packages/create-rstack/template-pnpm/rstack.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Configuration guide: https://rstack.rs/config
import { define } from 'rstack';

define.lint(({ js, ts, reactPlugin, reactHooksPlugin, rstestPlugin }) => [
js.configs.recommended,
ts.configs.recommendedTypeChecked,
reactPlugin.configs.recommended,
reactHooksPlugin.configs.recommended,
{
files: ['**/*.{test,spec}.?(c|m)[jt]s?(x)'],
...rstestPlugin.configs.recommended,
},
{
languageOptions: {
parserOptions: {
project: [
'./apps/*/tsconfig.json',
'./apps/*/tests/tsconfig.json',
'./packages/*/tsconfig.json',
'./packages/*/tests/tsconfig.json',
],
},
},
},
]);

define.fmt({
singleQuote: true,
});
22 changes: 22 additions & 0 deletions packages/create-rstack/template-pnpm/tsconfig.base.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2022"],
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"moduleDetection": "force",
"jsx": "react-jsx",
"strict": true,
"noEmit": true,
"skipLibCheck": true,
"isolatedModules": true,
"verbatimModuleSyntax": true,
"resolveJsonModule": true,
"allowImportingTsExtensions": true,
"noUncheckedIndexedAccess": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"types": ["rstack/types", "node"]
}
}
Loading
Loading