Skip to content

Commit

Permalink
feat!: use webpack externals & @require for libs
Browse files Browse the repository at this point in the history
  • Loading branch information
Sv443 committed Sep 7, 2023
1 parent 301fc8c commit a65579d
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 127 deletions.
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<h1><img alt="icon" src="./assets/icon.png"><br>Userscript.ts</h1>

Typescript ESM template for making [userscripts](https://en.wikipedia.org/wiki/Userscript) that supports importing, parsing and minifying HTML, CSS, Markdown and misc. files directly in code, packing it all up with webpack and applying custom injections for the userscript header and more.
Typescript ESM template for making [userscripts](https://en.wikipedia.org/wiki/Userscript) that supports importing and parsing HTML, CSS, Markdown and misc. files directly in code, packing it all up with webpack 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.

Like this template? Please consider [supporting the development ❤️](https://github.com/sponsors/Sv443)
Expand Down Expand Up @@ -52,12 +52,16 @@ Like this template? Please consider [supporting the development ❤️](https://
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](https://github.com/Sv443-Network/UserUtils) is already included as a dependency. It offers lots of utilities for userscripts like registering listeners for when CSS selectors exist, intercepting events, managing persistent user configurations, modifying the DOM more easily, various math and array functions and more. You can find the full list of features and its documentation [here.](https://github.com/Sv443-Network/UserUtils#table-of-contents)
- Libraries that are required at runtime should be declared inside `dependencies.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](https://greasyfork.org/en/help/code-rules) on platforms like GreasyFork.
You may use a service like [jsDelivr](https://www.jsdelivr.com/) to include any npm library this way.
You will still be able to import and use the libraries as usual in your code.
- 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 in `package.json` before building, so that every user of your userscript may receive the update.
- The name of the emitted bundle inside `dist/` is bound to `userscriptName` in `package.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](https://greasyfork.org) and/or [OpenUserJS](https://openuserjs.org)
- If you want other people to use your userscript, I recommend publishing it to [GreasyFork](https://greasyfork.org) and/or [OpenUserJS.](https://openuserjs.org)
Make sure to check out and follow their rules and guidelines before publishing.
- Use an IDE like [VS Code](https://code.visualstudio.com/) 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`](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)) and bind a hotkey for the `ESLint: Fix all auto-fixable problems` command so you can quickly format the currently active file according to the rules in `.eslintrc.cjs`
Expand Down
6 changes: 6 additions & 0 deletions dependencies.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"@sv443-network/userutils": {
"url": "https://greasyfork.org/scripts/472956-userutils/code/UserUtils.js",
"global": "UserUtils"
}
}
19 changes: 7 additions & 12 deletions dist/EXAMPLE.user.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
// ==UserScript==
// @name EXAMPLE
// @name Userscript.ts Example
// @homepageURL https://github.com/Sv443/Userscript.ts#readme
// @namespace https://github.com/Sv443/Userscript.ts
// @version 0.1.0
// @description EXAMPLE
// @description Example script - install and visit example.org for a quick and dirty demo
// @license WTFPL
// @author Sv443
// @copyright Sv443 (https://github.com/Sv443)
// @icon https://raw.githubusercontent.com/Sv443/Userscript.ts/main/assets/icon.png
// @run-at document-start
// @match https://example.org/*
// @match https://example.com/*
// @connect self
// @connect github.com
// @connect githubusercontent.com
// @require https://greasyfork.org/scripts/472956-userutils/code/UserUtils.js
// @downloadURL https://raw.githubusercontent.com/Sv443/Userscript.ts/main/dist/EXAMPLE.user.js
// @updateURL https://raw.githubusercontent.com/Sv443/Userscript.ts/main/dist/EXAMPLE.user.js
// ==/UserScript==
Expand All @@ -22,11 +20,8 @@

var __webpack_exports__ = {};

;// CONCATENATED MODULE: ./node_modules/@sv443-network/userutils/dist/index.mjs
var h=Object.defineProperty,y=Object.defineProperties;var g=Object.getOwnPropertyDescriptors;var p=Object.getOwnPropertySymbols;var w=Object.prototype.hasOwnProperty,v=Object.prototype.propertyIsEnumerable;var f=(t,e,n)=>e in t?h(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,c=(t,e)=>{for(var n in e||(e={}))w.call(e,n)&&f(t,n,e[n]);if(p)for(var n of p(e))v.call(e,n)&&f(t,n,e[n]);return t},b=(t,e)=>y(t,g(e));var T=(t,e,n)=>new Promise((r,o)=>{var i=s=>{try{a(n.next(s));}catch(m){o(m);}},u=s=>{try{a(n.throw(s));}catch(m){o(m);}},a=s=>s.done?r(s.value):Promise.resolve(s.value).then(i,u);a((n=n.apply(t,e)).next());});function S(t,e,n){return Math.max(Math.min(t,n),e)}function A(t,e,n,r,o){return Number(e)===0&&Number(r)===0?t*(o/n):(t-e)*((o-r)/(n-e))+r}function d(...t){let e,n;if(typeof t[0]=="number"&&typeof t[1]=="number")[e,n]=t;else if(typeof t[0]=="number"&&typeof t[1]!="number")e=0,n=t[0];else throw new TypeError(`Wrong parameter(s) provided - expected: "number" and "number|undefined", got: "${typeof t[0]}" and "${typeof t[1]}"`);if(e=Number(e),n=Number(n),isNaN(e)||isNaN(n))throw new TypeError(`Parameters "min" and "max" can't be NaN`);if(e>n)throw new TypeError(`Parameter "min" can't be bigger than "max"`);return Math.floor(Math.random()*(n-e+1))+e}function H(t){return x(t)[0]}function x(t){if(t.length===0)return [void 0,void 0];let e=d(t.length-1);return [t[e],e]}function I(t){let[e,n]=x(t);if(n!==void 0)return t.splice(n,1),e}function P(t){let e=[...t];if(t.length===0)return t;for(let n=e.length-1;n>0;n--){let r=Math.floor(d(0,1e4)/1e4*(n+1));[e[n],e[r]]=[e[r],e[n]];}return e}function O(){try{return unsafeWindow}catch(t){return window}}function j(t,e){var n;return (n=t.parentNode)==null||n.insertBefore(e,t.nextSibling),e}function R(t,e){let n=t.parentNode;if(!n)throw new Error("Element doesn't have a parent node");return n.replaceChild(e,t),e.appendChild(t),e}function F(t){let e=document.createElement("style");e.innerHTML=t,document.head.appendChild(e);}function W(t,e=!1){let n=t.map(r=>new Promise((o,i)=>{let u=new Image;u.src=r,u.addEventListener("load",()=>o(u)),u.addEventListener("error",a=>e&&i(a));}));return Promise.allSettled(n)}function $(t){let e=document.createElement("a");Object.assign(e,{className:"userutils-open-in-new-tab",target:"_blank",rel:"noopener noreferrer",href:t}),e.style.display="none",document.body.appendChild(e),e.click(),setTimeout(e.remove,50);}function L(t,e,n){typeof Error.stackTraceLimit=="number"&&Error.stackTraceLimit<1e3&&(Error.stackTraceLimit=1e3),function(r){element.__proto__.addEventListener=function(...o){if(!(o[0]===e&&n()))return r.apply(this,o)};}(t.__proto__.addEventListener);}function B(t,e){return L(O(),t,e)}function q(t,e=1){let n=new(window.AudioContext||window.webkitAudioContext),r={mediaElement:t,amplify:o=>{r.gain.gain.value=o;},getAmpLevel:()=>r.gain.gain.value,context:n,source:n.createMediaElementSource(t),gain:n.createGain()};return r.source.connect(r.gain),r.gain.connect(n.destination),r.amplify(e),r}function z(t,e){return (Array.isArray(e)||e instanceof NodeList)&&(e=e.length),`${t}${e===1?"":"s"}`}function U(t){return new Promise(e=>{setTimeout(e,t);})}function D(t,e=300){let n;return function(...r){clearTimeout(n),n=setTimeout(()=>t.apply(this,r),e);}}function J(n){return T(this,arguments,function*(t,e={}){let{timeout:r=1e4}=e,o=new AbortController,i=setTimeout(()=>o.abort(),r),u=yield fetch(t,b(c({},e),{signal:o.signal}));return clearTimeout(i),u})}var l=new Map;function V(t,e){let n=[];l.has(t)&&(n=l.get(t)),n.push(e),l.set(t,n),E(t,n);}function X(t){return l.delete(t)}function E(t,e){let n=[];if(e.forEach((r,o)=>{try{let i=r.all?document.querySelectorAll(t):document.querySelector(t);(i!==null&&i instanceof NodeList&&i.length>0||i!==null)&&(r.listener(i),r.continuous||n.push(o));}catch(i){console.error(`Couldn't call listener for selector '${t}'`,i);}}),n.length>0){let r=e.filter((o,i)=>!n.includes(i));r.length===0?l.delete(t):l.set(t,r);}}function Y(t={}){new MutationObserver(()=>{for(let[n,r]of l.entries())E(n,r);}).observe(document.body,c({subtree:!0,childList:!0},t));}function Z(){return l}



;// CONCATENATED MODULE: external "UserUtils"
const external_UserUtils_namespaceObject = UserUtils;
;// CONCATENATED MODULE: ./changelog.md
// Module
var code = "<!--\n## 0.2.0\n- ...\n\n<br>\n--> <h2 id=\"010\">0.1.0</h2> <ul> <li>Initial release</li> </ul> ";
Expand Down Expand Up @@ -66,7 +61,7 @@ function insertExampleElements() {
* Note: if you set `@run-at` to something like `document-end`, the `DOMContentLoaded` event may not be called depending on the userscript extension. In this case you may remove the onDomLoad() function and modify the DOM directly in init().
*/
function init() {
const buildNbr = "c79d7df";
const buildNbr = "301fc8c";
const buildNbrText = !buildNbr.match(/^{{.+}}$/) ? `-${buildNbr}` : "";
// watermark in the console based on values grabbed out of the userscript header
console.log(`${GM.info.script.name} (${GM.info.script.version}${buildNbrText}) - ${GM.info.script.namespace}`);
Expand All @@ -90,7 +85,7 @@ function onLoad() {
`;
// if no css file is imported anywhere, no bundle is emitted and so addGlobalStyle has to be skipped
if (!globalStyle.match(/^{{.+}}$/))
F(globalStyle);
(0,external_UserUtils_namespaceObject.addGlobalStyle)(globalStyle);
// go to this function's definition in `example.ts` for an example on how to import HTML, CSS and markdown
insertExampleElements();
}
Expand Down
Loading

0 comments on commit a65579d

Please sign in to comment.