Initial commit: Complete project-bootstrap tool
- Bootstrap script for creating monorepo projects - FastAPI backend templates with uv, ruff, mypy, pytest - React frontend templates with TypeScript, ESLint, Prettier - Docker Compose setup with backend, frontend, and database - 9 development and CI scripts - Gitea Actions CI/CD workflows - Comprehensive documentation (8 files) - 45 template files for complete project structure - Automated verification script (all tests pass) - Based on coding-agent-rules standards
This commit is contained in:
7
templates/frontend/.env.example
Normal file
7
templates/frontend/.env.example
Normal file
@@ -0,0 +1,7 @@
|
||||
# API Configuration
|
||||
REACT_APP_API_URL=http://localhost:8000
|
||||
REACT_APP_API_PREFIX=/api/v1
|
||||
|
||||
# Application
|
||||
REACT_APP_NAME=Frontend
|
||||
REACT_APP_VERSION=0.1.0
|
||||
34
templates/frontend/.eslintrc.json
Normal file
34
templates/frontend/.eslintrc.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es2021": true,
|
||||
"jest": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:react/recommended",
|
||||
"plugin:react-hooks/recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"prettier"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
},
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": ["react", "@typescript-eslint"],
|
||||
"rules": {
|
||||
"react/react-in-jsx-scope": "off",
|
||||
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off"
|
||||
},
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect"
|
||||
}
|
||||
}
|
||||
}
|
||||
10
templates/frontend/.prettierrc.json
Normal file
10
templates/frontend/.prettierrc.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"semi": true,
|
||||
"trailingComma": "es5",
|
||||
"singleQuote": true,
|
||||
"printWidth": 100,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"arrowParens": "always",
|
||||
"endOfLine": "lf"
|
||||
}
|
||||
73
templates/frontend/package.json
Normal file
73
templates/frontend/package.json
Normal file
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"name": "frontend",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.20.0",
|
||||
"axios": "^1.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/jest-dom": "^6.1.5",
|
||||
"@testing-library/react": "^14.1.2",
|
||||
"@testing-library/user-event": "^14.5.1",
|
||||
"@types/jest": "^29.5.10",
|
||||
"@types/node": "^20.10.0",
|
||||
"@types/react": "^18.2.42",
|
||||
"@types/react-dom": "^18.2.17",
|
||||
"@typescript-eslint/eslint-plugin": "^6.13.0",
|
||||
"@typescript-eslint/parser": "^6.13.0",
|
||||
"eslint": "^8.54.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"prettier": "^3.1.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"typescript": "^5.3.2"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"test:coverage": "react-scripts test --coverage --watchAll=false",
|
||||
"eject": "react-scripts eject",
|
||||
"lint": "eslint src --ext .ts,.tsx",
|
||||
"lint:fix": "eslint src --ext .ts,.tsx --fix",
|
||||
"format": "prettier --write \"src/**/*.{ts,tsx,json,css,md}\""
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"jest": {
|
||||
"collectCoverageFrom": [
|
||||
"src/**/*.{ts,tsx}",
|
||||
"!src/**/*.d.ts",
|
||||
"!src/index.tsx",
|
||||
"!src/reportWebVitals.ts"
|
||||
],
|
||||
"coverageThreshold": {
|
||||
"global": {
|
||||
"branches": 90,
|
||||
"functions": 90,
|
||||
"lines": 90,
|
||||
"statements": 90
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
15
templates/frontend/public/index.html
Normal file
15
templates/frontend/public/index.html
Normal file
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="description" content="Modern monorepo application" />
|
||||
<title>React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
15
templates/frontend/src/App.test.tsx
Normal file
15
templates/frontend/src/App.test.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import App from './App';
|
||||
|
||||
test('renders welcome message', () => {
|
||||
render(<App />);
|
||||
const headingElement = screen.getByText(/Welcome to Your Monorepo Project/i);
|
||||
expect(headingElement).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('renders edit instruction', () => {
|
||||
render(<App />);
|
||||
const instructionElement = screen.getByText(/Edit src\/App.tsx and save to reload/i);
|
||||
expect(instructionElement).toBeInTheDocument();
|
||||
});
|
||||
15
templates/frontend/src/App.tsx
Normal file
15
templates/frontend/src/App.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
import './styles/App.css';
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<div className="App">
|
||||
<header className="App-header">
|
||||
<h1>Welcome to Your Monorepo Project</h1>
|
||||
<p>Edit src/App.tsx and save to reload.</p>
|
||||
</header>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
11
templates/frontend/src/index.tsx
Normal file
11
templates/frontend/src/index.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import './styles/index.css';
|
||||
import App from './App';
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
41
templates/frontend/src/services/api.ts
Normal file
41
templates/frontend/src/services/api.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import axios, { AxiosInstance } from 'axios';
|
||||
|
||||
const API_URL = process.env.REACT_APP_API_URL || 'http://localhost:8000';
|
||||
const API_PREFIX = process.env.REACT_APP_API_PREFIX || '/api/v1';
|
||||
|
||||
const apiClient: AxiosInstance = axios.create({
|
||||
baseURL: `${API_URL}${API_PREFIX}`,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
// Request interceptor
|
||||
apiClient.interceptors.request.use(
|
||||
(config) => {
|
||||
// Add auth token if available
|
||||
const token = localStorage.getItem('token');
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// Response interceptor
|
||||
apiClient.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
// Handle errors globally
|
||||
if (error.response?.status === 401) {
|
||||
// Handle unauthorized
|
||||
localStorage.removeItem('token');
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
export default apiClient;
|
||||
5
templates/frontend/src/setupTests.ts
Normal file
5
templates/frontend/src/setupTests.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
||||
// allows you to do things like:
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import '@testing-library/jest-dom';
|
||||
23
templates/frontend/src/styles/App.css
Normal file
23
templates/frontend/src/styles/App.css
Normal file
@@ -0,0 +1,23 @@
|
||||
.App {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.App-header {
|
||||
background-color: #282c34;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: calc(10px + 2vmin);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.App-header h1 {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.App-header p {
|
||||
font-size: 1rem;
|
||||
color: #61dafb;
|
||||
}
|
||||
11
templates/frontend/src/styles/index.css
Normal file
11
templates/frontend/src/styles/index.css
Normal file
@@ -0,0 +1,11 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu',
|
||||
'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
|
||||
}
|
||||
32
templates/frontend/tsconfig.json
Normal file
32
templates/frontend/tsconfig.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"jsx": "react-jsx",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"allowJs": true,
|
||||
"checkJs": false,
|
||||
"outDir": "./build",
|
||||
"rootDir": "./src",
|
||||
"removeComments": true,
|
||||
"noEmit": true,
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"skipLibCheck": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"baseUrl": "src",
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
}
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "build", "dist"]
|
||||
}
|
||||
Reference in New Issue
Block a user