Skip to content

Commit

Permalink
Merge pull request #1395 from softwaremill/feature/change-buidler
Browse files Browse the repository at this point in the history
Change frontend builder from create-react-app to vite
  • Loading branch information
BartoszButrymSoftwareMill authored Jan 24, 2025
2 parents c20729b + 1664efe commit 1516991
Show file tree
Hide file tree
Showing 88 changed files with 4,632 additions and 10,737 deletions.
14 changes: 7 additions & 7 deletions docs/frontend.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
---
layout: default
title: 'Frontend application'
title: "Frontend application"
---

Bootzooka's frontend is a true Single Page Application built with React. It can be treated as a completely separate application or as a client for Bootzooka server.

As a separate application it deserves its own build process handling all the details (linting, testing, minifying etc). Hence the frontend part is almost completely decoupled from server side code. The only coupling is on the level of packaging final application (which is described later in this doc).

Please note, that the UI is based on [fantastic tool called Create React App](https://github.com/facebook/create-react-app) which takes care of fine details like build configuration, minification & hot reloading under the hood, without you having to worry about it. For more details, see the project's [page](https://github.com/facebook/create-react-app).
Please note, that the UI is based on [fantastic tool called Vite](https://github.com/vitejs/vite) which takes care of fine details like build configuration, minification & hot reloading under the hood, without you having to worry about it. For more details, see the project's [page](https://github.com/vitejs/vite).

## Installing Node.js & Yarn

To work with the `ui` module you need to have `node.js` installed in version 16.0 or newer. Make sure you have `node` command available on `PATH`.
To work with the `ui` module you need to have `node.js` installed in version 22 or newer. Make sure you have `node` command available on `PATH`.

As a package manager, Bootzooka's UI uses [Yarn](https://yarnpkg.com). Make sure to have it installed before the first run.

Expand All @@ -38,7 +38,7 @@ The most important tasks exposed are:

This task serves Bootzooka application on port `3000` on `0.0.0.0` (it is available to all hosts from the same network). Your default browser should open at this location. All requests to the backend will be proxied to port `8080` where it expects the server to be run.

Hot reload is in place already (provided by the Create React App stack), so every change is automatically compiled (if necessary) and browser is automatically refreshed to apply changes. No need to refresh it by hand.
Hot reload is in place already (provided by the Vite stack), so every change is automatically compiled (if necessary) and browser is automatically refreshed to apply changes. No need to refresh it by hand.

In this task all scripts are served in non-concatenated and non-minified version from their original locations (if possible).

Expand All @@ -48,12 +48,12 @@ It builds everything as a distribution-ready version to `dist` directory. It doe

## `yarn test` task

This task runs tests and watches for changes in files. When change is detected it runs tests automatically. This is especially helpful in hard-development mode. Tests are run with Jest using Enzyme.
This task runs tests and watches for changes in files. When change is detected it runs tests automatically. This is especially helpful in hard-development mode. Tests are run with Vitest.

## `yarn test` task
## `yarn test:ci` task

This task runs tests just once (useful in CI environments, where an exit code is required).

## Distribution and deployment

Although in development `ui` is separate project there is no need to deploy it separately. All files from `ui/dist/webapp` (which are generated during `yarn build`) are used by `backend` to build the final fat-jar application. All necessary integration with SBT (backend build) is provided. That means when you issue `package` in SBT, you get a complete web application which contains both server side and frontend components.
Although in development `ui` is separate project there is no need to deploy it separately. All files from `ui/dist` (which are generated during `yarn build`) are used by `backend` to build the final fat-jar application. All necessary integration with SBT (backend build) is provided. That means when you issue `package` in SBT, you get a complete web application which contains both server side and frontend components.
4 changes: 2 additions & 2 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
layout: default
title: 'Getting started'
title: "Getting started"
---

## Prerequisites
Expand All @@ -9,7 +9,7 @@ In order to build and develop on Bootzooka foundations you need the following:

- Java JDK >= 21
- [sbt](http://www.scala-sbt.org/) >= 1.10
- Node.js >= 16.0 (We recommend [nvm](https://github.com/creationix/nvm) - node version manager)
- Node.js >= 22 (We recommend [nvm](https://github.com/nvm-sh/nvm) - node version manager)
- PostgreSQL

## How to run
Expand Down
31 changes: 16 additions & 15 deletions docs/stack.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,30 @@
---
layout: default
title: "Technology stack"
title: "Technology stack"
---

When picking Bootzooka's technology stack we wanted to use modern, but reasonably proven technologies. So while you won't find the latest, hottest frameworks here, you also won't see any JSPs or <marquee> tags. The components are easy to replace, so if you'd like to experiment with a new library, this should be a matter of replacing only a small part of Bootzooka. Also, we try to update the stack once in a while, so that it's up-to-date with current developments and trends.

Bootzooka's stack consists of the following technologies/tools, on the backend:

* [Scala](https://www.scala-lang.org) (JVM based, functional language)
* [Tapir](https://github.com/softwaremill/tapir) (endpoint description library) + [netty](https://netty.io) (backend networking layer)
* SQL database, by default [PostgreSQL](https://www.postgresql.org) (persistence)
* [Magnum](https://github.com/AugustNagro/magnum) (type-safe database access) + [flyway](https://flywaydb.org) (schema evolution)
* [Ox](https://github.com/softwaremill/ox) (error handling, concurrency & resource management)
* [SBT](https://www.scala-sbt.org) (build tool)
* [OpenTelemetry](https://opentelemetry.io) (metrics & tracing)
- [Scala](https://www.scala-lang.org) (JVM based, functional language)
- [Tapir](https://github.com/softwaremill/tapir) (endpoint description library) + [netty](https://netty.io) (backend networking layer)
- SQL database, by default [PostgreSQL](https://www.postgresql.org) (persistence)
- [Magnum](https://github.com/AugustNagro/magnum) (type-safe database access) + [flyway](https://flywaydb.org) (schema evolution)
- [Ox](https://github.com/softwaremill/ox) (error handling, concurrency & resource management)
- [SBT](https://www.scala-sbt.org) (build tool)
- [OpenTelemetry](https://opentelemetry.io) (metrics & tracing)

And on the frontend:

* [TypeScript](https://www.typescriptlang.org) (JavaScript superset)
* [react](https://reactjs.org)
* [Swagger](https://swagger.io) (interactive API docs)
* [yarn](https://yarnpkg.com) (build tool)
* [formik](https://formik.org/) (forms)
* [yup](https://www.npmjs.com/package/yup/v/1.3.3) (validation)
* [openapi-codegen](https://github.com/fabien0102/openapi-codegen) (generate ui functions from OpenAPI specifications)
- [Vite](https://vite.dev/) (build tool)
- [TypeScript](https://www.typescriptlang.org) (JavaScript superset)
- [react](https://react.dev/)
- [Swagger](https://swagger.io) (interactive API docs)
- [yarn](https://yarnpkg.com) (package manager)
- [formik](https://formik.org/) (forms)
- [yup](https://www.npmjs.com/package/yup/v/1.3.3) (validation)
- [openapi-codegen](https://github.com/fabien0102/openapi-codegen) (generate ui functions from OpenAPI specifications)

### Why Scala?

Expand Down
2 changes: 1 addition & 1 deletion ui/.env.example
Original file line number Diff line number Diff line change
@@ -1 +1 @@
REACT_APP_BASE_URL = "http://localhost:3000/api/v1"
VITE_APP_BASE_URL = "http://localhost:8080/api/v1"
26 changes: 26 additions & 0 deletions ui/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local
build
coverage

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
5 changes: 4 additions & 1 deletion ui/.prettierrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
{
"printWidth": 120
"printWidth": 80,
"trailingComma": "es5",
"tabWidth": 2,
"singleQuote": true
}
22 changes: 6 additions & 16 deletions ui/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
This project was bootstrapped with [Vite](https://vite.dev/).

## Available Scripts

Expand Down Expand Up @@ -30,30 +30,20 @@ A file watch is engaged, re-generating types on each change to the `<project_roo
### `yarn test`

Launches the test runner in the interactive watch mode.<br />
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
See the section about [running tests](https://vitest.dev/guide/) for more information.

### `yarn build`

Builds the app for production to the `build` folder.<br />
Builds the app for production to the `dist` folder.<br />
It correctly bundles React in production mode and optimizes the build for the best performance.

The build is minified and the filenames include the hashes.<br />
Your app is ready to be deployed!

See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.

### `yarn eject`

**Note: this is a one-way operation. Once you `eject`, you can’t go back!**

If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.

Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.

You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
See the section about [deployment](https://vite.dev/guide/static-deploy) for more information.

## Learn More

You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
You can learn more in the [Vite documentation](https://vite.dev/).

To learn React, check out the [React documentation](https://reactjs.org/).
To learn React, check out the [React documentation](https://react.dev/).
35 changes: 35 additions & 0 deletions ui/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import js from '@eslint/js';
import globals from 'globals';
import reactHooks from 'eslint-plugin-react-hooks';
import reactRefresh from 'eslint-plugin-react-refresh';
import tseslint from 'typescript-eslint';
import eslintPluginPrettier from 'eslint-plugin-prettier/recommended';

export default tseslint.config(
{ ignores: ['dist', 'src/api'] },
{
extends: [
js.configs.recommended,
...tseslint.configs.recommended,
eslintPluginPrettier,
],
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
'@typescript-eslint/no-explicit-any': 'off',
'prettier/prettier': ['error', { printWidth: 80 }],
},
}
);
15 changes: 15 additions & 0 deletions ui/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/png" href="/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Bootzooka - application scaffolding by SoftwareMill" />
<link rel="manifest" href="/manifest.json" />
<title>Bootzooka</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
23 changes: 13 additions & 10 deletions ui/openapi-codegen.config.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { generateSchemaTypes, generateReactQueryComponents } from "@openapi-codegen/typescript";
import { defineConfig } from "@openapi-codegen/cli";
import {
generateSchemaTypes,
generateReactQueryComponents,
} from '@openapi-codegen/typescript';
import { defineConfig } from '@openapi-codegen/cli';
export default defineConfig({
apiFile: {
from: {
relativePath: "../backend/target/openapi.yaml",
source: "file",
relativePath: '../backend/target/openapi.yaml',
source: 'file',
},
outputDir: "./src/api",
outputDir: './src/api',
to: async (context) => {
const filenamePrefix = "api";
const filenamePrefix = 'api';
const { schemasFiles } = await generateSchemaTypes(context, {
filenamePrefix,
});
Expand All @@ -20,12 +23,12 @@ export default defineConfig({
},
apiWeb: {
from: {
source: "url",
url: "http://localhost:8080/api/v1/docs/docs.yaml",
source: 'url',
url: 'http://localhost:8080/api/v1/docs/docs.yaml',
},
outputDir: "./src/api",
outputDir: './src/api',
to: async (context) => {
const filenamePrefix = "api";
const filenamePrefix = 'api';
const { schemasFiles } = await generateSchemaTypes(context, {
filenamePrefix,
});
Expand Down
83 changes: 42 additions & 41 deletions ui/package.json
Original file line number Diff line number Diff line change
@@ -1,57 +1,65 @@
{
"name": "bootzooka-ui",
"version": "0.1.0",
"type": "module",
"private": true,
"proxy": "http://localhost:8080",
"engines": {
"node": ">=22"
},
"scripts": {
"start": "yarn generate:openapi-types && concurrently vite \"yarn watch:openapi\"",
"build": "yarn generate:openapi-types && tsc -b && vite build",
"preview": "vite preview",
"test": "vitest",
"test:coverage": "vitest run --coverage",
"test:ci": "CI=true vitest",
"lint": "eslint .",
"generate:openapi-types": "npx openapi-codegen gen apiWeb --source file --relativePath ../backend/target/openapi.yaml",
"watch:openapi": "chokidar '../backend/target/openapi.yaml' -c 'yarn generate:openapi-types'",
"start:frontend": "yarn start"
},
"dependencies": {
"@tanstack/react-query": "^5.62.7",
"@testing-library/jest-dom": "^6.2.0",
"@testing-library/react": "^14.1.2",
"@testing-library/user-event": "^14.5.2",
"@types/jest": "^29.2.6",
"@types/node": "^20.10.7",
"@types/react": "^18.2.47",
"@types/react-dom": "^18.2.18",
"@types/react-icons": "^3.0.0",
"@types/react-router-bootstrap": "^0.26.6",
"@types/react-router-dom": "^5.3.3",
"@types/yup": "^0.32.0",
"bootstrap": "^5.3.2",
"eslint-config-prettier": "^9.1.0",
"formik": "^2.4.5",
"immer": "^10.0.3",
"prettier": "^3.1.1",
"react": "^18.2.0",
"react-bootstrap": "^2.9.2",
"react-dom": "^18.2.0",
"react-icons": "^4.12.0",
"react-router": "^7.1.3",
"react-router-bootstrap": "^0.26.2",
"react-router-dom": "^6.21.1",
"react-scripts": "^5.0.1",
"typescript": "5.7.3",
"yup": "^1.3.3"
},
"scripts": {
"start": "yarn generate:openapi-types && concurrently \"react-scripts start\" \"yarn watch:openapi\"",
"build": "yarn generate:openapi-types && react-scripts build",
"test": "react-scripts test",
"test:coverage": "react-scripts test --coverage --watchAll=false",
"test:ci": "CI=true react-scripts test --maxWorkers 2",
"lint": "eslint --ext .ts,.tsx . && prettier --write ./src",
"eject": "react-scripts eject",
"generate:openapi-types": "npx openapi-codegen gen apiWeb --source file --relativePath ../backend/target/openapi.yaml",
"watch:openapi": "chokidar '../backend/target/openapi.yaml' -c 'yarn generate:openapi-types'",
"start:frontend": "yarn start"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest",
"prettier"
]
"devDependencies": {
"@eslint/js": "^9.18.0",
"@openapi-codegen/cli": "^2.0.2",
"@openapi-codegen/typescript": "^8.0.2",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.2.0",
"@testing-library/user-event": "^14.6.0",
"@types/node": "^22.10.7",
"@types/react": "^19.0.7",
"@types/react-dom": "^19.0.3",
"@vitejs/plugin-react-swc": "^3.7.2",
"@vitest/coverage-v8": "^3.0.2",
"chokidar-cli": "^3.0.0",
"concurrently": "^8.2.2",
"eslint": "^9.18.0",
"eslint-config-prettier": "^10.0.1",
"eslint-plugin-prettier": "^5.2.2",
"eslint-plugin-react": "^7.37.4",
"eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-react-refresh": "^0.4.18",
"globals": "^15.14.0",
"jsdom": "^26.0.0",
"prettier": "^3.4.2",
"typescript": "^5.7.3",
"typescript-eslint": "^8.20.0",
"vite": "^6.0.7",
"vitest": "^3.0.2"
},
"jest": {
"collectCoverageFrom": [
Expand All @@ -71,12 +79,5 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@openapi-codegen/cli": "^2.0.2",
"@openapi-codegen/typescript": "^8.0.2",
"chokidar-cli": "^3.0.0",
"concurrently": "^8.2.2"
}
}
Loading

0 comments on commit 1516991

Please sign in to comment.