mirror of https://github.com/alibaba/ice.git
140 lines
4.2 KiB
TypeScript
140 lines
4.2 KiB
TypeScript
import * as http from 'http';
|
|
import * as url from 'url';
|
|
import * as path from 'path';
|
|
import * as fse from 'fs-extra';
|
|
import type {
|
|
Page as PuppeteerPage,
|
|
Browser as PuppeteerBrowser,
|
|
WaitForOptions,
|
|
HTTPResponse,
|
|
} from 'puppeteer';
|
|
import puppeteer from 'puppeteer';
|
|
|
|
export interface Page extends PuppeteerPage {
|
|
html: () => Promise<string>;
|
|
$text: (selector: string, trim?: boolean) => Promise<string | null>;
|
|
$$text: (selector: string, trim?: boolean) => Promise<(string | null)[]>;
|
|
$attr: (selector: string, attr: string) => Promise<string | null>;
|
|
$$attr: (selector: string, attr: string) => Promise<(string | null)[]>;
|
|
push: (url: string, options?: WaitForOptions & {
|
|
referer?: string;
|
|
referrerPolicy?: string;
|
|
}) => Promise<HTTPResponse | null>;
|
|
}
|
|
|
|
interface BrowserOptions {
|
|
cwd?: string;
|
|
port?: number;
|
|
server?: http.Server;
|
|
}
|
|
|
|
export default class Browser {
|
|
private server: http.Server;
|
|
private browser: PuppeteerBrowser;
|
|
private baseUrl: string;
|
|
|
|
constructor(options: BrowserOptions) {
|
|
const { server } = options;
|
|
if (server) {
|
|
this.server = server;
|
|
} else {
|
|
const { cwd, port } = options;
|
|
this.server = this.createServer(cwd!, port!);
|
|
}
|
|
}
|
|
|
|
createServer(cwd: string, port: number) {
|
|
return http.createServer((req, res) => {
|
|
const reqUrl: string = req.url || '';
|
|
const pathname = `${cwd}${url.parse(reqUrl).pathname}`.split(path.sep).join('/');
|
|
if (fse.existsSync(pathname)) {
|
|
switch (path.extname(pathname)) { // set HTTP HEAD
|
|
case '.html':
|
|
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
break;
|
|
case '.js':
|
|
res.writeHead(200, { 'Content-Type': 'text/javascript' });
|
|
break;
|
|
case '.css':
|
|
res.writeHead(200, { 'Content-Type': 'text/css' });
|
|
break;
|
|
case '.gif':
|
|
res.writeHead(200, { 'Content-Type': 'image/gif' });
|
|
break;
|
|
case '.jpg':
|
|
res.writeHead(200, { 'Content-Type': 'image/jpeg' });
|
|
break;
|
|
case '.png':
|
|
res.writeHead(200, { 'Content-Type': 'image/png' });
|
|
break;
|
|
default:
|
|
res.writeHead(200, {
|
|
'Content-Type': 'application/octet-stream',
|
|
});
|
|
}
|
|
fse.readFile(pathname, (_err, data) => {
|
|
res.end(data);
|
|
});
|
|
} else {
|
|
res.writeHead(404, { 'Content-Type': 'text/html' });
|
|
res.end('<h1>404 Not Found</h1>');
|
|
console.log(`${pathname} Not Found.`);
|
|
}
|
|
}).listen(port, '127.0.0.1');
|
|
}
|
|
|
|
async start() {
|
|
this.browser = await puppeteer.launch(
|
|
{
|
|
args: ['--no-sandbox', '--disable-setuid-sandbox'],
|
|
},
|
|
);
|
|
}
|
|
|
|
async close() {
|
|
if (!this.browser) { return; }
|
|
await this.browser.close();
|
|
// @ts-ignore
|
|
if (this.server.stop) {
|
|
// @ts-ignore
|
|
this.server.stop();
|
|
} else {
|
|
this.server.close();
|
|
}
|
|
}
|
|
|
|
async page(baseUrl: string, path = '/', disableJS?: boolean): Promise<Page> {
|
|
this.baseUrl = baseUrl;
|
|
|
|
if (!this.browser) { throw new Error('Please call start() before page(url)'); }
|
|
const page = (await this.browser.newPage()) as Page;
|
|
|
|
if (disableJS) {
|
|
await page.setJavaScriptEnabled(false);
|
|
}
|
|
|
|
await page.goto(`${this.baseUrl}${path}`);
|
|
|
|
page.push = (url, options) => page.goto(`${this.baseUrl}${url}`, options);
|
|
page.html = () =>
|
|
page.evaluate(() => window.document.documentElement.outerHTML);
|
|
page.$text = (selector, trim) => page.$eval(selector, (el, trim) => {
|
|
return trim ? (el.textContent || '').replace(/^\s+|\s+$/g, '') : el.textContent;
|
|
}, trim);
|
|
page.$$text = (selector, trim) =>
|
|
page.$$eval(selector, (els, trim) => els.map((el) => {
|
|
return trim ? (el.textContent || '').replace(/^\s+|\s+$/g, '') : el.textContent;
|
|
}), trim);
|
|
page.$attr = (selector, attr) =>
|
|
page.$eval(selector, (el, attr) => el.getAttribute(attr as string), attr);
|
|
page.$$attr = (selector, attr) =>
|
|
page.$$eval(
|
|
selector,
|
|
(els, attr) => els.map(el => el.getAttribute(attr as string)),
|
|
attr,
|
|
);
|
|
|
|
return page;
|
|
}
|
|
}
|