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>
163 lines
No EOL
6.6 KiB
Text
163 lines
No EOL
6.6 KiB
Text
// Copyright 2025 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
import * as Handlers from '../handlers/handlers.js';
|
|
// Ignore modules smaller than an absolute threshold.
|
|
const ABSOLUTE_SIZE_THRESHOLD_BYTES = 1024 * 0.5;
|
|
// Ignore modules smaller than a % size of largest copy of the module.
|
|
const RELATIVE_SIZE_THRESHOLD = 0.1;
|
|
export function normalizeSource(source) {
|
|
// Trim trailing question mark - b/c webpack.
|
|
source = source.replace(/\?$/, '');
|
|
// Normalize paths for dependencies by only keeping everything after the last `node_modules`.
|
|
const lastNodeModulesIndex = source.lastIndexOf('node_modules');
|
|
if (lastNodeModulesIndex !== -1) {
|
|
source = source.substring(lastNodeModulesIndex);
|
|
}
|
|
return source;
|
|
}
|
|
function shouldIgnoreSource(source) {
|
|
// Ignore bundle overhead.
|
|
if (source.includes('webpack/bootstrap')) {
|
|
return true;
|
|
}
|
|
if (source.includes('(webpack)/buildin')) {
|
|
return true;
|
|
}
|
|
// Ignore webpack module shims, i.e. aliases of the form `module.exports = window.jQuery`
|
|
if (source.includes('external ')) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
/**
|
|
* Sorts each array within @see ScriptDuplication by attributedSize, drops information
|
|
* on sources that are too small, and calculates esimatedDuplicateBytes.
|
|
*/
|
|
export function normalizeDuplication(duplication) {
|
|
for (const [key, data] of duplication) {
|
|
// Sort by resource size.
|
|
data.duplicates.sort((a, b) => b.attributedSize - a.attributedSize);
|
|
// Ignore modules smaller than a % size of largest.
|
|
if (data.duplicates.length > 1) {
|
|
const largestResourceSize = data.duplicates[0].attributedSize;
|
|
data.duplicates = data.duplicates.filter(duplicate => {
|
|
const percentSize = duplicate.attributedSize / largestResourceSize;
|
|
return percentSize >= RELATIVE_SIZE_THRESHOLD;
|
|
});
|
|
}
|
|
// Ignore modules smaller than an absolute threshold.
|
|
data.duplicates = data.duplicates.filter(duplicate => duplicate.attributedSize >= ABSOLUTE_SIZE_THRESHOLD_BYTES);
|
|
// Delete any that now don't have multiple entries.
|
|
if (data.duplicates.length <= 1) {
|
|
duplication.delete(key);
|
|
continue;
|
|
}
|
|
data.estimatedDuplicateBytes = data.duplicates.slice(1).reduce((acc, cur) => acc + cur.attributedSize, 0);
|
|
}
|
|
}
|
|
function indexOfOrLength(haystack, needle, startPosition = 0) {
|
|
const index = haystack.indexOf(needle, startPosition);
|
|
return index === -1 ? haystack.length : index;
|
|
}
|
|
export function getNodeModuleName(source) {
|
|
const sourceSplit = source.split('node_modules/');
|
|
source = sourceSplit[sourceSplit.length - 1];
|
|
const indexFirstSlash = indexOfOrLength(source, '/');
|
|
if (source[0] === '@') {
|
|
return source.slice(0, indexOfOrLength(source, '/', indexFirstSlash + 1));
|
|
}
|
|
return source.slice(0, indexFirstSlash);
|
|
}
|
|
function groupByNodeModules(duplication) {
|
|
const groupedDuplication = new Map();
|
|
for (const [source, data] of duplication) {
|
|
if (!source.includes('node_modules')) {
|
|
groupedDuplication.set(source, data);
|
|
continue;
|
|
}
|
|
const nodeModuleKey = 'node_modules/' + getNodeModuleName(source);
|
|
const aggregatedData = groupedDuplication.get(nodeModuleKey) ?? {
|
|
duplicates: [],
|
|
// This is calculated in normalizeDuplication.
|
|
estimatedDuplicateBytes: 0,
|
|
};
|
|
groupedDuplication.set(nodeModuleKey, aggregatedData);
|
|
for (const { script, attributedSize } of data.duplicates) {
|
|
let duplicate = aggregatedData.duplicates.find(d => d.script === script);
|
|
if (!duplicate) {
|
|
duplicate = { script, attributedSize: 0 };
|
|
aggregatedData.duplicates.push(duplicate);
|
|
}
|
|
duplicate.attributedSize += attributedSize;
|
|
}
|
|
}
|
|
return groupedDuplication;
|
|
}
|
|
/**
|
|
* Sort by estimated savings.
|
|
*/
|
|
function sorted(duplication) {
|
|
return new Map([...duplication].sort((a, b) => b[1].estimatedDuplicateBytes - a[1].estimatedDuplicateBytes));
|
|
}
|
|
/**
|
|
* Returns 2 @see ScriptDuplication for the given collection of script contents + source maps:
|
|
*
|
|
* 1. `duplication` keys correspond to authored files
|
|
* 2. `duplication` keys correspond to authored files, except all files within the same
|
|
* node_module package are aggregated under the same entry.
|
|
*/
|
|
export function computeScriptDuplication(scriptsData, compressionRatios) {
|
|
const sourceDatasMap = new Map();
|
|
// Determine size of each `sources` entry.
|
|
for (const script of scriptsData.scripts) {
|
|
if (!script.content || !script.sourceMap) {
|
|
continue;
|
|
}
|
|
const sizes = Handlers.ModelHandlers.Scripts.getScriptGeneratedSizes(script);
|
|
if (!sizes) {
|
|
continue;
|
|
}
|
|
if ('errorMessage' in sizes) {
|
|
console.error(sizes.errorMessage);
|
|
continue;
|
|
}
|
|
const sourceDataArray = [];
|
|
sourceDatasMap.set(script, sourceDataArray);
|
|
const sources = script.sourceMap.sourceURLs();
|
|
for (let i = 0; i < sources.length; i++) {
|
|
if (shouldIgnoreSource(sources[i])) {
|
|
continue;
|
|
}
|
|
const sourceSize = sizes.files[sources[i]];
|
|
sourceDataArray.push({
|
|
source: normalizeSource(sources[i]),
|
|
resourceSize: sourceSize,
|
|
});
|
|
}
|
|
}
|
|
const duplication = new Map();
|
|
for (const [script, sourceDataArray] of sourceDatasMap) {
|
|
for (const sourceData of sourceDataArray) {
|
|
let data = duplication.get(sourceData.source);
|
|
if (!data) {
|
|
data = { estimatedDuplicateBytes: 0, duplicates: [] };
|
|
duplication.set(sourceData.source, data);
|
|
}
|
|
const compressionRatio = script.request ? compressionRatios.get(script.request?.args.data.requestId) ?? 1 : 1;
|
|
const transferSize = Math.round(sourceData.resourceSize * compressionRatio);
|
|
data.duplicates.push({
|
|
script,
|
|
attributedSize: transferSize,
|
|
});
|
|
}
|
|
}
|
|
const duplicationGroupedByNodeModules = groupByNodeModules(duplication);
|
|
normalizeDuplication(duplication);
|
|
normalizeDuplication(duplicationGroupedByNodeModules);
|
|
return {
|
|
duplication: sorted(duplication),
|
|
duplicationGroupedByNodeModules: sorted(duplicationGroupedByNodeModules),
|
|
};
|
|
}
|
|
//# sourceMappingURL=ScriptDuplication.js.map |