diff --git a/.github/workflows/static-data.yml b/.github/workflows/static-data.yml
index fbd323af4..b340d9718 100644
--- a/.github/workflows/static-data.yml
+++ b/.github/workflows/static-data.yml
@@ -35,6 +35,10 @@ jobs:
run: pnpm --filter @verdaccio/local-scripts run downloads:npmjs
- name: Get docker downloads
run: pnpm --filter @verdaccio/local-scripts run pull:docker
+ - name: Get npmjs monhtly downloads
+ run: pnpm --filter @verdaccio/local-scripts run downloads:mounthly
+ - name: Get npmjs year downloads
+ run: pnpm --filter @verdaccio/local-scripts run downloads:yearly
- name: update contributors
run: pnpm run contributors
env:
diff --git a/packages/tools/local-scripts/package.json b/packages/tools/local-scripts/package.json
index 44a33e2e8..83ec2b710 100644
--- a/packages/tools/local-scripts/package.json
+++ b/packages/tools/local-scripts/package.json
@@ -12,6 +12,8 @@
"start": "node build/index.js",
"translations": "node build/run.js translations",
"downloads:npmjs": "node build/run.js npmjs-api-download",
+ "downloads:monthly": "node build/run.js fetch-monthly-data",
+ "downloads:yearly": "node build/run.js fetch-yearly-data",
"pull:docker": "node build/run.js docker-pull-api-download"
},
"keywords": [],
diff --git a/packages/tools/local-scripts/src/api/npmjsApiDownloadPoints.ts b/packages/tools/local-scripts/src/api/npmjsApiDownloadPoints.ts
new file mode 100644
index 000000000..c176a5f87
--- /dev/null
+++ b/packages/tools/local-scripts/src/api/npmjsApiDownloadPoints.ts
@@ -0,0 +1,31 @@
+import { Command } from 'clipanion';
+
+import { fetchMonthlyData, fetchYearlyData } from './utils';
+
+export class FetchMonthlyDataCommand extends Command {
+ public static paths = [['fetch-monthly-data']];
+
+ public async execute() {
+ try {
+ await fetchMonthlyData();
+ } catch (err: any) {
+ // eslint-disable-next-line no-console
+ console.error(err);
+ process.exit(1);
+ }
+ }
+}
+
+export class FetchYearlyDataCommand extends Command {
+ public static paths = [['fetch-yearly-data']];
+
+ public async execute() {
+ try {
+ await fetchYearlyData();
+ } catch (err: any) {
+ // eslint-disable-next-line no-console
+ console.error(err);
+ process.exit(1);
+ }
+ }
+}
diff --git a/packages/tools/local-scripts/src/api/utils.ts b/packages/tools/local-scripts/src/api/utils.ts
index fa64d2be0..1b0217b50 100644
--- a/packages/tools/local-scripts/src/api/utils.ts
+++ b/packages/tools/local-scripts/src/api/utils.ts
@@ -6,12 +6,68 @@ import path from 'path';
const debug = require('debug')('verdaccio:local-scripts');
const token = process.env.TOKEN || '';
+const START_YEAR = 2016;
+const END_YEAR = new Date().getFullYear();
+const END_MONTH = new Date().getMonth() + 1;
+const API_URL = 'https://api.npmjs.org/downloads/point';
+const PACKAGE_NAME = 'verdaccio';
-const getISODateOnly = () => {
+export const getISODateOnly = () => {
const now = new Date();
return now.toISOString().split('T')[0];
};
+async function fetchDownloadData(
+ year: number,
+ month: number
+): Promise<{ downloads: number; start: string; end: string; package: string } | null> {
+ const startDate = `${year}-${String(month).padStart(2, '0')}-01`;
+ const endDate = `${year}-${String(month).padStart(2, '0')}-${new Date(year, month, 0).getDate()}`;
+ debug('fetching data for %s to %s', startDate, endDate);
+ const url = `${API_URL}/${startDate}:${endDate}/${PACKAGE_NAME}`;
+ debug('fetching data from %s', url);
+
+ try {
+ const response = await got.get(url).json();
+ return response;
+ } catch (error) {
+ // eslint-disable-next-line no-console
+ console.error(`Failed to fetch data for ${startDate} to ${endDate}:`, error);
+ return null;
+ }
+}
+
+export async function fetchMonthlyData() {
+ const npmjsFile = path.join(__dirname, '../../src/monthly_downloads.json');
+ const results = [];
+ for (let year = START_YEAR; year <= END_YEAR; year++) {
+ for (let month = 1; month <= 12; month++) {
+ if (year === END_YEAR && month > END_MONTH) break;
+ debug('fetching data for %s %s', year, month);
+ const data = await fetchDownloadData(year, month);
+ if (data) results.push(data);
+ }
+ }
+ await fs.writeFile(npmjsFile, JSON.stringify(results));
+ debug('Monthly data saved to monthly_downloads.json');
+}
+
+export async function fetchYearlyData() {
+ const npmjsFile = path.join(__dirname, '../../src/yearly_downloads.json');
+ const results: { [key: string]: number } = {};
+ for (let year = START_YEAR; year <= END_YEAR; year++) {
+ let yearlyTotal = 0;
+ for (let month = 1; month <= 12; month++) {
+ if (year === END_YEAR && month > END_MONTH) break;
+ const data = await fetchDownloadData(year, month);
+ if (data) yearlyTotal += data.downloads;
+ }
+ results[year.toString()] = yearlyTotal;
+ }
+ await fs.writeFile(npmjsFile, JSON.stringify(results));
+ debug('Yearly data saved to yearly_downloads.json');
+}
+
export async function fetchNpmjsApiDownloadsWeekly() {
try {
const npmjsFile = path.join(__dirname, '../../src/npmjs_downloads.json');
@@ -30,7 +86,7 @@ export async function fetchNpmjsApiDownloadsWeekly() {
npmjsDownloads[currentDate] = response.body.downloads;
- await fs.writeFile(npmjsFile, JSON.stringify(npmjsDownloads, null, 4));
+ await fs.writeFile(npmjsFile, JSON.stringify(npmjsDownloads));
debug('npmjs downloads written at %s ends', npmjsFile);
} catch (err: any) {
// eslint-disable-next-line no-console
@@ -64,7 +120,7 @@ export async function dockerPullWeekly() {
ipCount,
};
});
- await fs.writeFile(npmjsFile, JSON.stringify(pullCounts, null, 4));
+ await fs.writeFile(npmjsFile, JSON.stringify(pullCounts));
debug('npmjs downloads written at %s ends', npmjsFile);
} catch (err: any) {
// eslint-disable-next-line no-console
@@ -88,9 +144,9 @@ export async function fetchTranslationsAPI() {
return acc;
}, {});
const location = path.join(__dirname, '../../src/progress_lang.json');
- await fs.writeFile(location, JSON.stringify(final, null, 4));
+ await fs.writeFile(location, JSON.stringify(final));
// eslint-disable-next-line no-console
- console.log('translations written at %s ends', location);
+ debug('translations written at %s ends', location);
} catch (err: any) {
// eslint-disable-next-line no-console
console.error(`error on process crowdin translations run`, err);
diff --git a/packages/tools/local-scripts/src/index.ts b/packages/tools/local-scripts/src/index.ts
index cb40db3a8..233c7e873 100644
--- a/packages/tools/local-scripts/src/index.ts
+++ b/packages/tools/local-scripts/src/index.ts
@@ -2,5 +2,7 @@ const data = require('./progress_lang.json');
const translationsData = data;
const npmjsDownloads = require('./npmjs_downloads.json');
const dockerPulls = require('./docker_pull.json');
+const yearlyDownloads = require('./yearly_downloads.json');
+const monthlyDownloads = require('./monthly_downloads.json');
-export { data, npmjsDownloads, dockerPulls, translationsData };
+export { data, npmjsDownloads, dockerPulls, translationsData, yearlyDownloads, monthlyDownloads };
diff --git a/packages/tools/local-scripts/src/monthly_downloads.json b/packages/tools/local-scripts/src/monthly_downloads.json
new file mode 100644
index 000000000..77ebfe8ab
--- /dev/null
+++ b/packages/tools/local-scripts/src/monthly_downloads.json
@@ -0,0 +1,112 @@
+[
+ { "downloads": 0, "start": "2016-01-01", "end": "2016-01-31", "package": "verdaccio" },
+ { "downloads": 0, "start": "2016-02-01", "end": "2016-02-29", "package": "verdaccio" },
+ { "downloads": 0, "start": "2016-03-01", "end": "2016-03-31", "package": "verdaccio" },
+ { "downloads": 0, "start": "2016-04-01", "end": "2016-04-30", "package": "verdaccio" },
+ { "downloads": 0, "start": "2016-05-01", "end": "2016-05-31", "package": "verdaccio" },
+ { "downloads": 0, "start": "2016-06-01", "end": "2016-06-30", "package": "verdaccio" },
+ { "downloads": 31, "start": "2016-07-01", "end": "2016-07-31", "package": "verdaccio" },
+ { "downloads": 180, "start": "2016-08-01", "end": "2016-08-31", "package": "verdaccio" },
+ { "downloads": 255, "start": "2016-09-01", "end": "2016-09-30", "package": "verdaccio" },
+ { "downloads": 434, "start": "2016-10-01", "end": "2016-10-31", "package": "verdaccio" },
+ { "downloads": 395, "start": "2016-11-01", "end": "2016-11-30", "package": "verdaccio" },
+ { "downloads": 332, "start": "2016-12-01", "end": "2016-12-31", "package": "verdaccio" },
+ { "downloads": 1322, "start": "2017-01-01", "end": "2017-01-31", "package": "verdaccio" },
+ { "downloads": 711, "start": "2017-02-01", "end": "2017-02-28", "package": "verdaccio" },
+ { "downloads": 1940, "start": "2017-03-01", "end": "2017-03-31", "package": "verdaccio" },
+ { "downloads": 1882, "start": "2017-04-01", "end": "2017-04-30", "package": "verdaccio" },
+ { "downloads": 1889, "start": "2017-05-01", "end": "2017-05-31", "package": "verdaccio" },
+ { "downloads": 2613, "start": "2017-06-01", "end": "2017-06-30", "package": "verdaccio" },
+ { "downloads": 5099, "start": "2017-07-01", "end": "2017-07-31", "package": "verdaccio" },
+ { "downloads": 3670, "start": "2017-08-01", "end": "2017-08-31", "package": "verdaccio" },
+ { "downloads": 2923, "start": "2017-09-01", "end": "2017-09-30", "package": "verdaccio" },
+ { "downloads": 5210, "start": "2017-10-01", "end": "2017-10-31", "package": "verdaccio" },
+ { "downloads": 5100, "start": "2017-11-01", "end": "2017-11-30", "package": "verdaccio" },
+ { "downloads": 5131, "start": "2017-12-01", "end": "2017-12-31", "package": "verdaccio" },
+ { "downloads": 13745, "start": "2018-01-01", "end": "2018-01-31", "package": "verdaccio" },
+ { "downloads": 8786, "start": "2018-02-01", "end": "2018-02-28", "package": "verdaccio" },
+ { "downloads": 11528, "start": "2018-03-01", "end": "2018-03-31", "package": "verdaccio" },
+ { "downloads": 7366, "start": "2018-04-01", "end": "2018-04-30", "package": "verdaccio" },
+ { "downloads": 11966, "start": "2018-05-01", "end": "2018-05-31", "package": "verdaccio" },
+ { "downloads": 10532, "start": "2018-06-01", "end": "2018-06-30", "package": "verdaccio" },
+ { "downloads": 11541, "start": "2018-07-01", "end": "2018-07-31", "package": "verdaccio" },
+ { "downloads": 13041, "start": "2018-08-01", "end": "2018-08-31", "package": "verdaccio" },
+ { "downloads": 26650, "start": "2018-09-01", "end": "2018-09-30", "package": "verdaccio" },
+ { "downloads": 34952, "start": "2018-10-01", "end": "2018-10-31", "package": "verdaccio" },
+ { "downloads": 19367, "start": "2018-11-01", "end": "2018-11-30", "package": "verdaccio" },
+ { "downloads": 34753, "start": "2018-12-01", "end": "2018-12-31", "package": "verdaccio" },
+ { "downloads": 45819, "start": "2019-01-01", "end": "2019-01-31", "package": "verdaccio" },
+ { "downloads": 41869, "start": "2019-02-01", "end": "2019-02-28", "package": "verdaccio" },
+ { "downloads": 38958, "start": "2019-03-01", "end": "2019-03-31", "package": "verdaccio" },
+ { "downloads": 123377, "start": "2019-04-01", "end": "2019-04-30", "package": "verdaccio" },
+ { "downloads": 127805, "start": "2019-05-01", "end": "2019-05-31", "package": "verdaccio" },
+ { "downloads": 132708, "start": "2019-06-01", "end": "2019-06-30", "package": "verdaccio" },
+ { "downloads": 185707, "start": "2019-07-01", "end": "2019-07-31", "package": "verdaccio" },
+ { "downloads": 148043, "start": "2019-08-01", "end": "2019-08-31", "package": "verdaccio" },
+ { "downloads": 136353, "start": "2019-09-01", "end": "2019-09-30", "package": "verdaccio" },
+ { "downloads": 147244, "start": "2019-10-01", "end": "2019-10-31", "package": "verdaccio" },
+ { "downloads": 60357, "start": "2019-11-01", "end": "2019-11-30", "package": "verdaccio" },
+ { "downloads": 52255, "start": "2019-12-01", "end": "2019-12-31", "package": "verdaccio" },
+ { "downloads": 62815, "start": "2020-01-01", "end": "2020-01-31", "package": "verdaccio" },
+ { "downloads": 59476, "start": "2020-02-01", "end": "2020-02-29", "package": "verdaccio" },
+ { "downloads": 73003, "start": "2020-03-01", "end": "2020-03-31", "package": "verdaccio" },
+ { "downloads": 77269, "start": "2020-04-01", "end": "2020-04-30", "package": "verdaccio" },
+ { "downloads": 92001, "start": "2020-05-01", "end": "2020-05-31", "package": "verdaccio" },
+ { "downloads": 94261, "start": "2020-06-01", "end": "2020-06-30", "package": "verdaccio" },
+ { "downloads": 102521, "start": "2020-07-01", "end": "2020-07-31", "package": "verdaccio" },
+ { "downloads": 93523, "start": "2020-08-01", "end": "2020-08-31", "package": "verdaccio" },
+ { "downloads": 108664, "start": "2020-09-01", "end": "2020-09-30", "package": "verdaccio" },
+ { "downloads": 123953, "start": "2020-10-01", "end": "2020-10-31", "package": "verdaccio" },
+ { "downloads": 125049, "start": "2020-11-01", "end": "2020-11-30", "package": "verdaccio" },
+ { "downloads": 106645, "start": "2020-12-01", "end": "2020-12-31", "package": "verdaccio" },
+ { "downloads": 112861, "start": "2021-01-01", "end": "2021-01-31", "package": "verdaccio" },
+ { "downloads": 139975, "start": "2021-02-01", "end": "2021-02-28", "package": "verdaccio" },
+ { "downloads": 167038, "start": "2021-03-01", "end": "2021-03-31", "package": "verdaccio" },
+ { "downloads": 177302, "start": "2021-04-01", "end": "2021-04-30", "package": "verdaccio" },
+ { "downloads": 151617, "start": "2021-05-01", "end": "2021-05-31", "package": "verdaccio" },
+ { "downloads": 134956, "start": "2021-06-01", "end": "2021-06-30", "package": "verdaccio" },
+ { "downloads": 125070, "start": "2021-07-01", "end": "2021-07-31", "package": "verdaccio" },
+ { "downloads": 225969, "start": "2021-08-01", "end": "2021-08-31", "package": "verdaccio" },
+ { "downloads": 234809, "start": "2021-09-01", "end": "2021-09-30", "package": "verdaccio" },
+ { "downloads": 293088, "start": "2021-10-01", "end": "2021-10-31", "package": "verdaccio" },
+ { "downloads": 251822, "start": "2021-11-01", "end": "2021-11-30", "package": "verdaccio" },
+ { "downloads": 181139, "start": "2021-12-01", "end": "2021-12-31", "package": "verdaccio" },
+ { "downloads": 248329, "start": "2022-01-01", "end": "2022-01-31", "package": "verdaccio" },
+ { "downloads": 280671, "start": "2022-02-01", "end": "2022-02-28", "package": "verdaccio" },
+ { "downloads": 351946, "start": "2022-03-01", "end": "2022-03-31", "package": "verdaccio" },
+ { "downloads": 312241, "start": "2022-04-01", "end": "2022-04-30", "package": "verdaccio" },
+ { "downloads": 323348, "start": "2022-05-01", "end": "2022-05-31", "package": "verdaccio" },
+ { "downloads": 375749, "start": "2022-06-01", "end": "2022-06-30", "package": "verdaccio" },
+ { "downloads": 370502, "start": "2022-07-01", "end": "2022-07-31", "package": "verdaccio" },
+ { "downloads": 501449, "start": "2022-08-01", "end": "2022-08-31", "package": "verdaccio" },
+ { "downloads": 568526, "start": "2022-09-01", "end": "2022-09-30", "package": "verdaccio" },
+ { "downloads": 487293, "start": "2022-10-01", "end": "2022-10-31", "package": "verdaccio" },
+ { "downloads": 468798, "start": "2022-11-01", "end": "2022-11-30", "package": "verdaccio" },
+ { "downloads": 434725, "start": "2022-12-01", "end": "2022-12-31", "package": "verdaccio" },
+ { "downloads": 479056, "start": "2023-01-01", "end": "2023-01-31", "package": "verdaccio" },
+ { "downloads": 483402, "start": "2023-02-01", "end": "2023-02-28", "package": "verdaccio" },
+ { "downloads": 567208, "start": "2023-03-01", "end": "2023-03-31", "package": "verdaccio" },
+ { "downloads": 468070, "start": "2023-04-01", "end": "2023-04-30", "package": "verdaccio" },
+ { "downloads": 453703, "start": "2023-05-01", "end": "2023-05-31", "package": "verdaccio" },
+ { "downloads": 471742, "start": "2023-06-01", "end": "2023-06-30", "package": "verdaccio" },
+ { "downloads": 478651, "start": "2023-07-01", "end": "2023-07-31", "package": "verdaccio" },
+ { "downloads": 522940, "start": "2023-08-01", "end": "2023-08-31", "package": "verdaccio" },
+ { "downloads": 462706, "start": "2023-09-01", "end": "2023-09-30", "package": "verdaccio" },
+ { "downloads": 578650, "start": "2023-10-01", "end": "2023-10-31", "package": "verdaccio" },
+ { "downloads": 598748, "start": "2023-11-01", "end": "2023-11-30", "package": "verdaccio" },
+ { "downloads": 514988, "start": "2023-12-01", "end": "2023-12-31", "package": "verdaccio" },
+ { "downloads": 709529, "start": "2024-01-01", "end": "2024-01-31", "package": "verdaccio" },
+ { "downloads": 628021, "start": "2024-02-01", "end": "2024-02-29", "package": "verdaccio" },
+ { "downloads": 674788, "start": "2024-03-01", "end": "2024-03-31", "package": "verdaccio" },
+ { "downloads": 708686, "start": "2024-04-01", "end": "2024-04-30", "package": "verdaccio" },
+ { "downloads": 738101, "start": "2024-05-01", "end": "2024-05-31", "package": "verdaccio" },
+ { "downloads": 684335, "start": "2024-06-01", "end": "2024-06-30", "package": "verdaccio" },
+ { "downloads": 752409, "start": "2024-07-01", "end": "2024-07-31", "package": "verdaccio" },
+ { "downloads": 812871, "start": "2024-08-01", "end": "2024-08-31", "package": "verdaccio" },
+ { "downloads": 774378, "start": "2024-09-01", "end": "2024-09-30", "package": "verdaccio" },
+ { "downloads": 895188, "start": "2024-10-01", "end": "2024-10-31", "package": "verdaccio" },
+ { "downloads": 890807, "start": "2024-11-01", "end": "2024-11-30", "package": "verdaccio" },
+ { "downloads": 825901, "start": "2024-12-01", "end": "2024-12-31", "package": "verdaccio" },
+ { "downloads": 876894, "start": "2025-01-01", "end": "2025-01-31", "package": "verdaccio" },
+ { "downloads": 0, "start": "2025-02-01", "end": "2025-02-03", "package": "verdaccio" }
+]
diff --git a/packages/tools/local-scripts/src/run.ts b/packages/tools/local-scripts/src/run.ts
index a584fa570..b6da65795 100644
--- a/packages/tools/local-scripts/src/run.ts
+++ b/packages/tools/local-scripts/src/run.ts
@@ -2,6 +2,7 @@ import { Cli } from 'clipanion';
import { DockerPullCommand } from './api/dockerPullCommand';
import { NpmjsApiDownloadCommand } from './api/npmjsApiDownloadCommand';
+import { FetchMonthlyDataCommand, FetchYearlyDataCommand } from './api/npmjsApiDownloadPoints';
import { TranslationsApiCommand } from './api/translationsCommand';
const [node, app, ...args] = process.argv;
@@ -15,6 +16,8 @@ const cli = new Cli({
cli.register(TranslationsApiCommand);
cli.register(NpmjsApiDownloadCommand);
cli.register(DockerPullCommand);
+cli.register(FetchMonthlyDataCommand);
+cli.register(FetchYearlyDataCommand);
cli.runExit(args, Cli.defaultContext);
process.on('uncaughtException', function (err) {
diff --git a/packages/tools/local-scripts/src/yearly_downloads.json b/packages/tools/local-scripts/src/yearly_downloads.json
new file mode 100644
index 000000000..9d0748a20
--- /dev/null
+++ b/packages/tools/local-scripts/src/yearly_downloads.json
@@ -0,0 +1,12 @@
+{
+ "2016": 1627,
+ "2017": 37490,
+ "2018": 204227,
+ "2019": 1240495,
+ "2020": 1119180,
+ "2021": 2195646,
+ "2022": 4723577,
+ "2023": 6079864,
+ "2024": 9095014,
+ "2025": 876894
+}
diff --git a/website/src/components/Chart/MonhtlyNpmjsDownloadsChart.tsx b/website/src/components/Chart/MonhtlyNpmjsDownloadsChart.tsx
new file mode 100644
index 000000000..1ab639256
--- /dev/null
+++ b/website/src/components/Chart/MonhtlyNpmjsDownloadsChart.tsx
@@ -0,0 +1,54 @@
+import {
+ CategoryScale,
+ Chart as ChartJS,
+ Legend,
+ LineElement,
+ LinearScale,
+ PointElement,
+ Title,
+ Tooltip,
+} from 'chart.js';
+import React from 'react';
+import { Line } from 'react-chartjs-2';
+
+import { monthlyDownloads } from '@verdaccio/local-scripts';
+
+ChartJS.register(CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend);
+
+const data = {
+ labels: monthlyDownloads.map((entry) => entry.start),
+ datasets: [
+ {
+ label: 'Npmjs Monthly Downloads',
+ data: monthlyDownloads.map((entry) => entry.downloads),
+ borderColor: 'rgba(75, 192, 192, 1)',
+ backgroundColor: 'rgba(75, 192, 192, 0.2)',
+ fill: false,
+ tension: 0.1,
+ },
+ ],
+};
+
+const options = {
+ responsive: true,
+ scales: {
+ x: {
+ title: {
+ display: true,
+ text: 'Month',
+ },
+ },
+ y: {
+ title: {
+ display: true,
+ text: 'Downloads',
+ },
+ },
+ },
+};
+
+const NpmjsMonthlyDownloadsChart = () => {
+ return