Merge pull request #1273 from ueberdosis/feature/generate-json-from-html

New Feature: Generate JSON from HTML
This commit is contained in:
Philipp Kühn
2021-05-07 10:37:17 +02:00
committed by GitHub
14 changed files with 176 additions and 38 deletions

View File

@@ -0,0 +1,7 @@
context('/demos/Guide/Content/GenerateJSON', () => {
before(() => {
cy.visit('/demos/Guide/Content/GenerateJSON')
})
// TODO: Write tests
})

View File

@@ -0,0 +1,30 @@
<template>
<pre><code>{{ output }}</code></pre>
</template>
<script>
// Option 1: Browser + server-side
import { generateJSON } from '@tiptap/html'
// Option 2: Browser-only (lightweight)
// import { generateJSON } from '@tiptap/core'
import Document from '@tiptap/extension-document'
import Paragraph from '@tiptap/extension-paragraph'
import Text from '@tiptap/extension-text'
import Bold from '@tiptap/extension-bold'
const html = '<p>Example <strong>Text</strong></p>'
export default {
computed: {
output() {
return generateJSON(html, [
Document,
Paragraph,
Text,
Bold,
// other extensions …
])
},
},
}
</script>

View File

@@ -2,10 +2,15 @@
[![Version](https://img.shields.io/npm/v/@tiptap/html.svg?label=version)](https://www.npmjs.com/package/@tiptap/html)
[![Downloads](https://img.shields.io/npm/dm/@tiptap/html.svg)](https://npmcharts.com/compare/@tiptap/html?minimal=true)
The utility helps rendering JSON content as HTML without an editor instance, for example on the server side. All it needs is a JSON and an array of extensions.
The utility helps rendering JSON content as HTML, and generating JSON from HTML, without an editor instance, for example on the server side.
All it needs is JSON or a HTML string, and a list of extensions.
## Source code
[packages/html/](https://github.com/ueberdosis/tiptap/blob/main/packages/html/)
## Usage
## Generate HTML from JSON
<demo name="Guide/Content/GenerateHTML" highlight="6-7,42-48"/>
## Generate JSON from HTML
<demo name="Guide/Content/GenerateJSON" highlight="6-7,18-24"/>

View File

@@ -119,6 +119,10 @@ Thats what the `generateHTML()` is for. Its a helper function which render
<demo name="Guide/Content/GenerateHTML" highlight="6-7,42-48" />
By the way, the other way is possible, too. The below examples shows how to generate JSON from HTML.
<demo name="Guide/Content/GenerateJSON" highlight="6-7,18-24"/>
## Migration
If youre migrating existing content to tiptap we would recommend to get your existing output to HTML. Thats probably the best format to get your initial content into tiptap, because ProseMirror ensures there is nothing wrong with it. Even if there are some tags or attributes that arent allowed (based on your configuration), tiptap just throws them away quietly.

View File

@@ -0,0 +1,13 @@
import { DOMParser } from 'prosemirror-model'
import getSchema from './getSchema'
import elementFromString from '../utilities/elementFromString'
import { Extensions } from '../types'
export default function generateJSON(html: string, extensions: Extensions): Record<string, any> {
const schema = getSchema(extensions)
const dom = elementFromString(html)
return DOMParser.fromSchema(schema)
.parse(dom)
.toJSON()
}

View File

@@ -18,6 +18,7 @@ export { default as findChildren } from './helpers/findChildren'
export { default as findParentNode } from './helpers/findParentNode'
export { default as findParentNodeClosestToPos } from './helpers/findParentNodeClosestToPos'
export { default as generateHTML } from './helpers/generateHTML'
export { default as generateJSON } from './helpers/generateJSON'
export { default as getSchema } from './helpers/getSchema'
export { default as getHTMLFromFragment } from './helpers/getHTMLFromFragment'
export { default as getMarkAttributes } from './helpers/getMarkAttributes'

View File

@@ -1,24 +0,0 @@
import { generateHTML } from '@tiptap/html'
import Document from '@tiptap/extension-document'
import Paragraph from '@tiptap/extension-paragraph'
import Text from '@tiptap/extension-text'
// eslint-disable-next-line
const html = generateHTML({
type: 'doc',
content: [{
type: 'paragraph',
attrs: {
align: 'left',
},
content: [{
type: 'text',
text: 'Example Text',
}],
}],
}, [
new Document(),
new Paragraph(),
new Text(),
])

View File

@@ -0,0 +1,10 @@
import { Extensions, getSchema } from '@tiptap/core'
import { Node } from 'prosemirror-model'
import getHTMLFromFragment from './getHTMLFromFragment'
export default function generateHTML(doc: object, extensions: Extensions): string {
const schema = getSchema(extensions)
const contentNode = Node.fromJSON(schema, doc)
return getHTMLFromFragment(contentNode, schema)
}

View File

@@ -0,0 +1,13 @@
import { DOMParser } from 'prosemirror-model'
import { getSchema, Extensions } from '@tiptap/core'
// @ts-ignore
import { parseHTML } from 'hostic-dom'
export default function generateJSON(html: string, extensions: Extensions): Record<string, any> {
const schema = getSchema(extensions)
const dom = parseHTML(html)
return DOMParser.fromSchema(schema)
.parse(dom)
.toJSON()
}

View File

@@ -1,10 +1,2 @@
import { Extensions, getSchema } from '@tiptap/core'
import { Node } from 'prosemirror-model'
import getHTMLFromFragment from './getHTMLFromFragment'
export function generateHTML(doc: object, extensions: Extensions): string {
const schema = getSchema(extensions)
const contentNode = Node.fromJSON(schema, doc)
return getHTMLFromFragment(contentNode, schema)
}
export { default as generateHTML } from './generateHTML'
export { default as generateJSON } from './generateJSON'

View File

@@ -1,6 +1,6 @@
/// <reference types="cypress" />
import { generateHTML } from '@tiptap/html'
import { generateHTML } from '@tiptap/core'
import Document from '@tiptap/extension-document'
import Paragraph from '@tiptap/extension-paragraph'
import Text from '@tiptap/extension-text'

View File

@@ -0,0 +1,29 @@
/// <reference types="cypress" />
import { generateJSON } from '@tiptap/core'
import Document from '@tiptap/extension-document'
import Paragraph from '@tiptap/extension-paragraph'
import Text from '@tiptap/extension-text'
describe('generateJSON', () => {
it('generate JSON from HTML without an editor instance', () => {
const html = '<p>Example Text</p>'
const json = generateJSON(html, [
Document,
Paragraph,
Text,
])
expect(JSON.stringify(json)).to.eq(JSON.stringify({
type: 'doc',
content: [{
type: 'paragraph',
content: [{
type: 'text',
text: 'Example Text',
}],
}],
}))
})
})

View File

@@ -0,0 +1,29 @@
/// <reference types="cypress" />
import { generateHTML } from '@tiptap/html'
import Document from '@tiptap/extension-document'
import Paragraph from '@tiptap/extension-paragraph'
import Text from '@tiptap/extension-text'
describe('generateHTML', () => {
it('generate HTML from JSON without an editor instance', () => {
const json = {
type: 'doc',
content: [{
type: 'paragraph',
content: [{
type: 'text',
text: 'Example Text',
}],
}],
}
const html = generateHTML(json, [
Document,
Paragraph,
Text,
])
expect(html).to.eq('<p>Example Text</p>')
})
})

View File

@@ -0,0 +1,29 @@
/// <reference types="cypress" />
import { generateJSON } from '@tiptap/html'
import Document from '@tiptap/extension-document'
import Paragraph from '@tiptap/extension-paragraph'
import Text from '@tiptap/extension-text'
describe('generateJSON', () => {
it('generate JSON from HTML without an editor instance', () => {
const html = '<p>Example Text</p>'
const json = generateJSON(html, [
Document,
Paragraph,
Text,
])
expect(JSON.stringify(json)).to.eq(JSON.stringify({
type: 'doc',
content: [{
type: 'paragraph',
content: [{
type: 'text',
text: 'Example Text',
}],
}],
}))
})
})