Skip to content

Instantly share code, notes, and snippets.

@smmoosavi
Last active February 15, 2022 22:06
Show Gist options
  • Save smmoosavi/521077d41dc92023a2d4f423d266ea48 to your computer and use it in GitHub Desktop.
Save smmoosavi/521077d41dc92023a2d4f423d266ea48 to your computer and use it in GitHub Desktop.
GraphQL schema to select html elements
var { graphql, GraphQLSchema, GraphQLString, GraphQLObjectType, GraphQLNonNull, GraphQLList } = require('graphql');
// root query
const RootQuery = new GraphQLObjectType({
name: 'RootQuery',
fields: () => QueryFields,
});
const QueryFields = {};
// element type
const GqlElement = new GraphQLObjectType({
name: 'ELement',
fields: () => ELementFields,
});
const ELementFields = {};
// element query
QueryFields.root = {
type: GqlElement,
resolve: ({ rootElement }) => {
return { el: rootElement }
}
}
// select / select all / text / attr
ELementFields.select = {
type: GqlElement,
args: {
selector: { type: new GraphQLNonNull(GraphQLString) },
},
resolve: async ({ el }, { selector }, context, info) => {
const next = await context.driver.select(el, selector);
if (!next) {
return null;
}
return { el: next }
}
}
ELementFields.selectAll = {
type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GqlElement))),
args: {
selector: { type: new GraphQLNonNull(GraphQLString) },
},
resolve: async ({ el }, { selector }, context, info) => {
const next = await context.driver.selectAll(el, selector);
if (!next) {
return null
}
return next.map((el) => ({ el }))
}
}
ELementFields.text = {
type: GraphQLString,
resolve: async ({ el }, args, context, info) => {
const text = await context.driver.text(el);
return text
}
}
ELementFields.prop = {
type: GraphQLString,
args: {
name: { type: new GraphQLNonNull(GraphQLString) },
},
resolve: async ({ el }, { name }, context, info) => {
const value = await context.driver.prop(el, name);
return value
}
}
ELementFields.attr = {
type: GraphQLString,
args: {
name: { type: new GraphQLNonNull(GraphQLString) },
},
resolve: async ({ el }, { name }, context, info) => {
const value = await context.driver.attr(el, name);
return value
}
}
const schema = new GraphQLSchema({ query: RootQuery });
const puppeteerContext = {
driver: {
select: async (el, selector) => el.$(selector),
selectAll: async (el, selector) => el.$$(selector),
text: async (el) => el.evaluate(e => e.innerText),
prop: async (el, propName) => el.evaluate((e, name) => e[name], propName),
attr: async (el, attrName) => el.evaluate((e, name) => e.getAttribute(name), attrName),
}
}
function exec(source, variableValues, rootElement) {
return graphql({ source, schema, variableValues, rootValue: { rootElement }, contextValue: puppeteerContext });
}
module.exports = { exec };
query {
root {
title: select(selector: "#title") { text }
price: select(selector: "#price") { text value: attr(name: "data-value")}
sections: selectAll(selector: "#specs section") {
header: select(selector: ".section-header") { text }
rows: selectAll(selector: ".attribute-row") {
label: select(selector: ".attribute-label") { text }
value: select(selector: ".attribute-value") { text }
link: select(selector: "a") { href: prop(name: "href") }
}
}
}
}
const page = await browser.newPage();
await page.goto(url);
await exec(query, {}, page)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment