@@ -14,15 +14,45 @@ import { validTags } from '@content/_tagList'
1414
1515const pattern = '**\/[^_]*.{md,mdx}'
1616
17+ const MAX_BREADCRUMB_TITLE_LENGTH = 50
18+ const loggedBreadcrumbTitleWarnings = new Set < string > ( )
19+
20+ const warnOnBreadcrumbTitleLength = ( title : string , collectionName : string ) : void => {
21+ if ( ( process . env [ 'NODE_ENV' ] ?? 'development' ) !== 'production' ) return
22+ if ( title . length <= MAX_BREADCRUMB_TITLE_LENGTH ) return
23+
24+ const warningKey = `${ collectionName } :${ title } `
25+ if ( loggedBreadcrumbTitleWarnings . has ( warningKey ) ) return
26+
27+ loggedBreadcrumbTitleWarnings . add ( warningKey )
28+ console . warn (
29+ `[Breadcrumb Warning] ${ collectionName } title "${ title } " is ${ title . length } characters long. Titles longer than ${ MAX_BREADCRUMB_TITLE_LENGTH } characters will truncate in breadcrumbs.`
30+ )
31+ }
32+
33+ const withBreadcrumbTitleWarning = < T extends z . ZodRawShape > (
34+ schema : z . ZodObject < T > ,
35+ collectionName : string
36+ ) =>
37+ schema . superRefine ( data => {
38+ const candidateTitle = ( data as { title ?: string } ) . title
39+ if ( typeof candidateTitle === 'string' ) {
40+ warnOnBreadcrumbTitleLength ( candidateTitle , collectionName )
41+ }
42+ } )
43+
1744// export type AboutSchema = z.infer<typeof aboutSchema>
1845
1946/**
2047 * About
2148 */
22- const aboutSchema = z . object ( {
23- id : z . string ( ) ,
24- title : z . string ( ) ,
25- } )
49+ const aboutSchema = withBreadcrumbTitleWarning (
50+ z . object ( {
51+ id : z . string ( ) ,
52+ title : z . string ( ) ,
53+ } ) ,
54+ 'about'
55+ )
2656
2757const aboutCollection = defineCollection ( {
2858 loader : glob ( { pattern, base : './src/content/about' } ) ,
@@ -32,22 +62,25 @@ const aboutCollection = defineCollection({
3262/**
3363 * Articles
3464 */
35- const articlesSchema = z . object ( {
36- title : z . string ( ) ,
37- description : z . string ( ) ,
38- // Reference a single author from the `authors` collection by `id`
39- author : reference ( 'authors' ) ,
40- tags : z . array ( z . enum ( validTags ) ) ,
41- image : z . object ( {
42- src : z . string ( ) ,
43- alt : z . string ( ) ,
65+ const articlesSchema = withBreadcrumbTitleWarning (
66+ z . object ( {
67+ title : z . string ( ) ,
68+ description : z . string ( ) ,
69+ // Reference a single author from the `authors` collection by `id`
70+ author : reference ( 'authors' ) ,
71+ tags : z . array ( z . enum ( validTags ) ) ,
72+ image : z . object ( {
73+ src : z . string ( ) ,
74+ alt : z . string ( ) ,
75+ } ) ,
76+ // In YAML, dates written without quotes around them are interpreted as Date objects
77+ publishDate : z . date ( ) ,
78+ isDraft : z . boolean ( ) . default ( false ) ,
79+ featured : z . boolean ( ) . default ( false ) ,
80+ readingTime : z . string ( ) . optional ( ) ,
4481 } ) ,
45- // In YAML, dates written without quotes around them are interpreted as Date objects
46- publishDate : z . date ( ) ,
47- isDraft : z . boolean ( ) . default ( false ) ,
48- featured : z . boolean ( ) . default ( false ) ,
49- readingTime : z . string ( ) . optional ( ) ,
50- } )
82+ 'articles'
83+ )
5184
5285const articlesCollection = defineCollection ( {
5386 loader : glob ( { pattern, base : './src/content/articles' } ) ,
@@ -86,30 +119,33 @@ const authorsCollection = defineCollection({
86119/**
87120 * Case Studies
88121 */
89- const caseStudiesSchema = z . object ( {
90- title : z . string ( ) ,
91- description : z . string ( ) . optional ( ) ,
92- tags : z . array ( z . enum ( validTags ) ) ,
93- // In YAML, dates written without quotes around them are interpreted as Date objects
94- publishDate : z . date ( ) ,
95- isDraft : z . boolean ( ) . default ( false ) ,
96- featured : z . boolean ( ) . default ( false ) ,
97- // Optional fields that may exist in some case studies
98- image : z
99- . union ( [
100- z . string ( ) ,
101- z . object ( {
102- src : z . string ( ) ,
103- alt : z . string ( ) ,
104- } ) ,
105- ] )
106- . optional ( ) ,
107- client : z . string ( ) . optional ( ) ,
108- author : reference ( 'authors' ) . optional ( ) ,
109- industry : z . string ( ) . optional ( ) ,
110- projectType : z . string ( ) . optional ( ) ,
111- duration : z . string ( ) . optional ( ) ,
112- } )
122+ const caseStudiesSchema = withBreadcrumbTitleWarning (
123+ z . object ( {
124+ title : z . string ( ) ,
125+ description : z . string ( ) . optional ( ) ,
126+ tags : z . array ( z . enum ( validTags ) ) ,
127+ // In YAML, dates written without quotes around them are interpreted as Date objects
128+ publishDate : z . date ( ) ,
129+ isDraft : z . boolean ( ) . default ( false ) ,
130+ featured : z . boolean ( ) . default ( false ) ,
131+ // Optional fields that may exist in some case studies
132+ image : z
133+ . union ( [
134+ z . string ( ) ,
135+ z . object ( {
136+ src : z . string ( ) ,
137+ alt : z . string ( ) ,
138+ } ) ,
139+ ] )
140+ . optional ( ) ,
141+ client : z . string ( ) . optional ( ) ,
142+ author : reference ( 'authors' ) . optional ( ) ,
143+ industry : z . string ( ) . optional ( ) ,
144+ projectType : z . string ( ) . optional ( ) ,
145+ duration : z . string ( ) . optional ( ) ,
146+ } ) ,
147+ 'caseStudies'
148+ )
113149
114150const caseStudiesCollection = defineCollection ( {
115151 loader : glob ( { pattern, base : './src/content/case-studies' } ) ,
@@ -147,20 +183,23 @@ const contactDataCollection = defineCollection({
147183/**
148184 * Services
149185 */
150- const servicesSchema = z . object ( {
151- title : z . string ( ) ,
152- description : z . string ( ) . optional ( ) ,
153- tags : z . array ( z . enum ( validTags ) ) ,
154- // In YAML, dates written without quotes around them are interpreted as Date objects
155- publishDate : z . date ( ) ,
156- isDraft : z . boolean ( ) . default ( false ) ,
157- category : z . string ( ) . optional ( ) ,
158- icon : z . string ( ) . optional ( ) ,
159- featured : z . boolean ( ) . default ( false ) ,
160- pricing : z . string ( ) . optional ( ) ,
161- duration : z . string ( ) . optional ( ) ,
162- deliverables : z . array ( z . string ( ) ) . optional ( ) ,
163- } )
186+ const servicesSchema = withBreadcrumbTitleWarning (
187+ z . object ( {
188+ title : z . string ( ) ,
189+ description : z . string ( ) . optional ( ) ,
190+ tags : z . array ( z . enum ( validTags ) ) ,
191+ // In YAML, dates written without quotes around them are interpreted as Date objects
192+ publishDate : z . date ( ) ,
193+ isDraft : z . boolean ( ) . default ( false ) ,
194+ category : z . string ( ) . optional ( ) ,
195+ icon : z . string ( ) . optional ( ) ,
196+ featured : z . boolean ( ) . default ( false ) ,
197+ pricing : z . string ( ) . optional ( ) ,
198+ duration : z . string ( ) . optional ( ) ,
199+ deliverables : z . array ( z . string ( ) ) . optional ( ) ,
200+ } ) ,
201+ 'services'
202+ )
164203
165204const servicesCollection = defineCollection ( {
166205 loader : glob ( { pattern, base : './src/content/services' } ) ,
@@ -202,24 +241,27 @@ const testimonialCollection = defineCollection({
202241/**
203242 * Downloads
204243 */
205- const downloadsSchema = z . object ( {
206- title : z . string ( ) ,
207- description : z . string ( ) ,
208- author : reference ( 'authors' ) . optional ( ) ,
209- tags : z . array ( z . enum ( validTags ) ) ,
210- image : z . object ( {
211- src : z . string ( ) ,
212- alt : z . string ( ) ,
244+ const downloadsSchema = withBreadcrumbTitleWarning (
245+ z . object ( {
246+ title : z . string ( ) ,
247+ description : z . string ( ) ,
248+ author : reference ( 'authors' ) . optional ( ) ,
249+ tags : z . array ( z . enum ( validTags ) ) ,
250+ image : z . object ( {
251+ src : z . string ( ) ,
252+ alt : z . string ( ) ,
253+ } ) ,
254+ publishDate : z . date ( ) ,
255+ isDraft : z . boolean ( ) . default ( false ) ,
256+ featured : z . boolean ( ) . default ( false ) ,
257+ fileType : z . enum ( [ 'PDF' , 'eBook' , 'Whitepaper' , 'Guide' , 'Report' , 'Template' ] ) ,
258+ fileSize : z . string ( ) . optional ( ) ,
259+ pages : z . number ( ) . optional ( ) ,
260+ readingTime : z . string ( ) . optional ( ) ,
261+ fileName : z . string ( ) , // Filename in public/downloads directory
213262 } ) ,
214- publishDate : z . date ( ) ,
215- isDraft : z . boolean ( ) . default ( false ) ,
216- featured : z . boolean ( ) . default ( false ) ,
217- fileType : z . enum ( [ 'PDF' , 'eBook' , 'Whitepaper' , 'Guide' , 'Report' , 'Template' ] ) ,
218- fileSize : z . string ( ) . optional ( ) ,
219- pages : z . number ( ) . optional ( ) ,
220- readingTime : z . string ( ) . optional ( ) ,
221- fileName : z . string ( ) , // Filename in public/downloads directory
222- } )
263+ 'downloads'
264+ )
223265
224266const downloadsCollection = defineCollection ( {
225267 loader : glob ( { pattern, base : './src/content/downloads' } ) ,
0 commit comments