diff --git a/docs/app/components/BadgeGenerator.vue b/docs/app/components/BadgeGenerator.vue new file mode 100644 index 0000000000..8d36371233 --- /dev/null +++ b/docs/app/components/BadgeGenerator.vue @@ -0,0 +1,94 @@ + + + diff --git a/docs/app/components/BadgeGeneratorParameters.vue b/docs/app/components/BadgeGeneratorParameters.vue new file mode 100644 index 0000000000..aa1b931dd9 --- /dev/null +++ b/docs/app/components/BadgeGeneratorParameters.vue @@ -0,0 +1,263 @@ + + + diff --git a/docs/content/2.guide/1.features.md b/docs/content/2.guide/1.features.md index 75e8decb24..d15505da57 100644 --- a/docs/content/2.guide/1.features.md +++ b/docs/content/2.guide/1.features.md @@ -88,117 +88,3 @@ Quick access to online development environments detected from package READMEs: | :icon{name="i-lucide:pen-tool"} [CodePen](https://codepen.io) | Social development environment for front-end | | :icon{name="i-simple-icons-jsfiddle"} [JSFiddle](https://jsfiddle.net) | Online editor for web snippets | | :icon{name="i-simple-icons-replit"} [Replit](https://replit.com) | Collaborative browser-based IDE | - -### Custom badges - -You can add custom npmx badges to your markdown files using the following syntax: - -```md -[![Open on npmx.dev](https://npmx.dev/api/registry/badge/TYPE/YOUR_PACKAGE)](https://npmx.dev/package/YOUR_PACKAGE) -``` - -::tip -Make sure to replace `TYPE` with one of the options listed below and `YOUR_PACKAGE` with the actual package name (e.g., `vue`, `lodash`, or `@nuxt/kit`). -:: - -#### Available Badge Types - -- **version**: Shows the latest or specific version of the package. :img{src="https://img.shields.io/badge/%233b82f6-3b82f6" class="inline align-middle h-5 w-14"} -- **license**: Displays the package license (e.g., MIT, Apache-2.0). :img{src="https://img.shields.io/badge/%2322c55e-22c55e" class="inline align-middle h-5 w-14"} -- **size**: Shows the install size (via Bundlephobia) or unpacked size. :img{src="https://img.shields.io/badge/%23a855f7-a855f7" class="inline align-middle h-5 w-14"} -- **downloads**: Displays monthly download statistics. :img{src="https://img.shields.io/badge/%23f97316-f97316" class="inline align-middle h-5 w-14"} -- **downloads-day**: Displays daily download statistics. :img{src="https://img.shields.io/badge/%23f97316-f97316" class="inline align-middle h-5 w-14"} -- **downloads-week**: Displays weekly download statistics. :img{src="https://img.shields.io/badge/%23f97316-f97316" class="inline align-middle h-5 w-14"} -- **downloads-month**: Alias for monthly download statistics. :img{src="https://img.shields.io/badge/%23f97316-f97316" class="inline align-middle h-5 w-14"} -- **downloads-year**: Displays yearly download statistics. :img{src="https://img.shields.io/badge/%23f97316-f97316" class="inline align-middle h-5 w-14"} -- **vulnerabilities**: Shows the number of vulnerabilities found via OSV. :img{src="https://img.shields.io/badge/%2322c55e-22c55e" class="inline align-middle h-5 w-14"} / :img{src="https://img.shields.io/badge/%23ef4444-ef4444" class="inline align-middle h-5 w-14"} -- **dependencies**: Lists the total count of package dependencies. :img{src="https://img.shields.io/badge/%2306b6d4-06b6d4" class="inline align-middle h-5 w-14"} -- **created**: Displays the date the package was first published. :img{src="https://img.shields.io/badge/%2364748b-64748b" class="inline align-middle h-5 w-14"} -- **updated**: Displays the date of the most recent modification. :img{src="https://img.shields.io/badge/%2364748b-64748b" class="inline align-middle h-5 w-14"} -- **engines**: Shows the supported Node.js version range. :img{src="https://img.shields.io/badge/%23eab308-eab308" class="inline align-middle h-5 w-14"} -- **types**: Indicates if TypeScript types are included. :img{src="https://img.shields.io/badge/%233b82f6-3b82f6" class="inline align-middle h-5 w-14"} / :img{src="https://img.shields.io/badge/%2364748b-64748b" class="inline align-middle h-5 w-14"} -- **maintainers**: Displays the total count of package maintainers. :img{src="https://img.shields.io/badge/%2306b6d4-06b6d4" class="inline align-middle h-5 w-14"} -- **deprecated**: Shows if the package is active or deprecated. :img{src="https://img.shields.io/badge/%2322c55e-22c55e" class="inline align-middle h-5 w-14"} / :img{src="https://img.shields.io/badge/%23ef4444-ef4444" class="inline align-middle h-5 w-14"} -- **quality**: NPMS.io quality score based on linting and tests. :img{src="https://img.shields.io/badge/%23a855f7-a855f7" class="inline align-middle h-5 w-14"} -- **popularity**: NPMS.io popularity score based on downloads and stars. :img{src="https://img.shields.io/badge/%2306b6d4-06b6d4" class="inline align-middle h-5 w-14"} -- **maintenance**: NPMS.io maintenance score based on activity. :img{src="https://img.shields.io/badge/%23eab308-eab308" class="inline align-middle h-5 w-14"} -- **score**: The overall NPMS.io combined score. :img{src="https://img.shields.io/badge/%233b82f6-3b82f6" class="inline align-middle h-5 w-14"} -- **name**: Simple badge displaying the package name. :img{src="https://img.shields.io/badge/%2364748b-64748b" class="inline align-middle h-5 w-14"} - -#### Examples - -```md -# Version Badge - -[![Open on npmx.dev](https://npmx.dev/api/registry/badge/version/nuxt)](https://npmx.dev/package/nuxt) - -# License Badge - -[![Open on npmx.dev](https://npmx.dev/api/registry/badge/license/vue)](https://npmx.dev/package/vue) - -# Monthly Downloads - -[![Open on npmx.dev](https://npmx.dev/api/registry/badge/downloads/lodash)](https://npmx.dev/package/lodash) - -# Scoped Package (Install Size) - -[![Open on npmx.dev](https://npmx.dev/api/registry/badge/size/@nuxt/kit)](https://npmx.dev/package/@nuxt/kit) - -# Specific Version - -[![Open on npmx.dev](https://npmx.dev/api/registry/badge/version/react/v/18.0.0)](https://npmx.dev/package/react) - -# Quality Score - -[![Open on npmx.dev](https://npmx.dev/api/registry/badge/quality/pinia)](https://npmx.dev/package/pinia) -``` - -#### Customization Parameters - -You can further customize your badges by appending query parameters to the badge URL. - -##### `labelColor` - -Overrides the default label color. You can pass a standard hex code (with or without the `#` prefix). The label text color is automatically chosen (black or white) based on WCAG contrast ratio, so the badge remains readable. - -- **Default**: `#0a0a0a` -- **Usage**: `?labelColor=HEX_CODE` - -##### `label` - -Overrides the default label text. You can pass any string to customize the label displayed on the badge. - -- **Default**: Depends on the badge type (e.g., "version", "downloads/mo"). -- **Usage**: `?label=YOUR_LABEL` - -##### `color` - -Overrides the default strategy color. You can pass a standard hex code (with or without the `#` prefix). The text color is automatically chosen (black or white) based on WCAG contrast ratio, so the badge remains readable. - -- **Default**: Depends on the badge type (e.g., version is blue, downloads are orange). -- **Usage**: `?color=HEX_CODE` - -| Example | URL | -| :------------- | :------------------------------------ | -| **Hot Pink** | `.../badge/version/nuxt?color=ff69b4` | -| **Pure Black** | `.../badge/version/nuxt?color=000000` | -| **Brand Blue** | `.../badge/version/nuxt?color=3b82f6` | - -##### `name` - -When set to `true`, this parameter replaces the static category label (like "version" or "downloads/mo") with the actual name of the package. This is useful for brand-focused READMEs. - -- **Default**: `false` -- **Usage**: `?name=true` - -| Type | Default Label | With `name=true` | -| ------------- | -------------------- | ---------------- | -| **Version** | `version \| 3.12.0` | `nuxt \| 3.12.0` | -| **Downloads** | `downloads/mo \| 2M` | `lodash \| 2M` | - -##### `style` - -Overrides the default badge appearance. Pass `shieldsio` to use the shields.io-compatible style. - -- **Default**: `default` -- **Usage**: `?style=shieldsio` diff --git a/docs/content/2.guide/6.badges.md b/docs/content/2.guide/6.badges.md new file mode 100644 index 0000000000..d068fecf95 --- /dev/null +++ b/docs/content/2.guide/6.badges.md @@ -0,0 +1,123 @@ +--- +title: Badges +description: Generate modern markdown badges with the npmx.dev API +navigation: + icon: i-lucide:badge +--- + +npmx.dev offers many different SVG badges with stats about any package via its API. You can get the Markdown code to display an accessible badge which links to the package URL on npmx.dev with the following interactive generator: + +:badge-generator + +## Available Badge Types + +- **version**: Shows the latest or specific version of the package. :img{src="https://img.shields.io/badge/%233b82f6-3b82f6" class="inline align-middle h-5 w-14"} +- **license**: Displays the package license (e.g., MIT, Apache-2.0). :img{src="https://img.shields.io/badge/%2322c55e-22c55e" class="inline align-middle h-5 w-14"} +- **size**: Shows the install size (via Bundlephobia) or unpacked size. :img{src="https://img.shields.io/badge/%23a855f7-a855f7" class="inline align-middle h-5 w-14"} +- **downloads**: Displays monthly download statistics. :img{src="https://img.shields.io/badge/%23f97316-f97316" class="inline align-middle h-5 w-14"} +- **downloads-day**: Displays daily download statistics. :img{src="https://img.shields.io/badge/%23f97316-f97316" class="inline align-middle h-5 w-14"} +- **downloads-week**: Displays weekly download statistics. :img{src="https://img.shields.io/badge/%23f97316-f97316" class="inline align-middle h-5 w-14"} +- **downloads-month**: Alias for monthly download statistics. :img{src="https://img.shields.io/badge/%23f97316-f97316" class="inline align-middle h-5 w-14"} +- **downloads-year**: Displays yearly download statistics. :img{src="https://img.shields.io/badge/%23f97316-f97316" class="inline align-middle h-5 w-14"} +- **vulnerabilities**: Shows the number of vulnerabilities found via OSV. :img{src="https://img.shields.io/badge/%2322c55e-22c55e" class="inline align-middle h-5 w-14"} / :img{src="https://img.shields.io/badge/%23ef4444-ef4444" class="inline align-middle h-5 w-14"} +- **dependencies**: Lists the total count of package dependencies. :img{src="https://img.shields.io/badge/%2306b6d4-06b6d4" class="inline align-middle h-5 w-14"} +- **created**: Displays the date the package was first published. :img{src="https://img.shields.io/badge/%2364748b-64748b" class="inline align-middle h-5 w-14"} +- **updated**: Displays the date of the most recent modification. :img{src="https://img.shields.io/badge/%2364748b-64748b" class="inline align-middle h-5 w-14"} +- **engines**: Shows the supported Node.js version range. :img{src="https://img.shields.io/badge/%23eab308-eab308" class="inline align-middle h-5 w-14"} +- **types**: Indicates if TypeScript types are included. :img{src="https://img.shields.io/badge/%233b82f6-3b82f6" class="inline align-middle h-5 w-14"} / :img{src="https://img.shields.io/badge/%2364748b-64748b" class="inline align-middle h-5 w-14"} +- **maintainers**: Displays the total count of package maintainers. :img{src="https://img.shields.io/badge/%2306b6d4-06b6d4" class="inline align-middle h-5 w-14"} +- **deprecated**: Shows if the package is active or deprecated. :img{src="https://img.shields.io/badge/%2322c55e-22c55e" class="inline align-middle h-5 w-14"} / :img{src="https://img.shields.io/badge/%23ef4444-ef4444" class="inline align-middle h-5 w-14"} +- **quality**: NPMS.io quality score based on linting and tests. :img{src="https://img.shields.io/badge/%23a855f7-a855f7" class="inline align-middle h-5 w-14"} +- **popularity**: NPMS.io popularity score based on downloads and stars. :img{src="https://img.shields.io/badge/%2306b6d4-06b6d4" class="inline align-middle h-5 w-14"} +- **maintenance**: NPMS.io maintenance score based on activity. :img{src="https://img.shields.io/badge/%23eab308-eab308" class="inline align-middle h-5 w-14"} +- **score**: The overall NPMS.io combined score. :img{src="https://img.shields.io/badge/%233b82f6-3b82f6" class="inline align-middle h-5 w-14"} +- **name**: Simple badge displaying the package name. :img{src="https://img.shields.io/badge/%2364748b-64748b" class="inline align-middle h-5 w-14"} + +## Examples + +```md +# Version Badge + +[![Open on npmx.dev](https://npmx.dev/api/registry/badge/version/nuxt)](https://npmx.dev/package/nuxt) + +# License Badge + +[![Open on npmx.dev](https://npmx.dev/api/registry/badge/license/vue)](https://npmx.dev/package/vue) + +# Monthly Downloads + +[![Open on npmx.dev](https://npmx.dev/api/registry/badge/downloads/lodash)](https://npmx.dev/package/lodash) + +# Scoped Package (Install Size) + +[![Open on npmx.dev](https://npmx.dev/api/registry/badge/size/@nuxt/kit)](https://npmx.dev/package/@nuxt/kit) + +# Specific Version + +[![Open on npmx.dev](https://npmx.dev/api/registry/badge/version/react/v/18.0.0)](https://npmx.dev/package/react) + +# Quality Score + +[![Open on npmx.dev](https://npmx.dev/api/registry/badge/quality/pinia)](https://npmx.dev/package/pinia) +``` + +## Customization Parameters + +You can further customize your badges by appending query parameters to the badge URL. + +Use this generator to get the Markdown code you desire: + +:badge-generator-parameters + +### `label` + +Overrides the default label text. You can pass any string to customize the label displayed on the left half of the badge. + +- **Default**: Depends on the badge type (e.g., "version", "downloads/mo"). +- **Usage**: `?label=YOUR_LABEL` + +### `value` + +Overrides the default value text of the badge. You can pass any string to customize the value displayed on the right half of the badge. + +- **Default**: Calculated values depending on the badge type (e.g., "v4.2.0", "5.4M"). +- **Usage**: `?label=YOUR_LABEL` + +### `labelColor` + +Overrides the default label color. You can pass a standard hex code (with or without the `#` prefix). The label text color is automatically chosen (black or white) based on WCAG contrast ratio, so the badge remains readable. + +- **Default**: `#0a0a0a` +- **Usage**: `?labelColor=HEX_CODE` + +### `color` + +Overrides the default strategy color. You can pass a standard hex code (with or without the `#` prefix). The text color is automatically chosen (black or white) based on WCAG contrast ratio, so the badge remains readable. + +- **Default**: Depends on the badge type (e.g., version is blue, downloads are orange). +- **Usage**: `?color=HEX_CODE` + +| Example | URL | +| :------------- | :------------------------------------ | +| **Hot Pink** | `.../badge/version/nuxt?color=ff69b4` | +| **Pure Black** | `.../badge/version/nuxt?color=000000` | +| **Brand Blue** | `.../badge/version/nuxt?color=3b82f6` | + +### `name` + +When set to `true`, this parameter replaces the static category label (like "version" or "downloads/mo") with the actual name of the package. This is useful for brand-focused READMEs. + +- **Default**: `false` +- **Usage**: `?name=true` + +| Type | Default Label | With `name=true` | +| ------------- | -------------------- | ---------------- | +| **Version** | `version \| 3.12.0` | `nuxt \| 3.12.0` | +| **Downloads** | `downloads/mo \| 2M` | `lodash \| 2M` | + +### `style` + +Overrides the default badge appearance. Pass `shieldsio` to use the shields.io-compatible style. + +- **Default**: `default` +- **Usage**: `?style=shieldsio` diff --git a/docs/shared/utils/badges.ts b/docs/shared/utils/badges.ts new file mode 100644 index 0000000000..c9152c0fad --- /dev/null +++ b/docs/shared/utils/badges.ts @@ -0,0 +1,32 @@ +export const BADGE_TYPES = Object.freeze([ + 'version', + 'license', + 'size', + 'downloads', + 'downloads-day', + 'downloads-week', + 'downloads-month', + 'downloads-year', + 'vulnerabilities', + 'dependencies', + 'created', + 'updated', + 'engines', + 'types', + 'maintainers', + 'deprecated', + 'quality', + 'popularity', + 'maintenance', + 'score', + 'name', +] as const) + +export type BadgeType = (typeof BADGE_TYPES)[number] + +export function titleCase(str: string) { + return str + .split('-') + .map(word => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' per ') +} diff --git a/docs/tsconfig.json b/docs/tsconfig.json new file mode 100644 index 0000000000..307b2134b9 --- /dev/null +++ b/docs/tsconfig.json @@ -0,0 +1,18 @@ +{ + // https://nuxt.com/docs/guide/concepts/typescript + "files": [], + "references": [ + { + "path": "./.nuxt/tsconfig.app.json" + }, + { + "path": "./.nuxt/tsconfig.server.json" + }, + { + "path": "./.nuxt/tsconfig.shared.json" + }, + { + "path": "./.nuxt/tsconfig.node.json" + } + ] +} diff --git a/knip.ts b/knip.ts index 506be4c7c7..b40fc70b0d 100644 --- a/knip.ts +++ b/knip.ts @@ -51,7 +51,8 @@ const config: KnipConfig = { project: ['src/**/*.ts!', '!src/mock-*.ts'], }, 'docs': { - entry: ['app/**/*.{ts,vue,css}'], + entry: ['app/**/*.{ts,vue,css}', 'shared/**/*.{ts,vue,css}'], + project: ['**/*.{ts,vue,cjs,mjs}'], ignoreDependencies: ['@nuxtjs/mdc'], }, }, diff --git a/package.json b/package.json index 97b2e316ae..6812ebc6a7 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "mock-connector": "vp run --filter npmx-connector dev:mock", "generate-pwa-icons": "pwa-assets-generator", "preview": "nuxt preview", - "postinstall": "(pnpm rebuild @resvg/resvg-js || true) && vp run generate:lexicons && vp run generate:sprite && nuxt prepare && vp config", + "postinstall": "(pnpm rebuild @resvg/resvg-js || true) && vp run generate:lexicons && vp run generate:sprite && nuxt prepare && nuxt prepare --cwd docs && vp config", "generate:fixtures": "node scripts/generate-fixtures.ts", "generate:jwk": "node scripts/gen-jwk.ts", "test": "vp test", diff --git a/server/api/registry/badge/[type]/[...pkg].get.ts b/server/api/registry/badge/[type]/[...pkg].get.ts index 5b4033e642..2ce404e381 100644 --- a/server/api/registry/badge/[type]/[...pkg].get.ts +++ b/server/api/registry/badge/[type]/[...pkg].get.ts @@ -23,6 +23,7 @@ const SafeColorSchema = v.pipe( const QUERY_SCHEMA = v.object({ name: v.optional(v.string()), label: v.optional(SafeStringSchema), + value: v.optional(SafeStringSchema), color: v.optional(SafeColorSchema), labelColor: v.optional(SafeColorSchema), }) @@ -289,6 +290,10 @@ async function fetchInstallSize(packageName: string, version: string): Promise { + return { label: 'npm', value: pkgData.name, color: COLORS.slate } + }, + 'version': async (pkgData: globalThis.Packument, requestedVersion?: string) => { const version = requestedVersion ?? getLatestVersion(pkgData) ?? 'unknown' return { @@ -448,6 +453,7 @@ export default defineCachedEventHandler( const labelColor = queryParams.success ? queryParams.output.labelColor : undefined const showName = queryParams.success && queryParams.output.name === 'true' const userLabel = queryParams.success ? queryParams.output.label : undefined + const userValue = queryParams.success ? queryParams.output.value : undefined const badgeStyleResult = v.safeParse(BadgeStyleSchema, query.style) const badgeStyle = badgeStyleResult.success ? badgeStyleResult.output : 'default' @@ -461,7 +467,7 @@ export default defineCachedEventHandler( const strategyResult = await strategy(pkgData, requestedVersion) const finalLabel = userLabel ? userLabel : showName ? packageName : strategyResult.label - const finalValue = strategyResult.value + const finalValue = userValue ? userValue : strategyResult.value const rawColor = userColor ?? strategyResult.color const finalColor = rawColor?.startsWith('#') ? rawColor : `#${rawColor}`