UNPKG

vite-plugin-monkey

Version:

A vite plugin server and build your.user.js for userscript engine like Tampermonkey and Violentmonkey and Greasemonkey

1,742 lines (1,724 loc) 72.7 kB
var __defProp = Object.defineProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; // src/node/_logger.ts import pc from "picocolors"; var log = (tag, message, options) => { console.log( [ ((options == null ? void 0 : options.time) ?? false) === true ? pc.dim((/* @__PURE__ */ new Date()).toLocaleTimeString()) : "", pc.bold(pc.blue(`[${tag}]`)), message ].filter((s) => s).join(" ") ); }; var info = (tag, message, options) => { log(tag, pc.white(message), options); }; var warn = (tag, message, options) => { log(tag, pc.yellow(message), options); }; var error = (tag, message, options) => { log(tag, pc.red(message), options); }; var createLogger = (tag) => ({ info: async (message, options) => { info(tag, message, options); }, warn: async (message, options) => { warn(tag, message, options); }, error: async (message, options) => { error(tag, message, options); } }); var logger = createLogger("plugin-monkey"); // src/node/_util.ts import * as acornWalk from "acorn-walk"; import { resolve } from "import-meta-resolve"; import { readFileSync } from "fs"; import fs from "fs/promises"; import path from "path"; import { pathToFileURL } from "url"; import { normalizePath, transformWithEsbuild } from "vite"; // src/node/gm_api.ts var gmIdentifiers = [ "GM_addElement", "GM_addStyle", "GM_addValueChangeListener", "GM_cookie", "GM_deleteValue", "GM_deleteValues", "GM_download", "GM_getResourceText", "GM_getResourceURL", "GM_getTab", "GM_getTabs", "GM_getValue", "GM_getValues", "GM_info", "GM_listValues", "GM_log", "GM_notification", "GM_openInTab", "GM_registerMenuCommand", "GM_removeValueChangeListener", "GM_saveTab", "GM_setClipboard", "GM_setValue", "GM_setValues", "GM_unregisterMenuCommand", "GM_webRequest", "GM_xmlhttpRequest" ]; var gmMembers = [ "GM.addElement", "GM.addStyle", "GM.addValueChangeListener", "GM.cookie", "GM.deleteValue", "GM.deleteValues", "GM.download", "GM.getResourceText", // https://www.tampermonkey.net/documentation.php#api:GM_getResourceURL "GM.getResourceUrl", "GM.getTab", "GM.getTabs", "GM.getValue", "GM.getValues", "GM.info", "GM.listValues", "GM.log", "GM.notification", "GM.openInTab", "GM.registerMenuCommand", "GM.removeValueChangeListener", "GM.saveTab", "GM.setClipboard", "GM.setValue", "GM.setValues", "GM.unregisterMenuCommand", "GM.webRequest", "GM.xmlHttpRequest" ]; var othersGrantNames = [ "unsafeWindow", "window.close", "window.focus", "window.onurlchange" ]; var grantNames = [...gmMembers, ...gmIdentifiers, ...othersGrantNames]; // src/node/_util.ts var get_vite_start_time = () => { const n = Reflect.get(globalThis, "__vite_start_time") ?? 0; if (typeof n != "number") { return 0; } else { return n; } }; var isFirstBoot = (n = 1e3) => get_vite_start_time() < n; var projectPkg = (() => { var _a, _b, _c, _d, _e; let rawTarget = {}; try { rawTarget = JSON.parse( readFileSync(path.resolve(process.cwd(), "package.json"), "utf-8") ); } catch { rawTarget = {}; } const target = { name: "monkey" }; Object.entries(rawTarget).forEach(([k, v]) => { if (typeof v == "string") { Reflect.set(target, k, v); } }); if (rawTarget.author instanceof Object && typeof ((_a = rawTarget.author) == null ? void 0 : _a.name) == "string") { target.author = (_b = rawTarget.author) == null ? void 0 : _b.name; } if (rawTarget.bugs instanceof Object && typeof ((_c = rawTarget.bugs) == null ? void 0 : _c.url) == "string") { target.bugs = (_d = rawTarget.bugs) == null ? void 0 : _d.url; } if (rawTarget.repository instanceof Object && typeof ((_e = rawTarget.repository) == null ? void 0 : _e.url) == "string") { const { url } = rawTarget.repository; if (url.startsWith("http")) { target.repository = url; } else if (url.startsWith("git+http")) { target.repository = url.slice(4); } } return target; })(); var compatResolve = (id) => { return resolve(id, pathToFileURL(process.cwd() + "/any.js").href); }; var existFile = async (path5) => { try { return (await fs.stat(path5)).isFile(); } catch { return false; } }; var resolveModuleFromPath = async (subpath) => { const p = normalizePath(process.cwd()).split("/"); for (let i = p.length; i > 0; i--) { const p2 = `${p.slice(0, i).join("/")}/node_modules/${subpath}`; if (await existFile(p2)) { return p2; } } }; var compatResolveModulePath = async (id) => { try { return compatResolve(id); } catch (e) { const r = await resolveModuleFromPath(id); if (!r) { throw e; } return r; } }; var isScopePkg = (name) => name.startsWith("@"); var getModuleRealInfo = async (importName) => { const nameNoQuery = normalizePath(importName.split("?")[0]); const resolveName = await (async () => { const n = normalizePath(await compatResolveModulePath(nameNoQuery)).replace( /.*\/node_modules\/[^/]+\//, "" ); if (isScopePkg(importName)) { return n.split("/").slice(1).join("/"); } return n; })(); let version = void 0; const nameList = nameNoQuery.split("/"); let pkgName = nameNoQuery; while (nameList.length > 0) { pkgName = nameList.join("/"); const filePath = await (async () => { const p = await resolveModuleFromPath(`${pkgName}/package.json`); if (p) { return p; } try { return compatResolve(`${pkgName}/package.json`); } catch { return void 0; } })(); if (filePath === void 0 || !await existFile(filePath)) { nameList.pop(); continue; } const modulePack = JSON.parse( await fs.readFile(filePath, "utf-8") ); version = modulePack.version; break; } if (version === void 0) { logger.warn( `not found module ${nameNoQuery} version, use ${nameNoQuery}@latest` ); pkgName = nameNoQuery; version = "latest"; } return { version, name: pkgName, resolveName }; }; var miniCode = async (code, type = "js") => { return (await transformWithEsbuild(code, "any_name." + type, { minify: true, sourcemap: false, legalComments: "none" })).code.trimEnd(); }; var toValidURL = (url) => { if (typeof url != "string") return; try { return new URL(url); } catch { } }; var moduleExportExpressionWrapper = (expression) => { let n = 0; let identifier = ``; while (expression.includes(identifier)) { identifier = `_${(n || ``).toString(16)}`; n++; } return `(()=>{const ${identifier}=${expression};('default' in ${identifier})||(${identifier}.default=${identifier});return ${identifier}})()`; }; var cyrb53hash = (str = ``, seed = 0) => { let h1 = 3735928559 ^ seed, h2 = 1103547991 ^ seed; for (let i = 0, ch; i < str.length; i++) { ch = str.charCodeAt(i); h1 = Math.imul(h1 ^ ch, 2654435761); h2 = Math.imul(h2 ^ ch, 1597334677); } h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507); h1 ^= Math.imul(h2 ^ h2 >>> 13, 3266489909); h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507); h2 ^= Math.imul(h1 ^ h1 >>> 13, 3266489909); return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(36).substring(0, 8); }; async function* walk(dirPath) { const pathnames = (await fs.readdir(dirPath)).map( (s) => path.join(dirPath, s) ); while (pathnames.length > 0) { const pathname = pathnames.pop(); const state = await fs.lstat(pathname); if (state.isFile()) { yield pathname; } else if (state.isDirectory()) { pathnames.push( ...(await fs.readdir(pathname)).map((s) => path.join(pathname, s)) ); } } } var collectGrant = (context, chunks, injectCssCode, minify) => { const codes = /* @__PURE__ */ new Set(); if (injectCssCode) { codes.add(injectCssCode); } for (const chunk of chunks) { if (minify) { const modules = Object.values(chunk.modules); modules.forEach((m) => { const code = m.code; if (code) { codes.add(code); } }); } codes.add(chunk.code); } const unusedMembers = new Set( grantNames.filter((s) => s.includes(`.`)) ); const endsWithWin = (a, b) => { if (a.endsWith(b)) { return a === "monkeyWindow." + b || a === "_monkeyWindow." + b; } return false; }; const memberHandleMap = Object.fromEntries( grantNames.filter((s) => s.startsWith("window.")).map((name) => [name, (v) => endsWithWin(v, name.split(".")[1])]) ); const unusedIdentifiers = new Set( grantNames.filter((s) => !s.includes(`.`)) ); const usedGm = /* @__PURE__ */ new Set(); const matchIdentifier = (name) => { if (unusedIdentifiers.has(name)) { usedGm.add(name); unusedIdentifiers.delete(name); return true; } return false; }; const matchMember = (name) => { var _a; for (const unusedName of unusedMembers.values()) { if (name.endsWith(unusedName) || ((_a = memberHandleMap[unusedName]) == null ? void 0 : _a.call(memberHandleMap, name))) { usedGm.add(unusedName); unusedMembers.delete(unusedName); return true; } } return false; }; for (const code of codes) { if (!code.trim()) continue; const ast = context.parse(code); acornWalk.simple( ast, { MemberExpression(node) { if (unusedMembers.size === 0) return; if (node.computed || node.object.type !== "Identifier" || node.property.type !== "Identifier") { return; } if (node.object.name === "monkeyWindow" || node.object.name === "_monkeyWindow") { if (matchIdentifier(node.property.name)) { return; } } const name = node.object.name + "." + node.property.name; matchMember(name); }, Identifier(node) { matchIdentifier(node.name); } }, { ...acornWalk.base } ); if (unusedMembers.size == 0 && unusedIdentifiers.size == 0) { break; } } return usedGm; }; var getInjectCssCode = async (finalOption, rawBundle) => { const cssTexts = []; Object.entries(rawBundle).forEach(([k, v]) => { if (v.type == "asset" && k.endsWith(".css")) { cssTexts.push(v.source.toString()); delete rawBundle[k]; } }); const css = cssTexts.join("").trim(); if (css) { return await finalOption.cssSideEffects(` ` + css + ` `); } }; // src/node/cdn.ts var cdn_exports = {}; __export(cdn_exports, { baomitu: () => baomitu, bdstatic: () => bdstatic, bootcdn: () => bootcdn, bytecdntp: () => bytecdntp, cdnjs: () => cdnjs, elemecdn: () => elemecdn, jsdelivr: () => jsdelivr, jsdelivrFastly: () => jsdelivrFastly, npmmirror: () => npmmirror, staticfile: () => staticfile, unpkg: () => unpkg, zhimg: () => zhimg }); var jsdelivr = (exportVarName = "", pathname = "") => { return [ exportVarName, (version, name, _importName = "", resolveName = "") => { const p = pathname || resolveName; if (p) { return `https://cdn.jsdelivr.net/npm/${name}@${version}/${p}`; } else { return `https://cdn.jsdelivr.net/npm/${name}@${version}`; } } ]; }; var jsdelivrFastly = (exportVarName = "", pathname = "") => { return [ exportVarName, (version, name, _importName = "", resolveName = "") => { const p = pathname || resolveName; if (p) { return `https://fastly.jsdelivr.net/npm/${name}@${version}/${p}`; } else { return `https://fastly.jsdelivr.net/npm/${name}@${version}`; } } ]; }; var unpkg = (exportVarName = "", pathname = "") => { return [ exportVarName, (version, name, _importName = "", resolveName = "") => { const p = pathname || resolveName; if (p) { return `https://unpkg.com/${name}@${version}/${p}`; } else { return `https://unpkg.com/${name}@${version}`; } } ]; }; var bytecdntp = (exportVarName = "", pathname = "") => { return [ exportVarName, (version, name, _importName = "", resolveName = "") => { const p = pathname || resolveName; return `https://lf9-cdn-tos.bytecdntp.com/cdn/expire-10-y/${name}/${version}/${p}`; } ]; }; var bootcdn = (exportVarName = "", pathname = "") => { console.warn( "bootcdn will return virus-infected code. Please stop using it and switch to other sources. now it will return jsdelivr url." ); return [ exportVarName, (version, name, _importName = "", resolveName = "") => { const p = pathname || resolveName; return `https://fastly.jsdelivr.net/npm/${name}@${version}/${p}`; } ]; }; var baomitu = (exportVarName = "", pathname = "") => { return [ exportVarName, (version, name, _importName = "", resolveName = "") => { const p = pathname || resolveName; return `https://lib.baomitu.com/${name}/${version}/${p}`; } ]; }; var staticfile = (exportVarName = "", pathname = "") => { console.warn( "staticfile will return virus-infected code. Please stop using it and switch to other sources. now it will return jsdelivr url." ); return [ exportVarName, (version, name, _importName = "", resolveName = "") => { const p = pathname || resolveName; return `https://fastly.jsdelivr.net/npm/${name}@${version}/${p}`; } ]; }; var cdnjs = (exportVarName = "", pathname = "") => { return [ exportVarName, (version, name, _importName = "", resolveName = "") => { const p = pathname || resolveName; return `https://cdnjs.cloudflare.com/ajax/libs/${name}/${version}/${p}`; } ]; }; var zhimg = (exportVarName = "", pathname = "") => { return [ exportVarName, (version, name, _importName = "", resolveName = "") => { const p = pathname || resolveName; return `https://unpkg.zhimg.com/${name}@${version}/${p}`; } ]; }; var elemecdn = (exportVarName = "", pathname = "") => { return [ exportVarName, (version, name, _importName = "", resolveName = "") => { const p = pathname || resolveName; return `https://npm.elemecdn.com/${name}@${version}/${p}`; } ]; }; var bdstatic = (exportVarName = "", pathname = "") => { return [ exportVarName, (version, name, _importName = "", resolveName = "") => { const p = pathname || resolveName; return `https://code.bdstatic.com/npm/${name}@${version}/${p}`; } ]; }; var npmmirror = (exportVarName = "", pathname = "") => { return [ exportVarName, (version, name, _importName = "", resolveName = "") => { const p = pathname || resolveName; if (p) { return `https://registry.npmmirror.com/${name}/${version}/files/${p}`; } else { return `https://registry.npmmirror.com/${name}/${version}/files`; } } ]; }; // src/node/inject_template.ts var fn2string = (fn, ...args) => { return `;(${fn})(...${JSON.stringify(args, void 0, 2)});`; }; var htmlText = ( /* html */ ` <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <link rel="icon" type="image/svg+xml" href="https://vitejs.dev/logo.svg" /> <title>Vite</title> </head> <script type="module" data-source="vite-plugin-monkey"> __CODE__ </script> </html> `.trimStart() ); var fcToHtml = (fn, ...args) => { return htmlText.replace( `__CODE__`, `;(${fn})(...${JSON.stringify(args, void 0, 2)});` ); }; var serverInjectFn = ({ entrySrc }) => { window.GM; const key = `__monkeyWindow-` + new URL(entrySrc).origin; document[key] = window; console.log(`[vite-plugin-monkey] mount monkeyWindow to document`); if (typeof GM_addElement === "function") { GM_addElement(document.head, "script", { type: "module", src: entrySrc }); } else { const script = document.createElement("script"); script.type = "module"; if (window.trustedTypes) { const policy = window.trustedTypes.createPolicy(key, { createScriptURL: (input) => input }); const trustedScriptURL = policy.createScriptURL(entrySrc); script.src = trustedScriptURL; } else { script.src = entrySrc; } document.head.append(script); } console.log(`[vite-plugin-monkey] mount entry module to document.head`); }; var mountGmApiFn = (meta, apiNames = []) => { const key = `__monkeyWindow-` + new URL(meta.url).origin; const monkeyWindow = document[key]; if (!monkeyWindow) { console.log(`[vite-plugin-monkey] not found monkeyWindow`); return; } window.unsafeWindow = window; console.log(`[vite-plugin-monkey] mount unsafeWindow to unsafeWindow`); apiNames.push("GM"); let mountedApiSize = 0; apiNames.forEach((apiName) => { const fn = monkeyWindow[apiName]; if (fn) { window[apiName] = monkeyWindow[apiName]; mountedApiSize++; } }); console.log( `[vite-plugin-monkey] mount ${mountedApiSize}/${apiNames.length} GM api to unsafeWindow` ); }; var virtualHtmlTemplate = async (url) => { const delay = (n = 0) => new Promise((res) => setTimeout(res, n)); await delay(); const u = new URL(url, location.origin); u.searchParams.set("origin", u.origin); if (window == window.parent) { location.href = u.href; await delay(500); window.close(); return; } const style = document.createElement("style"); document.head.append(style); style.innerText = /* css */ ` body { font-family: Arial, sans-serif; margin: 0; } .App { margin: 25px; } p { font-size: 1.5em; } a { color: blue; text-decoration: none; font-size: 1.5em; } a:hover { text-decoration: underline; } `.trim(); document.body.innerHTML = /* html */ ` <div class="App"> <h1>PREVIEW PAGE</h1> <p>Click the links below to install userscripts:</p> <a target="_blank"></a></th> </div> `.trim(); await delay(); const a = document.querySelector("a"); a.href = location.href; a.text = location.href; }; var previewTemplate = async (urls) => { const delay = (n = 0) => new Promise((res) => setTimeout(res, n)); await delay(); const style = document.createElement("style"); document.head.append(style); style.innerText = /* css */ ` body { font-family: Arial, sans-serif; margin: 0; } .App { margin: 25px; } p { font-size: 1.5em; } table { width: 100%; border-collapse: collapse; font-size: 1.5em; } th, td { border: 1px solid black; padding: 8px; text-align: left; } th { background-color: #f2f2f2; } a { color: blue; text-decoration: none; } a:hover { text-decoration: underline; } `.trim(); if (window == window.parent && urls.length == 1) { const u = new URL(urls[0], location.origin); location.href = u.href; await delay(500); window.close(); return; } else if (urls.length == 0) { document.body.innerHTML = /* html */ ` <div class="App"> <h1> There is no script to install </h1> </div> `.trim(); return; } else { document.body.innerHTML = /* html */ ` <div class="App"> <h1>PREVIEW PAGE</h1> <p>Click the links below to install userscripts:</p> <table> <tr> <th>No.</th> <th>Install Link</th> </tr> </table> </div> `.trim(); await delay(); const table = document.querySelector(`table`); urls.sort().forEach((u, index) => { const tr = document.createElement("tr"); const td1 = document.createElement("td"); const td2 = document.createElement("td"); const a = document.createElement("a"); td1.innerText = `${index + 1}`; if (window != window.parent) { a.target = "_blank"; } a.href = u; a.textContent = new URL(u, location.origin).href; td2.append(a); tr.append(td1); tr.append(td2); table.append(tr); }); } }; // src/node/option.ts var resolvedOption = (pluginOption) => { var _a, _b; const build2 = pluginOption.build ?? {}; if (build2.minifyCss !== void 0) { logger.warn( `monkeyConfig.build.minifyCss is deprecated, use viteConfig.build.cssMinify in vite>=4.2.0`, { time: false } ); } const { externalResource = {} } = build2; const externalResource2 = {}; for (const [k, v] of Object.entries(externalResource)) { if (typeof v == "string") { externalResource2[k] = { resourceName: () => k, resourceUrl: () => v }; } else if (typeof v == "function") { externalResource2[k] = { resourceName: () => k, resourceUrl: v }; } else if (v instanceof Array) { let resourceUrl2; let resourceName2 = () => k; const [resourceName, resourceUrl] = v; if (typeof resourceName == "string") { resourceName2 = (pkg) => resourceName || pkg.importName; } else { resourceName2 = (pkg) => resourceName(pkg.version, pkg.name, pkg.importName, pkg.resolveName); } if (typeof resourceUrl == "string") { resourceUrl2 = () => resourceUrl; } else { resourceUrl2 = (pkg) => resourceUrl(pkg.version, pkg.name, pkg.importName, pkg.resolveName); } externalResource2[k] = { resourceName: resourceName2, resourceUrl: resourceUrl2 }; } else { const { resourceUrl, loader, nodeLoader, resourceName } = v; let resourceUrl2; let resourceName2 = () => k; let nodeLoader2 = void 0; if (typeof resourceUrl == "string") { resourceUrl2 = () => resourceUrl; } else { resourceUrl2 = resourceUrl; } if (typeof resourceName == "string") { resourceName2 = () => resourceName; } else if (typeof resourceName == "function") { resourceName2 = resourceName; } if (typeof nodeLoader == "function") { nodeLoader2 = nodeLoader; } else if (typeof nodeLoader == "string") { nodeLoader2 = () => nodeLoader; } externalResource2[k] = { resourceName: resourceName2, resourceUrl: resourceUrl2, loader, nodeLoader: nodeLoader2 }; } } const server = pluginOption.server ?? {}; const { prefix } = server; let prefix2 = (s) => "server:" + s; if (typeof prefix == "function") { prefix2 = prefix; } else if (typeof prefix == "string") { prefix2 = () => prefix; } else if (prefix === false) { prefix2 = (name2) => name2; } const externalGlobals2 = (build2 == null ? void 0 : build2.externalGlobals) ?? {}; const externalGlobals = []; if (externalGlobals2 instanceof Array) { externalGlobals2.forEach((s) => externalGlobals.push(s)); } else { Object.entries(externalGlobals2).forEach((s) => externalGlobals.push(s)); } const { grant = [], $extra = [] } = pluginOption.userscript ?? {}; let { name = {}, description = {}, "exclude-match": excludeMatch = [], match = [], exclude = [], include = [], antifeature = [], require: require2 = [], connect = [], webRequest = [] } = pluginOption.userscript ?? {}; if (typeof name == "string") { name = { "": name }; } else if (!("" in name)) { name = { "": projectPkg.name, ...name }; } if (typeof description == "string") { description = { "": description }; } else if (!("" in description) && projectPkg.description) { description = { "": projectPkg.description, ...description }; } if (!(excludeMatch instanceof Array)) { excludeMatch = [excludeMatch]; } if (!(match instanceof Array)) { match = [match]; } if (!(exclude instanceof Array)) { exclude = [exclude]; } if (!(include instanceof Array)) { include = [include]; } if (!(antifeature instanceof Array)) { antifeature = [antifeature]; } if (!(require2 instanceof Array)) { require2 = [require2]; } if (!(connect instanceof Array)) { connect = [connect]; } if (!(webRequest instanceof Array)) { webRequest = [webRequest]; } const grantSet = /* @__PURE__ */ new Set(); if (typeof grant == "string") { grantSet.add(grant); } else if (grant instanceof Array) { grant.forEach((s) => grantSet.add(s)); } const extra = []; ($extra instanceof Array ? $extra : Object.entries($extra)).forEach( ([k, v]) => { extra.push([k, ...v instanceof Array ? v : [v]]); } ); const { icon64, icon64URL, icon, iconURL, namespace, version = projectPkg.version, author = projectPkg.author, copyright, downloadURL, defaulticon, contributionURL, updateURL, supportURL = projectPkg.bugs, homepageURL = projectPkg.homepage, homepage = projectPkg.homepage, website, license = projectPkg.license, incompatible, source = projectPkg.repository, resource = {}, noframes = false, "run-at": runAt, "inject-into": injectInto, contributionAmount, compatible, sandbox, tag, unwrap = false } = pluginOption.userscript ?? {}; const { fileName = projectPkg.name + ".user.js" } = build2; let { metaFileName } = build2; if (typeof metaFileName == "string") { const t = metaFileName; metaFileName = () => t; } else if (metaFileName === true) { metaFileName = () => fileName.replace(/\.user\.js$/, ".meta.js"); } else if (metaFileName === false) { metaFileName = void 0; } const metaFileFc = metaFileName; const cssSideEffects = build2.cssSideEffects || (() => { return (e) => { if (typeof GM_addStyle == "function") { GM_addStyle(e); return; } const o = document.createElement("style"); o.textContent = e; document.head.append(o); }; }); const config = { userscript: { name, namespace, version, icon64, icon64URL, icon, iconURL, author, copyright, downloadURL, defaulticon, contributionURL, updateURL, supportURL, homepageURL, homepage, website, license, incompatible, source, resource, noframes, "run-at": runAt, "inject-into": injectInto, contributionAmount, compatible, "exclude-match": excludeMatch.map((s) => String(s)), match: match.map((s) => String(s)), include: include.map((s) => String(s)), exclude: exclude.map((s) => String(s)), antifeature, require: require2, connect, description, $extra: extra, grant: grantSet, sandbox, tag: tag ? tag instanceof Array ? tag : [tag] : [], unwrap, webRequest: webRequest.map((w) => JSON.stringify(w)) }, clientAlias: pluginOption.clientAlias ?? "$", entry: pluginOption.entry, format: { align: ((_a = pluginOption.format) == null ? void 0 : _a.align) ?? 2, generate: ((_b = pluginOption.format) == null ? void 0 : _b.generate) ?? ((o) => o.userscript) }, server: { mountGmApi: server.mountGmApi ?? false, open: server.open ?? (process.platform == "win32" || process.platform == "darwin"), prefix: prefix2 }, build: { fileName, metaFileName: metaFileFc ? () => metaFileFc(fileName) : void 0, autoGrant: build2.autoGrant ?? true, externalGlobals, externalResource: externalResource2 }, collectRequireUrls: [], collectResource: {}, globalsPkg2VarName: {}, requirePkgList: [], systemjs: build2.systemjs ?? jsdelivr()[1], cssSideEffects: async (css) => { const codeOrFc = await cssSideEffects(css); if (typeof codeOrFc == "string") { return codeOrFc; } return miniCode(fn2string(codeOrFc, css), "js"); } }; return config; }; // src/node/plugins/server.ts import { DomUtils, ElementType, parseDocument } from "htmlparser2"; import fs2 from "fs/promises"; import path3 from "path"; import { normalizePath as normalizePath2 } from "vite"; // src/node/open_browser.ts import spawn from "cross-spawn"; import { execSync } from "child_process"; import path2 from "path"; import colors from "picocolors"; var OSX_CHROME = "google chrome"; function openBrowser(url, opt, logger2) { const browser = typeof opt === "string" ? opt : process.env.BROWSER || ""; if (browser.toLowerCase().endsWith(".js")) { return executeNodeScript(browser, url, logger2); } else if (browser.toLowerCase() !== "none") { return startBrowserProcess(browser, url); } return false; } function executeNodeScript(scriptPath, url, logger2) { const extraArgs = process.argv.slice(2); const child = spawn(process.execPath, [scriptPath, ...extraArgs, url], { stdio: "inherit" }); child.on("close", (code) => { if (code !== 0) { logger2.error( colors.red( ` The script specified as BROWSER environment variable failed. ${colors.cyan( scriptPath )} exited with code ${code}.` ), { time: true } ); } }); return true; } function startBrowserProcess(browser, url) { const shouldTryOpenChromeWithAppleScript = process.platform === "darwin" && (browser === "" || browser === OSX_CHROME); if (shouldTryOpenChromeWithAppleScript) { try { execSync('ps cax | grep "Google Chrome"'); execSync('osascript openChrome.applescript "' + encodeURI(url) + '"', { cwd: path2.dirname(compatResolve("vite/bin/openChrome.applescript")), stdio: "ignore" }); return true; } catch { } } if (process.platform === "darwin" && browser === "open") { browser = void 0; } try { const options = browser ? { app: { name: browser } } : {}; import("open").then(({ default: open }) => { open(url, options).catch(() => { }); }); return true; } catch { return false; } } // src/node/userscript/index.ts var finalMonkeyOptionToComment = async ({ userscript, format, collectRequireUrls, collectResource }, collectGrantSet, mode) => { let attrList = []; const { name, namespace, version, author, description, license, copyright, icon, iconURL, icon64, icon64URL, defaulticon, homepage, homepageURL, website, source, supportURL, downloadURL, updateURL, include, match, exclude, require: require2, "exclude-match": excludeMatch, "inject-into": injectInto, "run-at": runAt, compatible, incompatible, antifeature, contributionAmount, contributionURL, connect, sandbox, tag, resource, grant, noframes, unwrap, webRequest, $extra } = userscript; Object.entries({ namespace, version, author, license, copyright, icon, iconURL, icon64, icon64URL, defaulticon, homepage, homepageURL, website, source, supportURL, downloadURL, updateURL, "inject-into": injectInto, "run-at": runAt, compatible, incompatible, contributionAmount, contributionURL, sandbox }).forEach(([k, v]) => { if (typeof v == "string") { attrList.push([k, v]); } }); Object.entries(name).forEach(([k, v]) => { if (k == "") { attrList.push(["name", v]); } else { attrList.push(["name:" + k, v]); } }); Object.entries(description).forEach(([k, v]) => { if (k == "") { attrList.push(["description", v]); } else { attrList.push(["description:" + k, v]); } }); Object.entries({ include, match, exclude, "exclude-match": excludeMatch }).forEach(([k, v]) => { v.forEach((v2) => { attrList.push([k, v2]); }); }); [...require2, ...collectRequireUrls].forEach((s) => { attrList.push(["require", s]); }); Object.entries({ ...resource, ...collectResource }).forEach(([k, v]) => { attrList.push(["resource", k, v]); }); connect.forEach((s) => { attrList.push(["connect", s]); }); tag.forEach((s) => { attrList.push(["tag", s]); }); webRequest.forEach((s) => { attrList.push(["webRequest", s]); }); if (grant.has("none")) { attrList.push(["grant", "none"]); } else if (grant.has("*")) { grantNames.forEach((s) => { attrList.push(["grant", s]); }); } else { (/* @__PURE__ */ new Set([...Array.from(collectGrantSet.values()).flat(), ...grant])).forEach( (s) => { if (!s.trim()) return; attrList.push(["grant", s]); } ); } antifeature.forEach(({ description: description2, type, tag: tag2 }) => { attrList.push([ tag2 ? `antifeature:${tag2}` : "antifeature", type, description2 ]); }); if (noframes) { attrList.push(["noframes"]); } if (unwrap) { attrList.push(["unwrap"]); } attrList.push(...$extra); attrList = defaultSortFormat(attrList); let { align = 2 } = format; if (align === true) { align = 2; } if (typeof align == "number" && Number.isInteger(align) && align >= 1) { const alignN = align; const formatKey = (subAttrList) => { if (subAttrList.length == 0) return; const maxLen2 = Math.max(...subAttrList.map((s) => s[1].length)); subAttrList.forEach((s) => { s[1] = s[1].padEnd(alignN + maxLen2); }); }; formatKey(attrList.filter((s) => s[0] == "resource")); formatKey( attrList.filter( (s) => s[0] == "antifeature" || s[0].startsWith("antifeature:") ) ); const maxLen = Math.max(...attrList.map((s) => s[0].length)); attrList.forEach((s) => { s[0] = s[0].padEnd(alignN + maxLen); }); } else if (typeof align == "function") { attrList = await align(attrList); } const uString = [ "==UserScript==", ...attrList.map( (attr) => "@" + attr.map((v) => { return v.endsWith(" ") ? v : v + " "; }).join("").trimEnd() ), "==/UserScript==" ].map((s) => "// " + s).join("\n"); return format.generate({ userscript: uString, mode }); }; var stringSort = (a, b) => { const minLen = Math.min(a.length, b.length); for (let i = 0; i < minLen; i++) { if (a[i] > b[i]) { return 1; } else if (a[i] < b[i]) { return -1; } } if (a.length > b.length) { return 1; } else if (a.length < b.length) { return -1; } return 0; }; var defaultSortFormat = (p0) => { const filter = (predicate) => { const notMatchList = []; const matchList = []; p0.forEach((value, index) => { if (!predicate(value, index)) { notMatchList.push(value); } else { matchList.push(value); } }); p0 = notMatchList; return matchList; }; return [ filter(([k]) => k == "name"), filter(([k]) => k.startsWith("name:")), filter(([k]) => k == "namespace"), filter(([k]) => k == "version"), filter(([k]) => k == "author"), filter(([k]) => k == "description"), filter(([k]) => k.startsWith("description:")), filter(([k]) => k == "license"), filter(([k]) => k == "copyright"), filter(([k]) => k == "icon"), filter(([k]) => k == "iconURL"), filter(([k]) => k == "icon64"), filter(([k]) => k == "icon64URL"), filter(([k]) => k == "defaulticon"), filter(([k]) => k == "homepage"), filter(([k]) => k == "homepageURL"), filter(([k]) => k == "website"), filter(([k]) => k == "source"), filter(([k]) => k == "supportURL"), filter(([k]) => k == "downloadURL"), filter(([k]) => k == "updateURL"), filter(([k]) => k == "include"), filter(([k]) => k == "match"), filter(([k]) => k == "exclude"), filter(([k]) => k == "exclude-match"), filter(([k]) => k == "webRequest"), filter(([k]) => k == "require"), filter(([k]) => k == "resource").sort(stringSort), filter(([k]) => k == "sandbox"), filter(([k]) => k == "tag"), filter(([k]) => k == "connect"), filter(([k]) => k == "grant").sort(stringSort), filter(([k]) => k == "inject-into"), filter(([k]) => k == "run-at"), filter(([k]) => k == "compatible"), filter(([k]) => k == "incompatible"), filter(([k]) => k == "antifeature").sort(stringSort), filter(([k]) => k.startsWith("antifeature:")).sort(stringSort), filter(([k]) => k == "contributionAmount"), filter(([k]) => k == "contributionURL"), filter(([k]) => k == "noframes"), filter(([k]) => k == "unwrap"), p0 ].flat(1); }; // src/node/plugins/server.ts var installUserPath = "/__vite-plugin-monkey.install.user.js"; var gmApiPath = "/__vite-plugin-monkey.gm.api.js"; var entryPath = "/__vite-plugin-monkey.entry.js"; var pullPath = "/__vite-plugin-monkey.pull.js"; var restartStoreKey = "__vite_plugin_monkey_install_url"; var serverPlugin = (finalOption) => { let viteConfig; return { name: "monkey:server", apply: "serve", async config(userConfig) { var _a, _b, _c; return { preview: { host: ((_a = userConfig.preview) == null ? void 0 : _a.host) ?? "127.0.0.1", cors: true }, server: { host: ((_b = userConfig.server) == null ? void 0 : _b.host) ?? "127.0.0.1", open: ((_c = userConfig.server) == null ? void 0 : _c.open) ?? finalOption.server.open, cors: true } }; }, async configResolved(resolvedConfig) { viteConfig = resolvedConfig; }, async configureServer(server) { for (const [k, v] of Object.entries(finalOption.userscript.name)) { Reflect.set( finalOption.userscript.name, k, finalOption.server.prefix(v) ); } finalOption.userscript.grant.add("*"); server.middlewares.use((_, res, next) => { if (res.getHeader("Access-Control-Allow-Private-Network") === void 0) { res.setHeader("Access-Control-Allow-Private-Network", "true"); } next(); }); server.middlewares.use(async (req, res, next) => { var _a; const reqUrl = req.url; if (req.method === "GET" && reqUrl && [installUserPath, entryPath, pullPath, gmApiPath].some( (u) => reqUrl.startsWith(u) )) { Object.entries({ "access-control-allow-origin": "*", "content-type": "application/javascript" }).forEach(([k, v]) => { res.setHeader(k, v); }); const usp = new URLSearchParams(reqUrl.split("?", 2)[1]); if (reqUrl.startsWith(installUserPath)) { let origin = (_a = toValidURL(usp.get(`origin`))) == null ? void 0 : _a.origin; if (!origin) { const { https, port } = viteConfig.server; let { host } = viteConfig.server; if (host == "0.0.0.0") { host = "127.0.0.1"; } origin = `${https ? "https" : "http"}://${host}:${port}`; logger.warn( `can not get origin from install url query parameter, use ${origin}`, { time: true } ); } Reflect.set(globalThis, restartStoreKey, origin); const u = new URL(entryPath, origin); res.end( [ await finalMonkeyOptionToComment( finalOption, /* @__PURE__ */ new Set(), "serve" ), fn2string(serverInjectFn, { entrySrc: u.href }), "" ].join("\n\n") ); } else if (reqUrl.startsWith(entryPath)) { const htmlText2 = await server.transformIndexHtml( "/", `<html><head></head></html>`, req.originalUrl ); const doc = parseDocument(htmlText2); const scriptList = DomUtils.getElementsByTagType( ElementType.Script, doc ); const entryList = finalOption.server.mountGmApi ? [gmApiPath] : []; scriptList.forEach((p) => { const src = p.attribs.src ?? ""; const textNode = p.firstChild; let text = ""; if ((textNode == null ? void 0 : textNode.type) == ElementType.Text) { text = textNode.data ?? ""; } if (src) { entryList.push(src); } else { const usp2 = new URLSearchParams({ text: Buffer.from(text, "utf-8").toString("base64url") }); entryList.push(pullPath + `?` + usp2.toString()); } }); let realEntry = finalOption.entry; if (path3.isAbsolute(realEntry)) { realEntry = normalizePath2( path3.relative(viteConfig.root, realEntry) ); } const entryUrl = new URL(realEntry, "http://127.0.0.1"); entryList.push(entryUrl.pathname + entryUrl.search); res.end( entryList.map((s) => `import ${JSON.stringify(s)};`).join("\n") ); } else if (reqUrl.startsWith(pullPath)) { res.end( Buffer.from(usp.get("text") ?? "", "base64url").toString("utf-8") ); } else if (reqUrl.startsWith(gmApiPath)) { if (finalOption.server.mountGmApi) { res.end( `;(${mountGmApiFn})(import.meta, ${JSON.stringify(gmIdentifiers)});` ); } else { res.end(""); } } return; } next(); }); if (finalOption.server.open) { const cacheUserPath = `node_modules/.vite/__vite-plugin-monkey.cache.${cyrb53hash( viteConfig.configFile )}.user.js`; let cacheComment = ""; if (await existFile(cacheUserPath)) { cacheComment = (await fs2.readFile(cacheUserPath)).toString("utf-8"); } else { await fs2.mkdir(path3.dirname(cacheUserPath)).catch(() => { }); } const newComment = await finalMonkeyOptionToComment( finalOption, /* @__PURE__ */ new Set(), "serve" ); const installUrl = Reflect.get(globalThis, restartStoreKey); if (!isFirstBoot() && cacheComment != newComment && installUrl) { openBrowser(installUrl, true, logger); logger.info("reopen, config comment has changed", { time: true }); } await fs2.writeFile(cacheUserPath, newComment).catch(() => { }); } } }; }; // src/node/plugins/virtualHtml.ts var virtualHtmlPlugin = (_) => { return { name: "monkey:virtualHtml", apply: "serve", configureServer(server) { server.middlewares.use(async (req, res, next) => { const url = req.url || "/"; if (["/", "/index.html"].includes(url)) { res.setHeader("content-type", "text/html"); res.setHeader("cache-control", "no-cache"); res.setHeader("access-control-allow-origin", "*"); return res.end(fcToHtml(virtualHtmlTemplate, installUserPath)); } next(); }); } }; }; // src/node/plugins/fixViteAsset.ts var template = ` export default ((()=>{ try{ return new URL(__VALUE__, import.meta['url']).href }catch(_){ return __VALUE__ } })()) `.trimStart(); var fixViteAssetPlugin = (_) => { let viteConfig; return { name: "monkey:fixViteAsset", apply: "serve", async configResolved(resolvedConfig) { viteConfig = resolvedConfig; }, async transform(code, id) { var _a; const [_2, query = "url"] = id.split("?", 2); if ((query.split("&").includes("url") || viteConfig.assetsInclude(id)) && code.match(/^\s*export\s+default/)) { const ast = this.parse(code); if (ast.type == "Program") { const defaultNode = (_a = ast.body) == null ? void 0 : _a[0]; if ((defaultNode == null ? void 0 : defaultNode.type) == "ExportDefaultDeclaration") { const declarationNode = defaultNode == null ? void 0 : defaultNode.declaration; const value = declarationNode == null ? void 0 : declarationNode.value; if ((declarationNode == null ? void 0 : declarationNode.type) == "Literal" && typeof value == "string") { return template.replace(/__VALUE__/g, JSON.stringify(value)); } } } } } }; }; // src/node/plugins/fixViteClient.ts var fixViteClientPlugin = (_) => { return { name: "monkey:fixViteClient", apply: "serve", async transform(code, id) { if (id.endsWith("node_modules/vite/dist/client/client.mjs")) { code = code.replace( /__BASE__/g, `((()=>{const b = __BASE__; const u = new URL(import.meta['url'], location.origin); return b !== '/' ? b : (u.origin+'/');})())` ); return code; } } }; }; // src/node/plugins/externalGlobals.ts import { normalizePath as normalizePath3 } from "vite"; var externalGlobalsPlugin = (finalOption) => { const { globalsPkg2VarName, requirePkgList } = finalOption; return { name: "monkey:externalGlobals", enforce: "pre", apply: "build", async config() { for (const [moduleName, varName2LibUrl] of finalOption.build.externalGlobals) { const { name, version } = await getModuleRealInfo(moduleName); if (typeof varName2LibUrl == "string") { globalsPkg2VarName[moduleName] = varName2LibUrl; } else if (typeof varName2LibUrl == "function") { globalsPkg2VarName[moduleName] = await varName2LibUrl( version, name, moduleName ); } else if (varName2LibUrl instanceof Array) { const [varName, ...libUrlList] = varName2LibUrl; if (typeof varName == "string") { globalsPkg2VarName[moduleName] = varName; } else if (typeof varName == "function") { globalsPkg2VarName[moduleName] = await varName( version, name, moduleName ); } for (const libUrl of libUrlList) { if (typeof libUrl == "string") { requirePkgList.push({ url: libUrl, moduleName }); } else if (typeof libUrl == "function") { requirePkgList.push({ url: await libUrl(version, name, moduleName), moduleName }); } } } } return { build: { rollupOptions: { external(source, _importer, _isResolved) { return source in globalsPkg2VarName; } // output: { // globals: globalsPkg2VarName, // inlineDynamicImports: true, // see https://rollupjs.org/guide/en/#outputinlinedynamicimports // }, } } }; }, // async resolveDynamicImport(specifier, _importer) { // if (typeof specifier == 'string' && specifier in globalsPkg2VarName) { // return dynamicImportPrefix + specifier + '\0'; // } // }, // async load(id) { // if (id.startsWith(dynamicImportPrefix) && id.endsWith('\0')) { // const rawId = id.slice(dynamicImportPrefix.length, id.length - 1); // if (rawId in globalsPkg2VarName) { // return `export {default} from '${rawId}';export * from '${rawId}';`; // } // } // }, async generateBundle() { const usedModIdSet = new Set( Array.from(this.getModuleIds()).map((s) => normalizePath3(s)) ); finalOption.collectRequireUrls = requirePkgList.filter((p) => usedModIdSet.has(p.moduleName)).map((p) => p.url); } }; }; // src/node/plugins/externalLoader.ts import { transformWithEsbuild as transformWithEsbuild2 } from "vite"; var cssLoader = (resourceName) => { const css = GM_getResourceText(resourceName); GM_addStyle(css); return css; }; var jsonLoader = (resourceName) => ( // @ts-ignore JSON.parse(GM_getResourceText(resourceName)) ); var urlLoader = (resourceName, mediaType) => ( // @ts-ignore GM_getResourceURL(resourceName, false).replace( /^data:application;base64,/, `data:${mediaType};base64,` ) ); var rawLoader = (resourceName) => ( // @ts-ignore GM_getResourceText(resourceName) ); var moduleSourceCode = [ `export const cssLoader = ${cssLoader}`, `export const jsonLoader = ${jsonLoader}`, `export const urlLoader = ${urlLoader}`, `export const rawLoader = ${rawLoader}` ].join(";"); var externalLoaderPlugin = (_) => { return { name: "monkey:externalLoader", enforce: "pre", apply: "build", async resolveId(id) { if (id == "virtual:plugin-monkey-loader") { return "\0" + id; } }, async load(id) { if (id == "\0virtual:plugin-monkey-loader") { return transformWithEsbuild2( moduleSourceCode, "/virtual/plugin-monkey-loader/index.js", { minify: true, sourcemap: true, legalComments: "none" } ); } } }; }; // src/node/plugins/externalResource.ts import { normalizePath as normalizePath4 } from "vite"; import { lookup, mimes } from "mrmime"; import { URLSearchParams as URLSearchParams2 } from "url"; var resourceImportPrefix = "\0monkey-resource-import:"; var externalResourcePlugin = (finalOption) => { const resourceRecord = {}; let viteConfig; return { name: "monkey:externalResource", enforce: "pre", apply: "build", configResolved(conf