lighthouse-audit-utils 1.0.0 → 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of lighthouse-audit-utils might be problematic. Click here for more details.
- package/README.md +90 -24
- package/dist/chunk-4DICEJP6.js +510 -0
- package/dist/chunk-4DICEJP6.js.map +1 -0
- package/dist/index.js +1 -194
- package/dist/index.js.map +1 -1
- package/dist/log-recommendations.d.ts +1 -1
- package/dist/playwright.d.ts +60 -0
- package/dist/playwright.js +78 -0
- package/dist/playwright.js.map +1 -0
- package/dist/thresholds.d.ts +1 -1
- package/package.json +82 -59
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../node_modules/.pnpm/es-toolkit@1.50.0/node_modules/es-toolkit/dist/predicate/isPrimitive.mjs","../node_modules/.pnpm/es-toolkit@1.50.0/node_modules/es-toolkit/dist/predicate/isTypedArray.mjs","../node_modules/.pnpm/es-toolkit@1.50.0/node_modules/es-toolkit/dist/object/clone.mjs","../node_modules/.pnpm/es-toolkit@1.50.0/node_modules/es-toolkit/dist/compat/_internal/getSymbols.mjs","../node_modules/.pnpm/es-toolkit@1.50.0/node_modules/es-toolkit/dist/compat/_internal/getTag.mjs","../node_modules/.pnpm/es-toolkit@1.50.0/node_modules/es-toolkit/dist/compat/_internal/tags.mjs","../node_modules/.pnpm/es-toolkit@1.50.0/node_modules/es-toolkit/dist/_internal/globalThis.mjs","../node_modules/.pnpm/es-toolkit@1.50.0/node_modules/es-toolkit/dist/predicate/isBuffer.mjs","../node_modules/.pnpm/es-toolkit@1.50.0/node_modules/es-toolkit/dist/object/cloneDeepWith.mjs","../node_modules/.pnpm/es-toolkit@1.50.0/node_modules/es-toolkit/dist/object/cloneDeep.mjs","../node_modules/.pnpm/es-toolkit@1.50.0/node_modules/es-toolkit/dist/predicate/isPlainObject.mjs","../node_modules/.pnpm/es-toolkit@1.50.0/node_modules/es-toolkit/dist/_internal/isUnsafeProperty.mjs","../node_modules/.pnpm/es-toolkit@1.50.0/node_modules/es-toolkit/dist/object/mergeWith.mjs","../node_modules/.pnpm/es-toolkit@1.50.0/node_modules/es-toolkit/dist/object/toMerged.mjs","../node_modules/.pnpm/es-toolkit@1.50.0/node_modules/es-toolkit/dist/array/compact.mjs","../node_modules/.pnpm/es-toolkit@1.50.0/node_modules/es-toolkit/dist/_internal/compareValues.mjs","../node_modules/.pnpm/es-toolkit@1.50.0/node_modules/es-toolkit/dist/array/orderBy.mjs","../src/log-recommendations.ts","../src/thresholds.ts","../src/write-reports.ts","../src/handle-audit-result.ts","../src/run-audit.ts"],"names":[],"mappings":"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","file":"chunk-4DICEJP6.js","sourcesContent":["//#region src/predicate/isPrimitive.ts\n/**\n* Checks whether a value is a JavaScript primitive.\n* JavaScript primitives include null, undefined, strings, numbers, booleans, symbols, and bigints.\n*\n* @param value The value to check.\n* @returns Returns true if `value` is a primitive, false otherwise.\n*\n* @example\n* isPrimitive(null); // true\n* isPrimitive(undefined); // true\n* isPrimitive('123'); // true\n* isPrimitive(false); // true\n* isPrimitive(true); // true\n* isPrimitive(Symbol('a')); // true\n* isPrimitive(123n); // true\n* isPrimitive({}); // false\n* isPrimitive(new Date()); // false\n* isPrimitive(new Map()); // false\n* isPrimitive(new Set()); // false\n* isPrimitive([1, 2, 3]); // false\n*/\nfunction isPrimitive(value) {\n\treturn value == null || typeof value !== \"object\" && typeof value !== \"function\";\n}\n//#endregion\nexport { isPrimitive };\n","//#region src/predicate/isTypedArray.ts\n/**\n* Checks if a value is a TypedArray.\n* @param x The value to check.\n* @returns Returns true if `x` is a TypedArray, false otherwise.\n*\n* @example\n* const arr = new Uint8Array([1, 2, 3]);\n* isTypedArray(arr); // true\n*\n* const regularArray = [1, 2, 3];\n* isTypedArray(regularArray); // false\n*\n* const buffer = new ArrayBuffer(16);\n* isTypedArray(buffer); // false\n*/\nfunction isTypedArray(x) {\n\treturn ArrayBuffer.isView(x) && !(x instanceof DataView);\n}\n//#endregion\nexport { isTypedArray };\n","import { isPrimitive } from \"../predicate/isPrimitive.mjs\";\nimport { isTypedArray } from \"../predicate/isTypedArray.mjs\";\n//#region src/object/clone.ts\n/**\n* Creates a shallow clone of the given object.\n*\n* @template T - The type of the object.\n* @param obj - The object to clone.\n* @returns A shallow clone of the given object.\n*\n* @example\n* // Clone a primitive value\n* const num = 29;\n* const clonedNum = clone(num);\n* console.log(clonedNum); // 29\n* console.log(clonedNum === num); // true\n*\n* @example\n* // Clone an array\n* const arr = [1, 2, 3];\n* const clonedArr = clone(arr);\n* console.log(clonedArr); // [1, 2, 3]\n* console.log(clonedArr === arr); // false\n*\n* @example\n* // Clone an object\n* const obj = { a: 1, b: 'es-toolkit', c: [1, 2, 3] };\n* const clonedObj = clone(obj);\n* console.log(clonedObj); // { a: 1, b: 'es-toolkit', c: [1, 2, 3] }\n* console.log(clonedObj === obj); // false\n*/\nfunction clone(obj) {\n\tif (isPrimitive(obj)) return obj;\n\tif (Array.isArray(obj) || isTypedArray(obj) || obj instanceof ArrayBuffer || typeof SharedArrayBuffer !== \"undefined\" && obj instanceof SharedArrayBuffer) return obj.slice(0);\n\tconst prototype = Object.getPrototypeOf(obj);\n\tif (prototype == null) return Object.assign(Object.create(prototype), obj);\n\tconst Constructor = prototype.constructor;\n\tif (obj instanceof Date || obj instanceof Map || obj instanceof Set) return new Constructor(obj);\n\tif (obj instanceof RegExp) {\n\t\tconst newRegExp = new Constructor(obj);\n\t\tnewRegExp.lastIndex = obj.lastIndex;\n\t\treturn newRegExp;\n\t}\n\tif (obj instanceof DataView) return new Constructor(obj.buffer.slice(0));\n\tif (obj instanceof Error) {\n\t\tlet newError;\n\t\tif (obj instanceof AggregateError) newError = new Constructor(obj.errors, obj.message, { cause: obj.cause });\n\t\telse newError = new Constructor(obj.message, { cause: obj.cause });\n\t\tnewError.stack = obj.stack;\n\t\tObject.assign(newError, obj);\n\t\treturn newError;\n\t}\n\tif (typeof File !== \"undefined\" && obj instanceof File) return new Constructor([obj], obj.name, {\n\t\ttype: obj.type,\n\t\tlastModified: obj.lastModified\n\t});\n\tif (typeof obj === \"object\") return Object.assign(Object.create(prototype), obj);\n\treturn obj;\n}\n//#endregion\nexport { clone };\n","//#region src/compat/_internal/getSymbols.ts\nfunction getSymbols(object) {\n\treturn Object.getOwnPropertySymbols(object).filter((symbol) => Object.prototype.propertyIsEnumerable.call(object, symbol));\n}\n//#endregion\nexport { getSymbols };\n","//#region src/compat/_internal/getTag.ts\n/**\n* Gets the `toStringTag` of `value`.\n*\n* @private\n* @param {T} value The value to query.\n* @returns {string} Returns the `Object.prototype.toString.call` result.\n*/\nfunction getTag(value) {\n\tif (value == null) return value === void 0 ? \"[object Undefined]\" : \"[object Null]\";\n\treturn Object.prototype.toString.call(value);\n}\n//#endregion\nexport { getTag };\n","//#region src/compat/_internal/tags.ts\nconst regexpTag = \"[object RegExp]\";\nconst stringTag = \"[object String]\";\nconst numberTag = \"[object Number]\";\nconst booleanTag = \"[object Boolean]\";\nconst argumentsTag = \"[object Arguments]\";\nconst symbolTag = \"[object Symbol]\";\nconst dateTag = \"[object Date]\";\nconst mapTag = \"[object Map]\";\nconst setTag = \"[object Set]\";\nconst arrayTag = \"[object Array]\";\nconst functionTag = \"[object Function]\";\nconst arrayBufferTag = \"[object ArrayBuffer]\";\nconst objectTag = \"[object Object]\";\nconst errorTag = \"[object Error]\";\nconst dataViewTag = \"[object DataView]\";\nconst uint8ArrayTag = \"[object Uint8Array]\";\nconst uint8ClampedArrayTag = \"[object Uint8ClampedArray]\";\nconst uint16ArrayTag = \"[object Uint16Array]\";\nconst uint32ArrayTag = \"[object Uint32Array]\";\nconst bigUint64ArrayTag = \"[object BigUint64Array]\";\nconst int8ArrayTag = \"[object Int8Array]\";\nconst int16ArrayTag = \"[object Int16Array]\";\nconst int32ArrayTag = \"[object Int32Array]\";\nconst bigInt64ArrayTag = \"[object BigInt64Array]\";\nconst float32ArrayTag = \"[object Float32Array]\";\nconst float64ArrayTag = \"[object Float64Array]\";\n//#endregion\nexport { argumentsTag, arrayBufferTag, arrayTag, bigInt64ArrayTag, bigUint64ArrayTag, booleanTag, dataViewTag, dateTag, errorTag, float32ArrayTag, float64ArrayTag, functionTag, int16ArrayTag, int32ArrayTag, int8ArrayTag, mapTag, numberTag, objectTag, regexpTag, setTag, stringTag, symbolTag, uint16ArrayTag, uint32ArrayTag, uint8ArrayTag, uint8ClampedArrayTag };\n","//#region src/_internal/globalThis.ts\nconst globalThis_ = typeof globalThis === \"object\" && globalThis || typeof window === \"object\" && window || typeof self === \"object\" && self || typeof global === \"object\" && global || (function() {\n\treturn this;\n})();\n//#endregion\nexport { globalThis_ };\n","import { globalThis_ } from \"../_internal/globalThis.mjs\";\n//#region src/predicate/isBuffer.ts\n/**\n* Checks if the given value is a Buffer instance.\n*\n* This function tests whether the provided value is an instance of Buffer.\n* It returns `true` if the value is a Buffer, and `false` otherwise.\n*\n* This function can also serve as a type predicate in TypeScript, narrowing the type of the argument to `Buffer`.\n*\n* @param x - The value to check if it is a Buffer.\n* @returns Returns `true` if `x` is a Buffer, else `false`.\n*\n* @example\n* const buffer = Buffer.from(\"test\");\n* console.log(isBuffer(buffer)); // true\n*\n* const notBuffer = \"not a buffer\";\n* console.log(isBuffer(notBuffer)); // false\n*/\nfunction isBuffer(x) {\n\treturn typeof globalThis_.Buffer !== \"undefined\" && globalThis_.Buffer.isBuffer(x);\n}\n//#endregion\nexport { isBuffer };\n","import { isPrimitive } from \"../predicate/isPrimitive.mjs\";\nimport { isTypedArray } from \"../predicate/isTypedArray.mjs\";\nimport { getSymbols } from \"../compat/_internal/getSymbols.mjs\";\nimport { getTag } from \"../compat/_internal/getTag.mjs\";\nimport { argumentsTag, arrayBufferTag, arrayTag, booleanTag, dataViewTag, dateTag, float32ArrayTag, float64ArrayTag, int16ArrayTag, int32ArrayTag, int8ArrayTag, mapTag, numberTag, objectTag, regexpTag, setTag, stringTag, symbolTag, uint16ArrayTag, uint32ArrayTag, uint8ArrayTag, uint8ClampedArrayTag } from \"../compat/_internal/tags.mjs\";\nimport { isBuffer } from \"../predicate/isBuffer.mjs\";\n//#region src/object/cloneDeepWith.ts\n/**\n* Deeply clones the given object.\n*\n* You can customize the deep cloning process using the `cloneValue` function.\n* The function takes the current value `value`, the property name `key`, and the entire object `obj` as arguments.\n* If the function returns a value, that value is used;\n* if it returns `undefined`, the default cloning method is used.\n*\n* @template T - The type of the object.\n* @param obj - The object to clone.\n* @param [cloneValue] - A function to customize the cloning process.\n* @returns A deep clone of the given object.\n*\n* @example\n* // Clone a primitive value\n* const num = 29;\n* const clonedNum = cloneDeepWith(num);\n* console.log(clonedNum); // 29\n* console.log(clonedNum === num); // true\n*\n* @example\n* // Clone an object with a customizer\n* const obj = { a: 1, b: 2 };\n* const clonedObj = cloneDeepWith(obj, (value) => {\n* if (typeof value === 'number') {\n* return value * 2; // Double the number\n* }\n* });\n* console.log(clonedObj); // { a: 2, b: 4 }\n* console.log(clonedObj === obj); // false\n*\n* @example\n* // Clone an array with a customizer\n* const arr = [1, 2, 3];\n* const clonedArr = cloneDeepWith(arr, (value) => {\n* return value + 1; // Increment each value\n* });\n* console.log(clonedArr); // [2, 3, 4]\n* console.log(clonedArr === arr); // false\n*/\nfunction cloneDeepWith(obj, cloneValue) {\n\treturn cloneDeepWithImpl(obj, void 0, obj, /* @__PURE__ */ new Map(), cloneValue);\n}\nfunction cloneDeepWithImpl(valueToClone, keyToClone, objectToClone, stack = /* @__PURE__ */ new Map(), cloneValue = void 0) {\n\tconst cloned = cloneValue?.(valueToClone, keyToClone, objectToClone, stack);\n\tif (cloned !== void 0) return cloned;\n\tif (isPrimitive(valueToClone)) return valueToClone;\n\tif (stack.has(valueToClone)) return stack.get(valueToClone);\n\tif (Array.isArray(valueToClone)) {\n\t\tconst result = new Array(valueToClone.length);\n\t\tstack.set(valueToClone, result);\n\t\tfor (let i = 0; i < valueToClone.length; i++) result[i] = cloneDeepWithImpl(valueToClone[i], i, objectToClone, stack, cloneValue);\n\t\tif (Object.hasOwn(valueToClone, \"index\")) result.index = valueToClone.index;\n\t\tif (Object.hasOwn(valueToClone, \"input\")) result.input = valueToClone.input;\n\t\treturn result;\n\t}\n\tif (valueToClone instanceof Date) return new Date(valueToClone.getTime());\n\tif (valueToClone instanceof RegExp) {\n\t\tconst result = new RegExp(valueToClone.source, valueToClone.flags);\n\t\tresult.lastIndex = valueToClone.lastIndex;\n\t\treturn result;\n\t}\n\tif (valueToClone instanceof Map) {\n\t\tconst result = /* @__PURE__ */ new Map();\n\t\tstack.set(valueToClone, result);\n\t\tfor (const [key, value] of valueToClone) result.set(key, cloneDeepWithImpl(value, key, objectToClone, stack, cloneValue));\n\t\treturn result;\n\t}\n\tif (valueToClone instanceof Set) {\n\t\tconst result = /* @__PURE__ */ new Set();\n\t\tstack.set(valueToClone, result);\n\t\tfor (const value of valueToClone) result.add(cloneDeepWithImpl(value, void 0, objectToClone, stack, cloneValue));\n\t\treturn result;\n\t}\n\tif (isBuffer(valueToClone)) return valueToClone.subarray();\n\tif (isTypedArray(valueToClone)) {\n\t\tconst result = new (Object.getPrototypeOf(valueToClone)).constructor(valueToClone.length);\n\t\tstack.set(valueToClone, result);\n\t\tfor (let i = 0; i < valueToClone.length; i++) result[i] = cloneDeepWithImpl(valueToClone[i], i, objectToClone, stack, cloneValue);\n\t\treturn result;\n\t}\n\tif (valueToClone instanceof ArrayBuffer || typeof SharedArrayBuffer !== \"undefined\" && valueToClone instanceof SharedArrayBuffer) return valueToClone.slice(0);\n\tif (valueToClone instanceof DataView) {\n\t\tconst result = new DataView(valueToClone.buffer.slice(0), valueToClone.byteOffset, valueToClone.byteLength);\n\t\tstack.set(valueToClone, result);\n\t\tcopyProperties(result, valueToClone, objectToClone, stack, cloneValue);\n\t\treturn result;\n\t}\n\tif (typeof File !== \"undefined\" && valueToClone instanceof File) {\n\t\tconst result = new File([valueToClone], valueToClone.name, { type: valueToClone.type });\n\t\tstack.set(valueToClone, result);\n\t\tcopyProperties(result, valueToClone, objectToClone, stack, cloneValue);\n\t\treturn result;\n\t}\n\tif (typeof Blob !== \"undefined\" && valueToClone instanceof Blob) {\n\t\tconst result = new Blob([valueToClone], { type: valueToClone.type });\n\t\tstack.set(valueToClone, result);\n\t\tcopyProperties(result, valueToClone, objectToClone, stack, cloneValue);\n\t\treturn result;\n\t}\n\tif (valueToClone instanceof Error) {\n\t\tconst result = structuredClone(valueToClone);\n\t\tstack.set(valueToClone, result);\n\t\tresult.message = valueToClone.message;\n\t\tresult.name = valueToClone.name;\n\t\tresult.stack = valueToClone.stack;\n\t\tresult.cause = valueToClone.cause;\n\t\tresult.constructor = valueToClone.constructor;\n\t\tcopyProperties(result, valueToClone, objectToClone, stack, cloneValue);\n\t\treturn result;\n\t}\n\tif (valueToClone instanceof Boolean) {\n\t\tconst result = new Boolean(valueToClone.valueOf());\n\t\tstack.set(valueToClone, result);\n\t\tcopyProperties(result, valueToClone, objectToClone, stack, cloneValue);\n\t\treturn result;\n\t}\n\tif (valueToClone instanceof Number) {\n\t\tconst result = new Number(valueToClone.valueOf());\n\t\tstack.set(valueToClone, result);\n\t\tcopyProperties(result, valueToClone, objectToClone, stack, cloneValue);\n\t\treturn result;\n\t}\n\tif (valueToClone instanceof String) {\n\t\tconst result = new String(valueToClone.valueOf());\n\t\tstack.set(valueToClone, result);\n\t\tcopyProperties(result, valueToClone, objectToClone, stack, cloneValue);\n\t\treturn result;\n\t}\n\tif (typeof valueToClone === \"object\" && isCloneableObject(valueToClone)) {\n\t\tconst result = Object.create(Object.getPrototypeOf(valueToClone));\n\t\tstack.set(valueToClone, result);\n\t\tcopyProperties(result, valueToClone, objectToClone, stack, cloneValue);\n\t\treturn result;\n\t}\n\treturn valueToClone;\n}\nfunction copyProperties(target, source, objectToClone = target, stack, cloneValue) {\n\tconst keys = [...Object.keys(source), ...getSymbols(source)];\n\tfor (let i = 0; i < keys.length; i++) {\n\t\tconst key = keys[i];\n\t\tconst descriptor = Object.getOwnPropertyDescriptor(target, key);\n\t\tif (descriptor == null || descriptor.writable) target[key] = cloneDeepWithImpl(source[key], key, objectToClone, stack, cloneValue);\n\t}\n}\nfunction isCloneableObject(object) {\n\tswitch (getTag(object)) {\n\t\tcase argumentsTag:\n\t\tcase arrayTag:\n\t\tcase arrayBufferTag:\n\t\tcase dataViewTag:\n\t\tcase booleanTag:\n\t\tcase dateTag:\n\t\tcase float32ArrayTag:\n\t\tcase float64ArrayTag:\n\t\tcase int8ArrayTag:\n\t\tcase int16ArrayTag:\n\t\tcase int32ArrayTag:\n\t\tcase mapTag:\n\t\tcase numberTag:\n\t\tcase objectTag:\n\t\tcase regexpTag:\n\t\tcase setTag:\n\t\tcase stringTag:\n\t\tcase symbolTag:\n\t\tcase uint8ArrayTag:\n\t\tcase uint8ClampedArrayTag:\n\t\tcase uint16ArrayTag:\n\t\tcase uint32ArrayTag: return true;\n\t\tdefault: return false;\n\t}\n}\n//#endregion\nexport { cloneDeepWith, cloneDeepWithImpl, copyProperties };\n","import { cloneDeepWithImpl } from \"./cloneDeepWith.mjs\";\n//#region src/object/cloneDeep.ts\n/**\n* Creates a deep clone of the given object.\n*\n* @template T - The type of the object.\n* @param obj - The object to clone.\n* @returns A deep clone of the given object.\n*\n* @example\n* // Clone a primitive value\n* const num = 29;\n* const clonedNum = cloneDeep(num);\n* console.log(clonedNum); // 29\n* console.log(clonedNum === num); // true\n*\n* @example\n* // Clone an array\n* const arr = [1, 2, 3];\n* const clonedArr = cloneDeep(arr);\n* console.log(clonedArr); // [1, 2, 3]\n* console.log(clonedArr === arr); // false\n*\n* @example\n* // Clone an array with nested objects\n* const arr = [1, { a: 1 }, [1, 2, 3]];\n* const clonedArr = cloneDeep(arr);\n* arr[1].a = 2;\n* console.log(arr); // [1, { a: 2 }, [1, 2, 3]]\n* console.log(clonedArr); // [1, { a: 1 }, [1, 2, 3]]\n* console.log(clonedArr === arr); // false\n*\n* @example\n* // Clone an object\n* const obj = { a: 1, b: 'es-toolkit', c: [1, 2, 3] };\n* const clonedObj = cloneDeep(obj);\n* console.log(clonedObj); // { a: 1, b: 'es-toolkit', c: [1, 2, 3] }\n* console.log(clonedObj === obj); // false\n*\n* @example\n* // Clone an object with nested objects\n* const obj = { a: 1, b: { c: 1 } };\n* const clonedObj = cloneDeep(obj);\n* obj.b.c = 2;\n* console.log(obj); // { a: 1, b: { c: 2 } }\n* console.log(clonedObj); // { a: 1, b: { c: 1 } }\n* console.log(clonedObj === obj); // false\n*/\nfunction cloneDeep(obj) {\n\treturn cloneDeepWithImpl(obj, void 0, obj, /* @__PURE__ */ new Map(), void 0);\n}\n//#endregion\nexport { cloneDeep };\n","//#region src/predicate/isPlainObject.ts\n/**\n* Checks if a given value is a plain object.\n*\n* @param value - The value to check.\n* @returns True if the value is a plain object, otherwise false.\n*\n* @example\n* ```typescript\n* // ✅👇 True\n*\n* isPlainObject({ }); // ✅\n* isPlainObject({ key: 'value' }); // ✅\n* isPlainObject({ key: new Date() }); // ✅\n* isPlainObject(new Object()); // ✅\n* isPlainObject(Object.create(null)); // ✅\n* isPlainObject({ nested: { key: true} }); // ✅\n* isPlainObject(new Proxy({}, {})); // ✅\n* isPlainObject({ [Symbol('tag')]: 'A' }); // ✅\n*\n* // ✅👇 (cross-realms, node context, workers, ...)\n* const runInNewContext = await import('node:vm').then(\n* (mod) => mod.runInNewContext\n* );\n* isPlainObject(runInNewContext('({})')); // ✅\n*\n* // ❌👇 False\n*\n* class Test { };\n* isPlainObject(new Test()) // ❌\n* isPlainObject(10); // ❌\n* isPlainObject(null); // ❌\n* isPlainObject('hello'); // ❌\n* isPlainObject([]); // ❌\n* isPlainObject(new Date()); // ❌\n* isPlainObject(new Uint8Array([1])); // ❌\n* isPlainObject(Buffer.from('ABC')); // ❌\n* isPlainObject(Promise.resolve({})); // ❌\n* isPlainObject(Object.create({})); // ❌\n* isPlainObject(new (class Cls {})); // ❌\n* isPlainObject(globalThis); // ❌,\n* ```\n*/\nfunction isPlainObject(value) {\n\tif (!value || typeof value !== \"object\") return false;\n\tconst proto = Object.getPrototypeOf(value);\n\tif (!(proto === null || proto === Object.prototype || Object.getPrototypeOf(proto) === null)) return false;\n\treturn Object.prototype.toString.call(value) === \"[object Object]\";\n}\n//#endregion\nexport { isPlainObject };\n","//#region src/_internal/isUnsafeProperty.ts\n/**\n* Checks if a property key is unsafe to modify directly.\n*\n* This function is used in functions like `merge` to prevent prototype pollution attacks\n* by identifying property keys that could modify the object's prototype chain or constructor.\n*\n* @param key - The property key to check\n* @returns `true` if the property is unsafe to modify directly, `false` otherwise\n* @internal\n*/\nfunction isUnsafeProperty(key) {\n\treturn key === \"__proto__\";\n}\n//#endregion\nexport { isUnsafeProperty };\n","import { isPlainObject } from \"../predicate/isPlainObject.mjs\";\nimport { isUnsafeProperty } from \"../_internal/isUnsafeProperty.mjs\";\n//#region src/object/mergeWith.ts\n/**\n* Merges the properties of the source object into the target object.\n*\n* You can provide a custom `merge` function to control how properties are merged. It should return the value to be set in the target object.\n*\n* If it returns `undefined`, a default deep merge will be applied for arrays and objects:\n*\n* - If a property in the source object is an array or an object and the corresponding property in the target object is also an array or object, they will be merged.\n* - If a property in the source object is undefined, it will not overwrite a defined property in the target object.\n*\n* Note that this function mutates the target object.\n*\n* @param target - The target object into which the source object properties will be merged. This object is modified in place.\n* @param source - The source object whose properties will be merged into the target object.\n* @param merge - A custom merge function that defines how properties should be combined. It receives the following arguments:\n* - `targetValue`: The current value of the property in the target object.\n* - `sourceValue`: The value of the property in the source object.\n* - `key`: The key of the property being merged.\n* - `target`: The target object.\n* - `source`: The source object.\n*\n* @returns The updated target object with properties from the source object merged in.\n*\n* @template T - Type of the target object.\n* @template S - Type of the source object.\n*\n* @example\n* const target = { a: 1, b: 2 };\n* const source = { b: 3, c: 4 };\n*\n* mergeWith(target, source, (targetValue, sourceValue) => {\n* if (typeof targetValue === 'number' && typeof sourceValue === 'number') {\n* return targetValue + sourceValue;\n* }\n* });\n* // Returns { a: 1, b: 5, c: 4 }\n* @example\n* const target = { a: [1], b: [2] };\n* const source = { a: [3], b: [4] };\n*\n* const result = mergeWith(target, source, (objValue, srcValue) => {\n* if (Array.isArray(objValue)) {\n* return objValue.concat(srcValue);\n* }\n* });\n*\n* expect(result).toEqual({ a: [1, 3], b: [2, 4] });\n*/\nfunction mergeWith(target, source, merge) {\n\tconst sourceKeys = Object.keys(source);\n\tfor (let i = 0; i < sourceKeys.length; i++) {\n\t\tconst key = sourceKeys[i];\n\t\tif (isUnsafeProperty(key)) continue;\n\t\tconst sourceValue = source[key];\n\t\tconst targetValue = target[key];\n\t\tconst merged = merge(targetValue, sourceValue, key, target, source);\n\t\tif (merged !== void 0) target[key] = merged;\n\t\telse if (Array.isArray(sourceValue)) if (Array.isArray(targetValue)) target[key] = mergeWith(targetValue, sourceValue, merge);\n\t\telse target[key] = mergeWith([], sourceValue, merge);\n\t\telse if (isPlainObject(sourceValue)) if (isPlainObject(targetValue)) target[key] = mergeWith(targetValue, sourceValue, merge);\n\t\telse target[key] = mergeWith({}, sourceValue, merge);\n\t\telse if (targetValue === void 0 || sourceValue !== void 0) target[key] = sourceValue;\n\t}\n\treturn target;\n}\n//#endregion\nexport { mergeWith };\n","import { clone } from \"./clone.mjs\";\nimport { cloneDeep } from \"./cloneDeep.mjs\";\nimport { isPlainObject } from \"../predicate/isPlainObject.mjs\";\nimport { mergeWith } from \"./mergeWith.mjs\";\n//#region src/object/toMerged.ts\n/**\n* Merges the properties of the source object into a deep clone of the target object.\n* Unlike `merge`, This function does not modify the original target object.\n*\n* This function performs a deep merge, meaning nested objects and arrays are merged recursively.\n*\n* - If a property in the source object is an array or object and the corresponding property in the target object is also an array or object, they will be merged.\n* - If a property in the source object is undefined, it will not overwrite a defined property in the target object.\n*\n* Note that this function does not mutate the target object.\n*\n* @param target - The target object to be cloned and merged into. This object is not modified directly.\n* @param source - The source object whose properties will be merged into the cloned target object.\n* @returns A new object with properties from the source object merged into a deep clone of the target object.\n*\n* @template T - Type of the target object.\n* @template S - Type of the source object.\n*\n* @example\n* const target = { a: 1, b: { x: 1, y: 2 } };\n* const source = { b: { y: 3, z: 4 }, c: 5 };\n*\n* const result = toMerged(target, source);\n* console.log(result);\n* // Output: { a: 1, b: { x: 1, y: 3, z: 4 }, c: 5 }\n*\n* @example\n* const target = { a: [1, 2], b: { x: 1 } };\n* const source = { a: [3], b: { y: 2 } };\n*\n* const result = toMerged(target, source);\n* console.log(result);\n* // Output: { a: [3, 2], b: { x: 1, y: 2 } }\n*\n* @example\n* const target = { a: null };\n* const source = { a: [1, 2, 3] };\n*\n* const result = toMerged(target, source);\n* console.log(result);\n* // Output: { a: [1, 2, 3] }\n*/\nfunction toMerged(target, source) {\n\treturn mergeWith(cloneDeep(target), source, function mergeRecursively(targetValue, sourceValue) {\n\t\tif (Array.isArray(sourceValue)) if (Array.isArray(targetValue)) return mergeWith(clone(targetValue), sourceValue, mergeRecursively);\n\t\telse return mergeWith([], sourceValue, mergeRecursively);\n\t\telse if (isPlainObject(sourceValue)) if (isPlainObject(targetValue)) return mergeWith(clone(targetValue), sourceValue, mergeRecursively);\n\t\telse return mergeWith({}, sourceValue, mergeRecursively);\n\t});\n}\n//#endregion\nexport { toMerged };\n","//#region src/array/compact.ts\n/**\n* Removes falsey values (false, null, 0, -0, 0n, '', undefined, NaN) from an array.\n*\n* @template T - The type of elements in the array.\n* @param arr - The input array to remove falsey values.\n* @returns A new array with all falsey values removed.\n*\n* @example\n* compact([0, -0, 0n, 1, false, 2, '', 3, null, undefined, 4, NaN, 5]);\n* Returns: [1, 2, 3, 4, 5]\n*/\nfunction compact(arr) {\n\tconst result = [];\n\tfor (let i = 0; i < arr.length; i++) {\n\t\tconst item = arr[i];\n\t\tif (item) result.push(item);\n\t}\n\treturn result;\n}\n//#endregion\nexport { compact };\n","//#region src/_internal/compareValues.ts\nfunction nullishRank(value) {\n\tif (value === null) return 1;\n\tif (value === void 0) return 2;\n\treturn 0;\n}\nfunction compareAscending(a, b) {\n\tconst aRank = nullishRank(a);\n\tconst bRank = nullishRank(b);\n\tif (aRank < bRank) return -1;\n\tif (aRank > bRank) return 1;\n\tif (aRank !== 0) return 0;\n\tif (a < b) return -1;\n\tif (a > b) return 1;\n\treturn 0;\n}\nfunction compareValues(a, b, order) {\n\treturn order === \"asc\" ? compareAscending(a, b) : compareAscending(b, a);\n}\n//#endregion\nexport { compareValues };\n","import { compareValues } from \"../_internal/compareValues.mjs\";\n//#region src/array/orderBy.ts\n/**\n* Sorts an array of objects based on the given `criteria` and their corresponding order directions.\n*\n* - If you provide keys, it sorts the objects by the values of those keys.\n* - If you provide functions, it sorts based on the values returned by those functions.\n*\n* The function returns the array of objects sorted in corresponding order directions.\n* If two objects have the same value for the current criterion, it uses the next criterion to determine their order.\n* If the number of orders is less than the number of criteria, it uses the last order for the rest of the criteria.\n*\n* @template T - The type of elements in the array.\n* @param arr - The array of objects to be sorted.\n* @param criteria - The criteria for sorting. This can be an array of object keys or functions that return values used for sorting.\n* @param orders - An array of order directions ('asc' for ascending or 'desc' for descending).\n* @returns The sorted array.\n*\n* @example\n* // Sort an array of objects by 'user' in ascending order and 'age' in descending order.\n* const users = [\n* { user: 'fred', age: 48 },\n* { user: 'barney', age: 34 },\n* { user: 'fred', age: 40 },\n* { user: 'barney', age: 36 },\n* ];\n*\n* const result = orderBy(users, [obj => obj.user, 'age'], ['asc', 'desc']);\n* // result will be:\n* // [\n* // { user: 'barney', age: 36 },\n* // { user: 'barney', age: 34 },\n* // { user: 'fred', age: 48 },\n* // { user: 'fred', age: 40 },\n* // ]\n*/\nfunction orderBy(arr, criteria, orders) {\n\treturn arr.slice().sort((a, b) => {\n\t\tconst ordersLength = orders.length;\n\t\tfor (let i = 0; i < criteria.length; i++) {\n\t\t\tconst order = ordersLength > i ? orders[i] : orders[ordersLength - 1];\n\t\t\tconst criterion = criteria[i];\n\t\t\tconst criterionIsFunction = typeof criterion === \"function\";\n\t\t\tconst result = compareValues(criterionIsFunction ? criterion(a) : a[criterion], criterionIsFunction ? criterion(b) : b[criterion], order);\n\t\t\tif (result !== 0) return result;\n\t\t}\n\t\treturn 0;\n\t});\n}\n//#endregion\nexport { orderBy };\n","import { compact, orderBy } from 'es-toolkit'\r\nimport type { RunnerResult } from 'lighthouse'\r\nimport type Details from 'lighthouse/types/lhr/audit-details'\r\n\r\ntype Lhr = RunnerResult['lhr']\r\ntype Audit = Lhr['audits'][string]\r\ntype AuditDetails = NonNullable<Audit['details']>\r\n\r\n/** resolves to values that are only non-zero numbers */\r\nconst resolveMetricSavings = (audit: Audit) =>\r\n Object.entries(audit.metricSavings ?? {}).filter(([, ms]) => !!ms) as [\r\n string,\r\n number,\r\n ][]\r\n\r\n/** CLS savings are a unitless shift score; every other metric is milliseconds. */\r\nconst metricValueType = (metric: string): Details.ItemValueType =>\r\n metric === 'CLS' ? 'numeric' : 'ms'\r\n\r\nexport type FormattingArgs = {\r\n /**\r\n * Rows shown per audit before collapsing to \"…and N more\"\r\n * @default 5\r\n */\r\n maxItems?: number\r\n /**\r\n * Max length of a single value before it's truncated with an \"…\"\r\n * @default 120\r\n */\r\n maxValueLength?: number\r\n}\r\n\r\nconst formatValue = (\r\n value: Details.ItemValue | undefined,\r\n valueType: Details.ItemValueType,\r\n maxValueLength: number\r\n): string => {\r\n if (value === undefined || typeof value === 'boolean') {\r\n return ''\r\n }\r\n\r\n if (typeof value === 'number') {\r\n if (valueType === 'bytes') {\r\n // Keep a decimal on small values so sub-KiB assets don't read as \"0 KiB\".\r\n const kib = value / 1024\r\n const rounded = kib >= 100 ? Math.round(kib) : Math.round(kib * 10) / 10\r\n return `${rounded} KiB`\r\n }\r\n if (valueType === 'ms' || valueType === 'timespanMs') {\r\n return `${Math.round(value)} ms`\r\n }\r\n return String(Math.round(value * 100) / 100)\r\n }\r\n\r\n // `IcuMessage` is the only object value without a `type` tag.\r\n const text = (() => {\r\n if (typeof value === 'string') {\r\n return value\r\n }\r\n if (!('type' in value)) {\r\n return ''\r\n }\r\n switch (value.type) {\r\n case 'url':\r\n return value.value\r\n case 'link':\r\n return value.url || value.text\r\n case 'code':\r\n return String(value.value)\r\n case 'node':\r\n return value.nodeLabel ?? value.selector ?? value.snippet ?? ''\r\n case 'source-location':\r\n return `${value.url}:${value.line}:${value.column}`\r\n default:\r\n return ''\r\n }\r\n })()\r\n\r\n // Node labels and snippets carry the element's own line breaks — flatten them\r\n // so one item stays on one line.\r\n const singleLine = text.replace(/\\s+/g, ' ').trim()\r\n\r\n return singleLine.length > maxValueLength\r\n ? `${singleLine.slice(0, maxValueLength)}…`\r\n : singleLine\r\n}\r\n\r\n/**\r\n * The individual offenders the report UI lists under an audit — the specific\r\n * render-blocking scripts, oversized images, failing DOM nodes, and so on.\r\n */\r\nconst formatItems = (\r\n details: AuditDetails | undefined,\r\n { maxItems, maxValueLength }: Required<FormattingArgs>\r\n): string[] => {\r\n if (details?.type !== 'opportunity' && details?.type !== 'table') {\r\n return []\r\n }\r\n\r\n const [identity, ...measures] = details.headings.filter(({ key }) => !!key)\r\n if (!identity) {\r\n return []\r\n }\r\n\r\n /** Columns worth repeating alongside an item's identity (URL, node, ...). */\r\n const MEASURE_TYPES: Details.ItemValueType[] = [\r\n 'bytes',\r\n 'ms',\r\n 'timespanMs',\r\n 'numeric',\r\n ]\r\n\r\n const lines = details.items.slice(0, maxItems).map((item) => {\r\n const columns = [\r\n formatValue(item[identity.key ?? ''], identity.valueType, maxValueLength),\r\n ...measures\r\n .filter(({ valueType }) => MEASURE_TYPES.includes(valueType))\r\n .map(\r\n (heading) =>\r\n `${heading.label}: ${formatValue(item[heading.key ?? ''], heading.valueType, maxValueLength)}`\r\n ),\r\n ]\r\n return ` - ${compact(columns).join(' · ')}`\r\n })\r\n\r\n const remaining = details.items.length - maxItems\r\n if (remaining > 0) {\r\n lines.push(` - …and ${remaining} more`)\r\n }\r\n return lines\r\n}\r\n\r\n/**\r\n * Print the same recommendations the Lighthouse report UI shows, so a run is\r\n * actionable from the terminal/CI log without opening the HTML report.\r\n */\r\nexport const logRecommendations = (\r\n /** The Lighthouse result object */\r\n lhr: Lhr,\r\n {\r\n label,\r\n maxItems = 5,\r\n maxValueLength = 120,\r\n }: {\r\n /** Distinguishes runs of the same URL in one log (e.g. `desktop`/`mobile`). */\r\n label?: string\r\n } & FormattingArgs\r\n) => {\r\n const lines = [\r\n '',\r\n `───── Lighthouse recommendations${label ? `: ${label}` : ''} — ${lhr.finalDisplayedUrl} ─────`,\r\n ]\r\n\r\n for (const category of Object.values(lhr.categories)) {\r\n const categoryScoreValue =\r\n category.score === null ? 'N/A' : Math.round(category.score * 100)\r\n const categoryLine = `${category.title}: ${categoryScoreValue}`\r\n\r\n const failing = orderBy(\r\n category.auditRefs\r\n .map(({ id }) => lhr.audits[id])\r\n .filter((audit) => !!audit && audit.score !== null && audit.score < 1),\r\n [\r\n (audit) =>\r\n Math.max(0, ...resolveMetricSavings(audit).map(([, ms]) => ms)),\r\n ({ score }) => score ?? 0,\r\n ],\r\n ['desc', 'asc']\r\n )\r\n\r\n if (!failing.length) {\r\n lines.push('', `${categoryLine} — nothing to flag`)\r\n continue\r\n }\r\n\r\n lines.push('', categoryLine)\r\n\r\n for (const audit of failing) {\r\n const displayValue = audit.displayValue ? ` (${audit.displayValue})` : ''\r\n\r\n const savingsValue: string = (() => {\r\n // Most opportunities already spell out their savings in `displayValue`.\r\n if (audit.displayValue?.toLowerCase().includes('savings')) {\r\n return ''\r\n }\r\n\r\n const overallSavingsBytes =\r\n audit.details?.type === 'opportunity'\r\n ? audit.details.overallSavingsBytes\r\n : undefined\r\n const estSavings = compact([\r\n ...resolveMetricSavings(audit).map(\r\n ([metric, value]) =>\r\n `${metric} ${formatValue(value, metricValueType(metric), maxValueLength)}`\r\n ),\r\n formatValue(overallSavingsBytes, 'bytes', maxValueLength),\r\n ])\r\n\r\n return estSavings.length\r\n ? ` — est. savings: ${estSavings.join(', ')}`\r\n : ''\r\n })()\r\n\r\n const scoreValue =\r\n audit.score === null ? 'N/A' : Math.round(audit.score * 100)\r\n\r\n lines.push(` • ${audit.title}${displayValue}${savingsValue}`)\r\n lines.push(` ${audit.id} · score ${scoreValue}`)\r\n lines.push(...formatItems(audit.details, { maxItems, maxValueLength }))\r\n }\r\n }\r\n\r\n lines.push('')\r\n console.log(lines.join('\\n'))\r\n}\r\n","import type { RunnerResult } from 'lighthouse'\n\ntype Lhr = RunnerResult['lhr']\ntype CategoryResult = Lhr['categories'][string]\n\nexport type Category =\n | 'performance'\n | 'accessibility'\n | 'best-practices'\n | 'seo'\n | 'agentic-browsing'\n\n/** Per-category minimums, or one number applied to every category. */\nexport type LighthouseThresholds = number | Partial<Record<Category, number>>\n\n/** A category that scored below the minimum it was given. */\nexport type ThresholdFailure = {\n /** The category's id as the run reported it — usually a `Category`. */\n category: string\n /** The minimum this category was checked against (0-100). */\n minimum: number\n /** What it actually scored (0-100). */\n score: number\n}\n\nconst thresholdFailureMessage = (failures: ThresholdFailure[]) =>\n [\n 'Lighthouse thresholds not met:',\n ...failures.map(\n ({ category, minimum, score }) =>\n `${category} scored ${score}, below the ${minimum} threshold`\n ),\n ].join('\\n')\n\nexport type ThresholdsArgs = {\n /**\n * Minimum category scores (0-100), either per category or one number for all\n * of them. If per category, any category omitted must score 100.\n */\n thresholds?: LighthouseThresholds\n /** Return the failures rather than throwing them. */\n ignoreError?: boolean\n}\n\n/**\n * Checks every category the run scored, throwing an error describing the ones\n * that fell short. Pass `ignoreError` to get those shortfalls back instead, so\n * the caller can log the recommendations before failing.\n */\nexport const checkAgainstThresholds = (\n /** The Lighthouse result object */\n lhr: Lhr,\n { thresholds = 100, ignoreError }: ThresholdsArgs\n): ThresholdFailure[] | undefined => {\n const isFlat = typeof thresholds === 'number'\n const defaultMinimum = isFlat ? thresholds : 100\n const minimums: Record<string, number | undefined> = isFlat\n ? {}\n : (thresholds ?? {})\n\n const failures = Object.values(lhr.categories)\n .filter((c): c is CategoryResult & { score: number } => c.score !== null)\n .map(({ id, score }) => ({\n category: id,\n minimum: minimums[id] ?? defaultMinimum,\n score: score * 100,\n }))\n .filter(({ score, minimum }) => score < minimum)\n\n if (!failures.length) {\n return undefined\n }\n\n if (!ignoreError) {\n throw new Error(thresholdFailureMessage(failures))\n }\n\n return failures\n}\n","import { mkdir, writeFile } from 'node:fs/promises'\nimport path from 'node:path'\nimport type { RunnerResult } from 'lighthouse'\n\nexport type WriteReportsArgs = { directory: string; name: string }\n\n/**\n * Writes each report to `<directory>/<name>.<format>`, using the formats the\n * run's `output` flag asked for.\n */\nexport const writeReports = async (\n result: RunnerResult,\n { directory, name }: WriteReportsArgs\n) => {\n const formats = [result.lhr.configSettings.output].flat()\n\n await mkdir(directory, { recursive: true })\n await Promise.all(\n [result.report]\n .flat()\n .map((report, i) =>\n writeFile(path.join(directory, `${name}.${formats[i]}`), report)\n )\n )\n}\n","import type { RunnerResult } from 'lighthouse'\n\nimport { type FormattingArgs, logRecommendations } from './log-recommendations'\nimport { checkAgainstThresholds, type ThresholdsArgs } from './thresholds'\nimport { type WriteReportsArgs, writeReports } from './write-reports'\n\nexport type HandleAuditResultArgs = {\n /** The full `RunnerResult` from a Lighthouse run */\n result: RunnerResult\n /** Where to write the reports. Omit to skip writing them. */\n reports?: WriteReportsArgs\n /** Options for recommendations logging. Set to `false` to disable. */\n recommendations?:\n | (FormattingArgs & {\n /** Distinguishes runs of the same URL in one log. Defaults to `reports.name`. */\n label?: string\n })\n | false\n} & ThresholdsArgs\n\n/**\n * Everything you'd do with a finished Lighthouse run:\n * 1. write the reports\n * 2. log the recommendations\n * 3. check the scores against the thresholds (goes last so reporting occurs before throwing).\n *\n * @returns the threshold failures, if `ignoreError` kept them from throwing\n */\nexport const handleAuditResult = async ({\n result,\n reports,\n thresholds,\n ignoreError,\n recommendations,\n}: HandleAuditResultArgs) => {\n if (reports) {\n await writeReports(result, reports)\n }\n\n if (recommendations !== false) {\n logRecommendations(result.lhr, {\n label: reports?.name,\n ...recommendations,\n })\n }\n\n const failures = checkAgainstThresholds(result.lhr, {\n thresholds,\n ignoreError,\n })\n\n return failures\n}\n","import lighthouse, { type Config, type Flags } from 'lighthouse'\n\nimport {\n type HandleAuditResultArgs,\n handleAuditResult,\n} from './handle-audit-result'\n\n/** What `lighthouse()` itself takes, in the order it takes it. */\nexport type LighthouseArgs = {\n /** The URL to audit */\n url: string\n /** Settings for the run, e.g. the `port` Lighthouse connects to */\n flags?: Flags\n /** Overrides the default config, e.g. `desktopConfig` */\n config?: Config\n}\n\n/**\n * Audits a URL and hands the result straight to {@link handleAuditResult}.\n *\n * @returns the run's result, plus the threshold failures if `ignoreError` kept\n * them from throwing\n */\nexport const runAudit = async ({\n lighthouseArgs: { url, flags, config },\n ...handleArgs\n}: {\n /** Passed through to `lighthouse()` */\n lighthouseArgs: LighthouseArgs\n} & Omit<HandleAuditResultArgs, 'result'>) => {\n const result = await lighthouse(url, flags, config)\n if (!result) {\n throw new Error(`Lighthouse returned no result for ${url}`)\n }\n\n const failures = await handleAuditResult({ result, ...handleArgs })\n\n return { result, failures }\n}\n"]}
|
package/dist/index.js
CHANGED
|
@@ -1,196 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import lighthouse from 'lighthouse';
|
|
4
|
-
|
|
5
|
-
// lighthouse-audit-utils 1.0.0
|
|
6
|
-
|
|
7
|
-
// src/log-recommendations.ts
|
|
8
|
-
var resolveMetricSavings = (audit) => Object.entries(audit.metricSavings ?? {}).filter(([, ms]) => !!ms);
|
|
9
|
-
var maxMetricSavings = (audit) => Math.max(0, ...resolveMetricSavings(audit).map(([, ms]) => ms));
|
|
10
|
-
var metricValueType = (metric) => metric === "CLS" ? "numeric" : "ms";
|
|
11
|
-
var MEASURE_TYPES = [
|
|
12
|
-
"bytes",
|
|
13
|
-
"ms",
|
|
14
|
-
"timespanMs",
|
|
15
|
-
"numeric"
|
|
16
|
-
];
|
|
17
|
-
var formatValue = (value, valueType, maxValueLength) => {
|
|
18
|
-
if (value === void 0 || typeof value === "boolean") {
|
|
19
|
-
return "";
|
|
20
|
-
}
|
|
21
|
-
if (typeof value === "number") {
|
|
22
|
-
if (valueType === "bytes") {
|
|
23
|
-
const kib = value / 1024;
|
|
24
|
-
const rounded = kib >= 100 ? Math.round(kib) : Math.round(kib * 10) / 10;
|
|
25
|
-
return `${rounded} KiB`;
|
|
26
|
-
}
|
|
27
|
-
if (valueType === "ms" || valueType === "timespanMs") {
|
|
28
|
-
return `${Math.round(value)} ms`;
|
|
29
|
-
}
|
|
30
|
-
return String(Math.round(value * 100) / 100);
|
|
31
|
-
}
|
|
32
|
-
const text = (() => {
|
|
33
|
-
if (typeof value === "string") {
|
|
34
|
-
return value;
|
|
35
|
-
}
|
|
36
|
-
if (!("type" in value)) {
|
|
37
|
-
return "";
|
|
38
|
-
}
|
|
39
|
-
switch (value.type) {
|
|
40
|
-
case "url":
|
|
41
|
-
return value.value;
|
|
42
|
-
case "link":
|
|
43
|
-
return value.url || value.text;
|
|
44
|
-
case "code":
|
|
45
|
-
return String(value.value);
|
|
46
|
-
case "node":
|
|
47
|
-
return value.nodeLabel ?? value.selector ?? value.snippet ?? "";
|
|
48
|
-
case "source-location":
|
|
49
|
-
return `${value.url}:${value.line}:${value.column}`;
|
|
50
|
-
default:
|
|
51
|
-
return "";
|
|
52
|
-
}
|
|
53
|
-
})();
|
|
54
|
-
const singleLine = text.replace(/\s+/g, " ").trim();
|
|
55
|
-
return singleLine.length > maxValueLength ? `${singleLine.slice(0, maxValueLength)}\u2026` : singleLine;
|
|
56
|
-
};
|
|
57
|
-
var formatItems = (details, { maxItems, maxValueLength }) => {
|
|
58
|
-
if (details?.type !== "opportunity" && details?.type !== "table") {
|
|
59
|
-
return [];
|
|
60
|
-
}
|
|
61
|
-
const [identity, ...measures] = details.headings.filter(({ key }) => !!key);
|
|
62
|
-
if (!identity) {
|
|
63
|
-
return [];
|
|
64
|
-
}
|
|
65
|
-
const lines = details.items.slice(0, maxItems).map((item) => {
|
|
66
|
-
const columns = [
|
|
67
|
-
formatValue(item[identity.key ?? ""], identity.valueType, maxValueLength),
|
|
68
|
-
...measures.filter(({ valueType }) => MEASURE_TYPES.includes(valueType)).map(
|
|
69
|
-
(heading) => `${heading.label}: ${formatValue(item[heading.key ?? ""], heading.valueType, maxValueLength)}`
|
|
70
|
-
)
|
|
71
|
-
];
|
|
72
|
-
return ` - ${columns.filter(Boolean).join(" \xB7 ")}`;
|
|
73
|
-
});
|
|
74
|
-
const remaining = details.items.length - maxItems;
|
|
75
|
-
if (remaining > 0) {
|
|
76
|
-
lines.push(` - \u2026and ${remaining} more`);
|
|
77
|
-
}
|
|
78
|
-
return lines;
|
|
79
|
-
};
|
|
80
|
-
var logRecommendations = (lhr, {
|
|
81
|
-
label,
|
|
82
|
-
maxItems = 5,
|
|
83
|
-
maxValueLength = 120
|
|
84
|
-
}) => {
|
|
85
|
-
const lines = [
|
|
86
|
-
"",
|
|
87
|
-
`\u2500\u2500\u2500\u2500\u2500 Lighthouse recommendations${label ? `: ${label}` : ""} \u2014 ${lhr.finalDisplayedUrl} \u2500\u2500\u2500\u2500\u2500`
|
|
88
|
-
];
|
|
89
|
-
for (const category of Object.values(lhr.categories)) {
|
|
90
|
-
const categoryScoreValue = category.score === null ? "N/A" : Math.round(category.score * 100);
|
|
91
|
-
const categoryLine = `${category.title}: ${categoryScoreValue}`;
|
|
92
|
-
const failing = category.auditRefs.map(({ id }) => lhr.audits[id]).filter((audit) => !!audit && audit.score !== null && audit.score < 1).sort(
|
|
93
|
-
(a, b) => maxMetricSavings(b) - maxMetricSavings(a) || (a.score ?? 0) - (b.score ?? 0)
|
|
94
|
-
);
|
|
95
|
-
if (!failing.length) {
|
|
96
|
-
lines.push("", `${categoryLine} \u2014 nothing to fix`);
|
|
97
|
-
continue;
|
|
98
|
-
}
|
|
99
|
-
lines.push("", categoryLine);
|
|
100
|
-
for (const audit of failing) {
|
|
101
|
-
const displayValue = audit.displayValue ? ` (${audit.displayValue})` : "";
|
|
102
|
-
const savingsValue = (() => {
|
|
103
|
-
if (audit.displayValue?.toLowerCase().includes("savings")) {
|
|
104
|
-
return "";
|
|
105
|
-
}
|
|
106
|
-
const overallSavingsBytes = audit.details?.type === "opportunity" ? audit.details.overallSavingsBytes : void 0;
|
|
107
|
-
const estSavings = [
|
|
108
|
-
...resolveMetricSavings(audit).map(
|
|
109
|
-
([metric, value]) => `${metric} ${formatValue(value, metricValueType(metric), maxValueLength)}`
|
|
110
|
-
),
|
|
111
|
-
formatValue(overallSavingsBytes, "bytes", maxValueLength)
|
|
112
|
-
].filter(Boolean);
|
|
113
|
-
return estSavings.length ? ` \u2014 est. savings: ${estSavings.join(", ")}` : "";
|
|
114
|
-
})();
|
|
115
|
-
const scoreValue = audit.score === null ? "N/A" : Math.round(audit.score * 100);
|
|
116
|
-
lines.push(` \u2022 ${audit.title}${displayValue}${savingsValue}`);
|
|
117
|
-
lines.push(` ${audit.id} \xB7 score ${scoreValue}`);
|
|
118
|
-
lines.push(...formatItems(audit.details, { maxItems, maxValueLength }));
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
lines.push("");
|
|
122
|
-
console.log(lines.join("\n"));
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
// src/thresholds.ts
|
|
126
|
-
var thresholdFailureMessage = (failures) => [
|
|
127
|
-
"Lighthouse thresholds not met:",
|
|
128
|
-
...failures.map(
|
|
129
|
-
({ category, minimum, score }) => `${category} scored ${score}, below the ${minimum} threshold`
|
|
130
|
-
)
|
|
131
|
-
].join("\n");
|
|
132
|
-
var checkAgainstThresholds = (lhr, { thresholds = 100, ignoreError }) => {
|
|
133
|
-
const isFlat = typeof thresholds === "number";
|
|
134
|
-
const defaultMinimum = isFlat ? thresholds : 100;
|
|
135
|
-
const minimums = isFlat ? {} : thresholds ?? {};
|
|
136
|
-
const failures = Object.values(lhr.categories).filter((c) => c.score !== null).map(({ id, score }) => ({
|
|
137
|
-
category: id,
|
|
138
|
-
minimum: minimums[id] ?? defaultMinimum,
|
|
139
|
-
score: score * 100
|
|
140
|
-
})).filter(({ score, minimum }) => score < minimum);
|
|
141
|
-
if (!failures.length) {
|
|
142
|
-
return void 0;
|
|
143
|
-
}
|
|
144
|
-
if (!ignoreError) {
|
|
145
|
-
throw new Error(thresholdFailureMessage(failures));
|
|
146
|
-
}
|
|
147
|
-
return failures;
|
|
148
|
-
};
|
|
149
|
-
var writeReports = async (result, { directory, name }) => {
|
|
150
|
-
const formats = [result.lhr.configSettings.output].flat();
|
|
151
|
-
await mkdir(directory, { recursive: true });
|
|
152
|
-
await Promise.all(
|
|
153
|
-
[result.report].flat().map(
|
|
154
|
-
(report, i) => writeFile(path.join(directory, `${name}.${formats[i]}`), report)
|
|
155
|
-
)
|
|
156
|
-
);
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
// src/handle-audit-result.ts
|
|
160
|
-
var handleAuditResult = async ({
|
|
161
|
-
result,
|
|
162
|
-
reports,
|
|
163
|
-
thresholds,
|
|
164
|
-
ignoreError,
|
|
165
|
-
recommendations
|
|
166
|
-
}) => {
|
|
167
|
-
if (reports) {
|
|
168
|
-
await writeReports(result, reports);
|
|
169
|
-
}
|
|
170
|
-
if (recommendations !== false) {
|
|
171
|
-
logRecommendations(result.lhr, {
|
|
172
|
-
label: reports?.name,
|
|
173
|
-
...recommendations
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
const failures = checkAgainstThresholds(result.lhr, {
|
|
177
|
-
thresholds,
|
|
178
|
-
ignoreError
|
|
179
|
-
});
|
|
180
|
-
return failures;
|
|
181
|
-
};
|
|
182
|
-
var runAudit = async ({
|
|
183
|
-
lighthouseArgs: { url, flags, config },
|
|
184
|
-
...handleArgs
|
|
185
|
-
}) => {
|
|
186
|
-
const result = await lighthouse(url, flags, config);
|
|
187
|
-
if (!result) {
|
|
188
|
-
throw new Error(`Lighthouse returned no result for ${url}`);
|
|
189
|
-
}
|
|
190
|
-
const failures = await handleAuditResult({ result, ...handleArgs });
|
|
191
|
-
return { result, failures };
|
|
192
|
-
};
|
|
193
|
-
|
|
194
|
-
export { checkAgainstThresholds, handleAuditResult, logRecommendations, runAudit, writeReports };
|
|
1
|
+
export { checkAgainstThresholds, handleAuditResult, logRecommendations, runAudit, writeReports } from './chunk-4DICEJP6.js';
|
|
195
2
|
//# sourceMappingURL=index.js.map
|
|
196
3
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/log-recommendations.ts","../src/thresholds.ts","../src/write-reports.ts","../src/handle-audit-result.ts","../src/run-audit.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { RunnerResult } from 'lighthouse'\nimport type Details from 'lighthouse/types/lhr/audit-details'\n\ntype Lhr = RunnerResult['lhr']\ntype Audit = Lhr['audits'][string]\ntype AuditDetails = NonNullable<Audit['details']>\n\n/** resolves to values that are only non-zero numbers */\nconst resolveMetricSavings = (audit: Audit) =>\n Object.entries(audit.metricSavings ?? {}).filter(([, ms]) => !!ms) as [\n string,\n number,\n ][]\n\nconst maxMetricSavings = (audit: Audit) =>\n Math.max(0, ...resolveMetricSavings(audit).map(([, ms]) => ms))\n\n/** CLS savings are a unitless shift score; every other metric is milliseconds. */\nconst metricValueType = (metric: string): Details.ItemValueType =>\n metric === 'CLS' ? 'numeric' : 'ms'\n\n/** Columns worth repeating alongside an item's identity (URL, node, ...). */\nconst MEASURE_TYPES: Details.ItemValueType[] = [\n 'bytes',\n 'ms',\n 'timespanMs',\n 'numeric',\n]\n\nexport type FormattingArgs = {\n /**\n * Rows shown per audit before collapsing to \"…and N more\"\n * @default 5\n */\n maxItems?: number\n /**\n * Max length of a single value before it's truncated with an \"…\"\n * @default 120\n */\n maxValueLength?: number\n}\n\nconst formatValue = (\n value: Details.ItemValue | undefined,\n valueType: Details.ItemValueType,\n maxValueLength: number\n): string => {\n if (value === undefined || typeof value === 'boolean') {\n return ''\n }\n\n if (typeof value === 'number') {\n if (valueType === 'bytes') {\n // Keep a decimal on small values so sub-KiB assets don't read as \"0 KiB\".\n const kib = value / 1024\n const rounded = kib >= 100 ? Math.round(kib) : Math.round(kib * 10) / 10\n return `${rounded} KiB`\n }\n if (valueType === 'ms' || valueType === 'timespanMs') {\n return `${Math.round(value)} ms`\n }\n return String(Math.round(value * 100) / 100)\n }\n\n // `IcuMessage` is the only object value without a `type` tag.\n const text = (() => {\n if (typeof value === 'string') {\n return value\n }\n if (!('type' in value)) {\n return ''\n }\n switch (value.type) {\n case 'url':\n return value.value\n case 'link':\n return value.url || value.text\n case 'code':\n return String(value.value)\n case 'node':\n return value.nodeLabel ?? value.selector ?? value.snippet ?? ''\n case 'source-location':\n return `${value.url}:${value.line}:${value.column}`\n default:\n return ''\n }\n })()\n\n // Node labels and snippets carry the element's own line breaks — flatten them\n // so one item stays on one line.\n const singleLine = text.replace(/\\s+/g, ' ').trim()\n\n return singleLine.length > maxValueLength\n ? `${singleLine.slice(0, maxValueLength)}…`\n : singleLine\n}\n\n/**\n * The individual offenders the report UI lists under an audit — the specific\n * render-blocking scripts, oversized images, failing DOM nodes, and so on.\n */\nconst formatItems = (\n details: AuditDetails | undefined,\n { maxItems, maxValueLength }: Required<FormattingArgs>\n): string[] => {\n if (details?.type !== 'opportunity' && details?.type !== 'table') {\n return []\n }\n\n const [identity, ...measures] = details.headings.filter(({ key }) => !!key)\n if (!identity) {\n return []\n }\n\n const lines = details.items.slice(0, maxItems).map((item) => {\n const columns = [\n formatValue(item[identity.key ?? ''], identity.valueType, maxValueLength),\n ...measures\n .filter(({ valueType }) => MEASURE_TYPES.includes(valueType))\n .map(\n (heading) =>\n `${heading.label}: ${formatValue(item[heading.key ?? ''], heading.valueType, maxValueLength)}`\n ),\n ]\n return ` - ${columns.filter(Boolean).join(' · ')}`\n })\n\n const remaining = details.items.length - maxItems\n if (remaining > 0) {\n lines.push(` - …and ${remaining} more`)\n }\n return lines\n}\n\n/**\n * Print the same fix list the Lighthouse report UI shows, so a failing run is\n * actionable from the terminal/CI log without opening the HTML report.\n */\nexport const logRecommendations = (\n /** The Lighthouse result object */\n lhr: Lhr,\n {\n label,\n maxItems = 5,\n maxValueLength = 120,\n }: {\n /** Distinguishes runs of the same URL in one log (e.g. `desktop`/`mobile`). */\n label?: string\n } & FormattingArgs\n) => {\n const lines = [\n '',\n `───── Lighthouse recommendations${label ? `: ${label}` : ''} — ${lhr.finalDisplayedUrl} ─────`,\n ]\n\n for (const category of Object.values(lhr.categories)) {\n const categoryScoreValue =\n category.score === null ? 'N/A' : Math.round(category.score * 100)\n const categoryLine = `${category.title}: ${categoryScoreValue}`\n\n const failing = category.auditRefs\n .map(({ id }) => lhr.audits[id])\n .filter((audit) => !!audit && audit.score !== null && audit.score < 1)\n .sort(\n (a, b) =>\n maxMetricSavings(b) - maxMetricSavings(a) ||\n (a.score ?? 0) - (b.score ?? 0)\n )\n\n if (!failing.length) {\n lines.push('', `${categoryLine} — nothing to fix`)\n continue\n }\n\n lines.push('', categoryLine)\n\n for (const audit of failing) {\n const displayValue = audit.displayValue ? ` (${audit.displayValue})` : ''\n\n const savingsValue: string = (() => {\n // Most opportunities already spell out their savings in `displayValue`.\n if (audit.displayValue?.toLowerCase().includes('savings')) {\n return ''\n }\n\n const overallSavingsBytes =\n audit.details?.type === 'opportunity'\n ? audit.details.overallSavingsBytes\n : undefined\n const estSavings = [\n ...resolveMetricSavings(audit).map(\n ([metric, value]) =>\n `${metric} ${formatValue(value, metricValueType(metric), maxValueLength)}`\n ),\n formatValue(overallSavingsBytes, 'bytes', maxValueLength),\n ].filter(Boolean)\n\n return estSavings.length\n ? ` — est. savings: ${estSavings.join(', ')}`\n : ''\n })()\n\n const scoreValue =\n audit.score === null ? 'N/A' : Math.round(audit.score * 100)\n\n lines.push(` • ${audit.title}${displayValue}${savingsValue}`)\n lines.push(` ${audit.id} · score ${scoreValue}`)\n lines.push(...formatItems(audit.details, { maxItems, maxValueLength }))\n }\n }\n\n lines.push('')\n console.log(lines.join('\\n'))\n}\n","import type { RunnerResult } from 'lighthouse'\n\ntype Lhr = RunnerResult['lhr']\ntype CategoryResult = Lhr['categories'][string]\n\nexport type Category =\n | 'performance'\n | 'accessibility'\n | 'best-practices'\n | 'seo'\n\n/** Per-category minimums, or one number applied to every category. */\nexport type LighthouseThresholds = number | Partial<Record<Category, number>>\n\n/** A category that scored below the minimum it was given. */\nexport type ThresholdFailure = {\n /** The category's id as the run reported it — usually a `Category`. */\n category: string\n /** The minimum this category was checked against (0-100). */\n minimum: number\n /** What it actually scored (0-100). */\n score: number\n}\n\nconst thresholdFailureMessage = (failures: ThresholdFailure[]) =>\n [\n 'Lighthouse thresholds not met:',\n ...failures.map(\n ({ category, minimum, score }) =>\n `${category} scored ${score}, below the ${minimum} threshold`\n ),\n ].join('\\n')\n\nexport type ThresholdsArgs = {\n /**\n * Minimum category scores (0-100), either per category or one number for all\n * of them. If per category, any category omitted must score 100.\n */\n thresholds?: LighthouseThresholds\n /** Return the failures rather than throwing them. */\n ignoreError?: boolean\n}\n\n/**\n * Checks every category the run scored, throwing an error describing the ones\n * that fell short. Pass `ignoreError` to get those shortfalls back instead, so\n * the caller can log the recommendations before failing.\n */\nexport const checkAgainstThresholds = (\n /** The Lighthouse result object */\n lhr: Lhr,\n { thresholds = 100, ignoreError }: ThresholdsArgs\n): ThresholdFailure[] | undefined => {\n const isFlat = typeof thresholds === 'number'\n const defaultMinimum = isFlat ? thresholds : 100\n const minimums: Record<string, number | undefined> = isFlat\n ? {}\n : (thresholds ?? {})\n\n const failures = Object.values(lhr.categories)\n .filter((c): c is CategoryResult & { score: number } => c.score !== null)\n .map(({ id, score }) => ({\n category: id,\n minimum: minimums[id] ?? defaultMinimum,\n score: score * 100,\n }))\n .filter(({ score, minimum }) => score < minimum)\n\n if (!failures.length) {\n return undefined\n }\n\n if (!ignoreError) {\n throw new Error(thresholdFailureMessage(failures))\n }\n\n return failures\n}\n","import { mkdir, writeFile } from 'node:fs/promises'\nimport path from 'node:path'\nimport type { RunnerResult } from 'lighthouse'\n\nexport type WriteReportsArgs = { directory: string; name: string }\n\n/**\n * Writes each report to `<directory>/<name>.<format>`, using the formats the\n * run's `output` flag asked for.\n */\nexport const writeReports = async (\n result: RunnerResult,\n { directory, name }: WriteReportsArgs\n) => {\n const formats = [result.lhr.configSettings.output].flat()\n\n await mkdir(directory, { recursive: true })\n await Promise.all(\n [result.report]\n .flat()\n .map((report, i) =>\n writeFile(path.join(directory, `${name}.${formats[i]}`), report)\n )\n )\n}\n","import type { RunnerResult } from 'lighthouse'\n\nimport { type FormattingArgs, logRecommendations } from './log-recommendations'\nimport { checkAgainstThresholds, type ThresholdsArgs } from './thresholds'\nimport { type WriteReportsArgs, writeReports } from './write-reports'\n\nexport type HandleAuditResultArgs = {\n /** The full `RunnerResult` from a Lighthouse run */\n result: RunnerResult\n /** Where to write the reports. Omit to skip writing them. */\n reports?: WriteReportsArgs\n /** Options for recommendations logging. Set to `false` to disable. */\n recommendations?:\n | (FormattingArgs & {\n /** Distinguishes runs of the same URL in one log. Defaults to `reports.name`. */\n label?: string\n })\n | false\n} & ThresholdsArgs\n\n/**\n * Everything you'd do with a finished Lighthouse run:\n * 1. write the reports\n * 2. log the recommendations\n * 3. check the scores against the thresholds (goes last so reporting occurs before throwing).\n *\n * @returns the threshold failures, if `ignoreError` kept them from throwing\n */\nexport const handleAuditResult = async ({\n result,\n reports,\n thresholds,\n ignoreError,\n recommendations,\n}: HandleAuditResultArgs) => {\n if (reports) {\n await writeReports(result, reports)\n }\n\n if (recommendations !== false) {\n logRecommendations(result.lhr, {\n label: reports?.name,\n ...recommendations,\n })\n }\n\n const failures = checkAgainstThresholds(result.lhr, {\n thresholds,\n ignoreError,\n })\n\n return failures\n}\n","import lighthouse, { type Config, type Flags } from 'lighthouse'\n\nimport {\n type HandleAuditResultArgs,\n handleAuditResult,\n} from './handle-audit-result'\n\n/** What `lighthouse()` itself takes, in the order it takes it. */\nexport type LighthouseArgs = {\n /** The URL to audit */\n url: string\n /** Settings for the run, e.g. the `port` Lighthouse connects to */\n flags?: Flags\n /** Overrides the default config, e.g. `desktopConfig` */\n config?: Config\n}\n\n/**\n * Audits a URL and hands the result straight to {@link handleAuditResult}.\n *\n * @returns the run's result, plus the threshold failures if `ignoreError` kept\n * them from throwing\n */\nexport const runAudit = async ({\n lighthouseArgs: { url, flags, config },\n ...handleArgs\n}: {\n /** Passed through to `lighthouse()` */\n lighthouseArgs: LighthouseArgs\n} & Omit<HandleAuditResultArgs, 'result'>) => {\n const result = await lighthouse(url, flags, config)\n if (!result) {\n throw new Error(`Lighthouse returned no result for ${url}`)\n }\n\n const failures = await handleAuditResult({ result, ...handleArgs })\n\n return { result, failures }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
|
|
@@ -13,7 +13,7 @@ export type FormattingArgs = {
|
|
|
13
13
|
maxValueLength?: number;
|
|
14
14
|
};
|
|
15
15
|
/**
|
|
16
|
-
* Print the same
|
|
16
|
+
* Print the same recommendations the Lighthouse report UI shows, so a run is
|
|
17
17
|
* actionable from the terminal/CI log without opening the HTML report.
|
|
18
18
|
*/
|
|
19
19
|
export declare const logRecommendations: (
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { chromium, type PlaywrightTestArgs, type PlaywrightTestOptions, type PlaywrightWorkerArgs, type PlaywrightWorkerOptions, type TestInfo, type TestType } from '@playwright/test';
|
|
2
|
+
import type { HandleAuditResultArgs } from './handle-audit-result';
|
|
3
|
+
import type { LighthouseArgs } from './run-audit';
|
|
4
|
+
import { runAudit as runLighthouse } from './run-audit';
|
|
5
|
+
import type { WriteReportsArgs } from './write-reports';
|
|
6
|
+
/** What `runAudit` takes on top of the overrides it shares with the fixture. */
|
|
7
|
+
export type RunAuditOptions = {
|
|
8
|
+
/** Names this run's reports, so audits in the same test don't overwrite each other. */
|
|
9
|
+
name?: string;
|
|
10
|
+
} & AuditOverrides;
|
|
11
|
+
/** Settable per fixture and again per `runAudit` call, the later one winning. */
|
|
12
|
+
type AuditOverrides = {
|
|
13
|
+
/**
|
|
14
|
+
* - Passed to `lighthouse()`, minus the `url` — that's whatever page the test
|
|
15
|
+
* is on.
|
|
16
|
+
* - `flags` merge over the worker's `port`, the only one set for you
|
|
17
|
+
* - `config` deep merges, so `settings` layer rather than replacing each other.
|
|
18
|
+
* - Config arrays merge by index, so list settings like `skipAudits` are best
|
|
19
|
+
* set at one level only.
|
|
20
|
+
*/
|
|
21
|
+
lighthouseArgs?: Omit<LighthouseArgs, 'url'>;
|
|
22
|
+
} & Omit<HandleAuditResultArgs, 'result' | 'reports'>;
|
|
23
|
+
export type LighthouseFixtures = {
|
|
24
|
+
/**
|
|
25
|
+
* Runs one Lighthouse audit against the current page. Call it again for a
|
|
26
|
+
* second form factor, passing that form factor's `config` and `name`.
|
|
27
|
+
*/
|
|
28
|
+
runAudit: (options?: RunAuditOptions) => ReturnType<typeof runLighthouse>;
|
|
29
|
+
};
|
|
30
|
+
export type LighthouseWorkerFixtures = {
|
|
31
|
+
/** CDP port Lighthouse connects to. Unique per worker so parallel workers don't collide. */
|
|
32
|
+
lighthousePort: number;
|
|
33
|
+
};
|
|
34
|
+
/** Everything the reports' location can be decided from. */
|
|
35
|
+
export type ReportsContext = {
|
|
36
|
+
testInfo: TestInfo;
|
|
37
|
+
/** The `name` the `runAudit` call passed, if any. */
|
|
38
|
+
name?: string;
|
|
39
|
+
};
|
|
40
|
+
export type LighthouseFixtureOptions = {
|
|
41
|
+
/** First worker's CDP port, e.g. `9222`; each further worker gets the next one up. */
|
|
42
|
+
basePort: number;
|
|
43
|
+
/** Where each run's reports go. Pass `false` to skip writing them. */
|
|
44
|
+
reports?: ((context: ReportsContext) => WriteReportsArgs) | false;
|
|
45
|
+
/** Merged into the persistent context launch, which already sets the CDP port and `baseURL`. */
|
|
46
|
+
launchOptions?: Parameters<typeof chromium.launchPersistentContext>[1];
|
|
47
|
+
} & AuditOverrides;
|
|
48
|
+
type BaseTest = TestType<PlaywrightTestArgs & PlaywrightTestOptions, PlaywrightWorkerArgs & PlaywrightWorkerOptions>;
|
|
49
|
+
/**
|
|
50
|
+
* Adds a `runAudit` fixture that audits whatever page the test is currently on.
|
|
51
|
+
*
|
|
52
|
+
* Lighthouse navigates over the CDP port itself rather than driving the
|
|
53
|
+
* Playwright `page`, so `context` is overridden to launch a persistent Chrome
|
|
54
|
+
* profile reachable on that port — the page Playwright drives and the page
|
|
55
|
+
* Lighthouse audits then live in the same profile.
|
|
56
|
+
*
|
|
57
|
+
* @param test the test to extend, so callers can layer this onto their own fixtures
|
|
58
|
+
*/
|
|
59
|
+
export declare const withLighthouse: <T extends BaseTest>({ basePort, reports, launchOptions, ...fixtureOverrides }: LighthouseFixtureOptions, test?: T) => TestType<PlaywrightTestArgs & PlaywrightTestOptions & LighthouseFixtures, PlaywrightWorkerArgs & PlaywrightWorkerOptions & LighthouseWorkerFixtures>;
|
|
60
|
+
export {};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { runAudit, toMerged } from './chunk-4DICEJP6.js';
|
|
2
|
+
import { rm } from 'fs/promises';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { test, chromium } from '@playwright/test';
|
|
6
|
+
|
|
7
|
+
var defaultReports = ({ testInfo, name }) => ({
|
|
8
|
+
directory: testInfo.outputPath("lighthouse"),
|
|
9
|
+
name: name ?? "lighthouse"
|
|
10
|
+
});
|
|
11
|
+
var mergeThresholds = (base, override) => {
|
|
12
|
+
if (override === void 0) {
|
|
13
|
+
return base;
|
|
14
|
+
}
|
|
15
|
+
if (typeof override === "number" || typeof base !== "object") {
|
|
16
|
+
return override;
|
|
17
|
+
}
|
|
18
|
+
return { ...base, ...override };
|
|
19
|
+
};
|
|
20
|
+
var withLighthouse = ({
|
|
21
|
+
basePort,
|
|
22
|
+
reports = defaultReports,
|
|
23
|
+
launchOptions,
|
|
24
|
+
...fixtureOverrides
|
|
25
|
+
}, test$1 = test) => test$1.extend({
|
|
26
|
+
runAudit: async ({ page, lighthousePort: port }, use, testInfo) => {
|
|
27
|
+
await use(({ name, lighthouseArgs, thresholds, ...callArgs } = {}) => {
|
|
28
|
+
const {
|
|
29
|
+
lighthouseArgs: baseArgs,
|
|
30
|
+
thresholds: baseThresholds,
|
|
31
|
+
...handleArgs
|
|
32
|
+
} = { ...fixtureOverrides, ...callArgs };
|
|
33
|
+
return runAudit({
|
|
34
|
+
lighthouseArgs: {
|
|
35
|
+
url: page.url(),
|
|
36
|
+
flags: { port, ...baseArgs?.flags, ...lighthouseArgs?.flags },
|
|
37
|
+
config: toMerged(
|
|
38
|
+
baseArgs?.config ?? {},
|
|
39
|
+
lighthouseArgs?.config ?? {}
|
|
40
|
+
)
|
|
41
|
+
},
|
|
42
|
+
reports: reports ? reports({ testInfo, name }) : void 0,
|
|
43
|
+
thresholds: mergeThresholds(baseThresholds, thresholds),
|
|
44
|
+
...handleArgs
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
},
|
|
48
|
+
lighthousePort: [
|
|
49
|
+
// biome-ignore lint/correctness/noEmptyPattern: Playwright requires destructuring for the fixtures arg
|
|
50
|
+
async ({}, use, workerInfo) => {
|
|
51
|
+
await use(basePort + workerInfo.parallelIndex);
|
|
52
|
+
},
|
|
53
|
+
{ scope: "worker" }
|
|
54
|
+
],
|
|
55
|
+
context: async ({ lighthousePort: port, baseURL }, use) => {
|
|
56
|
+
const userDataDir = path.join(
|
|
57
|
+
os.tmpdir(),
|
|
58
|
+
`pw-lighthouse-${port}-${Date.now()}`
|
|
59
|
+
);
|
|
60
|
+
const context = await chromium.launchPersistentContext(userDataDir, {
|
|
61
|
+
baseURL,
|
|
62
|
+
...launchOptions,
|
|
63
|
+
args: [
|
|
64
|
+
`--remote-debugging-port=${port}`,
|
|
65
|
+
...launchOptions?.args ?? []
|
|
66
|
+
]
|
|
67
|
+
});
|
|
68
|
+
await use(context);
|
|
69
|
+
await context.close();
|
|
70
|
+
await rm(userDataDir, { recursive: true, force: true }).catch(
|
|
71
|
+
() => void 0
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
export { withLighthouse };
|
|
77
|
+
//# sourceMappingURL=playwright.js.map
|
|
78
|
+
//# sourceMappingURL=playwright.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/playwright.ts"],"names":["test","baseTest"],"mappings":"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","file":"playwright.js","sourcesContent":["import { rm } from 'node:fs/promises'\nimport os from 'node:os'\nimport path from 'node:path'\nimport {\n test as baseTest,\n chromium,\n type PlaywrightTestArgs,\n type PlaywrightTestOptions,\n type PlaywrightWorkerArgs,\n type PlaywrightWorkerOptions,\n type TestInfo,\n type TestType,\n} from '@playwright/test'\nimport { toMerged } from 'es-toolkit'\n\nimport type { HandleAuditResultArgs } from './handle-audit-result'\nimport type { LighthouseArgs } from './run-audit'\nimport { runAudit as runLighthouse } from './run-audit'\nimport type { WriteReportsArgs } from './write-reports'\n\n/** What `runAudit` takes on top of the overrides it shares with the fixture. */\nexport type RunAuditOptions = {\n /** Names this run's reports, so audits in the same test don't overwrite each other. */\n name?: string\n} & AuditOverrides\n\n/** Settable per fixture and again per `runAudit` call, the later one winning. */\ntype AuditOverrides = {\n /**\n * - Passed to `lighthouse()`, minus the `url` — that's whatever page the test\n * is on.\n * - `flags` merge over the worker's `port`, the only one set for you\n * - `config` deep merges, so `settings` layer rather than replacing each other.\n * - Config arrays merge by index, so list settings like `skipAudits` are best\n * set at one level only.\n */\n lighthouseArgs?: Omit<LighthouseArgs, 'url'>\n} & Omit<HandleAuditResultArgs, 'result' | 'reports'>\n\nexport type LighthouseFixtures = {\n /**\n * Runs one Lighthouse audit against the current page. Call it again for a\n * second form factor, passing that form factor's `config` and `name`.\n */\n runAudit: (options?: RunAuditOptions) => ReturnType<typeof runLighthouse>\n}\n\nexport type LighthouseWorkerFixtures = {\n /** CDP port Lighthouse connects to. Unique per worker so parallel workers don't collide. */\n lighthousePort: number\n}\n\n/** Everything the reports' location can be decided from. */\nexport type ReportsContext = {\n testInfo: TestInfo\n /** The `name` the `runAudit` call passed, if any. */\n name?: string\n}\n\nexport type LighthouseFixtureOptions = {\n /** First worker's CDP port, e.g. `9222`; each further worker gets the next one up. */\n basePort: number\n /** Where each run's reports go. Pass `false` to skip writing them. */\n reports?: ((context: ReportsContext) => WriteReportsArgs) | false\n /** Merged into the persistent context launch, which already sets the CDP port and `baseURL`. */\n launchOptions?: Parameters<typeof chromium.launchPersistentContext>[1]\n} & AuditOverrides\n\nconst defaultReports = ({ testInfo, name }: ReportsContext) => ({\n directory: testInfo.outputPath('lighthouse'),\n name: name ?? 'lighthouse',\n})\n\n/** Object thresholds merge; anything else replaces, since a flat number can't be partially overridden. */\nconst mergeThresholds = (\n base: AuditOverrides['thresholds'],\n override: AuditOverrides['thresholds']\n): AuditOverrides['thresholds'] => {\n if (override === undefined) {\n return base\n }\n if (typeof override === 'number' || typeof base !== 'object') {\n return override\n }\n return { ...base, ...override }\n}\n\ntype BaseTest = TestType<\n PlaywrightTestArgs & PlaywrightTestOptions,\n PlaywrightWorkerArgs & PlaywrightWorkerOptions\n>\n\n/**\n * Adds a `runAudit` fixture that audits whatever page the test is currently on.\n *\n * Lighthouse navigates over the CDP port itself rather than driving the\n * Playwright `page`, so `context` is overridden to launch a persistent Chrome\n * profile reachable on that port — the page Playwright drives and the page\n * Lighthouse audits then live in the same profile.\n *\n * @param test the test to extend, so callers can layer this onto their own fixtures\n */\nexport const withLighthouse = <T extends BaseTest>(\n {\n basePort,\n reports = defaultReports,\n launchOptions,\n ...fixtureOverrides\n }: LighthouseFixtureOptions,\n test: T = baseTest as unknown as T\n) =>\n test.extend<LighthouseFixtures, LighthouseWorkerFixtures>({\n runAudit: async ({ page, lighthousePort: port }, use, testInfo) => {\n // `lighthouseArgs` and `thresholds` merge with the fixture's rather than\n // replacing them, so a call only has to name what it changes.\n await use(({ name, lighthouseArgs, thresholds, ...callArgs } = {}) => {\n const {\n lighthouseArgs: baseArgs,\n thresholds: baseThresholds,\n ...handleArgs\n } = { ...fixtureOverrides, ...callArgs }\n\n return runLighthouse({\n lighthouseArgs: {\n url: page.url(),\n flags: { port, ...baseArgs?.flags, ...lighthouseArgs?.flags },\n config: toMerged(\n baseArgs?.config ?? {},\n lighthouseArgs?.config ?? {}\n ),\n },\n reports: reports ? reports({ testInfo, name }) : undefined,\n thresholds: mergeThresholds(baseThresholds, thresholds),\n ...handleArgs,\n })\n })\n },\n\n lighthousePort: [\n // biome-ignore lint/correctness/noEmptyPattern: Playwright requires destructuring for the fixtures arg\n async ({}, use, workerInfo) => {\n await use(basePort + workerInfo.parallelIndex)\n },\n { scope: 'worker' },\n ],\n\n context: async ({ lighthousePort: port, baseURL }, use) => {\n const userDataDir = path.join(\n os.tmpdir(),\n `pw-lighthouse-${port}-${Date.now()}`\n )\n\n const context = await chromium.launchPersistentContext(userDataDir, {\n baseURL,\n ...launchOptions,\n args: [\n `--remote-debugging-port=${port}`,\n ...(launchOptions?.args ?? []),\n ],\n })\n\n await use(context)\n await context.close()\n\n // Best-effort cleanup; a leftover profile dir doesn't affect test correctness.\n await rm(userDataDir, { recursive: true, force: true }).catch(\n () => undefined\n )\n },\n })\n"]}
|
package/dist/thresholds.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { RunnerResult } from 'lighthouse';
|
|
2
2
|
type Lhr = RunnerResult['lhr'];
|
|
3
|
-
export type Category = 'performance' | 'accessibility' | 'best-practices' | 'seo';
|
|
3
|
+
export type Category = 'performance' | 'accessibility' | 'best-practices' | 'seo' | 'agentic-browsing';
|
|
4
4
|
/** Per-category minimums, or one number applied to every category. */
|
|
5
5
|
export type LighthouseThresholds = number | Partial<Record<Category, number>>;
|
|
6
6
|
/** A category that scored below the minimum it was given. */
|