Conversation
I think nuxt prepare will try to compile standard-site-sync and since the lexicon types don't exist yet it will fail. We should generate the types first, and then run prepare.
Also add some breathing room to the top of the blog post
This is a WIP. It only runs once on the client when you refresh the page.
- Combined keyboard shortcuts (settings, compare, blog navigation) - Merged package.json dependencies (kept main versions, added blog dependencies) - Added presetTypography and presetRtl to uno config
Co-authored-by: Dominik K. <dominik@rivo.gg> Co-authored-by: Luke Warlow <luke@warlow.dev> Co-authored-by: Victoria <vicmdo@gmail.com> Co-authored-by: Yevhen Husak <yev.husak@gmail.com> Co-authored-by: Yevhen Husak <gusa4grr@users.noreply.github.com> Co-authored-by: Daniel Roe <daniel@roe.dev> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Jialong Lu <48578099+kalu5@users.noreply.github.com> Co-authored-by: Alexander Schmid <120512287+alexschmd@users.noreply.github.com> Co-authored-by: James Garbutt <43081j@users.noreply.github.com> Co-authored-by: Vincent Taverna <vinnymac@gmail.com> Co-authored-by: Philippe Serhal <philippe.serhal@gmail.com> Co-authored-by: Garth de Wet <garthofhearts@gmail.com> Co-authored-by: Alex Savelyev <91429106+alexdln@users.noreply.github.com> Co-authored-by: Marcus Blättermann <marcus@essenmitsosse.de> Co-authored-by: Wojciech Maj <kontakt@wojtekmaj.pl> Co-authored-by: Chase Naples <cnaples79@gmail.com> Co-authored-by: Felix Schneider <99918022+trueberryless@users.noreply.github.com> Co-authored-by: btea <2356281422@qq.com> Co-authored-by: Santosh Yadav <santosh.yadav198613@gmail.com> Co-authored-by: Vida Xie <vida_2020@163.com> Co-authored-by: Okinea Dev <hi@okinea.dev> Co-authored-by: Maher <50132270+abaza738@users.noreply.github.com> Co-authored-by: Nandkishor Jadoun <183695114+NandkishorJadoun@users.noreply.github.com> Co-authored-by: abeer0 <47961062+iiio2@users.noreply.github.com> Co-authored-by: Alec Lloyd Probert <55991794+graphieros@users.noreply.github.com> Co-authored-by: Stephen Zhou <38493346+hyoban@users.noreply.github.com> Co-authored-by: Matteo Gabriele <m.gabriele.dev@gmail.com> Co-authored-by: Mihkel Eidast <mihkel@eida.st> Co-authored-by: Craig Hart <i@craigary.net> Co-authored-by: Houssam Elbadissi <skaldebane@gmail.com> Co-authored-by: Cassidy Williams <1454517+cassidoo@users.noreply.github.com> Co-authored-by: Cassidy Williams <cassidoo@users.noreply.github.com> Co-authored-by: Salma Alam-Naylor <52798353+whitep4nth3r@users.noreply.github.com> Co-authored-by: Bailey Townsend <baileytownsend2323@gmail.com> Co-authored-by: Denys <40246260+DDeenis@users.noreply.github.com> Co-authored-by: SHAMIL <64640025+shamilkotta@users.noreply.github.com> Co-authored-by: Jiří Michel <admin@venty.cz> Co-authored-by: Lars Kappert <oss@webpro.nl> Co-authored-by: Jonathan Yeong <hey@jonathanyeong.com> Co-authored-by: penjj <32674989+penjj@users.noreply.github.com> Co-authored-by: Nicolas DUBIEN <github@dubien.org> Co-authored-by: Chris <hizyyv@gmail.com> Co-authored-by: Josh King <59017897+kn0wn@users.noreply.github.com> Co-authored-by: Jens Rømer Hesselbjerg <jh.roemer@gmail.com> Co-authored-by: Nathan Knowler <nathan@knowler.dev> Co-authored-by: Stanyslas Bres <15731884+sybers@users.noreply.github.com> Co-authored-by: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Bobbie Goede <bobbiegoede@gmail.com> Co-authored-by: Joaquín Sánchez <userquin@gmail.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: TAKAHASHI Shuuji <id@shuuji3.xyz> Co-authored-by: Tierney Cyren <accounts@bnb.im> Co-authored-by: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Co-authored-by: Abderrahmen Mhemed <33667872+MhemedAbderrahmen@users.noreply.github.com> Co-authored-by: abderrahmen.mhemed <abderrahmen.mhemed@wevioo.com> Co-authored-by: Julien Déramond <juderamond@gmail.com> Co-authored-by: Roman <dev@rman.dev> Co-authored-by: shamilkotta <shamilkotta99@gmail.com> Co-authored-by: rygrit <62373365+RYGRIT@users.noreply.github.com> Co-authored-by: Vrajpal Jhala <43914725+vrajpal-jhala@users.noreply.github.com> Co-authored-by: Lars Kappert <lars@webpro.nl> Co-authored-by: Jaydip Sanghani <91427591+jellydeck@users.noreply.github.com> Co-authored-by: wr <58406705+onebyte8bits@users.noreply.github.com> Co-authored-by: wr <wr@wrtekiMacBook-Pro.local> Co-authored-by: Emanuele Orfanelli <emanueleorfanelli@gmail.com> Co-authored-by: Robin <robin.kehl@singular-it.de> Co-authored-by: rzzf <cszhjh@gmail.com> Co-authored-by: Florian Heuberger <10813063+Flo0806@users.noreply.github.com> Co-authored-by: Mikołaj Misztal <46728995+mikouaji@users.noreply.github.com> Co-authored-by: Nico <98180436+nitodeco@users.noreply.github.com> Co-authored-by: Benjamín Vicente <62021328+benjavicente@users.noreply.github.com> Co-authored-by: Nico Kempe <50241630+nicokempe@users.noreply.github.com> Co-authored-by: SerKo <serko.dev@gmail.com> Co-authored-by: Scott Wu <sw@scottwu.ca> Co-authored-by: Pascal Küsgen <pascalkuesgen@gmail.com> Co-authored-by: Luke Warlow <lwarlow@igalia.com> Co-authored-by: Idris Gadi <85882535+IdrisGit@users.noreply.github.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
2 Skipped Deployments
|
Lunaria Status Overview🌕 This pull request will trigger status changes. Learn moreBy default, every PR changing files present in the Lunaria configuration's You can change this by adding one of the keywords present in the Tracked Files
Warnings reference
|
Co-authored-by: Daniel Roe <daniel@roe.dev>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Daniel Roe <daniel@roe.dev> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Daniel Roe <daniel@roe.dev>
) Co-authored-by: Robin <robin.kehl@singular-it.de> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Nathan Knowler <nathan@knowler.dev> Co-authored-by: Daniel Roe <daniel@roe.dev>
…1080) Co-authored-by: Daniel Roe <daniel@roe.dev>
Co-authored-by: Willow (GHOST) <git@willow.sh> Co-authored-by: Daniel Roe <daniel@roe.dev>
There was a problem hiding this comment.
Actionable comments posted: 19
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (14)
app/pages/privacy.vue (2)
29-32:⚠️ Potential issue | 🟡 MinorRemove the per-button focus-visible utility.
Global button focus-visible styling already exists, so this inline utility should be removed.Proposed fix
- class="inline-flex items-center gap-2 font-mono text-sm text-fg-muted hover:text-fg transition-colors duration-200 rounded focus-visible:outline-accent/70 shrink-0" + class="inline-flex items-center gap-2 font-mono text-sm text-fg-muted hover:text-fg transition-colors duration-200 rounded shrink-0"Based on learnings: In the npmx.dev project, ensure that focus-visible styling for button and select elements is implemented globally in app/assets/main.css with the rule: button:focus-visible, select:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }. Do not apply per-element inline utility classes like focus-visible:outline-accent/70 on these elements in Vue templates or components (e.g., AccessControls.vue).
29-34:⚠️ Potential issue | 🟡 MinorGuard
router.options.history.stateaccess to prevent undefined errors on first render.
router.options.history.stateis undefined during SSR and on initial render (before any client-side navigation) because Vue Router's memory history doesn't create browser history state. Accessing.backdirectly will throw a TypeError.Proposed fix
- v-show="router.options.history.state.back !== null" + v-show="router.options.history.state?.back != null"app/pages/about.vue (2)
52-55:⚠️ Potential issue | 🟡 MinorRemove the per-button focus-visible utility.
Global button focus-visible styling already exists, so this inline utility should be removed.Proposed fix
- class="inline-flex items-center gap-2 font-mono text-sm text-fg-muted hover:text-fg transition-colors duration-200 rounded focus-visible:outline-accent/70 shrink-0" + class="inline-flex items-center gap-2 font-mono text-sm text-fg-muted hover:text-fg transition-colors duration-200 rounded shrink-0"Based on learnings: In the npmx.dev project, ensure that focus-visible styling for button and select elements is implemented globally in app/assets/main.css with the rule: button:focus-visible, select:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }. Do not apply per-element inline utility classes like focus-visible:outline-accent/70 on these elements in Vue templates or components (e.g., AccessControls.vue).
52-57:⚠️ Potential issue | 🟠 MajorGuard
history.stateaccess with client-side execution for SSR compatibility.Vue Router 4 uses memory history during server-side rendering in Nuxt, so
router.options.history.stateis undefined until the app hydrates in the browser. The optional chaining prevents a runtime error, but you should useprocess.clientto ensure the button logic only runs client-side, avoiding implicit undefined checks during SSR.Preferred fix
- v-show="router.options.history.state.back !== null" + v-show="process.client && router.options.history.state?.back != null"Alternatively, move this logic into an
onMounted()hook to delay execution until after hydration.app/components/Package/Versions.vue (1)
561-564:⚠️ Potential issue | 🟡 MinorRemove the per-button focus-visible utility on the major-group toggle.
Global button focus-visible styling already exists, so this inline utility should be removed.Proposed fix
- class="w-4 h-4 flex items-center justify-center text-fg-subtle hover:text-fg transition-colors shrink-0 focus-visible:outline-accent/70 rounded-sm" + class="w-4 h-4 flex items-center justify-center text-fg-subtle hover:text-fg transition-colors shrink-0 rounded-sm"Based on learnings: In the npmx.dev project, ensure that focus-visible styling for button and select elements is implemented globally in app/assets/main.css with the rule: button:focus-visible, select:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }. Do not apply per-element inline utility classes like focus-visible:outline-accent/70 on these elements in Vue templates or components (e.g., AccessControls.vue).
app/components/Package/DownloadAnalytics.vue (2)
977-977:⚠️ Potential issue | 🟡 MinorRemove inline
focus-visibleutility from select element.The select element has
focus-visible:outline-accent/70applied inline, but this project applies focus-visible styling globally viamain.cssfor buttons and selects. Remove the inline utility class to maintain consistency.Based on learnings: "In the npmx.dev project, focus-visible styling for buttons and selects is applied globally via main.css with: button:focus-visible, select:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }."
1035-1035:⚠️ Potential issue | 🟡 MinorRemove inline
focus-visibleutility from button element.The reset button has
focus-visible:outline-accent/70applied inline, which conflicts with the project's global focus-visible styling rule for buttons.Based on learnings: "In the npmx.dev project, ensure that focus-visible styling for button and select elements is implemented globally in app/assets/main.css... Do not apply per-element inline utility classes like focus-visible:outline-accent/70 on these elements."
app/components/Header/ConnectorModal.vue (1)
71-77:⚠️ Potential issue | 🟡 MinorRemove inline
focus-visibleutility from disconnect button.The disconnect button has
focus-visible:outline-accent/70applied inline, which conflicts with the project's global focus-visible styling for buttons.Based on learnings: "In the npmx.dev project, focus-visible styling for buttons and selects is applied globally via main.css."
app/components/Package/Dependencies.vue (3)
225-236:⚠️ Potential issue | 🟠 MajorButton missing essential styling classes.
The "show all" button for optional dependencies has been stripped down to just
mt-2 truncate, losing the text colour, hover states, and transition styling that the equivalent buttons at lines 136 and 184 have. This appears to be an unintentional regression.🐛 Proposed fix to restore consistent button styling
<button v-if="sortedOptionalDependencies.length > 10 && !optionalDepsExpanded" type="button" - class="mt-2 truncate" + class="mt-2 font-mono text-xs text-fg-muted hover:text-fg transition-colors duration-200 rounded" `@click`="optionalDepsExpanded = true" >
133-144:⚠️ Potential issue | 🟡 MinorRemove inline
focus-visibleutility from button.The "show all" button has
focus-visible:outline-accent/70applied inline, which should be handled by the global CSS rule.Based on learnings: "In the npmx.dev project, focus-visible styling for buttons and selects is applied globally via main.css."
181-192:⚠️ Potential issue | 🟡 MinorRemove inline
focus-visibleutility from button.Same issue as the dependencies "show all" button - the inline focus-visible class should be removed in favour of the global styling.
app/components/Package/InstallScripts.vue (1)
44-63:⚠️ Potential issue | 🟡 MinorRemove inline
focus-visibleutility from button.The expandable NPX packages button has
focus-visible:outline-accent/70applied inline. Per project conventions, this should be handled by the global CSS rule.Based on learnings: "In the npmx.dev project, focus-visible styling for buttons and selects is applied globally via main.css."
app/composables/usePackageComparison.ts (1)
133-144:⚠️ Potential issue | 🟠 MajorEncode package names in the likes endpoint.
Using raw
namewill break scoped packages (@scope/pkg) because/becomes a path segment, so likes won’t load. ReuseencodePackageNamehere.💡 Suggested fix
- $fetch<PackageLikes>(`/api/social/likes/${name}`).catch(() => null), + $fetch<PackageLikes>(`/api/social/likes/${encodePackageName(name)}`).catch(() => null),scripts/compare-translations.ts (1)
211-231:⚠️ Potential issue | 🟠 MajorAvoid writing merged locale content back into country-variant files.
When
mergeLocaleis true,loadJsonreturns a merged base+variant object. WritingnewContentback on--fix(or on extra-key removal) will expand the variant file with base translations, which breaks the “variants only contain differences” rule. Consider preventing--fixwrites for merge locales, or writing a raw (non-merged) variant object instead.💡 Minimal guard to prevent `--fix` from overwriting variant files
- const newContent = syncLocaleData(referenceContent, targetContent, stats, fix) + const effectiveFix = fix && !localeInfo.mergeLocale + const newContent = syncLocaleData(referenceContent, targetContent, stats, effectiveFix) - if (stats.extra.length > 0 || fix) { + if (stats.extra.length > 0 || effectiveFix) { writeFileSync(filePath, JSON.stringify(newContent, null, 2) + '\n', 'utf-8') }Based on learnings: “When using country locale variants (e.g., es-419, es-ES), place only translations that differ from the base language in variant JSON files.”
🧹 Nitpick comments (16)
lunaria/files/de-DE.json (1)
454-473: Terminology inconsistency withaccount_menu.ops.The queue section now uses "Aktion/Aktionen" consistently, which is good. However,
account_menu.opson line 823 still uses "Operation | Operationen". This may confuse users seeing different terminology for the same concept.Consider updating line 823 to match:
"ops": "{count} Aktion | {count} Aktionen".github/workflows/ci.yml (1)
89-93: Consider aligning the approach for uploading test results across jobs.The Unit tests job uses
codecov/test-results-action@v1whilst the Component tests job (lines 120-125) usescodecov/codecov-action@v5withreport_type: test_results. Both approaches work, but the inconsistency may cause confusion for maintainers.If the migration to
codecov-actionwithreport_typeis intentional, consider updating this step as well for consistency. If not, consider reverting the Component tests job to usetest-results-action.i18n/locales/de-DE.json (1)
5-5: Keep de-DE as an override-only locale file.This variant should only contain keys that differ from the base de.json; otherwise maintenance and divergence risk increase. Please move unchanged strings to the base file and leave only overrides here.
Based on learnings: “In the npmx.dev project, when using country locale variants (e.g., es-419, es-ES), place only translations that differ from the base language in variant JSON files; the base file is loaded first, then the variant file overlays any keys it defines.”
server/api/registry/badge/[type]/[...pkg].get.ts (1)
298-318: Consider conditionally rendering the label text element when empty.When
finalLabel.trim()is empty,leftWidthis set to 0, but the<text>element at line 316 still renders with empty content. This works correctly but leaves a redundant SVG element.🔧 Optional: Skip label text rendering when empty
<g text-anchor="middle" font-family="'Geist', system-ui, -apple-system, sans-serif" font-size="11"> - <text x="${leftWidth / 2}" y="14" fill="#ffffff">${finalLabel}</text> + ${leftWidth > 0 ? `<text x="${leftWidth / 2}" y="14" fill="#ffffff">${finalLabel}</text>` : ''} <text x="${leftWidth + rightWidth / 2}" y="14" fill="#ffffff">${finalValue}</text> </g>server/utils/shiki.ts (1)
79-79: Consider: Narrowed regex may miss edge cases.The regex now requires exactly one space before
style="...". If Shiki's output ever varies (e.g., multiple spaces or tabs), the style attribute won't be stripped. This is likely fine given Shiki's consistent output, but worth noting.♻️ Optional: More flexible whitespace matching
- html = html.replace(/<pre([^>]*) style="[^"]*"/, '<pre$1') + html = html.replace(/<pre([^>]*)\s+style="[^"]*"/, '<pre$1')app/components/Filter/Chips.vue (2)
22-29: Consider using global focus-visible styling for buttons.This button uses inline
focus-visible:ring-*classes, but the project has global focus-visible styling for buttons inmain.cssusingoutline: 2px solid var(--accent). Using inline utilities here may create inconsistent focus styling across the app.If the small icon button requires custom focus treatment, this exception is reasonable; otherwise, consider relying on the global rule.
Based on learnings: "focus-visible styling for button and select elements is implemented globally in app/assets/main.css... Do not apply per-element inline utility classes... on these elements."
33-40: Same focus-visible consideration applies here.The clear-all button also uses inline
focus-visible:ring-*classes instead of relying on the global button focus styling.scripts/rtl-checker.ts (2)
39-43: Add type guard for array access to ensure type safety.Per coding guidelines, array access by index should be checked. While the loop bounds guarantee
lines[i]exists at runtime, TypeScript cannot verify this. Consider using a non-null assertion or a type guard.🔧 Suggested fix
const lines = file.split('\n') for (let i = 0; i < lines.length; i++) { idx = i + 1 - line = lines[i] + line = lines[i]! await uno.generate(line) }Alternatively, use
for...ofwithentries()for cleaner iteration:- const lines = file.split('\n') - for (let i = 0; i < lines.length; i++) { - idx = i + 1 - line = lines[i] + for (const [i, currentLine] of file.split('\n').entries()) { + idx = i + 1 + line = currentLine await uno.generate(line) }As per coding guidelines: "Ensure you write strictly type-safe code, for example by ensuring you always check when accessing an array value by index".
12-18: Consider adding error handling for file read operations.The
readFilecall at line 18 could throw if the file is inaccessible or has encoding issues. While this is a build/CI script, wrapping in a try/catch would provide clearer error messages and prevent the entire check from failing on a single problematic file.🛡️ Suggested improvement
async function checkFile(path: Dirent): Promise<string | undefined> { if (path.isDirectory() || !path.name.endsWith('.vue')) { return undefined } const filename = resolve(APP_DIRECTORY, path.parentPath, path.name) - const file = await readFile(filename, 'utf-8') + let file: string + try { + file = await readFile(filename, 'utf-8') + } catch (error) { + console.error(`${COLORS.yellow}⚠️ Could not read ${filename}: ${error}${COLORS.reset}`) + return undefined + }Based on learnings: "Use error handling patterns consistently".
app/pages/~[username]/orgs.vue (1)
84-84: Remove the stray top‑level error assignment.
Line 84 pre-populates the error state before any request; keeping the initialnullsimplifies state flow.🧹 Suggested clean-up
-error.value = $t('header.orgs_dropdown.error')i18n/locales/fr-FR.json (3)
57-60: Empty translation objects may indicate incomplete work.The
blog.authorobject is empty. If this is intentional (relying on base locale fallback), consider adding a comment or removing the empty object. If translations are pending, consider adding placeholder text or a TODO.
190-190: Emptylikesobject.The
package.likesobject is empty. Same consideration as above regarding fallback behaviour vs incomplete translations.
959-972: Privacy policy section has empty nested objects.The
privacy_policysection contains multiple empty objects (cookies.what_are,cookies.types, etc.). If these are placeholders for future translations, consider either:
- Removing them until translations are ready
- Adding the actual French translations
- Adding a comment indicating they're pending
Empty objects will fall back to the base locale, which may be the intended behaviour.
app/components/Header/AuthModal.client.vue (1)
9-14: Consider edge case for empty locale value.If
locale.valueis an empty string,split('-')[0]returns an empty string (notundefined), so the?? 'en'fallback won't trigger. ThelocaleSubPathcheck handles this since an empty string won't match the array, but for clarity you might want to handle this explicitly.💡 Suggested improvement
const { locale } = useI18n() -const currentLang = locale.value.split('-')[0] ?? 'en' +const currentLang = locale.value.split('-')[0] || 'en' const localeSubPath = ['ko', 'pt', 'ja'].includes(currentLang) ? currentLang : ''lunaria/prepare-json-files.ts (1)
75-77: Consider adding error handling for JSON parsing.The
loadJsonFilefunction usesJSON.parsewithout a try-catch. If a locale file contains invalid JSON, this will throw an unhandled error that may be difficult to diagnose.💡 Optional: Add error context for debugging
async function loadJsonFile<T = unknown>(name: string): Promise<T> { - return JSON.parse(await fs.readFile(path.resolve(`${localesFolder}/${name}`), 'utf8')) + const filePath = path.resolve(`${localesFolder}/${name}`) + try { + return JSON.parse(await fs.readFile(filePath, 'utf8')) + } catch (error) { + throw new Error(`Failed to load/parse locale file: ${filePath}`, { cause: error }) + } }app/components/Compare/ComparisonGrid.vue (1)
41-48: Potential v-for key collision when comparing different versions of the same package.Using
col.nameas the key could cause issues if comparing different versions of the same package (e.g.,vue@3.5vsvue@3.4), as both would share the same key.Consider using a composite key that includes the version when present:
♻️ Suggested fix for unique key
- <div v-for="col in columns" :key="col.name" class="comparison-cell comparison-cell-header"> + <div v-for="col in columns" :key="col.version ? `${col.name}@${col.version}` : col.name" class="comparison-cell comparison-cell-header">
| - name: ⬆︎ Upload coverage reports to Codecov | ||
| uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5 | ||
| with: | ||
| token: ${{ secrets.CODECOV_TOKEN }} | ||
| report_type: test_results | ||
| env: | ||
| CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} |
There was a problem hiding this comment.
Missing if: ${{ !cancelled() }} condition will skip test results upload on failure.
The Unit tests job (line 90) includes if: ${{ !cancelled() }} to ensure test results are uploaded even when tests fail. This step is missing that condition, so component test results won't be reported to Codecov when the tests fail — precisely when you'd want visibility.
Additionally, the step name says "Upload coverage reports" but report_type: test_results indicates it uploads test results, not coverage. Consider renaming to "Upload test results to Codecov" for clarity (matching the Unit tests job naming).
🔧 Proposed fix
- name: ⬆︎ Upload coverage reports to Codecov
+ if: ${{ !cancelled() }}
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5
with:
report_type: test_results
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}Optionally, also rename the step for consistency:
- - name: ⬆︎ Upload coverage reports to Codecov
+ - name: ⬆︎ Upload test results to Codecov📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - name: ⬆︎ Upload coverage reports to Codecov | |
| uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5 | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| report_type: test_results | |
| env: | |
| CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} | |
| - name: ⬆︎ Upload test results to Codecov | |
| if: ${{ !cancelled() }} | |
| uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5 | |
| with: | |
| report_type: test_results | |
| env: | |
| CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} |
| :focus-visible, | ||
| :-moz-focusring { | ||
| /* weird Firefox behavior makes it necessary to add `!important` | ||
| or otherwise the selector would need to be more specific, | ||
| which it explicitly should not be. */ | ||
| outline: 2px solid var(--accent) !important; | ||
| outline-offset: 2px !important; | ||
| } |
There was a problem hiding this comment.
Restore the button/select focus-visible rule with border-radius.
The new generic :focus-visible rule drops the project’s agreed button/select focus styling (including the 4px radius). Please reintroduce the explicit rule so button/select focus remains consistent.
💡 Suggested fix
:focus-visible,
:-moz-focusring {
/* weird Firefox behavior makes it necessary to add `!important`
or otherwise the selector would need to be more specific,
which it explicitly should not be. */
outline: 2px solid var(--accent) !important;
outline-offset: 2px !important;
}
+
+button:focus-visible,
+select:focus-visible {
+ outline: 2px solid var(--accent) !important;
+ outline-offset: 2px !important;
+ border-radius: 4px;
+}Based on learnings: “Focus-visible styling for button and select elements is applied globally via main.css with: button:focus-visible, select:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }.”
| class="group cursor-pointer inline-flex gap-x-1 items-center justify-center font-mono border border-border rounded-md transition-all duration-200 disabled:(opacity-40 cursor-not-allowed border-transparent)" | ||
| :class="{ | ||
| 'text-sm px-4 py-2': size === 'medium', | ||
| 'text-xs px-2 py-0.5': size === 'small', | ||
| 'bg-transparent text-fg hover:enabled:(bg-fg/10) focus-visible:enabled:(bg-fg/10) aria-pressed:(bg-fg text-bg border-fg hover:enabled:(bg-fg text-bg/50))': | ||
| variant === 'secondary', | ||
| 'text-bg bg-fg hover:enabled:(bg-fg/50) focus-visible:enabled:(bg-fg/50) aria-pressed:(bg-fg text-bg border-fg hover:enabled:(text-bg/50))': | ||
| variant === 'primary', |
There was a problem hiding this comment.
Remove inline focus-visible styling from the button root.
This conflicts with the project rule that button focus-visible styling should be handled globally in app/assets/main.css.
Suggested fix
- 'bg-transparent text-fg hover:enabled:(bg-fg/10) focus-visible:enabled:(bg-fg/10) aria-pressed:(bg-fg text-bg border-fg hover:enabled:(bg-fg text-bg/50))':
+ 'bg-transparent text-fg hover:enabled:(bg-fg/10) aria-pressed:(bg-fg text-bg border-fg hover:enabled:(bg-fg text-bg/50))':
variant === 'secondary',
- 'text-bg bg-fg hover:enabled:(bg-fg/50) focus-visible:enabled:(bg-fg/50) aria-pressed:(bg-fg text-bg border-fg hover:enabled:(text-bg/50))':
+ 'text-bg bg-fg hover:enabled:(bg-fg/50) aria-pressed:(bg-fg text-bg border-fg hover:enabled:(text-bg/50))':
variant === 'primary',Based on learnings: “focus-visible styling for button/select elements is implemented globally in app/assets/main.css… Do not apply per-element inline focus-visible utilities.”
| <script setup lang="ts"> | ||
| const props = defineProps<{ | ||
| as?: string | Component | ||
| }>() | ||
| </script> |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
🧩 Analysis chain
🏁 Script executed:
# Check nuxt.config for auto-import configuration
find . -name "nuxt.config.*" -type f | head -5Repository: npmx-dev/npmx.dev
Length of output: 99
🏁 Script executed:
# Look at the actual file to see its current state
cat -n app/components/Button/Group.vue 2>/dev/null || echo "File not found at that location"Repository: npmx-dev/npmx.dev
Length of output: 508
🏁 Script executed:
# Search for other Vue files that use Component type to understand project pattern
rg "Component" app/components --type vue -B 2 -A 2 | head -50Repository: npmx-dev/npmx.dev
Length of output: 88
🏁 Script executed:
# Check if Component is imported explicitly in the project
rg "import.*Component.*from.*vue" app/ --type vue | head -20Repository: npmx-dev/npmx.dev
Length of output: 88
🏁 Script executed:
# Check the nuxt.config.ts for auto-import configuration
cat nuxt.config.tsRepository: npmx-dev/npmx.dev
Length of output: 8653
🏁 Script executed:
# Search for Component usage in .vue files using find and grep
find app/components -name "*.vue" -exec grep -l "Component" {} \; | head -10Repository: npmx-dev/npmx.dev
Length of output: 122
🏁 Script executed:
# Look for Component imports in any .vue files
find app/components -name "*.vue" -exec grep -A2 "import.*Component" {} + | head -30Repository: npmx-dev/npmx.dev
Length of output: 43
🏁 Script executed:
# Check tsconfig and type declaration files
find . -maxdepth 3 -name "tsconfig.json" -o -name "*.d.ts" | grep -E "(tsconfig|global)" | head -10Repository: npmx-dev/npmx.dev
Length of output: 96
🏁 Script executed:
# Look at the other component that uses Component to see if it has the import
cat app/components/Tag/Static.vueRepository: npmx-dev/npmx.dev
Length of output: 639
🏁 Script executed:
# Check if there's a global type declaration file that might auto-import Component
find . -name "*.d.ts" -type f -exec grep -l "Component" {} \; | head -10Repository: npmx-dev/npmx.dev
Length of output: 43
🏁 Script executed:
# Check tsconfig to understand type checking strictness
cat tsconfig.json | head -50Repository: npmx-dev/npmx.dev
Length of output: 380
Import Component type from Vue for strict type-safety.
The Component type is used in the props definition but not explicitly imported. This violates the project's requirement for strictly type-safe code. Import it from Vue.
<script setup lang="ts">
+import type { Component } from 'vue'
+
const props = defineProps<{
as?: string | Component
}>()📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <script setup lang="ts"> | |
| const props = defineProps<{ | |
| as?: string | Component | |
| }>() | |
| </script> | |
| <script setup lang="ts"> | |
| import type { Component } from 'vue' | |
| const props = defineProps<{ | |
| as?: string | Component | |
| }>() | |
| </script> |
| <button | ||
| type="button" | ||
| :aria-label="copied ? $t('connector.modal.copied') : $t('connector.modal.copy_command')" | ||
| class="ms-auto text-fg-subtle hover:text-fg transition-colors duration-200 focus-visible:outline-accent/70 rounded" | ||
| class="ms-auto text-fg-subtle p-1.5 -m-1.5 hover:text-fg transition-colors duration-200 focus-visible:outline-accent/70 rounded" | ||
| @click="copy('pnpm npmx-connector')" | ||
| > | ||
| <span v-if="!copied" class="i-carbon:copy w-5 h-5" aria-hidden="true" /> | ||
| <span v-else class="i-carbon:checkmark w-5 h-5 text-green-500" aria-hidden="true" /> | ||
| <span v-if="!copied" class="i-carbon:copy block w-5 h-5" aria-hidden="true" /> | ||
| <span v-else class="i-carbon:checkmark block w-5 h-5 text-green-500" aria-hidden="true" /> | ||
| </button> |
There was a problem hiding this comment.
Remove inline focus-visible utility from copy button.
The copy button has focus-visible:outline-accent/70 applied inline. Per project conventions, focus-visible styling for buttons should rely on the global CSS rule.
Based on learnings: "Do not apply per-element inline utility classes like focus-visible:outline-accent/70 on these elements in Vue templates or components."
| import { isCI, provider } from 'std-env' | ||
|
|
||
| export default defineNuxtConfig({ | ||
| extensions: ['.md'], |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Nuxt extensions config option default behaviour override .vue
💡 Result:
- In Nuxt,
nuxt.confighas a top-levelextensionsoption that controls which file extensions Nuxt’s resolver will try when you import/resolve a path without an extension. The default includes".vue"(along with".js",".jsx",".mjs",".ts",".tsx"). [1] - If you set
extensionsyourself, you replace the default list—it’s not automatically merged—so if you omit".vue", Nuxt will no longer resolveFoo→Foo.vueautomatically. [1] - Nuxt Kit’s
resolvePathexplicitly describes these as the extensions “to try”, i.e., they’re attempted in the provided list order (so order affects which file “wins” when multiple exist). [2]
Example (keep defaults but prefer .vue first):
// nuxt.config.ts
export default defineNuxtConfig({
extensions: ['.vue', '.ts', '.tsx', '.js', '.mjs', '.jsx'],
})Example (override and effectively “disable” .vue auto-resolution):
export default defineNuxtConfig({
extensions: ['.ts', '.tsx', '.js', '.mjs', '.jsx'], // no ".vue"
})References: [1] Nuxt config extensions default list and meaning, [2] Nuxt Kit resolvePath extensions “to try” behavior.
🏁 Script executed:
# Find and read the nuxt.config.ts file to understand the full context
find . -name "nuxt.config.ts" -type fRepository: npmx-dev/npmx.dev
Length of output: 99
🏁 Script executed:
# Read the nuxt.config.ts file to see the current extensions configuration
cat -n ./nuxt.config.ts | head -30Repository: npmx-dev/npmx.dev
Length of output: 1306
🏁 Script executed:
# Check if Vue components/files are being imported or used in the project
rg "\.vue['\"]|from '[^']*\.vue'|from \"[^\"]*\.vue\"" --type ts --type tsx --type vue | head -20Repository: npmx-dev/npmx.dev
Length of output: 88
🏁 Script executed:
# Also check if there are .vue files in the project
fd -e vue | head -20Repository: npmx-dev/npmx.dev
Length of output: 702
🏁 Script executed:
# Check how .vue files are being imported in the codebase - with or without extension
rg "from ['\"]\.?\.?[^'\"]*\.vue['\"]|import ['\"]\.?\.?[^'\"]*\.vue['\"]" | head -20Repository: npmx-dev/npmx.dev
Length of output: 1646
🏁 Script executed:
# Check imports without extension to see if auto-resolution is relied upon
rg "from ['\"]~/" --type ts | head -20Repository: npmx-dev/npmx.dev
Length of output: 2261
🏁 Script executed:
# Check for imports without explicit extensions (which would rely on auto-resolution)
rg "from ['\"]~/" --type ts -A 1 | grep -v "\.vue['\"]" | grep -v "\.ts['\"]" | grep -v "\.tsx['\"]" | grep -v "\.js['\"]" | grep -v "\.mjs['\"]" | head -30Repository: npmx-dev/npmx.dev
Length of output: 1447
🏁 Script executed:
# Check if any composables or utils are imported without extension
rg "from ['\"]~/(composables|utils)" --type ts | grep -v "\\.vue" | grep -v "\\.ts" | head -20Repository: npmx-dev/npmx.dev
Length of output: 43
🏁 Script executed:
# Check if there are any composables/utils being imported without extension (relying on auto-resolution)
rg "from ['\"]~/(composables|utils)/" -A 0 | grep -v "\\." | head -10Repository: npmx-dev/npmx.dev
Length of output: 43
🏁 Script executed:
# Double check: search for imports that don't have explicit file extensions
rg "import.*from ['\"]~/?[^'\"]*['\"]" --type ts | grep -v "\\.vue" | grep -v "\\.ts" | grep -v "\\.js" | grep -v "\\.mjs" | head -15Repository: npmx-dev/npmx.dev
Length of output: 43
🏁 Script executed:
# Check what file types are actually being imported in the codebase
rg "from ['\"]" --type ts | sed -E "s/.*from ['\"]([^'\"]*)['\"].*/\1/" | sort | uniq -c | sort -rn | head -30Repository: npmx-dev/npmx.dev
Length of output: 768
🏁 Script executed:
# Check if the project actually needs .md file resolution or if it's just for build/parsing
rg "\\.md" nuxt.config.ts -B 5 -A 5Repository: npmx-dev/npmx.dev
Length of output: 572
Include .vue in the extensions array.
Setting extensions: ['.md'] replaces Nuxt's default extension list entirely, which breaks auto-resolution for .vue files. Although the codebase currently uses explicit imports (e.g., import Component from '~/components/Component.vue'), this configuration is non-idiomatic and prevents implicit resolution from working. Add .vue (and optionally .markdown if needed for variant Markdown files):
Recommended change
- extensions: ['.md'],
+ extensions: ['.vue', '.md'],Or to be more explicit with full defaults:
- extensions: ['.md'],
+ extensions: ['.vue', '.ts', '.tsx', '.js', '.mjs', '.jsx', '.md'],📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| extensions: ['.md'], | |
| extensions: ['.vue', '.md'], |
| import * as v from 'valibot' | ||
|
|
||
| const NPM_USERNAME_RE = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/i | ||
| const NPM_USERNAME_RE = /^[a-z0-9]([\w.-]*[a-z0-9])?$/i |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
npm username allowed characters validation rules official
💡 Result:
Official npm documentation states that an npm username must be lowercase and may contain hyphens (-) and numerals (0-9). In other words, the allowed characters are a-z, 0-9, and - (with no uppercase letters). [1]
Source
- [1] npm Docs — “Creating a new user account on the public registry” (username field requirements) (docs.npmjs.com)
Citations:
Fix regex to reject uppercase letters, underscores, and dots in npm usernames.
The updated regex now accepts characters that npm usernames do not allow. According to npm's official documentation, usernames must be lowercase and can only contain hyphens and numerals—no uppercase letters, underscores, or dots. The current regex uses the case-insensitive flag (i) and character class [\w.-]*, which accept uppercase letters, underscores, and dots. This will accept usernames that npm would reject.
| import { describe, expect, it, vi, beforeEach } from 'vitest' | ||
|
|
||
| const mockRedisSet = vi.fn() | ||
| const mockRedisGet = vi.fn() | ||
| const mockRedisDel = vi.fn() | ||
|
|
||
| vi.mock('@upstash/redis', () => ({ | ||
| Redis: class { | ||
| set = mockRedisSet | ||
| get = mockRedisGet | ||
| del = mockRedisDel | ||
| }, | ||
| })) | ||
|
|
||
| const mockLocalLock = vi.fn() | ||
| vi.mock('@atproto/oauth-client-node', () => ({ | ||
| requestLocalLock: mockLocalLock, | ||
| })) | ||
|
|
||
| const mockConfig = { | ||
| upstash: { | ||
| redisRestUrl: '', | ||
| redisRestToken: '', | ||
| }, | ||
| } | ||
| vi.stubGlobal('useRuntimeConfig', () => mockConfig) | ||
|
|
||
| const LOCK_UUID = '00000000-0000-0000-0000-000000000000' | ||
| vi.spyOn(crypto, 'randomUUID').mockReturnValue(LOCK_UUID) | ||
|
|
||
| const { getOAuthLock } = await import('../../../../../server/utils/atproto/lock') | ||
|
|
||
| function getUpstashLock() { | ||
| mockConfig.upstash.redisRestUrl = 'https://redis.example.com' | ||
| mockConfig.upstash.redisRestToken = 'token-123' | ||
| return getOAuthLock() | ||
| } | ||
|
|
||
| describe('lock', () => { | ||
| beforeEach(() => { | ||
| vi.clearAllMocks() | ||
| mockConfig.upstash.redisRestUrl = '' | ||
| mockConfig.upstash.redisRestToken = '' | ||
| }) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's check if the test file exists and view its content
git ls-files | grep -E 'test/unit/server/utils/atproto/lock\.spec\.ts'Repository: npmx-dev/npmx.dev
Length of output: 104
🏁 Script executed:
# Check for Vitest configuration files
git ls-files | grep -E '(vitest|vite)\.config\.(ts|js)' | head -20Repository: npmx-dev/npmx.dev
Length of output: 77
🏁 Script executed:
# Also check package.json for any Vitest config
git ls-files | grep -E 'package\.json$'Repository: npmx-dev/npmx.dev
Length of output: 108
🏁 Script executed:
# Read the test file to confirm the code state
cat -n test/unit/server/utils/atproto/lock.spec.ts | head -80Repository: npmx-dev/npmx.dev
Length of output: 2940
🏁 Script executed:
# Read the vitest config to check for restoreMocks and unstubGlobals settings
cat -n vitest.config.tsRepository: npmx-dev/npmx.dev
Length of output: 2817
🏁 Script executed:
# Check package.json for any vitest config
cat -n package.json | head -100Repository: npmx-dev/npmx.dev
Length of output: 4610
🏁 Script executed:
# Check Vitest documentation on defaults by searching for any setup or config overrides
rg -i 'restoreMocks|unstubGlobals' --type ts --type js --type jsonRepository: npmx-dev/npmx.dev
Length of output: 43
Move global stubs and spies into beforeEach to prevent test isolation leaks.
vi.stubGlobal('useRuntimeConfig', ...) (line 26) and vi.spyOn(crypto, 'randomUUID', ...) (line 29) are set at module scope and never restored; vi.clearAllMocks() only resets call history. This allows mocked globals to persist across test files. Move these into beforeEach and restore them in afterEach using vi.restoreAllMocks() and vi.unstubAllGlobals().
Proposed change
-import { describe, expect, it, vi, beforeEach } from 'vitest'
+import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'
@@
-vi.stubGlobal('useRuntimeConfig', () => mockConfig)
-
const LOCK_UUID = '00000000-0000-0000-0000-000000000000'
-vi.spyOn(crypto, 'randomUUID').mockReturnValue(LOCK_UUID)
@@
beforeEach(() => {
vi.clearAllMocks()
+ vi.stubGlobal('useRuntimeConfig', () => mockConfig)
+ vi.spyOn(crypto, 'randomUUID').mockReturnValue(LOCK_UUID)
mockConfig.upstash.redisRestUrl = ''
mockConfig.upstash.redisRestToken = ''
})
+ afterEach(() => {
+ vi.restoreAllMocks()
+ vi.unstubAllGlobals()
+ })📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import { describe, expect, it, vi, beforeEach } from 'vitest' | |
| const mockRedisSet = vi.fn() | |
| const mockRedisGet = vi.fn() | |
| const mockRedisDel = vi.fn() | |
| vi.mock('@upstash/redis', () => ({ | |
| Redis: class { | |
| set = mockRedisSet | |
| get = mockRedisGet | |
| del = mockRedisDel | |
| }, | |
| })) | |
| const mockLocalLock = vi.fn() | |
| vi.mock('@atproto/oauth-client-node', () => ({ | |
| requestLocalLock: mockLocalLock, | |
| })) | |
| const mockConfig = { | |
| upstash: { | |
| redisRestUrl: '', | |
| redisRestToken: '', | |
| }, | |
| } | |
| vi.stubGlobal('useRuntimeConfig', () => mockConfig) | |
| const LOCK_UUID = '00000000-0000-0000-0000-000000000000' | |
| vi.spyOn(crypto, 'randomUUID').mockReturnValue(LOCK_UUID) | |
| const { getOAuthLock } = await import('../../../../../server/utils/atproto/lock') | |
| function getUpstashLock() { | |
| mockConfig.upstash.redisRestUrl = 'https://redis.example.com' | |
| mockConfig.upstash.redisRestToken = 'token-123' | |
| return getOAuthLock() | |
| } | |
| describe('lock', () => { | |
| beforeEach(() => { | |
| vi.clearAllMocks() | |
| mockConfig.upstash.redisRestUrl = '' | |
| mockConfig.upstash.redisRestToken = '' | |
| }) | |
| import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest' | |
| const mockRedisSet = vi.fn() | |
| const mockRedisGet = vi.fn() | |
| const mockRedisDel = vi.fn() | |
| vi.mock('@upstash/redis', () => ({ | |
| Redis: class { | |
| set = mockRedisSet | |
| get = mockRedisGet | |
| del = mockRedisDel | |
| }, | |
| })) | |
| const mockLocalLock = vi.fn() | |
| vi.mock('@atproto/oauth-client-node', () => ({ | |
| requestLocalLock: mockLocalLock, | |
| })) | |
| const mockConfig = { | |
| upstash: { | |
| redisRestUrl: '', | |
| redisRestToken: '', | |
| }, | |
| } | |
| const LOCK_UUID = '00000000-0000-0000-0000-000000000000' | |
| const { getOAuthLock } = await import('../../../../../server/utils/atproto/lock') | |
| function getUpstashLock() { | |
| mockConfig.upstash.redisRestUrl = 'https://redis.example.com' | |
| mockConfig.upstash.redisRestToken = 'token-123' | |
| return getOAuthLock() | |
| } | |
| describe('lock', () => { | |
| beforeEach(() => { | |
| vi.clearAllMocks() | |
| vi.stubGlobal('useRuntimeConfig', () => mockConfig) | |
| vi.spyOn(crypto, 'randomUUID').mockReturnValue(LOCK_UUID) | |
| mockConfig.upstash.redisRestUrl = '' | |
| mockConfig.upstash.redisRestToken = '' | |
| }) | |
| afterEach(() => { | |
| vi.restoreAllMocks() | |
| vi.unstubAllGlobals() | |
| }) |
| function getUpstashLock() { | ||
| mockConfig.upstash.redisRestUrl = 'https://redis.example.com' | ||
| mockConfig.upstash.redisRestToken = 'token-123' | ||
| return getOAuthLock() | ||
| } | ||
|
|
||
| describe('lock', () => { | ||
| beforeEach(() => { | ||
| vi.clearAllMocks() | ||
| mockConfig.upstash.redisRestUrl = '' | ||
| mockConfig.upstash.redisRestToken = '' | ||
| }) | ||
|
|
||
| it('returns local lock when upstash is not configured', () => { | ||
| const lock = getOAuthLock() | ||
| expect(lock).toBe(mockLocalLock) | ||
| }) | ||
|
|
||
| it('returns local lock when only redisRestUrl is set', () => { | ||
| mockConfig.upstash.redisRestUrl = 'https://totally-a-redis-server.com' | ||
| const lock = getOAuthLock() | ||
| expect(lock).toBe(mockLocalLock) | ||
| }) | ||
|
|
||
| it('returns local lock when only redisRestToken is set', () => { | ||
| mockConfig.upstash.redisRestToken = 'super-fancy-secret-token' | ||
| const lock = getOAuthLock() | ||
| expect(lock).toBe(mockLocalLock) | ||
| }) | ||
|
|
||
| it('returns upstash lock when both url and token are configured', () => { | ||
| mockConfig.upstash.redisRestUrl = 'https://redis.redis.redis' | ||
| mockConfig.upstash.redisRestToken = 'token-123' | ||
| const lock = getOAuthLock() | ||
| expect(lock).not.toBe(mockLocalLock) | ||
| expect(typeof lock).toBe('function') | ||
| }) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's find the source file for getOAuthLock()
find . -type f \( -name "*.ts" -o -name "*.tsx" \) ! -path "*/node_modules/*" ! -path "*/dist/*" ! -path "*/.next/*" | xargs grep -l "getOAuthLock" | grep -v "spec\|test"Repository: npmx-dev/npmx.dev
Length of output: 156
🏁 Script executed:
# Let's also check the test file to see what it imports
cat -n test/unit/server/utils/atproto/lock.spec.ts | head -40Repository: npmx-dev/npmx.dev
Length of output: 1293
🏁 Script executed:
# Search for import.meta.dev in the codebase
rg "import\.meta\.dev" --type ts --type tsxRepository: npmx-dev/npmx.dev
Length of output: 88
🏁 Script executed:
# Check vitest configuration
fd -e "vitest\|vite" -e "config" | grep -E "(vitest|vite)" | head -20Repository: npmx-dev/npmx.dev
Length of output: 43
🏁 Script executed:
cat -n server/utils/atproto/lock.tsRepository: npmx-dev/npmx.dev
Length of output: 2742
🏁 Script executed:
# Search for import.meta in the codebase more broadly
rg "import\.meta" --max-count 20Repository: npmx-dev/npmx.dev
Length of output: 4658
🏁 Script executed:
# Check for any development or environment-based conditions in the lock file
cat -n server/utils/atproto/lock.ts | head -100Repository: npmx-dev/npmx.dev
Length of output: 2742
🏁 Script executed:
cat -n vitest.config.tsRepository: npmx-dev/npmx.dev
Length of output: 2817
🏁 Script executed:
# Check if there are any environment variables or define configurations that affect import.meta.dev
rg "define\|import\.meta" vitest.config.ts -A 3 -B 3Repository: npmx-dev/npmx.dev
Length of output: 43
🏁 Script executed:
# Check the Nuxt configuration which might affect import.meta.dev
fd "nuxt.config" | head -5Repository: npmx-dev/npmx.dev
Length of output: 95
🏁 Script executed:
cat -n nuxt.config.ts | head -50Repository: npmx-dev/npmx.dev
Length of output: 1954
🏁 Script executed:
# Check if there's any setup file for the unit tests that might configure import.meta.dev
find test -name "setup.ts" -o -name "setup.js" | xargs catRepository: npmx-dev/npmx.dev
Length of output: 919
🏁 Script executed:
# Search for how import.meta.dev is typically controlled in Nuxt/Vite
rg "import\.meta\.dev.*=|dev.*:" vitest.config.ts nuxt.config.ts -B 2 -A 2Repository: npmx-dev/npmx.dev
Length of output: 317
🏁 Script executed:
# Let's check if we can run the specific test to see if it passes or fails
cd test/unit/server/utils/atproto && npx vitest run lock.spec.ts 2>&1 || trueRepository: npmx-dev/npmx.dev
Length of output: 1069
🏁 Script executed:
# Let's also check what import.meta.dev evaluates to in the test by looking at how vitest configures this
# In Vite/Nuxt, import.meta.dev is controlled by the 'dev' flag. For unit tests in 'node' environment:
rg "env\.DEV|dev.*:" vitest.config.ts nuxt.config.ts -B 3 -A 3Repository: npmx-dev/npmx.dev
Length of output: 377
🏁 Script executed:
# Check if there's a test that actually validates what import.meta.dev is during test execution
rg "import\.meta\.dev" test/ --type ts -B 2 -A 2Repository: npmx-dev/npmx.dev
Length of output: 43
🏁 Script executed:
# Let's look at the package.json to see how tests are typically run
cat -n package.json | grep -A 5 -B 5 "test\|vitest"Repository: npmx-dev/npmx.dev
Length of output: 3583
Make the Upstash-branch tests deterministic by explicitly configuring the development mode flag.
The tests assume import.meta.dev is false during execution, but this depends on Vitest's runtime configuration. To ensure the tests explicitly exercise the Upstash branch regardless of how import.meta.dev is configured, consider either: mocking import.meta.dev to false within the test suite, or wrapping the Upstash tests in a block that forces production-mode evaluation. This makes the test intent explicit and eliminates reliance on implicit runtime behaviour.
| export function presetRtl(checker?: CollectorChecker): Preset { | ||
| return { | ||
| name: 'rtl-preset', | ||
| shortcuts: [ | ||
| ['text-left', 'text-start x-rtl-start'], | ||
| ['text-right', 'text-end x-rtl-end'], | ||
| ], |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
fd -e ts -e vue uno-preset-rtl | head -5Repository: npmx-dev/npmx.dev
Length of output: 111
🏁 Script executed:
cat -n uno-preset-rtl.ts | head -120Repository: npmx-dev/npmx.dev
Length of output: 4706
🏁 Script executed:
rg -n "reportWarning" -A 3 -B 3Repository: npmx-dev/npmx.dev
Length of output: 2679
🏁 Script executed:
rg -n "function presetRtl|export.*presetRtl" -A 20Repository: npmx-dev/npmx.dev
Length of output: 1201
🏁 Script executed:
fd uno.config.ts -o uno.config.js | xargs head -60Repository: npmx-dev/npmx.dev
Length of output: 206
🏁 Script executed:
fd uno.configRepository: npmx-dev/npmx.dev
Length of output: 74
🏁 Script executed:
cat -n uno.config.ts | head -80Repository: npmx-dev/npmx.dev
Length of output: 5650
🏁 Script executed:
rg -n "presetRtl\|presetWind4" uno.config.ts -B 2 -A 2Repository: npmx-dev/npmx.dev
Length of output: 43
🏁 Script executed:
cat -n uno-preset-rtl.ts | tail -100Repository: npmx-dev/npmx.dev
Length of output: 4237
Define text-left/text-right as rules to override preset-wind4.
Preset-wind4 defines these utilities as rules, which take precedence over shortcuts regardless of preset order. Converting them to rules (while keeping presetRtl after presetWind4 in the presets array) ensures the RTL replacements are applied.
Proposed change
- shortcuts: [
- ['text-left', 'text-start x-rtl-start'],
- ['text-right', 'text-end x-rtl-end'],
- ],
rules: [
+ [
+ /^text-(left|right)$/,
+ ([match, direction], context) => {
+ const fullClass = context.rawSelector || match
+ const replacement = direction === 'left' ? 'start' : 'end'
+ const suggestedClass = fullClass.replace(`text-${direction}`, `text-${replacement}`)
+ reportWarning(fullClass, suggestedClass, checker)
+ return { 'text-align': replacement === 'start' ? 'start' : 'end' }
+ },
+ ],
// RTL overridesThere was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
app/pages/blog/index.vue (1)
85-166: Consider removing or tracking this commented-out code separately.This ~80-line block of commented helper functions clutters the file. If the keyboard navigation logic is planned for future use, consider tracking it in a GitHub issue or a separate branch rather than leaving it inline.
| <template | ||
| v-for="(post, idx) in posts" | ||
| :key="`${post.authors.map(a => a.name).join('-')}-${post.title}`" | ||
| > |
There was a problem hiding this comment.
Add defensive check for post.authors in key generation.
The key expression post.authors.map(a => a.name) will throw if authors is undefined or not an array. Per coding guidelines, ensure type-safe array access.
🛡️ Proposed fix
<template
v-for="(post, idx) in posts"
- :key="`${post.authors.map(a => a.name).join('-')}-${post.title}`"
+ :key="`${post.authors?.map(a => a.name).join('-') ?? idx}-${post.title}`"
>As per coding guidelines: "Ensure you write strictly type-safe code, for example by ensuring you always check when accessing an array value by index."
Merge this on 3rd March.