|
| 1 | +# JSMacros-Scripts Template |
| 2 | + |
| 3 | +This is a template for compiling, bundling, and allowing automatic updates for [JsMacros](https://github.com/JsMacros/JsMacros) scripts. |
| 4 | + |
| 5 | +**Key Features:** |
| 6 | + |
| 7 | +- **Dependency Bundling:** Uses rspack to bundle external libraries and your script code into a single `.js` file. This allows you to utilize any [GraalJS](https://github.com/oracle/graaljs) compliant packages whether custom or from a package registry like npm within your JsMacros scripts (any package that doesn't rely on browser specific globals or APIs such as `window` or Node specific globals or APIs such as `process`). |
| 8 | +- **Multi-File Script Development:** Organize your code into multiple files for better maintainability and project structure. rspack will handle bundling them together. |
| 9 | +- **Automatic Updates:** Integrates the `Updater` library to provide a seamless automatic update mechanism for your users. This ensures users are always running the latest version of your scripts. |
| 10 | + |
| 11 | +## Getting Started |
| 12 | + |
| 13 | +This template is designed to get you up and running quickly. Follow these steps to create your own JsMacros-scripts repository: |
| 14 | + |
| 15 | +1. **Create a template of the repository:** |
| 16 | + - Use the green `Use this template` button then `Create a new repository`. |
| 17 | + - Name your repository as you see fit (`JsMacros-Scripts` is a nice simple name) |
| 18 | + - Keep the project public, otherwise the `Updater` library will not work without changes. |
| 19 | +2. **Clone the repository:** |
| 20 | + ```bash |
| 21 | + git clone https://github.com/YourUsername/JsMacros-Scripts |
| 22 | + cd JsMacros-Scripts |
| 23 | + ``` |
| 24 | +3. **Install dependencies:** |
| 25 | + This template uses [pnpm](https://pnpm.io/) as it's package manager. You can find installation instructions [here](https://pnpm.io/installation). |
| 26 | + ```bash |
| 27 | + pnpm install |
| 28 | + ``` |
| 29 | +4. **Set up `package.json` (optional):** |
| 30 | + Optionally, set up the fields in `package.json`. This isn't strictly necessary necessary but is generally recommended. |
| 31 | +5. **GitHub Actions:** |
| 32 | + This template uses a GitHub Actions workflow to build and release your scripts. |
| 33 | + - If you've never used GitHub Actions before, you may need to enable them in the `Actions` tab of your repository. |
| 34 | +
|
| 35 | +## Writing your first script |
| 36 | +
|
| 37 | +There are a few things to know before you write your first script. |
| 38 | +
|
| 39 | +1. Create a new folder in `src/` using the `kebab-case` format for consistency, though technically, any name _should_ work. |
| 40 | +2. Create the following files (replace fields as expected): |
| 41 | +
|
| 42 | +`main.ts` |
| 43 | +
|
| 44 | +```ts |
| 45 | +import { updateScript } from '../libs/Updater'; |
| 46 | +// Currently, this method does *not* restart the script. Meaning that the old script will still be running until the next execution. |
| 47 | +updateScript(file.getAbsolutePath(), 'EastArctica/JSMacros-Scripts', './config/EastArctica-scripts.json'); |
| 48 | +
|
| 49 | +// Your code here |
| 50 | +
|
| 51 | +// An important required part of these scripts which you'll see in the example script is that they always `export default event`. This won't do anything functionally in our case, but is required to make TypeScript treat these files as modules and allow top level variable declarations with "duplicate" names across scripts. |
| 52 | +export default event; |
| 53 | +``` |
| 54 | +
|
| 55 | +`manifest.json` (Note that the comments must be removed to be a valid json file) |
| 56 | +
|
| 57 | +```json |
| 58 | +{ |
| 59 | + // This field can be any string (technically anything, although it should be a truthy javascript value to prevent issues) |
| 60 | + // When this field is changed, it triggers scripts using the Updater library to auto-update |
| 61 | + "version": "1.0.0", |
| 62 | + // These fields are not currently used |
| 63 | + "title": "Example TPS Display Service", |
| 64 | + "description": "An example service that displays the current TPS." |
| 65 | +} |
| 66 | +``` |
| 67 | +
|
| 68 | +3. Run either `pnpm build` or `pnpm watch` to respectively build once, or watch the files for updates and build on changes. |
| 69 | +4. Your output js files should now be in the `dist/` directory at `dist/folder-name.js` and can be run within JsMacros |
| 70 | +
|
| 71 | +## Creating your first release |
| 72 | +
|
| 73 | +Once your script is in a state ready for release, make sure your code is commited to GitHub and follow these steps to release an update. |
| 74 | +
|
| 75 | +> Note: Before the first release, the Updater script will log an error message to chat indicating that no releases were found. |
| 76 | +
|
| 77 | +1. [Create a new release in GitHub](https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository#creating-a-release) |
| 78 | +2. The title, tag name, and description of this release don't impact the GitHub Action or Updater. However, it's best to use a descriptive title and description. For tag, a simple incrementing number works if you wish, as there is no standardized verisoning system due to each script having it's own unique versions. |
| 79 | + > Note: It's important not to add any `.js` files or a `manifest.json` to the release as they may conflict with the files generated by the GitHub Action. |
| 80 | +3. Once you finish filling out your desired fields, publish the release with the `Publish release` button. |
| 81 | +4. Your release should be complete within a few minutes. After refreshing the page, you should now see the `.js` files and `manifest.json` files in your release. If not, head over to the `Actions` tab and check why it failed or why it's still running. |
| 82 | + |
| 83 | +## Examples |
| 84 | + |
| 85 | +This template comes with the builtin `example-tps-display-service` script from [here](https://jsmacros.wagyourtail.xyz/?/examples/tpsservice.html). This example has been adapted to this template with it's own `manifest.json` and respective `Updater` implementation. Please follow this for guidance or ask in the [JsMacros Discord](https://discord.gg/P6W58J8) if you're running into any issues. |
| 86 | + |
| 87 | +## Updater library |
| 88 | + |
| 89 | +In `src/libs/Updater.ts`, the `updateScript` method is exported. This method takes 3 arguments, the first being the path to the script, the second being the GitHub repository it belongs to, and the third being the config file path. Additionally, this method returns a boolean for whether the script was updated or not (regardless of whether an update was available or not). |
| 90 | + |
| 91 | +If any errors occur during while checking for an update, they are logged to chat and the function will return `false`. |
| 92 | + |
| 93 | +> Note: It's recommended to read over the [Config library](#config-library) section to understand the format and how to use the config file. |
| 94 | +
|
| 95 | +**Example:** |
| 96 | +
|
| 97 | +```ts |
| 98 | +import { updateScript } from '../libs/Updater'; |
| 99 | +// Currently, this method does *not* restart the script. Meaning that the old script will still be running until the next execution. |
| 100 | +updateScript(file.getAbsolutePath(), 'EastArctica/JSMacros-Scripts', './config/EastArctica-scripts.json'); |
| 101 | +``` |
| 102 | +
|
| 103 | +## Config Library |
| 104 | +
|
| 105 | +In `src/libs/Config.ts`, the `Config` class is exposed which provides a convenient way to manage JSON config files for your JsMacros scripts. It handles reading, writing, and creating default configs. The intent of this is to have one JSON file to manage the config for all your scripts. Rather than having a different config file for each script, this approach maintains a shared config per developer instead. |
| 106 | +
|
| 107 | +### Key Features |
| 108 | +
|
| 109 | +- **ID-Based Config:** Configs are accessed and modified using a unique `id`, allowing multiple scripts to store their configs within a single shared file. |
| 110 | +- **Automatic Default Config:** If a config file does not exist, or if a specific `id` does not exist within the file, the library automatically creates it using a provided default config object for that `id`. |
| 111 | +- **Shared Config File:** Designed to use a single config file to manage settings for multiple scripts, primarily reducing file clutter. |
| 112 | +- **Error Handling:** Error handling with backup creation in case of parsing failures. |
| 113 | +- **Config Merging:** Merges the loaded config for a specific `id` with the default config. This ensures all default properties are present. |
| 114 | +
|
| 115 | +### Important Note on the Shared Config File |
| 116 | +
|
| 117 | +This config library is designed with the intention of using a single config file (e.g., `author-config.json`) to store the settings for all of your scripts. Each script will access and modify its own config section within this shared file using a unique `id`. This approach simplifies config management and promotes organization, especially when you have multiple scripts. |
| 118 | +
|
| 119 | +### Usage |
| 120 | +
|
| 121 | +1. **Reading Config:** |
| 122 | + The `Config.readConfig<T>(path: string, defaultConfig: T, id: string): T` method reads a JSON config file from the specified `path` and retrieves the config for the specified `id`. |
| 123 | +
|
| 124 | + - `path`: The file path to the shared config file. |
| 125 | + - `defaultConfig`: An object representing the default config. |
| 126 | + - `id`: A unique string identifier for your script's config within the config file. |
| 127 | + |
| 128 | + If the file or id does not exist, it is created or added with the `defaultConfig` content. If parsing fails, the `defaultConfig` is returned, and a backup of the corrupted file is created. |
| 129 | + |
| 130 | + **Example:** |
| 131 | + |
| 132 | + ```ts |
| 133 | + import Config from '../libs/Config'; |
| 134 | +
|
| 135 | + const configPath = './config/author-config.json'; |
| 136 | + const scriptId = 'my-script-name'; // Unique ID for this script's config |
| 137 | + // Default config for the first run |
| 138 | + const defaultConfig = { |
| 139 | + enabled: true, |
| 140 | + threshold: 10, |
| 141 | + whitelistPlayers: ['notch'], |
| 142 | + }; |
| 143 | +
|
| 144 | + const config = Config.readConfig(configPath, defaultConfig, scriptId); |
| 145 | + // `config` will now contain the config specifically for 'my-script-name' |
| 146 | + console.log(config.enabled); // Access script-specific config properties directly. |
| 147 | + ``` |
| 148 | +
|
| 149 | +2. **Writing Config:** |
| 150 | + The `Config.writeConfig(path: string, config: object, id: string` method writes a config object to the specified config at the `path` under the provided `id`. |
| 151 | +
|
| 152 | + - `path`: The file path to the shared config file. |
| 153 | + - `config`: The object config to be written for the specified `id`. This will overwrite the existing config for this `id` in the config. |
| 154 | + - `id`: The unique string identifier for your script's config. |
| 155 | +
|
| 156 | + **Example:** |
| 157 | +
|
| 158 | + ```ts |
| 159 | + import Config from '../libs/Config'; |
| 160 | + |
| 161 | + const configPath = './config/author-config.json'; |
| 162 | + const scriptId = 'my-script-name'; // Unique ID for this script's config |
| 163 | + const defaultConfig = { |
| 164 | + enabled: true, |
| 165 | + threshold: 10, |
| 166 | + whitelistPlayers: ['notch'], |
| 167 | + }; |
| 168 | +
|
| 169 | + const config = Config.readConfig(configPath, defaultConfig, scriptId); |
| 170 | +
|
| 171 | + config.enabled = false; |
| 172 | +
|
| 173 | + Config.writeConfig(configPath, config, scriptId); |
| 174 | + ``` |
| 175 | +
|
| 176 | +## Preferences |
| 177 | +
|
| 178 | +I understand people may not have the same preferences as me for linting, package managers, etc. So I try to be considerate and give people an easy way to go their own way. |
| 179 | +
|
| 180 | +By default, none of my tooling is _required_, only recommended. If you choose to compile & bundle scripts in your own way, that's fine! This template is meant to be a base for people to extend off. Nothing in this project requires prettier or pnpm to be functioning (however the GitHub Action may complain or break without pnpm related files, but could be swapped over to npm without much effort). |
| 181 | + |
| 182 | +### Don't like prettier/formatting? |
| 183 | + |
| 184 | +Remove `./vscode/`, `.editorconfig`, `.prettierrc`, and `.prettierignore` |
| 185 | + |
| 186 | +```bash |
| 187 | +# Linux/Unix |
| 188 | +rm -r ./vscode/ .editorconfig .prettierrc .prettierignore |
| 189 | +# Windows (cmd) |
| 190 | +del /s /q .vscode\* .editorconfig .prettierrc .prettierignore |
| 191 | +# Windows (PowerShell) |
| 192 | +Remove-Item -Recurse -Force .vscode, .editorconfig, .prettierrc, .prettierignore |
| 193 | +``` |
| 194 | + |
| 195 | +Remove `prettier` from `package.json` |
| 196 | + |
| 197 | +```bash |
| 198 | +pnpm remove prettier |
| 199 | +``` |
| 200 | + |
| 201 | +Remove the prettier section from the `lint` script inside `package.json` |
| 202 | + |
| 203 | +### Don't like pnpm? |
| 204 | + |
| 205 | +First off, I would recommend you at least look into it if you weren't aware of it beforehand. However, Id like to mention that following these steps will remove any package lock information as well as may cause issues with the GitHub Action (however I _believe_ everything should work). |
| 206 | +
|
| 207 | +If you'd like to remove it, just remove `pnpm-lock.yaml`. Additionally, I believe you may remove `"packageManager": "[email protected]` line from the `package-lock.json` file. |
| 208 | +
|
| 209 | +## License |
| 210 | +
|
| 211 | +This project is licensed under the MIT License. See the [LICENSE.md](./LICENSE.md) file in the root of this repository for full details. |
| 212 | +
|
| 213 | +This license allows you to freely use, modify, distribute, and sublicense the code with proper attribution. |
0 commit comments