[READ-ONLY] a fast, modern browser for the npm registry
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

perf: use `shallowRef` and `useTemplateRef` (#476)

authored by

Robin and committed by
GitHub
ae781889 79371925

+77 -74
+1 -1
app/components/AppHeader.vue
··· 12 12 13 13 const router = useRouter() 14 14 15 - const showFullSearch = ref(false) 15 + const showFullSearch = shallowRef(false) 16 16 17 17 onKeyStroke(',', e => { 18 18 // Don't trigger if user is typing in an input
+1 -1
app/components/AuthButton.client.vue
··· 1 1 <script setup lang="ts"> 2 - const showModal = ref(false) 2 + const showModal = shallowRef(false) 3 3 const { user } = useAtproto() 4 4 </script> 5 5
+1 -1
app/components/AuthButton.vue
··· 1 1 <script setup lang="ts"> 2 - const showModal = ref(false) 2 + const showModal = shallowRef(false) 3 3 const { user } = useAtproto() 4 4 </script> 5 5
+1 -1
app/components/AuthModal.vue
··· 1 1 <script setup lang="ts"> 2 2 const open = defineModel<boolean>('open', { default: false }) 3 3 4 - const handleInput = ref('') 4 + const handleInput = shallowRef('') 5 5 6 6 const { user, logout } = useAtproto() 7 7
+1 -1
app/components/ColumnPicker.vue
··· 10 10 reset: [] 11 11 }>() 12 12 13 - const isOpen = ref(false) 13 + const isOpen = shallowRef(false) 14 14 const buttonRef = useTemplateRef('buttonRef') 15 15 const menuRef = useTemplateRef('menuRef') 16 16 const menuId = useId()
+2 -7
app/components/DependencyPathPopup.vue
··· 5 5 }>() 6 6 7 7 const isOpen = shallowRef(false) 8 - const popupEl = ref<HTMLElement | null>(null) 8 + const popupEl = useTemplateRef('popupEl') 9 9 const popupPosition = shallowRef<{ top: number; left: number } | null>(null) 10 - 11 - // Function ref - captures the element when popup mounts 12 - function setPopupRef(el: unknown) { 13 - popupEl.value = (el as HTMLElement) || null 14 - } 15 10 16 11 function closePopup() { 17 12 isOpen.value = false ··· 78 73 <!-- Tree popup --> 79 74 <div 80 75 v-if="isOpen" 81 - :ref="setPopupRef" 76 + ref="popupEl" 82 77 class="fixed z-[100] bg-bg-elevated border border-border rounded-lg shadow-xl p-3 min-w-64 max-w-sm" 83 78 :style="getPopupStyle()" 84 79 >
+2 -2
app/components/FilterPanel.vue
··· 27 27 'toggleKeyword': [keyword: string] 28 28 }>() 29 29 30 - const isExpanded = ref(false) 31 - const showAllKeywords = ref(false) 30 + const isExpanded = shallowRef(false) 31 + const showAllKeywords = shallowRef(false) 32 32 33 33 const displayedKeywords = computed(() => { 34 34 const keywords = props.availableKeywords ?? []
+3 -3
app/components/HeaderAccountMenu.client.vue
··· 10 10 11 11 const { user: atprotoUser } = useAtproto() 12 12 13 - const isOpen = ref(false) 14 - const showConnectorModal = ref(false) 15 - const showAuthModal = ref(false) 13 + const isOpen = shallowRef(false) 14 + const showConnectorModal = shallowRef(false) 15 + const showAuthModal = shallowRef(false) 16 16 17 17 /** Check if connected to at least one service */ 18 18 const hasAnyConnection = computed(() => isNpmConnected.value || !!atprotoUser.value)
+5 -5
app/components/HeaderOrgsDropdown.vue
··· 5 5 6 6 const { listUserOrgs } = useConnector() 7 7 8 - const isOpen = ref(false) 9 - const isLoading = ref(false) 10 - const orgs = ref<string[]>([]) 11 - const hasLoaded = ref(false) 12 - const error = ref<string | null>(null) 8 + const isOpen = shallowRef(false) 9 + const isLoading = shallowRef(false) 10 + const orgs = shallowRef<string[]>([]) 11 + const hasLoaded = shallowRef(false) 12 + const error = shallowRef<string | null>(null) 13 13 14 14 async function loadOrgs() { 15 15 if (hasLoaded.value || isLoading.value) return
+5 -5
app/components/HeaderPackagesDropdown.vue
··· 5 5 6 6 const { listUserPackages } = useConnector() 7 7 8 - const isOpen = ref(false) 9 - const isLoading = ref(false) 10 - const packages = ref<string[]>([]) 11 - const hasLoaded = ref(false) 12 - const error = ref<string | null>(null) 8 + const isOpen = shallowRef(false) 9 + const isLoading = shallowRef(false) 10 + const packages = shallowRef<string[]>([]) 11 + const hasLoaded = shallowRef(false) 12 + const error = shallowRef<string | null>(null) 13 13 14 14 async function loadPackages() { 15 15 if (hasLoaded.value || isLoading.value) return
+10 -10
app/components/PackageDownloadAnalytics.vue
··· 20 20 21 21 const { accentColors, selectedAccentColor } = useAccentColor() 22 22 const colorMode = useColorMode() 23 - const resolvedMode = ref<'light' | 'dark'>('light') 23 + const resolvedMode = shallowRef<'light' | 'dark'>('light') 24 24 const rootEl = shallowRef<HTMLElement | null>(null) 25 25 26 26 const { width } = useElementSize(rootEl) ··· 221 221 * - selectedGranularity: immediate UI 222 222 * - displayedGranularity: only updated once data is ready 223 223 */ 224 - const selectedGranularity = ref<ChartTimeGranularity>('weekly') 225 - const displayedGranularity = ref<ChartTimeGranularity>('weekly') 224 + const selectedGranularity = shallowRef<ChartTimeGranularity>('weekly') 225 + const displayedGranularity = shallowRef<ChartTimeGranularity>('weekly') 226 226 227 227 /** 228 228 * Date range inputs. ··· 230 230 * - weekly: from weeklyDownloads first -> weekStart/weekEnd 231 231 * - fallback: last 30 days ending yesterday (client-side) 232 232 */ 233 - const startDate = ref<string>('') // YYYY-MM-DD 234 - const endDate = ref<string>('') // YYYY-MM-DD 235 - const hasUserEditedDates = ref(false) 233 + const startDate = shallowRef<string>('') // YYYY-MM-DD 234 + const endDate = shallowRef<string>('') // YYYY-MM-DD 235 + const hasUserEditedDates = shallowRef(false) 236 236 237 237 function initDateRangeFromWeekly() { 238 238 if (hasUserEditedDates.value) return ··· 274 274 { immediate: true }, 275 275 ) 276 276 277 - const initialStartDate = ref<string>('') // YYYY-MM-DD 278 - const initialEndDate = ref<string>('') // YYYY-MM-DD 277 + const initialStartDate = shallowRef<string>('') // YYYY-MM-DD 278 + const initialEndDate = shallowRef<string>('') // YYYY-MM-DD 279 279 280 280 function setInitialRangeIfEmpty() { 281 281 if (initialStartDate.value || initialEndDate.value) return ··· 343 343 344 344 const { fetchPackageDownloadEvolution } = useCharts() 345 345 346 - const evolution = ref<EvolutionData>(weeklyDownloads) 347 - const pending = ref(false) 346 + const evolution = shallowRef<EvolutionData>(weeklyDownloads) 347 + const pending = shallowRef(false) 348 348 349 349 let lastRequestKey = '' 350 350 let requestToken = 0
+3 -3
app/components/PackageWeeklyDownloadStats.vue
··· 8 8 packageName: string 9 9 }>() 10 10 11 - const showModal = ref(false) 11 + const showModal = shallowRef(false) 12 12 13 13 const { data: packument } = usePackage(() => packageName) 14 14 const createdIso = computed(() => packument.value?.time?.created ?? null) ··· 19 19 20 20 const colorMode = useColorMode() 21 21 22 - const resolvedMode = ref<'light' | 'dark'>('light') 22 + const resolvedMode = shallowRef<'light' | 'dark'>('light') 23 23 24 24 const rootEl = shallowRef<HTMLElement | null>(null) 25 25 ··· 78 78 return isDarkMode.value ? accent.value : lightenOklch(accent.value, 0.5) 79 79 }) 80 80 81 - const weeklyDownloads = ref<WeeklyDownloadPoint[]>([]) 81 + const weeklyDownloads = shallowRef<WeeklyDownloadPoint[]>([]) 82 82 83 83 async function loadWeeklyDownloads() { 84 84 if (!import.meta.client) return
+4 -2
app/components/SearchBox.vue
··· 17 17 const router = useRouter() 18 18 const route = useRoute() 19 19 20 - const isSearchFocused = ref(false) 20 + const isSearchFocused = shallowRef(false) 21 21 22 22 const showSearchBar = computed(() => { 23 23 return route.name !== 'index' 24 24 }) 25 25 26 26 // Local input value (updates immediately as user types) 27 - const searchQuery = ref((route.query.q as string) ?? '') 27 + const searchQuery = shallowRef( 28 + (Array.isArray(route.query.q) ? route.query.q[0] : route.query.q) ?? '', 29 + ) 28 30 29 31 // Pages that have their own local filter using ?q 30 32 const pagesWithLocalFilter = new Set(['~username', 'org'])
+1 -1
app/components/TranslationHelper.vue
··· 7 7 8 8 // Show first N missing keys by default 9 9 const INITIAL_SHOW_COUNT = 5 10 - const showAll = ref(false) 10 + const showAll = shallowRef(false) 11 11 12 12 const missingKeysToShow = computed(() => { 13 13 if (showAll.value || props.status.missingKeys.length <= INITIAL_SHOW_COUNT) {
+4 -4
app/components/VersionSelector.vue
··· 21 21 urlPattern: string 22 22 }>() 23 23 24 - const isOpen = ref(false) 24 + const isOpen = shallowRef(false) 25 25 const dropdownRef = useTemplateRef('dropdownRef') 26 26 const listboxRef = useTemplateRef('listboxRef') 27 - const focusedIndex = ref(-1) 27 + const focusedIndex = shallowRef(-1) 28 28 29 29 onClickOutside(dropdownRef, () => { 30 30 isOpen.value = false ··· 57 57 const versionGroups = ref<VersionGroup[]>([]) 58 58 59 59 /** Whether we've loaded all versions from the API */ 60 - const hasLoadedAll = ref(false) 60 + const hasLoadedAll = shallowRef(false) 61 61 62 62 /** Loading state for initial all-versions fetch */ 63 - const isLoadingAll = ref(false) 63 + const isLoadingAll = shallowRef(false) 64 64 65 65 /** Cached full version list */ 66 66 const allVersionsCache = shallowRef<PackageVersionInfo[] | null>(null)
+1 -1
app/composables/useNpmRegistry.ts
··· 299 299 total: number 300 300 } | null>(null) 301 301 302 - const isLoadingMore = ref(false) 302 + const isLoadingMore = shallowRef(false) 303 303 304 304 // Standard (non-incremental) search implementation 305 305 let lastSearch: NpmSearchResponse | undefined = undefined
+1 -1
app/composables/usePreferencesProvider.ts
··· 62 62 export function usePreferencesProvider<T>(defaultValue: T) { 63 63 const provider = createLocalStorageProvider<T>(STORAGE_KEY) 64 64 const data = ref<T>(defaultValue) as Ref<T> 65 - const isHydrated = ref(false) 65 + const isHydrated = shallowRef(false) 66 66 67 67 // Load from storage on client 68 68 onMounted(() => {
+1 -1
app/composables/useStructuredFilters.ts
··· 123 123 }) 124 124 125 125 // Sort state 126 - const sortOption = ref<SortOption>(initialSort ?? 'updated-desc') 126 + const sortOption = shallowRef<SortOption>(initialSort ?? 'updated-desc') 127 127 128 128 // Available keywords extracted from all packages 129 129 const availableKeywords = computed(() => {
+2 -2
app/pages/@[org].vue
··· 58 58 }) 59 59 60 60 // Pagination state 61 - const currentPage = ref(1) 61 + const currentPage = shallowRef(1) 62 62 63 63 // Calculate total pages 64 64 const totalPages = computed(() => { ··· 114 114 setSort(option) 115 115 } 116 116 117 - const activeTab = ref<'members' | 'teams'>('members') 117 + const activeTab = shallowRef<'members' | 'teams'>('members') 118 118 119 119 // Canonical URL for this org page 120 120 const canonicalUrl = computed(() => `https://npmx.dev/@${orgName.value}`)
+3 -3
app/pages/code/[...path].vue
··· 104 104 ) 105 105 106 106 // Track hash manually since we update it via history API to avoid scroll 107 - const currentHash = ref('') 107 + const currentHash = shallowRef('') 108 108 109 109 onMounted(() => { 110 110 currentHash.value = window.location.hash ··· 136 136 }) 137 137 138 138 // Scroll to selected line only on initial load or file change (not on click) 139 - const shouldScrollOnHashChange = ref(true) 139 + const shouldScrollOnHashChange = shallowRef(true) 140 140 141 141 function scrollToLine() { 142 142 if (!shouldScrollOnHashChange.value) return ··· 256 256 }, 257 257 ] as const 258 258 259 - const markdownViewMode = ref<(typeof markdownViewModes)[number]['key']>('preview') 259 + const markdownViewMode = shallowRef<(typeof markdownViewModes)[number]['key']>('preview') 260 260 261 261 useHead({ 262 262 link: [{ rel: 'canonical', href: canonicalUrl }],
+1 -1
app/pages/index.vue
··· 2 2 import { debounce } from 'perfect-debounce' 3 3 4 4 const router = useRouter() 5 - const searchQuery = ref('') 5 + const searchQuery = shallowRef('') 6 6 const searchInputRef = useTemplateRef('searchInputRef') 7 7 const { focused: isSearchFocused } = useFocus(searchInputRef) 8 8
+8 -8
app/pages/search.vue
··· 32 32 // The actual search query (from URL, used for API calls) 33 33 const query = computed(() => (route.query.q as string) ?? '') 34 34 35 - const selectedIndex = ref(0) 35 + const selectedIndex = shallowRef(0) 36 36 const packageListRef = useTemplateRef('packageListRef') 37 37 38 38 // Track if page just loaded (for hiding "Searching..." during view transition) 39 - const hasInteracted = ref(false) 39 + const hasInteracted = shallowRef(false) 40 40 onMounted(() => { 41 41 // Small delay to let view transition complete 42 42 setTimeout(() => { ··· 46 46 47 47 // Infinite scroll / pagination state 48 48 const pageSize = 25 49 - const currentPage = ref(1) 49 + const currentPage = shallowRef(1) 50 50 51 51 // Calculate how many results we need based on current page and preferred page size 52 52 const requestedSize = computed(() => { ··· 230 230 const isValidPackageName = computed(() => isValidNewPackageName(query.value.trim())) 231 231 232 232 // Check if package name is available (doesn't exist on npm) 233 - const packageAvailability = ref<{ name: string; available: boolean } | null>(null) 233 + const packageAvailability = shallowRef<{ name: string; available: boolean } | null>(null) 234 234 235 235 // Debounced check for package availability 236 236 const checkAvailability = debounce(async (name: string) => { ··· 325 325 }) 326 326 327 327 // Modal state for claiming a package 328 - const claimModalOpen = ref(false) 328 + const claimModalOpen = shallowRef(false) 329 329 330 330 /** 331 331 * Check if a string is a valid npm username/org name ··· 452 452 453 453 /** Validated suggestions (only those that exist) */ 454 454 const validatedSuggestions = ref<ValidatedSuggestion[]>([]) 455 - const suggestionsLoading = ref(false) 455 + const suggestionsLoading = shallowRef(false) 456 456 457 457 /** Debounced function to validate suggestions */ 458 458 const validateSuggestions = debounce(async (parsed: ParsedQuery) => { ··· 551 551 const totalSelectableCount = computed(() => suggestionCount.value + resultCount.value) 552 552 553 553 /** Unified selected index: negative for suggestions, 0+ for packages */ 554 - const unifiedSelectedIndex = ref(0) 555 - const userHasNavigated = ref(false) 554 + const unifiedSelectedIndex = shallowRef(0) 555 + const userHasNavigated = shallowRef(false) 556 556 557 557 /** Convert unified index to suggestion index (0-based) or null */ 558 558 function toSuggestionIndex(unified: number): number | null {
+9 -4
app/pages/~[username]/index.vue
··· 10 10 // Infinite scroll state 11 11 const pageSize = 50 12 12 const maxResults = 250 // npm API hard limit 13 - const currentPage = ref(1) 13 + const currentPage = shallowRef(1) 14 14 15 15 // Get initial page from URL (for scroll restoration on reload) 16 16 const initialPage = computed(() => { ··· 33 33 type SortOption = 'downloads' | 'updated' | 'name-asc' | 'name-desc' 34 34 35 35 // Filter and sort state (from URL) 36 - const filterText = ref((route.query.q as string) ?? '') 37 - const sortOption = ref<SortOption>((route.query.sort as SortOption) || 'downloads') 36 + const filterText = shallowRef( 37 + (Array.isArray(route.query.q) ? route.query.q[0] : route.query.q) ?? '', 38 + ) 39 + const sortOption = shallowRef<SortOption>( 40 + ((Array.isArray(route.query.sort) ? route.query.sort[0] : route.query.sort) as SortOption) || 41 + 'downloads', 42 + ) 38 43 39 44 // Track if we've loaded all results (one-way flag, doesn't reset) 40 45 // Initialize to true if URL already has filter/sort params 41 - const hasLoadedAll = ref( 46 + const hasLoadedAll = shallowRef( 42 47 Boolean(route.query.q) || (route.query.sort && route.query.sort !== 'downloads'), 43 48 ) 44 49
+3 -3
app/pages/~[username]/orgs.vue
··· 17 17 isLoadingDetails: boolean 18 18 } 19 19 20 - const isLoading = ref(true) 21 - const orgs = ref<OrgInfo[]>([]) 22 - const error = ref<string | null>(null) 20 + const isLoading = shallowRef(true) 21 + const orgs = shallowRef<OrgInfo[]>([]) 22 + const error = shallowRef<string | null>(null) 23 23 24 24 async function loadOrgDetails(org: OrgInfo) { 25 25 org.isLoadingDetails = true
+1
modules/dev.ts
··· 18 18 existsSync(envPath) && /^NUXT_SESSION_PASSWORD=/m.test(readFileSync(envPath, 'utf-8')) 19 19 20 20 if (!hasPassword) { 21 + // eslint-disable-next-line no-console 21 22 console.info('Generating NUXT_SESSION_PASSWORD for development environment.') 22 23 const password = randomUUID().replace(/-/g, '') 23 24
+1 -1
test/nuxt/components/DateTime.spec.ts
··· 3 3 import DateTime from '~/components/DateTime.vue' 4 4 5 5 // Mock the useRelativeDates composable 6 - const mockRelativeDates = ref(false) 6 + const mockRelativeDates = shallowRef(false) 7 7 vi.mock('~/composables/useSettings', () => ({ 8 8 useRelativeDates: () => mockRelativeDates, 9 9 useSettings: () => ({
+2 -2
test/nuxt/composables/use-install-command.spec.ts
··· 247 247 }) 248 248 249 249 it('should update when using ref values', () => { 250 - const packageName = ref<string | null>('vue') 251 - const version = ref<string | null>(null) 250 + const packageName = shallowRef<string | null>('vue') 251 + const version = shallowRef<string | null>(null) 252 252 253 253 const { installCommand } = useInstallCommand(packageName, version, null, null) 254 254