5

I am writing a GreaseMonkey plugin in TypeScript. I am compiling main.ts file into main.js, and then put it on GreaseMonkey. The output file format looks like vanilla JavaScript, and GreaseMonkey consumes it happily. Now the project grows larger, I plan to split the code into several files. However, I failed to find a build system that works.

Requirements:

  • It should bundle everything into a single JavaScript file. GreaseMonkey only takes one file.
  • It should generate vanilla JavaScript. I don't think things like System.import works in GreaseMonkey.

Possible behavior

Given

// lib.ts
export function greet(name: string) {
    console.log(`Hello, ${ name }!`);
}

and

// main.ts
import { greet } from './lib.ts';

greet("Tom");

it returns

// main.js
let lib_public = {};
lib_public.greet = function greet(name: string) {
    console.log(`Hello, ${ name }!`);
}
lib_public.greet("Tom");
3

Update: Webpack is all you need! OK, some plugins here and there(webpack, webpack-cli, typescript for obvious reasons, ts-loader for webpack-Typescript interaction/compilation), but here is the recipe, assuming your code is in src folder (Main.ts being the entry point) and you want your lib.user.js in dist folder:

webpack.config.js

// required for path resolution for dist folder
const path = require("path");
// used to access the BannerPlugin
const webpack = require("webpack");
// required for pretty format for the Userscript banner
const stripIndent = require("common-tags").stripIndent;

module.exports = {
    entry: "./src/Main.ts",
    devtool: 'inline-source-map',
    mode: "development",
    module: {
        rules: [
            {
                test: /\.tsx?$/,
                use: 'ts-loader',
                exclude: /node_modules/
            }
        ]
    },
    resolve: {
        extensions: [".ts", ".js"]
    },
    output: {
        path: path.resolve(__dirname, "dist"),
        filename: "lib.user.js"
    },
    plugins: [
        new webpack.BannerPlugin({
            raw: true,
            banner: stripIndent`
                // ==UserScript==
                // @name    Your lib name
                // @namespace   https://path.com/your/lib/namespace
                // @version 1.0.0
                // @author  Your Name
                // @description Your lib description
                // @include https://www.example.com/*
                // @copyright   2019, Your Org
                // ==/UserScript==`
        })
    ]
};

Important bits from package.json:

  "scripts": {
    "build": "webpack"
  },
  "devDependencies": {
    "common-tags": "^1.8.0",
    "ts-loader": "^5.3.3",
    "typescript": "^3.3.1",
    "webpack": "^4.29.3",
    "webpack-cli": "^3.2.3"
  }

Build: npm run build, which will invoke webpack, as instructed in the scripts section of package.json.


Gulp + webpack

tl;dr Compile with gulp, bundle everything with webpack, concat using gulp your GreaseMonkey definitions with the bundle into script.user.js. Even if you specified the order for webpack to bundle together your library and the GreaseMonkey definitions, they would get lost in the middle of the script. webpack has to bootstrap everything required to merge the files together, emulate the module loader and so much more and you absolutely need the definitions as first thing in the final script. This is why is better to keep the definitions separately and merge them with your library at the end of the build process.

  1. Keep your GreaseMonkey definitions (e.g. //==UserScript==, //== @name, //== @match and the like) in UserScript.ts.
  2. Compile everything from src with gulp to a tmp folder. You'll have your library Javascript files in tmp, including the tmp/UserScript.js file.
  3. Bundle everything from tmpto dist with webpack. You'll have dist/Main.js.
  4. Concat the GreaseMonkey definitions with your bundled library from steps 2 and 3 into dist/library.user.js.
  5. Clean-up everything in tmp and dist, except the dist/library.user.js.

I am new to gulp + webpack and I'm not yet aware of their full power and the plugins available, this is what worked for me, hopefully there is a full gulp or full webpack solution, but I don't mind using both.

1

Another option is rollup. It allows compiling into one file (using iife)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.