Skip to content

Commit

Permalink
feat: print qr code for public address (#89)
Browse files Browse the repository at this point in the history
  • Loading branch information
antfu authored Aug 12, 2023
1 parent 11391e8 commit 745a34a
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 14 deletions.
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@
"mlly": "^1.4.0",
"node-forge": "^1.3.1",
"pathe": "^1.1.1",
"ufo": "^1.2.0"
"ufo": "^1.2.0",
"uqr": "^0.1.0"
},
"devDependencies": {
"@types/node": "^20.4.10",
Expand All @@ -66,4 +67,4 @@
"vitest": "^0.34.1"
},
"packageManager": "pnpm@8.6.12"
}
}
7 changes: 7 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

61 changes: 51 additions & 10 deletions src/listen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@ import { getPort } from "get-port-please";
import addShutdown from "http-shutdown";
import { defu } from "defu";
import { colors } from "consola/utils";
import { renderUnicodeCompact as renderQRCode } from "uqr";
import { open } from "./lib/open";
import type {
ListenOptions,
Listener,
ShowURLOptions,
HTTPSOptions,
ListenURL,
GetURLOptions,
} from "./types";
import { formatAddress, formatURL, getNetworkInterfaces } from "./_utils";
import { resolveCertificate } from "./_cert";
Expand Down Expand Up @@ -99,22 +102,44 @@ export async function listen(
});
}

const showURL = (options?: ShowURLOptions) => {
const add = options_.clipboard ? colors.gray("(copied to clipboard)") : "";
const lines = [];
const getURLs = (options?: GetURLOptions) => {
const urls: ListenURL[] = [];
const baseURL = options?.baseURL || options_.baseURL || "";
const name = options?.name ? ` (${options.name})` : "";

const anyV4 = addr?.addr === "0.0.0.0";
const anyV6 = addr?.addr === "[::]";

if (anyV4 || anyV6) {
lines.push(
` > Local${name}: ${formatURL(
getURL("localhost", baseURL),
)} ${add}`,
);
urls.push({
url: getURL("localhost", baseURL),
type: anyV4 ? "ipv4" : "ipv6",
public: false,
});

for (const addr of getNetworkInterfaces(anyV4)) {
lines.push(` > Network${name}: ${formatURL(getURL(addr, baseURL))}`);
urls.push({
url: getURL(addr, baseURL),
type: addr.includes("[") ? "ipv6" : "ipv4",
public: true,
});
}
}

return urls;
};

const showURL = (options?: ShowURLOptions) => {
const add = options_.clipboard ? colors.gray("(copied to clipboard)") : "";
const lines = [];
const name = options?.name ? ` (${options.name})` : "";
const baseURL = options?.baseURL || options_.baseURL || "";

const urls = getURLs(options);

if (urls.length > 0) {
for (const url of urls) {
const label = url.public ? `Network${name}: ` : `Local${name}: `;
lines.push(` > ${label} ${formatURL(url.url)} ${add}`);
}
} else {
lines.push(
Expand All @@ -123,6 +148,21 @@ export async function listen(
)} ${add}`,
);
}

const firstPublicIPv4 = urls.find(
(url) => url.public && url.type === "ipv4",
);
if (firstPublicIPv4 && options?.qrcode !== false) {
const space = " ".repeat(15);
lines.push(" ");
lines.push(
...renderQRCode(firstPublicIPv4.url)
.split("\n")
.map((line) => space + line),
);
lines.push(space + formatURL(firstPublicIPv4.url));
}

// eslint-disable-next-line no-console
console.log("\n" + lines.join("\n") + "\n");
};
Expand All @@ -148,6 +188,7 @@ export async function listen(
server,
open: _open,
showURL,
getURLs,
close,
};
}
20 changes: 18 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,33 @@ export interface ListenOptions {
autoCloseSignals: string[];
}

export interface ShowURLOptions {
export interface ListenURL {
url: string;
type: "ipv4" | "ipv6" | "unknown";
public: boolean;
}

export interface GetURLOptions {
baseURL: string;
name?: string;
}

export interface ShowURLOptions extends GetURLOptions {
/**
* Print QR Code for public IPv4 address
*
* @default true
*/
qrcode?: boolean;
}

export interface Listener {
url: string;
address: any;
server: Server | HTTPServer;
https: false | Certificate;
close: () => Promise<void>;
open: () => Promise<void>;
showURL: (options?: Pick<ListenOptions, "baseURL">) => void;
showURL: (options?: ShowURLOptions) => void;
getURLs: (options?: GetURLOptions) => ListenURL[];
}

0 comments on commit 745a34a

Please sign in to comment.