diff --git a/app/components/Package/MetricsBadges.vue b/app/components/Package/MetricsBadges.vue index b749df5288..4c07a29fea 100644 --- a/app/components/Package/MetricsBadges.vue +++ b/app/components/Package/MetricsBadges.vue @@ -26,6 +26,11 @@ const hasCjs = computed(() => { return analysis.value.moduleFormat === 'cjs' || analysis.value.moduleFormat === 'dual' }) +const isWasm = computed(() => { + if (!analysis.value) return false + return analysis.value.moduleFormat === 'wasm' +}) + // Types support const hasTypes = computed(() => { if (!analysis.value) return false @@ -80,35 +85,45 @@ const typesHref = computed(() => { - -
  • - - - ESM - - -
  • + - -
  • - - + +
  • + - CJS - - -
  • + + ESM + + + + + +
  • + + + CJS + + +
  • + diff --git a/i18n/locales/en.json b/i18n/locales/en.json index d7ba5a56c6..c455504e0d 100644 --- a/i18n/locales/en.json +++ b/i18n/locales/en.json @@ -533,6 +533,7 @@ "esm": "ES Modules supported", "cjs": "CommonJS supported", "no_esm": "ES Modules unsupported", + "wasm": "Has WebAssembly", "types_label": "Types", "types_included": "Types included", "types_available": "Types available via {package}", diff --git a/i18n/schema.json b/i18n/schema.json index fe6260a303..6fcc23929f 100644 --- a/i18n/schema.json +++ b/i18n/schema.json @@ -1603,6 +1603,9 @@ "no_esm": { "type": "string" }, + "wasm": { + "type": "string" + }, "types_label": { "type": "string" }, diff --git a/shared/utils/package-analysis.ts b/shared/utils/package-analysis.ts index 57ed391d82..18908c3832 100644 --- a/shared/utils/package-analysis.ts +++ b/shared/utils/package-analysis.ts @@ -2,7 +2,7 @@ * Package analysis utilities for detecting module format and TypeScript support */ -export type ModuleFormat = 'esm' | 'cjs' | 'dual' | 'unknown' +export type ModuleFormat = 'esm' | 'cjs' | 'dual' | 'wasm' | 'unknown' export type TypesStatus = | { kind: 'included' } @@ -88,6 +88,11 @@ export function detectModuleFormat(pkg: ExtendedPackageJson): ModuleFormat { return mainIsCJS ? 'dual' : 'esm' } + const mainIsWASM = pkg.main?.endsWith('.wasm') + if (mainIsWASM) { + return 'wasm' + } + if (hasModule || isTypeModule) { return 'esm' } diff --git a/test/nuxt/components/PackageLikeCard.spec.ts b/test/nuxt/components/Package/LikeCard.spec.ts similarity index 100% rename from test/nuxt/components/PackageLikeCard.spec.ts rename to test/nuxt/components/Package/LikeCard.spec.ts diff --git a/test/nuxt/components/Package/MetricsBadges.spec.ts b/test/nuxt/components/Package/MetricsBadges.spec.ts new file mode 100644 index 0000000000..5f7b528d3d --- /dev/null +++ b/test/nuxt/components/Package/MetricsBadges.spec.ts @@ -0,0 +1,66 @@ +import { afterEach, describe, expect, it, vi } from 'vitest' +import { mockNuxtImport, mountSuspended } from '@nuxt/test-utils/runtime' +import type { VueWrapper } from '@vue/test-utils' +import { ref } from 'vue' +import PackageMetricsBadges from '~/components/Package/MetricsBadges.vue' + +const { mockUsePackageAnalysis } = vi.hoisted(() => ({ + mockUsePackageAnalysis: vi.fn(), +})) + +mockNuxtImport('usePackageAnalysis', () => mockUsePackageAnalysis) + +describe('PackageMetricsBadges', () => { + let wrapper: VueWrapper + + afterEach(() => wrapper?.unmount()) + + it('renders the badges', async () => { + mockUsePackageAnalysis.mockReturnValue({ + data: ref({ moduleFormat: 'dual', types: { kind: 'included' } }), + status: ref('success'), + }) + + wrapper = await mountSuspended(PackageMetricsBadges, { + props: { packageName: 'ufo' }, + }) + + const text = wrapper.text() + expect(text).toContain('Types') + expect(text).toContain('ESM') + expect(text).toContain('CJS') + expect(text).not.toContain('WASM') + }) + + it('renders the wasm label', async () => { + mockUsePackageAnalysis.mockReturnValue({ + data: ref({ moduleFormat: 'wasm' }), + status: ref('success'), + }) + + wrapper = await mountSuspended(PackageMetricsBadges, { + props: { packageName: 'swc-plugin-transform-webpack-context' }, + }) + + const text = wrapper.text() + expect(text).toContain('WASM') + expect(text).not.toContain('ESM') + }) + + it('does not render the CJS label when no CJS', async () => { + mockUsePackageAnalysis.mockReturnValue({ + data: ref({ moduleFormat: 'esm', types: { kind: 'included' } }), + status: ref('success'), + }) + + wrapper = await mountSuspended(PackageMetricsBadges, { + props: { packageName: '@nuxt/kit' }, + }) + + const text = wrapper.text() + expect(text).toContain('Types') + expect(text).toContain('ESM') + expect(text).not.toContain('CJS') + expect(text).not.toContain('WASM') + }) +}) diff --git a/test/nuxt/components/PackageSidebar.spec.ts b/test/nuxt/components/Package/Sidebar.spec.ts similarity index 100% rename from test/nuxt/components/PackageSidebar.spec.ts rename to test/nuxt/components/Package/Sidebar.spec.ts diff --git a/test/nuxt/components/PackageVersions.spec.ts b/test/nuxt/components/Package/Versions.spec.ts similarity index 100% rename from test/nuxt/components/PackageVersions.spec.ts rename to test/nuxt/components/Package/Versions.spec.ts diff --git a/test/nuxt/components/PackageWeeklyDownloadStats.spec.ts b/test/nuxt/components/Package/WeeklyDownloadStats.spec.ts similarity index 100% rename from test/nuxt/components/PackageWeeklyDownloadStats.spec.ts rename to test/nuxt/components/Package/WeeklyDownloadStats.spec.ts diff --git a/test/unit/shared/utils/package-analysis.spec.ts b/test/unit/shared/utils/package-analysis.spec.ts index b4a572ac4a..9be81e232d 100644 --- a/test/unit/shared/utils/package-analysis.spec.ts +++ b/test/unit/shared/utils/package-analysis.spec.ts @@ -122,6 +122,10 @@ describe('detectModuleFormat', () => { }), ).toBe('esm') }) + + it('detects WASM from main field', () => { + expect(detectModuleFormat({ main: 'main.wasm' })).toBe('wasm') + }) }) describe('detectTypesStatus', () => {