Thank you for reading the contributing guidelines for this repository. Since this repository is maintained primarily by a solo developer, this file also serves as a single source of truth style and usage guide for the repository. Amongst other things, this includes information about releasing new versions, code style, repository configuration, build scripts etc., version control standards, and directory structure. If you are contributing to this repository, and part or all of your changes are not covered by the notes in this file, then please try to either match your changes to the existing code style of this repository, or follow general best practices for the language.
If you would like to make changes or additions to this repository, please make a pull request. Your pull request should follow the pull request template found when submitting your changes, and for extra brownie points, your changes should adhere to the contributing guidelines in this file, particularly the sections on git, code style, and script format. See the following list for a summary of steps required for making a successful pull request (where "should" is used to describe a step, that step is not strictly required, but its completion will make merging the changes faster):
- Fork repository and pull to your local machine
- Make a new branch based on the notes in the Branch Naming section
- Checkout new branch and make all changes on that branch
- Changes should follow contributing notes in the code style and script format sections
- Commits should follow contributing notes in the git section
- Changes should pass all tests in the repository test suite
- Changes should update any required documentation or tests
- Changes on the new branch must not be merged into the main branch before making a pull request
- Make a pull request which must complete the pull request template provided for this repository
Please note that this repository is maintained primarily by a solo developer. As such, it may take longer than expected for pull requests to be reviewed. If your changes are important and/or time critical for your personal use case, please consider forking this repository and maintaining that fork instead.
If you encounter an issue whilst using this repository or any software released by it, please submit an issue using the issue tracker for this repository. Issues for this repository are tracked using the default github issue tracker (see here for more information on managing repository issues on github).
This repository uses git for version control. Please consult the following subheadings for information on the preferred git commit format used in this repository, and for other guidelines to consider when making a pull request with changes you have made to this repository. The following notes form the git "best practices" for this repository.
This repository uses the lightweight commit format for formatting git commits. The lightweight commit format is largely based on the conventional commit format with modifications to allow for terser commit titles. Where possible, contributors should familiarise themselves with the lightweight commit format and use it to format their commits, however the format is not a strict requirement for making contributions.
If you are not intending on contributing to this repository regularly, then feel free to use commits using another standard such as the conventional commit format mentioned above or these commit message guidelines, making sure that your commits meet the following minimum standards:
- Commits must be formatted consistently for all your changes
- Any prose title description must start with an imperative form verb, for example
Add feature
orFix bug
- Any prose title description should start with a capital letter, and should not end with a period
If you are contributing to this repository, and are intending on making a pull request, please first make a new branch. Any changes should be made on this new branch. All branches in this repository should be named according to the following rules:
- Words in each part of the branch name must be separated by a
-
(i.e. each part of the branch name must be inlower-kebab-case
) - Each part of the branch name must be separated by a
/
- Branch names must be formed of some or all of the following parts in order:
- Type of changes in the branch (see table below for options)
- Reference number for branch if applicable (for instance issue number if branch resolves an issue)
- Author name or reference (for example github username or name given when calling
git config --get user.name
) - Brief branch description
For example feature/123/jimbob3806/implement-slider
Branch Type | Description |
---|---|
feature | Branch which implements a new or changed feature. |
bug | Branch which fixes a reported or observed bug. |
performance | Branch which fixes a performance issue. |
security | Branch which fixes a security vulnerability. |
dependency | Branch which updates a dependency, and changes code as required depending on the new version of the dependency. |
refactor | Branch which refactors a part of the codebase (predominantly changes which do not change how code functions). |
rewrite | Branch which rewrites a part of the codebase (significant changes to how the code functions). |
remove | Branch which removes a section of the codebase, and changes code as required to facilitate the removal. |
docs | Branch which only updates documentation. |
test | Branch which only updates the test suite. |
other | Any other Branch. |
Developer environment files such as a workspace ./.vscode
directory must not be committed to the repository. Additionally the .gitignore
in the root of the repository should not be responsible for excluding developer environments. This is in the interest of preventing one-off entries in the .gitignore
file for uncommon developer environment files etc.
Preferably developers contributing to this repository should avoid the use of workspace configuration files for their developer environment, favouring instead a global user configuration file such as a .vimrc
file. If you are contributing to this repository and have to use workspace configuration files for your developer environment, please ensure that you follow the following steps:
- Make new branch for all your changes
- Before your first commit on new branch, update the
.gitignore
file to ensure that no configuration files appear in the git history, and commit these changes with a commit title ofcfg~ Modify .gitignore
- Make your changes as required on your new branch
- After your last commit, remove your workspace configuration files, revert the
.gitignore
file, and commit these changes with a commit title ofcfg~ Modify .gitignore
Primarily this repository uses a custom configuration of ESLint to enforce code style. Other elements of code style and formatting not enforced by ESLint are listed in the subheadings below. For any specific code style or formatting which is not enforced by ESLint and not listed below, please try to match the general code style of the repository, or follow best practices for the language.
This repository uses ESLint as the primary tool for the standardisation of JavaScript and TypeScript source code files. Note that ESLint is primarily responsible for source code formatting within this repository, although some linting rules are also intended for preventing code which may give rise to logical errors.
The custom ESLint configuration for the source code files in this repository can be found in the .eslintrc
and .eslintignore
files. Both files are well documented with links to existing ESLint documentation for more information on the given configuration options. Particularly for layout rules, no justification for configuration is provided, as each choice is usually driven by maintainer preference rather than any objective benefits associated with that rule. Additionally the repository package.json
file specifies scripts to run ESLint. To run ESLint manually across the source code files in this repository, please see the npm scripts reference, or run npm run lint:check
. Similarly to fix linting errors which can be automatically resolved by ESLint, please see the npm scripts reference, or run npm run lint:fix
.
Any ESLint rule may be overridden with an appropriate directive. Due to the inclusion of the eslint comments plugin, the use of such directives within source code files in this repository is restricted as follows:
- Any
eslint-disable
directive which is not scoped to a single line must have a matchingeslint-enable
directive to re-enable the rule - Duplicate
eslint-disable
directives are not permitted if those lines of code already have the given rule disabled eslint-disable
directive must specify one or more rule names which should be disabled (universal directives are not permitted)- All
eslint-disable
directives must be used (i.e. at least one error must be prevented by any given directive) - All
eslint-disable
directives must have a description explaining why the directive is required
Additionally, although not enforced by the eslint comments plugin,
Please see the following code block for examples of preferred methods of overriding ESLint rules:
// This method is preferred when the rule needs to be off for multiple lines.
/* eslint-disable id-match -- Invalid IDs. */
let bad_name_A
let bad_name_B
/* eslint-enable id-match -- Close disable-enable pair. */
// This method is preferred when a single line needs to be ignored.
/* eslint-disable-next-line id-match -- Invalid ID. */
let bad_name_A
// This method is allowed but not preferred.
let bad_name_A /* eslint-disable-line id-match -- Invalid ID. */
The preferred format for ternaries in this repository is the Haskell-like ternary format which mimics a Haskell guard statement in syntax, replacing the |
(pipe) guard character with the JavaScript ternary :
(colon), and replacing the =
(equals) assignment operator with the JavaScript ternary ?
(question mark). This is the same format suggested under the conditional chains section of the mdn page on ternaries.
In its multiline ternary rule configuration, ESLint does not currently support this exact multiline ternary format where the ternary :
operator begins each line. The ESLint configuration for this repository therefore does not strictly enforce the desired ternary format, and instead uses a mix of indentation rules etc. which allow the desired ternary format but cannot necessarily enforce it. See the following code block for examples of the preferred single and multiline ternary formats:
// Consider the following examples of preferred single and multiline ternaries
// where caseA to caseZ are all functions which return boolean results, and
// resultA to resultZ are all variable values of the same template type.
// Single line ternary format.
const resultA = caseA() ? resultA : resultB
// Standard Haskell-style multiline line ternary format.
const resultB = caseA() ? resultA
: caseB() ? resultB
: caseC() ? resultC
: resultD // Default case.
// Multiline ternary format with long cases and/or statements causing wrapping.
const resultC = caseA() ? resultA
: caseB() // Case length causes result statement to exceed max line width.
? resultB // Wrapped result should be indented.
: caseC() // Case length causes result statement to exceed max line width.
? resultC // Wrapped result should be indented.
: resultD // Default case.
Identifiers in source files in this repository should follow the lowerCamelCase
naming convention with the following exceptions:
- Class and type names should follow the
UpperCamelCase
naming convention - Global variables such as environment variables or top level configuration variables may use
UPPER_SNAKE_CASE
where appropriate
Please see the instructions below on how to construct compliant identifiers, read the google specification, or read this response summarising the algorithm:
- Convert identifier name to an ASCII prose form of the same name, for example
player ID
:- Remove any apostrophes etc.
- Turn accented letters into standard letter groups (for example the German
ẞ
might becomesz
)
- Divide the result from step 1 into individual words:
- Split on spaces or any remaining punctuation such as hyphens
- Split any conventional abbreviations into their own separate words (for instance
AdWords
would becomead words
)
- Turn everything into lowercase, including all acronyms (for example
ID
will becomeid
,XML
will becomexml
etc.) - Turn first letter of every word to uppercase for
UpperCamelCase
, or exclude the first word to yieldlowerCamelCase
- Concatenate everything from the previous step into one word to get your camel case identifier
Note that these rules are particularly useful for standardising identifiers containing acronyms (only the first letter of an acronym is capitalised regardless of length). The method produces consistent identifiers when compared to standards which make exceptions for short acronyms such as the microsoft abbreviations standard, which suggests capitalising acronyms consisting of only two characters.
Especially when concatenating multiple adjacent acronyms, the google specification produces clearer identifiers. For example with a prose identifier such as player id url
, we yield the identifier playerIdUrl
. Compared to an identifier such as playerIDUrl
using the microsoft abbreviations standard, which not only looks inconsistent, but also raises ambiguity as to where some acronyms begin and/or end.
Where possible, a leading ./
path prefix is preferred for accessing relative scripts etc. from source code files, this applies to any statement where a path to a file is supplied in a string. Please see the following code block for clarification:
// Preferred path format for relative files.
const file = fs.readFileSync("./relative/path/to/file")
// Valid format, but not preferred.
const file = fs.readFileSync("relative/path/to/file")
The type
field of the package.json
file in this repository declares the scripts in this repository as ESM modules by default, therefore any module imports and exports in a given script must use ES6 module syntax unless the script uses the .cjs
extension to declare a CommonJS script.
All import paths should be relative paths, and where possible code and directory structure should be built to avoid long relative path imports. Imports should also prioritise loading the index.js
file of a given folder when importing members from module scripts within that folder. This tends to simplify the import path, and allows for less import statements when importing members from multiple different module scripts in a given folder.
Similarly, to facilitate this behaviour, each folder should generally include an index.js
file which exports all of the public members of the module scripts contained within that folder. Not all members of a given module script must be made public, and not all exported members of a module script must be exported by the folder index.js
file; ultimately the index.js
file for a given folder (and the the package entrypoint index.js
file) is responsible for exposing only those members which should be made public to other parts of the code (or made available for import in another package). Note that for importing members from scripts in the same folder, members must be imported directly from the given script instead of the folder index.js
file in order to avoid circular imports.
If an import must come from a script which has a long or unreadable relative path, then it may be appropriate to use an absolute import. This may be achieved either by using a local path as a dependency, or by using the subpath imports feature of node. See the package imports section for more information on subpath imports and why they are generally avoided in this repository.
// Prefer relative path for importing members from folder index.js file.
import { member } from "./relative/path/to/index.js"
// Import members from the same folder directly to avoid circular imports.
import { member } from "./sibling-script.js"
// Avoid absolute path imports defined in package.json.
import { member } from "#internal/subpath/import"
All code comments should be written in clear, concise, and good quality written English. For the sake of consistency, code comments should also follow sentence case. That is to say that any comment, regardless of how small, should start with a capital letter on the first word, and should end with a .
(period). Whilst especially short code comments are ideal for not following sentence case, the cutoff between where a comment becomes detailed enough to need one or more full sentences is arbitrary. By following sentence case all the time, code comments are more likely to appear consistent, and less likely to turn into inappropriately long statements that would benefit from sentence breaks. Please see the code block below for an explicit example on the preferred comment format, or see here for more discussion on why code comments should form readable, complete sentences.
// Bad:
// filter array for even numbers
// Preferred:
// Filter array for even numbers.
To maintain consistency across the repository, every script follows the same overall format with respect to filenames and the order of certain parts of each script such as imports, exports, and file descriptions. The general format of each script consists of a brief license text, a JSDoc comment containing a file description tag, and author tags for that file, a @ts-check
directive, import statements, the body of the script, and finally export statements.
Each script in this repository should be named using lower-kebab-case
(all lowercase words separated by hyphens), or if the main exported member of the script is a class or type, then the script should be named using UpperCamelCase
(all uppercase words without separation). Please consider the following points when naming a new script:
- Script names must minimally indicate the main functionality of the script
- Script names should be one or two words, longer names may indicate the need for further refactoring into more scripts or folders
- Script directory paths must be considered as context when naming the script (i.e.
container/primary.js
rather thancontainer/primary-container.js
) - Standard scripts must be named using
lower-kebab-case
unless the script's main export is a class or a type - Scripts whose main export is a class or a type must be named using
UpperCamelCase
, with the name reflecting the main exported class or type - Folders may also use
lower-kebab-case
to have multiple words in the folder name
Each script in this repository starts with a header section containing a license text, information about the script, and directives. Please see the following list for the required elements and order for the header section of each script:
- Brief license text listing the type of license which the script is released under, the location of the license in the root of the repository, and links to bare templates of that license on the internet
- JSDoc comment containing at a minimum the following tags:
@file
tag with a description of the main purpose of the file, for example the main exports@author
tag(s) indicating the main authors of that file
@ts-check
directive comment to enforce strict type checking
Please see the template section below for an example empty script containing the required header text and all of the custom maker tags. A blank line separates each section of the script, for example the license text is the first commented lines at the top of the sample script until the first blank line.
Custom marker tags are single line comments beginning with @@
, and mark specific parts of each script. Please see the following table for a list of available marker tags. Every script must include one of each type of tag (imports
, body
and exports
). If a section of the script is empty, for instance if the script does not import anything, mark the section with the no-<tag>
variant of the marker tag. Note that imports-<type>
tags are preferred over using a single imports
tag in order to increase the organisation of import statements at the top of each file. Any tag marked as unique in the following table may only be used once per script.
Marker Tag | Unique | Description |
---|---|---|
no-<tag> |
By tag | Indicates that this part of the script is empty. Multiple no-<tag> tags allowed if using different <tag> value. |
imports |
Yes | Script imports. Do not use an imports marker tag in conjunction with imports-<type> tags. |
imports-<type> |
By type | Script imports of a given type (see table below). Multiple imports-<type> tags allowed if using different <type> value. |
body |
Yes | Script body containing the code which will be executed or exported. |
exports |
Yes | Script exports. |
Please see the following table for the available import types. If multiple import types are required, please use multiple import-<type>
marker tags as required. The order of the import tags should follow the order shown in the table below (i.e. node imports should come before dependency imports etc.). This order is chosen to reflect the increasing locality of imports going down the table (i.e. module scripts are in the same folder, package scripts are in a parent folder, external dependencies are in the repository ./node_modules
directory etc.). Type imports are an exception to this rule, and always appear at the bottom of the imports section of a script.
Import Type | Description |
---|---|
node |
Native dependencies which are a part of node. |
dependencies |
External dependencies pointing to ./node_modules (i.e. listed in package.json ). |
utils |
Generic repository utilities which could be split from the main package scripts or functionality. |
package |
Imports from parent folder within the repository. |
module |
Imports from the same folder. |
submodule |
Imports from a child folder within the repository. |
types |
Type imports. |
To speed up the creation of correctly formatted new scripts, this repository offers a script template which may be instantiated using the command npm run admin:plop
and then following the directions in the terminal for creating a script from the supplied template. Please see the code block below for an example a script created from this template. For more information on the admin:plop
script, please see the npm scripts section.
// Copyright (c) 2023 James Reid. All rights reserved.
//
// This source code file is licensed under the terms of the MIT license, a copy
// of which may be found in the LICENSE.md file in the root of this repository.
//
// For a template copy of the license see one of the following 3rd party sites:
// - <https://opensource.org/licenses/MIT>
// - <https://choosealicense.com/licenses/mit>
// - <https://spdx.org/licenses/MIT>
/**
* @file This is an empty template script with markers for where the different
* parts of the script should be written.
* @author James Reid
*/
// @ts-check
// @@no-imports
// @@no-body
// @@no-exports
This repository is generated from a template repository which may be viewed here. Please see the following subheadings for descriptions of the purpose and use of the root directories included in the template repository. Note that other directories and architectures may be added by the maintainer(s) after duplicating the template. These additions will obviously have their own purpose(s), some of which may not be described below.
The ./.cache
directory is included in this repository by use of a .gitkeep
file, otherwise the contents of the ./.cache
directory are untracked by the .gitignore
file. The ./.cache
directory is reserved for cache files of repository build scripts etc. These include the cache for ESLint, parcel bundler, and any other script which needs to cache local data in order to make subsequent executions of that script faster, or consume fewer system resources.
The .github
directory contains some or all files relating to github and the management of the repository community (think code of conduct, templates for issues and pull requests etc.). Primarily this repository uses the ./.github
directory for specific issue templates (see below) and for a specific pull request template. For more information specifically on adding a pull request template for a github repository, see here. For more information on the general usage of the ./.github
directory in a repository hosted on github, please see this article. Note that although it may be included in the ./.github
directory, in the case of this repository, the CONTRIBUTING.md
file is not stored in the ./.github
directory. Instead the CONTRIBUTING.md
file may be found in the root of the repository. This is simply because the CONTRIBUTING.md
file is a more universal file which may be expected to be found in the root of any repository regardless of where that repository may be hosted.
Issue templates are stored in the ./.github/ISSUE_TEMPLATE
subdirectory. Each yaml file in this directory creates an issue template which consumers of the repository may use to report a bug, security vulnerability etc. For more information on creating issue templates, see the github documentation here. For information relating specifically to the syntax for the github form schema, please see here.
The ./admin
directory contains all build scripts etc., template files, and configuration files which do not need to be located in the root of the repository. All scripts in the ./admin
directory do not form any part of the code exported by this repository. Please see the following subheadings to see how the directory is structured.
The ./assets
subdirectory is used to store all repository assets. This includes images, vector files, fonts, and any other media that features in repository documentation or is used in demos of repository functionality. Note that in the event that the default favicon.ico
file is not is deleted and no other assets are included in the repository, the ./assets
folder will still be included in git history of this repository by use of a .gitkeep
file.
The ./admin/config
subdirectory contains all configuration files for tools which do not require configuration files in the root of the repository. These tools include build tools such as rollup, generators such as jsdoc and plop etc. Other configuration files such as tsconfig.json
and .eslintrc
must be located in the root of the repository in order to be found by tsserver, npm etc., and are therefore not found in the ./admin/config
subdirectory.
The ./admin/scripts
subdirectory contains all custom scripts written by the maintainer for common repository tasks such as changelog generation upon release of a new issue. Most of these scripts can be called using npm run admin:<script-name>
. See the npm scripts section for more information on all of the default available scripts.
The ./admin/templates
subdirectory contains all templates used by plop for automatic generation of new scripts or documentation files, and by the changelog generator to render new release note prompts. Note that template files are all saved as .hbs
(handlebars) files for syntax highlighting purposes in vscode, although some of the template files may be limited to the simpler mustache syntax (this applies mainly to the templates used by the changelog generator).
The ./admin/web
subdirectory contains files which should be copied to the ./dist/web
directory when building a site using the appropriate build command. Primarily this directory contains public record files including a CNAME
record file (required for gh-pages
to correctly set custom domain when deploying site to github pages), a robots.txt
file, and a sitemap.xml
file, both of which help search engines to crawl the generated site. For references on how to write and update these files, please see the following links:
CNAME
records references:robots.txt
references:- Cloudflare
- robotstxt.org (also includes a
robots.txt
file checker) - Yoast blog guide
sitemap.xml
references:
The ./build
directory is included in this repository by use of a .gitkeep
file, otherwise the contents of the ./build
directory are untracked by the .gitignore
file. The ./build
directory is reserved for development build outputs. The relevant npm build scripts will output packaged scripts etc. to subdirectories in the ./build
directory. These subdirectories will correspond to the subdirectories in the ./src
directory (i.e. ./build/bin
, ./build/package
, and ./build/web
). Auto generated JSDoc documentation output will also be found in the ./build
directory in the ./build/docs
subdirectory. Please see the src section for more information on the purpose of each subdirectory.
The ./dist
directory is reserved for production build outputs. The relevant npm build scripts will output packaged scripts etc. to subdirectories in the ./dist
directory. These subdirectories will correspond to the subdirectories in the ./src
directory (i.e. ./build/bin
, ./build/package
, and ./build/web
). Please see the src section for more information on the purpose of each subdirectory. The ./dist
directory may also contain any other production related data files etc., and is included by default in this repository by use of a .gitkeep
file.
The ./dist
directory is reserved for custom, author generated, documentation files. Primarily these will be markdown files which will be included in the tutorials section of the auto-generated JSDoc documentation output. End users may also browse these markdown documentation files on github etc. The ./docs
directory may also contain any other documentation related data files etc., and is included by default in this repository by use of a .gitkeep
file.
The ./src
directory contains all source files which form part of the exported software of this repository. This includes scripts for any CLI tools provided by this repository, any scripts forming the exported package of this repository, and any scripts or markup files for the static demo site of this repository. Please see the following subheadings to see how the directory is structured.
The ./src/bin
subdirectory contains source files for any CLIs which are provided by this repository, and is included by default in this repository by use of a .gitkeep
file. The standard entrypoint for any CLI provided by this repository is ./src/bin/index.js
, although rollup may build multiple executable scripts from other entrypoints if required (for instance if multiple CLI tools are provided by the repository). For each executable script which rollup produces, the package.json
file bin object must be updated with an entry to point to the new script.
The ./src/package
subdirectory contains source files for the package which this repository exports, and is included by default in this repository by use of a .gitkeep
file. The standard entrypoint for any package provided by this repository is ./src/package/index.js
, although rollup may build multiple bundles from other entrypoints if required (for instance if the exported package is split into many subpath exports to allow only parts of the package to be imported later). Each entrypoint must export only those members which should be made available for import within another package, and must not blindly export all members etc. from all source files. For each bundled script which rollup produces, the package.json
file subpath exports object must be updated with an entry to point to the new bundled script.
The ./src/web
subdirectory contains markup and source files for the static demo site of this repository. Its default subdirectories are included in this repository by use of .gitkeep
files. The ./src/web
subdirectory is structured like a standard vanilla static site, with a ./src/web/pages
subdirectory for all .html
page files, a ./src/web/scripts
subdirectory for all .js
script files, and a ./src/web/styles
subdirectory for all .css
stylesheet files. The standard entrypoint for the static demo site of this repository is ./src/web/pages/index.html
. The parcel bundler will create its bundled output from this file.
When deployed, this page will be found at a URL such as <subdomain>.blameitonyourisp.com
(gh-pages
looks for index.html
file by default). Other pages can be added to the static site by adding them to the ./src/web/pages
subdirectory, and linking them from the index.html
page. For instance the file ./src/web/pages/about.html
would be found at a URL such as <subdomain>.blameitonyourisp.com/about.html
, or the file ./src/web/pages/docs/index.html
would be found at a URL such as <subdomain>.blameitonyourisp.com/docs
.
Most commonly this directory will be used to demonstrate functionality of the package exported by this repository, by importing and using the production build of the package from the ./dist
directory. Where appropriate, the static site may also include some author generated documentation pages.
The properties of the package.json
file of this repository are generally ordered as follows:
- Properties which must be updated when duplicating the template repository such as
name
, andversion
- Properties which may be updated directly by the maintainer during the life of the repository such as
engines
, andscripts
- Properties which are generally not updated directly by the maintainer such as
dependencies
, anddevDependencies
Within each group, properties are loosely logically grouped: name
, version
, description
, and keywords
are grouped together since they describe the package; homepage
, repository
, and bugs
are grouped together since they refer to the remote repository of the package. Please see the following subheadings for information on usage of specific properties of the package.json
file.
Generally, this repository discourages the use of the subpath imports object for creating absolute path imports in favour of using absolute paths, and structuring code in such a way as to avoid long import paths where possible. This is because:
- The location of an absolute path import is not always immediately clear compared to the location of a relative path import, especially when the relative path is in the same or an adjacent directory
- The absolute path of an imported member will not always be inferred by tsserver solely from the subpath imports object of the
package.json
file, and instead requires an additional entry in the paths object of thetsconfig.json
file
If a particularly long import path is unavoidable, then an entry in the subpath imports object of the package.json
file may be appropriate at the discretion of the maintainer. If a subpath import is required, and tsserver is not correctly finding the absolute path to the imported member, please see the following code blocks for how to explicitly point tsserver to the absolute path:
Configuration in package.json
file:
"imports": {
"#feature": "./src/feature/index.js"
}
Configuration in tsconfig.json
file:
"paths": {
"#feature": [ "./src/feature/index.js", "./src/feature/*.js" ]
}
Since node now natively supports subpath imports, this repository also strongly discourages implementing absolute path imports by using package.json
file dependencies.
Where appropriate, this repository encourages splitting the package exports by creating bundles from multiple rollup entrypoints, and using the package.json
file subpath exports object to specify a path for each bundle. This allows only specific parts of the package to be imported by any dependent packages if desired.
Splitting exports is particularly useful for isolating parts of the package which are expected to be commonly used, or providing separate exports for parts of a package which differ in functionality, instead of providing only one bundle. For instance a package containing disparate code snippets and utilities may have subpath exports such as ./math
, and ./terminal
etc.
Please note that even if all of the public members of a package are exported across multiple different subpath exports, it is still advisable to provide a main entrypoint which exports all public members of the package from a single bundled script. Additionally, for backwards compatibility with CommonJS imports, this repository also provides a subpath export reserved for the exported package bundled for CommonJS.
See the following code block for an example subpath exports object containing the default exports and an additional export for a specific, commonly used feature:
"exports": {
".": "./dist/package/index.js",
"./feature": "./dist/package/feature.js",
"./COMMON_JS": "./dist/package/index.cjs"
}
These subpath exports may then be imported as follows (this example assumes that the feature
member was exported using a named export rather than a default export):
// ESM imports.
import { feature } from "<package-name>/feature"
// CJS imports.
const { feature } = require("<package-name>/COMMON_JS")
For more information on importing ESM modules into CommonJS scripts, please see here
This repository largely follows the npm script conventions from ESLint. Scripts in this repository must follow the following rules:
- Script names must contain only lowercase English words (abbreviations are acceptable where they are obvious)
- A colon (
:
) must be used to separate nested categories of each script - A hyphen (
-
) must be used to separate words - Scripts must be ordered alphabetically, using categories where necessary to group scripts logically
For more information on npm scripts in general, checkout the docs, review this npm style guide, or read this article. Please see the following table for all available scripts in the package.json
file and a description of their functionality:
Script Name | Description |
---|---|
admin:deploy |
Deploy static website to gh-pages branch after the site has been built with the appropriate build command. |
admin:plop |
Create or modify a script or other file with plop. |
admin:tokei 1 |
Count approximate lines of code written by the author, and generate a json endpoint for a shields badge. |
admin:update-labels |
Update github issue labels for repository (requires access token in ./.env file). |
build |
Run build:dev script. |
build:dev |
Run all build:dev:<target> scripts. |
build:dev:bin |
Output development build of all CLI tools provided by repository to ./build directory. |
build:dev:package |
Output development build of repository package to ./build directory. |
build:dev:web |
Run start:web script. |
build:dist |
Run all build:prod:<target> scripts. |
build:dist:bin |
Output production build of all CLI tools provided by repository to ./dist directory. |
build:dist:package |
Output production build of repository package to ./dist directory. |
build:dist:web |
Output production build of repository static demo site to ./dist directory. |
docs |
Run docs:jsdoc script. |
docs:changelog |
Autogenerate new changelog entry with prompts from each relevant commit since last version. |
docs:jsdoc |
Build autogenerated docs using JSDoc. |
lint |
Run lint:check script. |
lint:check |
Check source files for linting errors. |
lint:fix |
Fix linting errors in source files which can be automatically resolved. |
lint:fix-dry |
Fix linting errors without saving to filesystem. |
postversion |
Push new version and tag to remote following npm version <semver> command. |
preversion |
Run build:prod prior to npm version <semver> command. |
start |
Run start:web script. |
start:docs |
Run docs:jsdoc , start server hosting the autogenerated docs, and watch ./src directory for changes. |
start:web |
Bundle demo static site to ./build directory using parcel, and start development server. |
test |
Run all test:<suite> scripts. |
test:bin |
Run available tests for all CLI tools provided by repository. |
test:package |
Run available tests for repository package. |
test:web |
Run available tests for repository static demo site. |
types |
Run types:check script. |
types:check |
Check types using tsc |
types:declaration |
Output declaration file for repository package to ./dist directory. |
watch |
Watch ./src/bin and ./src/package directories, and run development build on change. |
watch:bin |
Watch ./src/bin directory, and run development build on change. |
watch:package |
Watch ./src/package directory, and run development build on change. |
This repository favours documentation which is not platform specific. This means a good quality README.md
file, additional author generated markdown documentation files, and custom documentation page(s) on the demo website where such extra documentation would be useful (for instance when documenting an API). Additionally this repository offers auto-generated documentation from JSDoc code comments. As such, the github wiki for this repository should be switched off by default.
If you wish to add a wiki to this repository, you must first re-enable the wiki in the repository settings. With the wiki re-enabled, you can now add content to the wiki either by cloning the repository in a separate local directory, or by adding the wiki repository as a subtree of the main repository.
When using subtrees, the git command will generally be git subtree <COMMAND> --prefix <PATH> <REMOTE_URL> <REMOTE_BRANCH> --squash
where:
<COMMAND>
is a git operation such aspull
,push
etc.<PATH>
is the relative path to where the subtree is located from the root of the main repository<REMOTE_URL>
is the URL of the repository which is to be included as a subtree of the main repository<REMOTE_BRANCH>
is the branch which should be fetched
The --squash
flag is optional, but recommended when using the pull
commands as it will squash the history of the fetched branch into only one commit, preventing cluttering of the main repository git history. Please see the following code blocks for information on how to edit the wiki repository as a subtree of the main repository:
# Add wiki as new remote.
git remote add wiki $WIKI_URL
# Add subtree in ./.github/wiki directory.
git subtree add --prefix .github/wiki wiki master --squash
git subtree pull --prefix .github/wiki wiki master --squash
Note that for any repository hosted on github, the wiki for that repository is a second standalone repository. The general form of the URL of the wiki repository will be https://github.com/<REPO_OWNER>/<REPO_NAME>.wiki.git
(i.e. append .wiki.git
to the main repository name).
Once you have added the wiki repository as a subtree of the main repository, you can change the contents of the wiki by editing the contents of the ./.github/wiki
directory. Please see here for documentation on how to format markdown documentation files for github repository wikis. To commit changes to the wiki repository please see the following code block:
# Commit changes to local repo, then push subtree to remote.
git commit ./github/wiki
git subtree push --prefix .github/wiki wiki master
To disable a wiki repository after enabling it and adding content, please follow the list below in order:
- Remove wiki subtree and optionally remove wiki content (see code blocks below)
- Disable wiki in repository settings
To only remove subtree from main repository, but preserve the wiki content in case it is required in the future, please see the following code block:
# Delete subtree directory and commit changes.
rm -rf ./github/wiki
git commit ./github/wiki
# Remove wiki remote *without* pushing removed directory to wiki remote.
git remote remove wiki
To remove subtree and remove all content from the wiki repository, please see the following code block:
# Delete subtree directory and commit changes.
rm -rf ./github/wiki
git commit ./github/wiki
# Push changes to wiki remote.
git subtree push --prefix .github/wiki wiki master
# Remove wiki remote.
git remote remove wiki
To remove all wiki content if the wiki repository has not been added as a subtree of the main repository, please see the following code block:
# Clone and open wiki repository.
git clone https://github.com/$REPO_OWNER/$REPO_NAME.wiki.git
cd $REPO_NAME.wiki
# Remove wiki content and commit changes.
git rm *.md
git commit -m "Remove wiki content"
# Push changes.
git push
To remove all wiki content and delete git history, please see the following code block (this process is irreversible, proceed with caution):
# Clone and open wiki repository.
git clone https://github.com/$REPO_OWNER/$REPO_NAME.wiki.git
cd $REPO_NAME.wiki
# Delete git history using new orphaned branch.
git checkout --orphan empty
git rm --cached -r .
git commit --allow-empty -m "Remove wiki content"
# Force push changes which overrides existing git history.
git push origin empty:master --force
CAUTION Please note that this will completely delete all data for any repository. Proceed with caution, ensuring that you have cloned the correct repository, and are sure that you want to overwrite its history. This process is irreversible!
In order to ensure that versions of software released by this repository are released in a complete and consistent manner, please use the following checklist when creating a new version or release:
- Test source files using the appropriate test command (only accept failed tests if the test is known to be not required or incorrect):
- Run
npm run test
to run all available test suites - Run
npm run test:bin
to only run CLI source file tests - Run
npm run test:package
to only run package source file tests - Run
npm run test:web
to only run static web source file tests
- Run
- Build production bundles of source files:
- If required, update public record files found at
./admin/web
:- Update URLs in public record files to ensure the correct domain and/or subdomain is used
- Update
sitemap.xml
with new pages added etc., optionally use a sitemap generator to create a new sitemap - See the admin/web section for more information on updating public record files
- Build type declarations with typescript by running
npm run types:declaration
(note that this command is also run automatically when building the package) - Build production bundles of the binary, package, and or web source files by running the appropriate build command:
- Run
npm run build:prod
to build a production bundle with all available source files - Run
npm run build:prod:bin
to build only the CLI source files - Run
npm run build:prod:package
to build only the released package source files - Run
npm run build:prod:web
to build only the static web source files
- Run
- If required, deploy static site to
gh-pages
by runningnpm run admin:deploy
- If required, update public record files found at
- Update
package.json
file:- Do not update the version field as this will be updated by using the
npm version <semver>
command - Update bin object with any changed executable names or paths
- Update subpath exports object with any additional exports provided by the package
- Update engines object if the minimum required node or npm version has changed
- Update scripts object with any new or changed scripts
- Do not update the version field as this will be updated by using the
- Update
README.md
file:- Update basic and specific usage instructions as required
- Update roadmap with new features, and remove features which have now been implemented
- Update attributions with any new 3rd party assets
- Update
CONTRIBUTING.md
file:- Update the getting around section with any significant new or changed directory structures
- Update the npm scripts section with any new or changed scripts
- Update
CHANGELOG.md
file:- Run
npm run docs:changelog
to generate a new section in theCHANGELOG.md
file with prompts for each relevant commit since the last version - Edit each prompt as required so that the new section in the
CHANGELOG.md
file reflects the changes since the last version in a concise, continuous and human readable fashion
- Run
- Update software versions for dropdowns in issue templates:
- Add the new version number of the package to the package version dropdown
- Add any new LTS version of node released since the last version to the node LTS version dropdown
- Commit all changes in the working tree, or stash uncommitted changes such that the working tree is clean prior to updating the version using the
npm version <semver>
command - Update version by running
npm version <semver>
where<semver>
is eithermajor
,minor
, orpatch
depending on the changes made 2
When publishing a new site, it may be useful to help search engine crawlers by explicitly submitting your sitemap.xml
file for faster indexing. Note that this is step is not required for a site to be indexed, as search engines will usually find any given site organically unless the site's robots.txt
file prevents this. To find out which page(s) are currently indexed by google for a given site, simply search for site:<url>
. This query will return all pages, posts etc. associated with that url which are currently indexed on google.
For information on how to submit your sitemap.xml
file for indexing on google or different search engines, please see the following resources:
- Google sitemap documentation
- Yoast blog guide (includes different search engines)
- Themeisle blog guide
Note that each subdomain of a given domain should have its own sitemap.xml
file and associated public records. As discussed in these forum threads (webmaster stack exchange and google webmasters), search engines generally consider subdomains as being standalone sites, and all URLs listed in a sitemap.xml
file must reside on the same host as the given sitemap.xml
file (i.e. a sitemap at https://www.example.com/sitemap.xml
cannot include URLs from https://subdomain.example.com
). Additionally, modern search engines are usually capable of inferring from content which subdomains should be indexed as independent sites, and which subdomains should be associated with the main domain.
Footnotes
-
Please note that this script relies on
tokei
, a CLI application written in Rust. Iftokei
is not installed on the system, then this script will not run. At the time of writing, the tokei endpoint for dynamic badges/shields is intermittent and unreliable, showing a 502 bad gateway error. This error also prevents the shields lines of code badge from rendering correctly, causing all repos to show as having 0 lines of code. The format for the tokei badge URL may be found here. To circumvent this, lines of code are being counted "manually" using the tokei rust CLI and a json endpoint to generate the badge. As such the lines of code badge may be out of sync with the latest commit, although given that this is a "just for fun" metric, it is not of importance. ↩ -
Note that the npm
preversion
script is configured to run thebuild:prod
script in case the build step is forgotten, the.npmrc
file is configured to add a git tag to the generated new version commit, and the npmpostversion
script is configured to push the new commit and tag to the remote repository. For more information on thenpm version
command, please see this cheatsheet. ↩