Skip to content

Commit

Permalink
Merge pull request #50 from filipsobol/main
Browse files Browse the repository at this point in the history
0.7.1
  • Loading branch information
filipsobol authored Dec 31, 2024
2 parents 13dec39 + 89cee7b commit 22e1f5d
Show file tree
Hide file tree
Showing 47 changed files with 6,641 additions and 318 deletions.
1 change: 1 addition & 0 deletions .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"updateInternalDependencies": "patch",
"ignore": [
"html-report",
"playground-angular-cli",
"playground-astro",
"playground-esbuild",
"playground-nextjs",
Expand Down
5 changes: 5 additions & 0 deletions .changeset/happy-vans-protect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"sonda": patch
---

Add Angular CLI integration
1 change: 1 addition & 0 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export default defineConfig({
{ text: 'Nuxt', link: '/frameworks/nuxt' },
{ text: 'Astro', link: '/frameworks/astro' },
{ text: 'SvelteKit', link: '/frameworks/sveltekit' },
{ text: 'Angular CLI', link: '/frameworks/angular-cli' },
]
}
]
Expand Down
120 changes: 120 additions & 0 deletions docs/frameworks/angular-cli.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
---
outline: deep
---

# Visualizing Angular CLI bundles with Sonda

To analyze and visualize your Angular CLI bundles, you need to install the Sonda npm package and run the `sonda-angular` CLI command.

## Installation

### Install the package

To get started, install the Sonda package using the following command:

```bash
npm install sonda --save-dev
```

### Enable JSON stats and source maps

Next, enable JSON stats and source maps in the `angular.json` file:

```js{7-12}
{
"projects": {
"<YOUR_PROJECT_NAME>": {
"architect": {
"build": {
"options": {
"statsJson": true, // [!code focus]
"sourceMap": { // [!code focus]
"scripts": true, // [!code focus]
"styles": true, // [!code focus]
"vendor": true // [!code focus]
} // [!code focus]
}
}
}
}
}
}
```

You may need to repeat this step for every project in your workspace.

The `sourceMap.styles` and `sourceMap.vendor` options are optional. Enable them if you want to analyze styles and vendor scripts.

Sonda requires source maps to function correctly, but some other plugins may not support or generate them by default. If Sonda does not work as expected, check the documentation for the other plugins you are using to ensure source maps are enabled.

### Run Sonda

After updating the configuration, you should **build your project**. Once the build is complete, you can run Sonda. You can either use the JavaScript API or the CLI command.

#### Option 1: Using the JavaScript API

Create a file named `sonda.mjs` with the following content:

```js
import Sonda from 'sonda/angular';

Sonda();
```

Then run the following command:

```bash
node sonda.mjs
```

#### Option 2: Using the CLI

Open the `package.json` file and add the following script:

```json
{
"scripts": {
"analyze": "sonda-angular"
}
}
```

Then run the following command:

```bash
npm run analyze
```

The command accepts the following options. For more information about these options, refer to the next section.

* `--config=<path>`
* `--projects=<name>` (can be specified multiple times)
* `--format=<format>`
* `--filename=<filename>`
* `--no-open`
* `--detailed`
* `--sources`
* `--gzip`
* `--brotli`

### Configure the plugin

The steps above will allow you to generate your first report. However, if the report does not contain the information you need, refer to the [Configuration](/configuration) page to explore additional options and learn how to enable them.

#### Additional configuration options

In addition to the options listed on the configuration page, the Angular CLI integration accepts two additional options.

##### `config`

* **Type:** string
* **Default:** `'angular.json'`

Specifies the path to the Angular CLI configuration file.

##### `projects`

* **Type:** string[]
* **Default:** All projects in the `angular.json` file

Specifies the projects to analyze. By default, Sonda will analyze all projects in the Angular CLI configuration file, but you can specify individual projects based on your needs.
1 change: 1 addition & 0 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ Sonda is compatible with a variety of bundlers and frameworks. Select your prefe
- [Nuxt](/frameworks/nuxt)
- [Astro](/frameworks/astro)
- [SvelteKit](/frameworks/sveltekit)
- [Angular CLI](/frameworks/angular-cli)
1 change: 1 addition & 0 deletions packages/sonda/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Sonda works with the following tools:
* Nuxt
* Astro
* SvelteKit
* Angular CLI

## Installation

Expand Down
31 changes: 31 additions & 0 deletions packages/sonda/bin/sonda-angular.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/usr/bin/env node

import { parseArgs } from 'util';
import Sonda from 'sonda/angular';

const { values } = parseArgs( {
options: {
config: { type: 'string' },
projects: { type: 'string', multiple: true },
format: { type: 'string' },
filename: { type: 'string' },
'no-open': { type: 'boolean' },
detailed: { type: 'boolean' },
sources: { type: 'boolean' },
gzip: { type: 'boolean' },
brotli: { type: 'boolean' }
},

// Skip `node sonda-angular`
args: process.argv.slice( 2 ),

// Fail when unknown argument is used
strict: true
} );

if ( values[ 'no-open' ] ) {
values.open = false;
delete values[ 'no-open' ];
}

Sonda( values );
20 changes: 16 additions & 4 deletions packages/sonda/package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
{
"name": "sonda",
"version": "0.7.0",
"description": "Universal visualizer and analyzer for JavaScript and CSS bundles. Works with Vite, Rollup, webpack, Rspack, and esbuild",
"description": "Universal visualizer and analyzer for JavaScript and CSS bundles. Works with most popular bundlers and frameworks.",
"keywords": [
"bundle",
"analyzer",
"visualizer",
"source-map",
"analyzer",
"vite",
"rollup",
"rolldown",
Expand All @@ -15,7 +14,11 @@
"esbuild",
"nextjs",
"nuxt",
"astro"
"withastro",
"sveltekit",
"angular",
"performance",
"devtools"
],
"license": "MIT",
"type": "module",
Expand All @@ -31,6 +34,11 @@
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
},
"./angular": {
"types": "./dist/entrypoints/angular.d.ts",
"import": "./dist/entrypoints/angular.mjs",
"require": "./dist/entrypoints/angular.cjs"
},
"./astro": {
"types": "./dist/entrypoints/astro.d.ts",
"import": "./dist/entrypoints/astro.mjs",
Expand Down Expand Up @@ -84,9 +92,13 @@
"./package.json": "./package.json"
},
"files": [
"bin",
"dist",
"CHANGELOG.md"
],
"bin": {
"sonda-angular": "./bin/sonda-angular.js"
},
"scripts": {
"test": "vitest run",
"test:watch": "vitest",
Expand Down
1 change: 1 addition & 0 deletions packages/sonda/rolldown.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ await rm( 'dist', { recursive: true, force: true } );
const sharedOptions = defineConfig( {
input: {
'index': 'src/index.ts',
'entrypoints/angular': 'src/entrypoints/angular.ts',
'entrypoints/astro': 'src/entrypoints/astro.ts',
'entrypoints/esbuild': 'src/entrypoints/esbuild.ts',
'entrypoints/next': 'src/entrypoints/next.ts',
Expand Down
98 changes: 98 additions & 0 deletions packages/sonda/src/entrypoints/angular.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { readFileSync, readdirSync } from 'fs';
import { basename, relative, resolve } from 'path';
import type { UserOptions } from '../types';
import type { Metafile } from 'esbuild';
import { processEsbuildMetaFile } from './esbuild';

interface AngularUserOptions extends UserOptions {
config: string;
projects: string[];
}

interface Paths {
base: string;
browser: string;
server: string;
}

export default function SondaAngular( options: Partial<AngularUserOptions> = {} ): void {
const cwd = process.cwd();
const {
config = 'angular.json',
projects = [],
...opts
} = options;

opts.format ??= 'html';
opts.filename ??= `sonda-report-[project].${ opts.format }`;

// Angular workspaces can have multiple projects, so we need to generate a report for each
if ( !opts.filename.includes( '[project]' ) ) {
throw new Error( 'SondaAngular: The "filename" option must include the "[project]" token.' );
}

const angularConfig = loadJson( config );
const projectsToGenerate = projects.length ? projects : Object.keys( angularConfig.projects );

for ( const project of projectsToGenerate ) {
const { outputPath } = angularConfig.projects[ project ].architect.build.options;
const paths: Paths = typeof outputPath === 'object'
? outputPath
: { base: outputPath };

paths.base = resolve( cwd, paths.base );
paths.browser = resolve( paths.base, paths.browser || 'browser' );
paths.server = resolve( paths.base, paths.server || 'server' );

const metafile = updateMetafile(
loadJson<Metafile>( resolve( paths.base, 'stats.json' ) ),
paths
);

// Because this configuration is shared between multiple projects, we need to clone it
const sondaOptions = Object.assign( {}, opts );

// Replace the "[project]" token with the current project name
sondaOptions.filename = sondaOptions.filename!.replace( '[project]', project );

processEsbuildMetaFile( metafile, sondaOptions );
}
}

function loadJson<T extends any = any>( path: string ): T {
return JSON.parse(
readFileSync( resolve( process.cwd(), path ), 'utf8' )
);
}

/**
* Output paths in metafile only include file name, without the relative path from the current
* working directory. For example, in the metafile the output path is "main-xxx.js", but in the
* file system it's "dist/project/browser/en/main-xxx.js". This function updates the output paths
* to include the relative path from the current working directory.
*/
function updateMetafile(
metafile: Metafile,
paths: Paths
): Metafile {
const cwd = process.cwd();

// Clone the original outputs object
const outputs = Object.assign( {}, metafile.outputs );

// Reset the outputs
metafile.outputs = {};

for ( const path of readdirSync( paths.base, { encoding: 'utf8', recursive: true } ) ) {
const absolutePath = resolve( paths.base, path );
const filename = basename( absolutePath );
const originalOutput = outputs[ filename ];

// If the output file name exists in the original outputs, add the updated relative path
if ( originalOutput ) {
metafile.outputs[ relative( cwd, absolutePath ) ] = originalOutput;
}
}

return metafile;
}
Loading

0 comments on commit 22e1f5d

Please sign in to comment.