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
-
-
-
+
+
+
+ WASM
+
+
+
-
-
-
-
+
+
+
- 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', () => {