The project uses pnpm to manage the code in separate packages. This allows for cleaner code and package files.
To prepare for development, pnpm must bootstrap the cross-dependencies between projects. To do this, install pnpm and
then simply run pnpm install
or npm run bootstrap
.
The package dependencies are slighly complicated to manage properly, so that all tooling understands them:
- Each
package.json
refers to the packages it depends on, with versionworkspace:*
. pnpm then symlinks the packages intonode_modules
. - All files are imported from
@tietokilta/ilmomasiina-foo
or@tietokilta/ilmomasiina-foo/dist
, just as they would in external code that depends on Ilmomasiina. - Each importable
package.json
specifiesexports
, including a root export and potentially other files in./dist
.- These main exports point to the compiled
.js
and.d.ts
files under./dist
. ./src
is also exported and points to.ts
files for TypeScript compilation.
- These main exports point to the compiled
references
in eachtsconfig.json
points to othertsconfig
s.- This allows us to import files from other packages as if they were already compiled, and the TypeScript compiler
will automatically compile them on demand, even if the target
dist
doesn't exist already. - This also requires using
tsc --build
for both type checking and building.
- This allows us to import files from other packages as if they were already compiled, and the TypeScript compiler
will automatically compile them on demand, even if the target
ts-node
(and by extensionts-node-dev
), which we use for the backend, doesn't understandreferences
. Therefore, the cross-package imports are also defined inpaths
intsconfig.json
, whichts-node
does understand.- Vite (used for frontend builds) also doesn't understand
references
, so we usepaths
again, along with thevite-tsconfig-paths
plugin.
To avoid mysterious errors from TypeScript compiler instances running simultaneously on the same folder, the root
project's package.json
specifies --workspace-concurrency=1
to prevent pnpm from running those tasks in parallel.
The project is divided into four packages. Source folders are listed under each, roughly in order of importance.
ilmomasiina-models
contains the single source of truth for the data model and API:src/schema
: TypeBox OpenAPI schema for the API layer.src/models
: The JS column types for DB models. These are implemented by the Sequelize models inilmomasiina-backend
.src/attrs
: Defines the attribute names used included in responses, passed to Sequelizeattributes
.
ilmomasiina-backend
contains the backend code and depends onilmomasiina-models
.src/config.ts
: Config loading and validation. All environment variable access goes through here.src/models
: Sequelize models implementing the interfaces fromilmomasiina-models
.src/routes
: API route implementations. Most code goes here.src/cron
: Functions that run periodical maintenance tasks.src/locales
: Locale files for things like email subjects.src/mail
: Code for formatting and sending emails.emails
: Pug templates and CSS for email templates.test
: Backend test code.
ilmomasiina-components
contains reusable components for the user-facing parts of the frontend.src/modules
: API access and minimal state logic for each route provided by this package. See state-context.md for more on what these files contain.src/routes
: React implementations of the routes using React-Bootstrap.src/locales
: Locale files for the routes.src/styles
: Shared styles for the React route implementations here.src/config
: Global state used for integrating the routes to other code.src/contexts
: Contexts used for integrating the routes to other code.src/components
: Some reusable components shared between routes.
ilmomasiina-frontend
contains the frontend code and Vite config for building and developing it. It also depends onilmomasiina-models
andilmomasiina-components
but notilmomasiina-backend
.src/modules
: Redux reducers for each route.src/routes
: React implementations for each route.src/locales
: Locale files for the app.src/styles
: Styles for the app.src/containers
: Router for the app, for both the routes from this package andilmomasiina-components
.src/components
: Some reusable components shared between routes.src/store
: Redux store.src/branding.ts
: Definitions for some configurable branding strings.src/paths.tsx
: Definitions for the router paths.
- In addition, the root folder has a
package.json
, which is used for ESLint and other development dependencies that are shared between the packages. That package contains no code.
Many of the libraries listed below were inherited from the Athene version of the code and might be subject to change, if it benefits the project.
- TypeScript everywhere
- pnpm to manage the multiple packages
- Lodash used where necessary, but preferring native methods
- TypeBox to create JSON Schema with TS typings
- Sequelize as ORM
- Fastify as REST backend
- Nodemailer to send emails
The frontend is built with Vite.
Libraries:
- React v17 with mostly functional components
- React upgrade is dependent on switching/upgrading from React-Bootstrap v1, which is a major upgrade
- Bootstrap v4 and React-Bootstrap v1 for UI components
- SCSS
- Redux and React Redux
- Some state is handled locally, if there's no need to share it between components
- React Router
- i18next for internationalization
- Formik
The project is configured to run development servers for the frontend and backend with npm start
. In development,
Vite proxies API calls to the backend server.
In production, the backend server can optionally serve the compiled frontend bundle, but ideally a separate reverse proxy such as Nginx or e.g. an Azure Static Website is used for this purpose.