UNPKG

vite-plugin-userscript

Version:
643 lines (633 loc) 20.9 kB
var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); // src/metablock/file-loader.ts import path from "path"; import fs from "fs-extra"; import YAML from "js-yaml"; import JSON5 from "json5"; // src/metablock/error.ts var UnsupportedFormat = class extends Error { }; var UnknownScriptManager = class extends Error { }; // src/metablock/file-loader.ts var loadJSON = async (filepath) => { try { const content = await fs.readFile(filepath, "utf8"); return JSON5.parse(content); } catch (error) { console.error(error); return null; } }; var loadYAML = async (filepath) => { try { const content = await fs.readFile(filepath, "utf8"); return YAML.load(content); } catch (error) { console.error(error); return null; } }; var loadMetaFile = async (filename) => { const filepath = path.resolve(filename != null ? filename : "./metablock.json"); const ext = path.extname(filepath); switch (ext) { case ".json": case ".json5": return loadJSON(filepath); case ".yaml": case ".yml": return loadYAML(filepath); default: throw new UnsupportedFormat(`Do not support ${ext} now.`); } }; var file_loader_default = loadMetaFile; // src/metablock/meta.ts import semver from "semver"; // src/metablock/validator.ts import { isUri } from "valid-url"; var isURI = (value) => { return Boolean(isUri(String(value))); }; var isNil = (value) => { return value === null || value === void 0; }; var isString = (value) => typeof value === "string"; var isObjectLike = (value) => value != null && typeof value == "object"; var getPrototype = (value) => Object.getPrototypeOf(value); var hasOwnProperty = Object.prototype.hasOwnProperty; var isPlainObject = (value) => { if (!isObjectLike(value) || String(value) !== "[object Object]") { return false; } const proto = getPrototype(value); if (proto === null) { return true; } const Ctor = hasOwnProperty.call(proto, "constructor") && proto.constructor; return typeof Ctor == "function" && Ctor instanceof Ctor && Function.prototype.toString.call(Ctor) === Function.prototype.toString.call(Object); }; var isURIMatchPattern = (value) => { const s = String(value); return /^([*]|https?|file|ftp):\/\/([*]|(?:\*\.)?[^*/]*)\/.*$/u.test(s); }; var isGlobURI = (value) => { const s = String(value); return /^\/.*\/$/.test(s) || isURI(s) || isString(s) && s.includes("*"); }; var isIPv4 = (value) => { const s = String(value); if (/^\d{1,3}[.]\d{1,3}[.]\d{1,3}[.]\d{1,3}$/.test(s)) { return s.split(".").filter(Boolean).map((t) => [t, parseInt(t)]).every(([t, n]) => Number.isInteger(n) && n >= 0 && n <= 255 && String(n) === t); } return false; }; var isValidConnectMetaValue = (value) => { return isIPv4(value) || isUri(String(value)) || /[\w-]+(\.[\w-]+)+/.test(String(value)) || value === "*" || value === "localhost"; }; // src/metablock/meta.ts var DEFAULT_ORDER = [ "name", "description", "namespace", "...", "grant" ]; var createMultilingual = (key) => (doError) => (val) => { if (!val) { return doError(`${key}'s meta value can't be falsy`); } if (isString(val)) { return [[key, val]]; } if (isPlainObject(val)) { const langRecord = val; if (!langRecord.default) { return doError(`${key}.default is required`); } return Object.entries(langRecord).map(([lang, text]) => { const langKey = lang === "default" ? `${key}` : `${key}:${lang}`; return [langKey, text]; }).sort(([a], [b]) => a.localeCompare(b)); } return doError(`${key}'s meta value is an invalid type`); }; var createBinaryString = (key) => (doError) => (val) => { if (!val) { return doError(`${key}'s meta value can't be falsy`); } if (!isString(val)) { return doError(`${key}'s meta value should be string type`); } return [[key, val]]; }; var createBinaryStringArray = (key) => (doError) => (val) => { if (!val) { return doError(`${key}'s meta value can't be falsy`); } if (Array.isArray(val)) { const goods = val.map((v) => createBinaryString(key)(doError)(v)).filter(Boolean).flat(); if (goods.length) { return goods; } } if (!isString(val)) { return doError(`${key}'s meta value should be string or string[] type`); } return createBinaryString(key)(doError)(val); }; var createBinaryURI = (key) => (doError) => (val) => { if (!val) { return doError(`${key}'s meta value can't be falsy`); } if (!isString(val)) { return doError(`${key}'s meta value should be string type`); } if (!isURI(val)) { doError(`${key}'s meta value should be a valid URI`); } return [[key, val]]; }; var createBinaryURIArray = (key) => (doError) => (val) => { if (!val) { return doError(`${key}'s meta value can't be falsy`); } if (Array.isArray(val)) { const goods = val.map((v) => createBinaryURI(key)(doError)(v)).filter(Boolean).flat(); if (goods.length) { return goods; } } if (!isString(val)) { return doError(`${key}'s meta value should be string or string[] type`); } return createBinaryURI(key)(doError)(val); }; var createBinaryGlobURI = (key) => (doError) => (val) => { if (!val) { return doError(`${key}'s meta value can't be falsy`); } if (!isString(val)) { return doError(`${key}'s meta value should be string type`); } if (!isGlobURI(val)) { doError(`${key}'s meta value should be a valid glob URI`); } return [[key, val]]; }; var createBinaryGlobURIArray = (key) => (doError) => (val) => { if (!val) { return doError(`${key}'s meta value can't be falsy`); } if (Array.isArray(val)) { const goods = val.map((v) => createBinaryGlobURI(key)(doError)(v)).filter(Boolean).flat(); if (goods.length) { return goods; } } if (!isString(val)) { return doError(`${key}'s meta value should be string or string[] type`); } return createBinaryGlobURI(key)(doError)(val); }; var createBinaryURIMatchPattern = (key) => (doError) => (val) => { if (!val) { return doError(`${key}'s meta value can't be falsy`); } if (!isString(val)) { return doError(`${key}'s meta value should be string type`); } if (!isURIMatchPattern(val)) { doError(`${key}'s meta value should be a valid URI`); } return [[key, val]]; }; var createBinaryURIMatchPatternArray = (key) => (doError) => (val) => { if (!val) { return doError(`${key}'s meta value can't be falsy`); } if (Array.isArray(val)) { const goods = val.map((v) => createBinaryURIMatchPattern(key)(doError)(v)).filter(Boolean).flat(); if (goods.length) { return goods; } } if (!isString(val)) { return doError(`${key}'s meta value should be string or string[] type`); } return createBinaryURIMatchPattern(key)(doError)(val); }; var createBinaryConnectPattern = (key) => (doError) => (val) => { if (!val) { return doError(`${key}'s meta value can't be falsy`); } if (!isString(val)) { return doError(`${key}'s meta value should be string type`); } if (!isValidConnectMetaValue(val)) { doError(`${key}'s meta value should be a valid connect pattern`); } return [[key, val]]; }; var createBinaryEnum = (key, valEnums) => (doError) => (val) => { if (!val) { return doError(`${key}'s meta value can't be falsy`); } if (!Array.isArray(valEnums) || !valEnums.every(isString)) { return doError(`${key}'s valEnums should be string[] type`); } if (!isString(val)) { return doError(`${key}'s meta value should be string type`); } const maybeEnumVal = val; if (!valEnums.includes(maybeEnumVal)) { return doError(`${key}'s meta value should be one of [${valEnums.join(", ")}]`); } return [[key, maybeEnumVal]]; }; var createBinaryVersion = (key) => (doError) => (val) => { if (!val) { return doError(`${key}'s meta value can't be falsy`); } if (!isString(val)) { return doError(`${key}'s meta value should be string type`); } const version = val; if (semver.valid(version)) { const cleanedVersion = semver.clean(version); if (cleanedVersion) { return [[key, cleanedVersion]]; } } const coerce = semver.coerce(version); if (semver.valid(coerce) && coerce !== null) { doError(`${key} can be transform to ${coerce}`); return [[key, coerce.version]]; } return doError(`${key}'s meta value is invalid`); }; var createUnary = (key) => (doError) => (val) => { if (!val) { return doError(`${key}'s meta value can't be falsy`); } return [[key]]; }; var createTernaryURI = (key) => (doError) => (val) => { if (!val) { return doError(`${key}'s meta value can't be falsy`); } if (!isPlainObject(val)) { return doError(`${key}'s meta value should be plain object type`); } const uriMap = val; const resultEntries = []; for (const [uriKey, uriVal] of Object.entries(uriMap)) { if (!isURI(uriVal)) { return doError(`${key}'s meta value should be URI`); } resultEntries.push([key, uriKey, uriVal]); } return resultEntries; }; var createGrant = (key) => (doError) => (val) => { if (!val) { return [[key, "none"]]; } if (isString(val)) { return [[key, val]]; } if (Array.isArray(val)) { const goods = val.map((v) => createGrant(key)(doError)(v)).filter(Boolean).flat(); if (goods.length) { return goods; } } return [[key, "none"]]; }; var RUN_AT_ENUM = ["end", "start", "idle", "body"].map((s) => `document-${s}`).concat("context-menu"); var INJECT_INTO_ENUM = ["page", "content", "auto"]; var COMPATIBLE_META_ENTRY_TRANSFORM_MAP = { "name": createMultilingual("name"), "namespace": createBinaryString("namespace"), "description": createMultilingual("description"), "version": createBinaryVersion("version"), "match": createBinaryURIMatchPatternArray("match"), "include": createBinaryGlobURIArray("include"), "exclude": createBinaryGlobURIArray("exclude"), "icon": createBinaryURI("icon"), "require": createBinaryURIArray("require"), "run-at": createBinaryEnum("run-at", RUN_AT_ENUM), "resource": createTernaryURI("resource"), "noframes": createUnary("noframes"), "grant": createGrant("grant"), "author": createBinaryString("author"), "updateURL": createBinaryURI("updateURL"), "installURL": createBinaryURI("installURL"), "downloadURL": createBinaryURI("downloadURL"), "license": createBinaryString("license"), "supportURL": createBinaryURI("supportURL"), "contributionURL": createBinaryURI("contributionURL"), "contributionAmount": createBinaryString("contributionAmount"), "compatible": createBinaryStringArray("compatible"), "incompatible": createBinaryStringArray("incompatible"), "antifeature": createMultilingual("antifeature") }; var COMPATIBLE_META_KEYS = Object.keys(COMPATIBLE_META_ENTRY_TRANSFORM_MAP); var TAMPERMONKEY_EXCLUSIVE_META_ENTRY_TRANSFORM_MAP = { homepage: createBinaryURI("homepage"), homepageURL: createBinaryURI("homepageURL"), website: createBinaryURI("website"), source: createBinaryURI("source"), defaulticon: createBinaryURI("defaulticon"), icon64: createBinaryURI("icon64"), iconURL: createBinaryURI("iconURL"), icon64URL: createBinaryURI("icon64URL"), connect: createBinaryConnectPattern("connect"), nocompat: createBinaryEnum("nocompat", ["Chrome", "chrome"]) }; var TAMPERMONKEY_EXCLUSIVE_META_KEYS = Object.keys(TAMPERMONKEY_EXCLUSIVE_META_ENTRY_TRANSFORM_MAP); var VIOLENTMONKEY_EXCLUSIVE_META_ENTRY_TRANSFORM_MAP = { "exclude-match": createBinaryURIMatchPatternArray("exclude-match"), "inject-into": createBinaryEnum("inject-into", INJECT_INTO_ENUM) }; var VIOLENTMONKEY_EXCLUSIVE_META_KEYS = Object.keys(VIOLENTMONKEY_EXCLUSIVE_META_ENTRY_TRANSFORM_MAP); var META_ENTRIES_TRANSFORM_MAP = __spreadValues(__spreadValues(__spreadValues({}, COMPATIBLE_META_ENTRY_TRANSFORM_MAP), TAMPERMONKEY_EXCLUSIVE_META_ENTRY_TRANSFORM_MAP), VIOLENTMONKEY_EXCLUSIVE_META_ENTRY_TRANSFORM_MAP); var ALL_META_KEYS = Object.keys(META_ENTRIES_TRANSFORM_MAP); // src/metablock/utils.ts var unique = (arr) => [...new Set(arr)]; var trim = (str) => str.trim(); // src/metablock/options.ts var toScriptManagerEnum = (name) => { const managerName = String(name != null ? name : "all").toLowerCase().trim(); switch (managerName) { case "tm": case "tampermonkey": { return 0 /* Tampermonkey */; } case "gm3": case "greasemonkey3": { return 1 /* Greasemonkey */; } case "gm": case "gm4": case "greasemonkey": case "greasemonkey4": { return 2 /* Greasemonkey4 */; } case "vm": case "violentmonkey": { return 3 /* Violentmonkey */; } case "compatible": { return 4 /* Compatible */; } case "all": { return 5 /* All */; } default: throw new UnknownScriptManager(`Unknown script manager: ${managerName}`); } }; var getSupportedMetaKeysByScriptManager = (manager) => { switch (manager) { case 0 /* Tampermonkey */: { return [ ...COMPATIBLE_META_KEYS, ...TAMPERMONKEY_EXCLUSIVE_META_KEYS ]; } case 1 /* Greasemonkey */: case 2 /* Greasemonkey4 */: { return [...COMPATIBLE_META_KEYS]; } case 3 /* Violentmonkey */: { return [ ...COMPATIBLE_META_KEYS, ...VIOLENTMONKEY_EXCLUSIVE_META_KEYS ]; } case 4 /* Compatible */: { return [...COMPATIBLE_META_KEYS]; } case 5 /* All */: { return [...ALL_META_KEYS]; } default: return []; } }; var toValidOrder = (orderInput, supportedMetaKeys) => { const order = [...orderInput]; const restIndex = order.indexOf("..."); if (restIndex >= 0) { order.splice(restIndex, 1, ...DEFAULT_ORDER); } else { order.push(...DEFAULT_ORDER); } return unique(order).filter((key) => key === "..." || supportedMetaKeys.includes(key)); }; var parsePluginOptions = (options) => { var _a, _b, _c; const opt = options != null ? options : {}; const applyPatterns = isNil(opt.applyTo) ? [/[.]user[.]js$/] : [].concat(opt.applyTo); const managers = isNil(opt.manager) ? ["all"] : [].concat(opt.manager); const supportedMetaKeys = unique(managers.map(toScriptManagerEnum).flatMap(getSupportedMetaKeysByScriptManager)); const errorLevel = (_a = opt == null ? void 0 : opt.errorLevel) != null ? _a : "warn"; const override = (_b = options == null ? void 0 : options.override) != null ? _b : {}; if (!isPlainObject(override)) { throw new Error("override must be an object"); } const order = toValidOrder((_c = opt == null ? void 0 : opt.order) != null ? _c : [], supportedMetaKeys); return { transformConfig: { order, override, managers, errorLevel, supportedMetaKeys }, outputConfig: { applyPatterns } }; }; // src/metablock/index.ts var filenameFilter = (patterns) => (filename) => { return patterns.some((pattern) => { if (typeof pattern === "string") { return filename.includes(pattern); } else { return pattern.test(filename); } }); }; var validateErrorAction = (level) => (msg) => { if (level === "warn") { return console.warn(msg); } else if (level === "error") { throw new Error(msg); } }; var transformToMetaEntries = (metablockInput, config) => { const { errorLevel, order, supportedMetaKeys, override } = config; const doError = validateErrorAction(errorLevel); const metablock = Object.assign({}, metablockInput, override); const hasMetablockProperty = (key) => Object.prototype.hasOwnProperty.call(metablock, key); if (!hasMetablockProperty("name")) { metablock.name = "New Script"; } if (!hasMetablockProperty("description")) { metablock.description = "Do something awesome!"; } if (!hasMetablockProperty("namespace")) { metablock.namespace = "https://www.npmjs.com/package/vite-plugin-userscript"; } if (!hasMetablockProperty("grant")) { metablock.grant = "none"; } const restIndex = order.indexOf("..."); const sortedEntries = Object.entries(metablock); sortedEntries.sort(([key1], [key2]) => { const idx1 = order.indexOf(key1); const idx2 = order.indexOf(key2); const i = idx1 < 0 ? restIndex : idx1; const j = idx2 < 0 ? restIndex : idx2; return i - j; }); const resultEntries = []; for (const [metaKey, metaValue] of sortedEntries) { if (supportedMetaKeys.includes(metaKey)) { const transformFn = META_ENTRIES_TRANSFORM_MAP[metaKey]; const transformedEntries = transformFn(doError)(metaValue); if (transformedEntries) { resultEntries.push(...transformedEntries); } } else { const errorMessage = `Unknown meta key: ${metaKey}`; if (errorLevel === "error") { throw new Error(errorMessage); } else if (errorLevel === "warn") { console.warn(errorMessage); } } } return resultEntries; }; var getMetaKeyMaxLengthFromMetaEntries = (metaEntries) => { const len = { name: 0, desc: 0, other: 0 }; for (const [key] of metaEntries) { if (/^name(:.*)?$/.test(key)) { len.name = Math.max(len.name, key.length); } else if (/^description(:.*)?$/.test(key)) { len.desc = Math.max(len.desc, key.length); } else { len.other = Math.max(len.other, key.length); } } const isOnlyName = len.name === "name".length; const isOnlyDescription = len.desc === "description".length; if (isOnlyName && isOnlyDescription) { const finalLength = Math.max(len.name, len.desc, len.other); return { name: finalLength, desc: finalLength, other: finalLength }; } else if (isOnlyName) { const finalLength = Math.max(len.name, len.other); return __spreadProps(__spreadValues({}, len), { name: finalLength, other: finalLength }); } else if (isOnlyDescription) { const finalLength = Math.max(len.desc, len.other); return __spreadProps(__spreadValues({}, len), { desc: finalLength, other: finalLength }); } return len; }; var renderMetaEntries = (entries) => { const len = getMetaKeyMaxLengthFromMetaEntries(entries); const toLine = (head, tails, padding) => `// @${head.padEnd(padding, " ")} ${tails.join(" ")}`; const getPadding = (head) => { if (/^name(:.*)?$/.test(head)) { return len.name; } else if (/^description(:.*)?$/.test(head)) { return len.desc; } else { return len.other; } }; const lines = []; lines.push("// ==UserScript=="); for (const [head, ...tails] of entries) { lines.push(toLine(head, tails, getPadding(head))); } lines.push("// ==/UserScript=="); return lines.map(trim).join("\n"); }; var metablock_default = (options = {}) => { try { const { transformConfig, outputConfig } = parsePluginOptions(options); const { applyPatterns } = outputConfig; const filter = filenameFilter(applyPatterns); return { name: "vite-plugin-userscript:metablock", async generateBundle(outputOptions, bundle) { var _a, _b; for (const outputChunk of Object.values(bundle)) { if (outputChunk.type === "asset") { continue; } const { code, fileName } = outputChunk; if (!filter(fileName)) { continue; } const metablockInput = await file_loader_default(options == null ? void 0 : options.file); const metablockEntries = transformToMetaEntries(metablockInput, transformConfig); const metablockOutput = renderMetaEntries(metablockEntries); const prepend = `${metablockOutput} `; const prependLineCount = (_b = (_a = [...prepend.matchAll(/\n/g)]) == null ? void 0 : _a.length) != null ? _b : 0; outputChunk.code = prepend + code; if (outputChunk.map) { outputChunk.map.mappings = ";".repeat(prependLineCount) + outputChunk.map.mappings; } } } }; } catch (error) { console.error(error); return { name: "vite-plugin-userscript:metablock" }; } }; export { metablock_default as metablock };