Files
docx-js/contribution-guidelines.md
2025-04-16 23:43:38 +00:00

5.7 KiB

Contribution Guidelines

  • Include documentation reference(s) at the top of each file as a comment. For example:

    // http://officeopenxml.com/WPdocument.php
    

    It can be a link to officeopenxml.com or datypic.com etc. It could also be a reference to the official ECMA-376 standard: https://www.ecma-international.org/publications-and-standards/standards/ecma-376/

  • Include a portion of the schema as a comment for cross reference. For example:

    // <xsd:element name="tbl" type="CT_Tbl" minOccurs="0" maxOccurs="1"/>
    
  • Follow Prettier standards, and consider using the Prettier VSCode plugin.

  • Follow the ESLint rules

Always think about the user

Put yourself in their position, and imagine how they would feel about the feature you wrote.

  1. Is it easy to use?
  2. Has it been documented well?
  3. Is it intuitive?
  4. Is it declarative?
  5. Is it fun to use?

Good Commit Names

Please write good commit messages when making a commit: https://chris.beams.io/posts/git-commit/

Do not:

c // What?
rtl // Adding acronyms without explaining anything else is not helpful
works! // Glad its working, but the message is not helpful
demo updated // Getting better, but capitalize the first letter
Unesesary coment removed // Make sure to use correct spelling

Do

ITableFloatOptions is an interface for a JSON of primitives. The end user would need to pass in a json object and not need to worry about the internals:

    public float(tableFloatOptions: ITableFloatOptions): Table

Declarative API

Make sure the API is declarative, so no method calling or mutation. This is a design decision, consistent with the rest of the project. There are benefits to declarative code over other styles of code, explained here: https://dzone.com/articles/why-declarative-coding-makes-you-a-better-programm

Do not:

const paragraph = doc.createParagraph();
const text = paragraph.createText();
text.contents = "Hello World";

Do

const doc = new Document({
    sections: [{
        children: [
            new Paragraph({
                children: [new TextRun("Hello World")],
            }),
        ],
    }];
});

Getters and Setters

Getters and Setters are done with a capital letter like so:

public get Level() {
   ...
}

This is the convention of this project. There is no performance advantage by doing this. It means we don't need to prefix all private variables with _:

Do not:

private get _level: string;

Do

private get level: string;

Types over interfaces

Using type aliases in TypeScript offers several advantages over interfaces:

  • Flexibility with Complex Types: type supports defining unions, intersections, and other complex type constructs that interfaces cannot handle. For example:

    type StringOrNumber = string | number;
    type Combined = TypeA & TypeB;
    
  • Support for Primitive Types: type can alias primitive types (e.g., type ID = string), while interfaces are limited to object shapes.

  • Tuple and Array Types: type allows defining tuples and specific array types easily (e.g., type Point = [number, number]), which interfaces cannot represent.

  • Utility Types Compatibility: type works seamlessly with TypeScript's utility types (e.g., Partial<T>, Pick<T, K>), enabling more expressive type transformations.

  • Functional Programming: type is ideal for functional programming patterns, such as defining function signatures or mapped types, due to its versatility.

  • No Declaration Merging: Unlike interfaces, type does not support declaration merging, which can prevent accidental type extensions and ensure predictable type definitions.

  • Consistent Pattern: This project uses type for all type definitions, so using type for all type definitions maintains consistency and readability across the codebase.

Detailed discussion: https://stackoverflow.com/questions/37233735/typescript-interfaces-vs-types

Do not:

interface IRelationshipFileInfo {
    id: number;
    target: string;
}

Do:

type RelationshipFileInfo = { id: number; target: string };

String enums vs type

To take full advantage of TypeScript's typing system, its best to use string enums:

Do not:

type WeaponType = "bow" | "sword" | "wand";

Do:

enum WeaponType = {
    BOW = "bow",
    SWORD = "sword",
    WAND = "wand",
}

Spell correctly, in full and in American English

Do not:

readdy; // misspelling
perm; // abbreviation
conf; // abbreviation
cnty; // abbreviation
relationFile; // abbreviation
colour; // U.K. English

Do:

ready;
permission;
config;
country;
relationshipFile;
color;

Keep files small (within reason)

To minimize merge conflicts, reduce complexity, and improve readability, keep the files small.

Name files and folders with /foo-bar/kebab-case.ts

To be consistent and in-line with the project, name files like-this.ts.

https://stackoverflow.com/questions/7273316/what-is-the-javascript-filename-naming-convention

Testing

Please write a test of every file you make and suffix it with .spec.ts.

Here is a template of a test:

import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";

describe("ClassName", () => {
    afterEach(() => {
        // TODO
    });

    beforeEach(() => {
        // TODO
    });

    describe("#methodName()", () => {
        it("should ", () => {
            // TODO
        });
    });
});

Try not to use the tests/utility.ts file as this is being deprecated.