Skip to content

Commit b7e109b

Browse files
ZeeJabtomdale
andauthored
Typescriptify Phase IV πŸ’ƒπŸ•Ί (#742)
* WIP on View/Tooltip TSification * Fix broken ImageSection inserting πŸ’ƒπŸ•Ί * Completes post-inserter migration πŸ’†β€β™€οΈ * PARSER! 0.2.0 🎩 * PARSER! 0.3.0 πŸͺ“πŸͺ“ AND all its side effects * PARSER! 0.3.1…0.3.2 🀿 + healthy refactor of renderers++ * Migrate index 🎧 * Long road πŸ›£ to migrating dom parser * Section + HTML parsers πŸ•΅οΈβ€β™€οΈ * Text Parser migration πŸŽ– * Temporarily disable image section test 🎭 * Migrate parse-utils 🚯 Co-authored-by: Tom Dale <[email protected]>
1 parent ed3dcc2 commit b7e109b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+914
-637
lines changed

β€Ž.eslintrc.jsβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ module.exports = {
250250
"no-unneeded-ternary": "off",
251251
"no-unused-expressions": "off",
252252
"no-unused-vars": "off",
253-
"@typescript-eslint/no-unused-vars": ["error"],
253+
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
254254
"no-useless-backreference": "error",
255255
"no-useless-call": "off",
256256
"no-useless-computed-key": "error",

β€Žrollup.config.jsβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ function commonPlugins() {
1919
},
2020
],
2121
}),
22-
typescript(),
22+
typescript({ noEmitOnError: false }),
2323
]
2424
}
2525

Lines changed: 63 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,32 @@
1-
import assert from 'mobiledoc-kit/utils/assert'
2-
import {
3-
MARKUP_SECTION_TYPE,
4-
LIST_SECTION_TYPE,
5-
POST_TYPE,
6-
CARD_TYPE,
7-
IMAGE_SECTION_TYPE,
8-
LIST_ITEM_TYPE,
9-
} from 'mobiledoc-kit/models/types'
10-
11-
const MARKERABLE = 'markerable',
12-
NESTED_MARKERABLE = 'nested_markerable',
13-
NON_MARKERABLE = 'non_markerable'
1+
import assert from '../../utils/assert'
2+
import { Option } from '../../utils/types'
3+
import { Type } from '../../models/types'
4+
import Post from '../../models/post'
5+
import Editor from '../editor'
6+
import PostNodeBuilder from '../../models/post-node-builder'
7+
import { Position } from '../../utils/cursor'
8+
import Section, { WithParent } from '../../models/_section'
9+
import MarkupSection from '../../models/markup-section'
10+
import ListSection from '../../models/list-section'
11+
import ListItem from '../../models/list-item'
12+
import Card from '../../models/card'
13+
import Image from '../../models/image'
14+
import Markerable from '../../models/_markerable'
15+
import { Cloneable } from '../../models/_cloneable'
16+
import HasChildSections, { hasChildSections } from '../../models/_has-child-sections'
17+
18+
const MARKERABLE = 'markerable'
19+
const NESTED_MARKERABLE = 'nested_markerable'
20+
const NON_MARKERABLE = 'non_markerable'
1421

1522
class Visitor {
16-
constructor(inserter, cursorPosition) {
17-
let { postEditor, post } = inserter
23+
postEditor: Editor
24+
builder: PostNodeBuilder
25+
_post: Post
26+
_hasInsertedFirstLeafSection: boolean
27+
_cursorPosition!: Position
28+
29+
constructor({ postEditor, post }: Inserter, cursorPosition: Position) {
1830
this.postEditor = postEditor
1931
this._post = post
2032
this.cursorPosition = cursorPosition
@@ -32,7 +44,7 @@ class Visitor {
3244
this.postEditor.setRange(position)
3345
}
3446

35-
visit(node) {
47+
visit(node: Post | Section) {
3648
let method = node.type
3749
assert(`Cannot visit node of type ${node.type}`, !!this[method])
3850
this[method](node)
@@ -51,7 +63,7 @@ class Visitor {
5163
}
5264

5365
get cursorSection() {
54-
return this.cursorPosition.section
66+
return this.cursorPosition.section!
5567
}
5668

5769
get cursorOffset() {
@@ -62,21 +74,22 @@ class Visitor {
6274
return this.cursorSection.isNested
6375
}
6476

65-
[POST_TYPE](node) {
66-
if (this.cursorSection.isBlank && !this._isNested) {
77+
[Type.POST](node: Post) {
78+
let { cursorSection } = this
79+
if (cursorSection.isBlank && !cursorSection.isNested) {
6780
// replace blank section with entire post
6881
let newSections = node.sections.map(s => s.clone())
69-
this._replaceSection(this.cursorSection, newSections)
82+
this._replaceSection(cursorSection as Section & WithParent<HasChildSections>, newSections)
7083
} else {
7184
node.sections.forEach(section => this.visit(section))
7285
}
7386
}
7487

75-
[MARKUP_SECTION_TYPE](node) {
88+
[Type.MARKUP_SECTION](node: MarkupSection) {
7689
this[MARKERABLE](node)
7790
}
7891

79-
[LIST_SECTION_TYPE](node) {
92+
[Type.LIST_SECTION](node: ListSection) {
8093
let hasNext = !!node.next
8194
node.items.forEach(item => this.visit(item))
8295

@@ -85,19 +98,19 @@ class Visitor {
8598
}
8699
}
87100

88-
[LIST_ITEM_TYPE](node) {
101+
[Type.LIST_ITEM](node: ListItem) {
89102
this[NESTED_MARKERABLE](node)
90103
}
91104

92-
[CARD_TYPE](node) {
105+
[Type.CARD](node: Card) {
93106
this[NON_MARKERABLE](node)
94107
}
95108

96-
[IMAGE_SECTION_TYPE](node) {
109+
[Type.IMAGE_SECTION](node: Image) {
97110
this[NON_MARKERABLE](node)
98111
}
99112

100-
[NON_MARKERABLE](section) {
113+
[NON_MARKERABLE](section: Cloneable<Section>) {
101114
if (this._isNested) {
102115
this._breakNestedAtCursor()
103116
} else if (!this.cursorSection.isBlank) {
@@ -107,7 +120,7 @@ class Visitor {
107120
this._insertLeafSection(section)
108121
}
109122

110-
[MARKERABLE](section) {
123+
[MARKERABLE](section: Markerable) {
111124
if (this._canMergeSection(section)) {
112125
this._mergeSection(section)
113126
} else if (this._isNested && this._isMarkerable) {
@@ -116,7 +129,7 @@ class Visitor {
116129
this._breakAtCursor()
117130

118131
// Advance the cursor to the head of the blank list item
119-
let nextPosition = this.cursorSection.next.headPosition()
132+
let nextPosition = this.cursorSection.next!.headPosition()
120133
this.cursorPosition = nextPosition
121134

122135
// Merge this section onto the list item
@@ -127,22 +140,22 @@ class Visitor {
127140
}
128141
}
129142

130-
[NESTED_MARKERABLE](section) {
143+
[NESTED_MARKERABLE](section: Markerable) {
131144
if (this._canMergeSection(section)) {
132145
this._mergeSection(section)
133146
return
134147
}
135148

136-
section = this._isNested ? section : this._wrapNestedSection(section)
149+
let insertedSection = this._isNested ? section : this._wrapNestedSection(section as ListItem)
137150
this._breakAtCursor()
138-
this._insertLeafSection(section)
151+
this._insertLeafSection(insertedSection)
139152
}
140153

141154
// break out of a nested cursor position
142155
_breakNestedAtCursor() {
143156
assert('Cannot call _breakNestedAtCursor if not nested', this._isNested)
144157

145-
let parent = this.cursorSection.parent
158+
let parent = this.cursorSection.parent!
146159
let cursorAtEndOfList = this.cursorPosition.isEqual(parent.tailPosition())
147160

148161
if (cursorAtEndOfList) {
@@ -160,6 +173,7 @@ class Visitor {
160173
let list = this.cursorSection.parent,
161174
position = this.cursorPosition,
162175
blank = this.builder.createMarkupSection()
176+
163177
let [pre, post] = this.postEditor._splitListAtPosition(list, position)
164178

165179
let collection = this._post.sections,
@@ -168,14 +182,14 @@ class Visitor {
168182
return [pre, blank, post]
169183
}
170184

171-
_wrapNestedSection(section) {
172-
let tagName = section.parent.tagName
185+
_wrapNestedSection(section: ListItem) {
186+
let tagName = section.parent!.tagName
173187
let parent = this.builder.createListSection(tagName)
174188
parent.items.append(section.clone())
175189
return parent
176190
}
177191

178-
_mergeSection(section) {
192+
_mergeSection(section: Markerable) {
179193
assert('Can only merge markerable sections', this._isMarkerable && section.isMarkerable)
180194
this._hasInsertedFirstLeafSection = true
181195

@@ -213,8 +227,8 @@ class Visitor {
213227
this.cursorPosition = pre.tailPosition()
214228
}
215229

216-
_replaceSection(section, newSections) {
217-
assert('Cannot replace section that does not have parent.sections', section.parent && section.parent.sections)
230+
_replaceSection(section: Section, newSections: Section[]) {
231+
assert('Cannot replace section that does not have parent.sections', hasChildSections(section.parent!))
218232
assert('Must pass enumerable to _replaceSection', !!newSections.forEach)
219233

220234
let collection = section.parent.sections
@@ -228,7 +242,11 @@ class Visitor {
228242
this.cursorPosition = lastSection.tailPosition()
229243
}
230244

231-
_insertSectionBefore(section, reference) {
245+
_insertSectionBefore(section: Section, reference?: Option<Section>) {
246+
assert(
247+
'Cannot insert into section that does not have parent.sections',
248+
hasChildSections(this.cursorSection.parent!)
249+
)
232250
let collection = this.cursorSection.parent.sections
233251
this.postEditor.insertSectionBefore(collection, section, reference)
234252

@@ -237,15 +255,15 @@ class Visitor {
237255

238256
// Insert a section after the parent section.
239257
// E.g., add a markup section after a list section
240-
_insertSectionAfter(section, parent) {
258+
_insertSectionAfter(section: Section, parent: Section) {
241259
assert('Cannot _insertSectionAfter nested section', !parent.isNested)
242260
let reference = parent.next
243261
let collection = this._post.sections
244262
this.postEditor.insertSectionBefore(collection, section, reference)
245263
this.cursorPosition = section.tailPosition()
246264
}
247265

248-
_insertLeafSection(section) {
266+
_insertLeafSection(section: Cloneable<Section>) {
249267
assert('Can only _insertLeafSection when cursor is at end of section', this.cursorPosition.isTail())
250268

251269
this._hasInsertedFirstLeafSection = true
@@ -267,12 +285,15 @@ class Visitor {
267285
}
268286

269287
export default class Inserter {
270-
constructor(postEditor, post) {
288+
postEditor: Editor
289+
post: Post
290+
291+
constructor(postEditor: Editor, post: Post) {
271292
this.postEditor = postEditor
272293
this.post = post
273294
}
274295

275-
insert(cursorPosition, newPost) {
296+
insert(cursorPosition: Position, newPost: Post) {
276297
let visitor = new Visitor(this, cursorPosition)
277298
if (!newPost.isBlank) {
278299
visitor.visit(newPost)

β€Žsrc/js/models/_cloneable.tsβ€Ž

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import Section from './_section'
2+
3+
export type Cloneable<T> = T & {
4+
clone(): Cloneable<T>
5+
}
6+
7+
export function expectCloneable<T extends Section>(section: T): Cloneable<T> {
8+
if (!('clone' in section)) {
9+
throw new Error('Expected section to be cloneable')
10+
}
11+
12+
return section as Cloneable<T>
13+
}

β€Žsrc/js/models/_markerable.tsβ€Ž

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@ import Section from './_section'
88
import Marker from './marker'
99
import { tagNameable } from './_tag-nameable'
1010
import { Type } from './types'
11+
import { Cloneable } from './_cloneable'
12+
import Markuperable from '../utils/markuperable'
1113

1214
type MarkerableType = Type.LIST_ITEM | Type.MARKUP_SECTION
1315

14-
export default abstract class Markerable extends tagNameable(Section) {
16+
export default abstract class Markerable extends tagNameable(Section) implements Cloneable<Markerable> {
1517
type: MarkerableType
16-
markers: LinkedList<Marker>
18+
markers: LinkedList<Markuperable>
1719

18-
constructor(type: MarkerableType, tagName: string, markers: Marker[] = []) {
20+
constructor(type: MarkerableType, tagName: string, markers: Markuperable[] = []) {
1921
super(type)
2022
this.type = type
2123
this.isMarkerable = true
@@ -70,7 +72,7 @@ export default abstract class Markerable extends tagNameable(Section) {
7072
*
7173
* @return {Number} The offset relative to the start of this section
7274
*/
73-
offsetOfMarker(marker: Marker, markerOffset = 0) {
75+
offsetOfMarker(marker: Markuperable, markerOffset = 0) {
7476
assert(`Cannot get offsetOfMarker for marker that is not child of this`, marker.section === this)
7577

7678
// FIXME it is possible, when we get a cursor position before having finished reparsing,
@@ -112,7 +114,7 @@ export default abstract class Markerable extends tagNameable(Section) {
112114
return [beforeSection, afterSection]
113115
}
114116

115-
abstract splitAtMarker(marker: Marker, offset: number): [Section, Section]
117+
abstract splitAtMarker(marker: Markuperable, offset: number): [Section, Section]
116118

117119
/**
118120
* Split this section's marker (if any) at the given offset, so that
@@ -128,7 +130,7 @@ export default abstract class Markerable extends tagNameable(Section) {
128130
let markerOffset
129131
let len = 0
130132
let currentMarker = this.markers.head
131-
let edit: { added: Marker[]; removed: Marker[] } = { added: [], removed: [] }
133+
let edit: { added: Markuperable[]; removed: Markuperable[] } = { added: [], removed: [] }
132134

133135
if (!currentMarker) {
134136
let blankMarker = this.builder.createMarker()
@@ -181,7 +183,7 @@ export default abstract class Markerable extends tagNameable(Section) {
181183

182184
markerPositionAtOffset(offset: number) {
183185
let currentOffset = 0
184-
let currentMarker: Marker | null = null
186+
let currentMarker: Markuperable | null = null
185187
let remaining = offset
186188
this.markers.detect(marker => {
187189
currentOffset = Math.min(remaining, marker.length)
@@ -211,7 +213,7 @@ export default abstract class Markerable extends tagNameable(Section) {
211213
markersFor(headOffset: number, tailOffset: number) {
212214
const range = Range.create(this, headOffset, this, tailOffset)
213215

214-
let markers: Marker[] = []
216+
let markers: Markuperable[] = []
215217
this._markersInRange(range, (marker, { markerHead, markerTail, isContained }) => {
216218
const cloned = marker.clone()
217219
if (!isContained) {
@@ -237,7 +239,7 @@ export default abstract class Markerable extends tagNameable(Section) {
237239
// for each marker that is wholly or partially contained in the range.
238240
_markersInRange(
239241
range: Range,
240-
callback: (marker: Marker, info: { markerHead: number; markerTail: number; isContained: boolean }) => void
242+
callback: (marker: Markuperable, info: { markerHead: number; markerTail: number; isContained: boolean }) => void
241243
) {
242244
const { head, tail } = range
243245
assert(
@@ -274,7 +276,7 @@ export default abstract class Markerable extends tagNameable(Section) {
274276
// mutates this by appending the other section's (cloned) markers to it
275277
join(otherSection: Markerable) {
276278
let beforeMarker = this.markers.tail
277-
let afterMarker: Marker | null = null
279+
let afterMarker: Markuperable | null = null
278280

279281
otherSection.markers.forEach(m => {
280282
if (!m.isBlank) {

0 commit comments

Comments
Β (0)