chore: pnpm workspace — landing, api, shared

apps/landing: Next.js 16 (App Router, standalone), Tailwind 4, ESLint
с запретом импорта из api. apps/api: Hono + better-sqlite3, сборка tsup,
/api/health. packages/shared: исходники TS без сборки. Vitest projects.
This commit is contained in:
2026-10-17 15:33:07 +03:00
parent 4b880e7d9a
commit 04b150f3c3
23 changed files with 5792 additions and 0 deletions
+22
View File
@@ -0,0 +1,22 @@
{
"name": "@kvarta/api",
"private": true,
"type": "module",
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "tsup",
"start": "node dist/index.js",
"typecheck": "tsc -p ."
},
"dependencies": {
"@hono/node-server": "^2.1.5",
"better-sqlite3": "^13.0.3",
"hono": "^4.13.13"
},
"devDependencies": {
"@kvarta/shared": "workspace:*",
"@types/better-sqlite3": "^9.6.0",
"tsup": "^9.0.0",
"tsx": "^4.20.0"
}
}
+7
View File
@@ -0,0 +1,7 @@
import { Hono } from 'hono';
export function createApp() {
const app = new Hono();
app.get('/api/health', (c) => c.json({ ok: true, version: process.env.APP_VERSION ?? 'dev' }));
return app;
}
+7
View File
@@ -0,0 +1,7 @@
import { serve } from '@hono/node-server';
import { createApp } from './app.js';
const port = Number(process.env.PORT ?? 3001);
serve({ fetch: createApp().fetch, port }, (info) => {
console.log(`api listening on :${info.port}`);
});
+5
View File
@@ -0,0 +1,5 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": { "types": ["node"] },
"include": ["src"]
}
+12
View File
@@ -0,0 +1,12 @@
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'],
format: ['esm'],
platform: 'node',
target: 'node22',
clean: true,
// shared поставляется исходниками — вшиваем; нативный better-sqlite3 остаётся внешним
noExternal: ['@kvarta/shared'],
external: ['better-sqlite3'],
});
+3
View File
@@ -0,0 +1,3 @@
import { defineConfig } from 'vitest/config';
export default defineConfig({ test: { name: 'api', environment: 'node' } });
+13
View File
@@ -0,0 +1,13 @@
import next from 'eslint-config-next';
const config = [
...next,
{
rules: {
// лендинг не знает о бэкенде: общий код только через @kvarta/shared
'no-restricted-imports': ['error', { patterns: ['@kvarta/api', '**/apps/api/**'] }],
},
},
];
export default config;
+16
View File
@@ -0,0 +1,16 @@
import path from 'node:path';
import type { NextConfig } from 'next';
const config: NextConfig = {
output: 'standalone',
outputFileTracingRoot: path.join(import.meta.dirname, '../..'),
transpilePackages: ['@kvarta/shared'],
poweredByHeader: false,
// в проде /api/ отдаёт nginx на контейнер api; локально проксируем на dev-сервер api
async rewrites() {
if (process.env.NODE_ENV === 'production') return [];
return [{ source: '/api/:path*', destination: 'http://127.0.0.1:3001/api/:path*' }];
},
};
export default config;
+26
View File
@@ -0,0 +1,26 @@
{
"name": "@kvarta/landing",
"private": true,
"type": "module",
"scripts": {
"dev": "next dev -p 3000",
"build": "next build",
"start": "next start -p 3000",
"typecheck": "tsc -p .",
"lint": "eslint ."
},
"dependencies": {
"next": "^16.4.2",
"react": "^19.3.1",
"react-dom": "^19.3.1"
},
"devDependencies": {
"@kvarta/shared": "workspace:*",
"@tailwindcss/postcss": "^4.3.3",
"@types/react": "^19.3.0",
"@types/react-dom": "^19.2.0",
"eslint": "^9.39.0",
"eslint-config-next": "^16.4.2",
"tailwindcss": "^4.3.3"
}
}
+3
View File
@@ -0,0 +1,3 @@
const config = { plugins: { '@tailwindcss/postcss': {} } };
export default config;
+1
View File
@@ -0,0 +1 @@
@import 'tailwindcss';
+15
View File
@@ -0,0 +1,15 @@
import type { Metadata } from 'next';
import './globals.css';
export const metadata: Metadata = {
title: 'Кварта — свежая обжарка в Минске',
description: 'Обжариваем по четвергам, привозим по пятницам.',
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="ru">
<body>{children}</body>
</html>
);
}
+3
View File
@@ -0,0 +1,3 @@
export default function Page() {
return <main>Кварта</main>;
}
+13
View File
@@ -0,0 +1,13 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"lib": ["ES2023", "DOM", "DOM.Iterable"],
"jsx": "preserve",
"allowJs": false,
"incremental": true,
"plugins": [{ "name": "next" }],
"paths": { "@/*": ["./src/*"] }
},
"include": ["next-env.d.ts", "src", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
+3
View File
@@ -0,0 +1,3 @@
import { defineConfig } from 'vitest/config';
export default defineConfig({ test: { name: 'landing', environment: 'node' } });