lisonge / vite-plugin-monkey

A vite plugin server and build your.user.js for userscript engine like Tampermonkey, Violentmonkey, Greasemonkey, ScriptCat
MIT License
1.31k stars 70 forks source link

库模式用户脚本构建方式 #98

Closed Sec-ant closed 1 year ago

Sec-ant commented 1 year ago

Greasy Fork 上是可以发布当作库来使用的用户脚本的。

我在使用 vite-plugin-monkey 插件并想将代码发布为用户脚本库时,用到了 vite库模式构建。

我遇到的问题是,如果我不指定 build.lib.fileName 或者将 build.lib.fileName 指定为字符串形式的文件名时,UMD 构建结果不能把导出的变量注册到全局作用域。只有将它指定为函数形式时才可以构建出正确的 UMD 包(可以在全局作用域访问导出的变量)。请问这个是正确的库模式用户脚本构建方式吗?

这是我的 vite.config.ts 文件:

import { defineConfig } from 'vite';
import monkey from 'vite-plugin-monkey';

export default defineConfig({
  build: {
    lib: {
      entry: './src/index.ts',
      formats: ['umd'],
      name: 'LIB',
      /**
       * 必须将 fileName 指定为一个函数时
       * UMD 模块才会将导出注册到全局作用域
       */
      fileName: () => 'dummy.js',
      /**
       * 省略 fileName 或者指定为文件名时
       * 全局作用域上没有导出的变量或函数
       */
      // fileName: 'dummy.js',
    },
  },
  plugins: [
    monkey({
      entry: './src/index.ts',
    }),
  ],
});

这里有一个用来复现的 Demo:

https://stackblitz.com/edit/stackblitz-starters-k8kbgm?file=vite.config.ts

lisonge commented 1 year ago

vite-plugin-monkey 不支持 umd 模式,而且为了支持 top level await 和 单文件下的 dynamic import,你的导出会被去掉

image

如果你想把代码构建为 库,你不应该使用 vite-plugin-monkey

另外我并不推荐在 Greasy Fork 发布库,Greasy Fork 上的库 没有类型声明,不能标准化安装

我更推荐同时构建 esm/iife 并发布在 npm,可以通过 pnpm 安装 也能通过 jsdelivr/unpkg 来 require 使用

Sec-ant commented 1 year ago

谢谢你的解答,单纯构建 UMD 和 ESM 是没问题的,这些我也发布了。用 vite-plugin-monkey 一是有现成的类型可以用写起来比较舒服 😄,二是如果要构建库模式的话还要保留 meta 信息也是用 vite-plugin-monkey 方便。我本来的想法是提供尽可能多的构建产物和安装渠道选择,不过既然 vite-plugin-monkey 不会支持这种方式,那我就先保持现状👌。

lisonge commented 1 year ago

这是我正在实现的一个库的构建配置

它构建两种格式 esm/iife ,esm 提供给 vite 使用,iife 可以直接在 @require 引入

两种格式都能在各自的环境里正常访问到 GM API

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 @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',
      };
    },
  },
]);
Sec-ant commented 1 year ago

好的,我还没有用过 tsup,后面的项目试一试

lisonge commented 1 year ago

与 tsup 无关, vite 也是可行的

vite.esm.config.ts 设置 external

  build: {
    rollupOptions: {
      external: [`vite-plugin-monkey/dist/client`],
    },
  },

vite.iife.config.ts 设置

  resolve: {
    alias: {
      'vite-plugin-monkey/dist/client': 'vite-plugin-monkey/dist/native',
    },
  },

即可

Sec-ant commented 1 year ago

与 tsup 无关, vite 也是可行的

vite.esm.config.ts 设置 external

  build: {
    rollupOptions: {
      external: [`vite-plugin-monkey/dist/client`],
    },
  },

vite.iife.config.ts 设置

  resolve: {
    alias: {
      'vite-plugin-monkey/dist/client': 'vite-plugin-monkey/dist/native',
    },
  },

即可

了解了,但是用 external 的话,es 构建产物会存在一句 import { GM_XXX } from "vite-plugin-monkey/dist/client",而由于 vite-plugin-monkeydevDependencies,其他人在安装我的包的时候并不会自动安装 vite-plugin-monkey 依赖,如果对方不使用 vite-plugin-monkey 开发的话但使用其它打包工具的话可能会造成一些问题?当然现在能用 es 模块开发油猴脚本 vite-plugin-monkey 应该是最优选择,不过我并不太想限制用户的选择…

lisonge commented 1 year ago

如果你不想使用本插件,那你把上面 iife 的配置给到 esm 并且把 client 模块打包进去就行

实际上就算你不给也行,也能直接把 client 模块打包进去的,并且正常运行

client 模块 先在 document 上获取 monkeyWindow ,获取不到直接使用 当前作用域的 window 作为 monkeyWindow ,然后使用 monkeyWindow.GM_ 获取 api ,所以就算你使用 webpack/rollup/esbuild 等其它打包工具,代码运行的时候也能正确使用 GM api

你多试几个配置然后看打包代码产物就知道怎么做了

lisonge commented 1 year ago

也就是说,你可以发布 3 种格式

排除 client 依赖的 esm ,给 vite 使用

打包 client 并将 client 替换成 native 的 esm 给 webpack/rollup 等其它工具使用

打包 client 并将 client 替换成 native 的 iife 给 通过 require url 的手写脚本使用

这 3 种格式完全覆盖你的需求

Sec-ant commented 1 year ago

嗯嗯好的,我最后决定 es 把 client 打包进去,iife/umd 用 alias 做替换