Skip to content

Commit

Permalink
Add "issues" modal, reorganize header, unify ASCII tree generation
Browse files Browse the repository at this point in the history
  • Loading branch information
filipsobol committed Oct 20, 2024
1 parent 2a42eea commit ca8322a
Show file tree
Hide file tree
Showing 26 changed files with 350 additions and 235 deletions.
5 changes: 5 additions & 0 deletions .changeset/odd-weeks-invent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"sonda": minor
---

Add modal showing list of duplicated modules
102 changes: 33 additions & 69 deletions packages/html-report/src/AsciiTree.ts
Original file line number Diff line number Diff line change
@@ -1,74 +1,38 @@
export class AsciiTree {
private depth: number = 0;
private tree: Array<string> = [];

/**
* Processes the given asset and its importers and adds them to the tree.
*/
private process( asset: string ): void {
const importers = this.getImporters( asset );

if ( !importers.length ) {
return;
}

// Narrow down the tree only if there is a single importer.
if ( importers.length === 1 ) {
this.addLast( `imported by ${ importers[ 0 ] }` );

return this.process( importers[ 0 ] );
}

const last = importers.pop()!;

importers.forEach( importer => this.add( `imported by ${ importer }` ) );

this.addLast( `imported by ${ last }` );
}

/**
* Returns an array of all the files that import the given asset.
*/
private getImporters( asset: string ): Array<string> {
if ( !asset ) {
return [];
}
export type Item = string | [ id: string, name: string ];
export type Items = Item[];
export type NullishItems = Items | null | undefined;
export type GetChildren = ( ( id: string, items: Items ) => NullishItems ) | null;

return Object.entries( window.SONDA_JSON_REPORT.inputs )
.filter( ( [ , file ] ) => file.imports.includes( asset! ) )
.map( ( [ path ] ) => path );
}

/**
* Adds a new line to the tree with the last element .
*/
private addLast( text: string ): void {
this.tree.push( ' '.repeat( this.depth * 4 ) + '└── ' + text );
this.depth++;
}

/**
* Adds a new line to the tree.
*/
private add( text: string ): void {
this.tree.push( ' '.repeat( this.depth * 4 ) + '├── ' + text );
export class AsciiTree {
public static generate(
items: Items,
getChildren?: GetChildren,
): string {
return AsciiTree.processItems( items, getChildren ).join( '\n' );
}

/**
* Renders the entire dependency tree.
*/
public render( asset: string ): string {
const input = window.SONDA_JSON_REPORT.inputs[ asset ];

if ( input.belongsTo ) {
this.tree.push( `└── part of the ${ input.belongsTo } bundle` );
this.depth++;

asset = input.belongsTo;
}

this.process( asset! );

return this.tree.join( '\n' );
private static processItems(
items: Items,
getChildren: GetChildren = null,
prefix: string = '',
): string[] {
const lines: string[] = [];
const lastIndex = items.length - 1;

items.forEach( ( item, index ) => {
const isLast = index === lastIndex;
const connector = isLast ? '└── ' : '├── ';
const [ id, name ] = typeof item === 'string' ? [ item, item ] : item;
const children = getChildren?.( id, items );

lines.push( prefix + connector + name );

if ( children ) {
const newPrefix = prefix + ( isLast ? ' ' : '│ ' );
lines.push( ...this.processItems( children, getChildren, newPrefix ) );
}
} );

return lines;
}
}
4 changes: 4 additions & 0 deletions packages/html-report/src/app.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

pre {
line-height: 1.125;
}
2 changes: 1 addition & 1 deletion packages/html-report/src/components/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import Header from './Header/Header.svelte';
import Treemap from './Treemap/Treemap.svelte';
import NoData from './NoData.svelte';
import Tooltip from './Tooltip.svelte';
import { activeOutput, dialog } from '../stores.svelte';
import { activeOutput, dialog } from '../stores/index.svelte.js';
import { isFolder } from '../FileSystemTrie';
let width = $state<number>( 0 );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@

<script lang="ts">
import { fade } from 'svelte/transition';
import { dialog } from '../stores.svelte';
import { dialog } from '../../stores/index.svelte.js';
import type { Snippet } from 'svelte';
interface Props {
Expand Down
7 changes: 6 additions & 1 deletion packages/html-report/src/components/Dialogs/Dialogs.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,14 @@
<OutputDialog output={ dialog.output } />
{/if}

{#if dialog.issues}
<IssuesDialog />
{/if}

<script lang="ts">
import FolderDialog from './FolderDialog.svelte';
import FileDialog from './FileDialog.svelte';
import IssuesDialog from './IssuesDialog.svelte';
import OutputDialog from './OutputDialog.svelte';
import { dialog } from '../../stores.svelte';
import { dialog } from '../../stores/index.svelte.js';
</script>
31 changes: 26 additions & 5 deletions packages/html-report/src/components/Dialogs/FileDialog.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@
</Dialog>

<script lang="ts">
import Dialog from '../Dialog.svelte';
import Dialog from './Dialog.svelte';
import { formatSize } from '../../format';
import { AsciiTree } from '../../AsciiTree';
import { AsciiTree, type Items } from '../../AsciiTree';
import type { File } from '../../FileSystemTrie';
interface Props {
Expand All @@ -62,14 +62,35 @@ const format = $derived.by( () => {
return window.SONDA_JSON_REPORT.inputs[ parent ].format.toUpperCase;
} );
function getImporters(
key: string,
items: Items
): Array<[ string, string ]> {
// Do not show more importers if current file already has multiple importers
if ( items.length > 1 ) {
return [];
}
return Object
.entries( window.SONDA_JSON_REPORT.inputs )
.filter( ( [ , file ] ) => file.imports.includes( key ) )
.map( ( [ path ] ) => [ path, `imported by ${ path }` ] );
}
const dependencyTree = $derived.by<string | null>( () => {
if ( !input ) {
return null;
}
const tree = new AsciiTree();
const start: Array<[ string, string ]> = input.belongsTo
// If the file is part of a bundle, show the bundle name first
? [ [ input.belongsTo, `part of the ${ input.belongsTo } bundle` ] ]
// Otherwise, show file importers
: getImporters( file.path, [] );
return tree.render( file.path );
return AsciiTree.generate(
start,
getImporters
);
} );
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
</Dialog>

<script lang="ts">
import Dialog from '../Dialog.svelte';
import Dialog from './Dialog.svelte';
import Treemap from '../Treemap/Treemap.svelte';
import { formatSize } from '../../format';
import type { Folder } from '../../FileSystemTrie';
Expand Down
23 changes: 23 additions & 0 deletions packages/html-report/src/components/Dialogs/IssuesDialog.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<Dialog heading="Issues found in the build" >
{#snippet children()}
{#if duplicates.size > 0}
<p>The following dependencies are duplicated:</p>
<code class="mt-2 p-4 w-max leading-5 bg-slate-200 rounded overflow-auto min-w-full">
<pre>{ tree }</pre>
</code>
{/if}
{/snippet}
</Dialog>

<script lang="ts">
import Dialog from './Dialog.svelte';
import { duplicates } from '../../stores/index.svelte.js';
import { AsciiTree } from '../../AsciiTree';
const tree = $derived(
AsciiTree.generate(
Array.from( duplicates.keys() ),
key => duplicates.get( key )
)
);
</script>
11 changes: 4 additions & 7 deletions packages/html-report/src/components/Dialogs/OutputDialog.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,16 @@

{#if dependencies.length > 0}
<code class="mt-2 p-4 w-max leading-5 bg-slate-200 rounded overflow-auto min-w-full">
<pre>{ asciiDependencies }</pre>
<pre>{ dependencyTree }</pre>
</code>
{/if}
{/snippet}
</Dialog>

<script lang="ts">
import Dialog from '../Dialog.svelte';
import Dialog from './Dialog.svelte';
import { formatSize } from '../../format';
import { AsciiTree } from '../../AsciiTree';
import type { ModuleFormat } from 'sonda';
import type { FileSystemTrie } from '../../FileSystemTrie';
Expand Down Expand Up @@ -101,9 +102,5 @@ const dependencies = $derived.by<Array<string>>( () => {
.sort();
} );
const asciiDependencies = $derived.by<string>( () => {
return dependencies
.map( ( name, index, self ) => self.length === index + 1 ? `└── ${ name }` : `├── ${ name }` )
.join( '\n' );
} );
const dependencyTree = $derived( AsciiTree.generate( dependencies ) );
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
class="px-4 py-2 text-sm font-medium hover:bg-gray-100 text-gray-900 border border-gray-300 first:rounded-s-lg last:rounded-e-lg focus:ring-1 focus:ring-blue-300 focus:z-10"
class:active={ type === compression.type }
onclick={ () => compression.setType( type ) }
title={ `Show the ${ name } file size in diagram` }
>
{ name }
</button>
Expand All @@ -14,7 +15,7 @@
{/if}

<script lang="ts">
import { compression, activeOutput, type CompressionType } from '../../stores.svelte';
import { compression, activeOutput, type CompressionType } from '../../stores/index.svelte.js';
const hasGzip = $derived( activeOutput.output!.root.gzip > 0 );
const hasBrotli = $derived( activeOutput.output!.root.brotli > 0 );
Expand Down
5 changes: 3 additions & 2 deletions packages/html-report/src/components/Header/Details.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<button
aria-label="Details of the entire build output"
class="text-gray-900 bg-white border border-gray-300 focus:outline-none hover:bg-gray-100 focus:ring-1 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 h-10"
title="Show details of the active output"
{ onclick }
>
<svg
Expand All @@ -13,7 +14,7 @@
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="text-gray-900"
class="text-gray-900 pointer-events-none"
>
<path d="M0 0h24v24H0z" stroke="none" shape-rendering="geometricPrecision"/>
<path d="M8 5H6a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h5.697M18 12V7a2 2 0 0 0-2-2h-2" shape-rendering="geometricPrecision"/>
Expand All @@ -22,7 +23,7 @@
</button>

<script lang="ts">
import { activeOutput, dialog } from '../../stores.svelte';
import { activeOutput, dialog } from '../../stores/index.svelte.js';
function onclick() {
dialog.open( 'output', activeOutput.output! )
Expand Down
31 changes: 31 additions & 0 deletions packages/html-report/src/components/Header/Duplicates.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{#if duplicates.size > 1}
<button
aria-label="Details of the entire build output"
class="text-gray-900 bg-white border border-red-300 focus:outline-none hover:bg-red-50 focus:ring-1 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 h-10"
{ onclick }
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="text-red-600"
>
<path stroke="none" d="M0 0h24v24H0z" shape-rendering="geometricPrecision"/>
<path d="M3 12a9 9 0 1 0 18 0 9 9 0 0 0-18 0M12 8v4M12 16h.01" shape-rendering="geometricPrecision"/>
</svg>
</button>
{/if}

<script lang="ts">
import { duplicates } from '../../stores/dependencies.js';
import { dialog } from '../../stores/index.svelte.js';
function onclick() {
dialog.open( 'issues', true );
}
</script>
40 changes: 13 additions & 27 deletions packages/html-report/src/components/Header/Header.svelte
Original file line number Diff line number Diff line change
@@ -1,34 +1,20 @@
<div class="flex flex-row p-4 items-center space-y-0 h-16 justify-end space-x-2 bg-gray-50 shadow">
<Compression />
<Output />
<Details />
<div class="flex flex-row p-4 items-center space-y-0 h-16 justify-between bg-gray-50 shadow">
<div class="flex flex-row space-x-2">
<Output />
<Details />
<Duplicates />
</div>

<a
href="https://github.com/filipsobol/sonda"
target="_blank"
aria-label="GitHub repository"
class="flex items-center text-gray-900 bg-white border border-gray-300 focus:outline-none hover:bg-gray-100 focus:ring-1 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 h-10"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="text-gray-900"
>
<path d="M0 0h24v24H0z" stroke="none" shape-rendering="geometricPrecision"/>
<path d="M9 19c-4.3 1.4-4.3-2.5-6-3m12 5v-3.5c0-1 .1-1.4-.5-2 2.8-.3 5.5-1.4 5.5-6a4.6 4.6 0 0 0-1.3-3.2 4.2 4.2 0 0 0-.1-3.2s-1.1-.3-3.5 1.3a12.3 12.3 0 0 0-6.2 0C6.5 2.8 5.4 3.1 5.4 3.1a4.2 4.2 0 0 0-.1 3.2A4.6 4.6 0 0 0 4 9.5c0 4.6 2.7 5.7 5.5 6-.6.6-.6 1.2-.5 2V21" shape-rendering="geometricPrecision"/>
</svg>
</a>
<div class="flex flex-row space-x-2">
<Compression />
<Repository />
</div>
</div>

<script lang="ts">
import Compression from './Compression.svelte';
import Output from './Output.svelte';
import Details from './Details.svelte';
import Duplicates from './Duplicates.svelte';
import Output from './Output.svelte';
import Repository from './Repository.svelte';
</script>
Loading

0 comments on commit ca8322a

Please sign in to comment.