Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 20 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const browserless = require('browserless')()
browserless
.screenshot('http://example.com', { device: 'iPhone 6' })
.then(buffer => {
console.log(`your screenshot is here!`)
console.log('your screenshot is here!')
})
```

Expand Down Expand Up @@ -160,7 +160,7 @@ const browserless = require('browserless')
;(async () => {
const url = 'https://example.com'
const buffer = await browserless.pdf(url)
console.log(`PDF generated!`)
console.log('PDF generated!')
})()
```

Expand Down Expand Up @@ -229,7 +229,7 @@ const browserless = require('browserless')
;(async () => {
const url = 'https://example.com'
const buffer = await browserless.screenshot(url)
console.log(`Screenshot taken!`)
console.log('Screenshot taken!')
})()
```

Expand All @@ -249,6 +249,20 @@ Also, any [page.screenshot](https://github.com/puppeteer/puppeteer/blob/master/d

Additionally, you can setup:

##### codeScheme

type: `string`</br>
default: `'atom-dark'`

When this value is present and the response `'Content-Type'` header is `'json'`, it beautifies HTML markup using [Prism](https://prismjs.com).

![](https://i.imgur.com/uFfviX7.png)

The syntax highlight theme can be customized, being possible to setup:

- A [prism-themes](https://github.com/PrismJS/prism-themes/tree/master/themes) identifier (e.g., `'dracula'`).
- A remote URL (e.g., `'https://unpkg.com/prism-theme-night-owl'`).

##### element

type: `string` </br>
Expand Down Expand Up @@ -543,7 +557,7 @@ It can accept:
scripts: [
'https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.min.js',
'local-file.js',
`document.body.style.backgroundColor = 'red`
"document.body.style.backgroundColor = 'red'"
]
})
})()
Expand Down Expand Up @@ -575,7 +589,7 @@ It can accept:
styles: [
'https://cdn.jsdelivr.net/npm/hack@0.8.1/dist/dark.css',
'local-file.css',
`body { background: red; }`
'body { background: red; }'
]
})
})()
Expand Down Expand Up @@ -656,7 +670,7 @@ After that, the API is the same than **browserless**:
browserlessPool
.screenshot('http://example.com', { device: 'iPhone 6' })
.then(buffer => {
console.log(`your screenshot is here!`)
console.log('your screenshot is here!')
})
```

Expand Down
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,5 +97,10 @@
"package.json": [
"finepack"
]
},
"standard": {
"ignore": [
"packages/screenshot/src/pretty/prism.js"
]
}
}
1 change: 1 addition & 0 deletions packages/goto/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"@cliqz/adblocker-puppeteer": "~1.7.2",
"debug-logfmt": "~1.0.4",
"got": "~10.5.5",
"is-url-http": "~2.1.1",
"p-reflect": "~2.1.0",
"p-timeout": "~3.2.0",
"tldts": "~5.6.3"
Expand Down
93 changes: 44 additions & 49 deletions packages/goto/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const createDevices = require('@browserless/devices')
const { getDomain } = require('tldts')
const pReflect = require('p-reflect')
const pTimeout = require('p-timeout')
const isUrl = require('is-url-http')
const path = require('path')
const fs = require('fs')

Expand All @@ -20,8 +21,6 @@ const isEmpty = val => val == null || !(Object.keys(val) || val).length

const toArray = value => [].concat(value)

const isUrl = string => /^(https?|file):\/\/|^data:/.test(string)

const getInjectKey = (ext, value) =>
isUrl(value) ? 'url' : value.endsWith(`.${ext}`) ? 'path' : 'content'

Expand Down Expand Up @@ -138,6 +137,32 @@ const getMediaFeatures = ({ animations, colorScheme }) => {
return prefers
}

const injectScripts = (page, values, attributes) =>
Promise.all(
toArray(values).map(value =>
pReflect(
page.addScriptTag({
[getInjectKey('js', value)]: value,
...attributes
})
)
)
)

const injectStyles = (page, styles) =>
Promise.all(
toArray(styles).map(style =>
pReflect(
page.addStyleTag({
[getInjectKey('css', style)]: style
})
)
)
)

const forEachSelector = (page, selectors, fn) =>
toArray(selectors).map(selector => pReflect(page.$$eval(selector, fn)))

module.exports = ({ timeout, ...deviceOpts }) => {
const gotoTimeout = timeout * (1 / 4)
const getDevice = createDevices(deviceOpts)
Expand Down Expand Up @@ -219,19 +244,14 @@ module.exports = ({ timeout, ...deviceOpts }) => {
await page.evaluate(disableAnimations)
}

if (hide) {
debug({ hide })
await Promise.all(
toArray(hide).map(selector => pReflect(page.$$eval(selector, hideElements)))
)
}
debug({ hide, remove })

if (remove) {
debug({ remove })
await Promise.all(
toArray(remove).map(selector => pReflect(page.$$eval(selector, removeElements)))
)
}
await Promise.all(
[
hide && forEachSelector(page, hide, hideElements),
remove && forEachSelector(page, hide, removeElements)
].filter(Boolean)
)

if (click) {
for (const selector of toArray(click)) {
Expand All @@ -240,42 +260,15 @@ module.exports = ({ timeout, ...deviceOpts }) => {
}
}

if (modules) {
await Promise.all(
toArray(modules).map(m =>
pReflect(
page.addScriptTag({
[getInjectKey('js', m)]: m,
type: 'module'
})
)
)
)
}
debug({ modules, scripts, styles })

if (scripts) {
await Promise.all(
toArray(scripts).map(script =>
pReflect(
page.addScriptTag({
[getInjectKey('js', script)]: script
})
)
)
)
}

if (styles) {
await Promise.all(
toArray(styles).map(style =>
pReflect(
page.addStyleTag({
[getInjectKey('css', style)]: style
})
)
)
)
}
await Promise.all(
[
modules && injectScripts(page, modules, { type: 'modules' }),
scripts && injectScripts(page, scripts),
styles && injectStyles(page, styles)
].filter(Boolean)
)

if (scrollTo) {
debug({ scrollTo })
Expand All @@ -296,3 +289,5 @@ module.exports = ({ timeout, ...deviceOpts }) => {
}

module.exports.parseCookies = parseCookies
module.exports.injectScripts = injectScripts
module.exports.injectStyles = injectStyles
Binary file modified packages/screenshot/media/browser.sketch
Binary file not shown.
2 changes: 2 additions & 0 deletions packages/screenshot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
"@browserless/goto": "^6.4.2",
"got": "~10.5.5",
"is-url-http": "~2.1.1",
"mime-types": "~2.1.26",
"prism-themes": "~1.3.0",
"sharp": "~0.24.0",
"svg-gradient": "~1.0.2"
},
Expand Down
Binary file removed packages/screenshot/src/browser/dark.png
Binary file not shown.
Binary file removed packages/screenshot/src/browser/light.png
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,12 @@ const getBoundingClientRect = element => {
module.exports = ({ goto, ...gotoOpts } = {}) => {
goto = goto || createGoto(gotoOpts)

return async (page, url, opts = {}) => {
const { device: deviceId = 'macbook pro 13', overlay, element, fullPage, ...args } = opts

return async (page, url, { device = 'macbook pro 13', element, ...opts } = {}) => {
page.on('dialog', async dialog => {
await dialog.dismiss()
})

const { device } = await goto(page, { url, device: deviceId, ...args })
const { response } = await goto(page, { url, device, ...opts })

const screenshotOptions = {}

Expand All @@ -27,6 +25,6 @@ module.exports = ({ goto, ...gotoOpts } = {}) => {
screenshotOptions.fullPage = false
}

return { device, ...screenshotOptions }
return [screenshotOptions, response]
}
}
62 changes: 21 additions & 41 deletions packages/screenshot/src/index.js
Original file line number Diff line number Diff line change
@@ -1,53 +1,33 @@
'use strict'

const svgGradient = require('svg-gradient')
const isHttpUrl = require('is-url-http')
const sharp = require('sharp')
const path = require('path')
const got = require('got')
const { extension } = require('mime-types')

const createPreparePage = require('./prepare')
const createGoto = require('./goto')
const overlay = require('./overlay')
const pretty = require('./pretty')

const BROWSER_THEMES = {
dark: path.resolve(__dirname, 'browser/dark.png'),
light: path.resolve(__dirname, 'browser/light.png')
}

const getBackground = async (bg = 'transparent') => {
if (isHttpUrl(bg)) return got(bg).buffer()

if (!bg.includes('gradient')) {
bg = `linear-gradient(45deg, ${bg} 0%, ${bg} 100%)`
}

return Buffer.from(createSvgBackground(bg))
}

const createSvgBackground = css => svgGradient(css, { width: '2776px', height: '1910px' })
const isJSON = headers => extension(headers['content-type']) === 'json'

module.exports = gotoOpts => {
const preparePage = createPreparePage(gotoOpts)
const goto = createGoto(gotoOpts)

return page => async (url, { type = 'png', overlay = {}, ...opts } = {}) => {
const screenshotOpts = {
...opts,
...(await preparePage(page, url, { overlay, ...opts })),
type
}

const screenshot = await page.screenshot(screenshotOpts)

if (Object.keys(overlay).length === 0) return screenshot
return page => async (
url,
{ codeScheme = 'atom-dark', overlay: overlayOpts = {}, ...opts } = {}
) => {
const [screenshotOpts, response] = await goto(page, url, opts)

const { browser: theme, background } = overlay
const browserOverlay = BROWSER_THEMES[theme]

const inputs = browserOverlay
? [{ input: browserOverlay }, { input: screenshot }]
: [{ input: screenshot }]
if (codeScheme && isJSON(response.headers())) {
await pretty(page, response, { codeScheme, ...opts })
}

const image = sharp(await getBackground(background)).composite(inputs)
const screenshot = await page.screenshot({
...opts,
...screenshotOpts
})

return opts.path ? image.toFile(opts.path) : image.toBuffer()
return Object.keys(overlayOpts).length === 0
? screenshot
: overlay(screenshot, { ...opts, ...overlayOpts })
}
}
Binary file added packages/screenshot/src/overlay/dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 36 additions & 0 deletions packages/screenshot/src/overlay/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
'use strict'

const svgGradient = require('svg-gradient')
const isHttpUrl = require('is-url-http')
const sharp = require('sharp')
const path = require('path')
const got = require('got')

const createSvgBackground = css => svgGradient(css, { width: '2776px', height: '1910px' })

const getBackground = async (bg = 'transparent') => {
if (isHttpUrl(bg)) return got(bg).buffer()

if (!bg.includes('gradient')) {
bg = `linear-gradient(45deg, ${bg} 0%, ${bg} 100%)`
}

return Buffer.from(createSvgBackground(bg))
}

const BROWSER_THEMES = {
dark: path.resolve(__dirname, 'dark.png'),
light: path.resolve(__dirname, 'light.png')
}

module.exports = async (screenshot, { browser: theme, background, path }) => {
const browserOverlay = BROWSER_THEMES[theme]

const inputs = browserOverlay
? [{ input: browserOverlay }, { input: screenshot }]
: [{ input: screenshot }]

const image = sharp(await getBackground(background)).composite(inputs)

return path ? image.toFile(path) : image.toBuffer()
}
Binary file added packages/screenshot/src/overlay/light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading