JavaScript and TypeScript

Note

Don’t use CoffeeScript. Unless the repository is a fork, use Decaffeinate to convert CoffeeScript to ECMAScript.

License

Use the MIT license.

Version

ECMAScript

Frontend code is written for ECMAScript 6 (ES6) (see the status of feature support in modern browsers). We don’t support Internet Explorer 11.

Tip

To transform older code to ECMAScript 6, use lebab, but be aware of its bugs. There is a lebab plugin for Sublime Text. Use these preferences (Preferences > Package Settings > Lebab > Settings - User):

{
  "transforms": [
    "arrow",
    "arrow-return",
    "let",
    "for-of",
    "for-each",
    "arg-rest",
    "arg-spread",
    "obj-method",
    "obj-shorthand",
    "no-strict",
    "exponent",
    "class",
    "commonjs",
    "template",
    "default-param",
    "includes"
  ]
}

To transform newer code to ECMAScript 6, use Babel with the defaults query from browserlist.

Node

Applications are written for the latest LTS version of Node. Packages are written for non-end-of-life versions (see the status of Node versions).

To upgrade Node, change the node-version key in GitHub Actions workflows and the node (or nikolaik/python-nodejs) image in Dockerfiles. Check the relevant changelog for breaking changes.

Preferences

Plain JavaScript is preferred to using jQuery, unless functionality depends on jQuery plugins. To replace jQuery in a project, refer to You Might Not Need jQuery. Similarly, use the Fetch API instead of the Axios package, etc.

Package manager

pnpm, for its built-in supply-chain protections (dependency cooldown, trust policy, build scripts blocked by default) and its strict node_modules layout. Do not use npm or yarn.

User interface

Vue is preferred to React. That said, do not use frameworks for simple interfaces.

Asset management

webpack, unless a framework provides its own, like Vue.

Sass

sass (dart-sass). Do not use node-sass, which is deprecated.

Formatter

Biome. See HTML code style.

Requirements

Use pnpm, not npm or yarn. Set the Node version in the engines property and the pnpm version in the packageManager property of package.json.

List outdated dependencies:

pnpm outdated

Upgrade outdated dependencies:

pnpm update

Upgrade Vue dependencies:

vue upgrade --next

Supply chain

To protect against supply chain attacks, set in pnpm-workspace.yaml:

minimumReleaseAge: 10080
trustPolicy: no-downgrade

no-downgrade has false positives if packages drop provenance attestation. Exclude the versions that pnpm install reports:

trustPolicyExclude:
  - chokidar@4.0.3
  - semver@6.3.1

By default, pnpm enables minimumReleaseAge (1 day) and blockExoticSubdeps (prevents transitive dependencies from using git or tarballs).

Vulnerabilities

Dependabot alerts

If the Dependabot alert is for a build dependency (like node-sass) or a test dependency (like mocha), you can dismiss it with “Risk is tolerable for this project”. The npm ecosystem has false positives.

To check for vulnerable dependencies:

pnpm audit --prod

Note

pnpm audit (without --prod) has false positives (Vue example).

To upgrade vulnerable dependencies:

pnpm audit --fix update

This updates the lockfile to non-vulnerable versions, where the dependency ranges allow it. Check each package’s changelog before committing.

Where the dependency ranges don’t allow it, pnpm audit --fix can add overrides to package.json to force non-vulnerable versions, instead.

Code style

package.json

  • Do not set the scripts property. Instead, document the full commands in the readme, to reduce indirection and obfuscation.

Vue

  • When navigating between “pages”, respect native browser behaviors (open in new tab, etc.) by using an <a> link or <router-link>.

Continuous integration

Important

In GitHub workflows, install project dependencies safely with:

- run: pnpm install --frozen-lockfile --ignore-scripts

Create a .github/workflows/js.yml file.

Tip

In most cases, you can reuse the js workflow. For example:

name: Lint JavaScript
on: [push, pull_request]
jobs:
  lint:
    uses: open-contracting/.github/.github/workflows/js.yml@main
    permissions:
      contents: read

If you don’t use this workflow, and the project uses npm, include this step:

- run: npx lockfile-lint --path package-lock.json --type npm --allowed-hosts npm --validate-https

Reference