Develop Tampermonkey scripts in a real IDE with automatic deployment to OpenUserJs repo
Asked Answered
H

8

53

I recently started development on a Tampermonkey script, which is hosted on OpenUserJs. It seems that I'm going to invest a bit more time in the future on this script by keep it up to date and extend his features when time is there. The first lines I wrote on the Tampermonkey editor which is integrated in chrome (edit button of a script).

But I don't like it, the most thing I'm missing is some kind of autocomplete/intellisense. Visual Studio is much better here, so I switched to VS. The problem: After any changes, I have to copy the hole code and paste it in the Tampermonkey editor (Google Chrome). Thats annoying and not very flexible, since I can't really split my code in multiple js files when the script grows.

So is there a way to automate this? My imagination would be: I save the js file in VS (ctrl + s), then the script is loaded in my local development instance of google chrome for testing purpose.

Extension:

I want to publish alpha/beta releases as hosted version on OpenUserJs. So I can test the release easily on different systems. And I also have at least one system, where I do the real update process over the OpenUserJs repo like my end users will do. I think this is important, I already saw some differences according to my manual workflow (c&p in the OpenUserJs editor).

My preferable soultion would be some kind of branches like I know from git. So that I install the script from OpenUserJs like my users do with the production one, but I can choose somewhere to get e.g. the branch development instead of master. OpenUserJs seems to support github as source base, but no kind of branches. I can't imagine, that there is no solution for such issues, which at least every developer with bigger scripts should have...

Hungerford answered 18/12, 2016 at 20:29 Comment(2)
Can edit the actual file that is stored on your machine using any editor. As soon as you save changes and reload corresponding page they will be active, as well as changes show in TM editor – Sevastopol
You can use a symbolic link that point to the file, but you'd have to switch to developing in Firefox... see https://mcmap.net/q/340567/-how-to-correctly-work-on-a-greasemonkey-userscript-using-git/145346 and https://mcmap.net/q/340568/-how-to-sync-locally-hosted-greasemonkey-scripts-across-multiple-machines/145346 – Menace
G
99

I found my way to it, so it's not official or anything. And it's easier than it looks, I just wanted to be thorough. We are just instructing the browser and Tampermonkey (TM) to load the script from our file system, which we'll then edit directly.

Coding to instant updates πŸ‘¨β€πŸ’»

  1. Go to Chrome => Extensions and find the TM 'card'. Click details. On the page that opens, allow it access to file URLs:

enter image description here

  1. Save your script file wherever you want in your filesystem. Save the entire thing, including the ==UserScript== header. This works in all desktop OS's, but since I'm using macOS, my path will be: /Users/me/Scripts/SameWindowHref.user.js

  2. Now, go to the TM extension's dashboard, open the script in question in its editor, and delete everything except the entire ==UserScript== header

  3. Add to the header a @require property pointing to the script's absolute path.

At this point, TM's editor should look something like this:

enter image description here


Update: It seems that using the file:// URI scheme at the beginning of your path is now required. On windows systems would be:

// @require      file://C:\path\to\userscript.user.js

For macOS and *nix, we'll need three slashes in a row:

// @require      file:///path/to/userscript.user.js

Execution Contexts πŸ’» (advanced)

If you have multiple JavaScript files called with @require (like jQuery or when fragmenting a massive script into smaller pieces for a better experience), don't skip this part.

The @require paths can reference *.user.js or directly *.js files, and any UserScript-style comment headers in these files have no effect.

From the main script's ==UserScript== header, all @require files are text-concatenated in the order specified, with a single newline separating each file. This amalgamation runs as one large script. This means any global function or variable in any file will also be global in all your userscript's files, which isn't ideal. Errors in one file may influence how subsequent files run. Additionally, to enable strict mode on all of your files, 'use strict'; must be the first statement of the first file listed with @require.

After all @require files run, your main UserScript (the one accessed by TamperMonkey's editor) is run in a separate context. If you want strict mode, you must also enable it here.

Workflow πŸ•Ί

Now every time that script matches (@match) the website you are visiting, TamperMonkey will directly load and run the code straight from the file on disk, pointed by the @require field.

I use VSCode (arguably the best multiplatform code editor ever. And free), so that's where I work on the script, but any text editor will do. It should look like this:

enter image description here

Notice how TM's editor and your IDE/Editor have the same header.

Every change in the code is saved automatically in VSCode, so if yours doesn't: remember to save. Then you'll have to reload the website to load the changes.


Bonus tips!

You can easily automate reloading the site on file change using a one-liner from browser-sync's CLI, to mention one tool, and have a great experience.

If you're not using git, you should consider using it with your userscripts, even if you are the sole developer. It will help keep track of your progress, sanely work on different features at the same time, roll back mistakes, and help you automatically release new updates to your users!

And please share all your creations here and here πŸ˜„

Working with GitHub or other SCMs

You have to add an @updateURL tag followed by the URL with the raw file from GitHub or whatever provider you chose. GitHub's example:

enter image description here

Note that a @version tag is required to make update checks work. The vast majority of users don't need the @downloadURL tag, so unless your script has a massive follower base, use @updateURL.

TM will check for updates as it's configured from the settings tab:

enter image description here

Externals sets how often the scripts called from your script's @require are checked to update (jQuery, for example).

You can also "force" an update check:

enter image description here

Using external libraries (like jQuery)

It must be present at least in TM's editor to load it. However, I recommend keeping both headers (the TM's and the file on disk's header) the same to avoid confusion. Simply @require it like this to use it:

// @require      https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js

Documentation

Take a look at TM's documentation page, it's very concise, and with a quick read, you'll have a clear picture of what you can do. Happy hacking! πŸ’ͺ

Guberniya answered 8/4, 2019 at 7:46 Comment(7)
The problem is that the script header from step 2 (the script on the file system) has no effect on the resulting userscript. The header is bound to the Tampermonkey editor. Requiring external scripts and resources and granting special GM_* API's will always result in changing the header in TM. – Ophiology
@Ophiology I'm not sure I follow. I edited the post adding a capture of what your code editor should look like, hopefully, it's clear now. If not, reply and we can discuss it further. – Guberniya
After taking these steps, if you attempt to @require some external script or a GM_* API in your /Users/me/Scripts/SameWindowHref.user.js , the script (or API) won't be available. You will have to make the corresponding change in the header of the script in Tampermonkey aswel. This is because of how TM loads required scripts. It just copies them in the current userscript function execution context: prnt.sc/nprafz . In other words - the userscript header from your filesystem is not accounted for. – Ophiology
Ah! I see the problem. I personally keep both headers 100% equal to avoid confusion. In your particular case, you forgot to @require the jQuery CDN link from the TM editor. If you do that, it'll work :) – Guberniya
Thank you! this saves a lot of time to develop scripts – Withy
Note: this doesn't work on Firefox due to a 7 year old Firefox bug. If you have a Mozilla account, you can vote on this issue to help prioritize it. – Barra
Importing a file locally with @require file:///... didn't work for me. This works: I automatically copy the file to a web server which makes the file available at a certain URL. Then, in the userscript headers, I added @require [the URL], @downloadURL [the URL] and @uploadURL [the URL]. To auto-copy the file I'm using: ls userScript.js | entr scp userScript.js [email protected]:public_html/ (using the entr command from github.com/eradman/entr) – Edelstein
P
5

I want to publish alpha/beta release [...]

You can use the @updateURL userscript tag to point out a web URL [1] and use it along with git to achieve your need.


Here's how I implement it:

  • On my Gitlab instance https://foo.com/user/project/raw/develop/main.user.js points out the raw userscript file of the develop branch.
  • Links to develop and other important feature branches are available on the description of the project so that people can choose to follow a development version instead of the master one[2].
  • and I use this template for sharing:
// ==UserScript==
// @name         New Userscript
// @namespace    foo.com
// @version      0.3
// @description  try to take over the world!
// @author       user
// @match        https://bar.org/*
// @grant        none
// @updateURL    https://foo.com/user/project/raw/develop/main.user.js
// ==/UserScript==

(function() {
    'use strict';

    // Your code here...
})();
  • Then upon triggering Check for userscript updates button on Greasemonkey or Tempermonkey, they will install the script available at this URL.

[1] Accessible one from where you want to install eg. public Github repo from your personal computer, or your companie's private Gitlab instance from your work computer

[2] note that in order to be installable upon a click on the link, the name of the file must ends with .user.js

Pedestal answered 4/6, 2018 at 16:39 Comment(1)
I'm not sure I understand so I want to repeat that back to you: the solution is to set up a local server that hosts your local file then refer to it with http(s)://? – Barra
D
4

Tampermonkey uses something called WebDAV to use an external editor to edit userscripts; TamperDAV.

I haven't tried it yet, but it looks like connecting with Visual Studio should be possible.

Declarative answered 17/6, 2019 at 14:37 Comment(0)
B
2

Extension to Neithan's answer

from time import *  
import pathlib
from pyautogui import *
from glob import *
from pyperclip import *
import re
author='SmartManoj'
repo='SmartUserScripts'
namespace=f'https://github.com/{author}'
def local():
    return f'''// ==UserScript==
// @name        {name}
// @version     0.1
// @description try to take over the world!
// @author      {author}
// @namespace   {namespace}
// @match       {link}
// @updateURL   https://raw.githubusercontent.com/{author}/{repo}/master/{fn}
// ==/UserScript==

'''
def browser():
    return f'''// ==UserScript==
// @name        {name}
// @version     0.1
// @description try to take over the world!
// @author      {author}
// @namespace   {namespace}
// @match       {link}
// @require     {local_path}/{fn}
// @grant       GM_setClipboard
// ==/UserScript==


'''
hotkey('win','3') # chrome index
hotkey('ctrl','shift','h')
fn=prompt('Enter File name')
name=prompt('Enter Script name',default=fn)
sleep(1)
hotkey('ctrl','a')
hotkey('ctrl','x')
local_path=pathlib.Path(__file__).parents[0].as_uri()   
ext='.user.js'
l=len(glob(fn+ext))
if l:fn+=f'_{l+1}'
fn+=ext
a=paste()
link=re.search('@match\s*(.*)',a)[1].strip()
print(local(),file=open(fn,'w'))
copy(browser())
hotkey('ctrl','v')

Latest version

Need to do another script if header changes

Bandore answered 24/5, 2019 at 14:15 Comment(1)
You might not need both @updateURL and @downloadURL – Guberniya
R
1

Trim21 provides, probably the best large-scale UserScript development solution so far, using webpack to cooperate LiveReloadPlugin realizes modular development and automated testing.

Here is the project.

It can use ES5/ES6 and TypeScript to develop modular scripts on IDE. It's really easy to use!

Integrated LiveReloadPlugin, you can directly refresh any @matchURL.

It is better than the previous scheme, which greatly improves the development efficiency of UserScript!


I've answered this in another question; I think someone should merge them. In the meantime, since it's I haven't seen a lot of info on this, I'll put it here for the next person looking for help.

Rosellaroselle answered 19/8, 2020 at 2:3 Comment(0)
B
1

Update 3/30/2023:

This is probably overkill unless you are using Firefox (the other solutions don't work there), but you can setup tamperdav, a project created by the same developer.

A WebDAV-like server to sync Tampermonkey scripts and edit them with an external editor.

WARNING: this is not a 100% WebDAV-compliant server. Many clients should, but don't necessarily have to work!

...

Tampermonkey will sync all scripts into a subfolder of the configured dav-directory i.e. Tampermonkey/sync using it's internally generated UUID for the filename. The actual file to edit can easily be found by running node find_script_in_meta.js --name="My Script Name". Alternatively you can find the the UUID via Tampermonkey's UI by navigating to the script and retrieving it from the address bar.

Seeing as how this could be more or less work than my original post, I consider this more of a workaround than a Firefox solution.


Unfortunately, you can't do this in Firefox, but that issue provides a workaround:

My solution has been using a local nginx server. All my tampermonkey scripts have only the header metadata, like:

// ==UserScript==
// @name         myscriptname
// @namespace    http://tampermonkey.net/
// @version      1.007
// @description  try to take over the world!
// @author       You
// @match        https://www.example.com/*
// @require      http://localhost/userscripts/myscriptname/main.js
// @grant        none
// ==/UserScript==

All the meat and potatoes are in the main.js script that gets @require'd in. Then I have to manually bump the version every time I update the script to break the cache. And I think sometimes reload the "example.com" page in question twice. It's not ideal. I made a scaffolding tool that helps set it up and a deploy script to copy it from my dev folder into nginx, but the deploy script can't update the version above, so I have to bump it manually. Maybe there's a way to host the above metadata from my nginx instance as well. Then my deploy script could auto-increment the version and I could update everything in one go with my deploy script, I guess assuming the metadata caching wasn't a dealbreaker.

Anyway, there's probably a better way.

Barra answered 24/3, 2023 at 0:46 Comment(0)
C
1

just the file:/// doesn't work for me, what i did to get the @require tag's value was drag my .user.js file into chrome, with tampermonkey disabled. This made chrome download the file and so i checked the address from which the file was downloaded. The path for that file started with file:///c%3A/Users/.../<script>.user.js which means that it wants the drive letter in the path as well.

Also, make sure you don't have the @require tag in your editor of choice, since then there could possibly a self-requiring script loop and that doesn't sound fun.

Cloe answered 30/12, 2023 at 18:52 Comment(1)
URL encoding is what you needed. – Cathryncathy
L
0

For Mac users, to add to Neithan's answer, the @required URL needs three slashes! Took me way too long to get it to work.

@required file:///Users/me/STUFF/Code/Scripts/SameWindowHref.user.js
Lawless answered 24/8, 2019 at 13:58 Comment(3)
Hey! I see you are pretty new here, so welcome to this mad house! :) You made a correction on an answer (happens to be mine, but it's all the same) which is always a good thing. More and better info is always good. Things like corrections or small additions, though, are best to leave them as comments (take a look here!) to the answer (or question). Have fun! – Guberniya
Hi Carlos, thank you for your welcome message and your tip :) You're correct with that and i tried it on this way, but i got an error message - something like my reputation is too low to comment your answer. So i solved it with another comment with the link to your answer :) – Lawless
You're so right! I forgot about it. You need 50 points to comment everywhere. Take a look here to know different ways to earn those points. Commenting is useful and earning those first points will also help you familiarize more with this place :) – Guberniya

© 2022 - 2024 β€” McMap. All rights reserved.