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;
`;