@pagepocket/cli 0.12.0 → 0.14.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,40 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ const thisDir = path.dirname(fileURLToPath(import.meta.url));
5
+ const vendorDir = thisDir.endsWith("src")
6
+ ? path.join(thisDir, "..", "dist", "vendor")
7
+ : path.join(thisDir, "vendor");
8
+ const resolveContentReaderBundle = () => ({
9
+ jsPath: path.join(vendorDir, "content-reader.iife.js"),
10
+ cssPath: path.join(vendorDir, "content-reader.css")
11
+ });
12
+ const buildShellHtml = (contentHtml, bundleJs, bundleCss) => `<!DOCTYPE html>
13
+ <html lang="en">
14
+ <head>
15
+ <meta charset="utf-8" />
16
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
17
+ <title>PagePocket Reader</title>
18
+ <style>${bundleCss}</style>
19
+ </head>
20
+ <body>
21
+ <div id="root"></div>
22
+ <template id="pp-content">${contentHtml}</template>
23
+ <script>${bundleJs}</script>
24
+ <script>
25
+ window.__PP_CONTENT_READER__.mount(
26
+ document.getElementById("root"),
27
+ document.getElementById("pp-content").innerHTML
28
+ );
29
+ </script>
30
+ </body>
31
+ </html>`;
32
+ export const buildMainContentShell = async (rootDir) => {
33
+ const indexPath = path.join(rootDir, "index.html");
34
+ const contentHtml = await fs.promises.readFile(indexPath, "utf-8");
35
+ const { jsPath, cssPath } = resolveContentReaderBundle();
36
+ const bundleJs = await fs.promises.readFile(jsPath, "utf-8");
37
+ const bundleCss = await fs.promises.readFile(cssPath, "utf-8");
38
+ const shellHtml = buildShellHtml(contentHtml, bundleJs, bundleCss);
39
+ return { shellHtml };
40
+ };
package/dist/view.js CHANGED
@@ -1,43 +1,64 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
- import Koa from "koa";
4
- import send from "koa-send";
5
- import serve from "koa-static";
6
- export const createViewServer = async (args) => {
7
- const indexPath = path.join(args.rootDir, "index.html");
8
- const stat = await fs.promises.stat(args.rootDir).catch(() => undefined);
9
- if (!stat || !stat.isDirectory()) {
10
- throw new Error(`view: directory not found: ${args.rootDir}`);
3
+ import { serve } from "@hono/node-server";
4
+ import { serveStatic } from "@hono/node-server/serve-static";
5
+ import { Hono } from "hono";
6
+ import { buildMainContentShell } from "./view-main-content.js";
7
+ const METADATA_FILENAME = ".pagepocket.json";
8
+ const readSnapshotType = async (rootDir) => {
9
+ const metaPath = path.join(rootDir, METADATA_FILENAME);
10
+ const raw = await fs.promises.readFile(metaPath, "utf-8").catch(() => undefined);
11
+ if (!raw) {
12
+ return "full";
11
13
  }
12
- const indexStat = await fs.promises.stat(indexPath).catch(() => undefined);
13
- if (!indexStat || !indexStat.isFile()) {
14
- throw new Error(`view: index.html not found under: ${args.rootDir}`);
14
+ try {
15
+ const meta = JSON.parse(raw);
16
+ return meta.snapshotType ?? "full";
17
+ }
18
+ catch {
19
+ return "full";
15
20
  }
16
- const app = new Koa();
17
- // Avoid Koa's generator-function detection (and its dependency chain) by
18
- // not calling `app.use()`.
19
- //
20
- // In some ESM/CJS interop environments, koa's transitive dependency
21
- // `is-generator-function` can break ("getGeneratorFunction is not a function")
22
- // when it tries to require `generator-function`. We don't need generator
23
- // middleware support here, so bypass it entirely.
24
- const staticMw = serve(args.rootDir, { index: false });
25
- app.middleware.push(staticMw);
26
- app.middleware.push(async (ctx) => {
27
- // SPA fallback (anything not matched by static middleware)
28
- ctx.status = 200;
29
- await send(ctx, "index.html", {
30
- root: args.rootDir
31
- });
21
+ };
22
+ const createFullViewApp = (rootDir, indexPath) => {
23
+ const app = new Hono();
24
+ app.use("*", serveStatic({
25
+ root: rootDir,
26
+ rewriteRequestPath: (p) => p
27
+ }));
28
+ app.get("*", async (c) => {
29
+ const html = await fs.promises.readFile(indexPath, "utf-8");
30
+ return c.html(html);
31
+ });
32
+ return app;
33
+ };
34
+ const createMainContentViewApp = async (rootDir) => {
35
+ const { shellHtml } = await buildMainContentShell(rootDir);
36
+ const app = new Hono();
37
+ app.use("*", async (c, next) => {
38
+ const reqPath = new URL(c.req.url).pathname;
39
+ if (reqPath === "/" || reqPath === "/index.html") {
40
+ return c.html(shellHtml);
41
+ }
42
+ return next();
32
43
  });
33
- const server = await new Promise((resolve, reject) => {
34
- const httpServer = app.listen(args.port, args.host, () => resolve(httpServer));
35
- httpServer.on("error", reject);
44
+ app.use("*", serveStatic({
45
+ root: rootDir,
46
+ rewriteRequestPath: (p) => p
47
+ }));
48
+ return app;
49
+ };
50
+ const startServer = async (app, host, port) => {
51
+ const server = serve({
52
+ fetch: app.fetch,
53
+ port,
54
+ hostname: host
55
+ });
56
+ await new Promise((resolve) => {
57
+ server.once("listening", resolve);
36
58
  });
37
59
  const address = server.address();
38
- const port = typeof address === "object" && address ? address.port : args.port;
39
- const host = args.host;
40
- const url = `http://${host}:${port}`;
60
+ const resolvedPort = typeof address === "object" && address ? address.port : port;
61
+ const url = `http://${host}:${resolvedPort}`;
41
62
  return {
42
63
  url,
43
64
  close: async () => {
@@ -47,3 +68,20 @@ export const createViewServer = async (args) => {
47
68
  }
48
69
  };
49
70
  };
71
+ export const createViewServer = async (args) => {
72
+ const indexPath = path.join(args.rootDir, "index.html");
73
+ const stat = await fs.promises.stat(args.rootDir).catch(() => undefined);
74
+ if (!stat || !stat.isDirectory()) {
75
+ throw new Error(`view: directory not found: ${args.rootDir}`);
76
+ }
77
+ const indexStat = await fs.promises.stat(indexPath).catch(() => undefined);
78
+ if (!indexStat || !indexStat.isFile()) {
79
+ throw new Error(`view: index.html not found under: ${args.rootDir}`);
80
+ }
81
+ const snapshotType = await readSnapshotType(args.rootDir);
82
+ const app = snapshotType === "main-content"
83
+ ? await createMainContentViewApp(args.rootDir)
84
+ : createFullViewApp(args.rootDir, indexPath);
85
+ const { url, close } = await startServer(app, args.host, args.port);
86
+ return { url, snapshotType, close };
87
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pagepocket/cli",
3
- "version": "0.12.0",
3
+ "version": "0.14.5",
4
4
  "description": "CLI for capturing offline snapshots of web pages.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -14,35 +14,35 @@
14
14
  "author": "",
15
15
  "license": "ISC",
16
16
  "dependencies": {
17
+ "@hono/node-server": "^1.14.1",
17
18
  "@oclif/core": "^4.0.9",
18
19
  "chalk": "^4.1.2",
19
20
  "env-paths": "^2.2.1",
20
- "koa": "^2.16.2",
21
- "koa-send": "^5.0.1",
22
- "koa-static": "^5.0.0",
21
+ "hono": "^4.7.6",
23
22
  "npm-package-arg": "^13.0.2",
24
23
  "ora": "^9.0.0",
25
- "@pagepocket/main-content-unit": "0.12.0",
26
- "@pagepocket/build-snapshot-unit": "0.12.0",
27
- "@pagepocket/capture-http-cdp-unit": "0.12.0",
28
- "@pagepocket/capture-http-lighterceptor-unit": "0.12.0",
29
- "@pagepocket/capture-http-puppeteer-unit": "0.12.0",
30
- "@pagepocket/contracts": "0.12.0",
31
- "@pagepocket/builtin-strategy": "0.12.0",
32
- "@pagepocket/lib": "0.12.0",
33
- "@pagepocket/plugin-yt-dlp": "0.12.0",
34
- "@pagepocket/shared": "0.12.0",
35
- "@pagepocket/single-file-unit": "0.12.0",
36
- "@pagepocket/write-down-unit": "0.12.0"
24
+ "@pagepocket/build-snapshot-unit": "0.14.5",
25
+ "@pagepocket/builtin-strategy": "0.14.5",
26
+ "@pagepocket/capture-http-lighterceptor-unit": "0.14.5",
27
+ "@pagepocket/capture-http-cdp-unit": "0.14.5",
28
+ "@pagepocket/capture-http-puppeteer-unit": "0.14.5",
29
+ "@pagepocket/lib": "0.14.5",
30
+ "@pagepocket/contracts": "0.14.5",
31
+ "@pagepocket/metadata-unit": "0.14.5",
32
+ "@pagepocket/plugin-yt-dlp": "0.14.5",
33
+ "@pagepocket/shared": "0.14.5",
34
+ "@pagepocket/main-content-unit": "0.14.5",
35
+ "@pagepocket/single-file-unit": "0.14.5",
36
+ "@pagepocket/write-down-unit": "0.14.5"
37
37
  },
38
38
  "devDependencies": {
39
- "@types/koa": "^2.15.0",
40
- "@types/koa-send": "^4.1.5",
41
- "@types/koa-static": "^4.0.4",
42
39
  "@types/node": "^20.11.30",
40
+ "cpx2": "^8.0.0",
41
+ "esbuild": "^0.25.12",
43
42
  "rimraf": "^6.0.1",
44
43
  "tsx": "^4.19.3",
45
- "typescript": "^5.4.5"
44
+ "typescript": "^5.4.5",
45
+ "@pagepocket/content-reader": "0.14.5"
46
46
  },
47
47
  "oclif": {
48
48
  "bin": "pp",
@@ -53,8 +53,10 @@
53
53
  }
54
54
  },
55
55
  "scripts": {
56
- "build": "rimraf dist && tsc",
56
+ "build": "rimraf dist && tsc && pnpm build:vendor",
57
+ "build:vendor": "cpx \"node_modules/@pagepocket/content-reader/dist/browser/*\" dist/vendor",
57
58
  "start": "node dist/index.js",
58
- "test": "pnpm build && tsx --test specs/*.spec.ts"
59
+ "test": "pnpm build && tsx --test specs/*.spec.ts",
60
+ "build:sea": "node --import tsx scripts/build-sea.ts"
59
61
  }
60
62
  }