lint and cleanup
This commit is contained in:
parent
1151d3bc96
commit
e6f131ca70
|
|
@ -3,7 +3,7 @@ const { createHash } = require("node:crypto");
|
|||
const puppeteer = require("puppeteer-core");
|
||||
const chromium = require("@sparticuz/chromium");
|
||||
|
||||
exports.handler = async (event, context) => {
|
||||
exports.handler = async (event) => {
|
||||
let browser = null;
|
||||
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -13,25 +13,18 @@
|
|||
"tar-fs": "^3.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tsconfig/node16": "^16.1.3",
|
||||
"@tsconfig/node20": "^20.1.4",
|
||||
"@tsconfig/strictest": "^2.0.5",
|
||||
"@types/follow-redirects": "^1.14.4",
|
||||
"@types/node": "^20.12.3",
|
||||
"@types/node": "^20.12.7",
|
||||
"@types/tar-fs": "^2.0.4",
|
||||
"clean-modules": "^3.0.5",
|
||||
"typescript": "^5.4.3"
|
||||
"typescript": "^5.4.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
}
|
||||
},
|
||||
"node_modules/@tsconfig/node16": {
|
||||
"version": "16.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-16.1.3.tgz",
|
||||
"integrity": "sha512-9nTOUBn+EMKO6rtSZJk+DcqsfgtlERGT9XPJ5PRj/HNENPCBY1yu/JEj5wT6GLtbCLBO2k46SeXDaY0pjMqypw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@tsconfig/node20": {
|
||||
"version": "20.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node20/-/node20-20.1.4.tgz",
|
||||
|
|
@ -54,9 +47,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.12.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.3.tgz",
|
||||
"integrity": "sha512-sD+ia2ubTeWrOu+YMF+MTAB7E+O7qsMqAbMfW7DG3K1URwhZ5hN1pLlRVGbf4wDFzSfikL05M17EyorS86jShw==",
|
||||
"version": "20.12.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz",
|
||||
"integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
|
|
@ -306,9 +299,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.4.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz",
|
||||
"integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==",
|
||||
"version": "5.4.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
|
||||
"integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
|
|
|
|||
|
|
@ -43,10 +43,10 @@
|
|||
"@tsconfig/node20": "^20.1.4",
|
||||
"@tsconfig/strictest": "^2.0.5",
|
||||
"@types/follow-redirects": "^1.14.4",
|
||||
"@types/node": "^20.12.3",
|
||||
"@types/node": "^20.12.7",
|
||||
"@types/tar-fs": "^2.0.4",
|
||||
"clean-modules": "^3.0.5",
|
||||
"typescript": "^5.4.3"
|
||||
"typescript": "^5.4.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import { unlink } from "node:fs";
|
||||
import { https } from "follow-redirects";
|
||||
import { unlink } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { type UrlWithStringQuery, parse } from "node:url";
|
||||
import { extract } from "tar-fs";
|
||||
import { parse } from "node:url";
|
||||
import type { UrlWithStringQuery } from "node:url";
|
||||
|
||||
interface FollowRedirOptions extends UrlWithStringQuery {
|
||||
maxBodyLength: number;
|
||||
|
|
@ -12,7 +11,7 @@ interface FollowRedirOptions extends UrlWithStringQuery {
|
|||
export const isValidUrl = (input: string) => {
|
||||
try {
|
||||
return !!new URL(input);
|
||||
} catch (err) {
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
|
@ -59,18 +58,18 @@ export const downloadAndExtract = async (url: string) =>
|
|||
new Promise<string>((resolve, reject) => {
|
||||
const getOptions = parse(url) as FollowRedirOptions;
|
||||
getOptions.maxBodyLength = 60 * 1024 * 1024; // 60mb
|
||||
const destDir = `${tmpdir()}/chromium-pack`;
|
||||
const extractObj = extract(destDir);
|
||||
const destinationDirectory = `${tmpdir()}/chromium-pack`;
|
||||
const extractObject = extract(destinationDirectory);
|
||||
https
|
||||
.get(url, (response) => {
|
||||
response.pipe(extractObj);
|
||||
extractObj.on("finish", () => {
|
||||
resolve(destDir);
|
||||
response.pipe(extractObject);
|
||||
extractObject.on("finish", () => {
|
||||
resolve(destinationDirectory);
|
||||
});
|
||||
})
|
||||
.on("error", (err) => {
|
||||
unlink(destDir, (_) => {
|
||||
reject(err);
|
||||
.on("error", (error) => {
|
||||
unlink(destinationDirectory, () => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
173
source/index.ts
173
source/index.ts
|
|
@ -1,3 +1,4 @@
|
|||
import { https } from "follow-redirects";
|
||||
import {
|
||||
access,
|
||||
createWriteStream,
|
||||
|
|
@ -5,27 +6,19 @@ import {
|
|||
mkdirSync,
|
||||
symlink,
|
||||
} from "node:fs";
|
||||
import { https } from "follow-redirects";
|
||||
import LambdaFS from "./lambdafs";
|
||||
import { join } from "node:path";
|
||||
import { URL } from "node:url";
|
||||
|
||||
import {
|
||||
downloadAndExtract,
|
||||
isRunningInAwsLambda,
|
||||
isValidUrl,
|
||||
isRunningInAwsLambdaNode20,
|
||||
isValidUrl,
|
||||
} from "./helper";
|
||||
import { inflate } from "./lambdafs";
|
||||
|
||||
/** Viewport taken from https://github.com/puppeteer/puppeteer/blob/main/docs/api/puppeteer.viewport.md */
|
||||
interface Viewport {
|
||||
/**
|
||||
* The page width in pixels.
|
||||
*/
|
||||
width: number;
|
||||
/**
|
||||
* The page height in pixels.
|
||||
*/
|
||||
height: number;
|
||||
/**
|
||||
* Specify device scale factor.
|
||||
* See {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio | devicePixelRatio} for more info.
|
||||
|
|
@ -33,20 +26,28 @@ interface Viewport {
|
|||
*/
|
||||
deviceScaleFactor?: number;
|
||||
/**
|
||||
* Whether the `meta viewport` tag is taken into account.
|
||||
* Specify if the viewport supports touch events.
|
||||
* @default false
|
||||
*/
|
||||
isMobile?: boolean;
|
||||
hasTouch?: boolean;
|
||||
/**
|
||||
* The page height in pixels.
|
||||
*/
|
||||
height: number;
|
||||
/**
|
||||
* Specifies if the viewport is in landscape mode.
|
||||
* @default false
|
||||
*/
|
||||
isLandscape?: boolean;
|
||||
/**
|
||||
* Specify if the viewport supports touch events.
|
||||
* Whether the `meta viewport` tag is taken into account.
|
||||
* @default false
|
||||
*/
|
||||
hasTouch?: boolean;
|
||||
isMobile?: boolean;
|
||||
/**
|
||||
* The page width in pixels.
|
||||
*/
|
||||
width: number;
|
||||
}
|
||||
|
||||
if (isRunningInAwsLambda()) {
|
||||
|
|
@ -93,71 +94,7 @@ class Chromium {
|
|||
* If false, webgl will be disabled.
|
||||
* (If false, the swiftshader.tar.br file will also not extract)
|
||||
*/
|
||||
private static graphicsMode: boolean = true;
|
||||
|
||||
/**
|
||||
* Downloads or symlinks a custom font and returns its basename, patching the environment so that Chromium can find it.
|
||||
*/
|
||||
static font(input: string): Promise<string> {
|
||||
if (process.env["HOME"] === undefined) {
|
||||
process.env["HOME"] = "/tmp";
|
||||
}
|
||||
|
||||
if (existsSync(`${process.env["HOME"]}/.fonts`) !== true) {
|
||||
mkdirSync(`${process.env["HOME"]}/.fonts`);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (/^https?:[/][/]/i.test(input) !== true) {
|
||||
input = `file://${input}`;
|
||||
}
|
||||
|
||||
const url = new URL(input);
|
||||
const output = `${process.env["HOME"]}/.fonts/${url.pathname
|
||||
.split("/")
|
||||
.pop()}`;
|
||||
|
||||
if (existsSync(output) === true) {
|
||||
return resolve(output.split("/").pop() as string);
|
||||
}
|
||||
|
||||
if (url.protocol === "file:") {
|
||||
access(url.pathname, (error) => {
|
||||
if (error != null) {
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
symlink(url.pathname, output, (error) => {
|
||||
return error != null
|
||||
? reject(error)
|
||||
: resolve(url.pathname.split("/").pop() as string);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
https.get(input, (response) => {
|
||||
if (response.statusCode !== 200) {
|
||||
return reject(`Unexpected status code: ${response.statusCode}.`);
|
||||
}
|
||||
|
||||
const stream = createWriteStream(output);
|
||||
|
||||
stream.once("error", (error) => {
|
||||
return reject(error);
|
||||
});
|
||||
|
||||
response.on("data", (chunk) => {
|
||||
stream.write(chunk);
|
||||
});
|
||||
|
||||
response.once("end", () => {
|
||||
stream.end(() => {
|
||||
return resolve(url.pathname.split("/").pop() as string);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
private static graphicsMode = true;
|
||||
|
||||
/**
|
||||
* Returns a list of additional Chromium flags recommended for serverless environments.
|
||||
|
|
@ -271,19 +208,19 @@ class Chromium {
|
|||
|
||||
// Extract the required files
|
||||
const promises = [
|
||||
LambdaFS.inflate(`${input}/chromium.br`),
|
||||
LambdaFS.inflate(`${input}/fonts.tar.br`),
|
||||
inflate(`${input}/chromium.br`),
|
||||
inflate(`${input}/fonts.tar.br`),
|
||||
];
|
||||
if (this.graphics) {
|
||||
// Only inflate graphics stack if needed
|
||||
promises.push(LambdaFS.inflate(`${input}/swiftshader.tar.br`));
|
||||
promises.push(inflate(`${input}/swiftshader.tar.br`));
|
||||
}
|
||||
if (isRunningInAwsLambda()) {
|
||||
// If running in AWS Lambda, extract more required files
|
||||
promises.push(LambdaFS.inflate(`${input}/al2.tar.br`));
|
||||
promises.push(inflate(`${input}/al2.tar.br`));
|
||||
}
|
||||
if (isRunningInAwsLambdaNode20()) {
|
||||
promises.push(LambdaFS.inflate(`${input}/al2023.tar.br`));
|
||||
promises.push(inflate(`${input}/al2023.tar.br`));
|
||||
}
|
||||
|
||||
// Await all extractions
|
||||
|
|
@ -292,6 +229,70 @@ class Chromium {
|
|||
return result.shift() as string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads or symlinks a custom font and returns its basename, patching the environment so that Chromium can find it.
|
||||
*/
|
||||
static font(input: string): Promise<string> {
|
||||
if (process.env["HOME"] === undefined) {
|
||||
process.env["HOME"] = "/tmp";
|
||||
}
|
||||
|
||||
if (existsSync(`${process.env["HOME"]}/.fonts`) !== true) {
|
||||
mkdirSync(`${process.env["HOME"]}/.fonts`);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (/^https?:\/\//i.test(input) !== true) {
|
||||
input = `file://${input}`;
|
||||
}
|
||||
|
||||
const url = new URL(input);
|
||||
const output = `${process.env["HOME"]}/.fonts/${url.pathname
|
||||
.split("/")
|
||||
.pop()}`;
|
||||
|
||||
if (existsSync(output) === true) {
|
||||
return resolve(output.split("/").pop() as string);
|
||||
}
|
||||
|
||||
if (url.protocol === "file:") {
|
||||
access(url.pathname, (error) => {
|
||||
if (error != null) {
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
symlink(url.pathname, output, (error) => {
|
||||
return error == null
|
||||
? resolve(url.pathname.split("/").pop() as string)
|
||||
: reject(error);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
https.get(input, (response) => {
|
||||
if (response.statusCode !== 200) {
|
||||
return reject(`Unexpected status code: ${response.statusCode}.`);
|
||||
}
|
||||
|
||||
const stream = createWriteStream(output);
|
||||
|
||||
stream.once("error", (error) => {
|
||||
return reject(error);
|
||||
});
|
||||
|
||||
response.on("data", (chunk) => {
|
||||
stream.write(chunk);
|
||||
});
|
||||
|
||||
response.once("end", () => {
|
||||
stream.end(() => {
|
||||
return resolve(url.pathname.split("/").pop() as string);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the graphics stack is enabled or disabled
|
||||
* @returns boolean
|
||||
|
|
@ -309,7 +310,7 @@ class Chromium {
|
|||
*/
|
||||
public static set setGraphicsMode(value: boolean) {
|
||||
if (typeof value !== "boolean") {
|
||||
throw new Error(
|
||||
throw new TypeError(
|
||||
`Graphics mode must be a boolean, you entered '${value}'`
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,21 @@
|
|||
import { createReadStream, createWriteStream, existsSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { basename, join } from "node:path";
|
||||
import { extract } from "tar-fs";
|
||||
import { createBrotliDecompress, createUnzip } from "node:zlib";
|
||||
import { extract } from "tar-fs";
|
||||
|
||||
class LambdaFS {
|
||||
/**
|
||||
* Decompresses a (tarballed) Brotli or Gzip compressed file and returns the path to the decompressed file/folder.
|
||||
*
|
||||
* @param filePath Path of the file to decompress.
|
||||
*/
|
||||
static inflate(filePath: string): Promise<string> {
|
||||
export const inflate = (filePath: string): Promise<string> => {
|
||||
const output = filePath.includes("swiftshader")
|
||||
? tmpdir()
|
||||
: join(
|
||||
tmpdir(),
|
||||
basename(filePath).replace(
|
||||
/[.](?:t(?:ar(?:[.](?:br|gz))?|br|gz)|br|gz)$/i,
|
||||
/\.(?:t(?:ar(?:\.(?:br|gz))?|br|gz)|br|gz)$/i,
|
||||
""
|
||||
)
|
||||
);
|
||||
|
|
@ -32,10 +31,10 @@ class LambdaFS {
|
|||
}
|
||||
}
|
||||
|
||||
let source = createReadStream(filePath, { highWaterMark: 2 ** 23 });
|
||||
const source = createReadStream(filePath, { highWaterMark: 2 ** 23 });
|
||||
let target = null;
|
||||
|
||||
if (/[.](?:t(?:ar(?:[.](?:br|gz))?|br|gz))$/i.test(filePath) === true) {
|
||||
if (/\.t(?:ar(?:\.(?:br|gz))?|br|gz)$/i.test(filePath) === true) {
|
||||
target = extract(output);
|
||||
|
||||
target.once("finish", () => {
|
||||
|
|
@ -69,7 +68,6 @@ class LambdaFS {
|
|||
source.pipe(target);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default LambdaFS;
|
||||
export default inflate;
|
||||
|
|
|
|||
Loading…
Reference in New Issue