This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
---
|
||||
title: Import HTML to Portable Text
|
||||
description: Use @portabletext/block-tools with JSDOM to convert HTML content
|
||||
---
|
||||
|
||||
## Import HTML to Portable Text
|
||||
|
||||
Use `@portabletext/block-tools` with `JSDOM` to convert HTML from legacy CMSs to Portable Text.
|
||||
|
||||
### Setup
|
||||
|
||||
```bash
|
||||
npm install @portabletext/block-tools jsdom
|
||||
```
|
||||
|
||||
### Basic Conversion
|
||||
|
||||
```typescript
|
||||
import { htmlToBlocks } from '@portabletext/block-tools'
|
||||
import { JSDOM } from 'jsdom'
|
||||
|
||||
// Get block content type from your schema
|
||||
const blockContentType = schema.get('blockContent')
|
||||
|
||||
const blocks = htmlToBlocks(htmlString, blockContentType, {
|
||||
parseHtml: html => new JSDOM(html).window.document,
|
||||
})
|
||||
```
|
||||
|
||||
### Custom Deserializers
|
||||
|
||||
Handle specific HTML patterns:
|
||||
|
||||
```javascript
|
||||
const blocks = htmlToBlocks(htmlString, blockContentType, {
|
||||
parseHtml: html => new JSDOM(html).window.document,
|
||||
rules: [
|
||||
{
|
||||
deserialize(el, next, block) {
|
||||
// Custom link handling
|
||||
if (el.tagName.toLowerCase() === 'a') {
|
||||
return {
|
||||
_type: 'link',
|
||||
href: el.getAttribute('href'),
|
||||
blank: el.getAttribute('target') === '_blank'
|
||||
}
|
||||
}
|
||||
// Custom image handling
|
||||
if (el.tagName.toLowerCase() === 'img') {
|
||||
return {
|
||||
_type: 'image',
|
||||
// Upload image separately, store reference
|
||||
_sanityAsset: `image@${el.getAttribute('src')}`
|
||||
}
|
||||
}
|
||||
return undefined // Fall through to default handling
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
### Pre-Processing HTML
|
||||
|
||||
Clean HTML before conversion:
|
||||
|
||||
```javascript
|
||||
function cleanHtml(html) {
|
||||
const dom = new JSDOM(html)
|
||||
const doc = dom.window.document
|
||||
|
||||
// Remove layout elements
|
||||
doc.querySelectorAll('header, footer, nav, .sidebar').forEach(el => el.remove())
|
||||
|
||||
// Extract metadata before processing body
|
||||
const title = doc.querySelector('title')?.textContent
|
||||
const description = doc.querySelector('meta[name="description"]')?.content
|
||||
|
||||
return {
|
||||
body: doc.body.innerHTML,
|
||||
metadata: { title, description }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Image Upload
|
||||
|
||||
Don't just link external images—upload them:
|
||||
|
||||
```javascript
|
||||
async function uploadImage(client, imageUrl) {
|
||||
const response = await fetch(imageUrl)
|
||||
const buffer = await response.arrayBuffer()
|
||||
|
||||
const asset = await client.assets.upload('image', Buffer.from(buffer), {
|
||||
filename: imageUrl.split('/').pop()
|
||||
})
|
||||
|
||||
return {
|
||||
_type: 'image',
|
||||
asset: { _type: 'reference', _ref: asset._id }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Using in a Migration
|
||||
|
||||
Wrap this in `defineMigration` for reproducible imports:
|
||||
|
||||
```typescript
|
||||
// migrations/import-wordpress-posts/index.ts
|
||||
import {defineMigration, createOrReplace} from 'sanity/migrate'
|
||||
import {htmlToBlocks} from '@portabletext/block-tools'
|
||||
|
||||
export default defineMigration({
|
||||
title: 'Import WordPress posts',
|
||||
async *migrate(documents, context) {
|
||||
const posts = await fetchWordPressPosts() // Your import source
|
||||
|
||||
for (const post of posts) {
|
||||
const blocks = htmlToBlocks(post.content, blockContentType, {
|
||||
parseHtml: html => new JSDOM(html).window.document,
|
||||
})
|
||||
|
||||
yield createOrReplace({
|
||||
_id: `post-${post.slug}`,
|
||||
_type: 'post',
|
||||
title: post.title,
|
||||
body: blocks,
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
Run with: `sanity migration run import-wordpress-posts --no-dry-run`
|
||||
|
||||
Reference: [Schema and Content Migrations](https://www.sanity.io/docs/content-lake/schema-and-content-migrations)
|
||||
Reference in New Issue
Block a user