Skip to content

Commit 77f9c51

Browse files
authored
feat(utils): add streaming sitemap parsers (#635)
1 parent e5def28 commit 77f9c51

9 files changed

Lines changed: 1149 additions & 76 deletions

File tree

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
---
2+
title: XML Parsing Utilities
3+
description: Parse sitemap XML from strings or streams with @nuxtjs/sitemap/utils.
4+
relatedPages:
5+
- path: /docs/sitemap/advanced/performance
6+
title: Performance
7+
---
8+
9+
The `@nuxtjs/sitemap/utils` export provides parsers for URL sets and sitemap indexes.
10+
11+
## Parse a string
12+
13+
Use `parseSitemapXml` when the complete XML string is already in memory.
14+
15+
```ts
16+
import { parseSitemapXml } from '@nuxtjs/sitemap/utils'
17+
18+
const { urls, warnings } = await parseSitemapXml(xml)
19+
```
20+
21+
A closed, empty `<urlset>` returns `{ urls: [], warnings: [] }`. Invalid XML and documents without a URL set throw an error.
22+
23+
`parseSitemapIndex` parses an index string. `isSitemapIndex` provides a quick string check when the document is already buffered.
24+
25+
## Parse a stream
26+
27+
`parseSitemapStream` accepts a `ReadableStream`, including `Response.body`. It also accepts strings, `Uint8Array` values, and sync or async iterables of either chunk type.
28+
29+
The first event identifies the document. Later events contain URL entries, index entries, or validation warnings.
30+
31+
```ts
32+
import { parseSitemapStream } from '@nuxtjs/sitemap/utils'
33+
34+
const response = await fetch('https://example.com/sitemap.xml')
35+
if (!response.body)
36+
throw new Error('Missing response body')
37+
38+
const urls = []
39+
events:
40+
for await (const event of parseSitemapStream(response.body)) {
41+
if (event._tag === 'kind') {
42+
console.log(event.kind) // "urlset" or "index"
43+
continue
44+
}
45+
if (event._tag !== 'url')
46+
continue
47+
48+
urls.push(event.url)
49+
if (urls.length === 50_000)
50+
break events
51+
}
52+
```
53+
54+
Breaking iteration cancels the response body, so unread bytes are not downloaded or parsed. The unread tail is not validated. Reading through completion validates the closing root element.
55+
56+
Use `parseSitemapXmlStream` when only a URL set is accepted. Use `parseSitemapIndexStream` when only an index is accepted. These variants omit the initial `kind` event.
57+
58+
Each entry and the incomplete XML buffer are capped at 1 MiB by default. Pass `{ maxEntryBytes, maxBufferBytes }` to set different bounds.

‎src/utils/index.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@ export type * from '../runtime/types'
22
export { parseHtmlExtractSitemapMeta } from './parseHtmlExtractSitemapMeta'
33
export { isSitemapIndex, parseSitemapIndex } from './parseSitemapIndex'
44
export type { SitemapIndexEntry, SitemapIndexParseResult } from './parseSitemapIndex'
5-
export { parseSitemapXml } from './parseSitemapXml'
6-
export type { SitemapParseResult, SitemapWarning } from './parseSitemapXml'
5+
export { parseSitemapIndexStream, parseSitemapStream, parseSitemapXml, parseSitemapXmlStream } from './parseSitemapXml'
6+
export type { SitemapIndexStreamEvent, SitemapKind, SitemapParseResult, SitemapStreamEvent, SitemapStreamOptions, SitemapWarning, SitemapXmlChunk, SitemapXmlInput, SitemapXmlStreamEvent } from './parseSitemapXml'

‎src/utils/parseSitemapIndex.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { SitemapWarning } from './parseSitemapXml'
2+
import { XMLParser } from 'fast-xml-parser'
23

34
export interface SitemapIndexEntry {
45
loc: string
@@ -31,7 +32,6 @@ export async function parseSitemapIndex(xml: string): Promise<SitemapIndexParseR
3132
if (!xml)
3233
throw new Error('Empty XML input provided')
3334

34-
const { XMLParser } = await import('fast-xml-parser')
3535
const parser = new XMLParser({
3636
isArray: (tagName: string) => tagName === 'sitemap',
3737
removeNSPrefix: true,

0 commit comments

Comments
 (0)