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
JavaScript
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