elm-pages 3.0.12 → 3.0.14
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 +2 -26
- package/codegen/elm-pages-codegen.cjs +10741 -10302
- package/generator/src/build.js +15 -5
- package/generator/src/cli.js +3 -5
- package/generator/src/compatibility-key.js +2 -2
- package/generator/src/dev-server.js +3 -0
- package/generator/src/render.js +681 -50
- package/generator/src/request-cache.js +13 -6
- package/generator/src/spinnies/index.js +200 -0
- package/generator/src/spinnies/utils.js +123 -0
- package/generator/src/validate-stream.js +25 -0
- package/generator/template/elm.json +4 -4
- package/generator/template/package.json +6 -6
- package/generator/template/script/elm.json +7 -8
- package/package.json +4 -3
- package/src/BackendTask/Custom.elm +38 -0
- package/src/BackendTask/Do.elm +233 -0
- package/src/BackendTask/File.elm +24 -9
- package/src/BackendTask/Glob.elm +208 -25
- package/src/BackendTask/Http.elm +32 -21
- package/src/BackendTask/Internal/Glob.elm +16 -4
- package/src/BackendTask/Stream.elm +1179 -0
- package/src/BackendTask.elm +214 -7
- package/src/Pages/Internal/Platform/CompatibilityKey.elm +1 -1
- package/src/Pages/Internal/Platform.elm +11 -2
- package/src/Pages/Script/Spinner.elm +505 -0
- package/src/Pages/Script.elm +199 -2
- package/src/Pages/StaticHttp/Request.elm +7 -0
- package/src/RequestsAndPending.elm +1 -1
- package/src/Scaffold/Form.elm +2 -3
- package/src/TerminalText.elm +8 -0
- package/src/Vendored/Result/Extra.elm +75 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmi +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmo +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateDataTest.elmi +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateDataTest.elmo +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Reporter.elmi +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Reporter.elmo +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Runner.elmi +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Runner.elmo +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/lock +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm.json +0 -1
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +0 -7900
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Runner.elm.js +0 -28657
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_runner.js +0 -110
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_supervisor.js +0 -187
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/package.json +0 -1
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/src/Reporter.elm +0 -26
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/src/Runner.elm +0 -62
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Internal-RoutePattern.elmi +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Internal-RoutePattern.elmo +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolations.elmi +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolations.elmo +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolationsTest.elmi +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolationsTest.elmo +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Reporter.elmi +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Reporter.elmo +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Runner.elmi +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Runner.elmo +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/lock +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm.json +0 -1
- package/generator/review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +0 -7900
- package/generator/review/elm-stuff/tests-0.19.1/js/Runner.elm.js +0 -30511
- package/generator/review/elm-stuff/tests-0.19.1/js/node_runner.js +0 -110
- package/generator/review/elm-stuff/tests-0.19.1/js/node_supervisor.js +0 -187
- package/generator/review/elm-stuff/tests-0.19.1/js/package.json +0 -1
- package/generator/review/elm-stuff/tests-0.19.1/src/Reporter.elm +0 -26
- package/generator/review/elm-stuff/tests-0.19.1/src/Runner.elm +0 -62
- package/src/Result/Extra.elm +0 -26
package/generator/src/render.js
CHANGED
|
@@ -13,6 +13,19 @@ import { compatibilityKey } from "./compatibility-key.js";
|
|
|
13
13
|
import * as fs from "node:fs";
|
|
14
14
|
import * as crypto from "node:crypto";
|
|
15
15
|
import { restoreColorSafe } from "./error-formatter.js";
|
|
16
|
+
import { Spinnies } from "./spinnies/index.js";
|
|
17
|
+
import { default as which } from "which";
|
|
18
|
+
import * as readline from "readline";
|
|
19
|
+
import { spawn as spawnCallback } from "cross-spawn";
|
|
20
|
+
import * as consumers from "stream/consumers";
|
|
21
|
+
import * as zlib from "node:zlib";
|
|
22
|
+
import { Readable, Writable } from "node:stream";
|
|
23
|
+
import * as validateStream from "./validate-stream.js";
|
|
24
|
+
import { default as makeFetchHappenOriginal } from "make-fetch-happen";
|
|
25
|
+
import mergeStreams from "@sindresorhus/merge-streams";
|
|
26
|
+
|
|
27
|
+
let verbosity = 2;
|
|
28
|
+
const spinnies = new Spinnies();
|
|
16
29
|
|
|
17
30
|
process.on("unhandledRejection", (error) => {
|
|
18
31
|
console.error(error);
|
|
@@ -184,7 +197,8 @@ function runGeneratorAppHelp(
|
|
|
184
197
|
mode,
|
|
185
198
|
requestToPerform,
|
|
186
199
|
hasFsAccess,
|
|
187
|
-
patternsToWatch
|
|
200
|
+
patternsToWatch,
|
|
201
|
+
portsFile
|
|
188
202
|
);
|
|
189
203
|
} else {
|
|
190
204
|
return runHttpJob(
|
|
@@ -203,6 +217,7 @@ function runGeneratorAppHelp(
|
|
|
203
217
|
);
|
|
204
218
|
} else if (fromElm.tag === "Errors") {
|
|
205
219
|
foundErrors = true;
|
|
220
|
+
spinnies.stopAll();
|
|
206
221
|
reject(fromElm.args[0].errorsJson);
|
|
207
222
|
} else {
|
|
208
223
|
console.log(fromElm);
|
|
@@ -321,7 +336,8 @@ function runElmApp(
|
|
|
321
336
|
mode,
|
|
322
337
|
requestToPerform,
|
|
323
338
|
hasFsAccess,
|
|
324
|
-
patternsToWatch
|
|
339
|
+
patternsToWatch,
|
|
340
|
+
portsFile
|
|
325
341
|
);
|
|
326
342
|
} else {
|
|
327
343
|
return runHttpJob(
|
|
@@ -340,6 +356,7 @@ function runElmApp(
|
|
|
340
356
|
);
|
|
341
357
|
} else if (fromElm.tag === "Errors") {
|
|
342
358
|
foundErrors = true;
|
|
359
|
+
spinnies.stopAll();
|
|
343
360
|
reject(fromElm.args[0].errorsJson);
|
|
344
361
|
} else {
|
|
345
362
|
console.log(fromElm);
|
|
@@ -461,54 +478,85 @@ async function runInternalJob(
|
|
|
461
478
|
mode,
|
|
462
479
|
requestToPerform,
|
|
463
480
|
hasFsAccess,
|
|
464
|
-
patternsToWatch
|
|
481
|
+
patternsToWatch,
|
|
482
|
+
portsFile
|
|
465
483
|
) {
|
|
466
484
|
try {
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
485
|
+
const cwd = path.resolve(...requestToPerform.dir);
|
|
486
|
+
const quiet = requestToPerform.quiet;
|
|
487
|
+
const env = { ...process.env, ...requestToPerform.env };
|
|
488
|
+
|
|
489
|
+
const context = { cwd, quiet, env };
|
|
490
|
+
switch (requestToPerform.url) {
|
|
491
|
+
case "elm-pages-internal://log":
|
|
492
|
+
return [requestHash, await runLogJob(requestToPerform)];
|
|
493
|
+
case "elm-pages-internal://read-file":
|
|
494
|
+
return [
|
|
495
|
+
requestHash,
|
|
496
|
+
await readFileJobNew(requestToPerform, patternsToWatch, context),
|
|
497
|
+
];
|
|
498
|
+
case "elm-pages-internal://glob":
|
|
499
|
+
return [
|
|
500
|
+
requestHash,
|
|
501
|
+
await runGlobNew(requestToPerform, patternsToWatch),
|
|
502
|
+
];
|
|
503
|
+
case "elm-pages-internal://randomSeed":
|
|
504
|
+
return [
|
|
505
|
+
requestHash,
|
|
506
|
+
jsonResponse(
|
|
507
|
+
requestToPerform,
|
|
508
|
+
crypto.getRandomValues(new Uint32Array(1))[0]
|
|
509
|
+
),
|
|
510
|
+
];
|
|
511
|
+
case "elm-pages-internal://now":
|
|
512
|
+
return [requestHash, jsonResponse(requestToPerform, Date.now())];
|
|
513
|
+
case "elm-pages-internal://env":
|
|
514
|
+
return [
|
|
515
|
+
requestHash,
|
|
516
|
+
await runEnvJob(requestToPerform, patternsToWatch),
|
|
517
|
+
];
|
|
518
|
+
case "elm-pages-internal://encrypt":
|
|
519
|
+
return [
|
|
520
|
+
requestHash,
|
|
521
|
+
await runEncryptJob(requestToPerform, patternsToWatch),
|
|
522
|
+
];
|
|
523
|
+
case "elm-pages-internal://decrypt":
|
|
524
|
+
return [
|
|
525
|
+
requestHash,
|
|
526
|
+
await runDecryptJob(requestToPerform, patternsToWatch),
|
|
527
|
+
];
|
|
528
|
+
case "elm-pages-internal://write-file":
|
|
529
|
+
return [requestHash, await runWriteFileJob(requestToPerform, context)];
|
|
530
|
+
case "elm-pages-internal://sleep":
|
|
531
|
+
return [requestHash, await runSleep(requestToPerform)];
|
|
532
|
+
case "elm-pages-internal://which":
|
|
533
|
+
return [requestHash, await runWhich(requestToPerform)];
|
|
534
|
+
case "elm-pages-internal://question":
|
|
535
|
+
return [requestHash, await runQuestion(requestToPerform)];
|
|
536
|
+
case "elm-pages-internal://shell":
|
|
537
|
+
return [requestHash, await runShell(requestToPerform)];
|
|
538
|
+
case "elm-pages-internal://stream":
|
|
539
|
+
return [
|
|
540
|
+
requestHash,
|
|
541
|
+
await runStream(requestToPerform, portsFile, context),
|
|
542
|
+
];
|
|
543
|
+
case "elm-pages-internal://start-spinner":
|
|
544
|
+
return [requestHash, runStartSpinner(requestToPerform)];
|
|
545
|
+
case "elm-pages-internal://stop-spinner":
|
|
546
|
+
return [requestHash, runStopSpinner(requestToPerform)];
|
|
547
|
+
default:
|
|
548
|
+
throw `Unexpected internal BackendTask request format: ${kleur.yellow(
|
|
549
|
+
JSON.stringify(2, null, requestToPerform)
|
|
550
|
+
)}`;
|
|
504
551
|
}
|
|
505
552
|
} catch (error) {
|
|
506
553
|
sendError(app, error);
|
|
507
554
|
}
|
|
508
555
|
}
|
|
509
556
|
|
|
510
|
-
async function readFileJobNew(req, patternsToWatch) {
|
|
511
|
-
|
|
557
|
+
async function readFileJobNew(req, patternsToWatch, { cwd }) {
|
|
558
|
+
// TODO use cwd
|
|
559
|
+
const filePath = path.resolve(cwd, req.body.args[1]);
|
|
512
560
|
try {
|
|
513
561
|
patternsToWatch.add(filePath);
|
|
514
562
|
|
|
@@ -527,36 +575,619 @@ async function readFileJobNew(req, patternsToWatch) {
|
|
|
527
575
|
});
|
|
528
576
|
}
|
|
529
577
|
}
|
|
530
|
-
|
|
578
|
+
|
|
579
|
+
function runSleep(req) {
|
|
580
|
+
const { milliseconds } = req.body.args[0];
|
|
581
|
+
return new Promise((resolve) => {
|
|
582
|
+
setTimeout(() => {
|
|
583
|
+
resolve(jsonResponse(req, null));
|
|
584
|
+
}, milliseconds);
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
async function runWhich(req) {
|
|
589
|
+
const command = req.body.args[0];
|
|
590
|
+
try {
|
|
591
|
+
return jsonResponse(req, await which(command));
|
|
592
|
+
} catch (error) {
|
|
593
|
+
return jsonResponse(req, null);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
async function runQuestion(req) {
|
|
598
|
+
return jsonResponse(req, await question(req.body.args[0]));
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
function runStream(req, portsFile, context) {
|
|
602
|
+
return new Promise(async (resolve) => {
|
|
603
|
+
let metadataResponse = null;
|
|
604
|
+
let lastStream = null;
|
|
605
|
+
try {
|
|
606
|
+
const kind = req.body.args[0].kind;
|
|
607
|
+
const parts = req.body.args[0].parts;
|
|
608
|
+
let index = 0;
|
|
609
|
+
|
|
610
|
+
for (const part of parts) {
|
|
611
|
+
let isLastProcess = index === parts.length - 1;
|
|
612
|
+
let thisStream;
|
|
613
|
+
const { stream, metadata } = await pipePartToStream(
|
|
614
|
+
lastStream,
|
|
615
|
+
part,
|
|
616
|
+
context,
|
|
617
|
+
portsFile,
|
|
618
|
+
(value) => resolve(jsonResponse(req, value)),
|
|
619
|
+
isLastProcess,
|
|
620
|
+
kind
|
|
621
|
+
);
|
|
622
|
+
metadataResponse = metadata;
|
|
623
|
+
thisStream = stream;
|
|
624
|
+
|
|
625
|
+
lastStream = thisStream;
|
|
626
|
+
index += 1;
|
|
627
|
+
}
|
|
628
|
+
if (kind === "json") {
|
|
629
|
+
resolve(
|
|
630
|
+
jsonResponse(req, {
|
|
631
|
+
body: await consumers.json(lastStream),
|
|
632
|
+
metadata: await tryCallingFunction(metadataResponse),
|
|
633
|
+
})
|
|
634
|
+
);
|
|
635
|
+
} else if (kind === "text") {
|
|
636
|
+
resolve(
|
|
637
|
+
jsonResponse(req, {
|
|
638
|
+
body: await consumers.text(lastStream),
|
|
639
|
+
metadata: await tryCallingFunction(metadataResponse),
|
|
640
|
+
})
|
|
641
|
+
);
|
|
642
|
+
} else if (kind === "none") {
|
|
643
|
+
if (!lastStream) {
|
|
644
|
+
// ensure all error handling gets a chance to fire before resolving successfully
|
|
645
|
+
await tryCallingFunction(metadataResponse);
|
|
646
|
+
resolve(jsonResponse(req, { body: null }));
|
|
647
|
+
} else {
|
|
648
|
+
let resolvedMeta = await tryCallingFunction(metadataResponse);
|
|
649
|
+
lastStream.once("finish", async () => {
|
|
650
|
+
resolve(
|
|
651
|
+
jsonResponse(req, {
|
|
652
|
+
body: null,
|
|
653
|
+
metadata: resolvedMeta,
|
|
654
|
+
})
|
|
655
|
+
);
|
|
656
|
+
});
|
|
657
|
+
lastStream.once("end", async () => {
|
|
658
|
+
resolve(
|
|
659
|
+
jsonResponse(req, {
|
|
660
|
+
body: null,
|
|
661
|
+
metadata: resolvedMeta,
|
|
662
|
+
})
|
|
663
|
+
);
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
} else if (kind === "command") {
|
|
667
|
+
// already handled in parts.forEach
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
*
|
|
671
|
+
* @param {import('node:stream').Stream?} lastStream
|
|
672
|
+
* @param {{ name: string }} part
|
|
673
|
+
* @param {{cwd: string, quiet: boolean, env: object}} param2
|
|
674
|
+
* @returns {Promise<{stream: import('node:stream').Stream, metadata?: any}>}
|
|
675
|
+
*/
|
|
676
|
+
async function pipePartToStream(
|
|
677
|
+
lastStream,
|
|
678
|
+
part,
|
|
679
|
+
{ cwd, quiet, env },
|
|
680
|
+
portsFile,
|
|
681
|
+
resolve,
|
|
682
|
+
isLastProcess,
|
|
683
|
+
kind
|
|
684
|
+
) {
|
|
685
|
+
if (verbosity > 1 && !quiet) {
|
|
686
|
+
}
|
|
687
|
+
if (part.name === "stdout") {
|
|
688
|
+
return { stream: pipeIfPossible(lastStream, stdout()) };
|
|
689
|
+
} else if (part.name === "stderr") {
|
|
690
|
+
return { stream: pipeIfPossible(lastStream, stderr()) };
|
|
691
|
+
} else if (part.name === "stdin") {
|
|
692
|
+
return { stream: process.stdin };
|
|
693
|
+
} else if (part.name === "fileRead") {
|
|
694
|
+
const newLocal = fs.createReadStream(path.resolve(cwd, part.path));
|
|
695
|
+
newLocal.once("error", (error) => {
|
|
696
|
+
newLocal.close();
|
|
697
|
+
resolve({ error: error.toString() });
|
|
698
|
+
});
|
|
699
|
+
return { stream: newLocal };
|
|
700
|
+
} else if (part.name === "customDuplex") {
|
|
701
|
+
const newLocal = await portsFile[part.portName](part.input, {
|
|
702
|
+
cwd,
|
|
703
|
+
quiet,
|
|
704
|
+
env,
|
|
705
|
+
});
|
|
706
|
+
if (validateStream.isDuplexStream(newLocal.stream)) {
|
|
707
|
+
pipeIfPossible(lastStream, newLocal.stream);
|
|
708
|
+
return newLocal;
|
|
709
|
+
} else {
|
|
710
|
+
throw `Expected '${part.portName}' to be a duplex stream!`;
|
|
711
|
+
}
|
|
712
|
+
} else if (part.name === "customRead") {
|
|
713
|
+
return {
|
|
714
|
+
metadata: null,
|
|
715
|
+
stream: await portsFile[part.portName](part.input, {
|
|
716
|
+
cwd,
|
|
717
|
+
quiet,
|
|
718
|
+
env,
|
|
719
|
+
}),
|
|
720
|
+
};
|
|
721
|
+
} else if (part.name === "customWrite") {
|
|
722
|
+
const newLocal = await portsFile[part.portName](part.input, {
|
|
723
|
+
cwd,
|
|
724
|
+
quiet,
|
|
725
|
+
env,
|
|
726
|
+
});
|
|
727
|
+
if (!validateStream.isWritableStream(newLocal.stream)) {
|
|
728
|
+
console.error("Expected a writable stream!");
|
|
729
|
+
resolve({ error: "Expected a writable stream!" });
|
|
730
|
+
} else {
|
|
731
|
+
pipeIfPossible(lastStream, newLocal.stream);
|
|
732
|
+
}
|
|
733
|
+
return newLocal;
|
|
734
|
+
} else if (part.name === "gzip") {
|
|
735
|
+
const gzip = zlib.createGzip();
|
|
736
|
+
if (!lastStream) {
|
|
737
|
+
gzip.end();
|
|
738
|
+
}
|
|
739
|
+
return {
|
|
740
|
+
metadata: null,
|
|
741
|
+
stream: pipeIfPossible(lastStream, gzip),
|
|
742
|
+
};
|
|
743
|
+
} else if (part.name === "unzip") {
|
|
744
|
+
return {
|
|
745
|
+
metadata: null,
|
|
746
|
+
stream: pipeIfPossible(lastStream, zlib.createUnzip()),
|
|
747
|
+
};
|
|
748
|
+
} else if (part.name === "fileWrite") {
|
|
749
|
+
const destinationPath = path.resolve(part.path);
|
|
750
|
+
try {
|
|
751
|
+
await fsPromises.mkdir(path.dirname(destinationPath), {
|
|
752
|
+
recursive: true,
|
|
753
|
+
});
|
|
754
|
+
} catch (error) {
|
|
755
|
+
resolve({ error: error.toString() });
|
|
756
|
+
}
|
|
757
|
+
const newLocal = fs.createWriteStream(destinationPath);
|
|
758
|
+
newLocal.once("error", (error) => {
|
|
759
|
+
newLocal.close();
|
|
760
|
+
newLocal.removeAllListeners();
|
|
761
|
+
resolve({ error: error.toString() });
|
|
762
|
+
});
|
|
763
|
+
return {
|
|
764
|
+
metadata: null,
|
|
765
|
+
stream: pipeIfPossible(lastStream, newLocal),
|
|
766
|
+
};
|
|
767
|
+
} else if (part.name === "httpWrite") {
|
|
768
|
+
const makeFetchHappen = makeFetchHappenOriginal.defaults({
|
|
769
|
+
// cache: mode === "build" ? "no-cache" : "default",
|
|
770
|
+
cache: "default",
|
|
771
|
+
});
|
|
772
|
+
const response = await makeFetchHappen(part.url, {
|
|
773
|
+
body: lastStream,
|
|
774
|
+
duplex: "half",
|
|
775
|
+
redirect: "follow",
|
|
776
|
+
method: part.method,
|
|
777
|
+
headers: part.headers,
|
|
778
|
+
retry: part.retries,
|
|
779
|
+
timeout: part.timeoutInMs,
|
|
780
|
+
});
|
|
781
|
+
if (!isLastProcess && !response.ok) {
|
|
782
|
+
resolve({
|
|
783
|
+
error: `HTTP request failed: ${response.status} ${response.statusText}`,
|
|
784
|
+
});
|
|
785
|
+
} else {
|
|
786
|
+
let metadata = () => {
|
|
787
|
+
return {
|
|
788
|
+
headers: Object.fromEntries(response.headers.entries()),
|
|
789
|
+
statusCode: response.status,
|
|
790
|
+
// bodyKind,
|
|
791
|
+
url: response.url,
|
|
792
|
+
statusText: response.statusText,
|
|
793
|
+
};
|
|
794
|
+
};
|
|
795
|
+
return { metadata, stream: response.body };
|
|
796
|
+
}
|
|
797
|
+
} else if (part.name === "command") {
|
|
798
|
+
const { command, args, allowNon0Status, output } = part;
|
|
799
|
+
/** @type {'ignore' | 'inherit'} } */
|
|
800
|
+
let letPrint = quiet ? "ignore" : "inherit";
|
|
801
|
+
let stderrKind = kind === "none" ? letPrint : "pipe";
|
|
802
|
+
if (output === "Ignore") {
|
|
803
|
+
stderrKind = "ignore";
|
|
804
|
+
} else if (output === "Print") {
|
|
805
|
+
stderrKind = letPrint;
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
808
|
+
* @type {import('node:child_process').ChildProcess}
|
|
809
|
+
*/
|
|
810
|
+
const newProcess = spawnCallback(command, args, {
|
|
811
|
+
stdio: [
|
|
812
|
+
"pipe",
|
|
813
|
+
// if we are capturing stderr instead of stdout, print out stdout with `inherit`
|
|
814
|
+
output === "InsteadOfStdout" || kind === "none"
|
|
815
|
+
? letPrint
|
|
816
|
+
: "pipe",
|
|
817
|
+
stderrKind,
|
|
818
|
+
],
|
|
819
|
+
cwd: cwd,
|
|
820
|
+
env: env,
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
pipeIfPossible(lastStream, newProcess.stdin);
|
|
824
|
+
let newStream;
|
|
825
|
+
if (output === "MergeWithStdout") {
|
|
826
|
+
newStream = mergeStreams([newProcess.stdout, newProcess.stderr]);
|
|
827
|
+
} else if (output === "InsteadOfStdout") {
|
|
828
|
+
newStream = newProcess.stderr;
|
|
829
|
+
} else {
|
|
830
|
+
newStream = newProcess.stdout;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
newProcess.once("error", (error) => {
|
|
834
|
+
newStream && newStream.end();
|
|
835
|
+
newProcess.removeAllListeners();
|
|
836
|
+
resolve({ error: error.toString() });
|
|
837
|
+
});
|
|
838
|
+
if (isLastProcess) {
|
|
839
|
+
return {
|
|
840
|
+
stream: newStream,
|
|
841
|
+
metadata: new Promise((resoveMeta) => {
|
|
842
|
+
newProcess.once("exit", (code) => {
|
|
843
|
+
if (code !== 0 && !allowNon0Status) {
|
|
844
|
+
newStream && newStream.end();
|
|
845
|
+
resolve({
|
|
846
|
+
error: `Command ${command} exited with code ${code}`,
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
resoveMeta({
|
|
851
|
+
exitCode: code,
|
|
852
|
+
});
|
|
853
|
+
});
|
|
854
|
+
}),
|
|
855
|
+
};
|
|
856
|
+
} else {
|
|
857
|
+
return { metadata: null, stream: newStream };
|
|
858
|
+
}
|
|
859
|
+
} else if (part.name === "fromString") {
|
|
860
|
+
return { stream: Readable.from([part.string]), metadata: null };
|
|
861
|
+
} else {
|
|
862
|
+
// console.error(`Unknown stream part: ${part.name}!`);
|
|
863
|
+
// process.exit(1);
|
|
864
|
+
throw `Unknown stream part: ${part.name}!`;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
} catch (error) {
|
|
868
|
+
if (lastStream) {
|
|
869
|
+
lastStream.destroy();
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
resolve(jsonResponse(req, { error: error.toString() }));
|
|
873
|
+
}
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
/**
|
|
878
|
+
* @param { import('stream').Stream? } input
|
|
879
|
+
* @param {import('stream').Writable | import('stream').Duplex} destination
|
|
880
|
+
*/
|
|
881
|
+
function pipeIfPossible(input, destination) {
|
|
882
|
+
if (input) {
|
|
883
|
+
return input.pipe(destination);
|
|
884
|
+
} else {
|
|
885
|
+
return destination;
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
function stdout() {
|
|
890
|
+
return new Writable({
|
|
891
|
+
write(chunk, encoding, callback) {
|
|
892
|
+
process.stdout.write(chunk, callback);
|
|
893
|
+
},
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
function stderr() {
|
|
897
|
+
return new Writable({
|
|
898
|
+
write(chunk, encoding, callback) {
|
|
899
|
+
process.stderr.write(chunk, callback);
|
|
900
|
+
},
|
|
901
|
+
});
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
async function tryCallingFunction(func) {
|
|
905
|
+
if (func) {
|
|
906
|
+
// if is promise
|
|
907
|
+
if (func.then) {
|
|
908
|
+
return await func;
|
|
909
|
+
}
|
|
910
|
+
// if is function
|
|
911
|
+
else if (typeof func === "function") {
|
|
912
|
+
return await func();
|
|
913
|
+
}
|
|
914
|
+
} else {
|
|
915
|
+
return func;
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
async function runShell(req) {
|
|
920
|
+
const cwd = path.resolve(...req.dir);
|
|
921
|
+
const quiet = req.quiet;
|
|
922
|
+
const env = { ...process.env, ...req.env };
|
|
923
|
+
const captureOutput = req.body.args[0].captureOutput;
|
|
924
|
+
if (req.body.args[0].commands.length === 1) {
|
|
925
|
+
return jsonResponse(
|
|
926
|
+
req,
|
|
927
|
+
await shell({ cwd, quiet, env, captureOutput }, req.body.args[0])
|
|
928
|
+
);
|
|
929
|
+
} else {
|
|
930
|
+
return jsonResponse(
|
|
931
|
+
req,
|
|
932
|
+
await pipeShells({ cwd, quiet, env, captureOutput }, req.body.args[0])
|
|
933
|
+
);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
function commandAndArgsToString(cwd, commandsAndArgs) {
|
|
938
|
+
return (
|
|
939
|
+
`$ ` +
|
|
940
|
+
commandsAndArgs.commands
|
|
941
|
+
.map((commandAndArgs) => {
|
|
942
|
+
return [commandAndArgs.command, ...commandAndArgs.args].join(" ");
|
|
943
|
+
})
|
|
944
|
+
.join(" | ")
|
|
945
|
+
);
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
export function shell({ cwd, quiet, env, captureOutput }, commandAndArgs) {
|
|
949
|
+
return new Promise((resolve, reject) => {
|
|
950
|
+
const command = commandAndArgs.commands[0].command;
|
|
951
|
+
const args = commandAndArgs.commands[0].args;
|
|
952
|
+
if (verbosity > 1 && !quiet) {
|
|
953
|
+
console.log(commandAndArgsToString(cwd, commandAndArgs));
|
|
954
|
+
}
|
|
955
|
+
if (!captureOutput && !quiet) {
|
|
956
|
+
const subprocess = spawnCallback(command, args, {
|
|
957
|
+
stdio: quiet
|
|
958
|
+
? ["inherit", "ignore", "ignore"]
|
|
959
|
+
: ["inherit", "inherit", "inherit"],
|
|
960
|
+
cwd: cwd,
|
|
961
|
+
env: env,
|
|
962
|
+
});
|
|
963
|
+
subprocess.on("close", async (code) => {
|
|
964
|
+
resolve({
|
|
965
|
+
output: "",
|
|
966
|
+
errorCode: code,
|
|
967
|
+
stderrOutput: "",
|
|
968
|
+
stdoutOutput: "",
|
|
969
|
+
});
|
|
970
|
+
});
|
|
971
|
+
} else {
|
|
972
|
+
const subprocess = spawnCallback(command, args, {
|
|
973
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
974
|
+
cwd: cwd,
|
|
975
|
+
env: env,
|
|
976
|
+
});
|
|
977
|
+
let commandOutput = "";
|
|
978
|
+
let stderrOutput = "";
|
|
979
|
+
let stdoutOutput = "";
|
|
980
|
+
|
|
981
|
+
if (verbosity > 0 && !quiet) {
|
|
982
|
+
subprocess.stdout.pipe(process.stdout);
|
|
983
|
+
subprocess.stderr.pipe(process.stderr);
|
|
984
|
+
}
|
|
985
|
+
subprocess.stderr.on("data", function (data) {
|
|
986
|
+
commandOutput += data;
|
|
987
|
+
stderrOutput += data;
|
|
988
|
+
});
|
|
989
|
+
subprocess.stdout.on("data", function (data) {
|
|
990
|
+
commandOutput += data;
|
|
991
|
+
stdoutOutput += data;
|
|
992
|
+
});
|
|
993
|
+
|
|
994
|
+
subprocess.on("close", async (code) => {
|
|
995
|
+
resolve({
|
|
996
|
+
output: commandOutput,
|
|
997
|
+
errorCode: code,
|
|
998
|
+
stderrOutput,
|
|
999
|
+
stdoutOutput,
|
|
1000
|
+
});
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
/**
|
|
1007
|
+
* @typedef {{ command: string, args: string[], timeout: number? }} ElmCommand
|
|
1008
|
+
*/
|
|
1009
|
+
|
|
1010
|
+
/**
|
|
1011
|
+
* @param {{ commands: ElmCommand[] }} commandsAndArgs
|
|
1012
|
+
*/
|
|
1013
|
+
export function pipeShells(
|
|
1014
|
+
{ cwd, quiet, env, captureOutput },
|
|
1015
|
+
commandsAndArgs
|
|
1016
|
+
) {
|
|
1017
|
+
return new Promise((resolve, reject) => {
|
|
1018
|
+
if (verbosity > 1 && !quiet) {
|
|
1019
|
+
console.log(commandAndArgsToString(cwd, commandsAndArgs));
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
/**
|
|
1023
|
+
* @type {null | import('node:child_process').ChildProcess}
|
|
1024
|
+
*/
|
|
1025
|
+
let previousProcess = null;
|
|
1026
|
+
let currentProcess = null;
|
|
1027
|
+
|
|
1028
|
+
commandsAndArgs.commands.forEach(({ command, args, timeout }, index) => {
|
|
1029
|
+
let isLastProcess = index === commandsAndArgs.commands.length - 1;
|
|
1030
|
+
/**
|
|
1031
|
+
* @type {import('node:child_process').ChildProcess}
|
|
1032
|
+
*/
|
|
1033
|
+
if (previousProcess === null) {
|
|
1034
|
+
currentProcess = spawnCallback(command, args, {
|
|
1035
|
+
stdio: ["inherit", "pipe", "inherit"],
|
|
1036
|
+
timeout: timeout ? undefined : timeout,
|
|
1037
|
+
cwd: cwd,
|
|
1038
|
+
env: env,
|
|
1039
|
+
});
|
|
1040
|
+
} else {
|
|
1041
|
+
if (isLastProcess && !captureOutput && false) {
|
|
1042
|
+
currentProcess = spawnCallback(command, args, {
|
|
1043
|
+
stdio: quiet
|
|
1044
|
+
? ["pipe", "ignore", "ignore"]
|
|
1045
|
+
: ["pipe", "inherit", "inherit"],
|
|
1046
|
+
timeout: timeout ? undefined : timeout,
|
|
1047
|
+
cwd: cwd,
|
|
1048
|
+
env: env,
|
|
1049
|
+
});
|
|
1050
|
+
} else {
|
|
1051
|
+
currentProcess = spawnCallback(command, args, {
|
|
1052
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1053
|
+
timeout: timeout ? undefined : timeout,
|
|
1054
|
+
cwd: cwd,
|
|
1055
|
+
env: env,
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
previousProcess.stdout.pipe(currentProcess.stdin);
|
|
1059
|
+
}
|
|
1060
|
+
previousProcess = currentProcess;
|
|
1061
|
+
});
|
|
1062
|
+
|
|
1063
|
+
if (currentProcess === null) {
|
|
1064
|
+
reject("");
|
|
1065
|
+
} else {
|
|
1066
|
+
let commandOutput = "";
|
|
1067
|
+
let stderrOutput = "";
|
|
1068
|
+
let stdoutOutput = "";
|
|
1069
|
+
|
|
1070
|
+
if (verbosity > 0 && !quiet) {
|
|
1071
|
+
currentProcess.stdout && currentProcess.stdout.pipe(process.stdout);
|
|
1072
|
+
currentProcess.stderr && currentProcess.stderr.pipe(process.stderr);
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
currentProcess.stderr &&
|
|
1076
|
+
currentProcess.stderr.on("data", function (data) {
|
|
1077
|
+
commandOutput += data;
|
|
1078
|
+
stderrOutput += data;
|
|
1079
|
+
});
|
|
1080
|
+
currentProcess.stdout &&
|
|
1081
|
+
currentProcess.stdout.on("data", function (data) {
|
|
1082
|
+
commandOutput += data;
|
|
1083
|
+
stdoutOutput += data;
|
|
1084
|
+
});
|
|
1085
|
+
|
|
1086
|
+
currentProcess.on("close", async (code) => {
|
|
1087
|
+
resolve({
|
|
1088
|
+
output: commandOutput,
|
|
1089
|
+
errorCode: code,
|
|
1090
|
+
stderrOutput,
|
|
1091
|
+
stdoutOutput,
|
|
1092
|
+
});
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
});
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
export async function question({ prompt }) {
|
|
1099
|
+
return new Promise((resolve) => {
|
|
1100
|
+
const rl = readline.createInterface({
|
|
1101
|
+
input: process.stdin,
|
|
1102
|
+
output: process.stdout,
|
|
1103
|
+
});
|
|
1104
|
+
|
|
1105
|
+
return rl.question(prompt, (answer) => {
|
|
1106
|
+
rl.close();
|
|
1107
|
+
resolve(answer);
|
|
1108
|
+
});
|
|
1109
|
+
});
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
async function runWriteFileJob(req, { cwd }) {
|
|
531
1113
|
const data = req.body.args[0];
|
|
1114
|
+
const filePath = path.resolve(cwd, data.path);
|
|
532
1115
|
try {
|
|
533
|
-
|
|
534
|
-
await fsPromises.
|
|
535
|
-
await fsPromises.writeFile(fullPathToWrite, data.body);
|
|
1116
|
+
await fsPromises.mkdir(path.dirname(filePath), { recursive: true });
|
|
1117
|
+
await fsPromises.writeFile(filePath, data.body);
|
|
536
1118
|
return jsonResponse(req, null);
|
|
537
1119
|
} catch (error) {
|
|
538
1120
|
console.trace(error);
|
|
539
1121
|
throw {
|
|
540
1122
|
title: "BackendTask Error",
|
|
541
1123
|
message: `BackendTask.Generator.writeFile failed for file path: ${kleur.yellow(
|
|
542
|
-
|
|
1124
|
+
filePath
|
|
543
1125
|
)}\n${kleur.red(error.toString())}`,
|
|
544
1126
|
};
|
|
545
1127
|
}
|
|
546
1128
|
}
|
|
547
1129
|
|
|
1130
|
+
function runStartSpinner(req) {
|
|
1131
|
+
const data = req.body.args[0];
|
|
1132
|
+
let spinnerId;
|
|
1133
|
+
|
|
1134
|
+
if (data.spinnerId) {
|
|
1135
|
+
spinnerId = data.spinnerId;
|
|
1136
|
+
// TODO use updateSpinnerState?
|
|
1137
|
+
spinnies.update(spinnerId, { text: data.text, status: "spinning" });
|
|
1138
|
+
} else {
|
|
1139
|
+
spinnerId = Math.random().toString(36);
|
|
1140
|
+
// spinnies.add(spinnerId, { text: data.text, status: data.immediateStart ? 'spinning' : 'stopped' });
|
|
1141
|
+
spinnies.add(spinnerId, { text: data.text, status: "spinning" });
|
|
1142
|
+
// }
|
|
1143
|
+
}
|
|
1144
|
+
return jsonResponse(req, spinnerId);
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
function runStopSpinner(req) {
|
|
1148
|
+
const data = req.body.args[0];
|
|
1149
|
+
const { spinnerId, completionText, completionFn } = data;
|
|
1150
|
+
let completeFn;
|
|
1151
|
+
if (completionFn === "succeed") {
|
|
1152
|
+
spinnies.succeed(spinnerId, { text: completionText });
|
|
1153
|
+
} else if (completionFn === "fail") {
|
|
1154
|
+
spinnies.fail(spinnerId, { text: completionText });
|
|
1155
|
+
} else {
|
|
1156
|
+
console.log("Unexpected");
|
|
1157
|
+
}
|
|
1158
|
+
return jsonResponse(req, null);
|
|
1159
|
+
}
|
|
1160
|
+
|
|
548
1161
|
async function runGlobNew(req, patternsToWatch) {
|
|
549
1162
|
try {
|
|
550
1163
|
const { pattern, options } = req.body.args[0];
|
|
551
|
-
const
|
|
1164
|
+
const cwd = path.resolve(...req.dir);
|
|
1165
|
+
const matchedPaths = await globby.globby(pattern, {
|
|
1166
|
+
...options,
|
|
1167
|
+
stats: true,
|
|
1168
|
+
cwd,
|
|
1169
|
+
});
|
|
552
1170
|
patternsToWatch.add(pattern);
|
|
553
1171
|
|
|
554
1172
|
return jsonResponse(
|
|
555
1173
|
req,
|
|
556
1174
|
matchedPaths.map((fullPath) => {
|
|
1175
|
+
const stats = fullPath.stats;
|
|
1176
|
+
if (!stats) {
|
|
1177
|
+
return null;
|
|
1178
|
+
}
|
|
557
1179
|
return {
|
|
558
|
-
fullPath,
|
|
559
|
-
captures: mm.capture(pattern, fullPath),
|
|
1180
|
+
fullPath: fullPath.path,
|
|
1181
|
+
captures: mm.capture(pattern, fullPath.path),
|
|
1182
|
+
fileStats: {
|
|
1183
|
+
size: stats.size,
|
|
1184
|
+
atime: Math.round(stats.atime.getTime()),
|
|
1185
|
+
mtime: Math.round(stats.mtime.getTime()),
|
|
1186
|
+
ctime: Math.round(stats.ctime.getTime()),
|
|
1187
|
+
birthtime: Math.round(stats.birthtime.getTime()),
|
|
1188
|
+
fullPath: fullPath.path,
|
|
1189
|
+
isDirectory: stats.isDirectory(),
|
|
1190
|
+
},
|
|
560
1191
|
};
|
|
561
1192
|
})
|
|
562
1193
|
);
|