Thanks for your interest in contributing. Here are some common scenarios for what you may want to contribute to.
- Feel free to post your question on our Discourse or join our Matrix Support Channel.
-
Ensure the bug wasn't already reported by searching on GitHub under Issues.
-
If you're unable to find an open issue addressing the problem, open a new one. Be sure to use one of the templates we provide if your request applies to them. If not, use the 'Question / Other' template.
-
Open a new GitHub pull request with the patch.
-
Ensure the PR description is precise about the problem and your solution. Just fill out our template. That should cover the most important information.
- Suggest your idea in the HedgeDoc Dev Channel and start writing code. Our maintainers and other project developers can provide useful details about the architecture and show you relevant issues and discussions.
If you want to improve a translation or add a new translation altogether, we handle those via POEditor.
HedgeDoc is a volunteer effort. We encourage you to pitch in and to help us making this project even better.
Thanks! ❤️ ❤️ ❤️
By contributing to this project you agree to the Developer Certificate of Origin (DCO). This document was created by the Linux Kernel community and is a simple statement that you, as a contributor, have the legal right to make the contribution. The DCO is a legally binding statement, please read it carefully.
If you can certify it, then just add a line to every git commit message:
Signed-off-by: Jane Doe <[email protected]>
Use your real name (sorry, no pseudonyms or anonymous contributions).
If you set your user.name
and user.email
git configs, you can sign your commit automatically with git commit -s
.
You can also use git aliases like git config --global alias.ci 'commit -s'
.
Now you can commit with git ci
and the commit will be signed.
Most of the code style is enforced by prettier and our eslint configuration.
If your IDE doesn't support integration of prettier and/or eslint you can use the npm tasks lint
and format
to check your code style.
For both npm tasks, there is also an additional :fix
task. This will try to fix the code to the best of either tools ability.
Try to keep the files as short as possible while keeping the context between the code parts by
- splitting your code into multiple files. Especially react components can and should be separated into atomic components and custom hooks.
- avoiding repetition.
- extracting types and interfaces into
.d.ts
files.
We prefer lambda functions over the function
keyword. Simple functions, that return a value can be shortened to one-liners.
👍 Good:
const addTwo = (x: number): number => x + 2
👎 Bad:
function addTwo (x: number): number {
return x + 2
}
Names of functions should
- be as short as possible while clearly communicating their purpose.
- not include technical details, if not necessary.
- avoid abbreviations.
👍 Good:
const addTwo = (x: number): number => x + 2
👎 Bad:
const doStuffWithX = (x: number): number => x + 2
👎 Bad:
const incrementXTwoTimesButNotRecursive = (x: number): number => x + 2
👎 Bad:
const clcX = (x: number): number => x + 2
To make the code as clear as possible to everyone, who will try to understand it, every function must have an ESDoc.
Please make sure that your documenation can be read in a standard text editor (hard line breaks at ~120 characters, etc.).
The documentation must
- explain what the function does. If the explanation is longer than one line, then write a short introdoctory first line, followed by a break and then the longer explanation.
- contain every parameter with the
@param name explaination
annotation, that explains the purpose of the parameter. - contain a
@return description
annotation if the return type isn'tvoid
, that explains the meaning of the value. - contain
@throws ErrorClass description
annotations if the function throws runtime errors or doesn't catch errors from other method calls. - contain a blank line between the description and the annotation block.
The described annotations must appear in this order: @param
, @return
, @throws
Example:
/**
* Calculates the divison of the given divisor and divident.
*
* @param divisor The divisor for the calculation
* @param divident The divident for the calculation
* @return The calculated division.
* @throws Error if the divident is zero.
*/
const divide = (divisor: number, divident: number): number => {
if (divident === 0) {
throw new Error("Can not divide by zero")
}
return divisor / divident
}
Every exported type and interface must have an ESDoc. No special annotations are needed.
React components
- must be functional. Use the
React.FC
type. - can omit the
@return
annotation in the ESDoc if it returns always the same React DOM. - should use the
useCallback
hook where possible. Don't use inline functions. - should use custom hooks to extract long functions to reduce the number of lines in the component file.
- must be placed in files that have the same name as the component, but in kebab-case. This file can also contain the interface for the properties.
- should be named in PascalCase.
- Don't log directly to the console. Use our logging class
Logger
in "src/utils". - Create one instance of
Logger
per file. Don't pass or share the instances. - The first argument of the constructor is the scope. Use the name of the class or component whose behaviour you want to log or choose an explanatory name.
- If you want to add a sub scope (because e.g. you have two components that are similar or are used together, like the sub-classes of the iframe communicator), separate the main and sub scope with " > ".
- Scopes should be upper camel case.
- Log messages should never start with a lowercase letter.
- Log messages should never end with a colon or white space.
/**
* Properties for the {@link IncrementNumberButton}
*/
export interface IncrementNumberButtonProps {
prefix: string
}
const logger = new Logger("IncrementNumberButton")
/**
* Shows a button that contains a text and a number that gets incremented each time you click it.
*
* @param prefix A text that should be added before the number.
*/
export const IncrementNumberButton: React.FC<IncrementNumberButtonProps> = ({ prefix }) => {
const [counter, setCounter] = useState(0)
const incrementCounter = useCallback(() => {
setCounter((lastCounter) => lastCounter + 1)
logger.info("Increased counter")
}, [])
return <button onClick={incrementCounter}>{prefix}: {counter}</button>
}