@within-7/jetr 0.4.3 → 0.5.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/dist/cli.js +170 -57
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import { resolve as resolve2, basename } from "path";
|
|
4
|
+
import { resolve as resolve2, basename as basename2 } from "path";
|
|
5
5
|
import { existsSync as existsSync4, statSync as statSync2 } from "fs";
|
|
6
6
|
import { Command } from "commander";
|
|
7
7
|
import chalk2 from "chalk";
|
|
@@ -252,6 +252,7 @@ function createJetrignore(dir) {
|
|
|
252
252
|
}
|
|
253
253
|
|
|
254
254
|
// src/deploy.ts
|
|
255
|
+
import { basename } from "path";
|
|
255
256
|
function hashFile(filePath) {
|
|
256
257
|
const content = readFileSync3(filePath);
|
|
257
258
|
const hash = createHash("sha256").update(content).digest("hex");
|
|
@@ -323,6 +324,49 @@ async function deploy(siteName, directory, onProgress) {
|
|
|
323
324
|
totalSize: result.total_size
|
|
324
325
|
};
|
|
325
326
|
}
|
|
327
|
+
async function deployFiles(siteName, filePaths, onProgress) {
|
|
328
|
+
onProgress?.(`Preparing ${filePaths.length} file(s)...`);
|
|
329
|
+
const manifest = {};
|
|
330
|
+
const absMap = {};
|
|
331
|
+
for (const fp of filePaths) {
|
|
332
|
+
const abs = resolve(fp);
|
|
333
|
+
const name = basename(abs);
|
|
334
|
+
const hash = hashFile(abs);
|
|
335
|
+
const size = statSync(abs).size;
|
|
336
|
+
manifest[name] = { hash, size };
|
|
337
|
+
absMap[name] = abs;
|
|
338
|
+
}
|
|
339
|
+
onProgress?.("Computing diff...");
|
|
340
|
+
const diff = await api.deployDiff(siteName, manifest);
|
|
341
|
+
onProgress?.(
|
|
342
|
+
`Upload: ${diff.upload.length}, Delete: ${diff.delete.length}, Unchanged: ${diff.unchanged.length}`
|
|
343
|
+
);
|
|
344
|
+
if (diff.upload.length > 0) {
|
|
345
|
+
const CONCURRENCY = 10;
|
|
346
|
+
let completed = 0;
|
|
347
|
+
const total = diff.upload.length;
|
|
348
|
+
const uploadOne = async (name) => {
|
|
349
|
+
const content = readFileSync3(absMap[name]);
|
|
350
|
+
await api.uploadFile(siteName, name, content, diff.deploy_id, manifest[name].hash);
|
|
351
|
+
completed++;
|
|
352
|
+
onProgress?.(`Uploading (${completed}/${total}) ${name}`);
|
|
353
|
+
};
|
|
354
|
+
for (let i = 0; i < total; i += CONCURRENCY) {
|
|
355
|
+
const batch = diff.upload.slice(i, i + CONCURRENCY);
|
|
356
|
+
await Promise.all(batch.map(uploadOne));
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
onProgress?.("Finalizing...");
|
|
360
|
+
const result = await api.finalize(siteName, diff.deploy_id);
|
|
361
|
+
return {
|
|
362
|
+
url: `https://${siteName}.jetr.within-7.com`,
|
|
363
|
+
filesUploaded: result.files_uploaded,
|
|
364
|
+
filesDeleted: result.files_deleted,
|
|
365
|
+
filesUnchanged: diff.unchanged.length,
|
|
366
|
+
filesIgnored: 0,
|
|
367
|
+
totalSize: result.total_size
|
|
368
|
+
};
|
|
369
|
+
}
|
|
326
370
|
|
|
327
371
|
// src/rc.ts
|
|
328
372
|
import { existsSync as existsSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
@@ -409,71 +453,60 @@ async function promptSelection(options) {
|
|
|
409
453
|
// src/cli.ts
|
|
410
454
|
var program = new Command();
|
|
411
455
|
program.name("jetr").description("Deploy static sites instantly").version("0.2.0");
|
|
412
|
-
program.argument("[
|
|
413
|
-
const dir = resolve2(directory || ".");
|
|
414
|
-
if (!existsSync4(dir)) {
|
|
415
|
-
console.error(chalk2.red(`Path not found: ${dir}`));
|
|
416
|
-
process.exit(1);
|
|
417
|
-
}
|
|
418
|
-
if (!statSync2(dir).isDirectory()) {
|
|
419
|
-
console.error(chalk2.red(`Not a directory: ${dir}`));
|
|
420
|
-
process.exit(1);
|
|
421
|
-
}
|
|
456
|
+
program.argument("[paths...]", "Files or directory to deploy").action(async (paths) => {
|
|
422
457
|
const config = loadConfig();
|
|
423
458
|
if (!config.token) {
|
|
424
459
|
console.error(chalk2.red("Not logged in."));
|
|
425
460
|
console.error(`Run: ${chalk2.cyan("jetr login")}`);
|
|
426
461
|
process.exit(1);
|
|
427
462
|
}
|
|
428
|
-
|
|
429
|
-
if (
|
|
430
|
-
const
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
463
|
+
const { mode, dir, files, explicitName } = parseDeployArgs(paths);
|
|
464
|
+
if (mode === "directory") {
|
|
465
|
+
const rcDir = dir;
|
|
466
|
+
let siteName = await resolveSiteName(rcDir, explicitName);
|
|
467
|
+
if (!siteName) {
|
|
468
|
+
const dirName = sanitizeName(basename2(resolve2(rcDir)));
|
|
469
|
+
siteName = dirName || generateName();
|
|
470
|
+
console.log(chalk2.dim(`Site name: ${siteName}`));
|
|
471
|
+
}
|
|
472
|
+
siteName = sanitizeName(siteName);
|
|
473
|
+
if (!siteName) siteName = generateName();
|
|
474
|
+
const spinner = ora("").start();
|
|
475
|
+
try {
|
|
476
|
+
await ensureSiteExists(spinner, siteName);
|
|
477
|
+
const result = await deploy(siteName, rcDir, (msg) => {
|
|
478
|
+
spinner.text = msg;
|
|
479
|
+
});
|
|
480
|
+
printDeployResult(spinner, siteName, result);
|
|
481
|
+
const rc = loadRc(rcDir);
|
|
482
|
+
saveRc(rcDir, updateRc(rc, siteName));
|
|
483
|
+
} catch (e) {
|
|
484
|
+
spinner.fail(e.message);
|
|
485
|
+
process.exit(1);
|
|
486
|
+
}
|
|
487
|
+
} else {
|
|
488
|
+
console.log(chalk2.dim(`File mode: ${files.map((f) => basename2(f)).join(", ")}`));
|
|
489
|
+
let siteName = explicitName;
|
|
490
|
+
if (!siteName) {
|
|
491
|
+
const fileName = sanitizeName(
|
|
492
|
+
basename2(files[0]).replace(/\.[^.]+$/, "")
|
|
443
493
|
);
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
}
|
|
451
|
-
const spinner = ora("").start();
|
|
452
|
-
try {
|
|
453
|
-
spinner.text = "Checking site...";
|
|
494
|
+
siteName = fileName || generateName();
|
|
495
|
+
console.log(chalk2.dim(`Site name: ${siteName}`));
|
|
496
|
+
}
|
|
497
|
+
siteName = sanitizeName(siteName);
|
|
498
|
+
if (!siteName) siteName = generateName();
|
|
499
|
+
const spinner = ora("").start();
|
|
454
500
|
try {
|
|
455
|
-
await
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
501
|
+
await ensureSiteExists(spinner, siteName);
|
|
502
|
+
const result = await deployFiles(siteName, files, (msg) => {
|
|
503
|
+
spinner.text = msg;
|
|
504
|
+
});
|
|
505
|
+
printDeployResult(spinner, siteName, result);
|
|
506
|
+
} catch (e) {
|
|
507
|
+
spinner.fail(e.message);
|
|
508
|
+
process.exit(1);
|
|
459
509
|
}
|
|
460
|
-
const result = await deploy(siteName, dir, (msg) => {
|
|
461
|
-
spinner.text = msg;
|
|
462
|
-
});
|
|
463
|
-
spinner.succeed("Deployed!");
|
|
464
|
-
console.log();
|
|
465
|
-
console.log(` ${chalk2.bold("Site:")} ${chalk2.cyan(siteName)}`);
|
|
466
|
-
console.log(` ${chalk2.bold("URL:")} ${chalk2.green(result.url)}`);
|
|
467
|
-
console.log(
|
|
468
|
-
` ${chalk2.bold("Files:")} ${result.filesUploaded} uploaded` + (result.filesDeleted > 0 ? `, ${result.filesDeleted} deleted` : "") + (result.filesUnchanged > 0 ? `, ${result.filesUnchanged} unchanged` : "") + (result.filesIgnored > 0 ? chalk2.dim(` (${result.filesIgnored} ignored)`) : "")
|
|
469
|
-
);
|
|
470
|
-
console.log(` ${chalk2.bold("Size:")} ${formatBytes(result.totalSize)}`);
|
|
471
|
-
console.log();
|
|
472
|
-
const rc = loadRc(dir);
|
|
473
|
-
saveRc(dir, updateRc(rc, siteName));
|
|
474
|
-
} catch (e) {
|
|
475
|
-
spinner.fail(e.message);
|
|
476
|
-
process.exit(1);
|
|
477
510
|
}
|
|
478
511
|
});
|
|
479
512
|
program.command("login").description("Login with username and password").argument("[user]", "Username").argument("[password]", "Password").option("--token <token>", "Login directly with a JWT token").action(async (user, password, opts) => {
|
|
@@ -668,6 +701,86 @@ tokenCmd.command("revoke").description("Revoke a share token").argument("<site>"
|
|
|
668
701
|
process.exit(1);
|
|
669
702
|
}
|
|
670
703
|
});
|
|
704
|
+
function parseDeployArgs(paths) {
|
|
705
|
+
if (paths.length === 0) {
|
|
706
|
+
return { mode: "directory", dir: "." };
|
|
707
|
+
}
|
|
708
|
+
const first = resolve2(paths[0]);
|
|
709
|
+
if (existsSync4(first) && statSync2(first).isDirectory()) {
|
|
710
|
+
return { mode: "directory", dir: first, explicitName: paths[1] };
|
|
711
|
+
}
|
|
712
|
+
const files = [];
|
|
713
|
+
let explicitName;
|
|
714
|
+
for (const p of paths) {
|
|
715
|
+
const abs = resolve2(p);
|
|
716
|
+
if (existsSync4(abs) && statSync2(abs).isFile()) {
|
|
717
|
+
files.push(abs);
|
|
718
|
+
} else if (files.length > 0) {
|
|
719
|
+
explicitName = p;
|
|
720
|
+
break;
|
|
721
|
+
} else {
|
|
722
|
+
console.error(chalk2.red(`Not found: ${p}`));
|
|
723
|
+
process.exit(1);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
if (files.length === 0) {
|
|
727
|
+
return { mode: "directory", dir: "." };
|
|
728
|
+
}
|
|
729
|
+
return { mode: "files", files, explicitName };
|
|
730
|
+
}
|
|
731
|
+
function generateName() {
|
|
732
|
+
const adjectives = [
|
|
733
|
+
"swift",
|
|
734
|
+
"cool",
|
|
735
|
+
"fast",
|
|
736
|
+
"neat",
|
|
737
|
+
"bold",
|
|
738
|
+
"calm",
|
|
739
|
+
"warm",
|
|
740
|
+
"keen",
|
|
741
|
+
"fair",
|
|
742
|
+
"wise"
|
|
743
|
+
];
|
|
744
|
+
const nouns = [
|
|
745
|
+
"page",
|
|
746
|
+
"site",
|
|
747
|
+
"note",
|
|
748
|
+
"docs",
|
|
749
|
+
"view",
|
|
750
|
+
"deck",
|
|
751
|
+
"leaf",
|
|
752
|
+
"post",
|
|
753
|
+
"blog",
|
|
754
|
+
"demo"
|
|
755
|
+
];
|
|
756
|
+
const adj = adjectives[Math.floor(Math.random() * adjectives.length)];
|
|
757
|
+
const noun = nouns[Math.floor(Math.random() * nouns.length)];
|
|
758
|
+
const hex = Math.floor(Math.random() * 65535).toString(16).padStart(4, "0");
|
|
759
|
+
return `${adj}-${noun}-${hex}`;
|
|
760
|
+
}
|
|
761
|
+
function sanitizeName(name) {
|
|
762
|
+
return name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/^-+|-+$/g, "").replace(/-+/g, "-");
|
|
763
|
+
}
|
|
764
|
+
async function ensureSiteExists(spinner, siteName) {
|
|
765
|
+
spinner.text = "Checking site...";
|
|
766
|
+
try {
|
|
767
|
+
await api.getSite(siteName);
|
|
768
|
+
} catch {
|
|
769
|
+
spinner.text = `Creating site ${siteName}...`;
|
|
770
|
+
await api.createSite(siteName);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
function printDeployResult(spinner, siteName, result) {
|
|
774
|
+
spinner.succeed("Deployed!");
|
|
775
|
+
console.log();
|
|
776
|
+
console.log(` ${chalk2.bold("Site:")} ${chalk2.cyan(siteName)}`);
|
|
777
|
+
console.log(` ${chalk2.bold("URL:")} ${chalk2.green(result.url)}`);
|
|
778
|
+
console.log(
|
|
779
|
+
` ${chalk2.bold("Files:")} ${result.filesUploaded} uploaded` + (result.filesDeleted > 0 ? `, ${result.filesDeleted} deleted` : "") + (result.filesUnchanged > 0 ? `, ${result.filesUnchanged} unchanged` : "") + (result.filesIgnored > 0 ? chalk2.dim(` (${result.filesIgnored} ignored)`) : "")
|
|
780
|
+
);
|
|
781
|
+
console.log(` ${chalk2.bold("Size:")} ${formatBytes(result.totalSize)}`);
|
|
782
|
+
console.log();
|
|
783
|
+
}
|
|
671
784
|
function readSecret(prompt) {
|
|
672
785
|
return new Promise((resolve3) => {
|
|
673
786
|
process.stdout.write(prompt);
|