Extensive Typescript ESNext template and boilerplate for userscripts.
Supports importing and parsing HTML and Markdown files directly in code, packing it all up with rollup and applying custom injections for the userscript header and more.
It also offers ESLint to lint and auto-fix the code and GitHub Actions with ESLint to lint the code in pull requests and CodeQL to check it for vulnerabilities on every push.
Requires a Git repo to be used as the asset CDN and for the build versioning system of the userscript.
Supports distribution on GitHub, GreasyFork and OpenUserJS out of the box.
Like this template? Please consider supporting the development ❤️
- Make sure Node.js and a userscript manager are installed (I recommend ViolentMonkey)
- Click here to create a repository on GitHub using this template
- Clone the repository you created
- Open a terminal in the root of the folder and use
npm i
to install dependencies - Refer to first steps
- Copy
.env.template
to.env
and change the values inside if needed - Search for
#REPLACE:
with your IDE in the entire project and replace all placeholders with your own values - Replace the icon at
assets/icon.png
with your own or use Google's or DuckDuckGo's favicon API in the userscript header (see step 5) - Replace the LICENSE.txt file with your own license or remove it if you want your code to be "all rights reserved" (needs to be adjusted in the
license
field ofpackage.json
too!) - Modify the userscript header inside
src/tools/post-build.ts
to whatever you need (see also GM header reference) - The eslint configuration at
.eslintrc.cjs
is what I use, feel free to remove rules if there are too many or modify them to your preferences - Add your own initialization functions to
init()
andrun()
inside the entrypoint file atsrc/index.ts
(read the comments for more info) - Refer to the sections project structure, commands and development next.
If you need a real world example, you can take a look at my BetterYTM project, which this template is based on.
assets/
Contains all the assets that are used in the userscript, like images, stylesheets, and any other misc files.assets/require.json
Contains a list of all modules that should be loaded using the@require
directive.
Props:pkgName
(required) - The npm name of the packagebaseUrl
- The base URL of the CDN to load from (defaults tohttps://cdn.jsdelivr.net/npm/
)path
- The path to the file inside the package (uses jsdelivr's standard path by default)link
- Set this totrue
if you have linked this package locally usingnpm link
and want to explicitly have the latest code included in the bundle.
assets/resources.json
Contains a list of all resources that should be loaded using the@resource
directive.
This is useful for loading files like stylesheets, images, and other assets that should not be included in the code bundle, but loaded in and cached by the userscript extension.
The object's key is used with thegetResourceUrl()
function insrc/utils.ts
to get the URL of the resource. The value is the path to the file inside theassets/
folder.
If the path starts with a slash, it will be resolved relative to the root of the project (where thepackage.json
file is located).
src/
Contains all the source files for the userscript.src/tools/
Contains all scripts that are used in the build process.src/tools/post-build.ts
Contains the post-build script that is run after the userscript is built.
This script inserts the userscript header and other custom injections into the final userscript file.
If you need to modify userscript directives or add custom injections, this is the place to do it.src/tools/serve.ts
Contains the development server that serves the userscript on port 8710 (by default).
src/index.ts
The entrypoint for the userscript. This is where you should call your own code from.src/config.ts
This file contains the DataStore instance that should be used to hold your userscript's configuration object.
The DataStore class is very powerful and does a lot of the heavy lifting for you. More instances can also be created to hold different types of data that should be persisted between sessions, cached in-memory for fast, synchronous access and be tagged with a version number, so migration functions can be used to migrate the data to any upcoming format.
For more info, please read the DataStore documentation.src/constants.ts
Contains some constant variables that are used throughout the userscript's runtime code.
This is where arguments are injected into the userscript's runtime code bytools/post-build.ts
src/declarations.d.ts
The declarations in this file allow you to import HTML and MD files directly in your code, using rollup plugins.src/observers.ts
This file should be filled up with manySelectorObserver
instances. These observe a designated part of the DOM for changes.
With the functionaddSelectorListener()
, you can then add a listener, given a specific CSS selector, that gets called when the selector has been found in the DOM.
For more info, please read the SelectorObserver documentation.src/types.ts
Contains all generic TypeScript types that are used in multiple files.src/utils.ts
Contains utility functions that are used throughout the userscript's runtime code.
This is where you should put functions that are used in multiple places in your code.
Once it gets too big you should split it up into multiple files.
-
npm i
Run once to install dependencies -
npm run dev
This is the command you want to use to locally develop and test your script.
It watches for any changes, then rebuilds and serves the userscript on port 8710, so it can be updated live if set up correctly in the userscript manager (see development tips).
Once it has finished building, a link will be printed to the console. Open it to install the userscript.
You can also configure request logging and more in.env
andsrc/tools/serve.ts
, just make sure to restart the dev server after changing anything. -
npm run build-prod
Builds the userscript for production for all host platforms (GitHub, GreasyFork and OpenUserJS) with their respective options already set.
Outputs the files using a suffix predefined in thepackage.json
file.
Use this to build the userscript for distribution on all hosts. -
npm run build -- <arguments>
Builds the userscript with custom options
Arguments:--config-mode=<value>
- The mode to build in. Can be eitherproduction
ordevelopment
(default)--config-branch=<value>
- The GitHub branch to target. Can be any branch name, but should bemain
for production anddevelop
for development (default)--config-host=<value>
- The host to build for. Can be eithergithub
(default),greasyfork
oropenuserjs
--config-assetSource=<value>
- Where to get the resource files from. Can be eitherlocal
orgithub
(default)--config-suffix=<value>
- Suffix to add just before the.user.js
extension. Defaults to an empty string
Shorthand commands:
npm run build-prod-base
- Sets--config-mode=production
and--config-branch=main
Used for building for production, targeting the main branchnpm run build-develop
- Sets--config-mode=development
,--config-branch=develop
and--config-assetSource=github
Used for building for experimental versions, targeting the develop branch
-
npm run lint
Builds the userscript with the TypeScript compiler and lints it with ESLint. Doesn't verify all of the functionality of the script, only syntax and TypeScript errors! -
npm run --silent invisible -- "<command>"
Runs the passed command as a detached child process without giving any console output.
Remove--silent
to see npm's info and error messages. -
npm run node-ts -- <path>
Runs the TypeScript file at the given path using the regular node binary and the node-ts loader.
Also enables source map support and disables experimental warnings.
- If you're using the ViolentMonkey extension (which I recommend), after running the command
npm run watch
, you may open the URL shown in the console in your browser and select theTrack local file
option in the installation dialog.
This makes it so the userscript automatically updates when the code changes (reloading the website is still necessary).
Note: the tab needs to stay open on Firefox or the script won't keep updating itself. - My library UserUtils is already included as a dependency. It offers lots of utilities for userscripts like registering listeners for when CSS selectors exist, intercepting events, creating persistent JSON databases, modifying the DOM more easily, various math and array functions and more. You can find the full list of features and their documentation here.
- Libraries that are required at runtime should be declared inside
require.json
, as long as they are hosted on a CDN and expose a global variable.
This way, they will be loaded using the@require
directive and will be exempt from minification rules on platforms like GreasyFork.
You may use a service like jsDelivr to include any npm library this way.
You will still be able to import and use the libraries as usual in your code, the bundler will handle everything else. - The final bundled userscript file in the
dist/
folder should be committed and pushed to GitHub.
This way, the@downloadURL
and@updateURL
directives make it so the script is automatically updated from that same file.
For this to work properly, don't forget to bump the version inpackage.json
before building, so that every user of your userscript may receive the update. - The name of the emitted bundle inside
dist/
is bound touserscriptName
inpackage.json
You may want to hard-code it or create a separate property for it if the userscript name contains characters that aren't allowed in a file path. - If you want other people to use your userscript, I recommend publishing it to GreasyFork and/or OpenUserJS.
Make sure to check out and follow their rules and guidelines before publishing. - Use an IDE like VS Code so Intellisense and Typescript can work together to give you really awesome code completion and warn you about potential runtime errors before you even build the code.
- If you are using VS Code, install the ESLint extension (
dbaeumer.vscode-eslint
) and bind a hotkey for theESLint: Fix all auto-fixable problems
command so you can quickly format the currently active file according to the rules in.eslintrc.cjs
- Try to get familiar with the rollup config at
rollup.config.mjs
In there you may add and configure rollup plugins and configure the build process.
Made with ❤️ by Sv443
If you like this template, please consider supporting me
© 2024 Sv443 - WTFPL license