@@ -7,14 +7,17 @@ import {
7
7
defineNuxtModule ,
8
8
getNuxtModuleVersion ,
9
9
hasNuxtModule ,
10
- hasNuxtModuleCompatibility ,
10
+ hasNuxtModuleCompatibility , resolveModule ,
11
11
useLogger ,
12
12
} from '@nuxt/kit'
13
13
import { joinURL , withBase , withLeadingSlash , withoutLeadingSlash , withoutTrailingSlash } from 'ufo'
14
14
import { installNuxtSiteConfig } from 'nuxt-site-config/kit'
15
15
import { defu } from 'defu'
16
16
import type { NitroRouteConfig } from 'nitropack'
17
17
import { readPackageJSON } from 'pkg-types'
18
+ import { dirname } from 'pathe'
19
+ import type { FileAfterParseHook } from '@nuxt/content'
20
+ import type { UseSeoMetaInput } from '@unhead/schema'
18
21
import type {
19
22
AppSourceContext ,
20
23
AutoI18nConfig ,
@@ -24,7 +27,7 @@ import type {
24
27
SitemapSourceBase ,
25
28
SitemapSourceInput ,
26
29
SitemapSourceResolved ,
27
- ModuleOptions as _ModuleOptions , FilterInput , I18nIntegrationOptions ,
30
+ ModuleOptions as _ModuleOptions , FilterInput , I18nIntegrationOptions , SitemapUrl ,
28
31
} from './runtime/types'
29
32
import { convertNuxtPagesToSitemapEntries , generateExtraRoutesFromNuxtConfig , resolveUrls } from './util/nuxtSitemap'
30
33
import { createNitroPromise , createPagesPromise , extendTypes , getNuxtModuleOptions , resolveNitroPreset } from './util/kit'
@@ -41,6 +44,8 @@ import { normalizeFilters } from './util/filter'
41
44
// eslint-disable-next-line
42
45
export interface ModuleOptions extends _ModuleOptions { }
43
46
47
+ export * from './content'
48
+
44
49
export default defineNuxtModule < ModuleOptions > ( {
45
50
meta : {
46
51
name : '@nuxtjs/sitemap' ,
@@ -352,34 +357,112 @@ declare module 'vue-router' {
352
357
353
358
// @ts -expect-error untyped
354
359
const isNuxtContentDocumentDriven = ( ! ! nuxt . options . content ?. documentDriven || config . strictNuxtContentPaths )
355
- if ( hasNuxtModule ( '@nuxt/content' ) ) {
356
- if ( await hasNuxtModuleCompatibility ( '@nuxt/content' , '^3' ) ) {
357
- logger . warn ( 'Nuxt Sitemap does not work with Nuxt Content v3 yet, the integration will be disabled.' )
358
- }
359
- else {
360
- addServerPlugin ( resolve ( './runtime/server/plugins/nuxt-content' ) )
361
- addServerHandler ( {
362
- route : '/__sitemap__/nuxt-content-urls.json' ,
363
- handler : resolve ( './runtime/server/routes/__sitemap__/nuxt-content-urls' ) ,
364
- } )
365
- const tips : string [ ] = [ ]
366
- // @ts -expect-error untyped
367
- if ( nuxt . options . content ?. documentDriven )
368
- tips . push ( 'Enabled because you\'re using `@nuxt/content` with `documentDriven: true`.' )
369
- else if ( config . strictNuxtContentPaths )
370
- tips . push ( 'Enabled because you\'ve set `config.strictNuxtContentPaths: true`.' )
371
- else
372
- tips . push ( 'You can provide a `sitemap` key in your markdown frontmatter to configure specific URLs. Make sure you include a `loc`.' )
360
+ const usingNuxtContent = hasNuxtModule ( '@nuxt/content' )
361
+ const isNuxtContentV3 = usingNuxtContent && await hasNuxtModuleCompatibility ( '@nuxt/content' , '^3' )
362
+ const nuxtV3Collections = new Set < string > ( )
363
+ const isNuxtContentV2 = usingNuxtContent && await hasNuxtModuleCompatibility ( '@nuxt/content' , '^2' )
364
+ if ( isNuxtContentV3 ) {
365
+ // TODO this is a hack until content gives us an alias
366
+ nuxt . options . alias [ '#sitemap/content-v3-nitro-path' ] = resolve ( dirname ( resolveModule ( '@nuxt/content' ) ) , 'runtime/nitro' )
367
+ // @ts -expect-error runtime type
368
+ nuxt . hooks . hook ( 'content:file:afterParse' , ( ctx : FileAfterParseHook ) => {
369
+ const content = ctx . content as {
370
+ body : { value : [ string , Record < string , any > ] [ ] }
371
+ sitemap ?: Partial < SitemapUrl >
372
+ path : string
373
+ seo : UseSeoMetaInput
374
+ updatedAt ?: string
375
+ }
376
+ nuxtV3Collections . add ( ctx . collection . name )
377
+ if ( ! ( 'sitemap' in ctx . collection . fields ) ) {
378
+ return
379
+ }
380
+ // add any top level images
381
+ const images : SitemapUrl [ 'images' ] = [ ]
382
+ if ( config . discoverImages ) {
383
+ images . push ( ...( content . body . value
384
+ ?. filter ( c =>
385
+ [ 'image' , 'img' , 'nuxtimg' , 'nuxt-img' ] . includes ( c [ 0 ] ) ,
386
+ )
387
+ . map ( c => ( { loc : c [ 1 ] . src } ) ) || [ ] ) ,
388
+ )
389
+ }
373
390
374
- appGlobalSources . push ( {
375
- context : {
376
- name : '@nuxt/content:urls' ,
377
- description : 'Generated from your markdown files.' ,
378
- tips,
379
- } ,
380
- fetch : '/__sitemap__/nuxt-content-urls.json' ,
381
- } )
391
+ // add any top level videos
392
+ const videos : SitemapUrl [ 'videos' ] = [ ]
393
+ if ( config . discoverVideos ) {
394
+ // TODO
395
+ // videos.push(...(content.body.value
396
+ // .filter(c => c[0] === 'video' && c[1]?.src)
397
+ // .map(c => ({
398
+ // content_loc: c[1].src
399
+ // })) || []),
400
+ // )
401
+ }
402
+
403
+ const sitemapConfig = typeof content . sitemap === 'object' ? content . sitemap : { }
404
+ const lastmod = content . seo ?. articleModifiedTime || content . updatedAt
405
+ const defaults : Partial < SitemapUrl > = {
406
+ loc : content . path ,
407
+ }
408
+ if ( images . length > 0 )
409
+ defaults . images = images
410
+ if ( videos . length > 0 )
411
+ defaults . videos = videos
412
+ if ( lastmod )
413
+ defaults . lastmod = lastmod
414
+ const definition = defu ( sitemapConfig , defaults ) as Partial < SitemapUrl >
415
+ if ( ! definition . loc ) {
416
+ // user hasn't provided a loc... lets fallback to a relative path
417
+ if ( content . path && content . path && content . path . startsWith ( '/' ) )
418
+ definition . loc = content . path
419
+ }
420
+ content . sitemap = definition
421
+ // loc is required
422
+ if ( ! definition . loc )
423
+ delete content . sitemap
424
+ ctx . content = content
425
+ } )
426
+
427
+ addServerHandler ( {
428
+ route : '/__sitemap__/nuxt-content-urls.json' ,
429
+ handler : resolve ( './runtime/server/routes/__sitemap__/nuxt-content-urls-v3' ) ,
430
+ } )
431
+ if ( config . strictNuxtContentPaths ) {
432
+ logger . warn ( 'You have set `strictNuxtContentPaths: true` but are using @nuxt/content v3. This is not required, please remove it.' )
382
433
}
434
+ appGlobalSources . push ( {
435
+ context : {
436
+ name : '@nuxt/content@v3:urls' ,
437
+ description : 'Generated from your markdown files.' ,
438
+ tips : [ `Parsing the following collections: ${ Array . from ( nuxtV3Collections ) . join ( ', ' ) } ` ] ,
439
+ } ,
440
+ fetch : '/__sitemap__/nuxt-content-urls.json' ,
441
+ } )
442
+ }
443
+ else if ( isNuxtContentV2 ) {
444
+ addServerPlugin ( resolve ( './runtime/server/plugins/nuxt-content-v2' ) )
445
+ addServerHandler ( {
446
+ route : '/__sitemap__/nuxt-content-urls.json' ,
447
+ handler : resolve ( './runtime/server/routes/__sitemap__/nuxt-content-urls-v2' ) ,
448
+ } )
449
+ const tips : string [ ] = [ ]
450
+ // @ts -expect-error untyped
451
+ if ( nuxt . options . content ?. documentDriven )
452
+ tips . push ( 'Enabled because you\'re using `@nuxt/content` with `documentDriven: true`.' )
453
+ else if ( config . strictNuxtContentPaths )
454
+ tips . push ( 'Enabled because you\'ve set `config.strictNuxtContentPaths: true`.' )
455
+ else
456
+ tips . push ( 'You can provide a `sitemap` key in your markdown frontmatter to configure specific URLs. Make sure you include a `loc`.' )
457
+
458
+ appGlobalSources . push ( {
459
+ context : {
460
+ name : '@nuxt/content@v2:urls' ,
461
+ description : 'Generated from your markdown files.' ,
462
+ tips,
463
+ } ,
464
+ fetch : '/__sitemap__/nuxt-content-urls.json' ,
465
+ } )
383
466
}
384
467
385
468
// config -> sitemaps
@@ -567,9 +650,9 @@ declare module 'vue-router' {
567
650
// check for file in lastSegment using regex
568
651
const isExplicitFile = ! ! ( lastSegment ?. match ( / \. [ 0 - 9 a - z ] + $ / i) ?. [ 0 ] )
569
652
// avoid adding fallback pages to sitemap
570
- if ( r . error || [ '/200.html' , '/404.html' , '/index.html' ] . includes ( r . route ) )
653
+ if ( isExplicitFile || r . error || [ '/200.html' , '/404.html' , '/index.html' ] . includes ( r . route ) )
571
654
return false
572
- return ( r . contentType ?. includes ( 'text/html' ) || ! isExplicitFile )
655
+ return r . contentType ?. includes ( 'text/html' )
573
656
} )
574
657
. map ( r => r . _sitemap ) ,
575
658
]
0 commit comments