Skip to content

Commit 267c60e

Browse files
committed
Implement null-filling on no match
1 parent 84d9d00 commit 267c60e

File tree

2 files changed

+38
-14
lines changed

2 files changed

+38
-14
lines changed

lib/errors.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
11

2+
class ModelError extends Error {
3+
constructor(...args) {
4+
super(...args)
5+
6+
if (Error.captureStackTrace) Error.captureStackTrace(this, ModelError)
7+
8+
this.name = 'ModelError'
9+
}
10+
}
11+
212
class UnknownFilterError extends Error {
313
constructor(...args) {
414
super(...args)
@@ -10,5 +20,6 @@ class UnknownFilterError extends Error {
1020
}
1121

1222
module.exports = {
23+
ModelError,
1324
UnknownFilterError,
1425
}

lib/extract.js

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const cssSelect = require('css-select')
22
const { DomHandler, Parser } = require('htmlparser2')
33

4-
const { UnknownFilterError } = require('./errors')
4+
const { ModelError, UnknownFilterError } = require('./errors')
55
const filters = require('./filters')
66
const getters = require('./getters')
77
const { DOMHANDLER_OPTIONS, HTMLPARSER2_OPTIONS } = require('./options')
@@ -22,7 +22,7 @@ const {
2222
*/
2323

2424
function extract(html, model, options = {}) {
25-
// Using Object.assig instead of object spread removes the need of null checks.
25+
// Using Object.assign instead of object spread removes the need of null checks.
2626
const parserOptions = Object.assign({}, HTMLPARSER2_OPTIONS, options.htmlparser2)
2727
const handlerOptions = Object.assign({}, DOMHANDLER_OPTIONS, options.domhandler)
2828

@@ -44,31 +44,44 @@ function extract(html, model, options = {}) {
4444
*/
4545

4646
function getItem(dom, item) {
47-
let data
48-
4947
if (isArray(item)) {
5048
const query = parseQuery(item[0])
5149
const matches = cssSelect.selectAll(query.selector, dom)
5250

51+
if (!matches || !matches.length) return null
52+
5353
if (isArray(item[1]) || isObject(item[1])) {
54-
data = matches.map((context) => getItem(context, item[1]))
55-
} else {
56-
data = matches.map(resolveGetter(getters, query)).map(applyFilters.bind(null, filters, query))
54+
return matches.map((context) => getItem(context, item[1]))
5755
}
58-
} else if (isObject(item)) {
59-
data = Object.keys(item).reduce((acc, key) => {
56+
57+
return matches.map((node) => {
58+
const data = resolveGetter(getters, query)(node)
59+
return applyFilters(filters, query, data)
60+
})
61+
}
62+
63+
if (isObject(item)) {
64+
return Object.keys(item).reduce((acc, key) => {
6065
acc[key] = getItem(dom, item[key])
6166
return acc
6267
}, {})
63-
} else if (isString(item)) {
68+
}
69+
70+
if (isString(item)) {
6471
const query = parseQuery(item)
65-
const matches = cssSelect.selectOne(query.selector, dom)
72+
const match = cssSelect.selectOne(query.selector, dom)
73+
74+
if (!match) return null
75+
76+
const data = resolveGetter(getters, query)(match)
6677

67-
data = resolveGetter(getters, query)(matches)
68-
data = applyFilters(filters, query, data)
78+
return applyFilters(filters, query, data)
6979
}
7080

71-
return data
81+
const unsupportedType = item === null ? 'null' : typeof item
82+
throw new ModelError(
83+
`The model has to be a string, an Object or an Array; got ${unsupportedType} instead.`
84+
)
7285
}
7386

7487
/**

0 commit comments

Comments
 (0)