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
- `turborepo` - Turborepo

## Documentation

Expand Down
36 changes: 34 additions & 2 deletions packages/create-rstack/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
import {
access,
appendFile,
copyFile,
mkdir,
readFile,
writeFile,
Expand Down Expand Up @@ -43,6 +44,7 @@ const templateNames = [
'lib-solid-ts',
'doc',
'doc-i18n',
'turborepo',
];

const resolveTemplateName = (template: string): string => {
Expand All @@ -55,7 +57,7 @@ const resolveTemplateName = (template: string): string => {

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

Expand All @@ -69,10 +71,22 @@ const getTemplateName = async ({ template }: Argv): Promise<string> => {
{ value: 'app', label: 'Web Application' },
{ value: 'lib', label: 'Library' },
{ value: 'doc', label: 'Documentation' },
{ value: 'monorepo', label: 'Monorepo' },
],
}),
);

if (projectType === 'monorepo') {
const monorepoType = checkCancel<string>(
await select({
message: 'Select monorepo tool',
options: [{ value: 'turborepo', label: 'Turborepo', hint: 'pnpm' }],
}),
);

return resolveTemplateName(monorepoType);
}

if (projectType === 'doc') {
const documentationType = checkCancel<string>(
await select({
Expand Down Expand Up @@ -215,11 +229,29 @@ const injectStagedSetup = async ({
]);
};

const configureTurborepo = async ({
templateName,
distFolder,
}: GitResolvedContext): Promise<void> => {
if (templateName !== 'turborepo') {
return;
}

// The toolkit may skip this file based on the invoking package manager.
await copyFile(
path.join(packageRoot, 'template-turborepo/pnpm-workspace.yaml'),
path.join(distFolder, 'pnpm-workspace.yaml'),
);
};

await create({
root: packageRoot,
name: 'rstack',
templates: templateNames,
builtinTools: [],
getTemplateName,
onGitResolved: injectStagedSetup,
onGitResolved: async (context) => {
await configureTurborepo(context);
await injectStagedSetup(context);
},
});
31 changes: 31 additions & 0 deletions packages/create-rstack/template-turborepo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Rstack + Turborepo

## Setup

```bash
pnpm install
```

## Structure

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

## Scripts

Run from the repository root:

- `pnpm run build`: Build all packages.
- `pnpm run dev`: Start development.
- `pnpm run test`: 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`.

## Learn more

- [Rstack CLI monorepo guide](https://rstack.rs/guide/monorepo)
- [Turborepo documentation](https://turborepo.com/docs)
27 changes: 27 additions & 0 deletions packages/create-rstack/template-turborepo/apps/web/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"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.2.8",
"react-dom": "^19.2.8"
},
"devDependencies": {
"@repo/tsconfig": "workspace:^",
"@rsbuild/plugin-react": "^2.1.0",
"@testing-library/dom": "^10.4.1",
"@testing-library/jest-dom": "^7.0.1",
"@testing-library/react": "^16.3.3",
"@types/react": "^19.2.18",
"@types/react-dom": "^19.2.5",
"happy-dom": "^20.13.2"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Configuration guide: https://rstack.rs/config
import { define } from 'rstack';

define.app(async () => {
const { pluginReact } = await import('@rsbuild/plugin-react');
return {
plugins: [pluginReact()],
html: { title: 'Web | Rstack' },
server: { port: 3000 },
};
});

define.test({
setupFiles: ['./tests/rstest.setup.ts'],
});
17 changes: 17 additions & 0 deletions packages/create-rstack/template-turborepo/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>Turborepo + 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-turborepo/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-turborepo/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>,
);
}
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": ["./"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "@repo/tsconfig/base.json",
"include": ["src", "rstack.config.ts"]
}
8 changes: 8 additions & 0 deletions packages/create-rstack/template-turborepo/apps/web/turbo.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"extends": ["//"],
"tasks": {
"dev": {
"persistent": true
}
}
}
16 changes: 16 additions & 0 deletions packages/create-rstack/template-turborepo/gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Local
.DS_Store
*.local
*.log*

# Dist
node_modules
dist/

# IDE
.vscode/*
!.vscode/extensions.json
.idea

# Turborepo
.turbo/
20 changes: 20 additions & 0 deletions packages/create-rstack/template-turborepo/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "rstack-turborepo",
"private": true,
"type": "module",
"scripts": {
"build": "turbo run build",
"check": "rs check --type-check",
"dev": "turbo watch dev",
"format": "rs fmt",
"lint": "rs lint",
"test": "turbo run test"
},
"devDependencies": {
"@types/node": "^26.4.1",
"rstack": "^0.7.3",
"turbo": "^2.10.12",
"typescript": "^7.0.2"
},
"packageManager": "pnpm@11.25.0"
}
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"]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "@repo/tsconfig",
"version": "0.0.0",
"private": true,
"exports": {
"./base.json": "./base.json"
},
"files": [
"base.json"
]
}
31 changes: 31 additions & 0 deletions packages/create-rstack/template-turborepo/packages/ui/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"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 --no-clean",
"test": "rs test"
},
"dependencies": {
"react": "^19.2.8"
},
"devDependencies": {
"@repo/tsconfig": "workspace:^",
"@rsbuild/plugin-react": "^2.1.0",
"@testing-library/dom": "^10.4.1",
"@testing-library/jest-dom": "^7.0.1",
"@testing-library/react": "^16.3.3",
"@types/react": "^19.2.18",
"@types/react-dom": "^19.2.5",
"happy-dom": "^20.13.2",
"react-dom": "^19.2.8"
}
}
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} />;
}
Loading