-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcli.js
More file actions
135 lines (119 loc) · 3.87 KB
/
Copy pathcli.js
File metadata and controls
135 lines (119 loc) · 3.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import { spawn } from "node:child_process";
import { existsSync, readFileSync, statSync } from "node:fs";
import { extname, resolve } from "node:path";
import { createStandaloneEditorServer } from "./src/standalone/index.js";
export const HTML_EDITOR_VERSION = JSON.parse(
readFileSync(new URL("./package.json", import.meta.url), "utf8"),
).version;
export function parseCliArgs(argv) {
const result = { help: false, version: false, input: null, open: true, port: 0, root: null };
let hasInput = false;
for (let index = 0; index < argv.length; index += 1) {
const value = argv[index];
if (value === "--help" || value === "-h") {
result.help = true;
continue;
}
if (value === "--version" || value === "-v") {
result.version = true;
continue;
}
if (value === "--no-open") {
result.open = false;
continue;
}
if (value === "--port") {
const port = Number(argv[index + 1]);
if (!Number.isInteger(port) || port < 0 || port > 65535) {
throw new Error("--port must be a valid port between 0 and 65535");
}
result.port = port;
index += 1;
continue;
}
if (value === "--root") {
const root = argv[index + 1];
if (!root || root.startsWith("--")) throw new Error("--root requires a directory path");
result.root = root;
index += 1;
continue;
}
if (value.startsWith("--")) {
throw new Error(`Unknown option: ${value}`);
}
if (hasInput) {
throw new Error(`Unexpected argument: ${value}`);
}
result.input = value;
hasInput = true;
}
return result;
}
export function validateEditorInput(input) {
if (typeof input !== "string" || !input.trim()) {
throw new Error("Missing input path. Usage: htmleditor <file.html|directory>");
}
const absolutePath = resolve(input);
if (!existsSync(absolutePath)) {
throw new Error(`Input path does not exist: ${absolutePath}`);
}
const stats = statSync(absolutePath);
if (stats.isDirectory()) return absolutePath;
if (!stats.isFile() || extname(absolutePath).toLowerCase() !== ".html") {
throw new Error(`Input must be an .html file or directory: ${absolutePath}`);
}
return absolutePath;
}
export function printHelp(log = console.log) {
log(`Local HTML Editor
Usage:
htmleditor <file.html|directory> [options]
htmleditor --version
Options:
--port <number> Preferred port; 0 selects an available port (default: 0)
--root <path> Project/resource root; may be a parent of the HTML file
--no-open Do not open the browser automatically
-v, --version Print the installed version and exit
-h, --help Show this help`);
}
export function printVersion(log = console.log) {
log(HTML_EDITOR_VERSION);
}
function openBrowser(url) {
const command =
process.platform === "darwin"
? { file: "open", args: [url] }
: process.platform === "win32"
? { file: "cmd", args: ["/c", "start", "", url] }
: { file: "xdg-open", args: [url] };
const child = spawn(command.file, command.args, { detached: true, stdio: "ignore" });
child.unref();
}
export async function runCli(argv = process.argv.slice(2)) {
const options = parseCliArgs(argv);
if (options.help) {
printHelp();
return null;
}
if (options.version) {
printVersion();
return null;
}
const input = validateEditorInput(options.input);
const editor = await createStandaloneEditorServer({
input,
root: options.root == null ? null : resolve(options.root),
port: options.port,
});
console.log(`Local HTML Editor: ${editor.url}`);
console.log(`Project: ${editor.projectDir}`);
console.log(`Editing: ${editor.defaultFile}`);
if (options.open) openBrowser(editor.url);
const shutdown = async () => {
await editor.close();
process.exit(0);
};
process.once("SIGINT", shutdown);
process.once("SIGTERM", shutdown);
return editor;
}