I'm seeking guidance on utilizing the DOM in AdonisJS for a specific use case.
I understand that AdonisJS is primarily designed for server-side development and may not automatically include TypeScript definitions for the document object and other DOM elements, as these concepts are specific to the browser environment.
However, my current project involves using the Playwright library for PDF generation. I need to evaluate HTML classes through the $evaluate method, which requires access to DOM types. Even when attempting to use the JSDOM library, I encounter a definition error.
I'd greatly appreciate any assistance or insights into this matter. Thank you in advance; I'm a bit stuck at this stage.
import { inject } from '@adonisjs/core'
import { HttpContext } from '@adonisjs/core/http'
import { chromium } from 'playwright'
import { ConfigPdfInteface } from '../interface/config_pdf_interface.js'
import { JSDOM } from 'jsdom'
@inject()
export default class PlaywrightService {
constructor(protected ctx: HttpContext) {}
async generatePdfPlaywright(
response: HttpContext['response'],
path: string,
documents: any,
config: ConfigPdfInteface
) {
try {
const browser = await chromium.launch({
headless: true,
})
const page = await browser.newPage()
await page.emulateMedia({ media: 'print' })
const html = await this.ctx.view.render(`${path}`, documents)
await page.setContent(html, {
waitUntil: 'networkidle',
})
const selector = '#someSelector'
const bodySize = await page.evaluate((p) => {
const { document } = new JSDOM(html).window
console.log('document', document)
const body = document?.querySelector(p)
const minHeight = window.getComputedStyle(body!).minHeight
return minHeight
}, selector)
console.log('bodySize', bodySize)
const pdfBuffer = await page.pdf(config)
await browser.close()
response.header('Content-type', 'application/pdf')
response.header('Content-Disposition', 'inline; filename=example.pdf')
response.send(pdfBuffer)
} catch (error) {
console.error(error)
response.status(500).send('PDF Error')
}
}
}
Error
page.evaluate: ReferenceError: JSDOM is not defined
at eval (eval at evaluate (:226:30), <anonymous>:2:38)
at UtilityScript.evaluate (<anonymous>:228:17)
at UtilityScript.<anonymous> (<anonymous>:1:44)
at PlaywrightService.generatePdfPlaywright
I'm not sure what you're trying to accomplish (seems likely a XY problem), but in any case, there's no sense in running JSDOM in a browser. The whole point of JSDOM is to simulate the browser DOM in Node. But with Playwright, you get access to the real DOM in an actual webpage. Use it--that's the purpose of Playwright!
Even if you wanted to use JSDOM in the browser, you'd have to explicitly add a script tag or pass the variable into evaluate
(it wouldn't work since you can only pass serializable values). The variable JSDOM
will be undefined once the evaluate
callback is executed in the browser process.
So
const selector = '#someSelector'
const bodySize = await page.evaluate((p) => {
const { document } = new JSDOM(html).window
console.log('document', document)
const body = document?.querySelector(p)
const minHeight = window.getComputedStyle(body!).minHeight
return minHeight
}, selector)
becomes:
const selector = "#someSelector";
const bodySize = await page.$eval(selector, el =>
getComputedStyle(el).minHeight
);
Minimal, runnable example:
const playwright = require("playwright"); // ^1.42.1
const html = `<!DOCTYPE html><html><body>
<p style="min-height: 42px"></p></body></html>`;
let browser;
(async () => {
browser = await playwright.firefox.launch();
const page = await browser.newPage();
await page.setContent(html);
const selector = "p";
const bodySize = await page.$eval(selector, el =>
getComputedStyle(el).minHeight
);
console.log(bodySize); // => 42px
})()
.catch(err => console.error(err))
.finally(() => browser?.close());
If selector
can't be found, $eval
will throw. You can catch it and handle accordingly if you expect it to be missing, or wrap the code in a condition:
const selector = "#someSelector";
const el = await page.$(selector);
if (el) {
const bodySize = await el.evaluate(el =>
getComputedStyle(el).minHeight
);
}
or wait for it:
const selector = "#someSelector";
const el = await page.waitForSelector(selector);
const bodySize = await el.evaluate(el =>
getComputedStyle(el).minHeight
);
But bodySize
is never used in your code.