feat(package.json): Add lint yarn script + make it a precondition to build all packages.

fmt(tiptap-commands): Fix all ESLint violations with `--fix`

fmt(tiptap-commands): Ignore some ESLint rules on code copied from prosemirror.

fmt(tiptap): Apply ESLint autofix to `tiptap` package sources.

fmt(tiptap-extensions): Fix ESlint violations from `marks`

refactor(tiptap-extensions): Fix ESLint violations for `plugins/Suggestions.js`.

Some of the violations required a bit of restructuring of the code/logic

fmt(tiptap-utils): Fix ESLint violations.

feat(package.json): Add yarn script to lint source code before compiling the examples.
This commit is contained in:
Erick Wilder
2018-10-13 16:45:22 +02:00
parent 3c650cf35f
commit 54550b2386
8 changed files with 80 additions and 69 deletions

View File

@@ -8,7 +8,10 @@
"start": "node ./node_modules/@babel/node/bin/babel-node.js ./build/examples/server.js --env=development", "start": "node ./node_modules/@babel/node/bin/babel-node.js ./build/examples/server.js --env=development",
"build:packages": "node ./node_modules/@babel/node/bin/babel-node.js ./build/packages/build.js", "build:packages": "node ./node_modules/@babel/node/bin/babel-node.js ./build/packages/build.js",
"build:examples": "node ./node_modules/@babel/node/bin/babel-node.js ./build/examples/build.js --env=production", "build:examples": "node ./node_modules/@babel/node/bin/babel-node.js ./build/examples/build.js --env=production",
"release": "yarn build:packages && lerna publish" "release": "yarn build:packages && lerna publish",
"lint": "eslint ./packages/**/src/**",
"prebuild:packages": "yarn lint",
"prebuild:examples": "yarn lint"
}, },
"babel": { "babel": {
"presets": [ "presets": [

View File

@@ -2,23 +2,24 @@ import { InputRule } from 'prosemirror-inputrules'
export default function (regexp, markType, getAttrs) { export default function (regexp, markType, getAttrs) {
return new InputRule(regexp, (state, match, start, end) => { return new InputRule(regexp, (state, match, start, end) => {
let attrs = getAttrs instanceof Function ? getAttrs(match) : getAttrs const attrs = getAttrs instanceof Function ? getAttrs(match) : getAttrs
let tr = state.tr const { tr } = state
let markEnd = end
if (match[1]) { if (match[1]) {
const startSpaces = match[0].search(/\S/) const startSpaces = match[0].search(/\S/)
let textStart = start + match[0].indexOf(match[1]) const textStart = start + match[0].indexOf(match[1])
let textEnd = textStart + match[1].length const textEnd = textStart + match[1].length
if (textEnd < end) { if (textEnd < end) {
tr.delete(textEnd, end) tr.delete(textEnd, end)
} }
if (textStart > start) { if (textStart > start) {
tr.delete(start + startSpaces, textStart) tr.delete(start + startSpaces, textStart)
} }
end = start + startSpaces + match[1].length markEnd = start + startSpaces + match[1].length
} }
tr.addMark(start, end, markType.create(attrs)) tr.addMark(start, markEnd, markType.create(attrs))
tr.removeStoredMark(markType) // Do not continue with mark. tr.removeStoredMark(markType) // Do not continue with mark.
return tr return tr
}) })

View File

@@ -1,73 +1,76 @@
// this is a copy of canSplit // this is a copy of canSplit
// see https://github.com/ProseMirror/prosemirror-transform/blob/master/src/structure.js // see https://github.com/ProseMirror/prosemirror-transform/blob/master/src/structure.js
// Since this piece of code was "borrowed" from prosemirror, ESLint rules are ignored.
/* eslint-disable max-len, no-plusplus, no-undef, eqeqeq */
function canSplit(doc, pos, depth = 1, typesAfter) { function canSplit(doc, pos, depth = 1, typesAfter) {
let $pos = doc.resolve(pos), base = $pos.depth - depth const $pos = doc.resolve(pos); const
let innerType = (typesAfter && typesAfter[typesAfter.length - 1]) || $pos.parent base = $pos.depth - depth
if (base < 0 || $pos.parent.type.spec.isolating || const innerType = (typesAfter && typesAfter[typesAfter.length - 1]) || $pos.parent
!$pos.parent.canReplace($pos.index(), $pos.parent.childCount) || if (base < 0 || $pos.parent.type.spec.isolating
!innerType.type.validContent($pos.parent.content.cutByIndex($pos.index(), $pos.parent.childCount))) || !$pos.parent.canReplace($pos.index(), $pos.parent.childCount)
return false || !innerType.type.validContent($pos.parent.content.cutByIndex($pos.index(), $pos.parent.childCount))) return false
for (let d = $pos.depth - 1, i = depth - 2; d > base; d--, i--) { for (let d = $pos.depth - 1, i = depth - 2; d > base; d--, i--) {
let node = $pos.node(d), index = $pos.index(d) const node = $pos.node(d); const
index = $pos.index(d)
if (node.type.spec.isolating) return false if (node.type.spec.isolating) return false
let rest = node.content.cutByIndex(index, node.childCount) let rest = node.content.cutByIndex(index, node.childCount)
let after = (typesAfter && typesAfter[i]) || node const after = (typesAfter && typesAfter[i]) || node
if (after != node) rest = rest.replaceChild(0, after.type.create(after.attrs)) if (after != node) rest = rest.replaceChild(0, after.type.create(after.attrs))
/* Change starts from here */ /* Change starts from here */
// if (!node.canReplace(index + 1, node.childCount) || !after.type.validContent(rest)) // if (!node.canReplace(index + 1, node.childCount) || !after.type.validContent(rest))
// return false // return false
if (!node.canReplace(index + 1, node.childCount)) if (!node.canReplace(index + 1, node.childCount)) return false
return false
/* Change ends here */ /* Change ends here */
} }
let index = $pos.indexAfter(base) const index = $pos.indexAfter(base)
let baseType = typesAfter && typesAfter[0] const baseType = typesAfter && typesAfter[0]
return $pos.node(base).canReplaceWith(index, index, baseType ? baseType.type : $pos.node(base + 1).type) return $pos.node(base).canReplaceWith(index, index, baseType ? baseType.type : $pos.node(base + 1).type)
} }
// this is a copy of splitListItem // this is a copy of splitListItem
// see https://github.com/ProseMirror/prosemirror-schema-list/blob/master/src/schema-list.js // see https://github.com/ProseMirror/prosemirror-schema-list/blob/master/src/schema-list.js
export default function (itemType) { export default function splitListItem(itemType) {
return function(state, dispatch) { return function _splitListItem(state, dispatch) {
let {$from, $to, node} = state.selection const { $from, $to, node } = state.selection
if ((node && node.isBlock) || $from.depth < 2 || !$from.sameParent($to)) return false if ((node && node.isBlock) || $from.depth < 2 || !$from.sameParent($to)) return false
let grandParent = $from.node(-1) const grandParent = $from.node(-1)
if (grandParent.type != itemType) return false if (grandParent.type != itemType) return false
if ($from.parent.content.size == 0) { if ($from.parent.content.size == 0) {
// In an empty block. If this is a nested list, the wrapping // In an empty block. If this is a nested list, the wrapping
// list item should be split. Otherwise, bail out and let next // list item should be split. Otherwise, bail out and let next
// command handle lifting. // command handle lifting.
if ($from.depth == 2 || $from.node(-3).type != itemType || if ($from.depth == 2 || $from.node(-3).type != itemType
$from.index(-2) != $from.node(-2).childCount - 1) return false || $from.index(-2) != $from.node(-2).childCount - 1) return false
if (dispatch) { if (dispatch) {
let wrap = Fragment.empty, keepItem = $from.index(-1) > 0 let wrap = Fragment.empty; const
keepItem = $from.index(-1) > 0
// Build a fragment containing empty versions of the structure // Build a fragment containing empty versions of the structure
// from the outer list item to the parent node of the cursor // from the outer list item to the parent node of the cursor
for (let d = $from.depth - (keepItem ? 1 : 2); d >= $from.depth - 3; d--) for (let d = $from.depth - (keepItem ? 1 : 2); d >= $from.depth - 3; d--) wrap = Fragment.from($from.node(d).copy(wrap))
wrap = Fragment.from($from.node(d).copy(wrap))
// Add a second list item with an empty default start node // Add a second list item with an empty default start node
wrap = wrap.append(Fragment.from(itemType.createAndFill())) wrap = wrap.append(Fragment.from(itemType.createAndFill()))
let tr = state.tr.replace($from.before(keepItem ? null : -1), $from.after(-3), new Slice(wrap, keepItem ? 3 : 2, 2)) const tr = state.tr.replace($from.before(keepItem ? null : -1), $from.after(-3), new Slice(wrap, keepItem ? 3 : 2, 2))
tr.setSelection(state.selection.constructor.near(tr.doc.resolve($from.pos + (keepItem ? 3 : 2)))) tr.setSelection(state.selection.constructor.near(tr.doc.resolve($from.pos + (keepItem ? 3 : 2))))
dispatch(tr.scrollIntoView()) dispatch(tr.scrollIntoView())
} }
return true return true
} }
let nextType = $to.pos == $from.end() ? grandParent.contentMatchAt($from.indexAfter(-1)).defaultType : null const nextType = $to.pos == $from.end() ? grandParent.contentMatchAt($from.indexAfter(-1)).defaultType : null
let tr = state.tr.delete($from.pos, $to.pos) const tr = state.tr.delete($from.pos, $to.pos)
/* Change starts from here */ /* Change starts from here */
// let types = nextType && [null, {type: nextType}] // let types = nextType && [null, {type: nextType}]
let types = nextType && [{type: itemType}, {type: nextType}] let types = nextType && [{ type: itemType }, { type: nextType }]
if (!types) types = [{type: itemType}, null] if (!types) types = [{ type: itemType }, null]
/* Change ends here */ /* Change ends here */
if (!canSplit(tr.doc, $from.pos, 2, types)) return false if (!canSplit(tr.doc, $from.pos, 2, types)) return false
if (dispatch) dispatch(tr.split($from.pos, 2, [{type: state.schema.nodes.todo_item, attrs: { done: false }}]).scrollIntoView()) if (dispatch) dispatch(tr.split($from.pos, 2, [{ type: state.schema.nodes.todo_item, attrs: { done: false } }]).scrollIntoView())
return true return true
} }
} }
/* eslint-enable max-len, no-plusplus, no-undef, eqeqeq */

View File

@@ -15,7 +15,7 @@ export default class BoldMark extends Mark {
}, },
{ {
tag: 'b', tag: 'b',
getAttrs: node => node.style.fontWeight != 'normal' && null, getAttrs: node => node.style.fontWeight !== 'normal' && null,
}, },
{ {
style: 'font-weight', style: 'font-weight',
@@ -38,7 +38,7 @@ export default class BoldMark extends Mark {
inputRules({ type }) { inputRules({ type }) {
return [ return [
markInputRule(/(?:\*\*|__)([^\*_]+)(?:\*\*|__)$/, type), markInputRule(/(?:\*\*|__)([^*_]+)(?:\*\*|__)$/, type),
] ]
} }

View File

@@ -30,7 +30,7 @@ export default class ItalicMark extends Mark {
inputRules({ type }) { inputRules({ type }) {
return [ return [
markInputRule(/(?:^|[^\*_])(?:\*|_)([^\*_]+)(?:\*|_)$/, type), markInputRule(/(?:^|[^*_])(?:\*|_)([^*_]+)(?:\*|_)$/, type),
] ]
} }

View File

@@ -22,28 +22,27 @@ function triggerCharacter({
const textTo = $position.end() const textTo = $position.end()
const text = $position.doc.textBetween(textFrom, textTo, '\0', '\0') const text = $position.doc.textBetween(textFrom, textTo, '\0', '\0')
let match let match = regexp.exec(text)
let position
while ((match = regexp.exec(text))) { while (match !== null) {
// Javascript doesn't have lookbehinds; this hacks a check that first character is " " or the line beginning // JavaScript doesn't have lookbehinds; this hacks a check that first character is " "
const prefix = match.input.slice(Math.max(0, match.index - 1), match.index) // or the line beginning
if (!/^[\s\0]?$/.test(prefix)) { const matchPrefix = match.input.slice(Math.max(0, match.index - 1), match.index)
continue if (/^[\s\0]?$/.test(matchPrefix)) {
}
// The absolute position of the match in the document // The absolute position of the match in the document
const from = match.index + $position.start() const from = match.index + $position.start()
let to = from + match[0].length let to = from + match[0].length
// Edge case handling; if spaces are allowed and we're directly in between two triggers // Edge case handling; if spaces are allowed and we're directly in between
// two triggers
if (allowSpaces && suffix.test(text.slice(to - 1, to + 1))) { if (allowSpaces && suffix.test(text.slice(to - 1, to + 1))) {
match[0] += ' ' match[0] += ' '
to++ to += 1
} }
// If the $position is located within the matched substring, return that range // If the $position is located within the matched substring, return that range
if (from < $position.pos && to >= $position.pos) { if (from < $position.pos && to >= $position.pos) {
return { position = {
range: { range: {
from, from,
to, to,
@@ -53,6 +52,9 @@ function triggerCharacter({
} }
} }
} }
match = regexp.exec(text)
}
return position
} }
} }

View File

@@ -6,7 +6,7 @@ export default function (state, type) {
marks = [...marks, ...node.marks] marks = [...marks, ...node.marks]
}) })
const mark = marks.find(mark => mark.type.name === type.name) const mark = marks.find(markItem => markItem.type.name === type.name)
if (mark) { if (mark) {
return mark.attrs return mark.attrs

View File

@@ -28,7 +28,9 @@ export default function ({ schema, state, commands }) {
command, command,
} }
}) })
.reduce((actions, { name, active, attrs, command }) => ({ .reduce((actions, {
name, active, attrs, command,
}) => ({
...actions, ...actions,
[name]: { [name]: {
active, active,