@node-cli/bundlecheck 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,229 @@
1
+ /**
2
+ * @node-cli/bundlecheck - Library API
3
+ *
4
+ * Programmatic interface for analyzing npm package bundle sizes.
5
+ *
6
+ */ import { checkBundleSize, formatBytes, getExternals, parsePackageSpecifier } from "./bundler.js";
7
+ import { getCachedResult, normalizeCacheKey, setCachedResult } from "./cache.js";
8
+ import { normalizePlatform, TREND_VERSION_COUNT } from "./defaults.js";
9
+ import { analyzeTrend, selectTrendVersions } from "./trend.js";
10
+ import { fetchPackageVersions as fetchVersions } from "./versions.js";
11
+ /**
12
+ * =============================================================================
13
+ * Library Functions
14
+ * =============================================================================
15
+ */ /**
16
+ * Get bundle size statistics for an npm package.
17
+ *
18
+ * @example
19
+ * ```js
20
+ * import { getBundleStats } from "@node-cli/bundlecheck";
21
+ *
22
+ * const stats = await getBundleStats({
23
+ * package: "@mantine/core@7.0.0",
24
+ * exports: ["Button", "Input"],
25
+ * });
26
+ *
27
+ * console.log(stats.gzipSizeFormatted); // "12.3 kB"
28
+ * ```
29
+ *
30
+ */ export async function getBundleStats(options) {
31
+ const { package: packageName, exports: exportsList, external: additionalExternals, noExternal, gzipLevel = 5, registry, platform: platformOption = "auto", force = false } = options;
32
+ // Normalize platform.
33
+ const platform = normalizePlatform(platformOption === "auto" ? undefined : platformOption);
34
+ // Parse package specifier.
35
+ const { name: baseName, version: requestedVersion } = parsePackageSpecifier(packageName);
36
+ // Resolve "latest" to actual version for cache key.
37
+ let resolvedVersion = requestedVersion;
38
+ if (requestedVersion === "latest") {
39
+ const { tags } = await fetchVersions({
40
+ packageName: baseName,
41
+ registry
42
+ });
43
+ resolvedVersion = tags.latest || requestedVersion;
44
+ }
45
+ // Compute externals for cache key.
46
+ const externals = getExternals(baseName, additionalExternals, noExternal);
47
+ // Build cache key.
48
+ const cacheKey = normalizeCacheKey({
49
+ packageName: baseName,
50
+ version: resolvedVersion,
51
+ exports: exportsList,
52
+ platform,
53
+ gzipLevel,
54
+ externals,
55
+ noExternal: noExternal ?? false
56
+ });
57
+ // Check cache (unless force is set).
58
+ if (!force) {
59
+ const cached = getCachedResult(cacheKey);
60
+ if (cached) {
61
+ return formatBundleStats(cached, true);
62
+ }
63
+ }
64
+ // Perform the analysis.
65
+ const result = await checkBundleSize({
66
+ packageName,
67
+ exports: exportsList,
68
+ additionalExternals,
69
+ noExternal,
70
+ gzipLevel,
71
+ registry,
72
+ platform
73
+ });
74
+ // Store in cache.
75
+ setCachedResult(cacheKey, result);
76
+ return formatBundleStats(result, false);
77
+ }
78
+ /**
79
+ * Get bundle size trend across multiple versions of a package.
80
+ *
81
+ * @example
82
+ * ```js
83
+ * import { getBundleTrend } from "@node-cli/bundlecheck";
84
+ *
85
+ * const trend = await getBundleTrend({
86
+ * package: "@mantine/core",
87
+ * versionCount: 5,
88
+ * });
89
+ *
90
+ * console.log(trend.change?.rawDiffFormatted); // "+5.2 kB"
91
+ * ```
92
+ *
93
+ */ export async function getBundleTrend(options) {
94
+ const { package: packageName, versionCount = TREND_VERSION_COUNT, exports: exportsList, external: additionalExternals, noExternal, gzipLevel, registry, platform: platformOption = "auto", force = false } = options;
95
+ // Normalize platform.
96
+ const platform = normalizePlatform(platformOption === "auto" ? undefined : platformOption);
97
+ // Parse package name (ignore version if provided).
98
+ const { name: baseName, subpath } = parsePackageSpecifier(packageName);
99
+ const fullPackagePath = subpath ? `${baseName}/${subpath}` : baseName;
100
+ // Fetch available versions.
101
+ const { versions } = await fetchVersions({
102
+ packageName: baseName,
103
+ registry
104
+ });
105
+ if (versions.length === 0) {
106
+ throw new Error(`No versions found for package: ${baseName}`);
107
+ }
108
+ // Select versions for trend.
109
+ const trendVersions = selectTrendVersions(versions, versionCount);
110
+ // Analyze all versions (silently - no console output).
111
+ const results = await analyzeTrend({
112
+ packageName: fullPackagePath,
113
+ versions: trendVersions,
114
+ exports: exportsList,
115
+ additionalExternals,
116
+ noExternal,
117
+ gzipLevel,
118
+ boring: true,
119
+ registry,
120
+ platform,
121
+ force
122
+ });
123
+ if (results.length === 0) {
124
+ throw new Error(`Failed to analyze any versions for package: ${baseName}`);
125
+ }
126
+ // Format results.
127
+ const formattedVersions = results.map((r)=>({
128
+ version: r.version,
129
+ rawSize: r.rawSize,
130
+ gzipSize: r.gzipSize,
131
+ rawSizeFormatted: formatBytes(r.rawSize),
132
+ gzipSizeFormatted: r.gzipSize !== null ? formatBytes(r.gzipSize) : null
133
+ }));
134
+ // Calculate change between oldest and newest.
135
+ let change = null;
136
+ if (results.length > 1) {
137
+ const newest = results[0];
138
+ const oldest = results[results.length - 1];
139
+ const rawDiff = newest.rawSize - oldest.rawSize;
140
+ // Handle division by zero: if oldest size is 0, percent is null.
141
+ const rawPercent = oldest.rawSize === 0 ? null : rawDiff / oldest.rawSize * 100;
142
+ let gzipDiff = null;
143
+ let gzipPercent = null;
144
+ let gzipDiffFormatted = null;
145
+ if (newest.gzipSize !== null && oldest.gzipSize !== null) {
146
+ gzipDiff = newest.gzipSize - oldest.gzipSize;
147
+ // Handle division by zero: if oldest size is 0, percent is null.
148
+ gzipPercent = oldest.gzipSize === 0 ? null : gzipDiff / oldest.gzipSize * 100;
149
+ gzipDiffFormatted = gzipDiff >= 0 ? `+${formatBytes(gzipDiff)}` : `-${formatBytes(Math.abs(gzipDiff))}`;
150
+ }
151
+ change = {
152
+ fromVersion: oldest.version,
153
+ toVersion: newest.version,
154
+ rawDiff,
155
+ rawPercent: rawPercent !== null ? Number.parseFloat(rawPercent.toFixed(1)) : null,
156
+ rawDiffFormatted: rawDiff >= 0 ? `+${formatBytes(rawDiff)}` : `-${formatBytes(Math.abs(rawDiff))}`,
157
+ gzipDiff,
158
+ gzipPercent: gzipPercent !== null ? Number.parseFloat(gzipPercent.toFixed(1)) : null,
159
+ gzipDiffFormatted
160
+ };
161
+ }
162
+ return {
163
+ packageName: fullPackagePath,
164
+ versions: formattedVersions,
165
+ change
166
+ };
167
+ }
168
+ /**
169
+ * Get available versions for an npm package.
170
+ *
171
+ * @example
172
+ * ```js
173
+ * import { getPackageVersions } from "@node-cli/bundlecheck";
174
+ *
175
+ * const { versions, tags } = await getPackageVersions({
176
+ * package: "@mantine/core",
177
+ * });
178
+ *
179
+ * console.log(tags.latest); // "7.0.0"
180
+ * ```
181
+ *
182
+ */ export async function getPackageVersions(options) {
183
+ const { package: packageName, registry } = options;
184
+ const result = await fetchVersions({
185
+ packageName,
186
+ registry
187
+ });
188
+ return {
189
+ versions: result.versions,
190
+ tags: result.tags
191
+ };
192
+ }
193
+ /**
194
+ * =============================================================================
195
+ * Re-exports for advanced usage
196
+ * =============================================================================
197
+ */ /**
198
+ * - Format bytes to human-readable string (e.g., 1024 → "1 kB").
199
+ * - Parse a package specifier (e.g., "@scope/name@1.0.0" → { name, version,
200
+ * subpath }).
201
+ */ export { formatBytes, parsePackageSpecifier } from "./bundler.js";
202
+ /**
203
+ * - Clear the bundle cache.
204
+ * - Get the number of cached entries.
205
+ */ export { clearCache, getCacheCount } from "./cache.js";
206
+ /**
207
+ * =============================================================================
208
+ * Internal Helpers
209
+ * =============================================================================
210
+ */ /**
211
+ * Format a BundleResult into a BundleStats object.
212
+ */ function formatBundleStats(result, fromCache) {
213
+ return {
214
+ packageName: result.packageName,
215
+ packageVersion: result.packageVersion,
216
+ exports: result.exports,
217
+ rawSize: result.rawSize,
218
+ gzipSize: result.gzipSize,
219
+ gzipLevel: result.gzipLevel,
220
+ externals: result.externals,
221
+ dependencies: result.dependencies,
222
+ platform: result.platform,
223
+ rawSizeFormatted: formatBytes(result.rawSize),
224
+ gzipSizeFormatted: result.gzipSize !== null ? formatBytes(result.gzipSize) : null,
225
+ fromCache
226
+ };
227
+ }
228
+
229
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @node-cli/bundlecheck - Library API\n *\n * Programmatic interface for analyzing npm package bundle sizes.\n *\n */\n\nimport {\n\ttype BundleResult,\n\tcheckBundleSize,\n\tformatBytes,\n\tgetExternals,\n\tparsePackageSpecifier,\n} from \"./bundler.js\";\nimport {\n\tgetCachedResult,\n\tnormalizeCacheKey,\n\tsetCachedResult,\n} from \"./cache.js\";\nimport { normalizePlatform, TREND_VERSION_COUNT } from \"./defaults.js\";\nimport { analyzeTrend, selectTrendVersions } from \"./trend.js\";\nimport { fetchPackageVersions as fetchVersions } from \"./versions.js\";\n\n/**\n * =============================================================================\n * Types\n * =============================================================================\n */\n\n/**\n * Options for getting bundle stats of a single package.\n */\nexport type GetBundleStatsOptions = {\n\t/**\n\t * Package name with optional version (e.g., \"@mantine/core\" or\n\t * \"@mantine/core@7.0.0\").\n\t */\n\tpackage: string;\n\t/**\n\t * Specific exports to measure (e.g., [\"Button\", \"Input\"]).\n\t */\n\texports?: string[];\n\t/**\n\t * Additional packages to mark as external (not bundled).\n\t */\n\texternal?: string[];\n\t/**\n\t * Bundle everything including default externals (react, react-dom).\n\t */\n\tnoExternal?: boolean;\n\t/**\n\t * Gzip compression level (1-9, default 5).\n\t */\n\tgzipLevel?: number;\n\t/**\n\t * Custom npm registry URL.\n\t */\n\tregistry?: string;\n\t/**\n\t * Target platform: \"browser\", \"node\", or \"auto\" (default: \"auto\" - auto-detect\n\t * from package.json).\n\t */\n\tplatform?: \"browser\" | \"node\" | \"auto\";\n\t/**\n\t * Bypass cache and force re-analysis.\n\t */\n\tforce?: boolean;\n};\n\n/**\n * Result from getBundleStats.\n */\nexport type BundleStats = {\n\t/**\n\t * Display name of the package (may include subpath).\n\t */\n\tpackageName: string;\n\t/**\n\t * Resolved package version.\n\t */\n\tpackageVersion: string;\n\t/**\n\t * Exports that were analyzed.\n\t */\n\texports: string[];\n\t/**\n\t * Raw (minified) bundle size in bytes.\n\t */\n\trawSize: number;\n\t/**\n\t * Gzipped bundle size in bytes (null for node platform).\n\t */\n\tgzipSize: number | null;\n\t/**\n\t * Gzip compression level used.\n\t */\n\tgzipLevel: number;\n\t/**\n\t * Packages marked as external (not included in bundle).\n\t */\n\texternals: string[];\n\t/**\n\t * Package dependencies.\n\t */\n\tdependencies: string[];\n\t/**\n\t * Target platform used for bundling.\n\t */\n\tplatform: \"browser\" | \"node\";\n\t/**\n\t * Human-readable raw size (e.g., \"45.2 kB\").\n\t */\n\trawSizeFormatted: string;\n\t/**\n\t * Human-readable gzip size (e.g., \"12.3 kB\") or null.\n\t */\n\tgzipSizeFormatted: string | null;\n\t/**\n\t * Whether the result was retrieved from cache.\n\t */\n\tfromCache: boolean;\n};\n\n/**\n * Options for getting bundle size trend across versions.\n */\nexport type GetBundleTrendOptions = {\n\t/**\n\t * Package name (e.g., \"@mantine/core\") - version is ignored if provided.\n\t */\n\tpackage: string;\n\t/**\n\t * Number of versions to analyze (default 5).\n\t */\n\tversionCount?: number;\n\t/**\n\t * Specific exports to measure (e.g., [\"Button\", \"Input\"]).\n\t */\n\texports?: string[];\n\t/**\n\t * Additional packages to mark as external (not bundled).\n\t */\n\texternal?: string[];\n\t/**\n\t * Bundle everything including default externals (react, react-dom).\n\t */\n\tnoExternal?: boolean;\n\t/**\n\t * Gzip compression level (1-9, default 5).\n\t */\n\tgzipLevel?: number;\n\t/**\n\t * Custom npm registry URL.\n\t */\n\tregistry?: string;\n\t/**\n\t * Target platform: \"browser\", \"node\", or \"auto\" (default: \"auto\" - auto-detect\n\t * from package.json).\n\t */\n\tplatform?: \"browser\" | \"node\" | \"auto\";\n\t/**\n\t * Bypass cache and force re-analysis.\n\t */\n\tforce?: boolean;\n};\n\n/**\n * Single version result in trend analysis.\n */\nexport type TrendVersionResult = {\n\t/**\n\t * Package version.\n\t */\n\tversion: string;\n\t/**\n\t * Raw (minified) bundle size in bytes.\n\t */\n\trawSize: number;\n\t/**\n\t * Gzipped bundle size in bytes (null for node platform).\n\t */\n\tgzipSize: number | null;\n\t/**\n\t * Human-readable raw size (e.g., \"45.2 kB\").\n\t */\n\trawSizeFormatted: string;\n\t/**\n\t * Human-readable gzip size (e.g., \"12.3 kB\") or null.\n\t */\n\tgzipSizeFormatted: string | null;\n};\n\n/**\n * Size change information between oldest and newest versions.\n */\nexport type TrendChange = {\n\t/**\n\t * Oldest version analyzed.\n\t */\n\tfromVersion: string;\n\t/**\n\t * Newest version analyzed.\n\t */\n\ttoVersion: string;\n\t/**\n\t * Raw size difference in bytes (positive = increase, negative = decrease).\n\t */\n\trawDiff: number;\n\t/**\n\t * Raw size percentage change (null if oldest size was 0).\n\t */\n\trawPercent: number | null;\n\t/**\n\t * Human-readable raw size change (e.g., \"+5.2 kB\" or \"-1.3 kB\").\n\t */\n\trawDiffFormatted: string;\n\t/**\n\t * Gzip size difference in bytes (null if not applicable).\n\t */\n\tgzipDiff: number | null;\n\t/**\n\t * Gzip size percentage change (null if not applicable or oldest size was 0).\n\t */\n\tgzipPercent: number | null;\n\t/**\n\t * Human-readable gzip size change (e.g., \"+1.5 kB\" or \"-0.8 kB\") or null.\n\t */\n\tgzipDiffFormatted: string | null;\n};\n\n/**\n * Result from getBundleTrend.\n */\nexport type BundleTrend = {\n\t/**\n\t * Package name.\n\t */\n\tpackageName: string;\n\t/**\n\t * Results for each version analyzed (newest first).\n\t */\n\tversions: TrendVersionResult[];\n\t/**\n\t * Size change between oldest and newest versions (null if only one version).\n\t */\n\tchange: TrendChange | null;\n};\n\n/**\n * Options for fetching package versions.\n */\nexport type GetPackageVersionsOptions = {\n\t/**\n\t * Package name (e.g., \"@mantine/core\").\n\t */\n\tpackage: string;\n\t/**\n\t * Custom npm registry URL.\n\t */\n\tregistry?: string;\n};\n\n/**\n * Result from getPackageVersions.\n */\nexport type PackageVersions = {\n\t/**\n\t * All available versions (sorted newest first).\n\t */\n\tversions: string[];\n\t/**\n\t * Distribution tags (e.g., { latest: \"7.0.0\", next: \"8.0.0-beta.1\" }).\n\t */\n\ttags: Record<string, string>;\n};\n\n/**\n * =============================================================================\n * Library Functions\n * =============================================================================\n */\n\n/**\n * Get bundle size statistics for an npm package.\n *\n * @example\n * ```js\n * import { getBundleStats } from \"@node-cli/bundlecheck\";\n *\n * const stats = await getBundleStats({\n * package: \"@mantine/core@7.0.0\",\n * exports: [\"Button\", \"Input\"],\n * });\n *\n * console.log(stats.gzipSizeFormatted); // \"12.3 kB\"\n * ```\n *\n */\nexport async function getBundleStats(\n\toptions: GetBundleStatsOptions,\n): Promise<BundleStats> {\n\tconst {\n\t\tpackage: packageName,\n\t\texports: exportsList,\n\t\texternal: additionalExternals,\n\t\tnoExternal,\n\t\tgzipLevel = 5,\n\t\tregistry,\n\t\tplatform: platformOption = \"auto\",\n\t\tforce = false,\n\t} = options;\n\n\t// Normalize platform.\n\tconst platform = normalizePlatform(\n\t\tplatformOption === \"auto\" ? undefined : platformOption,\n\t);\n\n\t// Parse package specifier.\n\tconst { name: baseName, version: requestedVersion } =\n\t\tparsePackageSpecifier(packageName);\n\n\t// Resolve \"latest\" to actual version for cache key.\n\tlet resolvedVersion = requestedVersion;\n\tif (requestedVersion === \"latest\") {\n\t\tconst { tags } = await fetchVersions({\n\t\t\tpackageName: baseName,\n\t\t\tregistry,\n\t\t});\n\t\tresolvedVersion = tags.latest || requestedVersion;\n\t}\n\n\t// Compute externals for cache key.\n\tconst externals = getExternals(baseName, additionalExternals, noExternal);\n\n\t// Build cache key.\n\tconst cacheKey = normalizeCacheKey({\n\t\tpackageName: baseName,\n\t\tversion: resolvedVersion,\n\t\texports: exportsList,\n\t\tplatform,\n\t\tgzipLevel,\n\t\texternals,\n\t\tnoExternal: noExternal ?? false,\n\t});\n\n\t// Check cache (unless force is set).\n\tif (!force) {\n\t\tconst cached = getCachedResult(cacheKey);\n\t\tif (cached) {\n\t\t\treturn formatBundleStats(cached, true);\n\t\t}\n\t}\n\n\t// Perform the analysis.\n\tconst result = await checkBundleSize({\n\t\tpackageName,\n\t\texports: exportsList,\n\t\tadditionalExternals,\n\t\tnoExternal,\n\t\tgzipLevel,\n\t\tregistry,\n\t\tplatform,\n\t});\n\n\t// Store in cache.\n\tsetCachedResult(cacheKey, result);\n\n\treturn formatBundleStats(result, false);\n}\n\n/**\n * Get bundle size trend across multiple versions of a package.\n *\n * @example\n * ```js\n * import { getBundleTrend } from \"@node-cli/bundlecheck\";\n *\n * const trend = await getBundleTrend({\n * package: \"@mantine/core\",\n * versionCount: 5,\n * });\n *\n * console.log(trend.change?.rawDiffFormatted); // \"+5.2 kB\"\n * ```\n *\n */\nexport async function getBundleTrend(\n\toptions: GetBundleTrendOptions,\n): Promise<BundleTrend> {\n\tconst {\n\t\tpackage: packageName,\n\t\tversionCount = TREND_VERSION_COUNT,\n\t\texports: exportsList,\n\t\texternal: additionalExternals,\n\t\tnoExternal,\n\t\tgzipLevel,\n\t\tregistry,\n\t\tplatform: platformOption = \"auto\",\n\t\tforce = false,\n\t} = options;\n\n\t// Normalize platform.\n\tconst platform = normalizePlatform(\n\t\tplatformOption === \"auto\" ? undefined : platformOption,\n\t);\n\n\t// Parse package name (ignore version if provided).\n\tconst { name: baseName, subpath } = parsePackageSpecifier(packageName);\n\tconst fullPackagePath = subpath ? `${baseName}/${subpath}` : baseName;\n\n\t// Fetch available versions.\n\tconst { versions } = await fetchVersions({\n\t\tpackageName: baseName,\n\t\tregistry,\n\t});\n\n\tif (versions.length === 0) {\n\t\tthrow new Error(`No versions found for package: ${baseName}`);\n\t}\n\n\t// Select versions for trend.\n\tconst trendVersions = selectTrendVersions(versions, versionCount);\n\n\t// Analyze all versions (silently - no console output).\n\tconst results = await analyzeTrend({\n\t\tpackageName: fullPackagePath,\n\t\tversions: trendVersions,\n\t\texports: exportsList,\n\t\tadditionalExternals,\n\t\tnoExternal,\n\t\tgzipLevel,\n\t\tboring: true, // Suppress logging\n\t\tregistry,\n\t\tplatform,\n\t\tforce,\n\t});\n\n\tif (results.length === 0) {\n\t\tthrow new Error(`Failed to analyze any versions for package: ${baseName}`);\n\t}\n\n\t// Format results.\n\tconst formattedVersions: TrendVersionResult[] = results.map((r) => ({\n\t\tversion: r.version,\n\t\trawSize: r.rawSize,\n\t\tgzipSize: r.gzipSize,\n\t\trawSizeFormatted: formatBytes(r.rawSize),\n\t\tgzipSizeFormatted: r.gzipSize !== null ? formatBytes(r.gzipSize) : null,\n\t}));\n\n\t// Calculate change between oldest and newest.\n\tlet change: TrendChange | null = null;\n\tif (results.length > 1) {\n\t\tconst newest = results[0];\n\t\tconst oldest = results[results.length - 1];\n\n\t\tconst rawDiff = newest.rawSize - oldest.rawSize;\n\t\t// Handle division by zero: if oldest size is 0, percent is null.\n\t\tconst rawPercent =\n\t\t\toldest.rawSize === 0 ? null : (rawDiff / oldest.rawSize) * 100;\n\n\t\tlet gzipDiff: number | null = null;\n\t\tlet gzipPercent: number | null = null;\n\t\tlet gzipDiffFormatted: string | null = null;\n\n\t\tif (newest.gzipSize !== null && oldest.gzipSize !== null) {\n\t\t\tgzipDiff = newest.gzipSize - oldest.gzipSize;\n\t\t\t// Handle division by zero: if oldest size is 0, percent is null.\n\t\t\tgzipPercent =\n\t\t\t\toldest.gzipSize === 0 ? null : (gzipDiff / oldest.gzipSize) * 100;\n\t\t\tgzipDiffFormatted =\n\t\t\t\tgzipDiff >= 0\n\t\t\t\t\t? `+${formatBytes(gzipDiff)}`\n\t\t\t\t\t: `-${formatBytes(Math.abs(gzipDiff))}`;\n\t\t}\n\n\t\tchange = {\n\t\t\tfromVersion: oldest.version,\n\t\t\ttoVersion: newest.version,\n\t\t\trawDiff,\n\t\t\trawPercent:\n\t\t\t\trawPercent !== null ? Number.parseFloat(rawPercent.toFixed(1)) : null,\n\t\t\trawDiffFormatted:\n\t\t\t\trawDiff >= 0\n\t\t\t\t\t? `+${formatBytes(rawDiff)}`\n\t\t\t\t\t: `-${formatBytes(Math.abs(rawDiff))}`,\n\t\t\tgzipDiff,\n\t\t\tgzipPercent:\n\t\t\t\tgzipPercent !== null ? Number.parseFloat(gzipPercent.toFixed(1)) : null,\n\t\t\tgzipDiffFormatted,\n\t\t};\n\t}\n\n\treturn {\n\t\tpackageName: fullPackagePath,\n\t\tversions: formattedVersions,\n\t\tchange,\n\t};\n}\n\n/**\n * Get available versions for an npm package.\n *\n * @example\n * ```js\n * import { getPackageVersions } from \"@node-cli/bundlecheck\";\n *\n * const { versions, tags } = await getPackageVersions({\n * package: \"@mantine/core\",\n * });\n *\n * console.log(tags.latest); // \"7.0.0\"\n * ```\n *\n */\nexport async function getPackageVersions(\n\toptions: GetPackageVersionsOptions,\n): Promise<PackageVersions> {\n\tconst { package: packageName, registry } = options;\n\n\tconst result = await fetchVersions({\n\t\tpackageName,\n\t\tregistry,\n\t});\n\n\treturn {\n\t\tversions: result.versions,\n\t\ttags: result.tags,\n\t};\n}\n\n/**\n * =============================================================================\n * Re-exports for advanced usage\n * =============================================================================\n */\n\n/**\n * - Format bytes to human-readable string (e.g., 1024 → \"1 kB\").\n * - Parse a package specifier (e.g., \"@scope/name@1.0.0\" → { name, version,\n * subpath }).\n */\nexport { formatBytes, parsePackageSpecifier } from \"./bundler.js\";\n/**\n * - Clear the bundle cache.\n * - Get the number of cached entries.\n */\nexport { clearCache, getCacheCount } from \"./cache.js\";\n\n/**\n * =============================================================================\n * Internal Helpers\n * =============================================================================\n */\n\n/**\n * Format a BundleResult into a BundleStats object.\n */\nfunction formatBundleStats(\n\tresult: BundleResult,\n\tfromCache: boolean,\n): BundleStats {\n\treturn {\n\t\tpackageName: result.packageName,\n\t\tpackageVersion: result.packageVersion,\n\t\texports: result.exports,\n\t\trawSize: result.rawSize,\n\t\tgzipSize: result.gzipSize,\n\t\tgzipLevel: result.gzipLevel,\n\t\texternals: result.externals,\n\t\tdependencies: result.dependencies,\n\t\tplatform: result.platform,\n\t\trawSizeFormatted: formatBytes(result.rawSize),\n\t\tgzipSizeFormatted:\n\t\t\tresult.gzipSize !== null ? formatBytes(result.gzipSize) : null,\n\t\tfromCache,\n\t};\n}\n"],"names":["checkBundleSize","formatBytes","getExternals","parsePackageSpecifier","getCachedResult","normalizeCacheKey","setCachedResult","normalizePlatform","TREND_VERSION_COUNT","analyzeTrend","selectTrendVersions","fetchPackageVersions","fetchVersions","getBundleStats","options","package","packageName","exports","exportsList","external","additionalExternals","noExternal","gzipLevel","registry","platform","platformOption","force","undefined","name","baseName","version","requestedVersion","resolvedVersion","tags","latest","externals","cacheKey","cached","formatBundleStats","result","getBundleTrend","versionCount","subpath","fullPackagePath","versions","length","Error","trendVersions","results","boring","formattedVersions","map","r","rawSize","gzipSize","rawSizeFormatted","gzipSizeFormatted","change","newest","oldest","rawDiff","rawPercent","gzipDiff","gzipPercent","gzipDiffFormatted","Math","abs","fromVersion","toVersion","Number","parseFloat","toFixed","rawDiffFormatted","getPackageVersions","clearCache","getCacheCount","fromCache","packageVersion","dependencies"],"mappings":"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"}
package/dist/trend.d.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  export type TrendResult = {
2
2
  version: string;
3
3
  rawSize: number;
4
- /** Gzip size in bytes, or null for node platform */
4
+ /**
5
+ * Gzip size in bytes, or null for node platform.
6
+ */
5
7
  gzipSize: number | null;
6
8
  };
7
9
  export type TrendOptions = {
@@ -13,22 +15,26 @@ export type TrendOptions = {
13
15
  gzipLevel?: number;
14
16
  boring?: boolean;
15
17
  registry?: string;
16
- /** Target platform. If undefined, auto-detects from package.json engines */
18
+ /**
19
+ * Target platform. If undefined, auto-detects from package.json engines.
20
+ */
17
21
  platform?: "browser" | "node";
18
- /** Bypass cache and force re-fetch/re-calculation */
22
+ /**
23
+ * Bypass cache and force re-fetch/re-calculation.
24
+ */
19
25
  force?: boolean;
20
26
  };
21
27
  /**
22
- * Select versions for trend analysis
23
- * Returns the most recent stable versions (newest first)
24
- * Filters out prerelease versions (canary, alpha, beta, rc, etc.)
28
+ * Select versions for trend analysis Returns the most recent stable versions
29
+ * (newest first) Filters out prerelease versions (canary, alpha, beta, rc,
30
+ * etc.)
25
31
  */
26
32
  export declare function selectTrendVersions(allVersions: string[], count?: number): string[];
27
33
  /**
28
- * Analyze bundle size trend across multiple versions
34
+ * Analyze bundle size trend across multiple versions.
29
35
  */
30
36
  export declare function analyzeTrend(options: TrendOptions): Promise<TrendResult[]>;
31
37
  /**
32
- * Render a bar graph showing bundle size trend
38
+ * Render a bar graph showing bundle size trend.
33
39
  */
34
40
  export declare function renderTrendGraph(packageName: string, results: TrendResult[], boring?: boolean): string[];
package/dist/trend.js CHANGED
@@ -5,31 +5,32 @@ import { checkBundleSize, formatBytes, getExternals, parsePackageSpecifier } fro
5
5
  import { getCachedResult, normalizeCacheKey, setCachedResult } from "./cache.js";
6
6
  import { TREND_VERSION_COUNT } from "./defaults.js";
7
7
  /**
8
- * Select versions for trend analysis
9
- * Returns the most recent stable versions (newest first)
10
- * Filters out prerelease versions (canary, alpha, beta, rc, etc.)
8
+ * Select versions for trend analysis Returns the most recent stable versions
9
+ * (newest first) Filters out prerelease versions (canary, alpha, beta, rc,
10
+ * etc.)
11
11
  */ export function selectTrendVersions(allVersions, count = TREND_VERSION_COUNT) {
12
12
  // Filter out prerelease versions (canary, alpha, beta, rc, etc.)
13
13
  const stableVersions = allVersions.filter((v)=>!prerelease(v));
14
14
  return stableVersions.slice(0, count);
15
15
  }
16
16
  /**
17
- * Analyze bundle size trend across multiple versions
17
+ * Analyze bundle size trend across multiple versions.
18
18
  */ export async function analyzeTrend(options) {
19
19
  const { packageName, versions, exports, additionalExternals, noExternal, gzipLevel, boring, registry, platform, force } = options;
20
20
  const log = new Logger({
21
21
  boring
22
22
  });
23
23
  const results = [];
24
- // Parse base package name (without version)
24
+ // Parse base package name (without version).
25
25
  const { name: baseName } = parsePackageSpecifier(packageName);
26
- // Compute externals for cache key (same logic as bundler)
26
+ // Compute externals for cache key (same logic as bundler).
27
27
  const externals = getExternals(baseName, additionalExternals, noExternal);
28
28
  for (const version of versions){
29
29
  const versionedPackage = `${packageName}@${version}`;
30
- // Build cache key for this version
31
- // Note: platform can be undefined (auto-detect), which is stored as "auto" in cache
32
- const cacheKey = normalizeCacheKey({
30
+ /**
31
+ * Build cache key for this version.
32
+ * NOTE: platform can be undefined (auto-detect), which is stored as "auto" in cache.
33
+ */ const cacheKey = normalizeCacheKey({
33
34
  packageName: baseName,
34
35
  version,
35
36
  exports,
@@ -38,7 +39,7 @@ import { TREND_VERSION_COUNT } from "./defaults.js";
38
39
  externals,
39
40
  noExternal: noExternal ?? false
40
41
  });
41
- // Check cache first (unless --force flag is set)
42
+ // Check cache first (unless --force flag is set).
42
43
  if (!force) {
43
44
  const cached = getCachedResult(cacheKey);
44
45
  if (cached) {
@@ -62,7 +63,7 @@ import { TREND_VERSION_COUNT } from "./defaults.js";
62
63
  registry,
63
64
  platform
64
65
  });
65
- // Store result in cache
66
+ // Store result in cache.
66
67
  setCachedResult(cacheKey, result);
67
68
  results.push({
68
69
  version,
@@ -70,14 +71,14 @@ import { TREND_VERSION_COUNT } from "./defaults.js";
70
71
  gzipSize: result.gzipSize
71
72
  });
72
73
  } catch {
73
- // Skip versions that fail to analyze
74
+ // Skip versions that fail to analyze.
74
75
  log.info(` Skipping ${version} (failed to analyze)`);
75
76
  }
76
77
  }
77
78
  return results;
78
79
  }
79
80
  /**
80
- * Render a bar graph showing bundle size trend
81
+ * Render a bar graph showing bundle size trend.
81
82
  */ export function renderTrendGraph(packageName, results, boring = false) {
82
83
  if (results.length === 0) {
83
84
  return [
@@ -85,18 +86,19 @@ import { TREND_VERSION_COUNT } from "./defaults.js";
85
86
  ];
86
87
  }
87
88
  const lines = [];
88
- // Color helper (respects boring flag)
89
+ // Color helper (respects boring flag).
89
90
  const blue = (text)=>boring ? text : kleur.blue(text);
90
- // Check if gzip data is available (null for node platform)
91
+ // Check if gzip data is available (null for node platform).
91
92
  const hasGzipData = results.some((r)=>r.gzipSize !== null);
92
- // Create maps from formatted string to representative value
93
- // This ensures values that display the same get the same bar length
94
- const gzipFormattedToValue = new Map();
93
+ /**
94
+ * Create maps from formatted string to representative value This ensures
95
+ * values that display the same get the same bar length.
96
+ */ const gzipFormattedToValue = new Map();
95
97
  const rawFormattedToValue = new Map();
96
98
  for (const result of results){
97
99
  if (hasGzipData && result.gzipSize !== null) {
98
100
  const gzipFormatted = formatBytes(result.gzipSize);
99
- // Use first occurrence as representative value for each formatted string
101
+ // Use first occurrence as representative value for each formatted string.
100
102
  if (!gzipFormattedToValue.has(gzipFormatted)) {
101
103
  gzipFormattedToValue.set(gzipFormatted, result.gzipSize);
102
104
  }
@@ -106,7 +108,7 @@ import { TREND_VERSION_COUNT } from "./defaults.js";
106
108
  rawFormattedToValue.set(rawFormatted, result.rawSize);
107
109
  }
108
110
  }
109
- // Get unique representative values for min/max calculation
111
+ // Get unique representative values for min/max calculation.
110
112
  const uniqueGzipValues = [
111
113
  ...gzipFormattedToValue.values()
112
114
  ];
@@ -117,26 +119,26 @@ import { TREND_VERSION_COUNT } from "./defaults.js";
117
119
  const maxGzipSize = hasGzipData ? Math.max(...uniqueGzipValues) : 0;
118
120
  const minRawSize = Math.min(...uniqueRawValues);
119
121
  const maxRawSize = Math.max(...uniqueRawValues);
120
- // Find max version length for alignment
122
+ // Find max version length for alignment.
121
123
  const maxVersionLen = Math.max(...results.map((r)=>r.version.length));
122
- // Bar width (characters)
124
+ // Bar width (characters).
123
125
  const barWidth = 30;
124
126
  const minBarWidth = 10; // Minimum bar length for smallest value
125
- // Helper to calculate bar length with min-max scaling
127
+ // Helper to calculate bar length with min-max scaling.
126
128
  const calcBarLength = (value, min, max)=>{
127
129
  if (max === min) {
128
130
  return barWidth; // All values are the same
129
131
  }
130
- // Scale from minBarWidth to barWidth based on position between min and max
132
+ // Scale from minBarWidth to barWidth based on position between min and max.
131
133
  const ratio = (value - min) / (max - min);
132
134
  return Math.round(minBarWidth + ratio * (barWidth - minBarWidth));
133
135
  };
134
- // Header
136
+ // Header.
135
137
  lines.push("");
136
138
  lines.push(`${blue("Bundle Size:")} ${packageName}`);
137
139
  lines.push("─".repeat(60));
138
140
  lines.push("");
139
- // Gzip size bars (only for browser platform)
141
+ // Gzip size bars (only for browser platform).
140
142
  if (hasGzipData) {
141
143
  lines.push(blue("Gzip Size:"));
142
144
  for (const result of results){
@@ -144,8 +146,10 @@ import { TREND_VERSION_COUNT } from "./defaults.js";
144
146
  continue;
145
147
  }
146
148
  const sizeStr = formatBytes(result.gzipSize);
147
- // Use representative value for this formatted string to ensure consistent bar length
148
- const representativeValue = gzipFormattedToValue.get(sizeStr);
149
+ /**
150
+ * Use representative value for this formatted string to ensure consistent
151
+ * bar length.
152
+ */ const representativeValue = gzipFormattedToValue.get(sizeStr);
149
153
  const barLength = calcBarLength(representativeValue, minGzipSize, maxGzipSize);
150
154
  const bar = "▇".repeat(barLength);
151
155
  const padding = " ".repeat(maxVersionLen - result.version.length);
@@ -153,19 +157,21 @@ import { TREND_VERSION_COUNT } from "./defaults.js";
153
157
  }
154
158
  lines.push("");
155
159
  }
156
- // Raw size bars
160
+ // Raw size bars.
157
161
  lines.push(blue("Raw Size:"));
158
162
  for (const result of results){
159
163
  const sizeStr = formatBytes(result.rawSize);
160
- // Use representative value for this formatted string to ensure consistent bar length
161
- const representativeValue = rawFormattedToValue.get(sizeStr);
164
+ /**
165
+ * Use representative value for this formatted string to ensure consistent bar
166
+ * length.
167
+ */ const representativeValue = rawFormattedToValue.get(sizeStr);
162
168
  const barLength = calcBarLength(representativeValue, minRawSize, maxRawSize);
163
169
  const bar = "▇".repeat(barLength);
164
170
  const padding = " ".repeat(maxVersionLen - result.version.length);
165
171
  lines.push(` ${result.version}${padding} ${bar} ${sizeStr}`);
166
172
  }
167
173
  lines.push("");
168
- // Summary
174
+ // Summary.
169
175
  const oldestResult = results[results.length - 1];
170
176
  const newestResult = results[0];
171
177
  if (results.length > 1) {
@@ -174,7 +180,7 @@ import { TREND_VERSION_COUNT } from "./defaults.js";
174
180
  const rawTrend = rawDiff >= 0 ? `+${formatBytes(rawDiff)}` : `-${formatBytes(Math.abs(rawDiff))}`;
175
181
  lines.push("─".repeat(60));
176
182
  lines.push(`Change from ${oldestResult.version} to ${newestResult.version}:`);
177
- // Gzip summary (only for browser platform)
183
+ // Gzip summary (only for browser platform).
178
184
  if (hasGzipData && newestResult.gzipSize !== null && oldestResult.gzipSize !== null) {
179
185
  const gzipDiff = newestResult.gzipSize - oldestResult.gzipSize;
180
186
  const gzipPercent = (gzipDiff / oldestResult.gzipSize * 100).toFixed(1);
package/dist/trend.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/trend.ts"],"sourcesContent":["import { Logger } from \"@node-cli/logger\";\nimport kleur from \"kleur\";\nimport { prerelease } from \"semver\";\nimport {\n\tcheckBundleSize,\n\tformatBytes,\n\tgetExternals,\n\tparsePackageSpecifier,\n} from \"./bundler.js\";\nimport {\n\tgetCachedResult,\n\tnormalizeCacheKey,\n\tsetCachedResult,\n} from \"./cache.js\";\nimport { TREND_VERSION_COUNT } from \"./defaults.js\";\n\nexport type TrendResult = {\n\tversion: string;\n\trawSize: number;\n\t/** Gzip size in bytes, or null for node platform */\n\tgzipSize: number | null;\n};\n\nexport type TrendOptions = {\n\tpackageName: string;\n\tversions: string[];\n\texports?: string[];\n\tadditionalExternals?: string[];\n\tnoExternal?: boolean;\n\tgzipLevel?: number;\n\tboring?: boolean;\n\tregistry?: string;\n\t/** Target platform. If undefined, auto-detects from package.json engines */\n\tplatform?: \"browser\" | \"node\";\n\t/** Bypass cache and force re-fetch/re-calculation */\n\tforce?: boolean;\n};\n\n/**\n * Select versions for trend analysis\n * Returns the most recent stable versions (newest first)\n * Filters out prerelease versions (canary, alpha, beta, rc, etc.)\n */\nexport function selectTrendVersions(\n\tallVersions: string[],\n\tcount: number = TREND_VERSION_COUNT,\n): string[] {\n\t// Filter out prerelease versions (canary, alpha, beta, rc, etc.)\n\tconst stableVersions = allVersions.filter((v) => !prerelease(v));\n\treturn stableVersions.slice(0, count);\n}\n\n/**\n * Analyze bundle size trend across multiple versions\n */\nexport async function analyzeTrend(\n\toptions: TrendOptions,\n): Promise<TrendResult[]> {\n\tconst {\n\t\tpackageName,\n\t\tversions,\n\t\texports,\n\t\tadditionalExternals,\n\t\tnoExternal,\n\t\tgzipLevel,\n\t\tboring,\n\t\tregistry,\n\t\tplatform,\n\t\tforce,\n\t} = options;\n\n\tconst log = new Logger({ boring });\n\tconst results: TrendResult[] = [];\n\n\t// Parse base package name (without version)\n\tconst { name: baseName } = parsePackageSpecifier(packageName);\n\n\t// Compute externals for cache key (same logic as bundler)\n\tconst externals = getExternals(baseName, additionalExternals, noExternal);\n\n\tfor (const version of versions) {\n\t\tconst versionedPackage = `${packageName}@${version}`;\n\n\t\t// Build cache key for this version\n\t\t// Note: platform can be undefined (auto-detect), which is stored as \"auto\" in cache\n\t\tconst cacheKey = normalizeCacheKey({\n\t\t\tpackageName: baseName,\n\t\t\tversion,\n\t\t\texports,\n\t\t\tplatform,\n\t\t\tgzipLevel: gzipLevel ?? 5,\n\t\t\texternals,\n\t\t\tnoExternal: noExternal ?? false,\n\t\t});\n\n\t\t// Check cache first (unless --force flag is set)\n\t\tif (!force) {\n\t\t\tconst cached = getCachedResult(cacheKey);\n\t\t\tif (cached) {\n\t\t\t\tlog.info(` Checking ${version}... (cached)`);\n\t\t\t\tresults.push({\n\t\t\t\t\tversion,\n\t\t\t\t\trawSize: cached.rawSize,\n\t\t\t\t\tgzipSize: cached.gzipSize,\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tlog.info(` Checking ${version}...`);\n\n\t\ttry {\n\t\t\tconst result = await checkBundleSize({\n\t\t\t\tpackageName: versionedPackage,\n\t\t\t\texports,\n\t\t\t\tadditionalExternals,\n\t\t\t\tnoExternal,\n\t\t\t\tgzipLevel,\n\t\t\t\tregistry,\n\t\t\t\tplatform,\n\t\t\t});\n\n\t\t\t// Store result in cache\n\t\t\tsetCachedResult(cacheKey, result);\n\n\t\t\tresults.push({\n\t\t\t\tversion,\n\t\t\t\trawSize: result.rawSize,\n\t\t\t\tgzipSize: result.gzipSize,\n\t\t\t});\n\t\t} catch {\n\t\t\t// Skip versions that fail to analyze\n\t\t\tlog.info(` Skipping ${version} (failed to analyze)`);\n\t\t}\n\t}\n\n\treturn results;\n}\n\n/**\n * Render a bar graph showing bundle size trend\n */\nexport function renderTrendGraph(\n\tpackageName: string,\n\tresults: TrendResult[],\n\tboring: boolean = false,\n): string[] {\n\tif (results.length === 0) {\n\t\treturn [\"No data to display\"];\n\t}\n\n\tconst lines: string[] = [];\n\n\t// Color helper (respects boring flag)\n\tconst blue = (text: string) => (boring ? text : kleur.blue(text));\n\n\t// Check if gzip data is available (null for node platform)\n\tconst hasGzipData = results.some((r) => r.gzipSize !== null);\n\n\t// Create maps from formatted string to representative value\n\t// This ensures values that display the same get the same bar length\n\tconst gzipFormattedToValue = new Map<string, number>();\n\tconst rawFormattedToValue = new Map<string, number>();\n\n\tfor (const result of results) {\n\t\tif (hasGzipData && result.gzipSize !== null) {\n\t\t\tconst gzipFormatted = formatBytes(result.gzipSize);\n\t\t\t// Use first occurrence as representative value for each formatted string\n\t\t\tif (!gzipFormattedToValue.has(gzipFormatted)) {\n\t\t\t\tgzipFormattedToValue.set(gzipFormatted, result.gzipSize);\n\t\t\t}\n\t\t}\n\t\tconst rawFormatted = formatBytes(result.rawSize);\n\t\tif (!rawFormattedToValue.has(rawFormatted)) {\n\t\t\trawFormattedToValue.set(rawFormatted, result.rawSize);\n\t\t}\n\t}\n\n\t// Get unique representative values for min/max calculation\n\tconst uniqueGzipValues = [...gzipFormattedToValue.values()];\n\tconst uniqueRawValues = [...rawFormattedToValue.values()];\n\n\tconst minGzipSize = hasGzipData ? Math.min(...uniqueGzipValues) : 0;\n\tconst maxGzipSize = hasGzipData ? Math.max(...uniqueGzipValues) : 0;\n\tconst minRawSize = Math.min(...uniqueRawValues);\n\tconst maxRawSize = Math.max(...uniqueRawValues);\n\n\t// Find max version length for alignment\n\tconst maxVersionLen = Math.max(...results.map((r) => r.version.length));\n\n\t// Bar width (characters)\n\tconst barWidth = 30;\n\tconst minBarWidth = 10; // Minimum bar length for smallest value\n\n\t// Helper to calculate bar length with min-max scaling\n\tconst calcBarLength = (value: number, min: number, max: number): number => {\n\t\tif (max === min) {\n\t\t\treturn barWidth; // All values are the same\n\t\t}\n\t\t// Scale from minBarWidth to barWidth based on position between min and max\n\t\tconst ratio = (value - min) / (max - min);\n\t\treturn Math.round(minBarWidth + ratio * (barWidth - minBarWidth));\n\t};\n\n\t// Header\n\tlines.push(\"\");\n\tlines.push(`${blue(\"Bundle Size:\")} ${packageName}`);\n\tlines.push(\"─\".repeat(60));\n\tlines.push(\"\");\n\n\t// Gzip size bars (only for browser platform)\n\tif (hasGzipData) {\n\t\tlines.push(blue(\"Gzip Size:\"));\n\t\tfor (const result of results) {\n\t\t\tif (result.gzipSize === null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst sizeStr = formatBytes(result.gzipSize);\n\t\t\t// Use representative value for this formatted string to ensure consistent bar length\n\t\t\tconst representativeValue = gzipFormattedToValue.get(sizeStr) as number;\n\t\t\tconst barLength = calcBarLength(\n\t\t\t\trepresentativeValue,\n\t\t\t\tminGzipSize,\n\t\t\t\tmaxGzipSize,\n\t\t\t);\n\t\t\tconst bar = \"▇\".repeat(barLength);\n\t\t\tconst padding = \" \".repeat(maxVersionLen - result.version.length);\n\t\t\tlines.push(` ${result.version}${padding} ${bar} ${sizeStr}`);\n\t\t}\n\n\t\tlines.push(\"\");\n\t}\n\n\t// Raw size bars\n\tlines.push(blue(\"Raw Size:\"));\n\tfor (const result of results) {\n\t\tconst sizeStr = formatBytes(result.rawSize);\n\t\t// Use representative value for this formatted string to ensure consistent bar length\n\t\tconst representativeValue = rawFormattedToValue.get(sizeStr) as number;\n\t\tconst barLength = calcBarLength(\n\t\t\trepresentativeValue,\n\t\t\tminRawSize,\n\t\t\tmaxRawSize,\n\t\t);\n\t\tconst bar = \"▇\".repeat(barLength);\n\t\tconst padding = \" \".repeat(maxVersionLen - result.version.length);\n\t\tlines.push(` ${result.version}${padding} ${bar} ${sizeStr}`);\n\t}\n\n\tlines.push(\"\");\n\n\t// Summary\n\tconst oldestResult = results[results.length - 1];\n\tconst newestResult = results[0];\n\n\tif (results.length > 1) {\n\t\tconst rawDiff = newestResult.rawSize - oldestResult.rawSize;\n\t\tconst rawPercent = ((rawDiff / oldestResult.rawSize) * 100).toFixed(1);\n\t\tconst rawTrend =\n\t\t\trawDiff >= 0\n\t\t\t\t? `+${formatBytes(rawDiff)}`\n\t\t\t\t: `-${formatBytes(Math.abs(rawDiff))}`;\n\n\t\tlines.push(\"─\".repeat(60));\n\t\tlines.push(\n\t\t\t`Change from ${oldestResult.version} to ${newestResult.version}:`,\n\t\t);\n\n\t\t// Gzip summary (only for browser platform)\n\t\tif (\n\t\t\thasGzipData &&\n\t\t\tnewestResult.gzipSize !== null &&\n\t\t\toldestResult.gzipSize !== null\n\t\t) {\n\t\t\tconst gzipDiff = newestResult.gzipSize - oldestResult.gzipSize;\n\t\t\tconst gzipPercent = ((gzipDiff / oldestResult.gzipSize) * 100).toFixed(1);\n\t\t\tconst gzipTrend =\n\t\t\t\tgzipDiff >= 0\n\t\t\t\t\t? `+${formatBytes(gzipDiff)}`\n\t\t\t\t\t: `-${formatBytes(Math.abs(gzipDiff))}`;\n\t\t\tlines.push(\n\t\t\t\t` ${blue(\"Gzip:\")} ${gzipTrend} (${gzipDiff >= 0 ? \"+\" : \"\"}${gzipPercent}%)`,\n\t\t\t);\n\t\t}\n\n\t\tlines.push(\n\t\t\t` ${blue(\"Raw:\")} ${rawTrend} (${rawDiff >= 0 ? \"+\" : \"\"}${rawPercent}%)`,\n\t\t);\n\t}\n\n\tlines.push(\"\");\n\n\treturn lines;\n}\n"],"names":["Logger","kleur","prerelease","checkBundleSize","formatBytes","getExternals","parsePackageSpecifier","getCachedResult","normalizeCacheKey","setCachedResult","TREND_VERSION_COUNT","selectTrendVersions","allVersions","count","stableVersions","filter","v","slice","analyzeTrend","options","packageName","versions","exports","additionalExternals","noExternal","gzipLevel","boring","registry","platform","force","log","results","name","baseName","externals","version","versionedPackage","cacheKey","cached","info","push","rawSize","gzipSize","result","renderTrendGraph","length","lines","blue","text","hasGzipData","some","r","gzipFormattedToValue","Map","rawFormattedToValue","gzipFormatted","has","set","rawFormatted","uniqueGzipValues","values","uniqueRawValues","minGzipSize","Math","min","maxGzipSize","max","minRawSize","maxRawSize","maxVersionLen","map","barWidth","minBarWidth","calcBarLength","value","ratio","round","repeat","sizeStr","representativeValue","get","barLength","bar","padding","oldestResult","newestResult","rawDiff","rawPercent","toFixed","rawTrend","abs","gzipDiff","gzipPercent","gzipTrend"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/trend.ts"],"sourcesContent":["import { Logger } from \"@node-cli/logger\";\nimport kleur from \"kleur\";\nimport { prerelease } from \"semver\";\nimport {\n\tcheckBundleSize,\n\tformatBytes,\n\tgetExternals,\n\tparsePackageSpecifier,\n} from \"./bundler.js\";\nimport {\n\tgetCachedResult,\n\tnormalizeCacheKey,\n\tsetCachedResult,\n} from \"./cache.js\";\nimport { TREND_VERSION_COUNT } from \"./defaults.js\";\n\nexport type TrendResult = {\n\tversion: string;\n\trawSize: number;\n\t/**\n\t * Gzip size in bytes, or null for node platform.\n\t */\n\tgzipSize: number | null;\n};\n\nexport type TrendOptions = {\n\tpackageName: string;\n\tversions: string[];\n\texports?: string[];\n\tadditionalExternals?: string[];\n\tnoExternal?: boolean;\n\tgzipLevel?: number;\n\tboring?: boolean;\n\tregistry?: string;\n\t/**\n\t * Target platform. If undefined, auto-detects from package.json engines.\n\t */\n\tplatform?: \"browser\" | \"node\";\n\t/**\n\t * Bypass cache and force re-fetch/re-calculation.\n\t */\n\tforce?: boolean;\n};\n\n/**\n * Select versions for trend analysis Returns the most recent stable versions\n * (newest first) Filters out prerelease versions (canary, alpha, beta, rc,\n * etc.)\n */\nexport function selectTrendVersions(\n\tallVersions: string[],\n\tcount: number = TREND_VERSION_COUNT,\n): string[] {\n\t// Filter out prerelease versions (canary, alpha, beta, rc, etc.)\n\tconst stableVersions = allVersions.filter((v) => !prerelease(v));\n\treturn stableVersions.slice(0, count);\n}\n\n/**\n * Analyze bundle size trend across multiple versions.\n */\nexport async function analyzeTrend(\n\toptions: TrendOptions,\n): Promise<TrendResult[]> {\n\tconst {\n\t\tpackageName,\n\t\tversions,\n\t\texports,\n\t\tadditionalExternals,\n\t\tnoExternal,\n\t\tgzipLevel,\n\t\tboring,\n\t\tregistry,\n\t\tplatform,\n\t\tforce,\n\t} = options;\n\n\tconst log = new Logger({ boring });\n\tconst results: TrendResult[] = [];\n\n\t// Parse base package name (without version).\n\tconst { name: baseName } = parsePackageSpecifier(packageName);\n\n\t// Compute externals for cache key (same logic as bundler).\n\tconst externals = getExternals(baseName, additionalExternals, noExternal);\n\n\tfor (const version of versions) {\n\t\tconst versionedPackage = `${packageName}@${version}`;\n\n\t\t/**\n\t\t * Build cache key for this version.\n\t\t * NOTE: platform can be undefined (auto-detect), which is stored as \"auto\" in cache.\n\t\t */\n\t\tconst cacheKey = normalizeCacheKey({\n\t\t\tpackageName: baseName,\n\t\t\tversion,\n\t\t\texports,\n\t\t\tplatform,\n\t\t\tgzipLevel: gzipLevel ?? 5,\n\t\t\texternals,\n\t\t\tnoExternal: noExternal ?? false,\n\t\t});\n\n\t\t// Check cache first (unless --force flag is set).\n\t\tif (!force) {\n\t\t\tconst cached = getCachedResult(cacheKey);\n\t\t\tif (cached) {\n\t\t\t\tlog.info(` Checking ${version}... (cached)`);\n\t\t\t\tresults.push({\n\t\t\t\t\tversion,\n\t\t\t\t\trawSize: cached.rawSize,\n\t\t\t\t\tgzipSize: cached.gzipSize,\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tlog.info(` Checking ${version}...`);\n\n\t\ttry {\n\t\t\tconst result = await checkBundleSize({\n\t\t\t\tpackageName: versionedPackage,\n\t\t\t\texports,\n\t\t\t\tadditionalExternals,\n\t\t\t\tnoExternal,\n\t\t\t\tgzipLevel,\n\t\t\t\tregistry,\n\t\t\t\tplatform,\n\t\t\t});\n\n\t\t\t// Store result in cache.\n\t\t\tsetCachedResult(cacheKey, result);\n\n\t\t\tresults.push({\n\t\t\t\tversion,\n\t\t\t\trawSize: result.rawSize,\n\t\t\t\tgzipSize: result.gzipSize,\n\t\t\t});\n\t\t} catch {\n\t\t\t// Skip versions that fail to analyze.\n\t\t\tlog.info(` Skipping ${version} (failed to analyze)`);\n\t\t}\n\t}\n\n\treturn results;\n}\n\n/**\n * Render a bar graph showing bundle size trend.\n */\nexport function renderTrendGraph(\n\tpackageName: string,\n\tresults: TrendResult[],\n\tboring: boolean = false,\n): string[] {\n\tif (results.length === 0) {\n\t\treturn [\"No data to display\"];\n\t}\n\n\tconst lines: string[] = [];\n\n\t// Color helper (respects boring flag).\n\tconst blue = (text: string) => (boring ? text : kleur.blue(text));\n\n\t// Check if gzip data is available (null for node platform).\n\tconst hasGzipData = results.some((r) => r.gzipSize !== null);\n\n\t/**\n\t * Create maps from formatted string to representative value This ensures\n\t * values that display the same get the same bar length.\n\t */\n\tconst gzipFormattedToValue = new Map<string, number>();\n\tconst rawFormattedToValue = new Map<string, number>();\n\n\tfor (const result of results) {\n\t\tif (hasGzipData && result.gzipSize !== null) {\n\t\t\tconst gzipFormatted = formatBytes(result.gzipSize);\n\t\t\t// Use first occurrence as representative value for each formatted string.\n\t\t\tif (!gzipFormattedToValue.has(gzipFormatted)) {\n\t\t\t\tgzipFormattedToValue.set(gzipFormatted, result.gzipSize);\n\t\t\t}\n\t\t}\n\t\tconst rawFormatted = formatBytes(result.rawSize);\n\t\tif (!rawFormattedToValue.has(rawFormatted)) {\n\t\t\trawFormattedToValue.set(rawFormatted, result.rawSize);\n\t\t}\n\t}\n\n\t// Get unique representative values for min/max calculation.\n\tconst uniqueGzipValues = [...gzipFormattedToValue.values()];\n\tconst uniqueRawValues = [...rawFormattedToValue.values()];\n\n\tconst minGzipSize = hasGzipData ? Math.min(...uniqueGzipValues) : 0;\n\tconst maxGzipSize = hasGzipData ? Math.max(...uniqueGzipValues) : 0;\n\tconst minRawSize = Math.min(...uniqueRawValues);\n\tconst maxRawSize = Math.max(...uniqueRawValues);\n\n\t// Find max version length for alignment.\n\tconst maxVersionLen = Math.max(...results.map((r) => r.version.length));\n\n\t// Bar width (characters).\n\tconst barWidth = 30;\n\tconst minBarWidth = 10; // Minimum bar length for smallest value\n\n\t// Helper to calculate bar length with min-max scaling.\n\tconst calcBarLength = (value: number, min: number, max: number): number => {\n\t\tif (max === min) {\n\t\t\treturn barWidth; // All values are the same\n\t\t}\n\t\t// Scale from minBarWidth to barWidth based on position between min and max.\n\t\tconst ratio = (value - min) / (max - min);\n\t\treturn Math.round(minBarWidth + ratio * (barWidth - minBarWidth));\n\t};\n\n\t// Header.\n\tlines.push(\"\");\n\tlines.push(`${blue(\"Bundle Size:\")} ${packageName}`);\n\tlines.push(\"─\".repeat(60));\n\tlines.push(\"\");\n\n\t// Gzip size bars (only for browser platform).\n\tif (hasGzipData) {\n\t\tlines.push(blue(\"Gzip Size:\"));\n\t\tfor (const result of results) {\n\t\t\tif (result.gzipSize === null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst sizeStr = formatBytes(result.gzipSize);\n\t\t\t/**\n\t\t\t * Use representative value for this formatted string to ensure consistent\n\t\t\t * bar length.\n\t\t\t */\n\t\t\tconst representativeValue = gzipFormattedToValue.get(sizeStr) as number;\n\t\t\tconst barLength = calcBarLength(\n\t\t\t\trepresentativeValue,\n\t\t\t\tminGzipSize,\n\t\t\t\tmaxGzipSize,\n\t\t\t);\n\t\t\tconst bar = \"▇\".repeat(barLength);\n\t\t\tconst padding = \" \".repeat(maxVersionLen - result.version.length);\n\t\t\tlines.push(` ${result.version}${padding} ${bar} ${sizeStr}`);\n\t\t}\n\n\t\tlines.push(\"\");\n\t}\n\n\t// Raw size bars.\n\tlines.push(blue(\"Raw Size:\"));\n\tfor (const result of results) {\n\t\tconst sizeStr = formatBytes(result.rawSize);\n\t\t/**\n\t\t * Use representative value for this formatted string to ensure consistent bar\n\t\t * length.\n\t\t */\n\t\tconst representativeValue = rawFormattedToValue.get(sizeStr) as number;\n\t\tconst barLength = calcBarLength(\n\t\t\trepresentativeValue,\n\t\t\tminRawSize,\n\t\t\tmaxRawSize,\n\t\t);\n\t\tconst bar = \"▇\".repeat(barLength);\n\t\tconst padding = \" \".repeat(maxVersionLen - result.version.length);\n\t\tlines.push(` ${result.version}${padding} ${bar} ${sizeStr}`);\n\t}\n\n\tlines.push(\"\");\n\n\t// Summary.\n\tconst oldestResult = results[results.length - 1];\n\tconst newestResult = results[0];\n\n\tif (results.length > 1) {\n\t\tconst rawDiff = newestResult.rawSize - oldestResult.rawSize;\n\t\tconst rawPercent = ((rawDiff / oldestResult.rawSize) * 100).toFixed(1);\n\t\tconst rawTrend =\n\t\t\trawDiff >= 0\n\t\t\t\t? `+${formatBytes(rawDiff)}`\n\t\t\t\t: `-${formatBytes(Math.abs(rawDiff))}`;\n\n\t\tlines.push(\"─\".repeat(60));\n\t\tlines.push(\n\t\t\t`Change from ${oldestResult.version} to ${newestResult.version}:`,\n\t\t);\n\n\t\t// Gzip summary (only for browser platform).\n\t\tif (\n\t\t\thasGzipData &&\n\t\t\tnewestResult.gzipSize !== null &&\n\t\t\toldestResult.gzipSize !== null\n\t\t) {\n\t\t\tconst gzipDiff = newestResult.gzipSize - oldestResult.gzipSize;\n\t\t\tconst gzipPercent = ((gzipDiff / oldestResult.gzipSize) * 100).toFixed(1);\n\t\t\tconst gzipTrend =\n\t\t\t\tgzipDiff >= 0\n\t\t\t\t\t? `+${formatBytes(gzipDiff)}`\n\t\t\t\t\t: `-${formatBytes(Math.abs(gzipDiff))}`;\n\t\t\tlines.push(\n\t\t\t\t` ${blue(\"Gzip:\")} ${gzipTrend} (${gzipDiff >= 0 ? \"+\" : \"\"}${gzipPercent}%)`,\n\t\t\t);\n\t\t}\n\n\t\tlines.push(\n\t\t\t` ${blue(\"Raw:\")} ${rawTrend} (${rawDiff >= 0 ? \"+\" : \"\"}${rawPercent}%)`,\n\t\t);\n\t}\n\n\tlines.push(\"\");\n\n\treturn lines;\n}\n"],"names":["Logger","kleur","prerelease","checkBundleSize","formatBytes","getExternals","parsePackageSpecifier","getCachedResult","normalizeCacheKey","setCachedResult","TREND_VERSION_COUNT","selectTrendVersions","allVersions","count","stableVersions","filter","v","slice","analyzeTrend","options","packageName","versions","exports","additionalExternals","noExternal","gzipLevel","boring","registry","platform","force","log","results","name","baseName","externals","version","versionedPackage","cacheKey","cached","info","push","rawSize","gzipSize","result","renderTrendGraph","length","lines","blue","text","hasGzipData","some","r","gzipFormattedToValue","Map","rawFormattedToValue","gzipFormatted","has","set","rawFormatted","uniqueGzipValues","values","uniqueRawValues","minGzipSize","Math","min","maxGzipSize","max","minRawSize","maxRawSize","maxVersionLen","map","barWidth","minBarWidth","calcBarLength","value","ratio","round","repeat","sizeStr","representativeValue","get","barLength","bar","padding","oldestResult","newestResult","rawDiff","rawPercent","toFixed","rawTrend","abs","gzipDiff","gzipPercent","gzipTrend"],"mappings":"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"}
@@ -8,6 +8,6 @@ export type FetchVersionsOptions = {
8
8
  };
9
9
  export declare function fetchPackageVersions(packageNameOrOptions: string | FetchVersionsOptions): Promise<NpmPackageInfo>;
10
10
  /**
11
- * Prompt user to select a version from available versions
11
+ * Prompt user to select a version from available versions.
12
12
  */
13
13
  export declare function promptForVersion(packageName: string, versions: string[], tags: Record<string, string>): Promise<string>;