Skip to content

Commit 29e8bc2

Browse files
committed
Merge branch 'feat/add-input-component' of https://github.com/alexdln/npmx.dev into feat/add-input-component
2 parents 8bd5845 + 2a1d466 commit 29e8bc2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

89 files changed

+2699
-4301
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ jobs:
184184
run: pnpm build:test
185185

186186
- name: ♿ Accessibility audit (Lighthouse - ${{ matrix.mode }} mode)
187-
run: ./scripts/lighthouse-a11y.sh
187+
run: pnpm test:a11y:prebuilt
188188
env:
189189
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
190190
LIGHTHOUSE_COLOR_MODE: ${{ matrix.mode }}

.nuxtrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
setups.@nuxt/test-utils="3.23.0"
1+
setups.@nuxt/test-utils="4.0.0"

CONTRIBUTING.md

Lines changed: 81 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ This focus helps guide our project decisions as a community and what we choose t
4343
- [RTL Support](#rtl-support)
4444
- [Localization (i18n)](#localization-i18n)
4545
- [Approach](#approach)
46+
- [i18n commands](#i18n-commands)
4647
- [Adding a new locale](#adding-a-new-locale)
4748
- [Update translation](#update-translation)
4849
- [Adding translations](#adding-translations)
@@ -52,6 +53,7 @@ This focus helps guide our project decisions as a community and what we choose t
5253
- [Testing](#testing)
5354
- [Unit tests](#unit-tests)
5455
- [Component accessibility tests](#component-accessibility-tests)
56+
- [Lighthouse accessibility tests](#lighthouse-accessibility-tests)
5557
- [End to end tests](#end-to-end-tests)
5658
- [Test fixtures (mocking external APIs)](#test-fixtures-mocking-external-apis)
5759
- [Submitting changes](#submitting-changes)
@@ -111,6 +113,7 @@ pnpm test # Run all Vitest tests
111113
pnpm test:unit # Unit tests only
112114
pnpm test:nuxt # Nuxt component tests
113115
pnpm test:browser # Playwright E2E tests
116+
pnpm test:a11y # Lighthouse accessibility audits
114117
```
115118

116119
### Project structure
@@ -378,6 +381,17 @@ npmx.dev uses [@nuxtjs/i18n](https://i18n.nuxtjs.org/) for internationalization.
378381
- We use the `no_prefix` strategy (no `/en-US/` or `/fr-FR/` in URLs)
379382
- Locale preference is stored in cookies and respected on subsequent visits
380383

384+
### i18n commands
385+
386+
The following scripts help manage translation files. `en.json` is the reference locale.
387+
388+
| Command | Description |
389+
| ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
390+
| `pnpm i18n:check [locale]` | Compares `en.json` with other locale files. Shows missing and extra keys. Optionally filter output by locale (e.g. `pnpm i18n:check ja-JP`). |
391+
| `pnpm i18n:check:fix [locale]` | Same as check, but adds missing keys to other locales with English placeholders. |
392+
| `pnpm i18n:report` | Audits translation keys against code usage in `.vue` and `.ts` files. Reports missing keys (used in code but not in locale), unused keys (in locale but not in code), and dynamic keys. |
393+
| `pnpm i18n:report:fix` | Removes unused keys from `en.json` and all other locale files. |
394+
381395
### Adding a new locale
382396

383397
We are using localization using country variants (ISO-6391) via [multiple translation files](https://i18n.nuxtjs.org/docs/guide/lazy-load-translations#multiple-files-lazy-loading) to avoid repeating every key per country.
@@ -421,25 +435,7 @@ Check [Pluralization rule callback](https://vue-i18n.intlify.dev/guide/essential
421435
We track the current progress of translations with [Lunaria](https://lunaria.dev/) on this site: https://i18n.npmx.dev/
422436
If you see any outdated translations in your language, feel free to update the keys to match the English version.
423437

424-
In order to make sure you have everything up-to-date, you can run:
425-
426-
```bash
427-
pnpm i18n:check <country-code>
428-
```
429-
430-
For example to check if all Japanese translation keys are up-to-date, run:
431-
432-
```bash
433-
pnpm i18n:check ja-JP
434-
```
435-
436-
To automatically add missing keys with English placeholders, use `--fix`:
437-
438-
```bash
439-
pnpm i18n:check:fix fr-FR
440-
```
441-
442-
This will add missing keys with `"EN TEXT TO REPLACE: {english text}"` as placeholder values, making it easier to see what needs translation.
438+
Use `pnpm i18n:check` and `pnpm i18n:check:fix` to verify and fix your locale (see [i18n commands](#i18n-commands) above for details).
443439

444440
#### Country variants (advanced)
445441

@@ -527,6 +523,32 @@ See how `es`, `es-ES`, and `es-419` are configured in [config/i18n.ts](./config/
527523
- Use `common.*` for shared strings (loading, retry, close, etc.)
528524
- Use component-specific prefixes: `package.card.*`, `settings.*`, `nav.*`
529525
- Do not use dashes (`-`) in translation keys; always use underscore (`_`): e.g., `privacy_policy` instead of `privacy-policy`
526+
- **Always use static string literals as translation keys.** Our i18n scripts (`pnpm i18n:report`) rely on static analysis to detect unused and missing keys. Dynamic keys cannot be analyzed and will be flagged as errors.
527+
528+
**Bad:**
529+
530+
```vue
531+
<!-- Template literal -->
532+
<p>{{ $t(`package.tabs.${tab}`) }}</p>
533+
534+
<!-- Variable -->
535+
<p>{{ $t(myKey) }}</p>
536+
```
537+
538+
**Good:**
539+
540+
```typescript
541+
const { t } = useI18n()
542+
543+
const tabLabels = computed(() => ({
544+
readme: t('package.tabs.readme'),
545+
versions: t('package.tabs.versions'),
546+
}))
547+
```
548+
549+
```vue
550+
<p>{{ tabLabels[tab] }}</p>
551+
```
530552

531553
### Using i18n-ally (recommended)
532554

@@ -598,6 +620,40 @@ A coverage test in `test/unit/a11y-component-coverage.spec.ts` ensures all compo
598620
> [!IMPORTANT]
599621
> Just because axe-core doesn't find any obvious issues, it does not mean a component is accessible. Please do additional checks and use best practices.
600622
623+
### Lighthouse accessibility tests
624+
625+
In addition to component-level axe audits, the project runs full-page accessibility audits using [Lighthouse CI](https://github.com/GoogleChrome/lighthouse-ci). These test the rendered pages in both light and dark mode against Lighthouse's accessibility category, requiring a perfect score.
626+
627+
#### How it works
628+
629+
1. The project is built in test mode (`pnpm build:test`), which activates server-side fixture mocking
630+
2. Lighthouse CI starts a preview server and audits three URLs: `/`, `/search?q=nuxt`, and `/package/nuxt`
631+
3. A Puppeteer setup script (`lighthouse-setup.cjs`) runs before each audit to set the color mode and intercept client-side API requests using the same fixtures as the E2E tests
632+
633+
#### Running locally
634+
635+
```bash
636+
# Build + run both light and dark audits
637+
pnpm test:a11y
638+
639+
# Or against an existing test build
640+
pnpm test:a11y:prebuilt
641+
642+
# Or run a single color mode manually
643+
pnpm build:test
644+
LIGHTHOUSE_COLOR_MODE=dark ./scripts/lighthouse-a11y.sh
645+
```
646+
647+
This requires Chrome or Chromium to be installed. The script will auto-detect common installation paths. Results are printed to the terminal and saved in `.lighthouseci/`.
648+
649+
#### Configuration
650+
651+
| File | Purpose |
652+
| ---------------------------- | --------------------------------------------------------- |
653+
| `.lighthouserc.cjs` | Lighthouse CI config (URLs, assertions, Chrome path) |
654+
| `lighthouse-setup.cjs` | Puppeteer script for color mode + client-side API mocking |
655+
| `scripts/lighthouse-a11y.sh` | Shell wrapper that runs the audit for a given color mode |
656+
601657
### End to end tests
602658

603659
Write end-to-end tests using Playwright:
@@ -619,10 +675,12 @@ E2E tests use a fixture system to mock external API requests, ensuring tests are
619675
- Serves pre-recorded fixture data from `test/fixtures/`
620676
- Enabled via `NUXT_TEST_FIXTURES=true` or Nuxt test mode
621677

622-
**Client-side mocking** (`test/e2e/test-utils.ts`):
678+
**Client-side mocking** (`test/fixtures/mock-routes.cjs`):
623679

624-
- Uses Playwright's route interception to mock browser requests
625-
- All test files import from `./test-utils` instead of `@nuxt/test-utils/playwright`
680+
- Shared URL matching and response generation logic used by both Playwright E2E tests and Lighthouse CI
681+
- Playwright tests (`test/e2e/test-utils.ts`) use this via `page.route()` interception
682+
- Lighthouse tests (`lighthouse-setup.cjs`) use this via Puppeteer request interception
683+
- All E2E test files import from `./test-utils` instead of `@nuxt/test-utils/playwright`
626684
- Throws a clear error if an unmocked external request is detected
627685

628686
#### Fixture files
@@ -670,7 +728,7 @@ URL: https://registry.npmjs.org/some-package
670728
You need to either:
671729

672730
1. Add a fixture file for that package/endpoint
673-
2. Update the mock handlers in `test/e2e/test-utils.ts` (client) or `modules/runtime/server/cache.ts` (server)
731+
2. Update the mock handlers in `test/fixtures/mock-routes.cjs` (client) or `modules/runtime/server/cache.ts` (server)
674732

675733
## Submitting changes
676734

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ npmx.dev supports npm permalinks &ndash; just replace `npmjs.com` with `npmx.dev
116116
| `npmjs.com/org/nuxt` | [`npmx.dev/org/nuxt`](https://npmx.dev/org/nuxt) |
117117

118118
> [!TIP]
119-
> Want automatic redirects? Try the [npmx-replace browser extension](https://github.com/tylersayshi/npmx-replace-extension).
119+
> Want automatic redirects? Try the [npmx-replace browser extension](https://github.com/tylersayshi/npmx-replace-extension) (Chrome only for now).
120120
121121
#### Not yet supported
122122

@@ -150,7 +150,7 @@ We welcome contributions &ndash; please do feel free to explore the project and
150150

151151
## Related projects
152152

153-
- [npmx-replace-extension](https://github.com/tylersayshi/npmx-replace-extension) &ndash; Browser extension to redirect npmjs.com to npmx.dev
153+
- [npmx-replace-extension](https://github.com/tylersayshi/npmx-replace-extension) &ndash; Browser extension to redirect npmjs.com to npmx.dev (Chrome only for now)
154154
- [JSR](https://jsr.io/) &ndash; The open-source package registry for modern JavaScript and TypeScript
155155
- [npm-userscript](https://github.com/bluwy/npm-userscript) &ndash; Browser userscript with various improvements and fixes for npmjs.com
156156
- [npm-alt](https://npm.willow.sh/) &ndash; An alternative npm package browser

app/components/Code/DirectoryListing.vue

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import type { PackageFileTree } from '#shared/types'
33
import type { RouteLocationRaw } from 'vue-router'
44
import { getFileIcon } from '~/utils/file-icons'
5-
import { formatBytes } from '~/utils/formatters'
65
76
const props = defineProps<{
87
tree: PackageFileTree[]
@@ -51,6 +50,8 @@ function getCodeRoute(nodePath?: string): RouteLocationRaw {
5150
params: { path: pathSegments as [string, ...string[]] },
5251
}
5352
}
53+
54+
const bytesFormatter = useBytesFormatter()
5455
</script>
5556

5657
<template>
@@ -107,7 +108,7 @@ function getCodeRoute(nodePath?: string): RouteLocationRaw {
107108
v-if="node.type === 'file' && node.size"
108109
class="text-end font-mono text-xs text-fg-subtle"
109110
>
110-
{{ formatBytes(node.size) }}
111+
{{ bytesFormatter.format(node.size) }}
111112
</span>
112113
</NuxtLink>
113114
</td>

app/components/ColumnPicker.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ onKeyDown(
4141
const toggleableColumns = computed(() => props.columns.filter(col => col.id !== 'name'))
4242
4343
// Map column IDs to i18n keys
44-
const columnLabelKey = computed(() => ({
44+
const columnLabels = computed(() => ({
4545
name: $t('filters.columns.name'),
4646
version: $t('filters.columns.version'),
4747
description: $t('filters.columns.description'),
@@ -57,7 +57,7 @@ const columnLabelKey = computed(() => ({
5757
}))
5858
5959
function getColumnLabel(id: ColumnId): string {
60-
const key = columnLabelKey.value[id]
60+
const key = columnLabels.value[id]
6161
return key ?? id
6262
}
6363

app/components/Compare/PackageSelector.vue

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ const filteredResults = computed(() => {
5353
.filter(r => !packages.value.includes(r.name))
5454
})
5555
56+
const numberFormatter = useNumberFormatter()
57+
5658
function addPackage(name: string) {
5759
if (packages.value.length >= maxPackages.value) return
5860
if (packages.value.includes(name)) return
@@ -210,8 +212,8 @@ function handleBlur() {
210212
<p class="text-xs text-fg-subtle">
211213
{{
212214
$t('compare.selector.packages_selected', {
213-
count: packages.length,
214-
max: maxPackages,
215+
count: numberFormatter.format(packages.length),
216+
max: numberFormatter.format(maxPackages),
215217
})
216218
}}
217219
<span v-if="packages.length < 2">{{ $t('compare.selector.add_hint') }}</span>

0 commit comments

Comments
 (0)