feat: write style guide (#52)

Reviewed-on: https://codeberg.org/nhcarrigan/docs/pulls/52
Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com>
Co-committed-by: Naomi Carrigan <commits@nhcarrigan.com>
This commit is contained in:
Naomi Carrigan 2025-01-06 03:05:31 +00:00 committed by Naomi the Technomancer
parent 82d9038cdd
commit 691b5390bf
4 changed files with 610 additions and 9 deletions

View File

@ -89,6 +89,10 @@ export const navigation = [
label: "Contributor Covenant", label: "Contributor Covenant",
link: "/dev/covenant" link: "/dev/covenant"
}, },
{
label: "Style Guide",
link: "/dev/style"
},
{ {
label: "Issue/PR Labels", label: "Issue/PR Labels",
link: "/dev/labels" link: "/dev/labels"

View File

@ -329,10 +329,16 @@ Implement JWT-based authentication for API endpoints.
### 5.7 Testing Your Changes ### 5.7 Testing Your Changes
- Run the linter to ensure your code complies with [our style guidelines](/dev/style).
```bash
pnpm lint # or the appropriate lint command for your project
```
- Run any existing tests to ensure your changes haven't broken anything: - Run any existing tests to ensure your changes haven't broken anything:
```bash ```bash
pnpm test # or the appropriate test command for your project pnpm test # or the appropriate test command for your project
``` ```
- Add new tests for your changes if applicable. - Add new tests for your changes if applicable.
@ -383,7 +389,8 @@ git push -u origin <branchname>
- A clear description of the changes - A clear description of the changes
- The issue number(s) your pull request addresses - The issue number(s) your pull request addresses
- Any additional context or explanations - Any additional context or explanations
3. If your changes include visual elements, consider adding screenshots or GIFs to illustrate the modifications. 3. Fill out the rest of the pull request form *completely*.
4. If your changes include visual elements, consider adding screenshots or GIFs to illustrate the modifications.
### 6.4 Pull Request Best Practices ### 6.4 Pull Request Best Practices

View File

@ -0,0 +1,595 @@
---
title: Style Guide
---
This document outlines the style guide that applies to all of our projects.
## 1. Global Conventions
These sections apply to all code in any of our projects.
### 1.1. Copyright Notice
All code files should begin with a comment section containing the copyright information:
```js
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
```
Note that `copyright` _must_ be assigned to `nhcarrigan`, and our `license` must be applied, but when checking in new code you may attribute yourself as the author, or add yourself to the list for existing code.
## 2. JavaScript Projects
The following sections apply to our JavaScript/TypeScript projects. Style conventions are enforced through our [custom ESLint package](/projects/eslint).
:::caution
We do _not_ use Prettier in any of our projects. Instead, our linter package also handles code styling.
If you are using VSCode, you can add this to your `.vscode/settings.json` file in the project directory to enable auto-formatting on save.
```json
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"eslint.validate": ["typescript"]
}
```
Because of this, our style guide will not cover the formatting section of our linter package.
:::
## 2.1. Main Rules
These rules apply to all TypeScript code, and will run on files in `src/**/*.ts`.
### 2.1.1. `eslint` Enforced Rules
- Setters must have a corresponding getter.
- Array methods which return a value must return a value in the callback.
- Arrow functions should always use curly braces.
- Variables must be used within the scope they are defined.
- Comments should always start with a capital letter.
- Avoid overly complex functions.
- `return` statements must either never return a value, or always return a value.
- `if` statements and similar control blocks should always use curly braces.
- Switch statements must always have a default case, and the default case must always come last.
- Never use loose equality.
- A `for` loops iteration direction must match the conditional bound.
- Functions assigned to a property must match the name of the property.
- With the exception of callback functions, all functions should be assigned a name.
- When using the `function` keyword, prefer `function name()` declarations over `const name = function()` expressions.
- Getters should always return a value.
- Getters and setters should be grouped together.
- You should never use logical assignment operators.
- You should never have more than one class per file.
- Logical blocks should not be nested more than five levels deep. Prefer extracting into separate functions.
- Files should not have more than 300 lines. Prefer extracting into modular functions.
- Functions should not have more than 50 lines. Prefer breaking down into smaller logic.
- Callbacks should not be nested more than two levels.
- Functions should not have more than 20 logical statements/branches.
- Constructor names should begin with a capital letter.
- Do not use the `alert` API.
- Promise executor functions should not be async.
- Do not use `await` within loops.
- Do not use bitwise operators.
- Do not use `arguments.caller` or `arguments.callee`.
- Do not declare variables within switch case statements.
- Do not reassign class definitions.
- Do not compare with `-0`.
- Do not perform assignments in conditions e.g. `if (name = "Naomi")`.
- Do not use the `console` API, prefer our custom logger.
- Avoid confusing operator combinations e.g. `a + b ?? c`.
- Avoid constant conditions e.g. `if(false)` or `while(true)`.
- Constructors should not return a value. They implicitly return the instance.
- Do not use control characters `\x00` in regular expressions.
- Do not use `debugger` statements.
- Do not use `delete` on variables.
- Do not start a regular expression with `=` (confusing with `/=` operator).
- Functions should not have multiple parameters with the same name.
- Classes should not have multiple members with the same name.
- Objects should not have multiple keys with the same name.
- If/else statements should not use the same condition.
- Switch statements should not have duplicate case conditions.
- Import statements should not have duplicate imports.
- Prefer using early return over if/else.
- Block statements should not be empty.
- Character classes in regular expressions should not be empty.
- Destructured values should not be empty.
- Static class blocks should not be empty.
- You should not use loose equality with `null`.
- You should not use `eval`.
- Do not reassign the error parameter in a catch block.
- Do not extend native prototypes.
- Do not call `bind` unnecessarily.
- Do not cast to a boolean in conditions.
- Do not use `continue` or `break` unnecessarily.
- All case statements should have a break statement.
- Do not reassign function declarations.
- Do not reassign global values (e.g. `window`).
- Do not use implicit coercion (e.g. `!!bool`, `+num`).
- Do not declare global variables. All values should be scoped to a module.
- Do not reassign to imported variables.
- Do not declare functions in logical blocks.
- Do not pass invalid regular expression strings to `RegExp()`.
- Do not use `this` outside of classes.
- Do not use unusual space characters.
- Do not access `__iterator__`.
- Do not use loop labels.
- Do not create unnecessary blocks.
- Do not nest `if` within `else`, use `else if`.
- Do not use multi-code characters (such as emotes) within character classes.
- Do not chain assignment operators.
- Do not use `\` to create multi-line strings. Use concatenation or template strings.
- `if` conditions should not be in the negative (e.g. `!bool`).
- Do not nest ternary statements.
- Do not use `new` outside of an assignment.
- Do not use `new` with `BigInt` or `Symbol`.
- Do not use `new` with `String`, `Number`, or `Boolean`.
- Do not use octal escapes (e.g. `\8`, `\2`).
- Do not call the `Math`, `JSON`, `Reflect`, `Atomics`, or `Intl` objects as functions or constructors.
- Do not call `new Object()` without an argument.
- Do not use octal literals e.g. `071`.
- Do not reassign function parameters.
- Do not use the `++` operator.
- Do not use `return` in promise executors.
- Do not use the `__proto__` property.
- Prefer calling the Object prototype over builtins (e.g. `Object.prototype.hasOwnProperty.call(foo, "bar")` over `foo.hasOwnProperty("bar")`).
- Do not use literal spaces in regular expressions, use the escape sequence `\s`.
- Do not return assignment operations.
- Do not use `javascript:void` URLs.
- Do not assign a variable to itself.
- Do not compare a variable with itself.
- Do not use the comma operator to evaluate multiple expressions.
- Setters should not return a value.
- Do not allow sparse arrays.
- Do not use `${foo}` template interpolation in regular strings.
- Do not reference `this` before calling `super` in constructors that call `super`.
- Do not reference variables that are not declared.
- Do not initialise variables to `undefined`.
- With the exception of trailing underscores for unused variables, do not use dangling underscores.
- Do not insert line breaks in confusing locations, such as the middle of a variable assignment.
- Do not use a variable in a loop condition that is not modified within the loop.
- Do not use a ternary if a simpler approach exists.
- There should be no unreachable code.
- Do not use control statements in `finally` blocks.
- You must use parentheses when negating `in` or `instanceOf`, e.g. `if (!(key in obj))`.
- Do not use optional chaining where an undefined value is not allowed.
- Do not declare private class members that are not consumed by the class.
- Do not declare variables that are unused.
- Avoid backreferences in regular expressions when they would match nothing.
- Do not use `.call()` or `.apply()` unnecessarily.
- `catch` statements should _handle_ an error, not turn around and throw the error.
- Do not use computed-key syntax for literal keys e.g. `{["a"]: "b"}`.
- Do not concatenate string literals without variables.
- Do not escape characters unnecessarily.
- Do not rename a reference to the same name e.g. `let { a: a } = b;`.
- Do not use redundant `return` statements.
- No use of the `var` keyword.
- Do not merge `TODO` or `FIX` comments into the main branch.
- Do not use the `with` statement.
- An object must use either all long-form definitions `{a : a}` or all short-form definitions `{ a }`, but never a mix of the two `{ a, b: c}`.
- Avoid shorthand operators `+=`, prefer the explicit `x = x + y`.
- Callback functions should be anonymous arrow functions.
- Use `const` for all variables that are not reassigned.
- Capture groups should be named.
- Prefer `Object.hasOwn()`.
- Prefer the spread operator over `Object.assign`.
- Prefer regular expression literals over the RegExp constructor.
- Prefer using the `...` operator instead of `arguments`.
- Prefer the spread operator over `.apply()`.
- Prefer template literals over string concatenation.
- `parseInt()` should always be given a radix.
- When an asynchronous function relies on an argument that might change (such as an object reference), care must be taken to avoid race conditions within that function.
- Generator functions must use the `yield` keyword.
- Object keys must be sorted alphabetically.
- Variables within the same declaration block must be sorted alphabetically.
- If using the `Symbol` function, you must include a description.
- Do not compare to `NaN`, use `Number.isNaN()`.
- `typeof` comparators must be a vaild return type of `typeof`.
- Conditions should always start with the variable, not the constant.
### 2.1.2. `typescript-eslint` Enforced Rules
- Do not use `await` on functions that do not return a Promise.
- Exported type definitions should use the `export type` keyword.
- Use dot notation for property access, unless such syntax would be invalid.
- Do not use the `delete` keyword with arrays.
- Do not use `<Object>.toString()` on objects. Prefer `JSON.stringify()`.
- Do not assign void values to variables, such as `const log = console.log("Bad");`.
- Do not duplicate types (e.g. `type myType = "A" | "A"`).
- Promises should always be awaited.
- Do not implicitly evaluate code, such as with `new Function()`.
- Do not use `void` with functions that do not return a value.
- Do not use Promises in places that do not consume them (such as callbacks).
- Do not combine string and number values in an enum.
- Do not use unions/intersections with overriding values (such as `any` or `unknown`).
- Do not use comparison operators with boolean values (e.g. `variable === false`).
- Do not pass values to a conditional statement that are always truthy or falsy.
- Do not use unnecessary namespace references.
- Do not use template strings without interpolated values.
- Do not pass type arguments to a function that match the default value of that parameter.
- Do not define unused type parameters in a function.
- Values with an `any` type should not be passed to function calls.
- Values with an `any` type should not be assigned to variables.
- Values which have an `any` type should not be called as functions.
- Do not merge declarations (instead of `interface Foo` and `class Foo`, use `interface Foo` and `class Bar implements Foo`).
- Do not compare enum values against arbitrary values.
- Do not use the `Function` type. Functions should have a specific type signature.
- Values with an `any` type cannot have properties, and such properties should not be accessed.
- Functions should not return an `any` value.
- The unary minus operator should only be used on numbers.
- `throw` statements should only throw an `Error` instance.
- Object properties should be destructured, rather than assigned to variables via direct access.
- Use `<Array>.find()` instead of `<Array>.filter()[0]` to find an element in an array.
- Use `<Array>.includes()` instead of `<Array>.indexOf() === -1`.
- Use `??` instead of `||`.
- Use optional chaining when possible.
- Promises that reject should always return an Error.
- Class members that are not modified outside of the constructor should be `readonly`.
- The `.reduce()` callback should have typed parameters.
- When using a non-global regular expression, use `<RegExp>.exec` over `<String>.match()`.
- Class methods that return `this` should have `this` as the return type, not the class.
- When testing the beginning or end of a string, use `<String>.startsWith()` or `<String>.endsWith()`.
- Functions which return a Promise must use the `async` keyword.
- `<Array>.sort()` should always be given a compare function.
- `async` functions must use `await`, or be rewritten without the promise.
- The `+` operator can only be used with operands of the same type.
- Values interpolated in a template string must be of type `string`.
- Functions which return a promise should await that return value.
- Conditions should not use nullable values - you must always explicitly compare against `null` or `undefined`.
- Switch statements are not preferred, but when you must use them it should have exhaustive cases.
- Unbound class methods must be called with the expected scope (e.g. `myClass.method.bind(myClass)`).
- The `error` parameter in a catch statement is of type `unknown`.
- Function overload signatures must be grouped together.
- Array type definitions should use the generic `Array<T>` over `T[]`.
- `@ts` directives are disallowed, except for `@ts-expect-error` which requires a description.
- Classes with static public properties should have a getter for each property.
- Class methods which are not static must make use of `this` or be converted to static.
- Prefer passing type arguments to a constructor instead of a type annotation.
- Prefer `Record<string, T>` over `{ [key: string]: T}`.
- Do not use any type assertions (`x as T`).
- Object types should be defined with `interface` over `type`.
- Imported values which are only used as types should be imported with `type`.
- Function parameters which are optional or have a default value should come after all mandatory parameters.
- Functions should always have an explicit return type.
- All class members should have an accessibility modifier (`public`, `protected`, `private`).
- All exported functions and public class methods should have explicit parameter and return types.
- Variables should always be initialised with a value.
- Functions which take more than three arguments should take a single object instead.
- Class members should be ordered as `constructor`, `fields`, `statics`, `getters`, `setters`, then `methods`.
- Method types should use the property signature `func: (arg: string) => string`.
- Variables should use `camelCase`, types and classes should use `PascalCase`. Only variables may use a leading underscore to indicate they are unused.
- Never use the `Array` constructor.
- Do not use non-null assertions.
- Enums should not have duplicate values.
- Do not use the `delete` operator on dynamic property accesses.
- Functions, interfaces, and object types should never be empty.
- Do not use `any`.
- Classes should not consist solely of static members.
- The `for...in` loop should not be used.
- When all imported members are type imports, use `import type { a, b }` over `import { type a, type b }`.
- Do not use type annotations for variables with a string, number, or boolean value. These can be inferred.
- The `void` type should only be used as the _sole_ return type of a function. `(): void | string` is invalid.
- Do not reference unsafe values in a loop.
- Do not use number literals which would lose precision due to memory constraints.
- Classes should use `constructor()`, not `new()`. Interfaces for the class should use `new()`, not `constructor()`.
- Do not use namespaces.
- Do not use `require()`.
- Do not declare variables in an inner scope that share a name with a variable in the outer scope.
- Do not alias `this`.
- Do not needlessly assign constructor parameters to class members of the same name.
- Do not `extends` `any` or `unknown`.
- Unused expressions should be removed.
- Unused variables should be removed.
- Do not access a variable before its definition.
- Do not define empty constructors.
- Do not define empty exports.
- Do not use wrapper types (e.g. use `boolean`, not `Boolean`).
- Use `as const` over literal types.
- Enum values should always be explicitly initialised.
- The `for...of` loop should be used over the `for` loop, when the index _value_ is not needed.
- Enum values should always be literal values.
- Do not use TypeScript's `///` reference.
- Do not use overloads when the same result can be achieved via union types or optional parameters.
### 2.1.3. `eslint-comments` Enforced Rules
- Do not use blanket `eslint-disable` directives. All `disable` directives must target specific rules.
- All `disable` directives must include a comment explaining why the linter is being bypassed.
### 2.1.4. `deprecation` Enforced Rules
- Do not use deprecated methods/features, even if the package still supports them.
### 2.1.5. `import` Enforced Rules
- Do not use default imports if the module does not have a default export.
- Do not import modules simply to re-export them.
- Exports should always be at the end of the file.
- With the exception of packages, imports should always have a file extension.
- Imports should always be at the beginning of the file.
- If a module has multiple named exports, group them in a single `export { a, b }` declaration.
- Import statements should be followed by a newline.
- Import paths should never be absolute
- You should not use AMD syntax
- You should not use CJS syntax
- You should not define cyclical imports (if file a imports file b, file b CANNOT import file a)
- Modules should not use a default export.
- A module should not import the same file multiple times.
- You should not use dynamic `require` calls.
- Named import blocks cannot be empty.
- You should not import packages that are not included in the `package.json` file.
- You cannot `import` a CommonJS module.
- Exports must always be declared with `const`.
- You cannot use a named export as the default export.
- You cannot use a named export as the property of a default export.
- You should not have orphaned modules (modules without any exports, or a module which is never imported).
- You cannot use webpack loader syntax.
- Imports must be sorted alphabetically, first grouped by: builtin node modules, external packages, internal packages, modules in parent directory, modules in current directory, type-only imports.
- There should be no new lines between these groups.
### 2.1.6. `jsdoc` Enforced Rules
- `@access` tags should be one of `package`, `private`, `protected`, or `public`.
- Asterisks should be aligned.
- Lines should not be improperly indented.
- `@param` names should reflect the actual parameters.
- `@property` names should reflect the actual properties.
- Should not use `=` (GCC syntax).
- All tags must be valid JSDoc tags.
- `@template` names should be used in the `@typedef`.
- `@license` tag MUST be set to `Naomi's Public License`.
- Tags that do not expect content should not have content.
- `@implements` should only be used on constructors or classes.
- Tag descriptions should not be just a reflection of the name.
- Description tags must be complete sentences.
- JSDocs must be multiple lines. The first and last line cannot have text content.
- JSDoc syntax must be valid.
- JSDocs must have a description.
- JSDocs cannot be empty.
- A hyphen should separate the parameter name from the description.
- JSDocs are required on all public functions, classes, and methods. They are encouraged on all public exports.
- Parameters must have a name and description.
- Properties must have a name and description.
- If a function returns a value, it must be documented in `@returns`.
- If a function throws an unhandled error intentionally, it must be documented in `@throws`.
- If a generator yields a value, it must be documented in `@yields`.
- Tags must be sorted alphabetically.
- There should be no blank lines between tags.
- Any types referenced must be valid types.
### 2.1.7. `unicorn` Enforced Rules
- Regular expressions should use special shorthand where possible.
- `catch` blocks should always have `error` as the parameter.
- When destructuring an object, all consumed properties should be destructured. Do not perform direct property access on an object that is previously destructured.
- When spreading a ternary in an array, both sides of the expression must be of the same type.
- Functions should be declared in the highest scope possible.
- `Error()` constructor calls must always be given a message.
- Escape sequences must use uppercase text.
- Files should be named in camelCase.
- Do not pass function references as callbacks to array methods.
- Do not use `.forEach()`.
- Do not use `this` in array methods.
- Do not chain `.push()` calls.
- Do not use `.reduce()`.
- Do not access properties directly from an `await` expression.
- Do not use `await` in Promise methods such as `.all()`.
- Files should not be empty.
- Do not use a `for` loop when you can use a `for...of` loop.
- Use Unicode escape sequences over hexadecimal escape sequences.
- Use `Array.isArray()` over `instanceof Array`.
- `GET` or `HEAD` requests should never have a body.
- Do not declare variables that start with `new` or `class`.
- Do not use `.length` as the argument to `.slice()`.
- Use `Buffer.from()`, not `new Buffer()`.
- Do not use objects as default parameters.
- Do not pass single-element arrays to Promise methods.
- Do not define a class that only has static members.
- Do not use `.then()`.
- Do not assign to `this`.
- Do not use `typeof x === "undefined"`.
- `await` cannot be used on non-promise values.
- You cannot ignore consecutive values when destructuring an array, e.g. `[,,,, val]`.
- You cannot use IIFEs.
- Promises should not return `.resolve()` or `.reject()`.
- You should not spread an array into a new array without adding values.
- You should not associate any case statements with the default statement.
- Number literals cannot have trailing zeroes after the decimal place.
- Number literals must use proper case.
- Numbers over 9999 should use `_` to separate each group of three digits, e.g. `10_000`.
- If you need to flatten an array, use `Array.flat()`.
- Prefer `.flatMap()` over `.map().flat()`.
- Prefer `indexOf` over `findIndex`.
- Prefer `.some()` over `.filter().length`.
- Prefer `.at()` for accessing array and string indices.
- Prefer `.codePointAt()` over `.charCodeAt()`.
- Prefer `Date.now()` over `new Date().getTime()`.
- Use default parameters instead of reassigning undefined values.
- Use `Math.trunc` to round numbers, not bitwise operators.
- Use ESM modules, not CJS.
- Use coercion functions such as `String()` directly.
- Use a negative index instead of `.legnth - 1`.
- Use the `node:` protocol when importing built-in packages.
- Use `Number` properties instead of global helpers e.g. `Number.isNaN()` over `isNaN()`.
- Use `Object.fromEntries` to create an object from key-value pairs.
- If a `catch` block does not use the `error` parameter, the parameter should be omitted.
- Prefer calling prototype methods over instance methods.
- Prefer using `Set.has()` over `Array.includes` to check for existence.
- Prefer using `Set.size` over `Array.length`.
- Prefer using the spread operator over `Array.concat()` or `String.split("")`.
- Prefer using `String.replaceAll()` over regex searches.
- Prefer `String.slice()` over `String.substr[ing]()`.
- Prefer `String.startsWith()` or `.endsWith()` over `RegExp.test()`.
- Prefer `String.trim[Start|End]()` over `String.trim[Left|Right]()`.
- Use `structuredClone` to create a deep clone of an object.
- Use top-level await instead of IIFEs.
- Variable names should not be abbreviated.
- `Array.join()` must always be given a separator.
- `Number.toFixed()` must always be given a digits argument.
## 2.2. React Rules
These rules apply to TSX, and will run on files in `src/**/*.tsx`.
- Boolean properties should be named accordingly, such as `isEnabled` instead of `enabled`.
- Buttons must always have a `type` attribute.
- If a radio button or checkbox is checked, it must be `readonly` or have an `onChange` handler.
- Default properties should match the property types.
- Properties, state, and context should always be destructured.
- Components must always have a nmae.
- A component cannot consume another component's propTypes.
- PropTypes cannot use `any`, `array`, or `object`.
- `forwardRef` components must use `ref`.
- Components should always be defined as named arrow functions.
- State should have matching names, e.g. `[color, setColor]`.
- `iframes` should always use the `sandbox` attribute.
- Boolean properties should always be explicitly written: `<Component prop={true}>`.
- Spaces between JSX elements must be explicitly written: `{' '}`.
- Files that contain JSX must have the extension `.jsx` or `.tsx`.
- Fragments should always use the shorthand syntax.
- Event handlers must be appropriately named: `onChange={this.handleChange}`
- Iterators must always have a `key` prop, and it should never be the index.
- You should not use `.bind()` in component props
- You should not insert comments as text nodes.
- Contexts should not use mutable values (e.g. objects), to prevent re-renders.
- Components should not have duplicate properties.
- Avoid leaking conditional values. Prefer `condition ? <Component> : null` over `condition && <Component>`.
- Element text should always be wrapped in JSX containers.
- You should never use `javascript:` urls.
- `target=_blank` must always be accompanied with `rel=noreferrer`.
- You should not use variables that are not declared.
- You should not use fragments unless there is no singular wrapping element.
- Components should use PascalCase.
- You should not spread props multiple times.
- Props should be sorted alphabetically.
- You should not access `this.state` inside any `setState` functions.
- You should not use arrow functions for lifecycle methods.
- Children cannot be passed as props.
- You cannot use dangerous properties (`dangerouslySetInnerHTML`).
- You cannot use deprecated methods.
- You cannot use `setState` in `componentDidMount` or `componentDidUpdate` or `componentWillUpdate`.
- You cannot directly mutate `this.state`.
- You cannot use `findDOMNode`.
- You cannot use `isMounted`.
- Each file should have only one component.
- `ReactDOM.render`'s return value should not be used.
- You should not use string references.
- `this` cannot be used in stateless functional components.
- HTML entities must be escaped.
- You cannot use unknown HTML properties.
- You cannot use unsafe lifecycle methods.
- You cannot define a component within another component.
- Class components should not have unused methods.
- Components should not have unused propTypes or state
- Class components should use the ES6 `class Component extends React.Component` syntax.
- Props should be readonly.
- Stateless components should be pure functions.
- Property accesses must be reflected in the propTypes.
- Any optional property must have a default value.
- Classes using the `render` method MUST return a value.
- Components without children must still have a separate closing tag.
- State initialisation must always happen in the constructor.
- Static properties should be defined as `static name = `.
- The `style` property must always be an object.
- Void elements `img`, `br` etc. must never have children.
## 2.3. Playwright Rules
These files apply to Playwright End-to-end tests, and will run on `e2e/**/*.spec.ts`.
- All tests must have at least one `expect` assertion.
- `describe` calls cannot be nested more than two levels deep.
- Playwright APIs must be `await`ed.
- Tests cannot be commented out.
- `expect` cannot be called conditionally.
- Tests cannot contain conditional logic.
- Setup and teardown hooks cannot be duplicated.
- You cannot use element handles (`page.$`).
- You cannot use `page.$eval()`.
- Tests cannot be focused with `.only()`.
- Tests cannot be forced with `{ force: true }`.
- You cannot use `getByTitle()`.
- You cannot use the `networkidle` option.
- You cannot use `first()`, `lat()`, or `nth()`.
- You cannot use `page.pause()`.
- You cannot use `page.locator()`. Prefer specific methods like `page.getByRole()`.
- Tests cannot be skipped with `.skip()`.
- `expect` can only be called within test blocks.
- You cannot reference variables in `page.evaluate()`.
- Some Playwright methods are synchronous - these cannot be `await`ed.
- You cannot use `.not()` when a specific matcher exists.
- You cannot use `page.waitForSelector()` or `page.waitForTimeout()`.
- Prefer `.toBeGreaterThan()` over `(x > 5).toBe(true)`.
- Prefer `.toBe(5)` over `(x === 5).toBe(true)`
- Hooks should be ordered as Playwright calls them: `beforeAll`, `beforeEach`, `afterEach`, `afterAll`.
- Hooks should be at the top of the test.
- Test names should be lowercase.
- Use `.toStrictEqual()` over `.toEqual()`.
- Use `.toBe()` over `.toStrictEqual()` for primitive values.
- Use `.toContain()` over `.includes()`.
- Use `.toHaveCount()` over `locator.count()` and `.toHaveLength()` over `.length`.
- Prefer assertions like `.toBeVisible()` over `(locator.isVisible()).toBe(true)`.
- Setup and teardown code must be in a hook.
- `.toThrow()` assertions require a message.
- Tests must be within a `describe` block.
- All `expect()` calls must have a custom message.
- Promises that contain an `expect` must be awaited.
- Tests must have a title that is not empty.
## 2.4. Vitest Rules
These files apply to Vitest unit tests, and will run on `test/**/*.spec.ts`.
:::warn
We also mandate the use of `describe`, `it`, and `expect` over `suite`, `test`, and `assert`.
:::
- Files must have `.spec.ts` extension.
- consistent-test-it
- Tests must have at least one `expect`.
- `describe` calls cannot be nested more than two levels deep.
- Prefer `.toHaveBeenCalled()` over `.toBeCalled()`.
- Tests cannot be commented out.
- `expect` cannot be called conditionally.
- Tests cannot contain conditional logic.
- Tests cannot be run conditionally.
- Tests cannot be disabled.
- You cannot use the `done()` callback.
- Setup and teardown hooks cannot be duplicated.
- Tests cannot be focused with `.only()`.
- Test titles must be unique.
- `node:test` cannot be imported.
- Snapshots cannot use string interpolation.
- `expect` must be within `it` or `test`.
- Tests cannot use `return`.
- Prefer `toHaveBeenCalledWith()` over `toHaveBeenCalled()`.
- Prefer `.toBeGreaterThan()` over `(x > 5).toBe(true)`.
- Use `describe.each` instead of manual loops.
- Prefer `.toBe(5)` over `(x === 5).toBe(true)`.
- All tests must start with `expect.assertions(number)`.
- Use `expect.resolves(fn)` over `expect(await fn)`.
- Hooks should be ordered as Vitest calls them: `beforeAll`, `beforeEach`, `afterEach`, `afterAll`.
- Hooks should be at the top of the test.
- Test titles should be lowercase.
- Prefer `vi.fn().mockResolvedValue(val)` over `vi.fn().mockImplementation(() => Promise.resolve(val))`.
- Prefer `vi.spyOn(Date, "now")` instead of overwriting the global `Date.now = vi.fn()`.
- Use `.toStrictEqual()` over `.toEqual()`.
- Use `.toBe()` over `.toStrictEqual()` for primitive values.
- Use `.toBeFalsy()` over `.toBe(false)`.
- Use `.toBeObject()` over `.toBeInstanceOf(Object)`.
- Use `.toBeTruthy()` over `.toBe(true)`.
- Use `.toContain()` over `.includes()`.
- Use `.toHaveLength()` over `.length`.
- Use `test.todo()` instead of `test.skip()`.
- Setup and teardown code must be in a hook.
- `.toThrow()` assertions require a message.
- Tests must be within a `describe` block.
- The `describe` callback should not have any parameters and cannot use `return`.
- All `expect()` calls must have a custom message.
- Tests must have a title that is not empty.

View File

@ -6,14 +6,9 @@ title: Join Our Staff Team
Unless explicitly stated otherwise in a separate written agreement, all positions within our organization are on a voluntary basis. No compensation, monetary or otherwise, should be expected for these roles. Unless explicitly stated otherwise in a separate written agreement, all positions within our organization are on a voluntary basis. No compensation, monetary or otherwise, should be expected for these roles.
## 2. Selection Process ## 2. Application Process
Our team member selection process is based on the following criteria: If you feel ready to apply to join our team, please complete the [application form](https://nhcarrigan.com/apply).
1. Active participation in the community
2. Length of membership in the community
3. Quality and nature of interactions with other community members
If you are selected as a potential team member, our representative, Naomi, will contact you directly with further information and instructions.
## 3. Legal Disclaimer ## 3. Legal Disclaimer