Skip to content

Commit 8f2ec8d

Browse files
authored
Merge branch 'main' into feat/add-input-component
2 parents b3c164a + 80e8db3 commit 8f2ec8d

File tree

11 files changed

+288
-13
lines changed

11 files changed

+288
-13
lines changed

app/components/OgImage/Package.vue

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,12 @@ const { data: likes, refresh: refreshLikes } = useFetch(() => `/api/social/likes
5353
const { stars, refresh: refreshRepoMeta } = useRepoMeta(repositoryUrl)
5454
5555
const formattedStars = computed(() =>
56-
Intl.NumberFormat('en', {
57-
notation: 'compact',
58-
maximumFractionDigits: 1,
59-
}).format(stars.value),
56+
stars.value > 0
57+
? Intl.NumberFormat('en', {
58+
notation: 'compact',
59+
maximumFractionDigits: 1,
60+
}).format(stars.value)
61+
: '',
6062
)
6163
6264
try {
@@ -75,6 +77,7 @@ try {
7577
class="h-full w-full flex flex-col justify-center px-20 bg-[#050505] text-[#fafafa] relative overflow-hidden"
7678
>
7779
<div class="relative z-10 flex flex-col gap-6">
80+
<!-- Package name -->
7881
<div class="flex items-start gap-4">
7982
<div
8083
class="flex items-center justify-center w-16 h-16 p-4 rounded-xl shadow-lg bg-gradient-to-tr from-[#3b82f6]"
@@ -107,6 +110,7 @@ try {
107110
</h1>
108111
</div>
109112

113+
<!-- Version -->
110114
<div
111115
class="flex items-center gap-5 text-4xl font-light text-[#a3a3a3]"
112116
style="font-family: 'Geist Sans', sans-serif"
@@ -122,6 +126,8 @@ try {
122126
>
123127
{{ resolvedVersion }}
124128
</span>
129+
130+
<!-- Downloads (if any) -->
125131
<span v-if="downloads" class="flex items-center gap-2">
126132
<svg
127133
width="30"
@@ -139,7 +145,9 @@ try {
139145
</svg>
140146
<span>{{ $n(downloads.downloads) }}/wk</span>
141147
</span>
142-
<span v-if="pkg?.license" class="flex items-center gap-2">
148+
149+
<!-- License (if any) -->
150+
<span v-if="pkg?.license" class="flex items-center gap-2" data-testid="license">
143151
<svg
144152
viewBox="0 0 32 32"
145153
:fill="primaryColor"
@@ -162,7 +170,9 @@ try {
162170
{{ pkg.license }}
163171
</span>
164172
</span>
165-
<span class="flex items-center gap-2">
173+
174+
<!-- Stars (if any) -->
175+
<span v-if="formattedStars" class="flex items-center gap-2" data-testid="stars">
166176
<svg
167177
xmlns="http://www.w3.org/2000/svg"
168178
viewBox="0 0 32 32"
@@ -179,7 +189,9 @@ try {
179189
{{ formattedStars }}
180190
</span>
181191
</span>
182-
<span class="flex items-center gap-2">
192+
193+
<!-- Likes (if any) -->
194+
<span v-if="likes.totalLikes > 0" class="flex items-center gap-2" data-testid="likes">
183195
<svg
184196
width="32"
185197
height="32"

app/components/Package/Replacement.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const mdnUrl = computed(() => {
1212
1313
const docPath = computed(() => {
1414
if (props.replacement.type !== 'documented' || !props.replacement.docPath) return null
15-
return `https://github.com/es-tooling/module-replacements/blob/main/docs/modules/${props.replacement.docPath}.md`
15+
return `https://e18e.dev/docs/replacements/${props.replacement.docPath}`
1616
})
1717
</script>
1818

app/composables/useRepoMeta.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -762,6 +762,8 @@ export function useRepoMeta(repositoryUrl: MaybeRefOrGetter<string | null | unde
762762
repoRef,
763763
meta,
764764

765+
// TODO(serhalp): Consider removing the zero fallback so callers can make a distinction between
766+
// "unresolved data" and "zero value"
765767
stars: computed(() => meta.value?.stars ?? 0),
766768
forks: computed(() => meta.value?.forks ?? 0),
767769
watchers: computed(() => meta.value?.watchers ?? 0),
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/**
2+
* Scoped View Transitions plugin.
3+
*
4+
* Only triggers the View Transition API when navigating between `/` and `/search`
5+
* (the search-box morph animation). All other navigations are left untouched so
6+
* they feel instant.
7+
*/
8+
export default defineNuxtPlugin(nuxtApp => {
9+
if (!document.startViewTransition) return
10+
11+
let transition: ViewTransition | undefined
12+
let finishTransition: (() => void) | undefined
13+
let hasUAVisualTransition = false
14+
15+
const resetTransitionState = () => {
16+
transition = undefined
17+
finishTransition = undefined
18+
hasUAVisualTransition = false
19+
}
20+
21+
// Respect browser-initiated visual transitions (e.g. swipe-back)
22+
window.addEventListener('popstate', event => {
23+
hasUAVisualTransition =
24+
(event as PopStateEvent & { hasUAVisualTransition?: boolean }).hasUAVisualTransition ?? false
25+
if (hasUAVisualTransition) {
26+
transition?.skipTransition()
27+
}
28+
})
29+
30+
const router = useRouter()
31+
32+
router.beforeResolve(async (to, from) => {
33+
if (to.matched.length === 0) return
34+
35+
const toPath = to.path
36+
const fromPath = from.path
37+
38+
// Only transition between / and /search
39+
if (!isSearchTransition(toPath, fromPath)) return
40+
41+
// Respect prefers-reduced-motion
42+
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return
43+
44+
// Skip if browser already handled the visual transition
45+
if (hasUAVisualTransition) return
46+
47+
const promise = new Promise<void>(resolve => {
48+
finishTransition = resolve
49+
})
50+
51+
let changeRoute: () => void
52+
const ready = new Promise<void>(resolve => (changeRoute = resolve))
53+
54+
transition = document.startViewTransition(() => {
55+
changeRoute!()
56+
return promise
57+
})
58+
59+
transition.finished.then(resetTransitionState)
60+
61+
await nuxtApp.callHook('page:view-transition:start', transition)
62+
63+
return ready
64+
})
65+
66+
// Abort on errors
67+
router.onError(() => {
68+
finishTransition?.()
69+
resetTransitionState()
70+
})
71+
nuxtApp.hook('app:error', () => {
72+
finishTransition?.()
73+
resetTransitionState()
74+
})
75+
nuxtApp.hook('vue:error', () => {
76+
finishTransition?.()
77+
resetTransitionState()
78+
})
79+
80+
// Finish when page render completes
81+
nuxtApp.hook('page:finish', () => {
82+
finishTransition?.()
83+
resetTransitionState()
84+
})
85+
})
86+
87+
/** Return true when navigating between `/` and `/search` (either direction). */
88+
function isSearchTransition(toPath: string, fromPath: string): boolean {
89+
const paths = new Set([toPath, fromPath])
90+
return paths.has('/') && paths.has('/search')
91+
}

modules/runtime/server/cache.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -710,12 +710,17 @@ export default defineNitroPlugin(nitroApp => {
710710
const originalFetch = globalThis.fetch
711711
const original$fetch = globalThis.$fetch
712712

713-
// Override native fetch for esm.sh requests
713+
// Override native fetch for esm.sh requests and to inject test fixture responses
714714
globalThis.fetch = async (input: URL | RequestInfo, init?: RequestInit): Promise<Response> => {
715715
const urlStr =
716716
typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url
717717

718-
if (urlStr.startsWith('/') || urlStr.includes('woff') || urlStr.includes('fonts')) {
718+
if (
719+
urlStr.startsWith('/') ||
720+
urlStr.startsWith('data:') ||
721+
urlStr.includes('woff') ||
722+
urlStr.includes('fonts')
723+
) {
719724
return await originalFetch(input, init)
720725
}
721726

nuxt.config.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,6 @@ export default defineNuxtConfig({
128128
entryImportMap: false,
129129
typescriptPlugin: true,
130130
viteEnvironmentApi: true,
131-
viewTransition: true,
132131
typedPages: true,
133132
},
134133

playwright.config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ export default defineConfig<ConfigOptions>({
1818
reuseExistingServer: false,
1919
timeout: 60_000,
2020
},
21+
// We currently only test on one browser on one platform
22+
snapshotPathTemplate: '{snapshotDir}/{testFileDir}/{testFileName}-snapshots/{arg}{ext}',
2123
use: {
2224
baseURL,
2325
trace: 'on-first-retry',

test/e2e/og-image.spec.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { expect, test } from './test-utils'
22

3+
// TODO(serhalp): The nuxt@3.20.2 fixture has no stars. Update fixture to have stars coverage here.
34
const paths = ['/', '/package/nuxt/v/3.20.2']
5+
46
for (const path of paths) {
57
test.describe(path, () => {
6-
test.skip(`og image for ${path}`, async ({ page, goto, baseURL }) => {
8+
test(`og image for ${path}`, async ({ page, goto, baseURL }) => {
79
await goto(path, { waitUntil: 'domcontentloaded' })
810

911
const ogImageUrl = await page.locator('meta[property="og:image"]').getAttribute('content')
@@ -19,7 +21,9 @@ for (const path of paths) {
1921
expect(response.headers()['content-type']).toContain('image/png')
2022

2123
const imageBuffer = await response.body()
22-
expect(imageBuffer).toMatchSnapshot({ name: `og-image-for-${path.replace(/\//g, '-')}.png` })
24+
expect(imageBuffer).toMatchSnapshot({
25+
name: `og-image-for-${path.replace(/\//g, '-')}.png`,
26+
})
2327
})
2428
})
2529
}
34.3 KB
Loading
31.3 KB
Loading

0 commit comments

Comments
 (0)