Welcome to CARMA, a monolithic repository (monorepo) powered by Nx, designed to streamline the development of reactive GIS applications. This repository leverages the Vite bundler to optimize the build process and improve developer experience with efficient tooling and clear project structures.
- Nx Monorepo: Utilizes Nx to facilitate better code sharing and tooling across multiple applications and libraries within the same repository.
- Vite Bundler: Employs Vite for fast and lean builds, enhancing development with rapid updates and optimized production builds.
- GIS-Focused: Tailored for developing GIS applications, integrating mapping technologies seamlessly with modern web technologies.
- React Framework: Built with React to create interactive UIs efficiently, making the application reactive and user-friendly.
- Node.js (LTS)
- npm
-
Clone the repository:
git clone https://github.com/cismet/carma.git cd carma -
npm install
-
on dev branch you can also run the custom script
npm run update-all
to refresh the repo and update all submodules
... node 20 or later
Before committing, ensure that the following conditions are met:
if only project is affected, run nx build before pushing.
npx nx build my-project-name
for the whole monorepot run,
npx nx run-many -t build --nxBail
to check that all projects build before committing.
Some possible additional Checks:
npx nx run-many -t lint --nxBail
npx nx run-many -t build-storybook --nxBail
npx nx run-many -t test --nxBail
If build errors occur due to submodules run:
npm run update-all
again, or just
git submodule update --init --recursive --remote --checkout --force
to force update to current remote state of all submodules in the repository.
carma is using Typescript 5.5 as of July 2024 the projects are not transpiled by TypeScript itself and does not emit js.
tsconfig settings exist primarily for DX.
vite build and vite-plugin-dts are taking care of the actual transpiling and typescript declaration.
- use exactly one base extend in connector
tsconfig.json:/tsconfig.base.jsonas default./tsconfig.legacy.base.jsononly when dependency compatibility issues require fallback.
- no custom project-level
compilerOptionsin connectortsconfig.json.
Manually imported/copied .js and .jsx files that don't have any meaningful type safety checks (ts-ified by lots of any) should stay as .js and .jsx and not be renamed to .ts and .tsx or flagged with ts-ignore before proper type safety is implemented.
All configurations should allow importing .js
Common custom CARMA types and type declarations for external libraries are defined in type-only packages. These packages use zero-build configuration: TypeScript consumes declaration files directly from source via path aliases, eliminating build steps for pure type definitions.
Type-only packages:
@carma-types- Global types index (libraries/types/)@carma-geo/types- Geographic types (libraries/geo/types/)@carma/units/types- Branded unit types with Radians-first convention (libraries/commons/units/types/)
Configuration: Type-only packages have no build target. Their package.json points exports directly to source (e.g., "types": "./src/index.d.ts"), and path aliases in tsconfig.base.json resolve directly to .d.ts files. This approach requires no compilation since TypeScript natively reads declaration files.
types are enforced to be imported separately from the module. e.g.:
import type { ReactNode } from "react";
import { useEffect } from "react";
not mixed in like
import React, { useEffect, ReactNode } from "react";
Use this canonical import order in repo code:
react,react-redux, and Node built-ins- true third-party packages such as
antd,d3,leaflet,maplibre-gl,three, Storybook, Vitest, and similar vendor modules - repo first-party packages that are not
@carma-*, especiallyreact-cismap,react-cismap/*,@cismet-dev/*, and@cismet/* - monorepo packages under
@carma-* - local relative imports
- order local relative imports from far to near (for example
../../foobefore../foobefore./foo) - do not jump across monorepo library boundaries with relative paths; use the package alias/import surface instead
- order local relative imports from far to near (for example
- side-effect imports last, especially CSS, widget styles, and other asset-only imports such as
import "cesium/Build/Cesium/Widgets/widgets.css";
Keep import blocks stable and alphabetized within each block.
For platform features that should remain mapping-engine-agnostic, prefer one shared feature package plus one explicit engine bridge package per mapping engine.
Example target shape:
libraries/mapping/annotations/
src/lib/core/* # contracts, domain logic, DTOs, pure transforms
src/lib/runtime/* # engine-agnostic orchestration only, if needed
libraries/mapping/annotations/cesium/
src/lib/runtime/* # Cesium scene, widget, and primitive bindings
libraries/mapping/annotations/maplibre/
src/lib/runtime/* # MapLibre GL JS bindings
libraries/mapping/annotations/leaflet/
src/lib/runtime/* # Leaflet or react-cismap bindings
Dependency direction:
annotationsmust not depend on Cesium, MapLibre GL JS, Leaflet, orreact-cismapannotations/cesiummay depend onannotations,@carma-cesium, and Cesium-specific helper packagesannotations/maplibremay depend onannotationsand MapLibre-specific helper packagesannotations/leafletmay depend onannotationsand Leaflet-specific helper packages- bridge packages should not depend on each other
Why this shape is preferred:
- shared feature logic stays vendor-light, testable, and easier to reuse
- mapping-engine retirement or replacement is localized to one bridge package instead of leaking through the feature core
- apps can opt into the engine bridges they need without scattering engine checks through shared providers or domain logic
A concrete example of this pattern is documented in libraries/mapping/engines-interop/navigation-controls/README.md.
uses eslint flat config in
can be run per project with
npx nx run [projectname]:lint
Default ruleset is tseslint.configs.strictTypeChecked.
with some custom react and a11y rules.
desirable should be 0 warnings for added code.
for nx to register changes to the file, one might need to clear the cache with
npx nx reset
or run with skip cache option
npx nx run [projectname]:lint --skipNxCache
Always do nx updates with the provided migrate utility.
-- npx nx migrate nx@latest
(prerequisites: have npm-check-updates installed globally with npm install -g npm-check-updates)
until further notice keep:
- eslint at 8.57
- prettier at 2.8.8
- react at 18
- vite at v5 (no v6 support in nx yet)
- vitest at 1.6
- ua-parser-js at 1.0.40 (v2 has AGPL license)
the remaining dev deps can be listed
npx npm-check-updates --dep dev --reject "eslint* vite* @vitest* prettier *storybook* react* @types"
update the individual packages as needed or use interactive mode for batch updates.
npx npm-check-updates --dep dev -i
should happen on a per package basis only as needed and has no update policy yet. be sure to check and update the complementing @types packages in dev deps as well.
