mirror of
https://github.com/dillonkearns/elm-pages.git
synced 2024-09-11 09:05:28 +03:00
Replace CommandOutput with StderrOutput option to choose to merge or ignore sterr, etc.
This commit is contained in:
parent
e2c75207eb
commit
9c4f42dfae
@ -105,23 +105,28 @@ b =
|
||||
(.body >> Expect.equal "src")
|
||||
, Stream.fromString "invalid elm module"
|
||||
|> Stream.pipe
|
||||
(Stream.commandWithOptions (defaultCommandOptions |> Stream.allowNon0Status)
|
||||
(Stream.commandWithOptions
|
||||
(defaultCommandOptions
|
||||
|> Stream.allowNon0Status
|
||||
|> Stream.withOutput Stream.MergeStderrAndStdout
|
||||
)
|
||||
"elm-format"
|
||||
[ "--stdin" ]
|
||||
)
|
||||
|> Stream.read
|
||||
|> try
|
||||
|> test "stderr"
|
||||
(Expect.equal
|
||||
{ body = ""
|
||||
, metadata =
|
||||
{ exitCode = 1
|
||||
, stdout = ""
|
||||
, stderr = "Unable to parse file <STDIN>:1:13 To see a detailed explanation, run elm make on the file.\n"
|
||||
, combined = "Unable to parse file <STDIN>:1:13 To see a detailed explanation, run elm make on the file.\n"
|
||||
}
|
||||
}
|
||||
)
|
||||
(.body >> Expect.equal "Unable to parse file <STDIN>:1:13 To see a detailed explanation, run elm make on the file.\n")
|
||||
, Stream.commandWithOptions
|
||||
(defaultCommandOptions
|
||||
|> Stream.allowNon0Status
|
||||
)
|
||||
"elm-review"
|
||||
[ "--report=json" ]
|
||||
|> Stream.readJson (Decode.field "type" Decode.string)
|
||||
|> try
|
||||
|> test "elm-review"
|
||||
(.body >> Expect.equal "review-errors")
|
||||
]
|
||||
|
||||
|
||||
|
@ -22,6 +22,7 @@ import * as zlib from "node:zlib";
|
||||
import { Readable } from "node:stream";
|
||||
import * as validateStream from "./validate-stream.js";
|
||||
import { default as makeFetchHappenOriginal } from "make-fetch-happen";
|
||||
import mergeStreams from "@sindresorhus/merge-streams";
|
||||
|
||||
let verbosity = 2;
|
||||
const spinnies = new Spinnies();
|
||||
@ -678,26 +679,15 @@ function runStream(req, portsFile) {
|
||||
})
|
||||
);
|
||||
} else if (kind === "none") {
|
||||
// lastStream.once("finish", async () => {
|
||||
// resolve(jsonResponse(req, null));
|
||||
// });
|
||||
lastStream.once("close", async () => {
|
||||
resolve(
|
||||
jsonResponse(req, {
|
||||
body: null,
|
||||
metadata: await metadataResponse,
|
||||
})
|
||||
);
|
||||
});
|
||||
resolve(
|
||||
jsonResponse(req, {
|
||||
body: null,
|
||||
metadata: await metadataResponse,
|
||||
})
|
||||
);
|
||||
} else if (kind === "command") {
|
||||
// already handled in parts.forEach
|
||||
}
|
||||
|
||||
// lastStream.once("error", (error) => {
|
||||
// console.log('Stream error!');
|
||||
// console.error(error);
|
||||
// reject(jsonResponse(req, null));
|
||||
// });
|
||||
} catch (error) {
|
||||
if (lastStream) {
|
||||
lastStream.destroy();
|
||||
@ -788,43 +778,49 @@ async function pipePartToStream(
|
||||
};
|
||||
return { metadata, stream: response.body };
|
||||
} else if (part.name === "command") {
|
||||
const { command, args, allowNon0Status } = part;
|
||||
const { command, args, allowNon0Status, output } = part;
|
||||
/**
|
||||
* @type {import('node:child_process').ChildProcess}
|
||||
*/
|
||||
let stderrKind = "pipe";
|
||||
if (output === "Ignore") {
|
||||
stderrKind = "ignore";
|
||||
} else if (output === "Print") {
|
||||
stderrKind = "inherit";
|
||||
}
|
||||
const newProcess = spawnCallback(command, args, {
|
||||
stdio: ["pipe", "pipe", "pipe"],
|
||||
stdio: [
|
||||
"pipe",
|
||||
// if we are capturing stderr instead of stdout, print out stdout with `inherit`
|
||||
output === "InsteadOfStdout" ? "inherit" : "pipe",
|
||||
stderrKind,
|
||||
],
|
||||
cwd: cwd,
|
||||
env: env,
|
||||
});
|
||||
// newProcess.on("error", (error) => {
|
||||
// console.error("ERROR in pipeline!", error);
|
||||
// process.exit(1);
|
||||
// });
|
||||
let stderrOutput = "";
|
||||
let combinedOutput = "";
|
||||
newProcess.stderr.on("data", (data) => {
|
||||
stderrOutput += data;
|
||||
combinedOutput += data;
|
||||
});
|
||||
|
||||
lastStream && lastStream.pipe(newProcess.stdin);
|
||||
let newStream;
|
||||
if (output === "MergeWithStdout") {
|
||||
newStream = mergeStreams([newProcess.stdout, newProcess.stderr]);
|
||||
} else if (output === "InsteadOfStdout") {
|
||||
newStream = newProcess.stderr;
|
||||
} else {
|
||||
newStream = newProcess.stdout;
|
||||
}
|
||||
if (isLastProcess) {
|
||||
return {
|
||||
stream: newProcess.stdout,
|
||||
stream: newStream,
|
||||
metadata: new Promise((resolve) => {
|
||||
newProcess.on("exit", (code) => {
|
||||
resolve({
|
||||
exitCode: code,
|
||||
stdoutOutput: "",
|
||||
stderrOutput,
|
||||
combinedOutput,
|
||||
});
|
||||
});
|
||||
}),
|
||||
};
|
||||
} else {
|
||||
return { metadata: null, stream: newProcess.stdout };
|
||||
return { metadata: null, stream: newStream };
|
||||
}
|
||||
} else if (part.name === "fromString") {
|
||||
return { stream: Readable.from([part.string]) };
|
||||
|
18
package-lock.json
generated
18
package-lock.json
generated
@ -9,6 +9,7 @@
|
||||
"version": "3.0.12",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@sindresorhus/merge-streams": "^3.0.0",
|
||||
"busboy": "^1.6.0",
|
||||
"chokidar": "^3.5.3",
|
||||
"cli-cursor": "^4.0.0",
|
||||
@ -982,9 +983,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sindresorhus/merge-streams": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-1.0.0.tgz",
|
||||
"integrity": "sha512-rUV5WyJrJLoloD4NDN1V1+LDMDWOa4OTsT4yYJwQNpTU6FWxkxHpL7eu4w+DmiH8x/EAM1otkPE1+LaspIbplw==",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-3.0.0.tgz",
|
||||
"integrity": "sha512-5Muw0TDzXvK/i0BmrL1tiTsb6Sh/DXe/e5d63GpmHWr59t7rUyQhhiIuw605q/yvJxyBf6gMWmsxCC2fqtcFvQ==",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
@ -4225,6 +4226,17 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/globby/node_modules/@sindresorhus/merge-streams": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-1.0.0.tgz",
|
||||
"integrity": "sha512-rUV5WyJrJLoloD4NDN1V1+LDMDWOa4OTsT4yYJwQNpTU6FWxkxHpL7eu4w+DmiH8x/EAM1otkPE1+LaspIbplw==",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/got": {
|
||||
"version": "11.8.6",
|
||||
"resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz",
|
||||
|
@ -25,6 +25,7 @@
|
||||
"author": "Dillon Kearns",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@sindresorhus/merge-streams": "^3.0.0",
|
||||
"busboy": "^1.6.0",
|
||||
"chokidar": "^3.5.3",
|
||||
"cli-cursor": "^4.0.0",
|
||||
|
@ -1,13 +1,12 @@
|
||||
module BackendTask.Stream exposing
|
||||
( Stream
|
||||
, fileRead, fileWrite, fromString, http, httpWithInput, pipe, stdin, stdout, stderr, gzip, unzip
|
||||
, CommandOutput
|
||||
, command
|
||||
, read, readJson, run
|
||||
, Error(..)
|
||||
, commandWithOptions
|
||||
, CommandOptions, defaultCommandOptions, allowNon0Status, inheritUnused, withOutput, withTimeout
|
||||
, OutputChannel(..)
|
||||
, StderrOutput(..)
|
||||
, CommandOptions, defaultCommandOptions, allowNon0Status, withOutput, withTimeout
|
||||
, customRead, customWrite, customDuplex
|
||||
)
|
||||
|
||||
@ -49,8 +48,6 @@ End example
|
||||
|
||||
@docs fileRead, fileWrite, fromString, http, httpWithInput, pipe, stdin, stdout, stderr, gzip, unzip
|
||||
|
||||
@docs CommandOutput
|
||||
|
||||
|
||||
## Shell Commands
|
||||
|
||||
@ -68,9 +65,9 @@ End example
|
||||
|
||||
@docs commandWithOptions
|
||||
|
||||
@docs CommandOptions, defaultCommandOptions, allowNon0Status, inheritUnused, withOutput, withTimeout
|
||||
@docs StderrOutput
|
||||
|
||||
@docs OutputChannel
|
||||
@docs CommandOptions, defaultCommandOptions, allowNon0Status, withOutput, withTimeout
|
||||
|
||||
|
||||
## Custom Streams
|
||||
@ -99,9 +96,11 @@ type alias Recoverable error =
|
||||
{ fatal : FatalError, recoverable : error }
|
||||
|
||||
|
||||
mapRecoverable : { a | fatal : b, recoverable : c } -> { fatal : b, recoverable : Error c }
|
||||
mapRecoverable { fatal, recoverable } =
|
||||
{ fatal = fatal, recoverable = CustomError recoverable }
|
||||
mapRecoverable : Maybe body -> { a | fatal : b, recoverable : c } -> { fatal : b, recoverable : Error c body }
|
||||
mapRecoverable maybeBody { fatal, recoverable } =
|
||||
{ fatal = fatal
|
||||
, recoverable = CustomError recoverable maybeBody
|
||||
}
|
||||
|
||||
|
||||
type StreamPart
|
||||
@ -285,15 +284,15 @@ fromString string =
|
||||
|
||||
|
||||
{-| -}
|
||||
type Error error
|
||||
type Error error body
|
||||
= StreamError String
|
||||
| CustomError error
|
||||
| CustomError error (Maybe body)
|
||||
|
||||
|
||||
{-| -}
|
||||
read :
|
||||
Stream error metadata { read : (), write : write }
|
||||
-> BackendTask { fatal : FatalError, recoverable : Error error } { metadata : metadata, body : String }
|
||||
-> BackendTask { fatal : FatalError, recoverable : Error error String } { metadata : metadata, body : String }
|
||||
read ((Stream ( decoderName, decoder ) pipeline) as stream) =
|
||||
BackendTask.Internal.Request.request
|
||||
{ name = "stream"
|
||||
@ -331,7 +330,12 @@ read ((Stream ( decoderName, decoder ) pipeline) as stream) =
|
||||
(Decode.field "body" Decode.string)
|
||||
|
||||
Err error ->
|
||||
error |> mapRecoverable |> Err |> Decode.succeed
|
||||
Decode.field "body" Decode.string
|
||||
|> Decode.maybe
|
||||
|> Decode.map
|
||||
(\body ->
|
||||
error |> mapRecoverable body |> Err
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
@ -356,7 +360,7 @@ decodeLog decoder =
|
||||
readJson :
|
||||
Decoder value
|
||||
-> Stream error metadata { read : (), write : write }
|
||||
-> BackendTask { fatal : FatalError, recoverable : Error error } { metadata : metadata, body : value }
|
||||
-> BackendTask { fatal : FatalError, recoverable : Error error value } { metadata : metadata, body : value }
|
||||
readJson decoder ((Stream ( decoderName, metadataDecoder ) pipeline) as stream) =
|
||||
BackendTask.Internal.Request.request
|
||||
{ name = "stream"
|
||||
@ -365,13 +369,22 @@ readJson decoder ((Stream ( decoderName, metadataDecoder ) pipeline) as stream)
|
||||
BackendTask.Http.expectJson
|
||||
(Decode.field "metadata" metadataDecoder
|
||||
|> Decode.andThen
|
||||
(\result ->
|
||||
case result of
|
||||
Ok metadata ->
|
||||
(\result1 ->
|
||||
let
|
||||
bodyResult : Decoder (Result Decode.Error value)
|
||||
bodyResult =
|
||||
Decode.field "body" Decode.value
|
||||
|> Decode.map
|
||||
(\bodyValue ->
|
||||
case Decode.decodeValue decoder bodyValue of
|
||||
Decode.decodeValue decoder bodyValue
|
||||
)
|
||||
in
|
||||
bodyResult
|
||||
|> Decode.map
|
||||
(\result ->
|
||||
case result1 of
|
||||
Ok metadata ->
|
||||
case result of
|
||||
Ok body ->
|
||||
Ok
|
||||
{ metadata = metadata
|
||||
@ -379,17 +392,18 @@ readJson decoder ((Stream ( decoderName, metadataDecoder ) pipeline) as stream)
|
||||
}
|
||||
|
||||
Err decoderError ->
|
||||
Err
|
||||
(FatalError.recoverable
|
||||
{ title = "Failed to decode body"
|
||||
, body = "Failed to decode body"
|
||||
}
|
||||
(StreamError (Decode.errorToString decoderError))
|
||||
)
|
||||
)
|
||||
FatalError.recoverable
|
||||
{ title = "Failed to decode body"
|
||||
, body = "Failed to decode body"
|
||||
}
|
||||
(StreamError (Decode.errorToString decoderError))
|
||||
|> Err
|
||||
|
||||
Err error ->
|
||||
error |> mapRecoverable |> Err |> Decode.succeed
|
||||
Err error ->
|
||||
error
|
||||
|> mapRecoverable (Result.toMaybe result)
|
||||
|> Err
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -403,32 +417,27 @@ readBytes stream =
|
||||
|
||||
|
||||
{-| -}
|
||||
command : String -> List String -> Stream Int CommandOutput { read : read, write : write }
|
||||
command : String -> List String -> Stream Int () { read : read, write : write }
|
||||
command command_ args_ =
|
||||
commandWithOptions defaultCommandOptions command_ args_
|
||||
|
||||
|
||||
commandDecoder : Bool -> ( String, Decoder (Result (Recoverable Int) CommandOutput) )
|
||||
commandDecoder : Bool -> ( String, Decoder (Result (Recoverable Int) ()) )
|
||||
commandDecoder allowNon0 =
|
||||
( "command"
|
||||
, commandOutputDecoder
|
||||
|> Decode.map
|
||||
(\output ->
|
||||
if output.exitCode == 0 || allowNon0 || True then
|
||||
Ok
|
||||
{ stdout = output.stdout
|
||||
, stderr = output.stderr
|
||||
, combined = output.combined
|
||||
, exitCode = output.exitCode
|
||||
}
|
||||
(\exitCode ->
|
||||
if exitCode == 0 || allowNon0 || True then
|
||||
Ok ()
|
||||
|
||||
else
|
||||
Err
|
||||
(FatalError.recoverable
|
||||
{ title = "Command Failed"
|
||||
, body = "Command failed with exit code " ++ String.fromInt output.exitCode
|
||||
, body = "Command failed with exit code " ++ String.fromInt exitCode
|
||||
}
|
||||
output.exitCode
|
||||
exitCode
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -439,7 +448,7 @@ commandDecoder allowNon0 =
|
||||
|
||||
|
||||
{-| -}
|
||||
commandWithOptions : CommandOptions -> String -> List String -> Stream Int CommandOutput { read : read, write : write }
|
||||
commandWithOptions : CommandOptions -> String -> List String -> Stream Int () { read : read, write : write }
|
||||
commandWithOptions (CommandOptions options) command_ args_ =
|
||||
single (commandDecoder options.allowNon0Status)
|
||||
"command"
|
||||
@ -461,21 +470,13 @@ nullable encoder maybeValue =
|
||||
Encode.null
|
||||
|
||||
|
||||
{-| -}
|
||||
type OutputChannel
|
||||
= Stdout
|
||||
| Stderr
|
||||
| Both
|
||||
|
||||
|
||||
{-| -}
|
||||
type CommandOptions
|
||||
= CommandOptions CommandOptions_
|
||||
|
||||
|
||||
type alias CommandOptions_ =
|
||||
{ output : OutputChannel
|
||||
, inheritUnused : Bool
|
||||
{ output : StderrOutput
|
||||
, allowNon0Status : Bool
|
||||
, timeoutInMs : Maybe Int
|
||||
}
|
||||
@ -485,15 +486,14 @@ type alias CommandOptions_ =
|
||||
defaultCommandOptions : CommandOptions
|
||||
defaultCommandOptions =
|
||||
CommandOptions
|
||||
{ output = Stdout
|
||||
, inheritUnused = False
|
||||
{ output = PrintStderr
|
||||
, allowNon0Status = False
|
||||
, timeoutInMs = Nothing
|
||||
}
|
||||
|
||||
|
||||
{-| -}
|
||||
withOutput : OutputChannel -> CommandOptions -> CommandOptions
|
||||
withOutput : StderrOutput -> CommandOptions -> CommandOptions
|
||||
withOutput output (CommandOptions cmd) =
|
||||
CommandOptions { cmd | output = output }
|
||||
|
||||
@ -510,45 +510,37 @@ withTimeout timeoutMs (CommandOptions cmd) =
|
||||
CommandOptions { cmd | timeoutInMs = Just timeoutMs }
|
||||
|
||||
|
||||
{-| -}
|
||||
inheritUnused : CommandOptions -> CommandOptions
|
||||
inheritUnused (CommandOptions cmd) =
|
||||
CommandOptions { cmd | inheritUnused = True }
|
||||
|
||||
|
||||
encodeChannel : OutputChannel -> Encode.Value
|
||||
encodeChannel : StderrOutput -> Encode.Value
|
||||
encodeChannel output =
|
||||
Encode.string
|
||||
(case output of
|
||||
Stdout ->
|
||||
"stdout"
|
||||
IgnoreStderr ->
|
||||
"Ignore"
|
||||
|
||||
Stderr ->
|
||||
"stderr"
|
||||
PrintStderr ->
|
||||
"Print"
|
||||
|
||||
Both ->
|
||||
"both"
|
||||
MergeStderrAndStdout ->
|
||||
"MergeWithStdout"
|
||||
|
||||
StderrInsteadOfStdout ->
|
||||
"InsteadOfStdout"
|
||||
)
|
||||
|
||||
|
||||
{-| -}
|
||||
type alias CommandOutput =
|
||||
{ stdout : String
|
||||
, stderr : String
|
||||
, combined : String
|
||||
, exitCode : Int
|
||||
}
|
||||
|
||||
|
||||
commandOutputDecoder : Decoder CommandOutput
|
||||
commandOutputDecoder : Decoder Int
|
||||
commandOutputDecoder =
|
||||
Decode.map4 CommandOutput
|
||||
(Decode.field "stdoutOutput" Decode.string)
|
||||
(Decode.field "stderrOutput" Decode.string)
|
||||
(Decode.field "combinedOutput" Decode.string)
|
||||
(Decode.field "exitCode" Decode.int)
|
||||
Decode.field "exitCode" Decode.int
|
||||
|
||||
|
||||
commandToString : String -> List String -> String
|
||||
commandToString command_ args_ =
|
||||
command_ ++ " " ++ String.join " " args_
|
||||
|
||||
|
||||
{-| -}
|
||||
type StderrOutput
|
||||
= IgnoreStderr
|
||||
| PrintStderr
|
||||
| MergeStderrAndStdout
|
||||
| StderrInsteadOfStdout
|
||||
|
@ -43,7 +43,7 @@ Read more about using the `elm-pages` CLI to run (or bundle) scripts, plus a bri
|
||||
import BackendTask exposing (BackendTask)
|
||||
import BackendTask.Http
|
||||
import BackendTask.Internal.Request
|
||||
import BackendTask.Stream as Stream
|
||||
import BackendTask.Stream as Stream exposing (defaultCommandOptions)
|
||||
import Cli.OptionsParser as OptionsParser
|
||||
import Cli.Program as Program
|
||||
import FatalError exposing (FatalError)
|
||||
@ -250,7 +250,10 @@ exec command_ args_ =
|
||||
{-| -}
|
||||
command : String -> List String -> BackendTask FatalError String
|
||||
command command_ args_ =
|
||||
Stream.command command_ args_
|
||||
Stream.commandWithOptions
|
||||
(defaultCommandOptions |> Stream.withOutput Stream.MergeStderrAndStdout)
|
||||
command_
|
||||
args_
|
||||
|> Stream.read
|
||||
|> BackendTask.map (.metadata >> .combined)
|
||||
|> BackendTask.map .body
|
||||
|> BackendTask.allowFatal
|
||||
|
Loading…
Reference in New Issue
Block a user