Skip to content

Commit a4d7468

Browse files
committed
Merge branch 'main' into feat/like-comparaison
2 parents 3526acf + d731543 commit a4d7468

156 files changed

Lines changed: 7214 additions & 1070 deletions

File tree

Some content is hidden

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

.github/workflows/autofix.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ jobs:
3232
- name: 📦 Install dependencies
3333
run: pnpm install
3434

35+
- name: 🎨 Check for non-RTL CSS classes
36+
run: pnpm rtl:check
37+
3538
- name: 🌐 Compare translations
3639
run: pnpm i18n:check
3740

.github/workflows/ci.yml

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -117,11 +117,12 @@ jobs:
117117
- name: 🧪 Component tests
118118
run: pnpm test:nuxt run --coverage --reporter=junit --outputFile=test-report.junit.xml
119119

120-
- name: ⬆︎ Upload test results to Codecov
121-
if: ${{ !cancelled() }}
122-
uses: codecov/test-results-action@0fa95f0e1eeaafde2c782583b36b28ad0d8c77d3 # v1
120+
- name: ⬆︎ Upload coverage reports to Codecov
121+
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5
123122
with:
124-
token: ${{ secrets.CODECOV_TOKEN }}
123+
report_type: test_results
124+
env:
125+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
125126

126127
- name: ⬆︎ Upload coverage reports to Codecov
127128
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5
@@ -150,7 +151,9 @@ jobs:
150151
run: pnpm install
151152

152153
- name: 🏗️ Build project
153-
run: pnpm build:playwright
154+
run: pnpm build:test
155+
env:
156+
VALIDATE_HTML: true
154157

155158
- name: 🖥️ Test project (browser)
156159
run: pnpm test:browser:prebuilt
@@ -178,7 +181,7 @@ jobs:
178181
run: pnpm install
179182

180183
- name: 🏗️ Build project
181-
run: NODE_ENV=test pnpm build
184+
run: pnpm build:test
182185

183186
- name: ♿ Accessibility audit (Lighthouse - ${{ matrix.mode }} mode)
184187
run: ./scripts/lighthouse-a11y.sh
@@ -210,3 +213,25 @@ jobs:
210213

211214
- name: 🧹 Check for unused production code
212215
run: pnpm knip --production
216+
217+
i18n:
218+
name: 🌐 i18n validation
219+
runs-on: ubuntu-24.04-arm
220+
221+
steps:
222+
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
223+
224+
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
225+
with:
226+
node-version: lts/*
227+
228+
- uses: pnpm/action-setup@1e1c8eafbd745f64b1ef30a7d7ed7965034c486c # 1e1c8eafbd745f64b1ef30a7d7ed7965034c486c
229+
name: 🟧 Install pnpm
230+
with:
231+
cache: true
232+
233+
- name: 📦 Install dependencies (root only, no scripts)
234+
run: pnpm install --filter . --ignore-scripts
235+
236+
- name: 🌐 Check for missing or dynamic i18n keys
237+
run: pnpm i18n:report

.github/workflows/welcome.yml

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
name: welcome
2+
3+
on:
4+
pull_request_target:
5+
types:
6+
- closed
7+
8+
permissions: {}
9+
10+
jobs:
11+
welcome:
12+
permissions:
13+
pull-requests: write # to comment on PRs
14+
if: github.repository == 'npmx-dev/npmx.dev' && github.event.pull_request.merged == true
15+
runs-on: ubuntu-slim
16+
name: 🎉 Welcome new contributor
17+
steps:
18+
- name: 🎉 Welcome new contributor
19+
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
20+
with:
21+
script: |
22+
const pr = context.payload.pull_request;
23+
const author = pr.user.login;
24+
25+
// Check if this is the author's first merged PR
26+
const { data: prs } = await github.rest.search.issuesAndPullRequests({
27+
q: `repo:${context.repo.owner}/${context.repo.repo} type:pr is:merged author:${author}`,
28+
});
29+
30+
// If the only merged PR is this one, it's their first contribution
31+
if (prs.total_count !== 1) {
32+
console.log(`@${author} already has ${prs.total_count} merged PRs — skipping welcome comment.`);
33+
return;
34+
}
35+
36+
const emojis = ['🎉', '🥳', '🎊', '🚀', '⭐', '💫', '✨', '💪', '👏', '🙌', '🤩', '💥'];
37+
const emoji = emojis[Math.floor(Math.random() * emojis.length)];
38+
39+
const body = [
40+
`Thanks for your first contribution, @${author}! ${emoji}`,
41+
'',
42+
`We'd love to welcome you to the npmx community. Come and say hi on [Discord](https://chat.npmx.dev)! And once you've joined, visit [npmx.wamellow.com](https://npmx.wamellow.com/) to claim the **contributor** role.`,
43+
].join('\n');
44+
45+
await github.rest.issues.createComment({
46+
owner: context.repo.owner,
47+
repo: context.repo.repo,
48+
issue_number: pr.number,
49+
body,
50+
});
51+
52+
console.log(`Welcomed new contributor @${author} on PR #${pr.number}`);

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,6 @@ test-results/
3939

4040
# generated files
4141
shared/types/lexicons
42+
43+
# output
44+
.vercel

.oxlintrc.json

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"$schema": "https://unpkg.com/oxlint/configuration_schema.json",
33
"plugins": ["unicorn", "typescript", "oxc", "vue", "vitest"],
4-
"jsPlugins": ["@e18e/eslint-plugin"],
4+
"jsPlugins": ["@e18e/eslint-plugin", "eslint-plugin-regexp"],
55
"categories": {
66
"correctness": "error",
77
"suspicious": "warn",
@@ -17,7 +17,72 @@
1717
"e18e/prefer-timer-args": "error",
1818
"e18e/prefer-date-now": "error",
1919
"e18e/prefer-regex-test": "error",
20-
"e18e/prefer-array-some": "error"
20+
"e18e/prefer-array-some": "error",
21+
// RegExp - Possible Errors (most critical)
22+
"regexp/no-contradiction-with-assertion": "error",
23+
"regexp/no-dupe-disjunctions": "error",
24+
"regexp/no-empty-alternative": "error",
25+
"regexp/no-empty-capturing-group": "error",
26+
"regexp/no-empty-character-class": "error",
27+
"regexp/no-empty-group": "error",
28+
"regexp/no-empty-lookarounds-assertion": "error",
29+
"regexp/no-escape-backspace": "error",
30+
"regexp/no-invalid-regexp": "error",
31+
"regexp/no-lazy-ends": "error",
32+
"regexp/no-misleading-capturing-group": "error",
33+
"regexp/no-misleading-unicode-character": "error",
34+
"regexp/no-missing-g-flag": "error",
35+
"regexp/no-optional-assertion": "error",
36+
"regexp/no-potentially-useless-backreference": "error",
37+
"regexp/no-super-linear-backtracking": "error",
38+
"regexp/no-useless-assertions": "error",
39+
"regexp/no-useless-backreference": "error",
40+
"regexp/no-useless-dollar-replacements": "error",
41+
"regexp/strict": "error",
42+
// RegExp - Best Practices
43+
"regexp/confusing-quantifier": "warn",
44+
"regexp/control-character-escape": "error",
45+
"regexp/negation": "error",
46+
"regexp/no-dupe-characters-character-class": "error",
47+
"regexp/no-empty-string-literal": "error",
48+
"regexp/no-extra-lookaround-assertions": "error",
49+
"regexp/no-invisible-character": "error",
50+
"regexp/no-legacy-features": "error",
51+
"regexp/no-non-standard-flag": "error",
52+
"regexp/no-obscure-range": "error",
53+
"regexp/no-octal": "error",
54+
"regexp/no-standalone-backslash": "error",
55+
"regexp/no-trivially-nested-assertion": "error",
56+
"regexp/no-trivially-nested-quantifier": "error",
57+
"regexp/no-unused-capturing-group": "warn",
58+
"regexp/no-useless-character-class": "error",
59+
"regexp/no-useless-flag": "error",
60+
"regexp/no-useless-lazy": "error",
61+
"regexp/no-useless-quantifier": "error",
62+
"regexp/no-useless-range": "error",
63+
"regexp/no-useless-set-operand": "error",
64+
"regexp/no-useless-string-literal": "error",
65+
"regexp/no-useless-two-nums-quantifier": "error",
66+
"regexp/no-zero-quantifier": "error",
67+
"regexp/optimal-lookaround-quantifier": "warn",
68+
"regexp/optimal-quantifier-concatenation": "error",
69+
"regexp/prefer-predefined-assertion": "error",
70+
"regexp/prefer-range": "error",
71+
"regexp/prefer-set-operation": "error",
72+
"regexp/simplify-set-operations": "error",
73+
"regexp/use-ignore-case": "error",
74+
// RegExp - Stylistic Issues (less critical, focused on consistency)
75+
"regexp/match-any": "warn",
76+
"regexp/no-useless-escape": "warn",
77+
"regexp/no-useless-non-capturing-group": "warn",
78+
"regexp/prefer-character-class": "warn",
79+
"regexp/prefer-d": "warn",
80+
"regexp/prefer-plus-quantifier": "warn",
81+
"regexp/prefer-question-quantifier": "warn",
82+
"regexp/prefer-star-quantifier": "warn",
83+
"regexp/prefer-unicode-codepoint-escapes": "warn",
84+
"regexp/prefer-w": "warn",
85+
"regexp/sort-flags": "warn"
2186
},
2287
"overrides": [
2388
{

.vscode/settings.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
{
2+
"editor.defaultFormatter": "oxc.oxc-vscode",
3+
"editor.formatOnSave": true,
4+
"i18n-ally.keystyle": "nested",
25
"i18n-ally.localesPaths": ["./i18n/locales"],
3-
"i18n-ally.keystyle": "nested"
6+
"typescript.tsdk": "node_modules/typescript/lib"
47
}

CONTRIBUTING.md

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ This focus helps guide our project decisions as a community and what we choose t
3939
- [Import order](#import-order)
4040
- [Naming conventions](#naming-conventions)
4141
- [Vue components](#vue-components)
42+
- [Internal linking](#internal-linking)
4243
- [RTL Support](#rtl-support)
4344
- [Localization (i18n)](#localization-i18n)
4445
- [Approach](#approach)
@@ -278,6 +279,79 @@ const props = defineProps<{
278279
279280
Ideally, extract utilities into separate files so they can be unit tested. 🙏
280281

282+
### Internal linking
283+
284+
Always use **object syntax with named routes** for internal navigation. This makes links resilient to URL structure changes and provides type safety via `unplugin-vue-router`.
285+
286+
```vue
287+
<!-- Good: named route -->
288+
<NuxtLink :to="{ name: 'settings' }">Settings</NuxtLink>
289+
290+
<!-- Bad: string path -->
291+
<NuxtLink to="/settings">Settings</NuxtLink>
292+
```
293+
294+
The same applies to programmatic navigation:
295+
296+
```typescript
297+
// Good
298+
navigateTo({ name: 'compare' })
299+
router.push({ name: 'search' })
300+
301+
// Bad
302+
navigateTo('/compare')
303+
router.push('/search')
304+
```
305+
306+
For routes with parameters, pass them explicitly:
307+
308+
```vue
309+
<NuxtLink :to="{ name: '~username', params: { username } }">Profile</NuxtLink>
310+
<NuxtLink :to="{ name: 'org', params: { org: orgName } }">Organization</NuxtLink>
311+
```
312+
313+
Query parameters work as expected:
314+
315+
```vue
316+
<NuxtLink :to="{ name: 'compare', query: { packages: pkg.name } }">Compare</NuxtLink>
317+
```
318+
319+
#### Package routes
320+
321+
For package links, use the auto-imported `packageRoute()` utility from `app/utils/router.ts`. It handles scoped/unscoped packages and optional versions:
322+
323+
```vue
324+
<!-- Links to /package/vue -->
325+
<NuxtLink :to="packageRoute('vue')">vue</NuxtLink>
326+
327+
<!-- Links to /package/@nuxt/kit -->
328+
<NuxtLink :to="packageRoute('@nuxt/kit')">@nuxt/kit</NuxtLink>
329+
330+
<!-- Links to /package/vue/v/3.5.0 -->
331+
<NuxtLink :to="packageRoute('vue', '3.5.0')">vue@3.5.0</NuxtLink>
332+
```
333+
334+
> [!IMPORTANT]
335+
> Never construct package URLs as strings. The route structure uses separate `org` and `name` params, and `packageRoute()` handles the splitting correctly.
336+
337+
#### Available route names
338+
339+
| Route name | URL pattern | Parameters |
340+
| ----------------- | --------------------------------- | ------------------------- |
341+
| `index` | `/` | &mdash; |
342+
| `about` | `/about` | &mdash; |
343+
| `compare` | `/compare` | &mdash; |
344+
| `privacy` | `/privacy` | &mdash; |
345+
| `search` | `/search` | &mdash; |
346+
| `settings` | `/settings` | &mdash; |
347+
| `package` | `/package/:org?/:name` | `org?`, `name` |
348+
| `package-version` | `/package/:org?/:name/v/:version` | `org?`, `name`, `version` |
349+
| `code` | `/package-code/:path+` | `path` (array) |
350+
| `docs` | `/package-docs/:path+` | `path` (array) |
351+
| `org` | `/org/:org` | `org` |
352+
| `~username` | `/~:username` | `username` |
353+
| `~username-orgs` | `/~:username/orgs` | `username` |
354+
281355
## RTL Support
282356

283357
We support `right-to-left` languages, we need to make sure that the UI is working correctly in both directions.
@@ -334,7 +408,7 @@ To add a new locale:
334408
cp i18n/locales/uk-UA.json lunaria/files/uk-UA.json
335409
```
336410

337-
> [!IMPORTANT]
411+
> **Important:**
338412
> This file must be committed. Lunaria uses git history to track translation progress, so the build will fail if this file is missing.
339413
340414
5. If the language is `right-to-left`, add `dir: 'rtl'` (see `ar-EG` in config for example)

app/app.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ onKeyDown(
6262
return
6363
}
6464
65-
router.push('/search')
65+
router.push({ name: 'search' })
6666
},
6767
{ dedupe: true },
6868
)

0 commit comments

Comments
 (0)