1
- import type { Query } from '@prismicio/client'
2
- import type { BlogPageDocument , PageDocument } from '~~/prismicio-types'
3
- import { filter } from '@prismicio/client'
4
- import { $fetch } from 'ofetch'
1
+ import type { PrerenderPagesOptions } from '../shared/services/prismic-data'
2
+ import { getPrismicData } from '../shared/services/prismic-data'
5
3
import { getBlogMetadata } from '../shared/utils/blog-post'
6
- import { analyzeImageSync , cleanupOrphanedImages , extractImageUrlsFromDocument , logImageSyncStatus } from '../shared/utils/prismic-images'
7
- import { repositoryName } from '../slicemachine.config.json'
8
4
9
- interface PrerenderPagesOptions {
10
- prismicAccessToken : string
11
- showDrafts ?: boolean
12
- }
13
-
14
- // Global collections for crawled data
15
5
const blogPosts : Post [ ] = [ ]
16
- const allImageUrls : string [ ] = [ ]
17
6
18
- // These pages are excluded from the prerender process
7
+ // Pages excluded from prerendering
19
8
export const EXCLUDED_PAGES = [
20
9
// Custom apps built by other projects
21
10
'/vote' ,
@@ -30,41 +19,42 @@ export const EXCLUDED_PAGES = [
30
19
]
31
20
32
21
export async function getDynamicPages ( options : PrerenderPagesOptions ) {
33
- const pagesUrl = await buildPrismicUrl ( 'page' , options )
34
- const pages = await getPages ( pagesUrl )
22
+ const data = await getPrismicData ( options )
23
+
24
+ const pages = data . pages . map ( ( { uid } ) => {
25
+ if ( uid === 'home' )
26
+ return '/'
27
+ return `/${ uid } `
28
+ } )
29
+
30
+ const blogArticles = data . blogPosts . map ( post => `/blog/${ post . uid } ` )
35
31
36
- const blogPostsUrl = await buildPrismicUrl ( 'blog_page' , options )
37
- const blogArticles = await getBlogPosts ( blogPostsUrl ) . then ( posts => posts . map ( post => `/blog/${ post . slug } ` ) )
32
+ const blogPaginationRoutes = generateBlogPaginationRoutes ( data . blogPosts . length )
38
33
39
- const blogPaginationRoutes = await getBlogPaginationRoutes ( blogPostsUrl )
34
+ blogPosts . length = 0
35
+ data . blogPosts . forEach ( ( post ) => {
36
+ const { titleText : title , url, abstract : description , prose : content , date, imageURL : image , authors } = getBlogMetadata ( post )
37
+ blogPosts . push ( { title, url, description, content, date, image, slug : post . uid , authors } )
38
+ } )
40
39
41
40
return [ ...pages , ...blogArticles , ...blogPaginationRoutes ] . filter ( page => ! EXCLUDED_PAGES . includes ( page ) )
42
41
}
43
42
44
- async function getPages ( url : URL ) {
45
- const prerenderPaths : string [ ] = [ ]
46
- let page : number = 1
47
- while ( true ) {
48
- url . searchParams . set ( 'page' , page . toString ( ) )
49
- const { next_page, results } = await $fetch < Query < PageDocument > > ( url . href )
50
- prerenderPaths . push ( ...results . map ( ( { uid } ) => {
51
- if ( uid === 'home' )
52
- return '/'
53
- return `/${ uid } `
54
- } ) )
55
-
56
- // Extract images from page documents
57
- results . forEach ( ( result ) => {
58
- const documentImages = extractImageUrlsFromDocument ( result )
59
- allImageUrls . push ( ...documentImages )
60
- } )
43
+ function generateBlogPaginationRoutes ( totalPosts : number ) : string [ ] {
44
+ const postsPerPage = 30
45
+ const totalPages = Math . ceil ( totalPosts / postsPerPage )
46
+ const paginationRoutes : string [ ] = [ ]
61
47
62
- if ( next_page === null )
63
- break
64
- page ++
48
+ for ( let i = 1 ; i <= totalPages ; i ++ ) {
49
+ if ( i === 1 ) {
50
+ paginationRoutes . push ( '/blog' )
51
+ }
52
+ else {
53
+ paginationRoutes . push ( `/blog?page=${ i } ` )
54
+ }
65
55
}
66
56
67
- return prerenderPaths
57
+ return paginationRoutes
68
58
}
69
59
70
60
interface Post {
@@ -78,101 +68,12 @@ interface Post {
78
68
authors ?: string [ ]
79
69
}
80
70
81
- export async function getBlogPosts ( url : URL ) {
82
- if ( blogPosts . length > 0 )
71
+ export async function getBlogPosts ( ) : Promise < Post [ ] > {
72
+ if ( blogPosts . length > 0 ) {
83
73
return blogPosts
84
- let page : number = 1
85
- while ( true ) {
86
- url . searchParams . set ( 'page' , page . toString ( ) )
87
- const { next_page, results } = await $fetch < Query < BlogPageDocument > > ( url . href )
88
- results . forEach ( ( result ) => {
89
- const { titleText : title , url, abstract : description , prose : content , date, imageURL : image , authors } = getBlogMetadata ( result )
90
- blogPosts . push ( { title, url, description, content, date, image, slug : result . uid , authors } )
91
-
92
- // Extract all image URLs from the document
93
- const documentImages = extractImageUrlsFromDocument ( result )
94
- allImageUrls . push ( ...documentImages )
95
- } )
96
-
97
- if ( next_page === null )
98
- break
99
- page ++
100
74
}
101
75
76
+ // Should not happen if getDynamicPages was called first
77
+ console . warn ( '⚠️ getBlogPosts called before getDynamicPages - this may cause duplicate API calls' )
102
78
return blogPosts
103
79
}
104
-
105
- export async function getBlogPaginationRoutes ( url : URL ) {
106
- const paginationRoutes : string [ ] = [ ]
107
-
108
- // Get the first page to determine total number of posts
109
- url . searchParams . set ( 'page' , '1' )
110
- const firstPageResult = await $fetch < Query < BlogPageDocument > > ( url . href )
111
- const totalPages = firstPageResult . total_pages || 1
112
-
113
- // Generate pagination routes
114
- for ( let i = 1 ; i <= totalPages ; i ++ ) {
115
- if ( i === 1 ) {
116
- // First page is just /blog
117
- paginationRoutes . push ( '/blog' )
118
- }
119
- else {
120
- // Other pages are /blog?page=2, /blog?page=3, etc.
121
- paginationRoutes . push ( `/blog?page=${ i } ` )
122
- }
123
- }
124
-
125
- return paginationRoutes
126
- }
127
-
128
- const prismicUrl = new URL ( `https://${ repositoryName } .cdn.prismic.io` )
129
-
130
- interface RefsResponse { refs : { id : 'master' , ref : string } [ ] }
131
- let ref : string
132
-
133
- export async function buildPrismicUrl ( documentType : 'blog_page' | 'page' , { prismicAccessToken, showDrafts = false } : PrerenderPagesOptions ) {
134
- if ( ! ref ) {
135
- const refsUrl = new URL ( '/api/v2' , prismicUrl )
136
- refsUrl . searchParams . set ( 'access_token' , prismicAccessToken )
137
-
138
- const refsResponse = await $fetch < RefsResponse > ( refsUrl . href )
139
- const _ref = refsResponse ?. refs . find ( ( { id } ) => id === 'master' ) ?. ref
140
- if ( ! _ref )
141
- throw new Error ( 'Could not find master ref' )
142
- ref = _ref
143
- }
144
-
145
- const searchUrl = new URL ( '/api/v2/documents/search' , prismicUrl )
146
-
147
- // Add routes for blog posts
148
- const documentTypeQuery = `[at(document.type,"${ documentType } ")]`
149
-
150
- // Apply the draft filter only when we don't want to show drafts
151
- let filtering = ''
152
- if ( ! showDrafts )
153
- filtering = filter . not ( `my.${ documentType } .draft` , true )
154
-
155
- searchUrl . searchParams . set ( 'q' , `[${ documentTypeQuery } ${ filtering } ]` )
156
- searchUrl . searchParams . set ( 'pageSize' , '100' ) // 100 is the maximum
157
- searchUrl . searchParams . set ( 'ref' , ref ! )
158
- searchUrl . searchParams . set ( 'access_token' , prismicAccessToken )
159
-
160
- return searchUrl
161
- }
162
-
163
- export function getAllImageUrls ( ) : string [ ] {
164
- return [ ...new Set ( allImageUrls ) ]
165
- }
166
-
167
- export async function performImageAnalysis ( ) : Promise < void > {
168
- const imageUrls = getAllImageUrls ( )
169
- console . warn ( `\n🔍 Analyzing ${ imageUrls . length } unique Prismic images...` )
170
-
171
- const status = await analyzeImageSync ( imageUrls )
172
- logImageSyncStatus ( status )
173
-
174
- if ( status . orphaned . length > 0 ) {
175
- console . warn ( '\n🧹 Cleaning up orphaned images...' )
176
- await cleanupOrphanedImages ( status )
177
- }
178
- }
0 commit comments