Skip to content

Commit

Permalink
Replace throw Error usage with assert, use custom MobiledocError
Browse files Browse the repository at this point in the history
This allows upstream consumers to detect and handle MobiledocKit errors
specially if needed.
  • Loading branch information
bantic committed Jan 13, 2016
1 parent 819085b commit ea09b52
Show file tree
Hide file tree
Showing 18 changed files with 96 additions and 53 deletions.
16 changes: 6 additions & 10 deletions src/js/editor/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,8 @@ const CALLBACK_QUEUES = {
*/
class Editor {
constructor(options={}) {
if (!options || options.nodeType) {
throw new Error('editor create accepts an options object. For legacy usage passing an element for the first argument, consider the `html` option for loading DOM or HTML posts. For other cases call `editor.render(domNode)` after editor creation');
}
assert('editor create accepts an options object. For legacy usage passing an element for the first argument, consider the `html` option for loading DOM or HTML posts. For other cases call `editor.render(domNode)` after editor creation',
(options && !options.nodeType));
this._elementListeners = [];
this._views = [];
this.isEditable = null;
Expand Down Expand Up @@ -207,9 +206,7 @@ class Editor {
* @public
*/
registerExpansion(expansion) {
if (!validateExpansion(expansion)) {
throw new Error('Expansion is not valid');
}
assert('Expansion is not valid', validateExpansion(expansion));
this.expansions.push(expansion);
}

Expand All @@ -223,9 +220,7 @@ class Editor {
*/
registerKeyCommand(rawKeyCommand) {
const keyCommand = buildKeyCommand(rawKeyCommand);
if (!validateKeyCommand(keyCommand)) {
throw new Error('Key Command is not valid');
}
assert('Key Command is not valid', validateKeyCommand(keyCommand));
this.keyCommands.unshift(keyCommand);
}

Expand Down Expand Up @@ -609,7 +604,8 @@ class Editor {
}

const methodName = `handle${capitalize(eventName)}`;
if (!this[methodName]) { throw new Error(`No handler for ${eventName}`); }
assert(`No handler "${methodName}" for ${eventName}`, !!this[methodName]);

this[methodName](...args);
}

Expand Down
17 changes: 8 additions & 9 deletions src/js/editor/post.js
Original file line number Diff line number Diff line change
Expand Up @@ -437,15 +437,14 @@ class PostEditor {
const { section } = position;
let nextPosition = position.clone();

if (!section.isMarkerable) {
throw new Error('Cannot join non-markerable section to next section');
} else {
const next = section.immediatelyNextMarkerableSection();
if (next) {
section.join(next);
this._markDirty(section);
this.removeSection(next);
}
assert('Cannot join non-markerable section to next section',
section.isMarkerable);

const next = section.immediatelyNextMarkerableSection();
if (next) {
section.join(next);
this._markDirty(section);
this.removeSection(next);
}

return nextPosition;
Expand Down
4 changes: 3 additions & 1 deletion src/js/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import Editor from './editor/editor';
import ImageCard from './cards/image';
import Range from './utils/cursor/range';
import Error from './utils/mobiledoc-error';

const Mobiledoc = {
Editor,
ImageCard,
Range
Range,
Error
};

export function registerGlobal(global) {
Expand Down
8 changes: 4 additions & 4 deletions src/js/models/_markerable.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ export default class Markerable extends Section {
* @return {Number} The offset relative to the start of this section
*/
offsetOfMarker(marker, markerOffset) {
if (marker.section !== this) {
throw new Error(`Cannot get offsetOfMarker for marker that is not child of this`);
}
assert(`Cannot get offsetOfMarker for marker that is not child of this`,
marker.section === this);

// FIXME it is possible, when we get a cursor position before having finished reparsing,
// for markerOffset to be > marker.length. We shouldn't rely on this functionality.

Expand Down Expand Up @@ -108,7 +108,7 @@ export default class Markerable extends Section {
}

splitAtMarker(/*marker, offset=0*/) {
throw new Error('splitAtMarker must be implemented by sub-class');
assert('splitAtMarker must be implemented by sub-class', false);
}

/**
Expand Down
3 changes: 2 additions & 1 deletion src/js/models/_section.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import LinkedItem from '../utils/linked-item';
import assert from '../utils/assert';

function unimplementedMethod(methodName, me) {
throw new Error(`\`${methodName}()\` must be implemented by ${me.constructor.name}`);
assert(`\`${methodName}()\` must be implemented by ${me.constructor.name}`,
false);
}

export default class Section extends LinkedItem {
Expand Down
4 changes: 2 additions & 2 deletions src/js/models/post-node-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ import {
import {
DEFAULT_TAG_NAME as DEFAULT_MARKUP_SECTION_TAG_NAME
} from '../models/markup-section';

import {
DEFAULT_TAG_NAME as DEFAULT_LIST_SECTION_TAG_NAME
} from '../models/list-section';
import assert from '../utils/assert';

function cacheKey(tagName, attributes) {
return `${normalizeTagName(tagName)}-${objectToSortedKVArray(attributes).join('-')}`;
Expand Down Expand Up @@ -54,7 +54,7 @@ export default class PostNodeBuilder {
case MARKUP_SECTION_TYPE:
return this.createMarkupSection(tagName, markers);
default:
throw new Error(`Cannot create markerable section of type ${type}`);
assert(`Cannot create markerable section of type ${type}`, false);
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/js/models/render-node.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import LinkedItem from 'mobiledoc-kit/utils/linked-item';
import LinkedList from 'mobiledoc-kit/utils/linked-list';
import { containsNode } from 'mobiledoc-kit/utils/dom-utils';
import assert from 'mobiledoc-kit/utils/assert';

export default class RenderNode extends LinkedItem {
constructor(postNode, renderTree) {
Expand All @@ -14,9 +15,8 @@ export default class RenderNode extends LinkedItem {
this.renderTree = renderTree;
}
isAttached() {
if (!this.element) {
throw new Error('Cannot check if a renderNode is attached without an element.');
}
assert('Cannot check if a renderNode is attached without an element.',
!!this.element);
return containsNode(this.renderTree.rootElement, this.element);
}
get childNodes() {
Expand Down
5 changes: 3 additions & 2 deletions src/js/parsers/mobiledoc/0-2.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
MOBILEDOC_CARD_SECTION_TYPE
} from 'mobiledoc-kit/renderers/mobiledoc/0-2';
import { kvArrayToObject, filter } from "../../utils/array-utils";
import assert from 'mobiledoc-kit/utils/assert';

/*
* Parses from mobiledoc -> post
Expand Down Expand Up @@ -32,7 +33,7 @@ export default class MobiledocParser {

return post;
} catch (e) {
throw new Error(`Unable to parse mobiledoc: ${e.message}`);
assert(`Unable to parse mobiledoc: ${e.message}`, false);
}
}

Expand Down Expand Up @@ -65,7 +66,7 @@ export default class MobiledocParser {
this.parseListSection(section, post);
break;
default:
throw new Error(`Unexpected section type ${type}`);
assert(`Unexpected section type ${type}`, false);
}
}

Expand Down
4 changes: 3 additions & 1 deletion src/js/parsers/mobiledoc/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import MobiledocParser_0_2 from './0-2';
import { MOBILEDOC_VERSION } from 'mobiledoc-kit/renderers/mobiledoc/0-2';
import assert from 'mobiledoc-kit/utils/assert';

function parseVersion(mobiledoc) {
return mobiledoc.version;
Expand All @@ -12,7 +13,8 @@ export default {
case MOBILEDOC_VERSION:
return new MobiledocParser_0_2(builder).parse(mobiledoc);
default:
throw new Error(`Unknown version of mobiledoc parser requested: ${version}`);
assert(`Unknown version of mobiledoc parser requested: ${version}`,
false);
}
}
};
5 changes: 3 additions & 2 deletions src/js/parsers/section.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@ export default class SectionParser {
this.parseElementNode(node);
break;
default:
throw new Error(`parseNode got unexpected element type ${node.nodeType} ` + node);
assert(`parseNode got unexpected element type ${node.nodeType} ` + node,
false);
}
}

Expand Down Expand Up @@ -292,7 +293,7 @@ export default class SectionParser {
section._inferredTagName = inferredTagName;
break;
default:
throw new Error('Cannot parse section from element');
assert('Cannot parse section from element', false);
}

return section;
Expand Down
10 changes: 3 additions & 7 deletions src/js/renderers/editor-dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ class Visitor {

let destroyHooks = {
[POST_TYPE](/*renderNode, post*/) {
throw new Error('post destruction is not supported by the renderer');
assert('post destruction is not supported by the renderer', false);
},

[MARKUP_SECTION_TYPE](renderNode, section) {
Expand Down Expand Up @@ -366,9 +366,7 @@ function removeDestroyedChildren(parentNode, forceRemoval=false) {
if (child.isRemoved || forceRemoval) {
removeDestroyedChildren(child, true);
method = child.postNode.type;
if (!destroyHooks[method]) {
throw new Error(`editor-dom cannot destroy "${method}"`);
}
assert(`editor-dom cannot destroy "${method}"`, !!destroyHooks[method]);
destroyHooks[method](child, child.postNode);
parentNode.childNodes.remove(child);
}
Expand Down Expand Up @@ -427,9 +425,7 @@ export default class Renderer {
postNode = renderNode.postNode;

method = postNode.type;
if (!this.visitor[method]) {
throw new Error(`EditorDom visitor cannot handle type ${method}`);
}
assert(`EditorDom visitor cannot handle type ${method}`, !!this.visitor[method]);
this.visitor[method](renderNode, postNode,
(...args) => this.visit(renderTree, ...args));
renderNode.markClean();
Expand Down
3 changes: 2 additions & 1 deletion src/js/renderers/mobiledoc/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import MobiledocRenderer_0_2, { MOBILEDOC_VERSION } from './0-2';
import assert from 'mobiledoc-kit/utils/assert';

export { MOBILEDOC_VERSION };

Expand All @@ -10,7 +11,7 @@ export default {
case null:
return MobiledocRenderer_0_2.render(post);
default:
throw new Error(`Unknown version of mobiledoc renderer requested: ${version}`);
assert(`Unknown version of mobiledoc renderer requested: ${version}`, false);
}
}
};
4 changes: 3 additions & 1 deletion src/js/utils/assert.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import MobiledocError from './mobiledoc-error';

export default function(message, conditional) {
if (!conditional) {
throw new Error(message);
throw new MobiledocError(message);
}
}
5 changes: 2 additions & 3 deletions src/js/utils/compiler.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { forEach } from './array-utils';
import assert from './assert';

export function visit(visitor, node, opcodes) {
const method = node.type;
if (!visitor[method]) {
throw new Error(`Cannot visit unknown type ${method}`);
}
assert(`Cannot visit unknown type ${method}`, !!visitor[method]);
visitor[method](node, opcodes);
}

Expand Down
6 changes: 3 additions & 3 deletions src/js/utils/dom-utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { forEach } from './array-utils';
import assert from './assert';

export const NODE_TYPES = {
ELEMENT: 1,
Expand Down Expand Up @@ -99,9 +100,8 @@ function findOffsetInElement(elementNode, textNode, offsetInTextNode=0) {
offset += _textNode.textContent.length;
}
});
if (!found) {
throw new Error('Unable to find offset of text node in element, it is not a child.');
}
assert('Unable to find offset of text node in element, it is not a child.',
found);
return offset;
}

Expand Down
6 changes: 3 additions & 3 deletions src/js/utils/element-map.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import assert from 'mobiledoc-kit/utils/assert';

// start at one to make the falsy semantics easier
let uuidGenerator = 1;

Expand All @@ -19,9 +21,7 @@ class ElementMap {
return null;
}
remove(key) {
if (!key._uuid) {
throw new Error('tried to fetch a value for an element not seen before');
}
assert('tried to fetch a value for an element not seen before', !!key._uuid);
delete this._map[key._uuid];
}

Expand Down
25 changes: 25 additions & 0 deletions src/js/utils/mobiledoc-error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
var errorProps = [
'description',
'fileName',
'lineNumber',
'message',
'name',
'number',
'stack'
];

function MobiledocError() {
let tmp = Error.apply(this, arguments);

if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor);
}
// Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
for (let idx = 0; idx < errorProps.length; idx++) {
this[errorProps[idx]] = tmp[errorProps[idx]];
}
}

MobiledocError.prototype = Object.create(Error.prototype);

export default MobiledocError;
18 changes: 18 additions & 0 deletions tests/unit/utils/assert-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Helpers from '../../test-helpers';
import mobiledocAssert from 'mobiledoc-kit/utils/assert';
import MobiledocError from 'mobiledoc-kit/utils/mobiledoc-error';

const {module, test} = Helpers;

module('Unit: Utils: assert');

test('#throws a MobiledocError when conditional is false', (assert) => {
try {
mobiledocAssert('The message', false);
} catch (e) {
assert.ok(true, 'caught error');
assert.equal(e.message, 'The message');
assert.ok(e instanceof MobiledocError, 'e instanceof MobiledocError');
}
});

0 comments on commit ea09b52

Please sign in to comment.