vite-plugin-monkey

repository·main·Indexed 24 days ago

https://github.com/lisonge/vite-plugin-monkey

A Vite plugin for building userscripts compatible with engines like Tampermonkey, Violentmonkey, Greasemonkey, and ScriptCat. It provides a modern development experience featuring HMR, TypeScript support, and automated management of userscript metadata such as @grant, @require, and @resource. The project includes a scaffolding tool, create-monkey, with templates for frameworks including React, Vue, Svelte, and Solid.

Tokens
18K
Snippets
34
Records
90
Agent score
83%

What's inside vite-plugin-monkey

  1. Overview of vite-plugin-monkey features

    main

    vite-plugin-monkey is a Vite plugin designed to facilitate the development of userscripts for engines like Tampermonkey, Violentmonkey, Greasemonkey, and ScriptCat.

    Key features include:

    • Automatic Header Injection: Automatically injects the required script configuration metadata (userscript header) into the build.
    • Automatic Installation: Automatically opens your default browser to install the script when starting for the first time or when the configuration changes.
    • CDN Optimization: Provides mechanisms via @require and @resource to use external CDNs, reducing the final bundle size.
    • GM_api Support: Allows using GM_api via ESM imports with full TypeScript type hints. It also intelligently collects used GM_api calls to automatically configure the @grant metadata.
    • Modern JS Support: Supports top level await and dynamic import within single files.
    • Developer Experience: Provides Module Hot Replacement (HMR) and instant startup within a Vite environment.
  2. How to preserve state during HMR in Svelte

    main

    Hot Module Replacement (HMR) state preservation is disabled by default in svelte-hmr and @sveltejs/vite-plugin-svelte due to unpredictable behavior.

    If you need to retain important component state during HMR, you should move that state into an external store. This prevents the state from being replaced when the component is reloaded.

    // store.ts
    // An extremely simple external store
    import { writable } from 'svelte/store';
    export default writable(0);
  3. Fix IIFE and UMD dependency issues using dataUrl

    main

    When using iife-cdn, variables declared with var in an IIFE do not become properties of window within the Userscript scope. This causes issues when a UMD library (like element-plus) depends on an IIFE library (like vue).

    To resolve this, append a dataUrl script after the iife-cdn declaration to manually attach the IIFE variable to the window object.

    import { cdn, util } from 'vite-plugin-monkey';
    const buildConfig = {
      vue: cdn
        .jsdelivr('Vue', 'dist/vue.global.prod.js')
        .concat(util.dataUrl(';window.Vue=Vue;')),
      'element-plus': cdn.jsdelivr('ElementPlus', 'dist/index.full.min.js'),
    };
  4. Preserve component state during HMR

    main

    HMR (Hot Module Replacement) state preservation is disabled by default in this template due to unpredictable behavior in svelte-hmr and @sveltejs/vite-plugin-svelte.

    If you need to retain important state within a component during HMR, use an external Svelte store. External stores are not replaced by HMR, allowing the data to persist even when components are re-rendered.

    // store.js
    // An extremely simple external store
    import { writable } from 'svelte/store';
    export default writable(0);
  5. Technical considerations for Svelte + TS + Vite template

    main

    This template is designed as a lightweight starting point for Vite + TypeScript + Svelte projects, focusing on HMR and Intellisense.

    SvelteKit vs. this template

    Use this template instead of SvelteKit if you want a standard Vite app rather than a framework-first environment. SvelteKit uses its own routing and specialized build processes where standard vite dev or vite build commands may not work as expected. This template is structured to allow for easy migration to SvelteKit if extended capabilities are needed later.

    TypeScript Configuration

    • Type Definitions: The template uses global.d.ts with triple-slash references instead of compilerOptions.types in tsconfig.json. This ensures svelte and vite/client type information is added while still allowing TypeScript to accept type information from the entire workspace.
    • JavaScript Interop: allowJs is enabled in the TypeScript configuration. This allows for mixed codebases and ensures that JavaScript syntax within .svelte files is handled correctly without compromising typechecking quality.
  6. How to build a library compatible with GM_api

    main

    To build a library that can be used by vite-plugin-monkey users via npm, follow these steps:

    1. Import GM_api from the client: In your library code, import GM_api from vite-plugin-monkey/dist/client.
    2. Exclude the client during build: When building your library (e.g., using tsup), exclude vite-plugin-monkey/dist/client from the bundle so users can provide their own implementation.
    3. Provide an IIFE fallback: To support users who want to use your library via @require, build an IIFE version where you alias vite-plugin-monkey/dist/client to vite-plugin-monkey/dist/native. This ensures the library uses the native implementation when bundled as an IIFE.

    This approach allows your library to be used seamlessly both as an npm dependency in a Vite project and as a standalone script via @require.

    // /src/index.ts
    import { GM_setValue } from 'vite-plugin-monkey/dist/client';
    
    export const setValue = (name: string, value: unknown) => {
      console.log('you invoke setValue', name, value);
      GM_setValue(name, value);
    };
    // tsup.config.ts
    import { defineConfig } from 'tsup';
    
    const outExtension = (ctx: { format: 'esm' | 'cjs' | 'iife' }) => ({
      js: { esm: '.mjs', cjs: '.cjs', iife: '.iife.js' }[ctx.format],
    });
    
    export default defineConfig([
      {
        // for vite import
        entry: ['src/index.ts'],
        outDir: 'dist',
        sourcemap: true,
        platform: 'browser',
        outExtension,
        dts: true,
        format: ['esm'],
        external: ['vite-plugin-monkey/dist/client'],
      },
      {
        // for userscript @require
        entry: ['src/index.ts'],
        outDir: 'dist',
        sourcemap: true,
        platform: 'browser',
        outExtension,
        dts: false,
        format: ['iife'],
        minify: true,
        globalName: `GmExtra`,
        target: 'es2015',
        esbuildOptions: (options) => {
          options.alias = {
            'vite-plugin-monkey/dist/client': 'vite-plugin-monkey/dist/native',
          };
        },
      },
    ]);
  7. Auto-import GM_api using unplugin-auto-import

    main

    To avoid manual imports while maintaining best practices, you can use unplugin-auto-import with the util.unimportPreset provided by vite-plugin-monkey.

    // vite.config.ts
    import { defineConfig } from 'vite';
    import monkey, { util } from 'vite-plugin-monkey';
    import AutoImport from 'unplugin-auto-import/vite';
    
    export default defineConfig({
      plugins: [
        AutoImport({
          imports: [
            util.unimportPreset,
          ],
        }),
        monkey({
          // ...
        }),
      ],
    });
    
    // main.ts
    // GM_api is automatically imported
    console.log({ GM_cookie, unsafeWindow, monkeyWindow, GM_addElement });