Skip to content

Commit

Permalink
chore: template setup
Browse files Browse the repository at this point in the history
configured with:
 - solid-js
 - elysia
 - bun
 - nvm
 - typescript
 - vitest
 - eslint
 - prettier
 - docker
 - github actions
  • Loading branch information
thedanchez committed Sep 24, 2024
0 parents commit 7003e5e
Show file tree
Hide file tree
Showing 35 changed files with 967 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
**/node_modules/
**/dist
**/__tests__
.git
.env
mockServiceWorker.js
26 changes: 26 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"parser": "@typescript-eslint/parser",
"plugins": ["simple-import-sort", "solid"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:solid/typescript"
],
"env": {
"browser": true,
"node": true,
"es6": true,
"jest": true
},
"parserOptions": {
"sourceType": "module"
},
"rules": {
"@typescript-eslint/no-unused-vars": ["error", { "varsIgnorePattern": "^_" }],
"no-unused-vars": "off",
"simple-import-sort/imports": "error",
"simple-import-sort/exports": "error"
},
"ignorePatterns": ["dist", "node_modules"]
}
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
thedanchez
31 changes: 31 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: CI
on:
push:
branches:
- main
pull_request:
branches:
- main
workflow_dispatch:

jobs:
quality-checks:
name: Quality Checks
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v1
with:
bun-version: 1.1.29
- name: Install Dependencies
run: bun install --frozen-lockfile
- name: Lint Check
run: bun lint
- name: Format Check
run: bun format
- name: Test Coverage Check (UI)
run: bun test:ui
- name: Test Coverage Check (Server)
run: bun test:server
37 changes: 37 additions & 0 deletions .github/workflows/deploy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Deploy

on:
push:
branches:
- main
workflow_dispatch:

jobs:
build:
name: "Build and Push Image"
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get Short Commit Hash
id: vars
shell: bash
run: |
echo "VERSION=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
echo Build Version: $VERSION
- name: Build Image
run: |
docker build -t app:${${{ github.sha }}::7} \
--build-arg VERSION=${GITHUB_SHA::7} \
.
# Upload Docker image Artifact
# NOTE: This is only for template purposes. You should be pushing the image to an image registry/repository.
# When you use this template, make sure to replace this step with the appropriate step to push the image to your
# image registry/repository.
- name: Upload Docker Image Artifact
uses: actions/upload-artifact@v3
with:
name: app_${GITHUB_SHA::7}
path: app_${GITHUB_SHA::7}.tar
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
coverage
dist
node_modules

.DS_Store
/*.tgz
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
20.17.0
4 changes: 4 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
coverage
dist
node_modules
mockServiceWorker.js
3 changes: 3 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"printWidth": 120
}
9 changes: 9 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"editor.codeActionsOnSave": {
"source.fixAll": "always"
},
"editor.formatOnSave": true,
"editor.rulers": [120],
"files.autoSave": "onFocusChange",
"files.insertFinalNewline": true
}
24 changes: 24 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
FROM oven/bun:1.1.29-alpine

WORKDIR /app

COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile

ARG VERSION
ENV VITE_VERSION=$VERSION

COPY public ./public
RUN echo "{ \"version\": \"${VERSION}\" }" > ./public/version.json

COPY index.html ./
COPY tsconfig.json ./
COPY vite.config.ts ./
COPY src ./src
COPY backend ./backend

RUN bun run build

EXPOSE 8000

CMD ["bun", "start:server"]
107 changes: 107 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Template: SolidJS + Elysia

Template for [SolidJS](https://www.solidjs.com/) SPA with [Elysia](https://elysiajs.com/at-glance.html) BFF (Backend-for-Frontend) layer. Elysia was chosen because it is performant, uses TypeScript thus creating language symmetry on both ends of the stack and, provides end-to-end type safety between the server (BFF) and the client (SPA) via [Eden Treaty](https://elysiajs.com/eden/treaty/overview.html). Furthermore, Elysia has a healthy and growing plugin ecosystem that provides first-party support for popular tools like [Swagger](https://elysiajs.com/plugins/swagger.html) for API documentation and [OpenTelemetry](https://elysiajs.com/plugins/opentelemetry.html) that provides observability, metrics and tracing to identify bottlenecks and general server performance which are important when productionizing applications.

With all of the above said, this additional BFF layer serves the following purposes:

- It is the primary source of data for the SolidJS frontend (`/api`). It will do the work of interfacing with external data sources for the sake of the frontend.
- It is responsible for serving the production built frontend assets generated from `bun run build`.
- Building on first point, issues related to CORS are gone as the frontend and backend BFF share the same origin URL.
- Can enrich or massage data for the frontend if/when desired.

Other things configured include:

- TypeScript (for both SPA and BFF)
- [Solid Query](https://tanstack.com/query/latest/docs/framework/solid/overview#motivation) (for data fetching / caching against BFF layer)
- [MSW](https://mswjs.io/docs/getting-started) + [Solid Testing Library](https://github.com/solidjs/solid-testing-library) + Vitest (for SPA testing)
- Bun Test (for BFF testing)
- ESLint / Prettier
- Docker (for containerized deployments)
- GitHub Actions

### Comments

Ideally, we would use `bun` for everything (frontend + backend) but there are gaps, specifically for the frontend, in the following two areas which force us to use Vite + Vitest: **bundling** and **testing**.

On the bundling side, Bun does not support bundling `index.html` nor injecting any bundled JS asset into the `index.html` file. We can probably work around this via doing some manual scripting but that is not worth doing for what is already a solved problem using other tools.

This is why we use Vite for bundling the frontend as there is the official `vite-plugin-solid` which does all the work under the hood for helping Vite to work with Solid. Furthermore, that same Vite plugin is what helps us to write tests against the SolidJS frontend using Vitest.

On the day when Bun provides an official plugin for Solid where it can bundle the web app and allow for writing tests against it, we can then deprecate Vite/Vitest and rely on Bun as the primary toolchain for the entire repository.

## Getting Started

Some pre-requisites before install dependencies:

- Install Node Version Manager (NVM)
```bash
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
```
- Install Bun
```bash
curl -fsSL https://bun.sh/install | bash
```

### Installing Dependencies

```bash
nvm use
bun install
```

### Local Development Build

```bash
# Note: UI dev server running on port 3000 proxies API requests to port 8000
bun start # http://localhost:3000
bun start:server --watch # http://localhost:8000
```

### Local Production Build

```bash
bun run build # Build production frontend static assets
bun start:server # Backend serves assets at http://localhost:8000

docker-compose up -d # Or alternatively using docker-compose
```

### Linting & Formatting

```bash
bun lint # checks source for lint violations
bun format # checks source for format violations

bun lint:fix # fixes lint violations
bun format:fix # fixes format violations
```

### Unit Testing

> We use [Solid Testing Library](https://github.com/solidjs/solid-testing-library) for integration style unit tests
```bash
bun test:ui
bun test:server # or `bun test`

bun test:ui --coverage # with test coverage
bun test:server --coverage # with test coverage
```

### Docker Image

The provided `Dockerfile` includes all the things needed to build the SPA and BFF to support a containerized deployment model.

```bash
docker build -t [tag_name] .
```

### Contributing

The only requirements when contributing are:

- You keep a clean git history in your branch
- rebasing `main` instead of making merge commits.
- Using proper commit message formats that adhere to [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/)
- Additionally, squashing (via rebase) commits that are not [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/)
- CI checks pass before merging into `main`
13 changes: 13 additions & 0 deletions backend/__tests__/server.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { treaty } from "@elysiajs/eden";
import { describe, expect, it } from "vitest";

import { app } from "../server";

const api = treaty(app);

describe("Elysia", () => {
it("return a response", async () => {
const { data } = await api.api.hello.get();
expect(data).toEqual({ message: "Hello World API!" });
});
});
5 changes: 5 additions & 0 deletions backend/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Elysia } from "elysia";

const api = new Elysia({ prefix: "/api" }).get("/hello", () => ({ message: "Hello World API!" }));

export default api;
30 changes: 30 additions & 0 deletions backend/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { staticPlugin } from "@elysiajs/static";
import { Elysia } from "elysia";

import api from "./api";

async function serveProductionFrontendAssetsIfAvailable(app: Elysia) {
const indexHTML = Bun.file("dist/index.html");
if (!(await indexHTML.exists())) return;

app
.use(staticPlugin({ assets: "public", indexHTML: false }))
.use(
staticPlugin({
assets: "dist/assets",
prefix: "/assets",
indexHTML: false,
}),
)
.get("/", () => indexHTML);
}

export const app = new Elysia().use(api);

await serveProductionFrontendAssetsIfAvailable(app);

app.listen({ port: 8000 });

console.log(`🦊 Elysia is running at ${app.server?.url}`);

export type Server = typeof app;
Binary file added bun.lockb
Binary file not shown.
4 changes: 4 additions & 0 deletions bunfig.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[test]
# We only use 'bun test' for the backend since there are still gaps in Bun for fully supporting SolidJS.
# When Bun officially supports SolidJS, we can then use 'bun test' for everything and deprectate Vitest.
root = "backend"
12 changes: 12 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
version: "3.4"

networks:
web-app:

services:
app:
build: .
networks:
- web-app
ports:
- "8000:8000"
16 changes: 16 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<link rel="shortcut icon" type="image/ico" href="/src/assets/favicon.ico" />
<title>Solid App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>

<script src="/src/index.tsx" type="module"></script>
</body>
</html>
Loading

0 comments on commit 7003e5e

Please sign in to comment.