Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
ed939b2
docs: refresh home stats for oxlint, vite, and vitest
nozomee May 4, 2026
23b81b2
feat(docs): automate trusted stack stats via JSON and workflow
nozomee May 4, 2026
b1ef0af
Removed the declare module '*.json' block from env.d.ts. & switched t…
nozomee May 4, 2026
55163f2
Dropped generatedAt from trusted-stack-stats.json
nozomee May 4, 2026
93be819
refactor(ci): secure trusted stack stats automation with PR workflow …
nozomee May 4, 2026
29b39bb
refactor(docs): move trusted-stack stats fetcher to .github and fix d…
nozomee May 5, 2026
a453a70
fix(ci): align trusted stack stats typecheck + workflow script paths
nozomee May 6, 2026
b688d4a
chore: remove unused docs type-check flow
nozomee May 7, 2026
bb15e58
chore: remove unused docs type-check config
nozomee May 7, 2026
e90b859
chore(docs): colocate trusted stack stats fetcher
nozomee May 8, 2026
c7e4cc9
chore: align packageManager to pnpm 10.33.2
nozomee May 8, 2026
840b1c2
Merge branch 'main' into main
nozomee May 9, 2026
5364880
Merge branch 'main' into main
fengmk2 May 11, 2026
327b333
Merge branch 'main' into main
nozomee May 11, 2026
377c8cb
fix(docs): satisfy vp fmt sortImports for VitePress theme files
nozomee May 12, 2026
7751d94
Merge branch 'main' into main
nozomee May 12, 2026
b677558
ci(workflows): use GitHub App token for trusted stack stats PRs
nozomee May 12, 2026
e7444cd
Merge remote-tracking branch 'origin/main'
nozomee May 12, 2026
f00f21d
Merge branch 'main' into main
nozomee May 13, 2026
572b7ca
Merge branch 'main' into main
fengmk2 May 14, 2026
e54c5a1
fix(docs): resolve vp check errors in trusted-stack-stats script
fengmk2 May 14, 2026
589b0dc
chore(ci): align setup-node to v6.4.0 in trusted stack stats workflow
fengmk2 May 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions .github/workflows/update-trusted-stack-stats.yml
Comment thread
camc314 marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Update trusted stack stats

on:
schedule:
# Weekly: Monday 06:00 UTC
- cron: '0 6 * * 1'
workflow_dispatch:

permissions: {}

defaults:
run:
shell: bash

jobs:
update:
if: github.repository == 'voidzero-dev/vite-plus' && github.event.repository.fork == false
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version-file: .node-version

- name: Fetch npm and GitHub stats
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: node docs/.vitepress/theme/data/fetch-trusted-stack-stats.ts

- uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
id: app-token
with:
client-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}

- name: Create or update PR
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
with:
base: main
branch: chore/docs-trusted-stack-stats
title: 'chore(docs): refresh trusted stack stats'
sign-commits: true
token: ${{ steps.app-token.outputs.token }}
commit-message: 'chore(docs): refresh trusted stack stats'
add-paths: |
docs/.vitepress/theme/data/trusted-stack-stats.json
body: |
Automated update of trusted stack statistics on the docs homepage.

- npm weekly downloads (last-week): vite, vitest, oxlint
- GitHub stars: vitejs/vite, vitest-dev/vitest, oxc-project/oxc
2 changes: 1 addition & 1 deletion docs/.vitepress/theme/components/home/FeatureCheck.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import oxcIcon from '@assets/icons/oxc-light.svg';
formatting
</li>
<li>
600+ <code class="mx-1 outline-none bg-nickel/50 text-aqua">ESLint</code> compatible
750+ <code class="mx-1 outline-none bg-nickel/50 text-aqua">ESLint</code> compatible
rules
</li>
<li>
Expand Down
30 changes: 24 additions & 6 deletions docs/.vitepress/theme/components/home/ProductivityGrid.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ import productivitySecurityImage from '@local-assets/productivity-security.png';
import tileOxc from '@local-assets/tiles/oxc.png';
import tileVite from '@local-assets/tiles/vite.png';
import tileVitest from '@local-assets/tiles/vitest.png';

import { trustedStackById } from '../../data/trusted-stack-stats';

const viteStack = trustedStackById('vite');
const vitestStack = trustedStackById('vitest');
const oxcStack = trustedStackById('oxc');
</script>

<template>
Expand All @@ -31,13 +37,17 @@ import tileVitest from '@local-assets/tiles/vitest.png';
<div
class="relative flex flex-col flex-1 sm:pl-6 before:content-none sm:before:content-[''] before:absolute before:left-0 before:top-1/2 before:-translate-y-1/2 before:h-full before:w-px before:bg-stroke"
>
<p class="text-primary font-medium text-base sm:text-xl">69m+</p>
<p class="text-primary font-medium text-base sm:text-xl">
{{ viteStack.npmWeeklyDownloadsDisplay }}
</p>
<p class="leading-tight text-base">Weekly npm downloads</p>
</div>
<div
class="relative flex flex-col flex-shrink-0 pl-6 before:content-[''] before:absolute before:left-0 before:top-1/2 before:-translate-y-1/2 before:h-full before:w-px before:bg-stroke"
>
<p class="text-primary font-medium text-base sm:text-xl">78.7k</p>
<p class="text-primary font-medium text-base sm:text-xl">
{{ viteStack.githubStarsDisplay }}
</p>
<p class="leading-tight text-base">GitHub stars</p>
</div>
</div>
Expand All @@ -54,13 +64,17 @@ import tileVitest from '@local-assets/tiles/vitest.png';
<div
class="relative flex flex-col flex-1 sm:pl-6 before:content-none sm:before:content-[''] before:absolute before:left-0 before:top-1/2 before:-translate-y-1/2 before:h-full before:w-px before:bg-stroke"
>
<p class="text-primary font-medium text-base sm:text-xl">35m+</p>
<p class="text-primary font-medium text-base sm:text-xl">
{{ vitestStack.npmWeeklyDownloadsDisplay }}
</p>
<p class="leading-tight text-base">Weekly npm downloads</p>
</div>
<div
class="relative flex flex-col flex-shrink-0 pl-6 before:content-[''] before:absolute before:left-0 before:top-1/2 before:-translate-y-1/2 before:h-full before:w-px before:bg-stroke"
>
<p class="text-primary font-medium text-base sm:text-xl">16.1k</p>
<p class="text-primary font-medium text-base sm:text-xl">
{{ vitestStack.githubStarsDisplay }}
</p>
<p class="leading-tight text-base">GitHub stars</p>
</div>
</div>
Expand All @@ -77,13 +91,17 @@ import tileVitest from '@local-assets/tiles/vitest.png';
<div
class="relative flex flex-col flex-1 sm:pl-6 before:content-none sm:before:content-[''] before:absolute before:left-0 before:top-1/2 before:-translate-y-1/2 before:h-full before:w-px before:bg-stroke"
>
<p class="text-primary font-medium text-base sm:text-xl">5m+</p>
<p class="text-primary font-medium text-base sm:text-xl">
{{ oxcStack.npmWeeklyDownloadsDisplay }}
</p>
<p class="leading-tight text-base">Weekly npm downloads</p>
</div>
<div
class="relative flex flex-col flex-shrink-0 pl-6 before:content-[''] before:absolute before:left-0 before:top-1/2 before:-translate-y-1/2 before:h-full before:w-px before:bg-stroke"
>
<p class="text-primary font-medium text-base sm:text-xl">19.8k</p>
<p class="text-primary font-medium text-base sm:text-xl">
{{ oxcStack.githubStarsDisplay }}
</p>
<p class="leading-tight text-base">GitHub stars</p>
</div>
</div>
Expand Down
127 changes: 127 additions & 0 deletions docs/.vitepress/theme/data/fetch-trusted-stack-stats.ts
Comment thread
camc314 marked this conversation as resolved.
Comment thread
fengmk2 marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { writeFile } from 'node:fs/promises';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';

/**
* Fetches last-week npm download counts and GitHub star counts, then writes
* `trusted-stack-stats.json` for the docs home page.
*
* Requires Node.js >=22.18 (strip types). Run:
* `pnpm -C docs update-trusted-stack-stats`
* or: `node docs/.vitepress/theme/data/fetch-trusted-stack-stats.ts`
*/
import type {
TrustedStackProjectId,
TrustedStackStatProject,
TrustedStackStatsFile,
} from './trusted-stack-stats.types';

const currentDir = dirname(fileURLToPath(import.meta.url));
const OUT = join(currentDir, 'trusted-stack-stats.json');

interface ProjectSource {
readonly id: TrustedStackProjectId;
readonly npmPackage: string;
readonly githubRepo: string;
}

const PROJECTS: readonly ProjectSource[] = [
{ id: 'vite', npmPackage: 'vite', githubRepo: 'vitejs/vite' },
{ id: 'vitest', npmPackage: 'vitest', githubRepo: 'vitest-dev/vitest' },
/** OXC row uses `oxlint` npm weekly downloads as a concrete proxy for the Oxc toolchain. */
{ id: 'oxc', npmPackage: 'oxlint', githubRepo: 'oxc-project/oxc' },
];

function formatWeeklyDownloads(n: number): string {
if (n >= 10_000_000) {
// "m+" reads as a lower bound, so avoid rounding up.
return `${Math.floor(n / 1e6)}m+`;
}
const m = n / 1e6;
const s = m.toFixed(1).replace(/\.0$/, '');
return `${s}m+`;
}

function formatStars(s: number): string {
return `${(s / 1000).toFixed(1)}k`;
}

function parseNpmDownloadsJson(data: unknown, pkg: string): number {
if (typeof data !== 'object' || data === null || !('downloads' in data)) {
throw new Error(`npm API ${pkg}: unexpected payload`);
}
const downloads = (data as { downloads: unknown }).downloads;
if (typeof downloads !== 'number') {
throw new Error(`npm API ${pkg}: unexpected payload`);
}
return downloads;
}

async function npmLastWeekDownloads(pkg: string): Promise<number> {
const url = `https://api.npmjs.org/downloads/point/last-week/${encodeURIComponent(pkg)}`;
const res = await fetch(url);
if (!res.ok) {
const body = await res.text();
throw new Error(`npm API ${pkg}: HTTP ${res.status} ${body}`);
}
return parseNpmDownloadsJson(await res.json(), pkg);
}

function parseGithubRepoJson(data: unknown, repo: string): number {
if (typeof data !== 'object' || data === null || !('stargazers_count' in data)) {
throw new Error(`GitHub API ${repo}: unexpected payload`);
}
const count = (data as { stargazers_count: unknown }).stargazers_count;
if (typeof count !== 'number') {
throw new Error(`GitHub API ${repo}: unexpected payload`);
}
return count;
}

async function fetchGithubStargazers(repo: string): Promise<number> {
const url = `https://api.github.com/repos/${repo}`;
const headers: Record<string, string> = {
Accept: 'application/vnd.github+json',
'X-GitHub-Api-Version': '2022-11-28',
'User-Agent':
'voidzero-dev/vite-plus (docs/.vitepress/theme/data/fetch-trusted-stack-stats.ts)',
};
const token = process.env.GITHUB_TOKEN;
if (token !== undefined && token !== '') {
headers.Authorization = `Bearer ${token}`;
}
const res = await fetch(url, { headers });
if (!res.ok) {
const body = await res.text();
throw new Error(`GitHub API ${repo}: HTTP ${res.status} ${body}`);
}
return parseGithubRepoJson(await res.json(), repo);
}

async function main(): Promise<void> {
const projects: TrustedStackStatProject[] = [];
for (const p of PROJECTS) {
const [npmWeeklyDownloads, stars] = await Promise.all([
npmLastWeekDownloads(p.npmPackage),
fetchGithubStargazers(p.githubRepo),
]);
const row: TrustedStackStatProject = {
id: p.id,
npmPackage: p.npmPackage,
githubRepo: p.githubRepo,
npmWeeklyDownloads,
githubStargazers: stars,
npmWeeklyDownloadsDisplay: formatWeeklyDownloads(npmWeeklyDownloads),
githubStarsDisplay: formatStars(stars),
};
projects.push(row);
}
const payload: TrustedStackStatsFile = { projects };
await writeFile(OUT, `${JSON.stringify(payload, null, 2)}\n`, 'utf8');
console.error(`Wrote ${OUT} at ${new Date().toISOString()}`);
}

void main().catch((err: unknown) => {
console.error(err);
process.exitCode = 1;
});
31 changes: 31 additions & 0 deletions docs/.vitepress/theme/data/trusted-stack-stats.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"projects": [
{
"id": "vite",
"npmPackage": "vite",
"githubRepo": "vitejs/vite",
"npmWeeklyDownloads": 114052837,
"githubStargazers": 80401,
"npmWeeklyDownloadsDisplay": "114m+",
"githubStarsDisplay": "80.4k"
},
{
"id": "vitest",
"npmPackage": "vitest",
"githubRepo": "vitest-dev/vitest",
"npmWeeklyDownloads": 56727793,
"githubStargazers": 16471,
"npmWeeklyDownloadsDisplay": "57m+",
"githubStarsDisplay": "16.5k"
},
{
"id": "oxc",
"npmPackage": "oxlint",
"githubRepo": "oxc-project/oxc",
"npmWeeklyDownloads": 5237088,
"githubStargazers": 20981,
"npmWeeklyDownloadsDisplay": "5.2m+",
"githubStarsDisplay": "21.0k"
}
]
}
22 changes: 22 additions & 0 deletions docs/.vitepress/theme/data/trusted-stack-stats.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import raw from './trusted-stack-stats.json';
import type {
TrustedStackProjectId,
TrustedStackStatProject,
TrustedStackStatsFile,
} from './trusted-stack-stats.types';

export type {
TrustedStackProjectId,
TrustedStackStatProject,
TrustedStackStatsFile,
} from './trusted-stack-stats.types';

export const trustedStackStats = raw as TrustedStackStatsFile;

export function trustedStackById(id: TrustedStackProjectId): TrustedStackStatProject {
const project = trustedStackStats.projects.find((p) => p.id === id);
if (!project) {
throw new Error(`trusted-stack-stats.json: missing project "${id}"`);
}
return project;
}
15 changes: 15 additions & 0 deletions docs/.vitepress/theme/data/trusted-stack-stats.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export type TrustedStackProjectId = 'vite' | 'vitest' | 'oxc';

export interface TrustedStackStatProject {
id: TrustedStackProjectId;
npmPackage: string;
githubRepo: string;
npmWeeklyDownloads: number;
githubStargazers: number;
npmWeeklyDownloadsDisplay: string;
githubStarsDisplay: string;
}

export interface TrustedStackStatsFile {
projects: TrustedStackStatProject[];
}
2 changes: 1 addition & 1 deletion docs/.vitepress/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@
"@components/*": ["../node_modules/@voidzero-dev/vitepress-theme/src/components/*"]
}
},
"include": ["**/*.ts", "**/*.d.ts", "**/*.vue"]
"include": ["**/*.ts", "**/*.d.ts", "**/*.vue", "theme/data/*.json"]
}
3 changes: 2 additions & 1 deletion docs/package.json
Comment thread
fengmk2 marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"scripts": {
"dev": "vitepress dev",
"build": "cp ../packages/cli/install.sh ../packages/cli/install.ps1 public/ && vitepress build",
"preview": "vitepress preview"
"preview": "vitepress preview",
"update-trusted-stack-stats": "node .vitepress/theme/data/fetch-trusted-stack-stats.ts"
},
"dependencies": {
"@iconify/vue": "^5.0.0",
Expand Down
Loading
Loading