Skip to content

Commit 5722aac

Browse files
committed
ci & conflict
1 parent 19c82f3 commit 5722aac

File tree

180 files changed

+12708
-4186
lines changed

Some content is hidden

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

180 files changed

+12708
-4186
lines changed

.github/workflows/mirror-tangled.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ permissions:
1313
jobs:
1414
mirror:
1515
name: 🕸️ Mirror to Tangled
16+
if: ${{ github.repository == 'npmx-dev/npmx.dev' }}
1617
runs-on: ubuntu-24.04-arm
1718

1819
steps:

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ test-results/
4040

4141
# generated files
4242
shared/types/lexicons
43+
file-tree-sprite.svg
4344

4445
**/__screenshots__/**
4546

.lighthouserc.cjs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,18 @@ module.exports = {
3535
chromePath: findChrome(),
3636
puppeteerScript: './lighthouse-setup.cjs',
3737
settings: {
38-
onlyCategories: ['accessibility'],
38+
onlyCategories: process.env.LH_PERF ? ['performance'] : ['accessibility'],
3939
skipAudits: ['valid-source-maps'],
4040
},
4141
},
4242
assert: {
43-
assertions: {
44-
'categories:accessibility': ['error', { minScore: 1 }],
45-
},
43+
assertions: process.env.LH_PERF
44+
? {
45+
'cumulative-layout-shift': ['error', { maxNumericValue: 0 }],
46+
}
47+
: {
48+
'categories:accessibility': ['error', { minScore: 1 }],
49+
},
4650
},
4751
upload: {
4852
target: 'temporary-public-storage',

.oxfmtignore

Lines changed: 0 additions & 2 deletions
This file was deleted.

CONTRIBUTING.md

Lines changed: 135 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ This focus helps guide our project decisions as a community and what we choose t
3333
- [Available commands](#available-commands)
3434
- [Project structure](#project-structure)
3535
- [Local connector CLI](#local-connector-cli)
36+
- [Mock connector (for local development)](#mock-connector-for-local-development)
3637
- [Code style](#code-style)
3738
- [TypeScript](#typescript)
3839
- [Server API patterns](#server-api-patterns)
@@ -54,6 +55,7 @@ This focus helps guide our project decisions as a community and what we choose t
5455
- [Unit tests](#unit-tests)
5556
- [Component accessibility tests](#component-accessibility-tests)
5657
- [Lighthouse accessibility tests](#lighthouse-accessibility-tests)
58+
- [Lighthouse performance tests](#lighthouse-performance-tests)
5759
- [End to end tests](#end-to-end-tests)
5860
- [Test fixtures (mocking external APIs)](#test-fixtures-mocking-external-apis)
5961
- [Submitting changes](#submitting-changes)
@@ -103,6 +105,10 @@ pnpm dev # Start development server
103105
pnpm build # Production build
104106
pnpm preview # Preview production build
105107

108+
# Connector
109+
pnpm npmx-connector # Start the real connector (requires npm login)
110+
pnpm mock-connector # Start the mock connector (no npm login needed)
111+
106112
# Code Quality
107113
pnpm lint # Run linter (oxlint + oxfmt)
108114
pnpm lint:fix # Auto-fix lint issues
@@ -114,6 +120,7 @@ pnpm test:unit # Unit tests only
114120
pnpm test:nuxt # Nuxt component tests
115121
pnpm test:browser # Playwright E2E tests
116122
pnpm test:a11y # Lighthouse accessibility audits
123+
pnpm test:perf # Lighthouse performance audits (CLS)
117124
```
118125

119126
### Project structure
@@ -155,6 +162,36 @@ pnpm npmx-connector
155162

156163
The connector will check your npm authentication, generate a connection token, and listen for requests from npmx.dev.
157164

165+
### Mock connector (for local development)
166+
167+
If you're working on admin features (org management, package access controls, operations queue) and don't want to use your real npm account, you can run the mock connector instead:
168+
169+
```bash
170+
pnpm mock-connector
171+
```
172+
173+
This starts a mock connector server pre-populated with sample data (orgs, teams, members, packages). No npm login is required — operations succeed immediately without making real npm CLI calls.
174+
175+
The mock connector prints a connection URL to the terminal, just like the real connector. Click it (or paste the token manually) to connect the UI.
176+
177+
**Options:**
178+
179+
```bash
180+
pnpm mock-connector # default: port 31415, user "mock-user", sample data
181+
pnpm mock-connector --port 9999 # custom port
182+
pnpm mock-connector --user alice # custom username
183+
pnpm mock-connector --empty # start with no pre-populated data
184+
```
185+
186+
**Default sample data:**
187+
188+
- **@nuxt**: 4 members (mock-user, danielroe, pi0, antfu), 3 teams (core, docs, triage)
189+
- **@unjs**: 2 members (mock-user, pi0), 1 team (maintainers)
190+
- **Packages**: @nuxt/kit, @nuxt/schema, @unjs/nitro with team-based access controls
191+
192+
> [!TIP]
193+
> Run `pnpm dev` in a separate terminal to start the Nuxt dev server, then click the connection URL from the mock connector to connect.
194+
158195
## Code style
159196

160197
When committing changes, try to keep an eye out for unintended formatting updates. These can make a pull request look noisier than it really is and slow down the review process. Sometimes IDEs automatically reformat files on save, which can unintentionally introduce extra changes.
@@ -379,7 +416,7 @@ npmx.dev uses [@nuxtjs/i18n](https://i18n.nuxtjs.org/) for internationalization.
379416
- All user-facing strings should use translation keys via `$t()` in templates and script
380417
- Translation files live in [`i18n/locales/`](i18n/locales) (e.g., `en-US.json`)
381418
- We use the `no_prefix` strategy (no `/en-US/` or `/fr-FR/` in URLs)
382-
- Locale preference is stored in cookies and respected on subsequent visits
419+
- Locale preference is stored in `localStorage` and respected on subsequent visits
383420

384421
### i18n commands
385422

@@ -641,18 +678,38 @@ pnpm test:a11y:prebuilt
641678

642679
# Or run a single color mode manually
643680
pnpm build:test
644-
LIGHTHOUSE_COLOR_MODE=dark ./scripts/lighthouse-a11y.sh
681+
LIGHTHOUSE_COLOR_MODE=dark ./scripts/lighthouse.sh
645682
```
646683

647684
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/`.
648685

649686
#### Configuration
650687

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 |
688+
| File | Purpose |
689+
| ----------------------- | --------------------------------------------------------- |
690+
| `.lighthouserc.cjs` | Lighthouse CI config (URLs, assertions, Chrome path) |
691+
| `lighthouse-setup.cjs` | Puppeteer script for color mode + client-side API mocking |
692+
| `scripts/lighthouse.sh` | Shell wrapper that runs the audit for a given color mode |
693+
694+
### Lighthouse performance tests
695+
696+
The project also runs Lighthouse performance audits to enforce zero Cumulative Layout Shift (CLS). These run separately from the accessibility audits and test the same set of URLs.
697+
698+
#### How it works
699+
700+
The same `.lighthouserc.cjs` config is shared between accessibility and performance audits. When the `LH_PERF` environment variable is set, the config switches from the `accessibility` category to the `performance` category and asserts that CLS is exactly 0.
701+
702+
#### Running locally
703+
704+
```bash
705+
# Build + run performance audit
706+
pnpm test:perf
707+
708+
# Or against an existing test build
709+
pnpm test:perf:prebuilt
710+
```
711+
712+
Unlike the accessibility audits, performance audits do not run in separate light/dark modes.
656713

657714
### End to end tests
658715

@@ -730,6 +787,74 @@ You need to either:
730787
1. Add a fixture file for that package/endpoint
731788
2. Update the mock handlers in `test/fixtures/mock-routes.cjs` (client) or `modules/runtime/server/cache.ts` (server)
732789

790+
### Testing connector features
791+
792+
Features that require authentication through the local connector (org management, package collaborators, operations queue) are tested using a mock connector server.
793+
794+
#### Architecture
795+
796+
The mock connector infrastructure is shared between the CLI, E2E tests, and Vitest component tests:
797+
798+
```
799+
cli/src/
800+
├── types.ts # ConnectorEndpoints contract (shared by real + mock)
801+
├── mock-state.ts # MockConnectorStateManager (canonical source)
802+
├── mock-app.ts # H3 mock app + MockConnectorServer class
803+
└── mock-server.ts # CLI entry point (pnpm mock-connector)
804+
805+
test/test-utils/ # Re-exports from cli/src/ for test convenience
806+
test/e2e/helpers/ # E2E-specific wrappers (fixtures, global setup)
807+
```
808+
809+
Both the real server (`cli/src/server.ts`) and the mock server (`cli/src/mock-app.ts`) conform to the `ConnectorEndpoints` interface defined in `cli/src/types.ts`. This ensures the API contract is enforced by TypeScript. When adding a new endpoint, update `ConnectorEndpoints` first, then implement it in both servers.
810+
811+
#### Vitest component tests (`test/nuxt/`)
812+
813+
- Mock the `useConnector` composable with reactive state
814+
- Use `document.body` queries for components using Teleport
815+
- See `test/nuxt/components/HeaderConnectorModal.spec.ts` for an example
816+
817+
```typescript
818+
// Create mock state
819+
const mockState = ref({ connected: false, npmUser: null, ... })
820+
821+
// Mock the composable
822+
vi.mock('~/composables/useConnector', () => ({
823+
useConnector: () => ({
824+
isConnected: computed(() => mockState.value.connected),
825+
// ... other properties
826+
}),
827+
}))
828+
```
829+
830+
#### Playwright E2E tests (`test/e2e/`)
831+
832+
- A mock HTTP server starts automatically via Playwright's global setup
833+
- Use the `mockConnector` fixture to set up test data and the `gotoConnected` helper to navigate with authentication
834+
835+
```typescript
836+
test('shows org members', async ({ page, gotoConnected, mockConnector }) => {
837+
// Set up test data
838+
await mockConnector.setOrgData('@testorg', {
839+
users: { testuser: 'owner', member1: 'admin' },
840+
})
841+
842+
// Navigate with connector authentication
843+
await gotoConnected('/@testorg')
844+
845+
// Test assertions
846+
await expect(page.getByRole('link', { name: '@testuser' })).toBeVisible()
847+
})
848+
```
849+
850+
The mock connector supports test endpoints for state manipulation:
851+
852+
- `/__test__/reset` - Reset all mock state
853+
- `/__test__/org` - Set org users, teams, and team members
854+
- `/__test__/user-orgs` - Set user's organizations
855+
- `/__test__/user-packages` - Set user's packages
856+
- `/__test__/package` - Set package collaborators
857+
733858
## Submitting changes
734859

735860
### Before submitting
@@ -767,8 +892,10 @@ Format: `type(scope): description`
767892
- `fix(i18n): update French translations`
768893
- `chore(deps): update vite to v6`
769894

895+
Where front end changes are made, please include before and after screenshots in your pull request description.
896+
770897
> [!NOTE]
771-
> The subject must start with a lowercase letter. Individual commit messages within your PR don't need to follow this format since they'll be squashed.
898+
> Use lowercase letters in your pull request title. Individual commit messages within your PR don't need to follow this format since they'll be squashed.
772899
773900
### PR descriptions
774901

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ npmx.dev supports npm permalinks – 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) (Chrome only for now).
119+
> Want automatic redirects? Try the [npmx-replace browser extension](https://github.com/tylersayshi/npmx-replace-extension) (Chrome only for now) or the separate [npmx-redirect extension](https://github.com/iaverages/npmx-redirect) for [Chrome](https://chromewebstore.google.com/detail/lbhjgfgpnlihfmobnohoipeljollhlnb) / [Firefox](https://addons.mozilla.org/en-GB/firefox/addon/npmx-redirect/).
120120
121121
#### Not yet supported
122122

@@ -159,6 +159,7 @@ We welcome contributions – please do feel free to explore the project and
159159
- [nxjt](https://nxjt.netlify.app) – npmx Jump To: Quickly navigate to npmx common webpages.
160160
- [npmx-weekly](https://npmx-weekly.trueberryless.org/) – A weekly newsletter for the npmx ecosystem. Add your own content via suggestions in the weekly PR on [GitHub](https://github.com/trueberryless-org/npmx-weekly/pulls?q=is%3Aopen+is%3Apr+label%3A%22%F0%9F%95%94+weekly+post%22).
161161
- [npmx-digest](https://npmx-digest.trueberryless.org/) – An automated news aggregation website that summarizes npmx activity from GitHub and Bluesky every 8 hours.
162+
- [npmx-redirect](https://github.com/iaverages/npmx-redirect) – Browser extension that automatically redirects npmjs.com URLs to npmx.dev.
162163

163164
If you're building something cool, let us know! 🙏
164165

app/app.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ if (import.meta.client) {
127127

128128
<AppHeader :show-logo="!isHomepage" />
129129

130-
<div id="main-content" class="flex-1 flex flex-col">
130+
<div id="main-content" class="flex-1 flex flex-col" tabindex="-1">
131131
<NuxtPage />
132132
</div>
133133

app/components/AppFooter.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ const showModal = () => modalRef.value?.showModal?.()
2323
<LinkBase :to="{ name: 'privacy' }">
2424
{{ $t('privacy_policy.title') }}
2525
</LinkBase>
26+
<LinkBase :to="{ name: 'accessibility' }">
27+
{{ $t('a11y.footer_title') }}
28+
</LinkBase>
2629
<button
2730
type="button"
2831
class="cursor-pointer group inline-flex gap-x-1 items-center justify-center underline-offset-[0.2rem] underline decoration-1 decoration-fg/30 font-mono text-fg hover:(decoration-accent text-accent) focus-visible:(decoration-accent text-accent) transition-colors duration-200"

app/components/AppHeader.vue

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,14 @@ const mobileLinks = computed<NavigationConfigWithGroups>(() => [
6464
external: false,
6565
iconClass: 'i-carbon:security',
6666
},
67+
{
68+
name: 'Accessibility',
69+
label: $t('a11y.title'),
70+
to: { name: 'accessibility' },
71+
type: 'link',
72+
external: false,
73+
iconClass: 'i-carbon:accessibility-alt',
74+
},
6775
],
6876
},
6977
{
@@ -172,7 +180,7 @@ onKeyStroke(
172180
for (const link of desktopLinks.value) {
173181
if (link.to && link.keyshortcut && isKeyWithoutModifiers(e, link.keyshortcut)) {
174182
e.preventDefault()
175-
navigateTo(link.to.name)
183+
navigateTo(link.to)
176184
break
177185
}
178186
}

app/components/AppLogo.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ defineProps<{
2424
font-size="420"
2525
font-weight="500"
2626
text-anchor="middle"
27+
style="user-select: none"
2728
>
2829
<tspan>/</tspan>
2930
</text>

0 commit comments

Comments
 (0)