您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Improve the Changesets experience in GitHub PRs
// ==UserScript== // @name GitHub Changesets // @license MIT // @homepageURL https://github.com/bluwy/github-changesets-userscript // @supportURL https://github.com/bluwy/github-changesets-userscript // @namespace https://gf.qytechs.cn/ // @version 0.1.3 // @description Improve the Changesets experience in GitHub PRs // @author Bjorn Lu // @match https://github.com/** // @icon https://www.google.com/s2/favicons?sz=64&domain=github.com // @grant none // ==/UserScript== // Options const shouldRemoveChangesetBotComment = true const shouldSkipCache = false ; (() => { var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); // node_modules/human-id/dist/index.js var require_dist = __commonJS({ "node_modules/human-id/dist/index.js"(exports) { "use strict"; var __spreadArray = exports && exports.__spreadArray || function(to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.minLength = exports.maxLength = exports.poolSize = exports.humanId = exports.adverbs = exports.verbs = exports.nouns = exports.adjectives = void 0; exports.adjectives = ["afraid", "all", "angry", "beige", "big", "better", "bitter", "blue", "brave", "breezy", "bright", "brown", "bumpy", "busy", "calm", "chatty", "chilly", "chubby", "clean", "clear", "clever", "cold", "crazy", "cruel", "cuddly", "curly", "curvy", "cute", "common", "cold", "cool", "cyan", "dark", "deep", "dirty", "dry", "dull", "eager", "early", "easy", "eight", "eighty", "eleven", "empty", "every", "evil", "fair", "famous", "fast", "fancy", "few", "fine", "fifty", "five", "flat", "fluffy", "floppy", "forty", "four", "free", "fresh", "fruity", "full", "funny", "fuzzy", "gentle", "giant", "gold", "good", "great", "green", "grumpy", "happy", "heavy", "hip", "honest", "hot", "huge", "hungry", "icy", "itchy", "khaki", "kind", "large", "late", "lazy", "lemon", "legal", "light", "little", "long", "loose", "loud", "lovely", "lucky", "major", "many", "mean", "metal", "mighty", "modern", "moody", "nasty", "neat", "new", "nice", "nine", "ninety", "odd", "old", "olive", "open", "orange", "pink", "plain", "plenty", "polite", "poor", "pretty", "proud", "public", "puny", "petite", "purple", "quick", "quiet", "rare", "real", "ready", "red", "rich", "ripe", "rotten", "rude", "sad", "salty", "seven", "shaggy", "shaky", "sharp", "shiny", "short", "shy", "silent", "silly", "silver", "six", "sixty", "slick", "slimy", "slow", "small", "smart", "smooth", "social", "soft", "solid", "some", "sour", "spicy", "spotty", "stale", "strong", "stupid", "sweet", "swift", "tall", "tame", "tangy", "tasty", "ten", "tender", "thick", "thin", "thirty", "three", "tidy", "tiny", "tired", "tough", "tricky", "true", "twelve", "twenty", "two", "upset", "vast", "violet", "warm", "weak", "wet", "whole", "wicked", "wide", "wild", "wise", "witty", "yellow", "young", "yummy"]; exports.nouns = ["apes", "animals", "areas", "bars", "banks", "baths", "breads", "bushes", "cloths", "clowns", "clubs", "hoops", "loops", "memes", "papers", "parks", "paths", "showers", "sides", "signs", "sites", "streets", "teeth", "tires", "webs", "actors", "ads", "adults", "aliens", "ants", "apples", "baboons", "badgers", "bags", "bananas", "bats", "beans", "bears", "beds", "beers", "bees", "berries", "bikes", "birds", "boats", "bobcats", "books", "bottles", "boxes", "brooms", "buckets", "bugs", "buses", "buttons", "camels", "cases", "cameras", "candies", "candles", "carpets", "carrots", "carrots", "cars", "cats", "chairs", "chefs", "chicken", "clocks", "clouds", "coats", "cobras", "coins", "corners", "colts", "comics", "cooks", "cougars", "regions", "results", "cows", "crabs", "crabs", "crews", "cups", "cities", "cycles", "dancers", "days", "deer", "dingos", "dodos", "dogs", "dolls", "donkeys", "donuts", "doodles", "doors", "dots", "dragons", "drinks", "dryers", "ducks", "ducks", "eagles", "ears", "eels", "eggs", "ends", "mammals", "emus", "experts", "eyes", "facts", "falcons", "fans", "feet", "files", "flies", "flowers", "forks", "foxes", "friends", "frogs", "games", "garlics", "geckos", "geese", "ghosts", "ghosts", "gifts", "glasses", "goats", "grapes", "groups", "guests", "hairs", "hands", "hats", "heads", "hornets", "horses", "hotels", "hounds", "houses", "humans", "icons", "ideas", "impalas", "insects", "islands", "items", "jars", "jeans", "jobs", "jokes", "keys", "kids", "kings", "kiwis", "knives", "lamps", "lands", "laws", "lemons", "lies", "lights", "lines", "lions", "lizards", "llamas", "mails", "mangos", "maps", "masks", "meals", "melons", "mice", "mirrors", "moments", "moles", "monkeys", "months", "moons", "moose", "mugs", "nails", "needles", "news", "nights", "numbers", "olives", "onions", "oranges", "otters", "owls", "pandas", "pans", "pants", "papayas", "parents", "parts", "parrots", "paws", "peaches", "pears", "peas", "pens", "pets", "phones", "pianos", "pigs", "pillows", "places", "planes", "planets", "plants", "plums", "poems", "poets", "points", "pots", "pugs", "pumas", "queens", "rabbits", "radios", "rats", "ravens", "readers", "rice", "rings", "rivers", "rockets", "rocks", "rooms", "roses", "rules", "schools", "bats", "seals", "seas", "sheep", "shirts", "shoes", "shrimps", "singers", "sloths", "snails", "snakes", "socks", "spiders", "spies", "spoons", "squids", "stars", "states", "steaks", "wings", "suits", "suns", "swans", "symbols", "tables", "taxes", "taxis", "teams", "terms", "things", "ties", "tigers", "times", "tips", "toes", "towns", "tools", "toys", "trains", "trams", "trees", "turkeys", "turtles", "vans", "views", "walls", "walls", "wasps", "waves", "ways", "weeks", "windows", "wolves", "women", "wombats", "words", "worlds", "worms", "yaks", "years", "zebras", "zoos"]; exports.verbs = ["accept", "act", "add", "admire", "agree", "allow", "appear", "argue", "arrive", "ask", "attack", "attend", "bake", "bathe", "battle", "beam", "beg", "begin", "behave", "bet", "boil", "bow", "brake", "brush", "build", "burn", "buy", "call", "camp", "care", "carry", "change", "cheat", "check", "cheer", "chew", "clap", "clean", "cough", "count", "cover", "crash", "create", "cross", "cry", "cut", "dance", "decide", "deny", "design", "dig", "divide", "do", "double", "doubt", "draw", "dream", "dress", "drive", "drop", "drum", "eat", "end", "enter", "enjoy", "exist", "fail", "fall", "feel", "fetch", "film", "find", "fix", "flash", "float", "flow", "fly", "fold", "follow", "fry", "give", "glow", "go", "grab", "greet", "grin", "grow", "guess", "hammer", "hang", "happen", "heal", "hear", "help", "hide", "hope", "hug", "hunt", "invent", "invite", "itch", "jam", "jog", "join", "joke", "judge", "juggle", "jump", "kick", "kiss", "kneel", "knock", "know", "laugh", "lay", "lead", "learn", "leave", "lick", "like", "lie", "listen", "live", "look", "lose", "love", "make", "march", "marry", "mate", "matter", "melt", "mix", "move", "nail", "notice", "obey", "occur", "open", "own", "pay", "peel", "play", "poke", "post", "press", "prove", "pull", "pump", "pick", "punch", "push", "raise", "read", "refuse", "relate", "relax", "remain", "repair", "repeat", "reply", "report", "rescue", "rest", "retire", "return", "rhyme", "ring", "roll", "rule", "run", "rush", "say", "scream", "see", "search", "sell", "send", "serve", "shake", "share", "shave", "shine", "show", "shop", "shout", "sin", "sink", "sing", "sip", "sit", "sleep", "slide", "smash", "smell", "smile", "smoke", "sneeze", "sniff", "sort", "speak", "spend", "stand", "start", "stay", "stick", "stop", "stare", "study", "strive", "swim", "switch", "take", "talk", "tan", "tap", "taste", "teach", "tease", "tell", "thank", "think", "throw", "tickle", "tie", "trade", "train", "travel", "try", "turn", "type", "unite", "vanish", "visit", "wait", "walk", "warn", "wash", "watch", "wave", "wear", "win", "wink", "wish", "wonder", "work", "worry", "write", "yawn", "yell"]; exports.adverbs = ["bravely", "brightly", "busily", "daily", "freely", "hungrily", "joyously", "knowlingly", "lazily", "oddly", "mysteriously", "noisily", "politely", "quickly", "quietly", "rapidly", "safely", "sleepily", "slowly", "truly", "yearly"]; function random(arr) { return arr[Math.floor(Math.random() * arr.length)]; } function longest(arr) { return arr.reduce(function(a, b) { return a.length > b.length ? a : b; }); } function shortest(arr) { return arr.reduce(function(a, b) { return a.length < b.length ? a : b; }); } function humanId(options) { if (options === void 0) { options = {}; } if (typeof options === "string") options = { separator: options }; if (typeof options === "boolean") options = { capitalize: options }; var _a = options.separator, separator = _a === void 0 ? "" : _a, _b = options.capitalize, capitalize = _b === void 0 ? true : _b, _c = options.adjectiveCount, adjectiveCount = _c === void 0 ? 1 : _c, _d = options.addAdverb, addAdverb = _d === void 0 ? false : _d; var res = __spreadArray(__spreadArray(__spreadArray([], __spreadArray([], Array(adjectiveCount), true).map(function(_) { return random(exports.adjectives); }), true), [ random(exports.nouns), random(exports.verbs) ], false), addAdverb ? [random(exports.adverbs)] : [], true); if (capitalize) res = res.map(function(r) { return r.charAt(0).toUpperCase() + r.substr(1); }); return res.join(separator); } exports.humanId = humanId; function poolSize(options) { if (options === void 0) { options = {}; } var _a = options.adjectiveCount, adjectiveCount = _a === void 0 ? 1 : _a, _b = options.addAdverb, addAdverb = _b === void 0 ? false : _b; return exports.adjectives.length * adjectiveCount * exports.nouns.length * exports.verbs.length * (addAdverb ? exports.adverbs.length : 1); } exports.poolSize = poolSize; function maxLength(options) { if (options === void 0) { options = {}; } var _a = options.adjectiveCount, adjectiveCount = _a === void 0 ? 1 : _a, _b = options.addAdverb, addAdverb = _b === void 0 ? false : _b, _c = options.separator, separator = _c === void 0 ? "" : _c; return longest(exports.adjectives).length * adjectiveCount + adjectiveCount * separator.length + longest(exports.nouns).length + separator.length + longest(exports.verbs).length + (addAdverb ? longest(exports.adverbs).length + separator.length : 0); } exports.maxLength = maxLength; function minLength(options) { if (options === void 0) { options = {}; } var _a = options.adjectiveCount, adjectiveCount = _a === void 0 ? 1 : _a, _b = options.addAdverb, addAdverb = _b === void 0 ? false : _b, _c = options.separator, separator = _c === void 0 ? "" : _c; return shortest(exports.adjectives).length * adjectiveCount + adjectiveCount * separator.length + shortest(exports.nouns).length + separator.length + shortest(exports.verbs).length + (addAdverb ? shortest(exports.adverbs).length + separator.length : 0); } exports.minLength = minLength; exports.default = humanId; } }); // src/index.js run(); document.addEventListener("pjax:end", () => run()); document.addEventListener("turbo:render", () => run()); async function run() { if (/^\/.+?\/.+?\/pull\/.+$/.exec(location.pathname) && // Skip if sidebar is already added !document.querySelector(".sidebar-changesets") && await repoHasChangesetsSetup()) { if (shouldRemoveChangesetBotComment) { removeChangesetBotComment(); } const updatedPackages = await prHasChangesetFiles(); await addChangesetSideSection(updatedPackages); } } async function repoHasChangesetsSetup() { const orgRepo = window.location.pathname.split("/").slice(1, 3).join("/"); const baseBranch = document.querySelector(".commit-ref").title.split(":")[1].trim(); const cacheKey = `github-changesets-userscript:repoHasChangesetsSetup-${orgRepo}-${baseBranch}`; const cacheValue = sessionStorage.getItem(cacheKey); if (!shouldSkipCache && cacheValue) return cacheValue === "true"; const changesetsFolderUrl = `https://github.com/${orgRepo}/tree/${baseBranch}/.changeset`; const response = await fetch(changesetsFolderUrl, { method: "HEAD" }); const result = response.status === 200; sessionStorage.setItem(cacheKey, result); return result; } async function prHasChangesetFiles() { const orgRepo = window.location.pathname.split("/").slice(1, 3).join("/"); const prNumber = window.location.pathname.split("/").pop(); const allCommitTimeline = document.querySelectorAll( ".js-timeline-item:has(svg.octicon-git-commit) a.markdown-title" ); const prCommitSha = allCommitTimeline[allCommitTimeline.length - 1].href.split("/").slice(-1).join("").slice(0, 7); const cacheKey = `github-changesets-userscript:prHasChangesetFiles-${orgRepo}-${prNumber}-${prCommitSha}`; const cacheValue = sessionStorage.getItem(cacheKey); if (!shouldSkipCache && cacheValue) return JSON.parse(cacheValue); const filesUrl = `https://api.github.com/repos/${orgRepo}/pulls/${prNumber}/files`; const response = await fetch(filesUrl); const files = await response.json(); const hasChangesetFiles = files.some( (file) => file.filename.startsWith(".changeset/") ); if (hasChangesetFiles) { const updatedPackages = await getUpdatedPackagesFromAddedChangedFiles(files); sessionStorage.setItem(cacheKey, JSON.stringify(updatedPackages)); return updatedPackages; } else { sessionStorage.setItem(cacheKey, "{}"); return {}; } } async function addChangesetSideSection(updatedPackages) { const { humanId } = await Promise.resolve().then(() => __toESM(require_dist(), 1)); updatedPackages = sortUpdatedPackages(updatedPackages); const headRef = document.querySelector(".commit-ref.head-ref > a").title; const orgRepo = headRef.split(":")[0].trim(); const branch = headRef.split(":")[1].trim(); const prTitle = document.querySelector(".js-issue-title").textContent.trim(); const changesetFileName = `.changeset/${humanId({ separator: "-", capitalize: false })}.md`; const changesetFileContent = `--- "package": patch --- ${prTitle} `; const canEditPr = !!document.querySelector("button.js-title-edit-button"); const isPrOpen = !!document.querySelector(".gh-header .State.State--open"); const notificationsSideSection = document.querySelector( ".discussion-sidebar-item.sidebar-notifications" ); const plusIcon = `<svg class="octicon octicon-plus" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path d="M7.75 2a.75.75 0 0 1 .75.75V7h4.25a.75.75 0 0 1 0 1.5H8.5v4.25a.75.75 0 0 1-1.5 0V8.5H2.75a.75.75 0 0 1 0-1.5H7V2.75A.75.75 0 0 1 7.75 2Z"></path></svg>`; let html = canEditPr && isPrOpen ? `<a class="d-block text-bold discussion-sidebar-heading discussion-sidebar-toggle" href="https://github.com/${orgRepo}/new/${branch}?filename=${changesetFileName}&value=${encodeURIComponent( changesetFileContent )}">Changesets ${plusIcon}</a>` : `<div class="d-block text-bold discussion-sidebar-heading">Changesets</div>`; if (Object.keys(updatedPackages).length) { html += `<table style="width: 100%; max-width: 400px;"> <tbody> ${Object.entries(updatedPackages).map(([pkg, bumpInfos]) => { const bumpElements = bumpInfos.map((info) => { if (info.diff) { return `<a class="Link--muted" href="${location.origin + location.pathname}/files#diff-${info.diff}">${info.type}</a>`; } else { return info.type; } }); return `<tr> <td style="width: 1px; white-space: nowrap; padding-right: 8px; vertical-align: top;">${pkg}</td> <td class="color-fg-muted">${bumpElements.join(", ")}</td> </tr>`; }).join("")} </tbody> </table>`; } const changesetSideSection = document.createElement("div"); changesetSideSection.className = "discussion-sidebar-item sidebar-changesets"; changesetSideSection.innerHTML = html; notificationsSideSection.before(changesetSideSection); } function removeChangesetBotComment() { const changesetBotComment = document.querySelector( '.js-timeline-item:has(a.author[href="/apps/changeset-bot"])' ); if (changesetBotComment) { changesetBotComment.remove(); } } async function getUpdatedPackagesFromAddedChangedFiles(changedFiles) { const map = {}; for (const file of changedFiles) { if (file.filename.startsWith(".changeset/") && file.status === "added") { const lines = parseAddedPatchStringAsLines(file.patch); let isInYaml = false; for (let i = 0; i < lines.length; i++) { const line = lines[i]; if (line === "---") { isInYaml = !isInYaml; if (isInYaml) continue; else break; } const match = /^['"](.+?)['"]:\s*(major|minor|patch)\s*$/.exec(line); if (!match) continue; const pkg = match[1]; const type = match[2]; const diff = await getAddedDiff(file.filename, i + 1); const packages = map[pkg] || []; packages.push({ type, diff }); map[pkg] = packages; } } } return map; } function parseAddedPatchStringAsLines(patch) { return patch.replace(/^@@.*?@@$\n/m, "").replace(/^\+/gm, "").split("\n"); } async function getAddedDiff(filename, line) { if (window.isSecureContext && window.crypto && window.crypto.subtle) { const filenameSha256 = await sha256(filename); return `${filenameSha256}R${line}`; } } async function sha256(message) { const encoder = new TextEncoder(); const data = encoder.encode(message); const hashBuffer = await crypto.subtle.digest("SHA-256", data); const hashArray = Array.from(new Uint8Array(hashBuffer)); const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join(""); return hashHex; } function sortUpdatedPackages(map) { const newMap = {}; for (const key of Object.keys(map).sort()) { const order = { major: 1, minor: 2, patch: 3 }; const value = [...map[key]].sort((a, b) => { return order[a.type] - order[b.type]; }); newMap[key] = value; } return newMap; } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址