@jxsuite/import 0.41.1 → 0.42.1
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/package.json +5 -5
- package/src/browser-local.ts +2 -1
- package/src/io.ts +2 -2
- package/src/run.ts +2 -2
- package/src/verify.ts +49 -27
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jxsuite/import",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.42.1",
|
|
4
4
|
"description": "Clone a live website into a Jx project",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
"import": "bun run src/cli.ts"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@jxsuite/markup": "^1.0.
|
|
39
|
-
"@jxsuite/protocol": "^
|
|
40
|
-
"@jxsuite/schema": "^
|
|
38
|
+
"@jxsuite/markup": "^1.0.2",
|
|
39
|
+
"@jxsuite/protocol": "^6.1.0",
|
|
40
|
+
"@jxsuite/schema": "^5.0.0",
|
|
41
41
|
"pixelmatch": "^8.0.0",
|
|
42
42
|
"pngjs": "^7.0.0",
|
|
43
43
|
"puppeteer-core": "^25.12.0"
|
|
@@ -49,6 +49,6 @@
|
|
|
49
49
|
"happy-dom": "^21.0.3"
|
|
50
50
|
},
|
|
51
51
|
"optionalDependencies": {
|
|
52
|
-
"@jxsuite/compiler": "^
|
|
52
|
+
"@jxsuite/compiler": "^8.0.1"
|
|
53
53
|
}
|
|
54
54
|
}
|
package/src/browser-local.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* the same pipeline against a `@cloudflare/puppeteer` session.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
import { which } from "@jxsuite/schema/which";
|
|
13
14
|
import { launch } from "puppeteer-core";
|
|
14
15
|
import type { Browser } from "puppeteer-core";
|
|
15
16
|
|
|
@@ -30,7 +31,7 @@ function findChrome(executablePath?: string): string {
|
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
for (const name of DEFAULT_CHROME_PATHS) {
|
|
33
|
-
const path =
|
|
34
|
+
const path = which(name);
|
|
34
35
|
if (path) {
|
|
35
36
|
return path;
|
|
36
37
|
}
|
package/src/io.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* `node:fs`, and a Worker supplies its own without the pipeline knowing the difference.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { mkdir } from "node:fs/promises";
|
|
11
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
12
12
|
import { dirname, join } from "node:path";
|
|
13
13
|
|
|
14
14
|
export interface ImportIo {
|
|
@@ -43,7 +43,7 @@ export function createLocalIo(outDir: string): ImportIo {
|
|
|
43
43
|
async write(relPath, data) {
|
|
44
44
|
const path = resolve(relPath);
|
|
45
45
|
await mkdir(dirname(path), { recursive: true });
|
|
46
|
-
await
|
|
46
|
+
await writeFile(path, data);
|
|
47
47
|
},
|
|
48
48
|
async mkdir(relPath) {
|
|
49
49
|
await mkdir(resolve(relPath), { recursive: true });
|
package/src/run.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import { existsSync, readdirSync } from "node:fs";
|
|
12
12
|
import { isAbsolute, join } from "node:path";
|
|
13
|
-
import { mkdir } from "node:fs/promises";
|
|
13
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
14
14
|
import { closeBrowser, launchBrowser } from "./browser-local.ts";
|
|
15
15
|
import { createLocalIo } from "./io.ts";
|
|
16
16
|
import { runImportPipeline } from "./pipeline.ts";
|
|
@@ -234,7 +234,7 @@ export async function importSite(
|
|
|
234
234
|
async function seedProject(): Promise<void> {
|
|
235
235
|
await mkdir(outDir, { recursive: true });
|
|
236
236
|
const seed = { name: seedName(url), imports: {}, images: { optimize: false } };
|
|
237
|
-
await
|
|
237
|
+
await writeFile(join(outDir, PROJECT_FILE), `${JSON.stringify(seed, null, 2)}\n`);
|
|
238
238
|
onProgress?.({
|
|
239
239
|
message: `Created ${outDir}`,
|
|
240
240
|
phase: "seed",
|
package/src/verify.ts
CHANGED
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
* during import.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import { createServer } from "node:http";
|
|
11
|
+
import type { Server } from "node:http";
|
|
10
12
|
import { extname, join } from "node:path";
|
|
11
|
-
import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
13
|
+
import { createReadStream, readFileSync, statSync, writeFileSync, mkdirSync } from "node:fs";
|
|
12
14
|
import { isFunction } from "@jxsuite/schema/guards";
|
|
13
15
|
import type { Browser, Page } from "puppeteer-core";
|
|
14
16
|
import { launchBrowser, closeBrowser } from "./browser-local.ts";
|
|
@@ -240,37 +242,57 @@ const CONTENT_TYPES = new Map<string, string>(
|
|
|
240
242
|
|
|
241
243
|
/** A running static server and the origin it answers on. */
|
|
242
244
|
export interface ServedDirectory {
|
|
243
|
-
server:
|
|
245
|
+
server: { stop: () => Promise<void> };
|
|
244
246
|
baseUrl: string;
|
|
245
247
|
}
|
|
246
248
|
|
|
249
|
+
/** True when `path` names a regular file. */
|
|
250
|
+
function isFile(path: string): boolean {
|
|
251
|
+
try {
|
|
252
|
+
return statSync(path).isFile();
|
|
253
|
+
} catch {
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
247
258
|
/**
|
|
248
|
-
* Serve a directory over HTTP
|
|
249
|
-
*
|
|
259
|
+
* Serve a directory over loopback HTTP. Resolves with a stop handle and the base URL once the
|
|
260
|
+
* server is listening.
|
|
261
|
+
*
|
|
262
|
+
* `node:http` rather than `Bun.serve`: verification runs inside the desktop backend, which is
|
|
263
|
+
* Electron's Node (specs/desktop.md §7.1), and a static server needs nothing either runtime lacks.
|
|
250
264
|
*/
|
|
251
|
-
export function serveDirectory(dir: string): ServedDirectory {
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
}
|
|
269
|
-
return new Response("Not Found", { status: 404 });
|
|
270
|
-
},
|
|
265
|
+
export async function serveDirectory(dir: string): Promise<ServedDirectory> {
|
|
266
|
+
const http: Server = createServer((req, res) => {
|
|
267
|
+
let { pathname } = new URL(req.url ?? "/", "http://localhost");
|
|
268
|
+
if (pathname === "/") {
|
|
269
|
+
pathname = "/index.html";
|
|
270
|
+
} else if (!pathname.includes(".")) {
|
|
271
|
+
pathname = `${pathname}/index.html`;
|
|
272
|
+
}
|
|
273
|
+
const filePath = join(dir, pathname);
|
|
274
|
+
if (!isFile(filePath)) {
|
|
275
|
+
res.writeHead(404).end("Not Found");
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
const contentType =
|
|
279
|
+
CONTENT_TYPES.get(extname(filePath).toLowerCase()) ?? "application/octet-stream";
|
|
280
|
+
res.writeHead(200, { "Content-Type": contentType });
|
|
281
|
+
createReadStream(filePath).pipe(res);
|
|
271
282
|
});
|
|
272
|
-
|
|
273
|
-
|
|
283
|
+
await new Promise<void>((resolve) => {
|
|
284
|
+
http.listen(0, "127.0.0.1", resolve);
|
|
285
|
+
});
|
|
286
|
+
/* SAFETY: a TCP listener's `address()` is an `AddressInfo`; it is a string only for a pipe. */
|
|
287
|
+
const { port } = http.address() as { port: number };
|
|
288
|
+
const server = {
|
|
289
|
+
stop: () =>
|
|
290
|
+
new Promise<void>((resolve) => {
|
|
291
|
+
http.closeAllConnections();
|
|
292
|
+
http.close(() => resolve());
|
|
293
|
+
}),
|
|
294
|
+
};
|
|
295
|
+
return { server, baseUrl: `http://127.0.0.1:${port}` };
|
|
274
296
|
}
|
|
275
297
|
|
|
276
298
|
function resolveScreenshot(ref: Buffer | string): Buffer {
|
|
@@ -339,7 +361,7 @@ export async function verifyProject(opts: VerifyOptions): Promise<VerifyResult>
|
|
|
339
361
|
|
|
340
362
|
// Step 2: Serve the built output
|
|
341
363
|
onProgress("Starting local server for rendered pages...");
|
|
342
|
-
const { server, baseUrl } = serveDirectory(distDir);
|
|
364
|
+
const { server, baseUrl } = await serveDirectory(distDir);
|
|
343
365
|
|
|
344
366
|
const browser = opts.browser ?? (await launchBrowser());
|
|
345
367
|
const results: PageVerifyResult[] = [];
|