The rule of the boy scouts is:
Always leave the campground cleaner than you found it
When you find a mess on the ground, clean it, it doesn’t matter who did it. Your job is always to leave the ground cleaner for the next campers.
We apply this rule to our code, too; every time we have to refactor or improve old code, we also take care of updating it to our current quality standards.
Unless otherwise noted below we follow the airbnb coding guidelines as formulated here: https://github.com/airbnb/javascript
We agreed to have a soft limit of 100 characters per line and a hard limit of 150; This will make it easier to have multiple files open side by side in your IDE.
Lengthy functions are complicated to understand. That’s why functions should be small enough to carry out small work, and long functions should be broken into small ones for completing small tasks.
We have a soft limit of 25 lines per function.
Use async
and await
, and avoid having long promise chains.
Avoid using Bluebird. Use ES6 promises instead; when vortex was written, ES Promises were incomplete, so we used Bluebird; this is not the case anymore.
To get rid of Bluebird
we have to avoid certain constructs that are widely in use in Vortex but are Bluebird extensions:
somethingAsync().catch(ExceptionType, err => { … }) // NO
somethingAsync().catch(err => { if (err instanceof ExceptionType) { … } else { return Promise.reject(err); } }) // YES
Promise.map(stuff, item => somethingAsync(item)) // NO
Promise.all(stuff.map(item => somethingAsync(item))) // YES
for (const item of stuff) { await somethingAsync(item) } // ALSO YES
We enforce PascalCase for user-defined JSX components.
<TestComponent />
<TestComponent>
Use PascalCase for type names.
type NotificationFunc = (dismiss: NotificationDismiss) => void;
We have the ESLint rule no-explicit-any
disabled, but this does not mean you can use Any
freely.
Use Any
only when stricly necessary
We use I as a prefix for our interfaces. This is because most of the team has a C# background.
interface IBaseProps {}
Use PascalCase for enum values.
export enum Decision {}
Use camelCase for function names.
function fetchReduxState(tries: number = 5) {}
Use camelCase for property names and local variables.
let visibleLineCount = 0;
const copy = ordered.slice();
Use m as a prefix for private properties. It is a bit uncommon compared to using _ or nothing at all, but this is how we always do it, and at the moment, we have no good reason to change it.
class SplashScreen {
` `private mWindow: Electron.BrowserWindow = null;
}
We use UPPER_SNAKE_CASE for global and/or exported variables.
export const NEXUS\_MEMBERSHIP\_URL = 'https://users.nexusmods.com/register/memberships';
Either have all parameters and return type on one line if it fits within the soft limit or one line per argument like this:
function convertRow<T>(t: TFunction,
group: string,
rowId: string,
value: T)
: IRow<T> {
[...]
}
We prefer to pass generic parameters, like api or t, as the first parameters to functions.
function setShortcut(api: IExtensionApi, t: TFunction, profile: IProfile) {
` `[...]
}
All text has to be localizable on the front-end side, excluding errors.
The localized text has to be static.
Don’t:
const text = something ? 'give you up' : 'let you down'
const song = t(`Never gonna ${text}`)
Do:
const text = something ? 'give you up' : 'let you down'
const song = t(`Never gonna {{ text }}`,{ replace: { text } })
The way our error feedback works is that error reports from users are grouped based on the error message and stack so that we don't get too many duplicates.
If you have a dynamic part in the error message that depends on the users system (or is random or a url or something) it has to be in quotes so that it gets ignored for the purpose of grouping the error reports otherwise every single occurrence of this error (if message is set) will produce a new report.
throw new CustomError(`CustomName "${Dynamic information goes here in quotes}"`)
Proposal: If a file contains primarily a class, interface or react component, the file name matches that class, including case (that is: UpperCamelCase). Files primarily consisting of free-standing functions use lowerCameCase.
At the moment, we don’t aim for a 99.9999999% code coverage, but this does not mean we don’t write tests.
We agreed to write a test for all “off-path” and critical behaviors, such as changing the settings for the mod staging folders or the downloads directory. This should ensure that all critical code is tested and reliable.