@typespec/http-server-csharp
Version:
TypeSpec service code generator for c-sharp
173 lines • 7.18 kB
JavaScript
/* eslint-disable no-console */
import { resolvePath } from "@typespec/compiler";
import { spawn } from "cross-spawn";
import path from "path";
import pc from "picocolors";
import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import { getFreePort } from "../lib/utils.js";
async function main() {
console.log(`TypeSpec Http Server Emitter for C-Sharp \n`);
await yargs(hideBin(process.argv))
.scriptName("hscs-scaffold")
.help()
.strict()
.parserConfiguration({
"greedy-arrays": false,
"boolean-negation": false,
})
.command("$0 <path-to-spec> [--output <project-directory>] [--use-swaggerui] [OPTIONS]", "Create an ASP.Net server project", (cmd) => {
return cmd
.option("use-swaggerui", {
description: "Include generated OpenAPI and a SwaggerUI endpoint in the service project. THIS OPTION REQUIRES '@typespec/openapi3' as a dependency of your typespec project.",
type: "boolean",
default: false,
})
.option("project-name", {
description: "The name of the generated project.",
type: "string",
default: "ServiceProject",
})
.option("http-port", {
description: "The http port for the generated project to use locally",
type: "number",
})
.option("https-port", {
description: "The https port for the generated service to listen on locally.",
type: "number",
})
.option("overwrite", {
description: "Overwrite existing mock implementations and project files",
type: "boolean",
default: true,
})
.option("output", {
description: "Path to the directory where the project will be created.",
type: "string",
})
.option("collection-type", {
description: "Specifies the type of collection to use: 'array' or 'enumerable'. If not specified, 'array' will be used by default.",
type: "string",
default: "array",
choices: ["array", "enumerable"],
})
.positional("path-to-spec", {
description: "The path to the TypeSpec spec or TypeSpec project directory",
type: "string",
demandOption: true,
});
}, async (args) => {
const projectDir = args["output"] !== undefined ? resolvePath(process.cwd(), args["output"]) : undefined;
const pathToSpec = resolvePath(process.cwd(), args["path-to-spec"]);
const useSwagger = args["use-swaggerui"];
const overwrite = args["overwrite"];
const projectName = args["project-name"];
const collectionType = args["collectionType"] ?? "array";
const httpPort = args["http-port"] || (await getFreePort(5000, 5999));
const httpsPort = args["https-port"] || (await getFreePort(7000, 7999));
console.log(pc.bold("Compiling spec to create ASP.Net core project with mock implementations"));
console.log(pc.bold(`using http port ${httpPort} and https port ${httpsPort}`));
const compileArgs = [
"tsp",
"compile",
pathToSpec,
"--emit",
"@typespec/http-server-csharp",
"--option",
"@typespec/http-server-csharp.emit-mocks=mocks-and-project-files",
"--option",
`@typespec/http-server-csharp.project-name=${projectName}`,
"--trace",
"http-server-csharp",
];
if (overwrite) {
compileArgs.push("--option", "@typespec/http-server-csharp.overwrite=true");
}
if (httpPort) {
compileArgs.push("--option", `@typespec/http-server-csharp.http-port=${httpPort}`);
}
if (httpsPort) {
compileArgs.push("--option", `@typespec/http-server-csharp.https-port=${httpsPort}`);
}
if (collectionType) {
compileArgs.push("--option", `@typespec/http-server-csharp.collection-type=${collectionType}`);
}
const swaggerArgs = [
"--emit",
"@typespec/openapi3",
"--option",
"@typespec/http-server-csharp.use-swaggerui=true",
];
if (projectDir) {
const generatedTargetDir = resolvePath(process.cwd(), projectDir);
const generatedOpenApiDir = resolvePath(process.cwd(), projectDir, "openapi");
const openApiPath = path
.relative(projectDir, resolvePath(generatedOpenApiDir, "openapi.yaml"))
.replaceAll("\\", "/");
compileArgs.push("--option", `@typespec/http-server-csharp.emitter-output-dir=${generatedTargetDir}`);
swaggerArgs.push("--option", `@typespec/openapi3.emitter-output-dir=${generatedOpenApiDir}`, "--option", `@typespec/http-server-csharp.openapi-path=${openApiPath}`);
}
if (useSwagger)
compileArgs.push(...swaggerArgs);
const result = await runScriptAsync("npx", compileArgs);
if (result === 0) {
console.log(pc.bold(`Your project was successfully created`));
}
else {
console.log(pc.bold("There were one or more errors"));
if (useSwagger) {
console.log("You must have @typespec/openapi3 as a dependency in your typespec project for use of SwaggerUI. ");
}
}
}).argv;
}
function internalError(error) {
// NOTE: An expected error, like one thrown for bad input, shouldn't reach
// here, but be handled somewhere else. If we reach here, it should be
// considered a bug and therefore we should not suppress the stack trace as
// that risks losing it in the case of a bug that does not repro easily.
console.log(error);
}
function processStream(input) {
if (input === undefined || input === null)
return "";
const data = `${input}`;
const lines = data.split("\n");
const result = [];
for (const line of lines) {
const token = "hscs-msg:";
const extraChars = token.length;
if (line.includes(token) && line.includes("trace")) {
const endPos = line.indexOf(token) + extraChars;
result.push(pc.bold(line.substring(endPos)));
}
else {
result.push(pc.dim(line));
}
}
return result.join("\n");
}
function runScriptAsync(cmd, args) {
let resolver;
const promise = new Promise((resolve, _) => {
resolver = resolve;
});
console.log(pc.green(`> ${cmd} ${args.join(" ")}`));
const proc = spawn(cmd, args);
proc.stdout?.on("data", (data) => {
console.log(processStream(data));
});
proc.stderr?.on("data", (data) => {
console.log(processStream(data));
});
proc.on("close", (_, __) => {
resolver(proc.exitCode || 0);
});
return promise;
}
process.on("unhandledRejection", (error) => {
console.error("Unhandled promise rejection!");
internalError(error);
});
main().catch(internalError);
//# sourceMappingURL=cli.js.map