@hamedb89/localghost 0.1.3 → 0.1.8
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.
- package/README.md +148 -14
- package/apps/macos-widget/LocalghostWidget.swift +218 -0
- package/apps/macos-widget/build.sh +53 -0
- package/dist/cli.js +831 -90
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +110 -6
- package/dist/index.js +361 -51
- package/dist/index.js.map +1 -1
- package/dist/vite.d.ts +5 -0
- package/dist/vite.js +516 -17
- package/dist/vite.js.map +1 -1
- package/docs/flows.md +27 -5
- package/docs/github.md +5 -5
- package/docs/localghost.1.md +60 -9
- package/docs/macos-widget.md +46 -0
- package/package.json +8 -4
package/dist/index.js
CHANGED
|
@@ -1,6 +1,100 @@
|
|
|
1
|
+
// src/activity.ts
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
3
|
+
import { homedir } from "os";
|
|
4
|
+
import { dirname, join } from "path";
|
|
5
|
+
var LOCALGHOST_ACTIVITY_VERSION = 1;
|
|
6
|
+
function getLocalghostActivityPath(env = process.env) {
|
|
7
|
+
if (env.LOCALGHOST_ACTIVITY_PATH) return env.LOCALGHOST_ACTIVITY_PATH;
|
|
8
|
+
const stateRoot = env.XDG_STATE_HOME || join(homedir(), ".local/state");
|
|
9
|
+
return join(stateRoot, "localghost", "activity.json");
|
|
10
|
+
}
|
|
11
|
+
function isProcessRunning(pid) {
|
|
12
|
+
if (!Number.isInteger(pid) || pid < 1) return false;
|
|
13
|
+
try {
|
|
14
|
+
process.kill(pid, 0);
|
|
15
|
+
return true;
|
|
16
|
+
} catch (error) {
|
|
17
|
+
const code = typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
|
|
18
|
+
return code === "EPERM";
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function emptyActivity() {
|
|
22
|
+
return { version: LOCALGHOST_ACTIVITY_VERSION, runs: [] };
|
|
23
|
+
}
|
|
24
|
+
function readLocalghostActivity(path = getLocalghostActivityPath()) {
|
|
25
|
+
if (!existsSync(path)) return emptyActivity();
|
|
26
|
+
try {
|
|
27
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
28
|
+
return {
|
|
29
|
+
version: LOCALGHOST_ACTIVITY_VERSION,
|
|
30
|
+
runs: Array.isArray(parsed.runs) ? parsed.runs : []
|
|
31
|
+
};
|
|
32
|
+
} catch {
|
|
33
|
+
return emptyActivity();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function writeLocalghostActivity(activity, path = getLocalghostActivityPath()) {
|
|
37
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
38
|
+
writeFileSync(path, `${JSON.stringify(activity, null, 2)}
|
|
39
|
+
`, "utf8");
|
|
40
|
+
return path;
|
|
41
|
+
}
|
|
42
|
+
function createRunId(input, pid) {
|
|
43
|
+
return `${input.projectName}:${input.mode}:${pid}:${Date.now()}`;
|
|
44
|
+
}
|
|
45
|
+
function pruneLocalghostActivity(path = getLocalghostActivityPath()) {
|
|
46
|
+
const activity = readLocalghostActivity(path);
|
|
47
|
+
const activeRuns = activity.runs.filter((run) => isProcessRunning(run.pid));
|
|
48
|
+
const pruned = activeRuns.length !== activity.runs.length;
|
|
49
|
+
if (pruned) {
|
|
50
|
+
writeLocalghostActivity({ version: LOCALGHOST_ACTIVITY_VERSION, runs: activeRuns }, path);
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
path,
|
|
54
|
+
pruned,
|
|
55
|
+
runs: activeRuns
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function listLocalghostRuns(path = getLocalghostActivityPath()) {
|
|
59
|
+
return pruneLocalghostActivity(path).runs;
|
|
60
|
+
}
|
|
61
|
+
function registerLocalghostRun(input, path = getLocalghostActivityPath()) {
|
|
62
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
63
|
+
const pid = input.pid ?? process.pid;
|
|
64
|
+
const record = {
|
|
65
|
+
id: input.id ?? createRunId(input, pid),
|
|
66
|
+
mode: input.mode,
|
|
67
|
+
pid,
|
|
68
|
+
cwd: input.cwd,
|
|
69
|
+
projectName: input.projectName,
|
|
70
|
+
startedAt: input.startedAt ?? now,
|
|
71
|
+
updatedAt: now,
|
|
72
|
+
...input.configPath ? { configPath: input.configPath } : {},
|
|
73
|
+
...input.caddyfilePath ? { caddyfilePath: input.caddyfilePath } : {},
|
|
74
|
+
...input.caddyPid ? { caddyPid: input.caddyPid } : {},
|
|
75
|
+
...input.childPid ? { childPid: input.childPid } : {},
|
|
76
|
+
...input.childCommand ? { childCommand: input.childCommand } : {},
|
|
77
|
+
...typeof input.https === "boolean" ? { https: input.https } : {},
|
|
78
|
+
...input.requestedPort ? { requestedPort: input.requestedPort } : {},
|
|
79
|
+
...input.port ? { port: input.port } : {},
|
|
80
|
+
...typeof input.dynamicPort === "boolean" ? { dynamicPort: input.dynamicPort } : {},
|
|
81
|
+
entries: input.entries
|
|
82
|
+
};
|
|
83
|
+
const current = pruneLocalghostActivity(path).runs.filter((run) => run.id !== record.id);
|
|
84
|
+
writeLocalghostActivity({ version: LOCALGHOST_ACTIVITY_VERSION, runs: [...current, record] }, path);
|
|
85
|
+
return record;
|
|
86
|
+
}
|
|
87
|
+
function unregisterLocalghostRun(id, path = getLocalghostActivityPath()) {
|
|
88
|
+
const activity = readLocalghostActivity(path);
|
|
89
|
+
const runs = activity.runs.filter((run) => run.id !== id);
|
|
90
|
+
if (runs.length !== activity.runs.length) {
|
|
91
|
+
writeLocalghostActivity({ version: LOCALGHOST_ACTIVITY_VERSION, runs }, path);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
1
95
|
// src/config.ts
|
|
2
|
-
import { existsSync, readFileSync, readdirSync } from "fs";
|
|
3
|
-
import { basename, join, resolve } from "path";
|
|
96
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, readdirSync } from "fs";
|
|
97
|
+
import { basename, join as join2, resolve } from "path";
|
|
4
98
|
|
|
5
99
|
// src/parse.ts
|
|
6
100
|
var HOST_PATTERN = /^(?=.{1,253}$)(?!-)[a-z0-9-]+(\.[a-z0-9-]+)*\.?$/i;
|
|
@@ -68,7 +162,7 @@ function resolveDevHostsPath(options = {}) {
|
|
|
68
162
|
const searchedFiles = getConfigFileCandidates(options);
|
|
69
163
|
for (const fileName2 of searchedFiles) {
|
|
70
164
|
const path = resolve(cwd, fileName2);
|
|
71
|
-
if (
|
|
165
|
+
if (existsSync2(path)) {
|
|
72
166
|
return {
|
|
73
167
|
path,
|
|
74
168
|
fileName: basename(fileName2),
|
|
@@ -104,11 +198,11 @@ function readDevHosts(options = {}) {
|
|
|
104
198
|
`Missing Localghost config in ${cwd}. Looked for ${formatSearchedFiles(resolvedPath.searchedFiles, resolvedPath.configPattern)}. Run \`localghost init\` or pass --config/--config-pattern.`
|
|
105
199
|
);
|
|
106
200
|
}
|
|
107
|
-
return parseDevHosts(
|
|
201
|
+
return parseDevHosts(readFileSync2(resolvedPath.path, "utf8"), resolvedPath.fileName);
|
|
108
202
|
}
|
|
109
203
|
function getProjectName(cwd = process.cwd()) {
|
|
110
204
|
try {
|
|
111
|
-
const pkg = JSON.parse(
|
|
205
|
+
const pkg = JSON.parse(readFileSync2(join2(cwd, "package.json"), "utf8"));
|
|
112
206
|
const name = typeof pkg.name === "string" && pkg.name ? pkg.name : "app";
|
|
113
207
|
return sanitizeProjectName(name.replace(/^@/, ""));
|
|
114
208
|
} catch {
|
|
@@ -121,18 +215,18 @@ function sanitizeProjectName(value) {
|
|
|
121
215
|
}
|
|
122
216
|
|
|
123
217
|
// src/caddy.ts
|
|
124
|
-
import { dirname as
|
|
218
|
+
import { dirname as dirname3, join as join3 } from "path";
|
|
125
219
|
import { execa } from "execa";
|
|
126
220
|
|
|
127
221
|
// src/fs.ts
|
|
128
|
-
import { mkdirSync, readFileSync as
|
|
129
|
-
import { dirname } from "path";
|
|
222
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
223
|
+
import { dirname as dirname2 } from "path";
|
|
130
224
|
function readTextFile(path) {
|
|
131
|
-
return
|
|
225
|
+
return readFileSync3(path, "utf8");
|
|
132
226
|
}
|
|
133
227
|
function writeTextFile(path, value) {
|
|
134
|
-
|
|
135
|
-
|
|
228
|
+
mkdirSync2(dirname2(path), { recursive: true });
|
|
229
|
+
writeFileSync2(path, value, "utf8");
|
|
136
230
|
return path;
|
|
137
231
|
}
|
|
138
232
|
|
|
@@ -147,40 +241,197 @@ function groupByPort(entries) {
|
|
|
147
241
|
return groups;
|
|
148
242
|
}
|
|
149
243
|
function getCaddyfilePath(cwd = process.cwd()) {
|
|
150
|
-
return
|
|
244
|
+
return join3(cwd, "ops/local/Caddyfile");
|
|
151
245
|
}
|
|
152
|
-
function renderCaddyfile(entries) {
|
|
246
|
+
function renderCaddyfile(entries, options = {}) {
|
|
153
247
|
const groups = groupByPort(entries);
|
|
248
|
+
const https = options.https === true;
|
|
154
249
|
const blocks = [...groups.entries()].sort(([leftPort], [rightPort]) => leftPort - rightPort).map(([port, group]) => {
|
|
155
|
-
const hosts = group.map((entry) => entry.host).sort().join(", ");
|
|
250
|
+
const hosts = group.map((entry) => https ? entry.host : `http://${entry.host}`).sort().join(", ");
|
|
156
251
|
return `${hosts} {
|
|
157
252
|
reverse_proxy 127.0.0.1:${port}
|
|
158
253
|
}`;
|
|
159
254
|
});
|
|
160
|
-
|
|
255
|
+
const globalOptions = https ? `{
|
|
161
256
|
local_certs
|
|
162
257
|
}
|
|
163
258
|
|
|
164
|
-
|
|
259
|
+
` : "";
|
|
260
|
+
return `${globalOptions}${blocks.join("\n\n")}
|
|
165
261
|
`;
|
|
166
262
|
}
|
|
167
|
-
async function writeCaddyfile(entries, cwd = process.cwd()) {
|
|
263
|
+
async function writeCaddyfile(entries, cwd = process.cwd(), options = {}) {
|
|
168
264
|
const path = getCaddyfilePath(cwd);
|
|
169
|
-
writeTextFile(path, renderCaddyfile(entries));
|
|
265
|
+
writeTextFile(path, renderCaddyfile(entries, options));
|
|
170
266
|
return path;
|
|
171
267
|
}
|
|
172
268
|
async function validateCaddyfile(path) {
|
|
173
269
|
await execa("caddy", ["validate", "--config", path], {
|
|
174
|
-
cwd:
|
|
270
|
+
cwd: dirname3(path),
|
|
175
271
|
stdio: "inherit"
|
|
176
272
|
});
|
|
177
273
|
}
|
|
178
274
|
async function runCaddy(path) {
|
|
179
275
|
await execa("caddy", ["run", "--config", path], {
|
|
180
|
-
cwd:
|
|
276
|
+
cwd: dirname3(path),
|
|
181
277
|
stdio: "inherit"
|
|
182
278
|
});
|
|
183
279
|
}
|
|
280
|
+
function startCaddy(path) {
|
|
281
|
+
return execa("caddy", ["run", "--config", path], {
|
|
282
|
+
cwd: dirname3(path),
|
|
283
|
+
stdio: "inherit"
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
async function trustCaddy(path) {
|
|
287
|
+
await execa("caddy", ["trust", "--config", path], {
|
|
288
|
+
cwd: dirname3(path),
|
|
289
|
+
stdio: "inherit"
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// src/context.ts
|
|
294
|
+
import { existsSync as existsSync3 } from "fs";
|
|
295
|
+
import { pathToFileURL } from "url";
|
|
296
|
+
|
|
297
|
+
// src/port.ts
|
|
298
|
+
import { createServer } from "net";
|
|
299
|
+
async function isPortAvailable(port, host = "127.0.0.1") {
|
|
300
|
+
return new Promise((resolve2) => {
|
|
301
|
+
const server = createServer();
|
|
302
|
+
server.once("error", () => {
|
|
303
|
+
resolve2(false);
|
|
304
|
+
});
|
|
305
|
+
server.once("listening", () => {
|
|
306
|
+
server.close(() => resolve2(true));
|
|
307
|
+
});
|
|
308
|
+
server.listen(port, host);
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
async function findAvailablePort(startPort, options = {}) {
|
|
312
|
+
const host = options.host ?? "127.0.0.1";
|
|
313
|
+
const maxAttempts = options.maxAttempts ?? 50;
|
|
314
|
+
for (let offset = 0; offset < maxAttempts; offset += 1) {
|
|
315
|
+
const port = startPort + offset;
|
|
316
|
+
if (await isPortAvailable(port, host)) {
|
|
317
|
+
return port;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
throw new Error(`No available port found from ${startPort} to ${startPort + maxAttempts - 1}.`);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// src/context.ts
|
|
324
|
+
var LOCALGHOST_PROJECT_CONFIG_FILES = [
|
|
325
|
+
"localghost.config.mjs",
|
|
326
|
+
"localghost.config.js",
|
|
327
|
+
"localghost.config.cjs"
|
|
328
|
+
];
|
|
329
|
+
function parsePort(value) {
|
|
330
|
+
if (!value) return void 0;
|
|
331
|
+
const port = Number.parseInt(value, 10);
|
|
332
|
+
return Number.isInteger(port) && port > 0 && port <= 65535 ? port : void 0;
|
|
333
|
+
}
|
|
334
|
+
function envPort() {
|
|
335
|
+
return parsePort(process.env.LOCALGHOST_PORT) ?? parsePort(process.env.VITE_PORT);
|
|
336
|
+
}
|
|
337
|
+
function envDynamicPort() {
|
|
338
|
+
const value = process.env.LOCALGHOST_DYNAMIC_PORT;
|
|
339
|
+
if (!value) return void 0;
|
|
340
|
+
return ["1", "true", "yes", "on"].includes(value.toLowerCase());
|
|
341
|
+
}
|
|
342
|
+
function envHttps() {
|
|
343
|
+
const value = process.env.LOCALGHOST_HTTPS;
|
|
344
|
+
if (!value) return void 0;
|
|
345
|
+
return ["1", "true", "yes", "on"].includes(value.toLowerCase());
|
|
346
|
+
}
|
|
347
|
+
function readOptionsFromContext(options) {
|
|
348
|
+
return {
|
|
349
|
+
cwd: options.cwd ?? process.cwd(),
|
|
350
|
+
...options.fileName ? { fileName: options.fileName } : {},
|
|
351
|
+
...options.configFiles ? { configFiles: options.configFiles } : {},
|
|
352
|
+
...options.configPattern ? { configPattern: options.configPattern } : {}
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
function withRuntimePort(entries, requestedPort, port) {
|
|
356
|
+
if (requestedPort === port) return entries;
|
|
357
|
+
const hasRequestedPort = entries.some((entry) => entry.port === requestedPort);
|
|
358
|
+
if (!hasRequestedPort) return entries;
|
|
359
|
+
return entries.map((entry) => entry.port === requestedPort ? { ...entry, port } : entry);
|
|
360
|
+
}
|
|
361
|
+
function uniqueHosts(entries) {
|
|
362
|
+
return [...new Set(entries.map((entry) => entry.host))];
|
|
363
|
+
}
|
|
364
|
+
function isAliasableHost(host) {
|
|
365
|
+
return host.includes(".") && !host.startsWith("www.") && !host.includes(":");
|
|
366
|
+
}
|
|
367
|
+
function getDefaultWwwAlias(host) {
|
|
368
|
+
return isAliasableHost(host) ? `www.${host}` : null;
|
|
369
|
+
}
|
|
370
|
+
function addDefaultWwwAliases(entries) {
|
|
371
|
+
const seen = new Set(entries.map((entry) => entry.host));
|
|
372
|
+
const aliases = [];
|
|
373
|
+
for (const entry of entries) {
|
|
374
|
+
const alias = getDefaultWwwAlias(entry.host);
|
|
375
|
+
if (alias && !seen.has(alias)) {
|
|
376
|
+
aliases.push({ host: alias, port: entry.port, target: `127.0.0.1:${entry.port}` });
|
|
377
|
+
seen.add(alias);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return [...entries, ...aliases];
|
|
381
|
+
}
|
|
382
|
+
function defined(input) {
|
|
383
|
+
return Object.fromEntries(Object.entries(input).filter(([, value]) => typeof value !== "undefined"));
|
|
384
|
+
}
|
|
385
|
+
async function readProjectConfig(cwd, configFile) {
|
|
386
|
+
if (configFile === false) return {};
|
|
387
|
+
const candidates = configFile ? [configFile] : LOCALGHOST_PROJECT_CONFIG_FILES;
|
|
388
|
+
const path = candidates.map((candidate) => resolveDevHostsPath({ cwd, fileName: candidate }).path).find((candidate) => existsSync3(candidate));
|
|
389
|
+
if (!path) return {};
|
|
390
|
+
const imported = await import(`${pathToFileURL(path).href}?localghost=${Date.now()}`);
|
|
391
|
+
const config = imported.default ?? imported;
|
|
392
|
+
return { config, path };
|
|
393
|
+
}
|
|
394
|
+
function defineLocalghostConfig(config) {
|
|
395
|
+
return config;
|
|
396
|
+
}
|
|
397
|
+
async function resolveLocalghostContext(options = {}) {
|
|
398
|
+
const cwd = options.cwd ?? process.cwd();
|
|
399
|
+
const projectConfig = await readProjectConfig(cwd, options.localghostConfig);
|
|
400
|
+
const merged = {
|
|
401
|
+
...projectConfig.config,
|
|
402
|
+
...defined(options)
|
|
403
|
+
};
|
|
404
|
+
const readOptions = readOptionsFromContext({ ...merged, cwd });
|
|
405
|
+
const resolvedPath = resolveDevHostsPath(readOptions);
|
|
406
|
+
const configEntries = readDevHosts(readOptions);
|
|
407
|
+
const requestedPort = merged.port ?? envPort() ?? configEntries[0]?.port ?? 5173;
|
|
408
|
+
const dynamicPort = merged.dynamicPort ?? envDynamicPort() ?? false;
|
|
409
|
+
const bindHost = merged.bindHost ?? "127.0.0.1";
|
|
410
|
+
const probeHost = typeof bindHost === "string" ? bindHost : "127.0.0.1";
|
|
411
|
+
const port = dynamicPort ? await findAvailablePort(requestedPort, { host: probeHost }) : requestedPort;
|
|
412
|
+
const wwwAlias = merged.wwwAlias ?? true;
|
|
413
|
+
const entries = wwwAlias ? addDefaultWwwAliases(withRuntimePort(configEntries, requestedPort, port)) : withRuntimePort(configEntries, requestedPort, port);
|
|
414
|
+
const hosts = uniqueHosts(entries);
|
|
415
|
+
const primaryHost = merged.primaryHost ?? entries.find((entry) => entry.port === port)?.host ?? hosts[0] ?? `${sanitizeProjectName(getProjectName(cwd))}.localhost`;
|
|
416
|
+
return {
|
|
417
|
+
cwd,
|
|
418
|
+
projectName: sanitizeProjectName(merged.project ?? getProjectName(cwd)),
|
|
419
|
+
readOptions,
|
|
420
|
+
configPath: resolvedPath.path,
|
|
421
|
+
configFileName: resolvedPath.fileName,
|
|
422
|
+
configEntries,
|
|
423
|
+
entries,
|
|
424
|
+
hosts,
|
|
425
|
+
requestedPort,
|
|
426
|
+
port,
|
|
427
|
+
dynamicPort,
|
|
428
|
+
bindHost,
|
|
429
|
+
primaryHost,
|
|
430
|
+
https: merged.https ?? envHttps() ?? false,
|
|
431
|
+
wwwAlias,
|
|
432
|
+
...projectConfig.path ? { projectConfigPath: projectConfig.path } : {}
|
|
433
|
+
};
|
|
434
|
+
}
|
|
184
435
|
|
|
185
436
|
// src/doctor.ts
|
|
186
437
|
import { execa as execa2 } from "execa";
|
|
@@ -208,10 +459,34 @@ async function runDoctor() {
|
|
|
208
459
|
};
|
|
209
460
|
}
|
|
210
461
|
|
|
462
|
+
// src/env.ts
|
|
463
|
+
var PRODUCTION_ENV_KEYS = ["NODE_ENV", "VERCEL_ENV", "NETLIFY", "CF_PAGES_BRANCH", "LOCALGHOST_ENV"];
|
|
464
|
+
function getProductionReason(env = process.env) {
|
|
465
|
+
if (env.LOCALGHOST_ENV === "production") return "LOCALGHOST_ENV=production";
|
|
466
|
+
if (env.NODE_ENV === "production") return "NODE_ENV=production";
|
|
467
|
+
if (env.VERCEL_ENV === "production") return "VERCEL_ENV=production";
|
|
468
|
+
if (env.NETLIFY === "true" && env.CONTEXT === "production") return "NETLIFY=true and CONTEXT=production";
|
|
469
|
+
if (env.CF_PAGES_BRANCH && env.CF_PAGES_BRANCH === env.CF_PAGES_PRODUCTION_BRANCH) {
|
|
470
|
+
return "CF_PAGES_BRANCH matches CF_PAGES_PRODUCTION_BRANCH";
|
|
471
|
+
}
|
|
472
|
+
return null;
|
|
473
|
+
}
|
|
474
|
+
function isProductionLike(env = process.env) {
|
|
475
|
+
return getProductionReason(env) !== null;
|
|
476
|
+
}
|
|
477
|
+
function assertLocalDevelopment(command, env = process.env) {
|
|
478
|
+
const reason = getProductionReason(env);
|
|
479
|
+
if (!reason) return;
|
|
480
|
+
throw new Error(`Localghost only runs in local development. Refusing \`${command}\` because ${reason}.`);
|
|
481
|
+
}
|
|
482
|
+
function getProductionEnvKeys() {
|
|
483
|
+
return PRODUCTION_ENV_KEYS;
|
|
484
|
+
}
|
|
485
|
+
|
|
211
486
|
// src/hosts-file.ts
|
|
212
|
-
import { writeFileSync as
|
|
487
|
+
import { writeFileSync as writeFileSync3 } from "fs";
|
|
213
488
|
import { tmpdir } from "os";
|
|
214
|
-
import { join as
|
|
489
|
+
import { join as join4 } from "path";
|
|
215
490
|
import { execa as execa3 } from "execa";
|
|
216
491
|
function escapeRegExp(value) {
|
|
217
492
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -253,8 +528,8 @@ function removeManagedBlock(existing, projectName) {
|
|
|
253
528
|
}
|
|
254
529
|
async function writeSystemHostsFile(hostsPath, next, projectName) {
|
|
255
530
|
const sanitizedProjectName = sanitizeProjectName(projectName);
|
|
256
|
-
const tempPath =
|
|
257
|
-
|
|
531
|
+
const tempPath = join4(tmpdir(), `localghost-${sanitizedProjectName}-hosts`);
|
|
532
|
+
writeFileSync3(tempPath, next, "utf8");
|
|
258
533
|
if (process.platform === "win32") {
|
|
259
534
|
throw new Error(`Windows support: run as administrator and copy ${tempPath} to ${hostsPath}.`);
|
|
260
535
|
}
|
|
@@ -286,11 +561,11 @@ async function removeSystemHosts(projectName) {
|
|
|
286
561
|
}
|
|
287
562
|
|
|
288
563
|
// src/init.ts
|
|
289
|
-
import { existsSync as
|
|
290
|
-
import { join as
|
|
564
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
565
|
+
import { join as join5 } from "path";
|
|
291
566
|
function detectPackageManager(cwd = process.cwd()) {
|
|
292
|
-
if (
|
|
293
|
-
if (
|
|
567
|
+
if (existsSync4(join5(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
568
|
+
if (existsSync4(join5(cwd, "yarn.lock"))) return "yarn";
|
|
294
569
|
return "npm";
|
|
295
570
|
}
|
|
296
571
|
function packageRunCommand(packageManager, script) {
|
|
@@ -315,7 +590,7 @@ function renderConfig(options) {
|
|
|
315
590
|
}
|
|
316
591
|
function readPackageJson(path) {
|
|
317
592
|
try {
|
|
318
|
-
return JSON.parse(
|
|
593
|
+
return JSON.parse(readFileSync4(path, "utf8"));
|
|
319
594
|
} catch {
|
|
320
595
|
return null;
|
|
321
596
|
}
|
|
@@ -336,17 +611,25 @@ function updatePackageScripts(packageJsonPath, configFile) {
|
|
|
336
611
|
...scripts,
|
|
337
612
|
"localghost:setup": scripts["localghost:setup"] ?? `localghost setup${configFlag}`,
|
|
338
613
|
"localghost:proxy": scripts["localghost:proxy"] ?? `localghost dev${configFlag}`,
|
|
614
|
+
"localghost:proxy:https": scripts["localghost:proxy:https"] ?? `localghost dev${configFlag} --https`,
|
|
615
|
+
"localghost:run": scripts["localghost:run"] ?? `localghost run${configFlag} --`,
|
|
616
|
+
"localghost:ready": scripts["localghost:ready"] ?? `localghost status${configFlag} --ready`,
|
|
617
|
+
"localghost:trust": scripts["localghost:trust"] ?? `localghost trust${configFlag}`,
|
|
618
|
+
"localghost:ps": scripts["localghost:ps"] ?? "localghost ps",
|
|
339
619
|
"localghost:print": scripts["localghost:print"] ?? `localghost print${configFlag}`,
|
|
340
620
|
"localghost:routes": scripts["localghost:routes"] ?? `localghost routes${configFlag}`,
|
|
341
621
|
"localghost:status": scripts["localghost:status"] ?? "localghost status",
|
|
622
|
+
"localghost:reset": scripts["localghost:reset"] ?? "localghost reset",
|
|
342
623
|
"localghost:teardown": scripts["localghost:teardown"] ?? "localghost teardown",
|
|
343
624
|
"localghost:doctor": scripts["localghost:doctor"] ?? "localghost doctor",
|
|
344
|
-
"localghost:update": scripts["localghost:update"] ?? "localghost update"
|
|
625
|
+
"localghost:update": scripts["localghost:update"] ?? "localghost update",
|
|
626
|
+
"caddy:setup": scripts["caddy:setup"] ?? `localghost setup${configFlag}`,
|
|
627
|
+
"caddy:dev": scripts["caddy:dev"] ?? `localghost dev${configFlag}`
|
|
345
628
|
};
|
|
346
629
|
const changed = JSON.stringify(scripts) !== JSON.stringify(nextScripts);
|
|
347
630
|
if (!changed) return false;
|
|
348
631
|
pkg.scripts = nextScripts;
|
|
349
|
-
|
|
632
|
+
writeFileSync4(packageJsonPath, `${JSON.stringify(pkg, null, 2)}
|
|
350
633
|
`, "utf8");
|
|
351
634
|
return true;
|
|
352
635
|
}
|
|
@@ -359,8 +642,8 @@ function initLocalghost(options = {}) {
|
|
|
359
642
|
const apiPort = options.apiPort ?? 8787;
|
|
360
643
|
const packageManager = options.packageManager ?? detectPackageManager(cwd);
|
|
361
644
|
const configFile = options.configFile ?? LOCALGHOST_CONFIG_FILE;
|
|
362
|
-
const configPath =
|
|
363
|
-
const configExists =
|
|
645
|
+
const configPath = join5(cwd, configFile);
|
|
646
|
+
const configExists = existsSync4(configPath);
|
|
364
647
|
if (configExists && !options.force) {
|
|
365
648
|
return {
|
|
366
649
|
configPath,
|
|
@@ -370,22 +653,24 @@ function initLocalghost(options = {}) {
|
|
|
370
653
|
nextSteps: [
|
|
371
654
|
packageRunCommand(packageManager, "localghost:doctor"),
|
|
372
655
|
packageRunCommand(packageManager, "localghost:setup"),
|
|
656
|
+
packageRunCommand(packageManager, "localghost:ready"),
|
|
373
657
|
packageRunCommand(packageManager, "localghost:proxy")
|
|
374
658
|
]
|
|
375
659
|
};
|
|
376
660
|
}
|
|
377
661
|
writeTextFile(configPath, renderConfig({ host, port, apiHost, apiPort }));
|
|
378
|
-
const packageJsonPath =
|
|
662
|
+
const packageJsonPath = join5(cwd, "package.json");
|
|
379
663
|
const packageJsonChanged = options.writeScripts ? updatePackageScripts(packageJsonPath, configFile) : false;
|
|
380
664
|
return {
|
|
381
665
|
configPath,
|
|
382
666
|
configCreated: true,
|
|
383
|
-
...
|
|
667
|
+
...existsSync4(packageJsonPath) ? { packageJsonPath } : {},
|
|
384
668
|
packageJsonChanged,
|
|
385
669
|
packageManager,
|
|
386
670
|
nextSteps: [
|
|
387
671
|
packageRunCommand(packageManager, "localghost:doctor"),
|
|
388
672
|
packageRunCommand(packageManager, "localghost:setup"),
|
|
673
|
+
packageRunCommand(packageManager, "localghost:ready"),
|
|
389
674
|
packageRunCommand(packageManager, "localghost:proxy")
|
|
390
675
|
]
|
|
391
676
|
};
|
|
@@ -393,7 +678,7 @@ function initLocalghost(options = {}) {
|
|
|
393
678
|
|
|
394
679
|
// src/routes.ts
|
|
395
680
|
function getDomainRoutes(entries, options = {}) {
|
|
396
|
-
const protocol = options.https ===
|
|
681
|
+
const protocol = options.https === true ? "https" : "http";
|
|
397
682
|
return [...entries].sort((left, right) => left.host.localeCompare(right.host) || left.port - right.port).map((entry) => ({
|
|
398
683
|
host: entry.host,
|
|
399
684
|
port: entry.port,
|
|
@@ -413,30 +698,35 @@ function formatDomainRoutes(entries, options = {}) {
|
|
|
413
698
|
}
|
|
414
699
|
|
|
415
700
|
// src/state.ts
|
|
416
|
-
import { existsSync as
|
|
417
|
-
import { join as
|
|
701
|
+
import { existsSync as existsSync5 } from "fs";
|
|
702
|
+
import { join as join6 } from "path";
|
|
418
703
|
var LOCALGHOST_STATE_FILE = "ops/local/localghost-state.json";
|
|
419
704
|
function getLocalghostStatePath(cwd = process.cwd()) {
|
|
420
|
-
return
|
|
705
|
+
return join6(cwd, LOCALGHOST_STATE_FILE);
|
|
421
706
|
}
|
|
422
707
|
function readLocalghostState(cwd = process.cwd()) {
|
|
423
708
|
const path = getLocalghostStatePath(cwd);
|
|
424
|
-
if (!
|
|
709
|
+
if (!existsSync5(path)) return null;
|
|
425
710
|
return JSON.parse(readTextFile(path));
|
|
426
711
|
}
|
|
427
712
|
function writeLocalghostState(cwd, state) {
|
|
428
713
|
const path = getLocalghostStatePath(cwd);
|
|
429
|
-
writeTextFile(path, `${JSON.stringify({ version: 1, updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
714
|
+
writeTextFile(path, `${JSON.stringify({ ...state, version: 1, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)}
|
|
430
715
|
`);
|
|
431
716
|
return path;
|
|
432
717
|
}
|
|
718
|
+
function patchLocalghostState(cwd, patch) {
|
|
719
|
+
const current = readLocalghostState(cwd);
|
|
720
|
+
if (!current) return null;
|
|
721
|
+
return writeLocalghostState(cwd, { ...current, ...patch });
|
|
722
|
+
}
|
|
433
723
|
|
|
434
724
|
// src/update-check.ts
|
|
435
|
-
import { existsSync as
|
|
436
|
-
import { homedir } from "os";
|
|
437
|
-
import { dirname as
|
|
725
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
726
|
+
import { homedir as homedir2 } from "os";
|
|
727
|
+
import { dirname as dirname4, join as join7 } from "path";
|
|
438
728
|
var LOCALGHOST_PACKAGE_NAME = "@hamedb89/localghost";
|
|
439
|
-
var LOCALGHOST_VERSION = "0.1.
|
|
729
|
+
var LOCALGHOST_VERSION = "0.1.8";
|
|
440
730
|
var UPDATE_CHECK_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
441
731
|
var UPDATE_CHECK_NOTIFY_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
442
732
|
var UPDATE_CHECK_TIMEOUT_MS = 900;
|
|
@@ -448,21 +738,21 @@ function isUpdateCheckDisabled(env = process.env) {
|
|
|
448
738
|
}
|
|
449
739
|
function getUpdateCheckCachePath(env = process.env) {
|
|
450
740
|
if (env.LOCALGHOST_UPDATE_CHECK_CACHE) return env.LOCALGHOST_UPDATE_CHECK_CACHE;
|
|
451
|
-
const cacheRoot = env.XDG_CACHE_HOME ||
|
|
452
|
-
return
|
|
741
|
+
const cacheRoot = env.XDG_CACHE_HOME || join7(homedir2(), ".cache");
|
|
742
|
+
return join7(cacheRoot, "localghost", "update-check.json");
|
|
453
743
|
}
|
|
454
744
|
function readCache(path = getUpdateCheckCachePath()) {
|
|
455
|
-
if (!
|
|
745
|
+
if (!existsSync6(path)) return null;
|
|
456
746
|
try {
|
|
457
|
-
return JSON.parse(
|
|
747
|
+
return JSON.parse(readFileSync5(path, "utf8"));
|
|
458
748
|
} catch {
|
|
459
749
|
return null;
|
|
460
750
|
}
|
|
461
751
|
}
|
|
462
752
|
function writeCache(cache, path = getUpdateCheckCachePath()) {
|
|
463
753
|
try {
|
|
464
|
-
|
|
465
|
-
|
|
754
|
+
mkdirSync3(dirname4(path), { recursive: true });
|
|
755
|
+
writeFileSync5(path, `${JSON.stringify(cache, null, 2)}
|
|
466
756
|
`, "utf8");
|
|
467
757
|
} catch {
|
|
468
758
|
}
|
|
@@ -598,6 +888,7 @@ ${message}`);
|
|
|
598
888
|
markUpdateNotified(result, cachePath);
|
|
599
889
|
}
|
|
600
890
|
export {
|
|
891
|
+
LOCALGHOST_ACTIVITY_VERSION,
|
|
601
892
|
LOCALGHOST_CONFIG_FILE,
|
|
602
893
|
LOCALGHOST_PACKAGE_NAME,
|
|
603
894
|
LOCALGHOST_STATE_FILE,
|
|
@@ -605,10 +896,13 @@ export {
|
|
|
605
896
|
UPDATE_CHECK_CACHE_TTL_MS,
|
|
606
897
|
UPDATE_CHECK_NOTIFY_TTL_MS,
|
|
607
898
|
UPDATE_CHECK_TIMEOUT_MS,
|
|
899
|
+
assertLocalDevelopment,
|
|
608
900
|
checkCaddy,
|
|
609
901
|
checkForUpdate,
|
|
610
902
|
compareVersions,
|
|
903
|
+
defineLocalghostConfig,
|
|
611
904
|
detectPackageManager,
|
|
905
|
+
findAvailablePort,
|
|
612
906
|
findLocalMdnsHosts,
|
|
613
907
|
formatDomainRoutes,
|
|
614
908
|
formatUpdateMessage,
|
|
@@ -616,33 +910,49 @@ export {
|
|
|
616
910
|
getConfigFileCandidates,
|
|
617
911
|
getDevHostsPath,
|
|
618
912
|
getDomainRoutes,
|
|
913
|
+
getLocalghostActivityPath,
|
|
619
914
|
getLocalghostStatePath,
|
|
915
|
+
getProductionEnvKeys,
|
|
916
|
+
getProductionReason,
|
|
620
917
|
getProjectName,
|
|
621
918
|
getSystemHostsPath,
|
|
622
919
|
getUpdateCheckCachePath,
|
|
623
920
|
initLocalghost,
|
|
624
921
|
isNewerVersion,
|
|
922
|
+
isPortAvailable,
|
|
923
|
+
isProcessRunning,
|
|
924
|
+
isProductionLike,
|
|
625
925
|
isUpdateCheckDisabled,
|
|
926
|
+
listLocalghostRuns,
|
|
626
927
|
markUpdateNotified,
|
|
627
928
|
maybeNotifyAboutUpdate,
|
|
628
929
|
packageAddCommand,
|
|
629
930
|
packageRunCommand,
|
|
630
931
|
parseDevHosts,
|
|
932
|
+
patchLocalghostState,
|
|
933
|
+
pruneLocalghostActivity,
|
|
631
934
|
readDevHosts,
|
|
935
|
+
readLocalghostActivity,
|
|
632
936
|
readLocalghostState,
|
|
937
|
+
registerLocalghostRun,
|
|
633
938
|
removeManagedBlock,
|
|
634
939
|
removeSystemHosts,
|
|
635
940
|
renderCaddyfile,
|
|
636
941
|
renderHostsBlock,
|
|
637
942
|
resolveDevHostsPath,
|
|
943
|
+
resolveLocalghostContext,
|
|
638
944
|
runCaddy,
|
|
639
945
|
runDoctor,
|
|
640
946
|
sanitizeProjectName,
|
|
641
947
|
shouldNotifyAboutUpdate,
|
|
948
|
+
startCaddy,
|
|
949
|
+
trustCaddy,
|
|
950
|
+
unregisterLocalghostRun,
|
|
642
951
|
updateSystemHosts,
|
|
643
952
|
upsertManagedBlock,
|
|
644
953
|
validateCaddyfile,
|
|
645
954
|
writeCaddyfile,
|
|
955
|
+
writeLocalghostActivity,
|
|
646
956
|
writeLocalghostState
|
|
647
957
|
};
|
|
648
958
|
//# sourceMappingURL=index.js.map
|