Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stable color rendering #501

Merged
merged 6 commits into from Aug 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -14,6 +14,7 @@ _Note: Gaps between patch versions are faulty, broken or test releases._

* **New Feature**
* Support outputting different URL in server mode ([#520](https://github.com/webpack-contrib/webpack-bundle-analyzer/pull/520) by [@southorange1228](https://github.com/southorange1228))
* Use deterministic chunk colors (#[501](https://github.com/webpack-contrib/webpack-bundle-analyzer/pull/501) by [@CreativeTechGuy](https://github.com/CreativeTechGuy))

## 4.5.0

Expand Down
76 changes: 76 additions & 0 deletions client/components/Treemap.jsx
Expand Up @@ -7,6 +7,7 @@ export default class Treemap extends Component {
super(props);
this.treemap = null;
this.zoomOutDisabled = false;
this.findChunkNamePartIndex();
}

componentDidMount() {
Expand All @@ -16,6 +17,7 @@ export default class Treemap extends Component {

componentWillReceiveProps(nextProps) {
if (nextProps.data !== this.props.data) {
this.findChunkNamePartIndex();
this.treemap.set({
dataObject: this.getTreemapDataObject(nextProps.data)
});
Expand Down Expand Up @@ -76,6 +78,19 @@ export default class Treemap extends Component {
vars.titleBarShown = false;
},
groupColorDecorator(options, properties, variables) {
const root = component.getGroupRoot(properties.group);
const chunkName = component.getChunkNamePart(root.label);
const hash = /[^0-9]/u.test(chunkName)
? hashCode(chunkName)
: (parseInt(chunkName) / 1000) * 360;
variables.groupColor = {
model: 'hsla',
h: Math.round(Math.abs(hash) % 360),
s: 60,
l: 50,
a: 0.9
};

const {highlightGroups} = component.props;
const module = properties.group;

Expand Down Expand Up @@ -136,6 +151,14 @@ export default class Treemap extends Component {
});
}

getGroupRoot(group) {
let nextParent;
while (!group.isAsset && (nextParent = this.treemap.get('hierarchy', group).parent)) {
group = nextParent;
}
return group;
}

zoomToGroup(group) {
this.zoomOutDisabled = false;

Expand Down Expand Up @@ -164,9 +187,62 @@ export default class Treemap extends Component {
if (props.onResize) {
props.onResize();
}
};

/**
* Finds patterns across all chunk names to identify the unique "name" part.
*/
findChunkNamePartIndex() {
const splitChunkNames = this.props.data.map((chunk) => chunk.label.split(/[^a-z0-9]/iu));
const longestSplitName = Math.max(...splitChunkNames.map((parts) => parts.length));
const namePart = {
index: 0,
votes: 0
};
for (let i = longestSplitName - 1; i >= 0; i--) {
const identifierVotes = {
name: 0,
hash: 0,
ext: 0
};
let lastChunkPart = '';
for (const splitChunkName of splitChunkNames) {
const part = splitChunkName[i];
if (part === undefined || part === '') {
continue;
}
if (part === lastChunkPart) {
identifierVotes.ext++;
} else if (/[a-z]/u.test(part) && /[0-9]/u.test(part) && part.length === lastChunkPart.length) {
identifierVotes.hash++;
} else if (/^[a-z]+$/iu.test(part) || /^[0-9]+$/u.test(part)) {
identifierVotes.name++;
}
lastChunkPart = part;
}
if (identifierVotes.name >= namePart.votes) {
namePart.index = i;
namePart.votes = identifierVotes.name;
}
}
this.chunkNamePartIndex = namePart.index;
}

getChunkNamePart(chunkLabel) {
return chunkLabel.split(/[^a-z0-9]/iu)[this.chunkNamePartIndex] || chunkLabel;
}
}

function preventDefault(event) {
event.preventDefault();
}

function hashCode(str) {
let hash = 0;
for (let i = 0; i < str.length; i++) {
const code = str.charCodeAt(i);
hash = (hash << 5) - hash + code;
hash = hash & hash;
}
return hash;
}