---
description: This documentation is intended to provide comprehensive guidelines for contributing to the codebase.
files: */..
---
This documentation is intended to provide comprehensive guidelines for contributing to the codebase. It outlines the tools, environment, coding standards, and best practices that ensure our code remains clean, maintainable, and efficient.
- Write elegant, concise, and readable code
- Prefer
constoverlet(never usevar) - Use kebab-case for file and directory names
- Use clear, descriptive names for variables, functions, and components
- Always use ESM
importandexport(never use CJSrequire)- File imports should end with
.js(NOT.tsor.tsx). Module or subpath imports don't need the extension. - GOOD examples:
import { Foo } from './foo.js';import { type Route } from './+types/root.js';import zod from 'zod';import { logger } from '@dx/core-utils';
- BAD examples:
import { Foo } from './foo';(missing.jsextension)import { type Route } from './+types/root';(missing.jsextension)import { Foo } from './foo.ts';(should be.jsnot.ts)import { AnInternalClass } from 'zod/dist/internals';(shouldn't import internals)import { logger } from '@dx/core-utils/src/logger.js';(shouldn't import source files or use.jsfor packages)
- File imports should end with
- Always prefer named exports over default exports
- It's OK to use a default export in
.tsxfiles (like Remix/React Router routes) - It's OK to use default exports in the CLI because it is required by oclif
- It's OK to use a default export in
All packages must follow these package.json rules:
typemust be set tomoduleexportsshould be used instead ofmainormodule- ALL source code should be exported from the root (
.) path - Any test-specific code should be exported from a
testpath - Exports should use the
.tsextension - Good example:
"exports": "./src/index.ts""exports": { ".": "./src/index.ts", "./test": "./src/test/index.ts" }
- BAD example:
"main": "./src/index.ts"(don't usemainormodule)"exports": "./src/index.js"(don't use.jsfor exports)"exports": { ".": "./src/index.ts", "./test": "./src/test/index.ts", "./test/mocks": "./src/test/mocks.ts" }(don't export multiple paths: "./test" and "./test/mocks")
- ALL source code should be exported from the root (
typesshould be used instead oftypings- NEVER use TypeScript path resolution (
pathsintsconfig.json)- Use ESM subpath imports instead if this is desired (eg:
"imports": { "#/ui": "./ui/index.ts" })
- Use ESM subpath imports instead if this is desired (eg:
All packages must be placed in packages/<package-name>/. Each package should have the following:
- A
src/directory containing the core source code. - A single entrypoint in
src/index.tsthat exports the package's public API. - Place test helpers, utilities, or mocks in
src/test/with a single entrypoint insrc/test/index.ts. - Tests should be placed beside source code like
src/my-file.tsandsrc/my-file.test.ts(NOTsrc/test/my-file.test.tsortest/my-file.test.ts). - A standard set of config files (like
package.jsonandtsconfig.json) to ensure consistency. - The
package.jsonmust define an "exports" object with"." : "./src/index.ts"and optionally"./test": "./src/test/index.ts", ensuring clearly defined entrypoints for production and test.
Example:
packages/foo/package.json
packages/foo/tsconfig.json
packages/foo/src/index.ts
packages/foo/src/bar.ts
packages/foo/src/bar.test.ts
packages/foo/test/index.ts
packages/foo/test/setup.ts
- Eg: `export async function loader() {` and `export async function action() {` don't need return types
- NEVER use
any/unknownor cast values like(value as any)orvalue!in TypeScript outside of test files e.g.*.test.tsor test fixtures e.g.**/test-data.ts. - Don't rely on
typeof,ReturnType<>,Awaited<>, etc for complex type inference (it's ok for simple types) - Use
as constfor better type inference - Use type guards to narrow types in conditional blocks
- Create custom types for complex data structures used throughout the application
- Utilize TypeScript's utility types (e.g.,
Partial,Pick,Omit) to manipulate existing types - Never use
React.FC. Use a function declaration instead - Functions should accept an object parameter (like
argsorprops) instead of multiple parameters- Good examples:
function myFunction(args: { foo: boolean; bar: string }) {} function VideoPlayer(props: { sid: string }) {}
- Bad examples:
function myFunction(foo: boolean, bar: string, baz: number) {}
- Good examples:
- Arguments should be destructured in the function body, not the function definition. It's ok for React components to destructure props in the function definition.
- Good example:
function myFunction(args: { foo: boolean; bar: string }) { const { foo, bar } = args; }
- Bad example:
function myFunction({ foo, bar }: { foo: boolean; bar: string });
- Good example:
- Zod should be used to parse untrusted data, but not for data that is trusted like function arguments
- Zod unions should always be used instead of enums
- For example, this union
z.union([z.literal('youtube'), z.literal('spotify')])is better than this enumz.enum(['youtube', 'spotify'])
- For example, this union
- Promises (and
asyncfunctions which implicitly create Promises) must always be properly handled, either via:- Using
awaitto wait for the Promise to resolve successfully - Using
.thenor.catchto handle Promise resolution - Returning a Promise to a calling function which itself has to handle the Promise. If you can't infer this from the available context, add a warning that the promise may not be handled properly.
- Using
Server-side code is written using Bun. Use native Bun (and the Node APIs it supports) when possible.
- Use standard lib modules like
Bun.file,$shell commands,Glob, etc - Prefer standard lib modules over third-party alternatives
- Utilize the
node:protocol when importing Node.js modules (e.g.,import fs from 'node:fs/promises') - Prefer the promise-based APIs over Node's legacy sync methods
- Use
Errorobjects for operational errors, and consider extendingBaseErrorfor specific error types - Use environment variables for configuration and secrets (avoid hardcoding sensitive information)
Always prefer using standard web APIs like fetch, WebSocket, and ReadableStream when possible. Avoid Node.js-specific modules (like Buffer) or redundant libraries (like node-fetch).
- Prefer the
fetchAPI for making HTTP requests instead of Node.js modules likehttporhttps- Use the native
fetchAPI instead ofnode-fetchor polyfilledcross-fetch - Use the
kylibrary for HTTP requests instead ofaxiosorsuperagent
- Use the native
- Use the WHATWG
URLandURLSearchParamsclasses instead of the Node.jsurlmodule - Use
RequestandResponseobjects from the Fetch API instead of Node.js-specific request and response objects - Utilize
BlobandFileAPIs for handling binary data when possible - Use
TextEncoderandTextDecoderfor encoding and decoding text
- Prefer
async/awaitover.then()and.catch() - Always handle errors correctly (eg:
try/catchor.catch()) - Implement React Error Boundaries to catch and handle errors in component trees
- Use Remix Error Boundaries for handling errors in Remix routes
- Avoid swallowing errors silently; always log or handle caught errors appropriately
Comments should be used to document and explain code. They should complement the use of descriptive variable and function names and type declarations.
- Add comments to explain complex sections of code
- Add comments that will improve the autocompletion preview in IDEs (eg: functions and types)
- Don't add comments that just reword symbol names or repeat type declarations
- Use JSDoc formatting for comments (not TSDoc or inline comments)
- Common JSDoc tags to use:
@param: define a parameter on a function@returns: define the return type of a function@throws: define an exception that can be thrown by a function@example: provide an example of how to use a function or class@deprecated: mark a function or class as deprecated@see: define an external reference related to the symbol{@link}: create a link to the namepath or URL@TODO: mark a task that needs to be completed
- DO NOT use the following tags:
@file,@async
- Always use an instance of the
pinologger from@dx/core-utilsfor logging. - NEVER use
consoleorpinodirectly.
- All unit tests should Vitest
- DO NOT attempt to install or use other testing libraries like Jest
- Write unit tests for individual components and utility functions
- Test files should be named
[target].test.tsand placed in the same directory as the code they are testing (NOT a separate directory)- Good example:
src/my-file.tsandsrc/my-file.test.ts - Bad example:
src/my-file.tsandsrc/test/my-file.test.tsortest/my-file.test.tsorsrc/__tests__/my-file.test.ts
- Good example:
- Tests should be run with
bun run test(you can't do justbun test) - It's acceptable to use
any/unknownin test files (such as*.test.ts) or test fixtures (like**/test-data.ts) to facilitate mocking or stubbing external modules or partial function arguments, referencing the usage guidelines in the TypeScript section. However, do not useanyorunknownin production code.
- Test critical business logic and edge cases
- Don't add tests for trivial code or just to increase test coverage
- Don't make tests too brittle or flaky by relying on implementation details
- Branch names should start with
bot-123-the-issue-namewherebot-123is the Linear issue identifier associated with the branch andthe-issue-nameis the name of the issue (or a shortened version) - Commit messages should be short, concise, and descriptive
- Pull requests should have a short descriptive title and a description that explains the changes made
- PR titles and descriptions must accurately reflect the changes made and should be kept up to date if the PR changes significantly
- Linear identifiers (eg: BOT-123) should not be included in PR titles (they should be in the branch name)
- Proper capitalization and punctuation should always be used in commit messages and pull request titles and descriptions
- When a new package (
/packages/<package-name>) or app/service (/apps/<app-name>) is added to the repository, a corresponding entry should be added to the.github/CODEOWNERSand thereadme.mdfile in the root directory.