UNPKG

@blameitonyourisp/blurrid

Version:

Generate and render blurred placeholders for lazy loaded images.

507 lines (349 loc) 67.7 kB
<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,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+CjwhLS0gQ3JlYXRlZCB3aXRoIFZlY3Rvcm5hdG9yIChodHRwOi8vdmVjdG9ybmF0b3IuaW8vKSAtLT4KPHN2ZyBoZWlnaHQ9IjEwMCUiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3R5bGU9ImZpbGwtcnVsZTpub256ZXJvO2NsaXAtcnVsZTpldmVub2RkO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDsiIHZlcnNpb249IjEuMSIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgd2lkdGg9IjEwMCUiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6dmVjdG9ybmF0b3I9Imh0dHA6Ly92ZWN0b3JuYXRvci5pbyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPgo8ZGVmcy8+CjxnIGlkPSJMYXllci01IiB2ZWN0b3JuYXRvcjpsYXllck5hbWU9IkxheWVyIDUiPgo8cGF0aCBkPSJNMTc1IDBMODQ5IDBDOTQ1LjY1IDAgMTAyNCA3OC4zNTAyIDEwMjQgMTc1TDEwMjQgODQ5QzEwMjQgOTQ1LjY1IDk0NS42NSAxMDI0IDg0OSAxMDI0TDE3NSAxMDI0Qzc4LjM1MDIgMTAyNCAwIDk0NS42NSAwIDg0OUwwIDE3NUMwIDc4LjM1MDIgNzguMzUwMiAwIDE3NSAwWiIgZmlsbD0iI2U5ZTdlMiIgZmlsbC1ydWxlPSJub256ZXJvIiBvcGFjaXR5PSIwIiBzdHJva2U9Im5vbmUiLz4KPHBhdGggZD0iTTMwMiAzODJDMzAyIDQzNy4yMjggMjU3LjIyOCA0ODIgMjAyIDQ4MkwyMDIgNjQyQzIzNy40MzcgNjQyIDI3MS4yMTYgNjM0Ljg2OSAzMDIgNjIyLjAzMUwzMDIgOTUyTDQ2MiA5NTJMNDYyIDM4MkwzMDIgMzgyWiIgZmlsbD0iIzZmNGRiMyIgZmlsbC1ydWxlPSJub256ZXJvIiBvcGFjaXR5PSIxIiBzdHJva2U9Im5vbmUiLz4KPHBhdGggZD0iTTU2MiA3MkM0MTguNDA2IDcxLjk5OTkgMzAyIDE4OC40MDUgMzAyIDMzMkw0NjIgMzMyQzQ2MiAyNzYuNzcyIDUwNi43NzIgMjMyIDU2MiAyMzJDNjE3LjIyOCAyMzIgNjYyIDI3Ni43NzIgNjYyIDMzMkM2NjIgMzg3LjIyOCA2MTcuMjI4IDQzMiA1NjIgNDMyTDUxMiA0MzJMNTEyIDU5Mkw1NjIgNTkyQzYxNy4yMjggNTkyIDY2MiA2MzYuNzcyIDY2MiA2OTJDNjYyIDc0Ny4yMjggNjE3LjIyOCA3OTIgNTYyIDc5Mkw1MTIgNzkyTDUxMiA5NTJMNTYyIDk1MkM3MDUuNTk0IDk1MiA4MjIgODM1LjU5NSA4MjIgNjkyQzgyMiA2MjIuMDM2IDc5NC4xMzcgNTU4LjczNiA3NDkuMTg4IDUxMkM3OTQuMTM3IDQ2NS4yNjQgODIyIDQwMS45NjQgODIyIDMzMkM4MjIgMTg4LjQwNiA3MDUuNTk1IDcyIDU2MiA3MloiIGZpbGw9IiM3Nzk5NjYiIGZpbGwtcnVsZT0ibm9uemVybyIgb3BhY2l0eT0iMSIgc3Ryb2tlPSJub25lIi8+CjxwYXRoIGQ9Ik0zMDIgMzgyQzMwMiA0MzcuMjI4IDI1Ny4yMjggNDgyIDIwMiA0ODJMMjAyIDY0MkMzNDUuNTk0IDY0MiA0NjIgNTI1LjU5NCA0NjIgMzgyTDMwMiAzODJaIiBmaWxsPSIjNGYzYTc4IiBmaWxsLXJ1bGU9Im5vbnplcm8iIG9wYWNpdHk9IjEiIHN0cm9rZT0ibm9uZSIvPgo8cGF0aCBkPSJNNDYyIDMzMkM0NjIgMjc2Ljc3MiA1MDYuNzcyIDIzMiA1NjIgMjMyTDU2MiA3MkM0MTguNDA2IDcxLjk5OTkgMzAyIDE4OC40MDYgMzAyIDMzMkw0NjIgMzMyWiIgZmlsbD0iIzQxNTg0MSIgZmlsbC1ydWxlPSJub256ZXJvIiBvcGFjaXR5PSIxIiBzdHJva2U9Im5vbmUiLz4KPC9nPgo8L3N2Zz4K"> </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/blurrid/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 | | --------------------- | ------------------------------------------------------------------------------------------------------------------------------- | | `admi