Next.js website for Rocky Mountain Vending company featuring: - Product catalog with Stripe integration - Service areas and parts pages - Admin dashboard with Clerk authentication - SEO optimized pages with JSON-LD structured data Co-authored-by: Cursor <cursoragent@cursor.com>
134 lines
4.1 KiB
Text
134 lines
4.1 KiB
Text
/**
|
|
* @license
|
|
* Copyright 2022 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import BaseGatherer from '../base-gatherer.js';
|
|
|
|
/**
|
|
* @template T, U
|
|
* @param {Array<T>} values
|
|
* @param {(value: T) => Promise<U>} promiseMapper
|
|
* @param {boolean} runInSeries
|
|
* @return {Promise<Array<U>>}
|
|
*/
|
|
async function runInSeriesOrParallel(values, promiseMapper, runInSeries) {
|
|
if (runInSeries) {
|
|
const results = [];
|
|
for (const value of values) {
|
|
const result = await promiseMapper(value);
|
|
results.push(result);
|
|
}
|
|
return results;
|
|
} else {
|
|
const promises = values.map(promiseMapper);
|
|
return await Promise.all(promises);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns true if the script was created via our own calls
|
|
* to Runtime.evaluate.
|
|
* @param {LH.Crdp.Debugger.ScriptParsedEvent} script
|
|
*/
|
|
function isLighthouseRuntimeEvaluateScript(script) {
|
|
// Scripts created by Runtime.evaluate that run on the main session/frame
|
|
// result in an empty string for the embedderName.
|
|
// Or, it means the script was dynamically created (eval, new Function, onload, ...)
|
|
if (!script.embedderName) return true;
|
|
|
|
// Otherwise, when running our own code inside other frames, the embedderName
|
|
// is set to the frame's url. In that case, we rely on the special sourceURL that
|
|
// we set.
|
|
return script.hasSourceURL && script.url === '_lighthouse-eval.js';
|
|
}
|
|
|
|
/**
|
|
* @fileoverview Gets JavaScript file contents.
|
|
*/
|
|
class Scripts extends BaseGatherer {
|
|
static symbol = Symbol('Scripts');
|
|
|
|
/** @type {LH.Gatherer.GathererMeta} */
|
|
meta = {
|
|
symbol: Scripts.symbol,
|
|
supportedModes: ['timespan', 'navigation'],
|
|
};
|
|
|
|
/** @type {LH.Crdp.Debugger.ScriptParsedEvent[]} */
|
|
_scriptParsedEvents = [];
|
|
|
|
/** @type {Array<string | undefined>} */
|
|
_scriptContents = [];
|
|
|
|
constructor() {
|
|
super();
|
|
this.onScriptParsed = this.onScriptParsed.bind(this);
|
|
}
|
|
|
|
/**
|
|
* @param {LH.Crdp.Debugger.ScriptParsedEvent} params
|
|
*/
|
|
onScriptParsed(params) {
|
|
if (!isLighthouseRuntimeEvaluateScript(params)) {
|
|
this._scriptParsedEvents.push(params);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {LH.Gatherer.Context} context
|
|
*/
|
|
async startInstrumentation(context) {
|
|
const session = context.driver.defaultSession;
|
|
session.on('Debugger.scriptParsed', this.onScriptParsed);
|
|
await session.sendCommand('Debugger.enable');
|
|
}
|
|
|
|
/**
|
|
* @param {LH.Gatherer.Context} context
|
|
*/
|
|
async stopInstrumentation(context) {
|
|
const session = context.driver.defaultSession;
|
|
const formFactor = context.baseArtifacts.HostFormFactor;
|
|
|
|
session.off('Debugger.scriptParsed', this.onScriptParsed);
|
|
|
|
// If run on a mobile device, be sensitive to memory limitations and only
|
|
// request one at a time.
|
|
this._scriptContents = await runInSeriesOrParallel(
|
|
this._scriptParsedEvents,
|
|
({scriptId}) => {
|
|
return session.sendCommand('Debugger.getScriptSource', {scriptId})
|
|
.then((resp) => resp.scriptSource)
|
|
.catch(() => undefined);
|
|
},
|
|
formFactor === 'mobile' /* runInSeries */
|
|
);
|
|
await session.sendCommand('Debugger.disable');
|
|
}
|
|
|
|
async getArtifact() {
|
|
/** @type {LH.Artifacts['Scripts']} */
|
|
const scripts = this._scriptParsedEvents.map((event, i) => {
|
|
// 'embedderName' and 'url' are confusingly named, so we rewrite them here.
|
|
// On the protocol, 'embedderName' always refers to the URL of the script (or HTML if inline).
|
|
// Same for 'url' ... except, magic "sourceURL=" comments will override the value.
|
|
// It's nice to display the user-provided value in Lighthouse, so we add a field 'name'
|
|
// to make it clear this is for presentational purposes.
|
|
// See https://chromium-review.googlesource.com/c/v8/v8/+/2317310
|
|
return {
|
|
name: event.url,
|
|
...event,
|
|
// embedderName is optional on the protocol because backends like Node may not set it.
|
|
// For our purposes, it is always set. But just in case it isn't... fallback to the url.
|
|
url: event.embedderName || event.url,
|
|
content: this._scriptContents[i],
|
|
};
|
|
});
|
|
|
|
return scripts;
|
|
}
|
|
}
|
|
|
|
export default Scripts;
|