Skip to content

Latest commit

 

History

History
105 lines (72 loc) · 5.17 KB

File metadata and controls

105 lines (72 loc) · 5.17 KB

This is a yarn classic + vite + ts + react 19 + tailwindcss v4 + openmct project

Unlike a regular react project, the page root is controlled by openmct, and react is used to render plugins inside openmct. There is typically one react tree per displayed plugin.

Development

Check if the dev server is running with ps aux | grep "vite", if not, start it with yarn dev.

OpenMCT Submodule

OpenMCT source lives in vendor/openmct/ (git submodule). The full documentation is at vendor/openmct/API.md.

NICK uses yarn classic, but openmct uses npm. They are independent projects with separate dependency trees. Never use yarn inside vendor/openmct/, and never use npm at the nick root.

IMPORTANT: If you encounter a type error on an OpenMCT API object, investigate whether the OpenMCT type definition is wrong first, and fix the type definition in vendor/openmct/ rather than working around it in NICK code. The JSDoc comments in the OpenMCT source are used to generate TypeScript declarations.

After modifying openmct source

  1. Run yarn install --force — this rebuilds openmct (via the preinstall hook) and re-copies it into node_modules/openmct.
  2. Clear vite dependency cache with rm -rf node_modules/.vite/deps
  3. Restart the dev server with yarn dev

Note: yarn install automatically runs npm install inside vendor/openmct via the preinstall script, which triggers openmct's prepare script (build:prod + tsc). So a fresh yarn install always builds openmct from source.

Committing openmct changes

When the user says to commit or push, the user means both the submodule and the parent git project.

The parent repo stores a pointer to a specific openmct commit, not the files themselves.

  1. Only if the user want to commit and push: run yarn tsc --noEmit, yarn test, yarn format:check and ensure no errors.
  2. Commit and push inside the submodule first:
    cd vendor/openmct
    git add -A && git commit -m "description of change"
    git push origin # if user wants to push
    
  3. Then update the parent repo to point to the new commit:
    cd ../..
    git add vendor/openmct
    git commit -m "update openmct submodule"
    git push                 # if user wants to push
    
  4. Always push the submodule before pushing the parent repo, otherwise CI will reference a commit that doesn't exist on the remote.

Architecture

Adding a new data source

  1. Create src/sources/<name>.ts implementing DataSource (allKeys() + subscribe()).
  2. In src/main.ts, call registerDataSource(new MySource()) (guard with getBackendType() if needed).

Adding a new layout

  1. Refer to src/layout/avionics.ts for an example.
  2. In src/main.ts, call openmct.install(MyLayoutPlugin) under the // register layouts here comment.

Measured timestamp vs. received timestamp

Datum.timestampMs is a single timestamp. If a sensor value carries both a measured timestamp (e.g. GPS fix time, which may be unavailable) and a received timestamp (wall-clock time the data arrived, always present), model them as two separate keys:

  • when measured timestamp is avaliable, emit one key with timestampMs = measured timestamp
  • when measured timestamp is unavaliable, emit another key with timestampMs = received timestamp

Then, in the layout, create an overlay plot with both keys.

Adding a new plugin

  1. Create src/plugins/<name>.ts. Export a plugin function MyPlugin(openmct: any).
  2. In src/main.ts, call openmct.install(MyPlugin).

TypeScript

General Guidelines

  • Prefer readability over performance: dumb code over clever solutions (e.g. for loop > reduce)
  • offensive programming: aggressively use types to convey contracts
    • assert non-null if all code path supports it (with comment explaining why), instead of adding if else.
  • No any: always use proper types
  • No vague object types: Record<string, unknown>, object, etc. are forbidden; define explicit types or derive.
  • Single source of truth: derive types from generated types or existing interfaces using Pick, Omit etc; reuse SDK types even if they have extra fields; use ReturnType, Awaited, Parameters, etc when types aren't exported
  • No barrel files: import directly from concrete module files, not re-export index.ts files
  • Document public APIs: JSDoc on all public methods/constructors with @param / @returns; keep docs in sync when modifying
  • Refactor call sites: when changing a function signature, update all call sites instead of adding optional parameters
  • Avoid hardcoded strings: export and reuse constants if possible

Before Finishing

IMPORTANT: Before you consider a task complete, always: (unless the task is trivial)

  1. Simplify: Re-examine all the files you just edited, think about all the related existing logic.
  • Collapse single-use abstractions
  • Align types with actual usage
  • Aggressively factor out duplicate logic between new code and existing code
  • Any other simplification you see appropriate
  1. Lint: Run yarn tsc --noEmit and yarn lint and ensure no type or lint errors.
  2. Test: Run yarn test and ensure all tests pass
  3. Format: Run yarn format at repo root.