Skip to content

Commit d45585f

Browse files
authored
perf(function): avoid page template if it's not used (#593)
1 parent 44f9a1f commit d45585f

4 files changed

Lines changed: 99 additions & 14 deletions

File tree

packages/function/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,14 @@
3535
},
3636
"devDependencies": {
3737
"@browserless/test": "^10.6.0",
38+
"acorn": "~8.12.1",
39+
"acorn-walk": "~8.3.4",
3840
"ava": "5",
3941
"browserless": "^10.6.0",
4042
"lodash": "latest"
4143
},
4244
"engines": {
43-
"node": ">= 12"
45+
"node": ">= 20"
4446
},
4547
"files": [
4648
"src"

packages/function/src/function.js

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,10 @@
11
'use strict'
22

33
const isolatedFunction = require('isolated-function')
4-
5-
const createFn = code => `
6-
async (url, browserWSEndpoint, opts) => {
7-
const puppeteer = require('@cloudflare/puppeteer')
8-
const browser = await puppeteer.connect({ browserWSEndpoint })
9-
const page = (await browser.pages())[1]
10-
try {
11-
return await (${code})({ page, ...opts })
12-
} finally {
13-
await browser.disconnect()
14-
}
15-
}`
4+
const template = require('./template')
165

176
module.exports = async ({ url, code, vmOpts, browserWSEndpoint, ...opts }) => {
18-
const [fn, teardown] = isolatedFunction(createFn(code), { ...vmOpts, throwError: false })
7+
const [fn, teardown] = isolatedFunction(template(code), { ...vmOpts, throwError: false })
198
const result = await fn(url, browserWSEndpoint, opts)
209
await teardown()
2110
return result

packages/function/src/template.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
'use strict'
2+
3+
const walk = require('acorn-walk')
4+
const acorn = require('acorn')
5+
6+
module.exports = code => {
7+
const ast = acorn.parse(code, { ecmaVersion: 2023, sourceType: 'module' })
8+
9+
let isUsingPage = false
10+
11+
walk.simple(ast, {
12+
ObjectPattern (node) {
13+
node.properties.forEach(prop => {
14+
if (prop.type === 'Property' && prop.key.name === 'page') {
15+
isUsingPage = true
16+
}
17+
if (prop.type === 'RestElement' && prop.argument.name === 'page') {
18+
isUsingPage = true
19+
}
20+
})
21+
},
22+
MemberExpression (node) {
23+
if (node.property.name === 'page' || node.property.value === 'page') {
24+
isUsingPage = true
25+
}
26+
}
27+
})
28+
29+
if (!isUsingPage) return `async (url, _, opts) => (${code})(opts)`
30+
return `
31+
async (url, browserWSEndpoint, opts) => {
32+
const puppeteer = require('@cloudflare/puppeteer')
33+
const browser = await puppeteer.connect({ browserWSEndpoint })
34+
const page = (await browser.pages())[1]
35+
try {
36+
return await (${code})({ page, ...opts })
37+
} finally {
38+
await browser.disconnect()
39+
}
40+
}`
41+
}

packages/function/test/template.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
'use strict'
2+
3+
const test = require('ava')
4+
5+
const template = require('../src/template')
6+
7+
test('use a simplified template if page is not used', t => {
8+
{
9+
const code = () => {
10+
function isStrict () {
11+
return !this
12+
}
13+
return isStrict()
14+
}
15+
16+
t.is(template(code.toString()).includes('puppeteer'), false)
17+
}
18+
{
19+
const code = () => ({ page: 'title' })
20+
t.is(template(code.toString()).includes('puppeteer'), false)
21+
}
22+
{
23+
const code = () => 'page'
24+
t.is(template(code.toString()).includes('puppeteer'), false)
25+
}
26+
})
27+
28+
test('require puppeteer if page is used', t => {
29+
{
30+
const code = ({ page }) => page.title()
31+
t.is(template(code.toString()).includes('puppeteer'), true)
32+
}
33+
{
34+
const code = ({ page: p }) => p.title()
35+
t.is(template(code.toString()).includes('puppeteer'), true)
36+
}
37+
{
38+
const code = obj => obj.page.title()
39+
t.is(template(code.toString()).includes('puppeteer'), true)
40+
}
41+
{
42+
const code = obj => (() => obj.page.title())()
43+
t.is(template(code.toString()).includes('puppeteer'), true)
44+
}
45+
{
46+
const code = ({ ...page }) => page.title
47+
t.is(template(code.toString()).includes('puppeteer'), true)
48+
}
49+
{
50+
const code = obj => obj.page.title()
51+
t.is(template(code.toString()).includes('puppeteer'), true)
52+
}
53+
})

0 commit comments

Comments
 (0)