vite-plugin-userscript
Version:
643 lines (633 loc) • 20.9 kB
JavaScript
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
};