1- const SimpleDom = require ( 'simple-dom' ) ;
2- const Renderer = require ( 'mobiledoc-dom-renderer' ) . default ;
3- const { slugify} = require ( '@tryghost/kg-utils' ) ;
1+ import { Document as SimpleDomDocument , HTMLSerializer , voidMap } from 'simple-dom' ;
2+ import MobiledocDomRenderer from 'mobiledoc-dom-renderer' ;
3+ import { slugify } from '@tryghost/kg-utils' ;
4+
5+ // mobiledoc-dom-renderer may use CJS default export pattern
6+ const Renderer = ( MobiledocDomRenderer as { default ?: typeof MobiledocDomRenderer } ) . default || MobiledocDomRenderer ;
7+
8+ interface SimpleDomNode {
9+ nodeType : number ;
10+ nodeName : string ;
11+ nodeValue : string | null ;
12+ tagName : string ;
13+ firstChild : SimpleDomNode | null ;
14+ lastChild : SimpleDomNode | null ;
15+ nextSibling : SimpleDomNode | null ;
16+ appendChild ( child : SimpleDomNode ) : void ;
17+ removeChild ( child : SimpleDomNode ) : void ;
18+ setAttribute ( name : string , value : string ) : void ;
19+ getAttribute ( name : string ) : string | null ;
20+ }
421
5- const walkDom = function ( node , func ) {
22+ const walkDom = function ( node : SimpleDomNode , func : ( node : SimpleDomNode ) => void ) : void {
623 func ( node ) ;
7- node = node . firstChild ;
24+ let child = node . firstChild ;
825
9- while ( node ) {
10- walkDom ( node , func ) ;
11- node = node . nextSibling ;
26+ while ( child ) {
27+ walkDom ( child , func ) ;
28+ child = child . nextSibling ;
1229 }
1330} ;
1431
15- const nodeTextContent = function ( node ) {
32+ const nodeTextContent = function ( node : SimpleDomNode ) : string {
1633 let textContent = '' ;
1734
18- walkDom ( node , ( currentNode ) => {
35+ walkDom ( node , ( currentNode : SimpleDomNode ) => {
1936 if ( currentNode . nodeType === 3 ) {
2037 textContent += currentNode . nodeValue ;
2138 }
@@ -24,21 +41,34 @@ const nodeTextContent = function (node) {
2441 return textContent ;
2542} ;
2643
44+ interface SimpleDom {
45+ createElement ( tag : string ) : SimpleDomNode ;
46+ }
47+
48+ interface DomModifierOptions {
49+ ghostVersion ?: string ;
50+ target ?: string ;
51+ dom : SimpleDom ;
52+ [ key : string ] : unknown ;
53+ }
54+
2755// used to walk the rendered SimpleDOM output and modify elements before
2856// serializing to HTML. Saves having a large HTML parsing dependency such as
2957// jsdom that may break on malformed HTML in MD or HTML cards
3058class DomModifier {
31- constructor ( options ) {
32- this . usedIds = [ ] ;
59+ usedIds : Record < string , number > = { } ;
60+ options : DomModifierOptions ;
61+
62+ constructor ( options : DomModifierOptions ) {
3363 this . options = options ;
3464 }
3565
36- addHeadingId ( node ) {
66+ addHeadingId ( node : SimpleDomNode ) : void {
3767 if ( ! node . firstChild || node . getAttribute ( 'id' ) ) {
3868 return ;
3969 }
4070
41- let text = nodeTextContent ( node ) ;
71+ const text = nodeTextContent ( node ) ;
4272 let id = slugify ( text , this . options ) ;
4373
4474 if ( this . usedIds [ id ] !== undefined ) {
@@ -51,7 +81,7 @@ class DomModifier {
5181 node . setAttribute ( 'id' , id ) ;
5282 }
5383
54- wrapBlockquoteContentInP ( node ) {
84+ wrapBlockquoteContentInP ( node : SimpleDomNode ) : void {
5585 if ( node . firstChild && node . firstChild . tagName === 'P' ) {
5686 return ;
5787 }
@@ -64,11 +94,11 @@ class DomModifier {
6494 node . appendChild ( p ) ;
6595 }
6696
67- modifyChildren ( node ) {
97+ modifyChildren ( node : SimpleDomNode ) : void {
6898 walkDom ( node , this . modify . bind ( this ) ) ;
6999 }
70100
71- modify ( node ) {
101+ modify ( node : SimpleDomNode ) : void {
72102 // add id attributes to H* tags
73103 if ( node . nodeType === 1 && node . nodeName . match ( / ^ h \d $ / i) ) {
74104 this . addHeadingId ( node ) ;
@@ -81,17 +111,50 @@ class DomModifier {
81111 }
82112}
83113
84- class MobiledocHtmlRenderer {
85- constructor ( options = { } ) {
114+ interface CardDefinition {
115+ name : string ;
116+ type : string ;
117+ render ( args : Record < string , unknown > ) : unknown ;
118+ }
119+
120+ interface AtomDefinition {
121+ name : string ;
122+ type : string ;
123+ render ( args : Record < string , unknown > ) : unknown ;
124+ }
125+
126+ interface RendererOptions {
127+ cards ?: CardDefinition [ ] ;
128+ atoms ?: AtomDefinition [ ] ;
129+ unknownCardHandler ?: ( ...args : unknown [ ] ) => void ;
130+ }
131+
132+ interface RendererInternalOptions {
133+ dom : SimpleDomDocument ;
134+ cards : CardDefinition [ ] ;
135+ atoms : AtomDefinition [ ] ;
136+ unknownCardHandler : ( ...args : unknown [ ] ) => void ;
137+ }
138+
139+ interface Mobiledoc {
140+ ghostVersion ?: string ;
141+ version : string ;
142+ [ key : string ] : unknown ;
143+ }
144+
145+ export class MobiledocHtmlRenderer {
146+ options : RendererInternalOptions ;
147+
148+ constructor ( options : RendererOptions = { } ) {
86149 this . options = {
87- dom : new SimpleDom . Document ( ) ,
150+ dom : new SimpleDomDocument ( ) ,
88151 cards : options . cards || [ ] ,
89152 atoms : options . atoms || [ ] ,
90153 unknownCardHandler : options . unknownCardHandler || function ( ) { }
91154 } ;
92155 }
93156
94- render ( mobiledoc , _cardOptions = { } ) {
157+ render ( mobiledoc : Mobiledoc , _cardOptions : Record < string , unknown > = { } ) : string {
95158 const ghostVersion = mobiledoc . ghostVersion || '4.0' ;
96159
97160 const defaultCardOptions = {
@@ -101,7 +164,7 @@ class MobiledocHtmlRenderer {
101164 const cardOptions = Object . assign ( { } , defaultCardOptions , _cardOptions ) ;
102165
103166 const sectionElementRenderer = {
104- ASIDE : function ( tagName , dom ) {
167+ ASIDE : function ( _tagName : string , dom : SimpleDom ) {
105168 // we use ASIDE sections in Koenig as a workaround for applying
106169 // a different blockquote style because mobiledoc doesn't support
107170 // storing arbitrary attributes with sections
@@ -114,11 +177,11 @@ class MobiledocHtmlRenderer {
114177 const rendererOptions = Object . assign ( { } , this . options , { cardOptions, sectionElementRenderer} ) ;
115178 const renderer = new Renderer ( rendererOptions ) ;
116179 const rendered = renderer . render ( mobiledoc ) ;
117- const serializer = new SimpleDom . HTMLSerializer ( SimpleDom . voidMap ) ;
180+ const serializer = new HTMLSerializer ( voidMap ) ;
118181
119182 // Koenig keeps a blank paragraph at the end of a doc but we want to
120183 // make sure it doesn't get rendered
121- const lastChild = rendered . result . lastChild ;
184+ const lastChild = rendered . result . lastChild as SimpleDomNode | null ;
122185 if ( lastChild && lastChild . tagName === 'P' ) {
123186 if ( ! nodeTextContent ( lastChild ) ) {
124187 rendered . result . removeChild ( lastChild ) ;
@@ -127,8 +190,8 @@ class MobiledocHtmlRenderer {
127190
128191 // Walk the DOM output and modify nodes as needed
129192 // eg. to add ID attributes to heading elements
130- const modifier = new DomModifier ( Object . assign ( { } , cardOptions , { dom : this . options . dom } ) ) ;
131- modifier . modifyChildren ( rendered . result ) ;
193+ const modifier = new DomModifier ( Object . assign ( { } , cardOptions , { dom : this . options . dom } ) as DomModifierOptions ) ;
194+ modifier . modifyChildren ( rendered . result as unknown as SimpleDomNode ) ;
132195
133196 const output = serializer . serializeChildren ( rendered . result ) ;
134197
@@ -138,5 +201,3 @@ class MobiledocHtmlRenderer {
138201 return output ;
139202 }
140203}
141-
142- module . exports = MobiledocHtmlRenderer ;
0 commit comments