Skip to content

Latest commit

 

History

History
1378 lines (982 loc) · 25.8 KB

javascript.md

File metadata and controls

1378 lines (982 loc) · 25.8 KB

JavaScript 🟨

Base

Objective

  • B1. There are no unhandled errors while code executing.

    While data loading and working with the app, there are no errors, the app does not break.

  • B2. The code is cross-browser (the last versions of Chrome, Firefox and Safari), works correctly in LTS version of the server environment (NodeJS, Deno) and does not cause errors on different operating systems (Windows, Linux and MacOS).

Markup

  • B3. There are no markup errors.

    Validators:

    Warnings are allowed.

  • B4. The minimum possible number of HTML elements has been used (no extra elements).

    There should be no extra wrappers and blocks that are used for decoration and can be replaced with pseudo-elements.

  • B5. Content images in <img> have the alt attribute filled.

  • B6. The size is specified in the <svg> tag for inline SVG images.

  • B7. Vector graphics are used.

    If there are svg images in the design, the use of PNG and other formats is prohibited.

Styles

  • B8. The layout of the blocks on the page is made by flex and/or grid.

  • B9. !important is forbidden.

    The important keyword is only allowed for overrides in libraries. It is forbidden to use for writing your own styles.

  • B10. No global tag styles. Only class selectors or a cascade are used to set styles.

    Global styles are only available in global style files (scaffolding.css, app.css, reset.css (🥲), etc).

    Bad:

    /* components/items-list/styles.module.css */
    
    .wrapper div {
      display: flex;
    }
    
    p {
      text-align: center;
    }

    Good:

    /* components/items-list/styles.module.css */
    
    .wrapper .column {
      display: flex;
    }
    
    .text {
      text-align: center;
    }

    Exceptions:

    id and other selectors other than class selector only allowed to be used to override libraries styles

    Bad

    /* components/items-list/styles.module.css – YOUR component */
    
    #list-item {
      display: grid;
    }

    Good

    /* components/accordion/styles.module.css – YOUR overrides to library components */
    
    #accordion {
      display: grid;
    }
    
    /* styles/scaffolding.css */
    
    /* root – default convention for the root element in React framework */
    #root {
      min-height: 100vh;
    }

  • B11. Nesting of selectors is no more than two levels.

    Bad

    /* components/items-list/styles.module.css */
    
    .wrapper .column .text {
      text-align: center;
    }
    
    .wrapper {
      display: grid;
    
      .column {
        background-color: red;
    
        .text {
          text-align: center;
        }
      }
    }

    Good:

    /* components/items-list/styles.module.css */
    
    .column .text {
      text-align: center;
    }
    
    .text {
      text-align: center;
    }
    
    .wrapper {
      display: grid;
    
      .column {
        background-color: red;
      }
    
      .text {
        text-align: center;
      }
    }

  • B12. While interacting with elements (hovering, clicking, etc.), neither the element itself nor the elements around / inside its block change their position.

Fonts

  • B13. Non-standard fonts are connected locally.

  • B14. Alternate font options specified.

    The font order:

    • Main font.
    • Safe font.
    • Font type.

    Example:

    /* styles/scaffolding.css */
    
    body {
      font-family: Roboto, Arial, sans-serif;
    }

Naming

  • B15. Names of variables, parameters, properties and methods begin with a lowercase letter and are written in camelCase notation.

  • B16. English nouns are used as variable and property names.

    Abbreviations in words are prohibited. Abbreviated variable names can be used only if the name is common (err, xhr, evt, src, i and etc).

  • B17. Variable names do not use the data type.

    Bad:

    const filtersArray = ['all', 'past', 'feature'];
    
    const catObject = {
      name: 'Pit',
      age: 7,
    };

    Good:

    const filters = ['all', 'past', 'feature'];
    
    const cat = {
      name: 'Pit',
      age: 7,
    };

  • B18. Arrays are named plural nouns.

    Bad:

    const age = [10, 15, 22];
    const name = ['John', 'Pit', 'Brew'];
    
    const cat = {
      name: 'Pit',
      friend: ['Nike', 'Sof', 'Kat'],
    };

    Good:

    const ages = [10, 15, 22];
    const names = ['John', 'Pit', 'Brew'];
    
    const cat = {
      name: 'Pit',
      friends: ['Nike', 'Sof', 'Kat'],
    };

  • B19. Boolean variables start with a prefix that can be answered with "yes".

    Bad:

    const login = true;
    
    const isNotRemoved = Boolean(!payload);
    if (isNotRemoved) {
    }
    
    const cat = {
      name: 'Pit',
      friend: false,
    };

    Good:

    const isLogin = true;
    
    const isRemoved = Boolean(payload);
    if (!isRemoved) {
    }
    
    const cat = {
      name: 'Pit',
      hasFriends: false,
    };

  • B20. Function or method begins with a verb.

    Exceptions:

    Handler functions or callbacks.

    Bad:

    const action = (names) => {
      console.log(names);
    };
    
    const cat = {
      name: 'Pit',
      action() {
        console.log('Meow');
      },
    };
    
    const randomNumber = () => Math.random();

    Good:

    const printNames = (names) => {
      console.log(names);
    };
    
    const cat = {
      name: 'Pit',
      say() {
        console.log('Meow');
      },
    };
    
    const getRandomNumber = () => Math.random();

  • B21. Classes are named with English nouns. The class name starts with a capital letter.

    Bad:

    class wizard {}

    Good:

    // class
    class Wizard {}

    In cases when there is one or more acronyms in the class name, it should have the first acronym in uppercase whereas all others be handled as a regular word (only the first letter is capitalized)

    Bad:

    class XmlHttpRequest {}

    Good:

    class XMLHttpRequest {}
  • B22. Constant names are written in capital letters.

    Words are separated by underscores (UPPER_SNAKE_CASE), for example:

    const MAX_HEIGHT = 6996;
    const IDX_NOT_FOUND = -1;

  • B23. Enums are named by English nouns and begin with an uppercase (capital) letter. Keys are declared in constant format (with uppercase letters).

    Bad:

    const statusCodes = {
      ok: 200,
      notFound: 404,
      badRequest: 400,
    };

    Good:

    const StatusCode = {
      OK: 200,
      NOT_FOUND: 404,
      BAD_REQUEST: 400,
    };

    In cases when there is one or more acronyms in the enum name, it should have the first acronym in uppercase whereas all others be handled as a regular word (only the first letter is capitalized)

    Bad:

    const HttpStatusCode = {};

    Good:

    const HTTPStatusCode = {};
  • B24. kebab-case is used to name files/folders (names are written in lowercase letters, words are separated by hyphens).

    In order to avoid name conflicts in different operating systems, it is better to use the least conflicting way of naming files — lowercase letters separated by a hyphen.

    Bad

    // src/components/common/Button/Button.tsx
    // src/services/UserService/UserService.ts

    Good

    // src/components/common/button/button.tsx
    // src/services/user-service/user-service.ts

    Exceptions:

    Framework/library files that cannot work with another case.

  • B25. There is no transliteration in any form (in file names, classes, variables, etc.).

Formatting

  • B26. The code matches the style of the project.

    There are no errors while checking the project with linters (ESLint, StyleLint, Prettier, etc). All types of linters are at the discretion of the team.

    Rules are not disabled anywhere in the source code.

  • B27. Curly braces are required everywhere.

    In any constructions that imply the use of a code block (curly braces), such as for, while, if, switch, function, the code block is necessarily used, even if the statement consists of one line.

    Bad

    if (x % 2 === 1) isEven = false;
    
    switch (actionType) {
      case ActionType.START_LOADING:
        return {
          ...state,
          isLoading: true,
        };
      case ActionType.END_LOADING:
        return {
          ...state,
          isLoading: false,
        };
    }

    Good

    if (x % 2 === 1) {
      isEven = false;
    }
    
    switch (actionType) {
      case ActionType.START_LOADING: {
        return {
          ...state,
          isLoading: true,
        };
      }
      case ActionType.END_LOADING: {
        return {
          ...state,
          isLoading: false,
        };
      }
    }

    The exceptions are single-line arrow functions, which can be used without the required blocks of code:

    const checkedCheckboxes = checkboxes.filter((checkbox) => checkbox.checked);

  • B28. All source files follow the recommended structure.

    // 1. Imports
    import { getUniqueItems } from 'helpers';
    
    // 2. Types / Variables whose value is known before the program starts
    const COLORS = ['red', 'green', 'blue'];
    
    // 3. Variables whose value is known before main code execution
    const colorPicker = document.querySelector('.color-picker');
    
    // 4. Functions
    const getUniqueColors = (userColors, defaultColors) => {
      return getUniqueItems(userColors, defaultColors);
    };
    
    // 5. Program code
    const rightColors = getColorsIntersection(colorPicker.value, DEFAULT_COLORS);
    
    // 6. Exports
    export { rightColors };

    Some blocks may be missing, but the rest should still adhere to the order.

  • B29. Sets of constants of the same type are collected into Enums.

    Bad:

    const LOAD_USERS_START = 'LOAD_USERS_START';
    const LOAD_USERS_END = 'LOAD_USERS_END';
    const LOAD_USERS_ERROR = 'LOAD_USERS_ERROR';

    Good:

    const UserAction = {
      LOAD_START: 'LOAD_START',
      LOAD_END: 'LOAD_END',
      LOAD_ERROR: 'LOAD_ERROR',
    };

    Note: constants that are used in the same context, but has different purposes should be split into different enums or separate constants

    Bad:

    const CompensationComputation = {
      HOLIDAY_COMPENSATION: 1.7,
      OVERTIME_COMPENSATION: 1.5,
      OVERTIME_THRESHOLD: 1.1, //related not to compensation rate, but to overtime hours calculation
    };

    Good:

    const CompensationCoefficient = {
      HOLIDAY_COMPENSATION: 1.7,
      OVERTIME_COMPENSATION: 1.5,
    };
    
    const OVERTIME_THRESHOLD = 1.1;

  • B30. All class properties and methods are marked with member access (private , public or protected).

    Bad:

    class Animal {
      constructor({ name }) {
        this.privateName = name;
      }
    
      getPrivateName() {
        return this.privateName;
      }
    }

    Good:

    class Animal {
      #privateName;
    
      public constructor({ name }) {
        this.#privateName = name;
      }
    
      public getPrivateName() {
        return this.#privateName;
      }
    }
    
    // or
    
    class Animal {
      private privateName;
    
      public constructor({ name }) {
        this.privateName = name;
      }
    
      public getPrivateName() {
        return this.privateName;
      }
    }

  • B31. The code does not use “magic values”, each of them has a separate variable named as a constant.

Rubbish

  • B32. The versions of dependencies used are fixed in package.json.

    The dependency lists in the package.json file indicate the exact versions of the packages used. The version must be specified. ^, * and ~ are not allowed.

  • B33. There are no unused dependencies in the project.

    Node: Some dependencies are needed by other dependencies. Ex. pg package is required for most of ORM packages.

  • B34. There are no files, modules and parts of code that are not used in the project code, including commented code pats.

    There are no script files that are “dead code” that is never executed.

Correctness

  • B35. Constants and enums are not redefined anywhere in the code.

  • B36. Potentially incorrect operations are missing. API is used correctly.

    For example, sum of two values with different data types.

    Bad:

    new Date() + 1000;

    Good:

    Number(new Date()) + 1000;

    Potentially incorrect operation of taking the integer part of a number.

    Bad:

    const minutesNumber = ~~(seconds / 60);

    Good:

    const minutesNumber = Math.trunc(seconds / 60);

    Valid values are passed as expected by the specification.

    Bad:

    const isPressed = element.getAttribute('aria-pressed', false);

    Good:

    const isPressed = element.getAttribute('aria-pressed');

    map method returns a value, so either this value should be used, or the method should be replaced with forEach.

    Bad:

    let greeting = 'Hey';
    
    wizards.map((wizard) => {
      greeting += `, ${wizard.name}`;
    });
    
    console.log(`${greeting}!`);

    Good:

    const greeting = 'Hey';
    
    const names = wizards.map((wizard) => {
      return wizard.name;
    });
    
    console.log(`${greeting} ${names.join(', ')}!`);

Modules

  • B37. Modules do not export mutable variables.

    A module should not export a variable whose value may change in the future.

    Bad:

    let latestResult;
    
    export { latestResult };

    Good:

    const latestResult = loadLatestResult();
    
    export { latestResult };

  • B38. The name of the module corresponds to its content.

    Different logical parts of the code are placed in separate module files. The name of the module must match its content. For example, if the module contains the GameView class, then the name of the module should be game-view.js.

  • B39. No index files. index file can be only used as the entry point of the application.

Security

  • B40. Event handlers are added and removed in a timely manner.

    Event handlers are added only when the element appears on the page and are removed when it disappear.

Database

  • B41. 🗄 It is forbidden to use the GET method to write data.

  • B42. 🗄 There are no SQL Injections in the code.

    While working with a database, all SQL queries must be protected from SQL injection.

  • B43. 🗄 Code protected from XSS.

    It is not allowed to display unfiltered information received from the user, because XSS attack is possible.

  • B44. 🗄 Passwords are always hashed.

  • B45. 🗄 Migrations roll in two ways (up and down) without any errors. Data consistency is preserved after every migration.

Advance

Objective

  • A1. Modern syntax is preferred.

    Prefer modern JS syntax if possible, based on your target of your bundler, usage of transpilers (ex. Babel) and the browsers/platforms you support. See some examples below

    Array methods:

    Bad:

    items.slice().reverse();

    Good:

    items.toReversed();

    Optional Chaining:

    Bad:

    const userName = user && user.name;

    Good:

    const userName = user?.name;

    Nullish coalescing:

    Bad:

    const userCreatePayload = payload || INITIAL_USER_CREATE_PAYLOAD_VALUES;

    Good:

    const userCreatePayload = payload ?? INITIAL_USER_CREATE_PAYLOAD_VALUES;

    Top-level await:

    Bad:

    (async () => {
      await app.run();
    })();

    Good:

    await app.run();

Markup

  • A2. All interactive elements have a description.

    Bad:

    <input placeholder="First Name" />
    
    <button onClick={handleEditUserClick}></button>
    
    <a href={AppRoute.DASHBOARD}></a>
    
    <button onClick={handleEditUserClick}><img src="img/user.svg" /></button>
    
    <a href={AppRoute.DASHBOARD}><img src="img/arrow.svg" /></a>

    Good:

    <label class="visually-hidden" for="first-name">First name</label>
    <input id="first-name" placeholder="First Name" />
    
    <button onClick={handleEditUserClick}>
      <span className="visually-hidden">Edit user</span>
    </button>
    
    <a href={AppRoute.DASHBOARD}>
      <span className="visually-hidden">Go to dashboard</span>
    </a>
    
    <label>
      <span class="visually-hidden">First name</span>
      <input placeholder="First Name" />
    </label>
    
    <button onClick={handleEditUserClick}>
      <img src="img/user.svg" alt="" />
      <span className="visually-hidden">Edit user</span>
    </button>
    
    <a href={AppRoute.DASHBOARD}>
      <img src="img/arrow.svg" alt="" />
      <span className="visually-hidden">Go to dashboard</span>
    </a>
    
    <input aria-label="First name" placeholder="First Name" />

Naming

  • A3. Uniform writing and formatting of code in all file types (HTML, CSS, JS, JSX, ect.).

    Example:

    If you use css-nesting it should be used through all the project (all style files must be written using nesting). If you use interfaces with I (ex. IUser) approach it should be used through all the project (all interfaces must be written with I prefix). If you use TS Enums approach it should be used through all the project (you can not use both TS Enum and JS Enum (JS plain object with as const)).

  • A4. Abstract classes or interfaces should have generic names and do not contain implementation details.

  • A5. The names of methods/functions and properties/variables of objects do not contain the names of object/module.

    Bad:

    const popup = {
      openPopup() {
        console.log('I will open popup');
      },
    };
    
    const wizard = {
      wizardName: 'Gandalf',
    };

    Good:

    const popup = {
      open() {
        console.log('I will open popup');
      },
    };
    
    const wizard = {
      name: 'Gandalf',
    };

    Bad:

    // src/validation-schemas/users/login.validation-schema.ts
    
    const userValidationSchema = {};

    Good:

    // src/validation-schemas/users/login.validation-schema.ts
    
    const user = {};
    
    // src/components/sign-in/sign-in.tsx
    import { user as userValidationSchema } from 'validation-schemas';

Uniformity

  • A6. 🔵 Interfaces are only used to implement a class.

  • A7. A uniform naming style for variables is used.

    Variable naming style is used the same in all modules, for example:

    If the variables that store the DOM element contain the word Element or anything else, it must be the same everywhere

    Bad:

    const popupMainElement = document.querySelector('.popup');
    const sidebarNode = document.querySelector('.sidebar');
    const similarContainer = popupMainElement.querySelector('ul.similar');

    Good:

    const popupMainElement = document.querySelector('.popup');
    const sidebarElement = document.querySelector('.sidebar');
    const similarContainerElement = popupMainElement.querySelector('ul.similar');

    Also good

    const popupMainNode = document.querySelector('.popup');
    const sidebarNode = document.querySelector('.sidebar');
    const similarContainerNode = popupMainNode.querySelector('ul.similar');

  • A8. One approach is used while using API that supports multiple approaches.

    If there are several different APIs that allow you to solve the same problem, for example, finding an element by id in the DOM tree, then only one of these APIs is used in the project.

    Bad:

    const popupMainElement = document.querySelector('#popup');
    const sidebarElement = document.getElementById('sidebar');
    
    const popupClassName = popupMainElement.getAttribute('class');
    const sidebarClassName = sidebarElement.className;

    Good:

    const popupMainElement = document.querySelector('#popup');
    const sidebarElement = document.querySelector('#sidebar');
    
    const popupClassName = popupMainElement.getAttribute('class');
    const sidebarClassName = sidebarElement.getAttribute('class');
    
    // or
    
    const popupMainElement = document.getElementById('popup');
    const sidebarElement = document.getElementById('sidebar');
    
    const popupClassName = popupMainElement.className;
    const sidebarClassName = sidebarElement.className;

  • A9. ⚛️ Callbacks passed to props are named with on prefix.

    <ListItem onClick={handleBtnClick} />

  • A10. ⚛️ Component functions are named with handle prefix.

    const Dashboard = () => {
      const handleBtnClick = () => {};
    
      return <ListItem onClick={handleBtnClick} />;
    };

Modules

  • A11. If the same code is repeated in several modules, the repeated part is moved to a separate module.

Redundancy

  • A12. Where possible, the ternary operator is used in the assignment of a value instead of if.

    Exceptions:

    Ternary operator should not be nested.

    Bad:

    let sex;
    
    if (isMale) {
      sex = 'Man';
    } else {
      sex = 'Woman';
    }

    Good:

    const sex = isMale ? 'Man' : 'Woman';

  • A13. Conditions are simplified.

    If the function returns a boolean value, do not use if..else with unnecessary return.

    Bad:

    const checkIsEquals = (firstValue, secondValue) => {
      if (firstValue === secondValue) {
        return true;
      } else {
        return false;
      }
    };

    Good:

    const checkIsEquals = (firstValue, secondValue) => {
      return firstValue === secondValue;
    };

Optimality

  • A14. 🔵 unknown is preferred over any. any is prohibited.

  • A15. Use the for/of to iterate over arrays and data structures that can be iterated over(Iterable).

    Where an array element index is not required, or where all elements of an iterable data structure need to be traversed, a for .. of loop is used instead of a for loop.

    Bad:

    for (let i = 0; i < levels.length; i++) {
      const level = levels[i];
      renderLevel(level);
    }

    Good:

    for (const level of levels) {
      renderLevel(level);
    }

Complexity and Readability

  • A16. Long functions and methods are split into several smaller ones.

  • A17. Iterators for arrays are used to work with JS collections.

    Iterators are used to with arrays — forEach, map, filter, and etc.

    elements.forEach((element) => {
      element.addEventListener('click', () => {
        console.log(element);
      });
    });