Commit 81933106 authored by Nurasyl's avatar Nurasyl

Initial commit

parents
.env
.env.*
node_modules
dist
build
.idea
.vscode
{
"semi": true,
"trailingComma": "all",
"singleQuote": true,
"printWidth": 150,
"tabWidth": 2
}
{
"watch": [ "src" ],
"ignore": [
"**/*.test.ts",
"**/*.spec.ts",
".git",
"node_modules"
],
"ext": "ts",
"exec": "node -r tsconfig-paths/register -r ts-node/register ./src/index.ts"
}
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "node_template",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "nodemon",
"lint": "eslint",
"lint:fix": "eslint --fix"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"express": "^4.18.2",
"reflect-metadata": "^0.2.2",
"save": "^2.9.0",
"ts-node": "^10.9.1",
"tslib": "^2.6.0",
"typescript": "^5.1.6",
"uuidv4": "^6.2.13"
},
"devDependencies": {
"@types/express": "^4.17.17",
"@types/node": "^20.4.2",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"eslint": "^8.45.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-prettier": "^5.0.0",
"nodemon": "^3.0.1",
"prettier": "^3.0.0",
"tsconfig-paths": "^4.2.0"
}
}
import express from 'express';
import { Application, RequestHandler } from 'express';
import { AppInit } from './interfaces/AppInit.interface';
import { IRoute } from './interfaces/IRoute.interface';
class App {
public app: Application;
public port: number;
constructor(appInit: AppInit) {
this.app = express();
this.port = appInit.port;
this.initAssets();
this.initMiddlewares(appInit.middlewares);
this.initRoutes(appInit.controllers);
}
private initMiddlewares(middlewares: RequestHandler[]) {
middlewares.forEach((middleware) => {
this.app.use(middleware);
});
}
private initRoutes(routes: IRoute[]) {
routes.forEach((route) => {
this.app.use(route.path, route.router);
});
}
private initAssets() {
this.app.use(express.json());
}
public listen() {
this.app.listen(this.port, () => {
console.log(`App listening on the http://localhost:${this.port}`);
});
}
}
export default App;
import { RequestHandler } from 'express';
import { ArticleService } from '../services/article.service';
export class ArticleController {
private service: ArticleService;
constructor() {
this.service = new ArticleService();
}
getAllArticles: RequestHandler = (req, res): void => {
const articles = this.service.getAllArticles();
res.send(articles);
};
getArticle: RequestHandler = (req, res): void => {
const article = this.service.getArticle(req.params.id);
res.send(article);
};
createArticle: RequestHandler = (req, res): void => {
const article = this.service.createArticle(req.body);
res.send(article);
};
}
import { ProductService } from "@/services/product.service";
import { RequestHandler } from "express";
import { ProductDto } from "@/dto/product.dto";
import { validate } from 'class-validator';
export class ProductController {
private service: ProductService
constructor() {
this.service = new ProductService()
}
getAllProducts: RequestHandler = (req, res) => {
const products = this.service.getAllProducts()
res.send(products)
}
getProduct: RequestHandler = (req, res) => {
const product = this.service.getProduct(req.params.id)
res.send(product)
}
createProduct: RequestHandler = async (req, res) => {
const productDto = new ProductDto(req)
const errors = await validate(productDto)
if(errors.length > 0) {
res.send(errors)
} else {
const product = this.service.createProduct(productDto)
res.send(product)
}
}
}
\ No newline at end of file
[
{
"id": "c1339157-5020-47c4-a2f4-f0f707ce12d2",
"title": "test",
"description": "test",
"price": 2000
},
{
"id": "27ef0324-2934-49e3-a1b9-3ad708485aa9",
"title": "test1",
"description": "test1",
"price": 2000
}
]
\ No newline at end of file
import {IsString, IsNumber} from 'class-validator'
import { Request } from 'express';
export class ProductDto {
constructor(req: Request) {
this.title = req.body.title
this.description = req.body.description
this.price = req.body.price
}
@IsString({message: 'Title must be string'})
title: string;
@IsString({message: 'Description must be string'})
description: string;
@IsNumber()
price: number;
}
\ No newline at end of file
import App from './app';
import logger from './middlewares/logger';
import { ArticleRoute } from './routes/article.route';
import { ProductRoute } from './routes/product.route';
const app = new App({
port: 8000,
middlewares: [logger()],
controllers: [new ArticleRoute(), new ProductRoute()],
});
app.listen();
import { RequestHandler } from 'express';
import { IRoute } from './IRoute.interface';
export interface AppInit {
port: number;
middlewares: RequestHandler[];
controllers: IRoute[];
}
export interface IArticle {
id: string;
title: string;
description: string;
}
export interface IProduct {
id?: string
title: string
description: string
price: number
}
\ No newline at end of file
import { Router } from 'express';
export interface IRoute {
path: string;
router: Router;
}
import { RequestHandler } from 'express';
const logger = (): RequestHandler => (req, res, next) => {
console.log(`Request logged: ${req.method}, ${req.path}`);
next();
};
export default logger;
import { Router } from 'express';
import { ArticleController } from '../controllers/article.controller';
import { IRoute } from '../interfaces/IRoute.interface';
export class ArticleRoute implements IRoute {
public path = '/articles';
public router = Router();
private controller: ArticleController;
constructor() {
this.controller = new ArticleController();
this.init();
}
private init() {
this.router.get('/', this.controller.getAllArticles);
this.router.get('/:id', this.controller.getArticle);
this.router.post('/', this.controller.createArticle);
}
}
import { Router } from "express";
import { IRoute } from "@/interfaces/IRoute.interface";
import { ProductController } from "@/controllers/product.controller";
export class ProductRoute implements IRoute {
public path = '/product';
public router = Router();
private controller: ProductController;
constructor() {
this.controller = new ProductController();
this.init()
}
private init() {
this.router.get('/', this.controller.getAllProducts)
this.router.get('/:id', this.controller.getProduct)
this.router.post('/', this.controller.createProduct)
}
}
\ No newline at end of file
import { IArticle } from '../interfaces/IArticle.interface';
export class ArticleService {
private articles: IArticle[] = [];
getAllArticles = (): IArticle[] => {
return this.articles;
};
getArticle = (id: string): IArticle => {
const article = this.articles.find((article) => article.id === id);
if (article) return article;
else throw new Error('invalid id');
};
createArticle = (data: IArticle): IArticle => {
return {
id: Math.random().toString(),
title: data.title,
description: data.description,
};
};
}
import { IProduct } from "@/interfaces/IProduct.interface";
import { uuid } from 'uuidv4';
import * as fs from 'fs';
import path from 'path';
const filePath = path.join(__dirname, '../data/products.json');
export class ProductService {
private products: IProduct[] = [];
save = (): void => {
fs.writeFileSync(filePath, JSON.stringify(this.products, null, 2));
}
getAllProducts = (): IProduct[] => {
return this.products
}
getProduct = (id: string): IProduct => {
const product = this.products.find(item => item.id === id)
if(product) return product
else throw new Error('Invalide ID')
}
createProduct = (data: IProduct): IProduct => {
const product = {
id: uuid(),
title: data.title,
description: data.description,
price: data.price
}
this.products.push(product)
this.save()
return product
}
}
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"pretty": true,
"sourceMap": true,
"outDir": "dist",
"importHelpers": true,
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noImplicitThis": true,
"alwaysStrict": true,
"noUnusedLocals": true,
"noUnusedParameters": false,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"moduleResolution": "node",
"paths": {
"@/*": ["src/*"]
},
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"resolveJsonModule": true,
"esModuleInterop": true
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules"]
}
This diff is collapsed.
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
'prettier',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 'latest',
sourceType: 'module',
// project: './tsconfig.json',
},
plugins: ['react-refresh', 'react', '@typescript-eslint'],
rules: {
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
'react/react-in-jsx-scope': 0,
},
};
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
module.exports = {
tabWidth: 2,
singleQuote: true,
trailingComma: 'es5',
printWidth: 100,
useTabs: false,
};
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"[javascript]": {
"editor.formatOnSave": true
},
"[typescript]": {
"editor.formatOnSave": true
},
"[typescriptreact]": {
"editor.formatOnSave": true
},
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact"
],
"typescript.tsdk": "node_modules/typescript/lib",
}
\ No newline at end of file
# React + TypeScript + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
## Expanding the ESLint configuration
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
- Configure the top-level `parserOptions` property like this:
```js
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: ['./tsconfig.json', './tsconfig.node.json'],
tsconfigRootDir: __dirname,
},
```
- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
This diff is collapsed.
{
"name": "shop-frontend_template",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
},
"dependencies": {
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@fontsource/roboto": "^5.0.5",
"@mui/icons-material": "^5.14.1",
"@mui/material": "^5.14.1",
"@reduxjs/toolkit": "^1.9.5",
"axios": "^1.4.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-redux": "^8.1.1",
"react-router-dom": "^6.14.2"
},
"devDependencies": {
"@types/react": "^18.2.15",
"@types/react-dom": "^18.2.7",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"@vitejs/plugin-react-swc": "^3.3.2",
"eslint": "^8.45.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-react": "^7.34.1",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.3",
"prettier": "^3.2.5",
"typescript": "4.3.5",
"vite": "^4.4.5"
}
}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
\ No newline at end of file
const App = () => (
<>
<header>Navbar will go here</header>
<main>Main content will go here</main>
</>
);
export default App;
import { createSlice } from "@reduxjs/toolkit";
const initialState = {};
const productsSlice = createSlice(
{
name: 'products',
initialState,
reducers: {},
}
)
export default productsSlice.reducer;
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;
}
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
import App from './App.tsx';
import './index.css';
import store from './store';
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
</React.StrictMode>
);
import { configureStore } from '@reduxjs/toolkit';
import productsReducer from '../features/productsSlice.ts';
const store = configureStore({
reducer: {
products: productsReducer,
}
})
export default store;
/// <reference types="vite/client" />
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": [
"ES2020",
"DOM",
"DOM.Iterable"
],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"paths": {
"@/*": [
"./src/*"
]
}
},
"include": [
"src"
],
"references": [
{
"path": "./tsconfig.node.json"
}
]
}
\ No newline at end of file
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
})
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment