Skip to content

Commit

Permalink
feat(ui): ModuleInfo - display reasons
Browse files Browse the repository at this point in the history
  • Loading branch information
vio committed Sep 24, 2023
1 parent dede7c2 commit 33351aa
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 106 deletions.
190 changes: 90 additions & 100 deletions packages/ui/src/components/bundle-modules/bundle-modules.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import {
MODULE_CHUNK,
MODULE_FILTERS,
MODULE_FILE_TYPE,
SECTIONS,
COMPONENT,
getBundleModulesEntry,
} from '@bundle-stats/utils';

import config from '../../config.json';
Expand All @@ -31,87 +30,60 @@ import { ModuleInfo } from '../module-info';
import css from './bundle-modules.module.css';

const getFilters = ({ filters, compareMode, chunks }) => ({
[MODULE_FILTERS.CHANGED]: {
label: 'Changed',
defaultValue: filters.changed,
disabled: !compareMode,
},
[MODULE_FILTERS.DUPLICATED]: {
label: 'Duplicate',
defaultValue: filters[MODULE_FILTERS.DUPLICATED],
},

// When chunks data available, list available chunks as filters
...(!isEmpty(chunks) && {
[MODULE_CHUNK]: {
label: 'Chunk',
...chunks.reduce(
(chunkFilters, { id, name }) => ({
...chunkFilters,
[id]: {
label: name,
defaultValue: get(filters, `${MODULE_CHUNK}.${id}`, true),
},
}),
{},
),
},
}),

[MODULE_SOURCE_TYPE]: {
label: 'Source',
[MODULE_FILTERS.FIRST_PARTY]: {
label: 'First party',
defaultValue: get(filters, `${MODULE_SOURCE_TYPE}.${MODULE_FILTERS.FIRST_PARTY}`, true),
},
[MODULE_FILTERS.THIRD_PARTY]: {
label: 'Third party',
defaultValue: get(filters, `${MODULE_SOURCE_TYPE}.${MODULE_FILTERS.THIRD_PARTY}`, true),
},
},
[MODULE_FILTERS.CHANGED]: {
label: 'Changed',
defaultValue: filters.changed,
disabled: !compareMode,
},
[MODULE_FILTERS.DUPLICATED]: {
label: 'Duplicate',
defaultValue: filters[MODULE_FILTERS.DUPLICATED],
},

// Module source types
[MODULE_FILE_TYPE]: {
label: 'File type',
...MODULE_SOURCE_FILE_TYPES.reduce(
(agg, fileType) => ({
...agg,
[fileType]: {
label: FILE_TYPE_LABELS[fileType],
defaultValue: get(filters, `${MODULE_FILE_TYPE}.${fileType}`, true),
// When chunks data available, list available chunks as filters
...(!isEmpty(chunks) && {
[MODULE_CHUNK]: {
label: 'Chunk',
...chunks.reduce(
(chunkFilters, { id, name }) => ({
...chunkFilters,
[id]: {
label: name,
defaultValue: get(filters, `${MODULE_CHUNK}.${id}`, true),
},
}),
{},
),
},
})
}),

const RowHeader = ({ row, filters, search, customComponentLink: CustomComponentLink }) => (
<CustomComponentLink
section={SECTIONS.MODULES}
params={{ [COMPONENT.BUNDLE_MODULES]: { filters, search, entryId: row.key } }}
className={css.name}
>
{row.duplicated && (
<Tag className={css.nameTagDuplicated} size="small" kind={Tag.KINDS.DANGER} />
)}
<FileName className={css.nameText} name={row.label} />
</CustomComponentLink>
);

RowHeader.propTypes = {
row: PropTypes.shape({
label: PropTypes.string,
duplicated: PropTypes.bool,
}).isRequired,
search: PropTypes.string,
filters: PropTypes.object,
customComponentLink: PropTypes.elementType.isRequired,
};
[MODULE_SOURCE_TYPE]: {
label: 'Source',
[MODULE_FILTERS.FIRST_PARTY]: {
label: 'First party',
defaultValue: get(filters, `${MODULE_SOURCE_TYPE}.${MODULE_FILTERS.FIRST_PARTY}`, true),
},
[MODULE_FILTERS.THIRD_PARTY]: {
label: 'Third party',
defaultValue: get(filters, `${MODULE_SOURCE_TYPE}.${MODULE_FILTERS.THIRD_PARTY}`, true),
},
},

RowHeader.defaultProps = {
chunks: [],
};
// Module source types
[MODULE_FILE_TYPE]: {
label: 'File type',
...MODULE_SOURCE_FILE_TYPES.reduce(
(agg, fileType) => ({
...agg,
[fileType]: {
label: FILE_TYPE_LABELS[fileType],
defaultValue: get(filters, `${MODULE_FILE_TYPE}.${fileType}`, true),
},
}),
{},
),
},
});

export const BundleModules = ({
className,
Expand Down Expand Up @@ -139,45 +111,56 @@ export const BundleModules = ({

const dropdownFilters = useMemo(
() => getFilters({ filters, chunks, compareMode: jobs.length > 1 }),
[jobs, filters, chunks]
[jobs, filters, chunks],
);

const metricsTableTitle = useMemo(
() => (
<MetricsTableTitle
title={I18N.MODULES}
info={`${items.length}/${totalRowCount}`}
popoverInfo={I18N.MODULES_INFO}
popoverHref={config.documentation.modules}
/>
),
[items, totalRowCount],
);

const metricsTableTitle = useMemo(() => (
<MetricsTableTitle
title={I18N.MODULES}
info={`${items.length}/${totalRowCount}`}
popoverInfo={I18N.MODULES_INFO}
popoverHref={config.documentation.modules}
/>
), [items, totalRowCount]);
const getEntryComponentLinkProps = useCallback(
(moduleEntryId) => getBundleModulesEntry(moduleEntryId, search, filters),
[filters, search],
);

const renderRowHeader = useCallback(
(row) => (
<RowHeader
row={row}
filters={filters}
search={search}
customComponentLink={CustomComponentLink}
/>
<CustomComponentLink {...getEntryComponentLinkProps(row.key)} className={css.name}>
{row.duplicated && (
<Tag className={css.nameTagDuplicated} size="small" kind={Tag.KINDS.DANGER} />
)}
<FileName className={css.nameText} name={row.label} />
</CustomComponentLink>
),
[jobs, chunks, CustomComponentLink, filters, search],
[jobs, chunks, CustomComponentLink, getEntryComponentLinkProps],
);

const emptyMessage = useMemo(() => (
<EmptySet
resources="modules"
filtered={totalRowCount !== 0}
handleResetFilters={resetFilters}
handleViewAll={resetAllFilters}
/>
), [totalRowCount, resetFilters, resetAllFilters]);
const emptyMessage = useMemo(
() => (
<EmptySet
resources="modules"
filtered={totalRowCount !== 0}
handleResetFilters={resetFilters}
handleViewAll={resetAllFilters}
/>
),
[totalRowCount, resetFilters, resetAllFilters],
);

const entryItem = useMemo(() => {
if (!entryId) {
return null;
}

return allItems.find(({ key }) => key === entryId)
return allItems.find(({ key }) => key === entryId);
}, [allItems, entryId]);

return (
Expand Down Expand Up @@ -230,6 +213,7 @@ export const BundleModules = ({
chunkIds={chunks?.map(({ id }) => id)}
labels={jobLabels}
customComponentLink={CustomComponentLink}
getEntryComponentLinkProps={getEntryComponentLinkProps}
onClose={hideEntryInfo}
/>
)}
Expand All @@ -240,8 +224,10 @@ export const BundleModules = ({
BundleModules.defaultProps = {
className: '',
items: [],
allItems: [],
jobs: [],
totalRowCount: 0,
entryId: '',
hasActiveFilters: false,
customComponentLink: ComponentLink,
};
Expand All @@ -253,6 +239,9 @@ BundleModules.propTypes = {
/** Rows data */
items: PropTypes.array, // eslint-disable-line react/forbid-prop-types

/** All rows data */
allItems: PropTypes.array, // eslint-disable-line react/forbid-prop-types

/** Jobs data */
jobs: PropTypes.array, // eslint-disable-line react/forbid-prop-types

Expand All @@ -278,6 +267,7 @@ BundleModules.propTypes = {
filters: PropTypes.shape({
changed: PropTypes.bool,
}).isRequired,
entryId: PropTypes.string,

hasActiveFilters: PropTypes.bool,

Expand Down
7 changes: 7 additions & 0 deletions packages/ui/src/components/module-info/module-info.module.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
.chunksItems {
display: inline;
}

.reasons {
display: inline-block;
padding: 0;
margin: 0;
list-style-position: inside;
}
24 changes: 21 additions & 3 deletions packages/ui/src/components/module-info/module-info.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React, { useMemo } from 'react';
import cx from 'classnames';
import isEmpty from 'lodash/isEmpty';
import noop from 'lodash/noop';
import {
BUNDLE_MODULES_DUPLICATE,
FILE_TYPE_LABELS,
Expand All @@ -14,6 +13,7 @@ import {
import { Module, MetaChunk } from '@bundle-stats/utils/types/webpack';

import { Stack } from '../../layout/stack';
import { FileName } from '../../ui/file-name';
import { Tag } from '../../ui/tag';
import { ComponentLink } from '../component-link';
import { EntryInfo, EntryInfoMetaLink } from '../entry-info';
Expand All @@ -32,6 +32,7 @@ interface ModuleInfoProps {
chunkIds?: Array<string>;
labels: Array<string>;
customComponentLink?: React.ElementType;
getEntryComponentLinkProps: (entryId: string) => Record<string, unknown>;
onClose: () => void;
}

Expand All @@ -43,7 +44,7 @@ export const ModuleInfo = (props: ModuleInfoProps & React.ComponentProps<'div'>)
chunks = [],
chunkIds = [],
customComponentLink: CustomComponentLink = ComponentLink,
onClick = noop,
getEntryComponentLinkProps,
onClose,
} = props;

Expand Down Expand Up @@ -110,11 +111,28 @@ export const ModuleInfo = (props: ModuleInfoProps & React.ComponentProps<'div'>)
<EntryInfoMetaLink
as={CustomComponentLink}
{...getBundleModulesBySource(item.thirdParty || false, sourceTypeLabel)}
onClick={onClick}
>
{sourceTypeLabel}
</EntryInfoMetaLink>
</EntryInfo.Meta>

{item?.runs?.[0].reasons && (
<EntryInfo.Meta label="Reasons">
<ul className={css.reasons}>
{item.runs[0].reasons.map((reason) => (
<li key={reason}>
<EntryInfoMetaLink
as={CustomComponentLink}
{...getEntryComponentLinkProps(reason)}
className={css.reasonLink}
>
<FileName name={reason} className={css.reason} />
</EntryInfoMetaLink>
</li>
))}
</ul>
</EntryInfo.Meta>
)}
</Stack>
</EntryInfo>
);
Expand Down
1 change: 1 addition & 0 deletions packages/utils/src/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export default {
COMPONENT_LINK_BUNDLE_ASSETS_COUNT: 'View all assets',
COMPONENT_LINK_BUNDLE_ASSETS_CHUNK_COUNT: 'View all chunks',
COMPONENT_LINK_MODULES: 'View modules',
COMPONENT_LINK_MODULE: 'View module information',
COMPONENT_LINK_MODULES_DUPLICATE: 'View duplicate modules',
COMPONENT_LINK_MODULES_BY_FILE_TYPE: (fileType) => `View all ${fileType} modules`,
COMPONENT_LINK_MODULES_BY_SOURCE: (source) => `View all ${source} modules`,
Expand Down
16 changes: 16 additions & 0 deletions packages/utils/src/utils/component-links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,22 @@ export const getBundleModulesBySearch = (search: string): ComponentLink => ({
},
});

export const getBundleModulesEntry = (
entryId: string,
search = '',
filters: ComponentLinkFilters = {},
): ComponentLink => ({
section: SECTIONS.MODULES,
title: I18N.COMPONENT_LINK_MODULE,
params: {
[COMPONENT.BUNDLE_MODULES]: {
search,
filters,
entryId,
},
},
});

export const getBundleModulesByChunk = (
chunkIds: Array<string>,
chunkId: string,
Expand Down
6 changes: 3 additions & 3 deletions packages/utils/src/webpack/extract/modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,10 @@ export const extractModules = (webpackStats?: WebpackStatsFiltered): MetricsModu

modulesByName.forEach((moduleEntry, normalizedName) => {
const { name, size = 0, chunks } = moduleEntry;
const normalizedName = getModuleName(name);

// skip modules that are orphane(do not belong to any chunk)
if (!chunks || chunks?.length === 0) {
return agg;
return;
}

const instances = chunks.length;
Expand All @@ -98,12 +97,13 @@ export const extractModules = (webpackStats?: WebpackStatsFiltered): MetricsModu
moduleCount += instances;
totalCodeSize += instances * size;

const reasons = moduleEntry.reasons?.map((reason) => getModuleName(reason.module));
if (duplicated) {
duplicateModulesCount += duplicateInstances;
duplicateCodeSize += duplicateInstances * size;
}

const reasons = moduleEntry.reasons?.map((reason) => getModuleName(reason.module));

modules[normalizedName] = {
name,
value: size,
Expand Down

0 comments on commit 33351aa

Please sign in to comment.