style: add explicit types to eliminate implicit any in index.ts
This commit is contained in:
@@ -65,6 +65,7 @@
|
|||||||
nodejs_22
|
nodejs_22
|
||||||
firefox
|
firefox
|
||||||
geckodriver
|
geckodriver
|
||||||
|
typescript-language-server
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { execFileSync } from "node:child_process";
|
import { execFileSync } from "node:child_process";
|
||||||
import { Builder, type WebDriver } from "selenium-webdriver";
|
import { Builder, type WebDriver } from "selenium-webdriver";
|
||||||
|
export { type WebDriver };
|
||||||
import firefox from "selenium-webdriver/firefox.js";
|
import firefox from "selenium-webdriver/firefox.js";
|
||||||
|
|
||||||
export interface DriverOptions {
|
export interface DriverOptions {
|
||||||
|
|||||||
65
src/index.ts
65
src/index.ts
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
import { loadConfig, type GlimpseConfig } from "./config.js";
|
import { loadConfig, type GlimpseConfig } from "./config.js";
|
||||||
import { createDriver } from "./driver.js";
|
import { createDriver, type WebDriver } from "./driver.js";
|
||||||
import { searchKagi } from "./providers/kagi.js";
|
import { searchKagi } from "./providers/kagi.js";
|
||||||
import { readFileSync, writeFileSync } from "node:fs";
|
import { readFileSync, writeFileSync } from "node:fs";
|
||||||
import TurndownService from "turndown";
|
import TurndownService from "turndown";
|
||||||
@@ -11,6 +11,18 @@ const POLL_INTERVAL_MS = 200;
|
|||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
const runContext: { targetUrl?: string; currentUrl?: string } = {};
|
const runContext: { targetUrl?: string; currentUrl?: string } = {};
|
||||||
|
|
||||||
|
interface ReaderArticle {
|
||||||
|
title?: string;
|
||||||
|
byline?: string;
|
||||||
|
siteName?: string;
|
||||||
|
html?: string;
|
||||||
|
text?: string;
|
||||||
|
readerUrl?: string;
|
||||||
|
sourceUrl?: string;
|
||||||
|
finalUrl?: string;
|
||||||
|
markdown?: string;
|
||||||
|
}
|
||||||
|
|
||||||
// Parse CLI Args
|
// Parse CLI Args
|
||||||
const [command, ...args] = process.argv.slice(2);
|
const [command, ...args] = process.argv.slice(2);
|
||||||
const headless = !args.includes("--no-headless");
|
const headless = !args.includes("--no-headless");
|
||||||
@@ -23,7 +35,7 @@ const configPath = getOption("--config");
|
|||||||
let appConfig: GlimpseConfig = {};
|
let appConfig: GlimpseConfig = {};
|
||||||
let timeoutMs = DEFAULT_TIMEOUT_MS;
|
let timeoutMs = DEFAULT_TIMEOUT_MS;
|
||||||
|
|
||||||
function getOption(name) {
|
function getOption(name: string) {
|
||||||
const prefix = `${name}=`;
|
const prefix = `${name}=`;
|
||||||
return args.find((arg) => arg.startsWith(prefix))?.slice(prefix.length);
|
return args.find((arg) => arg.startsWith(prefix))?.slice(prefix.length);
|
||||||
}
|
}
|
||||||
@@ -36,14 +48,14 @@ function elapsedMs() {
|
|||||||
return Date.now() - startTime;
|
return Date.now() - startTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
function printResult(result) {
|
function printResult(result: unknown) {
|
||||||
if (result === undefined) {
|
if (result === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const outputValue =
|
const outputValue =
|
||||||
result && typeof result === "object" && !Array.isArray(result)
|
result && typeof result === "object" && !Array.isArray(result)
|
||||||
? { ...result, elapsedMs: result.elapsedMs ?? elapsedMs() }
|
? { ...result, elapsedMs: (result as any).elapsedMs ?? elapsedMs() }
|
||||||
: result;
|
: result;
|
||||||
const output =
|
const output =
|
||||||
typeof outputValue === "object"
|
typeof outputValue === "object"
|
||||||
@@ -56,18 +68,18 @@ class CliError extends Error {
|
|||||||
code: string;
|
code: string;
|
||||||
details: Record<string, unknown>;
|
details: Record<string, unknown>;
|
||||||
|
|
||||||
constructor(code, message, details = {}) {
|
constructor(code: string, message: string, details = {}) {
|
||||||
super(message);
|
super(message);
|
||||||
this.code = code;
|
this.code = code;
|
||||||
this.details = details;
|
this.details = details;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function cliError(code, message, details = {}) {
|
function cliError(code: string, message: string, details = {}) {
|
||||||
throw new CliError(code, message, details);
|
throw new CliError(code, message, details);
|
||||||
}
|
}
|
||||||
|
|
||||||
function unknownCommand(name) {
|
function unknownCommand(name: string) {
|
||||||
cliError("UNKNOWN_COMMAND", `Unknown command: ${name}`);
|
cliError("UNKNOWN_COMMAND", `Unknown command: ${name}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,8 +178,8 @@ function getPreludeScriptSource() {
|
|||||||
return inlineJs;
|
return inlineJs;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function withDriver(action) {
|
async function withDriver(action: (driver: WebDriver) => Promise<unknown>) {
|
||||||
let driver;
|
let driver: WebDriver;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
driver = await createDriver({ headless, existingUrl });
|
driver = await createDriver({ headless, existingUrl });
|
||||||
@@ -182,16 +194,16 @@ async function withDriver(action) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function waitForReadyState(driver) {
|
async function waitForReadyState(driver: WebDriver) {
|
||||||
if (waitUntil === "none") {
|
if (waitUntil === "none") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await driver.wait(async () => {
|
await driver.wait(async () => {
|
||||||
const readyState = await driver.executeScript(
|
const readyState = (await driver.executeScript(
|
||||||
"return document.readyState",
|
"return document.readyState",
|
||||||
);
|
)) as string;
|
||||||
return waitUntil === "interactive"
|
return waitUntil === "interactive"
|
||||||
? ["interactive", "complete"].includes(readyState)
|
? ["interactive", "complete"].includes(readyState)
|
||||||
: readyState === "complete";
|
: readyState === "complete";
|
||||||
@@ -204,14 +216,14 @@ async function waitForReadyState(driver) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function waitForJs(driver) {
|
async function waitForJs(driver: WebDriver) {
|
||||||
if (!waitJs) {
|
if (!waitJs) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
while (Date.now() - start < timeoutMs) {
|
while (Date.now() - start < timeoutMs) {
|
||||||
let result;
|
let result: unknown;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
result = await driver.executeScript(waitJs);
|
result = await driver.executeScript(waitJs);
|
||||||
@@ -232,7 +244,7 @@ async function waitForJs(driver) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function runPreludeScript(driver) {
|
async function runPreludeScript(driver: WebDriver) {
|
||||||
const scriptSource = getPreludeScriptSource();
|
const scriptSource = getPreludeScriptSource();
|
||||||
if (!scriptSource) {
|
if (!scriptSource) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@@ -245,10 +257,13 @@ async function runPreludeScript(driver) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function withPage(targetUrl, action) {
|
async function withPage(
|
||||||
|
targetUrl: string,
|
||||||
|
action: (driver: WebDriver, scriptResult: unknown) => Promise<unknown>,
|
||||||
|
) {
|
||||||
runContext.targetUrl = targetUrl;
|
runContext.targetUrl = targetUrl;
|
||||||
|
|
||||||
return withDriver(async (driver) => {
|
return withDriver(async (driver: WebDriver) => {
|
||||||
// Navigate To Page
|
// Navigate To Page
|
||||||
try {
|
try {
|
||||||
await driver.get(targetUrl);
|
await driver.get(targetUrl);
|
||||||
@@ -340,7 +355,7 @@ async function snapshotCommand() {
|
|||||||
|
|
||||||
if (!targetUrl) usage();
|
if (!targetUrl) usage();
|
||||||
|
|
||||||
return withPage(targetUrl, async (driver) => {
|
return withPage(targetUrl, async (driver: WebDriver) => {
|
||||||
// Capture Page Metadata
|
// Capture Page Metadata
|
||||||
const [url, title, result] = await Promise.all([
|
const [url, title, result] = await Promise.all([
|
||||||
driver.getCurrentUrl(),
|
driver.getCurrentUrl(),
|
||||||
@@ -362,7 +377,7 @@ async function execCommand() {
|
|||||||
|
|
||||||
if (!targetUrl || (!inlineJs && !scriptPath)) usage();
|
if (!targetUrl || (!inlineJs && !scriptPath)) usage();
|
||||||
|
|
||||||
return withPage(targetUrl, async (_driver, scriptResult) => scriptResult);
|
return withPage(targetUrl, async (_driver: WebDriver, scriptResult: unknown) => scriptResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function screenshotCommand() {
|
async function screenshotCommand() {
|
||||||
@@ -371,7 +386,7 @@ async function screenshotCommand() {
|
|||||||
|
|
||||||
if (!targetUrl) usage();
|
if (!targetUrl) usage();
|
||||||
|
|
||||||
return withPage(targetUrl, async (driver) => {
|
return withPage(targetUrl, async (driver: WebDriver) => {
|
||||||
// Save Screenshot
|
// Save Screenshot
|
||||||
const image = await driver.takeScreenshot();
|
const image = await driver.takeScreenshot();
|
||||||
writeFileSync(outputPath, image, "base64");
|
writeFileSync(outputPath, image, "base64");
|
||||||
@@ -385,11 +400,11 @@ async function screenshotCommand() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function markdownTitle(text) {
|
function markdownTitle(text: string) {
|
||||||
return text.replaceAll(/\s+/g, " ").trim();
|
return text.replaceAll(/\s+/g, " ").trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
function articleToMarkdown(article) {
|
function articleToMarkdown(article: ReaderArticle) {
|
||||||
const turndown = new TurndownService({
|
const turndown = new TurndownService({
|
||||||
headingStyle: "atx",
|
headingStyle: "atx",
|
||||||
codeBlockStyle: "fenced",
|
codeBlockStyle: "fenced",
|
||||||
@@ -413,7 +428,7 @@ function articleToMarkdown(article) {
|
|||||||
return `${parts.join("\n\n").trim()}\n`;
|
return `${parts.join("\n\n").trim()}\n`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderReaderOutput(article, format) {
|
function renderReaderOutput(article: ReaderArticle, format: string) {
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case "markdown":
|
case "markdown":
|
||||||
return article.markdown;
|
return article.markdown;
|
||||||
@@ -464,7 +479,7 @@ async function readerCommand() {
|
|||||||
|
|
||||||
if (!targetUrl) usage();
|
if (!targetUrl) usage();
|
||||||
|
|
||||||
return withPage(targetUrl, async (driver) => {
|
return withPage(targetUrl, async (driver: WebDriver) => {
|
||||||
// Capture Final Url
|
// Capture Final Url
|
||||||
const finalUrl = await driver.getCurrentUrl();
|
const finalUrl = await driver.getCurrentUrl();
|
||||||
|
|
||||||
@@ -473,7 +488,7 @@ async function readerCommand() {
|
|||||||
await driver.get(readerUrl);
|
await driver.get(readerUrl);
|
||||||
|
|
||||||
// Wait For Reader Content
|
// Wait For Reader Content
|
||||||
let article;
|
let article: ReaderArticle;
|
||||||
try {
|
try {
|
||||||
article = await driver.wait(
|
article = await driver.wait(
|
||||||
async () => {
|
async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user