Comparing version
@@ -1,30 +0,7 @@ | ||
declare type QueryData = { | ||
[property: string]: Text | Attr | Html | List<any>; | ||
}; | ||
declare type Text = { | ||
type: "TEXT"; | ||
result: string; | ||
selector: string; | ||
}; | ||
declare type Attr = { | ||
type: "ATTR"; | ||
result: string; | ||
attribute: string; | ||
selector: string; | ||
}; | ||
declare type List<T extends object> = { | ||
type: "LIST"; | ||
result: Array<T>; | ||
selector: string; | ||
data: QueryData; | ||
}; | ||
declare type Html = { | ||
type: "HTML"; | ||
result: string; | ||
selector: string; | ||
}; | ||
import { Attr, Html, List, Text, QueryData, Exists } from "./type"; | ||
declare type GetTypeFromQuery<Q extends QueryData> = { | ||
[P in keyof Q]: Q[P]["result"]; | ||
[P in keyof Q]: Q[P]["convert"] extends (data: string) => infer R ? R : Q[P]["convert"]; | ||
}; | ||
export declare const scrap: <Q extends QueryData>(html: string, query: Q) => GetTypeFromQuery<Q>; | ||
declare type DefaultFn = (data: string) => string; | ||
export declare const Q: { | ||
@@ -35,3 +12,3 @@ /** | ||
*/ | ||
text: (selector: string) => Text; | ||
text: (selector: string) => Text<DefaultFn>; | ||
/** | ||
@@ -42,3 +19,3 @@ * Get html attribute | ||
*/ | ||
attr: (selector: string, attribute: string) => Attr; | ||
attr: (selector: string, attribute: string) => Attr<DefaultFn>; | ||
/** | ||
@@ -48,4 +25,9 @@ * Get html content | ||
*/ | ||
html: (selector: string) => Html; | ||
html: (selector: string) => Html<DefaultFn>; | ||
/** | ||
* Check if element exists | ||
* @param selector - css selector | ||
*/ | ||
exists: (selector: string) => Exists; | ||
/** | ||
* Get list of items | ||
@@ -52,0 +34,0 @@ * @param selector - css selector for list of items |
@@ -11,3 +11,3 @@ "use strict"; | ||
if (val.selector === "") { | ||
// Get text from root item | ||
// Get text from root element | ||
ref[prop] = $(context).text(); | ||
@@ -23,2 +23,3 @@ } | ||
if (val.selector === "") { | ||
// Get attribute from root element | ||
ref[prop] = $(context).attr(val.attribute); | ||
@@ -34,2 +35,3 @@ } | ||
if (val.selector === "") { | ||
// Get html from root element | ||
ref[prop] = $(context).html(); | ||
@@ -43,2 +45,8 @@ } | ||
} | ||
case "EXISTS": { | ||
// TODO: selector cannot be "" | ||
var el = $(val.selector, context); | ||
ref[prop] = el.length > 0 ? true : false; | ||
break; | ||
} | ||
case "LIST": { | ||
@@ -72,3 +80,7 @@ var result = []; | ||
*/ | ||
text: function (selector) { return ({ type: "TEXT", selector: selector, result: "" }); }, | ||
text: function (selector) { return ({ | ||
type: "TEXT", | ||
selector: selector, | ||
convert: function (data) { return data; } | ||
}); }, | ||
/** | ||
@@ -82,3 +94,3 @@ * Get html attribute | ||
selector: selector, | ||
result: "", | ||
convert: function (data) { return data; }, | ||
attribute: attribute | ||
@@ -91,7 +103,16 @@ }); }, | ||
html: function (selector) { return ({ | ||
type: 'HTML', | ||
type: "HTML", | ||
selector: selector, | ||
result: "" | ||
convert: function (data) { return data; } | ||
}); }, | ||
/** | ||
* Check if element exists | ||
* @param selector - css selector | ||
*/ | ||
exists: function (selector) { return ({ | ||
type: "EXISTS", | ||
selector: selector, | ||
convert: true | ||
}); }, | ||
/** | ||
* Get list of items | ||
@@ -101,3 +122,3 @@ * @param selector - css selector for list of items | ||
*/ | ||
list: function (selector, data) { return ({ type: "LIST", result: [], selector: selector, data: data }); } | ||
list: function (selector, data) { return ({ type: "LIST", convert: [], selector: selector, data: data }); } | ||
}; |
import { load } from "cheerio"; | ||
import { Attr, Html, List, Text, QueryData, Exists } from "./type"; | ||
type QueryData = { | ||
[property: string]: Text | Attr | Html | List<any>; | ||
}; | ||
type Query = QueryData; | ||
type Text = { | ||
// --- Internal --- | ||
type: "TEXT"; | ||
result: string; | ||
// ---Additional--- | ||
selector: string; | ||
type GetTypeFromQuery<Q extends QueryData> = { | ||
[P in keyof Q]: Q[P]["convert"] extends (data: string) => infer R | ||
? R | ||
:Q[P]["convert"] | ||
}; | ||
type Attr = { | ||
type: "ATTR"; | ||
result: string; | ||
attribute: string; | ||
selector: string; | ||
}; | ||
type List<T extends object> = { | ||
// --- Internal --- | ||
type: "LIST"; | ||
result: Array<T>; | ||
// ---Additional--- | ||
selector: string; | ||
data: QueryData; | ||
}; | ||
type Html = { | ||
// --- Internal --- | ||
type: "HTML"; | ||
result: string; | ||
// ---Additional--- | ||
selector: string; | ||
}; | ||
type Query = QueryData; | ||
type GetTypeFromQuery<Q extends QueryData> = { [P in keyof Q]: Q[P]["result"] }; | ||
const scrapObject = <Q extends QueryData>( | ||
@@ -54,3 +22,3 @@ $: CheerioStatic, | ||
if (val.selector === "") { | ||
// Get text from root item | ||
// Get text from root element | ||
ref[prop] = $(context).text(); | ||
@@ -65,2 +33,3 @@ } else { | ||
if (val.selector === "") { | ||
// Get attribute from root element | ||
ref[prop] = $(context).attr(val.attribute); | ||
@@ -75,2 +44,3 @@ } else { | ||
if (val.selector === "") { | ||
// Get html from root element | ||
ref[prop] = $(context).html(); | ||
@@ -82,3 +52,9 @@ } else { | ||
break; | ||
} | ||
} | ||
case "EXISTS": { | ||
// TODO: selector cannot be "" | ||
const el = $(val.selector, context); | ||
ref[prop] = el.length > 0 ? true : false; | ||
break; | ||
} | ||
case "LIST": { | ||
@@ -112,2 +88,3 @@ const result: GetTypeFromQuery<typeof val.data>[] = []; | ||
type DefaultFn = (data: string) => string; | ||
export const Q = { | ||
@@ -118,3 +95,7 @@ /** | ||
*/ | ||
text: (selector: string): Text => ({ type: "TEXT", selector, result: "" }), | ||
text: (selector: string): Text<DefaultFn> => ({ | ||
type: "TEXT", | ||
selector, | ||
convert: (data) => data | ||
}), | ||
@@ -126,9 +107,9 @@ /** | ||
*/ | ||
attr: (selector: string, attribute: string): Attr => ({ | ||
attr: (selector: string, attribute: string): Attr<DefaultFn> => ({ | ||
type: "ATTR", | ||
selector, | ||
result: "", | ||
convert: (data) => data, | ||
attribute | ||
}), | ||
/** | ||
@@ -138,9 +119,19 @@ * Get html content | ||
*/ | ||
html: (selector: string): Html => ({ | ||
type: 'HTML', | ||
html: (selector: string): Html<DefaultFn> => ({ | ||
type: "HTML", | ||
selector, | ||
result: "" | ||
}), | ||
convert: (data) => data | ||
}), | ||
/** | ||
* Check if element exists | ||
* @param selector - css selector | ||
*/ | ||
exists: (selector: string): Exists => ({ | ||
type: "EXISTS", | ||
selector, | ||
convert: true | ||
}), | ||
/** | ||
* Get list of items | ||
@@ -153,3 +144,3 @@ * @param selector - css selector for list of items | ||
data: Q | ||
): List<GetTypeFromQuery<Q>> => ({ type: "LIST", result: [], selector, data }) | ||
): List<GetTypeFromQuery<Q>> => ({ type: "LIST", convert: [], selector, data }) | ||
}; |
{ | ||
"name": "scrapq", | ||
"version": "1.0.0", | ||
"version": "1.0.1", | ||
"description": "Lightweight Typescript library for scrapping html", | ||
@@ -29,3 +29,3 @@ "main": "./dist/Index.js", | ||
"prettier": "^1.13.7", | ||
"typescript": "^2.9.2" | ||
"typescript": "^3.1.0-dev.20180721" | ||
}, | ||
@@ -32,0 +32,0 @@ "dependencies": { |
@@ -7,3 +7,3 @@ # ScrapQ | ||
Lightweight Typescript library for scrapping html. | ||
Lightweight Typescript library for scrapping html with **type inference**. | ||
@@ -13,4 +13,4 @@ ## About | ||
There are plenty scrapping libs out there, but only few with full Typescript support - Typescript will infer type based | ||
on your query. This is small library with only one purpuse to provide scrapping in human readable format with full | ||
Typescript support like intellisense. | ||
on your query. This is small library with only one purpose to provide scrapping in human readable format with full | ||
Typescript support like intellisense and type inference. | ||
@@ -93,3 +93,2 @@ ## Examples | ||
`Q.attr(selector: string, htmlAttribute: string)` | ||
@@ -103,4 +102,8 @@ | ||
`Q.exists(selector: string)` | ||
get `true/false` if element exists | ||
`Q.list(selector: string, query: Query)` | ||
get list of items |
@@ -7,3 +7,3 @@ import { scrap, Q } from '../lib/Index'; | ||
<li><span>Guten Tag</span></li> | ||
<li><span>Ciao</span></li> | ||
<li><span class="msg">Ciao</span></li> | ||
<li><span>Bonjour</span></li> | ||
@@ -18,10 +18,10 @@ </ul> | ||
title: Q.text('h1.title') | ||
}); | ||
}); | ||
expect(result).toEqual({ title: 'Hello'}); | ||
}); | ||
it('should scrap attributes from <h1/>', () => { | ||
const result = scrap(STR_TO_SCRAP, { | ||
title: Q.attr('h1.title', 'class') | ||
}); | ||
}); | ||
expect(result).toEqual({ title: 'title'}); | ||
@@ -40,2 +40,28 @@ }); | ||
it('should exists .title', () => { | ||
const result = scrap(STR_TO_SCRAP, { | ||
hasTitle: Q.exists('h1.title') | ||
}); | ||
expect(result.hasTitle).toBe(true); | ||
}); | ||
it('should not exists .castle', () => { | ||
const result = scrap(STR_TO_SCRAP, { | ||
hasCastle: Q.exists('.castle') | ||
}); | ||
expect(result.hasCastle).toBe(false); | ||
}); | ||
it('should exists .msg inside list', () => { | ||
const result = scrap(STR_TO_SCRAP, { | ||
items: Q.list('li', { | ||
hasMsg: Q.exists('span.msg') | ||
}) | ||
}); | ||
expect(result.items.length).toBe(3); | ||
expect(result.items[0].hasMsg).toBe(false); | ||
expect(result.items[1].hasMsg).toBe(true); | ||
expect(result.items[2].hasMsg).toBe(false); | ||
}); | ||
it('should scrap text from <li><span/>', () => { | ||
@@ -42,0 +68,0 @@ const result = scrap(STR_TO_SCRAP, { |
@@ -0,0 +0,0 @@ { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
21020
15.84%13
44.44%486
26.56%106
2.91%