import {Fragment} from 'react'; import styled from '@emotion/styled'; import Card from 'sentry/components/card'; import * as Layout from 'sentry/components/layouts/thirds'; import ExternalLink from 'sentry/components/links/externalLink'; import PanelTable from 'sentry/components/panels/panelTable'; import {SegmentedControl} from 'sentry/components/segmentedControl'; import {t} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import {formatBytesBase2} from 'sentry/utils'; import useRouter from 'sentry/utils/useRouter'; import {bundleStats as stats} from 'sentry/views/bundleAnalyzer'; import {CardSection} from 'sentry/views/performance/transactionSummary/transactionVitals/styles'; enum BundleType { ASSETS = 'assets', IN_APP = 'in_app', PACKAGES = 'packages', OTHER = 'other', } const BUNDLE_TYPES = [ { key: BundleType.ASSETS, label: t('Assets'), }, { key: BundleType.IN_APP, label: t('In App'), }, { key: BundleType.PACKAGES, label: t('Packages'), }, // { // key: BundleType.OTHER, // label: t('Other'), // }, ]; const chunks = stats.chunks; const assets = stats.assets.sort((a, b) => b.size - a.size); const modules = stats.modules; const inAppModules = modules .filter( m => m.moduleType.startsWith('javascript') && m.name.startsWith('./app') && !m.name.endsWith('namespace object') ) .sort((a, b) => b.size - a.size); const packageModules = modules .filter(m => m.name.startsWith('../node_modules')) .sort((a, b) => b.size - a.size); export default function BundleDetails() { const router = useRouter(); const bundleType = router.location.query.bundleType ?? BundleType.ASSETS; function handleBundleAnalysisSelection(type: BundleType) { router.replace({ ...router.location, query: { ...router.location.query, bundleType: type, }, }); } function getPanelItems() { switch (bundleType) { case BundleType.ASSETS: { return assets.map(asset => (
{asset.name}
{formatBytesBase2(asset.size)}
)); } case BundleType.IN_APP: { return inAppModules.map(module => ( {module.name}
{formatBytesBase2(module.size)}
)); } case BundleType.PACKAGES: { return packageModules.map(module => { const npmPackageName = getNpmPackageFromNodeModules(module.name); return ( {module.name.replace('../node_modules/', '')}
{formatBytesBase2(module.size)}
); }); } default: { throw new Error('Invalid bundle type'); } } } const entrypoints = chunks.filter(c => c.entry); return ( {entrypoints.map(entrypoint => (
{entrypoint.id}
{formatBytesBase2(entrypoint.size)}
))}
handleBundleAnalysisSelection(key as BundleType)} > {BUNDLE_TYPES.map(({key, label}) => ( {label} ))} {getPanelItems()}
); } function getNpmPackageFromNodeModules(name: string): string { const path = name.replace('../node_modules/', ''); const pathComponents = path.split('/'); for (let i = pathComponents.length - 1; i >= 0; i--) { if (pathComponents[i] === 'node_modules') { const maybePackageName = pathComponents[i + 1]; if (maybePackageName.startsWith('@')) { return maybePackageName + pathComponents[i + 2]; } return maybePackageName; } } if (pathComponents[0].startsWith('@')) { return pathComponents[0] + pathComponents[1]; } return pathComponents[0]; } const StyledCard = styled(Card)` margin-right: ${space(1)}; `; const SegmentedControlWrapper = styled('div')` padding-bottom: ${space(2)}; `; export const StatNumber = styled('div')` font-size: 32px; `; export const CardWrapper = styled('div')` display: flex; `;