@@ -11,2 +11,3 @@ #!/usr/bin/env node | ||
| format: { type: 'string' }, | ||
| filename: { type: 'string' }, | ||
| outputDir: { type: 'string' }, | ||
@@ -13,0 +14,0 @@ 'no-open': { type: 'boolean' }, |
+12
-0
| # Changelog | ||
| ## 0.8.2 | ||
| ### Patch Changes | ||
| - 3ec00ae: (Performance) Prevent duplicate resolution of some imports in Rollup | ||
| - 9f34bbf: Add a search field to the Treemap view for filtering inputs | ||
| - 1c14126: Log report path after generation | ||
| - 5ae02e5: Fix edge case in Treemap where items fail to sort in descending order by size | ||
| - 9d25830: Always show input code when available, even if CSS Custom Highlighting API is unsupported | ||
| - b230643: Allow filtering inputs by "type" when inspecting asset in Treemap view | ||
| - ab2f385: Reintroduce the `filename` configuration option | ||
| ## 0.8.1 | ||
@@ -4,0 +16,0 @@ |
@@ -9,3 +9,3 @@ import { basename, relative, resolve } from "path"; | ||
| integration: "angular", | ||
| filename: "sonda_[project]" | ||
| filename: "sonda_[env]_[index]" | ||
| }); | ||
@@ -22,3 +22,3 @@ const angularConfig = loadJson(config); | ||
| const sondaOptions = options.clone(); | ||
| sondaOptions.filename = sondaOptions.filename.replace("[project]", project); | ||
| sondaOptions.filename = sondaOptions.filename.replace("[env]", project); | ||
| sondaOptions.sourcesPathNormalizer = (path) => resolve(process.cwd(), path); | ||
@@ -25,0 +25,0 @@ await processEsbuildMetafile(metafile, sondaOptions); |
@@ -7,10 +7,10 @@ import { Config, SondaVitePlugin } from "sonda"; | ||
| integration: "astro", | ||
| filename: "sonda_[env]" | ||
| filename: "sonda_[env]_[index]" | ||
| }); | ||
| if (!options.enabled) return { | ||
| name: "sonda-astro", | ||
| name: "sonda/astro", | ||
| hooks: {} | ||
| }; | ||
| return { | ||
| name: "sonda-astro", | ||
| name: "sonda/astro", | ||
| hooks: { "astro:build:setup"({ vite, target }) { | ||
@@ -21,3 +21,6 @@ if (target === "server" && !options.server) return; | ||
| vite.plugins ??= []; | ||
| vite.plugins.push(SondaVitePlugin(sondaOptions)); | ||
| vite.plugins.push({ | ||
| ...SondaVitePlugin(sondaOptions), | ||
| name: "sonda/astro" | ||
| }); | ||
| } } | ||
@@ -24,0 +27,0 @@ }; |
@@ -8,3 +8,3 @@ import { Config, SondaWebpackPlugin } from "sonda"; | ||
| integration: "next", | ||
| filename: "sonda_[env]" | ||
| filename: "sonda_[env]_[index]" | ||
| }); | ||
@@ -11,0 +11,0 @@ if (!options.enabled) return nextConfig; |
@@ -8,3 +8,3 @@ import { Config, SondaVitePlugin } from "sonda"; | ||
| integration: "nuxt", | ||
| filename: "sonda_[env]" | ||
| filename: "sonda_[env]_[index]" | ||
| }); | ||
@@ -18,3 +18,6 @@ if (!options.enabled) return; | ||
| config.plugins ??= []; | ||
| config.plugins.push(SondaVitePlugin(sondaOptions)); | ||
| config.plugins.push({ | ||
| ...SondaVitePlugin(sondaOptions), | ||
| name: "sonda/nuxt" | ||
| }); | ||
| }); | ||
@@ -21,0 +24,0 @@ }; |
@@ -6,6 +6,6 @@ import { Config, SondaRollupPlugin } from "sonda"; | ||
| const options = new Config(userOptions, { integration: "rolldown" }); | ||
| if (!options.enabled) return { name: "sonda-rolldown" }; | ||
| if (!options.enabled) return { name: "sonda/rolldown" }; | ||
| return { | ||
| ...SondaRollupPlugin(options), | ||
| name: "sonda-rolldown" | ||
| name: "sonda/rolldown" | ||
| }; | ||
@@ -12,0 +12,0 @@ } |
@@ -7,8 +7,8 @@ import { Config, SondaVitePlugin } from "sonda"; | ||
| integration: "sveltekit", | ||
| filename: "sonda_[env]" | ||
| filename: "sonda_[env]_[index]" | ||
| }); | ||
| if (!options.enabled) return { name: "sonda-sveltekit" }; | ||
| if (!options.enabled) return { name: "sonda/sveltekit" }; | ||
| return { | ||
| ...SondaVitePlugin(options), | ||
| name: "sonda-sveltekit", | ||
| name: "sonda/sveltekit", | ||
| configResolved(config) { | ||
@@ -15,0 +15,0 @@ const env = config.build.ssr ? "server" : "client"; |
+22
-9
@@ -15,2 +15,3 @@ import { DecodedSourceMap } from "@ampproject/remapping"; | ||
| get format(): Format; | ||
| get filename(): string; | ||
| get outputDir(): string; | ||
@@ -24,3 +25,2 @@ get open(): boolean; | ||
| get integration(): Integration; | ||
| get filename(): string; | ||
| get sourcesPathNormalizer(): SourcesPathNormalizer; | ||
@@ -44,2 +44,21 @@ set filename(filename: string); | ||
| /** | ||
| * Specifies the filename of the generated report. If this value is an absolute path, | ||
| * it will override the `outputDir` option. | ||
| * | ||
| * The default value includes placeholders like `[index]` and `[env]`, which are replaced | ||
| * during report generation. | ||
| * | ||
| * The `[index]` placeholder is replaced with a version number that increments each time | ||
| * a new report is generated. This allows you to keep multiple revisions of the report without | ||
| * overwriting previous ones. If you want to generate only a single report and always overwrite | ||
| * the previous one, you can set this option to a static value, such as `'sonda'`. | ||
| * | ||
| * Additionally, framework integrations that can generate reports for both the client and server | ||
| * (with the `server` option) will include the `[env]` placeholder in the filename. This is replaced with | ||
| * the environment name (e.g., `client`, `server`), allowing you to distinguish between client and server reports. | ||
| * | ||
| * @default `'sonda_[index]'` for bundler integrations and `'sonda_[env]_[index]'` for framework integrations. | ||
| */ | ||
| filename?: string; | ||
| /** | ||
| * Specifies the name of the directory where the report will be saved. | ||
@@ -107,3 +126,3 @@ * | ||
| * | ||
| * This option is only available for meta-framework integrations. | ||
| * This option is only available for framework integrations. | ||
| * | ||
@@ -120,8 +139,2 @@ * @default false | ||
| /** | ||
| * Specifies the name of the file where the report will be saved. | ||
| * | ||
| * @default 'sonda' | ||
| */ | ||
| filename?: string; | ||
| /** | ||
| * Normalizes the paths in source maps to a consistent format. | ||
@@ -420,3 +433,3 @@ * | ||
| addAsset(name: string, entrypoints?: Array<string>): void; | ||
| generate(): Promise<void>; | ||
| generate(): Promise<string>; | ||
| addSourceMap(asset: string, sourcemap: DecodedReportSourceMap): void; | ||
@@ -423,0 +436,0 @@ } |
+42
-27
@@ -0,3 +1,4 @@ | ||
| import { styleText } from "util"; | ||
| import { access, mkdir, readFile, readdir, writeFile } from "fs/promises"; | ||
| import { basename, dirname, extname, format, isAbsolute, join, posix, relative, resolve, win32 } from "path"; | ||
| import { basename, dirname, extname, format, isAbsolute, join, parse, posix, relative, resolve, win32 } from "path"; | ||
| import { isBuiltin } from "module"; | ||
@@ -20,2 +21,3 @@ import open from "open"; | ||
| format: "html", | ||
| filename: "sonda_[index]", | ||
| outputDir: ".sonda", | ||
@@ -28,3 +30,2 @@ open: true, | ||
| server: false, | ||
| filename: "sonda", | ||
| sourcesPathNormalizer: null | ||
@@ -42,2 +43,5 @@ }, defaults, options); | ||
| } | ||
| get filename() { | ||
| return this.#options.filename; | ||
| } | ||
| get outputDir() { | ||
@@ -67,5 +71,2 @@ return this.#options.outputDir; | ||
| } | ||
| get filename() { | ||
| return this.#options.filename; | ||
| } | ||
| get sourcesPathNormalizer() { | ||
@@ -143,3 +144,3 @@ return this.#options.sourcesPathNormalizer; | ||
| function sortByKey(data, key) { | ||
| return data.toSorted((a, b) => a[key].localeCompare(b[key])); | ||
| return data.sort((a, b) => a[key].localeCompare(b[key])); | ||
| } | ||
@@ -171,3 +172,3 @@ /** | ||
| //#region package.json | ||
| var version = "0.8.1"; | ||
| var version = "0.8.2"; | ||
@@ -185,3 +186,3 @@ //#endregion | ||
| async write(data) { | ||
| const path = await this.getReportPath(); | ||
| const path = await this.getOutputPath(); | ||
| const content = await this.parse(data); | ||
@@ -193,17 +194,26 @@ await mkdir(dirname(path), { recursive: true }); | ||
| /** | ||
| * Generates a unique report name based on the existing files in | ||
| * the specified directory. The name is generated by appending | ||
| * a version number to the base name. | ||
| * Returns the output path for the report based on the configuration. It ensures | ||
| * that the path is absolute, has proper extension, and replaces the [index] placeholder | ||
| * with the next available index based on existing files in the output directory. | ||
| */ | ||
| async getReportPath() { | ||
| const { filename, outputDir } = this.config; | ||
| const regex = new RegExp(`^${filename}_(\\d+)\\${this.extension}$`); | ||
| const versions = (await getAllFiles(outputDir)).map((path) => basename(path).match(regex)).filter((match) => match !== null).map((match) => parseInt(match[1], 10)); | ||
| async getOutputPath() { | ||
| const configPath = resolve(process.cwd(), this.config.outputDir, this.config.filename); | ||
| const path = format({ | ||
| ...parse(configPath), | ||
| base: "", | ||
| ext: "." + this.config.format | ||
| }); | ||
| return this.replaceIndex(path); | ||
| } | ||
| /** | ||
| * Returns path with the [index] placeholder replaced by the next available index. | ||
| */ | ||
| async replaceIndex(path) { | ||
| if (!path.includes("[index]")) return path; | ||
| const { dir, base } = parse(path); | ||
| const regex = new RegExp("^" + base.replace("[index]", "(\\d+)") + "$"); | ||
| const versions = (await getAllFiles(dir)).map((path$1) => basename(path$1).match(regex)).filter((match) => match !== null).map((match) => parseInt(match[1], 10)); | ||
| const maxVersion = Math.max(...versions, -1); | ||
| const version$1 = String(maxVersion + 1); | ||
| return format({ | ||
| dir: this.config.outputDir, | ||
| name: `${filename}_${version$1}`, | ||
| ext: this.extension | ||
| }); | ||
| return path.replace("[index]", version$1); | ||
| } | ||
@@ -616,2 +626,3 @@ }; | ||
| if (this.config.open) await open(path); | ||
| return path; | ||
| } | ||
@@ -679,3 +690,4 @@ addSourceMap(asset, sourcemap) { | ||
| } | ||
| await report.generate(); | ||
| const reportPath = await report.generate(); | ||
| console.info(styleText("green", `๐ Sonda report generated: ${reportPath}`)); | ||
| } | ||
@@ -701,6 +713,6 @@ /** | ||
| const options = new Config(userOptions, { integration: "rollup" }); | ||
| if (!options.enabled) return { name: "sonda-rollup" }; | ||
| if (!options.enabled) return { name: "sonda/rollup" }; | ||
| const report = new Report(options); | ||
| return { | ||
| name: "sonda-rollup", | ||
| name: "sonda/rollup", | ||
| async resolveId(source, importer, options$1) { | ||
@@ -718,2 +730,3 @@ if (!importer) return; | ||
| }); | ||
| return resolved; | ||
| }, | ||
@@ -733,3 +746,4 @@ moduleParsed(module) { | ||
| for (const [path, asset] of Object.entries(bundle)) report.addAsset(resolve(outputDir, path), asset.type === "chunk" && asset.facadeModuleId ? [asset.facadeModuleId] : void 0); | ||
| await report.generate(); | ||
| const reportPath = await report.generate(); | ||
| this.info(styleText("green", `๐ Sonda report generated: ${reportPath}`)); | ||
| } | ||
@@ -748,6 +762,6 @@ }; | ||
| const options = new Config(userOptions, { integration: "vite" }); | ||
| if (!options.enabled) return { name: "sonda-vite" }; | ||
| if (!options.enabled) return { name: "sonda/vite" }; | ||
| return { | ||
| ...SondaRollupPlugin(options), | ||
| name: "sonda-vite", | ||
| name: "sonda/vite", | ||
| enforce: "pre", | ||
@@ -828,3 +842,4 @@ apply: "build" | ||
| }; | ||
| await report.generate(); | ||
| const reportPath = await report.generate(); | ||
| compilation.getLogger("SondaWebpackPlugin").info(styleText("green", `๐ Sonda report generated: ${reportPath}`)); | ||
| }); | ||
@@ -831,0 +846,0 @@ } |
+1
-1
| { | ||
| "name": "sonda", | ||
| "version": "0.8.1", | ||
| "version": "0.8.2", | ||
| "description": "Universal visualizer and analyzer for JavaScript and CSS bundles. Works with most popular bundlers and frameworks.", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
Sorry, the diff of this file is too big to display
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 2 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
281764
1.54%1523
2.35%