UNPKG

danger-plugin-pr-hygiene

Version:
2,255 lines (2,074 loc) 60.5 kB
'use strict'; // Values marked with @internal are not part of the public API and may change // without notice. class CustomType { withFields(fields) { let properties = Object.keys(this).map((label) => label in fields ? fields[label] : this[label], ); return new this.constructor(...properties); } } class List { static fromArray(array, tail) { let t = tail || new Empty(); for (let i = array.length - 1; i >= 0; --i) { t = new NonEmpty(array[i], t); } return t; } [Symbol.iterator]() { return new ListIterator(this); } toArray() { return [...this]; } // @internal atLeastLength(desired) { for (let _ of this) { if (desired <= 0) return true; desired--; } return desired <= 0; } // @internal hasLength(desired) { for (let _ of this) { if (desired <= 0) return false; desired--; } return desired === 0; } // @internal countLength() { let length = 0; for (let _ of this) length++; return length; } } // @internal function prepend(element, tail) { return new NonEmpty(element, tail); } function toList(elements, tail) { return List.fromArray(elements, tail); } // @internal class ListIterator { #current; constructor(current) { this.#current = current; } next() { if (this.#current instanceof Empty) { return { done: true }; } else { let { head, tail } = this.#current; this.#current = tail; return { value: head, done: false }; } } } class Empty extends List {} class NonEmpty extends List { constructor(head, tail) { super(); this.head = head; this.tail = tail; } } class Result extends CustomType { // @internal static isResult(data) { return data instanceof Result; } } class Ok extends Result { constructor(value) { super(); this[0] = value; } // @internal isOk() { return true; } } class Error extends Result { constructor(detail) { super(); this[0] = detail; } // @internal isOk() { return false; } } function isEqual(x, y) { let values = [x, y]; while (values.length) { let a = values.pop(); let b = values.pop(); if (a === b) continue; if (!isObject(a) || !isObject(b)) return false; let unequal = !structurallyCompatibleObjects(a, b) || unequalDates(a, b) || unequalBuffers(a, b) || unequalArrays(a, b) || unequalMaps(a, b) || unequalSets(a, b) || unequalRegExps(a, b); if (unequal) return false; const proto = Object.getPrototypeOf(a); if (proto !== null && typeof proto.equals === "function") { try { if (a.equals(b)) continue; else return false; } catch {} } let [keys, get] = getters(a); for (let k of keys(a)) { values.push(get(a, k), get(b, k)); } } return true; } function getters(object) { if (object instanceof Map) { return [(x) => x.keys(), (x, y) => x.get(y)]; } else { let extra = object instanceof globalThis.Error ? ["message"] : []; return [(x) => [...extra, ...Object.keys(x)], (x, y) => x[y]]; } } function unequalDates(a, b) { return a instanceof Date && (a > b || a < b); } function unequalBuffers(a, b) { return ( a.buffer instanceof ArrayBuffer && a.BYTES_PER_ELEMENT && !(a.byteLength === b.byteLength && a.every((n, i) => n === b[i])) ); } function unequalArrays(a, b) { return Array.isArray(a) && a.length !== b.length; } function unequalMaps(a, b) { return a instanceof Map && a.size !== b.size; } function unequalSets(a, b) { return ( a instanceof Set && (a.size != b.size || [...a].some((e) => !b.has(e))) ); } function unequalRegExps(a, b) { return a instanceof RegExp && (a.source !== b.source || a.flags !== b.flags); } function isObject(a) { return typeof a === "object" && a !== null; } function structurallyCompatibleObjects(a, b) { if (typeof a !== "object" && typeof b !== "object" && (!a || !b)) return false; let nonstructural = [Promise, WeakSet, WeakMap, Function]; if (nonstructural.some((c) => a instanceof c)) return false; return a.constructor === b.constructor; } // @internal function remainderInt(a, b) { if (b === 0) { return 0; } else { return a % b; } } // @internal function divideInt(a, b) { return Math.trunc(divideFloat(a, b)); } // @internal function divideFloat(a, b) { if (b === 0) { return 0; } else { return a / b; } } // @internal function makeError(variant, module, line, fn, message, extra) { let error = new globalThis.Error(message); error.gleam_error = variant; error.module = module; error.line = line; error.function = fn; // TODO: Remove this with Gleam v2.0.0 error.fn = fn; for (let k in extra) error[k] = extra[k]; return error; } /// <reference types="./option.d.mts" /> class Some extends CustomType { constructor(x0) { super(); this[0] = x0; } } class None extends CustomType {} /// <reference types="./regex.d.mts" /> class Match extends CustomType { constructor(content, submatches) { super(); this.content = content; this.submatches = submatches; } } class CompileError extends CustomType { constructor(error, byte_index) { super(); this.error = error; this.byte_index = byte_index; } } class Options extends CustomType { constructor(case_insensitive, multi_line) { super(); this.case_insensitive = case_insensitive; this.multi_line = multi_line; } } function compile(pattern, options) { return compile_regex(pattern, options); } function from_string$1(pattern) { return compile(pattern, new Options(false, false)); } function check(regex, content) { return regex_check(regex, content); } function scan(regex, string) { return regex_scan(regex, string); } /// <reference types="./result.d.mts" /> function is_ok(result) { if (!result.isOk()) { return false; } else { return true; } } function map_error(result, fun) { if (result.isOk()) { let x = result[0]; return new Ok(x); } else { let error = result[0]; return new Error(fun(error)); } } /// <reference types="./iterator.d.mts" /> class Stop extends CustomType {} class Continue extends CustomType { constructor(x0, x1) { super(); this[0] = x0; this[1] = x1; } } class Iterator extends CustomType { constructor(continuation) { super(); this.continuation = continuation; } } class Next extends CustomType { constructor(element, accumulator) { super(); this.element = element; this.accumulator = accumulator; } } function stop() { return new Stop(); } function do_unfold(initial, f) { return () => { let $ = f(initial); if ($ instanceof Next) { let x = $.element; let acc = $.accumulator; return new Continue(x, do_unfold(acc, f)); } else { return new Stop(); } }; } function unfold(initial, f) { let _pipe = initial; let _pipe$1 = do_unfold(_pipe, f); return new Iterator(_pipe$1); } function repeatedly(f) { return unfold(undefined, (_) => { return new Next(f(), undefined); }); } function repeat$1(x) { return repeatedly(() => { return x; }); } function do_fold(loop$continuation, loop$f, loop$accumulator) { while (true) { let continuation = loop$continuation; let f = loop$f; let accumulator = loop$accumulator; let $ = continuation(); if ($ instanceof Continue) { let elem = $[0]; let next = $[1]; loop$continuation = next; loop$f = f; loop$accumulator = f(accumulator, elem); } else { return accumulator; } } } function fold$1(iterator, initial, f) { let _pipe = iterator.continuation; return do_fold(_pipe, f, initial); } function to_list(iterator) { let _pipe = iterator; let _pipe$1 = fold$1( _pipe, toList([]), (acc, e) => { return prepend(e, acc); }, ); return reverse(_pipe$1); } function do_take$1(continuation, desired) { return () => { let $ = desired > 0; if (!$) { return new Stop(); } else { let $1 = continuation(); if ($1 instanceof Stop) { return new Stop(); } else { let e = $1[0]; let next = $1[1]; return new Continue(e, do_take$1(next, desired - 1)); } } }; } function take$1(iterator, desired) { let _pipe = iterator.continuation; let _pipe$1 = do_take$1(_pipe, desired); return new Iterator(_pipe$1); } function do_append(first, second) { let $ = first(); if ($ instanceof Continue) { let e = $[0]; let first$1 = $[1]; return new Continue(e, () => { return do_append(first$1, second); }); } else { return second(); } } function append(first, second) { let _pipe = () => { return do_append(first.continuation, second.continuation); }; return new Iterator(_pipe); } function once(f) { let _pipe = () => { return new Continue(f(), stop); }; return new Iterator(_pipe); } function single(elem) { return once(() => { return elem; }); } /// <reference types="./string_builder.d.mts" /> function from_strings(strings) { return concat(strings); } function concat$2(builders) { return concat(builders); } function from_string(string) { return identity(string); } function to_string(builder) { return identity(builder); } function split$2(iodata, pattern) { return split(iodata, pattern); } /// <reference types="./string.d.mts" /> function length$1(string) { return string_length(string); } function lowercase$1(string) { return lowercase(string); } function ends_with$1(string, suffix) { return ends_with(string, suffix); } function concat$1(strings) { let _pipe = strings; let _pipe$1 = from_strings(_pipe); return to_string(_pipe$1); } function repeat(string, times) { let _pipe = repeat$1(string); let _pipe$1 = take$1(_pipe, times); let _pipe$2 = to_list(_pipe$1); return concat$1(_pipe$2); } function trim$1(string) { return trim(string); } function do_slice(string, idx, len) { let _pipe = string; let _pipe$1 = graphemes(_pipe); let _pipe$2 = drop(_pipe$1); let _pipe$3 = take(_pipe$2, len); return concat$1(_pipe$3); } function slice(string, idx, len) { let $ = len < 0; if ($) { return ""; } else { { return do_slice(string, idx, len); } } } function split$1(x, substring) { { let _pipe = x; let _pipe$1 = from_string(_pipe); let _pipe$2 = split$2(_pipe$1, substring); return map(_pipe$2, to_string); } } function padding(size, pad_string) { let pad_length = length$1(pad_string); let num_pads = divideInt(size, pad_length); let extra = remainderInt(size, pad_length); let _pipe = repeat$1(pad_string); let _pipe$1 = take$1(_pipe, num_pads); return append( _pipe$1, single(slice(pad_string, 0, extra)), ); } function pad_left(string, desired_length, pad_string) { let current_length = length$1(string); let to_pad_length = desired_length - current_length; let _pipe = padding(to_pad_length, pad_string); let _pipe$1 = append(_pipe, single(string)); let _pipe$2 = to_list(_pipe$1); return concat$1(_pipe$2); } /** * This file uses jsdoc to annotate types. * These types can be checked using the typescript compiler with "checkjs" option. */ const referenceMap = new WeakMap(); const tempDataView = new DataView(new ArrayBuffer(8)); let referenceUID = 0; /** * hash the object by reference using a weak map and incrementing uid * @param {any} o * @returns {number} */ function hashByReference(o) { const known = referenceMap.get(o); if (known !== undefined) { return known; } const hash = referenceUID++; if (referenceUID === 0x7fffffff) { referenceUID = 0; } referenceMap.set(o, hash); return hash; } /** * merge two hashes in an order sensitive way * @param {number} a * @param {number} b * @returns {number} */ function hashMerge(a, b) { return (a ^ (b + 0x9e3779b9 + (a << 6) + (a >> 2))) | 0; } /** * standard string hash popularised by java * @param {string} s * @returns {number} */ function hashString(s) { let hash = 0; const len = s.length; for (let i = 0; i < len; i++) { hash = (Math.imul(31, hash) + s.charCodeAt(i)) | 0; } return hash; } /** * hash a number by converting to two integers and do some jumbling * @param {number} n * @returns {number} */ function hashNumber(n) { tempDataView.setFloat64(0, n); const i = tempDataView.getInt32(0); const j = tempDataView.getInt32(4); return Math.imul(0x45d9f3b, (i >> 16) ^ i) ^ j; } /** * hash a BigInt by converting it to a string and hashing that * @param {BigInt} n * @returns {number} */ function hashBigInt(n) { return hashString(n.toString()); } /** * hash any js object * @param {any} o * @returns {number} */ function hashObject(o) { const proto = Object.getPrototypeOf(o); if (proto !== null && typeof proto.hashCode === "function") { try { const code = o.hashCode(o); if (typeof code === "number") { return code; } } catch {} } if (o instanceof Promise || o instanceof WeakSet || o instanceof WeakMap) { return hashByReference(o); } if (o instanceof Date) { return hashNumber(o.getTime()); } let h = 0; if (o instanceof ArrayBuffer) { o = new Uint8Array(o); } if (Array.isArray(o) || o instanceof Uint8Array) { for (let i = 0; i < o.length; i++) { h = (Math.imul(31, h) + getHash(o[i])) | 0; } } else if (o instanceof Set) { o.forEach((v) => { h = (h + getHash(v)) | 0; }); } else if (o instanceof Map) { o.forEach((v, k) => { h = (h + hashMerge(getHash(v), getHash(k))) | 0; }); } else { const keys = Object.keys(o); for (let i = 0; i < keys.length; i++) { const k = keys[i]; const v = o[k]; h = (h + hashMerge(getHash(v), hashString(k))) | 0; } } return h; } /** * hash any js value * @param {any} u * @returns {number} */ function getHash(u) { if (u === null) return 0x42108422; if (u === undefined) return 0x42108423; if (u === true) return 0x42108421; if (u === false) return 0x42108420; switch (typeof u) { case "number": return hashNumber(u); case "string": return hashString(u); case "bigint": return hashBigInt(u); case "object": return hashObject(u); case "symbol": return hashByReference(u); case "function": return hashByReference(u); default: return 0; // should be unreachable } } /** * @template K,V * @typedef {ArrayNode<K,V> | IndexNode<K,V> | CollisionNode<K,V>} Node */ /** * @template K,V * @typedef {{ type: typeof ENTRY, k: K, v: V }} Entry */ /** * @template K,V * @typedef {{ type: typeof ARRAY_NODE, size: number, array: (undefined | Entry<K,V> | Node<K,V>)[] }} ArrayNode */ /** * @template K,V * @typedef {{ type: typeof INDEX_NODE, bitmap: number, array: (Entry<K,V> | Node<K,V>)[] }} IndexNode */ /** * @template K,V * @typedef {{ type: typeof COLLISION_NODE, hash: number, array: Entry<K, V>[] }} CollisionNode */ /** * @typedef {{ val: boolean }} Flag */ const SHIFT = 5; // number of bits you need to shift by to get the next bucket const BUCKET_SIZE = Math.pow(2, SHIFT); const MASK = BUCKET_SIZE - 1; // used to zero out all bits not in the bucket const MAX_INDEX_NODE = BUCKET_SIZE / 2; // when does index node grow into array node const MIN_ARRAY_NODE = BUCKET_SIZE / 4; // when does array node shrink to index node const ENTRY = 0; const ARRAY_NODE = 1; const INDEX_NODE = 2; const COLLISION_NODE = 3; /** @type {IndexNode<any,any>} */ const EMPTY = { type: INDEX_NODE, bitmap: 0, array: [], }; /** * Mask the hash to get only the bucket corresponding to shift * @param {number} hash * @param {number} shift * @returns {number} */ function mask(hash, shift) { return (hash >>> shift) & MASK; } /** * Set only the Nth bit where N is the masked hash * @param {number} hash * @param {number} shift * @returns {number} */ function bitpos(hash, shift) { return 1 << mask(hash, shift); } /** * Count the number of 1 bits in a number * @param {number} x * @returns {number} */ function bitcount(x) { x -= (x >> 1) & 0x55555555; x = (x & 0x33333333) + ((x >> 2) & 0x33333333); x = (x + (x >> 4)) & 0x0f0f0f0f; x += x >> 8; x += x >> 16; return x & 0x7f; } /** * Calculate the array index of an item in a bitmap index node * @param {number} bitmap * @param {number} bit * @returns {number} */ function index(bitmap, bit) { return bitcount(bitmap & (bit - 1)); } /** * Efficiently copy an array and set one value at an index * @template T * @param {T[]} arr * @param {number} at * @param {T} val * @returns {T[]} */ function cloneAndSet(arr, at, val) { const len = arr.length; const out = new Array(len); for (let i = 0; i < len; ++i) { out[i] = arr[i]; } out[at] = val; return out; } /** * Efficiently copy an array and insert one value at an index * @template T * @param {T[]} arr * @param {number} at * @param {T} val * @returns {T[]} */ function spliceIn(arr, at, val) { const len = arr.length; const out = new Array(len + 1); let i = 0; let g = 0; while (i < at) { out[g++] = arr[i++]; } out[g++] = val; while (i < len) { out[g++] = arr[i++]; } return out; } /** * Efficiently copy an array and remove one value at an index * @template T * @param {T[]} arr * @param {number} at * @returns {T[]} */ function spliceOut(arr, at) { const len = arr.length; const out = new Array(len - 1); let i = 0; let g = 0; while (i < at) { out[g++] = arr[i++]; } ++i; while (i < len) { out[g++] = arr[i++]; } return out; } /** * Create a new node containing two entries * @template K,V * @param {number} shift * @param {K} key1 * @param {V} val1 * @param {number} key2hash * @param {K} key2 * @param {V} val2 * @returns {Node<K,V>} */ function createNode(shift, key1, val1, key2hash, key2, val2) { const key1hash = getHash(key1); if (key1hash === key2hash) { return { type: COLLISION_NODE, hash: key1hash, array: [ { type: ENTRY, k: key1, v: val1 }, { type: ENTRY, k: key2, v: val2 }, ], }; } const addedLeaf = { val: false }; return assoc( assocIndex(EMPTY, shift, key1hash, key1, val1, addedLeaf), shift, key2hash, key2, val2, addedLeaf ); } /** * @template T,K,V * @callback AssocFunction * @param {T} root * @param {number} shift * @param {number} hash * @param {K} key * @param {V} val * @param {Flag} addedLeaf * @returns {Node<K,V>} */ /** * Associate a node with a new entry, creating a new node * @template T,K,V * @type {AssocFunction<Node<K,V>,K,V>} */ function assoc(root, shift, hash, key, val, addedLeaf) { switch (root.type) { case ARRAY_NODE: return assocArray(root, shift, hash, key, val, addedLeaf); case INDEX_NODE: return assocIndex(root, shift, hash, key, val, addedLeaf); case COLLISION_NODE: return assocCollision(root, shift, hash, key, val, addedLeaf); } } /** * @template T,K,V * @type {AssocFunction<ArrayNode<K,V>,K,V>} */ function assocArray(root, shift, hash, key, val, addedLeaf) { const idx = mask(hash, shift); const node = root.array[idx]; // if the corresponding index is empty set the index to a newly created node if (node === undefined) { addedLeaf.val = true; return { type: ARRAY_NODE, size: root.size + 1, array: cloneAndSet(root.array, idx, { type: ENTRY, k: key, v: val }), }; } if (node.type === ENTRY) { // if keys are equal replace the entry if (isEqual(key, node.k)) { if (val === node.v) { return root; } return { type: ARRAY_NODE, size: root.size, array: cloneAndSet(root.array, idx, { type: ENTRY, k: key, v: val, }), }; } // otherwise upgrade the entry to a node and insert addedLeaf.val = true; return { type: ARRAY_NODE, size: root.size, array: cloneAndSet( root.array, idx, createNode(shift + SHIFT, node.k, node.v, hash, key, val) ), }; } // otherwise call assoc on the child node const n = assoc(node, shift + SHIFT, hash, key, val, addedLeaf); // if the child node hasn't changed just return the old root if (n === node) { return root; } // otherwise set the index to the new node return { type: ARRAY_NODE, size: root.size, array: cloneAndSet(root.array, idx, n), }; } /** * @template T,K,V * @type {AssocFunction<IndexNode<K,V>,K,V>} */ function assocIndex(root, shift, hash, key, val, addedLeaf) { const bit = bitpos(hash, shift); const idx = index(root.bitmap, bit); // if there is already a item at this hash index.. if ((root.bitmap & bit) !== 0) { // if there is a node at the index (not an entry), call assoc on the child node const node = root.array[idx]; if (node.type !== ENTRY) { const n = assoc(node, shift + SHIFT, hash, key, val, addedLeaf); if (n === node) { return root; } return { type: INDEX_NODE, bitmap: root.bitmap, array: cloneAndSet(root.array, idx, n), }; } // otherwise there is an entry at the index // if the keys are equal replace the entry with the updated value const nodeKey = node.k; if (isEqual(key, nodeKey)) { if (val === node.v) { return root; } return { type: INDEX_NODE, bitmap: root.bitmap, array: cloneAndSet(root.array, idx, { type: ENTRY, k: key, v: val, }), }; } // if the keys are not equal, replace the entry with a new child node addedLeaf.val = true; return { type: INDEX_NODE, bitmap: root.bitmap, array: cloneAndSet( root.array, idx, createNode(shift + SHIFT, nodeKey, node.v, hash, key, val) ), }; } else { // else there is currently no item at the hash index const n = root.array.length; // if the number of nodes is at the maximum, expand this node into an array node if (n >= MAX_INDEX_NODE) { // create a 32 length array for the new array node (one for each bit in the hash) const nodes = new Array(32); // create and insert a node for the new entry const jdx = mask(hash, shift); nodes[jdx] = assocIndex(EMPTY, shift + SHIFT, hash, key, val, addedLeaf); let j = 0; let bitmap = root.bitmap; // place each item in the index node into the correct spot in the array node // loop through all 32 bits / array positions for (let i = 0; i < 32; i++) { if ((bitmap & 1) !== 0) { const node = root.array[j++]; nodes[i] = node; } // shift the bitmap to process the next bit bitmap = bitmap >>> 1; } return { type: ARRAY_NODE, size: n + 1, array: nodes, }; } else { // else there is still space in this index node // simply insert a new entry at the hash index const newArray = spliceIn(root.array, idx, { type: ENTRY, k: key, v: val, }); addedLeaf.val = true; return { type: INDEX_NODE, bitmap: root.bitmap | bit, array: newArray, }; } } } /** * @template T,K,V * @type {AssocFunction<CollisionNode<K,V>,K,V>} */ function assocCollision(root, shift, hash, key, val, addedLeaf) { // if there is a hash collision if (hash === root.hash) { const idx = collisionIndexOf(root, key); // if this key already exists replace the entry with the new value if (idx !== -1) { const entry = root.array[idx]; if (entry.v === val) { return root; } return { type: COLLISION_NODE, hash: hash, array: cloneAndSet(root.array, idx, { type: ENTRY, k: key, v: val }), }; } // otherwise insert the entry at the end of the array const size = root.array.length; addedLeaf.val = true; return { type: COLLISION_NODE, hash: hash, array: cloneAndSet(root.array, size, { type: ENTRY, k: key, v: val }), }; } // if there is no hash collision, upgrade to an index node return assoc( { type: INDEX_NODE, bitmap: bitpos(root.hash, shift), array: [root], }, shift, hash, key, val, addedLeaf ); } /** * Find the index of a key in the collision node's array * @template K,V * @param {CollisionNode<K,V>} root * @param {K} key * @returns {number} */ function collisionIndexOf(root, key) { const size = root.array.length; for (let i = 0; i < size; i++) { if (isEqual(key, root.array[i].k)) { return i; } } return -1; } /** * @template T,K,V * @callback FindFunction * @param {T} root * @param {number} shift * @param {number} hash * @param {K} key * @returns {undefined | Entry<K,V>} */ /** * Return the found entry or undefined if not present in the root * @template K,V * @type {FindFunction<Node<K,V>,K,V>} */ function find(root, shift, hash, key) { switch (root.type) { case ARRAY_NODE: return findArray(root, shift, hash, key); case INDEX_NODE: return findIndex(root, shift, hash, key); case COLLISION_NODE: return findCollision(root, key); } } /** * @template K,V * @type {FindFunction<ArrayNode<K,V>,K,V>} */ function findArray(root, shift, hash, key) { const idx = mask(hash, shift); const node = root.array[idx]; if (node === undefined) { return undefined; } if (node.type !== ENTRY) { return find(node, shift + SHIFT, hash, key); } if (isEqual(key, node.k)) { return node; } return undefined; } /** * @template K,V * @type {FindFunction<IndexNode<K,V>,K,V>} */ function findIndex(root, shift, hash, key) { const bit = bitpos(hash, shift); if ((root.bitmap & bit) === 0) { return undefined; } const idx = index(root.bitmap, bit); const node = root.array[idx]; if (node.type !== ENTRY) { return find(node, shift + SHIFT, hash, key); } if (isEqual(key, node.k)) { return node; } return undefined; } /** * @template K,V * @param {CollisionNode<K,V>} root * @param {K} key * @returns {undefined | Entry<K,V>} */ function findCollision(root, key) { const idx = collisionIndexOf(root, key); if (idx < 0) { return undefined; } return root.array[idx]; } /** * @template T,K,V * @callback WithoutFunction * @param {T} root * @param {number} shift * @param {number} hash * @param {K} key * @returns {undefined | Node<K,V>} */ /** * Remove an entry from the root, returning the updated root. * Returns undefined if the node should be removed from the parent. * @template K,V * @type {WithoutFunction<Node<K,V>,K,V>} * */ function without(root, shift, hash, key) { switch (root.type) { case ARRAY_NODE: return withoutArray(root, shift, hash, key); case INDEX_NODE: return withoutIndex(root, shift, hash, key); case COLLISION_NODE: return withoutCollision(root, key); } } /** * @template K,V * @type {WithoutFunction<ArrayNode<K,V>,K,V>} */ function withoutArray(root, shift, hash, key) { const idx = mask(hash, shift); const node = root.array[idx]; if (node === undefined) { return root; // already empty } let n = undefined; // if node is an entry and the keys are not equal there is nothing to remove // if node is not an entry do a recursive call if (node.type === ENTRY) { if (!isEqual(node.k, key)) { return root; // no changes } } else { n = without(node, shift + SHIFT, hash, key); if (n === node) { return root; // no changes } } // if the recursive call returned undefined the node should be removed if (n === undefined) { // if the number of child nodes is at the minimum, pack into an index node if (root.size <= MIN_ARRAY_NODE) { const arr = root.array; const out = new Array(root.size - 1); let i = 0; let j = 0; let bitmap = 0; while (i < idx) { const nv = arr[i]; if (nv !== undefined) { out[j] = nv; bitmap |= 1 << i; ++j; } ++i; } ++i; // skip copying the removed node while (i < arr.length) { const nv = arr[i]; if (nv !== undefined) { out[j] = nv; bitmap |= 1 << i; ++j; } ++i; } return { type: INDEX_NODE, bitmap: bitmap, array: out, }; } return { type: ARRAY_NODE, size: root.size - 1, array: cloneAndSet(root.array, idx, n), }; } return { type: ARRAY_NODE, size: root.size, array: cloneAndSet(root.array, idx, n), }; } /** * @template K,V * @type {WithoutFunction<IndexNode<K,V>,K,V>} */ function withoutIndex(root, shift, hash, key) { const bit = bitpos(hash, shift); if ((root.bitmap & bit) === 0) { return root; // already empty } const idx = index(root.bitmap, bit); const node = root.array[idx]; // if the item is not an entry if (node.type !== ENTRY) { const n = without(node, shift + SHIFT, hash, key); if (n === node) { return root; // no changes } // if not undefined, the child node still has items, so update it if (n !== undefined) { return { type: INDEX_NODE, bitmap: root.bitmap, array: cloneAndSet(root.array, idx, n), }; } // otherwise the child node should be removed // if it was the only child node, remove this node from the parent if (root.bitmap === bit) { return undefined; } // otherwise just remove the child node return { type: INDEX_NODE, bitmap: root.bitmap ^ bit, array: spliceOut(root.array, idx), }; } // otherwise the item is an entry, remove it if the key matches if (isEqual(key, node.k)) { if (root.bitmap === bit) { return undefined; } return { type: INDEX_NODE, bitmap: root.bitmap ^ bit, array: spliceOut(root.array, idx), }; } return root; } /** * @template K,V * @param {CollisionNode<K,V>} root * @param {K} key * @returns {undefined | Node<K,V>} */ function withoutCollision(root, key) { const idx = collisionIndexOf(root, key); // if the key not found, no changes if (idx < 0) { return root; } // otherwise the entry was found, remove it // if it was the only entry in this node, remove the whole node if (root.array.length === 1) { return undefined; } // otherwise just remove the entry return { type: COLLISION_NODE, hash: root.hash, array: spliceOut(root.array, idx), }; } /** * @template K,V * @param {undefined | Node<K,V>} root * @param {(value:V,key:K)=>void} fn * @returns {void} */ function forEach(root, fn) { if (root === undefined) { return; } const items = root.array; const size = items.length; for (let i = 0; i < size; i++) { const item = items[i]; if (item === undefined) { continue; } if (item.type === ENTRY) { fn(item.v, item.k); continue; } forEach(item, fn); } } /** * Extra wrapper to keep track of Dict size and clean up the API * @template K,V */ class Dict { /** * @template V * @param {Record<string,V>} o * @returns {Dict<string,V>} */ static fromObject(o) { const keys = Object.keys(o); /** @type Dict<string,V> */ let m = Dict.new(); for (let i = 0; i < keys.length; i++) { const k = keys[i]; m = m.set(k, o[k]); } return m; } /** * @template K,V * @param {Map<K,V>} o * @returns {Dict<K,V>} */ static fromMap(o) { /** @type Dict<K,V> */ let m = Dict.new(); o.forEach((v, k) => { m = m.set(k, v); }); return m; } static new() { return new Dict(undefined, 0); } /** * @param {undefined | Node<K,V>} root * @param {number} size */ constructor(root, size) { this.root = root; this.size = size; } /** * @template NotFound * @param {K} key * @param {NotFound} notFound * @returns {NotFound | V} */ get(key, notFound) { if (this.root === undefined) { return notFound; } const found = find(this.root, 0, getHash(key), key); if (found === undefined) { return notFound; } return found.v; } /** * @param {K} key * @param {V} val * @returns {Dict<K,V>} */ set(key, val) { const addedLeaf = { val: false }; const root = this.root === undefined ? EMPTY : this.root; const newRoot = assoc(root, 0, getHash(key), key, val, addedLeaf); if (newRoot === this.root) { return this; } return new Dict(newRoot, addedLeaf.val ? this.size + 1 : this.size); } /** * @param {K} key * @returns {Dict<K,V>} */ delete(key) { if (this.root === undefined) { return this; } const newRoot = without(this.root, 0, getHash(key), key); if (newRoot === this.root) { return this; } if (newRoot === undefined) { return Dict.new(); } return new Dict(newRoot, this.size - 1); } /** * @param {K} key * @returns {boolean} */ has(key) { if (this.root === undefined) { return false; } return find(this.root, 0, getHash(key), key) !== undefined; } /** * @returns {[K,V][]} */ entries() { if (this.root === undefined) { return []; } /** @type [K,V][] */ const result = []; this.forEach((v, k) => result.push([k, v])); return result; } /** * * @param {(val:V,key:K)=>void} fn */ forEach(fn) { forEach(this.root, fn); } hashCode() { let h = 0; this.forEach((v, k) => { h = (h + hashMerge(getHash(v), getHash(k))) | 0; }); return h; } /** * @param {unknown} o * @returns {boolean} */ equals(o) { if (!(o instanceof Dict) || this.size !== o.size) { return false; } let equal = true; this.forEach((v, k) => { equal = equal && isEqual(o.get(k, !v), v); }); return equal; } } const Nil = undefined; const NOT_FOUND = {}; function identity(x) { return x; } function string_length(string) { if (string === "") { return 0; } const iterator = graphemes_iterator(string); if (iterator) { let i = 0; for (const _ of iterator) { i++; } return i; } else { return string.match(/./gsu).length; } } function graphemes(string) { const iterator = graphemes_iterator(string); if (iterator) { return List.fromArray(Array.from(iterator).map((item) => item.segment)); } else { return List.fromArray(string.match(/./gsu)); } } function graphemes_iterator(string) { if (globalThis.Intl && Intl.Segmenter) { return new Intl.Segmenter().segment(string)[Symbol.iterator](); } } function lowercase(string) { return string.toLowerCase(); } function split(xs, pattern) { return List.fromArray(xs.split(pattern)); } function concat(xs) { let result = ""; for (const x of xs) { result = result + x; } return result; } function ends_with(haystack, needle) { return haystack.endsWith(needle); } const unicode_whitespaces = [ "\u0020", // Space "\u0009", // Horizontal tab "\u000A", // Line feed "\u000B", // Vertical tab "\u000C", // Form feed "\u000D", // Carriage return "\u0085", // Next line "\u2028", // Line separator "\u2029", // Paragraph separator ].join(""); const left_trim_regex = new RegExp(`^([${unicode_whitespaces}]*)`, "g"); const right_trim_regex = new RegExp(`([${unicode_whitespaces}]*)$`, "g"); function trim(string) { return trim_left(trim_right(string)); } function trim_left(string) { return string.replace(left_trim_regex, ""); } function trim_right(string) { return string.replace(right_trim_regex, ""); } function regex_check(regex, string) { regex.lastIndex = 0; return regex.test(string); } function compile_regex(pattern, options) { try { let flags = "gu"; if (options.case_insensitive) flags += "i"; if (options.multi_line) flags += "m"; return new Ok(new RegExp(pattern, flags)); } catch (error) { const number = (error.columnNumber || 0) | 0; return new Error(new CompileError(error.message, number)); } } function regex_scan(regex, string) { const matches = Array.from(string.matchAll(regex)).map((match) => { const content = match[0]; const submatches = []; for (let n = match.length - 1; n > 0; n--) { if (match[n]) { submatches[n - 1] = new Some(match[n]); continue; } if (submatches.length > 0) { submatches[n - 1] = new None(); } } return new Match(content, List.fromArray(submatches)); }); return List.fromArray(matches); } function new_map() { return Dict.new(); } function map_get(map, key) { const value = map.get(key, NOT_FOUND); if (value === NOT_FOUND) { return new Error(Nil); } return new Ok(value); } function map_insert(key, value, map) { return map.set(key, value); } function percent_encode$1(string) { return encodeURIComponent(string).replace("%2B", "+"); } /// <reference types="./dict.d.mts" /> function new$() { return new_map(); } function get(from, get) { return map_get(from, get); } function insert(dict, key, value) { return map_insert(key, value, dict); } /// <reference types="./list.d.mts" /> function count_length(loop$list, loop$count) { while (true) { let list = loop$list; let count = loop$count; if (list.atLeastLength(1)) { let list$1 = list.tail; loop$list = list$1; loop$count = count + 1; } else { return count; } } } function length(list) { return count_length(list, 0); } function do_reverse(loop$remaining, loop$accumulator) { while (true) { let remaining = loop$remaining; let accumulator = loop$accumulator; if (remaining.hasLength(0)) { return accumulator; } else { let item = remaining.head; let rest$1 = remaining.tail; loop$remaining = rest$1; loop$accumulator = prepend(item, accumulator); } } } function reverse(xs) { return do_reverse(xs, toList([])); } function contains$1(loop$list, loop$elem) { while (true) { let list = loop$list; let elem = loop$elem; if (list.hasLength(0)) { return false; } else if (list.atLeastLength(1) && (isEqual(list.head, elem))) { list.head; return true; } else { let rest$1 = list.tail; loop$list = rest$1; loop$elem = elem; } } } function do_filter_map(loop$list, loop$fun, loop$acc) { while (true) { let list = loop$list; let fun = loop$fun; let acc = loop$acc; if (list.hasLength(0)) { return reverse(acc); } else { let x = list.head; let xs = list.tail; let new_acc = (() => { let $ = fun(x); if ($.isOk()) { let x$1 = $[0]; return prepend(x$1, acc); } else { return acc; } })(); loop$list = xs; loop$fun = fun; loop$acc = new_acc; } } } function filter_map(list, fun) { return do_filter_map(list, fun, toList([])); } function do_map(loop$list, loop$fun, loop$acc) { while (true) { let list = loop$list; let fun = loop$fun; let acc = loop$acc; if (list.hasLength(0)) { return reverse(acc); } else { let x = list.head; let xs = list.tail; loop$list = xs; loop$fun = fun; loop$acc = prepend(fun(x), acc); } } } function map(list, fun) { return do_map(list, fun, toList([])); } function drop(loop$list, loop$n) { while (true) { let list = loop$list; { return list; } } } function do_take(loop$list, loop$n, loop$acc) { while (true) { let list = loop$list; let n = loop$n; let acc = loop$acc; let $ = n <= 0; if ($) { return reverse(acc); } else { if (list.hasLength(0)) { return reverse(acc); } else { let x = list.head; let xs = list.tail; loop$list = xs; loop$n = n - 1; loop$acc = prepend(x, acc); } } } } function take(list, n) { return do_take(list, n, toList([])); } function fold(loop$list, loop$initial, loop$fun) { while (true) { let list = loop$list; let initial = loop$initial; let fun = loop$fun; if (list.hasLength(0)) { return initial; } else { let x = list.head; let rest$1 = list.tail; loop$list = rest$1; loop$initial = fun(initial, x); loop$fun = fun; } } } function do_intersperse(loop$list, loop$separator, loop$acc) { while (true) { let list = loop$list; let separator = loop$separator; let acc = loop$acc; if (list.hasLength(0)) { return reverse(acc); } else { let x = list.head; let rest$1 = list.tail; loop$list = rest$1; loop$separator = separator; loop$acc = prepend(x, prepend(separator, acc)); } } } function intersperse(list, elem) { if (list.hasLength(0)) { return list; } else if (list.hasLength(1)) { return list; } else { let x = list.head; let rest$1 = list.tail; return do_intersperse(rest$1, elem, toList([x])); } } function each(loop$list, loop$f) { while (true) { let list = loop$list; let f = loop$f; if (list.hasLength(0)) { return undefined; } else { let x = list.head; let xs = list.tail; f(x); loop$list = xs; loop$f = f; } } } function do_take_while(loop$list, loop$predicate, loop$acc) { while (true) { let list = loop$list; let predicate = loop$predicate; let acc = loop$acc; if (list.hasLength(0)) { return reverse(acc); } else { let first$1 = list.head; let rest$1 = list.tail; let $ = predicate(first$1); if ($) { loop$list = rest$1; loop$predicate = predicate; loop$acc = prepend(first$1, acc); } else { return reverse(acc); } } } } function take_while(list, predicate) { return do_take_while(list, predicate, toList([])); } /// <reference types="./uri.d.mts" /> function percent_encode(value) { return percent_encode$1(value); } function query_pair(pair) { return from_strings( toList([percent_encode(pair[0]), "=", percent_encode(pair[1])]), ); } function query_to_string(query) { let _pipe = query; let _pipe$1 = map(_pipe, query_pair); let _pipe$2 = intersperse(_pipe$1, from_string("&")); let _pipe$3 = concat$2(_pipe$2); return to_string(_pipe$3); } /// <reference types="./emit_level.d.mts" /> class Message extends CustomType {} class Warn extends CustomType {} class Fail extends CustomType {} /// <reference types="./pr_title.d.mts" /> class PrTitle extends CustomType { constructor(prefix, suffix) { super(); this.prefix = prefix; this.suffix = suffix; } } function parse(pr_title, pattern) { let matches = (() => { let _pipe = pr_title; return scan(pattern, _pipe); })(); if (matches.hasLength(1)) { let match = matches.head; let $ = match.submatches; if ($.hasLength(2) && $.head instanceof Some && $.tail.head instanceof Some) { let prefix = $.head[0]; let suffix = $.tail.head[0]; return new PrTitle( new Some(prefix), (() => { let _pipe = suffix; return trim$1(_pipe); })(), ); } else { return new PrTitle(new None(), pr_title); } } else { return new PrTitle(new None(), pr_title); } } /// <reference types="./rule.d.mts" /> class Violation extends CustomType { constructor(span) { super(); this.span = span; } } /// <reference types="./no_trailing_punctuation.d.mts" /> class NoTrailingPunctuationConfig extends CustomType { constructor(level, message) { super(); this.level = level; this.message = message; } } function default_config$2() { return new NoTrailingPunctuationConfig( new Warn(), "Do not end PR titles with punctuation.", ); } function no_trailing_punctuation(pr_title) { let punctuation_marks = toList([".", "!", "?", ",", ":", ";"]); let number_of_trailing_punctuation_marks = (() => { let _pipe = pr_title; let _pipe$1 = graphemes(_pipe); let _pipe$2 = reverse(_pipe$1); let _pipe$3 = take_while( _pipe$2, (_capture) => { return contains$1(punctuation_marks, _capture); }, ); return length(_pipe$3); })(); let has_trailing_punctuation = number_of_trailing_punctuation_marks > 0; if (has_trailing_punctuation) { let end = length$1(pr_title); let start = end - number_of_trailing_punctuation_marks; return new Error(toList([new Violation([start, end])])); } else { return new Ok(undefined); } } /// <reference types="./require_prefix.d.mts" /> class RequirePrefixConfig extends CustomType { constructor(level, message) { super(); this.level = level; this.message = message; } } function require_prefix(prefix_pattern, pr_title) { let $ = parse(pr_title, prefix_pattern); let prefix = $.prefix; if (prefix instanceof None) { return new Error(toList([new Violation([0, 0])])); } else { return new Ok(undefined); } } /// <reference types="./set.d.mts" /> let Set$1 = class Set extends CustomType { constructor(dict) { super(); this.dict = dict; } }; function contains(set, member) { let _pipe = set.dict; let _pipe$1 = get(_pipe, member); return is_ok(_pipe$1); } const token = undefined; function from_list(members) { let dict = fold( members, new$(), (m, k) => { return insert(m, k, token); }, ); return new Set$1(dict); } /// <reference types="./verb_forms.d.mts" /> function third_person_singular_verbs() { return from_list( toList([ "adds", "amends", "applies", "boosts", "calculates", "captures", "changes", "cleans", "copies", "deletes", "deprecates", "documents", "duplicates", "empowers", "enhances", "expands", "explores", "facilitates", "fixes", "forces", "formalizes", "implements", "improves", "initializes", "integrates", "makes", "merges", "migrates", "modernizes", "modifies", "optimizes", "patches", "plugs", "presents", "refactors", "removes", "reshapes", "resolves", "reveals", "reverts", "revises", "secures", "simplifies", "standardizes", "stores", "streamlines", "supports", "tests", "transforms", "tries", "tweaks", "undoes", "unifies", "updates", "upgrades", "writes", ]), ); } function is_third_person_singular(verb) { let _pipe = verb; let _pipe$1 = lowercase$1(_pipe); return ((_capture) => { return contains(third_person_singular_verbs(), _capture); })(_pipe$1); } function is_past_tense(verb) { return ends_with$1(verb, "ed"); } function present_participle_exceptions() { return from_list(toList(["bring"])); } function is_present_participle(verb) { let $ = from_string$1("(\\w)*(ing)$"); if (!$.isOk()) { throw makeError( "let_assert", "danger_plugin_pr_hygiene/grammar/verb_forms", 35, "is_present_participle", "Pattern match failed, no pattern matched the value.", { value: $ } ) } let pattern = $[0]; return check(pattern, verb) && !contains( present_participle_exceptions(), lowercase$1(verb), ); } function is_bare_infinitive(verb) { return (!is_third_person_singular(verb) && !is_past_tense(verb)) && !is_present_participle( verb, ); } /// <reference types="./use_imperative_mood.d.mts" /> class UseImperativeMoodConfig extends CustomType { constructor(level, message) { super(); this.level = level; this.message = message; } } function default_config$1() { return new UseImperativeMoodConfig( new Warn(), "Write PR titles using the imperative mood.", ); } function use_imperative_mood(pr_title) { let words = (() => { let _pipe = pr_title; return split$1(_pipe, " "); })(); if (words.atLeastLength(1)) { let word = words.head; let $ = is_bare_infinitive(word); if (!$) { return new Error(toList([new Violation([0, length$1(word)])])); } else { return new Ok(undefined); } } else { return new Ok(undefined); } } /// <reference types="./use_sentence_case.d.mts" /> class UseSentenceCaseConfig extends CustomType { constructor(level, message) { super(); this.level = level; this.message = message; } } function default_config() { return new UseSentenceCaseConfig( new Warn(), "Write PR titles using sentence case.", ); } function use_sentence_case(pr_title) { let $ = from_string$1("^[a-z]"); if (!$.isOk()) { throw makeError( "let_assert", "danger_plugin_pr_hygiene/rules/use_sentence_case", 17, "use_sentence_case", "Pattern match failed, no pattern matched the value.", { value: $ } ) } let pattern = $[0]; let $1 = check(pattern, pr_title); if ($1) { return new Error(toList([new Violation([0, 1])])); } else { return new Ok(undefined); } } /// <reference types="./danger_plugin_pr_hygiene.d.mts" /> class PrHygieneContext extends CustomType { constructor(message, warn, fail, markdown, pr_title) { super(); this.message = message; this.warn = warn; this.fail = fail; this.markdown = mar