
<p align="center">
    <img width="605" src="https://img.shields.io/badge/blameitonyourisp-13-inactive?style=for-the-badge&labelColor=BAC99C&color=779966&logo=data:image/svg+xml;base64, ">
</p>

# Contributing

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.

### Table of Contents

- [Pull Requests](#pull-requests)
- [Issues](#issues)
- [Git](#git)
	- [Lightweight Commit Format](#lightweight-commit-format)
	- [Branch Naming](#branch-naming)
	- [Developer Environments](#developer-environments)
- [Code Style](#code-style)
	- [ESLint](#eslint)
	- [Ternaries](#ternaries)
	- [Identifier Naming](#identifier-naming)
	- [Paths](#paths)
	- [Module Imports](#imports-and-exports)
	- [Comment Grammar](#comment-grammar)
- [Script Format](#script-format)
	- [Naming](#naming)
	- [Header](#header)
	- [Custom Marker Tags](#custom-marker-tags)
	- [Template](#template)
- [Getting Around](#getting-around)
	- [.cache](#cache)
	- [.github](#github)
	- [admin](#admin)
	- [build](#build)
	- [dist](#dist)
	- [docs](#docs)
	- [src](#src)
- [Package](#package)
	- [Imports](#imports)
	- [Exports](#exports)
	- [NPM Scripts](#npm-scripts)
- [Wiki](#wiki)
    - [Enable](#enable)
    - [Update](#update)
    - [Disable](#disable)
- [Releasing](#releasing)
    - [Indexing](#indexing)

## Pull Requests

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](#git), [code style](#code-style), and [script format](#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):

1. Fork repository and pull to your local machine
2. Make a new branch based on the notes in the [Branch Naming](#branch-naming) section
3. Checkout new branch and make *all* changes on that branch
    1. Changes *should* follow contributing notes in the [code style](#code-style) and [script format](#script-format) sections
    2. Commits *should* follow contributing notes in the [git](#git) section
    3. Changes *should* pass all tests in the repository test suite
    4. Changes *should* update any required documentation or tests
    5. Changes on the new branch must *not* be merged into the main branch before making a pull request
4. 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.

## Issues

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](https://github.com/blameitonyourisp/three-shaders/issues). Issues for this repository are tracked using the default [github](https://github.com) issue tracker ([see here](https://docs.github.com/en/issues/tracking-your-work-with-issues) for more information on managing repository issues on [github](https://github.com)).

## Git

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.

### Lightweight Commit Format

This repository uses the [lightweight commit format](https://github.com/blameitonyourisp/lightweight-commits) for formatting git commits. The [lightweight commit format](https://github.com/blameitonyourisp/lightweight-commits) is largely based on the [conventional commit format](https://www.conventionalcommits.org/en/v1.0.0/) with modifications to allow for terser commit titles. Where possible, contributors should familiarise themselves with the [lightweight commit format](https://github.com/blameitonyourisp/lightweight-commits) 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](https://www.conventionalcommits.org/en/v1.0.0/) mentioned above or these [commit message guidelines](https://gist.github.com/robertpainsi/b632364184e70900af4ab688decf6f53), making sure that your commits meet the following minimum standards:

1. Commits *must* be formatted consistently for all your changes
2. Any prose title description *must* start with an imperative form verb, for example `Add feature` or `Fix bug`
3. Any prose title description *should* start with a capital letter, and should *not* end with a period

### Branch Naming

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:

1. Words in each part of the branch name *must* be separated by a `-` (i.e. each part of the branch name must be in `lower-kebab-case`)
2. Each part of the branch name *must* be separated by a `/`
3. Branch names *must* be formed of some or all of the following parts in order:
    1. Type of changes in the branch (see table below for options)
    2. Reference number for branch if applicable (for instance issue number if branch resolves an issue)
    3. Author name or reference (for example github username or name given when calling `git config --get user.name`)
    4. 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 Environments

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:

1. Make new branch for all your changes
2. *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 of `cfg~ Modify .gitignore`
3. Make your changes as required on your new branch
4. *After* your last commit, remove your workspace configuration files, revert the `.gitignore` file, and commit these changes with a commit title of `cfg~ Modify .gitignore`

## Code Style

Primarily this repository uses a custom configuration of [ESLint](https://eslint.org/) to enforce code style. Other elements of code style and formatting not enforced by [ESLint](https://eslint.org/) are listed in the subheadings below. For any specific code style or formatting which is not enforced by [ESLint](https://eslint.org/) and not listed below, please try to match the general code style of the repository, or follow best practices for the language.

### ESLint

This repository uses [ESLint](https://eslint.org/) as the primary tool for the standardisation of JavaScript and TypeScript source code files. Note that [ESLint](https://eslint.org/) 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.

#### Configuration

The custom [ESLint](https://eslint.org/) 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](https://eslint.org/) 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](https://eslint.org/). To run [ESLint](https://eslint.org/) manually across the source code files in this repository, please see the [npm scripts reference](#npm-scripts), or run `npm run lint:check`. Similarly to fix linting errors which can be automatically resolved by [ESLint](https://eslint.org/), please see the [npm scripts reference](#npm-scripts), or run `npm run lint:fix`.

#### Overrides

Any [ESLint](https://eslint.org/) rule may be overridden with an appropriate directive. Due to the inclusion of the [eslint comments plugin](https://mysticatea.github.io/eslint-plugin-eslint-comments/), 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 matching `eslint-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](https://mysticatea.github.io/eslint-plugin-eslint-comments/), 

Please see the following code block for examples of preferred methods of overriding [ESLint](https://eslint.org/) rules:

```javascript
// 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. */
```

### Ternaries

The preferred format for ternaries in this repository is the Haskell-like ternary format which mimics a [Haskell guard statement](http://learnyouahaskell.com/syntax-in-functions#guards-guards) 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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_operator#conditional_chains) section of the [mdn](https://developer.mozilla.org/en-US/) page on ternaries.

In its [multiline ternary](https://eslint.org/docs/latest/rules/multiline-ternary) rule configuration, [ESLint](https://eslint.org/) does not currently support this exact multiline ternary format where the ternary `:` operator begins each line. The [ESLint](https://eslint.org/) 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:

```javascript
// 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.
```

### Identifier Naming

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](https://google.github.io/styleguide/jsguide.html#naming-camel-case-defined), or read [this response](https://stackoverflow.com/a/45440841) summarising the algorithm:

1. Convert identifier name to an ASCII prose form of the same name, for example `player ID`:
    1. Remove any apostrophes etc.
    2. Turn accented letters into standard letter groups (for example the German `ẞ` might become `sz`)
2. Divide the result from step 1 into individual words: 
    1. Split on spaces or any remaining punctuation such as hyphens
    2. Split any conventional abbreviations into their own separate words (for instance `AdWords` would become `ad words`)
3. Turn everything into lowercase, including all acronyms (for example `ID` will become `id`, `XML` will become `xml` etc.)
4. Turn first letter of every word to uppercase for `UpperCamelCase`, or exclude the first word to yield `lowerCamelCase`
5. 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](https://learn.microsoft.com/en-us/previous-versions/dotnet/netframework-1.1/141e06ef(v=vs.71)?redirectedfrom=MSDN), which suggests capitalising acronyms consisting of only two characters.

Especially when concatenating multiple adjacent acronyms, the [google specification](https://google.github.io/styleguide/jsguide.html#naming-camel-case-defined) 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](https://learn.microsoft.com/en-us/previous-versions/dotnet/netframework-1.1/141e06ef(v=vs.71)?redirectedfrom=MSDN), which not only looks inconsistent, but also raises ambiguity as to where some acronyms begin and/or end.

### Paths

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:

```javascript
// 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")
```

### Imports and Exports

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](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#local-paths), or by using the [subpath imports](https://nodejs.org/api/packages.html#subpath-imports) feature of node. See the [package imports](#imports) section for more information on subpath imports and why they are generally avoided in this repository.

```javascript
// 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"
```

### Comment Grammar

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](https://nedbatchelder.com/blog/201401/comments_should_be_sentences.html) for more discussion on why code comments should form readable, complete sentences.

```javascript
// Bad:
// filter array for even numbers

// Preferred:
// Filter array for even numbers.
```

## Script Format

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. 

### Naming

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 than `container/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

### Header

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:

1. 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
2. JSDoc comment containing at a minimum the following tags:
    1. `@file` tag with a description of the main purpose of the file, for example the main exports
    2. `@author` tag(s) indicating the main authors of that file
3. `@ts-check` directive comment to enforce strict type checking

Please see the [template section](#template) 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

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.                                                                                     |

### Template

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](#npm-scripts).

```javascript
// 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

```

## Getting Around

This repository is generated from a template repository which may be viewed [here](https://github.com/blameitonyourisp/js-template). 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.

### .cache

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.

### .github

The `.github` directory contains some or all files relating to [github](https://github.com) 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](#githubissue_template)) and for a specific pull request template. For more information specifically on adding a pull request template for a github repository, [see here](https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/about-issue-and-pull-request-templates). For more information on the general usage of the `./.github` directory in a repository hosted on [github](https://github.com), please see [this article](https://www.freecodecamp.org/news/how-to-use-the-dot-github-repository/). 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.

#### .github/ISSUE_TEMPLATE

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](https://github.com) documentation [here](https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-issue-forms). For information relating specifically to the syntax for the [github](https://github.com) form schema, please see [here](https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema).

### admin

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.

#### admin/assets

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.

#### admin/config

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.

#### admin/scripts

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](#npm-scripts) for more information on all of the default available scripts.

#### admin/templates

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).

#### admin/web

The `./admin/web` subdirectory contains files which should be copied to the `./dist/web` directory when building a site using the [appropriate build command](#npm-scripts). 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](https://pages.github.com/)), 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:
    - [Google](https://support.google.com/a/answer/112037?hl=en#zippy=%2Cset-up-cname-records-now)
    - [Cloudflare](https://www.cloudflare.com/en-gb/learning/dns/dns-records/dns-cname-record/)
- `robots.txt` references:
    - [Google](https://developers.google.com/search/docs/crawling-indexing/robots/intro)
    - [Cloudflare](https://www.cloudflare.com/en-gb/learning/bots/what-is-robots-txt/)
    - [robotstxt.org](https://www.robotstxt.org/) (also includes a `robots.txt` file checker)
    - [Yoast blog guide](https://yoast.com/ultimate-guide-robots-txt/)
- `sitemap.xml` references:
    - [Sitemap protocol](https://www.sitemaps.org/protocol.html)
    - [Google](https://developers.google.com/search/docs/crawling-indexing/sitemaps/build-sitemap)
    - [Sitemap generator](https://www.xml-sitemaps.com/)

### build

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](#npm-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](#src) for more information on the purpose of each subdirectory.

### dist

The `./dist` directory is reserved for production build outputs. The relevant [npm build scripts](#npm-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](#src) 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.

### docs

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.

### src

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.

#### src/bin

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](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#bin) *must* be updated with an entry to point to the new script.

#### src/package

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](https://nodejs.org/api/packages.html#subpath-exports) *must* be updated with an entry to point to the new bundled script.

#### src/web

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](https://parceljs.org/) 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.

## Package

The properties of the `package.json` file of this repository are generally ordered as follows:

1. Properties which must be updated when duplicating the template repository such as `name`, and `version`
2. Properties which may be updated directly by the maintainer during the life of the repository such as `engines`, and `scripts`
3. Properties which are generally not updated *directly* by the maintainer such as `dependencies`, and `devDependencies`

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.

### Imports

Generally, this repository *discourages* the use of the [subpath imports object](https://nodejs.org/api/packages.html#subpath-imports) 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](https://nodejs.org/api/packages.html#subpath-imports) of the `package.json` file, and instead requires an additional entry in the [paths object](https://www.typescriptlang.org/tsconfig#paths) of the `tsconfig.json` file

If a particularly long import path is unavoidable, then an entry in the [subpath imports object](https://nodejs.org/api/packages.html#subpath-imports) of the `package.json` file *may* be appropriate at the discretion of the maintainer. If a [subpath import](https://nodejs.org/api/packages.html#subpath-imports) 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:

```json
"imports": {
    "#feature": "./src/feature/index.js"
}
```

Configuration in `tsconfig.json` file:

```json
"paths": {
    "#feature": [ "./src/feature/index.js", "./src/feature/*.js" ]
}
```

Since node now natively supports [subpath imports](https://nodejs.org/api/packages.html#subpath-imports), this repository also *strongly discourages* implementing absolute path imports by using `package.json` [file dependencies](https://docs.npmjs.com/cli/v10/configuring-npm/package-json#local-paths).

### Exports

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](https://nodejs.org/api/packages.html#subpath-exports) 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](https://nodejs.org/api/packages.html#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](https://nodejs.org/api/packages.html#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](https://nodejs.org/api/packages.html#subpath-exports) reserved for the exported package bundled for CommonJS.

See the following code block for an example [subpath exports object](https://nodejs.org/api/packages.html#subpath-exports) containing the default exports and an additional export for a specific, commonly used feature:

```json
"exports": {
    ".": "./dist/package/index.js",
    "./feature": "./dist/package/feature.js",
    "./COMMON_JS": "./dist/package/index.cjs"
}
```

These [subpath exports](https://nodejs.org/api/packages.html#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):

```javascript
// 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](https://adamcoster.com/blog/commonjs-and-esm-importexport-compatibility-examples)

### NPM Scripts

This repository *largely* follows the [npm script conventions](https://eslint.org/docs/latest/contribute/package-json-conventions) from [ESLint](https://eslint.org/). Scripts in this repository *must* follow the following rules:

1. Script names *must* contain only lowercase English words (abbreviations are acceptable where they are obvious)
2. A colon (`:`) *must* be used to separate nested categories of each script
3. A hyphen (`-`) *must* be used to separate words
4. Scripts *must* be ordered alphabetically, using categories where necessary to group scripts logically

For more information on npm scripts in general, checkout [the docs](https://docs.npmjs.com/cli/v10/using-npm/scripts), review [this npm style guide](https://github.com/voorhoede/npm-style-guide#use-standard-script-names), 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](https://plopjs.com/).                                                       |
| `admin:tokei` [^1]    | Count approximate lines of code written by the author, and generate a json endpoint for a [shields](https://shields.io/) badge. |
| `admin:update-labels` | Update [github](https://github.com/) 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](https://parceljs.org/), 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.                                                           |

[^1]: Please note that this script relies on `tokei`, a CLI application written in Rust. If `tokei` 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](https://shields.io/category/size) 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](https://github.com/XAMPPRocky/tokei#badges). To circumvent this, lines of code are being counted "manually" using the tokei [rust CLI](https://github.com/XAMPPRocky/tokei) 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.

## Wiki

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](https://docs.github.com/en/communities/documenting-your-project-with-wikis) for this repository should be switched off by default.

### Enable

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 as `pull`, `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:

```bash
# 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).

### Update

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](https://docs.github.com/en/communities/documenting-your-project-with-wikis) 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:

```bash
# Commit changes to local repo, then push subtree to remote.
git commit ./github/wiki
git subtree push --prefix .github/wiki wiki master
```

### Disable

To disable a wiki repository after enabling it and adding content, please follow the list below in order:

1. Remove wiki subtree and optionally remove wiki content (see code blocks below)
2. 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:

```bash
# 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:

```bash
# 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:

```bash
# 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):

```bash
# 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!

## Releasing

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:

1. Test source files using the appropriate test command (only accept failed tests if the test is known to be *not* required or incorrect):
    1. Run `npm run test` to run all available test suites
    2. Run `npm run test:bin` to only run CLI source file tests
    3. Run `npm run test:package` to only run package source file tests
    4. Run `npm run test:web` to only run static web source file tests
2. Build production bundles of source files:
    1. If required, update public record files found at `./admin/web`:
        1. Update URLs in public record files to ensure the correct domain and/or subdomain is used
        2. Update `sitemap.xml` with new pages added etc., optionally use a [sitemap generator](https://www.xml-sitemaps.com/) to create a new sitemap
        3. See the [admin/web](#adminweb) section for more information on updating public record files
    2. Build type declarations with typescript by running `npm run types:declaration` (note that this command is also run automatically when building the package)
    3. Build production bundles of the binary, package, and or web source files by running the appropriate build command:
        1. Run `npm run build:prod` to build a production bundle with all available source files
        2. Run `npm run build:prod:bin` to build only the CLI source files
        3. Run `npm run build:prod:package` to build only the released package source files
        4. Run `npm run build:prod:web` to build only the static web source files
    4. If required, deploy static site to `gh-pages` by running `npm run admin:deploy`
3. Update `package.json` file:
    1. Do *not* update the version field as this will be updated by using the `npm version <semver>` command
    2. Update [bin object](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#bin) with any changed executable names or paths
    3. Update [subpath exports object](https://nodejs.org/api/packages.html#subpath-exports) with any additional exports provided by the package
    4. Update [engines object](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#engines) if the minimum required node or npm version has changed
    5. Update [scripts object](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#scripts) with any new or changed scripts
4. Update `README.md` file:
    1. Update basic and specific usage instructions as required
    2. Update roadmap with new features, and remove features which have now been implemented
    3. Update attributions with any new 3rd party assets
5. Update `CONTRIBUTING.md` file:
    1. Update the [getting around](#getting-around) section with any significant new or changed directory structures
    2. Update the [npm scripts](#npm-scripts) section with any new or changed scripts
6. Update `CHANGELOG.md` file:
    1. Run `npm run docs:changelog` to generate a new section in the `CHANGELOG.md` file with prompts for each relevant commit since the last version
    2. 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
7. Update software versions for dropdowns in issue templates:
    1. Add the new version number of the package to the package version dropdown
    2. Add any new LTS version of node released since the last version to the node LTS version dropdown
8. 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
9.  Update version by running `npm version <semver>` where `<semver>` is either `major`, `minor`, or `patch` depending on the changes made [^2]

[^2]: Note that the npm `preversion` script is configured to run the `build: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 npm `postversion` script is configured to push the new commit and tag to the remote repository. For more information on the `npm version` command, please see [this cheatsheet](https://michaelcurrin.github.io/dev-cheatsheets/cheatsheets/package-managers/javascript/npm/commands/version.html).

### Indexing

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](https://www.google.com/) 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](https://www.google.com/).

For information on how to submit your `sitemap.xml` file for indexing on [google](https://www.google.com/) or different search engines, please see the following resources:

- [Google sitemap documentation](https://developers.google.com/search/docs/crawling-indexing/sitemaps/build-sitemap)
- [Yoast blog guide](https://yoast.com/help/submit-sitemap-search-engines/) (includes different search engines)
- [Themeisle blog guide](https://themeisle.com/blog/submit-website-to-google/)

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](https://webmasters.stackexchange.com/questions/82687/sitemaps-one-per-subdomain-or-one-for-the-base-domain) and [google webmasters](https://support.google.com/webmasters/thread/4795511/how-to-add-sub-domain-sitemaps-into-root-domain-sitemap?hl=en)), 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.
